Skip to content

Commit 56e571c

Browse files
committed
rpc: burn by group key
Return multiple proofs in BurnAssetResponse.
1 parent 9d8f087 commit 56e571c

File tree

3 files changed

+448
-392
lines changed

3 files changed

+448
-392
lines changed

rpcserver.go

Lines changed: 144 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -3702,24 +3702,6 @@ func (r *rpcServer) BurnAsset(ctx context.Context,
37023702

37033703
rpcsLog.Debug("Executing asset burn")
37043704

3705-
var assetID asset.ID
3706-
switch {
3707-
case len(in.GetAssetId()) > 0:
3708-
copy(assetID[:], in.GetAssetId())
3709-
3710-
case len(in.GetAssetIdStr()) > 0:
3711-
assetIDBytes, err := hex.DecodeString(in.GetAssetIdStr())
3712-
if err != nil {
3713-
return nil, fmt.Errorf("error decoding asset ID: %w",
3714-
err)
3715-
}
3716-
3717-
copy(assetID[:], assetIDBytes)
3718-
3719-
default:
3720-
return nil, fmt.Errorf("asset ID must be specified")
3721-
}
3722-
37233705
if in.AmountToBurn == 0 {
37243706
return nil, fmt.Errorf("amount to burn must be specified")
37253707
}
@@ -3729,36 +3711,120 @@ func (r *rpcServer) BurnAsset(ctx context.Context,
37293711
"accidental asset burns")
37303712
}
37313713

3732-
var groupKey *btcec.PublicKey
3733-
assetGroup, err := r.cfg.TapAddrBook.QueryAssetGroupByID(ctx, assetID)
3714+
var (
3715+
assetID *asset.ID
3716+
groupKey *btcec.PublicKey
3717+
err error
3718+
)
3719+
3720+
// TODO(darioAnongba): Remove this switch once the deprecated asset
3721+
// field is removed. Keep only the AssetSpecifier case.
3722+
// Added in v0.8.0.
37343723
switch {
3735-
case err == nil && assetGroup.GroupKey != nil:
3736-
// We found the asset group, so we can use the group key to
3737-
// burn the asset.
3738-
groupKey = &assetGroup.GroupPubKey
3724+
case in.Asset != nil:
3725+
rpcsLog.Warnf("using deprecated asset field, please use " +
3726+
"asset_specifier instead")
37393727

3740-
case errors.Is(err, address.ErrAssetGroupUnknown):
3741-
// We don't know the asset group, so we'll try to burn the
3742-
// asset using the asset ID only.
3743-
rpcsLog.Debug("Asset group key not found, asset may not be " +
3744-
"part of a group")
3728+
switch {
3729+
case len(in.GetAssetId()) > 0:
3730+
var assetIdBytes [32]byte
3731+
copy(assetIdBytes[:], in.GetAssetId())
3732+
id := asset.ID(assetIdBytes)
3733+
assetID = &id
3734+
3735+
case len(in.GetAssetIdStr()) > 0:
3736+
assetIDBytes, err := hex.DecodeString(
3737+
in.GetAssetIdStr(),
3738+
)
3739+
if err != nil {
3740+
return nil, fmt.Errorf("error decoding "+
3741+
"asset ID: %w",
3742+
err)
3743+
}
37453744

3746-
case err != nil:
3747-
return nil, fmt.Errorf("error querying asset group: %w", err)
3748-
}
3745+
var id asset.ID
3746+
copy(id[:], assetIDBytes)
3747+
assetID = &id
3748+
3749+
default:
3750+
return nil, fmt.Errorf("asset ID must be specified")
3751+
}
3752+
3753+
case in.AssetSpecifier != nil:
3754+
assetID, groupKey, err = parseAssetSpecifier(
3755+
in.AssetSpecifier.GetId(),
3756+
"",
3757+
in.AssetSpecifier.GetGroupKey(),
3758+
"",
3759+
)
3760+
if err != nil {
3761+
return nil, fmt.Errorf("unable to parse asset "+
3762+
"specifier: %w", err)
3763+
}
37493764

3750-
var serializedGroupKey []byte
3751-
if groupKey != nil {
3752-
serializedGroupKey = groupKey.SerializeCompressed()
3765+
default:
3766+
return nil, fmt.Errorf("asset_specifier field unset")
37533767
}
37543768

3755-
rpcsLog.Infof("Burning asset (asset_id=%x, group_key=%x, "+
3756-
"burn_amount=%d)", assetID[:], serializedGroupKey,
3757-
in.AmountToBurn)
3769+
// Handle different scenarios based on what was provided:
3770+
// 1. If only assetID is provided, look up the group key.
3771+
// 2. If only groupKey is provided, use it directly
3772+
// 3. If both are provided, validate consistency.
3773+
switch {
3774+
case assetID != nil && groupKey == nil:
3775+
assetGroup, err := r.cfg.TapAddrBook.QueryAssetGroupByID(
3776+
ctx, *assetID,
3777+
)
3778+
switch {
3779+
case err == nil && assetGroup.GroupKey != nil:
3780+
groupKey = &assetGroup.GroupPubKey
3781+
3782+
case errors.Is(err, address.ErrAssetGroupUnknown):
3783+
rpcsLog.Trace("Asset group key not found, asset " +
3784+
"may not be part of a group")
3785+
3786+
case err != nil:
3787+
return nil, fmt.Errorf("error querying asset "+
3788+
"group: %w", err)
3789+
}
3790+
3791+
case assetID != nil && groupKey != nil:
3792+
assetGroup, err := r.cfg.TapAddrBook.QueryAssetGroupByID(
3793+
ctx, *assetID,
3794+
)
3795+
switch {
3796+
case err == nil && assetGroup.GroupKey != nil:
3797+
if !groupKey.IsEqual(&assetGroup.GroupPubKey) {
3798+
return nil, fmt.Errorf("inconsistent asset " +
3799+
"ID for given group key")
3800+
}
3801+
3802+
case errors.Is(err, address.ErrAssetGroupUnknown):
3803+
return nil, fmt.Errorf("asset is not part of a group")
3804+
3805+
case err != nil:
3806+
return nil, fmt.Errorf("error querying asset "+
3807+
"group: %w", err)
3808+
}
3809+
3810+
case assetID == nil && groupKey != nil:
3811+
// Only group key provided - use it directly
3812+
3813+
default:
3814+
// Should be unreachable by assertion.
3815+
return nil, fmt.Errorf("invalid asset specifier state")
3816+
}
37583817

3759-
assetSpecifier := asset.NewSpecifierOptionalGroupPubKey(
3760-
assetID, groupKey,
3818+
assetSpecifier, err := asset.NewSpecifier(
3819+
assetID, groupKey, nil, true,
37613820
)
3821+
if err != nil {
3822+
return nil, fmt.Errorf("unable to create asset specifier: %w",
3823+
err)
3824+
}
3825+
3826+
rpcsLog.Infof("Burning asset (asset_specifier=%v, burn_amount=%d)",
3827+
assetSpecifier, in.AmountToBurn)
37623828

37633829
fundResp, err := r.cfg.AssetWallet.FundBurn(
37643830
ctx, &tapsend.FundingDescriptor{
@@ -3770,17 +3836,13 @@ func (r *rpcServer) BurnAsset(ctx context.Context,
37703836
return nil, fmt.Errorf("error funding burn: %w", err)
37713837
}
37723838

3773-
// We don't support burning by group key yet, so we only expect a single
3774-
// vPacket (which implies a single asset ID is involved).
3775-
if len(fundResp.VPackets) > 1 {
3776-
return nil, fmt.Errorf("only one packet supported")
3777-
}
3778-
3779-
// Now we can sign the packet and send it to the chain.
3780-
vPkt := fundResp.VPackets[0]
3781-
_, err = r.cfg.AssetWallet.SignVirtualPacket(ctx, vPkt)
3782-
if err != nil {
3783-
return nil, fmt.Errorf("error signing packet: %w", err)
3839+
// Sign all virtual packets created for this burn
3840+
// (may be more than one when burning by group key).
3841+
for _, vPkt := range fundResp.VPackets {
3842+
_, err = r.cfg.AssetWallet.SignVirtualPacket(ctx, vPkt)
3843+
if err != nil {
3844+
return nil, fmt.Errorf("error signing packet: %w", err)
3845+
}
37843846
}
37853847

37863848
resp, err := r.cfg.ChainPorter.RequestShipment(
@@ -3798,28 +3860,29 @@ func (r *rpcServer) BurnAsset(ctx context.Context,
37983860
err)
37993861
}
38003862

3801-
var burnProof *taprpc.DecodedProof
3802-
for idx := range resp.Outputs {
3803-
vOut := vPkt.Outputs[idx]
3804-
tOut := resp.Outputs[idx]
3805-
if vOut.Asset.IsBurn() {
3806-
p, err := proof.Decode(tOut.ProofSuffix)
3807-
if err != nil {
3808-
return nil, fmt.Errorf("error decoding "+
3809-
"burn proof: %w", err)
3810-
}
3863+
var burnProofs []*taprpc.DecodedProof
3864+
for _, tOut := range resp.Outputs {
3865+
p, err := proof.Decode(tOut.ProofSuffix)
3866+
if err != nil {
3867+
return nil, fmt.Errorf("error decoding burn proof: %w",
3868+
err)
3869+
}
38113870

3812-
burnProof, err = r.marshalProof(ctx, p, true, false)
3813-
if err != nil {
3814-
return nil, fmt.Errorf("error decoding "+
3815-
"burn proof: %w", err)
3816-
}
3871+
if !p.Asset.IsBurn() {
3872+
continue
3873+
}
3874+
3875+
burnProof, err := r.marshalProof(ctx, p, true, false)
3876+
if err != nil {
3877+
return nil, fmt.Errorf("error decoding burn proof: %w",
3878+
err)
38173879
}
3880+
burnProofs = append(burnProofs, burnProof)
38183881
}
38193882

38203883
return &taprpc.BurnAssetResponse{
38213884
BurnTransfer: parcel,
3822-
BurnProof: burnProof,
3885+
BurnProofs: burnProofs,
38233886
}, nil
38243887
}
38253888

@@ -8002,19 +8065,14 @@ func parseAssetSpecifier(reqAssetID []byte, reqAssetIDStr string,
80028065
reqGroupKey []byte, reqGroupKeyStr string) (*asset.ID, *btcec.PublicKey,
80038066
error) {
80048067

8005-
// Attempt to decode the asset specifier from the RPC request. In cases
8006-
// where both the asset ID and asset group key are provided, we will
8007-
// give precedence to the asset ID due to its higher level of
8008-
// specificity.
80098068
var (
80108069
assetID *asset.ID
80118070
groupKey *btcec.PublicKey
80128071
err error
80138072
)
80148073

8015-
switch {
8016-
// Parse the asset ID if it's set.
8017-
case len(reqAssetID) > 0:
8074+
// Parse the asset ID if it's set (either as bytes or string).
8075+
if len(reqAssetID) > 0 {
80188076
if len(reqAssetID) != sha256.Size {
80198077
return nil, nil, fmt.Errorf("asset ID must be 32 bytes")
80208078
}
@@ -8023,46 +8081,41 @@ func parseAssetSpecifier(reqAssetID []byte, reqAssetIDStr string,
80238081
copy(assetIdBytes[:], reqAssetID)
80248082
id := asset.ID(assetIdBytes)
80258083
assetID = &id
8026-
8027-
case len(reqAssetIDStr) > 0:
8084+
} else if len(reqAssetIDStr) > 0 {
80288085
assetIDBytes, err := hex.DecodeString(reqAssetIDStr)
80298086
if err != nil {
8030-
return nil, nil, fmt.Errorf("error decoding asset "+
8031-
"ID: %w", err)
8032-
}
8033-
8034-
if len(assetIDBytes) != sha256.Size {
8035-
return nil, nil, fmt.Errorf("asset ID must be 32 bytes")
8087+
return nil, nil, fmt.Errorf("error decoding "+
8088+
"asset ID: %w", err)
80368089
}
80378090

80388091
var id asset.ID
80398092
copy(id[:], assetIDBytes)
80408093
assetID = &id
8094+
}
80418095

8042-
// Parse the group key if it's set.
8043-
case len(reqGroupKey) > 0:
8096+
// Parse the group key if it's set (either as bytes or string).
8097+
if len(reqGroupKey) > 0 {
80448098
groupKey, err = btcec.ParsePubKey(reqGroupKey)
80458099
if err != nil {
80468100
return nil, nil, fmt.Errorf("error parsing group "+
80478101
"key: %w", err)
80488102
}
8049-
8050-
case len(reqGroupKeyStr) > 0:
8103+
} else if len(reqGroupKeyStr) > 0 {
80518104
groupKeyBytes, err := hex.DecodeString(reqGroupKeyStr)
80528105
if err != nil {
8053-
return nil, nil, fmt.Errorf("error decoding group "+
8054-
"key: %w", err)
8106+
return nil, nil, fmt.Errorf("error decoding "+
8107+
"group key: %w", err)
80558108
}
80568109

80578110
groupKey, err = btcec.ParsePubKey(groupKeyBytes)
80588111
if err != nil {
80598112
return nil, nil, fmt.Errorf("error parsing group "+
80608113
"key: %w", err)
80618114
}
8115+
}
80628116

8063-
default:
8064-
// At this point, we know that neither the asset ID nor the
8065-
// group key are specified. Return an error.
8117+
// Validate that at least one is specified.
8118+
if assetID == nil && groupKey == nil {
80668119
return nil, nil, fmt.Errorf("either asset ID or asset group " +
80678120
"key must be specified")
80688121
}

0 commit comments

Comments
 (0)