mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
signer, clef: remove passwords from responses clef<->ui
This commit is contained in:
parent
bda88d91db
commit
4b0674d5eb
10 changed files with 215 additions and 219 deletions
|
|
@ -35,8 +35,7 @@ Response to SignDataRequest
|
||||||
Example:
|
Example:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"approved": true,
|
"approved": true
|
||||||
"Password": "apassword"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
### SignDataResponse - deny
|
### SignDataResponse - deny
|
||||||
|
|
@ -46,8 +45,7 @@ Response to SignDataRequest
|
||||||
Example:
|
Example:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"approved": false,
|
"approved": false
|
||||||
"Password": ""
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
### SignTxRequest
|
### SignTxRequest
|
||||||
|
|
@ -89,9 +87,9 @@ Example:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
### SignDataResponse - approve
|
### SignTxResponse - approve
|
||||||
|
|
||||||
Response to SignDataRequest. This response needs to contain the `transaction`, because the UI is free to make modifications to the transaction.
|
Response to request to sign a transaction. This response needs to contain the `transaction`, because the UI is free to make modifications to the transaction.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```json
|
```json
|
||||||
|
|
@ -105,19 +103,26 @@ Example:
|
||||||
"nonce": "0x4",
|
"nonce": "0x4",
|
||||||
"data": "0x04030201"
|
"data": "0x04030201"
|
||||||
},
|
},
|
||||||
"approved": true,
|
"approved": true
|
||||||
"password": "apassword"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
### SignDataResponse - deny
|
### SignTxResponse - deny
|
||||||
|
|
||||||
Response to SignDataRequest. When denying a request, there's no need to provide the transaction in return
|
Response to SignTxRequest. When denying a request, there's no need to provide the transaction in return
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"approved": false,
|
"transaction": {
|
||||||
"Password": ""
|
"from": "0x",
|
||||||
|
"to": null,
|
||||||
|
"gas": "0x0",
|
||||||
|
"gasPrice": "0x0",
|
||||||
|
"value": "0x0",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"data": null
|
||||||
|
},
|
||||||
|
"approved": false
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
### OnApproved - SignTransactionResult
|
### OnApproved - SignTransactionResult
|
||||||
|
|
@ -164,7 +169,7 @@ Example:
|
||||||
```
|
```
|
||||||
### UserInputResponse
|
### UserInputResponse
|
||||||
|
|
||||||
Response to SignDataRequest
|
Response to UserInputRequest
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```json
|
```json
|
||||||
|
|
@ -198,7 +203,7 @@ Example:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
### UserInputResponse
|
### ListResponse
|
||||||
|
|
||||||
Response to list request. The response contains a list of all addresses to show to the caller. Note: the UI is free to respond with any address the caller, regardless of whether it exists or not
|
Response to list request. The response contains a list of all addresses to show to the caller. Note: the UI is free to respond with any address the caller, regardless of whether it exists or not
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ var (
|
||||||
ruleFlag = cli.StringFlag{
|
ruleFlag = cli.StringFlag{
|
||||||
Name: "rules",
|
Name: "rules",
|
||||||
Usage: "Enable rule-engine",
|
Usage: "Enable rule-engine",
|
||||||
Value: "rules.json",
|
Value: "",
|
||||||
}
|
}
|
||||||
stdiouiFlag = cli.BoolFlag{
|
stdiouiFlag = cli.BoolFlag{
|
||||||
Name: "stdio-ui",
|
Name: "stdio-ui",
|
||||||
|
|
@ -372,16 +372,13 @@ func signer(c *cli.Context) error {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
api core.ExternalAPI
|
api core.ExternalAPI
|
||||||
|
pwStorage storage.Storage = &storage.NoStorage{}
|
||||||
)
|
)
|
||||||
|
|
||||||
configDir := c.GlobalString(configdirFlag.Name)
|
configDir := c.GlobalString(configdirFlag.Name)
|
||||||
if stretchedKey, err := readMasterKey(c, ui); err != nil {
|
if stretchedKey, err := readMasterKey(c, ui); err != nil {
|
||||||
log.Info("No master seed provided, rules disabled", "error", err)
|
log.Info("No master seed provided, rules disabled", "error", err)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
utils.Fatalf(err.Error())
|
|
||||||
}
|
|
||||||
vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
|
vaultLocation := filepath.Join(configDir, common.Bytes2Hex(crypto.Keccak256([]byte("vault"), stretchedKey)[:10]))
|
||||||
|
|
||||||
// Generate domain specific keys
|
// Generate domain specific keys
|
||||||
|
|
@ -390,24 +387,24 @@ func signer(c *cli.Context) error {
|
||||||
confkey := crypto.Keccak256([]byte("config"), stretchedKey)
|
confkey := crypto.Keccak256([]byte("config"), stretchedKey)
|
||||||
|
|
||||||
// Initialize the encrypted storages
|
// Initialize the encrypted storages
|
||||||
pwStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey)
|
pwStorage = storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "credentials.json"), pwkey)
|
||||||
jsStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "jsstorage.json"), jskey)
|
jsStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "jsstorage.json"), jskey)
|
||||||
configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confkey)
|
configStorage := storage.NewAESEncryptedStorage(filepath.Join(vaultLocation, "config.json"), confkey)
|
||||||
|
|
||||||
//Do we have a rule-file?
|
//Do we have a rule-file?
|
||||||
ruleJS, err := ioutil.ReadFile(c.GlobalString(ruleFlag.Name))
|
if ruleFile := c.GlobalString(ruleFlag.Name); ruleFile != "" {
|
||||||
|
ruleJS, err := ioutil.ReadFile(c.GlobalString(ruleFile))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Info("Could not load rulefile, rules not enabled", "file", "rulefile")
|
log.Info("Could not load rulefile, rules not enabled", "file", "rulefile")
|
||||||
} else {
|
} else {
|
||||||
hasher := sha256.New()
|
shasum := sha256.Sum256(ruleJS)
|
||||||
hasher.Write(ruleJS)
|
foundShaSum := hex.EncodeToString(shasum[:])
|
||||||
shasum := hasher.Sum(nil)
|
|
||||||
storedShasum := configStorage.Get("ruleset_sha256")
|
storedShasum := configStorage.Get("ruleset_sha256")
|
||||||
if storedShasum != hex.EncodeToString(shasum) {
|
if storedShasum != foundShaSum {
|
||||||
log.Info("Could not validate ruleset hash, rules not enabled", "got", hex.EncodeToString(shasum), "expected", storedShasum)
|
log.Info("Could not validate ruleset hash, rules not enabled", "got", foundShaSum, "expected", storedShasum)
|
||||||
} else {
|
} else {
|
||||||
// Initialize rules
|
// Initialize rules
|
||||||
ruleEngine, err := rules.NewRuleEvaluator(ui, jsStorage, pwStorage)
|
ruleEngine, err := rules.NewRuleEvaluator(ui, jsStorage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf(err.Error())
|
utils.Fatalf(err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -417,6 +414,7 @@ func signer(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
var (
|
var (
|
||||||
chainId = c.GlobalInt64(chainIdFlag.Name)
|
chainId = c.GlobalInt64(chainIdFlag.Name)
|
||||||
ksLoc = c.GlobalString(keystoreFlag.Name)
|
ksLoc = c.GlobalString(keystoreFlag.Name)
|
||||||
|
|
@ -427,7 +425,7 @@ func signer(c *cli.Context) error {
|
||||||
log.Info("Starting signer", "chainid", chainId, "keystore", ksLoc,
|
log.Info("Starting signer", "chainid", chainId, "keystore", ksLoc,
|
||||||
"light-kdf", lightKdf, "advanced", advanced)
|
"light-kdf", lightKdf, "advanced", advanced)
|
||||||
am := core.StartClefAccountManager(ksLoc, nousb, lightKdf)
|
am := core.StartClefAccountManager(ksLoc, nousb, lightKdf)
|
||||||
apiImpl := core.NewSignerAPI(am, chainId, nousb, ui, db, advanced)
|
apiImpl := core.NewSignerAPI(am, chainId, nousb, ui, db, advanced, pwStorage)
|
||||||
|
|
||||||
// Establish the bidirectional communication, by creating a new UI backend and registering
|
// Establish the bidirectional communication, by creating a new UI backend and registering
|
||||||
// it with the UI.
|
// it with the UI.
|
||||||
|
|
@ -798,7 +796,7 @@ func GenDoc(ctx *cli.Context) {
|
||||||
}
|
}
|
||||||
{ // Sign plain text response
|
{ // Sign plain text response
|
||||||
add("SignDataResponse - approve", "Response to SignDataRequest",
|
add("SignDataResponse - approve", "Response to SignDataRequest",
|
||||||
&core.SignDataResponse{Password: "apassword", Approved: true})
|
&core.SignDataResponse{Approved: true})
|
||||||
add("SignDataResponse - deny", "Response to SignDataRequest",
|
add("SignDataResponse - deny", "Response to SignDataRequest",
|
||||||
&core.SignDataResponse{})
|
&core.SignDataResponse{})
|
||||||
}
|
}
|
||||||
|
|
@ -833,9 +831,9 @@ func GenDoc(ctx *cli.Context) {
|
||||||
}
|
}
|
||||||
{ // Sign tx response
|
{ // Sign tx response
|
||||||
data := hexutil.Bytes([]byte{0x04, 0x03, 0x02, 0x01})
|
data := hexutil.Bytes([]byte{0x04, 0x03, 0x02, 0x01})
|
||||||
add("SignDataResponse - approve", "Response to SignDataRequest. This response needs to contain the `transaction`"+
|
add("SignTxResponse - approve", "Response to request to sign a transaction. This response needs to contain the `transaction`"+
|
||||||
", because the UI is free to make modifications to the transaction.",
|
", because the UI is free to make modifications to the transaction.",
|
||||||
&core.SignTxResponse{Password: "apassword", Approved: true,
|
&core.SignTxResponse{Approved: true,
|
||||||
Transaction: core.SendTxArgs{
|
Transaction: core.SendTxArgs{
|
||||||
Data: &data,
|
Data: &data,
|
||||||
Nonce: 0x4,
|
Nonce: 0x4,
|
||||||
|
|
@ -846,9 +844,9 @@ func GenDoc(ctx *cli.Context) {
|
||||||
Gas: 1000,
|
Gas: 1000,
|
||||||
Input: nil,
|
Input: nil,
|
||||||
}})
|
}})
|
||||||
add("SignDataResponse - deny", "Response to SignDataRequest. When denying a request, there's no need to "+
|
add("SignTxResponse - deny", "Response to SignTxRequest. When denying a request, there's no need to "+
|
||||||
"provide the transaction in return",
|
"provide the transaction in return",
|
||||||
&core.SignDataResponse{})
|
&core.SignTxResponse{})
|
||||||
}
|
}
|
||||||
{ // WHen a signed tx is ready to go out
|
{ // WHen a signed tx is ready to go out
|
||||||
desc := "SignTransactionResult is used in the call `clef` -> `OnApprovedTx(result)`" +
|
desc := "SignTransactionResult is used in the call `clef` -> `OnApprovedTx(result)`" +
|
||||||
|
|
@ -874,7 +872,7 @@ func GenDoc(ctx *cli.Context) {
|
||||||
{ // User input
|
{ // User input
|
||||||
add("UserInputRequest", "Sent when clef needs the user to provide data. If 'password' is true, the input field should be treated accordingly (echo-free)",
|
add("UserInputRequest", "Sent when clef needs the user to provide data. If 'password' is true, the input field should be treated accordingly (echo-free)",
|
||||||
&core.UserInputRequest{IsPassword: true, Title: "The title here", Prompt: "The question to ask the user"})
|
&core.UserInputRequest{IsPassword: true, Title: "The title here", Prompt: "The question to ask the user"})
|
||||||
add("UserInputResponse", "Response to SignDataRequest",
|
add("UserInputResponse", "Response to UserInputRequest",
|
||||||
&core.UserInputResponse{Text: "The textual response from user"})
|
&core.UserInputResponse{Text: "The textual response from user"})
|
||||||
}
|
}
|
||||||
{ // List request
|
{ // List request
|
||||||
|
|
@ -888,7 +886,7 @@ func GenDoc(ctx *cli.Context) {
|
||||||
{b, accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/b"}}},
|
{b, accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/b"}}},
|
||||||
})
|
})
|
||||||
|
|
||||||
add("UserInputResponse", "Response to list request. The response contains a list of all addresses to show to the caller. "+
|
add("ListResponse", "Response to list request. The response contains a list of all addresses to show to the caller. "+
|
||||||
"Note: the UI is free to respond with any address the caller, regardless of whether it exists or not",
|
"Note: the UI is free to respond with any address the caller, regardless of whether it exists or not",
|
||||||
&core.ListResponse{
|
&core.ListResponse{
|
||||||
Accounts: []accounts.Account{
|
Accounts: []accounts.Account{
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,10 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/signer/storage"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
|
|
@ -97,6 +99,7 @@ type SignerAPI struct {
|
||||||
UI UIClientAPI
|
UI UIClientAPI
|
||||||
validator *Validator
|
validator *Validator
|
||||||
rejectMode bool
|
rejectMode bool
|
||||||
|
credentials storage.Storage
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata about a request
|
// Metadata about a request
|
||||||
|
|
@ -231,11 +234,11 @@ var ErrRequestDenied = errors.New("Request denied")
|
||||||
// key that is generated when a new Account is created.
|
// key that is generated when a new Account is created.
|
||||||
// noUSB disables USB support that is required to support hardware devices such as
|
// noUSB disables USB support that is required to support hardware devices such as
|
||||||
// ledger and trezor.
|
// 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 {
|
if advancedMode {
|
||||||
log.Info("Clef is in advanced mode: will warn instead of reject")
|
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 {
|
if !noUSB {
|
||||||
signer.startUSBListener()
|
signer.startUSBListener()
|
||||||
}
|
}
|
||||||
|
|
@ -356,24 +359,27 @@ func (api *SignerAPI) New(ctx context.Context) (common.Address, error) {
|
||||||
if len(be) == 0 {
|
if len(be) == 0 {
|
||||||
return common.Address{}, errors.New("password based accounts not supported")
|
return common.Address{}, errors.New("password based accounts not supported")
|
||||||
}
|
}
|
||||||
var (
|
if resp, err := api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)}); err != nil {
|
||||||
resp NewAccountResponse
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
// Three retries to get a valid password
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
resp, err = api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)})
|
|
||||||
if err != nil {
|
|
||||||
return common.Address{}, err
|
return common.Address{}, err
|
||||||
}
|
} else if !resp.Approved {
|
||||||
if !resp.Approved {
|
|
||||||
return common.Address{}, ErrRequestDenied
|
return common.Address{}, ErrRequestDenied
|
||||||
}
|
}
|
||||||
if pwErr := ValidatePasswordFormat(resp.Password); pwErr != nil {
|
|
||||||
|
// Three retries to get a valid password
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
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 {
|
||||||
|
log.Warn("error obtaining password", "attempt", i, "error", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
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))
|
api.UI.ShowError(fmt.Sprintf("Account creation attempt #%d failed due to password requirements: %v", (i + 1), pwErr))
|
||||||
} else {
|
} else {
|
||||||
// No error
|
// No error
|
||||||
acc, err := be[0].(*keystore.KeyStore).NewAccount(resp.Password)
|
acc, err := be[0].(*keystore.KeyStore).NewAccount(resp.Text)
|
||||||
return acc.Address, err
|
return acc.Address, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -427,6 +433,24 @@ func logDiff(original *SignTxRequest, new *SignTxResponse) bool {
|
||||||
return modified
|
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
|
// 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) {
|
func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) {
|
||||||
var (
|
var (
|
||||||
|
|
@ -470,9 +494,14 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, meth
|
||||||
}
|
}
|
||||||
// Convert fields into a real transaction
|
// Convert fields into a real transaction
|
||||||
var unsignedTx = result.Transaction.toTransaction()
|
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
|
// 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 {
|
if err != nil {
|
||||||
api.UI.ShowError(err.Error())
|
api.UI.ShowError(err.Error())
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ package core
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/signer/storage"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -38,47 +38,45 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
//Used for testing
|
//Used for testing
|
||||||
type HeadlessUI struct {
|
type headlessUi struct {
|
||||||
controller chan string
|
approveCh chan string // to send approve/deny
|
||||||
|
inputCh chan string // to send password
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *HeadlessUI) OnInputRequired(info UserInputRequest) (UserInputResponse, error) {
|
func (ui *headlessUi) OnInputRequired(info UserInputRequest) (UserInputResponse, error) {
|
||||||
return UserInputResponse{}, errors.New("not implemented")
|
input := <-ui.inputCh
|
||||||
|
return UserInputResponse{Text: input}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *HeadlessUI) OnSignerStartup(info StartupInfo) {
|
func (ui *headlessUi) OnSignerStartup(info StartupInfo) {}
|
||||||
}
|
func (ui *headlessUi) RegisterUIServer(api *UIServerAPI) {}
|
||||||
func (ui *HeadlessUI) RegisterUIServer(api *UIServerAPI) {
|
func (ui *headlessUi) OnApprovedTx(tx ethapi.SignTransactionResult) {}
|
||||||
}
|
|
||||||
|
|
||||||
func (ui *HeadlessUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
func (ui *headlessUi) ApproveTx(request *SignTxRequest) (SignTxResponse, error) {
|
||||||
fmt.Printf("OnApproved()\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ui *HeadlessUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) {
|
switch <-ui.approveCh {
|
||||||
|
|
||||||
switch <-ui.controller {
|
|
||||||
case "Y":
|
case "Y":
|
||||||
return SignTxResponse{request.Transaction, true, <-ui.controller}, nil
|
return SignTxResponse{request.Transaction, true}, nil
|
||||||
case "M": //Modify
|
case "M": // modify
|
||||||
|
// The headless UI always modifies the transaction
|
||||||
old := big.Int(request.Transaction.Value)
|
old := big.Int(request.Transaction.Value)
|
||||||
newVal := big.NewInt(0).Add(&old, big.NewInt(1))
|
newVal := big.NewInt(0).Add(&old, big.NewInt(1))
|
||||||
request.Transaction.Value = hexutil.Big(*newVal)
|
request.Transaction.Value = hexutil.Big(*newVal)
|
||||||
return SignTxResponse{request.Transaction, true, <-ui.controller}, nil
|
return SignTxResponse{request.Transaction, true}, nil
|
||||||
default:
|
default:
|
||||||
return SignTxResponse{request.Transaction, false, ""}, nil
|
return SignTxResponse{request.Transaction, false}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *HeadlessUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) {
|
func (ui *headlessUi) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) {
|
||||||
if "Y" == <-ui.controller {
|
approved := "Y" == <-ui.approveCh
|
||||||
return SignDataResponse{true, <-ui.controller}, nil
|
return SignDataResponse{approved}, nil
|
||||||
}
|
|
||||||
return SignDataResponse{false, ""}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *HeadlessUI) ApproveListing(request *ListRequest) (ListResponse, error) {
|
func (ui *headlessUi) ApproveListing(request *ListRequest) (ListResponse, error) {
|
||||||
switch <-ui.controller {
|
approval := <-ui.approveCh
|
||||||
|
//fmt.Printf("approval %s\n", approval)
|
||||||
|
switch approval {
|
||||||
case "A":
|
case "A":
|
||||||
return ListResponse{request.Accounts}, nil
|
return ListResponse{request.Accounts}, nil
|
||||||
case "1":
|
case "1":
|
||||||
|
|
@ -90,19 +88,19 @@ func (ui *HeadlessUI) ApproveListing(request *ListRequest) (ListResponse, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *HeadlessUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) {
|
func (ui *headlessUi) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) {
|
||||||
if "Y" == <-ui.controller {
|
if "Y" == <-ui.approveCh {
|
||||||
return NewAccountResponse{true, <-ui.controller}, nil
|
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
|
//stdout is used by communication
|
||||||
fmt.Fprintln(os.Stderr, message)
|
fmt.Fprintln(os.Stderr, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ui *HeadlessUI) ShowInfo(message string) {
|
func (ui *headlessUi) ShowInfo(message string) {
|
||||||
//stdout is used by communication
|
//stdout is used by communication
|
||||||
fmt.Fprintln(os.Stderr, message)
|
fmt.Fprintln(os.Stderr, message)
|
||||||
}
|
}
|
||||||
|
|
@ -119,25 +117,20 @@ func tmpDirName(t *testing.T) string {
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func setup(t *testing.T) (*SignerAPI, chan string) {
|
func setup(t *testing.T) (*SignerAPI, *headlessUi) {
|
||||||
|
|
||||||
controller := make(chan string, 20)
|
|
||||||
|
|
||||||
db, err := NewAbiDBFromFile("../../cmd/clef/4byte.json")
|
db, err := NewAbiDBFromFile("../../cmd/clef/4byte.json")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err.Error())
|
t.Fatal(err.Error())
|
||||||
}
|
}
|
||||||
var (
|
ui := &headlessUi{make(chan string, 20), make(chan string, 20)}
|
||||||
ui = &HeadlessUI{controller}
|
am := StartClefAccountManager(tmpDirName(t), true, true)
|
||||||
am = StartClefAccountManager(tmpDirName(t), true, true)
|
api := NewSignerAPI(am, 1337, true, ui, db, true, &storage.NoStorage{})
|
||||||
api = NewSignerAPI(am, 1337, true, ui, db, true)
|
return api, ui
|
||||||
)
|
|
||||||
return api, controller
|
|
||||||
}
|
|
||||||
func createAccount(control chan string, api *SignerAPI, t *testing.T) {
|
|
||||||
|
|
||||||
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())
|
_, err := api.New(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -146,14 +139,13 @@ func createAccount(control chan string, api *SignerAPI, t *testing.T) {
|
||||||
time.Sleep(250 * time.Millisecond)
|
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"
|
ui.approveCh <- "Y"
|
||||||
control <- password
|
// We will be asked three times to provide a suitable password
|
||||||
control <- "Y"
|
ui.inputCh <- password
|
||||||
control <- password
|
ui.inputCh <- password
|
||||||
control <- "Y"
|
ui.inputCh <- password
|
||||||
control <- password
|
|
||||||
|
|
||||||
addr, err := api.New(context.Background())
|
addr, err := api.New(context.Background())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
@ -164,8 +156,8 @@ func failCreateAccountWithPassword(control chan string, api *SignerAPI, password
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func failCreateAccount(control chan string, api *SignerAPI, t *testing.T) {
|
func failCreateAccount(ui *headlessUi, api *SignerAPI, t *testing.T) {
|
||||||
control <- "N"
|
ui.approveCh <- "N"
|
||||||
addr, err := api.New(context.Background())
|
addr, err := api.New(context.Background())
|
||||||
if err != ErrRequestDenied {
|
if err != ErrRequestDenied {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -175,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 {
|
func list(ui *headlessUi, api *SignerAPI, t *testing.T) ([]common.Address, error) {
|
||||||
control <- "A"
|
ui.approveCh <- "A"
|
||||||
list, err := api.List(context.Background())
|
return api.List(context.Background())
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
return list
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewAcc(t *testing.T) {
|
func TestNewAcc(t *testing.T) {
|
||||||
api, control := setup(t)
|
api, control := setup(t)
|
||||||
verifyNum := func(num int) {
|
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))
|
t.Errorf("Expected %d accounts, got %d", num, len(list))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -200,18 +193,16 @@ func TestNewAcc(t *testing.T) {
|
||||||
failCreateAccount(control, api, t)
|
failCreateAccount(control, api, t)
|
||||||
createAccount(control, api, t)
|
createAccount(control, api, t)
|
||||||
failCreateAccount(control, api, t)
|
failCreateAccount(control, api, t)
|
||||||
|
|
||||||
verifyNum(4)
|
verifyNum(4)
|
||||||
|
|
||||||
// Fail to create this, due to bad password
|
// Fail to create this, due to bad password
|
||||||
failCreateAccountWithPassword(control, api, "short", t)
|
failCreateAccountWithPassword(control, api, "short", t)
|
||||||
failCreateAccountWithPassword(control, api, "longerbutbad\rfoo", t)
|
failCreateAccountWithPassword(control, api, "longerbutbad\rfoo", t)
|
||||||
|
|
||||||
verifyNum(4)
|
verifyNum(4)
|
||||||
|
|
||||||
// Testing listing:
|
// Testing listing:
|
||||||
// Listing one Account
|
// Listing one Account
|
||||||
control <- "1"
|
control.approveCh <- "1"
|
||||||
list, err := api.List(context.Background())
|
list, err := api.List(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -220,7 +211,7 @@ func TestNewAcc(t *testing.T) {
|
||||||
t.Fatalf("List should only show one Account")
|
t.Fatalf("List should only show one Account")
|
||||||
}
|
}
|
||||||
// Listing denied
|
// Listing denied
|
||||||
control <- "Nope"
|
control.approveCh <- "Nope"
|
||||||
list, err = api.List(context.Background())
|
list, err = api.List(context.Background())
|
||||||
if len(list) != 0 {
|
if len(list) != 0 {
|
||||||
t.Fatalf("List should be empty")
|
t.Fatalf("List should be empty")
|
||||||
|
|
@ -257,7 +248,7 @@ func TestSignTx(t *testing.T) {
|
||||||
|
|
||||||
api, control := setup(t)
|
api, control := setup(t)
|
||||||
createAccount(control, api, t)
|
createAccount(control, api, t)
|
||||||
control <- "A"
|
control.approveCh <- "A"
|
||||||
list, err = api.List(context.Background())
|
list, err = api.List(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -267,8 +258,8 @@ func TestSignTx(t *testing.T) {
|
||||||
methodSig := "test(uint)"
|
methodSig := "test(uint)"
|
||||||
tx := mkTestTx(a)
|
tx := mkTestTx(a)
|
||||||
|
|
||||||
control <- "Y"
|
control.approveCh <- "Y"
|
||||||
control <- "wrongpassword"
|
control.inputCh <- "wrongpassword"
|
||||||
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
||||||
if res != nil {
|
if res != nil {
|
||||||
t.Errorf("Expected nil-response, got %v", res)
|
t.Errorf("Expected nil-response, got %v", res)
|
||||||
|
|
@ -276,7 +267,7 @@ func TestSignTx(t *testing.T) {
|
||||||
if err != keystore.ErrDecrypt {
|
if err != keystore.ErrDecrypt {
|
||||||
t.Errorf("Expected ErrLocked! %v", err)
|
t.Errorf("Expected ErrLocked! %v", err)
|
||||||
}
|
}
|
||||||
control <- "No way"
|
control.approveCh <- "No way"
|
||||||
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
||||||
if res != nil {
|
if res != nil {
|
||||||
t.Errorf("Expected nil-response, got %v", res)
|
t.Errorf("Expected nil-response, got %v", res)
|
||||||
|
|
@ -284,8 +275,9 @@ func TestSignTx(t *testing.T) {
|
||||||
if err != ErrRequestDenied {
|
if err != ErrRequestDenied {
|
||||||
t.Errorf("Expected ErrRequestDenied! %v", err)
|
t.Errorf("Expected ErrRequestDenied! %v", err)
|
||||||
}
|
}
|
||||||
control <- "Y"
|
// Sign with correct password
|
||||||
control <- "a_long_password"
|
control.approveCh <- "Y"
|
||||||
|
control.inputCh <- "a_long_password"
|
||||||
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -298,8 +290,8 @@ func TestSignTx(t *testing.T) {
|
||||||
if parsedTx.Value().Cmp(tx.Value.ToInt()) != 0 {
|
if parsedTx.Value().Cmp(tx.Value.ToInt()) != 0 {
|
||||||
t.Errorf("Expected value to be unchanged, expected %v got %v", tx.Value, parsedTx.Value())
|
t.Errorf("Expected value to be unchanged, expected %v got %v", tx.Value, parsedTx.Value())
|
||||||
}
|
}
|
||||||
control <- "Y"
|
control.approveCh <- "Y"
|
||||||
control <- "a_long_password"
|
control.inputCh <- "a_long_password"
|
||||||
|
|
||||||
res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -310,8 +302,8 @@ func TestSignTx(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//The tx is modified by the UI
|
//The tx is modified by the UI
|
||||||
control <- "M"
|
control.approveCh <- "M"
|
||||||
control <- "a_long_password"
|
control.inputCh <- "a_long_password"
|
||||||
|
|
||||||
res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -329,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)
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
|
||||||
|
|
@ -156,9 +156,9 @@ func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, erro
|
||||||
showMetadata(request.Meta)
|
showMetadata(request.Meta)
|
||||||
fmt.Printf("-------------------------------------------\n")
|
fmt.Printf("-------------------------------------------\n")
|
||||||
if !ui.confirm() {
|
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
|
// ApproveSignData prompt the user for confirmation to request to sign data
|
||||||
|
|
@ -178,9 +178,9 @@ func (ui *CommandlineUI) ApproveSignData(request *SignDataRequest) (SignDataResp
|
||||||
fmt.Printf("-------------------------------------------\n")
|
fmt.Printf("-------------------------------------------\n")
|
||||||
showMetadata(request.Meta)
|
showMetadata(request.Meta)
|
||||||
if !ui.confirm() {
|
if !ui.confirm() {
|
||||||
return SignDataResponse{false, ""}, nil
|
return SignDataResponse{false}, nil
|
||||||
}
|
}
|
||||||
return SignDataResponse{true, ui.readPassword()}, nil
|
return SignDataResponse{true}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ApproveListing prompt the user for confirmation to list accounts
|
// ApproveListing prompt the user for confirmation to list accounts
|
||||||
|
|
@ -217,9 +217,9 @@ func (ui *CommandlineUI) ApproveNewAccount(request *NewAccountRequest) (NewAccou
|
||||||
fmt.Printf("and the address is returned to the external caller\n\n")
|
fmt.Printf("and the address is returned to the external caller\n\n")
|
||||||
showMetadata(request.Meta)
|
showMetadata(request.Meta)
|
||||||
if !ui.confirm() {
|
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
|
// ShowError displays error message to user
|
||||||
|
|
|
||||||
|
|
@ -139,8 +139,14 @@ func (api *SignerAPI) sign(addr common.MixedcaseAddress, req *SignDataRequest, l
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -179,15 +179,15 @@ func TestSignData(t *testing.T) {
|
||||||
//Create two accounts
|
//Create two accounts
|
||||||
createAccount(control, api, t)
|
createAccount(control, api, t)
|
||||||
createAccount(control, api, t)
|
createAccount(control, api, t)
|
||||||
control <- "1"
|
control.approveCh <- "1"
|
||||||
list, err := api.List(context.Background())
|
list, err := api.List(context.Background())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
a := common.NewMixedcaseAddress(list[0])
|
a := common.NewMixedcaseAddress(list[0])
|
||||||
|
|
||||||
control <- "Y"
|
control.approveCh <- "Y"
|
||||||
control <- "wrongpassword"
|
control.inputCh <- "wrongpassword"
|
||||||
signature, err := api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
|
signature, err := api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
|
||||||
if signature != nil {
|
if signature != nil {
|
||||||
t.Errorf("Expected nil-data, got %x", signature)
|
t.Errorf("Expected nil-data, got %x", signature)
|
||||||
|
|
@ -195,7 +195,7 @@ func TestSignData(t *testing.T) {
|
||||||
if err != keystore.ErrDecrypt {
|
if err != keystore.ErrDecrypt {
|
||||||
t.Errorf("Expected ErrLocked! '%v'", err)
|
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")))
|
signature, err = api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
|
||||||
if signature != nil {
|
if signature != nil {
|
||||||
t.Errorf("Expected nil-data, got %x", signature)
|
t.Errorf("Expected nil-data, got %x", signature)
|
||||||
|
|
@ -204,8 +204,8 @@ func TestSignData(t *testing.T) {
|
||||||
t.Errorf("Expected ErrRequestDenied! '%v'", err)
|
t.Errorf("Expected ErrRequestDenied! '%v'", err)
|
||||||
}
|
}
|
||||||
// text/plain
|
// text/plain
|
||||||
control <- "Y"
|
control.approveCh <- "Y"
|
||||||
control <- "a_long_password"
|
control.inputCh <- "a_long_password"
|
||||||
signature, err = api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
|
signature, err = api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
@ -214,8 +214,8 @@ func TestSignData(t *testing.T) {
|
||||||
t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature))
|
t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature))
|
||||||
}
|
}
|
||||||
// data/typed
|
// data/typed
|
||||||
control <- "Y"
|
control.approveCh <- "Y"
|
||||||
control <- "a_long_password"
|
control.inputCh <- "a_long_password"
|
||||||
signature, err = api.SignTypedData(context.Background(), a, typedData)
|
signature, err = api.SignTypedData(context.Background(), a, typedData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/signer/core"
|
"github.com/ethereum/go-ethereum/signer/core"
|
||||||
|
|
@ -42,7 +41,7 @@ func consoleOutput(call otto.FunctionCall) otto.Value {
|
||||||
for _, argument := range call.ArgumentList {
|
for _, argument := range call.ArgumentList {
|
||||||
output = append(output, fmt.Sprintf("%v", argument))
|
output = append(output, fmt.Sprintf("%v", argument))
|
||||||
}
|
}
|
||||||
fmt.Fprintln(os.Stdout, strings.Join(output, " "))
|
fmt.Fprintln(os.Stderr, strings.Join(output, " "))
|
||||||
return otto.Value{}
|
return otto.Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,15 +50,13 @@ func consoleOutput(call otto.FunctionCall) otto.Value {
|
||||||
type rulesetUI struct {
|
type rulesetUI struct {
|
||||||
next core.UIClientAPI // The next handler, for manual processing
|
next core.UIClientAPI // The next handler, for manual processing
|
||||||
storage storage.Storage
|
storage storage.Storage
|
||||||
credentials storage.Storage
|
|
||||||
jsRules string // The rules to use
|
jsRules string // The rules to use
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRuleEvaluator(next core.UIClientAPI, jsbackend, credentialsBackend storage.Storage) (*rulesetUI, error) {
|
func NewRuleEvaluator(next core.UIClientAPI, jsbackend storage.Storage) (*rulesetUI, error) {
|
||||||
c := &rulesetUI{
|
c := &rulesetUI{
|
||||||
next: next,
|
next: next,
|
||||||
storage: jsbackend,
|
storage: jsbackend,
|
||||||
credentials: credentialsBackend,
|
|
||||||
jsRules: "",
|
jsRules: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -153,18 +150,12 @@ func (r *rulesetUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse,
|
||||||
if approved {
|
if approved {
|
||||||
return core.SignTxResponse{
|
return core.SignTxResponse{
|
||||||
Transaction: request.Transaction,
|
Transaction: request.Transaction,
|
||||||
Approved: true,
|
Approved: true},
|
||||||
Password: r.lookupPassword(request.Transaction.From.Address()),
|
|
||||||
},
|
|
||||||
nil
|
nil
|
||||||
}
|
}
|
||||||
return core.SignTxResponse{Approved: false}, err
|
return core.SignTxResponse{Approved: false}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rulesetUI) lookupPassword(address common.Address) string {
|
|
||||||
return r.credentials.Get(strings.ToLower(address.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *rulesetUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
|
func (r *rulesetUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
|
||||||
jsonreq, err := json.Marshal(request)
|
jsonreq, err := json.Marshal(request)
|
||||||
approved, err := r.checkApproval("ApproveSignData", jsonreq, err)
|
approved, err := r.checkApproval("ApproveSignData", jsonreq, err)
|
||||||
|
|
@ -173,9 +164,9 @@ func (r *rulesetUI) ApproveSignData(request *core.SignDataRequest) (core.SignDat
|
||||||
return r.next.ApproveSignData(request)
|
return r.next.ApproveSignData(request)
|
||||||
}
|
}
|
||||||
if approved {
|
if approved {
|
||||||
return core.SignDataResponse{Approved: true, Password: r.lookupPassword(request.Address.Address())}, nil
|
return core.SignDataResponse{Approved: true}, nil
|
||||||
}
|
}
|
||||||
return core.SignDataResponse{Approved: false, Password: ""}, err
|
return core.SignDataResponse{Approved: false}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnInputRequired not handled by rules
|
// OnInputRequired not handled by rules
|
||||||
|
|
|
||||||
|
|
@ -84,11 +84,11 @@ func (alwaysDenyUI) OnSignerStartup(info core.StartupInfo) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (alwaysDenyUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
|
func (alwaysDenyUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
|
||||||
return core.SignTxResponse{Transaction: request.Transaction, Approved: false, Password: ""}, nil
|
return core.SignTxResponse{Transaction: request.Transaction, Approved: false}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (alwaysDenyUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
|
func (alwaysDenyUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
|
||||||
return core.SignDataResponse{Approved: false, Password: ""}, nil
|
return core.SignDataResponse{Approved: false}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (alwaysDenyUI) ApproveListing(request *core.ListRequest) (core.ListResponse, error) {
|
func (alwaysDenyUI) ApproveListing(request *core.ListRequest) (core.ListResponse, error) {
|
||||||
|
|
@ -96,7 +96,7 @@ func (alwaysDenyUI) ApproveListing(request *core.ListRequest) (core.ListResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
func (alwaysDenyUI) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) {
|
func (alwaysDenyUI) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) {
|
||||||
return core.NewAccountResponse{Approved: false, Password: ""}, nil
|
return core.NewAccountResponse{Approved: false}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (alwaysDenyUI) ShowError(message string) {
|
func (alwaysDenyUI) ShowError(message string) {
|
||||||
|
|
@ -112,7 +112,7 @@ func (alwaysDenyUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func initRuleEngine(js string) (*rulesetUI, error) {
|
func initRuleEngine(js string) (*rulesetUI, error) {
|
||||||
r, err := NewRuleEvaluator(&alwaysDenyUI{}, storage.NewEphemeralStorage(), storage.NewEphemeralStorage())
|
r, err := NewRuleEvaluator(&alwaysDenyUI{}, storage.NewEphemeralStorage())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create js engine: %v", err)
|
return nil, fmt.Errorf("failed to create js engine: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -248,8 +248,7 @@ func TestForwarding(t *testing.T) {
|
||||||
js := ""
|
js := ""
|
||||||
ui := &dummyUI{make([]string, 0)}
|
ui := &dummyUI{make([]string, 0)}
|
||||||
jsBackend := storage.NewEphemeralStorage()
|
jsBackend := storage.NewEphemeralStorage()
|
||||||
credBackend := storage.NewEphemeralStorage()
|
r, err := NewRuleEvaluator(ui, jsBackend)
|
||||||
r, err := NewRuleEvaluator(ui, jsBackend, credBackend)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create js engine: %v", err)
|
t.Fatalf("Failed to create js engine: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -567,7 +566,7 @@ func TestContextIsCleared(t *testing.T) {
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
ui := &dontCallMe{t}
|
ui := &dontCallMe{t}
|
||||||
r, err := NewRuleEvaluator(ui, storage.NewEphemeralStorage(), storage.NewEphemeralStorage())
|
r, err := NewRuleEvaluator(ui, storage.NewEphemeralStorage())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create js engine: %v", err)
|
t.Fatalf("Failed to create js engine: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,10 +17,6 @@
|
||||||
|
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Storage interface {
|
type Storage interface {
|
||||||
// Put stores a value by key. 0-length keys results in no-op
|
// Put stores a value by key. 0-length keys results in no-op
|
||||||
Put(key, value string)
|
Put(key, value string)
|
||||||
|
|
@ -39,7 +35,7 @@ func (s *EphemeralStorage) Put(key, value string) {
|
||||||
if len(key) == 0 {
|
if len(key) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Printf("storage: put %v -> %v\n", key, value)
|
//fmt.Printf("storage: put %v -> %v\n", key, value)
|
||||||
s.data[key] = value
|
s.data[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,7 +43,7 @@ func (s *EphemeralStorage) Get(key string) string {
|
||||||
if len(key) == 0 {
|
if len(key) == 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
fmt.Printf("storage: get %v\n", key)
|
//fmt.Printf("storage: get %v\n", key)
|
||||||
if v, exist := s.data[key]; exist {
|
if v, exist := s.data[key]; exist {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
@ -60,3 +56,11 @@ func NewEphemeralStorage() Storage {
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NoStorage is a dummy construct which doesn't remember anything you tell it
|
||||||
|
type NoStorage struct{}
|
||||||
|
|
||||||
|
func (s *NoStorage) Put(key, value string) {}
|
||||||
|
func (s *NoStorage) Get(key string) string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue