From dc692d50c0d54864b2acab9ff135e667d5dcc03f Mon Sep 17 00:00:00 2001 From: Jonathan Harvey-Buschel Date: Fri, 12 Jan 2024 18:07:30 -0500 Subject: [PATCH] tapgarden: propogate taptree sibling for batch key --- tapgarden/batch.go | 11 +++++++---- tapgarden/caretaker.go | 26 ++++++++++++++++++++++++-- tapgarden/planter_test.go | 28 ++++++++++++++++++---------- 3 files changed, 49 insertions(+), 16 deletions(-) diff --git a/tapgarden/batch.go b/tapgarden/batch.go index 4db82b374..57abed224 100644 --- a/tapgarden/batch.go +++ b/tapgarden/batch.go @@ -6,6 +6,7 @@ import ( "time" "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/commitment" @@ -104,7 +105,9 @@ func (m *MintingBatch) validateGroupAnchor(s *Seedling) error { // MintingOutputKey derives the output key that once mined, will commit to the // Taproot asset root, thereby creating the set of included assets. -func (m *MintingBatch) MintingOutputKey() (*btcec.PublicKey, []byte, error) { +func (m *MintingBatch) MintingOutputKey( + sibling *chainhash.Hash) (*btcec.PublicKey, []byte, error) { + if m.mintingPubKey != nil { return m.mintingPubKey, m.taprootAssetScriptRoot, nil } @@ -113,7 +116,7 @@ func (m *MintingBatch) MintingOutputKey() (*btcec.PublicKey, []byte, error) { return nil, nil, fmt.Errorf("no asset commitment present") } - taprootAssetScriptRoot := m.RootAssetCommitment.TapscriptRoot(nil) + taprootAssetScriptRoot := m.RootAssetCommitment.TapscriptRoot(sibling) m.taprootAssetScriptRoot = taprootAssetScriptRoot[:] m.mintingPubKey = txscript.ComputeTaprootOutputKey( @@ -125,8 +128,8 @@ func (m *MintingBatch) MintingOutputKey() (*btcec.PublicKey, []byte, error) { // genesisScript returns the script that should be placed in the minting output // within the genesis transaction. -func (m *MintingBatch) genesisScript() ([]byte, error) { - mintingOutputKey, _, err := m.MintingOutputKey() +func (m *MintingBatch) genesisScript(sibling *chainhash.Hash) ([]byte, error) { + mintingOutputKey, _, err := m.MintingOutputKey(sibling) if err != nil { return nil, err } diff --git a/tapgarden/caretaker.go b/tapgarden/caretaker.go index e1bbbd8a8..03d951376 100644 --- a/tapgarden/caretaker.go +++ b/tapgarden/caretaker.go @@ -665,10 +665,30 @@ func (b *BatchCaretaker) stateStep(currentState BatchState) (BatchState, error) b.cfg.Batch.RootAssetCommitment = tapCommitment + // Fetch the optional Tapscript tree for this batch, and convert + // it to a TapHash. + batchKey := asset.ToSerialized(b.cfg.Batch.BatchKey.PubKey) + batchTapscriptTree, err := b.cfg.TreeStore.LoadTapscriptTree( + ctx, batchKey, + ) + if err != asset.TreeNotFound { + return 0, err + } + + var tapSibling *chainhash.Hash + if batchTapscriptTree != nil { + tapSibling, err = tapscript.TapTreeToSibling( + *batchTapscriptTree, + ) + if err != nil { + return 0, err + } + } + // With the commitment Taproot Asset root SMT constructed, we'll // map that into the tapscript root we'll insert into the // genesis transaction. - genesisScript, err := b.cfg.Batch.genesisScript() + genesisScript, err := b.cfg.Batch.genesisScript(tapSibling) if err != nil { return 0, fmt.Errorf("unable to create genesis "+ "script: %v", err) @@ -766,7 +786,9 @@ func (b *BatchCaretaker) stateStep(currentState BatchState) (BatchState, error) // // TODO(roasbeef): re-run during the broadcast phase to ensure // it's fully imported? - mintingOutputKey, tapRoot, err := b.cfg.Batch.MintingOutputKey() + mintingOutputKey, tapRoot, err := b.cfg.Batch.MintingOutputKey( + nil, + ) if err != nil { return 0, err } diff --git a/tapgarden/planter_test.go b/tapgarden/planter_test.go index 47aeba183..366d261d8 100644 --- a/tapgarden/planter_test.go +++ b/tapgarden/planter_test.go @@ -65,6 +65,8 @@ type mintingTestHarness struct { store tapgarden.MintingStore + treeStore *asset.MockTapscriptTreeStore + keyRing *tapgarden.MockKeyRing genSigner *tapgarden.MockGenSigner @@ -99,6 +101,7 @@ func newMintingTestHarness(t *testing.T, store tapgarden.MintingStore, return &mintingTestHarness{ T: t, store: store, + treeStore: asset.NewMockTapscriptTreeStore(), ticker: ticker.NewForce(interval), wallet: tapgarden.NewMockWalletAnchor(), chain: tapgarden.NewMockChainBridge(), @@ -125,6 +128,7 @@ func (t *mintingTestHarness) refreshChainPlanter() { Wallet: t.wallet, ChainBridge: t.chain, Log: t.store, + TreeStore: t.treeStore, KeyRing: t.keyRing, GenSigner: t.genSigner, GenTxBuilder: t.genTxBuilder, @@ -276,7 +280,7 @@ func (t *mintingTestHarness) assertFinalizeBatch(wg *sync.WaitGroup, // progressCaretaker uses the mock interfaces to progress a caretaker from start // to TX confirmation. func (t *mintingTestHarness) progressCaretaker( - seedlings []*tapgarden.Seedling) func() { + seedlings []*tapgarden.Seedling, batchSibling *chainhash.Hash) func() { // Assert that the caretaker has requested a genesis TX to be funded. _ = t.assertGenesisTxFunded() @@ -294,7 +298,7 @@ func (t *mintingTestHarness) progressCaretaker( // We should now transition to the next state where we'll attempt to // sign this PSBT packet generated above. - t.assertGenesisPsbtFinalized() + t.assertGenesisPsbtFinalized(batchSibling) // With the PSBT packet finalized for the caretaker, we should now // receive a request to publish a transaction followed by a @@ -625,7 +629,9 @@ func (t *mintingTestHarness) assertSeedlingsMatchSprouts( // assertGenesisPsbtFinalized asserts that a request to finalize the genesis // transaction has been requested by a caretaker. -func (t *mintingTestHarness) assertGenesisPsbtFinalized() { +func (t *mintingTestHarness) assertGenesisPsbtFinalized( + sibling *chainhash.Hash) { + t.Helper() // Ensure that a request to finalize the PSBt has come across. @@ -649,7 +655,7 @@ func (t *mintingTestHarness) assertGenesisPsbtFinalized() { // The minting key of the batch should match the public key // that was inserted into the wallet. - batchKey, _, err := pendingBatch.MintingOutputKey() + batchKey, _, err := pendingBatch.MintingOutputKey(sibling) require.NoError(t, err) importedKey, err := fn.RecvOrTimeout( @@ -790,7 +796,7 @@ func testBasicAssetCreation(t *mintingTestHarness) { // We should now transition to the next state where we'll attempt to // sign this PSBT packet generated above. - t.assertGenesisPsbtFinalized() + t.assertGenesisPsbtFinalized(nil) // With the PSBT packet finalized for the caretaker, we should now // receive a request to publish a transaction followed by a @@ -892,7 +898,7 @@ func testMintingTicker(t *mintingTestHarness) { // We should now transition to the next state where we'll attempt to // sign this PSBT packet generated above. - t.assertGenesisPsbtFinalized() + t.assertGenesisPsbtFinalized(nil) // With the PSBT packet finalized for the caretaker, we should now // receive a request to publish a transaction followed by a @@ -1028,7 +1034,7 @@ func testMintingCancelFinalize(t *mintingTestHarness) { // We should now transition to the next state where we'll attempt to // sign this PSBT packet generated above. - t.assertGenesisPsbtFinalized() + t.assertGenesisPsbtFinalized(nil) // With the PSBT packet finalized for the caretaker, we should now // receive a request to publish a transaction followed by a @@ -1136,7 +1142,7 @@ func testFinalizeBatch(t *mintingTestHarness) { t.finalizeBatch(&wg, respChan) batchCount++ - _ = t.progressCaretaker(seedlings) + _ = t.progressCaretaker(seedlings, nil) caretakerCount++ t.assertFinalizeBatch(&wg, respChan, "") @@ -1160,7 +1166,7 @@ func testFinalizeBatch(t *mintingTestHarness) { t.finalizeBatch(&wg, respChan) batchCount++ - sendConfNtfn := t.progressCaretaker(seedlings) + sendConfNtfn := t.progressCaretaker(seedlings, nil) caretakerCount++ // Trigger the confirmation event, which should cause the caretaker to @@ -1190,7 +1196,7 @@ func testFinalizeBatch(t *mintingTestHarness) { t.finalizeBatch(&wg, respChan) batchCount++ - sendConfNtfn = t.progressCaretaker(seedlings) + sendConfNtfn = t.progressCaretaker(seedlings, nil) sendConfNtfn() t.assertFinalizeBatch(&wg, respChan, "") @@ -1198,6 +1204,8 @@ func testFinalizeBatch(t *mintingTestHarness) { t.assertNoPendingBatch() t.assertNumCaretakersActive(caretakerCount) t.assertLastBatchState(batchCount, tapgarden.BatchStateFinalized) + + // TODO(jhb): add finalize with tapscript root } // mintingStoreTestCase is used to programmatically run a series of test cases