Skip to content
This repository has been archived by the owner on Aug 14, 2023. It is now read-only.

Adjust verify for Self-Spawn #479

Open
YaronWittenstein opened this issue Dec 23, 2021 · 0 comments
Open

Adjust verify for Self-Spawn #479

YaronWittenstein opened this issue Dec 23, 2021 · 0 comments
Assignees
Labels
AA Related to the Accounts Abstraction simple-coin-iteration-4 svm svm-core SVM core change

Comments

@YaronWittenstein
Copy link
Contributor

YaronWittenstein commented Dec 23, 2021

Depends on #470 #467 (from the Simple Coin Iteration 3)

In this SMIP, we want to extend the verify of the Runtime to work for Self-Spawn transactions.
(and not only for Non-Self Spawn ones).

Running verify in Self-Spawn mode is unique because the Principal account is in a Pending state (see #467).
It has no Template it's associated with yet.

The way to make it work is to make the Runtime execute the verify functionally.
We know what code needs to run for verify (since the Template is specified at the Spawn transaction).
Additionally, we know that we are only allowed reads from the Immutable Storage of the-to-be-self-spawned Account.

Even though the Account isn't active and has no Storage yet, we can make that verify behave as if it had Storage.
We'll use the immutable_data supplied in the Spawn Transaction, add another layer of indirection to the Storage host functions, and it should work.

To add that layer of indirection, we'd bypass the AccountStorage and leverage the FuncData struct we've introduced in #470. Until now, the immutable_data was being ignored - not anymore...

Implementation Proposal

In #470, we had this code:

...
...
...

#[derive(Debug, Clone, PartialEq)]
pub struct FuncData {
  fn_kind: FuncKind,
  tx_kind: TxKind,
  immutable_data: Option<Vec<u8>>
}

...
...
...

fn read<T, F: () -> T>(env: &FuncEnv, section_idx: u32, f: F) -> T {
  assert!(env.read_allowed(section_idx));

  if env.within_self_spawn_verify() {
    todo!("Simple Coin Iteration #4")
  }
  else {
    f()
  }
}

Now the read should in high-level work such as this:

fn read<T, F: () -> T>(env: &FuncEnv, section_idx: u32, f: F) -> T {
  assert!(env.read_allowed(section_idx));

  if env.within_self_spawn_verify() {
    let immutable_data = env.func_data();

    // We need to extract the  `immutable layout` by taking the `Template Address`
    // given in the `Spawn Transaction` and then do something such as:
    // 
    // let gs = func_env.gs();
    // immutable_section_idx = 0;
    let immutable_layout = AccountStorge::template_layout(gs.clone(), &template_addr, immutable_section_idx)?;
    
    // use the same code as in `AccountStorage::get_var`
    // see issue #470: https://github.com/spacemeshos/svm/issues/471
  }
  else {
    f()
  }
}

We essentially reuse the same logic as in issue #471.

Given the immutable_data buffer and the Immutable Storage Section Layout, we should be able to extract the required variable.

We need to load that Layout only once to optimize the code.
So the above line of code:

 let immutable_layout = AccountStorge::template_layout(gs.clone(), &template_addr, immutable_section_idx)?;

The loading of the Immutable Storage Section Layout should be done externally.
Probably it should become part of the FuncData.

It is better to make it optional (of type Option<FixedLayout>) since we want to have it only for Self-Spawn Verify.
In any other case, we just hit the AccountStorage directly.

Before the ctor running

We've another leftover from issue #477 that we need to fill in.
Here is the code appearing under #477.

impl Runtime {
  pub fn spawn(
        &mut self,
        envelope: &Envelope,
        message: &[u8],
        context: &Context,
    ) -> SpawnReceipt { 
  info!("Runtime `spawn`");

  let gas_left = GasTank::new(envelope.gas_limit());
  let spawn = SpawnAccount::decode_bytes(message).expect(ERR_VALIDATE_SPAWN);

  // If the `Principal` is a `Pending Account` (a.k.a a `Stub Account`)
  // then we say we're running a `Self-Spawn` transaction.
  let self_spawn = ...

  if self_spawn {
    todo!("Simple Coin Iteration #4")
  }
  else {
    // now the `self.create_account` expects an additional `immutable_data` param
    self.create_account(
            &target,
            template_addr.clone(),
            account.name().to_string(),
            0,
            0,
            &spawn.immutable_data;
        )
        .unwrap();

     self.call_ctor(&spawn, target, envelope, context, gas_left)
   }
 }

We need to do the following:

impl Runtime {
  pub fn spawn(
        &mut self,
        envelope: &Envelope,
        message: &[u8],
        context: &Context,
    ) -> SpawnReceipt { 
  info!("Runtime `spawn`");

  let gas_left = GasTank::new(envelope.gas_limit());
  let spawn = SpawnAccount::decode_bytes(message).expect(ERR_VALIDATE_SPAWN);

  // If the `Principal` is a `Pending Account` (a.k.a a `Stub Account`)
  // then we say we're running a `Self-Spawn` transaction.
  let self_spawn = ...

  let mut spawned_account: Option<AccountStorage> = None;

  if self_spawn {
    // We don't need to create the `Account`
    let mut account = AccountStorage::new(..);
    account.set_immutable( &spawn.immutable_data);
  }
  else {
    // now the `self.create_account` expects an additional `immutable_data` param
   spawned_account = self.create_account(
            &target,
            template_addr.clone(),
            account.name().to_string(),
            0,
            0,
            &spawn.immutable_data;
        )
        .unwrap();
   }

    let receipt = self.call_ctor(&spawn, target, envelope, context, gas_left);

   if receipt.success { 
       if self_spawn {
           // Turn the `Account` into `Active` and set it's `Template Address`.
           //  We've set above its `Immutable Storage` so that the `ctor` would work.
          AccountStorage::activate(&account_addr, &template_addr);
       }
       else {
           spawned_account.activate(&account_addr);
       }
    }
    else {
         // The Transaction has failed
        
          if self_spawn {
               // We can do nothing, but it'd be nicer to remove the `immutable_storage`
               // of the `Pending Principal`
               let mut account = AccountStorage::new(..);
               account.set_immutable(&[]);
          }
          else {
                // The `Non-Self Spawn` has failed
                // We need to rollback the account created before the `call_ctor`.
                // Everything related to the `spawned_account` should be  deleted.
                //
                // It was created as an implementation decision.
                // But basically, we can't call it an `Account` only after the `ctor` has succeeded.
                spawned_account.destroy();
           } 
    } 

   receipt
 }
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
AA Related to the Accounts Abstraction simple-coin-iteration-4 svm svm-core SVM core change
Projects
None yet
Development

No branches or pull requests

2 participants