Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Resumable EVM and heap-allocated callstack #9360

Merged
merged 49 commits into from
Oct 2, 2018
Merged

Conversation

sorpaas
Copy link
Collaborator

@sorpaas sorpaas commented Aug 16, 2018

Mostly fixes #6744. All basic things for this PR are fixed now.

  • The VM interface is changed to three traits -- Exec, which starts a VM execution, and can return a resumable trap, ResumeCall/ResumeCreate to resume execution from call or create traps. Those are accessed using trait objects.
  • Externalities::call/create now has an additional parameter trap. If trap is true (the case for EVM), then this function would return a trap. If trap is false (the case for Wasm), then it will directly execute the context.
  • Split call/create calls in Executive to use a separate struct CallCreateExecutive. This is the main struct that handles all resumes. One can use CallCreateExecutive::exec/resume_call/resume_create to get the resume behavior, or use CallCreateExecutive::consume to get the non-resume behavior.
  • A refactoring on tracing mod. This makes it to use a single struct to record subtracing infos so that executive does not need extra work to record them.

Things to be done:

  • Tracing mod is not working yet.
  • Fix all current tests and write tests for resumes.
  • Fix all TODO proof messages.
  • Find a way to insert crossbeam again. In rare cases we can still exceeds current call stack limit (if too many Wasm contexts are stacked).
  • Provide a compile feature flag to enable wasm trap. With pause PR merged to wasmi, that's doable. (This PR is already getting big, let's do this in another PR.)
  • Make sure we don't have extra clones.

@sorpaas sorpaas added A3-inprogress ⏳ Pull request is in progress. No review needed at this stage. M4-core ⛓ Core client code / Rust. labels Aug 16, 2018
@sorpaas sorpaas added this to the 2.1 milestone Aug 16, 2018
@debris debris self-requested a review September 13, 2018 08:54
@debris
Copy link
Collaborator

debris commented Sep 13, 2018

@tomusdrw let's review this together by the end of this week :)

Copy link
Collaborator

@debris debris left a comment

Choose a reason for hiding this comment

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

lgtm, just a few minor grumbles. Would be good to get another pair of eyes to look at it

self.stack.push(U256::zero());
Ok(InstructionResult::Ok)
},
Err(trap) => {
Ok(InstructionResult::Trap(trap))
Copy link
Collaborator

Choose a reason for hiding this comment

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

why is Err turned into Ok here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If ext.create returns Err, it can only be a trap. (That also means that if trap parameter is passed false, Err case is impossible.) So in here if we want to trap the interpreter, we can directly match this.

We really have three cases for the exec_instruction return type:

  • vm::Result::Err which indicates some errors happened.
  • vm::Result::Ok(Instruction::Result::Trap(..)) which indicates that a trap in the EVM happened.
  • vm::Result::Ok(..) which indicates some other instruction results.

Putting Trap to error and wrap vm::Result::Err can also work, but it means that we need to replicate a lot more From/Into impls to support ? syntax in this function, and I think it may be a little bit overkill.

self.stack.push(U256::zero());
Ok(InstructionResult::Ok)
},
Err(trap) => {
Ok(InstructionResult::Trap(trap))
Copy link
Collaborator

Choose a reason for hiding this comment

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

why is Err turned into Ok here? I just don't understand the flow :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Looks like the same case as #9360 (comment)

Ok(res)
},
CallCreateExecutiveKind::ResumeCreate(..) =>
panic!("Resumable as create, but called resume_call"),
Copy link
Collaborator

Choose a reason for hiding this comment

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

is there any way we can avoid those panics?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We can create traits similar to what we do for vm:

pub trait Exec: Send {
	fn exec(self: Box<Self>, state: &mut State<B>, ...) -> ExecutiveTrapResult<GasLeft>;
}

pub trait ResumeCall: Send {
	fn resume_call(self: Box<Self>, state: &mut State<B>, ...) -> Box<Exec>;
}

pub trait ResumeCreate: Send {
	fn resume_create(self: Box<Self>, state: &mut State<B>, ...) -> Box<Exec>;
}

However, they would need to use traits objects and dynamic dispatch, which is a small amount of runtime overhead. I think it may be a good idea to only use those when in the future we want to expose Executive to be some sort of public API.

action: Action::Create(create.expect("self.prepare_trace_create().is_some(): so we must be tracing: qed")),
trace_address: self.index_stack.clone(),
subtraces: self.sublen_stack.last().cloned().unwrap_or(0),
action: Action::Create(Create::from(params.clone())),
result: Res::Create(CreateResult {
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe it's worth introducing a third variant? or redesigning the implementation to avoid dummy data creation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think this may be okay. U256::zero(), Vec::new(), Address::default() all would just zero out the fields, and wouldn't allocate anything on the heap.

  • From performance's perspective, it wouldn't make any difference if we do dummy data creation or use Option, they would both just zeroing out the fields.
  • From clarity's perspective, this dummy data creation is restraint in ExecutiveTracer and doesn't add any complexity outside of it.

@debris debris added A8-looksgood 🦄 Pull request is reviewed well. and removed A0-pleasereview 🤓 Pull request needs code review. labels Sep 17, 2018
@5chdn 5chdn added A7-looksgoodtestsfail 🤖 Pull request is reviewed well, but cannot be merged due to tests failing. and removed A8-looksgood 🦄 Pull request is reviewed well. labels Sep 19, 2018
@debris debris added A8-looksgood 🦄 Pull request is reviewed well. and removed A7-looksgoodtestsfail 🤖 Pull request is reviewed well, but cannot be merged due to tests failing. labels Sep 24, 2018
@5chdn
Copy link
Contributor

5chdn commented Sep 30, 2018

Could I get a 2nd review here @fckt @andresilva :)

return;
}

let vecindex = self.vecindex_stack.pop().expect("prepare/done_trace are not balanced");
Copy link
Contributor

@NikVolf NikVolf Oct 2, 2018

Choose a reason for hiding this comment

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

would be nice to write why they are always balanced

(and many of those below)

@sorpaas sorpaas merged commit 1e9aebb into master Oct 2, 2018
@sorpaas sorpaas deleted the sp-resume-executive2 branch October 2, 2018 14:33
dvdplm added a commit that referenced this pull request Oct 3, 2018
…mon-deps

* origin/master:
  Add a new RPC `parity_submitWorkDetail` similar `eth_submitWork` but return block hash (#9404)
  Resumable EVM and heap-allocated callstack (#9360)
  update parity-wordlist library (#9682)
@debris
Copy link
Collaborator

debris commented Oct 4, 2018

🎉

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A8-looksgood 🦄 Pull request is reviewed well. M4-core ⛓ Core client code / Rust.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

evm interface overhaul
4 participants