Skip to content

Commit 8fb78e2

Browse files
authored
Merge pull request #1198 from lightninglabs/script-key-migrations
[tapdb]: add script key type enum, run Golang based post migration checks
2 parents 3c01a80 + 7b38305 commit 8fb78e2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+4603
-3578
lines changed

.github/workflows/main.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ env:
2121

2222
GO_VERSION: '1.23.6'
2323

24-
LITD_ITEST_BRANCH: 'master'
24+
LITD_ITEST_BRANCH: 'script-key-migrations'
2525

2626
jobs:
2727
#######################

address/book.go

+11-10
Original file line numberDiff line numberDiff line change
@@ -134,13 +134,9 @@ type Storage interface {
134134

135135
// InsertScriptKey inserts an address related script key into the
136136
// database, so it can be recognized as belonging to the wallet when a
137-
// transfer comes in later on. The script key can be declared as known
138-
// if it contains an internal key that isn't derived by the backing
139-
// wallet (e.g. NUMS key) but it should still be recognized as a key
140-
// being relevant for the local wallet (e.g. show assets received on
141-
// this key in the asset list and balances).
137+
// transfer comes in later on.
142138
InsertScriptKey(ctx context.Context, scriptKey asset.ScriptKey,
143-
declareAsKnown bool) error
139+
keyType asset.ScriptKeyType) error
144140
}
145141

146142
// KeyRing is used to create script and internal keys for Taproot Asset
@@ -401,7 +397,12 @@ func (b *Book) NewAddressWithKeys(ctx context.Context, addrVersion Version,
401397
if err != nil {
402398
return nil, fmt.Errorf("unable to insert internal key: %w", err)
403399
}
404-
err = b.cfg.Store.InsertScriptKey(ctx, scriptKey, true)
400+
401+
// We might not know the type of script key, if it was given to us
402+
// through an RPC call. So we make a guess here.
403+
keyType := scriptKey.DetermineType()
404+
405+
err = b.cfg.Store.InsertScriptKey(ctx, scriptKey, keyType)
405406
if err != nil {
406407
return nil, fmt.Errorf("unable to insert script key: %w", err)
407408
}
@@ -438,9 +439,9 @@ func (b *Book) IsLocalKey(ctx context.Context,
438439

439440
// InsertScriptKey inserts an address related script key into the database.
440441
func (b *Book) InsertScriptKey(ctx context.Context, scriptKey asset.ScriptKey,
441-
declareAsKnown bool) error {
442+
keyType asset.ScriptKeyType) error {
442443

443-
return b.cfg.Store.InsertScriptKey(ctx, scriptKey, declareAsKnown)
444+
return b.cfg.Store.InsertScriptKey(ctx, scriptKey, keyType)
444445
}
445446

446447
// NextInternalKey derives then inserts an internal key into the database to
@@ -475,7 +476,7 @@ func (b *Book) NextScriptKey(ctx context.Context,
475476
}
476477

477478
scriptKey := asset.NewScriptKeyBip86(keyDesc)
478-
err = b.cfg.Store.InsertScriptKey(ctx, scriptKey, true)
479+
err = b.cfg.Store.InsertScriptKey(ctx, scriptKey, asset.ScriptKeyBip86)
479480
if err != nil {
480481
return asset.ScriptKey{}, err
481482
}

asset/asset.go

+94-20
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,50 @@ const (
108108
EncodeSegwit
109109
)
110110

111+
// ScriptKeyType denotes the type of script key used for an asset. This type is
112+
// serialized to the database, so we don't use iota for the values to ensure
113+
// they don't change by accident.
114+
type ScriptKeyType uint8
115+
116+
const (
117+
// ScriptKeyUnknown is the default script key type used for assets that
118+
// we don't know the type of. This should only be stored for assets
119+
// where we don't know the internal key of the script key (e.g. for
120+
// imported proofs).
121+
ScriptKeyUnknown ScriptKeyType = 0
122+
123+
// ScriptKeyBip86 is the script key type used for assets that use the
124+
// BIP86 style tweak (e.g. an empty tweak).
125+
ScriptKeyBip86 ScriptKeyType = 1
126+
127+
// ScriptKeyScriptPathExternal is the script key type used for assets
128+
// that use a script path that is defined by an external application.
129+
// Keys with script paths are normally not shown in asset balances and
130+
// by default aren't used for coin selection unless specifically
131+
// requested.
132+
ScriptKeyScriptPathExternal ScriptKeyType = 2
133+
134+
// ScriptKeyBurn is the script key type used for assets that are burned
135+
// and not spendable.
136+
ScriptKeyBurn ScriptKeyType = 3
137+
138+
// ScriptKeyTombstone is the script key type used for assets that are
139+
// not spendable and have been marked as tombstones. This is only the
140+
// case for zero-value assets that result from a non-interactive (TAP
141+
// address) send where no change was left over. The script key used for
142+
// this is a NUMS key that is not spendable.
143+
ScriptKeyTombstone ScriptKeyType = 4
144+
145+
// ScriptKeyScriptPathChannel is the script key type used for assets
146+
// that use a script path that is somehow related to Taproot Asset
147+
// Channels. That means the script key is either a funding key
148+
// (OP_TRUE), a commitment output key (to_local, to_remote, htlc), or a
149+
// HTLC second-level transaction output key.
150+
// Keys related to channels are not shown in asset balances (unless
151+
// specifically requested) and are _never_ used for coin selection.
152+
ScriptKeyScriptPathChannel ScriptKeyType = 5
153+
)
154+
111155
var (
112156
// ZeroPrevID is the blank prev ID used for genesis assets and also
113157
// asset split leaves.
@@ -1005,14 +1049,8 @@ type TweakedScriptKey struct {
10051049
// public key. If this is nil, then a BIP-0086 tweak is assumed.
10061050
Tweak []byte
10071051

1008-
// DeclaredKnown indicates that this script key has been explicitly
1009-
// declared as being important to the local wallet, even if it might not
1010-
// be fully known to the local wallet. This could perhaps also be named
1011-
// "imported", though that might imply that the corresponding private
1012-
// key was also somehow imported and available. The only relevance this
1013-
// flag has is that assets with a declared key are shown in the asset
1014-
// list/balance.
1015-
DeclaredKnown bool
1052+
// Type is the type of script key that is being used.
1053+
Type ScriptKeyType
10161054
}
10171055

10181056
// IsEqual returns true is this tweaked script key is exactly equivalent to the
@@ -1083,12 +1121,9 @@ func (s *ScriptKey) IsEqual(otherScriptKey *ScriptKey) bool {
10831121
// the local wallet or was explicitly declared to be known by using the
10841122
// DeclareScriptKey RPC. Knowing the key conceptually means the key belongs to
10851123
// the local wallet or is at least known by a software that operates on the
1086-
// local wallet. The DeclaredAsKnown flag is never serialized in proofs, so this
1087-
// is never explicitly set for keys foreign to the local wallet. Therefore, if
1088-
// this method returns true for a script key, it means the asset with the script
1089-
// key will be shown in the wallet balance.
1124+
// local wallet.
10901125
func (s *ScriptKey) DeclaredAsKnown() bool {
1091-
return s.TweakedScriptKey != nil && s.TweakedScriptKey.DeclaredKnown
1126+
return s.TweakedScriptKey != nil && s.Type != ScriptKeyUnknown
10921127
}
10931128

10941129
// HasScriptPath returns true if we know the internals of the script key and
@@ -1098,15 +1133,55 @@ func (s *ScriptKey) HasScriptPath() bool {
10981133
return s.TweakedScriptKey != nil && len(s.TweakedScriptKey.Tweak) > 0
10991134
}
11001135

1136+
// DetermineType attempts to determine the type of the script key based on the
1137+
// information available. This method will only return ScriptKeyUnknown if the
1138+
// following condition is met:
1139+
// - The script key doesn't have a script path, but the final Taproot output
1140+
// key doesn't match a BIP-0086 key derived from the internal key. This will
1141+
// be the case for "foreign" script keys we import from proofs, where we set
1142+
// the internal key to the same key as the tweaked script key (because we
1143+
// don't know the internal key, as it's not part of the proof encoding).
1144+
func (s *ScriptKey) DetermineType() ScriptKeyType {
1145+
// If we have an explicit script key type set, we can return that.
1146+
if s.TweakedScriptKey != nil &&
1147+
s.TweakedScriptKey.Type != ScriptKeyUnknown {
1148+
1149+
return s.TweakedScriptKey.Type
1150+
}
1151+
1152+
// If there is a known tweak, then we know that this is a script path
1153+
// key. We never return the channel type, since those keys should always
1154+
// be declared properly, and we never should need to guess their type.
1155+
if s.HasScriptPath() {
1156+
return ScriptKeyScriptPathExternal
1157+
}
1158+
1159+
// Is it the known NUMS key? Then this is a tombstone output.
1160+
if s.PubKey != nil && s.PubKey.IsEqual(NUMSPubKey) {
1161+
return ScriptKeyTombstone
1162+
}
1163+
1164+
// Do we know the internal key? Then we can check whether it is a
1165+
// BIP-0086 key.
1166+
if s.PubKey != nil && s.TweakedScriptKey != nil &&
1167+
s.TweakedScriptKey.RawKey.PubKey != nil {
1168+
1169+
bip86 := NewScriptKeyBip86(s.TweakedScriptKey.RawKey)
1170+
if bip86.PubKey.IsEqual(s.PubKey) {
1171+
return ScriptKeyBip86
1172+
}
1173+
}
1174+
1175+
return ScriptKeyUnknown
1176+
}
1177+
11011178
// NewScriptKey constructs a ScriptKey with only the publicly available
11021179
// information. This resulting key may or may not have a tweak applied to it.
11031180
func NewScriptKey(key *btcec.PublicKey) ScriptKey {
11041181
// Since we'll never query lnd for a tweaked key, it doesn't matter if
11051182
// we lose the parity information here. And this will only ever be
11061183
// serialized on chain in a 32-bit representation as well.
1107-
key, _ = schnorr.ParsePubKey(
1108-
schnorr.SerializePubKey(key),
1109-
)
1184+
key, _ = schnorr.ParsePubKey(schnorr.SerializePubKey(key))
11101185
return ScriptKey{
11111186
PubKey: key,
11121187
}
@@ -1118,9 +1193,7 @@ func NewScriptKey(key *btcec.PublicKey) ScriptKey {
11181193
func NewScriptKeyBip86(rawKey keychain.KeyDescriptor) ScriptKey {
11191194
// Tweak the script key BIP-0086 style (such that we only commit to the
11201195
// internal key when signing).
1121-
tweakedPubKey := txscript.ComputeTaprootKeyNoScript(
1122-
rawKey.PubKey,
1123-
)
1196+
tweakedPubKey := txscript.ComputeTaprootKeyNoScript(rawKey.PubKey)
11241197

11251198
// Since we'll never query lnd for a tweaked key, it doesn't matter if
11261199
// we lose the parity information here. And this will only ever be
@@ -1133,6 +1206,7 @@ func NewScriptKeyBip86(rawKey keychain.KeyDescriptor) ScriptKey {
11331206
PubKey: tweakedPubKey,
11341207
TweakedScriptKey: &TweakedScriptKey{
11351208
RawKey: rawKey,
1209+
Type: ScriptKeyBip86,
11361210
},
11371211
}
11381212
}
@@ -1526,7 +1600,7 @@ func (a *Asset) Copy() *Asset {
15261600

15271601
if a.ScriptKey.TweakedScriptKey != nil {
15281602
assetCopy.ScriptKey.TweakedScriptKey = &TweakedScriptKey{
1529-
DeclaredKnown: a.ScriptKey.DeclaredKnown,
1603+
Type: a.ScriptKey.Type,
15301604
}
15311605
assetCopy.ScriptKey.RawKey = a.ScriptKey.RawKey
15321606

asset/generators.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,11 @@ var (
7171
})
7272
TweakedScriptKeyGen = rapid.Custom(func(t *rapid.T) TweakedScriptKey {
7373
return TweakedScriptKey{
74-
RawKey: KeyDescGen.Draw(t, "raw_key"),
75-
Tweak: HashBytesGen.Draw(t, "tweak"),
76-
DeclaredKnown: rapid.Bool().Draw(t, "declared_known"),
74+
RawKey: KeyDescGen.Draw(t, "raw_key"),
75+
Tweak: HashBytesGen.Draw(t, "tweak"),
76+
Type: ScriptKeyType(
77+
rapid.Int16().Draw(t, "script_key_type"),
78+
),
7779
}
7880
})
7981
ScriptKeyGen = rapid.Custom(func(t *rapid.T) ScriptKey {

0 commit comments

Comments
 (0)