|
| 1 | +package eth |
| 2 | + |
| 3 | +import ( |
| 4 | + "crypto/ecdsa" |
| 5 | + "errors" |
| 6 | + "fmt" |
| 7 | + "math/big" |
| 8 | + "sync" |
| 9 | + |
| 10 | + "github.com/ethereum/go-ethereum/accounts/abi/bind" |
| 11 | + "github.com/ethereum/go-ethereum/common" |
| 12 | + "github.com/ethereum/go-ethereum/core/types" |
| 13 | + "github.com/ethereum/go-ethereum/crypto" |
| 14 | + "github.com/ethereum/go-ethereum/rlp" |
| 15 | + "golang.org/x/crypto/sha3" |
| 16 | +) |
| 17 | + |
| 18 | +func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*bind.TransactOpts, error) { |
| 19 | + keyAddr := crypto.PubkeyToAddress(key.PublicKey) |
| 20 | + if chainID == nil { |
| 21 | + return nil, fmt.Errorf("ErrNoChainID") |
| 22 | + } |
| 23 | + signer := NewEIP155Signer(chainID) |
| 24 | + return &bind.TransactOpts{ |
| 25 | + From: keyAddr, |
| 26 | + Signer: func(_ types.Signer, address common.Address, tx *types.Transaction) (*types.Transaction, error) { |
| 27 | + if address != keyAddr { |
| 28 | + return nil, fmt.Errorf("ErrNotAuthorized") |
| 29 | + } |
| 30 | + signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key) |
| 31 | + if err != nil { |
| 32 | + return nil, err |
| 33 | + } |
| 34 | + return tx.WithSignature(signer, signature) |
| 35 | + }, |
| 36 | + }, nil |
| 37 | +} |
| 38 | + |
| 39 | +// EIP155Signer implements Signer using the EIP-155 rules. This accepts transactions which |
| 40 | +// are replay-protected as well as unprotected homestead transactions. |
| 41 | +type EIP155Signer struct { |
| 42 | + chainId, chainIdMul *big.Int |
| 43 | +} |
| 44 | + |
| 45 | +func NewEIP155Signer(chainId *big.Int) EIP155Signer { |
| 46 | + if chainId == nil { |
| 47 | + chainId = new(big.Int) |
| 48 | + } |
| 49 | + return EIP155Signer{ |
| 50 | + chainId: chainId, |
| 51 | + chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)), |
| 52 | + } |
| 53 | +} |
| 54 | + |
| 55 | +func (s EIP155Signer) ChainID() *big.Int { |
| 56 | + return s.chainId |
| 57 | +} |
| 58 | + |
| 59 | +func (s EIP155Signer) Equal(s2 types.Signer) bool { |
| 60 | + eip155, ok := s2.(EIP155Signer) |
| 61 | + return ok && eip155.chainId.Cmp(s.chainId) == 0 |
| 62 | +} |
| 63 | + |
| 64 | +var big8 = big.NewInt(8) |
| 65 | + |
| 66 | +func (s EIP155Signer) Sender(tx *types.Transaction) (common.Address, error) { |
| 67 | + if tx.ChainId().Cmp(s.chainId) != 0 { |
| 68 | + return common.Address{}, fmt.Errorf("ErrInvalidChainId") |
| 69 | + } |
| 70 | + V, R, S := tx.RawSignatureValues() |
| 71 | + V = new(big.Int).Sub(V, s.chainIdMul) |
| 72 | + V.Sub(V, big8) |
| 73 | + return recoverPlain(s.Hash(tx), R, S, V, true) |
| 74 | +} |
| 75 | + |
| 76 | +func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (common.Address, error) { |
| 77 | + if Vb.BitLen() > 8 { |
| 78 | + return common.Address{}, errors.New("ErrInvalidSig") |
| 79 | + } |
| 80 | + V := byte(Vb.Uint64() - 27) |
| 81 | + if !crypto.ValidateSignatureValues(V, R, S, homestead) { |
| 82 | + return common.Address{}, errors.New("ErrInvalidSig") |
| 83 | + } |
| 84 | + // encode the signature in uncompressed format |
| 85 | + r, s := R.Bytes(), S.Bytes() |
| 86 | + sig := make([]byte, crypto.SignatureLength) |
| 87 | + copy(sig[32-len(r):32], r) |
| 88 | + copy(sig[64-len(s):64], s) |
| 89 | + sig[64] = V |
| 90 | + // recover the public key from the signature |
| 91 | + pub, err := crypto.Ecrecover(sighash[:], sig) |
| 92 | + if err != nil { |
| 93 | + return common.Address{}, err |
| 94 | + } |
| 95 | + if len(pub) == 0 || pub[0] != 4 { |
| 96 | + return common.Address{}, errors.New("invalid public key") |
| 97 | + } |
| 98 | + var addr common.Address |
| 99 | + copy(addr[:], crypto.Keccak256(pub[1:])[12:]) |
| 100 | + return addr, nil |
| 101 | +} |
| 102 | + |
| 103 | +func decodeSignature(sig []byte) (r, s, v *big.Int) { |
| 104 | + if len(sig) != crypto.SignatureLength { |
| 105 | + panic(fmt.Sprintf("wrong size for signature: got %d, want %d", len(sig), crypto.SignatureLength)) |
| 106 | + } |
| 107 | + r = new(big.Int).SetBytes(sig[:32]) |
| 108 | + s = new(big.Int).SetBytes(sig[32:64]) |
| 109 | + v = new(big.Int).SetBytes([]byte{sig[64] + 27}) |
| 110 | + return r, s, v |
| 111 | +} |
| 112 | + |
| 113 | +// SignatureValues returns signature values. This signature |
| 114 | +// needs to be in the [R || S || V] format where V is 0 or 1. |
| 115 | +func (s EIP155Signer) SignatureValues(tx *types.Transaction, sig []byte) (R, S, V *big.Int, err error) { |
| 116 | + R, S, V = decodeSignature(sig) |
| 117 | + if s.chainId.Sign() != 0 { |
| 118 | + V = big.NewInt(int64(sig[64] + 35)) |
| 119 | + V.Add(V, s.chainIdMul) |
| 120 | + } |
| 121 | + return R, S, V, nil |
| 122 | +} |
| 123 | + |
| 124 | +// hasherPool holds LegacyKeccak256 hashers for rlpHash. |
| 125 | +var hasherPool = sync.Pool{ |
| 126 | + New: func() interface{} { return sha3.NewLegacyKeccak256() }, |
| 127 | +} |
| 128 | + |
| 129 | +// rlpHash encodes x and hashes the encoded bytes. |
| 130 | +func rlpHash(x interface{}) (h common.Hash) { |
| 131 | + sha := hasherPool.Get().(crypto.KeccakState) |
| 132 | + defer hasherPool.Put(sha) |
| 133 | + sha.Reset() |
| 134 | + rlp.Encode(sha, x) |
| 135 | + sha.Read(h[:]) |
| 136 | + return h |
| 137 | +} |
| 138 | + |
| 139 | +// Hash returns the hash to be signed by the sender. |
| 140 | +// It does not uniquely identify the transaction. |
| 141 | +func (s EIP155Signer) Hash(tx *types.Transaction) common.Hash { |
| 142 | + return rlpHash([]interface{}{ |
| 143 | + tx.Nonce(), |
| 144 | + tx.GasPrice(), |
| 145 | + tx.Gas(), |
| 146 | + tx.To(), |
| 147 | + tx.Value(), |
| 148 | + tx.Data(), |
| 149 | + s.chainId, uint(0), uint(0), |
| 150 | + }) |
| 151 | +} |
0 commit comments