Skip to content

Commit ce373ab

Browse files
authored
feat: vm improvements (#12)
1 parent 8fde848 commit ce373ab

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+4035
-109
lines changed

src/kth.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/kth.wasm

151 KB
Binary file not shown.

tests/Hash.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) 2016-2025 Knuth Project developers.
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
import { HashFunctions, bytesToHexStr } from '..';
6+
7+
describe('HashFunctions', () => {
8+
9+
it('Should compute sha256 hash', () => {
10+
const str = 'Hola';
11+
const bytes = new Uint8Array(str.split('').map(c => c.charCodeAt(0)));
12+
const hash = HashFunctions.sha256(bytes);
13+
expect(bytesToHexStr(hash)).toBe('e633f4fc79badea1dc5db970cf397c8248bac47cc3acf9915ba60b5d76b0e88f');
14+
});
15+
16+
it('Should compute sha256 hash reversed', () => {
17+
const str = 'Hola';
18+
const bytes = new Uint8Array(str.split('').map(c => c.charCodeAt(0)));
19+
const hash = HashFunctions.sha256Reversed(bytes);
20+
expect(bytesToHexStr(hash)).toBe('8fe8b0765d0ba65b91f9acc37cc4ba48827c39cf70b95ddca1deba79fcf433e6');
21+
});
22+
23+
24+
it('Should compute sha256 hash reversed string', () => {
25+
const str = 'Hola';
26+
const bytes = new Uint8Array(str.split('').map(c => c.charCodeAt(0)));
27+
const hashStr = HashFunctions.sha256ReversedStr(bytes);
28+
expect(hashStr).toBe('8fe8b0765d0ba65b91f9acc37cc4ba48827c39cf70b95ddca1deba79fcf433e6');
29+
});
30+
});

tests/HdPublic.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) 2016-2025 Knuth Project developers.
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
import { HdPublic, EcPublic } from '..';
6+
7+
const MAINNET_P2KH = 0x00;
8+
9+
describe('HdPublic', () => {
10+
11+
it('Should fail with empty string', () => {
12+
// m/44'/145'/0'
13+
const xpubStr = 'xpub6CAemD8S648fre9X1e1YwQwWnumAjaXX7t7BxrN7mJRNFm6cwFusodAeyM6GaZXMVbALsYj7m6yf4SGHnoW6NojroW9MxspnUpNEeA6wWPV'
14+
const xpubStr2 = 'xpub6FMojX6j83AkovffdfxdGcTXhmZ5Wvaed5L39eNyzQ5oK1CGiP2Gbaht8yTEBM2rfGMpNnkkXiQkhUKJnnrc31yLgvmimYWEhXdGXwy16eW';
15+
const addr = 'bitcoincash:qr9sawzzmstkluq9nqefsu7eqya4zk2w7udune2pmf';
16+
17+
const m44h145h0h = HdPublic.fromString(xpubStr);
18+
expect(m44h145h0h?.encoded).toBe(xpubStr);
19+
20+
const m44h145h0h0 = m44h145h0h?.derivePublic(0);
21+
expect(m44h145h0h0?.encoded).toBe(xpubStr2);
22+
23+
const m44h145h0h00 = m44h145h0h0?.derivePublic(0);
24+
25+
const point = m44h145h0h00?.point;
26+
27+
if ( ! point) {
28+
console.error('point is undefined');
29+
return;
30+
}
31+
const ecp = new EcPublic(point, true);
32+
const pa = ecp.toPaymentAddress(MAINNET_P2KH);
33+
expect(pa.encodedCashAddr()).toBe(addr);
34+
});
35+
});

tests/Input.test.ts

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
// Copyright (c) 2016-2025 Knuth Project developers.
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
import {
6+
Input, OutputPoint, RuleFork, Script,
7+
bytesToHexStr, hexStrToBytes, encodeHash, HashFunctions
8+
} from '..';
9+
10+
const noRules = RuleFork.toInt('no_rules');
11+
const allRules = RuleFork.toInt('all_rules');
12+
const bip16_rule = RuleFork.toInt('bip16_rule');
13+
const bip30_rule = RuleFork.toInt('bip30_rule');
14+
const bip34_rule = RuleFork.toInt('bip34_rule');
15+
const bip65_rule = RuleFork.toInt('bip65_rule');
16+
const bip66_rule = RuleFork.toInt('bip66_rule');
17+
const bip112_rule = RuleFork.toInt('bip112_rule');
18+
19+
const validRawHex = '54b755c39207d443fd96a8d12c94446a1c6f66e39c95e894c23418d7501f681b010000006b48304502203267910f55f2297360198fff57a3631be850965344370f732950b47795737875022100f7da90b82d24e6e957264b17d3e5042bab8946ee5fc676d15d915da450151d36012103893d5a06201d5cf61400e96fa4a7514fc12ab45166ace618d68b8066c9c585f9ffffffff';
20+
const validRawInput = hexStrToBytes(validRawHex);
21+
22+
describe('Input', () => {
23+
24+
it('Should be invalid by default', () => {
25+
const input = new Input();
26+
expect(input.valid).toBe(false);
27+
});
28+
29+
it('Should construct a valid input with a valid previous output, script, and sequence', () => {
30+
const prevOut = new OutputPoint(HashFunctions.nullHash, 5434);
31+
const script = Script.fromData(hexStrToBytes("ece424a6bb6ddf4db592c0faed60685047a361b1"), false);
32+
const seq = 4568656;
33+
34+
const input = new Input(prevOut, script, seq);
35+
expect(input.valid).toBe(true);
36+
expect(input.previousOutput).toBe(prevOut);
37+
expect(input.previousOutput?.hash).toEqual(HashFunctions.nullHash);
38+
expect(input.previousOutput?.index).toBe(5434);
39+
expect(input.script).toBe(script);
40+
expect(bytesToHexStr(input.script?.toData(false) ?? new Uint8Array)).toEqual("ece424a6bb6ddf4db592c0faed60685047a361b1");
41+
expect(input.seq).toBe(seq);
42+
});
43+
44+
it('Should construct a valid input with a valid previous output, script, and sequence', () => {
45+
const input = Input.fromData(validRawInput);
46+
expect(input?.valid).toBe(true);
47+
expect(encodeHash(input?.previousOutput?.hash ?? HashFunctions.nullHash)).toEqual('1b681f50d71834c294e8959ce3666f1c6a44942cd1a896fd43d40792c355b754');
48+
expect(input?.previousOutput?.index).toBe(1);
49+
expect(bytesToHexStr(input?.script?.toData(false) ?? new Uint8Array)).toEqual("48304502203267910f55f2297360198fff57a3631be850965344370f732950b47795737875022100f7da90b82d24e6e957264b17d3e5042bab8946ee5fc676d15d915da450151d36012103893d5a06201d5cf61400e96fa4a7514fc12ab45166ace618d68b8066c9c585f9");
50+
expect(input?.seq).toEqual(4294967295);
51+
expect(input?.serializedSize(true)).toBe(validRawInput.length);
52+
// expect(input?.toData(true)).toEqual(validRawInput);
53+
expect(bytesToHexStr(input?.toData(true) ?? new Uint8Array)).toEqual(validRawHex);
54+
});
55+
56+
it('Should return undefined if the input data is insufficient', () => {
57+
const input = Input.fromData(new Uint8Array(2));
58+
expect(input).toBeUndefined();
59+
});
60+
});
61+
62+
63+
// TEST_CASE("input from data insufficient data failure", "[input]") {
64+
// data_chunk data(2);
65+
66+
// input instance;
67+
68+
// REQUIRE( ! entity_from_data(instance, data));
69+
// REQUIRE( ! instance.is_valid());
70+
// }
71+
72+
// TEST_CASE("input from data valid data success", "[input]") {
73+
// auto const junk = base16_literal("000000000000005739943a9c29a1955dfae2b3f37de547005bfb9535192e5fb0000000000000005739943a9c29a1955dfae2b3f37de547005bfb9535192e5fb0");
74+
75+
// // data_chunk_stream_host host(junk);
76+
// byte_source<std::array<uint8_t, 64>> source(junk);
77+
// boost::iostreams::stream<byte_source<std::array<uint8_t, 64>>> stream(source);
78+
79+
// input instance;
80+
// REQUIRE(entity_from_data(instance, stream));
81+
// }
82+
83+
// TEST_CASE("input factory from data 1 valid input success", "[input]") {
84+
// auto const instance = create<input>(valid_raw_input);
85+
// REQUIRE(instance.is_valid());
86+
// REQUIRE(instance.serialized_size() == valid_raw_input.size());
87+
88+
// // Re-save and compare against original.
89+
// auto const resave = instance.to_data();
90+
// REQUIRE(resave.size() == valid_raw_input.size());
91+
// REQUIRE(resave == valid_raw_input);
92+
// }
93+
94+
// TEST_CASE("input factory from data 2 valid input success", "[input]") {
95+
// data_source stream(valid_raw_input);
96+
// auto instance = create<input>(stream);
97+
// REQUIRE(instance.is_valid());
98+
// REQUIRE(instance.serialized_size() == valid_raw_input.size());
99+
100+
// // Re-save and compare against original.
101+
// auto const resave = instance.to_data();
102+
// REQUIRE(resave.size() == valid_raw_input.size());
103+
// REQUIRE(resave == valid_raw_input);
104+
// }
105+
106+
// TEST_CASE("input factory from data 3 valid input success", "[input]") {
107+
// data_source stream(valid_raw_input);
108+
// istream_reader source(stream);
109+
// auto instance = create<input>(source);
110+
// REQUIRE(instance.is_valid());
111+
// REQUIRE(instance.serialized_size() == valid_raw_input.size());
112+
113+
// // Re-save and compare against original.
114+
// auto const resave = instance.to_data();
115+
// REQUIRE(resave.size() == valid_raw_input.size());
116+
// REQUIRE(resave == valid_raw_input);
117+
// }
118+
119+
// TEST_CASE("input is final max input sequence true", "[input]") {
120+
// input const instance({}, {}, max_input_sequence);
121+
// REQUIRE(instance.is_final());
122+
// }
123+
124+
// TEST_CASE("input is final sequence zero false", "[input]") {
125+
// input const instance({}, {}, 0);
126+
// REQUIRE( ! instance.is_final());
127+
// }
128+
129+
// TEST_CASE("input is locked enabled block type sequence age equals minimum false", "[input]") {
130+
// static auto const age = 7u;
131+
// static auto const sequence_enabled_block_type_minimum = age;
132+
// input instance({}, {}, sequence_enabled_block_type_minimum);
133+
// auto& prevout = instance.previous_output().validation;
134+
// prevout.height = 42;
135+
// REQUIRE( ! instance.is_locked(prevout.height + age, 0));
136+
// }
137+
138+
// TEST_CASE("input is locked enabled block type sequence age above minimum false", "[input]") {
139+
// static auto const age = 7u;
140+
// static auto const sequence_enabled_block_type_minimum = age - 1;
141+
// input instance({}, {}, sequence_enabled_block_type_minimum);
142+
// auto& prevout = instance.previous_output().validation;
143+
// prevout.height = 42;
144+
// REQUIRE( ! instance.is_locked(prevout.height + age, 0));
145+
// }
146+
147+
// TEST_CASE("input is locked enabled block type sequence age below minimum true", "[input]") {
148+
// static auto const age = 7u;
149+
// static auto const sequence_enabled_block_type_minimum = age + 1;
150+
// input instance({}, {}, sequence_enabled_block_type_minimum);
151+
// auto& prevout = instance.previous_output().validation;
152+
// prevout.height = 42;
153+
// REQUIRE(instance.is_locked(prevout.height + age, 0));
154+
// }
155+
156+
// TEST_CASE("input is locked disabled block type sequence age below minimum false", "[input]") {
157+
// static auto const age = 7u;
158+
// static auto const sequence_disabled_block_type_minimum = relative_locktime_disabled | (age + 1);
159+
// input instance({}, {}, sequence_disabled_block_type_minimum);
160+
// auto& prevout = instance.previous_output().validation;
161+
// prevout.height = 42;
162+
// REQUIRE( ! instance.is_locked(prevout.height + age, 0));
163+
// }
164+
165+
// TEST_CASE("input is locked enabled time type sequence age equals minimum false", "[input]") {
166+
// static auto const age = 7u;
167+
// static auto const age_seconds = 7u << relative_locktime_seconds_shift;
168+
// static auto const sequence_enabled_time_type_minimum = relative_locktime_time_locked | age;
169+
// input instance({}, {}, sequence_enabled_time_type_minimum);
170+
// auto& prevout = instance.previous_output().validation;
171+
// prevout.median_time_past = 42;
172+
// REQUIRE( ! instance.is_locked(0, prevout.median_time_past + age_seconds));
173+
// }
174+
175+
// TEST_CASE("input is locked enabled time type sequence age above minimum false", "[input]") {
176+
// static auto const age = 7u;
177+
// static auto const age_seconds = 7u << relative_locktime_seconds_shift;
178+
// static auto const sequence_enabled_time_type_minimum = relative_locktime_time_locked | (age - 1);
179+
// input instance({}, {}, sequence_enabled_time_type_minimum);
180+
// auto& prevout = instance.previous_output().validation;
181+
// prevout.median_time_past = 42;
182+
// REQUIRE( ! instance.is_locked(0, prevout.median_time_past + age_seconds));
183+
// }
184+
185+
// TEST_CASE("input is locked enabled time type sequence age below minimum true", "[input]") {
186+
// static auto const age = 7u;
187+
// static auto const age_seconds = 7u << relative_locktime_seconds_shift;
188+
// static auto const sequence_enabled_time_type_minimum = relative_locktime_time_locked | (age + 1);
189+
// input instance({}, {}, sequence_enabled_time_type_minimum);
190+
// auto& prevout = instance.previous_output().validation;
191+
// prevout.median_time_past = 42;
192+
// REQUIRE(instance.is_locked(0, prevout.median_time_past + age_seconds));
193+
// }
194+
195+
// TEST_CASE("input is locked disabled time type sequence age below minimum false", "[input]") {
196+
// static auto const age = 7u;
197+
// static auto const age_seconds = 7u << relative_locktime_seconds_shift;
198+
// static auto const sequence_disabled_time_type_minimum = relative_locktime_disabled | relative_locktime_time_locked | (age + 1);
199+
// input instance({}, {}, sequence_disabled_time_type_minimum);
200+
// auto& prevout = instance.previous_output().validation;
201+
// prevout.median_time_past = 42;
202+
// REQUIRE( ! instance.is_locked(0, prevout.median_time_past + age_seconds));
203+
// }
204+
205+
// TEST_CASE("input signature operations bip16 inactive returns script sigops", "[input]") {
206+
// auto const raw_script = to_chunk(base16_literal("02acad"));
207+
// script script;
208+
// REQUIRE(entity_from_data(script, raw_script, true));
209+
// input instance;
210+
// instance.set_script(script);
211+
// REQUIRE(script.sigops(false) == instance.signature_operations(false, false));
212+
// }
213+
214+
// TEST_CASE("input signature operations bip16 active cache empty returns script sigops", "[input]") {
215+
// auto const raw_script = to_chunk(base16_literal("02acad"));
216+
// script script;
217+
// REQUIRE(entity_from_data(script, raw_script, true));
218+
// input instance;
219+
// instance.set_script(script);
220+
// REQUIRE(script.sigops(false) == instance.signature_operations(true, false));
221+
// }
222+
223+
// TEST_CASE("input previous output setter 1 roundtrip success", "[input]") {
224+
// output_point const value{
225+
// hash_literal("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
226+
// 5434u};
227+
228+
// input instance;
229+
// REQUIRE(value != instance.previous_output());
230+
// instance.set_previous_output(value);
231+
// REQUIRE(value == instance.previous_output());
232+
// auto const& restricted = instance;
233+
// REQUIRE(value == restricted.previous_output());
234+
// }
235+
236+
// TEST_CASE("input previous output setter 2 roundtrip success", "[input]") {
237+
// output_point const value{
238+
// hash_literal("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"),
239+
// 5434u};
240+
241+
// auto dup_value = value;
242+
243+
// input instance;
244+
// REQUIRE(value != instance.previous_output());
245+
// instance.set_previous_output(std::move(dup_value));
246+
// REQUIRE(value == instance.previous_output());
247+
// auto const& restricted = instance;
248+
// REQUIRE(value == restricted.previous_output());
249+
// }
250+
251+
// TEST_CASE("input script setter 1 roundtrip success", "[input]") {
252+
// script value;
253+
// auto const data = to_chunk(base16_literal("ece424a6bb6ddf4db592c0faed60685047a361b1"));
254+
// REQUIRE(entity_from_data(value, data, false));
255+
256+
// input instance;
257+
// REQUIRE(value != instance.script());
258+
// instance.set_script(value);
259+
// REQUIRE(value == instance.script());
260+
// auto const& restricted = instance;
261+
// REQUIRE(value == restricted.script());
262+
// }
263+
264+
// TEST_CASE("input script setter 2 roundtrip success", "[input]") {
265+
// script value;
266+
// auto const data = to_chunk(base16_literal("ece424a6bb6ddf4db592c0faed60685047a361b1"));
267+
// REQUIRE(entity_from_data(value, data, false));
268+
269+
// auto dup_value = value;
270+
// input instance;
271+
// REQUIRE(value != instance.script());
272+
// instance.set_script(std::move(dup_value));
273+
// REQUIRE(value == instance.script());
274+
// auto const& restricted = instance;
275+
// REQUIRE(value == restricted.script());
276+
// }
277+
278+
// TEST_CASE("input sequence roundtrip success", "[input]") {
279+
// uint32_t value = 1254u;
280+
// input instance;
281+
// REQUIRE(value != instance.sequence());
282+
// instance.set_sequence(value);
283+
// REQUIRE(value == instance.sequence());
284+
// }
285+
286+
// TEST_CASE("input operator assign equals 1 always matches equivalent", "[input]") {
287+
// input expected;
288+
// REQUIRE(entity_from_data(expected, valid_raw_input));
289+
// input instance;
290+
// instance = create<input>(valid_raw_input);
291+
// REQUIRE(instance == expected);
292+
// }
293+
294+
// TEST_CASE("input operator assign equals 2 always matches equivalent", "[input]") {
295+
// input expected;
296+
// REQUIRE(entity_from_data(expected, valid_raw_input));
297+
// input instance;
298+
// instance = expected;
299+
// REQUIRE(instance == expected);
300+
// }
301+
302+
// TEST_CASE("input operator boolean equals duplicates returns true", "[input]") {
303+
// input alpha;
304+
// input beta;
305+
// REQUIRE(entity_from_data(alpha, valid_raw_input));
306+
// REQUIRE(entity_from_data(beta, valid_raw_input));
307+
// REQUIRE(alpha == beta);
308+
// }
309+
310+
// TEST_CASE("input operator boolean equals differs returns false", "[input]") {
311+
// input alpha;
312+
// input beta;
313+
// REQUIRE(entity_from_data(alpha, valid_raw_input));
314+
// REQUIRE(alpha != beta);
315+
// }
316+
317+
// TEST_CASE("input operator boolean not equals duplicates returns false", "[input]") {
318+
// input alpha;
319+
// input beta;
320+
// REQUIRE(entity_from_data(alpha, valid_raw_input));
321+
// REQUIRE(entity_from_data(beta, valid_raw_input));
322+
// REQUIRE(alpha == beta);
323+
// }
324+
325+
// TEST_CASE("input operator boolean not equals differs returns true", "[input]") {
326+
// input alpha;
327+
// input beta;
328+
// REQUIRE(entity_from_data(alpha, valid_raw_input));
329+
// REQUIRE(alpha != beta);
330+
// }
331+
332+
// // End Test Suite

0 commit comments

Comments
 (0)