Skip to content

Commit e0e8ff1

Browse files
authored
[Server] Add domain.ForfeitTx (#517)
* Add ForfeitTx domain & tidy up scaffolding * Update sqlite repo impl * Update app layer * Update interface layer * Lint
1 parent 6558b07 commit e0e8ff1

21 files changed

+201
-136
lines changed

server/internal/core/application/covenant.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -829,14 +829,14 @@ func (s *covenantService) finalizeRound(roundEndTime time.Time) {
829829
log.Debug("timeout waiting for forfeit txs and boarding inputs signatures")
830830
}
831831

832-
forfeitTxs, err := s.forfeitTxs.pop()
832+
forfeitTxList, err := s.forfeitTxs.pop()
833833
if err != nil {
834834
changes = round.Fail(fmt.Errorf("failed to finalize round: %s", err))
835835
log.WithError(err).Warn("failed to finalize round")
836836
return
837837
}
838838

839-
if err := s.verifyForfeitTxsSigs(forfeitTxs); err != nil {
839+
if err := s.verifyForfeitTxsSigs(forfeitTxList); err != nil {
840840
changes = round.Fail(err)
841841
log.WithError(err).Warn("failed to validate forfeit txs")
842842
return
@@ -893,6 +893,18 @@ func (s *covenantService) finalizeRound(roundEndTime time.Time) {
893893
return
894894
}
895895

896+
forfeitTxs := make([]domain.ForfeitTx, 0, len(forfeitTxList))
897+
for _, tx := range forfeitTxList {
898+
// nolint:all
899+
ptx, _ := psetv2.NewPsetFromBase64(tx)
900+
// nolint:all
901+
utx, _ := ptx.UnsignedTx()
902+
forfeitTxid := utx.TxHash().String()
903+
forfeitTxs = append(forfeitTxs, domain.ForfeitTx{
904+
Txid: forfeitTxid,
905+
Tx: tx,
906+
})
907+
}
896908
changes, err = round.EndFinalization(forfeitTxs, txid)
897909
if err != nil {
898910
changes = round.Fail(fmt.Errorf("failed to finalize round: %s", err))
@@ -1376,10 +1388,10 @@ func (s *covenantService) onchainNetwork() *network.Network {
13761388
}
13771389

13781390
func findForfeitTxLiquid(
1379-
forfeits []string, vtxo domain.VtxoKey,
1391+
forfeits []domain.ForfeitTx, vtxo domain.VtxoKey,
13801392
) (*psetv2.Pset, error) {
13811393
for _, forfeit := range forfeits {
1382-
forfeitTx, err := psetv2.NewPsetFromBase64(forfeit)
1394+
forfeitTx, err := psetv2.NewPsetFromBase64(forfeit.Tx)
13831395
if err != nil {
13841396
return nil, err
13851397
}

server/internal/core/application/covenantless.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,7 +1644,7 @@ func (s *covenantlessService) finalizeRound(notes []note.Note, roundEndTime time
16441644

16451645
txToSign := round.UnsignedTx
16461646
boardingInputs := make([]domain.VtxoKey, 0)
1647-
forfeitTxs := make([]string, 0)
1647+
forfeitTxs := make([]domain.ForfeitTx, 0)
16481648

16491649
if len(s.forfeitTxs.forfeitTxs) > 0 || includesBoardingInputs {
16501650
remainingTime := time.Until(roundEndTime)
@@ -1668,14 +1668,14 @@ func (s *covenantlessService) finalizeRound(notes []note.Note, roundEndTime time
16681668
}
16691669
txToSign = round.UnsignedTx
16701670

1671-
forfeitTxs, err = s.forfeitTxs.pop()
1671+
forfeitTxList, err := s.forfeitTxs.pop()
16721672
if err != nil {
16731673
changes = round.Fail(fmt.Errorf("failed to finalize round: %s", err))
16741674
log.WithError(err).Warn("failed to finalize round")
16751675
return
16761676
}
16771677

1678-
if err := s.verifyForfeitTxsSigs(forfeitTxs); err != nil {
1678+
if err := s.verifyForfeitTxsSigs(forfeitTxList); err != nil {
16791679
changes = round.Fail(err)
16801680
log.WithError(err).Warn("failed to validate forfeit txs")
16811681
return
@@ -1707,6 +1707,16 @@ func (s *covenantlessService) finalizeRound(notes []note.Note, roundEndTime time
17071707
return
17081708
}
17091709
}
1710+
1711+
for _, tx := range forfeitTxList {
1712+
// nolint:all
1713+
ptx, _ := psbt.NewFromRawBytes(strings.NewReader(tx), true)
1714+
forfeitTxid := ptx.UnsignedTx.TxHash().String()
1715+
forfeitTxs = append(forfeitTxs, domain.ForfeitTx{
1716+
Txid: forfeitTxid,
1717+
Tx: tx,
1718+
})
1719+
}
17101720
}
17111721

17121722
log.Debugf("signing transaction %s\n", round.Id)
@@ -2219,10 +2229,10 @@ func (s *covenantlessService) verifyForfeitTxsSigs(txs []string) error {
22192229
}
22202230

22212231
func findForfeitTxBitcoin(
2222-
forfeits []string, vtxo domain.VtxoKey,
2232+
forfeits []domain.ForfeitTx, vtxo domain.VtxoKey,
22232233
) (*psbt.Packet, error) {
22242234
for _, forfeit := range forfeits {
2225-
forfeitTx, err := psbt.NewFromRawBytes(strings.NewReader(forfeit), true)
2235+
forfeitTx, err := psbt.NewFromRawBytes(strings.NewReader(forfeit.Tx), true)
22262236
if err != nil {
22272237
return nil, err
22282238
}

server/internal/core/domain/events.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type RoundFinalizationStarted struct {
3030
type RoundFinalized struct {
3131
Id string
3232
Txid string
33-
ForfeitTxs []string
33+
ForfeitTxs []ForfeitTx
3434
Timestamp int64
3535
}
3636

server/internal/core/domain/round.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ type Stage struct {
3333
Failed bool
3434
}
3535

36+
type ForfeitTx struct {
37+
Txid string
38+
Tx string
39+
}
40+
3641
type Round struct {
3742
Id string
3843
StartingTimestamp int64
@@ -41,7 +46,7 @@ type Round struct {
4146
TxRequests map[string]TxRequest
4247
Txid string
4348
UnsignedTx string
44-
ForfeitTxs []string
49+
ForfeitTxs []ForfeitTx
4550
VtxoTree tree.TxTree
4651
Connectors tree.TxTree
4752
ConnectorAddress string
@@ -91,7 +96,7 @@ func (r *Round) On(event RoundEvent, replayed bool) {
9196
case RoundFinalized:
9297
r.Stage.Ended = true
9398
r.Txid = e.Txid
94-
r.ForfeitTxs = append([]string{}, e.ForfeitTxs...)
99+
r.ForfeitTxs = append([]ForfeitTx{}, e.ForfeitTxs...)
95100
r.EndingTimestamp = e.Timestamp
96101
case RoundFailed:
97102
r.Stage.Failed = true
@@ -177,7 +182,7 @@ func (r *Round) StartFinalization(
177182
return []RoundEvent{event}, nil
178183
}
179184

180-
func (r *Round) EndFinalization(forfeitTxs []string, txid string) ([]RoundEvent, error) {
185+
func (r *Round) EndFinalization(forfeitTxs []ForfeitTx, txid string) ([]RoundEvent, error) {
181186
if len(forfeitTxs) <= 0 {
182187
for _, request := range r.TxRequests {
183188
if len(request.Inputs) > 0 {
@@ -195,7 +200,7 @@ func (r *Round) EndFinalization(forfeitTxs []string, txid string) ([]RoundEvent,
195200
return nil, fmt.Errorf("round already finalized")
196201
}
197202
if forfeitTxs == nil {
198-
forfeitTxs = make([]string, 0)
203+
forfeitTxs = make([]ForfeitTx, 0)
199204
}
200205

201206
event := RoundFinalized{

server/internal/core/domain/round_test.go

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,14 @@ var (
6565
}},
6666
},
6767
}
68-
emptyPtx = "cHNldP8BAgQCAAAAAQQBAAEFAQABBgEDAfsEAgAAAAA="
69-
emptyTx = "0200000000000000000000"
70-
txid = "0000000000000000000000000000000000000000000000000000000000000000"
71-
txid2 = "0000000000000000000000000000000000000000000000000000000000000001"
68+
emptyPtx = "cHNldP8BAgQCAAAAAQQBAAEFAQABBgEDAfsEAgAAAAA="
69+
emptyTx = "0200000000000000000000"
70+
txid = "0000000000000000000000000000000000000000000000000000000000000000"
71+
txid2 = "0000000000000000000000000000000000000000000000000000000000000001"
72+
emptyForfeitTx = domain.ForfeitTx{
73+
Txid: txid,
74+
Tx: emptyPtx,
75+
}
7276
vtxoTree = tree.TxTree{
7377
{
7478
{
@@ -133,8 +137,11 @@ var (
133137
},
134138
},
135139
}
136-
forfeitTxs = []string{emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx, emptyPtx}
137-
roundTx = emptyTx
140+
forfeitTxs = []domain.ForfeitTx{
141+
emptyForfeitTx, emptyForfeitTx, emptyForfeitTx, emptyForfeitTx, emptyForfeitTx,
142+
emptyForfeitTx, emptyForfeitTx, emptyForfeitTx, emptyForfeitTx,
143+
}
144+
roundTx = emptyTx
138145
)
139146

140147
func TestRound(t *testing.T) {
@@ -474,7 +481,7 @@ func testEndFinalization(t *testing.T) {
474481
}
475482
fixtures := []struct {
476483
round *domain.Round
477-
forfeitTxs []string
484+
forfeitTxs []domain.ForfeitTx
478485
txid string
479486
expectedErr string
480487
}{
@@ -528,7 +535,7 @@ func testEndFinalization(t *testing.T) {
528535
Failed: true,
529536
},
530537
},
531-
forfeitTxs: []string{emptyPtx, emptyPtx, emptyPtx, emptyPtx},
538+
forfeitTxs: []domain.ForfeitTx{emptyForfeitTx, emptyForfeitTx, emptyForfeitTx, emptyForfeitTx},
532539
txid: txid,
533540
expectedErr: "not in a valid stage to end finalization",
534541
},
@@ -540,7 +547,7 @@ func testEndFinalization(t *testing.T) {
540547
Ended: true,
541548
},
542549
},
543-
forfeitTxs: []string{emptyPtx, emptyPtx, emptyPtx, emptyPtx},
550+
forfeitTxs: []domain.ForfeitTx{emptyForfeitTx, emptyForfeitTx, emptyForfeitTx, emptyForfeitTx},
544551
txid: txid,
545552
expectedErr: "round already finalized",
546553
},

server/internal/core/domain/payment.go renamed to server/internal/core/domain/tx_request.go

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
package domain
22

33
import (
4-
"crypto/sha256"
5-
"encoding/hex"
64
"fmt"
7-
"hash"
85

9-
"github.com/btcsuite/btcd/btcec/v2/schnorr"
10-
"github.com/decred/dcrd/dcrec/secp256k1/v4"
116
"github.com/google/uuid"
127
)
138

@@ -87,31 +82,6 @@ func (r TxRequest) validate(ignoreOuts bool) error {
8782
return nil
8883
}
8984

90-
type VtxoKey struct {
91-
Txid string
92-
VOut uint32
93-
}
94-
type Outpoint VtxoKey
95-
96-
func (k VtxoKey) String() string {
97-
return fmt.Sprintf("%s:%d", k.Txid, k.VOut)
98-
}
99-
100-
func (k VtxoKey) Hash() string {
101-
calcHash := func(buf []byte, hasher hash.Hash) []byte {
102-
_, _ = hasher.Write(buf)
103-
return hasher.Sum(nil)
104-
}
105-
106-
hash160 := func(buf []byte) []byte {
107-
return calcHash(calcHash(buf, sha256.New()), sha256.New())
108-
}
109-
110-
buf, _ := hex.DecodeString(k.Txid)
111-
buf = append(buf, byte(k.VOut))
112-
return hex.EncodeToString(hash160(buf))
113-
}
114-
11585
type Receiver struct {
11686
Amount uint64
11787
OnchainAddress string // onchain
@@ -121,25 +91,3 @@ type Receiver struct {
12191
func (r Receiver) IsOnchain() bool {
12292
return len(r.OnchainAddress) > 0
12393
}
124-
125-
type Vtxo struct {
126-
VtxoKey
127-
Amount uint64
128-
PubKey string
129-
RoundTxid string
130-
SpentBy string // round txid or redeem txid
131-
Spent bool
132-
Redeemed bool
133-
Swept bool
134-
ExpireAt int64
135-
RedeemTx string // empty if in-round vtxo
136-
CreatedAt int64
137-
}
138-
139-
func (v Vtxo) TapKey() (*secp256k1.PublicKey, error) {
140-
pubkeyBytes, err := hex.DecodeString(v.PubKey)
141-
if err != nil {
142-
return nil, err
143-
}
144-
return schnorr.ParsePubKey(pubkeyBytes)
145-
}

server/internal/core/domain/vtxo.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package domain
2+
3+
import (
4+
"crypto/sha256"
5+
"encoding/hex"
6+
"fmt"
7+
"hash"
8+
9+
"github.com/btcsuite/btcd/btcec/v2"
10+
"github.com/btcsuite/btcd/btcec/v2/schnorr"
11+
)
12+
13+
type VtxoKey struct {
14+
Txid string
15+
VOut uint32
16+
}
17+
type Outpoint VtxoKey
18+
19+
func (k VtxoKey) String() string {
20+
return fmt.Sprintf("%s:%d", k.Txid, k.VOut)
21+
}
22+
23+
func (k VtxoKey) Hash() string {
24+
calcHash := func(buf []byte, hasher hash.Hash) []byte {
25+
_, _ = hasher.Write(buf)
26+
return hasher.Sum(nil)
27+
}
28+
29+
hash160 := func(buf []byte) []byte {
30+
return calcHash(calcHash(buf, sha256.New()), sha256.New())
31+
}
32+
33+
buf, _ := hex.DecodeString(k.Txid)
34+
buf = append(buf, byte(k.VOut))
35+
return hex.EncodeToString(hash160(buf))
36+
}
37+
38+
type Vtxo struct {
39+
VtxoKey
40+
Amount uint64
41+
PubKey string
42+
RoundTxid string
43+
SpentBy string // round txid or redeem txid
44+
Spent bool
45+
Redeemed bool
46+
Swept bool
47+
ExpireAt int64
48+
RedeemTx string // empty if in-round vtxo
49+
CreatedAt int64
50+
}
51+
52+
func (v Vtxo) TapKey() (*btcec.PublicKey, error) {
53+
pubkeyBytes, err := hex.DecodeString(v.PubKey)
54+
if err != nil {
55+
return nil, err
56+
}
57+
return schnorr.ParsePubKey(pubkeyBytes)
58+
}

0 commit comments

Comments
 (0)