Skip to content

Commit

Permalink
Merge pull request #965 from lightninglabs/duplicate-asset-insert
Browse files Browse the repository at this point in the history
Fix duplicate asset creation in itest
  • Loading branch information
Roasbeef authored Jun 24, 2024
2 parents 7dfa9bf + e6713c5 commit b7117ea
Show file tree
Hide file tree
Showing 23 changed files with 734 additions and 594 deletions.
10 changes: 10 additions & 0 deletions itest/addrs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,16 @@ func runMultiSendTest(ctxt context.Context, t *harnessTest, alice,
return nil
}, defaultTimeout/2)
require.NoError(t.t, err)

// We start out with two assets at Alice, one normal and one grouped. We
// then send it to Bob and to ourselves, with change being created. So
// in each round we turn one of the assets into 3 pieces (two
// self-transfers via addresses and one change output).
if runIdx == 0 {
AssertNumAssets(t.t, ctxt, alice, 3+1)
} else {
AssertNumAssets(t.t, ctxt, alice, 3+(runIdx*3))
}
}

// sendProof manually exports a proof from the given source node and imports it
Expand Down
4 changes: 3 additions & 1 deletion itest/assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2016,7 +2016,7 @@ func assetRoots(ctx context.Context, uni unirpc.UniverseClient,
// assertNumAssetOutputs makes sure the given node has exactly the given number
// of asset outputs for the specified asset ID.
func assertNumAssetOutputs(t *testing.T, client taprpc.TaprootAssetsClient,
assetID []byte, numPieces int) {
assetID []byte, numPieces int) []*taprpc.Asset {

ctxb := context.Background()
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
Expand All @@ -2037,4 +2037,6 @@ func assertNumAssetOutputs(t *testing.T, client taprpc.TaprootAssetsClient,
}

require.Len(t, outputs, numPieces)

return outputs
}
33 changes: 31 additions & 2 deletions itest/psbt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2526,15 +2526,30 @@ func testPsbtLockTimeSend(t *harnessTest) {
numTransfers, numOutputs,
)

// Make sure the transfer shows the lock time.
resp, err := bob.ListTransfers(ctxb, &taprpc.ListTransfersRequest{})
require.NoError(t.t, err)
require.Len(t.t, resp.Transfers, numTransfers)
require.EqualValues(
t.t, lockTimeBlocks, resp.Transfers[0].Outputs[0].LockTime,
)
require.EqualValues(
t.t, 0, resp.Transfers[0].Outputs[0].RelativeLockTime,
)

// This is an interactive transfer, so we do need to manually
// send the proof from the sender to the receiver.
_ = sendProof(
t, bob, alice, sendRespSpend,
aliceScriptKey.PubKey.SerializeCompressed(), genInfo,
)

assertNumAssetOutputs(t.t, alice, genInfo.AssetId, 1)
spendOuts := assertNumAssetOutputs(t.t, alice, genInfo.AssetId, 1)
assertNumAssetOutputs(t.t, bob, genInfo.AssetId, 0)

// Does the asset also show the correct lock times?
require.EqualValues(t.t, lockTimeBlocks, spendOuts[0].LockTime)
require.EqualValues(t.t, 0, spendOuts[0].RelativeLockTime)
}

// testPsbtRelativeLockTimeSend tests that we can send minted assets into a
Expand Down Expand Up @@ -2719,15 +2734,29 @@ func testPsbtRelativeLockTimeSend(t *harnessTest) {
numTransfers, numOutputs,
)

// Make sure the transfer shows the lock time.
resp, err := bob.ListTransfers(ctxb, &taprpc.ListTransfersRequest{})
require.NoError(t.t, err)
require.Len(t.t, resp.Transfers, numTransfers)
require.EqualValues(
t.t, lockTimeBlocks,
resp.Transfers[0].Outputs[0].RelativeLockTime,
)
require.EqualValues(t.t, 0, resp.Transfers[0].Outputs[0].LockTime)

// This is an interactive transfer, so we do need to manually
// send the proof from the sender to the receiver.
_ = sendProof(
t, bob, alice, sendRespSpend,
aliceScriptKey.PubKey.SerializeCompressed(), genInfo,
)

assertNumAssetOutputs(t.t, alice, genInfo.AssetId, 1)
spendOuts := assertNumAssetOutputs(t.t, alice, genInfo.AssetId, 1)
assertNumAssetOutputs(t.t, bob, genInfo.AssetId, 0)

// Does the asset also show the correct lock times?
require.EqualValues(t.t, 0, spendOuts[0].LockTime)
require.EqualValues(t.t, lockTimeBlocks, spendOuts[0].RelativeLockTime)
}

// testPsbtRelativeLockTimeSendProofFail tests that we can send minted assets
Expand Down
2 changes: 2 additions & 0 deletions rpcserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -3291,6 +3291,8 @@ func marshalOutboundParcel(
ScriptKey: scriptPubKey.SerializeCompressed(),
ScriptKeyIsLocal: out.ScriptKeyLocal,
Amount: out.Amount,
LockTime: out.LockTime,
RelativeLockTime: out.RelativeLockTime,
NewProofBlob: out.ProofSuffix,
SplitCommitRootHash: splitCommitRoot,
OutputType: rpcOutType,
Expand Down
31 changes: 15 additions & 16 deletions tapdb/assets_common.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,10 @@ type UpsertAssetStore interface {
QueryAssets(context.Context, QueryAssetFilters) ([]ConfirmedAsset,
error)

// InsertNewAsset inserts a new asset on disk.
InsertNewAsset(ctx context.Context,
arg sqlc.InsertNewAssetParams) (int64, error)
// UpsertAsset inserts a new asset on disk, or returns the ID of the
// existing asset if it already exists.
UpsertAsset(ctx context.Context,
arg sqlc.UpsertAssetParams) (int64, error)

// UpsertAssetMeta inserts a new asset meta into the DB.
UpsertAssetMeta(ctx context.Context, arg NewAssetMeta) (int64, error)
Expand Down Expand Up @@ -263,19 +264,17 @@ func upsertAssetsWithGenesis(ctx context.Context, q UpsertAssetStore,

// With all the dependent data inserted, we can now insert the
// base asset information itself.
assetIDs[idx], err = q.InsertNewAsset(
ctx, sqlc.InsertNewAssetParams{
GenesisID: genAssetID,
Version: int32(a.Version),
ScriptKeyID: scriptKeyID,
AssetGroupWitnessID: groupWitnessID,
ScriptVersion: int32(a.ScriptVersion),
Amount: int64(a.Amount),
LockTime: sqlInt32(a.LockTime),
RelativeLockTime: sqlInt32(a.RelativeLockTime),
AnchorUtxoID: anchorUtxoID,
},
)
assetIDs[idx], err = q.UpsertAsset(ctx, sqlc.UpsertAssetParams{
GenesisID: genAssetID,
Version: int32(a.Version),
ScriptKeyID: scriptKeyID,
AssetGroupWitnessID: groupWitnessID,
ScriptVersion: int32(a.ScriptVersion),
Amount: int64(a.Amount),
LockTime: sqlInt32(a.LockTime),
RelativeLockTime: sqlInt32(a.RelativeLockTime),
AnchorUtxoID: anchorUtxoID,
})
if err != nil {
return 0, nil, fmt.Errorf("unable to insert asset: %w",
err)
Expand Down
19 changes: 11 additions & 8 deletions tapdb/assets_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -2395,6 +2395,8 @@ func insertAssetTransferOutput(ctx context.Context, q ActiveAssetsStore,
ScriptKey: scriptKeyID,
ScriptKeyLocal: output.ScriptKeyLocal,
Amount: int64(output.Amount),
LockTime: sqlInt32(output.LockTime),
RelativeLockTime: sqlInt32(output.RelativeLockTime),
AssetVersion: int32(output.AssetVersion),
SerializedWitnesses: witnessBuf.Bytes(),
ProofSuffix: output.ProofSuffix,
Expand Down Expand Up @@ -2513,9 +2515,11 @@ func fetchAssetTransferOutputs(ctx context.Context, q ActiveAssetsStore,
}

outputs[idx] = tapfreighter.TransferOutput{
Anchor: outputAnchor,
Amount: uint64(dbOut.Amount),
AssetVersion: asset.Version(dbOut.AssetVersion),
Anchor: outputAnchor,
Amount: uint64(dbOut.Amount),
LockTime: uint64(dbOut.LockTime.Int32),
RelativeLockTime: uint64(dbOut.RelativeLockTime.Int32),
AssetVersion: asset.Version(dbOut.AssetVersion),
ScriptKey: asset.ScriptKey{
PubKey: scriptKey,
TweakedScriptKey: &asset.TweakedScriptKey{
Expand Down Expand Up @@ -2801,17 +2805,16 @@ func (a *AssetStore) ConfirmParcelDelivery(ctx context.Context,
// inputs as a template for the new asset, since the
// genesis and group key will be the same. We'll
// overwrite all other fields.
//
// TODO(guggero): This will need an update once we want
// to support full lock_time and relative_lock_time
// support.
templateID := spentAssetIDs[0]
params := ApplyPendingOutput{
ScriptKeyID: out.ScriptKeyID,
AnchorUtxoID: sqlInt64(
out.AnchorUtxoID,
),
Amount: out.Amount,
Amount: out.Amount,
LockTime: out.LockTime,
RelativeLockTime: out.RelativeLockTime,
//nolint:lll
SplitCommitmentRootHash: out.SplitCommitmentRootHash,
SplitCommitmentRootValue: out.SplitCommitmentRootValue,
SpentAssetID: templateID,
Expand Down
10 changes: 6 additions & 4 deletions tapdb/assets_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1370,10 +1370,12 @@ func TestAssetExportLog(t *testing.T) {
TaprootAssetRoot: bytes.Repeat([]byte{0x1}, 32),
MerkleRoot: bytes.Repeat([]byte{0x1}, 32),
},
ScriptKey: newScriptKey,
ScriptKeyLocal: true,
Amount: uint64(newAmt),
WitnessData: []asset.Witness{newWitness},
ScriptKey: newScriptKey,
ScriptKeyLocal: true,
Amount: uint64(newAmt),
LockTime: 1337,
RelativeLockTime: 31337,
WitnessData: []asset.Witness{newWitness},
SplitCommitmentRoot: mssmt.NewComputedNode(
newRootHash, newRootValue,
),
Expand Down
85 changes: 45 additions & 40 deletions tapdb/sqlc/assets.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tapdb/sqlc/migrations/000020_asset_unique_key.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP INDEX IF EXISTS assets_genesis_id_script_key_id_anchor_utxo_id_unique;
4 changes: 4 additions & 0 deletions tapdb/sqlc/migrations/000020_asset_unique_key.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CREATE UNIQUE INDEX assets_genesis_id_script_key_id_anchor_utxo_id_unique
ON assets (
genesis_id, script_key_id, anchor_utxo_id
);
2 changes: 2 additions & 0 deletions tapdb/sqlc/migrations/000021_transfer_lock_times.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE asset_transfer_outputs DROP COLUMN lock_time;
ALTER TABLE asset_transfer_outputs DROP COLUMN relative_lock_time;
2 changes: 2 additions & 0 deletions tapdb/sqlc/migrations/000021_transfer_lock_times.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE asset_transfer_outputs ADD COLUMN lock_time INTEGER;
ALTER TABLE asset_transfer_outputs ADD COLUMN relative_lock_time INTEGER;
2 changes: 2 additions & 0 deletions tapdb/sqlc/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion tapdb/sqlc/querier.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions tapdb/sqlc/queries/assets.sql
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,18 @@ INSERT INTO genesis_assets (
DO UPDATE SET asset_id = EXCLUDED.asset_id
RETURNING gen_asset_id;

-- name: InsertNewAsset :one
-- name: UpsertAsset :one
INSERT INTO assets (
genesis_id, version, script_key_id, asset_group_witness_id, script_version,
amount, lock_time, relative_lock_time, anchor_utxo_id, spent
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10
) RETURNING asset_id;
)
ON CONFLICT (genesis_id, script_key_id, anchor_utxo_id)
-- This is a NOP, anchor_utxo_id is one of the unique fields that caused the
-- conflict.
DO UPDATE SET anchor_utxo_id = EXCLUDED.anchor_utxo_id
RETURNING asset_id;

-- name: FetchAssetsForBatch :many
WITH genesis_info AS (
Expand Down
Loading

0 comments on commit b7117ea

Please sign in to comment.