From 5eda8b95af6e863694bab71a3fff18dd6d570bb0 Mon Sep 17 00:00:00 2001 From: "John C. Vernaleo" Date: Thu, 13 Jun 2013 11:12:22 -0400 Subject: [PATCH] Add Base58Encode and Base58Decode functions. ok davec@ --- base58.go | 78 +++++++++++++++++++++++++++++++++++++ base58_test.go | 97 +++++++++++++++++++++++++++++++++++++++++++++++ doc.go | 11 ++++++ test_coverage.txt | 12 +++--- 4 files changed, 193 insertions(+), 5 deletions(-) create mode 100644 base58.go create mode 100644 base58_test.go diff --git a/base58.go b/base58.go new file mode 100644 index 0000000..375e850 --- /dev/null +++ b/base58.go @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2013 Conformal Systems LLC. + */ + +package btcutil + +import ( + "math/big" + "strings" +) + +// Alphabet used by BTC +const alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + +var bigRadix = big.NewInt(58) +var bigZero = big.NewInt(0) + +// Shared function to Base58 decode to a []byte +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 +} + +// Shared function to Base58 encode a []byte +func Base58Encode(b []byte) string { + x := new(big.Int) + x.SetBytes(b) + + answer := make([]byte, 0) + 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) +} diff --git a/base58_test.go b/base58_test.go new file mode 100644 index 0000000..91b0117 --- /dev/null +++ b/base58_test.go @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2013 Conformal Systems LLC. + */ + +package btcutil_test + +import ( + "bytes" + "encoding/hex" + "github.com/conformal/btcutil" + "testing" +) + +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/doc.go b/doc.go index 0902935..59ed952 100644 --- a/doc.go +++ b/doc.go @@ -11,5 +11,16 @@ A Block defines a bitcoin block that provides easier and more efficient manipulation of raw wire protocol blocks. It also memoizes hashes for the block and its transactions on their first access so subsequent accesses don't have to repeat the relatively expensive hashing operations. + +Base58 Usage + +To decode a base58 string: + + rawData := btcutil.Base58Decode(encodedData) + +Similarly, to encode the same data: + + encodedData := btcutil.Base58Encode(rawData) + */ package btcutil diff --git a/test_coverage.txt b/test_coverage.txt index 1a13304..8145603 100644 --- a/test_coverage.txt +++ b/test_coverage.txt @@ -1,16 +1,18 @@ +github.com/conformal/btcutil/base58.go Base58Decode 100.00% (20/20) github.com/conformal/btcutil/block.go Block.TxSha 100.00% (11/11) github.com/conformal/btcutil/block.go Block.TxShas 100.00% (10/10) github.com/conformal/btcutil/block.go NewBlockFromBytes 100.00% (7/7) github.com/conformal/btcutil/block.go Block.Sha 100.00% (5/5) -github.com/conformal/btcutil/block.go OutOfRangeError.Error 100.00% (1/1) -github.com/conformal/btcutil/block.go Block.MsgBlock 100.00% (1/1) -github.com/conformal/btcutil/block.go Block.ProtocolVersion 100.00% (1/1) -github.com/conformal/btcutil/block.go NewBlock 100.00% (1/1) github.com/conformal/btcutil/block.go NewBlockFromBlockAndBytes 100.00% (1/1) +github.com/conformal/btcutil/block.go OutOfRangeError.Error 100.00% (1/1) github.com/conformal/btcutil/block.go Block.Height 100.00% (1/1) github.com/conformal/btcutil/block.go Block.SetHeight 100.00% (1/1) +github.com/conformal/btcutil/block.go NewBlock 100.00% (1/1) +github.com/conformal/btcutil/block.go Block.ProtocolVersion 100.00% (1/1) +github.com/conformal/btcutil/block.go Block.MsgBlock 100.00% (1/1) +github.com/conformal/btcutil/base58.go Base58Encode 93.33% (14/15) github.com/conformal/btcutil/block.go Block.TxLoc 88.89% (8/9) github.com/conformal/btcutil/block.go Block.Bytes 88.89% (8/9) -github.com/conformal/btcutil ------------------------- 96.55% (56/58) +github.com/conformal/btcutil ------------------------- 96.77% (90/93)