Ori Newman 4658f9d05c
Implement BIP 39 and HD wallet features (#1705)
* Naive bip39 with address reuse

* Avoid address reuse in libkaspawallet

* Add wallet daemon

* Use daemon everywhere

* Add forceOverride

* Make CreateUnsignedTransaction endpoint receive amount in sompis

* Collect close UTXOs

* Filter out non-spendable UTXOs from selectUTXOs

* Use different paths for multisig and non multisig

* Fix tests to use non zero path

* Fix multisig cosigner index detection

* Add comments

* Fix dump_unencrypted_data.go according to bip39 and bip32

* Fix wrong derivation path for multisig on wallet creation

* Remove IsSynced endpoint and add validation if wallet is synced for the relevant endpoints

* Rename server address to daemon address

* Fix capacity for extendedPublicKeys

* Use ReadBytes instead of ReadLine

* Add validation when importing

* Increment before using index value, and use it as is

* Save keys file exactly where needed

* Use %+v printErrorAndExit

* Remove redundant consts

* Rnemae collectCloseUTXOs and collectFarUTXOs

* Move typedefs around

* Add comment to addressesToQuery

* Update collectUTXOsFromRecentAddresses comment about locks

* Split collectUTXOs to small functions

* Add sanity check

* Add addEntryToUTXOSet function

* Change validateIsSynced to isSynced

* Simplify createKeyPairsFromFunction logic

* Rename .Sync() to .Save()

* Fix typo

* Create bip39BitSize const

* Add consts to purposes

* Add multisig check for 'send'

* Rename updatedPSTxBytes to partiallySignedTransaction

* Change collectUTXOsFromFarAddresses's comment

* Use setters for last used indexes

* Don't use the pstx acronym

* Fix SetPath

* Remove spaces when reading lines

* Fix walletserver to daemonaddress

* Fix isUTXOSpendable to use DAA score

Co-authored-by: Svarog <feanorr@gmail.com>
2021-05-19 10:03:23 +03:00

149 lines
4.6 KiB
Go

package bip32
import (
"encoding/binary"
"github.com/kaspanet/go-secp256k1"
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet/bip32/base58"
"github.com/pkg/errors"
)
const (
versionSerializationLen = 4
depthSerializationLen = 1
fingerprintSerializationLen = 4
childNumberSerializationLen = 4
chainCodeSerializationLen = 32
keySerializationLen = 33
checkSumLen = 4
)
const extendedKeySerializationLen = versionSerializationLen +
depthSerializationLen +
fingerprintSerializationLen +
childNumberSerializationLen +
chainCodeSerializationLen +
keySerializationLen +
checkSumLen
// DeserializeExtendedKey deserialized the given base58 string and returns an extended key
func DeserializeExtendedKey(extKeyString string) (*ExtendedKey, error) {
serializedBytes := base58.Decode(extKeyString)
return deserializeExtendedPrivateKey(serializedBytes)
}
func deserializeExtendedPrivateKey(serialized []byte) (*ExtendedKey, error) {
if len(serialized) != extendedKeySerializationLen {
return nil, errors.Errorf("key length must be %d bytes but got %d", extendedKeySerializationLen, len(serialized))
}
err := validateChecksum(serialized)
if err != nil {
return nil, err
}
extKey := &ExtendedKey{}
copy(extKey.Version[:], serialized[:versionSerializationLen])
extKey.Depth = serialized[versionSerializationLen]
copy(extKey.ParentFingerprint[:], serialized[versionSerializationLen+depthSerializationLen:])
extKey.ChildNumber = binary.BigEndian.Uint32(
serialized[versionSerializationLen+depthSerializationLen+fingerprintSerializationLen:],
)
copy(
extKey.ChainCode[:],
serialized[versionSerializationLen+depthSerializationLen+fingerprintSerializationLen+childNumberSerializationLen:],
)
isPrivate := isPrivateVersion(extKey.Version)
if isPrivate {
privateKeyPadding := serialized[versionSerializationLen+
depthSerializationLen+
fingerprintSerializationLen+
childNumberSerializationLen+
chainCodeSerializationLen]
if privateKeyPadding != 0 {
return nil, errors.Errorf("expected 0 padding for private key but got %d", privateKeyPadding)
}
extKey.privateKey, err = secp256k1.DeserializeECDSAPrivateKeyFromSlice(serialized[versionSerializationLen+
depthSerializationLen+
fingerprintSerializationLen+
childNumberSerializationLen+
chainCodeSerializationLen+1 : versionSerializationLen+
depthSerializationLen+
fingerprintSerializationLen+
childNumberSerializationLen+
chainCodeSerializationLen+
keySerializationLen])
if err != nil {
return nil, err
}
} else {
extKey.publicKey, err = secp256k1.DeserializeECDSAPubKey(serialized[versionSerializationLen+
depthSerializationLen+
fingerprintSerializationLen+
childNumberSerializationLen+
chainCodeSerializationLen : versionSerializationLen+
depthSerializationLen+
fingerprintSerializationLen+
childNumberSerializationLen+
chainCodeSerializationLen+
keySerializationLen])
if err != nil {
return nil, err
}
}
return extKey, nil
}
func (extKey *ExtendedKey) serialize() ([]byte, error) {
var serialized [extendedKeySerializationLen]byte
copy(serialized[:], extKey.Version[:])
serialized[versionSerializationLen] = extKey.Depth
copy(serialized[versionSerializationLen+depthSerializationLen:], extKey.ParentFingerprint[:])
binary.BigEndian.PutUint32(
serialized[versionSerializationLen+depthSerializationLen+fingerprintSerializationLen:],
extKey.ChildNumber,
)
copy(
serialized[versionSerializationLen+depthSerializationLen+fingerprintSerializationLen+childNumberSerializationLen:],
extKey.ChainCode[:],
)
if extKey.IsPrivate() {
serialized[versionSerializationLen+depthSerializationLen+fingerprintSerializationLen+childNumberSerializationLen+chainCodeSerializationLen] = 0
copy(
serialized[versionSerializationLen+
depthSerializationLen+
fingerprintSerializationLen+
childNumberSerializationLen+
chainCodeSerializationLen+
1:],
extKey.privateKey.Serialize()[:],
)
} else {
publicKey, err := extKey.PublicKey()
if err != nil {
return nil, err
}
serializedPublicKey, err := publicKey.Serialize()
if err != nil {
return nil, err
}
copy(
serialized[versionSerializationLen+depthSerializationLen+fingerprintSerializationLen+childNumberSerializationLen+chainCodeSerializationLen:],
serializedPublicKey[:],
)
}
checkSum := doubleSha256(serialized[:len(serialized)-checkSumLen])
copy(
serialized[versionSerializationLen+depthSerializationLen+fingerprintSerializationLen+childNumberSerializationLen+chainCodeSerializationLen+keySerializationLen:],
checkSum,
)
return serialized[:], nil
}