New boarding protocol (#279)

* [domain] add reverse boarding inputs in Payment struct

* [tx-builder] support reverse boarding script

* [wallet] add GetTransaction

* [api-spec][application] add reverse boarding support in covenantless

* [config] add reverse boarding config

* [api-spec] add ReverseBoardingAddress RPC

* [domain][application] support empty forfeits txs in EndFinalization events

* [tx-builder] optional connector output in round tx

* [btc-embedded] fix getTx and taproot finalizer

* whitelist ReverseBoardingAddress RPC

* [test] add reverse boarding integration test

* [client] support reverse boarding

* [sdk] support reverse boarding

* [e2e] add sleep time after faucet

* [test] run using bitcoin-core RPC

* [tx-builder] fix GetSweepInput

* [application][tx-builder] support reverse onboarding in covenant

* [cli] support reverse onboarding in covenant CLI

* [test] rework integration tests

* [sdk] remove onchain wallet, replace by onboarding address

* remove old onboarding protocols

* [sdk] Fix RegisterPayment

* [e2e] add more funds to covenant ASP

* [e2e] add sleeping time

* several fixes

* descriptor boarding

* remove boarding delay from info

* [sdk] implement descriptor boarding

* go mod tidy

* fixes and revert error msgs

* move descriptor pkg to common

* add replace in go.mod

* [sdk] fix unit tests

* rename DescriptorInput --> BoardingInput

* genrest in SDK

* remove boarding input from domain

* remove all "reverse boarding"

* rename "onboarding" ==> "boarding"

* remove outdate payment unit test

* use tmpfs docker volument for compose testing files

* several fixes
This commit is contained in:
Louis Singer
2024-09-04 19:21:26 +02:00
committed by GitHub
parent 8cba9c9d42
commit 4da76ec88b
113 changed files with 5627 additions and 4430 deletions

View File

@@ -9,12 +9,12 @@ import (
"github.com/ark-network/ark/common"
"github.com/ark-network/ark/common/bitcointree"
"github.com/ark-network/ark/common/descriptor"
"github.com/ark-network/ark/pkg/client-sdk/explorer"
"github.com/ark-network/ark/pkg/client-sdk/internal/utils"
"github.com/ark-network/ark/pkg/client-sdk/store"
"github.com/ark-network/ark/pkg/client-sdk/wallet"
walletstore "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey/store"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/btcutil/psbt"
@@ -41,42 +41,42 @@ func NewBitcoinWallet(
func (w *bitcoinWallet) GetAddresses(
ctx context.Context,
) ([]string, []string, []string, error) {
offchainAddr, onchainAddr, redemptionAddr, err := w.getAddress(ctx)
offchainAddr, boardingAddr, redemptionAddr, err := w.getAddress(ctx)
if err != nil {
return nil, nil, nil, err
}
offchainAddrs := []string{offchainAddr}
onchainAddrs := []string{onchainAddr}
boardingAddrs := []string{boardingAddr}
redemptionAddrs := []string{redemptionAddr}
return offchainAddrs, onchainAddrs, redemptionAddrs, nil
return offchainAddrs, boardingAddrs, redemptionAddrs, nil
}
func (w *bitcoinWallet) NewAddress(
ctx context.Context, _ bool,
) (string, string, error) {
offchainAddr, onchainAddr, _, err := w.getAddress(ctx)
offchainAddr, boardingAddr, _, err := w.getAddress(ctx)
if err != nil {
return "", "", err
}
return offchainAddr, onchainAddr, nil
return offchainAddr, boardingAddr, nil
}
func (w *bitcoinWallet) NewAddresses(
ctx context.Context, _ bool, num int,
) ([]string, []string, error) {
offchainAddr, onchainAddr, _, err := w.getAddress(ctx)
offchainAddr, boardingAddr, _, err := w.getAddress(ctx)
if err != nil {
return nil, nil, err
}
offchainAddrs := make([]string, 0, num)
onchainAddrs := make([]string, 0, num)
boardingAddrs := make([]string, 0, num)
for i := 0; i < num; i++ {
offchainAddrs = append(offchainAddrs, offchainAddr)
onchainAddrs = append(onchainAddrs, onchainAddr)
boardingAddrs = append(boardingAddrs, boardingAddr)
}
return offchainAddrs, onchainAddrs, nil
return offchainAddrs, boardingAddrs, nil
}
func (s *bitcoinWallet) SignTransaction(
@@ -92,11 +92,6 @@ func (s *bitcoinWallet) SignTransaction(
return "", err
}
data, err := s.configStore.GetData(ctx)
if err != nil {
return "", err
}
for i, input := range updater.Upsbt.UnsignedTx.TxIn {
if updater.Upsbt.Inputs[i].WitnessUtxo != nil {
continue
@@ -122,28 +117,11 @@ func (s *bitcoinWallet) SignTransaction(
return "", err
}
sighashType := txscript.SigHashAll
if utxo.PkScript[0] == txscript.OP_1 {
sighashType = txscript.SigHashDefault
}
if err := updater.AddInSighashType(sighashType, i); err != nil {
if err := updater.AddInSighashType(txscript.SigHashDefault, i); err != nil {
return "", err
}
}
_, onchainAddr, _, err := s.getAddress(ctx)
if err != nil {
return "", err
}
net := utils.ToBitcoinNetwork(data.Network)
addr, _ := btcutil.DecodeAddress(onchainAddr, &net)
onchainWalletScript, err := txscript.PayToAddrScript(addr)
if err != nil {
return "", err
}
prevouts := make(map[wire.OutPoint]*wire.TxOut)
for i, input := range updater.Upsbt.Inputs {
@@ -158,36 +136,6 @@ func (s *bitcoinWallet) SignTransaction(
txsighashes := txscript.NewTxSigHashes(updater.Upsbt.UnsignedTx, prevoutFetcher)
for i, input := range ptx.Inputs {
if bytes.Equal(input.WitnessUtxo.PkScript, onchainWalletScript) {
if err := updater.AddInSighashType(txscript.SigHashAll, i); err != nil {
return "", err
}
preimage, err := txscript.CalcWitnessSigHash(
input.WitnessUtxo.PkScript,
txsighashes,
txscript.SigHashAll,
updater.Upsbt.UnsignedTx,
i,
int64(input.WitnessUtxo.Value),
)
if err != nil {
return "", err
}
sig := ecdsa.Sign(s.privateKey, preimage)
signatureWithSighashType := append(sig.Serialize(), byte(txscript.SigHashAll))
updater.Upsbt.Inputs[i].PartialSigs = []*psbt.PartialSig{
{
PubKey: s.walletData.Pubkey.SerializeCompressed(),
Signature: signatureWithSighashType,
},
}
continue
}
if len(input.TaprootLeafScript) > 0 {
pubkey := s.walletData.Pubkey
for _, leaf := range input.TaprootLeafScript {
@@ -269,11 +217,6 @@ func (w *bitcoinWallet) getAddress(
netParams := utils.ToBitcoinNetwork(data.Network)
onchainAddr, err := btcutil.NewAddressWitnessPubKeyHash(btcutil.Hash160(w.walletData.Pubkey.SerializeCompressed()), &netParams)
if err != nil {
return "", "", "", err
}
vtxoTapKey, _, err := bitcointree.ComputeVtxoTaprootScript(
w.walletData.Pubkey, data.AspPubkey, uint(data.UnilateralExitDelay),
)
@@ -289,5 +232,35 @@ func (w *bitcoinWallet) getAddress(
return "", "", "", err
}
return offchainAddr, onchainAddr.EncodeAddress(), redemptionAddr.EncodeAddress(), nil
myPubkeyStr := hex.EncodeToString(schnorr.SerializePubKey(w.walletData.Pubkey))
descriptorStr := strings.ReplaceAll(
data.BoardingDescriptorTemplate, "USER", myPubkeyStr,
)
desc, err := descriptor.ParseTaprootDescriptor(descriptorStr)
if err != nil {
return "", "", "", err
}
_, boardingTimeout, err := descriptor.ParseBoardingDescriptor(*desc)
if err != nil {
return "", "", "", err
}
boardingTapKey, _, err := bitcointree.ComputeVtxoTaprootScript(
w.walletData.Pubkey, data.AspPubkey, boardingTimeout,
)
if err != nil {
return "", "", "", err
}
boardingAddr, err := btcutil.NewAddressTaproot(
schnorr.SerializePubKey(boardingTapKey),
&netParams,
)
if err != nil {
return "", "", "", err
}
return offchainAddr, boardingAddr.EncodeAddress(), redemptionAddr.EncodeAddress(), nil
}

View File

@@ -3,20 +3,21 @@ package singlekeywallet
import (
"bytes"
"context"
"encoding/hex"
"fmt"
"strings"
"github.com/ark-network/ark/common"
"github.com/ark-network/ark/common/descriptor"
"github.com/ark-network/ark/common/tree"
"github.com/ark-network/ark/pkg/client-sdk/explorer"
"github.com/ark-network/ark/pkg/client-sdk/internal/utils"
"github.com/ark-network/ark/pkg/client-sdk/store"
"github.com/ark-network/ark/pkg/client-sdk/wallet"
walletstore "github.com/ark-network/ark/pkg/client-sdk/wallet/singlekey/store"
"github.com/btcsuite/btcd/btcec/v2/ecdsa"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/vulpemventures/go-elements/payment"
"github.com/vulpemventures/go-elements/psetv2"
"github.com/vulpemventures/go-elements/transaction"
)
@@ -40,42 +41,42 @@ func NewLiquidWallet(
func (w *liquidWallet) GetAddresses(
ctx context.Context,
) ([]string, []string, []string, error) {
offchainAddr, onchainAddr, redemptionAddr, err := w.getAddress(ctx)
offchainAddr, boardingAddr, redemptionAddr, err := w.getAddress(ctx)
if err != nil {
return nil, nil, nil, err
}
offchainAddrs := []string{offchainAddr}
onchainAddrs := []string{onchainAddr}
boardingAddrs := []string{boardingAddr}
redemptionAddrs := []string{redemptionAddr}
return offchainAddrs, onchainAddrs, redemptionAddrs, nil
return offchainAddrs, boardingAddrs, redemptionAddrs, nil
}
func (w *liquidWallet) NewAddress(
ctx context.Context, _ bool,
) (string, string, error) {
offchainAddr, onchainAddr, _, err := w.getAddress(ctx)
offchainAddr, boardingAddr, _, err := w.getAddress(ctx)
if err != nil {
return "", "", err
}
return offchainAddr, onchainAddr, nil
return offchainAddr, boardingAddr, nil
}
func (w *liquidWallet) NewAddresses(
ctx context.Context, _ bool, num int,
) ([]string, []string, error) {
offchainAddr, onchainAddr, _, err := w.getAddress(ctx)
offchainAddr, boardingAddr, _, err := w.getAddress(ctx)
if err != nil {
return nil, nil, err
}
offchainAddrs := make([]string, 0, num)
onchainAddrs := make([]string, 0, num)
boardingAddrs := make([]string, 0, num)
for i := 0; i < num; i++ {
offchainAddrs = append(offchainAddrs, offchainAddr)
onchainAddrs = append(onchainAddrs, onchainAddr)
boardingAddrs = append(boardingAddrs, boardingAddr)
}
return offchainAddrs, onchainAddrs, nil
return offchainAddrs, boardingAddrs, nil
}
func (s *liquidWallet) SignTransaction(
@@ -114,13 +115,7 @@ func (s *liquidWallet) SignTransaction(
return "", err
}
sighashType := txscript.SigHashAll
if utxo.Script[0] == txscript.OP_1 {
sighashType = txscript.SigHashDefault
}
if err := updater.AddInSighashType(i, sighashType); err != nil {
if err := updater.AddInSighashType(i, txscript.SigHashDefault); err != nil {
return "", err
}
}
@@ -135,8 +130,6 @@ func (s *liquidWallet) SignTransaction(
return "", err
}
liquidNet := utils.ToElementsNetwork(storeData.Network)
p2wpkh := payment.FromPublicKey(s.walletData.Pubkey, &liquidNet, nil)
onchainWalletScript := p2wpkh.WitnessScript
utx, err := pset.UnsignedTx()
if err != nil {
@@ -156,33 +149,6 @@ func (s *liquidWallet) SignTransaction(
serializedPubKey := s.walletData.Pubkey.SerializeCompressed()
for i, input := range pset.Inputs {
prevout := input.GetUtxo()
if bytes.Equal(prevout.Script, onchainWalletScript) {
p, err := payment.FromScript(prevout.Script, &liquidNet, nil)
if err != nil {
return "", err
}
preimage := utx.HashForWitnessV0(
i, p.Script, prevout.Value, txscript.SigHashAll,
)
sig := ecdsa.Sign(s.privateKey, preimage[:])
signatureWithSighashType := append(
sig.Serialize(), byte(txscript.SigHashAll),
)
err = signer.SignInput(
i, signatureWithSighashType, serializedPubKey, nil, nil,
)
if err != nil {
return "", err
}
continue
}
if len(input.TapLeafScript) > 0 {
genesis, err := chainhash.NewHashFromStr(liquidNet.GenesisBlockHash)
if err != nil {
@@ -276,12 +242,6 @@ func (w *liquidWallet) getAddress(
liquidNet := utils.ToElementsNetwork(data.Network)
p2wpkh := payment.FromPublicKey(w.walletData.Pubkey, &liquidNet, nil)
onchainAddr, err := p2wpkh.WitnessPubKeyHash()
if err != nil {
return "", "", "", err
}
_, _, _, redemptionAddr, err := tree.ComputeVtxoTaprootScript(
w.walletData.Pubkey, data.AspPubkey, uint(data.UnilateralExitDelay), liquidNet,
)
@@ -289,5 +249,27 @@ func (w *liquidWallet) getAddress(
return "", "", "", err
}
return offchainAddr, onchainAddr, redemptionAddr, nil
myPubkeyStr := hex.EncodeToString(schnorr.SerializePubKey(w.walletData.Pubkey))
descriptorStr := strings.ReplaceAll(
data.BoardingDescriptorTemplate, "USER", myPubkeyStr,
)
desc, err := descriptor.ParseTaprootDescriptor(descriptorStr)
if err != nil {
return "", "", "", err
}
_, boardingTimeout, err := descriptor.ParseBoardingDescriptor(*desc)
if err != nil {
return "", "", "", err
}
_, _, _, boardingAddr, err := tree.ComputeVtxoTaprootScript(
w.walletData.Pubkey, data.AspPubkey, boardingTimeout, liquidNet,
)
if err != nil {
return "", "", "", err
}
return offchainAddr, boardingAddr, redemptionAddr, nil
}

View File

@@ -20,7 +20,7 @@ type WalletService interface {
IsLocked() bool
GetAddresses(
ctx context.Context,
) (offchainAddresses, onchainAddresses, redemptionAddresses []string, err error)
) (offchainAddresses, boardingAddresses, redemptionAddresses []string, err error)
NewAddress(
ctx context.Context, change bool,
) (offchainAddr, onchainAddr string, err error)
@@ -29,5 +29,5 @@ type WalletService interface {
) (offchainAddresses, onchainAddresses []string, err error)
SignTransaction(
ctx context.Context, explorerSvc explorer.Explorer, tx string,
) (singedTx string, err error)
) (signedTx string, err error)
}

View File

@@ -21,14 +21,15 @@ func TestWallet(t *testing.T) {
key, _ := btcec.NewPrivateKey()
password := "password"
testStoreData := store.StoreData{
AspUrl: "localhost:7070",
AspPubkey: key.PubKey(),
WalletType: wallet.SingleKeyWallet,
ClientType: client.GrpcClient,
Network: common.LiquidRegTest,
RoundLifetime: 512,
UnilateralExitDelay: 512,
MinRelayFee: 300,
AspUrl: "localhost:7070",
AspPubkey: key.PubKey(),
WalletType: wallet.SingleKeyWallet,
ClientType: client.GrpcClient,
Network: common.LiquidRegTest,
RoundLifetime: 512,
UnilateralExitDelay: 512,
MinRelayFee: 300,
BoardingDescriptorTemplate: "tr(0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0,{ and(pk(873079a0091c9b16abd1f8c508320b07f0d50144d09ccd792ce9c915dac60465), pk(USER)), and(older(604672), pk(USER)) })",
}
tests := []struct {
name string