Merge pull request #3417 from karalabe/mobile-polishes
Account management API polishes
This commit is contained in:
commit
bb2e99dfc2
@ -152,8 +152,8 @@ func (am *Manager) Sign(addr common.Address, hash []byte) ([]byte, error) {
|
|||||||
// SignWithPassphrase signs hash if the private key matching the given address
|
// SignWithPassphrase signs hash if the private key matching the given address
|
||||||
// can be decrypted with the given passphrase. The produced signature is in the
|
// can be decrypted with the given passphrase. The produced signature is in the
|
||||||
// [R || S || V] format where V is 0 or 1.
|
// [R || S || V] format where V is 0 or 1.
|
||||||
func (am *Manager) SignWithPassphrase(addr common.Address, passphrase string, hash []byte) (signature []byte, err error) {
|
func (am *Manager) SignWithPassphrase(a Account, passphrase string, hash []byte) (signature []byte, err error) {
|
||||||
_, key, err := am.getDecryptedKey(Account{Address: addr}, passphrase)
|
_, key, err := am.getDecryptedKey(a, passphrase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ func TestSignWithPassphrase(t *testing.T) {
|
|||||||
t.Fatal("expected account to be locked")
|
t.Fatal("expected account to be locked")
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = am.SignWithPassphrase(acc.Address, pass, testSigData)
|
_, err = am.SignWithPassphrase(acc, pass, testSigData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ func TestSignWithPassphrase(t *testing.T) {
|
|||||||
t.Fatal("expected account to be locked")
|
t.Fatal("expected account to be locked")
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err = am.SignWithPassphrase(acc.Address, "invalid passwd", testSigData); err == nil {
|
if _, err = am.SignWithPassphrase(acc, "invalid passwd", testSigData); err == nil {
|
||||||
t.Fatal("expected SignHash to fail with invalid password")
|
t.Fatal("expected SignHash to fail with invalid password")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
|
|||||||
}
|
}
|
||||||
tx := args.toTransaction()
|
tx := args.toTransaction()
|
||||||
signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number())
|
signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number())
|
||||||
signature, err := s.am.SignWithPassphrase(args.From, passwd, signer.Hash(tx).Bytes())
|
signature, err := s.am.SignWithPassphrase(accounts.Account{Address: args.From}, passwd, signer.Hash(tx).Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
@ -294,11 +294,11 @@ func signHash(data []byte) []byte {
|
|||||||
//
|
//
|
||||||
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
|
// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
|
||||||
func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) {
|
func (s *PrivateAccountAPI) Sign(ctx context.Context, data hexutil.Bytes, addr common.Address, passwd string) (hexutil.Bytes, error) {
|
||||||
signature, err := s.b.AccountManager().SignWithPassphrase(addr, passwd, signHash(data))
|
signature, err := s.b.AccountManager().SignWithPassphrase(accounts.Account{Address: addr}, passwd, signHash(data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
signature[64] += 27 // SignWithPassphrase uses canonical secp256k1 signatures (v = 0 or 1), transform to yellow paper
|
signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
|
||||||
return signature, nil
|
return signature, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,7 +319,7 @@ func (s *PrivateAccountAPI) EcRecover(ctx context.Context, data, sig hexutil.Byt
|
|||||||
if sig[64] != 27 && sig[64] != 28 {
|
if sig[64] != 27 && sig[64] != 28 {
|
||||||
return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)")
|
return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)")
|
||||||
}
|
}
|
||||||
sig[64] -= 27 // Transform yellow paper signatures to canonical secp256k1 form
|
sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1
|
||||||
|
|
||||||
rpk, err := crypto.Ecrecover(signHash(data), sig)
|
rpk, err := crypto.Ecrecover(signHash(data), sig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1104,8 +1104,7 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod
|
|||||||
func (s *PublicTransactionPoolAPI) Sign(addr common.Address, data hexutil.Bytes) (hexutil.Bytes, error) {
|
func (s *PublicTransactionPoolAPI) Sign(addr common.Address, data hexutil.Bytes) (hexutil.Bytes, error) {
|
||||||
signature, err := s.b.AccountManager().Sign(addr, signHash(data))
|
signature, err := s.b.AccountManager().Sign(addr, signHash(data))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// Sign uses canonical secp256k1 signatures (v = 0 or 1), transform to yellow paper
|
signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
|
||||||
signature[64] += 27
|
|
||||||
}
|
}
|
||||||
return signature, err
|
return signature, err
|
||||||
}
|
}
|
||||||
|
@ -109,15 +109,17 @@ func (am *AccountManager) DeleteAccount(account *Account, passphrase string) err
|
|||||||
}, passphrase)
|
}, passphrase)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign signs hash with an unlocked private key matching the given address.
|
// Sign calculates a ECDSA signature for the given hash. The produced signature
|
||||||
|
// is in the [R || S || V] format where V is 0 or 1.
|
||||||
func (am *AccountManager) Sign(address *Address, hash []byte) (signature []byte, _ error) {
|
func (am *AccountManager) Sign(address *Address, hash []byte) (signature []byte, _ error) {
|
||||||
return am.manager.Sign(address.address, hash)
|
return am.manager.Sign(address.address, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SignWithPassphrase signs hash if the private key matching the given address can be
|
// SignWithPassphrase signs hash if the private key matching the given address
|
||||||
// decrypted with the given passphrase.
|
// can be decrypted with the given passphrase. The produced signature is in the
|
||||||
func (am *AccountManager) SignWithPassphrase(address *Address, passphrase string, hash []byte) (signature []byte, _ error) {
|
// [R || S || V] format where V is 0 or 1.
|
||||||
return am.manager.SignWithPassphrase(address.address, passphrase, hash)
|
func (am *AccountManager) SignWithPassphrase(account *Account, passphrase string, hash []byte) (signature []byte, _ error) {
|
||||||
|
return am.manager.SignWithPassphrase(account.account, passphrase, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unlock unlocks the given account indefinitely.
|
// Unlock unlocks the given account indefinitely.
|
||||||
@ -130,15 +132,15 @@ func (am *AccountManager) Lock(address *Address) error {
|
|||||||
return am.manager.Lock(address.address)
|
return am.manager.Lock(address.address)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TimedUnlock unlocks the given account with the passphrase. The account
|
// TimedUnlock unlocks the given account with the passphrase. The account stays
|
||||||
// stays unlocked for the duration of timeout. A timeout of 0 unlocks the account
|
// unlocked for the duration of timeout (nanoseconds). A timeout of 0 unlocks the
|
||||||
// until the program exits. The account must match a unique key file.
|
// account until the program exits. The account must match a unique key file.
|
||||||
//
|
//
|
||||||
// If the account address is already unlocked for a duration, TimedUnlock extends or
|
// If the account address is already unlocked for a duration, TimedUnlock extends or
|
||||||
// shortens the active unlock timeout. If the address was previously unlocked
|
// shortens the active unlock timeout. If the address was previously unlocked
|
||||||
// indefinitely the timeout is not altered.
|
// indefinitely the timeout is not altered.
|
||||||
func (am *AccountManager) TimedUnlock(a *Account, passphrase string, timeout int64) error {
|
func (am *AccountManager) TimedUnlock(account *Account, passphrase string, timeout int64) error {
|
||||||
return am.manager.TimedUnlock(a.account, passphrase, time.Duration(timeout))
|
return am.manager.TimedUnlock(account.account, passphrase, time.Duration(timeout))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAccount generates a new key and stores it into the key directory,
|
// NewAccount generates a new key and stores it into the key directory,
|
||||||
@ -165,8 +167,8 @@ func (am *AccountManager) ImportKey(keyJSON []byte, passphrase, newPassphrase st
|
|||||||
return &Account{acc}, nil
|
return &Account{acc}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update changes the passphrase of an existing account.
|
// UpdateAccount changes the passphrase of an existing account.
|
||||||
func (am *AccountManager) Update(account *Account, passphrase, newPassphrase string) error {
|
func (am *AccountManager) UpdateAccount(account *Account, passphrase, newPassphrase string) error {
|
||||||
return am.manager.Update(account.account, passphrase, newPassphrase)
|
return am.manager.Update(account.account, passphrase, newPassphrase)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,9 +14,6 @@
|
|||||||
// You should have received a copy of the GNU Lesser General Public License
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
// Contains all the wrappers from the accounts package to support client side key
|
|
||||||
// management on mobile platforms.
|
|
||||||
|
|
||||||
package geth
|
package geth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -46,14 +43,42 @@ public class AndroidTest extends InstrumentationTestCase {
|
|||||||
public AndroidTest() {}
|
public AndroidTest() {}
|
||||||
|
|
||||||
public void testAccountManagement() {
|
public void testAccountManagement() {
|
||||||
try {
|
// Create an encrypted keystore manager with light crypto parameters.
|
||||||
AccountManager am = new AccountManager(getInstrumentation().getContext().getFilesDir() + "/keystore", Geth.LightScryptN, Geth.LightScryptP);
|
AccountManager am = new AccountManager(getInstrumentation().getContext().getFilesDir() + "/keystore", Geth.LightScryptN, Geth.LightScryptP);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create a new account with the specified encryption passphrase.
|
||||||
Account newAcc = am.newAccount("Creation password");
|
Account newAcc = am.newAccount("Creation password");
|
||||||
|
|
||||||
|
// Export the newly created account with a different passphrase. The returned
|
||||||
|
// data from this method invocation is a JSON encoded, encrypted key-file.
|
||||||
byte[] jsonAcc = am.exportKey(newAcc, "Creation password", "Export password");
|
byte[] jsonAcc = am.exportKey(newAcc, "Creation password", "Export password");
|
||||||
|
|
||||||
am.deleteAccount(newAcc, "Creation password");
|
// Update the passphrase on the account created above inside the local keystore.
|
||||||
|
am.updateAccount(newAcc, "Creation password", "Update password");
|
||||||
|
|
||||||
|
// Delete the account updated above from the local keystore.
|
||||||
|
am.deleteAccount(newAcc, "Update password");
|
||||||
|
|
||||||
|
// Import back the account we've exported (and then deleted) above with yet
|
||||||
|
// again a fresh passphrase.
|
||||||
Account impAcc = am.importKey(jsonAcc, "Export password", "Import password");
|
Account impAcc = am.importKey(jsonAcc, "Export password", "Import password");
|
||||||
|
|
||||||
|
// Create a new account to sign transactions with
|
||||||
|
Account signer = am.newAccount("Signer password");
|
||||||
|
Hash txHash = new Hash("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef");
|
||||||
|
|
||||||
|
// Sign a transaction with a single authorization
|
||||||
|
byte[] signature = am.signWithPassphrase(signer, "Signer password", txHash.getBytes());
|
||||||
|
|
||||||
|
// Sign a transaction with multiple manually cancelled authorizations
|
||||||
|
am.unlock(signer, "Signer password");
|
||||||
|
signature = am.sign(signer.getAddress(), txHash.getBytes());
|
||||||
|
am.lock(signer.getAddress());
|
||||||
|
|
||||||
|
// Sign a transaction with multiple automatically cancelled authorizations
|
||||||
|
am.timedUnlock(signer, "Signer password", 1000000000);
|
||||||
|
signature = am.sign(signer.getAddress(), txHash.getBytes());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
fail(e.toString());
|
fail(e.toString());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user