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 }