use ai_registry_api::{
consts::{AGENT, FLAG_ACTIVE, MAX_KNOWLEDGE_REFERENCE_LENGTH, MAX_URI_LENGTH},
error::AgentError,
event::AgentCreatedEvent,
instruction::CreateAgent,
state::Agent,
};
use steel::*;
fn is_valid_cid(bytes: &[u8]) -> bool {
if let Ok(s) = core::str::from_utf8(bytes) {
if s.is_empty() || s.len() > MAX_URI_LENGTH {
return false;
}
if s.starts_with("bafy") {
return s.chars().all(|c| matches!(c, 'a'..='z' | '2'..='7'));
} else if s.starts_with("Qm") {
return s.len() == 46
&& s.chars().all(|c| {
matches!(
c,
'1'..='9'
| 'A'..='H'
| 'J'..='N'
| 'P'..='Z'
| 'a'..='k'
| 'm'..='z'
)
});
}
}
false
}
pub fn process_create_agent(accounts: &[AccountInfo<'_>], data: &[u8]) -> ProgramResult {
let args = CreateAgent::try_from_bytes(data)?;
let [creator_info, agent_info, system_program] = accounts else {
return Err(ProgramError::NotEnoughAccountKeys);
};
creator_info.is_signer()?;
agent_info
.is_empty()?
.is_writable()?
.has_seeds(&[AGENT, args.creator.as_ref()], &ai_registry_api::ID)?;
system_program.is_program(&system_program::ID)?;
if creator_info.key != &args.creator {
return Err(AgentError::InvalidCreator.into());
}
let uri_len = args
.identity_profile_uri
.iter()
.position(|&b| b == 0)
.unwrap_or(MAX_URI_LENGTH);
let uri_bytes = &args.identity_profile_uri[..uri_len];
if uri_bytes.is_empty() || uri_bytes.len() > MAX_URI_LENGTH {
return Err(AgentError::InvalidLength.into());
}
let uri_str =
core::str::from_utf8(uri_bytes).map_err(|_| AgentError::InvalidIdentityProfileUri)?;
if !uri_str.starts_with("https://") && !uri_str.starts_with("ipfs://") {
return Err(AgentError::InsecureUri.into());
}
if args.knowledge_reference_type != 0 {
let ref_len = args
.knowledge_reference
.iter()
.position(|&b| b == 0)
.unwrap_or(MAX_KNOWLEDGE_REFERENCE_LENGTH);
let ref_bytes = &args.knowledge_reference[..ref_len];
if ref_bytes.is_empty() {
return Err(AgentError::InvalidKnowledgeFields.into());
}
match args.knowledge_reference_type {
1 => {
if !is_valid_cid(ref_bytes) {
return Err(AgentError::InvalidKnowledgeFields.into());
}
}
2 => {
let url_str = core::str::from_utf8(ref_bytes)
.map_err(|_| AgentError::InvalidKnowledgeFields)?;
if !url_str.starts_with("https://") {
return Err(AgentError::InsecureUri.into());
}
if args.knowledge_content_hash == [0u8; 32] {
return Err(AgentError::InvalidKnowledgeFields.into());
}
}
3 => {
if args.knowledge_content_hash == [0u8; 32] {
return Err(AgentError::InvalidKnowledgeFields.into());
}
}
4 => {
if args.knowledge_content_hash == [0u8; 32] {
return Err(AgentError::InvalidKnowledgeFields.into());
}
}
_ => return Err(AgentError::InvalidKnowledgeFields.into()),
}
} else {
if args.knowledge_reference != [0u8; 128] || args.knowledge_content_hash != [0u8; 32] {
return Err(AgentError::InvalidKnowledgeFields.into());
}
}
let (expected_agent_pda, bump) = ai_registry_api::state::agent_pda(args.creator);
if agent_info.key != &expected_agent_pda {
return Err(ProgramError::InvalidArgument);
}
create_program_account::<Agent>(
agent_info,
system_program,
creator_info,
&ai_registry_api::ID,
&[AGENT, args.creator.as_ref()],
)?;
let clock = Clock::get()?;
let agent = agent_info.as_account_mut::<Agent>(&ai_registry_api::ID)?;
agent.version = 1; agent.agent_version = args.agent_version;
agent.created_at = clock.unix_timestamp;
agent.updated_at = clock.unix_timestamp;
agent.finalized_at = 0;
agent.creator = args.creator;
agent.controller = args.creator;
agent.identity_profile_uri = args.identity_profile_uri;
agent.identity_profile_uri_len = uri_len as u8;
agent.identity_profile_hash = args.identity_profile_hash;
agent.knowledge_reference_type = args.knowledge_reference_type;
if args.knowledge_reference_type != 0 {
let ref_len = args
.knowledge_reference
.iter()
.position(|&b| b == 0)
.unwrap_or(MAX_KNOWLEDGE_REFERENCE_LENGTH);
agent.knowledge_reference = args.knowledge_reference;
agent.knowledge_reference_len = ref_len as u8;
agent.knowledge_content_hash = args.knowledge_content_hash;
} else {
agent.knowledge_reference = [0u8; 128];
agent.knowledge_reference_len = 0;
agent.knowledge_content_hash = [0u8; 32];
}
agent.state_flags = FLAG_ACTIVE;
agent.bump = bump;
agent.reserved = [0u8; 8];
agent._padding = [0u8; 7];
AgentCreatedEvent {
creator: args.creator,
controller: args.creator,
agent: *agent_info.key,
timestamp: clock.unix_timestamp,
agent_version: args.agent_version,
}
.log();
Ok(())
}