Skip to content

Commit

Permalink
Merge pull request AleoNet#2419 from AleoHQ/feat/network-id
Browse files Browse the repository at this point in the history
[Feature] new `network.id` opcode
  • Loading branch information
howardwu authored Apr 12, 2024
2 parents 51c3d27 + 35e4c66 commit d48f6fb
Show file tree
Hide file tree
Showing 17 changed files with 150 additions and 1 deletion.
76 changes: 76 additions & 0 deletions ledger/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use console::{
account::{Address, PrivateKey},
network::prelude::*,
program::{Entry, Identifier, Literal, Plaintext, ProgramID, Value},
types::U16,
};
use ledger_block::{ConfirmedTransaction, Ratify, Rejected, Transaction};
use ledger_committee::{Committee, MIN_VALIDATOR_STAKE};
Expand Down Expand Up @@ -1824,6 +1825,81 @@ finalize foo:
}
}

#[test]
fn test_metadata() {
let rng = &mut TestRng::default();

// Initialize the test environment.
let crate::test_helpers::TestEnv { ledger, private_key, .. } = crate::test_helpers::sample_test_env(rng);

// Deploy a test program to the ledger.
let program_id = ProgramID::<CurrentNetwork>::from_str("metadata.aleo").unwrap();
let program = Program::<CurrentNetwork>::from_str(&format!(
"
program {program_id};
function is_block:
input r0 as u32.public;
async is_block r0 into r1;
output r1 as {program_id}/is_block.future;
finalize is_block:
input r0 as u32.public;
assert.eq r0 block.height;
function is_id:
input r0 as u16.public;
async is_id r0 into r1;
output r1 as {program_id}/is_id.future;
finalize is_id:
input r0 as u16.public;
assert.eq r0 network.id;
",
))
.unwrap();

// Deploy.
let transaction = ledger.vm.deploy(&private_key, &program, None, 0, None, rng).unwrap();
// Verify.
ledger.vm().check_transaction(&transaction, None, rng).unwrap();

// Construct the next block.
let block =
ledger.prepare_advance_to_next_beacon_block(&private_key, vec![], vec![], vec![transaction], rng).unwrap();
// Advance to the next block.
ledger.advance_to_next_block(&block).unwrap();
assert_eq!(ledger.latest_height(), 1);
assert_eq!(ledger.latest_hash(), block.hash());
assert_eq!(program, ledger.get_program(program_id).unwrap());

// Execute functions `is_block` and `is_id` to assert that the on-chain state is as expected.
let inputs_block: [Value<CurrentNetwork>; 1] = [Value::from_str("2u32").unwrap()];
let tx_block =
ledger.vm.execute(&private_key, (&program_id, "is_block"), inputs_block.iter(), None, 0, None, rng).unwrap();
let inputs_id: [Value<CurrentNetwork>; 1] = [Value::from(Literal::U16(U16::new(CurrentNetwork::ID)))];
let tx_id = ledger.vm.execute(&private_key, (&program_id, "is_id"), inputs_id.iter(), None, 0, None, rng).unwrap();

// Construct the next block.
let block_2 =
ledger.prepare_advance_to_next_beacon_block(&private_key, vec![], vec![], vec![tx_id, tx_block], rng).unwrap();
// Advance to the next block.
ledger.advance_to_next_block(&block_2).unwrap();

// Execute the program.
let inputs_block_2: [Value<CurrentNetwork>; 1] = [Value::from_str("3u32").unwrap()];
let tx_block_2 =
ledger.vm.execute(&private_key, (&program_id, "is_block"), inputs_block_2.iter(), None, 0, None, rng).unwrap();
let tx_id_2 =
ledger.vm.execute(&private_key, (&program_id, "is_id"), inputs_id.iter(), None, 0, None, rng).unwrap();

// Construct the next block.
let block_3 = ledger
.prepare_advance_to_next_beacon_block(&private_key, vec![], vec![], vec![tx_block_2, tx_id_2], rng)
.unwrap();
// Advance to the next block.
ledger.advance_to_next_block(&block_3).unwrap();
}

// These tests require the proof targets to be low enough to be able to generate **valid** solutions.
// This requires the 'test' feature to be enabled for the `console` dependency.
#[cfg(feature = "test")]
Expand Down
4 changes: 4 additions & 0 deletions synthesizer/process/src/stack/evaluate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ impl<N: Network> StackEvaluate<N> for Stack<N> {
Operand::Caller => Ok(Value::Plaintext(Plaintext::from(Literal::Address(registers.caller()?)))),
// If the operand is the block height, throw an error.
Operand::BlockHeight => bail!("Cannot retrieve the block height from a closure scope."),
// If the operand is the network id, throw an error.
Operand::NetworkID => bail!("Cannot retrieve the network ID from a closure scope."),
}
})
.collect();
Expand Down Expand Up @@ -213,6 +215,8 @@ impl<N: Network> StackEvaluate<N> for Stack<N> {
Operand::Caller => Ok(Value::Plaintext(Plaintext::from(Literal::Address(registers.caller()?)))),
// If the operand is the block height, throw an error.
Operand::BlockHeight => bail!("Cannot retrieve the block height from a function scope."),
// If the operand is the network id, throw an error.
Operand::NetworkID => bail!("Cannot retrieve the network ID from a function scope."),
}
})
.collect::<Result<Vec<_>>>()?;
Expand Down
8 changes: 8 additions & 0 deletions synthesizer/process/src/stack/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ impl<N: Network> StackExecute<N> for Stack<N> {
Operand::BlockHeight => {
bail!("Illegal operation: cannot retrieve the block height in a closure scope")
}
// If the operand is the network id, throw an error.
Operand::NetworkID => {
bail!("Illegal operation: cannot retrieve the network id in a closure scope")
}
}
})
.collect();
Expand Down Expand Up @@ -343,6 +347,10 @@ impl<N: Network> StackExecute<N> for Stack<N> {
Operand::BlockHeight => {
bail!("Illegal operation: cannot retrieve the block height in a function scope")
}
// If the operand is the network id, throw an error.
Operand::NetworkID => {
bail!("Illegal operation: cannot retrieve the network id in a function scope")
}
}
})
.collect::<Result<Vec<_>>>()?;
Expand Down
4 changes: 4 additions & 0 deletions synthesizer/process/src/stack/finalize_registers/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ impl<N: Network> RegistersLoad<N> for FinalizeRegisters<N> {
Operand::BlockHeight => {
return Ok(Value::Plaintext(Plaintext::from(Literal::U32(U32::new(self.state.block_height())))));
}
// If the operand is the network ID, load the network ID.
Operand::NetworkID => {
return Ok(Value::Plaintext(Plaintext::from(Literal::U16(U16::new(N::ID)))));
}
};

// Retrieve the value.
Expand Down
2 changes: 1 addition & 1 deletion synthesizer/process/src/stack/finalize_registers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::FinalizeTypes;
use console::{
network::prelude::*,
program::{Identifier, Literal, Plaintext, Register, Value},
types::U32,
types::{U16, U32},
};
use synthesizer_program::{
FinalizeGlobalState,
Expand Down
21 changes: 21 additions & 0 deletions synthesizer/process/src/stack/finalize_types/matches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ impl<N: Network> FinalizeTypes<N> {
"Struct member '{struct_name}.{member_name}' expects {member_type}, but found '{block_height_type}' in the operand '{operand}'.",
)
}
// Ensure the network ID type (u16) matches the member type.
Operand::NetworkID => {
// Retrieve the network ID type.
let network_id_type = PlaintextType::Literal(LiteralType::U16);
// Ensure the network ID type matches the member type.
ensure!(
&network_id_type == member_type,
"Struct member '{struct_name}.{member_name}' expects {member_type}, but found '{network_id_type}' in the operand '{operand}'.",
)
}
}
}
Ok(())
Expand Down Expand Up @@ -177,6 +187,17 @@ impl<N: Network> FinalizeTypes<N> {
array_type.next_element_type()
)
}
// Ensure the network ID type (u16) matches the member type.
Operand::NetworkID => {
// Retrieve the network ID type.
let network_id_type = PlaintextType::Literal(LiteralType::U16);
// Ensure the network ID type matches the member type.
ensure!(
&network_id_type == array_type.next_element_type(),
"Array element expects {}, but found '{network_id_type}' in the operand '{operand}'.",
array_type.next_element_type()
)
}
}
}
Ok(())
Expand Down
1 change: 1 addition & 0 deletions synthesizer/process/src/stack/finalize_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ impl<N: Network> FinalizeTypes<N> {
Operand::Signer => bail!("'self.signer' is not a valid operand in a finalize context."),
Operand::Caller => bail!("'self.caller' is not a valid operand in a finalize context."),
Operand::BlockHeight => FinalizeType::Plaintext(PlaintextType::Literal(LiteralType::U32)),
Operand::NetworkID => FinalizeType::Plaintext(PlaintextType::Literal(LiteralType::U16)),
})
}

Expand Down
15 changes: 15 additions & 0 deletions synthesizer/process/src/stack/register_types/matches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ impl<N: Network> RegisterTypes<N> {
Operand::BlockHeight => bail!(
"Struct member '{struct_name}.{member_name}' cannot be from a block height in a non-finalize scope"
),
// If the operand is a network ID type, throw an error.
Operand::NetworkID => bail!(
"Struct member '{struct_name}.{member_name}' cannot be from a network ID in a non-finalize scope"
),
}
}
Ok(())
Expand Down Expand Up @@ -162,6 +166,8 @@ impl<N: Network> RegisterTypes<N> {
}
// If the operand is a block height type, throw an error.
Operand::BlockHeight => bail!("Array element cannot be from a block height in a non-finalize scope"),
// If the operand is a network ID type, throw an error.
Operand::NetworkID => bail!("Array element cannot be from a network ID in a non-finalize scope"),
}
}
Ok(())
Expand Down Expand Up @@ -224,6 +230,9 @@ impl<N: Network> RegisterTypes<N> {
Operand::BlockHeight => {
bail!("Forbidden operation: Cannot cast a block height as a record owner")
}
Operand::NetworkID => {
bail!("Forbidden operation: Cannot cast a network ID as a record owner")
}
}

// Ensure the operand types match the record entry types.
Expand Down Expand Up @@ -279,6 +288,12 @@ impl<N: Network> RegisterTypes<N> {
"Record entry '{record_name}.{entry_name}' expects a '{plaintext_type}', but found a block height in the operand '{operand}'."
)
}
// Fail if the operand is a network ID.
Operand::NetworkID => {
bail!(
"Record entry '{record_name}.{entry_name}' expects a '{plaintext_type}', but found a network ID in the operand '{operand}'."
)
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions synthesizer/process/src/stack/register_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ impl<N: Network> RegisterTypes<N> {
RegisterType::Plaintext(PlaintextType::Literal(LiteralType::Address))
}
Operand::BlockHeight => bail!("'block.height' is not a valid operand in a non-finalize context."),
Operand::NetworkID => bail!("'network.id' is not a valid operand in a non-finalize context."),
})
}

Expand Down
4 changes: 4 additions & 0 deletions synthesizer/process/src/stack/registers/load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ impl<N: Network, A: circuit::Aleo<Network = N>> RegistersLoad<N> for Registers<N
Operand::Caller => return Ok(Value::Plaintext(Plaintext::from(Literal::Address(self.caller()?)))),
// If the operand is the block height, throw an error.
Operand::BlockHeight => bail!("Cannot load the block height in a non-finalize context"),
// If the operand is the network ID, throw an error.
Operand::NetworkID => bail!("Cannot load the network ID in a non-finalize context"),
};

// Retrieve the stack value.
Expand Down Expand Up @@ -121,6 +123,8 @@ impl<N: Network, A: circuit::Aleo<Network = N>> RegistersLoadCircuit<N, A> for R
}
// If the operand is the block height, throw an error.
Operand::BlockHeight => bail!("Cannot load the block height in a non-finalize context"),
// If the operand is the network ID, throw an error.
Operand::NetworkID => bail!("Cannot load the network ID in a non-finalize context"),
};

// Retrieve the circuit value.
Expand Down
2 changes: 2 additions & 0 deletions synthesizer/program/src/logic/instruction/operand/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ impl<N: Network> FromBytes for Operand<N> {
3 => Ok(Self::Signer),
4 => Ok(Self::Caller),
5 => Ok(Self::BlockHeight),
6 => Ok(Self::NetworkID),
variant => Err(error(format!("Failed to deserialize operand variant {variant}"))),
}
}
Expand All @@ -46,6 +47,7 @@ impl<N: Network> ToBytes for Operand<N> {
Self::Signer => 3u8.write_le(&mut writer),
Self::Caller => 4u8.write_le(&mut writer),
Self::BlockHeight => 5u8.write_le(&mut writer),
Self::NetworkID => 6u8.write_le(&mut writer),
}
}
}
3 changes: 3 additions & 0 deletions synthesizer/program/src/logic/instruction/operand/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ pub enum Operand<N: Network> {
/// The operand is the block height.
/// Note: This variant is only accessible in the `finalize` scope.
BlockHeight,
/// The operand is the network ID.
/// Note: This variant is only accessible in the `finalize` scope.
NetworkID,
}

impl<N: Network> From<Literal<N>> for Operand<N> {
Expand Down
6 changes: 6 additions & 0 deletions synthesizer/program/src/logic/instruction/operand/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ impl<N: Network> Parser for Operand<N> {
map(tag("self.signer"), |_| Self::Signer),
map(tag("self.caller"), |_| Self::Caller),
map(tag("block.height"), |_| Self::BlockHeight),
map(tag("network.id"), |_| Self::NetworkID),
// Note that `Operand::ProgramID`s must be parsed before `Operand::Literal`s, since a program ID can be implicitly parsed as a literal address.
// This ensures that the string representation of a program uses the `Operand::ProgramID` variant.
map(ProgramID::parse, |program_id| Self::ProgramID(program_id)),
Expand Down Expand Up @@ -76,6 +77,8 @@ impl<N: Network> Display for Operand<N> {
Self::Caller => write!(f, "self.caller"),
// Prints the identifier for the block height, i.e. block.height
Self::BlockHeight => write!(f, "block.height"),
// Prints the identifier for the network ID, i.e. network.id
Self::NetworkID => write!(f, "network.id"),
}
}
}
Expand Down Expand Up @@ -110,6 +113,9 @@ mod tests {
let operand = Operand::<CurrentNetwork>::parse("block.height").unwrap().1;
assert_eq!(Operand::BlockHeight, operand);

let operand = Operand::<CurrentNetwork>::parse("network.id").unwrap().1;
assert_eq!(Operand::NetworkID, operand);

let operand = Operand::<CurrentNetwork>::parse("group::GEN").unwrap().1;
assert_eq!(Operand::Literal(Literal::Group(Group::generator())), operand);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@
- Parsing was successful.
- Parsing was successful.
- Parsing was successful.
- Parsing was successful.
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
- Parsing was successful.
- Parsing was successful.
- Parsing was successful.
- Parsing was successful.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and r0 r1 into r2;
assert.eq r0 r1;
assert.neq r0 r1;
assert.eq block.height block.height;
assert.eq network.id network.id;
call foo;
call foo r0;
call foo r0 into r1;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
assert.eq self.caller self.caller;
assert.eq block.height block.height;
assert.eq network.id network.id;
assert.eq r88 r101;
assert.eq hello.aleo goodbye.aleo;
assert.eq aleo1dg722m22fzpz6xjdrvl9tzu5t68zmypj5p74khlqcac0gvednygqxaax0j aleo1dg722m22fzpz6xjdrvl9tzu5t68zmypj5p74khlqcac0gvednygqxaax0j;
Expand Down

0 comments on commit d48f6fb

Please sign in to comment.