Skip to content

Commit d7d6086

Browse files
committed
Parse even more transaction details including witnes data
1 parent a38c8c4 commit d7d6086

File tree

14 files changed

+594
-52
lines changed

14 files changed

+594
-52
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ transaction
44
*.dat
55
*.d
66
*.o
7+
*.dSYM
78
include/hexxer.hpp
89
include/ranger.hpp
910
include/serial.hpp

file.hex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
0100000000010a9991f26a84a9fc2635c67d7f125d27cf63f4136810012c5ea1376d88f9aae2b114000000fdfd0000483045022100fe87f33f029600635cfe847ef3ea502c4e3e2f239a3dc9d27d90066fbfec0eda02207fc309a9397d0fdb08239d7f73ad5116e6fe07c5d5916e467eb3d32de88ea7180147304402202ec054c13cd3da90399da3eb10f33c944a033973f341204543935b040dc956fc022002d51cfd639d0b8b388a765dc957b28e99f1cec0caa46c3274980b607f7d9be2014c69522103a08e09c3d55e0ee8db0d0baed7d869908c4cd8a604e2d537d70c2ae16e8bb81c2102ec192f140cf311cde03f3227090e564aad39eed468ea04622acb02def3025df5210219bc4cc344a3deb5b2e6fc1b67fc5c61bbc560d00ea4d1d2b9a959e0ef9c8b3153aeffffffff5c81fa34d59931ea7db968027e0157e4cca4642e69d71e128432c71b44e307df27000000fdfe0000483045022100a182f7f98da8d5a1e2db8cdf8952eb0d78a3ed9833a311a34b99a202eb4e4052022008769440f2de538ffbc89cd74f56725ff7ea7e4888b5066cddc63d84e7ec6ca501483045022100ca7bb0b7af080f5880fd2d147ae6238985499c0038dae291935765d7f088198c02202e481dd22c386ee5cbb72d34f70abca7dab0a96280fb432219a9ef02c4e16fd5014c69522103a08e09c3d55e0ee8db0d0baed7d869908c4cd8a604e2d537d70c2ae16e8bb81c2102ec192f140cf311cde03f3227090e564aad39eed468ea04622acb02def3025df5210219bc4cc344a3deb5b2e6fc1b67fc5c61bbc560d00ea4d1d2b9a959e0ef9c8b3153aeffffffff5b9adc2347a74260fe3a0b9ebd500be6df99fed4436401810f6495fbd253a182010000002322002096cffb35a760010b542cc2b6ed69451427629519a9d76c0f534f02e1c15939faffffffff71ebe0c40cf256c7ca9ca1fe4f980104d9b48d16ddaecf001bf01a1d6fc6a193010000002322002077904dca5fe0432edec7499e8e9b7fddfe0f896eecf358037091ac2e44c5e1cfffffffff51bd8921cb8751bf2a1a404428a2d36d9c099ce8a7fa3d3af56d264955d3d00c000000002322002021605811d7039d5dabe4f74975dee0634c9818075aa0e97cf2a9dff48a1e4a84ffffffff8ba88d286caa0253928924209a980926ed733ce190e07f424ef71c9c3d0c176b0100000023220020ef19dd92f7e265a8f409339a42fd44706720173aa4d8b51dedd15d8514d03488ffffffff4db0f15f9b639abd594f2213238f4a6f225c0180230749e16569c6951eaddb0101000000232200209f87a64b6b19839450a1d699b573b0d5eb1e1d40d99d10b7695dc066a3c82b1affffffffe2fe57375d35ffd517a51d4506438aa7be8e0c205085ecc596affc9657f0083401000000232200200c1fad806a89e554e46cc8107a127438ca09881acac46b5007bb6378f139e3ecffffffff16e4e97618f035a962d26650fadd7b2f523489b075d680c2b4c3b29611b6a3900100000023220020fc6ef235c51a7faf1394d363cad6c77ceac6eb50d78f9c77bcfb9b23724ffe1bffffffff061a63cfa2495c017d81989d1ba211f532eca8d5d39a3677fa2e850ad44a08b200000000232200206f0dc7cd1494b54a92d1386ec7f6520ccb34148a6dad33d5ff90d01ab101aa93ffffffff0210af4c010000000017a914dac5daeffb40a32530c78c86593fcd41309301aa87659280080000000017a914e6011e343729246a314d838c7cc7b1765b88589c870000040047304402207f93e5c7a0c35c76c5413ede651af52ab359d36872db0b12babeef0ba0940f8502201321fb2776dda4a0d362a32f01952fe2ed4847fca3ec94e502d599681fc02bda014630430220442eb6c338eb329e8115bd369ee7af4caa75851582177eee33cafa966dddec2a021f540383660ae9485ff7b8f85655350d889c0f4537448ff763e8af870d5ba8c6016952210349f84131e087f7b5f9ead0739859b2fe08f29c9d9285c34a05e607aaec039b7d210290a0a6d9e29fc8cf7e1c97004714229a5c7eaf1899446c16e8aabddc3ff5c7e5210357ebfc9d86fb32ed6f44733fdfd52ce9a2ef2f43cc338edfbd81a1d4c2736cd053ae0400483045022100867bfb2f793861b9de8f6711c21758bd5eb35d0837bfd5110f0b8e5f4cd62c2d022070279172e0e8138387f9826410736be647399ea41d66c0f5867d551e1221f0710147304402203c3d7dcfe119ad09b09db95e7da37dd897e73ee4f33737fd264d6769011284610220306bdc5ebd55651f3c8aa82de32ff2e81b05a81b5a9903c6d2f2836844c6263601695221032df1b2888f447bba0406cab06f9c5cd8131b02154127c9998b22842f1c1010f221034cd4ea2091118337719ef8eb2b4749d679b2d2c00e3ca605484f34ee8f6cfb26210270e32947541b65e3af6e39944ac2ee0980135ad5a0f506ee8c5eeb0e8fbc9bff53ae0400483045022100cb72668637d5f02ddb0e7ca493168b42cbe16bbcc5549a70f031b7878d04055f02201edcb5103ccacae534d95fe4c109f7869b40cce4caee5783a1a0457e2c13c8da0147304402202443637512d7a5312bce0fe97f788d174b2c0df1e2625eced2fe537525cfcbed02206c2f4f5bc50f9834bd3454f7d443c8572e925d3c5e8de8eee76ca9593dc302bc016952210247dce01eafbb6f9eef699a8322101ba099d28f81af93d4cf94acc5e207c2fe022103e9b0db8c8186901ae9b0862d4f57f5407c1145a60b5f75b4d11f368fe892a2f82103183a4c89825160e27703251898ed880791d8b235138a5d9f1e21cf47857413f253ae0400483045022100d7a5c3b553e8b4a4a1531e1c527513f7710412fad1117b4c8d5154141c9f4410022038aab81351486a24fd8e18be35868554999c1d9a56dfa893aae5544ecab6d22a0147304402205b0e6fdec7621baac8a4158d393f727df02c31435447993284197b10edb137f802204f744f54294c3f0fcf31ec50cb3d6664a246db200c5540194a90a784f913ca320169522102943605168d89f020768ba865e4e713501abbd92cb3503924fec5d74966feaa342103c2d1df53949b7e6d1fbc8c657cf69f777258a2609a20ab1a63a195e9ccd5e4a521028cbe45cd3c5b5c30c392f4e009338673ca42f1f37f6283d1a493cf2e30e7f79153ae04004730440220437a732628c747e31e4086c13db0e71a3e0a69e19b81da7927fc53b8bd43f8bb02207e78d5fd26ccf1001005fdae271ac397b6eb00626b4224ca4d623732d1b3a51101483045022100e84c9e39ec49b0e6d1d8e814fb15e88d6dc621db9f9fbaede53ccce775b6c0e10220569d09d045cff5e3962a5ddee46fd71aba09dffe13ab1076943bf1719a1171d0016952210392ac6518644e302bd5f98d7f54c4c0e46e3df6afa66721afc0989f3080d09e952103b726b6ecb71c050600e6944bb645c23c800fe6437481a220e2c9d4af448008ac2102d26bbef0fd5b5cefdd902bd8909cf93937a6d31947d31346a0b9895e2d70a12153ae0400473044022027eee35d7b9b6ab3cd41e29226176160b6aeb40b1ed4991418dde08ce32a09b502205419c5857455eb623d3ede43e20ff3b189aa92e0d03da6294b6149883f407c760147304402205277b81f9cffd7cacbe6ce97cc16a9cc89ba4e3422a4f1917a100e942310c97e022043c75549be6f490da3b6aeb903551efbd0a643e6804af070983cd27833fa90ad0169522102f975ec79781f2d03ee83c245ec42b204add58a7568174708e2954bf6893ac2982102fc6c062765e14224e5582639cfad88993a2f90ec5dff3d0a111539a79a2004ac2102421a1f2bea99cfc428ca25fc787a9136a80ee6a8dd7cff319c51b249b3e681a153ae040047304402202f2a584df3d931212cac1c4902f7ce1be177b7bd01c1897ccfdf8c9f1cfafd1002207e7a783ad17b90a396f06035a8e20753ff23bfc18e8eb99fc40a0f413754afbb01483045022100dedcf5739a81ce3d45e7bf947d47507d974861297e1a9d54031be97cb95f1b6602207bfef6e93e1729f1a07141fc2fc71ac4e81da719b762e9e8ca5cb327855438e40169522103f84d3ed765a5a4f71648aa29d70fe6ddd21366de8a3561e7a1b69bc40ddbaa942103249602461065218c3353caa0844afabe4423338655fb5a3c558d93ccc3033a8b21032bca69ad5e4114ddea86ea48392b88b7d2866fbd46f0a36d1683547c46414e6f53ae04004830450221009e2a98a3799853674d04cc4f01d87e8fc5c9803376ff84b88909232c985f8d6102203892ff2ef065e1e72a0d14af63d9ac668db202e1cb20e61dda7fc9fd6a14e74d01473044022009382f08aec443e701eaf2193dcc8eaa3d8a6d8e586245ea56adac650ad0818102201edfcb2717324fd59b4331b0784e4610d4d2ac2007bd0f86aa50357264002a890169522102bec47aef1a13b748c8e43f02d96e363baf399c467531dadaa5edd5d892e1961621032ef35e070b4158cc6e444cfc2b8df3068f76d136accec8520cf63df9c50999dd210354f2dc243b60fc2fcabc401f55d98cec966d5e44b690b943587b54ca8034409c53ae00000000

include/bitcoin-ops.hpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ enum OP {
139139

140140
// Magic operators
141141
OP_NOWITNESS = 0xe0,
142-
OP_WITNESS = 0xe1, // This means we must lookup the witness type from the previous transaction
143142
OP_P2WPKH = 0xe2,
144143
OP_P2WSH = 0xe3,
145144

@@ -272,7 +271,6 @@ auto getOpString (const uint8_t opcode) {
272271

273272
// magic operators
274273
case OP_NOWITNESS : return "OP_NOWITNESS";
275-
case OP_WITNESS : return "OP_WITNESS";
276274
case OP_P2WPKH : return "OP_P2WPKH";
277275
case OP_P2WSH : return "OP_P2WSH";
278276

include/bitcoin.hpp

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <cassert>
66
#include <cstdint>
77
#include <vector>
8+
#include <iostream>
89

910
#include "hash.hpp"
1011
#include "hexxer.hpp"
@@ -18,7 +19,8 @@ struct TransactionBase {
1819
R data;
1920
R hash;
2021
uint32_t vout;
21-
R script;
22+
R script;
23+
std::vector<R> scriptStack;
2224
uint32_t sequence;
2325
uint8_t witnessFlag; // OP_NOWITNESS means not a witness program
2426
};
@@ -30,8 +32,8 @@ struct TransactionBase {
3032
};
3133

3234
struct Witness {
33-
R data;
34-
std::vector<R> stack;
35+
R data;
36+
std::vector<R> stack;
3537
};
3638

3739
R data;
@@ -111,18 +113,57 @@ namespace {
111113
const auto hash = readRange(data, 32);
112114
const auto vout = serial::read<uint32_t>(data);
113115

114-
const auto scriptLen = readVI(data);
115-
const auto script = readRange(data, scriptLen);
116-
const auto sequence = serial::read<uint32_t>(data);
117-
isave.popBackN(data.size());
116+
const auto scriptLen = readVI(data);
117+
118+
__ranger::Range scriptRanger(data.begin(), data.begin() + scriptLen);
119+
120+
std::vector<R> scriptStack;
121+
122+
std::cerr << "Script bytes: " << scriptRanger.size() << "\n";
123+
124+
while(scriptRanger.size()) {
125+
126+
auto opcode = serial::read<uint8_t>(scriptRanger);
127+
128+
if(!opcode || opcode > OP_PUSHDATA4)
129+
continue;
130+
131+
auto size = readPD(opcode, scriptRanger);
132+
133+
std::cerr << "Read size: " << size << "\n";
134+
135+
scriptStack.push_back(readRange(scriptRanger, size));
136+
137+
std::cerr << "Remaining script bytes: " << scriptRanger.size() << "\n";
138+
}
139+
140+
const auto script = readRange(data, scriptLen);
141+
142+
const auto sequence = serial::read<uint32_t>(data);
143+
isave.popBackN(data.size());
144+
145+
146+
// std::vector<uint8_t> scriptData(script.begin(), script.end());
147+
// auto scriptItr = range(scriptData);
148+
//
149+
// while(scriptData.size()) {
150+
//
151+
// auto size = readVI(scriptItr);
152+
//
153+
// auto item = readRange(scriptItr, size);
154+
//
155+
// std::vector<uint8_t> item2(item.begin(), item.end());
156+
//
157+
// scriptStack.push_back(item2);
158+
// }
118159

119160
uint8_t witnessFlag = OP_NOWITNESS;
120161

121162
if(hasWitnesses) {
122163

123164
if(script.size() == 0) {
124165

125-
witnessFlag = OP_WITNESS;
166+
witnessFlag = OP_ERROR;
126167
}
127168
else if(script.size() >= 3) {
128169

@@ -138,7 +179,7 @@ namespace {
138179
}
139180
}
140181

141-
inputs.emplace_back(typename Transaction::Input{isave, hash, vout, script, sequence, witnessFlag});
182+
inputs.emplace_back(typename Transaction::Input{isave, hash, vout, script, scriptStack, sequence, witnessFlag});
142183
}
143184

144185
const auto nOutputs = readVI(data);
@@ -162,6 +203,14 @@ namespace {
162203
wsave.popBackN(data.size());
163204

164205
witnesses.emplace_back(typename Transaction::Witness{wsave, std::move(stack)});
206+
207+
if(inputs[i].witnessFlag == OP_ERROR) {
208+
209+
if(witnesses.back().stack.size() == 2)
210+
inputs[i].witnessFlag = OP_P2WPKH;
211+
else if(witnesses.back().stack.size() > 2)
212+
inputs[i].witnessFlag = OP_P2WSH;
213+
}
165214
}
166215
}
167216

src/transaction.cpp

Lines changed: 73 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@
1414

1515
#include "statistics.hpp"
1616

17+
// returns 0xff on failure
1718
static uint8_t parseHex(uint8_t character);
19+
1820
static uint8_t getHex(uint8_t character);
21+
1922
static void streamHex(std::ostream &out, uint8_t *ptr, size_t len);
20-
static void streamScript(std::ostream &out, uint8_t *ptr, size_t len);
23+
24+
static bool streamScript(std::ostream &out, uint8_t *ptr, size_t len);
2125

2226
#define MAX_TRANSACTION_SIZE 1024 * 1024 * 30
2327

@@ -39,13 +43,7 @@ int main (int argc, char** argv) {
3943
return 1;
4044
}
4145

42-
if(*(uint32_t*)buffer.data() != 0x01000000 && *(uint32_t*)buffer.data() != 0x02000000) {
43-
44-
if(strncmp((char*)buffer.data(), "01000000", 8) && strncmp((char*)buffer.data(), "02000000", 8)) {
45-
46-
std::cerr << "Transaction does not have needed 'version 1 or 2' flag.\n";
47-
return 1;
48-
}
46+
if(parseHex(buffer[0]) != 0xff && parseHex(buffer[1]) != 0xff) {
4947

5048
// ASCII hex data has been provided -- let's parse it into binary form.
5149

@@ -55,16 +53,24 @@ int main (int argc, char** argv) {
5553
bufSize /= 2;
5654
}
5755

56+
if(*(uint32_t*)buffer.data() != 0x00000001 && *(uint32_t*)buffer.data() != 0x00000002) {
57+
58+
std::cerr << "Transaction does not have needed 'version 1 or 2' flag.\n";
59+
std::cerr << "We will attempt to read it as if it were a script and then abort.\n";
60+
61+
streamScript(std::cout, buffer.data(), bufSize);
62+
std::cout << "\n";
63+
64+
return 1;
65+
}
66+
5867
auto data = ptr_range(buffer).take(bufSize);
5968

6069
auto transaction = readTransaction(data);
6170

6271
std::cout << "\n";
6372

64-
std::cout << "Transaction version: " << transaction.version << "\n";
65-
std::cout << "Transaction inputs: " << transaction.inputs.size() << "\n";
66-
std::cout << "Transaction outputs: " << transaction.outputs.size() << "\n";
67-
std::cout << "Transaction locktime: " << transaction.locktime << "\n";
73+
std::cout << "Transaction version: " << transaction.version << ", locktime: " << transaction.locktime << "\n";
6874

6975
std::cout << "\n";
7076

@@ -82,16 +88,39 @@ int main (int argc, char** argv) {
8288

8389
std::cout << "\tV-out: " << input.vout << "\n";
8490

85-
std::cout << "\tWitness flag: " << getOpString(input.witnessFlag) << "\n";
91+
bool outputScript = false;
8692

87-
if(input.script.size()) {
93+
if(input.scriptStack.size()) {
8894

8995
std::cout << "\tScript: ";
90-
streamScript(std::cout, input.script.data(), input.script.size());
96+
outputScript = streamScript(std::cout, input.scriptStack.back().begin(), input.scriptStack.back().size());
9197
std::cout << "\n";
98+
99+
if(outputScript && input.scriptStack.size() > 1) {
100+
101+
std::cout << "\tScript inputs:\n";
102+
103+
for(size_t i = 0; i < input.scriptStack.size() - 1; i++) {
104+
105+
std::cout << "\t[";
106+
streamHex(std::cout, input.scriptStack[i].begin(), input.scriptStack[i].size());
107+
std::cout << "]\n";
108+
}
109+
}
92110
}
93111

94-
std::cout << "\tSequence: " << input.sequence << "\n";
112+
if(!outputScript && input.script.size()) {
113+
114+
std::cout << "\tRaw script: ";
115+
streamScript(std::cout, input.script.begin(), input.script.size());
116+
std::cout << "\n";
117+
}
118+
119+
std::cout << "\tSequence: " << input.sequence << " aka. 0x";
120+
streamHex(std::cout, (uint8_t*)&input.sequence, sizeof(input.sequence));
121+
std::cout << "\n";
122+
123+
std::cout << "\tWitness flag: " << getOpString(input.witnessFlag) << "\n";
95124

96125
if(counter - 1 < transaction.witnesses.size()) {
97126

@@ -105,39 +134,39 @@ int main (int argc, char** argv) {
105134
}
106135
else {
107136

108-
std::cout << "\tWitness signature: ";
109-
streamHex(std::cout, witness.stack[0].data(), witness.stack[0].size());
110-
std::cout << "\n";
137+
std::cout << "\tWitness pubkey: [";
138+
streamHex(std::cout, witness.stack[1].begin(), witness.stack[1].size());
139+
std::cout << "]\n";
111140

112-
std::cout << "\tWitness pubkey: ";
113-
streamHex(std::cout, witness.stack[1].data(), witness.stack[1].size());
114-
std::cout << "\n";
141+
std::cout << "\tWitness signature: [";
142+
streamHex(std::cout, witness.stack[0].begin(), witness.stack[0].size());
143+
std::cout << "]\n";
115144
}
116145
}
117146
else if(input.witnessFlag == OP_P2WSH) {
118147

119-
if(transaction.witnesses.size() == 0) {
148+
if(witness.stack.size() == 0) {
120149

121150
std::cerr << "\tOP_P2WSH script has no witness elements!\n";
122151
}
123152
else {
124153

125-
std::cout << "\tWitness script input stack:";
154+
auto stack = witness.stack;
155+
156+
std::cout << "\tWitness script: ";
157+
streamScript(std::cout, stack.back().begin(), stack.back().size());
158+
std::cout << "\n";
126159

127160
size_t i = 0;
128161

162+
std::cout << "\tWitness script inputs: ";
163+
129164
for(; i < witness.stack.size() - 1; i++) {
130165

131-
std::cout << " [";
132-
streamHex(std::cout, witness.stack[i].data(), witness.stack[i].size());
133-
std::cout << "]";
166+
std::cout << (i ? "\t[" : "[");
167+
streamHex(std::cout, stack[i].begin(), stack[i].size());
168+
std::cout << "]\n";
134169
}
135-
136-
std::cout << "\n";
137-
138-
std::cout << "\tWitness script: ";
139-
streamHex(std::cout, witness.stack[i].data(), witness.stack[i].size());
140-
std::cout << "\n";
141170
}
142171
}
143172
else if(input.witnessFlag != OP_NOWITNESS) {
@@ -147,7 +176,7 @@ int main (int argc, char** argv) {
147176
for(auto data : transaction.witnesses[counter - 1].stack) {
148177

149178
std::cout << "\tWitness item: ";
150-
streamHex(std::cout, data.data(), data.size());
179+
streamHex(std::cout, data.begin(), data.size());
151180
std::cout << "\n";
152181
}
153182
}
@@ -166,7 +195,9 @@ int main (int argc, char** argv) {
166195
streamScript(std::cout, output.script.data(), output.script.size());
167196
std::cout << "\n";
168197

169-
std::cout << "\tValue: " << double(output.value) / 100000000 << "\n";
198+
std::cout << "\tValue: " << output.value / 100000000 << ".";
199+
200+
printf("%08llu\n", output.value % 100000000);
170201
}
171202

172203
std::cout << "\n";
@@ -185,7 +216,7 @@ static uint8_t parseHex(uint8_t character)
185216
if(character >= 'A' && character <= 'F')
186217
return uint8_t(character - 'A' + 10);
187218

188-
return 0;
219+
return 0xff;
189220
}
190221

191222
static uint8_t getHex(uint8_t character)
@@ -205,7 +236,7 @@ static void streamHex(std::ostream &out, uint8_t *ptr, size_t len)
205236
out << getHex(uint8_t(ptr[i] >> 4)) << getHex(ptr[i] & 0x0f);
206237
}
207238

208-
static void streamScript(std::ostream &out, uint8_t *ptr, size_t len)
239+
static bool streamScript(std::ostream &out, uint8_t *ptr, size_t len)
209240
{
210241
for(size_t i = 0; i < len; i++) {
211242

@@ -237,16 +268,16 @@ static void streamScript(std::ostream &out, uint8_t *ptr, size_t len)
237268
if(dataSize == OP_PUSHDATA1 || dataSize == OP_PUSHDATA2 || dataSize == OP_PUSHDATA4) {
238269

239270
out << "Script encountered invalid OP_PUSHDATA -- aborting parse.";
240-
return;
271+
return false;
241272
}
242273

243274
if(dataSize + i > len) {
244275

245276
out << "Script encountered push data larger than script -- aborting parse.";
246-
return;
277+
return false;
247278
}
248279

249-
out << "PUSH [";
280+
out << "PUSH(" << dataSize << ") [";
250281
streamHex(out, ptr + i, dataSize);
251282
out << "] ";
252283

@@ -257,4 +288,6 @@ static void streamScript(std::ostream &out, uint8_t *ptr, size_t len)
257288
out << getOpString(ptr[i]) << " ";
258289
}
259290
}
291+
292+
return true;
260293
}

0 commit comments

Comments
 (0)