mirror of
https://github.com/alexgo-io/stacks-blockchain-api.git
synced 2026-01-12 16:53:19 +08:00
feat: improve error handling in Stacks message parsing
This commit is contained in:
17
.eslintrc.js
17
.eslintrc.js
@@ -3,10 +3,15 @@ module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: [
|
||||
'@typescript-eslint',
|
||||
'eslint-plugin-tsdoc',
|
||||
],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
project: [
|
||||
'./tsconfig.json',
|
||||
],
|
||||
ecmaVersion: 2019,
|
||||
sourceType: 'module',
|
||||
},
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
@@ -15,8 +20,14 @@ module.exports = {
|
||||
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
ignorePatterns: ["lib/*"],
|
||||
ignorePatterns: [
|
||||
'lib/*',
|
||||
],
|
||||
rules: {
|
||||
"@typescript-eslint/no-use-before-define": ["error", "nofunc"]
|
||||
'@typescript-eslint/no-inferrable-types': 'off',
|
||||
'@typescript-eslint/no-use-before-define': ['error', 'nofunc'],
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'no-warning-comments': 'warn',
|
||||
'tsdoc/syntax': 'error',
|
||||
}
|
||||
};
|
||||
|
||||
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@@ -14,7 +14,9 @@
|
||||
],
|
||||
"args": [
|
||||
"${workspaceFolder}/src/index.ts"
|
||||
]
|
||||
],
|
||||
"outputCapture": "std",
|
||||
"internalConsoleOptions": "openOnSessionStart"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"search.exclude": {
|
||||
"./lib/**": true
|
||||
},
|
||||
}
|
||||
75
package-lock.json
generated
75
package-lock.json
generated
@@ -30,6 +30,44 @@
|
||||
"integrity": "sha512-A+wfdVwD1Sxd/D3PPJI67Evo7q2fp15hCOFLB5jmzcS1MdN8BQdFm6j51Sti8xLN4qHmuYkicbFBUluGx2h63g==",
|
||||
"dev": true
|
||||
},
|
||||
"@microsoft/tsdoc": {
|
||||
"version": "0.12.16",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.16.tgz",
|
||||
"integrity": "sha512-SX8JNEVy6U5+56aQnQB8A2XK+WSF//b0kBa6KqxE48pcccqVuIu1ePAR/EWd1cQB6zWv26QIY5uv/++qm+Z3Mw==",
|
||||
"dev": true
|
||||
},
|
||||
"@microsoft/tsdoc-config": {
|
||||
"version": "0.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.13.0.tgz",
|
||||
"integrity": "sha512-ga2nChnT2K4y9zqQ0/8xf3TgFz+zbgxLXVRsIsbBhgx2gi0Ez4VQ2CKjElu5kF0uzLz6fLemkkVzUyi/ptekvw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@microsoft/tsdoc": "0.12.16",
|
||||
"ajv": "~6.10.2",
|
||||
"jju": "~1.4.0",
|
||||
"resolve": "~1.12.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
|
||||
"integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
|
||||
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/eslint-visitor-keys": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
|
||||
@@ -43,9 +81,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "12.12.27",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.27.tgz",
|
||||
"integrity": "sha512-odQFl/+B9idbdS0e8IxDl2ia/LP8KZLXhV3BUeI98TrZp0uoIzQPhGd+5EtzHmT0SMOIaPd7jfz6pOHLWTtl7A=="
|
||||
"version": "13.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.4.tgz",
|
||||
"integrity": "sha512-oVeL12C6gQS/GAExndigSaLxTrKpQPxewx9bOcwfvJiJge4rr7wNaph4J+ns5hrmIV2as5qxqN8YKthn9qh0jw=="
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "2.20.0",
|
||||
@@ -384,6 +422,16 @@
|
||||
"prettier-linter-helpers": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"eslint-plugin-tsdoc": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.1.tgz",
|
||||
"integrity": "sha512-PPobACY4MUmk1r7oQZwQ53GcUvkmlGhkEoBc+JWRcHHXrJ7Ny4OGWUoha9lzLJCyKOP5C8AYjVAr2rxahsXWZg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@microsoft/tsdoc": "0.12.16",
|
||||
"@microsoft/tsdoc-config": "0.13.0"
|
||||
}
|
||||
},
|
||||
"eslint-scope": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz",
|
||||
@@ -683,6 +731,12 @@
|
||||
"integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
|
||||
"dev": true
|
||||
},
|
||||
"jju": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz",
|
||||
"integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=",
|
||||
"dev": true
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -845,6 +899,12 @@
|
||||
"integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
|
||||
"dev": true
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
|
||||
"dev": true
|
||||
},
|
||||
"prelude-ls": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||
@@ -884,6 +944,15 @@
|
||||
"integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==",
|
||||
"dev": true
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.12.3",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.3.tgz",
|
||||
"integrity": "sha512-hF6+hAPlxjqHWrw4p1rF3Wztbgxd4AjA5VlUzY5zcTb4J8D3JK4/1RjU48pHz2PJWzGVsLB1VWZkvJzhK2CCOA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-parse": "^1.0.6"
|
||||
}
|
||||
},
|
||||
"resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
|
||||
10
package.json
10
package.json
@@ -6,8 +6,8 @@
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "tsc",
|
||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx -f visualstudio",
|
||||
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx -f visualstudio --fix"
|
||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx -f codeframe",
|
||||
"lint:fix": "eslint . --ext .js,.jsx,.ts,.tsx -f codeframe --fix"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -20,8 +20,11 @@
|
||||
},
|
||||
"homepage": "https://github.com/blockstack/blockstack-core-sidecar#readme",
|
||||
"prettier": "@blockstack/prettier-config",
|
||||
"engines": {
|
||||
"node": ">=13"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": "^12",
|
||||
"@types/node": "^13.7.4",
|
||||
"big-integer": "^1.6.48",
|
||||
"smart-buffer": "^4.1.0",
|
||||
"ts-node": "^8.6.2",
|
||||
@@ -34,6 +37,7 @@
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-prettier": "^6.10.0",
|
||||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"eslint-plugin-tsdoc": "^0.2.1",
|
||||
"prettier": "1.19.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Readable } from 'stream';
|
||||
import { SmartBuffer } from 'smart-buffer';
|
||||
import { isEnum } from './helpers';
|
||||
|
||||
class BufferReader extends SmartBuffer {
|
||||
readBigUIntLE(length: number): bigint {
|
||||
@@ -8,6 +9,7 @@ class BufferReader extends SmartBuffer {
|
||||
const num = BigInt(`0x${hex}`);
|
||||
return num;
|
||||
}
|
||||
|
||||
readBigUIntBE(length: number): bigint {
|
||||
const buffer = this.readBuffer(length);
|
||||
const hex = buffer.toString('hex');
|
||||
@@ -17,21 +19,23 @@ class BufferReader extends SmartBuffer {
|
||||
}
|
||||
|
||||
export class BinaryReader {
|
||||
readonly stream: Readable;
|
||||
readonly readableStream: Readable;
|
||||
|
||||
constructor(stream: Readable) {
|
||||
this.stream = stream;
|
||||
constructor(readableStream: Readable) {
|
||||
this.readableStream = readableStream;
|
||||
}
|
||||
|
||||
private readExact(length: number, callback: (error: Error | null, data?: Buffer) => void): void {
|
||||
const chunk: Buffer = this.stream.read(length);
|
||||
// TODO: debug logging for this during async perf reading testing..
|
||||
// console.info(`___INFO: ${(this.readableStream as any).readableFlowing}`);
|
||||
const chunk: Buffer = this.readableStream.read(length);
|
||||
if (chunk !== null) {
|
||||
if (chunk.length !== length) {
|
||||
callback(new Error(`Unexpected chunk length, expected '${length}', received '${chunk.length}'`));
|
||||
}
|
||||
callback(null, chunk);
|
||||
} else {
|
||||
this.stream.once('readable', () => {
|
||||
this.readableStream.once('readable', () => {
|
||||
this.readExact(length, callback);
|
||||
});
|
||||
}
|
||||
@@ -53,6 +57,18 @@ export class BinaryReader {
|
||||
return this.readBuffer(1).then(buffer => buffer[0]);
|
||||
}
|
||||
|
||||
async readUInt8Enum<T extends string, TEnumValue extends number>(
|
||||
enumVariable: { [key in T]: TEnumValue },
|
||||
invalidEnumErrorFormatter: (val: number) => Error
|
||||
): Promise<TEnumValue> {
|
||||
const num = await this.readUInt8();
|
||||
if (isEnum(enumVariable, num)) {
|
||||
return num;
|
||||
} else {
|
||||
throw invalidEnumErrorFormatter(num);
|
||||
}
|
||||
}
|
||||
|
||||
readUInt16BE(): Promise<number> {
|
||||
return this.readBuffer(2).then(buffer => buffer.readUInt16BE(0));
|
||||
}
|
||||
@@ -69,7 +85,13 @@ export class BinaryReader {
|
||||
return this.readBuffer(32);
|
||||
}
|
||||
|
||||
sync(length: number): Promise<BufferReader> {
|
||||
/**
|
||||
* Read a fixed amount of bytes into a buffer which provides much faster synchronous
|
||||
* buffer read operations. This should always be used for reading a span of fixed-length
|
||||
* fields.
|
||||
* @param length - Byte count to read into a buffer.
|
||||
*/
|
||||
readFixed(length: number): Promise<BufferReader> {
|
||||
return this.readBuffer(length).then(buffer => new BufferReader({ buff: buffer }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export interface BlockHeader {
|
||||
}
|
||||
|
||||
export async function readBlockHeader(stream: BinaryReader): Promise<BlockHeader> {
|
||||
const cursor = await stream.sync(blockHeaderSize);
|
||||
const cursor = await stream.readFixed(blockHeaderSize);
|
||||
const header: BlockHeader = {
|
||||
version: cursor.readUInt8(),
|
||||
workScore: {
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
import { BinaryReader } from './binaryReader';
|
||||
import { readBlockHeader, BlockHeader } from './blockHeaderReader';
|
||||
import { readTransactions, Transaction } from './txReader';
|
||||
import { StacksMessageTypeID } from './stacks-p2p';
|
||||
|
||||
export interface Block {
|
||||
header: BlockHeader;
|
||||
transactions: Transaction[];
|
||||
}
|
||||
|
||||
export interface StacksMessageBlocks {
|
||||
messageTypeId: StacksMessageTypeID.Blocks;
|
||||
blocks: Block[];
|
||||
}
|
||||
|
||||
export async function readBlocks(stream: BinaryReader): Promise<Block[]> {
|
||||
const blockCount = await stream.readUInt32BE();
|
||||
const blocks = new Array<Block>(blockCount);
|
||||
|
||||
17
src/errors.ts
Normal file
17
src/errors.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export class StacksMessageParsingError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.message = message;
|
||||
this.name = this.constructor.name;
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
}
|
||||
|
||||
export class NotImplementedError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.message = message;
|
||||
this.name = this.constructor.name;
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
}
|
||||
65
src/helpers.ts
Normal file
65
src/helpers.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
function createEnumChecker<T extends string, TEnumValue extends number>(
|
||||
enumVariable: { [key in T]: TEnumValue }
|
||||
): (value: number) => value is TEnumValue {
|
||||
// Create a set of valid enum number values.
|
||||
const enumValues = Object.values<number>(enumVariable).filter(v => typeof v === 'number');
|
||||
const enumValueSet = new Set<number>(enumValues);
|
||||
return (value: number): value is TEnumValue => enumValueSet.has(value);
|
||||
}
|
||||
|
||||
const enumCheckFunctions = new Map<object, (value: number) => boolean>();
|
||||
|
||||
/**
|
||||
* Type guard to check if a given value is a valid enum value.
|
||||
* @param enumVariable - Literal `enum` type.
|
||||
* @param value - A value to check against the enum's values.
|
||||
* @example
|
||||
* ```ts
|
||||
* enum Color {
|
||||
* Purple = 3,
|
||||
* Orange = 5
|
||||
* }
|
||||
* const val: number = 3;
|
||||
* if (isEnum(Color, val)) {
|
||||
* // `val` is known as enum type `Color`, e.g.:
|
||||
* const colorVal: Color = val;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function isEnum<T extends string, TEnumValue extends number>(
|
||||
enumVariable: { [key in T]: TEnumValue },
|
||||
value: number
|
||||
): value is TEnumValue {
|
||||
const checker = enumCheckFunctions.get(enumVariable);
|
||||
if (checker !== undefined) {
|
||||
return checker(value);
|
||||
}
|
||||
const newChecker = createEnumChecker(enumVariable);
|
||||
enumCheckFunctions.set(enumVariable, newChecker);
|
||||
return isEnum(enumVariable, value);
|
||||
}
|
||||
|
||||
const enumMaps = new Map<object, Map<unknown, unknown>>();
|
||||
|
||||
export function getEnumDescription<T extends string, TEnumValue extends number>(
|
||||
enumVariable: { [key in T]: TEnumValue },
|
||||
value: number
|
||||
): string {
|
||||
const enumMap = enumMaps.get(enumVariable);
|
||||
if (enumMap !== undefined) {
|
||||
const enumKey = enumMap.get(value);
|
||||
if (enumKey !== undefined) {
|
||||
return `${value} '${enumKey}'`;
|
||||
} else {
|
||||
return `${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a map of `[enumValue: number]: enumNameString`
|
||||
const enumValues = Object.entries(enumVariable)
|
||||
.filter(([, v]) => typeof v === 'number')
|
||||
.map<[number, string]>(([k, v]) => [v as number, k]);
|
||||
const newEnumMap = new Map(enumValues);
|
||||
enumMaps.set(enumVariable, newEnumMap);
|
||||
return getEnumDescription(enumVariable, value);
|
||||
}
|
||||
25
src/index.ts
25
src/index.ts
@@ -11,19 +11,30 @@ async function readSocket(socket: net.Socket): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
const server = net.createServer(c => {
|
||||
// 'connection' listener.
|
||||
const server = net.createServer(clientSocket => {
|
||||
console.log('client connected');
|
||||
readSocket(c);
|
||||
c.on('end', () => {
|
||||
readSocket(clientSocket).catch(error => {
|
||||
console.error(`error reading messages from socket: ${error}`);
|
||||
console.error(error);
|
||||
clientSocket.destroy();
|
||||
server.close();
|
||||
});
|
||||
clientSocket.on('end', () => {
|
||||
console.log('client disconnected');
|
||||
});
|
||||
// c.write('hello\r\n');
|
||||
// c.pipe(c);
|
||||
});
|
||||
|
||||
server.on('error', err => {
|
||||
console.error('socket server error:');
|
||||
console.error(err);
|
||||
throw err;
|
||||
});
|
||||
|
||||
server.listen(3700, () => {
|
||||
console.log('server bound');
|
||||
const addr = server.address();
|
||||
if (addr === null) {
|
||||
throw new Error('server missing address');
|
||||
}
|
||||
const addrStr = typeof addr === 'string' ? addr : `${addr.address}:${addr.port}`;
|
||||
console.log(`server listening at ${addrStr}`);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { BinaryReader } from './binaryReader';
|
||||
import { readBlocks, Block, StacksMessageBlocks } from './blockReader';
|
||||
import { readBlocks, Block } from './blockReader';
|
||||
import { Transaction, readTransaction } from './txReader';
|
||||
import { StacksMessageParsingError, NotImplementedError } from './errors';
|
||||
import { getEnumDescription } from './helpers';
|
||||
|
||||
export enum StacksMessageTypeID {
|
||||
Handshake = 0,
|
||||
@@ -20,11 +23,31 @@ export enum StacksMessageTypeID {
|
||||
Reserved = 255,
|
||||
}
|
||||
|
||||
type StacksMessage = StacksMessageBlocks;
|
||||
export interface StacksMessageBlocks {
|
||||
messageTypeId: StacksMessageTypeID.Blocks;
|
||||
blocks: Block[];
|
||||
}
|
||||
|
||||
export function isStacksMessageBlocks(msg: StacksMessage): msg is StacksMessageBlocks {
|
||||
return msg.messageTypeId === StacksMessageTypeID.Blocks;
|
||||
}
|
||||
|
||||
export interface StacksMessageTransaction {
|
||||
messageTypeId: StacksMessageTypeID.Transaction;
|
||||
transaction: Transaction;
|
||||
}
|
||||
|
||||
export function isStacksMessageTransaction(msg: StacksMessage): msg is StacksMessageTransaction {
|
||||
return msg.messageTypeId === StacksMessageTypeID.Transaction;
|
||||
}
|
||||
|
||||
type StacksMessage = StacksMessageBlocks | StacksMessageTransaction;
|
||||
|
||||
export async function* readMessages(stream: BinaryReader): AsyncGenerator<StacksMessage> {
|
||||
while (!stream.stream.destroyed) {
|
||||
const messageTypeId: StacksMessageTypeID = await stream.readUInt8();
|
||||
while (!stream.readableStream.destroyed) {
|
||||
const messageTypeId = await stream.readUInt8Enum(StacksMessageTypeID, n => {
|
||||
throw new StacksMessageParsingError(`unexpected Stacks message type ID ${n}`);
|
||||
});
|
||||
if (messageTypeId === StacksMessageTypeID.Blocks) {
|
||||
const blocks = await readBlocks(stream);
|
||||
const msg: StacksMessageBlocks = {
|
||||
@@ -32,8 +55,15 @@ export async function* readMessages(stream: BinaryReader): AsyncGenerator<Stacks
|
||||
blocks: blocks,
|
||||
};
|
||||
yield msg;
|
||||
} else if (messageTypeId === StacksMessageTypeID.Transaction) {
|
||||
const tx = await readTransaction(stream);
|
||||
const msg: StacksMessageTransaction = {
|
||||
messageTypeId: StacksMessageTypeID.Transaction,
|
||||
transaction: tx,
|
||||
};
|
||||
yield msg;
|
||||
} else {
|
||||
throw new Error(`Not implemented - StacksMessageID ${messageTypeId}`);
|
||||
throw new NotImplementedError(`stacks message type: ${getEnumDescription(StacksMessageTypeID, messageTypeId)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
259
src/txReader.ts
259
src/txReader.ts
@@ -1,23 +1,25 @@
|
||||
import { Readable } from 'stream';
|
||||
import { BinaryReader } from './binaryReader';
|
||||
import { getEnumDescription } from './helpers';
|
||||
import { StacksMessageParsingError, NotImplementedError } from './errors';
|
||||
|
||||
const enum SingleSigHashMode {
|
||||
enum SigHashMode {
|
||||
/** SingleSigHashMode */
|
||||
P2PKH = 0x00,
|
||||
/** SingleSigHashMode */
|
||||
P2WPKH = 0x02,
|
||||
}
|
||||
|
||||
const enum MultiSigHashMode {
|
||||
/** MultiSigHashMode */
|
||||
P2SH = 0x01,
|
||||
/** MultiSigHashMode */
|
||||
P2WSH = 0x03,
|
||||
}
|
||||
|
||||
const enum TransactionPublicKeyEncoding {
|
||||
enum TransactionPublicKeyEncoding {
|
||||
Compressed = 0x00,
|
||||
Uncompressed = 0x01,
|
||||
}
|
||||
|
||||
interface TransactionSpendingConditionSingleSig {
|
||||
hashMode: SingleSigHashMode; // u8
|
||||
hashMode: SigHashMode.P2PKH | SigHashMode.P2WPKH; // u8
|
||||
signer: Buffer; // 20 bytes, HASH160
|
||||
nonce: bigint; // u64
|
||||
feeRate: bigint; // u64
|
||||
@@ -25,7 +27,7 @@ interface TransactionSpendingConditionSingleSig {
|
||||
signature: Buffer; // 65 bytes
|
||||
}
|
||||
|
||||
const enum TransactionAuthFieldID {
|
||||
enum TransactionAuthFieldTypeID {
|
||||
PublicKeyCompressed = 0x00,
|
||||
PublicKeyUncompressed = 0x01,
|
||||
SignatureCompressed = 0x02,
|
||||
@@ -33,26 +35,26 @@ const enum TransactionAuthFieldID {
|
||||
}
|
||||
|
||||
interface TransactionAuthFieldPublicKey {
|
||||
typeId: TransactionAuthFieldID.PublicKeyCompressed | TransactionAuthFieldID.PublicKeyUncompressed; // u8
|
||||
typeId: TransactionAuthFieldTypeID.PublicKeyCompressed | TransactionAuthFieldTypeID.PublicKeyUncompressed; // u8
|
||||
publicKey: Buffer; // 33 bytes
|
||||
}
|
||||
|
||||
interface TransactionAuthFieldSignature {
|
||||
typeId: TransactionAuthFieldID.SignatureCompressed | TransactionAuthFieldID.SignatureUncompressed; // u8
|
||||
typeId: TransactionAuthFieldTypeID.SignatureCompressed | TransactionAuthFieldTypeID.SignatureUncompressed; // u8
|
||||
signature: Buffer; // 65 bytes
|
||||
}
|
||||
|
||||
type TransactionAuthField = TransactionAuthFieldPublicKey | TransactionAuthFieldSignature;
|
||||
|
||||
interface TransactionSpendingConditionMultiSig {
|
||||
hashMode: MultiSigHashMode; // u8
|
||||
hashMode: SigHashMode.P2SH | SigHashMode.P2WSH; // u8
|
||||
signer: Buffer; // 20 bytes, HASH160
|
||||
nonce: bigint; // u64
|
||||
feeRate: bigint; // u64
|
||||
authFields: TransactionAuthField[];
|
||||
}
|
||||
|
||||
const enum TransactionAuthType {
|
||||
enum TransactionAuthTypeID {
|
||||
Standard = 0x04,
|
||||
Sponsored = 0x05,
|
||||
}
|
||||
@@ -60,49 +62,49 @@ const enum TransactionAuthType {
|
||||
type TransactionSpendingCondition = TransactionSpendingConditionSingleSig | TransactionSpendingConditionMultiSig;
|
||||
|
||||
interface TransactionAuthStandard {
|
||||
typeId: TransactionAuthType.Standard; // u8
|
||||
typeId: TransactionAuthTypeID.Standard; // u8
|
||||
originCondition: TransactionSpendingCondition;
|
||||
}
|
||||
|
||||
interface TransactionAuthSponsored {
|
||||
typeId: TransactionAuthType.Sponsored; // u8
|
||||
typeId: TransactionAuthTypeID.Sponsored; // u8
|
||||
originCondition: TransactionSpendingCondition;
|
||||
sponsorCondition: TransactionSpendingCondition;
|
||||
}
|
||||
|
||||
const enum TransactionPostConditionMode {
|
||||
enum TransactionPostConditionMode {
|
||||
Allow = 0x01,
|
||||
Deny = 0x02,
|
||||
}
|
||||
|
||||
const enum TransactionVersion {
|
||||
enum TransactionVersion {
|
||||
Mainnet = 0x00,
|
||||
Testnet = 0x80,
|
||||
}
|
||||
|
||||
const enum AssetInfoID {
|
||||
enum AssetInfoTypeID {
|
||||
STX = 0,
|
||||
FungibleAsset = 1,
|
||||
NonfungibleAsset = 2,
|
||||
}
|
||||
|
||||
const enum PostConditionPrincipalID {
|
||||
enum PostConditionPrincipalTypeID {
|
||||
Origin = 0x01,
|
||||
Standard = 0x02,
|
||||
Contract = 0x03,
|
||||
}
|
||||
|
||||
interface PostConditionPrincipalOrigin {
|
||||
typeId: PostConditionPrincipalID.Origin; // u8
|
||||
typeId: PostConditionPrincipalTypeID.Origin; // u8
|
||||
}
|
||||
|
||||
interface PostConditionPrincipalStandard {
|
||||
typeId: PostConditionPrincipalID.Standard; // u8
|
||||
typeId: PostConditionPrincipalTypeID.Standard; // u8
|
||||
address: StacksAddress;
|
||||
}
|
||||
|
||||
interface PostConditionPrincipalContract {
|
||||
typeId: PostConditionPrincipalID.Contract; // u8
|
||||
typeId: PostConditionPrincipalTypeID.Contract; // u8
|
||||
address: StacksAddress;
|
||||
contractName: string;
|
||||
}
|
||||
@@ -112,7 +114,7 @@ type PostConditionPrincipal =
|
||||
| PostConditionPrincipalStandard
|
||||
| PostConditionPrincipalContract;
|
||||
|
||||
const enum FungibleConditionCode {
|
||||
enum FungibleConditionCode {
|
||||
SentEq = 0x01,
|
||||
SentGt = 0x02,
|
||||
SentGe = 0x03,
|
||||
@@ -120,7 +122,7 @@ const enum FungibleConditionCode {
|
||||
SentLe = 0x05,
|
||||
}
|
||||
|
||||
const enum NonfungibleConditionCode {
|
||||
enum NonfungibleConditionCode {
|
||||
Sent = 0x10,
|
||||
NotSent = 0x11,
|
||||
}
|
||||
@@ -137,14 +139,14 @@ interface AssetInfo {
|
||||
}
|
||||
|
||||
interface TransactionPostConditionStx {
|
||||
assetInfoId: AssetInfoID.STX; // u8
|
||||
assetInfoId: AssetInfoTypeID.STX; // u8
|
||||
principal: PostConditionPrincipal;
|
||||
conditionCode: FungibleConditionCode; // u8
|
||||
amount: bigint; // u64
|
||||
}
|
||||
|
||||
interface TransactionPostConditionFungible {
|
||||
assetInfoId: AssetInfoID.FungibleAsset; // u8
|
||||
assetInfoId: AssetInfoTypeID.FungibleAsset; // u8
|
||||
principal: PostConditionPrincipal;
|
||||
asset: AssetInfo;
|
||||
conditionCode: FungibleConditionCode; // u8
|
||||
@@ -153,18 +155,23 @@ interface TransactionPostConditionFungible {
|
||||
|
||||
// TODO: incomplete
|
||||
interface TransactionPostConditionNonfungible {
|
||||
assetInfoId: AssetInfoID.NonfungibleAsset; // u8
|
||||
assetInfoId: AssetInfoTypeID.NonfungibleAsset; // u8
|
||||
asset: AssetInfo;
|
||||
assetValue: any; // TODO: Value (Clarity value)
|
||||
assetValue: ClarityValue;
|
||||
conditionCode: NonfungibleConditionCode; // u8
|
||||
}
|
||||
|
||||
// TODO: placeholder, needs clarity-js / stacks-transactions-js
|
||||
interface ClarityValue {
|
||||
value: Buffer; // wrong
|
||||
}
|
||||
|
||||
type TransactionPostCondition =
|
||||
| TransactionPostConditionStx
|
||||
| TransactionPostConditionFungible
|
||||
| TransactionPostConditionNonfungible;
|
||||
|
||||
const enum TransactionPayloadID {
|
||||
enum TransactionPayloadTypeID {
|
||||
TokenTransfer = 0,
|
||||
SmartContract = 1,
|
||||
ContractCall = 2,
|
||||
@@ -173,30 +180,30 @@ const enum TransactionPayloadID {
|
||||
}
|
||||
|
||||
interface TransactionPayloadTokenTransfer {
|
||||
typeId: TransactionPayloadID.TokenTransfer;
|
||||
typeId: TransactionPayloadTypeID.TokenTransfer;
|
||||
address: StacksAddress;
|
||||
amount: bigint; // u64
|
||||
memo: Buffer; // 34 bytes
|
||||
}
|
||||
|
||||
interface TransactionPayloadCoinbase {
|
||||
typeId: TransactionPayloadID.Coinbase;
|
||||
typeId: TransactionPayloadTypeID.Coinbase;
|
||||
payload: Buffer; // 32 bytes
|
||||
}
|
||||
|
||||
// TODO: incomplete
|
||||
interface TransactionPayloadContractCall {
|
||||
typeId: TransactionPayloadID.ContractCall;
|
||||
typeId: TransactionPayloadTypeID.ContractCall;
|
||||
}
|
||||
|
||||
// TODO: incomplete
|
||||
interface TransactionPayloadSmartContract {
|
||||
typeId: TransactionPayloadID.SmartContract;
|
||||
typeId: TransactionPayloadTypeID.SmartContract;
|
||||
}
|
||||
|
||||
// TODO: incomplete
|
||||
interface TransactionPayloadPoisonMicroblock {
|
||||
typeId: TransactionPayloadID.PoisonMicroblock;
|
||||
typeId: TransactionPayloadTypeID.PoisonMicroblock;
|
||||
}
|
||||
|
||||
type TransactionPayload =
|
||||
@@ -216,76 +223,82 @@ export interface Transaction {
|
||||
payload: TransactionPayload;
|
||||
}
|
||||
|
||||
export async function readTransaction(stream: BinaryReader): Promise<Transaction> {
|
||||
const version = await stream.readUInt8Enum(TransactionVersion, n => {
|
||||
throw new StacksMessageParsingError(`unexpected transactions version: ${n}`);
|
||||
});
|
||||
const chainId = await stream.readUInt32BE();
|
||||
const authType = await stream.readUInt8Enum(TransactionAuthTypeID, n => {
|
||||
throw new StacksMessageParsingError(`unexpected transaction auth type: ${n}`);
|
||||
});
|
||||
|
||||
let auth: TransactionAuthStandard | TransactionAuthSponsored;
|
||||
if (authType === TransactionAuthTypeID.Standard) {
|
||||
const originCondition = await readTransactionSpendingCondition(stream);
|
||||
const txAuth: TransactionAuthStandard = {
|
||||
typeId: authType,
|
||||
originCondition: originCondition,
|
||||
};
|
||||
auth = txAuth;
|
||||
} else if (authType === TransactionAuthTypeID.Sponsored) {
|
||||
const originCondition = await readTransactionSpendingCondition(stream);
|
||||
const sponsorCondition = await readTransactionSpendingCondition(stream);
|
||||
const txAuth: TransactionAuthSponsored = {
|
||||
typeId: authType,
|
||||
originCondition: originCondition,
|
||||
sponsorCondition: sponsorCondition,
|
||||
};
|
||||
auth = txAuth;
|
||||
} else {
|
||||
throw new NotImplementedError(`tx auth type: ${getEnumDescription(TransactionAuthTypeID, authType)}`);
|
||||
}
|
||||
|
||||
const anchorMode = await stream.readUInt8Enum(TransactionPostConditionMode, n => {
|
||||
throw new StacksMessageParsingError(`unexpected tx post condition anchor mode: ${n}`);
|
||||
});
|
||||
|
||||
const postConditionMode = await stream.readUInt8Enum(TransactionPostConditionMode, n => {
|
||||
throw new StacksMessageParsingError(`unexpected tx post condition mode: ${n}`);
|
||||
});
|
||||
|
||||
const postConditions = await readTransactionPostConditions(stream);
|
||||
|
||||
const txPayload = await readTransactionPayload(stream);
|
||||
|
||||
const tx: Transaction = {
|
||||
version: version,
|
||||
chainId: chainId,
|
||||
auth: auth,
|
||||
anchorMode: anchorMode,
|
||||
postConditionMode: postConditionMode,
|
||||
postConditions: postConditions,
|
||||
payload: txPayload,
|
||||
};
|
||||
return tx;
|
||||
}
|
||||
|
||||
export async function readTransactions(stream: BinaryReader): Promise<Transaction[]> {
|
||||
const txCount = await stream.readUInt32BE();
|
||||
const txs = new Array<Transaction>(txCount);
|
||||
for (let i = 0; i < txCount; i++) {
|
||||
const version = await stream.readUInt8();
|
||||
const chainId = await stream.readUInt32BE();
|
||||
const authType: TransactionAuthType = await stream.readUInt8();
|
||||
|
||||
let auth: TransactionAuthStandard | TransactionAuthSponsored;
|
||||
if (authType === TransactionAuthType.Standard) {
|
||||
const originCondition = await readTransactionSpendingCondition(stream);
|
||||
const txAuth: TransactionAuthStandard = {
|
||||
typeId: authType,
|
||||
originCondition: originCondition,
|
||||
};
|
||||
auth = txAuth;
|
||||
} else if (authType === TransactionAuthType.Sponsored) {
|
||||
const originCondition = await readTransactionSpendingCondition(stream);
|
||||
const sponsorCondition = await readTransactionSpendingCondition(stream);
|
||||
const txAuth: TransactionAuthSponsored = {
|
||||
typeId: authType,
|
||||
originCondition: originCondition,
|
||||
sponsorCondition: sponsorCondition,
|
||||
};
|
||||
auth = txAuth;
|
||||
} else {
|
||||
throw new Error(`Unexpected tx auth type: ${authType}`);
|
||||
}
|
||||
|
||||
const anchorMode: TransactionPostConditionMode = await stream.readUInt8();
|
||||
if (anchorMode !== TransactionPostConditionMode.Allow && anchorMode !== TransactionPostConditionMode.Deny) {
|
||||
throw new Error(`Unexpected tx post condition anchor mode: ${anchorMode}`);
|
||||
}
|
||||
|
||||
const postConditionMode: TransactionPostConditionMode = await stream.readUInt8();
|
||||
if (
|
||||
postConditionMode !== TransactionPostConditionMode.Allow &&
|
||||
postConditionMode !== TransactionPostConditionMode.Deny
|
||||
) {
|
||||
throw new Error(`Unexpected tx post condition mode: ${postConditionMode}`);
|
||||
}
|
||||
|
||||
const postConditions = await readTransactionPostConditions(stream);
|
||||
|
||||
const txPayload = await readTransactionPayload(stream);
|
||||
|
||||
const tx: Transaction = {
|
||||
version: version,
|
||||
chainId: chainId,
|
||||
auth: auth,
|
||||
anchorMode: anchorMode,
|
||||
postConditionMode: postConditionMode,
|
||||
postConditions: postConditions,
|
||||
payload: txPayload,
|
||||
};
|
||||
const tx = await readTransaction(stream);
|
||||
txs[i] = tx;
|
||||
}
|
||||
return txs;
|
||||
}
|
||||
|
||||
async function readTransactionPayload(stream: BinaryReader): Promise<TransactionPayload> {
|
||||
const txPayloadType: TransactionPayloadID = await stream.readUInt8();
|
||||
if (txPayloadType === TransactionPayloadID.Coinbase) {
|
||||
const txPayloadType = await stream.readUInt8Enum(TransactionPayloadTypeID, n => {
|
||||
throw new StacksMessageParsingError(`unexpected tx payload type: ${n}`);
|
||||
});
|
||||
if (txPayloadType === TransactionPayloadTypeID.Coinbase) {
|
||||
const payload: TransactionPayloadCoinbase = {
|
||||
typeId: txPayloadType,
|
||||
payload: await stream.readBuffer(32),
|
||||
};
|
||||
return payload;
|
||||
} else if (txPayloadType === TransactionPayloadID.TokenTransfer) {
|
||||
const cursor = await stream.sync(63);
|
||||
} else if (txPayloadType === TransactionPayloadTypeID.TokenTransfer) {
|
||||
const cursor = await stream.readFixed(63);
|
||||
const payload: TransactionPayloadTokenTransfer = {
|
||||
typeId: txPayloadType,
|
||||
address: {
|
||||
@@ -296,14 +309,8 @@ async function readTransactionPayload(stream: BinaryReader): Promise<Transaction
|
||||
memo: cursor.readBuffer(34),
|
||||
};
|
||||
return payload;
|
||||
} else if (txPayloadType === TransactionPayloadID.PoisonMicroblock) {
|
||||
throw new Error('not yet implemented');
|
||||
} else if (txPayloadType === TransactionPayloadID.SmartContract) {
|
||||
throw new Error('not yet implemented');
|
||||
} else if (txPayloadType === TransactionPayloadID.ContractCall) {
|
||||
throw new Error('not yet implemented');
|
||||
} else {
|
||||
throw new Error(`Unexpected tx payload type: ${txPayloadType}`);
|
||||
throw new NotImplementedError(`tx payload type: ${getEnumDescription(TransactionPayloadTypeID, txPayloadType)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,10 +318,12 @@ async function readTransactionPostConditions(stream: BinaryReader): Promise<Tran
|
||||
const conditionCount = await stream.readUInt32BE();
|
||||
const conditions = new Array<TransactionPostCondition>(conditionCount);
|
||||
for (let i = 0; i < conditionCount; i++) {
|
||||
const typeId: AssetInfoID = await stream.readUInt8();
|
||||
if (typeId === AssetInfoID.STX) {
|
||||
const typeId = await stream.readUInt8Enum(AssetInfoTypeID, n => {
|
||||
throw new StacksMessageParsingError(`unexpected tx asset info type: ${n}`);
|
||||
});
|
||||
if (typeId === AssetInfoTypeID.STX) {
|
||||
const principal = await readTransactionPostConditionPrincipal(stream);
|
||||
const cursor = await stream.sync(9);
|
||||
const cursor = await stream.readFixed(9);
|
||||
const conditionCode: FungibleConditionCode = cursor.readUInt8();
|
||||
const condition: TransactionPostConditionStx = {
|
||||
assetInfoId: typeId,
|
||||
@@ -323,26 +332,24 @@ async function readTransactionPostConditions(stream: BinaryReader): Promise<Tran
|
||||
amount: cursor.readBigInt64BE(),
|
||||
};
|
||||
conditions[i] = condition;
|
||||
} else if (typeId === AssetInfoID.FungibleAsset) {
|
||||
throw new Error('not yet implemented');
|
||||
} else if (typeId === AssetInfoID.NonfungibleAsset) {
|
||||
throw new Error('not yet implemented');
|
||||
} else {
|
||||
throw new Error(`Unexpected tx type ID: ${typeId}`);
|
||||
throw new NotImplementedError(`tx asset info type ${getEnumDescription(AssetInfoTypeID, typeId)}`);
|
||||
}
|
||||
}
|
||||
return conditions;
|
||||
}
|
||||
|
||||
async function readTransactionPostConditionPrincipal(stream: BinaryReader): Promise<PostConditionPrincipal> {
|
||||
const typeId: PostConditionPrincipalID = await stream.readUInt8();
|
||||
if (typeId === PostConditionPrincipalID.Origin) {
|
||||
const typeId = await stream.readUInt8Enum(PostConditionPrincipalTypeID, n => {
|
||||
throw new StacksMessageParsingError(`unexpected tx post condition principal type: ${n}`);
|
||||
});
|
||||
if (typeId === PostConditionPrincipalTypeID.Origin) {
|
||||
const principal: PostConditionPrincipalOrigin = {
|
||||
typeId: typeId,
|
||||
};
|
||||
return principal;
|
||||
} else if (typeId === PostConditionPrincipalID.Standard) {
|
||||
const cursor = await stream.sync(21);
|
||||
} else if (typeId === PostConditionPrincipalTypeID.Standard) {
|
||||
const cursor = await stream.readFixed(21);
|
||||
const principal: PostConditionPrincipalStandard = {
|
||||
typeId: typeId,
|
||||
address: {
|
||||
@@ -351,8 +358,8 @@ async function readTransactionPostConditionPrincipal(stream: BinaryReader): Prom
|
||||
},
|
||||
};
|
||||
return principal;
|
||||
} else if (typeId === PostConditionPrincipalID.Contract) {
|
||||
const cursor = await stream.sync(22);
|
||||
} else if (typeId === PostConditionPrincipalTypeID.Contract) {
|
||||
const cursor = await stream.readFixed(22);
|
||||
const address: StacksAddress = {
|
||||
version: cursor.readUInt8(),
|
||||
bytes: cursor.readBuffer(20),
|
||||
@@ -366,14 +373,18 @@ async function readTransactionPostConditionPrincipal(stream: BinaryReader): Prom
|
||||
};
|
||||
return principal;
|
||||
} else {
|
||||
throw new Error(`Unexpected tx post condition principal type ID: ${typeId}`);
|
||||
throw new NotImplementedError(
|
||||
`tx post condition principal type: ${getEnumDescription(PostConditionPrincipalTypeID, typeId)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function readTransactionSpendingCondition(stream: BinaryReader): Promise<TransactionSpendingCondition> {
|
||||
const conditionType = await stream.readUInt8();
|
||||
if (conditionType === SingleSigHashMode.P2PKH || conditionType === SingleSigHashMode.P2WPKH) {
|
||||
const cursor = await stream.sync(102);
|
||||
const conditionType = await stream.readUInt8Enum(SigHashMode, n => {
|
||||
throw new StacksMessageParsingError(`unexpected tx spend condition hash mode: ${n}`);
|
||||
});
|
||||
if (conditionType === SigHashMode.P2PKH || conditionType === SigHashMode.P2WPKH) {
|
||||
const cursor = await stream.readFixed(102);
|
||||
const condition: TransactionSpendingConditionSingleSig = {
|
||||
hashMode: conditionType,
|
||||
signer: cursor.readBuffer(20),
|
||||
@@ -383,8 +394,8 @@ async function readTransactionSpendingCondition(stream: BinaryReader): Promise<T
|
||||
signature: cursor.readBuffer(65),
|
||||
};
|
||||
return condition;
|
||||
} else if (conditionType === MultiSigHashMode.P2SH || conditionType === MultiSigHashMode.P2WSH) {
|
||||
const cursor = await stream.sync(40);
|
||||
} else if (conditionType === SigHashMode.P2SH || conditionType === SigHashMode.P2WSH) {
|
||||
const cursor = await stream.readFixed(40);
|
||||
const condition: TransactionSpendingConditionMultiSig = {
|
||||
hashMode: conditionType,
|
||||
signer: cursor.readBuffer(20),
|
||||
@@ -393,10 +404,12 @@ async function readTransactionSpendingCondition(stream: BinaryReader): Promise<T
|
||||
authFields: new Array<TransactionAuthField>(cursor.readUInt32BE()),
|
||||
};
|
||||
for (let i = 0; i < condition.authFields.length; i++) {
|
||||
const authType: TransactionAuthFieldID = await stream.readUInt8();
|
||||
const authType = await stream.readUInt8Enum(TransactionAuthFieldTypeID, n => {
|
||||
throw new StacksMessageParsingError(`unexpected tx auth field type: ${n}`);
|
||||
});
|
||||
if (
|
||||
authType === TransactionAuthFieldID.PublicKeyCompressed ||
|
||||
authType === TransactionAuthFieldID.PublicKeyUncompressed
|
||||
authType === TransactionAuthFieldTypeID.PublicKeyCompressed ||
|
||||
authType === TransactionAuthFieldTypeID.PublicKeyUncompressed
|
||||
) {
|
||||
const authFieldPubkey: TransactionAuthFieldPublicKey = {
|
||||
typeId: authType,
|
||||
@@ -404,8 +417,8 @@ async function readTransactionSpendingCondition(stream: BinaryReader): Promise<T
|
||||
};
|
||||
condition.authFields[i] = authFieldPubkey;
|
||||
} else if (
|
||||
authType === TransactionAuthFieldID.SignatureCompressed ||
|
||||
authType === TransactionAuthFieldID.SignatureUncompressed
|
||||
authType === TransactionAuthFieldTypeID.SignatureCompressed ||
|
||||
authType === TransactionAuthFieldTypeID.SignatureUncompressed
|
||||
) {
|
||||
const authFieldSig: TransactionAuthFieldSignature = {
|
||||
typeId: authType,
|
||||
@@ -413,11 +426,13 @@ async function readTransactionSpendingCondition(stream: BinaryReader): Promise<T
|
||||
};
|
||||
condition.authFields[i] = authFieldSig;
|
||||
} else {
|
||||
throw new Error(`Unexpected tx auth field ID type: ${authType}`);
|
||||
throw new NotImplementedError(
|
||||
`tx auth field type: ${getEnumDescription(TransactionAuthFieldTypeID, authType)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
return condition;
|
||||
} else {
|
||||
throw new Error(`Unexpected tx spend condition hash mode: ${conditionType}`);
|
||||
throw new NotImplementedError(`tx spend condition hash mode: ${getEnumDescription(SigHashMode, conditionType)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
"compilerOptions": {
|
||||
"target": "es2019",
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": false,
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
"outDir": "lib",
|
||||
"sourceMap": true,
|
||||
"sourceRoot": "."
|
||||
"sourceRoot": ".",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": false,
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
|
||||
Reference in New Issue
Block a user