Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
0f4df13
supplycommit: check asset metadata before starting state machine
ffranr Aug 11, 2025
01d0dbf
supplyverifier: add SupplySyncer for syncing supply commit leaves
ffranr Aug 22, 2025
4bd7517
tapdb: add supply syncer push log table
ffranr Aug 6, 2025
d4bf3b9
tapdb: add SQL queries for supply syncer push log
ffranr Aug 6, 2025
7568636
tapdb: add SupplySyncerStore
ffranr Aug 6, 2025
dff7c5f
proof: rename txSpendsPrevOut for export
ffranr Aug 20, 2025
d92ae38
supplycommit: rename applyTreeUpdates for export
ffranr Aug 22, 2025
1802abc
supplycommit: add UpdateRootSupplyTree for verifier use
ffranr Aug 22, 2025
bb11776
tapdb: extract fetchRootSupplyTreeInternal from FetchRootSupplyTree
ffranr Aug 22, 2025
a06697c
tapdb: add FetchSupplyTrees to retrieve all supply trees atomically
ffranr Aug 22, 2025
fa14439
supplyverifier: add placeholder supply verifier state machine manager
ffranr Aug 22, 2025
e588fcf
taprootassets: add supply verifier manager to main server config
ffranr Aug 8, 2025
0b4b0e9
universerpc: add InsertSupplyCommit RPC endpoint
ffranr Sep 3, 2025
80e58e0
taprpc: remove FetchSupplyLeaves from public uni whitelist
ffranr Aug 12, 2025
5897e62
multi: move inclusion proof feature to FetchSupplyLeaves RPC
ffranr Aug 12, 2025
02f9798
rpcserver: add helper function unmarshalGroupKey
ffranr Aug 20, 2025
94a761e
rpcserver: add marshalSupplyLeaves and unmarshalSupplyLeaves
ffranr Sep 3, 2025
f991952
supplycommit: push supply commit to remote uni in CommitFinalizeState
ffranr Sep 3, 2025
818e87f
tapdb+universe: consolidate code, link commitment to previous one
ffranr Aug 22, 2025
a479006
rpcserver: set previously added BlockHeader and MerkleProof fields
ffranr Aug 21, 2025
b5e5642
tapdb+universe: add SpentCommitment to RootCommitment
guggero Aug 15, 2025
8897e41
rpc: add field spent_commitment_outpoint to InsertSupplyCommitRequest
ffranr Aug 21, 2025
0165ccc
taprootassets: add RpcSupplySync to bridge RPC and verifier syncer
ffranr Aug 25, 2025
0d4a700
tapcfg: pass the supply syncer into the supply commit manager
ffranr Sep 3, 2025
f92176d
supplycommit: improve robustness of ApplyTreeUpdates
ffranr Aug 26, 2025
073b5d5
supplyverifier: add verification logic and use in uni server insert
ffranr Aug 23, 2025
a282e5e
supplyverifier+tapdb: add supply commit insert functionality
ffranr Aug 23, 2025
d47c17b
mssmt: add CopyFilter to Tree interface and tree implementations
ffranr Aug 25, 2025
29ad936
supplycommit+tapdb: extend FetchSubTrees with block height end param
ffranr Aug 25, 2025
8081f0f
supplyverifier: add FetchCommitment method
ffranr Aug 25, 2025
dc18c74
universerpc: add locator and update response in FetchSupplyCommit RPC
ffranr Aug 25, 2025
7269bb3
supplycommit: remove FetchCommitment from manager
ffranr Aug 25, 2025
d142812
itest: extend testSupplyCommitIgnoreAsset to fetch from universe server
ffranr Aug 26, 2025
ded200f
docs: add release notes
ffranr Aug 26, 2025
32c401c
supplysync_rpc: add support for FetchSupplyCommit RPC endpoint
ffranr Aug 29, 2025
94b1ad6
supplyverifier: add supply commit pull functionality to syncer
ffranr Aug 29, 2025
258c734
supplycommit: refactor by introducing startAssetSM method
ffranr Aug 29, 2025
f4ea8f0
supplyverifier: refactor by introducing startAssetSM method
ffranr Aug 29, 2025
5d936e9
tapdb: add TapAddressBook.FetchSupplyCommitAssets
ffranr Aug 29, 2025
59f9b07
tapdb: add SQL query QueryLatestSupplyCommitment
ffranr Sep 1, 2025
b88b574
tapdb: add method FetchLatestCommitment to SupplyCommitMachine db store
ffranr Sep 1, 2025
f612b00
supplyverifier: init state machines for asset groups with commitments
ffranr Aug 29, 2025
a5ac1c3
supplycommit: refactor asset check for reuse in supplyverifier package
ffranr Sep 1, 2025
17365f6
supplyverifier: validate asset group before starting state machine
ffranr Sep 1, 2025
c857617
WIP: supplyverifier: enhance supply verifier state machine
ffranr Sep 1, 2025
1c9da69
WIP: supplyverifier: remove state log; we don't need to save state ma…
ffranr Sep 1, 2025
17afcc0
tapdb: rename unit test functionality for new term "supply"
ffranr Sep 2, 2025
ba63071
tapdb: rename table to supply_pre_commits and unique on outpoint
ffranr Sep 2, 2025
a02ba50
tapdb: rename FetchMintAnchorUniCommitment to FetchSupplyPreCommits
ffranr Sep 2, 2025
fd84f6f
tapdb: rename UpsertMintAnchorUniCommitment to UpsertSupplyPreCommit
ffranr Sep 2, 2025
87ca58f
WIP
ffranr Sep 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 35 additions & 35 deletions cmd/commands/universe.go
Original file line number Diff line number Diff line change
Expand Up @@ -1638,21 +1638,6 @@ var fetchSupplyCommitCmd = cli.Command{
Usage: "the group key of the asset group to fetch",
Required: true,
},
&cli.StringSliceFlag{
Name: "issuance_leaf_keys",
Usage: "a list of issuance leaf keys to fetch " +
"inclusion proofs for",
},
&cli.StringSliceFlag{
Name: "burn_leaf_keys",
Usage: "a list of burn leaf keys to fetch inclusion " +
"proofs for",
},
&cli.StringSliceFlag{
Name: "ignore_leaf_keys",
Usage: "a list of ignore leaf keys to fetch " +
"inclusion proofs for",
},
},
Action: fetchSupplyCommit,
}
Expand All @@ -1668,26 +1653,6 @@ func fetchSupplyCommit(ctx *cli.Context) error {
},
}

issuanceKeys, err := parseHexStrings(
ctx.StringSlice("issuance_leaf_keys"),
)
if err != nil {
return fmt.Errorf("invalid issuance_leaf_keys: %w", err)
}
req.IssuanceLeafKeys = issuanceKeys

burnKeys, err := parseHexStrings(ctx.StringSlice("burn_leaf_keys"))
if err != nil {
return fmt.Errorf("invalid burn_leaf_keys: %w", err)
}
req.BurnLeafKeys = burnKeys

ignoreKeys, err := parseHexStrings(ctx.StringSlice("ignore_leaf_keys"))
if err != nil {
return fmt.Errorf("invalid ignore_leaf_keys: %w", err)
}
req.IgnoreLeafKeys = ignoreKeys

resp, err := client.FetchSupplyCommit(cliCtx, req)
if err != nil {
return err
Expand Down Expand Up @@ -1719,6 +1684,21 @@ var fetchSupplyLeavesCmd = cli.Command{
Usage: "the end of the block height range",
Required: true,
},
&cli.StringSliceFlag{
Name: "issuance_leaf_keys",
Usage: "a list of issuance leaf keys to fetch " +
"inclusion proofs for",
},
&cli.StringSliceFlag{
Name: "burn_leaf_keys",
Usage: "a list of burn leaf keys to fetch inclusion " +
"proofs for",
},
&cli.StringSliceFlag{
Name: "ignore_leaf_keys",
Usage: "a list of ignore leaf keys to fetch " +
"inclusion proofs for",
},
},
Action: fetchSupplyLeaves,
}
Expand All @@ -1736,6 +1716,26 @@ func fetchSupplyLeaves(ctx *cli.Context) error {
BlockHeightEnd: uint32(ctx.Uint64("block_height_end")),
}

issuanceKeys, err := parseHexStrings(
ctx.StringSlice("issuance_leaf_keys"),
)
if err != nil {
return fmt.Errorf("invalid issuance_leaf_keys: %w", err)
}
req.IssuanceLeafKeys = issuanceKeys

burnKeys, err := parseHexStrings(ctx.StringSlice("burn_leaf_keys"))
if err != nil {
return fmt.Errorf("invalid burn_leaf_keys: %w", err)
}
req.BurnLeafKeys = burnKeys

ignoreKeys, err := parseHexStrings(ctx.StringSlice("ignore_leaf_keys"))
if err != nil {
return fmt.Errorf("invalid ignore_leaf_keys: %w", err)
}
req.IgnoreLeafKeys = ignoreKeys

resp, err := client.FetchSupplyLeaves(cliCtx, req)
if err != nil {
return err
Expand Down
6 changes: 6 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/lightninglabs/taproot-assets/tapgarden"
"github.com/lightninglabs/taproot-assets/universe"
"github.com/lightninglabs/taproot-assets/universe/supplycommit"
"github.com/lightninglabs/taproot-assets/universe/supplyverifier"
"github.com/lightningnetwork/lnd"
"github.com/lightningnetwork/lnd/build"
"github.com/lightningnetwork/lnd/signal"
Expand Down Expand Up @@ -199,6 +200,11 @@ type Config struct {

IgnoreChecker *tapdb.CachingIgnoreChecker

// SupplyVerifyManager is a service that is used to verify supply
// commitments for assets. Supply commitments are issuer published
// attestations of the total supply of an asset.
SupplyVerifyManager *supplyverifier.Manager

UniverseArchive *universe.Archive

UniverseSyncer universe.Syncer
Expand Down
1 change: 1 addition & 0 deletions docs/release-notes/release-notes-0.7.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
- https://github.com/lightninglabs/taproot-assets/pull/1587
- https://github.com/lightninglabs/taproot-assets/pull/1716
- https://github.com/lightninglabs/taproot-assets/pull/1675
- https://github.com/lightninglabs/taproot-assets/pull/1674

- A new [address version 2 was introduced that supports grouped assets and
custom (sender-defined)
Expand Down
23 changes: 23 additions & 0 deletions fn/func.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,29 @@ func MapErr[I, O any, S []I](s S, f func(I) (O, error)) ([]O, error) {
return output, nil
}

// MapErrWithPtr applies the given fallible mapping function to each element of
// the given slice and generates a new slice. This is identical to MapErr, but
// can be used when the callback returns a pointer, and returns early if any
// single mapping fails.
func MapErrWithPtr[I, O any, S []I](s S, f func(I) (*O, error)) ([]O, error) {
output := make([]O, len(s))
for i, x := range s {
outPtr, err := f(x)
if err != nil {
return nil, err
}

if outPtr == nil {
return nil, fmt.Errorf("nil pointer returned for "+
"item %d", i)
}

output[i] = *outPtr
}

return output, nil
}

// FlatMapErr applies the given mapping function to each element of the given
// slice, concatenates the results into a new slice, and returns an error if
// the mapping function fails.
Expand Down
47 changes: 40 additions & 7 deletions itest/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2786,8 +2786,9 @@ func UpdateAndMineSupplyCommit(t *testing.T, ctx context.Context,
// it when the specified condition is met.
func WaitForSupplyCommit(t *testing.T, ctx context.Context,
tapd unirpc.UniverseClient, groupKeyBytes []byte,
spentCommitOutpoint fn.Option[wire.OutPoint],
condition func(*unirpc.FetchSupplyCommitResponse) bool,
) *unirpc.FetchSupplyCommitResponse {
) (*unirpc.FetchSupplyCommitResponse, wire.OutPoint) {

groupKeyReq := &unirpc.FetchSupplyCommitRequest_GroupKeyBytes{
GroupKeyBytes: groupKeyBytes,
Expand All @@ -2796,18 +2797,50 @@ func WaitForSupplyCommit(t *testing.T, ctx context.Context,
var fetchResp *unirpc.FetchSupplyCommitResponse
var err error

require.Eventually(t, func() bool {
fetchResp, err = tapd.FetchSupplyCommit(
ctx, &unirpc.FetchSupplyCommitRequest{
GroupKey: groupKeyReq,
// By default, we start the fetch from the very first commitment.
// If a spent outpoint is given, we start from there.
req := &unirpc.FetchSupplyCommitRequest{
GroupKey: groupKeyReq,
Locator: &unirpc.FetchSupplyCommitRequest_VeryFirst{
VeryFirst: true,
},
}

// nolint: lll
spentCommitOutpoint.WhenSome(func(outPoint wire.OutPoint) {
req = &unirpc.FetchSupplyCommitRequest{
GroupKey: groupKeyReq,
Locator: &unirpc.FetchSupplyCommitRequest_SpentCommitOutpoint{
SpentCommitOutpoint: &taprpc.OutPoint{
Txid: outPoint.Hash[:],
OutputIndex: outPoint.Index,
},
},
)
}
})

require.Eventually(t, func() bool {
fetchResp, err = tapd.FetchSupplyCommit(ctx, req)
if err != nil {
return false
}

return fetchResp != nil && condition(fetchResp)
}, defaultWaitTimeout, time.Second)

return fetchResp
// Return the supply commit outpoint used to fetch the next supply
// commitment. The next commitment is retrieved by referencing the
// outpoint of the previously spent commitment.
require.NotNil(t, fetchResp)

var msgTx wire.MsgTx
err = msgTx.Deserialize(bytes.NewReader(fetchResp.ChainData.Txn))
require.NoError(t, err)

supplyCommitOutpoint := wire.OutPoint{
Hash: msgTx.TxHash(),
Index: fetchResp.ChainData.TxOutIdx,
}

return fetchResp, supplyCommitOutpoint
}
31 changes: 16 additions & 15 deletions itest/supply_commit_mint_burn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
taprootassets "github.com/lightninglabs/taproot-assets"
"github.com/lightninglabs/taproot-assets/fn"
"github.com/lightninglabs/taproot-assets/taprpc"
Expand Down Expand Up @@ -48,18 +49,19 @@ func testSupplyCommitMintBurn(t *harnessTest) {
// Update the on-chain supply commitment for the asset group.
//
// TODO(roasbeef): still rely on the time based ticker here?
t.Log("Updating and mining supply commitment for asset group")
t.Log("Create first supply commitment tx for asset group")
UpdateAndMineSupplyCommit(
t.t, ctxb, t.tapd, t.lndHarness.Miner().Client,
groupKeyBytes, 1,
)

// Fetch the latest supply commitment for the asset group.
t.Log("Fetching supply commitment to verify mint leaves")
fetchResp := WaitForSupplyCommit(
t.t, ctxb, t.tapd, groupKeyBytes,
t.Log("Fetching first supply commitment to verify mint leaves")
fetchResp, supplyOutpoint := WaitForSupplyCommit(
t.t, ctxb, t.tapd, groupKeyBytes, fn.None[wire.OutPoint](),
func(resp *unirpc.FetchSupplyCommitResponse) bool {
return resp.BlockHeight > 0 && len(resp.BlockHash) > 0
return resp.ChainData.BlockHeight > 0 &&
len(resp.ChainData.BlockHash) > 0
},
)

Expand All @@ -72,7 +74,7 @@ func testSupplyCommitMintBurn(t *harnessTest) {

// Verify the issuance leaf inclusion in the supply tree.
AssertSubtreeInclusionProof(
t, fetchResp.SupplyCommitmentRoot.RootHash,
t, fetchResp.ChainData.SupplyRootHash,
fetchResp.IssuanceSubtreeRoot,
)

Expand Down Expand Up @@ -106,8 +108,6 @@ func testSupplyCommitMintBurn(t *harnessTest) {
)

t.Log("Updating supply commitment after second mint")

// Update and mine the supply commitment after second mint.
UpdateAndMineSupplyCommit(
t.t, ctxb, t.tapd, t.lndHarness.Miner().Client,
groupKeyBytes, 1,
Expand All @@ -119,8 +119,8 @@ func testSupplyCommitMintBurn(t *harnessTest) {
expectedTotal := int64(
mintReq.Asset.Amount + secondMintReq.Asset.Amount,
)
fetchResp = WaitForSupplyCommit(
t.t, ctxb, t.tapd, groupKeyBytes,
fetchResp, supplyOutpoint = WaitForSupplyCommit(
t.t, ctxb, t.tapd, groupKeyBytes, fn.Some(supplyOutpoint),
func(resp *unirpc.FetchSupplyCommitResponse) bool {
return resp.IssuanceSubtreeRoot != nil &&
resp.IssuanceSubtreeRoot.RootNode.RootSum == expectedTotal //nolint:lll
Expand Down Expand Up @@ -175,7 +175,8 @@ func testSupplyCommitMintBurn(t *harnessTest) {
t.Log("Verifying supply tree includes burn leaves")

// Fetch and verify the supply tree now includes burn leaves.
fetchResp = WaitForSupplyCommit(t.t, ctxb, t.tapd, groupKeyBytes,
fetchResp, _ = WaitForSupplyCommit(
t.t, ctxb, t.tapd, groupKeyBytes, fn.Some(supplyOutpoint),
func(resp *unirpc.FetchSupplyCommitResponse) bool {
return resp.BurnSubtreeRoot != nil &&
resp.BurnSubtreeRoot.RootNode.RootSum == int64(burnAmt) //nolint:lll
Expand All @@ -184,7 +185,7 @@ func testSupplyCommitMintBurn(t *harnessTest) {

// Verify the burn subtree inclusion in the supply tree.
AssertSubtreeInclusionProof(
t, fetchResp.SupplyCommitmentRoot.RootHash,
t, fetchResp.ChainData.SupplyRootHash,
fetchResp.BurnSubtreeRoot,
)

Expand Down Expand Up @@ -234,16 +235,16 @@ func testSupplyCommitMintBurn(t *harnessTest) {
block := finalMinedBlocks[0]
blockHash, _ := t.lndHarness.Miner().GetBestBlock()

fetchBlockHash, err := chainhash.NewHash(fetchResp.BlockHash)
fetchBlockHash, err := chainhash.NewHash(fetchResp.ChainData.BlockHash)
require.NoError(t.t, err)
require.True(t.t, fetchBlockHash.IsEqual(blockHash))

// Re-compute the supply commitment root hash from the latest fetch,
// then use that to derive the expected commitment output.
supplyCommitRootHash := fn.ToArray[[32]byte](
fetchResp.SupplyCommitmentRoot.RootHash,
fetchResp.ChainData.SupplyRootHash,
)
internalKey, err := btcec.ParsePubKey(fetchResp.AnchorTxOutInternalKey)
internalKey, err := btcec.ParsePubKey(fetchResp.ChainData.InternalKey)
require.NoError(t.t, err)
expectedTxOut, _, err := supplycommit.RootCommitTxOut(
internalKey, nil, supplyCommitRootHash,
Expand Down
Loading