Fix nonce encoding/decoding & Fix pop from queue of payment requests (#375)

* bug fix - paymentMap pop input traversal

* bug fix - nonce encode/decode

* pr review

* pr review
This commit is contained in:
Dusan Sekulic
2024-11-14 15:09:10 +01:00
committed by GitHub
parent cd502f3dac
commit 698ba4bf02
6 changed files with 176 additions and 204 deletions

6
.gitignore vendored
View File

@@ -1,5 +1,9 @@
.DS_Store .DS_Store
.vscode/ .vscode/
.idea
*.wasm *.wasm
wasm_exec.js wasm_exec.js
build
log

View File

@@ -2,6 +2,7 @@ package bitcointree
import ( import (
"bytes" "bytes"
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@@ -21,9 +22,6 @@ import (
var ( var (
ErrCongestionTreeNotSet = errors.New("congestion tree not set") ErrCongestionTreeNotSet = errors.New("congestion tree not set")
ErrAggregateKeyNotSet = errors.New("aggregate key not set") ErrAggregateKeyNotSet = errors.New("aggregate key not set")
columnSeparator = byte('|')
rowSeparator = byte('/')
) )
type Musig2Nonce struct { type Musig2Nonce struct {
@@ -37,12 +35,15 @@ func (n *Musig2Nonce) Encode(w io.Writer) error {
func (n *Musig2Nonce) Decode(r io.Reader) error { func (n *Musig2Nonce) Decode(r io.Reader) error {
bytes := make([]byte, 66) bytes := make([]byte, 66)
_, err := r.Read(bytes) bytesRead, err := io.ReadFull(r, bytes)
if err != nil { if err != nil {
return err return err
} }
if bytesRead != 66 {
return fmt.Errorf("expected to read 66 bytes, but read %d", bytesRead)
}
n.PubNonce = [66]byte(bytes) copy(n.PubNonce[:], bytes)
return nil return nil
} }
@@ -585,20 +586,23 @@ type readable interface {
func encodeMatrix[T writable](matrix [][]T) ([]byte, error) { func encodeMatrix[T writable](matrix [][]T) ([]byte, error) {
var buf bytes.Buffer var buf bytes.Buffer
for _, row := range matrix { // Write number of rows
for _, cell := range row { if err := binary.Write(&buf, binary.LittleEndian, uint32(len(matrix))); err != nil {
if err := buf.WriteByte(columnSeparator); err != nil { return nil, err
return nil, err }
}
// For each row, write its length and then its elements
for _, row := range matrix {
// Write row length
if err := binary.Write(&buf, binary.LittleEndian, uint32(len(row))); err != nil {
return nil, err
}
// Write row data
for _, cell := range row {
if err := cell.Encode(&buf); err != nil { if err := cell.Encode(&buf); err != nil {
return nil, err return nil, err
} }
} }
if err := buf.WriteByte(rowSeparator); err != nil {
return nil, err
}
} }
return buf.Bytes(), nil return buf.Bytes(), nil
@@ -606,36 +610,33 @@ func encodeMatrix[T writable](matrix [][]T) ([]byte, error) {
// decodeMatrix decode a byte stream into a matrix of serializable objects // decodeMatrix decode a byte stream into a matrix of serializable objects
func decodeMatrix[T readable](factory func() T, data io.Reader) ([][]T, error) { func decodeMatrix[T readable](factory func() T, data io.Reader) ([][]T, error) {
matrix := make([][]T, 0) var rowCount uint32
row := make([]T, 0)
for { // Read number of rows
separator := make([]byte, 1) if err := binary.Read(data, binary.LittleEndian, &rowCount); err != nil {
return nil, err
}
if _, err := data.Read(separator); err != nil { // Initialize matrix
if err == io.EOF { matrix := make([][]T, 0, rowCount)
break // For each row, read its length and then its elements
} for i := uint32(0); i < rowCount; i++ {
var colCount uint32
// Read row length
if err := binary.Read(data, binary.LittleEndian, &colCount); err != nil {
return nil, err return nil, err
} }
// Initialize row
b := separator[0] row := make([]T, 0, colCount)
// Read row data
if b == rowSeparator { for j := uint32(0); j < colCount; j++ {
matrix = append(matrix, row) cell := factory()
row = make([]T, 0) if err := cell.Decode(data); err != nil {
continue return nil, err
}
cell := factory()
if err := cell.Decode(data); err != nil {
if err == io.EOF {
break
} }
return nil, err row = append(row, cell)
} }
row = append(row, cell) matrix = append(matrix, row)
} }
return matrix, nil return matrix, nil

View File

@@ -1,14 +1,14 @@
package bitcointree_test package bitcointree_test
import ( import (
"bytes" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "github.com/ark-network/ark/common/tree"
"os" "os"
"testing" "testing"
"github.com/ark-network/ark/common/bitcointree" "github.com/ark-network/ark/common/bitcointree"
"github.com/ark-network/ark/common/tree" "github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcd/wire"
@@ -27,22 +27,21 @@ var testTxid, _ = chainhash.NewHashFromStr("49f8664acc899be91902f8ade781b7eeb9cb
func TestRoundTripSignTree(t *testing.T) { func TestRoundTripSignTree(t *testing.T) {
fixtures := parseFixtures(t) fixtures := parseFixtures(t)
for _, f := range fixtures.Valid { for _, f := range fixtures.Valid {
alice, err := secp256k1.GeneratePrivateKey() // Generate 20 cosigners
require.NoError(t, err) cosigners := make([]*secp256k1.PrivateKey, 20)
cosignerPubKeys := make([]*btcec.PublicKey, 20)
bob, err := secp256k1.GeneratePrivateKey() for i := 0; i < 20; i++ {
require.NoError(t, err) privKey, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err)
cosigners[i] = privKey
cosignerPubKeys[i] = privKey.PubKey()
}
asp, err := secp256k1.GeneratePrivateKey() asp, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err) require.NoError(t, err)
cosigners := make([]*secp256k1.PublicKey, 0)
cosigners = append(cosigners, alice.PubKey())
cosigners = append(cosigners, bob.PubKey())
cosigners = append(cosigners, asp.PubKey())
_, sharedOutputAmount, err := bitcointree.CraftSharedOutput( _, sharedOutputAmount, err := bitcointree.CraftSharedOutput(
cosigners, cosignerPubKeys,
asp.PubKey(), asp.PubKey(),
castReceivers(f.Receivers), castReceivers(f.Receivers),
minRelayFee, minRelayFee,
@@ -50,13 +49,12 @@ func TestRoundTripSignTree(t *testing.T) {
) )
require.NoError(t, err) require.NoError(t, err)
// Create a new tree
tree, err := bitcointree.CraftCongestionTree( tree, err := bitcointree.CraftCongestionTree(
&wire.OutPoint{ &wire.OutPoint{
Hash: *testTxid, Hash: *testTxid,
Index: 0, Index: 0,
}, },
cosigners, cosignerPubKeys,
asp.PubKey(), asp.PubKey(),
castReceivers(f.Receivers), castReceivers(f.Receivers),
minRelayFee, minRelayFee,
@@ -79,132 +77,48 @@ func TestRoundTripSignTree(t *testing.T) {
sharedOutputAmount, sharedOutputAmount,
tree, tree,
root.CloneBytes(), root.CloneBytes(),
[]*secp256k1.PublicKey{alice.PubKey(), bob.PubKey(), asp.PubKey()}, cosignerPubKeys,
) )
require.NoError(t, err) require.NoError(t, err)
aliceSession := bitcointree.NewTreeSignerSession(alice, sharedOutputAmount, tree, root.CloneBytes()) // Create signer sessions for all cosigners
bobSession := bitcointree.NewTreeSignerSession(bob, sharedOutputAmount, tree, root.CloneBytes()) signerSessions := make([]bitcointree.SignerSession, 20)
aspSession := bitcointree.NewTreeSignerSession(asp, sharedOutputAmount, tree, root.CloneBytes()) for i, cosigner := range cosigners {
signerSessions[i] = bitcointree.NewTreeSignerSession(cosigner, sharedOutputAmount, tree, root.CloneBytes())
aliceNonces, err := aliceSession.GetNonces()
require.NoError(t, err)
bobNonces, err := bobSession.GetNonces()
require.NoError(t, err)
aspNonces, err := aspSession.GetNonces()
require.NoError(t, err)
s := bytes.NewBuffer(nil)
err = aspNonces[0][0].Encode(s)
require.NoError(t, err)
bitcointreeNonce := new(bitcointree.Musig2Nonce)
err = bitcointreeNonce.Decode(s)
require.NoError(t, err)
require.Equal(t, aspNonces[0][0], bitcointreeNonce)
var serializedNonces bytes.Buffer
err = aspNonces.Encode(&serializedNonces)
require.NoError(t, err)
decodedNonces, err := bitcointree.DecodeNonces(&serializedNonces)
require.NoError(t, err)
for i, nonces := range aspNonces {
for j, nonce := range nonces {
require.Equal(t, nonce.PubNonce, decodedNonces[i][j].PubNonce, fmt.Sprintf("matrix nonce not equal at index i: %d, j: %d", i, j))
}
} }
err = aspCoordinator.AddNonce(alice.PubKey(), aliceNonces) // Get nonces from all signers
require.NoError(t, err) for i, session := range signerSessions {
nonces, err := session.GetNonces()
err = aspCoordinator.AddNonce(bob.PubKey(), bobNonces) require.NoError(t, err)
require.NoError(t, err) err = aspCoordinator.AddNonce(cosignerPubKeys[i], nonces)
require.NoError(t, err)
err = aspCoordinator.AddNonce(asp.PubKey(), aspNonces) }
require.NoError(t, err)
aggregatedNonce, err := aspCoordinator.AggregateNonces() aggregatedNonce, err := aspCoordinator.AggregateNonces()
require.NoError(t, err) require.NoError(t, err)
// coordinator sends the combined nonce to all signers // Set keys and aggregated nonces for all signers
for _, session := range signerSessions {
err = aliceSession.SetKeys( err = session.SetKeys(cosignerPubKeys)
cosigners, require.NoError(t, err)
) err = session.SetAggregatedNonces(aggregatedNonce)
require.NoError(t, err) require.NoError(t, err)
err = aliceSession.SetAggregatedNonces(
aggregatedNonce,
)
require.NoError(t, err)
err = bobSession.SetKeys(
cosigners,
)
require.NoError(t, err)
err = bobSession.SetAggregatedNonces(
aggregatedNonce,
)
require.NoError(t, err)
err = aspSession.SetKeys(
cosigners,
)
require.NoError(t, err)
err = aspSession.SetAggregatedNonces(
aggregatedNonce,
)
require.NoError(t, err)
aliceSig, err := aliceSession.Sign()
require.NoError(t, err)
bobSig, err := bobSession.Sign()
require.NoError(t, err)
aspSig, err := aspSession.Sign()
require.NoError(t, err)
// check that the sigs are serializable
serializedSigs := bytes.NewBuffer(nil)
err = aspSig.Encode(serializedSigs)
require.NoError(t, err)
decodedSigs, err := bitcointree.DecodeSignatures(serializedSigs)
require.NoError(t, err)
for i, sigs := range aspSig {
for j, sig := range sigs {
require.Equal(t, sig.S, decodedSigs[i][j].S, fmt.Sprintf("matrix sig not equal at index i: %d, j: %d", i, j))
}
} }
// coordinator receives the signatures and combines them // Get signatures from all signers
err = aspCoordinator.AddSig(alice.PubKey(), aliceSig) for i, session := range signerSessions {
require.NoError(t, err) sig, err := session.Sign()
require.NoError(t, err)
err = aspCoordinator.AddSig(bob.PubKey(), bobSig) err = aspCoordinator.AddSig(cosignerPubKeys[i], sig)
require.NoError(t, err) require.NoError(t, err)
}
err = aspCoordinator.AddSig(asp.PubKey(), aspSig)
require.NoError(t, err)
signedTree, err := aspCoordinator.SignTree() signedTree, err := aspCoordinator.SignTree()
require.NoError(t, err) require.NoError(t, err)
// verify the tree // verify the tree
aggregatedKey, err := bitcointree.AggregateKeys(cosigners, root.CloneBytes()) aggregatedKey, err := bitcointree.AggregateKeys(cosignerPubKeys, root.CloneBytes())
require.NoError(t, err) require.NoError(t, err)
err = bitcointree.ValidateTreeSigs( err = bitcointree.ValidateTreeSigs(
@@ -222,6 +136,24 @@ type receiverFixture struct {
Pubkey string `json:"pubkey"` Pubkey string `json:"pubkey"`
} }
func (r receiverFixture) toVtxoScript(asp *secp256k1.PublicKey) bitcointree.VtxoScript {
bytesKey, err := hex.DecodeString(r.Pubkey)
if err != nil {
panic(err)
}
pubkey, err := secp256k1.ParsePubKey(bytesKey)
if err != nil {
panic(err)
}
return &bitcointree.DefaultVtxoScript{
Owner: pubkey,
Asp: asp,
ExitDelay: exitDelay,
}
}
func castReceivers(receivers []receiverFixture) []tree.VtxoLeaf { func castReceivers(receivers []receiverFixture) []tree.VtxoLeaf {
receiversOut := make([]tree.VtxoLeaf, 0, len(receivers)) receiversOut := make([]tree.VtxoLeaf, 0, len(receivers))
for _, r := range receivers { for _, r := range receivers {

View File

@@ -3,48 +3,83 @@
"valid": [ "valid": [
{ {
"receivers": [ "receivers": [
{ {"amount": 1000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002", {"amount": 2000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"}
"amount": 1100
}
] ]
}, },
{ {
"receivers": [ "receivers": [
{ {"amount": 1000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002", {"amount": 2000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
"amount": 1100 {"amount": 3000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"}
},
{
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002",
"amount": 8000
}
] ]
}, },
{ {
"receivers": [ "receivers": [
{ {"amount": 1000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002", {"amount": 2000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
"amount": 1100 {"amount": 3000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
}, {"amount": 4000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"}
{ ]
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002", },
"amount": 1100 {
}, "receivers": [
{ {"amount": 1000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002", {"amount": 2000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
"amount": 1100 {"amount": 3000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
}, {"amount": 4000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{ {"amount": 5000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"}
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002", ]
"amount": 1000 },
}, {
{ "receivers": [
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002", {"amount": 1000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
"amount": 1100 {"amount": 2000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
} {"amount": 3000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 4000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 5000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 6000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"}
]
},
{
"receivers": [
{"amount": 1000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 2000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 3000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 4000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 5000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 6000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 7000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 8000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 9000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 10000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 11000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 12000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 13000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 14000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 15000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 16000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 17000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 18000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 19000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 20000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 21000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 22000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 23000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 24000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 25000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 26000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 27000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 28000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 29000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 30000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 31000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 32000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 33000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 34000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 35000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"}
] ]
} }
] ]
}
} }
}

View File

@@ -88,6 +88,8 @@ func mainAction(_ *cli.Context) error {
return err return err
} }
log.Infof("Ark Server config: %+v", appConfig)
log.Info("starting service...") log.Info("starting service...")
if err := svc.Start(); err != nil { if err := svc.Start(); err != nil {
return err return err

View File

@@ -146,11 +146,9 @@ func (m *paymentsMap) pop(num int64) ([]domain.Payment, []ports.BoardingInput, m
cosigners = append(cosigners, pubkey) cosigners = append(cosigners, pubkey)
delete(m.ephemeralKeys, p.Payment.Id) delete(m.ephemeralKeys, p.Payment.Id)
} }
for _, input := range payments { for _, vtxo := range p.Payment.Inputs {
for _, vtxo := range input.Inputs { descriptors[vtxo.VtxoKey] = m.descriptors[vtxo.VtxoKey]
descriptors[vtxo.VtxoKey] = m.descriptors[vtxo.VtxoKey] delete(m.descriptors, vtxo.VtxoKey)
delete(m.descriptors, vtxo.VtxoKey)
}
} }
delete(m.payments, p.Id) delete(m.payments, p.Id)
} }