@alexbosworth/blockchain 1.6.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 +5 -0
- package/README.md +49 -0
- package/hashes/id_for_transaction_components.js +99 -0
- package/hashes/index.js +6 -2
- package/index.js +5 -1
- package/package.json +2 -2
- package/test/hashes/test_id_for_block.js +2 -2
- package/test/hashes/test_no_locktime_id_for_transaction.js +2 -2
- package/test/transactions/test_query_transactions.js +51 -0
- package/transactions/index.js +8 -1
- package/{hashes → transactions}/no_locktime_id_for_transaction.js +1 -1
- package/transactions/parse_transaction.js +1 -1
- package/transactions/query_transactions.js +76 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
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
|
+
|
|
3
8
|
## 1.6.0
|
|
4
9
|
|
|
5
10
|
- `previousBlockId`: Add method to get the previous block id from a block
|
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
|
|
@@ -121,6 +148,28 @@ Given a raw block, return the previous block id
|
|
|
121
148
|
previous: <Previous Block Id Hex String>
|
|
122
149
|
}
|
|
123
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
|
+
|
|
124
173
|
### scriptAsScriptElements
|
|
125
174
|
|
|
126
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,5 +1,9 @@
|
|
|
1
1
|
const idForBlock = require('./id_for_block');
|
|
2
|
-
const
|
|
2
|
+
const idForTransactionComponents = require('./id_for_transaction_components');
|
|
3
3
|
const previousBlockId = require('./previous_block_id');
|
|
4
4
|
|
|
5
|
-
module.exports = {
|
|
5
|
+
module.exports = {
|
|
6
|
+
idForBlock,
|
|
7
|
+
idForTransactionComponents,
|
|
8
|
+
previousBlockId,
|
|
9
|
+
};
|
package/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
const {compactIntAsNumber} = require('./numbers');
|
|
2
2
|
const {componentsOfTransaction} = require('./transactions');
|
|
3
3
|
const {idForBlock} = require('./hashes');
|
|
4
|
-
const {
|
|
4
|
+
const {idForTransactionComponents} = require('./hashes');
|
|
5
|
+
const {noLocktimeIdForTransaction} = require('./transactions');
|
|
5
6
|
const {numberAsCompactInt} = require('./numbers');
|
|
6
7
|
const {previousBlockId} = require('./hashes');
|
|
8
|
+
const {queryTransactions} = require('./transactions');
|
|
7
9
|
const {scriptAsScriptElements} = require('./script');
|
|
8
10
|
const {scriptElementsAsScript} = require('./script');
|
|
9
11
|
|
|
@@ -11,9 +13,11 @@ module.exports = {
|
|
|
11
13
|
compactIntAsNumber,
|
|
12
14
|
componentsOfTransaction,
|
|
13
15
|
idForBlock,
|
|
16
|
+
idForTransactionComponents,
|
|
14
17
|
noLocktimeIdForTransaction,
|
|
15
18
|
numberAsCompactInt,
|
|
16
19
|
previousBlockId,
|
|
20
|
+
queryTransactions,
|
|
17
21
|
scriptAsScriptElements,
|
|
18
22
|
scriptElementsAsScript,
|
|
19
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@
|
|
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.
|
|
22
|
+
"version": "1.7.0"
|
|
23
23
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {deepEqual} = require('node:assert').strict;
|
|
2
2
|
const test = require('node:test');
|
|
3
3
|
const {throws} = require('node:assert').strict;
|
|
4
4
|
|
|
@@ -30,7 +30,7 @@ tests.forEach(({args, description, error, expected}) => {
|
|
|
30
30
|
} else {
|
|
31
31
|
const res = idForBlock(args);
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
deepEqual(res, expected, 'Got expected result');
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
return end();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {
|
|
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
|
-
|
|
51
|
+
deepEqual(res, expected, 'Got expected result');
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
return end();
|
|
@@ -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
|
+
});
|
package/transactions/index.js
CHANGED
|
@@ -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 = {
|
|
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
|
|
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
|
+
};
|