diff --git a/CHANGELOG.md b/CHANGELOG.md index 327829f3..0b192c3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,12 @@ Description of the upcoming release here. - [#1809](https://github.com/FuelLabs/fuel-core/pull/1809): Fetch `ConsensusParameters` from the database - [#1808](https://github.com/FuelLabs/fuel-core/pull/1808): Fetch consensus parameters from the provider. +#### Breaking + +- [#1822](https://github.com/FuelLabs/fuel-core/pull/1822): Removed support of `Create` transaction from debugger since it doesn't have any script to execute. +- [#1822](https://github.com/FuelLabs/fuel-core/pull/1822): Use `fuel-vm 0.49.0` with new transactions types - `Upgrade` and `Upload`. Also added `max_bytecode_subsections` field to the `ConsensusParameters` to limit the number of bytecode subsections in the state transition bytecode. +- [#1816](https://github.com/FuelLabs/fuel-core/pull/1816): Updated the upgradable executor to fetch the state transition bytecode from the database when the version doesn't match a native one. This change enables the WASM executor in the "production" build and requires a `wasm32-unknown-unknown` target. + ## [Version 0.24.2] ### Changed diff --git a/Cargo.lock b/Cargo.lock index 45ab62ac..a61c5845 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2835,9 +2835,9 @@ dependencies = [ [[package]] name = "fuel-asm" -version = "0.48.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20bc683784e35f3421aab3dc5a31a94c8ad80f1e9ec614ddddac930b4081cd92" +checksum = "42df651415e443094f86102473b7f9fa23633ab6c3c98dd3f713adde251acf0f" dependencies = [ "bitflags 2.5.0", "fuel-types", @@ -3325,6 +3325,7 @@ dependencies = [ "fuel-core-trace", "fuel-core-txpool", "fuel-core-types", + "fuel-core-upgradable-executor", "futures", "hyper", "insta", @@ -3423,9 +3424,9 @@ dependencies = [ [[package]] name = "fuel-crypto" -version = "0.48.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6112c726b5254cad831e61db36dcd0d83b28c50180ea22cb8815343fb162526" +checksum = "71cef93970fb8a26d3a683ae211833c6bbf391066887f501bd5859f29992b59a" dependencies = [ "coins-bip32", "coins-bip39", @@ -3444,9 +3445,9 @@ dependencies = [ [[package]] name = "fuel-derive" -version = "0.48.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5896603b839f04f27e8bddbae2990dc799fb119f5e62973d6666b2ea1a4b036b" +checksum = "2b85e8e508b26d088262075fcfe9921b7009c931fef1cc55fe1dafb116c99884" dependencies = [ "proc-macro2", "quote", @@ -3456,9 +3457,9 @@ dependencies = [ [[package]] name = "fuel-merkle" -version = "0.48.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8f75d97f6d43fbd15aa5ca0d594b33cc59ba5fd73ce42f4a10dfa9288a9a4a4" +checksum = "5198b4eab5a19b0034971da88199dae7dd61806ebd8df366d6af1f17cda2e151" dependencies = [ "derive_more", "digest 0.10.7", @@ -3471,15 +3472,15 @@ dependencies = [ [[package]] name = "fuel-storage" -version = "0.48.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6899452bbf8b09d536b0adf98d6a28d1081ce38c3490fa2d8cc1dd47ba153351" +checksum = "fa738e9c244f3f312af09faef108ec9a285f02afcefbc579c19c242cea742dd0" [[package]] name = "fuel-tx" -version = "0.48.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305c12d87f47d139505cbbaee1effa7750ce171c9a4362d212f4f7a651902121" +checksum = "8e4b4ea79ffe711af7bbf363b25f383fc6e481e652cf55a5ef8b5a458fcf4ef9" dependencies = [ "bitflags 2.5.0", "derivative", @@ -3490,6 +3491,7 @@ dependencies = [ "fuel-types", "hashbrown 0.14.3", "itertools 0.10.5", + "postcard", "rand", "serde", "serde_json", @@ -3499,9 +3501,9 @@ dependencies = [ [[package]] name = "fuel-types" -version = "0.48.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d92d34c6625f0c3b88463f01027e836ba561125f6ccfb1402d12743fc7b2c96" +checksum = "455cf5275d96f6907e81ed1825c4e6a9dd79f7c1c37a4e15134562f83024c7e7" dependencies = [ "fuel-derive", "hex", @@ -3511,9 +3513,9 @@ dependencies = [ [[package]] name = "fuel-vm" -version = "0.48.0" +version = "0.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06f02e86ebf357689e34412af34faf7f0d35a9b8b6ccb2ec42fd369559d6914" +checksum = "8811f949db8ce61cc68dcf81644047df4ee23be55879efcfe9f1aa5adc378965" dependencies = [ "anyhow", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 736476fd..0eb8890a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,7 +81,7 @@ fuel-core-wasm-executor = { version = "0.24.2", path = "./crates/services/upgrad fuel-core-xtask = { version = "0.0.0", path = "./xtask" } # Fuel dependencies -fuel-vm-private = { version = "0.48.0", package = "fuel-vm", default-features = false } +fuel-vm-private = { version = "0.49.0", package = "fuel-vm", default-features = false } # Common dependencies anyhow = "1.0" diff --git a/benches/benches/state.rs b/benches/benches/state.rs index b20edc7d..d08ecf21 100644 --- a/benches/benches/state.rs +++ b/benches/benches/state.rs @@ -19,7 +19,13 @@ use fuel_core_storage::{ vm_storage::VmStorage, }; use fuel_core_types::{ - blockchain::header::GeneratedConsensusFields, + blockchain::{ + header::{ + ApplicationHeader, + ConsensusHeader, + }, + primitives::Empty, + }, fuel_tx::Bytes32, fuel_types::ContractId, fuel_vm::InterpreterStorage, @@ -73,9 +79,10 @@ fn insert_state_single_contract_database(c: &mut Criterion) { let mut elapsed_time = Duration::default(); for _ in 0..iters { let inner = outer.read_transaction(); - let mut inner_db = VmStorage::new::( + let mut inner_db = VmStorage::new( inner, - &Default::default(), + &ConsensusHeader::::default(), + &ApplicationHeader::::default(), Default::default(), ); let start = std::time::Instant::now(); @@ -134,9 +141,10 @@ fn insert_state_single_contract_transaction(c: &mut Criterion) { let mut elapsed_time = Duration::default(); for _ in 0..iters { let inner = outer.read_transaction(); - let mut inner_db = VmStorage::new::( + let mut inner_db = VmStorage::new( inner, - &Default::default(), + &ConsensusHeader::::default(), + &ApplicationHeader::::default(), Default::default(), ); let start = std::time::Instant::now(); @@ -198,9 +206,10 @@ fn insert_state_multiple_contracts_database(c: &mut Criterion) { let contract: ContractId = rng.gen(); for _ in 0..iters { let inner = outer.read_transaction(); - let mut inner_db = VmStorage::new::( + let mut inner_db = VmStorage::new( inner, - &Default::default(), + &ConsensusHeader::::default(), + &ApplicationHeader::::default(), Default::default(), ); let start = std::time::Instant::now(); @@ -262,9 +271,10 @@ fn insert_state_multiple_contracts_transaction(c: &mut Criterion) { let contract: ContractId = rng.gen(); for _ in 0..iters { let inner = outer.read_transaction(); - let mut inner_db = VmStorage::new::( + let mut inner_db = VmStorage::new( inner, - &Default::default(), + &ConsensusHeader::::default(), + &ApplicationHeader::::default(), Default::default(), ); let start = std::time::Instant::now(); diff --git a/benches/benches/vm_set/blockchain.rs b/benches/benches/vm_set/blockchain.rs index c237b564..ada8d111 100644 --- a/benches/benches/vm_set/blockchain.rs +++ b/benches/benches/vm_set/blockchain.rs @@ -37,7 +37,10 @@ use fuel_core_storage::{ StorageAsMut, }; use fuel_core_types::{ - blockchain::header::ConsensusHeader, + blockchain::header::{ + ApplicationHeader, + ConsensusHeader, + }, fuel_asm::{ op, GTFArgs, @@ -123,15 +126,22 @@ impl BenchDb { /// Creates a `VmDatabase` instance. fn to_vm_database(&self) -> VmStorage> { - let header = ConsensusHeader { + let consensus = ConsensusHeader { prev_root: Default::default(), height: 1.into(), time: Tai64::UNIX_EPOCH, generated: (), }; + let application = ApplicationHeader { + da_height: Default::default(), + consensus_parameters_version: 0, + generated: (), + state_transition_bytecode_version: 0, + }; VmStorage::new( self.db.clone().into_transaction(), - &header, + &consensus, + &application, ContractId::zeroed(), ) } diff --git a/bin/e2e-test-client/src/tests/test_data/large_state/chain_config.json b/bin/e2e-test-client/src/tests/test_data/large_state/chain_config.json index 2aa33ca9..7748984b 100644 --- a/bin/e2e-test-client/src/tests/test_data/large_state/chain_config.json +++ b/bin/e2e-test-client/src/tests/test_data/large_state/chain_config.json @@ -8,7 +8,8 @@ "max_outputs": 255, "max_witnesses": 255, "max_gas_per_tx": 1000000000, - "max_size": 17825792 + "max_size": 17825792, + "max_bytecode_subsections": 256 } }, "predicate_params": { diff --git a/bin/fuel-core/chainspec/dev-testnet/chain_config.json b/bin/fuel-core/chainspec/dev-testnet/chain_config.json index 494b88a2..0305b800 100644 --- a/bin/fuel-core/chainspec/dev-testnet/chain_config.json +++ b/bin/fuel-core/chainspec/dev-testnet/chain_config.json @@ -8,7 +8,8 @@ "max_outputs": 255, "max_witnesses": 255, "max_gas_per_tx": 1000000000, - "max_size": 17825792 + "max_size": 17825792, + "max_bytecode_subsections": 256 } }, "predicate_params": { diff --git a/bin/fuel-core/chainspec/testnet/chain_config.json b/bin/fuel-core/chainspec/testnet/chain_config.json index 525e6f5e..17747b6d 100644 --- a/bin/fuel-core/chainspec/testnet/chain_config.json +++ b/bin/fuel-core/chainspec/testnet/chain_config.json @@ -8,7 +8,8 @@ "max_outputs": 255, "max_witnesses": 255, "max_gas_per_tx": 30000000, - "max_size": 112640 + "max_size": 112640, + "max_bytecode_subsections": 256 } }, "predicate_params": { diff --git a/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_configurable_block_height.snap b/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_configurable_block_height.snap index d5c5fe76..ec4e7c71 100644 --- a/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_configurable_block_height.snap +++ b/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_configurable_block_height.snap @@ -11,7 +11,8 @@ expression: json "max_outputs": 255, "max_witnesses": 255, "max_gas_per_tx": 100000000, - "max_size": 17825792 + "max_size": 17825792, + "max_bytecode_subsections": 256 }, "predicate_params": { "max_predicate_length": 1048576, diff --git a/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_local_testnet_config.snap b/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_local_testnet_config.snap index a7082e20..7e314486 100644 --- a/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_local_testnet_config.snap +++ b/crates/chain-config/src/config/snapshots/fuel_core_chain_config__config__chain__tests__snapshot_local_testnet_config.snap @@ -12,7 +12,8 @@ expression: json "max_outputs": 255, "max_witnesses": 255, "max_gas_per_tx": 100000000, - "max_size": 112640 + "max_size": 112640, + "max_bytecode_subsections": 255 } }, "predicate_params": { diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_configurable_block_height.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_configurable_block_height.snap index e8c49141..f03cb005 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_configurable_block_height.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_configurable_block_height.snap @@ -11,7 +11,8 @@ expression: json "max_outputs": 255, "max_witnesses": 255, "max_gas_per_tx": 100000000, - "max_size": 17825792 + "max_size": 17825792, + "max_bytecode_subsections": 256 }, "predicate_params": { "max_predicate_length": 1048576, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_balances.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_balances.snap index ea533f58..24b18202 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_balances.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_balances.snap @@ -25,7 +25,8 @@ expression: json "max_outputs": 255, "max_witnesses": 255, "max_gas_per_tx": 100000000, - "max_size": 17825792 + "max_size": 17825792, + "max_bytecode_subsections": 256 }, "predicate_params": { "max_predicate_length": 1048576, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_state.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_state.snap index d7f968f7..00a0bac4 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_state.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_state.snap @@ -25,7 +25,8 @@ expression: json "max_outputs": 255, "max_witnesses": 255, "max_gas_per_tx": 100000000, - "max_size": 17825792 + "max_size": 17825792, + "max_bytecode_subsections": 256 }, "predicate_params": { "max_predicate_length": 1048576, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_tx_pointer.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_tx_pointer.snap index e3f2b32b..6b1d97d8 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_tx_pointer.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_tx_pointer.snap @@ -21,7 +21,8 @@ expression: json "max_outputs": 255, "max_witnesses": 255, "max_gas_per_tx": 100000000, - "max_size": 17825792 + "max_size": 17825792, + "max_bytecode_subsections": 256 }, "predicate_params": { "max_predicate_length": 1048576, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_utxo_id.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_utxo_id.snap index a16cfeae..00868dc0 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_utxo_id.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_contract_with_utxo_id.snap @@ -21,7 +21,8 @@ expression: json "max_outputs": 255, "max_witnesses": 255, "max_gas_per_tx": 100000000, - "max_size": 17825792 + "max_size": 17825792, + "max_bytecode_subsections": 256 }, "predicate_params": { "max_predicate_length": 1048576, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_local_testnet_config.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_local_testnet_config.snap index 990d96c8..404cf227 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_local_testnet_config.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_local_testnet_config.snap @@ -11,7 +11,8 @@ expression: json "max_outputs": 255, "max_witnesses": 255, "max_gas_per_tx": 100000000, - "max_size": 17825792 + "max_size": 17825792, + "max_bytecode_subsections": 256 }, "predicate_params": { "max_predicate_length": 1048576, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_coin_state.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_coin_state.snap index 40950834..735fa951 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_coin_state.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_coin_state.snap @@ -24,7 +24,8 @@ expression: json "max_outputs": 255, "max_witnesses": 255, "max_gas_per_tx": 100000000, - "max_size": 17825792 + "max_size": 17825792, + "max_bytecode_subsections": 256 }, "predicate_params": { "max_predicate_length": 1048576, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_contract.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_contract.snap index 2f5b4efa..ba531bfb 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_contract.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_contract.snap @@ -19,7 +19,8 @@ expression: json "max_outputs": 255, "max_witnesses": 255, "max_gas_per_tx": 100000000, - "max_size": 17825792 + "max_size": 17825792, + "max_bytecode_subsections": 256 }, "predicate_params": { "max_predicate_length": 1048576, diff --git a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_message_state.snap b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_message_state.snap index 36887ff4..b5d981ce 100644 --- a/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_message_state.snap +++ b/crates/chain-config/src/snapshots/fuel_core_chain_config__config__tests__snapshot_simple_message_state.snap @@ -23,7 +23,8 @@ expression: json "max_outputs": 255, "max_witnesses": 255, "max_gas_per_tx": 100000000, - "max_size": 17825792 + "max_size": 17825792, + "max_bytecode_subsections": 256 }, "predicate_params": { "max_predicate_length": 1048576, diff --git a/crates/client/assets/schema.sdl b/crates/client/assets/schema.sdl index 23f8a066..4bac454e 100644 --- a/crates/client/assets/schema.sdl +++ b/crates/client/assets/schema.sdl @@ -190,6 +190,11 @@ type ConsensusParameters { privilegedAddress: Address! } +type ConsensusParametersPurpose { + witnessIndex: U16! + checksum: Bytes32! +} + union ConsensusParametersVersion = Version type Contract { @@ -960,6 +965,10 @@ type SqueezedOutStatus { reason: String! } +type StateTransitionPurpose { + root: Bytes32! +} + type SubmittedStatus { time: Tai64Timestamp! @@ -1012,6 +1021,8 @@ type Transaction { isScript: Boolean! isCreate: Boolean! isMint: Boolean! + isUpgrade: Boolean! + isUpload: Boolean! inputs: [Input!] outputs: [Output!]! outputContract: ContractOutput @@ -1023,6 +1034,11 @@ type Transaction { bytecodeWitnessIndex: U16 salt: Salt storageSlots: [HexString!] + bytecodeRoot: Bytes32 + subsectionIndex: U16 + subsectionsNumber: U16 + proofSet: [Bytes32!] + upgradePurpose: UpgradePurpose """ Return the transaction bytes using canonical encoding """ @@ -1069,6 +1085,7 @@ type TxParameters { maxWitnesses: U32! maxGasPerTx: U64! maxSize: U64! + maxBytecodeSubsections: U16! } union TxParametersVersion = Version @@ -1083,6 +1100,8 @@ scalar U64 scalar U8 +union UpgradePurpose = ConsensusParametersPurpose | StateTransitionPurpose + scalar UtxoId type VariableOutput { diff --git a/crates/client/src/client/schema/chain.rs b/crates/client/src/client/schema/chain.rs index b0b5903f..2f1d6493 100644 --- a/crates/client/src/client/schema/chain.rs +++ b/crates/client/src/client/schema/chain.rs @@ -49,6 +49,7 @@ pub struct TxParameters { pub max_witnesses: U32, pub max_gas_per_tx: U64, pub max_size: U64, + pub max_bytecode_subsections: U16, } #[derive(cynic::InlineFragments, Clone, Debug)] @@ -71,6 +72,7 @@ impl TryFrom for fuel_core_types::fuel_tx::TxParameters { max_witnesses: params.max_witnesses.into(), max_gas_per_tx: params.max_gas_per_tx.into(), max_size: params.max_size.into(), + max_bytecode_subsections: params.max_bytecode_subsections.into(), } .into(), ), diff --git a/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__chain__tests__chain_gql_query_output.snap b/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__chain__tests__chain_gql_query_output.snap index 6e730756..f2e6925e 100644 --- a/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__chain__tests__chain_gql_query_output.snap +++ b/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__chain__tests__chain_gql_query_output.snap @@ -58,6 +58,7 @@ query { maxWitnesses maxGasPerTx maxSize + maxBytecodeSubsections } predicateParams { version { diff --git a/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__tx__tests__transparent_transaction_by_id_query_gql_output.snap b/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__tx__tests__transparent_transaction_by_id_query_gql_output.snap index f2941ee7..48ad1a18 100644 --- a/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__tx__tests__transparent_transaction_by_id_query_gql_output.snap +++ b/crates/client/src/client/schema/snapshots/fuel_core_client__client__schema__tx__tests__transparent_transaction_by_id_query_gql_output.snap @@ -51,6 +51,8 @@ query($id: TransactionId!) { isScript isCreate isMint + isUpgrade + isUpload outputs { __typename ... on CoinOutput { @@ -191,5 +193,19 @@ query($id: TransactionId!) { salt storageSlots bytecodeWitnessIndex + bytecodeRoot + subsectionIndex + subsectionsNumber + proofSet + upgradePurpose { + __typename + ... on ConsensusParametersPurpose { + witnessIndex + checksum + } + ... on StateTransitionPurpose { + root + } + } } } diff --git a/crates/client/src/client/schema/tx/transparent_tx.rs b/crates/client/src/client/schema/tx/transparent_tx.rs index d7288883..230630d1 100644 --- a/crates/client/src/client/schema/tx/transparent_tx.rs +++ b/crates/client/src/client/schema/tx/transparent_tx.rs @@ -33,6 +33,7 @@ use fuel_core_types::{ output, policies::PolicyType, StorageSlot, + UploadBody, }, fuel_types, }; @@ -116,6 +117,16 @@ pub struct Transaction { /// The result of a `is_mint()` helper function is stored here. /// It is not an original field of the `Transaction`. pub is_mint: bool, + /// It is `true` for `Transaction::Upgrade`. + /// + /// The result of a `is_upgrade()` helper function is stored here. + /// It is not an original field of the `Transaction`. + pub is_upgrade: bool, + /// It is `true` for `Transaction::Upload`. + /// + /// The result of a `is_upload()` helper function is stored here. + /// It is not an original field of the `Transaction`. + pub is_upload: bool, /// The field of the `Transaction` type. pub outputs: Vec, /// The field of the `Transaction::Mint`. @@ -142,8 +153,18 @@ pub struct Transaction { pub salt: Option, /// The field of the `Transaction::Create`. pub storage_slots: Option>, - /// The field of the `Transaction::Create`. + /// The field of the `Transaction::Create` or `Transaction::Upload`. pub bytecode_witness_index: Option, + /// The field of the `Transaction::Upload`. + pub bytecode_root: Option, + /// The field of the `Transaction::Upload`. + pub subsection_index: Option, + /// The field of the `Transaction::Upload`. + pub subsections_number: Option, + /// The field of the `Transaction::Upload`. + pub proof_set: Option>, + /// The field of the `Transaction::Upgrade`. + pub upgrade_purpose: Option, } impl TryFrom for fuel_tx::Transaction { @@ -245,7 +266,7 @@ impl TryFrom for fuel_tx::Transaction { .collect(), ); create.into() - } else { + } else if tx.is_mint { let tx_pointer: fuel_tx::TxPointer = tx .tx_pointer .ok_or_else(|| ConversionError::MissingField("tx_pointer".to_string()))? @@ -279,6 +300,95 @@ impl TryFrom for fuel_tx::Transaction { .into(), ); mint.into() + } else if tx.is_upgrade { + let tx = fuel_tx::Transaction::upgrade( + tx.upgrade_purpose + .ok_or_else(|| { + ConversionError::MissingField("upgrade_purpose".to_string()) + })? + .try_into()?, + tx.policies + .ok_or_else(|| ConversionError::MissingField("policies".to_string()))? + .into(), + tx.inputs + .ok_or_else(|| ConversionError::MissingField("inputs".to_string()))? + .into_iter() + .map(TryInto::try_into) + .collect::, ConversionError>>()?, + tx.outputs + .into_iter() + .map(TryInto::try_into) + .collect::, ConversionError>>()?, + tx.witnesses + .ok_or_else(|| { + ConversionError::MissingField("witnesses".to_string()) + })? + .into_iter() + .map(|w| w.0 .0.into()) + .collect(), + ); + tx.into() + } else if tx.is_upload { + let tx = fuel_tx::Transaction::upload( + UploadBody { + root: tx + .bytecode_root + .ok_or_else(|| { + ConversionError::MissingField("bytecode_root".to_string()) + })? + .into(), + witness_index: tx + .bytecode_witness_index + .ok_or_else(|| { + ConversionError::MissingField("witness_index".to_string()) + })? + .into(), + subsection_index: tx + .subsection_index + .ok_or_else(|| { + ConversionError::MissingField("subsection_index".to_string()) + })? + .into(), + subsections_number: tx + .subsections_number + .ok_or_else(|| { + ConversionError::MissingField( + "subsections_number".to_string(), + ) + })? + .into(), + proof_set: tx + .proof_set + .ok_or_else(|| { + ConversionError::MissingField("proof_set".to_string()) + })? + .into_iter() + .map(|w| w.0 .0) + .collect(), + }, + tx.policies + .ok_or_else(|| ConversionError::MissingField("policies".to_string()))? + .into(), + tx.inputs + .ok_or_else(|| ConversionError::MissingField("inputs".to_string()))? + .into_iter() + .map(TryInto::try_into) + .collect::, ConversionError>>()?, + tx.outputs + .into_iter() + .map(TryInto::try_into) + .collect::, ConversionError>>()?, + tx.witnesses + .ok_or_else(|| { + ConversionError::MissingField("witnesses".to_string()) + })? + .into_iter() + .map(|w| w.0 .0.into()) + .collect(), + ); + tx.into() + } else { + return Err(ConversionError::UnknownVariant("Transaction")); }; // This `match` block is added here to enforce compilation error if a new variant @@ -289,6 +399,8 @@ impl TryFrom for fuel_tx::Transaction { fuel_tx::Transaction::Script(_) => {} fuel_tx::Transaction::Create(_) => {} fuel_tx::Transaction::Mint(_) => {} + fuel_tx::Transaction::Upgrade(_) => {} + fuel_tx::Transaction::Upload(_) => {} }; Ok(tx) @@ -548,3 +660,46 @@ impl From for fuel_tx::policies::Policies { policies } } + +#[derive(cynic::InlineFragments, Clone, Debug)] +#[cynic(schema_path = "./assets/schema.sdl")] +pub enum UpgradePurpose { + ConsensusParameters(ConsensusParametersPurpose), + StateTransition(StateTransitionPurpose), + #[cynic(fallback)] + Unknown, +} + +#[derive(cynic::QueryFragment, Clone, Debug)] +#[cynic(schema_path = "./assets/schema.sdl")] +pub struct ConsensusParametersPurpose { + witness_index: U16, + checksum: Bytes32, +} + +#[derive(cynic::QueryFragment, Clone, Debug)] +#[cynic(schema_path = "./assets/schema.sdl")] +pub struct StateTransitionPurpose { + root: Bytes32, +} + +impl TryFrom for fuel_tx::UpgradePurpose { + type Error = ConversionError; + + fn try_from(value: UpgradePurpose) -> Result { + match value { + UpgradePurpose::ConsensusParameters(v) => { + Ok(fuel_tx::UpgradePurpose::ConsensusParameters { + witness_index: v.witness_index.into(), + checksum: v.checksum.into(), + }) + } + UpgradePurpose::StateTransition(v) => { + Ok(fuel_tx::UpgradePurpose::StateTransition { + root: v.root.into(), + }) + } + UpgradePurpose::Unknown => Err(Self::Error::UnknownVariant("UpgradePurpose")), + } + } +} diff --git a/crates/fuel-core/src/graphql_api/worker_service.rs b/crates/fuel-core/src/graphql_api/worker_service.rs index f743d4fc..90a59717 100644 --- a/crates/fuel-core/src/graphql_api/worker_service.rs +++ b/crates/fuel-core/src/graphql_api/worker_service.rs @@ -218,6 +218,14 @@ where outputs = tx.outputs().as_slice(); } Transaction::Mint(_) => continue, + Transaction::Upgrade(tx) => { + inputs = tx.inputs().as_slice(); + outputs = tx.outputs().as_slice(); + } + Transaction::Upload(tx) => { + inputs = tx.inputs().as_slice(); + outputs = tx.outputs().as_slice(); + } } persist_owners_index( block_height, @@ -316,7 +324,10 @@ where db.storage::() .insert(&contract_id, &(salt.into()))?; } - Transaction::Script(_) | Transaction::Mint(_) => { + Transaction::Script(_) + | Transaction::Mint(_) + | Transaction::Upgrade(_) + | Transaction::Upload(_) => { // Do nothing } } diff --git a/crates/fuel-core/src/schema/chain.rs b/crates/fuel-core/src/schema/chain.rs index e57ff21d..4d164c0e 100644 --- a/crates/fuel-core/src/schema/chain.rs +++ b/crates/fuel-core/src/schema/chain.rs @@ -250,6 +250,10 @@ impl TxParameters { async fn max_size(&self) -> U64 { self.0.max_size().into() } + + async fn max_bytecode_subsections(&self) -> U16 { + self.0.max_bytecode_subsections().into() + } } #[Object] diff --git a/crates/fuel-core/src/schema/dap.rs b/crates/fuel-core/src/schema/dap.rs index d8d8c348..b270f365 100644 --- a/crates/fuel-core/src/schema/dap.rs +++ b/crates/fuel-core/src/schema/dap.rs @@ -48,7 +48,6 @@ use fuel_core_types::{ CheckedTransaction, IntoChecked, }, - consts, interpreter::InterpreterParams, state::DebugEval, Interpreter, @@ -98,12 +97,9 @@ impl ConcreteStorage { } pub fn memory(&self, id: &ID, start: usize, size: usize) -> Option<&[u8]> { - let (end, overflow) = start.overflowing_add(size); - if overflow || end as u64 > consts::VM_MAX_RAM { - return None - } - - self.vm.get(id).map(|vm| &vm.memory()[start..end]) + self.vm + .get(id) + .and_then(|vm| vm.memory().read(start, size).ok()) } pub fn init( @@ -203,6 +199,7 @@ impl ConcreteStorage { let vm_database = VmStorage::new( storage.into_transaction(), block.header().consensus(), + block.header().application(), // TODO: Use a real coinbase address Default::default(), ); @@ -475,27 +472,18 @@ impl DapMutation { json_receipts, }) } - CheckedTransaction::Create(create) => { - let ready_tx = create - .into_ready(GAS_PRICE, gas_costs, fee_params) - .map_err(|e| { - anyhow!("Failed to apply dynamic values to checked tx: {:?}", e) - })?; - vm.deploy(ready_tx).map_err(|err| { - async_graphql::Error::new(format!( - "Transaction deploy failed: {err:?}" - )) - })?; - - Ok(gql_types::RunResult { - state: gql_types::RunState::Completed, - breakpoint: None, - json_receipts: vec![], - }) + CheckedTransaction::Create(_) => { + Err(async_graphql::Error::new("`Create` is not supported")) } CheckedTransaction::Mint(_) => { Err(async_graphql::Error::new("`Mint` is not supported")) } + CheckedTransaction::Upgrade(_) => { + Err(async_graphql::Error::new("`Upgrade` is not supported")) + } + CheckedTransaction::Upload(_) => { + Err(async_graphql::Error::new("`Upload` is not supported")) + } } } diff --git a/crates/fuel-core/src/schema/tx.rs b/crates/fuel-core/src/schema/tx.rs index 522945cd..7bcdf974 100644 --- a/crates/fuel-core/src/schema/tx.rs +++ b/crates/fuel-core/src/schema/tx.rs @@ -77,6 +77,7 @@ pub mod input; pub mod output; pub mod receipt; pub mod types; +pub mod upgrade_purpose; #[derive(Default)] pub struct TxQuery; diff --git a/crates/fuel-core/src/schema/tx/types.rs b/crates/fuel-core/src/schema/tx/types.rs index a7d3a86d..4c23fe62 100644 --- a/crates/fuel-core/src/schema/tx/types.rs +++ b/crates/fuel-core/src/schema/tx/types.rs @@ -34,6 +34,7 @@ use crate::{ tx::{ input, output, + upgrade_purpose::UpgradePurpose, }, }, }; @@ -48,6 +49,7 @@ use fuel_core_types::{ fuel_tx::{ self, field::{ + BytecodeRoot, BytecodeWitnessIndex, InputContract, Inputs, @@ -58,13 +60,17 @@ use fuel_core_types::{ OutputContract, Outputs, Policies as PoliciesField, + ProofSet, ReceiptsRoot, Salt as SaltField, Script as ScriptField, ScriptData, ScriptGasLimit, StorageSlots, + SubsectionIndex, + SubsectionsNumber, TxPointer as TxPointerField, + UpgradePurpose as UpgradePurposeField, Witnesses, }, policies::PolicyType, @@ -349,19 +355,27 @@ impl Transaction { .latest_consensus_params(); let base_asset_id = params.base_asset_id(); match &self.0 { - fuel_tx::Transaction::Script(script) => Some( - script - .input_asset_ids(base_asset_id) + fuel_tx::Transaction::Script(tx) => Some( + tx.input_asset_ids(base_asset_id) .map(|c| AssetId(*c)) .collect(), ), - fuel_tx::Transaction::Create(create) => Some( - create - .input_asset_ids(base_asset_id) + fuel_tx::Transaction::Create(tx) => Some( + tx.input_asset_ids(base_asset_id) .map(|c| AssetId(*c)) .collect(), ), fuel_tx::Transaction::Mint(_) => None, + fuel_tx::Transaction::Upgrade(tx) => Some( + tx.input_asset_ids(base_asset_id) + .map(|c| AssetId(*c)) + .collect(), + ), + fuel_tx::Transaction::Upload(tx) => Some( + tx.input_asset_ids(base_asset_id) + .map(|c| AssetId(*c)) + .collect(), + ), } } @@ -376,21 +390,32 @@ impl Transaction { fuel_tx::Transaction::Mint(mint) => { Some(vec![mint.input_contract().contract_id.into()]) } + fuel_tx::Transaction::Upgrade(tx) => { + Some(tx.input_contracts().map(|v| (*v).into()).collect()) + } + fuel_tx::Transaction::Upload(tx) => { + Some(tx.input_contracts().map(|v| (*v).into()).collect()) + } } } async fn input_contract(&self) -> Option { match &self.0 { - fuel_tx::Transaction::Script(_) | fuel_tx::Transaction::Create(_) => None, + fuel_tx::Transaction::Script(_) + | fuel_tx::Transaction::Create(_) + | fuel_tx::Transaction::Upgrade(_) + | fuel_tx::Transaction::Upload(_) => None, fuel_tx::Transaction::Mint(mint) => Some(mint.input_contract().into()), } } async fn policies(&self) -> Option { match &self.0 { - fuel_tx::Transaction::Script(script) => Some((*script.policies()).into()), - fuel_tx::Transaction::Create(create) => Some((*create.policies()).into()), + fuel_tx::Transaction::Script(tx) => Some((*tx.policies()).into()), + fuel_tx::Transaction::Create(tx) => Some((*tx.policies()).into()), fuel_tx::Transaction::Mint(_) => None, + fuel_tx::Transaction::Upgrade(tx) => Some((*tx.policies()).into()), + fuel_tx::Transaction::Upload(tx) => Some((*tx.policies()).into()), } } @@ -399,36 +424,49 @@ impl Transaction { fuel_tx::Transaction::Script(script) => { Some((*script.script_gas_limit()).into()) } - fuel_tx::Transaction::Create(_) => Some(0.into()), - fuel_tx::Transaction::Mint(_) => None, + fuel_tx::Transaction::Create(_) + | fuel_tx::Transaction::Mint(_) + | fuel_tx::Transaction::Upgrade(_) + | fuel_tx::Transaction::Upload(_) => None, } } async fn maturity(&self) -> Option { match &self.0 { - fuel_tx::Transaction::Script(script) => Some(script.maturity().into()), - fuel_tx::Transaction::Create(create) => Some(create.maturity().into()), + fuel_tx::Transaction::Script(tx) => Some(tx.maturity().into()), + fuel_tx::Transaction::Create(tx) => Some(tx.maturity().into()), fuel_tx::Transaction::Mint(_) => None, + fuel_tx::Transaction::Upgrade(tx) => Some(tx.maturity().into()), + fuel_tx::Transaction::Upload(tx) => Some(tx.maturity().into()), } } async fn mint_amount(&self) -> Option { match &self.0 { - fuel_tx::Transaction::Script(_) | fuel_tx::Transaction::Create(_) => None, + fuel_tx::Transaction::Script(_) + | fuel_tx::Transaction::Create(_) + | fuel_tx::Transaction::Upgrade(_) + | fuel_tx::Transaction::Upload(_) => None, fuel_tx::Transaction::Mint(mint) => Some((*mint.mint_amount()).into()), } } async fn mint_asset_id(&self) -> Option { match &self.0 { - fuel_tx::Transaction::Script(_) | fuel_tx::Transaction::Create(_) => None, + fuel_tx::Transaction::Script(_) + | fuel_tx::Transaction::Create(_) + | fuel_tx::Transaction::Upgrade(_) + | fuel_tx::Transaction::Upload(_) => None, fuel_tx::Transaction::Mint(mint) => Some((*mint.mint_asset_id()).into()), } } async fn mint_gas_price(&self) -> Option { match &self.0 { - fuel_tx::Transaction::Script(_) | fuel_tx::Transaction::Create(_) => None, + fuel_tx::Transaction::Script(_) + | fuel_tx::Transaction::Create(_) + | fuel_tx::Transaction::Upgrade(_) + | fuel_tx::Transaction::Upload(_) => None, fuel_tx::Transaction::Mint(mint) => Some((*mint.gas_price()).into()), } } @@ -436,8 +474,10 @@ impl Transaction { // TODO: Maybe we need to do the same `Script` and `Create` async fn tx_pointer(&self) -> Option { match &self.0 { - fuel_tx::Transaction::Script(_) => None, - fuel_tx::Transaction::Create(_) => None, + fuel_tx::Transaction::Script(_) + | fuel_tx::Transaction::Create(_) + | fuel_tx::Transaction::Upgrade(_) + | fuel_tx::Transaction::Upload(_) => None, fuel_tx::Transaction::Mint(mint) => Some((*mint.tx_pointer()).into()), } } @@ -454,54 +494,87 @@ impl Transaction { self.0.is_mint() } + async fn is_upgrade(&self) -> bool { + self.0.is_upgrade() + } + + async fn is_upload(&self) -> bool { + self.0.is_upload() + } + async fn inputs(&self) -> Option> { match &self.0 { - fuel_tx::Transaction::Script(script) => { - Some(script.inputs().iter().map(Into::into).collect()) + fuel_tx::Transaction::Script(tx) => { + Some(tx.inputs().iter().map(Into::into).collect()) } - fuel_tx::Transaction::Create(create) => { - Some(create.inputs().iter().map(Into::into).collect()) + fuel_tx::Transaction::Create(tx) => { + Some(tx.inputs().iter().map(Into::into).collect()) } fuel_tx::Transaction::Mint(_) => None, + fuel_tx::Transaction::Upgrade(tx) => { + Some(tx.inputs().iter().map(Into::into).collect()) + } + fuel_tx::Transaction::Upload(tx) => { + Some(tx.inputs().iter().map(Into::into).collect()) + } } } async fn outputs(&self) -> Vec { match &self.0 { - fuel_tx::Transaction::Script(script) => { - script.outputs().iter().map(Into::into).collect() + fuel_tx::Transaction::Script(tx) => { + tx.outputs().iter().map(Into::into).collect() } - fuel_tx::Transaction::Create(create) => { - create.outputs().iter().map(Into::into).collect() + fuel_tx::Transaction::Create(tx) => { + tx.outputs().iter().map(Into::into).collect() } fuel_tx::Transaction::Mint(_) => vec![], + fuel_tx::Transaction::Upgrade(tx) => { + tx.outputs().iter().map(Into::into).collect() + } + fuel_tx::Transaction::Upload(tx) => { + tx.outputs().iter().map(Into::into).collect() + } } } async fn output_contract(&self) -> Option { match &self.0 { - fuel_tx::Transaction::Script(_) | fuel_tx::Transaction::Create(_) => None, + fuel_tx::Transaction::Script(_) + | fuel_tx::Transaction::Create(_) + | fuel_tx::Transaction::Upgrade(_) + | fuel_tx::Transaction::Upload(_) => None, fuel_tx::Transaction::Mint(mint) => Some(mint.output_contract().into()), } } async fn witnesses(&self) -> Option> { match &self.0 { - fuel_tx::Transaction::Script(script) => Some( - script - .witnesses() + fuel_tx::Transaction::Script(tx) => Some( + tx.witnesses() .iter() .map(|w| HexString(w.clone().into_inner())) .collect(), ), - fuel_tx::Transaction::Create(create) => Some( - create - .witnesses() + fuel_tx::Transaction::Create(tx) => Some( + tx.witnesses() .iter() .map(|w| HexString(w.clone().into_inner())) .collect(), ), fuel_tx::Transaction::Mint(_) => None, + fuel_tx::Transaction::Upgrade(tx) => Some( + tx.witnesses() + .iter() + .map(|w| HexString(w.clone().into_inner())) + .collect(), + ), + fuel_tx::Transaction::Upload(tx) => Some( + tx.witnesses() + .iter() + .map(|w| HexString(w.clone().into_inner())) + .collect(), + ), } } @@ -510,8 +583,10 @@ impl Transaction { fuel_tx::Transaction::Script(script) => { Some((*script.receipts_root()).into()) } - fuel_tx::Transaction::Create(_) => None, - fuel_tx::Transaction::Mint(_) => None, + fuel_tx::Transaction::Create(_) + | fuel_tx::Transaction::Mint(_) + | fuel_tx::Transaction::Upgrade(_) + | fuel_tx::Transaction::Upload(_) => None, } } @@ -530,8 +605,10 @@ impl Transaction { fuel_tx::Transaction::Script(script) => { Some(HexString(script.script().clone())) } - fuel_tx::Transaction::Create(_) => None, - fuel_tx::Transaction::Mint(_) => None, + fuel_tx::Transaction::Create(_) + | fuel_tx::Transaction::Mint(_) + | fuel_tx::Transaction::Upgrade(_) + | fuel_tx::Transaction::Upload(_) => None, } } @@ -540,18 +617,24 @@ impl Transaction { fuel_tx::Transaction::Script(script) => { Some(HexString(script.script_data().clone())) } - fuel_tx::Transaction::Create(_) => None, - fuel_tx::Transaction::Mint(_) => None, + fuel_tx::Transaction::Create(_) + | fuel_tx::Transaction::Mint(_) + | fuel_tx::Transaction::Upgrade(_) + | fuel_tx::Transaction::Upload(_) => None, } } async fn bytecode_witness_index(&self) -> Option { match &self.0 { fuel_tx::Transaction::Script(_) => None, - fuel_tx::Transaction::Create(create) => { - Some((*create.bytecode_witness_index()).into()) + fuel_tx::Transaction::Create(tx) => { + Some((*tx.bytecode_witness_index()).into()) } fuel_tx::Transaction::Mint(_) => None, + fuel_tx::Transaction::Upgrade(_) => None, + fuel_tx::Transaction::Upload(tx) => { + Some((*tx.bytecode_witness_index()).into()) + } } } @@ -560,6 +643,8 @@ impl Transaction { fuel_tx::Transaction::Script(_) => None, fuel_tx::Transaction::Create(create) => Some((*create.salt()).into()), fuel_tx::Transaction::Mint(_) => None, + fuel_tx::Transaction::Upgrade(_) => None, + fuel_tx::Transaction::Upload(_) => None, } } @@ -583,6 +668,60 @@ impl Transaction { .collect(), ), fuel_tx::Transaction::Mint(_) => None, + fuel_tx::Transaction::Upgrade(_) => None, + fuel_tx::Transaction::Upload(_) => None, + } + } + + async fn bytecode_root(&self) -> Option { + match &self.0 { + fuel_tx::Transaction::Script(_) + | fuel_tx::Transaction::Create(_) + | fuel_tx::Transaction::Mint(_) + | fuel_tx::Transaction::Upgrade(_) => None, + fuel_tx::Transaction::Upload(tx) => Some((*tx.bytecode_root()).into()), + } + } + + async fn subsection_index(&self) -> Option { + match &self.0 { + fuel_tx::Transaction::Script(_) + | fuel_tx::Transaction::Create(_) + | fuel_tx::Transaction::Mint(_) + | fuel_tx::Transaction::Upgrade(_) => None, + fuel_tx::Transaction::Upload(tx) => Some((*tx.subsection_index()).into()), + } + } + + async fn subsections_number(&self) -> Option { + match &self.0 { + fuel_tx::Transaction::Script(_) + | fuel_tx::Transaction::Create(_) + | fuel_tx::Transaction::Mint(_) + | fuel_tx::Transaction::Upgrade(_) => None, + fuel_tx::Transaction::Upload(tx) => Some((*tx.subsections_number()).into()), + } + } + + async fn proof_set(&self) -> Option> { + match &self.0 { + fuel_tx::Transaction::Script(_) + | fuel_tx::Transaction::Create(_) + | fuel_tx::Transaction::Mint(_) + | fuel_tx::Transaction::Upgrade(_) => None, + fuel_tx::Transaction::Upload(tx) => { + Some(tx.proof_set().iter().map(|proof| (*proof).into()).collect()) + } + } + } + + async fn upgrade_purpose(&self) -> Option { + match &self.0 { + fuel_tx::Transaction::Script(_) + | fuel_tx::Transaction::Create(_) + | fuel_tx::Transaction::Mint(_) => None, + fuel_tx::Transaction::Upgrade(tx) => Some((*tx.upgrade_purpose()).into()), + fuel_tx::Transaction::Upload(_) => None, } } diff --git a/crates/fuel-core/src/schema/tx/upgrade_purpose.rs b/crates/fuel-core/src/schema/tx/upgrade_purpose.rs new file mode 100644 index 00000000..53e492cb --- /dev/null +++ b/crates/fuel-core/src/schema/tx/upgrade_purpose.rs @@ -0,0 +1,71 @@ +use crate::schema::scalars::{ + Bytes32, + U16, +}; +use async_graphql::{ + Object, + Union, +}; + +#[derive(Union)] +pub enum UpgradePurpose { + /// The upgrade is performed to change the consensus parameters. + ConsensusParameters(ConsensusParametersPurpose), + /// The upgrade is performed to change the state transition function. + StateTransition(StateTransitionPurpose), +} + +pub struct ConsensusParametersPurpose { + /// The index of the witness in the [`Witnesses`] field that contains + /// the serialized consensus parameters. + witness_index: U16, + /// The hash of the serialized consensus parameters. + /// Since the serialized consensus parameters live inside witnesses(malleable + /// data), any party can override them. The `checksum` is used to verify that the + /// data was not modified. + checksum: Bytes32, +} + +#[Object] +impl ConsensusParametersPurpose { + async fn witness_index(&self) -> U16 { + self.witness_index + } + + async fn checksum(&self) -> Bytes32 { + self.checksum + } +} + +pub struct StateTransitionPurpose { + /// The Merkle root of the new bytecode of the state transition function. + /// The bytecode must be present on the blockchain(should be known by the + /// network) at the moment of inclusion of this transaction. + root: Bytes32, +} + +#[Object] +impl StateTransitionPurpose { + async fn root(&self) -> Bytes32 { + self.root + } +} + +impl From for UpgradePurpose { + fn from(value: fuel_core_types::fuel_tx::UpgradePurpose) -> Self { + match value { + fuel_core_types::fuel_tx::UpgradePurpose::ConsensusParameters { + witness_index, + checksum, + } => UpgradePurpose::ConsensusParameters(ConsensusParametersPurpose { + witness_index: witness_index.into(), + checksum: checksum.into(), + }), + fuel_core_types::fuel_tx::UpgradePurpose::StateTransition { root } => { + UpgradePurpose::StateTransition(StateTransitionPurpose { + root: root.into(), + }) + } + } + } +} diff --git a/crates/fuel-core/src/service/genesis.rs b/crates/fuel-core/src/service/genesis.rs index 1f1660d4..27b165ed 100644 --- a/crates/fuel-core/src/service/genesis.rs +++ b/crates/fuel-core/src/service/genesis.rs @@ -115,7 +115,7 @@ pub async fn execute_genesis_block( // https://github.com/FuelLabs/fuel-core/issues/1570 database_transaction_on_chain .storage_as_mut::() - .insert(&ConsensusParametersVersion::MIN, &[])?; + .insert(&ConsensusParametersVersion::MIN, &Default::default())?; // Needs to be given the progress because `iter_all` is not implemented on db transactions. for key in genesis_progress_on_chain { diff --git a/crates/services/executor/src/executor.rs b/crates/services/executor/src/executor.rs index d2ad0530..5feb1ebb 100644 --- a/crates/services/executor/src/executor.rs +++ b/crates/services/executor/src/executor.rs @@ -743,7 +743,10 @@ where fn tx_is_valid_variant(tx: &Transaction) -> Result<(), ForcedTransactionFailure> { match tx { Transaction::Mint(_) => Err(ForcedTransactionFailure::InvalidTransactionType), - Transaction::Script(_) | Transaction::Create(_) => Ok(()), + Transaction::Script(_) + | Transaction::Create(_) + | Transaction::Upgrade(_) + | Transaction::Upload(_) => Ok(()), } } @@ -756,11 +759,13 @@ where let gas_costs = consensus_params.gas_costs(); let fee_params = consensus_params.fee_params(); let actual_max_gas = match tx { - Transaction::Script(script) => script.max_gas(gas_costs, fee_params), - Transaction::Create(create) => create.max_gas(gas_costs, fee_params), + Transaction::Script(tx) => tx.max_gas(gas_costs, fee_params), + Transaction::Create(tx) => tx.max_gas(gas_costs, fee_params), Transaction::Mint(_) => { return Err(ForcedTransactionFailure::InvalidTransactionType) } + Transaction::Upgrade(tx) => tx.max_gas(gas_costs, fee_params), + Transaction::Upload(tx) => tx.max_gas(gas_costs, fee_params), }; if actual_max_gas > claimed_max_gas { return Err(ForcedTransactionFailure::InsufficientMaxGas { @@ -807,8 +812,8 @@ where }; match checked_tx { - CheckedTransaction::Script(script) => self.execute_create_or_script( - script, + CheckedTransaction::Script(tx) => self.execute_chargeable_transaction( + tx, header, coinbase_contract_id, gas_price, @@ -816,8 +821,8 @@ where tx_st_transaction, execution_kind, ), - CheckedTransaction::Create(create) => self.execute_create_or_script( - create, + CheckedTransaction::Create(tx) => self.execute_chargeable_transaction( + tx, header, coinbase_contract_id, gas_price, @@ -834,6 +839,24 @@ where tx_st_transaction, execution_kind, ), + CheckedTransaction::Upgrade(tx) => self.execute_chargeable_transaction( + tx, + header, + coinbase_contract_id, + gas_price, + execution_data, + tx_st_transaction, + execution_kind, + ), + CheckedTransaction::Upload(tx) => self.execute_chargeable_transaction( + tx, + header, + coinbase_contract_id, + gas_price, + execution_data, + tx_st_transaction, + execution_kind, + ), } } @@ -929,6 +952,7 @@ where let mut vm_db = VmStorage::new( &mut sub_block_db_commit, &header.consensus, + &header.application, coinbase_contract_id, ); @@ -994,7 +1018,7 @@ where } #[allow(clippy::too_many_arguments)] - fn execute_create_or_script( + fn execute_chargeable_transaction( &self, mut checked_tx: Checked, header: &PartialBlockHeader, @@ -1053,6 +1077,7 @@ where let vm_db = VmStorage::new( &mut sub_block_db_commit, &header.consensus, + &header.application, coinbase_contract_id, ); @@ -1609,7 +1634,7 @@ where backtrace.contract(), backtrace.registers(), backtrace.call_stack(), - hex::encode(&backtrace.memory()[..sp]), // print stack + hex::encode(backtrace.memory().read(0usize, sp).expect("`SP` always within stack")), // print stack ); } } diff --git a/crates/services/executor/src/ports.rs b/crates/services/executor/src/ports.rs index 40304a4d..74d169e6 100644 --- a/crates/services/executor/src/ports.rs +++ b/crates/services/executor/src/ports.rs @@ -28,6 +28,12 @@ impl MaybeCheckedTransaction { MaybeCheckedTransaction::CheckedTransaction(CheckedTransaction::Mint(tx)) => { tx.id() } + MaybeCheckedTransaction::CheckedTransaction(CheckedTransaction::Upgrade( + tx, + )) => tx.id(), + MaybeCheckedTransaction::CheckedTransaction(CheckedTransaction::Upload( + tx, + )) => tx.id(), MaybeCheckedTransaction::Transaction(tx) => tx.id(chain_id), } } diff --git a/crates/services/txpool/src/txpool.rs b/crates/services/txpool/src/txpool.rs index 33111774..0eb6d4ec 100644 --- a/crates/services/txpool/src/txpool.rs +++ b/crates/services/txpool/src/txpool.rs @@ -350,9 +350,11 @@ where let tx: CheckedTransaction = tx.into(); let tx = Arc::new(match tx { - CheckedTransaction::Script(script) => PoolTransaction::Script(script), - CheckedTransaction::Create(create) => PoolTransaction::Create(create), + CheckedTransaction::Script(tx) => PoolTransaction::Script(tx), + CheckedTransaction::Create(tx) => PoolTransaction::Create(tx), CheckedTransaction::Mint(_) => return Err(Error::MintIsDisallowed), + CheckedTransaction::Upgrade(tx) => PoolTransaction::Upgrade(tx), + CheckedTransaction::Upload(tx) => PoolTransaction::Upload(tx), }); self.check_blacklisting(tx.as_ref())?; @@ -542,6 +544,16 @@ fn verify_tx_min_gas_price( let (_, checked) = ready.decompose(); CheckedTransaction::Create(checked) } + CheckedTransaction::Upgrade(tx) => { + let ready = tx.into_ready(gas_price, gas_costs, fee_parameters)?; + let (_, checked) = ready.decompose(); + CheckedTransaction::Upgrade(checked) + } + CheckedTransaction::Upload(tx) => { + let ready = tx.into_ready(gas_price, gas_costs, fee_parameters)?; + let (_, checked) = ready.decompose(); + CheckedTransaction::Upload(checked) + } CheckedTransaction::Mint(_) => return Err(Error::MintIsDisallowed), }; Ok(read.into()) diff --git a/crates/services/upgradable-executor/build.rs b/crates/services/upgradable-executor/build.rs index 7b0f6b56..b5181c9c 100644 --- a/crates/services/upgradable-executor/build.rs +++ b/crates/services/upgradable-executor/build.rs @@ -11,6 +11,7 @@ use std::{ fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=wasm-executor/src/main.rs"); + println!("cargo:rerun-if-changed=src/executor.rs"); #[cfg(feature = "wasm-executor")] build_wasm() diff --git a/crates/services/upgradable-executor/src/executor.rs b/crates/services/upgradable-executor/src/executor.rs index c693b19d..c1548762 100644 --- a/crates/services/upgradable-executor/src/executor.rs +++ b/crates/services/upgradable-executor/src/executor.rs @@ -41,7 +41,10 @@ use std::sync::Arc; use fuel_core_storage::{ not_found, structured_storage::StructuredStorage, - tables::StateTransitionBytecodeVersions, + tables::{ + StateTransitionBytecodeVersions, + UploadedBytecodes, + }, StorageAsRef, }; #[cfg(any(test, feature = "test-helpers"))] @@ -421,19 +424,30 @@ where drop(guard); let view = StructuredStorage::new(self.storage_view_provider.latest_view()); - let bytecode = view + let bytecode_root = *view .storage::() .get(&version)? .ok_or(not_found!(StateTransitionBytecodeVersions))?; + let uploaded_bytecode = view + .storage::() + .get(&bytecode_root)? + .ok_or(not_found!(UploadedBytecodes))?; + + let fuel_core_types::fuel_vm::UploadedBytecode::Completed(bytecode) = + uploaded_bytecode.as_ref() + else { + return Err(ExecutorError::Other(format!( + "The bytecode under the bytecode_root(`{bytecode_root}`) is not completed", + ))) + }; // Compiles the module - let module = - wasmtime::Module::new(&self.engine, bytecode.as_ref()).map_err(|e| { - ExecutorError::Other(format!( - "Failed to compile the module for the version `{}` with {e}", - version, - )) - })?; + let module = wasmtime::Module::new(&self.engine, bytecode).map_err(|e| { + ExecutorError::Other(format!( + "Failed to compile the module for the version `{}` with {e}", + version, + )) + })?; self.cached_modules.lock().insert(version, module.clone()); Ok(module) @@ -645,6 +659,8 @@ mod test { executor::Executor, WASM_BYTECODE, }; + use fuel_core_storage::tables::UploadedBytecodes; + use fuel_core_types::fuel_vm::UploadedBytecode; #[test] fn can_validate_block__native_strategy() { @@ -719,10 +735,21 @@ mod test { fn storage_with_state_transition( next_version: StateTransitionBytecodeVersion, ) -> Storage { + // Only FuelVM requires the Merkle root to match the corresponding bytecode + // during uploading of it. The executor itself only uses a database to get the code, + // and how bytecode appeared there is not the executor's responsibility. + const BYTECODE_ROOT: Bytes32 = Bytes32::zeroed(); + let mut storage = storage(); let mut tx = storage.write_transaction(); tx.storage_as_mut::() - .insert(&next_version, WASM_BYTECODE) + .insert(&next_version, &BYTECODE_ROOT) + .unwrap(); + tx.storage_as_mut::() + .insert( + &BYTECODE_ROOT, + &UploadedBytecode::Completed(WASM_BYTECODE.to_vec()), + ) .unwrap(); tx.commit().unwrap(); @@ -766,7 +793,7 @@ mod test { // The test verifies that `Executor::get_module` method caches the compiled WASM module. // If it doesn't cache the modules, the test will fail with a timeout. #[test] - #[ntest::timeout(40_000)] + #[ntest::timeout(60_000)] fn reuse_cached_compiled_module__native_strategy() { // Given let next_version = Executor::::VERSION + 1; @@ -788,7 +815,7 @@ mod test { // The test verifies that `Executor::get_module` method caches the compiled WASM module. // If it doesn't cache the modules, the test will fail with a timeout. #[test] - #[ntest::timeout(40_000)] + #[ntest::timeout(60_000)] fn reuse_cached_compiled_module__wasm_strategy() { // Given let next_version = Executor::::VERSION + 1; diff --git a/crates/storage/src/column.rs b/crates/storage/src/column.rs index 078f20fc..ea9a1fd7 100644 --- a/crates/storage/src/column.rs +++ b/crates/storage/src/column.rs @@ -61,10 +61,12 @@ pub enum Column { ConsensusParametersVersions = 18, /// See [`StateTransitionBytecodeVersions`](crate::tables::StateTransitionBytecodeVersions) StateTransitionBytecodeVersions = 19, + /// See [`UploadedBytecodes`](crate::tables::UploadedBytecodes) + UploadedBytecodes = 20, // TODO: Remove this column and use `Metadata` column instead. /// Table for genesis state import progress tracking. - GenesisMetadata = 20, + GenesisMetadata = 21, } impl Column { diff --git a/crates/storage/src/structured_storage/upgrades.rs b/crates/storage/src/structured_storage/upgrades.rs index 8627f56d..e6d49e42 100644 --- a/crates/storage/src/structured_storage/upgrades.rs +++ b/crates/storage/src/structured_storage/upgrades.rs @@ -14,6 +14,7 @@ use crate::{ StateTransitionBytecodeVersions, }, }; +use fuel_vm_private::storage::UploadedBytecodes; impl TableWithBlueprint for ConsensusParametersVersions { type Blueprint = Plain, Postcard>; @@ -33,6 +34,15 @@ impl TableWithBlueprint for StateTransitionBytecodeVersions { } } +impl TableWithBlueprint for UploadedBytecodes { + type Blueprint = Plain; + type Column = Column; + + fn column() -> Self::Column { + Column::UploadedBytecodes + } +} + #[cfg(test)] mod test { use super::*; @@ -52,11 +62,15 @@ mod test { crate::basic_storage_tests!( StateTransitionBytecodeVersions, - ::Key::default(), - vec![32u8], - ::OwnedValue::from(vec![ - 32u8 - ]), + 0u32, + ::OwnedValue::from([123; 32]), + ::OwnedValue::from([123; 32]), generate_key ); + + crate::basic_storage_tests!( + UploadedBytecodes, + ::Key::default(), + ::OwnedValue::Completed(vec![123; 2048]) + ); } diff --git a/crates/storage/src/tables.rs b/crates/storage/src/tables.rs index 9d8531db..a3b95a79 100644 --- a/crates/storage/src/tables.rs +++ b/crates/storage/src/tables.rs @@ -17,6 +17,7 @@ use fuel_core_types::{ relayer::message::Message, }, fuel_tx::{ + Bytes32, ConsensusParameters, Transaction, TxId, @@ -32,6 +33,7 @@ pub use fuel_vm_private::storage::{ ContractsAssets, ContractsRawCode, ContractsState, + UploadedBytecodes, }; /// The table of blocks generated by Fuels validators. @@ -137,8 +139,9 @@ pub struct StateTransitionBytecodeVersions; impl Mappable for StateTransitionBytecodeVersions { type Key = Self::OwnedKey; type OwnedKey = StateTransitionBytecodeVersion; - type Value = [u8]; - type OwnedValue = Vec; + type Value = Self::OwnedValue; + /// The Merkle root of the bytecode from the [`UploadedBytecodes`]. + type OwnedValue = Bytes32; } /// The module contains definition of merkle-related tables. diff --git a/crates/storage/src/vm_storage.rs b/crates/storage/src/vm_storage.rs index 80ccb4fb..2eeea092 100644 --- a/crates/storage/src/vm_storage.rs +++ b/crates/storage/src/vm_storage.rs @@ -3,10 +3,12 @@ use crate::{ not_found, tables::{ + ConsensusParametersVersions, ContractsAssets, ContractsRawCode, ContractsState, FuelBlocks, + StateTransitionBytecodeVersions, }, ContractsAssetsStorage, ContractsStateKey, @@ -24,10 +26,16 @@ use crate::{ use anyhow::anyhow; use fuel_core_types::{ blockchain::{ - header::ConsensusHeader, + header::{ + ApplicationHeader, + ConsensusHeader, + ConsensusParametersVersion, + StateTransitionBytecodeVersion, + }, primitives::BlockId, }, fuel_tx::{ + ConsensusParameters, Contract, StorageSlot, }, @@ -42,7 +50,10 @@ use fuel_core_types::{ }; use fuel_vm_private::{ fuel_storage::StorageWrite, - storage::ContractsStateData, + storage::{ + ContractsStateData, + UploadedBytecodes, + }, }; use itertools::Itertools; use primitive_types::U256; @@ -51,6 +62,8 @@ use std::borrow::Cow; /// Used to store metadata relevant during the execution of a transaction. #[derive(Clone, Debug)] pub struct VmStorage { + consensus_parameters_version: ConsensusParametersVersion, + state_transition_version: StateTransitionBytecodeVersion, current_block_height: BlockHeight, current_timestamp: Tai64, coinbase: ContractId, @@ -77,6 +90,8 @@ impl IncreaseStorageKey for U256 { impl Default for VmStorage { fn default() -> Self { Self { + consensus_parameters_version: Default::default(), + state_transition_version: Default::default(), current_block_height: Default::default(), current_timestamp: Tai64::now(), coinbase: Default::default(), @@ -87,14 +102,17 @@ impl Default for VmStorage { impl VmStorage { /// Create and instance of the VM storage around the `header` and `coinbase` contract id. - pub fn new( + pub fn new( database: D, - header: &ConsensusHeader, + consensus: &ConsensusHeader, + application: &ApplicationHeader, coinbase: ContractId, ) -> Self { Self { - current_block_height: header.height, - current_timestamp: header.time, + consensus_parameters_version: application.consensus_parameters_version, + state_transition_version: application.state_transition_bytecode_version, + current_block_height: consensus.height, + current_timestamp: consensus.time, coinbase, database, } @@ -208,6 +226,9 @@ where + StorageWrite + StorageSize + StorageRead + + StorageMutate + + StorageMutate + + StorageMutate + VmStorageRequirements, { type DataError = StorageError; @@ -216,6 +237,14 @@ where Ok(self.current_block_height) } + fn consensus_parameters_version(&self) -> Result { + Ok(self.consensus_parameters_version) + } + + fn state_transition_version(&self) -> Result { + Ok(self.state_transition_version) + } + fn timestamp(&self, height: BlockHeight) -> Result { let timestamp = match height { // panic if $rB is greater than the current block height. @@ -247,6 +276,26 @@ where Ok(self.coinbase) } + fn set_consensus_parameters( + &mut self, + version: u32, + consensus_parameters: &ConsensusParameters, + ) -> Result, Self::DataError> { + self.database + .storage_as_mut::() + .insert(&version, consensus_parameters) + } + + fn set_state_transition_bytecode( + &mut self, + version: u32, + hash: &Bytes32, + ) -> Result, Self::DataError> { + self.database + .storage_as_mut::() + .insert(&version, hash) + } + fn deploy_contract_with_id( &mut self, slots: &[StorageSlot], diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 2924c139..1cefcb66 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -57,6 +57,7 @@ pub mod fuel_vm { state, storage::ContractsAssetKey, storage::ContractsStateKey, + storage::UploadedBytecode, util, }; } diff --git a/crates/types/src/services/txpool.rs b/crates/types/src/services/txpool.rs index 5259f4c3..1888d53b 100644 --- a/crates/types/src/services/txpool.rs +++ b/crates/types/src/services/txpool.rs @@ -19,6 +19,8 @@ use crate::{ Script, Transaction, TxId, + Upgrade, + Upload, UtxoId, }, fuel_types::{ @@ -58,30 +60,40 @@ pub enum PoolTransaction { Script(Checked