Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A new Upload transaction to upload the huge bytecode on the chain #720

Merged
merged 72 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
cb9d324
Versioned `GasCosts`
xgreenx Mar 21, 2024
2feab92
Versioned `ConsensusParameters`ю
xgreenx Mar 21, 2024
57c7730
Updated CHANGELOG.md
xgreenx Mar 21, 2024
41a537c
Versioned `FeeParameters`
xgreenx Mar 21, 2024
83721d9
Versioned `PredicateParameters`, `ScriptParameters`, `ContractParamet…
xgreenx Mar 21, 2024
ed2e94b
Versioned `TxParameters`
xgreenx Mar 21, 2024
34aa7dc
Updated CHANGELOG.md
xgreenx Mar 21, 2024
0ca4ddf
Make CI happy
xgreenx Mar 21, 2024
38e515d
Merge branch 'master' into feature/versioning-sub-parameters
xgreenx Mar 21, 2024
5c5b009
Reshuffled fields `Script` and `Create` transactions to unify part us…
xgreenx Mar 22, 2024
5eae221
Updated CHANGELOG.md
xgreenx Mar 22, 2024
bd12db3
Merge branch 'master' into feature/versioning-sub-parameters
xgreenx Mar 22, 2024
a3618cc
Merge branch 'feature/versioning-sub-parameters' into feature/common-…
xgreenx Mar 22, 2024
edc0c1d
Added privileged address to the `ConsensusParameters`
xgreenx Mar 23, 2024
a015fcc
Updated CHANGELOG.md
xgreenx Mar 23, 2024
b57047a
Unified `Create` and `Script` logic via `ChargeableTransaction`
xgreenx Mar 24, 2024
3ebe1d8
Updated CHANGELOG.md
xgreenx Mar 24, 2024
8f8fdf0
Merge branch 'feature/privileged-address' into feature/upgrade-transa…
xgreenx Mar 24, 2024
27d18ac
Merge branch 'master' into feature/versioning-sub-parameters
xgreenx Mar 25, 2024
2f2cfdc
Merge branch 'feature/versioning-sub-parameters' into feature/common-…
xgreenx Mar 25, 2024
13592ac
Merge branch 'feature/common-payable-part' into feature/chargeable-tr…
xgreenx Mar 25, 2024
fa4c795
A new `Upgrade` transaction to perform network upgrades
xgreenx Mar 25, 2024
cbee779
Merge branch 'feature/chargeable-transaction' into feature/upgrade-tr…
xgreenx Mar 25, 2024
e374ea3
Merge branch 'master' into feature/versioning-sub-parameters
xgreenx Mar 25, 2024
c4cbc5f
Merge branch 'feature/versioning-sub-parameters' into feature/common-…
xgreenx Mar 25, 2024
6db8b16
Merge branch 'feature/common-payable-part' into feature/chargeable-tr…
xgreenx Mar 25, 2024
d5532d0
Merge branch 'feature/chargeable-transaction' into feature/upgrade-tr…
xgreenx Mar 25, 2024
f9f2f39
Merge branch 'master' into feature/common-payable-part
xgreenx Mar 25, 2024
0fbcb44
Merge branch 'master' into feature/common-payable-part
xgreenx Mar 25, 2024
50502cf
Merge branch 'feature/common-payable-part' into feature/chargeable-tr…
xgreenx Mar 26, 2024
87424ec
Merge branch 'feature/chargeable-transaction' into feature/upgrade-tr…
xgreenx Mar 26, 2024
4496d2b
Update fuel-tx/src/transaction/id.rs
xgreenx Mar 26, 2024
f975aab
Merge branch 'master' into feature/chargeable-transaction
xgreenx Mar 26, 2024
09327bd
Merge branch 'feature/chargeable-transaction' into feature/upgrade-tr…
Dentosal Mar 26, 2024
652157e
Merge branch 'master' into feature/upgrade-transaction
xgreenx Apr 1, 2024
080e73c
Added tests required for any transaction
xgreenx Apr 1, 2024
3f07b06
Process `Upgrade` transaction inside of the `Interpreter`
xgreenx Apr 2, 2024
5ce49dc
Added tests to verify correctness of the upgrade behaviour.
xgreenx Apr 3, 2024
462e515
Review from meself
xgreenx Apr 3, 2024
3a9e27c
Updated CHANGELOG.md
xgreenx Apr 3, 2024
0b815be
Updated CHANGELOG.md
xgreenx Apr 3, 2024
68a41da
Updated CHANGELOG.md
xgreenx Apr 3, 2024
919560d
Updated CHANGELOG.md
xgreenx Apr 3, 2024
3510e37
Added a comment
xgreenx Apr 4, 2024
17957b3
Merge branch 'feature/upgrade-transaction' into feature/process-upgra…
xgreenx Apr 4, 2024
bb585a7
Merge branch 'master' into feature/upgrade-transaction
xgreenx Apr 5, 2024
68a1767
Merge branch 'feature/upgrade-transaction' into feature/process-upgra…
xgreenx Apr 5, 2024
71c026c
Merge branch 'master' into feature/upgrade-transaction
xgreenx Apr 6, 2024
f2a3c83
Merge branch 'feature/upgrade-transaction' into feature/process-upgra…
xgreenx Apr 6, 2024
4771d5a
Use right discriminant for the `Upgrade` transaction
xgreenx Apr 7, 2024
04f459c
Merge branch 'feature/upgrade-transaction' into feature/process-upgra…
xgreenx Apr 7, 2024
83c10d1
A new `Upload` transaction to upload the huge bytecode on the chain
xgreenx Apr 7, 2024
e991f3c
Self-review
xgreenx Apr 7, 2024
de3a20a
Added `max_bytecode_parts` to the `TxParameters`.
xgreenx Apr 8, 2024
b4f2cdd
Renamed "part" into "subsection"
xgreenx Apr 8, 2024
29aa53b
Applied comments from PR
xgreenx Apr 9, 2024
120de10
Removed iow
xgreenx Apr 9, 2024
c5163fb
Merge branch 'feature/upgrade-transaction' into feature/process-upgra…
xgreenx Apr 9, 2024
f9a04cc
Merge branch 'master' into feature/process-upgrade-transaction-within…
xgreenx Apr 9, 2024
92018ff
Merge branch 'feature/process-upgrade-transaction-within-interpreter'…
xgreenx Apr 9, 2024
b63bbd6
Applied comments from the review
xgreenx Apr 10, 2024
628e567
Removed non requried test
xgreenx Apr 10, 2024
e6aeef8
Update fuel-asm/src/panic_reason.rs
xgreenx Apr 10, 2024
e871566
Update fuel-asm/src/panic_reason.rs
xgreenx Apr 10, 2024
9f128fa
Merge branch 'feature/process-upgrade-transaction-within-interpreter'…
xgreenx Apr 10, 2024
47bad74
Applied comment
xgreenx Apr 11, 2024
fc2aed5
Merge branch 'feature/process-upgrade-transaction-within-interpreter'…
xgreenx Apr 11, 2024
5d40b23
Merge branch 'master' into feature/upload-transaction
xgreenx Apr 11, 2024
9d44cb0
Enable the test
xgreenx Apr 12, 2024
c019ad6
Revert change from the next PR
xgreenx Apr 12, 2024
9d4b944
Applied comments from PR
xgreenx Apr 12, 2024
2bf21a5
Applied comments from PR
xgreenx Apr 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

#### Breaking

- [#714](https://github.com/FuelLabs/fuel-vm/pull/714): The change adds a new `Upload` transaction that allows uploading huge byte code on chain subsection by subsection. This transaction is chargeable and is twice as expensive as the `Create` transaction. Anyone can submit this transaction.
- [#712](https://github.com/FuelLabs/fuel-vm/pull/712): The `Interpreter` supports the processing of the `Upgrade` transaction. The change affects `InterpreterStorage`, adding 5 new methods that must be implemented.
- [#707](https://github.com/FuelLabs/fuel-vm/pull/707): The change adds a new `Upgrade` transaction that allows upgrading either consensus parameters or state transition function used by the network to produce future blocks.
The purpose of the upgrade is defined by the `Upgrade Purpose` type:
Expand Down Expand Up @@ -64,6 +65,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

#### Breaking

- [#714](https://github.com/FuelLabs/fuel-vm/pull/714): Added `max_bytecode_subsections` field to the `TxParameters` to limit the number of subsections that can be uploaded.
- [#707](https://github.com/FuelLabs/fuel-vm/pull/707): Side small breaking for tests changes from the `Upgrade` transaction:
- Moved `fuel-tx-test-helpers` logic into the `fuel_tx::test_helpers` module.
- Added a new rule for `Create` transaction: all inputs should use base asset otherwise it returns `TransactionInputContainsNonBaseAssetId` error.
Expand Down
16 changes: 16 additions & 0 deletions fuel-tx/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ use crate::{
TxPointer,
Upgrade,
UpgradePurpose,
Upload,
UploadBody,
Witness,
};

Expand Down Expand Up @@ -181,6 +183,20 @@ impl TransactionBuilder<Upgrade> {
}
}

impl TransactionBuilder<Upload> {
pub fn upload(body: UploadBody) -> Self {
let tx = Upload {
body,
policies: Policies::new().with_max_fee(0),
inputs: Default::default(),
outputs: Default::default(),
witnesses: Default::default(),
metadata: None,
};
Self::with_tx(tx)
}
}

impl TransactionBuilder<Mint> {
pub fn mint(
block_height: BlockHeight,
Expand Down
4 changes: 4 additions & 0 deletions fuel-tx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ pub use transaction::{
UpgradeBody,
UpgradeMetadata,
UpgradePurpose,
Upload,
UploadBody,
UploadMetadata,
UploadSubsection,
UtxoId,
ValidityError,
Witness,
Expand Down
48 changes: 48 additions & 0 deletions fuel-tx/src/test_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ mod use_std {
TransactionBuilder,
Upgrade,
UpgradePurpose,
Upload,
UploadBody,
UploadSubsection,
};
use core::marker::PhantomData;
use fuel_crypto::{
Expand Down Expand Up @@ -130,6 +133,7 @@ mod use_std {
Transaction::Create(_) => (),
Transaction::Mint(_) => (),
Transaction::Upgrade(_) => (),
Transaction::Upload(_) => (),
})
.unwrap_or(());

Expand Down Expand Up @@ -406,6 +410,39 @@ mod use_std {
}
}

impl<R> TransactionFactory<R, Upload>
where
R: Rng + CryptoRng,
{
pub fn transaction(&mut self) -> Upload {
self.transaction_with_keys().0
}

pub fn transaction_with_keys(&mut self) -> (Upload, Vec<SecretKey>) {
let len = self.rng.gen_range(1..1024 * 1024);

let mut bytecode = alloc::vec![0u8; len];
self.rng.fill_bytes(bytecode.as_mut_slice());

let subsection = UploadSubsection::split_bytecode(&bytecode, len / 10)
.expect("Should split the bytecode")[0]
.clone();

let mut builder = TransactionBuilder::<Upload>::upload(UploadBody {
root: subsection.root,
witness_index: 0,
subsection_index: subsection.subsection_index,
subsections_number: subsection.subsections_number,
proof_set: subsection.proof_set,
});
debug_assert_eq!(builder.witnesses().len(), 0);
builder.add_witness(subsection.subsection.into());

let keys = self.fill_transaction(&mut builder);
(builder.finalize(), keys)
}
}

impl<R> TransactionFactory<R, Mint>
where
R: Rng + CryptoRng,
Expand Down Expand Up @@ -458,6 +495,17 @@ mod use_std {
}
}

impl<R> Iterator for TransactionFactory<R, Upload>
where
R: Rng + CryptoRng,
{
type Item = (Upload, Vec<SecretKey>);

fn next(&mut self) -> Option<(Upload, Vec<SecretKey>)> {
Some(self.transaction_with_keys())
}
}

impl<R> Iterator for TransactionFactory<R, Mint>
where
R: Rng + CryptoRng,
Expand Down
170 changes: 168 additions & 2 deletions fuel-tx/src/tests/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ fn transaction_serde_serialization_deserialization() {
.with_maturity((u32::MAX >> 3).into())
.with_witness_limit(Word::MAX >> 4)
.with_max_fee(Word::MAX >> 5),
vec![i],
vec![i.clone()],
vec![o],
vec![w.clone()],
),
Expand Down Expand Up @@ -481,7 +481,7 @@ fn transaction_serde_serialization_deserialization() {
.with_max_fee(Word::MAX >> 5),
vec![],
vec![],
vec![w],
vec![w.clone()],
),
Transaction::upgrade(
UpgradePurpose::StateTransition {
Expand All @@ -497,6 +497,76 @@ fn transaction_serde_serialization_deserialization() {
vec![],
),
]);
assert_encoding_correct(&[
Transaction::upload(
UploadBody {
root: [6; 32].into(),
witness_index: 0,
subsection_index: 0x1234,
subsections_number: 0x4321,
proof_set: vec![[1; 32].into(), [2; 32].into(), [3; 32].into()],
},
Policies::new()
.with_tip(Word::MAX >> 1)
.with_maturity((u32::MAX >> 3).into())
.with_witness_limit(Word::MAX >> 4)
.with_max_fee(Word::MAX >> 5),
vec![i.clone()],
vec![o],
vec![w.clone()],
),
Transaction::upload(
UploadBody {
root: [6; 32].into(),
witness_index: 0,
subsection_index: 0x1234,
subsections_number: 0x4321,
proof_set: vec![[1; 32].into(), [2; 32].into(), [3; 32].into()],
},
Policies::new()
.with_tip(Word::MAX >> 1)
.with_maturity((u32::MAX >> 3).into())
.with_witness_limit(Word::MAX >> 4)
.with_max_fee(Word::MAX >> 5),
vec![],
vec![o],
vec![w.clone()],
),
Transaction::upload(
UploadBody {
root: [6; 32].into(),
witness_index: 0,
subsection_index: 0x1234,
subsections_number: 0x4321,
proof_set: vec![[1; 32].into(), [2; 32].into(), [3; 32].into()],
},
Policies::new()
.with_tip(Word::MAX >> 1)
.with_maturity((u32::MAX >> 3).into())
.with_witness_limit(Word::MAX >> 4)
.with_max_fee(Word::MAX >> 5),
vec![],
vec![],
vec![w.clone()],
),
Transaction::upload(
UploadBody {
root: [6; 32].into(),
witness_index: 0,
subsection_index: 0x1234,
subsections_number: 0x4321,
proof_set: vec![[1; 32].into(), [2; 32].into(), [3; 32].into()],
},
Policies::new()
.with_tip(Word::MAX >> 1)
.with_maturity((u32::MAX >> 3).into())
.with_witness_limit(Word::MAX >> 4)
.with_max_fee(Word::MAX >> 5),
vec![],
vec![],
vec![],
),
]);
assert_encoding_correct(&[Transaction::mint(
rng.gen(),
rng.gen(),
Expand Down Expand Up @@ -837,3 +907,99 @@ fn upgrade_input_coin_data_offset() {
}
}
}

#[allow(non_snake_case)]
#[test]
fn upload__inputs_predicate_offset_at__returns_offset_to_the_predicate() {
let rng = &mut StdRng::seed_from_u64(8586);

let maturity = 10.into();

let inputs: Vec<Vec<Input>> = vec![
vec![],
vec![Input::contract(
rng.gen(),
rng.gen(),
rng.gen(),
rng.gen(),
rng.gen(),
)],
vec![
Input::contract(rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen()),
Input::contract(rng.gen(), rng.gen(), rng.gen(), rng.gen(), rng.gen()),
],
];
let outputs: Vec<Vec<Output>> = vec![
vec![],
vec![Output::coin(rng.gen(), rng.next_u64(), rng.gen())],
vec![Output::contract(rng.gen(), rng.gen(), rng.gen())],
];
let witnesses: Vec<Vec<Witness>> = vec![
vec![],
vec![generate_bytes(rng).into()],
vec![generate_bytes(rng).into(), generate_bytes(rng).into()],
];

let mut predicate = generate_nonempty_padded_bytes(rng);

// force word-unaligned predicate
if predicate.len() % 2 == 0 {
predicate.push(0xff);
}

let predicate_data = generate_bytes(rng);
let predicate_gas_used = rng.gen();

let owner = (*Contract::root_from_code(&predicate)).into();

let input_coin = Input::coin_predicate(
rng.gen(),
owner,
rng.next_u64(),
rng.gen(),
rng.gen(),
predicate_gas_used,
predicate.clone(),
predicate_data,
);

for inputs in inputs.iter() {
for outputs in outputs.iter() {
for witnesses in witnesses.iter() {
// Given
let mut inputs = inputs.clone();
let offset = inputs.len();
inputs.push(input_coin.clone());

let subsections = UploadSubsection::split_bytecode(&[123; 2048], 1023)
.expect("Failed to split bytecode");
let tx = Transaction::upload_from_subsection(
subsections[0].clone(),
Policies::new().with_maturity(maturity),
inputs,
outputs.clone(),
witnesses.clone(),
);

// WHen
let mut tx_p = tx.clone();
tx_p.precompute(&Default::default())
.expect("Should be able to calculate cache");

// Then
let bytes = tx.to_bytes();
let (offset, len) = tx
.inputs_predicate_offset_at(offset)
.expect("Failed to fetch offset");

assert_ne!(bytes::padded_len(&predicate), predicate.len());
assert_eq!(bytes::padded_len(&predicate), len);

assert_eq!(
predicate.as_slice(),
&bytes[offset..offset + predicate.len()]
);
}
}
}
}
9 changes: 9 additions & 0 deletions fuel-tx/src/tests/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ fn to_from_str() {

assert_eq!(tx, tx_p);
});
TransactionFactory::<_, Upload>::from_seed(1295)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was gonna suggest maybe drying this up a little with a shared function:

fn transaction_round_trip_serde<T>()
    where 
        TransactionFactory<StdRng, T>: Iterator,
        T: Into<Transaction>
     {
    TransactionFactory::<_, Upgrade>::from_seed(1295)
        .take(20)
        .for_each(|(tx, _)| {
            // given
            let tx: Transaction = tx.into();
            // when
            let tx_p = tx.to_json();
            let tx_p = Transaction::from_json(tx_p).expect("failed to restore tx");
            // then
            assert_eq!(tx, tx_p);
}

I don't know if this is much better with all the generics.

I think the name to_from_str could be a little clearer either way though.

.take(20)
.for_each(|(tx, _)| {
let tx: Transaction = tx.into();
let tx_p = tx.to_json();
let tx_p = Transaction::from_json(tx_p).expect("failed to restore tx");

assert_eq!(tx, tx_p);
});
TransactionFactory::<_, Mint>::from_seed(1295)
.take(20)
.for_each(|tx| {
Expand Down
42 changes: 41 additions & 1 deletion fuel-tx/src/tests/offset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ fn tx_offset_upgrade() {
chargeable_transaction_parts(&tx, &bytes, &mut cases);
cases.upgrade_purpose = true;

let ofs = Upgrade::upgrade_purpose_offset_static();
let ofs = tx.upgrade_purpose_offset();
let size = tx.upgrade_purpose().size();

let purpose_p = UpgradePurpose::from_bytes(&bytes[ofs..ofs + size]).unwrap();
Expand Down Expand Up @@ -490,6 +490,46 @@ fn tx_offset_upgrade() {
assert!(cases.output_contract_created_id);
}

#[test]
fn tx_offset_upload() {
let mut cases = TestedFields::default();
let number_cases = 100;

// The seed will define how the transaction factory will generate a new transaction.
// Different seeds might implicate on how many of the cases we cover - since we
// assert coverage for all scenarios with the boolean variables above, we need to
// pick a seed that, with low number of cases, will cover everything.
TransactionFactory::<_, Upload>::from_seed(1295)
.take(number_cases)
.for_each(|(tx, _)| {
let bytes = tx.to_bytes();
chargeable_transaction_parts(&tx, &bytes, &mut cases);
});

// Chargeable parts
assert!(cases.utxo_id);
assert!(cases.owner);
assert!(cases.asset_id);
assert!(cases.predicate_coin);
assert!(cases.predicate_message);
assert!(cases.predicate_data_coin);
assert!(cases.predicate_data_message);
assert!(cases.contract_balance_root);
assert!(cases.contract_state_root);
assert!(cases.contract_id);
assert!(cases.sender);
assert!(cases.recipient);
assert!(cases.message_data);
assert!(cases.message_predicate);
assert!(cases.message_predicate_data);
assert!(cases.output_to);
assert!(cases.output_asset_id);
assert!(cases.output_balance_root);
assert!(cases.output_contract_state_root);
assert!(cases.output_contract_created_state_root);
assert!(cases.output_contract_created_id);
}

#[test]
fn tx_offset_mint() {
let number_cases = 100;
Expand Down
Loading
Loading