signer: change the stdio jsonrpc to use legacy namespace conventions (#19047)
This PR will will break existing UIs, since it changes all calls like ApproveSignTransaction to be on the form ui_approveSignTransaction. This is to make it possible for the UI to reuse the json-rpc library from go-ethereum, which uses this convention. Also, this PR removes some unused structs, after import/export were removed from the external api (so no longer needs internal methods for approval) One more breaking change is introduced, removing passwords from the ApproveSignTxResponse and the likes. This makes the manual interface more like the rulebased interface, and integrates nicely with the credential storage. Thus, the way it worked before, it would be tempting for the UI to implement 'remember password' functionality. The way it is now, it will be easy instead to tell clef to store passwords and use them. If a pw is not found in the credential store, the user is prompted to provide the password.
This commit is contained in:
committed by
Péter Szilágyi
parent
eb199f1fc2
commit
5f94f8c7e7
@ -23,6 +23,7 @@ import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
@ -32,6 +33,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/signer/storage"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -40,7 +42,7 @@ const (
|
||||
// ExternalAPIVersion -- see extapi_changelog.md
|
||||
ExternalAPIVersion = "6.0.0"
|
||||
// InternalAPIVersion -- see intapi_changelog.md
|
||||
InternalAPIVersion = "4.0.0"
|
||||
InternalAPIVersion = "6s.0.0"
|
||||
)
|
||||
|
||||
// ExternalAPI defines the external API through which signing requests are made.
|
||||
@ -68,10 +70,6 @@ type UIClientAPI interface {
|
||||
ApproveTx(request *SignTxRequest) (SignTxResponse, error)
|
||||
// ApproveSignData prompt the user for confirmation to request to sign data
|
||||
ApproveSignData(request *SignDataRequest) (SignDataResponse, error)
|
||||
// ApproveExport prompt the user for confirmation to export encrypted Account json
|
||||
ApproveExport(request *ExportRequest) (ExportResponse, error)
|
||||
// ApproveImport prompt the user for confirmation to import Account json
|
||||
ApproveImport(request *ImportRequest) (ImportResponse, error)
|
||||
// ApproveListing prompt the user for confirmation to list accounts
|
||||
// the list of accounts to list can be modified by the UI
|
||||
ApproveListing(request *ListRequest) (ListResponse, error)
|
||||
@ -96,11 +94,12 @@ type UIClientAPI interface {
|
||||
|
||||
// SignerAPI defines the actual implementation of ExternalAPI
|
||||
type SignerAPI struct {
|
||||
chainID *big.Int
|
||||
am *accounts.Manager
|
||||
UI UIClientAPI
|
||||
validator *Validator
|
||||
rejectMode bool
|
||||
chainID *big.Int
|
||||
am *accounts.Manager
|
||||
UI UIClientAPI
|
||||
validator *Validator
|
||||
rejectMode bool
|
||||
credentials storage.Storage
|
||||
}
|
||||
|
||||
// Metadata about a request
|
||||
@ -187,25 +186,6 @@ type (
|
||||
//The UI may make changes to the TX
|
||||
Transaction SendTxArgs `json:"transaction"`
|
||||
Approved bool `json:"approved"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
// ExportRequest info about query to export accounts
|
||||
ExportRequest struct {
|
||||
Address common.Address `json:"address"`
|
||||
Meta Metadata `json:"meta"`
|
||||
}
|
||||
// ExportResponse response to export-request
|
||||
ExportResponse struct {
|
||||
Approved bool `json:"approved"`
|
||||
}
|
||||
// ImportRequest info about request to import an Account
|
||||
ImportRequest struct {
|
||||
Meta Metadata `json:"meta"`
|
||||
}
|
||||
ImportResponse struct {
|
||||
Approved bool `json:"approved"`
|
||||
OldPassword string `json:"old_password"`
|
||||
NewPassword string `json:"new_password"`
|
||||
}
|
||||
SignDataRequest struct {
|
||||
ContentType string `json:"content_type"`
|
||||
@ -217,14 +197,12 @@ type (
|
||||
}
|
||||
SignDataResponse struct {
|
||||
Approved bool `json:"approved"`
|
||||
Password string
|
||||
}
|
||||
NewAccountRequest struct {
|
||||
Meta Metadata `json:"meta"`
|
||||
}
|
||||
NewAccountResponse struct {
|
||||
Approved bool `json:"approved"`
|
||||
Password string `json:"password"`
|
||||
Approved bool `json:"approved"`
|
||||
}
|
||||
ListRequest struct {
|
||||
Accounts []accounts.Account `json:"accounts"`
|
||||
@ -240,8 +218,8 @@ type (
|
||||
Info map[string]interface{} `json:"info"`
|
||||
}
|
||||
UserInputRequest struct {
|
||||
Prompt string `json:"prompt"`
|
||||
Title string `json:"title"`
|
||||
Prompt string `json:"prompt"`
|
||||
IsPassword bool `json:"isPassword"`
|
||||
}
|
||||
UserInputResponse struct {
|
||||
@ -256,11 +234,11 @@ var ErrRequestDenied = errors.New("Request denied")
|
||||
// key that is generated when a new Account is created.
|
||||
// noUSB disables USB support that is required to support hardware devices such as
|
||||
// ledger and trezor.
|
||||
func NewSignerAPI(am *accounts.Manager, chainID int64, noUSB bool, ui UIClientAPI, abidb *AbiDb, advancedMode bool) *SignerAPI {
|
||||
func NewSignerAPI(am *accounts.Manager, chainID int64, noUSB bool, ui UIClientAPI, abidb *AbiDb, advancedMode bool, credentials storage.Storage) *SignerAPI {
|
||||
if advancedMode {
|
||||
log.Info("Clef is in advanced mode: will warn instead of reject")
|
||||
}
|
||||
signer := &SignerAPI{big.NewInt(chainID), am, ui, NewValidator(abidb), !advancedMode}
|
||||
signer := &SignerAPI{big.NewInt(chainID), am, ui, NewValidator(abidb), !advancedMode, credentials}
|
||||
if !noUSB {
|
||||
signer.startUSBListener()
|
||||
}
|
||||
@ -381,24 +359,27 @@ func (api *SignerAPI) New(ctx context.Context) (common.Address, error) {
|
||||
if len(be) == 0 {
|
||||
return common.Address{}, errors.New("password based accounts not supported")
|
||||
}
|
||||
var (
|
||||
resp NewAccountResponse
|
||||
err error
|
||||
)
|
||||
if resp, err := api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)}); err != nil {
|
||||
return common.Address{}, err
|
||||
} else if !resp.Approved {
|
||||
return common.Address{}, ErrRequestDenied
|
||||
}
|
||||
|
||||
// Three retries to get a valid password
|
||||
for i := 0; i < 3; i++ {
|
||||
resp, err = api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)})
|
||||
resp, err := api.UI.OnInputRequired(UserInputRequest{
|
||||
"New account password",
|
||||
fmt.Sprintf("Please enter a password for the new account to be created (attempt %d of 3)", i),
|
||||
true})
|
||||
if err != nil {
|
||||
return common.Address{}, err
|
||||
log.Warn("error obtaining password", "attempt", i, "error", err)
|
||||
continue
|
||||
}
|
||||
if !resp.Approved {
|
||||
return common.Address{}, ErrRequestDenied
|
||||
}
|
||||
if pwErr := ValidatePasswordFormat(resp.Password); pwErr != nil {
|
||||
if pwErr := ValidatePasswordFormat(resp.Text); pwErr != nil {
|
||||
api.UI.ShowError(fmt.Sprintf("Account creation attempt #%d failed due to password requirements: %v", (i + 1), pwErr))
|
||||
} else {
|
||||
// No error
|
||||
acc, err := be[0].(*keystore.KeyStore).NewAccount(resp.Password)
|
||||
acc, err := be[0].(*keystore.KeyStore).NewAccount(resp.Text)
|
||||
return acc.Address, err
|
||||
}
|
||||
}
|
||||
@ -452,6 +433,24 @@ func logDiff(original *SignTxRequest, new *SignTxResponse) bool {
|
||||
return modified
|
||||
}
|
||||
|
||||
func (api *SignerAPI) lookupPassword(address common.Address) string {
|
||||
return api.credentials.Get(strings.ToLower(address.String()))
|
||||
}
|
||||
func (api *SignerAPI) lookupOrQueryPassword(address common.Address, title, prompt string) (string, error) {
|
||||
if pw := api.lookupPassword(address); pw != "" {
|
||||
return pw, nil
|
||||
} else {
|
||||
pwResp, err := api.UI.OnInputRequired(UserInputRequest{title, prompt, true})
|
||||
if err != nil {
|
||||
log.Warn("error obtaining password", "error", err)
|
||||
// We'll not forward the error here, in case the error contains info about the response from the UI,
|
||||
// which could leak the password if it was malformed json or something
|
||||
return "", errors.New("internal error")
|
||||
}
|
||||
return pwResp.Text, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SignTransaction signs the given Transaction and returns it both as json and rlp-encoded form
|
||||
func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) {
|
||||
var (
|
||||
@ -495,9 +494,14 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, meth
|
||||
}
|
||||
// Convert fields into a real transaction
|
||||
var unsignedTx = result.Transaction.toTransaction()
|
||||
|
||||
// Get the password for the transaction
|
||||
pw, err := api.lookupOrQueryPassword(acc.Address, "Account password",
|
||||
fmt.Sprintf("Please enter the password for account %s", acc.Address.String()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// The one to sign is the one that was returned from the UI
|
||||
signedTx, err := wallet.SignTxWithPassphrase(acc, result.Password, unsignedTx, api.chainID)
|
||||
signedTx, err := wallet.SignTxWithPassphrase(acc, pw, unsignedTx, api.chainID)
|
||||
if err != nil {
|
||||
api.UI.ShowError(err.Error())
|
||||
return nil, err
|
||||
|
@ -19,7 +19,6 @@ package core
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
@ -35,62 +34,49 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/signer/storage"
|
||||
)
|
||||
|
||||
//Used for testing
|
||||
type HeadlessUI struct {
|
||||
controller chan string
|
||||
type headlessUi struct {
|
||||
approveCh chan string // to send approve/deny
|
||||
inputCh chan string // to send password
|
||||
}
|
||||
|
||||
func (ui *HeadlessUI) OnInputRequired(info UserInputRequest) (UserInputResponse, error) {
|
||||
return UserInputResponse{}, errors.New("not implemented")
|
||||
func (ui *headlessUi) OnInputRequired(info UserInputRequest) (UserInputResponse, error) {
|
||||
input := <-ui.inputCh
|
||||
return UserInputResponse{Text: input}, nil
|
||||
}
|
||||
|
||||
func (ui *HeadlessUI) OnSignerStartup(info StartupInfo) {
|
||||
}
|
||||
func (ui *HeadlessUI) RegisterUIServer(api *UIServerAPI) {
|
||||
}
|
||||
func (ui *headlessUi) OnSignerStartup(info StartupInfo) {}
|
||||
func (ui *headlessUi) RegisterUIServer(api *UIServerAPI) {}
|
||||
func (ui *headlessUi) OnApprovedTx(tx ethapi.SignTransactionResult) {}
|
||||
|
||||
func (ui *HeadlessUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
||||
fmt.Printf("OnApproved()\n")
|
||||
}
|
||||
func (ui *headlessUi) ApproveTx(request *SignTxRequest) (SignTxResponse, error) {
|
||||
|
||||
func (ui *HeadlessUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) {
|
||||
|
||||
switch <-ui.controller {
|
||||
switch <-ui.approveCh {
|
||||
case "Y":
|
||||
return SignTxResponse{request.Transaction, true, <-ui.controller}, nil
|
||||
case "M": //Modify
|
||||
return SignTxResponse{request.Transaction, true}, nil
|
||||
case "M": // modify
|
||||
// The headless UI always modifies the transaction
|
||||
old := big.Int(request.Transaction.Value)
|
||||
newVal := big.NewInt(0).Add(&old, big.NewInt(1))
|
||||
request.Transaction.Value = hexutil.Big(*newVal)
|
||||
return SignTxResponse{request.Transaction, true, <-ui.controller}, nil
|
||||
return SignTxResponse{request.Transaction, true}, nil
|
||||
default:
|
||||
return SignTxResponse{request.Transaction, false, ""}, nil
|
||||
return SignTxResponse{request.Transaction, false}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (ui *HeadlessUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) {
|
||||
if "Y" == <-ui.controller {
|
||||
return SignDataResponse{true, <-ui.controller}, nil
|
||||
}
|
||||
return SignDataResponse{false, ""}, nil
|
||||
func (ui *headlessUi) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) {
|
||||
approved := "Y" == <-ui.approveCh
|
||||
return SignDataResponse{approved}, nil
|
||||
}
|
||||
|
||||
func (ui *HeadlessUI) ApproveExport(request *ExportRequest) (ExportResponse, error) {
|
||||
return ExportResponse{<-ui.controller == "Y"}, nil
|
||||
|
||||
}
|
||||
|
||||
func (ui *HeadlessUI) ApproveImport(request *ImportRequest) (ImportResponse, error) {
|
||||
if "Y" == <-ui.controller {
|
||||
return ImportResponse{true, <-ui.controller, <-ui.controller}, nil
|
||||
}
|
||||
return ImportResponse{false, "", ""}, nil
|
||||
}
|
||||
|
||||
func (ui *HeadlessUI) ApproveListing(request *ListRequest) (ListResponse, error) {
|
||||
switch <-ui.controller {
|
||||
func (ui *headlessUi) ApproveListing(request *ListRequest) (ListResponse, error) {
|
||||
approval := <-ui.approveCh
|
||||
//fmt.Printf("approval %s\n", approval)
|
||||
switch approval {
|
||||
case "A":
|
||||
return ListResponse{request.Accounts}, nil
|
||||
case "1":
|
||||
@ -102,19 +88,19 @@ func (ui *HeadlessUI) ApproveListing(request *ListRequest) (ListResponse, error)
|
||||
}
|
||||
}
|
||||
|
||||
func (ui *HeadlessUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) {
|
||||
if "Y" == <-ui.controller {
|
||||
return NewAccountResponse{true, <-ui.controller}, nil
|
||||
func (ui *headlessUi) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) {
|
||||
if "Y" == <-ui.approveCh {
|
||||
return NewAccountResponse{true}, nil
|
||||
}
|
||||
return NewAccountResponse{false, ""}, nil
|
||||
return NewAccountResponse{false}, nil
|
||||
}
|
||||
|
||||
func (ui *HeadlessUI) ShowError(message string) {
|
||||
func (ui *headlessUi) ShowError(message string) {
|
||||
//stdout is used by communication
|
||||
fmt.Fprintln(os.Stderr, message)
|
||||
}
|
||||
|
||||
func (ui *HeadlessUI) ShowInfo(message string) {
|
||||
func (ui *headlessUi) ShowInfo(message string) {
|
||||
//stdout is used by communication
|
||||
fmt.Fprintln(os.Stderr, message)
|
||||
}
|
||||
@ -131,25 +117,20 @@ func tmpDirName(t *testing.T) string {
|
||||
return d
|
||||
}
|
||||
|
||||
func setup(t *testing.T) (*SignerAPI, chan string) {
|
||||
|
||||
controller := make(chan string, 20)
|
||||
|
||||
func setup(t *testing.T) (*SignerAPI, *headlessUi) {
|
||||
db, err := NewAbiDBFromFile("../../cmd/clef/4byte.json")
|
||||
if err != nil {
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
var (
|
||||
ui = &HeadlessUI{controller}
|
||||
am = StartClefAccountManager(tmpDirName(t), true, true)
|
||||
api = NewSignerAPI(am, 1337, true, ui, db, true)
|
||||
)
|
||||
return api, controller
|
||||
}
|
||||
func createAccount(control chan string, api *SignerAPI, t *testing.T) {
|
||||
ui := &headlessUi{make(chan string, 20), make(chan string, 20)}
|
||||
am := StartClefAccountManager(tmpDirName(t), true, true)
|
||||
api := NewSignerAPI(am, 1337, true, ui, db, true, &storage.NoStorage{})
|
||||
return api, ui
|
||||
|
||||
control <- "Y"
|
||||
control <- "a_long_password"
|
||||
}
|
||||
func createAccount(ui *headlessUi, api *SignerAPI, t *testing.T) {
|
||||
ui.approveCh <- "Y"
|
||||
ui.inputCh <- "a_long_password"
|
||||
_, err := api.New(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -158,14 +139,13 @@ func createAccount(control chan string, api *SignerAPI, t *testing.T) {
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
}
|
||||
|
||||
func failCreateAccountWithPassword(control chan string, api *SignerAPI, password string, t *testing.T) {
|
||||
func failCreateAccountWithPassword(ui *headlessUi, api *SignerAPI, password string, t *testing.T) {
|
||||
|
||||
control <- "Y"
|
||||
control <- password
|
||||
control <- "Y"
|
||||
control <- password
|
||||
control <- "Y"
|
||||
control <- password
|
||||
ui.approveCh <- "Y"
|
||||
// We will be asked three times to provide a suitable password
|
||||
ui.inputCh <- password
|
||||
ui.inputCh <- password
|
||||
ui.inputCh <- password
|
||||
|
||||
addr, err := api.New(context.Background())
|
||||
if err == nil {
|
||||
@ -176,8 +156,8 @@ func failCreateAccountWithPassword(control chan string, api *SignerAPI, password
|
||||
}
|
||||
}
|
||||
|
||||
func failCreateAccount(control chan string, api *SignerAPI, t *testing.T) {
|
||||
control <- "N"
|
||||
func failCreateAccount(ui *headlessUi, api *SignerAPI, t *testing.T) {
|
||||
ui.approveCh <- "N"
|
||||
addr, err := api.New(context.Background())
|
||||
if err != ErrRequestDenied {
|
||||
t.Fatal(err)
|
||||
@ -187,19 +167,20 @@ func failCreateAccount(control chan string, api *SignerAPI, t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func list(control chan string, api *SignerAPI, t *testing.T) []common.Address {
|
||||
control <- "A"
|
||||
list, err := api.List(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return list
|
||||
func list(ui *headlessUi, api *SignerAPI, t *testing.T) ([]common.Address, error) {
|
||||
ui.approveCh <- "A"
|
||||
return api.List(context.Background())
|
||||
|
||||
}
|
||||
|
||||
func TestNewAcc(t *testing.T) {
|
||||
api, control := setup(t)
|
||||
verifyNum := func(num int) {
|
||||
if list := list(control, api, t); len(list) != num {
|
||||
list, err := list(control, api, t)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %v", err)
|
||||
}
|
||||
if len(list) != num {
|
||||
t.Errorf("Expected %d accounts, got %d", num, len(list))
|
||||
}
|
||||
}
|
||||
@ -212,18 +193,16 @@ func TestNewAcc(t *testing.T) {
|
||||
failCreateAccount(control, api, t)
|
||||
createAccount(control, api, t)
|
||||
failCreateAccount(control, api, t)
|
||||
|
||||
verifyNum(4)
|
||||
|
||||
// Fail to create this, due to bad password
|
||||
failCreateAccountWithPassword(control, api, "short", t)
|
||||
failCreateAccountWithPassword(control, api, "longerbutbad\rfoo", t)
|
||||
|
||||
verifyNum(4)
|
||||
|
||||
// Testing listing:
|
||||
// Listing one Account
|
||||
control <- "1"
|
||||
control.approveCh <- "1"
|
||||
list, err := api.List(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -232,7 +211,7 @@ func TestNewAcc(t *testing.T) {
|
||||
t.Fatalf("List should only show one Account")
|
||||
}
|
||||
// Listing denied
|
||||
control <- "Nope"
|
||||
control.approveCh <- "Nope"
|
||||
list, err = api.List(context.Background())
|
||||
if len(list) != 0 {
|
||||
t.Fatalf("List should be empty")
|
||||
@ -269,7 +248,7 @@ func TestSignTx(t *testing.T) {
|
||||
|
||||
api, control := setup(t)
|
||||
createAccount(control, api, t)
|
||||
control <- "A"
|
||||
control.approveCh <- "A"
|
||||
list, err = api.List(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -279,8 +258,8 @@ func TestSignTx(t *testing.T) {
|
||||
methodSig := "test(uint)"
|
||||
tx := mkTestTx(a)
|
||||
|
||||
control <- "Y"
|
||||
control <- "wrongpassword"
|
||||
control.approveCh <- "Y"
|
||||
control.inputCh <- "wrongpassword"
|
||||
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
||||
if res != nil {
|
||||
t.Errorf("Expected nil-response, got %v", res)
|
||||
@ -288,7 +267,7 @@ func TestSignTx(t *testing.T) {
|
||||
if err != keystore.ErrDecrypt {
|
||||
t.Errorf("Expected ErrLocked! %v", err)
|
||||
}
|
||||
control <- "No way"
|
||||
control.approveCh <- "No way"
|
||||
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
||||
if res != nil {
|
||||
t.Errorf("Expected nil-response, got %v", res)
|
||||
@ -296,8 +275,9 @@ func TestSignTx(t *testing.T) {
|
||||
if err != ErrRequestDenied {
|
||||
t.Errorf("Expected ErrRequestDenied! %v", err)
|
||||
}
|
||||
control <- "Y"
|
||||
control <- "a_long_password"
|
||||
// Sign with correct password
|
||||
control.approveCh <- "Y"
|
||||
control.inputCh <- "a_long_password"
|
||||
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
||||
|
||||
if err != nil {
|
||||
@ -310,8 +290,8 @@ func TestSignTx(t *testing.T) {
|
||||
if parsedTx.Value().Cmp(tx.Value.ToInt()) != 0 {
|
||||
t.Errorf("Expected value to be unchanged, expected %v got %v", tx.Value, parsedTx.Value())
|
||||
}
|
||||
control <- "Y"
|
||||
control <- "a_long_password"
|
||||
control.approveCh <- "Y"
|
||||
control.inputCh <- "a_long_password"
|
||||
|
||||
res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
||||
if err != nil {
|
||||
@ -322,8 +302,8 @@ func TestSignTx(t *testing.T) {
|
||||
}
|
||||
|
||||
//The tx is modified by the UI
|
||||
control <- "M"
|
||||
control <- "a_long_password"
|
||||
control.approveCh <- "M"
|
||||
control.inputCh <- "a_long_password"
|
||||
|
||||
res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
||||
if err != nil {
|
||||
@ -341,31 +321,3 @@ func TestSignTx(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
func TestAsyncronousResponses(t *testing.T){
|
||||
|
||||
//Set up one account
|
||||
api, control := setup(t)
|
||||
createAccount(control, api, t)
|
||||
|
||||
// Two transactions, the second one with larger value than the first
|
||||
tx1 := mkTestTx()
|
||||
newVal := big.NewInt(0).Add((*big.Int) (tx1.Value), big.NewInt(1))
|
||||
tx2 := mkTestTx()
|
||||
tx2.Value = (*hexutil.Big)(newVal)
|
||||
|
||||
control <- "W" //wait
|
||||
control <- "Y" //
|
||||
control <- "a_long_password"
|
||||
control <- "Y" //
|
||||
control <- "a_long_password"
|
||||
|
||||
var err error
|
||||
|
||||
h1, err := api.SignTransaction(context.Background(), common.HexToAddress("1111"), tx1, nil)
|
||||
h2, err := api.SignTransaction(context.Background(), common.HexToAddress("2222"), tx2, nil)
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
|
@ -87,9 +87,10 @@ func (ui *CommandlineUI) readPasswordText(inputstring string) string {
|
||||
}
|
||||
|
||||
func (ui *CommandlineUI) OnInputRequired(info UserInputRequest) (UserInputResponse, error) {
|
||||
fmt.Println(info.Title)
|
||||
fmt.Println(info.Prompt)
|
||||
|
||||
fmt.Printf("## %s\n\n%s\n", info.Title, info.Prompt)
|
||||
if info.IsPassword {
|
||||
fmt.Printf("> ")
|
||||
text, err := terminal.ReadPassword(int(os.Stdin.Fd()))
|
||||
if err != nil {
|
||||
log.Error("Failed to read password", "err", err)
|
||||
@ -156,9 +157,9 @@ func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, erro
|
||||
showMetadata(request.Meta)
|
||||
fmt.Printf("-------------------------------------------\n")
|
||||
if !ui.confirm() {
|
||||
return SignTxResponse{request.Transaction, false, ""}, nil
|
||||
return SignTxResponse{request.Transaction, false}, nil
|
||||
}
|
||||
return SignTxResponse{request.Transaction, true, ui.readPassword()}, nil
|
||||
return SignTxResponse{request.Transaction, true}, nil
|
||||
}
|
||||
|
||||
// ApproveSignData prompt the user for confirmation to request to sign data
|
||||
@ -178,40 +179,9 @@ func (ui *CommandlineUI) ApproveSignData(request *SignDataRequest) (SignDataResp
|
||||
fmt.Printf("-------------------------------------------\n")
|
||||
showMetadata(request.Meta)
|
||||
if !ui.confirm() {
|
||||
return SignDataResponse{false, ""}, nil
|
||||
return SignDataResponse{false}, nil
|
||||
}
|
||||
return SignDataResponse{true, ui.readPassword()}, nil
|
||||
}
|
||||
|
||||
// ApproveExport prompt the user for confirmation to export encrypted Account json
|
||||
func (ui *CommandlineUI) ApproveExport(request *ExportRequest) (ExportResponse, error) {
|
||||
ui.mu.Lock()
|
||||
defer ui.mu.Unlock()
|
||||
|
||||
fmt.Printf("-------- Export Account request--------------\n")
|
||||
fmt.Printf("A request has been made to export the (encrypted) keyfile\n")
|
||||
fmt.Printf("Approving this operation means that the caller obtains the (encrypted) contents\n")
|
||||
fmt.Printf("\n")
|
||||
fmt.Printf("Account: %x\n", request.Address)
|
||||
//fmt.Printf("keyfile: \n%v\n", request.file)
|
||||
fmt.Printf("-------------------------------------------\n")
|
||||
showMetadata(request.Meta)
|
||||
return ExportResponse{ui.confirm()}, nil
|
||||
}
|
||||
|
||||
// ApproveImport prompt the user for confirmation to import Account json
|
||||
func (ui *CommandlineUI) ApproveImport(request *ImportRequest) (ImportResponse, error) {
|
||||
ui.mu.Lock()
|
||||
defer ui.mu.Unlock()
|
||||
|
||||
fmt.Printf("-------- Import Account request--------------\n")
|
||||
fmt.Printf("A request has been made to import an encrypted keyfile\n")
|
||||
fmt.Printf("-------------------------------------------\n")
|
||||
showMetadata(request.Meta)
|
||||
if !ui.confirm() {
|
||||
return ImportResponse{false, "", ""}, nil
|
||||
}
|
||||
return ImportResponse{true, ui.readPasswordText("Old password"), ui.readPasswordText("New password")}, nil
|
||||
return SignDataResponse{true}, nil
|
||||
}
|
||||
|
||||
// ApproveListing prompt the user for confirmation to list accounts
|
||||
@ -248,21 +218,20 @@ func (ui *CommandlineUI) ApproveNewAccount(request *NewAccountRequest) (NewAccou
|
||||
fmt.Printf("and the address is returned to the external caller\n\n")
|
||||
showMetadata(request.Meta)
|
||||
if !ui.confirm() {
|
||||
return NewAccountResponse{false, ""}, nil
|
||||
return NewAccountResponse{false}, nil
|
||||
}
|
||||
return NewAccountResponse{true, ui.readPassword()}, nil
|
||||
return NewAccountResponse{true}, nil
|
||||
}
|
||||
|
||||
// ShowError displays error message to user
|
||||
func (ui *CommandlineUI) ShowError(message string) {
|
||||
fmt.Printf("-------- Error message from Clef-----------\n")
|
||||
fmt.Println(message)
|
||||
fmt.Printf("## Error \n%s\n", message)
|
||||
fmt.Printf("-------------------------------------------\n")
|
||||
}
|
||||
|
||||
// ShowInfo displays info message to user
|
||||
func (ui *CommandlineUI) ShowInfo(message string) {
|
||||
fmt.Printf("Info: %v\n", message)
|
||||
fmt.Printf("## Info \n%s\n", message)
|
||||
}
|
||||
|
||||
func (ui *CommandlineUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
||||
|
@ -139,8 +139,14 @@ func (api *SignerAPI) sign(addr common.MixedcaseAddress, req *SignDataRequest, l
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pw, err := api.lookupOrQueryPassword(account.Address,
|
||||
"Password for signing",
|
||||
fmt.Sprintf("Please enter password for signing data with account %s", account.Address.Hex()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Sign the data with the wallet
|
||||
signature, err := wallet.SignDataWithPassphrase(account, res.Password, req.ContentType, req.Rawdata)
|
||||
signature, err := wallet.SignDataWithPassphrase(account, pw, req.ContentType, req.Rawdata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -179,15 +179,15 @@ func TestSignData(t *testing.T) {
|
||||
//Create two accounts
|
||||
createAccount(control, api, t)
|
||||
createAccount(control, api, t)
|
||||
control <- "1"
|
||||
control.approveCh <- "1"
|
||||
list, err := api.List(context.Background())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a := common.NewMixedcaseAddress(list[0])
|
||||
|
||||
control <- "Y"
|
||||
control <- "wrongpassword"
|
||||
control.approveCh <- "Y"
|
||||
control.inputCh <- "wrongpassword"
|
||||
signature, err := api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
|
||||
if signature != nil {
|
||||
t.Errorf("Expected nil-data, got %x", signature)
|
||||
@ -195,7 +195,7 @@ func TestSignData(t *testing.T) {
|
||||
if err != keystore.ErrDecrypt {
|
||||
t.Errorf("Expected ErrLocked! '%v'", err)
|
||||
}
|
||||
control <- "No way"
|
||||
control.approveCh <- "No way"
|
||||
signature, err = api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
|
||||
if signature != nil {
|
||||
t.Errorf("Expected nil-data, got %x", signature)
|
||||
@ -204,8 +204,8 @@ func TestSignData(t *testing.T) {
|
||||
t.Errorf("Expected ErrRequestDenied! '%v'", err)
|
||||
}
|
||||
// text/plain
|
||||
control <- "Y"
|
||||
control <- "a_long_password"
|
||||
control.approveCh <- "Y"
|
||||
control.inputCh <- "a_long_password"
|
||||
signature, err = api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -214,8 +214,8 @@ func TestSignData(t *testing.T) {
|
||||
t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature))
|
||||
}
|
||||
// data/typed
|
||||
control <- "Y"
|
||||
control <- "a_long_password"
|
||||
control.approveCh <- "Y"
|
||||
control.inputCh <- "a_long_password"
|
||||
signature, err = api.SignTypedData(context.Background(), a, typedData)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -65,71 +65,59 @@ func (ui *StdIOUI) notify(serviceMethod string, args interface{}) error {
|
||||
|
||||
func (ui *StdIOUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) {
|
||||
var result SignTxResponse
|
||||
err := ui.dispatch("ApproveTx", request, &result)
|
||||
err := ui.dispatch("ui_approveTx", request, &result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (ui *StdIOUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) {
|
||||
var result SignDataResponse
|
||||
err := ui.dispatch("ApproveSignData", request, &result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (ui *StdIOUI) ApproveExport(request *ExportRequest) (ExportResponse, error) {
|
||||
var result ExportResponse
|
||||
err := ui.dispatch("ApproveExport", request, &result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (ui *StdIOUI) ApproveImport(request *ImportRequest) (ImportResponse, error) {
|
||||
var result ImportResponse
|
||||
err := ui.dispatch("ApproveImport", request, &result)
|
||||
err := ui.dispatch("ui_approveSignData", request, &result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (ui *StdIOUI) ApproveListing(request *ListRequest) (ListResponse, error) {
|
||||
var result ListResponse
|
||||
err := ui.dispatch("ApproveListing", request, &result)
|
||||
err := ui.dispatch("ui_approveListing", request, &result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (ui *StdIOUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) {
|
||||
var result NewAccountResponse
|
||||
err := ui.dispatch("ApproveNewAccount", request, &result)
|
||||
err := ui.dispatch("ui_approveNewAccount", request, &result)
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (ui *StdIOUI) ShowError(message string) {
|
||||
err := ui.notify("ShowError", &Message{message})
|
||||
err := ui.notify("ui_showError", &Message{message})
|
||||
if err != nil {
|
||||
log.Info("Error calling 'ShowError'", "exc", err.Error(), "msg", message)
|
||||
log.Info("Error calling 'ui_showError'", "exc", err.Error(), "msg", message)
|
||||
}
|
||||
}
|
||||
|
||||
func (ui *StdIOUI) ShowInfo(message string) {
|
||||
err := ui.notify("ShowInfo", Message{message})
|
||||
err := ui.notify("ui_showInfo", Message{message})
|
||||
if err != nil {
|
||||
log.Info("Error calling 'ShowInfo'", "exc", err.Error(), "msg", message)
|
||||
log.Info("Error calling 'ui_showInfo'", "exc", err.Error(), "msg", message)
|
||||
}
|
||||
}
|
||||
func (ui *StdIOUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
||||
err := ui.notify("OnApprovedTx", tx)
|
||||
err := ui.notify("ui_onApprovedTx", tx)
|
||||
if err != nil {
|
||||
log.Info("Error calling 'OnApprovedTx'", "exc", err.Error(), "tx", tx)
|
||||
log.Info("Error calling 'ui_onApprovedTx'", "exc", err.Error(), "tx", tx)
|
||||
}
|
||||
}
|
||||
|
||||
func (ui *StdIOUI) OnSignerStartup(info StartupInfo) {
|
||||
err := ui.notify("OnSignerStartup", info)
|
||||
err := ui.notify("ui_onSignerStartup", info)
|
||||
if err != nil {
|
||||
log.Info("Error calling 'OnSignerStartup'", "exc", err.Error(), "info", info)
|
||||
log.Info("Error calling 'ui_onSignerStartup'", "exc", err.Error(), "info", info)
|
||||
}
|
||||
}
|
||||
func (ui *StdIOUI) OnInputRequired(info UserInputRequest) (UserInputResponse, error) {
|
||||
var result UserInputResponse
|
||||
err := ui.dispatch("OnInputRequired", info, &result)
|
||||
err := ui.dispatch("ui_onInputRequired", info, &result)
|
||||
if err != nil {
|
||||
log.Info("Error calling 'OnInputRequired'", "exc", err.Error(), "info", info)
|
||||
log.Info("Error calling 'ui_onInputRequired'", "exc", err.Error(), "info", info)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
Reference in New Issue
Block a user