waddrmgr: add adtWitnessScript serialization

This commit is contained in:
Oliver Gugger 2021-12-02 15:10:23 +01:00 committed by Roy Lee
parent 6d7b545acb
commit 9eb48cb6ab

View file

@ -73,6 +73,7 @@ const (
adtChain addressType = 0 adtChain addressType = 0
adtImport addressType = 1 // not iota as they need to be stable for db adtImport addressType = 1 // not iota as they need to be stable for db
adtScript addressType = 2 adtScript addressType = 2
adtWitnessScript addressType = 3
) )
// accountType represents a type of address stored in the database. // accountType represents a type of address stored in the database.
@ -155,6 +156,27 @@ type dbScriptAddressRow struct {
encryptedScript []byte encryptedScript []byte
} }
// dbWitnessScriptAddressRow houses additional information stored about a
// witness script address in the database.
type dbWitnessScriptAddressRow struct {
dbAddressRow
// witnessVersion is the version of the witness script.
witnessVersion byte
// isSecretScript denotes whether the script is considered to be
// "secret" and encrypted with the script encryption key or "public" and
// therefore only encrypted with the public encryption key.
isSecretScript bool
encryptedHash []byte
// encryptedScript is the actual payload of the script address and
// represents the script itself. The encoding of the script is up to the
// actual implementation, it is not parsed or interpreted in any way by
// the database code. So it can be a plain script or a TLV encoded MAST.
encryptedScript []byte
}
// Key names for various database fields. // Key names for various database fields.
var ( var (
// nullVall is null byte used as a flag value in a bucket entry // nullVall is null byte used as a flag value in a bucket entry
@ -1497,6 +1519,74 @@ func serializeScriptAddress(encryptedHash, encryptedScript []byte) []byte {
return rawData return rawData
} }
// deserializeWitnessScriptAddress deserializes the raw data from the passed
// address row as a witness script address.
func deserializeWitnessScriptAddress(
row *dbAddressRow) (*dbWitnessScriptAddressRow, error) {
// The serialized witness script address raw data format is:
// <witness_version><is_secret_script><encscripthashlen>
// <encscripthash><encscriptlen><encscript>
//
// 1 byte witness version + 1 byte boolean + 4 bytes encrypted script
// hash len + encrypted script hash + 4 bytes encrypted script len +
// encrypted script
const minLength = 1 + 1 + 4 + 4
// Given the above, the length of the entry must be at a minimum
// the constant value sizes.
if len(row.rawData) < minLength {
str := "malformed serialized witness script address"
return nil, managerError(ErrDatabase, str, nil)
}
retRow := dbWitnessScriptAddressRow{
dbAddressRow: *row,
witnessVersion: row.rawData[0],
isSecretScript: row.rawData[1] == 1,
}
hashLen := binary.LittleEndian.Uint32(row.rawData[2:6])
retRow.encryptedHash = make([]byte, hashLen)
copy(retRow.encryptedHash, row.rawData[6:6+hashLen])
offset := 6 + hashLen
scriptLen := binary.LittleEndian.Uint32(row.rawData[offset : offset+4])
offset += 4
retRow.encryptedScript = make([]byte, scriptLen)
copy(retRow.encryptedScript, row.rawData[offset:offset+scriptLen])
return &retRow, nil
}
// serializeWitnessScriptAddress returns the serialization of the raw data field
// for a witness script address.
func serializeWitnessScriptAddress(witnessVersion uint8, isSecretScript bool,
encryptedHash, encryptedScript []byte) []byte {
// The serialized witness script address raw data format is:
// <witness_version><is_secret_script><encscripthashlen>
// <encscripthash><encscriptlen><encscript>
//
// 1 byte witness version + 1 byte boolean + 4 bytes encrypted script
// hash len + encrypted script hash + 4 bytes encrypted script len +
// encrypted script
hashLen := uint32(len(encryptedHash))
scriptLen := uint32(len(encryptedScript))
rawData := make([]byte, 10+hashLen+scriptLen)
rawData[0] = witnessVersion
if isSecretScript {
rawData[1] = 1
}
binary.LittleEndian.PutUint32(rawData[2:6], hashLen)
copy(rawData[6:6+hashLen], encryptedHash)
offset := 6 + hashLen
binary.LittleEndian.PutUint32(rawData[offset:offset+4], scriptLen)
offset += 4
copy(rawData[offset:offset+scriptLen], encryptedScript)
return rawData
}
// fetchAddressByHash loads address information for the provided address hash // fetchAddressByHash loads address information for the provided address hash
// from the database. The returned value is one of the address rows for the // from the database. The returned value is one of the address rows for the
// specific address type. The caller should use type assertions to ascertain // specific address type. The caller should use type assertions to ascertain
@ -1530,6 +1620,8 @@ func fetchAddressByHash(ns walletdb.ReadBucket, scope *KeyScope,
return deserializeImportedAddress(row) return deserializeImportedAddress(row)
case adtScript: case adtScript:
return deserializeScriptAddress(row) return deserializeScriptAddress(row)
case adtWitnessScript:
return deserializeWitnessScriptAddress(row)
} }
str := fmt.Sprintf("unsupported address type '%d'", row.addrType) str := fmt.Sprintf("unsupported address type '%d'", row.addrType)
@ -1747,6 +1839,30 @@ func putScriptAddress(ns walletdb.ReadWriteBucket, scope *KeyScope,
return nil return nil
} }
// putWitnessScriptAddress stores the provided witness script address
// information to the database.
func putWitnessScriptAddress(ns walletdb.ReadWriteBucket, scope *KeyScope,
addressID []byte, account uint32, status syncStatus,
witnessVersion uint8, isSecretScript bool, encryptedHash,
encryptedScript []byte) error {
rawData := serializeWitnessScriptAddress(
witnessVersion, isSecretScript, encryptedHash, encryptedScript,
)
addrRow := dbAddressRow{
addrType: adtWitnessScript,
account: account,
addTime: uint64(time.Now().Unix()),
syncStatus: status,
rawData: rawData,
}
if err := putAddress(ns, scope, addressID, &addrRow); err != nil {
return err
}
return nil
}
// existsAddress returns whether or not the address id exists in the database. // existsAddress returns whether or not the address id exists in the database.
func existsAddress(ns walletdb.ReadBucket, scope *KeyScope, addressID []byte) bool { func existsAddress(ns walletdb.ReadBucket, scope *KeyScope, addressID []byte) bool {
scopedBucket, err := fetchReadScopeBucket(ns, scope) scopedBucket, err := fetchReadScopeBucket(ns, scope)
@ -2000,6 +2116,30 @@ func deletePrivateKeys(ns walletdb.ReadWriteBucket) error {
str := "failed to delete imported script" str := "failed to delete imported script"
return managerError(ErrDatabase, str, err) return managerError(ErrDatabase, str, err)
} }
case adtWitnessScript:
srow, err := deserializeWitnessScriptAddress(row)
if err != nil {
return err
}
// If the script is considered to be public, we
// don't need to do anything.
if !srow.isSecretScript {
return nil
}
// Re-serialize the script address without the
// script and store it.
row.rawData = serializeWitnessScriptAddress(
srow.witnessVersion, srow.isSecretScript,
srow.encryptedHash, nil,
)
err = bucket.Put(k, serializeAddressRow(row))
if err != nil {
str := "failed to delete imported script"
return managerError(ErrDatabase, str, err)
}
} }
return nil return nil