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

4
.gitignore vendored
View File

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

View File

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

View File

@@ -1,14 +1,14 @@
package bitcointree_test
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"github.com/ark-network/ark/common/tree"
"os"
"testing"
"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/txscript"
"github.com/btcsuite/btcd/wire"
@@ -27,22 +27,21 @@ var testTxid, _ = chainhash.NewHashFromStr("49f8664acc899be91902f8ade781b7eeb9cb
func TestRoundTripSignTree(t *testing.T) {
fixtures := parseFixtures(t)
for _, f := range fixtures.Valid {
alice, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err)
bob, err := secp256k1.GeneratePrivateKey()
// Generate 20 cosigners
cosigners := make([]*secp256k1.PrivateKey, 20)
cosignerPubKeys := make([]*btcec.PublicKey, 20)
for i := 0; i < 20; i++ {
privKey, err := secp256k1.GeneratePrivateKey()
require.NoError(t, err)
cosigners[i] = privKey
cosignerPubKeys[i] = privKey.PubKey()
}
asp, err := secp256k1.GeneratePrivateKey()
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(
cosigners,
cosignerPubKeys,
asp.PubKey(),
castReceivers(f.Receivers),
minRelayFee,
@@ -50,13 +49,12 @@ func TestRoundTripSignTree(t *testing.T) {
)
require.NoError(t, err)
// Create a new tree
tree, err := bitcointree.CraftCongestionTree(
&wire.OutPoint{
Hash: *testTxid,
Index: 0,
},
cosigners,
cosignerPubKeys,
asp.PubKey(),
castReceivers(f.Receivers),
minRelayFee,
@@ -79,132 +77,48 @@ func TestRoundTripSignTree(t *testing.T) {
sharedOutputAmount,
tree,
root.CloneBytes(),
[]*secp256k1.PublicKey{alice.PubKey(), bob.PubKey(), asp.PubKey()},
cosignerPubKeys,
)
require.NoError(t, err)
aliceSession := bitcointree.NewTreeSignerSession(alice, sharedOutputAmount, tree, root.CloneBytes())
bobSession := bitcointree.NewTreeSignerSession(bob, sharedOutputAmount, tree, root.CloneBytes())
aspSession := bitcointree.NewTreeSignerSession(asp, 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))
}
// Create signer sessions for all cosigners
signerSessions := make([]bitcointree.SignerSession, 20)
for i, cosigner := range cosigners {
signerSessions[i] = bitcointree.NewTreeSignerSession(cosigner, sharedOutputAmount, tree, root.CloneBytes())
}
err = aspCoordinator.AddNonce(alice.PubKey(), aliceNonces)
// Get nonces from all signers
for i, session := range signerSessions {
nonces, err := session.GetNonces()
require.NoError(t, err)
err = aspCoordinator.AddNonce(bob.PubKey(), bobNonces)
require.NoError(t, err)
err = aspCoordinator.AddNonce(asp.PubKey(), aspNonces)
err = aspCoordinator.AddNonce(cosignerPubKeys[i], nonces)
require.NoError(t, err)
}
aggregatedNonce, err := aspCoordinator.AggregateNonces()
require.NoError(t, err)
// coordinator sends the combined nonce to all signers
err = aliceSession.SetKeys(
cosigners,
)
// Set keys and aggregated nonces for all signers
for _, session := range signerSessions {
err = session.SetKeys(cosignerPubKeys)
require.NoError(t, err)
err = aliceSession.SetAggregatedNonces(
aggregatedNonce,
)
err = session.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
err = aspCoordinator.AddSig(alice.PubKey(), aliceSig)
// Get signatures from all signers
for i, session := range signerSessions {
sig, err := session.Sign()
require.NoError(t, err)
err = aspCoordinator.AddSig(bob.PubKey(), bobSig)
require.NoError(t, err)
err = aspCoordinator.AddSig(asp.PubKey(), aspSig)
err = aspCoordinator.AddSig(cosignerPubKeys[i], sig)
require.NoError(t, err)
}
signedTree, err := aspCoordinator.SignTree()
require.NoError(t, err)
// verify the tree
aggregatedKey, err := bitcointree.AggregateKeys(cosigners, root.CloneBytes())
aggregatedKey, err := bitcointree.AggregateKeys(cosignerPubKeys, root.CloneBytes())
require.NoError(t, err)
err = bitcointree.ValidateTreeSigs(
@@ -222,6 +136,24 @@ type receiverFixture struct {
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 {
receiversOut := make([]tree.VtxoLeaf, 0, len(receivers))
for _, r := range receivers {

View File

@@ -3,46 +3,81 @@
"valid": [
{
"receivers": [
{
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002",
"amount": 1100
}
{"amount": 1000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 2000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"}
]
},
{
"receivers": [
{
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002",
"amount": 1100
},
{
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002",
"amount": 8000
}
{"amount": 1000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 2000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 3000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"}
]
},
{
"receivers": [
{
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002",
"amount": 1100
{"amount": 1000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 2000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 3000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 4000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"}
]
},
{
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002",
"amount": 1100
"receivers": [
{"amount": 1000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 2000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 3000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 4000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"},
{"amount": 5000, "pubkey": "0000000000000000000000000000000000000000000000000000000000000002"}
]
},
{
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002",
"amount": 1100
"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"}
]
},
{
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002",
"amount": 1000
},
{
"pubkey": "0000000000000000000000000000000000000000000000000000000000000002",
"amount": 1100
}
"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
}
log.Infof("Ark Server config: %+v", appConfig)
log.Info("starting service...")
if err := svc.Start(); err != nil {
return err

View File

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