Skip to content
This repository was archived by the owner on Jul 14, 2020. It is now read-only.

Commit 46c0132

Browse files
author
Jan Xie
committed
implement EIP 155
1 parent c078ab5 commit 46c0132

File tree

6 files changed

+65
-14
lines changed

6 files changed

+65
-14
lines changed

lib/ethereum/env.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class Env
6464
dao_withdrawer: Utils.normalize_address('0xbf4ed7b27f1d666546e30d74d50d173d20bca754'),
6565

6666
anti_dos_fork_blknum: 2457000,
67-
spurious_dragon_fork_blknum: 2**100
67+
spurious_dragon_fork_blknum: 3500000
6868
}.freeze
6969

7070
attr :db, :config, :global_config

lib/ethereum/secp256k1.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def recoverable_sign(msg, privkey)
2929
pk = ::Secp256k1::PrivateKey.new privkey: privkey, raw: true
3030
signature = pk.ecdsa_recoverable_serialize pk.ecdsa_sign_recoverable(msg, raw: true)
3131

32-
v = signature[1] + 27
32+
v = signature[1]
3333
r = Utils.big_endian_to_int signature[0][0,32]
3434
s = Utils.big_endian_to_int signature[0][32,32]
3535

@@ -49,7 +49,7 @@ def signature_verify(msg, vrs, pubkey)
4949
def recover_pubkey(msg, vrs, compressed: false)
5050
pk = ::Secp256k1::PublicKey.new(flags: ::Secp256k1::ALL_FLAGS)
5151
sig = Utils.zpad_int(vrs[1]) + Utils.zpad_int(vrs[2])
52-
recsig = pk.ecdsa_recoverable_deserialize(sig, vrs[0]-27)
52+
recsig = pk.ecdsa_recoverable_deserialize(sig, vrs[0])
5353
pk.public_key = pk.ecdsa_recover msg, recsig, raw: true
5454
pk.serialize compressed: compressed
5555
end

lib/ethereum/special_contract.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def call(ext, msg)
1616
r = msg.data.extract32(64)
1717
s = msg.data.extract32(96)
1818

19-
if r >= Secp256k1::N || s >= Secp256k1::N || v < 27 || v > 28
19+
if r >= Secp256k1::N || s >= Secp256k1::N || v < Transaction::V_MIN || v > Transaction::V_MAX
2020
return 1, msg.gas - gas_cost, []
2121
end
2222

@@ -27,7 +27,7 @@ def call(ext, msg)
2727

2828
pub = nil
2929
begin
30-
pub = Secp256k1.recover_pubkey(message_hash, [v,r,s])
30+
pub = Secp256k1.recover_pubkey(message_hash, [Transaction.decode_v(v),r,s])
3131
rescue
3232
return 1, msg.gas - gas_cost, []
3333
end

lib/ethereum/transaction.rb

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,41 @@ class Transaction
4040
s: big_endian_int
4141
)
4242

43+
V_MIN = 27
44+
V_MAX = 28
45+
46+
EIP155_V_OFFSET = 35
47+
EIP155_CHAIN_ID = 1
48+
EIP155_V_MIN = EIP155_V_OFFSET + 2 * EIP155_CHAIN_ID
49+
EIP155_V_MAX = EIP155_V_MIN + 1
50+
4351
class <<self
4452
##
4553
# A contract is a special transaction without the `to` argument.
4654
#
4755
def contract(nonce, gasprice, startgas, endowment, code, v=0, r=0, s=0)
4856
new nonce, gasprice, startgas, '', endowment, code, v, r, s
4957
end
58+
59+
def encode_v(v, eip155=false)
60+
eip155 ? (v + EIP155_V_MIN) : (v + V_MIN)
61+
end
62+
63+
def decode_v(v)
64+
return unless v
65+
if v == V_MIN || v == V_MAX
66+
v - V_MIN
67+
elsif v == EIP155_V_MIN || v == EIP155_V_MAX
68+
v - EIP155_V_MIN
69+
else
70+
raise InvalidTransaction, "invalid signature"
71+
end
72+
end
73+
74+
def decode_chain_id(v)
75+
raise InvalidTransaction, "invalid chain id" if v < EIP155_V_OFFSET+2
76+
(v - EIP155_V_OFFSET) / 2
77+
end
5078
end
5179

5280
def initialize(*args)
@@ -66,12 +94,12 @@ def initialize(*args)
6694

6795
def sender
6896
unless @sender
69-
if v && v > 0
70-
raise InvalidTransaction, "Invalid signature values!" if r >= Secp256k1::N || s >= Secp256k1::N || v < 27 || v > 28 || r == 0 || s == 0
97+
v = Transaction.decode_v(self.v)
98+
if v
99+
raise InvalidTransaction, "Invalid signature values!" if r >= Secp256k1::N || s >= Secp256k1::N || v > 1 || r == 0 || s == 0
71100

72101
logger.debug "recovering sender"
73-
rlpdata = RLP.encode(self, sedes: UnsignedTransaction)
74-
rawhash = Utils.keccak256 rlpdata
102+
rawhash = Utils.keccak256 signing_data(:verify)
75103

76104
pub = nil
77105
begin
@@ -98,14 +126,14 @@ def sender=(v)
98126
#
99127
# A potentially already existing signature would be override.
100128
#
101-
def sign(key)
129+
def sign(key, eip155=false)
102130
raise InvalidTransaction, "Zero privkey cannot sign" if [0, '', Constant::PRIVKEY_ZERO, Constant::PRIVKEY_ZERO_HEX].include?(key)
103131

104-
rawhash = Utils.keccak256 RLP.encode(self, sedes: UnsignedTransaction)
132+
rawhash = Utils.keccak256 signing_data(:sign)
105133
key = PrivateKey.new(key).encode(:bin)
106134

107135
vrs = Secp256k1.recoverable_sign rawhash, key
108-
self.v = vrs[0]
136+
self.v = Transaction.encode_v(vrs[0], eip155)
109137
self.r = vrs[1]
110138
self.s = vrs[2]
111139

@@ -124,6 +152,29 @@ def check_low_s
124152
raise InvalidTransaction, "Invalid signature S value!" if s > Secp256k1::N/2 || s == 0
125153
end
126154

155+
def signing_data(mode)
156+
case mode
157+
when :sign
158+
if v == 0 # use encoding rules before EIP155
159+
RLP.encode(self, sedes: UnsignedTransaction)
160+
elsif v == EIP155_CHAIN_ID && r == 0 && s == 0 # after EIP155, v is chain_id >= 1
161+
RLP.encode(self, sedes: Transaction)
162+
else
163+
raise InvalidTransaction, "invalid signature"
164+
end
165+
when :verify
166+
if v == V_MIN || v == V_MAX # encoded v before EIP155
167+
RLP.encode(self, sedes: UnsignedTransaction)
168+
elsif v == EIP155_V_MIN || v == EIP155_V_MAX # after EIP155, v with chain_id encoded in it
169+
values = UnsignedTransaction.serializable_fields.keys.map {|k| send k }
170+
values += [EIP155_CHAIN_ID, 0, 0]
171+
RLP.encode(values, sedes: Transaction.serializable_sedes)
172+
end
173+
else
174+
raise InvalidTransaction, "invalid signature"
175+
end
176+
end
177+
127178
def full_hash
128179
Utils.keccak256_rlp self
129180
end

test/contracts_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1104,7 +1104,7 @@ def test_ecrecover
11041104
addr = Utils.keccak256(PublicKey.new(pub).encode(:bin)[1..-1])[12..-1]
11051105
assert_equal PrivateKey.new(priv).to_address, addr
11061106

1107-
assert_equal Utils.big_endian_to_int(addr), c.test_ecrecover(Utils.big_endian_to_int(msghash), v, r, s)
1107+
assert_equal Utils.big_endian_to_int(addr), c.test_ecrecover(Utils.big_endian_to_int(msghash), Transaction.encode_v(v), r, s)
11081108
end
11091109

11101110
SHA256_CODE = <<-EOF

test/special_contract_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def test_ecrecover
4848
msg = Utils.zpad("ethereum", 32)
4949
v, r, s = Secp256k1.recoverable_sign(msg, priv.encode(:bin))
5050

51-
sig = Utils.zpad_int(v) + Utils.zpad_int(r) + Utils.zpad_int(s)
51+
sig = Utils.zpad_int(Transaction.encode_v(v)) + Utils.zpad_int(r) + Utils.zpad_int(s)
5252
cd = VM::CallData.new Utils.bytes_to_int_array(msg + sig)
5353

5454
msg = Msg.new 0, cd

0 commit comments

Comments
 (0)