diff --git a/apps/alias-server/src/script.js b/apps/alias-server/src/script.js new file mode 100644 --- /dev/null +++ b/apps/alias-server/src/script.js @@ -0,0 +1,45 @@ +// Copyright (c) 2023 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. +'use strict'; +module.exports = { + /** + * Given outputScript as a hexString and a byteCount, return {consumed, remaining} + * @param {string} outputScript a hex string outputScript of an eCash tx + * @param {integer || string} byteCount integer or number as one-byte hex string + * @throws {Error} if input is not supported, + * i.e. if byteCount is not an integer or if byteCount exceeds outputScript bytes + * @returns {object} {consumed: , remaining: } + */ + consume: function (outputScript, byteCount) { + // Get byteCount in as a decimal integer + if (typeof byteCount === 'string') { + byteCount = parseInt(byteCount, 16); + } + // Throw an error if byteCount input is not an integer + if (!Number.isInteger(byteCount)) { + throw new Error( + `byteCount must be an integer or a one-byte hex string, received ${byteCount}`, + ); + } + // One byte is 2 characters of a hex string + const byteLength = 2; + + // Get byte slice size + const byteSliceSize = byteCount * byteLength; + + // Throw an error if byteCount is greater than consumable hex bytes in outputScript + if (byteSliceSize > outputScript.length) { + throw new Error( + `consume called with byteCount (${byteCount}) greater than remaining bytes in outputScript (${ + outputScript.length / byteLength + })`, + ); + } + // Get consumed bytes + const consumed = outputScript.slice(0, byteSliceSize); + // Get remaining bytes + const remaining = outputScript.slice(byteSliceSize); + return { consumed, remaining }; + }, +}; diff --git a/apps/alias-server/test/scriptTests.js b/apps/alias-server/test/scriptTests.js new file mode 100644 --- /dev/null +++ b/apps/alias-server/test/scriptTests.js @@ -0,0 +1,57 @@ +// Copyright (c) 2023 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +'use strict'; +const assert = require('assert'); +const { consume } = require('../src/script'); +const testOutputScript = + '6a042e78656300154361706974616c4c6574746572735f416e645f2b21150076458db0ed96fe9863fc1ccec9fa2cfab884b0f6'; + +describe('alias-server script.js', function () { + it('consume gets the first byte from the stack of a hex string outputScript with a byteCount as a number', function () { + assert.deepEqual(consume(testOutputScript, 1), { + consumed: '6a', + remaining: + '042e78656300154361706974616c4c6574746572735f416e645f2b21150076458db0ed96fe9863fc1ccec9fa2cfab884b0f6', + }); + }); + it('consume gets the first byte from the stack of a hex string outputScript with a byteCount as hex string', function () { + assert.deepEqual(consume(testOutputScript, '01'), { + consumed: '6a', + remaining: + '042e78656300154361706974616c4c6574746572735f416e645f2b21150076458db0ed96fe9863fc1ccec9fa2cfab884b0f6', + }); + }); + it('consume throws an error if byteCount is not an integer', function () { + assert.throws(() => { + consume(testOutputScript, 1.5); + }, Error('byteCount must be an integer or a one-byte hex string, received 1.5')); + }); + it('consume gets the full length of the stack of a hex string outputScript with a byteCount as a number', function () { + assert.deepEqual(consume(testOutputScript, 51), { + consumed: + '6a042e78656300154361706974616c4c6574746572735f416e645f2b21150076458db0ed96fe9863fc1ccec9fa2cfab884b0f6', + remaining: '', + }); + }); + it('consume gets the full length of the stack of a hex string outputScript with a byteCount as a hex string', function () { + assert.deepEqual(consume(testOutputScript, '33'), { + consumed: + '6a042e78656300154361706974616c4c6574746572735f416e645f2b21150076458db0ed96fe9863fc1ccec9fa2cfab884b0f6', + remaining: '', + }); + }); + it('consume throws an error if called with byteCount greater than outputScript', function () { + assert.throws(() => { + consume(testOutputScript, 52); + }, Error('consume called with byteCount (52) greater than remaining bytes in outputScript (51)')); + }); + it('consume consumes nothing if called with 0', function () { + assert.deepEqual(consume(testOutputScript, 0), { + consumed: '', + remaining: + '6a042e78656300154361706974616c4c6574746572735f416e645f2b21150076458db0ed96fe9863fc1ccec9fa2cfab884b0f6', + }); + }); +});