Skip to content

Commit 8ebe23f

Browse files
committed
multi: validate and insert supply commit into universe
1 parent aca9e1d commit 8ebe23f

File tree

10 files changed

+693
-283
lines changed

10 files changed

+693
-283
lines changed

proof/util.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ func unpackBits(bytes []byte) []bool {
4545
return bits
4646
}
4747

48-
// txSpendsPrevOut returns whether the given prevout is spent by the given
48+
// TxSpendsPrevOut returns whether the given prevout is spent by the given
4949
// transaction.
50-
func txSpendsPrevOut(tx *wire.MsgTx, prevOut *wire.OutPoint) bool {
50+
func TxSpendsPrevOut(tx *wire.MsgTx, prevOut *wire.OutPoint) bool {
5151
for _, txIn := range tx.TxIn {
5252
if txIn.PreviousOutPoint == *prevOut {
5353
return true

proof/verifier.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -877,7 +877,7 @@ func (p *Proof) Verify(ctx context.Context, prev *AssetSnapshot,
877877
return nil, fmt.Errorf("%w: prev output mismatch",
878878
commitment.ErrInvalidTaprootProof)
879879
}
880-
if !txSpendsPrevOut(&p.AnchorTx, &p.PrevOut) {
880+
if !TxSpendsPrevOut(&p.AnchorTx, &p.PrevOut) {
881881
return nil, fmt.Errorf("%w: doesn't spend prev output",
882882
commitment.ErrInvalidTaprootProof)
883883
}

rpcserver.go

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4476,34 +4476,32 @@ func (r *rpcServer) FetchSupplyLeaves(ctx context.Context,
44764476
// both a supplycommit.RootCommitment and supplycommit.ChainProof.
44774477
func unmarshalSupplyCommitChainData(
44784478
rpcData *unirpc.SupplyCommitChainData) (*supplycommit.RootCommitment,
4479-
*supplycommit.ChainProof, error) {
4479+
error) {
44804480

44814481
if rpcData == nil {
4482-
return nil, nil, fmt.Errorf("supply commit chain data is nil")
4482+
return nil, fmt.Errorf("supply commit chain data is nil")
44834483
}
44844484

44854485
var txn wire.MsgTx
44864486
err := txn.Deserialize(bytes.NewReader(rpcData.Txn))
44874487
if err != nil {
4488-
return nil, nil, fmt.Errorf("unable to deserialize "+
4489-
"transaction: %w", err)
4488+
return nil, fmt.Errorf("unable to deserialize transaction: %w",
4489+
err)
44904490
}
44914491

44924492
internalKey, err := btcec.ParsePubKey(rpcData.InternalKey)
44934493
if err != nil {
4494-
return nil, nil, fmt.Errorf("unable to parse internal key: %w",
4495-
err)
4494+
return nil, fmt.Errorf("unable to parse internal key: %w", err)
44964495
}
44974496

44984497
outputKey, err := btcec.ParsePubKey(rpcData.OutputKey)
44994498
if err != nil {
4500-
return nil, nil, fmt.Errorf("unable to parse output key: %w",
4501-
err)
4499+
return nil, fmt.Errorf("unable to parse output key: %w", err)
45024500
}
45034501

45044502
// Convert supply root hash.
45054503
if len(rpcData.SupplyRootHash) != 32 {
4506-
return nil, nil, fmt.Errorf("invalid supply root hash size: "+
4504+
return nil, fmt.Errorf("invalid supply root hash size: "+
45074505
"expected %d, got %d", 32, len(rpcData.SupplyRootHash))
45084506
}
45094507
var supplyRootHash mssmt.NodeHash
@@ -4513,7 +4511,7 @@ func unmarshalSupplyCommitChainData(
45134511
var commitmentBlock fn.Option[supplycommit.CommitmentBlock]
45144512
if len(rpcData.BlockHash) > 0 {
45154513
if len(rpcData.BlockHash) != chainhash.HashSize {
4516-
return nil, nil, fmt.Errorf("invalid block hash size: "+
4514+
return nil, fmt.Errorf("invalid block hash size: "+
45174515
"expected %d, got %d", chainhash.HashSize,
45184516
len(rpcData.BlockHash))
45194517
}
@@ -4543,25 +4541,25 @@ func unmarshalSupplyCommitChainData(
45434541
var blockHeader wire.BlockHeader
45444542
err = blockHeader.Deserialize(bytes.NewReader(rpcData.BlockHeader))
45454543
if err != nil {
4546-
return nil, nil, fmt.Errorf("unable to deserialize block "+
4547-
"header: %w", err)
4544+
return nil, fmt.Errorf("unable to deserialize block header: "+
4545+
"%w", err)
45484546
}
45494547

45504548
var merkleProof proof.TxMerkleProof
45514549
err = merkleProof.Decode(bytes.NewReader(rpcData.TxBlockMerkleProof))
45524550
if err != nil {
4553-
return nil, nil, fmt.Errorf("unable to decode merkle proof: %w",
4554-
err)
4551+
return nil, fmt.Errorf("unable to decode merkle proof: %w", err)
45554552
}
45564553

4557-
chainProof := &supplycommit.ChainProof{
4558-
Header: blockHeader,
4559-
BlockHeight: rpcData.BlockHeight,
4560-
MerkleProof: merkleProof,
4554+
rootCommitment.CommitmentBlock = fn.Some(supplycommit.CommitmentBlock{
4555+
Height: rpcData.BlockHeight,
4556+
Hash: blockHeader.BlockHash(),
45614557
TxIndex: rpcData.TxIndex,
4562-
}
4558+
BlockHeader: &blockHeader,
4559+
MerkleProof: &merkleProof,
4560+
})
45634561

4564-
return rootCommitment, chainProof, nil
4562+
return rootCommitment, nil
45654563
}
45664564

45674565
// InsertSupplyCommit stores a verified supply commitment for the given
@@ -4582,14 +4580,24 @@ func (r *rpcServer) InsertSupplyCommit(ctx context.Context,
45824580
groupPubKey.SerializeCompressed())
45834581

45844582
// Unmarshal the supply commit chain data.
4585-
rootCommitment, chainProof, err := unmarshalSupplyCommitChainData(
4586-
req.ChainData,
4587-
)
4583+
rootCommitment, err := unmarshalSupplyCommitChainData(req.ChainData)
45884584
if err != nil {
45894585
return nil, fmt.Errorf("failed to unmarshal chain data: %w",
45904586
err)
45914587
}
45924588

4589+
if req.SpentCommitmentOutpoint != nil {
4590+
op, err := rpcutils.UnmarshalOutPoint(
4591+
req.SpentCommitmentOutpoint,
4592+
)
4593+
if err != nil {
4594+
return nil, fmt.Errorf("failed to parse spent "+
4595+
"commitment outpoint: %w", err)
4596+
}
4597+
4598+
rootCommitment.SpentCommitment = fn.Some(op)
4599+
}
4600+
45934601
supplyLeaves, err := unmarshalSupplyLeaves(
45944602
req.IssuanceLeaves, req.BurnLeaves, req.IgnoreLeaves,
45954603
)
@@ -4606,7 +4614,7 @@ func (r *rpcServer) InsertSupplyCommit(ctx context.Context,
46064614

46074615
assetSpec := asset.NewSpecifierFromGroupKey(*groupPubKey)
46084616
err = r.cfg.SupplyVerifyManager.InsertSupplyCommit(
4609-
ctx, assetSpec, *rootCommitment, *supplyLeaves, *chainProof,
4617+
ctx, assetSpec, *rootCommitment, *supplyLeaves,
46104618
)
46114619
if err != nil {
46124620
return nil, fmt.Errorf("failed to insert supply commitment: %w",

tapdb/supply_commit.go

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -901,6 +901,198 @@ func (s *SupplyCommitMachine) InsertSignedCommitTx(ctx context.Context,
901901
})
902902
}
903903

904+
// InsertSupplyCommit inserts a new, fully complete supply commitment into the
905+
// database.
906+
func (s *SupplyCommitMachine) InsertSupplyCommit(ctx context.Context,
907+
assetSpec asset.Specifier, commit supplycommit.RootCommitment,
908+
leaves supplycommit.SupplyLeaves) error {
909+
910+
groupKey := assetSpec.UnwrapGroupKeyToPtr()
911+
if groupKey == nil {
912+
return ErrMissingGroupKey
913+
}
914+
groupKeyBytes := groupKey.SerializeCompressed()
915+
916+
commitTx := commit.Txn
917+
internalKey := commit.InternalKey
918+
outputKey := commit.OutputKey
919+
outputIndex := commit.TxOutIdx
920+
921+
block, err := commit.CommitmentBlock.UnwrapOrErr(
922+
supplycommit.ErrNoBlockInfo,
923+
)
924+
if err != nil {
925+
return fmt.Errorf("failed to unwrap commitment block: %w", err)
926+
}
927+
928+
writeTx := WriteTxOption()
929+
return s.db.ExecTx(ctx, writeTx, func(db SupplyCommitStore) error {
930+
// Next, we'll upsert the chain transaction on disk. The block
931+
// related fields are nil as this hasn't been confirmed yet.
932+
var txBytes bytes.Buffer
933+
if err := commitTx.Serialize(&txBytes); err != nil {
934+
return fmt.Errorf("failed to serialize commit "+
935+
"tx: %w", err)
936+
}
937+
txid := commitTx.TxHash()
938+
chainTxID, err := db.UpsertChainTx(ctx, UpsertChainTxParams{
939+
Txid: txid[:],
940+
RawTx: txBytes.Bytes(),
941+
})
942+
if err != nil {
943+
return fmt.Errorf("failed to upsert commit chain tx: "+
944+
"%w", err)
945+
}
946+
947+
// Upsert the internal key to get its ID. We assume key family
948+
// and index 0 for now, as this key is likely externally.
949+
internalKeyID, err := db.UpsertInternalKey(ctx, InternalKey{
950+
RawKey: internalKey.PubKey.SerializeCompressed(),
951+
KeyFamily: int32(internalKey.Family),
952+
KeyIndex: int32(internalKey.Index),
953+
})
954+
if err != nil {
955+
return fmt.Errorf("failed to upsert internal key %x: "+
956+
"%w", internalKey.PubKey.SerializeCompressed(),
957+
err)
958+
}
959+
960+
// Now we fetch the previous commitment that is being spent by
961+
// this one.
962+
var spentCommitment sql.NullInt64
963+
err = fn.MapOptionZ(
964+
commit.SpentCommitment, func(op wire.OutPoint) error {
965+
q := sqlc.QuerySupplyCommitmentByOutpointParams{
966+
GroupKey: groupKeyBytes,
967+
Txid: op.Hash[:],
968+
OutputIndex: sqlInt32(op.Index),
969+
}
970+
row, err := db.QuerySupplyCommitmentByOutpoint(
971+
ctx, q,
972+
)
973+
if err != nil {
974+
return fmt.Errorf("failed to query "+
975+
"spent commitment: %w", err)
976+
}
977+
978+
spentCommitment = sqlInt64(
979+
row.SupplyCommitment.CommitID,
980+
)
981+
982+
return nil
983+
},
984+
)
985+
986+
// Insert the new commitment record. Chain details (block
987+
// height, header, proof, output index) are NULL at this stage.
988+
newCommitmentID, err := db.InsertSupplyCommitment(
989+
//nolint:lll
990+
ctx, sqlc.InsertSupplyCommitmentParams{
991+
GroupKey: groupKeyBytes,
992+
ChainTxnID: chainTxID,
993+
InternalKeyID: internalKeyID,
994+
OutputKey: outputKey.SerializeCompressed(),
995+
SupplyRootHash: nil,
996+
SupplyRootSum: sql.NullInt64{},
997+
OutputIndex: sqlInt32(outputIndex),
998+
SpentCommitment: spentCommitment,
999+
},
1000+
)
1001+
if err != nil {
1002+
return fmt.Errorf("failed to insert new supply "+
1003+
"commitment: %w", err)
1004+
}
1005+
1006+
// Update the commitment record with the calculated root hash
1007+
// and sum.
1008+
finalRootSupplyRoot, err := applySupplyUpdatesInternal(
1009+
ctx, db, assetSpec, leaves.AllUpdates(),
1010+
)
1011+
if err != nil {
1012+
return fmt.Errorf("failed to apply SMT updates: "+
1013+
"%w", err)
1014+
}
1015+
finalRootHash := finalRootSupplyRoot.NodeHash()
1016+
finalRootSum := finalRootSupplyRoot.NodeSum()
1017+
err = db.UpdateSupplyCommitmentRoot(
1018+
ctx, UpdateSupplyCommitmentRootParams{
1019+
CommitID: newCommitmentID,
1020+
SupplyRootHash: finalRootHash[:],
1021+
SupplyRootSum: sqlInt64(int64(finalRootSum)),
1022+
},
1023+
)
1024+
if err != nil {
1025+
return fmt.Errorf("failed to update commitment root "+
1026+
"hash/sum for commit %d: %w",
1027+
newCommitmentID, err)
1028+
}
1029+
1030+
// Next, we'll serialize the merkle proofs and block header, so
1031+
// we can update them on disk.
1032+
var (
1033+
proofBuf bytes.Buffer
1034+
headerBuf bytes.Buffer
1035+
)
1036+
1037+
err = block.MerkleProof.Encode(&proofBuf)
1038+
if err != nil {
1039+
return fmt.Errorf("failed to encode "+
1040+
"merkle proof: %w", err)
1041+
}
1042+
err = block.BlockHeader.Serialize(&headerBuf)
1043+
if err != nil {
1044+
return fmt.Errorf("failed to "+
1045+
"serialize block header: %w",
1046+
err)
1047+
}
1048+
blockHeight := sqlInt32(block.Height)
1049+
1050+
// With all the information serialized above, we'll now update
1051+
// the chain proof information for this current supply commit.
1052+
err = db.UpdateSupplyCommitmentChainDetails(
1053+
ctx, SupplyCommitChainDetails{
1054+
CommitID: newCommitmentID,
1055+
MerkleProof: proofBuf.Bytes(),
1056+
OutputIndex: sqlInt32(commit.TxOutIdx),
1057+
BlockHeader: headerBuf.Bytes(),
1058+
ChainTxnID: chainTxID,
1059+
BlockHeight: blockHeight,
1060+
},
1061+
)
1062+
if err != nil {
1063+
return fmt.Errorf("failed to update commitment chain "+
1064+
"details: %w", err)
1065+
}
1066+
1067+
// Also update the chain_txns record itself with the
1068+
// confirmation details (block hash, height, index).
1069+
var commitTxBytes bytes.Buffer
1070+
err = commit.Txn.Serialize(&commitTxBytes)
1071+
if err != nil {
1072+
return fmt.Errorf("failed to serialize commit tx for "+
1073+
"update: %w", err)
1074+
}
1075+
commitTxid := commit.Txn.TxHash()
1076+
1077+
_, err = db.UpsertChainTx(ctx, UpsertChainTxParams{
1078+
Txid: commitTxid[:],
1079+
RawTx: commitTxBytes.Bytes(),
1080+
ChainFees: 0,
1081+
BlockHash: lnutils.ByteSlice(
1082+
block.BlockHeader.BlockHash(),
1083+
),
1084+
BlockHeight: blockHeight,
1085+
TxIndex: sqlInt32(block.TxIndex),
1086+
})
1087+
if err != nil {
1088+
return fmt.Errorf("failed to update chain_txns "+
1089+
"confirmation: %w", err)
1090+
}
1091+
1092+
return nil
1093+
})
1094+
}
1095+
9041096
// CommitState commits the state of the state machine to disk.
9051097
func (s *SupplyCommitMachine) CommitState(ctx context.Context,
9061098
assetSpec asset.Specifier, state supplycommit.State) error {

0 commit comments

Comments
 (0)