Skip to content

Commit f5a4efb

Browse files
committed
core/types: support yParity field in JSON transactions ethereum#27744
1 parent 8e49816 commit f5a4efb

File tree

4 files changed

+214
-25
lines changed

4 files changed

+214
-25
lines changed

core/types/transaction.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ var (
3838
ErrTxTypeNotSupported = errors.New("transaction type not supported")
3939
ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
4040
errShortTypedTx = errors.New("typed transaction too short")
41+
errInvalidYParity = errors.New("'yParity' field must be 0 or 1")
42+
errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match")
43+
errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction")
4144

4245
skipNonceDestinationAddress = map[common.Address]bool{
4346
common.XDCXAddrBinary: true,

core/types/transaction_marshalling.go

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,32 @@ type txJSON struct {
4242
V *hexutil.Big `json:"v"`
4343
R *hexutil.Big `json:"r"`
4444
S *hexutil.Big `json:"s"`
45+
YParity *hexutil.Uint64 `json:"yParity,omitempty"`
4546

4647
// Only used for encoding:
4748
Hash common.Hash `json:"hash"`
4849
}
4950

51+
// yParityValue returns the YParity value from JSON. For backwards-compatibility reasons,
52+
// this can be given in the 'v' field or the 'yParity' field. If both exist, they must match.
53+
func (tx *txJSON) yParityValue() (*big.Int, error) {
54+
if tx.YParity != nil {
55+
val := uint64(*tx.YParity)
56+
if val != 0 && val != 1 {
57+
return nil, errInvalidYParity
58+
}
59+
bigval := new(big.Int).SetUint64(val)
60+
if tx.V != nil && tx.V.ToInt().Cmp(bigval) != 0 {
61+
return nil, errVYParityMismatch
62+
}
63+
return bigval, nil
64+
}
65+
if tx.V != nil {
66+
return tx.V.ToInt(), nil
67+
}
68+
return nil, errVYParityMissing
69+
}
70+
5071
// MarshalJSON marshals as JSON with a hash.
5172
func (tx *Transaction) MarshalJSON() ([]byte, error) {
5273
var enc txJSON
@@ -79,6 +100,8 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
79100
enc.V = (*hexutil.Big)(itx.V)
80101
enc.R = (*hexutil.Big)(itx.R)
81102
enc.S = (*hexutil.Big)(itx.S)
103+
yparity := itx.V.Uint64()
104+
enc.YParity = (*hexutil.Uint64)(&yparity)
82105

83106
case *DynamicFeeTx:
84107
enc.ChainID = (*hexutil.Big)(itx.ChainID)
@@ -93,14 +116,17 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
93116
enc.V = (*hexutil.Big)(itx.V)
94117
enc.R = (*hexutil.Big)(itx.R)
95118
enc.S = (*hexutil.Big)(itx.S)
119+
yparity := itx.V.Uint64()
120+
enc.YParity = (*hexutil.Uint64)(&yparity)
96121
}
97122
return json.Marshal(&enc)
98123
}
99124

100125
// UnmarshalJSON unmarshals from JSON.
101126
func (tx *Transaction) UnmarshalJSON(input []byte) error {
102127
var dec txJSON
103-
if err := json.Unmarshal(input, &dec); err != nil {
128+
err := json.Unmarshal(input, &dec)
129+
if err != nil {
104130
return err
105131
}
106132

@@ -134,20 +160,22 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
134160
}
135161
itx.Data = *dec.Input
136162

137-
if dec.V == nil {
138-
return errors.New("missing required field 'v' in transaction")
139-
}
140-
itx.V = (*big.Int)(dec.V)
163+
// signature R
141164
if dec.R == nil {
142165
return errors.New("missing required field 'r' in transaction")
143166
}
144167
itx.R = (*big.Int)(dec.R)
168+
// signature S
145169
if dec.S == nil {
146170
return errors.New("missing required field 's' in transaction")
147171
}
148172
itx.S = (*big.Int)(dec.S)
149-
withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0
150-
if withSignature {
173+
// signature V
174+
if dec.V == nil {
175+
return errors.New("missing required field 'v' in transaction")
176+
}
177+
itx.V = (*big.Int)(dec.V)
178+
if itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 {
151179
if err := sanityCheckSignature(itx.V, itx.R, itx.S, true); err != nil {
152180
return err
153181
}
@@ -187,20 +215,22 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
187215
itx.AccessList = *dec.AccessList
188216
}
189217

190-
if dec.V == nil {
191-
return errors.New("missing required field 'v' in transaction")
192-
}
193-
itx.V = (*big.Int)(dec.V)
218+
// signature R
194219
if dec.R == nil {
195220
return errors.New("missing required field 'r' in transaction")
196221
}
197222
itx.R = (*big.Int)(dec.R)
223+
// signature S
198224
if dec.S == nil {
199225
return errors.New("missing required field 's' in transaction")
200226
}
201227
itx.S = (*big.Int)(dec.S)
202-
withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0
203-
if withSignature {
228+
// signature V
229+
itx.V, err = dec.yParityValue()
230+
if err != nil {
231+
return err
232+
}
233+
if itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 {
204234
if err := sanityCheckSignature(itx.V, itx.R, itx.S, false); err != nil {
205235
return err
206236
}
@@ -244,20 +274,22 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
244274
itx.AccessList = *dec.AccessList
245275
}
246276

247-
if dec.V == nil {
248-
return errors.New("missing required field 'v' in transaction")
249-
}
250-
itx.V = (*big.Int)(dec.V)
277+
// signature R
251278
if dec.R == nil {
252279
return errors.New("missing required field 'r' in transaction")
253280
}
254281
itx.R = (*big.Int)(dec.R)
282+
// signature S
255283
if dec.S == nil {
256284
return errors.New("missing required field 's' in transaction")
257285
}
258286
itx.S = (*big.Int)(dec.S)
259-
withSignature := itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0
260-
if withSignature {
287+
// signature V
288+
itx.V, err = dec.yParityValue()
289+
if err != nil {
290+
return err
291+
}
292+
if itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 {
261293
if err := sanityCheckSignature(itx.V, itx.R, itx.S, false); err != nil {
262294
return err
263295
}

internal/ethapi/api.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1670,6 +1670,7 @@ type RPCTransaction struct {
16701670
V *hexutil.Big `json:"v"`
16711671
R *hexutil.Big `json:"r"`
16721672
S *hexutil.Big `json:"s"`
1673+
YParity *hexutil.Uint64 `json:"yParity,omitempty"`
16731674
}
16741675

16751676
// newRPCTransaction returns a transaction that will serialize to the RPC
@@ -1697,15 +1698,21 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
16971698
result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber))
16981699
result.TransactionIndex = (*hexutil.Uint64)(&index)
16991700
}
1701+
17001702
switch tx.Type() {
17011703
case types.AccessListTxType:
17021704
al := tx.AccessList()
1705+
yparity := hexutil.Uint64(v.Sign())
17031706
result.Accesses = &al
17041707
result.ChainID = (*hexutil.Big)(tx.ChainId())
1708+
result.YParity = &yparity
1709+
17051710
case types.DynamicFeeTxType:
17061711
al := tx.AccessList()
1712+
yparity := hexutil.Uint64(v.Sign())
17071713
result.Accesses = &al
17081714
result.ChainID = (*hexutil.Big)(tx.ChainId())
1715+
result.YParity = &yparity
17091716
result.GasFeeCap = (*hexutil.Big)(tx.GasFeeCap())
17101717
result.GasTipCap = (*hexutil.Big)(tx.GasTipCap())
17111718
// if the transaction has been mined, compute the effective gas price

0 commit comments

Comments
 (0)