Files
ark/server/internal/core/domain/round.go
Pietralberto Mazza 7f937e8418 Vars and fields renaming (#387)
* 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
2024-11-26 15:57:16 +01:00

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)
}