moving to mongodb storage

This commit is contained in:
harpagon210 2019-06-03 18:36:11 -05:00
parent 43d54bbff0
commit db1e554d37
17 changed files with 1555 additions and 447 deletions

View File

@ -1,3 +1,4 @@
services: mongodb
language: node_js
node_js:
- "10.5"

View File

@ -1,6 +1,8 @@
{
"chainId": "00000000000000000002",
"chainId": "mainnet1",
"rpcNodePort": 5000,
"databaseURL": "mongodb://localhost:27017",
"databaseName": "ssc",
"dataDirectory": "./data/",
"databaseFileName": "database.db",
"blocksLogFilePath": "./blocks.log",
@ -12,6 +14,6 @@
"https://rpc.steemviz.com",
"https://steemd.minnowsupportproject.org"
],
"startSteemBlock": 29056257,
"genesisSteemBlock": 29056257
"startSteemBlock": 29864752,
"genesisSteemBlock": 29862600
}

View File

@ -80,7 +80,6 @@ class Block {
for (let i = 0; i < nbTransactions; i += 1) {
const transaction = this.transactions[i];
await this.processTransaction(ipc, jsVMTimeout, transaction, currentDatabaseHash); // eslint-disable-line
currentDatabaseHash = transaction.databaseHash;

View File

@ -1,19 +1,20 @@
const CONSTANTS = {
// mainnet
/*
UTILITY_TOKEN_SYMBOL: 'ENG',
STEEM_PEGGED_ACCOUNT: 'steem-peg',
INITIAL_TOKEN_CREATION_FEE: '100',
SSC_STORE_QTY: '0.001',
*/
// testnet
/*
UTILITY_TOKEN_SYMBOL: 'SSC',
STEEM_PEGGED_ACCOUNT: 'steemsc',
INITIAL_TOKEN_CREATION_FEE: '0',
SSC_STORE_QTY: '1',
*/
UTILITY_TOKEN_PRECISION: 8,
STEEM_PEGGED_SYMBOL: 'STEEMP',

View File

@ -175,7 +175,7 @@ class SmartContracts {
}
const newContract = {
name,
_id: name,
owner: finalSender,
code: codeTemplate,
codeHash: SHA256(codeTemplate).toString(enchex),

68
package-lock.json generated
View File

@ -261,6 +261,11 @@
"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": {
"version": "1.0.3",
"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",
"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": {
"version": "1.0.1",
"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": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@ -2133,6 +2164,22 @@
"integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
"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": {
"version": "1.8.1",
"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",
"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": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.5.2.tgz",
@ -2243,8 +2299,7 @@
"semver": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
"dev": true
"integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg=="
},
"send": {
"version": "0.16.2",
@ -2345,6 +2400,15 @@
"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": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz",

View File

@ -25,6 +25,7 @@
"js-base64": "^2.5.1",
"line-by-line": "^0.1.6",
"lokijs": "^1.5.6",
"mongodb": "^3.2.6",
"node-cleanup": "^2.1.2",
"read-last-lines": "^1.6.0",
"seedrandom": "^3.0.1",

View File

@ -1,9 +1,8 @@
const fs = require('fs-extra');
const Loki = require('lokijs');
/* eslint-disable no-await-in-loop */
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 { MongoClient } = require('mongodb');
const { IPC } = require('../libs/IPC');
const BC_PLUGIN_NAME = require('./Blockchain.constants').PLUGIN_NAME;
@ -22,61 +21,75 @@ let chain = null;
let saving = false;
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
async function init(conf, callback) {
const init = async (conf, callback) => {
const {
autosaveInterval,
databaseFileName,
dataDirectory,
databaseURL,
databaseName,
} = conf;
const databaseFilePath = dataDirectory + databaseFileName;
// init the database
database = new Loki(databaseFilePath, {
adapter: new lfsa(), // eslint-disable-line new-cap
autosave: autosaveInterval > 0,
autosaveInterval,
});
const client = await MongoClient.connect(databaseURL, { useNewUrlParser: true });
database = await client.db(databaseName);
// await database.dropDatabase();
// return
// get the chain collection and init the chain if not done yet
// 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);
}
const coll = await getCollection('chain');
// 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');
}
if (coll === null) {
await initSequence('chain', 0);
chain = await database.createCollection('chain');
callback(null);
});
await database.createCollection('transactions');
await database.createCollection('contracts');
} 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);
chain = coll;
}
}
callback(null);
};
async function generateGenesisBlock(conf, callback) {
const generateGenesisBlock = async (conf, callback) => {
const {
chainId,
genesisSteemBlock,
} = conf;
// check if genesis block hasn't been generated already
const genBlock = actions.getBlockInfo(0);
let genBlock = await actions.getBlockInfo(0);
if (!genBlock) {
// 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
// BlockProduction.initialize(database, genesisSteemBlock);
genBlock._id = await getNextSequence('chain'); // eslint-disable-line no-underscore-dangle
await chain.insertOne(genBlock);
}
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) {
const stop = (callback) => {
actions.save(callback);
}
};
function addTransactions(block) {
const transactionsTable = database.getCollection('transactions');
const addTransactions = async (block) => {
const transactionsTable = database.collection('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,
_id: transaction.transactionId,
blockNumber: block.blockNumber,
index,
};
transactionsTable.insert(transactionToSave);
await transactionsTable.insertOne(transactionToSave); // eslint-disable-line no-await-in-loop
}
}
};
function updateTableHash(contract, table, record) {
const contracts = database.getCollection('contracts');
const contractInDb = contracts.findOne({ name: contract });
const updateTableHash = async (contract, table, record) => {
const contracts = database.collection('contracts');
const contractInDb = await contracts.findOne({ _id: contract });
if (contractInDb && contractInDb.tables[table] !== undefined) {
const recordHash = SHA256(JSON.stringify(record)).toString(enchex);
@ -146,125 +153,159 @@ function updateTableHash(contract, table, record) {
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);
}
}
actions.initDatabaseHash = (previousDatabaseHash) => {
databaseHash = previousDatabaseHash;
};
actions.getDatabaseHash = () => databaseHash;
actions.initDatabaseHash = (previousDatabaseHash, callback) => {
databaseHash = previousDatabaseHash;
callback();
};
actions.getTransactionInfo = (txid) => { // eslint-disable-line no-unused-vars
const transactionsTable = database.getCollection('transactions');
actions.getDatabaseHash = (payload, callback) => {
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) {
const { index, blockNumber } = transaction;
const block = actions.getBlockInfo(blockNumber);
const block = await actions.getBlockInfo(blockNumber);
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
chain.insert(block);
addTransactions(block);
actions.addBlock = async (block, callback) => {
const finalBlock = 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
const { maxId } = chain;
return chain.get(maxId);
actions.getLatestBlockInfo = async (payload, callback) => {
const _idNewBlock = await getLastSequence('chain'); // eslint-disable-line no-underscore-dangle
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...)
* @param {String} contract name of the contract
* @returns {Object} returns the contract info if it exists, null otherwise
*/
actions.findContract = (payload) => {
actions.findContract = async (payload, callback) => {
const { name } = payload;
if (name && typeof name === 'string') {
const contracts = database.getCollection('contracts');
const contractInDb = contracts.findOne({ name });
const contracts = database.collection('contracts');
const contractInDb = await contracts.findOne({ _id: name });
if (contractInDb) {
if (callback) {
callback(contractInDb);
}
return contractInDb;
}
}
if (callback) {
callback(null);
}
return null;
};
/**
* 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} code code of 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 {
name,
_id,
owner,
code,
tables,
} = payload;
if (name && typeof name === 'string'
if (_id && typeof _id === 'string'
&& owner && typeof owner === 'string'
&& code && typeof code === 'string'
&& tables && typeof tables === 'object') {
const contracts = database.getCollection('contracts');
contracts.insert(payload);
const contracts = database.collection('contracts');
await contracts.insertOne(payload);
}
callback();
};
/**
* 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} code code of 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 {
name,
_id,
owner,
code,
tables,
} = payload;
if (name && typeof name === 'string'
if (_id && typeof _id === 'string'
&& owner && typeof owner === 'string'
&& code && typeof code === 'string'
&& tables && typeof tables === 'object') {
const contracts = database.getCollection('contracts');
const contracts = database.collection('contracts');
if (contracts.findOne({ name, owner }) !== null) {
contracts.update(payload);
if (contracts.findOne({ _id, owner }) !== null) {
await contracts.updateOne({ _id }, { $set: payload });
}
}
callback();
};
/**
* 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
actions.createTable = async (payload, callback) => { // eslint-disable-line no-unused-vars
const { contractName, tableName, indexes } = payload;
let result = false;
// check that the params are correct
// each element of the indexes array have to be a string if defined
@ -274,15 +315,30 @@ actions.createTable = (payload) => { // eslint-disable-line no-unused-vars
|| (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);
let table = await 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;
await initSequence(finalTableName);
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);
}
}
return false;
result = true;
}
}
callback(result);
};
/**
@ -328,10 +384,10 @@ actions.find = (payload) => { // eslint-disable-line no-unused-vars
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')) {
if (ind.length > 0 && ind.every(el => tableData.binaryIndices[el.index] !== undefined || el.index === '$loki' || el.index === '_id')) {
return tableData.chain()
.find(query)
.compoundsort(ind.map(el => [el.index, el.descending]))
.compoundsort(ind.map(el => [el.index === '$loki' ? '_id' : el.index, el.descending]))
.offset(off)
.limit(lim)
.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
* @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
* @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 {
const { contract, table, query } = payload;
let result = null;
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;
const tableData = await getCollection(finalTableName);
if (tableData) {
result = await tableData.findOne(query);
}
}
return null;
callback(result);
} 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} 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 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) {
const tableInDb = database.getCollection(finalTableName);
const tableInDb = await getCollection(finalTableName);
if (tableInDb) {
const result = tableInDb.insert(record);
updateTableHash(contract, finalTableName, result);
return result;
finalRecord = record;
finalRecord._id = await getNextSequence(finalTableName); // eslint-disable-line
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} 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 finalTableName = `${contract}_${table}`;
const contractInDb = actions.findContract({ name: contract });
const contractInDb = await actions.findContract({ name: contract });
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
const tableInDb = database.getCollection(finalTableName);
const tableInDb = await getCollection(finalTableName);
if (tableInDb) {
updateTableHash(contract, finalTableName, record);
tableInDb.remove(record);
await updateTableHash(contract, finalTableName, 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} 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 finalTableName = `${contract}_${table}`;
const contractInDb = actions.findContract({ name: contract });
const contractInDb = await actions.findContract({ name: contract });
if (contractInDb && contractInDb.tables[finalTableName] !== undefined) {
const tableInDb = database.getCollection(finalTableName);
const tableInDb = await getCollection(finalTableName);
if (tableInDb) {
updateTableHash(contract, finalTableName, record);
tableInDb.update(record);
await updateTableHash(contract, finalTableName, 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
* @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 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) {
const tableInDb = database.getCollection(finalTableName);
const tableInDb = await getCollection(finalTableName);
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
* @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 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) {
const tableInDb = database.getCollection(finalTableName);
const tableInDb = await getCollection(finalTableName);
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 }
* @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 {
table,
query,
@ -503,27 +631,18 @@ actions.dfind = (payload) => { // eslint-disable-line no-unused-vars
const off = offset || 0;
const ind = indexes || [];
const tableData = database.getCollection(table);
const tableInDb = await getCollection(table);
let records = [];
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();
if (tableInDb) {
records = await tableInDb.find(query, {
limit: lim,
skip: off,
sort: ind.map(el => [el.index, el.descending === true ? 'desc' : 'asc']),
});
}
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
* @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 tableData = database.getCollection(table);
if (tableData) {
return tableData.findOne(query);
const tableInDb = await getCollection(table);
let record = null;
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} 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 tableInDb = database.getCollection(table);
updateTableHash(table.split('_')[0], table.split('_')[1], record);
return tableInDb.insert(record);
const tableInDb = database.collection(table);
const finalRecord = 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} 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 tableInDb = database.getCollection(table);
updateTableHash(table.split('_')[0], table.split('_')[1], record);
tableInDb.update(record);
const tableInDb = database.collection(table);
await updateTableHash(table.split('_')[0], table.split('_')[1], 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} 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 tableInDb = database.getCollection(table);
updateTableHash(table.split('_')[0], table.split('_')[1], record);
tableInDb.remove(record);
const tableInDb = database.collection(table);
await updateTableHash(table.split('_')[0], table.split('_')[1], record);
await tableInDb.deleteOne({ _id: record._id }); // eslint-disable-line no-underscore-dangle
callback();
};
ipc.onReceiveMessage((message) => {
@ -609,9 +740,10 @@ ipc.onReceiveMessage((message) => {
});
} else if (action && typeof actions[action] === 'function') {
if (!saving) {
const res = actions[action](payload);
actions[action](payload, (res) => {
// console.log('action', action, 'res', res, 'payload', payload);
ipc.reply(message, res);
});
} else {
ipc.reply(message);
}

625
plugins/Database.loki.js Normal file
View File

@ -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;

15
scripts/cleanDB.sh Normal file
View File

@ -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"

View File

@ -1,13 +1,13 @@
/* eslint-disable */
const { fork } = require('child_process');
const assert = require('assert');
const fs = require('fs-extra');
const database = require('../plugins/Database');
const blockchain = require('../plugins/Blockchain');
const { Block } = require('../libs/Block');
const { Transaction } = require('../libs/Transaction');
const { CONSTANTS } = require('../libs/Constants');
const { MongoClient } = require('mongodb');
//process.env.NODE_ENV = 'test';
@ -18,16 +18,14 @@ const conf = {
databaseFileName: "database.db",
autosaveInterval: 0,
javascriptVMTimeout: 10000,
databaseURL: "mongodb://localhost:27017",
databaseName: "testssc",
};
let plugins = {};
let jobs = new Map();
let currentJobId = 0;
function cleanDataFolder() {
fs.emptyDirSync(conf.dataDirectory);
}
function send(pluginName, from, message) {
const plugin = plugins[pluginName];
const newMessage = {
@ -96,10 +94,54 @@ const unloadPlugin = (plugin) => {
}
// 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) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -140,7 +182,6 @@ describe.skip('dice', () => {
it('makes you lose', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);

View File

@ -2,16 +2,14 @@
const { fork } = require('child_process');
const assert = require('assert');
const fs = require('fs-extra');
const { MongoClient } = require('mongodb');
const database = require('../plugins/Database');
const blockchain = require('../plugins/Blockchain');
const { Block } = require('../libs/Block');
const { Transaction } = require('../libs/Transaction');
const { CONSTANTS } = require('../libs/Constants');
//process.env.NODE_ENV = 'test';
const conf = {
chainId: "test-chain-id",
genesisSteemBlock: 2000000,
@ -19,16 +17,14 @@ const conf = {
databaseFileName: "database.db",
autosaveInterval: 0,
javascriptVMTimeout: 10000,
databaseURL: "mongodb://localhost:27017",
databaseName: "testssc",
};
let plugins = {};
let jobs = new Map();
let currentJobId = 0;
function cleanDataFolder() {
fs.emptyDirSync(conf.dataDirectory);
}
function send(pluginName, from, message) {
const plugin = plugins[pluginName];
const newMessage = {
@ -132,10 +128,54 @@ let mktContractPayload = {
};
// 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) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -224,7 +264,6 @@ describe('Market', () => {
it('creates buy orders with expirations', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -265,11 +304,11 @@ describe('Market', () => {
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].symbol, 'TKN');
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].expiration, 1527811200 + 2592000);
@ -281,11 +320,11 @@ describe('Market', () => {
assert.equal(sellOrders[1].timestamp, 1527811200);
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].symbol, 'TKN');
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].expiration, 1527811200 + 2592000);
@ -300,7 +339,6 @@ describe('Market', () => {
it('generates error when trying to create a buy order with wrong parameters', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -345,7 +383,6 @@ describe('Market', () => {
it('creates sell orders with expirations', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -386,11 +423,11 @@ describe('Market', () => {
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].symbol, 'TKN');
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].expiration, 1527811200 + 2592000);
@ -402,11 +439,11 @@ describe('Market', () => {
assert.equal(sellOrders[1].timestamp, 1527811200);
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].symbol, 'TKN');
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].expiration, 1527811200 + 2592000);
@ -421,7 +458,6 @@ describe('Market', () => {
it('creates a sell order', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -510,7 +546,6 @@ describe('Market', () => {
it('generates error when trying to create a sell order with wrong parameters', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -556,7 +591,6 @@ describe('Market', () => {
it('cancels a buy order', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -692,7 +726,6 @@ describe('Market', () => {
it('cancels a sell order', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -831,7 +864,6 @@ describe('Market', () => {
it('buys from the market from one seller', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -871,6 +903,7 @@ describe('Market', () => {
});
let balances = res.payload;
balances.sort((a, b) => a._id - b._id);
assert.equal(balances[0].account, 'vitalik');
assert.equal(balances[0].symbol, 'TKN');
@ -936,7 +969,6 @@ describe('Market', () => {
it('buys from the market from several sellers', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -980,6 +1012,7 @@ describe('Market', () => {
});
const balances = res.payload;
balances.sort((a, b) => a._id - b._id);
assert.equal(balances[0].account, 'harpagon');
assert.equal(balances[0].symbol, 'STEEMP');
@ -1024,7 +1057,6 @@ describe('Market', () => {
it('buys from the market partially', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1068,6 +1100,7 @@ describe('Market', () => {
});
let balances = res.payload;
balances.sort((a, b) => a._id - b._id);
assert.equal(balances[0].account, 'harpagon');
assert.equal(balances[0].symbol, 'STEEMP');
@ -1150,7 +1183,6 @@ describe('Market', () => {
it('sells on the market to one buyer', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1193,6 +1225,7 @@ describe('Market', () => {
});
let balances = res.payload;
balances.sort((a, b) => a._id - b._id);
assert.equal(balances[0].account, 'vitalik');
assert.equal(balances[0].symbol, 'TKN');
@ -1258,7 +1291,6 @@ describe('Market', () => {
it('sells on the market to several buyers', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1306,6 +1338,7 @@ describe('Market', () => {
});
const balances = res.payload;
balances.sort((a, b) => a._id - b._id);
assert.equal(balances[0].account, 'harpagon');
assert.equal(balances[0].symbol, 'TKN');
@ -1350,7 +1383,6 @@ describe('Market', () => {
it('fills a buy order from different sellers', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1398,6 +1430,7 @@ describe('Market', () => {
});
const balances = res.payload;
balances.sort((a, b) => a._id - b._id);
assert.equal(balances[0].account, 'harpagon');
assert.equal(balances[0].symbol, 'STEEMP');
@ -1442,7 +1475,6 @@ describe('Market', () => {
it('creates a trade history', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1632,7 +1664,6 @@ describe('Market', () => {
it('maintains the different metrics', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1819,7 +1850,6 @@ describe('Market', () => {
it('removes an expired sell order', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1926,7 +1956,6 @@ describe('Market', () => {
it('removes an expired buy order', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -2032,7 +2061,6 @@ describe('Market', () => {
it('removes dust sell orders', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -2072,6 +2100,7 @@ describe('Market', () => {
});
let balances = res.payload;
balances.sort((a, b) => a._id - b._id);
assert.equal(balances[0].account, 'vitalik');
assert.equal(balances[0].symbol, 'TKN');
@ -2149,6 +2178,7 @@ describe('Market', () => {
});
balances = res.payload;
balances.sort((a, b) => a._id - b._id);
assert.equal(balances[0].account, 'vitalik');
assert.equal(balances[0].symbol, 'TKN');
@ -2210,7 +2240,6 @@ describe('Market', () => {
it('removes dust buy orders', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -2253,6 +2282,7 @@ describe('Market', () => {
});
let balances = res.payload;
balances.sort((a, b) => a._id - b._id);
assert.equal(balances[0].account, 'vitalik');
assert.equal(balances[0].symbol, 'TKN');
@ -2330,6 +2360,7 @@ describe('Market', () => {
});
balances = res.payload;
balances.sort((a, b) => a._id - b._id);
assert.equal(balances[0].account, 'vitalik');
assert.equal(balances[0].symbol, 'TKN');

View File

@ -2,18 +2,15 @@
const { fork } = require('child_process');
const assert = require('assert');
const fs = require('fs-extra');
const BigNumber = require('bignumber.js');
const { MongoClient } = require('mongodb');
const { Base64 } = require('js-base64');
const database = require('../plugins/Database');
const blockchain = require('../plugins/Blockchain');
const { Block } = require('../libs/Block');
const { Transaction } = require('../libs/Transaction');
const { CONSTANTS } = require('../libs/Constants');
//process.env.NODE_ENV = 'test';
const conf = {
chainId: "test-chain-id",
genesisSteemBlock: 2000000,
@ -21,16 +18,14 @@ const conf = {
databaseFileName: "database.db",
autosaveInterval: 0,
javascriptVMTimeout: 10000,
databaseURL: "mongodb://localhost:27017",
databaseName: "testssc",
};
let plugins = {};
let jobs = new Map();
let currentJobId = 0;
function cleanDataFolder() {
fs.emptyDirSync(conf.dataDirectory);
}
function send(pluginName, from, message) {
const plugin = plugins[pluginName];
const newMessage = {
@ -117,9 +112,51 @@ let contractPayload = {
describe('smart tokens', function () {
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) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -174,7 +211,6 @@ describe('smart tokens', function () {
it('should not enable delegation', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -228,7 +264,6 @@ describe('smart tokens', function () {
it('should delegate tokens', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -334,6 +369,7 @@ describe('smart tokens', function () {
});
balances = res.payload;
balances.sort((a, b) => a._id - b._id);
assert.equal(balances[0].symbol, 'TKN');
assert.equal(balances[0].account, 'satoshi');
@ -369,12 +405,12 @@ describe('smart tokens', function () {
assert.equal(delegations[0].symbol, 'TKN');
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[1].symbol, 'TKN');
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');
resolve();
@ -388,7 +424,6 @@ describe('smart tokens', function () {
it('should not delegate tokens', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -491,7 +526,6 @@ describe('smart tokens', function () {
it('should undelegate tokens', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -534,6 +568,8 @@ describe('smart tokens', function () {
});
let balances = res.payload;
balances.sort((a, b) => a._id - b._id);
assert.equal(balances[0].symbol, 'TKN');
assert.equal(balances[0].account, 'satoshi');
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].from, 'satoshi');
assert.equal(delegations[0].to, 'ned');
assert.equal(delegations[0].quantity, '0.00000001');
assert.equal(delegations[0].to, 'vitalik');
assert.equal(delegations[0].quantity, '0.00000002');
assert.equal(delegations[1].symbol, 'TKN');
assert.equal(delegations[1].from, 'satoshi');
assert.equal(delegations[1].to, 'vitalik');
assert.equal(delegations[1].quantity, '0.00000002');
assert.equal(delegations[1].to, 'ned');
assert.equal(delegations[1].quantity, '0.00000001');
transactions = [];
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.sort((a, b) => a._id - b._id);
assert.equal(balances[0].symbol, 'TKN');
assert.equal(balances[0].account, 'satoshi');
@ -682,7 +719,6 @@ describe('smart tokens', function () {
it('should not undelegate tokens', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -751,7 +787,6 @@ describe('smart tokens', function () {
it('should process the pending undelegations', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -870,7 +905,6 @@ describe('smart tokens', function () {
it('should enable staking', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -922,7 +956,6 @@ describe('smart tokens', function () {
it('should not enable staking', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -988,7 +1021,6 @@ describe('smart tokens', function () {
it('should not enable staking again', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1050,7 +1082,6 @@ describe('smart tokens', function () {
it('should stake tokens', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1180,7 +1211,6 @@ describe('smart tokens', function () {
it('should not stake tokens', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1252,7 +1282,6 @@ describe('smart tokens', function () {
it('should start the unstake process', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1363,7 +1392,6 @@ describe('smart tokens', function () {
it('should not start the unstake process', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1433,7 +1461,6 @@ describe('smart tokens', function () {
it('should cancel an unstake', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1592,7 +1619,6 @@ describe('smart tokens', function () {
it('should not cancel an unstake', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1757,7 +1783,6 @@ describe('smart tokens', function () {
it('should process the pending unstakes', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1962,7 +1987,6 @@ describe('smart tokens', function () {
it.skip('should process thousands of pending unstakes', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -2236,7 +2260,6 @@ describe('smart tokens', function () {
it('should process the pending unstakes (with multi transactions)', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);

View File

@ -2,17 +2,14 @@
const { fork } = require('child_process');
const assert = require('assert');
const fs = require('fs-extra');
const BigNumber = require('bignumber.js');
const { MongoClient } = require('mongodb');
const database = require('../plugins/Database');
const blockchain = require('../plugins/Blockchain');
const { Block } = require('../libs/Block');
const { Transaction } = require('../libs/Transaction');
const { CONSTANTS } = require('../libs/Constants');
//process.env.NODE_ENV = 'test';
const conf = {
chainId: "test-chain-id",
genesisSteemBlock: 2000000,
@ -20,16 +17,14 @@ const conf = {
databaseFileName: "database.db",
autosaveInterval: 0,
javascriptVMTimeout: 10000,
databaseURL: "mongodb://localhost:27017",
databaseName: "testssc",
};
let plugins = {};
let jobs = new Map();
let currentJobId = 0;
function cleanDataFolder() {
fs.emptyDirSync(conf.dataDirectory);
}
function send(pluginName, from, message) {
const plugin = plugins[pluginName];
const newMessage = {
@ -98,11 +93,54 @@ const unloadPlugin = (plugin) => {
}
// 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) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -149,7 +187,6 @@ describe('sscstore smart contract', () => {
it('should not buy tokens', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -227,7 +264,6 @@ describe('sscstore smart contract', () => {
it('should update params', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -275,7 +311,6 @@ describe('sscstore smart contract', () => {
it('should not update params', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);

View File

@ -2,10 +2,10 @@
const { fork } = require('child_process');
const assert = require('assert');
const fs = require('fs-extra');
const { MongoClient } = require('mongodb');
const database = require('../plugins/Database');
const blockchain = require('../plugins/Blockchain');
const { Block } = require('../libs/Block');
const { Transaction } = require('../libs/Transaction');
const { CONSTANTS } = require('../libs/Constants');
@ -19,16 +19,14 @@ const conf = {
databaseFileName: "database.db",
autosaveInterval: 0,
javascriptVMTimeout: 10000,
databaseURL: "mongodb://localhost:27017",
databaseName: "testssc",
};
let plugins = {};
let jobs = new Map();
let currentJobId = 0;
function cleanDataFolder() {
fs.emptyDirSync(conf.dataDirectory);
}
function send(pluginName, from, message) {
const plugin = plugins[pluginName];
const newMessage = {
@ -122,10 +120,54 @@ let spContractPayload = {
};
// 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) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -182,7 +224,6 @@ describe('Steem Pegged', () => {
it('withdraws STEEM', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -290,7 +331,6 @@ describe('Steem Pegged', () => {
it('does not withdraw STEEM', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);

View File

@ -2,15 +2,13 @@
const { fork } = require('child_process');
const assert = require('assert');
const { Base64 } = require('js-base64');
const fs = require('fs-extra');
const { MongoClient } = require('mongodb');
const database = require('../plugins/Database');
const blockchain = require('../plugins/Blockchain');
const { Block } = require('../libs/Block');
const { Transaction } = require('../libs/Transaction');
//process.env.NODE_ENV = 'test';
const conf = {
chainId: "test-chain-id",
genesisSteemBlock: 2000000,
@ -18,16 +16,14 @@ const conf = {
databaseFileName: "database.db",
autosaveInterval: 0,
javascriptVMTimeout: 10000,
databaseURL: "mongodb://localhost:27017",
databaseName: "testssc",
};
let plugins = {};
let jobs = new Map();
let currentJobId = 0;
function cleanDataFolder() {
fs.emptyDirSync(conf.dataDirectory);
}
function send(pluginName, from, message) {
const plugin = plugins[pluginName];
const newMessage = {
@ -96,11 +92,55 @@ const unloadPlugin = (plugin) => {
}
// 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) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -119,7 +159,6 @@ describe('Database', () => {
it('should get the latest block', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -168,10 +207,54 @@ describe('Database', () => {
});
// 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) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -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 contract = res.payload;
assert.equal(contract.name, 'testContract');
assert.equal(contract._id, 'testContract');
assert.equal(contract.owner, 'steemsc');
resolve()
})
@ -220,7 +303,6 @@ describe('Smart Contracts', () => {
it('should create a table during the smart contract deployment', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -262,7 +344,6 @@ describe('Smart Contracts', () => {
res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.GET_TABLE_DETAILS, payload: { contract: 'testContract', table: 'testTable' } });
assert.notEqual(res.payload, null);
resolve();
})
.then(() => {
@ -274,7 +355,6 @@ describe('Smart Contracts', () => {
it('should create a table with indexes during the smart contract deployment', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -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 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();
})
.then(() => {
@ -325,7 +412,6 @@ describe('Smart Contracts', () => {
it('should add a record into a smart contract table', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -385,7 +471,6 @@ describe('Smart Contracts', () => {
it('should update a record from a smart contract table', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -458,7 +543,6 @@ describe('Smart Contracts', () => {
it('should remove a record from a smart contract table', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -526,7 +610,6 @@ describe('Smart Contracts', () => {
it('should read the records from a smart contract table via pagination', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -590,8 +673,8 @@ describe('Smart Contracts', () => {
let res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
let users = res.payload;
assert.equal(users[0].$loki, 1);
assert.equal(users[4].$loki, 5);
assert.equal(users[0]._id, 1);
assert.equal(users[4]._id, 5);
payload = {
contract: 'usersContract',
@ -604,8 +687,8 @@ describe('Smart Contracts', () => {
res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
users = res.payload;
assert.equal(users[0].$loki, 6);
assert.equal(users[4].$loki, 10);
assert.equal(users[0]._id, 6);
assert.equal(users[4]._id, 10);
payload = {
contract: 'usersContract',
@ -631,7 +714,6 @@ describe('Smart Contracts', () => {
it('should read the records from a smart contract table using an index ascending', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -699,8 +781,8 @@ describe('Smart Contracts', () => {
let res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
let users = res.payload;
assert.equal(users[0].$loki, 6);
assert.equal(users[4].$loki, 2);
assert.equal(users[0]._id, 6);
assert.equal(users[4]._id, 2);
payload = {
contract: 'usersContract',
@ -714,8 +796,8 @@ describe('Smart Contracts', () => {
res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
users = res.payload;
assert.equal(users[0].$loki, 10);
assert.equal(users[4].$loki, 5);
assert.equal(users[0]._id, 10);
assert.equal(users[4]._id, 5);
payload = {
contract: 'usersContract',
@ -742,7 +824,6 @@ describe('Smart Contracts', () => {
it('should read the records from a smart contract table using an index descending', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -809,8 +890,8 @@ describe('Smart Contracts', () => {
let res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
let users = res.payload;
assert.equal(users[0].$loki, 5);
assert.equal(users[4].$loki, 10);
assert.equal(users[0]._id, 5);
assert.equal(users[4]._id, 10);
payload = {
contract: 'usersContract',
@ -824,8 +905,8 @@ describe('Smart Contracts', () => {
res = await send(database.PLUGIN_NAME, 'MASTER', { action: database.PLUGIN_ACTIONS.FIND, payload });
users = res.payload;
assert.equal(users[0].$loki, 2);
assert.equal(users[4].$loki, 6);
assert.equal(users[0]._id, 2);
assert.equal(users[4]._id, 6);
payload = {
contract: 'usersContract',
@ -852,7 +933,6 @@ describe('Smart Contracts', () => {
it('should allow only the owner of the smart contract to perform certain actions', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -934,7 +1014,6 @@ describe('Smart Contracts', () => {
it('should perform a search in a smart contract table from another smart contract', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1027,7 +1106,6 @@ describe('Smart Contracts', () => {
it('should execute a smart contract from another smart contract', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1120,7 +1198,6 @@ describe('Smart Contracts', () => {
it('should emit an event from a smart contract', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1176,7 +1253,6 @@ describe('Smart Contracts', () => {
it('should emit an event from another smart contract', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1255,7 +1331,6 @@ describe('Smart Contracts', () => {
it('should log an error during the deployment of a smart contract if an error is thrown', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1310,7 +1385,6 @@ describe('Smart Contracts', () => {
it('should log an error during the execution of a smart contract if an error is thrown', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1368,7 +1442,6 @@ describe('Smart Contracts', () => {
it('should log an error from another smart contract', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1445,7 +1518,6 @@ describe('Smart Contracts', () => {
it('should generate random numbers in a deterministic way', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1538,7 +1610,6 @@ describe('Smart Contracts', () => {
it('should update a smart contract', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);

View File

@ -4,10 +4,11 @@ const assert = require('assert');
const fs = require('fs-extra');
const BigNumber = require('bignumber.js');
const { Base64 } = require('js-base64');
const { MongoClient } = require('mongodb');
const database = require('../plugins/Database');
const blockchain = require('../plugins/Blockchain');
const { Block } = require('../libs/Block');
const { Transaction } = require('../libs/Transaction');
const { CONSTANTS } = require('../libs/Constants');
@ -21,16 +22,14 @@ const conf = {
databaseFileName: "database.db",
autosaveInterval: 0,
javascriptVMTimeout: 10000,
databaseURL: "mongodb://localhost:27017",
databaseName: "testssc",
};
let plugins = {};
let jobs = new Map();
let currentJobId = 0;
function cleanDataFolder() {
fs.emptyDirSync(conf.dataDirectory);
}
function send(pluginName, from, message) {
const plugin = plugins[pluginName];
const newMessage = {
@ -113,10 +112,54 @@ let contractPayload = {
};
// 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) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -170,7 +213,6 @@ describe('Tokens smart contract', () => {
it('generates error when trying to create a token with wrong parameters', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -227,7 +269,6 @@ describe('Tokens smart contract', () => {
it('updates the url of a token', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -289,7 +330,6 @@ describe('Tokens smart contract', () => {
it('does not update the url of a token', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -361,7 +401,6 @@ describe('Tokens smart contract', () => {
it('updates the metadata of a token', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -425,7 +464,6 @@ describe('Tokens smart contract', () => {
it('transfers the ownership of a token', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -504,7 +542,6 @@ describe('Tokens smart contract', () => {
it('does not transfer the ownership of a token', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -593,7 +630,6 @@ describe('Tokens smart contract', () => {
it('issues tokens', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -660,7 +696,6 @@ describe('Tokens smart contract', () => {
it('generates error when trying to issue tokens with wrong parameters', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -716,7 +751,6 @@ describe('Tokens smart contract', () => {
it('transfers tokens', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -819,7 +853,6 @@ describe('Tokens smart contract', () => {
it('generates errors when trying to transfer tokens with wrong parameters', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -878,7 +911,6 @@ describe('Tokens smart contract', () => {
it('transfers tokens to a contract', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -972,7 +1004,6 @@ describe('Tokens smart contract', () => {
it('generates errors when trying to transfer tokens to a contract with wrong parameters', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1031,7 +1062,6 @@ describe('Tokens smart contract', () => {
it('transfers tokens from a contract to a user', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1132,7 +1162,6 @@ describe('Tokens smart contract', () => {
it('generates errors when trying to transfer tokens from a contract to a user with wrong parameters', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1233,7 +1262,6 @@ describe('Tokens smart contract', () => {
it('transfers tokens from a contract to a contract', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);
@ -1348,7 +1376,6 @@ describe('Tokens smart contract', () => {
it('generates errors when trying to transfer tokens from a contract to another contract with wrong parameters', (done) => {
new Promise(async (resolve) => {
cleanDataFolder();
await loadPlugin(database);
await loadPlugin(blockchain);