Skip to content

Commit a17ea40

Browse files
authored
Merge pull request #1185 from Roasbeef/script-key-upsert
tapdb: fix bug w.r.t null bool handling, allow InsertScriptKey to allow flipping known to true
2 parents 7c1cd9a + d453f81 commit a17ea40

File tree

8 files changed

+155
-12
lines changed

8 files changed

+155
-12
lines changed

tapdb/addrs.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,9 @@ func (t *TapAddressBook) QueryAddrs(ctx context.Context,
467467
return fmt.Errorf("unable to make addr: %w", err)
468468
}
469469

470-
declaredKnown := addr.ScriptKeyDeclaredKnown.Valid
470+
declaredKnown := extractBool(
471+
addr.ScriptKeyDeclaredKnown,
472+
)
471473
addrs = append(addrs, address.AddrWithKeyInfo{
472474
Tap: tapAddr,
473475
ScriptKeyTweak: asset.TweakedScriptKey{
@@ -620,9 +622,11 @@ func fetchAddr(ctx context.Context, db AddrBook, params *address.ChainParams,
620622
return &address.AddrWithKeyInfo{
621623
Tap: tapAddr,
622624
ScriptKeyTweak: asset.TweakedScriptKey{
623-
RawKey: scriptKeyDesc,
624-
Tweak: dbAddr.ScriptKeyTweak,
625-
DeclaredKnown: dbAddr.ScriptKeyDeclaredKnown.Valid,
625+
RawKey: scriptKeyDesc,
626+
Tweak: dbAddr.ScriptKeyTweak,
627+
DeclaredKnown: extractBool(
628+
dbAddr.ScriptKeyDeclaredKnown,
629+
),
626630
},
627631
InternalKeyDesc: internalKeyDesc,
628632
TaprootOutputKey: *taprootOutputKey,
@@ -685,6 +689,7 @@ func (t *TapAddressBook) InsertScriptKey(ctx context.Context,
685689
return fmt.Errorf("error inserting internal key: %w",
686690
err)
687691
}
692+
688693
_, err = q.UpsertScriptKey(ctx, NewScriptKey{
689694
InternalKeyID: internalKeyID,
690695
TweakedScriptKey: scriptKey.PubKey.SerializeCompressed(),

tapdb/addrs_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tapdb
33
import (
44
"context"
55
"database/sql"
6+
"fmt"
67
"math/rand"
78
"testing"
89
"time"
@@ -11,9 +12,11 @@ import (
1112
"github.com/btcsuite/btcd/wire"
1213
"github.com/lightninglabs/lndclient"
1314
"github.com/lightninglabs/taproot-assets/address"
15+
"github.com/lightninglabs/taproot-assets/asset"
1416
"github.com/lightninglabs/taproot-assets/internal/test"
1517
"github.com/lightninglabs/taproot-assets/tapdb/sqlc"
1618
"github.com/lightningnetwork/lnd/clock"
19+
"github.com/lightningnetwork/lnd/keychain"
1720
"github.com/lightningnetwork/lnd/lnrpc"
1821
"github.com/stretchr/testify/require"
1922
)
@@ -644,3 +647,113 @@ func TestAddressEventQuery(t *testing.T) {
644647
})
645648
}
646649
}
650+
651+
// randScriptKey makes a random script key with a tweak.
652+
func randScriptKey(t *testing.T) asset.ScriptKey {
653+
scriptKey := asset.RandScriptKey(t)
654+
scriptKey.TweakedScriptKey = &asset.TweakedScriptKey{
655+
RawKey: keychain.KeyDescriptor{
656+
PubKey: asset.RandScriptKey(t).PubKey,
657+
},
658+
}
659+
660+
return scriptKey
661+
}
662+
663+
// insertScriptKeyWithNull is a helper function that inserts a script key with a
664+
// a NULL value for declared known. We use this so we can insert a NULL vs an
665+
// actual value. It is identical to the InsertScriptKey.
666+
func insertScriptKeyWithNull(ctx context.Context, key asset.ScriptKey,
667+
) func(AddrBook) error {
668+
669+
return func(q AddrBook) error {
670+
internalKeyID, err := insertInternalKey(
671+
ctx, q, key.RawKey,
672+
)
673+
if err != nil {
674+
return fmt.Errorf("error inserting internal key: %w",
675+
err)
676+
}
677+
678+
_, err = q.UpsertScriptKey(ctx, NewScriptKey{
679+
InternalKeyID: internalKeyID,
680+
TweakedScriptKey: key.PubKey.SerializeCompressed(),
681+
Tweak: key.Tweak,
682+
DeclaredKnown: sql.NullBool{
683+
Valid: false,
684+
},
685+
})
686+
return err
687+
}
688+
}
689+
690+
func assertKeyKnowledge(t *testing.T, ctx context.Context,
691+
addrBook *TapAddressBook, scriptKey asset.ScriptKey, known bool) {
692+
693+
dbScriptKey, err := addrBook.FetchScriptKey(ctx, scriptKey.PubKey)
694+
require.NoError(t, err)
695+
require.Equal(t, known, dbScriptKey.DeclaredKnown)
696+
}
697+
698+
// TestScriptKeyKnownUpsert tests that we can insert a script key, then insert
699+
// it again declared as known.
700+
func TestScriptKeyKnownUpsert(t *testing.T) {
701+
t.Parallel()
702+
703+
// First, make a new addr book instance we'll use in the test below.
704+
testClock := clock.NewTestClock(time.Now())
705+
addrBook, _ := newAddrBook(t, testClock)
706+
707+
ctx := context.Background()
708+
709+
// In this test, we insert the known field as false, and make sure we
710+
// can flip it back to true.
711+
t.Run("false_to_true", func(t *testing.T) {
712+
known := false
713+
scriptKey := randScriptKey(t)
714+
715+
// We'll insert a random script key into the database. We won't
716+
// declare it as known though.
717+
err := addrBook.InsertScriptKey(ctx, scriptKey, known)
718+
require.NoError(t, err)
719+
720+
// We'll fetch the script key and confirm that it's not known.
721+
assertKeyKnowledge(t, ctx, addrBook, scriptKey, known)
722+
723+
known = true
724+
725+
// We'll now insert it again, but this time declare it as known.
726+
err = addrBook.InsertScriptKey(ctx, scriptKey, known)
727+
require.NoError(t, err)
728+
729+
// We'll fetch the script key and confirm that it's known.
730+
assertKeyKnowledge(t, ctx, addrBook, scriptKey, known)
731+
})
732+
733+
// In this test, we insert a NULL value, and make sure that it can still
734+
// be set to true.
735+
t.Run("null_to_true", func(t *testing.T) {
736+
known := false
737+
scriptKey := randScriptKey(t)
738+
739+
// We'll lift the internal routine of InsertScriptKey so we can
740+
// insert an actual NULL here.
741+
err := addrBook.db.ExecTx(
742+
ctx, &AddrBookTxOptions{},
743+
insertScriptKeyWithNull(ctx, scriptKey),
744+
)
745+
require.NoError(t, err)
746+
747+
// We'll fetch the script key and confirm that it's not known.
748+
assertKeyKnowledge(t, ctx, addrBook, scriptKey, known)
749+
750+
known = true
751+
752+
// We'll now insert it again, but this time declare it as known.
753+
err = addrBook.InsertScriptKey(ctx, scriptKey, known)
754+
require.NoError(t, err)
755+
756+
// We'll fetch the script key and confirm that it's known.
757+
assertKeyKnowledge(t, ctx, addrBook, scriptKey, known)
758+
})
759+
}

tapdb/asset_minting.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,9 @@ func fetchAssetSeedlings(ctx context.Context, q PendingAssetStore,
666666
PubKey: scriptKeyInternalPub,
667667
}
668668
scriptKeyTweak := dbSeedling.ScriptKeyTweak
669-
declaredKnown := dbSeedling.ScriptKeyDeclaredKnown.Valid
669+
declaredKnown := extractBool(
670+
dbSeedling.ScriptKeyDeclaredKnown,
671+
)
670672
seedling.ScriptKey = asset.ScriptKey{
671673
PubKey: tweakedScriptKey,
672674
TweakedScriptKey: &asset.TweakedScriptKey{
@@ -800,7 +802,7 @@ func fetchAssetSprouts(ctx context.Context, q PendingAssetStore,
800802
Family: keychain.KeyFamily(sprout.ScriptKeyFam),
801803
},
802804
}
803-
declaredKnown := sprout.ScriptKeyDeclaredKnown.Valid
805+
declaredKnown := extractBool(sprout.ScriptKeyDeclaredKnown)
804806
scriptKey := asset.ScriptKey{
805807
PubKey: tweakedScriptKey,
806808
TweakedScriptKey: &asset.TweakedScriptKey{

tapdb/assets_common.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ func fetchScriptKey(ctx context.Context, q FetchScriptKeyStore,
459459
Index: uint32(dbKey.KeyIndex),
460460
},
461461
},
462-
DeclaredKnown: dbKey.DeclaredKnown.Valid,
462+
DeclaredKnown: extractBool(dbKey.DeclaredKnown),
463463
}
464464

465465
return scriptKey, nil

tapdb/assets_store.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -727,7 +727,7 @@ func (a *AssetStore) dbAssetsToChainAssets(dbAssets []ConfirmedAsset,
727727
if err != nil {
728728
return nil, err
729729
}
730-
declaredKnown := sprout.ScriptKeyDeclaredKnown.Valid
730+
declaredKnown := extractBool(sprout.ScriptKeyDeclaredKnown)
731731
scriptKey := asset.ScriptKey{
732732
PubKey: scriptKeyPub,
733733
TweakedScriptKey: &asset.TweakedScriptKey{
@@ -2686,7 +2686,7 @@ func fetchAssetTransferOutputs(ctx context.Context, q ActiveAssetsStore,
26862686
err)
26872687
}
26882688

2689-
declaredKnown := dbOut.ScriptKeyDeclaredKnown.Valid
2689+
declaredKnown := extractBool(dbOut.ScriptKeyDeclaredKnown)
26902690
outputAnchor := tapfreighter.Anchor{
26912691
Value: btcutil.Amount(
26922692
dbOut.AnchorValue,
@@ -3035,7 +3035,7 @@ func (a *AssetStore) LogAnchorTxConfirm(ctx context.Context,
30353035
out.OutputType == int16(tappsbt.TypeSplitRoot)
30363036
isBurn := !isNumsKey && len(witnessData) > 0 &&
30373037
asset.IsBurnKey(scriptPubKey, witnessData[0])
3038-
isKnown := out.ScriptKeyDeclaredKnown.Valid
3038+
isKnown := extractBool(out.ScriptKeyDeclaredKnown)
30393039
skipAssetCreation := !isTombstone && !isBurn &&
30403040
!out.ScriptKeyLocal && !isKnown
30413041

tapdb/sqlc/assets.sql.go

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tapdb/sqlc/queries/assets.sql

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -854,7 +854,15 @@ INSERT INTO script_keys (
854854
) ON CONFLICT (tweaked_script_key)
855855
-- As a NOP, we just set the script key to the one that triggered the
856856
-- conflict.
857-
DO UPDATE SET tweaked_script_key = EXCLUDED.tweaked_script_key
857+
DO UPDATE SET
858+
tweaked_script_key = EXCLUDED.tweaked_script_key,
859+
-- If the script key was previously unknown, we'll update to the new
860+
-- value.
861+
declared_known = CASE
862+
WHEN script_keys.declared_known IS NULL OR script_keys.declared_known = FALSE
863+
THEN COALESCE(EXCLUDED.declared_known, script_keys.declared_known)
864+
ELSE script_keys.declared_known
865+
END
858866
RETURNING script_key_id;
859867

860868
-- name: FetchScriptKeyIDByTweakedKey :one

tapdb/sqlutils.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ func extractSqlInt16[T constraints.Integer](num sql.NullInt16) T {
103103
return T(num.Int16)
104104
}
105105

106+
// extractBool turns a NullBool into a boolean. This can be useful when reading
107+
// directly from the database, as this function handles extracting the inner
108+
// value from the "option"-like struct.
109+
func extractBool(b sql.NullBool) bool {
110+
return b.Bool
111+
}
112+
106113
// readOutPoint reads the next sequence of bytes from r as an OutPoint.
107114
//
108115
// NOTE: This function is intended to be used along with the wire.WriteOutPoint

0 commit comments

Comments
 (0)