Skip to content

Commit

Permalink
Fix #657 Could not chain transactions and send them immediately
Browse files Browse the repository at this point in the history
  • Loading branch information
c-geek committed Oct 15, 2016
1 parent 8e817a0 commit 31127e3
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 19 deletions.
10 changes: 8 additions & 2 deletions app/lib/computation/blockGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,16 @@ function BlockGenerator(mainContext, prover) {
yield rules.HELPERS.checkTxBlockStamp(extractedTX, dal);
transactions.push(tx);
passingTxs.push(extractedTX);
logger.info('Transaction added to block');
logger.info('Transaction %s added to block', tx.hash);
} catch (err) {
logger.error(err);
yield dal.removeTxByHash(extractedTX.hash);
const currentNumber = (current && current.number) || 0;
const blockstamp = extractedTX.blockstamp || (currentNumber + '-');
const txBlockNumber = parseInt(blockstamp.split('-')[0]);
// 10 blocks before removing the transaction
if (currentNumber - txBlockNumber + 1 >= constants.TRANSACTION_MAX_TRIES) {
yield dal.removeTxByHash(extractedTX.hash);
}
}
}
return transactions;
Expand Down
4 changes: 3 additions & 1 deletion app/lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,9 @@ module.exports = {
POW_SECURITY_RETRY_DELAY: 10 * 60 * 1000,

POW_DIFFICULTY_RANGE_RATIO_V3: Math.sqrt(1.066),
POW_DIFFICULTY_RANGE_RATIO_V4: 1.189
POW_DIFFICULTY_RANGE_RATIO_V4: 1.189,

TRANSACTION_MAX_TRIES: 10
};

function exact (regexpContent) {
Expand Down
2 changes: 1 addition & 1 deletion app/lib/dal/fileDAL.js
Original file line number Diff line number Diff line change
Expand Up @@ -639,8 +639,8 @@ function FileDAL(params) {
if (tx.version == 3) {
const sp = tx.blockstamp.split('-');
tx.blockstampTime = (yield that.getBlockByNumberAndHash(sp[0], sp[1])).medianTime;
return that.txsDAL.addLinked(new Transaction(tx));
}
return that.txsDAL.addLinked(new Transaction(tx));
})));
};

Expand Down
18 changes: 11 additions & 7 deletions app/lib/entity/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,7 @@ let Transaction = function(obj, currency) {
// Outputs
tx.outputs = [];
this.outputs.forEach(function (output) {
const sp = output.split(':');
tx.outputs.push({
amount: parseInt(sp[0]),
base: parseInt(sp[1]),
conditions: sp[2],
raw: output
});
tx.outputs.push(Transaction.statics.outputStr2Obj(output));
});
tx.comment = this.comment;
tx.blockstamp = this.blockstamp;
Expand Down Expand Up @@ -110,6 +104,16 @@ Transaction.statics.outputs2recipients = (tx) => tx.outputs.map(function(out) {
return (recipent && recipent[1]) || 'UNKNOWN';
});

Transaction.statics.outputStr2Obj = (outputStr) => {
const sp = outputStr.split(':');
return {
amount: parseInt(sp[0]),
base: parseInt(sp[1]),
conditions: sp[2],
raw: outputStr
};
};

Transaction.statics.setRecipients = (txs) => {
// Each transaction must have a good "recipients" field for future searchs
txs.forEach((tx) => tx.recipients = Transaction.statics.outputs2recipients(tx));
Expand Down
24 changes: 21 additions & 3 deletions app/lib/rules/global_rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const rawer = require('../ucp/rawer');
const Identity = require('../entity/identity');
const Membership = require('../entity/membership');
const Certification = require('../entity/certification');
const Transaction = require('../entity/transaction');
const logger = require('../logger')('globr');
const unlock = require('../ucp/txunlock');
const local_rules = require('./local_rules');
Expand Down Expand Up @@ -464,7 +465,7 @@ rules.FUNCTIONS = {
return true;
}),

checkSourcesAvailability: (block, conf, dal) => co(function *() {
checkSourcesAvailability: (block, conf, dal, alsoCheckPendingTransactions) => co(function *() {
let txs = block.getTransactions();
const current = yield dal.getCurrentBlockOrNull();
for (const tx of txs) {
Expand All @@ -480,6 +481,23 @@ rules.FUNCTIONS = {
let src = tx.inputs[k];
let dbSrc = yield dal.getSource(src.identifier, src.noffset);
logger.debug('Source %s:%s = %s', src.identifier, src.noffset, dbSrc && dbSrc.consumed);
if (!dbSrc && alsoCheckPendingTransactions) {
// For chained transactions which are checked on sandbox submission, we accept them if there is already
// a previous transaction of the chain already recorded in the pool
dbSrc = yield co(function*() {
let hypotheticSrc = null;
let targetTX = yield dal.getTxByHash(src.identifier);
if (targetTX) {
let outputStr = targetTX.outputs[src.noffset];
if (outputStr) {
hypotheticSrc = Transaction.statics.outputStr2Obj(outputStr);
hypotheticSrc.consumed = false;
hypotheticSrc.time = 0;
}
}
return hypotheticSrc;
});
}
if (!dbSrc || dbSrc.consumed) {
logger.warn('Source ' + [src.type, src.identifier, src.noffset].join(':') + ' is not available');
throw constants.ERRORS.SOURCE_ALREADY_CONSUMED;
Expand Down Expand Up @@ -575,10 +593,10 @@ rules.HELPERS = {

checkExistsPubkey: (pub, dal) => dal.getWrittenIdtyByPubkey(pub),

checkSingleTransaction: (tx, block, conf, dal) => rules.FUNCTIONS.checkSourcesAvailability({
checkSingleTransaction: (tx, block, conf, dal, alsoCheckPendingTransactions) => rules.FUNCTIONS.checkSourcesAvailability({
getTransactions: () => [tx],
medianTime: block.medianTime
}, conf, dal),
}, conf, dal, alsoCheckPendingTransactions),

getNextUD: (dal, conf, version, nextMedianTime, current, nextN) => co(function *() {
const lastUDBlock = yield dal.lastUDBlock();
Expand Down
4 changes: 3 additions & 1 deletion app/service/TransactionsService.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ function TransactionService () {

AbstractService.call(this);

const CHECK_PENDING_TRANSACTIONS = true;

let conf, dal;

this.setConfDAL = (newConf, newDAL) => {
Expand All @@ -34,7 +36,7 @@ function TransactionService () {
const nextBlockWithFakeTimeVariation = { medianTime: current.medianTime + 1 };
yield Q.nbind(rules.HELPERS.checkSingleTransactionLocally, rules.HELPERS)(transaction);
yield rules.HELPERS.checkTxBlockStamp(transaction, dal);
yield rules.HELPERS.checkSingleTransaction(transaction, nextBlockWithFakeTimeVariation, conf, dal);
yield rules.HELPERS.checkSingleTransaction(transaction, nextBlockWithFakeTimeVariation, conf, dal, CHECK_PENDING_TRANSACTIONS);
const server_pubkey = conf.pair && conf.pair.pub;
transaction.pubkey = transaction.issuers.indexOf(server_pubkey) !== -1 ? server_pubkey : '';
if (!(yield dal.txsDAL.sandbox.acceptNewSandBoxEntry(transaction, server_pubkey))) {
Expand Down
9 changes: 7 additions & 2 deletions test/integration/server-sandbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ const should = require('should');
const bma = require('../../app/lib/streams/bma');
const user = require('./tools/user');
const commit = require('./tools/commit');
const until = require('./tools/until');
const toolbox = require('./tools/toolbox');
const multicaster = require('../../app/lib/streams/multicaster');
const limiter = require('../../app/lib/system/limiter');
const constants = require('../../app/lib/constants');
const limiter = require('../../app/lib/system/limiter');

limiter.noLimit();

Expand Down Expand Up @@ -230,9 +230,13 @@ describe("Sandboxes", function() {

describe('Transaction', () => {

const tmp = constants.TRANSACTION_MAX_TRIES;
constants.TRANSACTION_MAX_TRIES = 2;

it('should accept 2 transactions of 20, 30 units', () => co(function *() {
yield i4.send(20, i1);
yield i4.send(30, i1);
(yield s1.dal.txsDAL.getSandboxRoom()).should.equal(0);
}));

it('should reject amount of 10', () => shouldThrow(co(function *() {
Expand All @@ -247,6 +251,7 @@ describe("Sandboxes", function() {
yield s1.commit();
yield s1.commit();
(yield s1.dal.txsDAL.getSandboxRoom()).should.equal(2);
constants.TRANSACTION_MAX_TRIES = tmp;
}));
});
});
Expand Down
8 changes: 6 additions & 2 deletions test/integration/tools/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,13 @@ function User (uid, options, node) {
this.prepareUTX = (previousTX, unlocks, outputs, opts) => co(function *() {
let obj = parsers.parseTransaction.syncWrite(previousTX);
// Unlocks inputs with given "unlocks" strings
let inputs = obj.outputs.map((out, index) => {
let outputsToConsume = obj.outputs;
if (opts.theseOutputsStart !== undefined) {
outputsToConsume = outputsToConsume.slice(opts.theseOutputsStart);
}
let inputs = outputsToConsume.map((out, index) => {
return {
src: ['T', obj.hash, index].join(':'),
src: ['T', obj.hash, (opts.theseOutputsStart || 0) + index].join(':'),
unlock: unlocks[index]
};
});
Expand Down
83 changes: 83 additions & 0 deletions test/integration/transactions-chaining.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"use strict";

const co = require('co');
const _ = require('underscore');
const should = require('should');
const assert = require('assert');
const constants = require('../../app/lib/constants');
const bma = require('../../app/lib/streams/bma');
const toolbox = require('./tools/toolbox');
const node = require('./tools/node');
const user = require('./tools/user');
const unit = require('./tools/unit');
const http = require('./tools/http');
const limiter = require('../../app/lib/system/limiter');

limiter.noLimit();

describe("Transaction chaining", function() {

const now = 1476549382;

const s1 = toolbox.server({
pair: {
pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV',
sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'
},
dt: 3600,
ud0: 120,
c: 0.1
});

const tic = user('tic', { pub: 'DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV', sec: '468Q1XtTq7h84NorZdWBZFJrGkB18CbmbHr9tkp9snt5GiERP7ySs3wM8myLccbAAGejgMRC9rqnXuW3iAfZACm7'}, { server: s1 });
const toc = user('toc', { pub: 'DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo', sec: '64EYRvdPpTfLGGmaX5nijLXRqWXaVz8r1Z1GtaahXwVSJGQRn7tqkxLb288zwSYzELMEG5ZhXSBYSxsTsz1m9y8F'}, { server: s1 });

before(() => co(function*() {

yield s1.initWithDAL().then(bma).then((bmapi) => bmapi.openConnections());
yield tic.createIdentity();
yield toc.createIdentity();
yield tic.cert(toc);
yield toc.cert(tic);
yield tic.join();
yield toc.join();
yield s1.commit({ version: 2, time: now });
yield s1.commit({ version: 2, time: now + 7210 });
yield s1.commit({ version: 2, time: now + 7210 });
}));

describe("Sources", function(){

it('it should exist block#2 with UD of 120', () => s1.expect('/blockchain/block/2', (block) => {
should.exists(block);
assert.equal(block.number, 2);
assert.equal(block.dividend, 120);
}));
});

describe("Chaining", function(){

it('with SIG and XHX', () => co(function *() {
// Current state
(yield s1.get('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')).should.have.property('sources').length(1);
let tx1 = yield toc.prepareITX(104, tic); // Rest = 120 - 104 = 16
let tx2 = yield toc.prepareUTX(tx1, ['SIG(0)'], [{ qty: 16, base: 0, lock: 'SIG(' + tic.pub + ')' }], {
comment: 'also take the remaining 16 units',
theseOutputsStart: 1
});
const tmp = constants.TRANSACTION_MAX_TRIES = 2;
constants.TRANSACTION_MAX_TRIES = 2;
yield unit.shouldNotFail(toc.sendTX(tx1));
yield unit.shouldNotFail(toc.sendTX(tx2));
(yield s1.get('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')).should.have.property('sources').length(1);
(yield s1.get('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV')).should.have.property('sources').length(1);
yield s1.commit({ version: 2 }); // TX1 commited
(yield s1.get('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')).should.have.property('sources').length(1); // The 16 remaining units
(yield s1.get('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV')).should.have.property('sources').length(2); // The UD + 104 units sent by toc
yield s1.commit({ version: 2 }); // TX2 commited
(yield s1.get('/tx/sources/DKpQPUL4ckzXYdnDRvCRKAm1gNvSdmAXnTrJZ7LvM5Qo')).should.have.property('sources').length(0);
(yield s1.get('/tx/sources/DNann1Lh55eZMEDXeYt59bzHbA3NJR46DeQYCS2qQdLV')).should.have.property('sources').length(3); // The UD + 104 + 16 units sent by toc
constants.TRANSACTION_MAX_TRIES = tmp;
}));
});
});

0 comments on commit 31127e3

Please sign in to comment.