From 94182c9b3d6c238b3a4c9db21865795b78b716ca Mon Sep 17 00:00:00 2001 From: Oleg Andreev Date: Tue, 30 Mar 2021 02:50:18 +0300 Subject: [PATCH 1/3] zkvm: eval opcode spec --- zkvm/docs/zkvm-spec.md | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/zkvm/docs/zkvm-spec.md b/zkvm/docs/zkvm-spec.md index 7ecaa24ad..e10335855 100644 --- a/zkvm/docs/zkvm-spec.md +++ b/zkvm/docs/zkvm-spec.md @@ -842,7 +842,7 @@ Then, the VM executes the current program till completion: 1. Each instruction is read at the current program offset, including its immediate data (if any). 2. Program offset is advanced immediately after reading the instruction to the next instruction. 3. The instruction is executed per [specification below](#instructions). If the instruction fails, VM exits early with an error result. -4. If VM encounters [`call`](#call), [`signid`](#signid) or [`signtag`](#signtag) instruction, the new program with offset zero is set as the current program. The next iteration of the vm will start from the beginning of the new program. +4. If VM encounters [`eval`](#eval), [`call`](#call), [`signid`](#signid) or [`signtag`](#signtag) instruction, the new program with offset zero is set as the current program. The next iteration of the vm will start from the beginning of the new program. 5. If the offset is less than the current program’s length, a new instruction is read (go back to step 1). 6. Otherwise (reached the end of the current program): 1. If the program stack is not empty, pop top item from the program stack and set it to the current program. Go to step 5. @@ -965,10 +965,11 @@ Code | Instruction | Stack diagram | 0x1b | [`output:k`](#output) | _items... pred_ → ø | Modifies [tx log](#transaction-log) 0x1c | [`contract:k`](#contract) | _items... pred_ → _contract_ | 0x1d | [`log`](#log) | _data_ → ø | Modifies [tx log](#transaction-log) -0x1e | [`call`](#call) |_contract(P) proof prog_ → _results..._ | [Defers point operations](#deferred-point-operations) -0x1f | [`signtx`](#signtx) | _contract_ → _results..._ | Modifies [deferred verification keys](#transaction-signature) -0x20 | [`signid`](#signid) |_contract prog sig_ → _results..._ | [Defers point operations](#deferred-point-operations) -0x21 | [`signtag`](#signtag) |_contract prog sig_ → _results..._ | [Defers point operations](#deferred-point-operations) +0x1e | [`eval`](#eval) | _prog_ → _results..._ | +0x1f | [`call`](#call) |_contract(P) proof prog_ → _results..._ | [Defers point operations](#deferred-point-operations) +0x20 | [`signtx`](#signtx) | _contract_ → _results..._ | Modifies [deferred verification keys](#transaction-signature) +0x21 | [`signid`](#signid) |_contract prog sig_ → _results..._ | [Defers point operations](#deferred-point-operations) +0x22 | [`signtag`](#signtag) |_contract prog sig_ → _results..._ | [Defers point operations](#deferred-point-operations) — | [`ext`](#ext) | ø → ø | Fails if [extension flag](#vm-state) is not set. @@ -1409,6 +1410,16 @@ _data_ **log** → ø Fails if `data` is not a [string](#string-type). +#### eval + +_prog_ **eval** → _results..._ + +1. Pops [program](#program-type) `prog`. +2. Set the `prog` as current. + +Fails if `prog` is not a [program](#program-type). + + #### call _contract(P) proof prog_ **call** → _results..._ From 3f841f898a2e878c76737d69b040a443fe2c811e Mon Sep 17 00:00:00 2001 From: Oleg Andreev Date: Tue, 30 Mar 2021 02:56:33 +0300 Subject: [PATCH 2/3] zkvm: eval impl --- zkvm/src/debug.rs | 1 + zkvm/src/ops.rs | 20 ++++++++++++++++---- zkvm/src/program.rs | 2 +- zkvm/src/vm.rs | 10 ++++++++++ 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/zkvm/src/debug.rs b/zkvm/src/debug.rs index febc2105d..5c0d2a38f 100644 --- a/zkvm/src/debug.rs +++ b/zkvm/src/debug.rs @@ -126,6 +126,7 @@ impl Instruction { Instruction::Output(k) => write!(f, "output:{}", k), Instruction::Contract(k) => write!(f, "contract:{}", k), Instruction::Log => write!(f, "log"), + Instruction::Eval => write!(f, "eval"), Instruction::Call => write!(f, "call"), Instruction::Signtx => write!(f, "signtx"), Instruction::Signid => write!(f, "signid"), diff --git a/zkvm/src/ops.rs b/zkvm/src/ops.rs index 1558ac655..c0b5022a2 100644 --- a/zkvm/src/ops.rs +++ b/zkvm/src/ops.rs @@ -387,6 +387,14 @@ pub enum Instruction { /// Fails if `data` is not a _string_. Log, + /// _prog_ **eval** → _results..._ + /// + /// 1. Pops _program_ `prog`. + /// 2. Set the `prog` as current. + /// + /// Fails if `prog` is not a _program_. + Eval, + /// _contract(P) proof prog_ **call** → _results..._ /// /// 1. Pops _program_ `prog`, the _call proof_ `proof`, and a _contract_ `contract`. @@ -561,17 +569,19 @@ pub enum Opcode { Contract = 0x1c, /// A code for [Instruction::Log] Log = 0x1d, + /// A code for [Instruction::Eval] + Eval = 0x1e, /// A code for [Instruction::Call] - Call = 0x1e, + Call = 0x1f, /// A code for [Instruction::Signtx] - Signtx = 0x1f, + Signtx = 0x20, /// A code for [Instruction::Signid] - Signid = 0x20, + Signid = 0x21, /// A code for [Instruction::Signtag] Signtag = MAX_OPCODE, } -const MAX_OPCODE: u8 = 0x21; +const MAX_OPCODE: u8 = 0x22; impl Opcode { /// Converts the opcode to `u8`. @@ -650,6 +660,7 @@ impl Encodable for Instruction { w.write_u32(b"k", *k as u32)?; } Instruction::Log => write(Opcode::Log)?, + Instruction::Eval => write(Opcode::Eval)?, Instruction::Call => write(Opcode::Call)?, Instruction::Signtx => write(Opcode::Signtx)?, Instruction::Signid => write(Opcode::Signid)?, @@ -749,6 +760,7 @@ impl Instruction { Ok(Instruction::Contract(k)) } Opcode::Log => Ok(Instruction::Log), + Opcode::Eval => Ok(Instruction::Eval), Opcode::Call => Ok(Instruction::Call), Opcode::Signtx => Ok(Instruction::Signtx), Opcode::Signid => Ok(Instruction::Signid), diff --git a/zkvm/src/program.rs b/zkvm/src/program.rs index 88a93eb92..f8a2f55d2 100644 --- a/zkvm/src/program.rs +++ b/zkvm/src/program.rs @@ -187,8 +187,8 @@ impl Program { def_op!(contract, Contract, usize, "contract:k"); def_op!(log, Log, "log"); + def_op!(eval, Eval, "eval"); def_op!(call, Call, "call"); - def_op!(signtx, Signtx, "signtx"); def_op!(signid, Signid, "signid"); def_op!(signtag, Signtag, "signtag"); diff --git a/zkvm/src/vm.rs b/zkvm/src/vm.rs index 1546f321f..de2603438 100644 --- a/zkvm/src/vm.rs +++ b/zkvm/src/vm.rs @@ -177,6 +177,7 @@ where Instruction::Output(k) => self.output(k)?, Instruction::Contract(k) => self.contract(k)?, Instruction::Log => self.log()?, + Instruction::Eval => self.eval()?, Instruction::Call => self.call()?, Instruction::Signtx => self.signtx()?, Instruction::Signid => self.signid()?, @@ -572,6 +573,15 @@ where Ok(()) } + fn eval(&mut self) -> Result<(), VMError> { + // Pop program + let program_item = self.pop_item()?.to_program()?; + + // Replace current program with new program + self.continue_with_program(program_item)?; + Ok(()) + } + fn call(&mut self) -> Result<(), VMError> { // Pop program, call proof, and contract let program_item = self.pop_item()?.to_program()?; From 11e981832959f8fb5e00078cdfd6267ef904ca05 Mon Sep 17 00:00:00 2001 From: Oleg Andreev Date: Tue, 30 Mar 2021 11:37:51 +0300 Subject: [PATCH 3/3] zkvm: tests for eval opcode --- zkvm/tests/zkvm.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/zkvm/tests/zkvm.rs b/zkvm/tests/zkvm.rs index 6f6c424c5..8a2127228 100644 --- a/zkvm/tests/zkvm.rs +++ b/zkvm/tests/zkvm.rs @@ -572,6 +572,36 @@ fn constraints_cannot_be_copied() { ); } +#[test] +fn eval_test() { + let (pred, prv) = generate_predicate(); + let prog = Program::build(|p| { + p.push(String::from(Scalar::from(20u64))); + p.scalar(); + p.program(Program::build(|p2| { + p2.push(String::from(Scalar::from(1u64))); + p2.scalar(); + p2.program(Program::build(|p3| { + p3.add(); + })); + p2.eval(); + })); + p.eval(); + p.push(String::from(Scalar::from(21u64))); + p.scalar(); + p.eq(); + p.verify(); + + // to make program finish we need to spend a dummy input + p.input_helper(0, Scalar::zero(), pred.clone()); + p.output_helper(pred); + }); + + dbg!(&prog); + + build_and_verify(prog, &vec![prv]).expect("should succeed"); +} + #[test] fn borrow_output() { //inputs 10 units, borrows 5 units, outputs two (5 units)