status-go/geth/node/txqueue.go

195 lines
5.7 KiB
Go

package node
import (
"context"
"github.com/ethereum/go-ethereum/accounts/keystore"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/les/status"
"github.com/status-im/status-go/geth/common"
)
const (
// EventTransactionQueued is triggered when send transaction request is queued
EventTransactionQueued = "transaction.queued"
// EventTransactionFailed is triggered when send transaction request fails
EventTransactionFailed = "transaction.failed"
// SendTxDefaultErrorCode is sent by default, when error is not nil, but type is unknown/unexpected.
SendTxDefaultErrorCode = SendTransactionDefaultErrorCode
)
// Send transaction response codes
const (
SendTransactionNoErrorCode = "0"
SendTransactionDefaultErrorCode = "1"
SendTransactionPasswordErrorCode = "2"
SendTransactionTimeoutErrorCode = "3"
SendTransactionDiscardedErrorCode = "4"
)
var txReturnCodes = map[error]string{ // deliberately strings, in case more meaningful codes are to be returned
nil: SendTransactionNoErrorCode,
keystore.ErrDecrypt: SendTransactionPasswordErrorCode,
status.ErrQueuedTxTimedOut: SendTransactionTimeoutErrorCode,
status.ErrQueuedTxDiscarded: SendTransactionDiscardedErrorCode,
}
// TxQueueManager provides means to manage internal Status Backend (injected into LES)
type TxQueueManager struct {
nodeManager common.NodeManager
accountManager common.AccountManager
}
func NewTxQueueManager(nodeManager common.NodeManager, accountManager common.AccountManager) *TxQueueManager {
return &TxQueueManager{
nodeManager: nodeManager,
accountManager: accountManager,
}
}
// CompleteTransaction instructs backend to complete sending of a given transaction
func (m *TxQueueManager) CompleteTransaction(id, password string) (gethcommon.Hash, error) {
lightEthereum, err := m.nodeManager.LightEthereumService()
if err != nil {
return gethcommon.Hash{}, err
}
backend := lightEthereum.StatusBackend
selectedAccount, err := m.accountManager.SelectedAccount()
if err != nil {
return gethcommon.Hash{}, err
}
ctx := context.Background()
ctx = context.WithValue(ctx, status.SelectedAccountKey, selectedAccount.Hex())
return backend.CompleteQueuedTransaction(ctx, status.QueuedTxID(id), password)
}
// CompleteTransactions instructs backend to complete sending of multiple transactions
func (m *TxQueueManager) CompleteTransactions(ids, password string) map[string]common.RawCompleteTransactionResult {
results := make(map[string]common.RawCompleteTransactionResult)
parsedIDs, err := common.ParseJSONArray(ids)
if err != nil {
results["none"] = common.RawCompleteTransactionResult{
Error: err,
}
return results
}
for _, txID := range parsedIDs {
txHash, txErr := m.CompleteTransaction(txID, password)
results[txID] = common.RawCompleteTransactionResult{
Hash: txHash,
Error: txErr,
}
}
return results
}
// DiscardTransaction discards a given transaction from transaction queue
func (m *TxQueueManager) DiscardTransaction(id string) error {
lightEthereum, err := m.nodeManager.LightEthereumService()
if err != nil {
return err
}
backend := lightEthereum.StatusBackend
return backend.DiscardQueuedTransaction(status.QueuedTxID(id))
}
// DiscardTransactions discards given multiple transactions from transaction queue
func (m *TxQueueManager) DiscardTransactions(ids string) map[string]common.RawDiscardTransactionResult {
var parsedIDs []string
results := make(map[string]common.RawDiscardTransactionResult)
parsedIDs, err := common.ParseJSONArray(ids)
if err != nil {
results["none"] = common.RawDiscardTransactionResult{
Error: err,
}
return results
}
for _, txID := range parsedIDs {
err := m.DiscardTransaction(txID)
if err != nil {
results[txID] = common.RawDiscardTransactionResult{
Error: err,
}
}
}
return results
}
// SendTransactionEvent is a signal sent on a send transaction request
type SendTransactionEvent struct {
ID string `json:"id"`
Args status.SendTxArgs `json:"args"`
MessageID string `json:"message_id"`
}
// TransactionQueueHandler returns handler that processes incoming tx queue requests
func (m *TxQueueManager) TransactionQueueHandler() func(queuedTx status.QueuedTx) {
return func(queuedTx status.QueuedTx) {
SendSignal(SignalEnvelope{
Type: EventTransactionQueued,
Event: SendTransactionEvent{
ID: string(queuedTx.ID),
Args: queuedTx.Args,
MessageID: common.MessageIDFromContext(queuedTx.Context),
},
})
}
}
// ReturnSendTransactionEvent is a JSON returned whenever transaction send is returned
type ReturnSendTransactionEvent struct {
ID string `json:"id"`
Args status.SendTxArgs `json:"args"`
MessageID string `json:"message_id"`
ErrorMessage string `json:"error_message"`
ErrorCode string `json:"error_code"`
}
// TransactionReturnHandler returns handler that processes responses from internal tx manager
func (m *TxQueueManager) TransactionReturnHandler() func(queuedTx *status.QueuedTx, err error) {
return func(queuedTx *status.QueuedTx, err error) {
if err == nil {
return
}
// discard notifications with empty tx
if queuedTx == nil {
return
}
// error occurred, signal up to application
SendSignal(SignalEnvelope{
Type: EventTransactionFailed,
Event: ReturnSendTransactionEvent{
ID: string(queuedTx.ID),
Args: queuedTx.Args,
MessageID: common.MessageIDFromContext(queuedTx.Context),
ErrorMessage: err.Error(),
ErrorCode: m.sendTransactionErrorCode(err),
},
})
}
}
func (m *TxQueueManager) sendTransactionErrorCode(err error) string {
if code, ok := txReturnCodes[err]; ok {
return code
}
return SendTxDefaultErrorCode
}