Skip to content

Commit

Permalink
Rework tx receipt storage logic (#2085)
Browse files Browse the repository at this point in the history
* rework tx receipt storage logic

* fix personal transactions

* fix merge

* rename

* fix

* add missing receipt fields

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix
  • Loading branch information
tudor-malene authored Oct 16, 2024
1 parent 1c196b0 commit c058c64
Show file tree
Hide file tree
Showing 20 changed files with 466 additions and 273 deletions.
5 changes: 5 additions & 0 deletions go/enclave/components/batch_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ func (executor *batchExecutor) ComputeBatch(ctx context.Context, context *BatchE
for _, txResult := range txResults {
txReceipts = append(txReceipts, txResult.Receipt)
}
// populate the derived fields in the receipt
err = txReceipts.DeriveFields(executor.chainConfig, batch.Hash(), batch.NumberU64(), batch.Header.Time, batch.Header.BaseFee, nil, successfulTxs)
if err != nil {
return nil, fmt.Errorf("could not derive receipts. Cause: %w", err)
}

onBlockTx, err := executor.systemContracts.CreateOnBatchEndTransaction(ctx, stateDB, successfulTxs, txReceipts)
if err != nil && !errors.Is(err, system.ErrNoTransactions) {
Expand Down
89 changes: 89 additions & 0 deletions go/enclave/core/event_types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package core

import (
"math/big"

"github.com/ethereum/go-ethereum/common/hexutil"

gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)
Expand Down Expand Up @@ -28,7 +32,92 @@ type ContractVisibilityConfig struct {
type TxExecResult struct {
Receipt *types.Receipt
CreatedContracts map[gethcommon.Address]*ContractVisibilityConfig
Tx *types.Transaction
Err error
}

// InternalReceipt - Equivalent to the geth types.Receipt, but without weird quirks
type InternalReceipt struct {
PostState []byte
Status uint64
CumulativeGasUsed uint64
EffectiveGasPrice *uint64
CreatedContract *gethcommon.Address
TxContent []byte
TxHash gethcommon.Hash
BlockHash gethcommon.Hash
BlockNumber *big.Int
TransactionIndex uint
From gethcommon.Address
To *gethcommon.Address
TxType uint8
Logs []*types.Log
}

// MarshalToJson marshals a transaction receipt into a JSON object.
// taken from geth
func (receipt *InternalReceipt) MarshalToJson() map[string]interface{} {
var effGasPrice *hexutil.Big
if receipt.EffectiveGasPrice != nil {
effGasPrice = (*hexutil.Big)(big.NewInt(int64(*receipt.EffectiveGasPrice)))
}

fields := map[string]interface{}{
"blockHash": receipt.BlockHash,
"blockNumber": hexutil.Uint64(receipt.BlockNumber.Uint64()),
"transactionHash": receipt.TxHash,
"transactionIndex": hexutil.Uint64(receipt.TransactionIndex),
"from": receipt.From,
"to": receipt.To,
"gasUsed": hexutil.Uint64(receipt.CumulativeGasUsed),
"cumulativeGasUsed": hexutil.Uint64(receipt.CumulativeGasUsed),
"contractAddress": receipt.CreatedContract,
"logs": receipt.Logs,
"logsBloom": types.Bloom{},
"type": hexutil.Uint(receipt.TxType),
"effectiveGasPrice": effGasPrice,
}

// Assign receipt status or post state.
if len(receipt.PostState) > 0 {
fields["root"] = hexutil.Bytes(receipt.PostState)
} else {
fields["status"] = hexutil.Uint(receipt.Status)
}
if receipt.Logs == nil {
fields["logs"] = []*types.Log{}
}

return fields
}

func (receipt *InternalReceipt) ToReceipt() *types.Receipt {
var effGasPrice *big.Int
if receipt.EffectiveGasPrice != nil {
effGasPrice = big.NewInt(int64(*receipt.EffectiveGasPrice))
}

var cc gethcommon.Address
if receipt.CreatedContract != nil {
cc = *receipt.CreatedContract
}
return &types.Receipt{
Type: receipt.TxType,
PostState: receipt.PostState,
Status: receipt.Status,
CumulativeGasUsed: receipt.CumulativeGasUsed,
Bloom: types.Bloom{},
Logs: receipt.Logs,
TxHash: receipt.TxHash,
ContractAddress: cc,
GasUsed: receipt.CumulativeGasUsed,
EffectiveGasPrice: effGasPrice,
BlobGasUsed: 0,
BlobGasPrice: nil,
BlockHash: receipt.BlockHash,
BlockNumber: receipt.BlockNumber,
TransactionIndex: receipt.TransactionIndex,
}
}

type TxExecResults []*TxExecResult
4 changes: 2 additions & 2 deletions go/enclave/core/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import (

// VerifySignature - Checks that the L2Tx has a valid signature.
func VerifySignature(chainID int64, tx *types.Transaction) error {
signer := types.NewLondonSigner(big.NewInt(chainID))
signer := types.LatestSignerForChainID(big.NewInt(chainID))
_, err := types.Sender(signer, tx)
return err
}

// GetAuthenticatedSender - Get sender and tx nonce from transaction
func GetAuthenticatedSender(chainID int64, tx *types.Transaction) (*gethcommon.Address, error) {
signer := types.NewLondonSigner(big.NewInt(chainID))
signer := types.LatestSignerForChainID(tx.ChainId())
sender, err := types.Sender(signer, tx)
if err != nil {
return nil, err
Expand Down
8 changes: 4 additions & 4 deletions go/enclave/evm/evm_facade.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,9 @@ func executeTransaction(
) *core.TxExecResult {
var createdContracts []*gethcommon.Address
rules := cc.Rules(big.NewInt(0), true, 0)
from, err := types.Sender(types.LatestSigner(cc), t.Tx)
from, err := core.GetTxSigner(t.Tx)
if err != nil {
return &core.TxExecResult{Err: err}
return &core.TxExecResult{Tx: t.Tx, Err: err}
}
s.Prepare(rules, from, gethcommon.Address{}, t.Tx.To(), nil, nil)
snap := s.Snapshot()
Expand Down Expand Up @@ -245,15 +245,15 @@ func executeTransaction(
header.MixDigest = before
if err != nil {
s.RevertToSnapshot(snap)
return &core.TxExecResult{Receipt: receipt, Err: err}
return &core.TxExecResult{Receipt: receipt, Tx: t.Tx, Err: err}
}

contractsWithVisibility := make(map[gethcommon.Address]*core.ContractVisibilityConfig)
for _, contractAddress := range createdContracts {
contractsWithVisibility[*contractAddress] = readVisibilityConfig(vmenv, contractAddress)
}

return &core.TxExecResult{Receipt: receipt, CreatedContracts: contractsWithVisibility}
return &core.TxExecResult{Receipt: receipt, Tx: t.Tx, CreatedContracts: contractsWithVisibility}
}

const (
Expand Down
2 changes: 1 addition & 1 deletion go/enclave/l2chain/l2_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func (oc *obscuroChain) ObsCallAtBlock(ctx context.Context, apiArgs *gethapi.Tra
}

if oc.logger.Enabled(context.Background(), gethlog.LevelTrace) {
oc.logger.Trace("Obs_Call: Successful result", "result", fmt.Sprintf("contractAddress=%s, from=%s, data=%s, batch=%s, state=%s",
oc.logger.Trace("Obs_Call: Successful result", "result", fmt.Sprintf("to=%s, from=%s, data=%s, batch=%s, state=%s",
callMsg.To,
callMsg.From,
hexutils.BytesToHex(callMsg.Data),
Expand Down
2 changes: 1 addition & 1 deletion go/enclave/rpc/EstimateGas.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ func (rpc *EncryptionManager) doEstimateGas(ctx context.Context, args *gethapi.T
}
cap = hi //nolint: revive
isFailedAtMax, _, err := rpc.isGasEnough(ctx, args, hi, blkNumber)
//TODO: Workaround for the weird conensus nil statement down, which gets interwined with evm errors.
// TODO: Workaround for the weird conensus nil statement down, which gets interwined with evm errors.
// Here if there is a consensus error - we'd bail. If the tx fails at max gas - we'd bail (probably bad)
if err != nil {
return 0, gethcommon.Big0, err
Expand Down
11 changes: 9 additions & 2 deletions go/enclave/rpc/GetPersonalTransactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package rpc
import (
"fmt"

"github.com/ethereum/go-ethereum/core/types"

"github.com/ten-protocol/go-ten/go/common"
"github.com/ten-protocol/go-ten/go/common/gethencoding"
)
Expand Down Expand Up @@ -32,18 +34,23 @@ func GetPersonalTransactionsExecute(builder *CallBuilder[common.ListPrivateTrans
return nil //nolint:nilerr
}
addr := builder.Param.Address
encryptReceipts, err := rpc.storage.GetTransactionsPerAddress(builder.ctx, &addr, &builder.Param.Pagination)
internalReceipts, err := rpc.storage.GetTransactionsPerAddress(builder.ctx, &addr, &builder.Param.Pagination)
if err != nil {
return fmt.Errorf("GetTransactionsPerAddress - %w", err)
}

var receipts types.Receipts
for _, receipt := range internalReceipts {
receipts = append(receipts, receipt.ToReceipt())
}

receiptsCount, err := rpc.storage.CountTransactionsPerAddress(builder.ctx, &addr)
if err != nil {
return fmt.Errorf("CountTransactionsPerAddress - %w", err)
}

builder.ReturnValue = &common.PrivateTransactionsQueryResponse{
Receipts: encryptReceipts,
Receipts: receipts,
Total: receiptsCount,
}
return nil
Expand Down
9 changes: 2 additions & 7 deletions go/enclave/rpc/GetTransaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,7 @@ func GetTransactionExecute(builder *CallBuilder[gethcommon.Hash, RpcTransaction]
return nil
}

// Unlike in the Geth impl, we hardcode the use of a London signer.
// todo (#1553) - once the enclave's genesis.json is set, retrieve the signer type using `types.MakeSigner`
signer := types.NewLondonSigner(tx.ChainId())
rpcTx := newRPCTransaction(tx, blockHash, blockNumber, index, rpc.config.BaseFee, signer)
builder.ReturnValue = rpcTx
builder.ReturnValue = newRPCTransaction(tx, blockHash, blockNumber, index, rpc.config.BaseFee, sender)
return nil
}

Expand Down Expand Up @@ -85,8 +81,7 @@ type RpcTransaction struct { //nolint
}

// Lifted from Geth's internal `ethapi` package.
func newRPCTransaction(tx *types.Transaction, blockHash gethcommon.Hash, blockNumber uint64, index uint64, baseFee *big.Int, signer types.Signer) *RpcTransaction {
from, _ := types.Sender(signer, tx)
func newRPCTransaction(tx *types.Transaction, blockHash gethcommon.Hash, blockNumber uint64, index uint64, baseFee *big.Int, from gethcommon.Address) *RpcTransaction {
v, r, s := tx.RawSignatureValues()
result := &RpcTransaction{
Type: hexutil.Uint64(tx.Type()),
Expand Down
93 changes: 10 additions & 83 deletions go/enclave/rpc/GetTransactionReceipt.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,10 @@ package rpc
import (
"errors"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ten-protocol/go-ten/go/enclave/evm/ethchainadapter"

"github.com/ten-protocol/go-ten/go/enclave/core"

gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ten-protocol/go-ten/go/common/errutil"
"github.com/ten-protocol/go-ten/go/common/log"
"github.com/ten-protocol/go-ten/go/enclave/events"
)

func GetTransactionReceiptValidate(reqParams []any, builder *CallBuilder[gethcommon.Hash, map[string]interface{}], _ *EncryptionManager) error {
Expand All @@ -36,97 +28,32 @@ func GetTransactionReceiptValidate(reqParams []any, builder *CallBuilder[gethcom

func GetTransactionReceiptExecute(builder *CallBuilder[gethcommon.Hash, map[string]interface{}], rpc *EncryptionManager) error {
txHash := *builder.Param
// todo - optimise these calls. This can be done with a single sql
rpc.logger.Trace("Get receipt for ", log.TxKey, txHash)
// We retrieve the transaction.
tx, blockHash, number, txIndex, err := rpc.storage.GetTransaction(builder.ctx, txHash) //nolint:dogsled
if err != nil {
rpc.logger.Trace("error getting tx ", log.TxKey, txHash, log.ErrKey, err)
if errors.Is(err, errutil.ErrNotFound) {
builder.Status = NotFound
return nil
}
return err
}
requester := builder.VK.AccountAddress
rpc.logger.Trace("Get receipt for ", log.TxKey, txHash, "requester", requester.Hex())

// We retrieve the txSigner's address.
txSigner, err := core.GetTxSigner(tx)
exists, err := rpc.storage.ExistsTransactionReceipt(builder.ctx, txHash)
if err != nil {
builder.Err = err
return nil //nolint:nilerr
return fmt.Errorf("could not retrieve transaction receipt in eth_getTransactionReceipt request. Cause: %w", err)
}

if txSigner.Hex() != builder.VK.AccountAddress.Hex() {
builder.Status = NotAuthorised
if !exists {
builder.Status = NotFound
return nil
}

// We retrieve the transaction receipt.
txReceipt, err := rpc.storage.GetTransactionReceipt(builder.ctx, txHash)
receipt, err := rpc.storage.GetTransactionReceipt(builder.ctx, txHash, requester, false)
if err != nil {
rpc.logger.Trace("error getting tx receipt", log.TxKey, txHash, log.ErrKey, err)
if errors.Is(err, errutil.ErrNotFound) {
builder.Status = NotFound
builder.Status = NotAuthorised
return nil
}
// this is a system error
return fmt.Errorf("could not retrieve transaction receipt in eth_getTransactionReceipt request. Cause: %w", err)
}

// We filter out irrelevant logs.
txReceipt.Logs, err = events.FilterLogsForReceipt(builder.ctx, txReceipt, &txSigner, rpc.registry)
if err != nil {
rpc.logger.Error("error filter logs ", log.TxKey, txHash, log.ErrKey, err)
// this is a system error
return err
}

rpc.logger.Trace("Successfully retrieved receipt for ", log.TxKey, txHash, "rec", txReceipt)
signer := types.MakeSigner(ethchainadapter.ChainParams(big.NewInt(rpc.config.ObscuroChainID)), big.NewInt(int64(number)), 0)
r := marshalReceipt(txReceipt, blockHash, number, signer, tx, int(txIndex))
rpc.logger.Trace("Successfully retrieved receipt for ", log.TxKey, txHash, "rec", receipt)
r := receipt.MarshalToJson()
builder.ReturnValue = &r
return nil
}

// marshalReceipt marshals a transaction receipt into a JSON object.
// taken from geth
func marshalReceipt(receipt *types.Receipt, blockHash gethcommon.Hash, blockNumber uint64, signer types.Signer, tx *types.Transaction, txIndex int) map[string]interface{} {
from, _ := types.Sender(signer, tx)

fields := map[string]interface{}{
"blockHash": blockHash,
"blockNumber": hexutil.Uint64(blockNumber),
"transactionHash": tx.Hash(),
"transactionIndex": hexutil.Uint64(txIndex),
"from": from,
"to": tx.To(),
"gasUsed": hexutil.Uint64(receipt.GasUsed),
"cumulativeGasUsed": hexutil.Uint64(receipt.CumulativeGasUsed),
"contractAddress": nil,
"logs": receipt.Logs,
"logsBloom": receipt.Bloom,
"type": hexutil.Uint(tx.Type()),
"effectiveGasPrice": (*hexutil.Big)(receipt.EffectiveGasPrice),
}

// Assign receipt status or post state.
if len(receipt.PostState) > 0 {
fields["root"] = hexutil.Bytes(receipt.PostState)
} else {
fields["status"] = hexutil.Uint(receipt.Status)
}
if receipt.Logs == nil {
fields["logs"] = []*types.Log{}
}

if tx.Type() == types.BlobTxType {
fields["blobGasUsed"] = hexutil.Uint64(receipt.BlobGasUsed)
fields["blobGasPrice"] = (*hexutil.Big)(receipt.BlobGasPrice)
}

// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
if receipt.ContractAddress != (gethcommon.Address{}) {
fields["contractAddress"] = receipt.ContractAddress
}
return fields
}
Loading

0 comments on commit c058c64

Please sign in to comment.