Skip to content

Commit ed0670c

Browse files
accounts/abi/bind: allow specifying signer on transactOpts (#21356)
This commit enables users to specify which signer they want to use while creating their transactOpts. Previously all contract interactions used the homestead signer. Now a user can specify whether they want to sign with homestead or EIP155 and specify the chainID which adds another layer of security. Closes #16484
1 parent 6a4e730 commit ed0670c

File tree

9 files changed

+127
-40
lines changed

9 files changed

+127
-40
lines changed

accounts/abi/bind/auth.go

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,29 @@ import (
2121
"errors"
2222
"io"
2323
"io/ioutil"
24+
"math/big"
2425

2526
"github.com/ethereum/go-ethereum/accounts"
2627
"github.com/ethereum/go-ethereum/accounts/external"
2728
"github.com/ethereum/go-ethereum/accounts/keystore"
2829
"github.com/ethereum/go-ethereum/common"
2930
"github.com/ethereum/go-ethereum/core/types"
3031
"github.com/ethereum/go-ethereum/crypto"
32+
"github.com/ethereum/go-ethereum/log"
3133
)
3234

35+
// ErrNoChainID is returned whenever the user failed to specify a chain id.
36+
var ErrNoChainID = errors.New("no chain id specified")
37+
38+
// ErrNotAuthorized is returned when an account is not properly unlocked.
39+
var ErrNotAuthorized = errors.New("not authorized to sign this account")
40+
3341
// NewTransactor is a utility method to easily create a transaction signer from
3442
// an encrypted json key stream and the associated passphrase.
43+
//
44+
// Deprecated: Use NewTransactorWithChainID instead.
3545
func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
46+
log.Warn("WARNING: NewTransactor has been deprecated in favour of NewTransactorWithChainID")
3647
json, err := ioutil.ReadAll(keyin)
3748
if err != nil {
3849
return nil, err
@@ -45,13 +56,17 @@ func NewTransactor(keyin io.Reader, passphrase string) (*TransactOpts, error) {
4556
}
4657

4758
// NewKeyStoreTransactor is a utility method to easily create a transaction signer from
48-
// a decrypted key from a keystore.
59+
// an decrypted key from a keystore.
60+
//
61+
// Deprecated: Use NewKeyStoreTransactorWithChainID instead.
4962
func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) {
63+
log.Warn("WARNING: NewKeyStoreTransactor has been deprecated in favour of NewTransactorWithChainID")
64+
signer := types.HomesteadSigner{}
5065
return &TransactOpts{
5166
From: account.Address,
52-
Signer: func(signer types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) {
67+
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
5368
if address != account.Address {
54-
return nil, errors.New("not authorized to sign this account")
69+
return nil, ErrNotAuthorized
5570
}
5671
signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes())
5772
if err != nil {
@@ -64,13 +79,17 @@ func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account
6479

6580
// NewKeyedTransactor is a utility method to easily create a transaction signer
6681
// from a single private key.
82+
//
83+
// Deprecated: Use NewKeyedTransactorWithChainID instead.
6784
func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
85+
log.Warn("WARNING: NewKeyedTransactor has been deprecated in favour of NewKeyedTransactorWithChainID")
6886
keyAddr := crypto.PubkeyToAddress(key.PublicKey)
87+
signer := types.HomesteadSigner{}
6988
return &TransactOpts{
7089
From: keyAddr,
71-
Signer: func(signer types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) {
90+
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
7291
if address != keyAddr {
73-
return nil, errors.New("not authorized to sign this account")
92+
return nil, ErrNotAuthorized
7493
}
7594
signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key)
7695
if err != nil {
@@ -81,14 +100,73 @@ func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts {
81100
}
82101
}
83102

103+
// NewTransactorWithChainID is a utility method to easily create a transaction signer from
104+
// an encrypted json key stream and the associated passphrase.
105+
func NewTransactorWithChainID(keyin io.Reader, passphrase string, chainID *big.Int) (*TransactOpts, error) {
106+
json, err := ioutil.ReadAll(keyin)
107+
if err != nil {
108+
return nil, err
109+
}
110+
key, err := keystore.DecryptKey(json, passphrase)
111+
if err != nil {
112+
return nil, err
113+
}
114+
return NewKeyedTransactorWithChainID(key.PrivateKey, chainID)
115+
}
116+
117+
// NewKeyStoreTransactorWithChainID is a utility method to easily create a transaction signer from
118+
// an decrypted key from a keystore.
119+
func NewKeyStoreTransactorWithChainID(keystore *keystore.KeyStore, account accounts.Account, chainID *big.Int) (*TransactOpts, error) {
120+
if chainID == nil {
121+
return nil, ErrNoChainID
122+
}
123+
signer := types.NewEIP155Signer(chainID)
124+
return &TransactOpts{
125+
From: account.Address,
126+
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
127+
if address != account.Address {
128+
return nil, ErrNotAuthorized
129+
}
130+
signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes())
131+
if err != nil {
132+
return nil, err
133+
}
134+
return tx.WithSignature(signer, signature)
135+
},
136+
}, nil
137+
}
138+
139+
// NewKeyedTransactorWithChainID is a utility method to easily create a transaction signer
140+
// from a single private key.
141+
func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) {
142+
keyAddr := crypto.PubkeyToAddress(key.PublicKey)
143+
if chainID == nil {
144+
return nil, ErrNoChainID
145+
}
146+
signer := types.NewEIP155Signer(chainID)
147+
return &TransactOpts{
148+
From: keyAddr,
149+
Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {
150+
if address != keyAddr {
151+
return nil, ErrNotAuthorized
152+
}
153+
signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key)
154+
if err != nil {
155+
return nil, err
156+
}
157+
return tx.WithSignature(signer, signature)
158+
},
159+
}, nil
160+
}
161+
84162
// NewClefTransactor is a utility method to easily create a transaction signer
85163
// with a clef backend.
86164
func NewClefTransactor(clef *external.ExternalSigner, account accounts.Account) *TransactOpts {
87165
return &TransactOpts{
88166
From: account.Address,
89-
Signer: func(signer types.Signer, address common.Address, transaction *types.Transaction) (*types.Transaction, error) {
167+
Signer: func(address common.Address, transaction *types.Transaction) (*types.Transaction, error) {
90168
if address != account.Address {
91-
return nil, errors.New("not authorized to sign this account")
169+
return nil, ErrNotAuthorized
92170
}
93171
return clef.SignTx(account, transaction, nil) // Clef enforces its own chain id
94172
},

accounts/abi/bind/backends/simulated.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ type SimulatedBackend struct {
7474

7575
// NewSimulatedBackendWithDatabase creates a new binding backend based on the given database
7676
// and uses a simulated blockchain for testing purposes.
77+
// A simulated backend always uses chainID 1337.
7778
func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
7879
genesis := core.Genesis{Config: params.AllEthashProtocolChanges, GasLimit: gasLimit, Alloc: alloc}
7980
genesis.MustCommit(database)
@@ -91,6 +92,7 @@ func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.Genesis
9192

9293
// NewSimulatedBackend creates a new binding backend using a simulated blockchain
9394
// for testing purposes.
95+
// A simulated backend always uses chainID 1337.
9496
func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
9597
return NewSimulatedBackendWithDatabase(rawdb.NewMemoryDatabase(), alloc, gasLimit)
9698
}

accounts/abi/bind/backends/simulated_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import (
3939
func TestSimulatedBackend(t *testing.T) {
4040
var gasLimit uint64 = 8000029
4141
key, _ := crypto.GenerateKey() // nolint: gosec
42-
auth := bind.NewKeyedTransactor(key)
42+
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
4343
genAlloc := make(core.GenesisAlloc)
4444
genAlloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(9223372036854775807)}
4545

@@ -411,7 +411,7 @@ func TestSimulatedBackend_EstimateGas(t *testing.T) {
411411

412412
key, _ := crypto.GenerateKey()
413413
addr := crypto.PubkeyToAddress(key.PublicKey)
414-
opts := bind.NewKeyedTransactor(key)
414+
opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
415415

416416
sim := NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether)}}, 10000000)
417417
defer sim.Close()
@@ -888,7 +888,7 @@ func TestSimulatedBackend_PendingCodeAt(t *testing.T) {
888888
if err != nil {
889889
t.Errorf("could not get code at test addr: %v", err)
890890
}
891-
auth := bind.NewKeyedTransactor(testKey)
891+
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
892892
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
893893
if err != nil {
894894
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
@@ -924,7 +924,7 @@ func TestSimulatedBackend_CodeAt(t *testing.T) {
924924
if err != nil {
925925
t.Errorf("could not get code at test addr: %v", err)
926926
}
927-
auth := bind.NewKeyedTransactor(testKey)
927+
auth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
928928
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
929929
if err != nil {
930930
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
@@ -956,7 +956,7 @@ func TestSimulatedBackend_PendingAndCallContract(t *testing.T) {
956956
if err != nil {
957957
t.Errorf("could not get code at test addr: %v", err)
958958
}
959-
contractAuth := bind.NewKeyedTransactor(testKey)
959+
contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
960960
addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(abiBin), sim)
961961
if err != nil {
962962
t.Errorf("could not deploy contract: %v", err)
@@ -1043,7 +1043,7 @@ func TestSimulatedBackend_CallContractRevert(t *testing.T) {
10431043
if err != nil {
10441044
t.Errorf("could not get code at test addr: %v", err)
10451045
}
1046-
contractAuth := bind.NewKeyedTransactor(testKey)
1046+
contractAuth, _ := bind.NewKeyedTransactorWithChainID(testKey, big.NewInt(1337))
10471047
addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(reverterBin), sim)
10481048
if err != nil {
10491049
t.Errorf("could not deploy contract: %v", err)

accounts/abi/bind/base.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import (
3232

3333
// SignerFn is a signer function callback when a contract requires a method to
3434
// sign the transaction before submission.
35-
type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Transaction, error)
35+
type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, error)
3636

3737
// CallOpts is the collection of options to fine tune a contract call request.
3838
type CallOpts struct {
@@ -256,7 +256,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
256256
if opts.Signer == nil {
257257
return nil, errors.New("no signer to authorize the transaction with")
258258
}
259-
signedTx, err := opts.Signer(types.HomesteadSigner{}, opts.From, rawTx)
259+
signedTx, err := opts.Signer(opts.From, rawTx)
260260
if err != nil {
261261
return nil, err
262262
}

accounts/abi/bind/bind_test.go

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ var bindTests = []struct {
296296
`
297297
// Generate a new random account and a funded simulator
298298
key, _ := crypto.GenerateKey()
299-
auth := bind.NewKeyedTransactor(key)
299+
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
300300
301301
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000)
302302
defer sim.Close()
@@ -351,7 +351,7 @@ var bindTests = []struct {
351351
`
352352
// Generate a new random account and a funded simulator
353353
key, _ := crypto.GenerateKey()
354-
auth := bind.NewKeyedTransactor(key)
354+
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
355355
356356
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000)
357357
defer sim.Close()
@@ -397,7 +397,7 @@ var bindTests = []struct {
397397
`
398398
// Generate a new random account and a funded simulator
399399
key, _ := crypto.GenerateKey()
400-
auth := bind.NewKeyedTransactor(key)
400+
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
401401
402402
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000)
403403
defer sim.Close()
@@ -455,7 +455,7 @@ var bindTests = []struct {
455455
`
456456
// Generate a new random account and a funded simulator
457457
key, _ := crypto.GenerateKey()
458-
auth := bind.NewKeyedTransactor(key)
458+
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
459459
460460
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000)
461461
defer sim.Close()
@@ -503,7 +503,7 @@ var bindTests = []struct {
503503
`
504504
// Generate a new random account and a funded simulator
505505
key, _ := crypto.GenerateKey()
506-
auth := bind.NewKeyedTransactor(key)
506+
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
507507
508508
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000)
509509
defer sim.Close()
@@ -598,7 +598,7 @@ var bindTests = []struct {
598598
`
599599
// Generate a new random account and a funded simulator
600600
key, _ := crypto.GenerateKey()
601-
auth := bind.NewKeyedTransactor(key)
601+
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
602602
603603
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000)
604604
defer sim.Close()
@@ -648,7 +648,7 @@ var bindTests = []struct {
648648
`
649649
// Generate a new random account and a funded simulator
650650
key, _ := crypto.GenerateKey()
651-
auth := bind.NewKeyedTransactor(key)
651+
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
652652
653653
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000)
654654
defer sim.Close()
@@ -723,7 +723,7 @@ var bindTests = []struct {
723723
`
724724
// Generate a new random account and a funded simulator
725725
key, _ := crypto.GenerateKey()
726-
auth := bind.NewKeyedTransactor(key)
726+
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
727727
728728
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000)
729729
defer sim.Close()
@@ -817,7 +817,7 @@ var bindTests = []struct {
817817
`
818818
// Generate a new random account and a funded simulator
819819
key, _ := crypto.GenerateKey()
820-
auth := bind.NewKeyedTransactor(key)
820+
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
821821
822822
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000)
823823
defer sim.Close()
@@ -1007,7 +1007,7 @@ var bindTests = []struct {
10071007
`
10081008
// Generate a new random account and a funded simulator
10091009
key, _ := crypto.GenerateKey()
1010-
auth := bind.NewKeyedTransactor(key)
1010+
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
10111011
10121012
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000)
10131013
defer sim.Close()
@@ -1142,7 +1142,7 @@ var bindTests = []struct {
11421142

11431143
`
11441144
key, _ := crypto.GenerateKey()
1145-
auth := bind.NewKeyedTransactor(key)
1145+
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
11461146
11471147
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000)
11481148
defer sim.Close()
@@ -1284,7 +1284,7 @@ var bindTests = []struct {
12841284
`
12851285
// Generate a new random account and a funded simulator
12861286
key, _ := crypto.GenerateKey()
1287-
auth := bind.NewKeyedTransactor(key)
1287+
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
12881288
12891289
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000)
12901290
defer sim.Close()
@@ -1350,7 +1350,7 @@ var bindTests = []struct {
13501350
`
13511351
// Initialize test accounts
13521352
key, _ := crypto.GenerateKey()
1353-
auth := bind.NewKeyedTransactor(key)
1353+
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
13541354
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000)
13551355
defer sim.Close()
13561356
@@ -1444,7 +1444,7 @@ var bindTests = []struct {
14441444
sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 10000000)
14451445
defer sim.Close()
14461446
1447-
transactOpts := bind.NewKeyedTransactor(key)
1447+
transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
14481448
_, _, _, err := DeployIdentifierCollision(transactOpts, sim)
14491449
if err != nil {
14501450
t.Fatalf("failed to deploy contract: %v", err)
@@ -1506,7 +1506,7 @@ var bindTests = []struct {
15061506
sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 10000000)
15071507
defer sim.Close()
15081508
1509-
transactOpts := bind.NewKeyedTransactor(key)
1509+
transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
15101510
_, _, c1, err := DeployContractOne(transactOpts, sim)
15111511
if err != nil {
15121512
t.Fatal("Failed to deploy contract")
@@ -1563,7 +1563,7 @@ var bindTests = []struct {
15631563
`
15641564
// Generate a new random account and a funded simulator
15651565
key, _ := crypto.GenerateKey()
1566-
auth := bind.NewKeyedTransactor(key)
1566+
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
15671567
15681568
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000)
15691569
defer sim.Close()
@@ -1632,7 +1632,7 @@ var bindTests = []struct {
16321632
sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 1000000)
16331633
defer sim.Close()
16341634
1635-
opts := bind.NewKeyedTransactor(key)
1635+
opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
16361636
_, _, c, err := DeployNewFallbacks(opts, sim)
16371637
if err != nil {
16381638
t.Fatalf("Failed to deploy contract: %v", err)

contracts/checkpointoracle/oracle_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ func TestCheckpointRegister(t *testing.T) {
178178
contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{accounts[0].addr: {Balance: big.NewInt(1000000000)}, accounts[1].addr: {Balance: big.NewInt(1000000000)}, accounts[2].addr: {Balance: big.NewInt(1000000000)}}, 10000000)
179179
defer contractBackend.Close()
180180

181-
transactOpts := bind.NewKeyedTransactor(accounts[0].key)
181+
transactOpts, _ := bind.NewKeyedTransactorWithChainID(accounts[0].key, big.NewInt(1337))
182182

183183
// 3 trusted signers, threshold 2
184184
contractAddr, _, c, err := contract.DeployCheckpointOracle(transactOpts, contractBackend, []common.Address{accounts[0].addr, accounts[1].addr, accounts[2].addr}, sectionSize, processConfirms, big.NewInt(2))

0 commit comments

Comments
 (0)