Skip to content

Commit

Permalink
waddrmgr: extract script hash into identity closure
Browse files Browse the repository at this point in the history
As a preparation for when the identifier of a script isn't the hash of a
script in every case anymore, we extract that part out of the
importScriptAddress method and rename it.
Pay-to-Taproot script addresses use the x-only coordinates of a tweaked
public key as the address identifier.
  • Loading branch information
guggero committed Mar 7, 2022
1 parent 7132615 commit 210a84d
Showing 1 changed file with 38 additions and 26 deletions.
64 changes: 38 additions & 26 deletions waddrmgr/scoped_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,24 @@ func (k KeyScope) String() string {
return fmt.Sprintf("m/%v'/%v'", k.Purpose, k.Coin)
}

// Identity is a closure that returns the identifier of an address.
type Identity func() []byte

// ScriptHashIdentity returns the identity closure for a p2sh script.
func ScriptHashIdentity(script []byte) Identity {
return func() []byte {
return btcutil.Hash160(script)
}
}

// WitnessScriptHashIdentity returns the identity closure for a p2wsh script.
func WitnessScriptHashIdentity(script []byte) Identity {
return func() []byte {
digest := sha256.Sum256(script)
return digest[:]
}
}

// ScopeAddrSchema is the address schema of a particular KeyScope. This will be
// persisted within the database, and will be consulted when deriving any keys
// for a particular scope to know how to encode the public keys as addresses.
Expand Down Expand Up @@ -2065,7 +2083,9 @@ func (s *ScopedKeyManager) toImportedPublicManagedAddress(
func (s *ScopedKeyManager) ImportScript(ns walletdb.ReadWriteBucket,
script []byte, bs *BlockStamp) (ManagedScriptAddress, error) {

return s.importScriptAddress(ns, script, bs, Script, 0, true)
return s.importScriptAddress(
ns, ScriptHashIdentity(script), script, bs, Script, 0, true,
)
}

// ImportWitnessScript imports a user-provided script into the address manager.
Expand All @@ -2085,14 +2105,15 @@ func (s *ScopedKeyManager) ImportWitnessScript(ns walletdb.ReadWriteBucket,
isSecretScript bool) (ManagedScriptAddress, error) {

return s.importScriptAddress(
ns, script, bs, WitnessScript, witnessVersion, isSecretScript,
ns, WitnessScriptHashIdentity(script), script, bs,
WitnessScript, witnessVersion, isSecretScript,
)
}

// importScriptAddress imports a new pay-to-script or pay-to-witness-script
// address.
func (s *ScopedKeyManager) importScriptAddress(ns walletdb.ReadWriteBucket,
script []byte, bs *BlockStamp, addrType AddressType,
identity Identity, script []byte, bs *BlockStamp, addrType AddressType,
witnessVersion byte, isSecretScript bool) (ManagedScriptAddress,
error) {

Expand All @@ -2111,30 +2132,21 @@ func (s *ScopedKeyManager) importScriptAddress(ns walletdb.ReadWriteBucket,
return nil, managerError(ErrWatchingOnly, errWatchingOnly, nil)
}

// Witness script addresses use a SHA256.
var scriptHash []byte
switch addrType {
case WitnessScript:
digest := sha256.Sum256(script)
scriptHash = digest[:]
default:
scriptHash = btcutil.Hash160(script)
}

// Prevent duplicates.
alreadyExists := s.existsAddress(ns, scriptHash)
scriptIdent := identity()
alreadyExists := s.existsAddress(ns, scriptIdent)
if alreadyExists {
str := fmt.Sprintf("address for script hash %x already exists",
scriptHash)
str := fmt.Sprintf("address for script hash/key %x already "+
"exists", scriptIdent)
return nil, managerError(ErrDuplicateAddress, str, nil)
}

// Encrypt the script hash using the crypto public key so it is
// Encrypt the script hash/key using the crypto public key, so it is
// accessible when the address manager is locked or watching-only.
encryptedHash, err := s.rootManager.cryptoKeyPub.Encrypt(scriptHash)
encryptedHash, err := s.rootManager.cryptoKeyPub.Encrypt(scriptIdent)
if err != nil {
str := fmt.Sprintf("failed to encrypt script hash %x",
scriptHash)
str := fmt.Sprintf("failed to encrypt script hash/key %x",
scriptIdent)
return nil, managerError(ErrCrypto, str, err)
}

Expand All @@ -2151,7 +2163,7 @@ func (s *ScopedKeyManager) importScriptAddress(ns walletdb.ReadWriteBucket,
encryptedScript, err := cryptoKey.Encrypt(script)
if err != nil {
str := fmt.Sprintf("failed to encrypt script for %x",
scriptHash)
scriptIdent)
return nil, managerError(ErrCrypto, str, err)
}

Expand All @@ -2169,14 +2181,14 @@ func (s *ScopedKeyManager) importScriptAddress(ns walletdb.ReadWriteBucket,
switch addrType {
case WitnessScript:
err = putWitnessScriptAddress(
ns, &s.scope, scriptHash, ImportedAddrAccount, ssNone,
ns, &s.scope, scriptIdent, ImportedAddrAccount, ssNone,
witnessVersion, isSecretScript, encryptedHash,
encryptedScript,
)

default:
err = putScriptAddress(
ns, &s.scope, scriptHash, ImportedAddrAccount, ssNone,
ns, &s.scope, scriptIdent, ImportedAddrAccount, ssNone,
encryptedHash, encryptedScript,
)
}
Expand Down Expand Up @@ -2210,7 +2222,7 @@ func (s *ScopedKeyManager) importScriptAddress(ns walletdb.ReadWriteBucket,
switch addrType {
case WitnessScript:
witnessAddr, err := newWitnessScriptAddress(
s, ImportedAddrAccount, scriptHash, encryptedScript,
s, ImportedAddrAccount, scriptIdent, encryptedScript,
witnessVersion, isSecretScript,
)
if err != nil {
Expand All @@ -2221,7 +2233,7 @@ func (s *ScopedKeyManager) importScriptAddress(ns walletdb.ReadWriteBucket,

default:
scriptAddr, err := newScriptAddress(
s, ImportedAddrAccount, scriptHash, encryptedScript,
s, ImportedAddrAccount, scriptIdent, encryptedScript,
)
if err != nil {
return nil, err
Expand All @@ -2238,7 +2250,7 @@ func (s *ScopedKeyManager) importScriptAddress(ns walletdb.ReadWriteBucket,

// Add the new managed address to the cache of recent addresses and
// return it.
s.addrs[addrKey(scriptHash)] = managedAddr
s.addrs[addrKey(scriptIdent)] = managedAddr
return managedAddr, nil
}

Expand Down

0 comments on commit 210a84d

Please sign in to comment.