recon-generate 0.0.17 → 0.0.19
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/cfg/builder.d.ts +89 -0
- package/dist/cfg/builder.js +1295 -0
- package/dist/cfg/edge-mapper.d.ts +103 -0
- package/dist/cfg/edge-mapper.js +297 -0
- package/dist/cfg/emitter.d.ts +11 -0
- package/dist/cfg/emitter.js +492 -0
- package/dist/cfg/index.d.ts +35 -0
- package/dist/cfg/index.js +77 -0
- package/dist/cfg/sourcemap.d.ts +169 -0
- package/dist/cfg/sourcemap.js +605 -0
- package/dist/cfg/types.d.ts +270 -0
- package/dist/cfg/types.js +41 -0
- package/dist/cfg/unified-coverage.d.ts +87 -0
- package/dist/cfg/unified-coverage.js +248 -0
- package/dist/generator.js +1 -1
- package/dist/index.js +19 -0
- package/dist/info.js +0 -13
- package/dist/sourcemap.d.ts +14 -0
- package/dist/sourcemap.js +317 -0
- package/dist/types.d.ts +5 -0
- package/dist/wakeGenerator.js +2 -1
- package/package.json +2 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge Mapper - Maps bytecode PCs to CFG edge IDs
|
|
3
|
+
*
|
|
4
|
+
* Approach using AST-level information:
|
|
5
|
+
* 1. CFG edges have astId (branch condition's AST ID)
|
|
6
|
+
* 2. Source map maps PC → source location → AST ID
|
|
7
|
+
* 3. For each edge's astId, find the first PC that maps to it
|
|
8
|
+
* 4. That PC is the "branch point" for that edge
|
|
9
|
+
*
|
|
10
|
+
* At runtime:
|
|
11
|
+
* - Fuzzer hits PC X
|
|
12
|
+
* - Look up: is X a branch point for any edge?
|
|
13
|
+
* - If yes, mark that edge as covered based on which direction was taken
|
|
14
|
+
*/
|
|
15
|
+
/** Edge info parsed from CFG */
|
|
16
|
+
export interface CfgEdge {
|
|
17
|
+
id: string;
|
|
18
|
+
from: string;
|
|
19
|
+
to: string;
|
|
20
|
+
direction: 'T' | 'F' | 'J';
|
|
21
|
+
functionName: string;
|
|
22
|
+
astId?: number;
|
|
23
|
+
}
|
|
24
|
+
/** Branch point mapping for runtime coverage - enhanced with line numbers and body PCs */
|
|
25
|
+
export interface BranchPoint {
|
|
26
|
+
/** PC of the branch (JUMPI or first instruction of branch condition) */
|
|
27
|
+
pc: number;
|
|
28
|
+
/** AST ID of the condition */
|
|
29
|
+
astId: number;
|
|
30
|
+
/** Edge ID for true branch (condition != 0, fall through) */
|
|
31
|
+
trueEdge: string;
|
|
32
|
+
/** Edge ID for false branch (condition == 0, jump taken) */
|
|
33
|
+
falseEdge: string;
|
|
34
|
+
/** Function name */
|
|
35
|
+
function: string;
|
|
36
|
+
/** Block ID where branch occurs */
|
|
37
|
+
block: string;
|
|
38
|
+
/** Line number of the branch condition (1-based) */
|
|
39
|
+
conditionLine?: number;
|
|
40
|
+
/** Line number of the first statement in true branch (1-based) */
|
|
41
|
+
trueBodyLine?: number;
|
|
42
|
+
/** Line number of the first statement in false branch (1-based) */
|
|
43
|
+
falseBodyLine?: number;
|
|
44
|
+
/** First PC of the true branch body (for coverage tracking) */
|
|
45
|
+
trueBodyPc?: number;
|
|
46
|
+
/** First PC of the false branch body (for coverage tracking) */
|
|
47
|
+
falseBodyPc?: number;
|
|
48
|
+
/** Byte offset of condition in source */
|
|
49
|
+
offset?: number;
|
|
50
|
+
/** Length of condition in source */
|
|
51
|
+
length?: number;
|
|
52
|
+
}
|
|
53
|
+
/** Complete edge mapping for a contract */
|
|
54
|
+
export interface EdgeMapping {
|
|
55
|
+
contract: string;
|
|
56
|
+
totalEdges: number;
|
|
57
|
+
/** Branch points - PC → (trueEdge, falseEdge) */
|
|
58
|
+
branches: BranchPoint[];
|
|
59
|
+
/** Quick lookup: PC → branch info */
|
|
60
|
+
pcToBranch: Map<number, BranchPoint>;
|
|
61
|
+
/** Quick lookup: edge ID → PC */
|
|
62
|
+
edgeToPc: Map<string, number>;
|
|
63
|
+
}
|
|
64
|
+
export interface EdgeMappingManifest {
|
|
65
|
+
generated: string;
|
|
66
|
+
totalContracts: number;
|
|
67
|
+
totalEdges: number;
|
|
68
|
+
contracts: {
|
|
69
|
+
name: string;
|
|
70
|
+
edges: number;
|
|
71
|
+
branches: number;
|
|
72
|
+
}[];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Parse CFG s-expression to extract edges
|
|
76
|
+
*/
|
|
77
|
+
export declare function parseCfgEdges(cfgContent: string): CfgEdge[];
|
|
78
|
+
export declare class EdgeMapper {
|
|
79
|
+
private mappings;
|
|
80
|
+
/**
|
|
81
|
+
* Load CFG and source map branches, build edge mapping
|
|
82
|
+
*/
|
|
83
|
+
loadContract(cfgPath: string, branchesPath: string, contractName: string): Promise<EdgeMapping>;
|
|
84
|
+
/**
|
|
85
|
+
* Get mapping for a contract
|
|
86
|
+
*/
|
|
87
|
+
getMapping(contractName: string): EdgeMapping | undefined;
|
|
88
|
+
/**
|
|
89
|
+
* Look up edge from PC and branch direction
|
|
90
|
+
* @param taken - true if condition was non-zero (fall through), false if jump taken
|
|
91
|
+
*/
|
|
92
|
+
pcToEdge(contractName: string, pc: number, taken: boolean): string | undefined;
|
|
93
|
+
/**
|
|
94
|
+
* Look up PC from edge ID
|
|
95
|
+
*/
|
|
96
|
+
edgeToPc(contractName: string, edgeId: string): number | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* Export mappings to JSON for Rust fuzzer
|
|
99
|
+
* Enhanced with line number information for LLM integration
|
|
100
|
+
*/
|
|
101
|
+
exportForFuzzer(outputPath: string): void;
|
|
102
|
+
}
|
|
103
|
+
export declare function generateEdgeMappings(reconDir: string, contractNames: string[]): Promise<EdgeMappingManifest>;
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Edge Mapper - Maps bytecode PCs to CFG edge IDs
|
|
4
|
+
*
|
|
5
|
+
* Approach using AST-level information:
|
|
6
|
+
* 1. CFG edges have astId (branch condition's AST ID)
|
|
7
|
+
* 2. Source map maps PC → source location → AST ID
|
|
8
|
+
* 3. For each edge's astId, find the first PC that maps to it
|
|
9
|
+
* 4. That PC is the "branch point" for that edge
|
|
10
|
+
*
|
|
11
|
+
* At runtime:
|
|
12
|
+
* - Fuzzer hits PC X
|
|
13
|
+
* - Look up: is X a branch point for any edge?
|
|
14
|
+
* - If yes, mark that edge as covered based on which direction was taken
|
|
15
|
+
*/
|
|
16
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
+
}
|
|
22
|
+
Object.defineProperty(o, k2, desc);
|
|
23
|
+
}) : (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
o[k2] = m[k];
|
|
26
|
+
}));
|
|
27
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
28
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
29
|
+
}) : function(o, v) {
|
|
30
|
+
o["default"] = v;
|
|
31
|
+
});
|
|
32
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
33
|
+
var ownKeys = function(o) {
|
|
34
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
35
|
+
var ar = [];
|
|
36
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
37
|
+
return ar;
|
|
38
|
+
};
|
|
39
|
+
return ownKeys(o);
|
|
40
|
+
};
|
|
41
|
+
return function (mod) {
|
|
42
|
+
if (mod && mod.__esModule) return mod;
|
|
43
|
+
var result = {};
|
|
44
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
45
|
+
__setModuleDefault(result, mod);
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
})();
|
|
49
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
|
+
exports.EdgeMapper = void 0;
|
|
51
|
+
exports.parseCfgEdges = parseCfgEdges;
|
|
52
|
+
exports.generateEdgeMappings = generateEdgeMappings;
|
|
53
|
+
const fs = __importStar(require("fs"));
|
|
54
|
+
const path = __importStar(require("path"));
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// CFG Parser
|
|
57
|
+
// ============================================================================
|
|
58
|
+
/**
|
|
59
|
+
* Parse CFG s-expression to extract edges
|
|
60
|
+
*/
|
|
61
|
+
function parseCfgEdges(cfgContent) {
|
|
62
|
+
const edges = [];
|
|
63
|
+
const lines = cfgContent.split('\n');
|
|
64
|
+
for (const line of lines) {
|
|
65
|
+
// Parse edges: (edge "func_B0_T" B0 B1 condition :ast 123)
|
|
66
|
+
// Handle various formats including assert edges like "func_B0_assert0_T"
|
|
67
|
+
// Note: condition can contain nested parentheses, so we can't use [^)]
|
|
68
|
+
// Instead, match :ast at the end or allow optional :ast anywhere
|
|
69
|
+
let edgeMatch = /\(edge\s+"([^"]+)"\s+(\w+)\s+(\w+).*:ast\s+(\d+)/.exec(line);
|
|
70
|
+
if (!edgeMatch) {
|
|
71
|
+
// Try without :ast (edge might not have AST ID)
|
|
72
|
+
edgeMatch = /\(edge\s+"([^"]+)"\s+(\w+)\s+(\w+)/.exec(line);
|
|
73
|
+
}
|
|
74
|
+
if (edgeMatch) {
|
|
75
|
+
const [_, edgeId, fromBlock, toBlock, astIdStr] = edgeMatch;
|
|
76
|
+
// Parse edge ID to extract function, block, direction
|
|
77
|
+
// Patterns: "func_B0_T", "func_B0_F", "func_B0_J", "func_B0_assert0_T"
|
|
78
|
+
const parts = edgeId.match(/^(.+?)_(B\d+).*?_([TFJ])$/);
|
|
79
|
+
if (parts) {
|
|
80
|
+
const [__, funcName, blockId, dir] = parts;
|
|
81
|
+
edges.push({
|
|
82
|
+
id: edgeId,
|
|
83
|
+
from: fromBlock,
|
|
84
|
+
to: toBlock,
|
|
85
|
+
direction: dir,
|
|
86
|
+
functionName: funcName,
|
|
87
|
+
astId: astIdStr ? parseInt(astIdStr, 10) : undefined
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return edges;
|
|
93
|
+
}
|
|
94
|
+
// ============================================================================
|
|
95
|
+
// Edge Mapper Class
|
|
96
|
+
// ============================================================================
|
|
97
|
+
class EdgeMapper {
|
|
98
|
+
constructor() {
|
|
99
|
+
this.mappings = new Map();
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Load CFG and source map branches, build edge mapping
|
|
103
|
+
*/
|
|
104
|
+
async loadContract(cfgPath, branchesPath, contractName) {
|
|
105
|
+
var _a, _b, _c, _d, _e;
|
|
106
|
+
// Load and parse CFG
|
|
107
|
+
const cfgContent = fs.readFileSync(cfgPath, 'utf-8');
|
|
108
|
+
const edges = parseCfgEdges(cfgContent);
|
|
109
|
+
// Load branches manifest (from source map)
|
|
110
|
+
const branchManifest = JSON.parse(fs.readFileSync(branchesPath, 'utf-8'));
|
|
111
|
+
const contractBranches = (_b = (_a = branchManifest.contracts) === null || _a === void 0 ? void 0 : _a.find) === null || _b === void 0 ? void 0 : _b.call(_a, (c) => c.contract === contractName);
|
|
112
|
+
if (!contractBranches) {
|
|
113
|
+
throw new Error(`Contract ${contractName} not found in branches.json`);
|
|
114
|
+
}
|
|
115
|
+
// Build AST ID → first PC mapping from branches
|
|
116
|
+
// branches.json now contains: { pc, astId, allAstIds, offset, length } for each JUMPI
|
|
117
|
+
// allAstIds contains ALL AST nodes at this source location
|
|
118
|
+
const astIdToPc = new Map();
|
|
119
|
+
for (const branch of contractBranches.branches || []) {
|
|
120
|
+
// Include all AST IDs at this location
|
|
121
|
+
const allIds = branch.allAstIds || (branch.astId !== null ? [branch.astId] : []);
|
|
122
|
+
for (const astId of allIds) {
|
|
123
|
+
const existing = astIdToPc.get(astId);
|
|
124
|
+
// Keep the first (lowest) PC for each AST ID
|
|
125
|
+
if (existing === undefined || branch.pc < existing) {
|
|
126
|
+
astIdToPc.set(astId, branch.pc);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// Group CFG edges by astId to find T/F pairs
|
|
131
|
+
const edgesByAstId = new Map();
|
|
132
|
+
for (const edge of edges) {
|
|
133
|
+
if (edge.astId === undefined)
|
|
134
|
+
continue;
|
|
135
|
+
if (edge.direction === 'J')
|
|
136
|
+
continue; // Skip unconditional jumps
|
|
137
|
+
if (!edgesByAstId.has(edge.astId)) {
|
|
138
|
+
edgesByAstId.set(edge.astId, {});
|
|
139
|
+
}
|
|
140
|
+
const group = edgesByAstId.get(edge.astId);
|
|
141
|
+
if (edge.direction === 'T')
|
|
142
|
+
group.T = edge;
|
|
143
|
+
if (edge.direction === 'F')
|
|
144
|
+
group.F = edge;
|
|
145
|
+
}
|
|
146
|
+
// Build branch points by correlating AST IDs
|
|
147
|
+
const branches = [];
|
|
148
|
+
const pcToBranch = new Map();
|
|
149
|
+
const edgeToPc = new Map();
|
|
150
|
+
for (const [astId, group] of edgesByAstId) {
|
|
151
|
+
const pc = astIdToPc.get(astId);
|
|
152
|
+
if (pc === undefined)
|
|
153
|
+
continue;
|
|
154
|
+
if (!group.T && !group.F)
|
|
155
|
+
continue;
|
|
156
|
+
// Use the T edge's info for function/block (they should be the same)
|
|
157
|
+
const refEdge = group.T || group.F;
|
|
158
|
+
// Find the branch info from branches.json to get line number and body PCs
|
|
159
|
+
const branchInfo = (_c = contractBranches.branches) === null || _c === void 0 ? void 0 : _c.find((b) => b.pc === pc || (b.allAstIds && b.allAstIds.includes(astId)));
|
|
160
|
+
const branchPoint = {
|
|
161
|
+
pc,
|
|
162
|
+
astId,
|
|
163
|
+
trueEdge: ((_d = group.T) === null || _d === void 0 ? void 0 : _d.id) || `${refEdge.functionName}_${refEdge.from}_T`,
|
|
164
|
+
falseEdge: ((_e = group.F) === null || _e === void 0 ? void 0 : _e.id) || `${refEdge.functionName}_${refEdge.from}_F`,
|
|
165
|
+
function: refEdge.functionName,
|
|
166
|
+
block: refEdge.from,
|
|
167
|
+
// Include line/offset info if available
|
|
168
|
+
conditionLine: branchInfo === null || branchInfo === void 0 ? void 0 : branchInfo.conditionLine,
|
|
169
|
+
trueBodyLine: branchInfo === null || branchInfo === void 0 ? void 0 : branchInfo.trueBodyLine,
|
|
170
|
+
falseBodyLine: branchInfo === null || branchInfo === void 0 ? void 0 : branchInfo.falseBodyLine,
|
|
171
|
+
trueBodyPc: branchInfo === null || branchInfo === void 0 ? void 0 : branchInfo.trueBodyPc,
|
|
172
|
+
falseBodyPc: branchInfo === null || branchInfo === void 0 ? void 0 : branchInfo.falseBodyPc,
|
|
173
|
+
offset: branchInfo === null || branchInfo === void 0 ? void 0 : branchInfo.offset,
|
|
174
|
+
length: branchInfo === null || branchInfo === void 0 ? void 0 : branchInfo.length
|
|
175
|
+
};
|
|
176
|
+
branches.push(branchPoint);
|
|
177
|
+
pcToBranch.set(pc, branchPoint);
|
|
178
|
+
// Map both edges to this PC
|
|
179
|
+
if (group.T)
|
|
180
|
+
edgeToPc.set(group.T.id, pc);
|
|
181
|
+
if (group.F)
|
|
182
|
+
edgeToPc.set(group.F.id, pc);
|
|
183
|
+
}
|
|
184
|
+
const mapping = {
|
|
185
|
+
contract: contractName,
|
|
186
|
+
totalEdges: edges.length,
|
|
187
|
+
branches,
|
|
188
|
+
pcToBranch,
|
|
189
|
+
edgeToPc
|
|
190
|
+
};
|
|
191
|
+
this.mappings.set(contractName, mapping);
|
|
192
|
+
return mapping;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Get mapping for a contract
|
|
196
|
+
*/
|
|
197
|
+
getMapping(contractName) {
|
|
198
|
+
return this.mappings.get(contractName);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Look up edge from PC and branch direction
|
|
202
|
+
* @param taken - true if condition was non-zero (fall through), false if jump taken
|
|
203
|
+
*/
|
|
204
|
+
pcToEdge(contractName, pc, taken) {
|
|
205
|
+
const mapping = this.mappings.get(contractName);
|
|
206
|
+
if (!mapping)
|
|
207
|
+
return undefined;
|
|
208
|
+
const branch = mapping.pcToBranch.get(pc);
|
|
209
|
+
if (!branch)
|
|
210
|
+
return undefined;
|
|
211
|
+
return taken ? branch.trueEdge : branch.falseEdge;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Look up PC from edge ID
|
|
215
|
+
*/
|
|
216
|
+
edgeToPc(contractName, edgeId) {
|
|
217
|
+
const mapping = this.mappings.get(contractName);
|
|
218
|
+
return mapping === null || mapping === void 0 ? void 0 : mapping.edgeToPc.get(edgeId);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Export mappings to JSON for Rust fuzzer
|
|
222
|
+
* Enhanced with line number information for LLM integration
|
|
223
|
+
*/
|
|
224
|
+
exportForFuzzer(outputPath) {
|
|
225
|
+
const output = {
|
|
226
|
+
generated: new Date().toISOString(),
|
|
227
|
+
totalContracts: 0,
|
|
228
|
+
totalEdges: 0,
|
|
229
|
+
contracts: {}
|
|
230
|
+
};
|
|
231
|
+
for (const [name, mapping] of this.mappings) {
|
|
232
|
+
output.totalContracts++;
|
|
233
|
+
output.totalEdges += mapping.totalEdges;
|
|
234
|
+
output.contracts[name] = {
|
|
235
|
+
totalEdges: mapping.totalEdges,
|
|
236
|
+
branches: mapping.branches.map(b => ({
|
|
237
|
+
pc: b.pc,
|
|
238
|
+
astId: b.astId,
|
|
239
|
+
trueEdge: b.trueEdge,
|
|
240
|
+
falseEdge: b.falseEdge,
|
|
241
|
+
function: b.function,
|
|
242
|
+
block: b.block,
|
|
243
|
+
// Include line number info for LLM-friendly output
|
|
244
|
+
...(b.conditionLine !== undefined && { conditionLine: b.conditionLine }),
|
|
245
|
+
...(b.trueBodyLine !== undefined && { trueBodyLine: b.trueBodyLine }),
|
|
246
|
+
...(b.falseBodyLine !== undefined && { falseBodyLine: b.falseBodyLine }),
|
|
247
|
+
// Include body PCs for coverage tracking
|
|
248
|
+
...(b.trueBodyPc !== undefined && { trueBodyPc: b.trueBodyPc }),
|
|
249
|
+
...(b.falseBodyPc !== undefined && { falseBodyPc: b.falseBodyPc }),
|
|
250
|
+
...(b.offset !== undefined && { offset: b.offset }),
|
|
251
|
+
...(b.length !== undefined && { length: b.length })
|
|
252
|
+
}))
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
fs.writeFileSync(outputPath, JSON.stringify(output, null, 2));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
exports.EdgeMapper = EdgeMapper;
|
|
259
|
+
// ============================================================================
|
|
260
|
+
// CLI Integration
|
|
261
|
+
// ============================================================================
|
|
262
|
+
async function generateEdgeMappings(reconDir, contractNames) {
|
|
263
|
+
const mapper = new EdgeMapper();
|
|
264
|
+
const branchesPath = path.join(reconDir, 'branches.json');
|
|
265
|
+
if (!fs.existsSync(branchesPath)) {
|
|
266
|
+
throw new Error(`branches.json not found in ${reconDir}. Run 'recon-expander sourcemap' first.`);
|
|
267
|
+
}
|
|
268
|
+
const results = [];
|
|
269
|
+
for (const contractName of contractNames) {
|
|
270
|
+
const cfgPath = path.join(reconDir, `${contractName}.cfg.sexp`);
|
|
271
|
+
if (!fs.existsSync(cfgPath)) {
|
|
272
|
+
console.warn(`Warning: ${cfgPath} not found, skipping`);
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
try {
|
|
276
|
+
const mapping = await mapper.loadContract(cfgPath, branchesPath, contractName);
|
|
277
|
+
results.push({
|
|
278
|
+
name: contractName,
|
|
279
|
+
edges: mapping.totalEdges,
|
|
280
|
+
branches: mapping.branches.length
|
|
281
|
+
});
|
|
282
|
+
console.log(` ✅ ${contractName}: ${mapping.totalEdges} edges, ${mapping.branches.length} branch points mapped`);
|
|
283
|
+
}
|
|
284
|
+
catch (err) {
|
|
285
|
+
console.warn(`Warning: Failed to map ${contractName}: ${err}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
// Export combined mappings
|
|
289
|
+
const edgeMappingPath = path.join(reconDir, 'edge-mappings.json');
|
|
290
|
+
mapper.exportForFuzzer(edgeMappingPath);
|
|
291
|
+
return {
|
|
292
|
+
generated: new Date().toISOString(),
|
|
293
|
+
totalContracts: results.length,
|
|
294
|
+
totalEdges: results.reduce((sum, r) => sum + r.edges, 0),
|
|
295
|
+
contracts: results
|
|
296
|
+
};
|
|
297
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S-Expression Emitter - Converts CFG to s-expr format
|
|
3
|
+
*
|
|
4
|
+
* Output is designed for:
|
|
5
|
+
* - Easy parsing in Rust fuzzer
|
|
6
|
+
* - Direct translation to SMT-LIB2 for Bitwuzla
|
|
7
|
+
* - Human readability for debugging
|
|
8
|
+
*/
|
|
9
|
+
import { CompletePath, ContractModule, FunctionCFG } from './types';
|
|
10
|
+
export declare function emitContract(module: ContractModule): string;
|
|
11
|
+
export declare function emitPathToSMT(path: CompletePath, func: FunctionCFG): string;
|