Created
January 4, 2023 14:34
-
-
Save matiwinnetou/fcdf660fbc3467df6237f612c1f16540 to your computer and use it in GitHub Desktop.
Aiken's vesting contract
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use aiken/hash.{Blake2b_224, Hash} | |
use aiken/interval.{Finite, Interval, IntervalBound} | |
use aiken/list | |
use aiken/transaction.{ScriptContext, Spend, ValidityRange} | |
use aiken/transaction/credential.{VerificationKey} | |
type POSIXTime = | |
Int | |
type PubKeyHash = | |
Hash<Blake2b_224, VerificationKey> | |
pub type Datum { | |
/// unix time, e.g. 1672843961000 | |
lock_until: POSIXTime, | |
/// owner pub key | |
owner: PubKeyHash, | |
/// owner beneficiary pub key | |
beneficiary: PubKeyHash, | |
} | |
pub fn spend(datum: Datum, _: Void, ctx: ScriptContext) -> Bool { | |
when ctx.purpose is { | |
Spend(_) -> | |
bound_check( | |
ctx.transaction.validity_range, | |
ctx.transaction.extra_signatories, | |
datum, | |
) | |
_ -> False | |
} | |
} | |
fn bound_check( | |
validity_range: ValidityRange, | |
extra_signatories: List<PubKeyHash>, | |
datum: Datum, | |
) -> Bool { | |
let is_signed_by_owner = | |
list.any(extra_signatories, fn(vk) { vk == datum.owner }) | |
if is_signed_by_owner { | |
True | |
} else { | |
let start_bound_type = validity_range.lower_bound.bound_type | |
when start_bound_type is { | |
Finite(now) -> is_beneficiary_unlocked(now, extra_signatories, datum) | |
_ -> False | |
} | |
} | |
} | |
fn is_beneficiary_unlocked( | |
now: POSIXTime, | |
extra_signatories: List<PubKeyHash>, | |
datum: Datum, | |
) -> Bool { | |
let time_match = now >= datum.lock_until | |
let is_signed_by_beneficiary = | |
list.any(extra_signatories, fn(vk) { vk == datum.beneficiary }) | |
is_signed_by_beneficiary && time_match | |
} | |
test test_bound_check_success_beneficiary_can_only_unlock_if_time_is_right() { | |
let owner = #[1] | |
let beneficiary = #[2] | |
let datum = Datum { lock_until: 1, owner, beneficiary } | |
let transaction_validity_range: ValidityRange = | |
Interval { | |
lower_bound: IntervalBound { bound_type: Finite(2), is_inclusive: True }, | |
upper_bound: IntervalBound { bound_type: Finite(10), is_inclusive: True }, | |
} | |
let extra_signatories = [beneficiary] | |
bound_check(transaction_validity_range, extra_signatories, datum) == True | |
} | |
test test_bound_check_fails_beneficiary_cannot_only_unlock_if_time_is_not_right() { | |
let owner = #[1] | |
let beneficiary = #[2] | |
let datum = Datum { lock_until: 3, owner, beneficiary } | |
let transaction_validity_range: ValidityRange = | |
Interval { | |
lower_bound: IntervalBound { bound_type: Finite(2), is_inclusive: True }, | |
upper_bound: IntervalBound { bound_type: Finite(10), is_inclusive: True }, | |
} | |
let extra_signatories = [beneficiary] | |
bound_check(transaction_validity_range, extra_signatories, datum) == False | |
} | |
test test_bound_check_fails_beneficiary_can_unlock_if_time_is_exactly_right() { | |
let owner = #[1] | |
let beneficiary = #[2] | |
let datum = Datum { lock_until: 2, owner, beneficiary } | |
let trx_vr: ValidityRange = | |
Interval { | |
lower_bound: IntervalBound { bound_type: Finite(2), is_inclusive: True }, | |
upper_bound: IntervalBound { bound_type: Finite(10), is_inclusive: True }, | |
} | |
let signatories = [beneficiary] | |
bound_check(trx_vr, signatories, datum) == True | |
} | |
test test_bound_check_success_owner_can_always_unlock_even_though_time_is_not_right() { | |
let owner = #[1] | |
let beneficiary = #[2] | |
let datum = Datum { lock_until: 3, owner, beneficiary } | |
let trx_vr: ValidityRange = | |
Interval { | |
lower_bound: IntervalBound { bound_type: Finite(2), is_inclusive: True }, | |
upper_bound: IntervalBound { bound_type: Finite(10), is_inclusive: True }, | |
} | |
let signatories = [owner] | |
bound_check(trx_vr, signatories, datum) == True | |
} | |
test test_bound_check_success_owner_can_unlock_when_time_is_right() { | |
let owner = #[1] | |
let beneficiary = #[2] | |
let datum = Datum { lock_until: 1, owner, beneficiary } | |
let trx_vr: ValidityRange = | |
Interval { | |
lower_bound: IntervalBound { bound_type: Finite(2), is_inclusive: True }, | |
upper_bound: IntervalBound { bound_type: Finite(10), is_inclusive: True }, | |
} | |
let signatories = [owner] | |
bound_check(trx_vr, signatories, datum) == True | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment