Merge pull request #579 from nspcc-dev/refactor-crypto

This moves some functionality into micro-packages, improves testing,
unexports some code and fixes bugs along the way.
This commit is contained in:
Roman Khimov 2019-12-25 18:12:50 +03:00 committed by GitHub
commit 9145855d2c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 284 additions and 157 deletions

View file

@ -9,6 +9,7 @@ import (
"github.com/CityOfZion/neo-go/config"
"github.com/CityOfZion/neo-go/pkg/core"
"github.com/CityOfZion/neo-go/pkg/core/storage"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/network"
"github.com/CityOfZion/neo-go/pkg/network/metrics"
@ -361,6 +362,9 @@ func initBlockChain(cfg config.Config) (*core.Blockchain, error) {
if err != nil {
return nil, cli.NewExitError(fmt.Errorf("could not initialize blockchain: %s", err), 1)
}
if cfg.ProtocolConfiguration.AddressVersion != 0 {
address.Prefix = cfg.ProtocolConfiguration.AddressVersion
}
return chain, nil
}

View file

@ -43,7 +43,7 @@ type (
// ProtocolConfiguration represents the protocol config.
ProtocolConfiguration struct {
Magic NetMode `yaml:"Magic"`
AddressVersion int64 `yaml:"AddressVersion"`
AddressVersion byte `yaml:"AddressVersion"`
SecondsPerBlock int `yaml:"SecondsPerBlock"`
LowPriorityThreshold float64 `yaml:"LowPriorityThreshold"`
MaxTransactionsPerBlock int64 `yaml:"MaxTransactionsPerBlock"`

View file

@ -8,8 +8,8 @@ import (
"github.com/CityOfZion/neo-go/pkg/core"
"github.com/CityOfZion/neo-go/pkg/core/storage"
"github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/network"
"github.com/CityOfZion/neo-go/pkg/rpc"
"github.com/stretchr/testify/require"
@ -72,7 +72,7 @@ func getWif(t *testing.B) *keys.WIF {
// getTX returns Invocation transaction with some random attributes in order to have different hashes.
func getTX(t *testing.B, wif *keys.WIF) *transaction.Transaction {
fromAddress := wif.PrivateKey.Address()
fromAddressHash, err := crypto.Uint160DecodeAddress(fromAddress)
fromAddressHash, err := address.StringToUint160(fromAddress)
require.NoError(t, err)
tx := &transaction.Transaction{

View file

@ -11,7 +11,7 @@ import (
"strconv"
"strings"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
)
@ -674,7 +674,7 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
// contain double quotes that need to be stripped.
addressStr := expr.Args[0].(*ast.BasicLit).Value
addressStr = strings.Replace(addressStr, "\"", "", 2)
uint160, err := crypto.Uint160DecodeAddress(addressStr)
uint160, err := address.StringToUint160(addressStr)
if err != nil {
c.prog.Err = err
return

View file

@ -5,7 +5,7 @@ import (
"fmt"
"github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/Workiva/go-datastructures/queue"
@ -30,13 +30,13 @@ func (b *Block) Header() *Header {
}
}
func merkleTreeFromTransactions(txes []*transaction.Transaction) (*crypto.MerkleTree, error) {
func merkleTreeFromTransactions(txes []*transaction.Transaction) (*hash.MerkleTree, error) {
hashes := make([]util.Uint256, len(txes))
for i, tx := range txes {
hashes[i] = tx.Hash()
}
return crypto.NewMerkleTree(hashes)
return hash.NewMerkleTree(hashes)
}
// rebuildMerkleRoot rebuilds the merkleroot of the block.

View file

@ -5,8 +5,8 @@ import (
"testing"
"github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/stretchr/testify/assert"
)
@ -34,7 +34,7 @@ func TestDecodeBlock1(t *testing.T) {
assert.Equal(t, data["hash"].(string), block.Hash().StringLE())
assert.Equal(t, data["previousblockhash"].(string), block.PrevHash.StringLE())
assert.Equal(t, data["merkleroot"].(string), block.MerkleRoot.StringLE())
assert.Equal(t, data["nextconsensus"].(string), crypto.AddressFromUint160(block.NextConsensus))
assert.Equal(t, data["nextconsensus"].(string), address.Uint160ToString(block.NextConsensus))
script := data["script"].(map[string]interface{})
assert.Equal(t, script["invocation"].(string), hex.EncodeToString(block.Script.InvocationScript))
@ -273,7 +273,7 @@ func TestBlockSizeCalculation(t *testing.T) {
assert.Equal(t, 1527894405, int(b.Timestamp))
assert.Equal(t, 2340363, int(b.Index))
nextConsensus := crypto.AddressFromUint160(b.NextConsensus)
nextConsensus := address.Uint160ToString(b.NextConsensus)
assert.Equal(t, "APyEx5f4Zm4oCHwFWiSTaph1fPBxZacYVR", nextConsensus)
assert.Equal(t, "4012afae6df64195041e4764b57caa9e27fc2cfc596833163904136ec95816d104b44b3737d0e9f6b1b4445cd3b6a5cc80f6b0935675bc44dba44415eb309832b3404dc95bcf85e4635556a1d618e4ce947b26972992ed74788df5f9501b850ac0b40b7112d1ff30e4ade00369e16f0d13932d1ba76725e7682db072f8e2cd7752b840d12bb7dd45dd3b0e2098db5c67b6de55b7c40164937491fcaca1239b25860251224ead23ab232add78ccccd347239eae50ffc98f50b2a84c60ec5c3d284647a7406fabf6ca241b759af6b71080c0dfad7395632e989226a7e52f8cd2c133aeb2226e6e1aea47666fd81f578405a9f9bbd9d0bc523c3a44d7a5099ddc649feabe5f406188b8ee478731a89beeb76fdbd108eb0071b8f2b8678f40c5a1f387a491314336783255dee8cc5af4bf914dfeaacecc318fc13e02262658e39e8ce0631941b1", hex.EncodeToString(b.Script.InvocationScript))

View file

@ -3,7 +3,7 @@ package transaction
import (
"encoding/json"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/util"
)
@ -52,7 +52,7 @@ func (out *Output) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"asset": out.AssetID,
"value": out.Amount,
"address": crypto.AddressFromUint160(out.ScriptHash),
"address": address.Uint160ToString(out.ScriptHash),
"n": out.Position,
})
}

View file

@ -4,8 +4,8 @@ import (
"encoding/hex"
"testing"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/stretchr/testify/assert"
@ -58,7 +58,7 @@ func TestDecodeRegisterTXFromRawString(t *testing.T) {
assert.Equal(t, util.Fixed8FromInt64(100000000), txData.Amount)
assert.Equal(t, uint8(0), txData.Precision)
assert.Equal(t, keys.PublicKey{}, txData.Owner)
assert.Equal(t, "Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt", crypto.AddressFromUint160(txData.Admin))
assert.Equal(t, "Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt", address.Uint160ToString(txData.Admin))
assert.Equal(t, "c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b", tx.Hash().StringLE())
buf := io.NewBufBinWriter()

View file

@ -4,7 +4,7 @@ import (
"encoding/hex"
"testing"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/smartcontract"
"github.com/CityOfZion/neo-go/pkg/util"
@ -51,8 +51,7 @@ func TestDecodeEncodeClaimTX(t *testing.T) {
assert.Equal(t, 0, len(tx.Attributes))
assert.Equal(t, 0, len(tx.Inputs))
assert.Equal(t, 1, len(tx.Outputs))
address := crypto.AddressFromUint160(tx.Outputs[0].ScriptHash)
assert.Equal(t, "AQJseD8iBmCD4sgfHRhMahmoi9zvopG6yz", address)
assert.Equal(t, "AQJseD8iBmCD4sgfHRhMahmoi9zvopG6yz", address.Uint160ToString(tx.Outputs[0].ScriptHash))
assert.Equal(t, "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7", tx.Outputs[0].AssetID.StringLE())
assert.Equal(t, tx.Outputs[0].Amount.String(), "0.06247739")
invoc := "40456349cec43053009accdb7781b0799c6b591c812768804ab0a0b56b5eae7a97694227fcd33e70899c075848b2cee8fae733faac6865b484d3f7df8949e2aadb"

View file

@ -4,7 +4,7 @@ import (
"testing"
"github.com/CityOfZion/neo-go/config"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/stretchr/testify/assert"
)
@ -45,7 +45,7 @@ func TestGetConsensusAddressMainNet(t *testing.T) {
}
assert.Equal(t, consensusScript, script.String())
assert.Equal(t, consensusAddr, crypto.AddressFromUint160(script))
assert.Equal(t, consensusAddr, address.Uint160ToString(script))
}
func TestUtilityTokenTX(t *testing.T) {

View file

@ -1,23 +0,0 @@
package crypto
import (
"github.com/CityOfZion/neo-go/pkg/util"
)
// AddressFromUint160 returns the "NEO address" from the given
// Uint160.
func AddressFromUint160(u util.Uint160) string {
// Dont forget to prepend the Address version 0x17 (23) A
b := append([]byte{0x17}, u.BytesBE()...)
return Base58CheckEncode(b)
}
// Uint160DecodeAddress attempts to decode the given NEO address string
// into an Uint160.
func Uint160DecodeAddress(s string) (u util.Uint160, err error) {
b, err := Base58CheckDecode(s)
if err != nil {
return u, err
}
return util.Uint160DecodeBytesBE(b[1:21])
}

View file

@ -1,9 +1,8 @@
package crypto
package hash
import (
"errors"
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
"github.com/CityOfZion/neo-go/pkg/util"
)
@ -26,13 +25,8 @@ func NewMerkleTree(hashes []util.Uint256) (*MerkleTree, error) {
}
}
root, err := buildMerkleTree(nodes)
if err != nil {
return nil, err
}
return &MerkleTree{
root: root,
root: buildMerkleTree(nodes),
depth: 1,
}, nil
}
@ -42,12 +36,12 @@ func (t *MerkleTree) Root() util.Uint256 {
return t.root.hash
}
func buildMerkleTree(leaves []*MerkleTreeNode) (*MerkleTreeNode, error) {
func buildMerkleTree(leaves []*MerkleTreeNode) *MerkleTreeNode {
if len(leaves) == 0 {
return nil, errors.New("length of the leaves cannot be zero")
panic("length of leaves cannot be zero")
}
if len(leaves) == 1 {
return leaves[0], nil
return leaves[0]
}
parents := make([]*MerkleTreeNode, (len(leaves)+1)/2)
@ -66,7 +60,7 @@ func buildMerkleTree(leaves []*MerkleTreeNode) (*MerkleTreeNode, error) {
b1 := parents[i].leftChild.hash.BytesBE()
b2 := parents[i].rightChild.hash.BytesBE()
b1 = append(b1, b2...)
parents[i].hash = hash.DoubleSha256(b1)
parents[i].hash = DoubleSha256(b1)
}
return buildMerkleTree(parents)

View file

@ -0,0 +1,103 @@
package hash
import (
"testing"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func testComputeMerkleTree(t *testing.T, hexHashes []string, result string) {
hashes := make([]util.Uint256, len(hexHashes))
for i, str := range hexHashes {
hash, err := util.Uint256DecodeStringLE(str)
require.NoError(t, err)
hashes[i] = hash
}
merkle, err := NewMerkleTree(hashes)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, result, merkle.Root().StringLE())
assert.Equal(t, true, merkle.root.IsRoot())
assert.Equal(t, false, merkle.root.IsLeaf())
leaf := merkle.root
for leaf.leftChild != nil || leaf.rightChild != nil {
if leaf.leftChild != nil {
leaf = leaf.leftChild
continue
}
leaf = leaf.rightChild
}
assert.Equal(t, true, leaf.IsLeaf())
assert.Equal(t, false, leaf.IsRoot())
}
func TestComputeMerkleTree1(t *testing.T) {
// Mainnet block #0
rawHashes := []string{
"fb5bd72b2d6792d75dc2f1084ffa9e9f70ca85543c717a6b13d9959b452a57d6",
"c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b",
"602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7",
"3631f66024ca6f5b033d7e0809eb993443374830025af904fb51b0334f127cda",
}
res := "803ff4abe3ea6533bcc0be574efa02f83ae8fdc651c879056b0d9be336c01bf4"
testComputeMerkleTree(t, rawHashes, res)
}
func TestComputeMerkleTree2(t *testing.T) {
// Mainnet block #4635525
rawHashes := []string{
"c832ef573136eae2c57a35989d3fb9b3a135d08ffa0faa49d8766f7c1a92ca0f",
"b8606dfeb126a5963d6674f8dbfb786db7f6c27800167c3eef56ff7110ff0ffc",
"498a5d58179002dd9db7b23df657ecf7e1b2e8218bd48dda035e5accc664830a",
"5c350282b448c139adb1f5e3fba0e9326476a38c01ea88876ebc4a882c472d42",
"cea31cc85e7310183561d4f420026984ba48354516f9274c44b52c7f9a5c6107",
"744f985dd5ad6f4ad6376376b48552abf7755b2ebc5c6271950714f848d1cc3a",
"02c5fc225b6ead91f73a7b3ebb19bb30a113baea60f439b654c2811d630a2c48",
"2b3478e0fa91db3a309caeb4d9739f38233c1c189d6fa7e159e24afce9fae082",
"4d50693cee3ac2c976c092620834d4da264583cf15a1d11dd65d0e94861d49e0",
"5f179efae999f8f8086269cedd1fbfaf6e90aadf5369a12737db0fff5905b12e",
"6ef2237b6c8683f626269027050c45cc4be89042ee99e4e89bfd9d9fbd24da19",
"6fd5154af55b4a1e4a1a5272e33238b2a2da12a30fa06af4f740d207e54ed495",
}
res := "42489ad8043a834149cd8e406c90c61411a05a0ca9f8e921b456a00b5d5988d7"
testComputeMerkleTree(t, rawHashes, res)
}
func TestComputeMerkleTree3(t *testing.T) {
// Mainnet block #2097152
rawHashes := []string{
"a7ff2c6aa08d4b979d5f7605aec5a1783c9207c738b5852ed5ff17b37ada649d",
"34fd42c1f47aa306ad2fd0fc04437fd5c828a25b3de59e73b18157253094a8da",
"36458dffd678d9f75ed9f2a28beb58bf1ad739f8899469b8641b0ddea22fcf5d",
"3e8144abe2edda263593f24defd4557d403efa1b4fa839409ac217d6f8a87d3a",
"a1d2cf73841fefcd21ca9986c664518d2af61edcfe3a97b30b3cc58fab4e61f6",
"c1e868aef0e8fd76b95a18e155b1fa65f30d0a4887bc953411553728664725bc",
"52d2fda0fe0fd586063d801c5ba77ca123a790d7e4dae34c53398feab36da721",
"fdf8d4610cb2de35ab4c18d38357b86c52966d149c8975906170dc513cc26345",
"35a26a11ef65d8f7a2424f7ce5915aa1d8bf3449018516003799767c2696197e",
"c9d251abfc20a0d6eeac2d5a93b77a6a0632a679a07decea2c809aead89bb503",
"d92c72873f2929c621ec06433da3053db25ee396b70c83d53abd40801823f66c",
}
res := "09c2dbc88810c350a2e7ace56bb1b371b2a2b5c4744e7a303adace9a2c2bbf6d"
testComputeMerkleTree(t, rawHashes, res)
}
func TestNewMerkleTreeFailWithoutHashes(t *testing.T) {
var hashes []util.Uint256
_, err := NewMerkleTree(hashes)
require.Error(t, err)
hashes = make([]util.Uint256, 0)
_, err = NewMerkleTree(hashes)
require.Error(t, err)
}
func TestBuildMerkleTreeWithoutNodes(t *testing.T) {
var leaves []*MerkleTreeNode
require.Panics(t, func() { buildMerkleTree(leaves) })
leaves = make([]*MerkleTreeNode, 0)
require.Panics(t, func() { buildMerkleTree(leaves) })
}

View file

@ -1,12 +1,12 @@
package crypto
package keys
import (
"crypto/aes"
"crypto/cipher"
)
// AESEncrypt encrypts the key with the given source.
func AESEncrypt(src, key []byte) ([]byte, error) {
// aesEncrypt encrypts the key with the given source.
func aesEncrypt(src, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
@ -19,8 +19,8 @@ func AESEncrypt(src, key []byte) ([]byte, error) {
return out, nil
}
// AESDecrypt decrypts the encrypted source with the given key.
func AESDecrypt(crypted, key []byte) ([]byte, error) {
// aesDecrypt decrypts the encrypted source with the given key.
func aesDecrypt(crypted, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err

View file

@ -5,8 +5,8 @@ import (
"errors"
"fmt"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
"github.com/CityOfZion/neo-go/pkg/encoding/base58"
"golang.org/x/crypto/scrypt"
"golang.org/x/text/unicode/norm"
)
@ -57,7 +57,7 @@ func NEP2Encrypt(priv *PrivateKey, passphrase string) (s string, err error) {
derivedKey2 := derivedKey[32:]
xr := xor(priv.Bytes(), derivedKey1)
encrypted, err := crypto.AESEncrypt(xr, derivedKey2)
encrypted, err := aesEncrypt(xr, derivedKey2)
if err != nil {
return s, err
}
@ -72,13 +72,13 @@ func NEP2Encrypt(priv *PrivateKey, passphrase string) (s string, err error) {
return s, fmt.Errorf("invalid buffer length: expecting 39 bytes got %d", buf.Len())
}
return crypto.Base58CheckEncode(buf.Bytes()), nil
return base58.CheckEncode(buf.Bytes()), nil
}
// NEP2Decrypt decrypts an encrypted key using a given passphrase
// under the NEP-2 standard.
func NEP2Decrypt(key, passphrase string) (s string, err error) {
b, err := crypto.Base58CheckDecode(key)
b, err := base58.CheckDecode(key)
if err != nil {
return s, nil
}
@ -98,7 +98,7 @@ func NEP2Decrypt(key, passphrase string) (s string, err error) {
derivedKey2 := derivedKey[32:]
encryptedBytes := b[7:]
decrypted, err := crypto.AESDecrypt(encryptedBytes, derivedKey2)
decrypted, err := aesDecrypt(encryptedBytes, derivedKey2)
if err != nil {
return s, err
}

View file

@ -9,8 +9,8 @@ import (
"fmt"
"math/big"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
"github.com/pkg/errors"
@ -173,6 +173,8 @@ func (p *PublicKey) DecodeBinary(r *io.BinReader) {
return
}
p256 := elliptic.P256()
p256Params := p256.Params()
// Infinity
switch prefix {
case 0x00:
@ -189,6 +191,7 @@ func (p *PublicKey) DecodeBinary(r *io.BinReader) {
ylsb := uint(prefix & 0x1)
y, err = decodeCompressedY(x, ylsb)
if err != nil {
r.Err = err
return
}
case 0x04:
@ -201,17 +204,15 @@ func (p *PublicKey) DecodeBinary(r *io.BinReader) {
}
x = new(big.Int).SetBytes(xbytes)
y = new(big.Int).SetBytes(ybytes)
if !p256.IsOnCurve(x, y) {
r.Err = errors.New("encoded point is not on the P256 curve")
return
}
default:
r.Err = errors.Errorf("invalid prefix %d", prefix)
return
}
c := elliptic.P256()
cp := c.Params()
if !c.IsOnCurve(x, y) {
r.Err = errors.New("enccoded point is not on the P256 curve")
return
}
if x.Cmp(cp.P) >= 0 || y.Cmp(cp.P) >= 0 {
if x.Cmp(p256Params.P) >= 0 || y.Cmp(p256Params.P) >= 0 {
r.Err = errors.New("enccoded point is not correct (X or Y is bigger than P")
return
}
@ -242,11 +243,9 @@ func (p *PublicKey) Signature() []byte {
// Address returns a base58-encoded NEO-specific address based on the key hash.
func (p *PublicKey) Address() string {
var b = p.Signature()
sig := hash.Hash160(p.GetVerificationScript())
b = append([]byte{0x17}, b...)
return crypto.Base58CheckEncode(b)
return address.Uint160ToString(sig)
}
// Verify returns true if the signature is valid and corresponds

View file

@ -63,6 +63,30 @@ func TestDecodeFromString(t *testing.T) {
require.Error(t, err)
}
func TestDecodeFromStringBadCompressed(t *testing.T) {
str := "02ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
_, err := NewPublicKeyFromString(str)
require.Error(t, err)
}
func TestDecodeFromStringBadXMoreThanP(t *testing.T) {
str := "02ffffffff00000001000000000000000000000001ffffffffffffffffffffffff"
_, err := NewPublicKeyFromString(str)
require.Error(t, err)
}
func TestDecodeFromStringNotOnCurve(t *testing.T) {
str := "04ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
_, err := NewPublicKeyFromString(str)
require.Error(t, err)
}
func TestDecodeFromStringUncompressed(t *testing.T) {
str := "046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5"
_, err := NewPublicKeyFromString(str)
require.NoError(t, err)
}
func TestPubkeyToAddress(t *testing.T) {
pubKey, err := NewPublicKeyFromString("031ee4e73a17d8f76dc02532e2620bcb12425b33c0c9f9694cc2caa8226b68cad4")
require.NoError(t, err)

View file

@ -4,7 +4,7 @@ import (
"bytes"
"fmt"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/encoding/base58"
)
const (
@ -43,13 +43,13 @@ func WIFEncode(key []byte, version byte, compressed bool) (s string, err error)
buf.WriteByte(0x01)
}
s = crypto.Base58CheckEncode(buf.Bytes())
s = base58.CheckEncode(buf.Bytes())
return
}
// WIFDecode decodes the given WIF string into a WIF struct.
func WIFDecode(wif string, version byte) (*WIF, error) {
b, err := crypto.Base58CheckDecode(wif)
b, err := base58.CheckDecode(wif)
if err != nil {
return nil, err
}

View file

@ -1,29 +0,0 @@
package crypto
import (
"testing"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/stretchr/testify/assert"
)
func TestComputeMerkleTree(t *testing.T) {
rawHashes := []string{
"fb5bd72b2d6792d75dc2f1084ffa9e9f70ca85543c717a6b13d9959b452a57d6",
"c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b",
"602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7",
"3631f66024ca6f5b033d7e0809eb993443374830025af904fb51b0334f127cda",
}
hashes := make([]util.Uint256, len(rawHashes))
for i, str := range rawHashes {
hash, _ := util.Uint256DecodeStringLE(str)
hashes[i] = hash
}
merkle, err := NewMerkleTree(hashes)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, "803ff4abe3ea6533bcc0be574efa02f83ae8fdc651c879056b0d9be336c01bf4", merkle.Root().StringLE())
}

View file

@ -0,0 +1,32 @@
package address
import (
"errors"
"github.com/CityOfZion/neo-go/pkg/encoding/base58"
"github.com/CityOfZion/neo-go/pkg/util"
)
// Prefix is the byte used to prepend to addresses when encoding them, it can
// be changed and defaults to 23 (0x17), the standard NEO prefix.
var Prefix = byte(0x17)
// Uint160ToString returns the "NEO address" from the given Uint160.
func Uint160ToString(u util.Uint160) string {
// Dont forget to prepend the Address version 0x17 (23) A
b := append([]byte{Prefix}, u.BytesBE()...)
return base58.CheckEncode(b)
}
// StringToUint160 attempts to decode the given NEO address string
// into an Uint160.
func StringToUint160(s string) (u util.Uint160, err error) {
b, err := base58.CheckDecode(s)
if err != nil {
return u, err
}
if b[0] != Prefix {
return u, errors.New("wrong address prefix")
}
return util.Uint160DecodeBytesBE(b[1:21])
}

View file

@ -1,9 +1,10 @@
package crypto
package address
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestUint160DecodeEncodeAddress(t *testing.T) {
@ -13,18 +14,18 @@ func TestUint160DecodeEncodeAddress(t *testing.T) {
"AMxkaxFVG8Q1BhnB4fjTA5ZmUTEnnTMJMa",
}
for _, addr := range addrs {
val, err := Uint160DecodeAddress(addr)
val, err := StringToUint160(addr)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, addr, AddressFromUint160(val))
assert.Equal(t, addr, Uint160ToString(val))
}
}
func TestUint160DecodeKnownAddress(t *testing.T) {
address := "AJeAEsmeD6t279Dx4n2HWdUvUmmXQ4iJvP"
val, err := Uint160DecodeAddress(address)
val, err := StringToUint160(address)
if err != nil {
t.Fatal(err)
}
@ -32,3 +33,18 @@ func TestUint160DecodeKnownAddress(t *testing.T) {
assert.Equal(t, "b28427088a3729b2536d10122960394e8be6721f", val.StringLE())
assert.Equal(t, "1f72e68b4e39602912106d53b229378a082784b2", val.String())
}
func TestUint160DecodeBadBase58(t *testing.T) {
address := "AJeAEsmeD6t279Dx4n2HWdUvUmmXQ4iJv@"
_, err := StringToUint160(address)
require.Error(t, err)
}
func TestUint160DecodeBadPrefix(t *testing.T) {
// The same AJeAEsmeD6t279Dx4n2HWdUvUmmXQ4iJvP key encoded with 0x18 prefix.
address := "AhymDz4vvHLtvaN36CMbzkki7H2U8ENb8F"
_, err := StringToUint160(address)
require.Error(t, err)
}

View file

@ -1,4 +1,4 @@
package crypto
package base58
import (
"bytes"
@ -8,20 +8,14 @@ import (
"github.com/pkg/errors"
)
// Base58CheckDecode decodes the given string.
func Base58CheckDecode(s string) (b []byte, err error) {
// CheckDecode implements a base58-encoded string decoding with hash-based
// checksum check.
func CheckDecode(s string) (b []byte, err error) {
b, err = base58.Decode(s)
if err != nil {
return nil, err
}
for i := 0; i < len(s); i++ {
if s[i] != '1' {
break
}
b = append([]byte{0x00}, b...)
}
if len(b) < 5 {
return nil, errors.New("invalid base-58 check string: missing checksum")
}
@ -36,8 +30,9 @@ func Base58CheckDecode(s string) (b []byte, err error) {
return b, nil
}
// Base58CheckEncode encodes b into a base-58 check encoded string.
func Base58CheckEncode(b []byte) string {
// CheckEncode encodes given byte slice into a base58 string with hash-based
// checksum appended to it.
func CheckEncode(b []byte) string {
b = append(b, hash.Checksum(b)...)
return base58.Encode(b)

View file

@ -1,32 +1,41 @@
package crypto
package base58
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestBase58CheckEncodeDecode(t *testing.T) {
func TestCheckEncodeDecode(t *testing.T) {
var b58CsumEncoded = "KxhEDBQyyEFymvfJD96q8stMbJMbZUb6D1PmXqBWZDU2WvbvVs9o"
var b58CsumDecodedHex = "802bfe58ab6d9fd575bdc3a624e4825dd2b375d64ac033fbc46ea79dbab4f69a3e01"
b58CsumDecoded, _ := hex.DecodeString(b58CsumDecodedHex)
encoded := Base58CheckEncode(b58CsumDecoded)
decoded, err := Base58CheckDecode(b58CsumEncoded)
encoded := CheckEncode(b58CsumDecoded)
decoded, err := CheckDecode(b58CsumEncoded)
assert.Nil(t, err)
assert.Equal(t, encoded, b58CsumEncoded)
assert.Equal(t, decoded, b58CsumDecoded)
}
func TestBase58CheckDecodeFailures(t *testing.T) {
func TestCheckDecodeFailures(t *testing.T) {
badbase58 := "BASE%*"
_, err := Base58CheckDecode(badbase58)
_, err := CheckDecode(badbase58)
assert.NotNil(t, err)
shortbase58 := "THqY"
_, err = Base58CheckDecode(shortbase58)
_, err = CheckDecode(shortbase58)
assert.NotNil(t, err)
badcsum := "KxhEDBQyyEFymvfJD96q8stMbJMbZUb6D1PmXqBWZDU2WvbvVs9A"
_, err = Base58CheckDecode(badcsum)
_, err = CheckDecode(badcsum)
assert.NotNil(t, err)
}
func TestBase58LeadingZeroes(t *testing.T) {
buf := []byte{0, 0, 0, 1}
b58 := CheckEncode(buf)
dec, err := CheckDecode(b58)
require.NoError(t, err)
require.Equal(t, buf, dec)
}

View file

@ -0,0 +1,4 @@
/*
Package base58 wraps generic base58 encoder with NEO-specific checksumming.
*/
package base58

View file

@ -6,7 +6,7 @@ import (
"encoding/json"
"fmt"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/pkg/errors"
)
@ -96,7 +96,7 @@ func (p Param) GetUint160FromAddress() (util.Uint160, error) {
return util.Uint160{}, err
}
return crypto.Uint160DecodeAddress(s)
return address.StringToUint160(s)
}
// GetFuncParam returns current parameter as a function call parameter.

View file

@ -5,7 +5,7 @@ import (
"encoding/json"
"testing"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -129,7 +129,7 @@ func TestParamGetUint160FromHex(t *testing.T) {
func TestParamGetUint160FromAddress(t *testing.T) {
in := "AK2nJJpJr6o664CWJKi1QRXjqeic2zRp8y"
u160, _ := crypto.Uint160DecodeAddress(in)
u160, _ := address.StringToUint160(in)
p := Param{stringT, in}
u, err := p.GetUint160FromAddress()
assert.Equal(t, u160, u)

View file

@ -7,8 +7,8 @@ import (
"strconv"
"github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/smartcontract"
"github.com/CityOfZion/neo-go/pkg/util"
@ -26,17 +26,17 @@ func CreateRawContractTransaction(params ContractTxParams) (*transaction.Transac
fromAddress string
receiverOutput *transaction.Output
wif, assetID, address, amount, balancer = params.wif, params.assetID, params.address, params.value, params.balancer
wif, assetID, toAddress, amount, balancer = params.wif, params.assetID, params.address, params.value, params.balancer
)
fromAddress = wif.PrivateKey.Address()
if fromAddressHash, err = crypto.Uint160DecodeAddress(fromAddress); err != nil {
if fromAddressHash, err = address.StringToUint160(fromAddress); err != nil {
return nil, errs.Wrapf(err, "Failed to take script hash from address: %v", fromAddress)
}
if toAddressHash, err = crypto.Uint160DecodeAddress(address); err != nil {
return nil, errs.Wrapf(err, "Failed to take script hash from address: %v", address)
if toAddressHash, err = address.StringToUint160(toAddress); err != nil {
return nil, errs.Wrapf(err, "Failed to take script hash from address: %v", toAddress)
}
tx.Attributes = append(tx.Attributes,
transaction.Attribute{
@ -58,12 +58,12 @@ func CreateRawContractTransaction(params ContractTxParams) (*transaction.Transac
// AddInputsAndUnspentsToTx adds inputs needed to transaction and one output
// with change.
func AddInputsAndUnspentsToTx(tx *transaction.Transaction, address string, assetID util.Uint256, amount util.Fixed8, balancer BalanceGetter) error {
scriptHash, err := crypto.Uint160DecodeAddress(address)
func AddInputsAndUnspentsToTx(tx *transaction.Transaction, addr string, assetID util.Uint256, amount util.Fixed8, balancer BalanceGetter) error {
scriptHash, err := address.StringToUint160(addr)
if err != nil {
return errs.Wrapf(err, "failed to take script hash from address: %v", address)
return errs.Wrapf(err, "failed to take script hash from address: %v", addr)
}
inputs, spent, err := balancer.CalculateInputs(address, assetID, amount)
inputs, spent, err := balancer.CalculateInputs(addr, assetID, amount)
if err != nil {
return errs.Wrap(err, "failed to get inputs")
}

View file

@ -3,7 +3,7 @@ package wrappers
import (
"github.com/CityOfZion/neo-go/pkg/core/state"
"github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/util"
)
@ -37,8 +37,8 @@ func NewAssetState(a *state.Asset) AssetState {
FeeMode: a.FeeMode,
FeeAddress: a.FeeAddress,
Owner: a.Owner.String(),
Admin: crypto.AddressFromUint160(a.Admin),
Issuer: crypto.AddressFromUint160(a.Issuer),
Admin: address.Uint160ToString(a.Admin),
Issuer: address.Uint160ToString(a.Issuer),
Expiration: a.Expiration,
IsFrozen: a.IsFrozen,
}

View file

@ -1,7 +1,7 @@
package wrappers
import (
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
)
// ValidateAddressResponse represents response to validate address call.
@ -12,10 +12,10 @@ type ValidateAddressResponse struct {
// ValidateAddress verifies that the address is a correct NEO address
// see https://docs.neo.org/en-us/node/cli/2.9.4/api/validateaddress.html
func ValidateAddress(address interface{}) ValidateAddressResponse {
resp := ValidateAddressResponse{Address: address}
if address, ok := address.(string); ok {
_, err := crypto.Uint160DecodeAddress(address)
func ValidateAddress(addr interface{}) ValidateAddressResponse {
resp := ValidateAddressResponse{Address: addr}
if addr, ok := addr.(string); ok {
_, err := address.StringToUint160(addr)
resp.IsValid = err == nil
}
return resp

View file

@ -7,8 +7,8 @@ import (
"strings"
"unicode/utf8"
"github.com/CityOfZion/neo-go/pkg/crypto"
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
"github.com/CityOfZion/neo-go/pkg/encoding/address"
"github.com/CityOfZion/neo-go/pkg/io"
"github.com/CityOfZion/neo-go/pkg/util"
)
@ -156,7 +156,7 @@ func adjustValToType(typ ParamType, val string) (interface{}, error) {
case IntegerType:
return strconv.Atoi(val)
case Hash160Type:
u, err := crypto.Uint160DecodeAddress(val)
u, err := address.StringToUint160(val)
if err == nil {
return hex.EncodeToString(u.BytesBE()), nil
}
@ -215,7 +215,7 @@ func inferParamType(val string) ParamType {
return BoolType
}
_, err = crypto.Uint160DecodeAddress(val)
_, err = address.StringToUint160(val)
if err == nil {
return Hash160Type
}