crypto, common, xeth: implemented checksum addresses

This commit is contained in:
Jeffrey Wilcke 2015-08-16 17:22:53 +02:00
parent cd81356ace
commit b9e93b69a7
6 changed files with 138 additions and 19 deletions

View file

@ -117,6 +117,33 @@ func (a *Address) Set(other Address) {
} }
} }
// ChecksumAddress is an address with an included checksum. A checksum address
// is usually made out of SHA3(A) + A
type ChecksumAddress struct {
Checksum [4]byte
Address
}
// HexToChecksumAddress converts a hex checksum address to a ChecksumAddress
// type
func HexToChecksumAddress(s string) ChecksumAddress {
if len(s) > 2 && s[:2] == "0x" {
s = s[2:]
}
if len(s) != 48 {
return ChecksumAddress{}
}
addr := ChecksumAddress{[4]byte{}, HexToAddress(s[4:])}
copy(addr.Checksum[:], Hex2Bytes(s[:8]))
return addr
}
// Hex returns the hexadecimal representation of a checksum address
func (ca ChecksumAddress) Hex() string {
return "0x" + Bytes2Hex(ca.Checksum[:]) + ca.Address.Hex()[2:]
}
// PP Pretty Prints a byte slice in the following format: // PP Pretty Prints a byte slice in the following format:
// hex(value[:4])...(hex[len(value)-4:]) // hex(value[:4])...(hex[len(value)-4:])
func PP(value []byte) string { func PP(value []byte) string {

44
crypto/address.go Normal file
View file

@ -0,0 +1,44 @@
package crypto
import (
"bytes"
"crypto/ecdsa"
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
)
const checksumLen = 4
// Creates an ethereum address given the bytes and the nonce
func CreateAddress(b common.Address, nonce uint64) common.Address {
data, _ := rlp.EncodeToBytes([]interface{}{b, nonce})
return common.BytesToAddress(Sha3(data)[12:])
}
// ChecksumAddress creates modifies an address to include a checksum
func ChecksumAddress(addr common.Address) common.ChecksumAddress {
var checksumaddr common.ChecksumAddress
checksumaddr.Address = addr
copy(checksumaddr.Checksum[:], Sha3(addr.Bytes())[:4])
return checksumaddr
}
// ValidateAddress validates the given address. It must include a checksum
// on the end. It returns the address or an error if the checksum or length of
// the input failed.
func ValidateAddress(addr common.ChecksumAddress) (common.Address, error) {
if !bytes.Equal(Sha3(addr.Address[:])[:4], addr.Checksum[:]) {
return common.Address{}, fmt.Errorf("checksum failed for address %x (%x)", addr.Address, addr.Checksum)
}
return addr.Address, nil
}
// PubkeyToAddress converts a std ecdsa public key to a common.Address
func PubkeyToAddress(p ecdsa.PublicKey) common.Address {
pubBytes := FromECDSAPub(&p)
return common.BytesToAddress(Sha3(pubBytes[1:])[12:])
}

33
crypto/address_test.go Normal file
View file

@ -0,0 +1,33 @@
package crypto
import (
"bytes"
"testing"
"github.com/ethereum/go-ethereum/common"
)
func TestChecksum(t *testing.T) {
var addr common.Address
addr[0] = 1
checksumed := ChecksumAddress(addr)
if !bytes.Equal(checksumed.Checksum[:], Sha3(checksumed.Address[:])[:4]) {
t.Error("checksum failed")
}
addr, err := ValidateAddress(checksumed)
if err != nil {
t.Error(err)
}
if addr != addr {
t.Error("address failed")
}
checksumed.Checksum[3] |= 1
_, err = ValidateAddress(checksumed)
if err == nil {
t.Error("checksum success, should have failed")
}
}

View file

@ -38,7 +38,6 @@ import (
"github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/ethereum/go-ethereum/crypto/secp256k1"
"github.com/ethereum/go-ethereum/crypto/sha3" "github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
"golang.org/x/crypto/ripemd160" "golang.org/x/crypto/ripemd160"
) )
@ -68,13 +67,6 @@ func Sha3Hash(data ...[]byte) (h common.Hash) {
return h return h
} }
// Creates an ethereum address given the bytes and the nonce
func CreateAddress(b common.Address, nonce uint64) common.Address {
data, _ := rlp.EncodeToBytes([]interface{}{b, nonce})
return common.BytesToAddress(Sha3(data)[12:])
//return Sha3(common.NewValue([]interface{}{b, nonce}).Encode())[12:]
}
func Sha256(data []byte) []byte { func Sha256(data []byte) []byte {
hash := sha256.Sum256(data) hash := sha256.Sum256(data)
@ -332,8 +324,3 @@ func PKCS7Unpad(in []byte) []byte {
} }
return in[:len(in)-int(padding)] return in[:len(in)-int(padding)]
} }
func PubkeyToAddress(p ecdsa.PublicKey) common.Address {
pubBytes := FromECDSAPub(&p)
return common.BytesToAddress(Sha3(pubBytes[1:])[12:])
}

View file

@ -2256,6 +2256,17 @@ var isAddress = function (address) {
return /^(0x)?[0-9a-f]{40}$/.test(address); return /^(0x)?[0-9a-f]{40}$/.test(address);
}; };
/**
* Checks if the given string is an address
*
* @method isChecksumAddress
* @param {String} address the given HEX adress
* @return {Boolean}
*/
var isChecksumAddress = function (address) {
return /^(0x)?[0-9a-f]{48}$/.test(address);
};
/** /**
* Transforms given string to valid 20 bytes-length addres with 0x prefix * Transforms given string to valid 20 bytes-length addres with 0x prefix
* *
@ -2378,6 +2389,7 @@ module.exports = {
isBigNumber: isBigNumber, isBigNumber: isBigNumber,
isStrictAddress: isStrictAddress, isStrictAddress: isStrictAddress,
isAddress: isAddress, isAddress: isAddress,
isChecksumAddress: isChecksumAddress,
isFunction: isFunction, isFunction: isFunction,
isString: isString, isString: isString,
isObject: isObject, isObject: isObject,
@ -2513,6 +2525,7 @@ web3.toBigNumber = utils.toBigNumber;
web3.toWei = utils.toWei; web3.toWei = utils.toWei;
web3.fromWei = utils.fromWei; web3.fromWei = utils.fromWei;
web3.isAddress = utils.isAddress; web3.isAddress = utils.isAddress;
web3.isChecksumAddress = utils.isChecksumAddress;
web3.isIBAN = utils.isIBAN; web3.isIBAN = utils.isIBAN;
web3.sha3 = sha3; web3.sha3 = sha3;
web3.createBatch = function () { web3.createBatch = function () {
@ -3735,8 +3748,10 @@ var inputAddressFormatter = function (address) {
return address; return address;
} else if (utils.isAddress(address)) { } else if (utils.isAddress(address)) {
return '0x' + address; return '0x' + address;
} else if (utils.isChecksumAddress(address)) {
return address;
} }
throw 'invalid address'; throw 'invalid address'
}; };
module.exports = { module.exports = {

View file

@ -389,7 +389,7 @@ func (self *XEth) Accounts() []string {
accounts, _ := self.backend.AccountManager().Accounts() accounts, _ := self.backend.AccountManager().Accounts()
accountAddresses := make([]string, len(accounts)) accountAddresses := make([]string, len(accounts))
for i, ac := range accounts { for i, ac := range accounts {
accountAddresses[i] = ac.Address.Hex() accountAddresses[i] = crypto.ChecksumAddress(ac.Address).Hex()
} }
return accountAddresses return accountAddresses
} }
@ -890,7 +890,6 @@ func (self *XEth) Frontend() Frontend {
} }
func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
// this minimalistic recoding is enough (works for natspec.js) // this minimalistic recoding is enough (works for natspec.js)
var jsontx = fmt.Sprintf(`{"params":[{"to":"%s","data": "%s"}]}`, toStr, codeStr) var jsontx = fmt.Sprintf(`{"params":[{"to":"%s","data": "%s"}]}`, toStr, codeStr)
if !self.ConfirmTransaction(jsontx) { if !self.ConfirmTransaction(jsontx) {
@ -898,13 +897,27 @@ func (self *XEth) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceS
return "", err return "", err
} }
if len(toStr) > 0 && toStr != "0x" && !isAddress(toStr) { var (
return "", errors.New("Invalid address") to common.Address
err error
)
// If the address is larges than 20 bytes (+ 0x) assume a checksum
// address and verify.
if len(toStr) > 42 {
checkAddr := common.HexToChecksumAddress(toStr)
to, err = crypto.ValidateAddress(checkAddr)
if err != nil {
return "", err
}
} else {
if len(toStr) > 0 && toStr != "0x" && !isAddress(toStr) {
return "", errors.New("Invalid address")
}
to = common.HexToAddress(toStr)
} }
var ( var (
from = common.HexToAddress(fromStr) from = common.HexToAddress(fromStr)
to = common.HexToAddress(toStr)
value = common.Big(valueStr) value = common.Big(valueStr)
gas *big.Int gas *big.Int
price *big.Int price *big.Int