2017-05-16 12:09:52 +00:00
|
|
|
package common
|
|
|
|
|
|
|
|
import (
|
2017-08-10 15:31:29 +00:00
|
|
|
"bytes"
|
2017-09-04 12:56:58 +00:00
|
|
|
"context"
|
2017-05-16 12:09:52 +00:00
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
2017-08-10 15:31:29 +00:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
2017-05-16 12:09:52 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/ethereum/go-ethereum/accounts"
|
|
|
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
2017-09-04 12:56:58 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
2017-05-16 12:09:52 +00:00
|
|
|
"github.com/ethereum/go-ethereum/les"
|
|
|
|
"github.com/ethereum/go-ethereum/node"
|
|
|
|
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
|
|
|
|
"github.com/robertkrimen/otto"
|
|
|
|
"github.com/status-im/status-go/geth/params"
|
2017-09-14 20:14:31 +00:00
|
|
|
"github.com/status-im/status-go/geth/rpc"
|
2017-05-16 12:09:52 +00:00
|
|
|
"github.com/status-im/status-go/static"
|
|
|
|
)
|
|
|
|
|
2017-05-28 13:57:30 +00:00
|
|
|
// errors
|
2017-05-16 12:09:52 +00:00
|
|
|
var (
|
|
|
|
ErrDeprecatedMethod = errors.New("Method is depricated and will be removed in future release")
|
|
|
|
)
|
|
|
|
|
|
|
|
// SelectedExtKey is a container for currently selected (logged in) account
|
|
|
|
type SelectedExtKey struct {
|
|
|
|
Address common.Address
|
|
|
|
AccountKey *keystore.Key
|
|
|
|
SubAccounts []accounts.Account
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hex dumps address of a given extended key as hex string
|
|
|
|
func (k *SelectedExtKey) Hex() string {
|
|
|
|
if k == nil {
|
|
|
|
return "0x0"
|
|
|
|
}
|
|
|
|
|
|
|
|
return k.Address.Hex()
|
|
|
|
}
|
|
|
|
|
|
|
|
// NodeManager defines expected methods for managing Status node
|
|
|
|
type NodeManager interface {
|
|
|
|
// StartNode start Status node, fails if node is already started
|
|
|
|
StartNode(config *params.NodeConfig) (<-chan struct{}, error)
|
|
|
|
|
|
|
|
// StopNode stop the running Status node.
|
|
|
|
// Stopped node cannot be resumed, one starts a new node instead.
|
2017-05-25 13:14:52 +00:00
|
|
|
StopNode() (<-chan struct{}, error)
|
2017-05-16 12:09:52 +00:00
|
|
|
|
|
|
|
// RestartNode restart running Status node, fails if node is not running
|
|
|
|
RestartNode() (<-chan struct{}, error)
|
|
|
|
|
|
|
|
// ResetChainData remove chain data from data directory.
|
|
|
|
// Node is stopped, and new node is started, with clean data directory.
|
|
|
|
ResetChainData() (<-chan struct{}, error)
|
|
|
|
|
|
|
|
// IsNodeRunning confirm that node is running
|
|
|
|
IsNodeRunning() bool
|
|
|
|
|
|
|
|
// NodeConfig returns reference to running node's configuration
|
|
|
|
NodeConfig() (*params.NodeConfig, error)
|
|
|
|
|
|
|
|
// Node returns underlying Status node
|
|
|
|
Node() (*node.Node, error)
|
|
|
|
|
|
|
|
// PopulateStaticPeers populates node's list of static bootstrap peers
|
|
|
|
PopulateStaticPeers() error
|
|
|
|
|
|
|
|
// AddPeer adds URL of static peer
|
|
|
|
AddPeer(url string) error
|
|
|
|
|
|
|
|
// LightEthereumService exposes reference to LES service running on top of the node
|
|
|
|
LightEthereumService() (*les.LightEthereum, error)
|
|
|
|
|
|
|
|
// WhisperService returns reference to running Whisper service
|
|
|
|
WhisperService() (*whisper.Whisper, error)
|
|
|
|
|
|
|
|
// AccountManager returns reference to node's account manager
|
|
|
|
AccountManager() (*accounts.Manager, error)
|
|
|
|
|
|
|
|
// AccountKeyStore returns reference to account manager's keystore
|
|
|
|
AccountKeyStore() (*keystore.KeyStore, error)
|
|
|
|
|
|
|
|
// RPCClient exposes reference to RPC client connected to the running node
|
2017-09-14 20:14:31 +00:00
|
|
|
RPCClient() *rpc.Client
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// AccountManager defines expected methods for managing Status accounts
|
|
|
|
type AccountManager interface {
|
|
|
|
// CreateAccount creates an internal geth account
|
|
|
|
// BIP44-compatible keys are generated: CKD#1 is stored as account key, CKD#2 stored as sub-account root
|
|
|
|
// Public key of CKD#1 is returned, with CKD#2 securely encoded into account key file (to be used for
|
|
|
|
// sub-account derivations)
|
|
|
|
CreateAccount(password string) (address, pubKey, mnemonic string, err error)
|
|
|
|
|
|
|
|
// CreateChildAccount creates sub-account for an account identified by parent address.
|
|
|
|
// CKD#2 is used as root for master accounts (when parentAddress is "").
|
|
|
|
// Otherwise (when parentAddress != ""), child is derived directly from parent.
|
|
|
|
CreateChildAccount(parentAddress, password string) (address, pubKey string, err error)
|
|
|
|
|
|
|
|
// RecoverAccount re-creates master key using given details.
|
|
|
|
// Once master key is re-generated, it is inserted into keystore (if not already there).
|
|
|
|
RecoverAccount(password, mnemonic string) (address, pubKey string, err error)
|
|
|
|
|
|
|
|
// VerifyAccountPassword tries to decrypt a given account key file, with a provided password.
|
|
|
|
// If no error is returned, then account is considered verified.
|
|
|
|
VerifyAccountPassword(keyStoreDir, address, password string) (*keystore.Key, error)
|
|
|
|
|
|
|
|
// SelectAccount selects current account, by verifying that address has corresponding account which can be decrypted
|
|
|
|
// using provided password. Once verification is done, decrypted key is injected into Whisper (as a single identity,
|
|
|
|
// all previous identities are removed).
|
|
|
|
SelectAccount(address, password string) error
|
|
|
|
|
|
|
|
// ReSelectAccount selects previously selected account, often, after node restart.
|
|
|
|
ReSelectAccount() error
|
|
|
|
|
|
|
|
// SelectedAccount returns currently selected account
|
|
|
|
SelectedAccount() (*SelectedExtKey, error)
|
|
|
|
|
|
|
|
// Logout clears whisper identities
|
|
|
|
Logout() error
|
|
|
|
|
|
|
|
// AccountsListRequestHandler returns handler to process account list request
|
|
|
|
AccountsListRequestHandler() func(entities []common.Address) []common.Address
|
|
|
|
|
|
|
|
// AddressToDecryptedAccount tries to load decrypted key for a given account.
|
|
|
|
// The running node, has a keystore directory which is loaded on start. Key file
|
|
|
|
// for a given address is expected to be in that directory prior to node start.
|
|
|
|
AddressToDecryptedAccount(address, password string) (accounts.Account, *keystore.Key, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// RawCompleteTransactionResult is a JSON returned from transaction complete function (used internally)
|
|
|
|
type RawCompleteTransactionResult struct {
|
|
|
|
Hash common.Hash
|
|
|
|
Error error
|
|
|
|
}
|
|
|
|
|
|
|
|
// RawDiscardTransactionResult is list of results from CompleteTransactions() (used internally)
|
|
|
|
type RawDiscardTransactionResult struct {
|
|
|
|
Error error
|
|
|
|
}
|
|
|
|
|
2017-09-04 12:56:58 +00:00
|
|
|
// QueuedTxID queued transaction identifier
|
|
|
|
type QueuedTxID string
|
|
|
|
|
|
|
|
// QueuedTx holds enough information to complete the queued transaction.
|
|
|
|
type QueuedTx struct {
|
2017-09-18 08:53:08 +00:00
|
|
|
ID QueuedTxID
|
|
|
|
Hash common.Hash
|
|
|
|
Context context.Context
|
|
|
|
Args SendTxArgs
|
|
|
|
InProgress bool // true if transaction is being sent
|
|
|
|
Done chan struct{}
|
|
|
|
Discard chan struct{}
|
|
|
|
Err error
|
2017-09-04 12:56:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// SendTxArgs represents the arguments to submit a new transaction into the transaction pool.
|
|
|
|
type SendTxArgs struct {
|
|
|
|
From common.Address `json:"from"`
|
|
|
|
To *common.Address `json:"to"`
|
|
|
|
Gas *hexutil.Big `json:"gas"`
|
|
|
|
GasPrice *hexutil.Big `json:"gasPrice"`
|
|
|
|
Value *hexutil.Big `json:"value"`
|
|
|
|
Data hexutil.Bytes `json:"data"`
|
|
|
|
Nonce *hexutil.Uint64 `json:"nonce"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// EnqueuedTxHandler is a function that receives queued/pending transactions, when they get queued
|
|
|
|
type EnqueuedTxHandler func(*QueuedTx)
|
|
|
|
|
|
|
|
// EnqueuedTxReturnHandler is a function that receives response when tx is complete (both on success and error)
|
|
|
|
type EnqueuedTxReturnHandler func(*QueuedTx, error)
|
|
|
|
|
|
|
|
// TxQueue is a queue of transactions.
|
|
|
|
type TxQueue interface {
|
|
|
|
// Remove removes a transaction from the queue.
|
|
|
|
Remove(id QueuedTxID)
|
|
|
|
|
|
|
|
// Reset resets the state of the queue.
|
|
|
|
Reset()
|
|
|
|
|
|
|
|
// Count returns a number of transactions in the queue.
|
|
|
|
Count() int
|
|
|
|
|
|
|
|
// Has returns true if a transaction is in the queue.
|
|
|
|
Has(id QueuedTxID) bool
|
|
|
|
}
|
|
|
|
|
2017-05-16 12:09:52 +00:00
|
|
|
// TxQueueManager defines expected methods for managing transaction queue
|
|
|
|
type TxQueueManager interface {
|
2017-09-04 12:56:58 +00:00
|
|
|
// Start starts accepting new transaction in the queue.
|
|
|
|
Start()
|
|
|
|
|
|
|
|
// Stop stops accepting new transactions in the queue.
|
|
|
|
Stop()
|
|
|
|
|
|
|
|
// TransactionQueue returns a transaction queue.
|
|
|
|
TransactionQueue() TxQueue
|
|
|
|
|
|
|
|
// CreateTransactoin creates a new transaction.
|
|
|
|
CreateTransaction(ctx context.Context, args SendTxArgs) *QueuedTx
|
|
|
|
|
|
|
|
// QueueTransaction adds a new transaction to the queue.
|
|
|
|
QueueTransaction(tx *QueuedTx) error
|
|
|
|
|
|
|
|
// WaitForTransactions blocks until transaction is completed, discarded or timed out.
|
|
|
|
WaitForTransaction(tx *QueuedTx) error
|
|
|
|
|
|
|
|
// NotifyOnQueuedTxReturn notifies a handler when a transaction returns.
|
|
|
|
NotifyOnQueuedTxReturn(queuedTx *QueuedTx, err error)
|
|
|
|
|
2017-05-16 12:09:52 +00:00
|
|
|
// TransactionQueueHandler returns handler that processes incoming tx queue requests
|
2017-09-04 12:56:58 +00:00
|
|
|
TransactionQueueHandler() func(queuedTx *QueuedTx)
|
|
|
|
|
|
|
|
// TODO(adam): might be not needed
|
|
|
|
SetTransactionQueueHandler(fn EnqueuedTxHandler)
|
|
|
|
|
|
|
|
// TODO(adam): might be not needed
|
|
|
|
SetTransactionReturnHandler(fn EnqueuedTxReturnHandler)
|
2017-05-16 12:09:52 +00:00
|
|
|
|
|
|
|
// TransactionReturnHandler returns handler that processes responses from internal tx manager
|
2017-09-04 12:56:58 +00:00
|
|
|
TransactionReturnHandler() func(queuedTx *QueuedTx, err error)
|
2017-05-16 12:09:52 +00:00
|
|
|
|
|
|
|
// CompleteTransaction instructs backend to complete sending of a given transaction
|
2017-09-04 12:56:58 +00:00
|
|
|
CompleteTransaction(id QueuedTxID, password string) (common.Hash, error)
|
2017-05-16 12:09:52 +00:00
|
|
|
|
|
|
|
// CompleteTransactions instructs backend to complete sending of multiple transactions
|
2017-09-04 12:56:58 +00:00
|
|
|
CompleteTransactions(ids []QueuedTxID, password string) map[QueuedTxID]RawCompleteTransactionResult
|
2017-05-16 12:09:52 +00:00
|
|
|
|
|
|
|
// DiscardTransaction discards a given transaction from transaction queue
|
2017-09-04 12:56:58 +00:00
|
|
|
DiscardTransaction(id QueuedTxID) error
|
2017-05-16 12:09:52 +00:00
|
|
|
|
|
|
|
// DiscardTransactions discards given multiple transactions from transaction queue
|
2017-09-04 12:56:58 +00:00
|
|
|
DiscardTransactions(ids []QueuedTxID) map[QueuedTxID]RawDiscardTransactionResult
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// JailCell represents single jail cell, which is basically a JavaScript VM.
|
2017-09-08 11:55:17 +00:00
|
|
|
// It's designed to be a transparent wrapper around otto.VM's methods.
|
2017-05-16 12:09:52 +00:00
|
|
|
type JailCell interface {
|
2017-09-08 11:55:17 +00:00
|
|
|
// Set a value inside VM.
|
2017-08-04 16:14:17 +00:00
|
|
|
Set(string, interface{}) error
|
2017-09-08 11:55:17 +00:00
|
|
|
// Get a value from VM.
|
2017-08-04 16:14:17 +00:00
|
|
|
Get(string) (otto.Value, error)
|
2017-09-08 11:55:17 +00:00
|
|
|
// Run an arbitrary JS code. Input maybe string or otto.Script.
|
|
|
|
Run(interface{}) (otto.Value, error)
|
|
|
|
// Call an arbitrary JS function by name and args.
|
2017-09-02 17:04:23 +00:00
|
|
|
Call(item string, this interface{}, args ...interface{}) (otto.Value, error)
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// JailManager defines methods for managing jailed environments
|
|
|
|
type JailManager interface {
|
|
|
|
// Parse creates a new jail cell context, with the given chatID as identifier.
|
|
|
|
// New context executes provided JavaScript code, right after the initialization.
|
2017-09-02 17:04:23 +00:00
|
|
|
Parse(chatID, js string) string
|
2017-05-16 12:09:52 +00:00
|
|
|
|
|
|
|
// Call executes given JavaScript function w/i a jail cell context identified by the chatID.
|
2017-09-02 17:04:23 +00:00
|
|
|
Call(chatID, this, args string) string
|
2017-05-16 12:09:52 +00:00
|
|
|
|
2017-09-02 17:04:23 +00:00
|
|
|
// NewCell initializes and returns a new jail cell.
|
|
|
|
NewCell(chatID string) (JailCell, error)
|
2017-05-16 12:09:52 +00:00
|
|
|
|
2017-09-02 17:04:23 +00:00
|
|
|
// Cell returns an existing instance of JailCell.
|
|
|
|
Cell(chatID string) (JailCell, error)
|
2017-05-16 12:09:52 +00:00
|
|
|
|
|
|
|
// BaseJS allows to setup initial JavaScript to be loaded on each jail.Parse()
|
|
|
|
BaseJS(js string)
|
|
|
|
}
|
|
|
|
|
|
|
|
// APIResponse generic response from API
|
|
|
|
type APIResponse struct {
|
|
|
|
Error string `json:"error"`
|
|
|
|
}
|
|
|
|
|
2017-08-10 15:31:29 +00:00
|
|
|
// APIDetailedResponse represents a generic response
|
|
|
|
// with possible errors.
|
|
|
|
type APIDetailedResponse struct {
|
|
|
|
Status bool `json:"status"`
|
|
|
|
Message string `json:"message,omitempty"`
|
|
|
|
FieldErrors []APIFieldError `json:"field_errors,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r APIDetailedResponse) Error() string {
|
|
|
|
buf := bytes.NewBufferString("")
|
|
|
|
|
|
|
|
for _, err := range r.FieldErrors {
|
|
|
|
buf.WriteString(err.Error())
|
|
|
|
buf.WriteString("\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.TrimSpace(buf.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
// APIFieldError represents a set of errors
|
|
|
|
// related to a parameter.
|
|
|
|
type APIFieldError struct {
|
|
|
|
Parameter string `json:"parameter,omitempty"`
|
|
|
|
Errors []APIError `json:"errors"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e APIFieldError) Error() string {
|
|
|
|
if len(e.Errors) == 0 {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := bytes.NewBufferString(fmt.Sprintf("Parameter: %s\n", e.Parameter))
|
|
|
|
|
|
|
|
for _, err := range e.Errors {
|
|
|
|
buf.WriteString(err.Error())
|
|
|
|
buf.WriteString("\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.TrimSpace(buf.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
// APIError represents a single error.
|
|
|
|
type APIError struct {
|
|
|
|
Message string `json:"message"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e APIError) Error() string {
|
|
|
|
return fmt.Sprintf("message=%s", e.Message)
|
|
|
|
}
|
|
|
|
|
2017-05-16 12:09:52 +00:00
|
|
|
// AccountInfo represents account's info
|
|
|
|
type AccountInfo struct {
|
|
|
|
Address string `json:"address"`
|
|
|
|
PubKey string `json:"pubkey"`
|
|
|
|
Mnemonic string `json:"mnemonic"`
|
|
|
|
Error string `json:"error"`
|
|
|
|
}
|
|
|
|
|
2017-08-15 10:27:12 +00:00
|
|
|
// StopRPCCallError defines a error type specific for killing a execution process.
|
|
|
|
type StopRPCCallError struct {
|
|
|
|
Err error
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error returns the internal error associated with the critical error.
|
|
|
|
func (c StopRPCCallError) Error() string {
|
|
|
|
return c.Err.Error()
|
|
|
|
}
|
|
|
|
|
2017-05-16 12:09:52 +00:00
|
|
|
// CompleteTransactionResult is a JSON returned from transaction complete function (used in exposed method)
|
|
|
|
type CompleteTransactionResult struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
Hash string `json:"hash"`
|
|
|
|
Error string `json:"error"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// CompleteTransactionsResult is list of results from CompleteTransactions() (used in exposed method)
|
|
|
|
type CompleteTransactionsResult struct {
|
|
|
|
Results map[string]CompleteTransactionResult `json:"results"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// DiscardTransactionResult is a JSON returned from transaction discard function
|
|
|
|
type DiscardTransactionResult struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
Error string `json:"error"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// DiscardTransactionsResult is a list of results from DiscardTransactions()
|
|
|
|
type DiscardTransactionsResult struct {
|
|
|
|
Results map[string]DiscardTransactionResult `json:"results"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestConfig contains shared (among different test packages) parameters
|
|
|
|
type TestConfig struct {
|
|
|
|
Node struct {
|
|
|
|
SyncSeconds time.Duration
|
|
|
|
HTTPPort int
|
|
|
|
WSPort int
|
|
|
|
}
|
|
|
|
Account1 struct {
|
|
|
|
Address string
|
|
|
|
Password string
|
|
|
|
}
|
|
|
|
Account2 struct {
|
|
|
|
Address string
|
|
|
|
Password string
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// LoadTestConfig loads test configuration values from disk
|
|
|
|
func LoadTestConfig() (*TestConfig, error) {
|
|
|
|
var testConfig TestConfig
|
|
|
|
|
|
|
|
configData := string(static.MustAsset("config/test-data.json"))
|
|
|
|
if err := json.Unmarshal([]byte(configData), &testConfig); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &testConfig, nil
|
|
|
|
}
|