Skip to content

Commit

Permalink
Merge pull request lightninglabs#732 from ffranr/burn-asset-bug
Browse files Browse the repository at this point in the history
Add asset group burn itest, logging, and missing error handling
  • Loading branch information
ffranr authored Dec 12, 2023
2 parents 3b71255 + 2f09b7e commit 9c85a55
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 1 deletion.
110 changes: 110 additions & 0 deletions itest/burn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package itest

import (
"context"
"encoding/hex"

taprootassets "github.com/lightninglabs/taproot-assets"
"github.com/lightninglabs/taproot-assets/address"
Expand Down Expand Up @@ -301,3 +302,112 @@ func testBurnAssets(t *harnessTest) {
)
AssertBalanceByID(t.t, t.tapd, simpleGroupCollectGen.AssetId, 0)
}

// testBurnGroupedAssets tests that some amount of an asset from an asset group
// can be burnt successfully.
func testBurnGroupedAssets(t *harnessTest) {
var (
ctxb = context.Background()
miner = t.lndHarness.Miner.Client

firstMintReq = issuableAssets[0]
)

// We start off without any asset groups.
AssertNumGroups(t.t, t.tapd, 0)

// Next, we mint a re-issuable asset, creating a new asset group.
firstMintResponses := MintAssetsConfirmBatch(
t.t, miner, t.tapd, []*mintrpc.MintAssetRequest{firstMintReq},
)
require.Len(t.t, firstMintResponses, 1)

var (
firstMintResp = firstMintResponses[0]
assetGroupKey = firstMintResp.AssetGroup.TweakedGroupKey
)

// Ensure that an asset group was created.
AssertNumGroups(t.t, t.tapd, 1)

// Issue a further asset into the asset group.
simpleAssetsCopy := CopyRequests(simpleAssets)
secondMintReq := simpleAssetsCopy[0]
secondMintReq.Asset.Amount = 1010
secondMintReq.Asset.GroupKey = assetGroupKey
secondMintReq.Asset.GroupedAsset = true

secondMintResponses := MintAssetsConfirmBatch(
t.t, miner, t.tapd,
[]*mintrpc.MintAssetRequest{secondMintReq},
)
require.Len(t.t, secondMintResponses, 1)

// Ensure that we haven't created a new group.
AssertNumGroups(t.t, t.tapd, 1)

secondMintResp := secondMintResponses[0]

// Confirm that the minted asset group contains two assets.
assetGroups, err := t.tapd.ListGroups(
ctxb, &taprpc.ListGroupsRequest{},
)
require.NoError(t.t, err)

encodedGroupKey := hex.EncodeToString(assetGroupKey)
assetGroup := assetGroups.Groups[encodedGroupKey]
require.Len(t.t, assetGroup.Assets, 2)

// Burn some amount of the second asset.
var (
burnAssetID = secondMintResp.AssetGenesis.AssetId

preBurnAmt = secondMintResp.Amount
burnAmt = uint64(10)
postBurnAmt = preBurnAmt - burnAmt
)

burnResp, err := t.tapd.BurnAsset(ctxb, &taprpc.BurnAssetRequest{
Asset: &taprpc.BurnAssetRequest_AssetId{
AssetId: burnAssetID,
},
AmountToBurn: burnAmt,
ConfirmationText: taprootassets.AssetBurnConfirmationText,
})
require.NoError(t.t, err)

burnRespJSON, err := formatProtoJSON(burnResp)
require.NoError(t.t, err)
t.Logf("Got response from burning %d units: %v", burnAmt, burnRespJSON)

// Assert that the asset burn transfer occurred correctly.
AssertAssetOutboundTransferWithOutputs(
t.t, miner, t.tapd, burnResp.BurnTransfer,
burnAssetID, []uint64{postBurnAmt, burnAmt}, 0, 1, 2, true,
)

// Ensure that the burnt asset has the correct state.
burnedAsset := burnResp.BurnProof.Asset
allAssets, err := t.tapd.ListAssets(
ctxb, &taprpc.ListAssetRequest{IncludeSpent: true},
)
require.NoError(t.t, err)
AssertAssetStateByScriptKey(
t.t, allAssets.Assets, burnedAsset.ScriptKey,
AssetAmountCheck(burnedAsset.Amount),
AssetTypeCheck(burnedAsset.AssetGenesis.AssetType),
AssetScriptKeyIsLocalCheck(false),
AssetScriptKeyIsBurnCheck(true),
)

// Our asset balance should have been decreased by the burned amount.
AssertBalanceByID(t.t, t.tapd, burnAssetID, postBurnAmt)

// Confirm that the minted asset group still contains two assets.
assetGroups, err = t.tapd.ListGroups(ctxb, &taprpc.ListGroupsRequest{})
require.NoError(t.t, err)

encodedGroupKey = hex.EncodeToString(assetGroupKey)
assetGroup = assetGroups.Groups[encodedGroupKey]
require.Len(t.t, assetGroup.Assets, 2)
}
4 changes: 4 additions & 0 deletions itest/test_list_on_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ var testCases = []*testCase{
name: "burn test",
test: testBurnAssets,
},
{
name: "burn grouped assets",
test: testBurnGroupedAssets,
},
{
name: "federation sync config",
test: testFederationSyncConfig,
Expand Down
23 changes: 22 additions & 1 deletion rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -2063,6 +2063,8 @@ func (r *rpcServer) SendAsset(_ context.Context,
func (r *rpcServer) BurnAsset(ctx context.Context,
in *taprpc.BurnAssetRequest) (*taprpc.BurnAssetResponse, error) {

rpcsLog.Debug("Executing asset burn")

var assetID asset.ID
switch {
case len(in.GetAssetId()) > 0:
Expand Down Expand Up @@ -2092,10 +2094,29 @@ func (r *rpcServer) BurnAsset(ctx context.Context,

var groupKey *btcec.PublicKey
assetGroup, err := r.cfg.TapAddrBook.QueryAssetGroup(ctx, assetID)
if err == nil && assetGroup.GroupKey != nil {
switch {
case err == nil && assetGroup.GroupKey != nil:
// We found the asset group, so we can use the group key to
// burn the asset.
groupKey = &assetGroup.GroupPubKey
case errors.Is(err, address.ErrAssetGroupUnknown):
// We don't know the asset group, so we'll try to burn the
// asset using the asset ID only.
rpcsLog.Debug("Asset group key not found, asset may not be " +
"part of a group")
case err != nil:
return nil, fmt.Errorf("error querying asset group: %w", err)
}

var serializedGroupKey []byte
if groupKey != nil {
serializedGroupKey = groupKey.SerializeCompressed()
}

rpcsLog.Infof("Burning asset (asset_id=%x, group_key=%x, "+
"burn_amount=%d)", assetID[:], serializedGroupKey,
in.AmountToBurn)

fundResp, err := r.cfg.AssetWallet.FundBurn(
ctx, &tapscript.FundingDescriptor{
ID: assetID,
Expand Down

0 comments on commit 9c85a55

Please sign in to comment.