UNPKG

@cityofzion/neon-api

Version:

Neon-API module: High level API for neon-js

167 lines 6.34 kB
import { tx, u } from "@cityofzion/neon-core"; export var ValidationAttributes; (function (ValidationAttributes) { ValidationAttributes[ValidationAttributes["None"] = 0] = "None"; ValidationAttributes[ValidationAttributes["ValidUntilBlock"] = 1] = "ValidUntilBlock"; ValidationAttributes[ValidationAttributes["SystemFee"] = 2] = "SystemFee"; ValidationAttributes[ValidationAttributes["NetworkFee"] = 4] = "NetworkFee"; ValidationAttributes[ValidationAttributes["Script"] = 8] = "Script"; ValidationAttributes[ValidationAttributes["All"] = 15] = "All"; })(ValidationAttributes || (ValidationAttributes = {})); /** * A class with functions to validate transaction */ export class TransactionValidator { constructor(rpc, transaction) { this.rpcClient = rpc; this.transaction = transaction; } /** * validate validUntilBlock. * @param autoFix - autofix when number is below current height. */ async validateValidUntilBlock(autoFix = false) { const { validUntilBlock: prev } = this.transaction; const height = await this.rpcClient.getBlockCount(); // Suggest a lifespan of approx. 1hr based on 15s blocks const suggestion = TransactionValidator.TX_LIFESPAN_SUGGESTION + height - 1; if (prev <= height || prev >= height + tx.Transaction.MAX_TRANSACTION_LIFESPAN) { if (autoFix) { this.transaction.validUntilBlock = suggestion; return fixed(prev, suggestion); } return invalid(prev, suggestion, "Your transaction lifespan was out of range."); } if (prev - height <= 20) { return suggest(prev, suggestion, "Your transaction has a very limited lifespan. Consider increasing it."); } return valid(); } /** * Validate intents */ async validateScript() { const { state } = await this.rpcClient.invokeScript(this.transaction.script, this.transaction.signers); if (state !== "HALT") { return err("Encountered FAULT when validating script."); } return valid(); } /** * validate systemFee * @param autoFix - autofix when fee is too low. */ async validateSystemFee(autoFix = false) { const { script, signers, systemFee: prev } = this.transaction; const invokeResponse = await this.rpcClient.invokeScript(script, signers); if (invokeResponse.state === "FAULT") { return err("Cannot get precise systemFee as script execution on node reports FAULT."); } const gasConsumed = invokeResponse.gasconsumed; const suggestion = u.BigInteger.fromDecimal(gasConsumed, 0); const compareResult = suggestion.compare(prev); if (compareResult > 0) { // Did not hit the minimum fees to run the script. if (autoFix) { this.transaction.systemFee = suggestion; return fixed(prev, suggestion); } return invalid(prev, suggestion, "Insufficient fees attached to run the script."); } else if (compareResult < 0) { // Overpaying for the script. return suggest(prev, suggestion, "Overpaying for running the script."); } return valid(); } /** * Validate NetworkFee * @param autoFix - autofix when fee is too low. */ async validateNetworkFee(autoFix = false) { const { networkFee: prev } = this.transaction; const calculateResponse = await this.rpcClient.calculateNetworkFee(this.transaction); const suggestion = u.BigInteger.fromNumber(calculateResponse); const compareResult = suggestion.compare(prev); if (compareResult > 0) { // Underpaying if (autoFix) { this.transaction.networkFee = suggestion; return fixed(prev, suggestion); } return invalid(prev, suggestion, "Insufficient network fees."); } else if (compareResult < 0) { // Overpaying return suggest(prev, suggestion, "Overpaying network fee."); } return valid(); } async validate(attrs, autoFix = ValidationAttributes.None) { const validationTasks = []; const output = { valid: true, result: {}, }; if (attrs & ValidationAttributes.ValidUntilBlock) { validationTasks.push(this.validateValidUntilBlock((autoFix & ValidationAttributes.ValidUntilBlock) === ValidationAttributes.ValidUntilBlock).then((s) => (output.result.validUntilBlock = s))); } if (attrs & ValidationAttributes.SystemFee) { validationTasks.push(this.validateSystemFee((autoFix & ValidationAttributes.SystemFee) === ValidationAttributes.SystemFee).then((s) => (output.result.systemFee = s))); } if (attrs & ValidationAttributes.NetworkFee) { validationTasks.push(this.validateNetworkFee((autoFix & ValidationAttributes.NetworkFee) === ValidationAttributes.NetworkFee).then((s) => (output.result.networkFee = s))); } if (attrs & ValidationAttributes.Script) { validationTasks.push(this.validateScript().then((s) => (output.result.script = s))); } await Promise.all(validationTasks); output.valid = Object.values(output.result) .map((r) => (r ? r.valid : true)) .reduce((a, b) => a && b); return output; } } TransactionValidator.TX_LIFESPAN_SUGGESTION = 240; function valid() { return { valid: true, fixed: false }; } function fixed(prev, suggestion, message) { return { valid: true, fixed: true, prev, suggestion, message, }; } function err(message) { return { valid: false, fixed: false, message, }; } function suggest(prev, suggestion, message) { return { valid: true, fixed: false, prev, suggestion, message, }; } function invalid(prev, suggestion, message) { return { valid: false, fixed: false, prev, suggestion, message, }; } //# sourceMappingURL=validator.js.map