2016-08-31 18:02:06 +00:00
|
|
|
package ethapi
|
|
|
|
|
|
|
|
import (
|
2017-02-23 00:22:43 +00:00
|
|
|
"errors"
|
|
|
|
"math/big"
|
|
|
|
"time"
|
2016-09-27 13:51:08 +00:00
|
|
|
|
2017-02-27 12:40:42 +00:00
|
|
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
2016-08-31 18:02:06 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common"
|
2017-02-23 00:22:43 +00:00
|
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
2016-08-31 18:02:06 +00:00
|
|
|
"github.com/ethereum/go-ethereum/les/status"
|
|
|
|
"github.com/ethereum/go-ethereum/logger"
|
|
|
|
"github.com/ethereum/go-ethereum/logger/glog"
|
2017-02-23 00:22:43 +00:00
|
|
|
"github.com/pborman/uuid"
|
2016-08-31 18:02:06 +00:00
|
|
|
"golang.org/x/net/context"
|
|
|
|
)
|
|
|
|
|
2016-09-27 13:51:08 +00:00
|
|
|
// StatusBackend exposes Ethereum internals to support custom semantics in status-go bindings
|
2016-08-31 18:02:06 +00:00
|
|
|
type StatusBackend struct {
|
|
|
|
eapi *PublicEthereumAPI // Wrapper around the Ethereum object to access metadata
|
|
|
|
bcapi *PublicBlockChainAPI // Wrapper around the blockchain to access chain data
|
|
|
|
txapi *PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data
|
|
|
|
|
|
|
|
txQueue *status.TxQueue
|
2016-09-27 13:51:08 +00:00
|
|
|
am *status.AccountManager
|
2016-08-31 18:02:06 +00:00
|
|
|
}
|
|
|
|
|
2017-02-23 00:22:43 +00:00
|
|
|
var (
|
|
|
|
ErrStatusBackendNotInited = errors.New("StatusIM backend is not properly inited")
|
|
|
|
)
|
2016-09-27 13:51:08 +00:00
|
|
|
|
2016-08-31 18:02:06 +00:00
|
|
|
// NewStatusBackend creates a new backend using an existing Ethereum object.
|
|
|
|
func NewStatusBackend(apiBackend Backend) *StatusBackend {
|
2017-02-23 00:22:43 +00:00
|
|
|
glog.V(logger.Info).Infof("StatusIM: backend service inited")
|
|
|
|
return &StatusBackend{
|
|
|
|
eapi: NewPublicEthereumAPI(apiBackend),
|
|
|
|
bcapi: NewPublicBlockChainAPI(apiBackend),
|
|
|
|
txapi: NewPublicTransactionPoolAPI(apiBackend),
|
|
|
|
txQueue: status.NewTransactionQueue(),
|
|
|
|
am: status.NewAccountManager(apiBackend.AccountManager()),
|
|
|
|
}
|
|
|
|
}
|
2016-09-27 13:51:08 +00:00
|
|
|
|
2017-02-23 00:22:43 +00:00
|
|
|
func (b *StatusBackend) Start() {
|
|
|
|
glog.V(logger.Info).Infof("StatusIM: started as LES sub-protocol")
|
|
|
|
b.txQueue.Start()
|
2016-09-27 13:51:08 +00:00
|
|
|
}
|
2016-08-31 18:02:06 +00:00
|
|
|
|
2017-02-23 00:22:43 +00:00
|
|
|
func (b *StatusBackend) Stop() {
|
|
|
|
glog.V(logger.Info).Infof("StatusIM: stopped as LES sub-protocol")
|
|
|
|
b.txQueue.Stop()
|
2016-08-31 18:02:06 +00:00
|
|
|
}
|
|
|
|
|
2016-10-30 22:35:10 +00:00
|
|
|
func (b *StatusBackend) NotifyOnQueuedTxReturn(queuedTx *status.QueuedTx, err error) {
|
2016-10-26 02:51:33 +00:00
|
|
|
if b == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2016-10-30 22:35:10 +00:00
|
|
|
b.txQueue.NotifyOnQueuedTxReturn(queuedTx, err)
|
2016-10-26 02:51:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b *StatusBackend) SetTransactionReturnHandler(fn status.EnqueuedTxReturnHandler) {
|
|
|
|
b.txQueue.SetTxReturnHandler(fn)
|
|
|
|
}
|
|
|
|
|
2016-08-31 18:02:06 +00:00
|
|
|
func (b *StatusBackend) SetTransactionQueueHandler(fn status.EnqueuedTxHandler) {
|
|
|
|
b.txQueue.SetEnqueueHandler(fn)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *StatusBackend) TransactionQueue() *status.TxQueue {
|
|
|
|
return b.txQueue
|
|
|
|
}
|
|
|
|
|
2016-09-27 13:51:08 +00:00
|
|
|
func (b *StatusBackend) SetAccountsFilterHandler(fn status.AccountsFilterHandler) {
|
|
|
|
b.am.SetAccountsFilterHandler(fn)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (b *StatusBackend) AccountManager() *status.AccountManager {
|
|
|
|
return b.am
|
|
|
|
}
|
|
|
|
|
2016-08-31 18:02:06 +00:00
|
|
|
// SendTransaction wraps call to PublicTransactionPoolAPI.SendTransaction
|
|
|
|
func (b *StatusBackend) SendTransaction(ctx context.Context, args status.SendTxArgs) (common.Hash, error) {
|
|
|
|
if ctx == nil {
|
|
|
|
ctx = context.Background()
|
|
|
|
}
|
|
|
|
|
2017-02-23 00:22:43 +00:00
|
|
|
if estimatedGas, err := b.EstimateGas(ctx, args); err == nil {
|
|
|
|
if estimatedGas.ToInt().Cmp(big.NewInt(defaultGas)) == 1 { // gas > defaultGas
|
|
|
|
args.Gas = estimatedGas
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
queuedTx := &status.QueuedTx{
|
|
|
|
Id: status.QueuedTxId(uuid.New()),
|
|
|
|
Hash: common.Hash{},
|
|
|
|
Context: ctx,
|
|
|
|
Args: status.SendTxArgs(args),
|
|
|
|
Done: make(chan struct{}, 1),
|
|
|
|
Discard: make(chan struct{}, 1),
|
|
|
|
}
|
|
|
|
|
|
|
|
// send transaction to pending pool, w/o blocking
|
|
|
|
b.txQueue.EnqueueAsync(queuedTx)
|
|
|
|
|
|
|
|
// now wait up until transaction is:
|
|
|
|
// - completed (via CompleteQueuedTransaction),
|
|
|
|
// - discarded (via DiscardQueuedTransaction)
|
|
|
|
// - or times out
|
|
|
|
select {
|
|
|
|
case <-queuedTx.Done:
|
|
|
|
b.NotifyOnQueuedTxReturn(queuedTx, queuedTx.Err)
|
|
|
|
return queuedTx.Hash, queuedTx.Err
|
|
|
|
case <-queuedTx.Discard:
|
|
|
|
b.NotifyOnQueuedTxReturn(queuedTx, status.ErrQueuedTxDiscarded)
|
|
|
|
return queuedTx.Hash, queuedTx.Err
|
|
|
|
case <-time.After(status.DefaultTxSendCompletionTimeout * time.Second):
|
|
|
|
b.NotifyOnQueuedTxReturn(queuedTx, status.ErrQueuedTxTimedOut)
|
|
|
|
return common.Hash{}, status.ErrQueuedTxTimedOut
|
|
|
|
}
|
|
|
|
|
|
|
|
return queuedTx.Hash, nil
|
2016-08-31 18:02:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// CompleteQueuedTransaction wraps call to PublicTransactionPoolAPI.CompleteQueuedTransaction
|
2016-12-15 09:49:05 +00:00
|
|
|
func (b *StatusBackend) CompleteQueuedTransaction(ctx context.Context, id status.QueuedTxId, passphrase string) (common.Hash, error) {
|
2016-08-31 18:02:06 +00:00
|
|
|
queuedTx, err := b.txQueue.Get(id)
|
|
|
|
if err != nil {
|
|
|
|
return common.Hash{}, err
|
|
|
|
}
|
|
|
|
|
2016-12-15 09:49:05 +00:00
|
|
|
hash, err := b.txapi.CompleteQueuedTransaction(ctx, SendTxArgs(queuedTx.Args), passphrase)
|
2016-10-26 02:51:33 +00:00
|
|
|
|
|
|
|
// on password error, notify the app, and keep tx in queue (so that CompleteQueuedTransaction() can be resent)
|
2017-02-27 12:40:42 +00:00
|
|
|
if err == keystore.ErrDecrypt {
|
2016-10-30 22:35:10 +00:00
|
|
|
b.NotifyOnQueuedTxReturn(queuedTx, err)
|
2016-10-26 02:51:33 +00:00
|
|
|
return hash, err // SendTransaction is still blocked
|
|
|
|
}
|
|
|
|
|
2016-12-15 09:49:05 +00:00
|
|
|
// when incorrect sender tries to complete the account, notify and keep tx in queue (so that correct sender can complete)
|
|
|
|
if err == status.ErrInvalidCompleteTxSender {
|
|
|
|
b.NotifyOnQueuedTxReturn(queuedTx, err)
|
|
|
|
return hash, err // SendTransaction is still blocked
|
|
|
|
}
|
|
|
|
|
2016-10-26 02:51:33 +00:00
|
|
|
// allow SendTransaction to return
|
2016-08-31 18:02:06 +00:00
|
|
|
queuedTx.Hash = hash
|
|
|
|
queuedTx.Err = err
|
|
|
|
queuedTx.Done <- struct{}{} // sendTransaction() waits on this, notify so that it can return
|
|
|
|
|
|
|
|
return hash, err
|
|
|
|
}
|
|
|
|
|
2016-10-30 22:35:10 +00:00
|
|
|
// DiscardQueuedTransaction discards queued transaction forcing SendTransaction to return
|
|
|
|
func (b *StatusBackend) DiscardQueuedTransaction(id status.QueuedTxId) error {
|
|
|
|
queuedTx, err := b.txQueue.Get(id)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove from queue, before notifying SendTransaction
|
|
|
|
b.TransactionQueue().Remove(queuedTx.Id)
|
|
|
|
|
|
|
|
// allow SendTransaction to return
|
|
|
|
queuedTx.Err = status.ErrQueuedTxDiscarded
|
|
|
|
queuedTx.Discard <- struct{}{} // sendTransaction() waits on this, notify so that it can return
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-02-23 00:22:43 +00:00
|
|
|
// EstimateGas uses underlying blockchain API to obtain gas for a given tx arguments
|
|
|
|
func (b *StatusBackend) EstimateGas(ctx context.Context, args status.SendTxArgs) (*hexutil.Big, error) {
|
|
|
|
if args.Gas != nil {
|
|
|
|
return args.Gas, nil
|
2016-08-31 18:02:06 +00:00
|
|
|
}
|
|
|
|
|
2017-02-23 00:22:43 +00:00
|
|
|
var gasPrice hexutil.Big
|
|
|
|
if args.GasPrice != nil {
|
|
|
|
gasPrice = *args.GasPrice
|
2016-08-31 18:02:06 +00:00
|
|
|
}
|
2017-02-23 00:22:43 +00:00
|
|
|
|
|
|
|
var value hexutil.Big
|
|
|
|
if args.Value != nil {
|
|
|
|
value = *args.Value
|
|
|
|
}
|
|
|
|
|
|
|
|
callArgs := CallArgs{
|
|
|
|
From: args.From,
|
|
|
|
To: args.To,
|
|
|
|
GasPrice: gasPrice,
|
|
|
|
Value: value,
|
|
|
|
Data: args.Data,
|
|
|
|
}
|
|
|
|
|
|
|
|
return b.bcapi.EstimateGas(ctx, callArgs)
|
2016-08-31 18:02:06 +00:00
|
|
|
}
|