-
Notifications
You must be signed in to change notification settings - Fork 16
feat: add setcode transaction type for compatibility #189
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,22 +32,23 @@ import ( | |
| type txJSON struct { | ||
| Type hexutil.Uint64 `json:"type"` | ||
|
|
||
| ChainID *hexutil.Big `json:"chainId,omitempty"` | ||
| Nonce *hexutil.Uint64 `json:"nonce"` | ||
| To *common.Address `json:"to"` | ||
| Gas *hexutil.Uint64 `json:"gas"` | ||
| GasPrice *hexutil.Big `json:"gasPrice"` | ||
| MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"` | ||
| MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"` | ||
| MaxFeePerBlobGas *hexutil.Big `json:"maxFeePerBlobGas,omitempty"` | ||
| Value *hexutil.Big `json:"value"` | ||
| Input *hexutil.Bytes `json:"input"` | ||
| AccessList *AccessList `json:"accessList,omitempty"` | ||
| BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` | ||
| V *hexutil.Big `json:"v"` | ||
| R *hexutil.Big `json:"r"` | ||
| S *hexutil.Big `json:"s"` | ||
| YParity *hexutil.Uint64 `json:"yParity,omitempty"` | ||
| ChainID *hexutil.Big `json:"chainId,omitempty"` | ||
| Nonce *hexutil.Uint64 `json:"nonce"` | ||
| To *common.Address `json:"to"` | ||
| Gas *hexutil.Uint64 `json:"gas"` | ||
| GasPrice *hexutil.Big `json:"gasPrice"` | ||
| MaxPriorityFeePerGas *hexutil.Big `json:"maxPriorityFeePerGas"` | ||
| MaxFeePerGas *hexutil.Big `json:"maxFeePerGas"` | ||
| MaxFeePerBlobGas *hexutil.Big `json:"maxFeePerBlobGas,omitempty"` | ||
| Value *hexutil.Big `json:"value"` | ||
| Input *hexutil.Bytes `json:"input"` | ||
| AccessList *AccessList `json:"accessList,omitempty"` | ||
| BlobVersionedHashes []common.Hash `json:"blobVersionedHashes,omitempty"` | ||
| AuthorizationList []SetCodeAuthorization `json:"authorizationList,omitempty"` | ||
| V *hexutil.Big `json:"v"` | ||
| R *hexutil.Big `json:"r"` | ||
| S *hexutil.Big `json:"s"` | ||
| YParity *hexutil.Uint64 `json:"yParity,omitempty"` | ||
|
|
||
| // Blob transaction sidecar encoding: | ||
| Blobs []kzg4844.Blob `json:"blobs,omitempty"` | ||
|
|
@@ -166,6 +167,23 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) { | |
| enc.Commitments = itx.Sidecar.Commitments | ||
| enc.Proofs = itx.Sidecar.Proofs | ||
| } | ||
|
|
||
| case *SetCodeTx: | ||
| enc.ChainID = (*hexutil.Big)(itx.ChainID.ToBig()) | ||
| enc.Nonce = (*hexutil.Uint64)(&itx.Nonce) | ||
| enc.To = tx.To() | ||
| enc.Gas = (*hexutil.Uint64)(&itx.Gas) | ||
| enc.MaxFeePerGas = (*hexutil.Big)(itx.GasFeeCap.ToBig()) | ||
| enc.MaxPriorityFeePerGas = (*hexutil.Big)(itx.GasTipCap.ToBig()) | ||
| enc.Value = (*hexutil.Big)(itx.Value.ToBig()) | ||
| enc.Input = (*hexutil.Bytes)(&itx.Data) | ||
| enc.AccessList = &itx.AccessList | ||
| enc.AuthorizationList = itx.AuthList | ||
| enc.V = (*hexutil.Big)(itx.V.ToBig()) | ||
| enc.R = (*hexutil.Big)(itx.R.ToBig()) | ||
| enc.S = (*hexutil.Big)(itx.S.ToBig()) | ||
| yparity := itx.V.Uint64() | ||
| enc.YParity = (*hexutil.Uint64)(&yparity) | ||
| } | ||
| return json.Marshal(&enc) | ||
| } | ||
|
|
@@ -449,6 +467,84 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error { | |
| } | ||
| itx.Sender = *dec.Sender | ||
|
|
||
| case SetCodeTxType: | ||
| var itx SetCodeTx | ||
| inner = &itx | ||
| if dec.ChainID == nil { | ||
| return errors.New("missing required field 'chainId' in transaction") | ||
| } | ||
| var overflow bool | ||
| itx.ChainID, overflow = uint256.FromBig(dec.ChainID.ToInt()) | ||
| if overflow { | ||
| return errors.New("'chainId' value overflows uint256") | ||
| } | ||
| if dec.Nonce == nil { | ||
| return errors.New("missing required field 'nonce' in transaction") | ||
| } | ||
| itx.Nonce = uint64(*dec.Nonce) | ||
| if dec.To == nil { | ||
| return errors.New("missing required field 'to' in transaction") | ||
| } | ||
| itx.To = *dec.To | ||
| if dec.Gas == nil { | ||
| return errors.New("missing required field 'gas' for txdata") | ||
| } | ||
| itx.Gas = uint64(*dec.Gas) | ||
| if dec.MaxPriorityFeePerGas == nil { | ||
| return errors.New("missing required field 'maxPriorityFeePerGas' for txdata") | ||
| } | ||
| itx.GasTipCap = uint256.MustFromBig((*big.Int)(dec.MaxPriorityFeePerGas)) | ||
| if dec.MaxFeePerGas == nil { | ||
| return errors.New("missing required field 'maxFeePerGas' for txdata") | ||
| } | ||
| itx.GasFeeCap = uint256.MustFromBig((*big.Int)(dec.MaxFeePerGas)) | ||
| if dec.Value == nil { | ||
| return errors.New("missing required field 'value' in transaction") | ||
| } | ||
| itx.Value = uint256.MustFromBig((*big.Int)(dec.Value)) | ||
| if dec.Input == nil { | ||
| return errors.New("missing required field 'input' in transaction") | ||
| } | ||
| itx.Data = *dec.Input | ||
| if dec.AccessList != nil { | ||
| itx.AccessList = *dec.AccessList | ||
| } | ||
| if dec.AuthorizationList == nil { | ||
| return errors.New("missing required field 'authorizationList' in transaction") | ||
| } | ||
| itx.AuthList = dec.AuthorizationList | ||
|
|
||
|
Comment on lines
+470
to
+516
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Sanity‑check each authorisation entry during decoding We trust every element in
Adding a tight loop here prevents malformed txs from reaching deeper consensus code. for i, a := range dec.AuthorizationList {
if a.ChainID.Cmp(itx.ChainID) != 0 {
return fmt.Errorf("authorizationList[%d].chainId mismatch", i)
}
if _, err := a.Authority(); err != nil {
return fmt.Errorf("authorizationList[%d] invalid signature: %w", i, err)
}
// TODO: duplicate / nonce checks as per consensus rules
} |
||
| // signature R | ||
| if dec.R == nil { | ||
| return errors.New("missing required field 'r' in transaction") | ||
| } | ||
| itx.R, overflow = uint256.FromBig((*big.Int)(dec.R)) | ||
| if overflow { | ||
| return errors.New("'r' value overflows uint256") | ||
| } | ||
| // signature S | ||
| if dec.S == nil { | ||
| return errors.New("missing required field 's' in transaction") | ||
| } | ||
| itx.S, overflow = uint256.FromBig((*big.Int)(dec.S)) | ||
| if overflow { | ||
| return errors.New("'s' value overflows uint256") | ||
| } | ||
| // signature V | ||
| vbig, err := dec.yParityValue() | ||
| if err != nil { | ||
| return err | ||
| } | ||
| itx.V, overflow = uint256.FromBig(vbig) | ||
| if overflow { | ||
| return errors.New("'v' value overflows uint256") | ||
| } | ||
| if itx.V.Sign() != 0 || itx.R.Sign() != 0 || itx.S.Sign() != 0 { | ||
| if err := sanityCheckSignature(vbig, itx.R.ToBig(), itx.S.ToBig(), false); err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| default: | ||
| return ErrTxTypeNotSupported | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider pointer‑vs‑slice semantics for
authorizationListAuthorizationList []SetCodeAuthorizationis a value slice.During (un)marshal we rely on
nilvs. “empty slice” to distinguish “field omitted” from “present but empty”.Because Go serialises an empty slice as
[], clients can still send{"authorizationList":[]}– our laternilcheck (l.512‑514) will pass even though the list is empty.If an empty list is invalid you should: