forked from moonbeam-foundation/moonbeam
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Migrate Tracing tests to Moonwall (moonbeam-foundation#2498)
* set up initial config and start migration * add pnpm to CI * fmt * download built node in CI * binary permissions * move tracing folder to suites * erc20-xcm * Clean * migrate test-trace-gas * migrate test-trace-ethereum-xcm * migrate remaining test-trace * test-trace-filter * remove env vars * use proper branch ref --------- Co-authored-by: Francisco Gamundi <[email protected]>
- Loading branch information
Showing
19 changed files
with
1,909 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -374,24 +374,35 @@ jobs: | |
with: | ||
name: moonbeam | ||
path: build | ||
- name: Use pnpm | ||
uses: pnpm/action-setup@v2 | ||
with: | ||
version: 8.6.12 | ||
- name: Use Node.js 18.x | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: 20.x | ||
cache: "pnpm" | ||
cache-dependency-path: test/pnpm-lock.yaml | ||
- run: | | ||
mkdir -p target/release | ||
- name: "Download branch built node" | ||
uses: actions/[email protected] | ||
with: | ||
name: moonbeam | ||
path: target/release | ||
- name: Get tracing runtimes | ||
run: | | ||
./scripts/build-last-tracing-runtime.sh ${{ needs.set-tags.outputs.git_branch }} | ||
mkdir -p archived_tests/moonbase-overrides/ | ||
mv build/wasm/moonbase-runtime-local-substitute-tracing.wasm archived_tests/moonbase-overrides/ | ||
mkdir -p test/moonbase-overrides/ | ||
mv build/wasm/moonbase-runtime-local-substitute-tracing.wasm test/moonbase-overrides/ | ||
- name: Typescript tracing tests (against dev service) | ||
env: | ||
BINARY_PATH: ../build/moonbeam | ||
ETHAPI_CMD: --ethapi=txpool,debug,trace | ||
FORCE_WASM_EXECUTION: true | ||
FORCE_COMPILED_WASM: true | ||
WASM_RUNTIME_OVERRIDES: moonbase-overrides | ||
DEBUG_COLOURS: "1" | ||
NODE_OPTIONS: "--max-old-space-size=12288" | ||
run: | | ||
chmod uog+x build/moonbeam | ||
chmod uog+x target/release/moonbeam | ||
#### Preparing the repository | ||
cd moonbeam-types-bundle | ||
|
@@ -402,17 +413,11 @@ jobs: | |
cd ../typescript-api | ||
npm ci | ||
cd ../archived_tests | ||
npm ci | ||
#### Prepares and copies the typescript generated API to include in the tests | ||
npm run setup-typescript-api | ||
#### Compile typescript tests into javascript (more stable for Mocha) | ||
#### This also better display typescript issues | ||
npm run build | ||
#### Run tracing tests with mocha | ||
node_modules/.bin/mocha --exit --parallel -j 2 'build/tracing-tests/**/test-*.js' | ||
#### Install and run dev tracing | ||
cd ../test | ||
pnpm install | ||
pnpm compile-solidity | ||
pnpm moonwall test dev_moonbase_tracing | ||
docker-moonbeam: | ||
runs-on: ubuntu-latest | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"body": "// tracer allows Geth's `debug_traceTransaction` to mimic the output of Parity's `trace_replayTransaction`\n{\n // The call stack of the EVM execution.\n callStack: [{}],\n\n // step is invoked for every opcode that the VM executes.\n step(log, db) {\n // Capture any errors immediately\n const error = log.getError();\n\n if (error !== undefined) {\n this.fault(log, db);\n } else {\n this.success(log, db);\n }\n },\n\n // fault is invoked when the actual execution of an opcode fails.\n fault(log, db) {\n // If the topmost call already reverted, don't handle the additional fault again\n if (this.topCall().error === undefined) {\n this.putError(log);\n }\n },\n\n putError(log) {\n if (this.callStack.length > 1) {\n this.putErrorInTopCall(log);\n } else {\n this.putErrorInBottomCall(log);\n }\n },\n\n putErrorInTopCall(log) {\n // Pop off the just failed call\n const call = this.callStack.pop();\n this.putErrorInCall(log, call);\n this.pushChildCall(call);\n },\n\n putErrorInBottomCall(log) {\n const call = this.bottomCall();\n this.putErrorInCall(log, call);\n },\n\n putErrorInCall(log, call) {\n call.error = log.getError();\n\n // Consume all available gas and clean any leftovers\n if (call.gasBigInt !== undefined) {\n call.gasUsedBigInt = call.gasBigInt;\n }\n\n delete call.outputOffset;\n delete call.outputLength;\n },\n\n topCall() {\n return this.callStack[this.callStack.length - 1];\n },\n\n bottomCall() {\n return this.callStack[0];\n },\n\n pushChildCall(childCall) {\n const topCall = this.topCall();\n\n if (topCall.calls === undefined) {\n topCall.calls = [];\n }\n\n topCall.calls.push(childCall);\n },\n\n pushGasToTopCall(log) {\n const topCall = this.topCall();\n\n if (topCall.gasBigInt === undefined) {\n topCall.gasBigInt = log.getGas();\n }\n topCall.gasUsedBigInt = topCall.gasBigInt - log.getGas() - log.getCost();\n },\n\n success(log, db) {\n const op = log.op.toString();\n\n this.beforeOp(log, db);\n\n switch (op) {\n case 'CREATE':\n this.createOp(log);\n break;\n case 'CREATE2':\n this.create2Op(log);\n break;\n case 'SELFDESTRUCT':\n this.selfDestructOp(log, db);\n break;\n case 'CALL':\n case 'CALLCODE':\n case 'DELEGATECALL':\n case 'STATICCALL':\n this.callOp(log, op);\n break;\n case 'REVERT':\n this.revertOp();\n break;\n }\n },\n\n beforeOp(log, db) {\n /**\n * Depths\n * 0 - `ctx`. Never shows up in `log.getDepth()`\n * 1 - first level of `log.getDepth()`\n *\n * callStack indexes\n *\n * 0 - pseudo-call stand-in for `ctx` in initializer (`callStack: [{}]`)\n * 1 - first callOp inside of `ctx`\n */\n const logDepth = log.getDepth();\n const callStackDepth = this.callStack.length;\n\n if (logDepth < callStackDepth) {\n // Pop off the last call and get the execution results\n const call = this.callStack.pop();\n\n const ret = log.stack.peek(0);\n\n if (!ret.equals(0)) {\n if (call.type === 'create' || call.type === 'create2') {\n call.createdContractAddressHash = toHex(toAddress(ret.toString(16)));\n call.createdContractCode = toHex(db.getCode(toAddress(ret.toString(16))));\n } else {\n call.output = toHex(log.memory.slice(call.outputOffset, call.outputOffset + call.outputLength));\n }\n } else if (call.error === undefined) {\n call.error = 'internal failure';\n }\n\n delete call.outputOffset;\n delete call.outputLength;\n\n this.pushChildCall(call);\n }\n else {\n this.pushGasToTopCall(log);\n }\n },\n\n createOp(log) {\n const inputOffset = log.stack.peek(1).valueOf();\n const inputLength = log.stack.peek(2).valueOf();\n const inputEnd = inputOffset + inputLength;\n const stackValue = log.stack.peek(0);\n\n const call = {\n type: 'create',\n from: toHex(log.contract.getAddress()),\n init: toHex(log.memory.slice(inputOffset, inputEnd)),\n valueBigInt: bigInt(stackValue.toString(10))\n };\n this.callStack.push(call);\n },\n\n create2Op(log) {\n const inputOffset = log.stack.peek(1).valueOf();\n const inputLength = log.stack.peek(2).valueOf();\n const inputEnd = inputOffset + inputLength;\n const stackValue = log.stack.peek(0);\n\n const call = {\n type: 'create2',\n from: toHex(log.contract.getAddress()),\n init: toHex(log.memory.slice(inputOffset, inputEnd)),\n valueBigInt: bigInt(stackValue.toString(10))\n };\n this.callStack.push(call);\n },\n\n selfDestructOp(log, db) {\n const contractAddress = log.contract.getAddress();\n\n this.pushChildCall({\n type: 'selfdestruct',\n from: toHex(contractAddress),\n to: toHex(toAddress(log.stack.peek(0).toString(16))),\n gasBigInt: log.getGas(),\n gasUsedBigInt: log.getCost(),\n valueBigInt: db.getBalance(contractAddress)\n });\n },\n\n callOp(log, op) {\n const to = toAddress(log.stack.peek(1).toString(16));\n\n // Skip any pre-compile invocations, those are just fancy opcodes\n if (!isPrecompiled(to)) {\n this.callCustomOp(log, op, to);\n }\n },\n\n callCustomOp(log, op, to) {\n const stackOffset = (op === 'DELEGATECALL' || op === 'STATICCALL' ? 0 : 1);\n\n const inputOffset = log.stack.peek(2 + stackOffset).valueOf();\n const inputLength = log.stack.peek(3 + stackOffset).valueOf();\n const inputEnd = inputOffset + inputLength;\n\n const call = {\n type: 'call',\n callType: op.toLowerCase(),\n from: toHex(log.contract.getAddress()),\n to: toHex(to),\n input: toHex(log.memory.slice(inputOffset, inputEnd)),\n outputOffset: log.stack.peek(4 + stackOffset).valueOf(),\n outputLength: log.stack.peek(5 + stackOffset).valueOf()\n };\n\n switch (op) {\n case 'CALL':\n case 'CALLCODE':\n call.valueBigInt = bigInt(log.stack.peek(2));\n break;\n case 'DELEGATECALL':\n // value inherited from scope during call sequencing\n break;\n case 'STATICCALL':\n // by definition static calls transfer no value\n call.valueBigInt = bigInt.zero;\n break;\n default:\n throw \"Unknown custom call op \" + op;\n }\n\n this.callStack.push(call);\n },\n\n revertOp() {\n this.topCall().error = 'execution reverted';\n },\n\n // result is invoked when all the opcodes have been iterated over and returns\n // the final result of the tracing.\n result(ctx, db) {\n const result = this.ctxToResult(ctx, db);\n const filtered = this.filterNotUndefined(result);\n const callSequence = this.sequence(filtered, [], filtered.valueBigInt, []).callSequence;\n return this.encodeCallSequence(callSequence);\n },\n\n ctxToResult(ctx, db) {\n var result;\n\n switch (ctx.type) {\n case 'CALL':\n result = this.ctxToCall(ctx);\n break;\n case 'CREATE':\n result = this.ctxToCreate(ctx, db);\n break;\n case 'CREATE2':\n result = this.ctxToCreate2(ctx, db);\n break;\n }\n\n return result;\n },\n\n ctxToCall(ctx) {\n const result = {\n type: 'call',\n callType: 'call',\n from: toHex(ctx.from),\n to: toHex(ctx.to),\n valueBigInt: bigInt(ctx.value.toString(10)),\n gasBigInt: bigInt(ctx.gas),\n gasUsedBigInt: bigInt(ctx.gasUsed),\n input: toHex(ctx.input)\n };\n\n this.putBottomChildCalls(result);\n this.putErrorOrOutput(result, ctx);\n\n return result;\n },\n\n putErrorOrOutput(result, ctx) {\n const error = this.error(ctx);\n\n if (error !== undefined) {\n result.error = error;\n } else {\n result.output = toHex(ctx.output);\n }\n },\n\n ctxToCreate(ctx, db) {\n const result = {\n type: 'create',\n from: toHex(ctx.from),\n init: toHex(ctx.input),\n valueBigInt: bigInt(ctx.value.toString(10)),\n gasBigInt: bigInt(ctx.gas),\n gasUsedBigInt: bigInt(ctx.gasUsed)\n };\n\n this.putBottomChildCalls(result);\n this.putErrorOrCreatedContract(result, ctx, db);\n\n return result;\n },\n\n ctxToCreate2(ctx, db) {\n const result = {\n type: 'create2',\n from: toHex(ctx.from),\n init: toHex(ctx.input),\n valueBigInt: bigInt(ctx.value.toString(10)),\n gasBigInt: bigInt(ctx.gas),\n gasUsedBigInt: bigInt(ctx.gasUsed)\n };\n\n this.putBottomChildCalls(result);\n this.putErrorOrCreatedContract(result, ctx, db);\n\n return result;\n },\n\n putBottomChildCalls(result) {\n const bottomCall = this.bottomCall();\n const bottomChildCalls = bottomCall.calls;\n\n if (bottomChildCalls !== undefined) {\n result.calls = bottomChildCalls;\n }\n },\n\n putErrorOrCreatedContract(result, ctx, db) {\n const error = this.error(ctx);\n\n if (error !== undefined) {\n result.error = error\n } else {\n result.createdContractAddressHash = toHex(ctx.to);\n result.createdContractCode = toHex(db.getCode(ctx.to));\n }\n },\n\n error(ctx) {\n var error;\n\n const bottomCall = this.bottomCall();\n const bottomCallError = bottomCall.error;\n\n if (bottomCallError !== undefined) {\n error = bottomCallError;\n } else {\n const ctxError = ctx.error;\n\n if (ctxError !== undefined) {\n error = ctxError;\n }\n }\n\n return error;\n },\n\n filterNotUndefined(call) {\n for (var key in call) {\n if (call[key] === undefined) {\n delete call[key];\n }\n }\n\n if (call.calls !== undefined) {\n for (var i = 0; i < call.calls.length; i++) {\n call.calls[i] = this.filterNotUndefined(call.calls[i]);\n }\n }\n\n return call;\n },\n\n // sequence converts the finalized calls from a call tree to a call sequence\n sequence(call, callSequence, availableValueBigInt, traceAddress) {\n const subcalls = call.calls;\n delete call.calls;\n\n call.traceAddress = traceAddress;\n\n if (call.type === 'call' && call.callType === 'delegatecall') {\n call.valueBigInt = availableValueBigInt;\n }\n\n var newCallSequence = callSequence.concat([call]);\n\n if (subcalls !== undefined) {\n for (var i = 0; i < subcalls.length; i++) {\n const nestedSequenced = this.sequence(\n subcalls[i],\n newCallSequence,\n call.valueBigInt,\n traceAddress.concat([i])\n );\n newCallSequence = nestedSequenced.callSequence;\n }\n }\n\n return {\n callSequence: newCallSequence\n };\n },\n\n encodeCallSequence(calls) {\n for (var i = 0; i < calls.length; i++) {\n this.encodeCall(calls[i]);\n }\n\n return calls;\n },\n\n encodeCall(call) {\n this.putValue(call);\n this.putGas(call);\n this.putGasUsed(call);\n\n return call;\n },\n\n putValue(call) {\n const valueBigInt = call.valueBigInt;\n delete call.valueBigInt;\n\n call.value = '0x' + valueBigInt.toString(16);\n },\n\n putGas(call) {\n const gasBigInt = call.gasBigInt;\n delete call.gasBigInt;\n\n if (gasBigInt === undefined) {\n gasBigInt = bigInt.zero;\n }\n\n call.gas = '0x' + gasBigInt.toString(16);\n },\n\n putGasUsed(call) {\n const gasUsedBigInt = call.gasUsedBigInt;\n delete call.gasUsedBigInt;\n\n if (gasUsedBigInt === undefined) {\n gasUsedBigInt = bigInt.zero;\n }\n\n call.gasUsed = '0x' + gasUsedBigInt.toString(16);\n }\n}\n" | ||
} |
Oops, something went wrong.