diff --git a/aiken.lock b/aiken.lock index c9f842f..bb1e6e5 100644 --- a/aiken.lock +++ b/aiken.lock @@ -35,4 +35,4 @@ requirements = [] source = "github" [etags] -"aiken-lang/fuzz@main" = [{ secs_since_epoch = 1717698560, nanos_since_epoch = 351748000 }, "98cf81aa68f9ccf68bc5aba9be06d06cb1db6e8eff60b668ed5e8ddf3588206b"] +"aiken-lang/fuzz@main" = [{ secs_since_epoch = 1717904342, nanos_since_epoch = 572523000 }, "a8294651f1577c671d580c99c9bc5445ef1fd44e4aa3dde550434a4cbc8d50b6"] diff --git a/lib/fortunav2.ak b/lib/fortunav2.ak index 483b0d9..4cbd050 100644 --- a/lib/fortunav2.ak +++ b/lib/fortunav2.ak @@ -1,9 +1,11 @@ use aiken/builtin use aiken/bytearray -use aiken/dict +use aiken/dict.{Dict} +use aiken/interval.{Finite} use aiken/list use aiken/pairs -use aiken/transaction.{Input, Output, Transaction} as tx +use aiken/transaction.{InlineDatum, + Input, Output, OutputReference, Transaction} as tx use aiken/transaction/credential.{ Address, Inline, ScriptCredential, StakeCredential, } @@ -11,7 +13,9 @@ use aiken/transaction/value.{AssetName, PolicyId} use fortuna.{master_token_name} use fortuna/parameters.{latest_merkle_root, voting_days} use fortuna/types.{State, Statev2} -use fortuna/utils.{get_inline_datum, integer_to_bytes, list_at} +use fortuna/utils.{ + get_inline_datum, integer_to_bytes, list_at, resolve_output_reference, +} use hardfork/hftypes.{HardFork, NftForkAction} pub const big_tuna_prefix = "TUNA" @@ -46,7 +50,9 @@ pub fn genesis_v2(tx, own_policy, fortuna_v1_hash: Data, fork_script_hash: Data) expect Some(v1_miner_ref) = { let input <- list.find(reference_inputs) input.output.value - |> value.quantity_of(fortuna_v1_hash, master_token_name) + |> value.to_dict + |> dict.to_pairs + |> quantity_of(fortuna_v1_hash, master_token_name) |> builtin.equals_integer(1) } @@ -133,8 +139,8 @@ fn mini_loop(assets: Pairs, script_hash: AssetName) -> Int { } } -fn quantity_of( - value: Pairs>, +pub fn quantity_of( + value: Pairs>, own_hash: PolicyId, script_hash: AssetName, ) -> Int { @@ -142,13 +148,102 @@ fn quantity_of( [] -> 0 [Pair(policy, assets), ..rest] -> if policy == own_hash { - mini_loop(assets, script_hash) + mini_loop(assets |> dict.to_pairs, script_hash) } else { quantity_of(rest, own_hash, script_hash) } } } +pub type TunaUpgradeProcess { + Nominated { + script_hash: ByteArray, + for_count: Int, + anti_script_hash: ByteArray, + against_count: Int, + deadline: Int, + } + Mining { + script_hash: ByteArray, + miner_support_count: Int, + block_height_deadline: Int, + } +} + +pub fn vote( + for: Bool, + own_ref: OutputReference, + tx: Transaction, + dat: TunaUpgradeProcess, +) { + let Transaction { inputs, outputs, reference_inputs, validity_range, .. } = tx + + expect Nominated { + script_hash, + deadline, + for_count, + anti_script_hash, + against_count, + } = dat + + let check_hash = + if for { + script_hash + } else { + anti_script_hash + } + + let Output { address: in_address, value: in_value, .. } = + resolve_output_reference(inputs, own_ref) + + expect ScriptCredential(own_hash) = in_address.payment_credential + + expect Finite(upper_range) = validity_range.upper_bound.bound_type + + let Output { address: out_address, value: out_value, datum: out_datum, .. } = + utils.list_at(outputs, 0) + + let votes_in_tx = + count_votes( + reference_inputs, + own_hash, + check_hash, + Some(Inline(ScriptCredential(check_hash))), + ) + + let expected_datum = + InlineDatum( + Nominated { + script_hash, + for_count: if for { + votes_in_tx + } else { + for_count + }, + deadline, + anti_script_hash, + against_count: if for { + against_count + } else { + votes_in_tx + }, + }, + ) + + and { + quantity_of( + in_value |> value.to_dict |> dict.to_pairs, + own_hash, + bytearray.concat(nominated_prefix, script_hash), + ) == 1, + in_address == out_address, + upper_range <= deadline, + value.without_lovelace(in_value) == value.without_lovelace(out_value), + votes_in_tx > for_count, + expected_datum == out_datum, + } +} + pub fn count_votes( reference_inputs: List, own_hash: PolicyId, @@ -161,12 +256,11 @@ pub fn count_votes( let Output { address, value, .. } = input.output if stake_cred == address.stake_credential { - value.quantity_of(value, own_hash, fortuna.token_name) + count_votes( - rest, + quantity_of( + value |> value.to_dict |> dict.to_pairs, own_hash, - script_hash, - stake_cred, - ) + fortuna.token_name, + ) + count_votes(rest, own_hash, script_hash, stake_cred) } else { when value |> value.tokens(own_hash) |> dict.to_pairs is { [Pair(tuna_name, tuna_quantity), Pair(value_script_hash, 1)] -> @@ -189,67 +283,15 @@ pub fn count_votes( } } } -// A little experiment to see how the current optimizer handles the_counter function - -// pub fn the_counter() { -// super_optimize() -// } - -// pub fn super_optimize() { -// repeated_count_votes(10) -// } - -// pub type FakeOutput { -// address: Address, -// value: Pairs>, -// datum: Datum, -// thing: Data, -// } - -// pub type FakeInput { -// thing: OutputReference, -// output: FakeOutput, -// } - -// pub fn repeated_count_votes(a: Int) { -// if a == 0 { -// fn( -// reference_inputs: List, -// own_hash: PolicyId, -// script_hash: AssetName, -// stake_cred: Option, -// ) { -// count_votes(reference_inputs, own_hash, script_hash, stake_cred) -// } -// } else { -// fn( -// reference_inputs: List, -// own_hash: PolicyId, -// script_hash: AssetName, -// stake_cred: Option, -// ) { -// when reference_inputs is { -// [] -> 0 -// [input, ..rest] -> { -// let FakeOutput { address, value, .. } = input.output - -// let x = -// if stake_cred == address.stake_credential { -// quantity_of(value, own_hash, script_hash) -// } else if quantity_of(value, own_hash, script_hash) == 1 { -// quantity_of(value, own_hash, script_hash) -// } else { -// 0 -// } - -// x + repeated_count_votes(a - 1)( -// rest, -// own_hash, -// script_hash, -// stake_cred, -// ) -// } -// } -// } -// } -// } + +pub fn expect_first(self: Pairs, key k: key) -> value { + when self is { + [] -> fail + [Pair(k2, v), ..rest] -> + if k == k2 { + v + } else { + expect_first(rest, k) + } + } +} diff --git a/miner/main.ts b/miner/main.ts index 4ea481d..d8f5a05 100644 --- a/miner/main.ts +++ b/miner/main.ts @@ -7,13 +7,12 @@ import { fromHex, fromText, generateSeedPhrase, - KupmiosV5 as Kupmios, - Translucent, + Kupmios, + Lucid, type Script, toHex, UTxO, - applyDoubleCborEncoding, -} from 'translucent-cardano/index'; +} from 'lucid-cardano'; import fs from 'fs'; import crypto from 'crypto'; import { WebSocket } from 'ws'; @@ -105,7 +104,7 @@ app ); const provider = new Kupmios(kupoUrl, ogmiosUrl); - const lucid = await Translucent.new(provider, preview ? 'Preview' : 'Mainnet'); + const lucid = await Lucid.new(provider, preview ? 'Preview' : 'Mainnet'); lucid.selectWalletFromSeed(fs.readFileSync('seed.txt', { encoding: 'utf8' })); const userPkh = lucid.utils.getAddressDetails(await lucid.wallet.address()).paymentCredential!; @@ -348,7 +347,7 @@ app const unAppliedValidator = readValidator(); const provider = new Kupmios(kupoUrl, ogmiosUrl); - const lucid = await Translucent.new(provider, preview ? 'Preview' : 'Mainnet'); + const lucid = await Lucid.new(provider, preview ? 'Preview' : 'Mainnet'); lucid.selectWalletFromSeed(fs.readFileSync('seed.txt', { encoding: 'utf-8' })); const utxos = await lucid.wallet.getUtxos(); @@ -458,9 +457,8 @@ app encoding: 'utf-8', }); - console.log(forkMerkleRoot); const provider = new Kupmios(kupoUrl, ogmiosUrl); - const lucid = await Translucent.new(provider, preview ? 'Preview' : 'Mainnet'); + const lucid = await Lucid.new(provider, preview ? 'Preview' : 'Mainnet'); lucid.selectWalletFromSeed(fs.readFileSync('seed.txt', { encoding: 'utf-8' })); const utxos = await lucid.wallet.getUtxos(); @@ -481,9 +479,7 @@ app const forkValidatorApplied: Script = { type: 'PlutusV2', - script: applyDoubleCborEncoding( - applyParamsToScript(forkValidator.script, [initOutputRef, fortunaV1Hash]), - ), + script: applyParamsToScript(forkValidator.script, [initOutputRef, fortunaV1Hash]), }; console.log('here22211111'); @@ -496,9 +492,7 @@ app const tunaV2MintApplied: Script = { type: 'PlutusV2', - script: applyDoubleCborEncoding( - applyParamsToScript(fortunaV2Mint.script, [fortunaV1Hash, forkValidatorHash]), - ), + script: applyParamsToScript(fortunaV2Mint.script, [fortunaV1Hash, forkValidatorHash]), }; console.log('here222333'); @@ -507,9 +501,7 @@ app const tunaV2SpendApplied: Script = { type: 'PlutusV2', - script: applyDoubleCborEncoding( - applyParamsToScript(fortunaV2Spend.script, [tunaV2MintAppliedHash]), - ), + script: applyParamsToScript(fortunaV2Spend.script, [tunaV2MintAppliedHash]), }; const tunaV2SpendAppliedHash = lucid.utils.validatorToScriptHash(tunaV2SpendApplied); @@ -537,9 +529,14 @@ app const blockNumber = bn as bigint; + const blockNumberHex = + blockNumber.toString(16).length % 2 === 0 + ? blockNumber.toString(16) + : `0${blockNumber.toString(16)}`; + const masterTokensV2 = { [tunaV2MintAppliedHash + fromText('TUNA') + tunaV2SpendAppliedHash]: 1n, - [tunaV2MintAppliedHash + fromText('COUNTER') + blockNumber.toString(16)]: 1n, + [tunaV2MintAppliedHash + fromText('COUNTER') + blockNumberHex]: 1n, }; const forkLockToken = { @@ -584,7 +581,6 @@ app .attachSpendingValidator(tunaV2SpendApplied) .attachMintingPolicy(tunaV2MintApplied) .attachMintingPolicy(forkValidatorApplied) - .attachWithdrawalValidator(forkValidatorApplied) .readFrom([lastestV1Block]) .registerStake(forkValidatorRewardAddress) .withdraw(forkValidatorRewardAddress, 0n, forkRedeemer) @@ -687,7 +683,7 @@ app .addOption(previewOption) .action(async ({ preview, ogmiosUrl, kupoUrl }) => { const provider = new Kupmios(kupoUrl, ogmiosUrl); - const lucid = await Translucent.new(provider, preview ? 'Preview' : 'Mainnet'); + const lucid = await Lucid.new(provider, preview ? 'Preview' : 'Mainnet'); lucid.selectWalletFromSeed(fs.readFileSync('seed.txt', { encoding: 'utf-8' })); diff --git a/miner/test.ts b/miner/test.ts index 48d8700..b847674 100644 --- a/miner/test.ts +++ b/miner/test.ts @@ -6,16 +6,16 @@ import { Emulator, generatePrivateKey, getAddressDetails, - Translucent, + Lucid, PROTOCOL_PARAMETERS_DEFAULT, Script, TxSigned, -} from 'translucent-cardano'; +} from 'lucid-cardano'; import { printExecutionDetails, readValidator } from './utils'; export type TestContext = { - lucid: Translucent; + lucid: Lucid; emulator: Emulator; minerPaymentCredential?: Credential; minerAddr: string; @@ -31,7 +31,7 @@ export async function test(name: string, fn: (ctx: TestContext) => Promise Promise=12'} - '@dcspark/cardano-multiplatform-lib-browser@3.1.2': - resolution: {integrity: sha512-BDIF33nqEKAGn72+ESD1fLgnWCZJbPWnVKHr0cy7Al2zhvM30rGsajcJNHLPh2XXZQh1H35fBiHHR2uUyZRRpw==} - - '@dcspark/cardano-multiplatform-lib-nodejs@3.1.2': - resolution: {integrity: sha512-bfFI7ljC8El+yj4kG2G7GXcoHHhWJyJKonJ64H3Kcbns4+tHOTy525d1q/u4+hG+G42JPw4Zj5CFIBLv5GLMxg==} - - '@dcspark/cardano-multiplatform-lib-nodejs@5.2.0': - resolution: {integrity: sha512-EwXyOF0MrHGBDFyqjGqxhvpYqq0I4x1RtDW8eubQoePFpHnysiYUf6qYdnP7FRLpuiyubQC96P0lurYkISCBeA==} - - '@emurgo/cardano-message-signing-browser@1.0.1': - resolution: {integrity: sha512-yC4Ymq44WR0bXO1wzxCoyc2W/RD1KSAla0oYhin7IYoVkp2raGp8wt7QNF4pDdNnTcejn5fyPyYY9dL4666H1w==} - - '@emurgo/cardano-message-signing-nodejs@1.0.1': - resolution: {integrity: sha512-PoKh1tQnJX18f8iEr8Jk1KXxKCn9eqaSslMI1pyOJvYRJhQVDLCh0+9YReufjp0oFJIY1ShcrR+4/WnECVZUKQ==} - '@esbuild-plugins/node-globals-polyfill@0.2.3': resolution: {integrity: sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==} peerDependencies: @@ -765,6 +747,17 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@peculiar/asn1-schema@2.3.8': + resolution: {integrity: sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==} + + '@peculiar/json-schema@1.1.12': + resolution: {integrity: sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==} + engines: {node: '>=8.0.0'} + + '@peculiar/webcrypto@1.5.0': + resolution: {integrity: sha512-BRs5XUAwiyCDQMsVA9IDvDa7UBR9gAvPHgugOeGng3YN6vJ9JYonyDc0lNczErgtCWtucjR5N7VtaonboD/ezg==} + engines: {node: '>=10.12.0'} + '@polka/url@1.0.0-next.24': resolution: {integrity: sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==} @@ -889,9 +882,6 @@ packages: '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} - '@sinclair/typebox@0.31.28': - resolution: {integrity: sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ==} - '@sveltejs/adapter-auto@3.1.1': resolution: {integrity: sha512-6LeZft2Fo/4HfmLBi5CucMYmgRxgcETweQl/yQoZo/895K3S9YWYN4Sfm/IhwlIpbJp3QNvhKmwCHbsqQNYQpw==} peerDependencies: @@ -1195,6 +1185,10 @@ packages: asn1.js@4.10.1: resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} + asn1js@3.0.5: + resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==} + engines: {node: '>=12.0.0'} + assert@2.1.0: resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} @@ -1407,12 +1401,6 @@ packages: constants-browserify@1.0.0: resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} - convert-hex@0.1.0: - resolution: {integrity: sha512-w20BOb1PiR/sEJdS6wNrUjF5CSfscZFUp7R9NSlXH8h2wynzXVEPFPJECAnkNylZ+cvf3p7TyRUHggDmrwXT9A==} - - convert-string@0.1.0: - resolution: {integrity: sha512-1KX9ESmtl8xpT2LN2tFnKSbV4NiarbVi8DVb39ZriijvtTklyrT+4dT1wsGMHKD3CJUjXgvJzstm9qL9ICojGA==} - cookie@0.5.0: resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} engines: {node: '>= 0.6'} @@ -1561,6 +1549,10 @@ packages: data-uri-to-buffer@2.0.2: resolution: {integrity: sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==} + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + date-fns@3.6.0: resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==} @@ -1767,10 +1759,6 @@ packages: resolution: {integrity: sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==} engines: {node: '>=6'} - fast-check@3.17.1: - resolution: {integrity: sha512-jIKXJVe6ZO0SpwEgVtEVujTf8TwjI9wMXFJCjsDHUB3RroUbXBgF4kOSz3A7MW0UR26aqsoB8i9O2mjtjERAiA==} - engines: {node: '>=8.0.0'} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1790,6 +1778,10 @@ packages: fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + fflate@0.4.8: resolution: {integrity: sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==} @@ -1818,6 +1810,10 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} @@ -2155,6 +2151,10 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} + lucid-cardano@0.10.7: + resolution: {integrity: sha512-hxJRMWj8VH+SGFqsVMG6T3LSRuxzwFbWOQ5DTGQQUyR/20FL7bjiVL+2ivMJF52tAbFKcwqpRD4fddR7LbqcAw==} + engines: {node: '>=14'} + magic-string@0.25.9: resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} @@ -2264,6 +2264,14 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-forge@1.3.1: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} engines: {node: '>= 6.13.0'} @@ -2571,8 +2579,12 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - pure-rand@6.1.0: - resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + pvtsutils@1.3.5: + resolution: {integrity: sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==} + + pvutils@1.1.3: + resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} + engines: {node: '>=6.0.0'} qs@6.12.0: resolution: {integrity: sha512-trVZiI6RMOkO476zLGaBIzszOdFPnCCXHPG9kn0yuS1uz6xdVxPfZdB3vUig9pxPFDM9BRAgz/YUIVQ1/vuiUg==} @@ -2723,9 +2735,6 @@ packages: resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} hasBin: true - sha256@0.2.0: - resolution: {integrity: sha512-kTWMJUaez5iiT9CcMv8jSq6kMhw3ST0uRdcIWl3D77s6AsLXNXRp3heeqqfu5+Dyfu4hwpQnMzhqHh8iNQxw0w==} - shapefile@0.6.6: resolution: {integrity: sha512-rLGSWeK2ufzCVx05wYd+xrWnOOdSV7xNUW5/XFgx3Bc02hBkpMlrd2F1dDII7/jhWzv0MSyBFh5uJIy9hLdfuw==} hasBin: true @@ -2970,11 +2979,6 @@ packages: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} - translucent-cardano@0.0.6: - resolution: {integrity: sha512-gwVSAxgXq0ySfllTlI/vakz4+PuUm3PmmgXk9+uemFNTIosmHzEsntrsj7Ri1xUWthcuB+AAdJta/BEYqriv+A==} - peerDependencies: - typescript: ^5.0.0 - ts-api-utils@1.2.1: resolution: {integrity: sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==} engines: {node: '>=16'} @@ -3052,12 +3056,6 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - uplc-node@0.0.3: - resolution: {integrity: sha512-4vr7+RzsRmzS2xuMMMsAs7/VgUzq0Y6ha03IKY1yvTwTz4JQcrlOB0wISzpQ0wJUCxXCirsWKh95BJdrC9OT+w==} - - uplc-web@0.0.3: - resolution: {integrity: sha512-eRdaVvVzQJoEQ8+ZuXPeI47BhuJJG68sKsteJ/E21pIPY2/omZn49wZ6gcmevWoQuzEtKdPUjjPDiToUfWm0kw==} - uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -3158,6 +3156,13 @@ packages: vm-browserify@1.1.2: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + + webcrypto-core@1.8.0: + resolution: {integrity: sha512-kR1UQNH8MD42CYuLzvibfakG5Ew5seG85dMMoAM/1LqvckxaF6pUiidLuraIu4V+YCIFabYecUZAW0TuxAoaqw==} + webpack-sources@3.2.3: resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} engines: {node: '>=10.13.0'} @@ -3305,16 +3310,6 @@ snapshots: dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@dcspark/cardano-multiplatform-lib-browser@3.1.2': {} - - '@dcspark/cardano-multiplatform-lib-nodejs@3.1.2': {} - - '@dcspark/cardano-multiplatform-lib-nodejs@5.2.0': {} - - '@emurgo/cardano-message-signing-browser@1.0.1': {} - - '@emurgo/cardano-message-signing-nodejs@1.0.1': {} - '@esbuild-plugins/node-globals-polyfill@0.2.3(esbuild@0.17.19)': dependencies: esbuild: 0.17.19 @@ -3640,6 +3635,24 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 + '@peculiar/asn1-schema@2.3.8': + dependencies: + asn1js: 3.0.5 + pvtsutils: 1.3.5 + tslib: 2.6.2 + + '@peculiar/json-schema@1.1.12': + dependencies: + tslib: 2.6.2 + + '@peculiar/webcrypto@1.5.0': + dependencies: + '@peculiar/asn1-schema': 2.3.8 + '@peculiar/json-schema': 1.1.12 + pvtsutils: 1.3.5 + tslib: 2.6.2 + webcrypto-core: 1.8.0 + '@polka/url@1.0.0-next.24': {} '@rollup/plugin-inject@5.0.5(rollup@2.79.1)': @@ -3731,8 +3744,6 @@ snapshots: '@sinclair/typebox@0.27.8': {} - '@sinclair/typebox@0.31.28': {} - '@sveltejs/adapter-auto@3.1.1(@sveltejs/kit@2.5.5(@sveltejs/vite-plugin-svelte@3.0.2(svelte@4.2.12)(vite@5.2.8(@types/node@20.11.24)))(svelte@4.2.12)(vite@5.2.8(@types/node@20.11.24)))': dependencies: '@sveltejs/kit': 2.5.5(@sveltejs/vite-plugin-svelte@3.0.2(svelte@4.2.12)(vite@5.2.8(@types/node@20.11.24)))(svelte@4.2.12)(vite@5.2.8(@types/node@20.11.24)) @@ -4073,6 +4084,12 @@ snapshots: inherits: 2.0.4 minimalistic-assert: 1.0.1 + asn1js@3.0.5: + dependencies: + pvtsutils: 1.3.5 + pvutils: 1.1.3 + tslib: 2.6.2 + assert@2.1.0: dependencies: call-bind: 1.0.7 @@ -4339,10 +4356,6 @@ snapshots: constants-browserify@1.0.0: {} - convert-hex@0.1.0: {} - - convert-string@0.1.0: {} - cookie@0.5.0: {} cookie@0.6.0: {} @@ -4510,6 +4523,8 @@ snapshots: data-uri-to-buffer@2.0.2: {} + data-uri-to-buffer@4.0.1: {} + date-fns@3.6.0: {} debug@4.3.4: @@ -4818,10 +4833,6 @@ snapshots: exit-hook@2.2.1: {} - fast-check@3.17.1: - dependencies: - pure-rand: 6.1.0 - fast-deep-equal@3.1.3: {} fast-glob@3.3.2: @@ -4842,6 +4853,11 @@ snapshots: dependencies: reusify: 1.0.4 + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + fflate@0.4.8: {} file-entry-cache@6.0.1: @@ -4873,6 +4889,10 @@ snapshots: dependencies: is-callable: 1.2.7 + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + fraction.js@4.3.7: {} fs.realpath@1.0.0: {} @@ -5213,6 +5233,15 @@ snapshots: dependencies: yallist: 4.0.0 + lucid-cardano@0.10.7: + dependencies: + '@peculiar/webcrypto': 1.5.0 + node-fetch: 3.3.2 + ws: 8.16.0 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + magic-string@0.25.9: dependencies: sourcemap-codec: 1.4.8 @@ -5319,6 +5348,14 @@ snapshots: natural-compare@1.4.0: {} + node-domexception@1.0.0: {} + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + node-forge@1.3.1: {} node-gyp-build@4.8.1: {} @@ -5612,7 +5649,11 @@ snapshots: punycode@2.3.1: {} - pure-rand@6.1.0: {} + pvtsutils@1.3.5: + dependencies: + tslib: 2.6.2 + + pvutils@1.1.3: {} qs@6.12.0: dependencies: @@ -5790,11 +5831,6 @@ snapshots: inherits: 2.0.4 safe-buffer: 5.2.1 - sha256@0.2.0: - dependencies: - convert-hex: 0.1.0 - convert-string: 0.1.0 - shapefile@0.6.6: dependencies: array-source: 0.0.4 @@ -6102,20 +6138,6 @@ snapshots: totalist@3.0.1: {} - translucent-cardano@0.0.6(typescript@5.3.3): - dependencies: - '@dcspark/cardano-multiplatform-lib-browser': 3.1.2 - '@dcspark/cardano-multiplatform-lib-nodejs': 3.1.2 - '@emurgo/cardano-message-signing-browser': 1.0.1 - '@emurgo/cardano-message-signing-nodejs': 1.0.1 - '@sinclair/typebox': 0.31.28 - fast-check: 3.17.1 - prettier: 3.2.5 - sha256: 0.2.0 - typescript: 5.3.3 - uplc-node: 0.0.3 - uplc-web: 0.0.3 - ts-api-utils@1.2.1(typescript@5.3.3): dependencies: typescript: 5.3.3 @@ -6176,10 +6198,6 @@ snapshots: escalade: 3.1.1 picocolors: 1.0.0 - uplc-node@0.0.3: {} - - uplc-web@0.0.3: {} - uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -6288,6 +6306,16 @@ snapshots: vm-browserify@1.1.2: {} + web-streams-polyfill@3.3.3: {} + + webcrypto-core@1.8.0: + dependencies: + '@peculiar/asn1-schema': 2.3.8 + '@peculiar/json-schema': 1.1.12 + asn1js: 3.0.5 + pvtsutils: 1.3.5 + tslib: 2.6.2 + webpack-sources@3.2.3: {} webpack-virtual-modules@0.6.1: {} diff --git a/validators/tunav2.ak b/validators/tunav2.ak index 5b2e1a2..92e7dcc 100644 --- a/validators/tunav2.ak +++ b/validators/tunav2.ak @@ -6,7 +6,6 @@ use aiken/interval.{Finite, Interval, IntervalBound} use aiken/list use aiken/math.{pow2} use aiken/merkle_patricia_forestry.{Proof, from_root, insert} -use aiken/pairs use aiken/transaction.{ InlineDatum, Mint, Output, OutputReference, ScriptContext, Spend, Transaction, } as tx @@ -18,10 +17,11 @@ use fortuna/parameters.{ supply_threshold, vote_threshold, } use fortuna/types.{Statev2} -use fortuna/utils.{ - get_inline_datum, integer_to_bytes, quantity_of, resolve_output_reference, +use fortuna/utils.{get_inline_datum, integer_to_bytes, resolve_output_reference} +use fortunav2.{ + Mining, Nominated, TunaUpgradeProcess, expect_first, flip_hash, genesis_v2, + quantity_of, vote, voting_period, } -use fortunav2.{count_votes, flip_hash, genesis_v2, voting_period} use hardfork.{calculate_emission} use hardfork/hftypes.{Lock, NftForkAction} @@ -42,21 +42,6 @@ type TunaSpendAction { TransitionState(Int) } -type TunaUpgradeProcess { - Nominated { - script_hash: ByteArray, - for_count: Int, - anti_script_hash: ByteArray, - against_count: Int, - deadline: Int, - } - Mining { - script_hash: ByteArray, - miner_support_count: Int, - block_height_deadline: Int, - } -} - type Miner { Pkh(ByteArray, Data) Nft { policy: ByteArray, name: ByteArray, output_index: Int, extra: Data } @@ -88,12 +73,12 @@ validator(fortuna_v1_hash: Data, fork_script_hash: Data) { Redeem -> { let Transaction { mint, redeemers, .. } = tx - expect fork_script_hash: ByteArray = fork_script_hash - let withdraw_purpose = - tx.WithdrawFrom(Inline(ScriptCredential(fork_script_hash))) + tx.WithdrawFrom( + Inline(ScriptCredential(fork_script_hash |> builtin.un_b_data)), + ) - expect Some(lock_rdmr) = pairs.get_first(redeemers, withdraw_purpose) + let lock_rdmr = expect_first(redeemers, withdraw_purpose) expect Lock { locking_amount, .. }: NftForkAction = lock_rdmr @@ -231,9 +216,9 @@ validator(fortuna_v1_hash: Data, fork_script_hash: Data) { address.payment_credential == ScriptCredential(own_policy), expected_value == value.without_lovelace(nominated_value), expected_datum == nominated_datum, - !upper_is_inclusive?, - lower_is_inclusive?, - (upper_range - lower_range <= 180000)?, + !upper_is_inclusive, + lower_is_inclusive, + upper_range - lower_range <= 180000, } } @@ -257,8 +242,8 @@ validator(fortuna_v1_hash: Data, fork_script_hash: Data) { // Is from this contract spend_address.payment_credential == ScriptCredential(own_policy), // Has NFT for the nominated contract - value.quantity_of( - spend_value, + quantity_of( + spend_value |> value.to_dict |> dict.to_pairs, own_policy, bytearray.concat(fortunav2.nominated_prefix, script_hash), ) == 1, @@ -280,8 +265,8 @@ validator(fortuna_v1_hash: Data, fork_script_hash: Data) { |> dict.to_pairs |> list.all( fn(pair) { - let Pair(_, quantity) = pair - quantity < 0 + let Pair(name, quantity) = pair + quantity < 0 && name != fortuna.token_name }, ) } @@ -397,147 +382,11 @@ validator(fortuna_v1_hash: Data, fork_script_hash: Data) { rdmr: TunaSpendAction, ctx: ScriptContext, ) -> Bool { + expect ScriptContext { transaction: tx, purpose: Spend(own_ref) } = ctx when rdmr is { - TokenVoteFor -> { - expect ScriptContext { transaction: tx, purpose: Spend(own_ref) } = ctx - - let Transaction { - inputs, - outputs, - reference_inputs, - validity_range, - .. - } = tx - - expect Nominated { - script_hash, - deadline, - for_count, - anti_script_hash, - against_count, - } = dat - - let Output { address: in_address, value: in_value, .. } = - resolve_output_reference(inputs, own_ref) - - expect ScriptCredential(own_hash) = in_address.payment_credential - - expect Interval { - upper_bound: IntervalBound { bound_type: Finite(upper_range), .. }, - .. - } = validity_range - - let Output { - address: out_address, - value: out_value, - datum: out_datum, - .. - } = utils.list_at(outputs, 0) - - let votes_in_tx = - count_votes( - reference_inputs, - own_hash, - script_hash, - Some(Inline(ScriptCredential(script_hash))), - ) - - let expected_datum = - InlineDatum( - Nominated { - script_hash, - for_count: votes_in_tx, - deadline, - anti_script_hash, - against_count, - }, - ) - - and { - quantity_of( - in_value, - own_hash, - bytearray.concat(fortunav2.nominated_prefix, script_hash), - ) == 1, - in_address == out_address, - upper_range <= deadline, - value.without_lovelace(in_value) == value.without_lovelace(out_value), - votes_in_tx > for_count, - expected_datum == out_datum, - } - } - - TokenVoteAgainst -> { - expect ScriptContext { transaction: tx, purpose: Spend(own_ref) } = ctx - - let Transaction { - inputs, - outputs, - reference_inputs, - validity_range, - .. - } = tx - - expect Nominated { - script_hash, - deadline, - for_count, - anti_script_hash, - against_count, - } = dat - - let Output { address: in_address, value: in_value, .. } = - resolve_output_reference(inputs, own_ref) - - expect ScriptCredential(own_hash) = in_address.payment_credential - - expect Interval { - upper_bound: IntervalBound { bound_type: Finite(upper_range), .. }, - .. - } = validity_range - - let Output { - address: out_address, - value: out_value, - datum: out_datum, - .. - } = utils.list_at(outputs, 0) - - let votes_in_tx = - count_votes( - reference_inputs, - own_hash, - anti_script_hash, - Some(Inline(ScriptCredential(anti_script_hash))), - ) - - let expected_datum = - InlineDatum( - Nominated { - script_hash, - for_count, - deadline, - anti_script_hash, - against_count: votes_in_tx, - }, - ) - - and { - quantity_of( - in_value, - own_hash, - bytearray.concat(fortunav2.nominated_prefix, script_hash), - ) == 1, - in_address == out_address, - upper_range <= deadline, - value.without_lovelace(in_value) == value.without_lovelace(out_value), - votes_in_tx > against_count, - expected_datum == out_datum, - } - } + TokenVoteFor -> vote(True, own_ref, tx, dat) + TokenVoteAgainst -> vote(False, own_ref, tx, dat) MinerVoteFor(output_index, block_number) -> { - expect ScriptContext { transaction: tx, purpose: Spend(own_ref) } = ctx - let Transaction { inputs, outputs, mint, .. } = tx expect Mining { @@ -574,7 +423,9 @@ validator(fortuna_v1_hash: Data, fork_script_hash: Data) { block_height_deadline < block_number, mint |> from_minted_value - |> value.quantity_of( + |> value.to_dict + |> dict.to_pairs + |> quantity_of( own_hash, bytearray.concat( fortunav2.counter_prefix, @@ -588,8 +439,6 @@ validator(fortuna_v1_hash: Data, fork_script_hash: Data) { } TransitionState(block_number) -> { - expect ScriptContext { transaction: tx, purpose: Spend(own_ref) } = ctx - let Transaction { inputs, reference_inputs, @@ -605,13 +454,12 @@ validator(fortuna_v1_hash: Data, fork_script_hash: Data) { expect ScriptCredential(own_script_hash) = in_address.payment_credential - expect IntervalBound { bound_type: Finite(lower_bound), .. } = - validity_range.lower_bound + expect Finite(lower_bound) = validity_range.lower_bound.bound_type let common_checks = fn(script_hash) { and { - value.quantity_of( - in_value, + quantity_of( + in_value |> value.to_dict |> dict.to_pairs, own_script_hash, bytearray.concat(fortunav2.nominated_prefix, script_hash), ) == 1, @@ -681,6 +529,8 @@ validator(fortuna_v1_hash: Data, fork_script_hash: Data) { and { mint |> from_minted_value + |> value.to_dict + |> dict.to_pairs |> quantity_of( own_script_hash, bytearray.concat( @@ -704,8 +554,8 @@ validator(fortuna_v1_hash: Data, fork_script_hash: Data) { and { miner_support_count >= epoch_number * miner_threshold / 1000, { - expect Some(upgrade_rdmr) = - pairs.get_first(redeemers, Mint(own_script_hash)) + let upgrade_rdmr = + expect_first(redeemers, Mint(own_script_hash)) expect FinalizeNomination { .. }: TunaAction = upgrade_rdmr @@ -716,6 +566,8 @@ validator(fortuna_v1_hash: Data, fork_script_hash: Data) { miner_support_count < epoch_number * miner_threshold / 1000, mint |> from_minted_value + |> value.to_dict + |> dict.to_pairs |> quantity_of( own_script_hash, bytearray.concat( @@ -871,6 +723,8 @@ validator(tunav2_minting_policy: ByteArray) { Nft { policy: nft_policy, name: nft_name, output_index, .. } -> { let quantity = utils.list_at(outputs, output_index).value + |> value.to_dict + |> dict.to_pairs |> quantity_of(nft_policy, nft_name) quantity == 1 @@ -888,7 +742,7 @@ validator(tunav2_minting_policy: ByteArray) { }, // Spend(4) requirement: Input has master token (quantity_of( - in_value, + in_value |> value.to_dict |> dict.to_pairs, tunav2_minting_policy, bytearray.concat(fortunav2.big_tuna_prefix, own_script_hash), ) == 1)?, @@ -917,8 +771,7 @@ validator(tunav2_minting_policy: ByteArray) { Upgrade -> { let Transaction { redeemers, .. } = ctx.transaction - expect Some(upgrade_rdmr) = - pairs.get_first(redeemers, Mint(tunav2_minting_policy)) + let upgrade_rdmr = expect_first(redeemers, Mint(tunav2_minting_policy)) expect FinalizeNomination { .. }: TunaAction = upgrade_rdmr