mirror of
https://github.com/aljazceru/ark.git
synced 2025-12-17 04:04:21 +01:00
* Rename asp > server * Rename pool > round * Consolidate naming for pubkey/prvkey vars and types * Fix * Fix * Fix wasm * Rename congestionTree > vtxoTree * Fix wasm * Rename payment > request * Rename congestionTree > vtxoTree after syncing with master * Fix Send API in SDK * Fix wasm * Fix wasm * Fixes * Fixes after review * Fix * Fix naming * Fix * Fix e2e tests
259 lines
5.6 KiB
Go
259 lines
5.6 KiB
Go
package domain
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/ark-network/ark/common/tree"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
const (
|
|
UndefinedStage RoundStage = iota
|
|
RegistrationStage
|
|
FinalizationStage
|
|
)
|
|
|
|
type RoundStage int
|
|
|
|
func (s RoundStage) String() string {
|
|
switch s {
|
|
case RegistrationStage:
|
|
return "REGISTRATION_STAGE"
|
|
case FinalizationStage:
|
|
return "FINALIZATION_STAGE"
|
|
default:
|
|
return "UNDEFINED_STAGE"
|
|
}
|
|
}
|
|
|
|
type Stage struct {
|
|
Code RoundStage
|
|
Ended bool
|
|
Failed bool
|
|
}
|
|
|
|
type Round struct {
|
|
Id string
|
|
StartingTimestamp int64
|
|
EndingTimestamp int64
|
|
Stage Stage
|
|
TxRequests map[string]TxRequest
|
|
Txid string
|
|
UnsignedTx string
|
|
ForfeitTxs []string
|
|
VtxoTree tree.VtxoTree
|
|
Connectors []string
|
|
ConnectorAddress string
|
|
DustAmount uint64
|
|
Version uint
|
|
Swept bool // true if all the vtxos are vtxo.Swept or vtxo.Redeemed
|
|
changes []RoundEvent
|
|
}
|
|
|
|
func NewRound(dustAmount uint64) *Round {
|
|
return &Round{
|
|
Id: uuid.New().String(),
|
|
DustAmount: dustAmount,
|
|
TxRequests: make(map[string]TxRequest),
|
|
changes: make([]RoundEvent, 0),
|
|
}
|
|
}
|
|
|
|
func NewRoundFromEvents(events []RoundEvent) *Round {
|
|
r := &Round{}
|
|
|
|
for _, event := range events {
|
|
r.On(event, true)
|
|
}
|
|
|
|
r.changes = append([]RoundEvent{}, events...)
|
|
|
|
return r
|
|
}
|
|
|
|
func (r *Round) Events() []RoundEvent {
|
|
return r.changes
|
|
}
|
|
|
|
func (r *Round) On(event RoundEvent, replayed bool) {
|
|
switch e := event.(type) {
|
|
case RoundStarted:
|
|
r.Stage.Code = RegistrationStage
|
|
r.Id = e.Id
|
|
r.StartingTimestamp = e.Timestamp
|
|
case RoundFinalizationStarted:
|
|
r.Stage.Code = FinalizationStage
|
|
r.VtxoTree = e.VtxoTree
|
|
r.Connectors = append([]string{}, e.Connectors...)
|
|
r.ConnectorAddress = e.ConnectorAddress
|
|
r.UnsignedTx = e.RoundTx
|
|
case RoundFinalized:
|
|
r.Stage.Ended = true
|
|
r.Txid = e.Txid
|
|
r.ForfeitTxs = append([]string{}, e.ForfeitTxs...)
|
|
r.EndingTimestamp = e.Timestamp
|
|
case RoundFailed:
|
|
r.Stage.Failed = true
|
|
r.EndingTimestamp = e.Timestamp
|
|
case TxRequestsRegistered:
|
|
if r.TxRequests == nil {
|
|
r.TxRequests = make(map[string]TxRequest)
|
|
}
|
|
for _, p := range e.TxRequests {
|
|
r.TxRequests[p.Id] = p
|
|
}
|
|
}
|
|
|
|
if replayed {
|
|
r.Version++
|
|
}
|
|
}
|
|
|
|
func (r *Round) StartRegistration() ([]RoundEvent, error) {
|
|
empty := Stage{}
|
|
if r.Stage != empty {
|
|
return nil, fmt.Errorf("not in a valid stage to start tx requests registration")
|
|
}
|
|
|
|
event := RoundStarted{
|
|
Id: r.Id,
|
|
Timestamp: time.Now().Unix(),
|
|
}
|
|
r.raise(event)
|
|
|
|
return []RoundEvent{event}, nil
|
|
}
|
|
|
|
func (r *Round) RegisterTxRequests(txRequests []TxRequest) ([]RoundEvent, error) {
|
|
if r.Stage.Code != RegistrationStage || r.IsFailed() {
|
|
return nil, fmt.Errorf("not in a valid stage to register tx requests")
|
|
}
|
|
if len(txRequests) <= 0 {
|
|
return nil, fmt.Errorf("missing tx requests to register")
|
|
}
|
|
for _, request := range txRequests {
|
|
if err := request.validate(false); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
event := TxRequestsRegistered{
|
|
Id: r.Id,
|
|
TxRequests: txRequests,
|
|
}
|
|
r.raise(event)
|
|
|
|
return []RoundEvent{event}, nil
|
|
}
|
|
|
|
func (r *Round) StartFinalization(connectorAddress string, connectors []string, vtxoTree tree.VtxoTree, roundTx string) ([]RoundEvent, error) {
|
|
if len(roundTx) <= 0 {
|
|
return nil, fmt.Errorf("missing unsigned round tx")
|
|
}
|
|
if r.Stage.Code != RegistrationStage || r.IsFailed() {
|
|
return nil, fmt.Errorf("not in a valid stage to start finalization")
|
|
}
|
|
if len(r.TxRequests) <= 0 {
|
|
return nil, fmt.Errorf("no tx requests registered")
|
|
}
|
|
|
|
event := RoundFinalizationStarted{
|
|
Id: r.Id,
|
|
VtxoTree: vtxoTree,
|
|
Connectors: connectors,
|
|
ConnectorAddress: connectorAddress,
|
|
RoundTx: roundTx,
|
|
}
|
|
r.raise(event)
|
|
|
|
return []RoundEvent{event}, nil
|
|
}
|
|
|
|
func (r *Round) EndFinalization(forfeitTxs []string, txid string) ([]RoundEvent, error) {
|
|
if len(forfeitTxs) <= 0 {
|
|
for _, request := range r.TxRequests {
|
|
if len(request.Inputs) > 0 {
|
|
return nil, fmt.Errorf("missing list of signed forfeit txs")
|
|
}
|
|
}
|
|
}
|
|
if len(txid) <= 0 {
|
|
return nil, fmt.Errorf("missing round txid")
|
|
}
|
|
if r.Stage.Code != FinalizationStage || r.IsFailed() {
|
|
return nil, fmt.Errorf("not in a valid stage to end finalization")
|
|
}
|
|
if r.Stage.Ended {
|
|
return nil, fmt.Errorf("round already finalized")
|
|
}
|
|
if forfeitTxs == nil {
|
|
forfeitTxs = make([]string, 0)
|
|
}
|
|
|
|
event := RoundFinalized{
|
|
Id: r.Id,
|
|
Txid: txid,
|
|
ForfeitTxs: forfeitTxs,
|
|
Timestamp: time.Now().Unix(),
|
|
}
|
|
r.raise(event)
|
|
|
|
return []RoundEvent{event}, nil
|
|
}
|
|
|
|
func (r *Round) Fail(err error) []RoundEvent {
|
|
if r.Stage.Failed {
|
|
return nil
|
|
}
|
|
event := RoundFailed{
|
|
Id: r.Id,
|
|
Err: err.Error(),
|
|
Timestamp: time.Now().Unix(),
|
|
}
|
|
r.raise(event)
|
|
|
|
return []RoundEvent{event}
|
|
}
|
|
|
|
func (r *Round) IsStarted() bool {
|
|
empty := Stage{}
|
|
return !r.IsFailed() && !r.IsEnded() && r.Stage != empty
|
|
}
|
|
|
|
func (r *Round) IsEnded() bool {
|
|
return !r.IsFailed() && r.Stage.Code == FinalizationStage && r.Stage.Ended
|
|
}
|
|
|
|
func (r *Round) IsFailed() bool {
|
|
return r.Stage.Failed
|
|
}
|
|
|
|
func (r *Round) TotalInputAmount() uint64 {
|
|
totInputs := 0
|
|
for _, request := range r.TxRequests {
|
|
totInputs += len(request.Inputs)
|
|
}
|
|
return uint64(totInputs * int(r.DustAmount))
|
|
}
|
|
|
|
func (r *Round) TotalOutputAmount() uint64 {
|
|
tot := uint64(0)
|
|
for _, request := range r.TxRequests {
|
|
tot += request.TotalOutputAmount()
|
|
}
|
|
return tot
|
|
}
|
|
|
|
func (r *Round) Sweep() {
|
|
r.Swept = true
|
|
}
|
|
|
|
func (r *Round) raise(event RoundEvent) {
|
|
if r.changes == nil {
|
|
r.changes = make([]RoundEvent, 0)
|
|
}
|
|
r.changes = append(r.changes, event)
|
|
r.On(event, false)
|
|
}
|