diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index ec168ec5e..9f8a11bcd 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -836,3 +836,117 @@ func (a *EthModule) newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLo } return tx, nil } +<<<<<<< Updated upstream +======= + +func newEthTxReceipt(ctx context.Context, tx api.EthTx, lookup *api.MsgLookup, replay *api.InvocResult, events []types.Event, sa StateAPI) (api.EthTxReceipt, error) { + receipt := api.EthTxReceipt{ + TransactionHash: tx.Hash, + TransactionIndex: tx.TransactionIndex, + BlockHash: tx.BlockHash, + BlockNumber: tx.BlockNumber, + From: tx.From, + To: tx.To, + Type: api.EthUint64(2), + Logs: []api.EthLog{}, // empty log array is compulsory when no logs, or libraries like ethers.js break + LogsBloom: []byte{0}, + } + + if receipt.To == nil && lookup.Receipt.ExitCode.IsSuccess() { + // Create and Create2 return the same things. + var ret eam.CreateReturn + if err := ret.UnmarshalCBOR(bytes.NewReader(lookup.Receipt.Return)); err != nil { + return api.EthTxReceipt{}, xerrors.Errorf("failed to parse contract creation result: %w", err) + } + addr := api.EthAddress(ret.EthAddress) + receipt.ContractAddress = &addr + } + + if lookup.Receipt.ExitCode.IsSuccess() { + receipt.Status = 1 + } + if lookup.Receipt.ExitCode.IsError() { + receipt.Status = 0 + } + + if len(events) > 0 { + // TODO return a dummy non-zero bloom to signal that there are logs + // need to figure out how worth it is to populate with a real bloom + // should be feasible here since we are iterating over the logs anyway + receipt.LogsBloom = make([]byte, 256) + receipt.LogsBloom[255] = 0x01 + + receipt.Logs = make([]api.EthLog, 0, len(events)) + for i, evt := range events { + l := api.EthLog{ + Removed: false, + LogIndex: api.EthUint64(i), + TransactionIndex: tx.TransactionIndex, + TransactionHash: tx.Hash, + BlockHash: tx.BlockHash, + BlockNumber: tx.BlockNumber, + } + + for _, entry := range evt.Entries { + value := api.EthBytes(leftpad32(decodeLogBytes(entry.Value))) + if entry.Key == api.EthTopic1 || entry.Key == api.EthTopic2 || entry.Key == api.EthTopic3 || entry.Key == api.EthTopic4 { + l.Topics = append(l.Topics, value) + } else { + l.Data = value + } + } + + addr, err := address.NewIDAddress(uint64(evt.Emitter)) + if err != nil { + return api.EthTxReceipt{}, xerrors.Errorf("failed to create ID address: %w", err) + } + + l.Address, err = lookupEthAddress(ctx, addr, sa) + if err != nil { + return api.EthTxReceipt{}, xerrors.Errorf("failed to resolve Ethereum address: %w", err) + } + + receipt.Logs = append(receipt.Logs, l) + } + } + + receipt.GasUsed = api.EthUint64(lookup.Receipt.GasUsed) + + // TODO: handle CumulativeGasUsed + receipt.CumulativeGasUsed = api.EmptyEthInt + + effectiveGasPrice := big.Div(replay.GasCost.TotalCost, big.NewInt(lookup.Receipt.GasUsed)) + receipt.EffectiveGasPrice = api.EthBigInt(effectiveGasPrice) + + return receipt, nil +} + +// decodeLogBytes decodes a CBOR-serialized array into its original form. +// +// This function swallows errors and returns the original array if it failed +// to decode. +func decodeLogBytes(orig []byte) []byte { + if orig == nil { + return orig + } + decoded, err := cbg.ReadByteArray(bytes.NewReader(orig), uint64(len(orig))) + if err != nil { + return orig + } + return decoded +} + +// TODO we could also emit full EVM words from the EVM runtime, but not doing so +// makes the contract slightly cheaper (and saves storage bytes), at the expense +// of having to left pad in the API, which is a pretty acceptable tradeoff at +// face value. There may be other protocol implications to consider. +func leftpad32(orig []byte) []byte { + needed := 32 - len(orig) + if needed <= 0 { + return orig + } + ret := make([]byte, 32) + copy(ret[needed:], orig) + return ret +} +>>>>>>> Stashed changes