linting and remove unused code
This commit is contained in:
parent
bdf3775b0b
commit
a4701f41ee
|
@ -269,7 +269,7 @@ func (s *ManagerTestSuite) TestSetChatAccount() {
|
||||||
|
|
||||||
address := crypto.PubkeyToAddress(privKey.PublicKey)
|
address := crypto.PubkeyToAddress(privKey.PublicKey)
|
||||||
|
|
||||||
s.accManager.SetChatAccount(privKey)
|
s.Require().NoError(s.accManager.SetChatAccount(privKey))
|
||||||
selectedChatAccount, err := s.accManager.SelectedChatAccount()
|
selectedChatAccount, err := s.accManager.SelectedChatAccount()
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
s.Require().NotNil(selectedChatAccount)
|
s.Require().NotNil(selectedChatAccount)
|
||||||
|
|
|
@ -33,10 +33,6 @@ import (
|
||||||
"github.com/status-im/status-go/transactions"
|
"github.com/status-im/status-go/transactions"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
contractQueryTimeout = 1000 * time.Millisecond
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrWhisperClearIdentitiesFailure clearing whisper identities has failed.
|
// ErrWhisperClearIdentitiesFailure clearing whisper identities has failed.
|
||||||
ErrWhisperClearIdentitiesFailure = errors.New("failed to clear whisper identities")
|
ErrWhisperClearIdentitiesFailure = errors.New("failed to clear whisper identities")
|
||||||
|
@ -278,7 +274,9 @@ func (b *GethStatusBackend) startNodeWithKey(acc multiaccounts.Account, password
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b.accountManager.SetChatAccount(chatKey)
|
if err := b.accountManager.SetChatAccount(chatKey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
_, err = b.accountManager.SelectedChatAccount()
|
_, err = b.accountManager.SelectedChatAccount()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
stdlog "log"
|
stdlog "log"
|
||||||
|
@ -25,6 +24,7 @@ import (
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
"github.com/status-im/status-go/multiaccounts"
|
"github.com/status-im/status-go/multiaccounts"
|
||||||
"github.com/status-im/status-go/multiaccounts/accounts"
|
"github.com/status-im/status-go/multiaccounts/accounts"
|
||||||
|
|
||||||
//"github.com/status-im/status-go/appdatabase"
|
//"github.com/status-im/status-go/appdatabase"
|
||||||
//gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
//gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
||||||
"github.com/status-im/status-go/eth-node/crypto"
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
|
@ -54,8 +54,6 @@ var (
|
||||||
ipcEnabled = flag.Bool("ipc", false, "Enable IPC RPC endpoint")
|
ipcEnabled = flag.Bool("ipc", false, "Enable IPC RPC endpoint")
|
||||||
ipcFile = flag.String("ipcfile", "", "Set IPC file path")
|
ipcFile = flag.String("ipcfile", "", "Set IPC file path")
|
||||||
seedPhrase = flag.String("seed-phrase", "", "Seed phrase")
|
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")
|
version = flag.Bool("version", false, "Print version and dump configuration")
|
||||||
nAddedContacts = flag.Int("added-contacts", 100, "Number of added contacts to create")
|
nAddedContacts = flag.Int("added-contacts", 100, "Number of added contacts to create")
|
||||||
nContacts = flag.Int("contacts", 100, "Number of 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")
|
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")
|
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(
|
networkID = flag.Int(
|
||||||
"network-id",
|
"network-id",
|
||||||
params.RopstenNetworkID,
|
params.RopstenNetworkID,
|
||||||
|
@ -74,8 +71,6 @@ var (
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
listenAddr = flag.String("addr", "", "address to bind listener to")
|
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.
|
// 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 {
|
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.
|
// printVersion prints verbose output about version and config.
|
||||||
func printVersion(config *params.NodeConfig) {
|
func printVersion(config *params.NodeConfig) {
|
||||||
fmt.Println(strings.Title(config.Name))
|
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 {
|
func ImportAccount(seedPhrase string, backend *api.GethStatusBackend) error {
|
||||||
backend.UpdateRootDataDir("./tmp")
|
backend.UpdateRootDataDir("./tmp")
|
||||||
manager := backend.AccountManager()
|
manager := backend.AccountManager()
|
||||||
manager.InitKeystore("./tmp")
|
if err := manager.InitKeystore("./tmp"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
err := backend.OpenAccounts()
|
err := backend.OpenAccounts()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed open accounts", err)
|
logger.Error("failed open accounts", err)
|
||||||
|
@ -438,19 +423,16 @@ func ImportAccount(seedPhrase string, backend *api.GethStatusBackend) error {
|
||||||
generator := manager.AccountsGenerator()
|
generator := manager.AccountsGenerator()
|
||||||
generatedAccountInfo, err := generator.ImportMnemonic(seedPhrase, "")
|
generatedAccountInfo, err := generator.ImportMnemonic(seedPhrase, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("import mnemonic", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
derivedAddresses, err := generator.DeriveAddresses(generatedAccountInfo.ID, paths)
|
derivedAddresses, err := generator.DeriveAddresses(generatedAccountInfo.ID, paths)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("deriver addressess", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = generator.StoreDerivedAccounts(generatedAccountInfo.ID, "", paths)
|
_, err = generator.StoreDerivedAccounts(generatedAccountInfo.ID, "", paths)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("store addressess", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,13 +441,11 @@ func ImportAccount(seedPhrase string, backend *api.GethStatusBackend) error {
|
||||||
}
|
}
|
||||||
settings, err := defaultSettings(generatedAccountInfo, derivedAddresses, &seedPhrase)
|
settings, err := defaultSettings(generatedAccountInfo, derivedAddresses, &seedPhrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("default settings", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeConfig, err := defaultNodeConfig(settings.InstallationID)
|
nodeConfig, err := defaultNodeConfig(settings.InstallationID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("node config", err)
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,14 +509,14 @@ func buildMessage(chat *protocol.Chat, count int) *common.Message {
|
||||||
message.MessageType = protobuf.MessageType_PRIVATE_GROUP
|
message.MessageType = protobuf.MessageType_PRIVATE_GROUP
|
||||||
}
|
}
|
||||||
|
|
||||||
message.PrepareContent("")
|
_ = message.PrepareContent("")
|
||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
|
|
||||||
func randomString(n int) string {
|
func randomString(n int) string {
|
||||||
b := make([]rune, n)
|
b := make([]rune, n)
|
||||||
for i := range b {
|
for i := range b {
|
||||||
b[i] = letterRunes[rand.Intn(len(letterRunes))]
|
b[i] = letterRunes[rand.Intn(len(letterRunes))] // nolint: gosec
|
||||||
}
|
}
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
|
|
||||||
ma "github.com/multiformats/go-multiaddr"
|
ma "github.com/multiformats/go-multiaddr"
|
||||||
"github.com/syndtr/goleveldb/leveldb"
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
|
@ -35,7 +34,6 @@ import (
|
||||||
"github.com/status-im/status-go/services/browsers"
|
"github.com/status-im/status-go/services/browsers"
|
||||||
localnotifications "github.com/status-im/status-go/services/local-notifications"
|
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/mailservers"
|
||||||
"github.com/status-im/status-go/services/nodebridge"
|
|
||||||
"github.com/status-im/status-go/services/peer"
|
"github.com/status-im/status-go/services/peer"
|
||||||
"github.com/status-im/status-go/services/permissions"
|
"github.com/status-im/status-go/services/permissions"
|
||||||
"github.com/status-im/status-go/services/personal"
|
"github.com/status-im/status-go/services/personal"
|
||||||
|
@ -50,9 +48,6 @@ import (
|
||||||
"github.com/status-im/status-go/wakuv2"
|
"github.com/status-im/status-go/wakuv2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// tickerResolution is the delta to check blockchain sync progress.
|
|
||||||
const tickerResolution = time.Second
|
|
||||||
|
|
||||||
// errors
|
// errors
|
||||||
var (
|
var (
|
||||||
ErrNodeRunning = errors.New("node is already running")
|
ErrNodeRunning = errors.New("node is already running")
|
||||||
|
@ -96,7 +91,6 @@ type StatusNode struct {
|
||||||
rpcStatsSrvc *rpcstats.Service
|
rpcStatsSrvc *rpcstats.Service
|
||||||
accountsSrvc *accountssvc.Service
|
accountsSrvc *accountssvc.Service
|
||||||
browsersSrvc *browsers.Service
|
browsersSrvc *browsers.Service
|
||||||
nodeBridgeSrvc *nodebridge.NodeService
|
|
||||||
permissionsSrvc *permissions.Service
|
permissionsSrvc *permissions.Service
|
||||||
mailserversSrvc *mailservers.Service
|
mailserversSrvc *mailservers.Service
|
||||||
appMetricsSrvc *appmetricsservice.Service
|
appMetricsSrvc *appmetricsservice.Service
|
||||||
|
@ -403,7 +397,6 @@ func (n *StatusNode) stop() error {
|
||||||
n.rpcStatsSrvc = nil
|
n.rpcStatsSrvc = nil
|
||||||
n.accountsSrvc = nil
|
n.accountsSrvc = nil
|
||||||
n.browsersSrvc = nil
|
n.browsersSrvc = nil
|
||||||
n.nodeBridgeSrvc = nil
|
|
||||||
n.permissionsSrvc = nil
|
n.permissionsSrvc = nil
|
||||||
n.mailserversSrvc = nil
|
n.mailserversSrvc = nil
|
||||||
n.appMetricsSrvc = nil
|
n.appMetricsSrvc = nil
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package node
|
package node
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
@ -10,7 +9,6 @@ import (
|
||||||
"github.com/syndtr/goleveldb/leveldb"
|
"github.com/syndtr/goleveldb/leveldb"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/node"
|
"github.com/ethereum/go-ethereum/node"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"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/eth-node/crypto"
|
||||||
"github.com/status-im/status-go/params"
|
"github.com/status-im/status-go/params"
|
||||||
"github.com/status-im/status-go/static"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Errors related to node and services creation.
|
// 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
|
// newGethNodeConfig returns default stack configuration for mobile client node
|
||||||
func newGethNodeConfig(config *params.NodeConfig) (*node.Config, error) {
|
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
|
// 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
|
// to be used, so they need to be set to something
|
||||||
maxPeers := 100
|
maxPeers := 100
|
||||||
|
@ -133,46 +130,6 @@ func newGethNodeConfig(config *params.NodeConfig) (*node.Config, error) {
|
||||||
return nc, nil
|
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.
|
// parseNodes creates list of enode.Node out of enode strings.
|
||||||
func parseNodes(enodes []string) []*enode.Node {
|
func parseNodes(enodes []string) []*enode.Node {
|
||||||
var nodes []*enode.Node
|
var nodes []*enode.Node
|
||||||
|
|
|
@ -27,5 +27,4 @@ func addSuitableCallbacks(receiver reflect.Value, namespace string, methods map[
|
||||||
name := formatName(method.Name)
|
name := formatName(method.Name)
|
||||||
methods[namespace+"_"+name] = true
|
methods[namespace+"_"+name] = true
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ import (
|
||||||
"github.com/status-im/status-go/services/ext"
|
"github.com/status-im/status-go/services/ext"
|
||||||
localnotifications "github.com/status-im/status-go/services/local-notifications"
|
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/mailservers"
|
||||||
"github.com/status-im/status-go/services/nodebridge"
|
|
||||||
"github.com/status-im/status-go/services/peer"
|
"github.com/status-im/status-go/services/peer"
|
||||||
"github.com/status-im/status-go/services/permissions"
|
"github.com/status-im/status-go/services/permissions"
|
||||||
"github.com/status-im/status-go/services/personal"
|
"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)
|
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) {
|
func (b *StatusNode) wakuExtService(config *params.NodeConfig) (*wakuext.Service, error) {
|
||||||
if b.gethNode == nil {
|
if b.gethNode == nil {
|
||||||
return nil, errors.New("geth node not initialized")
|
return nil, errors.New("geth node not initialized")
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -8,21 +8,6 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
fromWallet = `
|
|
||||||
{
|
|
||||||
"name": "Cow",
|
|
||||||
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
|
|
||||||
}
|
|
||||||
`
|
|
||||||
toWallet = `
|
|
||||||
{
|
|
||||||
"name": "Bob",
|
|
||||||
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
|
|
||||||
}
|
|
||||||
`
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestChainIDValidation(t *testing.T) {
|
func TestChainIDValidation(t *testing.T) {
|
||||||
chain := big.NewInt(10)
|
chain := big.NewInt(10)
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
|
|
|
@ -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!
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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())
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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")
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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")
|
|
||||||
}
|
|
|
@ -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"}`)
|
|
||||||
}
|
|
193
t/e2e/suites.go
193
t/e2e/suites.go
|
@ -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())
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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{}))
|
|
||||||
}
|
|
|
@ -1,10 +1,8 @@
|
||||||
package transactions
|
package transactions
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -13,10 +11,8 @@ import (
|
||||||
"github.com/golang/mock/gomock"
|
"github.com/golang/mock/gomock"
|
||||||
"github.com/stretchr/testify/suite"
|
"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"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
|
||||||
gethtypes "github.com/ethereum/go-ethereum/core/types"
|
gethtypes "github.com/ethereum/go-ethereum/core/types"
|
||||||
gethcrypto "github.com/ethereum/go-ethereum/crypto"
|
gethcrypto "github.com/ethereum/go-ethereum/crypto"
|
||||||
gethparams "github.com/ethereum/go-ethereum/params"
|
gethparams "github.com/ethereum/go-ethereum/params"
|
||||||
|
|
873
vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go
generated
vendored
873
vendor/github.com/ethereum/go-ethereum/accounts/abi/bind/backends/simulated.go
generated
vendored
|
@ -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
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -43,7 +43,6 @@ github.com/ethereum/go-ethereum
|
||||||
github.com/ethereum/go-ethereum/accounts
|
github.com/ethereum/go-ethereum/accounts
|
||||||
github.com/ethereum/go-ethereum/accounts/abi
|
github.com/ethereum/go-ethereum/accounts/abi
|
||||||
github.com/ethereum/go-ethereum/accounts/abi/bind
|
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/external
|
||||||
github.com/ethereum/go-ethereum/accounts/keystore
|
github.com/ethereum/go-ethereum/accounts/keystore
|
||||||
github.com/ethereum/go-ethereum/accounts/scwallet
|
github.com/ethereum/go-ethereum/accounts/scwallet
|
||||||
|
|
Loading…
Reference in New Issue