Skip to content

Commit 06d8acc

Browse files
committed
vm: add Vts operator
1 parent 3a6d354 commit 06d8acc

File tree

4 files changed

+75
-30
lines changed

4 files changed

+75
-30
lines changed

src/vm/contract.rs

+8-12
Original file line numberDiff line numberDiff line change
@@ -544,24 +544,20 @@ pub struct VmContext<'op, S: ContractStateAccess> {
544544

545545
pub struct OpInfo<'op> {
546546
pub id: OpId,
547-
pub ty: OpFullType,
548-
pub metadata: &'op Metadata,
549547
pub prev_state: &'op Assignments<GraphSeal>,
550-
pub owned_state: AssignmentsRef<'op>,
551-
pub global: &'op GlobalState,
548+
pub op: &'op OrdOpRef<'op>,
552549
}
553550

554551
impl<'op> OpInfo<'op> {
555552
pub fn with(id: OpId, op: &'op OrdOpRef<'op>, prev_state: &'op Assignments<GraphSeal>) -> Self {
556-
OpInfo {
557-
id,
558-
ty: op.full_type(),
559-
metadata: op.metadata(),
560-
prev_state,
561-
owned_state: op.assignments(),
562-
global: op.globals(),
563-
}
553+
OpInfo { id, prev_state, op }
564554
}
555+
556+
pub fn global(&self) -> &'op GlobalState { self.op.globals() }
557+
558+
pub fn metadata(&self) -> &'op Metadata { self.op.metadata() }
559+
560+
pub fn owned_state(&self) -> AssignmentsRef<'op> { self.op.assignments() }
565561
}
566562

567563
#[cfg(test)]

src/vm/macroasm.rs

+6
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ macro_rules! isa_instr {
4848
(cnc $t:ident,a16[$a_idx:literal]) => {{
4949
RgbIsa::Contract(ContractOp::CnC($t, Reg32::from(u5::with($a_idx))))
5050
}};
51+
(ldc $t:ident,a32[$a_idx:literal],s16[$s_idx:literal]) => {{
52+
RgbIsa::Contract(ContractOp::LdC($t, Reg16::from(u4::with($a_idx)), RegS::from($s_idx)))
53+
}};
5154
(ldm $t:ident,s16[$s_idx:literal]) => {{
5255
RgbIsa::Contract(ContractOp::LdM($t, RegS::from($s_idx)))
5356
}};
@@ -60,6 +63,9 @@ macro_rules! isa_instr {
6063
(lds $t:ident,a16[$a_idx:literal],s16[$s_idx:literal]) => {{
6164
RgbIsa::Contract(ContractOp::LdS($t, Reg16::from(u4::with($a_idx)), RegS::from($s_idx)))
6265
}};
66+
(vts s16[$s_idx:literal]) => {{
67+
RgbIsa::Contract(ContractOp::Vts(RegS::from($s_idx)))
68+
}};
6369
($op:ident $($tt:tt)+) => {{
6470
compile_error!(concat!("unknown RGB assembly opcode `", stringify!($op), "`"))
6571
}};

src/vm/op_contract.rs

+57-7
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,11 @@ use aluvm::library::{CodeEofError, IsaSeg, LibSite, Read, Write};
3333
use aluvm::reg::{CoreRegs, Reg, Reg16, Reg32, RegA, RegS};
3434
use amplify::num::{u24, u3, u4};
3535
use amplify::Wrapper;
36+
use secp256k1::{ecdsa, Message, PublicKey};
3637

3738
use super::opcodes::*;
3839
use super::{ContractStateAccess, VmContext};
40+
use crate::vm::OrdOpRef;
3941
use crate::{Assign, AssignmentType, GlobalStateType, MetaType, TypedAssigns};
4042

4143
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
@@ -170,6 +172,14 @@ pub enum ContractOp<S: ContractStateAccess> {
170172
#[display("sps {0}")]
171173
Sps(/** owned state type */ AssignmentType),
172174

175+
/// Verifies the signature of a transition against the pubkey in the first argument.
176+
///
177+
/// If the register doesn't contain a valid public key or the operation
178+
/// signature is missing or invalid, sets `st0` to fail state and terminates
179+
/// the program.
180+
#[display("vts {0}")]
181+
Vts(RegS),
182+
173183
/// All other future unsupported operations, which must set `st0` to
174184
/// `false` and stop the execution.
175185
#[display("fail {0}")]
@@ -196,6 +206,9 @@ impl<S: ContractStateAccess> InstructionSet for ContractOp<S> {
196206
| ContractOp::LdM(_, _) => bset![],
197207
ContractOp::Svs(_) => bset![],
198208
ContractOp::Sas(_) | ContractOp::Sps(_) => bset![Reg::A(RegA::A64, Reg32::Reg0)],
209+
210+
ContractOp::Vts(_) => bset![],
211+
199212
ContractOp::Fail(_, _) => bset![],
200213
}
201214
}
@@ -221,6 +234,7 @@ impl<S: ContractStateAccess> InstructionSet for ContractOp<S> {
221234
ContractOp::Svs(_) | ContractOp::Sas(_) | ContractOp::Sps(_) => {
222235
bset![]
223236
}
237+
ContractOp::Vts(reg) => bset![Reg::S(*reg)],
224238
ContractOp::Fail(_, _) => bset![],
225239
}
226240
}
@@ -238,6 +252,7 @@ impl<S: ContractStateAccess> InstructionSet for ContractOp<S> {
238252
| ContractOp::LdC(_, _, _) => 8,
239253
ContractOp::LdM(_, _) => 6,
240254
ContractOp::Svs(_) | ContractOp::Sas(_) | ContractOp::Sps(_) => 20,
255+
ContractOp::Vts(_) => 512,
241256
ContractOp::Fail(_, _) => u64::MAX,
242257
}
243258
}
@@ -268,7 +283,7 @@ impl<S: ContractStateAccess> InstructionSet for ContractOp<S> {
268283
}
269284
macro_rules! load_revealed_outputs {
270285
($state_type:ident) => {{
271-
let Some(new_state) = context.op_info.owned_state.get(*$state_type) else {
286+
let Some(new_state) = context.op_info.owned_state().get(*$state_type) else {
272287
fail!()
273288
};
274289
match new_state {
@@ -302,7 +317,7 @@ impl<S: ContractStateAccess> InstructionSet for ContractOp<S> {
302317
*reg,
303318
context
304319
.op_info
305-
.owned_state
320+
.owned_state()
306321
.get(*state_type)
307322
.map(|a| a.len_u16()),
308323
);
@@ -311,7 +326,11 @@ impl<S: ContractStateAccess> InstructionSet for ContractOp<S> {
311326
regs.set_n(
312327
RegA::A8,
313328
*reg,
314-
context.op_info.global.get(state_type).map(|a| a.len_u16()),
329+
context
330+
.op_info
331+
.global()
332+
.get(state_type)
333+
.map(|a| a.len_u16()),
315334
);
316335
}
317336
ContractOp::CnC(state_type, reg) => {
@@ -347,7 +366,7 @@ impl<S: ContractStateAccess> InstructionSet for ContractOp<S> {
347366

348367
let Some(Ok(state)) = context
349368
.op_info
350-
.owned_state
369+
.owned_state()
351370
.get(*state_type)
352371
.map(|a| a.into_structured_state_at(index))
353372
else {
@@ -364,7 +383,7 @@ impl<S: ContractStateAccess> InstructionSet for ContractOp<S> {
364383

365384
let Some(Ok(state)) = context
366385
.op_info
367-
.owned_state
386+
.owned_state()
368387
.get(*state_type)
369388
.map(|a| a.into_fungible_state_at(index))
370389
else {
@@ -380,7 +399,7 @@ impl<S: ContractStateAccess> InstructionSet for ContractOp<S> {
380399

381400
let Some(state) = context
382401
.op_info
383-
.global
402+
.global()
384403
.get(state_type)
385404
.and_then(|a| a.get(index as usize))
386405
else {
@@ -407,7 +426,7 @@ impl<S: ContractStateAccess> InstructionSet for ContractOp<S> {
407426
regs.set_s16(*reg_s, state.borrow().as_inner());
408427
}
409428
ContractOp::LdM(type_id, reg) => {
410-
let Some(meta) = context.op_info.metadata.get(type_id) else {
429+
let Some(meta) = context.op_info.metadata().get(type_id) else {
411430
fail!()
412431
};
413432
regs.set_s16(*reg, meta.to_inner());
@@ -463,6 +482,31 @@ impl<S: ContractStateAccess> InstructionSet for ContractOp<S> {
463482
fail!()
464483
}
465484
}
485+
ContractOp::Vts(reg_s) => match context.op_info.op {
486+
OrdOpRef::Genesis(_) => fail!(),
487+
OrdOpRef::Transition(transition, _, _, _) => {
488+
let Some(pubkey) = regs.s16(*reg_s) else {
489+
fail!()
490+
};
491+
let Ok(pubkey) = PublicKey::from_slice(&pubkey.to_vec()) else {
492+
fail!()
493+
};
494+
let Some(ref witness) = transition.signature else {
495+
fail!()
496+
};
497+
let sig_bytes = witness.clone().into_inner().into_inner();
498+
let Ok(sig) = ecdsa::Signature::from_compact(&sig_bytes) else {
499+
fail!()
500+
};
501+
502+
let transition_id = context.op_info.id.into_inner().into_inner();
503+
let msg = Message::from_digest(transition_id);
504+
505+
if sig.verify(&msg, &pubkey).is_err() {
506+
fail!()
507+
}
508+
}
509+
},
466510
// All other future unsupported operations, which must set `st0` to `false`.
467511
_ => fail!(),
468512
}
@@ -491,6 +535,8 @@ impl<S: ContractStateAccess> Bytecode for ContractOp<S> {
491535
ContractOp::Sas(_) => INSTR_SAS,
492536
ContractOp::Sps(_) => INSTR_SPS,
493537

538+
ContractOp::Vts(_) => INSTR_VTS,
539+
494540
ContractOp::Fail(other, _) => *other,
495541
}
496542
}
@@ -553,6 +599,8 @@ impl<S: ContractStateAccess> Bytecode for ContractOp<S> {
553599
ContractOp::Sas(owned_type) => writer.write_u16(*owned_type)?,
554600
ContractOp::Sps(owned_type) => writer.write_u16(*owned_type)?,
555601

602+
ContractOp::Vts(reg_s) => writer.write_u4(*reg_s)?,
603+
556604
ContractOp::Fail(_, _) => {}
557605
}
558606
Ok(())
@@ -620,6 +668,8 @@ impl<S: ContractStateAccess> Bytecode for ContractOp<S> {
620668
INSTR_SAS => Self::Sas(reader.read_u16()?.into()),
621669
INSTR_SPS => Self::Sps(reader.read_u16()?.into()),
622670

671+
INSTR_VTS => Self::Vts(reader.read_u4()?.into()),
672+
623673
x => Self::Fail(x, PhantomData),
624674
})
625675
}

src/vm/opcodes.rs

+4-11
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,8 @@ pub const INSTR_LDM: u8 = 0b11_001_010;
4646
pub const INSTR_SVS: u8 = 0b11_010_000;
4747
pub const INSTR_SAS: u8 = 0b11_010_001;
4848
pub const INSTR_SPS: u8 = 0b11_010_010;
49-
// Reserved 0b11_010_011
50-
pub const INSTR_CONTRACT_FROM: u8 = 0b11_000_000;
51-
pub const INSTR_CONTRACT_TO: u8 = 0b11_010_011;
52-
53-
// TIMECHAIN:
54-
pub const INSTR_TIMECHAIN_FROM: u8 = 0b11_011_100;
55-
pub const INSTR_TIMECHAIN_TO: u8 = 0b11_011_111;
49+
pub const INSTR_VTS: u8 = 0b11_010_011;
50+
// Reserved 0b11_010_100
5651

57-
// Reserved 0b11_011_100
58-
// Reserved 0b11_011_101
59-
// Reserved 0b11_011_110
60-
// Reserved 0b11_011_111
52+
pub const INSTR_CONTRACT_FROM: u8 = 0b11_000_000;
53+
pub const INSTR_CONTRACT_TO: u8 = 0b11_010_100;

0 commit comments

Comments
 (0)