Format Ethereum addresses with EIP55 checksum
parent
d3931953d5
commit
ac9a721cc6
|
@ -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"
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue