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

OOR & Vtxo transactions v3 + P2A outputs #328

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from
76 changes: 59 additions & 17 deletions common/bitcointree/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ import (

// CraftSharedOutput returns the taproot script and the amount of the initial root output
func CraftSharedOutput(
cosigners []*secp256k1.PublicKey, aspPubkey *secp256k1.PublicKey, receivers []tree.VtxoLeaf,
feeSatsPerNode uint64, roundLifetime int64,
cosigners []*secp256k1.PublicKey,
aspPubkey *secp256k1.PublicKey,
receivers []tree.VtxoLeaf,
feeSatsPerNode,
dustAmount uint64,
roundLifetime int64,
) ([]byte, int64, error) {
aggregatedKey, _, err := createAggregatedKeyWithSweep(
cosigners, aspPubkey, roundLifetime,
Expand All @@ -27,7 +31,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 @@ -44,8 +48,13 @@ func CraftSharedOutput(

// CraftCongestionTree creates all the tree's transactions
func CraftCongestionTree(
initialInput *wire.OutPoint, cosigners []*secp256k1.PublicKey, aspPubkey *secp256k1.PublicKey, receivers []tree.VtxoLeaf,
feeSatsPerNode uint64, roundLifetime int64,
initialInput *wire.OutPoint,
cosigners []*secp256k1.PublicKey,
aspPubkey *secp256k1.PublicKey,
receivers []tree.VtxoLeaf,
feeSatsPerNode,
dustAmount uint64,
roundLifetime int64,
) (tree.CongestionTree, error) {
aggregatedKey, sweepTapLeaf, err := createAggregatedKeyWithSweep(
cosigners, aspPubkey, roundLifetime,
Expand All @@ -54,7 +63,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 @@ -107,11 +116,13 @@ type node interface {
getAmount() int64 // returns the input amount of the node = sum of all receivers' amounts + fees
getOutputs() ([]*wire.TxOut, error)
getChildren() []node
getTxVersion() int32
}

type leaf struct {
amount int64
pubkey *secp256k1.PublicKey
amount int64
dustAmount int64
pubkey *secp256k1.PublicKey
}

type branch struct {
Expand All @@ -121,6 +132,14 @@ type branch struct {
feeAmount int64
}

func (b *branch) getTxVersion() int32 {
return 2
}

func (l *leaf) getTxVersion() int32 {
return 3
}

func (b *branch) getChildren() []node {
return b.children
}
Expand All @@ -133,7 +152,9 @@ func (b *branch) getAmount() int64 {
amount := int64(0)
for _, child := range b.children {
amount += child.getAmount()
amount += b.feeAmount
if child.getTxVersion() == 2 {
amount += b.feeAmount
}
}

return amount
Expand All @@ -149,12 +170,17 @@ func (l *leaf) getOutputs() ([]*wire.TxOut, error) {
return nil, err
}

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

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

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

func (b *branch) getOutputs() ([]*wire.TxOut, error) {
Expand All @@ -166,8 +192,13 @@ func (b *branch) getOutputs() ([]*wire.TxOut, error) {
outputs := make([]*wire.TxOut, 0)

for _, child := range b.children {
value := child.getAmount()
if child.getTxVersion() == 2 {
value += b.feeAmount
}

outputs = append(outputs, &wire.TxOut{
Value: child.getAmount() + b.feeAmount,
Value: value,
PkScript: sharedOutputScript,
})
}
Expand Down Expand Up @@ -202,6 +233,9 @@ func getTreeNode(
}, nil
}

// getTx returns the psbt associated with the node
// the psbt contains the inputs of the parent and the outputs of the children (or VTXOs if it's a leaf)
// it also contains the internal key used to "unroll" and the sweep tascript branch of the input
func getTx(
n node,
input *wire.OutPoint,
Expand All @@ -214,7 +248,13 @@ func getTx(
return nil, err
}

tx, err := psbt.New([]*wire.OutPoint{input}, outputs, 2, 0, []uint32{wire.MaxTxInSequenceNum})
tx, err := psbt.New(
[]*wire.OutPoint{input},
outputs,
n.getTxVersion(),
0,
[]uint32{wire.MaxTxInSequenceNum},
)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -244,7 +284,8 @@ func createRootNode(
aggregatedKey *musig2.AggregateKey,
cosigners []*secp256k1.PublicKey,
receivers []tree.VtxoLeaf,
feeSatsPerNode uint64,
feeSatsPerNode,
dustAmount uint64,
) (root node, err error) {
if len(receivers) == 0 {
return nil, fmt.Errorf("no receivers provided")
Expand All @@ -263,8 +304,9 @@ func createRootNode(
}

leafNode := &leaf{
amount: int64(r.Amount),
pubkey: pubkey,
amount: int64(r.Amount),
dustAmount: int64(dustAmount),
pubkey: pubkey,
}
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),
minRelayFee,
dustAmount,
lifetime,
)
require.NoError(t, err)
Expand All @@ -60,6 +62,7 @@ func TestRoundTripSignTree(t *testing.T) {
asp.PubKey(),
castReceivers(f.Receivers),
minRelayFee,
dustAmount,
lifetime,
)
require.NoError(t, err)
Expand Down
4 changes: 4 additions & 0 deletions common/bitcointree/psbt.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ var (
COSIGNER_PSBT_KEY_PREFIX = []byte("cosigner")
)

// P2A script = 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}
var ANCHOR_AMOUNT = int64(330) // dust amount for P2A

func AddCosignerKey(inIndex int, ptx *psbt.Packet, key *secp256k1.PublicKey) error {
currentCosigners, err := GetCosignerKeys(ptx.Inputs[inIndex])
if err != nil {
Expand Down
20 changes: 14 additions & 6 deletions pkg/client-sdk/covenantless_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -1891,22 +1891,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:], vtxoTapKey) {
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:], vtxoTapKey) {
found = true
amount += uint64(output.Value)
}
}
}

if found {
if amount != receiver.Amount {
continue
}
break
}
}
Expand Down
Loading
Loading