signer,clef: implement timeout for external api

This commit is contained in:
Martin Holst Swende 2018-11-11 22:03:03 +01:00
parent 1ff152f3a4
commit a043ed2ff6
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
3 changed files with 157 additions and 10 deletions

View file

@ -49,12 +49,6 @@ import (
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
) )
// ExternalAPIVersion -- see extapi_changelog.md
const ExternalAPIVersion = "4.0.0"
// InternalAPIVersion -- see intapi_changelog.md
const InternalAPIVersion = "3.0.0"
const legalWarning = ` const legalWarning = `
WARNING! WARNING!
@ -110,6 +104,11 @@ var (
Usage: "File used to emit audit logs. Set to \"\" to disable", Usage: "File used to emit audit logs. Set to \"\" to disable",
Value: "audit.log", Value: "audit.log",
} }
timeoutFlag = cli.DurationFlag{
Name: "timeout",
Usage: "Specify a timeout, so external callers get a response within a certain time (default 0 = no timeout)",
Value: 0,
}
ruleFlag = cli.StringFlag{ ruleFlag = cli.StringFlag{
Name: "rules", Name: "rules",
Usage: "Enable rule-engine", Usage: "Enable rule-engine",
@ -194,6 +193,7 @@ func init() {
dBFlag, dBFlag,
customDBFlag, customDBFlag,
auditLogFlag, auditLogFlag,
timeoutFlag,
ruleFlag, ruleFlag,
stdiouiFlag, stdiouiFlag,
testFlag, testFlag,
@ -414,6 +414,12 @@ func signer(c *cli.Context) error {
c.GlobalBool(utils.LightKDFFlag.Name), c.GlobalBool(utils.LightKDFFlag.Name),
c.GlobalBool(advancedMode.Name)) c.GlobalBool(advancedMode.Name))
api = apiImpl api = apiImpl
// Timeout
if timeout := c.GlobalDuration(timeoutFlag.Name); timeout > 0 {
log.Info("extapi timeout set", "time", timeout)
api = core.NewTimedExternalAPI(api, timeout)
}
// Audit logging // Audit logging
if logfile := c.GlobalString(auditLogFlag.Name); logfile != "" { if logfile := c.GlobalString(auditLogFlag.Name); logfile != "" {
api, err = core.NewAuditLogger(logfile, api) api, err = core.NewAuditLogger(logfile, api)
@ -479,8 +485,8 @@ func signer(c *cli.Context) error {
} }
ui.OnSignerStartup(core.StartupInfo{ ui.OnSignerStartup(core.StartupInfo{
Info: map[string]interface{}{ Info: map[string]interface{}{
"extapi_version": ExternalAPIVersion, "extapi_version": core.ExternalAPIVersion,
"intapi_version": InternalAPIVersion, "intapi_version": core.InternalAPIVersion,
"extapi_http": extapiURL, "extapi_http": extapiURL,
"extapi_ipc": ipcapiURL, "extapi_ipc": ipcapiURL,
}, },

View file

@ -36,8 +36,14 @@ import (
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
) )
// numberOfAccountsToDerive For hardware wallets, the number of accounts to derive const(
const numberOfAccountsToDerive = 10 // numberOfAccountsToDerive For hardware wallets, the number of accounts to derive
numberOfAccountsToDerive = 10
// ExternalAPIVersion -- see extapi_changelog.md
ExternalAPIVersion = "4.0.0"
// InternalAPIVersion -- see intapi_changelog.md
InternalAPIVersion = "3.0.0"
)
// ExternalAPI defines the external API through which signing requests are made. // ExternalAPI defines the external API through which signing requests are made.
type ExternalAPI interface { type ExternalAPI interface {

135
signer/core/timeout.go Normal file
View file

@ -0,0 +1,135 @@
package core
import (
"context"
"encoding/json"
"fmt"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log"
"time"
)
// The TimedExternalAPI implements ExternalAPI, but can be configured to time out after a specified interval.
// This can be used to ensure that callers get a response within reasonable time,
// even if the user is unresponsive.
type TimedExternalAPI struct {
timeout time.Duration
next ExternalAPI
}
func NewTimedExternalAPI(next ExternalAPI, timeout time.Duration) ExternalAPI {
return &TimedExternalAPI{timeout, next}
}
var (
ErrTimeout = fmt.Errorf("timeout occurred")
)
func (t *TimedExternalAPI) List(ctx context.Context) ([]common.Address, error) {
type response struct {
addr []common.Address
err error
}
ch := make(chan response)
go func() {
addr, err := t.next.List(ctx)
ch <- response{addr, err}
}()
select {
case r := <-ch:
return r.addr, r.err
case <-time.After(t.timeout):
log.Info("timeout", "op", "list")
go func() { <-ch }()
return []common.Address{}, ErrTimeout
}
}
func (t *TimedExternalAPI) New(ctx context.Context) (accounts.Account, error) {
type response struct {
acc accounts.Account
err error
}
ch := make(chan response)
go func() {
acc, err := t.next.New(ctx)
ch <- response{acc, err}
}()
select {
case r := <-ch:
return r.acc, r.err
case <-time.After(t.timeout):
log.Info("timeout", "op", "list")
go func() { <-ch }()
return accounts.Account{}, ErrTimeout
}
}
func (t *TimedExternalAPI) SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) {
type response struct {
res *ethapi.SignTransactionResult
err error
}
ch := make(chan response)
go func() {
res, err := t.next.SignTransaction(ctx, args, methodSelector)
ch <- response{res, err}
}()
select {
case r := <-ch:
return r.res, r.err
case <-time.After(t.timeout):
log.Info("timeout", "op", "signTransaction")
go func() { <-ch }()
return nil, ErrTimeout
}
}
func (t *TimedExternalAPI) Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) {
type response struct {
res hexutil.Bytes
err error
}
ch := make(chan response)
go func() {
res, err := t.next.Sign(ctx, addr, data)
ch <- response{res, err}
}()
select {
case r := <-ch:
return r.res, r.err
case <-time.After(t.timeout):
log.Info("timeout", "op", "sign")
go func() { <-ch }()
return nil, ErrTimeout
}
}
func (t *TimedExternalAPI) Export(ctx context.Context, addr common.Address) (json.RawMessage, error) {
type response struct {
res json.RawMessage
err error
}
ch := make(chan response)
go func() {
res, err := t.next.Export(ctx, addr)
ch <- response{res, err}
}()
select {
case r := <-ch:
return r.res, r.err
case <-time.After(t.timeout):
log.Info("timeout", "op", "sign")
go func() { <-ch }()
return json.RawMessage{}, ErrTimeout
}
}
func (t *TimedExternalAPI) Version(ctx context.Context) (string, error) {
return ExternalAPIVersion, nil
}