diff --git a/address.go b/address.go index f3612a4..b658e4a 100644 --- a/address.go +++ b/address.go @@ -5,7 +5,6 @@ package btcutil import ( - "bytes" "encoding/hex" "errors" @@ -13,7 +12,7 @@ import ( "github.com/conformal/btcec" "github.com/conformal/btcnet" - "github.com/conformal/btcwire" + "github.com/conformal/btcutil/base58" ) var ( @@ -43,12 +42,7 @@ var ( func encodeAddress(hash160 []byte, netID byte) string { // Format is 1 byte for a network and address class (i.e. P2PKH vs // P2SH), 20 bytes for a RIPEMD160 hash, and 4 bytes of checksum. - b := make([]byte, 0, 1+ripemd160.Size+4) - b = append(b, netID) - b = append(b, hash160...) - cksum := btcwire.DoubleSha256(b)[:4] - b = append(b, cksum...) - return Base58Encode(b) + return base58.CheckEncode(hash160[:ripemd160.Size], netID) } // Address is an interface type for any type of destination a transaction @@ -99,21 +93,18 @@ func DecodeAddress(addr string, defaultNet *btcnet.Params) (Address, error) { } // Switch on decoded length to determine the type. - decoded := Base58Decode(addr) - switch len(decoded) { - case 1 + ripemd160.Size + 4: // P2PKH or P2SH - // Verify hash checksum. Checksum is calculated as the first - // four bytes of double SHA256 of the network byte and hash. - tosum := decoded[:ripemd160.Size+1] - cksum := btcwire.DoubleSha256(tosum)[:4] - if !bytes.Equal(cksum, decoded[len(decoded)-4:]) { + decoded, netID, err := base58.CheckDecode(addr) + if err != nil { + if err == base58.ErrChecksum { return nil, ErrChecksumMismatch } - - netID := decoded[0] + return nil, errors.New("decoded address is of unknown format") + } + switch len(decoded) { + case ripemd160.Size: // P2PKH or P2SH isP2PKH := btcnet.IsPubKeyHashAddrID(netID) isP2SH := btcnet.IsScriptHashAddrID(netID) - switch hash160 := decoded[1 : ripemd160.Size+1]; { + switch hash160 := decoded; { case isP2PKH && isP2SH: return nil, ErrAddressCollision case isP2PKH: diff --git a/base58.go b/base58.go index a94d38a..ea21eb0 100644 --- a/base58.go +++ b/base58.go @@ -4,75 +4,16 @@ package btcutil -import ( - "math/big" - "strings" -) +import "github.com/conformal/btcutil/base58" -// alphabet is the modified base58 alphabet used by Bitcoin. -const alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" - -var bigRadix = big.NewInt(58) -var bigZero = big.NewInt(0) - -// Base58Decode decodes a modified base58 string to a byte slice. +// Base58Decode wraps the new base58 package for backwards compatibility. +// TODO: Remove as soon as the other repos have been updated to use the new package. func Base58Decode(b string) []byte { - answer := big.NewInt(0) - j := big.NewInt(1) - - for i := len(b) - 1; i >= 0; i-- { - tmp := strings.IndexAny(alphabet, string(b[i])) - if tmp == -1 { - return []byte("") - } - idx := big.NewInt(int64(tmp)) - tmp1 := big.NewInt(0) - tmp1.Mul(j, idx) - - answer.Add(answer, tmp1) - j.Mul(j, bigRadix) - } - - tmpval := answer.Bytes() - - var numZeros int - for numZeros = 0; numZeros < len(b); numZeros++ { - if b[numZeros] != alphabet[0] { - break - } - } - flen := numZeros + len(tmpval) - val := make([]byte, flen, flen) - copy(val[numZeros:], tmpval) - - return val + return base58.Decode(b) } -// Base58Encode encodes a byte slice to a modified base58 string. +// Base58Encode wraps the new base58 package for backwards compatibility. +// TODO: Remove as soon as the other repos have been updated to use the new package. func Base58Encode(b []byte) string { - x := new(big.Int) - x.SetBytes(b) - - answer := make([]byte, 0, len(b)*136/100) - for x.Cmp(bigZero) > 0 { - mod := new(big.Int) - x.DivMod(x, bigRadix, mod) - answer = append(answer, alphabet[mod.Int64()]) - } - - // leading zero bytes - for _, i := range b { - if i != 0 { - break - } - answer = append(answer, alphabet[0]) - } - - // reverse - alen := len(answer) - for i := 0; i < alen/2; i++ { - answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i] - } - - return string(answer) + return base58.Encode(b) } diff --git a/base58_test.go b/base58_test.go deleted file mode 100644 index e80791a..0000000 --- a/base58_test.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2013-2014 Conformal Systems LLC. -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcutil_test - -import ( - "bytes" - "encoding/hex" - "testing" - - "github.com/conformal/btcutil" -) - -var stringTests = []struct { - in string - out string -}{ - {"", ""}, - {" ", "Z"}, - {"-", "n"}, - {"0", "q"}, - {"1", "r"}, - {"-1", "4SU"}, - {"11", "4k8"}, - {"abc", "ZiCa"}, - {"1234598760", "3mJr7AoUXx2Wqd"}, - {"abcdefghijklmnopqrstuvwxyz", "3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f"}, - {"00000000000000000000000000000000000000000000000000000000000000", "3sN2THZeE9Eh9eYrwkvZqNstbHGvrxSAM7gXUXvyFQP8XvQLUqNCS27icwUeDT7ckHm4FUHM2mTVh1vbLmk7y"}, -} - -var invalidStringTests = []struct { - in string - out string -}{ - {"0", ""}, - {"O", ""}, - {"I", ""}, - {"l", ""}, - {"3mJr0", ""}, - {"O3yxU", ""}, - {"3sNI", ""}, - {"4kl8", ""}, - {"0OIl", ""}, - {"!@#$%^&*()-_=+~`", ""}, -} - -var hexTests = []struct { - in string - out string -}{ - {"61", "2g"}, - {"626262", "a3gV"}, - {"636363", "aPEr"}, - {"73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2"}, - {"00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"}, - {"516b6fcd0f", "ABnLTmg"}, - {"bf4f89001e670274dd", "3SEo3LWLoPntC"}, - {"572e4794", "3EFU7m"}, - {"ecac89cad93923c02321", "EJDM8drfXA6uyA"}, - {"10c8511e", "Rt5zm"}, - {"00000000000000000000", "1111111111"}, -} - -func TestBase58(t *testing.T) { - // Base58Encode tests - for x, test := range stringTests { - tmp := []byte(test.in) - if res := btcutil.Base58Encode(tmp); res != test.out { - t.Errorf("Base58Encode test #%d failed: got: %s want: %s", - x, res, test.out) - continue - } - } - - // Base58Decode tests - for x, test := range hexTests { - b, err := hex.DecodeString(test.in) - if err != nil { - t.Errorf("hex.DecodeString failed failed #%d: got: %s", x, test.in) - continue - } - if res := btcutil.Base58Decode(test.out); bytes.Equal(res, b) != true { - t.Errorf("Base58Decode test #%d failed: got: %q want: %q", - x, res, test.in) - continue - } - } - - // Base58Decode with invalid input - for x, test := range invalidStringTests { - if res := btcutil.Base58Decode(test.in); string(res) != test.out { - t.Errorf("Base58Decode invalidString test #%d failed: got: %q want: %q", - x, res, test.out) - continue - } - } -} diff --git a/hdkeychain/extendedkey.go b/hdkeychain/extendedkey.go index 01d3d49..d9ff8ce 100644 --- a/hdkeychain/extendedkey.go +++ b/hdkeychain/extendedkey.go @@ -21,6 +21,7 @@ import ( "github.com/conformal/btcec" "github.com/conformal/btcnet" "github.com/conformal/btcutil" + "github.com/conformal/btcutil/base58" "github.com/conformal/btcwire" ) @@ -386,7 +387,7 @@ func (k *ExtendedKey) String() string { checkSum := btcwire.DoubleSha256(serializedBytes)[:4] serializedBytes = append(serializedBytes, checkSum...) - return btcutil.Base58Encode(serializedBytes) + return base58.Encode(serializedBytes) } // IsForNet returns whether or not the extended key is associated with the @@ -473,7 +474,7 @@ func NewMaster(seed []byte) (*ExtendedKey, error) { func NewKeyFromString(key string) (*ExtendedKey, error) { // The base58-decoded extended key must consist of a serialized payload // plus an additional 4 bytes for the checksum. - decoded := btcutil.Base58Decode(key) + decoded := base58.Decode(key) if len(decoded) != serializedKeyLen+4 { return nil, ErrInvalidKeyLen } diff --git a/internal_test.go b/internal_test.go index 702f0e8..b6f6903 100644 --- a/internal_test.go +++ b/internal_test.go @@ -15,6 +15,7 @@ import ( "golang.org/x/crypto/ripemd160" "github.com/conformal/btcec" + "github.com/conformal/btcutil/base58" ) // SetBlockBytes sets the internal serialized block byte buffer to the passed @@ -68,6 +69,6 @@ func TstAddressPubKey(serializedPubKey []byte, pubKeyFormat PubKeyFormat, // TstAddressSAddr returns the expected script address bytes for // P2PKH and P2SH bitcoin addresses. func TstAddressSAddr(addr string) []byte { - decoded := Base58Decode(addr) + decoded := base58.Decode(addr) return decoded[1 : 1+ripemd160.Size] } diff --git a/wif.go b/wif.go index c892be5..5fbf131 100644 --- a/wif.go +++ b/wif.go @@ -10,6 +10,7 @@ import ( "github.com/conformal/btcec" "github.com/conformal/btcnet" + "github.com/conformal/btcutil/base58" "github.com/conformal/btcwire" ) @@ -82,7 +83,7 @@ func (w *WIF) IsForNet(net *btcnet.Params) bool { // does not equal the expected value of 0x01. ErrChecksumMismatch is returned // if the expected WIF checksum does not match the calculated checksum. func DecodeWIF(wif string) (*WIF, error) { - decoded := Base58Decode(wif) + decoded := base58.Decode(wif) decodedLen := len(decoded) var compress bool @@ -143,7 +144,7 @@ func (w *WIF) String() string { } cksum := btcwire.DoubleSha256(a)[:4] a = append(a, cksum...) - return Base58Encode(a) + return base58.Encode(a) } // SerializePubKey serializes the associated public key of the imported or