Skip to content

Commit

Permalink
feat: improve versatility by combining input_conditions into a single…
Browse files Browse the repository at this point in the history
… function instead of finding input offset
  • Loading branch information
MicroProofs committed Jun 17, 2023
1 parent 06f6310 commit dc2fd07
Showing 1 changed file with 73 additions and 87 deletions.
160 changes: 73 additions & 87 deletions validators/stakinator3000.ak
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ use aiken/interval.{Finite, Interval, IntervalBound}
use aiken/list
use aiken/time.{PosixTime}
use aiken/transaction.{
InlineDatum, Input, Mint, Output, OutputReference, Publish, ScriptContext,
Spend, Transaction, WithdrawFrom,
DatumHash, InlineDatum, Input, Mint, Output, OutputReference, Publish,
ScriptContext, Spend, Transaction, WithdrawFrom,
}
use aiken/transaction/certificate.{
CredentialDelegation, CredentialDeregistration, CredentialRegistration,
Expand All @@ -23,12 +23,13 @@ type Owner {
Receipt(AssetName)
}

type InputCondition {
type OutputCondition {
HasAddress(Address)
HasToken(PolicyId, AssetName, Int)
HasDatum(Data)
HasPkh(Credential)
HasField { field_data: Data, field_path: List<Int> }
HasHash(ByteArray)
}

type TxCondition {
Expand All @@ -38,8 +39,9 @@ type TxCondition {
/// Please note the `outer list` in InputsCondition could all be satisfied by a single input.
/// The checks are per all inputs not a unique input
/// The `inner list` acts on a single input ensuring the input meets all conditions via &&
InputsCondition(List<List<InputCondition>>)
ReferenceInputsCondition(List<List<InputCondition>>)
InputsCondition(List<List<OutputCondition>>)
ReferenceInputsCondition(List<List<OutputCondition>>)
OutputsCondition(List<List<OutputCondition>>)
StakesTo(PoolId)
}

Expand Down Expand Up @@ -162,6 +164,9 @@ validator(state_token: NFTAsset) {
ReferenceInputsCondition(ref_conditions) ->
validate_inputs(ref_conditions, reference_inputs)

OutputsCondition(out_conditions) ->
validate_outputs(out_conditions, outputs)

StakesTo(pool) -> {
let expected_cert =
CredentialDelegation {
Expand Down Expand Up @@ -373,7 +378,7 @@ fn validate_stake_conditions(
}

fn validate_inputs(
input_conditions: List<List<InputCondition>>,
input_conditions: List<List<OutputCondition>>,
inputs: List<Input>,
) -> Bool {
when input_conditions is {
Expand All @@ -382,53 +387,82 @@ fn validate_inputs(
}
}

fn has_valid_input(
input_conds: List<InputCondition>,
inputs: List<Input>,
fn validate_outputs(
output_conditions: List<List<OutputCondition>>,
outputs: List<Output>,
) -> Bool {
when input_conds is {
when output_conditions is {
[] -> True
[x, ..xs] -> {
let combined_conditions = build_up_validation(x)
list.any(outputs, combined_conditions) && validate_outputs(xs, outputs)
}
}
}

fn build_up_validation(conds: List<OutputCondition>) -> fn(Output) -> Bool {
when conds is {
[] ->
fn(_output: Output) { True }
[cond, ..rest_conds] -> {
let input_condition_runner =
when cond is {
HasAddress(address) ->
fn(input: Input) { input.output.address == address }
let inner_check = build_up_validation(rest_conds)

HasToken(t_policy, t_name, t_amount) ->
fn(input: Input) {
( input.output.value |> quantity_of(t_policy, t_name) ) == t_amount
}
HasDatum(datum) -> {
let inline_datum = InlineDatum(datum)
fn(input: Input) { input.output.datum == inline_datum }
when cond is {
HasAddress(address) ->
fn(output: Output) {
output.address == address && inner_check(output)
}

HasPkh(cred) ->
fn(input: Input) { input.output.address.payment_credential == cred }

HasField { field_data, field_path } ->
fn(input: Input) {
when input.output.datum is {
InlineDatum(dat) -> traverse_datum(dat, field_data, field_path)
_ -> False
}
}
HasToken(t_policy, t_name, t_amount) ->
fn(output: Output) {
( output.value |> quantity_of(t_policy, t_name) ) == t_amount && inner_check(
output,
)
}
HasDatum(datum) -> {
let inline_datum = InlineDatum(datum)
fn(output: Output) {
output.datum == inline_datum && inner_check(output)
}
}

let inputs_offset = find_input_offset(inputs, input_condition_runner)
HasPkh(cred) ->
fn(output: Output) {
output.address.payment_credential == cred && inner_check(output)
}

when inputs_offset is {
[] -> False
[input, ..rest_inputs] ->
validate_other_conditions(rest_conds, input) || has_valid_input(
input_conds,
rest_inputs,
)
HasField { field_data, field_path } ->
fn(output: Output) {
when output.datum is {
InlineDatum(dat) ->
traverse_datum(dat, field_data, field_path) && inner_check(
output,
)
_ -> False
}
}
HasHash(datum_hash) -> {
let datum_hash = DatumHash(datum_hash)
fn(output: Output) {
output.datum == datum_hash && inner_check(output)
}
}
}
}
}
}

fn has_valid_input(
input_conds: List<OutputCondition>,
inputs: List<Input>,
) -> Bool {
let combined_conditions = build_up_validation(input_conds)
let input_condition_runner =
fn(input: Input) { combined_conditions(input.output) }

list.any(inputs, input_condition_runner)
}

fn traverse_datum(datum: Data, field: Data, field_path: List<Int>) -> Bool {
when field_path is {
[] -> datum == field
Expand All @@ -442,54 +476,6 @@ fn traverse_datum(datum: Data, field: Data, field_path: List<Int>) -> Bool {
}
}

fn find_input_offset(
inputs: List<Input>,
with: fn(Input) -> Bool,
) -> List<Input> {
when inputs is {
[] ->
[]
[input, ..rest] ->
if with(input) {
inputs
} else {
find_input_offset(rest, with)
}
}
}

fn validate_other_conditions(
input_conds: List<InputCondition>,
input: Input,
) -> Bool {
when input_conds is {
[] -> True
[cond, ..rest] -> {
let is_valid =
when cond is {
HasAddress(address) -> input.output.address == address

HasToken(t_policy, t_name, t_amount) ->
( input.output.value |> quantity_of(t_policy, t_name) ) == t_amount
HasDatum(datum) -> {
let inline_datum = InlineDatum(datum)
input.output.datum == inline_datum
}

HasPkh(cred) -> input.output.address.payment_credential == cred

HasField { field_data, field_path } ->
when input.output.datum is {
InlineDatum(dat) -> traverse_datum(dat, field_data, field_path)
_ -> False
}
}

is_valid && validate_other_conditions(rest, input)
}
}
}

// Check each minted token name is in the expected list, has quantity of 1,
// and has a corresponding ouput with datum containing token name.
// Alternatively allow for token burning
Expand Down

0 comments on commit dc2fd07

Please sign in to comment.