Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
44 changes: 44 additions & 0 deletions docs/website/src/python-sdk/migration-guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,50 @@ Within v0.3: Release-Candidate Changes
.. warning::
v0.3 is not released yet, and these changes are breaking *within* v0.3: a contract that ran on an earlier release candidate needs them. They are also folded into the v0.2 sections below

Message Funding
~~~~~~~~~~~~~~~

``emit``, ``emit_transfer`` and ``deploy`` accept an optional ``UseBalanceParams``
as a positional or keyword ``use_balance`` argument. Omission or ``None`` uses
sender funding. Replace ``use_balance=True, fee_params=fees`` with
``use_balance=gl.contract.UseBalanceParams(fee_params=fees)``

.. code-block:: python

target.emit().run() # Sender-funded
target.emit(gl.contract.UseBalanceParams(fees)).run() # Closed
target.emit(use_balance=gl.contract.UseBalanceParams(fees, descendants=100)).run() # Open
target.emit(gl.contract.UseBalanceParams(fees, descendants=[
gl.message_allocation.InternalAllocation(
recipient=child, call_key="run", fee_params=child_fees, budget=100,
children=[
gl.message_allocation.InternalAllocation(
recipient=grandchild, call_key="run", fee_params=grandchild_fees, budget=40,
),
],
),
])).run() # Pinned
target.emit_transfer(1, gl.contract.UseBalanceParams(fees))
gl.contract.deploy(gl.contract.UseBalanceParams(fees), code=source)

``descendants=None``, ``0`` and ``[]`` select Closed; a positive integer selects
Open, and a nonempty allocation list selects Pinned. The SDK converts the tree
to a flat list of parent indices; contracts do not need to calculate indices

For either allocation type, ``call_key=None`` now means any method, equivalent
to ``gl.message_allocation.ANY_METHOD``. Use ``bytes(32)`` for the exact zero
key previously selected by ``None``: internal deploys/transfers and external
calldata shorter than 4 bytes

Internal allocations accept method names or raw 32-byte keys. External allocations
accept 4-byte EVM selectors, padded on the right to 32 bytes, or raw 32-byte keys
unchanged; strings are rejected. For example, ``call_key=bytes.fromhex("a9059cbb")``
selects the EVM ``transfer(address,uint256)`` method

The executor validates topology, positive node budgets and funding inequalities
independently. The example budgets above are illustrative; actual budgets must
cover primary fees and descendants, including the applicable appeal multiplier

Pre-finalization State Is ``decided``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
6 changes: 5 additions & 1 deletion executor/codegen/data/public-abi.json
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,11 @@
]
},
"below_minimum",
"too_many_rounds"
"too_many_rounds",
{
"head": "descendant_grant",
"tail": ["budget", "tree", "external"]
}
]
},
"forbidden",
Expand Down
46 changes: 44 additions & 2 deletions executor/crates/calldata/src/codec/de/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,8 @@ impl<T: Decode> Decode for Vec<T> {
struct V<T>(std::marker::PhantomData<T>);
impl<T: Decode> Visitor for V<T> {
type Value = Vec<T>;
fn visit_seq<A: SeqAccess>(self, len: u64, mut seq: A) -> Result<Vec<T>, DecodeError> {
let mut result = Vec::with_capacity(len as usize);
fn visit_seq<A: SeqAccess>(self, _len: u64, mut seq: A) -> Result<Vec<T>, DecodeError> {
let mut result = Vec::new();
while let Some(elem) = seq.next_element::<T>()? {
result.push(elem);
}
Expand Down Expand Up @@ -452,3 +452,45 @@ impl<T: Decode> Decode for std::rc::Rc<T> {
T::validate(deserializer)
}
}

#[cfg(test)]
mod tests {
use crate::Value;

use super::super::{Decode, DecodeError, Deserializer, SeqAccess, ValueDeserializer, Visitor};

struct InflatedLen;

impl Deserializer for InflatedLen {
fn deserialize<V: Visitor>(self, visitor: V) -> Result<V::Value, DecodeError> {
visitor.visit_seq(1_000_000, OneBool(true))
}
}

struct OneBool(bool);

impl SeqAccess for OneBool {
fn next_element<T: Decode>(&mut self) -> Result<Option<T>, DecodeError> {
if std::mem::take(&mut self.0) {
T::decode(ValueDeserializer(Value::Bool(true))).map(Some)
} else {
Ok(None)
}
}

fn next_element_validate<T: Decode>(&mut self) -> Result<Option<()>, DecodeError> {
self.next_element::<T>().map(|value| value.map(drop))
}
}

#[test]
fn sequence_decoders_do_not_trust_declared_capacity() {
let decoded = Vec::<bool>::decode(InflatedLen).unwrap();
assert!(decoded.capacity() < 1_000_000);

let Value::Array(decoded) = Value::decode(InflatedLen).unwrap() else {
panic!("expected array");
};
assert!(decoded.capacity() < 1_000_000);
}
}
4 changes: 2 additions & 2 deletions executor/crates/calldata/src/codec/de/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ impl Decode for Value {
Ok(Value::Bytes(value.to_vec()))
}

fn visit_seq<A: SeqAccess>(self, len: u64, mut seq: A) -> Result<Value, DecodeError> {
let mut result = Vec::with_capacity(len as usize);
fn visit_seq<A: SeqAccess>(self, _len: u64, mut seq: A) -> Result<Value, DecodeError> {
let mut result = Vec::new();
while let Some(elem) = seq.next_element::<Value>()? {
result.push(elem);
}
Expand Down
15 changes: 15 additions & 0 deletions executor/crates/sdk-rs/src/abi/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,17 @@ pub mod __VmError {
}
}

pub struct FeeDescendantGrant;

impl FeeDescendantGrant {
pub const fn prefix_(&self) -> &'static str {
"fee descendant_grant"
}
pub const fn budget(&self) -> VmError { VmError(Cow::Borrowed("fee descendant_grant budget")) }
pub const fn tree(&self) -> VmError { VmError(Cow::Borrowed("fee descendant_grant tree")) }
pub const fn external(&self) -> VmError { VmError(Cow::Borrowed("fee descendant_grant external")) }
}

pub struct Fee;

impl Fee {
Expand All @@ -406,6 +417,7 @@ pub mod __VmError {
pub const fn below_minimum(&self) -> VmError { VmError(Cow::Borrowed("fee below_minimum")) }
pub const fn too_many_rounds(&self) -> VmError { VmError(Cow::Borrowed("fee too_many_rounds")) }
pub const fn no_matching_allocation(&self) -> FeeNoMatchingAllocation { FeeNoMatchingAllocation }
pub const fn descendant_grant(&self) -> FeeDescendantGrant { FeeDescendantGrant }
}

pub struct Evm;
Expand Down Expand Up @@ -525,6 +537,9 @@ impl VmError {
"fee no_matching_allocation" |
"fee below_minimum" |
"fee too_many_rounds" |
"fee descendant_grant budget" |
"fee descendant_grant tree" |
"fee descendant_grant external" |
"forbidden" |
"evm reverted" |
"invalid_contract" |
Expand Down
3 changes: 3 additions & 0 deletions executor/crates/sdk-rs/src/abi/fees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ use primitive_types::U256;
genlayer_calldata::Encode,
genlayer_calldata::Decode,
)]
#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
pub struct ExternalMessageParams {
#[cfg_attr(feature = "fuzzing", arbitrary(with = crate::abi::arb::arb_u256))]
pub gas_limit: U256,
#[cfg_attr(feature = "fuzzing", arbitrary(with = crate::abi::arb::arb_u256))]
pub max_gas_price: U256,
}

Expand Down
84 changes: 84 additions & 0 deletions executor/crates/sdk-rs/src/abi/gl_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,84 @@ pub enum On {
Decided,
}

#[derive(Clone, PartialEq, Eq, Debug, Encode)]
#[calldata(untagged)]
#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
pub enum Descendants {
Open(
#[cfg_attr(feature = "fuzzing", arbitrary(with = crate::abi::arb::arb_u256))]
primitive_types::U256,
),
Pinned(Vec<AllocationNode>),
}

impl calldata::codec::Decode for Descendants {
fn decode<D: calldata::codec::Deserializer>(
deserializer: D,
) -> Result<Self, calldata::codec::DecodeError> {
use calldata::codec::{
Decode, DecodeError, Deserializer, SeqAccess, ValueDeserializer, Visitor,
};

struct V;
impl Visitor for V {
type Value = Descendants;

fn visit_bigint(self, value: &num_bigint::BigInt) -> Result<Self::Value, DecodeError> {
primitive_types::U256::decode(ValueDeserializer(calldata::Value::Number(
value.clone(),
)))
.map(Descendants::Open)
}

fn visit_seq<A: SeqAccess>(self, len: u64, seq: A) -> Result<Self::Value, DecodeError> {
struct Sequence<A>(u64, A);
impl<A: SeqAccess> Deserializer for Sequence<A> {
fn deserialize<V: Visitor>(self, visitor: V) -> Result<V::Value, DecodeError> {
visitor.visit_seq(self.0, self.1)
}
}
calldata::LenLimitedVec::<1024, AllocationNode>::decode(Sequence(len, seq))
.map(|nodes| Descendants::Pinned(nodes.into_inner()))
}
}
deserializer.deserialize(V)
}
}

#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
#[calldata(untagged)]
#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
pub enum AllocationNode {
Internal(InternalAllocation),
External(ExternalAllocation),
}

#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
pub struct InternalAllocation {
#[cfg_attr(feature = "fuzzing", arbitrary(with = crate::abi::arb::arb_u256))]
pub parent_index: primitive_types::U256,
pub recipient: calldata::Address,
pub call_key: abi::CallKey,
#[cfg_attr(feature = "fuzzing", arbitrary(with = crate::abi::arb::arb_u256))]
pub budget: primitive_types::U256,
pub fee_params: fees::InternalMessageParams,
pub on: On,
}

#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
pub struct ExternalAllocation {
#[cfg_attr(feature = "fuzzing", arbitrary(with = crate::abi::arb::arb_u256))]
pub parent_index: primitive_types::U256,
pub recipient: calldata::Address,
pub call_key: abi::CallKey,
#[cfg_attr(feature = "fuzzing", arbitrary(with = crate::abi::arb::arb_u256))]
pub budget: primitive_types::U256,
pub fee_params: fees::ExternalMessageParams,
}

fn encode_storage_view<W: calldata::Writer>(
view: &public_abi::StorageView,
enc: &mut calldata::Encoder<W>,
Expand Down Expand Up @@ -581,6 +659,9 @@ pub enum Message {
/// only honored when `use_balance` is set.
#[calldata(default = default_none)]
fee_params: Option<fees::InternalMessageParams>,
/// Descendant funding policy for the emitted child
#[calldata(default = default_none)]
descendants: Option<Descendants>,
},
EmitInternalDeployMessage {
calldata: abi::entry::MainDeployData,
Expand All @@ -597,6 +678,9 @@ pub enum Message {
/// Guest fee params for the balance-funded deploy; see `EmitInternalMessage::fee_params`.
#[calldata(default = default_none)]
fee_params: Option<fees::InternalMessageParams>,
/// Descendant funding policy for the emitted child
#[calldata(default = default_none)]
descendants: Option<Descendants>,
},
EmitEvent {
#[cfg_attr(feature = "fuzzing", arbitrary(with = crate::abi::arb::arb_limited_vec_bytes))]
Expand Down
1 change: 1 addition & 0 deletions executor/crates/sdk-rs/src/abi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub mod wasi;
serde::Serialize,
serde::Deserialize,
)]
#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
pub struct CallKey(
#[calldata(
serialize_with = ::genlayer_calldata::codec::as_bytes::serialize,
Expand Down
Loading