@acala-network/chopsticks 0.9.13 → 0.10.0-2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/cli.js +4 -4
- package/dist/cjs/plugins/index.js +2 -1
- package/dist/cjs/plugins/trace-transaction/README.md +12 -0
- package/dist/cjs/plugins/trace-transaction/__tests__/__snapshots__/trace-call.test.ts.snap +365 -0
- package/dist/cjs/plugins/trace-transaction/index.d.ts +2 -0
- package/dist/cjs/plugins/trace-transaction/index.js +66 -0
- package/dist/cjs/plugins/trace-transaction/table.d.ts +148 -0
- package/dist/cjs/plugins/trace-transaction/table.js +168 -0
- package/dist/cjs/plugins/trace-transaction/types.d.ts +30 -0
- package/dist/cjs/plugins/trace-transaction/types.js +64 -0
- package/dist/cjs/plugins/trace-transaction/utils.d.ts +40 -0
- package/dist/cjs/plugins/trace-transaction/utils.js +207 -0
- package/dist/cjs/utils/tunnel.js +2 -1
- package/dist/esm/cli.js +5 -5
- package/dist/esm/plugins/index.js +2 -1
- package/dist/esm/plugins/trace-transaction/README.md +12 -0
- package/dist/esm/plugins/trace-transaction/__tests__/__snapshots__/trace-call.test.ts.snap +365 -0
- package/dist/esm/plugins/trace-transaction/index.d.ts +2 -0
- package/dist/esm/plugins/trace-transaction/index.js +56 -0
- package/dist/esm/plugins/trace-transaction/table.d.ts +148 -0
- package/dist/esm/plugins/trace-transaction/table.js +150 -0
- package/dist/esm/plugins/trace-transaction/types.d.ts +30 -0
- package/dist/esm/plugins/trace-transaction/types.js +54 -0
- package/dist/esm/plugins/trace-transaction/utils.d.ts +40 -0
- package/dist/esm/plugins/trace-transaction/utils.js +199 -0
- package/dist/esm/utils/tunnel.js +2 -1
- package/package.json +3 -3
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: all[name]
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
TABLE: function() {
|
|
13
|
+
return TABLE;
|
|
14
|
+
},
|
|
15
|
+
opName: function() {
|
|
16
|
+
return opName;
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
const TABLE = {
|
|
20
|
+
'0x00': 'STOP',
|
|
21
|
+
'0x01': 'ADD',
|
|
22
|
+
'0x02': 'MUL',
|
|
23
|
+
'0x03': 'SUB',
|
|
24
|
+
'0x04': 'DIV',
|
|
25
|
+
'0x05': 'SDIV',
|
|
26
|
+
'0x06': 'MOD',
|
|
27
|
+
'0x07': 'SMOD',
|
|
28
|
+
'0x08': 'ADDMOD',
|
|
29
|
+
'0x09': 'MULMOD',
|
|
30
|
+
'0x0a': 'EXP',
|
|
31
|
+
'0x0b': 'SIGNEXTEND',
|
|
32
|
+
'0x10': 'LT',
|
|
33
|
+
'0x11': 'GT',
|
|
34
|
+
'0x12': 'SLT',
|
|
35
|
+
'0x13': 'SGT',
|
|
36
|
+
'0x14': 'EQ',
|
|
37
|
+
'0x15': 'ISZERO',
|
|
38
|
+
'0x16': 'AND',
|
|
39
|
+
'0x17': 'OR',
|
|
40
|
+
'0x18': 'XOR',
|
|
41
|
+
'0x19': 'NOT',
|
|
42
|
+
'0x1a': 'BYTE',
|
|
43
|
+
'0x35': 'CALLDATALOAD',
|
|
44
|
+
'0x36': 'CALLDATASIZE',
|
|
45
|
+
'0x37': 'CALLDATACOPY',
|
|
46
|
+
'0x38': 'CODESIZE',
|
|
47
|
+
'0x39': 'CODECOPY',
|
|
48
|
+
'0x1b': 'SHL',
|
|
49
|
+
'0x1c': 'SHR',
|
|
50
|
+
'0x1d': 'SAR',
|
|
51
|
+
'0x50': 'POP',
|
|
52
|
+
'0x51': 'MLOAD',
|
|
53
|
+
'0x52': 'MSTORE',
|
|
54
|
+
'0x53': 'MSTORE8',
|
|
55
|
+
'0x56': 'JUMP',
|
|
56
|
+
'0x57': 'JUMPI',
|
|
57
|
+
'0x58': 'PC',
|
|
58
|
+
'0x59': 'MSIZE',
|
|
59
|
+
'0x5b': 'JUMPDEST',
|
|
60
|
+
'0x5f': 'PUSH0',
|
|
61
|
+
'0x60': 'PUSH1',
|
|
62
|
+
'0x61': 'PUSH2',
|
|
63
|
+
'0x62': 'PUSH3',
|
|
64
|
+
'0x63': 'PUSH4',
|
|
65
|
+
'0x64': 'PUSH5',
|
|
66
|
+
'0x65': 'PUSH6',
|
|
67
|
+
'0x66': 'PUSH7',
|
|
68
|
+
'0x67': 'PUSH8',
|
|
69
|
+
'0x68': 'PUSH9',
|
|
70
|
+
'0x69': 'PUSH10',
|
|
71
|
+
'0x6a': 'PUSH11',
|
|
72
|
+
'0x6b': 'PUSH12',
|
|
73
|
+
'0x6c': 'PUSH13',
|
|
74
|
+
'0x6d': 'PUSH14',
|
|
75
|
+
'0x6e': 'PUSH15',
|
|
76
|
+
'0x6f': 'PUSH16',
|
|
77
|
+
'0x70': 'PUSH17',
|
|
78
|
+
'0x71': 'PUSH18',
|
|
79
|
+
'0x72': 'PUSH19',
|
|
80
|
+
'0x73': 'PUSH20',
|
|
81
|
+
'0x74': 'PUSH21',
|
|
82
|
+
'0x75': 'PUSH22',
|
|
83
|
+
'0x76': 'PUSH23',
|
|
84
|
+
'0x77': 'PUSH24',
|
|
85
|
+
'0x78': 'PUSH25',
|
|
86
|
+
'0x79': 'PUSH26',
|
|
87
|
+
'0x7a': 'PUSH27',
|
|
88
|
+
'0x7b': 'PUSH28',
|
|
89
|
+
'0x7c': 'PUSH29',
|
|
90
|
+
'0x7d': 'PUSH30',
|
|
91
|
+
'0x7e': 'PUSH31',
|
|
92
|
+
'0x7f': 'PUSH32',
|
|
93
|
+
'0x80': 'DUP1',
|
|
94
|
+
'0x81': 'DUP2',
|
|
95
|
+
'0x82': 'DUP3',
|
|
96
|
+
'0x83': 'DUP4',
|
|
97
|
+
'0x84': 'DUP5',
|
|
98
|
+
'0x85': 'DUP6',
|
|
99
|
+
'0x86': 'DUP7',
|
|
100
|
+
'0x87': 'DUP8',
|
|
101
|
+
'0x88': 'DUP9',
|
|
102
|
+
'0x89': 'DUP10',
|
|
103
|
+
'0x8a': 'DUP11',
|
|
104
|
+
'0x8b': 'DUP12',
|
|
105
|
+
'0x8c': 'DUP13',
|
|
106
|
+
'0x8d': 'DUP14',
|
|
107
|
+
'0x8e': 'DUP15',
|
|
108
|
+
'0x8f': 'DUP16',
|
|
109
|
+
'0x90': 'SWAP1',
|
|
110
|
+
'0x91': 'SWAP2',
|
|
111
|
+
'0x92': 'SWAP3',
|
|
112
|
+
'0x93': 'SWAP4',
|
|
113
|
+
'0x94': 'SWAP5',
|
|
114
|
+
'0x95': 'SWAP6',
|
|
115
|
+
'0x96': 'SWAP7',
|
|
116
|
+
'0x97': 'SWAP8',
|
|
117
|
+
'0x98': 'SWAP9',
|
|
118
|
+
'0x99': 'SWAP10',
|
|
119
|
+
'0x9a': 'SWAP11',
|
|
120
|
+
'0x9b': 'SWAP12',
|
|
121
|
+
'0x9c': 'SWAP13',
|
|
122
|
+
'0x9d': 'SWAP14',
|
|
123
|
+
'0x9e': 'SWAP15',
|
|
124
|
+
'0x9f': 'SWAP16',
|
|
125
|
+
'0xf3': 'RETURN',
|
|
126
|
+
'0xfd': 'REVERT',
|
|
127
|
+
'0xfe': 'INVALID',
|
|
128
|
+
'0xef': 'EOFMAGIC',
|
|
129
|
+
'0x20': 'SHA3',
|
|
130
|
+
'0x30': 'ADDRESS',
|
|
131
|
+
'0x31': 'BALANCE',
|
|
132
|
+
'0x47': 'SELFBALANCE',
|
|
133
|
+
'0x48': 'BASEFEE',
|
|
134
|
+
'0x32': 'ORIGIN',
|
|
135
|
+
'0x33': 'CALLER',
|
|
136
|
+
'0x34': 'CALLVALUE',
|
|
137
|
+
'0x3a': 'GASPRICE',
|
|
138
|
+
'0x3b': 'EXTCODESIZE',
|
|
139
|
+
'0x3c': 'EXTCODECOPY',
|
|
140
|
+
'0x3f': 'EXTCODEHASH',
|
|
141
|
+
'0x3d': 'RETURNDATASIZE',
|
|
142
|
+
'0x3e': 'RETURNDATACOPY',
|
|
143
|
+
'0x40': 'BLOCKHASH',
|
|
144
|
+
'0x41': 'COINBASE',
|
|
145
|
+
'0x42': 'TIMESTAMP',
|
|
146
|
+
'0x43': 'NUMBER',
|
|
147
|
+
'0x44': 'DIFFICULTY',
|
|
148
|
+
'0x45': 'GASLIMIT',
|
|
149
|
+
'0x54': 'SLOAD',
|
|
150
|
+
'0x55': 'SSTORE',
|
|
151
|
+
'0x5a': 'GAS',
|
|
152
|
+
'0xa0': 'LOG0',
|
|
153
|
+
'0xa1': 'LOG1',
|
|
154
|
+
'0xa2': 'LOG2',
|
|
155
|
+
'0xa3': 'LOG3',
|
|
156
|
+
'0xa4': 'LOG4',
|
|
157
|
+
'0xf0': 'CREATE',
|
|
158
|
+
'0xf5': 'CREATE2',
|
|
159
|
+
'0xf1': 'CALL',
|
|
160
|
+
'0xf2': 'CALLCODE',
|
|
161
|
+
'0xf4': 'DELEGATECALL',
|
|
162
|
+
'0xfa': 'STATICCALL',
|
|
163
|
+
'0xff': 'SUICIDE',
|
|
164
|
+
'0x46': 'CHAINID'
|
|
165
|
+
};
|
|
166
|
+
const opName = (op)=>{
|
|
167
|
+
return TABLE[`0x${op.toString(16).padStart(2, '0')}`] || `0x${op.toString(16).padStart(2, '0')}`;
|
|
168
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { HexString } from '@polkadot/util/types';
|
|
2
|
+
import { Registry } from '@polkadot/types/types';
|
|
3
|
+
export type CallTrace = {
|
|
4
|
+
type: 'CALL' | 'CALLCODE' | 'STATICCALL' | 'DELEGATECALL' | 'CREATE' | 'SUICIDE';
|
|
5
|
+
from: HexString;
|
|
6
|
+
to: HexString;
|
|
7
|
+
input: HexString;
|
|
8
|
+
value: HexString;
|
|
9
|
+
gas: number;
|
|
10
|
+
gasUsed: number;
|
|
11
|
+
output: HexString | null;
|
|
12
|
+
error: string | null;
|
|
13
|
+
revertReason: string | null;
|
|
14
|
+
depth: number;
|
|
15
|
+
calls: CallTrace[];
|
|
16
|
+
};
|
|
17
|
+
export type Step = {
|
|
18
|
+
op: number;
|
|
19
|
+
pc: number;
|
|
20
|
+
depth: number;
|
|
21
|
+
gas: number;
|
|
22
|
+
stack: HexString[];
|
|
23
|
+
memory: string[] | null;
|
|
24
|
+
};
|
|
25
|
+
export type TraceOutcome = {
|
|
26
|
+
steps: Step[];
|
|
27
|
+
} | {
|
|
28
|
+
calls: CallTrace[];
|
|
29
|
+
};
|
|
30
|
+
export declare const registerTypes: (registry: Registry) => void;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "registerTypes", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return registerTypes;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const registerTypes = (registry)=>{
|
|
12
|
+
registry.register({
|
|
13
|
+
Step: {
|
|
14
|
+
op: 'u8',
|
|
15
|
+
pc: 'Compact<u32>',
|
|
16
|
+
depth: 'Compact<u32>',
|
|
17
|
+
gas: 'Compact<u64>',
|
|
18
|
+
stack: 'Vec<Bytes>',
|
|
19
|
+
memory: 'Option<Vec<Bytes>>'
|
|
20
|
+
},
|
|
21
|
+
CallType: {
|
|
22
|
+
_enum: {
|
|
23
|
+
CALL: null,
|
|
24
|
+
CALLCODE: null,
|
|
25
|
+
STATICCALL: null,
|
|
26
|
+
DELEGATECALL: null,
|
|
27
|
+
CREATE: null,
|
|
28
|
+
SUICIDE: null
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
CallTrace: {
|
|
32
|
+
type: 'CallType',
|
|
33
|
+
from: 'H160',
|
|
34
|
+
to: 'H160',
|
|
35
|
+
input: 'Bytes',
|
|
36
|
+
value: 'U256',
|
|
37
|
+
gas: 'Compact<u64>',
|
|
38
|
+
gasUsed: 'Compact<u64>',
|
|
39
|
+
output: 'Option<Bytes>',
|
|
40
|
+
error: 'Option<String>',
|
|
41
|
+
revertReason: 'Option<String>',
|
|
42
|
+
depth: 'Compact<u32>',
|
|
43
|
+
calls: 'Vec<CallTrace>'
|
|
44
|
+
},
|
|
45
|
+
TraceOutcome: {
|
|
46
|
+
_enum: {
|
|
47
|
+
Calls: 'Vec<CallTrace>',
|
|
48
|
+
Steps: 'Vec<Step>'
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
OpcodeConfig: {
|
|
52
|
+
page: 'u32',
|
|
53
|
+
pageSize: 'u32',
|
|
54
|
+
disableStack: 'bool',
|
|
55
|
+
enableMemory: 'bool'
|
|
56
|
+
},
|
|
57
|
+
TracerConfig: {
|
|
58
|
+
_enum: {
|
|
59
|
+
CallTracer: null,
|
|
60
|
+
OpcodeTracer: 'OpcodeConfig'
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
import { Block, Blockchain, RuntimeVersion } from '@acala-network/chopsticks-core';
|
|
3
|
+
import { HexString } from '@polkadot/util/types';
|
|
4
|
+
import { Step } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Fetches the runtime with tracing feature from Github releases.
|
|
7
|
+
* @param runtimeVersion - The version of the runtime.
|
|
8
|
+
* @returns A Promise that resolves to the fetched runtime as a Buffer.
|
|
9
|
+
*/
|
|
10
|
+
export declare const fetchRuntime: (runtimeVersion: RuntimeVersion) => Promise<Buffer | undefined>;
|
|
11
|
+
export declare const fetchEVMTransaction: (runtimeVersion: RuntimeVersion, txHash: string) => Promise<any>;
|
|
12
|
+
/**
|
|
13
|
+
* Traces the execution of a transaction in the VM.
|
|
14
|
+
* @param block - The block to trace the extrinsic in.
|
|
15
|
+
* @param extrinsic - The extrinsic to trace.
|
|
16
|
+
* @returns An array of VM steps.
|
|
17
|
+
* @throws Error if the trace outcome is invalid.
|
|
18
|
+
*/
|
|
19
|
+
export declare const traceVM: (block: Block, extrinsic: HexString, pageSize?: number, disableStack?: boolean, enableMemory?: boolean) => Promise<Step[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Traces the calls made by an extrinsic in a block.
|
|
22
|
+
* @param block - The block to trace the extrinsic in.
|
|
23
|
+
* @param extrinsic - The extrinsic to trace.
|
|
24
|
+
* @returns An array of calls made by the extrinsic.
|
|
25
|
+
* @throws Error if the trace outcome is invalid.
|
|
26
|
+
*/
|
|
27
|
+
export declare const traceCalls: (block: Block, extrinsic: HexString) => Promise<import("./types.js").CallTrace[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Prepares a block for tracing a transaction.
|
|
30
|
+
* @param chain The blockchain instance.
|
|
31
|
+
* @param blockHashNumber The block hash or block number.
|
|
32
|
+
* @param txHash The transaction hash.
|
|
33
|
+
* @param wasmPath The path to the runtime wasm file.
|
|
34
|
+
* @returns An object containing the tracing block and the transaction extrinsic.
|
|
35
|
+
* @throws Error if the block or parent block is not found, or if the runtime wasm with tracing feature cannot be found.
|
|
36
|
+
*/
|
|
37
|
+
export declare const prepareBlock: (chain: Blockchain, blockHashNumber: HexString | number, txHash: string, wasmPath?: string) => Promise<{
|
|
38
|
+
tracingBlock: Block;
|
|
39
|
+
extrinsic: `0x${string}`;
|
|
40
|
+
}>;
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
function _export(target, all) {
|
|
6
|
+
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: all[name]
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
_export(exports, {
|
|
12
|
+
fetchEVMTransaction: function() {
|
|
13
|
+
return fetchEVMTransaction;
|
|
14
|
+
},
|
|
15
|
+
fetchRuntime: function() {
|
|
16
|
+
return fetchRuntime;
|
|
17
|
+
},
|
|
18
|
+
prepareBlock: function() {
|
|
19
|
+
return prepareBlock;
|
|
20
|
+
},
|
|
21
|
+
traceCalls: function() {
|
|
22
|
+
return traceCalls;
|
|
23
|
+
},
|
|
24
|
+
traceVM: function() {
|
|
25
|
+
return traceVM;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const _chopstickscore = require("@acala-network/chopsticks-core");
|
|
29
|
+
const _utilcrypto = require("@polkadot/util-crypto");
|
|
30
|
+
const _lodash = /*#__PURE__*/ _interop_require_default(require("lodash"));
|
|
31
|
+
const _types = require("./types.js");
|
|
32
|
+
const _table = require("./table.js");
|
|
33
|
+
const _override = require("../../utils/override.js");
|
|
34
|
+
function _interop_require_default(obj) {
|
|
35
|
+
return obj && obj.__esModule ? obj : {
|
|
36
|
+
default: obj
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const fetchRuntime = async (runtimeVersion)=>{
|
|
40
|
+
const GIHTUB_RELEASES_API = 'https://api.github.com/repos/AcalaNetwork/Acala/releases';
|
|
41
|
+
const assetName = `${runtimeVersion.specName}_runtime_tracing_${runtimeVersion.specVersion}.compact.compressed.wasm`;
|
|
42
|
+
_chopstickscore.pinoLogger.info({
|
|
43
|
+
assetName
|
|
44
|
+
}, 'Search for runtime with tracing feature from Github releases ...');
|
|
45
|
+
const releases = await fetch(GIHTUB_RELEASES_API).then((res)=>res.json());
|
|
46
|
+
for (const release of releases){
|
|
47
|
+
if (release.assets) {
|
|
48
|
+
for (const asset of release.assets){
|
|
49
|
+
if (asset.name === assetName) {
|
|
50
|
+
_chopstickscore.pinoLogger.info({
|
|
51
|
+
url: asset.browser_download_url
|
|
52
|
+
}, 'Downloading ...');
|
|
53
|
+
const runtime = await fetch(asset.browser_download_url).then((x)=>x.arrayBuffer());
|
|
54
|
+
return Buffer.from(runtime);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const fetchEVMTransaction = async (runtimeVersion, txHash)=>{
|
|
61
|
+
const ACALA_ETH_RPC = 'https://eth-rpc-acala.aca-api.network';
|
|
62
|
+
const KARURA_ETH_RPC = 'https://eth-rpc-karura.aca-api.network';
|
|
63
|
+
let ethRpc;
|
|
64
|
+
if (runtimeVersion.specName.includes('acala')) {
|
|
65
|
+
ethRpc = ACALA_ETH_RPC;
|
|
66
|
+
} else if (runtimeVersion.specName.includes('karura')) {
|
|
67
|
+
ethRpc = KARURA_ETH_RPC;
|
|
68
|
+
} else {
|
|
69
|
+
throw new Error(`Unsupported chain. Only Acala and Karura are supported`);
|
|
70
|
+
}
|
|
71
|
+
_chopstickscore.pinoLogger.info(`Fetching EVM transaction ...`);
|
|
72
|
+
const response = await fetch(ethRpc, {
|
|
73
|
+
headers: [
|
|
74
|
+
[
|
|
75
|
+
'Content-Type',
|
|
76
|
+
'application/json'
|
|
77
|
+
],
|
|
78
|
+
[
|
|
79
|
+
'Accept',
|
|
80
|
+
'application/json'
|
|
81
|
+
]
|
|
82
|
+
],
|
|
83
|
+
method: 'POST',
|
|
84
|
+
body: JSON.stringify({
|
|
85
|
+
jsonrpc: '2.0',
|
|
86
|
+
id: 1,
|
|
87
|
+
method: 'eth_getTransactionByHash',
|
|
88
|
+
params: [
|
|
89
|
+
txHash
|
|
90
|
+
]
|
|
91
|
+
})
|
|
92
|
+
});
|
|
93
|
+
const data = await response.json();
|
|
94
|
+
if (data.error) {
|
|
95
|
+
throw new Error(data.error.message);
|
|
96
|
+
}
|
|
97
|
+
return data.result;
|
|
98
|
+
};
|
|
99
|
+
const traceVM = async (block, extrinsic, pageSize = 50_000, disableStack = false, enableMemory = true)=>{
|
|
100
|
+
const meta = await block.meta;
|
|
101
|
+
(0, _types.registerTypes)(meta.registry);
|
|
102
|
+
let page = 0;
|
|
103
|
+
let traceNextPage = true;
|
|
104
|
+
let steps = [];
|
|
105
|
+
while(traceNextPage){
|
|
106
|
+
_chopstickscore.pinoLogger.info(`VM trace page ${page} ...`);
|
|
107
|
+
const tracerConfig = meta.registry.createType('TracerConfig', {
|
|
108
|
+
OpcodeTracer: {
|
|
109
|
+
page,
|
|
110
|
+
pageSize,
|
|
111
|
+
disableStack,
|
|
112
|
+
enableMemory
|
|
113
|
+
}
|
|
114
|
+
}).toHex();
|
|
115
|
+
const taskResponse = await block.call('EVMTraceApi_trace_extrinsic', [
|
|
116
|
+
extrinsic,
|
|
117
|
+
tracerConfig
|
|
118
|
+
]);
|
|
119
|
+
const outcome = meta.registry.createType('Result<TraceOutcome, TransactionValidityError>', taskResponse.result).asOk.toJSON();
|
|
120
|
+
if (!('steps' in outcome)) {
|
|
121
|
+
throw new Error('Invalid trace outcome');
|
|
122
|
+
}
|
|
123
|
+
steps = steps.concat(outcome.steps.map((step)=>({
|
|
124
|
+
...step,
|
|
125
|
+
op: (0, _table.opName)(step.op),
|
|
126
|
+
// transform memory to 64 bytes chunks
|
|
127
|
+
memory: step.memory ? step.memory.map((chunk, idx)=>{
|
|
128
|
+
// remove 0x prefix
|
|
129
|
+
const slice = chunk.slice(2);
|
|
130
|
+
// make sure each chunk is 64 bytes
|
|
131
|
+
if (slice.length < 64 && idx + 1 < step.memory.length) {
|
|
132
|
+
return slice.padStart(64, '0');
|
|
133
|
+
}
|
|
134
|
+
return slice;
|
|
135
|
+
}) : null
|
|
136
|
+
})));
|
|
137
|
+
page += 1;
|
|
138
|
+
traceNextPage = outcome.steps.length == pageSize;
|
|
139
|
+
}
|
|
140
|
+
return steps;
|
|
141
|
+
};
|
|
142
|
+
const traceCalls = async (block, extrinsic)=>{
|
|
143
|
+
const meta = await block.meta;
|
|
144
|
+
(0, _types.registerTypes)(meta.registry);
|
|
145
|
+
const tracerConfig = meta.registry.createType('TracerConfig', {
|
|
146
|
+
CallTracer: null
|
|
147
|
+
}).toHex();
|
|
148
|
+
const taskResponse = await block.call('EVMTraceApi_trace_extrinsic', [
|
|
149
|
+
extrinsic,
|
|
150
|
+
tracerConfig
|
|
151
|
+
]);
|
|
152
|
+
const outcome = meta.registry.createType('Result<TraceOutcome, TransactionValidityError>', taskResponse.result).asOk.toJSON();
|
|
153
|
+
if (!('calls' in outcome)) {
|
|
154
|
+
throw new Error('Invalid trace outcome');
|
|
155
|
+
}
|
|
156
|
+
return outcome.calls;
|
|
157
|
+
};
|
|
158
|
+
const prepareBlock = async (chain, blockHashNumber, txHash, wasmPath)=>{
|
|
159
|
+
let wasm = wasmPath;
|
|
160
|
+
const block = typeof blockHashNumber == 'number' ? await chain.getBlockAt(blockHashNumber) : await chain.getBlock(blockHashNumber);
|
|
161
|
+
if (!block) {
|
|
162
|
+
throw new Error(`Block not found ${blockHashNumber}`);
|
|
163
|
+
}
|
|
164
|
+
const header = await block.header;
|
|
165
|
+
const parent = await chain.getBlock(header.parentHash.toHex());
|
|
166
|
+
if (!parent) {
|
|
167
|
+
throw new Error(`Block not found ${blockHashNumber}`);
|
|
168
|
+
}
|
|
169
|
+
await chain.setHead(parent);
|
|
170
|
+
// override wasm with tracing feature
|
|
171
|
+
if (typeof wasm === 'string') {
|
|
172
|
+
await (0, _override.overrideWasm)(chain, wasm);
|
|
173
|
+
} else {
|
|
174
|
+
// Fetch runtime wasm with tracing feature from Github releases
|
|
175
|
+
if (!wasm) {
|
|
176
|
+
wasm = await fetchRuntime(await chain.head.runtimeVersion);
|
|
177
|
+
if (!wasm) {
|
|
178
|
+
throw new Error('Could not find runtime with tracing feature from Github releasesw. Make sure to manually override runtime wasm built with `tracing` feature enabled.');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
chain.head.setWasm(`0x${wasm.toString('hex')}`);
|
|
182
|
+
}
|
|
183
|
+
const runtimeVersion = await chain.head.runtimeVersion;
|
|
184
|
+
_chopstickscore.pinoLogger.info(`${_lodash.default.capitalize(runtimeVersion.specName)} specVersion: ${runtimeVersion.specVersion}`);
|
|
185
|
+
const extrinsics = await block.extrinsics;
|
|
186
|
+
const txIndex = extrinsics.findIndex((tx)=>(0, _utilcrypto.blake2AsHex)(tx) === txHash);
|
|
187
|
+
const tracingBlock = new _chopstickscore.Block(chain, block.number, block.hash, parent, {
|
|
188
|
+
header,
|
|
189
|
+
extrinsics: [],
|
|
190
|
+
storage: parent.storage
|
|
191
|
+
});
|
|
192
|
+
_chopstickscore.pinoLogger.info(`Preparing block ${chain.head.number + 1} ...`);
|
|
193
|
+
const { storageDiff } = await tracingBlock.call('Core_initialize_block', [
|
|
194
|
+
header.toHex()
|
|
195
|
+
]);
|
|
196
|
+
tracingBlock.pushStorageLayer().setAll(storageDiff);
|
|
197
|
+
for (const extrinsic of extrinsics.slice(0, txIndex)){
|
|
198
|
+
const { storageDiff } = await tracingBlock.call('BlockBuilder_apply_extrinsic', [
|
|
199
|
+
extrinsic
|
|
200
|
+
]);
|
|
201
|
+
tracingBlock.pushStorageLayer().setAll(storageDiff);
|
|
202
|
+
}
|
|
203
|
+
return {
|
|
204
|
+
tracingBlock,
|
|
205
|
+
extrinsic: extrinsics[txIndex]
|
|
206
|
+
};
|
|
207
|
+
};
|
package/dist/cjs/utils/tunnel.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
5
|
const _globalagent = require("global-agent");
|
|
6
|
+
const _chopstickscore = require("@acala-network/chopsticks-core");
|
|
6
7
|
const _npmconf = /*#__PURE__*/ _interop_require_default(require("@pnpm/npm-conf"));
|
|
7
8
|
function _interop_require_default(obj) {
|
|
8
9
|
return obj && obj.__esModule ? obj : {
|
|
@@ -11,4 +12,4 @@ function _interop_require_default(obj) {
|
|
|
11
12
|
}
|
|
12
13
|
(0, _globalagent.bootstrap)();
|
|
13
14
|
const npmConfig = (0, _npmconf.default)().config;
|
|
14
|
-
global.GLOBAL_AGENT.HTTP_PROXY =
|
|
15
|
+
global.GLOBAL_AGENT.HTTP_PROXY = _chopstickscore.environment.HTTP_PROXY || _chopstickscore.environment.http_proxy || _chopstickscore.environment.HTTPS_PROXY || _chopstickscore.environment.https_proxy || npmConfig.get('proxy') || npmConfig.get('https-proxy') || global.GLOBAL_AGENT.HTTP_PROXY;
|
package/dist/esm/cli.js
CHANGED
|
@@ -2,7 +2,7 @@ import { config as dotenvConfig } from 'dotenv';
|
|
|
2
2
|
import { hideBin } from 'yargs/helpers';
|
|
3
3
|
import _ from 'lodash';
|
|
4
4
|
import yargs from 'yargs';
|
|
5
|
-
import { connectParachains, connectVertical } from '@acala-network/chopsticks-core';
|
|
5
|
+
import { connectParachains, connectVertical, environment } from '@acala-network/chopsticks-core';
|
|
6
6
|
import { configSchema, fetchConfig, getYargsOptions } from './schema/index.js';
|
|
7
7
|
import { pluginExtendCli } from './plugins/index.js';
|
|
8
8
|
import { setupWithServer } from './index.js';
|
|
@@ -11,8 +11,8 @@ const processArgv = async (argv)=>{
|
|
|
11
11
|
if (argv.config) {
|
|
12
12
|
Object.assign(argv, _.defaults(argv, await fetchConfig(argv.config)));
|
|
13
13
|
}
|
|
14
|
-
if (
|
|
15
|
-
argv.port = Number(
|
|
14
|
+
if (environment.PORT) {
|
|
15
|
+
argv.port = Number(environment.PORT);
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
18
|
const commands = yargs(hideBin(process.argv)).scriptName('chopsticks').middleware(processArgv, false).command('*', 'Dev mode, fork off a chain', (yargs)=>yargs.config('config', 'Path to config file with default options', ()=>({})).options(getYargsOptions(configSchema.shape)), async (argv)=>{
|
|
@@ -35,7 +35,7 @@ const commands = yargs(hideBin(process.argv)).scriptName('chopsticks').middlewar
|
|
|
35
35
|
parachains.push(chain);
|
|
36
36
|
}
|
|
37
37
|
if (parachains.length > 1) {
|
|
38
|
-
await connectParachains(parachains);
|
|
38
|
+
await connectParachains(parachains, environment.DISABLE_AUTO_HRMP);
|
|
39
39
|
}
|
|
40
40
|
if (argv.relaychain) {
|
|
41
41
|
const { chain: relaychain } = await setupWithServer(await fetchConfig(argv.relaychain));
|
|
@@ -44,7 +44,7 @@ const commands = yargs(hideBin(process.argv)).scriptName('chopsticks').middlewar
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
}).strict().help().alias('help', 'h').alias('version', 'v').alias('config', 'c').alias('endpoint', 'e').alias('port', 'p').alias('block', 'b').alias('import-storage', 's').alias('wasm-override', 'w').usage('Usage: $0 <command> [options]').example('$0', '-c acala');
|
|
47
|
-
if (!
|
|
47
|
+
if (!environment.DISABLE_PLUGINS) {
|
|
48
48
|
pluginExtendCli(commands.config('config', 'Path to config file with default options', ()=>({}))).then(()=>commands.parse());
|
|
49
49
|
} else {
|
|
50
50
|
commands.parse();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { environment } from '@acala-network/chopsticks-core';
|
|
1
2
|
import { lstatSync, readdirSync } from 'fs';
|
|
2
3
|
import _ from 'lodash';
|
|
3
4
|
import { defaultLogger } from '../logger.js';
|
|
@@ -10,7 +11,7 @@ const plugins = readdirSync(new URL('.', import.meta.url)).filter((file)=>lstatS
|
|
|
10
11
|
// find all rpc methods
|
|
11
12
|
export const rpcPluginMethods = plugins.filter((name)=>readdirSync(new URL(name, import.meta.url)).some((file)=>file.startsWith('rpc'))).map((name)=>`dev_${_.camelCase(name)}`);
|
|
12
13
|
export const loadRpcPlugin = async (method)=>{
|
|
13
|
-
if (
|
|
14
|
+
if (environment.DISABLE_PLUGINS) {
|
|
14
15
|
return undefined;
|
|
15
16
|
}
|
|
16
17
|
if (rpcPluginHandlers[method]) return rpcPluginHandlers[method];
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# EVM+ trace transaction plugin
|
|
2
|
+
|
|
3
|
+
This plugin allows you to trace the execution of a transaction on the Acala & Karura EVM+. The plugin will take a transaction hash as input and will look up the mainnet for transaction details and perform a trace call for the transaction. You can do either call trace or VM trace
|
|
4
|
+
|
|
5
|
+
Example usage:
|
|
6
|
+
To trace transaction calls, you can use the following command:
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npx @acala-network/chopsticks trace-transaction <tx-hash> --config acala --output trace.json
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
VM trace can be enabled by add `--vm` flag to the command
|