2017-05-16 12:09:52 +00:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
2017-09-04 12:56:58 +00:00
|
|
|
"context"
|
2017-05-25 13:14:52 +00:00
|
|
|
"sync"
|
|
|
|
|
2017-05-16 12:09:52 +00:00
|
|
|
gethcommon "github.com/ethereum/go-ethereum/common"
|
2017-09-26 13:44:26 +00:00
|
|
|
"github.com/status-im/status-go/geth/account"
|
2017-05-16 12:09:52 +00:00
|
|
|
"github.com/status-im/status-go/geth/common"
|
|
|
|
"github.com/status-im/status-go/geth/jail"
|
2017-08-10 13:35:58 +00:00
|
|
|
"github.com/status-im/status-go/geth/log"
|
2017-05-16 12:09:52 +00:00
|
|
|
"github.com/status-im/status-go/geth/node"
|
2017-10-11 13:51:43 +00:00
|
|
|
"github.com/status-im/status-go/geth/notification/fcm"
|
2017-05-16 12:09:52 +00:00
|
|
|
"github.com/status-im/status-go/geth/params"
|
2017-09-25 18:22:57 +00:00
|
|
|
"github.com/status-im/status-go/geth/signal"
|
2018-01-26 05:59:21 +00:00
|
|
|
"github.com/status-im/status-go/geth/transactions"
|
2017-10-10 15:30:56 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
//todo(jeka): should be removed
|
|
|
|
fcmServerKey = "AAAAxwa-r08:APA91bFtMIToDVKGAmVCm76iEXtA4dn9MPvLdYKIZqAlNpLJbd12EgdBI9DSDSXKdqvIAgLodepmRhGVaWvhxnXJzVpE6MoIRuKedDV3kfHSVBhWFqsyoLTwXY4xeufL9Sdzb581U-lx"
|
2017-05-16 12:09:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// StatusBackend implements Status.im service
|
|
|
|
type StatusBackend struct {
|
2017-05-25 13:14:52 +00:00
|
|
|
sync.Mutex
|
2017-10-18 20:17:40 +00:00
|
|
|
nodeReady chan struct{} // channel to wait for when node is fully ready
|
|
|
|
nodeManager common.NodeManager
|
|
|
|
accountManager common.AccountManager
|
2018-01-22 09:31:24 +00:00
|
|
|
txQueueManager *transactions.Manager
|
2017-10-18 20:17:40 +00:00
|
|
|
jailManager common.JailManager
|
|
|
|
newNotification common.NotificationConstructor
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewStatusBackend create a new NewStatusBackend instance
|
|
|
|
func NewStatusBackend() *StatusBackend {
|
|
|
|
defer log.Info("Status backend initialized")
|
|
|
|
|
|
|
|
nodeManager := node.NewNodeManager()
|
2017-09-26 13:44:26 +00:00
|
|
|
accountManager := account.NewManager(nodeManager)
|
2018-01-26 05:59:21 +00:00
|
|
|
txQueueManager := transactions.NewManager(nodeManager, accountManager)
|
2017-10-06 16:52:26 +00:00
|
|
|
jailManager := jail.New(nodeManager)
|
2017-10-18 20:56:39 +00:00
|
|
|
notificationManager := fcm.NewNotification(fcmServerKey)
|
2017-08-15 10:27:12 +00:00
|
|
|
|
2017-05-16 12:09:52 +00:00
|
|
|
return &StatusBackend{
|
2017-10-18 20:17:40 +00:00
|
|
|
nodeManager: nodeManager,
|
|
|
|
accountManager: accountManager,
|
|
|
|
jailManager: jailManager,
|
|
|
|
txQueueManager: txQueueManager,
|
|
|
|
newNotification: notificationManager,
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NodeManager returns reference to node manager
|
|
|
|
func (m *StatusBackend) NodeManager() common.NodeManager {
|
|
|
|
return m.nodeManager
|
|
|
|
}
|
|
|
|
|
|
|
|
// AccountManager returns reference to account manager
|
|
|
|
func (m *StatusBackend) AccountManager() common.AccountManager {
|
|
|
|
return m.accountManager
|
|
|
|
}
|
|
|
|
|
|
|
|
// JailManager returns reference to jail
|
|
|
|
func (m *StatusBackend) JailManager() common.JailManager {
|
|
|
|
return m.jailManager
|
|
|
|
}
|
|
|
|
|
2018-01-22 09:31:24 +00:00
|
|
|
// TxQueueManager returns reference to transactions manager
|
|
|
|
func (m *StatusBackend) TxQueueManager() *transactions.Manager {
|
2017-09-04 12:56:58 +00:00
|
|
|
return m.txQueueManager
|
|
|
|
}
|
|
|
|
|
2017-05-16 12:09:52 +00:00
|
|
|
// IsNodeRunning confirm that node is running
|
|
|
|
func (m *StatusBackend) IsNodeRunning() bool {
|
|
|
|
return m.nodeManager.IsNodeRunning()
|
|
|
|
}
|
|
|
|
|
|
|
|
// StartNode start Status node, fails if node is already started
|
|
|
|
func (m *StatusBackend) StartNode(config *params.NodeConfig) (<-chan struct{}, error) {
|
2017-05-25 13:14:52 +00:00
|
|
|
m.Lock()
|
|
|
|
defer m.Unlock()
|
|
|
|
|
|
|
|
if m.nodeReady != nil {
|
|
|
|
return nil, node.ErrNodeExists
|
|
|
|
}
|
|
|
|
|
2017-05-16 12:09:52 +00:00
|
|
|
nodeStarted, err := m.nodeManager.StartNode(config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-05-25 13:14:52 +00:00
|
|
|
m.nodeReady = make(chan struct{}, 1)
|
|
|
|
go m.onNodeStart(nodeStarted, m.nodeReady) // waits on nodeStarted, writes to backendReady
|
|
|
|
|
|
|
|
return m.nodeReady, err
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
2017-05-25 13:14:52 +00:00
|
|
|
// onNodeStart does everything required to prepare backend
|
|
|
|
func (m *StatusBackend) onNodeStart(nodeStarted <-chan struct{}, backendReady chan struct{}) {
|
2017-05-16 12:09:52 +00:00
|
|
|
<-nodeStarted
|
|
|
|
|
2018-01-18 16:55:17 +00:00
|
|
|
// tx queue manager should be started after node is started, it depends
|
|
|
|
// on rpc client being created
|
|
|
|
m.txQueueManager.Start()
|
|
|
|
|
2017-05-16 12:09:52 +00:00
|
|
|
if err := m.registerHandlers(); err != nil {
|
|
|
|
log.Error("Handler registration failed", "err", err)
|
|
|
|
}
|
|
|
|
|
2017-10-20 09:06:22 +00:00
|
|
|
if err := m.accountManager.ReSelectAccount(); err != nil {
|
|
|
|
log.Error("Reselect account failed", "err", err)
|
|
|
|
}
|
2017-05-25 13:14:52 +00:00
|
|
|
log.Info("Account reselected")
|
2017-05-16 12:09:52 +00:00
|
|
|
|
2017-05-25 13:14:52 +00:00
|
|
|
close(backendReady)
|
2017-09-25 18:22:57 +00:00
|
|
|
signal.Send(signal.Envelope{
|
|
|
|
Type: signal.EventNodeReady,
|
2017-05-25 13:14:52 +00:00
|
|
|
Event: struct{}{},
|
|
|
|
})
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// StopNode stop Status node. Stopped node cannot be resumed.
|
2017-05-25 13:14:52 +00:00
|
|
|
func (m *StatusBackend) StopNode() (<-chan struct{}, error) {
|
|
|
|
m.Lock()
|
|
|
|
defer m.Unlock()
|
2017-05-16 12:09:52 +00:00
|
|
|
|
2017-05-25 13:14:52 +00:00
|
|
|
if m.nodeReady == nil {
|
|
|
|
return nil, node.ErrNoRunningNode
|
|
|
|
}
|
|
|
|
<-m.nodeReady
|
|
|
|
|
2017-10-11 14:20:51 +00:00
|
|
|
m.txQueueManager.Stop()
|
|
|
|
m.jailManager.Stop()
|
|
|
|
|
2017-05-25 13:14:52 +00:00
|
|
|
nodeStopped, err := m.nodeManager.StopNode()
|
2017-05-16 12:09:52 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-05-25 13:14:52 +00:00
|
|
|
backendStopped := make(chan struct{}, 1)
|
|
|
|
go func() {
|
|
|
|
<-nodeStopped
|
|
|
|
m.Lock()
|
|
|
|
m.nodeReady = nil
|
|
|
|
m.Unlock()
|
|
|
|
close(backendStopped)
|
|
|
|
}()
|
2017-05-16 12:09:52 +00:00
|
|
|
|
2017-05-25 13:14:52 +00:00
|
|
|
return backendStopped, nil
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
2017-05-25 13:14:52 +00:00
|
|
|
// RestartNode restart running Status node, fails if node is not running
|
|
|
|
func (m *StatusBackend) RestartNode() (<-chan struct{}, error) {
|
|
|
|
m.Lock()
|
|
|
|
defer m.Unlock()
|
2017-05-16 12:09:52 +00:00
|
|
|
|
2017-05-25 13:14:52 +00:00
|
|
|
if m.nodeReady == nil {
|
|
|
|
return nil, node.ErrNoRunningNode
|
|
|
|
}
|
|
|
|
<-m.nodeReady
|
2017-05-16 12:09:52 +00:00
|
|
|
|
2017-05-25 13:14:52 +00:00
|
|
|
nodeRestarted, err := m.nodeManager.RestartNode()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2017-05-16 12:09:52 +00:00
|
|
|
|
2017-05-25 13:14:52 +00:00
|
|
|
m.nodeReady = make(chan struct{}, 1)
|
|
|
|
go m.onNodeStart(nodeRestarted, m.nodeReady) // waits on nodeRestarted, writes to backendReady
|
2017-05-16 12:09:52 +00:00
|
|
|
|
2017-05-25 13:14:52 +00:00
|
|
|
return m.nodeReady, err
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
2017-05-25 13:14:52 +00:00
|
|
|
// ResetChainData remove chain data from data directory.
|
|
|
|
// Node is stopped, and new node is started, with clean data directory.
|
|
|
|
func (m *StatusBackend) ResetChainData() (<-chan struct{}, error) {
|
|
|
|
m.Lock()
|
|
|
|
defer m.Unlock()
|
|
|
|
|
|
|
|
if m.nodeReady == nil {
|
|
|
|
return nil, node.ErrNoRunningNode
|
|
|
|
}
|
|
|
|
<-m.nodeReady
|
2017-05-16 12:09:52 +00:00
|
|
|
|
2017-05-25 13:14:52 +00:00
|
|
|
nodeReset, err := m.nodeManager.ResetChainData()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
m.nodeReady = make(chan struct{}, 1)
|
|
|
|
go m.onNodeStart(nodeReset, m.nodeReady) // waits on nodeReset, writes to backendReady
|
|
|
|
|
|
|
|
return m.nodeReady, err
|
2017-05-16 12:09:52 +00:00
|
|
|
}
|
|
|
|
|
2017-05-28 13:57:30 +00:00
|
|
|
// CallRPC executes RPC request on node's in-proc RPC server
|
|
|
|
func (m *StatusBackend) CallRPC(inputJSON string) string {
|
2017-09-14 20:14:31 +00:00
|
|
|
client := m.nodeManager.RPCClient()
|
|
|
|
return client.CallRaw(inputJSON)
|
2017-05-28 13:57:30 +00:00
|
|
|
}
|
|
|
|
|
2017-09-04 12:56:58 +00:00
|
|
|
// SendTransaction creates a new transaction and waits until it's complete.
|
2018-01-05 20:58:17 +00:00
|
|
|
func (m *StatusBackend) SendTransaction(ctx context.Context, args common.SendTxArgs) (hash gethcommon.Hash, err error) {
|
2017-09-04 12:56:58 +00:00
|
|
|
if ctx == nil {
|
|
|
|
ctx = context.Background()
|
|
|
|
}
|
2018-01-26 05:59:21 +00:00
|
|
|
tx := common.CreateTransaction(ctx, args)
|
2018-01-05 20:58:17 +00:00
|
|
|
if err = m.txQueueManager.QueueTransaction(tx); err != nil {
|
|
|
|
return hash, err
|
2017-09-04 12:56:58 +00:00
|
|
|
}
|
2018-01-05 20:58:17 +00:00
|
|
|
rst := m.txQueueManager.WaitForTransaction(tx)
|
|
|
|
if rst.Error != nil {
|
|
|
|
return hash, rst.Error
|
2017-09-04 12:56:58 +00:00
|
|
|
}
|
2018-01-05 20:58:17 +00:00
|
|
|
return rst.Hash, nil
|
2017-09-04 12:56:58 +00:00
|
|
|
}
|
|
|
|
|
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
|
|
|
func (m *StatusBackend) CompleteTransaction(id common.QueuedTxID, password string) (gethcommon.Hash, error) {
|
2017-05-16 12:09:52 +00:00
|
|
|
return m.txQueueManager.CompleteTransaction(id, password)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CompleteTransactions instructs backend to complete sending of multiple transactions
|
2018-01-05 20:58:17 +00:00
|
|
|
func (m *StatusBackend) CompleteTransactions(ids []common.QueuedTxID, password string) map[common.QueuedTxID]common.TransactionResult {
|
2017-05-16 12:09:52 +00:00
|
|
|
return m.txQueueManager.CompleteTransactions(ids, password)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DiscardTransaction discards a given transaction from transaction queue
|
2017-09-04 12:56:58 +00:00
|
|
|
func (m *StatusBackend) DiscardTransaction(id common.QueuedTxID) error {
|
2017-05-16 12:09:52 +00:00
|
|
|
return m.txQueueManager.DiscardTransaction(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DiscardTransactions discards given multiple transactions from transaction queue
|
2017-09-04 12:56:58 +00:00
|
|
|
func (m *StatusBackend) DiscardTransactions(ids []common.QueuedTxID) map[common.QueuedTxID]common.RawDiscardTransactionResult {
|
2017-05-16 12:09:52 +00:00
|
|
|
return m.txQueueManager.DiscardTransactions(ids)
|
|
|
|
}
|
|
|
|
|
|
|
|
// registerHandlers attaches Status callback handlers to running node
|
|
|
|
func (m *StatusBackend) registerHandlers() error {
|
2017-09-25 16:04:40 +00:00
|
|
|
rpcClient := m.NodeManager().RPCClient()
|
2017-10-26 13:25:38 +00:00
|
|
|
if rpcClient == nil {
|
|
|
|
return node.ErrRPCClient
|
|
|
|
}
|
|
|
|
|
2017-09-25 16:04:40 +00:00
|
|
|
rpcClient.RegisterHandler("eth_accounts", m.accountManager.AccountsRPCHandler())
|
|
|
|
rpcClient.RegisterHandler("eth_sendTransaction", m.txQueueManager.SendTransactionRPCHandler)
|
2017-05-16 12:09:52 +00:00
|
|
|
return nil
|
|
|
|
}
|