@aeternity/aepp-sdk
Version:
SDK for the æternity blockchain
442 lines (394 loc) • 17.8 kB
JavaScript
import _Object$keys from "@babel/runtime-corejs3/core-js-stable/object/keys";
import _Object$getOwnPropertySymbols from "@babel/runtime-corejs3/core-js-stable/object/get-own-property-symbols";
import _filterInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/filter";
import _Object$getOwnPropertyDescriptor from "@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptor";
import _forEachInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/for-each";
import _Object$getOwnPropertyDescriptors from "@babel/runtime-corejs3/core-js-stable/object/get-own-property-descriptors";
import _Object$defineProperties from "@babel/runtime-corejs3/core-js-stable/object/define-properties";
import _Object$defineProperty from "@babel/runtime-corejs3/core-js-stable/object/define-property";
import _defineProperty from "@babel/runtime-corejs3/helpers/defineProperty";
import _asyncToGenerator from "@babel/runtime-corejs3/helpers/asyncToGenerator";
import _toConsumableArray from "@babel/runtime-corejs3/helpers/toConsumableArray";
import _slicedToArray from "@babel/runtime-corejs3/helpers/slicedToArray";
import _regeneratorRuntime from "@babel/runtime-corejs3/regenerator";
function ownKeys(object, enumerableOnly) { var keys = _Object$keys(object); if (_Object$getOwnPropertySymbols) { var symbols = _Object$getOwnPropertySymbols(object); if (enumerableOnly) symbols = _filterInstanceProperty(symbols).call(symbols, function (sym) { return _Object$getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { var _context11; _forEachInstanceProperty(_context11 = ownKeys(Object(source), true)).call(_context11, function (key) { _defineProperty(target, key, source[key]); }); } else if (_Object$getOwnPropertyDescriptors) { _Object$defineProperties(target, _Object$getOwnPropertyDescriptors(source)); } else { var _context12; _forEachInstanceProperty(_context12 = ownKeys(Object(source))).call(_context12, function (key) { _Object$defineProperty(target, key, _Object$getOwnPropertyDescriptor(source, key)); }); } } return target; }
import _concatInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/concat";
import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";
import _reduceInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/reduce";
import _Object$entries from "@babel/runtime-corejs3/core-js-stable/object/entries";
import _parseInt from "@babel/runtime-corejs3/core-js-stable/parse-int";
import _findInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/find";
import _mapInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/map";
import { verify, decodeBase58Check, assertedType } from '../utils/crypto';
import { encode } from '../tx/builder/helpers';
import BigNumber from 'bignumber.js';
import { BASE_VERIFICATION_SCHEMA, CONTRACT_VERIFICATION_SCHEMA, MIN_GAS_PRICE, NAME_CLAIM_VERIFICATION_SCHEMA, OBJECT_ID_TX_TYPE, OBJECT_TAG_SIGNED_TRANSACTION, PROTOCOL_VM_ABI, SIGNATURE_VERIFICATION_SCHEMA, TX_TYPE } from './builder/schema';
import { buildTxHash, calculateFee, unpackTx } from './builder';
import { NodePool } from '../node-pool';
/**
* Transaction validator
* @module @aeternity/aepp-sdk/es/tx/validator
* @export TransactionValidator
* @example import { TransactionValidator } from '@aeternity/aepp-sdk'
*/
var VALIDATORS = {
// VALIDATE SIGNATURE
signature: function signature(_ref) {
var rlpEncoded = _ref.rlpEncoded,
signature = _ref.signature,
ownerPublicKey = _ref.ownerPublicKey,
_ref$networkId = _ref.networkId,
networkId = _ref$networkId === void 0 ? 'ae_mainnet' : _ref$networkId;
var txWithNetworkId = _concatInstanceProperty(Buffer).call(Buffer, [Buffer.from(networkId), rlpEncoded]);
var txHashWithNetworkId = _concatInstanceProperty(Buffer).call(Buffer, [Buffer.from(networkId), buildTxHash(rlpEncoded, {
raw: true
})]);
var decodedPub = decodeBase58Check(assertedType(ownerPublicKey, 'ak'));
return verify(txWithNetworkId, signature, decodedPub) || verify(txHashWithNetworkId, signature, decodedPub);
},
// VALIDATE IF ENOUGH FEE
insufficientFee: function insufficientFee(_ref2) {
var minFee = _ref2.minFee,
fee = _ref2.fee;
return BigNumber(minFee).lte(BigNumber(fee));
},
// VALIDATE IF TTL VALID
expiredTTL: function expiredTTL(_ref3) {
var ttl = _ref3.ttl,
height = _ref3.height;
return BigNumber(ttl).eq(0) || BigNumber(ttl).gte(BigNumber(height));
},
// Insufficient Balance for Amount plus Fee
insufficientBalanceForAmountFee: function insufficientBalanceForAmountFee(_ref4) {
var balance = _ref4.balance,
_ref4$amount = _ref4.amount,
amount = _ref4$amount === void 0 ? 0 : _ref4$amount,
fee = _ref4.fee;
return BigNumber(balance).gte(BigNumber(amount).plus(fee));
},
// Insufficient Balance for Amount
insufficientBalanceForAmount: function insufficientBalanceForAmount(_ref5) {
var balance = _ref5.balance,
_ref5$amount = _ref5.amount,
amount = _ref5$amount === void 0 ? 0 : _ref5$amount;
return BigNumber(balance).gte(BigNumber(amount));
},
// IF NONCE USED
nonceUsed: function nonceUsed(_ref6) {
var accountNonce = _ref6.accountNonce,
nonce = _ref6.nonce;
return BigNumber(nonce).gt(BigNumber(accountNonce));
},
// IF NONCE TO HIGH
nonceHigh: function nonceHigh(_ref7) {
var accountNonce = _ref7.accountNonce,
nonce = _ref7.nonce;
return !BigNumber(nonce).gt(BigNumber(accountNonce).plus(1));
},
minGasPrice: function minGasPrice(_ref8) {
var gasPrice = _ref8.gasPrice;
return isNaN(gasPrice) || BigNumber(gasPrice).gte(BigNumber(MIN_GAS_PRICE));
},
// VM/ABI version validation based on consensus protocol version
vmAndAbiVersion: function vmAndAbiVersion(_ref9) {
var _context, _context2;
var ctVersion = _ref9.ctVersion,
abiVersion = _ref9.abiVersion,
consensusProtocolVersion = _ref9.consensusProtocolVersion,
txType = _ref9.txType;
// If not contract tx
if (!ctVersion) ctVersion = {
abiVersion: abiVersion
};
var supportedProtocol = PROTOCOL_VM_ABI[consensusProtocolVersion]; // If protocol not implemented
if (!supportedProtocol) return true; // If protocol for tx type not implemented
var txProtocol = supportedProtocol[txType];
return !_includesInstanceProperty(_context = _reduceInstanceProperty(_context2 = _Object$entries(ctVersion)).call(_context2, function (acc, _ref10) {
var _context3, _context4;
var _ref11 = _slicedToArray(_ref10, 2),
key = _ref11[0],
value = _ref11[1];
return _concatInstanceProperty(_context3 = []).call(_context3, _toConsumableArray(acc), [value === undefined ? true : _includesInstanceProperty(_context4 = txProtocol[key]).call(_context4, _parseInt(value))]);
}, [])).call(_context, false);
},
insufficientBalanceForFeeNameFee: function insufficientBalanceForFeeNameFee(_ref12) {
var nameFee = _ref12.nameFee,
fee = _ref12.fee,
balance = _ref12.balance,
VSN = _ref12.VSN;
return VSN === 1 || BigNumber(balance).gte(BigNumber(nameFee).plus(fee));
}
};
var resolveDataForBase = /*#__PURE__*/function () {
var _ref14 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(chain, _ref13) {
var ownerPublicKey, accountNonce, accountBalance, _yield$chain$api$getA, nonce, balance;
return _regeneratorRuntime.wrap(function _callee$(_context5) {
while (1) {
switch (_context5.prev = _context5.next) {
case 0:
ownerPublicKey = _ref13.ownerPublicKey;
accountNonce = 0;
accountBalance = 0;
_context5.prev = 3;
_context5.next = 6;
return chain.api.getAccountByPubkey(ownerPublicKey);
case 6:
_yield$chain$api$getA = _context5.sent;
nonce = _yield$chain$api$getA.nonce;
balance = _yield$chain$api$getA.balance;
accountNonce = nonce;
accountBalance = balance;
_context5.next = 16;
break;
case 13:
_context5.prev = 13;
_context5.t0 = _context5["catch"](3);
console.log('We can not get info about this publicKey');
case 16:
_context5.t1 = _objectSpread;
_context5.next = 19;
return chain.api.getCurrentKeyBlockHeight();
case 19:
_context5.t2 = _context5.sent.height;
_context5.t3 = accountBalance;
_context5.t4 = accountNonce;
_context5.t5 = ownerPublicKey;
_context5.t6 = {
height: _context5.t2,
balance: _context5.t3,
accountNonce: _context5.t4,
ownerPublicKey: _context5.t5
};
_context5.t7 = chain.getNodeInfo();
return _context5.abrupt("return", (0, _context5.t1)(_context5.t6, _context5.t7));
case 26:
case "end":
return _context5.stop();
}
}
}, _callee, null, [[3, 13]]);
}));
return function resolveDataForBase(_x, _x2) {
return _ref14.apply(this, arguments);
};
}(); // Verification using SCHEMA
var verifySchema = function verifySchema(schema, data) {
// Verify through schema
return _reduceInstanceProperty(schema).call(schema, function (acc, _ref15) {
var _ref16 = _slicedToArray(_ref15, 3),
msg = _ref16[0],
validatorKey = _ref16[1],
_ref16$ = _ref16[2],
key = _ref16$.key,
type = _ref16$.type,
txKey = _ref16$.txKey;
if (!VALIDATORS[validatorKey](data)) acc.push({
msg: msg(data),
txKey: txKey,
type: type
});
return acc;
}, []);
};
/**
* Unpack and verify transaction (verify nonce, ttl, fee, signature, account balance)
* @function
* @alias module:@aeternity/aepp-sdk/es/tx/validator
*
* @param {String} txHash Base64Check transaction hash
* @param {Object} [options={}] Options
* @param {String} [options.networkId] networkId Use in signature verification
* @return {Promise<Object>} Object with verification errors and warnings
*/
function unpackAndVerify(_x3) {
return _unpackAndVerify.apply(this, arguments);
}
function _unpackAndVerify() {
_unpackAndVerify = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(txHash) {
var _ref18,
networkId,
_unpackTx,
unpackedTx,
rlpEncoded,
txType,
_context7,
_unpackedTx$encodedTx,
_txType,
tx,
signatures,
rlpEncodedTx,
_args2 = arguments;
return _regeneratorRuntime.wrap(function _callee2$(_context8) {
while (1) {
switch (_context8.prev = _context8.next) {
case 0:
_ref18 = _args2.length > 1 && _args2[1] !== undefined ? _args2[1] : {}, networkId = _ref18.networkId;
_unpackTx = unpackTx(txHash), unpackedTx = _unpackTx.tx, rlpEncoded = _unpackTx.rlpEncoded, txType = _unpackTx.txType;
if (!(+unpackedTx.tag === OBJECT_TAG_SIGNED_TRANSACTION)) {
_context8.next = 13;
break;
}
_unpackedTx$encodedTx = unpackedTx.encodedTx, _txType = _unpackedTx$encodedTx.txType, tx = _unpackedTx$encodedTx.tx;
signatures = _mapInstanceProperty(_context7 = unpackedTx.signatures).call(_context7, function (raw) {
return {
raw: raw,
hash: encode(raw, 'sg')
};
});
rlpEncodedTx = unpackedTx.encodedTx.rlpEncoded;
_context8.next = 8;
return this.verifyTx({
tx: tx,
signatures: signatures,
rlpEncoded: rlpEncodedTx
}, networkId);
case 8:
_context8.t0 = _context8.sent;
_context8.t1 = tx;
_context8.t2 = signatures;
_context8.t3 = _txType;
return _context8.abrupt("return", {
validation: _context8.t0,
tx: _context8.t1,
signatures: _context8.t2,
txType: _context8.t3
});
case 13:
_context8.next = 15;
return this.verifyTx({
tx: unpackedTx,
rlpEncoded: rlpEncoded
}, networkId);
case 15:
_context8.t4 = _context8.sent;
_context8.t5 = unpackedTx;
_context8.t6 = txType;
return _context8.abrupt("return", {
validation: _context8.t4,
tx: _context8.t5,
txType: _context8.t6
});
case 19:
case "end":
return _context8.stop();
}
}
}, _callee2, this);
}));
return _unpackAndVerify.apply(this, arguments);
}
var getOwnerPublicKey = function getOwnerPublicKey(tx) {
var _context6;
return tx[_findInstanceProperty(_context6 = ['senderId', 'accountId', 'ownerId', 'callerId', 'oracleId', 'fromId', 'initiator', 'gaId']).call(_context6, function (key) {
return tx[key];
})].replace('ok_', 'ak_');
};
/**
* Verify transaction (verify nonce, ttl, fee, signature, account balance)
* @function
* @alias module:@aeternity/aepp-sdk/es/tx/validator
*
* @param {Object} [data={}] data TX data object
* @param {String} [data.tx] tx Transaction hash
* @param {Array} [data.signatures] signatures Transaction signature's
* @param {Array} [data.rlpEncoded] rlpEncoded RLP encoded transaction
* @param {String} networkId networkId Use in signature verification
* @return {Promise<Array>} Object with verification errors and warnings
*/
function verifyTx(_x4, _x5) {
return _verifyTx.apply(this, arguments);
}
/**
* Verification for speciific txType
* @param txType
* @param data
* @return {Array}
*/
function _verifyTx() {
_verifyTx = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(_ref17, networkId) {
var _context9;
var tx, signatures, rlpEncoded, ownerPublicKey, gas, txType, resolvedData, signatureVerification, baseVerification;
return _regeneratorRuntime.wrap(function _callee3$(_context10) {
while (1) {
switch (_context10.prev = _context10.next) {
case 0:
tx = _ref17.tx, signatures = _ref17.signatures, rlpEncoded = _ref17.rlpEncoded;
networkId = networkId || this.getNetworkId() || 'ae_mainnet'; // Fetch data for verification
ownerPublicKey = getOwnerPublicKey(tx);
gas = Object.prototype.hasOwnProperty.call(tx, 'gas') ? +tx.gas : 0;
txType = OBJECT_ID_TX_TYPE[+tx.tag];
_context10.t0 = _objectSpread;
_context10.t1 = _objectSpread;
_context10.t2 = _objectSpread;
_context10.t3 = {
minFee: calculateFee(0, txType, {
gas: gas,
params: tx,
showWarning: false
})
};
_context10.next = 11;
return resolveDataForBase(this, {
ownerPublicKey: ownerPublicKey
});
case 11:
_context10.t4 = _context10.sent;
_context10.t5 = (0, _context10.t2)(_context10.t3, _context10.t4);
_context10.t6 = tx;
_context10.t7 = (0, _context10.t1)(_context10.t5, _context10.t6);
_context10.t8 = {};
_context10.t9 = {
txType: txType
};
resolvedData = (0, _context10.t0)(_context10.t7, _context10.t8, _context10.t9);
signatureVerification = signatures && signatures.length ? verifySchema(SIGNATURE_VERIFICATION_SCHEMA, {
rlpEncoded: rlpEncoded,
signature: signatures[0].raw,
ownerPublicKey: ownerPublicKey,
networkId: networkId
}) : [];
baseVerification = verifySchema(BASE_VERIFICATION_SCHEMA, resolvedData);
return _context10.abrupt("return", _concatInstanceProperty(_context9 = []).call(_context9, _toConsumableArray(baseVerification), _toConsumableArray(signatureVerification), _toConsumableArray(customVerification(txType, resolvedData))));
case 21:
case "end":
return _context10.stop();
}
}
}, _callee3, this);
}));
return _verifyTx.apply(this, arguments);
}
function customVerification(txType, data) {
switch (txType) {
case TX_TYPE.contractCreate:
case TX_TYPE.contractCall:
case TX_TYPE.oracleRegister:
return verifySchema(CONTRACT_VERIFICATION_SCHEMA, data);
case TX_TYPE.nameClaim:
return verifySchema(NAME_CLAIM_VERIFICATION_SCHEMA, data);
default:
return [];
}
}
/**
* Transaction Validator Stamp
* This stamp give us possibility to unpack and validate some of transaction properties,
* to make sure we can post it to the chain
* @function
* @alias module:@aeternity/aepp-sdk/es/tx/validator
* @rtype Stamp
* @param {Object} [options={}] - Initializer object
* @param {Object} [options.url] - Node url
* @param {Object} [options.internalUrl] - Node internal url
* @return {Object} Transaction Validator instance
* @example TransactionValidator({url: 'https://testnet.aeternity.io'})
*/
var TransactionValidator = NodePool.compose({
methods: {
verifyTx: verifyTx,
unpackAndVerify: unpackAndVerify
}
});
export default TransactionValidator;
//# sourceMappingURL=validator.js.map