Skip to content

Instantly share code, notes, and snippets.

@matiwinnetou
Created January 4, 2023 14:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matiwinnetou/fcdf660fbc3467df6237f612c1f16540 to your computer and use it in GitHub Desktop.
Save matiwinnetou/fcdf660fbc3467df6237f612c1f16540 to your computer and use it in GitHub Desktop.
Aiken's vesting contract
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