use {
crate::{
move_bindings::{
registry::network_auth as network_auth_binding,
sui_framework::transfer as transfer_binding,
},
move_boundary,
sui,
types::NexusContext,
},
sui::types::{Argument, ProgrammableTransaction},
};
fn description_option(
tx: &mut move_boundary::NexusPtbBuilder,
description: Option<Vec<u8>>,
) -> anyhow::Result<Argument> {
let description = description
.as_ref()
.map(|description| tx.arg(description))
.transpose()?;
Ok(tx.option::<Vec<u8>>(description)?)
}
fn proof_for_offchain_tool(
tx: &mut move_boundary::NexusPtbBuilder,
tool: Argument,
owner_cap: Argument,
) -> anyhow::Result<Argument> {
tx.call_target(
network_auth_binding::prove_offchain_tool_target,
vec![tool, owner_cap],
)
}
fn proof_for_leader(
tx: &mut move_boundary::NexusPtbBuilder,
leader_registry: Argument,
leader_cap: Argument,
) -> anyhow::Result<Argument> {
tx.call_target(
network_auth_binding::prove_leader_target,
vec![leader_registry, leader_cap],
)
}
fn register_key(
tx: &mut move_boundary::NexusPtbBuilder,
binding: Argument,
proof: Argument,
public_key: [u8; 32],
pop_signature: [u8; 64],
) -> anyhow::Result<()> {
let public_key = tx.arg(&public_key.to_vec())?;
let signature = tx.arg(&pop_signature.to_vec())?;
let proof_of_key = tx.call_target(
network_auth_binding::new_proof_of_key_target,
vec![binding, proof, public_key, signature],
)?;
let clock = tx.clock()?;
tx.call_target(
network_auth_binding::register_key_target,
vec![binding, proof, proof_of_key, clock],
)?;
Ok(())
}
fn create_binding(
tx: &mut move_boundary::NexusPtbBuilder,
proof: Argument,
description: Option<Vec<u8>>,
) -> anyhow::Result<Argument> {
let network_auth = tx.objects().network_auth;
let network_auth = tx.shared_root(&network_auth, true)?;
let description = description_option(tx, description)?;
tx.call_target(
network_auth_binding::create_binding_target,
vec![network_auth, proof, description],
)
}
pub(super) fn share_binding(
tx: &mut move_boundary::NexusPtbBuilder,
binding: Argument,
) -> anyhow::Result<()> {
tx.call_target(
transfer_binding::public_share_object_target::<network_auth_binding::KeyBinding>,
vec![binding],
)?;
Ok(())
}
pub(super) fn create_tool_binding_and_register_key(
tx: &mut move_boundary::NexusPtbBuilder,
tool: Argument,
owner_cap: Argument,
public_key: [u8; 32],
pop_signature: [u8; 64],
description: Option<Vec<u8>>,
) -> anyhow::Result<Argument> {
let proof_for_binding = proof_for_offchain_tool(tx, tool, owner_cap)?;
let binding = create_binding(tx, proof_for_binding, description)?;
let proof_for_key = proof_for_offchain_tool(tx, tool, owner_cap)?;
register_key(tx, binding, proof_for_key, public_key, pop_signature)?;
Ok(binding)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn create_tool_binding_and_register_key_ptb(
objects: &NexusContext,
tool: &sui::types::ObjectReference,
owner_cap_over_tool: &sui::types::ObjectReference,
public_key: [u8; 32],
pop_signature: [u8; 64],
description: Option<Vec<u8>>,
) -> anyhow::Result<ProgrammableTransaction> {
move_boundary::ptb(objects, |tx| {
let tool = tx.shared_object(tool, false)?;
let owner_cap = tx.owned_object(owner_cap_over_tool)?;
let binding = create_tool_binding_and_register_key(
tx,
tool,
owner_cap,
public_key,
pop_signature,
description,
)?;
share_binding(tx, binding)
})
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn register_tool_key_on_existing_binding_ptb(
objects: &NexusContext,
binding: &sui::types::ObjectReference,
tool: &sui::types::ObjectReference,
owner_cap_over_tool: &sui::types::ObjectReference,
public_key: [u8; 32],
pop_signature: [u8; 64],
) -> anyhow::Result<ProgrammableTransaction> {
move_boundary::ptb(objects, |tx| {
let binding = tx.shared_object(binding, true)?;
let tool = tx.shared_object(tool, false)?;
let owner_cap = tx.owned_object(owner_cap_over_tool)?;
let proof = proof_for_offchain_tool(tx, tool, owner_cap)?;
register_key(tx, binding, proof, public_key, pop_signature)
})
}
#[allow(clippy::too_many_arguments)]
pub fn create_leader_binding_and_register_key_ptb(
objects: &NexusContext,
leader_cap_over_network: &sui::types::ObjectReference,
public_key: [u8; 32],
pop_signature: [u8; 64],
description: Option<Vec<u8>>,
) -> anyhow::Result<ProgrammableTransaction> {
move_boundary::ptb(objects, |tx| {
let leader_registry = tx.shared_root(&objects.leader_registry, false)?;
let leader_cap = tx.shared_object(leader_cap_over_network, false)?;
let proof_for_binding = proof_for_leader(tx, leader_registry, leader_cap)?;
let binding = create_binding(tx, proof_for_binding, description)?;
let proof_for_key = proof_for_leader(tx, leader_registry, leader_cap)?;
register_key(tx, binding, proof_for_key, public_key, pop_signature)?;
share_binding(tx, binding)
})
}
#[allow(clippy::too_many_arguments)]
pub fn register_leader_key_on_existing_binding_ptb(
objects: &NexusContext,
binding: &sui::types::ObjectReference,
leader_cap_over_network: &sui::types::ObjectReference,
public_key: [u8; 32],
pop_signature: [u8; 64],
) -> anyhow::Result<ProgrammableTransaction> {
move_boundary::ptb(objects, |tx| {
let binding = tx.shared_object(binding, true)?;
let leader_registry = tx.shared_root(&objects.leader_registry, false)?;
let leader_cap = tx.shared_object(leader_cap_over_network, false)?;
let proof = proof_for_leader(tx, leader_registry, leader_cap)?;
register_key(tx, binding, proof, public_key, pop_signature)
})
}
#[cfg(test)]
mod tests {
use {super::*, crate::test_utils::sui_mocks};
#[test]
fn tool_binding_composition_matches_the_standalone_builder() {
let objects = sui_mocks::mock_nexus_context();
let tool = sui_mocks::mock_sui_object_ref();
let owner_cap = sui_mocks::mock_sui_object_ref();
let public_key = [3u8; 32];
let pop_signature = [5u8; 64];
let description = Some(b"atomic".to_vec());
let expected = create_tool_binding_and_register_key_ptb(
&objects,
&tool,
&owner_cap,
public_key,
pop_signature,
description.clone(),
)
.unwrap();
let actual = move_boundary::ptb(&objects, |tx| {
let tool = tx.shared_object(&tool, false)?;
let owner_cap = tx.owned_object(&owner_cap)?;
let binding = create_tool_binding_and_register_key(
tx,
tool,
owner_cap,
public_key,
pop_signature,
description,
)?;
share_binding(tx, binding)
})
.unwrap();
assert_eq!(actual, expected);
}
}