Skip to content

Commit 8856f68

Browse files
committed
tapgarden: propogate taptree sibling for batch key
1 parent 2d3d1e1 commit 8856f68

File tree

4 files changed

+173
-18
lines changed

4 files changed

+173
-18
lines changed

tapgarden/batch.go

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"time"
77

88
"github.com/btcsuite/btcd/btcec/v2"
9+
"github.com/btcsuite/btcd/chaincfg/chainhash"
910
"github.com/btcsuite/btcd/txscript"
1011
"github.com/lightninglabs/taproot-assets/asset"
1112
"github.com/lightninglabs/taproot-assets/commitment"
@@ -67,6 +68,10 @@ type MintingBatch struct {
6768
// to commit to the Taproot Asset commitment above.
6869
mintingPubKey *btcec.PublicKey
6970

71+
// tapSibling is an optional root hash of a tapscript tree that will be
72+
// used with the taprootAssetScriptRoot to construct the mintingPubKey.
73+
tapSibling *chainhash.Hash
74+
7075
// taprootAssetScriptRoot is the root hash of the Taproot Asset
7176
// commitment. If this is nil, then the mintingPubKey will be as well.
7277
taprootAssetScriptRoot []byte
@@ -104,7 +109,9 @@ func (m *MintingBatch) validateGroupAnchor(s *Seedling) error {
104109

105110
// MintingOutputKey derives the output key that once mined, will commit to the
106111
// Taproot asset root, thereby creating the set of included assets.
107-
func (m *MintingBatch) MintingOutputKey() (*btcec.PublicKey, []byte, error) {
112+
func (m *MintingBatch) MintingOutputKey(sibling *commitment.TapscriptPreimage) (
113+
*btcec.PublicKey, []byte, error) {
114+
108115
if m.mintingPubKey != nil {
109116
return m.mintingPubKey, m.taprootAssetScriptRoot, nil
110117
}
@@ -113,7 +120,21 @@ func (m *MintingBatch) MintingOutputKey() (*btcec.PublicKey, []byte, error) {
113120
return nil, nil, fmt.Errorf("no asset commitment present")
114121
}
115122

116-
taprootAssetScriptRoot := m.RootAssetCommitment.TapscriptRoot(nil)
123+
var (
124+
siblingHash *chainhash.Hash
125+
err error
126+
)
127+
128+
if sibling != nil {
129+
siblingHash, err = sibling.TapHash()
130+
if err != nil {
131+
return nil, nil, err
132+
}
133+
}
134+
135+
taprootAssetScriptRoot := m.RootAssetCommitment.TapscriptRoot(
136+
siblingHash,
137+
)
117138

118139
m.taprootAssetScriptRoot = taprootAssetScriptRoot[:]
119140
m.mintingPubKey = txscript.ComputeTaprootOutputKey(
@@ -125,8 +146,10 @@ func (m *MintingBatch) MintingOutputKey() (*btcec.PublicKey, []byte, error) {
125146

126147
// genesisScript returns the script that should be placed in the minting output
127148
// within the genesis transaction.
128-
func (m *MintingBatch) genesisScript() ([]byte, error) {
129-
mintingOutputKey, _, err := m.MintingOutputKey()
149+
func (m *MintingBatch) genesisScript(sibling *commitment.TapscriptPreimage) (
150+
[]byte, error) {
151+
152+
mintingOutputKey, _, err := m.MintingOutputKey(sibling)
130153
if err != nil {
131154
return nil, err
132155
}
@@ -149,3 +172,18 @@ func (m *MintingBatch) State() BatchState {
149172
func (m *MintingBatch) UpdateState(state BatchState) {
150173
m.batchState.Store(uint32(state))
151174
}
175+
176+
// TapSibling returns the optional tapscript sibling for the batch, which is a
177+
// root hash of a tapscript tree.
178+
func (m *MintingBatch) TapSibling() []byte {
179+
if m.tapSibling == nil {
180+
return nil
181+
}
182+
183+
return m.tapSibling.CloneBytes()
184+
}
185+
186+
// UpdateTapSibling updates the optional tapscript sibling for the batch.
187+
func (m *MintingBatch) UpdateTapSibling(sibling *chainhash.Hash) {
188+
m.tapSibling = sibling
189+
}

tapgarden/caretaker.go

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -679,10 +679,28 @@ func (b *BatchCaretaker) stateStep(currentState BatchState) (BatchState, error)
679679

680680
b.cfg.Batch.RootAssetCommitment = tapCommitment
681681

682+
// Fetch the optional Tapscript sibling for this batch, and
683+
// convert it to a TapscriptPreimage.
684+
var batchSibling *commitment.TapscriptPreimage
685+
if b.cfg.Batch.tapSibling != nil {
686+
tapSibling, err := b.cfg.TreeStore.LoadTapscriptTree(
687+
ctx, *b.cfg.Batch.tapSibling,
688+
)
689+
if err != nil {
690+
return 0, err
691+
}
692+
693+
batchSibling, err = commitment.
694+
NewPreimageFromTapscriptTreeNodes(*tapSibling)
695+
if err != nil {
696+
return 0, err
697+
}
698+
}
699+
682700
// With the commitment Taproot Asset root SMT constructed, we'll
683701
// map that into the tapscript root we'll insert into the
684702
// genesis transaction.
685-
genesisScript, err := b.cfg.Batch.genesisScript()
703+
genesisScript, err := b.cfg.Batch.genesisScript(batchSibling)
686704
if err != nil {
687705
return 0, fmt.Errorf("unable to create genesis "+
688706
"script: %w", err)
@@ -776,18 +794,51 @@ func (b *BatchCaretaker) stateStep(currentState BatchState) (BatchState, error)
776794

777795
// At this point we have a fully signed PSBT packet which'll
778796
// create our set of assets once mined. We'll write this to
779-
// disk, then import the public key into the wallet.
797+
// disk, then import the public key into the wallet. The sibling
798+
// here can always be nil as we'll fetch the output key computed
799+
// previously in BatchStateFrozen.
780800
//
781801
// TODO(roasbeef): re-run during the broadcast phase to ensure
782802
// it's fully imported?
783-
mintingOutputKey, tapRoot, err := b.cfg.Batch.MintingOutputKey()
803+
mintingOutputKey, merkleRoot, err := b.cfg.Batch.
804+
MintingOutputKey(nil)
784805
if err != nil {
785806
return 0, err
786807
}
808+
809+
// To spend this output in the future, we must also commit the
810+
// Taproot Asset commitment root and batch tapscript sibling.
811+
tapCommitmentRoot := b.cfg.Batch.RootAssetCommitment.
812+
TapscriptRoot(nil)
813+
814+
// Fetch the optional Tapscript sibling for this batch, and
815+
// encode it to bytes.
816+
var siblingBytes []byte
817+
if b.cfg.Batch.tapSibling != nil {
818+
tapSibling, err := b.cfg.TreeStore.LoadTapscriptTree(
819+
ctx, *b.cfg.Batch.tapSibling,
820+
)
821+
if err != nil {
822+
return 0, err
823+
}
824+
825+
batchSibling, err := commitment.
826+
NewPreimageFromTapscriptTreeNodes(*tapSibling)
827+
if err != nil {
828+
return 0, err
829+
}
830+
831+
siblingBytes, _, err = commitment.
832+
MaybeEncodeTapscriptPreimage(batchSibling)
833+
if err != nil {
834+
return 0, err
835+
}
836+
}
837+
787838
err = b.cfg.Log.CommitSignedGenesisTx(
788839
ctx, b.cfg.Batch.BatchKey.PubKey,
789840
b.cfg.Batch.GenesisPacket, b.anchorOutputIndex,
790-
tapRoot,
841+
merkleRoot, tapCommitmentRoot[:], siblingBytes,
791842
)
792843
if err != nil {
793844
return 0, fmt.Errorf("unable to commit genesis "+
@@ -976,6 +1027,24 @@ func (b *BatchCaretaker) stateStep(currentState BatchState) (BatchState, error)
9761027
groupVerifier := GenGroupVerifier(ctx, b.cfg.Log)
9771028
groupAnchorVerifier := GenGroupAnchorVerifier(ctx, b.cfg.Log)
9781029

1030+
// Fetch the optional tapscript sibling for this batch, which
1031+
// is needed to construct valid inclusion proofs.
1032+
var batchSibling *commitment.TapscriptPreimage
1033+
if b.cfg.Batch.tapSibling != nil {
1034+
tapSibling, err := b.cfg.TreeStore.LoadTapscriptTree(
1035+
ctx, *b.cfg.Batch.tapSibling,
1036+
)
1037+
if err != nil {
1038+
return 0, err
1039+
}
1040+
1041+
batchSibling, err = commitment.
1042+
NewPreimageFromTapscriptTreeNodes(*tapSibling)
1043+
if err != nil {
1044+
return 0, err
1045+
}
1046+
}
1047+
9791048
// Now that the minting transaction has been confirmed, we'll
9801049
// need to create the series of proof file blobs for each of
9811050
// the assets. In case the lnd wallet creates a P2TR change
@@ -990,6 +1059,7 @@ func (b *BatchCaretaker) stateStep(currentState BatchState) (BatchState, error)
9901059
TxIndex: int(confInfo.TxIndex),
9911060
OutputIndex: int(b.anchorOutputIndex),
9921061
InternalKey: b.cfg.Batch.BatchKey.PubKey,
1062+
TapscriptSibling: batchSibling,
9931063
TaprootAssetRoot: batchCommitment,
9941064
},
9951065
GenesisPoint: extractGenesisOutpoint(
@@ -1011,6 +1081,7 @@ func (b *BatchCaretaker) stateStep(currentState BatchState) (BatchState, error)
10111081
baseProof, headerVerifier, groupVerifier,
10121082
groupAnchorVerifier,
10131083
proof.WithAssetMetaReveals(b.cfg.Batch.AssetMetas),
1084+
proof.WithSiblingPreimage(batchSibling),
10141085
)
10151086
if err != nil {
10161087
return 0, fmt.Errorf("unable to construct minting "+

tapgarden/mock.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,3 +633,42 @@ func (m *MockProofWatcher) DefaultUpdateCallback() proof.UpdateCallback {
633633
return nil
634634
}
635635
}
636+
637+
type FallibleTapscriptTreeMgr struct {
638+
store MintingStore
639+
FailLoad, FailStore bool
640+
}
641+
642+
func (mgr FallibleTapscriptTreeMgr) DeleteTapscriptTree(ctx context.Context,
643+
rootHash chainhash.Hash) error {
644+
645+
return mgr.store.DeleteTapscriptTree(ctx, rootHash)
646+
}
647+
648+
func (mgr FallibleTapscriptTreeMgr) LoadTapscriptTree(ctx context.Context,
649+
rootHash chainhash.Hash) (*asset.TapscriptTreeNodes, error) {
650+
651+
if mgr.FailLoad {
652+
return nil, fmt.Errorf("failed to load tapscript tree")
653+
}
654+
655+
return mgr.store.LoadTapscriptTree(ctx, rootHash)
656+
}
657+
658+
func (mgr FallibleTapscriptTreeMgr) StoreTapscriptTree(ctx context.Context,
659+
treeNodes asset.TapscriptTreeNodes) (*chainhash.Hash, error) {
660+
661+
if mgr.FailStore {
662+
return nil, fmt.Errorf("unable to store tapscript tree")
663+
}
664+
665+
return mgr.store.StoreTapscriptTree(ctx, treeNodes)
666+
}
667+
668+
func NewFallibleTapscriptTreeMgr(store MintingStore) FallibleTapscriptTreeMgr {
669+
return FallibleTapscriptTreeMgr{
670+
store: store,
671+
}
672+
}
673+
674+
var _ asset.TapscriptTreeManager = (*FallibleTapscriptTreeMgr)(nil)

tapgarden/planter_test.go

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ type mintingTestHarness struct {
6666

6767
store tapgarden.MintingStore
6868

69+
treeStore *tapgarden.FallibleTapscriptTreeMgr
70+
6971
keyRing *tapgarden.MockKeyRing
7072

7173
genSigner *tapgarden.MockGenSigner
@@ -96,10 +98,12 @@ func newMintingTestHarness(t *testing.T, store tapgarden.MintingStore,
9698

9799
keyRing := tapgarden.NewMockKeyRing()
98100
genSigner := tapgarden.NewMockGenSigner(keyRing)
101+
treeMgr := tapgarden.NewFallibleTapscriptTreeMgr(store)
99102

100103
return &mintingTestHarness{
101104
T: t,
102105
store: store,
106+
treeStore: &treeMgr,
103107
ticker: ticker.NewForce(interval),
104108
wallet: tapgarden.NewMockWalletAnchor(),
105109
chain: tapgarden.NewMockChainBridge(),
@@ -126,6 +130,7 @@ func (t *mintingTestHarness) refreshChainPlanter() {
126130
Wallet: t.wallet,
127131
ChainBridge: t.chain,
128132
Log: t.store,
133+
TreeStore: t.treeStore,
129134
KeyRing: t.keyRing,
130135
GenSigner: t.genSigner,
131136
GenTxBuilder: t.genTxBuilder,
@@ -277,7 +282,7 @@ func (t *mintingTestHarness) assertFinalizeBatch(wg *sync.WaitGroup,
277282
// progressCaretaker uses the mock interfaces to progress a caretaker from start
278283
// to TX confirmation.
279284
func (t *mintingTestHarness) progressCaretaker(
280-
seedlings []*tapgarden.Seedling) func() {
285+
seedlings []*tapgarden.Seedling, batchSibling *chainhash.Hash) func() {
281286

282287
// Assert that the caretaker has requested a genesis TX to be funded.
283288
_ = t.assertGenesisTxFunded()
@@ -295,7 +300,7 @@ func (t *mintingTestHarness) progressCaretaker(
295300

296301
// We should now transition to the next state where we'll attempt to
297302
// sign this PSBT packet generated above.
298-
t.assertGenesisPsbtFinalized()
303+
t.assertGenesisPsbtFinalized(batchSibling)
299304

300305
// With the PSBT packet finalized for the caretaker, we should now
301306
// receive a request to publish a transaction followed by a
@@ -626,7 +631,9 @@ func (t *mintingTestHarness) assertSeedlingsMatchSprouts(
626631

627632
// assertGenesisPsbtFinalized asserts that a request to finalize the genesis
628633
// transaction has been requested by a caretaker.
629-
func (t *mintingTestHarness) assertGenesisPsbtFinalized() {
634+
func (t *mintingTestHarness) assertGenesisPsbtFinalized(
635+
sibling *chainhash.Hash) {
636+
630637
t.Helper()
631638

632639
// Ensure that a request to finalize the PSBt has come across.
@@ -650,7 +657,7 @@ func (t *mintingTestHarness) assertGenesisPsbtFinalized() {
650657

651658
// The minting key of the batch should match the public key
652659
// that was inserted into the wallet.
653-
batchKey, _, err := pendingBatch.MintingOutputKey()
660+
batchKey, _, err := pendingBatch.MintingOutputKey(sibling)
654661
require.NoError(t, err)
655662

656663
importedKey, err := fn.RecvOrTimeout(
@@ -791,7 +798,7 @@ func testBasicAssetCreation(t *mintingTestHarness) {
791798

792799
// We should now transition to the next state where we'll attempt to
793800
// sign this PSBT packet generated above.
794-
t.assertGenesisPsbtFinalized()
801+
t.assertGenesisPsbtFinalized(nil)
795802

796803
// With the PSBT packet finalized for the caretaker, we should now
797804
// receive a request to publish a transaction followed by a
@@ -893,7 +900,7 @@ func testMintingTicker(t *mintingTestHarness) {
893900

894901
// We should now transition to the next state where we'll attempt to
895902
// sign this PSBT packet generated above.
896-
t.assertGenesisPsbtFinalized()
903+
t.assertGenesisPsbtFinalized(nil)
897904

898905
// With the PSBT packet finalized for the caretaker, we should now
899906
// receive a request to publish a transaction followed by a
@@ -1029,7 +1036,7 @@ func testMintingCancelFinalize(t *mintingTestHarness) {
10291036

10301037
// We should now transition to the next state where we'll attempt to
10311038
// sign this PSBT packet generated above.
1032-
t.assertGenesisPsbtFinalized()
1039+
t.assertGenesisPsbtFinalized(nil)
10331040

10341041
// With the PSBT packet finalized for the caretaker, we should now
10351042
// receive a request to publish a transaction followed by a
@@ -1137,7 +1144,7 @@ func testFinalizeBatch(t *mintingTestHarness) {
11371144
t.finalizeBatch(&wg, respChan)
11381145
batchCount++
11391146

1140-
_ = t.progressCaretaker(seedlings)
1147+
_ = t.progressCaretaker(seedlings, nil)
11411148
caretakerCount++
11421149

11431150
t.assertFinalizeBatch(&wg, respChan, "")
@@ -1161,7 +1168,7 @@ func testFinalizeBatch(t *mintingTestHarness) {
11611168
t.finalizeBatch(&wg, respChan)
11621169
batchCount++
11631170

1164-
sendConfNtfn := t.progressCaretaker(seedlings)
1171+
sendConfNtfn := t.progressCaretaker(seedlings, nil)
11651172
caretakerCount++
11661173

11671174
// Trigger the confirmation event, which should cause the caretaker to
@@ -1191,7 +1198,7 @@ func testFinalizeBatch(t *mintingTestHarness) {
11911198
t.finalizeBatch(&wg, respChan)
11921199
batchCount++
11931200

1194-
sendConfNtfn = t.progressCaretaker(seedlings)
1201+
sendConfNtfn = t.progressCaretaker(seedlings, nil)
11951202
sendConfNtfn()
11961203

11971204
t.assertFinalizeBatch(&wg, respChan, "")

0 commit comments

Comments
 (0)