From 55f10f584eed945d258ddbc7285405422ef01dd7 Mon Sep 17 00:00:00 2001 From: Rob De Feo Date: Sun, 16 Feb 2020 14:34:04 +0000 Subject: [PATCH 1/7] chore: rename public key finder file --- internal/clients/etherscan/{pubkey-finder.go => pubkey_finder.go} | 0 .../etherscan/{pubkey-finder_test.go => pubkey_finder_test.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename internal/clients/etherscan/{pubkey-finder.go => pubkey_finder.go} (100%) rename internal/clients/etherscan/{pubkey-finder_test.go => pubkey_finder_test.go} (100%) diff --git a/internal/clients/etherscan/pubkey-finder.go b/internal/clients/etherscan/pubkey_finder.go similarity index 100% rename from internal/clients/etherscan/pubkey-finder.go rename to internal/clients/etherscan/pubkey_finder.go diff --git a/internal/clients/etherscan/pubkey-finder_test.go b/internal/clients/etherscan/pubkey_finder_test.go similarity index 100% rename from internal/clients/etherscan/pubkey-finder_test.go rename to internal/clients/etherscan/pubkey_finder_test.go From 3227fa9736e9e8b13d41192ae7cd8437699914fc Mon Sep 17 00:00:00 2001 From: Rob De Feo Date: Sun, 16 Feb 2020 15:17:09 +0000 Subject: [PATCH 2/7] feat: gettransactionbyhash --- .../internal/settings/ethrpc_test.go | 1 - internal/clients/blockscout/api.go | 28 ++++- internal/clients/blockscout/api_test.go | 109 ++++++++++++++++++ .../TestGetTransactionByHash/err-get.json | 5 + .../err-not-found.json | 5 + .../err-unmarshal.json | 0 .../err-unsupported-network.json | 0 .../TestGetTransactionByHash/success.json | 20 ++++ internal/clients/blockscout/types.go | 27 ++++- 9 files changed, 189 insertions(+), 6 deletions(-) create mode 100644 internal/clients/blockscout/testdata/TestGetTransactionByHash/err-get.json create mode 100644 internal/clients/blockscout/testdata/TestGetTransactionByHash/err-not-found.json create mode 100644 internal/clients/blockscout/testdata/TestGetTransactionByHash/err-unmarshal.json create mode 100644 internal/clients/blockscout/testdata/TestGetTransactionByHash/err-unsupported-network.json create mode 100644 internal/clients/blockscout/testdata/TestGetTransactionByHash/success.json diff --git a/cmd/mailchain/internal/settings/ethrpc_test.go b/cmd/mailchain/internal/settings/ethrpc_test.go index ac0cfc352..0209cd83f 100644 --- a/cmd/mailchain/internal/settings/ethrpc_test.go +++ b/cmd/mailchain/internal/settings/ethrpc_test.go @@ -32,7 +32,6 @@ func TestEthereumRPC2_Produce(t *testing.T) { func() values.String { m := valuestest.NewMockString(mockCtrl) m.EXPECT().Get().Return(server.URL) - // m.EXPECT().IsSet("server.cors.allowedOrigins").Return(false) return m }(), "mainnet", diff --git a/internal/clients/blockscout/api.go b/internal/clients/blockscout/api.go index 1162e037a..57cbcb2de 100644 --- a/internal/clients/blockscout/api.go +++ b/internal/clients/blockscout/api.go @@ -1,9 +1,12 @@ package blockscout import ( + "context" "encoding/json" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" "github.com/mailchain/mailchain/internal/protocols/ethereum" "github.com/pkg/errors" "gopkg.in/resty.v1" @@ -14,7 +17,10 @@ func NewAPIClient(apiKey string) (*APIClient, error) { return &APIClient{ key: apiKey, networkConfigs: map[string]networkConfig{ - ethereum.Mainnet: {url: "https://blockscout.com/eth/mainnet/api"}, + ethereum.Mainnet: { + url: "https://blockscout.com/eth/mainnet/api", + rpcURL: "https://relay.mailchain.xyz/json-rpc/ethereum/mainnet", + }, }, }, nil } @@ -26,7 +32,8 @@ type APIClient struct { } type networkConfig struct { - url string + rpcURL string + url string } // IsNetworkSupported checks if the network is supported by etherscan API @@ -62,3 +69,20 @@ func (c APIClient) getTransactionsByAddress(network string, address []byte) (*tx return txResult, nil } + +// GetTransactionByHash get transaction details from transaction hash via etherscan +func (c APIClient) getTransactionByHash(network string, hash common.Hash) (*types.Transaction, error) { + config, ok := c.networkConfigs[network] + if !ok { + return nil, errors.Errorf("network not supported") + } + + client, err := ethclient.Dial(config.rpcURL) + if err != nil { + return nil, err + } + + tx, _, err := client.TransactionByHash(context.Background(), hash) + + return tx, nil +} diff --git a/internal/clients/blockscout/api_test.go b/internal/clients/blockscout/api_test.go index 55ca500f3..923b6cfc4 100644 --- a/internal/clients/blockscout/api_test.go +++ b/internal/clients/blockscout/api_test.go @@ -18,10 +18,13 @@ import ( "errors" "fmt" "io/ioutil" + "math/big" "net/http" "net/http/httptest" "testing" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/mailchain/mailchain/internal/protocols/ethereum" "github.com/stretchr/testify/assert" ) @@ -162,3 +165,109 @@ func TestGetTransactionsByAddress(t *testing.T) { }) } } + +func TestGetTransactionByHash(t *testing.T) { + type args struct { + network string + } + tests := []struct { + name string + args args + wantErr error + wantNil bool + want *types.Transaction + }{ + { + "success", + args{ + "TestNetwork", + }, + nil, + false, + func() *types.Transaction { + return types.NewTransaction( + uint64(21), + common.HexToAddress("0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb"), + big.NewInt(4290000000000000), + uint64(50000), + big.NewInt(int64(20000000000)), + []byte("hello!")) + }(), + }, + { + "err-unsupported-network", + args{ + "UnsupportedNetwork", + }, + errors.New("network not supported"), + true, + nil, + }, + { + "err-get", + args{ + "TestNetwork", + }, + errors.New("Invalid address format"), + true, + nil, + }, + { + "err-not-found", + args{ + "TestNetwork", + }, + errors.New("not found"), + true, + nil, + }, + { + "err-unmarshal", + args{ + "TestNetwork", + }, + errors.New("unexpected end of JSON input"), + true, + nil, + }, + } + for _, tt := range tests { + testName := t.Name() + t.Run(tt.name, func(t *testing.T) { + golden, err := ioutil.ReadFile(fmt.Sprintf("./testdata/%s/%s.json", testName, tt.name)) + if err != nil { + assert.FailNow(t, err.Error()) + } + server := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write([]byte(golden)) + }), + ) + defer server.Close() + client := &APIClient{ + key: "api-key", + networkConfigs: map[string]networkConfig{"TestNetwork": {rpcURL: server.URL}}, + } + got, err := client.getTransactionByHash(tt.args.network, common.Hash{}) + if (err != nil) && err.Error() != tt.wantErr.Error() { + fmt.Print(err.Error()) + t.Errorf("APIClient.getTransactionByHash() error = %v, wantErr %v", err, tt.wantErr) + return + } + if (got == nil) != tt.wantNil { + t.Errorf("APIClient.getTransactionByHash() nil = %v, wantNil %v", got == nil, tt.wantNil) + return + } + + if got != nil && + (!assert.Equal(t, tt.want.Nonce(), got.Nonce()) || + !assert.Equal(t, tt.want.To(), got.To()) || + !assert.Equal(t, tt.want.Value(), got.Value()) || + !assert.Equal(t, tt.want.Gas(), got.Gas()) || + !assert.Equal(t, tt.want.GasPrice(), got.GasPrice()) || + !assert.Equal(t, tt.want.Data(), got.Data())) { + t.Errorf("APIClient.getTransactionByHash() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/clients/blockscout/testdata/TestGetTransactionByHash/err-get.json b/internal/clients/blockscout/testdata/TestGetTransactionByHash/err-get.json new file mode 100644 index 000000000..3656397f7 --- /dev/null +++ b/internal/clients/blockscout/testdata/TestGetTransactionByHash/err-get.json @@ -0,0 +1,5 @@ +{ + "status": "0", + "result": null, + "message": "Invalid address format" +} \ No newline at end of file diff --git a/internal/clients/blockscout/testdata/TestGetTransactionByHash/err-not-found.json b/internal/clients/blockscout/testdata/TestGetTransactionByHash/err-not-found.json new file mode 100644 index 000000000..40c6b29cc --- /dev/null +++ b/internal/clients/blockscout/testdata/TestGetTransactionByHash/err-not-found.json @@ -0,0 +1,5 @@ +{ + "jsonrpc": "2.0", + "result": null, + "id": 24 +} \ No newline at end of file diff --git a/internal/clients/blockscout/testdata/TestGetTransactionByHash/err-unmarshal.json b/internal/clients/blockscout/testdata/TestGetTransactionByHash/err-unmarshal.json new file mode 100644 index 000000000..e69de29bb diff --git a/internal/clients/blockscout/testdata/TestGetTransactionByHash/err-unsupported-network.json b/internal/clients/blockscout/testdata/TestGetTransactionByHash/err-unsupported-network.json new file mode 100644 index 000000000..e69de29bb diff --git a/internal/clients/blockscout/testdata/TestGetTransactionByHash/success.json b/internal/clients/blockscout/testdata/TestGetTransactionByHash/success.json new file mode 100644 index 000000000..c6f798de9 --- /dev/null +++ b/internal/clients/blockscout/testdata/TestGetTransactionByHash/success.json @@ -0,0 +1,20 @@ +{ + "jsonrpc": "2.0", + "id": 1, + "result": { + "blockHash": "0x1d59ff54b1eb26b013ce3cb5fc9dab3705b415a67127a003c3e61eb445bb8df2", + "blockNumber": "0x5daf3b", + "from": "0xa7d9ddbe1f17865597fbd27ec712455208b6b76d", + "gas": "0xc350", + "gasPrice": "0x4a817c800", + "hash": "0x88df016429689c079f3b2f6ad39fa052532c56795b733da78a91ebe6a713944b", + "input": "0x68656c6c6f21", + "nonce": "0x15", + "to": "0xf02c1c8e6114b1dbe8937a39260b5b0a374432bb", + "transactionIndex": "0x41", + "value": "0xf3dbb76162000", + "v": "0x25", + "r": "0x1b5e176d927f8e9ab405058b2d2457392da3e20f328b16ddabcebc33eaac5fea", + "s": "0x4ba69724e8f69de52f0125ad8b3c5c2cef33019bac3249e2c0a2192766d1721c" + } +} diff --git a/internal/clients/blockscout/types.go b/internal/clients/blockscout/types.go index 0889f4315..3169ad201 100644 --- a/internal/clients/blockscout/types.go +++ b/internal/clients/blockscout/types.go @@ -7,22 +7,43 @@ type txList struct { } type txResult struct { - BlockNumber string TimeStamp string Hash string Nonce string BlockHash string + BlockNumber string TransactionIndex string From string To string - Value string Gas string GasPrice string + GasUsed string IsError string // "txreceipt_status": "", Input string ContractAddress string CumulativeGasUsed string - GasUsed string Confirmations string + Value string +} + +type getTxInfo struct { + Status string + Message string + Result txInfo +} + +type txInfo struct { + BlockHash string + BlockNumber string + Confirmations string + From string + GasPrice string + GasUsed string + Hash string + Input string + Success bool + TimeStamp string + To string + Value string } From 2843dc62b166065c5d6c04c3bf11719c18aa0e82 Mon Sep 17 00:00:00 2001 From: Rob De Feo Date: Sun, 16 Feb 2020 16:02:28 +0000 Subject: [PATCH 3/7] tests: get public key by address etherscan --- internal/clients/etherscan/pubkey_finder.go | 10 +- .../clients/etherscan/pubkey_finder_test.go | 145 ++++++++++++++++++ .../err-get-result-from-hash-txlist.json | 26 ++++ ...tion-by-hash-eth_getTransactionByHash.json | 3 + .../err-get-transaction-by-hash-txlist.json | 106 +++++++++++++ ...rr-get-transactions-by-address-txlist.json | 3 + ...d-public-key-eth_getTransactionByHash.json | 25 +++ .../err-invalid-public-key-txlist.json | 106 +++++++++++++ .../success-eth_getTransactionByHash.json | 25 +++ .../success-txlist.json | 106 +++++++++++++ 10 files changed, 549 insertions(+), 6 deletions(-) create mode 100644 internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-result-from-hash-txlist.json create mode 100644 internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-eth_getTransactionByHash.json create mode 100644 internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-txlist.json create mode 100644 internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transactions-by-address-txlist.json create mode 100644 internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-eth_getTransactionByHash.json create mode 100644 internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-txlist.json create mode 100644 internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/success-eth_getTransactionByHash.json create mode 100644 internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/success-txlist.json diff --git a/internal/clients/etherscan/pubkey_finder.go b/internal/clients/etherscan/pubkey_finder.go index 80c83b9a5..703630b8e 100644 --- a/internal/clients/etherscan/pubkey_finder.go +++ b/internal/clients/etherscan/pubkey_finder.go @@ -21,6 +21,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/mailchain/mailchain/crypto" "github.com/mailchain/mailchain/crypto/secp256k1" + "github.com/mailchain/mailchain/encoding" "github.com/mailchain/mailchain/internal/protocols/ethereum" "github.com/pkg/errors" ) @@ -36,7 +37,7 @@ func (c APIClient) PublicKeyFromAddress(ctx context.Context, protocol, network s return nil, errors.WithStack(err) } - hash, err := getFromResultHash(common.BytesToAddress(address).Hex(), txResult) + hash, err := getFromResultHash(encoding.EncodeHexZeroX(address), txResult) if err != nil { return nil, errors.WithStack(err) } @@ -45,6 +46,7 @@ func (c APIClient) PublicKeyFromAddress(ctx context.Context, protocol, network s if err != nil { return nil, errors.WithStack(err) } + v, r, s := tx.RawSignatureValues() keyBytes, err := ethereum.GetPublicKeyFromTransaction( r, s, v, @@ -58,11 +60,7 @@ func (c APIClient) PublicKeyFromAddress(ctx context.Context, protocol, network s return nil, errors.WithMessage(err, "could not get public key from raw hash") } - publicKey, err := secp256k1.PublicKeyFromBytes(keyBytes) - if err != nil { - return nil, errors.WithStack(err) - } - return publicKey, nil + return secp256k1.PublicKeyFromBytes(keyBytes) } func getFromResultHash(address string, txResult *txList) (common.Hash, error) { diff --git a/internal/clients/etherscan/pubkey_finder_test.go b/internal/clients/etherscan/pubkey_finder_test.go index 9bb734768..52eb32fbc 100644 --- a/internal/clients/etherscan/pubkey_finder_test.go +++ b/internal/clients/etherscan/pubkey_finder_test.go @@ -15,9 +15,17 @@ package etherscan import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" "testing" "github.com/ethereum/go-ethereum/common" + "github.com/mailchain/mailchain/crypto" + "github.com/mailchain/mailchain/crypto/secp256k1" + "github.com/mailchain/mailchain/encoding/encodingtest" "github.com/stretchr/testify/assert" ) @@ -108,3 +116,140 @@ func TestGetFromResultHash(t *testing.T) { }) } } + +func TestAPIClient_PublicKeyFromAddress(t *testing.T) { + type fields struct { + key string + } + type args struct { + ctx context.Context + protocol string + network string + address []byte + } + tests := []struct { + name string + fields fields + args args + want crypto.PublicKey + wantErr bool + }{ + { + "success", + fields{ + "", + }, + args{ + context.Background(), + "ethereum", + "mainnet", + encodingtest.MustDecodeHexZeroX("0x92D8f10248C6a3953CC3692A894655ad05D61Efb"), + }, + func() crypto.PublicKey { + k, _ := secp256k1.PublicKeyFromBytes(encodingtest.MustDecodeHexZeroX("0xbdf6fb97c97c126b492186a4d5b28f34f0671a5aacc974da3bde0be93e45a1c50f89ceff72bd04ac9e25a04a1a6cb010aedaf65f91cec8ebe75901c49b63355d")) + return k + }(), + false, + }, + { + "err-invalid-public-key", + fields{ + "", + }, + args{ + context.Background(), + "ethereum", + "mainnet", + encodingtest.MustDecodeHexZeroX("0x92D8f10248C6a3953CC3692A894655ad05D61Efb"), + }, + nil, + true, + }, + { + "err-get-transaction-by-hash", + fields{ + "", + }, + args{ + context.Background(), + "ethereum", + "mainnet", + encodingtest.MustDecodeHexZeroX("0x92D8f10248C6a3953CC3692A894655ad05D61Efb"), + }, + nil, + true, + }, + { + "err-get-result-from-hash", + fields{ + "", + }, + args{ + context.Background(), + "ethereum", + "mainnet", + encodingtest.MustDecodeHexZeroX("0x92D8f10248C6a3953CC3692A894655ad05D61Efb"), + }, + nil, + true, + }, + { + "err-get-transactions-by-address", + fields{ + "", + }, + args{ + context.Background(), + "ethereum", + "mainnet", + encodingtest.MustDecodeHexZeroX("0x92D8f10248C6a3953CC3692A894655ad05D61Efb"), + }, + nil, + true, + }, + { + "err-invalid-network", + fields{ + "", + }, + args{ + context.Background(), + "ethereum", + "invalid", + encodingtest.MustDecodeHexZeroX("0x92D8f10248C6a3953CC3692A894655ad05D61Efb"), + }, + nil, + true, + }, + } + for _, tt := range tests { + testName := t.Name() + t.Run(tt.name, func(t *testing.T) { + server := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + golden, err := ioutil.ReadFile(fmt.Sprintf("./testdata/%s/%s-%s.json", testName, tt.name, r.URL.Query().Get("action"))) + if err != nil { + t.Log(r.URL.String()) + assert.FailNow(t, err.Error()) + } + w.Write([]byte(golden)) + }), + ) + defer server.Close() + c := APIClient{ + key: tt.fields.key, + networkConfigs: map[string]networkConfig{ + "mainnet": networkConfig{url: server.URL}, + }, + } + got, err := c.PublicKeyFromAddress(tt.args.ctx, tt.args.protocol, tt.args.network, tt.args.address) + if (err != nil) != tt.wantErr { + t.Errorf("APIClient.PublicKeyFromAddress() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !assert.Equal(t, tt.want, got) { + t.Errorf("APIClient.PublicKeyFromAddress() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-result-from-hash-txlist.json b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-result-from-hash-txlist.json new file mode 100644 index 000000000..974cd80d7 --- /dev/null +++ b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-result-from-hash-txlist.json @@ -0,0 +1,26 @@ +{ + "status": "1", + "result": [ + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "44", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562705082", + "nonce": "6", + "isError": "0", + "input": "0x6d61696c636861696e501282022e88fd4a3b84ad56b52b85158bfff5130203606434267e17f7c81f83e41d6c700ed9967822ed42d02a43b65e625b99034485f980fdf02799ac7743970099bafaa095a14cd187eecd7078a30b8a7a5c5be21eb2026b2e63252f21c05c724ad0c8c6e3fa94938c1007c5e1b6c7739c3b1201f3ddc390fa08a358c181785d35211ac0d574e58e47649c4d97aa1d1025e27432b18c04921197666f7e4186849db4ff8edb0ebac7d5368d3d109c49fb0345c56167747ff62bf46e7834f954633f5d311715988b4a7fbb26443cbc13ed91530acf1a685b429dc559a1df3b45118fc3abf336f2914f10f728b1de56c3841aba92a8b9d819a5dd3316055d2ee00c8e2bfb8fbf1a221620883c9a5ec19abceadb51d9a667e0e6db48a87d8251ed9b2a61da6a71450dcccf", + "hash": "0xdbd102bba397330152f858f1e86a4e121cd1c8d73b744223da750176246e1956", + "gasUsed": "41812", + "gasPrice": "4000000000", + "gas": "41812", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "4482561", + "contractAddress": "", + "confirmations": "1375668", + "blockNumber": "8119278", + "blockHash": "0xe58eab170d5f004127c4af26b4ff1e38b0e01fe34d0b478b77e7d98ceed62863" + } + ], + "message": "OK" + } \ No newline at end of file diff --git a/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-eth_getTransactionByHash.json b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-eth_getTransactionByHash.json new file mode 100644 index 000000000..4a433af06 --- /dev/null +++ b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-eth_getTransactionByHash.json @@ -0,0 +1,3 @@ +{ + invalid +} \ No newline at end of file diff --git a/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-txlist.json b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-txlist.json new file mode 100644 index 000000000..99bc31f27 --- /dev/null +++ b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-txlist.json @@ -0,0 +1,106 @@ +{ + "status": "1", + "result": [ + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "64", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1567780614", + "nonce": "8", + "isError": "0", + "input": "0x6d61696c636861696e010a82012ee10c59024c836d7ca12470b5ac74673002127ddedadbc6fc4375a8c086b650060ede199f603a158bc7884a903eadf97a2dd0fbe69ac81c216830f94e56b847d924b51a7d8227c80714219e6821a51bc7cba922f291a47bdffe29e7c3f67ad908ff377bfcc0b603007ead4bfd87ff0acc272528ca03d6381e6d0e1e2c5dfd24d521", + "hash": "0x2bf261a81e624d649450a3851df2d0639a8b98ed3bce39da14e8f318adf3edb9", + "gasUsed": "30660", + "gasPrice": "20000000000", + "gas": "30660", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "2465679", + "contractAddress": "", + "confirmations": "997967", + "blockNumber": "8496979", + "blockHash": "0x94fa39de3950f87d9225d2146b19981e5824f271d1049348462cc2ee2f20f94f" + }, + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "44", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562705082", + "nonce": "6", + "isError": "0", + "input": "0x6d61696c636861696e501282022e88fd4a3b84ad56b52b85158bfff5130203606434267e17f7c81f83e41d6c700ed9967822ed42d02a43b65e625b99034485f980fdf02799ac7743970099bafaa095a14cd187eecd7078a30b8a7a5c5be21eb2026b2e63252f21c05c724ad0c8c6e3fa94938c1007c5e1b6c7739c3b1201f3ddc390fa08a358c181785d35211ac0d574e58e47649c4d97aa1d1025e27432b18c04921197666f7e4186849db4ff8edb0ebac7d5368d3d109c49fb0345c56167747ff62bf46e7834f954633f5d311715988b4a7fbb26443cbc13ed91530acf1a685b429dc559a1df3b45118fc3abf336f2914f10f728b1de56c3841aba92a8b9d819a5dd3316055d2ee00c8e2bfb8fbf1a221620883c9a5ec19abceadb51d9a667e0e6db48a87d8251ed9b2a61da6a71450dcccf", + "hash": "0xdbd102bba397330152f858f1e86a4e121cd1c8d73b744223da750176246e1956", + "gasUsed": "41812", + "gasPrice": "4000000000", + "gas": "41812", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "4482561", + "contractAddress": "", + "confirmations": "1375668", + "blockNumber": "8119278", + "blockHash": "0xe58eab170d5f004127c4af26b4ff1e38b0e01fe34d0b478b77e7d98ceed62863" + }, + { + "value": "7899999999790000", + "txreceipt_status": "1", + "transactionIndex": "0", + "to": "0xc0302c62ced73abdf1d034553fe059bb596f2ef2", + "timeStamp": "1562704840", + "nonce": "0", + "isError": "0", + "input": "0x", + "hash": "0x2ec7f3e57c65f9a58f45e812db96f466a471d6fd6896edd638c962758b9c942f", + "gasUsed": "21000", + "gasPrice": "100000000000", + "gas": "21000", + "from": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "cumulativeGasUsed": "21000", + "contractAddress": "", + "confirmations": "1375691", + "blockNumber": "8119255", + "blockHash": "0xa47bc8ebcfb492c21f7dac255e5d92a9db91854fec62216fd3d4460d92e079e5" + }, + { + "value": "10000000000000000", + "txreceipt_status": "1", + "transactionIndex": "55", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562704809", + "nonce": "4", + "isError": "0", + "input": "0x", + "hash": "0xaad4525d6f0023a374cf2a0aba89365b0c6589650f9112b98313d72f6327d594", + "gasUsed": "21000", + "gasPrice": "4000000000", + "gas": "21000", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "5022954", + "contractAddress": "", + "confirmations": "1375693", + "blockNumber": "8119253", + "blockHash": "0x2267a3ad702dcb05e25999c9f142968faa299dd53540c24427e6ddfd3f29cae7" + }, + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "143", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562704602", + "nonce": "2", + "isError": "0", + "input": "0x", + "hash": "0xe4cc1bb86b42aed2d24f7ab0e437f62b6be19687f25f6d054a4db9666ec2211e", + "gasUsed": "21000", + "gasPrice": "4000000000", + "gas": "21000", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "7702352", + "contractAddress": "", + "confirmations": "1375704", + "blockNumber": "8119242", + "blockHash": "0x09d2cc61ab9eb28194b376d7dd0493ebebb196ab3bdbd9385c6b5690587ccbe6" + } + ], + "message": "OK" + } \ No newline at end of file diff --git a/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transactions-by-address-txlist.json b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transactions-by-address-txlist.json new file mode 100644 index 000000000..f0ff25cbf --- /dev/null +++ b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transactions-by-address-txlist.json @@ -0,0 +1,3 @@ +{ + invalid +} \ No newline at end of file diff --git a/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-eth_getTransactionByHash.json b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-eth_getTransactionByHash.json new file mode 100644 index 000000000..1a0f12961 --- /dev/null +++ b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-eth_getTransactionByHash.json @@ -0,0 +1,25 @@ +{ + "jsonrpc": "2.0", + "id": 24, + "result": { + "hash": "0x2ec7f3e57c65f9a58f45e812db96f466a471d6fd6896edd638c962758b9c942f", + "blockHash": "0xa47bc8ebcfb492c21f7dac255e5d92a9db91854fec62216fd3d4460d92e079e5", + "blockNumber": "0x7be3d7", + "chainId": "0x1", + "condition": null, + "creates": null, + "from": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "gas": "0x5208", + "gasPrice": "0x174876e800", + "input": "0x", + "nonce": "0x0", + "publicKey": "0x97c97c126b492186a4d5b28f34f0671a5aacc974da3bde0be93e45a1c50f89ceff72bd04ac9e25a04a1a6cb010aedaf65f91cec8ebe75901c49b63355d", + "r": "0x405", + "s": "0x988", + "standardV": "0x0", + "to": "0xc0302c62ced73abdf1d034553fe059bb596f2ef2", + "transactionIndex": "0x0", + "v": "0x25", + "value": "0x1c110215b68bb0" + } +} \ No newline at end of file diff --git a/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-txlist.json b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-txlist.json new file mode 100644 index 000000000..99bc31f27 --- /dev/null +++ b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-txlist.json @@ -0,0 +1,106 @@ +{ + "status": "1", + "result": [ + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "64", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1567780614", + "nonce": "8", + "isError": "0", + "input": "0x6d61696c636861696e010a82012ee10c59024c836d7ca12470b5ac74673002127ddedadbc6fc4375a8c086b650060ede199f603a158bc7884a903eadf97a2dd0fbe69ac81c216830f94e56b847d924b51a7d8227c80714219e6821a51bc7cba922f291a47bdffe29e7c3f67ad908ff377bfcc0b603007ead4bfd87ff0acc272528ca03d6381e6d0e1e2c5dfd24d521", + "hash": "0x2bf261a81e624d649450a3851df2d0639a8b98ed3bce39da14e8f318adf3edb9", + "gasUsed": "30660", + "gasPrice": "20000000000", + "gas": "30660", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "2465679", + "contractAddress": "", + "confirmations": "997967", + "blockNumber": "8496979", + "blockHash": "0x94fa39de3950f87d9225d2146b19981e5824f271d1049348462cc2ee2f20f94f" + }, + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "44", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562705082", + "nonce": "6", + "isError": "0", + "input": "0x6d61696c636861696e501282022e88fd4a3b84ad56b52b85158bfff5130203606434267e17f7c81f83e41d6c700ed9967822ed42d02a43b65e625b99034485f980fdf02799ac7743970099bafaa095a14cd187eecd7078a30b8a7a5c5be21eb2026b2e63252f21c05c724ad0c8c6e3fa94938c1007c5e1b6c7739c3b1201f3ddc390fa08a358c181785d35211ac0d574e58e47649c4d97aa1d1025e27432b18c04921197666f7e4186849db4ff8edb0ebac7d5368d3d109c49fb0345c56167747ff62bf46e7834f954633f5d311715988b4a7fbb26443cbc13ed91530acf1a685b429dc559a1df3b45118fc3abf336f2914f10f728b1de56c3841aba92a8b9d819a5dd3316055d2ee00c8e2bfb8fbf1a221620883c9a5ec19abceadb51d9a667e0e6db48a87d8251ed9b2a61da6a71450dcccf", + "hash": "0xdbd102bba397330152f858f1e86a4e121cd1c8d73b744223da750176246e1956", + "gasUsed": "41812", + "gasPrice": "4000000000", + "gas": "41812", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "4482561", + "contractAddress": "", + "confirmations": "1375668", + "blockNumber": "8119278", + "blockHash": "0xe58eab170d5f004127c4af26b4ff1e38b0e01fe34d0b478b77e7d98ceed62863" + }, + { + "value": "7899999999790000", + "txreceipt_status": "1", + "transactionIndex": "0", + "to": "0xc0302c62ced73abdf1d034553fe059bb596f2ef2", + "timeStamp": "1562704840", + "nonce": "0", + "isError": "0", + "input": "0x", + "hash": "0x2ec7f3e57c65f9a58f45e812db96f466a471d6fd6896edd638c962758b9c942f", + "gasUsed": "21000", + "gasPrice": "100000000000", + "gas": "21000", + "from": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "cumulativeGasUsed": "21000", + "contractAddress": "", + "confirmations": "1375691", + "blockNumber": "8119255", + "blockHash": "0xa47bc8ebcfb492c21f7dac255e5d92a9db91854fec62216fd3d4460d92e079e5" + }, + { + "value": "10000000000000000", + "txreceipt_status": "1", + "transactionIndex": "55", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562704809", + "nonce": "4", + "isError": "0", + "input": "0x", + "hash": "0xaad4525d6f0023a374cf2a0aba89365b0c6589650f9112b98313d72f6327d594", + "gasUsed": "21000", + "gasPrice": "4000000000", + "gas": "21000", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "5022954", + "contractAddress": "", + "confirmations": "1375693", + "blockNumber": "8119253", + "blockHash": "0x2267a3ad702dcb05e25999c9f142968faa299dd53540c24427e6ddfd3f29cae7" + }, + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "143", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562704602", + "nonce": "2", + "isError": "0", + "input": "0x", + "hash": "0xe4cc1bb86b42aed2d24f7ab0e437f62b6be19687f25f6d054a4db9666ec2211e", + "gasUsed": "21000", + "gasPrice": "4000000000", + "gas": "21000", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "7702352", + "contractAddress": "", + "confirmations": "1375704", + "blockNumber": "8119242", + "blockHash": "0x09d2cc61ab9eb28194b376d7dd0493ebebb196ab3bdbd9385c6b5690587ccbe6" + } + ], + "message": "OK" + } \ No newline at end of file diff --git a/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/success-eth_getTransactionByHash.json b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/success-eth_getTransactionByHash.json new file mode 100644 index 000000000..2e158c3ba --- /dev/null +++ b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/success-eth_getTransactionByHash.json @@ -0,0 +1,25 @@ +{ + "jsonrpc": "2.0", + "id": 24, + "result": { + "hash": "0x2ec7f3e57c65f9a58f45e812db96f466a471d6fd6896edd638c962758b9c942f", + "blockHash": "0xa47bc8ebcfb492c21f7dac255e5d92a9db91854fec62216fd3d4460d92e079e5", + "blockNumber": "0x7be3d7", + "chainId": "0x1", + "condition": null, + "creates": null, + "from": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "gas": "0x5208", + "gasPrice": "0x174876e800", + "input": "0x", + "nonce": "0x0", + "publicKey": "0xbdf6fb97c97c126b492186a4d5b28f34f0671a5aacc974da3bde0be93e45a1c50f89ceff72bd04ac9e25a04a1a6cb010aedaf65f91cec8ebe75901c49b63355d", + "r": "0x405260f1e8e4e2f9bd2e8b9928c720688fbe3361e83b24601447f690be881fc", + "s": "0x9880cb0521cc37de1f50de4f61cf6c89f0ce9f2691f56c14a4ac8d8c1e799ce", + "standardV": "0x0", + "to": "0xc0302c62ced73abdf1d034553fe059bb596f2ef2", + "transactionIndex": "0x0", + "v": "0x25", + "value": "0x1c110215b68bb0" + } +} \ No newline at end of file diff --git a/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/success-txlist.json b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/success-txlist.json new file mode 100644 index 000000000..99bc31f27 --- /dev/null +++ b/internal/clients/etherscan/testdata/TestAPIClient_PublicKeyFromAddress/success-txlist.json @@ -0,0 +1,106 @@ +{ + "status": "1", + "result": [ + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "64", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1567780614", + "nonce": "8", + "isError": "0", + "input": "0x6d61696c636861696e010a82012ee10c59024c836d7ca12470b5ac74673002127ddedadbc6fc4375a8c086b650060ede199f603a158bc7884a903eadf97a2dd0fbe69ac81c216830f94e56b847d924b51a7d8227c80714219e6821a51bc7cba922f291a47bdffe29e7c3f67ad908ff377bfcc0b603007ead4bfd87ff0acc272528ca03d6381e6d0e1e2c5dfd24d521", + "hash": "0x2bf261a81e624d649450a3851df2d0639a8b98ed3bce39da14e8f318adf3edb9", + "gasUsed": "30660", + "gasPrice": "20000000000", + "gas": "30660", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "2465679", + "contractAddress": "", + "confirmations": "997967", + "blockNumber": "8496979", + "blockHash": "0x94fa39de3950f87d9225d2146b19981e5824f271d1049348462cc2ee2f20f94f" + }, + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "44", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562705082", + "nonce": "6", + "isError": "0", + "input": "0x6d61696c636861696e501282022e88fd4a3b84ad56b52b85158bfff5130203606434267e17f7c81f83e41d6c700ed9967822ed42d02a43b65e625b99034485f980fdf02799ac7743970099bafaa095a14cd187eecd7078a30b8a7a5c5be21eb2026b2e63252f21c05c724ad0c8c6e3fa94938c1007c5e1b6c7739c3b1201f3ddc390fa08a358c181785d35211ac0d574e58e47649c4d97aa1d1025e27432b18c04921197666f7e4186849db4ff8edb0ebac7d5368d3d109c49fb0345c56167747ff62bf46e7834f954633f5d311715988b4a7fbb26443cbc13ed91530acf1a685b429dc559a1df3b45118fc3abf336f2914f10f728b1de56c3841aba92a8b9d819a5dd3316055d2ee00c8e2bfb8fbf1a221620883c9a5ec19abceadb51d9a667e0e6db48a87d8251ed9b2a61da6a71450dcccf", + "hash": "0xdbd102bba397330152f858f1e86a4e121cd1c8d73b744223da750176246e1956", + "gasUsed": "41812", + "gasPrice": "4000000000", + "gas": "41812", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "4482561", + "contractAddress": "", + "confirmations": "1375668", + "blockNumber": "8119278", + "blockHash": "0xe58eab170d5f004127c4af26b4ff1e38b0e01fe34d0b478b77e7d98ceed62863" + }, + { + "value": "7899999999790000", + "txreceipt_status": "1", + "transactionIndex": "0", + "to": "0xc0302c62ced73abdf1d034553fe059bb596f2ef2", + "timeStamp": "1562704840", + "nonce": "0", + "isError": "0", + "input": "0x", + "hash": "0x2ec7f3e57c65f9a58f45e812db96f466a471d6fd6896edd638c962758b9c942f", + "gasUsed": "21000", + "gasPrice": "100000000000", + "gas": "21000", + "from": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "cumulativeGasUsed": "21000", + "contractAddress": "", + "confirmations": "1375691", + "blockNumber": "8119255", + "blockHash": "0xa47bc8ebcfb492c21f7dac255e5d92a9db91854fec62216fd3d4460d92e079e5" + }, + { + "value": "10000000000000000", + "txreceipt_status": "1", + "transactionIndex": "55", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562704809", + "nonce": "4", + "isError": "0", + "input": "0x", + "hash": "0xaad4525d6f0023a374cf2a0aba89365b0c6589650f9112b98313d72f6327d594", + "gasUsed": "21000", + "gasPrice": "4000000000", + "gas": "21000", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "5022954", + "contractAddress": "", + "confirmations": "1375693", + "blockNumber": "8119253", + "blockHash": "0x2267a3ad702dcb05e25999c9f142968faa299dd53540c24427e6ddfd3f29cae7" + }, + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "143", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562704602", + "nonce": "2", + "isError": "0", + "input": "0x", + "hash": "0xe4cc1bb86b42aed2d24f7ab0e437f62b6be19687f25f6d054a4db9666ec2211e", + "gasUsed": "21000", + "gasPrice": "4000000000", + "gas": "21000", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "7702352", + "contractAddress": "", + "confirmations": "1375704", + "blockNumber": "8119242", + "blockHash": "0x09d2cc61ab9eb28194b376d7dd0493ebebb196ab3bdbd9385c6b5690587ccbe6" + } + ], + "message": "OK" + } \ No newline at end of file From 91d146170c9642d795e297667a3fd715a681b518 Mon Sep 17 00:00:00 2001 From: Rob De Feo Date: Sun, 16 Feb 2020 16:14:59 +0000 Subject: [PATCH 4/7] feat: get public key from address --- internal/clients/blockscout/api.go | 2 +- internal/clients/blockscout/api_test.go | 9 +- internal/clients/blockscout/pubkey_finder.go | 78 +++++ .../clients/blockscout/pubkey_finder_test.go | 268 ++++++++++++++++++ .../err-get-result-from-hash-txlist.json | 26 ++ .../err-get-transaction-by-hash-rpc.json | 8 + .../err-get-transaction-by-hash-txlist.json | 106 +++++++ ...rr-get-transactions-by-address-txlist.json | 3 + .../err-invalid-public-key-rpc.json | 25 ++ .../err-invalid-public-key-txlist.json | 106 +++++++ .../success-rpc.json | 25 ++ .../success-txlist.json | 106 +++++++ 12 files changed, 758 insertions(+), 4 deletions(-) create mode 100644 internal/clients/blockscout/pubkey_finder.go create mode 100644 internal/clients/blockscout/pubkey_finder_test.go create mode 100644 internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-result-from-hash-txlist.json create mode 100644 internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-rpc.json create mode 100644 internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-txlist.json create mode 100644 internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transactions-by-address-txlist.json create mode 100644 internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-rpc.json create mode 100644 internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-txlist.json create mode 100644 internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/success-rpc.json create mode 100644 internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/success-txlist.json diff --git a/internal/clients/blockscout/api.go b/internal/clients/blockscout/api.go index 57cbcb2de..457056948 100644 --- a/internal/clients/blockscout/api.go +++ b/internal/clients/blockscout/api.go @@ -84,5 +84,5 @@ func (c APIClient) getTransactionByHash(network string, hash common.Hash) (*type tx, _, err := client.TransactionByHash(context.Background(), hash) - return tx, nil + return tx, err } diff --git a/internal/clients/blockscout/api_test.go b/internal/clients/blockscout/api_test.go index 923b6cfc4..d969d4da1 100644 --- a/internal/clients/blockscout/api_test.go +++ b/internal/clients/blockscout/api_test.go @@ -34,7 +34,10 @@ func TestNewAPIClient(t *testing.T) { want := &APIClient{ key: "api-key", networkConfigs: map[string]networkConfig{ - ethereum.Mainnet: {url: "https://blockscout.com/eth/mainnet/api"}, + ethereum.Mainnet: { + rpcURL: "https://relay.mailchain.xyz/json-rpc/ethereum/mainnet", + url: "https://blockscout.com/eth/mainnet/api", + }, }, } if !assert.Equal(t, want, apiClient) { @@ -208,7 +211,7 @@ func TestGetTransactionByHash(t *testing.T) { args{ "TestNetwork", }, - errors.New("Invalid address format"), + errors.New("not found"), true, nil, }, @@ -226,7 +229,7 @@ func TestGetTransactionByHash(t *testing.T) { args{ "TestNetwork", }, - errors.New("unexpected end of JSON input"), + errors.New("EOF"), true, nil, }, diff --git a/internal/clients/blockscout/pubkey_finder.go b/internal/clients/blockscout/pubkey_finder.go new file mode 100644 index 000000000..5239a6324 --- /dev/null +++ b/internal/clients/blockscout/pubkey_finder.go @@ -0,0 +1,78 @@ +// Copyright 2019 Finobo +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blockscout + +import ( + "context" + "strings" + + "github.com/ethereum/go-ethereum/common" + "github.com/mailchain/mailchain/crypto" + "github.com/mailchain/mailchain/crypto/secp256k1" + "github.com/mailchain/mailchain/encoding" + "github.com/mailchain/mailchain/internal/protocols/ethereum" + "github.com/pkg/errors" +) + +// PublicKeyFromAddress get public key from the recipient address, this will only work if the recipient has previously sent a message. +func (c APIClient) PublicKeyFromAddress(ctx context.Context, protocol, network string, address []byte) (crypto.PublicKey, error) { + if !c.isNetworkSupported(network) { + return nil, errors.Errorf("network not supported") + } + + txResult, err := c.getTransactionsByAddress(network, address) + if err != nil { + return nil, errors.WithStack(err) + } + + hash, err := getFromResultHash(encoding.EncodeHexZeroX(address), txResult) + if err != nil { + return nil, errors.WithStack(err) + } + + tx, err := c.getTransactionByHash(network, hash) + if err != nil { + return nil, errors.WithStack(err) + } + + v, r, s := tx.RawSignatureValues() + keyBytes, err := ethereum.GetPublicKeyFromTransaction( + r, s, v, + tx.To().Bytes(), + tx.Data(), + tx.Nonce(), + tx.GasPrice(), + tx.Gas(), + tx.Value()) + if err != nil { + return nil, errors.WithMessage(err, "could not get public key from raw hash") + } + + return secp256k1.PublicKeyFromBytes(keyBytes) +} + +func getFromResultHash(address string, txResult *txList) (common.Hash, error) { + if len(txResult.Result) == 0 { + return common.Hash{}, errors.Errorf("No transactions found for address: %v", address) + } + + for i := range txResult.Result { + x := txResult.Result[i] + if strings.EqualFold(x.From, address) { + return common.HexToHash(x.Hash), nil + } + } + return common.Hash{}, errors.Errorf("No transactions from address found") +} diff --git a/internal/clients/blockscout/pubkey_finder_test.go b/internal/clients/blockscout/pubkey_finder_test.go new file mode 100644 index 000000000..5bbd8c05c --- /dev/null +++ b/internal/clients/blockscout/pubkey_finder_test.go @@ -0,0 +1,268 @@ +// Copyright 2019 Finobo +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blockscout + +import ( + "context" + "fmt" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/mailchain/mailchain/crypto" + "github.com/mailchain/mailchain/crypto/secp256k1" + "github.com/mailchain/mailchain/encoding/encodingtest" + "github.com/stretchr/testify/assert" +) + +func TestGetFromResultHash(t *testing.T) { + + mockTxResult := []txResult{ + {From: "address1", Hash: "aaa111"}, + {From: "address2", Hash: "bbb222"}, + {From: "address3", Hash: "ccc333"}, + } + + type args struct { + address string + txList *txList + } + + testCases := []struct { + name string + args args + want common.Hash + wantErr bool + }{ + { + "err-empty-transaction-list", + args{ + "should-not-matter", + &txList{ + Status: "", + Message: "", + Result: []txResult{}, + }, + }, + common.Hash{}, + true, + }, + { + "match-transaction-1", + args{ + "address1", + &txList{ + Status: "", + Message: "", + Result: mockTxResult, + }, + }, + common.HexToHash("aaa111"), + false, + }, + { + "match-transaction-3", + args{ + "address3", + &txList{ + Status: "", + Message: "", + Result: mockTxResult, + }, + }, + common.HexToHash("ccc333"), + false, + }, + { + "err-no-matching-transactions", + args{ + "address11", + &txList{ + Status: "", + Message: "", + Result: mockTxResult, + }, + }, + common.Hash{}, + true, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + hash, err := getFromResultHash(testCase.args.address, testCase.args.txList) + + if (err != nil) != testCase.wantErr { + t.Errorf("getFromResultHash() error = %v, wantErr %v", err, testCase.wantErr) + return + } + if !assert.Equal(t, testCase.want, hash) { + t.Errorf("getFromResultHash() = %v, want %v", hash, testCase.want) + } + }) + } +} + +func TestAPIClient_PublicKeyFromAddress(t *testing.T) { + type fields struct { + key string + } + type args struct { + ctx context.Context + protocol string + network string + address []byte + } + tests := []struct { + name string + fields fields + args args + want crypto.PublicKey + wantErr bool + }{ + { + "success", + fields{ + "", + }, + args{ + context.Background(), + "ethereum", + "mainnet", + encodingtest.MustDecodeHexZeroX("0x92D8f10248C6a3953CC3692A894655ad05D61Efb"), + }, + func() crypto.PublicKey { + k, _ := secp256k1.PublicKeyFromBytes(encodingtest.MustDecodeHexZeroX("0xbdf6fb97c97c126b492186a4d5b28f34f0671a5aacc974da3bde0be93e45a1c50f89ceff72bd04ac9e25a04a1a6cb010aedaf65f91cec8ebe75901c49b63355d")) + return k + }(), + false, + }, + { + "err-invalid-public-key", + fields{ + "", + }, + args{ + context.Background(), + "ethereum", + "mainnet", + encodingtest.MustDecodeHexZeroX("0x92D8f10248C6a3953CC3692A894655ad05D61Efb"), + }, + nil, + true, + }, + { + "err-get-transaction-by-hash", + fields{ + "", + }, + args{ + context.Background(), + "ethereum", + "mainnet", + encodingtest.MustDecodeHexZeroX("0x92D8f10248C6a3953CC3692A894655ad05D61Efb"), + }, + nil, + true, + }, + { + "err-get-result-from-hash", + fields{ + "", + }, + args{ + context.Background(), + "ethereum", + "mainnet", + encodingtest.MustDecodeHexZeroX("0x92D8f10248C6a3953CC3692A894655ad05D61Efb"), + }, + nil, + true, + }, + { + "err-get-transactions-by-address", + fields{ + "", + }, + args{ + context.Background(), + "ethereum", + "mainnet", + encodingtest.MustDecodeHexZeroX("0x92D8f10248C6a3953CC3692A894655ad05D61Efb"), + }, + nil, + true, + }, + { + "err-invalid-network", + fields{ + "", + }, + args{ + context.Background(), + "ethereum", + "invalid", + encodingtest.MustDecodeHexZeroX("0x92D8f10248C6a3953CC3692A894655ad05D61Efb"), + }, + nil, + true, + }, + } + for _, tt := range tests { + testName := t.Name() + t.Run(tt.name, func(t *testing.T) { + server := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + golden, err := ioutil.ReadFile(fmt.Sprintf("./testdata/%s/%s-txlist.json", testName, tt.name)) + if err != nil { + t.Log(r.URL.String()) + assert.FailNow(t, err.Error()) + } + w.Write([]byte(golden)) + }), + ) + defer server.Close() + rpcServer := httptest.NewServer( + http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + golden, err := ioutil.ReadFile(fmt.Sprintf("./testdata/%s/%s-rpc.json", testName, tt.name)) + if err != nil { + t.Log(r.URL.String()) + assert.FailNow(t, err.Error()) + } + w.Write([]byte(golden)) + }), + ) + c := APIClient{ + key: tt.fields.key, + networkConfigs: map[string]networkConfig{ + "mainnet": networkConfig{ + url: server.URL, + rpcURL: rpcServer.URL, + }, + }, + } + got, err := c.PublicKeyFromAddress(tt.args.ctx, tt.args.protocol, tt.args.network, tt.args.address) + if (err != nil) != tt.wantErr { + t.Errorf("APIClient.PublicKeyFromAddress() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !assert.Equal(t, tt.want, got) { + t.Errorf("APIClient.PublicKeyFromAddress() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-result-from-hash-txlist.json b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-result-from-hash-txlist.json new file mode 100644 index 000000000..974cd80d7 --- /dev/null +++ b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-result-from-hash-txlist.json @@ -0,0 +1,26 @@ +{ + "status": "1", + "result": [ + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "44", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562705082", + "nonce": "6", + "isError": "0", + "input": "0x6d61696c636861696e501282022e88fd4a3b84ad56b52b85158bfff5130203606434267e17f7c81f83e41d6c700ed9967822ed42d02a43b65e625b99034485f980fdf02799ac7743970099bafaa095a14cd187eecd7078a30b8a7a5c5be21eb2026b2e63252f21c05c724ad0c8c6e3fa94938c1007c5e1b6c7739c3b1201f3ddc390fa08a358c181785d35211ac0d574e58e47649c4d97aa1d1025e27432b18c04921197666f7e4186849db4ff8edb0ebac7d5368d3d109c49fb0345c56167747ff62bf46e7834f954633f5d311715988b4a7fbb26443cbc13ed91530acf1a685b429dc559a1df3b45118fc3abf336f2914f10f728b1de56c3841aba92a8b9d819a5dd3316055d2ee00c8e2bfb8fbf1a221620883c9a5ec19abceadb51d9a667e0e6db48a87d8251ed9b2a61da6a71450dcccf", + "hash": "0xdbd102bba397330152f858f1e86a4e121cd1c8d73b744223da750176246e1956", + "gasUsed": "41812", + "gasPrice": "4000000000", + "gas": "41812", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "4482561", + "contractAddress": "", + "confirmations": "1375668", + "blockNumber": "8119278", + "blockHash": "0xe58eab170d5f004127c4af26b4ff1e38b0e01fe34d0b478b77e7d98ceed62863" + } + ], + "message": "OK" + } \ No newline at end of file diff --git a/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-rpc.json b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-rpc.json new file mode 100644 index 000000000..ab1b75a4b --- /dev/null +++ b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-rpc.json @@ -0,0 +1,8 @@ +{ + "jsonrpc": "2.0", + "error": { + "code": -32602, + "message": "Invalid params: invalid length 63, expected a 0x-prefixed hex string with length of 64." + }, + "id": 24 +} \ No newline at end of file diff --git a/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-txlist.json b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-txlist.json new file mode 100644 index 000000000..99bc31f27 --- /dev/null +++ b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transaction-by-hash-txlist.json @@ -0,0 +1,106 @@ +{ + "status": "1", + "result": [ + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "64", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1567780614", + "nonce": "8", + "isError": "0", + "input": "0x6d61696c636861696e010a82012ee10c59024c836d7ca12470b5ac74673002127ddedadbc6fc4375a8c086b650060ede199f603a158bc7884a903eadf97a2dd0fbe69ac81c216830f94e56b847d924b51a7d8227c80714219e6821a51bc7cba922f291a47bdffe29e7c3f67ad908ff377bfcc0b603007ead4bfd87ff0acc272528ca03d6381e6d0e1e2c5dfd24d521", + "hash": "0x2bf261a81e624d649450a3851df2d0639a8b98ed3bce39da14e8f318adf3edb9", + "gasUsed": "30660", + "gasPrice": "20000000000", + "gas": "30660", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "2465679", + "contractAddress": "", + "confirmations": "997967", + "blockNumber": "8496979", + "blockHash": "0x94fa39de3950f87d9225d2146b19981e5824f271d1049348462cc2ee2f20f94f" + }, + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "44", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562705082", + "nonce": "6", + "isError": "0", + "input": "0x6d61696c636861696e501282022e88fd4a3b84ad56b52b85158bfff5130203606434267e17f7c81f83e41d6c700ed9967822ed42d02a43b65e625b99034485f980fdf02799ac7743970099bafaa095a14cd187eecd7078a30b8a7a5c5be21eb2026b2e63252f21c05c724ad0c8c6e3fa94938c1007c5e1b6c7739c3b1201f3ddc390fa08a358c181785d35211ac0d574e58e47649c4d97aa1d1025e27432b18c04921197666f7e4186849db4ff8edb0ebac7d5368d3d109c49fb0345c56167747ff62bf46e7834f954633f5d311715988b4a7fbb26443cbc13ed91530acf1a685b429dc559a1df3b45118fc3abf336f2914f10f728b1de56c3841aba92a8b9d819a5dd3316055d2ee00c8e2bfb8fbf1a221620883c9a5ec19abceadb51d9a667e0e6db48a87d8251ed9b2a61da6a71450dcccf", + "hash": "0xdbd102bba397330152f858f1e86a4e121cd1c8d73b744223da750176246e1956", + "gasUsed": "41812", + "gasPrice": "4000000000", + "gas": "41812", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "4482561", + "contractAddress": "", + "confirmations": "1375668", + "blockNumber": "8119278", + "blockHash": "0xe58eab170d5f004127c4af26b4ff1e38b0e01fe34d0b478b77e7d98ceed62863" + }, + { + "value": "7899999999790000", + "txreceipt_status": "1", + "transactionIndex": "0", + "to": "0xc0302c62ced73abdf1d034553fe059bb596f2ef2", + "timeStamp": "1562704840", + "nonce": "0", + "isError": "0", + "input": "0x", + "hash": "0x2ec7f3e57c65f9a58f45e812db96f466a471d6fd6896edd638c962758b9c942f", + "gasUsed": "21000", + "gasPrice": "100000000000", + "gas": "21000", + "from": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "cumulativeGasUsed": "21000", + "contractAddress": "", + "confirmations": "1375691", + "blockNumber": "8119255", + "blockHash": "0xa47bc8ebcfb492c21f7dac255e5d92a9db91854fec62216fd3d4460d92e079e5" + }, + { + "value": "10000000000000000", + "txreceipt_status": "1", + "transactionIndex": "55", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562704809", + "nonce": "4", + "isError": "0", + "input": "0x", + "hash": "0xaad4525d6f0023a374cf2a0aba89365b0c6589650f9112b98313d72f6327d594", + "gasUsed": "21000", + "gasPrice": "4000000000", + "gas": "21000", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "5022954", + "contractAddress": "", + "confirmations": "1375693", + "blockNumber": "8119253", + "blockHash": "0x2267a3ad702dcb05e25999c9f142968faa299dd53540c24427e6ddfd3f29cae7" + }, + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "143", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562704602", + "nonce": "2", + "isError": "0", + "input": "0x", + "hash": "0xe4cc1bb86b42aed2d24f7ab0e437f62b6be19687f25f6d054a4db9666ec2211e", + "gasUsed": "21000", + "gasPrice": "4000000000", + "gas": "21000", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "7702352", + "contractAddress": "", + "confirmations": "1375704", + "blockNumber": "8119242", + "blockHash": "0x09d2cc61ab9eb28194b376d7dd0493ebebb196ab3bdbd9385c6b5690587ccbe6" + } + ], + "message": "OK" + } \ No newline at end of file diff --git a/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transactions-by-address-txlist.json b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transactions-by-address-txlist.json new file mode 100644 index 000000000..f0ff25cbf --- /dev/null +++ b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-get-transactions-by-address-txlist.json @@ -0,0 +1,3 @@ +{ + invalid +} \ No newline at end of file diff --git a/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-rpc.json b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-rpc.json new file mode 100644 index 000000000..1a0f12961 --- /dev/null +++ b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-rpc.json @@ -0,0 +1,25 @@ +{ + "jsonrpc": "2.0", + "id": 24, + "result": { + "hash": "0x2ec7f3e57c65f9a58f45e812db96f466a471d6fd6896edd638c962758b9c942f", + "blockHash": "0xa47bc8ebcfb492c21f7dac255e5d92a9db91854fec62216fd3d4460d92e079e5", + "blockNumber": "0x7be3d7", + "chainId": "0x1", + "condition": null, + "creates": null, + "from": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "gas": "0x5208", + "gasPrice": "0x174876e800", + "input": "0x", + "nonce": "0x0", + "publicKey": "0x97c97c126b492186a4d5b28f34f0671a5aacc974da3bde0be93e45a1c50f89ceff72bd04ac9e25a04a1a6cb010aedaf65f91cec8ebe75901c49b63355d", + "r": "0x405", + "s": "0x988", + "standardV": "0x0", + "to": "0xc0302c62ced73abdf1d034553fe059bb596f2ef2", + "transactionIndex": "0x0", + "v": "0x25", + "value": "0x1c110215b68bb0" + } +} \ No newline at end of file diff --git a/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-txlist.json b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-txlist.json new file mode 100644 index 000000000..99bc31f27 --- /dev/null +++ b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/err-invalid-public-key-txlist.json @@ -0,0 +1,106 @@ +{ + "status": "1", + "result": [ + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "64", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1567780614", + "nonce": "8", + "isError": "0", + "input": "0x6d61696c636861696e010a82012ee10c59024c836d7ca12470b5ac74673002127ddedadbc6fc4375a8c086b650060ede199f603a158bc7884a903eadf97a2dd0fbe69ac81c216830f94e56b847d924b51a7d8227c80714219e6821a51bc7cba922f291a47bdffe29e7c3f67ad908ff377bfcc0b603007ead4bfd87ff0acc272528ca03d6381e6d0e1e2c5dfd24d521", + "hash": "0x2bf261a81e624d649450a3851df2d0639a8b98ed3bce39da14e8f318adf3edb9", + "gasUsed": "30660", + "gasPrice": "20000000000", + "gas": "30660", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "2465679", + "contractAddress": "", + "confirmations": "997967", + "blockNumber": "8496979", + "blockHash": "0x94fa39de3950f87d9225d2146b19981e5824f271d1049348462cc2ee2f20f94f" + }, + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "44", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562705082", + "nonce": "6", + "isError": "0", + "input": "0x6d61696c636861696e501282022e88fd4a3b84ad56b52b85158bfff5130203606434267e17f7c81f83e41d6c700ed9967822ed42d02a43b65e625b99034485f980fdf02799ac7743970099bafaa095a14cd187eecd7078a30b8a7a5c5be21eb2026b2e63252f21c05c724ad0c8c6e3fa94938c1007c5e1b6c7739c3b1201f3ddc390fa08a358c181785d35211ac0d574e58e47649c4d97aa1d1025e27432b18c04921197666f7e4186849db4ff8edb0ebac7d5368d3d109c49fb0345c56167747ff62bf46e7834f954633f5d311715988b4a7fbb26443cbc13ed91530acf1a685b429dc559a1df3b45118fc3abf336f2914f10f728b1de56c3841aba92a8b9d819a5dd3316055d2ee00c8e2bfb8fbf1a221620883c9a5ec19abceadb51d9a667e0e6db48a87d8251ed9b2a61da6a71450dcccf", + "hash": "0xdbd102bba397330152f858f1e86a4e121cd1c8d73b744223da750176246e1956", + "gasUsed": "41812", + "gasPrice": "4000000000", + "gas": "41812", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "4482561", + "contractAddress": "", + "confirmations": "1375668", + "blockNumber": "8119278", + "blockHash": "0xe58eab170d5f004127c4af26b4ff1e38b0e01fe34d0b478b77e7d98ceed62863" + }, + { + "value": "7899999999790000", + "txreceipt_status": "1", + "transactionIndex": "0", + "to": "0xc0302c62ced73abdf1d034553fe059bb596f2ef2", + "timeStamp": "1562704840", + "nonce": "0", + "isError": "0", + "input": "0x", + "hash": "0x2ec7f3e57c65f9a58f45e812db96f466a471d6fd6896edd638c962758b9c942f", + "gasUsed": "21000", + "gasPrice": "100000000000", + "gas": "21000", + "from": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "cumulativeGasUsed": "21000", + "contractAddress": "", + "confirmations": "1375691", + "blockNumber": "8119255", + "blockHash": "0xa47bc8ebcfb492c21f7dac255e5d92a9db91854fec62216fd3d4460d92e079e5" + }, + { + "value": "10000000000000000", + "txreceipt_status": "1", + "transactionIndex": "55", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562704809", + "nonce": "4", + "isError": "0", + "input": "0x", + "hash": "0xaad4525d6f0023a374cf2a0aba89365b0c6589650f9112b98313d72f6327d594", + "gasUsed": "21000", + "gasPrice": "4000000000", + "gas": "21000", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "5022954", + "contractAddress": "", + "confirmations": "1375693", + "blockNumber": "8119253", + "blockHash": "0x2267a3ad702dcb05e25999c9f142968faa299dd53540c24427e6ddfd3f29cae7" + }, + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "143", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562704602", + "nonce": "2", + "isError": "0", + "input": "0x", + "hash": "0xe4cc1bb86b42aed2d24f7ab0e437f62b6be19687f25f6d054a4db9666ec2211e", + "gasUsed": "21000", + "gasPrice": "4000000000", + "gas": "21000", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "7702352", + "contractAddress": "", + "confirmations": "1375704", + "blockNumber": "8119242", + "blockHash": "0x09d2cc61ab9eb28194b376d7dd0493ebebb196ab3bdbd9385c6b5690587ccbe6" + } + ], + "message": "OK" + } \ No newline at end of file diff --git a/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/success-rpc.json b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/success-rpc.json new file mode 100644 index 000000000..2e158c3ba --- /dev/null +++ b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/success-rpc.json @@ -0,0 +1,25 @@ +{ + "jsonrpc": "2.0", + "id": 24, + "result": { + "hash": "0x2ec7f3e57c65f9a58f45e812db96f466a471d6fd6896edd638c962758b9c942f", + "blockHash": "0xa47bc8ebcfb492c21f7dac255e5d92a9db91854fec62216fd3d4460d92e079e5", + "blockNumber": "0x7be3d7", + "chainId": "0x1", + "condition": null, + "creates": null, + "from": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "gas": "0x5208", + "gasPrice": "0x174876e800", + "input": "0x", + "nonce": "0x0", + "publicKey": "0xbdf6fb97c97c126b492186a4d5b28f34f0671a5aacc974da3bde0be93e45a1c50f89ceff72bd04ac9e25a04a1a6cb010aedaf65f91cec8ebe75901c49b63355d", + "r": "0x405260f1e8e4e2f9bd2e8b9928c720688fbe3361e83b24601447f690be881fc", + "s": "0x9880cb0521cc37de1f50de4f61cf6c89f0ce9f2691f56c14a4ac8d8c1e799ce", + "standardV": "0x0", + "to": "0xc0302c62ced73abdf1d034553fe059bb596f2ef2", + "transactionIndex": "0x0", + "v": "0x25", + "value": "0x1c110215b68bb0" + } +} \ No newline at end of file diff --git a/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/success-txlist.json b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/success-txlist.json new file mode 100644 index 000000000..99bc31f27 --- /dev/null +++ b/internal/clients/blockscout/testdata/TestAPIClient_PublicKeyFromAddress/success-txlist.json @@ -0,0 +1,106 @@ +{ + "status": "1", + "result": [ + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "64", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1567780614", + "nonce": "8", + "isError": "0", + "input": "0x6d61696c636861696e010a82012ee10c59024c836d7ca12470b5ac74673002127ddedadbc6fc4375a8c086b650060ede199f603a158bc7884a903eadf97a2dd0fbe69ac81c216830f94e56b847d924b51a7d8227c80714219e6821a51bc7cba922f291a47bdffe29e7c3f67ad908ff377bfcc0b603007ead4bfd87ff0acc272528ca03d6381e6d0e1e2c5dfd24d521", + "hash": "0x2bf261a81e624d649450a3851df2d0639a8b98ed3bce39da14e8f318adf3edb9", + "gasUsed": "30660", + "gasPrice": "20000000000", + "gas": "30660", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "2465679", + "contractAddress": "", + "confirmations": "997967", + "blockNumber": "8496979", + "blockHash": "0x94fa39de3950f87d9225d2146b19981e5824f271d1049348462cc2ee2f20f94f" + }, + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "44", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562705082", + "nonce": "6", + "isError": "0", + "input": "0x6d61696c636861696e501282022e88fd4a3b84ad56b52b85158bfff5130203606434267e17f7c81f83e41d6c700ed9967822ed42d02a43b65e625b99034485f980fdf02799ac7743970099bafaa095a14cd187eecd7078a30b8a7a5c5be21eb2026b2e63252f21c05c724ad0c8c6e3fa94938c1007c5e1b6c7739c3b1201f3ddc390fa08a358c181785d35211ac0d574e58e47649c4d97aa1d1025e27432b18c04921197666f7e4186849db4ff8edb0ebac7d5368d3d109c49fb0345c56167747ff62bf46e7834f954633f5d311715988b4a7fbb26443cbc13ed91530acf1a685b429dc559a1df3b45118fc3abf336f2914f10f728b1de56c3841aba92a8b9d819a5dd3316055d2ee00c8e2bfb8fbf1a221620883c9a5ec19abceadb51d9a667e0e6db48a87d8251ed9b2a61da6a71450dcccf", + "hash": "0xdbd102bba397330152f858f1e86a4e121cd1c8d73b744223da750176246e1956", + "gasUsed": "41812", + "gasPrice": "4000000000", + "gas": "41812", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "4482561", + "contractAddress": "", + "confirmations": "1375668", + "blockNumber": "8119278", + "blockHash": "0xe58eab170d5f004127c4af26b4ff1e38b0e01fe34d0b478b77e7d98ceed62863" + }, + { + "value": "7899999999790000", + "txreceipt_status": "1", + "transactionIndex": "0", + "to": "0xc0302c62ced73abdf1d034553fe059bb596f2ef2", + "timeStamp": "1562704840", + "nonce": "0", + "isError": "0", + "input": "0x", + "hash": "0x2ec7f3e57c65f9a58f45e812db96f466a471d6fd6896edd638c962758b9c942f", + "gasUsed": "21000", + "gasPrice": "100000000000", + "gas": "21000", + "from": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "cumulativeGasUsed": "21000", + "contractAddress": "", + "confirmations": "1375691", + "blockNumber": "8119255", + "blockHash": "0xa47bc8ebcfb492c21f7dac255e5d92a9db91854fec62216fd3d4460d92e079e5" + }, + { + "value": "10000000000000000", + "txreceipt_status": "1", + "transactionIndex": "55", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562704809", + "nonce": "4", + "isError": "0", + "input": "0x", + "hash": "0xaad4525d6f0023a374cf2a0aba89365b0c6589650f9112b98313d72f6327d594", + "gasUsed": "21000", + "gasPrice": "4000000000", + "gas": "21000", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "5022954", + "contractAddress": "", + "confirmations": "1375693", + "blockNumber": "8119253", + "blockHash": "0x2267a3ad702dcb05e25999c9f142968faa299dd53540c24427e6ddfd3f29cae7" + }, + { + "value": "0", + "txreceipt_status": "1", + "transactionIndex": "143", + "to": "0x92d8f10248c6a3953cc3692a894655ad05d61efb", + "timeStamp": "1562704602", + "nonce": "2", + "isError": "0", + "input": "0x", + "hash": "0xe4cc1bb86b42aed2d24f7ab0e437f62b6be19687f25f6d054a4db9666ec2211e", + "gasUsed": "21000", + "gasPrice": "4000000000", + "gas": "21000", + "from": "0x4ad2b251246aafc2f3bdf3b690de3bf906622c51", + "cumulativeGasUsed": "7702352", + "contractAddress": "", + "confirmations": "1375704", + "blockNumber": "8119242", + "blockHash": "0x09d2cc61ab9eb28194b376d7dd0493ebebb196ab3bdbd9385c6b5690587ccbe6" + } + ], + "message": "OK" + } \ No newline at end of file From 958b1cedf715d8c67d35895f657ce8cb82134f84 Mon Sep 17 00:00:00 2001 From: Rob De Feo Date: Sun, 16 Feb 2020 16:22:07 +0000 Subject: [PATCH 5/7] remove unused api key --- internal/clients/blockscout/api.go | 4 +--- internal/clients/blockscout/api_test.go | 5 +--- .../clients/blockscout/pubkey_finder_test.go | 23 ------------------- internal/clients/blockscout/receiver_test.go | 1 - 4 files changed, 2 insertions(+), 31 deletions(-) diff --git a/internal/clients/blockscout/api.go b/internal/clients/blockscout/api.go index 457056948..7865090b9 100644 --- a/internal/clients/blockscout/api.go +++ b/internal/clients/blockscout/api.go @@ -13,9 +13,8 @@ import ( ) // NewAPIClient create new API client -func NewAPIClient(apiKey string) (*APIClient, error) { +func NewAPIClient() (*APIClient, error) { return &APIClient{ - key: apiKey, networkConfigs: map[string]networkConfig{ ethereum.Mainnet: { url: "https://blockscout.com/eth/mainnet/api", @@ -27,7 +26,6 @@ func NewAPIClient(apiKey string) (*APIClient, error) { // APIClient for talking to etherscan type APIClient struct { - key string networkConfigs map[string]networkConfig } diff --git a/internal/clients/blockscout/api_test.go b/internal/clients/blockscout/api_test.go index d969d4da1..51b2bd6ec 100644 --- a/internal/clients/blockscout/api_test.go +++ b/internal/clients/blockscout/api_test.go @@ -30,9 +30,8 @@ import ( ) func TestNewAPIClient(t *testing.T) { - apiClient, _ := NewAPIClient("api-key") + apiClient, _ := NewAPIClient() want := &APIClient{ - key: "api-key", networkConfigs: map[string]networkConfig{ ethereum.Mainnet: { rpcURL: "https://relay.mailchain.xyz/json-rpc/ethereum/mainnet", @@ -150,7 +149,6 @@ func TestGetTransactionsByAddress(t *testing.T) { ) defer server.Close() client := &APIClient{ - key: "api-key", networkConfigs: map[string]networkConfig{"TestNetwork": {url: server.URL}}, } got, err := client.getTransactionsByAddress(tt.args.network, []byte{}) @@ -248,7 +246,6 @@ func TestGetTransactionByHash(t *testing.T) { ) defer server.Close() client := &APIClient{ - key: "api-key", networkConfigs: map[string]networkConfig{"TestNetwork": {rpcURL: server.URL}}, } got, err := client.getTransactionByHash(tt.args.network, common.Hash{}) diff --git a/internal/clients/blockscout/pubkey_finder_test.go b/internal/clients/blockscout/pubkey_finder_test.go index 5bbd8c05c..3cf351995 100644 --- a/internal/clients/blockscout/pubkey_finder_test.go +++ b/internal/clients/blockscout/pubkey_finder_test.go @@ -118,9 +118,6 @@ func TestGetFromResultHash(t *testing.T) { } func TestAPIClient_PublicKeyFromAddress(t *testing.T) { - type fields struct { - key string - } type args struct { ctx context.Context protocol string @@ -129,16 +126,12 @@ func TestAPIClient_PublicKeyFromAddress(t *testing.T) { } tests := []struct { name string - fields fields args args want crypto.PublicKey wantErr bool }{ { "success", - fields{ - "", - }, args{ context.Background(), "ethereum", @@ -153,9 +146,6 @@ func TestAPIClient_PublicKeyFromAddress(t *testing.T) { }, { "err-invalid-public-key", - fields{ - "", - }, args{ context.Background(), "ethereum", @@ -167,9 +157,6 @@ func TestAPIClient_PublicKeyFromAddress(t *testing.T) { }, { "err-get-transaction-by-hash", - fields{ - "", - }, args{ context.Background(), "ethereum", @@ -181,9 +168,6 @@ func TestAPIClient_PublicKeyFromAddress(t *testing.T) { }, { "err-get-result-from-hash", - fields{ - "", - }, args{ context.Background(), "ethereum", @@ -195,9 +179,6 @@ func TestAPIClient_PublicKeyFromAddress(t *testing.T) { }, { "err-get-transactions-by-address", - fields{ - "", - }, args{ context.Background(), "ethereum", @@ -209,9 +190,6 @@ func TestAPIClient_PublicKeyFromAddress(t *testing.T) { }, { "err-invalid-network", - fields{ - "", - }, args{ context.Background(), "ethereum", @@ -247,7 +225,6 @@ func TestAPIClient_PublicKeyFromAddress(t *testing.T) { }), ) c := APIClient{ - key: tt.fields.key, networkConfigs: map[string]networkConfig{ "mainnet": networkConfig{ url: server.URL, diff --git a/internal/clients/blockscout/receiver_test.go b/internal/clients/blockscout/receiver_test.go index a07d242b0..69e4a6055 100644 --- a/internal/clients/blockscout/receiver_test.go +++ b/internal/clients/blockscout/receiver_test.go @@ -133,7 +133,6 @@ func TestReceive(t *testing.T) { ) defer server.Close() client := &APIClient{ - key: "api-key", networkConfigs: map[string]networkConfig{"TestNetwork": {url: server.URL}}, } got, err := client.Receive(tt.args.ctx, tt.args.network, []byte{}) From 39851b476a2954d2224db07d303f5ca08db6ed6b Mon Sep 17 00:00:00 2001 From: Rob De Feo Date: Sun, 16 Feb 2020 16:38:22 +0000 Subject: [PATCH 6/7] fix: linting --- internal/clients/blockscout/pubkey_finder.go | 2 ++ internal/clients/blockscout/types.go | 21 -------------------- internal/clients/etherscan/api.go | 6 ++++++ internal/clients/etherscan/pubkey_finder.go | 2 ++ internal/clients/etherscan/receiver.go | 2 ++ 5 files changed, 12 insertions(+), 21 deletions(-) diff --git a/internal/clients/blockscout/pubkey_finder.go b/internal/clients/blockscout/pubkey_finder.go index 5239a6324..347afcdca 100644 --- a/internal/clients/blockscout/pubkey_finder.go +++ b/internal/clients/blockscout/pubkey_finder.go @@ -56,6 +56,7 @@ func (c APIClient) PublicKeyFromAddress(ctx context.Context, protocol, network s tx.GasPrice(), tx.Gas(), tx.Value()) + if err != nil { return nil, errors.WithMessage(err, "could not get public key from raw hash") } @@ -74,5 +75,6 @@ func getFromResultHash(address string, txResult *txList) (common.Hash, error) { return common.HexToHash(x.Hash), nil } } + return common.Hash{}, errors.Errorf("No transactions from address found") } diff --git a/internal/clients/blockscout/types.go b/internal/clients/blockscout/types.go index 3169ad201..18f767f78 100644 --- a/internal/clients/blockscout/types.go +++ b/internal/clients/blockscout/types.go @@ -26,24 +26,3 @@ type txResult struct { Confirmations string Value string } - -type getTxInfo struct { - Status string - Message string - Result txInfo -} - -type txInfo struct { - BlockHash string - BlockNumber string - Confirmations string - From string - GasPrice string - GasUsed string - Hash string - Input string - Success bool - TimeStamp string - To string - Value string -} diff --git a/internal/clients/etherscan/api.go b/internal/clients/etherscan/api.go index b09e56844..ba80306e0 100644 --- a/internal/clients/etherscan/api.go +++ b/internal/clients/etherscan/api.go @@ -61,6 +61,7 @@ func (c APIClient) getTransactionByHash(network string, hash common.Hash) (*type if !ok { return nil, errors.Errorf("network not supported") } + txListResponse, err := resty.R(). SetQueryParams(map[string]string{ "module": "proxy", @@ -71,6 +72,7 @@ func (c APIClient) getTransactionByHash(network string, hash common.Hash) (*type if err != nil { return nil, errors.WithStack(err) } + res := &jsonrpcMessage{} if err := json.Unmarshal(txListResponse.Body(), res); err != nil { return nil, errors.WithStack(err) @@ -79,6 +81,7 @@ func (c APIClient) getTransactionByHash(network string, hash common.Hash) (*type if res.Error != nil { return nil, errors.Errorf(res.Error.Message) } + if res.Result == nil { return nil, errors.New("not found") } @@ -88,6 +91,7 @@ func (c APIClient) getTransactionByHash(network string, hash common.Hash) (*type if err := json.Unmarshal(res.Result, &ts); err != nil { return nil, errors.WithStack(err) } + return ts, nil } @@ -97,6 +101,7 @@ func (c APIClient) getTransactionsByAddress(network string, address []byte) (*tx if !ok { return nil, errors.Errorf("network not supported") } + txListResponse, err := resty.R(). SetQueryParams(map[string]string{ "module": "account", @@ -110,6 +115,7 @@ func (c APIClient) getTransactionsByAddress(network string, address []byte) (*tx if err != nil { return nil, errors.WithStack(err) } + txResult := &txList{} if err := json.Unmarshal(txListResponse.Body(), txResult); err != nil { return nil, errors.WithStack(err) diff --git a/internal/clients/etherscan/pubkey_finder.go b/internal/clients/etherscan/pubkey_finder.go index 703630b8e..19b979eda 100644 --- a/internal/clients/etherscan/pubkey_finder.go +++ b/internal/clients/etherscan/pubkey_finder.go @@ -48,6 +48,7 @@ func (c APIClient) PublicKeyFromAddress(ctx context.Context, protocol, network s } v, r, s := tx.RawSignatureValues() + keyBytes, err := ethereum.GetPublicKeyFromTransaction( r, s, v, tx.To().Bytes(), @@ -74,5 +75,6 @@ func getFromResultHash(address string, txResult *txList) (common.Hash, error) { return common.HexToHash(x.Hash), nil } } + return common.Hash{}, errors.Errorf("No transactions from address found") } diff --git a/internal/clients/etherscan/receiver.go b/internal/clients/etherscan/receiver.go index 21dfc9234..fb9512573 100644 --- a/internal/clients/etherscan/receiver.go +++ b/internal/clients/etherscan/receiver.go @@ -28,6 +28,7 @@ func (c APIClient) Receive(ctx context.Context, network string, address []byte) if !c.isNetworkSupported(network) { return nil, errors.Errorf("network not supported") } + txResult, err := c.getTransactionsByAddress(network, address) if err != nil { return nil, errors.WithStack(err) @@ -61,5 +62,6 @@ func (c APIClient) Receive(ctx context.Context, network string, address []byte) Hash: []byte(x.Hash), }) } + return res, nil } From 24077d69389c5bab621e1b06282bc9421a6b1cfb Mon Sep 17 00:00:00 2001 From: Rob De Feo Date: Sun, 16 Feb 2020 16:39:32 +0000 Subject: [PATCH 7/7] feat: blockscout public key finder noauth settings --- .../internal/settings/blockscout_pkf.go | 56 ++++++++ .../internal/settings/blockscout_pkf_test.go | 122 ++++++++++++++++++ .../internal/settings/blockscout_receiver.go | 9 +- .../settings/blockscout_receiver_test.go | 52 +------- .../internal/settings/etherscan_pkf_test.go | 3 +- cmd/mailchain/internal/settings/pkf.go | 1 + cmd/mailchain/internal/settings/root.go | 2 +- cmd/mailchain/internal/settings/root_test.go | 1 - ...ment-defaults.comment-defaults.golden.yaml | 6 +- ...lude-defaults.include-defaults.golden.yaml | 6 +- 10 files changed, 192 insertions(+), 66 deletions(-) create mode 100644 cmd/mailchain/internal/settings/blockscout_pkf.go create mode 100644 cmd/mailchain/internal/settings/blockscout_pkf_test.go diff --git a/cmd/mailchain/internal/settings/blockscout_pkf.go b/cmd/mailchain/internal/settings/blockscout_pkf.go new file mode 100644 index 000000000..04ba2599c --- /dev/null +++ b/cmd/mailchain/internal/settings/blockscout_pkf.go @@ -0,0 +1,56 @@ +package settings //nolint: dupl + +import ( + "github.com/mailchain/mailchain/cmd/internal/settings/output" + "github.com/mailchain/mailchain/cmd/internal/settings/values" + "github.com/mailchain/mailchain/cmd/mailchain/internal/settings/defaults" + "github.com/mailchain/mailchain/internal/clients/blockscout" + "github.com/mailchain/mailchain/internal/mailbox" + "github.com/mailchain/mailchain/internal/protocols/ethereum" +) + +// BlockscoutPublicKeyFinder configuration settings. +type BlockscoutPublicKeyFinder struct { + EnabledProtocolNetworks values.StringSlice + APIKey values.String + kind string +} + +func blockscoutPublicKeyFinderNoAuth(s values.Store) *BlockscoutPublicKeyFinder { + kind := defaults.ClientBlockscoutNoAuth + + return &BlockscoutPublicKeyFinder{ + kind: kind, + EnabledProtocolNetworks: values.NewDefaultStringSlice( + []string{ethereum.Mainnet}, + s, + "public-key-finders."+kind+".enabled-networks", + ), + APIKey: values.NewDefaultString("", s, "public-key-finders."+kind+".api-key"), + } +} + +// Supports a map of what protocol and network combinations are supported. +func (r BlockscoutPublicKeyFinder) Supports() map[string]bool { + m := map[string]bool{} + for _, np := range r.EnabledProtocolNetworks.Get() { + m[np] = true + } + + return m +} + +// Produce `mailbox.PubKeyFinder` based on configuration settings. +func (r BlockscoutPublicKeyFinder) Produce() (mailbox.PubKeyFinder, error) { + return blockscout.NewAPIClient() +} + +// Output configuration as an `output.Element` for use in exporting configuration. +func (r BlockscoutPublicKeyFinder) Output() output.Element { + return output.Element{ + FullName: "public-key-finders." + r.kind, + Attributes: []output.Attribute{ + r.EnabledProtocolNetworks.Attribute(), + }, + } +} diff --git a/cmd/mailchain/internal/settings/blockscout_pkf_test.go b/cmd/mailchain/internal/settings/blockscout_pkf_test.go new file mode 100644 index 000000000..bc20f3397 --- /dev/null +++ b/cmd/mailchain/internal/settings/blockscout_pkf_test.go @@ -0,0 +1,122 @@ +package settings + +import ( + "testing" + + "github.com/golang/mock/gomock" + "github.com/mailchain/mailchain/cmd/internal/settings/values" + "github.com/mailchain/mailchain/cmd/internal/settings/values/valuestest" + "github.com/mailchain/mailchain/internal/clients/blockscout" + "github.com/mailchain/mailchain/internal/mailbox" + "github.com/stretchr/testify/assert" +) + +func Test_blockscoutPublicKeyFinderNoAuth(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + type args struct { + s values.Store + } + tests := []struct { + name string + args args + wantEnabledProtocolNetworks []string + }{ + { + "success", + args{ + func() values.Store { + m := valuestest.NewMockStore(mockCtrl) + m.EXPECT().IsSet("public-key-finders.blockscout-no-auth.enabled-networks").Return(false) + return m + }(), + }, + []string{"ethereum/goerli", "ethereum/kovan", "ethereum/mainnet", "ethereum/rinkeby", "ethereum/ropsten"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := blockscoutPublicKeyFinderNoAuth(tt.args.s) + assert.Equal(t, tt.wantEnabledProtocolNetworks, got.EnabledProtocolNetworks.Get()) + }) + } +} + +func TestBlockscoutPublicKeyFinder_Supports(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + type fields struct { + EnabledProtocolNetworks values.StringSlice + } + tests := []struct { + name string + fields fields + want map[string]bool + }{ + { + "success", + fields{ + func() values.StringSlice { + m := valuestest.NewMockStringSlice(mockCtrl) + m.EXPECT().Get().Return([]string{"ethereum/mainnet", "ethereum/ropsten"}) + return m + }(), + }, + map[string]bool{"ethereum/mainnet": true, "ethereum/ropsten": true}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := BlockscoutPublicKeyFinder{ + EnabledProtocolNetworks: tt.fields.EnabledProtocolNetworks, + } + if got := r.Supports(); !assert.Equal(t, tt.want, got) { + t.Errorf("BlockscoutPublicKeyFinder.Supports() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestBlockscoutPublicKeyFinder_Produce(t *testing.T) { + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + type fields struct { + kind string + EnabledProtocolNetworks values.StringSlice + } + tests := []struct { + name string + fields fields + want mailbox.PubKeyFinder + wantErr bool + }{ + { + "success", + fields{ + "test", + func() values.StringSlice { + m := valuestest.NewMockStringSlice(mockCtrl) + return m + }(), + }, + &blockscout.APIClient{}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := BlockscoutPublicKeyFinder{ + kind: tt.fields.kind, + EnabledProtocolNetworks: tt.fields.EnabledProtocolNetworks, + } + got, err := r.Produce() + if (err != nil) != tt.wantErr { + t.Errorf("BlockscoutPublicKeyFinder.Produce() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !assert.IsType(t, tt.want, got) { + t.Errorf("BlockscoutPublicKeyFinder.Produce() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/cmd/mailchain/internal/settings/blockscout_receiver.go b/cmd/mailchain/internal/settings/blockscout_receiver.go index 4c61b038c..91e753f7b 100644 --- a/cmd/mailchain/internal/settings/blockscout_receiver.go +++ b/cmd/mailchain/internal/settings/blockscout_receiver.go @@ -13,14 +13,11 @@ import ( type BlockscoutReceiver struct { kind string EnabledProtocolNetworks values.StringSlice - APIKey values.String } func blockscoutReceiverNoAuth(s values.Store) *BlockscoutReceiver { - return blockscoutReceiverAny(s, defaults.ClientBlockscoutNoAuth) -} + kind := defaults.ClientBlockscoutNoAuth -func blockscoutReceiverAny(s values.Store, kind string) *BlockscoutReceiver { return &BlockscoutReceiver{ kind: kind, EnabledProtocolNetworks: values.NewDefaultStringSlice( @@ -28,7 +25,6 @@ func blockscoutReceiverAny(s values.Store, kind string) *BlockscoutReceiver { s, "receivers."+kind+".enabled-networks", ), - APIKey: values.NewDefaultString("", s, "receivers."+kind+".api-key"), } } @@ -44,7 +40,7 @@ func (r BlockscoutReceiver) Supports() map[string]bool { // Produce `mailbox.Receiver` based on configuration settings. func (r BlockscoutReceiver) Produce() (mailbox.Receiver, error) { - return blockscout.NewAPIClient(r.APIKey.Get()) + return blockscout.NewAPIClient() } // Output configuration as an `output.Element` for use in exporting configuration. @@ -53,7 +49,6 @@ func (r BlockscoutReceiver) Output() output.Element { FullName: r.kind, Attributes: []output.Attribute{ r.EnabledProtocolNetworks.Attribute(), - r.APIKey.Attribute(), }, } } diff --git a/cmd/mailchain/internal/settings/blockscout_receiver_test.go b/cmd/mailchain/internal/settings/blockscout_receiver_test.go index 72a1684e1..b64bc050f 100644 --- a/cmd/mailchain/internal/settings/blockscout_receiver_test.go +++ b/cmd/mailchain/internal/settings/blockscout_receiver_test.go @@ -1,7 +1,6 @@ package settings import ( - "reflect" "testing" "github.com/golang/mock/gomock" @@ -12,43 +11,6 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_blockscoutReceiverAny(t *testing.T) { - mockCtrl := gomock.NewController(t) - defer mockCtrl.Finish() - type args struct { - s values.Store - kind string - } - tests := []struct { - name string - args args - wantEnabledProtocolNetworks []string - wantAPIKey string - }{ - { - "success", - args{ - func() values.Store { - m := valuestest.NewMockStore(mockCtrl) - m.EXPECT().IsSet("receivers.type.enabled-networks").Return(false) - m.EXPECT().IsSet("receivers.type.api-key").Return(false) - return m - }(), - "type", - }, - []string{"ethereum/mainnet"}, - "", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := blockscoutReceiverAny(tt.args.s, tt.args.kind) - assert.Equal(t, tt.wantEnabledProtocolNetworks, got.EnabledProtocolNetworks.Get()) - assert.Equal(t, tt.wantAPIKey, got.APIKey.Get()) - }) - } -} - func Test_blockscoutReceiverNoAuth(t *testing.T) { mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() @@ -59,7 +21,6 @@ func Test_blockscoutReceiverNoAuth(t *testing.T) { name string args args wantEnabledProtocolNetworks []string - wantAPIKey string }{ { "success", @@ -67,19 +28,16 @@ func Test_blockscoutReceiverNoAuth(t *testing.T) { func() values.Store { m := valuestest.NewMockStore(mockCtrl) m.EXPECT().IsSet("receivers.blockscout-no-auth.enabled-networks").Return(false) - m.EXPECT().IsSet("receivers.blockscout-no-auth.api-key").Return(false) return m }(), }, []string{"ethereum/mainnet"}, - "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := blockscoutReceiverNoAuth(tt.args.s) assert.Equal(t, tt.wantEnabledProtocolNetworks, got.EnabledProtocolNetworks.Get()) - assert.Equal(t, tt.wantAPIKey, got.APIKey.Get()) }) } } @@ -113,9 +71,8 @@ func TestBlockscoutReceiver_Supports(t *testing.T) { t.Run(tt.name, func(t *testing.T) { r := BlockscoutReceiver{ EnabledProtocolNetworks: tt.fields.EnabledProtocolNetworks, - APIKey: tt.fields.APIKey, } - if got := r.Supports(); !reflect.DeepEqual(got, tt.want) { + if got := r.Supports(); !assert.Equal(t, tt.want, got) { t.Errorf("blockscoutReceiver.Supports() = %v, want %v", got, tt.want) } }) @@ -128,7 +85,6 @@ func TestBlockscoutReceiver_Produce(t *testing.T) { type fields struct { kind string EnabledProtocolNetworks values.StringSlice - APIKey values.String } tests := []struct { name string @@ -144,11 +100,6 @@ func TestBlockscoutReceiver_Produce(t *testing.T) { m := valuestest.NewMockStringSlice(mockCtrl) return m }(), - func() values.String { - m := valuestest.NewMockString(mockCtrl) - m.EXPECT().Get().Return("apikey") - return m - }(), }, &blockscout.APIClient{}, false, @@ -159,7 +110,6 @@ func TestBlockscoutReceiver_Produce(t *testing.T) { r := BlockscoutReceiver{ kind: tt.fields.kind, EnabledProtocolNetworks: tt.fields.EnabledProtocolNetworks, - APIKey: tt.fields.APIKey, } got, err := r.Produce() if (err != nil) != tt.wantErr { diff --git a/cmd/mailchain/internal/settings/etherscan_pkf_test.go b/cmd/mailchain/internal/settings/etherscan_pkf_test.go index 45a69d452..42764c935 100644 --- a/cmd/mailchain/internal/settings/etherscan_pkf_test.go +++ b/cmd/mailchain/internal/settings/etherscan_pkf_test.go @@ -1,7 +1,6 @@ package settings import ( - "reflect" "testing" "github.com/golang/mock/gomock" @@ -148,7 +147,7 @@ func TestEtherscanPublicKeyFinder_Supports(t *testing.T) { EnabledProtocolNetworks: tt.fields.EnabledProtocolNetworks, APIKey: tt.fields.APIKey, } - if got := r.Supports(); !reflect.DeepEqual(got, tt.want) { + if got := r.Supports(); !assert.Equal(t, tt.want, got) { t.Errorf("EtherscanPublicKeyFinder.Supports() = %v, want %v", got, tt.want) } }) diff --git a/cmd/mailchain/internal/settings/pkf.go b/cmd/mailchain/internal/settings/pkf.go index 99a5c6be8..0687a4757 100644 --- a/cmd/mailchain/internal/settings/pkf.go +++ b/cmd/mailchain/internal/settings/pkf.go @@ -13,6 +13,7 @@ func publicKeyFinders(s values.Store) *PublicKeyFinders { clients: map[string]PublicKeyFinderClient{ defaults.ClientEtherscanNoAuth: etherscanPublicKeyFinderNoAuth(s), defaults.ClientEtherscan: etherscanPublicKeyFinder(s), + defaults.ClientBlockscoutNoAuth: blockscoutPublicKeyFinderNoAuth(s), defaults.SubstratePublicKeyFinder: substratePublicKeyFinder(s), }, } diff --git a/cmd/mailchain/internal/settings/root.go b/cmd/mailchain/internal/settings/root.go index a87669675..e019a199c 100644 --- a/cmd/mailchain/internal/settings/root.go +++ b/cmd/mailchain/internal/settings/root.go @@ -28,7 +28,7 @@ func FromStore(s values.Store) *Root { &defaults.NetworkDefaults{ NameServiceAddress: defaults.NameServiceAddressKind, NameServiceDomainName: defaults.NameServiceDomainNameKind, - PublicKeyFinder: defaults.ClientEtherscanNoAuth, + PublicKeyFinder: defaults.ClientBlockscoutNoAuth, Receiver: defaults.ClientBlockscoutNoAuth, Sender: defaults.EthereumRelay, Disabled: false, diff --git a/cmd/mailchain/internal/settings/root_test.go b/cmd/mailchain/internal/settings/root_test.go index 715f529ea..7e002ebbf 100644 --- a/cmd/mailchain/internal/settings/root_test.go +++ b/cmd/mailchain/internal/settings/root_test.go @@ -50,7 +50,6 @@ func TestFromStore(t *testing.T) { } func TestRoot_ToYaml(t *testing.T) { - mockCtrl := gomock.NewController(t) defer mockCtrl.Finish() type args struct { diff --git a/cmd/mailchain/internal/settings/testdata/TestRoot_ToYaml/comment-defaults.comment-defaults.golden.yaml b/cmd/mailchain/internal/settings/testdata/TestRoot_ToYaml/comment-defaults.comment-defaults.golden.yaml index 56e8504bc..9e72b6bae 100644 --- a/cmd/mailchain/internal/settings/testdata/TestRoot_ToYaml/comment-defaults.comment-defaults.golden.yaml +++ b/cmd/mailchain/internal/settings/testdata/TestRoot_ToYaml/comment-defaults.comment-defaults.golden.yaml @@ -20,7 +20,7 @@ # disabled: false # nameservice-address: "mailchain" # nameservice-domain-name: "mailchain" -# public-key-finder: "etherscan-no-auth" +# public-key-finder: "blockscout-no-auth" # receiver: "blockscout-no-auth" # sender: "ethereum-relay" # rinkeby: @@ -66,6 +66,9 @@ # - "ethereum/rinkeby" # - "ethereum/ropsten" # public-key-finders: +# blockscout-no-auth: +# enabled-networks: +# - "mainnet" # etherscan: # api-key: "" # enabled-networks: @@ -89,7 +92,6 @@ # - "substrate/edgeware-berlin" # receivers: # blockscout-no-auth: -# api-key: "" # enabled-networks: # - "ethereum/mainnet" # etherscan: diff --git a/cmd/mailchain/internal/settings/testdata/TestRoot_ToYaml/include-defaults.include-defaults.golden.yaml b/cmd/mailchain/internal/settings/testdata/TestRoot_ToYaml/include-defaults.include-defaults.golden.yaml index e8a036f0f..628f44d64 100644 --- a/cmd/mailchain/internal/settings/testdata/TestRoot_ToYaml/include-defaults.include-defaults.golden.yaml +++ b/cmd/mailchain/internal/settings/testdata/TestRoot_ToYaml/include-defaults.include-defaults.golden.yaml @@ -20,7 +20,7 @@ protocols: disabled: false nameservice-address: "mailchain" nameservice-domain-name: "mailchain" - public-key-finder: "etherscan-no-auth" + public-key-finder: "blockscout-no-auth" receiver: "blockscout-no-auth" sender: "ethereum-relay" rinkeby: @@ -66,6 +66,9 @@ nameservice-domain-name: - "ethereum/rinkeby" - "ethereum/ropsten" public-key-finders: + blockscout-no-auth: + enabled-networks: + - "mainnet" etherscan: api-key: "" enabled-networks: @@ -89,7 +92,6 @@ public-key-finders: - "substrate/edgeware-berlin" receivers: blockscout-no-auth: - api-key: "" enabled-networks: - "ethereum/mainnet" etherscan: