Task2
任务代码
以下是待分析的 Move 合约,完整代码请查看 挑战合约:
PackageID: 0xd26a14084af49f68d4612ef0815518c251f7a0459eaf2cbcb2757efafad442c5
Challenge ObjectID: 0x22c1330b43313cee7f0ca0ce36965343c1ae40577d80a4ffa8ab12986f50dea1
module task2::task2 {
use sui::event;
use sui::random::{Random, generate_u64, new_generator};
use sui::clock::{Self, Clock};
use std::hash;
use std::string::String;
const E_WRONG_STAGE: u64 = 1;
const E_COOLDOWN: u64 = 2;
const E_MAX_ATTEMPTS_EXCEEDED: u64 = 3;
public struct Challenge has key {
id: UID,
owner: address,
secret_hash: vector<u8>,
attempts: u64,
max_attempts: u64,
last_attempt_time: u64,
is_solved: bool,
stage: u64,
target_score: u64,
current_score: u64,
bonus_multiplier: u64,
guess_round: u64,
round_hash: vector<u8>,
seed: u64,
}
public struct FlagEvent has copy, drop {
flag: vector<u8>,
github_id: String
}
public struct ScoreEvent has copy, drop {
score: u64,
}
fun init(ctx: &mut TxContext) {
let secret = b"LetsMoveCTF";
let secret_hash = hash::sha3_256(secret);
let challenge = Challenge {
id: object::new(ctx),
owner: tx_context::sender(ctx),
secret_hash: secret_hash,
attempts: 0,
max_attempts: 50,
last_attempt_time: 0,
is_solved: false,
stage: 1,
target_score: 100,
current_score: 0,
bonus_multiplier: 0,
guess_round: 1,
round_hash: secret_hash,
seed: 0,
};
transfer::share_object(challenge);
}
public entry fun submit_score(challenge: &mut Challenge, score: u64, clock: &Clock) {
assert!(challenge.stage == 1, E_WRONG_STAGE);
let current_time = clock::timestamp_ms(clock);
assert!(current_time >= challenge.last_attempt_time + 5000, E_COOLDOWN);
challenge.attempts = challenge.attempts + 1;
assert!(challenge.attempts < challenge.max_attempts, E_MAX_ATTEMPTS_EXCEEDED);
let time_factor = (current_time - challenge.last_attempt_time) / 1000;
let attempt_penalty = challenge.attempts * 2;
let adjusted_score = if (score > attempt_penalty) {
score - attempt_penalty
} else {
0
};
let final_score = adjusted_score * challenge.bonus_multiplier + time_factor;
challenge.current_score = challenge.current_score + final_score;
challenge.last_attempt_time = current_time;
event::emit(ScoreEvent { score: challenge.current_score });
if (challenge.current_score >= challenge.target_score) {
challenge.stage = 2;
challenge.attempts = 0;
};
}
#[allow(lint(public_random))]
public entry fun submit_guess(challenge: &mut Challenge, randomseed: &Random, guess: vector<u8>, clock: &Clock, ctx: &mut TxContext) {
assert!(challenge.stage == 2, E_WRONG_STAGE);
let mut random_gen = new_generator(randomseed, ctx);
let seed = generate_u64(&mut random_gen);
let current_time = clock::timestamp_ms(clock);
assert!(current_time >= challenge.last_attempt_time + 5000, E_COOLDOWN);
assert!(challenge.attempts < challenge.max_attempts, E_MAX_ATTEMPTS_EXCEEDED);
challenge.attempts = challenge.attempts + 1;
let mut guess_data = guess;
vector::append(&mut guess_data, to_bytes(current_time));
vector::append(&mut guess_data, to_bytes(challenge.attempts));
let random = hash::sha3_256(guess_data);
let prefix_length = challenge.guess_round * 2;
if (compare_hash_prefix(&random, &challenge.round_hash, prefix_length)) {
challenge.guess_round = challenge.guess_round + 1;
let mut new_hash_data = random;
vector::append(&mut new_hash_data, to_bytes(challenge.seed + challenge.guess_round));
challenge.round_hash = hash::sha3_256(new_hash_data);
challenge.seed = seed;
if (challenge.guess_round > 3) {
challenge.is_solved = true;
challenge.stage = 3;
challenge.guess_round = 1;
challenge.attempts = 0;
};
};
challenge.last_attempt_time = current_time;
}
public entry fun get_flag(challenge: &mut Challenge,github_id: String, _: &mut TxContext) {
assert!(challenge.stage == 3 && challenge.is_solved, E_WRONG_STAGE);
reset_challenge(challenge);
event::emit(FlagEvent { flag: b"flag{LetsMoveCTF_chapter_2}" ,github_id});
}
public fun reset_challenge(challenge: &mut Challenge) {
challenge.attempts = 0;
challenge.last_attempt_time = 0;
challenge.is_solved = false;
challenge.stage = 1;
challenge.current_score = 0;
challenge.bonus_multiplier = 1;
challenge.guess_round = 1;
challenge.round_hash = challenge.secret_hash;
}
fun to_bytes(value: u64): vector<u8> {
let mut bytes = vector::empty<u8>();
let mut i = 0;
while (i < 8) {
vector::push_back(&mut bytes, ((value >> (i * 8)) & 0xFF as u8));
i = i + 1;
};
bytes
}
fun compare_hash_prefix(hash1: &vector<u8>, hash2: &vector<u8>, n: u64): bool {
if (vector::length(hash1) < n || vector::length(hash2) < n) {
return false
};
let mut i = 0;
while (i < n) {
if (*vector::borrow(hash1, i) != *vector::borrow(hash2, i)) {
return false
};
i = i + 1;
};
true
}
}