From d5824ddd138cf13f08e0d61b1f2c237bef3a7448 Mon Sep 17 00:00:00 2001 From: Green Baneling Date: Mon, 25 Mar 2024 16:49:19 +0100 Subject: [PATCH] Versioning of sub parameters (#702) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Versioned `GasCosts` * Versioned `ConsensusParameters`ю Reduced default `MAX_SIZE` to be 110kb. Reduced default `MAX_CONTRACT_SIZE` to be 100kb. * Updated CHANGELOG.md * Versioned `FeeParameters` * Versioned `PredicateParameters`, `ScriptParameters`, `ContractParameters`, * Versioned `TxParameters` * Updated CHANGELOG.md * Make CI happy --- .npm/packages/fuel-tx/index.test.cjs | 460 +++++++-------- .npm/packages/fuel-tx/index.test.mjs | 460 +++++++-------- CHANGELOG.md | 1 + fuel-tx/src/builder.rs | 2 +- fuel-tx/src/tests/valid_cases/input.rs | 10 +- fuel-tx/src/tests/valid_cases/transaction.rs | 58 +- .../src/transaction/consensus_parameters.rs | 550 ++++++++++++++---- fuel-tx/src/transaction/fee.rs | 10 +- fuel-tx/src/transaction/types/create.rs | 4 +- fuel-tx/src/transaction/types/script.rs | 8 +- fuel-tx/src/transaction/validity.rs | 20 +- fuel-vm/src/checked_transaction.rs | 43 +- fuel-vm/src/interpreter.rs | 7 +- fuel-vm/src/tests/limits.rs | 6 +- fuel-vm/src/util.rs | 2 +- 15 files changed, 988 insertions(+), 653 deletions(-) diff --git a/.npm/packages/fuel-tx/index.test.cjs b/.npm/packages/fuel-tx/index.test.cjs index f1c6683875..ae6d8d5b77 100644 --- a/.npm/packages/fuel-tx/index.test.cjs +++ b/.npm/packages/fuel-tx/index.test.cjs @@ -1,243 +1,243 @@ -const { expect } = require('chai') +const {expect} = require('chai') const path = require('node:path') const fs = require('node:fs') const tx = require('.') describe('fuel-tx [cjs]', () => { - it('should export all types', () => { - expect(tx.UtxoId).to.be.ok - expect(tx.TxPointer).to.be.ok - expect(tx.PredicateParameters).to.be.ok - expect(tx.Input).to.be.ok - expect(tx.Output).to.be.ok - expect(tx.Script).to.be.ok - expect(tx.Create).to.be.ok - expect(tx.Mint).to.be.ok - expect(tx.Transaction).to.be.ok - expect(tx.Policies).to.be.ok - }) - - it('should serialize and deserialize UtxoId correctly', () => { - let utxo_id = new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b00001a"); - let bytes = utxo_id.to_bytes(); - let utxo_id2 = tx.UtxoId.from_bytes(bytes); - expect(utxo_id.toString()).to.equal(utxo_id2.toString()) - }) - - it('should serialize and deserialize TxPointer correctly', () => { - let utxo_id = new tx.TxPointer("0123456789ab"); - let bytes = utxo_id.to_bytes(); - let utxo_id2 = tx.TxPointer.from_bytes(bytes); - expect(utxo_id.toString()).to.equal(utxo_id2.toString()) - }) - - - it('should serialize and deserialize all input variants correctly', () => { - [ - tx.Input.coin_predicate( - new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b001a"), - tx.Address.zeroed(), - 1234n, - tx.AssetId.zeroed(), - new tx.TxPointer("0123456789ab"), - 9012n, - [1, 2, 3, 4], - [5, 6, 7, 8], - ), - tx.Input.coin_signed( - new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b001a"), - tx.Address.zeroed(), - BigInt(1234), - tx.AssetId.zeroed(), - new tx.TxPointer("0123456789ab"), - 2, - ), - tx.Input.contract( - new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b001a"), - tx.Bytes32.zeroed(), - tx.Bytes32.zeroed(), - new tx.TxPointer("0123456789ab"), - tx.ContractId.zeroed(), - ), - tx.Input.message_coin_signed( - tx.Address.zeroed(), - tx.Address.zeroed(), - BigInt(1234), - tx.Nonce.zeroed(), - 2, - ), - tx.Input.message_coin_predicate( - tx.Address.zeroed(), - tx.Address.zeroed(), - BigInt(1234), - tx.Nonce.zeroed(), - BigInt(1234), - [1, 2, 3, 4], - [5, 6, 7, 8], - ), - tx.Input.message_data_signed( - tx.Address.zeroed(), - tx.Address.zeroed(), - BigInt(1234), - tx.Nonce.zeroed(), - 2, - [1, 2, 3, 4], - ), - tx.Input.message_data_predicate( - tx.Address.zeroed(), - tx.Address.zeroed(), - BigInt(1234), - tx.Nonce.zeroed(), - BigInt(1234), - [0, 1, 2, 3], - [1, 2, 3, 4], - [5, 6, 7, 8], - ), - ].forEach(input => { - let bytes = input.to_bytes(); - let input2 = tx.Input.from_bytes(bytes); - expect(input.toString()).to.equal(input2.toString()) + it('should export all types', () => { + expect(tx.UtxoId).to.be.ok + expect(tx.TxPointer).to.be.ok + expect(tx.PredicateParameters).to.be.ok + expect(tx.Input).to.be.ok + expect(tx.Output).to.be.ok + expect(tx.Script).to.be.ok + expect(tx.Create).to.be.ok + expect(tx.Mint).to.be.ok + expect(tx.Transaction).to.be.ok + expect(tx.Policies).to.be.ok }) - }) - - - it('should serialize and deserialize all output variants correctly', () => { - [ - tx.Output.coin( - tx.Address.zeroed(), - BigInt(1234), - tx.AssetId.zeroed(), - ), - tx.Output.contract( - 2, - tx.Bytes32.zeroed(), - tx.Bytes32.zeroed(), - ), - tx.Output.change( - tx.Address.zeroed(), - BigInt(1234), - tx.AssetId.zeroed(), - ), - tx.Output.variable( - tx.Address.zeroed(), - BigInt(1234), - tx.AssetId.zeroed(), - ), - tx.Output.contract_created( - tx.ContractId.zeroed(), - tx.Bytes32.zeroed(), - ), - ].forEach(output => { - let bytes = output.to_bytes(); - let output2 = tx.Output.from_bytes(bytes); - expect(output.toString()).to.equal(output2.toString()) + + it('should serialize and deserialize UtxoId correctly', () => { + let utxo_id = new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b00001a"); + let bytes = utxo_id.to_bytes(); + let utxo_id2 = tx.UtxoId.from_bytes(bytes); + expect(utxo_id.toString()).to.equal(utxo_id2.toString()) }) - }) - - - it('should serialize and deserialize all transaction variants correctly', () => { - [ - [tx.Script, tx.Transaction.script( - 1234n, - [1, 2, 3, 4], - [5, 6, 7, 8], - new tx.Policies(), - [], - [], - [], - )], - [tx.Create, tx.Transaction.create( - 1, - new tx.Policies(), - tx.Salt.zeroed(), - [], - [], - [], - [], - )], - [tx.Mint, tx.Transaction.mint( - new tx.TxPointer("0123456789ab"), - new tx.InputContract( - new tx.UtxoId("0xc49d65de61cf04588a764b557d25cc6c6b4bc0d7429227e2a21e61c213b3a3e2:18ab"), - tx.Bytes32.zeroed(), - tx.Bytes32.zeroed(), - new tx.TxPointer("0123456789ab"), - tx.ContractId.zeroed(), - ), - new tx.OutputContract( - 3, - tx.Bytes32.zeroed(), - tx.Bytes32.zeroed(), - ), - 1234n, - tx.AssetId.zeroed(), - 1234n, - )], - ].forEach(([tx_variant_type, tx_variant]) => { - let bytes = tx_variant.to_bytes(); - let tx_variant2 = tx_variant_type.from_bytes(bytes); - expect(tx_variant.toString()).to.equal(tx_variant2.toString()) - - let wrapped_tx = tx_variant.as_tx(); - let tx_bytes = wrapped_tx.to_bytes(); - let wrapped_tx2 = tx.Transaction.from_bytes(tx_bytes); - expect(wrapped_tx.toString()).to.equal(wrapped_tx2.toString()) + + it('should serialize and deserialize TxPointer correctly', () => { + let utxo_id = new tx.TxPointer("0123456789ab"); + let bytes = utxo_id.to_bytes(); + let utxo_id2 = tx.TxPointer.from_bytes(bytes); + expect(utxo_id.toString()).to.equal(utxo_id2.toString()) }) - }) - // Hex string to byte string conversion. - const hexToBytes = hex => { - if (hex.length % 2 != 0) { - throw new Error("Needs full bytes"); - } - const lookup = "0123456789abcdef"; - let result = new Uint8Array(hex.length / 2); - for (let i = 0; i < result.length; i += 1) { - let high = lookup.indexOf(hex[i * 2]); - let low = lookup.indexOf(hex[i * 2 + 1]); - if (high === -1 || low === -1) { - throw new Error("Invalid hex char"); - } - result[i] = (high << 4) | low; + + it('should serialize and deserialize all input variants correctly', () => { + [ + tx.Input.coin_predicate( + new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b001a"), + tx.Address.zeroed(), + 1234n, + tx.AssetId.zeroed(), + new tx.TxPointer("0123456789ab"), + 9012n, + [1, 2, 3, 4], + [5, 6, 7, 8], + ), + tx.Input.coin_signed( + new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b001a"), + tx.Address.zeroed(), + BigInt(1234), + tx.AssetId.zeroed(), + new tx.TxPointer("0123456789ab"), + 2, + ), + tx.Input.contract( + new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b001a"), + tx.Bytes32.zeroed(), + tx.Bytes32.zeroed(), + new tx.TxPointer("0123456789ab"), + tx.ContractId.zeroed(), + ), + tx.Input.message_coin_signed( + tx.Address.zeroed(), + tx.Address.zeroed(), + BigInt(1234), + tx.Nonce.zeroed(), + 2, + ), + tx.Input.message_coin_predicate( + tx.Address.zeroed(), + tx.Address.zeroed(), + BigInt(1234), + tx.Nonce.zeroed(), + BigInt(1234), + [1, 2, 3, 4], + [5, 6, 7, 8], + ), + tx.Input.message_data_signed( + tx.Address.zeroed(), + tx.Address.zeroed(), + BigInt(1234), + tx.Nonce.zeroed(), + 2, + [1, 2, 3, 4], + ), + tx.Input.message_data_predicate( + tx.Address.zeroed(), + tx.Address.zeroed(), + BigInt(1234), + tx.Nonce.zeroed(), + BigInt(1234), + [0, 1, 2, 3], + [1, 2, 3, 4], + [5, 6, 7, 8], + ), + ].forEach(input => { + let bytes = input.to_bytes(); + let input2 = tx.Input.from_bytes(bytes); + expect(input.toString()).to.equal(input2.toString()) + }) + }) + + + it('should serialize and deserialize all output variants correctly', () => { + [ + tx.Output.coin( + tx.Address.zeroed(), + BigInt(1234), + tx.AssetId.zeroed(), + ), + tx.Output.contract( + 2, + tx.Bytes32.zeroed(), + tx.Bytes32.zeroed(), + ), + tx.Output.change( + tx.Address.zeroed(), + BigInt(1234), + tx.AssetId.zeroed(), + ), + tx.Output.variable( + tx.Address.zeroed(), + BigInt(1234), + tx.AssetId.zeroed(), + ), + tx.Output.contract_created( + tx.ContractId.zeroed(), + tx.Bytes32.zeroed(), + ), + ].forEach(output => { + let bytes = output.to_bytes(); + let output2 = tx.Output.from_bytes(bytes); + expect(output.toString()).to.equal(output2.toString()) + }) + }) + + + it('should serialize and deserialize all transaction variants correctly', () => { + [ + [tx.Script, tx.Transaction.script( + 1234n, + [1, 2, 3, 4], + [5, 6, 7, 8], + new tx.Policies(), + [], + [], + [], + )], + [tx.Create, tx.Transaction.create( + 1, + new tx.Policies(), + tx.Salt.zeroed(), + [], + [], + [], + [], + )], + [tx.Mint, tx.Transaction.mint( + new tx.TxPointer("0123456789ab"), + new tx.InputContract( + new tx.UtxoId("0xc49d65de61cf04588a764b557d25cc6c6b4bc0d7429227e2a21e61c213b3a3e2:18ab"), + tx.Bytes32.zeroed(), + tx.Bytes32.zeroed(), + new tx.TxPointer("0123456789ab"), + tx.ContractId.zeroed(), + ), + new tx.OutputContract( + 3, + tx.Bytes32.zeroed(), + tx.Bytes32.zeroed(), + ), + 1234n, + tx.AssetId.zeroed(), + 1234n, + )], + ].forEach(([tx_variant_type, tx_variant]) => { + let bytes = tx_variant.to_bytes(); + let tx_variant2 = tx_variant_type.from_bytes(bytes); + expect(tx_variant.toString()).to.equal(tx_variant2.toString()) + + let wrapped_tx = tx_variant.as_tx(); + let tx_bytes = wrapped_tx.to_bytes(); + let wrapped_tx2 = tx.Transaction.from_bytes(tx_bytes); + expect(wrapped_tx.toString()).to.equal(wrapped_tx2.toString()) + }) + }) + + // Hex string to byte string conversion. + const hexToBytes = hex => { + if (hex.length % 2 != 0) { + throw new Error("Needs full bytes"); + } + const lookup = "0123456789abcdef"; + let result = new Uint8Array(hex.length / 2); + for (let i = 0; i < result.length; i += 1) { + let high = lookup.indexOf(hex[i * 2]); + let low = lookup.indexOf(hex[i * 2 + 1]); + if (high === -1 || low === -1) { + throw new Error("Invalid hex char"); + } + result[i] = (high << 4) | low; + } + return result; } - return result; - } - - it('should validate input correctly', () => { - let input = tx.Input.coin_signed( - new tx.UtxoId("0xc49d65de61cf04588a764b557d25cc6c6b4bc0d7429227e2a21e61c213b3a3e2:18ab"), - tx.Address.from_bytes(hexToBytes("f1e92c42b90934aa6372e30bc568a326f6e66a1a0288595e6e3fbd392a4f3e6e")), - 10599410012256088338n, - tx.AssetId.from_bytes(hexToBytes("2cafad611543e0265d89f1c2b60d9ebf5d56ad7e23d9827d6b522fd4d6e44bc3")), - new tx.TxPointer("000000000000"), - 0, - new tx.BlockHeight(0), - ); - - tx.check_input(input, 0, tx.Bytes32.from_bytes(hexToBytes("108eae4147d2c1c86ef4c2ab7c9fe94126645c8d8737495a0574ef1518ae74d8")), [], [{ data: hexToBytes("7ce4de2225f041b7f9fec727343a501d99e5b7b58d33f3d4a2cf218d3489959bdec24d13770b5d3bb084b4dac3474f95153e6ecc98f6f0f8ca37a2897b9562ee") }], new tx.PredicateParameters()); - }) - - it('should validate output correctly', () => { - let output = tx.Output.change( - tx.Address.zeroed(), - 1234n, - tx.AssetId.zeroed(), - ); - - tx.check_output(output, 0, []); - }) - - it('should be able to deserialize snapshots', () => { - const snapshots = '../../../fuel-tx/src/transaction/types/input/snapshots'; - fs.readdirSync(snapshots).forEach(file => { - fs.readFile(path.join(snapshots, file), 'utf8', (err, data) => { - expect(err).to.be.null; - let dataBytes = hexToBytes(data.split('---\n').at(-1).trim()); - let inTx = tx.Transaction.from_bytes(dataBytes); - let serialized = inTx.to_bytes(); - expect(serialized.toString()).to.eq(dataBytes.toString()); - }) + + it('should validate input correctly', () => { + let input = tx.Input.coin_signed( + new tx.UtxoId("0xc49d65de61cf04588a764b557d25cc6c6b4bc0d7429227e2a21e61c213b3a3e2:18ab"), + tx.Address.from_bytes(hexToBytes("f1e92c42b90934aa6372e30bc568a326f6e66a1a0288595e6e3fbd392a4f3e6e")), + 10599410012256088338n, + tx.AssetId.from_bytes(hexToBytes("2cafad611543e0265d89f1c2b60d9ebf5d56ad7e23d9827d6b522fd4d6e44bc3")), + new tx.TxPointer("000000000000"), + 0, + new tx.BlockHeight(0), + ); + + tx.check_input(input, 0, tx.Bytes32.from_bytes(hexToBytes("108eae4147d2c1c86ef4c2ab7c9fe94126645c8d8737495a0574ef1518ae74d8")), [], [{data: hexToBytes("7ce4de2225f041b7f9fec727343a501d99e5b7b58d33f3d4a2cf218d3489959bdec24d13770b5d3bb084b4dac3474f95153e6ecc98f6f0f8ca37a2897b9562ee")}], new tx.PredicateParameters(10000n, 10000n, 10000n, 10000n)); + }) + + it('should validate output correctly', () => { + let output = tx.Output.change( + tx.Address.zeroed(), + 1234n, + tx.AssetId.zeroed(), + ); + + tx.check_output(output, 0, []); + }) + + it('should be able to deserialize snapshots', () => { + const snapshots = '../../../fuel-tx/src/transaction/types/input/snapshots'; + fs.readdirSync(snapshots).forEach(file => { + fs.readFile(path.join(snapshots, file), 'utf8', (err, data) => { + expect(err).to.be.null; + let dataBytes = hexToBytes(data.split('---\n').at(-1).trim()); + let inTx = tx.Transaction.from_bytes(dataBytes); + let serialized = inTx.to_bytes(); + expect(serialized.toString()).to.eq(dataBytes.toString()); + }) + }) }) - }) }) diff --git a/.npm/packages/fuel-tx/index.test.mjs b/.npm/packages/fuel-tx/index.test.mjs index dda3bc9981..d0a960efc1 100644 --- a/.npm/packages/fuel-tx/index.test.mjs +++ b/.npm/packages/fuel-tx/index.test.mjs @@ -1,243 +1,243 @@ -import { expect } from 'chai' +import {expect} from 'chai' import * as path from 'node:path' import * as fs from 'node:fs' import * as tx from './dist/web/index.mjs' describe('fuel-tx [mjs]', () => { - it('should export all types', () => { - expect(tx.UtxoId).to.be.ok - expect(tx.TxPointer).to.be.ok - expect(tx.PredicateParameters).to.be.ok - expect(tx.Input).to.be.ok - expect(tx.Output).to.be.ok - expect(tx.Script).to.be.ok - expect(tx.Create).to.be.ok - expect(tx.Mint).to.be.ok - expect(tx.Transaction).to.be.ok - expect(tx.Policies).to.be.ok - }) - - it('should serialize and deserialize UtxoId correctly', () => { - let utxo_id = new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b001a"); - let bytes = utxo_id.to_bytes(); - let utxo_id2 = tx.UtxoId.from_bytes(bytes); - expect(utxo_id.toString()).to.equal(utxo_id2.toString()) - }) - - it('should serialize and deserialize TxPointer correctly', () => { - let utxo_id = new tx.TxPointer("0123456789ab"); - let bytes = utxo_id.to_bytes(); - let utxo_id2 = tx.TxPointer.from_bytes(bytes); - expect(utxo_id.toString()).to.equal(utxo_id2.toString()) - }) - - - it('should serialize and deserialize all input variants correctly', () => { - [ - tx.Input.coin_predicate( - new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b001a"), - tx.Address.zeroed(), - BigInt(1234), - tx.AssetId.zeroed(), - new tx.TxPointer("0123456789ab"), - BigInt(9012), - [1, 2, 3, 4], - [5, 6, 7, 8], - ), - tx.Input.coin_signed( - new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b001a"), - tx.Address.zeroed(), - BigInt(1234), - tx.AssetId.zeroed(), - new tx.TxPointer("0123456789ab"), - 2, - ), - tx.Input.contract( - new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b001a"), - tx.Bytes32.zeroed(), - tx.Bytes32.zeroed(), - new tx.TxPointer("0123456789ab"), - tx.ContractId.zeroed(), - ), - tx.Input.message_coin_signed( - tx.Address.zeroed(), - tx.Address.zeroed(), - BigInt(1234), - tx.Nonce.zeroed(), - 2, - ), - tx.Input.message_coin_predicate( - tx.Address.zeroed(), - tx.Address.zeroed(), - BigInt(1234), - tx.Nonce.zeroed(), - BigInt(1234), - [1, 2, 3, 4], - [5, 6, 7, 8], - ), - tx.Input.message_data_signed( - tx.Address.zeroed(), - tx.Address.zeroed(), - BigInt(1234), - tx.Nonce.zeroed(), - 2, - [1, 2, 3, 4], - ), - tx.Input.message_data_predicate( - tx.Address.zeroed(), - tx.Address.zeroed(), - BigInt(1234), - tx.Nonce.zeroed(), - BigInt(1234), - [0, 1, 2, 3], - [1, 2, 3, 4], - [5, 6, 7, 8], - ), - ].forEach(input => { - let bytes = input.to_bytes(); - let input2 = tx.Input.from_bytes(bytes); - expect(input.toString()).to.equal(input2.toString()) + it('should export all types', () => { + expect(tx.UtxoId).to.be.ok + expect(tx.TxPointer).to.be.ok + expect(tx.PredicateParameters).to.be.ok + expect(tx.Input).to.be.ok + expect(tx.Output).to.be.ok + expect(tx.Script).to.be.ok + expect(tx.Create).to.be.ok + expect(tx.Mint).to.be.ok + expect(tx.Transaction).to.be.ok + expect(tx.Policies).to.be.ok }) - }) - - - it('should serialize and deserialize all output variants correctly', () => { - [ - tx.Output.coin( - tx.Address.zeroed(), - BigInt(1234), - tx.AssetId.zeroed(), - ), - tx.Output.contract( - 2, - tx.Bytes32.zeroed(), - tx.Bytes32.zeroed(), - ), - tx.Output.change( - tx.Address.zeroed(), - BigInt(1234), - tx.AssetId.zeroed(), - ), - tx.Output.variable( - tx.Address.zeroed(), - BigInt(1234), - tx.AssetId.zeroed(), - ), - tx.Output.contract_created( - tx.ContractId.zeroed(), - tx.Bytes32.zeroed(), - ), - ].forEach(output => { - let bytes = output.to_bytes(); - let output2 = tx.Output.from_bytes(bytes); - expect(output.toString()).to.equal(output2.toString()) + + it('should serialize and deserialize UtxoId correctly', () => { + let utxo_id = new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b001a"); + let bytes = utxo_id.to_bytes(); + let utxo_id2 = tx.UtxoId.from_bytes(bytes); + expect(utxo_id.toString()).to.equal(utxo_id2.toString()) }) - }) - - - it('should serialize and deserialize all transaction variants correctly', () => { - [ - [tx.Script, tx.Transaction.script( - 1234n, - [1, 2, 3, 4], - [5, 6, 7, 8], - new tx.Policies(), - [], - [], - [], - )], - [tx.Create, tx.Transaction.create( - 1, - new tx.Policies(), - tx.Salt.zeroed(), - [], - [], - [], - [], - )], - [tx.Mint, tx.Transaction.mint( - new tx.TxPointer("0123456789ab"), - new tx.InputContract( - new tx.UtxoId("0xc49d65de61cf04588a764b557d25cc6c6b4bc0d7429227e2a21e61c213b3a3e2:18ab"), - tx.Bytes32.zeroed(), - tx.Bytes32.zeroed(), - new tx.TxPointer("0123456789ab"), - tx.ContractId.zeroed(), - ), - new tx.OutputContract( - 3, - tx.Bytes32.zeroed(), - tx.Bytes32.zeroed(), - ), - 1234n, - tx.AssetId.zeroed(), - 1234n, - )], - ].forEach(([tx_variant_type, tx_variant]) => { - let bytes = tx_variant.to_bytes(); - let tx_variant2 = tx_variant_type.from_bytes(bytes); - expect(tx_variant.toString()).to.equal(tx_variant2.toString()) - - let wrapped_tx = tx_variant.as_tx(); - let tx_bytes = wrapped_tx.to_bytes(); - let wrapped_tx2 = tx.Transaction.from_bytes(tx_bytes); - expect(wrapped_tx.toString()).to.equal(wrapped_tx2.toString()) + + it('should serialize and deserialize TxPointer correctly', () => { + let utxo_id = new tx.TxPointer("0123456789ab"); + let bytes = utxo_id.to_bytes(); + let utxo_id2 = tx.TxPointer.from_bytes(bytes); + expect(utxo_id.toString()).to.equal(utxo_id2.toString()) }) - }) - // Hex string to byte string conversion. - const hexToBytes = hex => { - if (hex.length % 2 != 0) { - throw new Error("Needs full bytes"); - } - const lookup = "0123456789abcdef"; - let result = new Uint8Array(hex.length / 2); - for (let i = 0; i < result.length; i += 1) { - let high = lookup.indexOf(hex[i * 2]); - let low = lookup.indexOf(hex[i * 2 + 1]); - if (high === -1 || low === -1) { - throw new Error("Invalid hex char"); - } - result[i] = (high << 4) | low; + + it('should serialize and deserialize all input variants correctly', () => { + [ + tx.Input.coin_predicate( + new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b001a"), + tx.Address.zeroed(), + BigInt(1234), + tx.AssetId.zeroed(), + new tx.TxPointer("0123456789ab"), + BigInt(9012), + [1, 2, 3, 4], + [5, 6, 7, 8], + ), + tx.Input.coin_signed( + new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b001a"), + tx.Address.zeroed(), + BigInt(1234), + tx.AssetId.zeroed(), + new tx.TxPointer("0123456789ab"), + 2, + ), + tx.Input.contract( + new tx.UtxoId("0x0c0000000000000000000000000000000000000000000000000000000000000b001a"), + tx.Bytes32.zeroed(), + tx.Bytes32.zeroed(), + new tx.TxPointer("0123456789ab"), + tx.ContractId.zeroed(), + ), + tx.Input.message_coin_signed( + tx.Address.zeroed(), + tx.Address.zeroed(), + BigInt(1234), + tx.Nonce.zeroed(), + 2, + ), + tx.Input.message_coin_predicate( + tx.Address.zeroed(), + tx.Address.zeroed(), + BigInt(1234), + tx.Nonce.zeroed(), + BigInt(1234), + [1, 2, 3, 4], + [5, 6, 7, 8], + ), + tx.Input.message_data_signed( + tx.Address.zeroed(), + tx.Address.zeroed(), + BigInt(1234), + tx.Nonce.zeroed(), + 2, + [1, 2, 3, 4], + ), + tx.Input.message_data_predicate( + tx.Address.zeroed(), + tx.Address.zeroed(), + BigInt(1234), + tx.Nonce.zeroed(), + BigInt(1234), + [0, 1, 2, 3], + [1, 2, 3, 4], + [5, 6, 7, 8], + ), + ].forEach(input => { + let bytes = input.to_bytes(); + let input2 = tx.Input.from_bytes(bytes); + expect(input.toString()).to.equal(input2.toString()) + }) + }) + + + it('should serialize and deserialize all output variants correctly', () => { + [ + tx.Output.coin( + tx.Address.zeroed(), + BigInt(1234), + tx.AssetId.zeroed(), + ), + tx.Output.contract( + 2, + tx.Bytes32.zeroed(), + tx.Bytes32.zeroed(), + ), + tx.Output.change( + tx.Address.zeroed(), + BigInt(1234), + tx.AssetId.zeroed(), + ), + tx.Output.variable( + tx.Address.zeroed(), + BigInt(1234), + tx.AssetId.zeroed(), + ), + tx.Output.contract_created( + tx.ContractId.zeroed(), + tx.Bytes32.zeroed(), + ), + ].forEach(output => { + let bytes = output.to_bytes(); + let output2 = tx.Output.from_bytes(bytes); + expect(output.toString()).to.equal(output2.toString()) + }) + }) + + + it('should serialize and deserialize all transaction variants correctly', () => { + [ + [tx.Script, tx.Transaction.script( + 1234n, + [1, 2, 3, 4], + [5, 6, 7, 8], + new tx.Policies(), + [], + [], + [], + )], + [tx.Create, tx.Transaction.create( + 1, + new tx.Policies(), + tx.Salt.zeroed(), + [], + [], + [], + [], + )], + [tx.Mint, tx.Transaction.mint( + new tx.TxPointer("0123456789ab"), + new tx.InputContract( + new tx.UtxoId("0xc49d65de61cf04588a764b557d25cc6c6b4bc0d7429227e2a21e61c213b3a3e2:18ab"), + tx.Bytes32.zeroed(), + tx.Bytes32.zeroed(), + new tx.TxPointer("0123456789ab"), + tx.ContractId.zeroed(), + ), + new tx.OutputContract( + 3, + tx.Bytes32.zeroed(), + tx.Bytes32.zeroed(), + ), + 1234n, + tx.AssetId.zeroed(), + 1234n, + )], + ].forEach(([tx_variant_type, tx_variant]) => { + let bytes = tx_variant.to_bytes(); + let tx_variant2 = tx_variant_type.from_bytes(bytes); + expect(tx_variant.toString()).to.equal(tx_variant2.toString()) + + let wrapped_tx = tx_variant.as_tx(); + let tx_bytes = wrapped_tx.to_bytes(); + let wrapped_tx2 = tx.Transaction.from_bytes(tx_bytes); + expect(wrapped_tx.toString()).to.equal(wrapped_tx2.toString()) + }) + }) + + // Hex string to byte string conversion. + const hexToBytes = hex => { + if (hex.length % 2 != 0) { + throw new Error("Needs full bytes"); + } + const lookup = "0123456789abcdef"; + let result = new Uint8Array(hex.length / 2); + for (let i = 0; i < result.length; i += 1) { + let high = lookup.indexOf(hex[i * 2]); + let low = lookup.indexOf(hex[i * 2 + 1]); + if (high === -1 || low === -1) { + throw new Error("Invalid hex char"); + } + result[i] = (high << 4) | low; + } + return result; } - return result; - } - - it('should validate input correctly', () => { - let input = tx.Input.coin_signed( - new tx.UtxoId("0xc49d65de61cf04588a764b557d25cc6c6b4bc0d7429227e2a21e61c213b3a3e2:18ab"), - tx.Address.from_bytes(hexToBytes("f1e92c42b90934aa6372e30bc568a326f6e66a1a0288595e6e3fbd392a4f3e6e")), - 10599410012256088338n, - tx.AssetId.from_bytes(hexToBytes("2cafad611543e0265d89f1c2b60d9ebf5d56ad7e23d9827d6b522fd4d6e44bc3")), - new tx.TxPointer("000000000000"), - 0, - new tx.BlockHeight(0), - ); - - tx.check_input(input, 0, tx.Bytes32.from_bytes(hexToBytes("108eae4147d2c1c86ef4c2ab7c9fe94126645c8d8737495a0574ef1518ae74d8")), [], [{ data: hexToBytes("7ce4de2225f041b7f9fec727343a501d99e5b7b58d33f3d4a2cf218d3489959bdec24d13770b5d3bb084b4dac3474f95153e6ecc98f6f0f8ca37a2897b9562ee") }], new tx.PredicateParameters()); - }) - - it('should validate output correctly', () => { - let output = tx.Output.change( - tx.Address.zeroed(), - 1234n, - tx.AssetId.zeroed(), - ); - - tx.check_output(output, 0, []); - }) - - it('should be able to deserialize snapshots', () => { - const snapshots = '../../../fuel-tx/src/transaction/types/input/snapshots'; - fs.readdirSync(snapshots).forEach(file => { - fs.readFile(path.join(snapshots, file), 'utf8', (err, data) => { - expect(err).to.be.null; - let dataBytes = hexToBytes(data.split('---\n').at(-1).trim()); - let inTx = tx.Transaction.from_bytes(dataBytes); - let serialized = inTx.to_bytes(); - expect(serialized.toString()).to.eq(dataBytes.toString()); - }) + + it('should validate input correctly', () => { + let input = tx.Input.coin_signed( + new tx.UtxoId("0xc49d65de61cf04588a764b557d25cc6c6b4bc0d7429227e2a21e61c213b3a3e2:18ab"), + tx.Address.from_bytes(hexToBytes("f1e92c42b90934aa6372e30bc568a326f6e66a1a0288595e6e3fbd392a4f3e6e")), + 10599410012256088338n, + tx.AssetId.from_bytes(hexToBytes("2cafad611543e0265d89f1c2b60d9ebf5d56ad7e23d9827d6b522fd4d6e44bc3")), + new tx.TxPointer("000000000000"), + 0, + new tx.BlockHeight(0), + ); + + tx.check_input(input, 0, tx.Bytes32.from_bytes(hexToBytes("108eae4147d2c1c86ef4c2ab7c9fe94126645c8d8737495a0574ef1518ae74d8")), [], [{data: hexToBytes("7ce4de2225f041b7f9fec727343a501d99e5b7b58d33f3d4a2cf218d3489959bdec24d13770b5d3bb084b4dac3474f95153e6ecc98f6f0f8ca37a2897b9562ee")}], new tx.PredicateParameters(10000n, 10000n, 10000n, 10000n)); + }) + + it('should validate output correctly', () => { + let output = tx.Output.change( + tx.Address.zeroed(), + 1234n, + tx.AssetId.zeroed(), + ); + + tx.check_output(output, 0, []); + }) + + it('should be able to deserialize snapshots', () => { + const snapshots = '../../../fuel-tx/src/transaction/types/input/snapshots'; + fs.readdirSync(snapshots).forEach(file => { + fs.readFile(path.join(snapshots, file), 'utf8', (err, data) => { + expect(err).to.be.null; + let dataBytes = hexToBytes(data.split('---\n').at(-1).trim()); + let inTx = tx.Transaction.from_bytes(dataBytes); + let serialized = inTx.to_bytes(); + expect(serialized.toString()).to.eq(dataBytes.toString()); + }) + }) }) - }) }) diff --git a/CHANGELOG.md b/CHANGELOG.md index d38016452c..76069a568c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). #### Breaking +- [#702](https://github.com/FuelLabs/fuel-vm/pull/702): Wrapped `FeeParameters`, `PredicateParameters`, `TxParameters`, `ScriptParameters` and `ContractParameters` into an enum to support versioning. - [#701](https://github.com/FuelLabs/fuel-vm/pull/701): Wrapped `ConsensusParameters` and `GasCosts` into an enum to support versioning. Moved `block_gas_limit` from `fuel_core_chain_config::ChainConfig` to `ConsensusPataremeters`. Reduced default `MAX_SIZE` to be [110kb](https://github.com/FuelLabs/fuel-core/pull/1761) and `MAX_CONTRACT_SIZE` to be [100kb](https://github.com/FuelLabs/fuel-core/pull/1761). - [#692](https://github.com/FuelLabs/fuel-vm/pull/692): Add GTF getters for tx size and address. - [#698](https://github.com/FuelLabs/fuel-vm/pull/698): Store input, output and witness limits to u16, while keeping the values limited to 255. diff --git a/fuel-tx/src/builder.rs b/fuel-tx/src/builder.rs index e162053f94..a4e9090c3d 100644 --- a/fuel-tx/src/builder.rs +++ b/fuel-tx/src/builder.rs @@ -410,7 +410,7 @@ impl TransactionBuilder { let witness_len = u16::try_from(self.witnesses().len()) .expect("The number of witnesses can't exceed `u16::MAX`"); - if u32::from(witness_len) > self.params.tx_params().max_witnesses { + if u32::from(witness_len) > self.params.tx_params().max_witnesses() { panic!("Max witnesses exceeded"); } diff --git a/fuel-tx/src/tests/valid_cases/input.rs b/fuel-tx/src/tests/valid_cases/input.rs index 8a0e026d4e..5e504b7dab 100644 --- a/fuel-tx/src/tests/valid_cases/input.rs +++ b/fuel-tx/src/tests/valid_cases/input.rs @@ -380,7 +380,7 @@ fn message_metadata() { assert_eq!(ValidityError::InputPredicateOwner { index: 1 }, err); - let data = vec![0xff; PREDICATE_PARAMS.max_message_data_length as usize + 1]; + let data = vec![0xff; PREDICATE_PARAMS.max_message_data_length() as usize + 1]; let err = Input::message_data_signed( rng.gen(), @@ -417,7 +417,7 @@ fn message_metadata() { assert_eq!(ValidityError::InputMessageDataLength { index: 1 }, err,); - let predicate = vec![0xff; PREDICATE_PARAMS.max_predicate_length as usize + 1]; + let predicate = vec![0xff; PREDICATE_PARAMS.max_predicate_length() as usize + 1]; let err = Input::message_data_predicate( rng.gen(), @@ -435,7 +435,7 @@ fn message_metadata() { assert_eq!(ValidityError::InputPredicateLength { index: 1 }, err,); let predicate_data = - vec![0xff; PREDICATE_PARAMS.max_predicate_data_length as usize + 1]; + vec![0xff; PREDICATE_PARAMS.max_predicate_data_length() as usize + 1]; let err = Input::message_data_predicate( rng.gen(), @@ -503,7 +503,7 @@ fn message_message_coin() { assert_eq!(ValidityError::InputPredicateOwner { index: 1 }, err); - let predicate = vec![0xff; PREDICATE_PARAMS.max_predicate_length as usize + 1]; + let predicate = vec![0xff; PREDICATE_PARAMS.max_predicate_length() as usize + 1]; let err = Input::message_coin_predicate( rng.gen(), @@ -520,7 +520,7 @@ fn message_message_coin() { assert_eq!(ValidityError::InputPredicateLength { index: 1 }, err,); let predicate_data = - vec![0xff; PREDICATE_PARAMS.max_predicate_data_length as usize + 1]; + vec![0xff; PREDICATE_PARAMS.max_predicate_data_length() as usize + 1]; let err = Input::message_coin_predicate( rng.gen(), diff --git a/fuel-tx/src/tests/valid_cases/transaction.rs b/fuel-tx/src/tests/valid_cases/transaction.rs index 0a21013985..61d7db9656 100644 --- a/fuel-tx/src/tests/valid_cases/transaction.rs +++ b/fuel-tx/src/tests/valid_cases/transaction.rs @@ -55,7 +55,7 @@ fn gas_limit() { .expect("Failed to validate transaction"); let err = Transaction::script( - TX_PARAMS.max_gas_per_tx + 1, + TX_PARAMS.max_gas_per_tx() + 1, generate_bytes(rng), generate_bytes(rng), Policies::new().with_max_fee(0), @@ -328,11 +328,11 @@ fn max_iow() { rng.gen(), ); - while builder.outputs().len() < TX_PARAMS.max_outputs as usize { + while builder.outputs().len() < TX_PARAMS.max_outputs() as usize { builder.add_output(Output::coin(rng.gen(), rng.gen(), asset_id)); } - while builder.witnesses().len() < TX_PARAMS.max_witnesses as usize { + while builder.witnesses().len() < TX_PARAMS.max_witnesses() as usize { builder.add_witness(generate_bytes(rng).into()); } @@ -348,7 +348,7 @@ fn max_iow() { builder.maturity(maturity); let secrets = - cmp::min(TX_PARAMS.max_inputs as u32, TX_PARAMS.max_witnesses - 1) as usize; + cmp::min(TX_PARAMS.max_inputs() as u32, TX_PARAMS.max_witnesses() - 1) as usize; let secrets: Vec = (0..secrets - builder.inputs().len()) .map(|_| SecretKey::random(rng)) .collect(); @@ -358,11 +358,11 @@ fn max_iow() { builder.add_unsigned_coin_input(*k, rng.gen(), rng.gen(), asset_id, rng.gen()); }); - while builder.outputs().len() < TX_PARAMS.max_outputs as usize { + while builder.outputs().len() < TX_PARAMS.max_outputs() as usize { builder.add_output(Output::coin(rng.gen(), rng.gen(), asset_id)); } - while builder.witnesses().len() < TX_PARAMS.max_witnesses as usize { + while builder.witnesses().len() < TX_PARAMS.max_witnesses() as usize { builder.add_witness(generate_bytes(rng).into()); } @@ -377,7 +377,7 @@ fn max_iow() { builder.maturity(maturity); - let secrets: Vec = (0..1 + TX_PARAMS.max_inputs as usize + let secrets: Vec = (0..1 + TX_PARAMS.max_inputs() as usize - builder.inputs().len()) .map(|_| SecretKey::random(rng)) .collect(); @@ -386,11 +386,11 @@ fn max_iow() { builder.add_unsigned_coin_input(*k, rng.gen(), rng.gen(), rng.gen(), rng.gen()); }); - while builder.outputs().len() < TX_PARAMS.max_outputs as usize { + while builder.outputs().len() < TX_PARAMS.max_outputs() as usize { builder.add_output(Output::coin(rng.gen(), rng.gen(), rng.gen())); } - while builder.witnesses().len() < TX_PARAMS.max_witnesses as usize { + while builder.witnesses().len() < TX_PARAMS.max_witnesses() as usize { builder.add_witness(generate_bytes(rng).into()); } @@ -407,7 +407,7 @@ fn max_iow() { builder.maturity(maturity); - let secrets: Vec = (0..TX_PARAMS.max_inputs as usize + let secrets: Vec = (0..TX_PARAMS.max_inputs() as usize - builder.inputs().len()) .map(|_| SecretKey::random(rng)) .collect(); @@ -416,11 +416,11 @@ fn max_iow() { builder.add_unsigned_coin_input(*k, rng.gen(), rng.gen(), rng.gen(), rng.gen()); }); - while builder.outputs().len() < 1 + TX_PARAMS.max_outputs as usize { + while builder.outputs().len() < 1 + TX_PARAMS.max_outputs() as usize { builder.add_output(Output::coin(rng.gen(), rng.gen(), rng.gen())); } - while builder.witnesses().len() < TX_PARAMS.max_witnesses as usize { + while builder.witnesses().len() < TX_PARAMS.max_witnesses() as usize { builder.add_witness(generate_bytes(rng).into()); } @@ -437,7 +437,7 @@ fn max_iow() { builder.maturity(maturity); - let secrets: Vec = (0..TX_PARAMS.max_inputs as usize + let secrets: Vec = (0..TX_PARAMS.max_inputs() as usize - builder.inputs().len()) .map(|_| SecretKey::random(rng)) .collect(); @@ -446,11 +446,11 @@ fn max_iow() { builder.add_unsigned_coin_input(*k, rng.gen(), rng.gen(), rng.gen(), rng.gen()); }); - while builder.outputs().len() < TX_PARAMS.max_outputs as usize { + while builder.outputs().len() < TX_PARAMS.max_outputs() as usize { builder.add_output(Output::coin(rng.gen(), rng.gen(), rng.gen())); } - while builder.witnesses().len() < 1 + TX_PARAMS.max_witnesses as usize { + while builder.witnesses().len() < 1 + TX_PARAMS.max_witnesses() as usize { builder.add_witness(generate_bytes(rng).into()); } @@ -542,8 +542,8 @@ fn script__check__happy_path() { let asset_id: AssetId = rng.gen(); TransactionBuilder::script( - vec![0xfa; SCRIPT_PARAMS.max_script_length as usize], - vec![0xfb; SCRIPT_PARAMS.max_script_data_length as usize], + vec![0xfa; SCRIPT_PARAMS.max_script_length() as usize], + vec![0xfb; SCRIPT_PARAMS.max_script_data_length() as usize], ) .maturity(maturity) .add_unsigned_coin_input(secret, rng.gen(), rng.gen(), asset_id, rng.gen()) @@ -564,8 +564,8 @@ fn script__check__cannot_create_contract() { let asset_id: AssetId = rng.gen(); let err = TransactionBuilder::script( - vec![0xfa; SCRIPT_PARAMS.max_script_length as usize], - vec![0xfb; SCRIPT_PARAMS.max_script_data_length as usize], + vec![0xfa; SCRIPT_PARAMS.max_script_length() as usize], + vec![0xfb; SCRIPT_PARAMS.max_script_data_length() as usize], ) .maturity(maturity) .add_unsigned_coin_input(secret, rng.gen(), rng.gen(), asset_id, rng.gen()) @@ -591,8 +591,8 @@ fn script__check__errors_if_script_too_long() { let asset_id: AssetId = rng.gen(); let err = TransactionBuilder::script( - vec![0xfa; 1 + SCRIPT_PARAMS.max_script_length as usize], - vec![0xfb; SCRIPT_PARAMS.max_script_data_length as usize], + vec![0xfa; 1 + SCRIPT_PARAMS.max_script_length() as usize], + vec![0xfb; SCRIPT_PARAMS.max_script_data_length() as usize], ) .maturity(maturity) .add_unsigned_coin_input(secret, rng.gen(), rng.gen(), asset_id, rng.gen()) @@ -615,8 +615,8 @@ fn script__check__errors_if_script_data_too_long() { let asset_id: AssetId = rng.gen(); let err = TransactionBuilder::script( - vec![0xfa; SCRIPT_PARAMS.max_script_length as usize], - vec![0xfb; 1 + SCRIPT_PARAMS.max_script_data_length as usize], + vec![0xfa; SCRIPT_PARAMS.max_script_length() as usize], + vec![0xfb; 1 + SCRIPT_PARAMS.max_script_data_length() as usize], ) .maturity(maturity) .add_unsigned_coin_input(secret, rng.gen(), rng.gen(), asset_id, rng.gen()) @@ -843,7 +843,7 @@ fn create__check__something_else() { let secret = SecretKey::random(rng); TransactionBuilder::create( - vec![0xfa; CONTRACT_PARAMS.contract_max_size as usize / 4].into(), + vec![0xfa; CONTRACT_PARAMS.contract_max_size() as usize / 4].into(), rng.gen(), vec![], ) @@ -865,7 +865,7 @@ fn create__check__errors_if_witness_bytecode_too_long() { let secret = SecretKey::random(rng); let err = TransactionBuilder::create( - vec![0xfa; 1 + CONTRACT_PARAMS.contract_max_size as usize].into(), + vec![0xfa; 1 + CONTRACT_PARAMS.contract_max_size() as usize].into(), rng.gen(), vec![], ) @@ -940,7 +940,7 @@ fn create__check__can_max_out_storage_slots() { let secret = SecretKey::random(rng); - let storage_slots = (0..CONTRACT_PARAMS.max_storage_slots) + let storage_slots = (0..CONTRACT_PARAMS.max_storage_slots()) .map(|i| { let mut slot_data = StorageSlot::default().to_bytes(); slot_data[..8].copy_from_slice(&i.to_be_bytes()); // Force ordering @@ -972,7 +972,7 @@ fn create__check__cannot_exceed_max_storage_slot() { let secret = SecretKey::random(rng); // Test max slots can't be exceeded - let mut storage_slots_max = (0..CONTRACT_PARAMS.max_storage_slots) + let mut storage_slots_max = (0..CONTRACT_PARAMS.max_storage_slots()) .map(|i| { let mut slot_data = StorageSlot::default().to_bytes(); slot_data[..8].copy_from_slice(&i.to_be_bytes()); // Force ordering @@ -1007,7 +1007,7 @@ fn script__check__transaction_at_maximum_size_is_valid() { let mut params = test_params(); let max_size = 1024usize; let mut tx_params = *params.tx_params(); - tx_params.max_size = max_size as u64; + tx_params.set_max_size(max_size as u64); params.set_tx_params(tx_params); let base_size = { @@ -1041,7 +1041,7 @@ fn script__check__transaction_exceeding_maximum_size_is_invalid() { let mut params = test_params(); let max_size = 1024usize; let mut tx_params = *params.tx_params(); - tx_params.max_size = max_size as u64; + tx_params.set_max_size(max_size as u64); params.set_tx_params(tx_params); let base_size = { diff --git a/fuel-tx/src/transaction/consensus_parameters.rs b/fuel-tx/src/transaction/consensus_parameters.rs index 0db04fd833..576ae54a59 100644 --- a/fuel-tx/src/transaction/consensus_parameters.rs +++ b/fuel-tx/src/transaction/consensus_parameters.rs @@ -71,63 +71,63 @@ impl ConsensusParameters { } /// Get the transaction parameters - pub fn tx_params(&self) -> &TxParameters { + pub const fn tx_params(&self) -> &TxParameters { match self { Self::V1(params) => ¶ms.tx_params, } } /// Get the predicate parameters - pub fn predicate_params(&self) -> &PredicateParameters { + pub const fn predicate_params(&self) -> &PredicateParameters { match self { Self::V1(params) => ¶ms.predicate_params, } } /// Get the script parameters - pub fn script_params(&self) -> &ScriptParameters { + pub const fn script_params(&self) -> &ScriptParameters { match self { Self::V1(params) => ¶ms.script_params, } } /// Get the contract parameters - pub fn contract_params(&self) -> &ContractParameters { + pub const fn contract_params(&self) -> &ContractParameters { match self { Self::V1(params) => ¶ms.contract_params, } } /// Get the fee parameters - pub fn fee_params(&self) -> &FeeParameters { + pub const fn fee_params(&self) -> &FeeParameters { match self { Self::V1(params) => ¶ms.fee_params, } } /// Get the chain ID - pub fn chain_id(&self) -> ChainId { + pub const fn chain_id(&self) -> ChainId { match self { Self::V1(params) => params.chain_id, } } /// Get the gas costs - pub fn gas_costs(&self) -> &GasCosts { + pub const fn gas_costs(&self) -> &GasCosts { match self { Self::V1(params) => ¶ms.gas_costs, } } /// Get the base asset ID - pub fn base_asset_id(&self) -> &AssetId { + pub const fn base_asset_id(&self) -> &AssetId { match self { Self::V1(params) => ¶ms.base_asset_id, } } /// Get the block gas limit - pub fn block_gas_limit(&self) -> u64 { + pub const fn block_gas_limit(&self) -> u64 { match self { Self::V1(params) => params.block_gas_limit, } @@ -250,7 +250,7 @@ impl ConsensusParametersV1 { chain_id, gas_costs: GasCosts::default(), base_asset_id: Default::default(), - block_gas_limit: TxParameters::DEFAULT.max_gas_per_tx, + block_gas_limit: TxParameters::DEFAULT.max_gas_per_tx(), privileged_address: Default::default(), } } @@ -268,37 +268,185 @@ impl From for ConsensusParameters { } } +/// The versioned fee parameters. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum FeeParameters { + V1(FeeParametersV1), +} + +impl FeeParameters { + /// Default fee parameters just for testing. + pub const DEFAULT: Self = Self::V1(FeeParametersV1::DEFAULT); + + /// Replace the gas price factor with the given argument + pub const fn with_gas_price_factor(self, gas_price_factor: u64) -> Self { + match self { + Self::V1(mut params) => { + params.gas_price_factor = gas_price_factor; + Self::V1(params) + } + } + } + + pub const fn with_gas_per_byte(self, gas_per_byte: u64) -> Self { + match self { + Self::V1(mut params) => { + params.gas_per_byte = gas_per_byte; + Self::V1(params) + } + } + } +} + +impl FeeParameters { + /// Get the gas price factor + pub const fn gas_price_factor(&self) -> u64 { + match self { + Self::V1(params) => params.gas_price_factor, + } + } + + /// Get the gas per byte + pub const fn gas_per_byte(&self) -> u64 { + match self { + Self::V1(params) => params.gas_per_byte, + } + } +} + +impl Default for FeeParameters { + fn default() -> Self { + Self::DEFAULT + } +} + +impl From for FeeParameters { + fn from(params: FeeParametersV1) -> Self { + Self::V1(params) + } +} + /// Consensus configurable parameters used for verifying transactions #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(default))] -pub struct FeeParameters { +pub struct FeeParametersV1 { /// Factor to convert between gas and transaction assets value. pub gas_price_factor: u64, /// A fixed ratio linking metered bytes to gas price pub gas_per_byte: u64, } -impl FeeParameters { - /// Default consensus parameters with settings suggested in fuel-specs - pub const DEFAULT: Self = Self { +impl FeeParametersV1 { + /// Default fee parameters just for tests. + pub const DEFAULT: Self = FeeParametersV1 { gas_price_factor: 1_000_000_000, gas_per_byte: 4, }; +} - /// Replace the gas price factor with the given argument - pub const fn with_gas_price_factor(mut self, gas_price_factor: u64) -> Self { - self.gas_price_factor = gas_price_factor; - self +impl Default for FeeParametersV1 { + fn default() -> Self { + Self::DEFAULT } +} + +/// Versioned predicate parameters. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum PredicateParameters { + V1(PredicateParametersV1), +} + +impl PredicateParameters { + /// Default parameters just for testing. + pub const DEFAULT: Self = Self::V1(PredicateParametersV1::DEFAULT); - pub const fn with_gas_per_byte(mut self, gas_per_byte: u64) -> Self { - self.gas_per_byte = gas_per_byte; - self + /// Replace the max predicate length with the given argument + pub const fn with_max_predicate_length(self, max_predicate_length: u64) -> Self { + match self { + Self::V1(mut params) => { + params.max_predicate_length = max_predicate_length; + Self::V1(params) + } + } + } + + /// Replace the max predicate data length with the given argument + pub const fn with_max_predicate_data_length( + self, + max_predicate_data_length: u64, + ) -> Self { + match self { + Self::V1(mut params) => { + params.max_predicate_data_length = max_predicate_data_length; + Self::V1(params) + } + } + } + + /// Replace the max message data length with the given argument + pub const fn with_max_message_data_length( + self, + max_message_data_length: u64, + ) -> Self { + match self { + Self::V1(mut params) => { + params.max_message_data_length = max_message_data_length; + Self::V1(params) + } + } + } + + /// Replace the max gas per predicate. + pub const fn with_max_gas_per_predicate(self, max_gas_per_predicate: u64) -> Self { + match self { + Self::V1(mut params) => { + params.max_gas_per_predicate = max_gas_per_predicate; + Self::V1(params) + } + } } } -impl Default for FeeParameters { +impl PredicateParameters { + /// Get the maximum predicate length + pub const fn max_predicate_length(&self) -> u64 { + match self { + Self::V1(params) => params.max_predicate_length, + } + } + + /// Get the maximum predicate data length + pub const fn max_predicate_data_length(&self) -> u64 { + match self { + Self::V1(params) => params.max_predicate_data_length, + } + } + + /// Get the maximum message data length + pub const fn max_message_data_length(&self) -> u64 { + match self { + Self::V1(params) => params.max_message_data_length, + } + } + + /// Get the maximum gas per predicate + pub const fn max_gas_per_predicate(&self) -> u64 { + match self { + Self::V1(params) => params.max_gas_per_predicate, + } + } +} + +impl From for PredicateParameters { + fn from(params: PredicateParametersV1) -> Self { + Self::V1(params) + } +} + +impl Default for PredicateParameters { fn default() -> Self { Self::DEFAULT } @@ -306,10 +454,9 @@ impl Default for FeeParameters { /// Consensus configurable parameters used for verifying transactions #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(default))] -pub struct PredicateParameters { +pub struct PredicateParametersV1 { /// Maximum length of predicate, in instructions. pub max_predicate_length: u64, /// Maximum length of predicate data, in bytes. @@ -320,50 +467,156 @@ pub struct PredicateParameters { pub max_gas_per_predicate: u64, } -impl PredicateParameters { - /// Default consensus parameters with settings suggested in fuel-specs +impl PredicateParametersV1 { + /// Default parameters just for testing. pub const DEFAULT: Self = Self { max_predicate_length: 1024 * 1024, max_predicate_data_length: 1024 * 1024, max_message_data_length: 1024 * 1024, max_gas_per_predicate: MAX_GAS, }; +} - /// Replace the max predicate length with the given argument - pub const fn with_max_predicate_length(mut self, max_predicate_length: u64) -> Self { - self.max_predicate_length = max_predicate_length; - self +impl Default for PredicateParametersV1 { + fn default() -> Self { + Self::DEFAULT } +} - /// Replace the max predicate data length with the given argument - pub const fn with_max_predicate_data_length( - mut self, - max_predicate_data_length: u64, - ) -> Self { - self.max_predicate_data_length = max_predicate_data_length; - self +/// Versioned transaction parameters. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum TxParameters { + /// Version 1 of the transaction parameters. + V1(TxParametersV1), +} + +impl TxParameters { + /// Default parameters just for testing. + pub const DEFAULT: Self = Self::V1(TxParametersV1::DEFAULT); + + /// Transaction memory offset in VM runtime + pub const fn tx_offset(&self) -> usize { + Bytes32::LEN // Tx ID + + AssetId::LEN // Base asset ID + // Asset ID/Balance coin input pairs + + self.max_inputs() as usize * (AssetId::LEN + WORD_SIZE) + + WORD_SIZE // Tx size } - /// Replace the max message data length with the given argument - pub const fn with_max_message_data_length( - mut self, - max_message_data_length: u64, - ) -> Self { - self.max_message_data_length = max_message_data_length; - self + /// Replace the max inputs with the given argument + pub const fn with_max_inputs(self, max_inputs: u16) -> Self { + match self { + Self::V1(mut params) => { + params.max_inputs = max_inputs; + Self::V1(params) + } + } + } + + /// Replace the max outputs with the given argument + pub const fn with_max_outputs(self, max_outputs: u16) -> Self { + match self { + Self::V1(mut params) => { + params.max_outputs = max_outputs; + Self::V1(params) + } + } + } + + /// Replace the max witnesses with the given argument + pub const fn with_max_witnesses(self, max_witnesses: u32) -> Self { + match self { + Self::V1(mut params) => { + params.max_witnesses = max_witnesses; + Self::V1(params) + } + } + } + + /// Replace the max gas per transaction with the given argument + pub const fn with_max_gas_per_tx(self, max_gas_per_tx: u64) -> Self { + match self { + Self::V1(mut params) => { + params.max_gas_per_tx = max_gas_per_tx; + Self::V1(params) + } + } + } + + /// Replace the max size of the transaction with the given argument + pub const fn with_max_size(self, max_size: u64) -> Self { + match self { + Self::V1(mut params) => { + params.max_size = max_size; + Self::V1(params) + } + } } } -impl Default for PredicateParameters { +impl TxParameters { + /// Get the maximum number of inputs + pub const fn max_inputs(&self) -> u16 { + match self { + Self::V1(params) => params.max_inputs, + } + } + + /// Get the maximum number of outputs + pub const fn max_outputs(&self) -> u16 { + match self { + Self::V1(params) => params.max_outputs, + } + } + + /// Get the maximum number of witnesses + pub const fn max_witnesses(&self) -> u32 { + match self { + Self::V1(params) => params.max_witnesses, + } + } + + /// Get the maximum gas per transaction + pub const fn max_gas_per_tx(&self) -> u64 { + match self { + Self::V1(params) => params.max_gas_per_tx, + } + } + + /// Get the maximum size in bytes + pub const fn max_size(&self) -> u64 { + match self { + Self::V1(params) => params.max_size, + } + } +} + +impl Default for TxParameters { fn default() -> Self { Self::DEFAULT } } +#[cfg(feature = "builder")] +impl TxParameters { + pub fn set_max_size(&mut self, max_size: u64) { + match self { + Self::V1(params) => params.max_size = max_size, + } + } +} + +impl From for TxParameters { + fn from(params: TxParametersV1) -> Self { + Self::V1(params) + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(default))] -pub struct TxParameters { +pub struct TxParametersV1 { /// Maximum number of inputs. pub max_inputs: u16, /// Maximum number of outputs. @@ -376,8 +629,8 @@ pub struct TxParameters { pub max_size: u64, } -impl TxParameters { - /// Default consensus parameters with settings suggested in fuel-specs +impl TxParametersV1 { + /// Default parameters just for testing. pub const DEFAULT: Self = Self { max_inputs: 255, max_outputs: 255, @@ -385,48 +638,69 @@ impl TxParameters { max_gas_per_tx: MAX_GAS, max_size: MAX_SIZE, }; +} - /// Transaction memory offset in VM runtime - pub const fn tx_offset(&self) -> usize { - Bytes32::LEN // Tx ID - + AssetId::LEN // Base asset ID - // Asset ID/Balance coin input pairs - + self.max_inputs as usize * (AssetId::LEN + WORD_SIZE) - + WORD_SIZE // Tx size +impl Default for TxParametersV1 { + fn default() -> Self { + Self::DEFAULT } +} - /// Replace the max inputs with the given argument - pub const fn with_max_inputs(mut self, max_inputs: u16) -> Self { - self.max_inputs = max_inputs; - self +/// Versioned script parameters. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum ScriptParameters { + V1(ScriptParametersV1), +} + +impl ScriptParameters { + /// Default parameters just for testing. + pub const DEFAULT: Self = Self::V1(ScriptParametersV1::DEFAULT); + + /// Replace the max script length with the given argument + pub const fn with_max_script_length(self, max_script_length: u64) -> Self { + match self { + Self::V1(mut params) => { + params.max_script_length = max_script_length; + Self::V1(params) + } + } } - /// Replace the max outputs with the given argument - pub const fn with_max_outputs(mut self, max_outputs: u16) -> Self { - self.max_outputs = max_outputs; - self + /// Replace the max script data length with the given argument + pub const fn with_max_script_data_length(self, max_script_data_length: u64) -> Self { + match self { + Self::V1(mut params) => { + params.max_script_data_length = max_script_data_length; + Self::V1(params) + } + } } +} - /// Replace the max witnesses with the given argument - pub const fn with_max_witnesses(mut self, max_witnesses: u32) -> Self { - self.max_witnesses = max_witnesses; - self +impl ScriptParameters { + /// Get the maximum script length + pub const fn max_script_length(&self) -> u64 { + match self { + Self::V1(params) => params.max_script_length, + } } - /// Replace the max gas per transaction with the given argument - pub const fn with_max_gas_per_tx(mut self, max_gas_per_tx: u64) -> Self { - self.max_gas_per_tx = max_gas_per_tx; - self + /// Get the maximum script data length + pub const fn max_script_data_length(&self) -> u64 { + match self { + Self::V1(params) => params.max_script_data_length, + } } +} - /// Replace the max size of the transaction with the given argument - pub const fn with_max_size(mut self, max_size: u64) -> Self { - self.max_size = max_size; - self +impl From for ScriptParameters { + fn from(params: ScriptParametersV1) -> Self { + Self::V1(params) } } -impl Default for TxParameters { +impl Default for ScriptParameters { fn default() -> Self { Self::DEFAULT } @@ -435,37 +709,82 @@ impl Default for TxParameters { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(default))] -pub struct ScriptParameters { +pub struct ScriptParametersV1 { /// Maximum length of script, in instructions. pub max_script_length: u64, /// Maximum length of script data, in bytes. pub max_script_data_length: u64, } -impl ScriptParameters { - /// Default consensus parameters with settings suggested in fuel-specs +impl ScriptParametersV1 { + /// Default parameters just for testing. pub const DEFAULT: Self = Self { max_script_length: 1024 * 1024, max_script_data_length: 1024 * 1024, }; +} - /// Replace the max script length with the given argument - pub const fn with_max_script_length(mut self, max_script_length: u64) -> Self { - self.max_script_length = max_script_length; - self +impl Default for ScriptParametersV1 { + fn default() -> Self { + Self::DEFAULT } +} - /// Replace the max script data length with the given argument - pub const fn with_max_script_data_length( - mut self, - max_script_data_length: u64, - ) -> Self { - self.max_script_data_length = max_script_data_length; - self +/// Versioned contract parameters. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum ContractParameters { + V1(ContractParametersV1), +} + +impl ContractParameters { + /// Default parameters just for testing. + pub const DEFAULT: Self = Self::V1(ContractParametersV1::DEFAULT); + + /// Replace the max contract size with the given argument + pub const fn with_contract_max_size(self, contract_max_size: u64) -> Self { + match self { + Self::V1(mut params) => { + params.contract_max_size = contract_max_size; + Self::V1(params) + } + } + } + + /// Replace the max storage slots with the given argument + pub const fn with_max_storage_slots(self, max_storage_slots: u64) -> Self { + match self { + Self::V1(mut params) => { + params.max_storage_slots = max_storage_slots; + Self::V1(params) + } + } } } -impl Default for ScriptParameters { +impl ContractParameters { + /// Get the maximum contract size + pub const fn contract_max_size(&self) -> u64 { + match self { + Self::V1(params) => params.contract_max_size, + } + } + + /// Get the maximum storage slots + pub const fn max_storage_slots(&self) -> u64 { + match self { + Self::V1(params) => params.max_storage_slots, + } + } +} + +impl From for ContractParameters { + fn from(params: ContractParametersV1) -> Self { + Self::V1(params) + } +} + +impl Default for ContractParameters { fn default() -> Self { Self::DEFAULT } @@ -474,7 +793,7 @@ impl Default for ScriptParameters { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(default))] -pub struct ContractParameters { +pub struct ContractParametersV1 { /// Maximum contract size, in bytes. pub contract_max_size: u64, @@ -482,43 +801,52 @@ pub struct ContractParameters { pub max_storage_slots: u64, } -impl ContractParameters { - /// Default consensus parameters with settings suggested in fuel-specs +impl ContractParametersV1 { + /// Default parameters just for testing. pub const DEFAULT: Self = Self { contract_max_size: 100 * 1024, max_storage_slots: 255, }; - - /// Replace the max contract size with the given argument - pub const fn with_contract_max_size(mut self, contract_max_size: u64) -> Self { - self.contract_max_size = contract_max_size; - self - } - - /// Replace the max storage slots with the given argument - pub const fn with_max_storage_slots(mut self, max_storage_slots: u64) -> Self { - self.max_storage_slots = max_storage_slots; - self - } } -impl Default for ContractParameters { +impl Default for ContractParametersV1 { fn default() -> Self { Self::DEFAULT } } #[cfg(feature = "typescript")] -mod typescript { +pub mod typescript { use wasm_bindgen::prelude::*; - use super::PredicateParameters; + use super::PredicateParameters as PredicateParametersRust; + + #[derive(Clone, Debug, PartialEq, Eq, Hash)] + #[cfg_attr(feature = "typescript", wasm_bindgen::prelude::wasm_bindgen)] + pub struct PredicateParameters(alloc::boxed::Box); + + impl AsRef for PredicateParameters { + fn as_ref(&self) -> &PredicateParametersRust { + &self.0 + } + } #[wasm_bindgen] impl PredicateParameters { #[wasm_bindgen(constructor)] - pub fn typescript_new() -> Self { - Self::DEFAULT + pub fn typescript_new( + max_predicate_length: u64, + max_predicate_data_length: u64, + max_message_data_length: u64, + max_gas_per_predicate: u64, + ) -> Self { + let params = PredicateParametersRust::default() + .with_max_predicate_length(max_predicate_length) + .with_max_predicate_data_length(max_predicate_data_length) + .with_max_message_data_length(max_message_data_length) + .with_max_gas_per_predicate(max_gas_per_predicate); + + PredicateParameters(params.into()) } } } diff --git a/fuel-tx/src/transaction/fee.rs b/fuel-tx/src/transaction/fee.rs index e8e7289c92..254398eaaa 100644 --- a/fuel-tx/src/transaction/fee.rs +++ b/fuel-tx/src/transaction/fee.rs @@ -126,7 +126,7 @@ pub trait Chargeable: field::Inputs + field::Witnesses + field::Policies { let vm_initialization_gas = gas_costs.vm_initialization().resolve(bytes_size as Word); - let bytes_gas = bytes_size as u64 * fee.gas_per_byte; + let bytes_gas = bytes_size as u64 * fee.gas_per_byte(); // It's okay to saturate because we have the `max_gas_per_tx` rule for transaction // validity. In the production, the value always will be lower than // `u64::MAX`. @@ -143,7 +143,7 @@ pub trait Chargeable: field::Inputs + field::Witnesses + field::Policies { let remaining_allowed_witness_gas = self .witness_limit() .saturating_sub(self.witnesses().size_dynamic() as u64) - .saturating_mul(fee.gas_per_byte); + .saturating_mul(fee.gas_per_byte()); self.min_gas(gas_costs, fee) .saturating_add(remaining_allowed_witness_gas) @@ -160,7 +160,7 @@ pub trait Chargeable: field::Inputs + field::Witnesses + field::Policies { let gas_fee = gas_to_fee( self.min_gas(gas_costs, fee), gas_price, - fee.gas_price_factor, + fee.gas_price_factor(), ); gas_fee.saturating_add(tip as u128) } @@ -178,7 +178,7 @@ pub trait Chargeable: field::Inputs + field::Witnesses + field::Policies { let gas_fee = gas_to_fee( self.max_gas(gas_costs, fee), gas_price, - fee.gas_price_factor, + fee.gas_price_factor(), ); gas_fee.saturating_add(tip as u128) } @@ -200,7 +200,7 @@ pub trait Chargeable: field::Inputs + field::Witnesses + field::Policies { let total_used_gas = min_gas.saturating_add(used_gas); let tip = self.policies().get(PolicyType::Tip).unwrap_or(0); - let used_fee = gas_to_fee(total_used_gas, gas_price, fee.gas_price_factor) + let used_fee = gas_to_fee(total_used_gas, gas_price, fee.gas_price_factor()) .saturating_add(tip as u128); // It is okay to saturate everywhere above because it only can decrease the value diff --git a/fuel-tx/src/transaction/types/create.rs b/fuel-tx/src/transaction/types/create.rs index 60ac6c8bc8..585edd6ac9 100644 --- a/fuel-tx/src/transaction/types/create.rs +++ b/fuel-tx/src/transaction/types/create.rs @@ -235,7 +235,7 @@ impl FormatValidityChecks for Create { .map(|w| w.as_ref().len() as Word) .ok_or(ValidityError::TransactionCreateBytecodeWitnessIndex)?; - if bytecode_witness_len > contract_params.contract_max_size + if bytecode_witness_len > contract_params.contract_max_size() || bytecode_witness_len / 4 != self.bytecode_length { return Err(ValidityError::TransactionCreateBytecodeLen); @@ -243,7 +243,7 @@ impl FormatValidityChecks for Create { // Restrict to subset of u16::MAX, allowing this to be increased in the future // in a non-breaking way. - if self.storage_slots.len() as u64 > contract_params.max_storage_slots { + if self.storage_slots.len() as u64 > contract_params.max_storage_slots() { return Err(ValidityError::TransactionCreateStorageSlotMax); } diff --git a/fuel-tx/src/transaction/types/script.rs b/fuel-tx/src/transaction/types/script.rs index 30a40579b8..6e61b59fb1 100644 --- a/fuel-tx/src/transaction/types/script.rs +++ b/fuel-tx/src/transaction/types/script.rs @@ -85,7 +85,7 @@ impl Default for Script { // We want to use any values much less than `max_gas_per_tx` // to avoid the `TransactionMaxGasExceeded` error. For example, // `max_gas_per_tx / 4`. - script_gas_limit: TxParameters::DEFAULT.max_gas_per_tx / 4, + script_gas_limit: TxParameters::DEFAULT.max_gas_per_tx() / 4, script, script_data: Default::default(), policies: Policies::new() @@ -145,7 +145,7 @@ impl Chargeable for Script { let remaining_allowed_witness = self .witness_limit() .saturating_sub(self.witnesses().size_dynamic() as u64) - .saturating_mul(fee.gas_per_byte); + .saturating_mul(fee.gas_per_byte()); self.min_gas(gas_costs, fee) .saturating_add(remaining_allowed_witness) @@ -191,11 +191,11 @@ impl FormatValidityChecks for Script { ) -> Result<(), ValidityError> { check_common_part(self, block_height, consensus_params)?; let script_params = consensus_params.script_params(); - if self.script.len() as u64 > script_params.max_script_length { + if self.script.len() as u64 > script_params.max_script_length() { Err(ValidityError::TransactionScriptLength)?; } - if self.script_data.len() as u64 > script_params.max_script_data_length { + if self.script_data.len() as u64 > script_params.max_script_data_length() { Err(ValidityError::TransactionScriptDataLength)?; } diff --git a/fuel-tx/src/transaction/validity.rs b/fuel-tx/src/transaction/validity.rs index fec1c5a8f6..98d9350272 100644 --- a/fuel-tx/src/transaction/validity.rs +++ b/fuel-tx/src/transaction/validity.rs @@ -158,7 +158,7 @@ impl Input { Self::CoinPredicate(CoinPredicate { predicate, .. }) | Self::MessageCoinPredicate(MessageCoinPredicate { predicate, .. }) | Self::MessageDataPredicate(MessageDataPredicate { predicate, .. }) - if predicate.len() as u64 > predicate_params.max_predicate_length => + if predicate.len() as u64 > predicate_params.max_predicate_length() => { Err(ValidityError::InputPredicateLength { index }) } @@ -170,7 +170,7 @@ impl Input { | Self::MessageDataPredicate(MessageDataPredicate { predicate_data, .. }) if predicate_data.len() as u64 - > predicate_params.max_predicate_data_length => + > predicate_params.max_predicate_data_length() => { Err(ValidityError::InputPredicateDataLength { index }) } @@ -203,7 +203,7 @@ impl Input { Self::MessageDataSigned(MessageDataSigned { data, .. }) | Self::MessageDataPredicate(MessageDataPredicate { data, .. }) if data.is_empty() - || data.len() as u64 > predicate_params.max_message_data_length => + || data.len() as u64 > predicate_params.max_message_data_length() => { Err(ValidityError::InputMessageDataLength { index }) } @@ -302,7 +302,7 @@ pub(crate) fn check_size(tx: &T, tx_params: &TxParameters) -> Result<(), Vali where T: canonical::Serialize, { - if tx.size() as u64 > tx_params.max_size { + if tx.size() as u64 > tx_params.max_size() { Err(ValidityError::TransactionSizeLimitExceeded)?; } @@ -337,7 +337,7 @@ where } let max_gas = tx.max_gas(gas_costs, fee_params); - if max_gas > tx_params.max_gas_per_tx { + if max_gas > tx_params.max_gas_per_tx() { Err(ValidityError::TransactionMaxGasExceeded)? } @@ -349,15 +349,15 @@ where Err(ValidityError::TransactionMaturity)?; } - if tx.inputs().len() > tx_params.max_inputs as usize { + if tx.inputs().len() > tx_params.max_inputs() as usize { Err(ValidityError::TransactionInputsMax)? } - if tx.outputs().len() > tx_params.max_outputs as usize { + if tx.outputs().len() > tx_params.max_outputs() as usize { Err(ValidityError::TransactionOutputsMax)? } - if tx.witnesses().len() > tx_params.max_witnesses as usize { + if tx.witnesses().len() > tx_params.max_witnesses() as usize { Err(ValidityError::TransactionWitnessesMax)? } @@ -496,7 +496,7 @@ where #[cfg(feature = "typescript")] mod typescript { use crate::{ - PredicateParameters, + transaction::consensus_parameters::typescript::PredicateParameters, Witness, }; use fuel_types::Bytes32; @@ -540,7 +540,7 @@ mod typescript { txhash, &outputs, &witnesses, - predicate_params, + predicate_params.as_ref(), &mut None, ) .map_err(|e| js_sys::Error::new(&format!("{:?}", e))) diff --git a/fuel-vm/src/checked_transaction.rs b/fuel-vm/src/checked_transaction.rs index 3a2b0eb015..ae2a2ae02b 100644 --- a/fuel-vm/src/checked_transaction.rs +++ b/fuel-vm/src/checked_transaction.rs @@ -351,11 +351,11 @@ impl From<&ConsensusParameters> for CheckPredicateParams { CheckPredicateParams { gas_costs: value.gas_costs().clone(), chain_id: value.chain_id(), - max_gas_per_predicate: value.predicate_params().max_gas_per_predicate, - max_gas_per_tx: value.tx_params().max_gas_per_tx, - max_inputs: value.tx_params().max_inputs, - contract_max_size: value.contract_params().contract_max_size, - max_message_data_length: value.predicate_params().max_message_data_length, + max_gas_per_predicate: value.predicate_params().max_gas_per_predicate(), + max_gas_per_tx: value.tx_params().max_gas_per_tx(), + max_inputs: value.tx_params().max_inputs(), + contract_max_size: value.contract_params().contract_max_size(), + max_message_data_length: value.predicate_params().max_message_data_length(), tx_offset: value.tx_params().tx_offset(), fee_params: *(value.fee_params()), base_asset_id: *value.base_asset_id(), @@ -1112,7 +1112,8 @@ mod tests { .unwrap(); let min_fee = fee.min_fee(); - let expected_min_fee = (tx.metered_bytes_size() as u64 * fee_params.gas_per_byte + let expected_min_fee = (tx.metered_bytes_size() as u64 + * fee_params.gas_per_byte() + gas_costs.vm_initialization().resolve(tx.size() as u64) + 3 * gas_costs.ecr1() + gas_costs.s256().resolve(tx.size() as u64)) @@ -1165,7 +1166,8 @@ mod tests { // Because all inputs are owned by the same address, the address will only need to // be recovered once. Therefore, we charge only once for the address // recovery of the signed inputs. - let expected_min_fee = (tx.metered_bytes_size() as u64 * fee_params.gas_per_byte + let expected_min_fee = (tx.metered_bytes_size() as u64 + * fee_params.gas_per_byte() + gas_costs.vm_initialization().resolve(tx.size() as u64) + gas_costs.ecr1() + gas_costs.s256().resolve(tx.size() as u64)) @@ -1231,7 +1233,7 @@ mod tests { .unwrap(); let min_fee = fee.min_fee(); - let expected_min_fee = (tx.size() as u64 * fee_params.gas_per_byte + let expected_min_fee = (tx.size() as u64 * fee_params.gas_per_byte() + gas_costs.vm_initialization().resolve(tx.size() as u64) + gas_costs.contract_root().resolve(predicate_1.len() as u64) + gas_costs.contract_root().resolve(predicate_2.len() as u64) @@ -1317,7 +1319,8 @@ mod tests { .unwrap(); let min_fee = fee.min_fee(); - let expected_min_fee = (tx.metered_bytes_size() as u64 * fee_params.gas_per_byte + let expected_min_fee = (tx.metered_bytes_size() as u64 + * fee_params.gas_per_byte() + 3 * gas_costs.ecr1() + gas_costs.vm_initialization().resolve(tx.size() as u64) + gas_costs.contract_root().resolve(predicate_1.len() as u64) @@ -1359,7 +1362,8 @@ mod tests { .unwrap(); let min_fee = fee.min_fee(); - let expected_min_fee = (tx.metered_bytes_size() as u64 * fee_params.gas_per_byte + let expected_min_fee = (tx.metered_bytes_size() as u64 + * fee_params.gas_per_byte() + gas_costs.state_root().resolve(storage_slots_len as Word) + gas_costs.contract_root().resolve(bytecode_len as Word) + gas_costs.vm_initialization().resolve(tx.size() as u64) @@ -1371,7 +1375,7 @@ mod tests { let max_fee = fee.max_fee(); let expected_max_fee = min_fee + (witness_limit - bytecode.size() as u64) - * fee_params.gas_per_byte + * fee_params.gas_per_byte() * gas_price; assert_eq!(max_fee, expected_max_fee); } @@ -1393,7 +1397,8 @@ mod tests { .unwrap(); let min_fee = fee.min_fee(); - let expected_min_fee = (tx.metered_bytes_size() as u64 * fee_params.gas_per_byte + let expected_min_fee = (tx.metered_bytes_size() as u64 + * fee_params.gas_per_byte() + gas_costs.state_root().resolve(0) + gas_costs.contract_root().resolve(0) + gas_costs.vm_initialization().resolve(tx.size() as u64) @@ -1405,7 +1410,7 @@ mod tests { let max_fee = fee.max_fee(); let expected_max_fee = min_fee + (witness_limit - bytecode.size_static() as u64) - * fee_params.gas_per_byte + * fee_params.gas_per_byte() * gas_price; assert_eq!(max_fee, expected_max_fee); } @@ -1801,7 +1806,7 @@ mod tests { // cant overflow as metered bytes * gas_per_byte < u64::MAX let gas_used_by_bytes = fee_params - .gas_per_byte + .gas_per_byte() .saturating_mul(tx.metered_bytes_size() as u64); let gas_used_by_inputs = tx.gas_used_by_inputs(gas_costs); let gas_used_by_metadata = tx.gas_used_by_metadata(gas_costs); @@ -1818,11 +1823,11 @@ mod tests { let witness_limit_allowance = tx .witness_limit() .saturating_sub(tx.witnesses().size_dynamic() as u64) - .saturating_mul(fee_params.gas_per_byte); + .saturating_mul(fee_params.gas_per_byte()); let max_gas = min_gas .saturating_add(*tx.script_gas_limit()) .saturating_add(witness_limit_allowance); - let max_fee = gas_to_fee(max_gas, gas_price, fee_params.gas_price_factor); + let max_fee = gas_to_fee(max_gas, gas_price, fee_params.gas_price_factor()); let max_fee_with_tip = max_fee.saturating_add(tx.tip() as u128); @@ -1842,7 +1847,7 @@ mod tests { // cant overflow as (metered bytes + gas_used_by_predicates) * gas_per_byte < // u64::MAX let gas_used_by_bytes = fee_params - .gas_per_byte + .gas_per_byte() .saturating_mul(tx.metered_bytes_size() as u64); let gas_used_by_inputs = tx.gas_used_by_inputs(gas_costs); let gas_used_by_metadata = tx.gas_used_by_metadata(gas_costs); @@ -1856,9 +1861,9 @@ mod tests { ); let total = gas as u128 * gas_price as u128; // use different division mechanism than impl - let fee = total / fee_params.gas_price_factor as u128; + let fee = total / fee_params.gas_price_factor() as u128; let fee_remainder = - (total.rem_euclid(fee_params.gas_price_factor as u128) > 0) as u128; + (total.rem_euclid(fee_params.gas_price_factor() as u128) > 0) as u128; let rounded_fee = fee .saturating_add(fee_remainder) .saturating_add(tx.tip() as u128); diff --git a/fuel-vm/src/interpreter.rs b/fuel-vm/src/interpreter.rs index 71720f1b77..66bcbf2ae6 100644 --- a/fuel-vm/src/interpreter.rs +++ b/fuel-vm/src/interpreter.rs @@ -162,10 +162,11 @@ impl Default for InterpreterParams { Self { gas_price: 0, gas_costs: Default::default(), - max_inputs: TxParameters::DEFAULT.max_inputs, - contract_max_size: ContractParameters::DEFAULT.contract_max_size, + max_inputs: TxParameters::DEFAULT.max_inputs(), + contract_max_size: ContractParameters::DEFAULT.contract_max_size(), tx_offset: TxParameters::DEFAULT.tx_offset(), - max_message_data_length: PredicateParameters::DEFAULT.max_message_data_length, + max_message_data_length: PredicateParameters::DEFAULT + .max_message_data_length(), chain_id: ChainId::default(), fee_params: FeeParameters::default(), base_asset_id: Default::default(), diff --git a/fuel-vm/src/tests/limits.rs b/fuel-vm/src/tests/limits.rs index 66ac1ce09b..5976232d55 100644 --- a/fuel-vm/src/tests/limits.rs +++ b/fuel-vm/src/tests/limits.rs @@ -25,7 +25,7 @@ fn cannot_exceed_max_inputs() { vec![op::ret(RegId::ONE)].into_iter().collect(), vec![], ); - for _ in 0..=params.tx_params().max_inputs { + for _ in 0..=params.tx_params().max_inputs() { script.add_input(Input::coin_signed( rng.gen(), rng.gen(), @@ -50,7 +50,7 @@ fn cannot_exceed_max_outputs() { vec![op::ret(RegId::ONE)].into_iter().collect(), vec![], ); - for _ in 0..=params.tx_params().max_outputs { + for _ in 0..=params.tx_params().max_outputs() { script.add_output(Output::variable(rng.gen(), rng.gen(), rng.gen())); } script @@ -68,7 +68,7 @@ fn cannot_exceed_max_witnesses() { vec![op::ret(RegId::ONE)].into_iter().collect(), vec![], ); - for _ in 0..=params.tx_params().max_witnesses { + for _ in 0..=params.tx_params().max_witnesses() { script.add_witness(Witness::from(vec![rng.gen::(); 1])); } script diff --git a/fuel-vm/src/util.rs b/fuel-vm/src/util.rs index 6b061779e7..aa10da6fb8 100644 --- a/fuel-vm/src/util.rs +++ b/fuel-vm/src/util.rs @@ -613,7 +613,7 @@ pub mod test_helpers { let tx_params = TxParameters::default().with_max_gas_per_tx(Word::MAX / 2); // The gas should be huge enough to cover the execution but still much less than // `MAX_GAS_PER_TX`. - let gas_limit = tx_params.max_gas_per_tx / 2; + let gas_limit = tx_params.max_gas_per_tx() / 2; let maturity = Default::default(); let height = Default::default(); let zero_fee_limit = 0;