@cashscript/utils 0.9.3 → 0.10.0-next.1
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/artifact.d.ts +19 -0
- package/dist/bitauth-script.d.ts +6 -0
- package/dist/bitauth-script.js +31 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/script.d.ts +3 -0
- package/dist/script.js +24 -1
- package/dist/source-map.d.ts +13 -0
- package/dist/source-map.js +95 -0
- package/package.json +3 -3
package/dist/artifact.d.ts
CHANGED
|
@@ -9,12 +9,31 @@ export interface AbiFunction {
|
|
|
9
9
|
covenant?: boolean;
|
|
10
10
|
inputs: AbiInput[];
|
|
11
11
|
}
|
|
12
|
+
export interface LogEntry {
|
|
13
|
+
ip: number;
|
|
14
|
+
line: number;
|
|
15
|
+
data: Array<{
|
|
16
|
+
stackIndex: number;
|
|
17
|
+
type: string;
|
|
18
|
+
} | string>;
|
|
19
|
+
}
|
|
20
|
+
export interface RequireMessage {
|
|
21
|
+
ip: number;
|
|
22
|
+
line: number;
|
|
23
|
+
message: string;
|
|
24
|
+
}
|
|
12
25
|
export interface Artifact {
|
|
13
26
|
contractName: string;
|
|
14
27
|
constructorInputs: AbiInput[];
|
|
15
28
|
abi: AbiFunction[];
|
|
16
29
|
bytecode: string;
|
|
17
30
|
source: string;
|
|
31
|
+
debug?: {
|
|
32
|
+
bytecode: string;
|
|
33
|
+
sourceMap: string;
|
|
34
|
+
logs: LogEntry[];
|
|
35
|
+
requireMessages: RequireMessage[];
|
|
36
|
+
};
|
|
18
37
|
compiler: {
|
|
19
38
|
name: string;
|
|
20
39
|
version: string;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Script } from './script.js';
|
|
2
|
+
export declare type LineToOpcodesMap = Record<string, Script>;
|
|
3
|
+
export declare type LineToAsmMap = Record<string, string>;
|
|
4
|
+
export declare function buildLineToOpcodesMap(bytecode: Script, souceMap: string): LineToOpcodesMap;
|
|
5
|
+
export declare function buildLineToAsmMap(bytecode: Script, souceMap: string): LineToAsmMap;
|
|
6
|
+
export declare function formatBitAuthScript(bytecode: Script, souceMap: string, sourceCode: string): string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { scriptToBitAuthAsm } from './script.js';
|
|
2
|
+
import { sourceMapToLocationData } from './source-map.js';
|
|
3
|
+
export function buildLineToOpcodesMap(bytecode, souceMap) {
|
|
4
|
+
const locationData = sourceMapToLocationData(souceMap);
|
|
5
|
+
return locationData.reduce((lineToOpcodeMap, [location, positionHint], index) => {
|
|
6
|
+
const opcode = bytecode[index];
|
|
7
|
+
const line = positionHint ? location?.end.line : location?.start.line;
|
|
8
|
+
return {
|
|
9
|
+
...lineToOpcodeMap,
|
|
10
|
+
[line]: [...(lineToOpcodeMap[line] || []), opcode],
|
|
11
|
+
};
|
|
12
|
+
}, {});
|
|
13
|
+
}
|
|
14
|
+
export function buildLineToAsmMap(bytecode, souceMap) {
|
|
15
|
+
const lineToOpcodesMap = buildLineToOpcodesMap(bytecode, souceMap);
|
|
16
|
+
return Object.fromEntries(Object.entries(lineToOpcodesMap).map(([lineNumber, opcodeList]) => [lineNumber, scriptToBitAuthAsm(opcodeList)]));
|
|
17
|
+
}
|
|
18
|
+
export function formatBitAuthScript(bytecode, souceMap, sourceCode) {
|
|
19
|
+
const lineToAsmMap = buildLineToAsmMap(bytecode, souceMap);
|
|
20
|
+
const sourceCodeLines = sourceCode.split('\n');
|
|
21
|
+
const sourceCodeLineLengths = sourceCodeLines.map((line) => line.length);
|
|
22
|
+
const bytecodeLineLengths = Object.values(lineToAsmMap).map((line) => line.length);
|
|
23
|
+
const maxSourceCodeLength = Math.max(...sourceCodeLineLengths);
|
|
24
|
+
const maxBytecodeLength = Math.max(...bytecodeLineLengths);
|
|
25
|
+
const annotatedAsmLines = sourceCodeLines.map((line, index) => {
|
|
26
|
+
const lineAsm = lineToAsmMap[index + 1];
|
|
27
|
+
return `${(lineAsm || '').padEnd(maxBytecodeLength)} /* ${line.padEnd(maxSourceCodeLength)} */`;
|
|
28
|
+
});
|
|
29
|
+
return annotatedAsmLines.join('\n');
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=bitauth-script.js.map
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/script.d.ts
CHANGED
|
@@ -3,11 +3,14 @@ export declare type Op = number;
|
|
|
3
3
|
export declare type OpOrData = Op | Uint8Array;
|
|
4
4
|
export declare type Script = OpOrData[];
|
|
5
5
|
export declare function scriptToAsm(script: Script): string;
|
|
6
|
+
export declare function scriptToBitAuthAsm(script: Script): string;
|
|
6
7
|
export declare function asmToScript(asm: string): Script;
|
|
8
|
+
export declare function bitAuthAsmToScript(asm: string): Script;
|
|
7
9
|
export declare function scriptToBytecode(script: Script): Uint8Array;
|
|
8
10
|
export declare function bytecodeToScript(bytecode: Uint8Array): Script;
|
|
9
11
|
export declare function asmToBytecode(asm: string): Uint8Array;
|
|
10
12
|
export declare function bytecodeToAsm(bytecode: Uint8Array): string;
|
|
13
|
+
export declare function bytecodeToBitAuthAsm(bytecode: Uint8Array): string;
|
|
11
14
|
export declare function countOpcodes(script: Script): number;
|
|
12
15
|
export declare function calculateBytesize(script: Script): number;
|
|
13
16
|
export declare function encodeNullDataScript(chunks: OpOrData[]): Uint8Array;
|
package/dist/script.js
CHANGED
|
@@ -5,9 +5,18 @@ export const Op = OpcodesBCH;
|
|
|
5
5
|
export function scriptToAsm(script) {
|
|
6
6
|
return bytecodeToAsm(scriptToBytecode(script));
|
|
7
7
|
}
|
|
8
|
+
// TODO: See note at bytecodeToAsm
|
|
9
|
+
export function scriptToBitAuthAsm(script) {
|
|
10
|
+
return bytecodeToBitAuthAsm(scriptToBytecode(script));
|
|
11
|
+
}
|
|
12
|
+
// TODO: Figure out why OP_0 is decoded to an empty Uint8Array (while e.g. OP_3 is just OP_3)
|
|
8
13
|
export function asmToScript(asm) {
|
|
9
14
|
return bytecodeToScript(asmToBytecode(asm));
|
|
10
15
|
}
|
|
16
|
+
// asmToBytecode also works for BitAuth ASM
|
|
17
|
+
export function bitAuthAsmToScript(asm) {
|
|
18
|
+
return asmToScript(asm);
|
|
19
|
+
}
|
|
11
20
|
export function scriptToBytecode(script) {
|
|
12
21
|
// Convert the script elements to AuthenticationInstructions
|
|
13
22
|
const instructions = script.map((opOrData) => {
|
|
@@ -34,7 +43,8 @@ export function asmToBytecode(asm) {
|
|
|
34
43
|
if (token.startsWith('OP_')) {
|
|
35
44
|
return { opcode: Op[token] };
|
|
36
45
|
}
|
|
37
|
-
|
|
46
|
+
const data = token.replace(/<|>/g, '').replace(/^0x/, '');
|
|
47
|
+
return decodeAuthenticationInstructions(encodeDataPush(hexToBin(data)))[0];
|
|
38
48
|
});
|
|
39
49
|
// Convert the AuthenticationInstructions to bytecode
|
|
40
50
|
return encodeAuthenticationInstructions(instructions);
|
|
@@ -50,6 +60,19 @@ export function bytecodeToAsm(bytecode) {
|
|
|
50
60
|
asm = asm.replace(/\s+/g, ' ').trim();
|
|
51
61
|
return asm;
|
|
52
62
|
}
|
|
63
|
+
// TODO: If we decide to change the ASM / artifact format, we can remove this function and merge it
|
|
64
|
+
// with bytecodeToAsm
|
|
65
|
+
export function bytecodeToBitAuthAsm(bytecode) {
|
|
66
|
+
// Convert the bytecode to libauth's ASM format
|
|
67
|
+
let asm = disassembleBytecodeBCH(bytecode);
|
|
68
|
+
// COnvert libauth's ASM format to BITBOX's
|
|
69
|
+
asm = asm.replace(/OP_PUSHBYTES_[^\s]+/g, '');
|
|
70
|
+
asm = asm.replace(/OP_PUSHDATA[^\s]+ [^\s]+/g, '');
|
|
71
|
+
asm = asm.replace(/(^|\s)(0x\w*)/g, ' \<$2\>');
|
|
72
|
+
// Remove any duplicate whitespace
|
|
73
|
+
asm = asm.replace(/\s+/g, ' ').trim();
|
|
74
|
+
return asm;
|
|
75
|
+
}
|
|
53
76
|
export function countOpcodes(script) {
|
|
54
77
|
return script
|
|
55
78
|
.filter((opOrData) => typeof opOrData === 'number')
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface LocationI {
|
|
2
|
+
start: {
|
|
3
|
+
line: number;
|
|
4
|
+
column: number;
|
|
5
|
+
};
|
|
6
|
+
end: {
|
|
7
|
+
line: number;
|
|
8
|
+
column: number;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export declare type LocationData = Array<[location: LocationI, positionHint?: number]>;
|
|
12
|
+
export declare function generateSourceMap(locationData: LocationData): string;
|
|
13
|
+
export declare const sourceMapToLocationData: (sourceMap: string) => LocationData;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The source mappings for the bytecode use the following notation (similar to Solidity):
|
|
3
|
+
*
|
|
4
|
+
* `sl:sc:el:ec:h`
|
|
5
|
+
*
|
|
6
|
+
* Where `sl` is the start line, `sc` - start column, `el` - end line, and `ec` - end column
|
|
7
|
+
* of the range in the source file.
|
|
8
|
+
* `h` is the position hint denoting where to place the instruction - at the beginning of the parent AST block
|
|
9
|
+
* or at the end.
|
|
10
|
+
* `h` is used with AST blocks, instructions of which surround children blocks, like function declarations and
|
|
11
|
+
* if-then-else blocks.
|
|
12
|
+
*
|
|
13
|
+
* Mappings are a list of `sl:sc:el:ec:h` separated by `;`. Each of these elements corresponds to an instruction.
|
|
14
|
+
*
|
|
15
|
+
* In order to compress these source mappings, the following rules are used:
|
|
16
|
+
*
|
|
17
|
+
* If a field is empty, the value of the preceding element is used.
|
|
18
|
+
* If a `:` is missing, all the following fields are considered empty.
|
|
19
|
+
* This means the following source mappings represent the same information:
|
|
20
|
+
*
|
|
21
|
+
* 14:20:14:29;14:20:14:29;14:12:14:30;14:34:14:43;14:34:14:43
|
|
22
|
+
*
|
|
23
|
+
* 14:20:14:29;;:12::30;:34::43;
|
|
24
|
+
*
|
|
25
|
+
*/
|
|
26
|
+
export function generateSourceMap(locationData) {
|
|
27
|
+
let prevStartLine = 0;
|
|
28
|
+
let prevStartColumn = 0;
|
|
29
|
+
let prevEndLine = 0;
|
|
30
|
+
let prevEndColumn = 0;
|
|
31
|
+
let prevHint = 0;
|
|
32
|
+
return locationData.map((row) => {
|
|
33
|
+
const prevStartLineString = prevStartLine === row[0].start.line ? '' : String(row[0].start.line);
|
|
34
|
+
prevStartLine = row[0].start.line;
|
|
35
|
+
const prevStartColumnString = prevStartColumn === row[0].start.column ? '' : String(row[0].start.column);
|
|
36
|
+
prevStartColumn = row[0].start.column;
|
|
37
|
+
const prevEndLineString = prevEndLine === row[0].end.line ? '' : String(row[0].end.line);
|
|
38
|
+
prevEndLine = row[0].end.line;
|
|
39
|
+
const prevEndColumnString = prevEndColumn === row[0].end.column ? '' : String(row[0].end.column);
|
|
40
|
+
prevEndColumn = row[0].end.column;
|
|
41
|
+
const hint = row[1] ?? 0;
|
|
42
|
+
const prevHintString = prevHint === hint ? '' : String(hint);
|
|
43
|
+
prevHint = hint;
|
|
44
|
+
let result = '';
|
|
45
|
+
if (prevStartLineString) {
|
|
46
|
+
result += prevStartLineString;
|
|
47
|
+
}
|
|
48
|
+
result += ':';
|
|
49
|
+
if (prevStartColumnString) {
|
|
50
|
+
result += prevStartColumnString;
|
|
51
|
+
}
|
|
52
|
+
result += ':';
|
|
53
|
+
if (prevEndLineString) {
|
|
54
|
+
result += prevEndLineString;
|
|
55
|
+
}
|
|
56
|
+
result += ':';
|
|
57
|
+
if (prevEndColumnString) {
|
|
58
|
+
result += prevEndColumnString;
|
|
59
|
+
}
|
|
60
|
+
result += ':';
|
|
61
|
+
if (prevHintString) {
|
|
62
|
+
result += prevHintString;
|
|
63
|
+
}
|
|
64
|
+
result = result.replace(/:*$/, '');
|
|
65
|
+
return result;
|
|
66
|
+
}).join(';');
|
|
67
|
+
}
|
|
68
|
+
export const sourceMapToLocationData = (sourceMap) => {
|
|
69
|
+
let prevStartLine = 0;
|
|
70
|
+
let prevStartColumn = 0;
|
|
71
|
+
let prevEndLine = 0;
|
|
72
|
+
let prevEndColumn = 0;
|
|
73
|
+
let prevHint;
|
|
74
|
+
return sourceMap.split(';').map((entry) => {
|
|
75
|
+
const val = entry.split(':');
|
|
76
|
+
const startLine = val[0] ? Number(val[0]) : prevStartLine;
|
|
77
|
+
prevStartLine = startLine;
|
|
78
|
+
const startColumn = val[1] ? Number(val[1]) : prevStartColumn;
|
|
79
|
+
prevStartColumn = startColumn;
|
|
80
|
+
const endLine = val[2] ? Number(val[2]) : prevEndLine;
|
|
81
|
+
prevEndLine = endLine;
|
|
82
|
+
const endColumn = val[3] ? Number(val[3]) : prevEndColumn;
|
|
83
|
+
prevEndColumn = endColumn;
|
|
84
|
+
const hint = val[4] ? Number(val[4]) : prevHint;
|
|
85
|
+
prevHint = hint;
|
|
86
|
+
return [
|
|
87
|
+
{
|
|
88
|
+
start: { line: startLine, column: startColumn },
|
|
89
|
+
end: { line: endLine, column: endColumn },
|
|
90
|
+
},
|
|
91
|
+
...(hint ? [hint] : []),
|
|
92
|
+
];
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
//# sourceMappingURL=source-map.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cashscript/utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0-next.1",
|
|
4
4
|
"description": "CashScript utilities and types",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"bitcoin cash",
|
|
@@ -45,9 +45,9 @@
|
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@jest/globals": "^29.4.1",
|
|
48
|
-
"eslint": "^
|
|
48
|
+
"eslint": "^8.54.0",
|
|
49
49
|
"jest": "^29.4.1",
|
|
50
50
|
"typescript": "^4.1.5"
|
|
51
51
|
},
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "1370c6b1b1489b96964b6df4a860709cd73a06cc"
|
|
53
53
|
}
|