Skip to content

Instantly share code, notes, and snippets.

@johnnieskywalker
Last active March 9, 2024 12:27
Show Gist options
  • Save johnnieskywalker/c56c89de45d378b44d2b77e21c764c0b to your computer and use it in GitHub Desktop.
Save johnnieskywalker/c56c89de45d378b44d2b77e21c764c0b to your computer and use it in GitHub Desktop.

Revisions

  1. johnnieskywalker revised this gist Mar 9, 2024. 2 changed files with 40 additions and 3 deletions.
    37 changes: 37 additions & 0 deletions testFee.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,37 @@
    const { assert } = require('chai');
    const Transaction = require('../Transaction');
    const TXO = require('../TXO');

    describe('Transaction', function () {
    const fromAddress = "1DBS97W3jWw6FnAqdduK1NW6kFo3Aid1N6";
    const toAddress = "12ruWjb4naCME5QhjrQSJuS5disgME22fe";

    describe('with no remainder', () => {
    const txo1 = new TXO(fromAddress, 5);
    const txo2 = new TXO(fromAddress, 5);
    const outputTXO1 = new TXO(toAddress, 7);
    const outputTXO2 = new TXO(fromAddress, 3);

    const tx = new Transaction([txo1, txo2], [outputTXO1, outputTXO2]);

    tx.execute();

    it('should have zero fee', () => {
    assert.equal(tx.fee, 0);
    });
    });

    describe('with some remainder', () => {
    const txo1 = new TXO(fromAddress, 15);
    const outputTXO1 = new TXO(toAddress, 7);
    const outputTXO2 = new TXO(fromAddress, 6);

    const tx = new Transaction([txo1], [outputTXO1, outputTXO2]);

    tx.execute();

    it('should have the remainder as the fee', () => {
    assert.equal(tx.fee, 2);
    });
    });
    });
    6 changes: 3 additions & 3 deletions transaction.js
    Original file line number Diff line number Diff line change
    @@ -5,15 +5,15 @@ class Transaction {
    }

    execute() {
    const sumInputUTXOs = this.inputUTXOs.reduce((acc, curr) => acc + curr.amount, 0);
    const sumOutputUTXOs = this.outputUTXOs.reduce((acc, curr) => acc + curr.amount, 0);
    try {
    const hasSpentInput = this.inputUTXOs.some(input => input.spent);

    if (hasSpentInput) {
    throw new Error('One or more input UTXOs have already been spent.');
    }

    const sumInputUTXOs = this.inputUTXOs.reduce((acc, curr) => acc + curr.amount, 0);
    const sumOutputUTXOs = this.outputUTXOs.reduce((acc, curr) => acc + curr.amount, 0);
    const sufficientInput = sumInputUTXOs >= sumOutputUTXOs;

    if (!sufficientInput) {
    @@ -27,7 +27,7 @@ class Transaction {
    throw error;
    }
    this.inputUTXOs.forEach(input => input.spent = true);

    this.fee = sumInputUTXOs - sumOutputUTXOs;
    }
    }

  2. johnnieskywalker revised this gist Mar 7, 2024. 2 changed files with 72 additions and 10 deletions.
    50 changes: 50 additions & 0 deletions testTransactionUTXO.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,50 @@
    const { assert } = require('chai');
    const Transaction = require('../Transaction');
    const TXO = require('../TXO');

    describe('Transaction', function () {
    const fromAddress = "1DBS97W3jWw6FnAqdduK1NW6kFo3Aid1N6";
    const toAddress = "12ruWjb4naCME5QhjrQSJuS5disgME22fe";

    const txo1 = new TXO(fromAddress, 5);
    const txo2 = new TXO(fromAddress, 5);
    const txo3 = new TXO(fromAddress, 3);
    const txo4 = new TXO(fromAddress, 4);
    const outputTXO1 = new TXO(toAddress, 7);
    const outputTXO2 = new TXO(fromAddress, 3);

    it('should mark both inputs as spent', () => {
    const tx = new Transaction([txo1, txo2], [outputTXO1, outputTXO2]);
    tx.execute();
    assert(txo1.spent);
    assert(txo2.spent);
    });

    it('should leave both inputs unspent if funds unavailable', () => {
    const tx = new Transaction([txo3, txo4], [outputTXO1, outputTXO2]);
    let ex;
    try {
    tx.execute();
    }
    catch (_ex) {
    ex = _ex;
    }
    assert(ex, "Expected an exception to be thrown!");
    assert(!txo3.spent, "The transaction should be marked as unspent");
    assert(!txo4.spent, "The transaction should be marked as unspent");
    });

    it('should leave valid inputs unspent if a double spend occurs', () => {
    const tx = new Transaction([txo1, txo4], [outputTXO1, outputTXO2]);
    let ex;
    try {
    tx.execute();
    }
    catch (_ex) {
    ex = _ex;
    }
    assert(ex, "Expected an exception to be thrown!");
    assert(!txo4.spent, "The transaction should be marked as unspent");
    });

    });
    32 changes: 22 additions & 10 deletions transaction.js
    Original file line number Diff line number Diff line change
    @@ -3,20 +3,32 @@ class Transaction {
    this.inputUTXOs = inputUTXOs;
    this.outputUTXOs = outputUTXOs;
    }

    execute() {
    const hasSpentInput = this.inputUTXOs.some(input => input.spent);
    try {
    const hasSpentInput = this.inputUTXOs.some(input => input.spent);

    if (hasSpentInput) {
    throw new Error('One or more input UTXOs have already been spent.');
    }

    const sumInputUTXOs = this.inputUTXOs.reduce((acc, curr) => acc + curr.amount, 0);
    const sumOutputUTXOs = this.outputUTXOs.reduce((acc, curr) => acc + curr.amount, 0);
    const sufficientInput = sumInputUTXOs >= sumOutputUTXOs;

    if (!sufficientInput) {
    throw new Error('Insufficient input UTXOs');
    }

    if (hasSpentInput) {
    throw new Error('One or more input UTXOs have already been spent.');
    } catch (error) {
    if (error.message === 'Insufficient input UTXOs') {
    this.inputUTXOs.forEach(input => input.spent = false);
    }
    throw error;
    }
    this.inputUTXOs.forEach(input => input.spent = true);

    const sumInputUTXOs = this.inputUTXOs.reduce((acc, curr) => acc + curr.amount, 0);
    const sumOutputUTXOs = this.outputUTXOs.reduce((acc, curr) => acc + curr.amount, 0);
    const sufficientInput = sumInputUTXOs >= sumOutputUTXOs;
    if (!sufficientInput) {
    throw new Error('insufficient input utxos')
    }
    }
    }

    module.exports = Transaction;
    module.exports = Transaction;
  3. johnnieskywalker revised this gist Mar 7, 2024. 2 changed files with 52 additions and 0 deletions.
    45 changes: 45 additions & 0 deletions testSufficientUTXO.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,45 @@
    const { assert } = require('chai');
    const Transaction = require('../Transaction');
    const TXO = require('../TXO');

    describe('Transaction', function () {
    const fromAddress = "1DBS97W3jWw6FnAqdduK1NW6kFo3Aid1N6";
    const toAddress = "12ruWjb4naCME5QhjrQSJuS5disgME22fe";

    describe('with sufficient UTXOs', () => {
    const txo1 = new TXO(fromAddress, 5);
    const txo2 = new TXO(fromAddress, 5);
    const outputTXO1 = new TXO(toAddress, 10);
    const tx = new Transaction([txo1, txo2], [outputTXO1]);

    it('should execute without error', () => {
    try {
    tx.execute();
    }
    catch (ex) {
    assert.fail(ex.message);
    console.log(ex);
    }
    });
    });

    describe('with insufficient UTXOs', () => {
    const txo1 = new TXO(fromAddress, 3);
    const txo2 = new TXO(fromAddress, 3);
    const txo3 = new TXO(fromAddress, 3);
    const outputTXO1 = new TXO(toAddress, 10);

    const tx = new Transaction([txo1, txo2, txo3], [outputTXO1]);

    it('should throw an error on execute', () => {
    let ex;
    try {
    tx.execute();
    }
    catch (_ex) {
    ex = _ex;
    }
    assert(ex, "Did not throw an exception for a lack of UTXO funds!");
    });
    });
    });
    7 changes: 7 additions & 0 deletions transaction.js
    Original file line number Diff line number Diff line change
    @@ -9,6 +9,13 @@ class Transaction {
    if (hasSpentInput) {
    throw new Error('One or more input UTXOs have already been spent.');
    }

    const sumInputUTXOs = this.inputUTXOs.reduce((acc, curr) => acc + curr.amount, 0);
    const sumOutputUTXOs = this.outputUTXOs.reduce((acc, curr) => acc + curr.amount, 0);
    const sufficientInput = sumInputUTXOs >= sumOutputUTXOs;
    if (!sufficientInput) {
    throw new Error('insufficient input utxos')
    }
    }
    }

  4. johnnieskywalker revised this gist Mar 6, 2024. 2 changed files with 62 additions and 0 deletions.
    47 changes: 47 additions & 0 deletions testTransaction.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,47 @@
    const { assert } = require('chai');
    const Transaction = require('../Transaction');
    const TXO = require('../TXO');

    describe('Transaction', function () {
    const fromAddress = "1DBS97W3jWw6FnAqdduK1NW6kFo3Aid1N6";
    const toAddress = "12ruWjb4naCME5QhjrQSJuS5disgME22fe";

    describe('with unspent input TXOs', () => {
    const inputTXO1 = new TXO(fromAddress, 5);
    const inputTXO2 = new TXO(fromAddress, 5);
    const outputTXO1 = new TXO(toAddress, 10);
    const tx = new Transaction([inputTXO1, inputTXO2], [outputTXO1]);

    it('should execute without error', () => {
    try {
    tx.execute();
    }
    catch(ex) {
    assert.fail(ex.message);
    console.log(ex);
    }
    });
    });

    describe('with a spent input TXO', () => {
    const txo1 = new TXO(fromAddress, 5);
    const txo2 = new TXO(fromAddress, 5);
    const txo3 = new TXO(fromAddress, 5);
    const outputTXO1 = new TXO(toAddress, 15);

    txo2.spend();

    const tx = new Transaction([txo1, txo2, txo3], [outputTXO1]);

    it('should throw an error on execute', () => {
    let ex;
    try {
    tx.execute();
    }
    catch (_ex) {
    ex = _ex;
    }
    assert(ex, "Did not throw an exception with a spent input TXO!");
    });
    });
    });
    15 changes: 15 additions & 0 deletions transaction.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    class Transaction {
    constructor(inputUTXOs, outputUTXOs) {
    this.inputUTXOs = inputUTXOs;
    this.outputUTXOs = outputUTXOs;
    }
    execute() {
    const hasSpentInput = this.inputUTXOs.some(input => input.spent);

    if (hasSpentInput) {
    throw new Error('One or more input UTXOs have already been spent.');
    }
    }
    }

    module.exports = Transaction;
  5. johnnieskywalker created this gist Mar 5, 2024.
    12 changes: 12 additions & 0 deletions TXO.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,12 @@
    class TXO {
    constructor(owner, amount) {
    this.owner = owner;
    this.amount = amount;
    this.spent = false;
    }
    spend() {
    this.spent = true;
    }
    }

    module.exports = TXO;
    27 changes: 27 additions & 0 deletions test.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,27 @@
    const { assert } = require('chai');
    const TXO = require('../TXO');

    describe('TXO', function () {
    const address = "1DBS97W3jWw6FnAqdduK1NW6kFo3Aid1N6";
    const amount = 10;
    const txo = new TXO(address, amount);

    describe('constructor', () => {
    it('should set the owner', () => {
    assert.equal(txo.owner, address);
    });
    it('should set the amount', () => {
    assert.equal(txo.amount, amount);
    });
    it('should set spent to false', () => {
    assert.equal(txo.spent, false);
    });
    });

    describe('spend', () => {
    it('should set spent to true', () => {
    txo.spend();
    assert.equal(txo.spent, true);
    });
    });
    });