Skip to content

Commit

Permalink
Port more aggregator tests to cairo (#304)
Browse files Browse the repository at this point in the history
* add more aggregator cairo tests

* fix linting

* remove hardhat tests covered by cairo tests

* fix incorrect abi

* fix test
  • Loading branch information
calvwang9 authored Jun 15, 2023
1 parent 12339c1 commit c224a3c
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 169 deletions.
211 changes: 194 additions & 17 deletions contracts/src/tests/test_aggregator.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use starknet::class_hash::Felt252TryIntoClassHash;
use starknet::syscalls::deploy_syscall;

use array::ArrayTrait;
use clone::Clone;
use traits::Into;
use traits::TryInto;
use option::OptionTrait;
Expand All @@ -15,12 +16,12 @@ use core::result::ResultTrait;
use chainlink::ocr2::aggregator::pow;
use chainlink::ocr2::aggregator::Aggregator;
use chainlink::ocr2::aggregator::Aggregator::Billing;
use chainlink::ocr2::aggregator::Aggregator::PayeeConfig;
use chainlink::access_control::access_controller::AccessController;
use chainlink::token::link_token::LinkToken;
use chainlink::tests::test_ownable::should_implement_ownable;
use chainlink::tests::test_access_controller::should_implement_access_control;

// TODO: aggregator tests

#[test]
#[available_gas(10000000)]
fn test_pow_2_0() {
Expand Down Expand Up @@ -67,7 +68,12 @@ trait IAccessController { // importing from access_controller.cairo doesnt work
fn disable_access_check();
}

fn setup() -> (ContractAddress, ContractAddress, ContractAddress, IAccessControllerDispatcher) {
#[abi]
trait ILinkToken {}

fn setup() -> (
ContractAddress, ContractAddress, IAccessControllerDispatcher, ILinkTokenDispatcher
) {
let acc1: ContractAddress = contract_address_const::<777>();
let acc2: ContractAddress = contract_address_const::<888>();
// set acc1 as default caller
Expand All @@ -84,8 +90,18 @@ fn setup() -> (ContractAddress, ContractAddress, ContractAddress, IAccessControl
contract_address: billingAccessControllerAddr
};

// return accounts and billing access controller
(acc1, acc2, billingAccessControllerAddr, billingAccessController)
// deploy link token contract
let mut calldata = ArrayTrait::new();
calldata.append(acc1.into()); // minter = acc1;
calldata.append(acc1.into()); // owner = acc1;
let (linkTokenAddr, _) = deploy_syscall(
LinkToken::TEST_CLASS_HASH.try_into().unwrap(), 0, calldata.span(), false
)
.unwrap();
let linkToken = ILinkTokenDispatcher { contract_address: linkTokenAddr };

// return accounts, billing access controller, link token
(acc1, acc2, billingAccessController, linkToken)
}

#[test]
Expand Down Expand Up @@ -145,23 +161,27 @@ fn test_upgrade_non_owner() {
#[available_gas(2000000)]
#[should_panic(expected: ('Ownable: caller is not owner', ))]
fn test_set_billing_access_controller_not_owner() {
let (owner, acc2, billingAccessControllerAddr, _) = setup();
// Aggregator initialization
let (owner, acc2, billingAccessController, _) = setup();
Aggregator::constructor(owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123);

// set billing access controller should revert if caller is not owner
set_caller_address(acc2);
Aggregator::set_billing_access_controller(billingAccessControllerAddr);
Aggregator::set_billing_access_controller(billingAccessController.contract_address);
}

#[test]
#[available_gas(2000000)]
#[should_panic(expected: ('caller does not have access', ))]
fn test_set_billing_config_no_access() {
let (owner, acc2, billingAccessControllerAddr, _) = setup();
// Aggregator initialization
let (owner, acc2, billingAccessController, _) = setup();
Aggregator::constructor(
owner, contract_address_const::<777>(), 0, 100, billingAccessControllerAddr, 8, 123
owner,
contract_address_const::<777>(),
0,
100,
billingAccessController.contract_address,
8,
123
);

// set billing config as acc2 with no access
Expand All @@ -178,10 +198,15 @@ fn test_set_billing_config_no_access() {
#[test]
#[available_gas(2000000)]
fn test_set_billing_config_as_owner() {
let (owner, _, billingAccessControllerAddr, _) = setup();
// Aggregator initialization
let (owner, _, billingAccessController, _) = setup();
Aggregator::constructor(
owner, contract_address_const::<777>(), 0, 100, billingAccessControllerAddr, 8, 123
owner,
contract_address_const::<777>(),
0,
100,
billingAccessController.contract_address,
8,
123
);

// set billing config as owner
Expand All @@ -204,14 +229,19 @@ fn test_set_billing_config_as_owner() {
#[test]
#[available_gas(2000000)]
fn test_set_billing_config_as_acc_with_access() {
let (owner, acc2, billingAccessControllerAddr, billingAccessController) = setup();
let (owner, acc2, billingAccessController, _) = setup();
// grant acc2 access on access controller
set_contract_address(owner);
billingAccessController.add_access(acc2);

// Aggregator initialization
Aggregator::constructor(
owner, contract_address_const::<777>(), 0, 100, billingAccessControllerAddr, 8, 123
owner,
contract_address_const::<777>(),
0,
100,
billingAccessController.contract_address,
8,
123
);

// set billing config as acc2 with access
Expand All @@ -231,3 +261,150 @@ fn test_set_billing_config_as_acc_with_access() {
assert(billing.gas_base == 1, 'should be 1');
assert(billing.gas_per_signature == 1, 'should be 1');
}

// --- Payee Management Tests ---

#[test]
#[available_gas(2000000)]
#[should_panic(expected: ('Ownable: caller is not owner', ))]
fn test_set_payees_caller_not_owner() {
let (owner, acc2, _, _) = setup();
Aggregator::constructor(owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123);

let mut payees = ArrayTrait::new();
payees.append(PayeeConfig { transmitter: acc2, payee: acc2, });

// set payee should revert if caller is not owner
set_caller_address(acc2);
Aggregator::set_payees(payees);
}

#[test]
#[available_gas(2000000)]
fn test_set_single_payee() {
let (owner, acc2, _, _) = setup();
Aggregator::constructor(owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123);

let mut payees = ArrayTrait::new();
payees.append(PayeeConfig { transmitter: acc2, payee: acc2, });

set_caller_address(owner);
Aggregator::set_payees(payees);
}

#[test]
#[available_gas(2000000)]
fn test_set_multiple_payees() {
let (owner, acc2, _, _) = setup();
Aggregator::constructor(owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123);

let mut payees = ArrayTrait::new();
payees.append(PayeeConfig { transmitter: acc2, payee: acc2, });
payees.append(PayeeConfig { transmitter: owner, payee: owner, });

set_caller_address(owner);
Aggregator::set_payees(payees);
}

#[test]
#[available_gas(2000000)]
#[should_panic(expected: ('only current payee can update', ))]
fn test_transfer_payeeship_caller_not_payee() {
let (owner, acc2, _, _) = setup();
Aggregator::constructor(owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123);

let transmitter = contract_address_const::<123>();
let mut payees = ArrayTrait::new();
payees.append(PayeeConfig { transmitter: transmitter, payee: acc2, });

set_caller_address(owner);
Aggregator::set_payees(payees);
Aggregator::transfer_payeeship(transmitter, owner);
}

#[test]
#[available_gas(2000000)]
#[should_panic(expected: ('cannot transfer to self', ))]
fn test_transfer_payeeship_to_self() {
let (owner, acc2, _, _) = setup();
Aggregator::constructor(owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123);

let transmitter = contract_address_const::<123>();
let mut payees = ArrayTrait::new();
payees.append(PayeeConfig { transmitter: transmitter, payee: acc2, });

set_caller_address(owner);
Aggregator::set_payees(payees);
set_caller_address(acc2);
Aggregator::transfer_payeeship(transmitter, acc2);
}

#[test]
#[available_gas(2000000)]
#[should_panic(expected: ('only proposed payee can accept', ))]
fn test_accept_payeeship_caller_not_proposed_payee() {
let (owner, acc2, _, _) = setup();
Aggregator::constructor(owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123);

let transmitter = contract_address_const::<123>();
let mut payees = ArrayTrait::new();
payees.append(PayeeConfig { transmitter: transmitter, payee: acc2, });

set_caller_address(owner);
Aggregator::set_payees(payees);
set_caller_address(acc2);
Aggregator::transfer_payeeship(transmitter, owner);
Aggregator::accept_payeeship(transmitter);
}

#[test]
#[available_gas(2000000)]
fn test_transfer_and_accept_payeeship() {
let (owner, acc2, _, _) = setup();
Aggregator::constructor(owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123);

let transmitter = contract_address_const::<123>();
let mut payees = ArrayTrait::new();
payees.append(PayeeConfig { transmitter: transmitter, payee: acc2, });

set_caller_address(owner);
Aggregator::set_payees(payees);
set_caller_address(acc2);
Aggregator::transfer_payeeship(transmitter, owner);
set_caller_address(owner);
Aggregator::accept_payeeship(transmitter);
}
// --- Payments and Withdrawals Tests ---
//
// NOTE: this test suite largely incomplete as we cannot generate or mock
// off-chain signatures in cairo-test, and thus cannot generate aggregator rounds.
// We could explore testing against a mock aggregator contract with the signature
// verification logic removed in the future.

#[test]
#[available_gas(2000000)]
fn test_owed_payment_no_rounds() {
let (owner, acc2, _, _) = setup();
Aggregator::constructor(owner, contract_address_const::<777>(), 0, 100, acc2, 8, 123);

let transmitter = contract_address_const::<123>();
let mut payees = ArrayTrait::new();
payees.append(PayeeConfig { transmitter: transmitter, payee: acc2, });

set_caller_address(owner);
Aggregator::set_payees(payees);

let owed = Aggregator::owed_payment(transmitter);
assert(owed == 0, 'owed payment should be 0');
}

#[test]
#[available_gas(2000000)]
fn test_link_available_for_payment_no_rounds_or_funds() {
let (owner, acc2, _, linkToken) = setup();
Aggregator::constructor(owner, linkToken.contract_address, 0, 100, acc2, 8, 123);

let (is_negative, diff) = Aggregator::link_available_for_payment();
assert(is_negative == true, 'is_negative should be true');
assert(diff == 0, 'absolute_diff should be 0');
}
57 changes: 3 additions & 54 deletions contracts/test/ocr2/aggregator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,67 +280,16 @@ describe('Aggregator', function () {
await expectInvokeError(transmit(4, UINT128_MAX), 'median is out of min-max range')
})

it('payee management', async () => {
it('payments and withdrawals', async () => {
// set up payees
let payees = oracles.map((oracle) => ({
transmitter: oracle.transmitter.starknetContract.address,
payee: oracle.transmitter.starknetContract.address, // reusing transmitter acocunts as payees for simplicity
}))
// call set_payees, should succeed because all payees are zero
await owner.invoke(aggregator, 'set_payees', { payees })
// call set_payees, should succeed because values are unchanged
await owner.invoke(aggregator, 'set_payees', { payees })

let oracle = oracles[0].transmitter
let transmitter = oracle.starknetContract.address
let payee = transmitter

let proposed_oracle = oracles[1].transmitter
let proposed_transmitter = proposed_oracle.starknetContract.address
let proposed_payee = proposed_transmitter

// can't transfer to self
try {
await oracle.invoke(aggregator, 'transfer_payeeship', {
transmitter,
proposed: payee,
})
expect.fail()
} catch (err: any) {
// TODO: expect(err.message).to.contain("");
}

// only payee can transfer
try {
await proposed_oracle.invoke(aggregator, 'transfer_payeeship', {
transmitter,
proposed: proposed_payee,
})
expect.fail()
} catch (err: any) {}

// successful transfer
await oracle.invoke(aggregator, 'transfer_payeeship', {
transmitter,
proposed: proposed_payee,
})

// only proposed payee can accept
try {
await oracle.invoke(aggregator, 'accept_payeeship', { transmitter })
expect.fail()
} catch (err: any) {}

// successful accept
await proposed_oracle.invoke(aggregator, 'accept_payeeship', {
transmitter,
})
})

it('payments and withdrawals', async () => {
let oracle = oracles[0]
// NOTE: previous test changed oracle0's payee to oracle1
let payee = oracles[1].transmitter
aggregator.call
let payee = oracle.transmitter
let { response: owed } = await aggregator.call('owed_payment', {
transmitter: oracle.transmitter.starknetContract.address,
})
Expand Down
Loading

0 comments on commit c224a3c

Please sign in to comment.