saito-js 0.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.
@@ -0,0 +1,173 @@
1
+ import type {WasmTransaction} from 'saito-wasm/dist/types/pkg/node/index_bg';
2
+ import Slip from "./slip";
3
+ import Saito from "../saito";
4
+ import Factory from "./factory";
5
+
6
+ export enum TransactionType {
7
+ Normal = 0,
8
+ Fee = 1,
9
+ GoldenTicket = 2,
10
+ ATR = 3,
11
+ Vip = 4,
12
+ SPV = 5,
13
+ Issuance = 6,
14
+ Other = 7,
15
+ }
16
+
17
+ export default class Transaction {
18
+ protected tx: WasmTransaction;
19
+ public static Type: any;
20
+ public msg: any = {};
21
+
22
+ // TODO : factory pattern might be useful here to remove unnecessary wrappings
23
+ constructor(tx?: WasmTransaction, json?: any) {
24
+ if (tx) {
25
+ this.tx = tx;
26
+ } else {
27
+ this.tx = new Transaction.Type();
28
+ }
29
+ if (json) {
30
+ for (let slip of json.to) {
31
+ let s = new Slip(undefined, slip);
32
+ this.addToSlip(s);
33
+ }
34
+ for (let slip of json.from) {
35
+ let s = new Slip(undefined, slip);
36
+ this.addFromSlip(s);
37
+ }
38
+ this.timestamp = json.timestamp;
39
+ this.type = json.type;
40
+ this.signature = json.signature;
41
+ this.data = new Uint8Array(Buffer.from(json.buffer, "base64"));
42
+ this.txs_replacements = json.txs_replacements;
43
+ }
44
+ }
45
+
46
+ public get wasmTransaction(): WasmTransaction {
47
+ return this.tx;
48
+ }
49
+
50
+ public addFromSlip(slip: Slip) {
51
+ this.tx.add_from_slip(slip.wasmSlip);
52
+ }
53
+
54
+ public addToSlip(slip: Slip) {
55
+ this.tx.add_to_slip(slip.wasmSlip);
56
+ }
57
+
58
+ public get to(): Array<Slip> {
59
+ return this.tx.to.map(slip => {
60
+ return Saito.getInstance().factory.createSlip(slip);
61
+ });
62
+ }
63
+
64
+ public get from(): Array<Slip> {
65
+ return this.tx.from.map(slip => {
66
+ return Saito.getInstance().factory.createSlip(slip);
67
+ });
68
+ }
69
+
70
+ public get type(): TransactionType {
71
+ return this.tx.type as TransactionType;
72
+ }
73
+
74
+ public set type(type: TransactionType) {
75
+ this.tx.type = type as number;
76
+ }
77
+
78
+ public get timestamp(): number {
79
+ return Number(this.tx.timestamp);
80
+ }
81
+
82
+ public set timestamp(timestamp: bigint | number) {
83
+ this.tx.timestamp = BigInt(timestamp);
84
+ }
85
+
86
+ public set signature(sig: string) {
87
+ this.tx.signature = sig;
88
+ }
89
+
90
+ public get signature(): string {
91
+ return this.tx.signature;
92
+ }
93
+
94
+ public set data(buffer: Uint8Array) {
95
+ this.tx.data = buffer;
96
+ }
97
+
98
+ public get data(): Uint8Array {
99
+ return this.tx.data;
100
+ }
101
+
102
+ public set txs_replacements(r: number) {
103
+ this.tx.txs_replacements = r;
104
+ }
105
+
106
+ public get txs_replacements(): number {
107
+ return this.tx.txs_replacements;
108
+ }
109
+
110
+ public get total_fees(): bigint {
111
+ return this.tx.total_fees;
112
+ }
113
+
114
+ public sign() {
115
+ Saito.getInstance().signTransaction(this);
116
+ }
117
+
118
+ public isFrom(key: string): boolean {
119
+ return this.tx.is_from(key);
120
+ }
121
+
122
+ public isTo(key: string): boolean {
123
+ return this.tx.is_to(key);
124
+ }
125
+
126
+ public toJson() {
127
+ this.packData();
128
+ return {
129
+ to: this.to.map((slip) => slip.toJson()),
130
+ from: this.from.map((slip) => slip.toJson()),
131
+ type: this.type,
132
+ timestamp: this.timestamp,
133
+ signature: this.signature,
134
+ buffer: Buffer.from(this.data).toString("base64"),
135
+ txs_replacements: this.txs_replacements,
136
+ total_fees: this.total_fees,
137
+ };
138
+ }
139
+
140
+ public static deserialize(buffer: Uint8Array, factory: Factory): Transaction | null {
141
+ console.log("1111");
142
+ try {
143
+ let wasmTx = Transaction.Type.deserialize(buffer);
144
+ console.log("2222");
145
+ return factory.createTransaction(wasmTx);
146
+ } catch (e) {
147
+ console.debug(e);
148
+ return null;
149
+ }
150
+ }
151
+
152
+ public packData() {
153
+ if (Object.keys(this.msg).length === 0) {
154
+ this.data = new Uint8Array(Buffer.alloc(0));
155
+ } else {
156
+ this.data = new Uint8Array(Buffer.from(JSON.stringify(this.msg), "utf-8"));
157
+ }
158
+ }
159
+
160
+ public unpackData() {
161
+ if (this.data.byteLength === 0) {
162
+ this.msg = {};
163
+ } else {
164
+ try {
165
+ this.msg = JSON.parse(Buffer.from(this.data).toString("utf-8"));
166
+ } catch (error) {
167
+ console.log("failed parsing tx buffer into msg");
168
+ console.error(error);
169
+ this.msg = {};
170
+ }
171
+ }
172
+ }
173
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "saito-js",
3
+ "version": "0.0.2",
4
+ "description": "js wrappings around saito-core using wasm",
5
+ "scripts": {
6
+ "test": "env TS_NODE_PROJECT=\"tsconfig.testing.json\" mocha --require ts-node/register 'tests/**/*.ts'",
7
+ "build": "webpack",
8
+ "serve": "webpack-dev-server"
9
+ },
10
+ "author": "",
11
+ "license": "ISC",
12
+ "devDependencies": {
13
+ "@babel/core": "^7.16.5",
14
+ "@babel/preset-env": "^7.16.5",
15
+ "@babel/preset-typescript": "^7.16.5",
16
+ "@types/jest": "^29.2.5",
17
+ "@types/ws": "^8.5.4",
18
+ "@wasm-tool/wasm-pack-plugin": "1.6.0",
19
+ "babel-loader": "^8.2.3",
20
+ "babel-polyfill": "^6.26.0",
21
+ "copy-webpack-plugin": "^11.0.0",
22
+ "crypto-browserify": "^3.12.0",
23
+ "html-webpack-plugin": "^5.5.0",
24
+ "ignore-loader": "^0.1.2",
25
+ "path-browserify": "^1.0.1",
26
+ "source-map-loader": "^3.0.0",
27
+ "stream-browserify": "^3.0.0",
28
+ "ts-loader": "^9.4.2",
29
+ "ts-node-dev": "^2.0.0",
30
+ "typescript": "^4.9.4",
31
+ "webpack": "^5.75.0",
32
+ "webpack-cli": "^5.0.1",
33
+ "webpack-merge": "^5.8.0"
34
+ },
35
+ "dependencies": {
36
+ "buffer": "^6.0.3",
37
+ "cookie-parser": "~1.4.6",
38
+ "cors": "^2.8.5",
39
+ "debug": "~4.3.4",
40
+ "express": "~4.18.2",
41
+ "morgan": "~1.10.0",
42
+ "node-fetch": "^2.6.1",
43
+ "process": "^0.11.10",
44
+ "url": "^0.11.0",
45
+ "util": "^0.12.5",
46
+ "ws": "^8.12.0"
47
+ }
48
+ }
package/saito.ts ADDED
@@ -0,0 +1,322 @@
1
+ import SharedMethods from "./shared_methods";
2
+ import Transaction from "./lib/transaction";
3
+ import Block from "./lib/block";
4
+ import Factory from "./lib/factory";
5
+ import Peer from "./lib/peer";
6
+
7
+ // export enum MessageType {
8
+ // HandshakeChallenge = 1,
9
+ // HandshakeResponse,
10
+ // //HandshakeCompletion,
11
+ // ApplicationMessage = 4,
12
+ // Block,
13
+ // Transaction,
14
+ // BlockchainRequest,
15
+ // BlockHeaderHash,
16
+ // Ping,
17
+ // SPVChain,
18
+ // Services,
19
+ // GhostChain,
20
+ // GhostChainRequest,
21
+ // Result,
22
+ // Error,
23
+ // ApplicationTransaction,
24
+ // }
25
+
26
+ export default class Saito {
27
+ private static instance: Saito;
28
+ private static libInstance: any;
29
+ sockets: Map<bigint, any> = new Map<bigint, any>();
30
+ nextIndex: bigint = BigInt(0);
31
+ factory = new Factory();
32
+ promises = new Map<number, any>();
33
+ private callbackIndex: number = 1;
34
+
35
+
36
+ public static async initialize(configs: any, sharedMethods: SharedMethods, factory = new Factory()) {
37
+ this.instance = new Saito(factory);
38
+
39
+ // @ts-ignore
40
+ globalThis.shared_methods = {
41
+ send_message: (peer_index: bigint, buffer: Uint8Array) => {
42
+ sharedMethods.sendMessage(peer_index, buffer);
43
+ },
44
+ send_message_to_all: (buffer: Uint8Array, exceptions: Array<bigint>) => {
45
+ sharedMethods.sendMessageToAll(buffer, exceptions);
46
+ },
47
+ connect_to_peer: (peer_data: any) => {
48
+ sharedMethods.connectToPeer(peer_data);
49
+ },
50
+ write_value: (key: string, value: Uint8Array) => {
51
+ return sharedMethods.writeValue(key, value);
52
+ },
53
+ read_value: (key: string) => {
54
+ return sharedMethods.readValue(key);
55
+ },
56
+ load_block_file_list: () => {
57
+ return sharedMethods.loadBlockFileList();
58
+ },
59
+ is_existing_file: (key: string) => {
60
+ return sharedMethods.isExistingFile(key);
61
+ },
62
+ remove_value: (key: string) => {
63
+ return sharedMethods.removeValue(key);
64
+ },
65
+ disconnect_from_peer: (peer_index: bigint) => {
66
+ return sharedMethods.disconnectFromPeer(peer_index);
67
+ },
68
+ fetch_block_from_peer: (hash: Uint8Array, peer_index: bigint, url: string) => {
69
+ console.log("fetching block : " + url);
70
+ sharedMethods.fetchBlockFromPeer(url).then((buffer: Uint8Array) => {
71
+ Saito.getLibInstance().process_fetched_block(buffer, hash, peer_index);
72
+ })
73
+ },
74
+ process_api_call: (buffer: Uint8Array, msgIndex: number, peerIndex: bigint) => {
75
+ return sharedMethods.processApiCall(buffer, msgIndex, peerIndex).then(() => {
76
+
77
+ });
78
+ },
79
+ process_api_success: (buffer: Uint8Array, msgIndex: number, peerIndex: bigint) => {
80
+ return sharedMethods.processApiSuccess(buffer, msgIndex, peerIndex);
81
+ },
82
+ process_api_error: (buffer: Uint8Array, msgIndex: number, peerIndex: bigint) => {
83
+ return sharedMethods.processApiError(buffer, msgIndex, peerIndex);
84
+ },
85
+ send_interface_event: (event: string, peerIndex: bigint) => {
86
+ return sharedMethods.sendInterfaceEvent(event, peerIndex);
87
+ }
88
+ };
89
+
90
+ await Saito.getLibInstance().initialize(JSON.stringify(configs));
91
+
92
+ console.log("saito initialized");
93
+
94
+ setInterval(() => {
95
+ Saito.getLibInstance().process_timer_event(BigInt(100));
96
+ // console.log(`WASM memory usage is ${wasm.memory.buffer.byteLength} bytes`);
97
+ }, 100);
98
+ }
99
+
100
+ constructor(factory: Factory) {
101
+ this.factory = factory;
102
+ }
103
+
104
+ public static getInstance(): Saito {
105
+ return Saito.instance;
106
+ }
107
+
108
+ public static getLibInstance(): any {
109
+ return Saito.libInstance;
110
+ }
111
+
112
+ public static setLibInstance(instance: any) {
113
+ Saito.libInstance = instance;
114
+ }
115
+
116
+ public addNewSocket(socket: any): bigint {
117
+ this.nextIndex++;
118
+ this.sockets.set(this.nextIndex, socket);
119
+ return this.nextIndex;
120
+ }
121
+
122
+ public getSocket(index: bigint): any | null {
123
+ return this.sockets.get(index);
124
+ }
125
+
126
+ public removeSocket(index: bigint) {
127
+ let socket = this.sockets.get(index);
128
+ this.sockets.delete(index);
129
+ socket.close();
130
+ }
131
+
132
+ public async initialize(configs: any): Promise<any> {
133
+ return Saito.getLibInstance().initialize(configs);
134
+ }
135
+
136
+ public async getLatestBlockHash(): Promise<string> {
137
+ return Saito.getLibInstance().get_latest_block_hash();
138
+ }
139
+
140
+ public async getBlock<B extends Block>(blockHash: string): Promise<B> {
141
+ let block = await Saito.getLibInstance().get_block(blockHash);
142
+ return Saito.getInstance().factory.createBlock(block) as B;
143
+ }
144
+
145
+ public async getPublicKey(): Promise<string> {
146
+ return Saito.getLibInstance().get_public_key();
147
+ }
148
+
149
+ public async getPrivateKey(): Promise<string> {
150
+ return Saito.getLibInstance().get_private_key();
151
+ }
152
+
153
+ public async processNewPeer(index: bigint, peer_config: any): Promise<void> {
154
+ return Saito.getLibInstance().process_new_peer(index, peer_config);
155
+ }
156
+
157
+ public async processPeerDisconnection(peer_index: bigint): Promise<void> {
158
+ return Saito.getLibInstance().process_peer_disconnection(peer_index);
159
+ }
160
+
161
+ public async processMsgBufferFromPeer(buffer: Uint8Array, peer_index: bigint): Promise<void> {
162
+ return Saito.getLibInstance().process_msg_buffer_from_peer(buffer, peer_index);
163
+ }
164
+
165
+ public async processFetchedBlock(buffer: Uint8Array, hash: Uint8Array, peer_index: bigint): Promise<void> {
166
+ return Saito.getLibInstance().process_fetched_block(buffer, hash, peer_index);
167
+ }
168
+
169
+ public async processTimerEvent(duration_in_ms: bigint): Promise<void> {
170
+ return Saito.getLibInstance().process_timer_event(duration_in_ms);
171
+ }
172
+
173
+ public hash(buffer: Uint8Array): string {
174
+ return Saito.getLibInstance().hash(buffer);
175
+ }
176
+
177
+ public signBuffer(buffer: Uint8Array, privateKey: String): string {
178
+ return Saito.getLibInstance().sign_buffer(buffer, privateKey);
179
+ }
180
+
181
+ public verifySignature(buffer: Uint8Array, signature: string, publicKey: string): boolean {
182
+ return Saito.getLibInstance().verify_signature(buffer, signature, publicKey);
183
+ }
184
+
185
+ public async createTransaction<T extends Transaction>(
186
+ publickey = "",
187
+ amount = BigInt(0),
188
+ fee = BigInt(0),
189
+ force_merge = false
190
+ ): Promise<T> {
191
+ let wasmTx = await Saito.getLibInstance().create_transaction(publickey, amount, fee, force_merge);
192
+ let tx = Saito.getInstance().factory.createTransaction(wasmTx) as T;
193
+ tx.timestamp = new Date().getTime();
194
+ console.log("wwwwwwwwwww : " + tx.timestamp);
195
+ return tx;
196
+ }
197
+
198
+ public async signTransaction(tx: Transaction): Promise<Transaction> {
199
+ tx.packData();
200
+ await tx.wasmTransaction.sign();
201
+ return tx;
202
+ }
203
+
204
+ public async getPendingTransactions<Tx extends Transaction>(): Promise<Array<Tx>> {
205
+ let txs = await Saito.getLibInstance().get_pending_txs();
206
+ return txs.map((tx: any) => Saito.getInstance().factory.createTransaction(tx));
207
+ }
208
+
209
+ public async signAndEncryptTransaction(tx: Transaction) {
210
+ return tx.wasmTransaction.sign_and_encrypt();
211
+ }
212
+
213
+ public async getBalance(): Promise<bigint> {
214
+ return Saito.getLibInstance().get_balance();
215
+ }
216
+
217
+ public async getPeers(): Promise<Array<Peer>> {
218
+ let peers = await Saito.getLibInstance().get_peers();
219
+ return peers.map((peer: any) => {
220
+ return this.factory.createPeer(peer);
221
+ });
222
+ }
223
+
224
+
225
+ public async getPeer(index: bigint): Promise<Peer | null> {
226
+ let peer = await Saito.getLibInstance().get_peer(index);
227
+ if (!peer) {
228
+ return null;
229
+ }
230
+ return this.factory.createPeer(peer);
231
+ }
232
+
233
+ public generatePrivateKey(): string {
234
+ return Saito.getLibInstance().generate_private_key();
235
+ }
236
+
237
+ public generatePublicKey(privateKey: string): string {
238
+ return Saito.getLibInstance().generate_public_key(privateKey);
239
+ }
240
+
241
+ public async propagateTransaction(tx: Transaction) {
242
+ return Saito.getLibInstance().propagate_transaction(tx.wasmTransaction);
243
+ }
244
+
245
+ public async sendApiCall(buffer: Uint8Array, peerIndex: bigint, waitForReply: boolean): Promise<Uint8Array> {
246
+ console.log("saito.sendApiCall : peer = " + peerIndex + " wait for reply = " + waitForReply);
247
+ let callbackIndex = this.callbackIndex++;
248
+ if (waitForReply) {
249
+ return new Promise((resolve, reject) => {
250
+ this.promises.set(callbackIndex, {
251
+ resolve,
252
+ reject
253
+ });
254
+ Saito.getLibInstance().send_api_call(buffer, callbackIndex, peerIndex);
255
+ });
256
+ } else {
257
+ return Saito.getLibInstance().send_api_call(buffer, callbackIndex, peerIndex);
258
+ }
259
+ }
260
+
261
+ public async sendApiSuccess(msgId: number, buffer: Uint8Array, peerIndex: bigint) {
262
+ await Saito.getLibInstance().send_api_success(buffer, msgId, peerIndex);
263
+ }
264
+
265
+ public async sendApiError(msgId: number, buffer: Uint8Array, peerIndex: bigint) {
266
+ await Saito.getLibInstance().send_api_error(buffer, msgId, peerIndex);
267
+ }
268
+
269
+ public async sendTransactionWithCallback(transaction: Transaction, callback?: any, peerIndex?: bigint): Promise<any> {
270
+ // TODO : implement retry on fail
271
+ // TODO : stun code goes here probably???
272
+ console.log("saito.sendTransactionWithCallback : peer = " + peerIndex + " sig = " + transaction.signature);
273
+ let buffer = transaction.wasmTransaction.serialize();
274
+ console.log("sendTransactionWithCallback : " + peerIndex + " with length : " + buffer.byteLength + " sent : ", transaction.msg);
275
+ await this.sendApiCall(buffer, peerIndex || BigInt(0), !!callback)
276
+ .then((buffer: Uint8Array) => {
277
+ if (callback) {
278
+ // console.log("deserializing tx. buffer length = " + buffer.byteLength);
279
+ console.log("sendTransactionWithCallback. buffer length = " + buffer.byteLength);
280
+
281
+ let tx = this.factory.createTransaction();
282
+ tx.data = buffer;
283
+ tx.unpackData();
284
+ // let tx = Transaction.deserialize(buffer, this.factory);
285
+ // if (tx) {
286
+ // console.log("sendTransactionWithCallback received : ", tx);
287
+ // return callback(tx.data);
288
+ // } else {
289
+ // console.log("sendTransactionWithCallback sending direct buffer since tx deserialization failed");
290
+ return callback(tx);
291
+ // }
292
+ }
293
+ })
294
+ .catch((error) => {
295
+ console.error(error);
296
+ if (callback) {
297
+ return callback({err: error.toString()});
298
+ }
299
+ });
300
+ }
301
+
302
+ public async propagateServices(peerIndex: bigint, services: string[]) {
303
+ return Saito.getLibInstance().propagateServices(peerIndex, services);
304
+ }
305
+
306
+ public async sendRequest(message: string, data: any = "", callback?: any, peerIndex?: bigint): Promise<any> {
307
+ console.log("saito.sendRequest : peer = " + peerIndex);
308
+ let publicKey = await this.getPublicKey();
309
+ let tx = await this.createTransaction(publicKey, BigInt(0), BigInt(0));
310
+ tx.msg = {
311
+ request: message,
312
+ data: data,
313
+ };
314
+ tx.packData();
315
+ return this.sendTransactionWithCallback(tx, (tx: Transaction) => {
316
+ if (callback) {
317
+ return callback(tx.msg);
318
+ }
319
+ }, peerIndex);
320
+ }
321
+ }
322
+
@@ -0,0 +1,30 @@
1
+ export default interface SharedMethods {
2
+ sendMessage(peerIndex: bigint, buffer: Uint8Array): void;
3
+
4
+ sendMessageToAll(buffer: Uint8Array, exceptions: Array<bigint>): void;
5
+
6
+ connectToPeer(peerData: any): void;
7
+
8
+ writeValue(key: string, value: Uint8Array): void;
9
+
10
+ readValue(key: string): Uint8Array | null;
11
+
12
+ loadBlockFileList(): Array<string>;
13
+
14
+ isExistingFile(key: string): boolean;
15
+
16
+ removeValue(key: string): void;
17
+
18
+ disconnectFromPeer(peerIndex: bigint): void;
19
+
20
+ fetchBlockFromPeer(url: string): Promise<Uint8Array>;
21
+
22
+ processApiCall(buffer: Uint8Array, msgIndex: number, peerIndex: bigint): Promise<void>;
23
+
24
+ processApiSuccess(buffer: Uint8Array, msgIndex: number, peerIndex: bigint): void;
25
+
26
+ processApiError(buffer: Uint8Array, msgIndex: number, peerIndex: bigint): void;
27
+
28
+ sendInterfaceEvent(event: String, peerIndex: bigint): void;
29
+
30
+ }
@@ -0,0 +1,35 @@
1
+ // // @ts-ignore
2
+ // import {expect} from "chai";
3
+ // import SaitoJs from "../index";
4
+ //
5
+ //
6
+ // describe('setup test', function () {
7
+ // it('should load the wasm lib correctly', async function () {
8
+ // console.log("loading...");
9
+ //
10
+ // // // @ts-ignore
11
+ // // const s = await import("saito-wasm/dist/server");
12
+ // // // @ts-ignore
13
+ // //
14
+ // // console.log("s = ", s);
15
+ // // // @ts-ignore
16
+ // // let s1 = await s;
17
+ // // console.log("s1 = ", s1);
18
+ // // // @ts-ignore
19
+ // // let saito = s1.default;
20
+ // // // console.log("s2 = ", saito);
21
+ // //
22
+ // // expect(saito).to.exist;
23
+ // // // let s = await saito.default;
24
+ // // // console.log(s);
25
+ // // console.log("lib test 2 = ", saito);
26
+ // let result = await SaitoJs.initialize();
27
+ // expect(result).to.equal("initialized");
28
+ //
29
+ // let saito = SaitoJs.getInstance();
30
+ //
31
+ // expect(saito).to.exist;
32
+ // expect(saito.get_public_key).to.exist;
33
+ // expect(saito.get_public_key()).to.equal("publickey");
34
+ // });
35
+ // });