@@ -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
0 commit comments