450 changed files with 840 additions and 65929 deletions
@ -0,0 +1,5 @@ |
|||||||
|
# Default ignored files |
||||||
|
/shelf/ |
||||||
|
/workspace.xml |
||||||
|
# Editor-based HTTP Client requests |
||||||
|
/httpRequests/ |
||||||
@ -0,0 +1,8 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project version="4"> |
||||||
|
<component name="ProjectModuleManager"> |
||||||
|
<modules> |
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/next.orly.dev.iml" filepath="$PROJECT_DIR$/.idea/next.orly.dev.iml" /> |
||||||
|
</modules> |
||||||
|
</component> |
||||||
|
</project> |
||||||
@ -0,0 +1,6 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
<project version="4"> |
||||||
|
<component name="VcsDirectoryMappings"> |
||||||
|
<mapping directory="" vcs="Git" /> |
||||||
|
</component> |
||||||
|
</project> |
||||||
@ -1,17 +0,0 @@ |
|||||||
ISC License |
|
||||||
|
|
||||||
Copyright (c) 2013-2017 The btcsuite developers |
|
||||||
Copyright (c) 2015-2020 The Decred developers |
|
||||||
Copyright (c) 2017 The Lightning Network Developers |
|
||||||
|
|
||||||
Permission to use, copy, modify, and distribute this software for any |
|
||||||
purpose with or without fee is hereby granted, provided that the above |
|
||||||
copyright notice and this permission notice appear in all copies. |
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
|
||||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
|
||||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
|
||||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
|
||||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
|
||||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
||||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
|
||||||
@ -1,38 +0,0 @@ |
|||||||
# realy.lol/pkg/ec |
|
||||||
|
|
||||||
This is a full drop-in replacement for |
|
||||||
[github.com/btcsuite/btcd/btcec](https://github.com/btcsuite/btcd/tree/master/btcec) |
|
||||||
eliminating the import from the Decred repository, and including the chainhash |
|
||||||
helper functions, needed for hashing messages for signatures. |
|
||||||
|
|
||||||
The decred specific tests also have been removed, as well as all tests that use |
|
||||||
blake256 hashes as these are irrelevant to bitcoin and nostr. Some of them |
|
||||||
remain present, commented out, in case it is worth regenerating the vectors |
|
||||||
based on sha256 hashes, but on first blush it seems unlikely to be any benefit. |
|
||||||
|
|
||||||
This includes the old style compact secp256k1 ECDSA signatures, that recover the |
|
||||||
public key rather than take a key as a parameter as used in Bitcoin |
|
||||||
transactions, the new style Schnorr signatures, and the Musig2 implementation. |
|
||||||
|
|
||||||
BIP 340 Schnorr signatures are implemented including the variable length |
|
||||||
message signing with the extra test vectors present and passing. |
|
||||||
|
|
||||||
The remainder of this document is from the original README.md. |
|
||||||
|
|
||||||
--- |
|
||||||
|
|
||||||
Package `ec` implements elliptic curve cryptography needed for working with |
|
||||||
Bitcoin. It is designed so that it may be used with the standard |
|
||||||
crypto/ecdsa packages provided with Go. |
|
||||||
|
|
||||||
A comprehensive suite of test is provided to ensure proper functionality. |
|
||||||
|
|
||||||
Package btcec was originally based on work from ThePiachu which is licensed |
|
||||||
underthe same terms as Go, but it has signficantly diverged since then. The |
|
||||||
btcsuite developers original is licensed under the liberal ISC license. |
|
||||||
|
|
||||||
## Installation and Updating |
|
||||||
|
|
||||||
```bash |
|
||||||
$ go get mleku.dev/pkg/ec@latest |
|
||||||
``` |
|
||||||
@ -1,14 +0,0 @@ |
|||||||
Copyright © 2004-2011 []byte Internet Systems Consortium, Inc. ("ISC") |
|
||||||
Copyright © 1995-2003 []byte Internet Software Consortium |
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any |
|
||||||
purpose with or without fee is hereby granted, provided that the above |
|
||||||
copyright notice and this permission notice appear in all copies. |
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD |
|
||||||
TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND |
|
||||||
FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR |
|
||||||
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
|
||||||
DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS |
|
||||||
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS |
|
||||||
SOFTWARE. |
|
||||||
@ -1,12 +0,0 @@ |
|||||||
= base58 |
|
||||||
|
|
||||||
image:http://img.shields.io/badge/license-ISC-blue.svg[ISC License,link=http://copyfree.org] |
|
||||||
|
|
||||||
Package base58 provides an API for encoding and decoding to and from the modified base58 encoding. |
|
||||||
It also provides an API to do Base58Check encoding, as described https://en.bitcoin.it/wiki/Base58Check_encoding[here]. |
|
||||||
|
|
||||||
A comprehensive suite of tests is provided to ensure proper functionality. |
|
||||||
|
|
||||||
== License |
|
||||||
|
|
||||||
Package base58 is licensed under the http://copyfree.org[copyfree] ISC License. |
|
||||||
@ -1,49 +0,0 @@ |
|||||||
// Copyright (c) 2015 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// AUTOGENERATED by genalphabet.go; do not edit.
|
|
||||||
|
|
||||||
package base58 |
|
||||||
|
|
||||||
const ( |
|
||||||
// Ciphers is the modified base58 Ciphers used by Bitcoin.
|
|
||||||
Ciphers = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" |
|
||||||
|
|
||||||
alphabetIdx0 = '1' |
|
||||||
) |
|
||||||
|
|
||||||
var b58 = [256]byte{ |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 0, 1, 2, 3, 4, 5, 6, |
|
||||||
7, 8, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 9, 10, 11, 12, 13, 14, 15, |
|
||||||
16, 255, 17, 18, 19, 20, 21, 255, |
|
||||||
22, 23, 24, 25, 26, 27, 28, 29, |
|
||||||
30, 31, 32, 255, 255, 255, 255, 255, |
|
||||||
255, 33, 34, 35, 36, 37, 38, 39, |
|
||||||
40, 41, 42, 43, 255, 44, 45, 46, |
|
||||||
47, 48, 49, 50, 51, 52, 53, 54, |
|
||||||
55, 56, 57, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
255, 255, 255, 255, 255, 255, 255, 255, |
|
||||||
} |
|
||||||
@ -1,142 +0,0 @@ |
|||||||
// Copyright (c) 2013-2015 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package base58 |
|
||||||
|
|
||||||
import ( |
|
||||||
"math/big" |
|
||||||
) |
|
||||||
|
|
||||||
//go:generate go run genalphabet.go
|
|
||||||
|
|
||||||
var bigRadix = [...]*big.Int{ |
|
||||||
big.NewInt(0), |
|
||||||
big.NewInt(58), |
|
||||||
big.NewInt(58 * 58), |
|
||||||
big.NewInt(58 * 58 * 58), |
|
||||||
big.NewInt(58 * 58 * 58 * 58), |
|
||||||
big.NewInt(58 * 58 * 58 * 58 * 58), |
|
||||||
big.NewInt(58 * 58 * 58 * 58 * 58 * 58), |
|
||||||
big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58), |
|
||||||
big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58), |
|
||||||
big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58), |
|
||||||
bigRadix10, |
|
||||||
} |
|
||||||
|
|
||||||
var bigRadix10 = big.NewInt(58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58 * 58) // 58^10
|
|
||||||
|
|
||||||
// Decode decodes a modified base58 string to a byte slice.
|
|
||||||
func Decode(b string) []byte { |
|
||||||
answer := big.NewInt(0) |
|
||||||
scratch := new(big.Int) |
|
||||||
|
|
||||||
// Calculating with big.Int is slow for each iteration.
|
|
||||||
// x += b58[b[i]] * j
|
|
||||||
// j *= 58
|
|
||||||
//
|
|
||||||
// Instead we can try to do as much calculations on int64.
|
|
||||||
// We can represent a 10 digit base58 number using an int64.
|
|
||||||
//
|
|
||||||
// Hence we'll try to convert 10, base58 digits at a time.
|
|
||||||
// The rough idea is to calculate `t`, such that:
|
|
||||||
//
|
|
||||||
// t := b58[b[i+9]] * 58^9 ... + b58[b[i+1]] * 58^1 + b58[b[i]] * 58^0
|
|
||||||
// x *= 58^10
|
|
||||||
// x += t
|
|
||||||
//
|
|
||||||
// Of course, in addition, we'll need to handle boundary condition when `b` is not multiple of 58^10.
|
|
||||||
// In that case we'll use the bigRadix[n] lookup for the appropriate power.
|
|
||||||
for t := b; len(t) > 0; { |
|
||||||
n := len(t) |
|
||||||
if n > 10 { |
|
||||||
n = 10 |
|
||||||
} |
|
||||||
|
|
||||||
total := uint64(0) |
|
||||||
for _, v := range t[:n] { |
|
||||||
if v > 255 { |
|
||||||
return []byte("") |
|
||||||
} |
|
||||||
|
|
||||||
tmp := b58[v] |
|
||||||
if tmp == 255 { |
|
||||||
return []byte("") |
|
||||||
} |
|
||||||
total = total*58 + uint64(tmp) |
|
||||||
} |
|
||||||
|
|
||||||
answer.Mul(answer, bigRadix[n]) |
|
||||||
scratch.SetUint64(total) |
|
||||||
answer.Add(answer, scratch) |
|
||||||
|
|
||||||
t = t[n:] |
|
||||||
} |
|
||||||
|
|
||||||
tmpval := answer.Bytes() |
|
||||||
|
|
||||||
var numZeros int |
|
||||||
for numZeros = 0; numZeros < len(b); numZeros++ { |
|
||||||
if b[numZeros] != alphabetIdx0 { |
|
||||||
break |
|
||||||
} |
|
||||||
} |
|
||||||
flen := numZeros + len(tmpval) |
|
||||||
val := make([]byte, flen) |
|
||||||
copy(val[numZeros:], tmpval) |
|
||||||
|
|
||||||
return val |
|
||||||
} |
|
||||||
|
|
||||||
// Encode encodes a byte slice to a modified base58 string.
|
|
||||||
func Encode(b []byte) string { |
|
||||||
x := new(big.Int) |
|
||||||
x.SetBytes(b) |
|
||||||
|
|
||||||
// maximum length of output is log58(2^(8*len(b))) == len(b) * 8 / log(58)
|
|
||||||
maxlen := int(float64(len(b))*1.365658237309761) + 1 |
|
||||||
answer := make([]byte, 0, maxlen) |
|
||||||
mod := new(big.Int) |
|
||||||
for x.Sign() > 0 { |
|
||||||
// Calculating with big.Int is slow for each iteration.
|
|
||||||
// x, mod = x / 58, x % 58
|
|
||||||
//
|
|
||||||
// Instead we can try to do as much calculations on int64.
|
|
||||||
// x, mod = x / 58^10, x % 58^10
|
|
||||||
//
|
|
||||||
// Which will give us mod, which is 10 digit base58 number.
|
|
||||||
// We'll loop that 10 times to convert to the answer.
|
|
||||||
|
|
||||||
x.DivMod(x, bigRadix10, mod) |
|
||||||
if x.Sign() == 0 { |
|
||||||
// When x = 0, we need to ensure we don't add any extra zeros.
|
|
||||||
m := mod.Int64() |
|
||||||
for m > 0 { |
|
||||||
answer = append(answer, Ciphers[m%58]) |
|
||||||
m /= 58 |
|
||||||
} |
|
||||||
} else { |
|
||||||
m := mod.Int64() |
|
||||||
for i := 0; i < 10; i++ { |
|
||||||
answer = append(answer, Ciphers[m%58]) |
|
||||||
m /= 58 |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// leading zero bytes
|
|
||||||
for _, i := range b { |
|
||||||
if i != 0 { |
|
||||||
break |
|
||||||
} |
|
||||||
answer = append(answer, alphabetIdx0) |
|
||||||
} |
|
||||||
|
|
||||||
// reverse
|
|
||||||
alen := len(answer) |
|
||||||
for i := 0; i < alen/2; i++ { |
|
||||||
answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i] |
|
||||||
} |
|
||||||
|
|
||||||
return string(answer) |
|
||||||
} |
|
||||||
@ -1,124 +0,0 @@ |
|||||||
// Copyright (c) 2013-2017 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package base58_test |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/hex" |
|
||||||
"testing" |
|
||||||
|
|
||||||
"next.orly.dev/pkg/crypto/ec/base58" |
|
||||||
"next.orly.dev/pkg/utils" |
|
||||||
) |
|
||||||
|
|
||||||
var stringTests = []struct { |
|
||||||
in string |
|
||||||
out string |
|
||||||
}{ |
|
||||||
{"", ""}, |
|
||||||
{" ", "Z"}, |
|
||||||
{"-", "n"}, |
|
||||||
{"0", "q"}, |
|
||||||
{"1", "r"}, |
|
||||||
{"-1", "4SU"}, |
|
||||||
{"11", "4k8"}, |
|
||||||
{"abc", "ZiCa"}, |
|
||||||
{"1234598760", "3mJr7AoUXx2Wqd"}, |
|
||||||
{"abcdefghijklmnopqrstuvwxyz", "3yxU3u1igY8WkgtjK92fbJQCd4BZiiT1v25f"}, |
|
||||||
{ |
|
||||||
"00000000000000000000000000000000000000000000000000000000000000", |
|
||||||
"3sN2THZeE9Eh9eYrwkvZqNstbHGvrxSAM7gXUXvyFQP8XvQLUqNCS27icwUeDT7ckHm4FUHM2mTVh1vbLmk7y", |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
var invalidStringTests = []struct { |
|
||||||
in string |
|
||||||
out string |
|
||||||
}{ |
|
||||||
{"0", ""}, |
|
||||||
{"O", ""}, |
|
||||||
{"I", ""}, |
|
||||||
{"l", ""}, |
|
||||||
{"3mJr0", ""}, |
|
||||||
{"O3yxU", ""}, |
|
||||||
{"3sNI", ""}, |
|
||||||
{"4kl8", ""}, |
|
||||||
{"0OIl", ""}, |
|
||||||
{"!@#$%^&*()-_=+~`", ""}, |
|
||||||
{"abcd\xd80", ""}, |
|
||||||
{"abcd\U000020BF", ""}, |
|
||||||
} |
|
||||||
|
|
||||||
var hexTests = []struct { |
|
||||||
in string |
|
||||||
out string |
|
||||||
}{ |
|
||||||
{"", ""}, |
|
||||||
{"61", "2g"}, |
|
||||||
{"626262", "a3gV"}, |
|
||||||
{"636363", "aPEr"}, |
|
||||||
{ |
|
||||||
"73696d706c792061206c6f6e6720737472696e67", |
|
||||||
"2cFupjhnEsSn59qHXstmK2ffpLv2", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"00eb15231dfceb60925886b67d065299925915aeb172c06647", |
|
||||||
"1NS17iag9jJgTHD1VXjvLCEnZuQ3rJDE9L", |
|
||||||
}, |
|
||||||
{"516b6fcd0f", "ABnLTmg"}, |
|
||||||
{"bf4f89001e670274dd", "3SEo3LWLoPntC"}, |
|
||||||
{"572e4794", "3EFU7m"}, |
|
||||||
{"ecac89cad93923c02321", "EJDM8drfXA6uyA"}, |
|
||||||
{"10c8511e", "Rt5zm"}, |
|
||||||
{"00000000000000000000", "1111111111"}, |
|
||||||
{ |
|
||||||
"000111d38e5fc9071ffcd20b4a763cc9ae4f252bb4e48fd66a835e252ada93ff480d6dd43dc62a641155a5", |
|
||||||
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", |
|
||||||
"1cWB5HCBdLjAuqGGReWE3R3CguuwSjw6RHn39s2yuDRTS5NsBgNiFpWgAnEx6VQi8csexkgYw3mdYrMHr8x9i7aEwP8kZ7vccXWqKDvGv3u1GxFKPuAkn8JCPPGDMf3vMMnbzm6Nh9zh1gcNsMvH3ZNLmP5fSG6DGbbi2tuwMWPthr4boWwCxf7ewSgNQeacyozhKDDQQ1qL5fQFUW52QKUZDZ5fw3KXNQJMcNTcaB723LchjeKun7MuGW5qyCBZYzA1KjofN1gYBV3NqyhQJ3Ns746GNuf9N2pQPmHz4xpnSrrfCvy6TVVz5d4PdrjeshsWQwpZsZGzvbdAdN8MKV5QsBDY", |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
func TestBase58(t *testing.T) { |
|
||||||
// Encode tests
|
|
||||||
for x, test := range stringTests { |
|
||||||
tmp := []byte(test.in) |
|
||||||
if res := base58.Encode(tmp); res != test.out { |
|
||||||
t.Errorf( |
|
||||||
"Encode test #%d failed: got: %s want: %s", |
|
||||||
x, res, test.out, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Decode tests
|
|
||||||
for x, test := range hexTests { |
|
||||||
b, err := hex.DecodeString(test.in) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("hex.DecodeString failed failed #%d: got: %s", x, test.in) |
|
||||||
continue |
|
||||||
} |
|
||||||
if res := base58.Decode(test.out); !utils.FastEqual(res, b) { |
|
||||||
t.Errorf( |
|
||||||
"Decode test #%d failed: got: %q want: %q", |
|
||||||
x, res, test.in, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Decode with invalid input
|
|
||||||
for x, test := range invalidStringTests { |
|
||||||
if res := base58.Decode(test.in); string(res) != test.out { |
|
||||||
t.Errorf( |
|
||||||
"Decode invalidString test #%d failed: got: %q want: %q", |
|
||||||
x, res, test.out, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,47 +0,0 @@ |
|||||||
// Copyright (c) 2013-2014 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package base58_test |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"testing" |
|
||||||
|
|
||||||
"next.orly.dev/pkg/crypto/ec/base58" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
raw5k = bytes.Repeat([]byte{0xff}, 5000) |
|
||||||
raw100k = bytes.Repeat([]byte{0xff}, 100*1000) |
|
||||||
encoded5k = base58.Encode(raw5k) |
|
||||||
encoded100k = base58.Encode(raw100k) |
|
||||||
) |
|
||||||
|
|
||||||
func BenchmarkBase58Encode_5K(b *testing.B) { |
|
||||||
b.SetBytes(int64(len(raw5k))) |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
base58.Encode(raw5k) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func BenchmarkBase58Encode_100K(b *testing.B) { |
|
||||||
b.SetBytes(int64(len(raw100k))) |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
base58.Encode(raw100k) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func BenchmarkBase58Decode_5K(b *testing.B) { |
|
||||||
b.SetBytes(int64(len(encoded5k))) |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
base58.Decode(encoded5k) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func BenchmarkBase58Decode_100K(b *testing.B) { |
|
||||||
b.SetBytes(int64(len(encoded100k))) |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
base58.Decode(encoded100k) |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,53 +0,0 @@ |
|||||||
// Copyright (c) 2013-2014 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package base58 |
|
||||||
|
|
||||||
import ( |
|
||||||
"errors" |
|
||||||
|
|
||||||
"github.com/minio/sha256-simd" |
|
||||||
) |
|
||||||
|
|
||||||
// ErrChecksum indicates that the checksum of a check-encoded string does not verify against
|
|
||||||
// the checksum.
|
|
||||||
var ErrChecksum = errors.New("checksum error") |
|
||||||
|
|
||||||
// ErrInvalidFormat indicates that the check-encoded string has an invalid format.
|
|
||||||
var ErrInvalidFormat = errors.New("invalid format: version and/or checksum bytes missing") |
|
||||||
|
|
||||||
// checksum: first four bytes of sha256^2
|
|
||||||
func checksum(input []byte) (cksum [4]byte) { |
|
||||||
h := sha256.Sum256(input) |
|
||||||
h2 := sha256.Sum256(h[:]) |
|
||||||
copy(cksum[:], h2[:4]) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// CheckEncode prepends a version byte and appends a four byte checksum.
|
|
||||||
func CheckEncode(input []byte, version byte) string { |
|
||||||
b := make([]byte, 0, 1+len(input)+4) |
|
||||||
b = append(b, version) |
|
||||||
b = append(b, input...) |
|
||||||
cksum := checksum(b) |
|
||||||
b = append(b, cksum[:]...) |
|
||||||
return Encode(b) |
|
||||||
} |
|
||||||
|
|
||||||
// CheckDecode decodes a string that was encoded with CheckEncode and verifies the checksum.
|
|
||||||
func CheckDecode(input string) (result []byte, version byte, err error) { |
|
||||||
decoded := Decode(input) |
|
||||||
if len(decoded) < 5 { |
|
||||||
return nil, 0, ErrInvalidFormat |
|
||||||
} |
|
||||||
version = decoded[0] |
|
||||||
var cksum [4]byte |
|
||||||
copy(cksum[:], decoded[len(decoded)-4:]) |
|
||||||
if checksum(decoded[:len(decoded)-4]) != cksum { |
|
||||||
return nil, 0, ErrChecksum |
|
||||||
} |
|
||||||
payload := decoded[1 : len(decoded)-4] |
|
||||||
result = append(result, payload...) |
|
||||||
return |
|
||||||
} |
|
||||||
@ -1,87 +0,0 @@ |
|||||||
// Copyright (c) 2013-2014 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package base58_test |
|
||||||
|
|
||||||
import ( |
|
||||||
"testing" |
|
||||||
|
|
||||||
"next.orly.dev/pkg/crypto/ec/base58" |
|
||||||
) |
|
||||||
|
|
||||||
var checkEncodingStringTests = []struct { |
|
||||||
version byte |
|
||||||
in string |
|
||||||
out string |
|
||||||
}{ |
|
||||||
{20, "", "3MNQE1X"}, |
|
||||||
{20, " ", "B2Kr6dBE"}, |
|
||||||
{20, "-", "B3jv1Aft"}, |
|
||||||
{20, "0", "B482yuaX"}, |
|
||||||
{20, "1", "B4CmeGAC"}, |
|
||||||
{20, "-1", "mM7eUf6kB"}, |
|
||||||
{20, "11", "mP7BMTDVH"}, |
|
||||||
{20, "abc", "4QiVtDjUdeq"}, |
|
||||||
{20, "1234598760", "ZmNb8uQn5zvnUohNCEPP"}, |
|
||||||
{ |
|
||||||
20, "abcdefghijklmnopqrstuvwxyz", |
|
||||||
"K2RYDcKfupxwXdWhSAxQPCeiULntKm63UXyx5MvEH2", |
|
||||||
}, |
|
||||||
{ |
|
||||||
20, "00000000000000000000000000000000000000000000000000000000000000", |
|
||||||
"bi1EWXwJay2udZVxLJozuTb8Meg4W9c6xnmJaRDjg6pri5MBAxb9XwrpQXbtnqEoRV5U2pixnFfwyXC8tRAVC8XxnjK", |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
func TestBase58Check(t *testing.T) { |
|
||||||
for x, test := range checkEncodingStringTests { |
|
||||||
// test encoding
|
|
||||||
if res := base58.CheckEncode( |
|
||||||
[]byte(test.in), |
|
||||||
test.version, |
|
||||||
); res != test.out { |
|
||||||
t.Errorf( |
|
||||||
"CheckEncode test #%d failed: got %s, want: %s", x, res, |
|
||||||
test.out, |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
// test decoding
|
|
||||||
res, version, err := base58.CheckDecode(test.out) |
|
||||||
switch { |
|
||||||
case err != nil: |
|
||||||
t.Errorf("CheckDecode test #%d failed with err: %v", x, err) |
|
||||||
|
|
||||||
case version != test.version: |
|
||||||
t.Errorf( |
|
||||||
"CheckDecode test #%d failed: got version: %d want: %d", x, |
|
||||||
version, test.version, |
|
||||||
) |
|
||||||
|
|
||||||
case string(res) != test.in: |
|
||||||
t.Errorf( |
|
||||||
"CheckDecode test #%d failed: got: %s want: %s", x, res, |
|
||||||
test.in, |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// test the two decoding failure cases
|
|
||||||
// case 1: checksum error
|
|
||||||
_, _, err := base58.CheckDecode("3MNQE1Y") |
|
||||||
if err != base58.ErrChecksum { |
|
||||||
t.Error("Checkdecode test failed, expected ErrChecksum") |
|
||||||
} |
|
||||||
// case 2: invalid formats (string lengths below 5 mean the version byte and/or the checksum
|
|
||||||
// bytes are missing).
|
|
||||||
testString := "" |
|
||||||
for len := 0; len < 4; len++ { |
|
||||||
testString += "x" |
|
||||||
_, _, err = base58.CheckDecode(testString) |
|
||||||
if err != base58.ErrInvalidFormat { |
|
||||||
t.Error("Checkdecode test failed, expected ErrInvalidFormat") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -1,17 +0,0 @@ |
|||||||
#!/bin/sh |
|
||||||
|
|
||||||
# This script uses gocov to generate a test coverage report. |
|
||||||
# The gocov tool my be obtained with the following command: |
|
||||||
# go get github.com/axw/gocov/gocov |
|
||||||
# |
|
||||||
# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH. |
|
||||||
|
|
||||||
# Check for gocov. |
|
||||||
type gocov >/dev/null 2>&1 |
|
||||||
if [ $? -ne 0 ]; then |
|
||||||
echo >&2 "This script requires the gocov tool." |
|
||||||
echo >&2 "You may obtain it with the following command:" |
|
||||||
echo >&2 "go get github.com/axw/gocov/gocov" |
|
||||||
exit 1 |
|
||||||
fi |
|
||||||
gocov test | gocov report |
|
||||||
@ -1,29 +0,0 @@ |
|||||||
// Copyright (c) 2014 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
/* |
|
||||||
Package base58 provides an API for working with modified base58 and Base58Check |
|
||||||
encodings. |
|
||||||
|
|
||||||
# Modified Base58 Encoding |
|
||||||
|
|
||||||
Standard base58 encoding is similar to standard base64 encoding except, as the |
|
||||||
name implies, it uses a 58 character Ciphers which results in an alphanumeric |
|
||||||
string and allows some characters which are problematic for humans to be |
|
||||||
excluded. Due to this, there can be various base58 alphabets. |
|
||||||
|
|
||||||
The modified base58 Ciphers used by Bitcoin, and hence this package, omits the |
|
||||||
0, O, I, and l characters that look the same in many fonts and are therefore |
|
||||||
hard to humans to distinguish. |
|
||||||
|
|
||||||
# Base58Check Encoding Scheme |
|
||||||
|
|
||||||
The Base58Check encoding scheme is primarily used for Bitcoin addresses at the |
|
||||||
time of this writing, however it can be used to generically encode arbitrary |
|
||||||
byte arrays into human-readable strings along with a version byte that can be |
|
||||||
used to differentiate the same payload. For Bitcoin addresses, the extra |
|
||||||
version is used to differentiate the network of otherwise identical public keys |
|
||||||
which helps prevent using an address intended for one network on another. |
|
||||||
*/ |
|
||||||
package base58 |
|
||||||
@ -1,71 +0,0 @@ |
|||||||
// Copyright (c) 2014 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package base58_test |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
|
|
||||||
"next.orly.dev/pkg/crypto/ec/base58" |
|
||||||
) |
|
||||||
|
|
||||||
// This example demonstrates how to decode modified base58 encoded data.
|
|
||||||
func ExampleDecode() { |
|
||||||
// Decode example modified base58 encoded data.
|
|
||||||
encoded := "25JnwSn7XKfNQ" |
|
||||||
decoded := base58.Decode(encoded) |
|
||||||
|
|
||||||
// Show the decoded data.
|
|
||||||
fmt.Println("Decoded Data:", string(decoded)) |
|
||||||
|
|
||||||
// Output:
|
|
||||||
// Decoded Data: Test data
|
|
||||||
} |
|
||||||
|
|
||||||
// This example demonstrates how to encode data using the modified base58
|
|
||||||
// encoding scheme.
|
|
||||||
func ExampleEncode() { |
|
||||||
// Encode example data with the modified base58 encoding scheme.
|
|
||||||
data := []byte("Test data") |
|
||||||
encoded := base58.Encode(data) |
|
||||||
|
|
||||||
// Show the encoded data.
|
|
||||||
fmt.Println("Encoded Data:", encoded) |
|
||||||
|
|
||||||
// Output:
|
|
||||||
// Encoded Data: 25JnwSn7XKfNQ
|
|
||||||
} |
|
||||||
|
|
||||||
// This example demonstrates how to decode Base58Check encoded data.
|
|
||||||
func ExampleCheckDecode() { |
|
||||||
// Decode an example Base58Check encoded data.
|
|
||||||
encoded := "1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa" |
|
||||||
decoded, version, err := base58.CheckDecode(encoded) |
|
||||||
if err != nil { |
|
||||||
fmt.Println(err) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// Show the decoded data.
|
|
||||||
fmt.Printf("Decoded data: %x\n", decoded) |
|
||||||
fmt.Println("Version Byte:", version) |
|
||||||
|
|
||||||
// Output:
|
|
||||||
// Decoded data: 62e907b15cbf27d5425399ebf6f0fb50ebb88f18
|
|
||||||
// Version Byte: 0
|
|
||||||
} |
|
||||||
|
|
||||||
// This example demonstrates how to encode data using the Base58Check encoding
|
|
||||||
// scheme.
|
|
||||||
func ExampleCheckEncode() { |
|
||||||
// Encode example data with the Base58Check encoding scheme.
|
|
||||||
data := []byte("Test data") |
|
||||||
encoded := base58.CheckEncode(data, 0) |
|
||||||
|
|
||||||
// Show the encoded data.
|
|
||||||
fmt.Println("Encoded Data:", encoded) |
|
||||||
|
|
||||||
// Output:
|
|
||||||
// Encoded Data: 182iP79GRURMp7oMHDU
|
|
||||||
} |
|
||||||
@ -1,77 +0,0 @@ |
|||||||
// Copyright (c) 2015 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package main |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"io" |
|
||||||
"log" |
|
||||||
"os" |
|
||||||
"strconv" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
start = []byte(`// Copyright (c) 2015 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// AUTOGENERATED by genalphabet.go; do not edit.
|
|
||||||
|
|
||||||
package base58 |
|
||||||
|
|
||||||
const ( |
|
||||||
// Ciphers is the modified base58 alphabet used by Bitcoin.
|
|
||||||
Ciphers = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" |
|
||||||
|
|
||||||
alphabetIdx0 = '1' |
|
||||||
) |
|
||||||
|
|
||||||
var b58 = [256]byte{`) |
|
||||||
|
|
||||||
end = []byte(`}`) |
|
||||||
|
|
||||||
alphabet = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") |
|
||||||
tab = []byte("\t") |
|
||||||
invalid = []byte("255") |
|
||||||
comma = []byte(",") |
|
||||||
space = []byte(" ") |
|
||||||
nl = []byte("\n") |
|
||||||
) |
|
||||||
|
|
||||||
func write(w io.Writer, b []byte) { |
|
||||||
_, err := w.Write(b) |
|
||||||
if err != nil { |
|
||||||
log.Fatal(err) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func main() { |
|
||||||
fi, err := os.Create("alphabet.go") |
|
||||||
if err != nil { |
|
||||||
log.Fatal(err) |
|
||||||
} |
|
||||||
defer fi.Close() |
|
||||||
|
|
||||||
write(fi, start) |
|
||||||
write(fi, nl) |
|
||||||
for i := byte(0); i < 32; i++ { |
|
||||||
write(fi, tab) |
|
||||||
for j := byte(0); j < 8; j++ { |
|
||||||
idx := bytes.IndexByte(alphabet, i*8+j) |
|
||||||
if idx == -1 { |
|
||||||
write(fi, invalid) |
|
||||||
} else { |
|
||||||
write(fi, strconv.AppendInt(nil, int64(idx), 10)) |
|
||||||
} |
|
||||||
write(fi, comma) |
|
||||||
if j != 7 { |
|
||||||
write(fi, space) |
|
||||||
} |
|
||||||
} |
|
||||||
write(fi, nl) |
|
||||||
} |
|
||||||
write(fi, end) |
|
||||||
write(fi, nl) |
|
||||||
} |
|
||||||
@ -1,27 +0,0 @@ |
|||||||
= bech32 |
|
||||||
|
|
||||||
image:http://img.shields.io/badge/license-ISC-blue.svg[ISC License,link=http://copyfree.org] |
|
||||||
image:https://godoc.org/realy.lol/pkg/ec/bech32?status.png[GoDoc,link=http://godoc.org/realy.lol/pkg/ec/bech32] |
|
||||||
|
|
||||||
Package bech32 provides a Go implementation of the bech32 format specified in |
|
||||||
https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki[BIP 173]. |
|
||||||
|
|
||||||
Test vectors from BIP 173 are added to ensure compatibility with the BIP. |
|
||||||
|
|
||||||
== Installation and Updating |
|
||||||
|
|
||||||
[source,bash] |
|
||||||
---- |
|
||||||
$ go get -u mleku.dev/pkg/ec/bech32 |
|
||||||
---- |
|
||||||
|
|
||||||
== Examples |
|
||||||
|
|
||||||
* http://godoc.org/realy.lol/pkg/ec/bech32#example-Bech32Decode[Bech32 decode Example] |
|
||||||
Demonstrates how to decode a bech32 encoded string. |
|
||||||
* http://godoc.org/realy.lol/pkg/ec/bech32#example-BechEncode[Bech32 encode Example] |
|
||||||
Demonstrates how to encode data into a bech32 string. |
|
||||||
|
|
||||||
== License |
|
||||||
|
|
||||||
Package bech32 is licensed under the http://copyfree.org[copyfree] ISC License. |
|
||||||
@ -1,411 +0,0 @@ |
|||||||
// Copyright (c) 2017 The btcsuite developers
|
|
||||||
// Copyright (c) 2019 The Decred developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package bech32 |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"strings" |
|
||||||
) |
|
||||||
|
|
||||||
// Charset is the set of characters used in the data section of bech32 strings.
|
|
||||||
// Note that this is ordered, such that for a given charset[i], i is the binary
|
|
||||||
// value of the character.
|
|
||||||
//
|
|
||||||
// This wasn't exported in the original lol.
|
|
||||||
const Charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" |
|
||||||
|
|
||||||
// gen encodes the generator polynomial for the bech32 BCH checksum.
|
|
||||||
var gen = []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} |
|
||||||
|
|
||||||
// toBytes converts each character in the string 'chars' to the value of the
|
|
||||||
// index of the corresponding character in 'charset'.
|
|
||||||
func toBytes(chars []byte) ([]byte, error) { |
|
||||||
decoded := make([]byte, 0, len(chars)) |
|
||||||
for i := 0; i < len(chars); i++ { |
|
||||||
index := strings.IndexByte(Charset, chars[i]) |
|
||||||
if index < 0 { |
|
||||||
return nil, ErrNonCharsetChar(chars[i]) |
|
||||||
} |
|
||||||
decoded = append(decoded, byte(index)) |
|
||||||
} |
|
||||||
return decoded, nil |
|
||||||
} |
|
||||||
|
|
||||||
// bech32Polymod calculates the BCH checksum for a given hrp, values and
|
|
||||||
// checksum data. Checksum is optional, and if nil a 0 checksum is assumed.
|
|
||||||
//
|
|
||||||
// Values and checksum (if provided) MUST be encoded as 5 bits per element (base
|
|
||||||
// 32), otherwise the results are undefined.
|
|
||||||
//
|
|
||||||
// For more details on the polymod calculation, please refer to BIP 173.
|
|
||||||
func bech32Polymod(hrp []byte, values, checksum []byte) int { |
|
||||||
check := 1 |
|
||||||
// Account for the high bits of the HRP in the checksum.
|
|
||||||
for i := 0; i < len(hrp); i++ { |
|
||||||
b := check >> 25 |
|
||||||
hiBits := int(hrp[i]) >> 5 |
|
||||||
check = (check&0x1ffffff)<<5 ^ hiBits |
|
||||||
for i := 0; i < 5; i++ { |
|
||||||
if (b>>uint(i))&1 == 1 { |
|
||||||
check ^= gen[i] |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
// Account for the separator (0) between high and low bits of the HRP.
|
|
||||||
// x^0 == x, so we eliminate the redundant xor used in the other rounds.
|
|
||||||
b := check >> 25 |
|
||||||
check = (check & 0x1ffffff) << 5 |
|
||||||
for i := 0; i < 5; i++ { |
|
||||||
if (b>>uint(i))&1 == 1 { |
|
||||||
check ^= gen[i] |
|
||||||
} |
|
||||||
} |
|
||||||
// Account for the low bits of the HRP.
|
|
||||||
for i := 0; i < len(hrp); i++ { |
|
||||||
b := check >> 25 |
|
||||||
loBits := int(hrp[i]) & 31 |
|
||||||
check = (check&0x1ffffff)<<5 ^ loBits |
|
||||||
for i := 0; i < 5; i++ { |
|
||||||
if (b>>uint(i))&1 == 1 { |
|
||||||
check ^= gen[i] |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
// Account for the values.
|
|
||||||
for _, v := range values { |
|
||||||
b := check >> 25 |
|
||||||
check = (check&0x1ffffff)<<5 ^ int(v) |
|
||||||
for i := 0; i < 5; i++ { |
|
||||||
if (b>>uint(i))&1 == 1 { |
|
||||||
check ^= gen[i] |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
if checksum == nil { |
|
||||||
// A nil checksum is used during encoding, so assume all bytes are zero.
|
|
||||||
// x^0 == x, so we eliminate the redundant xor used in the other rounds.
|
|
||||||
for v := 0; v < 6; v++ { |
|
||||||
b := check >> 25 |
|
||||||
check = (check & 0x1ffffff) << 5 |
|
||||||
for i := 0; i < 5; i++ { |
|
||||||
if (b>>uint(i))&1 == 1 { |
|
||||||
check ^= gen[i] |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} else { |
|
||||||
// Checksum is provided during decoding, so use it.
|
|
||||||
for _, v := range checksum { |
|
||||||
b := check >> 25 |
|
||||||
check = (check&0x1ffffff)<<5 ^ int(v) |
|
||||||
for i := 0; i < 5; i++ { |
|
||||||
if (b>>uint(i))&1 == 1 { |
|
||||||
check ^= gen[i] |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return check |
|
||||||
} |
|
||||||
|
|
||||||
// writeBech32Checksum calculates the checksum data expected for a string that
|
|
||||||
// will have the given hrp and payload data and writes it to the provided string
|
|
||||||
// builder.
|
|
||||||
//
|
|
||||||
// The payload data MUST be encoded as a base 32 (5 bits per element) byte slice
|
|
||||||
// and the hrp MUST only use the allowed character set (ascii chars between 33
|
|
||||||
// and 126), otherwise the results are undefined.
|
|
||||||
//
|
|
||||||
// For more details on the checksum calculation, please refer to BIP 173.
|
|
||||||
func writeBech32Checksum( |
|
||||||
hrp []byte, data []byte, bldr *bytes.Buffer, |
|
||||||
version Version, |
|
||||||
) { |
|
||||||
|
|
||||||
bech32Const := int(VersionToConsts[version]) |
|
||||||
polymod := bech32Polymod(hrp, data, nil) ^ bech32Const |
|
||||||
for i := 0; i < 6; i++ { |
|
||||||
b := byte((polymod >> uint(5*(5-i))) & 31) |
|
||||||
// This can't fail, given we explicitly cap the previous b byte by the
|
|
||||||
// first 31 bits.
|
|
||||||
c := Charset[b] |
|
||||||
bldr.WriteByte(c) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// bech32VerifyChecksum verifies whether the bech32 string specified by the
|
|
||||||
// provided hrp and payload data (encoded as 5 bits per element byte slice) has
|
|
||||||
// the correct checksum suffix. The version of bech32 used (bech32 OG, or
|
|
||||||
// bech32m) is also returned to allow the caller to perform proper address
|
|
||||||
// validation (segwitv0 should use bech32, v1+ should use bech32m).
|
|
||||||
//
|
|
||||||
// Data MUST have more than 6 elements, otherwise this function panics.
|
|
||||||
//
|
|
||||||
// For more details on the checksum verification, please refer to BIP 173.
|
|
||||||
func bech32VerifyChecksum(hrp []byte, data []byte) (Version, bool) { |
|
||||||
checksum := data[len(data)-6:] |
|
||||||
values := data[:len(data)-6] |
|
||||||
polymod := bech32Polymod(hrp, values, checksum) |
|
||||||
// Before BIP-350, we'd always check this against a static constant of
|
|
||||||
// 1 to know if the checksum was computed properly. As we want to
|
|
||||||
// generically support decoding for bech32m as well as bech32, we'll
|
|
||||||
// look up the returned value and compare it to the set of defined
|
|
||||||
// constants.
|
|
||||||
bech32Version, ok := ConstsToVersion[ChecksumConst(polymod)] |
|
||||||
if ok { |
|
||||||
return bech32Version, true |
|
||||||
} |
|
||||||
return VersionUnknown, false |
|
||||||
} |
|
||||||
|
|
||||||
// DecodeNoLimit is a bech32 checksum version aware arbitrary string length
|
|
||||||
// decoder. This function will return the version of the decoded checksum
|
|
||||||
// constant so higher level validation can be performed to ensure the correct
|
|
||||||
// version of bech32 was used when encoding.
|
|
||||||
func decodeNoLimit(bech []byte) ([]byte, []byte, Version, error) { |
|
||||||
// The minimum allowed size of a bech32 string is 8 characters, since it
|
|
||||||
// needs a non-empty HRP, a separator, and a 6 character checksum.
|
|
||||||
if len(bech) < 8 { |
|
||||||
return nil, nil, VersionUnknown, ErrInvalidLength(len(bech)) |
|
||||||
} |
|
||||||
// Only ASCII characters between 33 and 126 are allowed.
|
|
||||||
var hasLower, hasUpper bool |
|
||||||
for i := 0; i < len(bech); i++ { |
|
||||||
if bech[i] < 33 || bech[i] > 126 { |
|
||||||
return nil, nil, VersionUnknown, ErrInvalidCharacter(bech[i]) |
|
||||||
} |
|
||||||
// The characters must be either all lowercase or all uppercase. Testing
|
|
||||||
// directly with ascii codes is safe here, given the previous test.
|
|
||||||
hasLower = hasLower || (bech[i] >= 97 && bech[i] <= 122) |
|
||||||
hasUpper = hasUpper || (bech[i] >= 65 && bech[i] <= 90) |
|
||||||
if hasLower && hasUpper { |
|
||||||
return nil, nil, VersionUnknown, ErrMixedCase{} |
|
||||||
} |
|
||||||
} |
|
||||||
// Bech32 standard uses only the lowercase for of strings for checksum
|
|
||||||
// calculation.
|
|
||||||
if hasUpper { |
|
||||||
bech = bytes.ToLower(bech) |
|
||||||
} |
|
||||||
// The string is invalid if the last '1' is non-existent, it is the
|
|
||||||
// first character of the string (no human-readable part) or one of the
|
|
||||||
// last 6 characters of the string (since checksum cannot contain '1').
|
|
||||||
one := bytes.LastIndexByte(bech, '1') |
|
||||||
if one < 1 || one+7 > len(bech) { |
|
||||||
return nil, nil, VersionUnknown, ErrInvalidSeparatorIndex(one) |
|
||||||
} |
|
||||||
// The human-readable part is everything before the last '1'.
|
|
||||||
hrp := bech[:one] |
|
||||||
data := bech[one+1:] |
|
||||||
// Each character corresponds to the byte with value of the index in
|
|
||||||
// 'charset'.
|
|
||||||
decoded, err := toBytes(data) |
|
||||||
if err != nil { |
|
||||||
return nil, nil, VersionUnknown, err |
|
||||||
} |
|
||||||
// Verify if the checksum (stored inside decoded[:]) is valid, given the
|
|
||||||
// previously decoded hrp.
|
|
||||||
bech32Version, ok := bech32VerifyChecksum(hrp, decoded) |
|
||||||
if !ok { |
|
||||||
// Invalid checksum. Calculate what it should have been, so that the
|
|
||||||
// error contains this information.
|
|
||||||
//
|
|
||||||
// Extract the payload bytes and actual checksum in the string.
|
|
||||||
actual := bech[len(bech)-6:] |
|
||||||
payload := decoded[:len(decoded)-6] |
|
||||||
// Calculate the expected checksum, given the hrp and payload
|
|
||||||
// data. We'll actually compute _both_ possibly valid checksum
|
|
||||||
// to further aide in debugging.
|
|
||||||
var expectedBldr bytes.Buffer |
|
||||||
expectedBldr.Grow(6) |
|
||||||
writeBech32Checksum(hrp, payload, &expectedBldr, Version0) |
|
||||||
expectedVersion0 := expectedBldr.String() |
|
||||||
var b strings.Builder |
|
||||||
b.Grow(6) |
|
||||||
writeBech32Checksum(hrp, payload, &expectedBldr, VersionM) |
|
||||||
expectedVersionM := expectedBldr.String() |
|
||||||
err = ErrInvalidChecksum{ |
|
||||||
Expected: expectedVersion0, |
|
||||||
ExpectedM: expectedVersionM, |
|
||||||
Actual: string(actual), |
|
||||||
} |
|
||||||
return nil, nil, VersionUnknown, err |
|
||||||
} |
|
||||||
// We exclude the last 6 bytes, which is the checksum.
|
|
||||||
return hrp, decoded[:len(decoded)-6], bech32Version, nil |
|
||||||
} |
|
||||||
|
|
||||||
// DecodeNoLimit decodes a bech32 encoded string, returning the human-readable
|
|
||||||
// part and the data part excluding the checksum. This function does NOT
|
|
||||||
// validate against the BIP-173 maximum length allowed for bech32 strings and
|
|
||||||
// is meant for use in custom applications (such as lightning network payment
|
|
||||||
// requests), NOT on-chain addresses.
|
|
||||||
//
|
|
||||||
// Note that the returned data is 5-bit (base32) encoded and the human-readable
|
|
||||||
// part will be lowercase.
|
|
||||||
func DecodeNoLimit(bech []byte) ([]byte, []byte, error) { |
|
||||||
hrp, data, _, err := decodeNoLimit(bech) |
|
||||||
return hrp, data, err |
|
||||||
} |
|
||||||
|
|
||||||
// Decode decodes a bech32 encoded string, returning the human-readable part and
|
|
||||||
// the data part excluding the checksum.
|
|
||||||
//
|
|
||||||
// Note that the returned data is 5-bit (base32) encoded and the human-readable
|
|
||||||
// part will be lowercase.
|
|
||||||
func Decode(bech []byte) ([]byte, []byte, error) { |
|
||||||
// The maximum allowed length for a bech32 string is 90.
|
|
||||||
if len(bech) > 90 { |
|
||||||
return nil, nil, ErrInvalidLength(len(bech)) |
|
||||||
} |
|
||||||
hrp, data, _, err := decodeNoLimit(bech) |
|
||||||
return hrp, data, err |
|
||||||
} |
|
||||||
|
|
||||||
// DecodeGeneric is identical to the existing Decode method, but will also
|
|
||||||
// return bech32 version that matches the decoded checksum. This method should
|
|
||||||
// be used when decoding segwit addresses, as it enables additional
|
|
||||||
// verification to ensure the proper checksum is used.
|
|
||||||
func DecodeGeneric(bech []byte) ([]byte, []byte, Version, error) { |
|
||||||
// The maximum allowed length for a bech32 string is 90.
|
|
||||||
if len(bech) > 90 { |
|
||||||
return nil, nil, VersionUnknown, ErrInvalidLength(len(bech)) |
|
||||||
} |
|
||||||
return decodeNoLimit(bech) |
|
||||||
} |
|
||||||
|
|
||||||
// encodeGeneric is the base bech32 encoding function that is aware of the
|
|
||||||
// existence of the checksum versions. This method is private, as the Encode
|
|
||||||
// and EncodeM methods are intended to be used instead.
|
|
||||||
func encodeGeneric(hrp []byte, data []byte, version Version) ([]byte, error) { |
|
||||||
// The resulting bech32 string is the concatenation of the lowercase
|
|
||||||
// hrp, the separator 1, data and the 6-byte checksum.
|
|
||||||
hrp = bytes.ToLower(hrp) |
|
||||||
var bldr bytes.Buffer |
|
||||||
bldr.Grow(len(hrp) + 1 + len(data) + 6) |
|
||||||
bldr.Write(hrp) |
|
||||||
bldr.WriteString("1") |
|
||||||
// Write the data part, using the bech32 charset.
|
|
||||||
for _, b := range data { |
|
||||||
if int(b) >= len(Charset) { |
|
||||||
return nil, ErrInvalidDataByte(b) |
|
||||||
} |
|
||||||
bldr.WriteByte(Charset[b]) |
|
||||||
} |
|
||||||
// Calculate and write the checksum of the data.
|
|
||||||
writeBech32Checksum(hrp, data, &bldr, version) |
|
||||||
return bldr.Bytes(), nil |
|
||||||
} |
|
||||||
|
|
||||||
// Encode encodes a byte slice into a bech32 string with the given
|
|
||||||
// human-readable part (HRP). The HRP will be converted to lowercase if needed
|
|
||||||
// since mixed cased encodings are not permitted and lowercase is used for
|
|
||||||
// checksum purposes. Note that the bytes must each encode 5 bits (base32).
|
|
||||||
func Encode(hrp, data []byte) ([]byte, error) { |
|
||||||
return encodeGeneric(hrp, data, Version0) |
|
||||||
} |
|
||||||
|
|
||||||
// EncodeM is the exactly same as the Encode method, but it uses the new
|
|
||||||
// bech32m constant instead of the original one. It should be used whenever one
|
|
||||||
// attempts to encode a segwit address of v1 and beyond.
|
|
||||||
func EncodeM(hrp, data []byte) ([]byte, error) { |
|
||||||
return encodeGeneric(hrp, data, VersionM) |
|
||||||
} |
|
||||||
|
|
||||||
// ConvertBits converts a byte slice where each byte is encoding fromBits bits,
|
|
||||||
// to a byte slice where each byte is encoding toBits bits.
|
|
||||||
func ConvertBits(data []byte, fromBits, toBits uint8, pad bool) ( |
|
||||||
[]byte, |
|
||||||
error, |
|
||||||
) { |
|
||||||
|
|
||||||
if fromBits < 1 || fromBits > 8 || toBits < 1 || toBits > 8 { |
|
||||||
return nil, ErrInvalidBitGroups{} |
|
||||||
} |
|
||||||
// Determine the maximum size the resulting array can have after base
|
|
||||||
// conversion, so that we can size it a single time. This might be off
|
|
||||||
// by a byte depending on whether padding is used or not and if the input
|
|
||||||
// data is a multiple of both fromBits and toBits, but we ignore that and
|
|
||||||
// just size it to the maximum possible.
|
|
||||||
maxSize := len(data)*int(fromBits)/int(toBits) + 1 |
|
||||||
// The final bytes, each byte encoding toBits bits.
|
|
||||||
regrouped := make([]byte, 0, maxSize) |
|
||||||
// Keep track of the next byte we create and how many bits we have
|
|
||||||
// added to it out of the toBits goal.
|
|
||||||
nextByte := byte(0) |
|
||||||
filledBits := uint8(0) |
|
||||||
for _, b := range data { |
|
||||||
// Discard unused bits.
|
|
||||||
b <<= 8 - fromBits |
|
||||||
// How many bits remaining to extract from the input data.
|
|
||||||
remFromBits := fromBits |
|
||||||
for remFromBits > 0 { |
|
||||||
// How many bits remaining to be added to the next byte.
|
|
||||||
remToBits := toBits - filledBits |
|
||||||
// The number of bytes to next extract is the minimum of
|
|
||||||
// remFromBits and remToBits.
|
|
||||||
toExtract := remFromBits |
|
||||||
if remToBits < toExtract { |
|
||||||
toExtract = remToBits |
|
||||||
} |
|
||||||
// Add the next bits to nextByte, shifting the already
|
|
||||||
// added bits to the left.
|
|
||||||
nextByte = (nextByte << toExtract) | (b >> (8 - toExtract)) |
|
||||||
// Discard the bits we just extracted and get ready for
|
|
||||||
// next iteration.
|
|
||||||
b <<= toExtract |
|
||||||
remFromBits -= toExtract |
|
||||||
filledBits += toExtract |
|
||||||
// If the nextByte is completely filled, we add it to
|
|
||||||
// our regrouped bytes and start on the next byte.
|
|
||||||
if filledBits == toBits { |
|
||||||
regrouped = append(regrouped, nextByte) |
|
||||||
filledBits = 0 |
|
||||||
nextByte = 0 |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
// We pad any unfinished group if specified.
|
|
||||||
if pad && filledBits > 0 { |
|
||||||
nextByte <<= toBits - filledBits |
|
||||||
regrouped = append(regrouped, nextByte) |
|
||||||
filledBits = 0 |
|
||||||
nextByte = 0 |
|
||||||
} |
|
||||||
// Any incomplete group must be <= 4 bits, and all zeroes.
|
|
||||||
if filledBits > 0 && (filledBits > 4 || nextByte != 0) { |
|
||||||
return nil, ErrInvalidIncompleteGroup{} |
|
||||||
} |
|
||||||
return regrouped, nil |
|
||||||
} |
|
||||||
|
|
||||||
// EncodeFromBase256 converts a base256-encoded byte slice into a base32-encoded
|
|
||||||
// byte slice and then encodes it into a bech32 string with the given
|
|
||||||
// human-readable part (HRP). The HRP will be converted to lowercase if needed
|
|
||||||
// since mixed cased encodings are not permitted and lowercase is used for
|
|
||||||
// checksum purposes.
|
|
||||||
func EncodeFromBase256(hrp, data []byte) ([]byte, error) { |
|
||||||
converted, err := ConvertBits(data, 8, 5, true) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return Encode(hrp, converted) |
|
||||||
} |
|
||||||
|
|
||||||
// DecodeToBase256 decodes a bech32-encoded string into its associated
|
|
||||||
// human-readable part (HRP) and base32-encoded data, converts that data to a
|
|
||||||
// base256-encoded byte slice and returns it along with the lowercase HRP.
|
|
||||||
func DecodeToBase256(bech []byte) ([]byte, []byte, error) { |
|
||||||
hrp, data, err := Decode(bech) |
|
||||||
if err != nil { |
|
||||||
return nil, nil, err |
|
||||||
} |
|
||||||
converted, err := ConvertBits(data, 5, 8, false) |
|
||||||
if err != nil { |
|
||||||
return nil, nil, err |
|
||||||
} |
|
||||||
return hrp, converted, nil |
|
||||||
} |
|
||||||
@ -1,776 +0,0 @@ |
|||||||
// Copyright (c) 2017-2020 The btcsuite developers
|
|
||||||
// Copyright (c) 2019 The Decred developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package bech32 |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"encoding/hex" |
|
||||||
"errors" |
|
||||||
"fmt" |
|
||||||
"strings" |
|
||||||
"testing" |
|
||||||
|
|
||||||
"next.orly.dev/pkg/utils" |
|
||||||
) |
|
||||||
|
|
||||||
// TestBech32 tests whether decoding and re-encoding the valid BIP-173 test
|
|
||||||
// vectors works and if decoding invalid test vectors fails for the correct
|
|
||||||
// reason.
|
|
||||||
func TestBech32(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
str string |
|
||||||
expectedError error |
|
||||||
}{ |
|
||||||
{"A12UEL5L", nil}, |
|
||||||
{"a12uel5l", nil}, |
|
||||||
{ |
|
||||||
"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", |
|
||||||
nil, |
|
||||||
}, |
|
||||||
{"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", nil}, |
|
||||||
{ |
|
||||||
"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", |
|
||||||
nil, |
|
||||||
}, |
|
||||||
{"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", nil}, |
|
||||||
{ |
|
||||||
"split1checkupstagehandshakeupstreamerranterredcaperred2y9e2w", |
|
||||||
ErrInvalidChecksum{ |
|
||||||
"2y9e3w", "2y9e3wlc445v", |
|
||||||
"2y9e2w", |
|
||||||
}, |
|
||||||
}, // invalid checksum
|
|
||||||
{ |
|
||||||
"s lit1checkupstagehandshakeupstreamerranterredcaperredp8hs2p", |
|
||||||
ErrInvalidCharacter(' '), |
|
||||||
}, // invalid character (space) in hrp
|
|
||||||
{ |
|
||||||
"spl\x7Ft1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", |
|
||||||
ErrInvalidCharacter(127), |
|
||||||
}, // invalid character (DEL) in hrp
|
|
||||||
{ |
|
||||||
"split1cheo2y9e2w", |
|
||||||
ErrNonCharsetChar('o'), |
|
||||||
}, // invalid character (o) in data part
|
|
||||||
{"split1a2y9w", ErrInvalidSeparatorIndex(5)}, // too short data part
|
|
||||||
{ |
|
||||||
"1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", |
|
||||||
ErrInvalidSeparatorIndex(0), |
|
||||||
}, // empty hrp
|
|
||||||
{ |
|
||||||
"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", |
|
||||||
ErrInvalidLength(91), |
|
||||||
}, // too long
|
|
||||||
// Additional test vectors used in bitcoin core
|
|
||||||
{" 1nwldj5", ErrInvalidCharacter(' ')}, |
|
||||||
{"\x7f" + "1axkwrx", ErrInvalidCharacter(0x7f)}, |
|
||||||
{"\x801eym55h", ErrInvalidCharacter(0x80)}, |
|
||||||
{ |
|
||||||
"an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx", |
|
||||||
ErrInvalidLength(91), |
|
||||||
}, |
|
||||||
{"pzry9x0s0muk", ErrInvalidSeparatorIndex(-1)}, |
|
||||||
{"1pzry9x0s0muk", ErrInvalidSeparatorIndex(0)}, |
|
||||||
{"x1b4n0q5v", ErrNonCharsetChar(98)}, |
|
||||||
{"li1dgmt3", ErrInvalidSeparatorIndex(2)}, |
|
||||||
{"de1lg7wt\xff", ErrInvalidCharacter(0xff)}, |
|
||||||
{"A1G7SGD8", ErrInvalidChecksum{"2uel5l", "2uel5llqfn3a", "g7sgd8"}}, |
|
||||||
{"10a06t8", ErrInvalidLength(7)}, |
|
||||||
{"1qzzfhee", ErrInvalidSeparatorIndex(0)}, |
|
||||||
{"a12UEL5L", ErrMixedCase{}}, |
|
||||||
{"A12uEL5L", ErrMixedCase{}}, |
|
||||||
} |
|
||||||
for i, test := range tests { |
|
||||||
str := []byte(test.str) |
|
||||||
hrp, decoded, err := Decode([]byte(str)) |
|
||||||
if !errors.Is(err, test.expectedError) { |
|
||||||
t.Errorf( |
|
||||||
"%d: expected decoding error %v "+ |
|
||||||
"instead got %v", i, test.expectedError, err, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
if err != nil { |
|
||||||
// End test case here if a decoding error was expected.
|
|
||||||
continue |
|
||||||
} |
|
||||||
// Check that it encodes to the same string
|
|
||||||
encoded, err := Encode(hrp, decoded) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("encoding failed: %v", err) |
|
||||||
} |
|
||||||
if !utils.FastEqual(encoded, bytes.ToLower([]byte(str))) { |
|
||||||
t.Errorf( |
|
||||||
"expected data to encode to %v, but got %v", |
|
||||||
str, encoded, |
|
||||||
) |
|
||||||
} |
|
||||||
// Flip a bit in the string an make sure it is caught.
|
|
||||||
pos := bytes.LastIndexAny(str, "1") |
|
||||||
flipped := []byte(string(str[:pos+1]) + string(str[pos+1]^1) + string(str[pos+2:])) |
|
||||||
_, _, err = Decode(flipped) |
|
||||||
if err == nil { |
|
||||||
t.Error("expected decoding to fail") |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// TestBech32M tests that the following set of strings, based on the test
|
|
||||||
// vectors in BIP-350 are either valid or invalid using the new bech32m
|
|
||||||
// checksum algo. Some of these strings are similar to the set of above test
|
|
||||||
// vectors, but end up with different checksums.
|
|
||||||
func TestBech32M(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
str string |
|
||||||
expectedError error |
|
||||||
}{ |
|
||||||
{"A1LQFN3A", nil}, |
|
||||||
{"a1lqfn3a", nil}, |
|
||||||
{ |
|
||||||
"an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6", |
|
||||||
nil, |
|
||||||
}, |
|
||||||
{"abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx", nil}, |
|
||||||
{ |
|
||||||
"11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8", |
|
||||||
nil, |
|
||||||
}, |
|
||||||
{"split1checkupstagehandshakeupstreamerranterredcaperredlc445v", nil}, |
|
||||||
{"?1v759aa", nil}, |
|
||||||
// Additional test vectors used in bitcoin core
|
|
||||||
{"\x201xj0phk", ErrInvalidCharacter('\x20')}, |
|
||||||
{"\x7f1g6xzxy", ErrInvalidCharacter('\x7f')}, |
|
||||||
{"\x801vctc34", ErrInvalidCharacter('\x80')}, |
|
||||||
{ |
|
||||||
"an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4", |
|
||||||
ErrInvalidLength(91), |
|
||||||
}, |
|
||||||
{"qyrz8wqd2c9m", ErrInvalidSeparatorIndex(-1)}, |
|
||||||
{"1qyrz8wqd2c9m", ErrInvalidSeparatorIndex(0)}, |
|
||||||
{"y1b0jsk6g", ErrNonCharsetChar(98)}, |
|
||||||
{"lt1igcx5c0", ErrNonCharsetChar(105)}, |
|
||||||
{"in1muywd", ErrInvalidSeparatorIndex(2)}, |
|
||||||
{"mm1crxm3i", ErrNonCharsetChar(105)}, |
|
||||||
{"au1s5cgom", ErrNonCharsetChar(111)}, |
|
||||||
{"M1VUXWEZ", ErrInvalidChecksum{"mzl49c", "mzl49cw70eq6", "vuxwez"}}, |
|
||||||
{"16plkw9", ErrInvalidLength(7)}, |
|
||||||
{"1p2gdwpf", ErrInvalidSeparatorIndex(0)}, |
|
||||||
|
|
||||||
{" 1nwldj5", ErrInvalidCharacter(' ')}, |
|
||||||
{"\x7f" + "1axkwrx", ErrInvalidCharacter(0x7f)}, |
|
||||||
{"\x801eym55h", ErrInvalidCharacter(0x80)}, |
|
||||||
} |
|
||||||
for i, test := range tests { |
|
||||||
str := []byte(test.str) |
|
||||||
hrp, decoded, err := Decode(str) |
|
||||||
if test.expectedError != err { |
|
||||||
t.Errorf( |
|
||||||
"%d: (%v) expected decoding error %v "+ |
|
||||||
"instead got %v", i, str, test.expectedError, |
|
||||||
err, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
if err != nil { |
|
||||||
// End test case here if a decoding error was expected.
|
|
||||||
continue |
|
||||||
} |
|
||||||
// Check that it encodes to the same string, using bech32 m.
|
|
||||||
encoded, err := EncodeM(hrp, decoded) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("encoding failed: %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
if !utils.FastEqual(encoded, bytes.ToLower(str)) { |
|
||||||
t.Errorf( |
|
||||||
"expected data to encode to %v, but got %v", |
|
||||||
str, encoded, |
|
||||||
) |
|
||||||
} |
|
||||||
// Flip a bit in the string an make sure it is caught.
|
|
||||||
pos := bytes.LastIndexAny(str, "1") |
|
||||||
flipped := []byte(string(str[:pos+1]) + string(str[pos+1]^1) + string(str[pos+2:])) |
|
||||||
_, _, err = Decode(flipped) |
|
||||||
if err == nil { |
|
||||||
t.Error("expected decoding to fail") |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// TestBech32DecodeGeneric tests that given a bech32 string, or a bech32m
|
|
||||||
// string, the proper checksum version is returned so that callers can perform
|
|
||||||
// segwit addr validation.
|
|
||||||
func TestBech32DecodeGeneric(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
str string |
|
||||||
version Version |
|
||||||
}{ |
|
||||||
{"A1LQFN3A", VersionM}, |
|
||||||
{"a1lqfn3a", VersionM}, |
|
||||||
{ |
|
||||||
"an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6", |
|
||||||
VersionM, |
|
||||||
}, |
|
||||||
{"abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx", VersionM}, |
|
||||||
{ |
|
||||||
"11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8", |
|
||||||
VersionM, |
|
||||||
}, |
|
||||||
{ |
|
||||||
"split1checkupstagehandshakeupstreamerranterredcaperredlc445v", |
|
||||||
VersionM, |
|
||||||
}, |
|
||||||
{"?1v759aa", VersionM}, |
|
||||||
{"A12UEL5L", Version0}, |
|
||||||
{"a12uel5l", Version0}, |
|
||||||
{ |
|
||||||
"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", |
|
||||||
Version0, |
|
||||||
}, |
|
||||||
{"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", Version0}, |
|
||||||
{ |
|
||||||
"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", |
|
||||||
Version0, |
|
||||||
}, |
|
||||||
{ |
|
||||||
"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", |
|
||||||
Version0, |
|
||||||
}, |
|
||||||
{"BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", Version0}, |
|
||||||
{ |
|
||||||
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", |
|
||||||
Version0, |
|
||||||
}, |
|
||||||
{ |
|
||||||
"bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", |
|
||||||
VersionM, |
|
||||||
}, |
|
||||||
{"BC1SW50QGDZ25J", VersionM}, |
|
||||||
{"bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", VersionM}, |
|
||||||
{ |
|
||||||
"tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", |
|
||||||
Version0, |
|
||||||
}, |
|
||||||
{ |
|
||||||
"tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c", |
|
||||||
VersionM, |
|
||||||
}, |
|
||||||
{ |
|
||||||
"bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", |
|
||||||
VersionM, |
|
||||||
}, |
|
||||||
} |
|
||||||
for i, test := range tests { |
|
||||||
_, _, version, err := DecodeGeneric([]byte(test.str)) |
|
||||||
if err != nil { |
|
||||||
t.Errorf( |
|
||||||
"%d: (%v) unexpected error during "+ |
|
||||||
"decoding: %v", i, test.str, err, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
if version != test.version { |
|
||||||
t.Errorf( |
|
||||||
"(%v): invalid version: expected %v, got %v", |
|
||||||
test.str, test.version, version, |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// TestMixedCaseEncode ensures mixed case HRPs are converted to lowercase as
|
|
||||||
// expected when encoding and that decoding the produced encoding when converted
|
|
||||||
// to all uppercase produces the lowercase HRP and original data.
|
|
||||||
func TestMixedCaseEncode(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
name string |
|
||||||
hrp string |
|
||||||
data string |
|
||||||
encoded string |
|
||||||
}{ |
|
||||||
{ |
|
||||||
name: "all uppercase HRP with no data", |
|
||||||
hrp: "A", |
|
||||||
data: "", |
|
||||||
encoded: "a12uel5l", |
|
||||||
}, { |
|
||||||
name: "all uppercase HRP with data", |
|
||||||
hrp: "UPPERCASE", |
|
||||||
data: "787878", |
|
||||||
encoded: "uppercase10pu8sss7kmp", |
|
||||||
}, { |
|
||||||
name: "mixed case HRP even offsets uppercase", |
|
||||||
hrp: "AbCdEf", |
|
||||||
data: "00443214c74254b635cf84653a56d7c675be77df", |
|
||||||
encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", |
|
||||||
}, { |
|
||||||
name: "mixed case HRP odd offsets uppercase ", |
|
||||||
hrp: "aBcDeF", |
|
||||||
data: "00443214c74254b635cf84653a56d7c675be77df", |
|
||||||
encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", |
|
||||||
}, { |
|
||||||
name: "all lowercase HRP", |
|
||||||
hrp: "abcdef", |
|
||||||
data: "00443214c74254b635cf84653a56d7c675be77df", |
|
||||||
encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", |
|
||||||
}, |
|
||||||
} |
|
||||||
for _, test := range tests { |
|
||||||
// Convert the text hex to bytes, convert those bytes from base256 to
|
|
||||||
// base32, then ensure the encoded result with the HRP provided in the
|
|
||||||
// test data is as expected.
|
|
||||||
data, err := hex.DecodeString(test.data) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("%q: invalid hex %q: %v", test.name, test.data, err) |
|
||||||
continue |
|
||||||
} |
|
||||||
convertedData, err := ConvertBits(data, 8, 5, true) |
|
||||||
if err != nil { |
|
||||||
t.Errorf( |
|
||||||
"%q: unexpected convert bits error: %v", test.name, |
|
||||||
err, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
gotEncoded, err := Encode([]byte(test.hrp), convertedData) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("%q: unexpected encode error: %v", test.name, err) |
|
||||||
continue |
|
||||||
} |
|
||||||
if !utils.FastEqual(gotEncoded, []byte(test.encoded)) { |
|
||||||
t.Errorf( |
|
||||||
"%q: mismatched encoding -- got %q, want %q", test.name, |
|
||||||
gotEncoded, test.encoded, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
// Ensure the decoding the expected lowercase encoding converted to all
|
|
||||||
// uppercase produces the lowercase HRP and original data.
|
|
||||||
gotHRP, gotData, err := Decode(bytes.ToUpper([]byte(test.encoded))) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("%q: unexpected decode error: %v", test.name, err) |
|
||||||
continue |
|
||||||
} |
|
||||||
wantHRP := strings.ToLower(test.hrp) |
|
||||||
if !utils.FastEqual(gotHRP, []byte(wantHRP)) { |
|
||||||
t.Errorf( |
|
||||||
"%q: mismatched decoded HRP -- got %q, want %q", test.name, |
|
||||||
gotHRP, wantHRP, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
convertedGotData, err := ConvertBits(gotData, 5, 8, false) |
|
||||||
if err != nil { |
|
||||||
t.Errorf( |
|
||||||
"%q: unexpected convert bits error: %v", test.name, |
|
||||||
err, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
if !utils.FastEqual(convertedGotData, data) { |
|
||||||
t.Errorf( |
|
||||||
"%q: mismatched data -- got %x, want %x", test.name, |
|
||||||
convertedGotData, data, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// TestCanDecodeUnlimtedBech32 tests whether decoding a large bech32 string works
|
|
||||||
// when using the DecodeNoLimit version
|
|
||||||
func TestCanDecodeUnlimtedBech32(t *testing.T) { |
|
||||||
input := "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq5kx0yd" |
|
||||||
// Sanity check that an input of this length errors on regular Decode()
|
|
||||||
_, _, err := Decode([]byte(input)) |
|
||||||
if err == nil { |
|
||||||
t.Fatalf("Test vector not appropriate") |
|
||||||
} |
|
||||||
// Try and decode it.
|
|
||||||
hrp, data, err := DecodeNoLimit([]byte(input)) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf( |
|
||||||
"Expected decoding of large string to work. Got error: %v", |
|
||||||
err, |
|
||||||
) |
|
||||||
} |
|
||||||
// Verify data for correctness.
|
|
||||||
if !utils.FastEqual(hrp, []byte("1")) { |
|
||||||
t.Fatalf("Unexpected hrp: %v", hrp) |
|
||||||
} |
|
||||||
decodedHex := fmt.Sprintf("%x", data) |
|
||||||
expected := "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000" |
|
||||||
if decodedHex != expected { |
|
||||||
t.Fatalf("Unexpected decoded data: %s", decodedHex) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// TestBech32Base256 ensures decoding and encoding various bech32, HRPs, and
|
|
||||||
// data produces the expected results when using EncodeFromBase256 and
|
|
||||||
// DecodeToBase256. It includes tests for proper handling of case
|
|
||||||
// manipulations.
|
|
||||||
func TestBech32Base256(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
name string // test name
|
|
||||||
encoded string // bech32 string to decode
|
|
||||||
hrp string // expected human-readable part
|
|
||||||
data string // expected hex-encoded data
|
|
||||||
err error // expected error
|
|
||||||
}{ |
|
||||||
{ |
|
||||||
name: "all uppercase, no data", |
|
||||||
encoded: "A12UEL5L", |
|
||||||
hrp: "a", |
|
||||||
data: "", |
|
||||||
}, { |
|
||||||
name: "long hrp with separator and excluded chars, no data", |
|
||||||
encoded: "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs", |
|
||||||
hrp: "an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio", |
|
||||||
data: "", |
|
||||||
}, { |
|
||||||
name: "6 char hrp with data with leading zero", |
|
||||||
encoded: "abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", |
|
||||||
hrp: "abcdef", |
|
||||||
data: "00443214c74254b635cf84653a56d7c675be77df", |
|
||||||
}, { |
|
||||||
name: "hrp same as separator and max length encoded string", |
|
||||||
encoded: "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", |
|
||||||
hrp: "1", |
|
||||||
data: "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", |
|
||||||
}, { |
|
||||||
name: "5 char hrp with data chosen to produce human-readable data part", |
|
||||||
encoded: "split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", |
|
||||||
hrp: "split", |
|
||||||
data: "c5f38b70305f519bf66d85fb6cf03058f3dde463ecd7918f2dc743918f2d", |
|
||||||
}, { |
|
||||||
name: "same as previous but with checksum invalidated", |
|
||||||
encoded: "split1checkupstagehandshakeupstreamerranterredcaperred2y9e2w", |
|
||||||
err: ErrInvalidChecksum{"2y9e3w", "2y9e3wlc445v", "2y9e2w"}, |
|
||||||
}, { |
|
||||||
name: "hrp with invalid character (space)", |
|
||||||
encoded: "s lit1checkupstagehandshakeupstreamerranterredcaperredp8hs2p", |
|
||||||
err: ErrInvalidCharacter(' '), |
|
||||||
}, { |
|
||||||
name: "hrp with invalid character (DEL)", |
|
||||||
encoded: "spl\x7ft1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", |
|
||||||
err: ErrInvalidCharacter(127), |
|
||||||
}, { |
|
||||||
name: "data part with invalid character (o)", |
|
||||||
encoded: "split1cheo2y9e2w", |
|
||||||
err: ErrNonCharsetChar('o'), |
|
||||||
}, { |
|
||||||
name: "data part too short", |
|
||||||
encoded: "split1a2y9w", |
|
||||||
err: ErrInvalidSeparatorIndex(5), |
|
||||||
}, { |
|
||||||
name: "empty hrp", |
|
||||||
encoded: "1checkupstagehandshakeupstreamerranterredcaperred2y9e3w", |
|
||||||
err: ErrInvalidSeparatorIndex(0), |
|
||||||
}, { |
|
||||||
name: "no separator", |
|
||||||
encoded: "pzry9x0s0muk", |
|
||||||
err: ErrInvalidSeparatorIndex(-1), |
|
||||||
}, { |
|
||||||
name: "too long by one char", |
|
||||||
encoded: "11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqc8247j", |
|
||||||
err: ErrInvalidLength(91), |
|
||||||
}, { |
|
||||||
name: "invalid due to mixed case in hrp", |
|
||||||
encoded: "aBcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", |
|
||||||
err: ErrMixedCase{}, |
|
||||||
}, { |
|
||||||
name: "invalid due to mixed case in data part", |
|
||||||
encoded: "abcdef1Qpzry9x8gf2tvdw0s3jn54khce6mua7lmqqqxw", |
|
||||||
err: ErrMixedCase{}, |
|
||||||
}, |
|
||||||
} |
|
||||||
for _, test := range tests { |
|
||||||
// Ensure the decode either produces an error or not as expected.
|
|
||||||
str := test.encoded |
|
||||||
gotHRP, gotData, err := DecodeToBase256([]byte(str)) |
|
||||||
if test.err != err { |
|
||||||
t.Errorf( |
|
||||||
"%q: unexpected decode error -- got %v, want %v", |
|
||||||
test.name, err, test.err, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
if err != nil { |
|
||||||
// End test case here if a decoding error was expected.
|
|
||||||
continue |
|
||||||
} |
|
||||||
// Ensure the expected HRP and original data are as expected.
|
|
||||||
if !utils.FastEqual(gotHRP, []byte(test.hrp)) { |
|
||||||
t.Errorf( |
|
||||||
"%q: mismatched decoded HRP -- got %q, want %q", test.name, |
|
||||||
gotHRP, test.hrp, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
data, err := hex.DecodeString(test.data) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("%q: invalid hex %q: %v", test.name, test.data, err) |
|
||||||
continue |
|
||||||
} |
|
||||||
if !utils.FastEqual(gotData, data) { |
|
||||||
t.Errorf( |
|
||||||
"%q: mismatched data -- got %x, want %x", test.name, |
|
||||||
gotData, data, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
// Encode the same data with the HRP converted to all uppercase and
|
|
||||||
// ensure the result is the lowercase version of the original encoded
|
|
||||||
// bech32 string.
|
|
||||||
gotEncoded, err := EncodeFromBase256( |
|
||||||
bytes.ToUpper([]byte(test.hrp)), data, |
|
||||||
) |
|
||||||
if err != nil { |
|
||||||
t.Errorf( |
|
||||||
"%q: unexpected uppercase HRP encode error: %v", test.name, |
|
||||||
err, |
|
||||||
) |
|
||||||
} |
|
||||||
wantEncoded := bytes.ToLower([]byte(str)) |
|
||||||
if !utils.FastEqual(gotEncoded, wantEncoded) { |
|
||||||
t.Errorf( |
|
||||||
"%q: mismatched encoding -- got %q, want %q", test.name, |
|
||||||
gotEncoded, wantEncoded, |
|
||||||
) |
|
||||||
} |
|
||||||
// Encode the same data with the HRP converted to all lowercase and
|
|
||||||
// ensure the result is the lowercase version of the original encoded
|
|
||||||
// bech32 string.
|
|
||||||
gotEncoded, err = EncodeFromBase256( |
|
||||||
bytes.ToLower([]byte(test.hrp)), data, |
|
||||||
) |
|
||||||
if err != nil { |
|
||||||
t.Errorf( |
|
||||||
"%q: unexpected lowercase HRP encode error: %v", test.name, |
|
||||||
err, |
|
||||||
) |
|
||||||
} |
|
||||||
if !utils.FastEqual(gotEncoded, wantEncoded) { |
|
||||||
t.Errorf( |
|
||||||
"%q: mismatched encoding -- got %q, want %q", test.name, |
|
||||||
gotEncoded, wantEncoded, |
|
||||||
) |
|
||||||
} |
|
||||||
// Encode the same data with the HRP converted to mixed upper and
|
|
||||||
// lowercase and ensure the result is the lowercase version of the
|
|
||||||
// original encoded bech32 string.
|
|
||||||
var mixedHRPBuilder bytes.Buffer |
|
||||||
for i, r := range test.hrp { |
|
||||||
if i%2 == 0 { |
|
||||||
mixedHRPBuilder.WriteString(strings.ToUpper(string(r))) |
|
||||||
continue |
|
||||||
} |
|
||||||
mixedHRPBuilder.WriteRune(r) |
|
||||||
} |
|
||||||
gotEncoded, err = EncodeFromBase256(mixedHRPBuilder.Bytes(), data) |
|
||||||
if err != nil { |
|
||||||
t.Errorf( |
|
||||||
"%q: unexpected lowercase HRP encode error: %v", test.name, |
|
||||||
err, |
|
||||||
) |
|
||||||
} |
|
||||||
if !utils.FastEqual(gotEncoded, wantEncoded) { |
|
||||||
t.Errorf( |
|
||||||
"%q: mismatched encoding -- got %q, want %q", test.name, |
|
||||||
gotEncoded, wantEncoded, |
|
||||||
) |
|
||||||
} |
|
||||||
// Ensure a bit flip in the string is caught.
|
|
||||||
pos := strings.LastIndexAny(test.encoded, "1") |
|
||||||
flipped := str[:pos+1] + string(str[pos+1]^1) + str[pos+2:] |
|
||||||
_, _, err = DecodeToBase256([]byte(flipped)) |
|
||||||
if err == nil { |
|
||||||
t.Error("expected decoding to fail") |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkEncodeDecodeCycle performs a benchmark for a full encode/decode
|
|
||||||
// cycle of a bech32 string. It also reports the allocation count, which we
|
|
||||||
// expect to be 2 for a fully optimized cycle.
|
|
||||||
func BenchmarkEncodeDecodeCycle(b *testing.B) { |
|
||||||
// Use a fixed, 49-byte raw data for testing.
|
|
||||||
inputData, err := hex.DecodeString("cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed1") |
|
||||||
if err != nil { |
|
||||||
b.Fatalf("failed to initialize input data: %v", err) |
|
||||||
} |
|
||||||
// Convert this into a 79-byte, base 32 byte slice.
|
|
||||||
base32Input, err := ConvertBits(inputData, 8, 5, true) |
|
||||||
if err != nil { |
|
||||||
b.Fatalf("failed to convert input to 32 bits-per-element: %v", err) |
|
||||||
} |
|
||||||
// Use a fixed hrp for the tests. This should generate an encoded bech32
|
|
||||||
// string of size 90 (the maximum allowed by BIP-173).
|
|
||||||
hrp := "bc" |
|
||||||
// Begin the benchmark. Given that we test one roundtrip per iteration
|
|
||||||
// (that is, one Encode() and one Decode() operation), we expect at most
|
|
||||||
// 2 allocations per reported test op.
|
|
||||||
b.ReportAllocs() |
|
||||||
b.ResetTimer() |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
str, err := Encode([]byte(hrp), base32Input) |
|
||||||
if err != nil { |
|
||||||
b.Fatalf("failed to encode input: %v", err) |
|
||||||
} |
|
||||||
_, _, err = Decode(str) |
|
||||||
if err != nil { |
|
||||||
b.Fatalf("failed to decode string: %v", err) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// TestConvertBits tests whether base conversion works using TestConvertBits().
|
|
||||||
func TestConvertBits(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
input string |
|
||||||
output string |
|
||||||
fromBits uint8 |
|
||||||
toBits uint8 |
|
||||||
pad bool |
|
||||||
}{ |
|
||||||
// Trivial empty conversions.
|
|
||||||
{"", "", 8, 5, false}, |
|
||||||
{"", "", 8, 5, true}, |
|
||||||
{"", "", 5, 8, false}, |
|
||||||
{"", "", 5, 8, true}, |
|
||||||
// Conversions of 0 value with/without padding.
|
|
||||||
{"00", "00", 8, 5, false}, |
|
||||||
{"00", "0000", 8, 5, true}, |
|
||||||
{"0000", "00", 5, 8, false}, |
|
||||||
{"0000", "0000", 5, 8, true}, |
|
||||||
// Testing when conversion ends exactly at the byte edge. This makes
|
|
||||||
// both padded and unpadded versions the same.
|
|
||||||
{"0000000000", "0000000000000000", 8, 5, false}, |
|
||||||
{"0000000000", "0000000000000000", 8, 5, true}, |
|
||||||
{"0000000000000000", "0000000000", 5, 8, false}, |
|
||||||
{"0000000000000000", "0000000000", 5, 8, true}, |
|
||||||
// Conversions of full byte sequences.
|
|
||||||
{"ffffff", "1f1f1f1f1e", 8, 5, true}, |
|
||||||
{"1f1f1f1f1e", "ffffff", 5, 8, false}, |
|
||||||
{"1f1f1f1f1e", "ffffff00", 5, 8, true}, |
|
||||||
// Sample random conversions.
|
|
||||||
{"c9ca", "190705", 8, 5, false}, |
|
||||||
{"c9ca", "19070500", 8, 5, true}, |
|
||||||
{"19070500", "c9ca", 5, 8, false}, |
|
||||||
{"19070500", "c9ca00", 5, 8, true}, |
|
||||||
// Test cases tested on TestConvertBitsFailures with their corresponding
|
|
||||||
// fixes.
|
|
||||||
{"ff", "1f1c", 8, 5, true}, |
|
||||||
{"1f1c10", "ff20", 5, 8, true}, |
|
||||||
// Large conversions.
|
|
||||||
{ |
|
||||||
"cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed1", |
|
||||||
"190f13030c170e1b1916141a13040a14040b011f01040e01071e0607160b1906070e06130801131b1a0416020e110008081c1f1a0e19040703120e1d0a06181b160d0407070c1a07070d11131d1408", |
|
||||||
8, 5, true, |
|
||||||
}, |
|
||||||
{ |
|
||||||
"190f13030c170e1b1916141a13040a14040b011f01040e01071e0607160b1906070e06130801131b1a0416020e110008081c1f1a0e19040703120e1d0a06181b160d0407070c1a07070d11131d1408", |
|
||||||
"cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed100", |
|
||||||
5, 8, true, |
|
||||||
}, |
|
||||||
} |
|
||||||
for i, tc := range tests { |
|
||||||
input, err := hex.DecodeString(tc.input) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("invalid test input data: %v", err) |
|
||||||
} |
|
||||||
expected, err := hex.DecodeString(tc.output) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("invalid test output data: %v", err) |
|
||||||
} |
|
||||||
actual, err := ConvertBits(input, tc.fromBits, tc.toBits, tc.pad) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("test case %d failed: %v", i, err) |
|
||||||
} |
|
||||||
if !utils.FastEqual(actual, expected) { |
|
||||||
t.Fatalf( |
|
||||||
"test case %d has wrong output; expected=%x actual=%x", |
|
||||||
i, expected, actual, |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// TestConvertBitsFailures tests for the expected conversion failures of
|
|
||||||
// ConvertBits().
|
|
||||||
func TestConvertBitsFailures(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
input string |
|
||||||
fromBits uint8 |
|
||||||
toBits uint8 |
|
||||||
pad bool |
|
||||||
err error |
|
||||||
}{ |
|
||||||
// Not enough output bytes when not using padding.
|
|
||||||
{"ff", 8, 5, false, ErrInvalidIncompleteGroup{}}, |
|
||||||
{"1f1c10", 5, 8, false, ErrInvalidIncompleteGroup{}}, |
|
||||||
// Unsupported bit conversions.
|
|
||||||
{"", 0, 5, false, ErrInvalidBitGroups{}}, |
|
||||||
{"", 10, 5, false, ErrInvalidBitGroups{}}, |
|
||||||
{"", 5, 0, false, ErrInvalidBitGroups{}}, |
|
||||||
{"", 5, 10, false, ErrInvalidBitGroups{}}, |
|
||||||
} |
|
||||||
for i, tc := range tests { |
|
||||||
input, err := hex.DecodeString(tc.input) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("invalid test input data: %v", err) |
|
||||||
} |
|
||||||
_, err = ConvertBits(input, tc.fromBits, tc.toBits, tc.pad) |
|
||||||
if err != tc.err { |
|
||||||
t.Fatalf( |
|
||||||
"test case %d failure: expected '%v' got '%v'", i, |
|
||||||
tc.err, err, |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkConvertBitsDown benchmarks the speed and memory allocation behavior
|
|
||||||
// of ConvertBits when converting from a higher base into a lower base (e.g. 8
|
|
||||||
// => 5).
|
|
||||||
//
|
|
||||||
// Only a single allocation is expected, which is used for the output array.
|
|
||||||
func BenchmarkConvertBitsDown(b *testing.B) { |
|
||||||
// Use a fixed, 49-byte raw data for testing.
|
|
||||||
inputData, err := hex.DecodeString("cbe6365ddbcda9a9915422c3f091c13f8c7b2f263b8d34067bd12c274408473fa764871c9dd51b1bb34873b3473b633ed1") |
|
||||||
if err != nil { |
|
||||||
b.Fatalf("failed to initialize input data: %v", err) |
|
||||||
} |
|
||||||
b.ReportAllocs() |
|
||||||
b.ResetTimer() |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
_, err := ConvertBits(inputData, 8, 5, true) |
|
||||||
if err != nil { |
|
||||||
b.Fatalf("error converting bits: %v", err) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkConvertBitsDown benchmarks the speed and memory allocation behavior
|
|
||||||
// of ConvertBits when converting from a lower base into a higher base (e.g. 5
|
|
||||||
// => 8).
|
|
||||||
//
|
|
||||||
// Only a single allocation is expected, which is used for the output array.
|
|
||||||
func BenchmarkConvertBitsUp(b *testing.B) { |
|
||||||
// Use a fixed, 79-byte raw data for testing.
|
|
||||||
inputData, err := hex.DecodeString("190f13030c170e1b1916141a13040a14040b011f01040e01071e0607160b1906070e06130801131b1a0416020e110008081c1f1a0e19040703120e1d0a06181b160d0407070c1a07070d11131d1408") |
|
||||||
if err != nil { |
|
||||||
b.Fatalf("failed to initialize input data: %v", err) |
|
||||||
} |
|
||||||
b.ReportAllocs() |
|
||||||
b.ResetTimer() |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
_, err := ConvertBits(inputData, 8, 5, true) |
|
||||||
if err != nil { |
|
||||||
b.Fatalf("error converting bits: %v", err) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,13 +0,0 @@ |
|||||||
// Copyright (c) 2017 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package bech32 provides a Go implementation of the bech32 format specified in
|
|
||||||
// BIP 173.
|
|
||||||
//
|
|
||||||
// Bech32 strings consist of a human-readable part (hrp), followed by the
|
|
||||||
// separator 1, then a checksummed data part encoded using the 32 characters
|
|
||||||
// "qpzry9x8gf2tvdw0s3jn54khce6mua7l".
|
|
||||||
//
|
|
||||||
// More info: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki
|
|
||||||
package bech32 |
|
||||||
@ -1,89 +0,0 @@ |
|||||||
// Copyright (c) 2019 The Decred developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package bech32 |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
) |
|
||||||
|
|
||||||
// ErrMixedCase is returned when the bech32 string has both lower and uppercase
|
|
||||||
// characters.
|
|
||||||
type ErrMixedCase struct{} |
|
||||||
|
|
||||||
func (err ErrMixedCase) Error() string { |
|
||||||
return "string not all lowercase or all uppercase" |
|
||||||
} |
|
||||||
|
|
||||||
// ErrInvalidBitGroups is returned when conversion is attempted between byte
|
|
||||||
// slices using bit-per-element of unsupported value.
|
|
||||||
type ErrInvalidBitGroups struct{} |
|
||||||
|
|
||||||
func (err ErrInvalidBitGroups) Error() string { |
|
||||||
return "only bit groups between 1 and 8 allowed" |
|
||||||
} |
|
||||||
|
|
||||||
// ErrInvalidIncompleteGroup is returned when then byte slice used as input has
|
|
||||||
// data of wrong length.
|
|
||||||
type ErrInvalidIncompleteGroup struct{} |
|
||||||
|
|
||||||
func (err ErrInvalidIncompleteGroup) Error() string { |
|
||||||
return "invalid incomplete group" |
|
||||||
} |
|
||||||
|
|
||||||
// ErrInvalidLength is returned when the bech32 string has an invalid length
|
|
||||||
// given the BIP-173 defined restrictions.
|
|
||||||
type ErrInvalidLength int |
|
||||||
|
|
||||||
func (err ErrInvalidLength) Error() string { |
|
||||||
return fmt.Sprintf("invalid bech32 string length %d", int(err)) |
|
||||||
} |
|
||||||
|
|
||||||
// ErrInvalidCharacter is returned when the bech32 string has a character
|
|
||||||
// outside the range of the supported charset.
|
|
||||||
type ErrInvalidCharacter rune |
|
||||||
|
|
||||||
func (err ErrInvalidCharacter) Error() string { |
|
||||||
return fmt.Sprintf("invalid character in string: '%c'", rune(err)) |
|
||||||
} |
|
||||||
|
|
||||||
// ErrInvalidSeparatorIndex is returned when the separator character '1' is
|
|
||||||
// in an invalid position in the bech32 string.
|
|
||||||
type ErrInvalidSeparatorIndex int |
|
||||||
|
|
||||||
func (err ErrInvalidSeparatorIndex) Error() string { |
|
||||||
return fmt.Sprintf("invalid separator index %d", int(err)) |
|
||||||
} |
|
||||||
|
|
||||||
// ErrNonCharsetChar is returned when a character outside of the specific
|
|
||||||
// bech32 charset is used in the string.
|
|
||||||
type ErrNonCharsetChar rune |
|
||||||
|
|
||||||
func (err ErrNonCharsetChar) Error() string { |
|
||||||
return fmt.Sprintf("invalid character not part of charset: %v", int(err)) |
|
||||||
} |
|
||||||
|
|
||||||
// ErrInvalidChecksum is returned when the extracted checksum of the string
|
|
||||||
// is different than what was expected. Both the original version, as well as
|
|
||||||
// the new bech32m checksum may be specified.
|
|
||||||
type ErrInvalidChecksum struct { |
|
||||||
Expected string |
|
||||||
ExpectedM string |
|
||||||
Actual string |
|
||||||
} |
|
||||||
|
|
||||||
func (err ErrInvalidChecksum) Error() string { |
|
||||||
return fmt.Sprintf( |
|
||||||
"invalid checksum (expected (bech32=%v, "+ |
|
||||||
"bech32m=%v), got %v)", err.Expected, err.ExpectedM, err.Actual, |
|
||||||
) |
|
||||||
} |
|
||||||
|
|
||||||
// ErrInvalidDataByte is returned when a byte outside the range required for
|
|
||||||
// conversion into a string was found.
|
|
||||||
type ErrInvalidDataByte byte |
|
||||||
|
|
||||||
func (err ErrInvalidDataByte) Error() string { |
|
||||||
return fmt.Sprintf("invalid data byte: %v", byte(err)) |
|
||||||
} |
|
||||||
@ -1,43 +0,0 @@ |
|||||||
// Copyright (c) 2017 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package bech32 |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/hex" |
|
||||||
"fmt" |
|
||||||
) |
|
||||||
|
|
||||||
// This example demonstrates how to decode a bech32 encoded string.
|
|
||||||
func ExampleDecode() { |
|
||||||
encoded := "bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx" |
|
||||||
hrp, decoded, err := Decode([]byte(encoded)) |
|
||||||
if err != nil { |
|
||||||
fmt.Println("Error:", err) |
|
||||||
} |
|
||||||
// Show the decoded data.
|
|
||||||
fmt.Printf("Decoded human-readable part: %s\n", hrp) |
|
||||||
fmt.Println("Decoded Data:", hex.EncodeToString(decoded)) |
|
||||||
// Output:
|
|
||||||
// Decoded human-readable part: bc
|
|
||||||
// Decoded Data: 010e140f070d1a001912060b0d081504140311021d030c1d03040f1814060e1e160e140f070d1a001912060b0d081504140311021d030c1d03040f1814060e1e16
|
|
||||||
} |
|
||||||
|
|
||||||
// This example demonstrates how to encode data into a bech32 string.
|
|
||||||
func ExampleEncode() { |
|
||||||
data := []byte("Test data") |
|
||||||
// Convert test data to base32:
|
|
||||||
conv, err := ConvertBits(data, 8, 5, true) |
|
||||||
if err != nil { |
|
||||||
fmt.Println("Error:", err) |
|
||||||
} |
|
||||||
encoded, err := Encode([]byte("customHrp!11111q"), conv) |
|
||||||
if err != nil { |
|
||||||
fmt.Println("Error:", err) |
|
||||||
} |
|
||||||
// Show the encoded data.
|
|
||||||
fmt.Printf("Encoded Data: %s", encoded) |
|
||||||
// Output:
|
|
||||||
// Encoded Data: customhrp!11111q123jhxapqv3shgcgkxpuhe
|
|
||||||
} |
|
||||||
@ -1,40 +0,0 @@ |
|||||||
package bech32 |
|
||||||
|
|
||||||
// ChecksumConst is a type that represents the currently defined bech32
|
|
||||||
// checksum constants.
|
|
||||||
type ChecksumConst int |
|
||||||
|
|
||||||
const ( |
|
||||||
// Version0Const is the original constant used in the checksum
|
|
||||||
// verification for bech32.
|
|
||||||
Version0Const ChecksumConst = 1 |
|
||||||
// VersionMConst is the new constant used for bech32m checksum
|
|
||||||
// verification.
|
|
||||||
VersionMConst ChecksumConst = 0x2bc830a3 |
|
||||||
) |
|
||||||
|
|
||||||
// Version defines the current set of bech32 versions.
|
|
||||||
type Version uint8 |
|
||||||
|
|
||||||
const ( |
|
||||||
// Version0 defines the original bech version.
|
|
||||||
Version0 Version = iota |
|
||||||
// VersionM is the new bech32 version defined in BIP-350, also known as
|
|
||||||
// bech32m.
|
|
||||||
VersionM |
|
||||||
// VersionUnknown denotes an unknown bech version.
|
|
||||||
VersionUnknown |
|
||||||
) |
|
||||||
|
|
||||||
// VersionToConsts maps bech32 versions to the checksum constant to be used
|
|
||||||
// when encoding, and asserting a particular version when decoding.
|
|
||||||
var VersionToConsts = map[Version]ChecksumConst{ |
|
||||||
Version0: Version0Const, |
|
||||||
VersionM: VersionMConst, |
|
||||||
} |
|
||||||
|
|
||||||
// ConstsToVersion maps a bech32 constant to the version it's associated with.
|
|
||||||
var ConstsToVersion = map[ChecksumConst]Version{ |
|
||||||
Version0Const: Version0, |
|
||||||
VersionMConst: VersionM, |
|
||||||
} |
|
||||||
@ -1,188 +0,0 @@ |
|||||||
// Copyright 2013-2016 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcec |
|
||||||
|
|
||||||
import ( |
|
||||||
"math/big" |
|
||||||
"testing" |
|
||||||
|
|
||||||
"next.orly.dev/pkg/crypto/ec/secp256k1" |
|
||||||
"next.orly.dev/pkg/encoders/hex" |
|
||||||
) |
|
||||||
|
|
||||||
// setHex decodes the passed big-endian hex string into the internal field value
|
|
||||||
// representation. Only the first 32-bytes are used.
|
|
||||||
//
|
|
||||||
// This is NOT constant time.
|
|
||||||
//
|
|
||||||
// The field value is returned to support chaining. This enables syntax like:
|
|
||||||
// f := new(FieldVal).SetHex("0abc").Add(1) so that f = 0x0abc + 1
|
|
||||||
func setHex(hexString string) *FieldVal { |
|
||||||
if len(hexString)%2 != 0 { |
|
||||||
hexString = "0" + hexString |
|
||||||
} |
|
||||||
bytes, _ := hex.Dec(hexString) |
|
||||||
var f FieldVal |
|
||||||
f.SetByteSlice(bytes) |
|
||||||
return &f |
|
||||||
} |
|
||||||
|
|
||||||
// hexToFieldVal converts the passed hex string into a FieldVal and will panic
|
|
||||||
// if there is an error. This is only provided for the hard-coded constants so
|
|
||||||
// errors in the source code can be detected. It will only (and must only) be
|
|
||||||
// called with hard-coded values.
|
|
||||||
func hexToFieldVal(s string) *FieldVal { |
|
||||||
b, err := hex.Dec(s) |
|
||||||
if err != nil { |
|
||||||
panic("invalid hex in source file: " + s) |
|
||||||
} |
|
||||||
var f FieldVal |
|
||||||
if overflow := f.SetByteSlice(b); overflow { |
|
||||||
panic("hex in source file overflows mod P: " + s) |
|
||||||
} |
|
||||||
return &f |
|
||||||
} |
|
||||||
|
|
||||||
// fromHex converts the passed hex string into a big integer pointer and will
|
|
||||||
// panic is there is an error. This is only provided for the hard-coded
|
|
||||||
// constants so errors in the source code can bet detected. It will only (and
|
|
||||||
// must only) be called for initialization purposes.
|
|
||||||
func fromHex(s string) *big.Int { |
|
||||||
if s == "" { |
|
||||||
return big.NewInt(0) |
|
||||||
} |
|
||||||
r, ok := new(big.Int).SetString(s, 16) |
|
||||||
if !ok { |
|
||||||
panic("invalid hex in source file: " + s) |
|
||||||
} |
|
||||||
return r |
|
||||||
} |
|
||||||
|
|
||||||
// jacobianPointFromHex decodes the passed big-endian hex strings into a
|
|
||||||
// Jacobian point with its internal fields set to the resulting values. Only
|
|
||||||
// the first 32-bytes are used.
|
|
||||||
func jacobianPointFromHex(x, y, z string) JacobianPoint { |
|
||||||
var p JacobianPoint |
|
||||||
p.X = *setHex(x) |
|
||||||
p.Y = *setHex(y) |
|
||||||
p.Z = *setHex(z) |
|
||||||
return p |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkAddNonConst benchmarks the secp256k1 curve AddNonConst function with
|
|
||||||
// Z values of 1 so that the associated optimizations are used.
|
|
||||||
func BenchmarkAddJacobian(b *testing.B) { |
|
||||||
p1 := jacobianPointFromHex( |
|
||||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", |
|
||||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", |
|
||||||
"1", |
|
||||||
) |
|
||||||
p2 := jacobianPointFromHex( |
|
||||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", |
|
||||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", |
|
||||||
"1", |
|
||||||
) |
|
||||||
b.ReportAllocs() |
|
||||||
b.ResetTimer() |
|
||||||
var result JacobianPoint |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
secp256k1.AddNonConst(&p1, &p2, &result) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkAddNonConstNotZOne benchmarks the secp256k1 curve AddNonConst
|
|
||||||
// function with Z values other than one so the optimizations associated with
|
|
||||||
// Z=1 aren't used.
|
|
||||||
func BenchmarkAddJacobianNotZOne(b *testing.B) { |
|
||||||
x1 := setHex("d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718") |
|
||||||
y1 := setHex("5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190") |
|
||||||
z1 := setHex("2") |
|
||||||
x2 := setHex("91abba6a34b7481d922a4bd6a04899d5a686f6cf6da4e66a0cb427fb25c04bd4") |
|
||||||
y2 := setHex("03fede65e30b4e7576a2abefc963ddbf9fdccbf791b77c29beadefe49951f7d1") |
|
||||||
z2 := setHex("3") |
|
||||||
p1 := MakeJacobianPoint(x1, y1, z1) |
|
||||||
p2 := MakeJacobianPoint(x2, y2, z2) |
|
||||||
b.ReportAllocs() |
|
||||||
b.ResetTimer() |
|
||||||
var result JacobianPoint |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
AddNonConst(&p1, &p2, &result) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkScalarBaseMult benchmarks the secp256k1 curve ScalarBaseMult
|
|
||||||
// function.
|
|
||||||
func BenchmarkScalarBaseMult(b *testing.B) { |
|
||||||
k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") |
|
||||||
curve := S256() |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
curve.ScalarBaseMult(k.Bytes()) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkScalarBaseMultLarge benchmarks the secp256k1 curve ScalarBaseMult
|
|
||||||
// function with abnormally large k values.
|
|
||||||
func BenchmarkScalarBaseMultLarge(b *testing.B) { |
|
||||||
k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c005751111111011111110") |
|
||||||
curve := S256() |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
curve.ScalarBaseMult(k.Bytes()) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkScalarMult benchmarks the secp256k1 curve ScalarMult function.
|
|
||||||
func BenchmarkScalarMult(b *testing.B) { |
|
||||||
x := fromHex("34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6") |
|
||||||
y := fromHex("0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232") |
|
||||||
k := fromHex("d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575") |
|
||||||
curve := S256() |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
curve.ScalarMult(x, y, k.Bytes()) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// hexToModNScalar converts the passed hex string into a ModNScalar and will
|
|
||||||
// panic if there is an error. This is only provided for the hard-coded
|
|
||||||
// constants so errors in the source code can be detected. It will only (and
|
|
||||||
// must only) be called with hard-coded values.
|
|
||||||
func hexToModNScalar(s string) *ModNScalar { |
|
||||||
b, err := hex.Dec(s) |
|
||||||
if err != nil { |
|
||||||
panic("invalid hex in source file: " + s) |
|
||||||
} |
|
||||||
var scalar ModNScalar |
|
||||||
if overflow := scalar.SetByteSlice(b); overflow { |
|
||||||
panic("hex in source file overflows mod N scalar: " + s) |
|
||||||
} |
|
||||||
return &scalar |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkFieldNormalize benchmarks how long it takes the internal field
|
|
||||||
// to perform normalization (which includes modular reduction).
|
|
||||||
func BenchmarkFieldNormalize(b *testing.B) { |
|
||||||
// The normalize function is constant time so default value is fine.
|
|
||||||
var f FieldVal |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
f.Normalize() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkParseCompressedPubKey benchmarks how long it takes to decompress and
|
|
||||||
// validate a compressed public key from a byte array.
|
|
||||||
func BenchmarkParseCompressedPubKey(b *testing.B) { |
|
||||||
rawPk, _ := hex.Dec("0234f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6") |
|
||||||
|
|
||||||
var ( |
|
||||||
pk *PublicKey |
|
||||||
err error |
|
||||||
) |
|
||||||
b.ReportAllocs() |
|
||||||
b.ResetTimer() |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
pk, err = ParsePubKey(rawPk) |
|
||||||
} |
|
||||||
_ = pk |
|
||||||
_ = err |
|
||||||
} |
|
||||||
@ -1,53 +0,0 @@ |
|||||||
// Copyright 2010 The Go Authors. All rights reserved.
|
|
||||||
// Copyright 2011 ThePiachu. All rights reserved.
|
|
||||||
// Copyright 2013-2014 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcec |
|
||||||
|
|
||||||
// References:
|
|
||||||
// [SECG]: Recommended Elliptic Curve Domain Parameters
|
|
||||||
// http://www.secg.org/sec2-v2.pdf
|
|
||||||
//
|
|
||||||
// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone)
|
|
||||||
|
|
||||||
// This package operates, internally, on Jacobian coordinates. For a given
|
|
||||||
// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1)
|
|
||||||
// where x = x1/z1² and y = y1/z1³. The greatest speedups come when the whole
|
|
||||||
// calculation can be performed within the transform (as in ScalarMult and
|
|
||||||
// ScalarBaseMult). But even for Add and Double, it's faster to apply and
|
|
||||||
// reverse the transform than to operate in affine coordinates.
|
|
||||||
|
|
||||||
import ( |
|
||||||
"next.orly.dev/pkg/crypto/ec/secp256k1" |
|
||||||
) |
|
||||||
|
|
||||||
// KoblitzCurve provides an implementation for secp256k1 that fits the ECC
|
|
||||||
// Curve interface from crypto/elliptic.
|
|
||||||
type KoblitzCurve = secp256k1.KoblitzCurve |
|
||||||
|
|
||||||
// S256 returns a Curve which implements secp256k1.
|
|
||||||
func S256() *KoblitzCurve { |
|
||||||
return secp256k1.S256() |
|
||||||
} |
|
||||||
|
|
||||||
// CurveParams contains the parameters for the secp256k1 curve.
|
|
||||||
type CurveParams = secp256k1.CurveParams |
|
||||||
|
|
||||||
// Params returns the secp256k1 curve parameters for convenience.
|
|
||||||
func Params() *CurveParams { |
|
||||||
return secp256k1.Params() |
|
||||||
} |
|
||||||
|
|
||||||
// Generator returns the public key at the Generator Point.
|
|
||||||
func Generator() *PublicKey { |
|
||||||
var ( |
|
||||||
result JacobianPoint |
|
||||||
k secp256k1.ModNScalar |
|
||||||
) |
|
||||||
k.SetInt(1) |
|
||||||
ScalarBaseMultNonConst(&k, &result) |
|
||||||
result.ToAffine() |
|
||||||
return NewPublicKey(&result.X, &result.Y) |
|
||||||
} |
|
||||||
@ -1,918 +0,0 @@ |
|||||||
// Copyright 2011 The Go Authors. All rights reserved.
|
|
||||||
// Copyright 2011 ThePiachu. All rights reserved.
|
|
||||||
// Copyright 2013-2016 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcec |
|
||||||
|
|
||||||
import ( |
|
||||||
"crypto/rand" |
|
||||||
"fmt" |
|
||||||
"math/big" |
|
||||||
"testing" |
|
||||||
) |
|
||||||
|
|
||||||
// isJacobianOnS256Curve returns boolean if the point (x,y,z) is on the
|
|
||||||
// secp256k1 curve.
|
|
||||||
func isJacobianOnS256Curve(point *JacobianPoint) bool { |
|
||||||
// Elliptic curve equation for secp256k1 is: y^2 = x^3 + 7
|
|
||||||
// In Jacobian coordinates, Y = y/z^3 and X = x/z^2
|
|
||||||
// Thus:
|
|
||||||
// (y/z^3)^2 = (x/z^2)^3 + 7
|
|
||||||
// y^2/z^6 = x^3/z^6 + 7
|
|
||||||
// y^2 = x^3 + 7*z^6
|
|
||||||
var y2, z2, x3, result FieldVal |
|
||||||
y2.SquareVal(&point.Y).Normalize() |
|
||||||
z2.SquareVal(&point.Z) |
|
||||||
x3.SquareVal(&point.X).Mul(&point.X) |
|
||||||
result.SquareVal(&z2).Mul(&z2).MulInt(7).Add(&x3).Normalize() |
|
||||||
return y2.Equals(&result) |
|
||||||
} |
|
||||||
|
|
||||||
// TestAddJacobian tests addition of points projected in Jacobian coordinates.
|
|
||||||
func TestAddJacobian(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
x1, y1, z1 string // Coordinates (in hex) of first point to add
|
|
||||||
x2, y2, z2 string // Coordinates (in hex) of second point to add
|
|
||||||
x3, y3, z3 string // Coordinates (in hex) of expected point
|
|
||||||
}{ |
|
||||||
// Addition with a point at infinity (left hand side).
|
|
||||||
// ∞ + P = P
|
|
||||||
{ |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", |
|
||||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", |
|
||||||
"1", |
|
||||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", |
|
||||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", |
|
||||||
"1", |
|
||||||
}, |
|
||||||
// Addition with a point at infinity (right hand side).
|
|
||||||
// P + ∞ = P
|
|
||||||
{ |
|
||||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", |
|
||||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", |
|
||||||
"1", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", |
|
||||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", |
|
||||||
"1", |
|
||||||
}, |
|
||||||
// Addition with z1=z2=1 different x values.
|
|
||||||
{ |
|
||||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", |
|
||||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", |
|
||||||
"1", |
|
||||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", |
|
||||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", |
|
||||||
"1", |
|
||||||
"0cfbc7da1e569b334460788faae0286e68b3af7379d5504efc25e4dba16e46a6", |
|
||||||
"e205f79361bbe0346b037b4010985dbf4f9e1e955e7d0d14aca876bfa79aad87", |
|
||||||
"44a5646b446e3877a648d6d381370d9ef55a83b666ebce9df1b1d7d65b817b2f", |
|
||||||
}, |
|
||||||
// Addition with z1=z2=1 same x opposite y.
|
|
||||||
// P(x, y, z) + P(x, -y, z) = infinity
|
|
||||||
{ |
|
||||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", |
|
||||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", |
|
||||||
"1", |
|
||||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", |
|
||||||
"f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd", |
|
||||||
"1", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
}, |
|
||||||
// Addition with z1=z2=1 same point.
|
|
||||||
// P(x, y, z) + P(x, y, z) = 2P
|
|
||||||
{ |
|
||||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", |
|
||||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", |
|
||||||
"1", |
|
||||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", |
|
||||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", |
|
||||||
"1", |
|
||||||
"ec9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee64f87c50c27", |
|
||||||
"b082b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd0755c8f2a", |
|
||||||
"16e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c1e594464", |
|
||||||
}, |
|
||||||
|
|
||||||
// Addition with z1=z2 (!=1) different x values.
|
|
||||||
{ |
|
||||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", |
|
||||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", |
|
||||||
"2", |
|
||||||
"5d2fe112c21891d440f65a98473cb626111f8a234d2cd82f22172e369f002147", |
|
||||||
"98e3386a0a622a35c4561ffb32308d8e1c6758e10ebb1b4ebd3d04b4eb0ecbe8", |
|
||||||
"2", |
|
||||||
"cfbc7da1e569b334460788faae0286e68b3af7379d5504efc25e4dba16e46a60", |
|
||||||
"817de4d86ef80d1ac0ded00426176fd3e787a5579f43452b2a1db021e6ac3778", |
|
||||||
"129591ad11b8e1de99235b4e04dc367bd56a0ed99baf3a77c6c75f5a6e05f08d", |
|
||||||
}, |
|
||||||
// Addition with z1=z2 (!=1) same x opposite y.
|
|
||||||
// P(x, y, z) + P(x, -y, z) = infinity
|
|
||||||
{ |
|
||||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", |
|
||||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", |
|
||||||
"2", |
|
||||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", |
|
||||||
"a470ab21467813b6e0496d2c2b70c11446bab4fcbc9a52b7f225f30e869aea9f", |
|
||||||
"2", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
}, |
|
||||||
// Addition with z1=z2 (!=1) same point.
|
|
||||||
// P(x, y, z) + P(x, y, z) = 2P
|
|
||||||
{ |
|
||||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", |
|
||||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", |
|
||||||
"2", |
|
||||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", |
|
||||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", |
|
||||||
"2", |
|
||||||
"9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", |
|
||||||
"2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", |
|
||||||
"6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", |
|
||||||
}, |
|
||||||
|
|
||||||
// Addition with z1!=z2 and z2=1 different x values.
|
|
||||||
{ |
|
||||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", |
|
||||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", |
|
||||||
"2", |
|
||||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", |
|
||||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", |
|
||||||
"1", |
|
||||||
"3ef1f68795a6ccd1181e23eab80a1b9a2cebdcde755413bf097936eb5b91b4f3", |
|
||||||
"0bef26c377c068d606f6802130bb7e9f3c3d2abcfa1a295950ed81133561cb04", |
|
||||||
"252b235a2371c3bd3246b69c09b86cf7aad41db3375e74ef8d8ebeb4dc0be11a", |
|
||||||
}, |
|
||||||
// Addition with z1!=z2 and z2=1 same x opposite y.
|
|
||||||
// P(x, y, z) + P(x, -y, z) = infinity
|
|
||||||
{ |
|
||||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", |
|
||||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", |
|
||||||
"2", |
|
||||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", |
|
||||||
"f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd", |
|
||||||
"1", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
}, |
|
||||||
// Addition with z1!=z2 and z2=1 same point.
|
|
||||||
// P(x, y, z) + P(x, y, z) = 2P
|
|
||||||
{ |
|
||||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", |
|
||||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", |
|
||||||
"2", |
|
||||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", |
|
||||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", |
|
||||||
"1", |
|
||||||
"9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", |
|
||||||
"2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", |
|
||||||
"6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", |
|
||||||
}, |
|
||||||
|
|
||||||
// Addition with z1!=z2 and z2!=1 different x values.
|
|
||||||
// P(x, y, z) + P(x, y, z) = 2P
|
|
||||||
{ |
|
||||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", |
|
||||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", |
|
||||||
"2", |
|
||||||
"91abba6a34b7481d922a4bd6a04899d5a686f6cf6da4e66a0cb427fb25c04bd4", |
|
||||||
"03fede65e30b4e7576a2abefc963ddbf9fdccbf791b77c29beadefe49951f7d1", |
|
||||||
"3", |
|
||||||
"3f07081927fd3f6dadd4476614c89a09eba7f57c1c6c3b01fa2d64eac1eef31e", |
|
||||||
"949166e04ebc7fd95a9d77e5dfd88d1492ecffd189792e3944eb2b765e09e031", |
|
||||||
"eb8cba81bcffa4f44d75427506737e1f045f21e6d6f65543ee0e1d163540c931", |
|
||||||
}, // Addition with z1!=z2 and z2!=1 same x opposite y.
|
|
||||||
// P(x, y, z) + P(x, -y, z) = infinity
|
|
||||||
{ |
|
||||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", |
|
||||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", |
|
||||||
"2", |
|
||||||
"dcc3768780c74a0325e2851edad0dc8a566fa61a9e7fc4a34d13dcb509f99bc7", |
|
||||||
"cafc41904dd5428934f7d075129c8ba46eb622d4fc88d72cd1401452664add18", |
|
||||||
"3", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
}, |
|
||||||
// Addition with z1!=z2 and z2!=1 same point.
|
|
||||||
// P(x, y, z) + P(x, y, z) = 2P
|
|
||||||
{ |
|
||||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", |
|
||||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", |
|
||||||
"2", |
|
||||||
"dcc3768780c74a0325e2851edad0dc8a566fa61a9e7fc4a34d13dcb509f99bc7", |
|
||||||
"3503be6fb22abd76cb082f8aed63745b9149dd2b037728d32ebfebac99b51f17", |
|
||||||
"3", |
|
||||||
"9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", |
|
||||||
"2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", |
|
||||||
"6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests)) |
|
||||||
for i, test := range tests { |
|
||||||
// Convert hex to Jacobian points.
|
|
||||||
p1 := jacobianPointFromHex(test.x1, test.y1, test.z1) |
|
||||||
p2 := jacobianPointFromHex(test.x2, test.y2, test.z2) |
|
||||||
want := jacobianPointFromHex(test.x3, test.y3, test.z3) |
|
||||||
// Ensure the test data is using points that are actually on
|
|
||||||
// the curve (or the point at infinity).
|
|
||||||
if !p1.Z.IsZero() && !isJacobianOnS256Curve(&p1) { |
|
||||||
t.Errorf( |
|
||||||
"#%d first point is not on the curve -- "+ |
|
||||||
"invalid test data", i, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
if !p2.Z.IsZero() && !isJacobianOnS256Curve(&p2) { |
|
||||||
t.Errorf( |
|
||||||
"#%d second point is not on the curve -- "+ |
|
||||||
"invalid test data", i, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
if !want.Z.IsZero() && !isJacobianOnS256Curve(&want) { |
|
||||||
t.Errorf( |
|
||||||
"#%d expected point is not on the curve -- "+ |
|
||||||
"invalid test data", i, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
// Add the two points.
|
|
||||||
var r JacobianPoint |
|
||||||
AddNonConst(&p1, &p2, &r) |
|
||||||
|
|
||||||
// Ensure result matches expected.
|
|
||||||
if !r.X.Equals(&want.X) || !r.Y.Equals(&want.Y) || !r.Z.Equals(&want.Z) { |
|
||||||
t.Errorf( |
|
||||||
"#%d wrong result\ngot: (%v, %v, %v)\n"+ |
|
||||||
"want: (%v, %v, %v)", i, r.X, r.Y, r.Z, want.X, want.Y, |
|
||||||
want.Z, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// TestAddAffine tests addition of points in affine coordinates.
|
|
||||||
func TestAddAffine(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
x1, y1 string // Coordinates (in hex) of first point to add
|
|
||||||
x2, y2 string // Coordinates (in hex) of second point to add
|
|
||||||
x3, y3 string // Coordinates (in hex) of expected point
|
|
||||||
}{ |
|
||||||
// Addition with a point at infinity (left hand side).
|
|
||||||
// ∞ + P = P
|
|
||||||
{ |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", |
|
||||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", |
|
||||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", |
|
||||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", |
|
||||||
}, |
|
||||||
// Addition with a point at infinity (right hand side).
|
|
||||||
// P + ∞ = P
|
|
||||||
{ |
|
||||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", |
|
||||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", |
|
||||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", |
|
||||||
}, |
|
||||||
|
|
||||||
// Addition with different x values.
|
|
||||||
{ |
|
||||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", |
|
||||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", |
|
||||||
"d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575", |
|
||||||
"131c670d414c4546b88ac3ff664611b1c38ceb1c21d76369d7a7a0969d61d97d", |
|
||||||
"fd5b88c21d3143518d522cd2796f3d726793c88b3e05636bc829448e053fed69", |
|
||||||
"21cf4f6a5be5ff6380234c50424a970b1f7e718f5eb58f68198c108d642a137f", |
|
||||||
}, |
|
||||||
// Addition with same x opposite y.
|
|
||||||
// P(x, y) + P(x, -y) = infinity
|
|
||||||
{ |
|
||||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", |
|
||||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", |
|
||||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", |
|
||||||
"f48e156428cf0276dc092da5856e182288d7569f97934a56fe44be60f0d359fd", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
}, |
|
||||||
// Addition with same point.
|
|
||||||
// P(x, y) + P(x, y) = 2P
|
|
||||||
{ |
|
||||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", |
|
||||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", |
|
||||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", |
|
||||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", |
|
||||||
"59477d88ae64a104dbb8d31ec4ce2d91b2fe50fa628fb6a064e22582196b365b", |
|
||||||
"938dc8c0f13d1e75c987cb1a220501bd614b0d3dd9eb5c639847e1240216e3b6", |
|
||||||
}, |
|
||||||
} |
|
||||||
t.Logf("Running %d tests", len(tests)) |
|
||||||
for i, test := range tests { |
|
||||||
// Convert hex to field values.
|
|
||||||
x1, y1 := fromHex(test.x1), fromHex(test.y1) |
|
||||||
x2, y2 := fromHex(test.x2), fromHex(test.y2) |
|
||||||
x3, y3 := fromHex(test.x3), fromHex(test.y3) |
|
||||||
// Ensure the test data is using points that are actually on
|
|
||||||
// the curve (or the point at infinity).
|
|
||||||
if !(x1.Sign() == 0 && y1.Sign() == 0) && !S256().IsOnCurve(x1, y1) { |
|
||||||
t.Errorf( |
|
||||||
"#%d first point is not on the curve -- "+ |
|
||||||
"invalid test data", i, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
if !(x2.Sign() == 0 && y2.Sign() == 0) && !S256().IsOnCurve(x2, y2) { |
|
||||||
t.Errorf( |
|
||||||
"#%d second point is not on the curve -- "+ |
|
||||||
"invalid test data", i, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
if !(x3.Sign() == 0 && y3.Sign() == 0) && !S256().IsOnCurve(x3, y3) { |
|
||||||
t.Errorf( |
|
||||||
"#%d expected point is not on the curve -- "+ |
|
||||||
"invalid test data", i, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
// Add the two points.
|
|
||||||
rx, ry := S256().Add(x1, y1, x2, y2) |
|
||||||
|
|
||||||
// Ensure result matches expected.
|
|
||||||
if rx.Cmp(x3) != 00 || ry.Cmp(y3) != 0 { |
|
||||||
t.Errorf( |
|
||||||
"#%d wrong result\ngot: (%x, %x)\n"+ |
|
||||||
"want: (%x, %x)", i, rx, ry, x3, y3, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// isStrictlyEqual returns whether or not the two Jacobian points are strictly
|
|
||||||
// equal for use in the tests. Recall that several Jacobian points can be
|
|
||||||
// equal in affine coordinates, while not having the same coordinates in
|
|
||||||
// projective space, so the two points not being equal doesn't necessarily mean
|
|
||||||
// they aren't actually the same affine point.
|
|
||||||
func isStrictlyEqual(p, other *JacobianPoint) bool { |
|
||||||
return p.X.Equals(&other.X) && p.Y.Equals(&other.Y) && p.Z.Equals(&other.Z) |
|
||||||
} |
|
||||||
|
|
||||||
// TestDoubleJacobian tests doubling of points projected in Jacobian
|
|
||||||
// coordinates.
|
|
||||||
func TestDoubleJacobian(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
x1, y1, z1 string // Coordinates (in hex) of point to double
|
|
||||||
x3, y3, z3 string // Coordinates (in hex) of expected point
|
|
||||||
}{ |
|
||||||
// Doubling a point at infinity is still infinity.
|
|
||||||
{ |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
}, |
|
||||||
// Doubling with z1=1.
|
|
||||||
{ |
|
||||||
"34f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6", |
|
||||||
"0b71ea9bd730fd8923f6d25a7a91e7dd7728a960686cb5a901bb419e0f2ca232", |
|
||||||
"1", |
|
||||||
"ec9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee64f87c50c27", |
|
||||||
"b082b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd0755c8f2a", |
|
||||||
"16e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c1e594464", |
|
||||||
}, |
|
||||||
// Doubling with z1!=1.
|
|
||||||
{ |
|
||||||
"d3e5183c393c20e4f464acf144ce9ae8266a82b67f553af33eb37e88e7fd2718", |
|
||||||
"5b8f54deb987ec491fb692d3d48f3eebb9454b034365ad480dda0cf079651190", |
|
||||||
"2", |
|
||||||
"9f153b13ee7bd915882859635ea9730bf0dc7611b2c7b0e37ee65073c50fabac", |
|
||||||
"2b53702c466dcf6e984a35671756c506c67c2fcb8adb408c44dd125dc91cb988", |
|
||||||
"6e3d537ae61fb1247eda4b4f523cfbaee5152c0d0d96b520376833c2e5944a11", |
|
||||||
}, |
|
||||||
// From btcd issue #709.
|
|
||||||
{ |
|
||||||
"201e3f75715136d2f93c4f4598f91826f94ca01f4233a5bd35de9708859ca50d", |
|
||||||
"bdf18566445e7562c6ada68aef02d498d7301503de5b18c6aef6e2b1722412e1", |
|
||||||
"0000000000000000000000000000000000000000000000000000000000000001", |
|
||||||
"4a5e0559863ebb4e9ed85f5c4fa76003d05d9a7626616e614a1f738621e3c220", |
|
||||||
"00000000000000000000000000000000000000000000000000000001b1388778", |
|
||||||
"7be30acc88bceac58d5b4d15de05a931ae602a07bcb6318d5dedc563e4482993", |
|
||||||
}, |
|
||||||
} |
|
||||||
t.Logf("Running %d tests", len(tests)) |
|
||||||
for i, test := range tests { |
|
||||||
// Convert hex to field values.
|
|
||||||
p1 := jacobianPointFromHex(test.x1, test.y1, test.z1) |
|
||||||
want := jacobianPointFromHex(test.x3, test.y3, test.z3) |
|
||||||
// Ensure the test data is using points that are actually on
|
|
||||||
// the curve (or the point at infinity).
|
|
||||||
if !p1.Z.IsZero() && !isJacobianOnS256Curve(&p1) { |
|
||||||
t.Errorf( |
|
||||||
"#%d first point is not on the curve -- "+ |
|
||||||
"invalid test data", i, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
if !want.Z.IsZero() && !isJacobianOnS256Curve(&want) { |
|
||||||
t.Errorf( |
|
||||||
"#%d expected point is not on the curve -- "+ |
|
||||||
"invalid test data", i, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
// Double the point.
|
|
||||||
var result JacobianPoint |
|
||||||
DoubleNonConst(&p1, &result) |
|
||||||
// Ensure result matches expected.
|
|
||||||
if !isStrictlyEqual(&result, &want) { |
|
||||||
t.Errorf( |
|
||||||
"#%d wrong result\ngot: (%v, %v, %v)\n"+ |
|
||||||
"want: (%v, %v, %v)", i, result.X, result.Y, result.Z, |
|
||||||
want.X, want.Y, want.Z, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// TestDoubleAffine tests doubling of points in affine coordinates.
|
|
||||||
func TestDoubleAffine(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
x1, y1 string // Coordinates (in hex) of point to double
|
|
||||||
x3, y3 string // Coordinates (in hex) of expected point
|
|
||||||
}{ |
|
||||||
// Doubling a point at infinity is still infinity.
|
|
||||||
// 2*∞ = ∞ (point at infinity)
|
|
||||||
{ |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
"0", |
|
||||||
}, |
|
||||||
// Random points.
|
|
||||||
{ |
|
||||||
"e41387ffd8baaeeb43c2faa44e141b19790e8ac1f7ff43d480dc132230536f86", |
|
||||||
"1b88191d430f559896149c86cbcb703193105e3cf3213c0c3556399836a2b899", |
|
||||||
"88da47a089d333371bd798c548ef7caae76e737c1980b452d367b3cfe3082c19", |
|
||||||
"3b6f659b09a362821dfcfefdbfbc2e59b935ba081b6c249eb147b3c2100b1bc1", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"b3589b5d984f03ef7c80aeae444f919374799edf18d375cab10489a3009cff0c", |
|
||||||
"c26cf343875b3630e15bccc61202815b5d8f1fd11308934a584a5babe69db36a", |
|
||||||
"e193860172998751e527bb12563855602a227fc1f612523394da53b746bb2fb1", |
|
||||||
"2bfcf13d2f5ab8bb5c611fab5ebbed3dc2f057062b39a335224c22f090c04789", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"2b31a40fbebe3440d43ac28dba23eee71c62762c3fe3dbd88b4ab82dc6a82340", |
|
||||||
"9ba7deb02f5c010e217607fd49d58db78ec273371ea828b49891ce2fd74959a1", |
|
||||||
"2c8d5ef0d343b1a1a48aa336078eadda8481cb048d9305dc4fdf7ee5f65973a2", |
|
||||||
"bb4914ac729e26d3cd8f8dc8f702f3f4bb7e0e9c5ae43335f6e94c2de6c3dc95", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"61c64b760b51981fab54716d5078ab7dffc93730b1d1823477e27c51f6904c7a", |
|
||||||
"ef6eb16ea1a36af69d7f66524c75a3a5e84c13be8fbc2e811e0563c5405e49bd", |
|
||||||
"5f0dcdd2595f5ad83318a0f9da481039e36f135005420393e72dfca985b482f4", |
|
||||||
"a01c849b0837065c1cb481b0932c441f49d1cab1b4b9f355c35173d93f110ae0", |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
t.Logf("Running %d tests", len(tests)) |
|
||||||
for i, test := range tests { |
|
||||||
// Convert hex to field values.
|
|
||||||
x1, y1 := fromHex(test.x1), fromHex(test.y1) |
|
||||||
x3, y3 := fromHex(test.x3), fromHex(test.y3) |
|
||||||
// Ensure the test data is using points that are actually on
|
|
||||||
// the curve (or the point at infinity).
|
|
||||||
if !(x1.Sign() == 0 && y1.Sign() == 0) && !S256().IsOnCurve(x1, y1) { |
|
||||||
t.Errorf( |
|
||||||
"#%d first point is not on the curve -- "+ |
|
||||||
"invalid test data", i, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
if !(x3.Sign() == 0 && y3.Sign() == 0) && !S256().IsOnCurve(x3, y3) { |
|
||||||
t.Errorf( |
|
||||||
"#%d expected point is not on the curve -- "+ |
|
||||||
"invalid test data", i, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
// Double the point.
|
|
||||||
rx, ry := S256().Double(x1, y1) |
|
||||||
|
|
||||||
// Ensure result matches expected.
|
|
||||||
if rx.Cmp(x3) != 00 || ry.Cmp(y3) != 0 { |
|
||||||
t.Errorf( |
|
||||||
"#%d wrong result\ngot: (%x, %x)\n"+ |
|
||||||
"want: (%x, %x)", i, rx, ry, x3, y3, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestOnCurve(t *testing.T) { |
|
||||||
s256 := S256() |
|
||||||
if !s256.IsOnCurve(s256.Params().Gx, s256.Params().Gy) { |
|
||||||
t.Errorf("FAIL S256") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
type baseMultTest struct { |
|
||||||
k string |
|
||||||
x, y string |
|
||||||
} |
|
||||||
|
|
||||||
// TODO: add more test vectors
|
|
||||||
var s256BaseMultTests = []baseMultTest{ |
|
||||||
{ |
|
||||||
"AA5E28D6A97A2479A65527F7290311A3624D4CC0FA1578598EE3C2613BF99522", |
|
||||||
"34F9460F0E4F08393D192B3C5133A6BA099AA0AD9FD54EBCCFACDFA239FF49C6", |
|
||||||
"B71EA9BD730FD8923F6D25A7A91E7DD7728A960686CB5A901BB419E0F2CA232", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"7E2B897B8CEBC6361663AD410835639826D590F393D90A9538881735256DFAE3", |
|
||||||
"D74BF844B0862475103D96A611CF2D898447E288D34B360BC885CB8CE7C00575", |
|
||||||
"131C670D414C4546B88AC3FF664611B1C38CEB1C21D76369D7A7A0969D61D97D", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"6461E6DF0FE7DFD05329F41BF771B86578143D4DD1F7866FB4CA7E97C5FA945D", |
|
||||||
"E8AECC370AEDD953483719A116711963CE201AC3EB21D3F3257BB48668C6A72F", |
|
||||||
"C25CAF2F0EBA1DDB2F0F3F47866299EF907867B7D27E95B3873BF98397B24EE1", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"376A3A2CDCD12581EFFF13EE4AD44C4044B8A0524C42422A7E1E181E4DEECCEC", |
|
||||||
"14890E61FCD4B0BD92E5B36C81372CA6FED471EF3AA60A3E415EE4FE987DABA1", |
|
||||||
"297B858D9F752AB42D3BCA67EE0EB6DCD1C2B7B0DBE23397E66ADC272263F982", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"1B22644A7BE026548810C378D0B2994EEFA6D2B9881803CB02CEFF865287D1B9", |
|
||||||
"F73C65EAD01C5126F28F442D087689BFA08E12763E0CEC1D35B01751FD735ED3", |
|
||||||
"F449A8376906482A84ED01479BD18882B919C140D638307F0C0934BA12590BDE", |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
// TODO: test different curves as well?
|
|
||||||
func TestBaseMult(t *testing.T) { |
|
||||||
s256 := S256() |
|
||||||
for i, e := range s256BaseMultTests { |
|
||||||
k, ok := new(big.Int).SetString(e.k, 16) |
|
||||||
if !ok { |
|
||||||
t.Errorf("%d: bad value for k: %s", i, e.k) |
|
||||||
} |
|
||||||
x, y := s256.ScalarBaseMult(k.Bytes()) |
|
||||||
if fmt.Sprintf("%X", x) != e.x || fmt.Sprintf("%X", y) != e.y { |
|
||||||
t.Errorf( |
|
||||||
"%d: bad output for k=%s: got (%X, %X), want (%s, %s)", i, |
|
||||||
e.k, x, y, e.x, e.y, |
|
||||||
) |
|
||||||
} |
|
||||||
if testing.Short() && i > 5 { |
|
||||||
break |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestBaseMultVerify(t *testing.T) { |
|
||||||
s256 := S256() |
|
||||||
for bytes := 1; bytes < 40; bytes++ { |
|
||||||
for i := 0; i < 30; i++ { |
|
||||||
data := make([]byte, bytes) |
|
||||||
_, err := rand.Read(data) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("failed to read random data for %d", i) |
|
||||||
continue |
|
||||||
} |
|
||||||
x, y := s256.ScalarBaseMult(data) |
|
||||||
xWant, yWant := s256.ScalarMult(s256.Gx, s256.Gy, data) |
|
||||||
if x.Cmp(xWant) != 0 || y.Cmp(yWant) != 0 { |
|
||||||
t.Errorf( |
|
||||||
"%d: bad output for %X: got (%X, %X), want (%X, %X)", |
|
||||||
i, data, x, y, xWant, yWant, |
|
||||||
) |
|
||||||
} |
|
||||||
if testing.Short() && i > 2 { |
|
||||||
break |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestScalarMult(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
x string |
|
||||||
y string |
|
||||||
k string |
|
||||||
rx string |
|
||||||
ry string |
|
||||||
}{ |
|
||||||
// base mult, essentially.
|
|
||||||
{ |
|
||||||
"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", |
|
||||||
"483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", |
|
||||||
"18e14a7b6a307f426a94f8114701e7c8e774e7f9a47e2c2035db29a206321725", |
|
||||||
"50863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352", |
|
||||||
"2cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6", |
|
||||||
}, |
|
||||||
// From btcd issue #709.
|
|
||||||
{ |
|
||||||
"000000000000000000000000000000000000000000000000000000000000002c", |
|
||||||
"420e7a99bba18a9d3952597510fd2b6728cfeafc21a4e73951091d4d8ddbe94e", |
|
||||||
"a2e8ba2e8ba2e8ba2e8ba2e8ba2e8ba219b51835b55cc30ebfe2f6599bc56f58", |
|
||||||
"a2112dcdfbcd10ae1133a358de7b82db68e0a3eb4b492cc8268d1e7118c98788", |
|
||||||
"27fc7463b7bb3c5f98ecf2c84a6272bb1681ed553d92c69f2dfe25a9f9fd3836", |
|
||||||
}, |
|
||||||
} |
|
||||||
s256 := S256() |
|
||||||
for i, test := range tests { |
|
||||||
x, _ := new(big.Int).SetString(test.x, 16) |
|
||||||
y, _ := new(big.Int).SetString(test.y, 16) |
|
||||||
k, _ := new(big.Int).SetString(test.k, 16) |
|
||||||
xWant, _ := new(big.Int).SetString(test.rx, 16) |
|
||||||
yWant, _ := new(big.Int).SetString(test.ry, 16) |
|
||||||
xGot, yGot := s256.ScalarMult(x, y, k.Bytes()) |
|
||||||
if xGot.Cmp(xWant) != 0 || yGot.Cmp(yWant) != 0 { |
|
||||||
t.Fatalf( |
|
||||||
"%d: bad output: got (%X, %X), want (%X, %X)", i, xGot, |
|
||||||
yGot, xWant, yWant, |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestScalarMultRand(t *testing.T) { |
|
||||||
// Strategy for this test:
|
|
||||||
// Get a random exponent from the generator point at first
|
|
||||||
// This creates a new point which is used in the next iteration
|
|
||||||
// Use another random exponent on the new point.
|
|
||||||
// We use BaseMult to verify by multiplying the previous exponent
|
|
||||||
// and the new random exponent together (mod no)
|
|
||||||
s256 := S256() |
|
||||||
x, y := s256.Gx, s256.Gy |
|
||||||
exponent := big.NewInt(1) |
|
||||||
for i := 0; i < 1024; i++ { |
|
||||||
data := make([]byte, 32) |
|
||||||
_, err := rand.Read(data) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("failed to read random data at %d", i) |
|
||||||
break |
|
||||||
} |
|
||||||
x, y = s256.ScalarMult(x, y, data) |
|
||||||
exponent.Mul(exponent, new(big.Int).SetBytes(data)) |
|
||||||
xWant, yWant := s256.ScalarBaseMult(exponent.Bytes()) |
|
||||||
if x.Cmp(xWant) != 0 || y.Cmp(yWant) != 0 { |
|
||||||
t.Fatalf( |
|
||||||
"%d: bad output for %X: got (%X, %X), want (%X, %X)", i, |
|
||||||
data, x, y, xWant, yWant, |
|
||||||
) |
|
||||||
break |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
var ( |
|
||||||
// Next 6 constants are from Hal Finney's bitcointalk.org post:
|
|
||||||
// https://bitcointalk.org/index.php?topic=3238.msg45565#msg45565
|
|
||||||
// May he rest in peace.
|
|
||||||
//
|
|
||||||
// They have also been independently derived from the code in the
|
|
||||||
// EndomorphismVectors function in genstatics.go.
|
|
||||||
endomorphismLambda = fromHex("5363ad4cc05c30e0a5261c028812645a122e22ea20816678df02967c1b23bd72") |
|
||||||
endomorphismBeta = hexToFieldVal("7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee") |
|
||||||
endomorphismA1 = fromHex("3086d221a7d46bcde86c90e49284eb15") |
|
||||||
endomorphismB1 = fromHex("-e4437ed6010e88286f547fa90abfe4c3") |
|
||||||
endomorphismA2 = fromHex("114ca50f7a8e2f3f657c1108d9d44cfd8") |
|
||||||
endomorphismB2 = fromHex("3086d221a7d46bcde86c90e49284eb15") |
|
||||||
) |
|
||||||
|
|
||||||
// splitK returns a balanced length-two representation of k and their signs.
|
|
||||||
// This is algorithm 3.74 from [GECC].
|
|
||||||
//
|
|
||||||
// One thing of note about this algorithm is that no matter what c1 and c2 are,
|
|
||||||
// the final equation of k = k1 + k2 * lambda (mod n) will hold. This is
|
|
||||||
// provable mathematically due to how a1/b1/a2/b2 are computed.
|
|
||||||
//
|
|
||||||
// c1 and c2 are chosen to minimize the max(k1,k2).
|
|
||||||
func splitK(k []byte) ([]byte, []byte, int, int) { |
|
||||||
// All math here is done with big.Int, which is slow.
|
|
||||||
// At some point, it might be useful to write something similar to
|
|
||||||
// FieldVal but for no instead of P as the prime field if this ends up
|
|
||||||
// being a bottleneck.
|
|
||||||
bigIntK := new(big.Int) |
|
||||||
c1, c2 := new(big.Int), new(big.Int) |
|
||||||
tmp1, tmp2 := new(big.Int), new(big.Int) |
|
||||||
k1, k2 := new(big.Int), new(big.Int) |
|
||||||
bigIntK.SetBytes(k) |
|
||||||
// c1 = round(b2 * k / n) from step 4.
|
|
||||||
// Rounding isn't really necessary and costs too much, hence skipped
|
|
||||||
c1.Mul(endomorphismB2, bigIntK) |
|
||||||
c1.Div(c1, Params().N) |
|
||||||
// c2 = round(b1 * k / n) from step 4 (sign reversed to optimize one step)
|
|
||||||
// Rounding isn't really necessary and costs too much, hence skipped
|
|
||||||
c2.Mul(endomorphismB1, bigIntK) |
|
||||||
c2.Div(c2, Params().N) |
|
||||||
// k1 = k - c1 * a1 - c2 * a2 from step 5 (note c2's sign is reversed)
|
|
||||||
tmp1.Mul(c1, endomorphismA1) |
|
||||||
tmp2.Mul(c2, endomorphismA2) |
|
||||||
k1.Sub(bigIntK, tmp1) |
|
||||||
k1.Add(k1, tmp2) |
|
||||||
// k2 = - c1 * b1 - c2 * b2 from step 5 (note c2's sign is reversed)
|
|
||||||
tmp1.Mul(c1, endomorphismB1) |
|
||||||
tmp2.Mul(c2, endomorphismB2) |
|
||||||
k2.Sub(tmp2, tmp1) |
|
||||||
// Note Bytes() throws out the sign of k1 and k2. This matters
|
|
||||||
// since k1 and/or k2 can be negative. Hence, we pass that
|
|
||||||
// back separately.
|
|
||||||
return k1.Bytes(), k2.Bytes(), k1.Sign(), k2.Sign() |
|
||||||
} |
|
||||||
|
|
||||||
func TestSplitK(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
k string |
|
||||||
k1, k2 string |
|
||||||
s1, s2 int |
|
||||||
}{ |
|
||||||
{ |
|
||||||
"6df2b5d30854069ccdec40ae022f5c948936324a4e9ebed8eb82cfd5a6b6d766", |
|
||||||
"00000000000000000000000000000000b776e53fb55f6b006a270d42d64ec2b1", |
|
||||||
"00000000000000000000000000000000d6cc32c857f1174b604eefc544f0c7f7", |
|
||||||
-1, -1, |
|
||||||
}, |
|
||||||
{ |
|
||||||
"6ca00a8f10632170accc1b3baf2a118fa5725f41473f8959f34b8f860c47d88d", |
|
||||||
"0000000000000000000000000000000007b21976c1795723c1bfbfa511e95b84", |
|
||||||
"00000000000000000000000000000000d8d2d5f9d20fc64fd2cf9bda09a5bf90", |
|
||||||
1, -1, |
|
||||||
}, |
|
||||||
{ |
|
||||||
"b2eda8ab31b259032d39cbc2a234af17fcee89c863a8917b2740b67568166289", |
|
||||||
"00000000000000000000000000000000507d930fecda7414fc4a523b95ef3c8c", |
|
||||||
"00000000000000000000000000000000f65ffb179df189675338c6185cb839be", |
|
||||||
-1, -1, |
|
||||||
}, |
|
||||||
{ |
|
||||||
"f6f00e44f179936f2befc7442721b0633f6bafdf7161c167ffc6f7751980e3a0", |
|
||||||
"0000000000000000000000000000000008d0264f10bcdcd97da3faa38f85308d", |
|
||||||
"0000000000000000000000000000000065fed1506eb6605a899a54e155665f79", |
|
||||||
-1, -1, |
|
||||||
}, |
|
||||||
{ |
|
||||||
"8679085ab081dc92cdd23091ce3ee998f6b320e419c3475fae6b5b7d3081996e", |
|
||||||
"0000000000000000000000000000000089fbf24fbaa5c3c137b4f1cedc51d975", |
|
||||||
"00000000000000000000000000000000d38aa615bd6754d6f4d51ccdaf529fea", |
|
||||||
-1, -1, |
|
||||||
}, |
|
||||||
{ |
|
||||||
"6b1247bb7931dfcae5b5603c8b5ae22ce94d670138c51872225beae6bba8cdb3", |
|
||||||
"000000000000000000000000000000008acc2a521b21b17cfb002c83be62f55d", |
|
||||||
"0000000000000000000000000000000035f0eff4d7430950ecb2d94193dedc79", |
|
||||||
-1, -1, |
|
||||||
}, |
|
||||||
{ |
|
||||||
"a2e8ba2e8ba2e8ba2e8ba2e8ba2e8ba219b51835b55cc30ebfe2f6599bc56f58", |
|
||||||
"0000000000000000000000000000000045c53aa1bb56fcd68c011e2dad6758e4", |
|
||||||
"00000000000000000000000000000000a2e79d200f27f2360fba57619936159b", |
|
||||||
-1, -1, |
|
||||||
}, |
|
||||||
} |
|
||||||
s256 := S256() |
|
||||||
for i, test := range tests { |
|
||||||
k, ok := new(big.Int).SetString(test.k, 16) |
|
||||||
if !ok { |
|
||||||
t.Errorf("%d: bad value for k: %s", i, test.k) |
|
||||||
} |
|
||||||
k1, k2, k1Sign, k2Sign := splitK(k.Bytes()) |
|
||||||
k1str := fmt.Sprintf("%064x", k1) |
|
||||||
if test.k1 != k1str { |
|
||||||
t.Errorf("%d: bad k1: got %v, want %v", i, k1str, test.k1) |
|
||||||
} |
|
||||||
k2str := fmt.Sprintf("%064x", k2) |
|
||||||
if test.k2 != k2str { |
|
||||||
t.Errorf("%d: bad k2: got %v, want %v", i, k2str, test.k2) |
|
||||||
} |
|
||||||
if test.s1 != k1Sign { |
|
||||||
t.Errorf("%d: bad k1 sign: got %d, want %d", i, k1Sign, test.s1) |
|
||||||
} |
|
||||||
if test.s2 != k2Sign { |
|
||||||
t.Errorf("%d: bad k2 sign: got %d, want %d", i, k2Sign, test.s2) |
|
||||||
} |
|
||||||
k1Int := new(big.Int).SetBytes(k1) |
|
||||||
k1SignInt := new(big.Int).SetInt64(int64(k1Sign)) |
|
||||||
k1Int.Mul(k1Int, k1SignInt) |
|
||||||
k2Int := new(big.Int).SetBytes(k2) |
|
||||||
k2SignInt := new(big.Int).SetInt64(int64(k2Sign)) |
|
||||||
k2Int.Mul(k2Int, k2SignInt) |
|
||||||
gotK := new(big.Int).Mul(k2Int, endomorphismLambda) |
|
||||||
gotK.Add(k1Int, gotK) |
|
||||||
gotK.Mod(gotK, s256.N) |
|
||||||
if k.Cmp(gotK) != 0 { |
|
||||||
t.Errorf("%d: bad k: got %X, want %X", i, gotK.Bytes(), k.Bytes()) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestSplitKRand(t *testing.T) { |
|
||||||
s256 := S256() |
|
||||||
for i := 0; i < 1024; i++ { |
|
||||||
bytesK := make([]byte, 32) |
|
||||||
_, err := rand.Read(bytesK) |
|
||||||
if err != nil { |
|
||||||
t.Fatalf("failed to read random data at %d", i) |
|
||||||
break |
|
||||||
} |
|
||||||
k := new(big.Int).SetBytes(bytesK) |
|
||||||
k1, k2, k1Sign, k2Sign := splitK(bytesK) |
|
||||||
k1Int := new(big.Int).SetBytes(k1) |
|
||||||
k1SignInt := new(big.Int).SetInt64(int64(k1Sign)) |
|
||||||
k1Int.Mul(k1Int, k1SignInt) |
|
||||||
k2Int := new(big.Int).SetBytes(k2) |
|
||||||
k2SignInt := new(big.Int).SetInt64(int64(k2Sign)) |
|
||||||
k2Int.Mul(k2Int, k2SignInt) |
|
||||||
gotK := new(big.Int).Mul(k2Int, endomorphismLambda) |
|
||||||
gotK.Add(k1Int, gotK) |
|
||||||
gotK.Mod(gotK, s256.N) |
|
||||||
if k.Cmp(gotK) != 0 { |
|
||||||
t.Errorf("%d: bad k: got %X, want %X", i, gotK.Bytes(), k.Bytes()) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Test this curve's usage with the ecdsa package.
|
|
||||||
|
|
||||||
func testKeyGeneration(t *testing.T, c *KoblitzCurve, tag string) { |
|
||||||
priv, err := NewSecretKey() |
|
||||||
if err != nil { |
|
||||||
t.Errorf("%s: error: %s", tag, err) |
|
||||||
return |
|
||||||
} |
|
||||||
pub := priv.PubKey() |
|
||||||
if !c.IsOnCurve(pub.X(), pub.Y()) { |
|
||||||
t.Errorf("%s: public key invalid: %s", tag, err) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func TestKeyGeneration(t *testing.T) { |
|
||||||
testKeyGeneration(t, S256(), "S256") |
|
||||||
} |
|
||||||
|
|
||||||
// checkNAFEncoding returns an error if the provided positive and negative
|
|
||||||
// portions of an overall NAF encoding do not adhere to the requirements or they
|
|
||||||
// do not sum back to the provided original value.
|
|
||||||
func checkNAFEncoding(pos, neg []byte, origValue *big.Int) error { |
|
||||||
// NAF must not have a leading zero byte and the number of negative
|
|
||||||
// bytes must not exceed the positive portion.
|
|
||||||
if len(pos) > 0 && pos[0] == 0 { |
|
||||||
return fmt.Errorf("positive has leading zero -- got %x", pos) |
|
||||||
} |
|
||||||
if len(neg) > len(pos) { |
|
||||||
return fmt.Errorf( |
|
||||||
"negative has len %d > pos len %d", len(neg), |
|
||||||
len(pos), |
|
||||||
) |
|
||||||
} |
|
||||||
// Ensure the result doesn't have any adjacent non-zero digits.
|
|
||||||
gotPos := new(big.Int).SetBytes(pos) |
|
||||||
gotNeg := new(big.Int).SetBytes(neg) |
|
||||||
posOrNeg := new(big.Int).Or(gotPos, gotNeg) |
|
||||||
prevBit := posOrNeg.Bit(0) |
|
||||||
for bit := 1; bit < posOrNeg.BitLen(); bit++ { |
|
||||||
thisBit := posOrNeg.Bit(bit) |
|
||||||
if prevBit == 1 && thisBit == 1 { |
|
||||||
return fmt.Errorf( |
|
||||||
"adjacent non-zero digits found at bit pos %d", |
|
||||||
bit-1, |
|
||||||
) |
|
||||||
} |
|
||||||
prevBit = thisBit |
|
||||||
} |
|
||||||
// Ensure the resulting positive and negative portions of the overall
|
|
||||||
// NAF representation sum back to the original value.
|
|
||||||
gotValue := new(big.Int).Sub(gotPos, gotNeg) |
|
||||||
if origValue.Cmp(gotValue) != 0 { |
|
||||||
return fmt.Errorf( |
|
||||||
"pos-neg is not original value: got %x, want %x", |
|
||||||
gotValue, origValue, |
|
||||||
) |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
@ -1,153 +0,0 @@ |
|||||||
package chaincfg |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"time" |
|
||||||
|
|
||||||
"next.orly.dev/pkg/crypto/ec/wire" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// ErrNoBlockClock is returned when an operation fails due to lack of
|
|
||||||
// synchornization with the current up to date block clock.
|
|
||||||
ErrNoBlockClock = fmt.Errorf("no block clock synchronized") |
|
||||||
) |
|
||||||
|
|
||||||
// ConsensusDeploymentStarter determines if a given consensus deployment has
|
|
||||||
// started. A deployment has started once according to the current "time", the
|
|
||||||
// deployment is eligible for activation once a perquisite condition has
|
|
||||||
// passed.
|
|
||||||
type ConsensusDeploymentStarter interface { |
|
||||||
// HasStarted returns true if the consensus deployment has started.
|
|
||||||
HasStarted(*wire.BlockHeader) (bool, error) |
|
||||||
} |
|
||||||
|
|
||||||
// ConsensusDeploymentEnder determines if a given consensus deployment has
|
|
||||||
// ended. A deployment has ended once according got eh current "time", the
|
|
||||||
// deployment is no longer eligible for activation.
|
|
||||||
type ConsensusDeploymentEnder interface { |
|
||||||
// HasEnded returns true if the consensus deployment has ended.
|
|
||||||
HasEnded(*wire.BlockHeader) (bool, error) |
|
||||||
} |
|
||||||
|
|
||||||
// BlockClock is an abstraction over the past median time computation. The past
|
|
||||||
// median time computation is used in several consensus checks such as CSV, and
|
|
||||||
// also BIP 9 version bits. This interface allows callers to abstract away the
|
|
||||||
// computation of the past median time from the perspective of a given block
|
|
||||||
// header.
|
|
||||||
type BlockClock interface { |
|
||||||
// PastMedianTime returns the past median time from the PoV of the
|
|
||||||
// passed block header. The past median time is the median time of the
|
|
||||||
// 11 blocks prior to the passed block header.
|
|
||||||
PastMedianTime(*wire.BlockHeader) (time.Time, error) |
|
||||||
} |
|
||||||
|
|
||||||
// ClockConsensusDeploymentEnder is a more specialized version of the
|
|
||||||
// ConsensusDeploymentEnder that uses a BlockClock in order to determine if a
|
|
||||||
// deployment has started or not.
|
|
||||||
//
|
|
||||||
// NOTE: Any calls to HasEnded will _fail_ with ErrNoBlockClock if they
|
|
||||||
// happen before SynchronizeClock is executed.
|
|
||||||
type ClockConsensusDeploymentEnder interface { |
|
||||||
ConsensusDeploymentEnder |
|
||||||
// SynchronizeClock synchronizes the target ConsensusDeploymentStarter
|
|
||||||
// with the current up-to date BlockClock.
|
|
||||||
SynchronizeClock(clock BlockClock) |
|
||||||
} |
|
||||||
|
|
||||||
// MedianTimeDeploymentStarter is a ClockConsensusDeploymentStarter that uses
|
|
||||||
// the median time past of a target block node to determine if a deployment has
|
|
||||||
// started.
|
|
||||||
type MedianTimeDeploymentStarter struct { |
|
||||||
blockClock BlockClock |
|
||||||
startTime time.Time |
|
||||||
} |
|
||||||
|
|
||||||
// NewMedianTimeDeploymentStarter returns a new instance of a
|
|
||||||
// MedianTimeDeploymentStarter for a given start time. Using a time.Time
|
|
||||||
// instance where IsZero() is true, indicates that a deployment should be
|
|
||||||
// considered to always have been started.
|
|
||||||
func NewMedianTimeDeploymentStarter(startTime time.Time) *MedianTimeDeploymentStarter { |
|
||||||
return &MedianTimeDeploymentStarter{ |
|
||||||
startTime: startTime, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// HasStarted returns true if the consensus deployment has started.
|
|
||||||
func (m *MedianTimeDeploymentStarter) HasStarted(blkHeader *wire.BlockHeader) ( |
|
||||||
bool, |
|
||||||
error, |
|
||||||
) { |
|
||||||
switch { |
|
||||||
// If we haven't yet been synchronized with a block clock, then we
|
|
||||||
// can't tell the time, so we'll fail.
|
|
||||||
case m.blockClock == nil: |
|
||||||
return false, ErrNoBlockClock |
|
||||||
// If the time is "zero", then the deployment has always started.
|
|
||||||
case m.startTime.IsZero(): |
|
||||||
return true, nil |
|
||||||
} |
|
||||||
medianTime, err := m.blockClock.PastMedianTime(blkHeader) |
|
||||||
if err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
// We check both after and equal here as after will fail for equivalent
|
|
||||||
// times, and we want to be inclusive.
|
|
||||||
return medianTime.After(m.startTime) || medianTime.Equal(m.startTime), nil |
|
||||||
} |
|
||||||
|
|
||||||
// MedianTimeDeploymentEnder is a ClockConsensusDeploymentEnder that uses the
|
|
||||||
// median time past of a target block to determine if a deployment has ended.
|
|
||||||
type MedianTimeDeploymentEnder struct { |
|
||||||
blockClock BlockClock |
|
||||||
endTime time.Time |
|
||||||
} |
|
||||||
|
|
||||||
// NewMedianTimeDeploymentEnder returns a new instance of the
|
|
||||||
// MedianTimeDeploymentEnder anchored around the passed endTime. Using a
|
|
||||||
// time.Time instance where IsZero() is true, indicates that a deployment
|
|
||||||
// should be considered to never end.
|
|
||||||
func NewMedianTimeDeploymentEnder(endTime time.Time) *MedianTimeDeploymentEnder { |
|
||||||
return &MedianTimeDeploymentEnder{ |
|
||||||
endTime: endTime, |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// HasEnded returns true if the deployment has ended.
|
|
||||||
func (m *MedianTimeDeploymentEnder) HasEnded(blkHeader *wire.BlockHeader) ( |
|
||||||
bool, |
|
||||||
error, |
|
||||||
) { |
|
||||||
switch { |
|
||||||
// If we haven't yet been synchronized with a block clock, then we can't tell
|
|
||||||
// the time, so we'll we haven't yet been synchronized with a block
|
|
||||||
// clock, then w can't tell the time, so we'll fail.
|
|
||||||
case m.blockClock == nil: |
|
||||||
return false, ErrNoBlockClock |
|
||||||
// If the time is "zero", then the deployment never ends.
|
|
||||||
case m.endTime.IsZero(): |
|
||||||
return false, nil |
|
||||||
} |
|
||||||
medianTime, err := m.blockClock.PastMedianTime(blkHeader) |
|
||||||
if err != nil { |
|
||||||
return false, err |
|
||||||
} |
|
||||||
// We check both after and equal here as after will fail for equivalent
|
|
||||||
// times, and we want to be inclusive.
|
|
||||||
return medianTime.After(m.endTime) || medianTime.Equal(m.endTime), nil |
|
||||||
} |
|
||||||
|
|
||||||
// EndTime returns the raw end time of the deployment.
|
|
||||||
func (m *MedianTimeDeploymentEnder) EndTime() time.Time { |
|
||||||
return m.endTime |
|
||||||
} |
|
||||||
|
|
||||||
// SynchronizeClock synchronizes the target ConsensusDeploymentEnder with the
|
|
||||||
// current up-to date BlockClock.
|
|
||||||
func (m *MedianTimeDeploymentEnder) SynchronizeClock(clock BlockClock) { |
|
||||||
m.blockClock = clock |
|
||||||
} |
|
||||||
|
|
||||||
// A compile-time assertion to ensure MedianTimeDeploymentEnder implements the
|
|
||||||
// ClockConsensusDeploymentStarter interface.
|
|
||||||
var _ ClockConsensusDeploymentEnder = (*MedianTimeDeploymentEnder)(nil) |
|
||||||
@ -1,110 +0,0 @@ |
|||||||
package chaincfg |
|
||||||
|
|
||||||
import ( |
|
||||||
"time" |
|
||||||
|
|
||||||
"next.orly.dev/pkg/crypto/ec/chainhash" |
|
||||||
"next.orly.dev/pkg/crypto/ec/wire" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// genesisCoinbaseTx is the coinbase transaction for the genesis blocks for
|
|
||||||
// the main network, regression test network, and test network (version 3).
|
|
||||||
genesisCoinbaseTx = wire.MsgTx{ |
|
||||||
Version: 1, |
|
||||||
TxIn: []*wire.TxIn{ |
|
||||||
{ |
|
||||||
PreviousOutPoint: wire.OutPoint{ |
|
||||||
Hash: chainhash.Hash{}, |
|
||||||
Index: 0xffffffff, |
|
||||||
}, |
|
||||||
SignatureScript: []byte{ |
|
||||||
0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, |
|
||||||
0x45, /* |.......E| */ |
|
||||||
0x54, 0x68, 0x65, 0x20, 0x54, 0x69, 0x6d, |
|
||||||
0x65, /* |The Time| */ |
|
||||||
0x73, 0x20, 0x30, 0x33, 0x2f, 0x4a, 0x61, |
|
||||||
0x6e, /* |s 03/Jan| */ |
|
||||||
0x2f, 0x32, 0x30, 0x30, 0x39, 0x20, 0x43, |
|
||||||
0x68, /* |/2009 Ch| */ |
|
||||||
0x61, 0x6e, 0x63, 0x65, 0x6c, 0x6c, 0x6f, |
|
||||||
0x72, /* |ancellor| */ |
|
||||||
0x20, 0x6f, 0x6e, 0x20, 0x62, 0x72, 0x69, |
|
||||||
0x6e, /* | on brin| */ |
|
||||||
0x6b, 0x20, 0x6f, 0x66, 0x20, 0x73, 0x65, |
|
||||||
0x63, /* |k of sec|*/ |
|
||||||
0x6f, 0x6e, 0x64, 0x20, 0x62, 0x61, 0x69, |
|
||||||
0x6c, /* |ond bail| */ |
|
||||||
0x6f, 0x75, 0x74, 0x20, 0x66, 0x6f, 0x72, |
|
||||||
0x20, /* |out for |*/ |
|
||||||
0x62, 0x61, 0x6e, 0x6b, 0x73, /* |banks| */ |
|
||||||
}, |
|
||||||
Sequence: 0xffffffff, |
|
||||||
}, |
|
||||||
}, |
|
||||||
TxOut: []*wire.TxOut{ |
|
||||||
{ |
|
||||||
Value: 0x12a05f200, |
|
||||||
PkScript: []byte{ |
|
||||||
0x41, 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, |
|
||||||
0x55, /* |A.g....U| */ |
|
||||||
0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, |
|
||||||
0x30, /* |H'.g..q0| */ |
|
||||||
0xb7, 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, |
|
||||||
0x39, /* |..\..(.9| */ |
|
||||||
0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, |
|
||||||
0x61, /* |..yb...a| */ |
|
||||||
0xde, 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, |
|
||||||
0xef, /* |..I..?L.| */ |
|
||||||
0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, |
|
||||||
0xc1, /* |8..U....| */ |
|
||||||
0x12, 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, |
|
||||||
0x0b, /* |..\8M...| */ |
|
||||||
0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, |
|
||||||
0xf1, /* |.W.Lp+k.| */ |
|
||||||
0x1d, 0x5f, 0xac, /* |._.| */ |
|
||||||
}, |
|
||||||
}, |
|
||||||
}, |
|
||||||
LockTime: 0, |
|
||||||
} |
|
||||||
// genesisHash is the hash of the first block in the block chain for the main
|
|
||||||
// network (genesis block).
|
|
||||||
genesisHash = chainhash.Hash( |
|
||||||
[chainhash.HashSize]byte{ |
|
||||||
// Make go vet happy.
|
|
||||||
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, |
|
||||||
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, |
|
||||||
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, |
|
||||||
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, |
|
||||||
}, |
|
||||||
) |
|
||||||
// genesisMerkleRoot is the hash of the first transaction in the genesis block
|
|
||||||
// for the main network.
|
|
||||||
genesisMerkleRoot = chainhash.Hash( |
|
||||||
[chainhash.HashSize]byte{ |
|
||||||
// Make go vet happy.
|
|
||||||
0x3b, 0xa3, 0xed, 0xfd, 0x7a, 0x7b, 0x12, 0xb2, |
|
||||||
0x7a, 0xc7, 0x2c, 0x3e, 0x67, 0x76, 0x8f, 0x61, |
|
||||||
0x7f, 0xc8, 0x1b, 0xc3, 0x88, 0x8a, 0x51, 0x32, |
|
||||||
0x3a, 0x9f, 0xb8, 0xaa, 0x4b, 0x1e, 0x5e, 0x4a, |
|
||||||
}, |
|
||||||
) |
|
||||||
// genesisBlock defines
|
|
||||||
// genesisBlock defines the genesis block of the block chain which serves as the
|
|
||||||
// public transaction ledger for the main network.
|
|
||||||
genesisBlock = wire.MsgBlock{ |
|
||||||
Header: wire.BlockHeader{ |
|
||||||
Version: 1, |
|
||||||
PrevBlock: chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000
|
|
||||||
MerkleRoot: genesisMerkleRoot, // 4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b
|
|
||||||
Timestamp: time.Unix( |
|
||||||
0x495fab29, |
|
||||||
0, |
|
||||||
), // 2009-01-03 18:15:05 +0000 UTC
|
|
||||||
Bits: 0x1d00ffff, // 486604799 [00000000ffff0000000000000000000000000000000000000000000000000000]
|
|
||||||
Nonce: 0x7c2bac1d, // 2083236893
|
|
||||||
}, |
|
||||||
Transactions: []*wire.MsgTx{&genesisCoinbaseTx}, |
|
||||||
} |
|
||||||
) |
|
||||||
@ -1,493 +0,0 @@ |
|||||||
// Package chaincfg provides basic parameters for bitcoin chain and testnets.
|
|
||||||
package chaincfg |
|
||||||
|
|
||||||
import ( |
|
||||||
"math/big" |
|
||||||
"time" |
|
||||||
|
|
||||||
"next.orly.dev/pkg/crypto/ec/chainhash" |
|
||||||
"next.orly.dev/pkg/crypto/ec/wire" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// bigOne is 1 represented as a big.Int. It is defined here to avoid
|
|
||||||
// the overhead of creating it multiple times.
|
|
||||||
bigOne = big.NewInt(1) |
|
||||||
|
|
||||||
// mainPowLimit is the highest proof of work value a Bitcoin block can
|
|
||||||
// have for the main network. It is the value 2^224 - 1.
|
|
||||||
mainPowLimit = new(big.Int).Sub(new(big.Int).Lsh(bigOne, 224), bigOne) |
|
||||||
) |
|
||||||
|
|
||||||
// Constants that define the deployment offset in the deployments field of the
|
|
||||||
// parameters for each deployment. This is useful to be able to get the details
|
|
||||||
// of a specific deployment by name.
|
|
||||||
const ( |
|
||||||
// DeploymentTestDummy defines the rule change deployment ID for testing
|
|
||||||
// purposes.
|
|
||||||
DeploymentTestDummy = iota |
|
||||||
|
|
||||||
// DeploymentTestDummyMinActivation defines the rule change deployment
|
|
||||||
// ID for testing purposes. This differs from the DeploymentTestDummy
|
|
||||||
// in that it specifies the newer params the taproot fork used for
|
|
||||||
// activation: a custom threshold and a min activation height.
|
|
||||||
DeploymentTestDummyMinActivation |
|
||||||
|
|
||||||
// DeploymentCSV defines the rule change deployment ID for the CSV
|
|
||||||
// soft-fork package. The CSV package includes the deployment of BIPS
|
|
||||||
// 68, 112, and 113.
|
|
||||||
DeploymentCSV |
|
||||||
|
|
||||||
// DeploymentSegwit defines the rule change deployment ID for the
|
|
||||||
// Segregated Witness (segwit) soft-fork package. The segwit package
|
|
||||||
// includes the deployment of BIPS 141, 142, 144, 145, 147 and 173.
|
|
||||||
DeploymentSegwit |
|
||||||
|
|
||||||
// DeploymentTaproot defines the rule change deployment ID for the
|
|
||||||
// Taproot (+Schnorr) soft-fork package. The taproot package includes
|
|
||||||
// the deployment of BIPS 340, 341 and 342.
|
|
||||||
DeploymentTaproot |
|
||||||
|
|
||||||
// NOTE: DefinedDeployments must always come last since it is used to
|
|
||||||
// determine how many defined deployments there currently are.
|
|
||||||
|
|
||||||
// DefinedDeployments is the number of currently defined deployments.
|
|
||||||
DefinedDeployments |
|
||||||
) |
|
||||||
|
|
||||||
// ConsensusDeployment defines details related to a specific consensus rule
|
|
||||||
// change that is voted in. This is part of BIP0009.
|
|
||||||
type ConsensusDeployment struct { |
|
||||||
// BitNumber defines the specific bit number within the block version
|
|
||||||
// this particular soft-fork deployment refers to.
|
|
||||||
BitNumber uint8 |
|
||||||
|
|
||||||
// MinActivationHeight is an optional field that when set (default
|
|
||||||
// value being zero), modifies the traditional BIP 9 state machine by
|
|
||||||
// only transitioning from LockedIn to Active once the block height is
|
|
||||||
// greater than (or equal to) thus specified height.
|
|
||||||
MinActivationHeight uint32 |
|
||||||
|
|
||||||
// CustomActivationThreshold if set (non-zero), will _override_ the
|
|
||||||
// existing RuleChangeActivationThreshold value set at the
|
|
||||||
// network/chain level. This value divided by the active
|
|
||||||
// MinerConfirmationWindow denotes the threshold required for
|
|
||||||
// activation. A value of 1815 block denotes a 90% threshold.
|
|
||||||
CustomActivationThreshold uint32 |
|
||||||
|
|
||||||
// DeploymentStarter is used to determine if the given
|
|
||||||
// ConsensusDeployment has started or not.
|
|
||||||
DeploymentStarter ConsensusDeploymentStarter |
|
||||||
|
|
||||||
// DeploymentEnder is used to determine if the given
|
|
||||||
// ConsensusDeployment has ended or not.
|
|
||||||
DeploymentEnder ConsensusDeploymentEnder |
|
||||||
} |
|
||||||
|
|
||||||
// Checkpoint identifies a known good point in the block chain. Using
|
|
||||||
// checkpoints allows a few optimizations for old blocks during initial download
|
|
||||||
// and also prevents forks from old blocks.
|
|
||||||
//
|
|
||||||
// Each checkpoint is selected based upon several factors. See the
|
|
||||||
// documentation for blockchain.IsCheckpointCandidate for details on the
|
|
||||||
// selection criteria.
|
|
||||||
type Checkpoint struct { |
|
||||||
Height int32 |
|
||||||
Hash *chainhash.Hash |
|
||||||
} |
|
||||||
|
|
||||||
// DNSSeed identifies a DNS seed.
|
|
||||||
type DNSSeed struct { |
|
||||||
// Host defines the hostname of the seed.
|
|
||||||
Host string |
|
||||||
|
|
||||||
// HasFiltering defines whether the seed supports filtering
|
|
||||||
// by service flags (wire.ServiceFlag).
|
|
||||||
HasFiltering bool |
|
||||||
} |
|
||||||
|
|
||||||
// Params defines a Bitcoin network by its parameters. These parameters may be
|
|
||||||
// used by Bitcoin applications to differentiate networks as well as addresses
|
|
||||||
// and keys for one network from those intended for use on another network.
|
|
||||||
type Params struct { |
|
||||||
// Name defines a human-readable identifier for the network.
|
|
||||||
Name string |
|
||||||
|
|
||||||
// Net defines the magic bytes used to identify the network.
|
|
||||||
Net wire.BitcoinNet |
|
||||||
|
|
||||||
// DefaultPort defines the default peer-to-peer port for the network.
|
|
||||||
DefaultPort string |
|
||||||
|
|
||||||
// DNSSeeds defines a list of DNS seeds for the network that are used
|
|
||||||
// as one method to discover peers.
|
|
||||||
DNSSeeds []DNSSeed |
|
||||||
|
|
||||||
// GenesisBlock defines the first block of the chain.
|
|
||||||
GenesisBlock *wire.MsgBlock |
|
||||||
|
|
||||||
// GenesisHash is the starting block hash.
|
|
||||||
GenesisHash *chainhash.Hash |
|
||||||
|
|
||||||
// PowLimit defines the highest allowed proof of work value for a block
|
|
||||||
// as a uint256.
|
|
||||||
PowLimit *big.Int |
|
||||||
|
|
||||||
// PowLimitBits defines the highest allowed proof of work value for a
|
|
||||||
// block in compact form.
|
|
||||||
PowLimitBits uint32 |
|
||||||
|
|
||||||
// PoWNoRetargeting defines whether the network has difficulty
|
|
||||||
// retargeting enabled or not. This should only be set to true for
|
|
||||||
// regtest like networks.
|
|
||||||
PoWNoRetargeting bool |
|
||||||
|
|
||||||
// These fields define the block heights at which the specified softfork
|
|
||||||
// BIP became active.
|
|
||||||
BIP0034Height int32 |
|
||||||
BIP0065Height int32 |
|
||||||
BIP0066Height int32 |
|
||||||
|
|
||||||
// CoinbaseMaturity is the number of blocks required before newly mined
|
|
||||||
// coins (coinbase transactions) can be spent.
|
|
||||||
CoinbaseMaturity uint16 |
|
||||||
|
|
||||||
// SubsidyReductionInterval is the interval of blocks before the subsidy
|
|
||||||
// is reduced.
|
|
||||||
SubsidyReductionInterval int32 |
|
||||||
|
|
||||||
// TargetTimespan is the desired amount of time that should elapse
|
|
||||||
// before the block difficulty requirement is examined to determine how
|
|
||||||
// it should be changed in order to maintain the desired block
|
|
||||||
// generation rate.
|
|
||||||
TargetTimespan time.Duration |
|
||||||
|
|
||||||
// TargetTimePerBlock is the desired amount of time to generate each
|
|
||||||
// block.
|
|
||||||
TargetTimePerBlock time.Duration |
|
||||||
|
|
||||||
// RetargetAdjustmentFactor is the adjustment factor used to limit
|
|
||||||
// the minimum and maximum amount of adjustment that can occur between
|
|
||||||
// difficulty retargets.
|
|
||||||
RetargetAdjustmentFactor int64 |
|
||||||
|
|
||||||
// ReduceMinDifficulty defines whether the network should reduce the
|
|
||||||
// minimum required difficulty after a long enough period of time has
|
|
||||||
// passed without finding a block. This is really only useful for test
|
|
||||||
// networks and should not be set on a main network.
|
|
||||||
ReduceMinDifficulty bool |
|
||||||
|
|
||||||
// MinDiffReductionTime is the amount of time after which the minimum
|
|
||||||
// required difficulty should be reduced when a block hasn't been found.
|
|
||||||
//
|
|
||||||
// NOTE: This only applies if ReduceMinDifficulty is true.
|
|
||||||
MinDiffReductionTime time.Duration |
|
||||||
|
|
||||||
// GenerateSupported specifies whether or not CPU mining is allowed.
|
|
||||||
GenerateSupported bool |
|
||||||
|
|
||||||
// Checkpoints ordered from oldest to newest.
|
|
||||||
Checkpoints []Checkpoint |
|
||||||
|
|
||||||
// These fields are related to voting on consensus rule changes as
|
|
||||||
// defined by BIP0009.
|
|
||||||
//
|
|
||||||
// RuleChangeActivationThreshold is the number of blocks in a threshold
|
|
||||||
// state retarget window for which a positive vote for a rule change
|
|
||||||
// must be cast in order to lock in a rule change. It should typically
|
|
||||||
// be 95% for the main network and 75% for test networks.
|
|
||||||
//
|
|
||||||
// MinerConfirmationWindow is the number of blocks in each threshold
|
|
||||||
// state retarget window.
|
|
||||||
//
|
|
||||||
// Deployments define the specific consensus rule changes to be voted
|
|
||||||
// on.
|
|
||||||
RuleChangeActivationThreshold uint32 |
|
||||||
MinerConfirmationWindow uint32 |
|
||||||
Deployments [DefinedDeployments]ConsensusDeployment |
|
||||||
|
|
||||||
// Mempool parameters
|
|
||||||
RelayNonStdTxs bool |
|
||||||
|
|
||||||
// Human-readable part for Bech32 encoded segwit addresses, as defined
|
|
||||||
// in BIP 173.
|
|
||||||
Bech32HRPSegwit []byte |
|
||||||
|
|
||||||
// Address encoding magics
|
|
||||||
PubKeyHashAddrID byte // First byte of a P2PKH address
|
|
||||||
ScriptHashAddrID byte // First byte of a P2SH address
|
|
||||||
PrivateKeyID byte // First byte of a WIF private key
|
|
||||||
WitnessPubKeyHashAddrID byte // First byte of a P2WPKH address
|
|
||||||
WitnessScriptHashAddrID byte // First byte of a P2WSH address
|
|
||||||
|
|
||||||
// BIP32 hierarchical deterministic extended key magics
|
|
||||||
HDPrivateKeyID [4]byte |
|
||||||
HDPublicKeyID [4]byte |
|
||||||
|
|
||||||
// BIP44 coin type used in the hierarchical deterministic path for
|
|
||||||
// address generation.
|
|
||||||
HDCoinType uint32 |
|
||||||
} |
|
||||||
|
|
||||||
// MainNetParams defines the network parameters for the main Bitcoin network.
|
|
||||||
var MainNetParams = Params{ |
|
||||||
Name: "mainnet", |
|
||||||
Net: wire.MainNet, |
|
||||||
DefaultPort: "8333", |
|
||||||
DNSSeeds: []DNSSeed{ |
|
||||||
{"seed.bitcoin.sipa.be", true}, |
|
||||||
{"dnsseed.bluematt.me", true}, |
|
||||||
{"dnsseed.bitcoin.dashjr.org", false}, |
|
||||||
{"seed.bitcoinstats.com", true}, |
|
||||||
{"seed.bitnodes.io", false}, |
|
||||||
{"seed.bitcoin.jonasschnelli.ch", true}, |
|
||||||
}, |
|
||||||
|
|
||||||
// Chain parameters
|
|
||||||
GenesisBlock: &genesisBlock, |
|
||||||
GenesisHash: &genesisHash, |
|
||||||
PowLimit: mainPowLimit, |
|
||||||
PowLimitBits: 0x1d00ffff, |
|
||||||
BIP0034Height: 227931, // 000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8
|
|
||||||
BIP0065Height: 388381, // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0
|
|
||||||
BIP0066Height: 363725, // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
|
|
||||||
CoinbaseMaturity: 100, |
|
||||||
SubsidyReductionInterval: 210000, |
|
||||||
TargetTimespan: time.Hour * 24 * 14, // 14 days
|
|
||||||
TargetTimePerBlock: time.Minute * 10, // 10 minutes
|
|
||||||
RetargetAdjustmentFactor: 4, // 25% less, 400% more
|
|
||||||
ReduceMinDifficulty: false, |
|
||||||
MinDiffReductionTime: 0, |
|
||||||
GenerateSupported: false, |
|
||||||
|
|
||||||
// Checkpoints ordered from oldest to newest.
|
|
||||||
Checkpoints: []Checkpoint{ |
|
||||||
{ |
|
||||||
11111, |
|
||||||
newHashFromStr("0000000069e244f73d78e8fd29ba2fd2ed618bd6fa2ee92559f542fdb26e7c1d"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
33333, |
|
||||||
newHashFromStr("000000002dd5588a74784eaa7ab0507a18ad16a236e7b1ce69f00d7ddfb5d0a6"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
74000, |
|
||||||
newHashFromStr("0000000000573993a3c9e41ce34471c079dcf5f52a0e824a81e7f953b8661a20"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
105000, |
|
||||||
newHashFromStr("00000000000291ce28027faea320c8d2b054b2e0fe44a773f3eefb151d6bdc97"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
134444, |
|
||||||
newHashFromStr("00000000000005b12ffd4cd315cd34ffd4a594f430ac814c91184a0d42d2b0fe"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
168000, |
|
||||||
newHashFromStr("000000000000099e61ea72015e79632f216fe6cb33d7899acb35b75c8303b763"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
193000, |
|
||||||
newHashFromStr("000000000000059f452a5f7340de6682a977387c17010ff6e6c3bd83ca8b1317"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
210000, |
|
||||||
newHashFromStr("000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
216116, |
|
||||||
newHashFromStr("00000000000001b4f4b433e81ee46494af945cf96014816a4e2370f11b23df4e"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
225430, |
|
||||||
newHashFromStr("00000000000001c108384350f74090433e7fcf79a606b8e797f065b130575932"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
250000, |
|
||||||
newHashFromStr("000000000000003887df1f29024b06fc2200b55f8af8f35453d7be294df2d214"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
267300, |
|
||||||
newHashFromStr("000000000000000a83fbd660e918f218bf37edd92b748ad940483c7c116179ac"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
279000, |
|
||||||
newHashFromStr("0000000000000001ae8c72a0b0c301f67e3afca10e819efa9041e458e9bd7e40"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
300255, |
|
||||||
newHashFromStr("0000000000000000162804527c6e9b9f0563a280525f9d08c12041def0a0f3b2"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
319400, |
|
||||||
newHashFromStr("000000000000000021c6052e9becade189495d1c539aa37c58917305fd15f13b"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
343185, |
|
||||||
newHashFromStr("0000000000000000072b8bf361d01a6ba7d445dd024203fafc78768ed4368554"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
352940, |
|
||||||
newHashFromStr("000000000000000010755df42dba556bb72be6a32f3ce0b6941ce4430152c9ff"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
382320, |
|
||||||
newHashFromStr("00000000000000000a8dc6ed5b133d0eb2fd6af56203e4159789b092defd8ab2"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
400000, |
|
||||||
newHashFromStr("000000000000000004ec466ce4732fe6f1ed1cddc2ed4b328fff5224276e3f6f"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
430000, |
|
||||||
newHashFromStr("000000000000000001868b2bb3a285f3cc6b33ea234eb70facf4dcdf22186b87"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
460000, |
|
||||||
newHashFromStr("000000000000000000ef751bbce8e744ad303c47ece06c8d863e4d417efc258c"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
490000, |
|
||||||
newHashFromStr("000000000000000000de069137b17b8d5a3dfbd5b145b2dcfb203f15d0c4de90"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
520000, |
|
||||||
newHashFromStr("0000000000000000000d26984c0229c9f6962dc74db0a6d525f2f1640396f69c"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
550000, |
|
||||||
newHashFromStr("000000000000000000223b7a2298fb1c6c75fb0efc28a4c56853ff4112ec6bc9"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
560000, |
|
||||||
newHashFromStr("0000000000000000002c7b276daf6efb2b6aa68e2ce3be67ef925b3264ae7122"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
563378, |
|
||||||
newHashFromStr("0000000000000000000f1c54590ee18d15ec70e68c8cd4cfbadb1b4f11697eee"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
597379, |
|
||||||
newHashFromStr("00000000000000000005f8920febd3925f8272a6a71237563d78c2edfdd09ddf"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
623950, |
|
||||||
newHashFromStr("0000000000000000000f2adce67e49b0b6bdeb9de8b7c3d7e93b21e7fc1e819d"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
654683, |
|
||||||
newHashFromStr("0000000000000000000b9d2ec5a352ecba0592946514a92f14319dc2b367fc72"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
691719, |
|
||||||
newHashFromStr("00000000000000000008a89e854d57e5667df88f1cdef6fde2fbca1de5b639ad"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
724466, |
|
||||||
newHashFromStr("000000000000000000052d314a259755ca65944e68df6b12a067ea8f1f5a7091"), |
|
||||||
}, |
|
||||||
{ |
|
||||||
751565, |
|
||||||
newHashFromStr("00000000000000000009c97098b5295f7e5f183ac811fb5d1534040adb93cabd"), |
|
||||||
}, |
|
||||||
}, |
|
||||||
|
|
||||||
// Consensus rule change deployments.
|
|
||||||
//
|
|
||||||
// The miner confirmation window is defined as:
|
|
||||||
// target proof of work timespan / target proof of work spacing
|
|
||||||
RuleChangeActivationThreshold: 1916, // 95% of MinerConfirmationWindow
|
|
||||||
MinerConfirmationWindow: 2016, //
|
|
||||||
Deployments: [DefinedDeployments]ConsensusDeployment{ |
|
||||||
DeploymentTestDummy: { |
|
||||||
BitNumber: 28, |
|
||||||
DeploymentStarter: NewMedianTimeDeploymentStarter( |
|
||||||
time.Unix(11991456010, 0), // January 1, 2008 UTC
|
|
||||||
), |
|
||||||
DeploymentEnder: NewMedianTimeDeploymentEnder( |
|
||||||
time.Unix(1230767999, 0), // December 31, 2008 UTC
|
|
||||||
), |
|
||||||
}, |
|
||||||
DeploymentTestDummyMinActivation: { |
|
||||||
BitNumber: 22, |
|
||||||
CustomActivationThreshold: 1815, // Only needs 90% hash rate.
|
|
||||||
MinActivationHeight: 10_0000, // Can only activate after height 10k.
|
|
||||||
DeploymentStarter: NewMedianTimeDeploymentStarter( |
|
||||||
time.Time{}, // Always available for vote
|
|
||||||
), |
|
||||||
DeploymentEnder: NewMedianTimeDeploymentEnder( |
|
||||||
time.Time{}, // Never expires
|
|
||||||
), |
|
||||||
}, |
|
||||||
DeploymentCSV: { |
|
||||||
BitNumber: 0, |
|
||||||
DeploymentStarter: NewMedianTimeDeploymentStarter( |
|
||||||
time.Unix(1462060800, 0), // May 1st, 2016
|
|
||||||
), |
|
||||||
DeploymentEnder: NewMedianTimeDeploymentEnder( |
|
||||||
time.Unix(1493596800, 0), // May 1st, 2017
|
|
||||||
), |
|
||||||
}, |
|
||||||
DeploymentSegwit: { |
|
||||||
BitNumber: 1, |
|
||||||
DeploymentStarter: NewMedianTimeDeploymentStarter( |
|
||||||
time.Unix(1479168000, 0), // November 15, 2016 UTC
|
|
||||||
), |
|
||||||
DeploymentEnder: NewMedianTimeDeploymentEnder( |
|
||||||
time.Unix(1510704000, 0), // November 15, 2017 UTC.
|
|
||||||
), |
|
||||||
}, |
|
||||||
DeploymentTaproot: { |
|
||||||
BitNumber: 2, |
|
||||||
DeploymentStarter: NewMedianTimeDeploymentStarter( |
|
||||||
time.Unix(1619222400, 0), // April 24th, 2021 UTC.
|
|
||||||
), |
|
||||||
DeploymentEnder: NewMedianTimeDeploymentEnder( |
|
||||||
time.Unix(1628640000, 0), // August 11th, 2021 UTC.
|
|
||||||
), |
|
||||||
CustomActivationThreshold: 1815, // 90%
|
|
||||||
MinActivationHeight: 709_632, |
|
||||||
}, |
|
||||||
}, |
|
||||||
|
|
||||||
// Mempool parameters
|
|
||||||
RelayNonStdTxs: false, |
|
||||||
|
|
||||||
// Human-readable part for Bech32 encoded segwit addresses, as defined in
|
|
||||||
// BIP 173.
|
|
||||||
Bech32HRPSegwit: []byte("bc"), // always bc for main net
|
|
||||||
|
|
||||||
// Address encoding magics
|
|
||||||
PubKeyHashAddrID: 0x00, // starts with 1
|
|
||||||
ScriptHashAddrID: 0x05, // starts with 3
|
|
||||||
PrivateKeyID: 0x80, // starts with 5 (uncompressed) or K (compressed)
|
|
||||||
WitnessPubKeyHashAddrID: 0x06, // starts with p2
|
|
||||||
WitnessScriptHashAddrID: 0x0A, // starts with 7Xh
|
|
||||||
|
|
||||||
// BIP32 hierarchical deterministic extended key magics
|
|
||||||
HDPrivateKeyID: [4]byte{0x04, 0x88, 0xad, 0xe4}, // starts with xprv
|
|
||||||
HDPublicKeyID: [4]byte{0x04, 0x88, 0xb2, 0x1e}, // starts with xpub
|
|
||||||
|
|
||||||
// BIP44 coin type used in the hierarchical deterministic path for
|
|
||||||
// address generation.
|
|
||||||
HDCoinType: 0, |
|
||||||
} |
|
||||||
|
|
||||||
// newHashFromStr converts the passed big-endian hex string into a
|
|
||||||
// chainhash.Hash. It only differs from the one available in chainhash in that
|
|
||||||
// it panics on an error since it will only (and must only) be called with
|
|
||||||
// hard-coded, and therefore known good, hashes.
|
|
||||||
func newHashFromStr(hexStr string) *chainhash.Hash { |
|
||||||
hash, err := chainhash.NewHashFromStr(hexStr) |
|
||||||
if err != nil { |
|
||||||
// Ordinarily I don't like panics in library code since it
|
|
||||||
// can take applications down without them having a chance to
|
|
||||||
// recover which is extremely annoying, however an exception is
|
|
||||||
// being made in this case because the only way this can panic
|
|
||||||
// is if there is an error in the hard-coded hashes. Thus it
|
|
||||||
// will only ever potentially panic on init and therefore is
|
|
||||||
// 100% predictable.
|
|
||||||
panic(err) |
|
||||||
} |
|
||||||
return hash |
|
||||||
} |
|
||||||
@ -1,17 +0,0 @@ |
|||||||
# chainhash |
|
||||||
|
|
||||||
# [](http://copyfree.org) |
|
||||||
|
|
||||||
chainhash provides a generic hash type and associated functions that allows the |
|
||||||
specific hash algorithm to be abstracted. |
|
||||||
|
|
||||||
## Installation and Updating |
|
||||||
|
|
||||||
```bash |
|
||||||
$ go get -u mleku.online/git/ec/chainhash |
|
||||||
``` |
|
||||||
|
|
||||||
## License |
|
||||||
|
|
||||||
Package chainhash is licensed under the [copyfree](http://copyfree.org) ISC |
|
||||||
License. |
|
||||||
@ -1,5 +0,0 @@ |
|||||||
// Package chainhash provides abstracted hash functionality.
|
|
||||||
//
|
|
||||||
// This package provides a generic hash type and associated functions that
|
|
||||||
// allows the specific hash algorithm to be abstracted.
|
|
||||||
package chainhash |
|
||||||
@ -1,229 +0,0 @@ |
|||||||
// Copyright (c) 2013-2016 The btcsuite developers
|
|
||||||
// Copyright (c) 2015 The Decred developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package chainhash |
|
||||||
|
|
||||||
import ( |
|
||||||
"encoding/json" |
|
||||||
"fmt" |
|
||||||
|
|
||||||
"github.com/minio/sha256-simd" |
|
||||||
"next.orly.dev/pkg/encoders/hex" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
// HashSize of array used to store hashes. See Hash.
|
|
||||||
HashSize = 32 |
|
||||||
// MaxHashStringSize is the maximum length of a Hash hash string.
|
|
||||||
MaxHashStringSize = HashSize * 2 |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
// TagBIP0340Challenge is the BIP-0340 tag for challenges.
|
|
||||||
TagBIP0340Challenge = []byte("BIP0340/challenge") |
|
||||||
// TagBIP0340Aux is the BIP-0340 tag for aux data.
|
|
||||||
TagBIP0340Aux = []byte("BIP0340/aux") |
|
||||||
// TagBIP0340Nonce is the BIP-0340 tag for nonces.
|
|
||||||
TagBIP0340Nonce = []byte("BIP0340/nonce") |
|
||||||
// TagTapSighash is the tag used by BIP 341 to generate the sighash
|
|
||||||
// flags.
|
|
||||||
TagTapSighash = []byte("TapSighash") |
|
||||||
// TagTapLeaf is the message tag prefix used to compute the hash
|
|
||||||
// digest of a tapscript leaf.
|
|
||||||
TagTapLeaf = []byte("TapLeaf") |
|
||||||
// TagTapBranch is the message tag prefix used to compute the
|
|
||||||
// hash digest of two tap leaves into a taproot branch node.
|
|
||||||
TagTapBranch = []byte("TapBranch") |
|
||||||
// TagTapTweak is the message tag prefix used to compute the hash tweak
|
|
||||||
// used to enable a public key to commit to the taproot branch root
|
|
||||||
// for the witness program.
|
|
||||||
TagTapTweak = []byte("TapTweak") |
|
||||||
// precomputedTags is a map containing the SHA-256 hash of the BIP-0340
|
|
||||||
// tags.
|
|
||||||
precomputedTags = map[string]Hash{ |
|
||||||
string(TagBIP0340Challenge): sha256.Sum256(TagBIP0340Challenge), |
|
||||||
string(TagBIP0340Aux): sha256.Sum256(TagBIP0340Aux), |
|
||||||
string(TagBIP0340Nonce): sha256.Sum256(TagBIP0340Nonce), |
|
||||||
string(TagTapSighash): sha256.Sum256(TagTapSighash), |
|
||||||
string(TagTapLeaf): sha256.Sum256(TagTapLeaf), |
|
||||||
string(TagTapBranch): sha256.Sum256(TagTapBranch), |
|
||||||
string(TagTapTweak): sha256.Sum256(TagTapTweak), |
|
||||||
} |
|
||||||
) |
|
||||||
|
|
||||||
// ErrHashStrSize describes an error that indicates the caller specified a hash
|
|
||||||
// string that has too many characters.
|
|
||||||
var ErrHashStrSize = fmt.Errorf( |
|
||||||
"max hash string length is %v bytes", |
|
||||||
MaxHashStringSize, |
|
||||||
) |
|
||||||
|
|
||||||
// Hash is used in several of the bitcoin messages and common structures. It
|
|
||||||
// typically represents the double sha256 of data.
|
|
||||||
type Hash [HashSize]byte |
|
||||||
|
|
||||||
// String returns the Hash as the hexadecimal string of the byte-reversed
|
|
||||||
// hash.
|
|
||||||
func (hash Hash) String() string { |
|
||||||
for i := 0; i < HashSize/2; i++ { |
|
||||||
hash[i], hash[HashSize-1-i] = hash[HashSize-1-i], hash[i] |
|
||||||
} |
|
||||||
return hex.Enc(hash[:]) |
|
||||||
} |
|
||||||
|
|
||||||
// CloneBytes returns a copy of the bytes which represent the hash as a byte
|
|
||||||
// slice.
|
|
||||||
//
|
|
||||||
// NOTE: It is generally cheaper to just slice the hash directly thereby reusing
|
|
||||||
// the same bytes rather than calling this method.
|
|
||||||
func (hash *Hash) CloneBytes() []byte { |
|
||||||
newHash := make([]byte, HashSize) |
|
||||||
copy(newHash, hash[:]) |
|
||||||
return newHash |
|
||||||
} |
|
||||||
|
|
||||||
// SetBytes sets the bytes which represent the hash. An error is returned if
|
|
||||||
// the number of bytes passed in is not HashSize.
|
|
||||||
func (hash *Hash) SetBytes(newHash []byte) error { |
|
||||||
nhlen := len(newHash) |
|
||||||
if nhlen != HashSize { |
|
||||||
return fmt.Errorf( |
|
||||||
"invalid hash length of %v, want %v", nhlen, |
|
||||||
HashSize, |
|
||||||
) |
|
||||||
} |
|
||||||
copy(hash[:], newHash) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// IsEqual returns true if target is the same as hash.
|
|
||||||
func (hash *Hash) IsEqual(target *Hash) bool { |
|
||||||
if hash == nil && target == nil { |
|
||||||
return true |
|
||||||
} |
|
||||||
if hash == nil || target == nil { |
|
||||||
return false |
|
||||||
} |
|
||||||
return *hash == *target |
|
||||||
} |
|
||||||
|
|
||||||
// MarshalJSON serialises the hash as a JSON appropriate string value.
|
|
||||||
func (hash Hash) MarshalJSON() ([]byte, error) { |
|
||||||
return json.Marshal(hash.String()) |
|
||||||
} |
|
||||||
|
|
||||||
// UnmarshalJSON parses the hash with JSON appropriate string value.
|
|
||||||
func (hash *Hash) UnmarshalJSON(input []byte) error { |
|
||||||
// If the first byte indicates an array, the hash could have been marshalled
|
|
||||||
// using the legacy method and e.g. persisted.
|
|
||||||
if len(input) > 0 && input[0] == '[' { |
|
||||||
return decodeLegacy(hash, input) |
|
||||||
} |
|
||||||
var sh string |
|
||||||
err := json.Unmarshal(input, &sh) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
newHash, err := NewHashFromStr(sh) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
return hash.SetBytes(newHash[:]) |
|
||||||
} |
|
||||||
|
|
||||||
// NewHash returns a new Hash from a byte slice. An error is returned if
|
|
||||||
// the number of bytes passed in is not HashSize.
|
|
||||||
func NewHash(newHash []byte) (*Hash, error) { |
|
||||||
var sh Hash |
|
||||||
err := sh.SetBytes(newHash) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return &sh, err |
|
||||||
} |
|
||||||
|
|
||||||
// TaggedHash implements the tagged hash scheme described in BIP-340. We use
|
|
||||||
// sha-256 to bind a message hash to a specific context using a tag:
|
|
||||||
// sha256(sha256(tag) || sha256(tag) || msg).
|
|
||||||
func TaggedHash(tag []byte, msgs ...[]byte) *Hash { |
|
||||||
// Check to see if we've already pre-computed the hash of the tag. If
|
|
||||||
// so then this'll save us an extra sha256 hash.
|
|
||||||
shaTag, ok := precomputedTags[string(tag)] |
|
||||||
if !ok { |
|
||||||
shaTag = sha256.Sum256(tag) |
|
||||||
} |
|
||||||
// h = sha256(sha256(tag) || sha256(tag) || msg)
|
|
||||||
h := sha256.New() |
|
||||||
h.Write(shaTag[:]) |
|
||||||
h.Write(shaTag[:]) |
|
||||||
for _, msg := range msgs { |
|
||||||
h.Write(msg) |
|
||||||
} |
|
||||||
taggedHash := h.Sum(nil) |
|
||||||
// The function can't error out since the above hash is guaranteed to
|
|
||||||
// be 32 bytes.
|
|
||||||
hash, _ := NewHash(taggedHash) |
|
||||||
return hash |
|
||||||
} |
|
||||||
|
|
||||||
// NewHashFromStr creates a Hash from a hash string. The string should be
|
|
||||||
// the hexadecimal string of a byte-reversed hash, but any missing characters
|
|
||||||
// result in zero padding at the end of the Hash.
|
|
||||||
func NewHashFromStr(hash string) (*Hash, error) { |
|
||||||
ret := new(Hash) |
|
||||||
err := Decode(ret, hash) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
return ret, nil |
|
||||||
} |
|
||||||
|
|
||||||
// Decode decodes the byte-reversed hexadecimal string encoding of a Hash to a
|
|
||||||
// destination.
|
|
||||||
func Decode(dst *Hash, src string) error { |
|
||||||
// Return error if hash string is too long.
|
|
||||||
if len(src) > MaxHashStringSize { |
|
||||||
return ErrHashStrSize |
|
||||||
} |
|
||||||
// Hex decoder expects the hash to be a multiple of two. When not, pad
|
|
||||||
// with a leading zero.
|
|
||||||
var srcBytes []byte |
|
||||||
if len(src)%2 == 0 { |
|
||||||
srcBytes = []byte(src) |
|
||||||
} else { |
|
||||||
srcBytes = make([]byte, 1+len(src)) |
|
||||||
srcBytes[0] = '0' |
|
||||||
copy(srcBytes[1:], src) |
|
||||||
} |
|
||||||
// Hex decode the source bytes to a temporary destination.
|
|
||||||
var reversedHash Hash |
|
||||||
_, err := hex.DecAppend( |
|
||||||
reversedHash[HashSize-hex.DecLen(len(srcBytes)):], |
|
||||||
srcBytes, |
|
||||||
) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
// Reverse copy from the temporary hash to destination. Because the
|
|
||||||
// temporary was zeroed, the written result will be correctly padded.
|
|
||||||
for i, b := range reversedHash[:HashSize/2] { |
|
||||||
dst[i], dst[HashSize-1-i] = reversedHash[HashSize-1-i], b |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// decodeLegacy decodes an Hash that has been encoded with the legacy method
|
|
||||||
// (i.e. represented as a bytes array) to a destination.
|
|
||||||
func decodeLegacy(dst *Hash, src []byte) error { |
|
||||||
var hashBytes []byte |
|
||||||
err := json.Unmarshal(src, &hashBytes) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
if len(hashBytes) != HashSize { |
|
||||||
return ErrHashStrSize |
|
||||||
} |
|
||||||
return dst.SetBytes(hashBytes) |
|
||||||
} |
|
||||||
@ -1,228 +0,0 @@ |
|||||||
// Copyright (c) 2013-2016 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package chainhash |
|
||||||
|
|
||||||
import ( |
|
||||||
"testing" |
|
||||||
|
|
||||||
"next.orly.dev/pkg/utils" |
|
||||||
) |
|
||||||
|
|
||||||
// mainNetGenesisHash is the hash of the first block in the block chain for the
|
|
||||||
// main network (genesis block).
|
|
||||||
var mainNetGenesisHash = Hash( |
|
||||||
[HashSize]byte{ |
|
||||||
// Make go vet happy.
|
|
||||||
0x6f, 0xe2, 0x8c, 0x0a, 0xb6, 0xf1, 0xb3, 0x72, |
|
||||||
0xc1, 0xa6, 0xa2, 0x46, 0xae, 0x63, 0xf7, 0x4f, |
|
||||||
0x93, 0x1e, 0x83, 0x65, 0xe1, 0x5a, 0x08, 0x9c, |
|
||||||
0x68, 0xd6, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, |
|
||||||
}, |
|
||||||
) |
|
||||||
|
|
||||||
// TestHash tests the Hash API.
|
|
||||||
func TestHash(t *testing.T) { |
|
||||||
// Hash of block 234439.
|
|
||||||
blockHashStr := "14a0810ac680a3eb3f82edc878cea25ec41d6b790744e5daeef" |
|
||||||
blockHash, err := NewHashFromStr(blockHashStr) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("NewHashFromStr: %v", err) |
|
||||||
} |
|
||||||
// Hash of block 234440 as byte slice.
|
|
||||||
buf := []byte{ |
|
||||||
0x79, 0xa6, 0x1a, 0xdb, 0xc6, 0xe5, 0xa2, 0xe1, |
|
||||||
0x39, 0xd2, 0x71, 0x3a, 0x54, 0x6e, 0xc7, 0xc8, |
|
||||||
0x75, 0x63, 0x2e, 0x75, 0xf1, 0xdf, 0x9c, 0x3f, |
|
||||||
0xa6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
|
||||||
} |
|
||||||
hash, err := NewHash(buf) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("NewHash: unexpected error %v", err) |
|
||||||
} |
|
||||||
// Ensure proper size.
|
|
||||||
if len(hash) != HashSize { |
|
||||||
t.Errorf( |
|
||||||
"NewHash: hash length mismatch - got: %v, want: %v", |
|
||||||
len(hash), HashSize, |
|
||||||
) |
|
||||||
} |
|
||||||
// Ensure contents match.
|
|
||||||
if !utils.FastEqual(hash[:], buf) { |
|
||||||
t.Errorf( |
|
||||||
"NewHash: hash contents mismatch - got: %v, want: %v", |
|
||||||
hash[:], buf, |
|
||||||
) |
|
||||||
} |
|
||||||
// Ensure contents of hash of block 234440 don't match 234439.
|
|
||||||
if hash.IsEqual(blockHash) { |
|
||||||
t.Errorf( |
|
||||||
"IsEqual: hash contents should not match - got: %v, want: %v", |
|
||||||
hash, blockHash, |
|
||||||
) |
|
||||||
} |
|
||||||
// Set hash from byte slice and ensure contents match.
|
|
||||||
err = hash.SetBytes(blockHash.CloneBytes()) |
|
||||||
if err != nil { |
|
||||||
t.Errorf("SetBytes: %v", err) |
|
||||||
} |
|
||||||
if !hash.IsEqual(blockHash) { |
|
||||||
t.Errorf( |
|
||||||
"IsEqual: hash contents mismatch - got: %v, want: %v", |
|
||||||
hash, blockHash, |
|
||||||
) |
|
||||||
} |
|
||||||
// Ensure nil hashes are handled properly.
|
|
||||||
if !(*Hash)(nil).IsEqual(nil) { |
|
||||||
t.Error("IsEqual: nil hashes should match") |
|
||||||
} |
|
||||||
if hash.IsEqual(nil) { |
|
||||||
t.Error("IsEqual: non-nil hash matches nil hash") |
|
||||||
} |
|
||||||
// Invalid size for SetBytes.
|
|
||||||
err = hash.SetBytes([]byte{0x00}) |
|
||||||
if err == nil { |
|
||||||
t.Errorf("SetBytes: failed to received expected err - got: nil") |
|
||||||
} |
|
||||||
// Invalid size for NewHash.
|
|
||||||
invalidHash := make([]byte, HashSize+1) |
|
||||||
_, err = NewHash(invalidHash) |
|
||||||
if err == nil { |
|
||||||
t.Errorf("NewHash: failed to received expected err - got: nil") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// TestHashString tests the stringized output for hashes.
|
|
||||||
func TestHashString(t *testing.T) { |
|
||||||
// Block 100000 hash.
|
|
||||||
wantStr := "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506" |
|
||||||
hash := Hash( |
|
||||||
[HashSize]byte{ |
|
||||||
// Make go vet happy.
|
|
||||||
0x06, 0xe5, 0x33, 0xfd, 0x1a, 0xda, 0x86, 0x39, |
|
||||||
0x1f, 0x3f, 0x6c, 0x34, 0x32, 0x04, 0xb0, 0xd2, |
|
||||||
0x78, 0xd4, 0xaa, 0xec, 0x1c, 0x0b, 0x20, 0xaa, |
|
||||||
0x27, 0xba, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, |
|
||||||
}, |
|
||||||
) |
|
||||||
hashStr := hash.String() |
|
||||||
if hashStr != wantStr { |
|
||||||
t.Errorf( |
|
||||||
"String: wrong hash string - got %v, want %v", |
|
||||||
hashStr, wantStr, |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// todo: these fail for some reason
|
|
||||||
// // TestNewHashFromStr executes tests against the NewHashFromStr function.
|
|
||||||
// func TestNewHashFromStr(t *testing.T) {
|
|
||||||
// tests := []struct {
|
|
||||||
// in string
|
|
||||||
// want Hash
|
|
||||||
// err error
|
|
||||||
// }{
|
|
||||||
// // Genesis hash.
|
|
||||||
// {
|
|
||||||
// "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
|
|
||||||
// mainNetGenesisHash,
|
|
||||||
// nil,
|
|
||||||
// },
|
|
||||||
// // Genesis hash with stripped leading zeros.
|
|
||||||
// {
|
|
||||||
// "19d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
|
|
||||||
// mainNetGenesisHash,
|
|
||||||
// nil,
|
|
||||||
// },
|
|
||||||
// // Empty string.
|
|
||||||
// {
|
|
||||||
// "",
|
|
||||||
// Hash{},
|
|
||||||
// nil,
|
|
||||||
// },
|
|
||||||
// // Single digit hash.
|
|
||||||
// {
|
|
||||||
// "1",
|
|
||||||
// Hash([HashSize]byte{ // Make go vet happy.
|
|
||||||
// 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// }),
|
|
||||||
// nil,
|
|
||||||
// },
|
|
||||||
// // Block 203707 with stripped leading zeros.
|
|
||||||
// {
|
|
||||||
// "3264bc2ac36a60840790ba1d475d01367e7c723da941069e9dc",
|
|
||||||
// Hash([HashSize]byte{ // Make go vet happy.
|
|
||||||
// 0xdc, 0xe9, 0x69, 0x10, 0x94, 0xda, 0x23, 0xc7,
|
|
||||||
// 0xe7, 0x67, 0x13, 0xd0, 0x75, 0xd4, 0xa1, 0x0b,
|
|
||||||
// 0x79, 0x40, 0x08, 0xa6, 0x36, 0xac, 0xc2, 0x4b,
|
|
||||||
// 0x26, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// }),
|
|
||||||
// nil,
|
|
||||||
// },
|
|
||||||
// // Hash string that is too long.
|
|
||||||
// {
|
|
||||||
// "01234567890123456789012345678901234567890123456789012345678912345",
|
|
||||||
// Hash{},
|
|
||||||
// ErrHashStrSize,
|
|
||||||
// },
|
|
||||||
// // Hash string that is contains non-hex chars.
|
|
||||||
// {
|
|
||||||
// "abcdefg",
|
|
||||||
// Hash{},
|
|
||||||
// hex.InvalidByteError('g'),
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// unexpectedErrStr := "NewHashFromStr #%d failed to detect expected error - got: %v want: %v"
|
|
||||||
// unexpectedResultStr := "NewHashFromStr #%d got: %v want: %v"
|
|
||||||
// t.Logf("Running %d tests", len(tests))
|
|
||||||
// for i, test := range tests {
|
|
||||||
// result, err := NewHashFromStr(test.in)
|
|
||||||
// if err != test.err {
|
|
||||||
// t.Errorf(unexpectedErrStr, i, err, test.err)
|
|
||||||
// continue
|
|
||||||
// } else if err != nil {
|
|
||||||
// // Got expected error. Move on to the next test.
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
// if !test.want.IsEqual(result) {
|
|
||||||
// t.Errorf(unexpectedResultStr, i, result, &test.want)
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // TestHashJsonMarshal tests json marshal and unmarshal.
|
|
||||||
// func TestHashJsonMarshal(t *testing.T) {
|
|
||||||
// hashStr := "000000000003ba27aa200b1cecaad478d2b00432346c3f1f3986da1afd33e506"
|
|
||||||
// legacyHashStr := []byte("[6,229,51,253,26,218,134,57,31,63,108,52,50,4,176,210,120,212,170,236,28,11,32,170,39,186,3,0,0,0,0,0]")
|
|
||||||
// hash, err := NewHashFromStr(hashStr)
|
|
||||||
// if err != nil {
|
|
||||||
// t.Errorf("NewHashFromStr error:%v, hashStr:%s", err, hashStr)
|
|
||||||
// }
|
|
||||||
// hashBytes, err := json.Marshal(hash)
|
|
||||||
// if err != nil {
|
|
||||||
// t.Errorf("Marshal json error:%v, hash:%v", err, hashBytes)
|
|
||||||
// }
|
|
||||||
// var newHash Hash
|
|
||||||
// err = json.Unmarshal(hashBytes, &newHash)
|
|
||||||
// if err != nil {
|
|
||||||
// t.Errorf("Unmarshal json error:%v, hash:%v", err, hashBytes)
|
|
||||||
// }
|
|
||||||
// if !hash.IsEqual(&newHash) {
|
|
||||||
// t.Errorf("String: wrong hash string - got %v, want %v",
|
|
||||||
// newHash.String(), hashStr)
|
|
||||||
// }
|
|
||||||
// err = newHash.Unmarshal(legacyHashStr)
|
|
||||||
// if err != nil {
|
|
||||||
// t.Errorf("Unmarshal legacy json error:%v, hash:%v", err, legacyHashStr)
|
|
||||||
// }
|
|
||||||
// if !hash.IsEqual(&newHash) {
|
|
||||||
// t.Errorf("String: wrong hash string - got %v, want %v",
|
|
||||||
// newHash.String(), hashStr)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
@ -1,33 +0,0 @@ |
|||||||
// Copyright (c) 2015 The Decred developers
|
|
||||||
// Copyright (c) 2016-2017 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package chainhash |
|
||||||
|
|
||||||
import ( |
|
||||||
"github.com/minio/sha256-simd" |
|
||||||
) |
|
||||||
|
|
||||||
// HashB calculates hash(b) and returns the resulting bytes.
|
|
||||||
func HashB(b []byte) []byte { |
|
||||||
hash := sha256.Sum256(b) |
|
||||||
return hash[:] |
|
||||||
} |
|
||||||
|
|
||||||
// HashH calculates hash(b) and returns the resulting bytes as a Hash.
|
|
||||||
func HashH(b []byte) Hash { return Hash(sha256.Sum256(b)) } |
|
||||||
|
|
||||||
// DoubleHashB calculates hash(hash(b)) and returns the resulting bytes.
|
|
||||||
func DoubleHashB(b []byte) []byte { |
|
||||||
first := sha256.Sum256(b) |
|
||||||
second := sha256.Sum256(first[:]) |
|
||||||
return second[:] |
|
||||||
} |
|
||||||
|
|
||||||
// DoubleHashH calculates hash(hash(b)) and returns the resulting bytes as a
|
|
||||||
// Hash.
|
|
||||||
func DoubleHashH(b []byte) Hash { |
|
||||||
first := sha256.Sum256(b) |
|
||||||
return sha256.Sum256(first[:]) |
|
||||||
} |
|
||||||
@ -1,323 +0,0 @@ |
|||||||
// Copyright (c) 2016 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package chainhash |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
"testing" |
|
||||||
) |
|
||||||
|
|
||||||
// TestHashFuncs ensures the hash functions which perform hash(b) work as
|
|
||||||
// expected.
|
|
||||||
func TestHashFuncs(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
out string |
|
||||||
in string |
|
||||||
}{ |
|
||||||
{ |
|
||||||
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", |
|
||||||
"", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb", |
|
||||||
"a", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603", |
|
||||||
"ab", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", |
|
||||||
"abc", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"88d4266fd4e6338d13b845fcf289579d209c897823b9217da3e161936f031589", |
|
||||||
"abcd", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"36bbe50ed96841d10443bcb670d6554f0a34b761be67ec9c4a8ad2c0c44ca42c", |
|
||||||
"abcde", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"bef57ec7f53a6d40beb640a780a639c83bc29ac8a9816f1fc6c5c6dcd93c4721", |
|
||||||
"abcdef", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"7d1a54127b222502f5b79b5fb0803061152a44f92b37e23c6527baf665d4da9a", |
|
||||||
"abcdefg", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"9c56cc51b374c3ba189210d5b6d4bf57790d351c96c47c02190ecf1e430635ab", |
|
||||||
"abcdefgh", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"19cc02f26df43cc571bc9ed7b0c4d29224a3ec229529221725ef76d021c8326f", |
|
||||||
"abcdefghi", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"72399361da6a7754fec986dca5b7cbaf1c810a28ded4abaf56b2106d06cb78b0", |
|
||||||
"abcdefghij", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"a144061c271f152da4d151034508fed1c138b8c976339de229c3bb6d4bbb4fce", |
|
||||||
"Discard medicine more than two years old.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"6dae5caa713a10ad04b46028bf6dad68837c581616a1589a265a11288d4bb5c4", |
|
||||||
"He who has a shady past knows that nice guys finish last.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"ae7a702a9509039ddbf29f0765e70d0001177914b86459284dab8b348c2dce3f", |
|
||||||
"I wouldn't marry him with a ten foot pole.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"6748450b01c568586715291dfa3ee018da07d36bb7ea6f180c1af6270215c64f", |
|
||||||
"Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"14b82014ad2b11f661b5ae6a99b75105c2ffac278cd071cd6c05832793635774", |
|
||||||
"The days of the digital watch are numbered. -Tom Stoppard", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"7102cfd76e2e324889eece5d6c41921b1e142a4ac5a2692be78803097f6a48d8", |
|
||||||
"Nepal premier won't resign.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"23b1018cd81db1d67983c5f7417c44da9deb582459e378d7a068552ea649dc9f", |
|
||||||
"For every action there is an equal and opposite government program.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"8001f190dfb527261c4cfcab70c98e8097a7a1922129bc4096950e57c7999a5a", |
|
||||||
"His money is twice tainted: 'taint yours and 'taint mine.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"8c87deb65505c3993eb24b7a150c4155e82eee6960cf0c3a8114ff736d69cad5", |
|
||||||
"There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"bfb0a67a19cdec3646498b2e0f751bddc41bba4b7f30081b0b932aad214d16d7", |
|
||||||
"It's a tiny change to the code and not completely disgusting. - Bob Manchek", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"7f9a0b9bf56332e19f5a0ec1ad9c1425a153da1c624868fda44561d6b74daf36", |
|
||||||
"size: a.out: bad magic", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"b13f81b8aad9e3666879af19886140904f7f429ef083286195982a7588858cfc", |
|
||||||
"The major problem is with sendmail. -Mark Horton", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"b26c38d61519e894480c70c8374ea35aa0ad05b2ae3d6674eec5f52a69305ed4", |
|
||||||
"Give me a rock, paper and scissors and I will move the world. CCFestoon", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"049d5e26d4f10222cd841a119e38bd8d2e0d1129728688449575d4ff42b842c1", |
|
||||||
"If the enemy is within range, then so are you.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"0e116838e3cc1c1a14cd045397e29b4d087aa11b0853fc69ec82e90330d60949", |
|
||||||
"It's well we cannot hear the screams/That we create in others' dreams.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"4f7d8eb5bcf11de2a56b971021a444aa4eafd6ecd0f307b5109e4e776cd0fe46", |
|
||||||
"You remind me of a TV show, but that's all right: I watch it anyway.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"61c0cc4c4bd8406d5120b3fb4ebc31ce87667c162f29468b3c779675a85aebce", |
|
||||||
"C is as portable as Stonehedge!!", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"1fb2eb3688093c4a3f80cd87a5547e2ce940a4f923243a79a2a1e242220693ac", |
|
||||||
"Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"395585ce30617b62c80b93e8208ce866d4edc811a177fdb4b82d3911d8696423", |
|
||||||
"The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"4f9b189a13d030838269dce846b16a1ce9ce81fe63e65de2f636863336a98fe6", |
|
||||||
"How can you write a big system without C++? -Paul Glick", |
|
||||||
}, |
|
||||||
} |
|
||||||
|
|
||||||
// Ensure the hash function which returns a byte slice returns the
|
|
||||||
// expected result.
|
|
||||||
for _, test := range tests { |
|
||||||
h := fmt.Sprintf("%x", HashB([]byte(test.in))) |
|
||||||
if h != test.out { |
|
||||||
t.Errorf("HashB(%q) = %s, want %s", test.in, h, test.out) |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
// Ensure the hash function which returns a Hash returns the expected
|
|
||||||
// result.
|
|
||||||
for _, test := range tests { |
|
||||||
hash := HashH([]byte(test.in)) |
|
||||||
h := fmt.Sprintf("%x", hash[:]) |
|
||||||
if h != test.out { |
|
||||||
t.Errorf("HashH(%q) = %s, want %s", test.in, h, test.out) |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// TestDoubleHashFuncs ensures the hash functions which perform hash(hash(b))
|
|
||||||
// work as expected.
|
|
||||||
func TestDoubleHashFuncs(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
out string |
|
||||||
in string |
|
||||||
}{ |
|
||||||
{ |
|
||||||
"5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456", |
|
||||||
"", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"bf5d3affb73efd2ec6c36ad3112dd933efed63c4e1cbffcfa88e2759c144f2d8", |
|
||||||
"a", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"a1ff8f1856b5e24e32e3882edd4a021f48f28a8b21854b77fdef25a97601aace", |
|
||||||
"ab", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"4f8b42c22dd3729b519ba6f68d2da7cc5b2d606d05daed5ad5128cc03e6c6358", |
|
||||||
"abc", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"7e9c158ecd919fa439a7a214c9fc58b85c3177fb1613bdae41ee695060e11bc6", |
|
||||||
"abcd", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"1d72b6eb7ba8b9709c790b33b40d8c46211958e13cf85dbcda0ed201a99f2fb9", |
|
||||||
"abcde", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"ce65d4756128f0035cba4d8d7fae4e9fa93cf7fdf12c0f83ee4a0e84064bef8a", |
|
||||||
"abcdef", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"dad6b965ad86b880ceb6993f98ebeeb242de39f6b87a458c6510b5a15ff7bbf1", |
|
||||||
"abcdefg", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"b9b12e7125f73fda20b8c4161fb9b4b146c34cf88595a1e0503ca2cf44c86bc4", |
|
||||||
"abcdefgh", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"546db09160636e98405fbec8464a84b6464b32514db259e235eae0445346ffb7", |
|
||||||
"abcdefghi", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"27635cf23fdf8a10f4cb2c52ade13038c38718c6d7ca716bfe726111a57ad201", |
|
||||||
"abcdefghij", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"ae0d8e0e7c0336f0c3a72cefa4f24b625a6a460417a921d066058a0b81e23429", |
|
||||||
"Discard medicine more than two years old.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"eeb56d02cf638f87ea8f11ebd5b0201afcece984d87be458578d3cfb51978f1b", |
|
||||||
"He who has a shady past knows that nice guys finish last.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"dc640bf529608a381ea7065ecbcd0443b95f6e4c008de6e134aff1d36bd4b9d8", |
|
||||||
"I wouldn't marry him with a ten foot pole.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"42e54375e60535eb07fc15c6350e10f2c22526f84db1d6f6bba925e154486f33", |
|
||||||
"Free! Free!/A trip/to Mars/for 900/empty jars/Burma Shave", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"4ed6aa9b88c84afbf928710b03714de69e2ad967c6a78586069adcb4c470d150", |
|
||||||
"The days of the digital watch are numbered. -Tom Stoppard", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"590c24d1877c1919fad12fe01a8796999e9d20cfbf9bc9bc72fa0bd69f0b04dd", |
|
||||||
"Nepal premier won't resign.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"37d270687ee8ebafcd3c1a32f56e1e1304b3c93f252cb637d57a66d59c475eca", |
|
||||||
"For every action there is an equal and opposite government program.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"306828fd89278838bb1c544c3032a1fd25ea65c40bba586437568828a5fbe944", |
|
||||||
"His money is twice tainted: 'taint yours and 'taint mine.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"49965777eac71faf1e2fb0f6b239ba2fae770977940fd827bcbfe15def6ded53", |
|
||||||
"There is no reason for any individual to have a computer in their home. -Ken Olsen, 1977", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"df99ee4e87dd3fb07922dee7735997bbae8f26db20c86137d4219fc4a37b77c3", |
|
||||||
"It's a tiny change to the code and not completely disgusting. - Bob Manchek", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"920667c84a15b5ee3df4620169f5c0ec930cea0c580858e50e68848871ed65b4", |
|
||||||
"size: a.out: bad magic", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"5e817fe20848a4a3932db68e90f8d54ec1b09603f0c99fdc051892b776acd462", |
|
||||||
"The major problem is with sendmail. -Mark Horton", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"6a9d47248ed38852f5f4b2e37e7dfad0ce8d1da86b280feef94ef267e468cff2", |
|
||||||
"Give me a rock, paper and scissors and I will move the world. CCFestoon", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"2e7aa1b362c94efdbff582a8bd3f7f61c8ce4c25bbde658ef1a7ae1010e2126f", |
|
||||||
"If the enemy is within range, then so are you.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"e6729d51240b1e1da76d822fd0c55c75e409bcb525674af21acae1f11667c8ca", |
|
||||||
"It's well we cannot hear the screams/That we create in others' dreams.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"09945e4d2743eb669f85e4097aa1cc39ea680a0b2ae2a65a42a5742b3b809610", |
|
||||||
"You remind me of a TV show, but that's all right: I watch it anyway.", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"1018d8b2870a974887c5174360f0fbaf27958eef15b24522a605c5dae4ae0845", |
|
||||||
"C is as portable as Stonehedge!!", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"97c76b83c6645c78c261dcdc55d44af02d9f1df8057f997fd08c310c903624d5", |
|
||||||
"Even if I could be Shakespeare, I think I should still choose to be Faraday. - A. Huxley", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"6bcbf25469e9544c5b5806b24220554fedb6695ba9b1510a76837414f7adb113", |
|
||||||
"The fugacity of a constituent in a mixture of gases at a given temperature is proportional to its mole fraction. Lewis-Randall Rule", |
|
||||||
}, |
|
||||||
{ |
|
||||||
"1041988b06835481f0845be2a54f4628e1da26145b2de7ad1be3bb643cef9d4f", |
|
||||||
"How can you write a big system without C++? -Paul Glick", |
|
||||||
}, |
|
||||||
} |
|
||||||
// Ensure the hash function which returns a byte slice returns the
|
|
||||||
// expected result.
|
|
||||||
for _, test := range tests { |
|
||||||
h := fmt.Sprintf("%x", DoubleHashB([]byte(test.in))) |
|
||||||
if h != test.out { |
|
||||||
t.Errorf( |
|
||||||
"DoubleHashB(%q) = %s, want %s", test.in, h, |
|
||||||
test.out, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
// Ensure the hash function which returns a Hash returns the expected
|
|
||||||
// result.
|
|
||||||
for _, test := range tests { |
|
||||||
hash := DoubleHashH([]byte(test.in)) |
|
||||||
h := fmt.Sprintf("%x", hash[:]) |
|
||||||
if h != test.out { |
|
||||||
t.Errorf( |
|
||||||
"DoubleHashH(%q) = %s, want %s", test.in, h, |
|
||||||
test.out, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,16 +0,0 @@ |
|||||||
// Copyright (c) 2015-2016 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcec |
|
||||||
|
|
||||||
import ( |
|
||||||
"next.orly.dev/pkg/crypto/ec/secp256k1" |
|
||||||
) |
|
||||||
|
|
||||||
// GenerateSharedSecret generates a shared secret based on a secret key and a
|
|
||||||
// public key using Diffie-Hellman key exchange (ECDH) (RFC 4753).
|
|
||||||
// RFC5903 Section 9 states we should only return x.
|
|
||||||
func GenerateSharedSecret(privkey *SecretKey, pubkey *PublicKey) []byte { |
|
||||||
return secp256k1.GenerateSharedSecret(privkey, pubkey) |
|
||||||
} |
|
||||||
@ -1,32 +0,0 @@ |
|||||||
// Copyright (c) 2015-2016 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package btcec |
|
||||||
|
|
||||||
import ( |
|
||||||
"testing" |
|
||||||
|
|
||||||
"next.orly.dev/pkg/utils" |
|
||||||
) |
|
||||||
|
|
||||||
func TestGenerateSharedSecret(t *testing.T) { |
|
||||||
privKey1, err := NewSecretKey() |
|
||||||
if err != nil { |
|
||||||
t.Errorf("secret key generation error: %s", err) |
|
||||||
return |
|
||||||
} |
|
||||||
privKey2, err := NewSecretKey() |
|
||||||
if err != nil { |
|
||||||
t.Errorf("secret key generation error: %s", err) |
|
||||||
return |
|
||||||
} |
|
||||||
secret1 := GenerateSharedSecret(privKey1, privKey2.PubKey()) |
|
||||||
secret2 := GenerateSharedSecret(privKey2, privKey1.PubKey()) |
|
||||||
if !utils.FastEqual(secret1, secret2) { |
|
||||||
t.Errorf( |
|
||||||
"ECDH failed, secrets mismatch - first: %x, second: %x", |
|
||||||
secret1, secret2, |
|
||||||
) |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,111 +0,0 @@ |
|||||||
// Copyright (c) 2015-2021 The btcsuite developers
|
|
||||||
// Copyright (c) 2015-2021 The Decred developers
|
|
||||||
|
|
||||||
package btcec |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
|
|
||||||
"next.orly.dev/pkg/crypto/ec/secp256k1" |
|
||||||
) |
|
||||||
|
|
||||||
// JacobianPoint is an element of the group formed by the secp256k1 curve in
|
|
||||||
// Jacobian projective coordinates and thus represents a point on the curve.
|
|
||||||
type JacobianPoint = secp256k1.JacobianPoint |
|
||||||
|
|
||||||
// infinityPoint is the jacobian representation of the point at infinity.
|
|
||||||
var infinityPoint JacobianPoint |
|
||||||
|
|
||||||
// MakeJacobianPoint returns a Jacobian point with the provided X, Y, and Z
|
|
||||||
// coordinates.
|
|
||||||
func MakeJacobianPoint(x, y, z *FieldVal) JacobianPoint { |
|
||||||
return secp256k1.MakeJacobianPoint(x, y, z) |
|
||||||
} |
|
||||||
|
|
||||||
// AddNonConst adds the passed Jacobian points together and stores the result
|
|
||||||
// in the provided result param in *non-constant* time.
|
|
||||||
func AddNonConst(p1, p2, result *JacobianPoint) { |
|
||||||
secp256k1.AddNonConst(p1, p2, result) |
|
||||||
} |
|
||||||
|
|
||||||
// DecompressY attempts to calculate the Y coordinate for the given X
|
|
||||||
// coordinate such that the result pair is a point on the secp256k1 curve. It
|
|
||||||
// adjusts Y based on the desired oddness and returns whether or not it was
|
|
||||||
// successful since not all X coordinates are valid.
|
|
||||||
//
|
|
||||||
// The magnitude of the provided X coordinate field val must be a max of 8 for
|
|
||||||
// a correct result. The resulting Y field val will have a max magnitude of 2.
|
|
||||||
func DecompressY(x *FieldVal, odd bool, resultY *FieldVal) bool { |
|
||||||
return secp256k1.DecompressY(x, odd, resultY) |
|
||||||
} |
|
||||||
|
|
||||||
// DoubleNonConst doubles the passed Jacobian point and stores the result in
|
|
||||||
// the provided result parameter in *non-constant* time.
|
|
||||||
//
|
|
||||||
// NOTE: The point must be normalized for this function to return the correct
|
|
||||||
// result. The resulting point will be normalized.
|
|
||||||
func DoubleNonConst(p, result *JacobianPoint) { |
|
||||||
secp256k1.DoubleNonConst(p, result) |
|
||||||
} |
|
||||||
|
|
||||||
// ScalarBaseMultNonConst multiplies k*G where G is the base point of the group
|
|
||||||
// and k is a big endian integer. The result is stored in Jacobian coordinates
|
|
||||||
// (x1, y1, z1).
|
|
||||||
//
|
|
||||||
// NOTE: The resulting point will be normalized.
|
|
||||||
func ScalarBaseMultNonConst(k *ModNScalar, result *JacobianPoint) { |
|
||||||
secp256k1.ScalarBaseMultNonConst(k, result) |
|
||||||
} |
|
||||||
|
|
||||||
// ScalarMultNonConst multiplies k*P where k is a big endian integer modulo the
|
|
||||||
// curve order and P is a point in Jacobian projective coordinates and stores
|
|
||||||
// the result in the provided Jacobian point.
|
|
||||||
//
|
|
||||||
// NOTE: The point must be normalized for this function to return the correct
|
|
||||||
// result. The resulting point will be normalized.
|
|
||||||
func ScalarMultNonConst(k *ModNScalar, point, result *JacobianPoint) { |
|
||||||
secp256k1.ScalarMultNonConst(k, point, result) |
|
||||||
} |
|
||||||
|
|
||||||
// ParseJacobian parses a byte slice point as a secp256k1.Publickey and returns the
|
|
||||||
// pubkey as a JacobianPoint. If the nonce is a zero slice, the infinityPoint
|
|
||||||
// is returned.
|
|
||||||
func ParseJacobian(point []byte) (JacobianPoint, error) { |
|
||||||
var result JacobianPoint |
|
||||||
if len(point) != 33 { |
|
||||||
str := fmt.Sprintf( |
|
||||||
"invalid nonce: invalid length: %v", |
|
||||||
len(point), |
|
||||||
) |
|
||||||
return JacobianPoint{}, makeError(secp256k1.ErrPubKeyInvalidLen, str) |
|
||||||
} |
|
||||||
if point[0] == 0x00 { |
|
||||||
return infinityPoint, nil |
|
||||||
} |
|
||||||
noncePk, err := secp256k1.ParsePubKey(point) |
|
||||||
if err != nil { |
|
||||||
return JacobianPoint{}, err |
|
||||||
} |
|
||||||
noncePk.AsJacobian(&result) |
|
||||||
return result, nil |
|
||||||
} |
|
||||||
|
|
||||||
// JacobianToByteSlice converts the passed JacobianPoint to a Pubkey
|
|
||||||
// and serializes that to a byte slice. If the JacobianPoint is the infinity
|
|
||||||
// point, a zero slice is returned.
|
|
||||||
func JacobianToByteSlice(point JacobianPoint) []byte { |
|
||||||
if point.X == infinityPoint.X && point.Y == infinityPoint.Y { |
|
||||||
return make([]byte, 33) |
|
||||||
} |
|
||||||
point.ToAffine() |
|
||||||
return NewPublicKey( |
|
||||||
&point.X, &point.Y, |
|
||||||
).SerializeCompressed() |
|
||||||
} |
|
||||||
|
|
||||||
// GeneratorJacobian sets the passed JacobianPoint to the Generator Point.
|
|
||||||
func GeneratorJacobian(jacobian *JacobianPoint) { |
|
||||||
var k ModNScalar |
|
||||||
k.SetInt(1) |
|
||||||
ScalarBaseMultNonConst(&k, jacobian) |
|
||||||
} |
|
||||||
@ -1,19 +0,0 @@ |
|||||||
// Copyright (c) 2013-2014 The btcsuite developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package btcec implements support for the elliptic curves needed for bitcoin.
|
|
||||||
//
|
|
||||||
// Bitcoin uses elliptic curve cryptography using koblitz curves
|
|
||||||
// (specifically secp256k1) for cryptographic functions. See
|
|
||||||
// http://www.secg.org/collateral/sec2_final.pdf for details on the
|
|
||||||
// standard.
|
|
||||||
//
|
|
||||||
// This package provides the data structures and functions implementing the
|
|
||||||
// crypto/elliptic Curve interface in order to permit using these curves
|
|
||||||
// with the standard crypto/ecdsa package provided with go. Helper
|
|
||||||
// functionality is provided to parse signatures and public keys from
|
|
||||||
// standard formats. It was designed for use with btcd, but should be
|
|
||||||
// general enough for other uses of elliptic curve crypto. It was originally based
|
|
||||||
// on some initial work by ThePiachu, but has significantly diverged since then.
|
|
||||||
package btcec |
|
||||||
@ -1,27 +0,0 @@ |
|||||||
# ecdsa |
|
||||||
|
|
||||||
[](http://copyfree.org) |
|
||||||
[](https://pkg.go.dev/mleku.online/git/ec/secp/ecdsa) |
|
||||||
|
|
||||||
Package ecdsa provides secp256k1-optimized ECDSA signing and verification. |
|
||||||
|
|
||||||
This package provides data structures and functions necessary to produce and |
|
||||||
verify deterministic canonical signatures in accordance with RFC6979 and |
|
||||||
BIP0062, optimized specifically for the secp256k1 curve using the Elliptic Curve |
|
||||||
Digital Signature Algorithm (ECDSA), as defined in FIPS 186-3. See |
|
||||||
https://www.secg.org/sec2-v2.pdf (also found here |
|
||||||
at [sec2-v2.pdf](../sec2-v2.pdf)) for details on the secp256k1 standard. |
|
||||||
|
|
||||||
It also provides functions to parse and serialize the ECDSA signatures with the |
|
||||||
more strict Distinguished Encoding Rules (DER) of ISO/IEC 8825-1 and some |
|
||||||
additional restrictions specific to secp256k1. |
|
||||||
|
|
||||||
In addition, it supports a custom "compact" signature format which allows |
|
||||||
efficient recovery of the public key from a given valid signature and message |
|
||||||
hash combination. |
|
||||||
|
|
||||||
A comprehensive suite of tests is provided to ensure proper functionality. |
|
||||||
|
|
||||||
## License |
|
||||||
|
|
||||||
Package ecdsa is licensed under the [copyfree](http://copyfree.org) ISC License. |
|
||||||
@ -1,169 +0,0 @@ |
|||||||
// Copyright 2013-2016 The btcsuite developers
|
|
||||||
// Copyright (c) 2015-2022 The Decred developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package ecdsa |
|
||||||
|
|
||||||
import ( |
|
||||||
"testing" |
|
||||||
|
|
||||||
"next.orly.dev/pkg/crypto/ec/secp256k1" |
|
||||||
"next.orly.dev/pkg/encoders/hex" |
|
||||||
) |
|
||||||
|
|
||||||
// hexToModNScalar converts the passed hex string into a ModNScalar and will
|
|
||||||
// panic if there is an error. This is only provided for the hard-coded
|
|
||||||
// constants so errors in the source code can be detected. It will only (and
|
|
||||||
// must only) be called with hard-coded values.
|
|
||||||
func hexToModNScalar(s string) *secp256k1.ModNScalar { |
|
||||||
b, err := hex.Dec(s) |
|
||||||
if err != nil { |
|
||||||
panic("invalid hex in source file: " + s) |
|
||||||
} |
|
||||||
var scalar secp256k1.ModNScalar |
|
||||||
if overflow := scalar.SetByteSlice(b); overflow { |
|
||||||
panic("hex in source file overflows mod N scalar: " + s) |
|
||||||
} |
|
||||||
return &scalar |
|
||||||
} |
|
||||||
|
|
||||||
// hexToFieldVal converts the passed hex string into a FieldVal and will panic
|
|
||||||
// if there is an error. This is only provided for the hard-coded constants so
|
|
||||||
// errors in the source code can be detected. It will only (and must only) be
|
|
||||||
// called with hard-coded values.
|
|
||||||
func hexToFieldVal(s string) *secp256k1.FieldVal { |
|
||||||
b, err := hex.Dec(s) |
|
||||||
if err != nil { |
|
||||||
panic("invalid hex in source file: " + s) |
|
||||||
} |
|
||||||
var f secp256k1.FieldVal |
|
||||||
if overflow := f.SetByteSlice(b); overflow { |
|
||||||
panic("hex in source file overflows mod P: " + s) |
|
||||||
} |
|
||||||
return &f |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkSigVerify benchmarks how long it takes the secp256k1 curve to
|
|
||||||
// verify signatures.
|
|
||||||
func BenchmarkSigVerify(b *testing.B) { |
|
||||||
// Randomly generated keypair.
|
|
||||||
// Secret key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d
|
|
||||||
pubKey := secp256k1.NewPublicKey( |
|
||||||
hexToFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"), |
|
||||||
hexToFieldVal("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"), |
|
||||||
) |
|
||||||
// Double sha256 of by{0x01, 0x02, 0x03, 0x04}
|
|
||||||
msgHash := hexToBytes("8de472e2399610baaa7f84840547cd409434e31f5d3bd71e4d947f283874f9c0") |
|
||||||
sig := NewSignature( |
|
||||||
hexToModNScalar("fef45d2892953aa5bbcdb057b5e98b208f1617a7498af7eb765574e29b5d9c2c"), |
|
||||||
hexToModNScalar("d47563f52aac6b04b55de236b7c515eb9311757db01e02cff079c3ca6efb063f"), |
|
||||||
) |
|
||||||
if !sig.Verify(msgHash, pubKey) { |
|
||||||
b.Errorf("Signature failed to verify") |
|
||||||
return |
|
||||||
} |
|
||||||
b.ReportAllocs() |
|
||||||
b.ResetTimer() |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
sig.Verify(msgHash, pubKey) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkSign benchmarks how long it takes to sign a message.
|
|
||||||
func BenchmarkSign(b *testing.B) { |
|
||||||
// Randomly generated keypair.
|
|
||||||
d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") |
|
||||||
secKey := secp256k1.NewSecretKey(d) |
|
||||||
// blake256 of by{0x01, 0x02, 0x03, 0x04}.
|
|
||||||
msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7") |
|
||||||
b.ReportAllocs() |
|
||||||
b.ResetTimer() |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
signRFC6979(secKey, msgHash) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkSigSerialize benchmarks how long it takes to serialize a typical
|
|
||||||
// signature with the strict DER encoding.
|
|
||||||
func BenchmarkSigSerialize(b *testing.B) { |
|
||||||
// Randomly generated keypair.
|
|
||||||
// Secret key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d
|
|
||||||
// Signature for double sha256 of by{0x01, 0x02, 0x03, 0x04}.
|
|
||||||
sig := NewSignature( |
|
||||||
hexToModNScalar("fef45d2892953aa5bbcdb057b5e98b208f1617a7498af7eb765574e29b5d9c2c"), |
|
||||||
hexToModNScalar("d47563f52aac6b04b55de236b7c515eb9311757db01e02cff079c3ca6efb063f"), |
|
||||||
) |
|
||||||
b.ReportAllocs() |
|
||||||
b.ResetTimer() |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
sig.Serialize() |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkNonceRFC6979 benchmarks how long it takes to generate a
|
|
||||||
// deterministic nonce according to RFC6979.
|
|
||||||
func BenchmarkNonceRFC6979(b *testing.B) { |
|
||||||
// Randomly generated keypair.
|
|
||||||
// Secret key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d
|
|
||||||
// X: d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab
|
|
||||||
// Y: ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52
|
|
||||||
secKeyStr := "9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d" |
|
||||||
secKey := hexToBytes(secKeyStr) |
|
||||||
// BLAKE-256 of by{0x01, 0x02, 0x03, 0x04}.
|
|
||||||
msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7") |
|
||||||
b.ReportAllocs() |
|
||||||
b.ResetTimer() |
|
||||||
var noElideNonce *secp256k1.ModNScalar |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
noElideNonce = secp256k1.NonceRFC6979(secKey, msgHash, nil, nil, 0) |
|
||||||
} |
|
||||||
_ = noElideNonce |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkSignCompact benchmarks how long it takes to produce a compact
|
|
||||||
// signature for a message.
|
|
||||||
func BenchmarkSignCompact(b *testing.B) { |
|
||||||
d := hexToModNScalar("9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d") |
|
||||||
secKey := secp256k1.NewSecretKey(d) |
|
||||||
// blake256 of by{0x01, 0x02, 0x03, 0x04}.
|
|
||||||
msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7") |
|
||||||
b.ReportAllocs() |
|
||||||
b.ResetTimer() |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
_ = SignCompact(secKey, msgHash, true) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// BenchmarkRecoverCompact benchmarks how long it takes to recover a public key
|
|
||||||
// given a compact signature and message.
|
|
||||||
func BenchmarkRecoverCompact(b *testing.B) { |
|
||||||
// Secret key: 9e0699c91ca1e3b7e3c9ba71eb71c89890872be97576010fe593fbf3fd57e66d
|
|
||||||
wantPubKey := secp256k1.NewPublicKey( |
|
||||||
hexToFieldVal("d2e670a19c6d753d1a6d8b20bd045df8a08fb162cf508956c31268c6d81ffdab"), |
|
||||||
hexToFieldVal("ab65528eefbb8057aa85d597258a3fbd481a24633bc9b47a9aa045c91371de52"), |
|
||||||
) |
|
||||||
compactSig := hexToBytes( |
|
||||||
"205978b7896bc71676ba2e459882a8f52e1299449596c4f" + |
|
||||||
"93c59bf1fbfa2f9d3b76ecd0c99406f61a6de2bb5a8937c061c176ecf381d0231e0d" + |
|
||||||
"af73b922c8952c7", |
|
||||||
) |
|
||||||
// blake256 of by{0x01, 0x02, 0x03, 0x04}.
|
|
||||||
msgHash := hexToBytes("c301ba9de5d6053caad9f5eb46523f007702add2c62fa39de03146a36b8026b7") |
|
||||||
// Ensure a valid compact signature is being benchmarked.
|
|
||||||
pubKey, wasCompressed, err := RecoverCompact(compactSig, msgHash) |
|
||||||
if err != nil { |
|
||||||
b.Fatalf("unexpected err: %v", err) |
|
||||||
} |
|
||||||
if !wasCompressed { |
|
||||||
b.Fatal("recover claims uncompressed pubkey") |
|
||||||
} |
|
||||||
if !pubKey.IsEqual(wantPubKey) { |
|
||||||
b.Fatal("recover returned unexpected pubkey") |
|
||||||
} |
|
||||||
b.ReportAllocs() |
|
||||||
b.ResetTimer() |
|
||||||
for i := 0; i < b.N; i++ { |
|
||||||
_, _, _ = RecoverCompact(compactSig, msgHash) |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,40 +0,0 @@ |
|||||||
// Copyright (c) 2020-2023 The Decred developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package ecdsa provides secp256k1-optimized ECDSA signing and verification.
|
|
||||||
//
|
|
||||||
// This package provides data structures and functions necessary to produce and
|
|
||||||
// verify deterministic canonical signatures in accordance with RFC6979 and
|
|
||||||
// BIP0062, optimized specifically for the secp256k1 curve using the Elliptic Curve
|
|
||||||
// Digital Signature Algorithm (ECDSA), as defined in FIPS 186-3. See
|
|
||||||
// https://www.secg.org/sec2-v2.pdf for details on the secp256k1 standard.
|
|
||||||
//
|
|
||||||
// It also provides functions to parse and serialize the ECDSA signatures with the
|
|
||||||
// more strict Distinguished Encoding Rules (DER) of ISO/IEC 8825-1 and some
|
|
||||||
// additional restrictions specific to secp256k1.
|
|
||||||
//
|
|
||||||
// In addition, it supports a custom "compact" signature format which allows
|
|
||||||
// efficient recovery of the public key from a given valid signature and message
|
|
||||||
// hash combination.
|
|
||||||
//
|
|
||||||
// A comprehensive suite of tests is provided to ensure proper functionality.
|
|
||||||
//
|
|
||||||
// # ECDSA use in Decred
|
|
||||||
//
|
|
||||||
// At the time of this writing, ECDSA signatures are heavily used for proving coin
|
|
||||||
// ownership in Decred as the vast majority of transactions consist of what is
|
|
||||||
// effectively transferring ownership of coins to a public key associated with a
|
|
||||||
// secret key only known to the recipient of the coins along with an encumbrance
|
|
||||||
// that requires an ECDSA signature that proves the new owner possesses the secret
|
|
||||||
// key without actually revealing it.
|
|
||||||
//
|
|
||||||
// # Errors
|
|
||||||
//
|
|
||||||
// The errors returned by this package are of type ecdsa.Error and fully support
|
|
||||||
// the standard library errors.Is and errors.As functions. This allows the caller
|
|
||||||
// to programmatically determine the specific error by examining the ErrorKind
|
|
||||||
// field of the type asserted ecdsa.Error while still providing rich error messages
|
|
||||||
// with contextual information. See ErrorKind in the package documentation for a
|
|
||||||
// full list.
|
|
||||||
package ecdsa |
|
||||||
@ -1,106 +0,0 @@ |
|||||||
// Copyright (c) 2020-2022 The Decred developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package ecdsa |
|
||||||
|
|
||||||
// ErrorKind identifies a kind of error. It has full support for
|
|
||||||
// errors.Is and errors.As, so the caller can directly check against
|
|
||||||
// an error kind when determining the reason for an error.
|
|
||||||
type ErrorKind string |
|
||||||
|
|
||||||
// These constants are used to identify a specific Error.
|
|
||||||
const ( |
|
||||||
// ErrSigTooShort is returned when a signature that should be a DER
|
|
||||||
// signature is too short.
|
|
||||||
ErrSigTooShort = ErrorKind("ErrSigTooShort") |
|
||||||
// ErrSigTooLong is returned when a signature that should be a DER signature
|
|
||||||
// is too long.
|
|
||||||
ErrSigTooLong = ErrorKind("ErrSigTooLong") |
|
||||||
// ErrSigInvalidSeqID is returned when a signature that should be a DER
|
|
||||||
// signature does not have the expected ASN.1 sequence ID.
|
|
||||||
ErrSigInvalidSeqID = ErrorKind("ErrSigInvalidSeqID") |
|
||||||
// ErrSigInvalidDataLen is returned when a signature that should be a DER
|
|
||||||
// signature does not specify the correct number of remaining bytes for the
|
|
||||||
// R and S portions.
|
|
||||||
ErrSigInvalidDataLen = ErrorKind("ErrSigInvalidDataLen") |
|
||||||
// ErrSigMissingSTypeID is returned when a signature that should be a DER
|
|
||||||
// signature does not provide the ASN.1 type ID for S.
|
|
||||||
ErrSigMissingSTypeID = ErrorKind("ErrSigMissingSTypeID") |
|
||||||
// ErrSigMissingSLen is returned when a signature that should be a DER
|
|
||||||
// signature does not provide the length of S.
|
|
||||||
ErrSigMissingSLen = ErrorKind("ErrSigMissingSLen") |
|
||||||
// ErrSigInvalidSLen is returned when a signature that should be a DER
|
|
||||||
// signature does not specify the correct number of bytes for the S portion.
|
|
||||||
ErrSigInvalidSLen = ErrorKind("ErrSigInvalidSLen") |
|
||||||
// ErrSigInvalidRIntID is returned when a signature that should be a DER
|
|
||||||
// signature does not have the expected ASN.1 integer ID for R.
|
|
||||||
ErrSigInvalidRIntID = ErrorKind("ErrSigInvalidRIntID") |
|
||||||
// ErrSigZeroRLen is returned when a signature that should be a DER
|
|
||||||
// signature has an R length of zero.
|
|
||||||
ErrSigZeroRLen = ErrorKind("ErrSigZeroRLen") |
|
||||||
// ErrSigNegativeR is returned when a signature that should be a DER
|
|
||||||
// signature has a negative value for R.
|
|
||||||
ErrSigNegativeR = ErrorKind("ErrSigNegativeR") |
|
||||||
// ErrSigTooMuchRPadding is returned when a signature that should be a DER
|
|
||||||
// signature has too much padding for R.
|
|
||||||
ErrSigTooMuchRPadding = ErrorKind("ErrSigTooMuchRPadding") |
|
||||||
// ErrSigRIsZero is returned when a signature has R set to the value zero.
|
|
||||||
ErrSigRIsZero = ErrorKind("ErrSigRIsZero") |
|
||||||
// ErrSigRTooBig is returned when a signature has R with a value that is
|
|
||||||
// greater than or equal to the group order.
|
|
||||||
ErrSigRTooBig = ErrorKind("ErrSigRTooBig") |
|
||||||
// ErrSigInvalidSIntID is returned when a signature that should be a DER
|
|
||||||
// signature does not have the expected ASN.1 integer ID for S.
|
|
||||||
ErrSigInvalidSIntID = ErrorKind("ErrSigInvalidSIntID") |
|
||||||
// ErrSigZeroSLen is returned when a signature that should be a DER
|
|
||||||
// signature has an S length of zero.
|
|
||||||
ErrSigZeroSLen = ErrorKind("ErrSigZeroSLen") |
|
||||||
// ErrSigNegativeS is returned when a signature that should be a DER
|
|
||||||
// signature has a negative value for S.
|
|
||||||
ErrSigNegativeS = ErrorKind("ErrSigNegativeS") |
|
||||||
// ErrSigTooMuchSPadding is returned when a signature that should be a DER
|
|
||||||
// signature has too much padding for S.
|
|
||||||
ErrSigTooMuchSPadding = ErrorKind("ErrSigTooMuchSPadding") |
|
||||||
// ErrSigSIsZero is returned when a signature has S set to the value zero.
|
|
||||||
ErrSigSIsZero = ErrorKind("ErrSigSIsZero") |
|
||||||
// ErrSigSTooBig is returned when a signature has S with a value that is
|
|
||||||
// greater than or equal to the group order.
|
|
||||||
ErrSigSTooBig = ErrorKind("ErrSigSTooBig") |
|
||||||
// ErrSigInvalidLen is returned when a signature that should be a compact
|
|
||||||
// signature is not the required length.
|
|
||||||
ErrSigInvalidLen = ErrorKind("ErrSigInvalidLen") |
|
||||||
// ErrSigInvalidRecoveryCode is returned when a signature that should be a
|
|
||||||
// compact signature has an invalid value for the public key recovery code.
|
|
||||||
ErrSigInvalidRecoveryCode = ErrorKind("ErrSigInvalidRecoveryCode") |
|
||||||
// ErrSigOverflowsPrime is returned when a signature that should be a
|
|
||||||
// compact signature has the overflow bit set but adding the order to it
|
|
||||||
// would overflow the underlying field prime.
|
|
||||||
ErrSigOverflowsPrime = ErrorKind("ErrSigOverflowsPrime") |
|
||||||
// ErrPointNotOnCurve is returned when attempting to recover a public key
|
|
||||||
// from a compact signature results in a point that is not on the elliptic
|
|
||||||
// curve.
|
|
||||||
ErrPointNotOnCurve = ErrorKind("ErrPointNotOnCurve") |
|
||||||
) |
|
||||||
|
|
||||||
// Error satisfies the error interface and prints human-readable errors.
|
|
||||||
func (e ErrorKind) Error() string { return string(e) } |
|
||||||
|
|
||||||
// Error identifies an error related to an ECDSA signature. It has full
|
|
||||||
// support for errors.Is and errors.As, so the caller can ascertain the
|
|
||||||
// specific reason for the error by checking the underlying error.
|
|
||||||
type Error struct { |
|
||||||
Err error |
|
||||||
Description string |
|
||||||
} |
|
||||||
|
|
||||||
// Error satisfies the error interface and prints human-readable errors.
|
|
||||||
func (e Error) Error() string { return e.Description } |
|
||||||
|
|
||||||
// Unwrap returns the underlying wrapped error.
|
|
||||||
func (e Error) Unwrap() error { return e.Err } |
|
||||||
|
|
||||||
// signatureError creates an Error given a set of arguments.
|
|
||||||
func signatureError(kind ErrorKind, desc string) Error { |
|
||||||
return Error{Err: kind, Description: desc} |
|
||||||
} |
|
||||||
@ -1,154 +0,0 @@ |
|||||||
// Copyright (c) 2020-2022 The Decred developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package ecdsa |
|
||||||
|
|
||||||
import ( |
|
||||||
"errors" |
|
||||||
"testing" |
|
||||||
) |
|
||||||
|
|
||||||
// TestErrorKindStringer tests the stringized output for the ErrorKind type.
|
|
||||||
func TestErrorKindStringer(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
in ErrorKind |
|
||||||
want string |
|
||||||
}{ |
|
||||||
{ErrSigTooShort, "ErrSigTooShort"}, |
|
||||||
{ErrSigTooLong, "ErrSigTooLong"}, |
|
||||||
{ErrSigInvalidSeqID, "ErrSigInvalidSeqID"}, |
|
||||||
{ErrSigInvalidDataLen, "ErrSigInvalidDataLen"}, |
|
||||||
{ErrSigMissingSTypeID, "ErrSigMissingSTypeID"}, |
|
||||||
{ErrSigMissingSLen, "ErrSigMissingSLen"}, |
|
||||||
{ErrSigInvalidSLen, "ErrSigInvalidSLen"}, |
|
||||||
{ErrSigInvalidRIntID, "ErrSigInvalidRIntID"}, |
|
||||||
{ErrSigZeroRLen, "ErrSigZeroRLen"}, |
|
||||||
{ErrSigNegativeR, "ErrSigNegativeR"}, |
|
||||||
{ErrSigTooMuchRPadding, "ErrSigTooMuchRPadding"}, |
|
||||||
{ErrSigRIsZero, "ErrSigRIsZero"}, |
|
||||||
{ErrSigRTooBig, "ErrSigRTooBig"}, |
|
||||||
{ErrSigInvalidSIntID, "ErrSigInvalidSIntID"}, |
|
||||||
{ErrSigZeroSLen, "ErrSigZeroSLen"}, |
|
||||||
{ErrSigNegativeS, "ErrSigNegativeS"}, |
|
||||||
{ErrSigTooMuchSPadding, "ErrSigTooMuchSPadding"}, |
|
||||||
{ErrSigSIsZero, "ErrSigSIsZero"}, |
|
||||||
{ErrSigSTooBig, "ErrSigSTooBig"}, |
|
||||||
{ErrSigInvalidLen, "ErrSigInvalidLen"}, |
|
||||||
{ErrSigInvalidRecoveryCode, "ErrSigInvalidRecoveryCode"}, |
|
||||||
{ErrSigOverflowsPrime, "ErrSigOverflowsPrime"}, |
|
||||||
{ErrPointNotOnCurve, "ErrPointNotOnCurve"}, |
|
||||||
} |
|
||||||
|
|
||||||
for i, test := range tests { |
|
||||||
result := test.in.Error() |
|
||||||
if result != test.want { |
|
||||||
t.Errorf("#%d: got: %s want: %s", i, result, test.want) |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// TestError tests the error output for the Error type.
|
|
||||||
func TestError(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
in Error |
|
||||||
want string |
|
||||||
}{ |
|
||||||
{ |
|
||||||
Error{Description: "some error"}, |
|
||||||
"some error", |
|
||||||
}, { |
|
||||||
Error{Description: "human-readable error"}, |
|
||||||
"human-readable error", |
|
||||||
}, |
|
||||||
} |
|
||||||
for i, test := range tests { |
|
||||||
result := test.in.Error() |
|
||||||
if result != test.want { |
|
||||||
t.Errorf("#%d: got: %s want: %s", i, result, test.want) |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// TestErrorKindIsAs ensures both ErrorKind and Error can be identified as being
|
|
||||||
// a specific error kind via errors.Is and unwrapped via errors.As.
|
|
||||||
func TestErrorKindIsAs(t *testing.T) { |
|
||||||
tests := []struct { |
|
||||||
name string |
|
||||||
err error |
|
||||||
target error |
|
||||||
wantMatch bool |
|
||||||
wantAs ErrorKind |
|
||||||
}{ |
|
||||||
{ |
|
||||||
name: "ErrSigTooShort == ErrSigTooShort", |
|
||||||
err: ErrSigTooShort, |
|
||||||
target: ErrSigTooShort, |
|
||||||
wantMatch: true, |
|
||||||
wantAs: ErrSigTooShort, |
|
||||||
}, { |
|
||||||
name: "Error.ErrSigTooShort == ErrSigTooShort", |
|
||||||
err: signatureError(ErrSigTooShort, ""), |
|
||||||
target: ErrSigTooShort, |
|
||||||
wantMatch: true, |
|
||||||
wantAs: ErrSigTooShort, |
|
||||||
}, { |
|
||||||
name: "Error.ErrSigTooShort == Error.ErrSigTooShort", |
|
||||||
err: signatureError(ErrSigTooShort, ""), |
|
||||||
target: signatureError(ErrSigTooShort, ""), |
|
||||||
wantMatch: true, |
|
||||||
wantAs: ErrSigTooShort, |
|
||||||
}, { |
|
||||||
name: "ErrSigTooLong != ErrSigTooShort", |
|
||||||
err: ErrSigTooLong, |
|
||||||
target: ErrSigTooShort, |
|
||||||
wantMatch: false, |
|
||||||
wantAs: ErrSigTooLong, |
|
||||||
}, { |
|
||||||
name: "Error.ErrSigTooLong != ErrSigTooShort", |
|
||||||
err: signatureError(ErrSigTooLong, ""), |
|
||||||
target: ErrSigTooShort, |
|
||||||
wantMatch: false, |
|
||||||
wantAs: ErrSigTooLong, |
|
||||||
}, { |
|
||||||
name: "ErrSigTooLong != Error.ErrSigTooShort", |
|
||||||
err: ErrSigTooLong, |
|
||||||
target: signatureError(ErrSigTooShort, ""), |
|
||||||
wantMatch: false, |
|
||||||
wantAs: ErrSigTooLong, |
|
||||||
}, { |
|
||||||
name: "Error.ErrSigTooLong != Error.ErrSigTooShort", |
|
||||||
err: signatureError(ErrSigTooLong, ""), |
|
||||||
target: signatureError(ErrSigTooShort, ""), |
|
||||||
wantMatch: false, |
|
||||||
wantAs: ErrSigTooLong, |
|
||||||
}, |
|
||||||
} |
|
||||||
for _, test := range tests { |
|
||||||
// Ensure the error matches or not depending on the expected result.
|
|
||||||
result := errors.Is(test.err, test.target) |
|
||||||
if result != test.wantMatch { |
|
||||||
t.Errorf( |
|
||||||
"%s: incorrect error identification -- got %v, want %v", |
|
||||||
test.name, result, test.wantMatch, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
// Ensure the underlying error kind can be unwrapped and is the
|
|
||||||
// expected code.
|
|
||||||
var kind ErrorKind |
|
||||||
if !errors.As(test.err, &kind) { |
|
||||||
t.Errorf("%s: unable to unwrap to error", test.name) |
|
||||||
continue |
|
||||||
} |
|
||||||
if !errors.Is(kind, test.wantAs) { |
|
||||||
t.Errorf( |
|
||||||
"%s: unexpected unwrapped error -- got %v, want %v", |
|
||||||
test.name, kind, test.wantAs, |
|
||||||
) |
|
||||||
continue |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,79 +0,0 @@ |
|||||||
// Copyright (c) 2014 The btcsuite developers
|
|
||||||
// Copyright (c) 2015-2021 The Decred developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// TODO: change this to work with sha256
|
|
||||||
|
|
||||||
package ecdsa_test |
|
||||||
|
|
||||||
// // This example demonstrates signing a message with a secp256k1 secret key that
|
|
||||||
// // is first parsed from raw bytes and serializing the generated signature.
|
|
||||||
// func ExampleSign() {
|
|
||||||
// // Decode a hex-encoded secret key.
|
|
||||||
// pkBytes, err := hex.Dec("22a47fa09a223f2aa079edf85a7c2d4f87" +
|
|
||||||
// "20ee63e502ee2869afab7de234b80c")
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// secKey := secp256k1.SecKeyFromBytes(pkBytes)
|
|
||||||
//
|
|
||||||
// // Sign a message using the secret key.
|
|
||||||
// message := "test message"
|
|
||||||
// messageHash := blake256.Sum256(by(message))
|
|
||||||
// signature := ecdsa.Sign(secKey, messageHash[:])
|
|
||||||
//
|
|
||||||
// // Serialize and display the signature.
|
|
||||||
// fmt.Printf("Serialized Signature: %x\n", signature.Serialize())
|
|
||||||
//
|
|
||||||
// // Verify the signature for the message using the public key.
|
|
||||||
// pubKey := secKey.Pubkey()
|
|
||||||
// verified := signature.Verify(messageHash[:], pubKey)
|
|
||||||
// fmt.Printf("Signature Verified? %v\n", verified)
|
|
||||||
//
|
|
||||||
// // Output:
|
|
||||||
// // Serialized Signature: 3045022100fcc0a8768cfbcefcf2cadd7cfb0fb18ed08dd2e2ae84bef1a474a3d351b26f0302200fc1a350b45f46fa00101391302818d748c2b22615511a3ffd5bb638bd777207
|
|
||||||
// // Signature Verified? true
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // This example demonstrates verifying a secp256k1 signature against a public
|
|
||||||
// // key that is first parsed from raw bytes. The signature is also parsed from
|
|
||||||
// // raw bytes.
|
|
||||||
// func ExampleSignature_Verify() {
|
|
||||||
// // Decode hex-encoded serialized public key.
|
|
||||||
// pubKeyBytes, err := hex.Dec("02a673638cb9587cb68ea08dbef685c" +
|
|
||||||
// "6f2d2a751a8b3c6f2a7e9a4999e6e4bfaf5")
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// pubKey, err := secp256k1.ParsePubKey(pubKeyBytes)
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Decode hex-encoded serialized signature.
|
|
||||||
// sigBytes, err := hex.Dec("3045022100fcc0a8768cfbcefcf2cadd7cfb0" +
|
|
||||||
// "fb18ed08dd2e2ae84bef1a474a3d351b26f0302200fc1a350b45f46fa0010139130" +
|
|
||||||
// "2818d748c2b22615511a3ffd5bb638bd777207")
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// signature, err := ecdsa.ParseDERSignature(sigBytes)
|
|
||||||
// if err != nil {
|
|
||||||
// fmt.Println(err)
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Verify the signature for the message using the public key.
|
|
||||||
// message := "test message"
|
|
||||||
// messageHash := blake256.Sum256(by(message))
|
|
||||||
// verified := signature.Verify(messageHash[:], pubKey)
|
|
||||||
// fmt.Println("Signature Verified?", verified)
|
|
||||||
//
|
|
||||||
// // Output:
|
|
||||||
// // Signature Verified? true
|
|
||||||
// }
|
|
||||||
@ -1,954 +0,0 @@ |
|||||||
// Copyright (c) 2013-2014 The btcsuite developers
|
|
||||||
// Copyright (c) 2015-2022 The Decred developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package ecdsa |
|
||||||
|
|
||||||
import ( |
|
||||||
"fmt" |
|
||||||
|
|
||||||
"next.orly.dev/pkg/crypto/ec/secp256k1" |
|
||||||
) |
|
||||||
|
|
||||||
// References:
|
|
||||||
// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone)
|
|
||||||
//
|
|
||||||
// [ISO/IEC 8825-1]: Information technology — ASN.1 encoding rules:
|
|
||||||
// Specification of Basic Encoding Rules (BER), Canonical Encoding Rules
|
|
||||||
// (CER) and Distinguished Encoding Rules (DER)
|
|
||||||
//
|
|
||||||
// [SEC1]: Elliptic Curve Cryptography (May 31, 2009, Version 2.0)
|
|
||||||
// https://www.secg.org/sec1-v2.pdf
|
|
||||||
|
|
||||||
var ( |
|
||||||
// zero32 is an array of 32 bytes used for the purposes of zeroing and is
|
|
||||||
// defined here to avoid extra allocations.
|
|
||||||
zero32 = [32]byte{} |
|
||||||
// orderAsFieldVal is the order of the secp256k1 curve group stored as a
|
|
||||||
// field value. It is provided here to avoid the need to create it multiple
|
|
||||||
// times.
|
|
||||||
orderAsFieldVal = func() secp256k1.FieldVal { |
|
||||||
var f secp256k1.FieldVal |
|
||||||
f.SetByteSlice(secp256k1.Params().N.Bytes()) |
|
||||||
return f |
|
||||||
}() |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
// asn1SequenceID is the ASN.1 identifier for a sequence and is used when
|
|
||||||
// parsing and serializing signatures encoded with the Distinguished
|
|
||||||
// Encoding Rules (DER) format per section 10 of [ISO/IEC 8825-1].
|
|
||||||
asn1SequenceID = 0x30 |
|
||||||
// asn1IntegerID is the ASN.1 identifier for an integer and is used when
|
|
||||||
// parsing and serializing signatures encoded with the Distinguished
|
|
||||||
// Encoding Rules (DER) format per section 10 of [ISO/IEC 8825-1].
|
|
||||||
asn1IntegerID = 0x02 |
|
||||||
) |
|
||||||
|
|
||||||
// Signature is a type representing an ECDSA signature.
|
|
||||||
type Signature struct { |
|
||||||
r secp256k1.ModNScalar |
|
||||||
s secp256k1.ModNScalar |
|
||||||
} |
|
||||||
|
|
||||||
// NewSignature instantiates a new signature given some r and s values.
|
|
||||||
func NewSignature(r, s *secp256k1.ModNScalar) *Signature { |
|
||||||
return &Signature{*r, *s} |
|
||||||
} |
|
||||||
|
|
||||||
// Serialize returns the ECDSA signature in the Distinguished Encoding Rules
|
|
||||||
// (DER) format per section 10 of [ISO/IEC 8825-1] and such that the S component
|
|
||||||
// of the signature is less than or equal to the half order of the group.
|
|
||||||
//
|
|
||||||
// Note that the serialized bytes returned do not include the appended hash type
|
|
||||||
// used in Decred signature scripts.
|
|
||||||
func (sig *Signature) Serialize() []byte { |
|
||||||
// The format of a DER encoded signature is as follows:
|
|
||||||
//
|
|
||||||
// 0x30 <total length> 0x02 <length of R> <R> 0x02 <length of S> <S>
|
|
||||||
// - 0x30 is the ASN.1 identifier for a sequence.
|
|
||||||
// - Total length is 1 byte and specifies length of all remaining data.
|
|
||||||
// - 0x02 is the ASN.1 identifier that specifies an integer follows.
|
|
||||||
// - Length of R is 1 byte and specifies how many bytes R occupies.
|
|
||||||
// - R is the arbitrary length big-endian encoded number which
|
|
||||||
// represents the R value of the signature. DER encoding dictates
|
|
||||||
// that the value must be encoded using the minimum possible number
|
|
||||||
// of bytes. This implies the first byte can only be null if the
|
|
||||||
// highest bit of the next byte is set in order to prevent it from
|
|
||||||
// being interpreted as a negative number.
|
|
||||||
// - 0x02 is once again the ASN.1 integer identifier.
|
|
||||||
// - Length of S is 1 byte and specifies how many bytes S occupies.
|
|
||||||
// - S is the arbitrary length big-endian encoded number which
|
|
||||||
// represents the S value of the signature. The encoding rules are
|
|
||||||
// identical as those for R.
|
|
||||||
|
|
||||||
// Ensure the S component of the signature is less than or equal to the half
|
|
||||||
// order of the group because both S and its negation are valid signatures
|
|
||||||
// modulo the order, so this forces a consistent choice to reduce signature
|
|
||||||
// malleability.
|
|
||||||
sigS := new(secp256k1.ModNScalar).Set(&sig.s) |
|
||||||
if sigS.IsOverHalfOrder() { |
|
||||||
sigS.Negate() |
|
||||||
} |
|
||||||
|
|
||||||
// Serialize the R and S components of the signature into their fixed
|
|
||||||
// 32-byte big-endian encoding. Note that the extra leading zero byte is
|
|
||||||
// used to ensure it is canonical per DER and will be stripped if needed
|
|
||||||
// below.
|
|
||||||
var rBuf, sBuf [33]byte |
|
||||||
sig.r.PutBytesUnchecked(rBuf[1:33]) |
|
||||||
sigS.PutBytesUnchecked(sBuf[1:33]) |
|
||||||
// Ensure the encoded bytes for the R and S components are canonical per DER
|
|
||||||
// by trimming all leading zero bytes so long as the next byte does not have
|
|
||||||
// the high bit set and it's not the final byte.
|
|
||||||
canonR, canonS := rBuf[:], sBuf[:] |
|
||||||
for len(canonR) > 1 && canonR[0] == 0x00 && canonR[1]&0x80 == 0 { |
|
||||||
canonR = canonR[1:] |
|
||||||
} |
|
||||||
for len(canonS) > 1 && canonS[0] == 0x00 && canonS[1]&0x80 == 0 { |
|
||||||
canonS = canonS[1:] |
|
||||||
} |
|
||||||
// Total length of returned signature is 1 byte for each magic and length
|
|
||||||
// (6 total), plus lengths of R and S.
|
|
||||||
totalLen := 6 + len(canonR) + len(canonS) |
|
||||||
b := make([]byte, 0, totalLen) |
|
||||||
b = append(b, asn1SequenceID) |
|
||||||
b = append(b, byte(totalLen-2)) |
|
||||||
b = append(b, asn1IntegerID) |
|
||||||
b = append(b, byte(len(canonR))) |
|
||||||
b = append(b, canonR...) |
|
||||||
b = append(b, asn1IntegerID) |
|
||||||
b = append(b, byte(len(canonS))) |
|
||||||
b = append(b, canonS...) |
|
||||||
return b |
|
||||||
} |
|
||||||
|
|
||||||
// zeroArray32 zeroes the provided 32-byte buffer.
|
|
||||||
func zeroArray32(b *[32]byte) { |
|
||||||
copy(b[:], zero32[:]) |
|
||||||
} |
|
||||||
|
|
||||||
// fieldToModNScalar converts a field value to scalar modulo the group order and
|
|
||||||
// returns the scalar along with either 1 if it was reduced (aka it overflowed)
|
|
||||||
// or 0 otherwise.
|
|
||||||
//
|
|
||||||
// Note that a bool is not used here because it is not possible in Go to convert
|
|
||||||
// from a bool to numeric value in constant time and many constant-time
|
|
||||||
// operations require a numeric value.
|
|
||||||
func fieldToModNScalar(v *secp256k1.FieldVal) (secp256k1.ModNScalar, uint32) { |
|
||||||
var buf [32]byte |
|
||||||
v.PutBytes(&buf) |
|
||||||
var s secp256k1.ModNScalar |
|
||||||
overflow := s.SetBytes(&buf) |
|
||||||
zeroArray32(&buf) |
|
||||||
return s, overflow |
|
||||||
} |
|
||||||
|
|
||||||
// modNScalarToField converts a scalar modulo the group order to a field value.
|
|
||||||
func modNScalarToField(v *secp256k1.ModNScalar) secp256k1.FieldVal { |
|
||||||
var buf [32]byte |
|
||||||
v.PutBytes(&buf) |
|
||||||
var fv secp256k1.FieldVal |
|
||||||
fv.SetBytes(&buf) |
|
||||||
return fv |
|
||||||
} |
|
||||||
|
|
||||||
// Verify returns whether the signature is valid for the provided hash
|
|
||||||
// and secp256k1 public key.
|
|
||||||
func (sig *Signature) Verify(hash []byte, pubKey *secp256k1.PublicKey) bool { |
|
||||||
// The algorithm for verifying an ECDSA signature is given as algorithm 4.30
|
|
||||||
// in [GECC].
|
|
||||||
//
|
|
||||||
// The following is a paraphrased version for reference:
|
|
||||||
//
|
|
||||||
// G = curve generator
|
|
||||||
// N = curve order
|
|
||||||
// Q = public key
|
|
||||||
// m = message
|
|
||||||
// R, S = signature
|
|
||||||
//
|
|
||||||
// 1. Fail if R and S are not in [1, N-1]
|
|
||||||
// 2. e = H(m)
|
|
||||||
// 3. w = S^-1 mod N
|
|
||||||
// 4. u1 = e * w mod N
|
|
||||||
// u2 = R * w mod N
|
|
||||||
// 5. X = u1G + u2Q
|
|
||||||
// 6. Fail if X is the point at infinity
|
|
||||||
// 7. x = X.x mod N (X.x is the x coordinate of X)
|
|
||||||
// 8. Verified if x == R
|
|
||||||
//
|
|
||||||
// However, since all group operations are done internally in Jacobian
|
|
||||||
// projective space, the algorithm is modified slightly here in order to
|
|
||||||
// avoid an expensive inversion back into affine coordinates at step 7.
|
|
||||||
// Credits to Greg Maxwell for originally suggesting this optimization.
|
|
||||||
//
|
|
||||||
// Ordinarily, step 7 involves converting the x coordinate to affine by
|
|
||||||
// calculating x = x / z^2 (mod P) and then calculating the remainder as
|
|
||||||
// x = x (mod N). Then step 8 compares it to R.
|
|
||||||
//
|
|
||||||
// Note that since R is the x coordinate mod N from a random point that was
|
|
||||||
// originally mod P, and the cofactor of the secp256k1 curve is 1, there are
|
|
||||||
// only two possible x coordinates that the original random point could have
|
|
||||||
// been to produce R: x, where x < N, and x+N, where x+N < P.
|
|
||||||
//
|
|
||||||
// This implies that the signature is valid if either:
|
|
||||||
// a) R == X.x / X.z^2 (mod P)
|
|
||||||
// => R * X.z^2 == X.x (mod P)
|
|
||||||
// --or--
|
|
||||||
// b) R + N < P && R + N == X.x / X.z^2 (mod P)
|
|
||||||
// => R + N < P && (R + N) * X.z^2 == X.x (mod P)
|
|
||||||
//
|
|
||||||
// Therefore the following modified algorithm is used:
|
|
||||||
//
|
|
||||||
// 1. Fail if R and S are not in [1, N-1]
|
|
||||||
// 2. e = H(m)
|
|
||||||
// 3. w = S^-1 mod N
|
|
||||||
// 4. u1 = e * w mod N
|
|
||||||
// u2 = R * w mod N
|
|
||||||
// 5. X = u1G + u2Q
|
|
||||||
// 6. Fail if X is the point at infinity
|
|
||||||
// 7. z = (X.z)^2 mod P (X.z is the z coordinate of X)
|
|
||||||
// 8. Verified if R * z == X.x (mod P)
|
|
||||||
// 9. Fail if R + N >= P
|
|
||||||
// 10. Verified if (R + N) * z == X.x (mod P)
|
|
||||||
//
|
|
||||||
// Step 1.
|
|
||||||
//
|
|
||||||
// Fail if R and S are not in [1, N-1].
|
|
||||||
if sig.r.IsZero() || sig.s.IsZero() { |
|
||||||
return false |
|
||||||
} |
|
||||||
// Step 2.
|
|
||||||
//
|
|
||||||
// e = H(m)
|
|
||||||
var e secp256k1.ModNScalar |
|
||||||
e.SetByteSlice(hash) |
|
||||||
// Step 3.
|
|
||||||
//
|
|
||||||
// w = S^-1 mod N
|
|
||||||
w := new(secp256k1.ModNScalar).InverseValNonConst(&sig.s) |
|
||||||
// Step 4.
|
|
||||||
//
|
|
||||||
// u1 = e * w mod N
|
|
||||||
// u2 = R * w mod N
|
|
||||||
u1 := new(secp256k1.ModNScalar).Mul2(&e, w) |
|
||||||
u2 := new(secp256k1.ModNScalar).Mul2(&sig.r, w) |
|
||||||
// Step 5.
|
|
||||||
//
|
|
||||||
// X = u1G + u2Q
|
|
||||||
var X, Q, u1G, u2Q secp256k1.JacobianPoint |
|
||||||
pubKey.AsJacobian(&Q) |
|
||||||
secp256k1.ScalarBaseMultNonConst(u1, &u1G) |
|
||||||
secp256k1.ScalarMultNonConst(u2, &Q, &u2Q) |
|
||||||
secp256k1.AddNonConst(&u1G, &u2Q, &X) |
|
||||||
// Step 6.
|
|
||||||
//
|
|
||||||
// Fail if X is the point at infinity
|
|
||||||
if (X.X.IsZero() && X.Y.IsZero()) || X.Z.IsZero() { |
|
||||||
return false |
|
||||||
} |
|
||||||
// Step 7.
|
|
||||||
//
|
|
||||||
// z = (X.z)^2 mod P (X.z is the z coordinate of X)
|
|
||||||
z := new(secp256k1.FieldVal).SquareVal(&X.Z) |
|
||||||
// Step 8.
|
|
||||||
//
|
|
||||||
// Verified if R * z == X.x (mod P)
|
|
||||||
sigRModP := modNScalarToField(&sig.r) |
|
||||||
result := new(secp256k1.FieldVal).Mul2(&sigRModP, z).Normalize() |
|
||||||
if result.Equals(&X.X) { |
|
||||||
return true |
|
||||||
} |
|
||||||
// Step 9.
|
|
||||||
//
|
|
||||||
// Fail if R + N >= P
|
|
||||||
if sigRModP.IsGtOrEqPrimeMinusOrder() { |
|
||||||
return false |
|
||||||
} |
|
||||||
// Step 10.
|
|
||||||
//
|
|
||||||
// Verified if (R + N) * z == X.x (mod P)
|
|
||||||
sigRModP.Add(&orderAsFieldVal) |
|
||||||
result.Mul2(&sigRModP, z).Normalize() |
|
||||||
return result.Equals(&X.X) |
|
||||||
} |
|
||||||
|
|
||||||
// IsEqual compares this Signature instance to the one passed, returning true if
|
|
||||||
// both Signatures are equivalent. A signature is equivalent to another, if
|
|
||||||
// they both have the same scalar value for R and S.
|
|
||||||
func (sig *Signature) IsEqual(otherSig *Signature) bool { |
|
||||||
return sig.r.Equals(&otherSig.r) && sig.s.Equals(&otherSig.s) |
|
||||||
} |
|
||||||
|
|
||||||
// ParseDERSignature parses a signature in the Distinguished Encoding Rules
|
|
||||||
// (DER) format per section 10 of [ISO/IEC 8825-1] and enforces the following
|
|
||||||
// additional restrictions specific to secp256k1:
|
|
||||||
//
|
|
||||||
// - The R and S values must be in the valid range for secp256k1 scalars:
|
|
||||||
// - Negative values are rejected
|
|
||||||
// - Zero is rejected
|
|
||||||
// - Values greater than or equal to the secp256k1 group order are rejected
|
|
||||||
func ParseDERSignature(sig []byte) (*Signature, error) { |
|
||||||
// The format of a DER encoded signature for secp256k1 is as follows:
|
|
||||||
//
|
|
||||||
// 0x30 <total length> 0x02 <length of R> <R> 0x02 <length of S> <S>
|
|
||||||
// - 0x30 is the ASN.1 identifier for a sequence
|
|
||||||
// - Total length is 1 byte and specifies length of all remaining data
|
|
||||||
// - 0x02 is the ASN.1 identifier that specifies an integer follows
|
|
||||||
// - Length of R is 1 byte and specifies how many bytes R occupies
|
|
||||||
// - R is the arbitrary length big-endian encoded number which
|
|
||||||
// represents the R value of the signature. DER encoding dictates
|
|
||||||
// that the value must be encoded using the minimum possible number
|
|
||||||
// of bytes. This implies the first byte can only be null if the
|
|
||||||
// highest bit of the next byte is set in order to prevent it from
|
|
||||||
// being interpreted as a negative number.
|
|
||||||
// - 0x02 is once again the ASN.1 integer identifier
|
|
||||||
// - Length of S is 1 byte and specifies how many bytes S occupies
|
|
||||||
// - S is the arbitrary length big-endian encoded number which
|
|
||||||
// represents the S value of the signature. The encoding rules are
|
|
||||||
// identical as those for R.
|
|
||||||
//
|
|
||||||
// NOTE: The DER specification supports specifying lengths that can occupy
|
|
||||||
// more than 1 byte, however, since this is specific to secp256k1
|
|
||||||
// signatures, all lengths will be a single byte.
|
|
||||||
const ( |
|
||||||
// minSigLen is the minimum length of a DER encoded signature and is
|
|
||||||
// when both R and S are 1 byte each.
|
|
||||||
//
|
|
||||||
// 0x30 + <1-byte> + 0x02 + 0x01 + <byte> + 0x2 + 0x01 + <byte>
|
|
||||||
minSigLen = 8 |
|
||||||
// maxSigLen is the maximum length of a DER encoded signature and is
|
|
||||||
// when both R and S are 33 bytes each. It is 33 bytes because a
|
|
||||||
// 256-bit integer requires 32 bytes and an additional leading null byte
|
|
||||||
// might be required if the high bit is set in the value.
|
|
||||||
//
|
|
||||||
// 0x30 + <1-byte> + 0x02 + 0x21 + <33 bytes> + 0x2 + 0x21 + <33 bytes>
|
|
||||||
maxSigLen = 72 |
|
||||||
// sequenceOffset is the byte offset within the signature of the
|
|
||||||
// expected ASN.1 sequence identifier.
|
|
||||||
sequenceOffset = 0 |
|
||||||
// dataLenOffset is the byte offset within the signature of the expected
|
|
||||||
// total length of all remaining data in the signature.
|
|
||||||
dataLenOffset = 1 |
|
||||||
// rTypeOffset is the byte offset within the signature of the ASN.1
|
|
||||||
// identifier for R and is expected to indicate an ASN.1 integer.
|
|
||||||
rTypeOffset = 2 |
|
||||||
// rLenOffset is the byte offset within the signature of the length of
|
|
||||||
// R.
|
|
||||||
rLenOffset = 3 |
|
||||||
// rOffset is the byte offset within the signature of R.
|
|
||||||
rOffset = 4 |
|
||||||
) |
|
||||||
// The signature must adhere to the minimum and maximum allowed length.
|
|
||||||
sigLen := len(sig) |
|
||||||
if sigLen < minSigLen { |
|
||||||
str := fmt.Sprintf( |
|
||||||
"malformed signature: too short: %d < %d", sigLen, |
|
||||||
minSigLen, |
|
||||||
) |
|
||||||
return nil, signatureError(ErrSigTooShort, str) |
|
||||||
} |
|
||||||
if sigLen > maxSigLen { |
|
||||||
str := fmt.Sprintf( |
|
||||||
"malformed signature: too long: %d > %d", sigLen, |
|
||||||
maxSigLen, |
|
||||||
) |
|
||||||
return nil, signatureError(ErrSigTooLong, str) |
|
||||||
} |
|
||||||
// The signature must start with the ASN.1 sequence identifier.
|
|
||||||
if sig[sequenceOffset] != asn1SequenceID { |
|
||||||
str := fmt.Sprintf( |
|
||||||
"malformed signature: format has wrong type: %#x", |
|
||||||
sig[sequenceOffset], |
|
||||||
) |
|
||||||
return nil, signatureError(ErrSigInvalidSeqID, str) |
|
||||||
} |
|
||||||
// The signature must indicate the correct amount of data for all elements
|
|
||||||
// related to R and S.
|
|
||||||
if int(sig[dataLenOffset]) != sigLen-2 { |
|
||||||
str := fmt.Sprintf( |
|
||||||
"malformed signature: bad length: %d != %d", |
|
||||||
sig[dataLenOffset], sigLen-2, |
|
||||||
) |
|
||||||
return nil, signatureError(ErrSigInvalidDataLen, str) |
|
||||||
} |
|
||||||
// Calculate the offsets of the elements related to S and ensure S is inside
|
|
||||||
// the signature.
|
|
||||||
//
|
|
||||||
// rLen specifies the length of the big-endian encoded number which
|
|
||||||
// represents the R value of the signature.
|
|
||||||
//
|
|
||||||
// sTypeOffset is the offset of the ASN.1 identifier for S and, like its R
|
|
||||||
// counterpart, is expected to indicate an ASN.1 integer.
|
|
||||||
//
|
|
||||||
// sLenOffset and sOffset are the byte offsets within the signature of the
|
|
||||||
// length of S and S itself, respectively.
|
|
||||||
rLen := int(sig[rLenOffset]) |
|
||||||
sTypeOffset := rOffset + rLen |
|
||||||
sLenOffset := sTypeOffset + 1 |
|
||||||
if sTypeOffset >= sigLen { |
|
||||||
str := "malformed signature: S type indicator missing" |
|
||||||
return nil, signatureError(ErrSigMissingSTypeID, str) |
|
||||||
} |
|
||||||
if sLenOffset >= sigLen { |
|
||||||
str := "malformed signature: S length missing" |
|
||||||
return nil, signatureError(ErrSigMissingSLen, str) |
|
||||||
} |
|
||||||
// The lengths of R and S must match the overall length of the signature.
|
|
||||||
//
|
|
||||||
// sLen specifies the length of the big-endian encoded number which
|
|
||||||
// represents the S value of the signature.
|
|
||||||
sOffset := sLenOffset + 1 |
|
||||||
sLen := int(sig[sLenOffset]) |
|
||||||
if sOffset+sLen != sigLen { |
|
||||||
str := "malformed signature: invalid S length" |
|
||||||
return nil, signatureError(ErrSigInvalidSLen, str) |
|
||||||
} |
|
||||||
// R elements must be ASN.1 integers.
|
|
||||||
if sig[rTypeOffset] != asn1IntegerID { |
|
||||||
str := fmt.Sprintf( |
|
||||||
"malformed signature: R integer marker: %#x != %#x", |
|
||||||
sig[rTypeOffset], asn1IntegerID, |
|
||||||
) |
|
||||||
return nil, signatureError(ErrSigInvalidRIntID, str) |
|
||||||
} |
|
||||||
// Zero-length integers are not allowed for R.
|
|
||||||
if rLen == 0 { |
|
||||||
str := "malformed signature: R length is zero" |
|
||||||
return nil, signatureError(ErrSigZeroRLen, str) |
|
||||||
} |
|
||||||
// R must not be negative.
|
|
||||||
if sig[rOffset]&0x80 != 0 { |
|
||||||
str := "malformed signature: R is negative" |
|
||||||
return nil, signatureError(ErrSigNegativeR, str) |
|
||||||
} |
|
||||||
// Null bytes at the start of R are not allowed, unless R would otherwise be
|
|
||||||
// interpreted as a negative number.
|
|
||||||
if rLen > 1 && sig[rOffset] == 0x00 && sig[rOffset+1]&0x80 == 0 { |
|
||||||
str := "malformed signature: R value has too much padding" |
|
||||||
return nil, signatureError(ErrSigTooMuchRPadding, str) |
|
||||||
} |
|
||||||
// S elements must be ASN.1 integers.
|
|
||||||
if sig[sTypeOffset] != asn1IntegerID { |
|
||||||
str := fmt.Sprintf( |
|
||||||
"malformed signature: S integer marker: %#x != %#x", |
|
||||||
sig[sTypeOffset], asn1IntegerID, |
|
||||||
) |
|
||||||
return nil, signatureError(ErrSigInvalidSIntID, str) |
|
||||||
} |
|
||||||
// Zero-length integers are not allowed for S.
|
|
||||||
if sLen == 0 { |
|
||||||
str := "malformed signature: S length is zero" |
|
||||||
return nil, signatureError(ErrSigZeroSLen, str) |
|
||||||
} |
|
||||||
// S must not be negative.
|
|
||||||
if sig[sOffset]&0x80 != 0 { |
|
||||||
str := "malformed signature: S is negative" |
|
||||||
return nil, signatureError(ErrSigNegativeS, str) |
|
||||||
} |
|
||||||
// Null bytes at the start of S are not allowed, unless S would otherwise be
|
|
||||||
// interpreted as a negative number.
|
|
||||||
if sLen > 1 && sig[sOffset] == 0x00 && sig[sOffset+1]&0x80 == 0 { |
|
||||||
str := "malformed signature: S value has too much padding" |
|
||||||
return nil, signatureError(ErrSigTooMuchSPadding, str) |
|
||||||
} |
|
||||||
// The signature is validly encoded per DER at this point, however, enforce
|
|
||||||
// additional restrictions to ensure R and S are in the range [1, N-1] since
|
|
||||||
// valid ECDSA signatures are required to be in that range per spec.
|
|
||||||
//
|
|
||||||
// Also note that while the overflow checks are required to make use of the
|
|
||||||
// specialized mod N scalar type, rejecting zero here is not strictly
|
|
||||||
// required because it is also checked when verifying the signature, but
|
|
||||||
// there really isn't a good reason not to fail early here on signatures
|
|
||||||
// that do not conform to the ECDSA spec.
|
|
||||||
//
|
|
||||||
// Strip leading zeroes from R.
|
|
||||||
rBytes := sig[rOffset : rOffset+rLen] |
|
||||||
for len(rBytes) > 0 && rBytes[0] == 0x00 { |
|
||||||
rBytes = rBytes[1:] |
|
||||||
} |
|
||||||
// R must be in the range [1, N-1]. Notice the check for the maximum number
|
|
||||||
// of bytes is required because SetByteSlice truncates as noted in its
|
|
||||||
// comment so it could otherwise fail to detect the overflow.
|
|
||||||
var r secp256k1.ModNScalar |
|
||||||
if len(rBytes) > 32 { |
|
||||||
str := "invalid signature: R is larger than 256 bits" |
|
||||||
return nil, signatureError(ErrSigRTooBig, str) |
|
||||||
} |
|
||||||
if overflow := r.SetByteSlice(rBytes); overflow { |
|
||||||
str := "invalid signature: R >= group order" |
|
||||||
return nil, signatureError(ErrSigRTooBig, str) |
|
||||||
} |
|
||||||
if r.IsZero() { |
|
||||||
str := "invalid signature: R is 0" |
|
||||||
return nil, signatureError(ErrSigRIsZero, str) |
|
||||||
} |
|
||||||
// Strip leading zeroes from S.
|
|
||||||
sBytes := sig[sOffset : sOffset+sLen] |
|
||||||
for len(sBytes) > 0 && sBytes[0] == 0x00 { |
|
||||||
sBytes = sBytes[1:] |
|
||||||
} |
|
||||||
// S must be in the range [1, N-1]. Notice the check for the maximum number
|
|
||||||
// of bytes is required because SetByteSlice truncates as noted in its
|
|
||||||
// comment so it could otherwise fail to detect the overflow.
|
|
||||||
var s secp256k1.ModNScalar |
|
||||||
if len(sBytes) > 32 { |
|
||||||
str := "invalid signature: S is larger than 256 bits" |
|
||||||
return nil, signatureError(ErrSigSTooBig, str) |
|
||||||
} |
|
||||||
if overflow := s.SetByteSlice(sBytes); overflow { |
|
||||||
str := "invalid signature: S >= group order" |
|
||||||
return nil, signatureError(ErrSigSTooBig, str) |
|
||||||
} |
|
||||||
if s.IsZero() { |
|
||||||
str := "invalid signature: S is 0" |
|
||||||
return nil, signatureError(ErrSigSIsZero, str) |
|
||||||
} |
|
||||||
// Create and return the signature.
|
|
||||||
return NewSignature(&r, &s), nil |
|
||||||
} |
|
||||||
|
|
||||||
// sign generates an ECDSA signature over the secp256k1 curve for the provided
|
|
||||||
// hash (which should be the result of hashing a larger message) using the given
|
|
||||||
// nonce and secret key and returns it along with an additional public key
|
|
||||||
// recovery code and success indicator. Upon success, the produced signature is
|
|
||||||
// deterministic (same message, nonce, and key yield the same signature) and
|
|
||||||
// canonical in accordance with BIP0062.
|
|
||||||
//
|
|
||||||
// Note that signRFC6979 makes use of this function as it is the primary ECDSA
|
|
||||||
// signing logic. It differs in that it accepts a nonce to use when signing and
|
|
||||||
// may not successfully produce a valid signature for the given nonce. It is
|
|
||||||
// primarily separated for testing purposes.
|
|
||||||
func sign(secKey, nonce *secp256k1.ModNScalar, hash []byte) ( |
|
||||||
*Signature, byte, |
|
||||||
bool, |
|
||||||
) { |
|
||||||
// The algorithm for producing an ECDSA signature is given as algorithm 4.29
|
|
||||||
// in [GECC].
|
|
||||||
//
|
|
||||||
// The following is a paraphrased version for reference:
|
|
||||||
//
|
|
||||||
// G = curve generator
|
|
||||||
// N = curve order
|
|
||||||
// d = secret key
|
|
||||||
// m = message
|
|
||||||
// r, s = signature
|
|
||||||
//
|
|
||||||
// 1. Select random nonce k in [1, N-1]
|
|
||||||
// 2. Compute kG
|
|
||||||
// 3. r = kG.x mod N (kG.x is the x coordinate of the point kG)
|
|
||||||
// Repeat from step 1 if r = 0
|
|
||||||
// 4. e = H(m)
|
|
||||||
// 5. s = k^-1(e + dr) mod N
|
|
||||||
// Repeat from step 1 if s = 0
|
|
||||||
// 6. Return (r,s)
|
|
||||||
//
|
|
||||||
// This is slightly modified here to conform to RFC6979 and BIP 62 as
|
|
||||||
// follows:
|
|
||||||
//
|
|
||||||
// A. Instead of selecting a random nonce in step 1, use RFC6979 to generate
|
|
||||||
// a deterministic nonce in [1, N-1] parameterized by the secret key,
|
|
||||||
// message being signed, and an iteration count for the repeat cases
|
|
||||||
// B. Negate s calculated in step 5 if it is > N/2
|
|
||||||
// This is done because both s and its negation are valid signatures
|
|
||||||
// modulo the curve order N, so it forces a consistent choice to reduce
|
|
||||||
// signature malleability
|
|
||||||
//
|
|
||||||
// NOTE: Step 1 is performed by the caller.
|
|
||||||
//
|
|
||||||
// Step 2.
|
|
||||||
//
|
|
||||||
// Compute kG
|
|
||||||
//
|
|
||||||
// Note that the point must be in affine coordinates.
|
|
||||||
k := nonce |
|
||||||
var kG secp256k1.JacobianPoint |
|
||||||
secp256k1.ScalarBaseMultNonConst(k, &kG) |
|
||||||
kG.ToAffine() |
|
||||||
// Step 3.
|
|
||||||
//
|
|
||||||
// r = kG.x mod N
|
|
||||||
// Repeat from step 1 if r = 0
|
|
||||||
r, overflow := fieldToModNScalar(&kG.X) |
|
||||||
if r.IsZero() { |
|
||||||
return nil, 0, false |
|
||||||
} |
|
||||||
// Since the secp256k1 curve has a cofactor of 1, when recovering a
|
|
||||||
// public key from an ECDSA signature over it, there are four possible
|
|
||||||
// candidates corresponding to the following cases:
|
|
||||||
//
|
|
||||||
// 1) The X coord of the random point is < N and its Y coord even
|
|
||||||
// 2) The X coord of the random point is < N and its Y coord is odd
|
|
||||||
// 3) The X coord of the random point is >= N and its Y coord is even
|
|
||||||
// 4) The X coord of the random point is >= N and its Y coord is odd
|
|
||||||
//
|
|
||||||
// Rather than forcing the recovery procedure to check all possible
|
|
||||||
// cases, this creates a recovery code that uniquely identifies which of
|
|
||||||
// the cases apply by making use of 2 bits. Bit 0 identifies the
|
|
||||||
// oddness case and Bit 1 identifies the overflow case (aka when the X
|
|
||||||
// coord >= N).
|
|
||||||
//
|
|
||||||
// It is also worth noting that making use of Hasse's theorem shows
|
|
||||||
// there are around log_2((p-n)/p) ~= -127.65 ~= 1 in 2^127 points where
|
|
||||||
// the X coordinate is >= N. It is not possible to calculate these
|
|
||||||
// points since that would require breaking the ECDLP, but, in practice
|
|
||||||
// this strongly implies with extremely high probability that there are
|
|
||||||
// only a few actual points for which this case is true.
|
|
||||||
pubKeyRecoveryCode := byte(overflow<<1) | byte(kG.Y.IsOddBit()) |
|
||||||
// Step 4.
|
|
||||||
//
|
|
||||||
// e = H(m)
|
|
||||||
//
|
|
||||||
// Note that this actually sets e = H(m) mod N which is correct since
|
|
||||||
// it is only used in step 5 which itself is mod N.
|
|
||||||
var e secp256k1.ModNScalar |
|
||||||
e.SetByteSlice(hash) |
|
||||||
// Step 5 with modification B.
|
|
||||||
//
|
|
||||||
// s = k^-1(e + dr) mod N
|
|
||||||
// Repeat from step 1 if s = 0
|
|
||||||
// s = -s if s > N/2
|
|
||||||
kinv := new(secp256k1.ModNScalar).InverseValNonConst(k) |
|
||||||
s := new(secp256k1.ModNScalar).Mul2(secKey, &r).Add(&e).Mul(kinv) |
|
||||||
if s.IsZero() { |
|
||||||
return nil, 0, false |
|
||||||
} |
|
||||||
if s.IsOverHalfOrder() { |
|
||||||
s.Negate() |
|
||||||
// Negating s corresponds to the random point that would have been
|
|
||||||
// generated by -k (mod N), which necessarily has the opposite
|
|
||||||
// oddness since N is prime, thus flip the pubkey recovery code
|
|
||||||
// oddness bit accordingly.
|
|
||||||
pubKeyRecoveryCode ^= 0x01 |
|
||||||
} |
|
||||||
// Step 6.
|
|
||||||
//
|
|
||||||
// Return (r,s)
|
|
||||||
return NewSignature(&r, s), pubKeyRecoveryCode, true |
|
||||||
} |
|
||||||
|
|
||||||
// signRFC6979 generates a deterministic ECDSA signature according to RFC 6979
|
|
||||||
// and BIP0062 and returns it along with an additional public key recovery code
|
|
||||||
// for efficiently recovering the public key from the signature.
|
|
||||||
func signRFC6979(secKey *secp256k1.SecretKey, hash []byte) ( |
|
||||||
*Signature, |
|
||||||
byte, |
|
||||||
) { |
|
||||||
// The algorithm for producing an ECDSA signature is given as algorithm 4.29
|
|
||||||
// in [GECC].
|
|
||||||
//
|
|
||||||
// The following is a paraphrased version for reference:
|
|
||||||
//
|
|
||||||
// G = curve generator
|
|
||||||
// N = curve order
|
|
||||||
// d = secret key
|
|
||||||
// m = message
|
|
||||||
// r, s = signature
|
|
||||||
//
|
|
||||||
// 1. Select random nonce k in [1, N-1]
|
|
||||||
// 2. Compute kG
|
|
||||||
// 3. r = kG.x mod N (kG.x is the x coordinate of the point kG)
|
|
||||||
// Repeat from step 1 if r = 0
|
|
||||||
// 4. e = H(m)
|
|
||||||
// 5. s = k^-1(e + dr) mod N
|
|
||||||
// Repeat from step 1 if s = 0
|
|
||||||
// 6. Return (r,s)
|
|
||||||
//
|
|
||||||
// This is slightly modified here to conform to RFC6979 and BIP 62 as
|
|
||||||
// follows:
|
|
||||||
//
|
|
||||||
// A. Instead of selecting a random nonce in step 1, use RFC6979 to generate
|
|
||||||
// a deterministic nonce in [1, N-1] parameterized by the secret key,
|
|
||||||
// message being signed, and an iteration count for the repeat cases
|
|
||||||
// B. Negate s calculated in step 5 if it is > N/2
|
|
||||||
// This is done because both s and its negation are valid signatures
|
|
||||||
// modulo the curve order N, so it forces a consistent choice to reduce
|
|
||||||
// signature malleability
|
|
||||||
secKeyScalar := &secKey.Key |
|
||||||
var secKeyBytes [32]byte |
|
||||||
secKeyScalar.PutBytes(&secKeyBytes) |
|
||||||
defer zeroArray32(&secKeyBytes) |
|
||||||
for iteration := uint32(0); ; iteration++ { |
|
||||||
// Step 1 with modification A.
|
|
||||||
//
|
|
||||||
// Generate a deterministic nonce in [1, N-1] parameterized by the
|
|
||||||
// secret key, message being signed, and iteration count.
|
|
||||||
k := secp256k1.NonceRFC6979(secKeyBytes[:], hash, nil, nil, iteration) |
|
||||||
// Steps 2-6.
|
|
||||||
sig, pubKeyRecoveryCode, success := sign(secKeyScalar, k, hash) |
|
||||||
k.Zero() |
|
||||||
if !success { |
|
||||||
continue |
|
||||||
} |
|
||||||
return sig, pubKeyRecoveryCode |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// Sign generates an ECDSA signature over the secp256k1 curve for the provided
|
|
||||||
// hash (which should be the result of hashing a larger message) using the given
|
|
||||||
// secret key. The produced signature is deterministic (same message and same
|
|
||||||
// key yield the same signature) and canonical in accordance with RFC6979 and
|
|
||||||
// BIP0062.
|
|
||||||
func Sign(key *secp256k1.SecretKey, hash []byte) *Signature { |
|
||||||
signature, _ := signRFC6979(key, hash) |
|
||||||
return signature |
|
||||||
} |
|
||||||
|
|
||||||
const ( |
|
||||||
// compactSigSize is the size of a compact signature. It consists of a
|
|
||||||
// compact signature recovery code byte followed by the R and S components
|
|
||||||
// serialized as 32-byte big-endian values. 1+32*2 = 65.
|
|
||||||
// for the R and S components. 1+32+32=65.
|
|
||||||
compactSigSize = 65 |
|
||||||
// compactSigMagicOffset is a value used when creating the compact signature
|
|
||||||
// recovery code inherited from Bitcoin and has no meaning, but has been
|
|
||||||
// retained for compatibility. For historical purposes, it was originally
|
|
||||||
// picked to avoid a binary representation that would allow compact
|
|
||||||
// signatures to be mistaken for other components.
|
|
||||||
compactSigMagicOffset = 27 |
|
||||||
// compactSigCompPubKey is a value used when creating the compact signature
|
|
||||||
// recovery code to indicate the original public key was compressed.
|
|
||||||
compactSigCompPubKey = 4 |
|
||||||
// pubKeyRecoveryCodeOddnessBit specifies the bit that indicates the oddess
|
|
||||||
// of the Y coordinate of the random point calculated when creating a
|
|
||||||
// signature.
|
|
||||||
pubKeyRecoveryCodeOddnessBit = 1 << 0 |
|
||||||
// pubKeyRecoveryCodeOverflowBit specifies the bit that indicates the X
|
|
||||||
// coordinate of the random point calculated when creating a signature was
|
|
||||||
// >= N, where N is the order of the group.
|
|
||||||
pubKeyRecoveryCodeOverflowBit = 1 << 1 |
|
||||||
) |
|
||||||
|
|
||||||
// SignCompact produces a compact ECDSA signature over the secp256k1 curve for
|
|
||||||
// the provided hash (which should be the result of hashing a larger message)
|
|
||||||
// using the given secret key. The isCompressedKey parameter specifies if the
|
|
||||||
// produced signature should reference a compressed public key or not.
|
|
||||||
//
|
|
||||||
// Compact signature format:
|
|
||||||
// <1-byte compact sig recovery code><32-byte R><32-byte S>
|
|
||||||
//
|
|
||||||
// The compact sig recovery code is the value 27 + public key recovery code + 4
|
|
||||||
// if the compact signature was created with a compressed public key.
|
|
||||||
func SignCompact( |
|
||||||
key *secp256k1.SecretKey, hash []byte, |
|
||||||
isCompressedKey bool, |
|
||||||
) []byte { |
|
||||||
// Create the signature and associated pubkey recovery code and calculate
|
|
||||||
// the compact signature recovery code.
|
|
||||||
sig, pubKeyRecoveryCode := signRFC6979(key, hash) |
|
||||||
compactSigRecoveryCode := compactSigMagicOffset + pubKeyRecoveryCode |
|
||||||
if isCompressedKey { |
|
||||||
compactSigRecoveryCode += compactSigCompPubKey |
|
||||||
} |
|
||||||
// Output <compactSigRecoveryCode><32-byte R><32-byte S>.
|
|
||||||
var b [compactSigSize]byte |
|
||||||
b[0] = compactSigRecoveryCode |
|
||||||
sig.r.PutBytesUnchecked(b[1:33]) |
|
||||||
sig.s.PutBytesUnchecked(b[33:65]) |
|
||||||
return b[:] |
|
||||||
} |
|
||||||
|
|
||||||
// RecoverCompact attempts to recover the secp256k1 public key from the provided
|
|
||||||
// compact signature and message hash. It first verifies the signature, and, if
|
|
||||||
// the signature matches then the recovered public key will be returned as well
|
|
||||||
// as a boolean indicating whether or not the original key was compressed.
|
|
||||||
func RecoverCompact(signature, hash []byte) ( |
|
||||||
*secp256k1.PublicKey, bool, error, |
|
||||||
) { |
|
||||||
// The following is very loosely based on the information and algorithm that
|
|
||||||
// describes recovering a public key from and ECDSA signature in section
|
|
||||||
// 4.1.6 of [SEC1].
|
|
||||||
//
|
|
||||||
// Given the following parameters:
|
|
||||||
//
|
|
||||||
// G = curve generator
|
|
||||||
// N = group order
|
|
||||||
// P = field prime
|
|
||||||
// Q = public key
|
|
||||||
// m = message
|
|
||||||
// e = hash of the message
|
|
||||||
// r, s = signature
|
|
||||||
// X = random point used when creating signature whose x coordinate is r
|
|
||||||
//
|
|
||||||
// The equation to recover a public key candidate from an ECDSA signature
|
|
||||||
// is:
|
|
||||||
// Q = r^-1(sX - eG).
|
|
||||||
//
|
|
||||||
// This can be verified by plugging it in for Q in the sig verification
|
|
||||||
// equation:
|
|
||||||
// X = s^-1(eG + rQ) (mod N)
|
|
||||||
// => s^-1(eG + r(r^-1(sX - eG))) (mod N)
|
|
||||||
// => s^-1(eG + sX - eG) (mod N)
|
|
||||||
// => s^-1(sX) (mod N)
|
|
||||||
// => X (mod N)
|
|
||||||
//
|
|
||||||
// However, note that since r is the x coordinate mod N from a random point
|
|
||||||
// that was originally mod P, and the cofactor of the secp256k1 curve is 1,
|
|
||||||
// there are four possible points that the original random point could have
|
|
||||||
// been to produce r: (r,y), (r,-y), (r+N,y), and (r+N,-y). At least 2 of
|
|
||||||
// those points will successfully verify, and all 4 will successfully verify
|
|
||||||
// when the original x coordinate was in the range [N+1, P-1], but in any
|
|
||||||
// case, only one of them corresponds to the original secret key used.
|
|
||||||
//
|
|
||||||
// The method described by section 4.1.6 of [SEC1] to determine which one is
|
|
||||||
// the correct one involves calculating each possibility as a candidate
|
|
||||||
// public key and comparing the candidate to the authentic public key. It
|
|
||||||
// also hints that it is possible to generate the signature in a such a
|
|
||||||
// way that only one of the candidate public keys is viable.
|
|
||||||
//
|
|
||||||
// A more efficient approach that is specific to the secp256k1 curve is used
|
|
||||||
// here instead which is to produce a "pubkey recovery code" when signing
|
|
||||||
// that uniquely identifies which of the 4 possibilities is correct for the
|
|
||||||
// original random point and using that to recover the pubkey directly as
|
|
||||||
// follows:
|
|
||||||
//
|
|
||||||
// 1. Fail if r and s are not in [1, N-1]
|
|
||||||
// 2. Convert r to integer mod P
|
|
||||||
// 3. If pubkey recovery code overflow bit is set:
|
|
||||||
// 3.1 Fail if r + N >= P
|
|
||||||
// 3.2 r = r + N (mod P)
|
|
||||||
// 4. y = +sqrt(r^3 + 7) (mod P)
|
|
||||||
// 4.1 Fail if y does not exist
|
|
||||||
// 4.2 y = -y if needed to match pubkey recovery code oddness bit
|
|
||||||
// 5. X = (r, y)
|
|
||||||
// 6. e = H(m) mod N
|
|
||||||
// 7. w = r^-1 mod N
|
|
||||||
// 8. u1 = -(e * w) mod N
|
|
||||||
// u2 = s * w mod N
|
|
||||||
// 9. Q = u1G + u2X
|
|
||||||
// 10. Fail if Q is the point at infinity
|
|
||||||
//
|
|
||||||
// A compact signature consists of a recovery byte followed by the R and
|
|
||||||
// S components serialized as 32-byte big-endian values.
|
|
||||||
if len(signature) != compactSigSize { |
|
||||||
str := fmt.Sprintf( |
|
||||||
"malformed signature: wrong size: %d != %d", |
|
||||||
len(signature), compactSigSize, |
|
||||||
) |
|
||||||
return nil, false, signatureError(ErrSigInvalidLen, str) |
|
||||||
} |
|
||||||
// Parse and validate the compact signature recovery code.
|
|
||||||
const ( |
|
||||||
minValidCode = compactSigMagicOffset |
|
||||||
maxValidCode = compactSigMagicOffset + compactSigCompPubKey + 3 |
|
||||||
) |
|
||||||
sigRecoveryCode := signature[0] |
|
||||||
if sigRecoveryCode < minValidCode || sigRecoveryCode > maxValidCode { |
|
||||||
str := fmt.Sprintf( |
|
||||||
"invalid signature: public key recovery code %d is "+ |
|
||||||
"not in the valid range [%d, %d]", sigRecoveryCode, |
|
||||||
minValidCode, |
|
||||||
maxValidCode, |
|
||||||
) |
|
||||||
return nil, false, signatureError(ErrSigInvalidRecoveryCode, str) |
|
||||||
} |
|
||||||
sigRecoveryCode -= compactSigMagicOffset |
|
||||||
wasCompressed := sigRecoveryCode&compactSigCompPubKey != 0 |
|
||||||
pubKeyRecoveryCode := sigRecoveryCode & 3 |
|
||||||
// Step 1.
|
|
||||||
//
|
|
||||||
// Parse and validate the R and S signature components.
|
|
||||||
//
|
|
||||||
// Fail if r and s are not in [1, N-1].
|
|
||||||
var r, s secp256k1.ModNScalar |
|
||||||
if overflow := r.SetByteSlice(signature[1:33]); overflow { |
|
||||||
str := "invalid signature: R >= group order" |
|
||||||
return nil, false, signatureError(ErrSigRTooBig, str) |
|
||||||
} |
|
||||||
if r.IsZero() { |
|
||||||
str := "invalid signature: R is 0" |
|
||||||
return nil, false, signatureError(ErrSigRIsZero, str) |
|
||||||
} |
|
||||||
if overflow := s.SetByteSlice(signature[33:]); overflow { |
|
||||||
str := "invalid signature: S >= group order" |
|
||||||
return nil, false, signatureError(ErrSigSTooBig, str) |
|
||||||
} |
|
||||||
if s.IsZero() { |
|
||||||
str := "invalid signature: S is 0" |
|
||||||
return nil, false, signatureError(ErrSigSIsZero, str) |
|
||||||
} |
|
||||||
// Step 2.
|
|
||||||
//
|
|
||||||
// Convert r to integer mod P.
|
|
||||||
fieldR := modNScalarToField(&r) |
|
||||||
// Step 3.
|
|
||||||
//
|
|
||||||
// If pubkey recovery code overflow bit is set:
|
|
||||||
if pubKeyRecoveryCode&pubKeyRecoveryCodeOverflowBit != 0 { |
|
||||||
// Step 3.1.
|
|
||||||
//
|
|
||||||
// Fail if r + N >= P
|
|
||||||
//
|
|
||||||
// Either the signature or the recovery code must be invalid if the
|
|
||||||
// recovery code overflow bit is set and adding N to the R component
|
|
||||||
// would exceed the field prime since R originally came from the X
|
|
||||||
// coordinate of a random point on the curve.
|
|
||||||
if fieldR.IsGtOrEqPrimeMinusOrder() { |
|
||||||
str := "invalid signature: signature R + N >= P" |
|
||||||
return nil, false, signatureError(ErrSigOverflowsPrime, str) |
|
||||||
} |
|
||||||
// Step 3.2.
|
|
||||||
//
|
|
||||||
// r = r + N (mod P)
|
|
||||||
fieldR.Add(&orderAsFieldVal) |
|
||||||
} |
|
||||||
// Step 4.
|
|
||||||
//
|
|
||||||
// y = +sqrt(r^3 + 7) (mod P)
|
|
||||||
// Fail if y does not exist.
|
|
||||||
// y = -y if needed to match pubkey recovery code oddness bit
|
|
||||||
//
|
|
||||||
// The signature must be invalid if the calculation fails because the X
|
|
||||||
// coord originally came from a random point on the curve which means there
|
|
||||||
// must be a Y coord that satisfies the equation for a valid signature.
|
|
||||||
oddY := pubKeyRecoveryCode&pubKeyRecoveryCodeOddnessBit != 0 |
|
||||||
var y secp256k1.FieldVal |
|
||||||
if valid := secp256k1.DecompressY(&fieldR, oddY, &y); !valid { |
|
||||||
str := "invalid signature: not for a valid curve point" |
|
||||||
return nil, false, signatureError(ErrPointNotOnCurve, str) |
|
||||||
} |
|
||||||
// Step 5.
|
|
||||||
//
|
|
||||||
// X = (r, y)
|
|
||||||
var X secp256k1.JacobianPoint |
|
||||||
X.X.Set(fieldR.Normalize()) |
|
||||||
X.Y.Set(y.Normalize()) |
|
||||||
X.Z.SetInt(1) |
|
||||||
// Step 6.
|
|
||||||
//
|
|
||||||
// e = H(m) mod N
|
|
||||||
var e secp256k1.ModNScalar |
|
||||||
e.SetByteSlice(hash) |
|
||||||
// Step 7.
|
|
||||||
//
|
|
||||||
// w = r^-1 mod N
|
|
||||||
w := new(secp256k1.ModNScalar).InverseValNonConst(&r) |
|
||||||
// Step 8.
|
|
||||||
//
|
|
||||||
// u1 = -(e * w) mod N
|
|
||||||
// u2 = s * w mod N
|
|
||||||
u1 := new(secp256k1.ModNScalar).Mul2(&e, w).Negate() |
|
||||||
u2 := new(secp256k1.ModNScalar).Mul2(&s, w) |
|
||||||
// Step 9.
|
|
||||||
//
|
|
||||||
// Q = u1G + u2X
|
|
||||||
var Q, u1G, u2X secp256k1.JacobianPoint |
|
||||||
secp256k1.ScalarBaseMultNonConst(u1, &u1G) |
|
||||||
secp256k1.ScalarMultNonConst(u2, &X, &u2X) |
|
||||||
secp256k1.AddNonConst(&u1G, &u2X, &Q) |
|
||||||
// Step 10.
|
|
||||||
//
|
|
||||||
// Fail if Q is the point at infinity.
|
|
||||||
//
|
|
||||||
// Either the signature or the pubkey recovery code must be invalid if the
|
|
||||||
// recovered pubkey is the point at infinity.
|
|
||||||
if (Q.X.IsZero() && Q.Y.IsZero()) || Q.Z.IsZero() { |
|
||||||
str := "invalid signature: recovered pubkey is the point at infinity" |
|
||||||
return nil, false, signatureError(ErrPointNotOnCurve, str) |
|
||||||
} |
|
||||||
// Notice that the public key is in affine coordinates.
|
|
||||||
Q.ToAffine() |
|
||||||
pubKey := secp256k1.NewPublicKey(&Q.X, &Q.Y) |
|
||||||
return pubKey, wasCompressed, nil |
|
||||||
} |
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,24 +0,0 @@ |
|||||||
// Copyright (c) 2013-2021 The btcsuite developers
|
|
||||||
// Copyright (c) 2015-2021 The Decred developers
|
|
||||||
|
|
||||||
package btcec |
|
||||||
|
|
||||||
import ( |
|
||||||
"next.orly.dev/pkg/crypto/ec/secp256k1" |
|
||||||
) |
|
||||||
|
|
||||||
// Error identifies an error related to public key cryptography using a
|
|
||||||
// sec256k1 curve. It has full support for errors.Is and errors.As, so the
|
|
||||||
// caller can ascertain the specific reason for the error by checking the
|
|
||||||
// underlying error.
|
|
||||||
type Error = secp256k1.Error |
|
||||||
|
|
||||||
// ErrorKind identifies a kind of error. It has full support for errors.Is and
|
|
||||||
// errors.As, so the caller can directly check against an error kind when
|
|
||||||
// determining the reason for an error.
|
|
||||||
type ErrorKind = secp256k1.ErrorKind |
|
||||||
|
|
||||||
// makeError creates an secp256k1.Error given a set of arguments.
|
|
||||||
func makeError(kind ErrorKind, desc string) Error { |
|
||||||
return Error{Err: kind, Description: desc} |
|
||||||
} |
|
||||||
@ -1,45 +0,0 @@ |
|||||||
package btcec |
|
||||||
|
|
||||||
import ( |
|
||||||
"next.orly.dev/pkg/crypto/ec/secp256k1" |
|
||||||
) |
|
||||||
|
|
||||||
// FieldVal implements optimized fixed-precision arithmetic over the secp256k1
|
|
||||||
// finite field. This means all arithmetic is performed modulo
|
|
||||||
// '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f'.
|
|
||||||
//
|
|
||||||
// WARNING: Since it is so important for the field arithmetic to be extremely
|
|
||||||
// fast for high performance crypto, this type does not perform any validation
|
|
||||||
// of documented preconditions where it ordinarily would. As a result, it is
|
|
||||||
// IMPERATIVE for callers to understand some key concepts that are described
|
|
||||||
// below and ensure the methods are called with the necessary preconditions
|
|
||||||
// that each method is documented with. For example, some methods only give the
|
|
||||||
// correct result if the field value is normalized and others require the field
|
|
||||||
// values involved to have a maximum magnitude and THERE ARE NO EXPLICIT CHECKS
|
|
||||||
// TO ENSURE THOSE PRECONDITIONS ARE SATISFIED. This does, unfortunately, make
|
|
||||||
// the type more difficult to use correctly and while I typically prefer to
|
|
||||||
// ensure all state and input is valid for most code, this is a bit of an
|
|
||||||
// exception because those extra checks really add up in what ends up being
|
|
||||||
// critical hot paths.
|
|
||||||
//
|
|
||||||
// The first key concept when working with this type is normalization. In order
|
|
||||||
// to avoid the need to propagate a ton of carries, the internal representation
|
|
||||||
// provides additional overflow bits for each word of the overall 256-bit
|
|
||||||
// value. This means that there are multiple internal representations for the
|
|
||||||
// same value and, as a result, any methods that rely on comparison of the
|
|
||||||
// value, such as equality and oddness determination, require the caller to
|
|
||||||
// provide a normalized value.
|
|
||||||
//
|
|
||||||
// The second key concept when working with this type is magnitude. As
|
|
||||||
// previously mentioned, the internal representation provides additional
|
|
||||||
// overflow bits which means that the more math operations that are performed
|
|
||||||
// on the field value between normalizations, the more those overflow bits
|
|
||||||
// accumulate. The magnitude is effectively that maximum possible number of
|
|
||||||
// those overflow bits that could possibly be required as a result of a given
|
|
||||||
// operation. Since there are only a limited number of overflow bits available,
|
|
||||||
// this implies that the max possible magnitude MUST be tracked by the caller
|
|
||||||
// and the caller MUST normalize the field value if a given operation would
|
|
||||||
// cause the magnitude of the result to exceed the max allowed value.
|
|
||||||
//
|
|
||||||
// IMPORTANT: The max allowed magnitude of a field value is 64.
|
|
||||||
type FieldVal = secp256k1.FieldVal |
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue