Skip to content

Commit

Permalink
feat: added Transactions class
Browse files Browse the repository at this point in the history
Signed-off-by: theanmolsharma <anmolsharma0234@gmail.com>
  • Loading branch information
theanmolsharma committed Oct 8, 2023
1 parent 058f40b commit 6251027
Show file tree
Hide file tree
Showing 7 changed files with 813 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const WITNESS_MARKER = 0x00;
export const WITNESS_FLAG = 0x01;
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ export { hashOutpoints, calculateSumOfPrivateKeys } from './utility.ts';

export { createOutputs } from './outputs.ts';
export { scanOutputs } from './scanning.ts';

export { Transaction } from './transaction.ts';
10 changes: 10 additions & 0 deletions src/interface.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Buffer } from 'buffer';

export type Outpoint = {
txid: string;
vout: number;
Expand All @@ -19,3 +21,11 @@ export type Output = {
};

export type LabelMap = { [key: string]: string };

export type Input = {
hash: Buffer;
index: number;
script: Buffer;
sequence: number;
witness: Buffer[];
};
96 changes: 96 additions & 0 deletions src/transaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { WITNESS_FLAG, WITNESS_MARKER } from './constants.ts';
import { encodingLength, readVarInt } from './utility.ts';
import { Input, Output } from './interface.ts';
import { Buffer } from 'buffer';

export class Transaction {
version: number;
inputs: Input[];
outputs: Output[];
locktime: number;

constructor() {
this.version = 0;
this.inputs = [];
this.outputs = [];
this.locktime = 0;
}

static fromBuffer(buffer: Buffer): Transaction {
const tx = new Transaction();
let offset = 0;
tx.version = buffer.readInt32LE(offset);
offset += 4;

const hasWitnesses =
buffer.readUInt8(offset) === WITNESS_MARKER &&
buffer.readUInt8(offset + 1) === WITNESS_FLAG;
offset += hasWitnesses ? 2 : 0;

const vinLen = readVarInt(buffer, offset);
offset += encodingLength(vinLen);
for (let i = 0; i < vinLen; i++) {
const hash = buffer.subarray(offset, offset + 32);
offset += 32;
const index = buffer.readUInt32LE(offset);
offset += 4;
const scriptSize = readVarInt(buffer, offset);
offset += encodingLength(scriptSize);
const script = buffer.subarray(offset, offset + scriptSize);
offset += scriptSize;
const sequence = buffer.readUInt32LE(offset);
offset += 4;
tx.inputs.push({
hash: hash,
index: index,
script: script,
sequence: sequence,
witness: [],
});
}

const voutLen = readVarInt(buffer, offset);
offset += encodingLength(vinLen);
for (let i = 0; i < voutLen; i++) {
const value = buffer.readBigUint64LE(offset);
offset += 8;
const scriptSize = readVarInt(buffer, offset);
offset += encodingLength(scriptSize);
const script = buffer.subarray(offset, offset + scriptSize);
offset += scriptSize;
tx.outputs.push({
value: Number(BigInt.asUintN(56, value)),
script: script,
});
}

if (hasWitnesses) {
for (let i = 0; i < vinLen; i++) {
const vectorSize = readVarInt(buffer, offset);
offset += encodingLength(vectorSize);
const vector: Buffer[] = [];
for (let j = 0; j < vectorSize; j++) {
const witnessItemLen = readVarInt(buffer, offset);
offset += encodingLength(witnessItemLen);
vector.push(
buffer.subarray(offset, offset + witnessItemLen),
);
offset += witnessItemLen;
}
tx.inputs[i].witness = vector;
}
}

tx.locktime = buffer.readUInt32LE(offset);
offset += 4;

if (offset !== buffer.length)
throw new Error('Transaction has unexpected data');

return tx;
}

static fromHex(hex: string): Transaction {
return Transaction.fromBuffer(Buffer.from(hex, 'hex'));
}
}
21 changes: 21 additions & 0 deletions src/utility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,24 @@ const serialiseUint32LE = (n: number): Buffer => {
buf.writeUInt32LE(n);
return buf;
};

export const readVarInt = (buffer: Buffer, offset: number = 0): number => {
const first = buffer.readUInt8(offset);

// 8 bit
if (first < 0xfd) return first;
// 16 bit
else if (first === 0xfd) return buffer.readUInt16LE(offset + 1);
// 32 bit
else if (first === 0xfe) return buffer.readUInt32LE(offset + 1);
// 64 bit
else {
const lo = buffer.readUInt32LE(offset + 1);
const hi = buffer.readUInt32LE(offset + 5);
return hi * 0x0100000000 + lo;
}
};

export const encodingLength = (n: number) => {
return n < 0xfd ? 1 : n <= 0xffff ? 3 : n <= 0xffffffff ? 5 : 9;
};
Loading

0 comments on commit 6251027

Please sign in to comment.