Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .agents/code-insights.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
- `DecodeModule` skips DWARF indexing when `dwarf.New` returns nil, preventing crashes on minimal modules.
- DWARF lookups guard missing call-site metadata and tracing helpers skip absent debug info; tracing/DWARF/stylus coverage is still thin and needs focused tests.
- Use `maintester.StripKnownDWARFWarnings` to drop the known DWARF warning before asserting stderr in examples and filecache integration tests.
- Stylus `emit_log` host hook now resolves ABI signatures and decodes topics/payloads locally (including arrays, dynamic bytes/strings), falling back to hash-only output for dynamic indexed params.
- Stylus log decoder now understands tuple parameter types (including nested/dynamic fields) and renders them as `(v0, v1, …)`; extend tests accordingly when adding new ABI shapes.
68 changes: 68 additions & 0 deletions .agents/others/emit_log_serde_spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
### emit_log Serialization Decoding Guide

This document specifies how to decode the `Args` byte sequence that accompanies an `emit_log` hostio event in Stylus traces (as surfaced via `debug_traceCall`).

#### Byte Sequence Structure

```
Offset Size Type (encoded) Meaning
0 4 bytes u32 (big-endian) Topic count `topicCount`
4 32 * n topic[n] (bytes32) n 32-byte topic hashes, contiguous
rest m bytes bytes (raw) Log data payload
```

`n` equals `topicCount`. `m` may be zero. No additional padding or length prefixes appear after the header.

#### Decoding Procedure

1. **Read Topic Count (`u32`)**
- Bytes `[0..4)` form a big-endian unsigned 32-bit integer.
- Conventionally limited to the range `0…4`; higher values indicate malformed input.

2. **Extract Topics (`topicCount × bytes32`)**
- For each index `i` in `0…topicCount-1`, slice bytes `[4 + 32*i … 4 + 32*(i+1))`.
- Treat each slice as an EVM topic hash (32-byte word).
- Preserve the original order; `topic[0]` corresponds to `LOGn`’s `topic0`, etc.

3. **Extract Log Data (`bytes`)**
- Remaining bytes starting at offset `4 + 32*topicCount` form the log payload.
- Interpret as arbitrary byte array (may be empty).
- No alignment or padding: payload length is `totalLen - 4 - 32*topicCount`.

#### Mapping to EVM Event ABI

- `topic[0]` (if `topicCount > 0`) is usually the Keccak-256 hash of the event signature (e.g., `keccak256("Transfer(address,address,uint256)")`).
- Additional topics represent indexed event parameters encoded as 32-byte ABI words.
- The data payload contains the concatenated ABI encoding of all non-indexed event parameters, identical to Ethereum’s event ABI rules. You must know the event signature to decode individual fields.

#### ABI Decoding Cheat Sheet (Indexed Parameters → Topics)

- **`uint<M>` / `int<M>` / `bool`**: 32-byte word; decode exactly as you would pop from the stack (big-endian, two’s complement for signed).
- **`address`**: rightmost 20 bytes of the 32-byte topic; leftmost 12 bytes are zero padding.
- **`bytes32` / `hash`**: topic already is the 32-byte value.
- **Static tuple or fixed-size array**: not supported directly; the entire tuple serializes to its Keccak-256 hash before being placed in a topic.
- **Dynamic types (string, bytes, dynamic arrays, dynamic tuples, structs)**: Solidity ABI stores `keccak256(value)` in the topic. You must use the original event parameters or compare hashes because the raw value is not present.

#### ABI Decoding Cheat Sheet (Non-Indexed Parameters → Data Payload)

Static types occupy fixed 32-byte slots; dynamic types use 32-byte offsets into a tail section. The following summary matches the Ethereum ABI specification (`soliditylang.org/docs/abi-spec.html`):

- **`uint<M>` / `int<M>`**: big-endian two’s-complement in 32 bytes; `<M>` must be ≤256 and divisible by 8. Example: decode by reading the 32-byte word as unsigned/signed integer.
- **`bool`**: same as `uint8`; value is `0` or `1`.
- **`address`**: rightmost 20 bytes contain the address; leftmost 12 bytes are zero padding.
- **`bytes32` / `keccak256` hashes**: 32 raw bytes.
- **Static `tuple` / fixed-size array (`T[k]`)**: concatenation of each element’s 32-byte encoding.
- **`bytes` / `string` / dynamic array**:
- Word `w0`: 32-byte offset (from start of the data section) to the actual payload.
- At `offset`: 32-byte length `L`.
- Followed by `ceil(L/32)` words of data (right-padded with zeros).
- **Dynamic tuple**: treat each component like a standalone field; static components appear inline, dynamic components hold offsets into the shared tail.

Decoding recipe for payload:
1. Split the payload into 32-byte words.
2. For each parameter (in declaration order) apply the ABI rules:
- Static parameter: interpret its word(s) directly.
- Dynamic parameter: read its offset word, jump to `payloadStart + offset`, read length and the subsequent bytes.
3. Apply type-specific conversions (e.g., trim leading zeros for addresses and shorter ints, decode UTF-8 for strings, iterate arrays).

Remember: Stylus does not prepend additional metadata—ABI semantics are identical to standard Ethereum logs. Use the event signature or ABI supplied by the contract to decide which decoding path to follow.
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
The file `.agents/code-insights.md` is your checklist before interacting the codebase. Treat it as mandatory reading and maintenance.

- **Before starting any task, ALWAYS open and review** `.agents/code-insights.md` so you inherit the latest context.
- Capture new insights, architectural notes, domain knowledge, debugging breadcrumbs, surprising findings, edge cases, and other non-trivial behaviors in `.agents/code-insights.md`.
- Keep entries in that insights file clear, concise, and dated when helpful so others can trust the context quickly.
- Update the insights file continuously: add new learnings immediately, revise stale items, and REMOVE information the moment it stops being true.
- If you are unsure whether something belongs in `.agents/code-insights.md`, err on the side of writing it down.
- Capture only non-trivial insights—complex behaviors, surprising findings, architectural pivots, or tricky edge cases—in `.agents/code-insights.md`.
- Do not record routine task updates or obvious observations; keep the file focused on durable, high-signal knowledge.
- Keep entries concise so others can trust the context quickly, and prune or revise them when they stop being accurate or relevant.

Maintaining `.agents/code-insights.md` is part of completing every task.

## Testing Standards

- Always design and commit comprehensive tests that cover baseline behavior and all edge cases **before** implementing the associated functionality.
- Keep test output minimal: successful runs must be silent, and failures should surface only the information necessary to pinpoint the fault.
- Consider a task complete only after all relevant tests run and pass.

## Code Comments
Expand Down
6 changes: 5 additions & 1 deletion cmd/wazero/wazero.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,10 @@ func doRun(args []string, stdOut io.Writer, stdErr logging.Writer) int {
flags.StringVar(&stylusTracePath, "stylus", "",
"Imports the EVM hook functions and mocks their IO according the result of debug_traceTransaction in the path provided.")

var stylusSignatureMapPath string
flags.StringVar(&stylusSignatureMapPath, "stylus-signature-map", "",
"Signature map of the EVM events. Used to decode events.")

var traceDir string
flags.StringVar(&traceDir, "trace-dir", "",
"Directory where to save the trace record. If empty - no trace is produced. Default \"\".")
Expand Down Expand Up @@ -344,7 +348,7 @@ func doRun(args []string, stdOut io.Writer, stdErr logging.Writer) int {

var stylusState *stylus.StylusTrace
if stylusTracePath != "" {
stylusState, err = stylus.Instantiate(ctx, rt, stylusTracePath, traceRecordPtr)
stylusState, err = stylus.Instantiate(ctx, rt, stylusTracePath, stylusSignatureMapPath, traceRecordPtr)
if err != nil {
fmt.Fprintf(stdErr, "error reading stylus trace: %v\n", err)
return 1
Expand Down
Loading