'use strict';
const jayson = require('jayson');
const cors = require('cors');
const compression = require("compression");
const BeOnBlockChain = require("./BeOnBlockChain");
const Transaction = require("../lib/Transaction");
const config = require("./config");
const utils = require("../lib/utils");
const blockchain = new BeOnBlockChain(config);
/**
* Returns all the blocks or a specific block
* @param {string} [blkNum] the block number to return
* @param {string} [fromBlkNum] the from block number to return
* @param {string} [toBlkNum] the to block number to return
*/
const getBlocks = async (blkNum, fromBlkNum, toBlkNum) => {
if (blkNum != 0 && !blkNum) {
let blocks = await blockchain.db.getBlocks(false);
return blocks.map(b => b.toJSON());
}
if (fromBlkNum && toBlkNum) {
let blocks = await blockchain.db.getBlocksInRange(fromBlkNum, toBlkNum);
return blocks.map(b => b.toJSON());
}
blkNum = blkNum || 0;
let block = await blockchain.db.getBlock(blkNum);
if (block) {
return [block.toJSON()];
}
return [];
}
/**
* Returns the latest block
*/
const getLatestBlock = async () => {
let block = await blockchain.db.getLatestBlock();
return block.toJSON();
}
/**
* Returns current utxos
*/
const getUTXOs = async () => {
return blockchain.getUTXO()
}
/**
* Returns current pending txpools
*/
const getPendingTxs = async () => {
return blockchain.getTXPool()
}
const getPendingExits = async () => {
return blockchain.db.getPendingExits()
}
const getUTXOsByAddress = async (address) => {
return blockchain.db.getUTXOsByAddress(address);
}
const getTxByHash = async (hash) => {
return blockchain.db.getTxByHash(hash);
}
const getBlockByTxHash = async (hash) => {
return blockchain.db.getBlockByTxHash(hash);
}
const getUTXO = async (blkNum, txIndex, oIndex) => {
return blockchain.db.getUTXO(blkNum, txIndex, oIndex);
}
const getPendingTxsByAddress = async (address) => {
}
const getPendingExitsByAddress = async (address) => {
}
/**
* Submit a confirm signature for transaction txIndex in block blkNum
* @param {number} blkNum the block number
* @param {number} txIndex the transaction index in the block blkNum
* @param {string} confirmSignature the confirmation signature to use to confirm the transaction in hex string, i.e., "0x..."
*/
const confirmTx = async (blkNum, txIndex, confirmSignature) => {
let data = await blockchain.getTxHashRoot(blkNum, txIndex);
let {
txHash,
root,
sig1,
sig2
} = data;
if (!blockchain.rootChain.isValidConfirmSig(txHash, root, sig1, confirmSignature)) {
return {
"status": "error",
"error": "Confirmation signature is not valid"
};
}
blockchain.setConfirmSignature(blkNum, txIndex, confirmSignature);
return {
"status": "OK"
};
}
/**
* Submit a confirm signature for transaction txIndex in block blkNum using the private key to sign for it
* @param {number} blkNum the block number
* @param {number} txIndex the transaction index in the block blkNum
* @param {string} key the private key used to sign the confirmation in hex string, i.e., "0x..."
*/
const trustedConfirmTx = async (blkNum, txIndex, key) => {
let confirmSignature = blockchain.generateConfirmSig(blkNum, txIndex, key);
if (!confirmSignature) {
return {
"status": "error",
"error": "Confirmation signature is not valid"
};
}
blockchain.setConfirmSignature(blkNum, txIndex, confirmSignature);
return {
"status": "OK"
};
}
/**
* Submit a transaction by spending from to to for amount of token using the confirm signature and the from's private key to sign.
* The confirm signature is optional if the input of the spending UTXO is owned by from
* @param {string} [token="0x0000000000000000000000000000000000000000"] token address in hex string, i.e., "0x..."
* @param {string} from from address in hex string, i.e., "0x..."
* @param {string} to to address in hex string, i.e., "0x..."
* @param {number} amount the amount to send
* @param {string} [confirmSig] confirmation signature in hex string, i.e., "0x..."
* @param {string} key private key of from in hex string, i.e., "0x..."
*/
const trustedTransact = async (token, from, to, amount, confirmSig, key) => {
if (!token) {
token = "0x0000000000000000000000000000000000000000";
}
let unsignedTx = await blockchain.createUnsignedTransaction(token, from, to, amount, confirmSig);
let signature = blockchain.rootChain.signTransactionKey(unsignedTx.hash(), key);
unsignedTx.setSignature(signature);
let tx = await blockchain.submitTransaction(unsignedTx);
return tx.toJSON()
}
/**
* Create a transaction by spending from to to for amount of token using the confirm signature
* To be signed before submitting
* The confirm signature is optional if the input of the spending UTXO is owned by from
* @param {string} [token="0x0000000000000000000000000000000000000000"] token address in hex string, i.e., "0x..."
* @param {string} from from address in hex string, i.e., "0x..."
* @param {string} to to address in hex string, i.e., "0x..."
* @param {number} amount the amount to send
* @param {string} [confirmSig] confirmation signature in hex string, i.e., "0x..."
*/
const transact = async (token, from, to, amount, confirmSig) => {
if (!token) {
token = "0x0000000000000000000000000000000000000000";
}
let unsignedTx = await blockchain.createUnsignedTransaction(token, from, to, amount, confirmSig);
return unsignedTx.toJSON()
}
/**
* Submit a signed transaction to Plasma chain
* @param {json} signedTransaction signed transaction json object from toJSON() function of the Transaction.
*/
const submitTransact = async (signedTransaction) => {
console.log("signedTransaction", signedTransaction);
let signedTx = Transaction.fromJSON(signedTransaction);
console.log("signedTx", signedTx);
let tx = await blockchain.submitTransaction(signedTx);
return tx.toJSON();
}
/**
* Submit a signed transaction to Ethereum chain
* @param {string} signedTransaction signed transaction in hex string, i.e., "0x..."
*/
const submitSignedTransaction = async (signedTransaction) => {
let result = await blockchain.rootChain.submitSignedTransaction(signedTransaction);
return result;
}
/**
* Create a deposit object to be signed with web3.eth.accounts.signTransaction and submit
* with submitSignedTransaction(signedTransaction)
* @param {string} address from address in hex string, i.e., "0x..."
* @param {string} token token address in hex string, i.e., "0x..."
* @param {number} amount amount
*/
const deposit = async (address, token, amount) => {
let result = await blockchain.rootChain.createDeposit(address, token, amount);
return result;
}
/**
* Deposit into the plasma chain from address with token of amount using the private key of the address
* @param {string} address from address in hex string, i.e., "0x..."
* @param {string} token token address in hex string, i.e., "0x..."
* @param {number} amount amount
* @param {string} key private key of address in hex string, i.e., "0x..."
*/
const trustedDeposit = async (address, token, amount, key) => {
let result = await blockchain.rootChain.deposit(address, token, amount, key);
return result;
}
/**
* Create an object that start exiting the deposit from the plasma chain to be signed with web3.eth.accounts.signTransaction and submit with submitSignedTransaction(signedTransaction)
* @param {number} blkNum the block number of the deposit transaction
* @param {string} token token address in hex string, i.e., "0x..."
* @param {number} amount amount
* @param {string} from from address in hex string, i.e., "0x..."
*/
const startDepositExit = async (blkNum, token, amount, from) => {
let depositPos = blkNum * 1000000000;
const result = await blockchain.rootChain.createStartDepositWithdrawal(depositPos, token, amount, from);
return {
result,
depositPos
};
}
/**
* Start exiting the deposit from the plasma chain
* @param {number} blkNum the block number of the deposit transaction
* @param {string} token token address in hex string, i.e., "0x..."
* @param {number} amount amount
* @param {string} from from address in hex string, i.e., "0x..."
* @param {string} key private key of from address in hex string, i.e., "0x..."
*/
const trustedStartDepositExit = async (blkNum, token, amount, from, key) => {
let depositPos = blkNum * 1000000000;
const result = await blockchain.rootChain.startDepositWithdrawal(depositPos, token, amount, from, key);
return {
result,
depositPos
};
}
/**
* Start exiting the plasma chain object to be signed with web3.eth.accounts.signTransaction and submit with
* @param {number} blkNum the block number of the utxo to exit
* @param {number} txIndex the transaction index of the utxo to exit
* @param {number} oIndex the output index of the utxo to exit [0,1]
* @param {string} from from address in hex string, i.e., "0x..."
* @param {string} [confirmSigs] confirm signature from the owner of the input utxo in hex string, i.e., "0x..."
*/
const startExit = async (blkNum, txIndex, oIndex, from, confirmSigs) => {
blkNum = +blkNum;
txIndex = +txIndex;
oIndex = +oIndex;
let utxoPos = blkNum * 1000000000 + txIndex * 10000 + oIndex;
const p = await blockchain.getTransactionProofInBlock(blkNum, txIndex);
let txData = p.tx;
let block = await blockchain.db.getBlock(blkNum);
confirmSigs = confirmSigs || block.confirmations[txIndex];
// if (!confirmSigs) {
// let tx = Transaction.fromTxData(txData);
// let block1 = await blockchain.db.getBlock(tx.blkNum1);
// let block2 = await blockchain.db.getBlock(tx.blkNum2);
// console.log("1",tx.blkNum1, tx.txIndex1, tx.oIndex1);
// console.log("2",tx.blkNum2, tx.txIndex2, tx.oIndex2);
// confirmSigs = utils.concatHex(block1.confirmations[tx.txIndex1], block2.confirmations[tx.txIndex2]);
// }
// console.log(confirmSigs.length, confirmSigs);
let proof = p.proof;
const result = await blockchain.rootChain.createStartWithdrawal(blkNum, txIndex, oIndex, txData, proof, confirmSigs, from);
return {
result,
utxoPos
};
}
/**
* Exit immediately
* Child chain checks the validity of the withdraw by seeing if the utxo exists.
* Child chain creates a withdrawal transaction on the chain and then submit immediate withdrawal to Ethereum main chain
*/
const immediateExit = async () => {
}
/**
* Start exiting the plasma chain
* @param {number} blkNum the block number of the utxo to exit
* @param {number} txIndex the transaction index of the utxo to exit
* @param {number} oIndex the output index of the utxo to exit [0,1]
* @param {string} [confirmSigs] confirm signature from the owner of the input utxo in hex string, i.e., "0x..."
* @param {string} from from address in hex string, i.e., "0x..."
* @param {string} key private key of from address in hex string, i.e., "0x..."
*/
const trustedStartExit = async (blkNum, txIndex, oIndex, confirmSigs, from, key) => {
blkNum = +blkNum
oIndex = +oIndex
txIndex = +txIndex
let utxoPos = blkNum * 1000000000 + txIndex * 10000 + oIndex;
let block = await blockchain.db.getBlock(blkNum);
confirmSigs = confirmSigs || block.confirmations[txIndex];
const p = await blockchain.getTransactionProofInBlock(blkNum, txIndex);
let txData = p.tx;
let proof = p.proof;
const result = await blockchain.rootChain.startWithdrawal(blkNum, txIndex, oIndex, txData, proof, confirmSigs, from, key);
return {
result,
utxoPos
};
}
/**
* Challenge and exit object to be signed with web3.eth.accounts.signTransaction and submit with
* @param {number} eUtxoPos the eUtxoPos of the exit that you are challenging
* @param {number} blkNum the block number of the utxo to exit used to proof
* @param {number} txIndex the transaction index of the utxo to exit used to proof
* @param {number} oIndex the output index of the utxo to exit [0,1] used to proof
* @param {string} [confirmSig] confirm signature from the owner of the input utxo in hex string, i.e., "0x..."
* @param {string} from from address in hex string, i.e., "0x..."
*/
const challengeExit = async (eUtxoPos, blkNum, txIndex, oIndex, confirmSig, from) => {
let block = await blockchain.db.getBlock(blkNum);
confirmSig = confirmSig || block.confirmations[txIndex];
const p = await blockchain.getTransactionProofInBlock(blkNum, txIndex);
let txData = p.tx;
let proof = p.proof;
let result = await blockchain.rootChain.createChallengeWithdrawal(eUtxoPos, blkNum,
txIndex, oIndex, txData, proof, confirmSig, from);
return {
result
};
}
/**
* Challenge and exit
* @param {number} eUtxoPos the eUtxoPos of the exit that you are challenging
* @param {number} blkNum the block number of the utxo to exit used to proof
* @param {number} txIndex the transaction index of the utxo to exit used to proof
* @param {number} oIndex the output index of the utxo to exit [0,1] used to proof
* @param {string} [confirmSig] confirm signature from the owner of the input utxo in hex string, i.e., "0x..."
* @param {string} address address in hex string, i.e., "0x..."
* @param {string} key private key of address in hex string, i.e., "0x..."
*/
const trustedChallengeExit = async (eUtxoPos, blkNum, txIndex, oIndex, confirmSig, address, key) => {
let block = await blockchain.db.getBlock(blkNum);
confirmSig = confirmSig || block.confirmations[txIndex];
const p = await blockchain.getTransactionProofInBlock(blkNum, txIndex);
let txData = p.tx;
let proof = p.proof;
let result = await blockchain.rootChain.challengeWithdrawal(eUtxoPos, blkNum,
txIndex, oIndex, txData, proof, confirmSig, address, key);
return {
result
};
}
/**
* Finalize exit using from address and its private key object to be signed with web3.eth.accounts.signTransaction and submit with
* @param {string} address address in hex string, i.e., "0x..."
*/
const finalizeExit = async (address) => {
let result = await blockchain.rootChain.createFinalizeWithdrawal(address);
return {
result
};
}
/**
* Finalize exit using from address and its private key
* @param {string} address address in hex string, i.e., "0x..."
* @param {string} key private key of address in hex string, i.e., "0x..."
*/
const trustedFinalizeExit = async (address, key) => {
let result = await blockchain.rootChain.finalizeWithdrawal(address, key);
return {
result
};
}
const getUnconfirmedConfirmationHashes = async (address) => {
let result = await blockchain.db.getUnconfirmedConfirmationHashes(address);
return {
result
};
}
const getUnconfirmedTransactions = async (address) => {
let result = await blockchain.db.getUnconfirmedTransactions(address);
return {
result
};
}
const connect = require('connect');
const jsonParser = require('body-parser').json;
const app = connect();
// create a server
var server = jayson.server({
challengeExit: async (args, callback) => {
try {
let {
eUtxoPos,
blkNum,
txIndex,
oIndex,
address
} = args;
let result = await challengeExit(eUtxoPos, blkNum, txIndex, oIndex, address);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
trustedChallengeExit: async (args, callback) => {
try {
let {
eUtxoPos,
blkNum,
txIndex,
oIndex,
address,
key
} = args;
let result = await trustedChallengeExit(eUtxoPos, blkNum, txIndex, oIndex, address, key);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
confirmTx: async (args, callback) => {
try {
let {
blkNum,
txIndex,
confirmSignature
} = args;
let result = await confirmTx(blkNum, txIndex, confirmSignature);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
finalizeExit: async (args, callback) => {
try {
let {
address
} = args;
let result = await finalizeExit(address);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
trustedFinalizeExit: async (args, callback) => {
try {
let {
address,
key
} = args;
let result = await trustedFinalizeExit(address, key);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
startDepositExit: async (args, callback) => {
try {
let {
blkNum,
token,
amount,
from
} = args;
let result = await startDepositExit(blkNum, token, amount, from);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
trustedStartDepositExit: async (args, callback) => {
try {
let {
blkNum,
token,
amount,
from,
key
} = args;
let result = await trustedStartDepositExit(blkNum, token, amount, from, key);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
startExit: async (args, callback) => {
try {
let {
blkNum,
txIndex,
oIndex,
from,
confirmSigs
} = args;
let result = await startExit(blkNum, txIndex, oIndex, from, confirmSigs);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
trustedStartExit: async (args, callback) => {
try {
let {
blkNum,
txIndex,
oIndex,
from,
confirmSigs,
key
} = args;
let result = await trustedStartExit(blkNum, txIndex, oIndex, from, confirmSigs, key);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
submitTransact: async (args, callback) => {
try {
let {
signedTransaction
} = args;
let result = await submitTransact(signedTransaction);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
submitSignedTransaction: async (args, callback) => {
try {
let {
signedTransaction
} = args;
let result = await submitSignedTransaction(signedTransaction);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
transact: async (args, callback) => {
try {
let {
token,
from,
to,
amount,
confirmSig
} = args;
let result = await transact(token, from, to, amount, confirmSig);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
trustedConfirmTx: async (args, callback) => {
try {
let {
blkNum,
txIndex,
key
} = args;
let result = await trustedConfirmTx(blkNum, txIndex, key);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
deposit: async (args, callback) => {
try {
let {
address,
token,
amount
} = args;
let result = await deposit(address, token, amount);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
trustedDeposit: async (args, callback) => {
try {
let {
address,
token,
amount,
key
} = args;
let result = await trustedDeposit(address, token, amount, key);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
trustedTransact: async (args, callback) => {
try {
let {
token,
from,
to,
amount,
confirmSig,
key
} = args;
let result = await trustedTransact(token, from, to, amount, confirmSig, key);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
getLatestBlock: async (args, callback) => {
try {
let result = await getLatestBlock();
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
getPendingExits: async (args, callback) => {
try {
// let {
// blkNum,
// fromBlkNum,
// toBlkNum
// } = args;
let result = await getPendingExits();
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
getBlocks: async (args, callback) => {
try {
let {
blkNum,
fromBlkNum,
toBlkNum
} = args;
let result = await getBlocks(blkNum, fromBlkNum, toBlkNum);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
getUTXOs: async (args, callback) => {
try {
let result = await getUTXOs();
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
getPendingTxs: async (args, callback) => {
try {
let result = await getPendingTxs();
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
getUTXOsByAddress: async (args, callback) => {
try {
let {
address
} = args;
let result = await getUTXOsByAddress(address);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
getUTXO: async (args, callback) => {
try {
let {
blkNum,
txIndex,
oIndex
} = args;
let result = await getUTXO(blkNum, txIndex, oIndex);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
getTxByHash: async (args, callback) => {
try {
let {
hash
} = args;
let result = await getTxByHash(hash);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
getBlockByTxHash: async (args, callback) => {
try {
let {
hash
} = args;
let result = await getBlockByTxHash(hash);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
getUnconfirmedTransactions: async (args, callback) => {
try {
let {
address
} = args;
let result = await getUnconfirmedTransactions(address);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
getUnconfirmedConfirmationHashes: async (args, callback) => {
try {
let {
address
} = args;
let result = await getUnconfirmedConfirmationHashes(address);
callback(null, {
"status": "ok",
"result": result,
});
} catch (e) {
console.log(e);
callback(null, {
"status": "error",
"error": e.message,
});
}
},
});
app.use(compression({
level: 9
}));
app.use(cors({
methods: ['POST', 'GET']
}));
app.use(jsonParser());
app.use(server.middleware());
const start = async function () {
await blockchain.connect()
blockchain.startMining();
app.listen(process.env.PORT || 3001);
}
start();