From 30ac322031582ccb19c365c6d0bcd82845fa6529 Mon Sep 17 00:00:00 2001 From: microproofs Date: Tue, 19 Dec 2023 23:02:30 -0500 Subject: [PATCH] feat: finish incorporating one shot policy into stakinator --- lib/staking/utils.ak | 20 +++++++ validators/one_shot.ak | 23 -------- validators/stakinator3000.ak | 110 ++++++++++++++++++++--------------- 3 files changed, 82 insertions(+), 71 deletions(-) create mode 100644 lib/staking/utils.ak delete mode 100644 validators/one_shot.ak diff --git a/lib/staking/utils.ak b/lib/staking/utils.ak new file mode 100644 index 0000000..3ec76c6 --- /dev/null +++ b/lib/staking/utils.ak @@ -0,0 +1,20 @@ +use aiken/builtin +use aiken/cbor + +test maps_serialize() { + let thing = + [(0, 1), (1, 2), (8, 88)] + let diag = cbor.diagnostic(thing) + + let other_diag = cbor.diagnostic(builtin.serialise_data(thing)) + + trace @"first diag" + + trace diag + + trace @"second diag" + + trace other_diag + + True +} diff --git a/validators/one_shot.ak b/validators/one_shot.ak deleted file mode 100644 index e3b9ac2..0000000 --- a/validators/one_shot.ak +++ /dev/null @@ -1,23 +0,0 @@ -use aiken/dict -use aiken/list -use aiken/transaction.{Mint, OutputReference, ScriptContext, Transaction} -use aiken/transaction/value.{from_minted_value, tokens} - -validator(utxo_ref: OutputReference) { - fn oneshot(_rdr: Data, ctx: ScriptContext) -> Bool { - let ScriptContext { transaction, purpose } = ctx - - expect Mint(own_policy) = purpose - let asset_name = "stakinator" - - let Transaction { inputs, mint, .. } = transaction - - expect [(mint_asset, mint_amount)] = - mint |> from_minted_value |> tokens(own_policy) |> dict.to_list - - mint_asset == asset_name && mint_amount == 1 && list.any( - inputs, - fn(input) { input.output_reference == utxo_ref }, - ) - } -} diff --git a/validators/stakinator3000.ak b/validators/stakinator3000.ak index 002d455..006cde3 100644 --- a/validators/stakinator3000.ak +++ b/validators/stakinator3000.ak @@ -69,7 +69,7 @@ type NFTAsset { asset: AssetName, } -validator(state_token: NFTAsset) { +validator(init_input: OutputReference) { /// remember `rdr` still needs to be wrapped in a index 1 constr for multivalidator purposes fn conditional_spend(state: State, rdr: Data, ctx: ScriptContext) -> Bool { let ScriptContext { transaction, purpose } = ctx @@ -102,8 +102,6 @@ validator(state_token: NFTAsset) { let State { owner, redelegation_condition, .. } = state - let NFTAsset { policy: nft_policy, asset: nft_asset } = state_token - let Interval { lower_bound, upper_bound } = validity_range let IntervalBound { bound_type, is_inclusive: upper_inclusive } = @@ -117,7 +115,7 @@ validator(state_token: NFTAsset) { expect Finite(lower) = bound_type // Checks current owner is valid - expect True = + expect when owner is { // An owner can be a public key, a spent NFT, or a burned receipt token that was minted by this script PKH(signer) -> list.has(extra_signatories, signer) @@ -125,26 +123,20 @@ validator(state_token: NFTAsset) { // that we check is spent NFT(policy, token_name) -> { expect nft_input_owner: OutputReference = rdr - expect Some(nft_input) = list.find( inputs, fn(input) { input.output_reference == nft_input_owner }, ) - quantity_of(nft_input.output.value, policy, token_name) == 1 } - Receipt(token_name) -> { let x = mint |> from_minted_value |> quantity_of(own_pkh, token_name) - x == -1 } } - - // Check input contains parameterized NFT - expect True = quantity_of(in_value, nft_policy, nft_asset) == 1 - + // Check input contains init NFT + expect quantity_of(in_value, own_pkh, "") == 1 // Validates non-recursive conditions let tx_condition_runner = fn(cond: TxCondition) -> Bool { @@ -178,7 +170,7 @@ validator(state_token: NFTAsset) { } } - expect True = + expect when redelegation_condition is { Is(tx_cond) -> tx_condition_runner(tx_cond) Not(tx_cond) -> !tx_condition_runner(tx_cond) @@ -187,17 +179,12 @@ validator(state_token: NFTAsset) { Or(conds) -> validate_stake_conditions(conds, False, tx_condition_runner) } - // find output with same address and nft state token expect Some(Output { datum: InlineDatum(out_datum), .. }) = list.find( outputs, fn(output) { - output.address == in_address && quantity_of( - output.value, - nft_policy, - nft_asset, - ) == 1 + output.address == in_address && quantity_of(output.value, own_pkh, "") == 1 }, ) @@ -222,35 +209,67 @@ validator(state_token: NFTAsset) { .. } = transaction - let NFTAsset { policy: nft_policy, asset: nft_asset } = state_token - when purpose is { Mint(own_policy) -> { + let minted_under_policy = + mint + |> from_minted_value + |> tokens(own_policy) + |> dict.to_list + expect [first_input, ..] = inputs - let asset_name = first_input.output_reference |> builtin.serialise_data - - mint - |> from_minted_value - |> tokens(own_policy) - |> dict.to_list - |> check_mint_to_output_datum( - outputs, - asset_name, - state_token, - ScriptCredential(own_policy), - ) + let asset_name = + first_input.output_reference + |> builtin.serialise_data + |> builtin.blake2b_256 + + when minted_under_policy is { + // impossible case + [] -> False + [(minted_asset_name, 1), ..rest] if minted_asset_name == "" -> { + expect [output, ..] = outputs + + let Output { address, value, .. } = output + + and { + address.payment_credential == ScriptCredential(own_policy), + value.quantity_of(value, own_policy, "") == 1, + list.any( + inputs, + fn(input) { input.output_reference == init_input }, + ), + check_mint_to_output_datum( + rest, + outputs, + asset_name, + ScriptCredential(own_policy), + own_policy, + ), + } + } + + all -> + check_mint_to_output_datum( + all, + outputs, + asset_name, + ScriptCredential(own_policy), + own_policy, + ) + } } WithdrawFrom(cred) -> { expect Inline(cred) = cred + expect ScriptCredential(own_policy) = cred let find_predicate = fn(input: Input) { let output = input.output output.address.payment_credential == cred && ( - output.value |> quantity_of(nft_policy, nft_asset) + output.value |> quantity_of(own_policy, "") ) == 1 } @@ -308,6 +327,7 @@ validator(state_token: NFTAsset) { delegator, } -> { expect Inline(cred) = delegator + expect ScriptCredential(own_policy) = cred // Checks for state token is spent list.any( inputs, @@ -315,13 +335,11 @@ validator(state_token: NFTAsset) { let output = input.output output.address.payment_credential == cred && ( - output.value |> quantity_of(nft_policy, nft_asset) + output.value |> quantity_of(own_policy, "") ) == 1 }, ) } - - CredentialRegistration(_) -> True _ -> False } _ -> False @@ -484,25 +502,22 @@ fn check_mint_to_output_datum( minted_assets: List<(ByteArray, Int)>, outputs: List, expected_asset: ByteArray, - expected_nft: NFTAsset, - validator_cred: PaymentCredential, + validator_cred: Credential, + validator_policy: PolicyId, ) -> Bool { when minted_assets is { [] -> True [(minted_asset_name, quantity), ..rest_assets] -> if quantity == 1 { - expect True = expected_asset == minted_asset_name - - expect True = + expect expected_asset == minted_asset_name + expect list.any( outputs, fn(output) { let Output { address, value, datum, .. } = output - if address.payment_credential == validator_cred { // Check for nft present - let NFTAsset { policy, asset } = expected_nft - if quantity_of(value, policy, asset) == 1 { + if quantity_of(value, validator_policy, "") == 1 { // Check for receipt datum for minted receipt token expect InlineDatum(data) = datum expect OwnerCheck { owner: Receipt(asset), .. }: OwnerCheck = @@ -516,21 +531,20 @@ fn check_mint_to_output_datum( } }, ) - check_mint_to_output_datum( rest_assets, outputs, expected_asset, - expected_nft, validator_cred, + validator_policy, ) } else if quantity == -1 { check_mint_to_output_datum( rest_assets, outputs, expected_asset, - expected_nft, validator_cred, + validator_policy, ) } else { False