Skip to content

Commit 5b0795b

Browse files
committed
universerpc: add InsertSupplyCommit RPC endpoint
This endpoint allows the supply commit syncer to push new supply commits into a canonical universe.
1 parent 8380177 commit 5b0795b

File tree

6 files changed

+1127
-298
lines changed

6 files changed

+1127
-298
lines changed

rpcserver.go

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4594,6 +4594,213 @@ func (r *rpcServer) FetchSupplyLeaves(ctx context.Context,
45944594
}, nil
45954595
}
45964596

4597+
// unmarshalSupplyCommitChainData converts an RPC SupplyCommitChainData into
4598+
// both a supplycommit.RootCommitment and supplycommit.ChainProof.
4599+
func unmarshalSupplyCommitChainData(
4600+
rpcData *unirpc.SupplyCommitChainData) (*supplycommit.RootCommitment,
4601+
*supplycommit.ChainProof, error) {
4602+
4603+
if rpcData == nil {
4604+
return nil, nil, fmt.Errorf("supply commit chain data is nil")
4605+
}
4606+
4607+
var txn wire.MsgTx
4608+
err := txn.Deserialize(bytes.NewReader(rpcData.Txn))
4609+
if err != nil {
4610+
return nil, nil, fmt.Errorf("unable to deserialize "+
4611+
"transaction: %w", err)
4612+
}
4613+
4614+
internalKey, err := btcec.ParsePubKey(rpcData.InternalKey)
4615+
if err != nil {
4616+
return nil, nil, fmt.Errorf("unable to parse internal key: %w",
4617+
err)
4618+
}
4619+
4620+
outputKey, err := btcec.ParsePubKey(rpcData.OutputKey)
4621+
if err != nil {
4622+
return nil, nil, fmt.Errorf("unable to parse output key: %w",
4623+
err)
4624+
}
4625+
4626+
// Convert supply root hash.
4627+
if len(rpcData.SupplyRootHash) != 32 {
4628+
return nil, nil, fmt.Errorf("invalid supply root hash size: "+
4629+
"expected %d, got %d", 32, len(rpcData.SupplyRootHash))
4630+
}
4631+
var supplyRootHash mssmt.NodeHash
4632+
copy(supplyRootHash[:], rpcData.SupplyRootHash)
4633+
4634+
// Create commitment block from the hash.
4635+
var commitmentBlock fn.Option[supplycommit.CommitmentBlock]
4636+
if len(rpcData.BlockHash) > 0 {
4637+
if len(rpcData.BlockHash) != chainhash.HashSize {
4638+
return nil, nil, fmt.Errorf("invalid block hash size: "+
4639+
"expected %d, got %d", chainhash.HashSize,
4640+
len(rpcData.BlockHash))
4641+
}
4642+
var blockHash chainhash.Hash
4643+
copy(blockHash[:], rpcData.BlockHash)
4644+
4645+
commitmentBlock = fn.Some(supplycommit.CommitmentBlock{
4646+
Height: rpcData.BlockHeight,
4647+
Hash: blockHash,
4648+
TxIndex: rpcData.TxIndex,
4649+
})
4650+
}
4651+
4652+
rootCommitment := &supplycommit.RootCommitment{
4653+
Txn: &txn,
4654+
TxOutIdx: rpcData.TxOutIdx,
4655+
InternalKey: keychain.KeyDescriptor{
4656+
PubKey: internalKey,
4657+
},
4658+
OutputKey: outputKey,
4659+
SupplyRoot: mssmt.NewComputedBranch(
4660+
supplyRootHash, rpcData.SupplyRootSum,
4661+
),
4662+
CommitmentBlock: commitmentBlock,
4663+
}
4664+
4665+
var blockHeader wire.BlockHeader
4666+
err = blockHeader.Deserialize(bytes.NewReader(rpcData.BlockHeader))
4667+
if err != nil {
4668+
return nil, nil, fmt.Errorf("unable to deserialize block "+
4669+
"header: %w", err)
4670+
}
4671+
4672+
var merkleProof proof.TxMerkleProof
4673+
err = merkleProof.Decode(bytes.NewReader(rpcData.TxBlockMerkleProof))
4674+
if err != nil {
4675+
return nil, nil, fmt.Errorf("unable to decode merkle proof: %w",
4676+
err)
4677+
}
4678+
4679+
chainProof := &supplycommit.ChainProof{
4680+
Header: blockHeader,
4681+
BlockHeight: rpcData.BlockHeight,
4682+
MerkleProof: merkleProof,
4683+
TxIndex: rpcData.TxIndex,
4684+
}
4685+
4686+
return rootCommitment, chainProof, nil
4687+
}
4688+
4689+
// InsertSupplyCommit stores a verified supply commitment for the given
4690+
// asset group in the node's local database.
4691+
func (r *rpcServer) InsertSupplyCommit(ctx context.Context,
4692+
req *unirpc.InsertSupplyCommitRequest) (
4693+
*unirpc.InsertSupplyCommitResponse, error) {
4694+
4695+
// Parse asset group key from the request.
4696+
var groupPubKey btcec.PublicKey
4697+
4698+
switch {
4699+
case len(req.GetGroupKeyBytes()) > 0:
4700+
gk, err := btcec.ParsePubKey(req.GetGroupKeyBytes())
4701+
if err != nil {
4702+
return nil, fmt.Errorf("parsing group key: %w", err)
4703+
}
4704+
4705+
groupPubKey = *gk
4706+
4707+
case len(req.GetGroupKeyStr()) > 0:
4708+
groupKeyBytes, err := hex.DecodeString(req.GetGroupKeyStr())
4709+
if err != nil {
4710+
return nil, fmt.Errorf("decoding group key: %w", err)
4711+
}
4712+
4713+
gk, err := btcec.ParsePubKey(groupKeyBytes)
4714+
if err != nil {
4715+
return nil, fmt.Errorf("parsing group key: %w", err)
4716+
}
4717+
4718+
groupPubKey = *gk
4719+
4720+
default:
4721+
return nil, fmt.Errorf("group key unspecified")
4722+
}
4723+
4724+
// Log the operation for debugging purposes.
4725+
rpcsLog.Debugf("InsertSupplyCommitment called for group key: %x",
4726+
groupPubKey.SerializeCompressed())
4727+
4728+
// Unmarshal the supply commit chain data.
4729+
rootCommitment, chainProof, err := unmarshalSupplyCommitChainData(
4730+
req.ChainData,
4731+
)
4732+
if err != nil {
4733+
return nil, fmt.Errorf("failed to unmarshal chain data: %w",
4734+
err)
4735+
}
4736+
4737+
// Initialize the SupplyLeaves structure to collect all unmarshalled
4738+
// events.
4739+
var supplyLeaves supplycommit.SupplyLeaves
4740+
4741+
// Process issuance leaves.
4742+
supplyLeaves.IssuanceLeafEntries = make(
4743+
[]supplycommit.NewMintEvent, 0, len(req.IssuanceLeaves),
4744+
)
4745+
for _, rpcLeaf := range req.IssuanceLeaves {
4746+
mintEvent, err := unmarshalMintSupplyLeaf(rpcLeaf)
4747+
if err != nil {
4748+
return nil, fmt.Errorf("failed to unmarshal issuance "+
4749+
"leaf: %w", err)
4750+
}
4751+
supplyLeaves.IssuanceLeafEntries = append(
4752+
supplyLeaves.IssuanceLeafEntries, *mintEvent,
4753+
)
4754+
}
4755+
4756+
// Process burn leaves.
4757+
supplyLeaves.BurnLeafEntries = make(
4758+
[]supplycommit.NewBurnEvent, 0, len(req.BurnLeaves),
4759+
)
4760+
for _, rpcLeaf := range req.BurnLeaves {
4761+
burnEvent, err := unmarshalBurnSupplyLeaf(rpcLeaf)
4762+
if err != nil {
4763+
return nil, fmt.Errorf("failed to unmarshal burn "+
4764+
"leaf: %w", err)
4765+
}
4766+
supplyLeaves.BurnLeafEntries = append(
4767+
supplyLeaves.BurnLeafEntries, *burnEvent,
4768+
)
4769+
}
4770+
4771+
// Process ignore leaves.
4772+
supplyLeaves.IgnoreLeafEntries = make(
4773+
[]supplycommit.NewIgnoreEvent, 0, len(req.IgnoreLeaves),
4774+
)
4775+
for _, rpcLeaf := range req.IgnoreLeaves {
4776+
ignoreEvent, err := unmarshalIgnoreSupplyLeaf(rpcLeaf)
4777+
if err != nil {
4778+
return nil, fmt.Errorf("failed to unmarshal ignore "+
4779+
"leaf: %w", err)
4780+
}
4781+
supplyLeaves.IgnoreLeafEntries = append(
4782+
supplyLeaves.IgnoreLeafEntries, *ignoreEvent,
4783+
)
4784+
}
4785+
4786+
rpcsLog.Debugf("Successfully unmarshalled commitment, %d issuance, "+
4787+
"%d burn, and %d ignore leaves, and chain proof",
4788+
len(supplyLeaves.IssuanceLeafEntries),
4789+
len(supplyLeaves.BurnLeafEntries),
4790+
len(supplyLeaves.IgnoreLeafEntries))
4791+
4792+
assetSpec := asset.NewSpecifierFromGroupKey(groupPubKey)
4793+
err = r.cfg.SupplyVerifyManager.InsertSupplyCommit(
4794+
ctx, assetSpec, *rootCommitment, supplyLeaves, *chainProof,
4795+
)
4796+
if err != nil {
4797+
return nil, fmt.Errorf("failed to insert supply commitment: %w",
4798+
err)
4799+
}
4800+
4801+
return &unirpc.InsertSupplyCommitResponse{}, nil
4802+
}
4803+
45974804
// SubscribeSendAssetEventNtfns registers a subscription to the event
45984805
// notification stream which relates to the asset sending process.
45994806
func (r *rpcServer) SubscribeSendAssetEventNtfns(

0 commit comments

Comments
 (0)