test: fix tx tests.
This commit is contained in:
parent
4484a11ede
commit
bf2e7d1486
@ -706,7 +706,7 @@ Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) {
|
||||
}
|
||||
|
||||
// Make sure the miner isn't trying to conjure more coins.
|
||||
if (block.getClaimed() > block.getReward(view, this.network)) {
|
||||
if (block.getClaimed() > block.getReward(view, height, this.network)) {
|
||||
throw new VerifyError(block,
|
||||
'invalid',
|
||||
'bad-cb-amount',
|
||||
|
||||
@ -400,8 +400,6 @@ ChainEntry.prototype.rhash = function() {
|
||||
*/
|
||||
|
||||
ChainEntry.prototype.fromBlock = function fromBlock(block, prev) {
|
||||
assert(block.height !== -1);
|
||||
|
||||
this.hash = block.hash('hex');
|
||||
this.version = block.version;
|
||||
this.prevBlock = block.prevBlock;
|
||||
@ -409,9 +407,8 @@ ChainEntry.prototype.fromBlock = function fromBlock(block, prev) {
|
||||
this.ts = block.ts;
|
||||
this.bits = block.bits;
|
||||
this.nonce = block.nonce;
|
||||
this.height = block.height;
|
||||
this.height = prev ? prev.height + 1: 0;
|
||||
this.chainwork = this.getChainwork(prev);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
@ -539,11 +539,13 @@ Block.prototype.getCoinbaseHeight = function getCoinbaseHeight() {
|
||||
* @returns {Amount} reward
|
||||
*/
|
||||
|
||||
Block.prototype.getReward = function getReward(view, network) {
|
||||
Block.prototype.getReward = function getReward(view, height, network) {
|
||||
var i, tx, reward, fee;
|
||||
|
||||
assert(typeof height === 'number');
|
||||
|
||||
network = Network.get(network);
|
||||
reward = btcutils.getReward(this.height, network.halvingInterval);
|
||||
reward = btcutils.getReward(height, network.halvingInterval);
|
||||
|
||||
for (i = 1; i < this.txs.length; i++) {
|
||||
tx = this.txs[i];
|
||||
|
||||
@ -1,27 +1,29 @@
|
||||
'use strict';
|
||||
|
||||
var BN = require('bn.js');
|
||||
var bcoin = require('../').set('main');
|
||||
var util = bcoin.util;
|
||||
var assert = require('assert');
|
||||
var fs = require('fs');
|
||||
var util = require('../lib/utils/util');
|
||||
var btcutils = require('../lib/btc/utils');
|
||||
var crypto = require('../lib/crypto/crypto');
|
||||
var Bloom = require('../lib/utils/bloom');
|
||||
var Block = require('../lib/primitives/block');
|
||||
var Headers = require('../lib/primitives/headers');
|
||||
var MerkleBlock = require('../lib/primitives/merkleblock');
|
||||
var TX = require('../lib/primitives/tx');
|
||||
var CoinView = require('../lib/blockchain/coinview');
|
||||
var Coin = require('../lib/primitives/coin');
|
||||
var constants = bcoin.constants;
|
||||
var network = bcoin.networks;
|
||||
var assert = require('assert');
|
||||
var block300025 = require('./data/block300025.json');
|
||||
var fs = require('fs');
|
||||
var cmpct = fs.readFileSync(__dirname + '/data/compactblock.hex', 'utf8').trim().split('\n');
|
||||
var constants = require('../lib/protocol/constants');
|
||||
var bip152 = require('../lib/net/bip152');
|
||||
|
||||
var block300025 = require('./data/block300025.json');
|
||||
var cmpct = fs.readFileSync(__dirname + '/data/compactblock.hex', 'utf8').trim().split('\n');
|
||||
var cmpct2 = fs.readFileSync(__dirname + '/data/cmpct2', 'utf8').trim();
|
||||
var cmpct2block = fs.readFileSync(__dirname + '/data/cmpct2.bin');
|
||||
|
||||
bcoin.cache();
|
||||
|
||||
describe('Block', function() {
|
||||
var mblock = bcoin.merkleblock({
|
||||
var mblock, raw, block, raw2;
|
||||
|
||||
mblock = new MerkleBlock({
|
||||
version: 2,
|
||||
prevBlock: 'd1831d4411bdfda89d9d8c842b541beafd1437fc560dbe5c0000000000000000',
|
||||
merkleRoot: '28bec1d35af480ba3884553d72694f6ba6c163a5c081d7e6edaec15f373f19af',
|
||||
@ -43,10 +45,9 @@ describe('Block', function() {
|
||||
],
|
||||
flags: new Buffer([245, 122, 0])
|
||||
});
|
||||
var raw = mblock.toRaw().toString('hex');
|
||||
var block;
|
||||
raw = mblock.toRaw().toString('hex');
|
||||
|
||||
var raw2 = '02000000d1831d4411bdfda89d9d8c842b541beafd1437fc560dbe5c0'
|
||||
raw2 = '02000000d1831d4411bdfda89d9d8c842b541beafd1437fc560dbe5c0'
|
||||
+ '00000000000000028bec1d35af480ba3884553d72694f6ba6c163a5c081d7e6edaec'
|
||||
+ '15f373f19af62ef6d536c890019d0b4bf46cd0100000a7d22e53bce1bbb3294d1a39'
|
||||
+ '6c5acc45bdcc8f192cb492f0d9f55421fd4c62de19d6d585fdaf3737b9a54aaee1dd'
|
||||
@ -60,7 +61,7 @@ describe('Block', function() {
|
||||
+ '056c6d56433825657ba32afe269819f01993bd77baba86379043168c94845d32370e'
|
||||
+ '5356203f57a00';
|
||||
|
||||
var mblock = bcoin.merkleblock.fromRaw(raw2, 'hex');
|
||||
mblock = MerkleBlock.fromRaw(raw2, 'hex');
|
||||
|
||||
this.timeout(10000);
|
||||
|
||||
@ -80,26 +81,26 @@ describe('Block', function() {
|
||||
});
|
||||
|
||||
it('should decode/encode with parser/framer', function() {
|
||||
var b = bcoin.merkleblock.fromRaw(raw, 'hex');
|
||||
var b = MerkleBlock.fromRaw(raw, 'hex');
|
||||
assert.equal(b.toRaw().toString('hex'), raw);
|
||||
assert.equal(raw, raw2);
|
||||
});
|
||||
|
||||
it('should be verifiable', function() {
|
||||
var b = bcoin.merkleblock.fromRaw(raw, 'hex');
|
||||
var b = MerkleBlock.fromRaw(raw, 'hex');
|
||||
assert(b.verify());
|
||||
});
|
||||
|
||||
it('should be serialized and deserialized and still verify', function() {
|
||||
var raw = mblock.toRaw();
|
||||
var b = bcoin.merkleblock.fromRaw(raw);
|
||||
var b = MerkleBlock.fromRaw(raw);
|
||||
assert.deepEqual(b.toRaw(), raw);
|
||||
assert(b.verify());
|
||||
});
|
||||
|
||||
it('should be jsonified and unjsonified and still verify', function() {
|
||||
var raw = mblock.toJSON();
|
||||
var b = bcoin.merkleblock.fromJSON(raw);
|
||||
var b = MerkleBlock.fromJSON(raw);
|
||||
assert.deepEqual(b.toJSON(), raw);
|
||||
assert(b.verify());
|
||||
});
|
||||
@ -123,7 +124,7 @@ describe('Block', function() {
|
||||
});
|
||||
|
||||
it('should parse JSON', function() {
|
||||
block = bcoin.block.fromJSON(block300025);
|
||||
block = Block.fromJSON(block300025);
|
||||
assert.equal(block.hash('hex'),
|
||||
'8cc72c02a958de5a8b35a23bb7e3bced8bf840cc0a4e1c820000000000000000');
|
||||
assert.equal(block.rhash(),
|
||||
@ -132,53 +133,66 @@ describe('Block', function() {
|
||||
});
|
||||
|
||||
it('should create a merkle block', function() {
|
||||
var filter = Bloom.fromRate(1000, 0.01, constants.filterFlags.NONE);
|
||||
var item1 = '8e7445bbb8abd4b3174d80fa4c409fea6b94d96b';
|
||||
var item2 = '047b00000078da0dca3b0ec2300c00d0ab4466ed10'
|
||||
var filter, item1, item2, mblock2;
|
||||
|
||||
filter = Bloom.fromRate(1000, 0.01, constants.filterFlags.NONE);
|
||||
|
||||
item1 = '8e7445bbb8abd4b3174d80fa4c409fea6b94d96b';
|
||||
item2 = '047b00000078da0dca3b0ec2300c00d0ab4466ed10'
|
||||
+ 'e763272c6c9ca052972c69e3884a9022084215e2eef'
|
||||
+ '0e6f781656b5d5a87231cd4349e534b6dea55ad4ff55e';
|
||||
|
||||
filter.add(item1, 'hex');
|
||||
filter.add(item2, 'hex');
|
||||
var mblock2 = bcoin.merkleblock.fromBlock(block, filter);
|
||||
|
||||
mblock2 = MerkleBlock.fromBlock(block, filter);
|
||||
|
||||
assert(mblock2.verifyPartial());
|
||||
assert.deepEqual(mblock2.toRaw(), mblock.toRaw());
|
||||
});
|
||||
|
||||
it('should verify a historical block', function() {
|
||||
var view = new CoinView();
|
||||
for (var i = 1; i < block300025.txs.length; i++) {
|
||||
var tx = block300025.txs[i];
|
||||
for (var j = 0; j < tx.inputs.length; j++) {
|
||||
var input = tx.inputs[j];
|
||||
var coin = Coin.fromJSON(input.coin);
|
||||
var height = block300025.height;
|
||||
var i, j, tx, input, coin, flags;
|
||||
|
||||
for (i = 1; i < block300025.txs.length; i++) {
|
||||
tx = block300025.txs[i];
|
||||
for (j = 0; j < tx.inputs.length; j++) {
|
||||
input = tx.inputs[j];
|
||||
coin = Coin.fromJSON(input.coin);
|
||||
view.addCoin(coin);
|
||||
}
|
||||
}
|
||||
|
||||
assert(block.verify());
|
||||
assert(block.txs[0].isCoinbase());
|
||||
assert(block.txs[0].isSane());
|
||||
assert(!block.hasWitness());
|
||||
assert.equal(block.getWeight(), 1136924);
|
||||
var flags = constants.flags.VERIFY_P2SH | constants.flags.VERIFY_DERSIG;
|
||||
for (var i = 1; i < block.txs.length; i++) {
|
||||
var tx = block.txs[i];
|
||||
|
||||
flags = constants.flags.VERIFY_P2SH | constants.flags.VERIFY_DERSIG;
|
||||
|
||||
for (i = 1; i < block.txs.length; i++) {
|
||||
tx = block.txs[i];
|
||||
assert(tx.isSane());
|
||||
assert(tx.checkInputs(view, block300025.height));
|
||||
assert(tx.verify(view, flags));
|
||||
assert(!tx.hasWitness());
|
||||
view.addTX(tx, block300025.height);
|
||||
view.addTX(tx, height);
|
||||
}
|
||||
assert.equal(block.getReward(view), 2507773345);
|
||||
assert.equal(block.getReward(view), block.txs[0].outputs[0].value);
|
||||
|
||||
assert.equal(block.getReward(view, height), 2507773345);
|
||||
assert.equal(block.getReward(view, height), block.txs[0].outputs[0].value);
|
||||
});
|
||||
|
||||
it('should fail with a bad merkle root', function() {
|
||||
var block2 = new bcoin.block(block);
|
||||
var block2 = new Block(block);
|
||||
var ret = {};
|
||||
block2.hash();
|
||||
block2.merkleRoot = constants.NULL_HASH;
|
||||
block2._valid = null;
|
||||
block2._validHeaders = null;
|
||||
var ret = {};
|
||||
assert(!block2.verify(ret));
|
||||
assert.equal(ret.reason, 'bad-txnmrklroot');
|
||||
block2._valid = null;
|
||||
@ -189,10 +203,10 @@ describe('Block', function() {
|
||||
});
|
||||
|
||||
it('should fail on merkle block with a bad merkle root', function() {
|
||||
var mblock2 = new bcoin.merkleblock(mblock);
|
||||
var mblock2 = new MerkleBlock(mblock);
|
||||
var ret = {};
|
||||
mblock2.hash();
|
||||
mblock2.merkleRoot = constants.NULL_HASH;
|
||||
var ret = {};
|
||||
assert(!mblock2.verify(ret));
|
||||
assert.equal(ret.reason, 'bad-txnmrklroot');
|
||||
mblock2._valid = null;
|
||||
@ -204,10 +218,10 @@ describe('Block', function() {
|
||||
});
|
||||
|
||||
it('should fail with a low target', function() {
|
||||
var block2 = new bcoin.block(block);
|
||||
var block2 = new Block(block);
|
||||
var ret = {};
|
||||
block2.hash();
|
||||
block2.bits = 403014710;
|
||||
var ret = {};
|
||||
assert(!block2.verify(ret));
|
||||
assert.equal(ret.reason, 'high-hash');
|
||||
block2._valid = null;
|
||||
@ -218,83 +232,90 @@ describe('Block', function() {
|
||||
});
|
||||
|
||||
it('should fail on duplicate txs', function() {
|
||||
var block2 = new bcoin.block(block);
|
||||
block2.txs.push(block2.txs[block2.txs.length - 1]);
|
||||
var block2 = new Block(block);
|
||||
var ret = {};
|
||||
block2.txs.push(block2.txs[block2.txs.length - 1]);
|
||||
assert(!block2.verify(ret));
|
||||
assert.equal(ret.reason, 'bad-txns-duplicate');
|
||||
});
|
||||
|
||||
it('should verify with headers', function() {
|
||||
var headers = new bcoin.headers(block);
|
||||
var headers = new Headers(block);
|
||||
assert(headers.verify());
|
||||
});
|
||||
|
||||
it('should handle compact block', function(cb) {
|
||||
var cblock = bip152.CompactBlock.fromRaw(cmpct[0], 'hex');
|
||||
var block = bcoin.block.fromRaw(cmpct[1], 'hex');
|
||||
var block = Block.fromRaw(cmpct[1], 'hex');
|
||||
var cblock2 = bip152.CompactBlock.fromBlock(block, false, cblock.keyNonce);
|
||||
var map = {};
|
||||
var i, tx, mempool, result;
|
||||
|
||||
assert.equal(cblock.toRaw().toString('hex'), cmpct[0]);
|
||||
assert.equal(cblock2.toRaw().toString('hex'), cmpct[0]);
|
||||
|
||||
for (var i = 0; i < block.txs.length; i++) {
|
||||
var tx = block.txs[i];
|
||||
for (i = 0; i < block.txs.length; i++) {
|
||||
tx = block.txs[i];
|
||||
map[tx.hash('hex')] = tx;
|
||||
}
|
||||
|
||||
var fakeMempool = {
|
||||
getSnapshot: function(callback) {
|
||||
mempool = {
|
||||
getSnapshot: function() {
|
||||
return Object.keys(map);
|
||||
},
|
||||
getTX: function(hash, callback) {
|
||||
getTX: function(hash) {
|
||||
return map[hash];
|
||||
}
|
||||
};
|
||||
|
||||
assert.equal(cblock.sid(block.txs[1].hash()), 125673511480291);
|
||||
|
||||
var result = cblock.fillMempool(false, fakeMempool);
|
||||
result = cblock.fillMempool(false, mempool);
|
||||
assert(result);
|
||||
for (var i = 0; i < cblock.available.length; i++)
|
||||
|
||||
for (i = 0; i < cblock.available.length; i++)
|
||||
assert(cblock.available[i]);
|
||||
assert.equal(cblock.toBlock().toRaw().toString('hex'), block.toRaw().toString('hex'));
|
||||
|
||||
assert.equal(
|
||||
cblock.toBlock().toRaw().toString('hex'),
|
||||
block.toRaw().toString('hex'));
|
||||
|
||||
cb();
|
||||
});
|
||||
|
||||
it('should handle half-full compact block', function(cb) {
|
||||
var cblock = bip152.CompactBlock.fromRaw(cmpct[0], 'hex');
|
||||
var block = bcoin.block.fromRaw(cmpct[1], 'hex');
|
||||
var block = Block.fromRaw(cmpct[1], 'hex');
|
||||
var cblock2 = bip152.CompactBlock.fromBlock(block, false, cblock.keyNonce);
|
||||
var map = {};
|
||||
var i, tx, mid, keys, mempool, result, req, res;
|
||||
|
||||
assert.equal(cblock.toRaw().toString('hex'), cmpct[0]);
|
||||
assert.equal(cblock2.toRaw().toString('hex'), cmpct[0]);
|
||||
|
||||
for (var i = 0; i < block.txs.length; i++) {
|
||||
var tx = block.txs[i];
|
||||
for (i = 0; i < block.txs.length; i++) {
|
||||
tx = block.txs[i];
|
||||
map[tx.hash('hex')] = tx;
|
||||
}
|
||||
|
||||
var mid = block.txs.length >>> 1;
|
||||
var keys = Object.keys(map).slice(0, mid);
|
||||
mid = block.txs.length >>> 1;
|
||||
keys = Object.keys(map).slice(0, mid);
|
||||
|
||||
var fakeMempool = {
|
||||
getSnapshot: function(callback) {
|
||||
mempool = {
|
||||
getSnapshot: function() {
|
||||
return keys;
|
||||
},
|
||||
getTX: function(hash, callback) {
|
||||
getTX: function(hash) {
|
||||
return map[hash];
|
||||
}
|
||||
};
|
||||
|
||||
assert.equal(cblock.sid(block.txs[1].hash()), 125673511480291);
|
||||
|
||||
var result = cblock.fillMempool(false, fakeMempool);
|
||||
result = cblock.fillMempool(false, mempool);
|
||||
assert(!result);
|
||||
|
||||
var req = cblock.toRequest();
|
||||
req = cblock.toRequest();
|
||||
assert.equal(req.hash, cblock.hash('hex'));
|
||||
assert.deepEqual(req.indexes, [5, 6, 7, 8, 9]);
|
||||
|
||||
@ -302,102 +323,107 @@ describe('Block', function() {
|
||||
assert.equal(req.hash, cblock.hash('hex'));
|
||||
assert.deepEqual(req.indexes, [5, 6, 7, 8, 9]);
|
||||
|
||||
var res = bip152.TXResponse.fromBlock(block, req);
|
||||
res = bip152.TXResponse.fromBlock(block, req);
|
||||
res = bip152.TXResponse.fromRaw(res.toRaw());
|
||||
|
||||
var result = cblock.fillMissing(res);
|
||||
result = cblock.fillMissing(res);
|
||||
assert(result);
|
||||
|
||||
for (var i = 0; i < cblock.available.length; i++)
|
||||
for (i = 0; i < cblock.available.length; i++)
|
||||
assert(cblock.available[i]);
|
||||
|
||||
assert.equal(cblock.toBlock().toRaw().toString('hex'), block.toRaw().toString('hex'));
|
||||
assert.equal(
|
||||
cblock.toBlock().toRaw().toString('hex'),
|
||||
block.toRaw().toString('hex'));
|
||||
|
||||
cb();
|
||||
});
|
||||
|
||||
it('should handle compact block', function(cb) {
|
||||
var cblock = bip152.CompactBlock.fromRaw(cmpct2, 'hex');
|
||||
var block = bcoin.block.fromRaw(cmpct2block);
|
||||
var block = Block.fromRaw(cmpct2block);
|
||||
var cblock2 = bip152.CompactBlock.fromBlock(block, false, cblock.keyNonce);
|
||||
var map = {};
|
||||
var i, tx, result, mempool;
|
||||
|
||||
assert.equal(cblock.toRaw().toString('hex'), cmpct2);
|
||||
assert.equal(cblock2.toRaw().toString('hex'), cmpct2);
|
||||
|
||||
for (var i = 0; i < block.txs.length; i++) {
|
||||
var tx = block.txs[i];
|
||||
for (i = 0; i < block.txs.length; i++) {
|
||||
tx = block.txs[i];
|
||||
map[tx.hash('hex')] = tx;
|
||||
}
|
||||
|
||||
var fakeMempool = {
|
||||
getSnapshot: function(callback) {
|
||||
mempool = {
|
||||
getSnapshot: function() {
|
||||
return Object.keys(map);
|
||||
},
|
||||
getTX: function(hash, callback) {
|
||||
getTX: function(hash) {
|
||||
return map[hash];
|
||||
}
|
||||
};
|
||||
|
||||
//assert.equal(cblock.sid(block.txs[1].hash()), 125673511480291);
|
||||
|
||||
var result = cblock.fillMempool(false, fakeMempool);
|
||||
result = cblock.fillMempool(false, mempool);
|
||||
assert(result);
|
||||
for (var i = 0; i < cblock.available.length; i++)
|
||||
|
||||
for (i = 0; i < cblock.available.length; i++)
|
||||
assert(cblock.available[i]);
|
||||
assert.equal(cblock.toBlock().toRaw().toString('hex'), block.toRaw().toString('hex'));
|
||||
|
||||
assert.equal(
|
||||
cblock.toBlock().toRaw().toString('hex'),
|
||||
block.toRaw().toString('hex'));
|
||||
|
||||
cb();
|
||||
});
|
||||
|
||||
it('should handle half-full compact block', function(cb) {
|
||||
var cblock = bip152.CompactBlock.fromRaw(cmpct2, 'hex');
|
||||
var block = bcoin.block.fromRaw(cmpct2block);
|
||||
var block = Block.fromRaw(cmpct2block);
|
||||
var cblock2 = bip152.CompactBlock.fromBlock(block, false, cblock.keyNonce);
|
||||
var map = {};
|
||||
var i, tx, mid, keys, mempool, result, req, res;
|
||||
|
||||
assert.equal(cblock.toRaw().toString('hex'), cmpct2);
|
||||
assert.equal(cblock2.toRaw().toString('hex'), cmpct2);
|
||||
|
||||
for (var i = 0; i < block.txs.length; i++) {
|
||||
var tx = block.txs[i];
|
||||
for (i = 0; i < block.txs.length; i++) {
|
||||
tx = block.txs[i];
|
||||
map[tx.hash('hex')] = tx;
|
||||
}
|
||||
|
||||
var mid = block.txs.length >>> 1;
|
||||
var keys = Object.keys(map).slice(0, mid);
|
||||
mid = block.txs.length >>> 1;
|
||||
keys = Object.keys(map).slice(0, mid);
|
||||
|
||||
var fakeMempool = {
|
||||
getSnapshot: function(callback) {
|
||||
mempool = {
|
||||
getSnapshot: function() {
|
||||
return keys;
|
||||
},
|
||||
getTX: function(hash, callback) {
|
||||
getTX: function(hash) {
|
||||
return map[hash];
|
||||
}
|
||||
};
|
||||
|
||||
//assert.equal(cblock.sid(block.txs[1].hash()), 125673511480291);
|
||||
|
||||
var result = cblock.fillMempool(false, fakeMempool);
|
||||
result = cblock.fillMempool(false, mempool);
|
||||
assert(!result);
|
||||
|
||||
var req = cblock.toRequest();
|
||||
req = cblock.toRequest();
|
||||
assert.equal(req.hash, cblock.hash('hex'));
|
||||
//assert.deepEqual(req.indexes, [5, 6, 7, 8, 9]);
|
||||
|
||||
req = bip152.TXRequest.fromRaw(req.toRaw());
|
||||
assert.equal(req.hash, cblock.hash('hex'));
|
||||
//assert.deepEqual(req.indexes, [5, 6, 7, 8, 9]);
|
||||
|
||||
var res = bip152.TXResponse.fromBlock(block, req);
|
||||
res = bip152.TXResponse.fromBlock(block, req);
|
||||
res = bip152.TXResponse.fromRaw(res.toRaw());
|
||||
|
||||
var result = cblock.fillMissing(res);
|
||||
result = cblock.fillMissing(res);
|
||||
assert(result);
|
||||
|
||||
for (var i = 0; i < cblock.available.length; i++)
|
||||
for (i = 0; i < cblock.available.length; i++)
|
||||
assert(cblock.available[i]);
|
||||
|
||||
assert.equal(cblock.toBlock().toRaw().toString('hex'), block.toRaw().toString('hex'));
|
||||
assert.equal(
|
||||
cblock.toBlock().toRaw().toString('hex'),
|
||||
block.toRaw().toString('hex'));
|
||||
|
||||
cb();
|
||||
});
|
||||
|
||||
@ -1,25 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
var bcoin = require('../').set('main');
|
||||
var assert = require('assert');
|
||||
var Script = bcoin.script;
|
||||
var Stack = bcoin.stack;
|
||||
var util = bcoin.util;
|
||||
var Script = require('../lib/script/script');
|
||||
var Witness = require('../lib/script/witness');
|
||||
var Stack = require('../lib/script/stack');
|
||||
var TX = require('../lib/primitives/tx');
|
||||
var util = require('../lib/utils/util');
|
||||
var crypto = require('../lib/crypto/crypto');
|
||||
var constants = bcoin.constants;
|
||||
var opcodes = bcoin.constants.opcodes;
|
||||
var constants = require('../lib/protocol/constants');
|
||||
var opcodes = constants.opcodes;
|
||||
|
||||
var scripts = require('./data/script_tests');
|
||||
var BN = require('bn.js');
|
||||
|
||||
function success(res, stack) {
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
if (stack.length === 0)
|
||||
return false;
|
||||
|
||||
if (!Script.bool(stack.top(-1)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
describe('Script', function() {
|
||||
it('should encode/decode script', function() {
|
||||
var src = '20'
|
||||
var src, decoded, dst;
|
||||
|
||||
src = '20'
|
||||
+ '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'
|
||||
+ '20'
|
||||
+ '101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f'
|
||||
+ 'ac';
|
||||
|
||||
var decoded = bcoin.script(new Buffer(src, 'hex'));
|
||||
decoded = Script.fromRaw(src, 'hex');
|
||||
assert.equal(decoded.code.length, 3);
|
||||
assert.equal(decoded.code[0].data.toString('hex'),
|
||||
'000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f');
|
||||
@ -27,33 +43,36 @@ describe('Script', function() {
|
||||
'101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f');
|
||||
assert.equal(decoded.code[2].value, opcodes.OP_CHECKSIG);
|
||||
|
||||
var dst = decoded.toRaw();
|
||||
dst = decoded.toRaw();
|
||||
assert.equal(dst.toString('hex'), src);
|
||||
});
|
||||
|
||||
it('should encode/decode numbers', function() {
|
||||
var script = [0, 0x51, 0x52, 0x60];
|
||||
var encoded = bcoin.script.fromArray(script).raw;
|
||||
var decoded = bcoin.script(encoded).toArray();
|
||||
var encoded = Script.fromArray(script).raw;
|
||||
var decoded = Script(encoded).toArray();
|
||||
assert.deepEqual(decoded, script);
|
||||
});
|
||||
|
||||
it('should recognize a P2SH output', function() {
|
||||
var hex = 'a91419a7d869032368fd1f1e26e5e73a4ad0e474960e87';
|
||||
var decoded = bcoin.script.fromRaw(hex, 'hex');
|
||||
var decoded = Script.fromRaw(hex, 'hex');
|
||||
assert(decoded.isScripthash());
|
||||
});
|
||||
|
||||
it('should recognize a Null Data output', function() {
|
||||
var hex = '6a28590c080112220a1b353930632e6f7267282a5f'
|
||||
+ '5e294f7665726c6179404f7261636c65103b1a010c';
|
||||
var decoded = bcoin.script.fromRaw(hex, 'hex');
|
||||
var decoded = Script.fromRaw(hex, 'hex');
|
||||
assert(decoded.isNulldata());
|
||||
});
|
||||
|
||||
it('should handle if statements correctly', function() {
|
||||
var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]);
|
||||
var prevOutScript = new Script([
|
||||
var input, output, stack, res;
|
||||
|
||||
input = new Script([opcodes.OP_1, opcodes.OP_2]);
|
||||
|
||||
output = new Script([
|
||||
opcodes.OP_2,
|
||||
opcodes.OP_EQUAL,
|
||||
opcodes.OP_IF,
|
||||
@ -63,14 +82,18 @@ describe('Script', function() {
|
||||
opcodes.OP_ENDIF,
|
||||
opcodes.OP_5
|
||||
]);
|
||||
var stack = new Stack();
|
||||
inputScript.execute(stack);
|
||||
var res = prevOutScript.execute(stack);
|
||||
|
||||
stack = new Stack();
|
||||
|
||||
input.execute(stack);
|
||||
|
||||
res = output.execute(stack);
|
||||
assert(res);
|
||||
|
||||
assert.deepEqual(stack.items, [[1], [3], [5]]);
|
||||
|
||||
var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]);
|
||||
var prevOutScript = new Script([
|
||||
input = new Script([opcodes.OP_1, opcodes.OP_2]);
|
||||
output = new Script([
|
||||
opcodes.OP_9,
|
||||
opcodes.OP_EQUAL,
|
||||
opcodes.OP_IF,
|
||||
@ -80,14 +103,16 @@ describe('Script', function() {
|
||||
opcodes.OP_ENDIF,
|
||||
opcodes.OP_5
|
||||
]);
|
||||
var stack = new Stack();
|
||||
inputScript.execute(stack);
|
||||
var res = prevOutScript.execute(stack);
|
||||
|
||||
stack = new Stack();
|
||||
input.execute(stack);
|
||||
|
||||
res = output.execute(stack);
|
||||
assert(res);
|
||||
assert.deepEqual(stack.items, [[1], [4], [5]]);
|
||||
|
||||
var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]);
|
||||
var prevOutScript = new Script([
|
||||
input = new Script([opcodes.OP_1, opcodes.OP_2]);
|
||||
output = new Script([
|
||||
opcodes.OP_2,
|
||||
opcodes.OP_EQUAL,
|
||||
opcodes.OP_IF,
|
||||
@ -95,14 +120,17 @@ describe('Script', function() {
|
||||
opcodes.OP_ENDIF,
|
||||
opcodes.OP_5
|
||||
]);
|
||||
var stack = new Stack();
|
||||
inputScript.execute(stack);
|
||||
var res = prevOutScript.execute(stack);
|
||||
|
||||
stack = new Stack();
|
||||
|
||||
input.execute(stack);
|
||||
|
||||
res = output.execute(stack);
|
||||
assert(res);
|
||||
assert.deepEqual(stack.items, [[1], [3], [5]]);
|
||||
|
||||
var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]);
|
||||
var prevOutScript = new Script([
|
||||
input = new Script([opcodes.OP_1, opcodes.OP_2]);
|
||||
output = new Script([
|
||||
opcodes.OP_9,
|
||||
opcodes.OP_EQUAL,
|
||||
opcodes.OP_IF,
|
||||
@ -110,14 +138,16 @@ describe('Script', function() {
|
||||
opcodes.OP_ENDIF,
|
||||
opcodes.OP_5
|
||||
]);
|
||||
var stack = new Stack();
|
||||
inputScript.execute(stack);
|
||||
var res = prevOutScript.execute(stack);
|
||||
|
||||
stack = new Stack();
|
||||
input.execute(stack);
|
||||
|
||||
res = output.execute(stack);
|
||||
assert(res);
|
||||
assert.deepEqual(stack.items, [[1], [5]]);
|
||||
|
||||
var inputScript = new Script([opcodes.OP_1, opcodes.OP_2]);
|
||||
var prevOutScript = new Script([
|
||||
input = new Script([opcodes.OP_1, opcodes.OP_2]);
|
||||
output = new Script([
|
||||
opcodes.OP_9,
|
||||
opcodes.OP_EQUAL,
|
||||
opcodes.OP_NOTIF,
|
||||
@ -125,138 +155,66 @@ describe('Script', function() {
|
||||
opcodes.OP_ENDIF,
|
||||
opcodes.OP_5
|
||||
]);
|
||||
var stack = new Stack();
|
||||
inputScript.execute(stack);
|
||||
var res = prevOutScript.execute(stack);
|
||||
stack = new Stack();
|
||||
input.execute(stack);
|
||||
|
||||
res = output.execute(stack);
|
||||
assert(res);
|
||||
assert.deepEqual(stack.items, [[1], [3], [5]]);
|
||||
});
|
||||
|
||||
function success(res, stack) {
|
||||
if (!res)
|
||||
return false;
|
||||
if (stack.length === 0)
|
||||
return false;
|
||||
if (!bcoin.script.bool(stack.pop()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
it('should handle bad size pushes correctly.', function() {
|
||||
var err;
|
||||
var stack = new bcoin.stack();
|
||||
var s = bcoin.script.fromString(
|
||||
'OP_1 OP_DUP OP_PUSHDATA1'
|
||||
);
|
||||
assert(util.equal(s.raw, new Buffer('51764c', 'hex')));
|
||||
delete s.raw;
|
||||
assert(util.equal(s.encode(), new Buffer('51764c', 'hex')));
|
||||
try {
|
||||
s.execute(stack);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
assert(err);
|
||||
assert(err.code === 'BAD_OPCODE');
|
||||
var s = bcoin.script.fromString(
|
||||
'OP_1 OP_DUP OP_PUSHDATA2 0x01'
|
||||
);
|
||||
assert(util.equal(s.raw, new Buffer('51764d01', 'hex')));
|
||||
delete s.raw;
|
||||
assert(util.equal(s.encode(), new Buffer('51764d01', 'hex')));
|
||||
err = null;
|
||||
try {
|
||||
s.execute(stack);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
assert(err);
|
||||
assert(err.code === 'BAD_OPCODE');
|
||||
var s = bcoin.script.fromString(
|
||||
'OP_1 OP_DUP OP_PUSHDATA4 0x0001'
|
||||
);
|
||||
assert(util.equal(s.raw, new Buffer('51764e0001', 'hex')));
|
||||
delete s.raw;
|
||||
assert(util.equal(s.encode(), new Buffer('51764e0001', 'hex')));
|
||||
err = null;
|
||||
try {
|
||||
s.execute(stack);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
assert(err);
|
||||
assert(err.code === 'BAD_OPCODE');
|
||||
var s = bcoin.script.fromString(
|
||||
'OP_1 OP_DUP OP_PUSHDATA1 0x02 0x01'
|
||||
);
|
||||
assert(util.equal(s.raw, new Buffer('51764c0201', 'hex')));
|
||||
delete s.raw;
|
||||
assert(util.equal(s.encode(), new Buffer('51764c0201', 'hex')));
|
||||
err = null;
|
||||
try {
|
||||
s.execute(stack);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
assert(err);
|
||||
assert(err.code === 'BAD_OPCODE');
|
||||
var s = bcoin.script.fromString(
|
||||
'OP_1 OP_DUP OP_PUSHDATA2 0x0200 0x01'
|
||||
);
|
||||
assert(util.equal(s.raw, new Buffer('51764d020001', 'hex')));
|
||||
delete s.raw;
|
||||
assert(util.equal(s.encode(), new Buffer('51764d020001', 'hex')));
|
||||
err = null;
|
||||
try {
|
||||
s.execute(stack);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
assert(err);
|
||||
assert(err.code === 'BAD_OPCODE');
|
||||
});
|
||||
*/
|
||||
|
||||
it('should handle CScriptNums correctly', function() {
|
||||
var s = new bcoin.script([
|
||||
var s1, s2, stack;
|
||||
|
||||
s1 = new Script([
|
||||
new Buffer([0xff, 0xff, 0xff, 0x7f]),
|
||||
opcodes.OP_NEGATE,
|
||||
opcodes.OP_DUP,
|
||||
opcodes.OP_ADD
|
||||
]);
|
||||
var s2 = new bcoin.script([
|
||||
|
||||
s2 = new Script([
|
||||
new Buffer([0xfe, 0xff, 0xff, 0xff, 0x80]),
|
||||
opcodes.OP_EQUAL
|
||||
]);
|
||||
var stack = new bcoin.stack();
|
||||
assert(s.execute(stack));
|
||||
|
||||
stack = new Stack();
|
||||
|
||||
assert(s1.execute(stack));
|
||||
assert(success(s2.execute(stack), stack));
|
||||
});
|
||||
|
||||
it('should handle CScriptNums correctly', function() {
|
||||
var s = new bcoin.script([
|
||||
var s1, s2, stack;
|
||||
|
||||
s1 = new Script([
|
||||
opcodes.OP_11,
|
||||
opcodes.OP_10,
|
||||
opcodes.OP_1,
|
||||
opcodes.OP_ADD
|
||||
]);
|
||||
var s2 = new bcoin.script([
|
||||
|
||||
s2 = new Script([
|
||||
opcodes.OP_NUMNOTEQUAL,
|
||||
opcodes.OP_NOT
|
||||
]);
|
||||
var stack = new bcoin.stack();
|
||||
assert(s.execute(stack));
|
||||
|
||||
stack = new Stack();
|
||||
|
||||
assert(s1.execute(stack));
|
||||
assert(success(s2.execute(stack), stack));
|
||||
});
|
||||
|
||||
it('should handle OP_ROLL correctly', function() {
|
||||
var s = new bcoin.script([
|
||||
var s1, s2, stack;
|
||||
|
||||
s1 = new Script([
|
||||
new Buffer([0x16]),
|
||||
new Buffer([0x15]),
|
||||
new Buffer([0x14])
|
||||
]);
|
||||
var s2 = new bcoin.script([
|
||||
|
||||
s2 = new Script([
|
||||
opcodes.OP_0,
|
||||
opcodes.OP_ROLL,
|
||||
new Buffer([0x14]),
|
||||
@ -265,13 +223,14 @@ describe('Script', function() {
|
||||
opcodes.OP_2,
|
||||
opcodes.OP_EQUAL
|
||||
]);
|
||||
var stack = new bcoin.stack();
|
||||
assert(s.execute(stack));
|
||||
|
||||
stack = new Stack();
|
||||
|
||||
assert(s1.execute(stack));
|
||||
assert(success(s2.execute(stack), stack));
|
||||
});
|
||||
|
||||
scripts.forEach(function(data) {
|
||||
// ["Format is: [[wit...]?, scriptSig, scriptPubKey, flags, expected_scripterror, ... comments]"],
|
||||
var witness = Array.isArray(data[0]) ? data.shift() : [];
|
||||
var input = data[0] ? data[0].trim() : data[0] || '';
|
||||
var output = data[1] ? data[1].trim() : data[1] || '';
|
||||
@ -293,9 +252,9 @@ describe('Script', function() {
|
||||
if (witness.length !== 0)
|
||||
amount = witness.pop() * 100000000;
|
||||
|
||||
witness = bcoin.witness.fromString(witness);
|
||||
input = bcoin.script.fromString(input);
|
||||
output = bcoin.script.fromString(output);
|
||||
witness = Witness.fromString(witness);
|
||||
input = Script.fromString(input);
|
||||
output = Script.fromString(output);
|
||||
|
||||
for (i = 0; i < flags.length; i++) {
|
||||
name = 'VERIFY_' + flags[i];
|
||||
@ -311,7 +270,7 @@ describe('Script', function() {
|
||||
var prev, tx, err, res;
|
||||
|
||||
// Funding transaction.
|
||||
prev = bcoin.tx({
|
||||
prev = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [{
|
||||
@ -319,8 +278,8 @@ describe('Script', function() {
|
||||
hash: constants.NULL_HASH,
|
||||
index: 0xffffffff
|
||||
},
|
||||
script: [bcoin.script.array(0), bcoin.script.array(0)],
|
||||
witness: new bcoin.witness(),
|
||||
script: [Script.array(0), Script.array(0)],
|
||||
witness: new Witness(),
|
||||
sequence: 0xffffffff
|
||||
}],
|
||||
outputs: [{
|
||||
@ -331,7 +290,7 @@ describe('Script', function() {
|
||||
});
|
||||
|
||||
// Spending transaction.
|
||||
tx = bcoin.tx({
|
||||
tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [{
|
||||
@ -344,7 +303,7 @@ describe('Script', function() {
|
||||
sequence: 0xffffffff
|
||||
}],
|
||||
outputs: [{
|
||||
script: new bcoin.script(),
|
||||
script: new Script(),
|
||||
value: amount
|
||||
}],
|
||||
locktime: 0
|
||||
@ -392,158 +351,4 @@ describe('Script', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
it('should execute FindAndDelete correctly', function() {
|
||||
var s, d, expect;
|
||||
|
||||
function del(s) {
|
||||
s.mutable = true;
|
||||
return s;
|
||||
}
|
||||
|
||||
s = bcoin.script.fromString('OP_1 OP_2');
|
||||
del(s);
|
||||
d = new bcoin.script();
|
||||
expect = s.clone();
|
||||
assert.equal(s.findAndDelete(d.encode()), 0);
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
|
||||
s = bcoin.script.fromString('OP_1 OP_2 OP_3');
|
||||
del(s);
|
||||
d = bcoin.script.fromString('OP_2');
|
||||
del(d);
|
||||
expect = bcoin.script.fromString('OP_1 OP_3');
|
||||
del(expect);
|
||||
assert.equal(s.findAndDelete(d.encode()), 1);
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
|
||||
s = bcoin.script.fromString('OP_3 OP_1 OP_3 OP_3 OP_4 OP_3');
|
||||
del(s);
|
||||
d = bcoin.script.fromString('OP_3');
|
||||
del(d);
|
||||
expect = bcoin.script.fromString('OP_1 OP_4');
|
||||
del(expect);
|
||||
assert.equal(s.findAndDelete(d.encode()), 4);
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
|
||||
s = bcoin.script.fromRaw('0302ff03', 'hex');
|
||||
del(s);
|
||||
d = bcoin.script.fromRaw('0302ff03', 'hex');
|
||||
del(d);
|
||||
expect = new bcoin.script();
|
||||
del(expect);
|
||||
assert.equal(s.findAndDelete(d.encode()), 1);
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
|
||||
s = bcoin.script.fromRaw('0302ff030302ff03', 'hex');
|
||||
del(s);
|
||||
d = bcoin.script.fromRaw('0302ff03', 'hex');
|
||||
del(d);
|
||||
expect = new bcoin.script();
|
||||
del(expect);
|
||||
assert.equal(s.findAndDelete(d.encode()), 2);
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
|
||||
s = bcoin.script.fromRaw('0302ff030302ff03', 'hex');
|
||||
del(s);
|
||||
d = bcoin.script.fromRaw('02', 'hex');
|
||||
del(d);
|
||||
expect = s.clone();
|
||||
del(expect);
|
||||
//assert.equal(s.findAndDelete(d.encode()), 0);
|
||||
s.findAndDelete(d.encode());
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
|
||||
s = bcoin.script.fromRaw('0302ff030302ff03', 'hex');
|
||||
del(s);
|
||||
d = bcoin.script.fromRaw('ff', 'hex');
|
||||
del(d);
|
||||
expect = s.clone();
|
||||
del(expect);
|
||||
assert.equal(s.findAndDelete(d.encode()), 0);
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
|
||||
s = bcoin.script.fromRaw('0302ff030302ff03', 'hex');
|
||||
del(s);
|
||||
d = bcoin.script.fromRaw('03', 'hex');
|
||||
del(d);
|
||||
expect = new bcoin.script([new Buffer([0xff, 0x03]), new Buffer([0xff, 0x03])]);
|
||||
del(expect);
|
||||
assert.equal(s.findAndDelete(d.encode()), 2);
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
|
||||
s = bcoin.script.fromRaw('02feed5169', 'hex');
|
||||
del(s);
|
||||
d = bcoin.script.fromRaw('feed51', 'hex');
|
||||
del(d);
|
||||
expect = s.clone();
|
||||
del(expect);
|
||||
assert.equal(s.findAndDelete(d.encode()), 0);
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
|
||||
s = bcoin.script.fromRaw('02feed5169', 'hex');
|
||||
del(s);
|
||||
d = bcoin.script.fromRaw('02feed51', 'hex');
|
||||
del(d);
|
||||
expect = bcoin.script.fromRaw('69', 'hex');
|
||||
del(expect);
|
||||
assert.equal(s.findAndDelete(d.encode()), 1);
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
|
||||
s = bcoin.script.fromRaw('516902feed5169', 'hex');
|
||||
del(s);
|
||||
d = bcoin.script.fromRaw('feed51', 'hex');
|
||||
del(d);
|
||||
expect = s.clone();
|
||||
del(expect);
|
||||
assert.equal(s.findAndDelete(d.encode()), 0);
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
|
||||
s = bcoin.script.fromRaw('516902feed5169', 'hex');
|
||||
del(s);
|
||||
d = bcoin.script.fromRaw('02feed51', 'hex');
|
||||
del(d);
|
||||
expect = bcoin.script.fromRaw('516969', 'hex');
|
||||
del(expect);
|
||||
assert.equal(s.findAndDelete(d.encode()), 1);
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
|
||||
s = bcoin.script.fromString('OP_0 OP_0 OP_1 OP_1');
|
||||
del(s);
|
||||
d = bcoin.script.fromString('OP_0 OP_1');
|
||||
del(d);
|
||||
expect = bcoin.script.fromString('OP_0 OP_1');
|
||||
del(expect);
|
||||
assert.equal(s.findAndDelete(d.encode()), 1);
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
|
||||
s = bcoin.script.fromString('OP_0 OP_0 OP_1 OP_0 OP_1 OP_1');
|
||||
del(s);
|
||||
d = bcoin.script.fromString('OP_0 OP_1');
|
||||
del(d);
|
||||
expect = bcoin.script.fromString('OP_0 OP_1');
|
||||
del(expect);
|
||||
assert.equal(s.findAndDelete(d.encode()), 2);
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
|
||||
s = bcoin.script.fromRaw('0003feed', 'hex');
|
||||
del(s);
|
||||
d = bcoin.script.fromRaw('03feed', 'hex');
|
||||
del(d);
|
||||
expect = bcoin.script.fromRaw('00', 'hex');
|
||||
del(expect);
|
||||
assert.equal(s.findAndDelete(d.encode()), 1);
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
|
||||
s = bcoin.script.fromRaw('0003feed', 'hex');
|
||||
del(s);
|
||||
d = bcoin.script.fromRaw('00', 'hex');
|
||||
del(d);
|
||||
expect = bcoin.script.fromRaw('03feed', 'hex');
|
||||
del(expect);
|
||||
assert.equal(s.findAndDelete(d.encode()), 1);
|
||||
assert.deepEqual(s.encode(), expect.encode());
|
||||
});
|
||||
*/
|
||||
});
|
||||
|
||||
313
test/tx-test.js
313
test/tx-test.js
@ -1,18 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
var BN = require('bn.js');
|
||||
var bcoin = require('../').set('main');
|
||||
var fs = require('fs');
|
||||
var assert = require('assert');
|
||||
var util = bcoin.util;
|
||||
var util = require('../lib/utils/util');
|
||||
var encoding = require('../lib/utils/encoding');
|
||||
var crypto = require('../lib/crypto/crypto');
|
||||
var constants = bcoin.constants;
|
||||
var opcodes = bcoin.constants.opcodes;
|
||||
var constants = require('../lib/protocol/constants');
|
||||
var Network = require('../lib/protocol/network');
|
||||
var TX = require('../lib/primitives/tx');
|
||||
var Block = require('../lib/primitives/block');
|
||||
var Coin = require('../lib/primitives/coin');
|
||||
var Output = require('../lib/primitives/output');
|
||||
var Script = require('../lib/script/script');
|
||||
var CoinView = require('../lib/blockchain/coinview');
|
||||
|
||||
var valid = require('./data/tx_valid.json');
|
||||
var invalid = require('./data/tx_invalid.json');
|
||||
var sighash = require('./data/sighash.json');
|
||||
var fs = require('fs');
|
||||
var CoinView = require('../lib/blockchain/coinview');
|
||||
var tx1 = parseTX('data/tx1.hex');
|
||||
var tx2 = parseTX('data/tx2.hex');
|
||||
var tx3 = parseTX('data/tx3.hex');
|
||||
@ -24,13 +28,13 @@ function parseTX(file) {
|
||||
var data = fs.readFileSync(__dirname + '/' + file, 'utf8');
|
||||
var parts = data.trim().split(/\n+/);
|
||||
var raw = parts[0];
|
||||
var tx = bcoin.tx.fromRaw(raw.trim(), 'hex');
|
||||
var tx = TX.fromRaw(raw.trim(), 'hex');
|
||||
var view = new CoinView();
|
||||
var i, prev;
|
||||
|
||||
for (i = 1; i < parts.length; i++) {
|
||||
raw = parts[i];
|
||||
prev = bcoin.tx.fromRaw(raw.trim(), 'hex');
|
||||
prev = TX.fromRaw(raw.trim(), 'hex');
|
||||
view.addTX(prev, -1);
|
||||
}
|
||||
|
||||
@ -56,6 +60,56 @@ function clearCache(tx, nocache) {
|
||||
tx._hashOutputs = null;
|
||||
}
|
||||
|
||||
function parseTest(data) {
|
||||
var coins = data[0];
|
||||
var tx = TX.fromRaw(data[1], 'hex');
|
||||
var flags = data[2] ? data[2].trim().split(/,\s*/) : [];
|
||||
var view = new CoinView();
|
||||
var flag = 0;
|
||||
var i, name, coin;
|
||||
|
||||
for (i = 0; i < flags.length; i++) {
|
||||
name = 'VERIFY_' + flags[i];
|
||||
assert(constants.flags[name] != null, 'Unknown flag.');
|
||||
flag |= constants.flags[name];
|
||||
}
|
||||
|
||||
flags = flag;
|
||||
|
||||
coins.forEach(function(data) {
|
||||
var hash = data[0];
|
||||
var index = data[1];
|
||||
var script = Script.fromString(data[2]);
|
||||
var value = data[3];
|
||||
var coin;
|
||||
|
||||
coin = new Coin({
|
||||
version: 1,
|
||||
height: -1,
|
||||
coinbase: false,
|
||||
hash: util.revHex(hash),
|
||||
index: index,
|
||||
script: script,
|
||||
value: value != null ? parseInt(value, 10) : 0
|
||||
});
|
||||
|
||||
if (index !== -1)
|
||||
view.addCoin(coin);
|
||||
});
|
||||
|
||||
coin = view.getOutput(tx.inputs[0]);
|
||||
|
||||
return {
|
||||
tx: tx,
|
||||
flags: flags,
|
||||
view: view,
|
||||
comments: coin
|
||||
? util.inspectify(coin.script, false)
|
||||
: 'coinbase',
|
||||
data: data
|
||||
};
|
||||
}
|
||||
|
||||
describe('TX', function() {
|
||||
var raw = '010000000125393c67cd4f581456dd0805fa8e9db3abdf90dbe1d4b53e28' +
|
||||
'6490f35d22b6f2010000006b483045022100f4fa5ced20d2dbd2f905809d' +
|
||||
@ -98,14 +152,14 @@ describe('TX', function() {
|
||||
var suffix = nocache ? ' without cache' : ' with cache';
|
||||
|
||||
it('should decode/encode with parser/framer' + suffix, function() {
|
||||
var tx = bcoin.tx.fromRaw(raw, 'hex');
|
||||
var tx = TX.fromRaw(raw, 'hex');
|
||||
clearCache(tx, nocache);
|
||||
assert.equal(tx.toRaw().toString('hex'), raw);
|
||||
});
|
||||
|
||||
it('should be verifiable' + suffix, function() {
|
||||
var tx = bcoin.tx.fromRaw(raw, 'hex');
|
||||
var p = bcoin.tx.fromRaw(inp, 'hex');
|
||||
var tx = TX.fromRaw(raw, 'hex');
|
||||
var p = TX.fromRaw(inp, 'hex');
|
||||
var view = new CoinView();
|
||||
view.addTX(p, -1);
|
||||
|
||||
@ -163,63 +217,14 @@ describe('TX', function() {
|
||||
raw2 = wtx.tx.toRaw();
|
||||
assert.deepEqual(raw1, raw2);
|
||||
|
||||
wtx2 = bcoin.tx.fromRaw(raw2);
|
||||
wtx2 = TX.fromRaw(raw2);
|
||||
clearCache(wtx2, nocache);
|
||||
|
||||
assert.equal(wtx.tx.hash('hex'), wtx2.hash('hex'));
|
||||
assert.equal(wtx.tx.witnessHash('hex'), wtx2.witnessHash('hex'));
|
||||
});
|
||||
|
||||
function parseTest(data) {
|
||||
var coins = data[0];
|
||||
var tx = bcoin.tx.fromRaw(data[1], 'hex');
|
||||
var flags = data[2] ? data[2].trim().split(/,\s*/) : [];
|
||||
var view = new CoinView();
|
||||
var flag = 0;
|
||||
var i, name;
|
||||
|
||||
for (i = 0; i < flags.length; i++) {
|
||||
name = 'VERIFY_' + flags[i];
|
||||
assert(constants.flags[name] != null, 'Unknown flag.');
|
||||
flag |= constants.flags[name];
|
||||
}
|
||||
|
||||
flags = flag;
|
||||
|
||||
coins.forEach(function(data) {
|
||||
var hash = data[0];
|
||||
var index = data[1];
|
||||
var script = bcoin.script.fromString(data[2]);
|
||||
var value = data[3];
|
||||
var coin;
|
||||
|
||||
coin = new bcoin.coin({
|
||||
version: 1,
|
||||
height: -1,
|
||||
coinbase: false,
|
||||
hash: util.revHex(hash),
|
||||
index: index,
|
||||
script: script,
|
||||
value: value != null ? parseInt(value, 10) : 0
|
||||
});
|
||||
|
||||
if (index !== -1)
|
||||
view.addCoin(coin);
|
||||
});
|
||||
|
||||
return {
|
||||
tx: tx,
|
||||
flags: flags,
|
||||
view: view,
|
||||
comments: tx.hasCoins(view)
|
||||
? util.inspectify(view.getOutput(tx.inputs[0]).script, false)
|
||||
: 'coinbase',
|
||||
data: data
|
||||
};
|
||||
}
|
||||
|
||||
[[valid, true], [invalid, false]].forEach(function(test) {
|
||||
// ["[[[prevout hash, prevout index, prevout scriptPubKey], [input 2], ...],"],
|
||||
var arr = test[0];
|
||||
var valid = test[1];
|
||||
var comment = '';
|
||||
@ -296,15 +301,13 @@ describe('TX', function() {
|
||||
sighash.forEach(function(data) {
|
||||
var tx, script, index, type, expected, hexType;
|
||||
|
||||
// ["raw_transaction, script, input_index, hashType, signature_hash (result)"],
|
||||
|
||||
if (data.length === 1)
|
||||
return;
|
||||
|
||||
tx = bcoin.tx.fromRaw(data[0], 'hex');
|
||||
tx = TX.fromRaw(data[0], 'hex');
|
||||
clearCache(tx, nocache);
|
||||
|
||||
script = bcoin.script.fromRaw(data[1], 'hex');
|
||||
script = Script.fromRaw(data[1], 'hex');
|
||||
|
||||
index = data[2];
|
||||
type = data[3];
|
||||
@ -327,33 +330,25 @@ describe('TX', function() {
|
||||
});
|
||||
});
|
||||
|
||||
function createInput(value) {
|
||||
function createInput(value, view) {
|
||||
var hash = crypto.randomBytes(32).toString('hex');
|
||||
var output = new Output();
|
||||
output.value = value;
|
||||
view.addOutput(hash, 0, output);
|
||||
return {
|
||||
prevout: {
|
||||
hash: hash,
|
||||
index: 0
|
||||
},
|
||||
coin: {
|
||||
version: 1,
|
||||
height: 0,
|
||||
value: value,
|
||||
script: [],
|
||||
coinbase: false,
|
||||
hash: hash,
|
||||
index: 0
|
||||
},
|
||||
script: [],
|
||||
witness: [],
|
||||
sequence: 0xffffffff
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
it('should fail on >51 bit coin values', function() {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [createInput(constants.MAX_MONEY + 1)],
|
||||
inputs: [createInput(constants.MAX_MONEY + 1, view)],
|
||||
outputs: [{
|
||||
script: [],
|
||||
value: constants.MAX_MONEY
|
||||
@ -361,14 +356,15 @@ describe('TX', function() {
|
||||
locktime: 0
|
||||
});
|
||||
assert.ok(tx.isSane());
|
||||
// assert.ok(!tx.checkInputs(view, 0));
|
||||
assert.ok(!tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
it('should handle 51 bit coin values', function() {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [createInput(constants.MAX_MONEY)],
|
||||
inputs: [createInput(constants.MAX_MONEY, view)],
|
||||
outputs: [{
|
||||
script: [],
|
||||
value: constants.MAX_MONEY
|
||||
@ -376,14 +372,15 @@ describe('TX', function() {
|
||||
locktime: 0
|
||||
});
|
||||
assert.ok(tx.isSane());
|
||||
// assert.ok(tx.checkInputs(view, 0));
|
||||
assert.ok(tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
it('should fail on >51 bit output values', function() {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [createInput(constants.MAX_MONEY)],
|
||||
inputs: [createInput(constants.MAX_MONEY, view)],
|
||||
outputs: [{
|
||||
script: [],
|
||||
value: constants.MAX_MONEY + 1
|
||||
@ -391,14 +388,15 @@ describe('TX', function() {
|
||||
locktime: 0
|
||||
});
|
||||
assert.ok(!tx.isSane());
|
||||
// assert.ok(!tx.checkInputs(view, 0));
|
||||
assert.ok(!tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
it('should handle 51 bit output values', function() {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [createInput(constants.MAX_MONEY)],
|
||||
inputs: [createInput(constants.MAX_MONEY, view)],
|
||||
outputs: [{
|
||||
script: [],
|
||||
value: constants.MAX_MONEY
|
||||
@ -406,14 +404,15 @@ describe('TX', function() {
|
||||
locktime: 0
|
||||
});
|
||||
assert.ok(tx.isSane());
|
||||
// assert.ok(tx.checkInputs(view, 0));
|
||||
assert.ok(tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
it('should fail on >51 bit fees', function() {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [createInput(constants.MAX_MONEY + 1)],
|
||||
inputs: [createInput(constants.MAX_MONEY + 1, view)],
|
||||
outputs: [{
|
||||
script: [],
|
||||
value: 0
|
||||
@ -421,17 +420,18 @@ describe('TX', function() {
|
||||
locktime: 0
|
||||
});
|
||||
assert.ok(tx.isSane());
|
||||
// assert.ok(!tx.checkInputs(view, 0));
|
||||
assert.ok(!tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
it('should fail on >51 bit values from multiple', function() {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2)),
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2)),
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2))
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2), view),
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2), view),
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2), view)
|
||||
],
|
||||
outputs: [{
|
||||
script: [],
|
||||
@ -440,14 +440,15 @@ describe('TX', function() {
|
||||
locktime: 0
|
||||
});
|
||||
assert.ok(tx.isSane());
|
||||
// assert.ok(!tx.checkInputs(view, 0));
|
||||
assert.ok(!tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
it('should fail on >51 bit output values from multiple', function() {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [createInput(constants.MAX_MONEY)],
|
||||
inputs: [createInput(constants.MAX_MONEY, view)],
|
||||
outputs: [
|
||||
{
|
||||
script: [],
|
||||
@ -465,17 +466,18 @@ describe('TX', function() {
|
||||
locktime: 0
|
||||
});
|
||||
assert.ok(!tx.isSane());
|
||||
// assert.ok(!tx.checkInputs(view, 0));
|
||||
assert.ok(!tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
it('should fail on >51 bit fees from multiple', function() {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2)),
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2)),
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2))
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2), view),
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2), view),
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2), view)
|
||||
],
|
||||
outputs: [{
|
||||
script: [],
|
||||
@ -484,18 +486,21 @@ describe('TX', function() {
|
||||
locktime: 0
|
||||
});
|
||||
assert.ok(tx.isSane());
|
||||
// assert.ok(!tx.checkInputs(view, 0));
|
||||
assert.ok(!tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
it('should fail on >51 bit fees from multiple txs', function() {
|
||||
var data = util.merge({}, bcoin.network.get().genesis, { height: 0 });
|
||||
var block = new bcoin.block(data);
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var genesis = Network.get().genesis;
|
||||
var block = new Block(genesis);
|
||||
var i, tx;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2))
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2), view)
|
||||
],
|
||||
outputs: [{
|
||||
script: [],
|
||||
@ -503,17 +508,22 @@ describe('TX', function() {
|
||||
}],
|
||||
locktime: 0
|
||||
});
|
||||
|
||||
block.txs.push(tx);
|
||||
}
|
||||
// assert.equal(block.getReward(), -1);
|
||||
|
||||
assert.equal(block.getReward(view, 0), -1);
|
||||
});
|
||||
|
||||
it('should fail to parse >53 bit values', function() {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var tx, raw;
|
||||
|
||||
tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2))
|
||||
createInput(Math.floor(constants.MAX_MONEY / 2), view)
|
||||
],
|
||||
outputs: [{
|
||||
script: [],
|
||||
@ -521,27 +531,30 @@ describe('TX', function() {
|
||||
}],
|
||||
locktime: 0
|
||||
});
|
||||
var raw = tx.toRaw();
|
||||
|
||||
raw = tx.toRaw();
|
||||
assert(encoding.readU64(raw, 47) === 0xdeadbeef);
|
||||
raw[54] = 0x7f;
|
||||
assert.throws(function() {
|
||||
bcoin.tx.fromRaw(raw);
|
||||
TX.fromRaw(raw);
|
||||
});
|
||||
tx._raw = null;
|
||||
tx.outputs[0].value = 0;
|
||||
var raw = tx.toRaw();
|
||||
|
||||
raw = tx.toRaw();
|
||||
assert(encoding.readU64(raw, 47) === 0x00);
|
||||
raw[54] = 0x80;
|
||||
assert.throws(function() {
|
||||
bcoin.tx.fromRaw(raw);
|
||||
TX.fromRaw(raw);
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail on 53 bit coin values', function() {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [createInput(util.MAX_SAFE_INTEGER)],
|
||||
inputs: [createInput(util.MAX_SAFE_INTEGER, view)],
|
||||
outputs: [{
|
||||
script: [],
|
||||
value: constants.MAX_MONEY
|
||||
@ -549,14 +562,15 @@ describe('TX', function() {
|
||||
locktime: 0
|
||||
});
|
||||
assert.ok(tx.isSane());
|
||||
// assert.ok(!tx.checkInputs(view, 0));
|
||||
assert.ok(!tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
it('should fail on 53 bit output values', function() {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [createInput(constants.MAX_MONEY)],
|
||||
inputs: [createInput(constants.MAX_MONEY, view)],
|
||||
outputs: [{
|
||||
script: [],
|
||||
value: util.MAX_SAFE_INTEGER
|
||||
@ -564,14 +578,15 @@ describe('TX', function() {
|
||||
locktime: 0
|
||||
});
|
||||
assert.ok(!tx.isSane());
|
||||
// assert.ok(!tx.checkInputs(view, 0));
|
||||
assert.ok(!tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
it('should fail on 53 bit fees', function() {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [createInput(util.MAX_SAFE_INTEGER)],
|
||||
inputs: [createInput(util.MAX_SAFE_INTEGER, view)],
|
||||
outputs: [{
|
||||
script: [],
|
||||
value: 0
|
||||
@ -579,18 +594,19 @@ describe('TX', function() {
|
||||
locktime: 0
|
||||
});
|
||||
assert.ok(tx.isSane());
|
||||
// assert.ok(!tx.checkInputs(view, 0));
|
||||
assert.ok(!tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
[util.MAX_SAFE_ADDITION, util.MAX_SAFE_INTEGER].forEach(function(MAX) {
|
||||
it('should fail on >53 bit values from multiple', function() {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [
|
||||
createInput(MAX),
|
||||
createInput(MAX),
|
||||
createInput(MAX)
|
||||
createInput(MAX, view),
|
||||
createInput(MAX, view),
|
||||
createInput(MAX, view)
|
||||
],
|
||||
outputs: [{
|
||||
script: [],
|
||||
@ -599,14 +615,15 @@ describe('TX', function() {
|
||||
locktime: 0
|
||||
});
|
||||
assert.ok(tx.isSane());
|
||||
// assert.ok(!tx.checkInputs(view, 0));
|
||||
assert.ok(!tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
it('should fail on >53 bit output values from multiple', function() {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [createInput(constants.MAX_MONEY)],
|
||||
inputs: [createInput(constants.MAX_MONEY, view)],
|
||||
outputs: [
|
||||
{
|
||||
script: [],
|
||||
@ -624,17 +641,18 @@ describe('TX', function() {
|
||||
locktime: 0
|
||||
});
|
||||
assert.ok(!tx.isSane());
|
||||
// assert.ok(!tx.checkInputs(view, 0));
|
||||
assert.ok(!tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
it('should fail on >53 bit fees from multiple', function() {
|
||||
var tx = bcoin.tx({
|
||||
var view = new CoinView();
|
||||
var tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [
|
||||
createInput(MAX),
|
||||
createInput(MAX),
|
||||
createInput(MAX)
|
||||
createInput(MAX, view),
|
||||
createInput(MAX, view),
|
||||
createInput(MAX, view)
|
||||
],
|
||||
outputs: [{
|
||||
script: [],
|
||||
@ -643,20 +661,21 @@ describe('TX', function() {
|
||||
locktime: 0
|
||||
});
|
||||
assert.ok(tx.isSane());
|
||||
// assert.ok(!tx.checkInputs(view, 0));
|
||||
assert.ok(!tx.checkInputs(view, 0));
|
||||
});
|
||||
|
||||
it('should fail on >53 bit fees from multiple txs', function() {
|
||||
var genesis = bcoin.network.get().genesis;
|
||||
var block = new bcoin.block(genesis);
|
||||
var view = new CoinView();
|
||||
var genesis = Network.get().genesis;
|
||||
var block = new Block(genesis);
|
||||
var i, tx;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
tx = bcoin.tx({
|
||||
tx = new TX({
|
||||
version: 1,
|
||||
flag: 1,
|
||||
inputs: [
|
||||
createInput(MAX)
|
||||
createInput(MAX, view)
|
||||
],
|
||||
outputs: [{
|
||||
script: [],
|
||||
@ -667,7 +686,7 @@ describe('TX', function() {
|
||||
block.txs.push(tx);
|
||||
}
|
||||
|
||||
// assert.equal(block.getReward(view), -1);
|
||||
assert.equal(block.getReward(view, 0), -1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user