├── README.md ├── address.go ├── address_test.go ├── base58 ├── README.md ├── alphabet.go ├── base58.go ├── base58_test.go ├── base58bench_test.go ├── base58check.go ├── base58check_test.go ├── cov_report.sh ├── doc.go ├── example_test.go └── genalphabet.go ├── bip39 ├── README.md ├── bip39.go ├── bip39_test.go ├── example_test.go ├── wordlists_chinese_simplified.go └── wordlists_english.go ├── btcec ├── README.md ├── bench_test.go ├── btcec.go ├── btcec_test.go ├── ciphering.go ├── ciphering_test.go ├── doc.go ├── example_test.go ├── field.go ├── field_test.go ├── genprecomps.go ├── gensecp256k1.go ├── precompute.go ├── privkey.go ├── privkey_test.go ├── pubkey.go ├── pubkey_test.go ├── secp256k1.go ├── signature.go └── signature_test.go ├── cmd ├── ethutil-btc-vanity-gen │ └── main.go ├── ethutil-cat-keystore │ ├── get_passwd.go │ ├── get_passwd_darwin.go │ ├── get_passwd_linux.go │ ├── get_passwd_windows.go │ └── main.go ├── ethutil-key2address │ └── main.go └── ethutil-vanity-gen │ ├── README.md │ ├── lzsqo.md │ └── main.go ├── ethutil.go ├── go.mod ├── go.sum ├── hello.go ├── hex.go ├── keccak256.go ├── keccak256_test.go ├── key.go ├── key_test.go ├── keystore.go ├── keystore_example_test.go ├── keystore_test.go ├── number.go ├── rlp ├── decode.go ├── decode_tail_test.go ├── decode_test.go ├── doc.go ├── encode.go ├── encode_test.go ├── encoder_example_test.go ├── raw.go ├── raw_test.go └── typecache.go ├── secp256k1.go ├── secp256k1_test.go ├── sig.go ├── sig_test.go ├── testdata ├── v1_test_vector.json └── v3_test_vector.json ├── testing_test.go ├── tx.go ├── tx_test.go └── wei.go /README.md: -------------------------------------------------------------------------------- 1 | - *赞助 BTC: 1Cbd6oGAUUyBi7X7MaR4np4nTmQZXVgkCW* 2 | - *赞助 ETH: 0x623A3C3a72186A6336C79b18Ac1eD36e1c71A8a6* 3 | - *Go语言付费QQ群: 1055927514* 4 | 5 | ---- 6 | 7 | # ♦Ξ♦ 以太坊工具箱(零依赖) ♦Ξ♦ 8 | 9 | https://godoc.org/github.com/chai2010/ethutil 10 | -------------------------------------------------------------------------------- /address.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import ( 6 | "strings" 7 | "unicode" 8 | ) 9 | 10 | // 从私钥生成账户地址 11 | func GenAddressFromPrivateKey(privateKey string) string { 12 | return GenAddressFromPublicKey(GenPublicKey(privateKey)) 13 | } 14 | 15 | // 公钥生成账户地址 16 | // 结尾的20个字节, 对应十六进制的40个字符 17 | // 包含十六进制的 0x 开头 18 | func GenAddressFromPublicKey(publicKey string) string { 19 | // 去掉开头 0x 20 | if s := publicKey; len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') { 21 | publicKey = publicKey[2:] 22 | } 23 | 24 | // 去掉公钥开头的 04 部分 25 | var xy = publicKey[len("04"):] 26 | 27 | // 转换为字节格式, 并计算 Keccak256 哈希 28 | var hash = Keccak256Hash(Hex(xy).MustBytes()) 29 | 30 | // 取十六进制格式的最后40个字节作为地址 31 | return "0x" + hash[len(hash)-40:] 32 | } 33 | 34 | // 生成EIP55校验的地址 35 | // 36 | // EIP55通过十六进制的大小写来保存校验码信息 37 | // 38 | // 参考 https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md 39 | func GenEIP55Address(address string) string { 40 | // 去掉 0x 开头 41 | if len(address) > 2 { 42 | if address[0] == '0' && (address[1] == 'x' || address[1] == 'X') { 43 | address = address[len("0x"):] 44 | } 45 | } 46 | 47 | // 转换为小写十六进制 48 | address = strings.ToLower(address) 49 | 50 | // 计算 Keccak-256 哈希 51 | var h = Keccak256Hash([]byte(address)) 52 | 53 | // EIP-55编码 54 | // 每个字符对应位置的字符小于8, 则保留小写 55 | s1 := []byte(address) 56 | s2 := []byte(h) 57 | for i := 0; i < len(s1); i++ { 58 | if s2[i] < '8' { 59 | s1[i] = s1[i] 60 | } else { 61 | s1[i] = byte(unicode.ToUpper(rune(s1[i]))) 62 | } 63 | } 64 | 65 | // 得到新地址 66 | return "0x" + string(s1) 67 | } 68 | 69 | // 校验EIP55地址 70 | // 71 | // EIP55通过十六进制的大小写来保存校验码信息 72 | // 73 | // 参考 https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md 74 | func (s HexString) IsValidEIP55Address() bool { 75 | var address = s 76 | 77 | // 添加缺少的 0x 前缀 78 | if len(address) > 2 { 79 | if address[0] != '0' || (address[1] != 'x' && address[1] != 'X') { 80 | address = "0x" + address 81 | } 82 | } 83 | 84 | // 重新生成规范的EIP55格式地址 85 | var goden = GenEIP55Address(string(address)) 86 | 87 | // 校验新生成的地址是否相等 88 | return goden == string(address) 89 | } 90 | -------------------------------------------------------------------------------- /address_test.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import "testing" 6 | 7 | import "strings" 8 | 9 | // 测试数据来自《精通以太坊》 10 | // https://github.com/ethereumbook/ethereumbook/blob/develop/04keys-addresses.asciidoc 11 | 12 | func TestGenAddressFromPrivateKey(t *testing.T) { 13 | const privateKey = "f8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315" 14 | const addresses = "0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9" 15 | 16 | var s = GenAddressFromPrivateKey(privateKey) 17 | tAssert(t, s == addresses, s) 18 | } 19 | 20 | func TestGenAddressFromPublicKey(t *testing.T) { 21 | const privateKey = "f8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315" 22 | const addresses = "0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9" 23 | 24 | var s = GenAddressFromPublicKey(GenPublicKey(privateKey)) 25 | tAssert(t, s == addresses, s) 26 | } 27 | 28 | func TestGenEIP55Address(t *testing.T) { 29 | for i, s := range []string{ 30 | // https://github.com/ethereumbook/ethereumbook/blob/develop/04keys-addresses.asciidoc 31 | "0x001d3F1ef827552Ae1114027BD3ECF1f086bA0F9", 32 | 33 | // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md 34 | "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", 35 | "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", 36 | "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", 37 | "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", 38 | } { 39 | var got = GenEIP55Address(strings.ToLower(s)) 40 | tAssert(t, got == s, i, got, s) 41 | } 42 | } 43 | 44 | func TestHexString_IsValidEIP55Address(t *testing.T) { 45 | tAssert(t, Hex("001d3F1ef827552Ae1114027BD3ECF1f086bA0F9").IsValidEIP55Address()) 46 | tAssert(t, Hex("0x001d3F1ef827552Ae1114027BD3ECF1f086bA0F9").IsValidEIP55Address()) 47 | 48 | tAssert(t, !Hex("001d3f1ef827552ae1114027bd3ecf1f086ba0f9").IsValidEIP55Address()) 49 | tAssert(t, !Hex("0x001d3f1ef827552ae1114027bd3ecf1f086ba0f9").IsValidEIP55Address()) 50 | } 51 | -------------------------------------------------------------------------------- /base58/README.md: -------------------------------------------------------------------------------- 1 | base58 2 | ========== 3 | 4 | [![Build Status](http://img.shields.io/travis/btcsuite/btcutil.svg)](https://travis-ci.org/btcsuite/btcutil) 5 | [![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) 6 | [![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcutil/base58) 7 | 8 | Package base58 provides an API for encoding and decoding to and from the 9 | modified base58 encoding. It also provides an API to do Base58Check encoding, 10 | as described [here](https://en.bitcoin.it/wiki/Base58Check_encoding). 11 | 12 | A comprehensive suite of tests is provided to ensure proper functionality. 13 | 14 | ## Installation and Updating 15 | 16 | ```bash 17 | $ go get -u github.com/btcsuite/btcutil/base58 18 | ``` 19 | 20 | ## Examples 21 | 22 | * [Decode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-Decode) 23 | Demonstrates how to decode modified base58 encoded data. 24 | * [Encode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-Encode) 25 | Demonstrates how to encode data using the modified base58 encoding scheme. 26 | * [CheckDecode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-CheckDecode) 27 | Demonstrates how to decode Base58Check encoded data. 28 | * [CheckEncode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-CheckEncode) 29 | Demonstrates how to encode data using the Base58Check encoding scheme. 30 | 31 | ## License 32 | 33 | Package base58 is licensed under the [copyfree](http://copyfree.org) ISC 34 | License. 35 | -------------------------------------------------------------------------------- /base58/alphabet.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | // AUTOGENERATED by genalphabet.go; do not edit. 6 | 7 | package base58 8 | 9 | const ( 10 | // alphabet is the modified base58 alphabet used by Bitcoin. 11 | alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" 12 | 13 | alphabetIdx0 = '1' 14 | ) 15 | 16 | var b58 = [256]byte{ 17 | 255, 255, 255, 255, 255, 255, 255, 255, 18 | 255, 255, 255, 255, 255, 255, 255, 255, 19 | 255, 255, 255, 255, 255, 255, 255, 255, 20 | 255, 255, 255, 255, 255, 255, 255, 255, 21 | 255, 255, 255, 255, 255, 255, 255, 255, 22 | 255, 255, 255, 255, 255, 255, 255, 255, 23 | 255, 0, 1, 2, 3, 4, 5, 6, 24 | 7, 8, 255, 255, 255, 255, 255, 255, 25 | 255, 9, 10, 11, 12, 13, 14, 15, 26 | 16, 255, 17, 18, 19, 20, 21, 255, 27 | 22, 23, 24, 25, 26, 27, 28, 29, 28 | 30, 31, 32, 255, 255, 255, 255, 255, 29 | 255, 33, 34, 35, 36, 37, 38, 39, 30 | 40, 41, 42, 43, 255, 44, 45, 46, 31 | 47, 48, 49, 50, 51, 52, 53, 54, 32 | 55, 56, 57, 255, 255, 255, 255, 255, 33 | 255, 255, 255, 255, 255, 255, 255, 255, 34 | 255, 255, 255, 255, 255, 255, 255, 255, 35 | 255, 255, 255, 255, 255, 255, 255, 255, 36 | 255, 255, 255, 255, 255, 255, 255, 255, 37 | 255, 255, 255, 255, 255, 255, 255, 255, 38 | 255, 255, 255, 255, 255, 255, 255, 255, 39 | 255, 255, 255, 255, 255, 255, 255, 255, 40 | 255, 255, 255, 255, 255, 255, 255, 255, 41 | 255, 255, 255, 255, 255, 255, 255, 255, 42 | 255, 255, 255, 255, 255, 255, 255, 255, 43 | 255, 255, 255, 255, 255, 255, 255, 255, 44 | 255, 255, 255, 255, 255, 255, 255, 255, 45 | 255, 255, 255, 255, 255, 255, 255, 255, 46 | 255, 255, 255, 255, 255, 255, 255, 255, 47 | 255, 255, 255, 255, 255, 255, 255, 255, 48 | 255, 255, 255, 255, 255, 255, 255, 255, 49 | } 50 | -------------------------------------------------------------------------------- /base58/base58.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package base58 6 | 7 | import ( 8 | "math/big" 9 | ) 10 | 11 | //go:generate go run genalphabet.go 12 | 13 | var bigRadix = big.NewInt(58) 14 | var bigZero = big.NewInt(0) 15 | 16 | // Decode decodes a modified base58 string to a byte slice. 17 | func Decode(b string) []byte { 18 | answer := big.NewInt(0) 19 | j := big.NewInt(1) 20 | 21 | scratch := new(big.Int) 22 | for i := len(b) - 1; i >= 0; i-- { 23 | tmp := b58[b[i]] 24 | if tmp == 255 { 25 | return []byte("") 26 | } 27 | scratch.SetInt64(int64(tmp)) 28 | scratch.Mul(j, scratch) 29 | answer.Add(answer, scratch) 30 | j.Mul(j, bigRadix) 31 | } 32 | 33 | tmpval := answer.Bytes() 34 | 35 | var numZeros int 36 | for numZeros = 0; numZeros < len(b); numZeros++ { 37 | if b[numZeros] != alphabetIdx0 { 38 | break 39 | } 40 | } 41 | flen := numZeros + len(tmpval) 42 | val := make([]byte, flen) 43 | copy(val[numZeros:], tmpval) 44 | 45 | return val 46 | } 47 | 48 | // Encode encodes a byte slice to a modified base58 string. 49 | func Encode(b []byte) string { 50 | x := new(big.Int) 51 | x.SetBytes(b) 52 | 53 | answer := make([]byte, 0, len(b)*136/100) 54 | for x.Cmp(bigZero) > 0 { 55 | mod := new(big.Int) 56 | x.DivMod(x, bigRadix, mod) 57 | answer = append(answer, alphabet[mod.Int64()]) 58 | } 59 | 60 | // leading zero bytes 61 | for _, i := range b { 62 | if i != 0 { 63 | break 64 | } 65 | answer = append(answer, alphabetIdx0) 66 | } 67 | 68 | // reverse 69 | alen := len(answer) 70 | for i := 0; i < alen/2; i++ { 71 | answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i] 72 | } 73 | 74 | return string(answer) 75 | } 76 | -------------------------------------------------------------------------------- /base58/base58_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2017 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package base58_test 6 | 7 | import ( 8 | "bytes" 9 | "encoding/hex" 10 | "testing" 11 | 12 | "github.com/chai2010/ethutil/base58" 13 | ) 14 | 15 | var stringTests = []struct { 16 | in string 17 | out string 18 | }{ 19 | {"", ""}, 20 | {" ", "Z"}, 21 | {"-", "n"}, 22 | {"0", "q"}, 23 | {"1", "r"}, 24 | {"-1", "4SU"}, 25 | {"11", "4k8"}, 26 | {"abc", "ZiCa"}, 27 | {"1234598760", "3mJr7AoUXx2Wqd"}, 28 | {"abcdefghijklmnopqrstuvwxyz", "3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f"}, 29 | {"00000000000000000000000000000000000000000000000000000000000000", "3sN2THZeE9Eh9eYrwkvZqNstbHGvrxSAM7gXUXvyFQP8XvQLUqNCS27icwUeDT7ckHm4FUHM2mTVh1vbLmk7y"}, 30 | } 31 | 32 | var invalidStringTests = []struct { 33 | in string 34 | out string 35 | }{ 36 | {"0", ""}, 37 | {"O", ""}, 38 | {"I", ""}, 39 | {"l", ""}, 40 | {"3mJr0", ""}, 41 | {"O3yxU", ""}, 42 | {"3sNI", ""}, 43 | {"4kl8", ""}, 44 | {"0OIl", ""}, 45 | {"!@#$%^&*()-_=+~`", ""}, 46 | } 47 | 48 | var hexTests = []struct { 49 | in string 50 | out string 51 | }{ 52 | {"61", "2g"}, 53 | {"626262", "a3gV"}, 54 | {"636363", "aPEr"}, 55 | {"73696d706c792061206c6f6e6720737472696e67", "2cFupjhnEsSn59qHXstmK2ffpLv2"}, 56 | {"00eb15231dfceb60925886b67d065299925915aeb172c06647", "1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L"}, 57 | {"516b6fcd0f", "ABnLTmg"}, 58 | {"bf4f89001e670274dd", "3SEo3LWLoPntC"}, 59 | {"572e4794", "3EFU7m"}, 60 | {"ecac89cad93923c02321", "EJDM8drfXA6uyA"}, 61 | {"10c8511e", "Rt5zm"}, 62 | {"00000000000000000000", "1111111111"}, 63 | } 64 | 65 | func TestBase58(t *testing.T) { 66 | // Encode tests 67 | for x, test := range stringTests { 68 | tmp := []byte(test.in) 69 | if res := base58.Encode(tmp); res != test.out { 70 | t.Errorf("Encode test #%d failed: got: %s want: %s", 71 | x, res, test.out) 72 | continue 73 | } 74 | } 75 | 76 | // Decode tests 77 | for x, test := range hexTests { 78 | b, err := hex.DecodeString(test.in) 79 | if err != nil { 80 | t.Errorf("hex.DecodeString failed failed #%d: got: %s", x, test.in) 81 | continue 82 | } 83 | if res := base58.Decode(test.out); !bytes.Equal(res, b) { 84 | t.Errorf("Decode test #%d failed: got: %q want: %q", 85 | x, res, test.in) 86 | continue 87 | } 88 | } 89 | 90 | // Decode with invalid input 91 | for x, test := range invalidStringTests { 92 | if res := base58.Decode(test.in); string(res) != test.out { 93 | t.Errorf("Decode invalidString test #%d failed: got: %q want: %q", 94 | x, res, test.out) 95 | continue 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /base58/base58bench_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package base58_test 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | 11 | "github.com/chai2010/ethutil/base58" 12 | ) 13 | 14 | func BenchmarkBase58Encode(b *testing.B) { 15 | b.StopTimer() 16 | data := bytes.Repeat([]byte{0xff}, 5000) 17 | b.SetBytes(int64(len(data))) 18 | b.StartTimer() 19 | 20 | for i := 0; i < b.N; i++ { 21 | base58.Encode(data) 22 | } 23 | } 24 | 25 | func BenchmarkBase58Decode(b *testing.B) { 26 | b.StopTimer() 27 | data := bytes.Repeat([]byte{0xff}, 5000) 28 | encoded := base58.Encode(data) 29 | b.SetBytes(int64(len(encoded))) 30 | b.StartTimer() 31 | 32 | for i := 0; i < b.N; i++ { 33 | base58.Decode(encoded) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /base58/base58check.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package base58 6 | 7 | import ( 8 | "crypto/sha256" 9 | "errors" 10 | ) 11 | 12 | // ErrChecksum indicates that the checksum of a check-encoded string does not verify against 13 | // the checksum. 14 | var ErrChecksum = errors.New("checksum error") 15 | 16 | // ErrInvalidFormat indicates that the check-encoded string has an invalid format. 17 | var ErrInvalidFormat = errors.New("invalid format: version and/or checksum bytes missing") 18 | 19 | // checksum: first four bytes of sha256^2 20 | func checksum(input []byte) (cksum [4]byte) { 21 | h := sha256.Sum256(input) 22 | h2 := sha256.Sum256(h[:]) 23 | copy(cksum[:], h2[:4]) 24 | return 25 | } 26 | 27 | // CheckEncode prepends a version byte and appends a four byte checksum. 28 | func CheckEncode(input []byte, version byte) string { 29 | b := make([]byte, 0, 1+len(input)+4) 30 | b = append(b, version) 31 | b = append(b, input[:]...) 32 | cksum := checksum(b) 33 | b = append(b, cksum[:]...) 34 | return Encode(b) 35 | } 36 | 37 | // CheckDecode decodes a string that was encoded with CheckEncode and verifies the checksum. 38 | func CheckDecode(input string) (result []byte, version byte, err error) { 39 | decoded := Decode(input) 40 | if len(decoded) < 5 { 41 | return nil, 0, ErrInvalidFormat 42 | } 43 | version = decoded[0] 44 | var cksum [4]byte 45 | copy(cksum[:], decoded[len(decoded)-4:]) 46 | if checksum(decoded[:len(decoded)-4]) != cksum { 47 | return nil, 0, ErrChecksum 48 | } 49 | payload := decoded[1 : len(decoded)-4] 50 | result = append(result, payload...) 51 | return 52 | } 53 | -------------------------------------------------------------------------------- /base58/base58check_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package base58_test 6 | 7 | import ( 8 | "testing" 9 | 10 | "github.com/chai2010/ethutil/base58" 11 | ) 12 | 13 | var checkEncodingStringTests = []struct { 14 | version byte 15 | in string 16 | out string 17 | }{ 18 | {20, "", "3MNQE1X"}, 19 | {20, " ", "B2Kr6dBE"}, 20 | {20, "-", "B3jv1Aft"}, 21 | {20, "0", "B482yuaX"}, 22 | {20, "1", "B4CmeGAC"}, 23 | {20, "-1", "mM7eUf6kB"}, 24 | {20, "11", "mP7BMTDVH"}, 25 | {20, "abc", "4QiVtDjUdeq"}, 26 | {20, "1234598760", "ZmNb8uQn5zvnUohNCEPP"}, 27 | {20, "abcdefghijklmnopqrstuvwxyz", "K2RYDcKfupxwXdWhSAxQPCeiULntKm63UXyx5MvEH2"}, 28 | {20, "00000000000000000000000000000000000000000000000000000000000000", "bi1EWXwJay2udZVxLJozuTb8Meg4W9c6xnmJaRDjg6pri5MBAxb9XwrpQXbtnqEoRV5U2pixnFfwyXC8tRAVC8XxnjK"}, 29 | } 30 | 31 | func TestBase58Check(t *testing.T) { 32 | for x, test := range checkEncodingStringTests { 33 | // test encoding 34 | if res := base58.CheckEncode([]byte(test.in), test.version); res != test.out { 35 | t.Errorf("CheckEncode test #%d failed: got %s, want: %s", x, res, test.out) 36 | } 37 | 38 | // test decoding 39 | res, version, err := base58.CheckDecode(test.out) 40 | if err != nil { 41 | t.Errorf("CheckDecode test #%d failed with err: %v", x, err) 42 | } else if version != test.version { 43 | t.Errorf("CheckDecode test #%d failed: got version: %d want: %d", x, version, test.version) 44 | } else if string(res) != test.in { 45 | t.Errorf("CheckDecode test #%d failed: got: %s want: %s", x, res, test.in) 46 | } 47 | } 48 | 49 | // test the two decoding failure cases 50 | // case 1: checksum error 51 | _, _, err := base58.CheckDecode("3MNQE1Y") 52 | if err != base58.ErrChecksum { 53 | t.Error("Checkdecode test failed, expected ErrChecksum") 54 | } 55 | // case 2: invalid formats (string lengths below 5 mean the version byte and/or the checksum 56 | // bytes are missing). 57 | testString := "" 58 | for len := 0; len < 4; len++ { 59 | // make a string of length `len` 60 | _, _, err = base58.CheckDecode(testString) 61 | if err != base58.ErrInvalidFormat { 62 | t.Error("Checkdecode test failed, expected ErrInvalidFormat") 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /base58/cov_report.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script uses gocov to generate a test coverage report. 4 | # The gocov tool my be obtained with the following command: 5 | # go get github.com/axw/gocov/gocov 6 | # 7 | # It will be installed to $GOPATH/bin, so ensure that location is in your $PATH. 8 | 9 | # Check for gocov. 10 | type gocov >/dev/null 2>&1 11 | if [ $? -ne 0 ]; then 12 | echo >&2 "This script requires the gocov tool." 13 | echo >&2 "You may obtain it with the following command:" 14 | echo >&2 "go get github.com/axw/gocov/gocov" 15 | exit 1 16 | fi 17 | gocov test | gocov report 18 | -------------------------------------------------------------------------------- /base58/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package base58 provides an API for working with modified base58 and Base58Check 7 | encodings. 8 | 9 | Modified Base58 Encoding 10 | 11 | Standard base58 encoding is similar to standard base64 encoding except, as the 12 | name implies, it uses a 58 character alphabet which results in an alphanumeric 13 | string and allows some characters which are problematic for humans to be 14 | excluded. Due to this, there can be various base58 alphabets. 15 | 16 | The modified base58 alphabet used by Bitcoin, and hence this package, omits the 17 | 0, O, I, and l characters that look the same in many fonts and are therefore 18 | hard to humans to distinguish. 19 | 20 | Base58Check Encoding Scheme 21 | 22 | The Base58Check encoding scheme is primarily used for Bitcoin addresses at the 23 | time of this writing, however it can be used to generically encode arbitrary 24 | byte arrays into human-readable strings along with a version byte that can be 25 | used to differentiate the same payload. For Bitcoin addresses, the extra 26 | version is used to differentiate the network of otherwise identical public keys 27 | which helps prevent using an address intended for one network on another. 28 | */ 29 | package base58 30 | -------------------------------------------------------------------------------- /base58/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package base58_test 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/chai2010/ethutil/base58" 11 | ) 12 | 13 | // This example demonstrates how to decode modified base58 encoded data. 14 | func ExampleDecode() { 15 | // Decode example modified base58 encoded data. 16 | encoded := "25JnwSn7XKfNQ" 17 | decoded := base58.Decode(encoded) 18 | 19 | // Show the decoded data. 20 | fmt.Println("Decoded Data:", string(decoded)) 21 | 22 | // Output: 23 | // Decoded Data: Test data 24 | } 25 | 26 | // This example demonstrates how to encode data using the modified base58 27 | // encoding scheme. 28 | func ExampleEncode() { 29 | // Encode example data with the modified base58 encoding scheme. 30 | data := []byte("Test data") 31 | encoded := base58.Encode(data) 32 | 33 | // Show the encoded data. 34 | fmt.Println("Encoded Data:", encoded) 35 | 36 | // Output: 37 | // Encoded Data: 25JnwSn7XKfNQ 38 | } 39 | 40 | // This example demonstrates how to decode Base58Check encoded data. 41 | func ExampleCheckDecode() { 42 | // Decode an example Base58Check encoded data. 43 | encoded := "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" 44 | decoded, version, err := base58.CheckDecode(encoded) 45 | if err != nil { 46 | fmt.Println(err) 47 | return 48 | } 49 | 50 | // Show the decoded data. 51 | fmt.Printf("Decoded data: %x\n", decoded) 52 | fmt.Println("Version Byte:", version) 53 | 54 | // Output: 55 | // Decoded data: 62e907b15cbf27d5425399ebf6f0fb50ebb88f18 56 | // Version Byte: 0 57 | } 58 | 59 | // This example demonstrates how to encode data using the Base58Check encoding 60 | // scheme. 61 | func ExampleCheckEncode() { 62 | // Encode example data with the Base58Check encoding scheme. 63 | data := []byte("Test data") 64 | encoded := base58.CheckEncode(data, 0) 65 | 66 | // Show the encoded data. 67 | fmt.Println("Encoded Data:", encoded) 68 | 69 | // Output: 70 | // Encoded Data: 182iP79GRURMp7oMHDU 71 | } 72 | -------------------------------------------------------------------------------- /base58/genalphabet.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | //+build ignore 6 | 7 | package main 8 | 9 | import ( 10 | "bytes" 11 | "io" 12 | "log" 13 | "os" 14 | "strconv" 15 | ) 16 | 17 | var ( 18 | start = []byte(`// Copyright (c) 2015 The btcsuite developers 19 | // Use of this source code is governed by an ISC 20 | // license that can be found in the LICENSE file. 21 | 22 | // AUTOGENERATED by genalphabet.go; do not edit. 23 | 24 | package base58 25 | 26 | const ( 27 | // alphabet is the modified base58 alphabet used by Bitcoin. 28 | alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" 29 | 30 | alphabetIdx0 = '1' 31 | ) 32 | 33 | var b58 = [256]byte{`) 34 | 35 | end = []byte(`}`) 36 | 37 | alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") 38 | tab = []byte("\t") 39 | invalid = []byte("255") 40 | comma = []byte(",") 41 | space = []byte(" ") 42 | nl = []byte("\n") 43 | ) 44 | 45 | func write(w io.Writer, b []byte) { 46 | _, err := w.Write(b) 47 | if err != nil { 48 | log.Fatal(err) 49 | } 50 | } 51 | 52 | func main() { 53 | fi, err := os.Create("alphabet.go") 54 | if err != nil { 55 | log.Fatal(err) 56 | } 57 | defer fi.Close() 58 | 59 | write(fi, start) 60 | write(fi, nl) 61 | for i := byte(0); i < 32; i++ { 62 | write(fi, tab) 63 | for j := byte(0); j < 8; j++ { 64 | idx := bytes.IndexByte(alphabet, i*8+j) 65 | if idx == -1 { 66 | write(fi, invalid) 67 | } else { 68 | write(fi, strconv.AppendInt(nil, int64(idx), 10)) 69 | } 70 | write(fi, comma) 71 | if j != 7 { 72 | write(fi, space) 73 | } 74 | } 75 | write(fi, nl) 76 | } 77 | write(fi, end) 78 | write(fi, nl) 79 | } 80 | -------------------------------------------------------------------------------- /bip39/README.md: -------------------------------------------------------------------------------- 1 | # BIP39协议 2 | 3 | - https://github.com/bitcoin/bips/tree/master/bip-0039 4 | - https://github.com/tyler-smith/go-bip39 5 | -------------------------------------------------------------------------------- /bip39/bip39.go: -------------------------------------------------------------------------------- 1 | // Package bip39 is the Golang implementation of the BIP39 spec. 2 | // 3 | // The official BIP39 spec can be found at 4 | // https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki 5 | package bip39 6 | 7 | import ( 8 | "crypto/rand" 9 | "crypto/sha256" 10 | "crypto/sha512" 11 | "encoding/binary" 12 | "errors" 13 | "fmt" 14 | "math/big" 15 | "strings" 16 | 17 | "golang.org/x/crypto/pbkdf2" 18 | ) 19 | 20 | var ( 21 | // Some bitwise operands for working with big.Ints 22 | last11BitsMask = big.NewInt(2047) 23 | shift11BitsMask = big.NewInt(2048) 24 | bigOne = big.NewInt(1) 25 | bigTwo = big.NewInt(2) 26 | 27 | // used to isolate the checksum bits from the entropy+checksum byte array 28 | wordLengthChecksumMasksMapping = map[int]*big.Int{ 29 | 12: big.NewInt(15), 30 | 15: big.NewInt(31), 31 | 18: big.NewInt(63), 32 | 21: big.NewInt(127), 33 | 24: big.NewInt(255), 34 | } 35 | // used to use only the desired x of 8 available checksum bits. 36 | // 256 bit (word length 24) requires all 8 bits of the checksum, 37 | // and thus no shifting is needed for it (we would get a divByZero crash if we did) 38 | wordLengthChecksumShiftMapping = map[int]*big.Int{ 39 | 12: big.NewInt(16), 40 | 15: big.NewInt(8), 41 | 18: big.NewInt(4), 42 | 21: big.NewInt(2), 43 | } 44 | 45 | // wordList is the set of words to use 46 | wordList []string 47 | 48 | // wordMap is a reverse lookup map for wordList 49 | wordMap map[string]int 50 | ) 51 | 52 | var ( 53 | // ErrInvalidMnemonic is returned when trying to use a malformed mnemonic. 54 | ErrInvalidMnemonic = errors.New("Invalid mnenomic") 55 | 56 | // ErrEntropyLengthInvalid is returned when trying to use an entropy set with 57 | // an invalid size. 58 | ErrEntropyLengthInvalid = errors.New("Entropy length must be [128, 256] and a multiple of 32") 59 | 60 | // ErrValidatedSeedLengthMismatch is returned when a validated seed is not the 61 | // same size as the given seed. This should never happen is present only as a 62 | // sanity assertion. 63 | ErrValidatedSeedLengthMismatch = errors.New("Seed length does not match validated seed length") 64 | 65 | // ErrChecksumIncorrect is returned when entropy has the incorrect checksum. 66 | ErrChecksumIncorrect = errors.New("Checksum incorrect") 67 | ) 68 | 69 | func init() { 70 | SetWordList(English) 71 | } 72 | 73 | // SetWordList sets the list of words to use for mnemonics. Currently the list 74 | // that is set is used package-wide. 75 | func SetWordList(list []string) { 76 | wordList = list 77 | wordMap = map[string]int{} 78 | for i, v := range wordList { 79 | wordMap[v] = i 80 | } 81 | } 82 | 83 | // GetWordList gets the list of words to use for mnemonics. 84 | func GetWordList() []string { 85 | return wordList 86 | } 87 | 88 | // GetWordIndex gets word index in wordMap. 89 | func GetWordIndex(word string) (int, bool) { 90 | idx, ok := wordMap[word] 91 | return idx, ok 92 | } 93 | 94 | // NewEntropy will create random entropy bytes 95 | // so long as the requested size bitSize is an appropriate size. 96 | // 97 | // bitSize has to be a multiple 32 and be within the inclusive range of {128, 256} 98 | func NewEntropy(bitSize int) ([]byte, error) { 99 | err := validateEntropyBitSize(bitSize) 100 | if err != nil { 101 | return nil, err 102 | } 103 | 104 | entropy := make([]byte, bitSize/8) 105 | _, err = rand.Read(entropy) 106 | return entropy, err 107 | } 108 | 109 | // EntropyFromMnemonic takes a mnemonic generated by this library, 110 | // and returns the input entropy used to generate the given mnemonic. 111 | // An error is returned if the given mnemonic is invalid. 112 | func EntropyFromMnemonic(mnemonic string) ([]byte, error) { 113 | mnemonicSlice, isValid := splitMnemonicWords(mnemonic) 114 | if !isValid { 115 | return nil, ErrInvalidMnemonic 116 | } 117 | 118 | // Decode the words into a big.Int. 119 | b := big.NewInt(0) 120 | for _, v := range mnemonicSlice { 121 | index, found := wordMap[v] 122 | if found == false { 123 | return nil, fmt.Errorf("word `%v` not found in reverse map", v) 124 | } 125 | var wordBytes [2]byte 126 | binary.BigEndian.PutUint16(wordBytes[:], uint16(index)) 127 | b = b.Mul(b, shift11BitsMask) 128 | b = b.Or(b, big.NewInt(0).SetBytes(wordBytes[:])) 129 | } 130 | 131 | // Build and add the checksum to the big.Int. 132 | checksum := big.NewInt(0) 133 | checksumMask := wordLengthChecksumMasksMapping[len(mnemonicSlice)] 134 | checksum = checksum.And(b, checksumMask) 135 | 136 | b.Div(b, big.NewInt(0).Add(checksumMask, bigOne)) 137 | 138 | // The entropy is the underlying bytes of the big.Int. Any upper bytes of 139 | // all 0's are not returned so we pad the beginning of the slice with empty 140 | // bytes if necessary. 141 | entropy := b.Bytes() 142 | entropy = padByteSlice(entropy, len(mnemonicSlice)/3*4) 143 | 144 | // Generate the checksum and compare with the one we got from the mneomnic. 145 | entropyChecksumBytes := computeChecksum(entropy) 146 | entropyChecksum := big.NewInt(int64(entropyChecksumBytes[0])) 147 | if l := len(mnemonicSlice); l != 24 { 148 | checksumShift := wordLengthChecksumShiftMapping[l] 149 | entropyChecksum.Div(entropyChecksum, checksumShift) 150 | } 151 | 152 | if checksum.Cmp(entropyChecksum) != 0 { 153 | return nil, ErrChecksumIncorrect 154 | } 155 | 156 | return entropy, nil 157 | } 158 | 159 | // NewMnemonic will return a string consisting of the mnemonic words for 160 | // the given entropy. 161 | // If the provide entropy is invalid, an error will be returned. 162 | func NewMnemonic(entropy []byte) (string, error) { 163 | // Compute some lengths for convenience. 164 | entropyBitLength := len(entropy) * 8 165 | checksumBitLength := entropyBitLength / 32 166 | sentenceLength := (entropyBitLength + checksumBitLength) / 11 167 | 168 | // Validate that the requested size is supported. 169 | err := validateEntropyBitSize(entropyBitLength) 170 | if err != nil { 171 | return "", err 172 | } 173 | 174 | // Add checksum to entropy. 175 | entropy = addChecksum(entropy) 176 | 177 | // Break entropy up into sentenceLength chunks of 11 bits. 178 | // For each word AND mask the rightmost 11 bits and find the word at that index. 179 | // Then bitshift entropy 11 bits right and repeat. 180 | // Add to the last empty slot so we can work with LSBs instead of MSB. 181 | 182 | // Entropy as an int so we can bitmask without worrying about bytes slices. 183 | entropyInt := new(big.Int).SetBytes(entropy) 184 | 185 | // Slice to hold words in. 186 | words := make([]string, sentenceLength) 187 | 188 | // Throw away big.Int for AND masking. 189 | word := big.NewInt(0) 190 | 191 | for i := sentenceLength - 1; i >= 0; i-- { 192 | // Get 11 right most bits and bitshift 11 to the right for next time. 193 | word.And(entropyInt, last11BitsMask) 194 | entropyInt.Div(entropyInt, shift11BitsMask) 195 | 196 | // Get the bytes representing the 11 bits as a 2 byte slice. 197 | wordBytes := padByteSlice(word.Bytes(), 2) 198 | 199 | // Convert bytes to an index and add that word to the list. 200 | words[i] = wordList[binary.BigEndian.Uint16(wordBytes)] 201 | } 202 | 203 | return strings.Join(words, " "), nil 204 | } 205 | 206 | // MnemonicToByteArray takes a mnemonic string and turns it into a byte array 207 | // suitable for creating another mnemonic. 208 | // An error is returned if the mnemonic is invalid. 209 | func MnemonicToByteArray(mnemonic string, raw ...bool) ([]byte, error) { 210 | var ( 211 | mnemonicSlice = strings.Split(mnemonic, " ") 212 | entropyBitSize = len(mnemonicSlice) * 11 213 | checksumBitSize = entropyBitSize % 32 214 | fullByteSize = (entropyBitSize-checksumBitSize)/8 + 1 215 | checksumByteSize = fullByteSize - (fullByteSize % 4) 216 | ) 217 | 218 | // Pre validate that the mnemonic is well formed and only contains words that 219 | // are present in the word list. 220 | if !IsMnemonicValid(mnemonic) { 221 | return nil, ErrInvalidMnemonic 222 | } 223 | 224 | // Convert word indices to a big.Int representing the entropy. 225 | checksummedEntropy := big.NewInt(0) 226 | modulo := big.NewInt(2048) 227 | for _, v := range mnemonicSlice { 228 | index := big.NewInt(int64(wordMap[v])) 229 | checksummedEntropy.Mul(checksummedEntropy, modulo) 230 | checksummedEntropy.Add(checksummedEntropy, index) 231 | } 232 | 233 | // Calculate the unchecksummed entropy so we can validate that the checksum is 234 | // correct. 235 | checksumModulo := big.NewInt(0).Exp(bigTwo, big.NewInt(int64(checksumBitSize)), nil) 236 | rawEntropy := big.NewInt(0).Div(checksummedEntropy, checksumModulo) 237 | 238 | // Convert big.Ints to byte padded byte slices. 239 | rawEntropyBytes := padByteSlice(rawEntropy.Bytes(), checksumByteSize) 240 | checksummedEntropyBytes := padByteSlice(checksummedEntropy.Bytes(), fullByteSize) 241 | 242 | // Validate that the checksum is correct. 243 | newChecksummedEntropyBytes := padByteSlice(addChecksum(rawEntropyBytes), fullByteSize) 244 | if !compareByteSlices(checksummedEntropyBytes, newChecksummedEntropyBytes) { 245 | return nil, ErrChecksumIncorrect 246 | } 247 | 248 | if len(raw) > 0 && raw[0] { 249 | return rawEntropyBytes, nil 250 | } 251 | 252 | return checksummedEntropyBytes, nil 253 | } 254 | 255 | // NewSeedWithErrorChecking creates a hashed seed output given the mnemonic string and a password. 256 | // An error is returned if the mnemonic is not convertible to a byte array. 257 | func NewSeedWithErrorChecking(mnemonic string, password string) ([]byte, error) { 258 | _, err := MnemonicToByteArray(mnemonic) 259 | if err != nil { 260 | return nil, err 261 | } 262 | return NewSeed(mnemonic, password), nil 263 | } 264 | 265 | // NewSeed creates a hashed seed output given a provided string and password. 266 | // No checking is performed to validate that the string provided is a valid mnemonic. 267 | func NewSeed(mnemonic string, password string) []byte { 268 | return pbkdf2.Key([]byte(mnemonic), []byte("mnemonic"+password), 2048, 64, sha512.New) 269 | } 270 | 271 | // IsMnemonicValid attempts to verify that the provided mnemonic is valid. 272 | // Validity is determined by both the number of words being appropriate, 273 | // and that all the words in the mnemonic are present in the word list. 274 | func IsMnemonicValid(mnemonic string) bool { 275 | _, err := EntropyFromMnemonic(mnemonic) 276 | return err == nil 277 | } 278 | 279 | // Appends to data the first (len(data) / 32)bits of the result of sha256(data) 280 | // Currently only supports data up to 32 bytes 281 | func addChecksum(data []byte) []byte { 282 | // Get first byte of sha256 283 | hash := computeChecksum(data) 284 | firstChecksumByte := hash[0] 285 | 286 | // len() is in bytes so we divide by 4 287 | checksumBitLength := uint(len(data) / 4) 288 | 289 | // For each bit of check sum we want we shift the data one the left 290 | // and then set the (new) right most bit equal to checksum bit at that index 291 | // staring from the left 292 | dataBigInt := new(big.Int).SetBytes(data) 293 | for i := uint(0); i < checksumBitLength; i++ { 294 | // Bitshift 1 left 295 | dataBigInt.Mul(dataBigInt, bigTwo) 296 | 297 | // Set rightmost bit if leftmost checksum bit is set 298 | if uint8(firstChecksumByte&(1<<(7-i))) > 0 { 299 | dataBigInt.Or(dataBigInt, bigOne) 300 | } 301 | } 302 | 303 | return dataBigInt.Bytes() 304 | } 305 | 306 | func computeChecksum(data []byte) []byte { 307 | hasher := sha256.New() 308 | hasher.Write(data) 309 | return hasher.Sum(nil) 310 | } 311 | 312 | // validateEntropyBitSize ensures that entropy is the correct size for being a 313 | // mnemonic. 314 | func validateEntropyBitSize(bitSize int) error { 315 | if (bitSize%32) != 0 || bitSize < 128 || bitSize > 256 { 316 | return ErrEntropyLengthInvalid 317 | } 318 | return nil 319 | } 320 | 321 | // padByteSlice returns a byte slice of the given size with contents of the 322 | // given slice left padded and any empty spaces filled with 0's. 323 | func padByteSlice(slice []byte, length int) []byte { 324 | offset := length - len(slice) 325 | if offset <= 0 { 326 | return slice 327 | } 328 | newSlice := make([]byte, length) 329 | copy(newSlice[offset:], slice) 330 | return newSlice 331 | } 332 | 333 | // compareByteSlices returns true of the byte slices have equal contents and 334 | // returns false otherwise. 335 | func compareByteSlices(a, b []byte) bool { 336 | if len(a) != len(b) { 337 | return false 338 | } 339 | for i := range a { 340 | if a[i] != b[i] { 341 | return false 342 | } 343 | } 344 | return true 345 | } 346 | 347 | func splitMnemonicWords(mnemonic string) ([]string, bool) { 348 | // Create a list of all the words in the mnemonic sentence 349 | words := strings.Fields(mnemonic) 350 | 351 | // Get num of words 352 | numOfWords := len(words) 353 | 354 | // The number of words should be 12, 15, 18, 21 or 24 355 | if numOfWords%3 != 0 || numOfWords < 12 || numOfWords > 24 { 356 | return nil, false 357 | } 358 | return words, true 359 | } 360 | -------------------------------------------------------------------------------- /bip39/example_test.go: -------------------------------------------------------------------------------- 1 | package bip39_test 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | 7 | "github.com/chai2010/ethutil/bip39" 8 | ) 9 | 10 | func ExampleNewMnemonic() { 11 | // the entropy can be any byte slice, generated how pleased, 12 | // as long its bit size is a multiple of 32 and is within 13 | // the inclusive range of {128,256} 14 | entropy, _ := hex.DecodeString("066dca1a2bb7e8a1db2832148ce9933eea0f3ac9548d793112d9a95c9407efad") 15 | 16 | // generate a mnemomic 17 | mnemomic, _ := bip39.NewMnemonic(entropy) 18 | fmt.Println(mnemomic) 19 | // output: 20 | // all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform 21 | } 22 | 23 | func ExampleNewSeed() { 24 | seed := bip39.NewSeed("all hour make first leader extend hole alien behind guard gospel lava path output census museum junior mass reopen famous sing advance salt reform", "TREZOR") 25 | fmt.Println(hex.EncodeToString(seed)) 26 | // output: 27 | // 26e975ec644423f4a4c4f4215ef09b4bd7ef924e85d1d17c4cf3f136c2863cf6df0a475045652c57eb5fb41513ca2a2d67722b77e954b4b3fc11f7590449191d 28 | } 29 | -------------------------------------------------------------------------------- /btcec/README.md: -------------------------------------------------------------------------------- 1 | btcec 2 | ===== 3 | 4 | [![Build Status](https://travis-ci.org/btcsuite/btcd.png?branch=master)](https://travis-ci.org/btcsuite/btcec) 5 | [![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) 6 | [![GoDoc](https://godoc.org/github.com/btcsuite/btcd/btcec?status.png)](http://godoc.org/github.com/btcsuite/btcd/btcec) 7 | 8 | Package btcec implements elliptic curve cryptography needed for working with 9 | Bitcoin (secp256k1 only for now). It is designed so that it may be used with the 10 | standard crypto/ecdsa packages provided with go. A comprehensive suite of test 11 | is provided to ensure proper functionality. Package btcec was originally based 12 | on work from ThePiachu which is licensed under the same terms as Go, but it has 13 | signficantly diverged since then. The btcsuite developers original is licensed 14 | under the liberal ISC license. 15 | 16 | Although this package was primarily written for btcd, it has intentionally been 17 | designed so it can be used as a standalone package for any projects needing to 18 | use secp256k1 elliptic curve cryptography. 19 | 20 | ## Installation and Updating 21 | 22 | ```bash 23 | $ go get -u github.com/btcsuite/btcd/btcec 24 | ``` 25 | 26 | ## Examples 27 | 28 | * [Sign Message](http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--SignMessage) 29 | Demonstrates signing a message with a secp256k1 private key that is first 30 | parsed form raw bytes and serializing the generated signature. 31 | 32 | * [Verify Signature](http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--VerifySignature) 33 | Demonstrates verifying a secp256k1 signature against a public key that is 34 | first parsed from raw bytes. The signature is also parsed from raw bytes. 35 | 36 | * [Encryption](http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--EncryptMessage) 37 | Demonstrates encrypting a message for a public key that is first parsed from 38 | raw bytes, then decrypting it using the corresponding private key. 39 | 40 | * [Decryption](http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--DecryptMessage) 41 | Demonstrates decrypting a message using a private key that is first parsed 42 | from raw bytes. 43 | 44 | ## GPG Verification Key 45 | 46 | All official release tags are signed by Conformal so users can ensure the code 47 | has not been tampered with and is coming from the btcsuite developers. To 48 | verify the signature perform the following: 49 | 50 | - Download the public key from the Conformal website at 51 | https://opensource.conformal.com/GIT-GPG-KEY-conformal.txt 52 | 53 | - Import the public key into your GPG keyring: 54 | ```bash 55 | gpg --import GIT-GPG-KEY-conformal.txt 56 | ``` 57 | 58 | - Verify the release tag with the following command where `TAG_NAME` is a 59 | placeholder for the specific tag: 60 | ```bash 61 | git tag -v TAG_NAME 62 | ``` 63 | 64 | ## License 65 | 66 | Package btcec is licensed under the [copyfree](http://copyfree.org) ISC License 67 | except for btcec.go and btcec_test.go which is under the same license as Go. 68 | 69 | -------------------------------------------------------------------------------- /btcec/bench_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013-2016 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package btcec 6 | 7 | import ( 8 | "encoding/hex" 9 | "testing" 10 | ) 11 | 12 | // BenchmarkAddJacobian benchmarks the secp256k1 curve addJacobian function with 13 | // Z values of 1 so that the associated optimizations are used. 14 | func BenchmarkAddJacobian(b *testing.B) { 15 | b.StopTimer() 16 | x1 := new(fieldVal).SetHex("34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6") 17 | y1 := new(fieldVal).SetHex("0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232") 18 | z1 := new(fieldVal).SetHex("1") 19 | x2 := new(fieldVal).SetHex("34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6") 20 | y2 := new(fieldVal).SetHex("0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232") 21 | z2 := new(fieldVal).SetHex("1") 22 | x3, y3, z3 := new(fieldVal), new(fieldVal), new(fieldVal) 23 | curve := S256() 24 | b.StartTimer() 25 | for i := 0; i < b.N; i++ { 26 | curve.addJacobian(x1, y1, z1, x2, y2, z2, x3, y3, z3) 27 | } 28 | } 29 | 30 | // BenchmarkAddJacobianNotZOne benchmarks the secp256k1 curve addJacobian 31 | // function with Z values other than one so the optimizations associated with 32 | // Z=1 aren't used. 33 | func BenchmarkAddJacobianNotZOne(b *testing.B) { 34 | b.StopTimer() 35 | x1 := new(fieldVal).SetHex("d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718") 36 | y1 := new(fieldVal).SetHex("5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190") 37 | z1 := new(fieldVal).SetHex("2") 38 | x2 := new(fieldVal).SetHex("91abba6a34b7481d922a4bd6a04899d5a686f6cf6da4e66a0cb427fb25c04bd4") 39 | y2 := new(fieldVal).SetHex("03fede65e30b4e7576a2abefc963ddbf9fdccbf791b77c29beadefe49951f7d1") 40 | z2 := new(fieldVal).SetHex("3") 41 | x3, y3, z3 := new(fieldVal), new(fieldVal), new(fieldVal) 42 | curve := S256() 43 | b.StartTimer() 44 | for i := 0; i < b.N; i++ { 45 | curve.addJacobian(x1, y1, z1, x2, y2, z2, x3, y3, z3) 46 | } 47 | } 48 | 49 | // BenchmarkScalarBaseMult benchmarks the secp256k1 curve ScalarBaseMult 50 | // function. 51 | func BenchmarkScalarBaseMult(b *testing.B) { 52 | k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") 53 | curve := S256() 54 | for i := 0; i < b.N; i++ { 55 | curve.ScalarBaseMult(k.Bytes()) 56 | } 57 | } 58 | 59 | // BenchmarkScalarBaseMultLarge benchmarks the secp256k1 curve ScalarBaseMult 60 | // function with abnormally large k values. 61 | func BenchmarkScalarBaseMultLarge(b *testing.B) { 62 | k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c005751111111011111110") 63 | curve := S256() 64 | for i := 0; i < b.N; i++ { 65 | curve.ScalarBaseMult(k.Bytes()) 66 | } 67 | } 68 | 69 | // BenchmarkScalarMult benchmarks the secp256k1 curve ScalarMult function. 70 | func BenchmarkScalarMult(b *testing.B) { 71 | x := fromHex("34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6") 72 | y := fromHex("0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232") 73 | k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") 74 | curve := S256() 75 | for i := 0; i < b.N; i++ { 76 | curve.ScalarMult(x, y, k.Bytes()) 77 | } 78 | } 79 | 80 | // BenchmarkNAF benchmarks the NAF function. 81 | func BenchmarkNAF(b *testing.B) { 82 | k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") 83 | for i := 0; i < b.N; i++ { 84 | NAF(k.Bytes()) 85 | } 86 | } 87 | 88 | // BenchmarkSigVerify benchmarks how long it takes the secp256k1 curve to 89 | // verify signatures. 90 | func BenchmarkSigVerify(b *testing.B) { 91 | b.StopTimer() 92 | // Randomly generated keypair. 93 | // Private key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d 94 | pubKey := PublicKey{ 95 | Curve: S256(), 96 | X: fromHex("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"), 97 | Y: fromHex("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"), 98 | } 99 | 100 | // Double sha256 of []byte{0x01, 0x02, 0x03, 0x04} 101 | msgHash := fromHex("8de472e2399610baaa7f84840547cd409434e31f5d3bd71e4d947f283874f9c0") 102 | sig := Signature{ 103 | R: fromHex("fef45d2892953aa5bbcdb057b5e98b208f1617a7498af7eb765574e29b5d9c2c"), 104 | S: fromHex("d47563f52aac6b04b55de236b7c515eb9311757db01e02cff079c3ca6efb063f"), 105 | } 106 | 107 | if !sig.Verify(msgHash.Bytes(), &pubKey) { 108 | b.Errorf("Signature failed to verify") 109 | return 110 | } 111 | b.StartTimer() 112 | 113 | for i := 0; i < b.N; i++ { 114 | sig.Verify(msgHash.Bytes(), &pubKey) 115 | } 116 | } 117 | 118 | // BenchmarkFieldNormalize benchmarks how long it takes the internal field 119 | // to perform normalization (which includes modular reduction). 120 | func BenchmarkFieldNormalize(b *testing.B) { 121 | // The normalize function is constant time so default value is fine. 122 | f := new(fieldVal) 123 | for i := 0; i < b.N; i++ { 124 | f.Normalize() 125 | } 126 | } 127 | 128 | // BenchmarkParseCompressedPubKey benchmarks how long it takes to decompress and 129 | // validate a compressed public key from a byte array. 130 | func BenchmarkParseCompressedPubKey(b *testing.B) { 131 | rawPk, _ := hex.DecodeString("0234f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6") 132 | 133 | var ( 134 | pk *PublicKey 135 | err error 136 | ) 137 | 138 | b.ReportAllocs() 139 | b.ResetTimer() 140 | for i := 0; i < b.N; i++ { 141 | pk, err = ParsePubKey(rawPk, S256()) 142 | } 143 | _ = pk 144 | _ = err 145 | } 146 | -------------------------------------------------------------------------------- /btcec/ciphering.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package btcec 6 | 7 | import ( 8 | "bytes" 9 | "crypto/aes" 10 | "crypto/cipher" 11 | "crypto/hmac" 12 | "crypto/rand" 13 | "crypto/sha256" 14 | "crypto/sha512" 15 | "errors" 16 | "io" 17 | ) 18 | 19 | var ( 20 | // ErrInvalidMAC occurs when Message Authentication Check (MAC) fails 21 | // during decryption. This happens because of either invalid private key or 22 | // corrupt ciphertext. 23 | ErrInvalidMAC = errors.New("invalid mac hash") 24 | 25 | // errInputTooShort occurs when the input ciphertext to the Decrypt 26 | // function is less than 134 bytes long. 27 | errInputTooShort = errors.New("ciphertext too short") 28 | 29 | // errUnsupportedCurve occurs when the first two bytes of the encrypted 30 | // text aren't 0x02CA (= 712 = secp256k1, from OpenSSL). 31 | errUnsupportedCurve = errors.New("unsupported curve") 32 | 33 | errInvalidXLength = errors.New("invalid X length, must be 32") 34 | errInvalidYLength = errors.New("invalid Y length, must be 32") 35 | errInvalidPadding = errors.New("invalid PKCS#7 padding") 36 | 37 | // 0x02CA = 714 38 | ciphCurveBytes = [2]byte{0x02, 0xCA} 39 | // 0x20 = 32 40 | ciphCoordLength = [2]byte{0x00, 0x20} 41 | ) 42 | 43 | // GenerateSharedSecret generates a shared secret based on a private key and a 44 | // public key using Diffie-Hellman key exchange (ECDH) (RFC 4753). 45 | // RFC5903 Section 9 states we should only return x. 46 | func GenerateSharedSecret(privkey *PrivateKey, pubkey *PublicKey) []byte { 47 | x, _ := pubkey.Curve.ScalarMult(pubkey.X, pubkey.Y, privkey.D.Bytes()) 48 | return x.Bytes() 49 | } 50 | 51 | // Encrypt encrypts data for the target public key using AES-256-CBC. It also 52 | // generates a private key (the pubkey of which is also in the output). The only 53 | // supported curve is secp256k1. The `structure' that it encodes everything into 54 | // is: 55 | // 56 | // struct { 57 | // // Initialization Vector used for AES-256-CBC 58 | // IV [16]byte 59 | // // Public Key: curve(2) + len_of_pubkeyX(2) + pubkeyX + 60 | // // len_of_pubkeyY(2) + pubkeyY (curve = 714) 61 | // PublicKey [70]byte 62 | // // Cipher text 63 | // Data []byte 64 | // // HMAC-SHA-256 Message Authentication Code 65 | // HMAC [32]byte 66 | // } 67 | // 68 | // The primary aim is to ensure byte compatibility with Pyelliptic. Also, refer 69 | // to section 5.8.1 of ANSI X9.63 for rationale on this format. 70 | func Encrypt(pubkey *PublicKey, in []byte) ([]byte, error) { 71 | ephemeral, err := NewPrivateKey(S256()) 72 | if err != nil { 73 | return nil, err 74 | } 75 | ecdhKey := GenerateSharedSecret(ephemeral, pubkey) 76 | derivedKey := sha512.Sum512(ecdhKey) 77 | keyE := derivedKey[:32] 78 | keyM := derivedKey[32:] 79 | 80 | paddedIn := addPKCSPadding(in) 81 | // IV + Curve params/X/Y + padded plaintext/ciphertext + HMAC-256 82 | out := make([]byte, aes.BlockSize+70+len(paddedIn)+sha256.Size) 83 | iv := out[:aes.BlockSize] 84 | if _, err = io.ReadFull(rand.Reader, iv); err != nil { 85 | return nil, err 86 | } 87 | // start writing public key 88 | pb := ephemeral.PubKey().SerializeUncompressed() 89 | offset := aes.BlockSize 90 | 91 | // curve and X length 92 | copy(out[offset:offset+4], append(ciphCurveBytes[:], ciphCoordLength[:]...)) 93 | offset += 4 94 | // X 95 | copy(out[offset:offset+32], pb[1:33]) 96 | offset += 32 97 | // Y length 98 | copy(out[offset:offset+2], ciphCoordLength[:]) 99 | offset += 2 100 | // Y 101 | copy(out[offset:offset+32], pb[33:]) 102 | offset += 32 103 | 104 | // start encryption 105 | block, err := aes.NewCipher(keyE) 106 | if err != nil { 107 | return nil, err 108 | } 109 | mode := cipher.NewCBCEncrypter(block, iv) 110 | mode.CryptBlocks(out[offset:len(out)-sha256.Size], paddedIn) 111 | 112 | // start HMAC-SHA-256 113 | hm := hmac.New(sha256.New, keyM) 114 | hm.Write(out[:len(out)-sha256.Size]) // everything is hashed 115 | copy(out[len(out)-sha256.Size:], hm.Sum(nil)) // write checksum 116 | 117 | return out, nil 118 | } 119 | 120 | // Decrypt decrypts data that was encrypted using the Encrypt function. 121 | func Decrypt(priv *PrivateKey, in []byte) ([]byte, error) { 122 | // IV + Curve params/X/Y + 1 block + HMAC-256 123 | if len(in) < aes.BlockSize+70+aes.BlockSize+sha256.Size { 124 | return nil, errInputTooShort 125 | } 126 | 127 | // read iv 128 | iv := in[:aes.BlockSize] 129 | offset := aes.BlockSize 130 | 131 | // start reading pubkey 132 | if !bytes.Equal(in[offset:offset+2], ciphCurveBytes[:]) { 133 | return nil, errUnsupportedCurve 134 | } 135 | offset += 2 136 | 137 | if !bytes.Equal(in[offset:offset+2], ciphCoordLength[:]) { 138 | return nil, errInvalidXLength 139 | } 140 | offset += 2 141 | 142 | xBytes := in[offset : offset+32] 143 | offset += 32 144 | 145 | if !bytes.Equal(in[offset:offset+2], ciphCoordLength[:]) { 146 | return nil, errInvalidYLength 147 | } 148 | offset += 2 149 | 150 | yBytes := in[offset : offset+32] 151 | offset += 32 152 | 153 | pb := make([]byte, 65) 154 | pb[0] = byte(0x04) // uncompressed 155 | copy(pb[1:33], xBytes) 156 | copy(pb[33:], yBytes) 157 | // check if (X, Y) lies on the curve and create a Pubkey if it does 158 | pubkey, err := ParsePubKey(pb, S256()) 159 | if err != nil { 160 | return nil, err 161 | } 162 | 163 | // check for cipher text length 164 | if (len(in)-aes.BlockSize-offset-sha256.Size)%aes.BlockSize != 0 { 165 | return nil, errInvalidPadding // not padded to 16 bytes 166 | } 167 | 168 | // read hmac 169 | messageMAC := in[len(in)-sha256.Size:] 170 | 171 | // generate shared secret 172 | ecdhKey := GenerateSharedSecret(priv, pubkey) 173 | derivedKey := sha512.Sum512(ecdhKey) 174 | keyE := derivedKey[:32] 175 | keyM := derivedKey[32:] 176 | 177 | // verify mac 178 | hm := hmac.New(sha256.New, keyM) 179 | hm.Write(in[:len(in)-sha256.Size]) // everything is hashed 180 | expectedMAC := hm.Sum(nil) 181 | if !hmac.Equal(messageMAC, expectedMAC) { 182 | return nil, ErrInvalidMAC 183 | } 184 | 185 | // start decryption 186 | block, err := aes.NewCipher(keyE) 187 | if err != nil { 188 | return nil, err 189 | } 190 | mode := cipher.NewCBCDecrypter(block, iv) 191 | // same length as ciphertext 192 | plaintext := make([]byte, len(in)-offset-sha256.Size) 193 | mode.CryptBlocks(plaintext, in[offset:len(in)-sha256.Size]) 194 | 195 | return removePKCSPadding(plaintext) 196 | } 197 | 198 | // Implement PKCS#7 padding with block size of 16 (AES block size). 199 | 200 | // addPKCSPadding adds padding to a block of data 201 | func addPKCSPadding(src []byte) []byte { 202 | padding := aes.BlockSize - len(src)%aes.BlockSize 203 | padtext := bytes.Repeat([]byte{byte(padding)}, padding) 204 | return append(src, padtext...) 205 | } 206 | 207 | // removePKCSPadding removes padding from data that was added with addPKCSPadding 208 | func removePKCSPadding(src []byte) ([]byte, error) { 209 | length := len(src) 210 | padLength := int(src[length-1]) 211 | if padLength > aes.BlockSize || length < aes.BlockSize { 212 | return nil, errInvalidPadding 213 | } 214 | 215 | return src[:length-padLength], nil 216 | } 217 | -------------------------------------------------------------------------------- /btcec/ciphering_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2016 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package btcec 6 | 7 | import ( 8 | "bytes" 9 | "encoding/hex" 10 | "testing" 11 | ) 12 | 13 | func TestGenerateSharedSecret(t *testing.T) { 14 | privKey1, err := NewPrivateKey(S256()) 15 | if err != nil { 16 | t.Errorf("private key generation error: %s", err) 17 | return 18 | } 19 | privKey2, err := NewPrivateKey(S256()) 20 | if err != nil { 21 | t.Errorf("private key generation error: %s", err) 22 | return 23 | } 24 | 25 | secret1 := GenerateSharedSecret(privKey1, privKey2.PubKey()) 26 | secret2 := GenerateSharedSecret(privKey2, privKey1.PubKey()) 27 | 28 | if !bytes.Equal(secret1, secret2) { 29 | t.Errorf("ECDH failed, secrets mismatch - first: %x, second: %x", 30 | secret1, secret2) 31 | } 32 | } 33 | 34 | // Test 1: Encryption and decryption 35 | func TestCipheringBasic(t *testing.T) { 36 | privkey, err := NewPrivateKey(S256()) 37 | if err != nil { 38 | t.Fatal("failed to generate private key") 39 | } 40 | 41 | in := []byte("Hey there dude. How are you doing? This is a test.") 42 | 43 | out, err := Encrypt(privkey.PubKey(), in) 44 | if err != nil { 45 | t.Fatal("failed to encrypt:", err) 46 | } 47 | 48 | dec, err := Decrypt(privkey, out) 49 | if err != nil { 50 | t.Fatal("failed to decrypt:", err) 51 | } 52 | 53 | if !bytes.Equal(in, dec) { 54 | t.Error("decrypted data doesn't match original") 55 | } 56 | } 57 | 58 | // Test 2: Byte compatibility with Pyelliptic 59 | func TestCiphering(t *testing.T) { 60 | pb, _ := hex.DecodeString("fe38240982f313ae5afb3e904fb8215fb11af1200592b" + 61 | "fca26c96c4738e4bf8f") 62 | privkey, _ := PrivKeyFromBytes(S256(), pb) 63 | 64 | in := []byte("This is just a test.") 65 | out, _ := hex.DecodeString("b0d66e5adaa5ed4e2f0ca68e17b8f2fc02ca002009e3" + 66 | "3487e7fa4ab505cf34d98f131be7bd258391588ca7804acb30251e71a04e0020ecf" + 67 | "df0f84608f8add82d7353af780fbb28868c713b7813eb4d4e61f7b75d7534dd9856" + 68 | "9b0ba77cf14348fcff80fee10e11981f1b4be372d93923e9178972f69937ec850ed" + 69 | "6c3f11ff572ddd5b2bedf9f9c0b327c54da02a28fcdce1f8369ffec") 70 | 71 | dec, err := Decrypt(privkey, out) 72 | if err != nil { 73 | t.Fatal("failed to decrypt:", err) 74 | } 75 | 76 | if !bytes.Equal(in, dec) { 77 | t.Error("decrypted data doesn't match original") 78 | } 79 | } 80 | 81 | func TestCipheringErrors(t *testing.T) { 82 | privkey, err := NewPrivateKey(S256()) 83 | if err != nil { 84 | t.Fatal("failed to generate private key") 85 | } 86 | 87 | tests1 := []struct { 88 | ciphertext []byte // input ciphertext 89 | }{ 90 | {bytes.Repeat([]byte{0x00}, 133)}, // errInputTooShort 91 | {bytes.Repeat([]byte{0x00}, 134)}, // errUnsupportedCurve 92 | {bytes.Repeat([]byte{0x02, 0xCA}, 134)}, // errInvalidXLength 93 | {bytes.Repeat([]byte{0x02, 0xCA, 0x00, 0x20}, 134)}, // errInvalidYLength 94 | {[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IV 95 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 96 | 0x02, 0xCA, 0x00, 0x20, // curve and X length 97 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // X 98 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 99 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 100 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 101 | 0x00, 0x20, // Y length 102 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Y 103 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 104 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 105 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 106 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ciphertext 107 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 108 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // MAC 109 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 110 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 111 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 112 | }}, // invalid pubkey 113 | {[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IV 114 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 115 | 0x02, 0xCA, 0x00, 0x20, // curve and X length 116 | 0x11, 0x5C, 0x42, 0xE7, 0x57, 0xB2, 0xEF, 0xB7, // X 117 | 0x67, 0x1C, 0x57, 0x85, 0x30, 0xEC, 0x19, 0x1A, 118 | 0x13, 0x59, 0x38, 0x1E, 0x6A, 0x71, 0x12, 0x7A, 119 | 0x9D, 0x37, 0xC4, 0x86, 0xFD, 0x30, 0xDA, 0xE5, 120 | 0x00, 0x20, // Y length 121 | 0x7E, 0x76, 0xDC, 0x58, 0xF6, 0x93, 0xBD, 0x7E, // Y 122 | 0x70, 0x10, 0x35, 0x8C, 0xE6, 0xB1, 0x65, 0xE4, 123 | 0x83, 0xA2, 0x92, 0x10, 0x10, 0xDB, 0x67, 0xAC, 124 | 0x11, 0xB1, 0xB5, 0x1B, 0x65, 0x19, 0x53, 0xD2, 125 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ciphertext 126 | // padding not aligned to 16 bytes 127 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 128 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // MAC 129 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 130 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 131 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 132 | }}, // errInvalidPadding 133 | {[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IV 134 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 135 | 0x02, 0xCA, 0x00, 0x20, // curve and X length 136 | 0x11, 0x5C, 0x42, 0xE7, 0x57, 0xB2, 0xEF, 0xB7, // X 137 | 0x67, 0x1C, 0x57, 0x85, 0x30, 0xEC, 0x19, 0x1A, 138 | 0x13, 0x59, 0x38, 0x1E, 0x6A, 0x71, 0x12, 0x7A, 139 | 0x9D, 0x37, 0xC4, 0x86, 0xFD, 0x30, 0xDA, 0xE5, 140 | 0x00, 0x20, // Y length 141 | 0x7E, 0x76, 0xDC, 0x58, 0xF6, 0x93, 0xBD, 0x7E, // Y 142 | 0x70, 0x10, 0x35, 0x8C, 0xE6, 0xB1, 0x65, 0xE4, 143 | 0x83, 0xA2, 0x92, 0x10, 0x10, 0xDB, 0x67, 0xAC, 144 | 0x11, 0xB1, 0xB5, 0x1B, 0x65, 0x19, 0x53, 0xD2, 145 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ciphertext 146 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 147 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // MAC 148 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 149 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 150 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 151 | }}, // ErrInvalidMAC 152 | } 153 | 154 | for i, test := range tests1 { 155 | _, err = Decrypt(privkey, test.ciphertext) 156 | if err == nil { 157 | t.Errorf("Decrypt #%d did not get error", i) 158 | } 159 | } 160 | 161 | // test error from removePKCSPadding 162 | tests2 := []struct { 163 | in []byte // input data 164 | }{ 165 | {bytes.Repeat([]byte{0x11}, 17)}, 166 | {bytes.Repeat([]byte{0x07}, 15)}, 167 | } 168 | for i, test := range tests2 { 169 | _, err = removePKCSPadding(test.in) 170 | if err == nil { 171 | t.Errorf("removePKCSPadding #%d did not get error", i) 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /btcec/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | Package btcec implements support for the elliptic curves needed for bitcoin. 7 | 8 | Bitcoin uses elliptic curve cryptography using koblitz curves 9 | (specifically secp256k1) for cryptographic functions. See 10 | http://www.secg.org/collateral/sec2_final.pdf for details on the 11 | standard. 12 | 13 | This package provides the data structures and functions implementing the 14 | crypto/elliptic Curve interface in order to permit using these curves 15 | with the standard crypto/ecdsa package provided with go. Helper 16 | functionality is provided to parse signatures and public keys from 17 | standard formats. It was designed for use with btcd, but should be 18 | general enough for other uses of elliptic curve crypto. It was originally based 19 | on some initial work by ThePiachu, but has significantly diverged since then. 20 | */ 21 | package btcec 22 | -------------------------------------------------------------------------------- /btcec/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build ignore 6 | 7 | package btcec_test 8 | 9 | import ( 10 | "encoding/hex" 11 | "fmt" 12 | 13 | "github.com/btcsuite/btcd/btcec" 14 | "github.com/btcsuite/btcd/chaincfg/chainhash" 15 | ) 16 | 17 | // This example demonstrates signing a message with a secp256k1 private key that 18 | // is first parsed form raw bytes and serializing the generated signature. 19 | func Example_signMessage() { 20 | // Decode a hex-encoded private key. 21 | pkBytes, err := hex.DecodeString("22a47fa09a223f2aa079edf85a7c2d4f87" + 22 | "20ee63e502ee2869afab7de234b80c") 23 | if err != nil { 24 | fmt.Println(err) 25 | return 26 | } 27 | privKey, pubKey := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) 28 | 29 | // Sign a message using the private key. 30 | message := "test message" 31 | messageHash := chainhash.DoubleHashB([]byte(message)) 32 | signature, err := privKey.Sign(messageHash) 33 | if err != nil { 34 | fmt.Println(err) 35 | return 36 | } 37 | 38 | // Serialize and display the signature. 39 | fmt.Printf("Serialized Signature: %x\n", signature.Serialize()) 40 | 41 | // Verify the signature for the message using the public key. 42 | verified := signature.Verify(messageHash, pubKey) 43 | fmt.Printf("Signature Verified? %v\n", verified) 44 | 45 | // Output: 46 | // Serialized Signature: 304402201008e236fa8cd0f25df4482dddbb622e8a8b26ef0ba731719458de3ccd93805b022032f8ebe514ba5f672466eba334639282616bb3c2f0ab09998037513d1f9e3d6d 47 | // Signature Verified? true 48 | } 49 | 50 | // This example demonstrates verifying a secp256k1 signature against a public 51 | // key that is first parsed from raw bytes. The signature is also parsed from 52 | // raw bytes. 53 | func Example_verifySignature() { 54 | // Decode hex-encoded serialized public key. 55 | pubKeyBytes, err := hex.DecodeString("02a673638cb9587cb68ea08dbef685c" + 56 | "6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5") 57 | if err != nil { 58 | fmt.Println(err) 59 | return 60 | } 61 | pubKey, err := btcec.ParsePubKey(pubKeyBytes, btcec.S256()) 62 | if err != nil { 63 | fmt.Println(err) 64 | return 65 | } 66 | 67 | // Decode hex-encoded serialized signature. 68 | sigBytes, err := hex.DecodeString("30450220090ebfb3690a0ff115bb1b38b" + 69 | "8b323a667b7653454f1bccb06d4bbdca42c2079022100ec95778b51e707" + 70 | "1cb1205f8bde9af6592fc978b0452dafe599481c46d6b2e479") 71 | 72 | if err != nil { 73 | fmt.Println(err) 74 | return 75 | } 76 | signature, err := btcec.ParseSignature(sigBytes, btcec.S256()) 77 | if err != nil { 78 | fmt.Println(err) 79 | return 80 | } 81 | 82 | // Verify the signature for the message using the public key. 83 | message := "test message" 84 | messageHash := chainhash.DoubleHashB([]byte(message)) 85 | verified := signature.Verify(messageHash, pubKey) 86 | fmt.Println("Signature Verified?", verified) 87 | 88 | // Output: 89 | // Signature Verified? true 90 | } 91 | 92 | // This example demonstrates encrypting a message for a public key that is first 93 | // parsed from raw bytes, then decrypting it using the corresponding private key. 94 | func Example_encryptMessage() { 95 | // Decode the hex-encoded pubkey of the recipient. 96 | pubKeyBytes, err := hex.DecodeString("04115c42e757b2efb7671c578530ec191a1" + 97 | "359381e6a71127a9d37c486fd30dae57e76dc58f693bd7e7010358ce6b165e483a29" + 98 | "21010db67ac11b1b51b651953d2") // uncompressed pubkey 99 | if err != nil { 100 | fmt.Println(err) 101 | return 102 | } 103 | pubKey, err := btcec.ParsePubKey(pubKeyBytes, btcec.S256()) 104 | if err != nil { 105 | fmt.Println(err) 106 | return 107 | } 108 | 109 | // Encrypt a message decryptable by the private key corresponding to pubKey 110 | message := "test message" 111 | ciphertext, err := btcec.Encrypt(pubKey, []byte(message)) 112 | if err != nil { 113 | fmt.Println(err) 114 | return 115 | } 116 | 117 | // Decode the hex-encoded private key. 118 | pkBytes, err := hex.DecodeString("a11b0a4e1a132305652ee7a8eb7848f6ad" + 119 | "5ea381e3ce20a2c086a2e388230811") 120 | if err != nil { 121 | fmt.Println(err) 122 | return 123 | } 124 | // note that we already have corresponding pubKey 125 | privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) 126 | 127 | // Try decrypting and verify if it's the same message. 128 | plaintext, err := btcec.Decrypt(privKey, ciphertext) 129 | if err != nil { 130 | fmt.Println(err) 131 | return 132 | } 133 | 134 | fmt.Println(string(plaintext)) 135 | 136 | // Output: 137 | // test message 138 | } 139 | 140 | // This example demonstrates decrypting a message using a private key that is 141 | // first parsed from raw bytes. 142 | func Example_decryptMessage() { 143 | // Decode the hex-encoded private key. 144 | pkBytes, err := hex.DecodeString("a11b0a4e1a132305652ee7a8eb7848f6ad" + 145 | "5ea381e3ce20a2c086a2e388230811") 146 | if err != nil { 147 | fmt.Println(err) 148 | return 149 | } 150 | 151 | privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) 152 | 153 | ciphertext, err := hex.DecodeString("35f644fbfb208bc71e57684c3c8b437402ca" + 154 | "002047a2f1b38aa1a8f1d5121778378414f708fe13ebf7b4a7bb74407288c1958969" + 155 | "00207cf4ac6057406e40f79961c973309a892732ae7a74ee96cd89823913b8b8d650" + 156 | "a44166dc61ea1c419d47077b748a9c06b8d57af72deb2819d98a9d503efc59fc8307" + 157 | "d14174f8b83354fac3ff56075162") 158 | 159 | // Try decrypting the message. 160 | plaintext, err := btcec.Decrypt(privKey, ciphertext) 161 | if err != nil { 162 | fmt.Println(err) 163 | return 164 | } 165 | 166 | fmt.Println(string(plaintext)) 167 | 168 | // Output: 169 | // test message 170 | } 171 | -------------------------------------------------------------------------------- /btcec/genprecomps.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | // This file is ignored during the regular build due to the following build tag. 6 | // It is called by go generate and used to automatically generate pre-computed 7 | // tables used to accelerate operations. 8 | // +build ignore 9 | 10 | package main 11 | 12 | import ( 13 | "bytes" 14 | "compress/zlib" 15 | "encoding/base64" 16 | "fmt" 17 | "log" 18 | "os" 19 | 20 | "github.com/btcsuite/btcd/btcec" 21 | ) 22 | 23 | func main() { 24 | fi, err := os.Create("secp256k1.go") 25 | if err != nil { 26 | log.Fatal(err) 27 | } 28 | defer fi.Close() 29 | 30 | // Compress the serialized byte points. 31 | serialized := btcec.S256().SerializedBytePoints() 32 | var compressed bytes.Buffer 33 | w := zlib.NewWriter(&compressed) 34 | if _, err := w.Write(serialized); err != nil { 35 | fmt.Println(err) 36 | os.Exit(1) 37 | } 38 | w.Close() 39 | 40 | // Encode the compressed byte points with base64. 41 | encoded := make([]byte, base64.StdEncoding.EncodedLen(compressed.Len())) 42 | base64.StdEncoding.Encode(encoded, compressed.Bytes()) 43 | 44 | fmt.Fprintln(fi, "// Copyright (c) 2015 The btcsuite developers") 45 | fmt.Fprintln(fi, "// Use of this source code is governed by an ISC") 46 | fmt.Fprintln(fi, "// license that can be found in the LICENSE file.") 47 | fmt.Fprintln(fi) 48 | fmt.Fprintln(fi, "package btcec") 49 | fmt.Fprintln(fi) 50 | fmt.Fprintln(fi, "// Auto-generated file (see genprecomps.go)") 51 | fmt.Fprintln(fi, "// DO NOT EDIT") 52 | fmt.Fprintln(fi) 53 | fmt.Fprintf(fi, "var secp256k1BytePoints = %q\n", string(encoded)) 54 | 55 | a1, b1, a2, b2 := btcec.S256().EndomorphismVectors() 56 | fmt.Println("The following values are the computed linearly " + 57 | "independent vectors needed to make use of the secp256k1 " + 58 | "endomorphism:") 59 | fmt.Printf("a1: %x\n", a1) 60 | fmt.Printf("b1: %x\n", b1) 61 | fmt.Printf("a2: %x\n", a2) 62 | fmt.Printf("b2: %x\n", b2) 63 | } 64 | -------------------------------------------------------------------------------- /btcec/gensecp256k1.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | // This file is ignored during the regular build due to the following build tag. 6 | // This build tag is set during go generate. 7 | // +build gensecp256k1 8 | 9 | package btcec 10 | 11 | // References: 12 | // [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone) 13 | 14 | import ( 15 | "encoding/binary" 16 | "math/big" 17 | ) 18 | 19 | // secp256k1BytePoints are dummy points used so the code which generates the 20 | // real values can compile. 21 | var secp256k1BytePoints = "" 22 | 23 | // getDoublingPoints returns all the possible G^(2^i) for i in 24 | // 0..n-1 where n is the curve's bit size (256 in the case of secp256k1) 25 | // the coordinates are recorded as Jacobian coordinates. 26 | func (curve *KoblitzCurve) getDoublingPoints() [][3]fieldVal { 27 | doublingPoints := make([][3]fieldVal, curve.BitSize) 28 | 29 | // initialize px, py, pz to the Jacobian coordinates for the base point 30 | px, py := curve.bigAffineToField(curve.Gx, curve.Gy) 31 | pz := new(fieldVal).SetInt(1) 32 | for i := 0; i < curve.BitSize; i++ { 33 | doublingPoints[i] = [3]fieldVal{*px, *py, *pz} 34 | // P = 2*P 35 | curve.doubleJacobian(px, py, pz, px, py, pz) 36 | } 37 | return doublingPoints 38 | } 39 | 40 | // SerializedBytePoints returns a serialized byte slice which contains all of 41 | // the possible points per 8-bit window. This is used to when generating 42 | // secp256k1.go. 43 | func (curve *KoblitzCurve) SerializedBytePoints() []byte { 44 | doublingPoints := curve.getDoublingPoints() 45 | 46 | // Segregate the bits into byte-sized windows 47 | serialized := make([]byte, curve.byteSize*256*3*10*4) 48 | offset := 0 49 | for byteNum := 0; byteNum < curve.byteSize; byteNum++ { 50 | // Grab the 8 bits that make up this byte from doublingPoints. 51 | startingBit := 8 * (curve.byteSize - byteNum - 1) 52 | computingPoints := doublingPoints[startingBit : startingBit+8] 53 | 54 | // Compute all points in this window and serialize them. 55 | for i := 0; i < 256; i++ { 56 | px, py, pz := new(fieldVal), new(fieldVal), new(fieldVal) 57 | for j := 0; j < 8; j++ { 58 | if i>>uint(j)&1 == 1 { 59 | curve.addJacobian(px, py, pz, &computingPoints[j][0], 60 | &computingPoints[j][1], &computingPoints[j][2], px, py, pz) 61 | } 62 | } 63 | for i := 0; i < 10; i++ { 64 | binary.LittleEndian.PutUint32(serialized[offset:], px.n[i]) 65 | offset += 4 66 | } 67 | for i := 0; i < 10; i++ { 68 | binary.LittleEndian.PutUint32(serialized[offset:], py.n[i]) 69 | offset += 4 70 | } 71 | for i := 0; i < 10; i++ { 72 | binary.LittleEndian.PutUint32(serialized[offset:], pz.n[i]) 73 | offset += 4 74 | } 75 | } 76 | } 77 | 78 | return serialized 79 | } 80 | 81 | // sqrt returns the square root of the provided big integer using Newton's 82 | // method. It's only compiled and used during generation of pre-computed 83 | // values, so speed is not a huge concern. 84 | func sqrt(n *big.Int) *big.Int { 85 | // Initial guess = 2^(log_2(n)/2) 86 | guess := big.NewInt(2) 87 | guess.Exp(guess, big.NewInt(int64(n.BitLen()/2)), nil) 88 | 89 | // Now refine using Newton's method. 90 | big2 := big.NewInt(2) 91 | prevGuess := big.NewInt(0) 92 | for { 93 | prevGuess.Set(guess) 94 | guess.Add(guess, new(big.Int).Div(n, guess)) 95 | guess.Div(guess, big2) 96 | if guess.Cmp(prevGuess) == 0 { 97 | break 98 | } 99 | } 100 | return guess 101 | } 102 | 103 | // EndomorphismVectors runs the first 3 steps of algorithm 3.74 from [GECC] to 104 | // generate the linearly independent vectors needed to generate a balanced 105 | // length-two representation of a multiplier such that k = k1 + k2λ (mod N) and 106 | // returns them. Since the values will always be the same given the fact that N 107 | // and λ are fixed, the final results can be accelerated by storing the 108 | // precomputed values with the curve. 109 | func (curve *KoblitzCurve) EndomorphismVectors() (a1, b1, a2, b2 *big.Int) { 110 | bigMinus1 := big.NewInt(-1) 111 | 112 | // This section uses an extended Euclidean algorithm to generate a 113 | // sequence of equations: 114 | // s[i] * N + t[i] * λ = r[i] 115 | 116 | nSqrt := sqrt(curve.N) 117 | u, v := new(big.Int).Set(curve.N), new(big.Int).Set(curve.lambda) 118 | x1, y1 := big.NewInt(1), big.NewInt(0) 119 | x2, y2 := big.NewInt(0), big.NewInt(1) 120 | q, r := new(big.Int), new(big.Int) 121 | qu, qx1, qy1 := new(big.Int), new(big.Int), new(big.Int) 122 | s, t := new(big.Int), new(big.Int) 123 | ri, ti := new(big.Int), new(big.Int) 124 | a1, b1, a2, b2 = new(big.Int), new(big.Int), new(big.Int), new(big.Int) 125 | found, oneMore := false, false 126 | for u.Sign() != 0 { 127 | // q = v/u 128 | q.Div(v, u) 129 | 130 | // r = v - q*u 131 | qu.Mul(q, u) 132 | r.Sub(v, qu) 133 | 134 | // s = x2 - q*x1 135 | qx1.Mul(q, x1) 136 | s.Sub(x2, qx1) 137 | 138 | // t = y2 - q*y1 139 | qy1.Mul(q, y1) 140 | t.Sub(y2, qy1) 141 | 142 | // v = u, u = r, x2 = x1, x1 = s, y2 = y1, y1 = t 143 | v.Set(u) 144 | u.Set(r) 145 | x2.Set(x1) 146 | x1.Set(s) 147 | y2.Set(y1) 148 | y1.Set(t) 149 | 150 | // As soon as the remainder is less than the sqrt of n, the 151 | // values of a1 and b1 are known. 152 | if !found && r.Cmp(nSqrt) < 0 { 153 | // When this condition executes ri and ti represent the 154 | // r[i] and t[i] values such that i is the greatest 155 | // index for which r >= sqrt(n). Meanwhile, the current 156 | // r and t values are r[i+1] and t[i+1], respectively. 157 | 158 | // a1 = r[i+1], b1 = -t[i+1] 159 | a1.Set(r) 160 | b1.Mul(t, bigMinus1) 161 | found = true 162 | oneMore = true 163 | 164 | // Skip to the next iteration so ri and ti are not 165 | // modified. 166 | continue 167 | 168 | } else if oneMore { 169 | // When this condition executes ri and ti still 170 | // represent the r[i] and t[i] values while the current 171 | // r and t are r[i+2] and t[i+2], respectively. 172 | 173 | // sum1 = r[i]^2 + t[i]^2 174 | rSquared := new(big.Int).Mul(ri, ri) 175 | tSquared := new(big.Int).Mul(ti, ti) 176 | sum1 := new(big.Int).Add(rSquared, tSquared) 177 | 178 | // sum2 = r[i+2]^2 + t[i+2]^2 179 | r2Squared := new(big.Int).Mul(r, r) 180 | t2Squared := new(big.Int).Mul(t, t) 181 | sum2 := new(big.Int).Add(r2Squared, t2Squared) 182 | 183 | // if (r[i]^2 + t[i]^2) <= (r[i+2]^2 + t[i+2]^2) 184 | if sum1.Cmp(sum2) <= 0 { 185 | // a2 = r[i], b2 = -t[i] 186 | a2.Set(ri) 187 | b2.Mul(ti, bigMinus1) 188 | } else { 189 | // a2 = r[i+2], b2 = -t[i+2] 190 | a2.Set(r) 191 | b2.Mul(t, bigMinus1) 192 | } 193 | 194 | // All done. 195 | break 196 | } 197 | 198 | ri.Set(r) 199 | ti.Set(t) 200 | } 201 | 202 | return a1, b1, a2, b2 203 | } 204 | -------------------------------------------------------------------------------- /btcec/precompute.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package btcec 6 | 7 | import ( 8 | "compress/zlib" 9 | "encoding/base64" 10 | "encoding/binary" 11 | "io/ioutil" 12 | "strings" 13 | ) 14 | 15 | //go:generate go run -tags gensecp256k1 genprecomps.go 16 | 17 | // loadS256BytePoints decompresses and deserializes the pre-computed byte points 18 | // used to accelerate scalar base multiplication for the secp256k1 curve. This 19 | // approach is used since it allows the compile to use significantly less ram 20 | // and be performed much faster than it is with hard-coding the final in-memory 21 | // data structure. At the same time, it is quite fast to generate the in-memory 22 | // data structure at init time with this approach versus computing the table. 23 | func loadS256BytePoints() error { 24 | // There will be no byte points to load when generating them. 25 | bp := secp256k1BytePoints 26 | if len(bp) == 0 { 27 | return nil 28 | } 29 | 30 | // Decompress the pre-computed table used to accelerate scalar base 31 | // multiplication. 32 | decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(bp)) 33 | r, err := zlib.NewReader(decoder) 34 | if err != nil { 35 | return err 36 | } 37 | serialized, err := ioutil.ReadAll(r) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | // Deserialize the precomputed byte points and set the curve to them. 43 | offset := 0 44 | var bytePoints [32][256][3]fieldVal 45 | for byteNum := 0; byteNum < 32; byteNum++ { 46 | // All points in this window. 47 | for i := 0; i < 256; i++ { 48 | px := &bytePoints[byteNum][i][0] 49 | py := &bytePoints[byteNum][i][1] 50 | pz := &bytePoints[byteNum][i][2] 51 | for i := 0; i < 10; i++ { 52 | px.n[i] = binary.LittleEndian.Uint32(serialized[offset:]) 53 | offset += 4 54 | } 55 | for i := 0; i < 10; i++ { 56 | py.n[i] = binary.LittleEndian.Uint32(serialized[offset:]) 57 | offset += 4 58 | } 59 | for i := 0; i < 10; i++ { 60 | pz.n[i] = binary.LittleEndian.Uint32(serialized[offset:]) 61 | offset += 4 62 | } 63 | } 64 | } 65 | secp256k1.bytePoints = &bytePoints 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /btcec/privkey.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2016 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package btcec 6 | 7 | import ( 8 | "crypto/ecdsa" 9 | "crypto/elliptic" 10 | "crypto/rand" 11 | "math/big" 12 | ) 13 | 14 | // PrivateKey wraps an ecdsa.PrivateKey as a convenience mainly for signing 15 | // things with the the private key without having to directly import the ecdsa 16 | // package. 17 | type PrivateKey ecdsa.PrivateKey 18 | 19 | // PrivKeyFromBytes returns a private and public key for `curve' based on the 20 | // private key passed as an argument as a byte slice. 21 | func PrivKeyFromBytes(curve elliptic.Curve, pk []byte) (*PrivateKey, 22 | *PublicKey) { 23 | x, y := curve.ScalarBaseMult(pk) 24 | 25 | priv := &ecdsa.PrivateKey{ 26 | PublicKey: ecdsa.PublicKey{ 27 | Curve: curve, 28 | X: x, 29 | Y: y, 30 | }, 31 | D: new(big.Int).SetBytes(pk), 32 | } 33 | 34 | return (*PrivateKey)(priv), (*PublicKey)(&priv.PublicKey) 35 | } 36 | 37 | // NewPrivateKey is a wrapper for ecdsa.GenerateKey that returns a PrivateKey 38 | // instead of the normal ecdsa.PrivateKey. 39 | func NewPrivateKey(curve elliptic.Curve) (*PrivateKey, error) { 40 | key, err := ecdsa.GenerateKey(curve, rand.Reader) 41 | if err != nil { 42 | return nil, err 43 | } 44 | return (*PrivateKey)(key), nil 45 | } 46 | 47 | // PubKey returns the PublicKey corresponding to this private key. 48 | func (p *PrivateKey) PubKey() *PublicKey { 49 | return (*PublicKey)(&p.PublicKey) 50 | } 51 | 52 | // ToECDSA returns the private key as a *ecdsa.PrivateKey. 53 | func (p *PrivateKey) ToECDSA() *ecdsa.PrivateKey { 54 | return (*ecdsa.PrivateKey)(p) 55 | } 56 | 57 | // Sign generates an ECDSA signature for the provided hash (which should be the result 58 | // of hashing a larger message) using the private key. Produced signature 59 | // is deterministic (same message and same key yield the same signature) and canonical 60 | // in accordance with RFC6979 and BIP0062. 61 | func (p *PrivateKey) Sign(hash []byte) (*Signature, error) { 62 | return signRFC6979(p, hash) 63 | } 64 | 65 | // PrivKeyBytesLen defines the length in bytes of a serialized private key. 66 | const PrivKeyBytesLen = 32 67 | 68 | // Serialize returns the private key number d as a big-endian binary-encoded 69 | // number, padded to a length of 32 bytes. 70 | func (p *PrivateKey) Serialize() []byte { 71 | b := make([]byte, 0, PrivKeyBytesLen) 72 | return paddedAppend(PrivKeyBytesLen, b, p.ToECDSA().D.Bytes()) 73 | } 74 | -------------------------------------------------------------------------------- /btcec/privkey_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2016 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package btcec 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | ) 11 | 12 | func TestPrivKeys(t *testing.T) { 13 | tests := []struct { 14 | name string 15 | key []byte 16 | }{ 17 | { 18 | name: "check curve", 19 | key: []byte{ 20 | 0xea, 0xf0, 0x2c, 0xa3, 0x48, 0xc5, 0x24, 0xe6, 21 | 0x39, 0x26, 0x55, 0xba, 0x4d, 0x29, 0x60, 0x3c, 22 | 0xd1, 0xa7, 0x34, 0x7d, 0x9d, 0x65, 0xcf, 0xe9, 23 | 0x3c, 0xe1, 0xeb, 0xff, 0xdc, 0xa2, 0x26, 0x94, 24 | }, 25 | }, 26 | } 27 | 28 | for _, test := range tests { 29 | priv, pub := PrivKeyFromBytes(S256(), test.key) 30 | 31 | _, err := ParsePubKey(pub.SerializeUncompressed(), S256()) 32 | if err != nil { 33 | t.Errorf("%s privkey: %v", test.name, err) 34 | continue 35 | } 36 | 37 | hash := []byte{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9} 38 | sig, err := priv.Sign(hash) 39 | if err != nil { 40 | t.Errorf("%s could not sign: %v", test.name, err) 41 | continue 42 | } 43 | 44 | if !sig.Verify(hash, pub) { 45 | t.Errorf("%s could not verify: %v", test.name, err) 46 | continue 47 | } 48 | 49 | serializedKey := priv.Serialize() 50 | if !bytes.Equal(serializedKey, test.key) { 51 | t.Errorf("%s unexpected serialized bytes - got: %x, "+ 52 | "want: %x", test.name, serializedKey, test.key) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /btcec/pubkey.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2014 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package btcec 6 | 7 | import ( 8 | "crypto/ecdsa" 9 | "errors" 10 | "fmt" 11 | "math/big" 12 | ) 13 | 14 | // These constants define the lengths of serialized public keys. 15 | const ( 16 | PubKeyBytesLenCompressed = 33 17 | PubKeyBytesLenUncompressed = 65 18 | PubKeyBytesLenHybrid = 65 19 | ) 20 | 21 | func isOdd(a *big.Int) bool { 22 | return a.Bit(0) == 1 23 | } 24 | 25 | // decompressPoint decompresses a point on the secp256k1 curve given the X point and 26 | // the solution to use. 27 | func decompressPoint(curve *KoblitzCurve, bigX *big.Int, ybit bool) (*big.Int, error) { 28 | var x fieldVal 29 | x.SetByteSlice(bigX.Bytes()) 30 | 31 | // Compute x^3 + B mod p. 32 | var x3 fieldVal 33 | x3.SquareVal(&x).Mul(&x) 34 | x3.Add(curve.fieldB).Normalize() 35 | 36 | // Now calculate sqrt mod p of x^3 + B 37 | // This code used to do a full sqrt based on tonelli/shanks, 38 | // but this was replaced by the algorithms referenced in 39 | // https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294 40 | var y fieldVal 41 | y.SqrtVal(&x3).Normalize() 42 | if ybit != y.IsOdd() { 43 | y.Negate(1).Normalize() 44 | } 45 | 46 | // Check that y is a square root of x^3 + B. 47 | var y2 fieldVal 48 | y2.SquareVal(&y).Normalize() 49 | if !y2.Equals(&x3) { 50 | return nil, fmt.Errorf("invalid square root") 51 | } 52 | 53 | // Verify that y-coord has expected parity. 54 | if ybit != y.IsOdd() { 55 | return nil, fmt.Errorf("ybit doesn't match oddness") 56 | } 57 | 58 | return new(big.Int).SetBytes(y.Bytes()[:]), nil 59 | } 60 | 61 | const ( 62 | pubkeyCompressed byte = 0x2 // y_bit + x coord 63 | pubkeyUncompressed byte = 0x4 // x coord + y coord 64 | pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord 65 | ) 66 | 67 | // IsCompressedPubKey returns true the the passed serialized public key has 68 | // been encoded in compressed format, and false otherwise. 69 | func IsCompressedPubKey(pubKey []byte) bool { 70 | // The public key is only compressed if it is the correct length and 71 | // the format (first byte) is one of the compressed pubkey values. 72 | return len(pubKey) == PubKeyBytesLenCompressed && 73 | (pubKey[0]&^byte(0x1) == pubkeyCompressed) 74 | } 75 | 76 | // ParsePubKey parses a public key for a koblitz curve from a bytestring into a 77 | // ecdsa.Publickey, verifying that it is valid. It supports compressed, 78 | // uncompressed and hybrid signature formats. 79 | func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *PublicKey, err error) { 80 | pubkey := PublicKey{} 81 | pubkey.Curve = curve 82 | 83 | if len(pubKeyStr) == 0 { 84 | return nil, errors.New("pubkey string is empty") 85 | } 86 | 87 | format := pubKeyStr[0] 88 | ybit := (format & 0x1) == 0x1 89 | format &= ^byte(0x1) 90 | 91 | switch len(pubKeyStr) { 92 | case PubKeyBytesLenUncompressed: 93 | if format != pubkeyUncompressed && format != pubkeyHybrid { 94 | return nil, fmt.Errorf("invalid magic in pubkey str: "+ 95 | "%d", pubKeyStr[0]) 96 | } 97 | 98 | pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33]) 99 | pubkey.Y = new(big.Int).SetBytes(pubKeyStr[33:]) 100 | // hybrid keys have extra information, make use of it. 101 | if format == pubkeyHybrid && ybit != isOdd(pubkey.Y) { 102 | return nil, fmt.Errorf("ybit doesn't match oddness") 103 | } 104 | 105 | if pubkey.X.Cmp(pubkey.Curve.Params().P) >= 0 { 106 | return nil, fmt.Errorf("pubkey X parameter is >= to P") 107 | } 108 | if pubkey.Y.Cmp(pubkey.Curve.Params().P) >= 0 { 109 | return nil, fmt.Errorf("pubkey Y parameter is >= to P") 110 | } 111 | if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) { 112 | return nil, fmt.Errorf("pubkey isn't on secp256k1 curve") 113 | } 114 | 115 | case PubKeyBytesLenCompressed: 116 | // format is 0x2 | solution, 117 | // solution determines which solution of the curve we use. 118 | /// y^2 = x^3 + Curve.B 119 | if format != pubkeyCompressed { 120 | return nil, fmt.Errorf("invalid magic in compressed "+ 121 | "pubkey string: %d", pubKeyStr[0]) 122 | } 123 | pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33]) 124 | pubkey.Y, err = decompressPoint(curve, pubkey.X, ybit) 125 | if err != nil { 126 | return nil, err 127 | } 128 | 129 | default: // wrong! 130 | return nil, fmt.Errorf("invalid pub key length %d", 131 | len(pubKeyStr)) 132 | } 133 | 134 | return &pubkey, nil 135 | } 136 | 137 | // PublicKey is an ecdsa.PublicKey with additional functions to 138 | // serialize in uncompressed, compressed, and hybrid formats. 139 | type PublicKey ecdsa.PublicKey 140 | 141 | // ToECDSA returns the public key as a *ecdsa.PublicKey. 142 | func (p *PublicKey) ToECDSA() *ecdsa.PublicKey { 143 | return (*ecdsa.PublicKey)(p) 144 | } 145 | 146 | // SerializeUncompressed serializes a public key in a 65-byte uncompressed 147 | // format. 148 | func (p *PublicKey) SerializeUncompressed() []byte { 149 | b := make([]byte, 0, PubKeyBytesLenUncompressed) 150 | b = append(b, pubkeyUncompressed) 151 | b = paddedAppend(32, b, p.X.Bytes()) 152 | return paddedAppend(32, b, p.Y.Bytes()) 153 | } 154 | 155 | // SerializeCompressed serializes a public key in a 33-byte compressed format. 156 | func (p *PublicKey) SerializeCompressed() []byte { 157 | b := make([]byte, 0, PubKeyBytesLenCompressed) 158 | format := pubkeyCompressed 159 | if isOdd(p.Y) { 160 | format |= 0x1 161 | } 162 | b = append(b, format) 163 | return paddedAppend(32, b, p.X.Bytes()) 164 | } 165 | 166 | // SerializeHybrid serializes a public key in a 65-byte hybrid format. 167 | func (p *PublicKey) SerializeHybrid() []byte { 168 | b := make([]byte, 0, PubKeyBytesLenHybrid) 169 | format := pubkeyHybrid 170 | if isOdd(p.Y) { 171 | format |= 0x1 172 | } 173 | b = append(b, format) 174 | b = paddedAppend(32, b, p.X.Bytes()) 175 | return paddedAppend(32, b, p.Y.Bytes()) 176 | } 177 | 178 | // IsEqual compares this PublicKey instance to the one passed, returning true if 179 | // both PublicKeys are equivalent. A PublicKey is equivalent to another, if they 180 | // both have the same X and Y coordinate. 181 | func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool { 182 | return p.X.Cmp(otherPubKey.X) == 0 && 183 | p.Y.Cmp(otherPubKey.Y) == 0 184 | } 185 | 186 | // paddedAppend appends the src byte slice to dst, returning the new slice. 187 | // If the length of the source is smaller than the passed size, leading zero 188 | // bytes are appended to the dst slice before appending src. 189 | func paddedAppend(size uint, dst, src []byte) []byte { 190 | for i := 0; i < int(size)-len(src); i++ { 191 | dst = append(dst, 0) 192 | } 193 | return append(dst, src...) 194 | } 195 | -------------------------------------------------------------------------------- /btcec/pubkey_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013-2016 The btcsuite developers 2 | // Use of this source code is governed by an ISC 3 | // license that can be found in the LICENSE file. 4 | 5 | package btcec 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | ) 11 | 12 | type pubKeyTest struct { 13 | name string 14 | key []byte 15 | format byte 16 | isValid bool 17 | } 18 | 19 | var pubKeyTests = []pubKeyTest{ 20 | // pubkey from bitcoin blockchain tx 21 | // 0437cd7f8525ceed2324359c2d0ba26006d92d85 22 | { 23 | name: "uncompressed ok", 24 | key: []byte{0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 25 | 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 26 | 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 27 | 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 28 | 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 29 | 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 30 | 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 31 | 0xb4, 0x12, 0xa3, 32 | }, 33 | isValid: true, 34 | format: pubkeyUncompressed, 35 | }, 36 | { 37 | name: "uncompressed x changed", 38 | key: []byte{0x04, 0x15, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 39 | 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 40 | 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 41 | 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 42 | 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 43 | 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 44 | 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 45 | 0xb4, 0x12, 0xa3, 46 | }, 47 | isValid: false, 48 | }, 49 | { 50 | name: "uncompressed y changed", 51 | key: []byte{0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 52 | 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 53 | 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 54 | 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 55 | 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 56 | 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 57 | 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 58 | 0xb4, 0x12, 0xa4, 59 | }, 60 | isValid: false, 61 | }, 62 | { 63 | name: "uncompressed claims compressed", 64 | key: []byte{0x03, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 65 | 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 66 | 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 67 | 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 68 | 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 69 | 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 70 | 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 71 | 0xb4, 0x12, 0xa3, 72 | }, 73 | isValid: false, 74 | }, 75 | { 76 | name: "uncompressed as hybrid ok", 77 | key: []byte{0x07, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 78 | 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 79 | 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 80 | 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 81 | 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 82 | 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 83 | 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 84 | 0xb4, 0x12, 0xa3, 85 | }, 86 | isValid: true, 87 | format: pubkeyHybrid, 88 | }, 89 | { 90 | name: "uncompressed as hybrid wrong", 91 | key: []byte{0x06, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 92 | 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 93 | 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 94 | 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xb2, 0xe0, 95 | 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 96 | 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 97 | 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 98 | 0xb4, 0x12, 0xa3, 99 | }, 100 | isValid: false, 101 | }, 102 | // from tx 0b09c51c51ff762f00fb26217269d2a18e77a4fa87d69b3c363ab4df16543f20 103 | { 104 | name: "compressed ok (ybit = 0)", 105 | key: []byte{0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 106 | 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, 107 | 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, 108 | 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, 109 | }, 110 | isValid: true, 111 | format: pubkeyCompressed, 112 | }, 113 | // from tx fdeb8e72524e8dab0da507ddbaf5f88fe4a933eb10a66bc4745bb0aa11ea393c 114 | { 115 | name: "compressed ok (ybit = 1)", 116 | key: []byte{0x03, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33, 117 | 0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34, 118 | 0x25, 0x21, 0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4, 119 | 0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e, 120 | }, 121 | isValid: true, 122 | format: pubkeyCompressed, 123 | }, 124 | { 125 | name: "compressed claims uncompressed (ybit = 0)", 126 | key: []byte{0x04, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 127 | 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, 128 | 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, 129 | 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, 130 | }, 131 | isValid: false, 132 | }, 133 | { 134 | name: "compressed claims uncompressed (ybit = 1)", 135 | key: []byte{0x05, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33, 136 | 0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34, 137 | 0x25, 0x21, 0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4, 138 | 0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e, 139 | }, 140 | isValid: false, 141 | }, 142 | { 143 | name: "wrong length)", 144 | key: []byte{0x05}, 145 | isValid: false, 146 | }, 147 | { 148 | name: "X == P", 149 | key: []byte{0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 150 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 151 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 152 | 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F, 0xb2, 0xe0, 153 | 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 154 | 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 155 | 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 156 | 0xb4, 0x12, 0xa3, 157 | }, 158 | isValid: false, 159 | }, 160 | { 161 | name: "X > P", 162 | key: []byte{0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 163 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 164 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 165 | 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFD, 0x2F, 0xb2, 0xe0, 166 | 0xea, 0xdd, 0xfb, 0x84, 0xcc, 0xf9, 0x74, 0x44, 0x64, 167 | 0xf8, 0x2e, 0x16, 0x0b, 0xfa, 0x9b, 0x8b, 0x64, 0xf9, 168 | 0xd4, 0xc0, 0x3f, 0x99, 0x9b, 0x86, 0x43, 0xf6, 0x56, 169 | 0xb4, 0x12, 0xa3, 170 | }, 171 | isValid: false, 172 | }, 173 | { 174 | name: "Y == P", 175 | key: []byte{0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 176 | 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 177 | 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 178 | 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xFF, 0xFF, 179 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 180 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 181 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 182 | 0xFF, 0xFC, 0x2F, 183 | }, 184 | isValid: false, 185 | }, 186 | { 187 | name: "Y > P", 188 | key: []byte{0x04, 0x11, 0xdb, 0x93, 0xe1, 0xdc, 0xdb, 0x8a, 189 | 0x01, 0x6b, 0x49, 0x84, 0x0f, 0x8c, 0x53, 0xbc, 0x1e, 190 | 0xb6, 0x8a, 0x38, 0x2e, 0x97, 0xb1, 0x48, 0x2e, 0xca, 191 | 0xd7, 0xb1, 0x48, 0xa6, 0x90, 0x9a, 0x5c, 0xFF, 0xFF, 192 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 193 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 194 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 195 | 0xFF, 0xFD, 0x2F, 196 | }, 197 | isValid: false, 198 | }, 199 | { 200 | name: "hybrid", 201 | key: []byte{0x06, 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 202 | 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87, 0x0b, 0x07, 203 | 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 204 | 0xf2, 0x81, 0x5b, 0x16, 0xf8, 0x17, 0x98, 0x48, 0x3a, 205 | 0xda, 0x77, 0x26, 0xa3, 0xc4, 0x65, 0x5d, 0xa4, 0xfb, 206 | 0xfc, 0x0e, 0x11, 0x08, 0xa8, 0xfd, 0x17, 0xb4, 0x48, 207 | 0xa6, 0x85, 0x54, 0x19, 0x9c, 0x47, 0xd0, 0x8f, 0xfb, 208 | 0x10, 0xd4, 0xb8, 209 | }, 210 | format: pubkeyHybrid, 211 | isValid: true, 212 | }, 213 | } 214 | 215 | func TestPubKeys(t *testing.T) { 216 | for _, test := range pubKeyTests { 217 | pk, err := ParsePubKey(test.key, S256()) 218 | if err != nil { 219 | if test.isValid { 220 | t.Errorf("%s pubkey failed when shouldn't %v", 221 | test.name, err) 222 | } 223 | continue 224 | } 225 | if !test.isValid { 226 | t.Errorf("%s counted as valid when it should fail", 227 | test.name) 228 | continue 229 | } 230 | var pkStr []byte 231 | switch test.format { 232 | case pubkeyUncompressed: 233 | pkStr = (*PublicKey)(pk).SerializeUncompressed() 234 | case pubkeyCompressed: 235 | pkStr = (*PublicKey)(pk).SerializeCompressed() 236 | case pubkeyHybrid: 237 | pkStr = (*PublicKey)(pk).SerializeHybrid() 238 | } 239 | if !bytes.Equal(test.key, pkStr) { 240 | t.Errorf("%s pubkey: serialized keys do not match.", test.name) 241 | t.Logf("%x", test.key) 242 | t.Logf("%x", pkStr) 243 | } 244 | } 245 | } 246 | 247 | func TestPublicKeyIsEqual(t *testing.T) { 248 | pubKey1, err := ParsePubKey( 249 | []byte{0x03, 0x26, 0x89, 0xc7, 0xc2, 0xda, 0xb1, 0x33, 250 | 0x09, 0xfb, 0x14, 0x3e, 0x0e, 0x8f, 0xe3, 0x96, 0x34, 251 | 0x25, 0x21, 0x88, 0x7e, 0x97, 0x66, 0x90, 0xb6, 0xb4, 252 | 0x7f, 0x5b, 0x2a, 0x4b, 0x7d, 0x44, 0x8e, 253 | }, 254 | S256(), 255 | ) 256 | if err != nil { 257 | t.Fatalf("failed to parse raw bytes for pubKey1: %v", err) 258 | } 259 | 260 | pubKey2, err := ParsePubKey( 261 | []byte{0x02, 0xce, 0x0b, 0x14, 0xfb, 0x84, 0x2b, 0x1b, 262 | 0xa5, 0x49, 0xfd, 0xd6, 0x75, 0xc9, 0x80, 0x75, 0xf1, 263 | 0x2e, 0x9c, 0x51, 0x0f, 0x8e, 0xf5, 0x2b, 0xd0, 0x21, 264 | 0xa9, 0xa1, 0xf4, 0x80, 0x9d, 0x3b, 0x4d, 265 | }, 266 | S256(), 267 | ) 268 | if err != nil { 269 | t.Fatalf("failed to parse raw bytes for pubKey2: %v", err) 270 | } 271 | 272 | if !pubKey1.IsEqual(pubKey1) { 273 | t.Fatalf("value of IsEqual is incorrect, %v is "+ 274 | "equal to %v", pubKey1, pubKey1) 275 | } 276 | 277 | if pubKey1.IsEqual(pubKey2) { 278 | t.Fatalf("value of IsEqual is incorrect, %v is not "+ 279 | "equal to %v", pubKey1, pubKey2) 280 | } 281 | } 282 | 283 | func TestIsCompressed(t *testing.T) { 284 | for _, test := range pubKeyTests { 285 | isCompressed := IsCompressedPubKey(test.key) 286 | wantCompressed := (test.format == pubkeyCompressed) 287 | if isCompressed != wantCompressed { 288 | t.Fatalf("%s (%x) pubkey: unexpected compressed result, "+ 289 | "got %v, want %v", test.name, test.key, 290 | isCompressed, wantCompressed) 291 | } 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /cmd/ethutil-btc-vanity-gen/main.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | // https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses 4 | 5 | // 比特币荣耀地址生成器 6 | // 7 | // 使用例子: 8 | // 9 | // go run main.go 10 | // go run main.go -n=3 11 | // go run main.go -p=a 12 | // go run main.go -p=abc 13 | // go run main.go -p=a -s=bc 14 | // go run main.go -re="^a\d.*" 15 | // 16 | package main 17 | 18 | import ( 19 | "crypto/ecdsa" 20 | "crypto/rand" 21 | "crypto/sha256" 22 | "flag" 23 | "fmt" 24 | "math/big" 25 | "os" 26 | "regexp" 27 | "strings" 28 | 29 | "github.com/chai2010/ethutil" 30 | "github.com/chai2010/ethutil/base58" 31 | "golang.org/x/crypto/ripemd160" 32 | ) 33 | 34 | var ( 35 | flagPrefix = flag.String("p", "", "地址前缀, 必须是十六进制格式 ([0-9a-f]*)") 36 | flagSuffix = flag.String("s", "", "地址后缀, 必须是十六进制格式 ([0-9a-f]*)") 37 | flagRegexp = flag.String("re", "", "正则模式, 需要字节验证") 38 | flagPrefixPrv = flag.String("p-prv", "", "私钥前缀, 必须是十六进制格式 ([0-9a-f]*)") 39 | flagNumKey = flag.Int("n", 1, "生成几个地址") 40 | flagHelp = flag.Bool("h", false, "显示帮助") 41 | ) 42 | 43 | func init() { 44 | flag.Usage = func() { 45 | fmt.Println("比特币荣耀地址生成器") 46 | fmt.Println() 47 | 48 | fmt.Println(" ethutil-btc-vanity-gen # 生成1个地址") 49 | fmt.Println(" ethutil-btc-vanity-gen -n=3 # 生成3个地址") 50 | fmt.Println(" ethutil-btc-vanity-gen -p=a -s=bc # ab开头, c结尾") 51 | fmt.Println() 52 | 53 | fmt.Println("参数说明:") 54 | flag.PrintDefaults() 55 | fmt.Println() 56 | 57 | fmt.Println("https://github.com/chai2010/ethutil") 58 | fmt.Println("版权 @2019 柴树杉") 59 | } 60 | } 61 | 62 | func main() { 63 | flag.Parse() 64 | 65 | if *flagHelp { 66 | flag.Usage() 67 | os.Exit(0) 68 | } 69 | 70 | if s := *flagPrefix; s != "" { 71 | if !regexp.MustCompile("^[0-9a-f]*$").MatchString(string(s)) { 72 | fmt.Printf("非法的前缀参数: %q\n", s) 73 | os.Exit(1) 74 | } 75 | } 76 | if s := *flagSuffix; s != "" { 77 | if !regexp.MustCompile("^[0-9a-f]*$").MatchString(string(s)) { 78 | fmt.Printf("非法的后缀参数: %q\n", s) 79 | os.Exit(1) 80 | } 81 | } 82 | if s := *flagRegexp; s != "" { 83 | if _, err := regexp.Compile(s); err != nil { 84 | fmt.Printf("非法的正则参数: %q\n", s) 85 | os.Exit(1) 86 | } 87 | } 88 | 89 | for i := 0; i < *flagNumKey; i++ { 90 | key, addr := genVanityBtc(*flagPrefixPrv, *flagPrefix, *flagSuffix, *flagRegexp) 91 | fmt.Println(i, addr, key) 92 | } 93 | } 94 | 95 | func genVanityBtc(prefixPrv, prefix, suffix, reExpr string) (key, addr string) { 96 | fmt.Printf("prefixPrv=%q, prefix=%q, suffix=%q, reExpr=%q\n", prefixPrv, prefix, suffix, reExpr) 97 | 98 | if reExpr == "" { 99 | reExpr = ".*" 100 | } 101 | var re = regexp.MustCompile(reExpr) 102 | 103 | defer func() { 104 | if r := recover(); r != nil { 105 | fmt.Printf("key = %q, addr = %q\n", key, addr) 106 | panic(r) 107 | } 108 | }() 109 | 110 | for { 111 | k := NewBtcKey(true) 112 | key = k.KeyString() 113 | addr = k.AddressString() 114 | 115 | if strings.HasPrefix(key, prefixPrv) && 116 | strings.HasPrefix(addr[2:], prefix) && 117 | strings.HasSuffix(addr, suffix) && 118 | re.MatchString(addr[2:]) { 119 | return key, addr 120 | } 121 | } 122 | } 123 | 124 | type BtcKey struct { 125 | Compressed bool 126 | D *big.Int 127 | } 128 | 129 | func NewBtcKey(compressed bool) *BtcKey { 130 | key, err := ecdsa.GenerateKey(ethutil.S256(), rand.Reader) 131 | if err != nil { 132 | panic(err) 133 | } 134 | return &BtcKey{ 135 | Compressed: compressed, 136 | D: key.D, 137 | } 138 | } 139 | 140 | func (p *BtcKey) Key() *big.Int { 141 | return p.D 142 | } 143 | 144 | func (p *BtcKey) KeyBytes() []byte { 145 | if p.Compressed { 146 | return append(p.bigintPaddedBytes(p.D, 32), 0x01) 147 | } else { 148 | return p.bigintPaddedBytes(p.D, 32) 149 | } 150 | } 151 | 152 | func (p *BtcKey) KeyString() string { 153 | return base58.CheckEncode(p.KeyBytes(), 0x80) 154 | } 155 | 156 | func (p *BtcKey) PubKey() (x, y *big.Int) { 157 | var Kx, Ky = ethutil.S256().ScalarBaseMult(p.D.Bytes()) 158 | return Kx, Ky 159 | } 160 | func (p *BtcKey) PubKeyBytes() []byte { 161 | if p.Compressed { 162 | if x, y := p.PubKey(); y.Bit(0) == 0 { 163 | return append([]byte{0x02}, p.bigintPaddedBytes(x, 32)...) 164 | 165 | } else { 166 | return append([]byte{0x03}, p.bigintPaddedBytes(x, 32)...) 167 | } 168 | } else { 169 | x, y := p.PubKey() 170 | pubkeyBytes := []byte{0x04} 171 | pubkeyBytes = append(pubkeyBytes, p.bigintPaddedBytes(x, 32)...) 172 | pubkeyBytes = append(pubkeyBytes, p.bigintPaddedBytes(y, 32)...) 173 | return pubkeyBytes 174 | } 175 | } 176 | func (p *BtcKey) PubKeyString() string { 177 | return fmt.Sprintf("%x", p.PubKeyBytes()) 178 | } 179 | 180 | func (p *BtcKey) AddressString() string { 181 | var pubkeyBytes = p.PubKeyBytes() 182 | var addrBytes = p.ripemd160Hash(p.sha256Hash(pubkeyBytes)) 183 | return base58.CheckEncode(addrBytes, 0x00) 184 | } 185 | 186 | func (p *BtcKey) String() string { 187 | return p.AddressString() 188 | } 189 | 190 | func (p *BtcKey) ripemd160Hash(data []byte) []byte { 191 | h := ripemd160.New() 192 | h.Reset() 193 | h.Write(data) 194 | return h.Sum(nil) 195 | } 196 | 197 | func (p *BtcKey) sha256Hash(data []byte) []byte { 198 | h := sha256.New() 199 | h.Write(data) 200 | return h.Sum(nil) 201 | } 202 | 203 | // PaddedBigBytes encodes a big integer as a big-endian byte slice. The length 204 | // of the slice is at least n bytes. 205 | func (p *BtcKey) bigintPaddedBytes(bigint *big.Int, n int) []byte { 206 | if bigint.BitLen()/8 >= n { 207 | return bigint.Bytes() 208 | } 209 | ret := make([]byte, n) 210 | p.bigintReadBits(bigint, ret) 211 | return ret 212 | } 213 | 214 | // ReadBits encodes the absolute value of bigint as big-endian bytes. Callers must ensure 215 | // that buf has enough space. If buf is too short the result will be incomplete. 216 | func (p *BtcKey) bigintReadBits(bigint *big.Int, buf []byte) { 217 | const ( 218 | // number of bits in a big.Word 219 | wordBits = 32 << (uint64(^big.Word(0)) >> 63) 220 | // number of bytes in a big.Word 221 | wordBytes = wordBits / 8 222 | ) 223 | 224 | i := len(buf) 225 | for _, d := range bigint.Bits() { 226 | for j := 0; j < wordBytes && i > 0; j++ { 227 | buf[i-1] = byte(d) 228 | d >>= 8 229 | i-- 230 | } 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /cmd/ethutil-cat-keystore/get_passwd.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 chaishushan@gmail.com. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "os" 9 | ) 10 | 11 | // GetPasswd returns the password read from the terminal without echoing input. 12 | // The returned byte array does not include end-of-line characters. 13 | func GetPasswd() []byte { 14 | return getPasswd(false) 15 | } 16 | 17 | // GetPasswdMasked returns the password read from the terminal, echoing asterisks. 18 | // The returned byte array does not include end-of-line characters. 19 | func GetPasswdMasked() []byte { 20 | return getPasswd(true) 21 | } 22 | 23 | // getPasswd returns the input read from terminal. 24 | // If masked is true, typing will be matched by asterisks on the screen. 25 | // Otherwise, typing will echo nothing. 26 | func getPasswd(masked bool) []byte { 27 | var pass, bs, mask []byte 28 | if masked { 29 | bs = []byte("\b \b") 30 | mask = []byte("*") 31 | } 32 | 33 | for { 34 | if v := getch(); v == 127 || v == 8 { 35 | if l := len(pass); l > 0 { 36 | pass = pass[:l-1] 37 | os.Stdout.Write(bs) 38 | } 39 | } else if v == 13 || v == 10 { 40 | break 41 | } else { 42 | pass = append(pass, v) 43 | os.Stdout.Write(mask) 44 | } 45 | } 46 | println() 47 | return pass 48 | } 49 | -------------------------------------------------------------------------------- /cmd/ethutil-cat-keystore/get_passwd_darwin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 chaishushan@gmail.com. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !windows 6 | 7 | package main 8 | 9 | import ( 10 | "runtime" 11 | "syscall" 12 | "unsafe" 13 | ) 14 | 15 | // These constants are declared here, rather than importing 16 | // them from the syscall package as some syscall packages, even 17 | // on linux, for example gccgo, do not declare them. 18 | var ioctlReadTermios = func() uintptr { 19 | if runtime.GOOS == "linux" { 20 | return 0x5401 // syscall.TCGETS 21 | } else { 22 | return syscall.TIOCGETA 23 | } 24 | } 25 | var ioctlWriteTermios = func() uintptr { 26 | if runtime.GOOS == "linux" { 27 | return 0x5402 // syscall.TCSETS 28 | } else { 29 | return syscall.TIOCSETA 30 | } 31 | } 32 | 33 | // terminalState contains the state of a terminal. 34 | type terminalState struct { 35 | termios syscall.Termios 36 | } 37 | 38 | // terminalMakeRaw put the terminal connected to the given file descriptor into raw 39 | // mode and returns the previous state of the terminal so that it can be 40 | // restored. 41 | func terminalMakeRaw(fd int) (*terminalState, error) { 42 | var oldState terminalState 43 | if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios(), uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { 44 | return nil, err 45 | } 46 | 47 | newState := oldState.termios 48 | newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF 49 | newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG 50 | if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios(), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { 51 | return nil, err 52 | } 53 | 54 | return &oldState, nil 55 | } 56 | 57 | // terminalRestore restores the terminal connected to the given file descriptor to a 58 | // previous state. 59 | func terminalRestore(fd int, state *terminalState) error { 60 | _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios(), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) 61 | return err 62 | } 63 | 64 | func getch() byte { 65 | if oldState, err := terminalMakeRaw(0); err != nil { 66 | panic(err) 67 | } else { 68 | defer terminalRestore(0, oldState) 69 | } 70 | 71 | var buf [1]byte 72 | if n, err := syscall.Read(0, buf[:]); n == 0 || err != nil { 73 | panic(err) 74 | } 75 | return buf[0] 76 | } 77 | -------------------------------------------------------------------------------- /cmd/ethutil-cat-keystore/get_passwd_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 chaishushan@gmail.com. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // +build !windows 6 | 7 | package main 8 | 9 | import ( 10 | "syscall" 11 | "unsafe" 12 | ) 13 | 14 | // These constants are declared here, rather than importing 15 | // them from the syscall package as some syscall packages, even 16 | // on linux, for example gccgo, do not declare them. 17 | var ioctlReadTermios = func() uintptr { 18 | return 0x5401 // syscall.TCGETS 19 | } 20 | var ioctlWriteTermios = func() uintptr { 21 | return 0x5402 // syscall.TCSETS 22 | } 23 | 24 | // terminalState contains the state of a terminal. 25 | type terminalState struct { 26 | termios syscall.Termios 27 | } 28 | 29 | // terminalMakeRaw put the terminal connected to the given file descriptor into raw 30 | // mode and returns the previous state of the terminal so that it can be 31 | // restored. 32 | func terminalMakeRaw(fd int) (*terminalState, error) { 33 | var oldState terminalState 34 | if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios(), uintptr(unsafe.Pointer(&oldState.termios)), 0, 0, 0); err != 0 { 35 | return nil, err 36 | } 37 | 38 | newState := oldState.termios 39 | newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF 40 | newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG 41 | if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios(), uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 { 42 | return nil, err 43 | } 44 | 45 | return &oldState, nil 46 | } 47 | 48 | // terminalRestore restores the terminal connected to the given file descriptor to a 49 | // previous state. 50 | func terminalRestore(fd int, state *terminalState) error { 51 | _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios(), uintptr(unsafe.Pointer(&state.termios)), 0, 0, 0) 52 | return err 53 | } 54 | 55 | func getch() byte { 56 | if oldState, err := terminalMakeRaw(0); err != nil { 57 | panic(err) 58 | } else { 59 | defer terminalRestore(0, oldState) 60 | } 61 | 62 | var buf [1]byte 63 | if n, err := syscall.Read(0, buf[:]); n == 0 || err != nil { 64 | panic(err) 65 | } 66 | return buf[0] 67 | } 68 | -------------------------------------------------------------------------------- /cmd/ethutil-cat-keystore/get_passwd_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 chaishushan@gmail.com. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "syscall" 9 | "unicode/utf16" 10 | "unsafe" 11 | ) 12 | 13 | var ( 14 | modkernel32 = syscall.NewLazyDLL("kernel32.dll") 15 | procReadConsole = modkernel32.NewProc("ReadConsoleW") 16 | procGetConsoleMode = modkernel32.NewProc("GetConsoleMode") 17 | procSetConsoleMode = modkernel32.NewProc("SetConsoleMode") 18 | ) 19 | 20 | func getch() byte { 21 | var mode uint32 22 | pMode := &mode 23 | procGetConsoleMode.Call(uintptr(syscall.Stdin), uintptr(unsafe.Pointer(pMode))) 24 | 25 | var echoMode, lineMode uint32 26 | echoMode = 4 27 | lineMode = 2 28 | var newMode uint32 29 | newMode = mode ^ (echoMode | lineMode) 30 | 31 | procSetConsoleMode.Call(uintptr(syscall.Stdin), uintptr(newMode)) 32 | 33 | line := make([]uint16, 1) 34 | pLine := &line[0] 35 | var n uint16 36 | procReadConsole.Call( 37 | uintptr(syscall.Stdin), uintptr(unsafe.Pointer(pLine)), uintptr(len(line)), 38 | uintptr(unsafe.Pointer(&n)), 39 | ) 40 | 41 | // For some reason n returned seems to big by 2 (Null terminated maybe?) 42 | if n > 2 { 43 | n -= 2 44 | } 45 | 46 | b := []byte(string(utf16.Decode(line[:n]))) 47 | 48 | procSetConsoleMode.Call(uintptr(syscall.Stdin), uintptr(mode)) 49 | 50 | // Not sure how this could happen, but it did for someone 51 | if len(b) > 0 { 52 | return b[0] 53 | } else { 54 | return 13 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /cmd/ethutil-cat-keystore/main.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | // 以太坊KeyStore文件查看器 4 | // 5 | // 创建KeyStore文件步骤: 6 | // 1. 准备好私钥, 保存到key.txt文件 7 | // 2. 执行 geth account import --keystore `pwd` key.txt 8 | // 3. 通过步骤2中输入的密码可以查看生成的文件 9 | // 4. 私钥可以使用 工具生成 10 | package main 11 | 12 | import ( 13 | "flag" 14 | "fmt" 15 | "io/ioutil" 16 | "os" 17 | "path/filepath" 18 | 19 | "github.com/chai2010/ethutil" 20 | ) 21 | 22 | var ( 23 | flagPassword = flag.String("password", "", "设置KeyStore文件的密码(默认手工输入)") 24 | flagGlobMatch = flag.Bool("glob-match", false, "采用Glob模式匹配文件名") 25 | flagShowKey = flag.Bool("show-key", false, "显示私钥") 26 | flagHelp = flag.Bool("h", false, "显示帮助") 27 | ) 28 | 29 | func init() { 30 | flag.Usage = func() { 31 | fmt.Println("以太坊KeyStore文件查看器") 32 | fmt.Println() 33 | 34 | fmt.Println(" # 指定文件名") 35 | fmt.Println(" ethutil-cat-keystore file.json") 36 | fmt.Println(" ethutil-cat-keystore file.json file2.json") 37 | fmt.Println() 38 | 39 | fmt.Println(" # 显示私钥") 40 | fmt.Println(" ethutil-cat-keystore -show-key file.json") 41 | fmt.Println(" ethutil-cat-keystore -show-key file.json file2.json") 42 | fmt.Println() 43 | 44 | fmt.Println(" # Glob模式匹配") 45 | fmt.Println(" ethutil-cat-keystore -glob-match file?.json") 46 | fmt.Println(" ethutil-cat-keystore -glob-match file*.json") 47 | fmt.Println() 48 | 49 | fmt.Println("参数说明:") 50 | flag.PrintDefaults() 51 | fmt.Println() 52 | 53 | fmt.Println("https://github.com/chai2010/ethutil") 54 | fmt.Println("版权 @2019 柴树杉") 55 | } 56 | } 57 | 58 | func main() { 59 | flag.Parse() 60 | 61 | // 显示帮助信息 62 | if *flagHelp || len(os.Args) < 2 { 63 | flag.Usage() 64 | os.Exit(0) 65 | } 66 | 67 | // 指定输入文件名 68 | if flag.NArg() == 0 { 69 | fmt.Fprintf(os.Stderr, "错误: 缺少文件名!\n") 70 | os.Exit(-1) 71 | } 72 | 73 | // 读取文件名列表 74 | var files = flag.Args() 75 | 76 | // Glob模式展开文件列表 77 | if *flagGlobMatch { 78 | files = []string{} 79 | for _, pattern := range flag.Args() { 80 | matches, err := filepath.Glob(pattern) 81 | if err != nil { 82 | fmt.Fprintf(os.Stderr, "错误: %v\n", err) 83 | os.Exit(-1) 84 | } 85 | files = append(files, matches...) 86 | } 87 | } 88 | 89 | // 手工输入密码 90 | if *flagPassword == "" { 91 | fmt.Printf("输入密码: ") 92 | *flagPassword = string(GetPasswdMasked()) 93 | if len(*flagPassword) <= 0 { 94 | fmt.Fprintf(os.Stderr, "错误: 密码不能为空!\n") 95 | os.Exit(-1) 96 | } 97 | } 98 | 99 | // 处理全部的ks文件 100 | for _, s := range files { 101 | // 读取ks文件 102 | keyjson, err := ioutil.ReadFile(s) 103 | if err != nil { 104 | fmt.Fprintf(os.Stderr, "错误: 读文件 %q 失败: %v\n", s, err) 105 | os.Exit(-1) 106 | } 107 | 108 | // 解码ks文件 109 | uuid, privateKey, err := ethutil.KeyStoreDecryptKey( 110 | []byte(keyjson), *flagPassword, 111 | ) 112 | if err != nil { 113 | fmt.Fprintf(os.Stderr, "错误: 解密 %q 失败: %v\n", s, err) 114 | os.Exit(-1) 115 | } 116 | 117 | if *flagShowKey { 118 | fmt.Printf(" \n", uuid, 119 | ethutil.GenAddressFromPrivateKey(privateKey), privateKey, 120 | filepath.Clean(s), 121 | ) 122 | } else { 123 | fmt.Printf(" \n", uuid, 124 | ethutil.GenAddressFromPrivateKey(privateKey), 125 | filepath.Clean(s), 126 | ) 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /cmd/ethutil-key2address/main.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package main 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "io/ioutil" 9 | "os" 10 | 11 | "github.com/chai2010/ethutil" 12 | ) 13 | 14 | func main() { 15 | if len(os.Args) < 2 || os.Args[1] == "-h" { 16 | fmt.Println("Usage: ethutil-key2address key.file") 17 | fmt.Println(" ethutil-key2address -h") 18 | fmt.Println() 19 | 20 | fmt.Println("https://github.com/chai2010/ethutil") 21 | fmt.Println("版权 @2019 柴树杉") 22 | 23 | os.Exit(0) 24 | } 25 | 26 | for i := 1; i < len(os.Args); i++ { 27 | // 读取key文件 28 | keydata, err := ioutil.ReadFile(os.Args[i]) 29 | if err != nil { 30 | fmt.Fprintf(os.Stderr, "错误: 读文件 %q 失败: %v\n", os.Args[i], err) 31 | os.Exit(-1) 32 | } 33 | 34 | // 去掉开头和结尾的空白部分 35 | keydata = bytes.TrimSpace(keydata) 36 | 37 | // 输出对应的地址 38 | fmt.Println(ethutil.GenAddressFromPrivateKey(string(keydata))) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cmd/ethutil-vanity-gen/README.md: -------------------------------------------------------------------------------- 1 | # 以太坊荣耀地址生成器 2 | 3 | 下载安装: 4 | 5 | ``` 6 | $ go get github.com/chai2010/ethutil/cmd/ethutil-vanity-gen 7 | ``` 8 | 9 | 查看帮助: 10 | 11 | ``` 12 | $ ethutil-vanity-gen 13 | Usage of ethutil-vanity-gen: 14 | -n int 15 | 生成几个地址 (default 1) 16 | -p string 17 | 地址前缀, 必须是十六进制格式 ([0-9a-f]*) 18 | -s string 19 | 地址后缀, 必须是十六进制格式 ([0-9a-f]*) 20 | ``` 21 | 22 | 随机生成1个地址: 23 | 24 | ``` 25 | $ ethutil-vanity-gen 26 | 0 0xdc8f886f0d002f99b35062809a1305096b55db25 f44a0541a89272c22f996aa9f07d431f... 27 | ``` 28 | 29 | 随机生成3个地址: 30 | 31 | ``` 32 | $ ethutil-vanity-gen -n=3 33 | 34 | 0 0xfd0c05647bc8ef3ec7f6a9d5de9141d6e268fe57 772b7e71d291d3ba01fc8d7b09f6bcf9... 35 | 1 0x8ac4c30d1cb8abd2bc5de7780b53bf49c19ab468 fb5365f4f3d3a89c1c43b8802b6e6c61... 36 | 2 0x3c5f928683c22504385bd66d4797a46055289ebb 0fb1d6a356758e43bf41f2b09a6fbad4... 37 | ``` 38 | 39 | 生成2个以 `ab` 开头的地址: 40 | 41 | ``` 42 | $ ethutil-vanity-gen -p=ab -n=2 43 | 0 0xab5e3bae6d17af2619c9984e8d49b77ef237111f fb80a1818249789e696f5c400a359210... 44 | 1 0xab7bb7361d373188251a54405cf38075b96f23fd 4518dffcf27e185c8d32c653d4f11573... 45 | ``` 46 | 47 | 生成1个以`ab`开头以`c`结尾的地址: 48 | 49 | ``` 50 | $ ethutil-vanity-gen -p=a -s=bc 51 | 0 0xab0caf3f7d6041ab91f7988791d217df83fa049c e955552c0d6ef9680ca0fb84fc356ca9... 52 | ``` 53 | 54 | 正则模式: 55 | 56 | ``` 57 | $ ethutil-vanity-gen -re="^a\d.*" 58 | ``` 59 | 60 | ## `abcdef`组成的单词表(37个) 61 | 62 | http://www.wordaxis.com/advanced-anagram/words-within 63 | 64 | ``` 65 | ab 66 | abed 67 | ace 68 | aced 69 | ad 70 | ae 71 | ba 72 | bad 73 | bade 74 | be 75 | bead 76 | bed 77 | cab 78 | cad 79 | cade 80 | cafe 81 | da 82 | dab 83 | dace 84 | dae 85 | de 86 | deaf 87 | deb 88 | decaf 89 | def 90 | ea 91 | ecad 92 | ed 93 | ef 94 | fa 95 | fab 96 | face 97 | faced 98 | fad 99 | fade 100 | fe 101 | fed 102 | ``` 103 | 104 | 数字中和`12590`可对应`lzsqo`扩展的单词表, 可以对应454个单词([lzsqo.md](lzsqo.md)). 105 | 106 | -------------------------------------------------------------------------------- /cmd/ethutil-vanity-gen/lzsqo.md: -------------------------------------------------------------------------------- 1 | ## `abcdef`+`lzsqo`组成的单词表(454个) 2 | 3 | 数字中和`12590`对应`lzsqo`扩展的单词表: 4 | 5 | ``` 6 | ab 7 | abed 8 | able 9 | abled 10 | ables 11 | abo 12 | abode 13 | abodes 14 | abos 15 | abs 16 | ace 17 | aced 18 | aces 19 | acold 20 | ad 21 | ado 22 | adobe 23 | adobes 24 | ados 25 | adoze 26 | ads 27 | adz 28 | adze 29 | adzes 30 | ae 31 | aesc 32 | al 33 | alb 34 | albe 35 | albedo 36 | albedos 37 | albs 38 | aldose 39 | ale 40 | alec 41 | alecs 42 | alef 43 | alefs 44 | ales 45 | alod 46 | alods 47 | aloe 48 | aloed 49 | aloes 50 | als 51 | also 52 | as 53 | azo 54 | azole 55 | azoles 56 | ba 57 | bad 58 | bade 59 | bads 60 | bael 61 | baels 62 | bal 63 | bald 64 | balds 65 | bale 66 | baled 67 | bales 68 | bals 69 | bas 70 | base 71 | based 72 | be 73 | bead 74 | beads 75 | bed 76 | beds 77 | bel 78 | bels 79 | bes 80 | bez 81 | blad 82 | blade 83 | blades 84 | blads 85 | blae 86 | blaes 87 | blase 88 | blaze 89 | blazed 90 | blazes 91 | bled 92 | bloc 93 | blocs 94 | bo 95 | boa 96 | boas 97 | bod 98 | bode 99 | bodes 100 | bodle 101 | bodles 102 | bods 103 | bola 104 | bolas 105 | bold 106 | boldface 107 | boldfaces 108 | bolds 109 | bole 110 | boles 111 | bos 112 | cab 113 | cable 114 | cabled 115 | cables 116 | cabs 117 | cad 118 | cade 119 | cades 120 | cads 121 | cafe 122 | cafes 123 | calf 124 | calfs 125 | calo 126 | calos 127 | case 128 | cased 129 | ceas 130 | cel 131 | cels 132 | clad 133 | clade 134 | clades 135 | clads 136 | claes 137 | clef 138 | clefs 139 | clod 140 | clods 141 | close 142 | closed 143 | cloze 144 | clozes 145 | coal 146 | coaled 147 | coals 148 | cob 149 | coble 150 | cobles 151 | cobs 152 | cobza 153 | cobzas 154 | cod 155 | coda 156 | codable 157 | codas 158 | code 159 | codes 160 | cods 161 | coed 162 | coeds 163 | col 164 | cola 165 | colas 166 | cold 167 | colds 168 | cole 169 | colead 170 | coleads 171 | coled 172 | coles 173 | cols 174 | colza 175 | colzas 176 | cos 177 | cose 178 | cosed 179 | coz 180 | coze 181 | cozed 182 | cozes 183 | da 184 | dab 185 | dabs 186 | dace 187 | daces 188 | dae 189 | daes 190 | dal 191 | dale 192 | dales 193 | dals 194 | das 195 | daze 196 | dazes 197 | de 198 | deaf 199 | deal 200 | deals 201 | deb 202 | debs 203 | decaf 204 | decafs 205 | decal 206 | decals 207 | deco 208 | decos 209 | def 210 | del 211 | delf 212 | delfs 213 | dels 214 | do 215 | doab 216 | doable 217 | doabs 218 | dob 219 | dobla 220 | doblas 221 | dobs 222 | doc 223 | docs 224 | doe 225 | does 226 | dol 227 | dolce 228 | dolces 229 | dole 230 | doles 231 | dols 232 | dos 233 | dose 234 | doze 235 | dozes 236 | dso 237 | dzo 238 | dzos 239 | ea 240 | eas 241 | ecad 242 | ecads 243 | eco 244 | ecod 245 | ed 246 | eds 247 | ef 248 | efs 249 | el 250 | eld 251 | elds 252 | elf 253 | elfs 254 | els 255 | es 256 | fa 257 | fab 258 | fable 259 | fabled 260 | fables 261 | fabs 262 | face 263 | faced 264 | faces 265 | fad 266 | fade 267 | fades 268 | fado 269 | fados 270 | fads 271 | falces 272 | false 273 | falsed 274 | fas 275 | faze 276 | fazed 277 | fazes 278 | fe 279 | feal 280 | feals 281 | fecal 282 | fed 283 | feds 284 | feod 285 | feodal 286 | feods 287 | fes 288 | fez 289 | flab 290 | flabs 291 | flea 292 | fleas 293 | fled 294 | floc 295 | flocs 296 | floe 297 | floes 298 | foal 299 | foaled 300 | foals 301 | fob 302 | fobs 303 | focal 304 | foe 305 | foes 306 | fold 307 | folds 308 | la 309 | lab 310 | labs 311 | lac 312 | lace 313 | laced 314 | laces 315 | lacs 316 | lad 317 | lade 318 | lades 319 | lads 320 | las 321 | lase 322 | lased 323 | laze 324 | lazed 325 | lazes 326 | lazo 327 | lazoed 328 | lazoes 329 | lazos 330 | lea 331 | lead 332 | leads 333 | leaf 334 | leafs 335 | leas 336 | led 337 | les 338 | lesbo 339 | lez 340 | lo 341 | load 342 | loads 343 | loaf 344 | loafed 345 | loafs 346 | lob 347 | lobe 348 | lobed 349 | lobes 350 | lobs 351 | loca 352 | lode 353 | lodes 354 | los 355 | lose 356 | losed 357 | oaf 358 | oafs 359 | ob 360 | oba 361 | obas 362 | obe 363 | obes 364 | obs 365 | oca 366 | ocas 367 | od 368 | oda 369 | odal 370 | odals 371 | odas 372 | ode 373 | odea 374 | odes 375 | ods 376 | oe 377 | oes 378 | of 379 | old 380 | olde 381 | olds 382 | ole 383 | olea 384 | oles 385 | os 386 | ose 387 | sab 388 | sabe 389 | sabed 390 | sable 391 | sabled 392 | sac 393 | sad 394 | sade 395 | sae 396 | safe 397 | safed 398 | sal 399 | sale 400 | saz 401 | scab 402 | scad 403 | scald 404 | scale 405 | scaled 406 | sco 407 | scoed 408 | scold 409 | sea 410 | seal 411 | sec 412 | seco 413 | sed 414 | sel 415 | seld 416 | self 417 | sez 418 | slab 419 | slade 420 | slae 421 | sleazo 422 | sled 423 | slob 424 | sloe 425 | so 426 | sob 427 | soc 428 | soca 429 | socle 430 | sod 431 | soda 432 | sofa 433 | sofabed 434 | sol 435 | sola 436 | solace 437 | solaced 438 | sold 439 | solde 440 | sole 441 | soled 442 | solfa 443 | solfaed 444 | za 445 | zas 446 | zea 447 | zeal 448 | zeals 449 | zeas 450 | zed 451 | zeds 452 | zel 453 | zels 454 | zo 455 | zoa 456 | zoea 457 | zoeal 458 | zoeas 459 | zos 460 | ``` 461 | 462 | -------------------------------------------------------------------------------- /cmd/ethutil-vanity-gen/main.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | // 生成荣耀地址 4 | // 5 | // 使用例子: 6 | // 7 | // go run main.go 8 | // go run main.go -n=3 9 | // go run main.go -p=a 10 | // go run main.go -p=abc 11 | // go run main.go -p=a -s=bc 12 | // go run main.go -re="^a\d.*" 13 | // 14 | package main 15 | 16 | import ( 17 | "flag" 18 | "fmt" 19 | "os" 20 | "regexp" 21 | "strings" 22 | 23 | "github.com/chai2010/ethutil" 24 | ) 25 | 26 | var ( 27 | flagPrefix = flag.String("p", "", "地址前缀, 必须是十六进制格式 ([0-9a-f]*)") 28 | flagSuffix = flag.String("s", "", "地址后缀, 必须是十六进制格式 ([0-9a-f]*)") 29 | flagRegexp = flag.String("re", "", "正则模式, 需要字节验证") 30 | flagPrefixPrv = flag.String("p-prv", "", "私钥前缀, 必须是十六进制格式 ([0-9a-f]*)") 31 | flagNumKey = flag.Int("n", 1, "生成几个地址") 32 | flagHelp = flag.Bool("h", false, "显示帮助") 33 | ) 34 | 35 | func init() { 36 | flag.Usage = func() { 37 | fmt.Println("以太坊荣耀地址生成器") 38 | fmt.Println() 39 | 40 | fmt.Println(" ethutil-vanity-gen # 生成1个地址") 41 | fmt.Println(" ethutil-vanity-gen -n=3 # 生成3个地址") 42 | fmt.Println(" ethutil-vanity-gen -p=a -s=bc # ab开头, c结尾") 43 | fmt.Println() 44 | 45 | fmt.Println("参数说明:") 46 | flag.PrintDefaults() 47 | fmt.Println() 48 | 49 | fmt.Println("https://github.com/chai2010/ethutil") 50 | fmt.Println("版权 @2019 柴树杉") 51 | } 52 | } 53 | 54 | func main() { 55 | flag.Parse() 56 | 57 | if *flagHelp { 58 | flag.Usage() 59 | os.Exit(0) 60 | } 61 | 62 | if s := *flagPrefix; s != "" { 63 | if !regexp.MustCompile("^[0-9a-f]*$").MatchString(string(s)) { 64 | fmt.Printf("非法的前缀参数: %q\n", s) 65 | os.Exit(1) 66 | } 67 | } 68 | if s := *flagSuffix; s != "" { 69 | if !regexp.MustCompile("^[0-9a-f]*$").MatchString(string(s)) { 70 | fmt.Printf("非法的后缀参数: %q\n", s) 71 | os.Exit(1) 72 | } 73 | } 74 | if s := *flagRegexp; s != "" { 75 | if _, err := regexp.Compile(s); err != nil { 76 | fmt.Printf("非法的正则参数: %q\n", s) 77 | os.Exit(1) 78 | } 79 | } 80 | 81 | for i := 0; i < *flagNumKey; i++ { 82 | key, addr := genVanityEth(*flagPrefixPrv, *flagPrefix, *flagSuffix, *flagRegexp) 83 | fmt.Println(i, addr, key) 84 | } 85 | } 86 | 87 | func genVanityEth(prefixPrv, prefix, suffix, reExpr string) (key, addr string) { 88 | fmt.Printf("prefixPrv=%q, prefix=%q, suffix=%q, reExpr=%q\n", prefixPrv, prefix, suffix, reExpr) 89 | 90 | if reExpr == "" { 91 | reExpr = ".*" 92 | } 93 | var re = regexp.MustCompile(reExpr) 94 | 95 | defer func() { 96 | if r := recover(); r != nil { 97 | fmt.Printf("key = %q, addr = %q\n", key, addr) 98 | panic(r) 99 | } 100 | }() 101 | 102 | for { 103 | key = ethutil.GenPrivateKey() 104 | addr = ethutil.GenAddressFromPrivateKey(key) 105 | 106 | if strings.HasPrefix(key, prefixPrv) && 107 | strings.HasPrefix(addr[2:], prefix) && 108 | strings.HasSuffix(addr, suffix) && 109 | re.MatchString(addr[2:]) { 110 | return 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /ethutil.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | // ♦Ξ♦ 以太坊工具箱(零第三方库依赖) ♦Ξ♦ 4 | package ethutil 5 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | module github.com/chai2010/ethutil 4 | 5 | go 1.13 6 | 7 | require golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1 8 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= 2 | github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw= 3 | github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= 4 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= 5 | github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= 6 | github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= 7 | github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= 8 | github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= 9 | github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= 10 | github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= 11 | github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 12 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 13 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 14 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 15 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 16 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 17 | github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 18 | github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= 19 | github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= 20 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 21 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 22 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 23 | golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 24 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 25 | golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1 h1:anGSYQpPhQwXlwsu5wmfq0nWkCNaMEMUwAv13Y92hd8= 26 | golang.org/x/crypto v0.0.0-20191128160524-b544559bb6d1/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 27 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 28 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 29 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 30 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 31 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 32 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 33 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 34 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 35 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 36 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 37 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 38 | -------------------------------------------------------------------------------- /hello.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | // +build ignore 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "strings" 11 | 12 | "github.com/chai2010/ethutil" 13 | ) 14 | 15 | func main() { 16 | var password = "testpassword" 17 | var uuid = "3198bc9c-6672-5ab3-d995-4942343ae5b6" 18 | var privateKey = "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d" 19 | keyjson, err := ethutil.KeyStoreEncryptKey(uuid, privateKey, password) 20 | if err != nil { 21 | log.Fatal(err) 22 | } 23 | 24 | uuid, privateKey, err = ethutil.KeyStoreDecryptKey( 25 | keyjson, password, 26 | ) 27 | if err != nil { 28 | log.Fatal(err) 29 | } 30 | 31 | fmt.Println(uuid) 32 | fmt.Println(privateKey) 33 | fmt.Println(string(keyjson)) 34 | 35 | fmt.Println(ethutil.Hex("%x", genTxData1().Hash())) 36 | fmt.Println(ethutil.Hex("%x", genTxData2().Hash())) 37 | 38 | var sgiData = "0xec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080" 39 | fmt.Println(ethutil.Keccak256Hash(ethutil.Hex(sgiData).MustBytes())) 40 | 41 | // 0xec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080 42 | fmt.Println(ethutil.Hex("%x", genTxData2().SigData())) 43 | //fmt.Println(ethutil.Hex("%x", genTxData2().SigDataEIP155())) 44 | 45 | } 46 | 47 | 48 | // 精通以太坊例子 49 | // Tx Hash: 0xaa7f03f9f4e52fcf69f836a6d2bbc7706580adce0a068ff6525ba337218e6992 50 | func genTxData1() *ethutil.TxData { 51 | var tx = ðutil.TxData{} 52 | { 53 | tx.Nonce = 0 54 | tx.GasPrice = "0x09184e72a000" 55 | tx.GasLimit = "0x30000" 56 | tx.To = "0xb0920c523d582040f2bcb1bd7fb1c7c1ecebdb34" 57 | tx.Value = "0" 58 | } 59 | return tx 60 | } 61 | 62 | // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md 63 | func genTxData2() *ethutil.TxData { 64 | var tx = ðutil.TxData{} 65 | { 66 | tx.Nonce = 9 67 | tx.GasPrice = "20"+strings.Repeat("0", 9) 68 | tx.GasLimit = "21000" 69 | tx.To = "0x3535353535353535353535353535353535353535" 70 | tx.Value = "1"+strings.Repeat("0", 18) 71 | } 72 | return tx 73 | } 74 | 75 | /* 76 | Consider a transaction with nonce = 9, gasprice = 20 * 10**9, startgas = 21000, to = 0x3535353535353535353535353535353535353535, value = 10**18, data='' 77 | */ -------------------------------------------------------------------------------- /hex.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import ( 6 | "encoding/hex" 7 | "fmt" 8 | "log" 9 | "math/big" 10 | "regexp" 11 | "strconv" 12 | ) 13 | 14 | // 十六进制字符串 15 | type HexString string 16 | 17 | // 生成十六进制字符串 18 | // 类型 fmt.Sprintf 函数, 但是只有一个参数时只做转型操作 19 | func Hex(format string, a ...interface{}) HexString { 20 | if len(a) > 0 { 21 | return HexString(fmt.Sprintf(format, a...)) 22 | } 23 | return HexString(format) 24 | } 25 | 26 | // 从整数生成 27 | func HexFromInt(x int64) HexString { 28 | return HexString(fmt.Sprintf("%x", x)) 29 | } 30 | 31 | // 从大整数生成 32 | func HexFromBigint(x *big.Int) HexString { 33 | return HexString(fmt.Sprintf("%x", x)) 34 | } 35 | 36 | // 返回Wei(忽略益处情况) 37 | func (s HexString) Wei() int64 { 38 | return s.MustInt() 39 | } 40 | 41 | // 返回GWei 42 | // quo是整数部分, rem是余数部分 43 | func (s HexString) GWei() (quo, rem int64) { 44 | var x = s.MustBigint() 45 | var y = big.NewInt(GWei) 46 | quo = new(big.Int).Div(x, y).Int64() 47 | rem = new(big.Int).Mod(x, y).Int64() 48 | return 49 | } 50 | 51 | // 返回Ether 52 | // quo是整数部分, rem是余数部分 53 | func (s HexString) Ether() (quo, rem int64) { 54 | var x = s.MustBigint() 55 | var y = big.NewInt(Ether) 56 | quo = new(big.Int).Div(x, y).Int64() 57 | rem = new(big.Int).Mod(x, y).Int64() 58 | return 59 | } 60 | 61 | // 是否为零 62 | func (s HexString) IsZero() bool { 63 | re := regexp.MustCompile("^(0[xX])?[0]*$") 64 | return re.MatchString(string(s)) 65 | } 66 | 67 | // 是否为有效 68 | func (s HexString) IsValid() bool { 69 | re := regexp.MustCompile("^(0[xX])?[0-9a-fA-F]*$") 70 | return re.MatchString(string(s)) 71 | } 72 | 73 | // 是否为以太坊地址 74 | func (s HexString) IsValidAddress() bool { 75 | re := regexp.MustCompile("^(0[xX])?[0-9a-fA-F]{40}$") 76 | return re.MatchString(string(s)) 77 | } 78 | 79 | // 是否为有效下私钥格式 80 | // 私钥必须是十六进制格式, 开头的0x可选 81 | // 没有检查超出素数P的情况 82 | func (s HexString) IsValidPrivateKey() bool { 83 | re := regexp.MustCompile("^(0[xX])?[0-9a-fA-F]{64}$") 84 | return re.MatchString(string(s)) 85 | } 86 | 87 | // 是否为有效下公钥格式 88 | // 公钥必须是十六进制格式, 开头的0x可选 89 | // 不计0x开头, 公钥的十六进制格式为130个字节 90 | // 公钥开头的04表示未压缩点, 是以太坊唯一的格式 91 | func (s HexString) IsValidPublicKey() bool { 92 | re := regexp.MustCompile("^(0[xX])?04[0-9a-fA-F]{128}$") 93 | return re.MatchString(string(s)) 94 | } 95 | 96 | // 转为整数 97 | func (s HexString) ToInt() (int64, error) { 98 | if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') { 99 | return strconv.ParseInt(string(s[2:]), 16, 64) 100 | } else { 101 | return strconv.ParseInt(string(s), 16, 64) 102 | } 103 | } 104 | 105 | // 转为整数, 如果失败则panic 106 | func (s HexString) MustInt() int64 { 107 | v, err := s.ToInt() 108 | if err != nil { 109 | log.Panic(err) 110 | } 111 | return v 112 | } 113 | 114 | // 转为无符号整数 115 | func (s HexString) ToUint() (uint64, error) { 116 | if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') { 117 | return strconv.ParseUint(string(s[2:]), 16, 64) 118 | } else { 119 | return strconv.ParseUint(string(s), 16, 64) 120 | } 121 | } 122 | 123 | // 转为无符号整数, 如果失败则panic 124 | func (s HexString) MustUint() uint64 { 125 | v, err := s.ToUint() 126 | if err != nil { 127 | log.Panic(err) 128 | } 129 | return v 130 | } 131 | 132 | // 转为大整数 133 | func (s HexString) ToBigint() (*big.Int, bool) { 134 | d, err := s.ToBytes() 135 | if err != nil { 136 | return nil, false 137 | } 138 | return new(big.Int).SetBytes(d), true 139 | } 140 | 141 | // 转为大整数, 如果失败则panic 142 | func (s HexString) MustBigint() *big.Int { 143 | v, ok := s.ToBigint() 144 | if !ok { 145 | log.Panic("invalid bigint: " + string(s)) 146 | } 147 | return v 148 | } 149 | 150 | // 转换为字节类型 151 | // 忽略 0x 开头部分, 返回错误 152 | func (s HexString) ToBytes() ([]byte, error) { 153 | if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') { 154 | return hex.DecodeString(string(s[2:])) 155 | } else { 156 | return hex.DecodeString(string(s)) 157 | } 158 | } 159 | 160 | // 转换为字节类型 161 | // 忽略 0x 开头部分, 时错误panic 162 | func (s HexString) MustBytes() []byte { 163 | v, err := s.ToBytes() 164 | if err != nil { 165 | log.Panic(err) 166 | } 167 | return v 168 | } 169 | 170 | // 转型为字符串 171 | func (s HexString) String() string { 172 | return string(s) 173 | } 174 | -------------------------------------------------------------------------------- /keccak256.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import ( 6 | "fmt" 7 | 8 | "golang.org/x/crypto/sha3" 9 | ) 10 | 11 | // Keccak-256 哈希 12 | // 十六进制格式, 不含0x前缀 13 | func Keccak256Hash(data ...[]byte) string { 14 | keccak256 := sha3.NewLegacyKeccak256() 15 | for _, v := range data { 16 | keccak256.Write(v) 17 | } 18 | return fmt.Sprintf("%x", keccak256.Sum(nil)) 19 | } 20 | -------------------------------------------------------------------------------- /keccak256_test.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import ( 6 | "testing" 7 | ) 8 | 9 | func TestKeccak256Hash(t *testing.T) { 10 | const s = "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" 11 | tAssert(t, Keccak256Hash([]byte("")) == s) 12 | } 13 | -------------------------------------------------------------------------------- /key.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import ( 6 | "crypto/rand" 7 | "fmt" 8 | "math/big" 9 | ) 10 | 11 | // 生成以太坊私钥 12 | // 十六进制格式, 不包含0x头 13 | func GenPrivateKey() string { 14 | // 256bit 对应 32 字节 15 | var buf [32]byte 16 | 17 | // 生成256bit随机数 18 | // 必须是由加密库的强随机数函数生成!!! 19 | if _, err := rand.Read(buf[:]); err != nil { 20 | panic(err) 21 | } 22 | 23 | // 得到对应的256bit整数 24 | // 然后必须对 secp256k1 模素数取模(及小概率会超出, 那是无效的) 25 | var key = new(big.Int) 26 | key.SetBytes(buf[:]).Mod(key, _SECP256K1_P) 27 | 28 | // 最终以十六进制的格式输出 29 | // 256bit对应32字节, 对应64个十六进制字符 30 | return fmt.Sprintf("%064x", key) 31 | } 32 | 33 | // 生成公钥(04开头) 34 | // 十六进制格式, 不包含0x头 35 | func GenPublicKey(privateKey string) string { 36 | // 私钥展开为 big.Int 37 | var k = Hex(privateKey).MustBigint() 38 | 39 | // 生成公钥算法 40 | // secp256k1 椭圆曲线上定义的加法运算 41 | // 公钥 K = k*G, K 是k*G得到的椭圆上的点 42 | var Kx, Ky = S256().ScalarBaseMult(k.Bytes()) 43 | 44 | // 格式化公钥 45 | // 以太坊公钥以04开头, 然后是x和y的十六进制格式字符串 46 | var publicKey = fmt.Sprintf("04%064x%064x", Kx, Ky) 47 | 48 | // OK 49 | return publicKey 50 | } 51 | -------------------------------------------------------------------------------- /key_test.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import ( 6 | "testing" 7 | ) 8 | 9 | // 测试数据来自《精通以太坊》 10 | // https://github.com/ethereumbook/ethereumbook/blob/develop/04keys-addresses.asciidoc 11 | 12 | func TestGenPublicKey(t *testing.T) { 13 | const privateKey = "f8f8a2f43c8376ccb0871305060d7b27b0554d2cc72bccf41b2705608452f315" 14 | const publicKey = "046e145ccef1033dea239875dd00dfb4fee6e3348b84985c92f103444683bae07b83b5c38e5e2b0c8529d7fa3f64d46daa1ece2d9ac14cab9477d042c84c32ccd0" 15 | 16 | tAssert(t, GenPublicKey(privateKey) == publicKey) 17 | } 18 | -------------------------------------------------------------------------------- /keystore.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import ( 6 | "crypto/aes" 7 | "crypto/cipher" 8 | "crypto/rand" 9 | "crypto/sha256" 10 | "encoding/hex" 11 | "encoding/json" 12 | "errors" 13 | "fmt" 14 | "io" 15 | "strconv" 16 | 17 | "golang.org/x/crypto/pbkdf2" 18 | "golang.org/x/crypto/scrypt" 19 | ) 20 | 21 | // V3版本KeyStore对应的JSON结构 22 | // Version 是整数类型 23 | type ksKeyJSON struct { 24 | Address string `json:"address"` 25 | Crypto ksCryptoJSON `json:"crypto"` 26 | Id string `json:"id"` 27 | Version int `json:"version"` 28 | } 29 | 30 | // V1版本KeyStore对应的JSON结构 31 | // Version 是字符串类型 32 | type ksKeyJSONV1 struct { 33 | Address string `json:"address"` 34 | Crypto ksCryptoJSON `json:"crypto"` 35 | Id string `json:"id"` 36 | Version string `json:"version"` 37 | } 38 | 39 | // 加密部分的参数 40 | type ksCryptoJSON struct { 41 | Cipher string `json:"cipher"` 42 | CipherText string `json:"ciphertext"` 43 | CipherParams ksCipherparamsJSON `json:"cipherparams"` 44 | KDF string `json:"kdf"` 45 | KDFParams map[string]interface{} `json:"kdfparams"` 46 | MAC string `json:"mac"` 47 | } 48 | 49 | // 私钥加密算法需要的参数 50 | type ksCipherparamsJSON struct { 51 | IV string `json:"iv"` 52 | } 53 | 54 | // 加密私钥 55 | // 生成KeyStore格式JSON 56 | func KeyStoreEncryptKey(uuid, privateKey, password string) (keyjson []byte, err error) { 57 | // 生成key的长度 58 | const scryptDKLen = 32 59 | 60 | // 默认的生成key参数, 可以调整 61 | const scryptN = 262144 62 | const scryptR = 8 63 | const scryptP = 1 64 | 65 | // 采用 scrypt 算法从 password 生成解密 key 66 | salt := make([]byte, 32) 67 | if _, err := io.ReadFull(rand.Reader, salt); err != nil { 68 | panic("reading from crypto/rand failed: " + err.Error()) 69 | } 70 | derivedKey, err := scrypt.Key([]byte(password), salt, scryptN, scryptR, scryptP, scryptDKLen) 71 | if err != nil { 72 | return nil, err 73 | } 74 | 75 | // 取生成密码的后16字节作为解密Key 76 | encryptKey := derivedKey[:16] 77 | 78 | // 生成AES解密参数 79 | iv := make([]byte, aes.BlockSize) // 16 80 | if _, err := io.ReadFull(rand.Reader, iv); err != nil { 81 | panic("reading from crypto/rand failed: " + err.Error()) 82 | } 83 | 84 | // 加密私钥 85 | keyBytes := Hex(privateKey).MustBytes() 86 | cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) 87 | if err != nil { 88 | return nil, err 89 | } 90 | 91 | // 记录加密私钥的参数 92 | cryptoStruct := ksCryptoJSON{ 93 | // 私钥加密算法 94 | Cipher: "aes-128-ctr", 95 | // 私钥加密后的数据 96 | CipherText: fmt.Sprintf("%x", cipherText), 97 | // 私钥加密算法参数 98 | CipherParams: ksCipherparamsJSON{ 99 | IV: fmt.Sprintf("%x", iv), 100 | }, 101 | 102 | // 私钥加密算法key的生成算法 103 | KDF: "scrypt", 104 | // 生成key算法的参数 105 | KDFParams: map[string]interface{}{ 106 | "n": scryptN, 107 | "r": scryptR, 108 | "p": scryptP, 109 | "dklen": scryptDKLen, 110 | "salt": fmt.Sprintf("%x", salt), 111 | }, 112 | 113 | // password 校验码 114 | MAC: Keccak256Hash(derivedKey[16:32], cipherText), 115 | } 116 | 117 | // 生成 JSON 118 | return json.MarshalIndent( 119 | &ksKeyJSON{ 120 | Address: GenAddressFromPrivateKey(privateKey), 121 | Crypto: cryptoStruct, 122 | Id: uuid, 123 | Version: 3, 124 | }, 125 | "", // 前缀 126 | "\t", // 缩进 127 | ) 128 | } 129 | 130 | // 解密私钥 131 | func KeyStoreDecryptKey(keyjson []byte, password string) (uuid, privateKey string, err error) { 132 | // 解码到map 133 | // V1和V3版本的Version字段类型不同, 因此不能直接解码到结构体中 134 | // V1的Version是字符串类型, V3版本是整数类型 135 | m := make(map[string]interface{}) 136 | if err := json.Unmarshal(keyjson, &m); err != nil { 137 | return "", "", err 138 | } 139 | 140 | // 判读版本 141 | if version, ok := m["version"].(string); ok && version == "1" { 142 | var kV1 ksKeyJSONV1 143 | if err := json.Unmarshal(keyjson, &kV1); err != nil { 144 | return "", "", err 145 | } 146 | 147 | var k = ksKeyJSON{ 148 | Address: kV1.Address, 149 | Crypto: kV1.Crypto, 150 | Id: kV1.Id, 151 | Version: 1, 152 | } 153 | return k.decryptKeyV1(password) 154 | } else { 155 | // 解码JSON 156 | var k ksKeyJSON 157 | if err := json.Unmarshal(keyjson, &k); err != nil { 158 | return "", "", err 159 | } 160 | return k.decryptKeyV3(password) 161 | } 162 | } 163 | 164 | func (p *ksKeyJSON) decryptKeyV1(password string) (uuid, privateKey string, err error) { 165 | // 重现加密key 166 | derivedKey, err := p.getKDFKey(password) 167 | if err != nil { 168 | return "", "", err 169 | } 170 | 171 | // 校验 password 是否正确 172 | cipherText, err := hex.DecodeString(p.Crypto.CipherText) 173 | if err != nil { 174 | return "", "", err 175 | } 176 | calculatedMAC := Keccak256Hash(derivedKey[16:32], cipherText) 177 | if calculatedMAC != p.Crypto.MAC { 178 | return "", "", errors.New("could not decrypt key with given password") 179 | } 180 | 181 | // V1版解密的密码还需要再做一次Keccak256Hash 182 | derivedKey, err = hex.DecodeString(Keccak256Hash(derivedKey[:16])) 183 | if err != nil { 184 | return "", "", err 185 | } 186 | 187 | // AES 对称解密 188 | iv, err := hex.DecodeString(p.Crypto.CipherParams.IV) 189 | if err != nil { 190 | return "", "", err 191 | } 192 | plainText, err := aesCBCDecrypt(derivedKey[:16], cipherText, iv) 193 | if err != nil { 194 | return "", "", err 195 | } 196 | 197 | // OK 198 | return p.Id, fmt.Sprintf("%x", plainText), err 199 | } 200 | 201 | func (p *ksKeyJSON) decryptKeyV3(password string) (uuid, privateKey string, err error) { 202 | if p.Crypto.Cipher != "aes-128-ctr" { 203 | return "", "", fmt.Errorf("cipher not supported: %v", p.Crypto.Cipher) 204 | } 205 | 206 | // 重现加密key 207 | derivedKey, err := p.getKDFKey(password) 208 | if err != nil { 209 | return "", "", err 210 | } 211 | 212 | // 校验 password 是否正确 213 | cipherText, err := hex.DecodeString(p.Crypto.CipherText) 214 | if err != nil { 215 | return "", "", err 216 | } 217 | calculatedMAC := Keccak256Hash(derivedKey[16:32], cipherText) 218 | if calculatedMAC != p.Crypto.MAC { 219 | return "", "", errors.New("could not decrypt key with given password") 220 | } 221 | 222 | // AES 对称解密 223 | iv, err := hex.DecodeString(p.Crypto.CipherParams.IV) 224 | if err != nil { 225 | return "", "", err 226 | } 227 | plainText, err := aesCTRXOR(derivedKey[:16], cipherText, iv) 228 | if err != nil { 229 | return "", "", err 230 | } 231 | 232 | // OK 233 | return p.Id, fmt.Sprintf("%x", plainText), err 234 | } 235 | 236 | func (p *ksKeyJSON) getKDFKey(password string) ([]byte, error) { 237 | authArray := []byte(password) 238 | salt, err := hex.DecodeString(p.Crypto.KDFParams["salt"].(string)) 239 | if err != nil { 240 | return nil, err 241 | } 242 | 243 | switch { 244 | case p.Crypto.KDF == "scrypt": 245 | return scrypt.Key(authArray, salt, 246 | p.getKDFParamsInt("n"), 247 | p.getKDFParamsInt("r"), 248 | p.getKDFParamsInt("p"), 249 | p.getKDFParamsInt("dklen"), 250 | ) 251 | 252 | case p.Crypto.KDF == "pbkdf2": 253 | if prf := p.getKDFParamsString("prf"); prf != "hmac-sha256" { 254 | return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf) 255 | } 256 | key := pbkdf2.Key(authArray, salt, 257 | p.getKDFParamsInt("c"), 258 | p.getKDFParamsInt("dklen"), 259 | sha256.New, 260 | ) 261 | return key, nil 262 | 263 | default: 264 | return nil, fmt.Errorf("unsupported KDF: %s", p.Crypto.KDF) 265 | } 266 | } 267 | 268 | func (p *ksKeyJSON) getKDFParamsInt(name string) int { 269 | f, _ := strconv.ParseFloat(p.getKDFParamsString(name), 64) 270 | return int(f) 271 | } 272 | func (p *ksKeyJSON) getKDFParamsString(name string) string { 273 | return fmt.Sprint(p.Crypto.KDFParams[name]) 274 | } 275 | 276 | // AES加密/解密数据 277 | func aesCTRXOR(key, inText, iv []byte) ([]byte, error) { 278 | aesBlock, err := aes.NewCipher(key) 279 | if err != nil { 280 | return nil, err 281 | } 282 | stream := cipher.NewCTR(aesBlock, iv) 283 | outText := make([]byte, len(inText)) 284 | stream.XORKeyStream(outText, inText) 285 | return outText, err 286 | } 287 | 288 | // V1版本加密/解密 289 | func aesCBCDecrypt(key, cipherText, iv []byte) ([]byte, error) { 290 | aesBlock, err := aes.NewCipher(key) 291 | if err != nil { 292 | return nil, err 293 | } 294 | decrypter := cipher.NewCBCDecrypter(aesBlock, iv) 295 | paddedPlaintext := make([]byte, len(cipherText)) 296 | decrypter.CryptBlocks(paddedPlaintext, cipherText) 297 | plaintext := pkcs7Unpad(paddedPlaintext) 298 | if plaintext == nil { 299 | return nil, errors.New("could not decrypt key with given password") 300 | } 301 | return plaintext, err 302 | } 303 | 304 | // From https://leanpub.com/gocrypto/read#leanpub-auto-block-cipher-modes 305 | func pkcs7Unpad(in []byte) []byte { 306 | if len(in) == 0 { 307 | return nil 308 | } 309 | 310 | padding := in[len(in)-1] 311 | if int(padding) > len(in) || padding > aes.BlockSize { 312 | return nil 313 | } else if padding == 0 { 314 | return nil 315 | } 316 | 317 | for i := len(in) - 1; i > len(in)-int(padding)-1; i-- { 318 | if in[i] != padding { 319 | return nil 320 | } 321 | } 322 | return in[:len(in)-int(padding)] 323 | } 324 | -------------------------------------------------------------------------------- /keystore_example_test.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil_test 4 | 5 | import ( 6 | "fmt" 7 | "log" 8 | 9 | "github.com/chai2010/ethutil" 10 | ) 11 | 12 | func ExampleKeyStoreDecryptKey() { 13 | const keyjson = `{ 14 | "crypto" : { 15 | "cipher" : "aes-128-ctr", 16 | "cipherparams" : { 17 | "iv" : "83dbcc02d8ccb40e466191a123791e0e" 18 | }, 19 | "ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c", 20 | "kdf" : "scrypt", 21 | "kdfparams" : { 22 | "dklen" : 32, 23 | "n" : 262144, 24 | "r" : 1, 25 | "p" : 8, 26 | "salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19" 27 | }, 28 | "mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097" 29 | }, 30 | "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6", 31 | "version" : 3 32 | }` 33 | 34 | uuid, privateKey, err := ethutil.KeyStoreDecryptKey( 35 | []byte(keyjson), "testpassword", 36 | ) 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | 41 | fmt.Println(uuid) 42 | fmt.Println(privateKey) 43 | 44 | // Output: 45 | // 3198bc9c-6672-5ab3-d995-4942343ae5b6 46 | // 7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d 47 | } 48 | -------------------------------------------------------------------------------- /keystore_test.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import "testing" 6 | 7 | import ( 8 | "encoding/json" 9 | "io/ioutil" 10 | ) 11 | 12 | func TestKeyStoreEncryptKey(t *testing.T) { 13 | var password = "testpassword" 14 | var uuid = "3198bc9c-6672-5ab3-d995-4942343ae5b6" 15 | var privateKey = "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d" 16 | 17 | keyjson, err := KeyStoreEncryptKey(uuid, privateKey, password) 18 | tAssert(t, err == nil, err) 19 | 20 | uuid2, privateKey2, err := KeyStoreDecryptKey(keyjson, password) 21 | tAssert(t, err == nil, err) 22 | tAssert(t, uuid == uuid2, uuid2) 23 | tAssert(t, privateKey == privateKey2, privateKey2) 24 | } 25 | 26 | func TestKeyStoreDecryptKey(t *testing.T) { 27 | for _, tm := range []map[string]interface{}{ 28 | tLoadTestdata(t, "v1_test_vector.json"), 29 | tLoadTestdata(t, "v3_test_vector.json"), 30 | } { 31 | for tname, m := range tm { 32 | m := m.(map[string]interface{}) 33 | mJson := m["json"].(map[string]interface{}) 34 | 35 | mJsonData, err := json.Marshal(mJson) 36 | if err != nil { 37 | t.Fatal(tname, err) 38 | } 39 | 40 | id, priv, err := KeyStoreDecryptKey(mJsonData, m["password"].(string)) 41 | tAssert(t, err == nil, tname, err) 42 | tAssert(t, id == mJson["id"].(string), tname, id, priv) 43 | tAssert(t, m["priv"] == priv, tname, id, priv) 44 | } 45 | } 46 | } 47 | 48 | // 加载 testdata 目录下的文件 49 | func tLoadTestdata(tb testing.TB, filename string) map[string]interface{} { 50 | data, err := ioutil.ReadFile("./testdata/" + filename) 51 | if err != nil { 52 | tb.Fatal(err) 53 | } 54 | 55 | var m = make(map[string]interface{}) 56 | if err := json.Unmarshal(data, &m); err != nil { 57 | tb.Fatal(err) 58 | } 59 | return m 60 | } 61 | -------------------------------------------------------------------------------- /number.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import ( 6 | "fmt" 7 | "log" 8 | "math/big" 9 | "strconv" 10 | ) 11 | 12 | // 解析整数(失败时panic) 13 | // 支持二进制/八进制/十进制/十六进制 14 | func MustInt64(s string, base int) int64 { 15 | v, err := ParseInt64(s, base) 16 | if err != nil { 17 | log.Panic(err) 18 | } 19 | return v 20 | } 21 | 22 | // 解析整数 23 | // 支持二进制/八进制/十进制/十六进制 24 | func ParseInt64(s string, base int) (v int64, err error) { 25 | // 0b/0o/0x 开头 26 | if len(s) > 2 && s[0] == '0' { 27 | switch { 28 | case s[1] == 'b' || s[1] == 'B': 29 | if base != 0 && base != 2 { 30 | return 0, fmt.Errorf("ethutil.ParseInt64: invalid base %d", base) 31 | } 32 | return strconv.ParseInt(s[2:], 2, 64) 33 | case s[1] == 'o' || s[1] == 'O': 34 | if base != 0 && base != 8 { 35 | return 0, fmt.Errorf("ethutil.ParseInt64: invalid base %d", base) 36 | } 37 | return strconv.ParseInt(s[2:], 8, 64) 38 | case s[1] == 'x' || s[1] == 'X': 39 | if base != 0 && base != 16 { 40 | return 0, fmt.Errorf("ethutil.ParseInt64: invalid base %d", base) 41 | } 42 | return strconv.ParseInt(s[2:], 16, 64) 43 | } 44 | } 45 | 46 | // 其它根据base解析(默认为十进制) 47 | if base > 0 { 48 | return strconv.ParseInt(s, base, 64) 49 | } else { 50 | return strconv.ParseInt(s, 10, 64) 51 | } 52 | } 53 | 54 | // 解析无符号整数(失败时panic) 55 | // 支持二进制/八进制/十进制/十六进制 56 | func MustUint64(s string, base int) uint64 { 57 | v, err := ParseUint64(s, base) 58 | if err != nil { 59 | log.Panic(err) 60 | } 61 | return v 62 | } 63 | 64 | // 解析无符号整数 65 | // 支持二进制/八进制/十进制/十六进制 66 | func ParseUint64(s string, base int) (v uint64, err error) { 67 | // 0b/0o/0x 开头 68 | if len(s) > 2 && s[0] == '0' { 69 | switch { 70 | case s[1] == 'b' || s[1] == 'B': 71 | if base != 0 && base != 2 { 72 | return 0, fmt.Errorf("ethutil.ParseUint64: invalid base %d", base) 73 | } 74 | return strconv.ParseUint(s[2:], 2, 64) 75 | case s[1] == 'o' || s[1] == 'O': 76 | if base != 0 && base != 8 { 77 | return 0, fmt.Errorf("ethutil.ParseUint64: invalid base %d", base) 78 | } 79 | return strconv.ParseUint(s[2:], 8, 64) 80 | case s[1] == 'x' || s[1] == 'X': 81 | if base != 0 && base != 16 { 82 | return 0, fmt.Errorf("ethutil.ParseUint64: invalid base %d", base) 83 | } 84 | return strconv.ParseUint(s[2:], 16, 64) 85 | } 86 | } 87 | 88 | // 其它根据base解析(默认为十进制) 89 | if base > 0 { 90 | return strconv.ParseUint(s, base, 64) 91 | } else { 92 | return strconv.ParseUint(s, 10, 64) 93 | } 94 | } 95 | 96 | // 解析大整数(失败时panic) 97 | // 支持二进制/八进制/十进制/十六进制 98 | func MustBigint(s string, base int) *big.Int { 99 | v, err := ParseBigint(s, base) 100 | if err != nil { 101 | log.Panic(err) 102 | } 103 | return v 104 | } 105 | 106 | // 解析大整数 107 | // 支持二进制/八进制/十进制/十六进制 108 | func ParseBigint(s string, base int) (v *big.Int, err error) { 109 | // 0b/0o/0x 开头 110 | if len(s) > 2 && s[0] == '0' { 111 | switch { 112 | case s[1] == 'b' || s[1] == 'B': 113 | if base != 0 && base != 2 { 114 | println("ParseBigint 11") 115 | return nil, fmt.Errorf("ethutil.ParseBigint: invalid base %d", base) 116 | } 117 | v, ok := new(big.Int).SetString(s[2:], 2) 118 | if !ok { 119 | println("ParseBigint 22") 120 | return nil, fmt.Errorf("ethutil.ParseBigint: invalid bigint %q", s) 121 | } 122 | return v, nil 123 | case s[1] == 'o' || s[1] == 'O': 124 | if base != 0 && base != 8 { 125 | println("ParseBigint 33") 126 | return nil, fmt.Errorf("ethutil.ParseBigint: invalid base %d", base) 127 | } 128 | v, ok := new(big.Int).SetString(s[2:], 8) 129 | if !ok { 130 | println("ParseBigint 44") 131 | return nil, fmt.Errorf("ethutil.ParseBigint: invalid bigint %q", s) 132 | } 133 | return v, nil 134 | case s[1] == 'x' || s[1] == 'X': 135 | if base != 0 && base != 16 { 136 | println("ParseBigint 55") 137 | return nil, fmt.Errorf("ethutil.ParseBigint: invalid base %d", base) 138 | } 139 | v, ok := new(big.Int).SetString(s[2:], 16) 140 | if !ok { 141 | println("ParseBigint 66") 142 | return nil, fmt.Errorf("ethutil.ParseBigint: invalid bigint %q", s) 143 | } 144 | return v, nil 145 | } 146 | } 147 | 148 | // 其它根据base解析(默认为十进制) 149 | if base > 0 { 150 | v, ok := new(big.Int).SetString(s, base) 151 | if !ok { 152 | println("ParseBigint 77") 153 | return nil, fmt.Errorf("ethutil.ParseBigint: invalid bigint %q", s) 154 | } 155 | return v, nil 156 | } else { 157 | v, ok := new(big.Int).SetString(s, 10) 158 | if !ok { 159 | println("ParseBigint 88") 160 | return nil, fmt.Errorf("ethutil.ParseBigint: invalid bigint %q", s) 161 | } 162 | return v, nil 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /rlp/decode_tail_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package rlp 18 | 19 | import ( 20 | "bytes" 21 | "fmt" 22 | ) 23 | 24 | type structWithTail struct { 25 | A, B uint 26 | C []uint `rlp:"tail"` 27 | } 28 | 29 | func ExampleDecode_structTagTail() { 30 | // In this example, the "tail" struct tag is used to decode lists of 31 | // differing length into a struct. 32 | var val structWithTail 33 | 34 | err := Decode(bytes.NewReader([]byte{0xC4, 0x01, 0x02, 0x03, 0x04}), &val) 35 | fmt.Printf("with 4 elements: err=%v val=%v\n", err, val) 36 | 37 | err = Decode(bytes.NewReader([]byte{0xC6, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06}), &val) 38 | fmt.Printf("with 6 elements: err=%v val=%v\n", err, val) 39 | 40 | // Note that at least two list elements must be present to 41 | // fill fields A and B: 42 | err = Decode(bytes.NewReader([]byte{0xC1, 0x01}), &val) 43 | fmt.Printf("with 1 element: err=%q\n", err) 44 | 45 | // Output: 46 | // with 4 elements: err= val={1 2 [3 4]} 47 | // with 6 elements: err= val={1 2 [3 4 5 6]} 48 | // with 1 element: err="rlp: too few elements for rlp.structWithTail" 49 | } 50 | -------------------------------------------------------------------------------- /rlp/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | /* 18 | Package rlp implements the RLP serialization format. 19 | 20 | The purpose of RLP (Recursive Linear Prefix) is to encode arbitrarily nested arrays of 21 | binary data, and RLP is the main encoding method used to serialize objects in Ethereum. 22 | The only purpose of RLP is to encode structure; encoding specific atomic data types (eg. 23 | strings, ints, floats) is left up to higher-order protocols. In Ethereum integers must be 24 | represented in big endian binary form with no leading zeroes (thus making the integer 25 | value zero equivalent to the empty string). 26 | 27 | RLP values are distinguished by a type tag. The type tag precedes the value in the input 28 | stream and defines the size and kind of the bytes that follow. 29 | 30 | 31 | Encoding Rules 32 | 33 | Package rlp uses reflection and encodes RLP based on the Go type of the value. 34 | 35 | If the type implements the Encoder interface, Encode calls EncodeRLP. It does not 36 | call EncodeRLP on nil pointer values. 37 | 38 | To encode a pointer, the value being pointed to is encoded. A nil pointer to a struct 39 | type, slice or array always encodes as an empty RLP list unless the slice or array has 40 | elememt type byte. A nil pointer to any other value encodes as the empty string. 41 | 42 | Struct values are encoded as an RLP list of all their encoded public fields. Recursive 43 | struct types are supported. 44 | 45 | To encode slices and arrays, the elements are encoded as an RLP list of the value's 46 | elements. Note that arrays and slices with element type uint8 or byte are always encoded 47 | as an RLP string. 48 | 49 | A Go string is encoded as an RLP string. 50 | 51 | An unsigned integer value is encoded as an RLP string. Zero always encodes as an empty RLP 52 | string. big.Int values are treated as integers. Signed integers (int, int8, int16, ...) 53 | are not supported and will return an error when encoding. 54 | 55 | Boolean values are encoded as the unsigned integers zero (false) and one (true). 56 | 57 | An interface value encodes as the value contained in the interface. 58 | 59 | Floating point numbers, maps, channels and functions are not supported. 60 | 61 | 62 | Decoding Rules 63 | 64 | Decoding uses the following type-dependent rules: 65 | 66 | If the type implements the Decoder interface, DecodeRLP is called. 67 | 68 | To decode into a pointer, the value will be decoded as the element type of the pointer. If 69 | the pointer is nil, a new value of the pointer's element type is allocated. If the pointer 70 | is non-nil, the existing value will be reused. Note that package rlp never leaves a 71 | pointer-type struct field as nil unless one of the "nil" struct tags is present. 72 | 73 | To decode into a struct, decoding expects the input to be an RLP list. The decoded 74 | elements of the list are assigned to each public field in the order given by the struct's 75 | definition. The input list must contain an element for each decoded field. Decoding 76 | returns an error if there are too few or too many elements for the struct. 77 | 78 | To decode into a slice, the input must be a list and the resulting slice will contain the 79 | input elements in order. For byte slices, the input must be an RLP string. Array types 80 | decode similarly, with the additional restriction that the number of input elements (or 81 | bytes) must match the array's defined length. 82 | 83 | To decode into a Go string, the input must be an RLP string. The input bytes are taken 84 | as-is and will not necessarily be valid UTF-8. 85 | 86 | To decode into an unsigned integer type, the input must also be an RLP string. The bytes 87 | are interpreted as a big endian representation of the integer. If the RLP string is larger 88 | than the bit size of the type, decoding will return an error. Decode also supports 89 | *big.Int. There is no size limit for big integers. 90 | 91 | To decode into a boolean, the input must contain an unsigned integer of value zero (false) 92 | or one (true). 93 | 94 | To decode into an interface value, one of these types is stored in the value: 95 | 96 | []interface{}, for RLP lists 97 | []byte, for RLP strings 98 | 99 | Non-empty interface types are not supported when decoding. 100 | Signed integers, floating point numbers, maps, channels and functions cannot be decoded into. 101 | 102 | 103 | Struct Tags 104 | 105 | Package rlp honours certain struct tags: "-", "tail", "nil", "nilList" and "nilString". 106 | 107 | The "-" tag ignores fields. 108 | 109 | The "tail" tag, which may only be used on the last exported struct field, allows slurping 110 | up any excess list elements into a slice. See examples for more details. 111 | 112 | The "nil" tag applies to pointer-typed fields and changes the decoding rules for the field 113 | such that input values of size zero decode as a nil pointer. This tag can be useful when 114 | decoding recursive types. 115 | 116 | type StructWithOptionalFoo struct { 117 | Foo *[20]byte `rlp:"nil"` 118 | } 119 | 120 | RLP supports two kinds of empty values: empty lists and empty strings. When using the 121 | "nil" tag, the kind of empty value allowed for a type is chosen automatically. A struct 122 | field whose Go type is a pointer to an unsigned integer, string, boolean or byte 123 | array/slice expects an empty RLP string. Any other pointer field type encodes/decodes as 124 | an empty RLP list. 125 | 126 | The choice of null value can be made explicit with the "nilList" and "nilString" struct 127 | tags. Using these tags encodes/decodes a Go nil pointer value as the kind of empty 128 | RLP value defined by the tag. 129 | */ 130 | package rlp 131 | -------------------------------------------------------------------------------- /rlp/encode_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package rlp 18 | 19 | import ( 20 | "bytes" 21 | "errors" 22 | "fmt" 23 | "io" 24 | "io/ioutil" 25 | "math/big" 26 | "sync" 27 | "testing" 28 | ) 29 | 30 | type testEncoder struct { 31 | err error 32 | } 33 | 34 | func (e *testEncoder) EncodeRLP(w io.Writer) error { 35 | if e == nil { 36 | panic("EncodeRLP called on nil value") 37 | } 38 | if e.err != nil { 39 | return e.err 40 | } else { 41 | w.Write([]byte{0, 1, 0, 1, 0, 1, 0, 1, 0, 1}) 42 | } 43 | return nil 44 | } 45 | 46 | type testEncoderValueMethod struct{} 47 | 48 | func (e testEncoderValueMethod) EncodeRLP(w io.Writer) error { 49 | w.Write([]byte{0xFA, 0xFE, 0xF0}) 50 | return nil 51 | } 52 | 53 | type byteEncoder byte 54 | 55 | func (e byteEncoder) EncodeRLP(w io.Writer) error { 56 | w.Write(EmptyList) 57 | return nil 58 | } 59 | 60 | type undecodableEncoder func() 61 | 62 | func (f undecodableEncoder) EncodeRLP(w io.Writer) error { 63 | w.Write([]byte{0xF5, 0xF5, 0xF5}) 64 | return nil 65 | } 66 | 67 | type encodableReader struct { 68 | A, B uint 69 | } 70 | 71 | func (e *encodableReader) Read(b []byte) (int, error) { 72 | panic("called") 73 | } 74 | 75 | type namedByteType byte 76 | 77 | var ( 78 | _ = Encoder(&testEncoder{}) 79 | _ = Encoder(byteEncoder(0)) 80 | 81 | reader io.Reader = &encodableReader{1, 2} 82 | ) 83 | 84 | type encTest struct { 85 | val interface{} 86 | output, error string 87 | } 88 | 89 | var encTests = []encTest{ 90 | // booleans 91 | {val: true, output: "01"}, 92 | {val: false, output: "80"}, 93 | 94 | // integers 95 | {val: uint32(0), output: "80"}, 96 | {val: uint32(127), output: "7F"}, 97 | {val: uint32(128), output: "8180"}, 98 | {val: uint32(256), output: "820100"}, 99 | {val: uint32(1024), output: "820400"}, 100 | {val: uint32(0xFFFFFF), output: "83FFFFFF"}, 101 | {val: uint32(0xFFFFFFFF), output: "84FFFFFFFF"}, 102 | {val: uint64(0xFFFFFFFF), output: "84FFFFFFFF"}, 103 | {val: uint64(0xFFFFFFFFFF), output: "85FFFFFFFFFF"}, 104 | {val: uint64(0xFFFFFFFFFFFF), output: "86FFFFFFFFFFFF"}, 105 | {val: uint64(0xFFFFFFFFFFFFFF), output: "87FFFFFFFFFFFFFF"}, 106 | {val: uint64(0xFFFFFFFFFFFFFFFF), output: "88FFFFFFFFFFFFFFFF"}, 107 | 108 | // big integers (should match uint for small values) 109 | {val: big.NewInt(0), output: "80"}, 110 | {val: big.NewInt(1), output: "01"}, 111 | {val: big.NewInt(127), output: "7F"}, 112 | {val: big.NewInt(128), output: "8180"}, 113 | {val: big.NewInt(256), output: "820100"}, 114 | {val: big.NewInt(1024), output: "820400"}, 115 | {val: big.NewInt(0xFFFFFF), output: "83FFFFFF"}, 116 | {val: big.NewInt(0xFFFFFFFF), output: "84FFFFFFFF"}, 117 | {val: big.NewInt(0xFFFFFFFFFF), output: "85FFFFFFFFFF"}, 118 | {val: big.NewInt(0xFFFFFFFFFFFF), output: "86FFFFFFFFFFFF"}, 119 | {val: big.NewInt(0xFFFFFFFFFFFFFF), output: "87FFFFFFFFFFFFFF"}, 120 | { 121 | val: big.NewInt(0).SetBytes(unhex("102030405060708090A0B0C0D0E0F2")), 122 | output: "8F102030405060708090A0B0C0D0E0F2", 123 | }, 124 | { 125 | val: big.NewInt(0).SetBytes(unhex("0100020003000400050006000700080009000A000B000C000D000E01")), 126 | output: "9C0100020003000400050006000700080009000A000B000C000D000E01", 127 | }, 128 | { 129 | val: big.NewInt(0).SetBytes(unhex("010000000000000000000000000000000000000000000000000000000000000000")), 130 | output: "A1010000000000000000000000000000000000000000000000000000000000000000", 131 | }, 132 | 133 | // non-pointer big.Int 134 | {val: *big.NewInt(0), output: "80"}, 135 | {val: *big.NewInt(0xFFFFFF), output: "83FFFFFF"}, 136 | 137 | // negative ints are not supported 138 | {val: big.NewInt(-1), error: "rlp: cannot encode negative *big.Int"}, 139 | 140 | // byte slices, strings 141 | {val: []byte{}, output: "80"}, 142 | {val: []byte{0x7E}, output: "7E"}, 143 | {val: []byte{0x7F}, output: "7F"}, 144 | {val: []byte{0x80}, output: "8180"}, 145 | {val: []byte{1, 2, 3}, output: "83010203"}, 146 | 147 | {val: []namedByteType{1, 2, 3}, output: "83010203"}, 148 | {val: [...]namedByteType{1, 2, 3}, output: "83010203"}, 149 | 150 | {val: "", output: "80"}, 151 | {val: "\x7E", output: "7E"}, 152 | {val: "\x7F", output: "7F"}, 153 | {val: "\x80", output: "8180"}, 154 | {val: "dog", output: "83646F67"}, 155 | { 156 | val: "Lorem ipsum dolor sit amet, consectetur adipisicing eli", 157 | output: "B74C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E7365637465747572206164697069736963696E6720656C69", 158 | }, 159 | { 160 | val: "Lorem ipsum dolor sit amet, consectetur adipisicing elit", 161 | output: "B8384C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E7365637465747572206164697069736963696E6720656C6974", 162 | }, 163 | { 164 | val: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur mauris magna, suscipit sed vehicula non, iaculis faucibus tortor. Proin suscipit ultricies malesuada. Duis tortor elit, dictum quis tristique eu, ultrices at risus. Morbi a est imperdiet mi ullamcorper aliquet suscipit nec lorem. Aenean quis leo mollis, vulputate elit varius, consequat enim. Nulla ultrices turpis justo, et posuere urna consectetur nec. Proin non convallis metus. Donec tempor ipsum in mauris congue sollicitudin. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Suspendisse convallis sem vel massa faucibus, eget lacinia lacus tempor. Nulla quis ultricies purus. Proin auctor rhoncus nibh condimentum mollis. Aliquam consequat enim at metus luctus, a eleifend purus egestas. Curabitur at nibh metus. Nam bibendum, neque at auctor tristique, lorem libero aliquet arcu, non interdum tellus lectus sit amet eros. Cras rhoncus, metus ac ornare cursus, dolor justo ultrices metus, at ullamcorper volutpat", 165 | output: "B904004C6F72656D20697073756D20646F6C6F722073697420616D65742C20636F6E73656374657475722061646970697363696E6720656C69742E20437572616269747572206D6175726973206D61676E612C20737573636970697420736564207665686963756C61206E6F6E2C20696163756C697320666175636962757320746F72746F722E2050726F696E20737573636970697420756C74726963696573206D616C6573756164612E204475697320746F72746F7220656C69742C2064696374756D2071756973207472697374697175652065752C20756C7472696365732061742072697375732E204D6F72626920612065737420696D70657264696574206D6920756C6C616D636F7270657220616C6971756574207375736369706974206E6563206C6F72656D2E2041656E65616E2071756973206C656F206D6F6C6C69732C2076756C70757461746520656C6974207661726975732C20636F6E73657175617420656E696D2E204E756C6C6120756C74726963657320747572706973206A7573746F2C20657420706F73756572652075726E6120636F6E7365637465747572206E65632E2050726F696E206E6F6E20636F6E76616C6C6973206D657475732E20446F6E65632074656D706F7220697073756D20696E206D617572697320636F6E67756520736F6C6C696369747564696E2E20566573746962756C756D20616E746520697073756D207072696D697320696E206661756369627573206F726369206C756374757320657420756C74726963657320706F737565726520637562696C69612043757261653B2053757370656E646973736520636F6E76616C6C69732073656D2076656C206D617373612066617563696275732C2065676574206C6163696E6961206C616375732074656D706F722E204E756C6C61207175697320756C747269636965732070757275732E2050726F696E20617563746F722072686F6E637573206E69626820636F6E64696D656E74756D206D6F6C6C69732E20416C697175616D20636F6E73657175617420656E696D206174206D65747573206C75637475732C206120656C656966656E6420707572757320656765737461732E20437572616269747572206174206E696268206D657475732E204E616D20626962656E64756D2C206E6571756520617420617563746F72207472697374697175652C206C6F72656D206C696265726F20616C697175657420617263752C206E6F6E20696E74657264756D2074656C6C7573206C65637475732073697420616D65742065726F732E20437261732072686F6E6375732C206D65747573206163206F726E617265206375727375732C20646F6C6F72206A7573746F20756C747269636573206D657475732C20617420756C6C616D636F7270657220766F6C7574706174", 166 | }, 167 | 168 | // slices 169 | {val: []uint{}, output: "C0"}, 170 | {val: []uint{1, 2, 3}, output: "C3010203"}, 171 | { 172 | // [ [], [[]], [ [], [[]] ] ] 173 | val: []interface{}{[]interface{}{}, [][]interface{}{{}}, []interface{}{[]interface{}{}, [][]interface{}{{}}}}, 174 | output: "C7C0C1C0C3C0C1C0", 175 | }, 176 | { 177 | val: []string{"aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii", "jjj", "kkk", "lll", "mmm", "nnn", "ooo"}, 178 | output: "F83C836161618362626283636363836464648365656583666666836767678368686883696969836A6A6A836B6B6B836C6C6C836D6D6D836E6E6E836F6F6F", 179 | }, 180 | { 181 | val: []interface{}{uint(1), uint(0xFFFFFF), []interface{}{[]uint{4, 5, 5}}, "abc"}, 182 | output: "CE0183FFFFFFC4C304050583616263", 183 | }, 184 | { 185 | val: [][]string{ 186 | {"asdf", "qwer", "zxcv"}, 187 | {"asdf", "qwer", "zxcv"}, 188 | {"asdf", "qwer", "zxcv"}, 189 | {"asdf", "qwer", "zxcv"}, 190 | {"asdf", "qwer", "zxcv"}, 191 | {"asdf", "qwer", "zxcv"}, 192 | {"asdf", "qwer", "zxcv"}, 193 | {"asdf", "qwer", "zxcv"}, 194 | {"asdf", "qwer", "zxcv"}, 195 | {"asdf", "qwer", "zxcv"}, 196 | {"asdf", "qwer", "zxcv"}, 197 | {"asdf", "qwer", "zxcv"}, 198 | {"asdf", "qwer", "zxcv"}, 199 | {"asdf", "qwer", "zxcv"}, 200 | {"asdf", "qwer", "zxcv"}, 201 | {"asdf", "qwer", "zxcv"}, 202 | {"asdf", "qwer", "zxcv"}, 203 | {"asdf", "qwer", "zxcv"}, 204 | {"asdf", "qwer", "zxcv"}, 205 | {"asdf", "qwer", "zxcv"}, 206 | {"asdf", "qwer", "zxcv"}, 207 | {"asdf", "qwer", "zxcv"}, 208 | {"asdf", "qwer", "zxcv"}, 209 | {"asdf", "qwer", "zxcv"}, 210 | {"asdf", "qwer", "zxcv"}, 211 | {"asdf", "qwer", "zxcv"}, 212 | {"asdf", "qwer", "zxcv"}, 213 | {"asdf", "qwer", "zxcv"}, 214 | {"asdf", "qwer", "zxcv"}, 215 | {"asdf", "qwer", "zxcv"}, 216 | {"asdf", "qwer", "zxcv"}, 217 | {"asdf", "qwer", "zxcv"}, 218 | }, 219 | output: "F90200CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376CF84617364668471776572847A786376", 220 | }, 221 | 222 | // RawValue 223 | {val: RawValue(unhex("01")), output: "01"}, 224 | {val: RawValue(unhex("82FFFF")), output: "82FFFF"}, 225 | {val: []RawValue{unhex("01"), unhex("02")}, output: "C20102"}, 226 | 227 | // structs 228 | {val: simplestruct{}, output: "C28080"}, 229 | {val: simplestruct{A: 3, B: "foo"}, output: "C50383666F6F"}, 230 | {val: &recstruct{5, nil}, output: "C205C0"}, 231 | {val: &recstruct{5, &recstruct{4, &recstruct{3, nil}}}, output: "C605C404C203C0"}, 232 | {val: &tailRaw{A: 1, Tail: []RawValue{unhex("02"), unhex("03")}}, output: "C3010203"}, 233 | {val: &tailRaw{A: 1, Tail: []RawValue{unhex("02")}}, output: "C20102"}, 234 | {val: &tailRaw{A: 1, Tail: []RawValue{}}, output: "C101"}, 235 | {val: &tailRaw{A: 1, Tail: nil}, output: "C101"}, 236 | {val: &hasIgnoredField{A: 1, B: 2, C: 3}, output: "C20103"}, 237 | {val: &intField{X: 3}, error: "rlp: type int is not RLP-serializable (struct field rlp.intField.X)"}, 238 | 239 | // nil 240 | {val: (*uint)(nil), output: "80"}, 241 | {val: (*string)(nil), output: "80"}, 242 | {val: (*[]byte)(nil), output: "80"}, 243 | {val: (*[10]byte)(nil), output: "80"}, 244 | {val: (*big.Int)(nil), output: "80"}, 245 | {val: (*[]string)(nil), output: "C0"}, 246 | {val: (*[10]string)(nil), output: "C0"}, 247 | {val: (*[]interface{})(nil), output: "C0"}, 248 | {val: (*[]struct{ uint })(nil), output: "C0"}, 249 | {val: (*interface{})(nil), output: "C0"}, 250 | 251 | // nil struct fields 252 | { 253 | val: struct { 254 | X *[]byte 255 | }{}, 256 | output: "C180", 257 | }, 258 | { 259 | val: struct { 260 | X *[2]byte 261 | }{}, 262 | output: "C180", 263 | }, 264 | { 265 | val: struct { 266 | X *uint64 267 | }{}, 268 | output: "C180", 269 | }, 270 | { 271 | val: struct { 272 | X *uint64 `rlp:"nilList"` 273 | }{}, 274 | output: "C1C0", 275 | }, 276 | { 277 | val: struct { 278 | X *[]uint64 279 | }{}, 280 | output: "C1C0", 281 | }, 282 | { 283 | val: struct { 284 | X *[]uint64 `rlp:"nilString"` 285 | }{}, 286 | output: "C180", 287 | }, 288 | 289 | // interfaces 290 | {val: []io.Reader{reader}, output: "C3C20102"}, // the contained value is a struct 291 | 292 | // Encoder 293 | {val: (*testEncoder)(nil), output: "C0"}, 294 | {val: &testEncoder{}, output: "00010001000100010001"}, 295 | {val: &testEncoder{errors.New("test error")}, error: "test error"}, 296 | {val: struct{ E testEncoderValueMethod }{}, output: "C3FAFEF0"}, 297 | {val: struct{ E *testEncoderValueMethod }{}, output: "C1C0"}, 298 | 299 | // Verify that the Encoder interface works for unsupported types like func(). 300 | {val: undecodableEncoder(func() {}), output: "F5F5F5"}, 301 | 302 | // Verify that pointer method testEncoder.EncodeRLP is called for 303 | // addressable non-pointer values. 304 | {val: &struct{ TE testEncoder }{testEncoder{}}, output: "CA00010001000100010001"}, 305 | {val: &struct{ TE testEncoder }{testEncoder{errors.New("test error")}}, error: "test error"}, 306 | 307 | // Verify the error for non-addressable non-pointer Encoder. 308 | {val: testEncoder{}, error: "rlp: unadressable value of type rlp.testEncoder, EncodeRLP is pointer method"}, 309 | 310 | // Verify Encoder takes precedence over []byte. 311 | {val: []byteEncoder{0, 1, 2, 3, 4}, output: "C5C0C0C0C0C0"}, 312 | } 313 | 314 | func runEncTests(t *testing.T, f func(val interface{}) ([]byte, error)) { 315 | for i, test := range encTests { 316 | output, err := f(test.val) 317 | if err != nil && test.error == "" { 318 | t.Errorf("test %d: unexpected error: %v\nvalue %#v\ntype %T", 319 | i, err, test.val, test.val) 320 | continue 321 | } 322 | if test.error != "" && fmt.Sprint(err) != test.error { 323 | t.Errorf("test %d: error mismatch\ngot %v\nwant %v\nvalue %#v\ntype %T", 324 | i, err, test.error, test.val, test.val) 325 | continue 326 | } 327 | if err == nil && !bytes.Equal(output, unhex(test.output)) { 328 | t.Errorf("test %d: output mismatch:\ngot %X\nwant %s\nvalue %#v\ntype %T", 329 | i, output, test.output, test.val, test.val) 330 | } 331 | } 332 | } 333 | 334 | func TestEncode(t *testing.T) { 335 | runEncTests(t, func(val interface{}) ([]byte, error) { 336 | b := new(bytes.Buffer) 337 | err := Encode(b, val) 338 | return b.Bytes(), err 339 | }) 340 | } 341 | 342 | func TestEncodeToBytes(t *testing.T) { 343 | runEncTests(t, EncodeToBytes) 344 | } 345 | 346 | func TestEncodeToReader(t *testing.T) { 347 | runEncTests(t, func(val interface{}) ([]byte, error) { 348 | _, r, err := EncodeToReader(val) 349 | if err != nil { 350 | return nil, err 351 | } 352 | return ioutil.ReadAll(r) 353 | }) 354 | } 355 | 356 | func TestEncodeToReaderPiecewise(t *testing.T) { 357 | runEncTests(t, func(val interface{}) ([]byte, error) { 358 | size, r, err := EncodeToReader(val) 359 | if err != nil { 360 | return nil, err 361 | } 362 | 363 | // read output piecewise 364 | output := make([]byte, size) 365 | for start, end := 0, 0; start < size; start = end { 366 | if remaining := size - start; remaining < 3 { 367 | end += remaining 368 | } else { 369 | end = start + 3 370 | } 371 | n, err := r.Read(output[start:end]) 372 | end = start + n 373 | if err == io.EOF { 374 | break 375 | } else if err != nil { 376 | return nil, err 377 | } 378 | } 379 | return output, nil 380 | }) 381 | } 382 | 383 | // This is a regression test verifying that encReader 384 | // returns its encbuf to the pool only once. 385 | func TestEncodeToReaderReturnToPool(t *testing.T) { 386 | buf := make([]byte, 50) 387 | wg := new(sync.WaitGroup) 388 | for i := 0; i < 5; i++ { 389 | wg.Add(1) 390 | go func() { 391 | for i := 0; i < 1000; i++ { 392 | _, r, _ := EncodeToReader("foo") 393 | ioutil.ReadAll(r) 394 | r.Read(buf) 395 | r.Read(buf) 396 | r.Read(buf) 397 | r.Read(buf) 398 | } 399 | wg.Done() 400 | }() 401 | } 402 | wg.Wait() 403 | } 404 | -------------------------------------------------------------------------------- /rlp/encoder_example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package rlp 18 | 19 | import ( 20 | "fmt" 21 | "io" 22 | ) 23 | 24 | type MyCoolType struct { 25 | Name string 26 | a, b uint 27 | } 28 | 29 | // EncodeRLP writes x as RLP list [a, b] that omits the Name field. 30 | func (x *MyCoolType) EncodeRLP(w io.Writer) (err error) { 31 | return Encode(w, []uint{x.a, x.b}) 32 | } 33 | 34 | func ExampleEncoder() { 35 | var t *MyCoolType // t is nil pointer to MyCoolType 36 | bytes, _ := EncodeToBytes(t) 37 | fmt.Printf("%v → %X\n", t, bytes) 38 | 39 | t = &MyCoolType{Name: "foobar", a: 5, b: 6} 40 | bytes, _ = EncodeToBytes(t) 41 | fmt.Printf("%v → %X\n", t, bytes) 42 | 43 | // Output: 44 | // → C0 45 | // &{foobar 5 6} → C20506 46 | } 47 | -------------------------------------------------------------------------------- /rlp/raw.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package rlp 18 | 19 | import ( 20 | "io" 21 | "reflect" 22 | ) 23 | 24 | // RawValue represents an encoded RLP value and can be used to delay 25 | // RLP decoding or to precompute an encoding. Note that the decoder does 26 | // not verify whether the content of RawValues is valid RLP. 27 | type RawValue []byte 28 | 29 | var rawValueType = reflect.TypeOf(RawValue{}) 30 | 31 | // ListSize returns the encoded size of an RLP list with the given 32 | // content size. 33 | func ListSize(contentSize uint64) uint64 { 34 | return uint64(headsize(contentSize)) + contentSize 35 | } 36 | 37 | // Split returns the content of first RLP value and any 38 | // bytes after the value as subslices of b. 39 | func Split(b []byte) (k Kind, content, rest []byte, err error) { 40 | k, ts, cs, err := readKind(b) 41 | if err != nil { 42 | return 0, nil, b, err 43 | } 44 | return k, b[ts : ts+cs], b[ts+cs:], nil 45 | } 46 | 47 | // SplitString splits b into the content of an RLP string 48 | // and any remaining bytes after the string. 49 | func SplitString(b []byte) (content, rest []byte, err error) { 50 | k, content, rest, err := Split(b) 51 | if err != nil { 52 | return nil, b, err 53 | } 54 | if k == List { 55 | return nil, b, ErrExpectedString 56 | } 57 | return content, rest, nil 58 | } 59 | 60 | // SplitList splits b into the content of a list and any remaining 61 | // bytes after the list. 62 | func SplitList(b []byte) (content, rest []byte, err error) { 63 | k, content, rest, err := Split(b) 64 | if err != nil { 65 | return nil, b, err 66 | } 67 | if k != List { 68 | return nil, b, ErrExpectedList 69 | } 70 | return content, rest, nil 71 | } 72 | 73 | // CountValues counts the number of encoded values in b. 74 | func CountValues(b []byte) (int, error) { 75 | i := 0 76 | for ; len(b) > 0; i++ { 77 | _, tagsize, size, err := readKind(b) 78 | if err != nil { 79 | return 0, err 80 | } 81 | b = b[tagsize+size:] 82 | } 83 | return i, nil 84 | } 85 | 86 | func readKind(buf []byte) (k Kind, tagsize, contentsize uint64, err error) { 87 | if len(buf) == 0 { 88 | return 0, 0, 0, io.ErrUnexpectedEOF 89 | } 90 | b := buf[0] 91 | switch { 92 | case b < 0x80: 93 | k = Byte 94 | tagsize = 0 95 | contentsize = 1 96 | case b < 0xB8: 97 | k = String 98 | tagsize = 1 99 | contentsize = uint64(b - 0x80) 100 | // Reject strings that should've been single bytes. 101 | if contentsize == 1 && len(buf) > 1 && buf[1] < 128 { 102 | return 0, 0, 0, ErrCanonSize 103 | } 104 | case b < 0xC0: 105 | k = String 106 | tagsize = uint64(b-0xB7) + 1 107 | contentsize, err = readSize(buf[1:], b-0xB7) 108 | case b < 0xF8: 109 | k = List 110 | tagsize = 1 111 | contentsize = uint64(b - 0xC0) 112 | default: 113 | k = List 114 | tagsize = uint64(b-0xF7) + 1 115 | contentsize, err = readSize(buf[1:], b-0xF7) 116 | } 117 | if err != nil { 118 | return 0, 0, 0, err 119 | } 120 | // Reject values larger than the input slice. 121 | if contentsize > uint64(len(buf))-tagsize { 122 | return 0, 0, 0, ErrValueTooLarge 123 | } 124 | return k, tagsize, contentsize, err 125 | } 126 | 127 | func readSize(b []byte, slen byte) (uint64, error) { 128 | if int(slen) > len(b) { 129 | return 0, io.ErrUnexpectedEOF 130 | } 131 | var s uint64 132 | switch slen { 133 | case 1: 134 | s = uint64(b[0]) 135 | case 2: 136 | s = uint64(b[0])<<8 | uint64(b[1]) 137 | case 3: 138 | s = uint64(b[0])<<16 | uint64(b[1])<<8 | uint64(b[2]) 139 | case 4: 140 | s = uint64(b[0])<<24 | uint64(b[1])<<16 | uint64(b[2])<<8 | uint64(b[3]) 141 | case 5: 142 | s = uint64(b[0])<<32 | uint64(b[1])<<24 | uint64(b[2])<<16 | uint64(b[3])<<8 | uint64(b[4]) 143 | case 6: 144 | s = uint64(b[0])<<40 | uint64(b[1])<<32 | uint64(b[2])<<24 | uint64(b[3])<<16 | uint64(b[4])<<8 | uint64(b[5]) 145 | case 7: 146 | s = uint64(b[0])<<48 | uint64(b[1])<<40 | uint64(b[2])<<32 | uint64(b[3])<<24 | uint64(b[4])<<16 | uint64(b[5])<<8 | uint64(b[6]) 147 | case 8: 148 | s = uint64(b[0])<<56 | uint64(b[1])<<48 | uint64(b[2])<<40 | uint64(b[3])<<32 | uint64(b[4])<<24 | uint64(b[5])<<16 | uint64(b[6])<<8 | uint64(b[7]) 149 | } 150 | // Reject sizes < 56 (shouldn't have separate size) and sizes with 151 | // leading zero bytes. 152 | if s < 56 || b[0] == 0 { 153 | return 0, ErrCanonSize 154 | } 155 | return s, nil 156 | } 157 | -------------------------------------------------------------------------------- /rlp/raw_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package rlp 18 | 19 | import ( 20 | "bytes" 21 | "io" 22 | "reflect" 23 | "testing" 24 | ) 25 | 26 | func TestCountValues(t *testing.T) { 27 | tests := []struct { 28 | input string // note: spaces in input are stripped by unhex 29 | count int 30 | err error 31 | }{ 32 | // simple cases 33 | {"", 0, nil}, 34 | {"00", 1, nil}, 35 | {"80", 1, nil}, 36 | {"C0", 1, nil}, 37 | {"01 02 03", 3, nil}, 38 | {"01 C406070809 02", 3, nil}, 39 | {"820101 820202 8403030303 04", 4, nil}, 40 | 41 | // size errors 42 | {"8142", 0, ErrCanonSize}, 43 | {"01 01 8142", 0, ErrCanonSize}, 44 | {"02 84020202", 0, ErrValueTooLarge}, 45 | 46 | { 47 | input: "A12000BF49F440A1CD0527E4D06E2765654C0F56452257516D793A9B8D604DCFDF2AB853F851808D10000000000000000000000000A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470", 48 | count: 2, 49 | }, 50 | } 51 | for i, test := range tests { 52 | count, err := CountValues(unhex(test.input)) 53 | if count != test.count { 54 | t.Errorf("test %d: count mismatch, got %d want %d\ninput: %s", i, count, test.count, test.input) 55 | } 56 | if !reflect.DeepEqual(err, test.err) { 57 | t.Errorf("test %d: err mismatch, got %q want %q\ninput: %s", i, err, test.err, test.input) 58 | } 59 | } 60 | } 61 | 62 | func TestSplitTypes(t *testing.T) { 63 | if _, _, err := SplitString(unhex("C100")); err != ErrExpectedString { 64 | t.Errorf("SplitString returned %q, want %q", err, ErrExpectedString) 65 | } 66 | if _, _, err := SplitList(unhex("01")); err != ErrExpectedList { 67 | t.Errorf("SplitString returned %q, want %q", err, ErrExpectedList) 68 | } 69 | if _, _, err := SplitList(unhex("81FF")); err != ErrExpectedList { 70 | t.Errorf("SplitString returned %q, want %q", err, ErrExpectedList) 71 | } 72 | } 73 | 74 | func TestSplit(t *testing.T) { 75 | tests := []struct { 76 | input string 77 | kind Kind 78 | val, rest string 79 | err error 80 | }{ 81 | {input: "01FFFF", kind: Byte, val: "01", rest: "FFFF"}, 82 | {input: "80FFFF", kind: String, val: "", rest: "FFFF"}, 83 | {input: "C3010203", kind: List, val: "010203"}, 84 | 85 | // errors 86 | {input: "", err: io.ErrUnexpectedEOF}, 87 | 88 | {input: "8141", err: ErrCanonSize, rest: "8141"}, 89 | {input: "B800", err: ErrCanonSize, rest: "B800"}, 90 | {input: "B802FFFF", err: ErrCanonSize, rest: "B802FFFF"}, 91 | {input: "B90000", err: ErrCanonSize, rest: "B90000"}, 92 | {input: "B90055", err: ErrCanonSize, rest: "B90055"}, 93 | {input: "BA0002FFFF", err: ErrCanonSize, rest: "BA0002FFFF"}, 94 | {input: "F800", err: ErrCanonSize, rest: "F800"}, 95 | {input: "F90000", err: ErrCanonSize, rest: "F90000"}, 96 | {input: "F90055", err: ErrCanonSize, rest: "F90055"}, 97 | {input: "FA0002FFFF", err: ErrCanonSize, rest: "FA0002FFFF"}, 98 | 99 | {input: "81", err: ErrValueTooLarge, rest: "81"}, 100 | {input: "8501010101", err: ErrValueTooLarge, rest: "8501010101"}, 101 | {input: "C60607080902", err: ErrValueTooLarge, rest: "C60607080902"}, 102 | 103 | // size check overflow 104 | {input: "BFFFFFFFFFFFFFFFFF", err: ErrValueTooLarge, rest: "BFFFFFFFFFFFFFFFFF"}, 105 | {input: "FFFFFFFFFFFFFFFFFF", err: ErrValueTooLarge, rest: "FFFFFFFFFFFFFFFFFF"}, 106 | 107 | { 108 | input: "B838FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 109 | err: ErrValueTooLarge, 110 | rest: "B838FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 111 | }, 112 | { 113 | input: "F838FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 114 | err: ErrValueTooLarge, 115 | rest: "F838FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 116 | }, 117 | 118 | // a few bigger values, just for kicks 119 | { 120 | input: "F839FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 121 | kind: List, 122 | val: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 123 | rest: "", 124 | }, 125 | { 126 | input: "F90211A060EF29F20CC1007AE6E9530AEE16F4B31F8F1769A2D1264EC995C6D1241868D6A07C62AB8AC9838F5F5877B20BB37B387BC2106E97A3D52172CBEDB5EE17C36008A00EAB6B7324AADC0F6047C6AFC8229F09F7CF451B51D67C8DFB08D49BA8C3C626A04453343B2F3A6E42FCF87948F88AF7C8FC16D0C2735CBA7F026836239AB2C15FA024635C7291C882CE4C0763760C1A362DFC3FFCD802A55722236DE058D74202ACA0A220C808DE10F55E40AB25255201CFF009EA181D3906638E944EE2BF34049984A08D325AB26796F1CCB470F69C0F842501DC35D368A0C2575B2D243CFD1E8AB0FDA0B5298FF60DA5069463D610513C9F04F24051348391A143AFFAB7197DFACDEA72A02D2A7058A4463F8FB69378369E11EF33AE3252E2DB86CB545B36D3C26DDECE5AA0888F97BCA8E0BD83DC5B3B91CFF5FAF2F66F9501010682D67EF4A3B4E66115FBA0E8175A60C93BE9ED02921958F0EA55DA0FB5E4802AF5846147BAD92BC2D8AF26A08B3376FF433F3A4250FA64B7F804004CAC5807877D91C4427BD1CD05CF912ED8A09B32EF0F03BD13C37FF950C0CCCEFCCDD6669F2E7F2AA5CB859928E84E29763EA09BBA5E46610C8C8B1F8E921E5691BF8C7E40D75825D5EA3217AA9C3A8A355F39A0EEB95BC78251CCCEC54A97F19755C4A59A293544EEE6119AFA50531211E53C4FA00B6E86FE150BF4A9E0FEEE9C90F5465E617A861BB5E357F942881EE762212E2580", 127 | kind: List, 128 | val: "A060EF29F20CC1007AE6E9530AEE16F4B31F8F1769A2D1264EC995C6D1241868D6A07C62AB8AC9838F5F5877B20BB37B387BC2106E97A3D52172CBEDB5EE17C36008A00EAB6B7324AADC0F6047C6AFC8229F09F7CF451B51D67C8DFB08D49BA8C3C626A04453343B2F3A6E42FCF87948F88AF7C8FC16D0C2735CBA7F026836239AB2C15FA024635C7291C882CE4C0763760C1A362DFC3FFCD802A55722236DE058D74202ACA0A220C808DE10F55E40AB25255201CFF009EA181D3906638E944EE2BF34049984A08D325AB26796F1CCB470F69C0F842501DC35D368A0C2575B2D243CFD1E8AB0FDA0B5298FF60DA5069463D610513C9F04F24051348391A143AFFAB7197DFACDEA72A02D2A7058A4463F8FB69378369E11EF33AE3252E2DB86CB545B36D3C26DDECE5AA0888F97BCA8E0BD83DC5B3B91CFF5FAF2F66F9501010682D67EF4A3B4E66115FBA0E8175A60C93BE9ED02921958F0EA55DA0FB5E4802AF5846147BAD92BC2D8AF26A08B3376FF433F3A4250FA64B7F804004CAC5807877D91C4427BD1CD05CF912ED8A09B32EF0F03BD13C37FF950C0CCCEFCCDD6669F2E7F2AA5CB859928E84E29763EA09BBA5E46610C8C8B1F8E921E5691BF8C7E40D75825D5EA3217AA9C3A8A355F39A0EEB95BC78251CCCEC54A97F19755C4A59A293544EEE6119AFA50531211E53C4FA00B6E86FE150BF4A9E0FEEE9C90F5465E617A861BB5E357F942881EE762212E2580", 129 | rest: "", 130 | }, 131 | { 132 | input: "F877A12000BF49F440A1CD0527E4D06E2765654C0F56452257516D793A9B8D604DCFDF2AB853F851808D10000000000000000000000000A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470", 133 | kind: List, 134 | val: "A12000BF49F440A1CD0527E4D06E2765654C0F56452257516D793A9B8D604DCFDF2AB853F851808D10000000000000000000000000A056E81F171BCC55A6FF8345E692C0F86E5B48E01B996CADC001622FB5E363B421A0C5D2460186F7233C927E7DB2DCC703C0E500B653CA82273B7BFAD8045D85A470", 135 | rest: "", 136 | }, 137 | } 138 | 139 | for i, test := range tests { 140 | kind, val, rest, err := Split(unhex(test.input)) 141 | if kind != test.kind { 142 | t.Errorf("test %d: kind mismatch: got %v, want %v", i, kind, test.kind) 143 | } 144 | if !bytes.Equal(val, unhex(test.val)) { 145 | t.Errorf("test %d: val mismatch: got %x, want %s", i, val, test.val) 146 | } 147 | if !bytes.Equal(rest, unhex(test.rest)) { 148 | t.Errorf("test %d: rest mismatch: got %x, want %s", i, rest, test.rest) 149 | } 150 | if err != test.err { 151 | t.Errorf("test %d: error mismatch: got %q, want %q", i, err, test.err) 152 | } 153 | } 154 | } 155 | 156 | func TestReadSize(t *testing.T) { 157 | tests := []struct { 158 | input string 159 | slen byte 160 | size uint64 161 | err error 162 | }{ 163 | {input: "", slen: 1, err: io.ErrUnexpectedEOF}, 164 | {input: "FF", slen: 2, err: io.ErrUnexpectedEOF}, 165 | {input: "00", slen: 1, err: ErrCanonSize}, 166 | {input: "36", slen: 1, err: ErrCanonSize}, 167 | {input: "37", slen: 1, err: ErrCanonSize}, 168 | {input: "38", slen: 1, size: 0x38}, 169 | {input: "FF", slen: 1, size: 0xFF}, 170 | {input: "FFFF", slen: 2, size: 0xFFFF}, 171 | {input: "FFFFFF", slen: 3, size: 0xFFFFFF}, 172 | {input: "FFFFFFFF", slen: 4, size: 0xFFFFFFFF}, 173 | {input: "FFFFFFFFFF", slen: 5, size: 0xFFFFFFFFFF}, 174 | {input: "FFFFFFFFFFFF", slen: 6, size: 0xFFFFFFFFFFFF}, 175 | {input: "FFFFFFFFFFFFFF", slen: 7, size: 0xFFFFFFFFFFFFFF}, 176 | {input: "FFFFFFFFFFFFFFFF", slen: 8, size: 0xFFFFFFFFFFFFFFFF}, 177 | {input: "0102", slen: 2, size: 0x0102}, 178 | {input: "010203", slen: 3, size: 0x010203}, 179 | {input: "01020304", slen: 4, size: 0x01020304}, 180 | {input: "0102030405", slen: 5, size: 0x0102030405}, 181 | {input: "010203040506", slen: 6, size: 0x010203040506}, 182 | {input: "01020304050607", slen: 7, size: 0x01020304050607}, 183 | {input: "0102030405060708", slen: 8, size: 0x0102030405060708}, 184 | } 185 | 186 | for _, test := range tests { 187 | size, err := readSize(unhex(test.input), test.slen) 188 | if err != test.err { 189 | t.Errorf("readSize(%s, %d): error mismatch: got %q, want %q", test.input, test.slen, err, test.err) 190 | continue 191 | } 192 | if size != test.size { 193 | t.Errorf("readSize(%s, %d): size mismatch: got %#x, want %#x", test.input, test.slen, size, test.size) 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /rlp/typecache.go: -------------------------------------------------------------------------------- 1 | // Copyright 2014 The go-ethereum Authors 2 | // This file is part of the go-ethereum library. 3 | // 4 | // The go-ethereum library is free software: you can redistribute it and/or modify 5 | // it under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 3 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // The go-ethereum library is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU Lesser General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU Lesser General Public License 15 | // along with the go-ethereum library. If not, see . 16 | 17 | package rlp 18 | 19 | import ( 20 | "fmt" 21 | "reflect" 22 | "strings" 23 | "sync" 24 | ) 25 | 26 | var ( 27 | typeCacheMutex sync.RWMutex 28 | typeCache = make(map[typekey]*typeinfo) 29 | ) 30 | 31 | type typeinfo struct { 32 | decoder decoder 33 | decoderErr error // error from makeDecoder 34 | writer writer 35 | writerErr error // error from makeWriter 36 | } 37 | 38 | // tags represents struct tags. 39 | type tags struct { 40 | // rlp:"nil" controls whether empty input results in a nil pointer. 41 | nilOK bool 42 | 43 | // This controls whether nil pointers are encoded/decoded as empty strings 44 | // or empty lists. 45 | nilKind Kind 46 | 47 | // rlp:"tail" controls whether this field swallows additional list 48 | // elements. It can only be set for the last field, which must be 49 | // of slice type. 50 | tail bool 51 | 52 | // rlp:"-" ignores fields. 53 | ignored bool 54 | } 55 | 56 | // typekey is the key of a type in typeCache. It includes the struct tags because 57 | // they might generate a different decoder. 58 | type typekey struct { 59 | reflect.Type 60 | tags 61 | } 62 | 63 | type decoder func(*Stream, reflect.Value) error 64 | 65 | type writer func(reflect.Value, *encbuf) error 66 | 67 | func cachedDecoder(typ reflect.Type) (decoder, error) { 68 | info := cachedTypeInfo(typ, tags{}) 69 | return info.decoder, info.decoderErr 70 | } 71 | 72 | func cachedWriter(typ reflect.Type) (writer, error) { 73 | info := cachedTypeInfo(typ, tags{}) 74 | return info.writer, info.writerErr 75 | } 76 | 77 | func cachedTypeInfo(typ reflect.Type, tags tags) *typeinfo { 78 | typeCacheMutex.RLock() 79 | info := typeCache[typekey{typ, tags}] 80 | typeCacheMutex.RUnlock() 81 | if info != nil { 82 | return info 83 | } 84 | // not in the cache, need to generate info for this type. 85 | typeCacheMutex.Lock() 86 | defer typeCacheMutex.Unlock() 87 | return cachedTypeInfo1(typ, tags) 88 | } 89 | 90 | func cachedTypeInfo1(typ reflect.Type, tags tags) *typeinfo { 91 | key := typekey{typ, tags} 92 | info := typeCache[key] 93 | if info != nil { 94 | // another goroutine got the write lock first 95 | return info 96 | } 97 | // put a dummy value into the cache before generating. 98 | // if the generator tries to lookup itself, it will get 99 | // the dummy value and won't call itself recursively. 100 | info = new(typeinfo) 101 | typeCache[key] = info 102 | info.generate(typ, tags) 103 | return info 104 | } 105 | 106 | type field struct { 107 | index int 108 | info *typeinfo 109 | } 110 | 111 | func structFields(typ reflect.Type) (fields []field, err error) { 112 | lastPublic := lastPublicField(typ) 113 | for i := 0; i < typ.NumField(); i++ { 114 | if f := typ.Field(i); f.PkgPath == "" { // exported 115 | tags, err := parseStructTag(typ, i, lastPublic) 116 | if err != nil { 117 | return nil, err 118 | } 119 | if tags.ignored { 120 | continue 121 | } 122 | info := cachedTypeInfo1(f.Type, tags) 123 | fields = append(fields, field{i, info}) 124 | } 125 | } 126 | return fields, nil 127 | } 128 | 129 | type structFieldError struct { 130 | typ reflect.Type 131 | field int 132 | err error 133 | } 134 | 135 | func (e structFieldError) Error() string { 136 | return fmt.Sprintf("%v (struct field %v.%s)", e.err, e.typ, e.typ.Field(e.field).Name) 137 | } 138 | 139 | type structTagError struct { 140 | typ reflect.Type 141 | field, tag, err string 142 | } 143 | 144 | func (e structTagError) Error() string { 145 | return fmt.Sprintf("rlp: invalid struct tag %q for %v.%s (%s)", e.tag, e.typ, e.field, e.err) 146 | } 147 | 148 | func parseStructTag(typ reflect.Type, fi, lastPublic int) (tags, error) { 149 | f := typ.Field(fi) 150 | var ts tags 151 | for _, t := range strings.Split(f.Tag.Get("rlp"), ",") { 152 | switch t = strings.TrimSpace(t); t { 153 | case "": 154 | case "-": 155 | ts.ignored = true 156 | case "nil", "nilString", "nilList": 157 | ts.nilOK = true 158 | if f.Type.Kind() != reflect.Ptr { 159 | return ts, structTagError{typ, f.Name, t, "field is not a pointer"} 160 | } 161 | switch t { 162 | case "nil": 163 | ts.nilKind = defaultNilKind(f.Type.Elem()) 164 | case "nilString": 165 | ts.nilKind = String 166 | case "nilList": 167 | ts.nilKind = List 168 | } 169 | case "tail": 170 | ts.tail = true 171 | if fi != lastPublic { 172 | return ts, structTagError{typ, f.Name, t, "must be on last field"} 173 | } 174 | if f.Type.Kind() != reflect.Slice { 175 | return ts, structTagError{typ, f.Name, t, "field type is not slice"} 176 | } 177 | default: 178 | return ts, fmt.Errorf("rlp: unknown struct tag %q on %v.%s", t, typ, f.Name) 179 | } 180 | } 181 | return ts, nil 182 | } 183 | 184 | func lastPublicField(typ reflect.Type) int { 185 | last := 0 186 | for i := 0; i < typ.NumField(); i++ { 187 | if typ.Field(i).PkgPath == "" { 188 | last = i 189 | } 190 | } 191 | return last 192 | } 193 | 194 | func (i *typeinfo) generate(typ reflect.Type, tags tags) { 195 | i.decoder, i.decoderErr = makeDecoder(typ, tags) 196 | i.writer, i.writerErr = makeWriter(typ, tags) 197 | } 198 | 199 | // defaultNilKind determines whether a nil pointer to typ encodes/decodes 200 | // as an empty string or empty list. 201 | func defaultNilKind(typ reflect.Type) Kind { 202 | k := typ.Kind() 203 | if isUint(k) || k == reflect.String || k == reflect.Bool || isByteArray(typ) { 204 | return String 205 | } 206 | return List 207 | } 208 | 209 | func isUint(k reflect.Kind) bool { 210 | return k >= reflect.Uint && k <= reflect.Uintptr 211 | } 212 | 213 | func isByteArray(typ reflect.Type) bool { 214 | return (typ.Kind() == reflect.Slice || typ.Kind() == reflect.Array) && isByte(typ.Elem()) 215 | } 216 | -------------------------------------------------------------------------------- /secp256k1.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import "math/big" 6 | 7 | // secp256k1 椭圆曲线参数 8 | const ( 9 | // secp256k1 模素数的不同形式 10 | // SECP256K1_P0 是指数组成的常量表达式(无法在运行时直接使用) 11 | // SECP256K1_P1 是最终的十进制格式(无法在运行时直接使用) 12 | // SECP256K1_P2 是十进制的字符串面值 13 | // SECP256K1_P0/SECP256K1_P1 和 SECP256K1_P3 无法在运行时直接比较 14 | // 15 | // var P, _ = new(big.Int).SetString(SECP256K1_P2, 10) 16 | // assert(t, P.String() == SECP256K1_P2) 17 | // 18 | SECP256K1_P0 = 1<<256 - 1<<32 - 1<<9 - 1<<8 - 1<<7 - 1<<6 - 1<<4 - 1 19 | SECP256K1_P1 = 115792089237316195423570985008687907853269984665640564039457584007908834671663 20 | SECP256K1_P2 = "115792089237316195423570985008687907853269984665640564039457584007908834671663" 21 | ) 22 | 23 | // secp256k1 模素数(常量) 24 | var ( 25 | _SECP256K1_P, _ = new(big.Int).SetString(SECP256K1_P2, 10) 26 | ) 27 | 28 | // 根据 x 计算 secp256k1 椭圆曲线函数的值 29 | // secp256k1_Fx(x) = (x^3 + 7)% P 30 | func Secp256k1_Fx(x *big.Int) *big.Int { 31 | v := new(big.Int) 32 | v.Exp(x, big.NewInt(3), nil).Add(v, big.NewInt(7)).Mod(v, _SECP256K1_P) 33 | return v 34 | } 35 | 36 | // 根据 y 计算 secp256k1 椭圆曲线函数的值 37 | // secp256k1_Fy(y) = (y^2)% P 38 | func Secp256k1_Fy(y *big.Int) *big.Int { 39 | v := new(big.Int) 40 | v.Exp(y, big.NewInt(2), nil).Mod(v, _SECP256K1_P) 41 | return v 42 | } 43 | 44 | // secp256k1_Fx(x) - secp256k1_Fy(y) 45 | // 如果结果为0, 表示(x,y)点在椭圆曲线上(z.BitLen() == 0) 46 | func Secp256k1_Fx_Fy(x, y *big.Int) *big.Int { 47 | v := new(big.Int) 48 | v.Sub(Secp256k1_Fx(x), Secp256k1_Fy(y)).Mod(v, _SECP256K1_P) 49 | return v 50 | } 51 | -------------------------------------------------------------------------------- /secp256k1_test.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import ( 6 | "math/big" 7 | "testing" 8 | ) 9 | 10 | func TestSECP256K1_P(t *testing.T) { 11 | tAssert(t, SECP256K1_P0 == SECP256K1_P1) 12 | 13 | var p, _ = new(big.Int).SetString(SECP256K1_P2, 10) 14 | tAssert(t, p.String() == SECP256K1_P2) 15 | } 16 | 17 | func TestSecp256k1_Fx_Fy(t *testing.T) { 18 | // 测试数据来自《精通以太坊》 19 | // https://github.com/ethereumbook/ethereumbook/blob/develop/04keys-addresses.asciidoc 20 | x, _ := new(big.Int).SetString( 21 | "49790390825249384486033144355916864607616083520101638681403973749255924539515", 22 | 10, 23 | ) 24 | y, _ := new(big.Int).SetString( 25 | "59574132161899900045862086493921015780032175291755807399284007721050341297360", 26 | 10, 27 | ) 28 | 29 | z := Secp256k1_Fx_Fy(x, y) 30 | tAssert(t, z.BitLen() == 0) 31 | } 32 | -------------------------------------------------------------------------------- /sig.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import ( 6 | "crypto/elliptic" 7 | "fmt" 8 | "math/big" 9 | 10 | "github.com/chai2010/ethutil/btcec" 11 | ) 12 | 13 | var ( 14 | secp256k1N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) 15 | secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2)) 16 | ) 17 | 18 | // 私钥签名 19 | // 涉及的字符串都是十六进制编码格式 20 | func Sign(privateKey string, hash []byte) (sig []byte, err error) { 21 | if len(hash) != 32 { 22 | return nil, fmt.Errorf("hash is required to be exactly 32 bytes (%d)", len(hash)) 23 | } 24 | 25 | var c elliptic.Curve = btcec.S256() 26 | var priv = new(btcec.PrivateKey) 27 | 28 | priv.PublicKey.Curve = c 29 | priv.D = Hex(privateKey).MustBigint() 30 | priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(Hex(privateKey).MustBytes()) 31 | 32 | sig, err = btcec.SignCompact(btcec.S256(), priv, hash, false) 33 | if err != nil { 34 | return nil, err 35 | } 36 | 37 | // r: sig[0:32] 38 | // s: sig[32:64] 39 | // v: sig[64] 40 | v := sig[0] - 27 41 | copy(sig, sig[1:]) 42 | sig[64] = v 43 | 44 | // ok 45 | return sig, nil 46 | } 47 | 48 | // 公钥验证签名 49 | // 涉及的字符串都是十六进制编码格式 50 | func VerifySignature(pubkey string, hash, signature []byte) bool { 51 | if len(signature) < 64 { 52 | return false 53 | } 54 | sig := &btcec.Signature{ 55 | R: new(big.Int).SetBytes(signature[:32]), 56 | S: new(big.Int).SetBytes(signature[32:64]), 57 | } 58 | key, err := btcec.ParsePubKey( 59 | Hex(pubkey).MustBytes(), btcec.S256(), 60 | ) 61 | if err != nil { 62 | return false 63 | } 64 | // Reject malleable signatures. libsecp256k1 does this check but btcec doesn't. 65 | if sig.S.Cmp(secp256k1halfN) > 0 { 66 | return false 67 | } 68 | return sig.Verify(hash, key) 69 | } 70 | 71 | // 签名导出公钥 72 | func SigToPub(hash, sig []byte) (publicKey string, err error) { 73 | // 将v移到开头, 并加上27, 对应比特币的签名格式 74 | btcsig := make([]byte, 64+1) 75 | btcsig[0] = sig[64] + 27 76 | copy(btcsig[1:], sig) 77 | 78 | pub, _, err := btcec.RecoverCompact(btcec.S256(), btcsig, hash) 79 | if err != nil { 80 | return "", err 81 | } 82 | 83 | publicKey = fmt.Sprintf("04%064x%064x", pub.X, pub.Y) 84 | return 85 | } 86 | 87 | // secp256k1 椭圆曲线 88 | func S256() elliptic.Curve { 89 | return btcec.S256() 90 | } 91 | -------------------------------------------------------------------------------- /sig_test.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import "testing" 6 | 7 | func TestVerifySignature(t *testing.T) { 8 | sig := "0x638a54215d80a6713c8d523a6adc4e6e73652d859103a36b700851cb0e61b66b8ebfc1a610c57d732ec6e0a8f06a9a7a28df5051ece514702ff9cdff0b11f454" 9 | key := "0x03ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd3138" 10 | msg := "0xd301ce462d3e639518f482c7f03821fec1e602018630ce621e1e7851c12343a6" 11 | 12 | // func VerifySignature(pubkey, digestHash string, r, s *big.Int) bool { 13 | 14 | _ = sig 15 | _ = key 16 | _ = msg 17 | } 18 | -------------------------------------------------------------------------------- /testdata/v1_test_vector.json: -------------------------------------------------------------------------------- 1 | { 2 | "test1": { 3 | "json": { 4 | "Crypto": { 5 | "cipher": "aes-128-cbc", 6 | "cipherparams": { 7 | "iv": "35337770fc2117994ecdcad026bccff4" 8 | }, 9 | "ciphertext": "6143d3192db8b66eabd693d9c4e414dcfaee52abda451af79ccf474dafb35f1bfc7ea013aa9d2ee35969a1a2e8d752d0", 10 | "kdf": "scrypt", 11 | "kdfparams": { 12 | "dklen": 32, 13 | "n": 262144, 14 | "p": 1, 15 | "r": 8, 16 | "salt": "9afcddebca541253a2f4053391c673ff9fe23097cd8555d149d929e4ccf1257f" 17 | }, 18 | "mac": "3f3d5af884b17a100b0b3232c0636c230a54dc2ac8d986227219b0dd89197644", 19 | "version": "1" 20 | }, 21 | "address": "cb61d5a9c4896fb9658090b597ef0e7be6f7b67e", 22 | "id": "e25f7c1f-d318-4f29-b62c-687190d4d299", 23 | "version": "1" 24 | }, 25 | "password": "g", 26 | "priv": "d1b1178d3529626a1a93e073f65028370d14c7eb0936eb42abef05db6f37ad7d" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /testdata/v3_test_vector.json: -------------------------------------------------------------------------------- 1 | { 2 | "wikipage_test_vector_scrypt": { 3 | "json": { 4 | "crypto" : { 5 | "cipher" : "aes-128-ctr", 6 | "cipherparams" : { 7 | "iv" : "83dbcc02d8ccb40e466191a123791e0e" 8 | }, 9 | "ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c", 10 | "kdf" : "scrypt", 11 | "kdfparams" : { 12 | "dklen" : 32, 13 | "n" : 262144, 14 | "r" : 1, 15 | "p" : 8, 16 | "salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19" 17 | }, 18 | "mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097" 19 | }, 20 | "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6", 21 | "version" : 3 22 | }, 23 | "password": "testpassword", 24 | "priv": "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d" 25 | }, 26 | "wikipage_test_vector_pbkdf2": { 27 | "json": { 28 | "crypto" : { 29 | "cipher" : "aes-128-ctr", 30 | "cipherparams" : { 31 | "iv" : "6087dab2f9fdbbfaddc31a909735c1e6" 32 | }, 33 | "ciphertext" : "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46", 34 | "kdf" : "pbkdf2", 35 | "kdfparams" : { 36 | "c" : 262144, 37 | "dklen" : 32, 38 | "prf" : "hmac-sha256", 39 | "salt" : "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd" 40 | }, 41 | "mac" : "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2" 42 | }, 43 | "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6", 44 | "version" : 3 45 | }, 46 | "password": "testpassword", 47 | "priv": "7a28b5ba57c53603b0b07b56bba752f7784bf506fa95edc395f5cf6c7514fe9d" 48 | }, 49 | "31_byte_key": { 50 | "json": { 51 | "crypto" : { 52 | "cipher" : "aes-128-ctr", 53 | "cipherparams" : { 54 | "iv" : "e0c41130a323adc1446fc82f724bca2f" 55 | }, 56 | "ciphertext" : "9517cd5bdbe69076f9bf5057248c6c050141e970efa36ce53692d5d59a3984", 57 | "kdf" : "scrypt", 58 | "kdfparams" : { 59 | "dklen" : 32, 60 | "n" : 2, 61 | "r" : 8, 62 | "p" : 1, 63 | "salt" : "711f816911c92d649fb4c84b047915679933555030b3552c1212609b38208c63" 64 | }, 65 | "mac" : "d5e116151c6aa71470e67a7d42c9620c75c4d23229847dcc127794f0732b0db5" 66 | }, 67 | "id" : "fecfc4ce-e956-48fd-953b-30f8b52ed66c", 68 | "version" : 3 69 | }, 70 | "password": "foo", 71 | "priv": "fa7b3db73dc7dfdf8c5fbdb796d741e4488628c41fc4febd9160a866ba0f35" 72 | }, 73 | "30_byte_key": { 74 | "json": { 75 | "crypto" : { 76 | "cipher" : "aes-128-ctr", 77 | "cipherparams" : { 78 | "iv" : "3ca92af36ad7c2cd92454c59cea5ef00" 79 | }, 80 | "ciphertext" : "108b7d34f3442fc26ab1ab90ca91476ba6bfa8c00975a49ef9051dc675aa", 81 | "kdf" : "scrypt", 82 | "kdfparams" : { 83 | "dklen" : 32, 84 | "n" : 2, 85 | "r" : 8, 86 | "p" : 1, 87 | "salt" : "d0769e608fb86cda848065642a9c6fa046845c928175662b8e356c77f914cd3b" 88 | }, 89 | "mac" : "75d0e6759f7b3cefa319c3be41680ab6beea7d8328653474bd06706d4cc67420" 90 | }, 91 | "id" : "a37e1559-5955-450d-8075-7b8931b392b2", 92 | "version" : 3 93 | }, 94 | "password": "foo", 95 | "priv": "81c29e8142bb6a81bef5a92bda7a8328a5c85bb2f9542e76f9b0f94fc018" 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /testing_test.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import "testing" 6 | 7 | func tAssert(tb testing.TB, ok bool, a ...interface{}) { 8 | if !ok { 9 | tb.Helper() 10 | tb.Fatal(a...) 11 | } 12 | } 13 | 14 | func tAssertf(tb testing.TB, ok bool, format string, a ...interface{}) { 15 | if !ok { 16 | tb.Helper() 17 | tb.Fatalf(format, a...) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tx.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import ( 6 | "bytes" 7 | "math/big" 8 | 9 | "golang.org/x/crypto/sha3" 10 | 11 | "github.com/chai2010/ethutil/rlp" 12 | ) 13 | 14 | // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md 15 | // FORK_BLKNUM: 2,675,000 16 | // CHAIN_ID: 1 (main net) 17 | 18 | // EIP分叉时区块高度 19 | // 之后交易的签名数据从6个变成9个 20 | const EIP155_FORK_BLKNUM = 2675000 21 | 22 | // 交易数据 23 | type TxData struct { 24 | Nonce uint64 25 | GasPrice string 26 | GasLimit string 27 | To string // 0地址 表示合约 28 | Value string 29 | Data []byte 30 | 31 | V *big.Int 32 | R *big.Int 33 | S *big.Int 34 | } 35 | 36 | // RLP编码签名过后的交易 37 | func (p *TxData) EncodeRLP() []byte { 38 | var buf bytes.Buffer 39 | rlp.Encode(&buf, p) 40 | return buf.Bytes() 41 | } 42 | 43 | // 生成最新规范的要签名编码数据 44 | func (p *TxData) SigData() []byte { 45 | // 输出EIP55之后的格式, 不包含 chainId 46 | return p.sigData(0, EIP155_FORK_BLKNUM) 47 | } 48 | 49 | // 生成要签名编码数据(支持旧规范) 50 | // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md 51 | func (p *TxData) SigDataEx(chainId, blockNumber int64) []byte { 52 | // 输出EIP55之后的格式, 包含 chainId 53 | return p.sigData(chainId, blockNumber) 54 | } 55 | 56 | // 计算交易的哈希(可用于后续的签名) 57 | func (p *TxData) Hash() []byte { 58 | keccak256 := sha3.NewLegacyKeccak256() 59 | keccak256.Write(p.SigData()) 60 | return keccak256.Sum(nil) 61 | } 62 | 63 | // 计算交易的哈希 64 | func (p *TxData) HashEx(chainId, blockNumber int64) []byte { 65 | keccak256 := sha3.NewLegacyKeccak256() 66 | keccak256.Write(p.SigDataEx(chainId, blockNumber)) 67 | return keccak256.Sum(nil) 68 | } 69 | 70 | // 输出用于签名hash的RLP编码数据 71 | func (p *TxData) sigData(chainId, blockNumber int64) []byte { 72 | // 解码Hex格式地址 73 | // 如果是零地址, 用 nil 表示 74 | var p_To *[20]byte 75 | if !Hex(p.To).IsZero() { 76 | var address [20]byte 77 | copy(address[:], Hex(p.To).MustBytes()) 78 | p_To = &address 79 | } 80 | 81 | // 初始规范只编码交易的6个信息 82 | if blockNumber < EIP155_FORK_BLKNUM { 83 | var txData = &struct { 84 | Nonce uint64 85 | GasPrice *big.Int 86 | GasLimit uint64 87 | To *[20]byte // nil 表示合约 88 | Value *big.Int 89 | Data []byte 90 | }{ 91 | Nonce: p.Nonce, 92 | GasPrice: MustBigint(p.GasPrice, 0), 93 | GasLimit: MustUint64(p.GasLimit, 0), 94 | To: p_To, 95 | Value: MustBigint(p.Value, 0), 96 | Data: p.Data, 97 | } 98 | 99 | var buf bytes.Buffer 100 | rlp.Encode(&buf, txData) 101 | return buf.Bytes() 102 | } 103 | 104 | // EIP155之后增加 V/R/S 三个元素 105 | // V 可以包含 chainId, 但是依然兼容以前旧的风格 106 | // 因为增加来内容, 导致旧版本的客户端不再兼容 107 | var txData = &struct { 108 | Nonce uint64 109 | GasPrice *big.Int 110 | GasLimit uint64 111 | To *[20]byte // nil 表示合约 112 | Value *big.Int 113 | Data []byte 114 | 115 | V *big.Int 116 | R *big.Int 117 | S *big.Int 118 | }{ 119 | Nonce: p.Nonce, 120 | GasPrice: MustBigint(p.GasPrice, 16), 121 | GasLimit: MustUint64(p.GasLimit, 16), 122 | To: p_To, 123 | Value: MustBigint(p.Value, 16), 124 | Data: p.Data, 125 | } 126 | 127 | // 包含 chainId 信息 128 | if chainId > 0 { 129 | // v = CHAIN_ID * 2 + 35 or v = CHAIN_ID * 2 + 36 130 | txData.V = big.NewInt(chainId) 131 | } else { 132 | txData.V = big.NewInt(27) 133 | } 134 | 135 | // RLP编码 136 | var buf bytes.Buffer 137 | rlp.Encode(&buf, txData) 138 | return buf.Bytes() 139 | } 140 | -------------------------------------------------------------------------------- /tx_test.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | import ( 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func TestTxData_SigData(t *testing.T) { 11 | var tests = []struct { 12 | Name string 13 | ChainId int64 14 | BlockNumber int64 15 | HexSigData string 16 | HexHash string 17 | TxData 18 | }{ 19 | // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md 20 | { 21 | "EIP155-Example", 22 | 1, EIP155_FORK_BLKNUM, 23 | "0xec098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a764000080018080", 24 | "0xdaf5a779ae972f972197303d7b574746c7ef83eadac0f2791ad23db92e4c8e53", 25 | TxData{ 26 | Nonce: 9, 27 | GasPrice: "20" + strings.Repeat("0", 9), 28 | GasLimit: "21000", 29 | To: "0x3535353535353535353535353535353535353535", 30 | Value: "1" + strings.Repeat("0", 18), 31 | Data: nil, 32 | }, 33 | }, 34 | 35 | // 书中 RLP-Encoded 并不是用于签名的编码数据, 还包含来 V/R/S 36 | // https://github.com/ethereumbook/ethereumbook/blob/develop/06transactions.asciidoc 37 | // https://github.com/ethereumbook/ethereumbook/blob/develop/code/web3js/raw_tx/raw_tx_demo.js 38 | // 39 | // $ node raw_tx_demo.js 40 | // RLP-Encoded Tx: 0xe6808609184e72a0008303000094b0920c523d582040f2bcb1bd7fb1c7c1... 41 | // Tx Hash: 0xaa7f03f9f4e52fcf69f836a6d2bbc7706580adce0a068ff6525ba337218e6992 42 | // Signed Raw Transaction: 0xf866808609184e72a0008303000094b0920c523d582040f2bcb1... 43 | { 44 | "精通以太坊例子", 45 | 46 | // 只编码6个交易数据 47 | // 书中的RLP编码数据不是用于签名, 因此忽略该检查 48 | 0, 0, "", 49 | 50 | "0xaa7f03f9f4e52fcf69f836a6d2bbc7706580adce0a068ff6525ba337218e6992", 51 | TxData{ 52 | Nonce: 0, 53 | GasPrice: "0x09184e72a000", 54 | GasLimit: "0x30000", 55 | To: "0xb0920c523d582040f2bcb1bd7fb1c7c1ecebdb34", 56 | Value: "0x00", 57 | Data: nil, 58 | }, 59 | }, 60 | } 61 | 62 | for _, tv := range tests { 63 | if tv.HexSigData != "" { 64 | var got = Hex("0x%x", tv.SigDataEx(tv.ChainId, tv.BlockNumber)).String() 65 | tAssert(t, got == tv.HexSigData, tv.Name, tv.HexSigData, got) 66 | } 67 | 68 | var h = Hex("0x%x", tv.HashEx(tv.ChainId, tv.BlockNumber)).String() 69 | tAssert(t, h == tv.HexHash, tv.Name, tv.HexHash, h) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /wei.go: -------------------------------------------------------------------------------- 1 | // 以太坊工具箱(零第三方库依赖) 版权 @2019 柴树杉。 2 | 3 | package ethutil 4 | 5 | const ( 6 | Wei = 1 7 | GWei = 1e9 // int32 大约最多表示 2 个 GWei 8 | Ether = 1e18 // int64 大约最多表示 9 个 Ether 9 | ) 10 | --------------------------------------------------------------------------------