Skip to content

Commit

Permalink
feat: add in intention validation and continue working on spend
Browse files Browse the repository at this point in the history
  • Loading branch information
MicroProofs committed Dec 23, 2023
1 parent 808f66b commit 82c6f91
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 9 deletions.
20 changes: 19 additions & 1 deletion lib/composable-account/utils.ak
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use aiken/builtin
use aiken/dict.{Dict}
use aiken/hash.{Blake2b_224, Hash}
use aiken/list
use aiken/transaction/credential.{VerificationKey}
use aiken/transaction/credential.{VerificationKey, verify_signature}

pub fn list_at_speed(list: List<a>, index: Int) -> a {
if index <= 9 {
Expand Down Expand Up @@ -58,3 +58,21 @@ pub fn validate_keys_present(
)
}
}

pub fn validate_signatures(
owners: List<(Hash<Blake2b_224, VerificationKey>, VerificationKey)>,
signatures: List<ByteArray>,
message: ByteArray,
) {
when owners is {
[] -> True
[owner, ..rest_owners] -> {
expect [signature, ..rest_signatures] = signatures

and {
verify_signature(builtin.snd_pair(owner), message, signature),
validate_signatures(rest_owners, rest_signatures, message),
}
}
}
}
199 changes: 191 additions & 8 deletions validators/account.ak
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use aiken/builtin
use aiken/bytearray
use aiken/dict.{Dict}
use aiken/hash.{Blake2b_224, Hash}
use aiken/hash.{Blake2b_224, Hash, blake2b_256}
use aiken/interval.{Finite, Interval, IntervalBound}
use aiken/list
use aiken/time.{PosixTime}
Expand All @@ -16,7 +17,7 @@ use aiken/transaction/credential.{
VerificationKey,
}
use aiken/transaction/value.{
AssetName, PolicyId, from_minted_value, quantity_of, tokens,
AssetName, PolicyId, Value, from_minted_value, quantity_of, tokens,
}
use composable_account/utils.{list_at, list_at_speed, validate_keys_present}

Expand Down Expand Up @@ -65,7 +66,6 @@ type State {

type Intention {
condition: IntentionCondition,
/// The Ints in value leaving should always be negative
value_leaving: List<(PolicyId, AssetName, Int)>,
nonce: OutputReference,
/// User to track ordering number. Useful for dexes to ensure
Expand Down Expand Up @@ -212,30 +212,35 @@ validator {

expect ControlState { owner }: State = datum

// TODO: switch acc to be (intentions, inputs)
// to reduce the inputs we sift through for each account
expect [account_intention_list, ..other_account_intentions] =
acc

let account_value_used_nonces =
let (merged_values, nonce_list) =
list.foldl(
inputs,
(value.zero(), 0),
(value.zero(), []),
fn(input: Input, acc) {
let Input { output, .. } = input
if output.address == address {
let Output { value, datum, .. } = output

expect InlineDatum(dat) = datum

let (acc_value, unused_nonces) = acc
let (acc_value, nonce_list) = acc

let combined_value = value.merge(acc_value, value)

let non_datum_return =
fn() { (combined_value, unused_nonces) }
fn() { (combined_value, nonce_list) }

builtin.choose_data(
dat,
(combined_value, unused_nonces + 1),
(
combined_value,
[input.output_reference, ..nonce_list],
),
non_datum_return(),
non_datum_return(),
non_datum_return(),
Expand All @@ -247,6 +252,36 @@ validator {
},
)

let (value_leaving_account, combined_intentions) =
list.foldl(
account_intention_list,
(value.zero(), []),
fn(data_envelope, acc) {
let DataEnvelope {
prefix,
intention,
postfix,
signatures,
} = data_envelope

let message =
intention
|> builtin.serialise_data()
|> bytearray.concat(prefix, _)
|> bytearray.concat(postfix)
|> blake2b_256()

expect
utils.validate_signatures(
owner.owner_list,
signatures,
message,
)

todo
},
)

todo
} else {
acc
Expand Down Expand Up @@ -475,3 +510,151 @@ fn validate_account_spend(
list.foldl(inputs, True, inputs_check),
}
}

fn validate_intention_conditions(
conditions: List<IntentionCondition>,
is_and: Bool,
with: fn(TxCondition) -> Bool,
) {
when conditions is {
[] -> is_and
[cond, ..rest_conds] ->
when cond is {
Is(tx_cond) -> {
let condition_check = with(tx_cond)
if is_and == condition_check {
validate_intention_conditions(rest_conds, is_and, with)
} else {
condition_check
}
}

Not(tx_cond) -> {
let condition_check = !with(tx_cond)
if is_and == condition_check {
validate_intention_conditions(rest_conds, is_and, with)
} else {
condition_check
}
}
And(more_conds) -> {
let condition_check =
validate_intention_conditions(more_conds, True, with)
if is_and == condition_check {
validate_intention_conditions(rest_conds, is_and, with)
} else {
condition_check
}
}
Or(more_conds) -> {
let condition_check =
validate_intention_conditions(more_conds, False, with)
if is_and == condition_check {
validate_intention_conditions(rest_conds, is_and, with)
} else {
condition_check
}
}
}
}
}

fn validate_inputs(
input_conditions: List<List<OutputCondition>>,
inputs: List<Input>,
) -> Bool {
when input_conditions is {
[] -> True
[x, ..xs] -> has_valid_input(x, inputs) && validate_inputs(xs, inputs)
}
}

fn validate_outputs(
output_conditions: List<List<OutputCondition>>,
outputs: List<Output>,
) -> Bool {
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 inner_check = build_up_validation(rest_conds)

when cond is {
HasAddress(address) ->
fn(output: Output) {
output.address == address && inner_check(output)
}

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)
}
}

HasPC(cred) ->
fn(output: Output) {
output.address.payment_credential == cred && inner_check(output)
}

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
}
}
HasDatumHash(datum_hash) -> {
let datum_hash = DatumHash(datum_hash)
fn(output: Output) {
output.datum == datum_hash && inner_check(output)
}
}
_ -> todo
}
}
}
}

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
[path, ..rest_path] -> {
let list_of_fields = builtin.snd_pair(builtin.un_constr_data(datum))
when list.at(list_of_fields, path) is {
None -> False
Some(x) -> traverse_datum(x, field, rest_path)
}
}
}
}

0 comments on commit 82c6f91

Please sign in to comment.