Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions contract/contracts/agent_registry/src/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ pub fn rate_agent(

rater.require_auth();

// An agent can never rate itself, even if it somehow appears as a party.
if rater == agent {
return Err(AgentError::SelfRatingNotAllowed);
}

if !(1..=5).contains(&score) {
return Err(AgentError::InvalidRatingScore);
}
Expand Down Expand Up @@ -197,6 +202,7 @@ pub fn get_agent_count(env: &Env) -> u32 {

pub fn register_transaction(
env: &Env,
caller: Address,
transaction_id: String,
agent: Address,
parties: Vec<Address>,
Expand All @@ -207,11 +213,51 @@ pub fn register_transaction(

check_paused(env)?;

// Authenticate the caller. Without this, anyone could fabricate transaction
// records naming an arbitrary agent and parties.
caller.require_auth();

let agent_key = DataKey::Agent(agent.clone());
if !env.storage().persistent().has(&agent_key) {
return Err(AgentError::AgentNotFound);
}

// Validate the parties list: it must be non-empty, all entries distinct, and
// none of them may be the agent. Parties are the counterparties who can later
// rate the agent, so keeping the agent out of this list forecloses self-rating
// at the source.
let len = parties.len();
if len == 0 {
return Err(AgentError::InvalidParties);
}
for i in 0..len {
let party = parties.get(i).unwrap();
if party == agent {
return Err(AgentError::InvalidParties);
}
for j in (i + 1)..len {
if party == parties.get(j).unwrap() {
return Err(AgentError::InvalidParties);
}
}
}

// The caller must be a genuine participant of the transaction: either the
// agent itself or one of the listed parties. This stops a third party from
// manufacturing reputation-bearing records for others.
let mut caller_is_participant = caller == agent;
if !caller_is_participant {
for party in parties.iter() {
if party == caller {
caller_is_participant = true;
break;
}
}
}
if !caller_is_participant {
return Err(AgentError::NotTransactionParty);
}

let txn_key = DataKey::Transaction(transaction_id.clone());

let transaction = AgentTransaction {
Expand Down Expand Up @@ -253,6 +299,17 @@ pub fn complete_transaction(
return Err(AgentError::Unauthorized);
}

// Only the agent named on the transaction may mark it complete. Without this
// auth check anyone could complete on the agent's behalf and inflate the
// agent's completed_agreements counter.
agent.require_auth();

// Guard against double-completion, which would otherwise bump
// completed_agreements more than once for a single transaction.
if transaction.completed {
return Err(AgentError::TransactionAlreadyCompleted);
}

transaction.completed = true;

env.storage().persistent().set(&txn_key, &transaction);
Expand Down
3 changes: 3 additions & 0 deletions contract/contracts/agent_registry/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,7 @@ pub enum AgentError {
NotPaused = 16,
NoPendingAdmin = 17,
NotPendingAdmin = 18,
SelfRatingNotAllowed = 19,
InvalidParties = 20,
TransactionAlreadyCompleted = 21,
}
9 changes: 8 additions & 1 deletion contract/contracts/agent_registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ impl AgentRegistryContract {
/// * `TransactionNotFound` - If the transaction doesn't exist
/// * `TransactionNotCompleted` - If the transaction is not marked as completed
/// * `NotTransactionParty` - If the rater wasn't part of the transaction
/// * `SelfRatingNotAllowed` - If the rater is the agent being rated
/// * `AlreadyRated` - If the rater has already rated this agent
pub fn rate_agent(
env: Env,
Expand Down Expand Up @@ -147,20 +148,25 @@ impl AgentRegistryContract {
/// This is called when a rent agreement or property transaction is created.
///
/// # Arguments
/// * `caller` - The participant registering the transaction; must be the
/// agent or one of the parties and must authorize the call
/// * `transaction_id` - Unique identifier for the transaction
/// * `agent` - The agent involved in the transaction
/// * `parties` - Vector of addresses involved (tenant, landlord, etc.)
///
/// # Errors
/// * `NotInitialized` - If the contract hasn't been initialized
/// * `AgentNotFound` - If the agent doesn't exist
/// * `InvalidParties` - If parties is empty, has duplicates, or includes the agent
/// * `NotTransactionParty` - If the caller is neither the agent nor a party
pub fn register_transaction(
env: Env,
caller: Address,
transaction_id: String,
agent: Address,
parties: Vec<Address>,
) -> Result<(), AgentError> {
agent::register_transaction(&env, transaction_id, agent, parties)
agent::register_transaction(&env, caller, transaction_id, agent, parties)
}

/// Mark a transaction as completed.
Expand All @@ -174,6 +180,7 @@ impl AgentRegistryContract {
/// * `NotInitialized` - If the contract hasn't been initialized
/// * `TransactionNotFound` - If the transaction doesn't exist
/// * `Unauthorized` - If the caller is not the agent for this transaction
/// * `TransactionAlreadyCompleted` - If the transaction was already completed
pub fn complete_transaction(
env: Env,
transaction_id: String,
Expand Down
Loading