Make the round participants sign the vtxo tree (#271)

* [proto] add APIs to send and receive musig2 signing data

* [common] add serialization functions for nonces and signatures

* [application] implements tree signing

* fix: remove old debug logs

* [proto] cleaning

* [common] fix musig2.go

* [application] fixes and logs

* [interface] fix: stop forwarding 2 times the events

* [client] add musig2 support + sign the tree when joining a round

* [interface] add new APIs into permissions.go

* [application][proto] rework PingResponse (return all events type)

* [common] split SetKeys into 2 distinct methods

* [client] fixes according to musig2.go changes

* [sdk] support tree signing + new PingResponse

* [sdk] fixes

* [application] revert event channel type

* [application] use domain.RoundEvent as lastEvent type

* [application] remove IsCovenantLess

* comments

* [application] revert roundAborted changes

* [interface] remove bitcointree dependencie
This commit is contained in:
Louis Singer
2024-08-30 14:32:35 +02:00
committed by GitHub
parent 1b9660ec89
commit c183f99244
40 changed files with 4143 additions and 713 deletions

View File

@@ -3,6 +3,7 @@ package covenantless
import (
"bytes"
"context"
"encoding/hex"
"fmt"
"io"
"strings"
@@ -14,6 +15,7 @@ import (
"github.com/ark-network/ark/common/tree"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil/psbt"
"github.com/btcsuite/btcd/txscript"
"github.com/decred/dcrd/dcrec/secp256k1/v4"
"github.com/urfave/cli/v2"
"google.golang.org/grpc"
@@ -220,12 +222,15 @@ func castCongestionTree(congestionTree tree.CongestionTree) *arkv1.Tree {
func handleRoundStream(
ctx *cli.Context, client arkv1.ArkServiceClient, paymentID string,
vtxosToSign []vtxo, secKey *secp256k1.PrivateKey, receivers []*arkv1.Output,
ephemeralKey *secp256k1.PrivateKey,
) (poolTxID string, err error) {
stream, err := client.GetEventStream(ctx.Context, &arkv1.GetEventStreamRequest{})
if err != nil {
return "", err
}
myEphemeralPublicKey := hex.EncodeToString(ephemeralKey.PubKey().SerializeCompressed())
var pingStop func()
pingReq := &arkv1.PingRequest{
PaymentId: paymentID,
@@ -236,6 +241,8 @@ func handleRoundStream(
defer pingStop()
var treeSignerSession bitcointree.SignerSession
for {
event, err := stream.Recv()
if err == io.EOF {
@@ -250,6 +257,143 @@ func handleRoundStream(
return "", fmt.Errorf("round failed: %s", e.GetReason())
}
if e := event.GetRoundSigning(); e != nil {
pingStop()
fmt.Println("tree created, generating nonces...")
cosignersPubKeysHex := e.GetCosignersPubkeys()
cosigners := make([]*secp256k1.PublicKey, 0, len(cosignersPubKeysHex))
for _, pubkeyHex := range cosignersPubKeysHex {
pubkeyBytes, err := hex.DecodeString(pubkeyHex)
if err != nil {
return "", err
}
pubkey, err := secp256k1.ParsePubKey(pubkeyBytes)
if err != nil {
return "", err
}
cosigners = append(cosigners, pubkey)
}
congestionTree, err := toCongestionTree(e.GetUnsignedTree())
if err != nil {
return "", err
}
minRelayFee, err := utils.GetMinRelayFee(ctx)
if err != nil {
return "", err
}
aspPubkey, err := utils.GetAspPublicKey(ctx)
if err != nil {
return "", err
}
lifetime, err := utils.GetRoundLifetime(ctx)
if err != nil {
return "", err
}
sweepClosure := bitcointree.CSVSigClosure{
Pubkey: aspPubkey,
Seconds: uint(lifetime),
}
sweepTapLeaf, err := sweepClosure.Leaf()
if err != nil {
return "", err
}
sweepTapTree := txscript.AssembleTaprootScriptTree(*sweepTapLeaf)
root := sweepTapTree.RootNode.TapHash()
treeSignerSession = bitcointree.NewTreeSignerSession(
ephemeralKey, congestionTree, int64(minRelayFee), root.CloneBytes(),
)
nonces, err := treeSignerSession.GetNonces()
if err != nil {
return "", err
}
if err := treeSignerSession.SetKeys(cosigners); err != nil {
return "", err
}
var nonceBuffer bytes.Buffer
if err := nonces.Encode(&nonceBuffer); err != nil {
return "", err
}
serializedNonces := hex.EncodeToString(nonceBuffer.Bytes())
if _, err := client.SendTreeNonces(ctx.Context, &arkv1.SendTreeNoncesRequest{
RoundId: e.GetId(),
PublicKey: myEphemeralPublicKey,
TreeNonces: serializedNonces,
}); err != nil {
return "", err
}
fmt.Println("nonces sent")
continue
}
if e := event.GetRoundSigningNoncesGenerated(); e != nil {
pingStop()
fmt.Println("nonces generated, signing the tree...")
if treeSignerSession == nil {
return "", fmt.Errorf("tree signer session not set")
}
combinedNoncesBytes, err := hex.DecodeString(e.GetTreeNonces())
if err != nil {
return "", err
}
combinedNonces, err := bitcointree.DecodeNonces(bytes.NewReader(combinedNoncesBytes))
if err != nil {
return "", err
}
if err := treeSignerSession.SetAggregatedNonces(combinedNonces); err != nil {
return "", err
}
sigs, err := treeSignerSession.Sign()
if err != nil {
return "", err
}
var sigBuffer bytes.Buffer
if err := sigs.Encode(&sigBuffer); err != nil {
return "", err
}
serializedSigs := hex.EncodeToString(sigBuffer.Bytes())
if _, err := client.SendTreeSignatures(ctx.Context, &arkv1.SendTreeSignaturesRequest{
RoundId: e.GetId(),
TreeSignatures: serializedSigs,
PublicKey: myEphemeralPublicKey,
}); err != nil {
return "", err
}
fmt.Println("tree signed")
continue
}
if e := event.GetRoundFinalization(); e != nil {
// stop pinging as soon as we receive some forfeit txs
pingStop()