diff --git a/console/bridge.go b/console/bridge.go index 22ed7192bb..14509ea428 100644 --- a/console/bridge.go +++ b/console/bridge.go @@ -126,6 +126,44 @@ func (b *bridge) UnlockAccount(call otto.FunctionCall) (response otto.Value) { return val } +// Sign is a wrapper around the personal.sign RPC method that uses a non-echoing password +// prompt to aquire the passphrase and executes the original RPC method (saved in +// jeth.sign) with it to actually execute the RPC call. +func (b *bridge) Sign(call otto.FunctionCall) (response otto.Value) { + var ( + hash = call.Argument(0) + account = call.Argument(1) + passwd = call.Argument(2) + ) + + if !hash.IsString() { + throwJSException("first argument must be the hash to sign") + } + if !account.IsString() { + throwJSException("second argument must be the account to sign with") + } + + // if the password is not given or null ask the user, otherwise ensure it's a string + if passwd.IsUndefined() || passwd.IsNull() { + fmt.Fprintf(b.printer, "Give password for account %s\n", account) + if input, err := b.prompter.PromptPassword("Passphrase: "); err != nil { + throwJSException(err.Error()) + } else { + passwd, _ = otto.ToValue(input) + } + } + if !passwd.IsString() { + throwJSException("third argument must be the password to unlock the account") + } + + // Send the request to the backend and return + val, err := call.Otto.Call("jeth.sign", nil, hash, account, passwd) + if err != nil { + throwJSException(err.Error()) + } + return val +} + // Sleep will block the console for the specified number of seconds. func (b *bridge) Sleep(call otto.FunctionCall) (response otto.Value) { if call.Argument(0).IsNumber() { diff --git a/console/console.go b/console/console.go index f224f0c2eb..3cde9b8f53 100644 --- a/console/console.go +++ b/console/console.go @@ -156,10 +156,9 @@ func (c *Console) init(preload []string) error { if err != nil { return err } - // Override the unlockAccount and newAccount methods since these require user interaction. - // Assign the jeth.unlockAccount and jeth.newAccount in the Console the original web3 callbacks. - // These will be called by the jeth.* methods after they got the password from the user and send - // the original web3 request to the backend. + // Override the unlockAccount, newAccount and sign methods since these require user interaction. + // Assign these method in the Console the original web3 callbacks. These will be called by the jeth.* + // methods after they got the password from the user and send the original web3 request to the backend. if obj := personal.Object(); obj != nil { // make sure the personal api is enabled over the interface if _, err = c.jsre.Run(`jeth.unlockAccount = personal.unlockAccount;`); err != nil { return fmt.Errorf("personal.unlockAccount: %v", err) @@ -167,8 +166,12 @@ func (c *Console) init(preload []string) error { if _, err = c.jsre.Run(`jeth.newAccount = personal.newAccount;`); err != nil { return fmt.Errorf("personal.newAccount: %v", err) } + if _, err = c.jsre.Run(`jeth.sign = personal.sign;`); err != nil { + return fmt.Errorf("personal.sign: %v", err) + } obj.Set("unlockAccount", bridge.UnlockAccount) obj.Set("newAccount", bridge.NewAccount) + obj.Set("sign", bridge.Sign) } } // The admin.sleep and admin.sleepBlocks are offered by the console and not by the RPC layer. diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 135f9f8e81..aa3546c357 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -298,6 +298,16 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs return submitTransaction(ctx, s.b, tx, signature) } +// Sign decrypts the private key associated with the given address with the given password. +// If the key was successful decrypted the given hash is signed and the signature is returned. +func (s *PrivateAccountAPI) Sign(ctx context.Context, hash common.Hash, addr common.Address, passwd string) (string, error) { + signature, err := s.b.AccountManager().SignWithPassphrase(addr, passwd, hash.Bytes()) + if err != nil { + return "0x", err + } + return "0x" + hex.EncodeToString(signature), nil +} + // SignAndSendTransaction was renamed to SendTransaction. This method is deprecated // and will be removed in the future. It primary goal is to give clients time to update. func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) { diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index 748b37c84a..db276da5e1 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -428,6 +428,12 @@ web3._extend({ call: 'personal_sendTransaction', params: 2, inputFormatter: [web3._extend.formatters.inputTransactionFormatter, null] + }), + new web3._extend.Method({ + name: 'sign', + call: 'personal_sign', + params: 3, + inputFormatter: [null, web3._extend.formatters.inputAddressFormatter, null] }) ] });