@alexbosworth/blockchain 1.5.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Versions
2
2
 
3
+ ## 1.7.0
4
+
5
+ - `idForTransactionComponents`: Add method to get transaction id for components
6
+ - `queryTransactions`: Add method to find transaction outputs matching a query
7
+
8
+ ## 1.6.0
9
+
10
+ - `previousBlockId`: Add method to get the previous block id from a block
11
+
3
12
  ## 1.5.0
4
13
 
5
14
  - `scriptAsScriptElements`: Convert an encoded script into script elements
package/README.md CHANGED
@@ -72,6 +72,33 @@ Get an id for a block: the double sha256 hash of the block header
72
72
  id: <Block Id Hex Encoded String>
73
73
  }
74
74
 
75
+ ### idForTransactionComponents
76
+
77
+ Determine a transaction id from transaction components
78
+
79
+ {
80
+ inputs: [{
81
+ hash: <Spending Internal Byte Order Transaction Id Buffer Object>
82
+ script: <Script Buffer Object>
83
+ sequence: <Sequence Number>
84
+ vout: <Spending Transaction Output Index Number>
85
+ }]
86
+ locktime: <Timelock nLockTime Number>
87
+ outputs: [{
88
+ script: <Output Script Buffer Object>
89
+ tokens: <Tokens Count Number>
90
+ }]
91
+ version: <Version Number>
92
+ }
93
+
94
+ @throws
95
+ <Error>
96
+
97
+ @returns
98
+ {
99
+ id: <Transaction Id Hex String>
100
+ }
101
+
75
102
  ### noLocktimeIdForTransaction
76
103
 
77
104
  Get an id for a transaction with witness data and mlocktime not included
@@ -105,6 +132,44 @@ Convert a number to compact size integer serialization
105
132
  encoded: <Serialized Compact Integer Buffer Object>
106
133
  }
107
134
 
135
+ ### previousBlockId
136
+
137
+ Given a raw block, return the previous block id
138
+
139
+ {
140
+ block: <Hex Encoded Block String>
141
+ }
142
+
143
+ @throws
144
+ <Error>
145
+
146
+ @returns
147
+ {
148
+ previous: <Previous Block Id Hex String>
149
+ }
150
+
151
+ ### queryTransactions
152
+
153
+ Find matching transactions within a block
154
+
155
+ {
156
+ block: <Hex Encoded Raw Block String>
157
+ outputs: [<Output Script Hex String>]
158
+ }
159
+
160
+ @throws
161
+ <Error>
162
+
163
+ @returns
164
+ {
165
+ outputs: [{
166
+ script: <Output Script Hex String>
167
+ tokens: <Output Value Number>
168
+ transaction_id: <Transaction Id Hex String>
169
+ transaction_vout: <Transaction Output Index Number>
170
+ }]
171
+ }
172
+
108
173
  ### scriptAsScriptElements
109
174
 
110
175
  Map a serialized script into an array of script elements
@@ -0,0 +1,99 @@
1
+ const {createHash} = require('crypto');
2
+
3
+ const {numberAsCompactInt} = require('./../numbers');
4
+
5
+ const {alloc} = Buffer;
6
+ const bufferAsHex = buffer => buffer.toString('hex');
7
+ const byteCountHash = 32;
8
+ const byteCountInt32 = 4;
9
+ const byteCountInt64 = 8;
10
+ const {concat} = Buffer;
11
+ const sha256 = preimage => createHash('sha256').update(preimage).digest();
12
+
13
+ /** Determine a transaction id from transaction components
14
+
15
+ {
16
+ inputs: [{
17
+ hash: <Spending Internal Byte Order Transaction Id Buffer Object>
18
+ script: <Script Buffer Object>
19
+ sequence: <Sequence Number>
20
+ vout: <Spending Transaction Output Index Number>
21
+ }]
22
+ locktime: <Timelock nLockTime Number>
23
+ outputs: [{
24
+ script: <Output Script Buffer Object>
25
+ tokens: <Tokens Count Number>
26
+ }]
27
+ version: <Version Number>
28
+ }
29
+
30
+ @throws
31
+ <Error>
32
+
33
+ @returns
34
+ {
35
+ id: <Transaction Id Hex String>
36
+ }
37
+ */
38
+ module.exports = ({inputs, locktime, outputs, version}) => {
39
+ const elements = [];
40
+
41
+ // Write the signed transaction version number
42
+ const ver = alloc(byteCountInt32);
43
+
44
+ ver.writeInt32LE(version);
45
+
46
+ elements.push(ver);
47
+
48
+ // Write how many inputs there are
49
+ elements.push(numberAsCompactInt({number: inputs.length}).encoded);
50
+
51
+ // Write the inputs
52
+ elements.push(concat(inputs.map(input => {
53
+ // Encode the sequence number
54
+ const sequence = alloc(byteCountInt32);
55
+
56
+ sequence.writeUInt32LE(input.sequence);
57
+
58
+ // Encode the spend output index
59
+ const vout = alloc(byteCountInt32);
60
+
61
+ vout.writeUInt32LE(input.vout);
62
+
63
+ // Write the spend outpoint, script sig, sequence number
64
+ return concat([
65
+ input.hash,
66
+ vout,
67
+ numberAsCompactInt({number: input.script.length}).encoded,
68
+ input.script,
69
+ sequence,
70
+ ]);
71
+ })));
72
+
73
+ // Write the outputs count
74
+ elements.push(numberAsCompactInt({number: outputs.length}).encoded);
75
+
76
+ elements.push(concat(outputs.map(output => {
77
+ // Encode the output value
78
+ const value = alloc(byteCountInt64);
79
+
80
+ value.writeBigUInt64LE(BigInt(output.tokens));
81
+
82
+ // Write the output value and output script
83
+ return concat([
84
+ value,
85
+ numberAsCompactInt({number: output.script.length}).encoded,
86
+ output.script,
87
+ ]);
88
+ })));
89
+
90
+ // Write the locktime
91
+ const timelock = alloc(byteCountInt32);
92
+
93
+ timelock.writeUInt32LE(locktime);
94
+
95
+ elements.push(timelock);
96
+
97
+ // The id is the double sha256 hash of the elements, reversed
98
+ return {id: bufferAsHex(sha256(sha256(concat(elements))).reverse())};
99
+ };
package/hashes/index.js CHANGED
@@ -1,4 +1,9 @@
1
1
  const idForBlock = require('./id_for_block');
2
- const noLocktimeIdForTransaction = require('./no_locktime_id_for_transaction');
2
+ const idForTransactionComponents = require('./id_for_transaction_components');
3
+ const previousBlockId = require('./previous_block_id');
3
4
 
4
- module.exports = {idForBlock, noLocktimeIdForTransaction};
5
+ module.exports = {
6
+ idForBlock,
7
+ idForTransactionComponents,
8
+ previousBlockId,
9
+ };
@@ -0,0 +1,28 @@
1
+ const hashBytesHexCount = 32 * 2;
2
+ const reverseByteOrder = n => Buffer.from(n, 'hex').reverse().toString('hex');
3
+ const versionBytesHexCount = 4 * 2;
4
+
5
+ /** Given a raw block, return the previous block id
6
+
7
+ {
8
+ block: <Hex Encoded Block String>
9
+ }
10
+
11
+ @throws
12
+ <Error>
13
+
14
+ @returns
15
+ {
16
+ previous: <Previous Block Id Hex String>
17
+ }
18
+ */
19
+ module.exports = ({block}) => {
20
+ if (!block) {
21
+ throw new Error('ExpectedHexEncodedBlockToDerivePreviousBlockId');
22
+ }
23
+
24
+ const end = hashBytesHexCount + versionBytesHexCount;
25
+ const start = versionBytesHexCount;
26
+
27
+ return {previous: reverseByteOrder(block.substring(start, end))};
28
+ };
package/index.js CHANGED
@@ -1,8 +1,11 @@
1
1
  const {compactIntAsNumber} = require('./numbers');
2
2
  const {componentsOfTransaction} = require('./transactions');
3
3
  const {idForBlock} = require('./hashes');
4
- const {noLocktimeIdForTransaction} = require('./hashes');
4
+ const {idForTransactionComponents} = require('./hashes');
5
+ const {noLocktimeIdForTransaction} = require('./transactions');
5
6
  const {numberAsCompactInt} = require('./numbers');
7
+ const {previousBlockId} = require('./hashes');
8
+ const {queryTransactions} = require('./transactions');
6
9
  const {scriptAsScriptElements} = require('./script');
7
10
  const {scriptElementsAsScript} = require('./script');
8
11
 
@@ -10,8 +13,11 @@ module.exports = {
10
13
  compactIntAsNumber,
11
14
  componentsOfTransaction,
12
15
  idForBlock,
16
+ idForTransactionComponents,
13
17
  noLocktimeIdForTransaction,
14
18
  numberAsCompactInt,
19
+ previousBlockId,
20
+ queryTransactions,
15
21
  scriptAsScriptElements,
16
22
  scriptElementsAsScript,
17
23
  };
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "url": "https://github.com/alexbosworth/blockchain.git"
18
18
  },
19
19
  "scripts": {
20
- "test": "npx nyc@latest node --experimental-test-coverage --test test/hashes/*.js test/numbers/*.js test/script/*.js test/transactions/*.js"
20
+ "test": "npx nyc@15.1.0 node --experimental-test-coverage --test test/hashes/*.js test/numbers/*.js test/script/*.js test/transactions/*.js"
21
21
  },
22
- "version": "1.5.0"
22
+ "version": "1.7.0"
23
23
  }
@@ -1,6 +1,6 @@
1
- const {deepStrictEqual} = require('node:assert').strict;
2
- const {throws} = require('node:assert').strict;
1
+ const {deepEqual} = require('node:assert').strict;
3
2
  const test = require('node:test');
3
+ const {throws} = require('node:assert').strict;
4
4
 
5
5
  const {idForBlock} = require('./../../');
6
6
 
@@ -30,7 +30,7 @@ tests.forEach(({args, description, error, expected}) => {
30
30
  } else {
31
31
  const res = idForBlock(args);
32
32
 
33
- deepStrictEqual(res, expected, 'Got expected result');
33
+ deepEqual(res, expected, 'Got expected result');
34
34
  }
35
35
 
36
36
  return end();
@@ -1,4 +1,4 @@
1
- const {deepStrictEqual} = require('node:assert').strict;
1
+ const {deepEqual} = require('node:assert').strict;
2
2
  const {throws} = require('node:assert').strict;
3
3
  const test = require('node:test');
4
4
 
@@ -48,7 +48,7 @@ tests.forEach(({args, description, error, expected}) => {
48
48
  } else {
49
49
  const res = noLocktimeIdForTransaction(args);
50
50
 
51
- deepStrictEqual(res, expected, 'Got expected result');
51
+ deepEqual(res, expected, 'Got expected result');
52
52
  }
53
53
 
54
54
  return end();
@@ -0,0 +1,38 @@
1
+ const {deepEqual} = require('node:assert').strict;
2
+ const test = require('node:test');
3
+ const {throws} = require('node:assert').strict;
4
+
5
+ const {previousBlockId} = require('./../../');
6
+
7
+ const hexAsBuffer = hex => Buffer.from(hex, 'hex');
8
+
9
+ const tests = [
10
+ {
11
+ args: {block: ''},
12
+ description: 'A hex encoded block is required',
13
+ error: 'ExpectedHexEncodedBlockToDerivePreviousBlockId',
14
+ },
15
+ {
16
+ args: {
17
+ block: '00609c32519912209ce3196c09d7a409035874057780d4072fa31adc1f0000000000000090882aac112a7353ffc0cf13ebc8196620bc83c810b03bd5d6678b536a203c4b1a45d264f4422e193e7ea2ab01010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff1a03ceb625012013090909200909200904631e00a3290000000000ffffffff02be40250000000000160014820d4a343a44e915c36494995c2899abe37418930000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000',
18
+ },
19
+ description: 'The previous block id is returned',
20
+ expected: {
21
+ previous: '000000000000001fdc1aa32f07d480770574580309a4d7096c19e39c20129951',
22
+ },
23
+ },
24
+ ];
25
+
26
+ tests.forEach(({args, description, error, expected}) => {
27
+ return test(description, (t, end) => {
28
+ if (!!error) {
29
+ throws(() => previousBlockId(args), new Error(error), 'Error returned');
30
+ } else {
31
+ const res = previousBlockId(args);
32
+
33
+ deepEqual(res, expected, 'Got expected result');
34
+ }
35
+
36
+ return end();
37
+ });
38
+ });
@@ -0,0 +1,51 @@
1
+ const {deepStrictEqual} = require('node:assert').strict;
2
+ const {throws} = require('node:assert').strict;
3
+ const test = require('node:test');
4
+
5
+ const {queryTransactions} = require('./../../');
6
+
7
+ const hexAsBuffer = hex => Buffer.from(hex, 'hex');
8
+
9
+ const tests = [
10
+ {
11
+ args: {},
12
+ description: 'A block is required',
13
+ error: 'ExpectedHexEncodedBlockToQueryTransactions',
14
+ },
15
+ {
16
+ args: {
17
+ block: '00008020b43ebc848b8a78430f745b58dd0cfecb5a41fda1f9a0793833a98dad00000000cbbb00aa6ab7ea52d99e87088d2d80e7ca27f217f4cb8bc7ac635f17a1ff336900ccd364f4422e1906713cdb07010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff1a03bcb725012013090909200909200904686700c4ee5902000000ffffffff022162250000000000160014820d4a343a44e915c36494995c2899abe37418930000000000000000266a24aa21a9ed78afc412c2c7c1b3135c2a2473a6837068c5ea891e5068d396041057114a749501200000000000000000000000000000000000000000000000000000000000000000000000000100000001ce99c6498760022c4a268bb2bd553b71c5dd7e705ad47704ca2b8cdf72b2d755030000006b483045022100ca65bd58a725c83c3420874e4e0017562ef451f1df17eb42cfd35ee9dd28f98902204a6aafb47a2e1492e50cb0c7038000741d952e3770f9a1f9c9a6229c124e5e790121037435c194e9b01b3d7f7a2802d6684a3af68d05bbf4ec8f17021980d777691f1dfdffffff040000000000000000536a4c5054325bb3a276c4f8c7ecdccd704cf9a0e691db1364e66648672e23871de8b005816f3ec0ecda15e15dbec4e7f2be15f1e4e52e72c187deb36bd3b89d9793ea0e7c71e00025b7b9000f0025b12400114a10270000000000001976a914000000000000000000000000000000000000000088ac10270000000000001976a914000000000000000000000000000000000000000088acb2e8ae08000000001976a914ba27f99e007c7f605a8305e318c1abde3cd220ac88ac00000000010000000001014d4f50b9002361be3b82382e628e41ec29ffb5a28abec8a59ebbf4f28ba4675401000000171600142642be55762762143195a7eb3b965fcc47df9000ffffffff013b0d0000000000001600146ad03f5c84f201cbcbae045ffb9a8b9d769f83d40247304402205993d5c216bcb968c8ed1a99d67a3e4bcf54434c29ffc1bd72c49769d4814f6302200a40821effc9c1fb00d6de454b35244cb70f294b7f3971fee796b9a302f7efa1012103eadead8e5b3a54e36ad936096bd4ca8960ef753b2d81db9afb4ce7a458155e2e0000000002000000000101a4a1f7f07d0d95cd22e7039646133cb29d7b3c5ec55f86720f9cfbd0aa5d155001000000000000000002102700000000000017a91423269e856a01136288e09d8ea5049f5e64d56399872ca218000000000016001456ddc1cec4c3cf468646d6cc2d531d232d43dd4502473044022069f7f79e6c8cfe3ad43e9a5e3da0d77f93169bd4d98a9ef1f5d521340510d688022053b18b1914a40b2b60cb453d8421fa8f2d1c05c9f084b90f38092379276281c701210216243ae4738f200518bf8f0204f6b95e408aadd65f553bad98f0f8a9fd7472eb0000000002000000000102c9f48fe21843a24377835106e94e3ecee80e128027a4fa84d9371df56464b0190000000000fdffffffc9f48fe21843a24377835106e94e3ecee80e128027a4fa84d9371df56464b0190100000000fdffffff01e7170000000000001976a91459cada50314c829e19f5a7786f8ee0d4987f429d88ac0247304402204d578466dad41a451ac42a58bfb15b4ede988181ca384d3d7dfc158289dc369402205b35dd90b83ed4b02ccd300b370d57d3f919d4a100280ba9b2b68feef6f75e720121039757fd1ca6e33c3433cd8de13178dfd50f5688b688301a932cc4eba38e85e80c02473044022052dbdb28b87d8bed9edb8abc664452ad4bd85dd0c068f61a4bffa92a66748d090220674e642420b027c00dc3a0fcec74f53fdd31b9bcd76d20397ef9c8f61a45788c01210251db63b01e13ff7656cb606d732970b4760466162c83e041e018846c627760a1bab7250002000000011e43091d1b554f7e3ce175241aba175bf797996dda991bfdc34fc5cb8298ee5e010000006a473044022027af54aec1a603c1260312241f6cc27d49db4d79cb946a3fb56aff6c8e5e4132022022d3954e49699d369480624c3e8397fd361d9baffdd700d1e9882b465e523b670121021bc2b79284dca29127ed41021571fd3026157c01b4fef8824e94976592b165b3fdffffff0280d725000000000016001475f1d3e5088db87f9a336287cb55142d2062ada3ab72000000000000160014f5e5e9b3af38635bf6992f9732206d0ccb421e9ebbb7250001000000017c1194e8b17d9d26aaba8a78c2cd84490d96d5405619da3b2a9f454860b18911010000006b483045022100e1a219e719575a51e70ccfb06a1968dfd6d2055a3c46664ede399b8a3aab7f8902204b7994dec9e0a6f74e4db0d5a0a06738bd608476160c530392f8f220ad3cb4c6012103ef3f06ff6ea92d1b0a4693ed3f83ad45740b02259739e6427209cb88581416a0ffffffff0278350000000000001976a914e830afe90f87e5c561df7e66ef530d4dd8ea182088ac55d61300000000001976a914fe0f6279060ba6bc6a8b4375d7de8cf8b35a683788ac00000000',
18
+ },
19
+ description: 'An outputs array is required',
20
+ error: 'ExpectedArrayOfOutputsToQueryTransactionsInBlock',
21
+ },
22
+ {
23
+ args: {
24
+ block: '00008020b43ebc848b8a78430f745b58dd0cfecb5a41fda1f9a0793833a98dad00000000cbbb00aa6ab7ea52d99e87088d2d80e7ca27f217f4cb8bc7ac635f17a1ff336900ccd364f4422e1906713cdb07010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff1a03bcb725012013090909200909200904686700c4ee5902000000ffffffff022162250000000000160014820d4a343a44e915c36494995c2899abe37418930000000000000000266a24aa21a9ed78afc412c2c7c1b3135c2a2473a6837068c5ea891e5068d396041057114a749501200000000000000000000000000000000000000000000000000000000000000000000000000100000001ce99c6498760022c4a268bb2bd553b71c5dd7e705ad47704ca2b8cdf72b2d755030000006b483045022100ca65bd58a725c83c3420874e4e0017562ef451f1df17eb42cfd35ee9dd28f98902204a6aafb47a2e1492e50cb0c7038000741d952e3770f9a1f9c9a6229c124e5e790121037435c194e9b01b3d7f7a2802d6684a3af68d05bbf4ec8f17021980d777691f1dfdffffff040000000000000000536a4c5054325bb3a276c4f8c7ecdccd704cf9a0e691db1364e66648672e23871de8b005816f3ec0ecda15e15dbec4e7f2be15f1e4e52e72c187deb36bd3b89d9793ea0e7c71e00025b7b9000f0025b12400114a10270000000000001976a914000000000000000000000000000000000000000088ac10270000000000001976a914000000000000000000000000000000000000000088acb2e8ae08000000001976a914ba27f99e007c7f605a8305e318c1abde3cd220ac88ac00000000010000000001014d4f50b9002361be3b82382e628e41ec29ffb5a28abec8a59ebbf4f28ba4675401000000171600142642be55762762143195a7eb3b965fcc47df9000ffffffff013b0d0000000000001600146ad03f5c84f201cbcbae045ffb9a8b9d769f83d40247304402205993d5c216bcb968c8ed1a99d67a3e4bcf54434c29ffc1bd72c49769d4814f6302200a40821effc9c1fb00d6de454b35244cb70f294b7f3971fee796b9a302f7efa1012103eadead8e5b3a54e36ad936096bd4ca8960ef753b2d81db9afb4ce7a458155e2e0000000002000000000101a4a1f7f07d0d95cd22e7039646133cb29d7b3c5ec55f86720f9cfbd0aa5d155001000000000000000002102700000000000017a91423269e856a01136288e09d8ea5049f5e64d56399872ca218000000000016001456ddc1cec4c3cf468646d6cc2d531d232d43dd4502473044022069f7f79e6c8cfe3ad43e9a5e3da0d77f93169bd4d98a9ef1f5d521340510d688022053b18b1914a40b2b60cb453d8421fa8f2d1c05c9f084b90f38092379276281c701210216243ae4738f200518bf8f0204f6b95e408aadd65f553bad98f0f8a9fd7472eb0000000002000000000102c9f48fe21843a24377835106e94e3ecee80e128027a4fa84d9371df56464b0190000000000fdffffffc9f48fe21843a24377835106e94e3ecee80e128027a4fa84d9371df56464b0190100000000fdffffff01e7170000000000001976a91459cada50314c829e19f5a7786f8ee0d4987f429d88ac0247304402204d578466dad41a451ac42a58bfb15b4ede988181ca384d3d7dfc158289dc369402205b35dd90b83ed4b02ccd300b370d57d3f919d4a100280ba9b2b68feef6f75e720121039757fd1ca6e33c3433cd8de13178dfd50f5688b688301a932cc4eba38e85e80c02473044022052dbdb28b87d8bed9edb8abc664452ad4bd85dd0c068f61a4bffa92a66748d090220674e642420b027c00dc3a0fcec74f53fdd31b9bcd76d20397ef9c8f61a45788c01210251db63b01e13ff7656cb606d732970b4760466162c83e041e018846c627760a1bab7250002000000011e43091d1b554f7e3ce175241aba175bf797996dda991bfdc34fc5cb8298ee5e010000006a473044022027af54aec1a603c1260312241f6cc27d49db4d79cb946a3fb56aff6c8e5e4132022022d3954e49699d369480624c3e8397fd361d9baffdd700d1e9882b465e523b670121021bc2b79284dca29127ed41021571fd3026157c01b4fef8824e94976592b165b3fdffffff0280d725000000000016001475f1d3e5088db87f9a336287cb55142d2062ada3ab72000000000000160014f5e5e9b3af38635bf6992f9732206d0ccb421e9ebbb7250001000000017c1194e8b17d9d26aaba8a78c2cd84490d96d5405619da3b2a9f454860b18911010000006b483045022100e1a219e719575a51e70ccfb06a1968dfd6d2055a3c46664ede399b8a3aab7f8902204b7994dec9e0a6f74e4db0d5a0a06738bd608476160c530392f8f220ad3cb4c6012103ef3f06ff6ea92d1b0a4693ed3f83ad45740b02259739e6427209cb88581416a0ffffffff0278350000000000001976a914e830afe90f87e5c561df7e66ef530d4dd8ea182088ac55d61300000000001976a914fe0f6279060ba6bc6a8b4375d7de8cf8b35a683788ac00000000',
25
+ outputs: ['00146ad03f5c84f201cbcbae045ffb9a8b9d769f83d4'],
26
+ },
27
+ description: 'A block is queried for transaction outputs',
28
+ expected: {
29
+ outputs: [{
30
+ script: '00146ad03f5c84f201cbcbae045ffb9a8b9d769f83d4',
31
+ tokens: 3387,
32
+ transaction_id: 'e3a79c31b81c2852ff7eaf170900842e8060dba9afc1613f09a4b387793d56e0',
33
+ transaction_vout: 0,
34
+ }],
35
+ },
36
+ },
37
+ ];
38
+
39
+ tests.forEach(({args, description, error, expected}) => {
40
+ return test(description, (t, end) => {
41
+ if (!!error) {
42
+ throws(() => queryTransactions(args), new Error(error), 'Got err');
43
+ } else {
44
+ const res = queryTransactions(args);
45
+
46
+ deepStrictEqual(res, expected, 'Got expected result');
47
+ }
48
+
49
+ return end();
50
+ });
51
+ });
@@ -1,4 +1,11 @@
1
1
  const componentsOfTransaction = require('./components_of_transaction');
2
+ const noLocktimeIdForTransaction = require('./no_locktime_id_for_transaction');
2
3
  const parseTransaction = require('./parse_transaction');
4
+ const queryTransactions = require('./query_transactions');
3
5
 
4
- module.exports = {componentsOfTransaction, parseTransaction};
6
+ module.exports = {
7
+ componentsOfTransaction,
8
+ noLocktimeIdForTransaction,
9
+ parseTransaction,
10
+ queryTransactions,
11
+ };
@@ -1,6 +1,6 @@
1
1
  const {createHash} = require('crypto');
2
2
 
3
- const {parseTransaction} = require('./../transactions');
3
+ const parseTransaction = require('./parse_transaction');
4
4
 
5
5
  const bufferAsHex = buffer => buffer.toString('hex');
6
6
  const sha256 = preimage => createHash('sha256').update(preimage).digest();
@@ -19,7 +19,7 @@ const times = n => [...Array(n).keys()];
19
19
 
20
20
  {
21
21
  buffer: <Data Buffer Object>
22
- is_terminating_after_outputs: <Ignore Data After Outputs Bool>
22
+ [is_terminating_after_outputs]: <Ignore Data After Outputs Bool>
23
23
  [start]: <Starting Offset Index Number>
24
24
  }
25
25
 
@@ -0,0 +1,76 @@
1
+ const {createHash} = require('crypto');
2
+
3
+ const {idForTransactionComponents} = require('./../hashes');
4
+ const parseTransaction = require('./parse_transaction');
5
+
6
+ const bufferAsHex = buffer => buffer.toString('hex');
7
+ const hexAsBuffer = hex => Buffer.from(hex, 'hex');
8
+ const {isArray} = Array;
9
+ const transactionsStartIndex = 81;
10
+
11
+ /** Find matching transactions within a block
12
+
13
+ {
14
+ block: <Hex Encoded Raw Block String>
15
+ outputs: [<Output Script Hex String>]
16
+ }
17
+
18
+ @throws
19
+ <Error>
20
+
21
+ @returns
22
+ {
23
+ outputs: [{
24
+ script: <Output Script Hex String>
25
+ tokens: <Output Value Number>
26
+ transaction_id: <Transaction Id Hex String>
27
+ transaction_vout: <Transaction Output Index Number>
28
+ }]
29
+ }
30
+ */
31
+ module.exports = ({block, outputs}) => {
32
+ if (!block) {
33
+ throw new Error('ExpectedHexEncodedBlockToQueryTransactions');
34
+ }
35
+
36
+ if (!isArray(outputs)) {
37
+ throw new Error('ExpectedArrayOfOutputsToQueryTransactionsInBlock');
38
+ }
39
+
40
+ const buffer = hexAsBuffer(block);
41
+ let cursor = transactionsStartIndex;
42
+ const hits = [];
43
+
44
+ // Iterate forward through the block, parsing txs and looking for hits
45
+ while (cursor < buffer.length) {
46
+ const tx = parseTransaction({buffer, start: cursor});
47
+
48
+ // Look at the transaction outputs to see if there is a match
49
+ const matches = tx.outputs.forEach((output, vout) => {
50
+ // Exit early when the script isn't included in the outputs
51
+ if (!outputs.includes(bufferAsHex(output.script))) {
52
+ return;
53
+ }
54
+
55
+ // Calculate the transaction id
56
+ const {id} = idForTransactionComponents({
57
+ inputs: tx.inputs,
58
+ locktime: tx.locktime,
59
+ outputs: tx.outputs,
60
+ version: tx.version,
61
+ });
62
+
63
+ // Add the output to found hits
64
+ return hits.push({
65
+ script: bufferAsHex(output.script),
66
+ tokens: output.tokens,
67
+ transaction_id: id,
68
+ transaction_vout: vout,
69
+ });
70
+ });
71
+
72
+ cursor += tx.bytes.length;
73
+ }
74
+
75
+ return {outputs: hits};
76
+ };