Skip to content

Commit

Permalink
sync blob tx encoding/decoding fixes (#879)
Browse files Browse the repository at this point in the history
* feat(transactions): support blob tx only in encoding/decoding (#626)

* draft change

* fix CI

* remove CancunBlock params and change cancunSigner to londonSignerWithEIP4844

* support blob transactions in receipt

* trigger ci

* bump version

* feat(ethclient & gethclient): support blob transaction (#661)

* feat: support blob transaction

* sync tx encoding/decoding

* more fixes

* add uint236 support in rlp encoding decoding

* revert a format change

* trigger ci

---------

Co-authored-by: colin <102356659+colinlyguo@users.noreply.github.com>
  • Loading branch information
0xmountaintop and colinlyguo authored Jul 4, 2024
1 parent 9c73816 commit 0892822
Show file tree
Hide file tree
Showing 9 changed files with 78 additions and 21 deletions.
1 change: 1 addition & 0 deletions core/types/receipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type Receipt struct {
Logs []*Log `json:"logs" gencodec:"required"`

// Implementation fields: These fields are added by geth when processing a transaction.
// They are stored in the chain database.
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
ContractAddress common.Address `json:"contractAddress"`
GasUsed uint64 `json:"gasUsed" gencodec:"required"`
Expand Down
3 changes: 3 additions & 0 deletions core/types/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ var (
ErrTxTypeNotSupported = errors.New("transaction type not supported")
ErrGasFeeCapTooLow = errors.New("fee cap less than base fee")
errShortTypedTx = errors.New("typed transaction too short")
errInvalidYParity = errors.New("'yParity' field must be 0 or 1")
errVYParityMismatch = errors.New("'v' and 'yParity' fields do not match")
errVYParityMissing = errors.New("missing 'yParity' or 'v' field in transaction")
)

// Transaction types.
Expand Down
23 changes: 14 additions & 9 deletions core/types/transaction_marshalling.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto/kzg4844"
"github.com/holiman/uint256"
)

Expand All @@ -47,6 +48,11 @@ type txJSON struct {
S *hexutil.Big `json:"s"`
YParity *hexutil.Uint64 `json:"yParity,omitempty"`

// Blob transaction sidecar encoding:
Blobs []kzg4844.Blob `json:"blobs,omitempty"`
Commitments []kzg4844.Commitment `json:"commitments,omitempty"`
Proofs []kzg4844.Proof `json:"proofs,omitempty"`

// Only used for encoding:
Hash common.Hash `json:"hash"`

Expand All @@ -61,18 +67,18 @@ func (tx *txJSON) yParityValue() (*big.Int, error) {
if tx.YParity != nil {
val := uint64(*tx.YParity)
if val != 0 && val != 1 {
return nil, errors.New("'yParity' field must be 0 or 1")
return nil, errInvalidYParity
}
bigval := new(big.Int).SetUint64(val)
if tx.V != nil && tx.V.ToInt().Cmp(bigval) != 0 {
return nil, errors.New("'v' and 'yParity' fields do not match")
return nil, errVYParityMismatch
}
return bigval, nil
}
if tx.V != nil {
return tx.V.ToInt(), nil
}
return nil, errors.New("missing 'yParity' or 'v' field in transaction")
return nil, errVYParityMissing
}

// MarshalJSON marshals as JSON with a hash.
Expand Down Expand Up @@ -146,6 +152,11 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
enc.S = (*hexutil.Big)(itx.S.ToBig())
yparity := itx.V.Uint64()
enc.YParity = (*hexutil.Uint64)(&yparity)
if sidecar := itx.Sidecar; sidecar != nil {
enc.Blobs = itx.Sidecar.Blobs
enc.Commitments = itx.Sidecar.Commitments
enc.Proofs = itx.Sidecar.Proofs
}

case *L1MessageTx:
enc.QueueIndex = (*hexutil.Uint64)(&itx.QueueIndex)
Expand Down Expand Up @@ -306,9 +317,6 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
return errors.New("missing required field 'input' in transaction")
}
itx.Data = *dec.Input
if dec.V == nil {
return errors.New("missing required field 'v' in transaction")
}
if dec.AccessList != nil {
itx.AccessList = *dec.AccessList
}
Expand Down Expand Up @@ -373,9 +381,6 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
return errors.New("missing required field 'input' in transaction")
}
itx.Data = *dec.Input
if dec.V == nil {
return errors.New("missing required field 'v' in transaction")
}
if dec.AccessList != nil {
itx.AccessList = *dec.AccessList
}
Expand Down
7 changes: 5 additions & 2 deletions core/types/transaction_signing.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,11 @@ func MakeSigner(config *params.ChainConfig, blockNumber *big.Int, blockTime uint
// have the current block number available, use MakeSigner instead.
func LatestSigner(config *params.ChainConfig) Signer {
if config.ChainID != nil {
if config.CurieBlock != nil {
return NewLondonSignerWithEIP4844(config.ChainID)
}
if config.CancunTime != nil {
return NewCancunSigner(config.ChainID)
return NewLondonSignerWithEIP4844(config.ChainID)
}
if config.LondonBlock != nil {
return NewLondonSignerWithEIP4844(config.ChainID)
Expand All @@ -94,7 +97,7 @@ func LatestSignerForChainID(chainID *big.Int) Signer {
if chainID == nil {
return HomesteadSigner{}
}
return NewCancunSigner(chainID)
return NewLondonSignerWithEIP4844(chainID)
}

// SignTx signs the transaction using the given signer and private key.
Expand Down
12 changes: 2 additions & 10 deletions core/types/tx_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ type BlobTxSidecar struct {

// BlobHashes computes the blob hashes of the given blobs.
func (sc *BlobTxSidecar) BlobHashes() []common.Hash {
hasher := sha256.New()
h := make([]common.Hash, len(sc.Commitments))
for i := range sc.Blobs {
h[i] = blobHash(&sc.Commitments[i])
h[i] = kzg4844.CalcBlobHashV1(hasher, &sc.Commitments[i])
}
return h
}
Expand Down Expand Up @@ -235,12 +236,3 @@ func (tx *BlobTx) decode(input []byte) error {
}
return nil
}

func blobHash(commit *kzg4844.Commitment) common.Hash {
hasher := sha256.New()
hasher.Write(commit[:])
var vhash common.Hash
hasher.Sum(vhash[:0])
vhash[0] = params.BlobTxHashVersion
return vhash
}
19 changes: 19 additions & 0 deletions crypto/kzg4844/kzg4844.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package kzg4844
import (
"embed"
"errors"
"hash"
"sync/atomic"
)

Expand Down Expand Up @@ -108,3 +109,21 @@ func VerifyBlobProof(blob Blob, commitment Commitment, proof Proof) error {
}
return gokzgVerifyBlobProof(blob, commitment, proof)
}

// CalcBlobHashV1 calculates the 'versioned blob hash' of a commitment.
// The given hasher must be a sha256 hash instance, otherwise the result will be invalid!
func CalcBlobHashV1(hasher hash.Hash, commit *Commitment) (vh [32]byte) {
if hasher.Size() != 32 {
panic("wrong hash size")
}
hasher.Reset()
hasher.Write(commit[:])
hasher.Sum(vh[:0])
vh[0] = 0x01 // version
return vh
}

// IsValidVersionedHash checks that h is a structurally-valid versioned blob hash.
func IsValidVersionedHash(h []byte) bool {
return len(h) == 32 && h[0] == 0x01
}
15 changes: 15 additions & 0 deletions ethclient/ethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,21 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
if msg.GasPrice != nil {
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
}
if msg.GasFeeCap != nil {
arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap)
}
if msg.GasTipCap != nil {
arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap)
}
if msg.AccessList != nil {
arg["accessList"] = msg.AccessList
}
if msg.BlobGasFeeCap != nil {
arg["maxFeePerBlobGas"] = (*hexutil.Big)(msg.BlobGasFeeCap)
}
if msg.BlobHashes != nil {
arg["blobVersionedHashes"] = msg.BlobHashes
}
return arg
}

Expand Down
15 changes: 15 additions & 0 deletions ethclient/gethclient/gethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,21 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
if msg.GasPrice != nil {
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
}
if msg.GasFeeCap != nil {
arg["maxFeePerGas"] = (*hexutil.Big)(msg.GasFeeCap)
}
if msg.GasTipCap != nil {
arg["maxPriorityFeePerGas"] = (*hexutil.Big)(msg.GasTipCap)
}
if msg.AccessList != nil {
arg["accessList"] = msg.AccessList
}
if msg.BlobGasFeeCap != nil {
arg["maxFeePerBlobGas"] = (*hexutil.Big)(msg.BlobGasFeeCap)
}
if msg.BlobHashes != nil {
arg["blobVersionedHashes"] = msg.BlobHashes
}
return arg
}

Expand Down
4 changes: 4 additions & 0 deletions interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ type CallMsg struct {

AccessList types.AccessList // EIP-2930 access list.

// For BlobTxType
BlobGasFeeCap *big.Int
BlobHashes []common.Hash

// scroll-related:
// not need to have a `IsL1MessageTx` field, should always be false
}
Expand Down

0 comments on commit 0892822

Please sign in to comment.