github.com/ethereum-optimism/optimism@v1.7.2/op-node/rollup/derive/deposit_log.go (about)

     1  package derive
     2  
     3  import (
     4  	"encoding/binary"
     5  	"fmt"
     6  	"math/big"
     7  
     8  	"github.com/holiman/uint256"
     9  
    10  	"github.com/ethereum/go-ethereum/common"
    11  	"github.com/ethereum/go-ethereum/core/types"
    12  	"github.com/ethereum/go-ethereum/crypto"
    13  
    14  	"github.com/ethereum-optimism/optimism/op-service/eth"
    15  )
    16  
    17  var (
    18  	DepositEventABI      = "TransactionDeposited(address,address,uint256,bytes)"
    19  	DepositEventABIHash  = crypto.Keccak256Hash([]byte(DepositEventABI))
    20  	DepositEventVersion0 = common.Hash{}
    21  )
    22  
    23  // UnmarshalDepositLogEvent decodes an EVM log entry emitted by the deposit contract into typed deposit data.
    24  //
    25  // parse log data for:
    26  //
    27  //	event TransactionDeposited(
    28  //	    address indexed from,
    29  //	    address indexed to,
    30  //	    uint256 indexed version,
    31  //	    bytes opaqueData
    32  //	);
    33  //
    34  // Additionally, the event log-index and
    35  func UnmarshalDepositLogEvent(ev *types.Log) (*types.DepositTx, error) {
    36  	if len(ev.Topics) != 4 {
    37  		return nil, fmt.Errorf("expected 4 event topics (event identity, indexed from, indexed to, indexed version), got %d", len(ev.Topics))
    38  	}
    39  	if ev.Topics[0] != DepositEventABIHash {
    40  		return nil, fmt.Errorf("invalid deposit event selector: %s, expected %s", ev.Topics[0], DepositEventABIHash)
    41  	}
    42  	if len(ev.Data) < 64 {
    43  		return nil, fmt.Errorf("incomplate opaqueData slice header (%d bytes): %x", len(ev.Data), ev.Data)
    44  	}
    45  	if len(ev.Data)%32 != 0 {
    46  		return nil, fmt.Errorf("expected log data to be multiple of 32 bytes: got %d bytes", len(ev.Data))
    47  	}
    48  
    49  	// indexed 0
    50  	from := common.BytesToAddress(ev.Topics[1][12:])
    51  	// indexed 1
    52  	to := common.BytesToAddress(ev.Topics[2][12:])
    53  	// indexed 2
    54  	version := ev.Topics[3]
    55  	// unindexed data
    56  	// Solidity serializes the event's Data field as follows:
    57  	// abi.encode(abi.encodPacked(uint256 mint, uint256 value, uint64 gasLimit, uint8 isCreation, bytes data))
    58  	// Thus the first 32 bytes of the Data will give us the offset of the opaqueData,
    59  	// which should always be 0x20.
    60  	var opaqueContentOffset uint256.Int
    61  	opaqueContentOffset.SetBytes(ev.Data[0:32])
    62  	if !opaqueContentOffset.IsUint64() || opaqueContentOffset.Uint64() != 32 {
    63  		return nil, fmt.Errorf("invalid opaqueData slice header offset: %d", opaqueContentOffset.Uint64())
    64  	}
    65  	// The next 32 bytes indicate the length of the opaqueData content.
    66  	var opaqueContentLength uint256.Int
    67  	opaqueContentLength.SetBytes(ev.Data[32:64])
    68  	// Make sure the length is an uint64, it's not larger than the remaining data, and the log is using minimal padding (i.e. can't add 32 bytes without exceeding data)
    69  	if !opaqueContentLength.IsUint64() || opaqueContentLength.Uint64() > uint64(len(ev.Data)-64) || opaqueContentLength.Uint64()+32 <= uint64(len(ev.Data)-64) {
    70  		return nil, fmt.Errorf("invalid opaqueData slice header length: %d", opaqueContentLength.Uint64())
    71  	}
    72  	// The remaining data is the opaqueData which is tightly packed
    73  	// and then padded to 32 bytes by the EVM.
    74  	opaqueData := ev.Data[64 : 64+opaqueContentLength.Uint64()]
    75  
    76  	var dep types.DepositTx
    77  
    78  	source := UserDepositSource{
    79  		L1BlockHash: ev.BlockHash,
    80  		LogIndex:    uint64(ev.Index),
    81  	}
    82  	dep.SourceHash = source.SourceHash()
    83  	dep.From = from
    84  	dep.IsSystemTransaction = false
    85  
    86  	var err error
    87  	switch version {
    88  	case DepositEventVersion0:
    89  		err = unmarshalDepositVersion0(&dep, to, opaqueData)
    90  	default:
    91  		return nil, fmt.Errorf("invalid deposit version, got %s", version)
    92  	}
    93  	if err != nil {
    94  		return nil, fmt.Errorf("failed to decode deposit (version %s): %w", version, err)
    95  	}
    96  	return &dep, nil
    97  }
    98  
    99  func unmarshalDepositVersion0(dep *types.DepositTx, to common.Address, opaqueData []byte) error {
   100  	if len(opaqueData) < 32+32+8+1 {
   101  		return fmt.Errorf("unexpected opaqueData length: %d", len(opaqueData))
   102  	}
   103  	offset := uint64(0)
   104  
   105  	// uint256 mint
   106  	dep.Mint = new(big.Int).SetBytes(opaqueData[offset : offset+32])
   107  	// 0 mint is represented as nil to skip minting code
   108  	if dep.Mint.Cmp(new(big.Int)) == 0 {
   109  		dep.Mint = nil
   110  	}
   111  	offset += 32
   112  
   113  	// uint256 value
   114  	dep.Value = new(big.Int).SetBytes(opaqueData[offset : offset+32])
   115  	offset += 32
   116  
   117  	// uint64 gas
   118  	gas := new(big.Int).SetBytes(opaqueData[offset : offset+8])
   119  	if !gas.IsUint64() {
   120  		return fmt.Errorf("bad gas value: %x", opaqueData[offset:offset+8])
   121  	}
   122  	dep.Gas = gas.Uint64()
   123  	offset += 8
   124  
   125  	// uint8 isCreation
   126  	// isCreation: If the boolean byte is 1 then dep.To will stay nil,
   127  	// and it will create a contract using L2 account nonce to determine the created address.
   128  	if opaqueData[offset] == 0 {
   129  		dep.To = &to
   130  	}
   131  	offset += 1
   132  
   133  	// The remainder of the opaqueData is the transaction data (without length prefix).
   134  	// The data may be padded to a multiple of 32 bytes
   135  	txDataLen := uint64(len(opaqueData)) - offset
   136  
   137  	// remaining bytes fill the data
   138  	dep.Data = opaqueData[offset : offset+txDataLen]
   139  
   140  	return nil
   141  }
   142  
   143  // MarshalDepositLogEvent returns an EVM log entry that encodes a TransactionDeposited event from the deposit contract.
   144  // This is the reverse of the deposit transaction derivation.
   145  func MarshalDepositLogEvent(depositContractAddr common.Address, deposit *types.DepositTx) (*types.Log, error) {
   146  	toBytes := common.Hash{}
   147  	if deposit.To != nil {
   148  		toBytes = eth.AddressAsLeftPaddedHash(*deposit.To)
   149  	}
   150  	topics := []common.Hash{
   151  		DepositEventABIHash,
   152  		eth.AddressAsLeftPaddedHash(deposit.From),
   153  		toBytes,
   154  		DepositEventVersion0,
   155  	}
   156  
   157  	data := make([]byte, 64, 64+3*32)
   158  
   159  	// opaqueData slice content offset: value will always be 0x20.
   160  	binary.BigEndian.PutUint64(data[32-8:32], 32)
   161  
   162  	opaqueData, err := marshalDepositVersion0(deposit)
   163  	if err != nil {
   164  		return &types.Log{}, err
   165  	}
   166  
   167  	// opaqueData slice length
   168  	binary.BigEndian.PutUint64(data[64-8:64], uint64(len(opaqueData)))
   169  
   170  	// opaqueData slice content
   171  	data = append(data, opaqueData...)
   172  
   173  	// pad to multiple of 32
   174  	if len(data)%32 != 0 {
   175  		data = append(data, make([]byte, 32-(len(data)%32))...)
   176  	}
   177  
   178  	return &types.Log{
   179  		Address: depositContractAddr,
   180  		Topics:  topics,
   181  		Data:    data,
   182  		Removed: false,
   183  
   184  		// ignored (zeroed):
   185  		BlockNumber: 0,
   186  		TxHash:      common.Hash{},
   187  		TxIndex:     0,
   188  		BlockHash:   common.Hash{},
   189  		Index:       0,
   190  	}, nil
   191  }
   192  
   193  func marshalDepositVersion0(deposit *types.DepositTx) ([]byte, error) {
   194  	opaqueData := make([]byte, 32+32+8+1, 32+32+8+1+len(deposit.Data))
   195  	offset := 0
   196  
   197  	// uint256 mint
   198  	if deposit.Mint != nil {
   199  		if deposit.Mint.BitLen() > 256 {
   200  			return nil, fmt.Errorf("mint value exceeds 256 bits: %d", deposit.Mint)
   201  		}
   202  		deposit.Mint.FillBytes(opaqueData[offset : offset+32])
   203  	}
   204  	offset += 32
   205  
   206  	// uint256 value
   207  	if deposit.Value.BitLen() > 256 {
   208  		return nil, fmt.Errorf("value value exceeds 256 bits: %d", deposit.Value)
   209  	}
   210  	deposit.Value.FillBytes(opaqueData[offset : offset+32])
   211  	offset += 32
   212  
   213  	// uint64 gas
   214  	binary.BigEndian.PutUint64(opaqueData[offset:offset+8], deposit.Gas)
   215  	offset += 8
   216  
   217  	// uint8 isCreation
   218  	if deposit.To == nil { // isCreation
   219  		opaqueData[offset] = 1
   220  	}
   221  
   222  	// Deposit data then fills the remaining event data
   223  	opaqueData = append(opaqueData, deposit.Data...)
   224  
   225  	return opaqueData, nil
   226  }