diff --git a/Cargo.lock b/Cargo.lock index 274138f0..33df869f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,8 +5,7 @@ version = 3 [[package]] name = "aluvm" version = "0.11.0-beta.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10be187b383247e1902aa5a415f76ffc9a04f197829c46b9ccb6da3582e394f" +source = "git+https://github.com/crisdut/rust-aluvm?branch=fix/cnv#98e1b4c2c5b2ce0663de10765fb1c7fe2c32999c" dependencies = [ "amplify", "baid58", diff --git a/Cargo.toml b/Cargo.toml index 158a3d67..d57e4e79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,3 +59,6 @@ wasm-bindgen-test = "0.3" [package.metadata.docs.rs] features = [ "all" ] + +[patch.crates-io] +aluvm = { git = "https://github.com/crisdut/rust-aluvm", branch = "fix/cnv" } \ No newline at end of file diff --git a/src/vm/macroasm.rs b/src/vm/macroasm.rs index cf996f46..5f5692c4 100644 --- a/src/vm/macroasm.rs +++ b/src/vm/macroasm.rs @@ -23,6 +23,7 @@ #[macro_export] macro_rules! rgbasm { ($( $tt:tt )+) => {{ #[allow(unused_imports)] { + use $crate::AssignmentType; use $crate::vm::{RgbIsa, ContractOp, TimechainOp}; use $crate::vm::aluasm_isa; use $crate::isa_instr; @@ -34,8 +35,24 @@ macro_rules! rgbasm { macro_rules! isa_instr { (pcvs $no:literal) => {{ RgbIsa::Contract(ContractOp::PcVs($no.into())) }}; (pccs $no1:literal, $no2:literal) => {{ RgbIsa::Contract(ContractOp::PcCs($no1.into(), $no2.into())) }}; - (ldg $t:literal, $no:literal,s16[$s_idx:literal]) => {{ RgbIsa::Contract(ContractOp::LdG($t.into(), $no, RegS::from($s_idx))) }}; - (lds $t:literal, $no:literal,s16[$s_idx:literal]) => {{ RgbIsa::Contract(ContractOp::LdS($t.into(), $no, RegS::from($s_idx))) }}; + (cng $t:literal,a8[$a_idx:literal]) => {{ RgbIsa::Contract(ContractOp::CnG($t.into(), Reg32::from(RegS::from($a_idx)))) }}; + (cnc $t:literal,a16[$a_idx:literal]) => {{ RgbIsa::Contract(ContractOp::CnC($t.into(), Reg32::from(RegS::from($a_idx)))) }}; + (ldg $t:literal, $no:literal,s16[$s_idx:literal]) => {{ RgbIsa::Contract(ContractOp::LdGV($t.into(), $no, RegS::from($s_idx))) }}; + (lds $t:literal, $no:literal,s16[$s_idx:literal]) => {{ RgbIsa::Contract(ContractOp::LdSV($t.into(), $no, RegS::from($s_idx))) }}; (ldp $t:literal, $no:literal,s16[$s_idx:literal]) => {{ RgbIsa::Contract(ContractOp::LdP($t.into(), $no, RegS::from($s_idx))) }}; + (ldg $t:literal,a8[$a_idx:literal],s16[$s_idx:literal]) => {{ + RgbIsa::Contract(ContractOp::LdG( + GlobalStateType::from($t as u16), + Reg32::from(RegS::from($a_idx)), + RegS::from($s_idx), + )) + }}; + (lds $t:literal,a16[$a_idx:literal],s16[$s_idx:literal]) => {{ + RgbIsa::Contract(ContractOp::LdS( + AssignmentType::from($t as u16), + Reg32::from(RegS::from($a_idx)), + RegS::from($s_idx), + )) + }}; ($op:ident $($tt:tt)+) => {{ compile_error!(concat!("unknown RGB assembly opcode `", stringify!($op), "`")) }}; } diff --git a/src/vm/op_contract.rs b/src/vm/op_contract.rs index b8676913..11d41668 100644 --- a/src/vm/op_contract.rs +++ b/src/vm/op_contract.rs @@ -28,7 +28,7 @@ use std::ops::RangeInclusive; use aluvm::isa::{Bytecode, BytecodeError, ExecStep, InstructionSet}; use aluvm::library::{CodeEofError, LibSite, Read, Write}; use aluvm::reg::{CoreRegs, Reg, Reg32, RegA, RegS}; -use amplify::num::{u3, u4}; +use amplify::num::{u3, u4, u7}; use amplify::Wrapper; use commit_verify::CommitVerify; @@ -61,6 +61,25 @@ pub enum ContractOp { #[display("cnc {0},a16{1}")] CnC(AssignmentType, Reg32), + /// Loads global state from the current operation with type id from the + /// first argument and dynamic index from the second argument into a + /// register provided in the third argument. + /// + /// If the state is absent sets `st0` to `false` and terminates the program. + #[display("ldg {0},{1},{2}")] + LdG(GlobalStateType, Reg32, RegS), + + /// Loads owned structured state with type id from the first argument and + /// dynamic index from the second argument into a register provided in + /// the third argument. + /// + /// If the state is absent or is not a structured state sets `st0` to + /// `false` and terminates the program. + /// + /// If the state at the index is concealed, sets destination to `None`. + #[display("lds {0},{1},{2}")] + LdS(AssignmentType, Reg32, RegS), + /// Loads input (previous) state with type id from the first argument and /// index from the second argument into a register provided in the third /// argument. @@ -81,7 +100,7 @@ pub enum ContractOp { /// /// If the state at the index is concealed, sets destination to `None`. #[display("lds {0},{1},{2}")] - LdS(AssignmentType, u16, RegS), + LdSV(AssignmentType, u16, RegS), /// Loads owned fungible state with type id from the first argument and /// index from the second argument into `a64` register provided in the third @@ -100,7 +119,7 @@ pub enum ContractOp { /// /// If the state is absent sets `st0` to `false` and terminates the program. #[display("ldg {0},{1},{2}")] - LdG(GlobalStateType, u8, RegS), + LdGV(GlobalStateType, u8, RegS), /// Loads part of the contract global state with type id from the first /// argument at the depth from the second argument into a register @@ -161,18 +180,21 @@ impl InstructionSet for ContractOp { fn dst_regs(&self) -> HashSet { match self { - ContractOp::CnP(_, reg) | - ContractOp::CnS(_, reg) | - ContractOp::CnG(_, reg) | - ContractOp::CnC(_, reg) => { + ContractOp::CnP(_, reg) | ContractOp::CnS(_, reg) | ContractOp::CnC(_, reg) => { set![Reg::A(RegA::A16, *reg)] } + ContractOp::CnG(_, reg) => { + set![Reg::A(RegA::A8, *reg)] + } ContractOp::LdF(_, _, reg) => { set![Reg::A(RegA::A64, *reg)] } - ContractOp::LdP(_, _, reg) | - ContractOp::LdS(_, _, reg) | + ContractOp::LdG(_, _, reg) | + ContractOp::LdS(_, _, reg) | + ContractOp::LdGV(_, _, reg) | + ContractOp::LdP(_, _, reg) | + ContractOp::LdSV(_, _, reg) | ContractOp::LdC(_, _, reg) | ContractOp::LdM(reg) => { set![Reg::S(*reg)] @@ -199,8 +221,8 @@ impl InstructionSet for ContractOp { return ExecStep::Next; } let Some(prev_state) = context.prev_state.get($state_type) else { - fail!() - }; + fail!() + }; match prev_state { TypedAssigns::Fungible(state) => state .iter() @@ -217,8 +239,8 @@ impl InstructionSet for ContractOp { return ExecStep::Next; } let Some(new_state) = context.owned_state.get(*$state_type) else { - fail!() - }; + fail!() + }; match new_state { TypedAssigns::Fungible(state) => state .iter() @@ -246,11 +268,14 @@ impl InstructionSet for ContractOp { ); } ContractOp::CnG(state_type, reg) => { - regs.set_n(RegA::A16, *reg, context.global.get(state_type).map(|a| a.len_u16())); + regs.set_n(RegA::A8, *reg, context.global.get(state_type).map(|a| a.len_u16())); } - ContractOp::CnC(_state_type, _reg) => { - // TODO: implement global contract state - fail!() + ContractOp::CnC(state_type, reg) => { + regs.set_n( + RegA::A16, + *reg, + context.owned_state.get(*state_type).map(|a| a.len_u16()), + ); } ContractOp::LdP(state_type, index, reg) => { let Some(Ok(state)) = context @@ -263,28 +288,31 @@ impl InstructionSet for ContractOp { let state = state.map(|s| s.value.as_inner()); regs.set_s(*reg, state); } - ContractOp::LdS(state_type, index, reg) => { + + ContractOp::LdF(state_type, index, reg) => { let Some(Ok(state)) = context .owned_state .get(*state_type) - .map(|a| a.into_structured_state_at(*index)) + .map(|a| a.into_fungible_state_at(*index)) else { fail!() }; - let state = state.map(|s| s.value.into_inner()); - regs.set_s(*reg, state); + regs.set_n(RegA::A64, *reg, state.map(|s| s.value.as_u64())); } - ContractOp::LdF(state_type, index, reg) => { - let Some(Ok(state)) = context - .owned_state - .get(*state_type) - .map(|a| a.into_fungible_state_at(*index)) + ContractOp::LdG(state_type, reg_32, reg_s) => { + let reg_32 = regs.get_n(RegA::A8, *reg_32); + let index: u8 = reg_32.unwrap_or_default().into(); + + let Some(state) = context + .global + .get(state_type) + .and_then(|a| a.get(index as usize)) else { fail!() }; - regs.set_n(RegA::A64, *reg, state.map(|s| s.value.as_u64())); + regs.set_s(*reg_s, Some(state.value.as_inner())); } - ContractOp::LdG(state_type, index, reg) => { + ContractOp::LdGV(state_type, index, reg) => { let Some(state) = context .global .get(state_type) @@ -294,6 +322,32 @@ impl InstructionSet for ContractOp { }; regs.set_s(*reg, Some(state.value.as_inner())); } + ContractOp::LdS(state_type, reg_32, reg) => { + let reg_32 = regs.get_n(RegA::A16, *reg_32); + let index: u16 = reg_32.unwrap_or_default().into(); + + let Some(Ok(state)) = context + .owned_state + .get(*state_type) + .map(|a| a.into_structured_state_at(index)) + else { + fail!() + }; + let state = state.map(|s| s.value.into_inner()); + regs.set_s(*reg, state); + } + ContractOp::LdSV(state_type, index, reg) => { + let Some(Ok(state)) = context + .owned_state + .get(*state_type) + .map(|a| a.into_structured_state_at(*index)) + else { + fail!() + }; + let state = state.map(|s| s.value.into_inner()); + regs.set_s(*reg, state); + } + ContractOp::LdC(_state_type, _index, _reg) => { // TODO: implement global contract state fail!() @@ -360,11 +414,12 @@ impl Bytecode for ContractOp { ContractOp::CnG(_, _) | ContractOp::CnC(_, _) => 4, - ContractOp::LdP(_, _, _) | ContractOp::LdS(_, _, _) | + ContractOp::LdSV(_, _, _) | + ContractOp::LdP(_, _, _) | ContractOp::LdF(_, _, _) | ContractOp::LdC(_, _, _) => 6, - ContractOp::LdG(_, _, _) => 5, + ContractOp::LdG(_, _, _) | ContractOp::LdGV(_, _, _) => 5, ContractOp::LdM(_) => 2, ContractOp::PcVs(_) => 3, @@ -383,10 +438,12 @@ impl Bytecode for ContractOp { ContractOp::CnG(_, _) => INSTR_CNG, ContractOp::CnC(_, _) => INSTR_CNC, + ContractOp::LdG(_, _, _) => INSTR_DLDG, + ContractOp::LdS(_, _, _) => INSTR_DLDS, ContractOp::LdP(_, _, _) => INSTR_LDP, - ContractOp::LdS(_, _, _) => INSTR_LDS, + ContractOp::LdSV(_, _, _) => INSTR_LDS, ContractOp::LdF(_, _, _) => INSTR_LDF, - ContractOp::LdG(_, _, _) => INSTR_LDG, + ContractOp::LdGV(_, _, _) => INSTR_LDG, ContractOp::LdC(_, _, _) => INSTR_LDC, ContractOp::LdM(_) => INSTR_LDM, @@ -426,7 +483,14 @@ impl Bytecode for ContractOp { writer.write_u4(reg)?; writer.write_u4(u4::ZERO)?; } - ContractOp::LdS(state_type, index, reg) => { + ContractOp::LdS(state_type, reg_32, reg_s) => { + writer.write_u16(*state_type)?; + writer.write_u5(reg_32)?; + writer.write_u8(reg_s)?; + writer.write_u4(u4::ZERO)?; + writer.write_u7(u7::ZERO)?; + } + ContractOp::LdSV(state_type, index, reg) => { writer.write_u16(*state_type)?; writer.write_u16(*index)?; writer.write_u4(reg)?; @@ -438,7 +502,13 @@ impl Bytecode for ContractOp { writer.write_u5(reg)?; writer.write_u3(u3::ZERO)?; } - ContractOp::LdG(state_type, index, reg) => { + ContractOp::LdG(state_type, reg_a, reg_s) => { + writer.write_u16(*state_type)?; + writer.write_u5(*reg_a)?; + writer.write_u8(*reg_s)?; + writer.write_u3(u3::ZERO)?; + } + ContractOp::LdGV(state_type, index, reg) => { writer.write_u16(*state_type)?; writer.write_u8(*index)?; writer.write_u4(reg)?; @@ -474,22 +544,22 @@ impl Bytecode for ContractOp { Ok(match reader.read_u8()? { INSTR_CNP => { let i = Self::CnP(reader.read_u16()?.into(), reader.read_u5()?.into()); - reader.read_u4()?; // Discard garbage bits + reader.read_u3()?; // Discard garbage bits i } INSTR_CNS => { let i = Self::CnS(reader.read_u16()?.into(), reader.read_u5()?.into()); - reader.read_u4()?; // Discard garbage bits + reader.read_u3()?; // Discard garbage bits i } INSTR_CNG => { let i = Self::CnG(reader.read_u16()?.into(), reader.read_u5()?.into()); - reader.read_u4()?; // Discard garbage bits + reader.read_u3()?; // Discard garbage bits i } INSTR_CNC => { let i = Self::CnC(reader.read_u16()?.into(), reader.read_u5()?.into()); - reader.read_u4()?; // Discard garbage bits + reader.read_u3()?; // Discard garbage bits i } @@ -503,7 +573,7 @@ impl Bytecode for ContractOp { i } INSTR_LDS => { - let i = Self::LdS( + let i = Self::LdSV( reader.read_u16()?.into(), reader.read_u16()?, reader.read_u4()?.into(), @@ -521,7 +591,7 @@ impl Bytecode for ContractOp { i } INSTR_LDG => { - let i = Self::LdG( + let i = Self::LdGV( reader.read_u16()?.into(), reader.read_u8()?, reader.read_u4()?.into(), @@ -529,6 +599,25 @@ impl Bytecode for ContractOp { reader.read_u4()?; // Discard garbage bits i } + INSTR_DLDG => { + let i = Self::LdG( + reader.read_u16()?.into(), + reader.read_u5()?.into(), + reader.read_u8()?.into(), + ); + reader.read_u3()?; // Discard garbage bits + i + } + INSTR_DLDS => { + let i = Self::LdS( + reader.read_u16()?.into(), + reader.read_u5()?.into(), + reader.read_u8()?.into(), + ); + reader.read_u4()?; // Discard garbage bits + reader.read_u7()?; + i + } INSTR_LDC => { let i = Self::LdC( reader.read_u16()?.into(), diff --git a/src/vm/opcodes.rs b/src/vm/opcodes.rs index 7d1fab28..7bdee41b 100644 --- a/src/vm/opcodes.rs +++ b/src/vm/opcodes.rs @@ -41,6 +41,8 @@ pub const INSTR_LDF: u8 = 0b11_000_110; pub const INSTR_LDG: u8 = 0b11_001_000; pub const INSTR_LDC: u8 = 0b11_001_001; pub const INSTR_LDM: u8 = 0b11_001_010; +pub const INSTR_DLDG: u8 = 0b11_001_011; +pub const INSTR_DLDS: u8 = 0b11_001_100; // Reserved 0b11_001_111 pub const INSTR_PCVS: u8 = 0b11_010_000;