laksa-core-transaction
Version:
Transaction instance for laksa
360 lines (305 loc) • 10.2 kB
JavaScript
/**
* This source code is being disclosed to you solely for the purpose of your participation in
* testing Zilliqa and Laksa. You may view, compile and run the code for that purpose and pursuant to
* the protocols and algorithms that are programmed into, and intended by, the code. You may
* not do anything else with the code without express permission from Zilliqa Research Pte. Ltd.,
* including modifying or publishing the code (or any part of it), and developing or forming
* another public or private blockchain network. This source code is provided ‘as is’ and no
* warranties are given as to title or non-infringement, merchantability or fitness for purpose
* and, to the extent permitted by law, all liability for your use of the code is disclaimed.
* Some programs in this code are governed by the GNU General Public License v3.0 (available at
* https://www.gnu.org/licenses/gpl-3.0.en.html) (‘GPLv3’). The programs that are governed by
* GPLv3.0 are those programs that are located in the folders src/depends and tests/depends
* and which include a reference to GPLv3 in their program files.
*/
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('laksa-core-crypto'), require('laksa-shared')) :
typeof define === 'function' && define.amd ? define(['exports', 'laksa-core-crypto', 'laksa-shared'], factory) :
(factory((global.Laksa = {}),global.laksaCoreCrypto,global.laksaShared));
}(this, (function (exports,laksaCoreCrypto,laksaShared) { 'use strict';
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _objectSpread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
var ownKeys = Object.keys(source);
if (typeof Object.getOwnPropertySymbols === 'function') {
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
}));
}
ownKeys.forEach(function (key) {
_defineProperty(target, key, source[key]);
});
}
return target;
}
/**
* @function sleep
* @param {Number} ms - miliSeconds
* @return {Promise<Function>} {description}
*/
const sleep = async ms => new Promise(resolve => {
setTimeout(() => resolve(), ms);
});
const TxStatus = Object.freeze({
Pending: 'Pending',
Initialised: 'Initialised',
Signed: 'Signed',
Confirmed: 'Confirmed',
Rejected: 'Rejected'
});
/**
* @class Transaction
* @description Createa a Transaction instance
* @param {Object} params - params object
* @param {Messenger} messenger - messenger instance
* @param {String} status - txstatus
* @param {Boolean} toDs - send to Shard
* @return {Transaction} = Transaction instance
*/
class Transaction {
constructor(params, messenger, status = TxStatus.Initialised, toDS = false) {
// params
this.version = params.version;
this.TranID = params.TranID;
this.toAddr = laksaCoreCrypto.getAddress(params.toAddr, [laksaCoreCrypto.AddressType.bech32, laksaCoreCrypto.AddressType.checkSum], laksaCoreCrypto.AddressType.checkSum);
this.nonce = params.nonce;
this.pubKey = params.pubKey;
this.amount = params.amount;
this.code = params.code || '';
this.data = params.data || '';
this.signature = params.signature;
this.gasPrice = params.gasPrice;
this.gasLimit = params.gasLimit;
this.receipt = params.receipt; // status
this.status = status;
this.messenger = messenger;
this.toDS = toDS;
}
/**
* @function confirm
* @memberof Transaction
* @param {BaseTx} params
*/
static confirm(params, messenger) {
return new Transaction(params, messenger, TxStatus.Confirmed);
}
/**
*@function reject
* @memberof Transaction
* @param {BaseTx} params
*/
static reject(params, messenger) {
return new Transaction(params, messenger, TxStatus.Rejected);
}
setMessenger(messenger) {
this.messenger = messenger;
}
/**
* @param {TxStatus} status
* @returns {undefined}
*/
setStatus(status) {
this.status = status;
return this;
}
get bytes() {
return laksaCoreCrypto.encodeTransactionProto(this.txParams);
}
get txParams() {
try {
return {
version: this.version,
// this.messenger.setTransactionVersion(this.version),
TranID: this.TranID,
toAddr: laksaCoreCrypto.getAddress(this.toAddr, [laksaCoreCrypto.AddressType.bech32, laksaCoreCrypto.AddressType.checkSum], laksaCoreCrypto.AddressType.checkSum),
// after updated to the core, it will not slice
nonce: this.nonce,
pubKey: this.pubKey,
amount: this.amount,
gasPrice: this.gasPrice,
gasLimit: this.gasLimit,
code: this.code,
data: this.data,
signature: this.signature,
receipt: this.receipt
};
} catch (error) {
throw error;
}
}
get senderAddress() {
if (!this.pubKey) {
return String(0).repeat(40);
}
return laksaCoreCrypto.getAddressFromPublicKey(this.pubKey);
}
/**
* @function isPending
*
* @returns {boolean}
*/
isPending() {
return this.status === TxStatus.Pending;
}
/**
* @function isInitialised
*
* @returns {boolean}
*/
isInitialised() {
return this.status === TxStatus.Initialised;
}
/**
* @function isRejected
*
* @returns {boolean}
*/
isSigned() {
return this.status === TxStatus.Signed;
}
/**
* @function isConfirmed
*
* @returns {boolean}
*/
isConfirmed() {
return this.status === TxStatus.Confirmed;
}
/**
* @function isRejected
*
* @returns {boolean}
*/
isRejected() {
return this.status === TxStatus.Rejected;
}
async sendTransaction() {
if (!this.signature) {
throw new Error('The Transaction has not been signed');
}
try {
const raw = this.txParams;
const result = await this.messenger.send('CreateTransaction', _objectSpread({}, raw, {
amount: raw.amount.toString(),
gasLimit: raw.gasLimit.toString(),
gasPrice: raw.gasPrice.toString(),
priority: this.toDS
}));
const {
TranID
} = result;
if (!TranID) {
throw new Error('Transaction fail');
} else {
this.TranID = TranID;
this.status = TxStatus.Pending;
return {
transaction: this,
response: result
};
}
} catch (error) {
throw error;
}
} // /**
// * confirmReceipt
// *
// * Similar to the Promise API. This sets the Transaction instance to a state
// * of pending. Calling this function kicks off a passive loop that polls the
// * lookup node for confirmation on the txHash.
// *
// * The polls are performed with a linear backoff:
// *
// * `const delay = interval * attempt`
// *
// * This is a low-level method that you should generally not have to use
// * directly.
// */
/*
*
* @param {string} txHash
* @param {number} maxAttempts
* @param {number} initial interval in milliseconds
* @returns {Promise<Transaction>}
*/
async confirm(txHash, maxAttempts = 20, interval = 1000) {
this.status = TxStatus.Pending;
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
try {
if (await this.trackTx(txHash)) {
return this;
}
} catch (err) {
this.status = TxStatus.Rejected;
throw err;
}
if (attempt + 1 < maxAttempts) {
await sleep(interval * attempt);
}
}
this.status = TxStatus.Rejected;
throw new Error(`The transaction is still not confirmed after ${maxAttempts} attempts.`);
}
map(fn) {
const newParams = fn(this.txParams);
this.setParams(newParams);
return this;
}
setParams(params) {
this.version = params.version;
this.TranID = params.TranID;
this.toAddr = laksaCoreCrypto.getAddress(params.toAddr, [laksaCoreCrypto.AddressType.bech32, laksaCoreCrypto.AddressType.checkSum], laksaCoreCrypto.AddressType.checkSum);
this.nonce = params.nonce;
this.pubKey = params.pubKey;
this.amount = params.amount;
this.code = params.code;
this.data = params.data;
this.signature = params.signature;
this.gasPrice = params.gasPrice;
this.gasLimit = params.gasLimit;
this.receipt = params.receipt;
}
async trackTx(txHash) {
// TODO: regex validation for txHash so we don't get garbage
const res = await this.messenger.send('GetTransaction', txHash);
if (res.responseType === 'error') {
return false;
}
this.TranID = res.ID;
this.receipt = res.receipt;
this.status = this.receipt && this.receipt.success ? TxStatus.Confirmed : TxStatus.Rejected;
return true;
}
}
/**
* @class Transactions
*/
class Transactions extends laksaShared.Core {
constructor(messenger, signer) {
super();
this.messenger = messenger;
this.signer = signer;
}
new(txParams, toDS = false) {
return new Transaction(txParams, this.messenger, TxStatus.Initialised, toDS);
}
}
exports.Transaction = Transaction;
exports.Transactions = Transactions;
exports.sleep = sleep;
exports.TxStatus = TxStatus;
Object.defineProperty(exports, '__esModule', { value: true });
})));