Skip to content

Commit e8527e9

Browse files
committed
multi: validate and insert supply commit into universe
1 parent 16b3add commit e8527e9

File tree

10 files changed

+695
-285
lines changed

10 files changed

+695
-285
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
@@ -4457,34 +4457,32 @@ func (r *rpcServer) FetchSupplyLeaves(ctx context.Context,
44574457
// both a supplycommit.RootCommitment and supplycommit.ChainProof.
44584458
func unmarshalSupplyCommitChainData(
44594459
rpcData *unirpc.SupplyCommitChainData) (*supplycommit.RootCommitment,
4460-
*supplycommit.ChainProof, error) {
4460+
error) {
44614461

44624462
if rpcData == nil {
4463-
return nil, nil, fmt.Errorf("supply commit chain data is nil")
4463+
return nil, fmt.Errorf("supply commit chain data is nil")
44644464
}
44654465

44664466
var txn wire.MsgTx
44674467
err := txn.Deserialize(bytes.NewReader(rpcData.Txn))
44684468
if err != nil {
4469-
return nil, nil, fmt.Errorf("unable to deserialize "+
4470-
"transaction: %w", err)
4469+
return nil, fmt.Errorf("unable to deserialize transaction: %w",
4470+
err)
44714471
}
44724472

44734473
internalKey, err := btcec.ParsePubKey(rpcData.InternalKey)
44744474
if err != nil {
4475-
return nil, nil, fmt.Errorf("unable to parse internal key: %w",
4476-
err)
4475+
return nil, fmt.Errorf("unable to parse internal key: %w", err)
44774476
}
44784477

44794478
outputKey, err := btcec.ParsePubKey(rpcData.OutputKey)
44804479
if err != nil {
4481-
return nil, nil, fmt.Errorf("unable to parse output key: %w",
4482-
err)
4480+
return nil, fmt.Errorf("unable to parse output key: %w", err)
44834481
}
44844482

44854483
// Convert supply root hash.
44864484
if len(rpcData.SupplyRootHash) != 32 {
4487-
return nil, nil, fmt.Errorf("invalid supply root hash size: "+
4485+
return nil, fmt.Errorf("invalid supply root hash size: "+
44884486
"expected %d, got %d", 32, len(rpcData.SupplyRootHash))
44894487
}
44904488
var supplyRootHash mssmt.NodeHash
@@ -4494,7 +4492,7 @@ func unmarshalSupplyCommitChainData(
44944492
var commitmentBlock fn.Option[supplycommit.CommitmentBlock]
44954493
if len(rpcData.BlockHash) > 0 {
44964494
if len(rpcData.BlockHash) != chainhash.HashSize {
4497-
return nil, nil, fmt.Errorf("invalid block hash size: "+
4495+
return nil, fmt.Errorf("invalid block hash size: "+
44984496
"expected %d, got %d", chainhash.HashSize,
44994497
len(rpcData.BlockHash))
45004498
}
@@ -4524,25 +4522,25 @@ func unmarshalSupplyCommitChainData(
45244522
var blockHeader wire.BlockHeader
45254523
err = blockHeader.Deserialize(bytes.NewReader(rpcData.BlockHeader))
45264524
if err != nil {
4527-
return nil, nil, fmt.Errorf("unable to deserialize block "+
4528-
"header: %w", err)
4525+
return nil, fmt.Errorf("unable to deserialize block header: "+
4526+
"%w", err)
45294527
}
45304528

45314529
var merkleProof proof.TxMerkleProof
45324530
err = merkleProof.Decode(bytes.NewReader(rpcData.TxBlockMerkleProof))
45334531
if err != nil {
4534-
return nil, nil, fmt.Errorf("unable to decode merkle proof: %w",
4535-
err)
4532+
return nil, fmt.Errorf("unable to decode merkle proof: %w", err)
45364533
}
45374534

4538-
chainProof := &supplycommit.ChainProof{
4539-
Header: blockHeader,
4540-
BlockHeight: rpcData.BlockHeight,
4541-
MerkleProof: merkleProof,
4535+
rootCommitment.CommitmentBlock = fn.Some(supplycommit.CommitmentBlock{
4536+
Height: rpcData.BlockHeight,
4537+
Hash: blockHeader.BlockHash(),
45424538
TxIndex: rpcData.TxIndex,
4543-
}
4539+
BlockHeader: &blockHeader,
4540+
MerkleProof: &merkleProof,
4541+
})
45444542

4545-
return rootCommitment, chainProof, nil
4543+
return rootCommitment, nil
45464544
}
45474545

45484546
// InsertSupplyCommit stores a verified supply commitment for the given
@@ -4563,14 +4561,24 @@ func (r *rpcServer) InsertSupplyCommit(ctx context.Context,
45634561
groupPubKey.SerializeCompressed())
45644562

45654563
// Unmarshal the supply commit chain data.
4566-
rootCommitment, chainProof, err := unmarshalSupplyCommitChainData(
4567-
req.ChainData,
4568-
)
4564+
rootCommitment, err := unmarshalSupplyCommitChainData(req.ChainData)
45694565
if err != nil {
45704566
return nil, fmt.Errorf("failed to unmarshal chain data: %w",
45714567
err)
45724568
}
45734569

4570+
if req.SpentCommitmentOutpoint != nil {
4571+
op, err := rpcutils.UnmarshalOutPoint(
4572+
req.SpentCommitmentOutpoint,
4573+
)
4574+
if err != nil {
4575+
return nil, fmt.Errorf("failed to parse spent "+
4576+
"commitment outpoint: %w", err)
4577+
}
4578+
4579+
rootCommitment.SpentCommitment = fn.Some(op)
4580+
}
4581+
45744582
supplyLeaves, err := unmarshalSupplyLeaves(
45754583
req.IssuanceLeaves, req.BurnLeaves, req.IgnoreLeaves,
45764584
)
@@ -4587,7 +4595,7 @@ func (r *rpcServer) InsertSupplyCommit(ctx context.Context,
45874595

45884596
assetSpec := asset.NewSpecifierFromGroupKey(*groupPubKey)
45894597
err = r.cfg.SupplyVerifyManager.InsertSupplyCommit(
4590-
ctx, assetSpec, *rootCommitment, *supplyLeaves, *chainProof,
4598+
ctx, assetSpec, *rootCommitment, *supplyLeaves,
45914599
)
45924600
if err != nil {
45934601
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
@@ -909,6 +909,198 @@ func (s *SupplyCommitMachine) InsertSignedCommitTx(ctx context.Context,
909909
})
910910
}
911911

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

0 commit comments

Comments
 (0)