Skip to content

Commit 0379c30

Browse files
mainnet-patrkalis
authored andcommitted
Add extensive sourcemap tests, fix generate traversal issues
1 parent 9c6686d commit 0379c30

File tree

5 files changed

+107
-18
lines changed

5 files changed

+107
-18
lines changed

packages/cashc/src/generation/GenerateTargetTraversal.ts

+11-10
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal {
197197
// we add it back to the stack
198198
this.pushToStack('(value)');
199199
} else {
200-
this.emit(finalOp, location!);
200+
this.emit(finalOp, location!, 1);
201201

202202
// At this point there is no verification value left on the stack:
203203
// - scoped stack is cleared inside branch ended by OP_ENDIF
@@ -267,8 +267,9 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal {
267267
}
268268

269269
visitTimeOp(node: TimeOpNode): Node {
270+
// const countBefore = this.output.length;
270271
node.expression = this.visit(node.expression);
271-
this.emit(compileTimeOp(node.timeOp), node.location!);
272+
this.emit(compileTimeOp(node.timeOp), node.location!, 1);
272273

273274
// add debug require message
274275
if (node.message) {
@@ -286,7 +287,7 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal {
286287
visitRequire(node: RequireNode): Node {
287288
node.expression = this.visit(node.expression);
288289

289-
this.emit(Op.OP_VERIFY, node.location!);
290+
this.emit(Op.OP_VERIFY, node.location!, 1);
290291

291292
// add debug require message
292293
if (node.message) {
@@ -343,7 +344,7 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal {
343344
this.popFromStack();
344345
}
345346

346-
this.emit(compileCast(node.expression.type as PrimitiveType, node.type), node.location!);
347+
this.emit(compileCast(node.expression.type as PrimitiveType, node.type), node.location!, 1);
347348
this.popFromStack();
348349
this.pushToStack('(value)');
349350
return node;
@@ -356,7 +357,7 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal {
356357

357358
node.parameters = this.visitList(node.parameters);
358359

359-
this.emit(compileGlobalFunction(node.identifier.name as GlobalFunction), node.location!);
360+
this.emit(compileGlobalFunction(node.identifier.name as GlobalFunction), node.location!, 1);
360361
this.popFromStack(node.parameters.length);
361362
this.pushToStack('(value)');
362363

@@ -367,7 +368,7 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal {
367368
this.emit(encodeBool(false), node.location!);
368369
this.pushToStack('(value)');
369370
node.parameters = this.visitList(node.parameters);
370-
this.emit(Op.OP_CHECKMULTISIG, node.location!);
371+
this.emit(Op.OP_CHECKMULTISIG, node.location!, 1);
371372
const sigs = node.parameters[0] as ArrayNode;
372373
const pks = node.parameters[1] as ArrayNode;
373374
this.popFromStack(sigs.elements.length + pks.elements.length + 3);
@@ -459,10 +460,10 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal {
459460
node.tuple = this.visit(node.tuple);
460461

461462
if (node.index === 0) {
462-
this.emit(Op.OP_DROP, node.location!);
463+
this.emit(Op.OP_DROP, node.location!, 1);
463464
this.popFromStack();
464465
} else if (node.index === 1) {
465-
this.emit(Op.OP_NIP, node.location!);
466+
this.emit(Op.OP_NIP, node.location!, 1);
466467
this.nipFromStack();
467468
}
468469

@@ -473,7 +474,7 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal {
473474
node.left = this.visit(node.left);
474475
node.right = this.visit(node.right);
475476
const isNumeric = resultingType(node.left.type, node.right.type) === PrimitiveType.INT;
476-
this.emit(compileBinaryOp(node.operator, isNumeric), node.location!);
477+
this.emit(compileBinaryOp(node.operator, isNumeric), node.location!, 1);
477478
this.popFromStack(2);
478479
this.pushToStack('(value)');
479480
if (node.operator === BinaryOperator.SPLIT) this.pushToStack('(value)');
@@ -496,7 +497,7 @@ export default class GenerateTargetTraversalWithLocation extends AstTraversal {
496497

497498
visitArray(node: ArrayNode): Node {
498499
node.elements = this.visitList(node.elements);
499-
this.emit(encodeInt(BigInt(node.elements.length)), node.location!);
500+
this.emit(encodeInt(BigInt(node.elements.length)), node.location!, 1);
500501
this.pushToStack('(value)');
501502
return node;
502503
}
+56-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import fs from 'fs';
22
import { URL } from 'url';
3-
import { parseCode } from '../../src/compiler.js';
3+
import { compileString, parseCode } from '../../src/compiler.js';
4+
import { buildOpCodeMap, bytecodeToAsm, bytecodeToScript, formatLibauthScript } from '@cashscript/utils';
5+
import { hexToBin } from '@bitauth/libauth';
46

57
describe('Location', () => {
6-
it('should retrieve correct text from location', () => {
8+
it.skip('should retrieve correct text from location', () => {
79
const code = fs.readFileSync(new URL('../valid-contract-files/simple_functions.cash', import.meta.url), { encoding: 'utf-8' });
810
const ast = parseCode(code);
911

@@ -12,4 +14,56 @@ describe('Location', () => {
1214
expect(f.location).toBeDefined();
1315
expect((f.location!).text(code)).toEqual('function hello(sig s, pubkey pk) {\n require(checkSig(s, pk));\n }');
1416
});
17+
18+
const wrap = (code: string): string => {
19+
return `
20+
contract test() {
21+
function test() {
22+
require(${code});
23+
}
24+
}`
25+
};
26+
27+
describe('should produce same bytecode', () => {
28+
const blocks = [
29+
'1 < 1', '1 <= 1', '1 == 1', '1 != 1', '1 > 1', '1 >= 1',
30+
'(1 - 1) == 1', '(1 + 1) == 1', '(1 * 1) == 1', '(1 / 1) == 1',
31+
'(true && true) == true', '(true || true) == true',
32+
'(0x01 & 0x01) == 0x01', '(0x01 | 0x01) == 0x01', '(0x01 ^ 0x01) == 0x01',
33+
'"1" + "1" == "1"', '"1" + "1" != "1"', '"11".split(1)[0] == "1"', '"11".split(1)[1] == "1"',
34+
'"1".reverse() == "1"', '"1".length == 1', '0x01.length == 1', '-333 == 1',
35+
'tx.inputs[0].tokenAmount == 1',
36+
'this.activeInputIndex == 1', 'tx.version == 1',
37+
'abs(-1) == 1', 'within(1,1,1) == true', 'bytes(sha256(1)) == bytes(0x01)',
38+
'checkSig(sig(0x00), pubkey(0x00))', 'checkMultiSig([sig(0x00), sig(0x00)], [pubkey(0x00), pubkey(0x00)])',
39+
'checkDataSig(datasig(0x00), 0x00, pubkey(0x00))',
40+
'tx.time >= 1', 'tx.age >= 1',
41+
'bytes(1) == 0x01', 'int(0x01) == 1',
42+
];
43+
44+
blocks.forEach(block => {
45+
it(`should test ${block}`, () => {
46+
{
47+
const source = wrap(block);
48+
const artifact = compileString(source);
49+
const expected = bytecodeToAsm(hexToBin(artifact.debug!.bytecode));
50+
const opCodeMap = buildOpCodeMap(bytecodeToScript(hexToBin(artifact.debug!.bytecode)), artifact.debug!.sourceMap);
51+
52+
const received = Object.values(opCodeMap).join(' ').replaceAll('<0x', '').replaceAll('>', '').replace(/\s+/g, ' ');
53+
expect(received).toBe(expected);
54+
}
55+
56+
{
57+
// break statements into separate lines, test `hint`
58+
const source = wrap(block.replaceAll(' ', '\n').replaceAll(')', '\n)')).replaceAll('(\n)', '()').replace(/\((?!\))/g, '(\n');
59+
const artifact = compileString(source);
60+
const expected = bytecodeToAsm(hexToBin(artifact.debug!.bytecode));
61+
const opCodeMap = buildOpCodeMap(bytecodeToScript(hexToBin(artifact.debug!.bytecode)), artifact.debug!.sourceMap);
62+
63+
const received = Object.values(opCodeMap).join(' ').replaceAll('<0x', '').replaceAll('>', '').replace(/\s+/g, ' ');
64+
expect(received).toBe(expected);
65+
}
66+
});
67+
});
68+
});
1569
});

packages/cashscript/test/LibauthTemplate.test.ts

+22
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,28 @@ import './JestExtensions.js';
77
import { randomUtxo } from '../src/utils.js';
88

99
describe('Libauth template generation tests', () => {
10+
it('should log console statements', async () => {
11+
const code = `
12+
contract Example(string cash) {
13+
function test(string script) {
14+
require(
15+
cash +
16+
script ==
17+
"cashscript"
18+
);
19+
}
20+
}
21+
`;
22+
const artifact = compileString(code);
23+
24+
const provider = new MockNetworkProvider();
25+
const contract = new Contract(artifact, ["cash"], { provider });
26+
provider.addUtxo(contract.address, randomUtxo());
27+
28+
const transaction = contract.functions.test("script").to(contract.address, 10000n);
29+
await transaction.debug()
30+
});
31+
1032
it('should log console statements', async () => {
1133
const code = `
1234
pragma cashscript ^0.9.0;

packages/utils/src/libauth.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { binToHex } from '@bitauth/libauth';
22
import { Script, Op } from './script.js';
33
import { sourceMapToLocationData } from './sourceMap.js';
44

5-
export function formatLibauthScript(bytecode: Script, souceMap: string, sourceCode: string): string {
5+
function buildLineMap(bytecode: Script, souceMap: string): { [line: string]: Script; } {
66
const lineMap: { [line: string]: Script } = {} as any;
77

88
const locationData = sourceMapToLocationData(souceMap);
@@ -15,9 +15,9 @@ export function formatLibauthScript(bytecode: Script, souceMap: string, sourceCo
1515
// eslint-disable-next-line
1616
positionHint
1717
? location?.end.line
18-
: (op === Op.OP_ENDIF || op === Op.OP_ELSE || op === Op.OP_NIP || op === Op.OP_CHECKLOCKTIMEVERIFY
19-
? location?.end.line
20-
: location?.start.line),
18+
// : (op === Op.OP_ENDIF || op === Op.OP_ELSE || op === Op.OP_NIP || op === Op.OP_CHECKLOCKTIMEVERIFY
19+
// ? location?.end.line
20+
: location?.start.line,
2121
];
2222
});
2323

@@ -28,6 +28,12 @@ export function formatLibauthScript(bytecode: Script, souceMap: string, sourceCo
2828
lineMap[line as string].push(op as any);
2929
});
3030

31+
return lineMap;
32+
}
33+
34+
export function buildOpCodeMap(bytecode: Script, souceMap: string) {
35+
const lineMap = buildLineMap(bytecode, souceMap);
36+
3137
const opCodeMap: { [key: string]: string } = {};
3238
for (const [key, value] of Object.entries(lineMap)) {
3339
opCodeMap[key] = value.map((asmElement) => {
@@ -41,6 +47,12 @@ export function formatLibauthScript(bytecode: Script, souceMap: string, sourceCo
4147
}).join(' ');
4248
}
4349

50+
return opCodeMap;
51+
}
52+
53+
export function formatLibauthScript(bytecode: Script, souceMap: string, sourceCode: string): string {
54+
const opCodeMap = buildOpCodeMap(bytecode, souceMap);
55+
4456
const split = sourceCode.split('\n');
4557

4658
const maxCodeLength = Math.max(...split.map((val) => val.length));

tsconfig.build.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"compilerOptions": {
3-
"target": "es2020",
3+
"target": "es2021",
44
"module": "esnext",
55
"declaration": true,
6-
"lib": ["es2020", "dom"],
6+
"lib": ["es2021", "dom"],
77
"types": ["node", "jest"],
88
"sourceMap": true,
99
"strict": true,

0 commit comments

Comments
 (0)