linting and remove unused code

This commit is contained in:
Andrea Maria Piana 2021-07-12 13:18:46 +02:00
parent bdf3775b0b
commit a4701f41ee
29 changed files with 11 additions and 2973 deletions

View File

@ -269,7 +269,7 @@ func (s *ManagerTestSuite) TestSetChatAccount() {
address := crypto.PubkeyToAddress(privKey.PublicKey)
s.accManager.SetChatAccount(privKey)
s.Require().NoError(s.accManager.SetChatAccount(privKey))
selectedChatAccount, err := s.accManager.SelectedChatAccount()
s.Require().NoError(err)
s.Require().NotNil(selectedChatAccount)

View File

@ -33,10 +33,6 @@ import (
"github.com/status-im/status-go/transactions"
)
const (
contractQueryTimeout = 1000 * time.Millisecond
)
var (
// ErrWhisperClearIdentitiesFailure clearing whisper identities has failed.
ErrWhisperClearIdentitiesFailure = errors.New("failed to clear whisper identities")
@ -278,7 +274,9 @@ func (b *GethStatusBackend) startNodeWithKey(acc multiaccounts.Account, password
if err != nil {
return err
}
b.accountManager.SetChatAccount(chatKey)
if err := b.accountManager.SetChatAccount(chatKey); err != nil {
return err
}
_, err = b.accountManager.SelectedChatAccount()
if err != nil {
return err

View File

@ -3,7 +3,6 @@ package main
import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
stdlog "log"
@ -25,6 +24,7 @@ import (
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/multiaccounts/accounts"
//"github.com/status-im/status-go/appdatabase"
//gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
"github.com/status-im/status-go/eth-node/crypto"
@ -54,8 +54,6 @@ var (
ipcEnabled = flag.Bool("ipc", false, "Enable IPC RPC endpoint")
ipcFile = flag.String("ipcfile", "", "Set IPC file path")
seedPhrase = flag.String("seed-phrase", "", "Seed phrase")
pprofEnabled = flag.Bool("pprof", false, "Enable runtime profiling via pprof")
pprofPort = flag.Int("pprof-port", 52525, "Port for runtime profiling via pprof")
version = flag.Bool("version", false, "Print version and dump configuration")
nAddedContacts = flag.Int("added-contacts", 100, "Number of added contacts to create")
nContacts = flag.Int("contacts", 100, "Number of contacts to create")
@ -64,7 +62,6 @@ var (
nOneToOneChats = flag.Int("one-to-one-chats", 5, "Number of one to one chats")
dataDir = flag.String("dir", getDefaultDataDir(), "Directory used by node to store data")
register = flag.Bool("register", false, "Register and make the node discoverable by other nodes")
networkID = flag.Int(
"network-id",
params.RopstenNetworkID,
@ -74,8 +71,6 @@ var (
),
)
listenAddr = flag.String("addr", "", "address to bind listener to")
syncAndExit = flag.Int("sync-and-exit", -1, "Timeout in minutes for blockchain sync and exit, zero means no timeout unless sync is finished")
)
// All general log messages in this package should be routed through this logger.
@ -253,12 +248,6 @@ func main() {
}
}
url := "enode://30211cbd81c25f07b03a0196d56e6ce4604bb13db773ff1c0ea2253547fafd6c06eae6ad3533e2ba39d59564cfbdbb5e2ce7c137a5ebb85e99dcfc7a75f99f55@23.236.58.92:443"
fmt.Println("UPDATING")
wakuext.UpdateMailservers([]string{url})
time.Sleep(10 * time.Second)
fmt.Println("UPDATED")
}
func getDefaultDataDir() string {
@ -279,12 +268,6 @@ func setupLogging(config *params.NodeConfig) {
}
}
var (
errStatusServiceRequiresIPC = errors.New("to enable the StatusService on IPC, -ipc flag must be set")
errStatusServiceRequiresHTTP = errors.New("to enable the StatusService on HTTP, -http flag must be set")
errStatusServiceInvalidFlag = errors.New("-status flag valid values are: ipc, http")
)
// printVersion prints verbose output about version and config.
func printVersion(config *params.NodeConfig) {
fmt.Println(strings.Title(config.Name))
@ -429,7 +412,9 @@ func defaultNodeConfig(installationID string) (*params.NodeConfig, error) {
func ImportAccount(seedPhrase string, backend *api.GethStatusBackend) error {
backend.UpdateRootDataDir("./tmp")
manager := backend.AccountManager()
manager.InitKeystore("./tmp")
if err := manager.InitKeystore("./tmp"); err != nil {
return err
}
err := backend.OpenAccounts()
if err != nil {
logger.Error("failed open accounts", err)
@ -438,19 +423,16 @@ func ImportAccount(seedPhrase string, backend *api.GethStatusBackend) error {
generator := manager.AccountsGenerator()
generatedAccountInfo, err := generator.ImportMnemonic(seedPhrase, "")
if err != nil {
logger.Error("import mnemonic", err)
return err
}
derivedAddresses, err := generator.DeriveAddresses(generatedAccountInfo.ID, paths)
if err != nil {
logger.Error("deriver addressess", err)
return err
}
_, err = generator.StoreDerivedAccounts(generatedAccountInfo.ID, "", paths)
if err != nil {
logger.Error("store addressess", err)
return err
}
@ -459,13 +441,11 @@ func ImportAccount(seedPhrase string, backend *api.GethStatusBackend) error {
}
settings, err := defaultSettings(generatedAccountInfo, derivedAddresses, &seedPhrase)
if err != nil {
logger.Error("default settings", err)
return err
}
nodeConfig, err := defaultNodeConfig(settings.InstallationID)
if err != nil {
logger.Error("node config", err)
return err
}
@ -529,14 +509,14 @@ func buildMessage(chat *protocol.Chat, count int) *common.Message {
message.MessageType = protobuf.MessageType_PRIVATE_GROUP
}
message.PrepareContent("")
_ = message.PrepareContent("")
return message
}
func randomString(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = letterRunes[rand.Intn(len(letterRunes))]
b[i] = letterRunes[rand.Intn(len(letterRunes))] // nolint: gosec
}
return string(b)
}

View File

@ -1,14 +0,0 @@
package main
import (
"context"
"time"
)
func createContextFromTimeout(timeout int) (context.Context, context.CancelFunc) {
if timeout == 0 {
return context.WithCancel(context.Background())
}
return context.WithTimeout(context.Background(), time.Duration(timeout)*time.Minute)
}

View File

@ -1,14 +0,0 @@
package main
import (
"context"
"time"
)
func createContextFromTimeout(timeout int) (context.Context, context.CancelFunc) {
if timeout == 0 {
return context.WithCancel(context.Background())
}
return context.WithTimeout(context.Background(), time.Duration(timeout)*time.Minute)
}

View File

@ -9,7 +9,6 @@ import (
"path/filepath"
"reflect"
"sync"
"time"
ma "github.com/multiformats/go-multiaddr"
"github.com/syndtr/goleveldb/leveldb"
@ -35,7 +34,6 @@ import (
"github.com/status-im/status-go/services/browsers"
localnotifications "github.com/status-im/status-go/services/local-notifications"
"github.com/status-im/status-go/services/mailservers"
"github.com/status-im/status-go/services/nodebridge"
"github.com/status-im/status-go/services/peer"
"github.com/status-im/status-go/services/permissions"
"github.com/status-im/status-go/services/personal"
@ -50,9 +48,6 @@ import (
"github.com/status-im/status-go/wakuv2"
)
// tickerResolution is the delta to check blockchain sync progress.
const tickerResolution = time.Second
// errors
var (
ErrNodeRunning = errors.New("node is already running")
@ -96,7 +91,6 @@ type StatusNode struct {
rpcStatsSrvc *rpcstats.Service
accountsSrvc *accountssvc.Service
browsersSrvc *browsers.Service
nodeBridgeSrvc *nodebridge.NodeService
permissionsSrvc *permissions.Service
mailserversSrvc *mailservers.Service
appMetricsSrvc *appmetricsservice.Service
@ -403,7 +397,6 @@ func (n *StatusNode) stop() error {
n.rpcStatsSrvc = nil
n.accountsSrvc = nil
n.browsersSrvc = nil
n.nodeBridgeSrvc = nil
n.permissionsSrvc = nil
n.mailserversSrvc = nil
n.appMetricsSrvc = nil

View File

@ -1,7 +1,6 @@
package node
import (
"encoding/json"
"errors"
"fmt"
"os"
@ -10,7 +9,6 @@ import (
"github.com/syndtr/goleveldb/leveldb"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
@ -20,7 +18,6 @@ import (
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/static"
)
// Errors related to node and services creation.
@ -69,7 +66,7 @@ func MakeNode(config *params.NodeConfig, accs *accounts.Manager, db *leveldb.DB)
// newGethNodeConfig returns default stack configuration for mobile client node
func newGethNodeConfig(config *params.NodeConfig) (*node.Config, error) {
// NOTE: I haven't changed anything related to this paramters, but
// NOTE: I haven't changed anything related to this parameters, but
// it seems they were previously ignored if set to 0, but now they seem
// to be used, so they need to be set to something
maxPeers := 100
@ -133,46 +130,6 @@ func newGethNodeConfig(config *params.NodeConfig) (*node.Config, error) {
return nc, nil
}
// calculateGenesis retrieves genesis value for given network
func calculateGenesis(networkID uint64) (*core.Genesis, error) {
var genesis *core.Genesis
switch networkID {
case params.MainNetworkID:
genesis = core.DefaultGenesisBlock()
case params.RopstenNetworkID:
genesis = core.DefaultRopstenGenesisBlock()
case params.RinkebyNetworkID:
genesis = core.DefaultRinkebyGenesisBlock()
case params.GoerliNetworkID:
genesis = core.DefaultGoerliGenesisBlock()
case params.StatusChainNetworkID:
var err error
if genesis, err = defaultStatusChainGenesisBlock(); err != nil {
return nil, err
}
default:
return nil, nil
}
return genesis, nil
}
// defaultStatusChainGenesisBlock returns the StatusChain network genesis block.
func defaultStatusChainGenesisBlock() (*core.Genesis, error) {
genesisJSON, err := static.ConfigStatusChainGenesisJsonBytes()
if err != nil {
return nil, fmt.Errorf("status-chain-genesis.json could not be loaded: %s", err)
}
var genesis *core.Genesis
err = json.Unmarshal(genesisJSON, &genesis)
if err != nil {
return nil, fmt.Errorf("cannot unmarshal status-chain-genesis.json: %s", err)
}
return genesis, nil
}
// parseNodes creates list of enode.Node out of enode strings.
func parseNodes(enodes []string) []*enode.Node {
var nodes []*enode.Node

View File

@ -27,5 +27,4 @@ func addSuitableCallbacks(receiver reflect.Value, namespace string, methods map[
name := formatName(method.Name)
methods[namespace+"_"+name] = true
}
return
}

View File

@ -27,7 +27,6 @@ import (
"github.com/status-im/status-go/services/ext"
localnotifications "github.com/status-im/status-go/services/local-notifications"
"github.com/status-im/status-go/services/mailservers"
"github.com/status-im/status-go/services/nodebridge"
"github.com/status-im/status-go/services/peer"
"github.com/status-im/status-go/services/permissions"
"github.com/status-im/status-go/services/personal"
@ -142,13 +141,6 @@ func (b *StatusNode) nodeBridge() types.Node {
return gethbridge.NewNodeBridge(b.gethNode, b.wakuSrvc, b.wakuV2Srvc)
}
func (b *StatusNode) nodeBridgeService() *nodebridge.NodeService {
if b.nodeBridgeSrvc == nil {
b.nodeBridgeSrvc = &nodebridge.NodeService{Node: b.nodeBridge()}
}
return b.nodeBridgeSrvc
}
func (b *StatusNode) wakuExtService(config *params.NodeConfig) (*wakuext.Service, error) {
if b.gethNode == nil {
return nil, errors.New("geth node not initialized")

View File

@ -1,45 +0,0 @@
package nodebridge
import (
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc"
"github.com/status-im/status-go/eth-node/types"
)
// Make sure that NodeService implements node.Lifecycle interface.
var _ node.Lifecycle = (*NodeService)(nil)
type NodeService struct {
Node types.Node
}
// Protocols returns a new protocols list. In this case, there are none.
func (w *NodeService) Protocols() []p2p.Protocol {
return []p2p.Protocol{}
}
// APIs returns a list of new APIs.
func (w *NodeService) APIs() []rpc.API {
return []rpc.API{
{
Namespace: "status",
Version: "1.0",
Service: w.Node,
Public: false,
},
}
}
// Start is run when a service is started.
// It does nothing in this case but is required by `node.Lifecycle` interface.
func (w *NodeService) Start() error {
return nil
}
// Stop is run when a service is stopped.
// It does nothing in this case but is required by `node.Lifecycle` interface.
func (w *NodeService) Stop() error {
return nil
}

View File

@ -1,45 +0,0 @@
package nodebridge
import (
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc"
"github.com/status-im/status-go/eth-node/types"
)
// Make sure that WakuService implements node.Lifecycle interface.
var _ node.Lifecycle = (*WakuService)(nil)
type WakuService struct {
Waku types.Waku
}
// Protocols returns a new protocols list. In this case, there are none.
func (w *WakuService) Protocols() []p2p.Protocol {
return []p2p.Protocol{}
}
// APIs returns a list of new APIs.
func (w *WakuService) APIs() []rpc.API {
return []rpc.API{
{
Namespace: "status",
Version: "1.0",
Service: w.Waku,
Public: false,
},
}
}
// Start is run when a service is started.
// It does nothing in this case but is required by `node.Lifecycle` interface.
func (w *WakuService) Start() error {
return nil
}
// Stop is run when a service is stopped.
// It does nothing in this case but is required by `node.Service` interface.
func (w *WakuService) Stop() error {
return nil
}

View File

@ -8,21 +8,6 @@ import (
"github.com/stretchr/testify/require"
)
var (
fromWallet = `
{
"name": "Cow",
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
}
`
toWallet = `
{
"name": "Bob",
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
}
`
)
func TestChainIDValidation(t *testing.T) {
chain := big.NewInt(10)
type testCase struct {

View File

@ -1,81 +0,0 @@
e2e
===
This package contains all e2e tests divided into subpackages which represents (or should represent) business domains like transactions, chat etc.
These tests are run against public testnets: Ropsten and Rinkeby.
e2e package contains a few utilities which are described in a [godoc](https://godoc.org/github.com/status-im/status-go/t/e2e).
### Flags
#### 1. `-network`
The `-network` flag is used to provide either a network id or network name which specifies the ethereum network to use
for running all test. It by default uses the `StatusChain` network.
#### Usage
First of all you need to export an ACCOUNT_PASSWORD environment variable. It should be a passphrase
that was used to generate accounts used in tests. If you don't know this variable for default accounts
you will have to create your own accounts and request some funds from rinkeby or ropsten faucet.
Please see Preparation section for details.
To use the `ropsten` network for testing using network name:
```bash
ACCOUNT_PASSWORD=test go test -v ./t/e2e/... -p=1 -network=ropsten
```
To use the `rinkeby` network with chain id `4` for testing:
```bash
ACCOUNT_PASSWORD=test go test -v ./t/e2e/... -p=1 -network=4
```
#### Preparation
You will need `geth` in your PATH. Please visit: https://www.ethereum.org/cli.
Once installed - generate 2 accounts and remember the passphrase for them, so run this command twice:
```bash
geth account new --keystore=static/keys/
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase:
Repeat passphrase:
Address: {b6120ddd881593537c2bd4280bae509ec94b1a6b}
```
We expect that accounts will be named in a certain way:
```bash
pushd static/keys/
mv UTC--2018-01-26T13-46-53.657752811Z--b6120ddd881593537c2bd4280bae509ec94b1a6b test-account1.pk
mv UTC--2018-01-26T13-47-49.289567120Z--9f04dc05c4c3ec3b8b1f36f7d7d153f3934b1f07 test-account2.pk
popd
```
Update config for tests with new accounts `t/config/public-chain-accounts.json`:
```json
{
"Account1": {
"Address": "0xb6120ddd881593537c2bd4280bae509ec94b1a6b"
},
"Account2": {
"Address": "0x9f04dc05c4c3ec3b8b1f36f7d7d153f3934b1f07"
}
}
```
Embed keys as a binary data, you will need to install `npm` tool and web3.js lib:
```bash
make generate
```
As a final step request funds from faucet for a chosen network:
- [Rinkeby](https://faucet.rinkeby.io/)
- [Ropsten](http://faucet.ropsten.be:3001/)
Finally, you are ready to run tests!

View File

@ -1,75 +0,0 @@
package accounts
import (
"strings"
"testing"
"github.com/stretchr/testify/suite"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/t/e2e"
. "github.com/status-im/status-go/t/utils"
)
func TestAccountsRPCTestSuite(t *testing.T) {
suite.Run(t, new(AccountsRPCTestSuite))
}
type AccountsRPCTestSuite struct {
e2e.BackendTestSuite
}
func (s *AccountsTestSuite) TestRPCEthAccounts() {
s.StartTestBackend()
defer s.StopTestBackend()
// log into test account
loginParams, err := buildLoginParams(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password, nil)
s.Require().NoError(err)
err = s.Backend.SelectAccount(loginParams)
s.NoError(err)
rpcClient := s.Backend.StatusNode().RPCClient()
s.NotNil(rpcClient)
expectedResponse := `{"jsonrpc":"2.0","id":1,"result":["` + strings.ToLower(TestConfig.Account1.WalletAddress) + `"]}`
resp := rpcClient.CallRaw(`{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_accounts",
"params": []
}`)
s.Equal(expectedResponse, resp)
}
func (s *AccountsTestSuite) TestRPCEthAccountsWithUpstream() {
if GetNetworkID() == params.StatusChainNetworkID {
s.T().Skip()
}
addr, err := GetRemoteURL()
s.NoError(err)
s.StartTestBackend(e2e.WithUpstream(addr))
defer s.StopTestBackend()
// log into test account
loginParams, err := buildLoginParams(TestConfig.Account1.WalletAddress, TestConfig.Account1.ChatAddress, TestConfig.Account1.Password, nil)
s.Require().NoError(err)
err = s.Backend.SelectAccount(loginParams)
s.NoError(err)
rpcClient := s.Backend.StatusNode().RPCClient()
s.NotNil(rpcClient)
expectedResponse := `{"jsonrpc":"2.0","id":1,"result":["` + strings.ToLower(TestConfig.Account1.WalletAddress) + `"]}`
resp := rpcClient.CallRaw(`{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_accounts",
"params": []
}`)
s.Equal(expectedResponse, resp)
}

View File

@ -1,297 +0,0 @@
package accounts
import (
"errors"
"fmt"
"testing"
"github.com/stretchr/testify/suite"
"github.com/ethereum/go-ethereum/crypto"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/account/generator"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/extkeys"
"github.com/status-im/status-go/t/e2e"
"github.com/status-im/status-go/t/utils"
)
func buildLoginParams(mainAccountAddress, chatAddress, password string, watchAddresses []types.Address) (account.LoginParams, error) {
privateKey, err := crypto.GenerateKey()
if err != nil {
return account.LoginParams{}, err
}
acc := generator.NewAccount(privateKey, nil)
iai := acc.ToIdentifiedAccountInfo("")
return account.LoginParams{
ChatAddress: types.HexToAddress(chatAddress),
Password: password,
MainAccount: types.HexToAddress(mainAccountAddress),
WatchAddresses: watchAddresses,
MultiAccount: iai.ToMultiAccount(),
}, nil
}
func TestAccountsTestSuite(t *testing.T) {
utils.Init()
suite.Run(t, new(AccountsTestSuite))
}
type AccountsTestSuite struct {
e2e.BackendTestSuite
}
func (s *AccountsTestSuite) TestAccountsList() {
s.StartTestBackend()
defer s.StopTestBackend()
accounts, err := s.Backend.AccountManager().Accounts()
s.NoError(err)
// make sure that we start with empty accounts list (nobody has logged in yet)
s.Zero(len(accounts), "accounts returned, while there should be none (we haven't logged in yet)")
// create an account
_, accountInfo, _, err := s.Backend.AccountManager().CreateAccount(utils.TestConfig.Account1.Password)
s.NoError(err)
// ensure that there is still no accounts returned
accounts, err = s.Backend.AccountManager().Accounts()
s.NoError(err)
s.Zero(len(accounts), "accounts returned, while there should be none (we haven't logged in yet)")
loginParams, err := buildLoginParams(
accountInfo.WalletAddress,
accountInfo.ChatAddress,
utils.TestConfig.Account1.Password,
nil,
)
s.Require().NoError(err)
// select account (sub-accounts will be created for this key)
err = s.Backend.SelectAccount(loginParams)
s.NoError(err, "account selection failed")
// at this point main account should show up
accounts, err = s.Backend.AccountManager().Accounts()
s.NoError(err)
s.Equal(1, len(accounts), "exactly single account is expected (main account)")
s.Equal(accounts[0].Hex(), accountInfo.WalletAddress,
fmt.Sprintf("main account is not retured as the first key: got %s, expected %s", accounts[0].Hex(), "0x"+accountInfo.WalletAddress))
}
func (s *AccountsTestSuite) TestImportSingleExtendedKey() {
s.StartTestBackend()
defer s.StopTestBackend()
keyStore := s.Backend.AccountManager().GetKeystore()
s.NotNil(keyStore)
// create a master extended key
mn := extkeys.NewMnemonic()
mnemonic, err := mn.MnemonicPhrase(extkeys.EntropyStrength128, extkeys.EnglishLanguage)
s.NoError(err)
extKey, err := extkeys.NewMaster(mn.MnemonicSeed(mnemonic, ""))
s.NoError(err)
derivedExtendedKey, err := extKey.EthBIP44Child(0)
s.NoError(err)
// import single extended key
password := "test-password-1"
addr, _, err := s.Backend.AccountManager().ImportSingleExtendedKey(derivedExtendedKey, password)
s.NoError(err)
_, key, err := s.Backend.AccountManager().AddressToDecryptedAccount(addr, password)
s.NoError(err)
s.Equal(crypto.FromECDSA(key.PrivateKey), crypto.FromECDSA(key.ExtendedKey.ToECDSA()))
}
func (s *AccountsTestSuite) TestImportAccount() {
s.StartTestBackend()
defer s.StopTestBackend()
keyStore := s.Backend.AccountManager().GetKeystore()
s.NotNil(keyStore)
// create a private key
privateKey, err := crypto.GenerateKey()
s.NoError(err)
// import as normal account
password := "test-password-2"
addr, err := s.Backend.AccountManager().ImportAccount(privateKey, password)
s.Require().NoError(err)
_, key, err := s.Backend.AccountManager().AddressToDecryptedAccount(addr.String(), password)
s.NoError(err)
s.Equal(crypto.FromECDSA(privateKey), crypto.FromECDSA(key.PrivateKey))
s.True(key.ExtendedKey.IsZeroed())
}
func (s *AccountsTestSuite) TestRecoverAccount() {
s.StartTestBackend()
defer s.StopTestBackend()
keyStore := s.Backend.AccountManager().GetKeystore()
s.NotNil(keyStore)
// create an acc
_, accountInfo, mnemonic, err := s.Backend.AccountManager().CreateAccount(utils.TestConfig.Account1.Password)
s.NoError(err)
s.T().Logf("Account created: {walletAddress: %s, walletKey: %s, chatAddress: %s, chatKey: %s, mnemonic:%s}",
accountInfo.WalletAddress, accountInfo.WalletPubKey, accountInfo.ChatAddress, accountInfo.ChatPubKey, mnemonic)
// try recovering using password + mnemonic
accountInfoCheck, err := s.Backend.AccountManager().RecoverAccount(utils.TestConfig.Account1.Password, mnemonic)
s.NoError(err, "recover acc failed")
s.EqualValues(accountInfo, accountInfoCheck, "incorrect accound details recovered")
// now test recovering, but make sure that acc/key file is removed i.e. simulate recovering on a new device
acc, err := account.ParseAccountString(accountInfo.WalletAddress)
s.NoError(err, "can not get acc from address")
acc, key, err := keyStore.AccountDecryptedKey(acc, utils.TestConfig.Account1.Password)
s.NoError(err, "can not obtain decrypted acc key")
extChild2String := key.ExtendedKey.String()
s.NoError(keyStore.Delete(acc, utils.TestConfig.Account1.Password), "cannot remove acc")
accountInfoCheck, err = s.Backend.AccountManager().RecoverAccount(utils.TestConfig.Account1.Password, mnemonic)
s.NoError(err, "recover acc failed (for non-cached acc)")
s.EqualValues(accountInfo, accountInfoCheck, "incorrect acc details recovered (for non-cached acc)")
// make sure that extended key exists and is imported ok too
_, key, err = keyStore.AccountDecryptedKey(acc, utils.TestConfig.Account1.Password)
s.NoError(err)
s.Equal(extChild2String, key.ExtendedKey.String(), "CKD#2 key mismatch")
// make sure that calling import several times, just returns from cache (no error is expected)
accountInfoCheck, err = s.Backend.AccountManager().RecoverAccount(utils.TestConfig.Account1.Password, mnemonic)
s.NoError(err, "recover acc failed (for non-cached acc)")
s.EqualValues(accountInfo, accountInfoCheck, "incorrect acc details recovered (for non-cached acc)")
}
func (s *AccountsTestSuite) TestSelectAccount() {
s.StartTestBackend()
defer s.StopTestBackend()
// create an account
_, accountInfo1, _, err := s.Backend.AccountManager().CreateAccount(utils.TestConfig.Account1.Password)
s.NoError(err)
s.T().Logf("Account created: {walletAddress: %s, walletKey: %s, chatAddress: %s, chatKey: %s}",
accountInfo1.WalletAddress, accountInfo1.WalletPubKey, accountInfo1.ChatAddress, accountInfo1.ChatPubKey)
_, accountInfo2, _, err := s.Backend.AccountManager().CreateAccount(utils.TestConfig.Account1.Password)
s.NoError(err)
s.T().Logf("Account created: {walletAddress: %s, walletKey: %s, chatAddress: %s, chatKey: %s}",
accountInfo2.WalletAddress, accountInfo2.WalletPubKey, accountInfo2.ChatAddress, accountInfo2.ChatPubKey)
loginParams, err := buildLoginParams(accountInfo1.WalletAddress, accountInfo1.ChatAddress, "wrongPassword", nil)
s.Require().NoError(err)
// try selecting with wrong password
err = s.Backend.SelectAccount(loginParams)
expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given password")
s.EqualError(expectedErr, err.Error(), "select account is expected to throw error: wrong password used")
loginParams, err = buildLoginParams(accountInfo1.WalletAddress, accountInfo1.ChatAddress, utils.TestConfig.Account1.Password, nil)
s.Require().NoError(err)
err = s.Backend.SelectAccount(loginParams)
s.NoError(err)
// select another account, make sure that previous account is wiped out from Whisper cache
loginParams, err = buildLoginParams(accountInfo2.WalletAddress, accountInfo2.ChatAddress, utils.TestConfig.Account1.Password, nil)
s.Require().NoError(err)
s.NoError(s.Backend.SelectAccount(loginParams))
}
func (s *AccountsTestSuite) TestSelectedAccountOnRestart() {
s.StartTestBackend()
// create test accounts
_, accountInfo1, _, err := s.Backend.AccountManager().CreateAccount(utils.TestConfig.Account1.Password)
s.NoError(err)
_, accountInfo2, _, err := s.Backend.AccountManager().CreateAccount(utils.TestConfig.Account1.Password)
s.NoError(err)
// make sure that no account is selected by default
selectedWalletAccount, err := s.Backend.AccountManager().MainAccountAddress()
s.EqualError(account.ErrNoAccountSelected, err.Error(), "account selected, but should not be")
s.Equal(types.Address{}, selectedWalletAccount)
selectedChatAccount, err := s.Backend.AccountManager().SelectedChatAccount()
s.EqualError(account.ErrNoAccountSelected, err.Error(), "account selected, but should not be")
s.Nil(selectedChatAccount)
// select account
loginParams, err := buildLoginParams(accountInfo1.WalletAddress, accountInfo1.ChatAddress, "wrongPassword", nil)
s.Require().NoError(err)
err = s.Backend.SelectAccount(loginParams)
expectedErr := errors.New("cannot retrieve a valid key for a given account: could not decrypt key with given password")
s.EqualError(expectedErr, err.Error())
watchAddresses := []types.Address{
types.HexToAddress("0x00000000000000000000000000000000000001"),
types.HexToAddress("0x00000000000000000000000000000000000002"),
}
loginParams, err = buildLoginParams(accountInfo2.WalletAddress, accountInfo2.ChatAddress, utils.TestConfig.Account1.Password, watchAddresses)
s.Require().NoError(err)
s.NoError(s.Backend.SelectAccount(loginParams))
// stop node (and all of its sub-protocols)
nodeConfig := s.Backend.StatusNode().Config()
s.NotNil(nodeConfig)
preservedNodeConfig := *nodeConfig
s.NoError(s.Backend.StopNode())
// make sure that account is still selected
selectedWalletAccount, err = s.Backend.AccountManager().MainAccountAddress()
s.Require().NoError(err)
s.NotNil(selectedWalletAccount)
s.Equal(selectedWalletAccount.String(), accountInfo2.WalletAddress, "incorrect wallet address selected")
selectedChatAccount, err = s.Backend.AccountManager().SelectedChatAccount()
s.NoError(err)
s.NotNil(selectedChatAccount)
s.Equal(selectedChatAccount.Address.Hex(), accountInfo2.ChatAddress, "incorrect chat address selected")
s.Equal(watchAddresses, s.Backend.AccountManager().WatchAddresses())
// resume node
s.Require().NoError(s.Backend.StartNode(&preservedNodeConfig))
// re-check selected account (account2 MUST be selected)
selectedWalletAccount, err = s.Backend.AccountManager().MainAccountAddress()
s.NoError(err)
s.NotNil(selectedWalletAccount)
s.Equal(selectedWalletAccount.String(), accountInfo2.WalletAddress, "incorrect wallet address selected")
selectedChatAccount, err = s.Backend.AccountManager().SelectedChatAccount()
s.NoError(err)
s.NotNil(selectedChatAccount)
s.Equal(selectedChatAccount.Address.Hex(), accountInfo2.WalletAddress, "incorrect chat address selected")
s.Equal(watchAddresses, s.Backend.AccountManager().WatchAddresses())
// now restart node using RestartNode() method, and make sure that account is still available
s.RestartTestNode()
defer s.StopTestBackend()
// now logout, and make sure that on restart no account is selected (i.e. logout works properly)
s.NoError(s.Backend.Logout())
s.RestartTestNode()
selectedWalletAccount, err = s.Backend.AccountManager().MainAccountAddress()
s.EqualError(account.ErrNoAccountSelected, err.Error())
s.Equal(types.Address{}, selectedWalletAccount)
selectedChatAccount, err = s.Backend.AccountManager().SelectedChatAccount()
s.EqualError(account.ErrNoAccountSelected, err.Error())
s.Nil(selectedChatAccount)
s.Len(s.Backend.AccountManager().WatchAddresses(), 0)
}

View File

@ -1,204 +0,0 @@
package api_test
import (
"encoding/json"
"math/rand"
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/storage"
"github.com/status-im/status-go/api"
"github.com/status-im/status-go/node"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/signal"
"github.com/status-im/status-go/t/utils"
)
func TestAPI(t *testing.T) {
utils.Init()
suite.Run(t, new(APITestSuite))
}
type APITestSuite struct {
suite.Suite
backend *api.GethStatusBackend
}
func (s *APITestSuite) ensureNodeStopped() {
if err := s.backend.StopNode(); err != node.ErrNoRunningNode && err != nil {
s.NoError(err, "unexpected error")
}
}
func (s *APITestSuite) SetupTest() {
s.backend = api.NewGethStatusBackend()
s.NotNil(s.backend)
}
func (s *APITestSuite) TestCHTUpdate() {
// TODO(tiabc): Test that CHT is really updated.
}
func (s *APITestSuite) TestRaceConditions() {
cnt := 25
progress := make(chan struct{}, cnt)
rnd := rand.New(rand.NewSource(time.Now().UnixNano())) // nolint: gosec
nodeConfig1, err := utils.MakeTestNodeConfig(utils.GetNetworkID())
s.NoError(err)
nodeConfig2, err := utils.MakeTestNodeConfig(utils.GetNetworkID())
s.NoError(err)
nodeConfigs := []*params.NodeConfig{nodeConfig1, nodeConfig2}
var funcsToTest = []func(*params.NodeConfig){
func(config *params.NodeConfig) {
s.T().Logf("async call to StartNode() for network: %d", config.NetworkID)
api.RunAsync(func() error { return s.backend.StartNode(config) })
progress <- struct{}{}
},
func(config *params.NodeConfig) {
s.T().Logf("async call to StopNode() for network: %d", config.NetworkID)
api.RunAsync(s.backend.StopNode)
progress <- struct{}{}
},
func(config *params.NodeConfig) {
s.T().Logf("async call to RestartNode() for network: %d", config.NetworkID)
api.RunAsync(s.backend.RestartNode)
progress <- struct{}{}
},
// TODO(adam): quarantined until it uses a different datadir
// as otherwise it wipes out cached blockchain data.
// func(config *params.NodeConfig) {
// s.T().Logf("async call to ResetChainData() for network: %d", config.NetworkID)
// _, err := s.api.ResetChainData()
// progress <- struct{}{}
// },
}
// increase StartNode()/StopNode() population
for i := 0; i < 5; i++ {
funcsToTest = append(funcsToTest, funcsToTest[0], funcsToTest[1])
}
for i := 0; i < cnt; i++ {
randConfig := nodeConfigs[rnd.Intn(len(nodeConfigs))] // nolint: gosec
randFunc := funcsToTest[rnd.Intn(len(funcsToTest))] // nolint: gosec
// introduce random delays
if rnd.Intn(100) > 75 { // nolint: gosec
time.Sleep(500 * time.Millisecond)
}
s.NoError(s.backend.AccountManager().InitKeystore(randConfig.KeyStoreDir))
go randFunc(randConfig)
}
for range progress {
cnt--
if cnt <= 0 {
break
}
}
time.Sleep(2 * time.Second) // so that we see some logs
// just in case we have a node running
s.ensureNodeStopped()
}
func (s *APITestSuite) TestEventsNodeStartStop() {
envelopes := make(chan signal.Envelope, 3)
signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var envelope signal.Envelope
err := json.Unmarshal([]byte(jsonEvent), &envelope)
s.NoError(err)
// whitelist types that we are interested in
switch envelope.Type {
case signal.EventNodeStarted:
case signal.EventNodeStopped:
case signal.EventNodeReady:
default:
return
}
envelopes <- envelope
})
nodeConfig, err := utils.MakeTestNodeConfig(utils.GetNetworkID())
s.NoError(err)
s.NoError(s.backend.AccountManager().InitKeystore(nodeConfig.KeyStoreDir))
s.Require().NoError(s.backend.StartNode(nodeConfig))
s.NoError(s.backend.StopNode())
s.verifyEnvelopes(envelopes, signal.EventNodeStarted, signal.EventNodeReady, signal.EventNodeStopped)
s.Require().NoError(s.backend.StartNode(nodeConfig))
s.verifyEnvelopes(envelopes, signal.EventNodeStarted, signal.EventNodeReady)
s.Require().NoError(s.backend.RestartNode())
s.verifyEnvelopes(envelopes, signal.EventNodeStopped, signal.EventNodeStarted, signal.EventNodeReady)
s.NoError(s.backend.StopNode())
s.verifyEnvelopes(envelopes, signal.EventNodeStopped)
}
func (s *APITestSuite) verifyEnvelopes(envelopes chan signal.Envelope, envelopeTypes ...string) {
for _, envelopeType := range envelopeTypes {
select {
case env := <-envelopes:
s.Equal(envelopeType, env.Type)
case <-time.After(1 * time.Second):
s.Fail("timeout waiting for envelope")
}
}
}
func (s *APITestSuite) TestNodeStartCrash() {
// let's listen for node.crashed signal
signalReceived := make(chan struct{})
signal.SetDefaultNodeNotificationHandler(func(jsonEvent string) {
var envelope signal.Envelope
err := json.Unmarshal([]byte(jsonEvent), &envelope)
s.NoError(err)
if envelope.Type == signal.EventNodeCrashed {
close(signalReceived)
}
})
defer signal.ResetDefaultNodeNotificationHandler()
nodeConfig, err := utils.MakeTestNodeConfig(utils.GetNetworkID())
s.NoError(err)
db, err := leveldb.Open(storage.NewMemStorage(), nil)
s.NoError(err)
defer func() { s.NoError(db.Close()) }()
// start node outside the manager (on the same port), so that manager node.Start() method fails
outsideNode, err := node.MakeNode(nodeConfig, nil, db)
s.NoError(err)
err = outsideNode.Start()
s.NoError(err)
// now try starting using node manager, it should fail (error is irrelevant as it is implementation detail)
s.NoError(s.backend.AccountManager().InitKeystore(nodeConfig.KeyStoreDir))
s.Error(<-api.RunAsync(func() error { return s.backend.StartNode(nodeConfig) }))
select {
case <-time.After(500 * time.Millisecond):
s.FailNow("timed out waiting for signal")
case <-signalReceived:
}
// stop outside node, and re-try
s.NoError(outsideNode.Stop())
signalReceived = make(chan struct{})
s.NoError(<-api.RunAsync(func() error { return s.backend.StartNode(nodeConfig) }))
select {
case <-time.After(500 * time.Millisecond):
case <-signalReceived:
s.FailNow("signal should not be received")
}
// cleanup
s.NoError(s.backend.StopNode())
}

View File

@ -1,110 +0,0 @@
package api_test
import (
"io/ioutil"
"os"
"testing"
"github.com/stretchr/testify/suite"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/t/e2e"
. "github.com/status-im/status-go/t/utils"
)
func TestAPIBackendTestSuite(t *testing.T) {
Init() // from t/utils
suite.Run(t, new(APIBackendTestSuite))
}
type APIBackendTestSuite struct {
e2e.BackendTestSuite
}
// FIXME(tiabc): There's also a test with the same name in node/manager_test.go
// so this test should only check StatusBackend logic with a mocked version of the underlying StatusNode.
func (s *APIBackendTestSuite) TestNetworkSwitching() {
// Get test node configuration.
nodeConfig, err := MakeTestNodeConfig(GetNetworkID())
s.NoError(err)
s.NoError(s.Backend.AccountManager().InitKeystore(nodeConfig.KeyStoreDir))
s.False(s.Backend.IsNodeRunning())
s.Require().NoError(s.Backend.StartNode(nodeConfig))
s.True(s.Backend.IsNodeRunning())
firstHash, err := e2e.FirstBlockHash(s.Backend.StatusNode())
s.NoError(err)
s.Equal(GetHeadHash(), firstHash)
// now stop node, and make sure that a new node, on different network can be started
s.NoError(s.Backend.StopNode())
// start new node with completely different config
nodeConfig, err = MakeTestNodeConfig(GetNetworkID())
s.NoError(err)
s.False(s.Backend.IsNodeRunning())
s.Require().NoError(s.Backend.StartNode(nodeConfig))
s.True(s.Backend.IsNodeRunning())
// make sure we are on another network indeed
firstHash, err = e2e.FirstBlockHash(s.Backend.StatusNode())
s.NoError(err)
s.Equal(GetHeadHash(), firstHash)
s.NoError(s.Backend.StopNode())
}
func (s *APIBackendTestSuite) TestResetChainData() {
if GetNetworkID() != params.StatusChainNetworkID {
s.T().Skip("test must be running on status network")
}
require := s.Require()
require.NotNil(s.Backend)
path, err := ioutil.TempDir("/tmp", "status-reset-chain-test")
require.NoError(err)
defer func() { s.NoError(os.RemoveAll(path)) }()
s.StartTestBackend(e2e.WithDataDir(path))
defer s.StopTestBackend()
EnsureNodeSync(s.Backend.StatusNode().EnsureSync)
require.NoError(s.Backend.ResetChainData())
s.True(s.Backend.IsNodeRunning()) // new node, with previous config should be running
// make sure we can read the first byte, and it is valid (for Rinkeby)
firstHash, err := e2e.FirstBlockHash(s.Backend.StatusNode())
s.NoError(err)
s.Equal(GetHeadHash(), firstHash)
}
// FIXME(tiabc): There's also a test with the same name in node/manager_test.go
// so this test should only check StatusBackend logic with a mocked version of the underlying StatusNode.
func (s *APIBackendTestSuite) TestRestartNode() {
require := s.Require()
require.NotNil(s.Backend)
// get config
nodeConfig, err := MakeTestNodeConfig(GetNetworkID())
s.NoError(err)
s.NoError(s.Backend.AccountManager().InitKeystore(nodeConfig.KeyStoreDir))
s.False(s.Backend.IsNodeRunning())
require.NoError(s.Backend.StartNode(nodeConfig))
s.True(s.Backend.IsNodeRunning())
firstHash, err := e2e.FirstBlockHash(s.Backend.StatusNode())
s.NoError(err)
s.Equal(GetHeadHash(), firstHash)
s.True(s.Backend.IsNodeRunning())
require.NoError(s.Backend.RestartNode())
s.True(s.Backend.IsNodeRunning()) // new node, with previous config should be running
// make sure we can read the first byte, and it is valid (for Rinkeby)
firstHash, err = e2e.FirstBlockHash(s.Backend.StatusNode())
s.NoError(err)
s.Equal(GetHeadHash(), firstHash)
}

View File

@ -1,50 +0,0 @@
package rpc
import (
"testing"
"github.com/stretchr/testify/suite"
"github.com/status-im/status-go/node"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/t/e2e"
. "github.com/status-im/status-go/t/utils" //nolint: golint
)
type RPCClientTestSuite struct {
e2e.StatusNodeTestSuite
}
func TestRPCClientTestSuite(t *testing.T) {
e2e.Init()
suite.Run(t, new(RPCClientTestSuite))
}
func (s *RPCClientTestSuite) SetupTest() {
s.StatusNode = node.New()
s.NotNil(s.StatusNode)
}
func (s *RPCClientTestSuite) TestNewClient() {
config, err := MakeTestNodeConfig(GetNetworkID())
s.NoError(err)
// upstream disabled
s.False(config.UpstreamConfig.Enabled)
_, err = rpc.NewClient(nil, config.UpstreamConfig)
s.NoError(err)
// upstream enabled with correct URL
upstreamGood := config.UpstreamConfig
upstreamGood.Enabled = true
upstreamGood.URL = "http://example.com/rpc"
_, err = rpc.NewClient(nil, upstreamGood)
s.NoError(err)
// upstream enabled with incorrect URL
upstreamBad := config.UpstreamConfig
upstreamBad.Enabled = true
upstreamBad.URL = "///__httphh://///incorrect_urlxxx"
_, err = rpc.NewClient(nil, upstreamBad)
s.Error(err)
}

View File

@ -1,159 +0,0 @@
package rpc
import (
"context"
"fmt"
"math/big"
"sync"
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/node"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/t/e2e"
. "github.com/status-im/status-go/t/utils"
)
func TestRPCTestSuite(t *testing.T) {
e2e.Init()
suite.Run(t, new(RPCTestSuite))
}
type RPCTestSuite struct {
e2e.StatusNodeTestSuite
}
func (s *RPCTestSuite) SetupTest() {
s.StatusNode = node.New()
s.NotNil(s.StatusNode)
}
func (s *RPCTestSuite) TestCallRPC() {
if GetNetworkID() == params.StatusChainNetworkID {
s.T().Skip()
}
for _, upstreamEnabled := range []bool{false, true} {
nodeConfig, err := MakeTestNodeConfig(GetNetworkID())
s.NoError(err)
nodeConfig.IPCEnabled = false
nodeConfig.HTTPHost = "" // to make sure that no HTTP interface is started
if upstreamEnabled {
networkURL, err := GetRemoteURL()
s.NoError(err)
nodeConfig.UpstreamConfig.Enabled = true
nodeConfig.UpstreamConfig.URL = networkURL
}
s.NoError(s.StatusNode.Start(nodeConfig, nil))
rpcClient := s.StatusNode.RPCClient()
s.NotNil(rpcClient)
type rpcCall struct {
inputJSON string
validator func(resultJSON string)
}
var rpcCalls = []rpcCall{
{
`{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":64}`,
func(resultJSON string) {
expected := `{"jsonrpc":"2.0","id":64,"result":"0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"}`
s.Equal(expected, resultJSON)
},
},
{
`{"jsonrpc":"2.0","method":"net_version","params":[],"id":67}`,
func(resultJSON string) {
expected := `{"jsonrpc":"2.0","id":67,"result":"` + fmt.Sprintf("%d", GetNetworkID()) + `"}`
s.Equal(expected, resultJSON)
},
},
{
`[{"jsonrpc":"2.0","method":"net_listening","params":[],"id":67}]`,
func(resultJSON string) {
expected := `[{"jsonrpc":"2.0","id":67,"result":true}]`
s.Equal(expected, resultJSON)
},
},
{
`[{"jsonrpc":"2.0","method":"net_version","params":[],"id":67},{"jsonrpc":"2.0","method":"web3_sha3","params":["0x68656c6c6f20776f726c64"],"id":68}]`,
func(resultJSON string) {
expected := `[{"jsonrpc":"2.0","id":67,"result":"` + fmt.Sprintf("%d", GetNetworkID()) + `"},{"jsonrpc":"2.0","id":68,"result":"0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad"}]`
s.Equal(expected, resultJSON)
},
},
}
var wg sync.WaitGroup
for _, r := range rpcCalls {
wg.Add(1)
go func(r rpcCall) {
defer wg.Done()
resultJSON := rpcClient.CallRaw(r.inputJSON)
r.validator(resultJSON)
}(r)
}
done := make(chan struct{})
go func() {
wg.Wait()
close(done)
}()
select {
case <-time.After(time.Second * 30):
s.Fail("test timed out")
case <-done:
}
s.NoError(s.StatusNode.Stop())
}
}
// TestCallRawResultGetTransactionReceipt checks if returned response
// for a not yet mined transaction is "result": null.
// Issue: https://github.com/status-im/status-go/issues/547
func (s *RPCTestSuite) TestCallRawResultGetTransactionReceipt() {
nodeConfig, err := MakeTestNodeConfig(GetNetworkID())
s.NoError(err)
s.NoError(s.StatusNode.Start(nodeConfig, nil))
client := s.StatusNode.RPCClient()
s.NotNil(client)
jsonResult := client.CallRaw(`{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["0x0ca0d8f2422f62bea77e24ed17db5711a77fa72064cccbb8e53c53b699cd3b34"],"id":5}`)
s.Equal(`{"jsonrpc":"2.0","id":5,"result":null}`, jsonResult)
s.NoError(s.StatusNode.Stop())
}
// TestCallContextResult checks if result passed to CallContext
// is set accordingly to its underlying memory layout.
func (s *RPCTestSuite) TestCallContextResult() {
CheckTestSkipForNetworks(s.T(), params.MainNetworkID)
s.StartTestNode()
defer s.StopTestNode()
EnsureNodeSync(s.StatusNode.EnsureSync)
client := s.StatusNode.RPCClient()
s.NotNil(client)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()
var balance hexutil.Big
err := client.CallContext(ctx, &balance, "eth_getBalance", TestConfig.Account1.WalletAddress, "latest")
s.NoError(err)
s.True(balance.ToInt().Cmp(big.NewInt(0)) > 0, "balance should be higher than 0")
}

View File

@ -1,94 +0,0 @@
package services
import (
"encoding/json"
"fmt"
"github.com/status-im/status-go/api"
"github.com/status-im/status-go/t/e2e"
"github.com/status-im/status-go/t/utils"
)
const (
// see vendor/github.com/ethereum/go-ethereum/rpc/errors.go:L27
methodNotFoundErrorCode = -32601
)
type rpcError struct {
Code int `json:"code"`
}
type BaseJSONRPCSuite struct {
e2e.BackendTestSuite
}
func (s *BaseJSONRPCSuite) AssertAPIMethodUnexported(method string) {
exported := s.isMethodExported(method, false)
s.False(exported,
"method %s should be hidden, but it isn't",
method)
}
func (s *BaseJSONRPCSuite) AssertAPIMethodExported(method string) {
exported := s.isMethodExported(method, false)
s.True(exported,
"method %s should be exported, but it isn't",
method)
}
func (s *BaseJSONRPCSuite) AssertAPIMethodExportedPrivately(method string) {
exported := s.isMethodExported(method, true)
s.True(exported,
"method %s should be exported, but it isn't",
method)
}
func (s *BaseJSONRPCSuite) isMethodExported(method string, private bool) bool {
var (
result string
err error
)
cmd := fmt.Sprintf(`{"jsonrpc":"2.0", "method": "%s", "params": []}`, method)
if private {
result, err = s.Backend.CallPrivateRPC(cmd)
} else {
result, err = s.Backend.CallRPC(cmd)
}
s.NoError(err)
var response struct {
Error *rpcError `json:"error"`
}
s.NoError(json.Unmarshal([]byte(result), &response))
return !(response.Error != nil && response.Error.Code == methodNotFoundErrorCode)
}
func (s *BaseJSONRPCSuite) SetupTest(upstreamEnabled, statusServiceEnabled, debugAPIEnabled bool) error {
s.Backend = api.NewGethStatusBackend()
s.NotNil(s.Backend)
nodeConfig, err := utils.MakeTestNodeConfig(utils.GetNetworkID())
s.NoError(err)
s.NoError(s.Backend.AccountManager().InitKeystore(nodeConfig.KeyStoreDir))
nodeConfig.IPCEnabled = false
nodeConfig.EnableStatusService = statusServiceEnabled
if debugAPIEnabled {
nodeConfig.AddAPIModule("debug")
}
nodeConfig.HTTPHost = "" // to make sure that no HTTP interface is started
if upstreamEnabled {
networkURL, err := utils.GetRemoteURL()
s.NoError(err)
nodeConfig.UpstreamConfig.Enabled = true
nodeConfig.UpstreamConfig.URL = networkURL
}
return s.Backend.StartNode(nodeConfig)
}

View File

@ -1,123 +0,0 @@
package services
import (
"encoding/json"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/suite"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/t/utils"
)
func TestFiltersAPISuite(t *testing.T) {
utils.Init()
s := new(FiltersAPISuite)
s.upstream = false
suite.Run(t, s)
}
func TestFiltersAPISuiteUpstream(t *testing.T) {
utils.Init()
s := new(FiltersAPISuite)
s.upstream = true
if s.upstream && utils.GetNetworkID() == params.StatusChainNetworkID {
t.Skip()
return
}
suite.Run(t, s)
}
type FiltersAPISuite struct {
BaseJSONRPCSuite
upstream bool
}
func (s *FiltersAPISuite) TestFilters() {
err := s.SetupTest(s.upstream, false, false)
s.NoError(err)
defer func() {
err := s.Backend.StopNode()
s.NoError(err)
}()
basicCall := `{"jsonrpc":"2.0","method":"eth_newBlockFilter","params":[],"id":67}`
response, err := s.Backend.CallRPC(basicCall)
s.NoError(err)
filterID := s.filterIDFromRPCResponse(response)
// we don't check new blocks on private network, because no one mines them
if utils.GetNetworkID() != params.StatusChainNetworkID {
timeout := time.After(time.Minute)
newBlocksChannel := s.getFirstFilterChange(filterID)
select {
case hash := <-newBlocksChannel:
s.True(len(hash) > 0, "received hash isn't empty")
case <-timeout:
s.Fail("timeout while waiting for filter results")
}
}
basicCall = fmt.Sprintf(`{"jsonrpc":"2.0","method":"eth_uninstallFilter","params":["%s"],"id":67}`, filterID)
response, err = s.Backend.CallRPC(basicCall)
s.NoError(err)
result := s.boolFromRPCResponse(response)
s.True(result, "filter expected to be removed successfully")
}
func (s *FiltersAPISuite) getFirstFilterChange(filterID string) chan string {
result := make(chan string)
go func() {
timeout := time.Now().Add(time.Minute)
for time.Now().Before(timeout) {
basicCall := fmt.Sprintf(`{"jsonrpc":"2.0","method":"eth_getFilterChanges","params":["%s"],"id":67}`, filterID)
response, err := s.Backend.CallRPC(basicCall)
s.NoError(err)
filterChanges := s.arrayFromRPCResponse(response)
if len(filterChanges) > 0 {
result <- filterChanges[0]
return
}
time.Sleep(10 * time.Millisecond)
}
}()
return result
}
func (s *FiltersAPISuite) filterIDFromRPCResponse(response string) string {
var r struct {
Result string `json:"result"`
}
s.NoError(json.Unmarshal([]byte(response), &r))
return r.Result
}
func (s *FiltersAPISuite) arrayFromRPCResponse(response string) []string {
var r struct {
Result []string `json:"result"`
}
s.NoError(json.Unmarshal([]byte(response), &r))
return r.Result
}
func (s *FiltersAPISuite) boolFromRPCResponse(response string) bool {
var r struct {
Result bool `json:"result"`
}
s.NoError(json.Unmarshal([]byte(response), &r))
return r.Result
}

View File

@ -1,45 +0,0 @@
package services
import (
"testing"
"github.com/stretchr/testify/suite"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/t/utils"
)
func TestPeerAPISuite(t *testing.T) {
utils.Init()
s := new(PeerAPISuite)
s.upstream = false
suite.Run(t, s)
}
func TestPeerAPISuiteUpstream(t *testing.T) {
utils.Init()
s := new(PeerAPISuite)
s.upstream = true
suite.Run(t, s)
}
type PeerAPISuite struct {
BaseJSONRPCSuite
upstream bool
}
func (s *PeerAPISuite) TestAccessiblePeerAPIs() {
if s.upstream && utils.GetNetworkID() == params.StatusChainNetworkID {
s.T().Skip()
return
}
err := s.SetupTest(s.upstream, true, false)
s.NoError(err)
defer func() {
err := s.Backend.StopNode()
s.NoError(err)
}()
// These peer APIs should be available
s.AssertAPIMethodExported("peer_discover")
}

View File

@ -1,99 +0,0 @@
package services
import (
"fmt"
"testing"
"github.com/stretchr/testify/suite"
"github.com/status-im/status-go/params"
. "github.com/status-im/status-go/t/utils"
)
const (
signDataString = "0xBAADBEEF"
)
type PersonalSignSuite struct {
upstream bool
BaseJSONRPCSuite
}
func TestPersonalSignSuiteUpstream(t *testing.T) {
s := new(PersonalSignSuite)
s.upstream = true
suite.Run(t, s)
}
func (s *PersonalSignSuite) TestRestrictedPersonalAPIs() {
if s.upstream && GetNetworkID() == params.StatusChainNetworkID {
s.T().Skip()
return
}
err := s.SetupTest(true, false, false)
s.NoError(err)
defer func() {
err := s.Backend.StopNode()
s.NoError(err)
}()
// These personal APIs should be available
s.AssertAPIMethodExported("personal_sign")
s.AssertAPIMethodExported("personal_ecRecover")
// These personal APIs shouldn't be exported
s.AssertAPIMethodUnexported("personal_sendTransaction")
s.AssertAPIMethodUnexported("personal_unlockAccount")
s.AssertAPIMethodUnexported("personal_newAccount")
s.AssertAPIMethodUnexported("personal_lockAccount")
s.AssertAPIMethodUnexported("personal_listAccounts")
s.AssertAPIMethodUnexported("personal_importRawKey")
}
func (s *PersonalSignSuite) TestPersonalSignUnsupportedMethod() {
// Test upstream if that's not StatusChain
if s.upstream && GetNetworkID() == params.StatusChainNetworkID {
s.T().Skip()
}
err := s.SetupTest(true, false, false)
s.NoError(err)
defer func() {
err := s.Backend.StopNode()
s.NoError(err)
}()
basicCall := fmt.Sprintf(
`{"jsonrpc":"2.0","method":"personal_sign","params":["%s", "%s"],"id":67}`,
signDataString,
TestConfig.Account1.WalletAddress)
rawResult, err := s.Backend.CallRPC(basicCall)
s.NoError(err)
s.Contains(rawResult, `"error":{"code":-32700,"message":"method is unsupported by RPC interface"}`)
}
func (s *PersonalSignSuite) TestPersonalRecoverUnsupportedMethod() {
// Test upstream if that's not StatusChain
if s.upstream && GetNetworkID() == params.StatusChainNetworkID {
s.T().Skip()
return
}
err := s.SetupTest(true, false, false)
s.NoError(err)
defer func() {
err := s.Backend.StopNode()
s.NoError(err)
}()
// 2. Test recover
basicCall := fmt.Sprintf(
`{"jsonrpc":"2.0","method":"personal_ecRecover","params":["%s", "%s"],"id":67}`,
signDataString,
"")
rawResult, err := s.Backend.CallRPC(basicCall)
s.NoError(err)
s.Contains(rawResult, `"error":{"code":-32700,"message":"method is unsupported by RPC interface"}`)
}

View File

@ -1,193 +0,0 @@
package e2e
import (
"encoding/json"
"github.com/stretchr/testify/suite"
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/api"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/node"
"github.com/status-im/status-go/signal"
"github.com/status-im/status-go/t/utils"
"github.com/status-im/status-go/transactions"
"github.com/status-im/status-go/waku"
)
// StatusNodeTestSuite defines a test suite with StatusNode.
type StatusNodeTestSuite struct {
suite.Suite
StatusNode *node.StatusNode
}
var (
// All general log messages in this package should be routed through this logger.
logger = log.New("package", "status-go/t/e2e")
// Settings for testing
networks = json.RawMessage("{}")
settings = accounts.Settings{
Address: types.HexToAddress("0xaC540f3745Ff2964AFC1171a5A0DD726d1F6B472"),
CurrentNetwork: "mainnet_rpc",
DappsAddress: types.HexToAddress("0xa1300f99fDF7346986CbC766903245087394ecd0"),
EIP1581Address: types.HexToAddress("0xa1DDDE9235a541d1344550d969715CF43982de9f"),
InstallationID: "d3efcff6-cffa-560e-a547-21d3858cbc51",
KeyUID: "0x4e8129f3edfc004875be17bf468a784098a9f69b53c095be1f52deff286935ab",
LatestDerivedPath: 0,
Name: "Jittery Cornflowerblue Kingbird",
Networks: &networks,
PhotoPath: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==",
PreviewPrivacy: false,
PublicKey: "0x04211fe0f69772ecf7eb0b5bfc7678672508a9fb01f2d699096f0d59ef7fe1a0cb1e648a80190db1c0f5f088872444d846f2956d0bd84069f3f9f69335af852ac0",
SigningPhrase: "yurt joey vibe",
WalletRootAddress: types.HexToAddress("0xaB591fd819F86D0A6a2EF2Bcb94f77807a7De1a6")}
)
func Init() {
utils.Init()
for id := range utils.TestNetworkNames {
nodeConfig, err := utils.MakeTestNodeConfig(id)
if err != nil {
panic(err)
}
err = importTestAccounts(nodeConfig.KeyStoreDir)
if err != nil {
panic(err)
}
}
}
// StartTestNode initiazes a StatusNode instances with configuration retrieved
// from the test config.
func (s *StatusNodeTestSuite) StartTestNode(opts ...TestNodeOption) {
nodeConfig, err := utils.MakeTestNodeConfig(utils.GetNetworkID())
s.NoError(err)
// Apply any options altering node config.
for i := range opts {
opts[i](nodeConfig)
}
// import account keys
s.NoError(importTestAccounts(nodeConfig.KeyStoreDir))
s.False(s.StatusNode.IsRunning())
s.NoError(s.StatusNode.Start(nodeConfig, nil))
s.True(s.StatusNode.IsRunning())
}
// StopTestNode attempts to stop initialized StatusNode.
func (s *StatusNodeTestSuite) StopTestNode() {
s.NotNil(s.StatusNode)
s.True(s.StatusNode.IsRunning())
s.NoError(s.StatusNode.Stop())
s.False(s.StatusNode.IsRunning())
}
// BackendTestSuite is a test suite with api.GethStatusBackend initialized
// and a few utility methods to start and stop node or get various services.
type BackendTestSuite struct {
suite.Suite
Backend *api.GethStatusBackend
}
// SetupTest initializes Backend.
func (s *BackendTestSuite) SetupTest() {
s.Backend = api.NewGethStatusBackend()
s.NotNil(s.Backend)
}
// TearDownTest cleans up the packages state.
func (s *BackendTestSuite) TearDownTest() {
signal.ResetDefaultNodeNotificationHandler()
}
// StartTestBackend imports some keys and starts a node.
func (s *BackendTestSuite) StartTestBackend(opts ...TestNodeOption) {
nodeConfig, err := utils.MakeTestNodeConfig(utils.GetNetworkID())
s.Require().NoError(err)
// Apply any options altering node config.
for i := range opts {
opts[i](nodeConfig)
}
s.NoError(s.Backend.AccountManager().InitKeystore(nodeConfig.KeyStoreDir))
// import account keys
s.NoError(importTestAccounts(nodeConfig.KeyStoreDir))
// start node
s.False(s.Backend.IsNodeRunning())
s.Require().NoError(s.Backend.StartNode(nodeConfig))
s.True(s.Backend.IsNodeRunning())
}
func (s *BackendTestSuite) StartTestBackendWithAccount(account multiaccounts.Account, password string, subaccs []accounts.Account, opts ...TestNodeOption) {
nodeConfig, err := utils.MakeTestNodeConfig(utils.GetNetworkID())
s.Require().NoError(err)
// Apply any options altering node config.
for i := range opts {
opts[i](nodeConfig)
}
keystoreDir := nodeConfig.KeyStoreDir
dataDir := nodeConfig.DataDir
nodeConfig.KeyStoreDir = "keystore"
nodeConfig.DataDir = "/"
// accounts must be imported before keystore is initialized
s.NoError(importTestAccounts(keystoreDir))
s.Backend.UpdateRootDataDir(dataDir)
s.NoError(s.Backend.OpenAccounts())
s.NoError(s.Backend.AccountManager().InitKeystore(keystoreDir))
s.Require().NoError(s.Backend.StartNodeWithAccountAndConfig(account, password, settings, nodeConfig, subaccs))
}
func (s *BackendTestSuite) LogoutAndStop() {
s.NoError(s.Backend.Logout())
s.StopTestBackend()
}
// StopTestBackend stops the node.
func (s *BackendTestSuite) StopTestBackend() {
s.True(s.Backend.IsNodeRunning())
s.NoError(s.Backend.StopNode())
s.False(s.Backend.IsNodeRunning())
}
// RestartTestNode restarts a currently running node.
func (s *BackendTestSuite) RestartTestNode() {
s.True(s.Backend.IsNodeRunning())
s.Require().NoError(s.Backend.RestartNode())
s.True(s.Backend.IsNodeRunning())
}
// WakuService returns a reference to the Waku service.
func (s *BackendTestSuite) WakuService() *waku.Waku {
wakuService, err := s.Backend.StatusNode().WakuService()
s.NoError(err)
s.NotNil(wakuService)
return wakuService
}
// Transactor returns a reference to the Transactor.
func (s *BackendTestSuite) Transactor() *transactions.Transactor {
return s.Backend.Transactor()
}
func importTestAccounts(keyStoreDir string) (err error) {
logger.Debug("Import accounts to", "dir", keyStoreDir)
err = utils.ImportTestAccount(keyStoreDir, utils.GetAccount1PKFile())
if err != nil {
return
}
return utils.ImportTestAccount(keyStoreDir, utils.GetAccount2PKFile())
}

View File

@ -1,57 +0,0 @@
package e2e
import (
"context"
"path"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/node"
"github.com/status-im/status-go/params"
)
// TestNodeOption is a callback passed to StartTestNode which alters its config.
type TestNodeOption func(config *params.NodeConfig)
// WithUpstream returns TestNodeOption which enabled UpstreamConfig.
func WithUpstream(url string) TestNodeOption {
return func(config *params.NodeConfig) {
config.UpstreamConfig.Enabled = true
config.UpstreamConfig.URL = url
}
}
// WithDataDir returns TestNodeOption that allows to set another data dir.
func WithDataDir(dataDir string) TestNodeOption {
return func(config *params.NodeConfig) {
config.DataDir = dataDir
config.KeyStoreDir = path.Join(dataDir, "keystore")
config.WakuConfig.DataDir = path.Join(dataDir, "wnode")
}
}
// FirstBlockHash validates Attach operation for the StatusNode.
func FirstBlockHash(statusNode *node.StatusNode) (string, error) {
// obtain RPC client for running node
runningNode := statusNode.GethNode()
if runningNode == nil {
return "", node.ErrNoGethNode
}
rpcClient, err := runningNode.Attach()
if err != nil {
return "", err
}
// get first block
var firstBlock struct {
Hash gethcommon.Hash `json:"hash"`
}
err = rpcClient.CallContext(context.Background(), &firstBlock, "eth_getBlockByNumber", "0x0", true)
if err != nil {
return "", err
}
return firstBlock.Hash.Hex(), nil
}

View File

@ -1,283 +0,0 @@
package transactions
import (
"io/ioutil"
"math/big"
"os"
"reflect"
"testing"
"github.com/stretchr/testify/suite"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/account/generator"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/t/e2e"
"github.com/status-im/status-go/t/utils"
"github.com/status-im/status-go/transactions"
)
type initFunc func([]byte, *transactions.SendTxArgs)
func buildLoginParams(mainAccountAddress, chatAddress, password string) (account.LoginParams, error) {
privateKey, err := crypto.GenerateKey()
if err != nil {
return account.LoginParams{}, err
}
acc := generator.NewAccount(privateKey, nil)
iai := acc.ToIdentifiedAccountInfo("")
return account.LoginParams{
ChatAddress: types.HexToAddress(chatAddress),
Password: password,
MainAccount: types.HexToAddress(mainAccountAddress),
MultiAccount: iai.ToMultiAccount(),
}, nil
}
func TestTransactionsTestSuite(t *testing.T) {
utils.Init()
suite.Run(t, new(TransactionsTestSuite))
}
type TransactionsTestSuite struct {
e2e.BackendTestSuite
}
func (s *TransactionsTestSuite) TestCallRPCSendTransaction() {
utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID)
s.StartTestBackend()
defer s.StopTestBackend()
utils.EnsureNodeSync(s.Backend.StatusNode().EnsureSync)
s.sendTransactionUsingRPCClient(s.Backend.CallRPC)
}
func (s *TransactionsTestSuite) TestCallUpstreamRPCSendTransaction() {
utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID, params.StatusChainNetworkID)
addr, err := utils.GetRemoteURL()
s.NoError(err)
s.StartTestBackend(e2e.WithUpstream(addr))
defer s.StopTestBackend()
s.sendTransactionUsingRPCClient(s.Backend.CallRPC)
}
func (s *TransactionsTestSuite) TestCallPrivateRPCSendTransaction() {
utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID)
s.StartTestBackend()
defer s.StopTestBackend()
utils.EnsureNodeSync(s.Backend.StatusNode().EnsureSync)
s.sendTransactionUsingRPCClient(s.Backend.CallPrivateRPC)
}
func (s *TransactionsTestSuite) TestCallUpstreamPrivateRPCSendTransaction() {
utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID, params.StatusChainNetworkID)
addr, err := utils.GetRemoteURL()
s.NoError(err)
s.StartTestBackend(e2e.WithUpstream(addr))
defer s.StopTestBackend()
s.sendTransactionUsingRPCClient(s.Backend.CallPrivateRPC)
}
func (s *TransactionsTestSuite) sendTransactionUsingRPCClient(
callRPCFn func(string) (string, error),
) {
loginParams, err := buildLoginParams(
utils.TestConfig.Account1.WalletAddress,
utils.TestConfig.Account1.ChatAddress,
utils.TestConfig.Account1.Password,
)
s.Require().NoError(err)
err = s.Backend.SelectAccount(loginParams)
s.NoError(err)
result, err := callRPCFn(`{
"jsonrpc": "2.0",
"id": 1,
"method": "eth_sendTransaction",
"params": [{
"from": "` + utils.TestConfig.Account1.WalletAddress + `",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"value": "0x9184e72a"
}]
}`)
s.NoError(err)
s.Contains(result, `"error":{"code":-32700,"message":"method is unsupported by RPC interface"}`)
}
func (s *TransactionsTestSuite) TestEmptyToFieldPreserved() {
utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID)
tmpdir, err := ioutil.TempDir("", "transactions-tests-")
s.Require().NoError(err)
defer os.Remove(tmpdir)
wallet := types.HexToAddress(utils.TestConfig.Account1.WalletAddress)
s.StartTestBackendWithAccount(multiaccounts.Account{KeyUID: utils.TestConfig.Account1.WalletAddress}, utils.TestConfig.Account1.Password,
[]accounts.Account{{Address: wallet, Wallet: true, Chat: true}},
e2e.WithDataDir(tmpdir),
)
defer s.LogoutAndStop()
utils.EnsureNodeSync(s.Backend.StatusNode().EnsureSync)
args := transactions.SendTxArgs{
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
}
hash, err := s.Backend.SendTransaction(args, utils.TestConfig.Account1.Password)
s.NoError(err)
s.NotNil(hash)
}
// TestSendContractCompat tries to send transaction using the legacy "Data"
// field, which is supported for backward compatibility reasons.
func (s *TransactionsTestSuite) TestSendContractTxCompat() {
utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID)
initFunc := func(byteCode []byte, args *transactions.SendTxArgs) {
args.Data = (types.HexBytes)(byteCode)
}
s.testSendContractTx(initFunc, nil, "")
}
// TestSendContractCompat tries to send transaction using both the legacy
// "Data" and "Input" fields. Also makes sure that the error is returned if
// they have different values.
func (s *TransactionsTestSuite) TestSendContractTxCollision() {
utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID)
// Scenario 1: Both fields are filled and have the same value, expect success
initFunc := func(byteCode []byte, args *transactions.SendTxArgs) {
args.Input = (types.HexBytes)(byteCode)
args.Data = (types.HexBytes)(byteCode)
}
s.testSendContractTx(initFunc, nil, "")
// Scenario 2: Both fields are filled with different values, expect an error
inverted := func(source []byte) []byte {
inverse := make([]byte, len(source))
copy(inverse, source)
for i, b := range inverse {
inverse[i] = b ^ 0xFF
}
return inverse
}
initFunc2 := func(byteCode []byte, args *transactions.SendTxArgs) {
args.Input = (types.HexBytes)(byteCode)
args.Data = (types.HexBytes)(inverted(byteCode))
}
s.testSendContractTx(initFunc2, transactions.ErrInvalidSendTxArgs, "expected error when invalid tx args are sent")
}
func (s *TransactionsTestSuite) TestSendContractTx() {
utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID)
initFunc := func(byteCode []byte, args *transactions.SendTxArgs) {
args.Input = (types.HexBytes)(byteCode)
}
s.testSendContractTx(initFunc, nil, "")
}
func (s *TransactionsTestSuite) testSendContractTx(setInputAndDataValue initFunc, expectedError error, expectedErrorDescription string) {
tmpdir, err := ioutil.TempDir("", "transactions-tests-")
s.Require().NoError(err)
defer os.Remove(tmpdir)
wallet := types.HexToAddress(utils.TestConfig.Account1.WalletAddress)
s.StartTestBackendWithAccount(multiaccounts.Account{KeyUID: utils.TestConfig.Account1.WalletAddress}, utils.TestConfig.Account1.Password,
[]accounts.Account{{Address: wallet, Wallet: true, Chat: true}},
e2e.WithDataDir(tmpdir),
)
defer s.LogoutAndStop()
utils.EnsureNodeSync(s.Backend.StatusNode().EnsureSync)
// this call blocks, up until Complete Transaction is called
byteCode, err := types.DecodeHex(`0x6060604052341561000c57fe5b5b60a58061001b6000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680636ffa1caa14603a575bfe5b3415604157fe5b60556004808035906020019091905050606b565b6040518082815260200191505060405180910390f35b60008160020290505b9190505600a165627a7a72305820ccdadd737e4ac7039963b54cee5e5afb25fa859a275252bdcf06f653155228210029`)
s.NoError(err)
gas := uint64(params.DefaultGas)
args := transactions.SendTxArgs{
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
To: nil, // marker, contract creation is expected
//Value: (*hexutil.Big)(new(big.Int).Mul(big.NewInt(1), common.Ether)),
Gas: (*hexutil.Uint64)(&gas),
}
setInputAndDataValue(byteCode, &args)
hash, err := s.Backend.SendTransaction(args, utils.TestConfig.Account1.Password)
if expectedError != nil {
s.Equal(expectedError, err, expectedErrorDescription)
return
}
s.NoError(err)
s.False(reflect.DeepEqual(hash, types.Hash{}))
}
func (s *TransactionsTestSuite) TestSendEther() {
utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID)
tmpdir, err := ioutil.TempDir("", "transactions-tests-")
s.Require().NoError(err)
defer os.Remove(tmpdir)
wallet := types.HexToAddress(utils.TestConfig.Account1.WalletAddress)
s.StartTestBackendWithAccount(multiaccounts.Account{KeyUID: utils.TestConfig.Account1.WalletAddress}, utils.TestConfig.Account1.Password,
[]accounts.Account{{Address: wallet, Wallet: true, Chat: true}},
e2e.WithDataDir(tmpdir),
)
defer s.LogoutAndStop()
utils.EnsureNodeSync(s.Backend.StatusNode().EnsureSync)
hash, err := s.Backend.SendTransaction(transactions.SendTxArgs{
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
}, utils.TestConfig.Account1.Password)
s.NoError(err)
s.False(reflect.DeepEqual(hash, types.Hash{}))
}
func (s *TransactionsTestSuite) TestSendEtherTxUpstream() {
utils.CheckTestSkipForNetworks(s.T(), params.MainNetworkID, params.StatusChainNetworkID)
tmpdir, err := ioutil.TempDir("", "transactions-tests-")
s.Require().NoError(err)
defer os.Remove(tmpdir)
addr, err := utils.GetRemoteURL()
s.NoError(err)
wallet := types.HexToAddress(utils.TestConfig.Account1.WalletAddress)
s.StartTestBackendWithAccount(multiaccounts.Account{KeyUID: utils.TestConfig.Account1.WalletAddress}, utils.TestConfig.Account1.Password,
[]accounts.Account{{Address: wallet, Wallet: true, Chat: true}},
e2e.WithUpstream(addr),
e2e.WithDataDir(tmpdir),
)
defer s.LogoutAndStop()
hash, err := s.Backend.SendTransaction(transactions.SendTxArgs{
From: account.FromAddress(utils.TestConfig.Account1.WalletAddress),
To: account.ToAddress(utils.TestConfig.Account2.WalletAddress),
GasPrice: (*hexutil.Big)(big.NewInt(28000000000)),
Value: (*hexutil.Big)(big.NewInt(1000000000000)),
}, utils.TestConfig.Account1.Password)
s.NoError(err)
s.False(reflect.DeepEqual(hash, types.Hash{}))
}

View File

@ -1,10 +1,8 @@
package transactions
import (
"context"
"errors"
"fmt"
"math"
"math/big"
"reflect"
"testing"
@ -13,10 +11,8 @@ import (
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/suite"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
gethtypes "github.com/ethereum/go-ethereum/core/types"
gethcrypto "github.com/ethereum/go-ethereum/crypto"
gethparams "github.com/ethereum/go-ethereum/params"

View File

@ -1,873 +0,0 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package backends
import (
"context"
"errors"
"fmt"
"math/big"
"sync"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
// This nil assignment ensures at compile time that SimulatedBackend implements bind.ContractBackend.
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
var (
errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
errBlockDoesNotExist = errors.New("block does not exist in blockchain")
errTransactionDoesNotExist = errors.New("transaction does not exist")
)
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
// the background. Its main purpose is to allow for easy testing of contract bindings.
// Simulated backend implements the following interfaces:
// ChainReader, ChainStateReader, ContractBackend, ContractCaller, ContractFilterer, ContractTransactor,
// DeployBackend, GasEstimator, GasPricer, LogFilterer, PendingContractCaller, TransactionReader, and TransactionSender
type SimulatedBackend struct {
database ethdb.Database // In memory database to store our testing data
blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
mu sync.Mutex
pendingBlock *types.Block // Currently pending block that will be imported on request
pendingState *state.StateDB // Currently pending state that will be the active on request
events *filters.EventSystem // Event system for filtering log events live
config *params.ChainConfig
}
// NewSimulatedBackendWithDatabase creates a new binding backend based on the given database
// and uses a simulated blockchain for testing purposes.
// A simulated backend always uses chainID 1337.
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
genesis.MustCommit(database)
blockchain, _ := core.NewBlockChain(database, nil, genesis.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
backend := &SimulatedBackend{
database: database,
blockchain: blockchain,
config: genesis.Config,
events: filters.NewEventSystem(&filterBackend{database, blockchain}, false),
}
backend.rollback(blockchain.CurrentBlock())
return backend
}
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
// for testing purposes.
// A simulated backend always uses chainID 1337.
func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc, gasLimit)
}
// Close terminates the underlying blockchain's update loop.
func (b *SimulatedBackend) Close() error {
b.blockchain.Stop()
return nil
}
// Commit imports all the pending transactions as a single block and starts a
// fresh new state.
func (b *SimulatedBackend) Commit() {
b.mu.Lock()
defer b.mu.Unlock()
if _, err := b.blockchain.InsertChain([]*types.Block{b.pendingBlock}); err != nil {
panic(err) // This cannot happen unless the simulator is wrong, fail in that case
}
// Using the last inserted block here makes it possible to build on a side
// chain after a fork.
b.rollback(b.pendingBlock)
}
// Rollback aborts all pending transactions, reverting to the last committed state.
func (b *SimulatedBackend) Rollback() {
b.mu.Lock()
defer b.mu.Unlock()
b.rollback(b.blockchain.CurrentBlock())
}
func (b *SimulatedBackend) rollback(parent *types.Block) {
blocks, _ := core.GenerateChain(b.config, parent, ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.blockchain.StateCache(), nil)
}
// Fork creates a side-chain that can be used to simulate reorgs.
//
// This function should be called with the ancestor block where the new side
// chain should be started. Transactions (old and new) can then be applied on
// top and Commit-ed.
//
// Note, the side-chain will only become canonical (and trigger the events) when
// it becomes longer. Until then CallContract will still operate on the current
// canonical chain.
//
// There is a % chance that the side chain becomes canonical at the same length
// to simulate live network behavior.
func (b *SimulatedBackend) Fork(ctx context.Context, parent common.Hash) error {
b.mu.Lock()
defer b.mu.Unlock()
if len(b.pendingBlock.Transactions()) != 0 {
return errors.New("pending block dirty")
}
block, err := b.blockByHash(ctx, parent)
if err != nil {
return err
}
b.rollback(block)
return nil
}
// stateByBlockNumber retrieves a state by a given blocknumber.
func (b *SimulatedBackend) stateByBlockNumber(ctx context.Context, blockNumber *big.Int) (*state.StateDB, error) {
if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) == 0 {
return b.blockchain.State()
}
block, err := b.blockByNumber(ctx, blockNumber)
if err != nil {
return nil, err
}
return b.blockchain.StateAt(block.Root())
}
// CodeAt returns the code associated with a certain account in the blockchain.
func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
b.mu.Lock()
defer b.mu.Unlock()
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
if err != nil {
return nil, err
}
return stateDB.GetCode(contract), nil
}
// BalanceAt returns the wei balance of a certain account in the blockchain.
func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (*big.Int, error) {
b.mu.Lock()
defer b.mu.Unlock()
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
if err != nil {
return nil, err
}
return stateDB.GetBalance(contract), nil
}
// NonceAt returns the nonce of a certain account in the blockchain.
func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address, blockNumber *big.Int) (uint64, error) {
b.mu.Lock()
defer b.mu.Unlock()
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
if err != nil {
return 0, err
}
return stateDB.GetNonce(contract), nil
}
// StorageAt returns the value of key in the storage of an account in the blockchain.
func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
b.mu.Lock()
defer b.mu.Unlock()
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
if err != nil {
return nil, err
}
val := stateDB.GetState(contract, key)
return val[:], nil
}
// TransactionReceipt returns the receipt of a transaction.
func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
b.mu.Lock()
defer b.mu.Unlock()
receipt, _, _, _ := rawdb.ReadReceipt(b.database, txHash, b.config)
return receipt, nil
}
// TransactionByHash checks the pool of pending transactions in addition to the
// blockchain. The isPending return value indicates whether the transaction has been
// mined yet. Note that the transaction may not be part of the canonical chain even if
// it's not pending.
func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
b.mu.Lock()
defer b.mu.Unlock()
tx := b.pendingBlock.Transaction(txHash)
if tx != nil {
return tx, true, nil
}
tx, _, _, _ = rawdb.ReadTransaction(b.database, txHash)
if tx != nil {
return tx, false, nil
}
return nil, false, ethereum.NotFound
}
// BlockByHash retrieves a block based on the block hash.
func (b *SimulatedBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
b.mu.Lock()
defer b.mu.Unlock()
return b.blockByHash(ctx, hash)
}
// blockByHash retrieves a block based on the block hash without Locking.
func (b *SimulatedBackend) blockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
if hash == b.pendingBlock.Hash() {
return b.pendingBlock, nil
}
block := b.blockchain.GetBlockByHash(hash)
if block != nil {
return block, nil
}
return nil, errBlockDoesNotExist
}
// BlockByNumber retrieves a block from the database by number, caching it
// (associated with its hash) if found.
func (b *SimulatedBackend) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
b.mu.Lock()
defer b.mu.Unlock()
return b.blockByNumber(ctx, number)
}
// blockByNumber retrieves a block from the database by number, caching it
// (associated with its hash) if found without Lock.
func (b *SimulatedBackend) blockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
if number == nil || number.Cmp(b.pendingBlock.Number()) == 0 {
return b.blockchain.CurrentBlock(), nil
}
block := b.blockchain.GetBlockByNumber(uint64(number.Int64()))
if block == nil {
return nil, errBlockDoesNotExist
}
return block, nil
}
// HeaderByHash returns a block header from the current canonical chain.
func (b *SimulatedBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
b.mu.Lock()
defer b.mu.Unlock()
if hash == b.pendingBlock.Hash() {
return b.pendingBlock.Header(), nil
}
header := b.blockchain.GetHeaderByHash(hash)
if header == nil {
return nil, errBlockDoesNotExist
}
return header, nil
}
// HeaderByNumber returns a block header from the current canonical chain. If number is
// nil, the latest known header is returned.
func (b *SimulatedBackend) HeaderByNumber(ctx context.Context, block *big.Int) (*types.Header, error) {
b.mu.Lock()
defer b.mu.Unlock()
if block == nil || block.Cmp(b.pendingBlock.Number()) == 0 {
return b.blockchain.CurrentHeader(), nil
}
return b.blockchain.GetHeaderByNumber(uint64(block.Int64())), nil
}
// TransactionCount returns the number of transactions in a given block.
func (b *SimulatedBackend) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) {
b.mu.Lock()
defer b.mu.Unlock()
if blockHash == b.pendingBlock.Hash() {
return uint(b.pendingBlock.Transactions().Len()), nil
}
block := b.blockchain.GetBlockByHash(blockHash)
if block == nil {
return uint(0), errBlockDoesNotExist
}
return uint(block.Transactions().Len()), nil
}
// TransactionInBlock returns the transaction for a specific block at a specific index.
func (b *SimulatedBackend) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) {
b.mu.Lock()
defer b.mu.Unlock()
if blockHash == b.pendingBlock.Hash() {
transactions := b.pendingBlock.Transactions()
if uint(len(transactions)) < index+1 {
return nil, errTransactionDoesNotExist
}
return transactions[index], nil
}
block := b.blockchain.GetBlockByHash(blockHash)
if block == nil {
return nil, errBlockDoesNotExist
}
transactions := block.Transactions()
if uint(len(transactions)) < index+1 {
return nil, errTransactionDoesNotExist
}
return transactions[index], nil
}
// PendingCodeAt returns the code associated with an account in the pending state.
func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
b.mu.Lock()
defer b.mu.Unlock()
return b.pendingState.GetCode(contract), nil
}
func newRevertError(result *core.ExecutionResult) *revertError {
reason, errUnpack := abi.UnpackRevert(result.Revert())
err := errors.New("execution reverted")
if errUnpack == nil {
err = fmt.Errorf("execution reverted: %v", reason)
}
return &revertError{
error: err,
reason: hexutil.Encode(result.Revert()),
}
}
// revertError is an API error that encompasses an EVM revert with JSON error
// code and a binary data blob.
type revertError struct {
error
reason string // revert reason hex encoded
}
// ErrorCode returns the JSON error code for a revert.
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal
func (e *revertError) ErrorCode() int {
return 3
}
// ErrorData returns the hex encoded revert reason.
func (e *revertError) ErrorData() interface{} {
return e.reason
}
// CallContract executes a contract call.
func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
b.mu.Lock()
defer b.mu.Unlock()
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
return nil, errBlockNumberUnsupported
}
stateDB, err := b.blockchain.State()
if err != nil {
return nil, err
}
res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), stateDB)
if err != nil {
return nil, err
}
// If the result contains a revert reason, try to unpack and return it.
if len(res.Revert()) > 0 {
return nil, newRevertError(res)
}
return res.Return(), res.Err
}
// PendingCallContract executes a contract call on the pending state.
func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
b.mu.Lock()
defer b.mu.Unlock()
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot())
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
if err != nil {
return nil, err
}
// If the result contains a revert reason, try to unpack and return it.
if len(res.Revert()) > 0 {
return nil, newRevertError(res)
}
return res.Return(), res.Err
}
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
// the nonce currently pending for the account.
func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
b.mu.Lock()
defer b.mu.Unlock()
return b.pendingState.GetOrNewStateObject(account).Nonce(), nil
}
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
// chain doesn't have miners, we just return a gas price of 1 for any call.
func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
return big.NewInt(1), nil
}
// SuggestGasTipCap implements ContractTransactor.SuggestGasTipCap. Since the simulated
// chain doesn't have miners, we just return a gas tip of 1 for any call.
func (b *SimulatedBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
return big.NewInt(1), nil
}
// EstimateGas executes the requested code against the currently pending block/state and
// returns the used amount of gas.
func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) {
b.mu.Lock()
defer b.mu.Unlock()
// Determine the lowest and highest possible gas limits to binary search in between
var (
lo uint64 = params.TxGas - 1
hi uint64
cap uint64
)
if call.Gas >= params.TxGas {
hi = call.Gas
} else {
hi = b.pendingBlock.GasLimit()
}
// Recap the highest gas allowance with account's balance.
if call.GasPrice != nil && call.GasPrice.BitLen() != 0 {
balance := b.pendingState.GetBalance(call.From) // from can't be nil
available := new(big.Int).Set(balance)
if call.Value != nil {
if call.Value.Cmp(available) >= 0 {
return 0, errors.New("insufficient funds for transfer")
}
available.Sub(available, call.Value)
}
allowance := new(big.Int).Div(available, call.GasPrice)
if allowance.IsUint64() && hi > allowance.Uint64() {
transfer := call.Value
if transfer == nil {
transfer = new(big.Int)
}
log.Warn("Gas estimation capped by limited funds", "original", hi, "balance", balance,
"sent", transfer, "gasprice", call.GasPrice, "fundable", allowance)
hi = allowance.Uint64()
}
}
cap = hi
// Create a helper to check if a gas allowance results in an executable transaction
executable := func(gas uint64) (bool, *core.ExecutionResult, error) {
call.Gas = gas
snapshot := b.pendingState.Snapshot()
res, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
b.pendingState.RevertToSnapshot(snapshot)
if err != nil {
if errors.Is(err, core.ErrIntrinsicGas) {
return true, nil, nil // Special case, raise gas limit
}
return true, nil, err // Bail out
}
return res.Failed(), res, nil
}
// Execute the binary search and hone in on an executable gas limit
for lo+1 < hi {
mid := (hi + lo) / 2
failed, _, err := executable(mid)
// If the error is not nil(consensus error), it means the provided message
// call or transaction will never be accepted no matter how much gas it is
// assigned. Return the error directly, don't struggle any more
if err != nil {
return 0, err
}
if failed {
lo = mid
} else {
hi = mid
}
}
// Reject the transaction as invalid if it still fails at the highest allowance
if hi == cap {
failed, result, err := executable(hi)
if err != nil {
return 0, err
}
if failed {
if result != nil && result.Err != vm.ErrOutOfGas {
if len(result.Revert()) > 0 {
return 0, newRevertError(result)
}
return 0, result.Err
}
// Otherwise, the specified gas cap is too low
return 0, fmt.Errorf("gas required exceeds allowance (%d)", cap)
}
}
return hi, nil
}
// callContract implements common code between normal and pending contract calls.
// state is modified during execution, make sure to copy it if necessary.
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, stateDB *state.StateDB) (*core.ExecutionResult, error) {
// Gas prices post 1559 need to be initialized
if call.GasPrice != nil && (call.GasFeeCap != nil || call.GasTipCap != nil) {
return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
}
head := b.blockchain.CurrentHeader()
if !b.blockchain.Config().IsLondon(head.Number) {
// If there's no basefee, then it must be a non-1559 execution
if call.GasPrice == nil {
call.GasPrice = new(big.Int)
}
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
} else {
// A basefee is provided, necessitating 1559-type execution
if call.GasPrice != nil {
// User specified the legacy gas field, convert to 1559 gas typing
call.GasFeeCap, call.GasTipCap = call.GasPrice, call.GasPrice
} else {
// User specified 1559 gas feilds (or none), use those
if call.GasFeeCap == nil {
call.GasFeeCap = new(big.Int)
}
if call.GasTipCap == nil {
call.GasTipCap = new(big.Int)
}
// Backfill the legacy gasPrice for EVM execution, unless we're all zeroes
call.GasPrice = new(big.Int)
if call.GasFeeCap.BitLen() > 0 || call.GasTipCap.BitLen() > 0 {
call.GasPrice = math.BigMin(new(big.Int).Add(call.GasTipCap, head.BaseFee), call.GasFeeCap)
}
}
}
// Ensure message is initialized properly.
if call.Gas == 0 {
call.Gas = 50000000
}
if call.Value == nil {
call.Value = new(big.Int)
}
// Set infinite balance to the fake caller account.
from := stateDB.GetOrNewStateObject(call.From)
from.SetBalance(math.MaxBig256)
// Execute the call.
msg := callMsg{call}
txContext := core.NewEVMTxContext(msg)
evmContext := core.NewEVMBlockContext(block.Header(), b.blockchain, nil)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmEnv := vm.NewEVM(evmContext, txContext, stateDB, b.config, vm.Config{NoBaseFee: true})
gasPool := new(core.GasPool).AddGas(math.MaxUint64)
return core.NewStateTransition(vmEnv, msg, gasPool).TransitionDb()
}
// SendTransaction updates the pending block to include the given transaction.
// It panics if the transaction is invalid.
func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
b.mu.Lock()
defer b.mu.Unlock()
// Get the last block
block, err := b.blockByHash(ctx, b.pendingBlock.ParentHash())
if err != nil {
panic("could not fetch parent")
}
// Check transaction validity
signer := types.MakeSigner(b.blockchain.Config(), block.Number())
sender, err := types.Sender(signer, tx)
if err != nil {
panic(fmt.Errorf("invalid transaction: %v", err))
}
nonce := b.pendingState.GetNonce(sender)
if tx.Nonce() != nonce {
panic(fmt.Errorf("invalid transaction nonce: got %d, want %d", tx.Nonce(), nonce))
}
// Include tx in chain
blocks, _ := core.GenerateChain(b.config, block, ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
for _, tx := range b.pendingBlock.Transactions() {
block.AddTxWithChain(b.blockchain, tx)
}
block.AddTxWithChain(b.blockchain, tx)
})
stateDB, _ := b.blockchain.State()
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
return nil
}
// FilterLogs executes a log filter operation, blocking during execution and
// returning all the results in one batch.
//
// TODO(karalabe): Deprecate when the subscription one can return past data too.
func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.FilterQuery) ([]types.Log, error) {
var filter *filters.Filter
if query.BlockHash != nil {
// Block filter requested, construct a single-shot filter
filter = filters.NewBlockFilter(&filterBackend{b.database, b.blockchain}, *query.BlockHash, query.Addresses, query.Topics)
} else {
// Initialize unset filter boundaries to run from genesis to chain head
from := int64(0)
if query.FromBlock != nil {
from = query.FromBlock.Int64()
}
to := int64(-1)
if query.ToBlock != nil {
to = query.ToBlock.Int64()
}
// Construct the range filter
filter = filters.NewRangeFilter(&filterBackend{b.database, b.blockchain}, from, to, query.Addresses, query.Topics)
}
// Run the filter and return all the logs
logs, err := filter.Logs(ctx)
if err != nil {
return nil, err
}
res := make([]types.Log, len(logs))
for i, nLog := range logs {
res[i] = *nLog
}
return res, nil
}
// SubscribeFilterLogs creates a background log filtering operation, returning a
// subscription immediately, which can be used to stream the found events.
func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
// Subscribe to contract events
sink := make(chan []*types.Log)
sub, err := b.events.SubscribeLogs(query, sink)
if err != nil {
return nil, err
}
// Since we're getting logs in batches, we need to flatten them into a plain stream
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case logs := <-sink:
for _, nlog := range logs {
select {
case ch <- *nlog:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// SubscribeNewHead returns an event subscription for a new header.
func (b *SimulatedBackend) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
// subscribe to a new head
sink := make(chan *types.Header)
sub := b.events.SubscribeNewHeads(sink)
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case head := <-sink:
select {
case ch <- head:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// AdjustTime adds a time shift to the simulated clock.
// It can only be called on empty blocks.
func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
b.mu.Lock()
defer b.mu.Unlock()
if len(b.pendingBlock.Transactions()) != 0 {
return errors.New("Could not adjust time on non-empty block")
}
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
block.OffsetTime(int64(adjustment.Seconds()))
})
stateDB, _ := b.blockchain.State()
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
return nil
}
// Blockchain returns the underlying blockchain.
func (b *SimulatedBackend) Blockchain() *core.BlockChain {
return b.blockchain
}
// callMsg implements core.Message to allow passing it as a transaction simulator.
type callMsg struct {
ethereum.CallMsg
}
func (m callMsg) From() common.Address { return m.CallMsg.From }
func (m callMsg) Nonce() uint64 { return 0 }
func (m callMsg) CheckNonce() bool { return false }
func (m callMsg) To() *common.Address { return m.CallMsg.To }
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
func (m callMsg) GasFeeCap() *big.Int { return m.CallMsg.GasFeeCap }
func (m callMsg) GasTipCap() *big.Int { return m.CallMsg.GasTipCap }
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
func (m callMsg) Data() []byte { return m.CallMsg.Data }
func (m callMsg) AccessList() types.AccessList { return m.CallMsg.AccessList }
// filterBackend implements filters.Backend to support filtering for logs without
// taking bloom-bits acceleration structures into account.
type filterBackend struct {
db ethdb.Database
bc *core.BlockChain
}
func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db }
func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) {
if block == rpc.LatestBlockNumber {
return fb.bc.CurrentHeader(), nil
}
return fb.bc.GetHeaderByNumber(uint64(block.Int64())), nil
}
func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
return fb.bc.GetHeaderByHash(hash), nil
}
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
number := rawdb.ReadHeaderNumber(fb.db, hash)
if number == nil {
return nil, nil
}
return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil
}
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
number := rawdb.ReadHeaderNumber(fb.db, hash)
if number == nil {
return nil, nil
}
receipts := rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config())
if receipts == nil {
return nil, nil
}
logs := make([][]*types.Log, len(receipts))
for i, receipt := range receipts {
logs[i] = receipt.Logs
}
return logs, nil
}
func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
return nullSubscription()
}
func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
return fb.bc.SubscribeChainEvent(ch)
}
func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
return fb.bc.SubscribeRemovedLogsEvent(ch)
}
func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
return fb.bc.SubscribeLogsEvent(ch)
}
func (fb *filterBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription {
return nullSubscription()
}
func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 }
func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) {
panic("not supported")
}
func nullSubscription() event.Subscription {
return event.NewSubscription(func(quit <-chan struct{}) error {
<-quit
return nil
})
}

1
vendor/modules.txt vendored
View File

@ -43,7 +43,6 @@ github.com/ethereum/go-ethereum
github.com/ethereum/go-ethereum/accounts
github.com/ethereum/go-ethereum/accounts/abi
github.com/ethereum/go-ethereum/accounts/abi/bind
github.com/ethereum/go-ethereum/accounts/abi/bind/backends
github.com/ethereum/go-ethereum/accounts/external
github.com/ethereum/go-ethereum/accounts/keystore
github.com/ethereum/go-ethereum/accounts/scwallet