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
{
23 static struct SharedData
{
25 int fd
; // file descriptor
26 char* fname
; // 0-terminated, 'cause why not?
27 ubyte* mbuf
; // mmaped
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
;
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; }
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;
62 // `fd` and `rc` must be already deinitialized
63 void clear () nothrow @trusted @nogc {
65 import core
.stdc
.stdlib
: free
;
70 import core
.sys
.linux
.sys
.mman
;
71 munmap(mbuf
, mbufsize
);
76 import core
.sys
.posix
.unistd
;
77 core
.sys
.posix
.unistd
.close(fd
);
87 void addref () nothrow @trusted @nogc {
89 if (sdptr
) ++(cast(SharedData
*)sdptr
).rc
;
94 if (--(cast(SharedData
*)sdptr
).rc
== 0) {
95 import core
.stdc
.stdlib
: free
;
96 (cast(SharedData
*)sdptr
).clear();
97 free(cast(void*)sdptr
);
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);
112 if (afl
.sdptr
) ++(cast(SharedData
*)afl
.sdptr
).rc
;
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
);
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); }
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);
158 static if (mode
== "LE") {
159 foreach (immutable ubyte b
; buf
[]) {
160 *d
++ = hexd
.ptr
[b
>>4];
161 *d
++ = hexd
.ptr
[b
&0x0f];
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
;
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
++];
193 static if (dochecks
) if (membuf
.length
-ofs
< 3) throw new Exception("malformed block");
195 v
= membuf
.ptr
[ofs
]|
(membuf
.ptr
[ofs
+1]<<8);
197 memcpy(&v
, membuf
.ptr
+ofs
, 2);
200 } else if (v
== 0xFE) {
201 static if (dochecks
) if (membuf
.length
-ofs
< 5) throw new Exception("malformed block");
203 v
= membuf
.ptr
[ofs
]|
(membuf
.ptr
[ofs
+1]<<8)|
(membuf
.ptr
[ofs
+2]<<16)|
(membuf
.ptr
[ofs
+3]<<24);
205 memcpy(&v
, membuf
.ptr
+ofs
, 4);
208 } else if (v
== 0xFF) {
209 static if (dochecks
) if (membuf
.length
-ofs
< 9) throw new Exception("malformed block");
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);
217 memcpy(&v
, membuf
.ptr
+ofs
, 8);
221 static if (!is(T
== ulong)) {
222 if (v
> T
.max
) throw new Exception("value too big");
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");
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));
238 import core
.bitop
: bswap;
239 T res
= bswap(*cast(const(T
)*)(membuf
.ptr
+ofs
));
242 T res
= *cast(const(T
)*)(membuf
.ptr
+ofs
);
244 ofs
+= cast(uint)T
.sizeof
;
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
);
266 void skipInput () @trusted {
267 if (ofs
>= membuf
.length
) throw new Exception("malformed block");
269 if (membuf
.length
-ofs
< 32) throw new Exception("malformed block");
272 if (membuf
.length
-ofs
< 4) throw new Exception("malformed block");
275 uint scsz
= getvl
!uint;
276 if (membuf
.length
-ofs
< scsz
) throw new Exception("malformed block");
279 if (membuf
.length
-ofs
< 4) throw new Exception("malformed block");
283 void skipOutput () @trusted {
284 if (ofs
>= membuf
.length
) throw new Exception("malformed block");
286 if (membuf
.length
-ofs
< 8) throw new Exception("malformed block");
289 uint scsz
= getvl
!uint;
290 if (membuf
.length
-ofs
< scsz
) throw new Exception("malformed block");
294 void skipTx(bool skiplocktime
=true) () @trusted {
295 if (ofs
>= membuf
.length
) throw new Exception("malformed block");
297 if (membuf
.length
-ofs
< 4) throw new Exception("malformed block");
300 uint icount
= getvl
!ushort;
301 while (icount
-- > 0) skipInput();
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");
313 // ////////////////////////////////////////////////////////////////////////// //
314 public align(1) struct BtcBlock
{
319 TestNet
= 0xDAB5BFFAU
,
320 TestNet3
= 0x0709110BU
,
321 NameCoin
= 0xFEB4BEF9U
,
324 align(1) static struct Header
{
327 const(ubyte)[32] prev
;
328 const(ubyte)[32] root
;
333 ubyte[32] decodeBits () const nothrow @trusted @nogc {
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;
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");
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];
361 align(1) static struct Input
{
363 const(ubyte)[] id
; // [32]
365 const(ubyte)[] script
;
369 align(1) static struct Output
{
371 ulong value
; // satoshis
372 const(ubyte)[] script
;
376 private static struct TxInOutRange(FT
, string parser
) {
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
..$];
389 if (acount
> 0) xparse();
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
; }
399 pragma(inline
, true);
402 if (cur
< len
) xparse();
407 public alias TxInRange
= TxInOutRange
!(Input
, q
{
409 crec
.id
= mbuf
.getbufnc
!ubyte(32);
411 crec
.vout
= mbuf
.get
!uint;
413 uint scsz
= mbuf
.getvl
!uint;
414 crec
.script
= mbuf
.getbufnc
!ubyte(scsz
);
416 crec
.seq
= mbuf
.get
!uint;
419 public alias TxOutRange
= TxInOutRange
!(Output
, q
{
421 crec
.value
= mbuf
.get
!ulong;
423 uint scsz
= mbuf
.getvl
!uint;
424 crec
.script
= mbuf
.getbufnc
!ubyte(scsz
);
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;
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;
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;
451 mbuf
.membuf
= mbuf
.membuf
[0..mbuf
.ofs
];
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
[]);
469 foreach (immutable idx
, ref ubyte b
; dg0
[0..16]) {
470 ubyte t
= dg0
.ptr
[31-idx
];
478 TxInRange
inputs () const @safe { return TxInRange(mbuf
, iofs
, icount
); }
479 TxOutRange
outputs () const @safe { return TxOutRange(mbuf
, oofs
, ocount
); }
483 static struct TxRange
{
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
) {
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
);
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
);
513 pragma(inline
, true);
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");
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
;
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
); }