// Copyright (c) 2013 Conformal Systems LLC. // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. package btcutil import ( "bytes" "code.google.com/p/go.crypto/ripemd160" "errors" "github.com/conformal/btcwire" ) // ErrUnknownNet describes an error where the Bitcoin network is // not recognized. var ErrUnknownNet = errors.New("unrecognized bitcoin network") // ErrMalformedAddress describes an error where an address is improperly // formatted, either due to an incorrect length of the hashed pubkey or // a non-matching checksum. var ErrMalformedAddress = errors.New("malformed address") // Constants used to specify which network a payment address belongs // to. Mainnet address cannot be used on the Testnet, and vice versa. const ( // MainNetAddr is the address identifier for MainNet MainNetAddr = 0x00 // TestNetAddr is the address identifier for TestNet TestNetAddr = 0x6f ) // EncodeAddress takes a 20-byte raw payment address (hash160 of a pubkey) // and the Bitcoin network to create a human-readable payment address string. func EncodeAddress(addrHash []byte, net btcwire.BitcoinNet) (encoded string, err error) { if len(addrHash) != ripemd160.Size { return "", ErrMalformedAddress } var netID byte switch net { case btcwire.MainNet: netID = MainNetAddr case btcwire.TestNet3: netID = TestNetAddr default: return "", ErrUnknownNet } tosum := append([]byte{netID}, addrHash...) cksum := btcwire.DoubleSha256(tosum) // Address before base58 encoding is 1 byte for netID, 20 bytes for // hash, plus 4 bytes of checksum. a := make([]byte, 25, 25) a[0] = netID copy(a[1:], addrHash) copy(a[21:], cksum[:4]) return Base58Encode(a), nil } // DecodeAddress decodes a human-readable payment address string // returning the 20-byte decoded address, along with the Bitcoin // network for the address. func DecodeAddress(addr string) (addrHash []byte, net btcwire.BitcoinNet, err error) { decoded := Base58Decode(addr) // Length of decoded address must be 20 bytes + 1 byte for a network // identifier byte + 4 bytes of checksum. if len(decoded) != ripemd160.Size+5 { return nil, 0x00, ErrMalformedAddress } switch decoded[0] { case MainNetAddr: net = btcwire.MainNet case TestNetAddr: net = btcwire.TestNet3 default: return nil, 0, ErrUnknownNet } // Checksum is first four bytes of double SHA256 of the network byte // and addrHash. Verify this matches the final 4 bytes of the decoded // address. tosum := decoded[:ripemd160.Size+1] cksum := btcwire.DoubleSha256(tosum)[:4] if !bytes.Equal(cksum, decoded[len(decoded)-4:]) { return nil, net, ErrMalformedAddress } addrHash = make([]byte, ripemd160.Size, ripemd160.Size) copy(addrHash, decoded[1:ripemd160.Size+1]) return addrHash, net, nil }