moving to mongodb storage
This commit is contained in:
parent
43d54bbff0
commit
db1e554d37
|
@ -1,3 +1,4 @@
|
||||||
|
services: mongodb
|
||||||
language: node_js
|
language: node_js
|
||||||
node_js:
|
node_js:
|
||||||
- "10.5"
|
- "10.5"
|
||||||
|
|
10
config.json
10
config.json
|
@ -1,6 +1,8 @@
|
||||||
{
|
{
|
||||||
"chainId": "00000000000000000002",
|
"chainId": "mainnet1",
|
||||||
"rpcNodePort": 5000,
|
"rpcNodePort": 5000,
|
||||||
|
"databaseURL": "mongodb://localhost:27017",
|
||||||
|
"databaseName": "ssc",
|
||||||
"dataDirectory": "./data/",
|
"dataDirectory": "./data/",
|
||||||
"databaseFileName": "database.db",
|
"databaseFileName": "database.db",
|
||||||
"blocksLogFilePath": "./blocks.log",
|
"blocksLogFilePath": "./blocks.log",
|
||||||
|
@ -12,6 +14,6 @@
|
||||||
"https://rpc.steemviz.com",
|
"https://rpc.steemviz.com",
|
||||||
"https://steemd.minnowsupportproject.org"
|
"https://steemd.minnowsupportproject.org"
|
||||||
],
|
],
|
||||||
"startSteemBlock": 29056257,
|
"startSteemBlock": 29864752,
|
||||||
"genesisSteemBlock": 29056257
|
"genesisSteemBlock": 29862600
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,6 @@ class Block {
|
||||||
|
|
||||||
for (let i = 0; i < nbTransactions; i += 1) {
|
for (let i = 0; i < nbTransactions; i += 1) {
|
||||||
const transaction = this.transactions[i];
|
const transaction = this.transactions[i];
|
||||||
|
|
||||||
await this.processTransaction(ipc, jsVMTimeout, transaction, currentDatabaseHash); // eslint-disable-line
|
await this.processTransaction(ipc, jsVMTimeout, transaction, currentDatabaseHash); // eslint-disable-line
|
||||||
|
|
||||||
currentDatabaseHash = transaction.databaseHash;
|
currentDatabaseHash = transaction.databaseHash;
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
const CONSTANTS = {
|
const CONSTANTS = {
|
||||||
|
|
||||||
// mainnet
|
// mainnet
|
||||||
/*
|
|
||||||
UTILITY_TOKEN_SYMBOL: 'ENG',
|
UTILITY_TOKEN_SYMBOL: 'ENG',
|
||||||
STEEM_PEGGED_ACCOUNT: 'steem-peg',
|
STEEM_PEGGED_ACCOUNT: 'steem-peg',
|
||||||
INITIAL_TOKEN_CREATION_FEE: '100',
|
INITIAL_TOKEN_CREATION_FEE: '100',
|
||||||
SSC_STORE_QTY: '0.001',
|
SSC_STORE_QTY: '0.001',
|
||||||
*/
|
|
||||||
|
|
||||||
// testnet
|
// testnet
|
||||||
|
/*
|
||||||
UTILITY_TOKEN_SYMBOL: 'SSC',
|
UTILITY_TOKEN_SYMBOL: 'SSC',
|
||||||
STEEM_PEGGED_ACCOUNT: 'steemsc',
|
STEEM_PEGGED_ACCOUNT: 'steemsc',
|
||||||
INITIAL_TOKEN_CREATION_FEE: '0',
|
INITIAL_TOKEN_CREATION_FEE: '0',
|
||||||
SSC_STORE_QTY: '1',
|
SSC_STORE_QTY: '1',
|
||||||
|
*/
|
||||||
UTILITY_TOKEN_PRECISION: 8,
|
UTILITY_TOKEN_PRECISION: 8,
|
||||||
STEEM_PEGGED_SYMBOL: 'STEEMP',
|
STEEM_PEGGED_SYMBOL: 'STEEMP',
|
||||||
|
|
||||||
|
|
|
@ -175,7 +175,7 @@ class SmartContracts {
|
||||||
}
|
}
|
||||||
|
|
||||||
const newContract = {
|
const newContract = {
|
||||||
name,
|
_id: name,
|
||||||
owner: finalSender,
|
owner: finalSender,
|
||||||
code: codeTemplate,
|
code: codeTemplate,
|
||||||
codeHash: SHA256(codeTemplate).toString(enchex),
|
codeHash: SHA256(codeTemplate).toString(enchex),
|
||||||
|
|
|
@ -261,6 +261,11 @@
|
||||||
"base-x": "^3.0.2"
|
"base-x": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"bson": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/bson/-/bson-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-jCGVYLoYMHDkOsbwJZBCqwMHyH4c+wzgI9hG7Z6SZJRXWr+x58pdIbm2i9a/jFGCkRJqRUr8eoI7lDWa0hTkxg=="
|
||||||
|
},
|
||||||
"buffer-xor": {
|
"buffer-xor": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
|
||||||
|
@ -1604,6 +1609,12 @@
|
||||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||||
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
||||||
},
|
},
|
||||||
|
"memory-pager": {
|
||||||
|
"version": "1.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
|
||||||
|
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"merge-descriptors": {
|
"merge-descriptors": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||||
|
@ -1717,6 +1728,26 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mongodb": {
|
||||||
|
"version": "3.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.2.6.tgz",
|
||||||
|
"integrity": "sha512-qnHc4tjEkHKemuzBq9R7ycYnhFE0Dlpt6+n6suoZp2DcDdqviQ+teloJU24fsOw/PLmr75yGk4mRx/YabjDQEQ==",
|
||||||
|
"requires": {
|
||||||
|
"mongodb-core": "3.2.6",
|
||||||
|
"safe-buffer": "^5.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mongodb-core": {
|
||||||
|
"version": "3.2.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-3.2.6.tgz",
|
||||||
|
"integrity": "sha512-i+XRVjur9D0ywGF7cFebOUnALnbvMHajdNhhl3TQuopW6QDE655G8CpPeERbqSqfa3rOKEUo08lENDIiBIuAvQ==",
|
||||||
|
"requires": {
|
||||||
|
"bson": "^1.1.1",
|
||||||
|
"require_optional": "^1.0.1",
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"saslprep": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
@ -2133,6 +2164,22 @@
|
||||||
"integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
|
"integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"require_optional": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
|
||||||
|
"requires": {
|
||||||
|
"resolve-from": "^2.0.0",
|
||||||
|
"semver": "^5.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"resolve-from": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"resolve": {
|
"resolve": {
|
||||||
"version": "1.8.1",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz",
|
||||||
|
@ -2220,6 +2267,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
},
|
},
|
||||||
|
"saslprep": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"sparse-bitfield": "^3.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"secp256k1": {
|
"secp256k1": {
|
||||||
"version": "3.5.2",
|
"version": "3.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.5.2.tgz",
|
||||||
|
@ -2243,8 +2299,7 @@
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "5.6.0",
|
"version": "5.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
|
||||||
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
|
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"send": {
|
"send": {
|
||||||
"version": "0.16.2",
|
"version": "0.16.2",
|
||||||
|
@ -2345,6 +2400,15 @@
|
||||||
"is-fullwidth-code-point": "^2.0.0"
|
"is-fullwidth-code-point": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"sparse-bitfield": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
|
||||||
|
"integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"memory-pager": "^1.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"spdx-correct": {
|
"spdx-correct": {
|
||||||
"version": "3.0.2",
|
"version": "3.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz",
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
"js-base64": "^2.5.1",
|
"js-base64": "^2.5.1",
|
||||||
"line-by-line": "^0.1.6",
|
"line-by-line": "^0.1.6",
|
||||||
"lokijs": "^1.5.6",
|
"lokijs": "^1.5.6",
|
||||||
|
"mongodb": "^3.2.6",
|
||||||
"node-cleanup": "^2.1.2",
|
"node-cleanup": "^2.1.2",
|
||||||
"read-last-lines": "^1.6.0",
|
"read-last-lines": "^1.6.0",
|
||||||
"seedrandom": "^3.0.1",
|
"seedrandom": "^3.0.1",
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
const fs = require('fs-extra');
|
/* eslint-disable no-await-in-loop */
|
||||||
const Loki = require('lokijs');
|
|
||||||
const SHA256 = require('crypto-js/sha256');
|
const SHA256 = require('crypto-js/sha256');
|
||||||
const enchex = require('crypto-js/enc-hex');
|
const enchex = require('crypto-js/enc-hex');
|
||||||
const validator = require('validator');
|
const validator = require('validator');
|
||||||
const lfsa = require('../libs/loki-fs-structured-adapter');
|
const { MongoClient } = require('mongodb');
|
||||||
const { IPC } = require('../libs/IPC');
|
const { IPC } = require('../libs/IPC');
|
||||||
|
|
||||||
const BC_PLUGIN_NAME = require('./Blockchain.constants').PLUGIN_NAME;
|
const BC_PLUGIN_NAME = require('./Blockchain.constants').PLUGIN_NAME;
|
||||||
|
@ -22,61 +21,75 @@ let chain = null;
|
||||||
let saving = false;
|
let saving = false;
|
||||||
let databaseHash = '';
|
let databaseHash = '';
|
||||||
|
|
||||||
|
const initSequence = async (name, startID = 1) => {
|
||||||
|
const sequences = database.collection('sequences');
|
||||||
|
|
||||||
|
await sequences.insertOne({ _id: name, seq: startID });
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNextSequence = async (name) => {
|
||||||
|
const sequences = database.collection('sequences');
|
||||||
|
|
||||||
|
const sequence = await sequences.findOneAndUpdate(
|
||||||
|
{ _id: name }, { $inc: { seq: 1 } }, { new: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
return sequence.value.seq;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getLastSequence = async (name) => {
|
||||||
|
const sequences = database.collection('sequences');
|
||||||
|
|
||||||
|
const sequence = await sequences.findOne({ _id: name });
|
||||||
|
|
||||||
|
return sequence.seq;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCollection = async name => new Promise((resolve) => {
|
||||||
|
database.collection(name, { strict: true }, (err, collection) => {
|
||||||
|
// collection does not exist
|
||||||
|
if (err) {
|
||||||
|
resolve(null);
|
||||||
|
}
|
||||||
|
resolve(collection);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// load the database from the filesystem
|
// load the database from the filesystem
|
||||||
async function init(conf, callback) {
|
const init = async (conf, callback) => {
|
||||||
const {
|
const {
|
||||||
autosaveInterval,
|
databaseURL,
|
||||||
databaseFileName,
|
databaseName,
|
||||||
dataDirectory,
|
|
||||||
} = conf;
|
} = conf;
|
||||||
|
|
||||||
const databaseFilePath = dataDirectory + databaseFileName;
|
|
||||||
|
|
||||||
// init the database
|
// init the database
|
||||||
database = new Loki(databaseFilePath, {
|
const client = await MongoClient.connect(databaseURL, { useNewUrlParser: true });
|
||||||
adapter: new lfsa(), // eslint-disable-line new-cap
|
database = await client.db(databaseName);
|
||||||
autosave: autosaveInterval > 0,
|
// await database.dropDatabase();
|
||||||
autosaveInterval,
|
// return
|
||||||
});
|
// get the chain collection and init the chain if not done yet
|
||||||
|
|
||||||
// check if the app has already be run
|
const coll = await getCollection('chain');
|
||||||
if (fs.pathExistsSync(databaseFilePath)) {
|
|
||||||
// load the database from the filesystem to the RAM
|
|
||||||
database.loadDatabase({}, (errorDb) => {
|
|
||||||
if (errorDb) {
|
|
||||||
callback(errorDb);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the chain or the contracts collection doesn't exist we return an error
|
if (coll === null) {
|
||||||
chain = database.getCollection('chain');
|
await initSequence('chain', 0);
|
||||||
const contracts = database.getCollection('contracts');
|
chain = await database.createCollection('chain');
|
||||||
if (chain === null || contracts === null) {
|
|
||||||
callback('The database is missing either the chain or the contracts table');
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null);
|
await database.createCollection('transactions');
|
||||||
});
|
await database.createCollection('contracts');
|
||||||
} else {
|
} else {
|
||||||
// create the data directory if necessary and empty it if files exists
|
chain = coll;
|
||||||
fs.emptyDirSync(dataDirectory);
|
|
||||||
|
|
||||||
// init the main tables
|
|
||||||
chain = database.addCollection('chain', { indices: ['blockNumber'], disableMeta: true });
|
|
||||||
database.addCollection('transactions', { unique: ['txid'], disableMeta: true });
|
|
||||||
database.addCollection('contracts', { indices: ['name'], disableMeta: true });
|
|
||||||
|
|
||||||
callback(null);
|
|
||||||
}
|
}
|
||||||
}
|
callback(null);
|
||||||
|
};
|
||||||
|
|
||||||
async function generateGenesisBlock(conf, callback) {
|
const generateGenesisBlock = async (conf, callback) => {
|
||||||
const {
|
const {
|
||||||
chainId,
|
chainId,
|
||||||
genesisSteemBlock,
|
genesisSteemBlock,
|
||||||
} = conf;
|
} = conf;
|
||||||
|
|
||||||
// check if genesis block hasn't been generated already
|
// check if genesis block hasn't been generated already
|
||||||
const genBlock = actions.getBlockInfo(0);
|
let genBlock = await actions.getBlockInfo(0);
|
||||||
|
|
||||||
if (!genBlock) {
|
if (!genBlock) {
|
||||||
// insert the genesis block
|
// insert the genesis block
|
||||||
|
@ -90,55 +103,49 @@ async function generateGenesisBlock(conf, callback) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
chain.insert(res.payload);
|
genBlock = res.payload;
|
||||||
|
|
||||||
// initialize the block production tools
|
genBlock._id = await getNextSequence('chain'); // eslint-disable-line no-underscore-dangle
|
||||||
// BlockProduction.initialize(database, genesisSteemBlock);
|
|
||||||
|
await chain.insertOne(genBlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
}
|
};
|
||||||
|
|
||||||
// save the blockchain as well as the database on the filesystem
|
// save the blockchain as well as the database on the filesystem
|
||||||
actions.save = (callback) => {
|
actions.save = (callback) => {
|
||||||
saving = true;
|
saving = true;
|
||||||
|
|
||||||
// save the database from the RAM to the filesystem
|
saving = false;
|
||||||
database.saveDatabase((err) => {
|
callback(null);
|
||||||
saving = false;
|
|
||||||
if (err) {
|
|
||||||
callback(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(null);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// save the blockchain as well as the database on the filesystem
|
// save the blockchain as well as the database on the filesystem
|
||||||
function stop(callback) {
|
const stop = (callback) => {
|
||||||
actions.save(callback);
|
actions.save(callback);
|
||||||
}
|
};
|
||||||
|
|
||||||
function addTransactions(block) {
|
const addTransactions = async (block) => {
|
||||||
const transactionsTable = database.getCollection('transactions');
|
const transactionsTable = database.collection('transactions');
|
||||||
const { transactions } = block;
|
const { transactions } = block;
|
||||||
const nbTransactions = transactions.length;
|
const nbTransactions = transactions.length;
|
||||||
|
|
||||||
for (let index = 0; index < nbTransactions; index += 1) {
|
for (let index = 0; index < nbTransactions; index += 1) {
|
||||||
const transaction = transactions[index];
|
const transaction = transactions[index];
|
||||||
const transactionToSave = {
|
const transactionToSave = {
|
||||||
txid: transaction.transactionId,
|
_id: transaction.transactionId,
|
||||||
blockNumber: block.blockNumber,
|
blockNumber: block.blockNumber,
|
||||||
index,
|
index,
|
||||||
};
|
};
|
||||||
|
|
||||||
transactionsTable.insert(transactionToSave);
|
await transactionsTable.insertOne(transactionToSave); // eslint-disable-line no-await-in-loop
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
function updateTableHash(contract, table, record) {
|
const updateTableHash = async (contract, table, record) => {
|
||||||
const contracts = database.getCollection('contracts');
|
const contracts = database.collection('contracts');
|
||||||
const contractInDb = contracts.findOne({ name: contract });
|
const contractInDb = await contracts.findOne({ _id: contract });
|
||||||
|
|
||||||
if (contractInDb && contractInDb.tables[table] !== undefined) {
|
if (contractInDb && contractInDb.tables[table] !== undefined) {
|
||||||
const recordHash = SHA256(JSON.stringify(record)).toString(enchex);
|
const recordHash = SHA256(JSON.stringify(record)).toString(enchex);
|
||||||
|
@ -146,143 +153,192 @@ function updateTableHash(contract, table, record) {
|
||||||
|
|
||||||
contractInDb.tables[table].hash = SHA256(tableHash + recordHash).toString(enchex);
|
contractInDb.tables[table].hash = SHA256(tableHash + recordHash).toString(enchex);
|
||||||
|
|
||||||
contracts.update(contractInDb);
|
await contracts.updateOne({ _id: contract }, { $set: contractInDb });
|
||||||
|
|
||||||
databaseHash = SHA256(databaseHash + contractInDb.tables[table].hash).toString(enchex);
|
databaseHash = SHA256(databaseHash + contractInDb.tables[table].hash).toString(enchex);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
actions.initDatabaseHash = (previousDatabaseHash) => {
|
|
||||||
databaseHash = previousDatabaseHash;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
actions.getDatabaseHash = () => databaseHash;
|
actions.initDatabaseHash = (previousDatabaseHash, callback) => {
|
||||||
|
databaseHash = previousDatabaseHash;
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
|
||||||
actions.getTransactionInfo = (txid) => { // eslint-disable-line no-unused-vars
|
actions.getDatabaseHash = (payload, callback) => {
|
||||||
const transactionsTable = database.getCollection('transactions');
|
callback(databaseHash);
|
||||||
|
};
|
||||||
|
|
||||||
const transaction = transactionsTable.findOne({ txid });
|
actions.getTransactionInfo = async (txid, callback) => {
|
||||||
|
const transactionsTable = database.collection('transactions');
|
||||||
|
|
||||||
|
const transaction = await transactionsTable.findOne({ _id: txid });
|
||||||
|
|
||||||
|
let result = null;
|
||||||
|
|
||||||
if (transaction) {
|
if (transaction) {
|
||||||
const { index, blockNumber } = transaction;
|
const { index, blockNumber } = transaction;
|
||||||
const block = actions.getBlockInfo(blockNumber);
|
const block = await actions.getBlockInfo(blockNumber);
|
||||||
|
|
||||||
if (block) {
|
if (block) {
|
||||||
return Object.assign({}, { blockNumber }, block.transactions[index]);
|
result = Object.assign({}, { blockNumber }, block.transactions[index]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
callback(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
actions.addBlock = (block) => { // eslint-disable-line no-unused-vars
|
actions.addBlock = async (block, callback) => {
|
||||||
chain.insert(block);
|
const finalBlock = block;
|
||||||
addTransactions(block);
|
finalBlock._id = await getNextSequence('chain'); // eslint-disable-line no-underscore-dangle
|
||||||
|
await chain.insertOne(finalBlock);
|
||||||
|
await addTransactions(finalBlock);
|
||||||
|
|
||||||
|
callback();
|
||||||
};
|
};
|
||||||
|
|
||||||
actions.getLatestBlockInfo = () => { // eslint-disable-line no-unused-vars
|
actions.getLatestBlockInfo = async (payload, callback) => {
|
||||||
const { maxId } = chain;
|
const _idNewBlock = await getLastSequence('chain'); // eslint-disable-line no-underscore-dangle
|
||||||
return chain.get(maxId);
|
|
||||||
|
const lastestBlock = await chain.findOne({ _id: _idNewBlock - 1 });
|
||||||
|
|
||||||
|
callback(lastestBlock);
|
||||||
};
|
};
|
||||||
|
|
||||||
actions.getBlockInfo = blockNumber => chain.findOne({ blockNumber });
|
actions.getBlockInfo = async (blockNumber, callback) => {
|
||||||
|
const block = await chain.findOne({ _id: blockNumber });
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
return block;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the information of a contract (owner, source code, etc...)
|
* Get the information of a contract (owner, source code, etc...)
|
||||||
* @param {String} contract name of the contract
|
* @param {String} contract name of the contract
|
||||||
* @returns {Object} returns the contract info if it exists, null otherwise
|
* @returns {Object} returns the contract info if it exists, null otherwise
|
||||||
*/
|
*/
|
||||||
actions.findContract = (payload) => {
|
actions.findContract = async (payload, callback) => {
|
||||||
const { name } = payload;
|
const { name } = payload;
|
||||||
if (name && typeof name === 'string') {
|
if (name && typeof name === 'string') {
|
||||||
const contracts = database.getCollection('contracts');
|
const contracts = database.collection('contracts');
|
||||||
const contractInDb = contracts.findOne({ name });
|
|
||||||
|
const contractInDb = await contracts.findOne({ _id: name });
|
||||||
|
|
||||||
if (contractInDb) {
|
if (contractInDb) {
|
||||||
|
if (callback) {
|
||||||
|
callback(contractInDb);
|
||||||
|
}
|
||||||
return contractInDb;
|
return contractInDb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(null);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* add a smart contract to the database
|
* add a smart contract to the database
|
||||||
* @param {String} name name of the contract
|
* @param {String} _id _id of the contract
|
||||||
* @param {String} owner owner of the contract
|
* @param {String} owner owner of the contract
|
||||||
* @param {String} code code of the contract
|
* @param {String} code code of the contract
|
||||||
* @param {String} tables tables linked to the contract
|
* @param {String} tables tables linked to the contract
|
||||||
*/
|
*/
|
||||||
actions.addContract = (payload) => { // eslint-disable-line no-unused-vars
|
actions.addContract = async (payload, callback) => { // eslint-disable-line no-unused-vars
|
||||||
const {
|
const {
|
||||||
name,
|
_id,
|
||||||
owner,
|
owner,
|
||||||
code,
|
code,
|
||||||
tables,
|
tables,
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
if (name && typeof name === 'string'
|
if (_id && typeof _id === 'string'
|
||||||
&& owner && typeof owner === 'string'
|
&& owner && typeof owner === 'string'
|
||||||
&& code && typeof code === 'string'
|
&& code && typeof code === 'string'
|
||||||
&& tables && typeof tables === 'object') {
|
&& tables && typeof tables === 'object') {
|
||||||
const contracts = database.getCollection('contracts');
|
const contracts = database.collection('contracts');
|
||||||
contracts.insert(payload);
|
await contracts.insertOne(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callback();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* update a smart contract in the database
|
* update a smart contract in the database
|
||||||
* @param {String} name name of the contract
|
* @param {String} _id _id of the contract
|
||||||
* @param {String} owner owner of the contract
|
* @param {String} owner owner of the contract
|
||||||
* @param {String} code code of the contract
|
* @param {String} code code of the contract
|
||||||
* @param {String} tables tables linked to the contract
|
* @param {String} tables tables linked to the contract
|
||||||
*/
|
*/
|
||||||
actions.updateContract = (payload) => { // eslint-disable-line no-unused-vars
|
|
||||||
|
actions.updateContract = async (payload, callback) => { // eslint-disable-line no-unused-vars
|
||||||
const {
|
const {
|
||||||
name,
|
_id,
|
||||||
owner,
|
owner,
|
||||||
code,
|
code,
|
||||||
tables,
|
tables,
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
if (name && typeof name === 'string'
|
if (_id && typeof _id === 'string'
|
||||||
&& owner && typeof owner === 'string'
|
&& owner && typeof owner === 'string'
|
||||||
&& code && typeof code === 'string'
|
&& code && typeof code === 'string'
|
||||||
&& tables && typeof tables === 'object') {
|
&& tables && typeof tables === 'object') {
|
||||||
const contracts = database.getCollection('contracts');
|
const contracts = database.collection('contracts');
|
||||||
|
|
||||||
if (contracts.findOne({ name, owner }) !== null) {
|
if (contracts.findOne({ _id, owner }) !== null) {
|
||||||
contracts.update(payload);
|
await contracts.updateOne({ _id }, { $set: payload });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callback();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a table to the database
|
* Add a table to the database
|
||||||
* @param {String} contractName name of the contract
|
* @param {String} contractName name of the contract
|
||||||
* @param {String} tableName name of the table
|
* @param {String} tableName name of the table
|
||||||
* @param {Array} indexes array of string containing the name of the indexes to create
|
* @param {Array} indexes array of string containing the name of the indexes to create
|
||||||
*/
|
*/
|
||||||
actions.createTable = (payload) => { // eslint-disable-line no-unused-vars
|
actions.createTable = async (payload, callback) => { // eslint-disable-line no-unused-vars
|
||||||
const { contractName, tableName, indexes } = payload;
|
const { contractName, tableName, indexes } = payload;
|
||||||
|
let result = false;
|
||||||
|
|
||||||
// check that the params are correct
|
// check that the params are correct
|
||||||
// each element of the indexes array have to be a string if defined
|
// each element of the indexes array have to be a string if defined
|
||||||
if (validator.isAlphanumeric(tableName)
|
if (validator.isAlphanumeric(tableName)
|
||||||
&& Array.isArray(indexes)
|
&& Array.isArray(indexes)
|
||||||
&& (indexes.length === 0
|
&& (indexes.length === 0
|
||||||
|| (indexes.length > 0 && indexes.every(el => typeof el === 'string' && validator.isAlphanumeric(el))))) {
|
|| (indexes.length > 0 && indexes.every(el => typeof el === 'string' && validator.isAlphanumeric(el))))) {
|
||||||
const finalTableName = `${contractName}_${tableName}`;
|
const finalTableName = `${contractName}_${tableName}`;
|
||||||
// get the table from the database
|
// get the table from the database
|
||||||
const table = database.getCollection(finalTableName);
|
let table = await getCollection(finalTableName);
|
||||||
if (table === null) {
|
if (table === null) {
|
||||||
// if it doesn't exist, create it (with the binary indexes)
|
// if it doesn't exist, create it (with the binary indexes)
|
||||||
database.addCollection(finalTableName, { indices: indexes, disableMeta: true });
|
await initSequence(finalTableName);
|
||||||
return true;
|
await database.createCollection(finalTableName);
|
||||||
|
table = database.collection(finalTableName);
|
||||||
|
|
||||||
|
if (indexes.length > 0) {
|
||||||
|
const nbIndexes = indexes.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < nbIndexes; i += 1) {
|
||||||
|
const index = indexes[i];
|
||||||
|
const finalIndex = {};
|
||||||
|
finalIndex[index] = 1;
|
||||||
|
|
||||||
|
await table.createIndex(finalIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
callback(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -328,10 +384,10 @@ actions.find = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
|
||||||
if (tableData) {
|
if (tableData) {
|
||||||
// if there is an index passed, check if it exists
|
// if there is an index passed, check if it exists
|
||||||
if (ind.length > 0 && ind.every(el => tableData.binaryIndices[el.index] !== undefined || el.index === '$loki')) {
|
if (ind.length > 0 && ind.every(el => tableData.binaryIndices[el.index] !== undefined || el.index === '$loki' || el.index === '_id')) {
|
||||||
return tableData.chain()
|
return tableData.chain()
|
||||||
.find(query)
|
.find(query)
|
||||||
.compoundsort(ind.map(el => [el.index, el.descending]))
|
.compoundsort(ind.map(el => [el.index === '$loki' ? '_id' : el.index, el.descending]))
|
||||||
.offset(off)
|
.offset(off)
|
||||||
.limit(lim)
|
.limit(lim)
|
||||||
.data();
|
.data();
|
||||||
|
@ -351,6 +407,65 @@ actions.find = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retrieve records from the table of a contract
|
||||||
|
* @param {String} contract contract name
|
||||||
|
* @param {String} table table name
|
||||||
|
* @param {JSON} query query to perform on the table
|
||||||
|
* @param {Integer} limit limit the number of records to retrieve
|
||||||
|
* @param {Integer} offset offset applied to the records set
|
||||||
|
* @param {Array<Object>} indexes array of index definitions { index: string, descending: boolean }
|
||||||
|
* @returns {Array<Object>} returns an array of objects if records found, an empty array otherwise
|
||||||
|
*/
|
||||||
|
actions.find = async (payload, callback) => {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
contract,
|
||||||
|
table,
|
||||||
|
query,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
indexes,
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
const lim = limit || 1000;
|
||||||
|
const off = offset || 0;
|
||||||
|
const ind = indexes || [];
|
||||||
|
let result = null;
|
||||||
|
|
||||||
|
if (contract && typeof contract === 'string'
|
||||||
|
&& table && typeof table === 'string'
|
||||||
|
&& query && typeof query === 'object'
|
||||||
|
&& JSON.stringify(query).indexOf('$regex') === -1
|
||||||
|
&& Array.isArray(ind)
|
||||||
|
&& (ind.length === 0
|
||||||
|
|| (ind.length > 0
|
||||||
|
&& ind.every(el => el.index && typeof el.index === 'string'
|
||||||
|
&& el.descending !== undefined && typeof el.descending === 'boolean')))
|
||||||
|
&& Number.isInteger(lim)
|
||||||
|
&& Number.isInteger(off)
|
||||||
|
&& lim > 0 && lim <= 1000
|
||||||
|
&& off >= 0) {
|
||||||
|
const finalTableName = `${contract}_${table}`;
|
||||||
|
const tableData = await getCollection(finalTableName);
|
||||||
|
|
||||||
|
if (tableData) {
|
||||||
|
// if there is an index passed, check if it exists
|
||||||
|
// TODO: check index exists
|
||||||
|
result = await tableData.find(query, {
|
||||||
|
limit: lim,
|
||||||
|
skip: off,
|
||||||
|
sort: ind.map(el => [el.index, el.descending === true ? 'desc' : 'asc']),
|
||||||
|
}).toArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(result);
|
||||||
|
} catch (error) {
|
||||||
|
callback(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retrieve a record from the table of a contract
|
* retrieve a record from the table of a contract
|
||||||
* @param {String} contract contract name
|
* @param {String} contract contract name
|
||||||
|
@ -358,23 +473,25 @@ actions.find = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
* @param {JSON} query query to perform on the table
|
* @param {JSON} query query to perform on the table
|
||||||
* @returns {Object} returns a record if it exists, null otherwise
|
* @returns {Object} returns a record if it exists, null otherwise
|
||||||
*/
|
*/
|
||||||
actions.findOne = (payload) => { // eslint-disable-line no-unused-vars
|
actions.findOne = async (payload, callback) => { // eslint-disable-line no-unused-vars
|
||||||
try {
|
try {
|
||||||
const { contract, table, query } = payload;
|
const { contract, table, query } = payload;
|
||||||
|
let result = null;
|
||||||
if (contract && typeof contract === 'string'
|
if (contract && typeof contract === 'string'
|
||||||
&& table && typeof table === 'string'
|
&& table && typeof table === 'string'
|
||||||
&& query && typeof query === 'object'
|
&& query && typeof query === 'object'
|
||||||
&& JSON.stringify(query).indexOf('$regex') === -1) {
|
&& JSON.stringify(query).indexOf('$regex') === -1) {
|
||||||
const finalTableName = `${contract}_${table}`;
|
const finalTableName = `${contract}_${table}`;
|
||||||
|
|
||||||
const tableData = database.getCollection(finalTableName);
|
const tableData = await getCollection(finalTableName);
|
||||||
return tableData ? tableData.findOne(query) : null;
|
if (tableData) {
|
||||||
|
result = await tableData.findOne(query);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
callback(result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return null;
|
callback(null);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -384,20 +501,23 @@ actions.findOne = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
* @param {String} table table name
|
* @param {String} table table name
|
||||||
* @param {String} record record to save in the table
|
* @param {String} record record to save in the table
|
||||||
*/
|
*/
|
||||||
actions.insert = (payload) => { // eslint-disable-line no-unused-vars
|
actions.insert = async (payload, callback) => { // eslint-disable-line no-unused-vars
|
||||||
const { contract, table, record } = payload;
|
const { contract, table, record } = payload;
|
||||||
const finalTableName = `${contract}_${table}`;
|
const finalTableName = `${contract}_${table}`;
|
||||||
|
let finalRecord = null;
|
||||||
|
|
||||||
const contractInDb = actions.findContract({ name: contract });
|
const contractInDb = await actions.findContract({ name: contract });
|
||||||
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
|
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
|
||||||
const tableInDb = database.getCollection(finalTableName);
|
const tableInDb = await getCollection(finalTableName);
|
||||||
if (tableInDb) {
|
if (tableInDb) {
|
||||||
const result = tableInDb.insert(record);
|
finalRecord = record;
|
||||||
updateTableHash(contract, finalTableName, result);
|
finalRecord._id = await getNextSequence(finalTableName); // eslint-disable-line
|
||||||
return result;
|
await tableInDb.insertOne(finalRecord);
|
||||||
|
await updateTableHash(contract, finalTableName, finalRecord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
|
callback(finalRecord);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -406,16 +526,18 @@ actions.insert = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
* @param {String} table table name
|
* @param {String} table table name
|
||||||
* @param {String} record record to remove from the table
|
* @param {String} record record to remove from the table
|
||||||
*/
|
*/
|
||||||
actions.remove = (payload) => { // eslint-disable-line no-unused-vars
|
actions.remove = async (payload, callback) => { // eslint-disable-line no-unused-vars
|
||||||
const { contract, table, record } = payload;
|
const { contract, table, record } = payload;
|
||||||
const finalTableName = `${contract}_${table}`;
|
const finalTableName = `${contract}_${table}`;
|
||||||
|
|
||||||
const contractInDb = actions.findContract({ name: contract });
|
const contractInDb = await actions.findContract({ name: contract });
|
||||||
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
|
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
|
||||||
const tableInDb = database.getCollection(finalTableName);
|
const tableInDb = await getCollection(finalTableName);
|
||||||
if (tableInDb) {
|
if (tableInDb) {
|
||||||
updateTableHash(contract, finalTableName, record);
|
await updateTableHash(contract, finalTableName, record);
|
||||||
tableInDb.remove(record);
|
tableInDb.deleteOne({ _id: record._id }); // eslint-disable-line no-underscore-dangle
|
||||||
|
|
||||||
|
callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -426,18 +548,21 @@ actions.remove = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
* @param {String} table table name
|
* @param {String} table table name
|
||||||
* @param {String} record record to update in the table
|
* @param {String} record record to update in the table
|
||||||
*/
|
*/
|
||||||
actions.update = (payload) => { // eslint-disable-line no-unused-vars
|
actions.update = async (payload, callback) => {
|
||||||
const { contract, table, record } = payload;
|
const { contract, table, record } = payload;
|
||||||
const finalTableName = `${contract}_${table}`;
|
const finalTableName = `${contract}_${table}`;
|
||||||
|
|
||||||
const contractInDb = actions.findContract({ name: contract });
|
const contractInDb = await actions.findContract({ name: contract });
|
||||||
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
|
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
|
||||||
const tableInDb = database.getCollection(finalTableName);
|
const tableInDb = await getCollection(finalTableName);
|
||||||
if (tableInDb) {
|
if (tableInDb) {
|
||||||
updateTableHash(contract, finalTableName, record);
|
await updateTableHash(contract, finalTableName, record);
|
||||||
tableInDb.update(record);
|
|
||||||
|
tableInDb.updateOne({ _id: record._id }, { $set: record }); // eslint-disable-line
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
callback();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -447,18 +572,20 @@ actions.update = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
* @param {String} record record to update in the table
|
* @param {String} record record to update in the table
|
||||||
* @returns {Object} returns the table details if it exists, null otherwise
|
* @returns {Object} returns the table details if it exists, null otherwise
|
||||||
*/
|
*/
|
||||||
actions.getTableDetails = (payload) => { // eslint-disable-line no-unused-vars
|
actions.getTableDetails = async (payload, callback) => {
|
||||||
const { contract, table } = payload;
|
const { contract, table } = payload;
|
||||||
const finalTableName = `${contract}_${table}`;
|
const finalTableName = `${contract}_${table}`;
|
||||||
const contractInDb = actions.findContract({ name: contract });
|
const contractInDb = await actions.findContract({ name: contract });
|
||||||
|
let tableDetails = null;
|
||||||
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
|
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
|
||||||
const tableInDb = database.getCollection(finalTableName);
|
const tableInDb = await getCollection(finalTableName);
|
||||||
if (tableInDb) {
|
if (tableInDb) {
|
||||||
return { ...tableInDb, data: [] };
|
tableDetails = Object.assign({}, contractInDb.tables[finalTableName]);
|
||||||
|
tableDetails.indexes = await tableInDb.indexInformation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
callback(tableDetails);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -467,18 +594,19 @@ actions.getTableDetails = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
* @param {String} table table name
|
* @param {String} table table name
|
||||||
* @returns {Object} returns true if the table exists, false otherwise
|
* @returns {Object} returns true if the table exists, false otherwise
|
||||||
*/
|
*/
|
||||||
actions.tableExists = (payload) => { // eslint-disable-line no-unused-vars
|
actions.tableExists = async (payload, callback) => {
|
||||||
const { contract, table } = payload;
|
const { contract, table } = payload;
|
||||||
const finalTableName = `${contract}_${table}`;
|
const finalTableName = `${contract}_${table}`;
|
||||||
const contractInDb = actions.findContract({ name: contract });
|
let result = false;
|
||||||
|
const contractInDb = await actions.findContract({ name: contract });
|
||||||
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
|
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
|
||||||
const tableInDb = database.getCollection(finalTableName);
|
const tableInDb = await getCollection(finalTableName);
|
||||||
if (tableInDb) {
|
if (tableInDb) {
|
||||||
return true;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
callback(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -490,7 +618,7 @@ actions.tableExists = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
* @param {Array<Object>} indexes array of index definitions { index: string, descending: boolean }
|
* @param {Array<Object>} indexes array of index definitions { index: string, descending: boolean }
|
||||||
* @returns {Array<Object>} returns an array of objects if records found, an empty array otherwise
|
* @returns {Array<Object>} returns an array of objects if records found, an empty array otherwise
|
||||||
*/
|
*/
|
||||||
actions.dfind = (payload) => { // eslint-disable-line no-unused-vars
|
actions.dfind = async (payload, callback) => { // eslint-disable-line no-unused-vars
|
||||||
const {
|
const {
|
||||||
table,
|
table,
|
||||||
query,
|
query,
|
||||||
|
@ -503,27 +631,18 @@ actions.dfind = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
const off = offset || 0;
|
const off = offset || 0;
|
||||||
const ind = indexes || [];
|
const ind = indexes || [];
|
||||||
|
|
||||||
const tableData = database.getCollection(table);
|
const tableInDb = await getCollection(table);
|
||||||
|
let records = [];
|
||||||
|
|
||||||
if (tableData) {
|
if (tableInDb) {
|
||||||
// if there is an index passed, check if it exists
|
records = await tableInDb.find(query, {
|
||||||
if (ind.length > 0) {
|
limit: lim,
|
||||||
return tableData.chain()
|
skip: off,
|
||||||
.find(query)
|
sort: ind.map(el => [el.index, el.descending === true ? 'desc' : 'asc']),
|
||||||
.compoundsort(ind.map(el => [el.index, el.descending]))
|
});
|
||||||
.offset(off)
|
|
||||||
.limit(lim)
|
|
||||||
.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
return tableData.chain()
|
|
||||||
.find(query)
|
|
||||||
.offset(off)
|
|
||||||
.limit(lim)
|
|
||||||
.data();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
callback(records);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -532,15 +651,17 @@ actions.dfind = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
* @param {JSON} query query to perform on the table
|
* @param {JSON} query query to perform on the table
|
||||||
* @returns {Object} returns a record if it exists, null otherwise
|
* @returns {Object} returns a record if it exists, null otherwise
|
||||||
*/
|
*/
|
||||||
actions.dfindOne = (payload) => { // eslint-disable-line no-unused-vars
|
actions.dfindOne = async (payload, callback) => {
|
||||||
const { table, query } = payload;
|
const { table, query } = payload;
|
||||||
|
|
||||||
const tableData = database.getCollection(table);
|
const tableInDb = await getCollection(table);
|
||||||
if (tableData) {
|
let record = null;
|
||||||
return tableData.findOne(query);
|
|
||||||
|
if (tableInDb) {
|
||||||
|
record = await tableInDb.findOne(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
callback(record);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -548,11 +669,15 @@ actions.dfindOne = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
* @param {String} table table name
|
* @param {String} table table name
|
||||||
* @param {String} record record to save in the table
|
* @param {String} record record to save in the table
|
||||||
*/
|
*/
|
||||||
actions.dinsert = (payload) => { // eslint-disable-line no-unused-vars
|
actions.dinsert = async (payload, callback) => {
|
||||||
const { table, record } = payload;
|
const { table, record } = payload;
|
||||||
const tableInDb = database.getCollection(table);
|
const tableInDb = database.collection(table);
|
||||||
updateTableHash(table.split('_')[0], table.split('_')[1], record);
|
const finalRecord = record;
|
||||||
return tableInDb.insert(record);
|
finalRecord._id = await getNextSequence(table); // eslint-disable-line
|
||||||
|
await tableInDb.insertOne(finalRecord);
|
||||||
|
await updateTableHash(table.split('_')[0], table.split('_')[1], record);
|
||||||
|
|
||||||
|
callback(finalRecord);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -560,12 +685,16 @@ actions.dinsert = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
* @param {String} table table name
|
* @param {String} table table name
|
||||||
* @param {String} record record to update in the table
|
* @param {String} record record to update in the table
|
||||||
*/
|
*/
|
||||||
actions.dupdate = (payload) => { // eslint-disable-line no-unused-vars
|
actions.dupdate = async (payload, callback) => {
|
||||||
const { table, record } = payload;
|
const { table, record } = payload;
|
||||||
|
|
||||||
const tableInDb = database.getCollection(table);
|
const tableInDb = database.collection(table);
|
||||||
updateTableHash(table.split('_')[0], table.split('_')[1], record);
|
await updateTableHash(table.split('_')[0], table.split('_')[1], record);
|
||||||
tableInDb.update(record);
|
await tableInDb.updateOne(
|
||||||
|
{ _id: record._id }, { $set: record }, // eslint-disable-line no-underscore-dangle
|
||||||
|
);
|
||||||
|
|
||||||
|
callback();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -573,12 +702,14 @@ actions.dupdate = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
* @param {String} table table name
|
* @param {String} table table name
|
||||||
* @param {String} record record to remove from the table
|
* @param {String} record record to remove from the table
|
||||||
*/
|
*/
|
||||||
actions.dremove = (payload) => { // eslint-disable-line no-unused-vars
|
actions.dremove = async (payload, callback) => { // eslint-disable-line no-unused-vars
|
||||||
const { table, record } = payload;
|
const { table, record } = payload;
|
||||||
|
|
||||||
const tableInDb = database.getCollection(table);
|
const tableInDb = database.collection(table);
|
||||||
updateTableHash(table.split('_')[0], table.split('_')[1], record);
|
await updateTableHash(table.split('_')[0], table.split('_')[1], record);
|
||||||
tableInDb.remove(record);
|
await tableInDb.deleteOne({ _id: record._id }); // eslint-disable-line no-underscore-dangle
|
||||||
|
|
||||||
|
callback();
|
||||||
};
|
};
|
||||||
|
|
||||||
ipc.onReceiveMessage((message) => {
|
ipc.onReceiveMessage((message) => {
|
||||||
|
@ -609,9 +740,10 @@ ipc.onReceiveMessage((message) => {
|
||||||
});
|
});
|
||||||
} else if (action && typeof actions[action] === 'function') {
|
} else if (action && typeof actions[action] === 'function') {
|
||||||
if (!saving) {
|
if (!saving) {
|
||||||
const res = actions[action](payload);
|
actions[action](payload, (res) => {
|
||||||
// console.log('action', action, 'res', res, 'payload', payload);
|
// console.log('action', action, 'res', res, 'payload', payload);
|
||||||
ipc.reply(message, res);
|
ipc.reply(message, res);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
ipc.reply(message);
|
ipc.reply(message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,625 @@
|
||||||
|
const fs = require('fs-extra');
|
||||||
|
const Loki = require('lokijs');
|
||||||
|
const SHA256 = require('crypto-js/sha256');
|
||||||
|
const enchex = require('crypto-js/enc-hex');
|
||||||
|
const validator = require('validator');
|
||||||
|
const lfsa = require('../libs/loki-fs-structured-adapter');
|
||||||
|
const { IPC } = require('../libs/IPC');
|
||||||
|
|
||||||
|
const BC_PLUGIN_NAME = require('./Blockchain.constants').PLUGIN_NAME;
|
||||||
|
const BC_PLUGIN_ACTIONS = require('./Blockchain.constants').PLUGIN_ACTIONS;
|
||||||
|
|
||||||
|
const { PLUGIN_NAME, PLUGIN_ACTIONS } = require('./Database.constants');
|
||||||
|
|
||||||
|
const PLUGIN_PATH = require.resolve(__filename);
|
||||||
|
|
||||||
|
const actions = {};
|
||||||
|
|
||||||
|
const ipc = new IPC(PLUGIN_NAME);
|
||||||
|
|
||||||
|
let database = null;
|
||||||
|
let chain = null;
|
||||||
|
let saving = false;
|
||||||
|
let databaseHash = '';
|
||||||
|
|
||||||
|
// load the database from the filesystem
|
||||||
|
async function init(conf, callback) {
|
||||||
|
const {
|
||||||
|
autosaveInterval,
|
||||||
|
databaseFileName,
|
||||||
|
dataDirectory,
|
||||||
|
} = conf;
|
||||||
|
|
||||||
|
const databaseFilePath = dataDirectory + databaseFileName;
|
||||||
|
|
||||||
|
// init the database
|
||||||
|
database = new Loki(databaseFilePath, {
|
||||||
|
adapter: new lfsa(), // eslint-disable-line new-cap
|
||||||
|
autosave: autosaveInterval > 0,
|
||||||
|
autosaveInterval,
|
||||||
|
});
|
||||||
|
|
||||||
|
// check if the app has already be run
|
||||||
|
if (fs.pathExistsSync(databaseFilePath)) {
|
||||||
|
// load the database from the filesystem to the RAM
|
||||||
|
database.loadDatabase({}, (errorDb) => {
|
||||||
|
if (errorDb) {
|
||||||
|
callback(errorDb);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the chain or the contracts collection doesn't exist we return an error
|
||||||
|
chain = database.getCollection('chain');
|
||||||
|
const contracts = database.getCollection('contracts');
|
||||||
|
if (chain === null || contracts === null) {
|
||||||
|
callback('The database is missing either the chain or the contracts table');
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// create the data directory if necessary and empty it if files exists
|
||||||
|
fs.emptyDirSync(dataDirectory);
|
||||||
|
|
||||||
|
// init the main tables
|
||||||
|
chain = database.addCollection('chain', { indices: ['blockNumber'], disableMeta: true });
|
||||||
|
database.addCollection('transactions', { unique: ['txid'], disableMeta: true });
|
||||||
|
database.addCollection('contracts', { indices: ['name'], disableMeta: true });
|
||||||
|
|
||||||
|
callback(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generateGenesisBlock(conf, callback) {
|
||||||
|
const {
|
||||||
|
chainId,
|
||||||
|
genesisSteemBlock,
|
||||||
|
} = conf;
|
||||||
|
|
||||||
|
// check if genesis block hasn't been generated already
|
||||||
|
const genBlock = actions.getBlockInfo(0);
|
||||||
|
|
||||||
|
if (!genBlock) {
|
||||||
|
// insert the genesis block
|
||||||
|
const res = await ipc.send(
|
||||||
|
{
|
||||||
|
to: BC_PLUGIN_NAME,
|
||||||
|
action: BC_PLUGIN_ACTIONS.CREATE_GENESIS_BLOCK,
|
||||||
|
payload: {
|
||||||
|
chainId,
|
||||||
|
genesisSteemBlock,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
chain.insert(res.payload);
|
||||||
|
|
||||||
|
// initialize the block production tools
|
||||||
|
// BlockProduction.initialize(database, genesisSteemBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the blockchain as well as the database on the filesystem
|
||||||
|
actions.save = (callback) => {
|
||||||
|
saving = true;
|
||||||
|
|
||||||
|
// save the database from the RAM to the filesystem
|
||||||
|
database.saveDatabase((err) => {
|
||||||
|
saving = false;
|
||||||
|
if (err) {
|
||||||
|
callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// save the blockchain as well as the database on the filesystem
|
||||||
|
function stop(callback) {
|
||||||
|
actions.save(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTransactions(block) {
|
||||||
|
const transactionsTable = database.getCollection('transactions');
|
||||||
|
const { transactions } = block;
|
||||||
|
const nbTransactions = transactions.length;
|
||||||
|
|
||||||
|
for (let index = 0; index < nbTransactions; index += 1) {
|
||||||
|
const transaction = transactions[index];
|
||||||
|
const transactionToSave = {
|
||||||
|
txid: transaction.transactionId,
|
||||||
|
blockNumber: block.blockNumber,
|
||||||
|
index,
|
||||||
|
};
|
||||||
|
|
||||||
|
transactionsTable.insert(transactionToSave);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTableHash(contract, table, record) {
|
||||||
|
const contracts = database.getCollection('contracts');
|
||||||
|
const contractInDb = contracts.findOne({ name: contract });
|
||||||
|
|
||||||
|
if (contractInDb && contractInDb.tables[table] !== undefined) {
|
||||||
|
const recordHash = SHA256(JSON.stringify(record)).toString(enchex);
|
||||||
|
const tableHash = contractInDb.tables[table].hash;
|
||||||
|
|
||||||
|
contractInDb.tables[table].hash = SHA256(tableHash + recordHash).toString(enchex);
|
||||||
|
|
||||||
|
contracts.update(contractInDb);
|
||||||
|
|
||||||
|
databaseHash = SHA256(databaseHash + contractInDb.tables[table].hash).toString(enchex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.initDatabaseHash = (previousDatabaseHash) => {
|
||||||
|
databaseHash = previousDatabaseHash;
|
||||||
|
};
|
||||||
|
|
||||||
|
actions.getDatabaseHash = () => databaseHash;
|
||||||
|
|
||||||
|
actions.getTransactionInfo = (txid) => { // eslint-disable-line no-unused-vars
|
||||||
|
const transactionsTable = database.getCollection('transactions');
|
||||||
|
|
||||||
|
const transaction = transactionsTable.findOne({ txid });
|
||||||
|
|
||||||
|
if (transaction) {
|
||||||
|
const { index, blockNumber } = transaction;
|
||||||
|
const block = actions.getBlockInfo(blockNumber);
|
||||||
|
|
||||||
|
if (block) {
|
||||||
|
return Object.assign({}, { blockNumber }, block.transactions[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
actions.addBlock = (block) => { // eslint-disable-line no-unused-vars
|
||||||
|
chain.insert(block);
|
||||||
|
addTransactions(block);
|
||||||
|
};
|
||||||
|
|
||||||
|
actions.getLatestBlockInfo = () => { // eslint-disable-line no-unused-vars
|
||||||
|
const { maxId } = chain;
|
||||||
|
return chain.get(maxId);
|
||||||
|
};
|
||||||
|
|
||||||
|
actions.getBlockInfo = blockNumber => chain.findOne({ blockNumber });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the information of a contract (owner, source code, etc...)
|
||||||
|
* @param {String} contract name of the contract
|
||||||
|
* @returns {Object} returns the contract info if it exists, null otherwise
|
||||||
|
*/
|
||||||
|
actions.findContract = (payload) => {
|
||||||
|
const { name } = payload;
|
||||||
|
if (name && typeof name === 'string') {
|
||||||
|
const contracts = database.getCollection('contracts');
|
||||||
|
const contractInDb = contracts.findOne({ name });
|
||||||
|
|
||||||
|
if (contractInDb) {
|
||||||
|
return contractInDb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* add a smart contract to the database
|
||||||
|
* @param {String} name name of the contract
|
||||||
|
* @param {String} owner owner of the contract
|
||||||
|
* @param {String} code code of the contract
|
||||||
|
* @param {String} tables tables linked to the contract
|
||||||
|
*/
|
||||||
|
actions.addContract = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
owner,
|
||||||
|
code,
|
||||||
|
tables,
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
if (name && typeof name === 'string'
|
||||||
|
&& owner && typeof owner === 'string'
|
||||||
|
&& code && typeof code === 'string'
|
||||||
|
&& tables && typeof tables === 'object') {
|
||||||
|
const contracts = database.getCollection('contracts');
|
||||||
|
contracts.insert(payload);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update a smart contract in the database
|
||||||
|
* @param {String} name name of the contract
|
||||||
|
* @param {String} owner owner of the contract
|
||||||
|
* @param {String} code code of the contract
|
||||||
|
* @param {String} tables tables linked to the contract
|
||||||
|
*/
|
||||||
|
actions.updateContract = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
owner,
|
||||||
|
code,
|
||||||
|
tables,
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
if (name && typeof name === 'string'
|
||||||
|
&& owner && typeof owner === 'string'
|
||||||
|
&& code && typeof code === 'string'
|
||||||
|
&& tables && typeof tables === 'object') {
|
||||||
|
const contracts = database.getCollection('contracts');
|
||||||
|
|
||||||
|
if (contracts.findOne({ name, owner }) !== null) {
|
||||||
|
contracts.update(payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a table to the database
|
||||||
|
* @param {String} contractName name of the contract
|
||||||
|
* @param {String} tableName name of the table
|
||||||
|
* @param {Array} indexes array of string containing the name of the indexes to create
|
||||||
|
*/
|
||||||
|
actions.createTable = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
const { contractName, tableName, indexes } = payload;
|
||||||
|
|
||||||
|
// check that the params are correct
|
||||||
|
// each element of the indexes array have to be a string if defined
|
||||||
|
if (validator.isAlphanumeric(tableName)
|
||||||
|
&& Array.isArray(indexes)
|
||||||
|
&& (indexes.length === 0
|
||||||
|
|| (indexes.length > 0 && indexes.every(el => typeof el === 'string' && validator.isAlphanumeric(el))))) {
|
||||||
|
const finalTableName = `${contractName}_${tableName}`;
|
||||||
|
// get the table from the database
|
||||||
|
const table = database.getCollection(finalTableName);
|
||||||
|
if (table === null) {
|
||||||
|
// if it doesn't exist, create it (with the binary indexes)
|
||||||
|
database.addCollection(finalTableName, { indices: indexes, disableMeta: true });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retrieve records from the table of a contract
|
||||||
|
* @param {String} contract contract name
|
||||||
|
* @param {String} table table name
|
||||||
|
* @param {JSON} query query to perform on the table
|
||||||
|
* @param {Integer} limit limit the number of records to retrieve
|
||||||
|
* @param {Integer} offset offset applied to the records set
|
||||||
|
* @param {Array<Object>} indexes array of index definitions { index: string, descending: boolean }
|
||||||
|
* @returns {Array<Object>} returns an array of objects if records found, an empty array otherwise
|
||||||
|
*/
|
||||||
|
actions.find = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
contract,
|
||||||
|
table,
|
||||||
|
query,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
indexes,
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
const lim = limit || 1000;
|
||||||
|
const off = offset || 0;
|
||||||
|
const ind = indexes || [];
|
||||||
|
|
||||||
|
if (contract && typeof contract === 'string'
|
||||||
|
&& table && typeof table === 'string'
|
||||||
|
&& query && typeof query === 'object'
|
||||||
|
&& JSON.stringify(query).indexOf('$regex') === -1
|
||||||
|
&& Array.isArray(ind)
|
||||||
|
&& (ind.length === 0
|
||||||
|
|| (ind.length > 0
|
||||||
|
&& ind.every(el => el.index && typeof el.index === 'string'
|
||||||
|
&& el.descending !== undefined && typeof el.descending === 'boolean')))
|
||||||
|
&& Number.isInteger(lim)
|
||||||
|
&& Number.isInteger(off)
|
||||||
|
&& lim > 0 && lim <= 1000
|
||||||
|
&& off >= 0) {
|
||||||
|
const finalTableName = `${contract}_${table}`;
|
||||||
|
const tableData = database.getCollection(finalTableName);
|
||||||
|
|
||||||
|
if (tableData) {
|
||||||
|
// if there is an index passed, check if it exists
|
||||||
|
if (ind.length > 0 && ind.every(el => tableData.binaryIndices[el.index] !== undefined || el.index === '$loki')) {
|
||||||
|
return tableData.chain()
|
||||||
|
.find(query)
|
||||||
|
.compoundsort(ind.map(el => [el.index, el.descending]))
|
||||||
|
.offset(off)
|
||||||
|
.limit(lim)
|
||||||
|
.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
return tableData.chain()
|
||||||
|
.find(query)
|
||||||
|
.offset(off)
|
||||||
|
.limit(lim)
|
||||||
|
.data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retrieve a record from the table of a contract
|
||||||
|
* @param {String} contract contract name
|
||||||
|
* @param {String} table table name
|
||||||
|
* @param {JSON} query query to perform on the table
|
||||||
|
* @returns {Object} returns a record if it exists, null otherwise
|
||||||
|
*/
|
||||||
|
actions.findOne = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
try {
|
||||||
|
const { contract, table, query } = payload;
|
||||||
|
|
||||||
|
if (contract && typeof contract === 'string'
|
||||||
|
&& table && typeof table === 'string'
|
||||||
|
&& query && typeof query === 'object'
|
||||||
|
&& JSON.stringify(query).indexOf('$regex') === -1) {
|
||||||
|
const finalTableName = `${contract}_${table}`;
|
||||||
|
|
||||||
|
const tableData = database.getCollection(finalTableName);
|
||||||
|
return tableData ? tableData.findOne(query) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* insert a record in the table of a contract
|
||||||
|
* @param {String} contract contract name
|
||||||
|
* @param {String} table table name
|
||||||
|
* @param {String} record record to save in the table
|
||||||
|
*/
|
||||||
|
actions.insert = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
const { contract, table, record } = payload;
|
||||||
|
const finalTableName = `${contract}_${table}`;
|
||||||
|
|
||||||
|
const contractInDb = actions.findContract({ name: contract });
|
||||||
|
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
|
||||||
|
const tableInDb = database.getCollection(finalTableName);
|
||||||
|
if (tableInDb) {
|
||||||
|
const result = tableInDb.insert(record);
|
||||||
|
updateTableHash(contract, finalTableName, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove a record in the table of a contract
|
||||||
|
* @param {String} contract contract name
|
||||||
|
* @param {String} table table name
|
||||||
|
* @param {String} record record to remove from the table
|
||||||
|
*/
|
||||||
|
actions.remove = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
const { contract, table, record } = payload;
|
||||||
|
const finalTableName = `${contract}_${table}`;
|
||||||
|
|
||||||
|
const contractInDb = actions.findContract({ name: contract });
|
||||||
|
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
|
||||||
|
const tableInDb = database.getCollection(finalTableName);
|
||||||
|
if (tableInDb) {
|
||||||
|
updateTableHash(contract, finalTableName, record);
|
||||||
|
tableInDb.remove(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update a record in the table of a contract
|
||||||
|
* @param {String} contract contract name
|
||||||
|
* @param {String} table table name
|
||||||
|
* @param {String} record record to update in the table
|
||||||
|
*/
|
||||||
|
actions.update = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
const { contract, table, record } = payload;
|
||||||
|
const finalTableName = `${contract}_${table}`;
|
||||||
|
|
||||||
|
const contractInDb = actions.findContract({ name: contract });
|
||||||
|
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
|
||||||
|
const tableInDb = database.getCollection(finalTableName);
|
||||||
|
if (tableInDb) {
|
||||||
|
updateTableHash(contract, finalTableName, record);
|
||||||
|
tableInDb.update(record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the details of a smart contract table
|
||||||
|
* @param {String} contract contract name
|
||||||
|
* @param {String} table table name
|
||||||
|
* @param {String} record record to update in the table
|
||||||
|
* @returns {Object} returns the table details if it exists, null otherwise
|
||||||
|
*/
|
||||||
|
actions.getTableDetails = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
const { contract, table } = payload;
|
||||||
|
const finalTableName = `${contract}_${table}`;
|
||||||
|
const contractInDb = actions.findContract({ name: contract });
|
||||||
|
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
|
||||||
|
const tableInDb = database.getCollection(finalTableName);
|
||||||
|
if (tableInDb) {
|
||||||
|
return { ...tableInDb, data: [] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if a table exists
|
||||||
|
* @param {String} contract contract name
|
||||||
|
* @param {String} table table name
|
||||||
|
* @returns {Object} returns true if the table exists, false otherwise
|
||||||
|
*/
|
||||||
|
actions.tableExists = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
const { contract, table } = payload;
|
||||||
|
const finalTableName = `${contract}_${table}`;
|
||||||
|
const contractInDb = actions.findContract({ name: contract });
|
||||||
|
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
|
||||||
|
const tableInDb = database.getCollection(finalTableName);
|
||||||
|
if (tableInDb) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retrieve records from the table
|
||||||
|
* @param {String} table table name
|
||||||
|
* @param {JSON} query query to perform on the table
|
||||||
|
* @param {Integer} limit limit the number of records to retrieve
|
||||||
|
* @param {Integer} offset offset applied to the records set
|
||||||
|
* @param {Array<Object>} indexes array of index definitions { index: string, descending: boolean }
|
||||||
|
* @returns {Array<Object>} returns an array of objects if records found, an empty array otherwise
|
||||||
|
*/
|
||||||
|
actions.dfind = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
const {
|
||||||
|
table,
|
||||||
|
query,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
indexes,
|
||||||
|
} = payload;
|
||||||
|
|
||||||
|
const lim = limit || 1000;
|
||||||
|
const off = offset || 0;
|
||||||
|
const ind = indexes || [];
|
||||||
|
|
||||||
|
const tableData = database.getCollection(table);
|
||||||
|
|
||||||
|
if (tableData) {
|
||||||
|
// if there is an index passed, check if it exists
|
||||||
|
if (ind.length > 0) {
|
||||||
|
return tableData.chain()
|
||||||
|
.find(query)
|
||||||
|
.compoundsort(ind.map(el => [el.index, el.descending]))
|
||||||
|
.offset(off)
|
||||||
|
.limit(lim)
|
||||||
|
.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
return tableData.chain()
|
||||||
|
.find(query)
|
||||||
|
.offset(off)
|
||||||
|
.limit(lim)
|
||||||
|
.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* retrieve a record from the table
|
||||||
|
* @param {String} table table name
|
||||||
|
* @param {JSON} query query to perform on the table
|
||||||
|
* @returns {Object} returns a record if it exists, null otherwise
|
||||||
|
*/
|
||||||
|
actions.dfindOne = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
const { table, query } = payload;
|
||||||
|
|
||||||
|
const tableData = database.getCollection(table);
|
||||||
|
if (tableData) {
|
||||||
|
return tableData.findOne(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* insert a record
|
||||||
|
* @param {String} table table name
|
||||||
|
* @param {String} record record to save in the table
|
||||||
|
*/
|
||||||
|
actions.dinsert = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
const { table, record } = payload;
|
||||||
|
const tableInDb = database.getCollection(table);
|
||||||
|
updateTableHash(table.split('_')[0], table.split('_')[1], record);
|
||||||
|
return tableInDb.insert(record);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* update a record in the table
|
||||||
|
* @param {String} table table name
|
||||||
|
* @param {String} record record to update in the table
|
||||||
|
*/
|
||||||
|
actions.dupdate = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
const { table, record } = payload;
|
||||||
|
|
||||||
|
const tableInDb = database.getCollection(table);
|
||||||
|
updateTableHash(table.split('_')[0], table.split('_')[1], record);
|
||||||
|
tableInDb.update(record);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* remove a record
|
||||||
|
* @param {String} table table name
|
||||||
|
* @param {String} record record to remove from the table
|
||||||
|
*/
|
||||||
|
actions.dremove = (payload) => { // eslint-disable-line no-unused-vars
|
||||||
|
const { table, record } = payload;
|
||||||
|
|
||||||
|
const tableInDb = database.getCollection(table);
|
||||||
|
updateTableHash(table.split('_')[0], table.split('_')[1], record);
|
||||||
|
tableInDb.remove(record);
|
||||||
|
};
|
||||||
|
|
||||||
|
ipc.onReceiveMessage((message) => {
|
||||||
|
const {
|
||||||
|
action,
|
||||||
|
payload,
|
||||||
|
// from,
|
||||||
|
} = message;
|
||||||
|
|
||||||
|
if (action === 'init') {
|
||||||
|
init(payload, (res) => {
|
||||||
|
console.log('successfully initialized'); // eslint-disable-line no-console
|
||||||
|
ipc.reply(message, res);
|
||||||
|
});
|
||||||
|
} else if (action === 'stop') {
|
||||||
|
stop((res) => {
|
||||||
|
console.log('successfully saved'); // eslint-disable-line no-console
|
||||||
|
ipc.reply(message, res);
|
||||||
|
});
|
||||||
|
} else if (action === PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK) {
|
||||||
|
generateGenesisBlock(payload, () => {
|
||||||
|
ipc.reply(message);
|
||||||
|
});
|
||||||
|
} else if (action === PLUGIN_ACTIONS.SAVE) {
|
||||||
|
actions.save((res) => {
|
||||||
|
console.log('successfully saved'); // eslint-disable-line no-console
|
||||||
|
ipc.reply(message, res);
|
||||||
|
});
|
||||||
|
} else if (action && typeof actions[action] === 'function') {
|
||||||
|
if (!saving) {
|
||||||
|
const res = actions[action](payload);
|
||||||
|
// console.log('action', action, 'res', res, 'payload', payload);
|
||||||
|
ipc.reply(message, res);
|
||||||
|
} else {
|
||||||
|
ipc.reply(message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ipc.reply(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.PLUGIN_PATH = PLUGIN_PATH;
|
||||||
|
module.exports.PLUGIN_NAME = PLUGIN_NAME;
|
||||||
|
module.exports.PLUGIN_ACTIONS = PLUGIN_ACTIONS;
|
|
@ -0,0 +1,15 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
read -p "Enter the mongodb container name : " containerName
|
||||||
|
read -p "Enter the database name : " database
|
||||||
|
|
||||||
|
read -p "$database is going to be dropped in the container $containerName, are you sure? " -n 1 -r
|
||||||
|
echo # (optional) move to a new line
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]
|
||||||
|
then
|
||||||
|
[[ "$0" = "$BASH_SOURCE" ]] && exit 1 || return 1 # handle exits from shell or function but don't exit interactive shell
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Dropping database $database in the container $containerName"
|
||||||
|
docker exec $containerName /bin/sh -c "mongo $database --eval \"db.dropDatabase()\""
|
||||||
|
echo "Done extracting blocks from $containerName"
|
61
test/dice.js
61
test/dice.js
|
@ -1,13 +1,13 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
const { fork } = require('child_process');
|
const { fork } = require('child_process');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const fs = require('fs-extra');
|
|
||||||
|
|
||||||
const database = require('../plugins/Database');
|
const database = require('../plugins/Database');
|
||||||
const blockchain = require('../plugins/Blockchain');
|
const blockchain = require('../plugins/Blockchain');
|
||||||
const { Block } = require('../libs/Block');
|
const { Block } = require('../libs/Block');
|
||||||
const { Transaction } = require('../libs/Transaction');
|
const { Transaction } = require('../libs/Transaction');
|
||||||
const { CONSTANTS } = require('../libs/Constants');
|
const { CONSTANTS } = require('../libs/Constants');
|
||||||
|
const { MongoClient } = require('mongodb');
|
||||||
|
|
||||||
//process.env.NODE_ENV = 'test';
|
//process.env.NODE_ENV = 'test';
|
||||||
|
|
||||||
|
@ -18,16 +18,14 @@ const conf = {
|
||||||
databaseFileName: "database.db",
|
databaseFileName: "database.db",
|
||||||
autosaveInterval: 0,
|
autosaveInterval: 0,
|
||||||
javascriptVMTimeout: 10000,
|
javascriptVMTimeout: 10000,
|
||||||
|
databaseURL: "mongodb://localhost:27017",
|
||||||
|
databaseName: "testssc",
|
||||||
};
|
};
|
||||||
|
|
||||||
let plugins = {};
|
let plugins = {};
|
||||||
let jobs = new Map();
|
let jobs = new Map();
|
||||||
let currentJobId = 0;
|
let currentJobId = 0;
|
||||||
|
|
||||||
function cleanDataFolder() {
|
|
||||||
fs.emptyDirSync(conf.dataDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
function send(pluginName, from, message) {
|
function send(pluginName, from, message) {
|
||||||
const plugin = plugins[pluginName];
|
const plugin = plugins[pluginName];
|
||||||
const newMessage = {
|
const newMessage = {
|
||||||
|
@ -96,11 +94,55 @@ const unloadPlugin = (plugin) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// dice
|
// dice
|
||||||
describe.skip('dice', () => {
|
describe.skip('dice', function() {
|
||||||
|
this.timeout(10000);
|
||||||
|
|
||||||
|
before((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
client = await MongoClient.connect(conf.databaseURL, { useNewUrlParser: true });
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
await db.dropDatabase();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
after((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await client.close();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach((done) => {
|
||||||
|
// runs after each test in this block
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await db.dropDatabase()
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
it('makes you win', (done) => {
|
it('makes you win', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -140,8 +182,7 @@ describe.skip('dice', () => {
|
||||||
|
|
||||||
it('makes you lose', (done) => {
|
it('makes you lose', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
|
143
test/market.js
143
test/market.js
|
@ -2,16 +2,14 @@
|
||||||
const { fork } = require('child_process');
|
const { fork } = require('child_process');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
|
const { MongoClient } = require('mongodb');
|
||||||
|
|
||||||
const database = require('../plugins/Database');
|
const database = require('../plugins/Database');
|
||||||
const blockchain = require('../plugins/Blockchain');
|
const blockchain = require('../plugins/Blockchain');
|
||||||
const { Block } = require('../libs/Block');
|
|
||||||
const { Transaction } = require('../libs/Transaction');
|
const { Transaction } = require('../libs/Transaction');
|
||||||
|
|
||||||
const { CONSTANTS } = require('../libs/Constants');
|
const { CONSTANTS } = require('../libs/Constants');
|
||||||
|
|
||||||
//process.env.NODE_ENV = 'test';
|
|
||||||
|
|
||||||
const conf = {
|
const conf = {
|
||||||
chainId: "test-chain-id",
|
chainId: "test-chain-id",
|
||||||
genesisSteemBlock: 2000000,
|
genesisSteemBlock: 2000000,
|
||||||
|
@ -19,16 +17,14 @@ const conf = {
|
||||||
databaseFileName: "database.db",
|
databaseFileName: "database.db",
|
||||||
autosaveInterval: 0,
|
autosaveInterval: 0,
|
||||||
javascriptVMTimeout: 10000,
|
javascriptVMTimeout: 10000,
|
||||||
|
databaseURL: "mongodb://localhost:27017",
|
||||||
|
databaseName: "testssc",
|
||||||
};
|
};
|
||||||
|
|
||||||
let plugins = {};
|
let plugins = {};
|
||||||
let jobs = new Map();
|
let jobs = new Map();
|
||||||
let currentJobId = 0;
|
let currentJobId = 0;
|
||||||
|
|
||||||
function cleanDataFolder() {
|
|
||||||
fs.emptyDirSync(conf.dataDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
function send(pluginName, from, message) {
|
function send(pluginName, from, message) {
|
||||||
const plugin = plugins[pluginName];
|
const plugin = plugins[pluginName];
|
||||||
const newMessage = {
|
const newMessage = {
|
||||||
|
@ -132,11 +128,55 @@ let mktContractPayload = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Market
|
// Market
|
||||||
describe('Market', () => {
|
describe('Market', function() {
|
||||||
|
this.timeout(10000);
|
||||||
|
|
||||||
|
before((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
client = await MongoClient.connect(conf.databaseURL, { useNewUrlParser: true });
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
await db.dropDatabase();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
after((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await client.close();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach((done) => {
|
||||||
|
// runs after each test in this block
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await db.dropDatabase()
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
it('creates a buy order', (done) => {
|
it('creates a buy order', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -224,8 +264,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('creates buy orders with expirations', (done) => {
|
it('creates buy orders with expirations', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -265,11 +304,11 @@ describe('Market', () => {
|
||||||
|
|
||||||
const sellOrders = res.payload;
|
const sellOrders = res.payload;
|
||||||
|
|
||||||
assert.equal(sellOrders[0].txId, 'TXID1237');
|
assert.equal(sellOrders[0].txId, 'TXID1235');
|
||||||
assert.equal(sellOrders[0].account, 'satoshi');
|
assert.equal(sellOrders[0].account, 'satoshi');
|
||||||
assert.equal(sellOrders[0].symbol, 'TKN');
|
assert.equal(sellOrders[0].symbol, 'TKN');
|
||||||
assert.equal(sellOrders[0].price, '0.00000001');
|
assert.equal(sellOrders[0].price, '0.00000001');
|
||||||
assert.equal(sellOrders[0].quantity, 3);
|
assert.equal(sellOrders[0].quantity, 1);
|
||||||
assert.equal(sellOrders[0].timestamp, 1527811200);
|
assert.equal(sellOrders[0].timestamp, 1527811200);
|
||||||
assert.equal(sellOrders[0].expiration, 1527811200 + 2592000);
|
assert.equal(sellOrders[0].expiration, 1527811200 + 2592000);
|
||||||
|
|
||||||
|
@ -281,11 +320,11 @@ describe('Market', () => {
|
||||||
assert.equal(sellOrders[1].timestamp, 1527811200);
|
assert.equal(sellOrders[1].timestamp, 1527811200);
|
||||||
assert.equal(sellOrders[1].expiration, 1527811200 + 10);
|
assert.equal(sellOrders[1].expiration, 1527811200 + 10);
|
||||||
|
|
||||||
assert.equal(sellOrders[2].txId, 'TXID1235');
|
assert.equal(sellOrders[2].txId, 'TXID1237');
|
||||||
assert.equal(sellOrders[2].account, 'satoshi');
|
assert.equal(sellOrders[2].account, 'satoshi');
|
||||||
assert.equal(sellOrders[2].symbol, 'TKN');
|
assert.equal(sellOrders[2].symbol, 'TKN');
|
||||||
assert.equal(sellOrders[2].price, '0.00000001');
|
assert.equal(sellOrders[2].price, '0.00000001');
|
||||||
assert.equal(sellOrders[2].quantity, 1);
|
assert.equal(sellOrders[2].quantity, 3);
|
||||||
assert.equal(sellOrders[2].timestamp, 1527811200);
|
assert.equal(sellOrders[2].timestamp, 1527811200);
|
||||||
assert.equal(sellOrders[2].expiration, 1527811200 + 2592000);
|
assert.equal(sellOrders[2].expiration, 1527811200 + 2592000);
|
||||||
|
|
||||||
|
@ -300,8 +339,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('generates error when trying to create a buy order with wrong parameters', (done) => {
|
it('generates error when trying to create a buy order with wrong parameters', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -345,8 +383,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('creates sell orders with expirations', (done) => {
|
it('creates sell orders with expirations', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -386,11 +423,11 @@ describe('Market', () => {
|
||||||
|
|
||||||
const sellOrders = res.payload;
|
const sellOrders = res.payload;
|
||||||
|
|
||||||
assert.equal(sellOrders[0].txId, 'TXID1237');
|
assert.equal(sellOrders[0].txId, 'TXID1235');
|
||||||
assert.equal(sellOrders[0].account, 'satoshi');
|
assert.equal(sellOrders[0].account, 'satoshi');
|
||||||
assert.equal(sellOrders[0].symbol, 'TKN');
|
assert.equal(sellOrders[0].symbol, 'TKN');
|
||||||
assert.equal(sellOrders[0].price, '0.00000001');
|
assert.equal(sellOrders[0].price, '0.00000001');
|
||||||
assert.equal(sellOrders[0].quantity, 3);
|
assert.equal(sellOrders[0].quantity, 1);
|
||||||
assert.equal(sellOrders[0].timestamp, 1527811200);
|
assert.equal(sellOrders[0].timestamp, 1527811200);
|
||||||
assert.equal(sellOrders[0].expiration, 1527811200 + 2592000);
|
assert.equal(sellOrders[0].expiration, 1527811200 + 2592000);
|
||||||
|
|
||||||
|
@ -402,11 +439,11 @@ describe('Market', () => {
|
||||||
assert.equal(sellOrders[1].timestamp, 1527811200);
|
assert.equal(sellOrders[1].timestamp, 1527811200);
|
||||||
assert.equal(sellOrders[1].expiration, 1527811200 + 10);
|
assert.equal(sellOrders[1].expiration, 1527811200 + 10);
|
||||||
|
|
||||||
assert.equal(sellOrders[2].txId, 'TXID1235');
|
assert.equal(sellOrders[2].txId, 'TXID1237');
|
||||||
assert.equal(sellOrders[2].account, 'satoshi');
|
assert.equal(sellOrders[2].account, 'satoshi');
|
||||||
assert.equal(sellOrders[2].symbol, 'TKN');
|
assert.equal(sellOrders[2].symbol, 'TKN');
|
||||||
assert.equal(sellOrders[2].price, '0.00000001');
|
assert.equal(sellOrders[2].price, '0.00000001');
|
||||||
assert.equal(sellOrders[2].quantity, 1);
|
assert.equal(sellOrders[2].quantity, 3);
|
||||||
assert.equal(sellOrders[2].timestamp, 1527811200);
|
assert.equal(sellOrders[2].timestamp, 1527811200);
|
||||||
assert.equal(sellOrders[2].expiration, 1527811200 + 2592000);
|
assert.equal(sellOrders[2].expiration, 1527811200 + 2592000);
|
||||||
|
|
||||||
|
@ -421,8 +458,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('creates a sell order', (done) => {
|
it('creates a sell order', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -510,8 +546,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('generates error when trying to create a sell order with wrong parameters', (done) => {
|
it('generates error when trying to create a sell order with wrong parameters', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -556,8 +591,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('cancels a buy order', (done) => {
|
it('cancels a buy order', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -692,8 +726,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('cancels a sell order', (done) => {
|
it('cancels a sell order', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -831,8 +864,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('buys from the market from one seller', (done) => {
|
it('buys from the market from one seller', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -871,6 +903,7 @@ describe('Market', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
let balances = res.payload;
|
let balances = res.payload;
|
||||||
|
balances.sort((a, b) => a._id - b._id);
|
||||||
|
|
||||||
assert.equal(balances[0].account, 'vitalik');
|
assert.equal(balances[0].account, 'vitalik');
|
||||||
assert.equal(balances[0].symbol, 'TKN');
|
assert.equal(balances[0].symbol, 'TKN');
|
||||||
|
@ -936,8 +969,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('buys from the market from several sellers', (done) => {
|
it('buys from the market from several sellers', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -980,6 +1012,7 @@ describe('Market', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const balances = res.payload;
|
const balances = res.payload;
|
||||||
|
balances.sort((a, b) => a._id - b._id);
|
||||||
|
|
||||||
assert.equal(balances[0].account, 'harpagon');
|
assert.equal(balances[0].account, 'harpagon');
|
||||||
assert.equal(balances[0].symbol, 'STEEMP');
|
assert.equal(balances[0].symbol, 'STEEMP');
|
||||||
|
@ -1024,8 +1057,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('buys from the market partially', (done) => {
|
it('buys from the market partially', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1068,6 +1100,7 @@ describe('Market', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
let balances = res.payload;
|
let balances = res.payload;
|
||||||
|
balances.sort((a, b) => a._id - b._id);
|
||||||
|
|
||||||
assert.equal(balances[0].account, 'harpagon');
|
assert.equal(balances[0].account, 'harpagon');
|
||||||
assert.equal(balances[0].symbol, 'STEEMP');
|
assert.equal(balances[0].symbol, 'STEEMP');
|
||||||
|
@ -1150,8 +1183,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('sells on the market to one buyer', (done) => {
|
it('sells on the market to one buyer', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1193,6 +1225,7 @@ describe('Market', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
let balances = res.payload;
|
let balances = res.payload;
|
||||||
|
balances.sort((a, b) => a._id - b._id);
|
||||||
|
|
||||||
assert.equal(balances[0].account, 'vitalik');
|
assert.equal(balances[0].account, 'vitalik');
|
||||||
assert.equal(balances[0].symbol, 'TKN');
|
assert.equal(balances[0].symbol, 'TKN');
|
||||||
|
@ -1258,8 +1291,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('sells on the market to several buyers', (done) => {
|
it('sells on the market to several buyers', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1306,6 +1338,7 @@ describe('Market', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const balances = res.payload;
|
const balances = res.payload;
|
||||||
|
balances.sort((a, b) => a._id - b._id);
|
||||||
|
|
||||||
assert.equal(balances[0].account, 'harpagon');
|
assert.equal(balances[0].account, 'harpagon');
|
||||||
assert.equal(balances[0].symbol, 'TKN');
|
assert.equal(balances[0].symbol, 'TKN');
|
||||||
|
@ -1350,8 +1383,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('fills a buy order from different sellers', (done) => {
|
it('fills a buy order from different sellers', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1398,6 +1430,7 @@ describe('Market', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const balances = res.payload;
|
const balances = res.payload;
|
||||||
|
balances.sort((a, b) => a._id - b._id);
|
||||||
|
|
||||||
assert.equal(balances[0].account, 'harpagon');
|
assert.equal(balances[0].account, 'harpagon');
|
||||||
assert.equal(balances[0].symbol, 'STEEMP');
|
assert.equal(balances[0].symbol, 'STEEMP');
|
||||||
|
@ -1442,8 +1475,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('creates a trade history', (done) => {
|
it('creates a trade history', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1632,8 +1664,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('maintains the different metrics', (done) => {
|
it('maintains the different metrics', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1819,8 +1850,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('removes an expired sell order', (done) => {
|
it('removes an expired sell order', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1926,8 +1956,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('removes an expired buy order', (done) => {
|
it('removes an expired buy order', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -2032,8 +2061,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('removes dust sell orders', (done) => {
|
it('removes dust sell orders', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -2072,6 +2100,7 @@ describe('Market', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
let balances = res.payload;
|
let balances = res.payload;
|
||||||
|
balances.sort((a, b) => a._id - b._id);
|
||||||
|
|
||||||
assert.equal(balances[0].account, 'vitalik');
|
assert.equal(balances[0].account, 'vitalik');
|
||||||
assert.equal(balances[0].symbol, 'TKN');
|
assert.equal(balances[0].symbol, 'TKN');
|
||||||
|
@ -2149,6 +2178,7 @@ describe('Market', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
balances = res.payload;
|
balances = res.payload;
|
||||||
|
balances.sort((a, b) => a._id - b._id);
|
||||||
|
|
||||||
assert.equal(balances[0].account, 'vitalik');
|
assert.equal(balances[0].account, 'vitalik');
|
||||||
assert.equal(balances[0].symbol, 'TKN');
|
assert.equal(balances[0].symbol, 'TKN');
|
||||||
|
@ -2210,8 +2240,7 @@ describe('Market', () => {
|
||||||
|
|
||||||
it('removes dust buy orders', (done) => {
|
it('removes dust buy orders', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -2253,6 +2282,7 @@ describe('Market', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
let balances = res.payload;
|
let balances = res.payload;
|
||||||
|
balances.sort((a, b) => a._id - b._id);
|
||||||
|
|
||||||
assert.equal(balances[0].account, 'vitalik');
|
assert.equal(balances[0].account, 'vitalik');
|
||||||
assert.equal(balances[0].symbol, 'TKN');
|
assert.equal(balances[0].symbol, 'TKN');
|
||||||
|
@ -2330,6 +2360,7 @@ describe('Market', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
balances = res.payload;
|
balances = res.payload;
|
||||||
|
balances.sort((a, b) => a._id - b._id);
|
||||||
|
|
||||||
assert.equal(balances[0].account, 'vitalik');
|
assert.equal(balances[0].account, 'vitalik');
|
||||||
assert.equal(balances[0].symbol, 'TKN');
|
assert.equal(balances[0].symbol, 'TKN');
|
||||||
|
|
|
@ -2,18 +2,15 @@
|
||||||
const { fork } = require('child_process');
|
const { fork } = require('child_process');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const BigNumber = require('bignumber.js');
|
const { MongoClient } = require('mongodb');
|
||||||
const { Base64 } = require('js-base64');
|
const { Base64 } = require('js-base64');
|
||||||
|
|
||||||
const database = require('../plugins/Database');
|
const database = require('../plugins/Database');
|
||||||
const blockchain = require('../plugins/Blockchain');
|
const blockchain = require('../plugins/Blockchain');
|
||||||
const { Block } = require('../libs/Block');
|
|
||||||
const { Transaction } = require('../libs/Transaction');
|
const { Transaction } = require('../libs/Transaction');
|
||||||
|
|
||||||
const { CONSTANTS } = require('../libs/Constants');
|
const { CONSTANTS } = require('../libs/Constants');
|
||||||
|
|
||||||
//process.env.NODE_ENV = 'test';
|
|
||||||
|
|
||||||
const conf = {
|
const conf = {
|
||||||
chainId: "test-chain-id",
|
chainId: "test-chain-id",
|
||||||
genesisSteemBlock: 2000000,
|
genesisSteemBlock: 2000000,
|
||||||
|
@ -21,16 +18,14 @@ const conf = {
|
||||||
databaseFileName: "database.db",
|
databaseFileName: "database.db",
|
||||||
autosaveInterval: 0,
|
autosaveInterval: 0,
|
||||||
javascriptVMTimeout: 10000,
|
javascriptVMTimeout: 10000,
|
||||||
|
databaseURL: "mongodb://localhost:27017",
|
||||||
|
databaseName: "testssc",
|
||||||
};
|
};
|
||||||
|
|
||||||
let plugins = {};
|
let plugins = {};
|
||||||
let jobs = new Map();
|
let jobs = new Map();
|
||||||
let currentJobId = 0;
|
let currentJobId = 0;
|
||||||
|
|
||||||
function cleanDataFolder() {
|
|
||||||
fs.emptyDirSync(conf.dataDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
function send(pluginName, from, message) {
|
function send(pluginName, from, message) {
|
||||||
const plugin = plugins[pluginName];
|
const plugin = plugins[pluginName];
|
||||||
const newMessage = {
|
const newMessage = {
|
||||||
|
@ -117,10 +112,52 @@ let contractPayload = {
|
||||||
describe('smart tokens', function () {
|
describe('smart tokens', function () {
|
||||||
this.timeout(30000);
|
this.timeout(30000);
|
||||||
|
|
||||||
|
before((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
client = await MongoClient.connect(conf.databaseURL, { useNewUrlParser: true });
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
await db.dropDatabase();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
after((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await client.close();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach((done) => {
|
||||||
|
// runs after each test in this block
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await db.dropDatabase()
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
it('should enable delegation', (done) => {
|
it('should enable delegation', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -174,8 +211,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should not enable delegation', (done) => {
|
it('should not enable delegation', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -228,8 +264,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should delegate tokens', (done) => {
|
it('should delegate tokens', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -334,6 +369,7 @@ describe('smart tokens', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
balances = res.payload;
|
balances = res.payload;
|
||||||
|
balances.sort((a, b) => a._id - b._id);
|
||||||
|
|
||||||
assert.equal(balances[0].symbol, 'TKN');
|
assert.equal(balances[0].symbol, 'TKN');
|
||||||
assert.equal(balances[0].account, 'satoshi');
|
assert.equal(balances[0].account, 'satoshi');
|
||||||
|
@ -369,12 +405,12 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
assert.equal(delegations[0].symbol, 'TKN');
|
assert.equal(delegations[0].symbol, 'TKN');
|
||||||
assert.equal(delegations[0].from, 'satoshi');
|
assert.equal(delegations[0].from, 'satoshi');
|
||||||
assert.equal(delegations[0].to, 'ned');
|
assert.equal(delegations[0].to, 'vitalik');
|
||||||
assert.equal(delegations[0].quantity, '0.00000002');
|
assert.equal(delegations[0].quantity, '0.00000002');
|
||||||
|
|
||||||
assert.equal(delegations[1].symbol, 'TKN');
|
assert.equal(delegations[1].symbol, 'TKN');
|
||||||
assert.equal(delegations[1].from, 'satoshi');
|
assert.equal(delegations[1].from, 'satoshi');
|
||||||
assert.equal(delegations[1].to, 'vitalik');
|
assert.equal(delegations[1].to, 'ned');
|
||||||
assert.equal(delegations[1].quantity, '0.00000002');
|
assert.equal(delegations[1].quantity, '0.00000002');
|
||||||
|
|
||||||
resolve();
|
resolve();
|
||||||
|
@ -388,8 +424,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should not delegate tokens', (done) => {
|
it('should not delegate tokens', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -491,8 +526,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should undelegate tokens', (done) => {
|
it('should undelegate tokens', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -534,6 +568,8 @@ describe('smart tokens', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
let balances = res.payload;
|
let balances = res.payload;
|
||||||
|
balances.sort((a, b) => a._id - b._id);
|
||||||
|
|
||||||
assert.equal(balances[0].symbol, 'TKN');
|
assert.equal(balances[0].symbol, 'TKN');
|
||||||
assert.equal(balances[0].account, 'satoshi');
|
assert.equal(balances[0].account, 'satoshi');
|
||||||
assert.equal(balances[0].balance, "99.99999997");
|
assert.equal(balances[0].balance, "99.99999997");
|
||||||
|
@ -569,13 +605,13 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
assert.equal(delegations[0].symbol, 'TKN');
|
assert.equal(delegations[0].symbol, 'TKN');
|
||||||
assert.equal(delegations[0].from, 'satoshi');
|
assert.equal(delegations[0].from, 'satoshi');
|
||||||
assert.equal(delegations[0].to, 'ned');
|
assert.equal(delegations[0].to, 'vitalik');
|
||||||
assert.equal(delegations[0].quantity, '0.00000001');
|
assert.equal(delegations[0].quantity, '0.00000002');
|
||||||
|
|
||||||
assert.equal(delegations[1].symbol, 'TKN');
|
assert.equal(delegations[1].symbol, 'TKN');
|
||||||
assert.equal(delegations[1].from, 'satoshi');
|
assert.equal(delegations[1].from, 'satoshi');
|
||||||
assert.equal(delegations[1].to, 'vitalik');
|
assert.equal(delegations[1].to, 'ned');
|
||||||
assert.equal(delegations[1].quantity, '0.00000002');
|
assert.equal(delegations[1].quantity, '0.00000001');
|
||||||
|
|
||||||
transactions = [];
|
transactions = [];
|
||||||
transactions.push(new Transaction(12345678901, 'TXID1242', 'satoshi', 'tokens', 'undelegate', '{ "symbol": "TKN", "quantity": "0.00000001", "to": "vitalik", "isSignedWithActiveKey": true }'));
|
transactions.push(new Transaction(12345678901, 'TXID1242', 'satoshi', 'tokens', 'undelegate', '{ "symbol": "TKN", "quantity": "0.00000001", "to": "vitalik", "isSignedWithActiveKey": true }'));
|
||||||
|
@ -605,6 +641,7 @@ describe('smart tokens', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
balances = res.payload;
|
balances = res.payload;
|
||||||
|
balances.sort((a, b) => a._id - b._id);
|
||||||
|
|
||||||
assert.equal(balances[0].symbol, 'TKN');
|
assert.equal(balances[0].symbol, 'TKN');
|
||||||
assert.equal(balances[0].account, 'satoshi');
|
assert.equal(balances[0].account, 'satoshi');
|
||||||
|
@ -682,8 +719,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should not undelegate tokens', (done) => {
|
it('should not undelegate tokens', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -751,8 +787,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should process the pending undelegations', (done) => {
|
it('should process the pending undelegations', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -870,8 +905,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should enable staking', (done) => {
|
it('should enable staking', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -922,8 +956,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should not enable staking', (done) => {
|
it('should not enable staking', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -988,8 +1021,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should not enable staking again', (done) => {
|
it('should not enable staking again', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1050,8 +1082,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should stake tokens', (done) => {
|
it('should stake tokens', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1180,8 +1211,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should not stake tokens', (done) => {
|
it('should not stake tokens', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1252,8 +1282,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should start the unstake process', (done) => {
|
it('should start the unstake process', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1363,8 +1392,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should not start the unstake process', (done) => {
|
it('should not start the unstake process', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1433,8 +1461,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should cancel an unstake', (done) => {
|
it('should cancel an unstake', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1592,8 +1619,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should not cancel an unstake', (done) => {
|
it('should not cancel an unstake', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1757,8 +1783,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should process the pending unstakes', (done) => {
|
it('should process the pending unstakes', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1962,8 +1987,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it.skip('should process thousands of pending unstakes', (done) => {
|
it.skip('should process thousands of pending unstakes', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -2236,8 +2260,7 @@ describe('smart tokens', function () {
|
||||||
|
|
||||||
it('should process the pending unstakes (with multi transactions)', (done) => {
|
it('should process the pending unstakes (with multi transactions)', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
|
|
@ -2,17 +2,14 @@
|
||||||
const { fork } = require('child_process');
|
const { fork } = require('child_process');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const BigNumber = require('bignumber.js');
|
const { MongoClient } = require('mongodb');
|
||||||
|
|
||||||
const database = require('../plugins/Database');
|
const database = require('../plugins/Database');
|
||||||
const blockchain = require('../plugins/Blockchain');
|
const blockchain = require('../plugins/Blockchain');
|
||||||
const { Block } = require('../libs/Block');
|
|
||||||
const { Transaction } = require('../libs/Transaction');
|
const { Transaction } = require('../libs/Transaction');
|
||||||
|
|
||||||
const { CONSTANTS } = require('../libs/Constants');
|
const { CONSTANTS } = require('../libs/Constants');
|
||||||
|
|
||||||
//process.env.NODE_ENV = 'test';
|
|
||||||
|
|
||||||
const conf = {
|
const conf = {
|
||||||
chainId: "test-chain-id",
|
chainId: "test-chain-id",
|
||||||
genesisSteemBlock: 2000000,
|
genesisSteemBlock: 2000000,
|
||||||
|
@ -20,16 +17,14 @@ const conf = {
|
||||||
databaseFileName: "database.db",
|
databaseFileName: "database.db",
|
||||||
autosaveInterval: 0,
|
autosaveInterval: 0,
|
||||||
javascriptVMTimeout: 10000,
|
javascriptVMTimeout: 10000,
|
||||||
|
databaseURL: "mongodb://localhost:27017",
|
||||||
|
databaseName: "testssc",
|
||||||
};
|
};
|
||||||
|
|
||||||
let plugins = {};
|
let plugins = {};
|
||||||
let jobs = new Map();
|
let jobs = new Map();
|
||||||
let currentJobId = 0;
|
let currentJobId = 0;
|
||||||
|
|
||||||
function cleanDataFolder() {
|
|
||||||
fs.emptyDirSync(conf.dataDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
function send(pluginName, from, message) {
|
function send(pluginName, from, message) {
|
||||||
const plugin = plugins[pluginName];
|
const plugin = plugins[pluginName];
|
||||||
const newMessage = {
|
const newMessage = {
|
||||||
|
@ -98,12 +93,55 @@ const unloadPlugin = (plugin) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// sscstore
|
// sscstore
|
||||||
describe('sscstore smart contract', () => {
|
describe('sscstore smart contract', function() {
|
||||||
|
this.timeout(10000);
|
||||||
|
|
||||||
|
before((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
client = await MongoClient.connect(conf.databaseURL, { useNewUrlParser: true });
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
await db.dropDatabase();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
after((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await client.close();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach((done) => {
|
||||||
|
// runs after each test in this block
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await db.dropDatabase()
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
it('should buy tokens', (done) => {
|
it('should buy tokens', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -149,8 +187,7 @@ describe('sscstore smart contract', () => {
|
||||||
|
|
||||||
it('should not buy tokens', (done) => {
|
it('should not buy tokens', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -227,8 +264,7 @@ describe('sscstore smart contract', () => {
|
||||||
|
|
||||||
it('should update params', (done) => {
|
it('should update params', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -275,8 +311,7 @@ describe('sscstore smart contract', () => {
|
||||||
|
|
||||||
it('should not update params', (done) => {
|
it('should not update params', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
const { fork } = require('child_process');
|
const { fork } = require('child_process');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
|
const { MongoClient } = require('mongodb');
|
||||||
|
|
||||||
const database = require('../plugins/Database');
|
const database = require('../plugins/Database');
|
||||||
const blockchain = require('../plugins/Blockchain');
|
const blockchain = require('../plugins/Blockchain');
|
||||||
const { Block } = require('../libs/Block');
|
|
||||||
const { Transaction } = require('../libs/Transaction');
|
const { Transaction } = require('../libs/Transaction');
|
||||||
|
|
||||||
const { CONSTANTS } = require('../libs/Constants');
|
const { CONSTANTS } = require('../libs/Constants');
|
||||||
|
@ -19,16 +19,14 @@ const conf = {
|
||||||
databaseFileName: "database.db",
|
databaseFileName: "database.db",
|
||||||
autosaveInterval: 0,
|
autosaveInterval: 0,
|
||||||
javascriptVMTimeout: 10000,
|
javascriptVMTimeout: 10000,
|
||||||
|
databaseURL: "mongodb://localhost:27017",
|
||||||
|
databaseName: "testssc",
|
||||||
};
|
};
|
||||||
|
|
||||||
let plugins = {};
|
let plugins = {};
|
||||||
let jobs = new Map();
|
let jobs = new Map();
|
||||||
let currentJobId = 0;
|
let currentJobId = 0;
|
||||||
|
|
||||||
function cleanDataFolder() {
|
|
||||||
fs.emptyDirSync(conf.dataDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
function send(pluginName, from, message) {
|
function send(pluginName, from, message) {
|
||||||
const plugin = plugins[pluginName];
|
const plugin = plugins[pluginName];
|
||||||
const newMessage = {
|
const newMessage = {
|
||||||
|
@ -122,11 +120,55 @@ let spContractPayload = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// STEEMP
|
// STEEMP
|
||||||
describe('Steem Pegged', () => {
|
describe('Steem Pegged', function () {
|
||||||
|
this.timeout(10000);
|
||||||
|
|
||||||
|
before((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
client = await MongoClient.connect(conf.databaseURL, { useNewUrlParser: true });
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
await db.dropDatabase();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
after((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await client.close();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach((done) => {
|
||||||
|
// runs after each test in this block
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await db.dropDatabase()
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
it('buys STEEMP', (done) => {
|
it('buys STEEMP', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -182,8 +224,7 @@ describe('Steem Pegged', () => {
|
||||||
|
|
||||||
it('withdraws STEEM', (done) => {
|
it('withdraws STEEM', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -290,8 +331,7 @@ describe('Steem Pegged', () => {
|
||||||
|
|
||||||
it('does not withdraw STEEM', (done) => {
|
it('does not withdraw STEEM', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,13 @@
|
||||||
const { fork } = require('child_process');
|
const { fork } = require('child_process');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const { Base64 } = require('js-base64');
|
const { Base64 } = require('js-base64');
|
||||||
const fs = require('fs-extra');
|
const { MongoClient } = require('mongodb');
|
||||||
|
|
||||||
const database = require('../plugins/Database');
|
const database = require('../plugins/Database');
|
||||||
const blockchain = require('../plugins/Blockchain');
|
const blockchain = require('../plugins/Blockchain');
|
||||||
const { Block } = require('../libs/Block');
|
const { Block } = require('../libs/Block');
|
||||||
const { Transaction } = require('../libs/Transaction');
|
const { Transaction } = require('../libs/Transaction');
|
||||||
|
|
||||||
//process.env.NODE_ENV = 'test';
|
|
||||||
|
|
||||||
const conf = {
|
const conf = {
|
||||||
chainId: "test-chain-id",
|
chainId: "test-chain-id",
|
||||||
genesisSteemBlock: 2000000,
|
genesisSteemBlock: 2000000,
|
||||||
|
@ -18,16 +16,14 @@ const conf = {
|
||||||
databaseFileName: "database.db",
|
databaseFileName: "database.db",
|
||||||
autosaveInterval: 0,
|
autosaveInterval: 0,
|
||||||
javascriptVMTimeout: 10000,
|
javascriptVMTimeout: 10000,
|
||||||
|
databaseURL: "mongodb://localhost:27017",
|
||||||
|
databaseName: "testssc",
|
||||||
};
|
};
|
||||||
|
|
||||||
let plugins = {};
|
let plugins = {};
|
||||||
let jobs = new Map();
|
let jobs = new Map();
|
||||||
let currentJobId = 0;
|
let currentJobId = 0;
|
||||||
|
|
||||||
function cleanDataFolder() {
|
|
||||||
fs.emptyDirSync(conf.dataDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
function send(pluginName, from, message) {
|
function send(pluginName, from, message) {
|
||||||
const plugin = plugins[pluginName];
|
const plugin = plugins[pluginName];
|
||||||
const newMessage = {
|
const newMessage = {
|
||||||
|
@ -96,12 +92,56 @@ const unloadPlugin = (plugin) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
describe('Database', () => {
|
describe('Database', function () {
|
||||||
|
|
||||||
|
this.timeout(10000);
|
||||||
|
|
||||||
|
before((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
client = await MongoClient.connect(conf.databaseURL, { useNewUrlParser: true });
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
await db.dropDatabase();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
after((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await client.close();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach((done) => {
|
||||||
|
// runs after each test in this block
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await db.dropDatabase()
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
it('should get the genesis block', (done) => {
|
it('should get the genesis block', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -119,8 +159,7 @@ describe('Database', () => {
|
||||||
|
|
||||||
it('should get the latest block', (done) => {
|
it('should get the latest block', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -168,11 +207,55 @@ describe('Database', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// smart contracts
|
// smart contracts
|
||||||
describe('Smart Contracts', () => {
|
describe('Smart Contracts', function () {
|
||||||
|
this.timeout(10000);
|
||||||
|
|
||||||
|
before((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
client = await MongoClient.connect(conf.databaseURL, { useNewUrlParser: true });
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
await db.dropDatabase();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
after((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await client.close();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach((done) => {
|
||||||
|
// runs after each test in this block
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await db.dropDatabase()
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
it('should deploy a basic smart contract', (done) => {
|
it('should deploy a basic smart contract', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -207,7 +290,7 @@ describe('Smart Contracts', () => {
|
||||||
const res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND_CONTRACT, payload: { name: 'testContract' } });
|
const res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND_CONTRACT, payload: { name: 'testContract' } });
|
||||||
const contract = res.payload;
|
const contract = res.payload;
|
||||||
|
|
||||||
assert.equal(contract.name, 'testContract');
|
assert.equal(contract._id, 'testContract');
|
||||||
assert.equal(contract.owner, 'steemsc');
|
assert.equal(contract.owner, 'steemsc');
|
||||||
resolve()
|
resolve()
|
||||||
})
|
})
|
||||||
|
@ -220,8 +303,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should create a table during the smart contract deployment', (done) => {
|
it('should create a table during the smart contract deployment', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -258,11 +340,10 @@ describe('Smart Contracts', () => {
|
||||||
const contract = res.payload;
|
const contract = res.payload;
|
||||||
|
|
||||||
assert.notEqual(contract.tables['testContract_testTable'], undefined);
|
assert.notEqual(contract.tables['testContract_testTable'], undefined);
|
||||||
|
|
||||||
res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GET_TABLE_DETAILS, payload: { contract: 'testContract', table: 'testTable' } });
|
res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GET_TABLE_DETAILS, payload: { contract: 'testContract', table: 'testTable' } });
|
||||||
|
|
||||||
assert.notEqual(res.payload, null);
|
assert.notEqual(res.payload, null);
|
||||||
|
|
||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
@ -274,8 +355,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should create a table with indexes during the smart contract deployment', (done) => {
|
it('should create a table with indexes during the smart contract deployment', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -310,10 +390,17 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
const res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GET_TABLE_DETAILS, payload: { contract: 'testContract', table: 'testTable' } });
|
const res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GET_TABLE_DETAILS, payload: { contract: 'testContract', table: 'testTable' } });
|
||||||
const table = res.payload;
|
const table = res.payload;
|
||||||
const { binaryIndices } = table;
|
const { indexes } = table;
|
||||||
|
|
||||||
|
assert.equal(indexes._id_[0][0], '_id');
|
||||||
|
assert.equal(indexes._id_[0][1], 1);
|
||||||
|
|
||||||
|
assert.equal(indexes.index1_1[0][0], 'index1');
|
||||||
|
assert.equal(indexes.index1_1[0][1], 1);
|
||||||
|
|
||||||
|
assert.equal(indexes.index2_1[0][0], 'index2');
|
||||||
|
assert.equal(indexes.index2_1[0][1], 1);
|
||||||
|
|
||||||
assert.notEqual(binaryIndices['index1'], undefined);
|
|
||||||
assert.notEqual(binaryIndices['index2'], undefined);
|
|
||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
@ -325,8 +412,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should add a record into a smart contract table', (done) => {
|
it('should add a record into a smart contract table', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -385,8 +471,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should update a record from a smart contract table', (done) => {
|
it('should update a record from a smart contract table', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -458,8 +543,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should remove a record from a smart contract table', (done) => {
|
it('should remove a record from a smart contract table', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -526,8 +610,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should read the records from a smart contract table via pagination', (done) => {
|
it('should read the records from a smart contract table via pagination', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -590,8 +673,8 @@ describe('Smart Contracts', () => {
|
||||||
let res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
|
let res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
|
||||||
let users = res.payload;
|
let users = res.payload;
|
||||||
|
|
||||||
assert.equal(users[0].$loki, 1);
|
assert.equal(users[0]._id, 1);
|
||||||
assert.equal(users[4].$loki, 5);
|
assert.equal(users[4]._id, 5);
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
contract: 'usersContract',
|
contract: 'usersContract',
|
||||||
|
@ -604,8 +687,8 @@ describe('Smart Contracts', () => {
|
||||||
res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
|
res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
|
||||||
users = res.payload;
|
users = res.payload;
|
||||||
|
|
||||||
assert.equal(users[0].$loki, 6);
|
assert.equal(users[0]._id, 6);
|
||||||
assert.equal(users[4].$loki, 10);
|
assert.equal(users[4]._id, 10);
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
contract: 'usersContract',
|
contract: 'usersContract',
|
||||||
|
@ -631,8 +714,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should read the records from a smart contract table using an index ascending', (done) => {
|
it('should read the records from a smart contract table using an index ascending', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -699,8 +781,8 @@ describe('Smart Contracts', () => {
|
||||||
let res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
|
let res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
|
||||||
let users = res.payload;
|
let users = res.payload;
|
||||||
|
|
||||||
assert.equal(users[0].$loki, 6);
|
assert.equal(users[0]._id, 6);
|
||||||
assert.equal(users[4].$loki, 2);
|
assert.equal(users[4]._id, 2);
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
contract: 'usersContract',
|
contract: 'usersContract',
|
||||||
|
@ -714,8 +796,8 @@ describe('Smart Contracts', () => {
|
||||||
res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
|
res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
|
||||||
users = res.payload;
|
users = res.payload;
|
||||||
|
|
||||||
assert.equal(users[0].$loki, 10);
|
assert.equal(users[0]._id, 10);
|
||||||
assert.equal(users[4].$loki, 5);
|
assert.equal(users[4]._id, 5);
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
contract: 'usersContract',
|
contract: 'usersContract',
|
||||||
|
@ -742,8 +824,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should read the records from a smart contract table using an index descending', (done) => {
|
it('should read the records from a smart contract table using an index descending', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -809,8 +890,8 @@ describe('Smart Contracts', () => {
|
||||||
let res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
|
let res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
|
||||||
let users = res.payload;
|
let users = res.payload;
|
||||||
|
|
||||||
assert.equal(users[0].$loki, 5);
|
assert.equal(users[0]._id, 5);
|
||||||
assert.equal(users[4].$loki, 10);
|
assert.equal(users[4]._id, 10);
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
contract: 'usersContract',
|
contract: 'usersContract',
|
||||||
|
@ -824,8 +905,8 @@ describe('Smart Contracts', () => {
|
||||||
res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
|
res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
|
||||||
users = res.payload;
|
users = res.payload;
|
||||||
|
|
||||||
assert.equal(users[0].$loki, 2);
|
assert.equal(users[0]._id, 2);
|
||||||
assert.equal(users[4].$loki, 6);
|
assert.equal(users[4]._id, 6);
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
contract: 'usersContract',
|
contract: 'usersContract',
|
||||||
|
@ -852,8 +933,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should allow only the owner of the smart contract to perform certain actions', (done) => {
|
it('should allow only the owner of the smart contract to perform certain actions', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -934,8 +1014,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should perform a search in a smart contract table from another smart contract', (done) => {
|
it('should perform a search in a smart contract table from another smart contract', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -1027,8 +1106,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should execute a smart contract from another smart contract', (done) => {
|
it('should execute a smart contract from another smart contract', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -1120,8 +1198,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should emit an event from a smart contract', (done) => {
|
it('should emit an event from a smart contract', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -1176,8 +1253,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should emit an event from another smart contract', (done) => {
|
it('should emit an event from another smart contract', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -1255,8 +1331,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should log an error during the deployment of a smart contract if an error is thrown', (done) => {
|
it('should log an error during the deployment of a smart contract if an error is thrown', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -1310,8 +1385,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should log an error during the execution of a smart contract if an error is thrown', (done) => {
|
it('should log an error during the execution of a smart contract if an error is thrown', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -1368,8 +1442,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should log an error from another smart contract', (done) => {
|
it('should log an error from another smart contract', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -1445,8 +1518,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should generate random numbers in a deterministic way', (done) => {
|
it('should generate random numbers in a deterministic way', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
@ -1538,8 +1610,7 @@ describe('Smart Contracts', () => {
|
||||||
|
|
||||||
it('should update a smart contract', (done) => {
|
it('should update a smart contract', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GENERATE_GENESIS_BLOCK, payload: conf });
|
||||||
|
|
107
test/tokens.js
107
test/tokens.js
|
@ -4,10 +4,11 @@ const assert = require('assert');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const BigNumber = require('bignumber.js');
|
const BigNumber = require('bignumber.js');
|
||||||
const { Base64 } = require('js-base64');
|
const { Base64 } = require('js-base64');
|
||||||
|
const { MongoClient } = require('mongodb');
|
||||||
|
|
||||||
|
|
||||||
const database = require('../plugins/Database');
|
const database = require('../plugins/Database');
|
||||||
const blockchain = require('../plugins/Blockchain');
|
const blockchain = require('../plugins/Blockchain');
|
||||||
const { Block } = require('../libs/Block');
|
|
||||||
const { Transaction } = require('../libs/Transaction');
|
const { Transaction } = require('../libs/Transaction');
|
||||||
|
|
||||||
const { CONSTANTS } = require('../libs/Constants');
|
const { CONSTANTS } = require('../libs/Constants');
|
||||||
|
@ -21,16 +22,14 @@ const conf = {
|
||||||
databaseFileName: "database.db",
|
databaseFileName: "database.db",
|
||||||
autosaveInterval: 0,
|
autosaveInterval: 0,
|
||||||
javascriptVMTimeout: 10000,
|
javascriptVMTimeout: 10000,
|
||||||
|
databaseURL: "mongodb://localhost:27017",
|
||||||
|
databaseName: "testssc",
|
||||||
};
|
};
|
||||||
|
|
||||||
let plugins = {};
|
let plugins = {};
|
||||||
let jobs = new Map();
|
let jobs = new Map();
|
||||||
let currentJobId = 0;
|
let currentJobId = 0;
|
||||||
|
|
||||||
function cleanDataFolder() {
|
|
||||||
fs.emptyDirSync(conf.dataDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
function send(pluginName, from, message) {
|
function send(pluginName, from, message) {
|
||||||
const plugin = plugins[pluginName];
|
const plugin = plugins[pluginName];
|
||||||
const newMessage = {
|
const newMessage = {
|
||||||
|
@ -113,11 +112,55 @@ let contractPayload = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// tokens
|
// tokens
|
||||||
describe('Tokens smart contract', () => {
|
describe('Tokens smart contract', function () {
|
||||||
|
this.timeout(10000);
|
||||||
|
|
||||||
|
before((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
client = await MongoClient.connect(conf.databaseURL, { useNewUrlParser: true });
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
await db.dropDatabase();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
after((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await client.close();
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach((done) => {
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
db = await client.db(conf.databaseName);
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach((done) => {
|
||||||
|
// runs after each test in this block
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
await db.dropDatabase()
|
||||||
|
resolve();
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
it('creates a token', (done) => {
|
it('creates a token', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -170,8 +213,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('generates error when trying to create a token with wrong parameters', (done) => {
|
it('generates error when trying to create a token with wrong parameters', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -227,8 +269,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('updates the url of a token', (done) => {
|
it('updates the url of a token', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -289,8 +330,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('does not update the url of a token', (done) => {
|
it('does not update the url of a token', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -361,8 +401,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('updates the metadata of a token', (done) => {
|
it('updates the metadata of a token', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -425,8 +464,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('transfers the ownership of a token', (done) => {
|
it('transfers the ownership of a token', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -504,8 +542,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('does not transfer the ownership of a token', (done) => {
|
it('does not transfer the ownership of a token', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -593,8 +630,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('issues tokens', (done) => {
|
it('issues tokens', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -660,8 +696,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('generates error when trying to issue tokens with wrong parameters', (done) => {
|
it('generates error when trying to issue tokens with wrong parameters', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -716,8 +751,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('transfers tokens', (done) => {
|
it('transfers tokens', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -819,8 +853,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('generates errors when trying to transfer tokens with wrong parameters', (done) => {
|
it('generates errors when trying to transfer tokens with wrong parameters', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -878,8 +911,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('transfers tokens to a contract', (done) => {
|
it('transfers tokens to a contract', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -972,8 +1004,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('generates errors when trying to transfer tokens to a contract with wrong parameters', (done) => {
|
it('generates errors when trying to transfer tokens to a contract with wrong parameters', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1031,8 +1062,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('transfers tokens from a contract to a user', (done) => {
|
it('transfers tokens from a contract to a user', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1132,8 +1162,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('generates errors when trying to transfer tokens from a contract to a user with wrong parameters', (done) => {
|
it('generates errors when trying to transfer tokens from a contract to a user with wrong parameters', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1233,8 +1262,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('transfers tokens from a contract to a contract', (done) => {
|
it('transfers tokens from a contract to a contract', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
@ -1348,8 +1376,7 @@ describe('Tokens smart contract', () => {
|
||||||
|
|
||||||
it('generates errors when trying to transfer tokens from a contract to another contract with wrong parameters', (done) => {
|
it('generates errors when trying to transfer tokens from a contract to another contract with wrong parameters', (done) => {
|
||||||
new Promise(async (resolve) => {
|
new Promise(async (resolve) => {
|
||||||
cleanDataFolder();
|
|
||||||
|
|
||||||
await loadPlugin(database);
|
await loadPlugin(database);
|
||||||
await loadPlugin(blockchain);
|
await loadPlugin(blockchain);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue