From 5b95b0f0fdd8e0b00b5f9f3322a1267445190dd2 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 21 Mar 2014 12:50:19 -0300 Subject: [PATCH 1/6] Block.js tests WIP --- Block.js | 1 + bitcore.js | 1 + test/data/blk86756-testnet.dat | Bin 0 -> 9500 bytes test/test.Block.js | 101 ++++++++++++++++++++++++++++++++- test/testdata.js | 5 ++ 5 files changed, 106 insertions(+), 2 deletions(-) create mode 100644 test/data/blk86756-testnet.dat diff --git a/Block.js b/Block.js index aeda5f9..64ae287 100644 --- a/Block.js +++ b/Block.js @@ -67,6 +67,7 @@ Block.prototype.parse = function parse(parser, headerOnly) { for (var i = 0; i < txCount; i++) { var tx = new Transaction(); tx.parse(parser); + tx.calcHash(); this.txs.push(tx); } }; diff --git a/bitcore.js b/bitcore.js index 3336406..79e8ffb 100644 --- a/bitcore.js +++ b/bitcore.js @@ -21,6 +21,7 @@ requireWhenAccessed('networks', './networks'); requireWhenAccessed('util', './util/util'); requireWhenAccessed('EncodedData', './util/EncodedData'); requireWhenAccessed('VersionedData', './util/VersionedData'); +requireWhenAccessed('BinaryParser', './util/BinaryParser'); requireWhenAccessed('Address', './Address'); requireWhenAccessed('Opcode', './Opcode'); requireWhenAccessed('Script', './Script'); diff --git a/test/data/blk86756-testnet.dat b/test/data/blk86756-testnet.dat new file mode 100644 index 0000000000000000000000000000000000000000..207abdac05c64e3bd7eac65736d13bd32f24b5b6 GIT binary patch literal 9500 zcmb7~byU>fy2fDu=>|b^Xb}aG?nb&n8l*v*fgz<^N>aK*Ku}P+Te=&hyIVl+40@E` zy=UFynYH-C#dr4mJRhF@?!CWTRBU8K5poz901OO_^!ow@cOJS(-}p_pCU8N*#_b>dU{Vnfdc89DN< z6UC%%qWzmpjwfRCd^`WDdh2X#K{7VUKw@_#!<{OJaAjrb-A06maHcOO)=~9=l4d{j z;a3t*#Q+pAZyip?!GeBV*=Q`gafT>?9`f`h0{SP-PgiK;fv6d2216?S*3PTH@;a(HfsN29&z=_7&AGV`0$&r(Tx-FH6Bo3Y^ZkiDrbzULL zPhfddokGb0x-EQIpoO92-~(H|bM1^$Nk97RY=j|mv*?)O1{Kj{lBWFY2@Q;N%&ZI_ z?_mIjTY5{z*oR!h#-D7V4grs-+ve-4W^~Cu4~KQFx1Aet?Z z#^U)?Q2@|2SF)@cZnwzNrZODlg_cc7?`bgQ*yiJx-gesiJ+1-(d?r_wwmpqeStqxR zP~Uhgc-)NpnCSAoYYEm7DZ#`Z0G0yqBvQ59E;hA*Q|r8oE~r(<*Nkw&jAas+xo^qi z4gM}K)F5`i#!kDE1$VjO;64ubQn+d~yDXE}T#6TSwhyR+un;QAQu3)d1iD|ZOKQin z4Nau3g}sE0R1qh)SFKwXr~Ss;^9-xCafOgarbtP|&`otXdEO#xGrW@=i>_o)De|tS zNGnb_S3n3ZhKTxX+HxeyWcS618ou7W(dL?m09~dkG;->rIiV}!mTAB(b^V#g(89OW z%~(;R#Be-l=x1Noa*LUczIYd36jU^@^W^sv9JcET0Pim>iUC9fK_uG^?{hk&?@-57 zfJs-oAcboU9v@VVK<=nzK{@&wE@Eq?T^cdgj#3Q$)BL24WmXnNSkmHKL+r8fKH}Ts z*}Bk`q2$7Mqc?}#XnSg=03dLzzubEh$aU^=VhYp9Uucq_c<{Kn)^|yzo?Gbul)*L>on^BMg{l zQ#~Vmo)PbsQ|tIZ1tIT`B-4k*%gLX&gZL3AirU362uc^-JdHZBMam5si+9*{8390s zrSb|YAo_ZvZ57@C0!FdjppbW5I>(6~k=2cy*oP&Eq*UW;YL}6n(xYkX?V_of8J$_H z@DsxR06dfVg=Uh@s9WX(A31&#O|+GJ{4Q2c!CZC~GrD&~@;STn`=Fx6_CL(0x`^l3 zky))%PS%u*-|t1Uvr1pxVSeS0%R@$!X0_X_p=xOEmh@@~r^C2jTgoL@KXYqZ7g=<$v59 zRsMj36AZG*zG?b|pv;7s@|Vov>FqdbAE!x};w~?LFK1?*!so{fUtd>J6^tBUqD9F0 zBk7fW(zOgOgDGzskx9GQ4u=S5oB){xj~Zo~eK}i%7Z?Dfs?V?t9x`R-%bmx66#t4n z%bK`8k8>}iYC|Y-W(-M&5aIibUfq~bU z|MVK3Iti?n`^TV0%|Vn)C3$fOel_1s3>~TJh{%+oRrOZxChF%%;y@PwP)z7GFhc0O z4CL^&gx&S!4n-L`4uZY{aE_WRxMCYE_b$7KZU3+Y|ak_2kK-kBsy7qc}#d0O0A3Rq7H5ztCOyB%qjNuIpkm@eT@_d^uKE^SeH+KL;v@!1K3ah`-ufS`SY(R zO(K=tQ`CZj4I7<CR-z@0a33Qxa zi&mLBa&=x=I9gLflRuw|gH$^F#*Pj!?-wG^=0e3)> zUV@0sI8Hz5q5Ej#JTvy-(-dtu2(ORtq6~yU>hv6$;r}H4W7QW8dS{I~X4L#@o7fk; zhFTAZ{+V<`OO>QkI4!zx5!zL2;(ngK>Ly``&=@ZBX z+pMjH=XBBQIF9;=HTyob?GhJe@QsD~Q|!M?dJQRVgnMe(zC~c~y@(dL?l?3zHCshZ zu!ga~a0Q9=YpA53NvC^V`i&>peF&wcAw%>0(PGn@iv3L=yiXr~Os^RGIwvIQLHA|E zkFI=n*q&CXJ|fNC9y*OQ0{PcqF67<&fQdZ%1)|9=)LcWfYW3hrg|0_Og(rYFH2$8T zmcdl}YL{$N2Zh`%^ruav(7LzF=pvJ%y6UqeTzxv{j4^L|T?ysL%6~4Sgg&bTG7nla zi!437>}=O8YaQ=n-hmzyB6|if>bzgpuJw^ws~P=p7pJ-~$ICl>=vhum{WBkEp)6Ih>X-I-5KXNM0P@LOHQGqE-o$u#XKSY_e`H)w z6{dP`in04Rn+@MnUj^dO{N4j(cl~6z&PpbahlxcYvP?_XnnG6`u()Bn+r0^hP={f& zCxh6ygvR9&40z;*n(eLRc*PwOn`mQ5o?}V+qYl3{0oB7_S3S2CnQ`+-Qm#mQcOb zzo@6r7#WVjfw3YY`uf$cB0`sns^x>agYgBiJ|5KJ&$&!|g%bNvXZh_{oam}?As&Ul;wKr4Csc;3W(_{p5E3d!um?H7p^fjUo{zEu^5@ws^kL3d|w zt*;QG5x*)1&^#XvbwwkP++XxKz1&nqt)VB|Pe<*BLf`TIfSF-6s{c3xbkHeQPfo&t zw(R>>XUbYh&c>&TX96ne*OoDCG0CQ6YQ`1s>5Uj~a`|T51)(L0NY&t>NT8)Gp}Qu8 z`o2I&h)7G~%VfA|ullJAY_fHN9MH}Mj*Ax0o1d@5M&HiGyoL>_9c6(I%^3@=_K>Vc-yXScXM=-P z>T@^|HvwwxDZE0tp-~1DgC}>WXGsT^yoB3qD=tj;GB3(INY;-!{dipY)j3=x9<30_ z1>K+hoXwJb25@Xj>C}Cw#bv`?(|#pylR0?2t%?_4;u{8%N%%n=rg?4H8}|j==MaCB zK;?RGJ{jcG<4(Y}&|(qpuk7x5AJRe@&3adZ4<#;U`UHr1^g%G!IF9f6xP8daNhdYg z#TGH44htN*PTWLW&rk_*v8#!`pb78-O}LrqhFb}%YIq2AkD=IqYLeC2?xL8#P`m8U z$N??$qZ3qRr+)LKmj<1y?9aZ2)rV*riT7Rzus!0Se>wZ&-Jk&?8Llr=!GU6Y7rI+W z<^Ax#&idVrdJlSv)pP3~J_x~t?8g`|Uk|UB-~O}1{QK1wDjW(FU=H7m2z0YAFeP(7 zUwA0Bz~iRhM$ASb+$lNCxqh^M@Ml*HLZLuxo~+k1Afz;LOKuS}Q6#}IHJyvZkw9!ysJWGL z&OqL|bltFbKFQj5kKnkV-aPf*8dTD+tj914T#HdUr-9ti&Lko^X=%rY1N^>}2 zO9u_i4I$Jw`x(=B58J|NO+*=nnI%%{vbpESM!b?4?dCX3&aU4=k}zkonFueps8}6h z^@G%GmxPg^s6;1JEksf82(0vF6XQ1Pcc~W{?Ih!-ie872CJu#>B3*?<*q;Aa)Oo}< zQU(y=Feeo8SgEvZ^M)dYw#e*p41xDYkAnTu-L2RhmYgMT zE-*=0%}Wc`)`;$&Z-O{n8Mh4tS7|)4O)&IN{&qYXd1BG8Rc5SIbx}-c_Q^7mv~AQM5rc zd2#23J#2-wr6NNBUU5!kBj90-Er1(A?i&;1vzBwqx74v_*`QIq?H{#dv0%ByB`exM zJJ}2|MO-F?pwfSCVf+&wbZfAOBaU=wDN%fv5w|iim%P(qoF)1^YvpNgacwF&ytHpPD|7rw+&4 zuh>n#6q!zId|G6$&Ll2m@DBAymTixN;E}?nn~eI?$z)U4*10L&~XOpbc=;9qd z_sdZn9)#?nwi`45k~%vrlQfuMJBvl+@ni}3)#p8F`pivYl5=o*obYsvw!v@ekhK!j zN&ehMVQTydA7bCBnKHwt(jG8_WZ|iM$%xbPGA;l}#=bYX+rhx3cf8qpQ}a#Z%}f6; zysz@Vad1>1tuaf+K-%7u*9h7}A))S>FWCb(KKIHW7ihG}f{v4MSanDvoXbGB)c^Bp zYRkz5rFA18}_vM@gq+xKhwA0~wvs28mg< zNu$xU&Z-GUK`b^0aF>ZUGry?*P_Ao*#ps~Y6Ri7f*3G)N!?jF$Ru&T)tiO2O82)~L zobpG?-evp7sL1g>Kbh#kSrSA(hXlhGyrJUbJ!qnSZf6A^nn?pAFs0AkGigLuV*T^H zJbN`6J0iMxH}DsISX2Q(qEZ#WaOS5d*-6!S^5TsrouE}w;$}_`lkHF$wV6yuh$O|V zI(Y6M+Q$}xt$i{j{84t1>TE%V&#@Ietj}C~L_%+=-+h(%@6SMyg=A1+w^{$>$x+d_ z=(84#DQ|UA9t%3s^;f!+_3v}t7Fxuk`(_axv+ya!lWHw6_m*c~;OkK~sx+x0MnQ-9 zxtfx8dpHbw^%l&2e7l0#uZQtHXVoj3oS|7)eU?2;+FK6--CL5zGdiXeJLtXN#iMNp z5KE*}ITAafDU}8HK_Fo-w1@u9&QUFOKwXPe>E#biLavQus)1e}pLiPDwi+$($6JyD zMJ<1_u4ep!-nZDgRT$`vVk7f#++&omCzz~i$5esa(K@~r4AE35_9`TqJl=^a)?8%b zAowxQxopVX_iO}xD^`ai&&qNL^_qD^`&+tbZ0_1yGG3|_FLReR|AE-ng1xp*b5@g3@XDd^{cWdWmW1 z5uaZe@a57Cwwx1{Nx#c?UxO7zzl?Mp%Qq)hJcJJOtHTUA2=o}v2Goix51AcUi@y%c zt4}2r5bNIVrlzXcBC&*8jAN&4dQ!-rjOLWThl`Jm*NsU~xZ0fjm8rXkR`x0ZVi9dU z-Jg-!`^jqM;U|6Bm^Y<15uSo_wi%J8J?X?c$U)HQY^sOc^(dYG=pp|`Ze^Vf;1O9? zUC+m1Ze$_r4bD{lZm6iA6OdsmO#VRFi3#W2G{emDkrJ=!4a=*LrFV}O=z$MPHV+`0 zGS_yhZa&35RKXlzpy-rpAZZP=?K&+ki&dyBIk9io{L3XCaQEmbn#|q7V&S~sE=aCu zhb#%B^VswIKO6ghc>Bdd#rSwtmrLsLYjMT24-!=s*q`y4oTOxpR2mvpndmg{V1FCo zgz>(5N3o%G8aAj?%HqKuYkpwBVWy6kLMNM*e;CAKQrVKcD=$L1OT18vY21&#oezQa{2sJImM~M5*YSWl7 zYn24t@tS)QFw|5V{qSQe6MQKzhv|o|3Gor-Hz^QNsm};}@U!0?J_+Ogkw#;ufyc{7 zz3*W6ZSTp32Zc3{Fhmq$n4m%yULfjNu)dQ5{>r)ElD2-i9M!kOA)^ZJqHOSOYyZzL zTJFBAahoS0JWsF-x{gNi^bTu!R1SJC(McX#)a}F-n`o(R}sWw0wdkwe%ZNN z@*a*Qno52s3=_<#EtVzdq_qsMOFsr;@vlxw|LM$pN7Rp6nfW>ehh0a@c*D{KFXUhz zVMg3$-3ZXv_(~bHGXaRGd`(NNxdyTIVwmZNS1VUv{NOi=r2XVk7DkgX4zb4ZAfjdx z=Qf&o^U|ZrH?L%W9PS_%U%*YRm>r2BD^t8rezADV{qCmSG)rjwhwN%TR+G;TU3#p_ zw{k(9f9Cz{;8aQjDQ>2}(!6g#e_tKOlQZ-ucT~t#Iswtot90z|57Y`B+{*8(ba?0xNav7mLa+AGnH)g&FF}r03N|*DOwfT~ZPhS- zU$d)(`d!z<*W6r6tj4S?5=Fx1t)~466CTcNrG|u?Mf!RSYWx^dNq9HUCL#`Sh?pHE zBx|bX%nmx9#O+nEdIf>cm{brkeRr=QCXS}CWJ+l5G0-v7;i3-LSi-{t0JPhq2y32! zNhUz)dPdb5pO0&0&tyniK*ma%e;`{&8wv|G!j$cbtIg7=ojTXwW#Ja`uG}>43MbvV z@R=uD*!t&e8tH)nwolcytoGi}Pu6UAhB+ZW8!! z^*;0D3K!8=aAG^uIMi0Wg(2lP8o#ud2<=Gycw3S0epx(oUGZ`Fm;E9u-k~?(6QQCP z^|JVK=fQe;S~LuIDjw^Tdm?J^yYx%f&(^u3^Vj0iFhuveGRoGmAAVUi`vL%(Rc&~R+M_q-G;0$+3(k?b?Pis=O)8w-2M22 z$B$Ok2sm01iE3y?sh_v!IV3IL^DwAuU5>=Iw${GkOEnV}yU_}j>x(<*_;E(&LS|p} zwZRV!+NdbU74Gg$%>dzyK!mvod7d{D0rKntG4mp~B^3lz)Yq?s4`r4UxWL9z%g%bs z*425;z9=KMi-XsI+XKWetTzi5sv6x*pbPTy2j5YLWDFDUVGIwM&h9 zPp4wt8^Eo`hG%;@IDpN|*oc9qD0Z4*9SvPmrQ`bBikcW9wL(cU>k8;A`8QOo2KQ!X zMkto4VaFH2ee@$7#&@u`bqmS@C$1w0Q?_F%x}4%iBl3+AO97LZ2))~iCTd1C_mNcIOeQL>2xu;x^uuxY zR{FyFy|&I*kr(TGlou+>@4i0*C4Fx~fL!lw$vMsxH6Pk+eNsO1l z8tcpOcix_og3)JR48<$e7C(jxv3XO1o|==6u<&%8LdmbX2Wyoi>n2{|}kp BWlR77 literal 0 HcmV?d00001 diff --git a/test/test.Block.js b/test/test.Block.js index f159f43..7d366b1 100644 --- a/test/test.Block.js +++ b/test/test.Block.js @@ -4,9 +4,33 @@ var chai = chai || require('chai'); var bitcore = bitcore || require('../bitcore'); var should = chai.should(); +var testdata = testdata || require('./testdata'); var BlockModule = bitcore.Block; +var BinaryParser = bitcore.BinaryParser; var Block; + +var getBlock = function (onlyHeader) { + + var testnetMagic = bitcore.networks.testnet.magic.toString('hex'); + + var b = new Block(); + // this is block 86756 from testnet3 + var p = new BinaryParser(testdata.dataRawBlock); + + + var magic = p.buffer(4).toString('hex'); + + + if (magic !== testnetMagic ) + throw new Error('CRITICAL ERROR: Magic number mismatch: ' + + magic + ' : ' + testnetMagic); + + p.word32le(); + b.parse(p, onlyHeader); + return b; +}; + describe('Block', function() { it('should initialze the main object', function() { should.exist(BlockModule); @@ -16,9 +40,82 @@ describe('Block', function() { should.exist(Block); }); it('should be able to create instance', function() { - var p = new Block(); - should.exist(p); + var b = new Block(); + should.exist(b); }); + + it('should be able to parse a block from hex', function() { + var b = getBlock(); + should.exist(b); + should.exist(b.getHash()); + }); + + it('should be able to check block contents', function() { + var b = getBlock(); + should.exist(b.getHash()); + b.checkHash().should.equal(true); + b.checkProofOfWork().should.equal(true); + b.checkProofOfWork().should.equal(true); + b.getWork().toString().should.equal('17180131332'); + b.checkTimestamp().should.equal(true); + + }); + it('#checkBlock should be able to check block contents', function() { + var b = getBlock(); + should.exist(b.getHash()); + b.checkBlock().should.equal(true); + }); + + + it('should be able to check Transactions', function() { + var b = getBlock(); + + b.checkTransactions(b.txs).should.equal(true); + b.checkTransactions.bind([]).should.throw(); + + b.getMerkleTree(b.txs).length.should.equal(45); + bitcore.buffertools.toHex(b.calcMerkleRoot(b.txs)).should.equal(bitcore.buffertools.toHex(b.merkle_root)); + + var coinbase = b.txs.shift; + b.checkTransactions.bind(b.txs).should.throw(); + + b.txs.push(coinbase); + + b.checkTransactions.bind(b.txs).should.throw(); + + }); + + it('should be able to checkProofOfWork', function() { + var b = getBlock(); + + b.hash = bitcore.buffertools.reverse(new Buffer('000000000b99b16390660d79fcc138d2ad0c89a0d044c4201a02bdf1f61ffa11', 'hex')); + b.checkHash().should.equal(true); + b.checkProofOfWork().should.equal(true); + + // wrong hash hash, ok proof of work + b.hash = bitcore.buffertools.reverse(new Buffer('000000000000016390660d79fcc138d2ad0c89a0d044c4201a02bdf1f61ffa11', 'hex')); + b.checkProofOfWork().should.equal(true); + b.checkHash().should.equal(false); + + + // wrong hash hash, wrong proof of work + b.hash = bitcore.buffertools.reverse(new Buffer('0000000bbb99b16390660d79fcc138d2ad0c89a0d044c4201a02bdf1f61ffa11', 'hex')); + b.checkHash().should.equal(false); + b.checkProofOfWork.bind().should.throw(); + }); + + it('should be able to get components from blocks', function() { + var b = getBlock(true); + + bitcore.util.formatHashFull(b.getHash()).should.equal('000000000b99b16390660d79fcc138d2ad0c89a0d044c4201a02bdf1f61ffa11'); + + bitcore.util.formatHashFull(b.getHeader()).should.equal('d6383bd51c3fffc051be10ce58e6d52d1eb00470ae1ab4d5a3375c0f51382c6f249fff84e9888286974cfc97000000003c35b5e70b13d5b938fef4e998a977c17bea978390273b7c50a9aa4b00000002'); + + bitcore.util.formatHashFull(b.merkle_root).should.equal('58e6d52d1eb00470ae1ab4d5a3375c0f51382c6f249fff84e9888286974cfc97'); + + }); + + }); diff --git a/test/testdata.js b/test/testdata.js index 3c7f3fd..a92fe41 100644 --- a/test/testdata.js +++ b/test/testdata.js @@ -30,3 +30,8 @@ module.exports.dataSigNonCanonical = dataSigNonCanonical; module.exports.dataBase58KeysValid = dataBase58KeysValid; module.exports.dataBase58KeysInvalid = dataBase58KeysInvalid; +var fd = fs.openSync('test/data/blk86756-testnet.dat', 'r'); +var buffer = new Buffer(9000); +fs.readSync(fd, buffer, 0, 9000, 0); +module.exports.dataRawBlock = buffer; + From 684be77268b342ac07bd3b47a6fb0feae4839d2f Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 21 Mar 2014 16:39:38 -0300 Subject: [PATCH 2/6] small fixes in block, adapt to browser bignum. remove legacy code --- Block.js | 297 +-------------------------------------------- Transaction.js | 3 + browser/build.js | 1 + test/test.Block.js | 79 +++++++++++- test/testdata.js | 4 +- util/util.js | 11 +- 6 files changed, 92 insertions(+), 303 deletions(-) diff --git a/Block.js b/Block.js index 64ae287..05f9b69 100644 --- a/Block.js +++ b/Block.js @@ -94,15 +94,11 @@ Block.prototype.checkProofOfWork = function checkProofOfWork() { // TODO: Create a compare method in node-buffertools that uses the correct // endian so we don't have to reverse both buffers before comparing. - buffertools.reverse(this.hash); - - if (buffertools.compare(this.hash, target) > 0) { + var reverseHash = buffertools.reverse(this.hash); + if (buffertools.compare(reverseHash, target) > 0) { throw new VerificationError('Difficulty target not met'); } - // Return the hash to its normal order - buffertools.reverse(this.hash); - return true; }; @@ -200,7 +196,7 @@ Block.prototype.checkMerkleRoot = function checkMerkleRoot(txs) { throw new VerificationError('No merkle root'); } - if (buffertools.compare(this.calcMerkleRoot(), this.merkle_root) == 0) { + if (buffertools.compare(this.calcMerkleRoot(txs), this.merkle_root) !== 0) { throw new VerificationError('Merkle root incorrect'); } @@ -237,214 +233,6 @@ Block.prototype.toString = function toString() { return ""; }; -/** - * Initializes some properties based on information from the parent block. - */ -Block.prototype.attachTo = function attachTo(parent) { - this.height = parent.height + 1; - this.setChainWork(parent.getChainWork().add(this.getWork())); -}; - -Block.prototype.setChainWork = function setChainWork(chainWork) { - if (Buffer.isBuffer(chainWork)) { - // Nothing to do - } else if ("function" === typeof chainWork.toBuffer) { // duck-typing bignum - chainWork = chainWork.toBuffer(); - } else { - throw new Error("Block.setChainWork(): Invalid datatype"); - } - - this.chainWork = chainWork; -}; - -Block.prototype.getChainWork = function getChainWork() { - return Bignum.fromBuffer(this.chainWork); -}; - -/** - * Compares the chainWork of two blocks. - */ -Block.prototype.moreWorkThan = function moreWorkThan(otherBlock) { - return this.getChainWork().cmp(otherBlock.getChainWork()) > 0; -}; - -/** - * Returns the difficulty target for the next block after this one. - */ -Block.prototype.getNextWork = -function getNextWork(blockChain, nextBlock, callback) { - var self = this; - - var powLimit = blockChain.getMinDiff(); - var powLimitTarget = util.decodeDiffBits(powLimit, true); - - var targetTimespan = blockChain.getTargetTimespan(); - var targetSpacing = blockChain.getTargetSpacing(); - var interval = targetTimespan / targetSpacing; - - if (this.height == 0) { - callback(null, this.bits); - } - - if ((this.height+1) % interval !== 0) { - if (blockChain.isTestnet()) { - // Special testnet difficulty rules - var lastBlock = blockChain.getTopBlock(); - - // If the new block's timestamp is more than 2 * 10 minutes - // then allow mining of a min-difficulty block. - if (nextBlock.timestamp > this.timestamp + targetSpacing*2) { - callback(null, powLimit); - } else { - // Return last non-"special-min-difficulty" block - if (this.bits != powLimit) { - // Current block is non-min-diff - callback(null, this.bits); - } else { - // Recurse backwards until a non min-diff block is found. - function lookForLastNonMinDiff(block, callback) { - try { - if (block.height > 0 && - block.height % interval !== 0 && - block.bits == powLimit) { - blockChain.getBlockByHeight( - block.height - 1, - function (err, lastBlock) { - try { - if (err) throw err; - lookForLastNonMinDiff(lastBlock, callback); - } catch (err) { - callback(err); - } - } - ); - } else { - callback(null, block.bits); - } - } catch (err) { - callback(err); - } - }; - lookForLastNonMinDiff(this, callback); - } - } - } else { - // Not adjustment interval, next block has same difficulty - callback(null, this.bits); - } - } else { - // Get the first block from the old difficulty period - blockChain.getBlockByHeight( - this.height - interval + 1, - function (err, lastBlock) { - try { - if (err) throw err; - - // Determine how long the difficulty period really took - var actualTimespan = self.timestamp - lastBlock.timestamp; - - // There are some limits to how much we will adjust the difficulty in - // one step - if (actualTimespan < targetTimespan/4) { - actualTimespan = targetTimespan/4; - } - if (actualTimespan > targetTimespan*4) { - actualTimespan = targetTimespan*4; - } - - var oldTarget = util.decodeDiffBits(self.bits, true); - var newTarget = oldTarget.mul(actualTimespan).div(targetTimespan); - - if (newTarget.cmp(powLimitTarget) > 0) { - newTarget = powLimitTarget; - } - - Debug1('Difficulty retarget (target='+targetTimespan + - ', actual='+actualTimespan+')'); - Debug1('Before: '+oldTarget.toBuffer().toString('hex')); - Debug1('After: '+newTarget.toBuffer().toString('hex')); - - callback(null, util.encodeDiffBits(newTarget)); - } catch (err) { - callback(err); - } - } - ); - } -}; - -var medianTimeSpan = 11; - -Block.prototype.getMedianTimePast = -function getMedianTimePast(blockChain, callback) -{ - var self = this; - - Step( - function getBlocks() { - var heights = []; - for (var i = 0, m = medianTimeSpan; i < m && (self.height - i) >= 0; i++) { - heights.push(self.height - i); - } - blockChain.getBlocksByHeights(heights, this); - }, - function calcMedian(err, blocks) { - if (err) throw err; - - var timestamps = blocks.map(function (block) { - if (!block) { - throw new Error("Prior block missing, cannot calculate median time"); - } - - return +block.timestamp; - }); - - // Sort timestamps - timestamps = timestamps.sort(); - - // Return median timestamp - this(null, timestamps[Math.floor(timestamps.length/2)]); - }, - callback - ); -}; - -Block.prototype.verifyChild = -function verifyChild(blockChain, child, callback) -{ - var self = this; - - Step( - function getExpectedDifficulty() { - self.getNextWork(blockChain, child, this); - }, - function verifyExpectedDifficulty(err, nextWork) { - if (err) throw err; - - if (+child.bits !== +nextWork) { - throw new VerificationError("Incorrect proof of work '"+child.bits+"',"+ - " should be '"+nextWork+"'."); - } - - this(); - }, - function getMinimumTimestamp(err) { - if (err) throw err; - - self.getMedianTimePast(blockChain, this); - }, - function verifyTimestamp(err, medianTimePast) { - if (err) throw err; - - if (child.timestamp <= medianTimePast) { - throw new VerificationError("Block's timestamp is too early"); - } - - this(); - }, - callback - ); -}; Block.prototype.createCoinbaseTx = function createCoinbaseTx(beneficiary) @@ -462,85 +250,6 @@ function createCoinbaseTx(beneficiary) return tx; }; -Block.prototype.prepareNextBlock = -function prepareNextBlock(blockChain, beneficiary, time, callback) -{ - var self = this; - - var newBlock = new Block(); - Step( - function getMedianTimePastStep() { - self.getMedianTimePast(blockChain, this); - }, - - function getNextWorkStep(err, medianTimePast) { - if (err) throw err; - - if (!time) { - // TODO: Use getAdjustedTime for the second timestamp - time = Math.max(medianTimePast+1, - Math.floor(new Date().getTime() / 1000)); - } - - self.getNextWork(blockChain, newBlock, this); - }, - - function applyNextWorkStep(err, nextWork) { - if (err) throw err; - newBlock.bits = nextWork; - this(null); - }, - - function miscStep(err) { - if (err) throw err; - - newBlock.version = 1; - newBlock.timestamp = time; - newBlock.prev_hash = self.getHash().slice(0); - newBlock.height = self.height+1; - - // Create coinbase transaction - var txs = []; - - var tx = newBlock.createCoinbaseTx(beneficiary); - txs.push(tx); - - newBlock.merkle_root = newBlock.calcMerkleRoot(txs); - - // Return reference to (unfinished) block - this(null, {block: newBlock, txs: txs}); - }, - callback - ); -}; - -Block.prototype.mineNextBlock = -function mineNextBlock(blockChain, beneficiary, time, miner, callback) -{ - this.prepareNextBlock(blockChain, beneficiary, time, function (err, data) { - try { - if (err) throw err; - - var newBlock = data.block; - var txs = data.txs; - - newBlock.solve(miner, function (err, nonce) { - newBlock.nonce = nonce; - - // Make sure hash is cached - newBlock.getHash(); - - callback(err, newBlock, txs); - }); - - // Return reference to (unfinished) block - return newBlock; - } catch (e) { - callback(e); - } - }); -}; - Block.prototype.solve = function solve(miner, callback) { var header = this.getHeader(); var target = util.decodeDiffBits(this.bits); diff --git a/Transaction.js b/Transaction.js index 6d28741..cb4e9c5 100644 --- a/Transaction.js +++ b/Transaction.js @@ -18,6 +18,8 @@ var PrivateKey = imports.PrivateKey || require('./PrivateKey'); var COINBASE_OP = Buffer.concat([util.NULL_HASH, new Buffer('FFFFFFFF', 'hex')]); var FEE_PER_1000B_SAT = parseInt(0.0001 * util.COIN); +Transaction.COINBASE_OP = COINBASE_OP; + function TransactionIn(data) { if ("object" !== typeof data) { data = {}; @@ -43,6 +45,7 @@ TransactionIn.prototype.getScript = function getScript() { }; TransactionIn.prototype.isCoinBase = function isCoinBase() { + if (!this.o) return false; return buffertools.compare(this.o, COINBASE_OP) === 0; }; diff --git a/browser/build.js b/browser/build.js index 14f14ed..736ae3d 100644 --- a/browser/build.js +++ b/browser/build.js @@ -52,6 +52,7 @@ var modules = [ 'util/util', 'util/EncodedData', 'util/VersionedData', + 'util/BinaryParser', ]; var createBitcore = function(opts) { diff --git a/test/test.Block.js b/test/test.Block.js index 7d366b1..771c1e6 100644 --- a/test/test.Block.js +++ b/test/test.Block.js @@ -55,7 +55,6 @@ describe('Block', function() { should.exist(b.getHash()); b.checkHash().should.equal(true); b.checkProofOfWork().should.equal(true); - b.checkProofOfWork().should.equal(true); b.getWork().toString().should.equal('17180131332'); b.checkTimestamp().should.equal(true); @@ -73,17 +72,31 @@ describe('Block', function() { b.checkTransactions(b.txs).should.equal(true); b.checkTransactions.bind([]).should.throw(); + var coinbase = b.txs.shift; + b.checkTransactions.bind(b.txs).should.throw(); + b.txs.push(coinbase); + b.checkTransactions.bind(b.txs).should.throw(); + + + }); + + it('should be able to checkMerkleRoot', function() { + + var b = getBlock(); b.getMerkleTree(b.txs).length.should.equal(45); bitcore.buffertools.toHex(b.calcMerkleRoot(b.txs)).should.equal(bitcore.buffertools.toHex(b.merkle_root)); - var coinbase = b.txs.shift; - b.checkTransactions.bind(b.txs).should.throw(); + b.checkMerkleRoot(b.txs); - b.txs.push(coinbase); + delete b['merkle_root']; + b.checkMerkleRoot.bind(b.txs).should.throw(); - b.checkTransactions.bind(b.txs).should.throw(); + b.merkle_root=new Buffer('wrong'); + b.checkMerkleRoot.bind(b.txs).should.throw(); }); + + it('should be able to checkProofOfWork', function() { var b = getBlock(); @@ -104,6 +117,14 @@ describe('Block', function() { b.checkProofOfWork.bind().should.throw(); }); + + it('should be able to check via checkBlock', function() { + var b = getBlock(); + b.checkBlock.bind(b.txs).should.throw(); + b.getHash(); + b.checkBlock(b.txs).should.equal(true); + }); + it('should be able to get components from blocks', function() { var b = getBlock(true); @@ -116,6 +137,54 @@ describe('Block', function() { }); + it('#getBlockValue should return the correct block value', function() { + var c = bitcore.util.COIN; + bitcore.Block.getBlockValue(0).div(c).toNumber().should.equal(50); + bitcore.Block.getBlockValue(1).div(c).toNumber().should.equal(50); + bitcore.Block.getBlockValue(209999).div(c).toNumber().should.equal(50); + bitcore.Block.getBlockValue(210000).div(c).toNumber().should.equal(25); + bitcore.Block.getBlockValue(2100000).toNumber().should.equal(4882812); + }); + + + it('#getStandardizedObject should return object', function() { + var b = getBlock(); + var o = b.getStandardizedObject(b.txs); + + o.hash.should.equal('000000000b99b16390660d79fcc138d2ad0c89a0d044c4201a02bdf1f61ffa11'); + o.n_tx.should.equal(22); + o.size.should.equal(8003); + var o2 = b.getStandardizedObject(); + o2.hash.should.equal('000000000b99b16390660d79fcc138d2ad0c89a0d044c4201a02bdf1f61ffa11'); + o2.size.should.equal(0); + }); + + + it('#miner should call the callback', function(done) { + var b = getBlock(); + var Miner = function() {}; + Miner.prototype.solve = function (header,target,cb) { + this.called=1; + should.exist(header); + should.exist(target); + return cb(); + }; + var miner = new Miner(); + b.solve(miner, function () { + miner.called.should.equal(1); + done(); + }); + + }); + + + it('#createCoinbaseTx should create a tx', function() { + var b = new Block(); + var pubkey = new Buffer('02d20b3fba521dcf88dfaf0eee8c15a8ba692d7eb0cb957d5bcf9f4cc052fb9cc6'); + var tx = b.createCoinbaseTx(pubkey); + should.exist(tx); + tx.isCoinBase().should.equal(true); + }); }); diff --git a/test/testdata.js b/test/testdata.js index a92fe41..72e0d4e 100644 --- a/test/testdata.js +++ b/test/testdata.js @@ -30,8 +30,6 @@ module.exports.dataSigNonCanonical = dataSigNonCanonical; module.exports.dataBase58KeysValid = dataBase58KeysValid; module.exports.dataBase58KeysInvalid = dataBase58KeysInvalid; -var fd = fs.openSync('test/data/blk86756-testnet.dat', 'r'); -var buffer = new Buffer(9000); -fs.readSync(fd, buffer, 0, 9000, 0); +var buffer = new Buffer(fs.readFileSync('test/data/blk86756-testnet.dat')); module.exports.dataRawBlock = buffer; diff --git a/util/util.js b/util/util.js index 7a33869..2f6085b 100644 --- a/util/util.js +++ b/util/util.js @@ -359,8 +359,17 @@ var generateNonce = exports.generateNonce = function () { */ var decodeDiffBits = exports.decodeDiffBits = function (diffBits, asBigInt) { diffBits = +diffBits; + var target = bignum(diffBits & 0xffffff); - target = target.shiftLeft(8*((diffBits >>> 24) - 3)); + /* + * shiftLeft is not implemented on the bignum browser + * + * target = target.shiftLeft(8*((diffBits >>> 24) - 3)); + */ + + var mov = 8*((diffBits >>> 24) - 3); + while (mov-- > 0) + target = target.mul(2); if (asBigInt) { return target; From aad33d0c4a94386d387aa05e5aa8c69b349bbb09 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 21 Mar 2014 16:47:45 -0300 Subject: [PATCH 3/6] remove test code --- Block.js | 1 - 1 file changed, 1 deletion(-) diff --git a/Block.js b/Block.js index 05f9b69..47c0a6b 100644 --- a/Block.js +++ b/Block.js @@ -67,7 +67,6 @@ Block.prototype.parse = function parse(parser, headerOnly) { for (var i = 0; i < txCount; i++) { var tx = new Transaction(); tx.parse(parser); - tx.calcHash(); this.txs.push(tx); } }; From 8ec4f2f9c5951526c438b1bddd2856dd226d4fd3 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 21 Mar 2014 17:58:59 -0300 Subject: [PATCH 4/6] add -target=dev option to grunt to prevent minifying the source --- Gruntfile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gruntfile.js b/Gruntfile.js index 76f758c..f6c0b2f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -16,7 +16,8 @@ module.exports = function(grunt) { stdout: true, stderr: true }, - command: 'node ./browser/build.js -a', + command: grunt.option('target') === 'dev' ? + 'node ./browser/build.js -a -d ' : 'node ./browser/build.js -a' } }, watch: { From 227a95296b4a714bece57a271f798a1813da9a13 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 21 Mar 2014 18:00:13 -0300 Subject: [PATCH 5/6] remove stracktrace for firefox error reporting --- util/error.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/util/error.js b/util/error.js index 428fc89..4e5097f 100644 --- a/util/error.js +++ b/util/error.js @@ -9,7 +9,8 @@ function MissingSourceError(msg, missingTxHash) { // TODO: Since this happens in normal operation, perhaps we should // avoid generating a whole stack trace. Error.call(this); - Error.captureStackTrace(this, arguments.callee); +// This is not compatible with firefox. +// Error.captureStackTrace(this, arguments.callee); this.message = msg; this.missingTxHash = missingTxHash; this.name = 'MissingSourceError'; @@ -19,6 +20,7 @@ MissingSourceError.prototype.__proto__ = Error.prototype; exports.MissingSourceError = MissingSourceError; + /** * Used in several places to indicate invalid data. * @@ -29,7 +31,9 @@ function VerificationError(msg, missingTxHash) { // TODO: Since this happens in normal operation, perhaps we should // avoid generating a whole stack trace. Error.call(this); - Error.captureStackTrace(this, arguments.callee); + +// This is not compatible with firefox. +// Error.captureStackTrace(this, arguments.callee); this.message = msg; this.missingTxHash = missingTxHash; this.name = 'VerificationError'; From 02296d9517ec3cd58df5b8fe129d98a644cd4c06 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 21 Mar 2014 18:52:49 -0300 Subject: [PATCH 6/6] fix firefox compatibility issue with buffertools#compare --- Block.js | 2 +- Transaction.js | 4 +++- test/test.Block.js | 3 +-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Block.js b/Block.js index 47c0a6b..84be543 100644 --- a/Block.js +++ b/Block.js @@ -195,7 +195,7 @@ Block.prototype.checkMerkleRoot = function checkMerkleRoot(txs) { throw new VerificationError('No merkle root'); } - if (buffertools.compare(this.calcMerkleRoot(txs), this.merkle_root) !== 0) { + if (buffertools.compare(this.calcMerkleRoot(txs), new Buffer(this.merkle_root)) !== 0) { throw new VerificationError('Merkle root incorrect'); } diff --git a/Transaction.js b/Transaction.js index cb4e9c5..a91c95a 100644 --- a/Transaction.js +++ b/Transaction.js @@ -46,7 +46,9 @@ TransactionIn.prototype.getScript = function getScript() { TransactionIn.prototype.isCoinBase = function isCoinBase() { if (!this.o) return false; - return buffertools.compare(this.o, COINBASE_OP) === 0; + + //The new Buffer is for Firefox compatibility + return buffertools.compare(new Buffer(this.o), COINBASE_OP) === 0; }; TransactionIn.prototype.serialize = function serialize() { diff --git a/test/test.Block.js b/test/test.Block.js index 771c1e6..b29ffff 100644 --- a/test/test.Block.js +++ b/test/test.Block.js @@ -84,7 +84,7 @@ describe('Block', function() { var b = getBlock(); b.getMerkleTree(b.txs).length.should.equal(45); - bitcore.buffertools.toHex(b.calcMerkleRoot(b.txs)).should.equal(bitcore.buffertools.toHex(b.merkle_root)); + bitcore.buffertools.toHex(b.calcMerkleRoot(b.txs)).should.equal(bitcore.buffertools.toHex(new Buffer(b.merkle_root))); b.checkMerkleRoot(b.txs); @@ -177,7 +177,6 @@ describe('Block', function() { }); - it('#createCoinbaseTx should create a tx', function() { var b = new Block(); var pubkey = new Buffer('02d20b3fba521dcf88dfaf0eee8c15a8ba692d7eb0cb957d5bcf9f4cc052fb9cc6');