From 7719833387ba86bebe3b66c31e8b35277db6f237 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 10 Oct 2023 19:27:10 +0200 Subject: [PATCH 01/11] asset: fix copy to preserve deep equality --- asset/asset.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/asset/asset.go b/asset/asset.go index 7051f33c1..f423657c5 100644 --- a/asset/asset.go +++ b/asset/asset.go @@ -1192,8 +1192,13 @@ func (a *Asset) Copy() *Asset { if a.ScriptKey.TweakedScriptKey != nil { assetCopy.ScriptKey.TweakedScriptKey = &TweakedScriptKey{} assetCopy.ScriptKey.RawKey = a.ScriptKey.RawKey - assetCopy.ScriptKey.Tweak = make([]byte, len(a.ScriptKey.Tweak)) - copy(assetCopy.ScriptKey.Tweak, a.ScriptKey.Tweak) + + if len(a.ScriptKey.Tweak) > 0 { + assetCopy.ScriptKey.Tweak = make( + []byte, len(a.ScriptKey.Tweak), + ) + copy(assetCopy.ScriptKey.Tweak, a.ScriptKey.Tweak) + } } if a.GroupKey != nil { From 1540534de2f9b86b9658e97ec734cb0acdbe1a10 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 10 Oct 2023 19:27:12 +0200 Subject: [PATCH 02/11] tapdb: fix grouped assets being overwritten Because grouped assets will have the same Tap level key, the NewTapCommitments function will overwrite all grouped assets one after each other instead of merging them. So we use the more easy to use FromAssets function instead. We'll fix the behavior of NewTapCommitments in the next commit. --- tapdb/asset_minting.go | 18 +++++------------- tapdb/asset_minting_test.go | 15 ++++++--------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/tapdb/asset_minting.go b/tapdb/asset_minting.go index 12690a27e..de20ea173 100644 --- a/tapdb/asset_minting.go +++ b/tapdb/asset_minting.go @@ -541,9 +541,9 @@ func fetchAssetSprouts(ctx context.Context, q PendingAssetStore, return nil, fmt.Errorf("unable to fetch batch assets: %w", err) } - // For each sprout, we'll create a new asset commitment which will be a - // leaf at the top-level Taproot Asset commitment. - assetCommitments := make([]*commitment.AssetCommitment, len(dbSprout)) + // We collect all the sprouts into fully grown assets, from which we'll + // then create asset and tap level commitments. + assetSprouts := make([]*asset.Asset, len(dbSprout)) for i, sprout := range dbSprout { // First, we'll decode the script key which very asset must // specify, and populate the key locator information @@ -649,18 +649,10 @@ func fetchAssetSprouts(ctx context.Context, q PendingAssetStore, // TODO(roasbeef): need to update the above to set the // witnesses of a valid asset - // Finally make a new asset commitment from this sprout and - // accumulate it along the rest of the assets. - assetCommitment, err := commitment.NewAssetCommitment( - assetSprout, - ) - if err != nil { - return nil, err - } - assetCommitments[i] = assetCommitment + assetSprouts[i] = assetSprout } - tapCommitment, err := commitment.NewTapCommitment(assetCommitments...) + tapCommitment, err := commitment.FromAssets(assetSprouts...) if err != nil { return nil, err } diff --git a/tapdb/asset_minting_test.go b/tapdb/asset_minting_test.go index 3da0c3142..d3486abc0 100644 --- a/tapdb/asset_minting_test.go +++ b/tapdb/asset_minting_test.go @@ -1092,11 +1092,9 @@ func TestGroupAnchors(t *testing.T) { orderedSeedlings := tapgarden.SortSeedlings(maps.Values(seedlings)) for _, seedlingName := range orderedSeedlings { seedling := seedlings[seedlingName] - require.NoError(t, - assetStore.AddSeedlingsToBatch( - ctx, batchKey, seedling, - ), "unable to write seedlings: %v", err, - ) + require.NoError(t, assetStore.AddSeedlingsToBatch( + ctx, batchKey, seedling, + )) } // If we read the batch from disk again, then we should have 20 total @@ -1109,10 +1107,9 @@ func TestGroupAnchors(t *testing.T) { badGrouped := seedlings[secondGrouped] badAnchorName := secondAnchor + secondGrouped badGrouped.GroupAnchor = &badAnchorName - require.ErrorContains(t, - assetStore.AddSeedlingsToBatch( - ctx, batchKey, badGrouped, - ), "no rows in result set", + require.ErrorContains( + t, assetStore.AddSeedlingsToBatch(ctx, batchKey, badGrouped), + "no rows in result set", ) seedlings[secondGrouped].GroupAnchor = &secondAnchor From 1291077594de789dae5fc928eccfcd4079c60eb4 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 10 Oct 2023 19:27:13 +0200 Subject: [PATCH 03/11] asset+commitment: properly merge asset commitments This commit fixes an issue with the NewTapCommitment function which used to overwrite asset commitments if they had the same Taproot Asset commitment key (which is true for grouped assets for example). --- asset/mock.go | 4 +- commitment/commitment_test.go | 43 ++++++++++++++++++- commitment/tap.go | 33 ++++++++++---- tappsbt/testdata/psbt_encoding_generated.json | 18 ++++---- vm/testdata/vm_validation_generated.json | 14 +++--- .../vm_validation_generated_error_cases.json | 4 +- 6 files changed, 86 insertions(+), 30 deletions(-) diff --git a/asset/mock.go b/asset/mock.go index b99489eb4..8772d5842 100644 --- a/asset/mock.go +++ b/asset/mock.go @@ -358,7 +358,7 @@ func RandAssetWithValues(t testing.TB, genesis Genesis, groupKey *GroupKey, t.Helper() - units := test.RandInt[uint64]() + 1 + units := test.RandInt[uint32]() + 1 switch genesis.Type { case Normal: @@ -376,7 +376,7 @@ func RandAssetWithValues(t testing.TB, genesis Genesis, groupKey *GroupKey, } a, err := New( - genesis, units, 0, 0, scriptKey, groupKey, + genesis, uint64(units), 0, 0, scriptKey, groupKey, WithAssetVersion(assetVersion), ) require.NoError(t, err) diff --git a/commitment/commitment_test.go b/commitment/commitment_test.go index 0280a03e9..b3e667912 100644 --- a/commitment/commitment_test.go +++ b/commitment/commitment_test.go @@ -1082,6 +1082,12 @@ func TestUpdateTapCommitment(t *testing.T) { groupKey1 := asset.RandGroupKey(t, genesis1, protoAsset1) groupKey2 := asset.RandGroupKey(t, genesis2, protoAsset2) + // We also create a thirds asset which is in the same group as the first + // one, to ensure that we can properly create Taproot Asset commitments + // from asset commitments of the same group. + genesis3 := asset.RandGenesis(t, asset.Normal) + asset3 := randAsset(t, genesis3, groupKey1) + asset1 := protoAsset1.Copy() asset1.GroupKey = groupKey1 @@ -1097,10 +1103,45 @@ func TestUpdateTapCommitment(t *testing.T) { require.NoError(t, err) commitmentKey2 := assetCommitment2.TapCommitmentKey() + assetCommitment3, err := NewAssetCommitment(asset3) + require.NoError(t, err) + commitmentKey3 := assetCommitment3.TapCommitmentKey() + + // When creating a Taproot Asset commitment from all three assets, we + // expect two commitments to be created, one for each group. + cp1, err := assetCommitment1.Copy() + require.NoError(t, err) + cp2, err := assetCommitment2.Copy() + require.NoError(t, err) + cp3, err := assetCommitment3.Copy() + require.NoError(t, err) + commitment, err := NewTapCommitment(cp1, cp2, cp3) + require.NoError(t, err) + require.Len(t, commitment.Commitments(), 2) + require.Len(t, commitment.CommittedAssets(), 3) + + require.Equal(t, commitmentKey1, commitmentKey3) + + // Make sure we can still generate proper proofs for all assets. + p1, _, err := commitment.Proof( + commitmentKey1, asset1.AssetCommitmentKey(), + ) + require.NoError(t, err) + require.True(t, p1.DeepEqual(asset1)) + p2, _, err := commitment.Proof( + commitmentKey2, asset2.AssetCommitmentKey(), + ) + require.NoError(t, err) + require.True(t, p2.DeepEqual(asset2)) + p3, _, err := commitment.Proof( + commitmentKey3, asset3.AssetCommitmentKey(), + ) + require.NoError(t, err) + require.True(t, p3.DeepEqual(asset3)) // Mint a new Taproot Asset commitment with only the first // assetCommitment. - commitment, err := NewTapCommitment(assetCommitment1) + commitment, err = NewTapCommitment(assetCommitment1) require.NoError(t, err) copyOfCommitment, err := NewTapCommitment(assetCommitment1) diff --git a/commitment/tap.go b/commitment/tap.go index d37e55424..ffbccd2ee 100644 --- a/commitment/tap.go +++ b/commitment/tap.go @@ -78,18 +78,33 @@ type TapCommitment struct { // NewTapCommitment creates a new Taproot Asset commitment for the given asset // commitments capable of computing merkle proofs. -func NewTapCommitment(assets ...*AssetCommitment) (*TapCommitment, error) { +func NewTapCommitment(newCommitments ...*AssetCommitment) (*TapCommitment, + error) { + maxVersion := asset.V0 tree := mssmt.NewCompactedTree(mssmt.NewDefaultStore()) - assetCommitments := make(AssetCommitments, len(assets)) - for _, asset := range assets { - asset := asset + assetCommitments := make(AssetCommitments, len(newCommitments)) + for idx := range newCommitments { + assetCommitment := newCommitments[idx] - if asset.Version > maxVersion { - maxVersion = asset.Version + if assetCommitment.Version > maxVersion { + maxVersion = assetCommitment.Version } - key := asset.TapCommitmentKey() - leaf := asset.TapCommitmentLeaf() + key := assetCommitment.TapCommitmentKey() + + // Do we already have an asset commitment for this key? If so, + // we need to merge them together. + existingCommitment, ok := assetCommitments[key] + if ok { + err := existingCommitment.Merge(assetCommitment) + if err != nil { + return nil, err + } + + assetCommitment = existingCommitment + } + + leaf := assetCommitment.TapCommitmentLeaf() // TODO(bhandras): thread the context through. _, err := tree.Insert(context.TODO(), key, leaf) @@ -97,7 +112,7 @@ func NewTapCommitment(assets ...*AssetCommitment) (*TapCommitment, error) { return nil, err } - assetCommitments[key] = asset + assetCommitments[key] = assetCommitment } root, err := tree.Root(context.Background()) diff --git a/tappsbt/testdata/psbt_encoding_generated.json b/tappsbt/testdata/psbt_encoding_generated.json index eaf266ad7..7109eb660 100644 --- a/tappsbt/testdata/psbt_encoding_generated.json +++ b/tappsbt/testdata/psbt_encoding_generated.json @@ -169,7 +169,7 @@ "genesis_meta_hash": "1fb586b14323a6bc8f9e7df1d929333ff993933bea6f5b3af6de0374366c4719", "genesis_output_index": 3197016449, "genesis_type": 0, - "amount": 2873287401706343735, + "amount": 609209655, "lock_time": 0, "relative_lock_time": 0, "prev_witnesses": [ @@ -180,7 +180,7 @@ "script_key": "000000000000000000000000000000000000000000000000000000000000000000" }, "tx_witness": [ - "9ca48fa123a923de60d62bd8f5122db894939bd2718d73e884d282644818cd2c8daae166b56a2051b93ec8f00bec736c0c8aa5cdba0c5030e6faa6ceb6673b60" + "189c0126f90da1ce11a0826fa3e81feda8e44d9916a52c660a12e093ca00795b130085141b7d23cb8d210351c00c61072757e707f5978d227ebc6093b6b448c7" ], "split_commitment": null } @@ -261,7 +261,7 @@ "genesis_meta_hash": "63afa467d49dec6a40e9a1d007f033c2823061bdd0eaa59f8e4da6430105220d", "genesis_output_index": 654045851, "genesis_type": 0, - "amount": 3724427934598140042, + "amount": 1094946954, "lock_time": 0, "relative_lock_time": 0, "prev_witnesses": [ @@ -272,7 +272,7 @@ "script_key": "000000000000000000000000000000000000000000000000000000000000000000" }, "tx_witness": [ - "d950c3393d2e8f7180c4e56d6601ec7f3a9f1cf1fd59a79f52969f2570c592b30757f3201d64e3b959e4f99a3ada35f03ab6d7cb350798740c13331d46132333" + "e146db011035548ffd57d9b36b69834268212414ca5ae2a1e6f47b411eac70ade3b4f60f4668bf277ade3ed0e4d45ca13902de5904e52e4f6adba355df03a963" ], "split_commitment": null } @@ -291,7 +291,7 @@ "genesis_meta_hash": "63afa467d49dec6a40e9a1d007f033c2823061bdd0eaa59f8e4da6430105220d", "genesis_output_index": 654045851, "genesis_type": 0, - "amount": 3724427934598140042, + "amount": 1094946954, "lock_time": 0, "relative_lock_time": 0, "prev_witnesses": [ @@ -302,7 +302,7 @@ "script_key": "000000000000000000000000000000000000000000000000000000000000000000" }, "tx_witness": [ - "d950c3393d2e8f7180c4e56d6601ec7f3a9f1cf1fd59a79f52969f2570c592b30757f3201d64e3b959e4f99a3ada35f03ab6d7cb350798740c13331d46132333" + "e146db011035548ffd57d9b36b69834268212414ca5ae2a1e6f47b411eac70ade3b4f60f4668bf277ade3ed0e4d45ca13902de5904e52e4f6adba355df03a963" ], "split_commitment": null } @@ -362,7 +362,7 @@ "genesis_meta_hash": "63afa467d49dec6a40e9a1d007f033c2823061bdd0eaa59f8e4da6430105220d", "genesis_output_index": 654045851, "genesis_type": 0, - "amount": 3724427934598140042, + "amount": 1094946954, "lock_time": 0, "relative_lock_time": 0, "prev_witnesses": [ @@ -373,7 +373,7 @@ "script_key": "000000000000000000000000000000000000000000000000000000000000000000" }, "tx_witness": [ - "d950c3393d2e8f7180c4e56d6601ec7f3a9f1cf1fd59a79f52969f2570c592b30757f3201d64e3b959e4f99a3ada35f03ab6d7cb350798740c13331d46132333" + "e146db011035548ffd57d9b36b69834268212414ca5ae2a1e6f47b411eac70ade3b4f60f4668bf277ade3ed0e4d45ca13902de5904e52e4f6adba355df03a963" ], "split_commitment": null } @@ -396,7 +396,7 @@ "version": 0, "chain_params_hrp": "tapbc" }, - "expected": "cHNidP8BALICAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACewAAAAAAAAAiUSCJ8JbNbyRfMqLwI05SkskNl9PQoEOOxhOdHbR0nx2w61kBAAAAAAAAIlEgifCWzW8kXzKi8CNOUpLJDZfT0KBDjsYTnR20dJ8dsOsAAAAAAXABAQFxBXRhcGJjAXIBAAAiBgIc6f3TKwqPpgc8hXE0FknfIB3G6OVaMRSAcgCZLm7OChgAAAAA+QMAgAAAAIB7AACAAAAAAMgBAAAhFhzp/dMrCo+mBzyFcTQWSd8gHcbo5VoxFIByAJkubs4KGQAAAAAA+QMAgAAAAIB7AACAAAAAAMgBAAABFyAc6f3TKwqPpgc8hXE0FknfIB3G6OVaMRSAcgCZLm7OCgEYC21lcmtsZSByb290AXBl9PdDkQQDdPaSS5jL+HE/jZYtfI0BkZLCQiTiyvzK46Za0UX0k0eACXnRgwNW8qVMPeqypLRHXWOvvo+1aYfHf1gYUm8CIm1MYs8cxLlZVTRYZ3fnI6nGqsSfK5RrSsDH13gvpVcBcQgAAAAAAAADCQFyD2FuY2hvciBwa3NjcmlwdAFzCAAAAAAAAAADAXQhAhzp/dMrCo+mBzyFcTQWSd8gHcbo5VoxFIByAJkubs4KAXULbWVya2xlIHJvb3QidgIc6f3TKwqPpgc8hXE0FknfIB3G6OVaMRSAcgCZLm7OChgAAAAA+QMAgAAAAIB7AACAAAAAAMgBAAAhdxzp/dMrCo+mBzyFcTQWSd8gHcbo5VoxFIByAJkubs4KGQAAAAAA+QMAgAAAAIB7AACAAAAAAMgBAAABeAdzaWJsaW5nAXn9lgEAAQECiuQ6G/VzmBZZpE/xekxyFaO1OeseWEnGB327VyL1cXoovIl9BkAxZmI1ODZiMTQzMjNhNmJjOGY5ZTdkZjFkOTI5MzMzZmY5OTM5MzNiZWE2ZjViM2FmNmRlMDM3NDM2NmM0NzE5H7WGsUMjpryPnn3x2SkzP/mTkzvqb1s69t4DdDZsRxm+jpmBAAQBAAYJ/yff96skT803C60BqwFlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQgFAnKSPoSOpI95g1ivY9RItuJSTm9JxjXPohNKCZEgYzSyNquFmtWogUbk+yPAL7HNsDIqlzboMUDDm+qbOtmc7YA4CAAAQIQKYHWvLYXtJhnOyFFMAP3VeWCotu0AD9f+h8VoTyV29NREhAsFUFcmOySjOlb3vPoP1b6LesXc4cojv/zli2tJsgXyTAXoPdGhpcyBpcyBhIHByb29mAAFwZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXEIAAAAAAAAAAABcgABcwgAAAAAAAAAAAF1AAF4AAF6AAABcAEBAXEBAQFyCAAAAAAAAAAAAXMhAhzp/dMrCo+mBzyFcTQWSd8gHcbo5VoxFIByAJkubs4KInQCHOn90ysKj6YHPIVxNBZJ3yAdxujlWjEUgHIAmS5uzgoYAAAAAPkDAIAAAACAewAAgAAAAADIAQAAIXUc6f3TKwqPpgc8hXE0FknfIB3G6OVaMRSAcgCZLm7OChkAAAAAAPkDAIAAAACAewAAgAAAAADIAQAAAXb9lgEAAQECigvzypk26EYfENd8luqAp6Zl9gb2pjt/Pf0lZ8GJeeTWc4toKUA2M2FmYTQ2N2Q0OWRlYzZhNDBlOWExZDAwN2YwMzNjMjgyMzA2MWJkZDBlYWE1OWY4ZTRkYTY0MzAxMDUyMjBkY6+kZ9Sd7GpA6aHQB/AzwoIwYb3Q6qWfjk2mQwEFIg0m+/KbAAQBAAYJ/zOv03lBQ5CKC60BqwFlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQgFA2VDDOT0uj3GAxOVtZgHsfzqfHPH9WaefUpafJXDFkrMHV/MgHWTjuVnk+Zo62jXwOrbXyzUHmHQMEzMdRhMjMw4CAAAQIQKJ8JbNbyRfMqLwI05SkskNl9PQoEOOxhOdHbR0nx2w6xEhAjsEDI31CyN0QD/sN503AHb2pqG5fcO3Id9ARd8LGGogAXf9lgEAAQECigvzypk26EYfENd8luqAp6Zl9gb2pjt/Pf0lZ8GJeeTWc4toKUA2M2FmYTQ2N2Q0OWRlYzZhNDBlOWExZDAwN2YwMzNjMjgyMzA2MWJkZDBlYWE1OWY4ZTRkYTY0MzAxMDUyMjBkY6+kZ9Sd7GpA6aHQB/AzwoIwYb3Q6qWfjk2mQwEFIg0m+/KbAAQBAAYJ/zOv03lBQ5CKC60BqwFlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQgFA2VDDOT0uj3GAxOVtZgHsfzqfHPH9WaefUpafJXDFkrMHV/MgHWTjuVnk+Zo62jXwOrbXyzUHmHQMEzMdRhMjMw4CAAAQIQKJ8JbNbyRfMqLwI05SkskNl9PQoEOOxhOdHbR0nx2w6xEhAjsEDI31CyN0QD/sN503AHb2pqG5fcO3Id9ARd8LGGogAXgVAMASbm90IGEgdmFsaWQgc2NyaXB0AXkBAQABcAEBAXEBAAFyCAAAAAAAAAABAXMhAhzp/dMrCo+mBzyFcTQWSd8gHcbo5VoxFIByAJkubs4KInQCHOn90ysKj6YHPIVxNBZJ3yAdxujlWjEUgHIAmS5uzgoYAAAAAPkDAIAAAACAewAAgAAAAADIAQAAIXUc6f3TKwqPpgc8hXE0FknfIB3G6OVaMRSAcgCZLm7OChkAAAAAAPkDAIAAAACAewAAgAAAAADIAQAAAXb9lgEAAQECigvzypk26EYfENd8luqAp6Zl9gb2pjt/Pf0lZ8GJeeTWc4toKUA2M2FmYTQ2N2Q0OWRlYzZhNDBlOWExZDAwN2YwMzNjMjgyMzA2MWJkZDBlYWE1OWY4ZTRkYTY0MzAxMDUyMjBkY6+kZ9Sd7GpA6aHQB/AzwoIwYb3Q6qWfjk2mQwEFIg0m+/KbAAQBAAYJ/zOv03lBQ5CKC60BqwFlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQgFA2VDDOT0uj3GAxOVtZgHsfzqfHPH9WaefUpafJXDFkrMHV/MgHWTjuVnk+Zo62jXwOrbXyzUHmHQMEzMdRhMjMw4CAAAQIQKJ8JbNbyRfMqLwI05SkskNl9PQoEOOxhOdHbR0nx2w6xEhAjsEDI31CyN0QD/sN503AHb2pqG5fcO3Id9ARd8LGGogAXhBARl84i0SvFqZWHUzr0EWn6Hcn/hmwNTTAhFY1ikzZy0RGXziLRK8WplYdTOvQRafodyf+GbA1NMCEVjWKTNnLREBeQEBAA==", + "expected": "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", "comment": "random packet" } ], diff --git a/vm/testdata/vm_validation_generated.json b/vm/testdata/vm_validation_generated.json index 76584c28d..7b1235fb5 100644 --- a/vm/testdata/vm_validation_generated.json +++ b/vm/testdata/vm_validation_generated.json @@ -43,7 +43,7 @@ "genesis_meta_hash": "8afa3f85b8a62708caebbac880b5b89b93da53810164402104e648b6226a1b78", "genesis_output_index": 327692610, "genesis_type": 0, - "amount": 6933583034365165053, + "amount": 3087187453, "lock_time": 0, "relative_lock_time": 0, "prev_witnesses": [ @@ -54,7 +54,7 @@ "script_key": "000000000000000000000000000000000000000000000000000000000000000000" }, "tx_witness": [ - "f628bda4ee5a82c1465810b64fc808cef2e1d191e96172b8b9e6535c9057feebf033bc5b2f503c87086218ff8bbd4f704d340b755f711f6cbd94383814340c54" + "be6027ac2d3cef07abb355fee5e8786bb7e0841c1c3461af7deeb82161f93fd8092e12f3c15160267d7445cfb65efb48f1ac74b2895fa2de2e5b4467d3c6963d" ], "split_commitment": null } @@ -149,7 +149,7 @@ "genesis_meta_hash": "23e196c9dfff7fbaff4ffe94f4589733e563e19d3045aad3e226488ac02cca42", "genesis_output_index": 164677904, "genesis_type": 0, - "amount": 13637201695855811026, + "amount": 2077694418, "lock_time": 0, "relative_lock_time": 0, "prev_witnesses": [ @@ -160,7 +160,7 @@ "script_key": "0290e890b331192e241b29d9ff916cdb85c53be4b3afee55028fa75307d8c86b41" }, "tx_witness": [ - "e91d45c0a83f5e57410201c19b3145413097d1764fa21c2d4494affe86b6c592cef0054a5dfdc8e0210c6615ce17525698de65f593c3049bd61859b9f520378e" + "68563c1630ff6b93a3749fbdc8bd10eee6778dd466f4c44d8101db824e276b22f1e6958abc790f47f9965356ea744e119225090084bb2faa645e2328e2362414" ], "split_commitment": null }, @@ -171,7 +171,7 @@ "script_key": "02a382379d9e1635e23369627a19c587e751e447f073dafc92bdcfb89630eccc6a" }, "tx_witness": [ - "06f89772b022ce52821ec2e204f15cdc6f2ca8e10413d1f6c7d5e73a12af7da6987c72527c6821830f3d83c325e6f1d48427db9972647fd25468cec8228239ed", + "edc2c4951995bc82659d090dc4f61f533f27b4e835a16e66cfbb5162c3c709eb331a6bf4ba3ea6c59a5206f8daa05d5b7754702e4e67b73efc6ccfe2467e0b02", "20a354a15678381931fb574954a609244fc0b601830cac80bf6774882bfcf2e610ad56b2", "c1a354a15678381931fb574954a609244fc0b601830cac80bf6774882bfcf2e610" ], @@ -200,7 +200,7 @@ "genesis_meta_hash": "23e196c9dfff7fbaff4ffe94f4589733e563e19d3045aad3e226488ac02cca42", "genesis_output_index": 164677904, "genesis_type": 0, - "amount": 8695401978176517618, + "amount": 1752217074, "lock_time": 0, "relative_lock_time": 0, "prev_witnesses": [ @@ -235,7 +235,7 @@ "genesis_meta_hash": "01c0ab7ac65e502d39b216cbc50e73a32eaf936401e2506bd8b82c30d346bc4b", "genesis_output_index": 231487098, "genesis_type": 0, - "amount": 4941799717679293408, + "amount": 325477344, "lock_time": 0, "relative_lock_time": 6, "prev_witnesses": [ diff --git a/vm/testdata/vm_validation_generated_error_cases.json b/vm/testdata/vm_validation_generated_error_cases.json index 29ad8838b..5065bcf04 100644 --- a/vm/testdata/vm_validation_generated_error_cases.json +++ b/vm/testdata/vm_validation_generated_error_cases.json @@ -307,7 +307,7 @@ "genesis_meta_hash": "cde5e60c5ead6fc7ae77ba1d259b188a4b21c86fbc23d728b45347eada650af2", "genesis_output_index": 2905736656, "genesis_type": 0, - "amount": 8262326810234485413, + "amount": 1312193189, "lock_time": 0, "relative_lock_time": 0, "prev_witnesses": [ @@ -318,7 +318,7 @@ "script_key": "000000000000000000000000000000000000000000000000000000000000000000" }, "tx_witness": [ - "78dd6640c34a21aafecf552e44a4d8b957d8070f6a30a3ef7db2f31b9619220b0d696430969ea2cd972dc863c9301ea3b858319a09678c87b2d9177f5568e635" + "6c53c1b89d7ad7a42c438dc62b9515e477620e759f698a09fa75adffe698a129dc3913ba1812397e45fc8178cb1533e40975df74cc68eb3c927240f078a45f4f" ], "split_commitment": null } From 89082cd0682a08cae7fa947248f083769e7e2a9f Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 10 Oct 2023 19:27:14 +0200 Subject: [PATCH 04/11] multi: rename asset commitment's AssetID to TapKey The AssetCommitment had a field called AssetID that was quite confusing and misleading because for grouped assets this would be the hashed group key instead of the asset ID. To make the use of the field more clear and to avoid bugs (and actually fix a bug in a test), we rename the field to TapKey. --- commitment/asset.go | 38 +++++++++---------- commitment/commitment_test.go | 2 +- commitment/encoding.go | 4 +- commitment/mock.go | 8 ++-- commitment/proof.go | 8 ++-- commitment/tap.go | 2 +- .../proof_tlv_encoding_generated.json | 20 +++++----- .../testdata/proof_tlv_encoding_regtest.json | 32 ++++++++-------- tapscript/send_test.go | 2 +- 9 files changed, 57 insertions(+), 59 deletions(-) diff --git a/commitment/asset.go b/commitment/asset.go index 22965ea2d..510e35444 100644 --- a/commitment/asset.go +++ b/commitment/asset.go @@ -8,7 +8,7 @@ import ( "errors" "fmt" - "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/btcsuite/btcd/btcec/v2" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/mssmt" @@ -61,11 +61,11 @@ type AssetCommitment struct { // Version is the max version of the assets committed. Version asset.Version - // AssetID is the common identifier for all assets found within the + // TapKey is the common identifier for all assets found within the // AssetCommitment. This can either be an asset.ID, which every // committed asset must match, or the hash of an asset.GroupKey which // every committed asset must match if their asset.ID differs. - AssetID [32]byte + TapKey [32]byte // AssetType is the type of asset(s) committed to within the tree. AssetType asset.Type @@ -98,7 +98,7 @@ func parseCommon(assets ...*asset.Asset) (*AssetCommitment, error) { assetType asset.Type tapCommitmentKey [32]byte maxVersion = asset.Version(0) - assetGenesis = assets[0].Genesis.ID() + firstAssetID = assets[0].Genesis.ID() assetGroupKey = assets[0].GroupKey assetsMap = make(CommittedAssets, len(assets)) ) @@ -125,7 +125,7 @@ func parseCommon(assets ...*asset.Asset) (*AssetCommitment, error) { return nil, ErrAssetGroupKeyMismatch case assetGroupKey == nil: - if assetGenesis != newAsset.Genesis.ID() { + if firstAssetID != newAsset.Genesis.ID() { return nil, ErrAssetGenesisMismatch } } @@ -151,22 +151,20 @@ func parseCommon(assets ...*asset.Asset) (*AssetCommitment, error) { assetsMap[key] = newAsset } - // The assetID here is what will be used to place this asset commitment + var groupPubKey *btcec.PublicKey + if assetGroupKey != nil { + groupPubKey = &assetGroupKey.GroupPubKey + } + + // The tapKey here is what will be used to place this asset commitment // into the top-level Taproot Asset commitment. For assets without a // group key, then this will be the normal asset ID. Otherwise, this'll // be the sha256 of the group key. - var assetID [32]byte - if assetGroupKey == nil { - assetID = assetGenesis - } else { - assetID = sha256.Sum256( - schnorr.SerializePubKey(&assetGroupKey.GroupPubKey), - ) - } + tapKey := asset.TapCommitmentKey(firstAssetID, groupPubKey) return &AssetCommitment{ Version: maxVersion, - AssetID: assetID, + TapKey: tapKey, AssetType: assetType, assets: assetsMap, }, nil @@ -221,7 +219,7 @@ func (c *AssetCommitment) Upsert(newAsset *asset.Asset) error { // The given Asset must have an ID that matches the AssetCommitment ID. // The AssetCommitment ID is either a hash of the groupKey, or the ID // of all the assets in the AssetCommitment. - if newAsset.TapCommitmentKey() != c.AssetID { + if newAsset.TapCommitmentKey() != c.TapKey { if newAsset.GroupKey != nil { return ErrAssetGroupKeyMismatch } @@ -269,7 +267,7 @@ func (c *AssetCommitment) Delete(oldAsset *asset.Asset) error { // The given Asset must have an ID that matches the AssetCommitment ID. // The AssetCommitment ID is either a hash of the groupKey, or the ID // of all the assets in the AssetCommitment. - if oldAsset.TapCommitmentKey() != c.AssetID { + if oldAsset.TapCommitmentKey() != c.TapKey { if oldAsset.GroupKey != nil { return ErrAssetGroupKeyMismatch } @@ -322,7 +320,7 @@ func (c *AssetCommitment) Root() [sha256.Size]byte { right := c.TreeRoot.Right.NodeHash() h := sha256.New() - _, _ = h.Write(c.AssetID[:]) + _, _ = h.Write(c.TapKey[:]) _, _ = h.Write(left[:]) _, _ = h.Write(right[:]) _ = binary.Write(h, binary.BigEndian, c.TreeRoot.NodeSum()) @@ -332,7 +330,7 @@ func (c *AssetCommitment) Root() [sha256.Size]byte { // TapCommitmentKey computes the insertion key for this specific asset // commitment to include in the Taproot Asset commitment MS-SMT. func (c *AssetCommitment) TapCommitmentKey() [32]byte { - return c.AssetID + return c.TapKey } // TapCommitmentLeaf computes the leaf node for this specific asset commitment @@ -390,7 +388,7 @@ func (c *AssetCommitment) Copy() (*AssetCommitment, error) { treeRoot := c.TreeRoot.Copy().(*mssmt.BranchNode) return &AssetCommitment{ Version: c.Version, - AssetID: c.AssetID, + TapKey: c.TapKey, TreeRoot: treeRoot, }, nil } diff --git a/commitment/commitment_test.go b/commitment/commitment_test.go index b3e667912..53da735bc 100644 --- a/commitment/commitment_test.go +++ b/commitment/commitment_test.go @@ -1242,7 +1242,7 @@ func TestAssetCommitmentDeepCopy(t *testing.T) { require.NoError(t, err) require.Equal(t, assetCommitment.Version, assetCommitmentCopy.Version) - require.Equal(t, assetCommitment.AssetID, assetCommitmentCopy.AssetID) + require.Equal(t, assetCommitment.TapKey, assetCommitmentCopy.TapKey) require.True( t, mssmt.IsEqualNode( assetCommitment.TreeRoot, assetCommitmentCopy.TreeRoot, diff --git a/commitment/encoding.go b/commitment/encoding.go index ae5e4d1f8..6e9fbae64 100644 --- a/commitment/encoding.go +++ b/commitment/encoding.go @@ -12,7 +12,7 @@ func AssetProofEncoder(w io.Writer, val any, buf *[8]byte) error { if t, ok := val.(**AssetProof); ok { records := []tlv.Record{ AssetProofVersionRecord(&(*t).Version), - AssetProofAssetIDRecord(&(*t).AssetID), + AssetProofAssetIDRecord(&(*t).TapKey), AssetProofRecord(&(*t).Proof), } stream, err := tlv.NewStream(records...) @@ -39,7 +39,7 @@ func AssetProofDecoder(r io.Reader, val any, buf *[8]byte, l uint64) error { var proof AssetProof records := []tlv.Record{ AssetProofVersionRecord(&proof.Version), - AssetProofAssetIDRecord(&proof.AssetID), + AssetProofAssetIDRecord(&proof.TapKey), AssetProofRecord(&proof.Proof), } stream, err := tlv.NewStream(records...) diff --git a/commitment/mock.go b/commitment/mock.go index cbfd1b87d..b214e92b5 100644 --- a/commitment/mock.go +++ b/commitment/mock.go @@ -83,7 +83,7 @@ func NewTestFromProof(t testing.TB, p *Proof) *TestProof { tp.AssetProof = &TestAssetProof{ Proof: mssmt.HexProof(t, &p.AssetProof.Proof), Version: uint8(p.AssetProof.Version), - AssetID: hex.EncodeToString(p.AssetProof.AssetID[:]), + TapKey: hex.EncodeToString(p.AssetProof.TapKey[:]), } } @@ -111,9 +111,9 @@ func (tp *TestProof) ToProof(t testing.TB) *Proof { Proof: mssmt.ParseProof(t, tp.AssetProof.Proof), Version: asset.Version(tp.AssetProof.Version), } - assetID, err := hex.DecodeString(tp.AssetProof.AssetID) + assetID, err := hex.DecodeString(tp.AssetProof.TapKey) require.NoError(t, err) - copy(p.AssetProof.AssetID[:], assetID) + copy(p.AssetProof.TapKey[:], assetID) } return p @@ -122,7 +122,7 @@ func (tp *TestProof) ToProof(t testing.TB) *Proof { type TestAssetProof struct { Proof string `json:"proof"` Version uint8 `json:"version"` - AssetID string `json:"asset_id"` + TapKey string `json:"tap_key"` } type TestTaprootAssetProof struct { diff --git a/commitment/proof.go b/commitment/proof.go index 8932c08fb..3f12d94d0 100644 --- a/commitment/proof.go +++ b/commitment/proof.go @@ -24,11 +24,11 @@ type AssetProof struct { // Version is the max version of the assets committed. Version asset.Version - // AssetID is the common identifier for all assets found within the + // TapKey is the common identifier for all assets found within the // AssetCommitment. This can either be an asset.ID, which every // committed asset must match, otherwise an asset.GroupKey which every // committed asset must match. - AssetID [32]byte + TapKey [32]byte } // TaprootAssetProof is the proof used along with an asset commitment leaf to @@ -117,7 +117,7 @@ func (p Proof) DeriveByAssetInclusion(asset *asset.Asset) (*TapCommitment, ) assetCommitment := &AssetCommitment{ Version: p.AssetProof.Version, - AssetID: p.AssetProof.AssetID, + TapKey: p.AssetProof.TapKey, TreeRoot: assetProofRoot, } @@ -158,7 +158,7 @@ func (p Proof) DeriveByAssetExclusion(assetCommitmentKey [32]byte) ( ) assetCommitment := &AssetCommitment{ Version: p.AssetProof.Version, - AssetID: p.AssetProof.AssetID, + TapKey: p.AssetProof.TapKey, TreeRoot: assetProofRoot, } diff --git a/commitment/tap.go b/commitment/tap.go index ffbccd2ee..94e523b72 100644 --- a/commitment/tap.go +++ b/commitment/tap.go @@ -380,7 +380,7 @@ func (c *TapCommitment) Proof(tapCommitmentKey, proof.AssetProof = &AssetProof{ Proof: *assetProof, Version: assetCommitment.Version, - AssetID: assetCommitment.AssetID, + TapKey: assetCommitment.TapKey, } return a, proof, nil diff --git a/proof/testdata/proof_tlv_encoding_generated.json b/proof/testdata/proof_tlv_encoding_generated.json index 7fcbeeb15..d7759b982 100644 --- a/proof/testdata/proof_tlv_encoding_generated.json +++ b/proof/testdata/proof_tlv_encoding_generated.json @@ -55,7 +55,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "1624d9703a0fea948dd97385753c57c8225b9421af0ec87d4c92a4832a01b03c" + "tap_key": "1624d9703a0fea948dd97385753c57c8225b9421af0ec87d4c92a4832a01b03c" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -141,7 +141,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 1, - "asset_id": "4c3b9aff77396d1ff1d5470229b11164512a56d86db16be09c26b890512e349d" + "tap_key": "4c3b9aff77396d1ff1d5470229b11164512a56d86db16be09c26b890512e349d" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -227,7 +227,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "c2b2144031c7f0158c9b080b3d4f28533c8bd79f3371fa2d486305cb32d52919" + "tap_key": "c2b2144031c7f0158c9b080b3d4f28533c8bd79f3371fa2d486305cb32d52919" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -313,7 +313,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "ee82654f2c644ea7941327d5d842649c79588e663d67314a0b999d13471aafa0" + "tap_key": "ee82654f2c644ea7941327d5d842649c79588e663d67314a0b999d13471aafa0" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -399,7 +399,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "c4fd51257eced9f6bb3f5918545d257fa7a490b129189d0d8096117341b87425" + "tap_key": "c4fd51257eced9f6bb3f5918545d257fa7a490b129189d0d8096117341b87425" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -485,7 +485,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 1, - "asset_id": "cb74bf55f8cd601b17db37abc586a01b59533caa2e1ecd629f840a9d416423c6" + "tap_key": "cb74bf55f8cd601b17db37abc586a01b59533caa2e1ecd629f840a9d416423c6" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -571,7 +571,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "db7a3ba3a675af4724280dc944a6377cc68de6fb2c4c6c51b494e9ca23e22dd0" + "tap_key": "db7a3ba3a675af4724280dc944a6377cc68de6fb2c4c6c51b494e9ca23e22dd0" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -657,7 +657,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "f0b65d4efa4a0c2daec2372a807c817a3a4c2eab20fa90c6d36ab78366d98c5c" + "tap_key": "f0b65d4efa4a0c2daec2372a807c817a3a4c2eab20fa90c6d36ab78366d98c5c" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -743,7 +743,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "341dcde8656fe3a35d4c69986694f44934a74f7b76aabe111d39e6b1f579dbe3" + "tap_key": "341dcde8656fe3a35d4c69986694f44934a74f7b76aabe111d39e6b1f579dbe3" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -832,7 +832,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "90100b5f5edf4a3e49b48a91703912e695f4796fcd23e38bd362c3ef3775172f" + "tap_key": "90100b5f5edf4a3e49b48a91703912e695f4796fcd23e38bd362c3ef3775172f" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", diff --git a/proof/testdata/proof_tlv_encoding_regtest.json b/proof/testdata/proof_tlv_encoding_regtest.json index 6286ec7a8..5dec3fda5 100644 --- a/proof/testdata/proof_tlv_encoding_regtest.json +++ b/proof/testdata/proof_tlv_encoding_regtest.json @@ -55,7 +55,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0001475985869e2c0a018e7f4997cf646922438dfca3a3e571f72e6371db0f1e150500000000000007d0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7", @@ -153,7 +153,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0001b2433a697dcd01e0c5462c85cd53b3ef8662c1484216d4722d0c96851040a3bb00000000000007d0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7", @@ -173,7 +173,7 @@ "asset_proof": { "proof": "0001db29f471c91019c8acfe37b9486e7ca67b0440a0e427347cf83dfb75c6a52b1500000000000004b0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffef", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -289,7 +289,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -309,7 +309,7 @@ "asset_proof": { "proof": "00017367d3004d4eacd4e931e0a9df36a4a4161b94c5d05db5b47a4d014381ccb2e2000000000000012cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffef", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0001b2433a697dcd01e0c5462c85cd53b3ef8662c1484216d4722d0c96851040a3bb00000000000007d0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7", @@ -339,7 +339,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0001b2433a697dcd01e0c5462c85cd53b3ef8662c1484216d4722d0c96851040a3bb00000000000007d0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7", @@ -447,7 +447,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -467,7 +467,7 @@ "asset_proof": { "proof": "00010a1b05a2e5107e4912babe314f825d0ce1588e0f0733d1d85804483de66fa0690000000000000258ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -497,7 +497,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -574,7 +574,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0001475985869e2c0a018e7f4997cf646922438dfca3a3e571f72e6371db0f1e150500000000000007d0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7", @@ -704,7 +704,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -724,7 +724,7 @@ "asset_proof": { "proof": "00017367d3004d4eacd4e931e0a9df36a4a4161b94c5d05db5b47a4d014381ccb2e2000000000000012cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffef", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0001b2433a697dcd01e0c5462c85cd53b3ef8662c1484216d4722d0c96851040a3bb00000000000007d0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7", @@ -754,7 +754,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0001b2433a697dcd01e0c5462c85cd53b3ef8662c1484216d4722d0c96851040a3bb00000000000007d0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7", @@ -862,7 +862,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -882,7 +882,7 @@ "asset_proof": { "proof": "00010a1b05a2e5107e4912babe314f825d0ce1588e0f0733d1d85804483de66fa0690000000000000258ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -912,7 +912,7 @@ "asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", "version": 0, - "asset_id": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" + "tap_key": "a88ed3e42cfbf677223639c2f0e9339bceb795223cfa476b149c73f73560edad" }, "taproot_asset_proof": { "proof": "0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", diff --git a/tapscript/send_test.go b/tapscript/send_test.go index 7d9b707c9..dc98c8a8c 100644 --- a/tapscript/send_test.go +++ b/tapscript/send_test.go @@ -2219,7 +2219,7 @@ func sendCommitment(t *testing.T, a *asset.Asset, sendAmt btcutil.Amount, return &commitment.AssetCommitment{ Version: a.Version, - AssetID: a.ID(), + TapKey: a.TapCommitmentKey(), TreeRoot: root, } } From afc4ff7e202cfb8c2de3e4a9cd5a26197bf96644 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 10 Oct 2023 19:27:15 +0200 Subject: [PATCH 05/11] tapfreighter: don't send proofs for tombstones Don't attempt to send proofs for tombstone outputs. --- tapfreighter/chain_porter.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tapfreighter/chain_porter.go b/tapfreighter/chain_porter.go index 8583bff30..9c9cf1d0e 100644 --- a/tapfreighter/chain_porter.go +++ b/tapfreighter/chain_porter.go @@ -605,6 +605,18 @@ func (p *ChainPorter) transferReceiverProof(pkg *sendPackage) error { return nil } + unSpendable, err := out.ScriptKey.IsUnSpendable() + if err != nil { + return fmt.Errorf("error checking if script key is "+ + "unspendable: %w", err) + } + if unSpendable { + log.Debugf("Not transferring proof for un-spendable "+ + "output script key %x", + key.SerializeCompressed()) + return nil + } + // We just look for the full proof in the list of final proofs // by matching the content of the proof suffix. var receiverProof *proof.AnnotatedProof From 1f2eaabe62122185c563b7f7080279d608fb73c8 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 10 Oct 2023 19:27:16 +0200 Subject: [PATCH 06/11] multi: fix setting log level, add logger for tapscript pkg We called ParseAndSetDebugLevels too early, so the log level config/CLI flag didn't have any effect. Also, we're going to add some trace statements to the tapscript package in the next commit, so we prepare by adding a package logger. --- log.go | 4 ++++ tapcfg/config.go | 16 +++++++++------- tapscript/log.go | 26 ++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 tapscript/log.go diff --git a/log.go b/log.go index aa82dcb23..27b5e2b3a 100644 --- a/log.go +++ b/log.go @@ -8,6 +8,7 @@ import ( "github.com/lightninglabs/taproot-assets/tapdb" "github.com/lightninglabs/taproot-assets/tapfreighter" "github.com/lightninglabs/taproot-assets/tapgarden" + "github.com/lightninglabs/taproot-assets/tapscript" "github.com/lightninglabs/taproot-assets/universe" "github.com/lightningnetwork/lnd/build" "github.com/lightningnetwork/lnd/signal" @@ -96,6 +97,9 @@ func SetupLoggers(root *build.RotatingLogWriter, interceptor signal.Interceptor) ) AddSubLogger(root, proof.Subsystem, interceptor, proof.UseLogger) AddSubLogger(root, tapdb.Subsystem, interceptor, tapdb.UseLogger) + AddSubLogger( + root, tapscript.Subsystem, interceptor, tapscript.UseLogger, + ) AddSubLogger(root, universe.Subsystem, interceptor, universe.UseLogger) AddSubLogger( root, commitment.Subsystem, interceptor, commitment.UseLogger, diff --git a/tapcfg/config.go b/tapcfg/config.go index 62bdb5578..65e0f1a0c 100644 --- a/tapcfg/config.go +++ b/tapcfg/config.go @@ -455,6 +455,14 @@ func LoadConfig(interceptor signal.Interceptor) (*Config, btclog.Logger, error) return nil, nil, err } + // Parse, validate, and set debug log level(s). + err = build.ParseAndSetDebugLevels(cfg.DebugLevel, cfg.LogWriter) + if err != nil { + str := "error parsing debug level: %v" + cfgLogger.Warnf(str, err) + return nil, nil, fmt.Errorf(str, err) + } + // Warn about missing config file only after all other configuration is // done. This prevents the warning on help messages and invalid // options. Note this should go directly before the return. @@ -687,13 +695,6 @@ func ValidateConfig(cfg Config, cfgLogger btclog.Logger) (*Config, error) { os.Exit(0) } - // Parse, validate, and set debug log level(s). - err := build.ParseAndSetDebugLevels(cfg.DebugLevel, cfg.LogWriter) - if err != nil { - str := "error parsing debug level: %v" - return nil, &usageError{mkErr(str, err)} - } - // At least one RPCListener is required. So listen on localhost per // default. if len(cfg.RpcConf.RawRPCListeners) == 0 { @@ -713,6 +714,7 @@ func ValidateConfig(cfg Config, cfgLogger btclog.Logger) (*Config, error) { // Add default port to all RPC listener addresses if needed and remove // duplicate addresses. + var err error cfg.rpcListeners, err = lncfg.NormalizeAddresses( cfg.RpcConf.RawRPCListeners, strconv.Itoa(defaultRPCPort), cfg.net.ResolveTCPAddr, diff --git a/tapscript/log.go b/tapscript/log.go new file mode 100644 index 000000000..30c43be20 --- /dev/null +++ b/tapscript/log.go @@ -0,0 +1,26 @@ +package tapscript + +import ( + "github.com/btcsuite/btclog" +) + +// Subsystem defines the logging code for this subsystem. +const Subsystem = "SEND" + +// log is a logger that is initialized with no output filters. This +// means the package will not perform any logging by default until the caller +// requests it. +var log = btclog.Disabled + +// DisableLog disables all library log output. Logging output is disabled +// by default until UseLogger is called. +func DisableLog() { + UseLogger(btclog.Disabled) +} + +// UseLogger uses a specified Logger to output package logging info. +// This should be used in preference to SetLogWriter if the caller is also +// using btclog. +func UseLogger(logger btclog.Logger) { + log = logger +} From 23827ffd94f80b277c198029baaaccf9b6985f3d Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 10 Oct 2023 19:27:17 +0200 Subject: [PATCH 07/11] tapscript: rename variables for clarity, add trace logs This is an area that is frequently visited when debugging commitment based problems. To improve clarity on some of the variable names, we perform a series of renames. We also add in some trace level log statments. This commit does NOT change any behavior in the code (other than logging). --- tapscript/send.go | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/tapscript/send.go b/tapscript/send.go index 11f40e0b1..517338bca 100644 --- a/tapscript/send.go +++ b/tapscript/send.go @@ -732,12 +732,12 @@ func CreateOutputCommitments(inputTapCommitments tappsbt.InputCommitments, // Merge all input Taproot Asset commitments into a single commitment. // // TODO(ffranr): Use `fn.ForEach` and `inputTapCommitments[1:]`. - inputTapCommitment := inputTapCommitments[0] + firstCommitment := inputTapCommitments[0] for idx := range inputTapCommitments { if idx == 0 { continue } - err := inputTapCommitment.Merge(inputTapCommitments[idx]) + err := firstCommitment.Merge(inputTapCommitments[idx]) if err != nil { return nil, fmt.Errorf("failed to merge input Taproot "+ "Asset commitments: %w", err) @@ -753,13 +753,13 @@ func CreateOutputCommitments(inputTapCommitments tappsbt.InputCommitments, // Remove the spent Asset from the AssetCommitment of the sender. Fail // if the input AssetCommitment or Asset were not in the input // TapCommitment. - inputTapCommitmentCopy, err := inputTapCommitment.Copy() + inputTapCommitment, err := firstCommitment.Copy() if err != nil { return nil, err } - inputCommitments := inputTapCommitmentCopy.Commitments() - inputCommitment, ok := inputCommitments[assetsTapCommitmentKey] + assetCommitments := inputTapCommitment.Commitments() + assetCommitment, ok := assetCommitments[assetsTapCommitmentKey] if !ok { return nil, ErrMissingAssetCommitment } @@ -770,13 +770,13 @@ func CreateOutputCommitments(inputTapCommitments tappsbt.InputCommitments, // Just a sanity check that the asset we're spending really was // in the list of input assets. - _, ok = inputCommitment.Asset(inputAsset.AssetCommitmentKey()) + _, ok = assetCommitment.Asset(inputAsset.AssetCommitmentKey()) if !ok { return nil, ErrMissingInputAsset } // Remove all input assets from the asset commitment tree. - err := inputCommitment.Delete(inputAsset) + err := assetCommitment.Delete(inputAsset) if err != nil { return nil, err } @@ -798,7 +798,7 @@ func CreateOutputCommitments(inputTapCommitments tappsbt.InputCommitments, // The asset is present, just commit it to the input // asset commitment. case vOut.Asset != nil: - err := inputCommitment.Upsert(vOut.Asset) + err := assetCommitment.Upsert(vOut.Asset) if err != nil { return nil, err } @@ -820,22 +820,31 @@ func CreateOutputCommitments(inputTapCommitments tappsbt.InputCommitments, // the new spend details. If there is nothing contained // in the input commitment, it is removed from the // Taproot Asset tree automatically. - err = inputTapCommitmentCopy.Upsert(inputCommitment) + err = inputTapCommitment.Upsert(assetCommitment) if err != nil { return nil, err } + log.Tracef("Adding %d passive assets to output with %d "+ + "current assets", len(passiveAssets), + len(inputTapCommitment.CommittedAssets())) + // Anchor passive assets to this output, since it's the // split root (=change output). err = AnchorPassiveAssets( - passiveAssets, inputTapCommitmentCopy, + passiveAssets, inputTapCommitment, ) if err != nil { return nil, fmt.Errorf("unable to anchor "+ "passive assets: %w", err) } - outputCommitments[idx] = inputTapCommitmentCopy + root := inputTapCommitment.TapscriptRoot(nil) + log.Tracef("Output %d commitment: root=%x, "+ + "num_assets=%d", idx, root[:], + len(inputTapCommitment.CommittedAssets())) + + outputCommitments[idx] = inputTapCommitment continue } From f12cf2446baa4c16aa67ac00db163ef425b6bf32 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 10 Oct 2023 19:27:19 +0200 Subject: [PATCH 08/11] commitment: remove superfluous copying --- commitment/tap.go | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/commitment/tap.go b/commitment/tap.go index 94e523b72..ffdca10e8 100644 --- a/commitment/tap.go +++ b/commitment/tap.go @@ -452,18 +452,14 @@ func (c *TapCommitment) Merge(other *TapCommitment) error { // Otherwise, we'll need to merge the other asset commitments into // this commitment. - for key, otherCommitment := range other.assetCommitments { - existingCommitment, ok := c.assetCommitments[key] + for key := range other.assetCommitments { + otherCommitment := other.assetCommitments[key] // If we already have an asset commitment for this key, then we // merge the two asset trees together. + existingCommitment, ok := c.assetCommitments[key] if ok { - commitmentCopy, err := otherCommitment.Copy() - if err != nil { - return fmt.Errorf("error copying asset "+ - "commitment: %w", err) - } - err = existingCommitment.Merge(commitmentCopy) + err := existingCommitment.Merge(otherCommitment) if err != nil { return fmt.Errorf("error merging asset "+ "commitment: %w", err) @@ -474,12 +470,7 @@ func (c *TapCommitment) Merge(other *TapCommitment) error { // With either the new or merged asset commitment obtained, we // can now (re-)insert it into the Taproot Asset commitment. - existingCommitmentCopy, err := existingCommitment.Copy() - if err != nil { - return fmt.Errorf("error copying asset commitment: "+ - "%w", err) - } - if err := c.Upsert(existingCommitmentCopy); err != nil { + if err := c.Upsert(existingCommitment); err != nil { return fmt.Errorf("error upserting other commitment: "+ "%w", err) } From 62e2be4999e9b0f45d846baf30b6cfec5b7c9345 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 10 Oct 2023 19:27:20 +0200 Subject: [PATCH 09/11] proof: dump proof anchor TX for easy debugging --- proof/proof_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/proof/proof_test.go b/proof/proof_test.go index 910ec43d7..671bedcb1 100644 --- a/proof/proof_test.go +++ b/proof/proof_test.go @@ -19,6 +19,7 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" + "github.com/davecgh/go-spew/spew" "github.com/lightninglabs/taproot-assets/asset" "github.com/lightninglabs/taproot-assets/commitment" "github.com/lightninglabs/taproot-assets/internal/test" @@ -731,6 +732,8 @@ func TestProofVerification(t *testing.T) { assetID := p.Asset.ID() t.Logf("Proof asset ID: %x", assetID[:]) + t.Logf("Proof anchor TX: %v", spew.Sdump(p.AnchorTx)) + inclusionTxOut := p.AnchorTx.TxOut[p.InclusionProof.OutputIndex] t.Logf("Proof inclusion tx out: %x", inclusionTxOut.PkScript) proofKey, proofTree, err := p.InclusionProof.DeriveByAssetInclusion( From bd59fa8b2f47bccfab69c80ca0b809248215a075 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 10 Oct 2023 19:27:21 +0200 Subject: [PATCH 10/11] tapfreighter: remove tombstones and burns from commitment This fixes a bug that we only discovered after sending assets many times back and forth. We didn't properly remove tombstones and burns from the commitments when creating new outputs. We did have tests that sent passive assets out from a commitment that had a tombstone. But we never spent that second output, which would have discovered the bug earlier. We'll fix this with a new integration test case in the next commit. --- tapfreighter/wallet.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tapfreighter/wallet.go b/tapfreighter/wallet.go index 271cb849a..c05cd8607 100644 --- a/tapfreighter/wallet.go +++ b/tapfreighter/wallet.go @@ -766,6 +766,17 @@ func (f *AssetWallet) fundPacketWithInputs(ctx context.Context, } } + // We now also need to remove all tombstone or burns from our active + // commitments. + for idx := range inputCommitments { + inputCommitments[idx], err = pruneTombstonesAndBurns( + inputCommitments[idx], + ) + if err != nil { + return nil, err + } + } + // We expect some change back, or have passive assets to commit to, so // let's make sure we create a transfer output. var changeOut *tappsbt.VOutput @@ -1127,6 +1138,20 @@ func verifyInclusionProof(vIn *tappsbt.VInput) error { return nil } +// pruneTombstonesAndBurns removes all tombstones and burns from the active +// input commitment. +func pruneTombstonesAndBurns( + inputCommitment *commitment.TapCommitment) (*commitment.TapCommitment, + error) { + + committedAssets := inputCommitment.CommittedAssets() + committedAssets = fn.Filter(committedAssets, func(a *asset.Asset) bool { + return !a.IsUnSpendable() && !a.IsBurn() + }) + + return commitment.FromAssets(committedAssets...) +} + // removeActiveCommitments removes all active commitments from the given input // commitment and only returns a tree of passive commitments. func removeActiveCommitments(inputCommitment *commitment.TapCommitment, From b1084e8cd26312b61748ad6e21665c4222822064 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Tue, 10 Oct 2023 19:27:22 +0200 Subject: [PATCH 11/11] itest: add multi send test with grouped assets This test reproduced the bug fixed in the previous commit and now confirms the fix is working. This test also asserts the fix for the problem with attempting to deliver a proof for a tombstone that was done in an earlier commit. --- itest/multi_asset_group_test.go | 153 ++++++++++++++++++++++++++++++-- itest/test_list_on_test.go | 5 ++ 2 files changed, 153 insertions(+), 5 deletions(-) diff --git a/itest/multi_asset_group_test.go b/itest/multi_asset_group_test.go index 601e8e14d..6c4f43836 100644 --- a/itest/multi_asset_group_test.go +++ b/itest/multi_asset_group_test.go @@ -1,9 +1,11 @@ package itest import ( + "bytes" "context" "encoding/hex" - "strconv" + "fmt" + "testing" "github.com/lightninglabs/taproot-assets/fn" "github.com/lightninglabs/taproot-assets/taprpc" @@ -190,14 +192,14 @@ func createMultiAssetGroup(anchor *mintrpc.MintAssetRequest, groupRequests := []*mintrpc.MintAssetRequest{CopyRequest(anchor)} anchorAmount := anchor.Asset.Amount anchorName := anchor.Asset.Name - nameModifier := "-tranche-" groupSum := uint64(0) - for i := uint64(1); i < numAssets+1; i++ { + for i := uint64(1); i <= numAssets; i++ { assetReq := CopyRequest(anchor) assetReq.EnableEmission = false assetReq.Asset.GroupAnchor = anchorName - reqName := anchorName + nameModifier + strconv.FormatUint(i, 10) - assetReq.Asset.Name = reqName + assetReq.Asset.Name = fmt.Sprintf( + "%s-tranche-%d", anchorName, i, + ) if assetReq.Asset.AssetType == taprpc.AssetType_NORMAL { reqAmount := anchorAmount / (2 * i) @@ -270,3 +272,144 @@ func testMintMultiAssetGroupErrors(t *harnessTest) { validAnchor.Asset.Amount AssertBalanceByGroup(t.t, t.tapd, groupKeyHex, expectedGroupBalance) } + +// testMultiAssetGroupSend tests that we can randomly send assets from a group +// of collectibles one after another from one node to the other. +func testMultiAssetGroupSend(t *harnessTest) { + // We use a hashmail proof courier for this test, which takes a bit + // longer to send proofs. So we use a longer timeout. + ctxb := context.Background() + ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout*3) + defer cancel() + + // First, we'll build a batch to mint. + issuableAsset := CopyRequest(simpleAssets[1]) + issuableAsset.EnableEmission = true + + collectibleGroupMembers := 50 + collectibleGroup, collectibleGroupSum := createMultiAssetGroup( + issuableAsset, uint64(collectibleGroupMembers), + ) + + // The minted batch should contain 51 assets total, and the daemon + // should now be aware of one asset group. + mintedBatch := MintAssetsConfirmBatch( + t.t, t.lndHarness.Miner.Client, t.tapd, collectibleGroup, + ) + require.Len(t.t, mintedBatch, collectibleGroupMembers+1) + + // Once the batch is minted, we can verify that all asset groups were + // created correctly. We begin by verifying the number of asset groups. + groupCount := 1 + AssertNumGroups(t.t, t.tapd, groupCount) + balancesResp, err := t.tapd.ListBalances( + ctxt, &taprpc.ListBalancesRequest{ + GroupBy: &taprpc.ListBalancesRequest_GroupKey{ + GroupKey: true, + }, + }, + ) + require.NoError(t.t, err) + + require.NotNil(t.t, mintedBatch[0].AssetGroup) + groupKeyStr := hex.EncodeToString( + mintedBatch[0].AssetGroup.TweakedGroupKey, + ) + + require.Contains(t.t, balancesResp.AssetGroupBalances, groupKeyStr) + require.EqualValues( + t.t, collectibleGroupSum, + balancesResp.AssetGroupBalances[groupKeyStr].Balance, + ) + + AssertGroupSizes(t.t, t.tapd, []string{groupKeyStr}, []int{ + collectibleGroupMembers + 1, + }) + + // We'll make a second node now that'll be the receiver of all the + // assets made above. + secondTapd := setupTapdHarness( + t.t, t, t.lndHarness.Bob, t.universeServer, + func(params *tapdHarnessParams) { + params.startupSyncNode = t.tapd + params.startupSyncNumAssets = groupCount + }, + ) + defer func() { + require.NoError(t.t, secondTapd.stop(!*noDelete)) + }() + + // Send 5 of the assets to Bob, and verify that they are received. + numUnits := issuableAsset.Asset.Amount + assetType := issuableAsset.Asset.AssetType + for i := 0; i < 5; i++ { + // Query the asset we'll be sending, so we can assert some + // things about it later. + sendAsset := assetIDWithBalance( + t.t, t.tapd, numUnits, assetType, + ) + genInfo := sendAsset.AssetGenesis + t.Logf("Attempt %d: Sending %d asset(s) with ID %x from "+ + "alice to bob", i+1, numUnits, genInfo.AssetId) + + addr, err := secondTapd.NewAddr(ctxt, &taprpc.NewAddrRequest{ + AssetId: genInfo.AssetId, + Amt: numUnits, + }) + require.NoError(t.t, err) + AssertAddrCreated(t.t, secondTapd, sendAsset, addr) + + sendResp := sendAssetsToAddr(t, t.tapd, addr) + + ConfirmAndAssertOutboundTransfer( + t.t, t.lndHarness.Miner.Client, t.tapd, + sendResp, genInfo.AssetId, + []uint64{0, numUnits}, i, i+1, + ) + + AssertNonInteractiveRecvComplete(t.t, secondTapd, i+1) + } +} + +// assetIDWithBalance returns the asset ID of an asset that has at least the +// given balance. If no such asset is found, nil is returned. +func assetIDWithBalance(t *testing.T, node *tapdHarness, + minBalance uint64, assetType taprpc.AssetType) *taprpc.Asset { + + ctxb := context.Background() + ctxt, cancel := context.WithTimeout(ctxb, defaultWaitTimeout) + defer cancel() + + balances, err := node.ListBalances(ctxt, &taprpc.ListBalancesRequest{ + GroupBy: &taprpc.ListBalancesRequest_AssetId{ + AssetId: true, + }, + }) + require.NoError(t, err) + + for assetIDHex, balance := range balances.AssetBalances { + if balance.Balance >= minBalance && + balance.AssetType == assetType { + + assetIDBytes, err := hex.DecodeString(assetIDHex) + require.NoError(t, err) + + assets, err := node.ListAssets( + ctxt, &taprpc.ListAssetRequest{}, + ) + require.NoError(t, err) + + for _, asset := range assets.Assets { + if bytes.Equal( + asset.AssetGenesis.AssetId, + assetIDBytes, + ) { + + return asset + } + } + } + } + + return nil +} diff --git a/itest/test_list_on_test.go b/itest/test_list_on_test.go index ecdc77bcc..78e435d22 100644 --- a/itest/test_list_on_test.go +++ b/itest/test_list_on_test.go @@ -96,6 +96,11 @@ var testCases = []*testCase{ name: "minting multi asset groups", test: testMintMultiAssetGroups, }, + { + name: "sending multi asset groups", + test: testMultiAssetGroupSend, + proofCourierType: proof.HashmailCourierType, + }, { name: "re-issuance amount overflow", test: testReIssuanceAmountOverflow,