Format Ethereum addresses with EIP55 checksum

fiatRates
Martin Boehm 2019-09-30 17:11:17 +02:00
parent d3931953d5
commit ac9a721cc6
5 changed files with 62 additions and 23 deletions

2
Gopkg.lock generated
View File

@ -235,7 +235,7 @@
branch = "master" branch = "master"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
packages = ["ripemd160", "sha3"] packages = ["ripemd160", "sha3"]
revision = "d6449816ce06963d9d136eee5a56fca5b0616e7e" revision = "a832865fa7ada6126f4c6124ac49f71be71bff2a"
[[projects]] [[projects]]
branch = "master" branch = "master"

View File

@ -9,7 +9,6 @@ import (
"sync" "sync"
ethcommon "github.com/ethereum/go-ethereum/common" ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
) )
@ -165,7 +164,7 @@ func (b *EthereumRPC) EthereumTypeGetErc20ContractInfo(contractDesc bchain.Addre
contract, found := cachedContracts[cds] contract, found := cachedContracts[cds]
cachedContractsMux.Unlock() cachedContractsMux.Unlock()
if !found { if !found {
address := hexutil.Encode(contractDesc) address := EIP55Address(contractDesc)
data, err := b.ethCall(erc20NameSignature, address) data, err := b.ethCall(erc20NameSignature, address)
if err != nil { if err != nil {
return nil, err return nil, err
@ -204,8 +203,8 @@ func (b *EthereumRPC) EthereumTypeGetErc20ContractInfo(contractDesc bchain.Addre
// EthereumTypeGetErc20ContractBalance returns balance of ERC20 contract for given address // EthereumTypeGetErc20ContractBalance returns balance of ERC20 contract for given address
func (b *EthereumRPC) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (*big.Int, error) { func (b *EthereumRPC) EthereumTypeGetErc20ContractBalance(addrDesc, contractDesc bchain.AddressDescriptor) (*big.Int, error) {
addr := hexutil.Encode(addrDesc) addr := EIP55Address(addrDesc)
contract := hexutil.Encode(contractDesc) contract := EIP55Address(contractDesc)
req := erc20BalanceOf + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr)-2:] + addr[2:] req := erc20BalanceOf + "0000000000000000000000000000000000000000000000000000000000000000"[len(addr)-2:] + addr[2:]
data, err := b.ethCall(req, contract) data, err := b.ethCall(req, contract)
if err != nil { if err != nil {

View File

@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/juju/errors" "github.com/juju/errors"
"golang.org/x/crypto/sha3"
) )
// EthereumTypeAddressDescriptorLen - in case of EthereumType, the AddressDescriptor has fixed length // EthereumTypeAddressDescriptorLen - in case of EthereumType, the AddressDescriptor has fixed length
@ -176,9 +177,34 @@ func (p *EthereumParser) GetAddrDescFromAddress(address string) (bchain.AddressD
return hex.DecodeString(address) return hex.DecodeString(address)
} }
// EIP55Address returns an EIP55-compliant hex string representation of the address
func EIP55Address(addrDesc bchain.AddressDescriptor) string {
raw := hexutil.Encode(addrDesc)
if len(raw) != 42 {
return raw
}
sha := sha3.NewLegacyKeccak256()
result := []byte(raw)
sha.Write(result[2:])
hash := sha.Sum(nil)
for i := 2; i < len(result); i++ {
hashByte := hash[(i-2)>>1]
if i%2 == 0 {
hashByte = hashByte >> 4
} else {
hashByte &= 0xf
}
if result[i] > '9' && hashByte > 7 {
result[i] -= 32
}
}
return string(result)
}
// GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable // GetAddressesFromAddrDesc returns addresses for given address descriptor with flag if the addresses are searchable
func (p *EthereumParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) { func (p *EthereumParser) GetAddressesFromAddrDesc(addrDesc bchain.AddressDescriptor) ([]string, bool, error) {
return []string{hexutil.Encode(addrDesc)}, true, nil return []string{EIP55Address(addrDesc)}, true, nil
} }
// GetScriptFromAddrDesc returns output script for given address descriptor // GetScriptFromAddrDesc returns output script for given address descriptor
@ -308,7 +334,7 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
rt := rpcTransaction{ rt := rpcTransaction{
AccountNonce: hexutil.EncodeUint64(pt.Tx.AccountNonce), AccountNonce: hexutil.EncodeUint64(pt.Tx.AccountNonce),
BlockNumber: hexutil.EncodeUint64(uint64(pt.BlockNumber)), BlockNumber: hexutil.EncodeUint64(uint64(pt.BlockNumber)),
From: hexutil.Encode(pt.Tx.From), From: EIP55Address(pt.Tx.From),
GasLimit: hexutil.EncodeUint64(pt.Tx.GasLimit), GasLimit: hexutil.EncodeUint64(pt.Tx.GasLimit),
Hash: hexutil.Encode(pt.Tx.Hash), Hash: hexutil.Encode(pt.Tx.Hash),
Payload: hexutil.Encode(pt.Tx.Payload), Payload: hexutil.Encode(pt.Tx.Payload),
@ -316,7 +342,7 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
// R: hexEncodeBig(pt.R), // R: hexEncodeBig(pt.R),
// S: hexEncodeBig(pt.S), // S: hexEncodeBig(pt.S),
// V: hexEncodeBig(pt.V), // V: hexEncodeBig(pt.V),
To: hexutil.Encode(pt.Tx.To), To: EIP55Address(pt.Tx.To),
TransactionIndex: hexutil.EncodeUint64(uint64(pt.Tx.TransactionIndex)), TransactionIndex: hexutil.EncodeUint64(uint64(pt.Tx.TransactionIndex)),
Value: hexEncodeBig(pt.Tx.Value), Value: hexEncodeBig(pt.Tx.Value),
} }
@ -329,7 +355,7 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
topics[j] = hexutil.Encode(t) topics[j] = hexutil.Encode(t)
} }
logs[i] = &rpcLog{ logs[i] = &rpcLog{
Address: hexutil.Encode(l.Address), Address: EIP55Address(l.Address),
Data: hexutil.Encode(l.Data), Data: hexutil.Encode(l.Data),
Topics: topics, Topics: topics,
} }

View File

@ -77,14 +77,14 @@ func init() {
Txid: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b", Txid: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
Vin: []bchain.Vin{ Vin: []bchain.Vin{
{ {
Addresses: []string{"0x3e3a3d69dc66ba10737f531ed088954a9ec89d97"}, Addresses: []string{"0x3E3a3D69dc66bA10737F531ed088954a9EC89d97"},
}, },
}, },
Vout: []bchain.Vout{ Vout: []bchain.Vout{
{ {
ValueSat: *big.NewInt(1999622000000000000), ValueSat: *big.NewInt(1999622000000000000),
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f"}, Addresses: []string{"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"},
}, },
}, },
}, },
@ -93,12 +93,12 @@ func init() {
AccountNonce: "0xb26c", AccountNonce: "0xb26c",
GasPrice: "0x430e23400", GasPrice: "0x430e23400",
GasLimit: "0x5208", GasLimit: "0x5208",
To: "0x555ee11fbddc0e49a9bab358a8941ad95ffdb48f", To: "0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f",
Value: "0x1bc0159d530e6000", Value: "0x1bc0159d530e6000",
Payload: "0x", Payload: "0x",
Hash: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b", Hash: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
BlockNumber: "0x41eee8", BlockNumber: "0x41eee8",
From: "0x3e3a3d69dc66ba10737f531ed088954a9ec89d97", From: "0x3E3a3D69dc66bA10737F531ed088954a9EC89d97",
TransactionIndex: "0xa", TransactionIndex: "0xa",
}, },
Receipt: &rpcReceipt{ Receipt: &rpcReceipt{
@ -115,14 +115,14 @@ func init() {
Txid: "0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101", Txid: "0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101",
Vin: []bchain.Vin{ Vin: []bchain.Vin{
{ {
Addresses: []string{"0x20cd153de35d469ba46127a0c8f18626b59a256a"}, Addresses: []string{"0x20cD153de35D469BA46127A0C8F18626b59a256A"},
}, },
}, },
Vout: []bchain.Vout{ Vout: []bchain.Vout{
{ {
ValueSat: *big.NewInt(0), ValueSat: *big.NewInt(0),
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x4af4114f73d1c1c903ac9e0361b379d1291808a2"}, Addresses: []string{"0x4af4114F73d1c1C903aC9E0361b379D1291808A2"},
}, },
}, },
}, },
@ -131,19 +131,19 @@ func init() {
AccountNonce: "0xd0", AccountNonce: "0xd0",
GasPrice: "0x9502f9000", GasPrice: "0x9502f9000",
GasLimit: "0x130d5", GasLimit: "0x130d5",
To: "0x4af4114f73d1c1c903ac9e0361b379d1291808a2", To: "0x4af4114F73d1c1C903aC9E0361b379D1291808A2",
Value: "0x0", Value: "0x0",
Payload: "0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000", Payload: "0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000",
Hash: "0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101", Hash: "0xa9cd088aba2131000da6f38a33c20169baee476218deea6b78720700b895b101",
BlockNumber: "0x41eee8", BlockNumber: "0x41eee8",
From: "0x20cd153de35d469ba46127a0c8f18626b59a256a", From: "0x20cD153de35D469BA46127A0C8F18626b59a256A",
TransactionIndex: "0x0"}, TransactionIndex: "0x0"},
Receipt: &rpcReceipt{ Receipt: &rpcReceipt{
GasUsed: "0xcb39", GasUsed: "0xcb39",
Status: "0x1", Status: "0x1",
Logs: []*rpcLog{ Logs: []*rpcLog{
{ {
Address: "0x4af4114f73d1c1c903ac9e0361b379d1291808a2", Address: "0x4af4114F73d1c1C903aC9E0361b379D1291808A2",
Data: "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000", Data: "0x00000000000000000000000000000000000000000000021e19e0c9bab2400000",
Topics: []string{ Topics: []string{
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",

View File

@ -8,6 +8,7 @@ import (
"io/ioutil" "io/ioutil"
"path/filepath" "path/filepath"
"reflect" "reflect"
"strings"
"testing" "testing"
"time" "time"
@ -177,8 +178,8 @@ func testGetTransaction(t *testing.T, h *TestHandler) {
// CoinSpecificData are not specified in the fixtures // CoinSpecificData are not specified in the fixtures
got.CoinSpecificData = nil got.CoinSpecificData = nil
normalizeEmptyAddresses(want) normalizeAddresses(want, h.Chain.GetChainParser())
normalizeEmptyAddresses(got) normalizeAddresses(got, h.Chain.GetChainParser())
if !reflect.DeepEqual(got, want) { if !reflect.DeepEqual(got, want) {
t.Errorf("GetTransaction() got %+#v, want %+#v", got, want) t.Errorf("GetTransaction() got %+#v, want %+#v", got, want)
@ -196,8 +197,8 @@ func testGetTransactionForMempool(t *testing.T, h *TestHandler) {
t.Fatal(err) t.Fatal(err)
} }
normalizeEmptyAddresses(want) normalizeAddresses(want, h.Chain.GetChainParser())
normalizeEmptyAddresses(got) normalizeAddresses(got, h.Chain.GetChainParser())
// transactions parsed from JSON may contain additional data // transactions parsed from JSON may contain additional data
got.Confirmations, got.Blocktime, got.Time, got.CoinSpecificData = 0, 0, 0, nil got.Confirmations, got.Blocktime, got.Time, got.CoinSpecificData = 0, 0, 0, nil
@ -208,15 +209,28 @@ func testGetTransactionForMempool(t *testing.T, h *TestHandler) {
} }
// empty slice can be either []slice{} or nil; reflect.DeepEqual treats them as different value // empty slice can be either []slice{} or nil; reflect.DeepEqual treats them as different value
func normalizeEmptyAddresses(tx *bchain.Tx) { // remove checksums from ethereum addresses
func normalizeAddresses(tx *bchain.Tx, parser bchain.BlockChainParser) {
for i := range tx.Vin { for i := range tx.Vin {
if len(tx.Vin[i].Addresses) == 0 { if len(tx.Vin[i].Addresses) == 0 {
tx.Vin[i].Addresses = nil tx.Vin[i].Addresses = nil
} else {
if parser.GetChainType() == bchain.ChainEthereumType {
for j := range tx.Vin[i].Addresses {
tx.Vin[i].Addresses[j] = strings.ToLower(tx.Vin[i].Addresses[j])
}
}
} }
} }
for i := range tx.Vout { for i := range tx.Vout {
if len(tx.Vout[i].ScriptPubKey.Addresses) == 0 { if len(tx.Vout[i].ScriptPubKey.Addresses) == 0 {
tx.Vout[i].ScriptPubKey.Addresses = nil tx.Vout[i].ScriptPubKey.Addresses = nil
} else {
if parser.GetChainType() == bchain.ChainEthereumType {
for j := range tx.Vout[i].ScriptPubKey.Addresses {
tx.Vout[i].ScriptPubKey.Addresses[j] = strings.ToLower(tx.Vout[i].ScriptPubKey.Addresses[j])
}
}
} }
} }
} }