Add BufferReader to Transaction and fix tests
This commit is contained in:
parent
91e6c8abc3
commit
06674b19fe
5 changed files with 40 additions and 113 deletions
|
@ -1,6 +1,5 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
Object.defineProperty(exports, '__esModule', { value: true });
|
Object.defineProperty(exports, '__esModule', { value: true });
|
||||||
const bufferutils = require('./bufferutils');
|
|
||||||
const bufferutils_1 = require('./bufferutils');
|
const bufferutils_1 = require('./bufferutils');
|
||||||
const bcrypto = require('./crypto');
|
const bcrypto = require('./crypto');
|
||||||
const bscript = require('./script');
|
const bscript = require('./script');
|
||||||
|
@ -47,80 +46,48 @@ class Transaction {
|
||||||
this.outs = [];
|
this.outs = [];
|
||||||
}
|
}
|
||||||
static fromBuffer(buffer, _NO_STRICT) {
|
static fromBuffer(buffer, _NO_STRICT) {
|
||||||
let offset = 0;
|
const bufferReader = new bufferutils_1.BufferReader(buffer);
|
||||||
function readSlice(n) {
|
|
||||||
offset += n;
|
|
||||||
return buffer.slice(offset - n, offset);
|
|
||||||
}
|
|
||||||
function readUInt32() {
|
|
||||||
const i = buffer.readUInt32LE(offset);
|
|
||||||
offset += 4;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
function readInt32() {
|
|
||||||
const i = buffer.readInt32LE(offset);
|
|
||||||
offset += 4;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
function readUInt64() {
|
|
||||||
const i = bufferutils.readUInt64LE(buffer, offset);
|
|
||||||
offset += 8;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
function readVarInt() {
|
|
||||||
const vi = varuint.decode(buffer, offset);
|
|
||||||
offset += varuint.decode.bytes;
|
|
||||||
return vi;
|
|
||||||
}
|
|
||||||
function readVarSlice() {
|
|
||||||
return readSlice(readVarInt());
|
|
||||||
}
|
|
||||||
function readVector() {
|
|
||||||
const count = readVarInt();
|
|
||||||
const vector = [];
|
|
||||||
for (let i = 0; i < count; i++) vector.push(readVarSlice());
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
const tx = new Transaction();
|
const tx = new Transaction();
|
||||||
tx.version = readInt32();
|
tx.version = bufferReader.readInt32();
|
||||||
const marker = buffer.readUInt8(offset);
|
const marker = bufferReader.readUInt8();
|
||||||
const flag = buffer.readUInt8(offset + 1);
|
const flag = bufferReader.readUInt8();
|
||||||
let hasWitnesses = false;
|
let hasWitnesses = false;
|
||||||
if (
|
if (
|
||||||
marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
|
marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
|
||||||
flag === Transaction.ADVANCED_TRANSACTION_FLAG
|
flag === Transaction.ADVANCED_TRANSACTION_FLAG
|
||||||
) {
|
) {
|
||||||
offset += 2;
|
|
||||||
hasWitnesses = true;
|
hasWitnesses = true;
|
||||||
|
} else {
|
||||||
|
bufferReader.offset -= 2;
|
||||||
}
|
}
|
||||||
const vinLen = readVarInt();
|
const vinLen = bufferReader.readVarInt();
|
||||||
for (let i = 0; i < vinLen; ++i) {
|
for (let i = 0; i < vinLen; ++i) {
|
||||||
tx.ins.push({
|
tx.ins.push({
|
||||||
hash: readSlice(32),
|
hash: bufferReader.readSlice(32),
|
||||||
index: readUInt32(),
|
index: bufferReader.readUInt32(),
|
||||||
script: readVarSlice(),
|
script: bufferReader.readVarSlice(),
|
||||||
sequence: readUInt32(),
|
sequence: bufferReader.readUInt32(),
|
||||||
witness: EMPTY_WITNESS,
|
witness: EMPTY_WITNESS,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const voutLen = readVarInt();
|
const voutLen = bufferReader.readVarInt();
|
||||||
for (let i = 0; i < voutLen; ++i) {
|
for (let i = 0; i < voutLen; ++i) {
|
||||||
tx.outs.push({
|
tx.outs.push({
|
||||||
value: readUInt64(),
|
value: bufferReader.readUInt64(),
|
||||||
script: readVarSlice(),
|
script: bufferReader.readVarSlice(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (hasWitnesses) {
|
if (hasWitnesses) {
|
||||||
for (let i = 0; i < vinLen; ++i) {
|
for (let i = 0; i < vinLen; ++i) {
|
||||||
tx.ins[i].witness = readVector();
|
tx.ins[i].witness = bufferReader.readVector();
|
||||||
}
|
}
|
||||||
// was this pointless?
|
// was this pointless?
|
||||||
if (!tx.hasWitnesses())
|
if (!tx.hasWitnesses())
|
||||||
throw new Error('Transaction has superfluous witness data');
|
throw new Error('Transaction has superfluous witness data');
|
||||||
}
|
}
|
||||||
tx.locktime = readUInt32();
|
tx.locktime = bufferReader.readUInt32();
|
||||||
if (_NO_STRICT) return tx;
|
if (_NO_STRICT) return tx;
|
||||||
if (offset !== buffer.length)
|
if (bufferReader.offset !== buffer.length)
|
||||||
throw new Error('Transaction has unexpected data');
|
throw new Error('Transaction has unexpected data');
|
||||||
return tx;
|
return tx;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import { describe, it } from 'mocha';
|
import { describe, it } from 'mocha';
|
||||||
import { BufferWriter } from '../src/buffer_writer';
|
import { BufferWriter } from '../src/bufferutils';
|
||||||
|
|
||||||
const varuint = require('varuint-bitcoin');
|
const varuint = require('varuint-bitcoin');
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ export function reverseBuffer(buffer: Buffer): Buffer {
|
||||||
* Helper class for serialization of bitcoin data types into a pre-allocated buffer.
|
* Helper class for serialization of bitcoin data types into a pre-allocated buffer.
|
||||||
*/
|
*/
|
||||||
export class BufferWriter {
|
export class BufferWriter {
|
||||||
constructor(private buffer: Buffer, public offset: number = 0) {
|
constructor(public buffer: Buffer, public offset: number = 0) {
|
||||||
typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]);
|
typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ export class BufferWriter {
|
||||||
* Helper class for serialization of bitcoin data types into a pre-allocated buffer.
|
* Helper class for serialization of bitcoin data types into a pre-allocated buffer.
|
||||||
*/
|
*/
|
||||||
export class BufferReader {
|
export class BufferReader {
|
||||||
constructor(private buffer: Buffer, public offset: number = 0) {
|
constructor(public buffer: Buffer, public offset: number = 0) {
|
||||||
typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]);
|
typeforce(types.tuple(types.Buffer, types.UInt32), [buffer, offset]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import * as bufferutils from './bufferutils';
|
import { BufferReader, BufferWriter, reverseBuffer } from './bufferutils';
|
||||||
import { BufferWriter, reverseBuffer } from './bufferutils';
|
|
||||||
import * as bcrypto from './crypto';
|
import * as bcrypto from './crypto';
|
||||||
import * as bscript from './script';
|
import * as bscript from './script';
|
||||||
import { OPS as opcodes } from './script';
|
import { OPS as opcodes } from './script';
|
||||||
|
@ -68,85 +67,46 @@ export class Transaction {
|
||||||
static readonly ADVANCED_TRANSACTION_FLAG = 0x01;
|
static readonly ADVANCED_TRANSACTION_FLAG = 0x01;
|
||||||
|
|
||||||
static fromBuffer(buffer: Buffer, _NO_STRICT?: boolean): Transaction {
|
static fromBuffer(buffer: Buffer, _NO_STRICT?: boolean): Transaction {
|
||||||
let offset: number = 0;
|
const bufferReader = new BufferReader(buffer);
|
||||||
|
|
||||||
function readSlice(n: number): Buffer {
|
|
||||||
offset += n;
|
|
||||||
return buffer.slice(offset - n, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
function readUInt32(): number {
|
|
||||||
const i = buffer.readUInt32LE(offset);
|
|
||||||
offset += 4;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
function readInt32(): number {
|
|
||||||
const i = buffer.readInt32LE(offset);
|
|
||||||
offset += 4;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
function readUInt64(): number {
|
|
||||||
const i = bufferutils.readUInt64LE(buffer, offset);
|
|
||||||
offset += 8;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
function readVarInt(): number {
|
|
||||||
const vi = varuint.decode(buffer, offset);
|
|
||||||
offset += varuint.decode.bytes;
|
|
||||||
return vi;
|
|
||||||
}
|
|
||||||
|
|
||||||
function readVarSlice(): Buffer {
|
|
||||||
return readSlice(readVarInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
function readVector(): Buffer[] {
|
|
||||||
const count = readVarInt();
|
|
||||||
const vector: Buffer[] = [];
|
|
||||||
for (let i = 0; i < count; i++) vector.push(readVarSlice());
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tx = new Transaction();
|
const tx = new Transaction();
|
||||||
tx.version = readInt32();
|
tx.version = bufferReader.readInt32();
|
||||||
|
|
||||||
const marker = buffer.readUInt8(offset);
|
const marker = bufferReader.readUInt8();
|
||||||
const flag = buffer.readUInt8(offset + 1);
|
const flag = bufferReader.readUInt8();
|
||||||
|
|
||||||
let hasWitnesses = false;
|
let hasWitnesses = false;
|
||||||
if (
|
if (
|
||||||
marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
|
marker === Transaction.ADVANCED_TRANSACTION_MARKER &&
|
||||||
flag === Transaction.ADVANCED_TRANSACTION_FLAG
|
flag === Transaction.ADVANCED_TRANSACTION_FLAG
|
||||||
) {
|
) {
|
||||||
offset += 2;
|
|
||||||
hasWitnesses = true;
|
hasWitnesses = true;
|
||||||
|
} else {
|
||||||
|
bufferReader.offset -= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
const vinLen = readVarInt();
|
const vinLen = bufferReader.readVarInt();
|
||||||
for (let i = 0; i < vinLen; ++i) {
|
for (let i = 0; i < vinLen; ++i) {
|
||||||
tx.ins.push({
|
tx.ins.push({
|
||||||
hash: readSlice(32),
|
hash: bufferReader.readSlice(32),
|
||||||
index: readUInt32(),
|
index: bufferReader.readUInt32(),
|
||||||
script: readVarSlice(),
|
script: bufferReader.readVarSlice(),
|
||||||
sequence: readUInt32(),
|
sequence: bufferReader.readUInt32(),
|
||||||
witness: EMPTY_WITNESS,
|
witness: EMPTY_WITNESS,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const voutLen = readVarInt();
|
const voutLen = bufferReader.readVarInt();
|
||||||
for (let i = 0; i < voutLen; ++i) {
|
for (let i = 0; i < voutLen; ++i) {
|
||||||
tx.outs.push({
|
tx.outs.push({
|
||||||
value: readUInt64(),
|
value: bufferReader.readUInt64(),
|
||||||
script: readVarSlice(),
|
script: bufferReader.readVarSlice(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasWitnesses) {
|
if (hasWitnesses) {
|
||||||
for (let i = 0; i < vinLen; ++i) {
|
for (let i = 0; i < vinLen; ++i) {
|
||||||
tx.ins[i].witness = readVector();
|
tx.ins[i].witness = bufferReader.readVector();
|
||||||
}
|
}
|
||||||
|
|
||||||
// was this pointless?
|
// was this pointless?
|
||||||
|
@ -154,10 +114,10 @@ export class Transaction {
|
||||||
throw new Error('Transaction has superfluous witness data');
|
throw new Error('Transaction has superfluous witness data');
|
||||||
}
|
}
|
||||||
|
|
||||||
tx.locktime = readUInt32();
|
tx.locktime = bufferReader.readUInt32();
|
||||||
|
|
||||||
if (_NO_STRICT) return tx;
|
if (_NO_STRICT) return tx;
|
||||||
if (offset !== buffer.length)
|
if (bufferReader.offset !== buffer.length)
|
||||||
throw new Error('Transaction has unexpected data');
|
throw new Error('Transaction has unexpected data');
|
||||||
|
|
||||||
return tx;
|
return tx;
|
||||||
|
|
4
types/bufferutils.d.ts
vendored
4
types/bufferutils.d.ts
vendored
|
@ -5,7 +5,7 @@ export declare function reverseBuffer(buffer: Buffer): Buffer;
|
||||||
* Helper class for serialization of bitcoin data types into a pre-allocated buffer.
|
* Helper class for serialization of bitcoin data types into a pre-allocated buffer.
|
||||||
*/
|
*/
|
||||||
export declare class BufferWriter {
|
export declare class BufferWriter {
|
||||||
private buffer;
|
buffer: Buffer;
|
||||||
offset: number;
|
offset: number;
|
||||||
constructor(buffer: Buffer, offset?: number);
|
constructor(buffer: Buffer, offset?: number);
|
||||||
writeUInt8(i: number): void;
|
writeUInt8(i: number): void;
|
||||||
|
@ -21,7 +21,7 @@ export declare class BufferWriter {
|
||||||
* Helper class for serialization of bitcoin data types into a pre-allocated buffer.
|
* Helper class for serialization of bitcoin data types into a pre-allocated buffer.
|
||||||
*/
|
*/
|
||||||
export declare class BufferReader {
|
export declare class BufferReader {
|
||||||
private buffer;
|
buffer: Buffer;
|
||||||
offset: number;
|
offset: number;
|
||||||
constructor(buffer: Buffer, offset?: number);
|
constructor(buffer: Buffer, offset?: number);
|
||||||
readUInt8(): number;
|
readUInt8(): number;
|
||||||
|
|
Loading…
Reference in a new issue