status-go/geth-patches/0002-les-api-status.patch

382 lines
12 KiB
Diff

diff --git a/eth/api_backend.go b/eth/api_backend.go
index 91f392f94..471275de5 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -32,14 +32,20 @@ import (
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
// EthApiBackend implements ethapi.Backend for full nodes
type EthApiBackend struct {
- eth *Ethereum
- gpo *gasprice.Oracle
+ eth *Ethereum
+ gpo *gasprice.Oracle
+ statusBackend *ethapi.StatusBackend
+}
+
+func (b *EthApiBackend) GetStatusBackend() *ethapi.StatusBackend {
+ return b.statusBackend
}
func (b *EthApiBackend) ChainConfig() *params.ChainConfig {
diff --git a/eth/backend.go b/eth/backend.go
index 1cd9e8fff..2fbdec4e2 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -169,7 +169,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.engine)
eth.miner.SetExtra(makeExtraData(config.ExtraData))
- eth.ApiBackend = &EthApiBackend{eth, nil}
+ eth.ApiBackend = &EthApiBackend{eth, nil, nil}
gpoParams := config.GPO
if gpoParams.Default == nil {
gpoParams.Default = config.GasPrice
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 025f42617..d8f48a890 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -178,15 +178,24 @@ func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string]string {
// It offers only methods that can retrieve accounts.
type PublicAccountAPI struct {
am *accounts.Manager
+ b Backend
}
// NewPublicAccountAPI creates a new PublicAccountAPI.
-func NewPublicAccountAPI(am *accounts.Manager) *PublicAccountAPI {
- return &PublicAccountAPI{am: am}
+func NewPublicAccountAPI(b Backend) *PublicAccountAPI {
+ return &PublicAccountAPI{
+ am: b.AccountManager(),
+ b: b,
+ }
}
// Accounts returns the collection of accounts this node manages
func (s *PublicAccountAPI) Accounts() []common.Address {
+ backend := s.b.GetStatusBackend()
+ if backend != nil {
+ return backend.am.Accounts()
+ }
+
addresses := make([]common.Address, 0) // return [] instead of nil if empty
for _, wallet := range s.am.Wallets() {
for _, account := range wallet.Accounts() {
@@ -216,6 +225,11 @@ func NewPrivateAccountAPI(b Backend, nonceLock *AddrLocker) *PrivateAccountAPI {
// ListAccounts will return a list of addresses for accounts this node manages.
func (s *PrivateAccountAPI) ListAccounts() []common.Address {
+ backend := s.b.GetStatusBackend()
+ if backend != nil {
+ return backend.am.Accounts()
+ }
+
addresses := make([]common.Address, 0) // return [] instead of nil if empty
for _, wallet := range s.am.Wallets() {
for _, account := range wallet.Accounts() {
@@ -1122,10 +1136,46 @@ func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (c
return tx.Hash(), nil
}
-// SendTransaction creates a transaction for the given argument, sign it and submit it to the
+// SendTransactionWithPassphrase creates a transaction by unpacking queued transaction, signs it and submits to the
// transaction pool.
-func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) {
+// @Status
+func (s *PublicTransactionPoolAPI) SendTransactionWithPassphrase(ctx context.Context, args SendTxArgs, passphrase string) (common.Hash, error) {
+ // Look up the wallet containing the requested signer
+ account := accounts.Account{Address: args.From}
+
+ wallet, err := s.b.AccountManager().Find(account)
+ if err != nil {
+ return common.Hash{}, err
+ }
+
+ if args.Nonce == nil {
+ // Hold the addresse's mutex around signing to prevent concurrent assignment of
+ // the same nonce to multiple accounts.
+ s.nonceLock.LockAddr(args.From)
+ defer s.nonceLock.UnlockAddr(args.From)
+ }
+ // Set some sanity defaults and terminate on failure
+ if err := args.setDefaults(ctx, s.b); err != nil {
+ return common.Hash{}, err
+ }
+ // Assemble the transaction and sign with the wallet
+ tx := args.toTransaction()
+
+ var chainID *big.Int
+ if config := s.b.ChainConfig(); config.IsEIP155(s.b.CurrentBlock().Number()) {
+ chainID = config.ChainId
+ }
+ signed, err := wallet.SignTxWithPassphrase(account, passphrase, tx, chainID)
+ if err != nil {
+ return common.Hash{}, err
+ }
+ return submitTransaction(ctx, s.b, signed)
+}
+
+// SendTransaction creates a transaction by unpacking queued transaction, signs it and submits to the
+// transaction pool.
+func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) {
// Look up the wallet containing the requested signer
account := accounts.Account{Address: args.From}
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index 368fa4872..cac58dfc0 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -68,6 +68,8 @@ type Backend interface {
ChainConfig() *params.ChainConfig
CurrentBlock() *types.Block
+
+ GetStatusBackend() *StatusBackend
}
func GetAPIs(apiBackend Backend) []rpc.API {
@@ -105,7 +107,7 @@ func GetAPIs(apiBackend Backend) []rpc.API {
}, {
Namespace: "eth",
Version: "1.0",
- Service: NewPublicAccountAPI(apiBackend.AccountManager()),
+ Service: NewPublicAccountAPI(apiBackend),
Public: true,
}, {
Namespace: "personal",
diff --git a/internal/ethapi/status_backend.go b/internal/ethapi/status_backend.go
new file mode 100644
index 000000000..c4e553cae
--- /dev/null
+++ b/internal/ethapi/status_backend.go
@@ -0,0 +1,88 @@
+package ethapi
+
+import (
+ "context"
+ "errors"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/les/status"
+ "github.com/ethereum/go-ethereum/log"
+)
+
+// StatusBackend exposes Ethereum internals to support custom semantics in status-go bindings
+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
+
+ am *status.AccountManager
+}
+
+var (
+ ErrStatusBackendNotInited = errors.New("StatusIM backend is not properly inited")
+)
+
+// NewStatusBackend creates a new backend using an existing Ethereum object.
+func NewStatusBackend(apiBackend Backend) *StatusBackend {
+ log.Info("StatusIM: backend service inited")
+ return &StatusBackend{
+ eapi: NewPublicEthereumAPI(apiBackend),
+ bcapi: NewPublicBlockChainAPI(apiBackend),
+ txapi: NewPublicTransactionPoolAPI(apiBackend, new(AddrLocker)),
+ am: status.NewAccountManager(apiBackend.AccountManager()),
+ }
+}
+
+// SetAccountsFilterHandler sets a callback that is triggered when account list is requested
+func (b *StatusBackend) SetAccountsFilterHandler(fn status.AccountsFilterHandler) {
+ b.am.SetAccountsFilterHandler(fn)
+}
+
+// AccountManager returns reference to account manager
+func (b *StatusBackend) AccountManager() *status.AccountManager {
+ return b.am
+}
+
+// SendTransaction wraps call to PublicTransactionPoolAPI.SendTransactionWithPassphrase
+func (b *StatusBackend) SendTransaction(ctx context.Context, args status.SendTxArgs, passphrase string) (common.Hash, error) {
+ if ctx == nil {
+ ctx = context.Background()
+ }
+
+ if estimatedGas, err := b.EstimateGas(ctx, args); err == nil {
+ if estimatedGas.ToInt().Cmp(big.NewInt(defaultGas)) == 1 { // gas > defaultGas
+ args.Gas = estimatedGas
+ }
+ }
+
+ return b.txapi.SendTransactionWithPassphrase(ctx, SendTxArgs(args), passphrase)
+}
+
+// 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
+ }
+
+ var gasPrice hexutil.Big
+ if args.GasPrice != nil {
+ gasPrice = *args.GasPrice
+ }
+
+ 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)
+}
diff --git a/les/api_backend.go b/les/api_backend.go
index 56f617a7d..f839f24e6 100644
--- a/les/api_backend.go
+++ b/les/api_backend.go
@@ -32,14 +32,20 @@ import (
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
)
type LesApiBackend struct {
- eth *LightEthereum
- gpo *gasprice.Oracle
+ eth *LightEthereum
+ gpo *gasprice.Oracle
+ statusBackend *ethapi.StatusBackend
+}
+
+func (b *LesApiBackend) GetStatusBackend() *ethapi.StatusBackend {
+ return b.statusBackend
}
func (b *LesApiBackend) ChainConfig() *params.ChainConfig {
diff --git a/les/backend.go b/les/backend.go
index 333df920e..7d8cf3916 100644
--- a/les/backend.go
+++ b/les/backend.go
@@ -75,6 +75,8 @@ type LightEthereum struct {
netRPCService *ethapi.PublicNetAPI
wg sync.WaitGroup
+
+ StatusBackend *ethapi.StatusBackend
}
func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
@@ -126,12 +128,17 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
if leth.protocolManager, err = NewProtocolManager(leth.chainConfig, true, ClientProtocolVersions, config.NetworkId, leth.eventMux, leth.engine, leth.peers, leth.blockchain, nil, chainDb, leth.odr, leth.relay, quitSync, &leth.wg); err != nil {
return nil, err
}
- leth.ApiBackend = &LesApiBackend{leth, nil}
+ leth.ApiBackend = &LesApiBackend{leth, nil, nil}
gpoParams := config.GPO
if gpoParams.Default == nil {
gpoParams.Default = config.GasPrice
}
leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, gpoParams)
+
+ // inject status-im backend
+ leth.ApiBackend.statusBackend = ethapi.NewStatusBackend(leth.ApiBackend)
+ leth.StatusBackend = leth.ApiBackend.statusBackend // alias
+
return leth, nil
}
diff --git a/les/status/accounts.go b/les/status/accounts.go
new file mode 100644
index 000000000..78bd2ad92
--- /dev/null
+++ b/les/status/accounts.go
@@ -0,0 +1,45 @@
+package status
+
+import (
+ "github.com/ethereum/go-ethereum/accounts"
+ "github.com/ethereum/go-ethereum/common"
+)
+
+// AccountManager abstracts both internal account manager and extra filter status backend requires
+type AccountManager struct {
+ am *accounts.Manager
+ accountsFilterHandler AccountsFilterHandler
+}
+
+// NewAccountManager creates a new AccountManager
+func NewAccountManager(am *accounts.Manager) *AccountManager {
+ return &AccountManager{
+ am: am,
+ }
+}
+
+// AccountsFilterHandler function to filter out accounts list
+type AccountsFilterHandler func([]common.Address) []common.Address
+
+// Accounts returns accounts' addresses of currently logged in user.
+// Since status supports HD keys, the following list is returned:
+// [addressCDK#1, addressCKD#2->Child1, addressCKD#2->Child2, .. addressCKD#2->ChildN]
+func (d *AccountManager) Accounts() []common.Address {
+ var addresses []common.Address
+ for _, wallet := range d.am.Wallets() {
+ for _, account := range wallet.Accounts() {
+ addresses = append(addresses, account.Address)
+ }
+ }
+
+ if d.accountsFilterHandler != nil {
+ return d.accountsFilterHandler(addresses)
+ }
+
+ return addresses
+}
+
+// SetAccountsFilterHandler sets filtering function for accounts list
+func (d *AccountManager) SetAccountsFilterHandler(fn AccountsFilterHandler) {
+ d.accountsFilterHandler = fn
+}
diff --git a/les/status/types.go b/les/status/types.go
new file mode 100644
index 000000000..04437bdb6
--- /dev/null
+++ b/les/status/types.go
@@ -0,0 +1,17 @@
+package status
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+// 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"`
+}