Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add anchor output in vtxo tree and async redeem transaction #328

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 32 additions & 10 deletions common/bitcointree/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ import (

// CraftSharedOutput returns the taproot script and the amount of the initial root output
func CraftSharedOutput(
cosigners []*secp256k1.PublicKey, aspPubkey *secp256k1.PublicKey, receivers []Receiver,
feeSatsPerNode uint64, roundLifetime int64,
cosigners []*secp256k1.PublicKey,
aspPubkey *secp256k1.PublicKey,
receivers []Receiver,
feeSatsPerNode,
dustAmount uint64,
roundLifetime int64,
) ([]byte, int64, error) {
aggregatedKey, _, err := createAggregatedKeyWithSweep(
cosigners, aspPubkey, roundLifetime,
Expand All @@ -25,7 +29,7 @@ func CraftSharedOutput(
return nil, 0, err
}

root, err := createRootNode(aggregatedKey, cosigners, receivers, feeSatsPerNode)
root, err := createRootNode(aggregatedKey, cosigners, receivers, feeSatsPerNode, dustAmount)
if err != nil {
return nil, 0, err
}
Expand All @@ -42,8 +46,13 @@ func CraftSharedOutput(

// CraftCongestionTree creates all the tree's transactions
func CraftCongestionTree(
initialInput *wire.OutPoint, cosigners []*secp256k1.PublicKey, aspPubkey *secp256k1.PublicKey, receivers []Receiver,
feeSatsPerNode uint64, roundLifetime int64,
initialInput *wire.OutPoint,
cosigners []*secp256k1.PublicKey,
aspPubkey *secp256k1.PublicKey,
receivers []Receiver,
feeSatsPerNode,
dustAmount uint64,
roundLifetime int64,
) (tree.CongestionTree, error) {
aggregatedKey, sweepTapLeaf, err := createAggregatedKeyWithSweep(
cosigners, aspPubkey, roundLifetime,
Expand All @@ -52,7 +61,7 @@ func CraftCongestionTree(
return nil, err
}

root, err := createRootNode(aggregatedKey, cosigners, receivers, feeSatsPerNode)
root, err := createRootNode(aggregatedKey, cosigners, receivers, feeSatsPerNode, dustAmount)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -110,6 +119,7 @@ type node interface {
type leaf struct {
vtxoScript VtxoScript
amount int64
dustAmount int64
}

type branch struct {
Expand Down Expand Up @@ -152,12 +162,22 @@ func (l *leaf) getOutputs() ([]*wire.TxOut, error) {
return nil, err
}

output := &wire.TxOut{
Value: l.amount,
vtxoOutputAmount := l.amount - l.dustAmount
if vtxoOutputAmount <= l.dustAmount {
return nil, fmt.Errorf("vtxo output amount must be greater than dust amount")
}

vtxoOutput := &wire.TxOut{
Value: l.amount - l.dustAmount,
PkScript: script,
}

return []*wire.TxOut{output}, nil
anchorOutput := &wire.TxOut{
Value: l.dustAmount,
PkScript: ANCHOR_PKSCRIPT,
}

return []*wire.TxOut{vtxoOutput, anchorOutput}, nil
}

func (b *branch) getOutputs() ([]*wire.TxOut, error) {
Expand Down Expand Up @@ -247,7 +267,8 @@ func createRootNode(
aggregatedKey *musig2.AggregateKey,
cosigners []*secp256k1.PublicKey,
receivers []Receiver,
feeSatsPerNode uint64,
feeSatsPerNode,
dustAmount uint64,
) (root node, err error) {
if len(receivers) == 0 {
return nil, fmt.Errorf("no receivers provided")
Expand All @@ -258,6 +279,7 @@ func createRootNode(
leafNode := &leaf{
vtxoScript: r.Script,
amount: int64(r.Amount),
dustAmount: int64(dustAmount),
}
nodes = append(nodes, leafNode)
}
Expand Down
3 changes: 3 additions & 0 deletions common/bitcointree/musig2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
)

const (
dustAmount = 100
minRelayFee = 1000
exitDelay = 512
lifetime = 1024
Expand Down Expand Up @@ -46,6 +47,7 @@ func TestRoundTripSignTree(t *testing.T) {
asp.PubKey(),
castReceivers(f.Receivers, asp.PubKey()),
minRelayFee,
dustAmount,
lifetime,
)
require.NoError(t, err)
Expand All @@ -60,6 +62,7 @@ func TestRoundTripSignTree(t *testing.T) {
asp.PubKey(),
castReceivers(f.Receivers, asp.PubKey()),
minRelayFee,
dustAmount,
lifetime,
)
require.NoError(t, err)
Expand Down
3 changes: 3 additions & 0 deletions common/bitcointree/psbt.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ var (
COSIGNER_PSBT_KEY_PREFIX = []byte("cosigner")
)

// p2wsh(OP_TRUE)
var ANCHOR_PKSCRIPT = []byte{0, 32, 74, 232, 21, 114, 240, 110, 27, 136, 253, 92, 237, 122, 26, 0, 9, 69, 67, 46, 131, 225, 85, 30, 111, 114, 30, 233, 192, 11, 140, 195, 50, 96}

func AddCosignerKey(inIndex int, ptx *psbt.Packet, key *secp256k1.PublicKey) error {
currentCosigners, err := GetCosignerKeys(ptx.Inputs[inIndex])
if err != nil {
Expand Down
22 changes: 14 additions & 8 deletions pkg/client-sdk/covenantless_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1886,24 +1886,30 @@ func (a *covenantlessArkClient) validateOffChainReceiver(
return err
}

var amount uint64

for _, output := range tx.UnsignedTx.TxOut {
if len(output.PkScript) == 0 {
continue
}

if bytes.Equal(
output.PkScript[2:], schnorr.SerializePubKey(outputTapKey),
) {
if output.Value != int64(receiver.Amount) {
continue
}
if bytes.Equal(output.PkScript, bitcointree.ANCHOR_PKSCRIPT) {
amount += uint64(output.Value)
continue
}

found = true
break
if len(output.PkScript) == 34 {
if bytes.Equal(output.PkScript[2:], schnorr.SerializePubKey(outputTapKey)) {
found = true
amount += uint64(output.Value)
}
}
}

if found {
if amount != receiver.Amount {
continue
}
break
}
}
Expand Down
4 changes: 4 additions & 0 deletions server/internal/core/application/covenantless.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,10 @@ func (s *covenantlessService) CompleteAsyncPayment(
vtxos := make([]domain.Vtxo, 0, len(asyncPayData.receivers))

for outIndex, out := range redeemPtx.UnsignedTx.TxOut {
if bytes.Equal(out.PkScript, bitcointree.ANCHOR_PKSCRIPT) {
continue // skip anchor output
}

desc := asyncPayData.receivers[outIndex].Descriptor
_, _, _, _, err := descriptor.ParseReversibleVtxoDescriptor(desc)
isPending := err == nil
Expand Down
19 changes: 17 additions & 2 deletions server/internal/infrastructure/tx-builder/covenantless/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,9 +281,16 @@ func (b *txBuilder) BuildRoundTx(
return "", nil, "", err
}

var dustAmount uint64

if !isOnchainOnly(payments) {
dustAmount, err = b.wallet.GetDustAmount(context.Background())
if err != nil {
return "", nil, "", err
}

sharedOutputScript, sharedOutputAmount, err = bitcointree.CraftSharedOutput(
cosigners, aspPubkey, receivers, feeAmount, b.roundLifetime,
cosigners, aspPubkey, receivers, feeAmount, dustAmount, b.roundLifetime,
)
if err != nil {
return
Expand Down Expand Up @@ -314,7 +321,7 @@ func (b *txBuilder) BuildRoundTx(
}

congestionTree, err = bitcointree.CraftCongestionTree(
initialOutpoint, cosigners, aspPubkey, receivers, feeAmount, b.roundLifetime,
initialOutpoint, cosigners, aspPubkey, receivers, feeAmount, dustAmount, b.roundLifetime,
)
if err != nil {
return
Expand Down Expand Up @@ -497,6 +504,11 @@ func (b *txBuilder) BuildAsyncPaymentTransactions(
return "", fmt.Errorf("redeem tx fee is higher than the amount of the change receiver")
}

dustAmount, err := b.wallet.GetDustAmount(context.Background())
if err != nil {
return "", err
}

for i, receiver := range receivers {
offchainScript, err := bitcointree.ParseVtxoScript(receiver.Descriptor)
if err != nil {
Expand All @@ -518,6 +530,9 @@ func (b *txBuilder) BuildAsyncPaymentTransactions(
value := receiver.Amount
if i == len(receivers)-1 {
value -= redeemTxMinRelayFee
if value <= dustAmount {
return "", fmt.Errorf("change amount is dust amount")
}
}
outs = append(outs, &wire.TxOut{
Value: int64(value),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func TestMain(m *testing.M) {
wallet.On("MinRelayFee", mock.Anything, mock.Anything).
Return(uint64(30), nil)
wallet.On("GetDustAmount", mock.Anything).
Return(uint64(1000), nil)
Return(uint64(100), nil)
wallet.On("GetForfeitAddress", mock.Anything).
Return(forfeitAddress, nil)

Expand Down
Loading
Loading