editor: another undo fix
[iv.d.git] / btc_expers / btcblock.d
blob011c24001a3e2ea97b8ba3aa93fed2c3b74fc190
1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 module btcblock is aliced;
20 // ////////////////////////////////////////////////////////////////////////// //
21 public struct MMapFile {
22 private:
23 static struct SharedData {
24 int rc; // refcounter
25 int fd; // file descriptor
26 char* fname; // 0-terminated, 'cause why not?
27 ubyte* mbuf; // mmaped
28 uint mbufsize;
29 public:
30 // `rc` won't be touched; throws on errors
31 void setup (const(char)[] afname) @trusted {
32 //import core.sys.posix.sys.mman;
33 import core.sys.linux.sys.mman;
34 import core.sys.posix.fcntl;
35 import core.sys.posix.unistd;
36 import core.stdc.stdio : SEEK_CUR, SEEK_END, SEEK_SET;
37 import std.internal.cstring;
38 fd = core.sys.posix.fcntl.open(afname.tempCString, O_RDONLY|O_CLOEXEC|O_DIRECT/*|O_NOATIME*/);
39 if (fd < 0) throw new Exception("cannot open file '"~afname.idup~"'");
40 scope(failure) { core.sys.posix.unistd.close(fd); fd = -1; }
41 auto size = lseek(fd, 0, SEEK_END);
42 if (size > uint.max/4) throw new Exception("file '"~afname.idup~"' too big");
43 if (lseek(fd, 0, SEEK_SET) == cast(off_t)-1) throw new Exception("seek error in file '"~afname.idup~"'");
44 mbufsize = cast(uint)size;
45 if (mbufsize > 0) {
46 mbuf = cast(ubyte*)mmap(null, mbufsize, PROT_READ, MAP_PRIVATE, fd, 0);
47 if (mbuf is null) throw new Exception("cannot mmap file '"~afname.idup~"'");
49 scope(failure) { munmap(mbuf, mbufsize); mbuf = null; }
50 if (afname.length) {
51 import core.stdc.stdlib : malloc;
52 import core.stdc.string : memcpy;
53 fname = cast(char*)malloc(afname.length+1);
54 if (fname is null) assert(0, "out of memory");
55 memcpy(fname, afname.ptr, afname.length);
56 fname[afname.length] = 0;
57 } else {
58 fname = null;
62 // `fd` and `rc` must be already deinitialized
63 void clear () nothrow @trusted @nogc {
64 if (fname !is null) {
65 import core.stdc.stdlib : free;
66 free(fname);
67 fname = null;
69 if (mbuf !is null) {
70 import core.sys.linux.sys.mman;
71 munmap(mbuf, mbufsize);
72 mbuf = null;
74 mbufsize = 0;
75 if (fd >= 0) {
76 import core.sys.posix.unistd;
77 core.sys.posix.unistd.close(fd);
78 fd = -1;
83 private:
84 usize sdptr;
86 private:
87 void addref () nothrow @trusted @nogc {
88 pragma(inline, true);
89 if (sdptr) ++(cast(SharedData*)sdptr).rc;
92 void decref () {
93 if (sdptr) {
94 if (--(cast(SharedData*)sdptr).rc == 0) {
95 import core.stdc.stdlib : free;
96 (cast(SharedData*)sdptr).clear();
97 free(cast(void*)sdptr);
98 sdptr = 0;
103 public:
104 this (const(char)[] fname) { open(fname); }
105 this() (in auto ref MMapFile afl) { sdptr = afl.sdptr; addref(); }
106 this (this) { addref(); }
107 ~this () { decref(); }
109 void opAssign() (in auto ref MMapFile afl) {
110 pragma(inline, true);
111 // order matters!
112 if (afl.sdptr) ++(cast(SharedData*)afl.sdptr).rc;
113 decref();
114 sdptr = afl.sdptr;
117 void open (const(char)[] fname) {
118 import core.stdc.stdlib : calloc, free;
119 auto sd = cast(SharedData*)calloc(1, SharedData.sizeof);
120 if (sd is null) assert(0, "out of memory");
121 scope(failure) free(sd);
122 sd.rc = 1;
123 sd.setup(fname);
124 sdptr = cast(usize)sd;
127 void close () { decref(); }
129 @property bool isOpen () const pure nothrow @safe @nogc { pragma(inline, true); return (sdptr != 0); }
131 @property uint size () const nothrow @trusted @nogc { pragma(inline, true); return (sdptr ? (cast(const(SharedData)*)sdptr).mbufsize : 0); }
133 alias length = size;
134 alias opDollar = size;
136 const(ubyte)[] opSlice () @trusted {
137 if (!sdptr) return null;
138 auto sd = cast(SharedData*)sdptr;
139 return sd.mbuf[0..sd.mbufsize];
142 const(ubyte)[] opSlice (usize lo, usize hi) @trusted {
143 if (!sdptr || lo >= hi) return null;
144 auto sd = cast(SharedData*)sdptr;
145 if (hi > sd.mbufsize) throw new Exception("invalid index");
146 return sd.mbuf[lo..hi];
151 // ////////////////////////////////////////////////////////////////////////// //
152 public string bin2hex(string mode="BE") (const(ubyte)[] buf) @trusted nothrow {
153 static assert(mode == "BE" || mode == "LE", "invalid mode: '"~mode~"'");
154 static immutable string hexd = "0123456789abcdef";
155 if (buf.length == 0) return null;
156 auto res = new char[](buf.length*2);
157 auto d = res.ptr;
158 static if (mode == "LE") {
159 foreach (immutable ubyte b; buf[]) {
160 *d++ = hexd.ptr[b>>4];
161 *d++ = hexd.ptr[b&0x0f];
163 } else {
164 foreach (immutable ubyte b; buf[]; reverse) {
165 *d++ = hexd.ptr[b>>4];
166 *d++ = hexd.ptr[b&0x0f];
169 return cast(string)res;
173 // ////////////////////////////////////////////////////////////////////////// //
174 public struct MemBuffer {
175 const(ubyte)[] membuf;
176 uint ofs;
178 this (const(void)[] abuf) pure nothrow @safe @nogc {
179 membuf = cast(const(ubyte)[])abuf;
182 @property usize length () const pure nothrow @safe @nogc { pragma(inline, true); return (ofs < membuf.length ? membuf.length-ofs : 0); }
184 @property bool empty () const pure nothrow @safe @nogc { pragma(inline, true); return (ofs >= membuf.length); }
186 void clear () pure nothrow @safe @nogc { pragma(inline, true); membuf = null; ofs = 0; }
188 T getvl(T, bool dochecks=true) () @trusted if (is(T == byte) || is(T == ubyte) || is(T == short) || is(T == ushort) || is(T == int) || is(T == uint) || is(T == long) || is(T == ulong)) {
189 import core.stdc.string : memcpy;
190 static if (dochecks) if (ofs >= membuf.length) throw new Exception("malformed block");
191 ulong v = membuf.ptr[ofs++];
192 if (v == 0xFD) {
193 static if (dochecks) if (membuf.length-ofs < 3) throw new Exception("malformed block");
194 version(BigEndian) {
195 v = membuf.ptr[ofs]|(membuf.ptr[ofs+1]<<8);
196 } else {
197 memcpy(&v, membuf.ptr+ofs, 2);
199 ofs += 2;
200 } else if (v == 0xFE) {
201 static if (dochecks) if (membuf.length-ofs < 5) throw new Exception("malformed block");
202 version(BigEndian) {
203 v = membuf.ptr[ofs]|(membuf.ptr[ofs+1]<<8)|(membuf.ptr[ofs+2]<<16)|(membuf.ptr[ofs+3]<<24);
204 } else {
205 memcpy(&v, membuf.ptr+ofs, 4);
207 ofs += 4;
208 } else if (v == 0xFF) {
209 static if (dochecks) if (membuf.length-ofs < 9) throw new Exception("malformed block");
210 version(BigEndian) {
211 v = cast(ulong)(membuf.ptr[ofs]|(membuf.ptr[ofs+1]<<8)|(membuf.ptr[ofs+2]<<16)|(membuf.ptr[ofs+3]<<24))|
212 ((cast(ulong)(membuf.ptr[ofs+4]))<<32)|
213 ((cast(ulong)(membuf.ptr[ofs+5]))<<40)|
214 ((cast(ulong)(membuf.ptr[ofs+6]))<<48)|
215 ((cast(ulong)(membuf.ptr[ofs+7]))<<56);
216 } else {
217 memcpy(&v, membuf.ptr+ofs, 8);
219 ofs += 8;
221 static if (!is(T == ulong)) {
222 if (v > T.max) throw new Exception("value too big");
224 return cast(T)v;
227 T get(T, bool dochecks=true) () @trusted if (is(T == byte) || is(T == ubyte) || is(T == short) || is(T == ushort) || is(T == int) || is(T == uint) || is(T == long) || is(T == ulong)) {
228 static if (dochecks) {
229 if (ofs >= membuf.length) throw new Exception("malformed block");
230 if (membuf.length-ofs < T.sizeof) throw new Exception("malformed block");
232 version(BigEndian) {
233 static if (T.sizeof == 1) {
234 T res = *cast(const(T)*)(membuf.ptr+ofs);
235 } else static if (T.sizeof == 2) {
236 T res = cast(T)(membuf.ptr[ofs]|(membuf.ptr[ofs+1]<<8));
237 } else {
238 import core.bitop : bswap;
239 T res = bswap(*cast(const(T)*)(membuf.ptr+ofs));
241 } else {
242 T res = *cast(const(T)*)(membuf.ptr+ofs);
244 ofs += cast(uint)T.sizeof;
245 return res;
248 //FIXME: overflow checks
249 void getbuf(T) (ref T[] buf) @trusted if (is(T == byte) || is(T == ubyte) || is(T == short) || is(T == ushort) || is(T == int) || is(T == uint) || is(T == long) || is(T == ulong)) {
250 import core.stdc.string : memcpy;
251 if (ofs >= membuf.length) throw new Exception("malformed block");
252 if (membuf.length-ofs < buf.length*T.sizeof) throw new Exception("malformed block");
253 memcpy(buf.ptr, membuf.ptr+ofs, buf.length*T.sizeof);
254 ofs += cast(uint)(buf.length*T.sizeof);
257 //FIXME: overflow checks
258 const(T)[] getbufnc(T) (uint len) @trusted if (is(T == byte) || is(T == ubyte) || is(T == short) || is(T == ushort) || is(T == int) || is(T == uint) || is(T == long) || is(T == ulong)) {
259 if (ofs >= membuf.length) throw new Exception("malformed block");
260 if (membuf.length-ofs < len*T.sizeof) throw new Exception("malformed block");
261 auto res = cast(const(T)[])(membuf.ptr[ofs..ofs+len*T.sizeof]);
262 ofs += cast(uint)(len*T.sizeof);
263 return res;
266 void skipInput () @trusted {
267 if (ofs >= membuf.length) throw new Exception("malformed block");
268 // id[32]
269 if (membuf.length-ofs < 32) throw new Exception("malformed block");
270 ofs += 32;
271 // vout
272 if (membuf.length-ofs < 4) throw new Exception("malformed block");
273 ofs += 4;
274 // script
275 uint scsz = getvl!uint;
276 if (membuf.length-ofs < scsz) throw new Exception("malformed block");
277 ofs += scsz;
278 // seq
279 if (membuf.length-ofs < 4) throw new Exception("malformed block");
280 ofs += 4;
283 void skipOutput () @trusted {
284 if (ofs >= membuf.length) throw new Exception("malformed block");
285 // value
286 if (membuf.length-ofs < 8) throw new Exception("malformed block");
287 ofs += 8;
288 // script
289 uint scsz = getvl!uint;
290 if (membuf.length-ofs < scsz) throw new Exception("malformed block");
291 ofs += scsz;
294 void skipTx(bool skiplocktime=true) () @trusted {
295 if (ofs >= membuf.length) throw new Exception("malformed block");
296 // version
297 if (membuf.length-ofs < 4) throw new Exception("malformed block");
298 ofs += 4;
299 // inputs
300 uint icount = getvl!ushort;
301 while (icount-- > 0) skipInput();
302 // outputs
303 uint ocount = getvl!ushort;
304 while (ocount-- > 0) skipOutput();
305 static if (skiplocktime) {
306 if (membuf.length-ofs < 4) throw new Exception("malformed block");
307 ofs += 4;
313 // ////////////////////////////////////////////////////////////////////////// //
314 public align(1) struct BtcBlock {
315 align(1):
316 public:
317 enum Magic : uint {
318 Main = 0xD9B4BEF9U,
319 TestNet = 0xDAB5BFFAU,
320 TestNet3 = 0x0709110BU,
321 NameCoin = 0xFEB4BEF9U,
324 align(1) static struct Header {
325 align(1):
326 uint ver;
327 const(ubyte)[32] prev;
328 const(ubyte)[32] root;
329 uint time; // unix
330 uint bits;
331 uint nonce;
333 ubyte[32] decodeBits () const nothrow @trusted @nogc {
334 ubyte[32] res = 0;
335 if (bits > 0x1d00ffff) assert(0, "bits is too big");
336 int len = (bits>>24)&0xff;
337 if (len < 6 || len > 30) assert(0, "fucked block");
338 res[len-3] = bits&0xff;
339 res[len-2] = (bits>>8)&0xff;
340 res[len-1] = (bits>>16)&0xff;
341 return res;
344 string bits2str () const {
345 static immutable string hexd = "0123456789abcdef";
346 if (bits > 0x1d00ffff) assert(0, "bits is too big");
347 int len = (bits>>24)&0xff;
348 if (len < 6 || len > 30) assert(0, "fucked block");
349 char[64] res = '0';
350 int pos = cast(int)(res.length)-len*2;
351 foreach (immutable bc; 0..3) {
352 ubyte b = (bits>>((2-bc)*8))&0xff;
353 res[pos+0] = hexd[b>>4];
354 res[pos+1] = hexd[b&0x0f];
355 pos += 2;
357 return res[].idup;
361 align(1) static struct Input {
362 align(1):
363 const(ubyte)[] id; // [32]
364 uint vout;
365 const(ubyte)[] script;
366 uint seq;
369 align(1) static struct Output {
370 align(1):
371 ulong value; // satoshis
372 const(ubyte)[] script;
375 public:
376 private static struct TxInOutRange(FT, string parser) {
377 private:
378 MemBuffer mbuf;
379 uint cur, len;
380 FT crec;
382 private:
383 void xparse() () { mixin(parser); }
385 // no need to validate anything here
386 this (in ref MemBuffer abuf, uint aofs, int acount) @trusted {
387 mbuf.membuf = abuf.membuf[aofs..$];
388 len = acount;
389 if (acount > 0) xparse();
392 public:
393 @property bool empty () const pure nothrow @safe @nogc { pragma(inline, true); return (cur >= len); }
394 @property uint length () const pure nothrow @safe @nogc { pragma(inline, true); return len-cur; }
396 @property FT front () const pure nothrow @trusted @nogc { pragma(inline, true); return crec; }
398 void popFront () {
399 pragma(inline, true);
400 if (cur < len) {
401 ++cur;
402 if (cur < len) xparse();
407 public alias TxInRange = TxInOutRange!(Input, q{
408 // id[32]
409 crec.id = mbuf.getbufnc!ubyte(32);
410 // outnum
411 crec.vout = mbuf.get!uint;
412 // script
413 uint scsz = mbuf.getvl!uint;
414 crec.script = mbuf.getbufnc!ubyte(scsz);
415 // seq
416 crec.seq = mbuf.get!uint;
419 public alias TxOutRange = TxInOutRange!(Output, q{
420 // value
421 crec.value = mbuf.get!ulong;
422 // script
423 uint scsz = mbuf.getvl!uint;
424 crec.script = mbuf.getbufnc!ubyte(scsz);
428 static struct Tx {
429 private:
430 MemBuffer mbuf;
431 int icount, ocount;
432 uint iofs, oofs;
433 uint txver;
434 uint txlocktm;
436 private:
437 this (in ref MemBuffer abuf, uint atxofs) @trusted {
438 mbuf.membuf = abuf.membuf[atxofs..$];
439 scope(failure) mbuf.clear();
440 txver = mbuf.get!uint;
441 icount = mbuf.getvl!ushort;
442 iofs = mbuf.ofs;
443 //{ import core.stdc.stdio; printf("txver=%u; icount=%u; iofs=%u\n", txver, icount, iofs); }
444 foreach (immutable _; 0..icount) mbuf.skipInput();
445 ocount = mbuf.getvl!ushort;
446 oofs = mbuf.ofs;
447 //{ import core.stdc.stdio; printf(" ocount=%u; oofs=%u\n", ocount, oofs); }
448 foreach (immutable _; 0..ocount) mbuf.skipOutput();
449 txlocktm = mbuf.get!uint;
450 // drop alien data
451 mbuf.membuf = mbuf.membuf[0..mbuf.ofs];
454 public:
455 @property uint ver () const pure nothrow @safe @nogc { pragma(inline, true); return txver; }
456 @property uint locktime () const pure nothrow @safe @nogc { pragma(inline, true); return txlocktm; }
458 @property int incount () const pure nothrow @safe @nogc { pragma(inline, true); return icount; }
459 @property int outcount () const pure nothrow @safe @nogc { pragma(inline, true); return ocount; }
461 @property auto data () const pure nothrow @trusted @nogc { pragma(inline, true); return mbuf.membuf; }
463 // calculate txid; return reversed txid, 'cause this is how it is stored in inputs
464 @property ubyte[32] txid () const pure nothrow @trusted @nogc {
465 import std.digest.sha : sha256Of;
466 auto dg0 = sha256Of(mbuf.membuf);
467 dg0 = sha256Of(dg0[]);
468 version(none) {
469 foreach (immutable idx, ref ubyte b; dg0[0..16]) {
470 ubyte t = dg0.ptr[31-idx];
471 dg0.ptr[31-idx] = t;
472 b = t;
475 return dg0;
478 TxInRange inputs () const @safe { return TxInRange(mbuf, iofs, icount); }
479 TxOutRange outputs () const @safe { return TxOutRange(mbuf, oofs, ocount); }
483 static struct TxRange {
484 private:
485 MemBuffer mbuf;
486 uint txn, txe;
488 private:
489 this (in ref MemBuffer abuf, usize txlo, usize txhi) @trusted {
490 mbuf.membuf = abuf.membuf;
491 mbuf.ofs = cast(uint)BtcBlock.Header.sizeof;
492 auto txc = mbuf.getvl!uint;
493 if (txlo >= txhi || txlo >= txc) {
494 mbuf.ofs = 0;
495 } else {
496 //{ import core.stdc.stdio; printf("txlo=%u; txhi=%u; txc=%u\n", cast(uint)txlo, cast(uint)txhi, cast(uint)txc); }
497 while (txn < txlo) { mbuf.skipTx(); ++txn; }
498 txe = (txhi <= txc ? cast(uint)txhi : txc);
502 public:
503 @property bool empty () const pure nothrow @safe @nogc { pragma(inline, true); return (txn >= txe); }
504 @property uint length () const pure nothrow @safe @nogc { pragma(inline, true); return txe-txn; }
506 @property Tx front () const @trusted {
507 pragma(inline, true);
508 if (txn >= txe) assert(0, "no front element in empty range");
509 return Tx(mbuf, mbuf.ofs);
512 void popFront () {
513 pragma(inline, true);
514 if (txn < txe) {
515 ++txn;
516 mbuf.skipTx();
521 private:
522 MemBuffer mbuf;
524 public @trusted:
525 // throws on invalid data
526 static const(ubyte)[] getPackedData (const(ubyte)[] abuf) {
527 if (abuf.length < 8) throw new Exception("invalid packet size");
528 auto magic = *cast(const(uint)*)(abuf.ptr);
529 auto len = *cast(const(uint)*)(abuf.ptr+4);
530 if (magic != Magic.Main && magic != Magic.TestNet && magic != Magic.TestNet3 && magic != Magic.NameCoin) throw new Exception("invalid packet signature");
531 if (len < Header.sizeof+1 || len >= uint.max-16) throw new Exception("invalid packet size");
532 if (8+len > abuf.length) throw new Exception("invalid packet size");
533 return abuf[8..8+len];
536 // returns zero-length slice on EOF
537 static const(ubyte)[] skipPackedData (const(ubyte)[] abuf) {
538 if (abuf.length == 0) return null;
539 if (abuf.length < 8) throw new Exception("invalid packet size");
540 auto magic = *cast(const(uint)*)(abuf.ptr);
541 auto len = *cast(const(uint)*)(abuf.ptr+4);
542 if (magic != Magic.Main && magic != Magic.TestNet && magic != Magic.TestNet3 && magic != Magic.NameCoin) throw new Exception("invalid packet signature");
543 if (8+len > abuf.length || len >= uint.max-16) throw new Exception("invalid packet size");
544 if (8+len == abuf.length) return null;
545 return abuf[8+len..$];
548 static uint packedDataSize (const(ubyte)[] abuf) {
549 if (abuf.length == 0) return 0;
550 if (abuf.length < 8) throw new Exception("invalid packet size");
551 auto magic = *cast(const(uint)*)(abuf.ptr);
552 auto len = *cast(const(uint)*)(abuf.ptr+4);
553 if (magic != Magic.Main && magic != Magic.TestNet && magic != Magic.TestNet3 && magic != Magic.NameCoin) throw new Exception("invalid packet signature");
554 if (8+len > abuf.length || len >= uint.max-16) throw new Exception("invalid packet size");
555 return len+8;
558 // advance abuf offset past the block
559 this (ref MemBuffer abuf, uint amagic=Magic.Main) @trusted {
560 if (abuf.length < 8) throw new Exception("malformed block");
561 auto magic = abuf.get!uint;
562 if (magic != amagic) throw new Exception("invalid packet magic");
563 auto len = abuf.get!uint;
564 if (len >= uint.max-16 || len > abuf.length) throw new Exception("invalid packet size");
565 if (len < Header.sizeof) throw new Exception("malformed block");
566 mbuf.membuf = abuf.membuf[abuf.ofs..abuf.ofs+len];
567 mbuf.ofs = cast(uint)Header.sizeof;
568 abuf.ofs += len;
571 void clear () nothrow @nogc { pragma(inline, true); mbuf.clear(); }
573 @property bool valid () const pure nothrow @nogc { pragma(inline, true); return (mbuf.length > Header.sizeof); }
574 @property auto header () const nothrow @nogc { pragma(inline, true); return (mbuf.length > Header.sizeof ? cast(const Header*)mbuf.membuf.ptr : cast(const Header*)null); }
576 @property int txcount () const {
577 MemBuffer xmbuf = mbuf;
578 xmbuf.ofs = cast(uint)Header.sizeof;
579 return cast(int)xmbuf.getvl!ushort;
582 @property usize length () const { pragma(inline, true); return txcount; }
583 alias opDollar = length;
585 TxRange opSlice () const { pragma(inline, true); return TxRange(mbuf, 0, length); }
586 TxRange opSlice (usize lo, usize hi) const { pragma(inline, true); return TxRange(mbuf, lo, hi); }