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

Misc problems for interpreter simulation #612

Open
InfiniteEchoes opened this issue Sep 11, 2024 · 3 comments
Open

Misc problems for interpreter simulation #612

InfiniteEchoes opened this issue Sep 11, 2024 · 3 comments

Comments

@InfiniteEchoes
Copy link
Collaborator

InfiniteEchoes commented Sep 11, 2024

1 Deserializing constant

In Bytecode::LdConst's case for execute_instruction, we will use deserialize_constant to deserialize a raw stream of u8 into a specific constant type.

  Bytecode::LdConst(idx) => {
      let constant = resolver.constant_at(*idx); //*)
      gas_meter.charge_ld_const(NumBytes::new(constant.data.len() as u64))?;

      let val = Value::deserialize_constant(constant).ok_or_else(|| {
          PartialVMError::new(StatusCode::VERIFIER_INVARIANT_VIOLATION).with_message(
              "Verifier failed to verify the deserialization of constants".to_owned(),
          )
      })?;

      gas_meter.charge_ld_const_after_deserialization(&val)?;

      interpreter.operand_stack.push(val)?

In values_impl, this function is defined by the following:

    fn constant_sig_token_to_layout(constant_signature: &SignatureToken) -> Option<MoveTypeLayout> {
        use MoveTypeLayout as L;
        use SignatureToken as S;

        Some(match constant_signature {
            S::Bool => L::Bool,
            S::U8 => L::U8,
            S::U16 => L::U16,
            S::U32 => L::U32,
            S::U64 => L::U64,
            S::U128 => L::U128,
            S::U256 => L::U256,
            S::Address => L::Address,
            S::Signer => return None,
            S::Vector(inner) => L::Vector(Box::new(Self::constant_sig_token_to_layout(inner)?)),
            // Not yet supported
            S::Struct(_) | S::StructInstantiation(_) => return None,
            // Not allowed/Not meaningful
            S::TypeParameter(_) | S::Reference(_) | S::MutableReference(_) => return None,
        })
    }

    pub fn simple_deserialize(blob: &[u8], layout: &MoveTypeLayout) -> Option<Value> {
        bcs::from_bytes_seed(SeedWrapper { layout }, blob).ok()
    }

    pub fn deserialize_constant(constant: &Constant) -> Option<Value> {
        let layout = Self::constant_sig_token_to_layout(&constant.type_)?;
        Value::simple_deserialize(&constant.data, &layout)
    }

Other dependencies are not presented in the above code.

But generally for execute_instruction to deserialize a constant:

  1. Invoke the resolver to get a value of Constant type, that contains a specified type of SignatureToken and a stream of [u8]
  2. Preprocess the type of Constant into a almost identical "layout", filtering out irrevalent items such as Struct, TypeParameter which are not types for constants. The layout would be stored in a SeedWrapper
  3. SeedWrapper also implements a deserializer. We use external bcs library to invoke the deserializer to massage the [u8] stream into a single Value.

Question: taken that the deserialization procedure involves external library, do we need to implement the deserializing process?

@InfiniteEchoes
Copy link
Collaborator Author

InfiniteEchoes commented Sep 11, 2024

2 Reference Counting

move_loc and store_loc for Bytecode::MoveLoc & Bytecode::StLoc involves swap_loc. Here's its definition:

    fn swap_loc(&mut self, idx: usize, x: Value, violation_check: bool) -> PartialVMResult<Value> {
        let mut v = self.0.borrow_mut();
        match v.get_mut(idx) {
            Some(v) => {
                if violation_check {
                    if let ValueImpl::Container(c) = v {
                        if c.rc_count() > 1 {
                            return Err(PartialVMError::new(
                                StatusCode::UNKNOWN_INVARIANT_VIOLATION_ERROR,
                            )
                            .with_message(
                                "moving container with dangling references".to_string(),
                            ));
                        }
                    }
                }
                Ok(Value(std::mem::replace(v, x.0)))
            }
            None => Err(
                PartialVMError::new(StatusCode::VERIFIER_INVARIANT_VIOLATION).with_message(
                    format!("local index out of bounds: got {}, len: {}", idx, v.len()),
                ),
            ),
        }
    }

In which there's a particular line:

if c.rc_count() > 1 { // <- we are using the counter for rc values here

So the problem is, we have to concern about rc_count for a Rc type wrapped in Container type. Typically speaking we can just ignore the Rc type. Question: should we implement swap_loc function? If yes, how?

@clarus
Copy link
Collaborator

clarus commented Sep 11, 2024

Question: taken that the deserialization procedure involves external library, do we need to implement the deserializing process?

We can ignore the external libraries for now and axiomatize the serialization/deserialization functions. This will block the evaluation if we try to run Compure on the code but given that we do not need to test the interpreter to compare it to the Rust code, this should not be an issue.

@clarus
Copy link
Collaborator

clarus commented Sep 11, 2024

Question: should we implement swap_loc function? If yes, how?

OK, this function seems quite complicated; this is the first time I have seen some code using the rc_count() value!

For now, what we can do is assume that violation_check is always false. It will miss some execution errors but we will see later how to deal with them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants