diff --git a/lbrycrd/address.go b/lbrycrd/address.go new file mode 100644 index 0000000..90f6197 --- /dev/null +++ b/lbrycrd/address.go @@ -0,0 +1,58 @@ +package lbrycrd + +import ( + "encoding/hex" + + "github.com/lbryio/lbry.go/errors" + + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcutil" + "github.com/btcsuite/btcutil/base58" + "golang.org/x/crypto/ripemd160" +) + +// DecodeAddress decodes the string encoding of an address and returns +// the Address if addr is a valid encoding for a known address type. +// +// The bitcoin network the address is associated with is extracted if possible. +// When the address does not encode the network, such as in the case of a raw +// public key, the address will be associated with the passed defaultNet. +func DecodeAddress(addr string, defaultNet *chaincfg.Params) (btcutil.Address, error) { + // Serialized public keys are either 65 bytes (130 hex chars) if + // uncompressed/hybrid or 33 bytes (66 hex chars) if compressed. + if len(addr) == 130 || len(addr) == 66 { + serializedPubKey, err := hex.DecodeString(addr) + if err != nil { + return nil, err + } + return btcutil.NewAddressPubKey(serializedPubKey, defaultNet) + } + + // Switch on decoded length to determine the type. + decoded, netID, err := base58.CheckDecode(addr) + if err != nil { + if err == base58.ErrChecksum { + return nil, btcutil.ErrChecksumMismatch + } + return nil, errors.Err("decoded address is of unknown format") + } + + switch len(decoded) { + case ripemd160.Size: // P2PKH or P2SH + isP2PKH := chaincfg.IsPubKeyHashAddrID(netID) + isP2SH := chaincfg.IsScriptHashAddrID(netID) + switch hash160 := decoded; { + case isP2PKH && isP2SH: + return nil, btcutil.ErrAddressCollision + case isP2PKH: + return btcutil.NewAddressPubKeyHash(hash160, &chaincfg.Params{PubKeyHashAddrID: netID}) + case isP2SH: + return btcutil.NewAddressScriptHashFromHash(hash160, &chaincfg.Params{ScriptHashAddrID: netID}) + default: + return nil, btcutil.ErrUnknownAddressType + } + + default: + return nil, errors.Err("decoded address is of unknown size") + } +} diff --git a/lbrycrd/client.go b/lbrycrd/client.go index e55ba22..e66d135 100644 --- a/lbrycrd/client.go +++ b/lbrycrd/client.go @@ -16,11 +16,20 @@ import ( const DefaultPort = 9245 +var GenesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy. + 0x9c, 0x89, 0x28, 0x3b, 0xa0, 0xf3, 0x22, 0x7f, + 0x6c, 0x03, 0xb7, 0x02, 0x16, 0xb9, 0xf6, 0x65, + 0xf0, 0x11, 0x8d, 0x5e, 0x0f, 0xa7, 0x29, 0xce, + 0xdf, 0x4f, 0xb3, 0x4d, 0x6a, 0x34, 0xf4, 0x63, +}) + // MainNetParams define the lbrycrd network. See https://github.com/lbryio/lbrycrd/blob/master/src/chainparams.cpp var MainNetParams = chaincfg.Params{ PubKeyHashAddrID: 0x55, ScriptHashAddrID: 0x7a, PrivateKeyID: 0x1c, + Bech32HRPSegwit: "not-used", // we don't have this (yet) + GenesisHash: &GenesisHash, } func init() { @@ -85,7 +94,7 @@ var errInsufficientFunds = errors.Base("insufficient funds") // SimpleSend is a convenience function to send credits to an address (0 min confirmations) func (c *Client) SimpleSend(toAddress string, amount float64) (*chainhash.Hash, error) { - decodedAddress, err := btcutil.DecodeAddress(toAddress, &MainNetParams) + decodedAddress, err := DecodeAddress(toAddress, &MainNetParams) if err != nil { return nil, errors.Err(err) } @@ -106,7 +115,7 @@ func (c *Client) SimpleSend(toAddress string, amount float64) (*chainhash.Hash, } //func (c *Client) SendWithSplit(toAddress string, amount float64, numUTXOs int) (*chainhash.Hash, error) { -// decodedAddress, err := btcutil.DecodeAddress(toAddress, &MainNetParams) +// decodedAddress, err := DecodeAddress(toAddress, &MainNetParams) // if err != nil { // return nil, errors.Wrap(err, 0) // }