Skip to content

Commit

Permalink
Mariner Release 2.0.55 (#929)
Browse files Browse the repository at this point in the history
- Prevent remote control to crash finalize command.
- Allow disabling auto-payouts (#935)
  • Loading branch information
kipliklotrika authored May 9, 2019
1 parent 65843d8 commit 07307bc
Show file tree
Hide file tree
Showing 9 changed files with 352 additions and 31 deletions.
5 changes: 5 additions & 0 deletions modules/RemoteControl.js
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,11 @@ class RemoteControl {
async (bid) => {
const holding = await this._findHoldingByBid(bid);

if (holding == null || holding.data_set_id == null) {
this.log.debug('Failed to get holding data for for bid %s.', bid);
return;
}

const dataInfo = await Models.data_info.findOne({
where: {
data_set_id: holding.data_set_id,
Expand Down
32 changes: 19 additions & 13 deletions modules/command/dh/dh-offer-finalized-command.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,26 @@ class DhOfferFinalizedCommand extends Command {
this.logger.important(`I've been chosen for offer ${offerId}.`);

await this.remoteControl.onCompletedBids();
const scheduledTime = (bid.holding_time_in_minutes * 60 * 1000) + (60 * 1000);
return {
commands: [
{
name: 'dhPayOutCommand',
delay: scheduledTime,
retries: 3,
transactional: false,
data: {
offerId,

if (this.config.disableAutoPayouts !== true) {
const scheduledTime =
(bid.holding_time_in_minutes * 60 * 1000) + (60 * 1000);
return {
commands: [
{
name: 'dhPayOutCommand',
delay: scheduledTime,
retries: 3,
transactional: false,
data: {
offerId,
viaAPI: false,
},
},
},
],
};
],
};
}
return Command.empty();
}

bid.status = 'NOT_CHOSEN';
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "origintrail_node",
"version": "2.0.54",
"version": "2.0.55",
"description": "OriginTrail node",
"main": ".eslintrc.js",
"config": {
Expand Down
53 changes: 52 additions & 1 deletion test/bdd/features/network.feature
Original file line number Diff line number Diff line change
Expand Up @@ -120,4 +120,55 @@ Feature: Test basic network features

@first
Scenario: Bootstraps should have /api/info route enabled
Then 1st bootstrap should reply on info route
Then 1st bootstrap should reply on info route

@first
Scenario: DH payout scenario
Given the replication difficulty is 0
And I setup 5 nodes
And I override configuration for all nodes
| dc_holding_time_in_minutes | 1 |
And I start the nodes
And I use 1st node as DC
And DC imports "importers/xml_examples/Retail/01_Green_to_pink_shipment.xml" as GS1
Given DC initiates the replication for last imported dataset
And I wait for replications to finish
And DC waits for holding time
Then selected DHes should be payed out

@first
Scenario: DH with disabled auto-payouts
Given the replication difficulty is 0
And I setup 5 nodes
And I override configuration for all nodes
| dc_holding_time_in_minutes | 1 |
| disableAutoPayouts | true |
And I start the nodes
And I use 1st node as DC
And DC imports "importers/xml_examples/Retail/01_Green_to_pink_shipment.xml" as GS1
Given DC initiates the replication for last imported dataset
And I wait for replications to finish
And DC waits for holding time
Then selected DHes should not be payed out

@first
Scenario: Node with diff management and operational wallet should successfully start
Given I setup 1 node
And I set 1st node's management wallet to be different then operational wallet
And I start the node
Then default initial token amount should be deposited on 1st node's profile

@third
Scenario: Test repeated offer creation with same dataset
Given the replication difficulty is 0
And I setup 3 nodes
And I start the nodes
And I use 1st node as DC
And DC imports "importers/xml_examples/Retail/01_Green_to_pink_shipment.xml" as GS1
Then DC's last import's hash should be the same as one manually calculated
Given DC initiates the replication for last imported dataset
And I wait for DC to fail to finalize last offer
Given I additionally setup 1 node
And I start additional nodes
Given DC initiates the replication for last imported dataset
And I wait for replications to finish
11 changes: 11 additions & 0 deletions test/bdd/steps/endpoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const {
Then, Given,
} = require('cucumber');
const { expect } = require('chai');
const BN = require('bn.js');

const httpApiHelper = require('./lib/http-api-helper');

Expand Down Expand Up @@ -188,3 +189,13 @@ Given(/^([DC|DH|DV]+) calls consensus endpoint for sender: "(\S+)"$/, async func
expect(consensusResponse, 'Should have key called events').to.have.all.keys('events');
this.state.lastConsensusResponse = consensusResponse;
});

Given(/^default initial token amount should be deposited on (\d+)[st|nd|rd|th]+ node's profile$/, async function (nodeIndex) {
expect(nodeIndex, 'Invalid index.').to.be.within(0, this.state.nodes.length);

const balance = await httpApiHelper.apiBalance(this.state.nodes[nodeIndex - 1].state.node_rpc_url, false);
const staked = new BN(balance.profile.staked);
const initialDepositAmount = new BN(this.state.nodes[nodeIndex - 1].options.nodeConfiguration.initial_deposit_amount);

expect(staked.toString()).to.be.equal(initialDepositAmount.toString());
});
40 changes: 40 additions & 0 deletions test/bdd/steps/lib/http-api-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,45 @@ async function apiNodeInfo(nodeRpcUrl) {
});
}

/**
* @typedef {Object} ApiBalanceInfo
* @property {Object} profile info about profile balance
* @property {string} profile.minimalStake minimal stake
* @property {string} profile.reserved reserved
* @property {string} profile.staked staked
* @property {Object} wallet info about wallet balance
* @property {string} wallet.address node's wallet address
* @property {string} wallet.ethBalance wallet balance in ethers
* @property {string} wallet.tokenBalance wallet balance in tokens
*/

/**
* Fetch api/balance?humanReadable={{humanReadable}}
*
* @param {string} nodeRpcUrl URL in following format http://host:port
* @param {boolean} humanReadable friendly format of response
* @return {Promise.<ApiBalanceInfo>}
*/
async function apiBalance(nodeRpcUrl, humanReadable) {
return new Promise((accept, reject) => {
request(
{
method: 'GET',
headers: { 'Content-Type': 'application/json' },
uri: `${nodeRpcUrl}/api/balance?humanReadable=${humanReadable}`,
json: true,
},
(err, res, body) => {
if (err) {
reject(err);
return;
}
accept(body);
},
);
});
}

module.exports = {
apiImport,
apiImportContent,
Expand All @@ -477,4 +516,5 @@ module.exports = {
apiConsensus,
apiTrail,
apiNodeInfo,
apiBalance,
};
71 changes: 70 additions & 1 deletion test/bdd/steps/lib/otnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const defaultConfiguration = require('../../../../config/config.json').developme
const uuidRegex = /\b[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\b/gi;
const walletRegex = /\b0x[0-9A-F]{40}\b/gi;
const identityRegex = /\b[0-9A-F]{40}\b/gi;
const identityWithPrefixRegex = /\b0x[0-9A-F]{40}\b/gi;
const offerIdRegex = /\b0x[0-9A-F]{64}\b/gi;
const dataSetRegex = /\b0x[0-9A-F]{64}\b/gi;
const walletAmountRegex = /\b\d+\b/g;
Expand All @@ -34,7 +35,8 @@ class OtNode extends EventEmitter {
this.options.configDir = path.join(appDataBaseDir || tmpdir, this.id);
this.options.nodeConfiguration = nodeConfiguration || {};
this.options.nodeConfiguration = deepExtend(
Object.assign({}, defaultConfiguration), // deepExtend changes original object.
{},
defaultConfiguration,
this.options.nodeConfiguration,
);
this.logger = logger || console;
Expand Down Expand Up @@ -62,9 +64,13 @@ class OtNode extends EventEmitter {
this.state = {};
this.state.addedBids = []; // List of offer IDs (DH side).
this.state.takenBids = []; // List of offer IDs (DH side).
this.state.pendingLitigationDhIdentities = []; // List of pending litigations (DHs)
this.state.takenReplacements = []; // List of replacement offer IDs (DH side).
// Valid replications (DH side). List of internal offer IDs and their replications DH IDs
// in pairs. { internalOfferId, dhId }.
this.state.replications = [];
// Array of replacement offer IDs
this.state.replacements = [];
// Valid replications (DC side). List of objects { importId, dhWallet }.
this.state.holdingData = [];
// Offers finalized. List of offer IDs.
Expand Down Expand Up @@ -149,6 +155,7 @@ class OtNode extends EventEmitter {
stop() {
this.logger.log(`Stopping node ${this.id}.`);
assert(this.isRunning);
this.started = false;
this.process.kill('SIGINT');
}

Expand Down Expand Up @@ -211,6 +218,8 @@ class OtNode extends EventEmitter {
const offerId = line.match(offerIdRegex)[0];
} else if (line.match(/Miner found a solution of offer .+\./gi)) {
const offerId = line.match(offerIdRegex)[0];
} else if (line.match(/Not enough DHs submitted/gi)) {
this.emit('not-enough-dhs');
} else if (line.match(/Offer .+ finalized/gi)) {
const offerId = line.match(offerIdRegex)[0];
assert(offerId);
Expand Down Expand Up @@ -290,6 +299,51 @@ class OtNode extends EventEmitter {
this.emit('replication-window-closed');
} else if (line.match(/Offer with internal ID .+ for data set .+ written to blockchain. Waiting for DHs\.\.\./gi)) {
this.emit('offer-written-blockchain');
} else if (line.match(/Command dhPayOutCommand and ID .+ processed\./gi)) {
this.emit('dh-pay-out-finalized');
} else if (line.match(/Command dhOfferFinalizedCommand and ID .+ processed\./gi)) {
this.emit('dh-offer-finalized');
} else if (line.match(/Litigation initiated for DH .+ and offer .+\./gi)) {
this.state.litigationStatus = 'LITIGATION_STARTED';
this.emit('dc-litigation-initiated');
} else if (line.match(/Litigation answered for DH .+ and offer .+\./gi)) {
this.emit('dc-litigation-answered');
} else if (line.match(/Litigation answered for offer .+\. DH identity .+/gi)) {
this.emit('dh-litigation-answered');
} else if (line.match(/Litigation completed for DH .+ and offer .+\./gi)) {
this.state.litigationStatus = 'LITIGATION_COMPLETED';
this.emit('dc-litigation-completed');
} else if (line.match(/Litigation already in progress\.\.\. It needs to be completed in order to litigate .+ for offer .+/gi)) {
const dhIdentity = line.match(identityWithPrefixRegex)[0];
this.state.pendingLitigationDhIdentities.push(dhIdentity);
this.emit('dc-litigation-pending');
} else if (line.match(/DH .+ was penalized for the offer .+\./gi)) {
this.emit('dc-litigation-completed-dh-penalized');
} else if (line.match(/DH .+ was not penalized for the offer .+\./gi)) {
this.emit('dc-litigation-completed-dh-not-penalized');
} else if (line.match(/Replacement for DH .+ and offer .+ has been successfully started. Waiting for DHs\.\.\./gi)) {
this.emit('dc-litigation-replacement-started');
} else if (line.match(/Replacement triggered for offer .+\. Litigator .+\./gi)) {
const offerId = line.match(offerIdRegex)[0];
this.state.replacements.push(offerId);
} else if (line.match(/\[DH] Replacement replication finished for offer ID .+/gi)) {
const offerId = line.match(offerIdRegex)[0];
assert(offerId);
this.state.takenReplacements.push(offerId);
this.emit('dh-litigation-replacement-received');
} else if (line.match(/Successfully replaced DH .+ with DH .+ for offer .+/gi)) {
this.emit('dc-litigation-replacement-completed');
} else if (line.match(/Challenge answer .+ sent to .+\./gi)) {
this.emit('dh-challenge-sent');
} else if (line.match(/Not chosen as a replacement for offer .+\./gi)) {
this.emit('dh-not-chosen-as-replacement');
} else if (line.match(/Chosen as a replacement for offer .+\./gi)) {
const offerId = line.match(offerIdRegex)[0];
this.state.takenBids.push(offerId);
this.emit('dh-chosen-as-replacement');
} else if (line.match(/Replication finished for DH node .+/gi)) {
const nodeId = line.match(identityRegex)[0];
this.emit('dh-replication-verified', nodeId);
}
}

Expand All @@ -301,6 +355,21 @@ class OtNode extends EventEmitter {
return this.initialized && this.started && !!this.process;
}

/**
* Retruns path to the system.db.
* @return {string} Path.
*/
get systemDbPath() {
return path.join(this.options.configDir, 'system.db');
}

get erc725Identity() {
return JSON.parse(fs.readFileSync(path.join(
this.options.configDir,
'erc725_identity.json',
))).identity;
}

/**
* Returns array of node IDs of nodes that confirmed possession of the data for
* the given query,
Expand Down
Loading

0 comments on commit 07307bc

Please sign in to comment.