-
Notifications
You must be signed in to change notification settings - Fork 16
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
Feat: Ecall/Halt #258
Feat: Ecall/Halt #258
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not check the exit_code
.
The VM looks for the exit code in register x10 / a0
and requires it to be 0 for success.
While non-zero values are an error, and they cannot be proven. E.g. panic is halt(1)
.
There are two options:
- Read the register
CENO_PLATFORM.reg_arg0()
with success value0
.
… OR … - redefine
halt
tohalt_success
. Inceno_rt
, call it only for success, not for panic:
pub fn halt(exit_code: u32) -> ! {
if exit_code == 0 {
// SUCCESS
unsafe {
asm!(
// Set the ecall code HALT_SUCCESS.
"li t0, 0x0",
);
riscv::asm::ecall();
}
}
#[allow(clippy::empty_loop)]
loop {}
}
cb, | ||
[ECALL_HALT[0].into(), ECALL_HALT[1].into()], | ||
None, | ||
Some(0.into()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We define PC=0
as invalid, and no other instruction can execute after that. I would document this requirement right here as follows:
assert!(!CENO_PLATFORM.can_execute(0), "PC=0 means the program is terminated.");
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: we can define 0 as constant EXIT_PC to improve the readibility
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want PC=0
to be invalid, I would suggest also adding 0x0000_0000
in the first 32 bits of memory. Just for consistency. (32 bits of all zeroes is Risc-V's way of specifying a guaranteed invalid instruction in every version of the instruction set.)
|
That incurs a cost on every instruction then. There should be a better way. For instance, initialize by writing state (pc_start, ts_start), and require a write of (pc=0, ts) to exist. Or require the HaltCircuit to exist with exactly one instance. |
The cost is negligible as we stores the rlc not the 3-tuple (or old 2-tuple). |
The problem with this approach is that we need to add a lookup record for program table. But we don't know that |
As a desired feature of zkvm prover/verifier: we should skip generating opcode proof if the |
How do you plan on checking the presence of the code in the state? However that works, it could check that (pc=0, ts=0) or whatever special value. Same as checking (pc, ts, code=1). |
That is not part of this PR though, right? That would be issue #127 |
Regarding non-zero exit codes: this does not need to be supported by the zkVM circuits. It is just impossible to generate a proof for an execution that did not terminate correctly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That works now with the exit code!
No. For computation that ends with error we can detect it by exit_code. But for program that failed by misaligned memory address, that might be hard to prove. |
That is a lot of extended multiplications in the prover and verifier. I’d be interested to learn how much those weight though. |
We assert that the value read from register x10 is equal to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
awesome job!! 🔥 just left reviews with few questions
cb, | ||
[ECALL_HALT[0].into(), ECALL_HALT[1].into()], | ||
None, | ||
Some(0.into()), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: we can define 0 as constant EXIT_PC to improve the readibility
@@ -66,18 +66,39 @@ pub struct ZKVMTableProof<E: ExtensionField, PCS: PolynomialCommitmentScheme<E>> | |||
pub wits_opening_proof: PCS::Proof, | |||
} | |||
|
|||
#[derive(Default, Clone, Debug)] | |||
pub struct PublicValues<T: Default + Clone + Debug> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A side topic: would we plan to separate public input into 2 kinds, one are hinted from prover, and the others are set from verifier directly? For those hinted from prover, we can have mark #[serde(skip)]
to make proof size smaller
A few notes on design: Do we have use for exit codes that go beyond success / failure? If not, we might want to consider using For successful exit, Making debugging simpler is one of the big lessons we learned from previously working on Mozak's zkVM. Overloading |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM and nice job! +1 for resolving other pending issues in later PR
Fixes #125.