Skip to content

Instantly share code, notes, and snippets.

@ffranr
Created October 27, 2025 22:28
Show Gist options
  • Select an option

  • Save ffranr/9b94850c4fc828a1a3e5170b1d1af199 to your computer and use it in GitHub Desktop.

Select an option

Save ffranr/9b94850c4fc828a1a3e5170b1d1af199 to your computer and use it in GitHub Desktop.
burn via asset ID or asset group pub key
// BurnAsset burns the given number of units of a given asset by sending them
// to a provably un-spendable script key. Burning means irrevocably destroying
// a certain number of assets, reducing the total supply of the asset. Because
// burning is such a destructive and non-reversible operation, some specific
// values need to be set in the request to avoid accidental burns.
func (r *rpcServer) BurnAsset(ctx context.Context,
in *taprpc.BurnAssetRequest) (*taprpc.BurnAssetResponse, error) {
rpcsLog.Debug("Executing asset burn")
if in.AmountToBurn == 0 {
return nil, fmt.Errorf("amount to burn must be specified")
}
if in.ConfirmationText != AssetBurnConfirmationText {
return nil, fmt.Errorf("invalid confirmation text, please " +
"read API doc and confirm safety measure to avoid " +
"accidental asset burns")
}
var (
assetSpec asset.Specifier
err error
)
// TODO(darioAnongba): Remove this switch once the deprecated asset
// field is removed. Keep only the AssetSpecifier case.
// Added in v0.8.0.
switch {
case in.Asset != nil:
rpcsLog.Warnf("Using deprecated asset field, please use " +
"asset_specifier instead")
switch {
case len(in.GetAssetId()) > 0:
var assetIdBytes [32]byte
copy(assetIdBytes[:], in.GetAssetId())
id := asset.ID(assetIdBytes)
assetSpec = asset.NewSpecifierFromId(id)
case len(in.GetAssetIdStr()) > 0:
assetIDBytes, err := hex.DecodeString(
in.GetAssetIdStr(),
)
if err != nil {
return nil, fmt.Errorf("decoding asset ID: %w",
err)
}
var id asset.ID
copy(id[:], assetIDBytes)
assetSpec = asset.NewSpecifierFromId(id)
default:
return nil, fmt.Errorf("asset ID must be specified")
}
case in.AssetSpecifier != nil:
assetID, groupKey, err := parseAssetSpecifier(
in.AssetSpecifier.GetId(),
"",
in.AssetSpecifier.GetGroupKey(),
"",
)
if err != nil {
return nil, fmt.Errorf("unable to parse asset "+
"specifier: %w", err)
}
assetSpec, err = asset.NewSpecifier(
assetID, groupKey, nil, true,
)
if err != nil {
return nil, fmt.Errorf("unable to create asset "+
"specifier: %w", err)
}
default:
return nil, fmt.Errorf("asset_specifier field unset")
}
// If both a group key and an asset ID are provided, ensure they
// correspond.
if assetSpec.HasId() && assetSpec.HasGroupPubKey() {
assetID, err := assetSpec.ID().UnwrapOrErr(
fmt.Errorf("code error: asset ID missing"),
)
if err != nil {
return nil, err
}
assetGroup, err := r.cfg.TapAddrBook.QueryAssetGroupByID(
ctx, assetID,
)
if err != nil {
return nil, fmt.Errorf("querying asset group: %w", err)
}
// The group key retrieved from the database must match the key
// provided in the request.
groupPubKey, err := assetSpec.GroupKey().UnwrapOrErr(
fmt.Errorf("code error: asset group key missing"),
)
if err != nil {
return nil, err
}
if !groupPubKey.IsEqual(&assetGroup.GroupPubKey) {
return nil, fmt.Errorf("provided group key does " +
"not match provided asset ID")
}
}
rpcsLog.Infof("Burning asset (asset_specifier=%v, burn_amount=%d)",
assetSpec, in.AmountToBurn)
fundResp, err := r.cfg.AssetWallet.FundBurn(
ctx, &tapsend.FundingDescriptor{
AssetSpecifier: assetSpec,
Amount: in.AmountToBurn,
},
)
if err != nil {
return nil, fmt.Errorf("error funding burn: %w", err)
}
// Sign all virtual packets created for this burn
// (may be more than one when burning by group key).
for _, vPkt := range fundResp.VPackets {
_, err = r.cfg.AssetWallet.SignVirtualPacket(ctx, vPkt)
if err != nil {
return nil, fmt.Errorf("error signing packet: %w", err)
}
}
resp, err := r.cfg.ChainPorter.RequestShipment(
tapfreighter.NewPreSignedParcel(
fundResp.VPackets, fundResp.InputCommitments, in.Note,
),
)
if err != nil {
return nil, err
}
parcel, err := marshalOutboundParcel(resp)
if err != nil {
return nil, fmt.Errorf("error marshaling outbound parcel: %w",
err)
}
var burnProofs []*taprpc.DecodedProof
for _, tOut := range resp.Outputs {
p, err := proof.Decode(tOut.ProofSuffix)
if err != nil {
return nil, fmt.Errorf("error decoding burn proof: %w",
err)
}
if !p.Asset.IsBurn() {
continue
}
burnProof, err := r.marshalProof(ctx, p, true, false)
if err != nil {
return nil, fmt.Errorf("error decoding burn proof: %w",
err)
}
burnProofs = append(burnProofs, burnProof)
}
return &taprpc.BurnAssetResponse{
BurnTransfer: parcel,
BurnProofs: burnProofs,
}, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment