diff --git a/execution_engine/src/runtime_context/mod.rs b/execution_engine/src/runtime_context/mod.rs index 704856b65d..2b7d412200 100644 --- a/execution_engine/src/runtime_context/mod.rs +++ b/execution_engine/src/runtime_context/mod.rs @@ -689,7 +689,8 @@ where | StoredValue::ContractPackage(_) | StoredValue::ContractWasm(_) | StoredValue::MessageTopic(_) - | StoredValue::Message(_) => Ok(()), + | StoredValue::Message(_) + | StoredValue::Reservation(_) => Ok(()), } } diff --git a/resources/test/sse_data_schema.json b/resources/test/sse_data_schema.json index 566e6b3096..fe8d8ab1c9 100644 --- a/resources/test/sse_data_schema.json +++ b/resources/test/sse_data_schema.json @@ -1699,9 +1699,7 @@ "Reserved": { "type": "object", "required": [ - "paid_amount", - "receipt", - "strike_price" + "receipt" ], "properties": { "receipt": { @@ -1711,18 +1709,6 @@ "$ref": "#/definitions/Digest" } ] - }, - "paid_amount": { - "description": "Price paid in the past to reserve space in a future block.", - "type": "integer", - "format": "uint64", - "minimum": 0.0 - }, - "strike_price": { - "description": "The gas price at the time of reservation.", - "type": "integer", - "format": "uint8", - "minimum": 0.0 } }, "additionalProperties": false @@ -3920,6 +3906,19 @@ } }, "additionalProperties": false + }, + { + "description": "A reservation record.", + "type": "object", + "required": [ + "Reservation" + ], + "properties": { + "Reservation": { + "$ref": "#/definitions/ReservationKind" + } + }, + "additionalProperties": false } ] }, @@ -4783,6 +4782,28 @@ } } }, + "ReservationKind": { + "description": "Container for bytes recording location, type and data for a gas reservation", + "type": "object", + "required": [ + "receipt", + "reservation_data", + "reservation_kind" + ], + "properties": { + "receipt": { + "$ref": "#/definitions/Digest" + }, + "reservation_kind": { + "type": "integer", + "format": "uint8", + "minimum": 0.0 + }, + "reservation_data": { + "$ref": "#/definitions/Bytes" + } + } + }, "TransformError": { "description": "Error type for applying and combining transforms.\n\nA `TypeMismatch` occurs when a transform cannot be applied because the types are not compatible (e.g. trying to add a number to a string).", "oneOf": [ diff --git a/storage/src/tracking_copy/byte_size.rs b/storage/src/tracking_copy/byte_size.rs index 417defdd05..7b9a4eeeb1 100644 --- a/storage/src/tracking_copy/byte_size.rs +++ b/storage/src/tracking_copy/byte_size.rs @@ -47,6 +47,7 @@ impl ByteSize for StoredValue { } StoredValue::Message(message_summary) => message_summary.serialized_length(), StoredValue::NamedKey(named_key) => named_key.serialized_length(), + StoredValue::Reservation(reservation_kind) => reservation_kind.serialized_length(), } } } diff --git a/storage/src/tracking_copy/mod.rs b/storage/src/tracking_copy/mod.rs index 69d5fd0d9c..952051785a 100644 --- a/storage/src/tracking_copy/mod.rs +++ b/storage/src/tracking_copy/mod.rs @@ -698,6 +698,10 @@ where StoredValue::Message(_) => { return Ok(query.into_not_found_result("Message value found.")); } + // TODO: We may be interested in this value, check the logic + StoredValue::Reservation(_) => { + return Ok(query.into_not_found_result("Reservation value found.")) + } } } } diff --git a/types/src/execution/transform_kind.rs b/types/src/execution/transform_kind.rs index 8fd52a042e..5b94e04d64 100644 --- a/types/src/execution/transform_kind.rs +++ b/types/src/execution/transform_kind.rs @@ -183,6 +183,11 @@ impl TransformKindV2 { let found = "Message".to_string(); Err(StoredValueTypeMismatch::new(expected, found).into()) } + StoredValue::Reservation(_) => { + let expected = "Contract or Account".to_string(); + let found = "Reservation".to_string(); + Err(StoredValueTypeMismatch::new(expected, found).into()) + } }, TransformKindV2::Failure(error) => Err(error), } diff --git a/types/src/gens.rs b/types/src/gens.rs index 63f9462c80..c80d1d171b 100644 --- a/types/src/gens.rs +++ b/types/src/gens.rs @@ -754,6 +754,7 @@ pub fn stored_value_arb() -> impl Strategy { StoredValue::MessageTopic(_) => stored_value, StoredValue::Message(_) => stored_value, StoredValue::NamedKey(_) => stored_value, + StoredValue::Reservation(_) => stored_value, }) } diff --git a/types/src/stored_value.rs b/types/src/stored_value.rs index df4b2f3480..2598b39e1e 100644 --- a/types/src/stored_value.rs +++ b/types/src/stored_value.rs @@ -23,7 +23,10 @@ use crate::{ contract_wasm::ContractWasm, contracts::{Contract, ContractPackage}, package::Package, - system::auction::{Bid, BidKind, EraInfo, UnbondingPurse, WithdrawPurse}, + system::{ + auction::{Bid, BidKind, EraInfo, UnbondingPurse, WithdrawPurse}, + reservations::ReservationKind, + }, AddressableEntity, ByteCode, CLValue, DeployInfo, TransferV1, }; pub use global_state_identifier::GlobalStateIdentifier; @@ -50,6 +53,7 @@ enum Tag { MessageTopic = 15, Message = 16, NamedKey = 17, + Reservation = 18, } /// A value stored in Global State. @@ -98,6 +102,8 @@ pub enum StoredValue { Message(MessageChecksum), /// A NamedKey record. NamedKey(NamedKeyValue), + /// A reservation record. + Reservation(ReservationKind), } impl StoredValue { @@ -360,6 +366,7 @@ impl StoredValue { StoredValue::MessageTopic(_) => "MessageTopic".to_string(), StoredValue::Message(_) => "Message".to_string(), StoredValue::NamedKey(_) => "NamedKey".to_string(), + StoredValue::Reservation(_) => "Reservation".to_string(), } } @@ -383,6 +390,7 @@ impl StoredValue { StoredValue::MessageTopic(_) => Tag::MessageTopic, StoredValue::Message(_) => Tag::Message, StoredValue::NamedKey(_) => Tag::NamedKey, + StoredValue::Reservation(_) => Tag::Reservation, } } } @@ -664,6 +672,7 @@ impl ToBytes for StoredValue { } StoredValue::Message(message_digest) => message_digest.serialized_length(), StoredValue::NamedKey(named_key_value) => named_key_value.serialized_length(), + StoredValue::Reservation(reservation_kind) => reservation_kind.serialized_length(), } } @@ -690,6 +699,7 @@ impl ToBytes for StoredValue { } StoredValue::Message(message_digest) => message_digest.write_bytes(writer), StoredValue::NamedKey(named_key_value) => named_key_value.write_bytes(writer), + StoredValue::Reservation(reservation_kind) => reservation_kind.write_bytes(writer), } } } @@ -782,6 +792,7 @@ mod serde_helpers { MessageTopic(&'a MessageTopicSummary), Message(&'a MessageChecksum), NamedKey(&'a NamedKeyValue), + Reservation(&'a ReservationKind), } #[derive(Deserialize)] @@ -837,6 +848,7 @@ mod serde_helpers { HumanReadableSerHelper::Message(message_digest) } StoredValue::NamedKey(payload) => HumanReadableSerHelper::NamedKey(payload), + StoredValue::Reservation(payload) => HumanReadableSerHelper::Reservation(payload), } } } diff --git a/types/src/system.rs b/types/src/system.rs index b4e39ad8e1..6f3d5edb7f 100644 --- a/types/src/system.rs +++ b/types/src/system.rs @@ -4,6 +4,7 @@ mod caller; mod error; pub mod handle_payment; pub mod mint; +pub mod reservations; pub mod standard_payment; mod system_contract_type; diff --git a/types/src/system/reservations.rs b/types/src/system/reservations.rs new file mode 100644 index 0000000000..ee2897ed0e --- /dev/null +++ b/types/src/system/reservations.rs @@ -0,0 +1,4 @@ +//! Contains implementation of the gas reservation system +mod reservation_kind; + +pub use reservation_kind::ReservationKind; diff --git a/types/src/system/reservations/reservation_kind.rs b/types/src/system/reservations/reservation_kind.rs new file mode 100644 index 0000000000..bead14ce47 --- /dev/null +++ b/types/src/system/reservations/reservation_kind.rs @@ -0,0 +1,42 @@ +use crate::{ + bytesrepr, + bytesrepr::{Bytes, ToBytes, U8_SERIALIZED_LENGTH}, + Digest, +}; +use alloc::vec::Vec; +#[cfg(feature = "datasize")] +use datasize::DataSize; +#[cfg(feature = "json-schema")] +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +/// Container for bytes recording location, type and data for a gas reservation +#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(feature = "datasize", derive(DataSize))] +#[cfg_attr(feature = "json-schema", derive(JsonSchema))] +pub struct ReservationKind { + receipt: Digest, + reservation_kind: u8, + reservation_data: Bytes, +} + +impl ToBytes for ReservationKind { + fn to_bytes(&self) -> Result, bytesrepr::Error> { + let mut buffer = bytesrepr::allocate_buffer(self)?; + self.write_bytes(&mut buffer)?; + Ok(buffer) + } + + fn serialized_length(&self) -> usize { + self.receipt.serialized_length() + + U8_SERIALIZED_LENGTH + + self.reservation_data.serialized_length() + } + + fn write_bytes(&self, writer: &mut Vec) -> Result<(), bytesrepr::Error> { + self.receipt.write_bytes(writer)?; + self.reservation_kind.write_bytes(writer)?; + self.reservation_data.write_bytes(writer)?; + Ok(()) + } +} diff --git a/types/src/transaction/pricing_mode.rs b/types/src/transaction/pricing_mode.rs index b50f13a52c..be5a2f5f51 100644 --- a/types/src/transaction/pricing_mode.rs +++ b/types/src/transaction/pricing_mode.rs @@ -57,10 +57,6 @@ pub enum PricingMode { Reserved { /// Pre-paid receipt. receipt: Digest, - /// Price paid in the past to reserve space in a future block. - paid_amount: u64, - /// The gas price at the time of reservation. - strike_price: u8, }, } @@ -77,11 +73,7 @@ impl PricingMode { 1 => PricingMode::Fixed { gas_price_tolerance: rng.gen(), }, - 2 => PricingMode::Reserved { - receipt: rng.gen(), - paid_amount: rng.gen(), - strike_price: rng.gen(), - }, + 2 => PricingMode::Reserved { receipt: rng.gen() }, _ => unreachable!(), } } @@ -101,15 +93,7 @@ impl Display for PricingMode { payment_amount, gas_price, standard_payment ) } - PricingMode::Reserved { - receipt, - paid_amount, - strike_price, - } => write!( - formatter, - "reserved: {} paid_amount: {} strike_price: {}", - receipt, paid_amount, strike_price - ), + PricingMode::Reserved { receipt } => write!(formatter, "reserved: {}", receipt), PricingMode::Fixed { gas_price_tolerance, } => write!(formatter, "fixed pricing {}", gas_price_tolerance), @@ -130,15 +114,9 @@ impl ToBytes for PricingMode { gas_price.write_bytes(writer)?; standard_payment.write_bytes(writer) } - PricingMode::Reserved { - receipt, - paid_amount, - strike_price, - } => { + PricingMode::Reserved { receipt } => { RESERVED_TAG.write_bytes(writer)?; - receipt.write_bytes(writer)?; - paid_amount.write_bytes(writer)?; - strike_price.write_bytes(writer) + receipt.write_bytes(writer) } PricingMode::Fixed { gas_price_tolerance, @@ -167,15 +145,7 @@ impl ToBytes for PricingMode { + gas_price.serialized_length() + standard_payment.serialized_length() } - PricingMode::Reserved { - receipt, - paid_amount, - strike_price, - } => { - receipt.serialized_length() - + paid_amount.serialized_length() - + strike_price.serialized_length() - } + PricingMode::Reserved { receipt } => receipt.serialized_length(), PricingMode::Fixed { gas_price_tolerance, } => gas_price_tolerance.serialized_length(), @@ -212,16 +182,7 @@ impl FromBytes for PricingMode { } RESERVED_TAG => { let (receipt, remainder) = Digest::from_bytes(remainder)?; - let (paid_amount, remainder) = u64::from_bytes(remainder)?; - let (strike_price, remainder) = u8::from_bytes(remainder)?; - Ok(( - PricingMode::Reserved { - receipt, - paid_amount, - strike_price, - }, - remainder, - )) + Ok((PricingMode::Reserved { receipt }, remainder)) } _ => Err(bytesrepr::Error::Formatting), } diff --git a/types/src/transaction/transaction_v1.rs b/types/src/transaction/transaction_v1.rs index 9ec4e34bbb..f94b79ef3e 100644 --- a/types/src/transaction/transaction_v1.rs +++ b/types/src/transaction/transaction_v1.rs @@ -675,18 +675,10 @@ impl GasLimited for TransactionV1 { }; Gas::new(U512::from(computation_limit)) } - PricingMode::Reserved { - paid_amount, - strike_price, - .. - } => { - // prepaid, if receipt is legit (future use) - Gas::from_price(U512::from(*paid_amount), *strike_price).ok_or( - InvalidTransactionV1::GasPriceConversion { - amount: *paid_amount, - gas_price: *strike_price, - }, - )? + PricingMode::Reserved { receipt } => { + return Err(InvalidTransactionV1::InvalidPricingMode { + price_mode: PricingMode::Reserved { receipt: *receipt }, + }) } }; Ok(gas) @@ -1110,8 +1102,6 @@ mod tests { let reserved_mode = PricingMode::Reserved { receipt: Default::default(), - paid_amount: Default::default(), - strike_price: Default::default(), }; let reserved_transaction = TransactionV1Builder::new_random(rng) @@ -1307,64 +1297,4 @@ mod tests { "in fixed pricing, the cost should == limit * gas_price" ); } - - #[test] - fn should_have_limit_but_no_cost_for_reserved() { - reserved_pricing(500u64, 1u8); - } - - #[test] - fn should_respect_strike_price_for_reserved() { - reserved_pricing(500u64, 2u8); - } - - #[cfg(test)] - fn reserved_pricing(paid_amount: u64, strike_price: u8) { - let mut chainspec = Chainspec::default(); - let chain_name = "net-1"; - chainspec - .with_chain_name(chain_name.to_string()) - .with_pricing_handling(PricingHandling::Fixed) - .with_allow_reservations(true); - - let rng = &mut TestRng::new(); - let builder = TransactionV1Builder::new_random(rng) - .with_chain_name(chain_name) - .with_pricing_mode(PricingMode::Reserved { - paid_amount, - strike_price, - receipt: Digest::default(), - }); - let transaction = builder.build().expect("should build"); - let mut gas_price = 1; - let limit = transaction - .gas_limit(&chainspec) - .expect("should limit") - .value() - .as_u64(); - assert_eq!( - limit, - paid_amount / strike_price as u64, - "in reserved pricing, limit should == paid_amount / strike price" - ); - let cost = transaction - .gas_cost(&chainspec, gas_price) - .expect("should cost") - .value(); - assert_eq!( - cost, - U512::zero(), - "in reserved pricing, cost should == 0 as it was prepaid" - ); - gas_price += 1; - let cost = transaction - .gas_cost(&chainspec, gas_price) - .expect("should cost") - .value(); - assert_eq!( - cost, - U512::zero(), - "in reserved pricing, gas price does not matter as it was prepaid" - ); - } }