diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 87bc26ea..b92d4559 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -14,7 +14,7 @@ jobs: submodules: 'recursive' - uses: actions/setup-node@v1 with: - node-version: '12.18.1' + node-version: '18' - name: Cache node modules uses: actions/cache@v1 with: @@ -50,7 +50,7 @@ jobs: submodules: 'recursive' - uses: actions/setup-node@v1 with: - node-version: '12.18.1' + node-version: '18' - name: Cache node modules uses: actions/cache@v1 with: diff --git a/.nvmrc b/.nvmrc index 46a81848..3f430af8 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v12.18.1 +v18 diff --git a/README.md b/README.md index 5c0dae0f..4711a689 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,14 @@ This is a library, written in Rust, for serialization & deserialization of data - [Browser (chrome/firefox) WASM package](https://www.npmjs.com/package/@emurgo/cardano-serialization-lib-browser) - [Browser (pure JS - no WASM) ASM.js package](https://www.npmjs.com/package/@emurgo/cardano-serialization-lib-asmjs) +##### NPM packages with GC support +Note: This package uses [weak references flag from wasm-bindgen](https://rustwasm.github.io/wasm-bindgen/reference/weak-references.html). +It uses `FinalizationRegistry` under the hood to automatically call "free" for each CSL struct when it is no longer needed. However, use this feature with caution as it may have unpredictable behaviors. +- [NodeJS WASM package with GC](https://www.npmjs.com/package/@emurgo/cardano-serialization-lib-nodejs-gc) +- [Browser (chrome/firefox) WASM package with GC](https://www.npmjs.com/package/@emurgo/cardano-serialization-lib-browser-gc) +- [Browser (pure JS - no WASM) ASM.js package with GC](https://www.npmjs.com/package/@emurgo/cardano-serialization-lib-asmjs-gc) + + ##### Rust crates - [cardano-serialization-lib](https://crates.io/crates/cardano-serialization-lib) diff --git a/build-and-test.sh b/build-and-test.sh index ecd5f31a..6120323a 100644 --- a/build-and-test.sh +++ b/build-and-test.sh @@ -1 +1 @@ -. test.sh && npm run rust:build-nodejs \ No newline at end of file +. test.sh && npm run rust:build-nodejs diff --git a/doc/getting-started/generating-transactions.md b/doc/getting-started/generating-transactions.md index 989ea30b..4767a29f 100644 --- a/doc/getting-started/generating-transactions.md +++ b/doc/getting-started/generating-transactions.md @@ -117,3 +117,53 @@ const transaction = CardanoWasm.Transaction.new( ## A note on fees Fees is Cardano Shelley are based directly on the size of the final encoded transaction. It is important to note that a transaction created by this library potentially can vary in size compared to one built with other tools. This is because transactions, as well as other Cardano Shelley structures, are encoded using [CBOR](https://cbor.io/) a binary JSON-like encoding. Due to arrays and maps allowing both definite or indefinite length encoding in the encoded transaction created by the library, the size can vary. This is because definite encoding consists of a tag containing the size of the array/map which can be 1 or more bytes long depending on the number of elements the size of the encoded structure, while indefinite length encoding consists of a 1 byte starting tag and after all elements are listed, a 1 byte ending tag. These variances should should only be a couple bytes and cardano-serialization-lib uses definite encoding which is the same length or smaller for any reasonable sized transaction. + +## UTxO Selection + +The `TransactionBuilder` struct allows you to manually enter inputs and outputs to create a valid transaction, of course, this means that you'll have to calculate things such as fees, change outputs, and perform UTxO selection on the inputs yourself. + +The `TransactionBuilder` struct has some exposed APIs that may be helpful in performing these actions. Namely the `builder.add_inputs_from_and_change` function. The function first looks at the outputs that already exists in the `builder`, then attempts to balance the transaction using `inputs` that are given in the arguments of the function. The function will set `inputs`, `outputs` and `fees` in the `builder`. + +The reason why all 3 have to be set within a single function, is because unfortunately, they all affect each other. Performing UTxO selection on some given `outputs` may result in some extra `change output`, which maybe increase the `fees` needed, which in turn, may change the `inputs` required, thereby changing what's required in the `change output`, and so on. + +Further complications arise due to the `minimum UTxO value` requirements on the Cardano network, which is tied to the size of the output. Tokens significantly increase the size of each output, and so any inputs with tokens complicates UTxO selection somewhat. + +`builder.add_inputs_from_and_change` should correctly perform UTxO selection, add these into the `builder.inputs`, add one extra output for `change`, and set the `builder.fee`. + +## Example Code +Here is a quick example of how it might be used + +```javascript +const txBuilder = wasm.TransactionBuilder.new( + wasm.TransactionBuilderConfigBuilder.new() + .fee_algo(wasm.LinearFee.new(wasm.BigNum.from_str('44'), wasm.BigNum.from_str('155381'))) + .coins_per_utxo_word(wasm.BigNum.from_str('34482')) + .pool_deposit(wasm.BigNum.from_str('500000000')) + .key_deposit(wasm.BigNum.from_str('2000000')) + .ex_unit_prices( + wasm.ExUnitPrices.new( + wasm.UnitInterval.new(wasm.BigNum.from_str('577'), wasm.BigNum.from_str('10000')), + wasm.UnitInterval.new(wasm.BigNum.from_str('721'), wasm.BigNum.from_str('10000000')), + ), + ) + .max_value_size(5000) + .max_tx_size(16384) + .build(), + ) +const utxos = [ + "82825820731224c9d2bc3528578009fec9f9e34a67110aca2bd4dde0f050845a2daf660d0082583900436075347d6a452eba4289ae345a8eb15e73eb80979a7e817d988fc56c8e2cfd5a9478355fa1d60759f93751237af3299d7faa947023e493821a001deabfa1581c9a5e0d55cdf4ce4e19c8acbff7b4dafc890af67a594a4c46d7dd1c0fa14001", + "82825820a04996d5ef87fdece0c74625f02ee5c1497a06e0e476c5095a6b0626b295074a00825839001772f234940519e71318bb9c5c8ad6eacfe8fd91a509050624e3855e6c8e2cfd5a9478355fa1d60759f93751237af3299d7faa947023e4931a0016e360" +] +const output = wasm.TransactionOutput.new(wasm.Address.from_bech32("addr_test1qppkqaf5044y2t46g2y6udz636c4uultszte5l5p0kvgl3tv3ck06k550q64lgwkqavljd63yda0x2va074fguprujfsjre4xh"), wasm.Value.new(wasm.BigNum.from_str("969750"))) +txBuilder.add_output(output) + +const wasmUtxos = wasm.TransactionUnspentOutputs.new(); +for (let i = 0; i < utxos.length; i++) { + wasmUtxos.add(wasm.TransactionUnspentOutput.from_hex(utxos[i])); + } +const wasmChangeConfig = wasm.ChangeConfig.new(wasm.Address.from_bech32("addr_test1qqzf7fhgm0gf370ngxgpskg5c3kgp2g0u4ltxlrmsvumaztv3ck06k550q64lgwkqavljd63yda0x2va074fguprujfs43mc83")) + +txBuilder.add_inputs_from_and_change(wasmUtxos, wasm.CoinSelectionStrategyCIP2.LargestFirstMultiAsset, wasmChangeConfig) + +const transaction = txBuilder.build_tx() +``` \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 008d6c6d..006f489f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,395 +1,337 @@ { "name": "cardano-serialization-lib", - "version": "11.5.0", - "lockfileVersion": 1, + "version": "12.0.0", + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@apidevtools/json-schema-ref-parser": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz", - "integrity": "sha512-GBD2Le9w2+lVFoc4vswGI/TjkNIZSVp7+9xPf+X3uidBfWnAeUWmquteSyt0+VCrhNMWj/FTABISQrD3Z/YA+w==", - "dev": true, - "requires": { - "@jsdevtools/ono": "^7.1.3", - "@types/json-schema": "^7.0.6", - "call-me-maybe": "^1.0.1", - "js-yaml": "^4.1.0" + "packages": { + "": { + "name": "cardano-serialization-lib", + "version": "12.0.0", + "hasInstallScript": true, + "license": "MIT", + "devDependencies": { + "flowgen": "1.21.0", + "husky": "^9.0.11", + "json-schema-to-typescript": "^14.0.5", + "rimraf": "^5.0.7" } }, - "@babel/code-frame": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.3.tgz", - "integrity": "sha512-fDx9eNW0qz0WkUeqL6tXEXzVlPh6Y5aCDEZesl0xBGA8ndRukX91Uk44ZqnkECp01NAZUdCAl+aiQNGi0k88Eg==", + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "11.6.4", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.6.4.tgz", + "integrity": "sha512-9K6xOqeevacvweLGik6LnZCb1fBtCOSIWQs8d096XGeqoLKC33UVMGz9+77Gw44KvbH4pKcQPWo4ZpxkXYj05w==", "dev": true, - "requires": { - "@babel/highlight": "^7.10.3" + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" } }, - "@babel/helper-validator-identifier": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.3.tgz", - "integrity": "sha512-bU8JvtlYpJSBPuj1VUmKpFGaDZuLxASky3LhaKj3bmpSTY6VWooSM8msk+Z0CZoErFye2tlABF6yDkT3FOPAXw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.10.3", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.3.tgz", - "integrity": "sha512-Ih9B/u7AtgEnySE2L2F0Xm0GaM729XqqLfHkalTsbjXGyqmf/6M0Cu0WpvqueUlW+xk88BHw9Nkpj49naU+vWw==", + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.3", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "@jsdevtools/ono": { + "node_modules/@jsdevtools/ono": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", "dev": true }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, - "@types/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, - "requires": { - "@types/minimatch": "*", - "@types/node": "*" + "optional": true, + "engines": { + "node": ">=14" } }, - "@types/json-schema": { - "version": "7.0.9", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", - "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", - "dev": true - }, - "@types/lodash": { - "version": "4.14.178", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz", - "integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw==", - "dev": true - }, - "@types/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", - "dev": true - }, - "@types/node": { - "version": "17.0.8", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.8.tgz", - "integrity": "sha512-YofkM6fGv4gDJq78g4j0mMuGMkZVxZDgtU0JRdx6FgiJDG+0fY0GKVolOV8WqVmEhLCXkQRjwDdKyPxJp/uucg==", + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, - "@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "node_modules/@types/lodash": { + "version": "4.17.5", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.5.tgz", + "integrity": "sha512-MBIOHVZqVqgfro1euRDWX7OO0fBVUUMrN6Pwm8LQsz8cWhEpihlvR70ENj3f40j58TNxZaWv2ndSkInykNBBJw==", "dev": true }, - "@types/prettier": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.4.2.tgz", - "integrity": "sha512-ekoj4qOQYp7CvjX8ZDBgN86w3MqQhLE1hczEJbEIjgFEumDy+na/4AJAbLXfgEWFNB2pKadM5rPFtuSGMWK7xA==", - "dev": true + "node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } }, - "ansi-styles": { + "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "requires": { + "dependencies": { "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" } }, - "any-promise": { + "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", "dev": true }, - "argparse": { + "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "balanced-match": { + "node_modules/balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "call-me-maybe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", - "integrity": "sha512-wCyFsDQkKPwwF8BDwOiWNx/9K45L/hvggQiDbve+viMNMQnWhrlYIuBk09offfwCRtCO9P6XwUttufzU11WCVw==", - "dev": true - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "cli-color": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.1.tgz", - "integrity": "sha512-eBbxZF6fqPUNnf7CLAFOersUnyYzv83tHFLSlts+OAHsNendaqv2tHCq+/MO+b3Y+9JeoUlIvobyxG/Z8GNeOg==", + "node_modules/cli-color": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.4.tgz", + "integrity": "sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==", "dev": true, - "requires": { + "dependencies": { "d": "^1.0.1", - "es5-ext": "^0.10.53", + "es5-ext": "^0.10.64", "es6-iterator": "^2.0.3", "memoizee": "^0.4.15", "timers-ext": "^0.1.7" + }, + "engines": { + "node": ">=0.10" } }, - "color-convert": { + "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "requires": { + "dependencies": { "color-name": "1.1.3" } }, - "color-name": { + "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "commander": { + "node_modules/commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", - "dev": true - }, - "compare-versions": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", - "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "dev": true, + "engines": { + "node": ">= 6" + } }, - "cosmiconfig": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", - "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "requires": { - "@types/parse-json": "^4.0.0", - "import-fresh": "^3.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0", - "yaml": "^1.7.2" + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "d": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", - "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", "dev": true, - "requires": { - "es5-ext": "^0.10.50", - "type": "^1.0.1" + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" } }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", "dev": true, - "requires": { - "is-arrayish": "^0.2.1" + "engines": { + "node": ">= 12" } }, - "es5-ext": { - "version": "0.10.53", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", - "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", "dev": true, - "requires": { - "es6-iterator": "~2.0.3", - "es6-symbol": "~3.1.3", - "next-tick": "~1.0.0" + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" } }, - "es6-iterator": { + "node_modules/es6-iterator": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", "dev": true, - "requires": { + "dependencies": { "d": "1", "es5-ext": "^0.10.35", "es6-symbol": "^3.1.1" } }, - "es6-symbol": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", - "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", "dev": true, - "requires": { - "d": "^1.0.1", - "ext": "^1.1.2" + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.12" } }, - "es6-weak-map": { + "node_modules/es6-weak-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz", "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==", "dev": true, - "requires": { + "dependencies": { "d": "1", "es5-ext": "^0.10.46", "es6-iterator": "^2.0.3", "es6-symbol": "^3.1.1" } }, - "escape-string-regexp": { + "node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.8.0" + } }, - "event-emitter": { + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "dev": true, + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/event-emitter": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", "dev": true, - "requires": { + "dependencies": { "d": "1", "es5-ext": "~0.10.14" } }, - "ext": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ext/-/ext-1.6.0.tgz", - "integrity": "sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg==", + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", "dev": true, - "requires": { - "type": "^2.5.0" - }, "dependencies": { - "type": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz", - "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==", - "dev": true - } + "type": "^2.7.2" } }, - "find-versions": { + "node_modules/fetch-blob": { "version": "3.2.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", - "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", "dev": true, - "requires": { - "semver-regex": "^2.0.0" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" } }, - "flowgen": { + "node_modules/flowgen": { "version": "1.21.0", "resolved": "https://registry.npmjs.org/flowgen/-/flowgen-1.21.0.tgz", "integrity": "sha512-pFNFFyMLRmW6njhOIm5TrbGUDTv64aujmys2KrkRE2NYD8sXwJUyicQRwU5SPRBRJnFSD/FNlnHo2NnHI5eJSw==", "dev": true, - "requires": { + "dependencies": { "@babel/code-frame": "^7.16.7", "@babel/highlight": "^7.16.7", "commander": "^6.1.0", @@ -399,290 +341,417 @@ "typescript": "~4.4.4", "typescript-compiler": "^1.4.1-2" }, + "bin": { + "flowgen": "lib/cli/index.js" + } + }, + "node_modules/flowgen/node_modules/@babel/code-frame": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", + "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "dev": true, "dependencies": { - "@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", - "dev": true, - "requires": { - "@babel/highlight": "^7.22.5" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", - "dev": true - }, - "@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - } + "@babel/highlight": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/flowgen/node_modules/@babel/helper-validator-identifier": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" } }, - "fs.realpath": { + "node_modules/flowgen/node_modules/@babel/highlight": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", + "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/flowgen/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/foreground-child": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", + "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "dev": true, + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "function-bind": { + "node_modules/function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, - "get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true - }, - "glob": { + "node_modules/glob": { "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "requires": { + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "glob-promise": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/glob-promise/-/glob-promise-3.4.0.tgz", - "integrity": "sha512-q08RJ6O+eJn+dVanerAndJwIcumgbDdYiUT7zFQl3Wm1xD6fBKtah7H8ZJChj4wP+8C+QfeVy8xautR7rdmKEw==", + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, - "requires": { - "@types/glob": "*" + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "has": { + "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, - "requires": { + "dependencies": { "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" } }, - "has-flag": { + "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "husky": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/husky/-/husky-4.2.5.tgz", - "integrity": "sha512-SYZ95AjKcX7goYVZtVZF2i6XiZcHknw50iXvY7b0MiGoj5RwdgRQNEHdb+gPDPCXKlzwrybjFjkL6FOj8uRhZQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "ci-info": "^2.0.0", - "compare-versions": "^3.6.0", - "cosmiconfig": "^6.0.0", - "find-versions": "^3.2.0", - "opencollective-postinstall": "^2.0.2", - "pkg-dir": "^4.2.0", - "please-upgrade-node": "^3.2.0", - "slash": "^3.0.0", - "which-pm-runs": "^1.0.0" - } - }, - "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "node_modules/husky": { + "version": "9.0.11", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz", + "integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==", "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "bin": { + "husky": "bin.mjs" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" } }, - "inflight": { + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, - "requires": { + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "interpret": { + "node_modules/interpret": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.10" + } }, - "is-core-module": { + "node_modules/is-core-module": { "version": "2.12.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", "dev": true, - "requires": { + "dependencies": { "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "is-extglob": { + "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "is-glob": { + "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "requires": { + "dependencies": { "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-promise": { + "node_modules/is-promise": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", "dev": true }, - "js-tokens": { + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", + "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, - "js-yaml": { + "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "requires": { + "dependencies": { "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema-ref-parser": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz", - "integrity": "sha512-qcP2lmGy+JUoQJ4DOQeLaZDqH9qSkeGCK3suKWxJXS82dg728Mn3j97azDMaOUmJAN4uCq91LdPx4K7E8F1a7Q==", - "dev": true, - "requires": { - "@apidevtools/json-schema-ref-parser": "9.0.9" - } - }, - "json-schema-to-typescript": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/json-schema-to-typescript/-/json-schema-to-typescript-10.1.5.tgz", - "integrity": "sha512-X8bNNksfCQo6LhEuqNxmZr4eZpPjXZajmimciuk8eWXzZlif9Brq7WuMGD/SOhBKcRKP2SGVDNZbC28WQqx9Rg==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.6", - "@types/lodash": "^4.14.168", - "@types/prettier": "^2.1.5", - "cli-color": "^2.0.0", - "get-stdin": "^8.0.0", - "glob": "^7.1.6", - "glob-promise": "^3.4.0", - "is-glob": "^4.0.1", - "json-schema-ref-parser": "^9.0.6", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.20", - "minimist": "^1.2.5", - "mkdirp": "^1.0.4", + "node_modules/json-schema-to-typescript": { + "version": "14.0.5", + "resolved": "https://registry.npmjs.org/json-schema-to-typescript/-/json-schema-to-typescript-14.0.5.tgz", + "integrity": "sha512-JmHsbgY0KKo8Pw0HRXpGzAlZYxlu+M5kFhSzhNkUSrVJ4sCXPdAGIdSpzva5ev2/Kybz10S6AfnNdF4o3Pzt3A==", + "dev": true, + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^11.5.5", + "@types/json-schema": "^7.0.15", + "@types/lodash": "^4.17.0", + "cli-color": "^2.0.4", + "glob": "^10.3.12", + "is-glob": "^4.0.3", + "js-yaml": "^4.1.0", + "lodash": "^4.17.21", + "minimist": "^1.2.8", + "mkdirp": "^3.0.1", "mz": "^2.7.0", - "prettier": "^2.2.0" + "node-fetch": "^3.3.2", + "prettier": "^3.2.5" }, + "bin": { + "json2ts": "dist/src/cli.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/json-schema-to-typescript/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, "dependencies": { - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "prettier": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", - "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", - "dev": true - } + "balanced-match": "^1.0.0" } }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", - "dev": true + "node_modules/json-schema-to-typescript/node_modules/glob": { + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", + "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true + "node_modules/json-schema-to-typescript/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "lodash": { + "node_modules/json-schema-to-typescript/node_modules/prettier": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz", + "integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "lru-queue": { + "node_modules/lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/lru-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", "integrity": "sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", "dev": true, - "requires": { + "dependencies": { "es5-ext": "~0.10.2" } }, - "memoizee": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.15.tgz", - "integrity": "sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==", + "node_modules/memoizee": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.17.tgz", + "integrity": "sha512-DGqD7Hjpi/1or4F/aYAspXKNm5Yili0QDAFAY4QYvpqpgiY6+1jOfqpmByzjxbWd/T9mChbCArXAbDAsTm5oXA==", "dev": true, - "requires": { - "d": "^1.0.1", - "es5-ext": "^0.10.53", + "dependencies": { + "d": "^1.0.2", + "es5-ext": "^0.10.64", "es6-weak-map": "^2.0.3", "event-emitter": "^0.3.5", "is-promise": "^2.2.2", @@ -690,330 +759,637 @@ "next-tick": "^1.1.0", "timers-ext": "^0.1.7" }, - "dependencies": { - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", - "dev": true - } - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" + "engines": { + "node": ">=0.12" } }, - "minimist": { + "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } }, - "mz": { + "node_modules/mkdirp": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", + "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "dev": true, + "bin": { + "mkdirp": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "dev": true, - "requires": { + "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, - "next-tick": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", - "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=", + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", "dev": true }, - "object-assign": { + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "dev": true, + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "requires": { + "dependencies": { "wrappy": "1" } }, - "opencollective-postinstall": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", - "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", "dev": true }, - "parent-module": { + "node_modules/path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, - "requires": { - "callsites": "^3.0.0" + "engines": { + "node": ">=0.10.0" } }, - "parse-json": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", - "lines-and-columns": "^1.1.6" + "engines": { + "node": ">=8" } }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-parse": { + "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, - "requires": { - "find-up": "^4.0.0" - }, "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - } - } - }, - "please-upgrade-node": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", - "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", - "dev": true, - "requires": { - "semver-compare": "^1.0.0" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "prettier": { + "node_modules/prettier": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } }, - "rechoir": { + "node_modules/rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "dev": true, - "requires": { + "dependencies": { "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" } }, - "resolve": { + "node_modules/resolve": { "version": "1.22.2", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dev": true, - "requires": { + "dependencies": { "is-core-module": "^2.11.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "node_modules/rimraf": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz", + "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==", + "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, - "requires": { - "glob": "^7.1.3" + "dependencies": { + "balanced-match": "^1.0.0" } }, - "semver-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", - "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", - "dev": true + "node_modules/rimraf/node_modules/glob": { + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", + "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "semver-regex": { + "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", - "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", - "dev": true + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } }, - "shelljs": { + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shelljs": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "dev": true, - "requires": { + "dependencies": { "glob": "^7.0.0", "interpret": "^1.0.0", "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" } }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "supports-color": { + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "requires": { + "dependencies": { "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "supports-preserve-symlinks-flag": { + "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "thenify": { + "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", "dev": true, - "requires": { + "dependencies": { "any-promise": "^1.0.0" } }, - "thenify-all": { + "node_modules/thenify-all": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "dev": true, - "requires": { + "dependencies": { "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" } }, - "timers-ext": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.7.tgz", - "integrity": "sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==", + "node_modules/timers-ext": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/timers-ext/-/timers-ext-0.1.8.tgz", + "integrity": "sha512-wFH7+SEAcKfJpfLPkrgMPvvwnEtj8W4IurvEyrKsDleXnKLCDw71w8jltvfLa8Rm4qQxxT4jmDBYbJG/z7qoww==", "dev": true, - "requires": { - "es5-ext": "~0.10.46", - "next-tick": "1" + "dependencies": { + "es5-ext": "^0.10.64", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.12" } }, - "type": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", - "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==", + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", "dev": true }, - "typescript": { + "node_modules/typescript": { "version": "4.4.4", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", - "dev": true + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } }, - "typescript-compiler": { + "node_modules/typescript-compiler": { "version": "1.4.1-2", "resolved": "https://registry.npmjs.org/typescript-compiler/-/typescript-compiler-1.4.1-2.tgz", - "integrity": "sha1-uk99si2RU0oZKdkACdzhYety/T8=", + "integrity": "sha512-EMopKmoAEJqA4XXRFGOb7eSBhmQMbBahW6P1Koayeatp0b4AW2q/bBqYWkpG7QVQc9HGQUiS4trx2ZHcnAaZUg==", "dev": true }, - "which-pm-runs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", - "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "wrappy": { + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true - }, - "yaml": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", - "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", - "dev": true } } } diff --git a/package.json b/package.json index 8b456744..8cebde4f 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,34 @@ { "name": "cardano-serialization-lib", - "version": "11.5.0", + "version": "12.0.0", "description": "(De)serialization functions for the Cardano blockchain along with related utility functions", "scripts": { "rust:build-nodejs": "(rimraf ./rust/pkg && cd rust; wasm-pack build --target=nodejs; cd ..; npm run js:ts-json-gen; cd rust; wasm-pack pack) && npm run js:flowgen", "rust:build-browser": "(rimraf ./rust/pkg && cd rust; wasm-pack build --target=browser; cd ..; npm run js:ts-json-gen; cd rust; wasm-pack pack) && npm run js:flowgen", "rust:build-web": "(rimraf ./rust/pkg && cd rust; wasm-pack build --target=web; cd ..; npm run js:ts-json-gen; cd rust; wasm-pack pack) && npm run js:flowgen", "rust:build-asm": "(rimraf ./rust/pkg && cd rust; wasm-pack build --target=browser; cd ..; npm run js:ts-json-gen; cd rust; wasm-pack pack) && npm run asm:build && npm run js:flowgen", + "rust:build-nodejs:gc": "(rimraf ./rust/pkg && cd rust; WASM_BINDGEN_WEAKREF=1 wasm-pack build --target=nodejs; cd ..; npm run js:ts-json-gen; cd rust; wasm-pack pack) && npm run js:flowgen", + "rust:build-browser:gc": "(rimraf ./rust/pkg && cd rust; WASM_BINDGEN_WEAKREF=1 wasm-pack build --target=browser; cd ..; npm run js:ts-json-gen; cd rust; wasm-pack pack) && npm run js:flowgen", + "rust:build-web:gc": "(rimraf ./rust/pkg && cd rust; WASM_BINDGEN_WEAKREF=1 wasm-pack build --target=web; cd ..; npm run js:ts-json-gen; cd rust; wasm-pack pack) && npm run js:flowgen", + "rust:build-asm:gc": "(rimraf ./rust/pkg && cd rust; WASM_BINDGEN_WEAKREF=1 wasm-pack build --target=browser; cd ..; npm run js:ts-json-gen; cd rust; wasm-pack pack) && npm run asm:build && npm run js:flowgen", "rust:publish": "cd rust && cargo publish && cd ../", "asm:build": "./binaryen/bin/wasm2js ./rust/pkg/cardano_serialization_lib_bg.wasm --output ./rust/pkg/cardano_serialization_lib.asm.js && node ./scripts/wasm-to-asm && node ./scripts/fix-buffer-ref.js", "rust:check-warnings": "(cd rust; RUSTFLAGS=\"-D warnings\" cargo +stable build)", "rust:test": "(cd rust; cargo test)", "js:flowgen": "flowgen ./rust/pkg/cardano_serialization_lib.d.ts -o ./rust/pkg/cardano_serialization_lib.js.flow --add-flow-header", "js:prepublish": "npm run rust:test && rimraf ./publish && cp -r ./rust/pkg ./publish && cp README.md publish/ && cp LICENSE publish/", - "js:publish-nodejs:prod": "npm run rust:build-nodejs && npm run js:prepublish && node ./scripts/publish-helper -nodejs && cd publish && npm publish --access public", - "js:publish-nodejs:beta": "npm run rust:build-nodejs && npm run js:prepublish && node ./scripts/publish-helper -nodejs && cd publish && npm publish --tag beta --access public", - "js:publish-browser:prod": "npm run rust:build-browser && npm run js:prepublish && node ./scripts/publish-helper -browser && cd publish && npm publish --access public", - "js:publish-browser:beta": "npm run rust:build-browser && npm run js:prepublish && node ./scripts/publish-helper -browser && cd publish && npm publish --tag beta --access public", - "js:publish-asm:prod": "npm run rust:build-asm && npm run js:prepublish && node ./scripts/publish-helper -asmjs && cd publish && npm publish --access public", - "js:publish-asm:beta": "npm run rust:build-asm && npm run js:prepublish && node ./scripts/publish-helper -asmjs && cd publish && npm publish --tag beta --access public", + "js:publish-nodejs:prod:no-gc": "npm run rust:build-nodejs && npm run js:prepublish && node ./scripts/publish-helper -nodejs && cd publish && npm publish --access public", + "js:publish-nodejs:beta:no-gc": "npm run rust:build-nodejs && npm run js:prepublish && node ./scripts/publish-helper -nodejs && cd publish && npm publish --tag beta --access public", + "js:publish-browser:prod:no-gc": "npm run rust:build-browser && npm run js:prepublish && node ./scripts/publish-helper -browser && cd publish && npm publish --access public", + "js:publish-browser:beta:no-gc": "npm run rust:build-browser && npm run js:prepublish && node ./scripts/publish-helper -browser && cd publish && npm publish --tag beta --access public", + "js:publish-asm:prod:no-gc": "npm run rust:build-asm && npm run js:prepublish && node ./scripts/publish-helper -asmjs && cd publish && npm publish --access public", + "js:publish-asm:beta:no-gc": "npm run rust:build-asm && npm run js:prepublish && node ./scripts/publish-helper -asmjs && cd publish && npm publish --tag beta --access public", + "js:publish-nodejs:prod:gc": "npm run rust:build-nodejs:gc && npm run js:prepublish && node ./scripts/publish-helper -nodejs -gc && cd publish && npm publish --access public", + "js:publish-nodejs:beta:gc": "npm run rust:build-nodejs:gc && npm run js:prepublish && node ./scripts/publish-helper -nodejs -gc && cd publish && npm publish --tag beta --access public", + "js:publish-browser:prod:gc": "npm run rust:build-browser:gc && npm run js:prepublish && node ./scripts/publish-helper -browser -gc && cd publish && npm publish --access public", + "js:publish-browser:beta:gc": "npm run rust:build-browser:gc && npm run js:prepublish && node ./scripts/publish-helper -browser -gc && cd publish && npm publish --tag beta --access public", + "js:publish-asm:prod:gc": "npm run rust:build-asm:gc && npm run js:prepublish && node ./scripts/publish-helper -asmjs -gc && cd publish && npm publish --access public", + "js:publish-asm:beta:gc": "npm run rust:build-asm:gc && npm run js:prepublish && node ./scripts/publish-helper -asmjs -gc && cd publish && npm publish --tag beta --access public", "js:ts-json-gen": "cd rust/json-gen && cargo +stable run && cd ../.. && node ./scripts/run-json2ts.js && node ./scripts/json-ts-types.js", "postinstall": "git submodule update --init --recursive && cd binaryen; cmake . && make" }, @@ -35,8 +45,8 @@ }, "devDependencies": { "flowgen": "1.21.0", - "husky": "4.2.5", - "json-schema-to-typescript": "^10.1.5", - "rimraf": "3.0.2" + "husky": "^9.0.11", + "json-schema-to-typescript": "^14.0.5", + "rimraf": "^5.0.7" } } diff --git a/release.sh b/release.sh index c15ecb44..a43d289b 100644 --- a/release.sh +++ b/release.sh @@ -9,8 +9,11 @@ fi echo "Preparing ${RELEASE_TYPE} release" -. build-and-test.sh \ -&& npm run js:publish-nodejs:${RELEASE_TYPE} \ -&& npm run js:publish-browser:${RELEASE_TYPE} \ -&& npm run js:publish-asm:${RELEASE_TYPE} \ -&& (cd rust; cargo publish --allow-dirty) \ No newline at end of file +. ./build-and-test.sh \ +&& npm run js:publish-nodejs:${RELEASE_TYPE}:no-gc \ +&& npm run js:publish-browser:${RELEASE_TYPE}:no-gc \ +&& npm run js:publish-asm:${RELEASE_TYPE}:no-gc \ +&& npm run js:publish-nodejs:${RELEASE_TYPE}:gc \ +&& npm run js:publish-browser:${RELEASE_TYPE}:gc \ +&& npm run js:publish-asm:${RELEASE_TYPE}:gc \ +&& (cd rust; cargo publish --allow-dirty) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index fb5a0f66..a9f78622 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cardano-serialization-lib" -version = "11.5.0" +version = "12.0.0" edition = "2018" authors = ["EMURGO"] license = "MIT" @@ -12,6 +12,13 @@ exclude = [ "pkg/*", ] +[features] +default = [] +#TODO: need to review the features and delete legacy ones. List is defined to avoid warnings. +property-test-api = [] +generic-serialization = [] +with-bench = [] + [lib] crate-type = ["cdylib", "rlib"] @@ -24,8 +31,8 @@ digest = "^0.9" bech32 = "0.7.2" hex = "0.4.0" cfg-if = "1" -linked-hash-map = "0.5.3" -serde_json = "1.0.57" +hashlink = "0.9.1" +serde_json = { version = "1.0.114", features = ["arbitrary_precision"] } num-bigint = "0.4.0" num-integer = "0.1.45" # The default can't be compiled to wasm, so it's necessary to use either the 'nightly' @@ -35,6 +42,9 @@ itertools = "0.10.1" rand = "0.8.4" schemars = "0.8.8" serde = { version = "1.0", features = ["derive"] } +num-derive = "0.4.0" +num-traits = "0.2.16" +num = "0.4.1" # non-wasm [target.'cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))'.dependencies] @@ -45,7 +55,7 @@ getrandom = "0.2.3" # wasm [target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] serde-wasm-bindgen = "0.4.5" -wasm-bindgen = "=0.2.87" +wasm-bindgen = "=0.2.92" rand_os = { version = "0.1", features = ["wasm-bindgen"] } js-sys = "0.3.51" getrandom = { version = "0.2.3", features = ["js"] } diff --git a/rust/json-gen/Cargo.lock b/rust/json-gen/Cargo.lock index 9d99a696..dd98dbae 100644 --- a/rust/json-gen/Cargo.lock +++ b/rust/json-gen/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -37,7 +49,7 @@ checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" [[package]] name = "cardano-serialization-lib" -version = "11.5.0" +version = "12.0.0" dependencies = [ "bech32", "cbor_event", @@ -47,13 +59,16 @@ dependencies = [ "digest", "ed25519-bip32", "getrandom", + "hashlink", "hex", "itertools", "js-sys", - "linked-hash-map", "noop_proc_macro", + "num", "num-bigint", + "num-derive", "num-integer", + "num-traits", "rand", "rand_os", "schemars", @@ -183,6 +198,24 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown", +] + [[package]] name = "hex" version = "0.4.3" @@ -219,12 +252,6 @@ version = "0.2.112" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125" -[[package]] -name = "linked-hash-map" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" - [[package]] name = "log" version = "0.4.14" @@ -240,6 +267,20 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.3" @@ -251,6 +292,26 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-complex" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e6a0fd4f737c707bd9086cc16c925f294943eb62eb71499e9fd4cf71f8b9f4e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -261,20 +322,43 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] [[package]] name = "once_cell" -version = "1.15.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" @@ -290,18 +374,18 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.29" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -417,9 +501,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.133" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] @@ -437,13 +521,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.133" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.85", + "syn 2.0.48", ] [[package]] @@ -459,9 +543,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.75" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c059c05b48c5c0067d4b4b2b4f0732dd65feb52daf7e0ea09cd87e7dadc1af79" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ "itoa", "ryu", @@ -494,9 +578,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.23" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -535,9 +619,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -545,24 +629,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.48", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -570,22 +654,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.23", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "winapi" @@ -608,3 +692,23 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] diff --git a/rust/json-gen/Cargo.toml b/rust/json-gen/Cargo.toml index 43a104e8..df6c8a6c 100644 --- a/rust/json-gen/Cargo.toml +++ b/rust/json-gen/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT" [dependencies] -serde_json = "1.0.57" +serde_json = { version = "1.0.114", features = ["arbitrary_precision"] } schemars = "0.8.8" #serde = { version = "1.0", features = ["derive"] } cardano-serialization-lib = { path = ".." } diff --git a/rust/json-gen/src/main.rs b/rust/json-gen/src/main.rs index 59d656ae..d22dc080 100644 --- a/rust/json-gen/src/main.rs +++ b/rust/json-gen/src/main.rs @@ -2,11 +2,6 @@ use std::fs; use std::path::Path; use cardano_serialization_lib::*; -use cardano_serialization_lib::address::*; -use cardano_serialization_lib::crypto::*; -use cardano_serialization_lib::metadata::*; -use cardano_serialization_lib::plutus::*; -use cardano_serialization_lib::utils::*; //#[macro_export] macro_rules! gen_json_schema { @@ -21,7 +16,19 @@ fn main() { let schema_path = Path::new(&"schemas"); if !schema_path.exists() { fs::create_dir(schema_path).unwrap(); + } else { + let files = schema_path.read_dir().unwrap(); + for file in files { + let file = file.unwrap(); + if file.file_type().unwrap().is_file() { + let filename = file.file_name().into_string().unwrap(); + if filename.ends_with(".json") { + fs::remove_file(file.path()).unwrap(); + } + } + } } + // lib.rs gen_json_schema!(UnitInterval); gen_json_schema!(Transaction); @@ -58,7 +65,7 @@ fn main() { gen_json_schema!(Relay); //gen_json_schema!(RelayEnum); gen_json_schema!(PoolMetadata); - gen_json_schema!(StakeCredentials); + gen_json_schema!(Credentials); gen_json_schema!(RewardAddresses); gen_json_schema!(Withdrawals); gen_json_schema!(TransactionWitnessSet); @@ -81,6 +88,7 @@ fn main() { gen_json_schema!(TransactionWitnessSets); gen_json_schema!(AuxiliaryDataSet); gen_json_schema!(Block); + gen_json_schema!(VersionedBlock); gen_json_schema!(Header); gen_json_schema!(OperationalCert); gen_json_schema!(HeaderBody); @@ -89,6 +97,7 @@ fn main() { gen_json_schema!(Assets); gen_json_schema!(MultiAsset); gen_json_schema!(MintAssets); + gen_json_schema!(MintsAssets); gen_json_schema!(Mint); gen_json_schema!(NetworkId); gen_json_schema!(NetworkIdKind); @@ -116,13 +125,14 @@ fn main() { gen_json_schema!(BlockHash); gen_json_schema!(DataHash); gen_json_schema!(ScriptDataHash); + gen_json_schema!(AnchorDataHash); gen_json_schema!(VRFVKey); gen_json_schema!(KESVKey); gen_json_schema!(Nonce); gen_json_schema!(VRFCert); // address.rs - gen_json_schema!(StakeCredential); - gen_json_schema!(StakeCredType); + gen_json_schema!(Credential); + gen_json_schema!(CredType); gen_json_schema!(Address); gen_json_schema!(RewardAddress); // plutus.rs @@ -156,4 +166,38 @@ fn main() { gen_json_schema!(Value); gen_json_schema!(TransactionUnspentOutput); gen_json_schema!(TransactionUnspentOutputs); + + gen_json_schema!(DRep); + gen_json_schema!(Anchor); + gen_json_schema!(Voter); + gen_json_schema!(Voters); + gen_json_schema!(GovernanceActionId); + gen_json_schema!(GovernanceActionIds); + gen_json_schema!(VotingProcedure); + gen_json_schema!(VotingProcedures); + gen_json_schema!(CommitteeHotAuth); + gen_json_schema!(CommitteeColdResign); + gen_json_schema!(DRepDeregistration); + gen_json_schema!(DRepRegistration); + gen_json_schema!(DRepUpdate); + gen_json_schema!(StakeAndVoteDelegation); + gen_json_schema!(StakeRegistrationAndDelegation); + gen_json_schema!(StakeVoteRegistrationAndDelegation); + gen_json_schema!(VoteDelegation); + gen_json_schema!(VoteRegistrationAndDelegation); + + gen_json_schema!(VotingProposal); + gen_json_schema!(VotingProposals); + gen_json_schema!(GovernanceAction); + gen_json_schema!(HardForkInitiationAction); + gen_json_schema!(UpdateCommitteeAction); + gen_json_schema!(NewConstitutionAction); + gen_json_schema!(NoConfidenceAction); + gen_json_schema!(ParameterChangeAction); + gen_json_schema!(TreasuryWithdrawals); + gen_json_schema!(TreasuryWithdrawalsAction); + gen_json_schema!(Committee); + gen_json_schema!(Constitution); + gen_json_schema!(DRepVotingThresholds); + gen_json_schema!(PoolVotingThresholds); } diff --git a/rust/pkg/cardano_serialization_lib.js.flow b/rust/pkg/cardano_serialization_lib.js.flow index b82cd01e..ba623ef2 100644 --- a/rust/pkg/cardano_serialization_lib.js.flow +++ b/rust/pkg/cardano_serialization_lib.js.flow @@ -6,60 +6,16 @@ */ /** - * @param {string} json - * @param {number} schema - * @returns {PlutusData} - */ -declare export function encode_json_str_to_plutus_datum( - json: string, - schema: number -): PlutusData; - -/** - * @param {PlutusData} datum - * @param {number} schema - * @returns {string} - */ -declare export function decode_plutus_datum_to_json_str( - datum: PlutusData, - schema: number -): string; - -/** - * @param {Uint8Array} bytes - * @returns {TransactionMetadatum} - */ -declare export function encode_arbitrary_bytes_as_metadatum( - bytes: Uint8Array -): TransactionMetadatum; - -/** - * @param {TransactionMetadatum} metadata - * @returns {Uint8Array} - */ -declare export function decode_arbitrary_bytes_from_metadatum( - metadata: TransactionMetadatum -): Uint8Array; - -/** - * @param {string} json - * @param {number} schema - * @returns {TransactionMetadatum} - */ -declare export function encode_json_str_to_metadatum( - json: string, - schema: number -): TransactionMetadatum; - -/** - * @param {TransactionMetadatum} metadatum - * @param {number} schema - * @returns {string} + * @param {Address} address + * @param {TransactionUnspentOutputs} utxos + * @param {TransactionBuilderConfig} config + * @returns {TransactionBatchList} */ -declare export function decode_metadatum_to_json_str( - metadatum: TransactionMetadatum, - schema: number -): string; +declare export function create_send_all( + address: Address, + utxos: TransactionUnspentOutputs, + config: TransactionBuilderConfig +): TransactionBatchList; /** * @param {string} password @@ -85,18 +41,6 @@ declare export function decrypt_with_password( data: string ): string; -/** - * @param {Address} address - * @param {TransactionUnspentOutputs} utxos - * @param {TransactionBuilderConfig} config - * @returns {TransactionBatchList} - */ -declare export function create_send_all( - address: Address, - utxos: TransactionUnspentOutputs, - config: TransactionBuilderConfig -): TransactionBatchList; - /** * @param {Transaction} tx * @param {LinearFee} linear_fee @@ -124,6 +68,80 @@ declare export function min_script_fee( ex_unit_prices: ExUnitPrices ): BigNum; +/** + * @param {number} total_ref_scripts_size + * @param {UnitInterval} ref_script_coins_per_byte + * @returns {BigNum} + */ +declare export function min_ref_script_fee( + total_ref_scripts_size: number, + ref_script_coins_per_byte: UnitInterval +): BigNum; + +/** + * @param {string} json + * @param {$Values< + typeof + PlutusDatumSchema>} schema + * @returns {PlutusData} + */ +declare export function encode_json_str_to_plutus_datum( + json: string, + schema: $Values +): PlutusData; + +/** + * @param {PlutusData} datum + * @param {$Values< + typeof + PlutusDatumSchema>} schema + * @returns {string} + */ +declare export function decode_plutus_datum_to_json_str( + datum: PlutusData, + schema: $Values +): string; + +/** + * @param {Uint8Array} bytes + * @returns {TransactionMetadatum} + */ +declare export function encode_arbitrary_bytes_as_metadatum( + bytes: Uint8Array +): TransactionMetadatum; + +/** + * @param {TransactionMetadatum} metadata + * @returns {Uint8Array} + */ +declare export function decode_arbitrary_bytes_from_metadatum( + metadata: TransactionMetadatum +): Uint8Array; + +/** + * @param {string} json + * @param {$Values< + typeof + MetadataJsonSchema>} schema + * @returns {TransactionMetadatum} + */ +declare export function encode_json_str_to_metadatum( + json: string, + schema: $Values +): TransactionMetadatum; + +/** + * @param {TransactionMetadatum} metadatum + * @param {$Values< + typeof + MetadataJsonSchema>} schema + * @returns {string} + */ +declare export function decode_metadatum_to_json_str( + metadatum: TransactionMetadatum, + schema: $Values +): string; + /** * @param {TransactionHash} tx_body_hash * @param {ByronAddress} addr @@ -183,7 +201,7 @@ declare export function hash_plutus_data(plutus_data: PlutusData): DataHash; /** * @param {Redeemers} redeemers * @param {Costmdls} cost_models - * @param {PlutusList | void} datums + * @param {PlutusList | void} [datums] * @returns {ScriptDataHash} */ declare export function hash_script_data( @@ -227,77 +245,64 @@ declare export function min_ada_for_output( data_cost: DataCost ): BigNum; -/** - * !!! DEPRECATED !!! - * This function uses outdated set of arguments. - * Use `min_ada_for_output` instead - * @param {Value} assets - * @param {boolean} has_data_hash - * @param {BigNum} coins_per_utxo_word - * @returns {BigNum} - */ -declare export function min_ada_required( - assets: Value, - has_data_hash: boolean, - coins_per_utxo_word: BigNum -): BigNum; - /** * Receives a script JSON string * and returns a NativeScript. * Cardano Wallet and Node styles are supported. - * + * * * wallet: https://github.com/input-output-hk/cardano-wallet/blob/master/specifications/api/swagger.yaml * * node: https://github.com/input-output-hk/cardano-node/blob/master/doc/reference/simple-scripts.md - * + * * self_xpub is expected to be a Bip32PublicKey as hex-encoded bytes * @param {string} json * @param {string} self_xpub - * @param {number} schema + * @param {$Values< + typeof + ScriptSchema>} schema * @returns {NativeScript} */ declare export function encode_json_str_to_native_script( json: string, self_xpub: string, - schema: number + schema: $Values ): NativeScript; /** */ -declare export var CertificateKind: {| - +StakeRegistration: 0, // 0 - +StakeDeregistration: 1, // 1 - +StakeDelegation: 2, // 2 - +PoolRegistration: 3, // 3 - +PoolRetirement: 4, // 4 - +GenesisKeyDelegation: 5, // 5 - +MoveInstantaneousRewardsCert: 6, // 6 +declare export var MetadataJsonSchema: {| + +NoConversions: 0, // 0 + +BasicConversions: 1, // 1 + +DetailedSchema: 2, // 2 |}; /** */ -declare export var MIRPot: {| - +Reserves: 0, // 0 - +Treasury: 1, // 1 +declare export var VoterKind: {| + +ConstitutionalCommitteeHotKeyHash: 0, // 0 + +ConstitutionalCommitteeHotScriptHash: 1, // 1 + +DRepKeyHash: 2, // 2 + +DRepScriptHash: 3, // 3 + +StakingPoolKeyHash: 4, // 4 |}; /** */ -declare export var MIRKind: {| - +ToOtherPot: 0, // 0 - +ToStakeCredentials: 1, // 1 +declare export var CoinSelectionStrategyCIP2: {| + +LargestFirst: 0, // 0 + +RandomImprove: 1, // 1 + +LargestFirstMultiAsset: 2, // 2 + +RandomImproveMultiAsset: 3, // 3 |}; /** */ -declare export var RelayKind: {| - +SingleHostAddr: 0, // 0 - +SingleHostName: 1, // 1 - +MultiHostName: 2, // 2 +declare export var NetworkIdKind: {| + +Testnet: 0, // 0 + +Mainnet: 1, // 1 |}; /** @@ -312,6 +317,50 @@ declare export var NativeScriptKind: {| +TimelockExpiry: 5, // 5 |}; +/** + */ + +declare export var VoteKind: {| + +No: 0, // 0 + +Yes: 1, // 1 + +Abstain: 2, // 2 +|}; + +/** + */ + +declare export var CertificateKind: {| + +StakeRegistration: 0, // 0 + +StakeDeregistration: 1, // 1 + +StakeDelegation: 2, // 2 + +PoolRegistration: 3, // 3 + +PoolRetirement: 4, // 4 + +GenesisKeyDelegation: 5, // 5 + +MoveInstantaneousRewardsCert: 6, // 6 + +CommitteeHotAuth: 7, // 7 + +CommitteeColdResign: 8, // 8 + +DRepDeregistration: 9, // 9 + +DRepRegistration: 10, // 10 + +DRepUpdate: 11, // 11 + +StakeAndVoteDelegation: 12, // 12 + +StakeRegistrationAndDelegation: 13, // 13 + +StakeVoteRegistrationAndDelegation: 14, // 14 + +VoteDelegation: 15, // 15 + +VoteRegistrationAndDelegation: 16, // 16 +|}; + +/** + */ + +declare export var AddressKind: {| + +Base: 0, // 0 + +Pointer: 1, // 1 + +Enterprise: 2, // 2 + +Reward: 3, // 3 + +Byron: 4, // 4 + +Malformed: 5, // 5 +|}; + /** * Each new language uses a different namespace for hashing its script * This is because you could have a language where the same bytes have different semantics @@ -323,14 +372,7 @@ declare export var ScriptHashNamespace: {| +NativeScript: 0, // 0 +PlutusScript: 1, // 1 +PlutusScriptV2: 2, // 2 -|}; - -/** - */ - -declare export var NetworkIdKind: {| - +Testnet: 0, // 0 - +Mainnet: 1, // 1 + +PlutusScriptV3: 3, // 3 |}; /** @@ -339,44 +381,24 @@ declare export var NetworkIdKind: {| declare export var LanguageKind: {| +PlutusV1: 0, // 0 +PlutusV2: 1, // 1 + +PlutusV3: 2, // 2 |}; /** */ -declare export var PlutusDataKind: {| - +ConstrPlutusData: 0, // 0 - +Map: 1, // 1 - +List: 2, // 2 - +Integer: 3, // 3 - +Bytes: 4, // 4 -|}; - -/** - */ - -declare export var RedeemerTagKind: {| - +Spend: 0, // 0 - +Mint: 1, // 1 - +Cert: 2, // 2 - +Reward: 3, // 3 +declare export var MIRPot: {| + +Reserves: 0, // 0 + +Treasury: 1, // 1 |}; /** - * JSON <-> PlutusData conversion schemas. - * Follows ScriptDataJsonSchema in cardano-cli defined at: - * https://github.com/input-output-hk/cardano-node/blob/master/cardano-api/src/Cardano/Api/ScriptData.hs#L254 - * - * All methods here have the following restrictions due to limitations on dependencies: - * * JSON numbers above u64::MAX (positive) or below i64::MIN (negative) will throw errors - * * Hex strings for bytes don't accept odd-length (half-byte) strings. - * cardano-cli seems to support these however but it seems to be different than just 0-padding - * on either side when tested so proceed with caution + * Used to choosed the schema for a script JSON string */ -declare export var PlutusDatumSchema: {| - +BasicConversions: 0, // 0 - +DetailedSchema: 1, // 1 +declare export var ScriptSchema: {| + +Wallet: 0, // 0 + +Node: 1, // 1 |}; /** @@ -393,54 +415,120 @@ declare export var TransactionMetadatumKind: {| /** */ -declare export var MetadataJsonSchema: {| - +NoConversions: 0, // 0 - +BasicConversions: 1, // 1 - +DetailedSchema: 2, // 2 +declare export var CborContainerType: {| + +Array: 0, // 0 + +Map: 1, // 1 |}; /** */ -declare export var StakeCredKind: {| - +Key: 0, // 0 - +Script: 1, // 1 +declare export var PlutusDataKind: {| + +ConstrPlutusData: 0, // 0 + +Map: 1, // 1 + +List: 2, // 2 + +Integer: 3, // 3 + +Bytes: 4, // 4 |}; /** */ -declare export var CoinSelectionStrategyCIP2: {| - +LargestFirst: 0, // 0 - +RandomImprove: 1, // 1 - +LargestFirstMultiAsset: 2, // 2 - +RandomImproveMultiAsset: 3, // 3 +declare export var DRepKind: {| + +KeyHash: 0, // 0 + +ScriptHash: 1, // 1 + +AlwaysAbstain: 2, // 2 + +AlwaysNoConfidence: 3, // 3 |}; /** */ -declare export var CborContainerType: {| - +Array: 0, // 0 - +Map: 1, // 1 +declare export var CredKind: {| + +Key: 0, // 0 + +Script: 1, // 1 |}; /** - * Used to choosed the schema for a script JSON string */ -declare export var ScriptSchema: {| - +Wallet: 0, // 0 - +Node: 1, // 1 +declare export var BlockEra: {| + +Byron: 0, // 0 + +Shelley: 1, // 1 + +Allegra: 2, // 2 + +Mary: 3, // 3 + +Alonzo: 4, // 4 + +Babbage: 5, // 5 + +Conway: 6, // 6 + +Unknown: 7, // 7 |}; /** */ -declare export class Address { - free(): void; - /** - * @param {Uint8Array} data +declare export var GovernanceActionKind: {| + +ParameterChangeAction: 0, // 0 + +HardForkInitiationAction: 1, // 1 + +TreasuryWithdrawalsAction: 2, // 2 + +NoConfidenceAction: 3, // 3 + +UpdateCommitteeAction: 4, // 4 + +NewConstitutionAction: 5, // 5 + +InfoAction: 6, // 6 +|}; + +/** + */ + +declare export var RelayKind: {| + +SingleHostAddr: 0, // 0 + +SingleHostName: 1, // 1 + +MultiHostName: 2, // 2 +|}; + +/** + * JSON <-> PlutusData conversion schemas. + * Follows ScriptDataJsonSchema in cardano-cli defined at: + * https://github.com/input-output-hk/cardano-node/blob/master/cardano-api/src/Cardano/Api/ScriptData.hs#L254 + * + * All methods here have the following restrictions due to limitations on dependencies: + * * JSON numbers above u64::MAX (positive) or below i64::MIN (negative) will throw errors + * * Hex strings for bytes don't accept odd-length (half-byte) strings. + * cardano-cli seems to support these however but it seems to be different than just 0-padding + * on either side when tested so proceed with caution + */ + +declare export var PlutusDatumSchema: {| + +BasicConversions: 0, // 0 + +DetailedSchema: 1, // 1 +|}; + +/** + */ + +declare export var RedeemerTagKind: {| + +Spend: 0, // 0 + +Mint: 1, // 1 + +Cert: 2, // 2 + +Reward: 3, // 3 + +Vote: 4, // 4 + +VotingProposal: 5, // 5 +|}; + +/** + */ + +declare export var MIRKind: {| + +ToOtherPot: 0, // 0 + +ToStakeCredentials: 1, // 1 +|}; + +/** + */ +declare export class Address { + free(): void; + + /** + * @param {Uint8Array} data * @returns {Address} */ static from_bytes(data: Uint8Array): Address; @@ -461,6 +549,23 @@ declare export class Address { */ static from_json(json: string): Address; + /** + * @returns {$Values< + typeof + AddressKind>} + */ + kind(): $Values; + + /** + * @returns {Credential | void} + */ + payment_cred(): Credential | void; + + /** + * @returns {boolean} + */ + is_malformed(): boolean; + /** * @returns {string} */ @@ -478,7 +583,7 @@ declare export class Address { to_bytes(): Uint8Array; /** - * @param {string | void} prefix + * @param {string | void} [prefix] * @returns {string} */ to_bech32(prefix?: string): string; @@ -494,6 +599,105 @@ declare export class Address { */ network_id(): number; } +/** + */ +declare export class Anchor { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {Anchor} + */ + static from_bytes(bytes: Uint8Array): Anchor; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {Anchor} + */ + static from_hex(hex_str: string): Anchor; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {AnchorJSON} + */ + to_js_value(): AnchorJSON; + + /** + * @param {string} json + * @returns {Anchor} + */ + static from_json(json: string): Anchor; + + /** + * @returns {URL} + */ + url(): URL; + + /** + * @returns {AnchorDataHash} + */ + anchor_data_hash(): AnchorDataHash; + + /** + * @param {URL} anchor_url + * @param {AnchorDataHash} anchor_data_hash + * @returns {Anchor} + */ + static new(anchor_url: URL, anchor_data_hash: AnchorDataHash): Anchor; +} +/** + */ +declare export class AnchorDataHash { + free(): void; + + /** + * @param {Uint8Array} bytes + * @returns {AnchorDataHash} + */ + static from_bytes(bytes: Uint8Array): AnchorDataHash; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {string} prefix + * @returns {string} + */ + to_bech32(prefix: string): string; + + /** + * @param {string} bech_str + * @returns {AnchorDataHash} + */ + static from_bech32(bech_str: string): AnchorDataHash; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex + * @returns {AnchorDataHash} + */ + static from_hex(hex: string): AnchorDataHash; +} /** */ declare export class AssetName { @@ -850,25 +1054,25 @@ declare export class BaseAddress { /** * @param {number} network - * @param {StakeCredential} payment - * @param {StakeCredential} stake + * @param {Credential} payment + * @param {Credential} stake * @returns {BaseAddress} */ static new( network: number, - payment: StakeCredential, - stake: StakeCredential + payment: Credential, + stake: Credential ): BaseAddress; /** - * @returns {StakeCredential} + * @returns {Credential} */ - payment_cred(): StakeCredential; + payment_cred(): Credential; /** - * @returns {StakeCredential} + * @returns {Credential} */ - stake_cred(): StakeCredential; + stake_cred(): Credential; /** * @returns {Address} @@ -880,6 +1084,11 @@ declare export class BaseAddress { * @returns {BaseAddress | void} */ static from_address(addr: Address): BaseAddress | void; + + /** + * @returns {number} + */ + network_id(): number; } /** */ @@ -956,17 +1165,39 @@ declare export class BigInt { */ add(other: BigInt): BigInt; + /** + * @param {BigInt} other + * @returns {BigInt} + */ + sub(other: BigInt): BigInt; + /** * @param {BigInt} other * @returns {BigInt} */ mul(other: BigInt): BigInt; + /** + * @param {number} exp + * @returns {BigInt} + */ + pow(exp: number): BigInt; + /** * @returns {BigInt} */ static one(): BigInt; + /** + * @returns {BigInt} + */ + static zero(): BigInt; + + /** + * @returns {BigInt} + */ + abs(): BigInt; + /** * @returns {BigInt} */ @@ -977,6 +1208,12 @@ declare export class BigInt { * @returns {BigInt} */ div_ceil(other: BigInt): BigInt; + + /** + * @param {BigInt} other + * @returns {BigInt} + */ + div_floor(other: BigInt): BigInt; } /** */ @@ -1220,49 +1457,25 @@ declare export class Bip32PublicKey { free(): void; /** - * derive this public key with the given index. - * - * # Errors - * - * If the index is not a soft derivation index (< 0x80000000) then - * calling this method will fail. - * - * # Security considerations - * - * * hard derivation index cannot be soft derived with the public key - * - * # Hard derivation vs Soft derivation - * - * If you pass an index below 0x80000000 then it is a soft derivation. - * The advantage of soft derivation is that it is possible to derive the - * public key too. I.e. derivation the private key with a soft derivation - * index and then retrieving the associated public key is equivalent to - * deriving the public key associated to the parent private key. - * - * Hard derivation index does not allow public key derivation. - * - * This is why deriving the private key should not fail while deriving - * the public key may fail (if the derivation index is invalid). - * @param {number} index + * @param {string} hex_str * @returns {Bip32PublicKey} */ - derive(index: number): Bip32PublicKey; + static from_hex(hex_str: string): Bip32PublicKey; /** - * @returns {PublicKey} + * @returns {string} */ - to_raw_key(): PublicKey; + to_hex(): string; /** - * @param {Uint8Array} bytes - * @returns {Bip32PublicKey} + * @returns {Uint8Array} */ - static from_bytes(bytes: Uint8Array): Bip32PublicKey; + chaincode(): Uint8Array; /** - * @returns {Uint8Array} + * @returns {string} */ - as_bytes(): Uint8Array; + to_bech32(): string; /** * @param {string} bech32_str @@ -1271,25 +1484,49 @@ declare export class Bip32PublicKey { static from_bech32(bech32_str: string): Bip32PublicKey; /** - * @returns {string} + * @returns {Uint8Array} */ - to_bech32(): string; + as_bytes(): Uint8Array; /** - * @returns {Uint8Array} + * @param {Uint8Array} bytes + * @returns {Bip32PublicKey} */ - chaincode(): Uint8Array; + static from_bytes(bytes: Uint8Array): Bip32PublicKey; /** - * @returns {string} + * @returns {PublicKey} */ - to_hex(): string; + to_raw_key(): PublicKey; /** - * @param {string} hex_str + * derive this public key with the given index. + * + * # Errors + * + * If the index is not a soft derivation index (< 0x80000000) then + * calling this method will fail. + * + * # Security considerations + * + * * hard derivation index cannot be soft derived with the public key + * + * # Hard derivation vs Soft derivation + * + * If you pass an index below 0x80000000 then it is a soft derivation. + * The advantage of soft derivation is that it is possible to derive the + * public key too. I.e. derivation the private key with a soft derivation + * index and then retrieving the associated public key is equivalent to + * deriving the public key associated to the parent private key. + * + * Hard derivation index does not allow public key derivation. + * + * This is why deriving the private key should not fail while deriving + * the public key may fail (if the derivation index is invalid). + * @param {number} index * @returns {Bip32PublicKey} */ - static from_hex(hex_str: string): Bip32PublicKey; + derive(index: number): Bip32PublicKey; } /** */ @@ -1497,25 +1734,66 @@ declare export class BootstrapWitnesses { free(): void; /** - * @returns {BootstrapWitnesses} + * @returns {Uint8Array} */ - static new(): BootstrapWitnesses; + to_bytes(): Uint8Array; /** - * @returns {number} + * @param {Uint8Array} bytes + * @returns {BootstrapWitnesses} */ - len(): number; + static from_bytes(bytes: Uint8Array): BootstrapWitnesses; /** - * @param {number} index + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {BootstrapWitnesses} + */ + static from_hex(hex_str: string): BootstrapWitnesses; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {BootstrapWitnessesJSON} + */ + to_js_value(): BootstrapWitnessesJSON; + + /** + * @param {string} json + * @returns {BootstrapWitnesses} + */ + static from_json(json: string): BootstrapWitnesses; + + /** + * @returns {BootstrapWitnesses} + */ + static new(): BootstrapWitnesses; + + /** + * @returns {number} + */ + len(): number; + + /** + * @param {number} index * @returns {BootstrapWitness} */ get(index: number): BootstrapWitness; /** + * Add a new `BootstrapWitness` to the set. + * Returns `true` if the element was not already present in the set. * @param {BootstrapWitness} elem + * @returns {boolean} */ - add(elem: BootstrapWitness): void; + add(elem: BootstrapWitness): boolean; } /** */ @@ -1639,6 +1917,15 @@ declare export class Certificate { stake_registration: StakeRegistration ): Certificate; + /** + * Since StakeRegistration can represent stake_registration certificate or reg_cert certificate, because both certificates have the same semantics. + * And in some cases you want to create a reg_cert, this function is used to create a reg_cert. + * The function will return an error if StakeRegistration represents a stake_registration certificate. + * @param {StakeRegistration} stake_registration + * @returns {Certificate} + */ + static new_reg_cert(stake_registration: StakeRegistration): Certificate; + /** * @param {StakeDeregistration} stake_deregistration * @returns {Certificate} @@ -1647,6 +1934,15 @@ declare export class Certificate { stake_deregistration: StakeDeregistration ): Certificate; + /** + * Since StakeDeregistration can represent stake_deregistration certificate or unreg_cert certificate, because both certificates have the same semantics. + * And in some cases you want to create an unreg_cert, this function is used to create an unreg_cert. + * The function will return an error if StakeDeregistration represents a stake_deregistration certificate. + * @param {StakeDeregistration} stake_deregistration + * @returns {Certificate} + */ + static new_unreg_cert(stake_deregistration: StakeDeregistration): Certificate; + /** * @param {StakeDelegation} stake_delegation * @returns {Certificate} @@ -1684,20 +1980,114 @@ declare export class Certificate { ): Certificate; /** - * @returns {number} + * @param {CommitteeHotAuth} committee_hot_auth + * @returns {Certificate} + */ + static new_committee_hot_auth( + committee_hot_auth: CommitteeHotAuth + ): Certificate; + + /** + * @param {CommitteeColdResign} committee_cold_resign + * @returns {Certificate} + */ + static new_committee_cold_resign( + committee_cold_resign: CommitteeColdResign + ): Certificate; + + /** + * @param {DRepDeregistration} drep_deregistration + * @returns {Certificate} + */ + static new_drep_deregistration( + drep_deregistration: DRepDeregistration + ): Certificate; + + /** + * @param {DRepRegistration} drep_registration + * @returns {Certificate} + */ + static new_drep_registration( + drep_registration: DRepRegistration + ): Certificate; + + /** + * @param {DRepUpdate} drep_update + * @returns {Certificate} + */ + static new_drep_update(drep_update: DRepUpdate): Certificate; + + /** + * @param {StakeAndVoteDelegation} stake_and_vote_delegation + * @returns {Certificate} + */ + static new_stake_and_vote_delegation( + stake_and_vote_delegation: StakeAndVoteDelegation + ): Certificate; + + /** + * @param {StakeRegistrationAndDelegation} stake_registration_and_delegation + * @returns {Certificate} + */ + static new_stake_registration_and_delegation( + stake_registration_and_delegation: StakeRegistrationAndDelegation + ): Certificate; + + /** + * @param {StakeVoteRegistrationAndDelegation} stake_vote_registration_and_delegation + * @returns {Certificate} + */ + static new_stake_vote_registration_and_delegation( + stake_vote_registration_and_delegation: StakeVoteRegistrationAndDelegation + ): Certificate; + + /** + * @param {VoteDelegation} vote_delegation + * @returns {Certificate} + */ + static new_vote_delegation(vote_delegation: VoteDelegation): Certificate; + + /** + * @param {VoteRegistrationAndDelegation} vote_registration_and_delegation + * @returns {Certificate} */ - kind(): number; + static new_vote_registration_and_delegation( + vote_registration_and_delegation: VoteRegistrationAndDelegation + ): Certificate; + + /** + * @returns {$Values< + typeof + CertificateKind>} + */ + kind(): $Values; /** * @returns {StakeRegistration | void} */ as_stake_registration(): StakeRegistration | void; + /** + * Since StakeRegistration can represent stake_registration certificate or reg_cert certificate, because both certificates have the same semantics. + * And in some cases you want to get a reg_cert, this function is used to get a reg_cert. + * The function will return None if StakeRegistration represents a stake_registration certificate or Certificate is not a StakeRegistration. + * @returns {StakeRegistration | void} + */ + as_reg_cert(): StakeRegistration | void; + /** * @returns {StakeDeregistration | void} */ as_stake_deregistration(): StakeDeregistration | void; + /** + * Since StakeDeregistration can represent stake_deregistration certificate or unreg_cert certificate, because both certificates have the same semantics. + * And in some cases you want to get an unreg_cert, this function is used to get an unreg_cert. + * The function will return None if StakeDeregistration represents a stake_deregistration certificate or Certificate is not a StakeDeregistration. + * @returns {StakeDeregistration | void} + */ + as_unreg_cert(): StakeDeregistration | void; + /** * @returns {StakeDelegation | void} */ @@ -1723,6 +2113,56 @@ declare export class Certificate { */ as_move_instantaneous_rewards_cert(): MoveInstantaneousRewardsCert | void; + /** + * @returns {CommitteeHotAuth | void} + */ + as_committee_hot_auth(): CommitteeHotAuth | void; + + /** + * @returns {CommitteeColdResign | void} + */ + as_committee_cold_resign(): CommitteeColdResign | void; + + /** + * @returns {DRepDeregistration | void} + */ + as_drep_deregistration(): DRepDeregistration | void; + + /** + * @returns {DRepRegistration | void} + */ + as_drep_registration(): DRepRegistration | void; + + /** + * @returns {DRepUpdate | void} + */ + as_drep_update(): DRepUpdate | void; + + /** + * @returns {StakeAndVoteDelegation | void} + */ + as_stake_and_vote_delegation(): StakeAndVoteDelegation | void; + + /** + * @returns {StakeRegistrationAndDelegation | void} + */ + as_stake_registration_and_delegation(): StakeRegistrationAndDelegation | void; + + /** + * @returns {StakeVoteRegistrationAndDelegation | void} + */ + as_stake_vote_registration_and_delegation(): StakeVoteRegistrationAndDelegation | void; + + /** + * @returns {VoteDelegation | void} + */ + as_vote_delegation(): VoteDelegation | void; + + /** + * @returns {VoteRegistrationAndDelegation | void} + */ + as_vote_registration_and_delegation(): VoteRegistrationAndDelegation | void; + /** * @returns {boolean} */ @@ -1788,9 +2228,12 @@ declare export class Certificates { get(index: number): Certificate; /** + * Add a new `Certificate` to the set. + * Returns `true` if the element was not already present in the set. * @param {Certificate} elem + * @returns {boolean} */ - add(elem: Certificate): void; + add(elem: Certificate): boolean; } /** */ @@ -1863,51 +2306,36 @@ declare export class CertificatesBuilder { } /** */ -declare export class ConstrPlutusData { +declare export class ChangeConfig { free(): void; /** - * @returns {Uint8Array} - */ - to_bytes(): Uint8Array; - - /** - * @param {Uint8Array} bytes - * @returns {ConstrPlutusData} - */ - static from_bytes(bytes: Uint8Array): ConstrPlutusData; - - /** - * @returns {string} - */ - to_hex(): string; - - /** - * @param {string} hex_str - * @returns {ConstrPlutusData} + * @param {Address} address + * @returns {ChangeConfig} */ - static from_hex(hex_str: string): ConstrPlutusData; + static new(address: Address): ChangeConfig; /** - * @returns {BigNum} + * @param {Address} address + * @returns {ChangeConfig} */ - alternative(): BigNum; + change_address(address: Address): ChangeConfig; /** - * @returns {PlutusList} + * @param {OutputDatum} plutus_data + * @returns {ChangeConfig} */ - data(): PlutusList; + change_plutus_data(plutus_data: OutputDatum): ChangeConfig; /** - * @param {BigNum} alternative - * @param {PlutusList} data - * @returns {ConstrPlutusData} + * @param {ScriptRef} script_ref + * @returns {ChangeConfig} */ - static new(alternative: BigNum, data: PlutusList): ConstrPlutusData; + change_script_ref(script_ref: ScriptRef): ChangeConfig; } /** */ -declare export class CostModel { +declare export class Committee { free(): void; /** @@ -1917,9 +2345,9 @@ declare export class CostModel { /** * @param {Uint8Array} bytes - * @returns {CostModel} + * @returns {Committee} */ - static from_bytes(bytes: Uint8Array): CostModel; + static from_bytes(bytes: Uint8Array): Committee; /** * @returns {string} @@ -1928,9 +2356,9 @@ declare export class CostModel { /** * @param {string} hex_str - * @returns {CostModel} + * @returns {Committee} */ - static from_hex(hex_str: string): CostModel; + static from_hex(hex_str: string): Committee; /** * @returns {string} @@ -1938,46 +2366,47 @@ declare export class CostModel { to_json(): string; /** - * @returns {CostModelJSON} + * @returns {CommitteeJSON} */ - to_js_value(): CostModelJSON; + to_js_value(): CommitteeJSON; /** * @param {string} json - * @returns {CostModel} + * @returns {Committee} */ - static from_json(json: string): CostModel; + static from_json(json: string): Committee; /** - * Creates a new CostModels instance of an unrestricted length - * @returns {CostModel} + * @param {UnitInterval} quorum_threshold + * @returns {Committee} */ - static new(): CostModel; + static new(quorum_threshold: UnitInterval): Committee; /** - * Sets the cost at the specified index to the specified value. - * In case the operation index is larger than the previous largest used index, - * it will fill any inbetween indexes with zeroes - * @param {number} operation - * @param {Int} cost - * @returns {Int} + * @returns {Credentials} */ - set(operation: number, cost: Int): Int; + members_keys(): Credentials; /** - * @param {number} operation - * @returns {Int} + * @returns {UnitInterval} */ - get(operation: number): Int; + quorum_threshold(): UnitInterval; /** - * @returns {number} + * @param {Credential} committee_cold_credential + * @param {number} epoch */ - len(): number; + add_member(committee_cold_credential: Credential, epoch: number): void; + + /** + * @param {Credential} committee_cold_credential + * @returns {number | void} + */ + get_member_epoch(committee_cold_credential: Credential): number | void; } /** */ -declare export class Costmdls { +declare export class CommitteeColdResign { free(): void; /** @@ -1987,9 +2416,9 @@ declare export class Costmdls { /** * @param {Uint8Array} bytes - * @returns {Costmdls} + * @returns {CommitteeColdResign} */ - static from_bytes(bytes: Uint8Array): Costmdls; + static from_bytes(bytes: Uint8Array): CommitteeColdResign; /** * @returns {string} @@ -1998,9 +2427,9 @@ declare export class Costmdls { /** * @param {string} hex_str - * @returns {Costmdls} + * @returns {CommitteeColdResign} */ - static from_hex(hex_str: string): Costmdls; + static from_hex(hex_str: string): CommitteeColdResign; /** * @returns {string} @@ -2008,53 +2437,50 @@ declare export class Costmdls { to_json(): string; /** - * @returns {CostmdlsJSON} + * @returns {CommitteeColdResignJSON} */ - to_js_value(): CostmdlsJSON; + to_js_value(): CommitteeColdResignJSON; /** * @param {string} json - * @returns {Costmdls} - */ - static from_json(json: string): Costmdls; - - /** - * @returns {Costmdls} + * @returns {CommitteeColdResign} */ - static new(): Costmdls; + static from_json(json: string): CommitteeColdResign; /** - * @returns {number} + * @returns {Credential} */ - len(): number; + committee_cold_credential(): Credential; /** - * @param {Language} key - * @param {CostModel} value - * @returns {CostModel | void} + * @returns {Anchor | void} */ - insert(key: Language, value: CostModel): CostModel | void; + anchor(): Anchor | void; /** - * @param {Language} key - * @returns {CostModel | void} + * @param {Credential} committee_cold_credential + * @returns {CommitteeColdResign} */ - get(key: Language): CostModel | void; + static new(committee_cold_credential: Credential): CommitteeColdResign; /** - * @returns {Languages} + * @param {Credential} committee_cold_credential + * @param {Anchor} anchor + * @returns {CommitteeColdResign} */ - keys(): Languages; + static new_with_anchor( + committee_cold_credential: Credential, + anchor: Anchor + ): CommitteeColdResign; /** - * @param {Languages} languages - * @returns {Costmdls} + * @returns {boolean} */ - retain_language_versions(languages: Languages): Costmdls; + has_script_credentials(): boolean; } /** */ -declare export class DNSRecordAorAAAA { +declare export class CommitteeHotAuth { free(): void; /** @@ -2064,9 +2490,9 @@ declare export class DNSRecordAorAAAA { /** * @param {Uint8Array} bytes - * @returns {DNSRecordAorAAAA} + * @returns {CommitteeHotAuth} */ - static from_bytes(bytes: Uint8Array): DNSRecordAorAAAA; + static from_bytes(bytes: Uint8Array): CommitteeHotAuth; /** * @returns {string} @@ -2075,9 +2501,9 @@ declare export class DNSRecordAorAAAA { /** * @param {string} hex_str - * @returns {DNSRecordAorAAAA} + * @returns {CommitteeHotAuth} */ - static from_hex(hex_str: string): DNSRecordAorAAAA; + static from_hex(hex_str: string): CommitteeHotAuth; /** * @returns {string} @@ -2085,30 +2511,44 @@ declare export class DNSRecordAorAAAA { to_json(): string; /** - * @returns {DNSRecordAorAAAAJSON} + * @returns {CommitteeHotAuthJSON} */ - to_js_value(): DNSRecordAorAAAAJSON; + to_js_value(): CommitteeHotAuthJSON; /** * @param {string} json - * @returns {DNSRecordAorAAAA} + * @returns {CommitteeHotAuth} */ - static from_json(json: string): DNSRecordAorAAAA; + static from_json(json: string): CommitteeHotAuth; /** - * @param {string} dns_name - * @returns {DNSRecordAorAAAA} + * @returns {Credential} */ - static new(dns_name: string): DNSRecordAorAAAA; + committee_cold_credential(): Credential; /** - * @returns {string} + * @returns {Credential} */ - record(): string; + committee_hot_credential(): Credential; + + /** + * @param {Credential} committee_cold_credential + * @param {Credential} committee_hot_credential + * @returns {CommitteeHotAuth} + */ + static new( + committee_cold_credential: Credential, + committee_hot_credential: Credential + ): CommitteeHotAuth; + + /** + * @returns {boolean} + */ + has_script_credentials(): boolean; } /** */ -declare export class DNSRecordSRV { +declare export class Constitution { free(): void; /** @@ -2118,9 +2558,9 @@ declare export class DNSRecordSRV { /** * @param {Uint8Array} bytes - * @returns {DNSRecordSRV} + * @returns {Constitution} */ - static from_bytes(bytes: Uint8Array): DNSRecordSRV; + static from_bytes(bytes: Uint8Array): Constitution; /** * @returns {string} @@ -2129,9 +2569,9 @@ declare export class DNSRecordSRV { /** * @param {string} hex_str - * @returns {DNSRecordSRV} + * @returns {Constitution} */ - static from_hex(hex_str: string): DNSRecordSRV; + static from_hex(hex_str: string): Constitution; /** * @returns {string} @@ -2139,149 +2579,159 @@ declare export class DNSRecordSRV { to_json(): string; /** - * @returns {DNSRecordSRVJSON} + * @returns {ConstitutionJSON} */ - to_js_value(): DNSRecordSRVJSON; + to_js_value(): ConstitutionJSON; /** * @param {string} json - * @returns {DNSRecordSRV} + * @returns {Constitution} */ - static from_json(json: string): DNSRecordSRV; - - /** - * @param {string} dns_name - * @returns {DNSRecordSRV} - */ - static new(dns_name: string): DNSRecordSRV; + static from_json(json: string): Constitution; /** - * @returns {string} + * @returns {Anchor} */ - record(): string; -} -/** - */ -declare export class DataCost { - free(): void; + anchor(): Anchor; /** - * !!! DEPRECATED !!! - * Since babbage era we should use coins per byte. Use `.new_coins_per_byte` instead. - * @param {BigNum} coins_per_word - * @returns {DataCost} + * @returns {ScriptHash | void} */ - static new_coins_per_word(coins_per_word: BigNum): DataCost; + script_hash(): ScriptHash | void; /** - * @param {BigNum} coins_per_byte - * @returns {DataCost} + * @param {Anchor} anchor + * @returns {Constitution} */ - static new_coins_per_byte(coins_per_byte: BigNum): DataCost; + static new(anchor: Anchor): Constitution; /** - * @returns {BigNum} + * @param {Anchor} anchor + * @param {ScriptHash} script_hash + * @returns {Constitution} */ - coins_per_byte(): BigNum; + static new_with_script_hash( + anchor: Anchor, + script_hash: ScriptHash + ): Constitution; } /** */ -declare export class DataHash { +declare export class ConstrPlutusData { free(): void; /** - * @param {Uint8Array} bytes - * @returns {DataHash} + * @returns {Uint8Array} */ - static from_bytes(bytes: Uint8Array): DataHash; + to_bytes(): Uint8Array; /** - * @returns {Uint8Array} + * @param {Uint8Array} bytes + * @returns {ConstrPlutusData} */ - to_bytes(): Uint8Array; + static from_bytes(bytes: Uint8Array): ConstrPlutusData; /** - * @param {string} prefix * @returns {string} */ - to_bech32(prefix: string): string; + to_hex(): string; /** - * @param {string} bech_str - * @returns {DataHash} + * @param {string} hex_str + * @returns {ConstrPlutusData} */ - static from_bech32(bech_str: string): DataHash; + static from_hex(hex_str: string): ConstrPlutusData; /** - * @returns {string} + * @returns {BigNum} */ - to_hex(): string; + alternative(): BigNum; /** - * @param {string} hex - * @returns {DataHash} + * @returns {PlutusList} */ - static from_hex(hex: string): DataHash; + data(): PlutusList; + + /** + * @param {BigNum} alternative + * @param {PlutusList} data + * @returns {ConstrPlutusData} + */ + static new(alternative: BigNum, data: PlutusList): ConstrPlutusData; } /** */ -declare export class DatumSource { +declare export class CostModel { free(): void; /** - * @param {PlutusData} datum - * @returns {DatumSource} + * @returns {Uint8Array} */ - static new(datum: PlutusData): DatumSource; + to_bytes(): Uint8Array; /** - * @param {TransactionInput} input - * @returns {DatumSource} + * @param {Uint8Array} bytes + * @returns {CostModel} */ - static new_ref_input(input: TransactionInput): DatumSource; -} -/** - */ -declare export class Ed25519KeyHash { - free(): void; + static from_bytes(bytes: Uint8Array): CostModel; /** - * @param {Uint8Array} bytes - * @returns {Ed25519KeyHash} + * @returns {string} */ - static from_bytes(bytes: Uint8Array): Ed25519KeyHash; + to_hex(): string; /** - * @returns {Uint8Array} + * @param {string} hex_str + * @returns {CostModel} */ - to_bytes(): Uint8Array; + static from_hex(hex_str: string): CostModel; /** - * @param {string} prefix * @returns {string} */ - to_bech32(prefix: string): string; + to_json(): string; /** - * @param {string} bech_str - * @returns {Ed25519KeyHash} + * @returns {CostModelJSON} */ - static from_bech32(bech_str: string): Ed25519KeyHash; + to_js_value(): CostModelJSON; /** - * @returns {string} + * @param {string} json + * @returns {CostModel} */ - to_hex(): string; + static from_json(json: string): CostModel; /** - * @param {string} hex - * @returns {Ed25519KeyHash} + * Creates a new CostModels instance of an unrestricted length + * @returns {CostModel} */ - static from_hex(hex: string): Ed25519KeyHash; + static new(): CostModel; + + /** + * Sets the cost at the specified index to the specified value. + * In case the operation index is larger than the previous largest used index, + * it will fill any inbetween indexes with zeroes + * @param {number} operation + * @param {Int} cost + * @returns {Int} + */ + set(operation: number, cost: Int): Int; + + /** + * @param {number} operation + * @returns {Int} + */ + get(operation: number): Int; + + /** + * @returns {number} + */ + len(): number; } /** */ -declare export class Ed25519KeyHashes { +declare export class Costmdls { free(): void; /** @@ -2291,9 +2741,9 @@ declare export class Ed25519KeyHashes { /** * @param {Uint8Array} bytes - * @returns {Ed25519KeyHashes} + * @returns {Costmdls} */ - static from_bytes(bytes: Uint8Array): Ed25519KeyHashes; + static from_bytes(bytes: Uint8Array): Costmdls; /** * @returns {string} @@ -2302,9 +2752,9 @@ declare export class Ed25519KeyHashes { /** * @param {string} hex_str - * @returns {Ed25519KeyHashes} + * @returns {Costmdls} */ - static from_hex(hex_str: string): Ed25519KeyHashes; + static from_hex(hex_str: string): Costmdls; /** * @returns {string} @@ -2312,20 +2762,20 @@ declare export class Ed25519KeyHashes { to_json(): string; /** - * @returns {Ed25519KeyHashesJSON} + * @returns {CostmdlsJSON} */ - to_js_value(): Ed25519KeyHashesJSON; + to_js_value(): CostmdlsJSON; /** * @param {string} json - * @returns {Ed25519KeyHashes} + * @returns {Costmdls} */ - static from_json(json: string): Ed25519KeyHashes; + static from_json(json: string): Costmdls; /** - * @returns {Ed25519KeyHashes} + * @returns {Costmdls} */ - static new(): Ed25519KeyHashes; + static new(): Costmdls; /** * @returns {number} @@ -2333,90 +2783,109 @@ declare export class Ed25519KeyHashes { len(): number; /** - * @param {number} index - * @returns {Ed25519KeyHash} + * @param {Language} key + * @param {CostModel} value + * @returns {CostModel | void} */ - get(index: number): Ed25519KeyHash; + insert(key: Language, value: CostModel): CostModel | void; /** - * @param {Ed25519KeyHash} elem + * @param {Language} key + * @returns {CostModel | void} */ - add(elem: Ed25519KeyHash): void; + get(key: Language): CostModel | void; /** - * @returns {Ed25519KeyHashes | void} + * @returns {Languages} */ - to_option(): Ed25519KeyHashes | void; + keys(): Languages; + + /** + * @param {Languages} languages + * @returns {Costmdls} + */ + retain_language_versions(languages: Languages): Costmdls; } /** */ -declare export class Ed25519Signature { +declare export class Credential { free(): void; /** - * @returns {Uint8Array} + * @param {Ed25519KeyHash} hash + * @returns {Credential} */ - to_bytes(): Uint8Array; + static from_keyhash(hash: Ed25519KeyHash): Credential; /** - * @returns {string} + * @param {ScriptHash} hash + * @returns {Credential} */ - to_bech32(): string; + static from_scripthash(hash: ScriptHash): Credential; /** - * @returns {string} + * @returns {Ed25519KeyHash | void} */ - to_hex(): string; + to_keyhash(): Ed25519KeyHash | void; /** - * @param {string} bech32_str - * @returns {Ed25519Signature} + * @returns {ScriptHash | void} */ - static from_bech32(bech32_str: string): Ed25519Signature; + to_scripthash(): ScriptHash | void; /** - * @param {string} input - * @returns {Ed25519Signature} + * @returns {$Values< + typeof + CredKind>} + */ + kind(): $Values; + + /** + * @returns {boolean} */ - static from_hex(input: string): Ed25519Signature; + has_script_hash(): boolean; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; /** * @param {Uint8Array} bytes - * @returns {Ed25519Signature} + * @returns {Credential} */ - static from_bytes(bytes: Uint8Array): Ed25519Signature; -} -/** - */ -declare export class EnterpriseAddress { - free(): void; + static from_bytes(bytes: Uint8Array): Credential; /** - * @param {number} network - * @param {StakeCredential} payment - * @returns {EnterpriseAddress} + * @returns {string} */ - static new(network: number, payment: StakeCredential): EnterpriseAddress; + to_hex(): string; /** - * @returns {StakeCredential} + * @param {string} hex_str + * @returns {Credential} */ - payment_cred(): StakeCredential; + static from_hex(hex_str: string): Credential; /** - * @returns {Address} + * @returns {string} */ - to_address(): Address; + to_json(): string; /** - * @param {Address} addr - * @returns {EnterpriseAddress | void} + * @returns {CredentialJSON} */ - static from_address(addr: Address): EnterpriseAddress | void; + to_js_value(): CredentialJSON; + + /** + * @param {string} json + * @returns {Credential} + */ + static from_json(json: string): Credential; } /** */ -declare export class ExUnitPrices { +declare export class Credentials { free(): void; /** @@ -2426,9 +2895,9 @@ declare export class ExUnitPrices { /** * @param {Uint8Array} bytes - * @returns {ExUnitPrices} + * @returns {Credentials} */ - static from_bytes(bytes: Uint8Array): ExUnitPrices; + static from_bytes(bytes: Uint8Array): Credentials; /** * @returns {string} @@ -2437,9 +2906,9 @@ declare export class ExUnitPrices { /** * @param {string} hex_str - * @returns {ExUnitPrices} + * @returns {Credentials} */ - static from_hex(hex_str: string): ExUnitPrices; + static from_hex(hex_str: string): Credentials; /** * @returns {string} @@ -2447,36 +2916,43 @@ declare export class ExUnitPrices { to_json(): string; /** - * @returns {ExUnitPricesJSON} + * @returns {CredentialsJSON} */ - to_js_value(): ExUnitPricesJSON; + to_js_value(): CredentialsJSON; /** * @param {string} json - * @returns {ExUnitPrices} + * @returns {Credentials} */ - static from_json(json: string): ExUnitPrices; + static from_json(json: string): Credentials; /** - * @returns {UnitInterval} + * @returns {Credentials} */ - mem_price(): UnitInterval; + static new(): Credentials; /** - * @returns {UnitInterval} + * @returns {number} */ - step_price(): UnitInterval; + len(): number; /** - * @param {UnitInterval} mem_price - * @param {UnitInterval} step_price - * @returns {ExUnitPrices} + * @param {number} index + * @returns {Credential} */ - static new(mem_price: UnitInterval, step_price: UnitInterval): ExUnitPrices; + get(index: number): Credential; + + /** + * Add a new `Credential` to the set. + * Returns `true` if the element was not already present in the set. + * @param {Credential} elem + * @returns {boolean} + */ + add(elem: Credential): boolean; } /** */ -declare export class ExUnits { +declare export class DNSRecordAorAAAA { free(): void; /** @@ -2486,9 +2962,9 @@ declare export class ExUnits { /** * @param {Uint8Array} bytes - * @returns {ExUnits} + * @returns {DNSRecordAorAAAA} */ - static from_bytes(bytes: Uint8Array): ExUnits; + static from_bytes(bytes: Uint8Array): DNSRecordAorAAAA; /** * @returns {string} @@ -2497,9 +2973,9 @@ declare export class ExUnits { /** * @param {string} hex_str - * @returns {ExUnits} + * @returns {DNSRecordAorAAAA} */ - static from_hex(hex_str: string): ExUnits; + static from_hex(hex_str: string): DNSRecordAorAAAA; /** * @returns {string} @@ -2507,36 +2983,84 @@ declare export class ExUnits { to_json(): string; /** - * @returns {ExUnitsJSON} + * @returns {DNSRecordAorAAAAJSON} */ - to_js_value(): ExUnitsJSON; + to_js_value(): DNSRecordAorAAAAJSON; /** * @param {string} json - * @returns {ExUnits} + * @returns {DNSRecordAorAAAA} */ - static from_json(json: string): ExUnits; + static from_json(json: string): DNSRecordAorAAAA; /** - * @returns {BigNum} + * @param {string} dns_name + * @returns {DNSRecordAorAAAA} */ - mem(): BigNum; + static new(dns_name: string): DNSRecordAorAAAA; /** - * @returns {BigNum} + * @returns {string} */ - steps(): BigNum; + record(): string; +} +/** + */ +declare export class DNSRecordSRV { + free(): void; /** - * @param {BigNum} mem - * @param {BigNum} steps - * @returns {ExUnits} + * @returns {Uint8Array} */ - static new(mem: BigNum, steps: BigNum): ExUnits; + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {DNSRecordSRV} + */ + static from_bytes(bytes: Uint8Array): DNSRecordSRV; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {DNSRecordSRV} + */ + static from_hex(hex_str: string): DNSRecordSRV; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {DNSRecordSRVJSON} + */ + to_js_value(): DNSRecordSRVJSON; + + /** + * @param {string} json + * @returns {DNSRecordSRV} + */ + static from_json(json: string): DNSRecordSRV; + + /** + * @param {string} dns_name + * @returns {DNSRecordSRV} + */ + static new(dns_name: string): DNSRecordSRV; + + /** + * @returns {string} + */ + record(): string; } /** */ -declare export class FixedTransaction { +declare export class DRep { free(): void; /** @@ -2546,9 +3070,9 @@ declare export class FixedTransaction { /** * @param {Uint8Array} bytes - * @returns {FixedTransaction} + * @returns {DRep} */ - static from_bytes(bytes: Uint8Array): FixedTransaction; + static from_bytes(bytes: Uint8Array): DRep; /** * @returns {string} @@ -2557,94 +3081,85 @@ declare export class FixedTransaction { /** * @param {string} hex_str - * @returns {FixedTransaction} + * @returns {DRep} */ - static from_hex(hex_str: string): FixedTransaction; + static from_hex(hex_str: string): DRep; /** - * @param {Uint8Array} raw_body - * @param {Uint8Array} raw_witness_set - * @param {boolean} is_valid - * @returns {FixedTransaction} + * @returns {string} */ - static new( - raw_body: Uint8Array, - raw_witness_set: Uint8Array, - is_valid: boolean - ): FixedTransaction; + to_json(): string; /** - * @param {Uint8Array} raw_body - * @param {Uint8Array} raw_witness_set - * @param {Uint8Array} raw_auxiliary_data - * @param {boolean} is_valid - * @returns {FixedTransaction} + * @returns {DRepJSON} */ - static new_with_auxiliary( - raw_body: Uint8Array, - raw_witness_set: Uint8Array, - raw_auxiliary_data: Uint8Array, - is_valid: boolean - ): FixedTransaction; + to_js_value(): DRepJSON; /** - * @returns {TransactionBody} + * @param {string} json + * @returns {DRep} */ - body(): TransactionBody; + static from_json(json: string): DRep; /** - * @returns {Uint8Array} + * @param {Ed25519KeyHash} key_hash + * @returns {DRep} */ - raw_body(): Uint8Array; + static new_key_hash(key_hash: Ed25519KeyHash): DRep; /** - * @param {Uint8Array} raw_body + * @param {ScriptHash} script_hash + * @returns {DRep} */ - set_body(raw_body: Uint8Array): void; + static new_script_hash(script_hash: ScriptHash): DRep; /** - * @param {Uint8Array} raw_witness_set + * @returns {DRep} */ - set_witness_set(raw_witness_set: Uint8Array): void; + static new_always_abstain(): DRep; /** - * @returns {TransactionWitnessSet} + * @returns {DRep} */ - witness_set(): TransactionWitnessSet; + static new_always_no_confidence(): DRep; /** - * @returns {Uint8Array} + * @param {Credential} cred + * @returns {DRep} */ - raw_witness_set(): Uint8Array; + static new_from_credential(cred: Credential): DRep; /** - * @param {boolean} valid - */ - set_is_valid(valid: boolean): void; + * @returns {$Values< + typeof + DRepKind>} + */ + kind(): $Values; /** - * @returns {boolean} + * @returns {Ed25519KeyHash | void} */ - is_valid(): boolean; + to_key_hash(): Ed25519KeyHash | void; /** - * @param {Uint8Array} raw_auxiliary_data + * @returns {ScriptHash | void} */ - set_auxiliary_data(raw_auxiliary_data: Uint8Array): void; + to_script_hash(): ScriptHash | void; /** - * @returns {AuxiliaryData | void} + * @returns {string} */ - auxiliary_data(): AuxiliaryData | void; + to_bech32(): string; /** - * @returns {Uint8Array | void} + * @param {string} bech32_str + * @returns {DRep} */ - raw_auxiliary_data(): Uint8Array | void; + static from_bech32(bech32_str: string): DRep; } /** */ -declare export class GeneralTransactionMetadata { +declare export class DRepDeregistration { free(): void; /** @@ -2654,9 +3169,9 @@ declare export class GeneralTransactionMetadata { /** * @param {Uint8Array} bytes - * @returns {GeneralTransactionMetadata} + * @returns {DRepDeregistration} */ - static from_bytes(bytes: Uint8Array): GeneralTransactionMetadata; + static from_bytes(bytes: Uint8Array): DRepDeregistration; /** * @returns {string} @@ -2665,9 +3180,9 @@ declare export class GeneralTransactionMetadata { /** * @param {string} hex_str - * @returns {GeneralTransactionMetadata} + * @returns {DRepDeregistration} */ - static from_hex(hex_str: string): GeneralTransactionMetadata; + static from_hex(hex_str: string): DRepDeregistration; /** * @returns {string} @@ -2675,125 +3190,123 @@ declare export class GeneralTransactionMetadata { to_json(): string; /** - * @returns {GeneralTransactionMetadataJSON} + * @returns {DRepDeregistrationJSON} */ - to_js_value(): GeneralTransactionMetadataJSON; + to_js_value(): DRepDeregistrationJSON; /** * @param {string} json - * @returns {GeneralTransactionMetadata} - */ - static from_json(json: string): GeneralTransactionMetadata; - - /** - * @returns {GeneralTransactionMetadata} + * @returns {DRepDeregistration} */ - static new(): GeneralTransactionMetadata; + static from_json(json: string): DRepDeregistration; /** - * @returns {number} + * @returns {Credential} */ - len(): number; + voting_credential(): Credential; /** - * @param {BigNum} key - * @param {TransactionMetadatum} value - * @returns {TransactionMetadatum | void} + * @returns {BigNum} */ - insert(key: BigNum, value: TransactionMetadatum): TransactionMetadatum | void; + coin(): BigNum; /** - * @param {BigNum} key - * @returns {TransactionMetadatum | void} + * @param {Credential} voting_credential + * @param {BigNum} coin + * @returns {DRepDeregistration} */ - get(key: BigNum): TransactionMetadatum | void; + static new(voting_credential: Credential, coin: BigNum): DRepDeregistration; /** - * @returns {TransactionMetadatumLabels} + * @returns {boolean} */ - keys(): TransactionMetadatumLabels; + has_script_credentials(): boolean; } /** */ -declare export class GenesisDelegateHash { +declare export class DRepRegistration { free(): void; /** - * @param {Uint8Array} bytes - * @returns {GenesisDelegateHash} + * @returns {Uint8Array} */ - static from_bytes(bytes: Uint8Array): GenesisDelegateHash; + to_bytes(): Uint8Array; /** - * @returns {Uint8Array} + * @param {Uint8Array} bytes + * @returns {DRepRegistration} */ - to_bytes(): Uint8Array; + static from_bytes(bytes: Uint8Array): DRepRegistration; /** - * @param {string} prefix * @returns {string} */ - to_bech32(prefix: string): string; + to_hex(): string; /** - * @param {string} bech_str - * @returns {GenesisDelegateHash} + * @param {string} hex_str + * @returns {DRepRegistration} */ - static from_bech32(bech_str: string): GenesisDelegateHash; + static from_hex(hex_str: string): DRepRegistration; /** * @returns {string} */ - to_hex(): string; + to_json(): string; /** - * @param {string} hex - * @returns {GenesisDelegateHash} + * @returns {DRepRegistrationJSON} */ - static from_hex(hex: string): GenesisDelegateHash; -} -/** - */ -declare export class GenesisHash { - free(): void; + to_js_value(): DRepRegistrationJSON; /** - * @param {Uint8Array} bytes - * @returns {GenesisHash} + * @param {string} json + * @returns {DRepRegistration} */ - static from_bytes(bytes: Uint8Array): GenesisHash; + static from_json(json: string): DRepRegistration; /** - * @returns {Uint8Array} + * @returns {Credential} */ - to_bytes(): Uint8Array; + voting_credential(): Credential; /** - * @param {string} prefix - * @returns {string} + * @returns {BigNum} */ - to_bech32(prefix: string): string; + coin(): BigNum; /** - * @param {string} bech_str - * @returns {GenesisHash} + * @returns {Anchor | void} */ - static from_bech32(bech_str: string): GenesisHash; + anchor(): Anchor | void; /** - * @returns {string} + * @param {Credential} voting_credential + * @param {BigNum} coin + * @returns {DRepRegistration} */ - to_hex(): string; + static new(voting_credential: Credential, coin: BigNum): DRepRegistration; /** - * @param {string} hex - * @returns {GenesisHash} + * @param {Credential} voting_credential + * @param {BigNum} coin + * @param {Anchor} anchor + * @returns {DRepRegistration} */ - static from_hex(hex: string): GenesisHash; + static new_with_anchor( + voting_credential: Credential, + coin: BigNum, + anchor: Anchor + ): DRepRegistration; + + /** + * @returns {boolean} + */ + has_script_credentials(): boolean; } /** */ -declare export class GenesisHashes { +declare export class DRepUpdate { free(): void; /** @@ -2803,9 +3316,9 @@ declare export class GenesisHashes { /** * @param {Uint8Array} bytes - * @returns {GenesisHashes} + * @returns {DRepUpdate} */ - static from_bytes(bytes: Uint8Array): GenesisHashes; + static from_bytes(bytes: Uint8Array): DRepUpdate; /** * @returns {string} @@ -2814,9 +3327,9 @@ declare export class GenesisHashes { /** * @param {string} hex_str - * @returns {GenesisHashes} + * @returns {DRepUpdate} */ - static from_hex(hex_str: string): GenesisHashes; + static from_hex(hex_str: string): DRepUpdate; /** * @returns {string} @@ -2824,40 +3337,50 @@ declare export class GenesisHashes { to_json(): string; /** - * @returns {GenesisHashesJSON} + * @returns {DRepUpdateJSON} */ - to_js_value(): GenesisHashesJSON; + to_js_value(): DRepUpdateJSON; /** * @param {string} json - * @returns {GenesisHashes} + * @returns {DRepUpdate} */ - static from_json(json: string): GenesisHashes; + static from_json(json: string): DRepUpdate; /** - * @returns {GenesisHashes} + * @returns {Credential} */ - static new(): GenesisHashes; + voting_credential(): Credential; /** - * @returns {number} + * @returns {Anchor | void} */ - len(): number; + anchor(): Anchor | void; /** - * @param {number} index - * @returns {GenesisHash} + * @param {Credential} voting_credential + * @returns {DRepUpdate} */ - get(index: number): GenesisHash; + static new(voting_credential: Credential): DRepUpdate; /** - * @param {GenesisHash} elem + * @param {Credential} voting_credential + * @param {Anchor} anchor + * @returns {DRepUpdate} */ - add(elem: GenesisHash): void; + static new_with_anchor( + voting_credential: Credential, + anchor: Anchor + ): DRepUpdate; + + /** + * @returns {boolean} + */ + has_script_credentials(): boolean; } /** */ -declare export class GenesisKeyDelegation { +declare export class DRepVotingThresholds { free(): void; /** @@ -2867,9 +3390,9 @@ declare export class GenesisKeyDelegation { /** * @param {Uint8Array} bytes - * @returns {GenesisKeyDelegation} + * @returns {DRepVotingThresholds} */ - static from_bytes(bytes: Uint8Array): GenesisKeyDelegation; + static from_bytes(bytes: Uint8Array): DRepVotingThresholds; /** * @returns {string} @@ -2878,9 +3401,9 @@ declare export class GenesisKeyDelegation { /** * @param {string} hex_str - * @returns {GenesisKeyDelegation} + * @returns {DRepVotingThresholds} */ - static from_hex(hex_str: string): GenesisKeyDelegation; + static from_hex(hex_str: string): DRepVotingThresholds; /** * @returns {string} @@ -2888,469 +3411,405 @@ declare export class GenesisKeyDelegation { to_json(): string; /** - * @returns {GenesisKeyDelegationJSON} + * @returns {DRepVotingThresholdsJSON} */ - to_js_value(): GenesisKeyDelegationJSON; + to_js_value(): DRepVotingThresholdsJSON; /** * @param {string} json - * @returns {GenesisKeyDelegation} + * @returns {DRepVotingThresholds} */ - static from_json(json: string): GenesisKeyDelegation; + static from_json(json: string): DRepVotingThresholds; /** - * @returns {GenesisHash} + * @param {UnitInterval} motion_no_confidence + * @param {UnitInterval} committee_normal + * @param {UnitInterval} committee_no_confidence + * @param {UnitInterval} update_constitution + * @param {UnitInterval} hard_fork_initiation + * @param {UnitInterval} pp_network_group + * @param {UnitInterval} pp_economic_group + * @param {UnitInterval} pp_technical_group + * @param {UnitInterval} pp_governance_group + * @param {UnitInterval} treasury_withdrawal + * @returns {DRepVotingThresholds} */ - genesishash(): GenesisHash; + static new( + motion_no_confidence: UnitInterval, + committee_normal: UnitInterval, + committee_no_confidence: UnitInterval, + update_constitution: UnitInterval, + hard_fork_initiation: UnitInterval, + pp_network_group: UnitInterval, + pp_economic_group: UnitInterval, + pp_technical_group: UnitInterval, + pp_governance_group: UnitInterval, + treasury_withdrawal: UnitInterval + ): DRepVotingThresholds; /** - * @returns {GenesisDelegateHash} + * @param {UnitInterval} motion_no_confidence */ - genesis_delegate_hash(): GenesisDelegateHash; + set_motion_no_confidence(motion_no_confidence: UnitInterval): void; /** - * @returns {VRFKeyHash} + * @param {UnitInterval} committee_normal */ - vrf_keyhash(): VRFKeyHash; + set_committee_normal(committee_normal: UnitInterval): void; /** - * @param {GenesisHash} genesishash - * @param {GenesisDelegateHash} genesis_delegate_hash - * @param {VRFKeyHash} vrf_keyhash - * @returns {GenesisKeyDelegation} - */ - static new( - genesishash: GenesisHash, - genesis_delegate_hash: GenesisDelegateHash, - vrf_keyhash: VRFKeyHash - ): GenesisKeyDelegation; -} -/** - */ -declare export class Header { - free(): void; - - /** - * @returns {Uint8Array} + * @param {UnitInterval} committee_no_confidence */ - to_bytes(): Uint8Array; + set_committee_no_confidence(committee_no_confidence: UnitInterval): void; /** - * @param {Uint8Array} bytes - * @returns {Header} + * @param {UnitInterval} update_constitution */ - static from_bytes(bytes: Uint8Array): Header; + set_update_constitution(update_constitution: UnitInterval): void; /** - * @returns {string} + * @param {UnitInterval} hard_fork_initiation */ - to_hex(): string; + set_hard_fork_initiation(hard_fork_initiation: UnitInterval): void; /** - * @param {string} hex_str - * @returns {Header} + * @param {UnitInterval} pp_network_group */ - static from_hex(hex_str: string): Header; + set_pp_network_group(pp_network_group: UnitInterval): void; /** - * @returns {string} + * @param {UnitInterval} pp_economic_group */ - to_json(): string; + set_pp_economic_group(pp_economic_group: UnitInterval): void; /** - * @returns {HeaderJSON} + * @param {UnitInterval} pp_technical_group */ - to_js_value(): HeaderJSON; + set_pp_technical_group(pp_technical_group: UnitInterval): void; /** - * @param {string} json - * @returns {Header} + * @param {UnitInterval} pp_governance_group */ - static from_json(json: string): Header; + set_pp_governance_group(pp_governance_group: UnitInterval): void; /** - * @returns {HeaderBody} + * @param {UnitInterval} treasury_withdrawal */ - header_body(): HeaderBody; + set_treasury_withdrawal(treasury_withdrawal: UnitInterval): void; /** - * @returns {KESSignature} + * @returns {UnitInterval} */ - body_signature(): KESSignature; + motion_no_confidence(): UnitInterval; /** - * @param {HeaderBody} header_body - * @param {KESSignature} body_signature - * @returns {Header} + * @returns {UnitInterval} */ - static new(header_body: HeaderBody, body_signature: KESSignature): Header; -} -/** - */ -declare export class HeaderBody { - free(): void; + committee_normal(): UnitInterval; /** - * @returns {Uint8Array} + * @returns {UnitInterval} */ - to_bytes(): Uint8Array; + committee_no_confidence(): UnitInterval; /** - * @param {Uint8Array} bytes - * @returns {HeaderBody} + * @returns {UnitInterval} */ - static from_bytes(bytes: Uint8Array): HeaderBody; + update_constitution(): UnitInterval; /** - * @returns {string} + * @returns {UnitInterval} */ - to_hex(): string; + hard_fork_initiation(): UnitInterval; /** - * @param {string} hex_str - * @returns {HeaderBody} + * @returns {UnitInterval} */ - static from_hex(hex_str: string): HeaderBody; + pp_network_group(): UnitInterval; /** - * @returns {string} + * @returns {UnitInterval} */ - to_json(): string; + pp_economic_group(): UnitInterval; /** - * @returns {HeaderBodyJSON} + * @returns {UnitInterval} */ - to_js_value(): HeaderBodyJSON; + pp_technical_group(): UnitInterval; /** - * @param {string} json - * @returns {HeaderBody} + * @returns {UnitInterval} */ - static from_json(json: string): HeaderBody; + pp_governance_group(): UnitInterval; /** - * @returns {number} + * @returns {UnitInterval} */ - block_number(): number; + treasury_withdrawal(): UnitInterval; +} +/** + */ +declare export class DataCost { + free(): void; /** - * !!! DEPRECATED !!! - * Returns a Slot32 (u32) value in case the underlying original BigNum (u64) value is within the limits. - * Otherwise will just raise an error. - * @returns {number} + * @param {BigNum} coins_per_byte + * @returns {DataCost} */ - slot(): number; + static new_coins_per_byte(coins_per_byte: BigNum): DataCost; /** * @returns {BigNum} */ - slot_bignum(): BigNum; + coins_per_byte(): BigNum; +} +/** + */ +declare export class DataHash { + free(): void; /** - * @returns {BlockHash | void} + * @param {Uint8Array} bytes + * @returns {DataHash} */ - prev_hash(): BlockHash | void; + static from_bytes(bytes: Uint8Array): DataHash; /** - * @returns {Vkey} + * @returns {Uint8Array} */ - issuer_vkey(): Vkey; + to_bytes(): Uint8Array; /** - * @returns {VRFVKey} + * @param {string} prefix + * @returns {string} */ - vrf_vkey(): VRFVKey; + to_bech32(prefix: string): string; /** - * If this function returns true, the `.nonce_vrf_or_nothing` - * and the `.leader_vrf_or_nothing` functions will return - * non-empty results - * @returns {boolean} + * @param {string} bech_str + * @returns {DataHash} */ - has_nonce_and_leader_vrf(): boolean; + static from_bech32(bech_str: string): DataHash; /** - * Might return nothing in case `.has_nonce_and_leader_vrf` returns false - * @returns {VRFCert | void} + * @returns {string} */ - nonce_vrf_or_nothing(): VRFCert | void; + to_hex(): string; /** - * Might return nothing in case `.has_nonce_and_leader_vrf` returns false - * @returns {VRFCert | void} + * @param {string} hex + * @returns {DataHash} */ - leader_vrf_or_nothing(): VRFCert | void; + static from_hex(hex: string): DataHash; +} +/** + */ +declare export class DatumSource { + free(): void; /** - * If this function returns true, the `.vrf_result_or_nothing` - * function will return a non-empty result - * @returns {boolean} + * @param {PlutusData} datum + * @returns {DatumSource} */ - has_vrf_result(): boolean; + static new(datum: PlutusData): DatumSource; /** - * Might return nothing in case `.has_vrf_result` returns false - * @returns {VRFCert | void} + * @param {TransactionInput} input + * @returns {DatumSource} */ - vrf_result_or_nothing(): VRFCert | void; + static new_ref_input(input: TransactionInput): DatumSource; +} +/** + */ +declare export class Ed25519KeyHash { + free(): void; /** - * @returns {number} + * @param {Uint8Array} bytes + * @returns {Ed25519KeyHash} */ - block_body_size(): number; + static from_bytes(bytes: Uint8Array): Ed25519KeyHash; /** - * @returns {BlockHash} + * @returns {Uint8Array} */ - block_body_hash(): BlockHash; + to_bytes(): Uint8Array; /** - * @returns {OperationalCert} + * @param {string} prefix + * @returns {string} */ - operational_cert(): OperationalCert; + to_bech32(prefix: string): string; /** - * @returns {ProtocolVersion} + * @param {string} bech_str + * @returns {Ed25519KeyHash} */ - protocol_version(): ProtocolVersion; + static from_bech32(bech_str: string): Ed25519KeyHash; /** - * !!! DEPRECATED !!! - * This constructor uses outdated slot number format. - * Use `.new_headerbody` instead - * @param {number} block_number - * @param {number} slot - * @param {BlockHash | void} prev_hash - * @param {Vkey} issuer_vkey - * @param {VRFVKey} vrf_vkey - * @param {VRFCert} vrf_result - * @param {number} block_body_size - * @param {BlockHash} block_body_hash - * @param {OperationalCert} operational_cert - * @param {ProtocolVersion} protocol_version - * @returns {HeaderBody} + * @returns {string} */ - static new( - block_number: number, - slot: number, - prev_hash: BlockHash | void, - issuer_vkey: Vkey, - vrf_vkey: VRFVKey, - vrf_result: VRFCert, - block_body_size: number, - block_body_hash: BlockHash, - operational_cert: OperationalCert, - protocol_version: ProtocolVersion - ): HeaderBody; + to_hex(): string; /** - * @param {number} block_number - * @param {BigNum} slot - * @param {BlockHash | void} prev_hash - * @param {Vkey} issuer_vkey - * @param {VRFVKey} vrf_vkey - * @param {VRFCert} vrf_result - * @param {number} block_body_size - * @param {BlockHash} block_body_hash - * @param {OperationalCert} operational_cert - * @param {ProtocolVersion} protocol_version - * @returns {HeaderBody} + * @param {string} hex + * @returns {Ed25519KeyHash} */ - static new_headerbody( - block_number: number, - slot: BigNum, - prev_hash: BlockHash | void, - issuer_vkey: Vkey, - vrf_vkey: VRFVKey, - vrf_result: VRFCert, - block_body_size: number, - block_body_hash: BlockHash, - operational_cert: OperationalCert, - protocol_version: ProtocolVersion - ): HeaderBody; + static from_hex(hex: string): Ed25519KeyHash; } /** */ -declare export class InputWithScriptWitness { +declare export class Ed25519KeyHashes { free(): void; /** - * @param {TransactionInput} input - * @param {NativeScript} witness - * @returns {InputWithScriptWitness} + * @returns {Uint8Array} */ - static new_with_native_script_witness( - input: TransactionInput, - witness: NativeScript - ): InputWithScriptWitness; + to_bytes(): Uint8Array; /** - * @param {TransactionInput} input - * @param {PlutusWitness} witness - * @returns {InputWithScriptWitness} + * @param {Uint8Array} bytes + * @returns {Ed25519KeyHashes} */ - static new_with_plutus_witness( - input: TransactionInput, - witness: PlutusWitness - ): InputWithScriptWitness; + static from_bytes(bytes: Uint8Array): Ed25519KeyHashes; /** - * @returns {TransactionInput} + * @returns {string} */ - input(): TransactionInput; -} -/** - */ -declare export class InputsWithScriptWitness { - free(): void; + to_hex(): string; /** - * @returns {InputsWithScriptWitness} + * @param {string} hex_str + * @returns {Ed25519KeyHashes} */ - static new(): InputsWithScriptWitness; + static from_hex(hex_str: string): Ed25519KeyHashes; /** - * @param {InputWithScriptWitness} input + * @returns {string} */ - add(input: InputWithScriptWitness): void; + to_json(): string; /** - * @param {number} index - * @returns {InputWithScriptWitness} + * @returns {Ed25519KeyHashesJSON} */ - get(index: number): InputWithScriptWitness; + to_js_value(): Ed25519KeyHashesJSON; /** - * @returns {number} + * @param {string} json + * @returns {Ed25519KeyHashes} */ - len(): number; -} -/** - */ -declare export class Int { - free(): void; - - /** - * @returns {Uint8Array} - */ - to_bytes(): Uint8Array; + static from_json(json: string): Ed25519KeyHashes; /** - * @param {Uint8Array} bytes - * @returns {Int} + * @returns {Ed25519KeyHashes} */ - static from_bytes(bytes: Uint8Array): Int; + static new(): Ed25519KeyHashes; /** - * @returns {string} + * @returns {number} */ - to_hex(): string; + len(): number; /** - * @param {string} hex_str - * @returns {Int} + * @param {number} index + * @returns {Ed25519KeyHash} */ - static from_hex(hex_str: string): Int; + get(index: number): Ed25519KeyHash; /** - * @returns {string} + * Add a new `Ed25519KeyHash` to the set. + * Returns `true` if the element was not already present in the set. + * @param {Ed25519KeyHash} elem + * @returns {boolean} */ - to_json(): string; + add(elem: Ed25519KeyHash): boolean; /** - * @returns {IntJSON} + * @param {Ed25519KeyHash} elem + * @returns {boolean} */ - to_js_value(): IntJSON; + contains(elem: Ed25519KeyHash): boolean; /** - * @param {string} json - * @returns {Int} + * @returns {Ed25519KeyHashes | void} */ - static from_json(json: string): Int; + to_option(): Ed25519KeyHashes | void; +} +/** + */ +declare export class Ed25519Signature { + free(): void; /** - * @param {BigNum} x - * @returns {Int} + * @returns {Uint8Array} */ - static new(x: BigNum): Int; + to_bytes(): Uint8Array; /** - * @param {BigNum} x - * @returns {Int} + * @returns {string} */ - static new_negative(x: BigNum): Int; + to_bech32(): string; /** - * @param {number} x - * @returns {Int} + * @returns {string} */ - static new_i32(x: number): Int; + to_hex(): string; /** - * @returns {boolean} + * @param {string} bech32_str + * @returns {Ed25519Signature} */ - is_positive(): boolean; + static from_bech32(bech32_str: string): Ed25519Signature; /** - * BigNum can only contain unsigned u64 values - * - * This function will return the BigNum representation - * only in case the underlying i128 value is positive. - * - * Otherwise nothing will be returned (undefined). - * @returns {BigNum | void} + * @param {string} input + * @returns {Ed25519Signature} */ - as_positive(): BigNum | void; + static from_hex(input: string): Ed25519Signature; /** - * BigNum can only contain unsigned u64 values - * - * This function will return the *absolute* BigNum representation - * only in case the underlying i128 value is negative. - * - * Otherwise nothing will be returned (undefined). - * @returns {BigNum | void} + * @param {Uint8Array} bytes + * @returns {Ed25519Signature} */ - as_negative(): BigNum | void; + static from_bytes(bytes: Uint8Array): Ed25519Signature; +} +/** + */ +declare export class EnterpriseAddress { + free(): void; /** - * !!! DEPRECATED !!! - * Returns an i32 value in case the underlying original i128 value is within the limits. - * Otherwise will just return an empty value (undefined). - * @returns {number | void} + * @param {number} network + * @param {Credential} payment + * @returns {EnterpriseAddress} */ - as_i32(): number | void; + static new(network: number, payment: Credential): EnterpriseAddress; /** - * Returns the underlying value converted to i32 if possible (within limits) - * Otherwise will just return an empty value (undefined). - * @returns {number | void} + * @returns {Credential} */ - as_i32_or_nothing(): number | void; + payment_cred(): Credential; /** - * Returns the underlying value converted to i32 if possible (within limits) - * JsError in case of out of boundary overflow - * @returns {number} + * @returns {Address} */ - as_i32_or_fail(): number; + to_address(): Address; /** - * Returns string representation of the underlying i128 value directly. - * Might contain the minus sign (-) in case of negative value. - * @returns {string} + * @param {Address} addr + * @returns {EnterpriseAddress | void} */ - to_str(): string; + static from_address(addr: Address): EnterpriseAddress | void; /** - * @param {string} string - * @returns {Int} + * @returns {number} */ - static from_str(string: string): Int; + network_id(): number; } /** */ -declare export class Ipv4 { +declare export class ExUnitPrices { free(): void; /** @@ -3360,9 +3819,9 @@ declare export class Ipv4 { /** * @param {Uint8Array} bytes - * @returns {Ipv4} + * @returns {ExUnitPrices} */ - static from_bytes(bytes: Uint8Array): Ipv4; + static from_bytes(bytes: Uint8Array): ExUnitPrices; /** * @returns {string} @@ -3371,9 +3830,9 @@ declare export class Ipv4 { /** * @param {string} hex_str - * @returns {Ipv4} + * @returns {ExUnitPrices} */ - static from_hex(hex_str: string): Ipv4; + static from_hex(hex_str: string): ExUnitPrices; /** * @returns {string} @@ -3381,30 +3840,36 @@ declare export class Ipv4 { to_json(): string; /** - * @returns {Ipv4JSON} + * @returns {ExUnitPricesJSON} */ - to_js_value(): Ipv4JSON; + to_js_value(): ExUnitPricesJSON; /** * @param {string} json - * @returns {Ipv4} + * @returns {ExUnitPrices} */ - static from_json(json: string): Ipv4; + static from_json(json: string): ExUnitPrices; /** - * @param {Uint8Array} data - * @returns {Ipv4} + * @returns {UnitInterval} */ - static new(data: Uint8Array): Ipv4; + mem_price(): UnitInterval; /** - * @returns {Uint8Array} + * @returns {UnitInterval} */ - ip(): Uint8Array; + step_price(): UnitInterval; + + /** + * @param {UnitInterval} mem_price + * @param {UnitInterval} step_price + * @returns {ExUnitPrices} + */ + static new(mem_price: UnitInterval, step_price: UnitInterval): ExUnitPrices; } /** */ -declare export class Ipv6 { +declare export class ExUnits { free(): void; /** @@ -3414,9 +3879,9 @@ declare export class Ipv6 { /** * @param {Uint8Array} bytes - * @returns {Ipv6} + * @returns {ExUnits} */ - static from_bytes(bytes: Uint8Array): Ipv6; + static from_bytes(bytes: Uint8Array): ExUnits; /** * @returns {string} @@ -3425,9 +3890,9 @@ declare export class Ipv6 { /** * @param {string} hex_str - * @returns {Ipv6} + * @returns {ExUnits} */ - static from_hex(hex_str: string): Ipv6; + static from_hex(hex_str: string): ExUnits; /** * @returns {string} @@ -3435,85 +3900,85 @@ declare export class Ipv6 { to_json(): string; /** - * @returns {Ipv6JSON} + * @returns {ExUnitsJSON} */ - to_js_value(): Ipv6JSON; + to_js_value(): ExUnitsJSON; /** * @param {string} json - * @returns {Ipv6} + * @returns {ExUnits} */ - static from_json(json: string): Ipv6; + static from_json(json: string): ExUnits; /** - * @param {Uint8Array} data - * @returns {Ipv6} + * @returns {BigNum} */ - static new(data: Uint8Array): Ipv6; + mem(): BigNum; /** - * @returns {Uint8Array} + * @returns {BigNum} */ - ip(): Uint8Array; + steps(): BigNum; + + /** + * @param {BigNum} mem + * @param {BigNum} steps + * @returns {ExUnits} + */ + static new(mem: BigNum, steps: BigNum): ExUnits; } /** + * Read only view of a block with more strict structs for hash sensitive structs. + * Warning: This is experimental and may be removed or changed in the future. */ -declare export class KESSignature { +declare export class FixedBlock { free(): void; /** - * @returns {Uint8Array} + * @param {Uint8Array} bytes + * @returns {FixedBlock} */ - to_bytes(): Uint8Array; + static from_bytes(bytes: Uint8Array): FixedBlock; /** - * @param {Uint8Array} bytes - * @returns {KESSignature} + * @param {string} hex_str + * @returns {FixedBlock} */ - static from_bytes(bytes: Uint8Array): KESSignature; -} -/** - */ -declare export class KESVKey { - free(): void; + static from_hex(hex_str: string): FixedBlock; /** - * @param {Uint8Array} bytes - * @returns {KESVKey} + * @returns {Header} */ - static from_bytes(bytes: Uint8Array): KESVKey; + header(): Header; /** - * @returns {Uint8Array} + * @returns {FixedTransactionBodies} */ - to_bytes(): Uint8Array; + transaction_bodies(): FixedTransactionBodies; /** - * @param {string} prefix - * @returns {string} + * @returns {TransactionWitnessSets} */ - to_bech32(prefix: string): string; + transaction_witness_sets(): TransactionWitnessSets; /** - * @param {string} bech_str - * @returns {KESVKey} + * @returns {AuxiliaryDataSet} */ - static from_bech32(bech_str: string): KESVKey; + auxiliary_data_set(): AuxiliaryDataSet; /** - * @returns {string} + * @returns {Uint32Array} */ - to_hex(): string; + invalid_transactions(): Uint32Array; /** - * @param {string} hex - * @returns {KESVKey} + * @returns {BlockHash} */ - static from_hex(hex: string): KESVKey; + block_hash(): BlockHash; } /** */ -declare export class Language { +declare export class FixedTransaction { free(): void; /** @@ -3523,9 +3988,9 @@ declare export class Language { /** * @param {Uint8Array} bytes - * @returns {Language} + * @returns {FixedTransaction} */ - static from_bytes(bytes: Uint8Array): Language; + static from_bytes(bytes: Uint8Array): FixedTransaction; /** * @returns {string} @@ -3534,118 +3999,197 @@ declare export class Language { /** * @param {string} hex_str - * @returns {Language} + * @returns {FixedTransaction} */ - static from_hex(hex_str: string): Language; + static from_hex(hex_str: string): FixedTransaction; /** - * @returns {string} + * @param {Uint8Array} raw_body + * @param {Uint8Array} raw_witness_set + * @param {boolean} is_valid + * @returns {FixedTransaction} */ - to_json(): string; + static new( + raw_body: Uint8Array, + raw_witness_set: Uint8Array, + is_valid: boolean + ): FixedTransaction; /** - * @returns {LanguageJSON} + * @param {Uint8Array} raw_body + * @param {Uint8Array} raw_witness_set + * @param {Uint8Array} raw_auxiliary_data + * @param {boolean} is_valid + * @returns {FixedTransaction} */ - to_js_value(): LanguageJSON; - - /** - * @param {string} json - * @returns {Language} + static new_with_auxiliary( + raw_body: Uint8Array, + raw_witness_set: Uint8Array, + raw_auxiliary_data: Uint8Array, + is_valid: boolean + ): FixedTransaction; + + /** + * @returns {TransactionBody} */ - static from_json(json: string): Language; + body(): TransactionBody; /** - * @returns {Language} + * @returns {Uint8Array} */ - static new_plutus_v1(): Language; + raw_body(): Uint8Array; /** - * @returns {Language} + * @param {Uint8Array} raw_body */ - static new_plutus_v2(): Language; + set_body(raw_body: Uint8Array): void; /** - * @returns {number} + * @param {Uint8Array} raw_witness_set + */ + set_witness_set(raw_witness_set: Uint8Array): void; + + /** + * @returns {TransactionWitnessSet} + */ + witness_set(): TransactionWitnessSet; + + /** + * @returns {Uint8Array} + */ + raw_witness_set(): Uint8Array; + + /** + * @param {boolean} valid + */ + set_is_valid(valid: boolean): void; + + /** + * @returns {boolean} + */ + is_valid(): boolean; + + /** + * @param {Uint8Array} raw_auxiliary_data + */ + set_auxiliary_data(raw_auxiliary_data: Uint8Array): void; + + /** + * @returns {AuxiliaryData | void} + */ + auxiliary_data(): AuxiliaryData | void; + + /** + * @returns {Uint8Array | void} */ - kind(): number; + raw_auxiliary_data(): Uint8Array | void; } /** + * Warning: This is experimental and may be removed or changed in the future. */ -declare export class Languages { +declare export class FixedTransactionBodies { free(): void; /** - * @returns {Languages} + * @param {Uint8Array} bytes + * @returns {FixedTransactionBodies} */ - static new(): Languages; + static from_bytes(bytes: Uint8Array): FixedTransactionBodies; /** - * @returns {number} + * @param {string} hex_str + * @returns {FixedTransactionBodies} */ - len(): number; + static from_hex(hex_str: string): FixedTransactionBodies; /** - * @param {number} index - * @returns {Language} + * @returns {FixedTransactionBodies} */ - get(index: number): Language; + static new(): FixedTransactionBodies; /** - * @param {Language} elem + * @returns {number} */ - add(elem: Language): void; + len(): number; /** - * @returns {Languages} + * @param {number} index + * @returns {FixedTransactionBody} */ - static list(): Languages; + get(index: number): FixedTransactionBody; + + /** + * @param {FixedTransactionBody} elem + */ + add(elem: FixedTransactionBody): void; } /** + * Read-only view of a transaction body. With correct hash and original bytes. + * Warning: This is experimental and may be removed in the future. */ -declare export class LegacyDaedalusPrivateKey { +declare export class FixedTransactionBody { free(): void; /** * @param {Uint8Array} bytes - * @returns {LegacyDaedalusPrivateKey} + * @returns {FixedTransactionBody} */ - static from_bytes(bytes: Uint8Array): LegacyDaedalusPrivateKey; + static from_bytes(bytes: Uint8Array): FixedTransactionBody; /** - * @returns {Uint8Array} + * @param {string} hex_str + * @returns {FixedTransactionBody} */ - as_bytes(): Uint8Array; + static from_hex(hex_str: string): FixedTransactionBody; + + /** + * @returns {TransactionBody} + */ + transaction_body(): TransactionBody; + + /** + * @returns {TransactionHash} + */ + tx_hash(): TransactionHash; /** * @returns {Uint8Array} */ - chaincode(): Uint8Array; + original_bytes(): Uint8Array; } /** + * Warning: This is experimental and may be removed in the future. */ -declare export class LinearFee { +declare export class FixedVersionedBlock { free(): void; /** - * @returns {BigNum} + * @param {Uint8Array} bytes + * @returns {FixedVersionedBlock} */ - constant(): BigNum; + static from_bytes(bytes: Uint8Array): FixedVersionedBlock; /** - * @returns {BigNum} + * @param {string} hex_str + * @returns {FixedVersionedBlock} */ - coefficient(): BigNum; + static from_hex(hex_str: string): FixedVersionedBlock; /** - * @param {BigNum} coefficient - * @param {BigNum} constant - * @returns {LinearFee} + * @returns {FixedBlock} */ - static new(coefficient: BigNum, constant: BigNum): LinearFee; + block(): FixedBlock; + + /** + * @returns {$Values< + typeof + BlockEra>} + */ + era(): $Values; } /** */ -declare export class MIRToStakeCredentials { +declare export class GeneralTransactionMetadata { free(): void; /** @@ -3655,9 +4199,9 @@ declare export class MIRToStakeCredentials { /** * @param {Uint8Array} bytes - * @returns {MIRToStakeCredentials} + * @returns {GeneralTransactionMetadata} */ - static from_bytes(bytes: Uint8Array): MIRToStakeCredentials; + static from_bytes(bytes: Uint8Array): GeneralTransactionMetadata; /** * @returns {string} @@ -3666,9 +4210,9 @@ declare export class MIRToStakeCredentials { /** * @param {string} hex_str - * @returns {MIRToStakeCredentials} + * @returns {GeneralTransactionMetadata} */ - static from_hex(hex_str: string): MIRToStakeCredentials; + static from_hex(hex_str: string): GeneralTransactionMetadata; /** * @returns {string} @@ -3676,20 +4220,20 @@ declare export class MIRToStakeCredentials { to_json(): string; /** - * @returns {MIRToStakeCredentialsJSON} + * @returns {GeneralTransactionMetadataJSON} */ - to_js_value(): MIRToStakeCredentialsJSON; + to_js_value(): GeneralTransactionMetadataJSON; /** * @param {string} json - * @returns {MIRToStakeCredentials} + * @returns {GeneralTransactionMetadata} */ - static from_json(json: string): MIRToStakeCredentials; + static from_json(json: string): GeneralTransactionMetadata; /** - * @returns {MIRToStakeCredentials} + * @returns {GeneralTransactionMetadata} */ - static new(): MIRToStakeCredentials; + static new(): GeneralTransactionMetadata; /** * @returns {number} @@ -3697,38 +4241,50 @@ declare export class MIRToStakeCredentials { len(): number; /** - * @param {StakeCredential} cred - * @param {Int} delta - * @returns {Int | void} + * @param {BigNum} key + * @param {TransactionMetadatum} value + * @returns {TransactionMetadatum | void} */ - insert(cred: StakeCredential, delta: Int): Int | void; + insert(key: BigNum, value: TransactionMetadatum): TransactionMetadatum | void; /** - * @param {StakeCredential} cred - * @returns {Int | void} + * @param {BigNum} key + * @returns {TransactionMetadatum | void} */ - get(cred: StakeCredential): Int | void; + get(key: BigNum): TransactionMetadatum | void; /** - * @returns {StakeCredentials} + * @returns {TransactionMetadatumLabels} */ - keys(): StakeCredentials; + keys(): TransactionMetadatumLabels; } /** */ -declare export class MetadataList { +declare export class GenesisDelegateHash { free(): void; + /** + * @param {Uint8Array} bytes + * @returns {GenesisDelegateHash} + */ + static from_bytes(bytes: Uint8Array): GenesisDelegateHash; + /** * @returns {Uint8Array} */ to_bytes(): Uint8Array; /** - * @param {Uint8Array} bytes - * @returns {MetadataList} + * @param {string} prefix + * @returns {string} */ - static from_bytes(bytes: Uint8Array): MetadataList; + to_bech32(prefix: string): string; + + /** + * @param {string} bech_str + * @returns {GenesisDelegateHash} + */ + static from_bech32(bech_str: string): GenesisDelegateHash; /** * @returns {string} @@ -3736,35 +4292,53 @@ declare export class MetadataList { to_hex(): string; /** - * @param {string} hex_str - * @returns {MetadataList} + * @param {string} hex + * @returns {GenesisDelegateHash} */ - static from_hex(hex_str: string): MetadataList; + static from_hex(hex: string): GenesisDelegateHash; +} +/** + */ +declare export class GenesisHash { + free(): void; /** - * @returns {MetadataList} + * @param {Uint8Array} bytes + * @returns {GenesisHash} */ - static new(): MetadataList; + static from_bytes(bytes: Uint8Array): GenesisHash; /** - * @returns {number} + * @returns {Uint8Array} */ - len(): number; + to_bytes(): Uint8Array; /** - * @param {number} index - * @returns {TransactionMetadatum} + * @param {string} prefix + * @returns {string} */ - get(index: number): TransactionMetadatum; + to_bech32(prefix: string): string; /** - * @param {TransactionMetadatum} elem + * @param {string} bech_str + * @returns {GenesisHash} */ - add(elem: TransactionMetadatum): void; + static from_bech32(bech_str: string): GenesisHash; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex + * @returns {GenesisHash} + */ + static from_hex(hex: string): GenesisHash; } /** */ -declare export class MetadataMap { +declare export class GenesisHashes { free(): void; /** @@ -3774,9 +4348,9 @@ declare export class MetadataMap { /** * @param {Uint8Array} bytes - * @returns {MetadataMap} + * @returns {GenesisHashes} */ - static from_bytes(bytes: Uint8Array): MetadataMap; + static from_bytes(bytes: Uint8Array): GenesisHashes; /** * @returns {string} @@ -3785,82 +4359,50 @@ declare export class MetadataMap { /** * @param {string} hex_str - * @returns {MetadataMap} + * @returns {GenesisHashes} */ - static from_hex(hex_str: string): MetadataMap; + static from_hex(hex_str: string): GenesisHashes; /** - * @returns {MetadataMap} + * @returns {string} */ - static new(): MetadataMap; + to_json(): string; /** - * @returns {number} + * @returns {GenesisHashesJSON} */ - len(): number; + to_js_value(): GenesisHashesJSON; /** - * @param {TransactionMetadatum} key - * @param {TransactionMetadatum} value - * @returns {TransactionMetadatum | void} + * @param {string} json + * @returns {GenesisHashes} */ - insert( - key: TransactionMetadatum, - value: TransactionMetadatum - ): TransactionMetadatum | void; + static from_json(json: string): GenesisHashes; /** - * @param {string} key - * @param {TransactionMetadatum} value - * @returns {TransactionMetadatum | void} + * @returns {GenesisHashes} */ - insert_str( - key: string, - value: TransactionMetadatum - ): TransactionMetadatum | void; + static new(): GenesisHashes; /** - * @param {number} key - * @param {TransactionMetadatum} value - * @returns {TransactionMetadatum | void} + * @returns {number} */ - insert_i32( - key: number, - value: TransactionMetadatum - ): TransactionMetadatum | void; + len(): number; /** - * @param {TransactionMetadatum} key - * @returns {TransactionMetadatum} + * @param {number} index + * @returns {GenesisHash} */ - get(key: TransactionMetadatum): TransactionMetadatum; + get(index: number): GenesisHash; /** - * @param {string} key - * @returns {TransactionMetadatum} - */ - get_str(key: string): TransactionMetadatum; - - /** - * @param {number} key - * @returns {TransactionMetadatum} - */ - get_i32(key: number): TransactionMetadatum; - - /** - * @param {TransactionMetadatum} key - * @returns {boolean} - */ - has(key: TransactionMetadatum): boolean; - - /** - * @returns {MetadataList} + * @param {GenesisHash} elem */ - keys(): MetadataList; + add(elem: GenesisHash): void; } /** */ -declare export class Mint { +declare export class GenesisKeyDelegation { free(): void; /** @@ -3870,9 +4412,9 @@ declare export class Mint { /** * @param {Uint8Array} bytes - * @returns {Mint} + * @returns {GenesisKeyDelegation} */ - static from_bytes(bytes: Uint8Array): Mint; + static from_bytes(bytes: Uint8Array): GenesisKeyDelegation; /** * @returns {string} @@ -3881,9 +4423,9 @@ declare export class Mint { /** * @param {string} hex_str - * @returns {Mint} + * @returns {GenesisKeyDelegation} */ - static from_hex(hex_str: string): Mint; + static from_hex(hex_str: string): GenesisKeyDelegation; /** * @returns {string} @@ -3891,200 +4433,185 @@ declare export class Mint { to_json(): string; /** - * @returns {MintJSON} + * @returns {GenesisKeyDelegationJSON} */ - to_js_value(): MintJSON; + to_js_value(): GenesisKeyDelegationJSON; /** * @param {string} json - * @returns {Mint} - */ - static from_json(json: string): Mint; - - /** - * @returns {Mint} + * @returns {GenesisKeyDelegation} */ - static new(): Mint; + static from_json(json: string): GenesisKeyDelegation; /** - * @param {ScriptHash} key - * @param {MintAssets} value - * @returns {Mint} + * @returns {GenesisHash} */ - static new_from_entry(key: ScriptHash, value: MintAssets): Mint; + genesishash(): GenesisHash; /** - * @returns {number} + * @returns {GenesisDelegateHash} */ - len(): number; + genesis_delegate_hash(): GenesisDelegateHash; /** - * @param {ScriptHash} key - * @param {MintAssets} value - * @returns {MintAssets | void} + * @returns {VRFKeyHash} */ - insert(key: ScriptHash, value: MintAssets): MintAssets | void; + vrf_keyhash(): VRFKeyHash; /** - * !!! DEPRECATED !!! - * Mint can store multiple entries for the same policy id. - * Use `.get_all` instead. - * @param {ScriptHash} key - * @returns {MintAssets | void} + * @param {GenesisHash} genesishash + * @param {GenesisDelegateHash} genesis_delegate_hash + * @param {VRFKeyHash} vrf_keyhash + * @returns {GenesisKeyDelegation} */ - get(key: ScriptHash): MintAssets | void; + static new( + genesishash: GenesisHash, + genesis_delegate_hash: GenesisDelegateHash, + vrf_keyhash: VRFKeyHash + ): GenesisKeyDelegation; +} +/** + */ +declare export class GovernanceAction { + free(): void; /** - * @param {ScriptHash} key - * @returns {MintsAssets | void} + * @returns {Uint8Array} */ - get_all(key: ScriptHash): MintsAssets | void; + to_bytes(): Uint8Array; /** - * @returns {ScriptHashes} + * @param {Uint8Array} bytes + * @returns {GovernanceAction} */ - keys(): ScriptHashes; + static from_bytes(bytes: Uint8Array): GovernanceAction; /** - * Returns the multiasset where only positive (minting) entries are present - * @returns {MultiAsset} + * @returns {string} */ - as_positive_multiasset(): MultiAsset; + to_hex(): string; /** - * Returns the multiasset where only negative (burning) entries are present - * @returns {MultiAsset} + * @param {string} hex_str + * @returns {GovernanceAction} */ - as_negative_multiasset(): MultiAsset; -} -/** - */ -declare export class MintAssets { - free(): void; + static from_hex(hex_str: string): GovernanceAction; /** - * @returns {MintAssets} + * @returns {string} */ - static new(): MintAssets; + to_json(): string; /** - * @param {AssetName} key - * @param {Int} value - * @returns {MintAssets} + * @returns {GovernanceActionJSON} */ - static new_from_entry(key: AssetName, value: Int): MintAssets; + to_js_value(): GovernanceActionJSON; /** - * @returns {number} + * @param {string} json + * @returns {GovernanceAction} */ - len(): number; + static from_json(json: string): GovernanceAction; /** - * @param {AssetName} key - * @param {Int} value - * @returns {Int | void} + * @param {ParameterChangeAction} parameter_change_action + * @returns {GovernanceAction} */ - insert(key: AssetName, value: Int): Int | void; + static new_parameter_change_action( + parameter_change_action: ParameterChangeAction + ): GovernanceAction; /** - * @param {AssetName} key - * @returns {Int | void} + * @param {HardForkInitiationAction} hard_fork_initiation_action + * @returns {GovernanceAction} */ - get(key: AssetName): Int | void; + static new_hard_fork_initiation_action( + hard_fork_initiation_action: HardForkInitiationAction + ): GovernanceAction; /** - * @returns {AssetNames} + * @param {TreasuryWithdrawalsAction} treasury_withdrawals_action + * @returns {GovernanceAction} */ - keys(): AssetNames; -} -/** - */ -declare export class MintBuilder { - free(): void; + static new_treasury_withdrawals_action( + treasury_withdrawals_action: TreasuryWithdrawalsAction + ): GovernanceAction; /** - * @returns {MintBuilder} + * @param {NoConfidenceAction} no_confidence_action + * @returns {GovernanceAction} */ - static new(): MintBuilder; + static new_no_confidence_action( + no_confidence_action: NoConfidenceAction + ): GovernanceAction; /** - * @param {MintWitness} mint - * @param {AssetName} asset_name - * @param {Int} amount + * @param {UpdateCommitteeAction} new_committee_action + * @returns {GovernanceAction} */ - add_asset(mint: MintWitness, asset_name: AssetName, amount: Int): void; + static new_new_committee_action( + new_committee_action: UpdateCommitteeAction + ): GovernanceAction; /** - * @param {MintWitness} mint - * @param {AssetName} asset_name - * @param {Int} amount + * @param {NewConstitutionAction} new_constitution_action + * @returns {GovernanceAction} */ - set_asset(mint: MintWitness, asset_name: AssetName, amount: Int): void; + static new_new_constitution_action( + new_constitution_action: NewConstitutionAction + ): GovernanceAction; /** - * @returns {Mint} + * @param {InfoAction} info_action + * @returns {GovernanceAction} */ - build(): Mint; + static new_info_action(info_action: InfoAction): GovernanceAction; /** - * @returns {NativeScripts} - */ - get_native_scripts(): NativeScripts; + * @returns {$Values< + typeof + GovernanceActionKind>} + */ + kind(): $Values; /** - * @returns {PlutusWitnesses} + * @returns {ParameterChangeAction | void} */ - get_plutus_witnesses(): PlutusWitnesses; + as_parameter_change_action(): ParameterChangeAction | void; /** - * @returns {TransactionInputs} + * @returns {HardForkInitiationAction | void} */ - get_ref_inputs(): TransactionInputs; + as_hard_fork_initiation_action(): HardForkInitiationAction | void; /** - * @returns {Redeemers} + * @returns {TreasuryWithdrawalsAction | void} */ - get_redeeemers(): Redeemers; + as_treasury_withdrawals_action(): TreasuryWithdrawalsAction | void; /** - * @returns {boolean} + * @returns {NoConfidenceAction | void} */ - has_plutus_scripts(): boolean; + as_no_confidence_action(): NoConfidenceAction | void; /** - * @returns {boolean} + * @returns {UpdateCommitteeAction | void} */ - has_native_scripts(): boolean; -} -/** - */ -declare export class MintWitness { - free(): void; + as_new_committee_action(): UpdateCommitteeAction | void; /** - * @param {NativeScript} native_script - * @returns {MintWitness} + * @returns {NewConstitutionAction | void} */ - static new_native_script(native_script: NativeScript): MintWitness; + as_new_constitution_action(): NewConstitutionAction | void; /** - * @param {PlutusScriptSource} plutus_script - * @param {Redeemer} redeemer - * @returns {MintWitness} + * @returns {InfoAction | void} */ - static new_plutus_script( - plutus_script: PlutusScriptSource, - redeemer: Redeemer - ): MintWitness; + as_info_action(): InfoAction | void; } /** */ -declare export class MintsAssets { - free(): void; -} -/** - */ -declare export class MoveInstantaneousReward { +declare export class GovernanceActionId { free(): void; /** @@ -4094,9 +4621,9 @@ declare export class MoveInstantaneousReward { /** * @param {Uint8Array} bytes - * @returns {MoveInstantaneousReward} + * @returns {GovernanceActionId} */ - static from_bytes(bytes: Uint8Array): MoveInstantaneousReward; + static from_bytes(bytes: Uint8Array): GovernanceActionId; /** * @returns {string} @@ -4105,9 +4632,9 @@ declare export class MoveInstantaneousReward { /** * @param {string} hex_str - * @returns {MoveInstantaneousReward} + * @returns {GovernanceActionId} */ - static from_hex(hex_str: string): MoveInstantaneousReward; + static from_hex(hex_str: string): GovernanceActionId; /** * @returns {string} @@ -4115,56 +4642,81 @@ declare export class MoveInstantaneousReward { to_json(): string; /** - * @returns {MoveInstantaneousRewardJSON} + * @returns {GovernanceActionIdJSON} */ - to_js_value(): MoveInstantaneousRewardJSON; + to_js_value(): GovernanceActionIdJSON; /** * @param {string} json - * @returns {MoveInstantaneousReward} + * @returns {GovernanceActionId} */ - static from_json(json: string): MoveInstantaneousReward; + static from_json(json: string): GovernanceActionId; /** - * @param {number} pot - * @param {BigNum} amount - * @returns {MoveInstantaneousReward} + * @returns {TransactionHash} */ - static new_to_other_pot(pot: number, amount: BigNum): MoveInstantaneousReward; + transaction_id(): TransactionHash; /** - * @param {number} pot - * @param {MIRToStakeCredentials} amounts - * @returns {MoveInstantaneousReward} + * @returns {number} */ - static new_to_stake_creds( - pot: number, - amounts: MIRToStakeCredentials - ): MoveInstantaneousReward; + index(): number; /** - * @returns {number} + * @param {TransactionHash} transaction_id + * @param {number} index + * @returns {GovernanceActionId} */ - pot(): number; + static new( + transaction_id: TransactionHash, + index: number + ): GovernanceActionId; +} +/** + */ +declare export class GovernanceActionIds { + free(): void; /** - * @returns {number} + * @returns {string} */ - kind(): number; + to_json(): string; /** - * @returns {BigNum | void} + * @returns {GovernanceActionIdsJSON} */ - as_to_other_pot(): BigNum | void; + to_js_value(): GovernanceActionIdsJSON; /** - * @returns {MIRToStakeCredentials | void} + * @param {string} json + * @returns {GovernanceActionIds} */ - as_to_stake_creds(): MIRToStakeCredentials | void; + static from_json(json: string): GovernanceActionIds; + + /** + * @returns {GovernanceActionIds} + */ + static new(): GovernanceActionIds; + + /** + * @param {GovernanceActionId} governance_action_id + */ + add(governance_action_id: GovernanceActionId): void; + + /** + * @param {number} index + * @returns {GovernanceActionId | void} + */ + get(index: number): GovernanceActionId | void; + + /** + * @returns {number} + */ + len(): number; } /** */ -declare export class MoveInstantaneousRewardsCert { +declare export class HardForkInitiationAction { free(): void; /** @@ -4174,9 +4726,9 @@ declare export class MoveInstantaneousRewardsCert { /** * @param {Uint8Array} bytes - * @returns {MoveInstantaneousRewardsCert} + * @returns {HardForkInitiationAction} */ - static from_bytes(bytes: Uint8Array): MoveInstantaneousRewardsCert; + static from_bytes(bytes: Uint8Array): HardForkInitiationAction; /** * @returns {string} @@ -4185,9 +4737,9 @@ declare export class MoveInstantaneousRewardsCert { /** * @param {string} hex_str - * @returns {MoveInstantaneousRewardsCert} + * @returns {HardForkInitiationAction} */ - static from_hex(hex_str: string): MoveInstantaneousRewardsCert; + static from_hex(hex_str: string): HardForkInitiationAction; /** * @returns {string} @@ -4195,32 +4747,45 @@ declare export class MoveInstantaneousRewardsCert { to_json(): string; /** - * @returns {MoveInstantaneousRewardsCertJSON} + * @returns {HardForkInitiationActionJSON} */ - to_js_value(): MoveInstantaneousRewardsCertJSON; + to_js_value(): HardForkInitiationActionJSON; /** * @param {string} json - * @returns {MoveInstantaneousRewardsCert} + * @returns {HardForkInitiationAction} */ - static from_json(json: string): MoveInstantaneousRewardsCert; + static from_json(json: string): HardForkInitiationAction; /** - * @returns {MoveInstantaneousReward} + * @returns {GovernanceActionId | void} */ - move_instantaneous_reward(): MoveInstantaneousReward; + gov_action_id(): GovernanceActionId | void; /** - * @param {MoveInstantaneousReward} move_instantaneous_reward - * @returns {MoveInstantaneousRewardsCert} + * @returns {ProtocolVersion} */ - static new( - move_instantaneous_reward: MoveInstantaneousReward - ): MoveInstantaneousRewardsCert; + protocol_version(): ProtocolVersion; + + /** + * @param {ProtocolVersion} protocol_version + * @returns {HardForkInitiationAction} + */ + static new(protocol_version: ProtocolVersion): HardForkInitiationAction; + + /** + * @param {GovernanceActionId} gov_action_id + * @param {ProtocolVersion} protocol_version + * @returns {HardForkInitiationAction} + */ + static new_with_action_id( + gov_action_id: GovernanceActionId, + protocol_version: ProtocolVersion + ): HardForkInitiationAction; } /** */ -declare export class MultiAsset { +declare export class Header { free(): void; /** @@ -4230,9 +4795,9 @@ declare export class MultiAsset { /** * @param {Uint8Array} bytes - * @returns {MultiAsset} + * @returns {Header} */ - static from_bytes(bytes: Uint8Array): MultiAsset; + static from_bytes(bytes: Uint8Array): Header; /** * @returns {string} @@ -4241,9 +4806,9 @@ declare export class MultiAsset { /** * @param {string} hex_str - * @returns {MultiAsset} + * @returns {Header} */ - static from_hex(hex_str: string): MultiAsset; + static from_hex(hex_str: string): Header; /** * @returns {string} @@ -4251,82 +4816,36 @@ declare export class MultiAsset { to_json(): string; /** - * @returns {MultiAssetJSON} + * @returns {HeaderJSON} */ - to_js_value(): MultiAssetJSON; + to_js_value(): HeaderJSON; /** * @param {string} json - * @returns {MultiAsset} - */ - static from_json(json: string): MultiAsset; - - /** - * @returns {MultiAsset} - */ - static new(): MultiAsset; - - /** - * the number of unique policy IDs in the multiasset - * @returns {number} - */ - len(): number; - - /** - * set (and replace if it exists) all assets with policy {policy_id} to a copy of {assets} - * @param {ScriptHash} policy_id - * @param {Assets} assets - * @returns {Assets | void} - */ - insert(policy_id: ScriptHash, assets: Assets): Assets | void; - - /** - * all assets under {policy_id}, if any exist, or else None (undefined in JS) - * @param {ScriptHash} policy_id - * @returns {Assets | void} - */ - get(policy_id: ScriptHash): Assets | void; - - /** - * sets the asset {asset_name} to {value} under policy {policy_id} - * returns the previous amount if it was set, or else None (undefined in JS) - * @param {ScriptHash} policy_id - * @param {AssetName} asset_name - * @param {BigNum} value - * @returns {BigNum | void} + * @returns {Header} */ - set_asset( - policy_id: ScriptHash, - asset_name: AssetName, - value: BigNum - ): BigNum | void; + static from_json(json: string): Header; /** - * returns the amount of asset {asset_name} under policy {policy_id} - * If such an asset does not exist, 0 is returned. - * @param {ScriptHash} policy_id - * @param {AssetName} asset_name - * @returns {BigNum} + * @returns {HeaderBody} */ - get_asset(policy_id: ScriptHash, asset_name: AssetName): BigNum; + header_body(): HeaderBody; /** - * returns all policy IDs used by assets in this multiasset - * @returns {ScriptHashes} + * @returns {KESSignature} */ - keys(): ScriptHashes; + body_signature(): KESSignature; /** - * removes an asset from the list if the result is 0 or less - * does not modify this object, instead the result is returned - * @param {MultiAsset} rhs_ma - * @returns {MultiAsset} + * @param {HeaderBody} header_body + * @param {KESSignature} body_signature + * @returns {Header} */ - sub(rhs_ma: MultiAsset): MultiAsset; + static new(header_body: HeaderBody, body_signature: KESSignature): Header; } /** */ -declare export class MultiHostName { +declare export class HeaderBody { free(): void; /** @@ -4336,9 +4855,9 @@ declare export class MultiHostName { /** * @param {Uint8Array} bytes - * @returns {MultiHostName} + * @returns {HeaderBody} */ - static from_bytes(bytes: Uint8Array): MultiHostName; + static from_bytes(bytes: Uint8Array): HeaderBody; /** * @returns {string} @@ -4347,9 +4866,9 @@ declare export class MultiHostName { /** * @param {string} hex_str - * @returns {MultiHostName} + * @returns {HeaderBody} */ - static from_hex(hex_str: string): MultiHostName; + static from_hex(hex_str: string): HeaderBody; /** * @returns {string} @@ -4357,206 +4876,293 @@ declare export class MultiHostName { to_json(): string; /** - * @returns {MultiHostNameJSON} + * @returns {HeaderBodyJSON} */ - to_js_value(): MultiHostNameJSON; + to_js_value(): HeaderBodyJSON; /** * @param {string} json - * @returns {MultiHostName} + * @returns {HeaderBody} */ - static from_json(json: string): MultiHostName; + static from_json(json: string): HeaderBody; /** - * @returns {DNSRecordSRV} + * @returns {number} */ - dns_name(): DNSRecordSRV; + block_number(): number; /** - * @param {DNSRecordSRV} dns_name - * @returns {MultiHostName} + * !!! DEPRECATED !!! + * Returns a Slot32 (u32) value in case the underlying original BigNum (u64) value is within the limits. + * Otherwise will just raise an error. + * @returns {number} */ - static new(dns_name: DNSRecordSRV): MultiHostName; -} -/** - */ -declare export class NativeScript { - free(): void; + slot(): number; /** - * @returns {Uint8Array} + * @returns {BigNum} */ - to_bytes(): Uint8Array; + slot_bignum(): BigNum; /** - * @param {Uint8Array} bytes - * @returns {NativeScript} + * @returns {BlockHash | void} */ - static from_bytes(bytes: Uint8Array): NativeScript; + prev_hash(): BlockHash | void; /** - * @returns {string} + * @returns {Vkey} */ - to_hex(): string; + issuer_vkey(): Vkey; /** - * @param {string} hex_str - * @returns {NativeScript} + * @returns {VRFVKey} */ - static from_hex(hex_str: string): NativeScript; + vrf_vkey(): VRFVKey; /** - * @returns {string} + * If this function returns true, the `.nonce_vrf_or_nothing` + * and the `.leader_vrf_or_nothing` functions will return + * non-empty results + * @returns {boolean} */ - to_json(): string; + has_nonce_and_leader_vrf(): boolean; /** - * @returns {NativeScriptJSON} + * Might return nothing in case `.has_nonce_and_leader_vrf` returns false + * @returns {VRFCert | void} */ - to_js_value(): NativeScriptJSON; + nonce_vrf_or_nothing(): VRFCert | void; /** - * @param {string} json - * @returns {NativeScript} + * Might return nothing in case `.has_nonce_and_leader_vrf` returns false + * @returns {VRFCert | void} */ - static from_json(json: string): NativeScript; + leader_vrf_or_nothing(): VRFCert | void; /** - * @returns {ScriptHash} + * If this function returns true, the `.vrf_result_or_nothing` + * function will return a non-empty result + * @returns {boolean} */ - hash(): ScriptHash; + has_vrf_result(): boolean; /** - * @param {ScriptPubkey} script_pubkey - * @returns {NativeScript} + * Might return nothing in case `.has_vrf_result` returns false + * @returns {VRFCert | void} */ - static new_script_pubkey(script_pubkey: ScriptPubkey): NativeScript; + vrf_result_or_nothing(): VRFCert | void; /** - * @param {ScriptAll} script_all - * @returns {NativeScript} + * @returns {number} */ - static new_script_all(script_all: ScriptAll): NativeScript; + block_body_size(): number; /** - * @param {ScriptAny} script_any - * @returns {NativeScript} + * @returns {BlockHash} */ - static new_script_any(script_any: ScriptAny): NativeScript; + block_body_hash(): BlockHash; /** - * @param {ScriptNOfK} script_n_of_k - * @returns {NativeScript} + * @returns {OperationalCert} */ - static new_script_n_of_k(script_n_of_k: ScriptNOfK): NativeScript; + operational_cert(): OperationalCert; /** - * @param {TimelockStart} timelock_start - * @returns {NativeScript} + * @returns {ProtocolVersion} */ - static new_timelock_start(timelock_start: TimelockStart): NativeScript; + protocol_version(): ProtocolVersion; /** - * @param {TimelockExpiry} timelock_expiry - * @returns {NativeScript} + * !!! DEPRECATED !!! + * This constructor uses outdated slot number format. + * Use `.new_headerbody` instead + * @param {number} block_number + * @param {number} slot + * @param {BlockHash | void} prev_hash + * @param {Vkey} issuer_vkey + * @param {VRFVKey} vrf_vkey + * @param {VRFCert} vrf_result + * @param {number} block_body_size + * @param {BlockHash} block_body_hash + * @param {OperationalCert} operational_cert + * @param {ProtocolVersion} protocol_version + * @returns {HeaderBody} */ - static new_timelock_expiry(timelock_expiry: TimelockExpiry): NativeScript; + static new( + block_number: number, + slot: number, + prev_hash: BlockHash | void, + issuer_vkey: Vkey, + vrf_vkey: VRFVKey, + vrf_result: VRFCert, + block_body_size: number, + block_body_hash: BlockHash, + operational_cert: OperationalCert, + protocol_version: ProtocolVersion + ): HeaderBody; /** - * @returns {number} + * @param {number} block_number + * @param {BigNum} slot + * @param {BlockHash | void} prev_hash + * @param {Vkey} issuer_vkey + * @param {VRFVKey} vrf_vkey + * @param {VRFCert} vrf_result + * @param {number} block_body_size + * @param {BlockHash} block_body_hash + * @param {OperationalCert} operational_cert + * @param {ProtocolVersion} protocol_version + * @returns {HeaderBody} */ - kind(): number; + static new_headerbody( + block_number: number, + slot: BigNum, + prev_hash: BlockHash | void, + issuer_vkey: Vkey, + vrf_vkey: VRFVKey, + vrf_result: VRFCert, + block_body_size: number, + block_body_hash: BlockHash, + operational_cert: OperationalCert, + protocol_version: ProtocolVersion + ): HeaderBody; +} +/** + */ +declare export class InfoAction { + free(): void; /** - * @returns {ScriptPubkey | void} + * @returns {InfoAction} */ - as_script_pubkey(): ScriptPubkey | void; + static new(): InfoAction; +} +/** + */ +declare export class Int { + free(): void; /** - * @returns {ScriptAll | void} + * @returns {Uint8Array} */ - as_script_all(): ScriptAll | void; + to_bytes(): Uint8Array; /** - * @returns {ScriptAny | void} + * @param {Uint8Array} bytes + * @returns {Int} */ - as_script_any(): ScriptAny | void; + static from_bytes(bytes: Uint8Array): Int; /** - * @returns {ScriptNOfK | void} + * @returns {string} */ - as_script_n_of_k(): ScriptNOfK | void; + to_hex(): string; /** - * @returns {TimelockStart | void} + * @param {string} hex_str + * @returns {Int} */ - as_timelock_start(): TimelockStart | void; + static from_hex(hex_str: string): Int; /** - * @returns {TimelockExpiry | void} + * @returns {string} */ - as_timelock_expiry(): TimelockExpiry | void; + to_json(): string; /** - * Returns an array of unique Ed25519KeyHashes - * contained within this script recursively on any depth level. - * The order of the keys in the result is not determined in any way. - * @returns {Ed25519KeyHashes} + * @returns {IntJSON} */ - get_required_signers(): Ed25519KeyHashes; -} -/** - */ -declare export class NativeScriptSource { - free(): void; + to_js_value(): IntJSON; /** - * @param {NativeScript} script - * @returns {NativeScriptSource} + * @param {string} json + * @returns {Int} */ - static new(script: NativeScript): NativeScriptSource; + static from_json(json: string): Int; /** - * @param {ScriptHash} script_hash - * @param {TransactionInput} input - * @param {Ed25519KeyHashes} required_signers - * @returns {NativeScriptSource} + * @param {BigNum} x + * @returns {Int} */ - static new_ref_input( - script_hash: ScriptHash, - input: TransactionInput, - required_signers: Ed25519KeyHashes - ): NativeScriptSource; -} -/** - */ -declare export class NativeScripts { - free(): void; + static new(x: BigNum): Int; /** - * @returns {NativeScripts} + * @param {BigNum} x + * @returns {Int} */ - static new(): NativeScripts; + static new_negative(x: BigNum): Int; + + /** + * @param {number} x + * @returns {Int} + */ + static new_i32(x: number): Int; + + /** + * @returns {boolean} + */ + is_positive(): boolean; + + /** + * BigNum can only contain unsigned u64 values + * + * This function will return the BigNum representation + * only in case the underlying i128 value is positive. + * + * Otherwise nothing will be returned (undefined). + * @returns {BigNum | void} + */ + as_positive(): BigNum | void; + + /** + * BigNum can only contain unsigned u64 values + * + * This function will return the *absolute* BigNum representation + * only in case the underlying i128 value is negative. + * + * Otherwise nothing will be returned (undefined). + * @returns {BigNum | void} + */ + as_negative(): BigNum | void; + + /** + * !!! DEPRECATED !!! + * Returns an i32 value in case the underlying original i128 value is within the limits. + * Otherwise will just return an empty value (undefined). + * @returns {number | void} + */ + as_i32(): number | void; + + /** + * Returns the underlying value converted to i32 if possible (within limits) + * Otherwise will just return an empty value (undefined). + * @returns {number | void} + */ + as_i32_or_nothing(): number | void; /** + * Returns the underlying value converted to i32 if possible (within limits) + * JsError in case of out of boundary overflow * @returns {number} */ - len(): number; + as_i32_or_fail(): number; /** - * @param {number} index - * @returns {NativeScript} + * Returns string representation of the underlying i128 value directly. + * Might contain the minus sign (-) in case of negative value. + * @returns {string} */ - get(index: number): NativeScript; + to_str(): string; /** - * @param {NativeScript} elem + * @param {string} string + * @returns {Int} */ - add(elem: NativeScript): void; + static from_str(string: string): Int; } /** */ -declare export class NetworkId { +declare export class Ipv4 { free(): void; /** @@ -4566,9 +5172,9 @@ declare export class NetworkId { /** * @param {Uint8Array} bytes - * @returns {NetworkId} + * @returns {Ipv4} */ - static from_bytes(bytes: Uint8Array): NetworkId; + static from_bytes(bytes: Uint8Array): Ipv4; /** * @returns {string} @@ -4577,9 +5183,9 @@ declare export class NetworkId { /** * @param {string} hex_str - * @returns {NetworkId} + * @returns {Ipv4} */ - static from_hex(hex_str: string): NetworkId; + static from_hex(hex_str: string): Ipv4; /** * @returns {string} @@ -4587,78 +5193,84 @@ declare export class NetworkId { to_json(): string; /** - * @returns {NetworkIdJSON} + * @returns {Ipv4JSON} */ - to_js_value(): NetworkIdJSON; + to_js_value(): Ipv4JSON; /** * @param {string} json - * @returns {NetworkId} - */ - static from_json(json: string): NetworkId; - - /** - * @returns {NetworkId} + * @returns {Ipv4} */ - static testnet(): NetworkId; + static from_json(json: string): Ipv4; /** - * @returns {NetworkId} + * @param {Uint8Array} data + * @returns {Ipv4} */ - static mainnet(): NetworkId; + static new(data: Uint8Array): Ipv4; /** - * @returns {number} + * @returns {Uint8Array} */ - kind(): number; + ip(): Uint8Array; } /** */ -declare export class NetworkInfo { +declare export class Ipv6 { free(): void; /** - * @param {number} network_id - * @param {number} protocol_magic - * @returns {NetworkInfo} + * @returns {Uint8Array} */ - static new(network_id: number, protocol_magic: number): NetworkInfo; + to_bytes(): Uint8Array; /** - * @returns {number} + * @param {Uint8Array} bytes + * @returns {Ipv6} */ - network_id(): number; + static from_bytes(bytes: Uint8Array): Ipv6; /** - * @returns {number} + * @returns {string} */ - protocol_magic(): number; + to_hex(): string; /** - * @returns {NetworkInfo} + * @param {string} hex_str + * @returns {Ipv6} */ - static testnet_preview(): NetworkInfo; + static from_hex(hex_str: string): Ipv6; /** - * @returns {NetworkInfo} + * @returns {string} */ - static testnet_preprod(): NetworkInfo; + to_json(): string; /** - * !!! DEPRECATED !!! - * This network does not exist anymore. Use `.testnet_preview()` or `.testnet_preprod()` - * @returns {NetworkInfo} + * @returns {Ipv6JSON} */ - static testnet(): NetworkInfo; + to_js_value(): Ipv6JSON; /** - * @returns {NetworkInfo} + * @param {string} json + * @returns {Ipv6} */ - static mainnet(): NetworkInfo; + static from_json(json: string): Ipv6; + + /** + * @param {Uint8Array} data + * @returns {Ipv6} + */ + static new(data: Uint8Array): Ipv6; + + /** + * @returns {Uint8Array} + */ + ip(): Uint8Array; } /** */ -declare export class Nonce { +declare export class KESSignature { free(): void; /** @@ -4668,56 +5280,52 @@ declare export class Nonce { /** * @param {Uint8Array} bytes - * @returns {Nonce} + * @returns {KESSignature} */ - static from_bytes(bytes: Uint8Array): Nonce; + static from_bytes(bytes: Uint8Array): KESSignature; +} +/** + */ +declare export class KESVKey { + free(): void; /** - * @returns {string} + * @param {Uint8Array} bytes + * @returns {KESVKey} */ - to_hex(): string; + static from_bytes(bytes: Uint8Array): KESVKey; /** - * @param {string} hex_str - * @returns {Nonce} + * @returns {Uint8Array} */ - static from_hex(hex_str: string): Nonce; + to_bytes(): Uint8Array; /** + * @param {string} prefix * @returns {string} */ - to_json(): string; - - /** - * @returns {NonceJSON} - */ - to_js_value(): NonceJSON; - - /** - * @param {string} json - * @returns {Nonce} - */ - static from_json(json: string): Nonce; + to_bech32(prefix: string): string; /** - * @returns {Nonce} + * @param {string} bech_str + * @returns {KESVKey} */ - static new_identity(): Nonce; + static from_bech32(bech_str: string): KESVKey; /** - * @param {Uint8Array} hash - * @returns {Nonce} + * @returns {string} */ - static new_from_hash(hash: Uint8Array): Nonce; + to_hex(): string; /** - * @returns {Uint8Array | void} + * @param {string} hex + * @returns {KESVKey} */ - get_hash(): Uint8Array | void; + static from_hex(hex: string): KESVKey; } /** */ -declare export class OperationalCert { +declare export class Language { free(): void; /** @@ -4727,9 +5335,9 @@ declare export class OperationalCert { /** * @param {Uint8Array} bytes - * @returns {OperationalCert} + * @returns {Language} */ - static from_bytes(bytes: Uint8Array): OperationalCert; + static from_bytes(bytes: Uint8Array): Language; /** * @returns {string} @@ -4738,9 +5346,9 @@ declare export class OperationalCert { /** * @param {string} hex_str - * @returns {OperationalCert} + * @returns {Language} */ - static from_hex(hex_str: string): OperationalCert; + static from_hex(hex_str: string): Language; /** * @returns {string} @@ -4748,205 +5356,207 @@ declare export class OperationalCert { to_json(): string; /** - * @returns {OperationalCertJSON} + * @returns {LanguageJSON} */ - to_js_value(): OperationalCertJSON; + to_js_value(): LanguageJSON; /** * @param {string} json - * @returns {OperationalCert} + * @returns {Language} */ - static from_json(json: string): OperationalCert; + static from_json(json: string): Language; /** - * @returns {KESVKey} + * @returns {Language} */ - hot_vkey(): KESVKey; + static new_plutus_v1(): Language; /** - * @returns {number} + * @returns {Language} */ - sequence_number(): number; + static new_plutus_v2(): Language; /** - * @returns {number} + * @returns {Language} */ - kes_period(): number; + static new_plutus_v3(): Language; /** - * @returns {Ed25519Signature} - */ - sigma(): Ed25519Signature; - - /** - * @param {KESVKey} hot_vkey - * @param {number} sequence_number - * @param {number} kes_period - * @param {Ed25519Signature} sigma - * @returns {OperationalCert} - */ - static new( - hot_vkey: KESVKey, - sequence_number: number, - kes_period: number, - sigma: Ed25519Signature - ): OperationalCert; + * @returns {$Values< + typeof + LanguageKind>} + */ + kind(): $Values; } /** */ -declare export class OutputDatum { +declare export class Languages { free(): void; /** - * @param {DataHash} data_hash - * @returns {OutputDatum} + * @returns {Languages} */ - static new_data_hash(data_hash: DataHash): OutputDatum; + static new(): Languages; /** - * @param {PlutusData} data - * @returns {OutputDatum} + * @returns {number} */ - static new_data(data: PlutusData): OutputDatum; + len(): number; /** - * @returns {DataHash | void} + * @param {number} index + * @returns {Language} */ - data_hash(): DataHash | void; + get(index: number): Language; /** - * @returns {PlutusData | void} + * @param {Language} elem */ - data(): PlutusData | void; + add(elem: Language): void; + + /** + * @returns {Languages} + */ + static list(): Languages; } /** */ -declare export class PlutusData { +declare export class LegacyDaedalusPrivateKey { free(): void; + /** + * @param {Uint8Array} bytes + * @returns {LegacyDaedalusPrivateKey} + */ + static from_bytes(bytes: Uint8Array): LegacyDaedalusPrivateKey; + /** * @returns {Uint8Array} */ - to_bytes(): Uint8Array; + as_bytes(): Uint8Array; /** - * @param {Uint8Array} bytes - * @returns {PlutusData} + * @returns {Uint8Array} */ - static from_bytes(bytes: Uint8Array): PlutusData; + chaincode(): Uint8Array; +} +/** + */ +declare export class LinearFee { + free(): void; /** - * @returns {string} + * @returns {BigNum} */ - to_hex(): string; + constant(): BigNum; /** - * @param {string} hex_str - * @returns {PlutusData} + * @returns {BigNum} */ - static from_hex(hex_str: string): PlutusData; + coefficient(): BigNum; /** - * @param {ConstrPlutusData} constr_plutus_data - * @returns {PlutusData} + * @param {BigNum} coefficient + * @param {BigNum} constant + * @returns {LinearFee} */ - static new_constr_plutus_data( - constr_plutus_data: ConstrPlutusData - ): PlutusData; + static new(coefficient: BigNum, constant: BigNum): LinearFee; +} +/** + */ +declare export class MIRToStakeCredentials { + free(): void; /** - * Same as `.new_constr_plutus_data` but creates constr with empty data list - * @param {BigNum} alternative - * @returns {PlutusData} + * @returns {Uint8Array} */ - static new_empty_constr_plutus_data(alternative: BigNum): PlutusData; + to_bytes(): Uint8Array; /** - * @param {BigNum} alternative - * @param {PlutusData} plutus_data - * @returns {PlutusData} + * @param {Uint8Array} bytes + * @returns {MIRToStakeCredentials} */ - static new_single_value_constr_plutus_data( - alternative: BigNum, - plutus_data: PlutusData - ): PlutusData; + static from_bytes(bytes: Uint8Array): MIRToStakeCredentials; /** - * @param {PlutusMap} map - * @returns {PlutusData} + * @returns {string} */ - static new_map(map: PlutusMap): PlutusData; + to_hex(): string; /** - * @param {PlutusList} list - * @returns {PlutusData} + * @param {string} hex_str + * @returns {MIRToStakeCredentials} */ - static new_list(list: PlutusList): PlutusData; + static from_hex(hex_str: string): MIRToStakeCredentials; /** - * @param {BigInt} integer - * @returns {PlutusData} + * @returns {string} */ - static new_integer(integer: BigInt): PlutusData; + to_json(): string; /** - * @param {Uint8Array} bytes - * @returns {PlutusData} + * @returns {MIRToStakeCredentialsJSON} */ - static new_bytes(bytes: Uint8Array): PlutusData; + to_js_value(): MIRToStakeCredentialsJSON; /** - * @returns {number} + * @param {string} json + * @returns {MIRToStakeCredentials} */ - kind(): number; + static from_json(json: string): MIRToStakeCredentials; /** - * @returns {ConstrPlutusData | void} + * @returns {MIRToStakeCredentials} */ - as_constr_plutus_data(): ConstrPlutusData | void; + static new(): MIRToStakeCredentials; /** - * @returns {PlutusMap | void} + * @returns {number} */ - as_map(): PlutusMap | void; + len(): number; /** - * @returns {PlutusList | void} + * @param {Credential} cred + * @param {Int} delta + * @returns {Int | void} */ - as_list(): PlutusList | void; + insert(cred: Credential, delta: Int): Int | void; /** - * @returns {BigInt | void} + * @param {Credential} cred + * @returns {Int | void} */ - as_integer(): BigInt | void; + get(cred: Credential): Int | void; /** - * @returns {Uint8Array | void} + * @returns {Credentials} */ - as_bytes(): Uint8Array | void; + keys(): Credentials; +} +/** + */ +declare export class MalformedAddress { + free(): void; /** - * @param {number} schema - * @returns {string} + * @returns {Uint8Array} */ - to_json(schema: number): string; + original_bytes(): Uint8Array; /** - * @param {string} json - * @param {number} schema - * @returns {PlutusData} + * @returns {Address} */ - static from_json(json: string, schema: number): PlutusData; + to_address(): Address; /** - * @param {Address} address - * @returns {PlutusData} + * @param {Address} addr + * @returns {MalformedAddress | void} */ - static from_address(address: Address): PlutusData; + static from_address(addr: Address): MalformedAddress | void; } /** */ -declare export class PlutusList { +declare export class MetadataList { free(): void; /** @@ -4956,9 +5566,9 @@ declare export class PlutusList { /** * @param {Uint8Array} bytes - * @returns {PlutusList} + * @returns {MetadataList} */ - static from_bytes(bytes: Uint8Array): PlutusList; + static from_bytes(bytes: Uint8Array): MetadataList; /** * @returns {string} @@ -4967,14 +5577,14 @@ declare export class PlutusList { /** * @param {string} hex_str - * @returns {PlutusList} + * @returns {MetadataList} */ - static from_hex(hex_str: string): PlutusList; + static from_hex(hex_str: string): MetadataList; /** - * @returns {PlutusList} + * @returns {MetadataList} */ - static new(): PlutusList; + static new(): MetadataList; /** * @returns {number} @@ -4983,18 +5593,18 @@ declare export class PlutusList { /** * @param {number} index - * @returns {PlutusData} + * @returns {TransactionMetadatum} */ - get(index: number): PlutusData; + get(index: number): TransactionMetadatum; /** - * @param {PlutusData} elem + * @param {TransactionMetadatum} elem */ - add(elem: PlutusData): void; + add(elem: TransactionMetadatum): void; } /** */ -declare export class PlutusMap { +declare export class MetadataMap { free(): void; /** @@ -5004,9 +5614,9 @@ declare export class PlutusMap { /** * @param {Uint8Array} bytes - * @returns {PlutusMap} + * @returns {MetadataMap} */ - static from_bytes(bytes: Uint8Array): PlutusMap; + static from_bytes(bytes: Uint8Array): MetadataMap; /** * @returns {string} @@ -5015,14 +5625,14 @@ declare export class PlutusMap { /** * @param {string} hex_str - * @returns {PlutusMap} + * @returns {MetadataMap} */ - static from_hex(hex_str: string): PlutusMap; + static from_hex(hex_str: string): MetadataMap; /** - * @returns {PlutusMap} + * @returns {MetadataMap} */ - static new(): PlutusMap; + static new(): MetadataMap; /** * @returns {number} @@ -5030,212 +5640,170 @@ declare export class PlutusMap { len(): number; /** - * @param {PlutusData} key - * @param {PlutusData} value - * @returns {PlutusData | void} + * @param {TransactionMetadatum} key + * @param {TransactionMetadatum} value + * @returns {TransactionMetadatum | void} */ - insert(key: PlutusData, value: PlutusData): PlutusData | void; + insert( + key: TransactionMetadatum, + value: TransactionMetadatum + ): TransactionMetadatum | void; /** - * @param {PlutusData} key - * @returns {PlutusData | void} + * @param {string} key + * @param {TransactionMetadatum} value + * @returns {TransactionMetadatum | void} */ - get(key: PlutusData): PlutusData | void; + insert_str( + key: string, + value: TransactionMetadatum + ): TransactionMetadatum | void; /** - * @returns {PlutusList} + * @param {number} key + * @param {TransactionMetadatum} value + * @returns {TransactionMetadatum | void} */ - keys(): PlutusList; -} -/** - */ -declare export class PlutusScript { - free(): void; + insert_i32( + key: number, + value: TransactionMetadatum + ): TransactionMetadatum | void; /** - * @returns {Uint8Array} + * @param {TransactionMetadatum} key + * @returns {TransactionMetadatum} */ - to_bytes(): Uint8Array; + get(key: TransactionMetadatum): TransactionMetadatum; /** - * @param {Uint8Array} bytes - * @returns {PlutusScript} + * @param {string} key + * @returns {TransactionMetadatum} */ - static from_bytes(bytes: Uint8Array): PlutusScript; + get_str(key: string): TransactionMetadatum; /** - * @returns {string} + * @param {number} key + * @returns {TransactionMetadatum} */ - to_hex(): string; + get_i32(key: number): TransactionMetadatum; /** - * @param {string} hex_str - * @returns {PlutusScript} + * @param {TransactionMetadatum} key + * @returns {boolean} */ - static from_hex(hex_str: string): PlutusScript; + has(key: TransactionMetadatum): boolean; /** - * - * * Creates a new Plutus script from the RAW bytes of the compiled script. - * * This does NOT include any CBOR encoding around these bytes (e.g. from "cborBytes" in cardano-cli) - * * If you creating this from those you should use PlutusScript::from_bytes() instead. - * @param {Uint8Array} bytes - * @returns {PlutusScript} + * @returns {MetadataList} */ - static new(bytes: Uint8Array): PlutusScript; - - /** - * - * * Creates a new Plutus script from the RAW bytes of the compiled script. - * * This does NOT include any CBOR encoding around these bytes (e.g. from "cborBytes" in cardano-cli) - * * If you creating this from those you should use PlutusScript::from_bytes() instead. - * @param {Uint8Array} bytes - * @returns {PlutusScript} - */ - static new_v2(bytes: Uint8Array): PlutusScript; - - /** - * - * * Creates a new Plutus script from the RAW bytes of the compiled script. - * * This does NOT include any CBOR encoding around these bytes (e.g. from "cborBytes" in cardano-cli) - * * If you creating this from those you should use PlutusScript::from_bytes() instead. - * @param {Uint8Array} bytes - * @param {Language} language - * @returns {PlutusScript} - */ - static new_with_version(bytes: Uint8Array, language: Language): PlutusScript; + keys(): MetadataList; +} +/** + */ +declare export class Mint { + free(): void; /** - * - * * The raw bytes of this compiled Plutus script. - * * If you need "cborBytes" for cardano-cli use PlutusScript::to_bytes() instead. * @returns {Uint8Array} */ - bytes(): Uint8Array; + to_bytes(): Uint8Array; /** - * Same as `.from_bytes` but will consider the script as requiring the Plutus Language V2 * @param {Uint8Array} bytes - * @returns {PlutusScript} + * @returns {Mint} */ - static from_bytes_v2(bytes: Uint8Array): PlutusScript; + static from_bytes(bytes: Uint8Array): Mint; /** - * Same as `.from_bytes` but will consider the script as requiring the specified language version - * @param {Uint8Array} bytes - * @param {Language} language - * @returns {PlutusScript} + * @returns {string} */ - static from_bytes_with_version( - bytes: Uint8Array, - language: Language - ): PlutusScript; + to_hex(): string; /** - * Same as .from_hex but will consider the script as requiring the specified language version * @param {string} hex_str - * @param {Language} language - * @returns {PlutusScript} + * @returns {Mint} */ - static from_hex_with_version( - hex_str: string, - language: Language - ): PlutusScript; + static from_hex(hex_str: string): Mint; /** - * @returns {ScriptHash} + * @returns {string} */ - hash(): ScriptHash; + to_json(): string; /** - * @returns {Language} + * @returns {MintJSON} */ - language_version(): Language; -} -/** - */ -declare export class PlutusScriptSource { - free(): void; + to_js_value(): MintJSON; /** - * @param {PlutusScript} script - * @returns {PlutusScriptSource} + * @param {string} json + * @returns {Mint} */ - static new(script: PlutusScript): PlutusScriptSource; + static from_json(json: string): Mint; /** - * !!! DEPRECATED !!! - * This constructor has missed information about plutus script language vesrion. That can affect - * the script data hash calculation. - * Use `.new_ref_input_with_lang_ver` instead - * @param {ScriptHash} script_hash - * @param {TransactionInput} input - * @returns {PlutusScriptSource} + * @returns {Mint} */ - static new_ref_input( - script_hash: ScriptHash, - input: TransactionInput - ): PlutusScriptSource; + static new(): Mint; /** - * @param {ScriptHash} script_hash - * @param {TransactionInput} input - * @param {Language} lang_ver - * @returns {PlutusScriptSource} + * @param {ScriptHash} key + * @param {MintAssets} value + * @returns {Mint} */ - static new_ref_input_with_lang_ver( - script_hash: ScriptHash, - input: TransactionInput, - lang_ver: Language - ): PlutusScriptSource; -} -/** - */ -declare export class PlutusScripts { - free(): void; + static new_from_entry(key: ScriptHash, value: MintAssets): Mint; /** - * @returns {Uint8Array} + * @returns {number} */ - to_bytes(): Uint8Array; + len(): number; /** - * @param {Uint8Array} bytes - * @returns {PlutusScripts} + * @param {ScriptHash} key + * @param {MintAssets} value + * @returns {MintAssets | void} */ - static from_bytes(bytes: Uint8Array): PlutusScripts; + insert(key: ScriptHash, value: MintAssets): MintAssets | void; /** - * @returns {string} + * @param {ScriptHash} key + * @returns {MintsAssets | void} */ - to_hex(): string; + get(key: ScriptHash): MintsAssets | void; /** - * @param {string} hex_str - * @returns {PlutusScripts} + * @returns {ScriptHashes} */ - static from_hex(hex_str: string): PlutusScripts; + keys(): ScriptHashes; /** - * @returns {string} + * Returns the multiasset where only positive (minting) entries are present + * @returns {MultiAsset} */ - to_json(): string; + as_positive_multiasset(): MultiAsset; /** - * @returns {PlutusScriptsJSON} + * Returns the multiasset where only negative (burning) entries are present + * @returns {MultiAsset} */ - to_js_value(): PlutusScriptsJSON; + as_negative_multiasset(): MultiAsset; +} +/** + */ +declare export class MintAssets { + free(): void; /** - * @param {string} json - * @returns {PlutusScripts} + * @returns {MintAssets} */ - static from_json(json: string): PlutusScripts; + static new(): MintAssets; /** - * @returns {PlutusScripts} + * @param {AssetName} key + * @param {Int} value + * @returns {MintAssets} */ - static new(): PlutusScripts; + static new_from_entry(key: AssetName, value: Int): MintAssets; /** * @returns {number} @@ -5243,205 +5811,148 @@ declare export class PlutusScripts { len(): number; /** - * @param {number} index - * @returns {PlutusScript} + * @param {AssetName} key + * @param {Int} value + * @returns {Int | void} */ - get(index: number): PlutusScript; + insert(key: AssetName, value: Int): Int | void; /** - * @param {PlutusScript} elem + * @param {AssetName} key + * @returns {Int | void} */ - add(elem: PlutusScript): void; + get(key: AssetName): Int | void; + + /** + * @returns {AssetNames} + */ + keys(): AssetNames; } /** */ -declare export class PlutusWitness { +declare export class MintBuilder { free(): void; /** - * @param {PlutusScript} script - * @param {PlutusData} datum - * @param {Redeemer} redeemer - * @returns {PlutusWitness} - */ - static new( - script: PlutusScript, - datum: PlutusData, - redeemer: Redeemer - ): PlutusWitness; - - /** - * @param {PlutusScriptSource} script - * @param {DatumSource} datum - * @param {Redeemer} redeemer - * @returns {PlutusWitness} + * @returns {MintBuilder} */ - static new_with_ref( - script: PlutusScriptSource, - datum: DatumSource, - redeemer: Redeemer - ): PlutusWitness; + static new(): MintBuilder; /** - * @param {PlutusScript} script - * @param {Redeemer} redeemer - * @returns {PlutusWitness} + * @param {MintWitness} mint + * @param {AssetName} asset_name + * @param {Int} amount */ - static new_without_datum( - script: PlutusScript, - redeemer: Redeemer - ): PlutusWitness; + add_asset(mint: MintWitness, asset_name: AssetName, amount: Int): void; /** - * @param {PlutusScriptSource} script - * @param {Redeemer} redeemer - * @returns {PlutusWitness} + * @param {MintWitness} mint + * @param {AssetName} asset_name + * @param {Int} amount */ - static new_with_ref_without_datum( - script: PlutusScriptSource, - redeemer: Redeemer - ): PlutusWitness; + set_asset(mint: MintWitness, asset_name: AssetName, amount: Int): void; /** - * @returns {PlutusScript | void} + * @returns {Mint} */ - script(): PlutusScript | void; + build(): Mint; /** - * @returns {PlutusData | void} + * @returns {NativeScripts} */ - datum(): PlutusData | void; + get_native_scripts(): NativeScripts; /** - * @returns {Redeemer} + * @returns {PlutusWitnesses} */ - redeemer(): Redeemer; -} -/** - */ -declare export class PlutusWitnesses { - free(): void; + get_plutus_witnesses(): PlutusWitnesses; /** - * @returns {PlutusWitnesses} + * @returns {TransactionInputs} */ - static new(): PlutusWitnesses; + get_ref_inputs(): TransactionInputs; /** - * @returns {number} + * @returns {Redeemers} */ - len(): number; + get_redeemers(): Redeemers; /** - * @param {number} index - * @returns {PlutusWitness} + * @returns {boolean} */ - get(index: number): PlutusWitness; + has_plutus_scripts(): boolean; /** - * @param {PlutusWitness} elem + * @returns {boolean} */ - add(elem: PlutusWitness): void; + has_native_scripts(): boolean; } /** */ -declare export class Pointer { +declare export class MintWitness { free(): void; /** - * !!! DEPRECATED !!! - * This constructor uses outdated slot number format for the ttl value, tx_index and cert_index. - * Use `.new_pointer` instead - * @param {number} slot - * @param {number} tx_index - * @param {number} cert_index - * @returns {Pointer} + * @param {NativeScriptSource} native_script + * @returns {MintWitness} */ - static new(slot: number, tx_index: number, cert_index: number): Pointer; + static new_native_script(native_script: NativeScriptSource): MintWitness; /** - * @param {BigNum} slot - * @param {BigNum} tx_index - * @param {BigNum} cert_index - * @returns {Pointer} - */ - static new_pointer( - slot: BigNum, - tx_index: BigNum, - cert_index: BigNum - ): Pointer; - - /** - * @returns {number} - */ - slot(): number; - - /** - * @returns {number} - */ - tx_index(): number; - - /** - * @returns {number} - */ - cert_index(): number; - - /** - * @returns {BigNum} + * @param {PlutusScriptSource} plutus_script + * @param {Redeemer} redeemer + * @returns {MintWitness} */ - slot_bignum(): BigNum; + static new_plutus_script( + plutus_script: PlutusScriptSource, + redeemer: Redeemer + ): MintWitness; +} +/** + */ +declare export class MintsAssets { + free(): void; /** - * @returns {BigNum} + * @returns {string} */ - tx_index_bignum(): BigNum; + to_json(): string; /** - * @returns {BigNum} + * @returns {MintsAssetsJSON} */ - cert_index_bignum(): BigNum; -} -/** - */ -declare export class PointerAddress { - free(): void; + to_js_value(): MintsAssetsJSON; /** - * @param {number} network - * @param {StakeCredential} payment - * @param {Pointer} stake - * @returns {PointerAddress} + * @param {string} json + * @returns {MintsAssets} */ - static new( - network: number, - payment: StakeCredential, - stake: Pointer - ): PointerAddress; + static from_json(json: string): MintsAssets; /** - * @returns {StakeCredential} + * @returns {MintsAssets} */ - payment_cred(): StakeCredential; + static new(): MintsAssets; /** - * @returns {Pointer} + * @param {MintAssets} mint_assets */ - stake_pointer(): Pointer; + add(mint_assets: MintAssets): void; /** - * @returns {Address} + * @param {number} index + * @returns {MintAssets | void} */ - to_address(): Address; + get(index: number): MintAssets | void; /** - * @param {Address} addr - * @returns {PointerAddress | void} + * @returns {number} */ - static from_address(addr: Address): PointerAddress | void; + len(): number; } /** */ -declare export class PoolMetadata { +declare export class MoveInstantaneousReward { free(): void; /** @@ -5451,9 +5962,9 @@ declare export class PoolMetadata { /** * @param {Uint8Array} bytes - * @returns {PoolMetadata} + * @returns {MoveInstantaneousReward} */ - static from_bytes(bytes: Uint8Array): PoolMetadata; + static from_bytes(bytes: Uint8Array): MoveInstantaneousReward; /** * @returns {string} @@ -5462,9 +5973,9 @@ declare export class PoolMetadata { /** * @param {string} hex_str - * @returns {PoolMetadata} + * @returns {MoveInstantaneousReward} */ - static from_hex(hex_str: string): PoolMetadata; + static from_hex(hex_str: string): MoveInstantaneousReward; /** * @returns {string} @@ -5472,75 +5983,67 @@ declare export class PoolMetadata { to_json(): string; /** - * @returns {PoolMetadataJSON} + * @returns {MoveInstantaneousRewardJSON} */ - to_js_value(): PoolMetadataJSON; + to_js_value(): MoveInstantaneousRewardJSON; /** * @param {string} json - * @returns {PoolMetadata} - */ - static from_json(json: string): PoolMetadata; - - /** - * @returns {URL} - */ - url(): URL; - - /** - * @returns {PoolMetadataHash} + * @returns {MoveInstantaneousReward} */ - pool_metadata_hash(): PoolMetadataHash; + static from_json(json: string): MoveInstantaneousReward; /** - * @param {URL} url - * @param {PoolMetadataHash} pool_metadata_hash - * @returns {PoolMetadata} - */ - static new(url: URL, pool_metadata_hash: PoolMetadataHash): PoolMetadata; -} -/** + * @param {$Values< + typeof + MIRPot>} pot + * @param {BigNum} amount + * @returns {MoveInstantaneousReward} */ -declare export class PoolMetadataHash { - free(): void; - - /** - * @param {Uint8Array} bytes - * @returns {PoolMetadataHash} - */ - static from_bytes(bytes: Uint8Array): PoolMetadataHash; + static new_to_other_pot( + pot: $Values, + amount: BigNum + ): MoveInstantaneousReward; /** - * @returns {Uint8Array} - */ - to_bytes(): Uint8Array; + * @param {$Values< + typeof + MIRPot>} pot + * @param {MIRToStakeCredentials} amounts + * @returns {MoveInstantaneousReward} + */ + static new_to_stake_creds( + pot: $Values, + amounts: MIRToStakeCredentials + ): MoveInstantaneousReward; /** - * @param {string} prefix - * @returns {string} - */ - to_bech32(prefix: string): string; + * @returns {$Values< + typeof + MIRPot>} + */ + pot(): $Values; /** - * @param {string} bech_str - * @returns {PoolMetadataHash} - */ - static from_bech32(bech_str: string): PoolMetadataHash; + * @returns {$Values< + typeof + MIRKind>} + */ + kind(): $Values; /** - * @returns {string} + * @returns {BigNum | void} */ - to_hex(): string; + as_to_other_pot(): BigNum | void; /** - * @param {string} hex - * @returns {PoolMetadataHash} + * @returns {MIRToStakeCredentials | void} */ - static from_hex(hex: string): PoolMetadataHash; + as_to_stake_creds(): MIRToStakeCredentials | void; } /** */ -declare export class PoolParams { +declare export class MoveInstantaneousRewardsCert { free(): void; /** @@ -5550,9 +6053,9 @@ declare export class PoolParams { /** * @param {Uint8Array} bytes - * @returns {PoolParams} + * @returns {MoveInstantaneousRewardsCert} */ - static from_bytes(bytes: Uint8Array): PoolParams; + static from_bytes(bytes: Uint8Array): MoveInstantaneousRewardsCert; /** * @returns {string} @@ -5561,9 +6064,9 @@ declare export class PoolParams { /** * @param {string} hex_str - * @returns {PoolParams} + * @returns {MoveInstantaneousRewardsCert} */ - static from_hex(hex_str: string): PoolParams; + static from_hex(hex_str: string): MoveInstantaneousRewardsCert; /** * @returns {string} @@ -5571,88 +6074,32 @@ declare export class PoolParams { to_json(): string; /** - * @returns {PoolParamsJSON} + * @returns {MoveInstantaneousRewardsCertJSON} */ - to_js_value(): PoolParamsJSON; + to_js_value(): MoveInstantaneousRewardsCertJSON; /** * @param {string} json - * @returns {PoolParams} - */ - static from_json(json: string): PoolParams; - - /** - * @returns {Ed25519KeyHash} - */ - operator(): Ed25519KeyHash; - - /** - * @returns {VRFKeyHash} - */ - vrf_keyhash(): VRFKeyHash; - - /** - * @returns {BigNum} - */ - pledge(): BigNum; - - /** - * @returns {BigNum} - */ - cost(): BigNum; - - /** - * @returns {UnitInterval} - */ - margin(): UnitInterval; - - /** - * @returns {RewardAddress} - */ - reward_account(): RewardAddress; - - /** - * @returns {Ed25519KeyHashes} - */ - pool_owners(): Ed25519KeyHashes; - - /** - * @returns {Relays} + * @returns {MoveInstantaneousRewardsCert} */ - relays(): Relays; + static from_json(json: string): MoveInstantaneousRewardsCert; /** - * @returns {PoolMetadata | void} + * @returns {MoveInstantaneousReward} */ - pool_metadata(): PoolMetadata | void; + move_instantaneous_reward(): MoveInstantaneousReward; /** - * @param {Ed25519KeyHash} operator - * @param {VRFKeyHash} vrf_keyhash - * @param {BigNum} pledge - * @param {BigNum} cost - * @param {UnitInterval} margin - * @param {RewardAddress} reward_account - * @param {Ed25519KeyHashes} pool_owners - * @param {Relays} relays - * @param {PoolMetadata | void} pool_metadata - * @returns {PoolParams} + * @param {MoveInstantaneousReward} move_instantaneous_reward + * @returns {MoveInstantaneousRewardsCert} */ static new( - operator: Ed25519KeyHash, - vrf_keyhash: VRFKeyHash, - pledge: BigNum, - cost: BigNum, - margin: UnitInterval, - reward_account: RewardAddress, - pool_owners: Ed25519KeyHashes, - relays: Relays, - pool_metadata?: PoolMetadata - ): PoolParams; + move_instantaneous_reward: MoveInstantaneousReward + ): MoveInstantaneousRewardsCert; } /** */ -declare export class PoolRegistration { +declare export class MultiAsset { free(): void; /** @@ -5662,9 +6109,9 @@ declare export class PoolRegistration { /** * @param {Uint8Array} bytes - * @returns {PoolRegistration} + * @returns {MultiAsset} */ - static from_bytes(bytes: Uint8Array): PoolRegistration; + static from_bytes(bytes: Uint8Array): MultiAsset; /** * @returns {string} @@ -5673,9 +6120,9 @@ declare export class PoolRegistration { /** * @param {string} hex_str - * @returns {PoolRegistration} + * @returns {MultiAsset} */ - static from_hex(hex_str: string): PoolRegistration; + static from_hex(hex_str: string): MultiAsset; /** * @returns {string} @@ -5683,163 +6130,136 @@ declare export class PoolRegistration { to_json(): string; /** - * @returns {PoolRegistrationJSON} + * @returns {MultiAssetJSON} */ - to_js_value(): PoolRegistrationJSON; + to_js_value(): MultiAssetJSON; /** * @param {string} json - * @returns {PoolRegistration} + * @returns {MultiAsset} */ - static from_json(json: string): PoolRegistration; + static from_json(json: string): MultiAsset; /** - * @returns {PoolParams} + * @returns {MultiAsset} */ - pool_params(): PoolParams; + static new(): MultiAsset; /** - * @param {PoolParams} pool_params - * @returns {PoolRegistration} + * the number of unique policy IDs in the multiasset + * @returns {number} */ - static new(pool_params: PoolParams): PoolRegistration; -} -/** - */ -declare export class PoolRetirement { - free(): void; - - /** - * @returns {Uint8Array} - */ - to_bytes(): Uint8Array; - - /** - * @param {Uint8Array} bytes - * @returns {PoolRetirement} - */ - static from_bytes(bytes: Uint8Array): PoolRetirement; - - /** - * @returns {string} - */ - to_hex(): string; - - /** - * @param {string} hex_str - * @returns {PoolRetirement} - */ - static from_hex(hex_str: string): PoolRetirement; + len(): number; /** - * @returns {string} + * set (and replace if it exists) all assets with policy {policy_id} to a copy of {assets} + * @param {ScriptHash} policy_id + * @param {Assets} assets + * @returns {Assets | void} */ - to_json(): string; + insert(policy_id: ScriptHash, assets: Assets): Assets | void; /** - * @returns {PoolRetirementJSON} + * all assets under {policy_id}, if any exist, or else None (undefined in JS) + * @param {ScriptHash} policy_id + * @returns {Assets | void} */ - to_js_value(): PoolRetirementJSON; + get(policy_id: ScriptHash): Assets | void; /** - * @param {string} json - * @returns {PoolRetirement} + * sets the asset {asset_name} to {value} under policy {policy_id} + * returns the previous amount if it was set, or else None (undefined in JS) + * @param {ScriptHash} policy_id + * @param {AssetName} asset_name + * @param {BigNum} value + * @returns {BigNum | void} */ - static from_json(json: string): PoolRetirement; + set_asset( + policy_id: ScriptHash, + asset_name: AssetName, + value: BigNum + ): BigNum | void; /** - * @returns {Ed25519KeyHash} + * returns the amount of asset {asset_name} under policy {policy_id} + * If such an asset does not exist, 0 is returned. + * @param {ScriptHash} policy_id + * @param {AssetName} asset_name + * @returns {BigNum} */ - pool_keyhash(): Ed25519KeyHash; + get_asset(policy_id: ScriptHash, asset_name: AssetName): BigNum; /** - * @returns {number} + * returns all policy IDs used by assets in this multiasset + * @returns {ScriptHashes} */ - epoch(): number; + keys(): ScriptHashes; /** - * @param {Ed25519KeyHash} pool_keyhash - * @param {number} epoch - * @returns {PoolRetirement} + * removes an asset from the list if the result is 0 or less + * does not modify this object, instead the result is returned + * @param {MultiAsset} rhs_ma + * @returns {MultiAsset} */ - static new(pool_keyhash: Ed25519KeyHash, epoch: number): PoolRetirement; + sub(rhs_ma: MultiAsset): MultiAsset; } /** */ -declare export class PrivateKey { +declare export class MultiHostName { free(): void; /** - * @returns {PublicKey} - */ - to_public(): PublicKey; - - /** - * @returns {PrivateKey} - */ - static generate_ed25519(): PrivateKey; - - /** - * @returns {PrivateKey} + * @returns {Uint8Array} */ - static generate_ed25519extended(): PrivateKey; + to_bytes(): Uint8Array; /** - * Get private key from its bech32 representation - * ```javascript - * PrivateKey.from_bech32('ed25519_sk1ahfetf02qwwg4dkq7mgp4a25lx5vh9920cr5wnxmpzz9906qvm8qwvlts0'); - * ``` - * For an extended 25519 key - * ```javascript - * PrivateKey.from_bech32('ed25519e_sk1gqwl4szuwwh6d0yk3nsqcc6xxc3fpvjlevgwvt60df59v8zd8f8prazt8ln3lmz096ux3xvhhvm3ca9wj2yctdh3pnw0szrma07rt5gl748fp'); - * ``` - * @param {string} bech32_str - * @returns {PrivateKey} + * @param {Uint8Array} bytes + * @returns {MultiHostName} */ - static from_bech32(bech32_str: string): PrivateKey; + static from_bytes(bytes: Uint8Array): MultiHostName; /** * @returns {string} */ - to_bech32(): string; + to_hex(): string; /** - * @returns {Uint8Array} + * @param {string} hex_str + * @returns {MultiHostName} */ - as_bytes(): Uint8Array; + static from_hex(hex_str: string): MultiHostName; /** - * @param {Uint8Array} bytes - * @returns {PrivateKey} + * @returns {string} */ - static from_extended_bytes(bytes: Uint8Array): PrivateKey; + to_json(): string; /** - * @param {Uint8Array} bytes - * @returns {PrivateKey} + * @returns {MultiHostNameJSON} */ - static from_normal_bytes(bytes: Uint8Array): PrivateKey; + to_js_value(): MultiHostNameJSON; /** - * @param {Uint8Array} message - * @returns {Ed25519Signature} + * @param {string} json + * @returns {MultiHostName} */ - sign(message: Uint8Array): Ed25519Signature; + static from_json(json: string): MultiHostName; /** - * @returns {string} + * @returns {DNSRecordSRV} */ - to_hex(): string; + dns_name(): DNSRecordSRV; /** - * @param {string} hex_str - * @returns {PrivateKey} + * @param {DNSRecordSRV} dns_name + * @returns {MultiHostName} */ - static from_hex(hex_str: string): PrivateKey; + static new(dns_name: DNSRecordSRV): MultiHostName; } /** */ -declare export class ProposedProtocolParameterUpdates { +declare export class NativeScript { free(): void; /** @@ -5849,9 +6269,9 @@ declare export class ProposedProtocolParameterUpdates { /** * @param {Uint8Array} bytes - * @returns {ProposedProtocolParameterUpdates} + * @returns {NativeScript} */ - static from_bytes(bytes: Uint8Array): ProposedProtocolParameterUpdates; + static from_bytes(bytes: Uint8Array): NativeScript; /** * @returns {string} @@ -5860,9 +6280,9 @@ declare export class ProposedProtocolParameterUpdates { /** * @param {string} hex_str - * @returns {ProposedProtocolParameterUpdates} + * @returns {NativeScript} */ - static from_hex(hex_str: string): ProposedProtocolParameterUpdates; + static from_hex(hex_str: string): NativeScript; /** * @returns {string} @@ -5870,332 +6290,373 @@ declare export class ProposedProtocolParameterUpdates { to_json(): string; /** - * @returns {ProposedProtocolParameterUpdatesJSON} + * @returns {NativeScriptJSON} */ - to_js_value(): ProposedProtocolParameterUpdatesJSON; + to_js_value(): NativeScriptJSON; /** * @param {string} json - * @returns {ProposedProtocolParameterUpdates} + * @returns {NativeScript} */ - static from_json(json: string): ProposedProtocolParameterUpdates; + static from_json(json: string): NativeScript; /** - * @returns {ProposedProtocolParameterUpdates} + * @returns {ScriptHash} */ - static new(): ProposedProtocolParameterUpdates; + hash(): ScriptHash; /** - * @returns {number} + * @param {ScriptPubkey} script_pubkey + * @returns {NativeScript} */ - len(): number; + static new_script_pubkey(script_pubkey: ScriptPubkey): NativeScript; /** - * @param {GenesisHash} key - * @param {ProtocolParamUpdate} value - * @returns {ProtocolParamUpdate | void} + * @param {ScriptAll} script_all + * @returns {NativeScript} */ - insert( - key: GenesisHash, - value: ProtocolParamUpdate - ): ProtocolParamUpdate | void; + static new_script_all(script_all: ScriptAll): NativeScript; /** - * @param {GenesisHash} key - * @returns {ProtocolParamUpdate | void} + * @param {ScriptAny} script_any + * @returns {NativeScript} */ - get(key: GenesisHash): ProtocolParamUpdate | void; + static new_script_any(script_any: ScriptAny): NativeScript; /** - * @returns {GenesisHashes} + * @param {ScriptNOfK} script_n_of_k + * @returns {NativeScript} */ - keys(): GenesisHashes; -} -/** - */ -declare export class ProtocolParamUpdate { - free(): void; + static new_script_n_of_k(script_n_of_k: ScriptNOfK): NativeScript; /** - * @returns {Uint8Array} + * @param {TimelockStart} timelock_start + * @returns {NativeScript} */ - to_bytes(): Uint8Array; + static new_timelock_start(timelock_start: TimelockStart): NativeScript; /** - * @param {Uint8Array} bytes - * @returns {ProtocolParamUpdate} + * @param {TimelockExpiry} timelock_expiry + * @returns {NativeScript} */ - static from_bytes(bytes: Uint8Array): ProtocolParamUpdate; + static new_timelock_expiry(timelock_expiry: TimelockExpiry): NativeScript; /** - * @returns {string} - */ - to_hex(): string; + * @returns {$Values< + typeof + NativeScriptKind>} + */ + kind(): $Values; /** - * @param {string} hex_str - * @returns {ProtocolParamUpdate} + * @returns {ScriptPubkey | void} */ - static from_hex(hex_str: string): ProtocolParamUpdate; + as_script_pubkey(): ScriptPubkey | void; /** - * @returns {string} + * @returns {ScriptAll | void} */ - to_json(): string; + as_script_all(): ScriptAll | void; /** - * @returns {ProtocolParamUpdateJSON} + * @returns {ScriptAny | void} */ - to_js_value(): ProtocolParamUpdateJSON; + as_script_any(): ScriptAny | void; /** - * @param {string} json - * @returns {ProtocolParamUpdate} + * @returns {ScriptNOfK | void} */ - static from_json(json: string): ProtocolParamUpdate; + as_script_n_of_k(): ScriptNOfK | void; /** - * @param {BigNum} minfee_a + * @returns {TimelockStart | void} */ - set_minfee_a(minfee_a: BigNum): void; + as_timelock_start(): TimelockStart | void; /** - * @returns {BigNum | void} + * @returns {TimelockExpiry | void} */ - minfee_a(): BigNum | void; + as_timelock_expiry(): TimelockExpiry | void; /** - * @param {BigNum} minfee_b + * Returns a set of Ed25519KeyHashes + * contained within this script recursively on any depth level. + * The order of the keys in the result is not determined in any way. + * @returns {Ed25519KeyHashes} */ - set_minfee_b(minfee_b: BigNum): void; + get_required_signers(): Ed25519KeyHashes; +} +/** + */ +declare export class NativeScriptSource { + free(): void; /** - * @returns {BigNum | void} + * @param {NativeScript} script + * @returns {NativeScriptSource} */ - minfee_b(): BigNum | void; + static new(script: NativeScript): NativeScriptSource; /** - * @param {number} max_block_body_size + * @param {ScriptHash} script_hash + * @param {TransactionInput} input + * @param {number} script_size + * @returns {NativeScriptSource} */ - set_max_block_body_size(max_block_body_size: number): void; + static new_ref_input( + script_hash: ScriptHash, + input: TransactionInput, + script_size: number + ): NativeScriptSource; /** - * @returns {number | void} - */ - max_block_body_size(): number | void; - - /** - * @param {number} max_tx_size + * @param {Ed25519KeyHashes} key_hashes */ - set_max_tx_size(max_tx_size: number): void; + set_required_signers(key_hashes: Ed25519KeyHashes): void; /** * @returns {number | void} */ - max_tx_size(): number | void; + get_ref_script_size(): number | void; +} +/** + */ +declare export class NativeScripts { + free(): void; /** - * @param {number} max_block_header_size + * @returns {NativeScripts} */ - set_max_block_header_size(max_block_header_size: number): void; + static new(): NativeScripts; /** - * @returns {number | void} + * @returns {number} */ - max_block_header_size(): number | void; + len(): number; /** - * @param {BigNum} key_deposit + * @param {number} index + * @returns {NativeScript} */ - set_key_deposit(key_deposit: BigNum): void; + get(index: number): NativeScript; /** - * @returns {BigNum | void} + * @param {NativeScript} elem */ - key_deposit(): BigNum | void; + add(elem: NativeScript): void; /** - * @param {BigNum} pool_deposit + * @returns {Uint8Array} */ - set_pool_deposit(pool_deposit: BigNum): void; + to_bytes(): Uint8Array; /** - * @returns {BigNum | void} + * @param {Uint8Array} bytes + * @returns {NativeScripts} */ - pool_deposit(): BigNum | void; + static from_bytes(bytes: Uint8Array): NativeScripts; /** - * @param {number} max_epoch + * @returns {string} */ - set_max_epoch(max_epoch: number): void; + to_hex(): string; /** - * @returns {number | void} + * @param {string} hex_str + * @returns {NativeScripts} */ - max_epoch(): number | void; + static from_hex(hex_str: string): NativeScripts; /** - * @param {number} n_opt + * @returns {string} */ - set_n_opt(n_opt: number): void; + to_json(): string; /** - * @returns {number | void} + * @returns {NativeScriptsJSON} */ - n_opt(): number | void; + to_js_value(): NativeScriptsJSON; /** - * @param {UnitInterval} pool_pledge_influence + * @param {string} json + * @returns {NativeScripts} */ - set_pool_pledge_influence(pool_pledge_influence: UnitInterval): void; + static from_json(json: string): NativeScripts; +} +/** + */ +declare export class NetworkId { + free(): void; /** - * @returns {UnitInterval | void} + * @returns {Uint8Array} */ - pool_pledge_influence(): UnitInterval | void; + to_bytes(): Uint8Array; /** - * @param {UnitInterval} expansion_rate + * @param {Uint8Array} bytes + * @returns {NetworkId} */ - set_expansion_rate(expansion_rate: UnitInterval): void; + static from_bytes(bytes: Uint8Array): NetworkId; /** - * @returns {UnitInterval | void} + * @returns {string} */ - expansion_rate(): UnitInterval | void; + to_hex(): string; /** - * @param {UnitInterval} treasury_growth_rate + * @param {string} hex_str + * @returns {NetworkId} */ - set_treasury_growth_rate(treasury_growth_rate: UnitInterval): void; + static from_hex(hex_str: string): NetworkId; /** - * @returns {UnitInterval | void} + * @returns {string} */ - treasury_growth_rate(): UnitInterval | void; + to_json(): string; /** - * !!! DEPRECATED !!! - * Since babbage era this param is outdated. But this param you can meet in a pre-babbage block. - * @returns {UnitInterval | void} + * @returns {NetworkIdJSON} */ - d(): UnitInterval | void; + to_js_value(): NetworkIdJSON; /** - * !!! DEPRECATED !!! - * Since babbage era this param is outdated. But this param you can meet in a pre-babbage block. - * @returns {Nonce | void} + * @param {string} json + * @returns {NetworkId} */ - extra_entropy(): Nonce | void; + static from_json(json: string): NetworkId; /** - * @param {ProtocolVersion} protocol_version + * @returns {NetworkId} */ - set_protocol_version(protocol_version: ProtocolVersion): void; + static testnet(): NetworkId; /** - * @returns {ProtocolVersion | void} + * @returns {NetworkId} */ - protocol_version(): ProtocolVersion | void; + static mainnet(): NetworkId; /** - * @param {BigNum} min_pool_cost - */ - set_min_pool_cost(min_pool_cost: BigNum): void; + * @returns {$Values< + typeof + NetworkIdKind>} + */ + kind(): $Values; +} +/** + */ +declare export class NetworkInfo { + free(): void; /** - * @returns {BigNum | void} + * @param {number} network_id + * @param {number} protocol_magic + * @returns {NetworkInfo} */ - min_pool_cost(): BigNum | void; + static new(network_id: number, protocol_magic: number): NetworkInfo; /** - * @param {BigNum} ada_per_utxo_byte + * @returns {number} */ - set_ada_per_utxo_byte(ada_per_utxo_byte: BigNum): void; + network_id(): number; /** - * @returns {BigNum | void} + * @returns {number} */ - ada_per_utxo_byte(): BigNum | void; + protocol_magic(): number; /** - * @param {Costmdls} cost_models + * @returns {NetworkInfo} */ - set_cost_models(cost_models: Costmdls): void; + static testnet_preview(): NetworkInfo; /** - * @returns {Costmdls | void} + * @returns {NetworkInfo} */ - cost_models(): Costmdls | void; + static testnet_preprod(): NetworkInfo; /** - * @param {ExUnitPrices} execution_costs + * @returns {NetworkInfo} */ - set_execution_costs(execution_costs: ExUnitPrices): void; + static mainnet(): NetworkInfo; +} +/** + */ +declare export class NewConstitutionAction { + free(): void; /** - * @returns {ExUnitPrices | void} + * @returns {Uint8Array} */ - execution_costs(): ExUnitPrices | void; + to_bytes(): Uint8Array; /** - * @param {ExUnits} max_tx_ex_units + * @param {Uint8Array} bytes + * @returns {NewConstitutionAction} */ - set_max_tx_ex_units(max_tx_ex_units: ExUnits): void; + static from_bytes(bytes: Uint8Array): NewConstitutionAction; /** - * @returns {ExUnits | void} + * @returns {string} */ - max_tx_ex_units(): ExUnits | void; + to_hex(): string; /** - * @param {ExUnits} max_block_ex_units + * @param {string} hex_str + * @returns {NewConstitutionAction} */ - set_max_block_ex_units(max_block_ex_units: ExUnits): void; + static from_hex(hex_str: string): NewConstitutionAction; /** - * @returns {ExUnits | void} + * @returns {string} */ - max_block_ex_units(): ExUnits | void; + to_json(): string; /** - * @param {number} max_value_size + * @returns {NewConstitutionActionJSON} */ - set_max_value_size(max_value_size: number): void; + to_js_value(): NewConstitutionActionJSON; /** - * @returns {number | void} + * @param {string} json + * @returns {NewConstitutionAction} */ - max_value_size(): number | void; + static from_json(json: string): NewConstitutionAction; /** - * @param {number} collateral_percentage + * @returns {GovernanceActionId | void} */ - set_collateral_percentage(collateral_percentage: number): void; + gov_action_id(): GovernanceActionId | void; /** - * @returns {number | void} + * @returns {Constitution} */ - collateral_percentage(): number | void; + constitution(): Constitution; /** - * @param {number} max_collateral_inputs + * @param {Constitution} constitution + * @returns {NewConstitutionAction} */ - set_max_collateral_inputs(max_collateral_inputs: number): void; + static new(constitution: Constitution): NewConstitutionAction; /** - * @returns {number | void} + * @param {GovernanceActionId} gov_action_id + * @param {Constitution} constitution + * @returns {NewConstitutionAction} */ - max_collateral_inputs(): number | void; + static new_with_action_id( + gov_action_id: GovernanceActionId, + constitution: Constitution + ): NewConstitutionAction; /** - * @returns {ProtocolParamUpdate} + * @returns {boolean} */ - static new(): ProtocolParamUpdate; + has_script_hash(): boolean; } /** */ -declare export class ProtocolVersion { +declare export class NoConfidenceAction { free(): void; /** @@ -6205,9 +6666,9 @@ declare export class ProtocolVersion { /** * @param {Uint8Array} bytes - * @returns {ProtocolVersion} + * @returns {NoConfidenceAction} */ - static from_bytes(bytes: Uint8Array): ProtocolVersion; + static from_bytes(bytes: Uint8Array): NoConfidenceAction; /** * @returns {string} @@ -6216,9 +6677,9 @@ declare export class ProtocolVersion { /** * @param {string} hex_str - * @returns {ProtocolVersion} + * @returns {NoConfidenceAction} */ - static from_hex(hex_str: string): ProtocolVersion; + static from_hex(hex_str: string): NoConfidenceAction; /** * @returns {string} @@ -6226,194 +6687,200 @@ declare export class ProtocolVersion { to_json(): string; /** - * @returns {ProtocolVersionJSON} + * @returns {NoConfidenceActionJSON} */ - to_js_value(): ProtocolVersionJSON; + to_js_value(): NoConfidenceActionJSON; /** * @param {string} json - * @returns {ProtocolVersion} + * @returns {NoConfidenceAction} */ - static from_json(json: string): ProtocolVersion; + static from_json(json: string): NoConfidenceAction; /** - * @returns {number} + * @returns {GovernanceActionId | void} */ - major(): number; + gov_action_id(): GovernanceActionId | void; /** - * @returns {number} + * @returns {NoConfidenceAction} */ - minor(): number; + static new(): NoConfidenceAction; /** - * @param {number} major - * @param {number} minor - * @returns {ProtocolVersion} + * @param {GovernanceActionId} gov_action_id + * @returns {NoConfidenceAction} */ - static new(major: number, minor: number): ProtocolVersion; + static new_with_action_id( + gov_action_id: GovernanceActionId + ): NoConfidenceAction; } /** - * ED25519 key used as public key */ -declare export class PublicKey { +declare export class Nonce { free(): void; /** - * Get public key from its bech32 representation - * Example: - * ```javascript - * const pkey = PublicKey.from_bech32('ed25519_pk1dgaagyh470y66p899txcl3r0jaeaxu6yd7z2dxyk55qcycdml8gszkxze2'); - * ``` - * @param {string} bech32_str - * @returns {PublicKey} + * @returns {Uint8Array} */ - static from_bech32(bech32_str: string): PublicKey; + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {Nonce} + */ + static from_bytes(bytes: Uint8Array): Nonce; /** * @returns {string} */ - to_bech32(): string; + to_hex(): string; /** - * @returns {Uint8Array} + * @param {string} hex_str + * @returns {Nonce} */ - as_bytes(): Uint8Array; + static from_hex(hex_str: string): Nonce; /** - * @param {Uint8Array} bytes - * @returns {PublicKey} + * @returns {string} */ - static from_bytes(bytes: Uint8Array): PublicKey; + to_json(): string; /** - * @param {Uint8Array} data - * @param {Ed25519Signature} signature - * @returns {boolean} + * @returns {NonceJSON} */ - verify(data: Uint8Array, signature: Ed25519Signature): boolean; + to_js_value(): NonceJSON; /** - * @returns {Ed25519KeyHash} + * @param {string} json + * @returns {Nonce} */ - hash(): Ed25519KeyHash; + static from_json(json: string): Nonce; /** - * @returns {string} + * @returns {Nonce} */ - to_hex(): string; + static new_identity(): Nonce; /** - * @param {string} hex_str - * @returns {PublicKey} + * @param {Uint8Array} hash + * @returns {Nonce} */ - static from_hex(hex_str: string): PublicKey; + static new_from_hash(hash: Uint8Array): Nonce; + + /** + * @returns {Uint8Array | void} + */ + get_hash(): Uint8Array | void; } /** */ -declare export class PublicKeys { +declare export class OperationalCert { free(): void; /** + * @returns {Uint8Array} */ - constructor(): this; + to_bytes(): Uint8Array; /** - * @returns {number} + * @param {Uint8Array} bytes + * @returns {OperationalCert} */ - size(): number; + static from_bytes(bytes: Uint8Array): OperationalCert; /** - * @param {number} index - * @returns {PublicKey} + * @returns {string} */ - get(index: number): PublicKey; + to_hex(): string; /** - * @param {PublicKey} key + * @param {string} hex_str + * @returns {OperationalCert} */ - add(key: PublicKey): void; -} -/** - */ -declare export class Redeemer { - free(): void; + static from_hex(hex_str: string): OperationalCert; /** - * @returns {Uint8Array} + * @returns {string} */ - to_bytes(): Uint8Array; + to_json(): string; /** - * @param {Uint8Array} bytes - * @returns {Redeemer} + * @returns {OperationalCertJSON} */ - static from_bytes(bytes: Uint8Array): Redeemer; + to_js_value(): OperationalCertJSON; /** - * @returns {string} + * @param {string} json + * @returns {OperationalCert} */ - to_hex(): string; + static from_json(json: string): OperationalCert; /** - * @param {string} hex_str - * @returns {Redeemer} + * @returns {KESVKey} */ - static from_hex(hex_str: string): Redeemer; + hot_vkey(): KESVKey; /** - * @returns {string} + * @returns {number} */ - to_json(): string; + sequence_number(): number; /** - * @returns {RedeemerJSON} + * @returns {number} */ - to_js_value(): RedeemerJSON; + kes_period(): number; /** - * @param {string} json - * @returns {Redeemer} + * @returns {Ed25519Signature} */ - static from_json(json: string): Redeemer; + sigma(): Ed25519Signature; /** - * @returns {RedeemerTag} + * @param {KESVKey} hot_vkey + * @param {number} sequence_number + * @param {number} kes_period + * @param {Ed25519Signature} sigma + * @returns {OperationalCert} */ - tag(): RedeemerTag; + static new( + hot_vkey: KESVKey, + sequence_number: number, + kes_period: number, + sigma: Ed25519Signature + ): OperationalCert; +} +/** + */ +declare export class OutputDatum { + free(): void; /** - * @returns {BigNum} + * @param {DataHash} data_hash + * @returns {OutputDatum} */ - index(): BigNum; + static new_data_hash(data_hash: DataHash): OutputDatum; /** - * @returns {PlutusData} + * @param {PlutusData} data + * @returns {OutputDatum} */ - data(): PlutusData; + static new_data(data: PlutusData): OutputDatum; /** - * @returns {ExUnits} + * @returns {DataHash | void} */ - ex_units(): ExUnits; + data_hash(): DataHash | void; /** - * @param {RedeemerTag} tag - * @param {BigNum} index - * @param {PlutusData} data - * @param {ExUnits} ex_units - * @returns {Redeemer} + * @returns {PlutusData | void} */ - static new( - tag: RedeemerTag, - index: BigNum, - data: PlutusData, - ex_units: ExUnits - ): Redeemer; + data(): PlutusData | void; } /** */ -declare export class RedeemerTag { +declare export class ParameterChangeAction { free(): void; /** @@ -6423,9 +6890,9 @@ declare export class RedeemerTag { /** * @param {Uint8Array} bytes - * @returns {RedeemerTag} + * @returns {ParameterChangeAction} */ - static from_bytes(bytes: Uint8Array): RedeemerTag; + static from_bytes(bytes: Uint8Array): ParameterChangeAction; /** * @returns {string} @@ -6434,9 +6901,9 @@ declare export class RedeemerTag { /** * @param {string} hex_str - * @returns {RedeemerTag} + * @returns {ParameterChangeAction} */ - static from_hex(hex_str: string): RedeemerTag; + static from_hex(hex_str: string): ParameterChangeAction; /** * @returns {string} @@ -6444,44 +6911,74 @@ declare export class RedeemerTag { to_json(): string; /** - * @returns {RedeemerTagJSON} + * @returns {ParameterChangeActionJSON} */ - to_js_value(): RedeemerTagJSON; + to_js_value(): ParameterChangeActionJSON; /** * @param {string} json - * @returns {RedeemerTag} + * @returns {ParameterChangeAction} */ - static from_json(json: string): RedeemerTag; + static from_json(json: string): ParameterChangeAction; /** - * @returns {RedeemerTag} + * @returns {GovernanceActionId | void} */ - static new_spend(): RedeemerTag; + gov_action_id(): GovernanceActionId | void; /** - * @returns {RedeemerTag} + * @returns {ProtocolParamUpdate} */ - static new_mint(): RedeemerTag; + protocol_param_updates(): ProtocolParamUpdate; /** - * @returns {RedeemerTag} + * @returns {ScriptHash | void} */ - static new_cert(): RedeemerTag; + policy_hash(): ScriptHash | void; /** - * @returns {RedeemerTag} + * @param {ProtocolParamUpdate} protocol_param_updates + * @returns {ParameterChangeAction} */ - static new_reward(): RedeemerTag; + static new( + protocol_param_updates: ProtocolParamUpdate + ): ParameterChangeAction; /** - * @returns {number} + * @param {GovernanceActionId} gov_action_id + * @param {ProtocolParamUpdate} protocol_param_updates + * @returns {ParameterChangeAction} + */ + static new_with_action_id( + gov_action_id: GovernanceActionId, + protocol_param_updates: ProtocolParamUpdate + ): ParameterChangeAction; + + /** + * @param {ProtocolParamUpdate} protocol_param_updates + * @param {ScriptHash} policy_hash + * @returns {ParameterChangeAction} + */ + static new_with_policy_hash( + protocol_param_updates: ProtocolParamUpdate, + policy_hash: ScriptHash + ): ParameterChangeAction; + + /** + * @param {GovernanceActionId} gov_action_id + * @param {ProtocolParamUpdate} protocol_param_updates + * @param {ScriptHash} policy_hash + * @returns {ParameterChangeAction} */ - kind(): number; + static new_with_policy_hash_and_action_id( + gov_action_id: GovernanceActionId, + protocol_param_updates: ProtocolParamUpdate, + policy_hash: ScriptHash + ): ParameterChangeAction; } /** */ -declare export class Redeemers { +declare export class PlutusData { free(): void; /** @@ -6491,9 +6988,9 @@ declare export class Redeemers { /** * @param {Uint8Array} bytes - * @returns {Redeemers} + * @returns {PlutusData} */ - static from_bytes(bytes: Uint8Array): Redeemers; + static from_bytes(bytes: Uint8Array): PlutusData; /** * @returns {string} @@ -6502,136 +6999,168 @@ declare export class Redeemers { /** * @param {string} hex_str - * @returns {Redeemers} + * @returns {PlutusData} */ - static from_hex(hex_str: string): Redeemers; + static from_hex(hex_str: string): PlutusData; /** - * @returns {string} + * @param {ConstrPlutusData} constr_plutus_data + * @returns {PlutusData} */ - to_json(): string; + static new_constr_plutus_data( + constr_plutus_data: ConstrPlutusData + ): PlutusData; /** - * @returns {RedeemersJSON} + * Same as `.new_constr_plutus_data` but creates constr with empty data list + * @param {BigNum} alternative + * @returns {PlutusData} */ - to_js_value(): RedeemersJSON; + static new_empty_constr_plutus_data(alternative: BigNum): PlutusData; /** - * @param {string} json - * @returns {Redeemers} + * @param {BigNum} alternative + * @param {PlutusData} plutus_data + * @returns {PlutusData} */ - static from_json(json: string): Redeemers; + static new_single_value_constr_plutus_data( + alternative: BigNum, + plutus_data: PlutusData + ): PlutusData; /** - * @returns {Redeemers} + * @param {PlutusMap} map + * @returns {PlutusData} */ - static new(): Redeemers; + static new_map(map: PlutusMap): PlutusData; /** - * @returns {number} + * @param {PlutusList} list + * @returns {PlutusData} */ - len(): number; + static new_list(list: PlutusList): PlutusData; /** - * @param {number} index - * @returns {Redeemer} + * @param {BigInt} integer + * @returns {PlutusData} */ - get(index: number): Redeemer; + static new_integer(integer: BigInt): PlutusData; /** - * @param {Redeemer} elem + * @param {Uint8Array} bytes + * @returns {PlutusData} */ - add(elem: Redeemer): void; + static new_bytes(bytes: Uint8Array): PlutusData; /** - * @returns {ExUnits} - */ - total_ex_units(): ExUnits; -} -/** + * @returns {$Values< + typeof + PlutusDataKind>} */ -declare export class Relay { - free(): void; + kind(): $Values; /** - * @returns {Uint8Array} + * @returns {ConstrPlutusData | void} */ - to_bytes(): Uint8Array; + as_constr_plutus_data(): ConstrPlutusData | void; /** - * @param {Uint8Array} bytes - * @returns {Relay} + * @returns {PlutusMap | void} */ - static from_bytes(bytes: Uint8Array): Relay; + as_map(): PlutusMap | void; /** - * @returns {string} + * @returns {PlutusList | void} */ - to_hex(): string; + as_list(): PlutusList | void; /** - * @param {string} hex_str - * @returns {Relay} + * @returns {BigInt | void} */ - static from_hex(hex_str: string): Relay; + as_integer(): BigInt | void; /** - * @returns {string} + * @returns {Uint8Array | void} */ - to_json(): string; + as_bytes(): Uint8Array | void; /** - * @returns {RelayJSON} - */ - to_js_value(): RelayJSON; + * @param {$Values< + typeof + PlutusDatumSchema>} schema + * @returns {string} + */ + to_json(schema: $Values): string; /** - * @param {string} json - * @returns {Relay} - */ - static from_json(json: string): Relay; + * @param {string} json + * @param {$Values< + typeof + PlutusDatumSchema>} schema + * @returns {PlutusData} + */ + static from_json( + json: string, + schema: $Values + ): PlutusData; /** - * @param {SingleHostAddr} single_host_addr - * @returns {Relay} + * @param {Address} address + * @returns {PlutusData} */ - static new_single_host_addr(single_host_addr: SingleHostAddr): Relay; + static from_address(address: Address): PlutusData; +} +/** + */ +declare export class PlutusList { + free(): void; /** - * @param {SingleHostName} single_host_name - * @returns {Relay} + * @returns {Uint8Array} */ - static new_single_host_name(single_host_name: SingleHostName): Relay; + to_bytes(): Uint8Array; /** - * @param {MultiHostName} multi_host_name - * @returns {Relay} + * @param {Uint8Array} bytes + * @returns {PlutusList} */ - static new_multi_host_name(multi_host_name: MultiHostName): Relay; + static from_bytes(bytes: Uint8Array): PlutusList; /** - * @returns {number} + * @returns {string} */ - kind(): number; + to_hex(): string; /** - * @returns {SingleHostAddr | void} + * @param {string} hex_str + * @returns {PlutusList} */ - as_single_host_addr(): SingleHostAddr | void; + static from_hex(hex_str: string): PlutusList; /** - * @returns {SingleHostName | void} + * @returns {PlutusList} */ - as_single_host_name(): SingleHostName | void; + static new(): PlutusList; /** - * @returns {MultiHostName | void} + * @returns {number} */ - as_multi_host_name(): MultiHostName | void; + len(): number; + + /** + * @param {number} index + * @returns {PlutusData} + */ + get(index: number): PlutusData; + + /** + * @param {PlutusData} elem + */ + add(elem: PlutusData): void; } /** */ -declare export class Relays { +declare export class PlutusMap { free(): void; /** @@ -6641,9 +7170,9 @@ declare export class Relays { /** * @param {Uint8Array} bytes - * @returns {Relays} + * @returns {PlutusMap} */ - static from_bytes(bytes: Uint8Array): Relays; + static from_bytes(bytes: Uint8Array): PlutusMap; /** * @returns {string} @@ -6652,78 +7181,70 @@ declare export class Relays { /** * @param {string} hex_str - * @returns {Relays} - */ - static from_hex(hex_str: string): Relays; - - /** - * @returns {string} - */ - to_json(): string; - - /** - * @returns {RelaysJSON} + * @returns {PlutusMap} */ - to_js_value(): RelaysJSON; + static from_hex(hex_str: string): PlutusMap; /** - * @param {string} json - * @returns {Relays} + * @returns {PlutusMap} */ - static from_json(json: string): Relays; + static new(): PlutusMap; /** - * @returns {Relays} + * Return count ok different keys in the map. + * @returns {number} */ - static new(): Relays; + len(): number; /** - * @returns {number} + * Returns the previous value associated with the key, if any. + * Replace the values associated with the key. + * @param {PlutusData} key + * @param {PlutusMapValues} values + * @returns {PlutusMapValues | void} */ - len(): number; + insert(key: PlutusData, values: PlutusMapValues): PlutusMapValues | void; /** - * @param {number} index - * @returns {Relay} + * @param {PlutusData} key + * @returns {PlutusMapValues | void} */ - get(index: number): Relay; + get(key: PlutusData): PlutusMapValues | void; /** - * @param {Relay} elem + * @returns {PlutusList} */ - add(elem: Relay): void; + keys(): PlutusList; } /** */ -declare export class RewardAddress { +declare export class PlutusMapValues { free(): void; /** - * @param {number} network - * @param {StakeCredential} payment - * @returns {RewardAddress} + * @returns {PlutusMapValues} */ - static new(network: number, payment: StakeCredential): RewardAddress; + static new(): PlutusMapValues; /** - * @returns {StakeCredential} + * @returns {number} */ - payment_cred(): StakeCredential; + len(): number; /** - * @returns {Address} + * @param {number} index + * @returns {PlutusData | void} */ - to_address(): Address; + get(index: number): PlutusData | void; /** - * @param {Address} addr - * @returns {RewardAddress | void} + * @param {PlutusData} elem */ - static from_address(addr: Address): RewardAddress | void; + add(elem: PlutusData): void; } /** */ -declare export class RewardAddresses { +declare export class PlutusScript { free(): void; /** @@ -6733,9 +7254,9 @@ declare export class RewardAddresses { /** * @param {Uint8Array} bytes - * @returns {RewardAddresses} + * @returns {PlutusScript} */ - static from_bytes(bytes: Uint8Array): RewardAddresses; + static from_bytes(bytes: Uint8Array): PlutusScript; /** * @returns {string} @@ -6744,104 +7265,143 @@ declare export class RewardAddresses { /** * @param {string} hex_str - * @returns {RewardAddresses} - */ - static from_hex(hex_str: string): RewardAddresses; - - /** - * @returns {string} + * @returns {PlutusScript} */ - to_json(): string; + static from_hex(hex_str: string): PlutusScript; /** - * @returns {RewardAddressesJSON} + * + * * Creates a new Plutus script from the RAW bytes of the compiled script. + * * This does NOT include any CBOR encoding around these bytes (e.g. from "cborBytes" in cardano-cli) + * * If you creating this from those you should use PlutusScript::from_bytes() instead. + * @param {Uint8Array} bytes + * @returns {PlutusScript} */ - to_js_value(): RewardAddressesJSON; + static new(bytes: Uint8Array): PlutusScript; /** - * @param {string} json - * @returns {RewardAddresses} + * + * * Creates a new Plutus script from the RAW bytes of the compiled script. + * * This does NOT include any CBOR encoding around these bytes (e.g. from "cborBytes" in cardano-cli) + * * If you creating this from those you should use PlutusScript::from_bytes() instead. + * @param {Uint8Array} bytes + * @returns {PlutusScript} */ - static from_json(json: string): RewardAddresses; + static new_v2(bytes: Uint8Array): PlutusScript; /** - * @returns {RewardAddresses} + * + * * Creates a new Plutus script from the RAW bytes of the compiled script. + * * This does NOT include any CBOR encoding around these bytes (e.g. from "cborBytes" in cardano-cli) + * * If you creating this from those you should use PlutusScript::from_bytes() instead. + * @param {Uint8Array} bytes + * @returns {PlutusScript} */ - static new(): RewardAddresses; + static new_v3(bytes: Uint8Array): PlutusScript; /** - * @returns {number} + * + * * Creates a new Plutus script from the RAW bytes of the compiled script. + * * This does NOT include any CBOR encoding around these bytes (e.g. from "cborBytes" in cardano-cli) + * * If you creating this from those you should use PlutusScript::from_bytes() instead. + * @param {Uint8Array} bytes + * @param {Language} language + * @returns {PlutusScript} */ - len(): number; + static new_with_version(bytes: Uint8Array, language: Language): PlutusScript; /** - * @param {number} index - * @returns {RewardAddress} + * + * * The raw bytes of this compiled Plutus script. + * * If you need "cborBytes" for cardano-cli use PlutusScript::to_bytes() instead. + * @returns {Uint8Array} */ - get(index: number): RewardAddress; + bytes(): Uint8Array; /** - * @param {RewardAddress} elem + * Same as `.from_bytes` but will consider the script as requiring the Plutus Language V2 + * @param {Uint8Array} bytes + * @returns {PlutusScript} */ - add(elem: RewardAddress): void; -} -/** - */ -declare export class ScriptAll { - free(): void; + static from_bytes_v2(bytes: Uint8Array): PlutusScript; /** - * @returns {Uint8Array} + * Same as `.from_bytes` but will consider the script as requiring the Plutus Language V3 + * @param {Uint8Array} bytes + * @returns {PlutusScript} */ - to_bytes(): Uint8Array; + static from_bytes_v3(bytes: Uint8Array): PlutusScript; /** + * Same as `.from_bytes` but will consider the script as requiring the specified language version * @param {Uint8Array} bytes - * @returns {ScriptAll} + * @param {Language} language + * @returns {PlutusScript} */ - static from_bytes(bytes: Uint8Array): ScriptAll; + static from_bytes_with_version( + bytes: Uint8Array, + language: Language + ): PlutusScript; /** - * @returns {string} + * Same as .from_hex but will consider the script as requiring the specified language version + * @param {string} hex_str + * @param {Language} language + * @returns {PlutusScript} */ - to_hex(): string; + static from_hex_with_version( + hex_str: string, + language: Language + ): PlutusScript; /** - * @param {string} hex_str - * @returns {ScriptAll} + * @returns {ScriptHash} */ - static from_hex(hex_str: string): ScriptAll; + hash(): ScriptHash; /** - * @returns {string} + * @returns {Language} */ - to_json(): string; + language_version(): Language; +} +/** + */ +declare export class PlutusScriptSource { + free(): void; /** - * @returns {ScriptAllJSON} + * @param {PlutusScript} script + * @returns {PlutusScriptSource} */ - to_js_value(): ScriptAllJSON; + static new(script: PlutusScript): PlutusScriptSource; /** - * @param {string} json - * @returns {ScriptAll} + * @param {ScriptHash} script_hash + * @param {TransactionInput} input + * @param {Language} lang_ver + * @param {number} script_size + * @returns {PlutusScriptSource} */ - static from_json(json: string): ScriptAll; + static new_ref_input( + script_hash: ScriptHash, + input: TransactionInput, + lang_ver: Language, + script_size: number + ): PlutusScriptSource; /** - * @returns {NativeScripts} + * @param {Ed25519KeyHashes} key_hashes */ - native_scripts(): NativeScripts; + set_required_signers(key_hashes: Ed25519KeyHashes): void; /** - * @param {NativeScripts} native_scripts - * @returns {ScriptAll} + * @returns {number | void} */ - static new(native_scripts: NativeScripts): ScriptAll; + get_ref_script_size(): number | void; } /** */ -declare export class ScriptAny { +declare export class PlutusScripts { free(): void; /** @@ -6851,9 +7411,9 @@ declare export class ScriptAny { /** * @param {Uint8Array} bytes - * @returns {ScriptAny} + * @returns {PlutusScripts} */ - static from_bytes(bytes: Uint8Array): ScriptAny; + static from_bytes(bytes: Uint8Array): PlutusScripts; /** * @returns {string} @@ -6862,9 +7422,9 @@ declare export class ScriptAny { /** * @param {string} hex_str - * @returns {ScriptAny} + * @returns {PlutusScripts} */ - static from_hex(hex_str: string): ScriptAny; + static from_hex(hex_str: string): PlutusScripts; /** * @returns {string} @@ -6872,172 +7432,231 @@ declare export class ScriptAny { to_json(): string; /** - * @returns {ScriptAnyJSON} + * @returns {PlutusScriptsJSON} */ - to_js_value(): ScriptAnyJSON; + to_js_value(): PlutusScriptsJSON; /** * @param {string} json - * @returns {ScriptAny} + * @returns {PlutusScripts} */ - static from_json(json: string): ScriptAny; + static from_json(json: string): PlutusScripts; /** - * @returns {NativeScripts} + * @returns {PlutusScripts} */ - native_scripts(): NativeScripts; + static new(): PlutusScripts; /** - * @param {NativeScripts} native_scripts - * @returns {ScriptAny} + * @returns {number} */ - static new(native_scripts: NativeScripts): ScriptAny; + len(): number; + + /** + * @param {number} index + * @returns {PlutusScript} + */ + get(index: number): PlutusScript; + + /** + * @param {PlutusScript} elem + */ + add(elem: PlutusScript): void; } /** */ -declare export class ScriptDataHash { +declare export class PlutusWitness { free(): void; /** - * @param {Uint8Array} bytes - * @returns {ScriptDataHash} + * @param {PlutusScript} script + * @param {PlutusData} datum + * @param {Redeemer} redeemer + * @returns {PlutusWitness} */ - static from_bytes(bytes: Uint8Array): ScriptDataHash; + static new( + script: PlutusScript, + datum: PlutusData, + redeemer: Redeemer + ): PlutusWitness; /** - * @returns {Uint8Array} + * @param {PlutusScriptSource} script + * @param {DatumSource} datum + * @param {Redeemer} redeemer + * @returns {PlutusWitness} */ - to_bytes(): Uint8Array; + static new_with_ref( + script: PlutusScriptSource, + datum: DatumSource, + redeemer: Redeemer + ): PlutusWitness; /** - * @param {string} prefix - * @returns {string} + * @param {PlutusScript} script + * @param {Redeemer} redeemer + * @returns {PlutusWitness} */ - to_bech32(prefix: string): string; + static new_without_datum( + script: PlutusScript, + redeemer: Redeemer + ): PlutusWitness; /** - * @param {string} bech_str - * @returns {ScriptDataHash} + * @param {PlutusScriptSource} script + * @param {Redeemer} redeemer + * @returns {PlutusWitness} */ - static from_bech32(bech_str: string): ScriptDataHash; + static new_with_ref_without_datum( + script: PlutusScriptSource, + redeemer: Redeemer + ): PlutusWitness; /** - * @returns {string} + * @returns {PlutusScript | void} */ - to_hex(): string; + script(): PlutusScript | void; /** - * @param {string} hex - * @returns {ScriptDataHash} + * @returns {PlutusData | void} */ - static from_hex(hex: string): ScriptDataHash; + datum(): PlutusData | void; + + /** + * @returns {Redeemer} + */ + redeemer(): Redeemer; } /** */ -declare export class ScriptHash { +declare export class PlutusWitnesses { free(): void; /** - * @param {Uint8Array} bytes - * @returns {ScriptHash} + * @returns {PlutusWitnesses} */ - static from_bytes(bytes: Uint8Array): ScriptHash; + static new(): PlutusWitnesses; /** - * @returns {Uint8Array} + * @returns {number} */ - to_bytes(): Uint8Array; + len(): number; /** - * @param {string} prefix - * @returns {string} + * @param {number} index + * @returns {PlutusWitness} */ - to_bech32(prefix: string): string; + get(index: number): PlutusWitness; /** - * @param {string} bech_str - * @returns {ScriptHash} + * @param {PlutusWitness} elem */ - static from_bech32(bech_str: string): ScriptHash; + add(elem: PlutusWitness): void; +} +/** + */ +declare export class Pointer { + free(): void; /** - * @returns {string} + * !!! DEPRECATED !!! + * This constructor uses outdated slot number format for the ttl value, tx_index and cert_index. + * Use `.new_pointer` instead + * @param {number} slot + * @param {number} tx_index + * @param {number} cert_index + * @returns {Pointer} */ - to_hex(): string; + static new(slot: number, tx_index: number, cert_index: number): Pointer; /** - * @param {string} hex - * @returns {ScriptHash} + * @param {BigNum} slot + * @param {BigNum} tx_index + * @param {BigNum} cert_index + * @returns {Pointer} */ - static from_hex(hex: string): ScriptHash; -} -/** - */ -declare export class ScriptHashes { - free(): void; + static new_pointer( + slot: BigNum, + tx_index: BigNum, + cert_index: BigNum + ): Pointer; /** - * @returns {Uint8Array} + * @returns {number} */ - to_bytes(): Uint8Array; + slot(): number; /** - * @param {Uint8Array} bytes - * @returns {ScriptHashes} + * @returns {number} */ - static from_bytes(bytes: Uint8Array): ScriptHashes; + tx_index(): number; /** - * @returns {string} + * @returns {number} */ - to_hex(): string; + cert_index(): number; /** - * @param {string} hex_str - * @returns {ScriptHashes} + * @returns {BigNum} */ - static from_hex(hex_str: string): ScriptHashes; + slot_bignum(): BigNum; /** - * @returns {string} + * @returns {BigNum} */ - to_json(): string; + tx_index_bignum(): BigNum; /** - * @returns {ScriptHashesJSON} + * @returns {BigNum} */ - to_js_value(): ScriptHashesJSON; + cert_index_bignum(): BigNum; +} +/** + */ +declare export class PointerAddress { + free(): void; /** - * @param {string} json - * @returns {ScriptHashes} + * @param {number} network + * @param {Credential} payment + * @param {Pointer} stake + * @returns {PointerAddress} */ - static from_json(json: string): ScriptHashes; + static new( + network: number, + payment: Credential, + stake: Pointer + ): PointerAddress; /** - * @returns {ScriptHashes} + * @returns {Credential} */ - static new(): ScriptHashes; + payment_cred(): Credential; /** - * @returns {number} + * @returns {Pointer} */ - len(): number; + stake_pointer(): Pointer; /** - * @param {number} index - * @returns {ScriptHash} + * @returns {Address} */ - get(index: number): ScriptHash; + to_address(): Address; /** - * @param {ScriptHash} elem + * @param {Address} addr + * @returns {PointerAddress | void} */ - add(elem: ScriptHash): void; + static from_address(addr: Address): PointerAddress | void; + + /** + * @returns {number} + */ + network_id(): number; } /** */ -declare export class ScriptNOfK { +declare export class PoolMetadata { free(): void; /** @@ -7047,9 +7666,9 @@ declare export class ScriptNOfK { /** * @param {Uint8Array} bytes - * @returns {ScriptNOfK} + * @returns {PoolMetadata} */ - static from_bytes(bytes: Uint8Array): ScriptNOfK; + static from_bytes(bytes: Uint8Array): PoolMetadata; /** * @returns {string} @@ -7058,9 +7677,9 @@ declare export class ScriptNOfK { /** * @param {string} hex_str - * @returns {ScriptNOfK} + * @returns {PoolMetadata} */ - static from_hex(hex_str: string): ScriptNOfK; + static from_hex(hex_str: string): PoolMetadata; /** * @returns {string} @@ -7068,90 +7687,75 @@ declare export class ScriptNOfK { to_json(): string; /** - * @returns {ScriptNOfKJSON} + * @returns {PoolMetadataJSON} */ - to_js_value(): ScriptNOfKJSON; + to_js_value(): PoolMetadataJSON; /** * @param {string} json - * @returns {ScriptNOfK} + * @returns {PoolMetadata} */ - static from_json(json: string): ScriptNOfK; + static from_json(json: string): PoolMetadata; /** - * @returns {number} + * @returns {URL} */ - n(): number; + url(): URL; /** - * @returns {NativeScripts} + * @returns {PoolMetadataHash} */ - native_scripts(): NativeScripts; + pool_metadata_hash(): PoolMetadataHash; /** - * @param {number} n - * @param {NativeScripts} native_scripts - * @returns {ScriptNOfK} + * @param {URL} url + * @param {PoolMetadataHash} pool_metadata_hash + * @returns {PoolMetadata} */ - static new(n: number, native_scripts: NativeScripts): ScriptNOfK; + static new(url: URL, pool_metadata_hash: PoolMetadataHash): PoolMetadata; } /** */ -declare export class ScriptPubkey { +declare export class PoolMetadataHash { free(): void; - /** - * @returns {Uint8Array} - */ - to_bytes(): Uint8Array; - /** * @param {Uint8Array} bytes - * @returns {ScriptPubkey} - */ - static from_bytes(bytes: Uint8Array): ScriptPubkey; - - /** - * @returns {string} + * @returns {PoolMetadataHash} */ - to_hex(): string; + static from_bytes(bytes: Uint8Array): PoolMetadataHash; /** - * @param {string} hex_str - * @returns {ScriptPubkey} + * @returns {Uint8Array} */ - static from_hex(hex_str: string): ScriptPubkey; + to_bytes(): Uint8Array; /** + * @param {string} prefix * @returns {string} */ - to_json(): string; - - /** - * @returns {ScriptPubkeyJSON} - */ - to_js_value(): ScriptPubkeyJSON; + to_bech32(prefix: string): string; /** - * @param {string} json - * @returns {ScriptPubkey} + * @param {string} bech_str + * @returns {PoolMetadataHash} */ - static from_json(json: string): ScriptPubkey; + static from_bech32(bech_str: string): PoolMetadataHash; /** - * @returns {Ed25519KeyHash} + * @returns {string} */ - addr_keyhash(): Ed25519KeyHash; + to_hex(): string; /** - * @param {Ed25519KeyHash} addr_keyhash - * @returns {ScriptPubkey} + * @param {string} hex + * @returns {PoolMetadataHash} */ - static new(addr_keyhash: Ed25519KeyHash): ScriptPubkey; + static from_hex(hex: string): PoolMetadataHash; } /** */ -declare export class ScriptRef { +declare export class PoolParams { free(): void; /** @@ -7161,9 +7765,9 @@ declare export class ScriptRef { /** * @param {Uint8Array} bytes - * @returns {ScriptRef} + * @returns {PoolParams} */ - static from_bytes(bytes: Uint8Array): ScriptRef; + static from_bytes(bytes: Uint8Array): PoolParams; /** * @returns {string} @@ -7172,9 +7776,9 @@ declare export class ScriptRef { /** * @param {string} hex_str - * @returns {ScriptRef} + * @returns {PoolParams} */ - static from_hex(hex_str: string): ScriptRef; + static from_hex(hex_str: string): PoolParams; /** * @returns {string} @@ -7182,51 +7786,88 @@ declare export class ScriptRef { to_json(): string; /** - * @returns {ScriptRefJSON} + * @returns {PoolParamsJSON} */ - to_js_value(): ScriptRefJSON; + to_js_value(): PoolParamsJSON; /** * @param {string} json - * @returns {ScriptRef} + * @returns {PoolParams} */ - static from_json(json: string): ScriptRef; + static from_json(json: string): PoolParams; /** - * @param {NativeScript} native_script - * @returns {ScriptRef} + * @returns {Ed25519KeyHash} */ - static new_native_script(native_script: NativeScript): ScriptRef; + operator(): Ed25519KeyHash; /** - * @param {PlutusScript} plutus_script - * @returns {ScriptRef} + * @returns {VRFKeyHash} */ - static new_plutus_script(plutus_script: PlutusScript): ScriptRef; + vrf_keyhash(): VRFKeyHash; /** - * @returns {boolean} + * @returns {BigNum} */ - is_native_script(): boolean; + pledge(): BigNum; /** - * @returns {boolean} + * @returns {BigNum} */ - is_plutus_script(): boolean; + cost(): BigNum; /** - * @returns {NativeScript | void} + * @returns {UnitInterval} */ - native_script(): NativeScript | void; + margin(): UnitInterval; /** - * @returns {PlutusScript | void} + * @returns {RewardAddress} */ - plutus_script(): PlutusScript | void; + reward_account(): RewardAddress; + + /** + * @returns {Ed25519KeyHashes} + */ + pool_owners(): Ed25519KeyHashes; + + /** + * @returns {Relays} + */ + relays(): Relays; + + /** + * @returns {PoolMetadata | void} + */ + pool_metadata(): PoolMetadata | void; + + /** + * @param {Ed25519KeyHash} operator + * @param {VRFKeyHash} vrf_keyhash + * @param {BigNum} pledge + * @param {BigNum} cost + * @param {UnitInterval} margin + * @param {RewardAddress} reward_account + * @param {Ed25519KeyHashes} pool_owners + * @param {Relays} relays + * @param {PoolMetadata | void} [pool_metadata] + * @returns {PoolParams} + */ + static new( + operator: Ed25519KeyHash, + vrf_keyhash: VRFKeyHash, + pledge: BigNum, + cost: BigNum, + margin: UnitInterval, + reward_account: RewardAddress, + pool_owners: Ed25519KeyHashes, + relays: Relays, + pool_metadata?: PoolMetadata + ): PoolParams; } /** */ -declare export class SingleHostAddr { +declare export class PoolRegistration { free(): void; /** @@ -7236,9 +7877,9 @@ declare export class SingleHostAddr { /** * @param {Uint8Array} bytes - * @returns {SingleHostAddr} + * @returns {PoolRegistration} */ - static from_bytes(bytes: Uint8Array): SingleHostAddr; + static from_bytes(bytes: Uint8Array): PoolRegistration; /** * @returns {string} @@ -7247,9 +7888,9 @@ declare export class SingleHostAddr { /** * @param {string} hex_str - * @returns {SingleHostAddr} + * @returns {PoolRegistration} */ - static from_hex(hex_str: string): SingleHostAddr; + static from_hex(hex_str: string): PoolRegistration; /** * @returns {string} @@ -7257,42 +7898,30 @@ declare export class SingleHostAddr { to_json(): string; /** - * @returns {SingleHostAddrJSON} + * @returns {PoolRegistrationJSON} */ - to_js_value(): SingleHostAddrJSON; + to_js_value(): PoolRegistrationJSON; /** * @param {string} json - * @returns {SingleHostAddr} - */ - static from_json(json: string): SingleHostAddr; - - /** - * @returns {number | void} - */ - port(): number | void; - - /** - * @returns {Ipv4 | void} + * @returns {PoolRegistration} */ - ipv4(): Ipv4 | void; + static from_json(json: string): PoolRegistration; /** - * @returns {Ipv6 | void} + * @returns {PoolParams} */ - ipv6(): Ipv6 | void; + pool_params(): PoolParams; /** - * @param {number | void} port - * @param {Ipv4 | void} ipv4 - * @param {Ipv6 | void} ipv6 - * @returns {SingleHostAddr} + * @param {PoolParams} pool_params + * @returns {PoolRegistration} */ - static new(port?: number, ipv4?: Ipv4, ipv6?: Ipv6): SingleHostAddr; + static new(pool_params: PoolParams): PoolRegistration; } /** */ -declare export class SingleHostName { +declare export class PoolRetirement { free(): void; /** @@ -7302,9 +7931,9 @@ declare export class SingleHostName { /** * @param {Uint8Array} bytes - * @returns {SingleHostName} + * @returns {PoolRetirement} */ - static from_bytes(bytes: Uint8Array): SingleHostName; + static from_bytes(bytes: Uint8Array): PoolRetirement; /** * @returns {string} @@ -7313,9 +7942,9 @@ declare export class SingleHostName { /** * @param {string} hex_str - * @returns {SingleHostName} + * @returns {PoolRetirement} */ - static from_hex(hex_str: string): SingleHostName; + static from_hex(hex_str: string): PoolRetirement; /** * @returns {string} @@ -7323,175 +7952,193 @@ declare export class SingleHostName { to_json(): string; /** - * @returns {SingleHostNameJSON} + * @returns {PoolRetirementJSON} */ - to_js_value(): SingleHostNameJSON; + to_js_value(): PoolRetirementJSON; /** * @param {string} json - * @returns {SingleHostName} + * @returns {PoolRetirement} */ - static from_json(json: string): SingleHostName; + static from_json(json: string): PoolRetirement; /** - * @returns {number | void} + * @returns {Ed25519KeyHash} */ - port(): number | void; + pool_keyhash(): Ed25519KeyHash; /** - * @returns {DNSRecordAorAAAA} + * @returns {number} */ - dns_name(): DNSRecordAorAAAA; + epoch(): number; /** - * @param {number | void} port - * @param {DNSRecordAorAAAA} dns_name - * @returns {SingleHostName} + * @param {Ed25519KeyHash} pool_keyhash + * @param {number} epoch + * @returns {PoolRetirement} */ - static new(port: number | void, dns_name: DNSRecordAorAAAA): SingleHostName; + static new(pool_keyhash: Ed25519KeyHash, epoch: number): PoolRetirement; } /** */ -declare export class StakeCredential { +declare export class PoolVotingThresholds { free(): void; /** - * @param {Ed25519KeyHash} hash - * @returns {StakeCredential} + * @returns {Uint8Array} */ - static from_keyhash(hash: Ed25519KeyHash): StakeCredential; + to_bytes(): Uint8Array; /** - * @param {ScriptHash} hash - * @returns {StakeCredential} + * @param {Uint8Array} bytes + * @returns {PoolVotingThresholds} */ - static from_scripthash(hash: ScriptHash): StakeCredential; + static from_bytes(bytes: Uint8Array): PoolVotingThresholds; /** - * @returns {Ed25519KeyHash | void} + * @returns {string} */ - to_keyhash(): Ed25519KeyHash | void; + to_hex(): string; /** - * @returns {ScriptHash | void} + * @param {string} hex_str + * @returns {PoolVotingThresholds} */ - to_scripthash(): ScriptHash | void; + static from_hex(hex_str: string): PoolVotingThresholds; /** - * @returns {number} + * @returns {string} */ - kind(): number; + to_json(): string; /** - * @returns {boolean} + * @returns {PoolVotingThresholdsJSON} */ - has_script_hash(): boolean; + to_js_value(): PoolVotingThresholdsJSON; /** - * @returns {Uint8Array} + * @param {string} json + * @returns {PoolVotingThresholds} */ - to_bytes(): Uint8Array; + static from_json(json: string): PoolVotingThresholds; /** - * @param {Uint8Array} bytes - * @returns {StakeCredential} + * @param {UnitInterval} motion_no_confidence + * @param {UnitInterval} committee_normal + * @param {UnitInterval} committee_no_confidence + * @param {UnitInterval} hard_fork_initiation + * @param {UnitInterval} security_relevant_threshold + * @returns {PoolVotingThresholds} */ - static from_bytes(bytes: Uint8Array): StakeCredential; + static new( + motion_no_confidence: UnitInterval, + committee_normal: UnitInterval, + committee_no_confidence: UnitInterval, + hard_fork_initiation: UnitInterval, + security_relevant_threshold: UnitInterval + ): PoolVotingThresholds; /** - * @returns {string} + * @returns {UnitInterval} */ - to_hex(): string; + motion_no_confidence(): UnitInterval; /** - * @param {string} hex_str - * @returns {StakeCredential} + * @returns {UnitInterval} */ - static from_hex(hex_str: string): StakeCredential; + committee_normal(): UnitInterval; /** - * @returns {string} + * @returns {UnitInterval} */ - to_json(): string; + committee_no_confidence(): UnitInterval; /** - * @returns {StakeCredentialJSON} + * @returns {UnitInterval} */ - to_js_value(): StakeCredentialJSON; + hard_fork_initiation(): UnitInterval; /** - * @param {string} json - * @returns {StakeCredential} + * @returns {UnitInterval} */ - static from_json(json: string): StakeCredential; + security_relevant_threshold(): UnitInterval; } /** */ -declare export class StakeCredentials { +declare export class PrivateKey { free(): void; /** - * @returns {Uint8Array} + * @param {string} hex_str + * @returns {PrivateKey} */ - to_bytes(): Uint8Array; + static from_hex(hex_str: string): PrivateKey; /** - * @param {Uint8Array} bytes - * @returns {StakeCredentials} + * @returns {string} */ - static from_bytes(bytes: Uint8Array): StakeCredentials; + to_hex(): string; /** - * @returns {string} + * @param {Uint8Array} message + * @returns {Ed25519Signature} */ - to_hex(): string; + sign(message: Uint8Array): Ed25519Signature; /** - * @param {string} hex_str - * @returns {StakeCredentials} + * @param {Uint8Array} bytes + * @returns {PrivateKey} */ - static from_hex(hex_str: string): StakeCredentials; + static from_normal_bytes(bytes: Uint8Array): PrivateKey; /** - * @returns {string} + * @param {Uint8Array} bytes + * @returns {PrivateKey} */ - to_json(): string; + static from_extended_bytes(bytes: Uint8Array): PrivateKey; /** - * @returns {StakeCredentialsJSON} + * @returns {Uint8Array} */ - to_js_value(): StakeCredentialsJSON; + as_bytes(): Uint8Array; /** - * @param {string} json - * @returns {StakeCredentials} + * @returns {string} */ - static from_json(json: string): StakeCredentials; + to_bech32(): string; /** - * @returns {StakeCredentials} + * Get private key from its bech32 representation + * ```javascript + * PrivateKey.from_bech32('ed25519_sk1ahfetf02qwwg4dkq7mgp4a25lx5vh9920cr5wnxmpzz9906qvm8qwvlts0'); + * ``` + * For an extended 25519 key + * ```javascript + * PrivateKey.from_bech32('ed25519e_sk1gqwl4szuwwh6d0yk3nsqcc6xxc3fpvjlevgwvt60df59v8zd8f8prazt8ln3lmz096ux3xvhhvm3ca9wj2yctdh3pnw0szrma07rt5gl748fp'); + * ``` + * @param {string} bech32_str + * @returns {PrivateKey} */ - static new(): StakeCredentials; + static from_bech32(bech32_str: string): PrivateKey; /** - * @returns {number} + * @returns {PrivateKey} */ - len(): number; + static generate_ed25519extended(): PrivateKey; /** - * @param {number} index - * @returns {StakeCredential} + * @returns {PrivateKey} */ - get(index: number): StakeCredential; + static generate_ed25519(): PrivateKey; /** - * @param {StakeCredential} elem + * @returns {PublicKey} */ - add(elem: StakeCredential): void; + to_public(): PublicKey; } /** */ -declare export class StakeDelegation { +declare export class ProposedProtocolParameterUpdates { free(): void; /** @@ -7501,9 +8148,9 @@ declare export class StakeDelegation { /** * @param {Uint8Array} bytes - * @returns {StakeDelegation} + * @returns {ProposedProtocolParameterUpdates} */ - static from_bytes(bytes: Uint8Array): StakeDelegation; + static from_bytes(bytes: Uint8Array): ProposedProtocolParameterUpdates; /** * @returns {string} @@ -7512,9 +8159,9 @@ declare export class StakeDelegation { /** * @param {string} hex_str - * @returns {StakeDelegation} + * @returns {ProposedProtocolParameterUpdates} */ - static from_hex(hex_str: string): StakeDelegation; + static from_hex(hex_str: string): ProposedProtocolParameterUpdates; /** * @returns {string} @@ -7522,44 +8169,50 @@ declare export class StakeDelegation { to_json(): string; /** - * @returns {StakeDelegationJSON} + * @returns {ProposedProtocolParameterUpdatesJSON} */ - to_js_value(): StakeDelegationJSON; + to_js_value(): ProposedProtocolParameterUpdatesJSON; /** * @param {string} json - * @returns {StakeDelegation} + * @returns {ProposedProtocolParameterUpdates} */ - static from_json(json: string): StakeDelegation; + static from_json(json: string): ProposedProtocolParameterUpdates; /** - * @returns {StakeCredential} + * @returns {ProposedProtocolParameterUpdates} */ - stake_credential(): StakeCredential; + static new(): ProposedProtocolParameterUpdates; /** - * @returns {Ed25519KeyHash} + * @returns {number} */ - pool_keyhash(): Ed25519KeyHash; + len(): number; /** - * @param {StakeCredential} stake_credential - * @param {Ed25519KeyHash} pool_keyhash - * @returns {StakeDelegation} + * @param {GenesisHash} key + * @param {ProtocolParamUpdate} value + * @returns {ProtocolParamUpdate | void} */ - static new( - stake_credential: StakeCredential, - pool_keyhash: Ed25519KeyHash - ): StakeDelegation; + insert( + key: GenesisHash, + value: ProtocolParamUpdate + ): ProtocolParamUpdate | void; /** - * @returns {boolean} + * @param {GenesisHash} key + * @returns {ProtocolParamUpdate | void} */ - has_script_credentials(): boolean; -} -/** - */ -declare export class StakeDeregistration { + get(key: GenesisHash): ProtocolParamUpdate | void; + + /** + * @returns {GenesisHashes} + */ + keys(): GenesisHashes; +} +/** + */ +declare export class ProtocolParamUpdate { free(): void; /** @@ -7569,9 +8222,9 @@ declare export class StakeDeregistration { /** * @param {Uint8Array} bytes - * @returns {StakeDeregistration} + * @returns {ProtocolParamUpdate} */ - static from_bytes(bytes: Uint8Array): StakeDeregistration; + static from_bytes(bytes: Uint8Array): ProtocolParamUpdate; /** * @returns {string} @@ -7580,9 +8233,9 @@ declare export class StakeDeregistration { /** * @param {string} hex_str - * @returns {StakeDeregistration} + * @returns {ProtocolParamUpdate} */ - static from_hex(hex_str: string): StakeDeregistration; + static from_hex(hex_str: string): ProtocolParamUpdate; /** * @returns {string} @@ -7590,431 +8243,356 @@ declare export class StakeDeregistration { to_json(): string; /** - * @returns {StakeDeregistrationJSON} + * @returns {ProtocolParamUpdateJSON} */ - to_js_value(): StakeDeregistrationJSON; + to_js_value(): ProtocolParamUpdateJSON; /** * @param {string} json - * @returns {StakeDeregistration} + * @returns {ProtocolParamUpdate} */ - static from_json(json: string): StakeDeregistration; + static from_json(json: string): ProtocolParamUpdate; /** - * @returns {StakeCredential} + * @param {BigNum} minfee_a */ - stake_credential(): StakeCredential; + set_minfee_a(minfee_a: BigNum): void; /** - * @param {StakeCredential} stake_credential - * @returns {StakeDeregistration} + * @returns {BigNum | void} */ - static new(stake_credential: StakeCredential): StakeDeregistration; + minfee_a(): BigNum | void; /** - * @returns {boolean} + * @param {BigNum} minfee_b */ - has_script_credentials(): boolean; -} -/** - */ -declare export class StakeRegistration { - free(): void; + set_minfee_b(minfee_b: BigNum): void; /** - * @returns {Uint8Array} + * @returns {BigNum | void} */ - to_bytes(): Uint8Array; + minfee_b(): BigNum | void; /** - * @param {Uint8Array} bytes - * @returns {StakeRegistration} + * @param {number} max_block_body_size */ - static from_bytes(bytes: Uint8Array): StakeRegistration; + set_max_block_body_size(max_block_body_size: number): void; /** - * @returns {string} + * @returns {number | void} */ - to_hex(): string; + max_block_body_size(): number | void; /** - * @param {string} hex_str - * @returns {StakeRegistration} + * @param {number} max_tx_size */ - static from_hex(hex_str: string): StakeRegistration; + set_max_tx_size(max_tx_size: number): void; /** - * @returns {string} + * @returns {number | void} */ - to_json(): string; + max_tx_size(): number | void; /** - * @returns {StakeRegistrationJSON} + * @param {number} max_block_header_size */ - to_js_value(): StakeRegistrationJSON; + set_max_block_header_size(max_block_header_size: number): void; /** - * @param {string} json - * @returns {StakeRegistration} + * @returns {number | void} */ - static from_json(json: string): StakeRegistration; + max_block_header_size(): number | void; /** - * @returns {StakeCredential} + * @param {BigNum} key_deposit */ - stake_credential(): StakeCredential; + set_key_deposit(key_deposit: BigNum): void; /** - * @param {StakeCredential} stake_credential - * @returns {StakeRegistration} + * @returns {BigNum | void} */ - static new(stake_credential: StakeCredential): StakeRegistration; -} -/** - */ -declare export class Strings { - free(): void; + key_deposit(): BigNum | void; /** - * @returns {Strings} + * @param {BigNum} pool_deposit */ - static new(): Strings; + set_pool_deposit(pool_deposit: BigNum): void; /** - * @returns {number} + * @returns {BigNum | void} */ - len(): number; + pool_deposit(): BigNum | void; /** - * @param {number} index - * @returns {string} + * @param {number} max_epoch */ - get(index: number): string; + set_max_epoch(max_epoch: number): void; /** - * @param {string} elem + * @returns {number | void} */ - add(elem: string): void; -} -/** - */ -declare export class TimelockExpiry { - free(): void; + max_epoch(): number | void; /** - * @returns {Uint8Array} + * @param {number} n_opt */ - to_bytes(): Uint8Array; + set_n_opt(n_opt: number): void; /** - * @param {Uint8Array} bytes - * @returns {TimelockExpiry} + * @returns {number | void} */ - static from_bytes(bytes: Uint8Array): TimelockExpiry; + n_opt(): number | void; /** - * @returns {string} + * @param {UnitInterval} pool_pledge_influence */ - to_hex(): string; + set_pool_pledge_influence(pool_pledge_influence: UnitInterval): void; /** - * @param {string} hex_str - * @returns {TimelockExpiry} + * @returns {UnitInterval | void} */ - static from_hex(hex_str: string): TimelockExpiry; + pool_pledge_influence(): UnitInterval | void; /** - * @returns {string} + * @param {UnitInterval} expansion_rate */ - to_json(): string; + set_expansion_rate(expansion_rate: UnitInterval): void; /** - * @returns {TimelockExpiryJSON} + * @returns {UnitInterval | void} */ - to_js_value(): TimelockExpiryJSON; + expansion_rate(): UnitInterval | void; /** - * @param {string} json - * @returns {TimelockExpiry} + * @param {UnitInterval} treasury_growth_rate */ - static from_json(json: string): TimelockExpiry; + set_treasury_growth_rate(treasury_growth_rate: UnitInterval): void; /** - * @returns {number} + * @returns {UnitInterval | void} */ - slot(): number; + treasury_growth_rate(): UnitInterval | void; /** - * @returns {BigNum} + * !!! DEPRECATED !!! + * Since babbage era this param is outdated. But this param you can meet in a pre-babbage block. + * @returns {UnitInterval | void} */ - slot_bignum(): BigNum; + d(): UnitInterval | void; /** * !!! DEPRECATED !!! - * This constructor uses outdated slot number format. - * Use `.new_timelockexpiry` instead - * @param {number} slot - * @returns {TimelockExpiry} + * Since babbage era this param is outdated. But this param you can meet in a pre-babbage block. + * @returns {Nonce | void} */ - static new(slot: number): TimelockExpiry; + extra_entropy(): Nonce | void; /** - * @param {BigNum} slot - * @returns {TimelockExpiry} + * !!! DEPRECATED !!! + * Since conway era this param is outdated. But this param you can meet in a pre-conway block. + * @param {ProtocolVersion} protocol_version */ - static new_timelockexpiry(slot: BigNum): TimelockExpiry; -} -/** - */ -declare export class TimelockStart { - free(): void; + set_protocol_version(protocol_version: ProtocolVersion): void; /** - * @returns {Uint8Array} + * @returns {ProtocolVersion | void} */ - to_bytes(): Uint8Array; + protocol_version(): ProtocolVersion | void; /** - * @param {Uint8Array} bytes - * @returns {TimelockStart} + * @param {BigNum} min_pool_cost */ - static from_bytes(bytes: Uint8Array): TimelockStart; + set_min_pool_cost(min_pool_cost: BigNum): void; /** - * @returns {string} + * @returns {BigNum | void} */ - to_hex(): string; + min_pool_cost(): BigNum | void; /** - * @param {string} hex_str - * @returns {TimelockStart} + * @param {BigNum} ada_per_utxo_byte */ - static from_hex(hex_str: string): TimelockStart; + set_ada_per_utxo_byte(ada_per_utxo_byte: BigNum): void; /** - * @returns {string} + * @returns {BigNum | void} */ - to_json(): string; + ada_per_utxo_byte(): BigNum | void; /** - * @returns {TimelockStartJSON} + * @param {Costmdls} cost_models */ - to_js_value(): TimelockStartJSON; + set_cost_models(cost_models: Costmdls): void; /** - * @param {string} json - * @returns {TimelockStart} + * @returns {Costmdls | void} */ - static from_json(json: string): TimelockStart; + cost_models(): Costmdls | void; /** - * !!! DEPRECATED !!! - * Returns a Slot32 (u32) value in case the underlying original BigNum (u64) value is within the limits. - * Otherwise will just raise an error. - * Use `.slot_bignum` instead - * @returns {number} + * @param {ExUnitPrices} execution_costs */ - slot(): number; + set_execution_costs(execution_costs: ExUnitPrices): void; /** - * @returns {BigNum} + * @returns {ExUnitPrices | void} */ - slot_bignum(): BigNum; + execution_costs(): ExUnitPrices | void; /** - * !!! DEPRECATED !!! - * This constructor uses outdated slot number format. - * Use `.new_timelockstart` instead. - * @param {number} slot - * @returns {TimelockStart} + * @param {ExUnits} max_tx_ex_units */ - static new(slot: number): TimelockStart; + set_max_tx_ex_units(max_tx_ex_units: ExUnits): void; /** - * @param {BigNum} slot - * @returns {TimelockStart} + * @returns {ExUnits | void} */ - static new_timelockstart(slot: BigNum): TimelockStart; -} -/** - */ -declare export class Transaction { - free(): void; + max_tx_ex_units(): ExUnits | void; /** - * @returns {Uint8Array} + * @param {ExUnits} max_block_ex_units */ - to_bytes(): Uint8Array; + set_max_block_ex_units(max_block_ex_units: ExUnits): void; /** - * @param {Uint8Array} bytes - * @returns {Transaction} + * @returns {ExUnits | void} */ - static from_bytes(bytes: Uint8Array): Transaction; + max_block_ex_units(): ExUnits | void; /** - * @returns {string} + * @param {number} max_value_size */ - to_hex(): string; + set_max_value_size(max_value_size: number): void; /** - * @param {string} hex_str - * @returns {Transaction} + * @returns {number | void} */ - static from_hex(hex_str: string): Transaction; + max_value_size(): number | void; /** - * @returns {string} + * @param {number} collateral_percentage */ - to_json(): string; + set_collateral_percentage(collateral_percentage: number): void; /** - * @returns {TransactionJSON} + * @returns {number | void} */ - to_js_value(): TransactionJSON; + collateral_percentage(): number | void; /** - * @param {string} json - * @returns {Transaction} + * @param {number} max_collateral_inputs */ - static from_json(json: string): Transaction; + set_max_collateral_inputs(max_collateral_inputs: number): void; /** - * @returns {TransactionBody} - */ - body(): TransactionBody; - - /** - * @returns {TransactionWitnessSet} + * @returns {number | void} */ - witness_set(): TransactionWitnessSet; + max_collateral_inputs(): number | void; /** - * @returns {boolean} + * @param {PoolVotingThresholds} pool_voting_thresholds */ - is_valid(): boolean; + set_pool_voting_thresholds( + pool_voting_thresholds: PoolVotingThresholds + ): void; /** - * @returns {AuxiliaryData | void} + * @returns {PoolVotingThresholds | void} */ - auxiliary_data(): AuxiliaryData | void; + pool_voting_thresholds(): PoolVotingThresholds | void; /** - * @param {boolean} valid + * @param {DRepVotingThresholds} drep_voting_thresholds */ - set_is_valid(valid: boolean): void; + set_drep_voting_thresholds( + drep_voting_thresholds: DRepVotingThresholds + ): void; /** - * @param {TransactionBody} body - * @param {TransactionWitnessSet} witness_set - * @param {AuxiliaryData | void} auxiliary_data - * @returns {Transaction} + * @returns {DRepVotingThresholds | void} */ - static new( - body: TransactionBody, - witness_set: TransactionWitnessSet, - auxiliary_data?: AuxiliaryData - ): Transaction; -} -/** - */ -declare export class TransactionBatch { - free(): void; + drep_voting_thresholds(): DRepVotingThresholds | void; /** - * @returns {number} + * @param {number} min_committee_size */ - len(): number; + set_min_committee_size(min_committee_size: number): void; /** - * @param {number} index - * @returns {Transaction} + * @returns {number | void} */ - get(index: number): Transaction; -} -/** - */ -declare export class TransactionBatchList { - free(): void; + min_committee_size(): number | void; /** - * @returns {number} + * @param {number} committee_term_limit */ - len(): number; + set_committee_term_limit(committee_term_limit: number): void; /** - * @param {number} index - * @returns {TransactionBatch} + * @returns {number | void} */ - get(index: number): TransactionBatch; -} -/** - */ -declare export class TransactionBodies { - free(): void; + committee_term_limit(): number | void; /** - * @returns {Uint8Array} + * @param {number} governance_action_validity_period */ - to_bytes(): Uint8Array; + set_governance_action_validity_period( + governance_action_validity_period: number + ): void; /** - * @param {Uint8Array} bytes - * @returns {TransactionBodies} + * @returns {number | void} */ - static from_bytes(bytes: Uint8Array): TransactionBodies; + governance_action_validity_period(): number | void; /** - * @returns {string} + * @param {BigNum} governance_action_deposit */ - to_hex(): string; + set_governance_action_deposit(governance_action_deposit: BigNum): void; /** - * @param {string} hex_str - * @returns {TransactionBodies} + * @returns {BigNum | void} */ - static from_hex(hex_str: string): TransactionBodies; + governance_action_deposit(): BigNum | void; /** - * @returns {string} + * @param {BigNum} drep_deposit */ - to_json(): string; + set_drep_deposit(drep_deposit: BigNum): void; /** - * @returns {TransactionBodiesJSON} + * @returns {BigNum | void} */ - to_js_value(): TransactionBodiesJSON; + drep_deposit(): BigNum | void; /** - * @param {string} json - * @returns {TransactionBodies} + * @param {number} drep_inactivity_period */ - static from_json(json: string): TransactionBodies; + set_drep_inactivity_period(drep_inactivity_period: number): void; /** - * @returns {TransactionBodies} + * @returns {number | void} */ - static new(): TransactionBodies; + drep_inactivity_period(): number | void; /** - * @returns {number} + * @param {UnitInterval} ref_script_coins_per_byte */ - len(): number; + set_ref_script_coins_per_byte(ref_script_coins_per_byte: UnitInterval): void; /** - * @param {number} index - * @returns {TransactionBody} + * @returns {UnitInterval | void} */ - get(index: number): TransactionBody; + ref_script_coins_per_byte(): UnitInterval | void; /** - * @param {TransactionBody} elem + * @returns {ProtocolParamUpdate} */ - add(elem: TransactionBody): void; + static new(): ProtocolParamUpdate; } /** */ -declare export class TransactionBody { +declare export class ProtocolVersion { free(): void; /** @@ -8024,9 +8602,9 @@ declare export class TransactionBody { /** * @param {Uint8Array} bytes - * @returns {TransactionBody} + * @returns {ProtocolVersion} */ - static from_bytes(bytes: Uint8Array): TransactionBody; + static from_bytes(bytes: Uint8Array): ProtocolVersion; /** * @returns {string} @@ -8035,9 +8613,9 @@ declare export class TransactionBody { /** * @param {string} hex_str - * @returns {TransactionBody} + * @returns {ProtocolVersion} */ - static from_hex(hex_str: string): TransactionBody; + static from_hex(hex_str: string): ProtocolVersion; /** * @returns {string} @@ -8045,887 +8623,741 @@ declare export class TransactionBody { to_json(): string; /** - * @returns {TransactionBodyJSON} + * @returns {ProtocolVersionJSON} */ - to_js_value(): TransactionBodyJSON; + to_js_value(): ProtocolVersionJSON; /** * @param {string} json - * @returns {TransactionBody} + * @returns {ProtocolVersion} */ - static from_json(json: string): TransactionBody; + static from_json(json: string): ProtocolVersion; /** - * @returns {TransactionInputs} + * @returns {number} */ - inputs(): TransactionInputs; + major(): number; /** - * @returns {TransactionOutputs} + * @returns {number} */ - outputs(): TransactionOutputs; + minor(): number; /** - * @returns {BigNum} + * @param {number} major + * @param {number} minor + * @returns {ProtocolVersion} */ - fee(): BigNum; + static new(major: number, minor: number): ProtocolVersion; +} +/** + * ED25519 key used as public key + */ +declare export class PublicKey { + free(): void; /** - * !!! DEPRECATED !!! - * Returns a Slot32 (u32) value in case the underlying original BigNum (u64) value is within the limits. - * Otherwise will just raise an error. - * @returns {number | void} + * @param {string} hex_str + * @returns {PublicKey} */ - ttl(): number | void; + static from_hex(hex_str: string): PublicKey; /** - * @returns {BigNum | void} + * @returns {string} */ - ttl_bignum(): BigNum | void; + to_hex(): string; /** - * @param {BigNum} ttl + * @returns {Ed25519KeyHash} */ - set_ttl(ttl: BigNum): void; + hash(): Ed25519KeyHash; /** + * @param {Uint8Array} data + * @param {Ed25519Signature} signature + * @returns {boolean} */ - remove_ttl(): void; + verify(data: Uint8Array, signature: Ed25519Signature): boolean; /** - * @param {Certificates} certs + * @param {Uint8Array} bytes + * @returns {PublicKey} */ - set_certs(certs: Certificates): void; + static from_bytes(bytes: Uint8Array): PublicKey; /** - * @returns {Certificates | void} + * @returns {Uint8Array} */ - certs(): Certificates | void; + as_bytes(): Uint8Array; /** - * @param {Withdrawals} withdrawals + * @returns {string} */ - set_withdrawals(withdrawals: Withdrawals): void; + to_bech32(): string; /** - * @returns {Withdrawals | void} + * Get public key from its bech32 representation + * Example: + * ```javascript + * const pkey = PublicKey.from_bech32('ed25519_pk1dgaagyh470y66p899txcl3r0jaeaxu6yd7z2dxyk55qcycdml8gszkxze2'); + * ``` + * @param {string} bech32_str + * @returns {PublicKey} */ - withdrawals(): Withdrawals | void; + static from_bech32(bech32_str: string): PublicKey; +} +/** + */ +declare export class PublicKeys { + free(): void; /** - * @param {Update} update */ - set_update(update: Update): void; + constructor(): this; /** - * @returns {Update | void} + * @returns {number} */ - update(): Update | void; + size(): number; /** - * @param {AuxiliaryDataHash} auxiliary_data_hash + * @param {number} index + * @returns {PublicKey} */ - set_auxiliary_data_hash(auxiliary_data_hash: AuxiliaryDataHash): void; + get(index: number): PublicKey; /** - * @returns {AuxiliaryDataHash | void} + * @param {PublicKey} key */ - auxiliary_data_hash(): AuxiliaryDataHash | void; + add(key: PublicKey): void; +} +/** + */ +declare export class Redeemer { + free(): void; /** - * !!! DEPRECATED !!! - * Uses outdated slot number format. - * @param {number} validity_start_interval + * @returns {Uint8Array} */ - set_validity_start_interval(validity_start_interval: number): void; + to_bytes(): Uint8Array; /** - * @param {BigNum} validity_start_interval + * @param {Uint8Array} bytes + * @returns {Redeemer} */ - set_validity_start_interval_bignum(validity_start_interval: BigNum): void; + static from_bytes(bytes: Uint8Array): Redeemer; /** - * @returns {BigNum | void} + * @returns {string} */ - validity_start_interval_bignum(): BigNum | void; + to_hex(): string; /** - * !!! DEPRECATED !!! - * Returns a Option (u32) value in case the underlying original Option (u64) value is within the limits. - * Otherwise will just raise an error. - * Use `.validity_start_interval_bignum` instead. - * @returns {number | void} + * @param {string} hex_str + * @returns {Redeemer} */ - validity_start_interval(): number | void; + static from_hex(hex_str: string): Redeemer; /** - * @param {Mint} mint + * @returns {string} */ - set_mint(mint: Mint): void; + to_json(): string; /** - * @returns {Mint | void} + * @returns {RedeemerJSON} */ - mint(): Mint | void; + to_js_value(): RedeemerJSON; /** - * This function returns the mint value of the transaction - * Use `.mint()` instead. - * @returns {Mint | void} + * @param {string} json + * @returns {Redeemer} */ - multiassets(): Mint | void; + static from_json(json: string): Redeemer; /** - * @param {TransactionInputs} reference_inputs + * @returns {RedeemerTag} */ - set_reference_inputs(reference_inputs: TransactionInputs): void; + tag(): RedeemerTag; /** - * @returns {TransactionInputs | void} + * @returns {BigNum} */ - reference_inputs(): TransactionInputs | void; + index(): BigNum; /** - * @param {ScriptDataHash} script_data_hash + * @returns {PlutusData} */ - set_script_data_hash(script_data_hash: ScriptDataHash): void; + data(): PlutusData; /** - * @returns {ScriptDataHash | void} + * @returns {ExUnits} */ - script_data_hash(): ScriptDataHash | void; + ex_units(): ExUnits; /** - * @param {TransactionInputs} collateral + * @param {RedeemerTag} tag + * @param {BigNum} index + * @param {PlutusData} data + * @param {ExUnits} ex_units + * @returns {Redeemer} */ - set_collateral(collateral: TransactionInputs): void; - - /** - * @returns {TransactionInputs | void} - */ - collateral(): TransactionInputs | void; + static new( + tag: RedeemerTag, + index: BigNum, + data: PlutusData, + ex_units: ExUnits + ): Redeemer; +} +/** + */ +declare export class RedeemerTag { + free(): void; /** - * @param {Ed25519KeyHashes} required_signers + * @returns {Uint8Array} */ - set_required_signers(required_signers: Ed25519KeyHashes): void; + to_bytes(): Uint8Array; /** - * @returns {Ed25519KeyHashes | void} + * @param {Uint8Array} bytes + * @returns {RedeemerTag} */ - required_signers(): Ed25519KeyHashes | void; + static from_bytes(bytes: Uint8Array): RedeemerTag; /** - * @param {NetworkId} network_id + * @returns {string} */ - set_network_id(network_id: NetworkId): void; + to_hex(): string; /** - * @returns {NetworkId | void} + * @param {string} hex_str + * @returns {RedeemerTag} */ - network_id(): NetworkId | void; + static from_hex(hex_str: string): RedeemerTag; /** - * @param {TransactionOutput} collateral_return + * @returns {string} */ - set_collateral_return(collateral_return: TransactionOutput): void; + to_json(): string; /** - * @returns {TransactionOutput | void} + * @returns {RedeemerTagJSON} */ - collateral_return(): TransactionOutput | void; + to_js_value(): RedeemerTagJSON; /** - * @param {BigNum} total_collateral + * @param {string} json + * @returns {RedeemerTag} */ - set_total_collateral(total_collateral: BigNum): void; + static from_json(json: string): RedeemerTag; /** - * @returns {BigNum | void} + * @returns {RedeemerTag} */ - total_collateral(): BigNum | void; + static new_spend(): RedeemerTag; /** - * !!! DEPRECATED !!! - * This constructor uses outdated slot number format for the ttl value. - * Use `.new_tx_body` and then `.set_ttl` instead - * @param {TransactionInputs} inputs - * @param {TransactionOutputs} outputs - * @param {BigNum} fee - * @param {number | void} ttl - * @returns {TransactionBody} + * @returns {RedeemerTag} */ - static new( - inputs: TransactionInputs, - outputs: TransactionOutputs, - fee: BigNum, - ttl?: number - ): TransactionBody; + static new_mint(): RedeemerTag; /** - * Returns a new TransactionBody. - * In the new version of "new" we removed optional ttl for support it by wasm_bingen. - * Your can use "set_ttl" and "remove_ttl" to set a new value for ttl or set it as None. - * @param {TransactionInputs} inputs - * @param {TransactionOutputs} outputs - * @param {BigNum} fee - * @returns {TransactionBody} + * @returns {RedeemerTag} */ - static new_tx_body( - inputs: TransactionInputs, - outputs: TransactionOutputs, - fee: BigNum - ): TransactionBody; -} -/** - */ -declare export class TransactionBuilder { - free(): void; + static new_cert(): RedeemerTag; /** - * This automatically selects and adds inputs from {inputs} consisting of just enough to cover - * the outputs that have already been added. - * This should be called after adding all certs/outputs/etc and will be an error otherwise. - * Uses CIP2: https://github.com/cardano-foundation/CIPs/blob/master/CIP-0002/CIP-0002.md - * Adding a change output must be called after via TransactionBuilder::add_change_if_needed() - * This function, diverging from CIP2, takes into account fees and will attempt to add additional - * inputs to cover the minimum fees. This does not, however, set the txbuilder's fee. - * @param {TransactionUnspentOutputs} inputs - * @param {number} strategy + * @returns {RedeemerTag} */ - add_inputs_from(inputs: TransactionUnspentOutputs, strategy: number): void; + static new_reward(): RedeemerTag; /** - * @param {TxInputsBuilder} inputs + * @returns {RedeemerTag} */ - set_inputs(inputs: TxInputsBuilder): void; + static new_vote(): RedeemerTag; /** - * @param {TxInputsBuilder} collateral + * @returns {RedeemerTag} */ - set_collateral(collateral: TxInputsBuilder): void; + static new_voting_proposal(): RedeemerTag; /** - * @param {TransactionOutput} collateral_return - */ - set_collateral_return(collateral_return: TransactionOutput): void; + * @returns {$Values< + typeof + RedeemerTagKind>} + */ + kind(): $Values; +} +/** + */ +declare export class Redeemers { + free(): void; /** - * This function will set the collateral-return value and then auto-calculate and assign - * the total collateral coin value. Will raise an error in case no collateral inputs are set - * or in case the total collateral value will have any assets in it except coin. - * @param {TransactionOutput} collateral_return + * @returns {Uint8Array} */ - set_collateral_return_and_total(collateral_return: TransactionOutput): void; + to_bytes(): Uint8Array; /** - * @param {BigNum} total_collateral + * @param {Uint8Array} bytes + * @returns {Redeemers} */ - set_total_collateral(total_collateral: BigNum): void; + static from_bytes(bytes: Uint8Array): Redeemers; /** - * This function will set the total-collateral coin and then auto-calculate and assign - * the collateral return value. Will raise an error in case no collateral inputs are set. - * The specified address will be the received of the collateral return - * @param {BigNum} total_collateral - * @param {Address} return_address + * @returns {string} */ - set_total_collateral_and_return( - total_collateral: BigNum, - return_address: Address - ): void; + to_hex(): string; /** - * @param {TransactionInput} reference_input + * @param {string} hex_str + * @returns {Redeemers} */ - add_reference_input(reference_input: TransactionInput): void; + static from_hex(hex_str: string): Redeemers; /** - * We have to know what kind of inputs these are to know what kind of mock witnesses to create since - * 1) mock witnesses have different lengths depending on the type which changes the expecting fee - * 2) Witnesses are a set so we need to get rid of duplicates to avoid over-estimating the fee - * @param {Ed25519KeyHash} hash - * @param {TransactionInput} input - * @param {Value} amount + * @returns {string} */ - add_key_input( - hash: Ed25519KeyHash, - input: TransactionInput, - amount: Value - ): void; + to_json(): string; /** - * This method adds the input to the builder BUT leaves a missing spot for the witness native script - * - * After adding the input with this method, use `.add_required_native_input_scripts` - * and `.add_required_plutus_input_scripts` to add the witness scripts - * - * Or instead use `.add_native_script_input` and `.add_plutus_script_input` - * to add inputs right along with the script, instead of the script hash - * @param {ScriptHash} hash - * @param {TransactionInput} input - * @param {Value} amount + * @returns {RedeemersJSON} */ - add_script_input( - hash: ScriptHash, - input: TransactionInput, - amount: Value - ): void; + to_js_value(): RedeemersJSON; /** - * This method will add the input to the builder and also register the required native script witness - * @param {NativeScript} script - * @param {TransactionInput} input - * @param {Value} amount + * @param {string} json + * @returns {Redeemers} */ - add_native_script_input( - script: NativeScript, - input: TransactionInput, - amount: Value - ): void; + static from_json(json: string): Redeemers; /** - * This method will add the input to the builder and also register the required plutus witness - * @param {PlutusWitness} witness - * @param {TransactionInput} input - * @param {Value} amount + * @returns {Redeemers} */ - add_plutus_script_input( - witness: PlutusWitness, - input: TransactionInput, - amount: Value - ): void; + static new(): Redeemers; /** - * @param {ByronAddress} hash - * @param {TransactionInput} input - * @param {Value} amount + * @returns {number} */ - add_bootstrap_input( - hash: ByronAddress, - input: TransactionInput, - amount: Value - ): void; + len(): number; /** - * Note that for script inputs this method will use underlying generic `.add_script_input` - * which leaves a required empty spot for the script witness (or witnesses in case of Plutus). - * You can use `.add_native_script_input` or `.add_plutus_script_input` directly to register the input along with the witness. - * @param {Address} address - * @param {TransactionInput} input - * @param {Value} amount + * @param {number} index + * @returns {Redeemer} */ - add_input(address: Address, input: TransactionInput, amount: Value): void; + get(index: number): Redeemer; /** - * Returns the number of still missing input scripts (either native or plutus) - * Use `.add_required_native_input_scripts` or `.add_required_plutus_input_scripts` to add the missing scripts - * @returns {number} + * @param {Redeemer} elem */ - count_missing_input_scripts(): number; + add(elem: Redeemer): void; /** - * Try adding the specified scripts as witnesses for ALREADY ADDED script inputs - * Any scripts that don't match any of the previously added inputs will be ignored - * Returns the number of remaining required missing witness scripts - * Use `.count_missing_input_scripts` to find the number of still missing scripts - * @param {NativeScripts} scripts - * @returns {number} + * @returns {ExUnits} */ - add_required_native_input_scripts(scripts: NativeScripts): number; + total_ex_units(): ExUnits; +} +/** + */ +declare export class Relay { + free(): void; /** - * Try adding the specified scripts as witnesses for ALREADY ADDED script inputs - * Any scripts that don't match any of the previously added inputs will be ignored - * Returns the number of remaining required missing witness scripts - * Use `.count_missing_input_scripts` to find the number of still missing scripts - * @param {PlutusWitnesses} scripts - * @returns {number} + * @returns {Uint8Array} */ - add_required_plutus_input_scripts(scripts: PlutusWitnesses): number; + to_bytes(): Uint8Array; /** - * Returns a copy of the current script input witness scripts in the builder - * @returns {NativeScripts | void} + * @param {Uint8Array} bytes + * @returns {Relay} */ - get_native_input_scripts(): NativeScripts | void; + static from_bytes(bytes: Uint8Array): Relay; /** - * Returns a copy of the current plutus input witness scripts in the builder. - * NOTE: each plutus witness will be cloned with a specific corresponding input index - * @returns {PlutusWitnesses | void} + * @returns {string} */ - get_plutus_input_scripts(): PlutusWitnesses | void; + to_hex(): string; /** - * calculates how much the fee would increase if you added a given output - * @param {Address} address - * @param {TransactionInput} input - * @param {Value} amount - * @returns {BigNum} + * @param {string} hex_str + * @returns {Relay} */ - fee_for_input( - address: Address, - input: TransactionInput, - amount: Value - ): BigNum; + static from_hex(hex_str: string): Relay; /** - * Add explicit output via a TransactionOutput object - * @param {TransactionOutput} output + * @returns {string} */ - add_output(output: TransactionOutput): void; + to_json(): string; /** - * calculates how much the fee would increase if you added a given output - * @param {TransactionOutput} output - * @returns {BigNum} + * @returns {RelayJSON} */ - fee_for_output(output: TransactionOutput): BigNum; + to_js_value(): RelayJSON; /** - * @param {BigNum} fee + * @param {string} json + * @returns {Relay} */ - set_fee(fee: BigNum): void; + static from_json(json: string): Relay; /** - * !!! DEPRECATED !!! - * Set ttl value. - * @param {number} ttl + * @param {SingleHostAddr} single_host_addr + * @returns {Relay} */ - set_ttl(ttl: number): void; + static new_single_host_addr(single_host_addr: SingleHostAddr): Relay; /** - * @param {BigNum} ttl + * @param {SingleHostName} single_host_name + * @returns {Relay} */ - set_ttl_bignum(ttl: BigNum): void; + static new_single_host_name(single_host_name: SingleHostName): Relay; /** - * !!! DEPRECATED !!! - * Uses outdated slot number format. - * @param {number} validity_start_interval + * @param {MultiHostName} multi_host_name + * @returns {Relay} */ - set_validity_start_interval(validity_start_interval: number): void; + static new_multi_host_name(multi_host_name: MultiHostName): Relay; /** - * @param {BigNum} validity_start_interval + * @returns {$Values< + typeof + RelayKind>} + */ + kind(): $Values; + + /** + * @returns {SingleHostAddr | void} */ - set_validity_start_interval_bignum(validity_start_interval: BigNum): void; + as_single_host_addr(): SingleHostAddr | void; /** - * !!! DEPRECATED !!! - * Can emit error if add a cert with script credential. - * Use set_certs_builder instead. - * @param {Certificates} certs + * @returns {SingleHostName | void} */ - set_certs(certs: Certificates): void; + as_single_host_name(): SingleHostName | void; /** - * @param {CertificatesBuilder} certs + * @returns {MultiHostName | void} */ - set_certs_builder(certs: CertificatesBuilder): void; + as_multi_host_name(): MultiHostName | void; +} +/** + */ +declare export class Relays { + free(): void; /** - * !!! DEPRECATED !!! - * Can emit error if add a withdrawal with script credential. - * Use set_withdrawals_builder instead. - * @param {Withdrawals} withdrawals + * @returns {Uint8Array} */ - set_withdrawals(withdrawals: Withdrawals): void; + to_bytes(): Uint8Array; /** - * @param {WithdrawalsBuilder} withdrawals + * @param {Uint8Array} bytes + * @returns {Relays} */ - set_withdrawals_builder(withdrawals: WithdrawalsBuilder): void; + static from_bytes(bytes: Uint8Array): Relays; /** - * @returns {AuxiliaryData | void} + * @returns {string} */ - get_auxiliary_data(): AuxiliaryData | void; + to_hex(): string; /** - * Set explicit auxiliary data via an AuxiliaryData object - * It might contain some metadata plus native or Plutus scripts - * @param {AuxiliaryData} auxiliary_data + * @param {string} hex_str + * @returns {Relays} */ - set_auxiliary_data(auxiliary_data: AuxiliaryData): void; + static from_hex(hex_str: string): Relays; /** - * Set metadata using a GeneralTransactionMetadata object - * It will be set to the existing or new auxiliary data in this builder - * @param {GeneralTransactionMetadata} metadata + * @returns {string} */ - set_metadata(metadata: GeneralTransactionMetadata): void; + to_json(): string; /** - * Add a single metadatum using TransactionMetadatumLabel and TransactionMetadatum objects - * It will be securely added to existing or new metadata in this builder - * @param {BigNum} key - * @param {TransactionMetadatum} val + * @returns {RelaysJSON} */ - add_metadatum(key: BigNum, val: TransactionMetadatum): void; + to_js_value(): RelaysJSON; /** - * Add a single JSON metadatum using a TransactionMetadatumLabel and a String - * It will be securely added to existing or new metadata in this builder - * @param {BigNum} key - * @param {string} val + * @param {string} json + * @returns {Relays} */ - add_json_metadatum(key: BigNum, val: string): void; + static from_json(json: string): Relays; /** - * Add a single JSON metadatum using a TransactionMetadatumLabel, a String, and a MetadataJsonSchema object - * It will be securely added to existing or new metadata in this builder - * @param {BigNum} key - * @param {string} val - * @param {number} schema + * @returns {Relays} */ - add_json_metadatum_with_schema( - key: BigNum, - val: string, - schema: number - ): void; + static new(): Relays; /** - * @param {MintBuilder} mint_builder + * @returns {number} */ - set_mint_builder(mint_builder: MintBuilder): void; + len(): number; /** - * @returns {MintBuilder | void} + * @param {number} index + * @returns {Relay} */ - get_mint_builder(): MintBuilder | void; + get(index: number): Relay; /** - * !!! DEPRECATED !!! - * Mints are defining by MintBuilder now. - * Use `.set_mint_builder()` and `MintBuilder` instead. - * Set explicit Mint object and the required witnesses to this builder - * it will replace any previously existing mint and mint scripts - * NOTE! Error will be returned in case a mint policy does not have a matching script - * @param {Mint} mint - * @param {NativeScripts} mint_scripts + * @param {Relay} elem */ - set_mint(mint: Mint, mint_scripts: NativeScripts): void; + add(elem: Relay): void; +} +/** + */ +declare export class RewardAddress { + free(): void; /** - * !!! DEPRECATED !!! - * Mints are defining by MintBuilder now. - * Use `.get_mint_builder()` and `.build()` instead. - * Returns a copy of the current mint state in the builder - * @returns {Mint | void} + * @param {number} network + * @param {Credential} payment + * @returns {RewardAddress} */ - get_mint(): Mint | void; + static new(network: number, payment: Credential): RewardAddress; /** - * Returns a copy of the current mint witness scripts in the builder - * @returns {NativeScripts | void} + * @returns {Credential} */ - get_mint_scripts(): NativeScripts | void; + payment_cred(): Credential; /** - * !!! DEPRECATED !!! - * Mints are defining by MintBuilder now. - * Use `.set_mint_builder()` and `MintBuilder` instead. - * Add a mint entry to this builder using a PolicyID and MintAssets object - * It will be securely added to existing or new Mint in this builder - * It will replace any existing mint assets with the same PolicyID - * @param {NativeScript} policy_script - * @param {MintAssets} mint_assets + * @returns {Address} */ - set_mint_asset(policy_script: NativeScript, mint_assets: MintAssets): void; + to_address(): Address; /** - * !!! DEPRECATED !!! - * Mints are defining by MintBuilder now. - * Use `.set_mint_builder()` and `MintBuilder` instead. - * Add a mint entry to this builder using a PolicyID, AssetName, and Int object for amount - * It will be securely added to existing or new Mint in this builder - * It will replace any previous existing amount same PolicyID and AssetName - * @param {NativeScript} policy_script - * @param {AssetName} asset_name - * @param {Int} amount + * @param {Address} addr + * @returns {RewardAddress | void} */ - add_mint_asset( - policy_script: NativeScript, - asset_name: AssetName, - amount: Int - ): void; + static from_address(addr: Address): RewardAddress | void; /** - * Add a mint entry together with an output to this builder - * Using a PolicyID, AssetName, Int for amount, Address, and Coin (BigNum) objects - * The asset will be securely added to existing or new Mint in this builder - * A new output will be added with the specified Address, the Coin value, and the minted asset - * @param {NativeScript} policy_script - * @param {AssetName} asset_name - * @param {Int} amount - * @param {TransactionOutputAmountBuilder} output_builder - * @param {BigNum} output_coin + * @returns {number} */ - add_mint_asset_and_output( - policy_script: NativeScript, - asset_name: AssetName, - amount: Int, - output_builder: TransactionOutputAmountBuilder, - output_coin: BigNum - ): void; + network_id(): number; +} +/** + */ +declare export class RewardAddresses { + free(): void; /** - * Add a mint entry together with an output to this builder - * Using a PolicyID, AssetName, Int for amount, and Address objects - * The asset will be securely added to existing or new Mint in this builder - * A new output will be added with the specified Address and the minted asset - * The output will be set to contain the minimum required amount of Coin - * @param {NativeScript} policy_script - * @param {AssetName} asset_name - * @param {Int} amount - * @param {TransactionOutputAmountBuilder} output_builder + * @returns {Uint8Array} */ - add_mint_asset_and_output_min_required_coin( - policy_script: NativeScript, - asset_name: AssetName, - amount: Int, - output_builder: TransactionOutputAmountBuilder - ): void; + to_bytes(): Uint8Array; /** - * @param {TransactionBuilderConfig} cfg - * @returns {TransactionBuilder} + * @param {Uint8Array} bytes + * @returns {RewardAddresses} */ - static new(cfg: TransactionBuilderConfig): TransactionBuilder; + static from_bytes(bytes: Uint8Array): RewardAddresses; /** - * @returns {TransactionInputs} + * @returns {string} */ - get_reference_inputs(): TransactionInputs; + to_hex(): string; /** - * does not include refunds or withdrawals - * @returns {Value} + * @param {string} hex_str + * @returns {RewardAddresses} */ - get_explicit_input(): Value; + static from_hex(hex_str: string): RewardAddresses; /** - * withdrawals and refunds - * @returns {Value} + * @returns {string} */ - get_implicit_input(): Value; + to_json(): string; /** - * Return explicit input plus implicit input plus mint - * @returns {Value} + * @returns {RewardAddressesJSON} */ - get_total_input(): Value; + to_js_value(): RewardAddressesJSON; /** - * Return explicit output plus deposit plus burn - * @returns {Value} + * @param {string} json + * @returns {RewardAddresses} */ - get_total_output(): Value; + static from_json(json: string): RewardAddresses; /** - * does not include fee - * @returns {Value} + * @returns {RewardAddresses} */ - get_explicit_output(): Value; + static new(): RewardAddresses; /** - * @returns {BigNum} + * @returns {number} */ - get_deposit(): BigNum; + len(): number; /** - * @returns {BigNum | void} + * @param {number} index + * @returns {RewardAddress} */ - get_fee_if_set(): BigNum | void; + get(index: number): RewardAddress; /** - * Warning: this function will mutate the /fee/ field - * Make sure to call this function last after setting all other tx-body properties - * Editing inputs, outputs, mint, etc. after change been calculated - * might cause a mismatch in calculated fee versus the required fee - * @param {Address} address - * @returns {boolean} + * @param {RewardAddress} elem */ - add_change_if_needed(address: Address): boolean; + add(elem: RewardAddress): void; +} +/** + */ +declare export class ScriptAll { + free(): void; /** - * @param {Address} address - * @param {OutputDatum} plutus_data - * @returns {boolean} + * @returns {Uint8Array} */ - add_change_if_needed_with_datum( - address: Address, - plutus_data: OutputDatum - ): boolean; + to_bytes(): Uint8Array; /** - * This method will calculate the script hash data - * using the plutus datums and redeemers already present in the builder - * along with the provided cost model, and will register the calculated value - * in the builder to be used when building the tx body. - * In case there are no plutus input witnesses present - nothing will change - * You can set specific hash value using `.set_script_data_hash` - * NOTE: this function will check which language versions are used in the present scripts - * and will assert and require for a corresponding cost-model to be present in the passed map. - * Only the cost-models for the present language versions will be used in the hash calculation. - * @param {Costmdls} cost_models + * @param {Uint8Array} bytes + * @returns {ScriptAll} */ - calc_script_data_hash(cost_models: Costmdls): void; + static from_bytes(bytes: Uint8Array): ScriptAll; /** - * Sets the specified hash value. - * Alternatively you can use `.calc_script_data_hash` to calculate the hash automatically. - * Or use `.remove_script_data_hash` to delete the previously set value - * @param {ScriptDataHash} hash + * @returns {string} */ - set_script_data_hash(hash: ScriptDataHash): void; + to_hex(): string; /** - * Deletes any previously set plutus data hash value. - * Use `.set_script_data_hash` or `.calc_script_data_hash` to set it. + * @param {string} hex_str + * @returns {ScriptAll} */ - remove_script_data_hash(): void; + static from_hex(hex_str: string): ScriptAll; /** - * @param {Ed25519KeyHash} key + * @returns {string} */ - add_required_signer(key: Ed25519KeyHash): void; + to_json(): string; /** - * @returns {number} + * @returns {ScriptAllJSON} */ - full_size(): number; + to_js_value(): ScriptAllJSON; /** - * @returns {Uint32Array} + * @param {string} json + * @returns {ScriptAll} */ - output_sizes(): Uint32Array; + static from_json(json: string): ScriptAll; /** - * Returns object the body of the new transaction - * Auxiliary data itself is not included - * You can use `get_auxiliary_data` or `build_tx` - * @returns {TransactionBody} + * @returns {NativeScripts} */ - build(): TransactionBody; + native_scripts(): NativeScripts; /** - * Returns full Transaction object with the body and the auxiliary data - * NOTE: witness_set will contain all mint_scripts if any been added or set - * NOTE: is_valid set to true - * NOTE: Will fail in case there are any script inputs added with no corresponding witness - * @returns {Transaction} + * @param {NativeScripts} native_scripts + * @returns {ScriptAll} */ - build_tx(): Transaction; + static new(native_scripts: NativeScripts): ScriptAll; +} +/** + */ +declare export class ScriptAny { + free(): void; /** - * Similar to `.build_tx()` but will NOT fail in case there are missing script witnesses - * @returns {Transaction} + * @returns {Uint8Array} */ - build_tx_unsafe(): Transaction; + to_bytes(): Uint8Array; /** - * warning: sum of all parts of a transaction must equal 0. You cannot just set the fee to the min value and forget about it - * warning: min_fee may be slightly larger than the actual minimum fee (ex: a few lovelaces) - * this is done to simplify the library code, but can be fixed later - * @returns {BigNum} + * @param {Uint8Array} bytes + * @returns {ScriptAny} */ - min_fee(): BigNum; -} -/** - */ -declare export class TransactionBuilderConfig { - free(): void; -} -/** - */ -declare export class TransactionBuilderConfigBuilder { - free(): void; + static from_bytes(bytes: Uint8Array): ScriptAny; /** - * @returns {TransactionBuilderConfigBuilder} + * @returns {string} */ - static new(): TransactionBuilderConfigBuilder; + to_hex(): string; /** - * @param {LinearFee} fee_algo - * @returns {TransactionBuilderConfigBuilder} + * @param {string} hex_str + * @returns {ScriptAny} */ - fee_algo(fee_algo: LinearFee): TransactionBuilderConfigBuilder; + static from_hex(hex_str: string): ScriptAny; /** - * !!! DEPRECATED !!! - * Since babbage era cardano nodes use coins per byte. Use '.coins_per_utxo_byte' instead. - * @param {BigNum} coins_per_utxo_word - * @returns {TransactionBuilderConfigBuilder} + * @returns {string} */ - coins_per_utxo_word( - coins_per_utxo_word: BigNum - ): TransactionBuilderConfigBuilder; + to_json(): string; /** - * @param {BigNum} coins_per_utxo_byte - * @returns {TransactionBuilderConfigBuilder} + * @returns {ScriptAnyJSON} */ - coins_per_utxo_byte( - coins_per_utxo_byte: BigNum - ): TransactionBuilderConfigBuilder; + to_js_value(): ScriptAnyJSON; /** - * @param {ExUnitPrices} ex_unit_prices - * @returns {TransactionBuilderConfigBuilder} + * @param {string} json + * @returns {ScriptAny} */ - ex_unit_prices(ex_unit_prices: ExUnitPrices): TransactionBuilderConfigBuilder; + static from_json(json: string): ScriptAny; /** - * @param {BigNum} pool_deposit - * @returns {TransactionBuilderConfigBuilder} + * @returns {NativeScripts} */ - pool_deposit(pool_deposit: BigNum): TransactionBuilderConfigBuilder; + native_scripts(): NativeScripts; /** - * @param {BigNum} key_deposit - * @returns {TransactionBuilderConfigBuilder} + * @param {NativeScripts} native_scripts + * @returns {ScriptAny} */ - key_deposit(key_deposit: BigNum): TransactionBuilderConfigBuilder; + static new(native_scripts: NativeScripts): ScriptAny; +} +/** + */ +declare export class ScriptDataHash { + free(): void; /** - * @param {number} max_value_size - * @returns {TransactionBuilderConfigBuilder} + * @param {Uint8Array} bytes + * @returns {ScriptDataHash} */ - max_value_size(max_value_size: number): TransactionBuilderConfigBuilder; + static from_bytes(bytes: Uint8Array): ScriptDataHash; /** - * @param {number} max_tx_size - * @returns {TransactionBuilderConfigBuilder} + * @returns {Uint8Array} */ - max_tx_size(max_tx_size: number): TransactionBuilderConfigBuilder; + to_bytes(): Uint8Array; /** - * @param {boolean} prefer_pure_change - * @returns {TransactionBuilderConfigBuilder} + * @param {string} prefix + * @returns {string} */ - prefer_pure_change( - prefer_pure_change: boolean - ): TransactionBuilderConfigBuilder; + to_bech32(prefix: string): string; /** - * @returns {TransactionBuilderConfig} + * @param {string} bech_str + * @returns {ScriptDataHash} */ - build(): TransactionBuilderConfig; + static from_bech32(bech_str: string): ScriptDataHash; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex + * @returns {ScriptDataHash} + */ + static from_hex(hex: string): ScriptDataHash; } /** */ -declare export class TransactionHash { +declare export class ScriptHash { free(): void; /** * @param {Uint8Array} bytes - * @returns {TransactionHash} + * @returns {ScriptHash} */ - static from_bytes(bytes: Uint8Array): TransactionHash; + static from_bytes(bytes: Uint8Array): ScriptHash; /** * @returns {Uint8Array} @@ -8940,9 +9372,9 @@ declare export class TransactionHash { /** * @param {string} bech_str - * @returns {TransactionHash} + * @returns {ScriptHash} */ - static from_bech32(bech_str: string): TransactionHash; + static from_bech32(bech_str: string): ScriptHash; /** * @returns {string} @@ -8951,13 +9383,13 @@ declare export class TransactionHash { /** * @param {string} hex - * @returns {TransactionHash} + * @returns {ScriptHash} */ - static from_hex(hex: string): TransactionHash; + static from_hex(hex: string): ScriptHash; } /** */ -declare export class TransactionInput { +declare export class ScriptHashes { free(): void; /** @@ -8967,9 +9399,9 @@ declare export class TransactionInput { /** * @param {Uint8Array} bytes - * @returns {TransactionInput} + * @returns {ScriptHashes} */ - static from_bytes(bytes: Uint8Array): TransactionInput; + static from_bytes(bytes: Uint8Array): ScriptHashes; /** * @returns {string} @@ -8978,9 +9410,9 @@ declare export class TransactionInput { /** * @param {string} hex_str - * @returns {TransactionInput} + * @returns {ScriptHashes} */ - static from_hex(hex_str: string): TransactionInput; + static from_hex(hex_str: string): ScriptHashes; /** * @returns {string} @@ -8988,36 +9420,40 @@ declare export class TransactionInput { to_json(): string; /** - * @returns {TransactionInputJSON} + * @returns {ScriptHashesJSON} */ - to_js_value(): TransactionInputJSON; + to_js_value(): ScriptHashesJSON; /** * @param {string} json - * @returns {TransactionInput} + * @returns {ScriptHashes} */ - static from_json(json: string): TransactionInput; + static from_json(json: string): ScriptHashes; /** - * @returns {TransactionHash} + * @returns {ScriptHashes} */ - transaction_id(): TransactionHash; + static new(): ScriptHashes; /** * @returns {number} */ - index(): number; + len(): number; /** - * @param {TransactionHash} transaction_id * @param {number} index - * @returns {TransactionInput} + * @returns {ScriptHash} */ - static new(transaction_id: TransactionHash, index: number): TransactionInput; + get(index: number): ScriptHash; + + /** + * @param {ScriptHash} elem + */ + add(elem: ScriptHash): void; } /** */ -declare export class TransactionInputs { +declare export class ScriptNOfK { free(): void; /** @@ -9027,9 +9463,9 @@ declare export class TransactionInputs { /** * @param {Uint8Array} bytes - * @returns {TransactionInputs} + * @returns {ScriptNOfK} */ - static from_bytes(bytes: Uint8Array): TransactionInputs; + static from_bytes(bytes: Uint8Array): ScriptNOfK; /** * @returns {string} @@ -9038,9 +9474,9 @@ declare export class TransactionInputs { /** * @param {string} hex_str - * @returns {TransactionInputs} + * @returns {ScriptNOfK} */ - static from_hex(hex_str: string): TransactionInputs; + static from_hex(hex_str: string): ScriptNOfK; /** * @returns {string} @@ -9048,45 +9484,36 @@ declare export class TransactionInputs { to_json(): string; /** - * @returns {TransactionInputsJSON} + * @returns {ScriptNOfKJSON} */ - to_js_value(): TransactionInputsJSON; + to_js_value(): ScriptNOfKJSON; /** * @param {string} json - * @returns {TransactionInputs} - */ - static from_json(json: string): TransactionInputs; - - /** - * @returns {TransactionInputs} + * @returns {ScriptNOfK} */ - static new(): TransactionInputs; + static from_json(json: string): ScriptNOfK; /** * @returns {number} */ - len(): number; - - /** - * @param {number} index - * @returns {TransactionInput} - */ - get(index: number): TransactionInput; + n(): number; /** - * @param {TransactionInput} elem + * @returns {NativeScripts} */ - add(elem: TransactionInput): void; + native_scripts(): NativeScripts; /** - * @returns {TransactionInputs | void} + * @param {number} n + * @param {NativeScripts} native_scripts + * @returns {ScriptNOfK} */ - to_option(): TransactionInputs | void; + static new(n: number, native_scripts: NativeScripts): ScriptNOfK; } /** */ -declare export class TransactionMetadatum { +declare export class ScriptPubkey { free(): void; /** @@ -9096,9 +9523,9 @@ declare export class TransactionMetadatum { /** * @param {Uint8Array} bytes - * @returns {TransactionMetadatum} + * @returns {ScriptPubkey} */ - static from_bytes(bytes: Uint8Array): TransactionMetadatum; + static from_bytes(bytes: Uint8Array): ScriptPubkey; /** * @returns {string} @@ -9107,121 +9534,123 @@ declare export class TransactionMetadatum { /** * @param {string} hex_str - * @returns {TransactionMetadatum} + * @returns {ScriptPubkey} */ - static from_hex(hex_str: string): TransactionMetadatum; + static from_hex(hex_str: string): ScriptPubkey; /** - * @param {MetadataMap} map - * @returns {TransactionMetadatum} + * @returns {string} */ - static new_map(map: MetadataMap): TransactionMetadatum; + to_json(): string; /** - * @param {MetadataList} list - * @returns {TransactionMetadatum} + * @returns {ScriptPubkeyJSON} */ - static new_list(list: MetadataList): TransactionMetadatum; + to_js_value(): ScriptPubkeyJSON; /** - * @param {Int} int - * @returns {TransactionMetadatum} + * @param {string} json + * @returns {ScriptPubkey} */ - static new_int(int: Int): TransactionMetadatum; + static from_json(json: string): ScriptPubkey; /** - * @param {Uint8Array} bytes - * @returns {TransactionMetadatum} + * @returns {Ed25519KeyHash} */ - static new_bytes(bytes: Uint8Array): TransactionMetadatum; + addr_keyhash(): Ed25519KeyHash; /** - * @param {string} text - * @returns {TransactionMetadatum} + * @param {Ed25519KeyHash} addr_keyhash + * @returns {ScriptPubkey} */ - static new_text(text: string): TransactionMetadatum; + static new(addr_keyhash: Ed25519KeyHash): ScriptPubkey; +} +/** + */ +declare export class ScriptRef { + free(): void; /** - * @returns {number} + * @returns {Uint8Array} */ - kind(): number; + to_bytes(): Uint8Array; /** - * @returns {MetadataMap} + * @param {Uint8Array} bytes + * @returns {ScriptRef} */ - as_map(): MetadataMap; + static from_bytes(bytes: Uint8Array): ScriptRef; /** - * @returns {MetadataList} + * @returns {string} */ - as_list(): MetadataList; + to_hex(): string; /** - * @returns {Int} + * @param {string} hex_str + * @returns {ScriptRef} */ - as_int(): Int; + static from_hex(hex_str: string): ScriptRef; /** - * @returns {Uint8Array} + * @returns {string} */ - as_bytes(): Uint8Array; + to_json(): string; /** - * @returns {string} + * @returns {ScriptRefJSON} */ - as_text(): string; -} -/** - */ -declare export class TransactionMetadatumLabels { - free(): void; + to_js_value(): ScriptRefJSON; /** - * @returns {Uint8Array} + * @param {string} json + * @returns {ScriptRef} */ - to_bytes(): Uint8Array; + static from_json(json: string): ScriptRef; /** - * @param {Uint8Array} bytes - * @returns {TransactionMetadatumLabels} + * @param {NativeScript} native_script + * @returns {ScriptRef} */ - static from_bytes(bytes: Uint8Array): TransactionMetadatumLabels; + static new_native_script(native_script: NativeScript): ScriptRef; /** - * @returns {string} + * @param {PlutusScript} plutus_script + * @returns {ScriptRef} */ - to_hex(): string; + static new_plutus_script(plutus_script: PlutusScript): ScriptRef; /** - * @param {string} hex_str - * @returns {TransactionMetadatumLabels} + * @returns {boolean} */ - static from_hex(hex_str: string): TransactionMetadatumLabels; + is_native_script(): boolean; /** - * @returns {TransactionMetadatumLabels} + * @returns {boolean} */ - static new(): TransactionMetadatumLabels; + is_plutus_script(): boolean; /** - * @returns {number} + * @returns {NativeScript | void} */ - len(): number; + native_script(): NativeScript | void; /** - * @param {number} index - * @returns {BigNum} + * @returns {PlutusScript | void} */ - get(index: number): BigNum; + plutus_script(): PlutusScript | void; /** - * @param {BigNum} elem + * Return bytes array of script ref struct but without wrapping into CBOR array under the tag + * to_bytes returns "#6.24(bytes .cbor script)" from CDDL + * to_unwrapped_bytes return "script" from CDDL + * @returns {Uint8Array} */ - add(elem: BigNum): void; + to_unwrapped_bytes(): Uint8Array; } /** */ -declare export class TransactionOutput { +declare export class SingleHostAddr { free(): void; /** @@ -9231,9 +9660,9 @@ declare export class TransactionOutput { /** * @param {Uint8Array} bytes - * @returns {TransactionOutput} + * @returns {SingleHostAddr} */ - static from_bytes(bytes: Uint8Array): TransactionOutput; + static from_bytes(bytes: Uint8Array): SingleHostAddr; /** * @returns {string} @@ -9242,9 +9671,9 @@ declare export class TransactionOutput { /** * @param {string} hex_str - * @returns {TransactionOutput} + * @returns {SingleHostAddr} */ - static from_hex(hex_str: string): TransactionOutput; + static from_hex(hex_str: string): SingleHostAddr; /** * @returns {string} @@ -9252,184 +9681,177 @@ declare export class TransactionOutput { to_json(): string; /** - * @returns {TransactionOutputJSON} + * @returns {SingleHostAddrJSON} */ - to_js_value(): TransactionOutputJSON; + to_js_value(): SingleHostAddrJSON; /** * @param {string} json - * @returns {TransactionOutput} + * @returns {SingleHostAddr} */ - static from_json(json: string): TransactionOutput; + static from_json(json: string): SingleHostAddr; /** - * @returns {Address} + * @returns {number | void} */ - address(): Address; + port(): number | void; /** - * @returns {Value} + * @returns {Ipv4 | void} */ - amount(): Value; + ipv4(): Ipv4 | void; /** - * @returns {DataHash | void} + * @returns {Ipv6 | void} */ - data_hash(): DataHash | void; + ipv6(): Ipv6 | void; /** - * @returns {PlutusData | void} + * @param {number | void} [port] + * @param {Ipv4 | void} [ipv4] + * @param {Ipv6 | void} [ipv6] + * @returns {SingleHostAddr} */ - plutus_data(): PlutusData | void; + static new(port?: number, ipv4?: Ipv4, ipv6?: Ipv6): SingleHostAddr; +} +/** + */ +declare export class SingleHostName { + free(): void; /** - * @returns {ScriptRef | void} + * @returns {Uint8Array} */ - script_ref(): ScriptRef | void; + to_bytes(): Uint8Array; /** - * @param {ScriptRef} script_ref + * @param {Uint8Array} bytes + * @returns {SingleHostName} */ - set_script_ref(script_ref: ScriptRef): void; + static from_bytes(bytes: Uint8Array): SingleHostName; /** - * @param {PlutusData} data + * @returns {string} */ - set_plutus_data(data: PlutusData): void; + to_hex(): string; /** - * @param {DataHash} data_hash + * @param {string} hex_str + * @returns {SingleHostName} */ - set_data_hash(data_hash: DataHash): void; + static from_hex(hex_str: string): SingleHostName; /** - * @returns {boolean} + * @returns {string} */ - has_plutus_data(): boolean; + to_json(): string; /** - * @returns {boolean} + * @returns {SingleHostNameJSON} */ - has_data_hash(): boolean; + to_js_value(): SingleHostNameJSON; /** - * @returns {boolean} + * @param {string} json + * @returns {SingleHostName} */ - has_script_ref(): boolean; + static from_json(json: string): SingleHostName; /** - * @param {Address} address - * @param {Value} amount - * @returns {TransactionOutput} + * @returns {number | void} */ - static new(address: Address, amount: Value): TransactionOutput; + port(): number | void; /** - * @returns {number | void} + * @returns {DNSRecordAorAAAA} + */ + dns_name(): DNSRecordAorAAAA; + + /** + * @param {number | void} port + * @param {DNSRecordAorAAAA} dns_name + * @returns {SingleHostName} */ - serialization_format(): number | void; + static new(port: number | void, dns_name: DNSRecordAorAAAA): SingleHostName; } /** */ -declare export class TransactionOutputAmountBuilder { +declare export class StakeAndVoteDelegation { free(): void; /** - * @param {Value} amount - * @returns {TransactionOutputAmountBuilder} + * @returns {Uint8Array} */ - with_value(amount: Value): TransactionOutputAmountBuilder; + to_bytes(): Uint8Array; /** - * @param {BigNum} coin - * @returns {TransactionOutputAmountBuilder} + * @param {Uint8Array} bytes + * @returns {StakeAndVoteDelegation} */ - with_coin(coin: BigNum): TransactionOutputAmountBuilder; + static from_bytes(bytes: Uint8Array): StakeAndVoteDelegation; /** - * @param {BigNum} coin - * @param {MultiAsset} multiasset - * @returns {TransactionOutputAmountBuilder} + * @returns {string} */ - with_coin_and_asset( - coin: BigNum, - multiasset: MultiAsset - ): TransactionOutputAmountBuilder; + to_hex(): string; /** - * !!! DEPRECATED !!! - * Since babbage era cardano nodes use coins per byte. Use '.with_asset_and_min_required_coin_by_utxo_cost' instead. - * @param {MultiAsset} multiasset - * @param {BigNum} coins_per_utxo_word - * @returns {TransactionOutputAmountBuilder} + * @param {string} hex_str + * @returns {StakeAndVoteDelegation} */ - with_asset_and_min_required_coin( - multiasset: MultiAsset, - coins_per_utxo_word: BigNum - ): TransactionOutputAmountBuilder; + static from_hex(hex_str: string): StakeAndVoteDelegation; /** - * @param {MultiAsset} multiasset - * @param {DataCost} data_cost - * @returns {TransactionOutputAmountBuilder} + * @returns {string} */ - with_asset_and_min_required_coin_by_utxo_cost( - multiasset: MultiAsset, - data_cost: DataCost - ): TransactionOutputAmountBuilder; + to_json(): string; /** - * @returns {TransactionOutput} + * @returns {StakeAndVoteDelegationJSON} */ - build(): TransactionOutput; -} -/** - * We introduce a builder-pattern format for creating transaction outputs - * This is because: - * 1. Some fields (i.e. data hash) are optional, and we can't easily expose Option<> in WASM - * 2. Some fields like amounts have many ways it could be set (some depending on other field values being known) - * 3. Easier to adapt as the output format gets more complicated in future Cardano releases - */ -declare export class TransactionOutputBuilder { - free(): void; + to_js_value(): StakeAndVoteDelegationJSON; /** - * @returns {TransactionOutputBuilder} + * @param {string} json + * @returns {StakeAndVoteDelegation} */ - static new(): TransactionOutputBuilder; + static from_json(json: string): StakeAndVoteDelegation; /** - * @param {Address} address - * @returns {TransactionOutputBuilder} + * @returns {Credential} */ - with_address(address: Address): TransactionOutputBuilder; + stake_credential(): Credential; /** - * @param {DataHash} data_hash - * @returns {TransactionOutputBuilder} + * @returns {Ed25519KeyHash} */ - with_data_hash(data_hash: DataHash): TransactionOutputBuilder; + pool_keyhash(): Ed25519KeyHash; /** - * @param {PlutusData} data - * @returns {TransactionOutputBuilder} + * @returns {DRep} */ - with_plutus_data(data: PlutusData): TransactionOutputBuilder; + drep(): DRep; /** - * @param {ScriptRef} script_ref - * @returns {TransactionOutputBuilder} + * @param {Credential} stake_credential + * @param {Ed25519KeyHash} pool_keyhash + * @param {DRep} drep + * @returns {StakeAndVoteDelegation} */ - with_script_ref(script_ref: ScriptRef): TransactionOutputBuilder; + static new( + stake_credential: Credential, + pool_keyhash: Ed25519KeyHash, + drep: DRep + ): StakeAndVoteDelegation; /** - * @returns {TransactionOutputAmountBuilder} + * @returns {boolean} */ - next(): TransactionOutputAmountBuilder; + has_script_credentials(): boolean; } /** */ -declare export class TransactionOutputs { +declare export class StakeDelegation { free(): void; /** @@ -9439,9 +9861,9 @@ declare export class TransactionOutputs { /** * @param {Uint8Array} bytes - * @returns {TransactionOutputs} + * @returns {StakeDelegation} */ - static from_bytes(bytes: Uint8Array): TransactionOutputs; + static from_bytes(bytes: Uint8Array): StakeDelegation; /** * @returns {string} @@ -9450,9 +9872,9 @@ declare export class TransactionOutputs { /** * @param {string} hex_str - * @returns {TransactionOutputs} + * @returns {StakeDelegation} */ - static from_hex(hex_str: string): TransactionOutputs; + static from_hex(hex_str: string): StakeDelegation; /** * @returns {string} @@ -9460,40 +9882,44 @@ declare export class TransactionOutputs { to_json(): string; /** - * @returns {TransactionOutputsJSON} + * @returns {StakeDelegationJSON} */ - to_js_value(): TransactionOutputsJSON; + to_js_value(): StakeDelegationJSON; /** * @param {string} json - * @returns {TransactionOutputs} + * @returns {StakeDelegation} */ - static from_json(json: string): TransactionOutputs; + static from_json(json: string): StakeDelegation; /** - * @returns {TransactionOutputs} + * @returns {Credential} */ - static new(): TransactionOutputs; + stake_credential(): Credential; /** - * @returns {number} + * @returns {Ed25519KeyHash} */ - len(): number; + pool_keyhash(): Ed25519KeyHash; /** - * @param {number} index - * @returns {TransactionOutput} + * @param {Credential} stake_credential + * @param {Ed25519KeyHash} pool_keyhash + * @returns {StakeDelegation} */ - get(index: number): TransactionOutput; + static new( + stake_credential: Credential, + pool_keyhash: Ed25519KeyHash + ): StakeDelegation; /** - * @param {TransactionOutput} elem + * @returns {boolean} */ - add(elem: TransactionOutput): void; + has_script_credentials(): boolean; } /** */ -declare export class TransactionUnspentOutput { +declare export class StakeDeregistration { free(): void; /** @@ -9503,9 +9929,9 @@ declare export class TransactionUnspentOutput { /** * @param {Uint8Array} bytes - * @returns {TransactionUnspentOutput} + * @returns {StakeDeregistration} */ - static from_bytes(bytes: Uint8Array): TransactionUnspentOutput; + static from_bytes(bytes: Uint8Array): StakeDeregistration; /** * @returns {string} @@ -9514,9 +9940,9 @@ declare export class TransactionUnspentOutput { /** * @param {string} hex_str - * @returns {TransactionUnspentOutput} + * @returns {StakeDeregistration} */ - static from_hex(hex_str: string): TransactionUnspentOutput; + static from_hex(hex_str: string): StakeDeregistration; /** * @returns {string} @@ -9524,81 +9950,50 @@ declare export class TransactionUnspentOutput { to_json(): string; /** - * @returns {TransactionUnspentOutputJSON} + * @returns {StakeDeregistrationJSON} */ - to_js_value(): TransactionUnspentOutputJSON; + to_js_value(): StakeDeregistrationJSON; /** * @param {string} json - * @returns {TransactionUnspentOutput} - */ - static from_json(json: string): TransactionUnspentOutput; - - /** - * @param {TransactionInput} input - * @param {TransactionOutput} output - * @returns {TransactionUnspentOutput} - */ - static new( - input: TransactionInput, - output: TransactionOutput - ): TransactionUnspentOutput; - - /** - * @returns {TransactionInput} - */ - input(): TransactionInput; - - /** - * @returns {TransactionOutput} - */ - output(): TransactionOutput; -} -/** - */ -declare export class TransactionUnspentOutputs { - free(): void; - - /** - * @returns {string} - */ - to_json(): string; - - /** - * @returns {TransactionUnspentOutputsJSON} + * @returns {StakeDeregistration} */ - to_js_value(): TransactionUnspentOutputsJSON; + static from_json(json: string): StakeDeregistration; /** - * @param {string} json - * @returns {TransactionUnspentOutputs} + * @returns {Credential} */ - static from_json(json: string): TransactionUnspentOutputs; + stake_credential(): Credential; /** - * @returns {TransactionUnspentOutputs} + * @returns {BigNum | void} */ - static new(): TransactionUnspentOutputs; + coin(): BigNum | void; /** - * @returns {number} + * @param {Credential} stake_credential + * @returns {StakeDeregistration} */ - len(): number; + static new(stake_credential: Credential): StakeDeregistration; /** - * @param {number} index - * @returns {TransactionUnspentOutput} + * @param {Credential} stake_credential + * @param {BigNum} coin + * @returns {StakeDeregistration} */ - get(index: number): TransactionUnspentOutput; + static new_with_explicit_refund( + stake_credential: Credential, + coin: BigNum + ): StakeDeregistration; /** - * @param {TransactionUnspentOutput} elem + * @returns {boolean} */ - add(elem: TransactionUnspentOutput): void; + has_script_credentials(): boolean; } /** */ -declare export class TransactionWitnessSet { +declare export class StakeRegistration { free(): void; /** @@ -9608,9 +10003,9 @@ declare export class TransactionWitnessSet { /** * @param {Uint8Array} bytes - * @returns {TransactionWitnessSet} + * @returns {StakeRegistration} */ - static from_bytes(bytes: Uint8Array): TransactionWitnessSet; + static from_bytes(bytes: Uint8Array): StakeRegistration; /** * @returns {string} @@ -9619,9 +10014,9 @@ declare export class TransactionWitnessSet { /** * @param {string} hex_str - * @returns {TransactionWitnessSet} + * @returns {StakeRegistration} */ - static from_hex(hex_str: string): TransactionWitnessSet; + static from_hex(hex_str: string): StakeRegistration; /** * @returns {string} @@ -9629,84 +10024,125 @@ declare export class TransactionWitnessSet { to_json(): string; /** - * @returns {TransactionWitnessSetJSON} + * @returns {StakeRegistrationJSON} */ - to_js_value(): TransactionWitnessSetJSON; + to_js_value(): StakeRegistrationJSON; /** * @param {string} json - * @returns {TransactionWitnessSet} + * @returns {StakeRegistration} */ - static from_json(json: string): TransactionWitnessSet; + static from_json(json: string): StakeRegistration; /** - * @param {Vkeywitnesses} vkeys + * @returns {Credential} */ - set_vkeys(vkeys: Vkeywitnesses): void; + stake_credential(): Credential; /** - * @returns {Vkeywitnesses | void} + * @returns {BigNum | void} */ - vkeys(): Vkeywitnesses | void; + coin(): BigNum | void; /** - * @param {NativeScripts} native_scripts + * @param {Credential} stake_credential + * @returns {StakeRegistration} */ - set_native_scripts(native_scripts: NativeScripts): void; + static new(stake_credential: Credential): StakeRegistration; /** - * @returns {NativeScripts | void} + * @param {Credential} stake_credential + * @param {BigNum} coin + * @returns {StakeRegistration} */ - native_scripts(): NativeScripts | void; + static new_with_explicit_deposit( + stake_credential: Credential, + coin: BigNum + ): StakeRegistration; /** - * @param {BootstrapWitnesses} bootstraps + * @returns {boolean} */ - set_bootstraps(bootstraps: BootstrapWitnesses): void; + has_script_credentials(): boolean; +} +/** + */ +declare export class StakeRegistrationAndDelegation { + free(): void; /** - * @returns {BootstrapWitnesses | void} + * @returns {Uint8Array} */ - bootstraps(): BootstrapWitnesses | void; + to_bytes(): Uint8Array; /** - * @param {PlutusScripts} plutus_scripts + * @param {Uint8Array} bytes + * @returns {StakeRegistrationAndDelegation} */ - set_plutus_scripts(plutus_scripts: PlutusScripts): void; + static from_bytes(bytes: Uint8Array): StakeRegistrationAndDelegation; /** - * @returns {PlutusScripts | void} + * @returns {string} */ - plutus_scripts(): PlutusScripts | void; + to_hex(): string; /** - * @param {PlutusList} plutus_data + * @param {string} hex_str + * @returns {StakeRegistrationAndDelegation} */ - set_plutus_data(plutus_data: PlutusList): void; + static from_hex(hex_str: string): StakeRegistrationAndDelegation; /** - * @returns {PlutusList | void} + * @returns {string} */ - plutus_data(): PlutusList | void; + to_json(): string; /** - * @param {Redeemers} redeemers + * @returns {StakeRegistrationAndDelegationJSON} */ - set_redeemers(redeemers: Redeemers): void; + to_js_value(): StakeRegistrationAndDelegationJSON; /** - * @returns {Redeemers | void} + * @param {string} json + * @returns {StakeRegistrationAndDelegation} */ - redeemers(): Redeemers | void; + static from_json(json: string): StakeRegistrationAndDelegation; /** - * @returns {TransactionWitnessSet} + * @returns {Credential} */ - static new(): TransactionWitnessSet; + stake_credential(): Credential; + + /** + * @returns {Ed25519KeyHash} + */ + pool_keyhash(): Ed25519KeyHash; + + /** + * @returns {BigNum} + */ + coin(): BigNum; + + /** + * @param {Credential} stake_credential + * @param {Ed25519KeyHash} pool_keyhash + * @param {BigNum} coin + * @returns {StakeRegistrationAndDelegation} + */ + static new( + stake_credential: Credential, + pool_keyhash: Ed25519KeyHash, + coin: BigNum + ): StakeRegistrationAndDelegation; + + /** + * @returns {boolean} + */ + has_script_credentials(): boolean; } /** */ -declare export class TransactionWitnessSets { +declare export class StakeVoteRegistrationAndDelegation { free(): void; /** @@ -9716,9 +10152,9 @@ declare export class TransactionWitnessSets { /** * @param {Uint8Array} bytes - * @returns {TransactionWitnessSets} + * @returns {StakeVoteRegistrationAndDelegation} */ - static from_bytes(bytes: Uint8Array): TransactionWitnessSets; + static from_bytes(bytes: Uint8Array): StakeVoteRegistrationAndDelegation; /** * @returns {string} @@ -9727,9 +10163,9 @@ declare export class TransactionWitnessSets { /** * @param {string} hex_str - * @returns {TransactionWitnessSets} + * @returns {StakeVoteRegistrationAndDelegation} */ - static from_hex(hex_str: string): TransactionWitnessSets; + static from_hex(hex_str: string): StakeVoteRegistrationAndDelegation; /** * @returns {string} @@ -9737,238 +10173,224 @@ declare export class TransactionWitnessSets { to_json(): string; /** - * @returns {TransactionWitnessSetsJSON} + * @returns {StakeVoteRegistrationAndDelegationJSON} */ - to_js_value(): TransactionWitnessSetsJSON; + to_js_value(): StakeVoteRegistrationAndDelegationJSON; /** * @param {string} json - * @returns {TransactionWitnessSets} + * @returns {StakeVoteRegistrationAndDelegation} */ - static from_json(json: string): TransactionWitnessSets; + static from_json(json: string): StakeVoteRegistrationAndDelegation; /** - * @returns {TransactionWitnessSets} + * @returns {Credential} */ - static new(): TransactionWitnessSets; + stake_credential(): Credential; /** - * @returns {number} + * @returns {Ed25519KeyHash} */ - len(): number; + pool_keyhash(): Ed25519KeyHash; /** - * @param {number} index - * @returns {TransactionWitnessSet} + * @returns {DRep} */ - get(index: number): TransactionWitnessSet; + drep(): DRep; /** - * @param {TransactionWitnessSet} elem + * @returns {BigNum} */ - add(elem: TransactionWitnessSet): void; + coin(): BigNum; + + /** + * @param {Credential} stake_credential + * @param {Ed25519KeyHash} pool_keyhash + * @param {DRep} drep + * @param {BigNum} coin + * @returns {StakeVoteRegistrationAndDelegation} + */ + static new( + stake_credential: Credential, + pool_keyhash: Ed25519KeyHash, + drep: DRep, + coin: BigNum + ): StakeVoteRegistrationAndDelegation; + + /** + * @returns {boolean} + */ + has_script_credentials(): boolean; } /** */ -declare export class TxBuilderConstants { +declare export class Strings { free(): void; /** - * @returns {Costmdls} + * @returns {Strings} */ - static plutus_default_cost_models(): Costmdls; + static new(): Strings; /** - * @returns {Costmdls} + * @returns {number} */ - static plutus_alonzo_cost_models(): Costmdls; + len(): number; /** - * @returns {Costmdls} + * @param {number} index + * @returns {string} */ - static plutus_vasil_cost_models(): Costmdls; + get(index: number): string; + + /** + * @param {string} elem + */ + add(elem: string): void; } /** */ -declare export class TxInputsBuilder { +declare export class TimelockExpiry { free(): void; /** - * @returns {TxInputsBuilder} + * @returns {Uint8Array} */ - static new(): TxInputsBuilder; + to_bytes(): Uint8Array; /** - * We have to know what kind of inputs these are to know what kind of mock witnesses to create since - * 1) mock witnesses have different lengths depending on the type which changes the expecting fee - * 2) Witnesses are a set so we need to get rid of duplicates to avoid over-estimating the fee - * @param {Ed25519KeyHash} hash - * @param {TransactionInput} input - * @param {Value} amount + * @param {Uint8Array} bytes + * @returns {TimelockExpiry} */ - add_key_input( - hash: Ed25519KeyHash, - input: TransactionInput, - amount: Value - ): void; + static from_bytes(bytes: Uint8Array): TimelockExpiry; /** - * !!! DEPRECATED !!! - * This function can make a mistake in choosing right input index. Use `.add_native_script_input` or `.add_plutus_script_input` instead. - * This method adds the input to the builder BUT leaves a missing spot for the witness native script - * - * After adding the input with this method, use `.add_required_native_input_scripts` - * and `.add_required_plutus_input_scripts` to add the witness scripts - * - * Or instead use `.add_native_script_input` and `.add_plutus_script_input` - * to add inputs right along with the script, instead of the script hash - * @param {ScriptHash} hash - * @param {TransactionInput} input - * @param {Value} amount + * @returns {string} */ - add_script_input( - hash: ScriptHash, - input: TransactionInput, - amount: Value - ): void; + to_hex(): string; /** - * This method will add the input to the builder and also register the required native script witness - * @param {NativeScript} script - * @param {TransactionInput} input - * @param {Value} amount + * @param {string} hex_str + * @returns {TimelockExpiry} */ - add_native_script_input( - script: NativeScript, - input: TransactionInput, - amount: Value - ): void; + static from_hex(hex_str: string): TimelockExpiry; /** - * This method will add the input to the builder and also register the required plutus witness - * @param {PlutusWitness} witness - * @param {TransactionInput} input - * @param {Value} amount + * @returns {string} */ - add_plutus_script_input( - witness: PlutusWitness, - input: TransactionInput, - amount: Value - ): void; + to_json(): string; /** - * @param {ByronAddress} hash - * @param {TransactionInput} input - * @param {Value} amount + * @returns {TimelockExpiryJSON} */ - add_bootstrap_input( - hash: ByronAddress, - input: TransactionInput, - amount: Value - ): void; + to_js_value(): TimelockExpiryJSON; /** - * Note that for script inputs this method will use underlying generic `.add_script_input` - * which leaves a required empty spot for the script witness (or witnesses in case of Plutus). - * You can use `.add_native_script_input` or `.add_plutus_script_input` directly to register the input along with the witness. - * @param {Address} address - * @param {TransactionInput} input - * @param {Value} amount + * @param {string} json + * @returns {TimelockExpiry} */ - add_input(address: Address, input: TransactionInput, amount: Value): void; + static from_json(json: string): TimelockExpiry; /** - * Returns the number of still missing input scripts (either native or plutus) - * Use `.add_required_native_input_scripts` or `.add_required_plutus_input_scripts` to add the missing scripts * @returns {number} */ - count_missing_input_scripts(): number; + slot(): number; /** - * Try adding the specified scripts as witnesses for ALREADY ADDED script inputs - * Any scripts that don't match any of the previously added inputs will be ignored - * Returns the number of remaining required missing witness scripts - * Use `.count_missing_input_scripts` to find the number of still missing scripts - * @param {NativeScripts} scripts - * @returns {number} + * @returns {BigNum} */ - add_required_native_input_scripts(scripts: NativeScripts): number; + slot_bignum(): BigNum; /** * !!! DEPRECATED !!! - * This function can make a mistake in choosing right input index. Use `.add_required_script_input_witnesses` instead. - * Try adding the specified scripts as witnesses for ALREADY ADDED script inputs - * Any scripts that don't match any of the previously added inputs will be ignored - * Returns the number of remaining required missing witness scripts - * Use `.count_missing_input_scripts` to find the number of still missing scripts - * @param {PlutusWitnesses} scripts - * @returns {number} + * This constructor uses outdated slot number format. + * Use `.new_timelockexpiry` instead + * @param {number} slot + * @returns {TimelockExpiry} */ - add_required_plutus_input_scripts(scripts: PlutusWitnesses): number; + static new(slot: number): TimelockExpiry; /** - * Try adding the specified scripts as witnesses for ALREADY ADDED script inputs - * Any scripts that don't match any of the previously added inputs will be ignored - * Returns the number of remaining required missing witness scripts - * Use `.count_missing_input_scripts` to find the number of still missing scripts - * @param {InputsWithScriptWitness} inputs_with_wit - * @returns {number} + * @param {BigNum} slot + * @returns {TimelockExpiry} */ - add_required_script_input_witnesses( - inputs_with_wit: InputsWithScriptWitness - ): number; + static new_timelockexpiry(slot: BigNum): TimelockExpiry; +} +/** + */ +declare export class TimelockStart { + free(): void; /** - * @returns {TransactionInputs} + * @returns {Uint8Array} */ - get_ref_inputs(): TransactionInputs; + to_bytes(): Uint8Array; /** - * Returns a copy of the current script input witness scripts in the builder - * @returns {NativeScripts | void} + * @param {Uint8Array} bytes + * @returns {TimelockStart} */ - get_native_input_scripts(): NativeScripts | void; + static from_bytes(bytes: Uint8Array): TimelockStart; /** - * Returns a copy of the current plutus input witness scripts in the builder. - * NOTE: each plutus witness will be cloned with a specific corresponding input index - * @returns {PlutusWitnesses | void} + * @returns {string} */ - get_plutus_input_scripts(): PlutusWitnesses | void; + to_hex(): string; /** - * @returns {number} + * @param {string} hex_str + * @returns {TimelockStart} */ - len(): number; + static from_hex(hex_str: string): TimelockStart; /** - * @param {Ed25519KeyHash} key + * @returns {string} */ - add_required_signer(key: Ed25519KeyHash): void; + to_json(): string; /** - * @param {Ed25519KeyHashes} keys + * @returns {TimelockStartJSON} */ - add_required_signers(keys: Ed25519KeyHashes): void; + to_js_value(): TimelockStartJSON; /** - * @returns {Value} + * @param {string} json + * @returns {TimelockStart} */ - total_value(): Value; + static from_json(json: string): TimelockStart; /** - * @returns {TransactionInputs} + * !!! DEPRECATED !!! + * Returns a Slot32 (u32) value in case the underlying original BigNum (u64) value is within the limits. + * Otherwise will just raise an error. + * Use `.slot_bignum` instead + * @returns {number} */ - inputs(): TransactionInputs; + slot(): number; /** - * @returns {TransactionInputs | void} + * @returns {BigNum} */ - inputs_option(): TransactionInputs | void; + slot_bignum(): BigNum; + + /** + * !!! DEPRECATED !!! + * This constructor uses outdated slot number format. + * Use `.new_timelockstart` instead. + * @param {number} slot + * @returns {TimelockStart} + */ + static new(slot: number): TimelockStart; + + /** + * @param {BigNum} slot + * @returns {TimelockStart} + */ + static new_timelockstart(slot: BigNum): TimelockStart; } /** */ -declare export class URL { +declare export class Transaction { free(): void; /** @@ -9978,9 +10400,9 @@ declare export class URL { /** * @param {Uint8Array} bytes - * @returns {URL} + * @returns {Transaction} */ - static from_bytes(bytes: Uint8Array): URL; + static from_bytes(bytes: Uint8Array): Transaction; /** * @returns {string} @@ -9989,9 +10411,9 @@ declare export class URL { /** * @param {string} hex_str - * @returns {URL} + * @returns {Transaction} */ - static from_hex(hex_str: string): URL; + static from_hex(hex_str: string): Transaction; /** * @returns {string} @@ -9999,90 +10421,88 @@ declare export class URL { to_json(): string; /** - * @returns {URLJSON} + * @returns {TransactionJSON} */ - to_js_value(): URLJSON; + to_js_value(): TransactionJSON; /** * @param {string} json - * @returns {URL} - */ - static from_json(json: string): URL; - - /** - * @param {string} url - * @returns {URL} - */ - static new(url: string): URL; - - /** - * @returns {string} + * @returns {Transaction} */ - url(): string; -} -/** - */ -declare export class UnitInterval { - free(): void; + static from_json(json: string): Transaction; /** - * @returns {Uint8Array} + * @returns {TransactionBody} */ - to_bytes(): Uint8Array; + body(): TransactionBody; /** - * @param {Uint8Array} bytes - * @returns {UnitInterval} + * @returns {TransactionWitnessSet} */ - static from_bytes(bytes: Uint8Array): UnitInterval; + witness_set(): TransactionWitnessSet; /** - * @returns {string} + * @returns {boolean} */ - to_hex(): string; + is_valid(): boolean; /** - * @param {string} hex_str - * @returns {UnitInterval} + * @returns {AuxiliaryData | void} */ - static from_hex(hex_str: string): UnitInterval; + auxiliary_data(): AuxiliaryData | void; /** - * @returns {string} + * @param {boolean} valid */ - to_json(): string; + set_is_valid(valid: boolean): void; /** - * @returns {UnitIntervalJSON} + * @param {TransactionBody} body + * @param {TransactionWitnessSet} witness_set + * @param {AuxiliaryData | void} [auxiliary_data] + * @returns {Transaction} */ - to_js_value(): UnitIntervalJSON; + static new( + body: TransactionBody, + witness_set: TransactionWitnessSet, + auxiliary_data?: AuxiliaryData + ): Transaction; +} +/** + */ +declare export class TransactionBatch { + free(): void; /** - * @param {string} json - * @returns {UnitInterval} + * @returns {number} */ - static from_json(json: string): UnitInterval; + len(): number; /** - * @returns {BigNum} + * @param {number} index + * @returns {Transaction} */ - numerator(): BigNum; + get(index: number): Transaction; +} +/** + */ +declare export class TransactionBatchList { + free(): void; /** - * @returns {BigNum} + * @returns {number} */ - denominator(): BigNum; + len(): number; /** - * @param {BigNum} numerator - * @param {BigNum} denominator - * @returns {UnitInterval} + * @param {number} index + * @returns {TransactionBatch} */ - static new(numerator: BigNum, denominator: BigNum): UnitInterval; + get(index: number): TransactionBatch; } /** */ -declare export class Update { +declare export class TransactionBodies { free(): void; /** @@ -10092,9 +10512,9 @@ declare export class Update { /** * @param {Uint8Array} bytes - * @returns {Update} + * @returns {TransactionBodies} */ - static from_bytes(bytes: Uint8Array): Update; + static from_bytes(bytes: Uint8Array): TransactionBodies; /** * @returns {string} @@ -10103,9 +10523,9 @@ declare export class Update { /** * @param {string} hex_str - * @returns {Update} + * @returns {TransactionBodies} */ - static from_hex(hex_str: string): Update; + static from_hex(hex_str: string): TransactionBodies; /** * @returns {string} @@ -10113,39 +10533,40 @@ declare export class Update { to_json(): string; /** - * @returns {UpdateJSON} + * @returns {TransactionBodiesJSON} */ - to_js_value(): UpdateJSON; + to_js_value(): TransactionBodiesJSON; /** * @param {string} json - * @returns {Update} + * @returns {TransactionBodies} */ - static from_json(json: string): Update; + static from_json(json: string): TransactionBodies; /** - * @returns {ProposedProtocolParameterUpdates} + * @returns {TransactionBodies} */ - proposed_protocol_parameter_updates(): ProposedProtocolParameterUpdates; + static new(): TransactionBodies; /** * @returns {number} */ - epoch(): number; + len(): number; /** - * @param {ProposedProtocolParameterUpdates} proposed_protocol_parameter_updates - * @param {number} epoch - * @returns {Update} + * @param {number} index + * @returns {TransactionBody} */ - static new( - proposed_protocol_parameter_updates: ProposedProtocolParameterUpdates, - epoch: number - ): Update; + get(index: number): TransactionBody; + + /** + * @param {TransactionBody} elem + */ + add(elem: TransactionBody): void; } /** */ -declare export class VRFCert { +declare export class TransactionBody { free(): void; /** @@ -10155,9 +10576,9 @@ declare export class VRFCert { /** * @param {Uint8Array} bytes - * @returns {VRFCert} + * @returns {TransactionBody} */ - static from_bytes(bytes: Uint8Array): VRFCert; + static from_bytes(bytes: Uint8Array): TransactionBody; /** * @returns {string} @@ -10166,9 +10587,9 @@ declare export class VRFCert { /** * @param {string} hex_str - * @returns {VRFCert} + * @returns {TransactionBody} */ - static from_hex(hex_str: string): VRFCert; + static from_hex(hex_str: string): TransactionBody; /** * @returns {string} @@ -10176,1062 +10597,4638 @@ declare export class VRFCert { to_json(): string; /** - * @returns {VRFCertJSON} + * @returns {TransactionBodyJSON} */ - to_js_value(): VRFCertJSON; + to_js_value(): TransactionBodyJSON; /** * @param {string} json - * @returns {VRFCert} + * @returns {TransactionBody} */ - static from_json(json: string): VRFCert; + static from_json(json: string): TransactionBody; /** - * @returns {Uint8Array} + * @returns {TransactionInputs} */ - output(): Uint8Array; + inputs(): TransactionInputs; /** - * @returns {Uint8Array} + * @returns {TransactionOutputs} */ - proof(): Uint8Array; + outputs(): TransactionOutputs; /** - * @param {Uint8Array} output - * @param {Uint8Array} proof - * @returns {VRFCert} + * @returns {BigNum} */ - static new(output: Uint8Array, proof: Uint8Array): VRFCert; -} -/** - */ -declare export class VRFKeyHash { - free(): void; + fee(): BigNum; /** - * @param {Uint8Array} bytes - * @returns {VRFKeyHash} + * !!! DEPRECATED !!! + * Returns a Slot32 (u32) value in case the underlying original BigNum (u64) value is within the limits. + * Otherwise will just raise an error. + * @returns {number | void} */ - static from_bytes(bytes: Uint8Array): VRFKeyHash; + ttl(): number | void; /** - * @returns {Uint8Array} + * @returns {BigNum | void} */ - to_bytes(): Uint8Array; + ttl_bignum(): BigNum | void; /** - * @param {string} prefix - * @returns {string} + * @param {BigNum} ttl */ - to_bech32(prefix: string): string; + set_ttl(ttl: BigNum): void; /** - * @param {string} bech_str - * @returns {VRFKeyHash} */ - static from_bech32(bech_str: string): VRFKeyHash; + remove_ttl(): void; /** - * @returns {string} + * @param {Certificates} certs */ - to_hex(): string; + set_certs(certs: Certificates): void; /** - * @param {string} hex - * @returns {VRFKeyHash} + * @returns {Certificates | void} */ - static from_hex(hex: string): VRFKeyHash; -} -/** - */ -declare export class VRFVKey { - free(): void; + certs(): Certificates | void; /** - * @param {Uint8Array} bytes - * @returns {VRFVKey} + * @param {Withdrawals} withdrawals */ - static from_bytes(bytes: Uint8Array): VRFVKey; + set_withdrawals(withdrawals: Withdrawals): void; /** - * @returns {Uint8Array} + * @returns {Withdrawals | void} */ - to_bytes(): Uint8Array; + withdrawals(): Withdrawals | void; /** - * @param {string} prefix - * @returns {string} + * @param {Update} update */ - to_bech32(prefix: string): string; + set_update(update: Update): void; /** - * @param {string} bech_str - * @returns {VRFVKey} + * @returns {Update | void} */ - static from_bech32(bech_str: string): VRFVKey; + update(): Update | void; /** - * @returns {string} + * @param {AuxiliaryDataHash} auxiliary_data_hash */ - to_hex(): string; + set_auxiliary_data_hash(auxiliary_data_hash: AuxiliaryDataHash): void; /** - * @param {string} hex - * @returns {VRFVKey} + * @returns {AuxiliaryDataHash | void} */ - static from_hex(hex: string): VRFVKey; -} -/** - */ -declare export class Value { - free(): void; + auxiliary_data_hash(): AuxiliaryDataHash | void; /** - * @returns {Uint8Array} + * !!! DEPRECATED !!! + * Uses outdated slot number format. + * @param {number} validity_start_interval */ - to_bytes(): Uint8Array; + set_validity_start_interval(validity_start_interval: number): void; /** - * @param {Uint8Array} bytes - * @returns {Value} + * @param {BigNum} validity_start_interval */ - static from_bytes(bytes: Uint8Array): Value; + set_validity_start_interval_bignum(validity_start_interval: BigNum): void; /** - * @returns {string} + * @returns {BigNum | void} */ - to_hex(): string; + validity_start_interval_bignum(): BigNum | void; /** - * @param {string} hex_str - * @returns {Value} + * !!! DEPRECATED !!! + * Returns a Option (u32) value in case the underlying original Option (u64) value is within the limits. + * Otherwise will just raise an error. + * Use `.validity_start_interval_bignum` instead. + * @returns {number | void} */ - static from_hex(hex_str: string): Value; + validity_start_interval(): number | void; /** - * @returns {string} + * @param {Mint} mint */ - to_json(): string; + set_mint(mint: Mint): void; /** - * @returns {ValueJSON} + * @returns {Mint | void} */ - to_js_value(): ValueJSON; + mint(): Mint | void; /** - * @param {string} json - * @returns {Value} + * @param {TransactionInputs} reference_inputs */ - static from_json(json: string): Value; + set_reference_inputs(reference_inputs: TransactionInputs): void; /** - * @param {BigNum} coin - * @returns {Value} + * @returns {TransactionInputs | void} */ - static new(coin: BigNum): Value; + reference_inputs(): TransactionInputs | void; /** - * @param {MultiAsset} multiasset - * @returns {Value} + * @param {ScriptDataHash} script_data_hash */ - static new_from_assets(multiasset: MultiAsset): Value; + set_script_data_hash(script_data_hash: ScriptDataHash): void; /** - * @param {BigNum} coin - * @param {MultiAsset} multiasset - * @returns {Value} + * @returns {ScriptDataHash | void} */ - static new_with_assets(coin: BigNum, multiasset: MultiAsset): Value; + script_data_hash(): ScriptDataHash | void; /** - * @returns {Value} + * @param {TransactionInputs} collateral */ - static zero(): Value; + set_collateral(collateral: TransactionInputs): void; /** - * @returns {boolean} + * @returns {TransactionInputs | void} */ - is_zero(): boolean; + collateral(): TransactionInputs | void; /** - * @returns {BigNum} + * @param {Ed25519KeyHashes} required_signers */ - coin(): BigNum; + set_required_signers(required_signers: Ed25519KeyHashes): void; /** - * @param {BigNum} coin + * @returns {Ed25519KeyHashes | void} */ - set_coin(coin: BigNum): void; + required_signers(): Ed25519KeyHashes | void; /** - * @returns {MultiAsset | void} + * @param {NetworkId} network_id */ - multiasset(): MultiAsset | void; + set_network_id(network_id: NetworkId): void; /** - * @param {MultiAsset} multiasset + * @returns {NetworkId | void} */ - set_multiasset(multiasset: MultiAsset): void; + network_id(): NetworkId | void; /** - * @param {Value} rhs - * @returns {Value} + * @param {TransactionOutput} collateral_return */ - checked_add(rhs: Value): Value; + set_collateral_return(collateral_return: TransactionOutput): void; /** - * @param {Value} rhs_value - * @returns {Value} + * @returns {TransactionOutput | void} */ - checked_sub(rhs_value: Value): Value; + collateral_return(): TransactionOutput | void; /** - * @param {Value} rhs_value - * @returns {Value} + * @param {BigNum} total_collateral */ - clamped_sub(rhs_value: Value): Value; + set_total_collateral(total_collateral: BigNum): void; /** - * note: values are only partially comparable - * @param {Value} rhs_value - * @returns {number | void} + * @returns {BigNum | void} */ - compare(rhs_value: Value): number | void; -} -/** - */ -declare export class Vkey { - free(): void; + total_collateral(): BigNum | void; /** - * @returns {Uint8Array} + * @param {VotingProcedures} voting_procedures */ - to_bytes(): Uint8Array; + set_voting_procedures(voting_procedures: VotingProcedures): void; /** - * @param {Uint8Array} bytes - * @returns {Vkey} + * @returns {VotingProcedures | void} */ - static from_bytes(bytes: Uint8Array): Vkey; + voting_procedures(): VotingProcedures | void; /** - * @returns {string} + * @param {VotingProposals} voting_proposals */ - to_hex(): string; + set_voting_proposals(voting_proposals: VotingProposals): void; /** - * @param {string} hex_str - * @returns {Vkey} + * @returns {VotingProposals | void} */ - static from_hex(hex_str: string): Vkey; + voting_proposals(): VotingProposals | void; /** - * @returns {string} + * @param {BigNum} donation */ - to_json(): string; + set_donation(donation: BigNum): void; /** - * @returns {VkeyJSON} + * @returns {BigNum | void} */ - to_js_value(): VkeyJSON; + donation(): BigNum | void; /** - * @param {string} json - * @returns {Vkey} + * @param {BigNum} current_treasury_value */ - static from_json(json: string): Vkey; + set_current_treasury_value(current_treasury_value: BigNum): void; /** - * @param {PublicKey} pk - * @returns {Vkey} + * @returns {BigNum | void} */ - static new(pk: PublicKey): Vkey; + current_treasury_value(): BigNum | void; /** - * @returns {PublicKey} - */ - public_key(): PublicKey; -} -/** - */ -declare export class Vkeys { - free(): void; - - /** - * @returns {Vkeys} - */ - static new(): Vkeys; - - /** - * @returns {number} - */ - len(): number; - - /** - * @param {number} index - * @returns {Vkey} + * !!! DEPRECATED !!! + * This constructor uses outdated slot number format for the ttl value. + * Use `.new_tx_body` and then `.set_ttl` instead + * @param {TransactionInputs} inputs + * @param {TransactionOutputs} outputs + * @param {BigNum} fee + * @param {number | void} [ttl] + * @returns {TransactionBody} */ - get(index: number): Vkey; + static new( + inputs: TransactionInputs, + outputs: TransactionOutputs, + fee: BigNum, + ttl?: number + ): TransactionBody; /** - * @param {Vkey} elem + * Returns a new TransactionBody. + * In the new version of "new" we removed optional ttl for support it by wasm_bingen. + * Your can use "set_ttl" and "remove_ttl" to set a new value for ttl or set it as None. + * @param {TransactionInputs} inputs + * @param {TransactionOutputs} outputs + * @param {BigNum} fee + * @returns {TransactionBody} */ - add(elem: Vkey): void; + static new_tx_body( + inputs: TransactionInputs, + outputs: TransactionOutputs, + fee: BigNum + ): TransactionBody; } /** */ -declare export class Vkeywitness { +declare export class TransactionBuilder { free(): void; /** - * @returns {Uint8Array} - */ - to_bytes(): Uint8Array; + * This automatically selects and adds inputs from {inputs} consisting of just enough to cover + * the outputs that have already been added. + * This should be called after adding all certs/outputs/etc and will be an error otherwise. + * Uses CIP2: https://github.com/cardano-foundation/CIPs/blob/master/CIP-0002/CIP-0002.md + * Adding a change output must be called after via TransactionBuilder::add_change_if_needed() + * This function, diverging from CIP2, takes into account fees and will attempt to add additional + * inputs to cover the minimum fees. This does not, however, set the txbuilder's fee. + * @param {TransactionUnspentOutputs} inputs + * @param {$Values< + typeof + CoinSelectionStrategyCIP2>} strategy + */ + add_inputs_from( + inputs: TransactionUnspentOutputs, + strategy: $Values + ): void; /** - * @param {Uint8Array} bytes - * @returns {Vkeywitness} + * @param {TxInputsBuilder} inputs */ - static from_bytes(bytes: Uint8Array): Vkeywitness; + set_inputs(inputs: TxInputsBuilder): void; /** - * @returns {string} + * @param {TxInputsBuilder} collateral */ - to_hex(): string; + set_collateral(collateral: TxInputsBuilder): void; /** - * @param {string} hex_str - * @returns {Vkeywitness} + * @param {TransactionOutput} collateral_return */ - static from_hex(hex_str: string): Vkeywitness; + set_collateral_return(collateral_return: TransactionOutput): void; /** - * @returns {string} */ - to_json(): string; + remove_collateral_return(): void; /** - * @returns {VkeywitnessJSON} + * This function will set the collateral-return value and then auto-calculate and assign + * the total collateral coin value. Will raise an error in case no collateral inputs are set + * or in case the total collateral value will have any assets in it except coin. + * @param {TransactionOutput} collateral_return */ - to_js_value(): VkeywitnessJSON; + set_collateral_return_and_total(collateral_return: TransactionOutput): void; /** - * @param {string} json - * @returns {Vkeywitness} + * @param {BigNum} total_collateral */ - static from_json(json: string): Vkeywitness; + set_total_collateral(total_collateral: BigNum): void; /** - * @param {Vkey} vkey - * @param {Ed25519Signature} signature - * @returns {Vkeywitness} */ - static new(vkey: Vkey, signature: Ed25519Signature): Vkeywitness; + remove_total_collateral(): void; /** - * @returns {Vkey} + * This function will set the total-collateral coin and then auto-calculate and assign + * the collateral return value. Will raise an error in case no collateral inputs are set. + * The specified address will be the received of the collateral return + * @param {BigNum} total_collateral + * @param {Address} return_address */ - vkey(): Vkey; + set_total_collateral_and_return( + total_collateral: BigNum, + return_address: Address + ): void; /** - * @returns {Ed25519Signature} + * @param {TransactionInput} reference_input */ - signature(): Ed25519Signature; -} -/** - */ -declare export class Vkeywitnesses { - free(): void; + add_reference_input(reference_input: TransactionInput): void; /** - * @returns {Uint8Array} + * @param {TransactionInput} reference_input + * @param {number} script_size */ - to_bytes(): Uint8Array; + add_script_reference_input( + reference_input: TransactionInput, + script_size: number + ): void; /** - * @param {Uint8Array} bytes - * @returns {Vkeywitnesses} + * We have to know what kind of inputs these are to know what kind of mock witnesses to create since + * 1) mock witnesses have different lengths depending on the type which changes the expecting fee + * 2) Witnesses are a set so we need to get rid of duplicates to avoid over-estimating the fee + * @param {Ed25519KeyHash} hash + * @param {TransactionInput} input + * @param {Value} amount */ - static from_bytes(bytes: Uint8Array): Vkeywitnesses; + add_key_input( + hash: Ed25519KeyHash, + input: TransactionInput, + amount: Value + ): void; /** - * @returns {string} + * This method will add the input to the builder and also register the required native script witness + * @param {NativeScript} script + * @param {TransactionInput} input + * @param {Value} amount */ - to_hex(): string; + add_native_script_input( + script: NativeScript, + input: TransactionInput, + amount: Value + ): void; /** - * @param {string} hex_str - * @returns {Vkeywitnesses} + * This method will add the input to the builder and also register the required plutus witness + * @param {PlutusWitness} witness + * @param {TransactionInput} input + * @param {Value} amount */ - static from_hex(hex_str: string): Vkeywitnesses; + add_plutus_script_input( + witness: PlutusWitness, + input: TransactionInput, + amount: Value + ): void; /** - * @returns {string} + * @param {ByronAddress} hash + * @param {TransactionInput} input + * @param {Value} amount */ - to_json(): string; + add_bootstrap_input( + hash: ByronAddress, + input: TransactionInput, + amount: Value + ): void; /** - * @returns {VkeywitnessesJSON} + * This function is replace for previous one add_input. + * The functions adds a non script input, if it is a script input it returns an error. + * To add script input you need to use add_native_script_input or add_plutus_script_input. + * Also we recommend to use TxInputsBuilder and .set_inputs, because all add_*_input functions might be removed from transaction builder. + * @param {Address} address + * @param {TransactionInput} input + * @param {Value} amount */ - to_js_value(): VkeywitnessesJSON; + add_regular_input( + address: Address, + input: TransactionInput, + amount: Value + ): void; /** - * @param {string} json - * @returns {Vkeywitnesses} - */ - static from_json(json: string): Vkeywitnesses; + * @param {TransactionUnspentOutputs} inputs + * @param {$Values< + typeof + CoinSelectionStrategyCIP2>} strategy + * @param {ChangeConfig} change_config + * @returns {boolean} + */ + add_inputs_from_and_change( + inputs: TransactionUnspentOutputs, + strategy: $Values, + change_config: ChangeConfig + ): boolean; /** - * @returns {Vkeywitnesses} + * @param {TransactionUnspentOutputs} inputs + * @param {$Values< + typeof + CoinSelectionStrategyCIP2>} strategy + * @param {ChangeConfig} change_config + * @param {BigNum} collateral_percentage + */ + add_inputs_from_and_change_with_collateral_return( + inputs: TransactionUnspentOutputs, + strategy: $Values, + change_config: ChangeConfig, + collateral_percentage: BigNum + ): void; + + /** + * Returns a copy of the current script input witness scripts in the builder + * @returns {NativeScripts | void} */ - static new(): Vkeywitnesses; + get_native_input_scripts(): NativeScripts | void; /** - * @returns {number} + * Returns a copy of the current plutus input witness scripts in the builder. + * NOTE: each plutus witness will be cloned with a specific corresponding input index + * @returns {PlutusWitnesses | void} */ - len(): number; + get_plutus_input_scripts(): PlutusWitnesses | void; /** - * @param {number} index - * @returns {Vkeywitness} + * calculates how much the fee would increase if you added a given output + * @param {Address} address + * @param {TransactionInput} input + * @param {Value} amount + * @returns {BigNum} */ - get(index: number): Vkeywitness; + fee_for_input( + address: Address, + input: TransactionInput, + amount: Value + ): BigNum; /** - * @param {Vkeywitness} elem + * Add explicit output via a TransactionOutput object + * @param {TransactionOutput} output */ - add(elem: Vkeywitness): void; -} -/** - */ -declare export class Withdrawals { - free(): void; + add_output(output: TransactionOutput): void; /** - * @returns {Uint8Array} + * calculates how much the fee would increase if you added a given output + * @param {TransactionOutput} output + * @returns {BigNum} */ - to_bytes(): Uint8Array; + fee_for_output(output: TransactionOutput): BigNum; /** - * @param {Uint8Array} bytes - * @returns {Withdrawals} + * @param {BigNum} fee */ - static from_bytes(bytes: Uint8Array): Withdrawals; + set_fee(fee: BigNum): void; /** - * @returns {string} + * !!! DEPRECATED !!! + * Set ttl value. + * @param {number} ttl */ - to_hex(): string; + set_ttl(ttl: number): void; /** - * @param {string} hex_str - * @returns {Withdrawals} + * @param {BigNum} ttl */ - static from_hex(hex_str: string): Withdrawals; + set_ttl_bignum(ttl: BigNum): void; /** - * @returns {string} */ - to_json(): string; + remove_ttl(): void; /** - * @returns {WithdrawalsJSON} + * !!! DEPRECATED !!! + * Uses outdated slot number format. + * @param {number} validity_start_interval */ - to_js_value(): WithdrawalsJSON; + set_validity_start_interval(validity_start_interval: number): void; /** - * @param {string} json - * @returns {Withdrawals} + * @param {BigNum} validity_start_interval */ - static from_json(json: string): Withdrawals; + set_validity_start_interval_bignum(validity_start_interval: BigNum): void; /** - * @returns {Withdrawals} */ - static new(): Withdrawals; + remove_validity_start_interval(): void; /** - * @returns {number} + * !!! DEPRECATED !!! + * Can emit error if add a cert with script credential. + * Use set_certs_builder instead. + * @param {Certificates} certs */ - len(): number; + set_certs(certs: Certificates): void; /** - * @param {RewardAddress} key - * @param {BigNum} value - * @returns {BigNum | void} */ - insert(key: RewardAddress, value: BigNum): BigNum | void; + remove_certs(): void; + + /** + * @param {CertificatesBuilder} certs + */ + set_certs_builder(certs: CertificatesBuilder): void; + + /** + * !!! DEPRECATED !!! + * Can emit error if add a withdrawal with script credential. + * Use set_withdrawals_builder instead. + * @param {Withdrawals} withdrawals + */ + set_withdrawals(withdrawals: Withdrawals): void; + + /** + * @param {WithdrawalsBuilder} withdrawals + */ + set_withdrawals_builder(withdrawals: WithdrawalsBuilder): void; + + /** + * @param {VotingBuilder} voting_builder + */ + set_voting_builder(voting_builder: VotingBuilder): void; + + /** + * @param {VotingProposalBuilder} voting_proposal_builder + */ + set_voting_proposal_builder( + voting_proposal_builder: VotingProposalBuilder + ): void; + + /** + */ + remove_withdrawals(): void; + + /** + * @returns {AuxiliaryData | void} + */ + get_auxiliary_data(): AuxiliaryData | void; + + /** + * Set explicit auxiliary data via an AuxiliaryData object + * It might contain some metadata plus native or Plutus scripts + * @param {AuxiliaryData} auxiliary_data + */ + set_auxiliary_data(auxiliary_data: AuxiliaryData): void; + + /** + */ + remove_auxiliary_data(): void; + + /** + * Set metadata using a GeneralTransactionMetadata object + * It will be set to the existing or new auxiliary data in this builder + * @param {GeneralTransactionMetadata} metadata + */ + set_metadata(metadata: GeneralTransactionMetadata): void; + + /** + * Add a single metadatum using TransactionMetadatumLabel and TransactionMetadatum objects + * It will be securely added to existing or new metadata in this builder + * @param {BigNum} key + * @param {TransactionMetadatum} val + */ + add_metadatum(key: BigNum, val: TransactionMetadatum): void; + + /** + * Add a single JSON metadatum using a TransactionMetadatumLabel and a String + * It will be securely added to existing or new metadata in this builder + * @param {BigNum} key + * @param {string} val + */ + add_json_metadatum(key: BigNum, val: string): void; + + /** + * Add a single JSON metadatum using a TransactionMetadatumLabel, a String, and a MetadataJsonSchema object + * It will be securely added to existing or new metadata in this builder + * @param {BigNum} key + * @param {string} val + * @param {$Values< + typeof + MetadataJsonSchema>} schema + */ + add_json_metadatum_with_schema( + key: BigNum, + val: string, + schema: $Values + ): void; + + /** + * @param {MintBuilder} mint_builder + */ + set_mint_builder(mint_builder: MintBuilder): void; + + /** + */ + remove_mint_builder(): void; + + /** + * @returns {MintBuilder | void} + */ + get_mint_builder(): MintBuilder | void; + + /** + * !!! DEPRECATED !!! + * Mints are defining by MintBuilder now. + * Use `.set_mint_builder()` and `MintBuilder` instead. + * Set explicit Mint object and the required witnesses to this builder + * it will replace any previously existing mint and mint scripts + * NOTE! Error will be returned in case a mint policy does not have a matching script + * @param {Mint} mint + * @param {NativeScripts} mint_scripts + */ + set_mint(mint: Mint, mint_scripts: NativeScripts): void; + + /** + * !!! DEPRECATED !!! + * Mints are defining by MintBuilder now. + * Use `.get_mint_builder()` and `.build()` instead. + * Returns a copy of the current mint state in the builder + * @returns {Mint | void} + */ + get_mint(): Mint | void; + + /** + * Returns a copy of the current mint witness scripts in the builder + * @returns {NativeScripts | void} + */ + get_mint_scripts(): NativeScripts | void; + + /** + * !!! DEPRECATED !!! + * Mints are defining by MintBuilder now. + * Use `.set_mint_builder()` and `MintBuilder` instead. + * Add a mint entry to this builder using a PolicyID and MintAssets object + * It will be securely added to existing or new Mint in this builder + * It will replace any existing mint assets with the same PolicyID + * @param {NativeScript} policy_script + * @param {MintAssets} mint_assets + */ + set_mint_asset(policy_script: NativeScript, mint_assets: MintAssets): void; + + /** + * !!! DEPRECATED !!! + * Mints are defining by MintBuilder now. + * Use `.set_mint_builder()` and `MintBuilder` instead. + * Add a mint entry to this builder using a PolicyID, AssetName, and Int object for amount + * It will be securely added to existing or new Mint in this builder + * It will replace any previous existing amount same PolicyID and AssetName + * @param {NativeScript} policy_script + * @param {AssetName} asset_name + * @param {Int} amount + */ + add_mint_asset( + policy_script: NativeScript, + asset_name: AssetName, + amount: Int + ): void; + + /** + * Add a mint entry together with an output to this builder + * Using a PolicyID, AssetName, Int for amount, Address, and Coin (BigNum) objects + * The asset will be securely added to existing or new Mint in this builder + * A new output will be added with the specified Address, the Coin value, and the minted asset + * @param {NativeScript} policy_script + * @param {AssetName} asset_name + * @param {Int} amount + * @param {TransactionOutputAmountBuilder} output_builder + * @param {BigNum} output_coin + */ + add_mint_asset_and_output( + policy_script: NativeScript, + asset_name: AssetName, + amount: Int, + output_builder: TransactionOutputAmountBuilder, + output_coin: BigNum + ): void; + + /** + * Add a mint entry together with an output to this builder + * Using a PolicyID, AssetName, Int for amount, and Address objects + * The asset will be securely added to existing or new Mint in this builder + * A new output will be added with the specified Address and the minted asset + * The output will be set to contain the minimum required amount of Coin + * @param {NativeScript} policy_script + * @param {AssetName} asset_name + * @param {Int} amount + * @param {TransactionOutputAmountBuilder} output_builder + */ + add_mint_asset_and_output_min_required_coin( + policy_script: NativeScript, + asset_name: AssetName, + amount: Int, + output_builder: TransactionOutputAmountBuilder + ): void; + + /** + * @param {PlutusData} datum + */ + add_extra_witness_datum(datum: PlutusData): void; + + /** + * @returns {PlutusList | void} + */ + get_extra_witness_datums(): PlutusList | void; + + /** + * @param {BigNum} donation + */ + set_donation(donation: BigNum): void; /** - * @param {RewardAddress} key * @returns {BigNum | void} */ - get(key: RewardAddress): BigNum | void; + get_donation(): BigNum | void; /** - * @returns {RewardAddresses} + * @param {BigNum} current_treasury_value */ - keys(): RewardAddresses; + set_current_treasury_value(current_treasury_value: BigNum): void; + + /** + * @returns {BigNum | void} + */ + get_current_treasury_value(): BigNum | void; + + /** + * @param {TransactionBuilderConfig} cfg + * @returns {TransactionBuilder} + */ + static new(cfg: TransactionBuilderConfig): TransactionBuilder; + + /** + * @returns {TransactionInputs} + */ + get_reference_inputs(): TransactionInputs; + + /** + * does not include refunds or withdrawals + * @returns {Value} + */ + get_explicit_input(): Value; + + /** + * withdrawals and refunds + * @returns {Value} + */ + get_implicit_input(): Value; + + /** + * Return explicit input plus implicit input plus mint + * @returns {Value} + */ + get_total_input(): Value; + + /** + * Return explicit output plus deposit plus burn + * @returns {Value} + */ + get_total_output(): Value; + + /** + * does not include fee + * @returns {Value} + */ + get_explicit_output(): Value; + + /** + * @returns {BigNum} + */ + get_deposit(): BigNum; + + /** + * @returns {BigNum | void} + */ + get_fee_if_set(): BigNum | void; + + /** + * Warning: this function will mutate the /fee/ field + * Make sure to call this function last after setting all other tx-body properties + * Editing inputs, outputs, mint, etc. after change been calculated + * might cause a mismatch in calculated fee versus the required fee + * @param {Address} address + * @returns {boolean} + */ + add_change_if_needed(address: Address): boolean; + + /** + * @param {Address} address + * @param {OutputDatum} plutus_data + * @returns {boolean} + */ + add_change_if_needed_with_datum( + address: Address, + plutus_data: OutputDatum + ): boolean; + + /** + * This method will calculate the script hash data + * using the plutus datums and redeemers already present in the builder + * along with the provided cost model, and will register the calculated value + * in the builder to be used when building the tx body. + * In case there are no plutus input witnesses present - nothing will change + * You can set specific hash value using `.set_script_data_hash` + * NOTE: this function will check which language versions are used in the present scripts + * and will assert and require for a corresponding cost-model to be present in the passed map. + * Only the cost-models for the present language versions will be used in the hash calculation. + * @param {Costmdls} cost_models + */ + calc_script_data_hash(cost_models: Costmdls): void; + + /** + * Sets the specified hash value. + * Alternatively you can use `.calc_script_data_hash` to calculate the hash automatically. + * Or use `.remove_script_data_hash` to delete the previously set value + * @param {ScriptDataHash} hash + */ + set_script_data_hash(hash: ScriptDataHash): void; + + /** + * Deletes any previously set plutus data hash value. + * Use `.set_script_data_hash` or `.calc_script_data_hash` to set it. + */ + remove_script_data_hash(): void; + + /** + * @param {Ed25519KeyHash} key + */ + add_required_signer(key: Ed25519KeyHash): void; + + /** + * @returns {number} + */ + full_size(): number; + + /** + * @returns {Uint32Array} + */ + output_sizes(): Uint32Array; + + /** + * Returns object the body of the new transaction + * Auxiliary data itself is not included + * You can use `get_auxiliary_data` or `build_tx` + * @returns {TransactionBody} + */ + build(): TransactionBody; + + /** + * Returns full Transaction object with the body and the auxiliary data + * NOTE: witness_set will contain all mint_scripts if any been added or set + * NOTE: is_valid set to true + * NOTE: Will fail in case there are any script inputs added with no corresponding witness + * @returns {Transaction} + */ + build_tx(): Transaction; + + /** + * Similar to `.build_tx()` but will NOT fail in case there are missing script witnesses + * @returns {Transaction} + */ + build_tx_unsafe(): Transaction; + + /** + * warning: sum of all parts of a transaction must equal 0. You cannot just set the fee to the min value and forget about it + * warning: min_fee may be slightly larger than the actual minimum fee (ex: a few lovelaces) + * this is done to simplify the library code, but can be fixed later + * @returns {BigNum} + */ + min_fee(): BigNum; +} +/** + */ +declare export class TransactionBuilderConfig { + free(): void; +} +/** + */ +declare export class TransactionBuilderConfigBuilder { + free(): void; + + /** + * @returns {TransactionBuilderConfigBuilder} + */ + static new(): TransactionBuilderConfigBuilder; + + /** + * @param {LinearFee} fee_algo + * @returns {TransactionBuilderConfigBuilder} + */ + fee_algo(fee_algo: LinearFee): TransactionBuilderConfigBuilder; + + /** + * @param {BigNum} coins_per_utxo_byte + * @returns {TransactionBuilderConfigBuilder} + */ + coins_per_utxo_byte( + coins_per_utxo_byte: BigNum + ): TransactionBuilderConfigBuilder; + + /** + * @param {ExUnitPrices} ex_unit_prices + * @returns {TransactionBuilderConfigBuilder} + */ + ex_unit_prices(ex_unit_prices: ExUnitPrices): TransactionBuilderConfigBuilder; + + /** + * @param {BigNum} pool_deposit + * @returns {TransactionBuilderConfigBuilder} + */ + pool_deposit(pool_deposit: BigNum): TransactionBuilderConfigBuilder; + + /** + * @param {BigNum} key_deposit + * @returns {TransactionBuilderConfigBuilder} + */ + key_deposit(key_deposit: BigNum): TransactionBuilderConfigBuilder; + + /** + * @param {number} max_value_size + * @returns {TransactionBuilderConfigBuilder} + */ + max_value_size(max_value_size: number): TransactionBuilderConfigBuilder; + + /** + * @param {number} max_tx_size + * @returns {TransactionBuilderConfigBuilder} + */ + max_tx_size(max_tx_size: number): TransactionBuilderConfigBuilder; + + /** + * @param {UnitInterval} ref_script_coins_per_byte + * @returns {TransactionBuilderConfigBuilder} + */ + ref_script_coins_per_byte( + ref_script_coins_per_byte: UnitInterval + ): TransactionBuilderConfigBuilder; + + /** + * @param {boolean} prefer_pure_change + * @returns {TransactionBuilderConfigBuilder} + */ + prefer_pure_change( + prefer_pure_change: boolean + ): TransactionBuilderConfigBuilder; + + /** + * Removes a ref input (that was set via set_reference_inputs) if the ref inputs was presented in regular tx inputs + * @param {boolean} deduplicate_explicit_ref_inputs_with_regular_inputs + * @returns {TransactionBuilderConfigBuilder} + */ + deduplicate_explicit_ref_inputs_with_regular_inputs( + deduplicate_explicit_ref_inputs_with_regular_inputs: boolean + ): TransactionBuilderConfigBuilder; + + /** + * @returns {TransactionBuilderConfig} + */ + build(): TransactionBuilderConfig; +} +/** + */ +declare export class TransactionHash { + free(): void; + + /** + * @param {Uint8Array} bytes + * @returns {TransactionHash} + */ + static from_bytes(bytes: Uint8Array): TransactionHash; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {string} prefix + * @returns {string} + */ + to_bech32(prefix: string): string; + + /** + * @param {string} bech_str + * @returns {TransactionHash} + */ + static from_bech32(bech_str: string): TransactionHash; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex + * @returns {TransactionHash} + */ + static from_hex(hex: string): TransactionHash; +} +/** + */ +declare export class TransactionInput { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {TransactionInput} + */ + static from_bytes(bytes: Uint8Array): TransactionInput; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {TransactionInput} + */ + static from_hex(hex_str: string): TransactionInput; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {TransactionInputJSON} + */ + to_js_value(): TransactionInputJSON; + + /** + * @param {string} json + * @returns {TransactionInput} + */ + static from_json(json: string): TransactionInput; + + /** + * @returns {TransactionHash} + */ + transaction_id(): TransactionHash; + + /** + * @returns {number} + */ + index(): number; + + /** + * @param {TransactionHash} transaction_id + * @param {number} index + * @returns {TransactionInput} + */ + static new(transaction_id: TransactionHash, index: number): TransactionInput; +} +/** + */ +declare export class TransactionInputs { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {TransactionInputs} + */ + static from_bytes(bytes: Uint8Array): TransactionInputs; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {TransactionInputs} + */ + static from_hex(hex_str: string): TransactionInputs; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {TransactionInputsJSON} + */ + to_js_value(): TransactionInputsJSON; + + /** + * @param {string} json + * @returns {TransactionInputs} + */ + static from_json(json: string): TransactionInputs; + + /** + * @returns {TransactionInputs} + */ + static new(): TransactionInputs; + + /** + * @returns {number} + */ + len(): number; + + /** + * @param {number} index + * @returns {TransactionInput} + */ + get(index: number): TransactionInput; + + /** + * Add a new `TransactionInput` to the set. + * Returns `true` if the element was not already present in the set. + * Note that the `TransactionInput` is added to the set only if it is not already present. + * @param {TransactionInput} elem + * @returns {boolean} + */ + add(elem: TransactionInput): boolean; + + /** + * @returns {TransactionInputs | void} + */ + to_option(): TransactionInputs | void; +} +/** + */ +declare export class TransactionMetadatum { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {TransactionMetadatum} + */ + static from_bytes(bytes: Uint8Array): TransactionMetadatum; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {TransactionMetadatum} + */ + static from_hex(hex_str: string): TransactionMetadatum; + + /** + * @param {MetadataMap} map + * @returns {TransactionMetadatum} + */ + static new_map(map: MetadataMap): TransactionMetadatum; + + /** + * @param {MetadataList} list + * @returns {TransactionMetadatum} + */ + static new_list(list: MetadataList): TransactionMetadatum; + + /** + * @param {Int} int + * @returns {TransactionMetadatum} + */ + static new_int(int: Int): TransactionMetadatum; + + /** + * @param {Uint8Array} bytes + * @returns {TransactionMetadatum} + */ + static new_bytes(bytes: Uint8Array): TransactionMetadatum; + + /** + * @param {string} text + * @returns {TransactionMetadatum} + */ + static new_text(text: string): TransactionMetadatum; + + /** + * @returns {$Values< + typeof + TransactionMetadatumKind>} + */ + kind(): $Values; + + /** + * @returns {MetadataMap} + */ + as_map(): MetadataMap; + + /** + * @returns {MetadataList} + */ + as_list(): MetadataList; + + /** + * @returns {Int} + */ + as_int(): Int; + + /** + * @returns {Uint8Array} + */ + as_bytes(): Uint8Array; + + /** + * @returns {string} + */ + as_text(): string; +} +/** + */ +declare export class TransactionMetadatumLabels { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {TransactionMetadatumLabels} + */ + static from_bytes(bytes: Uint8Array): TransactionMetadatumLabels; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {TransactionMetadatumLabels} + */ + static from_hex(hex_str: string): TransactionMetadatumLabels; + + /** + * @returns {TransactionMetadatumLabels} + */ + static new(): TransactionMetadatumLabels; + + /** + * @returns {number} + */ + len(): number; + + /** + * @param {number} index + * @returns {BigNum} + */ + get(index: number): BigNum; + + /** + * @param {BigNum} elem + */ + add(elem: BigNum): void; +} +/** + */ +declare export class TransactionOutput { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {TransactionOutput} + */ + static from_bytes(bytes: Uint8Array): TransactionOutput; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {TransactionOutput} + */ + static from_hex(hex_str: string): TransactionOutput; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {TransactionOutputJSON} + */ + to_js_value(): TransactionOutputJSON; + + /** + * @param {string} json + * @returns {TransactionOutput} + */ + static from_json(json: string): TransactionOutput; + + /** + * @returns {Address} + */ + address(): Address; + + /** + * @returns {Value} + */ + amount(): Value; + + /** + * @returns {DataHash | void} + */ + data_hash(): DataHash | void; + + /** + * @returns {PlutusData | void} + */ + plutus_data(): PlutusData | void; + + /** + * @returns {ScriptRef | void} + */ + script_ref(): ScriptRef | void; + + /** + * @param {ScriptRef} script_ref + */ + set_script_ref(script_ref: ScriptRef): void; + + /** + * @param {PlutusData} data + */ + set_plutus_data(data: PlutusData): void; + + /** + * @param {DataHash} data_hash + */ + set_data_hash(data_hash: DataHash): void; + + /** + * @returns {boolean} + */ + has_plutus_data(): boolean; + + /** + * @returns {boolean} + */ + has_data_hash(): boolean; + + /** + * @returns {boolean} + */ + has_script_ref(): boolean; + + /** + * @param {Address} address + * @param {Value} amount + * @returns {TransactionOutput} + */ + static new(address: Address, amount: Value): TransactionOutput; + + /** + * @returns {$Values< + typeof + CborContainerType> | void} + */ + serialization_format(): $Values | void; +} +/** + */ +declare export class TransactionOutputAmountBuilder { + free(): void; + + /** + * @param {Value} amount + * @returns {TransactionOutputAmountBuilder} + */ + with_value(amount: Value): TransactionOutputAmountBuilder; + + /** + * @param {BigNum} coin + * @returns {TransactionOutputAmountBuilder} + */ + with_coin(coin: BigNum): TransactionOutputAmountBuilder; + + /** + * @param {BigNum} coin + * @param {MultiAsset} multiasset + * @returns {TransactionOutputAmountBuilder} + */ + with_coin_and_asset( + coin: BigNum, + multiasset: MultiAsset + ): TransactionOutputAmountBuilder; + + /** + * @param {MultiAsset} multiasset + * @param {DataCost} data_cost + * @returns {TransactionOutputAmountBuilder} + */ + with_asset_and_min_required_coin_by_utxo_cost( + multiasset: MultiAsset, + data_cost: DataCost + ): TransactionOutputAmountBuilder; + + /** + * @returns {TransactionOutput} + */ + build(): TransactionOutput; +} +/** + * We introduce a builder-pattern format for creating transaction outputs + * This is because: + * 1. Some fields (i.e. data hash) are optional, and we can't easily expose Option<> in WASM + * 2. Some fields like amounts have many ways it could be set (some depending on other field values being known) + * 3. Easier to adapt as the output format gets more complicated in future Cardano releases + */ +declare export class TransactionOutputBuilder { + free(): void; + + /** + * @returns {TransactionOutputBuilder} + */ + static new(): TransactionOutputBuilder; + + /** + * @param {Address} address + * @returns {TransactionOutputBuilder} + */ + with_address(address: Address): TransactionOutputBuilder; + + /** + * @param {DataHash} data_hash + * @returns {TransactionOutputBuilder} + */ + with_data_hash(data_hash: DataHash): TransactionOutputBuilder; + + /** + * @param {PlutusData} data + * @returns {TransactionOutputBuilder} + */ + with_plutus_data(data: PlutusData): TransactionOutputBuilder; + + /** + * @param {ScriptRef} script_ref + * @returns {TransactionOutputBuilder} + */ + with_script_ref(script_ref: ScriptRef): TransactionOutputBuilder; + + /** + * @returns {TransactionOutputAmountBuilder} + */ + next(): TransactionOutputAmountBuilder; +} +/** + */ +declare export class TransactionOutputs { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {TransactionOutputs} + */ + static from_bytes(bytes: Uint8Array): TransactionOutputs; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {TransactionOutputs} + */ + static from_hex(hex_str: string): TransactionOutputs; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {TransactionOutputsJSON} + */ + to_js_value(): TransactionOutputsJSON; + + /** + * @param {string} json + * @returns {TransactionOutputs} + */ + static from_json(json: string): TransactionOutputs; + + /** + * @returns {TransactionOutputs} + */ + static new(): TransactionOutputs; + + /** + * @returns {number} + */ + len(): number; + + /** + * @param {number} index + * @returns {TransactionOutput} + */ + get(index: number): TransactionOutput; + + /** + * @param {TransactionOutput} elem + */ + add(elem: TransactionOutput): void; +} +/** + */ +declare export class TransactionUnspentOutput { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {TransactionUnspentOutput} + */ + static from_bytes(bytes: Uint8Array): TransactionUnspentOutput; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {TransactionUnspentOutput} + */ + static from_hex(hex_str: string): TransactionUnspentOutput; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {TransactionUnspentOutputJSON} + */ + to_js_value(): TransactionUnspentOutputJSON; + + /** + * @param {string} json + * @returns {TransactionUnspentOutput} + */ + static from_json(json: string): TransactionUnspentOutput; + + /** + * @param {TransactionInput} input + * @param {TransactionOutput} output + * @returns {TransactionUnspentOutput} + */ + static new( + input: TransactionInput, + output: TransactionOutput + ): TransactionUnspentOutput; + + /** + * @returns {TransactionInput} + */ + input(): TransactionInput; + + /** + * @returns {TransactionOutput} + */ + output(): TransactionOutput; +} +/** + */ +declare export class TransactionUnspentOutputs { + free(): void; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {TransactionUnspentOutputsJSON} + */ + to_js_value(): TransactionUnspentOutputsJSON; + + /** + * @param {string} json + * @returns {TransactionUnspentOutputs} + */ + static from_json(json: string): TransactionUnspentOutputs; + + /** + * @returns {TransactionUnspentOutputs} + */ + static new(): TransactionUnspentOutputs; + + /** + * @returns {number} + */ + len(): number; + + /** + * @param {number} index + * @returns {TransactionUnspentOutput} + */ + get(index: number): TransactionUnspentOutput; + + /** + * @param {TransactionUnspentOutput} elem + */ + add(elem: TransactionUnspentOutput): void; +} +/** + */ +declare export class TransactionWitnessSet { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {TransactionWitnessSet} + */ + static from_bytes(bytes: Uint8Array): TransactionWitnessSet; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {TransactionWitnessSet} + */ + static from_hex(hex_str: string): TransactionWitnessSet; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {TransactionWitnessSetJSON} + */ + to_js_value(): TransactionWitnessSetJSON; + + /** + * @param {string} json + * @returns {TransactionWitnessSet} + */ + static from_json(json: string): TransactionWitnessSet; + + /** + * @param {Vkeywitnesses} vkeys + */ + set_vkeys(vkeys: Vkeywitnesses): void; + + /** + * @returns {Vkeywitnesses | void} + */ + vkeys(): Vkeywitnesses | void; + + /** + * @param {NativeScripts} native_scripts + */ + set_native_scripts(native_scripts: NativeScripts): void; + + /** + * @returns {NativeScripts | void} + */ + native_scripts(): NativeScripts | void; + + /** + * @param {BootstrapWitnesses} bootstraps + */ + set_bootstraps(bootstraps: BootstrapWitnesses): void; + + /** + * @returns {BootstrapWitnesses | void} + */ + bootstraps(): BootstrapWitnesses | void; + + /** + * @param {PlutusScripts} plutus_scripts + */ + set_plutus_scripts(plutus_scripts: PlutusScripts): void; + + /** + * @returns {PlutusScripts | void} + */ + plutus_scripts(): PlutusScripts | void; + + /** + * @param {PlutusList} plutus_data + */ + set_plutus_data(plutus_data: PlutusList): void; + + /** + * @returns {PlutusList | void} + */ + plutus_data(): PlutusList | void; + + /** + * @param {Redeemers} redeemers + */ + set_redeemers(redeemers: Redeemers): void; + + /** + * @returns {Redeemers | void} + */ + redeemers(): Redeemers | void; + + /** + * @returns {TransactionWitnessSet} + */ + static new(): TransactionWitnessSet; +} +/** + */ +declare export class TransactionWitnessSets { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {TransactionWitnessSets} + */ + static from_bytes(bytes: Uint8Array): TransactionWitnessSets; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {TransactionWitnessSets} + */ + static from_hex(hex_str: string): TransactionWitnessSets; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {TransactionWitnessSetsJSON} + */ + to_js_value(): TransactionWitnessSetsJSON; + + /** + * @param {string} json + * @returns {TransactionWitnessSets} + */ + static from_json(json: string): TransactionWitnessSets; + + /** + * @returns {TransactionWitnessSets} + */ + static new(): TransactionWitnessSets; + + /** + * @returns {number} + */ + len(): number; + + /** + * @param {number} index + * @returns {TransactionWitnessSet} + */ + get(index: number): TransactionWitnessSet; + + /** + * @param {TransactionWitnessSet} elem + */ + add(elem: TransactionWitnessSet): void; +} +/** + */ +declare export class TreasuryWithdrawals { + free(): void; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {TreasuryWithdrawalsJSON} + */ + to_js_value(): TreasuryWithdrawalsJSON; + + /** + * @param {string} json + * @returns {TreasuryWithdrawals} + */ + static from_json(json: string): TreasuryWithdrawals; + + /** + * @returns {TreasuryWithdrawals} + */ + static new(): TreasuryWithdrawals; + + /** + * @param {RewardAddress} key + * @returns {BigNum | void} + */ + get(key: RewardAddress): BigNum | void; + + /** + * @param {RewardAddress} key + * @param {BigNum} value + */ + insert(key: RewardAddress, value: BigNum): void; + + /** + * @returns {RewardAddresses} + */ + keys(): RewardAddresses; + + /** + * @returns {number} + */ + len(): number; +} +/** + */ +declare export class TreasuryWithdrawalsAction { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {TreasuryWithdrawalsAction} + */ + static from_bytes(bytes: Uint8Array): TreasuryWithdrawalsAction; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {TreasuryWithdrawalsAction} + */ + static from_hex(hex_str: string): TreasuryWithdrawalsAction; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {TreasuryWithdrawalsActionJSON} + */ + to_js_value(): TreasuryWithdrawalsActionJSON; + + /** + * @param {string} json + * @returns {TreasuryWithdrawalsAction} + */ + static from_json(json: string): TreasuryWithdrawalsAction; + + /** + * @returns {TreasuryWithdrawals} + */ + withdrawals(): TreasuryWithdrawals; + + /** + * @returns {ScriptHash | void} + */ + policy_hash(): ScriptHash | void; + + /** + * @param {TreasuryWithdrawals} withdrawals + * @returns {TreasuryWithdrawalsAction} + */ + static new(withdrawals: TreasuryWithdrawals): TreasuryWithdrawalsAction; + + /** + * @param {TreasuryWithdrawals} withdrawals + * @param {ScriptHash} policy_hash + * @returns {TreasuryWithdrawalsAction} + */ + static new_with_policy_hash( + withdrawals: TreasuryWithdrawals, + policy_hash: ScriptHash + ): TreasuryWithdrawalsAction; +} +/** + */ +declare export class TxBuilderConstants { + free(): void; + + /** + * @returns {Costmdls} + */ + static plutus_default_cost_models(): Costmdls; + + /** + * @returns {Costmdls} + */ + static plutus_alonzo_cost_models(): Costmdls; + + /** + * @returns {Costmdls} + */ + static plutus_vasil_cost_models(): Costmdls; + + /** + * @returns {Costmdls} + */ + static plutus_conway_cost_models(): Costmdls; +} +/** + */ +declare export class TxInputsBuilder { + free(): void; + + /** + * @returns {TxInputsBuilder} + */ + static new(): TxInputsBuilder; + + /** + * We have to know what kind of inputs these are to know what kind of mock witnesses to create since + * 1) mock witnesses have different lengths depending on the type which changes the expecting fee + * 2) Witnesses are a set so we need to get rid of duplicates to avoid over-estimating the fee + * @param {Ed25519KeyHash} hash + * @param {TransactionInput} input + * @param {Value} amount + */ + add_key_input( + hash: Ed25519KeyHash, + input: TransactionInput, + amount: Value + ): void; + + /** + * This method will add the input to the builder and also register the required native script witness + * @param {NativeScriptSource} script + * @param {TransactionInput} input + * @param {Value} amount + */ + add_native_script_input( + script: NativeScriptSource, + input: TransactionInput, + amount: Value + ): void; + + /** + * This method will add the input to the builder and also register the required plutus witness + * @param {PlutusWitness} witness + * @param {TransactionInput} input + * @param {Value} amount + */ + add_plutus_script_input( + witness: PlutusWitness, + input: TransactionInput, + amount: Value + ): void; + + /** + * @param {ByronAddress} address + * @param {TransactionInput} input + * @param {Value} amount + */ + add_bootstrap_input( + address: ByronAddress, + input: TransactionInput, + amount: Value + ): void; + + /** + * Adds non script input, in case of script or reward address input it will return an error + * @param {Address} address + * @param {TransactionInput} input + * @param {Value} amount + */ + add_regular_input( + address: Address, + input: TransactionInput, + amount: Value + ): void; + + /** + * @returns {TransactionInputs} + */ + get_ref_inputs(): TransactionInputs; + + /** + * Returns a copy of the current script input witness scripts in the builder + * @returns {NativeScripts | void} + */ + get_native_input_scripts(): NativeScripts | void; + + /** + * Returns a copy of the current plutus input witness scripts in the builder. + * NOTE: each plutus witness will be cloned with a specific corresponding input index + * @returns {PlutusWitnesses | void} + */ + get_plutus_input_scripts(): PlutusWitnesses | void; + + /** + * @returns {number} + */ + len(): number; + + /** + * @param {Ed25519KeyHash} key + */ + add_required_signer(key: Ed25519KeyHash): void; + + /** + * @param {Ed25519KeyHashes} keys + */ + add_required_signers(keys: Ed25519KeyHashes): void; + + /** + * @returns {Value} + */ + total_value(): Value; + + /** + * @returns {TransactionInputs} + */ + inputs(): TransactionInputs; + + /** + * @returns {TransactionInputs | void} + */ + inputs_option(): TransactionInputs | void; +} +/** + */ +declare export class URL { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {URL} + */ + static from_bytes(bytes: Uint8Array): URL; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {URL} + */ + static from_hex(hex_str: string): URL; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {URLJSON} + */ + to_js_value(): URLJSON; + + /** + * @param {string} json + * @returns {URL} + */ + static from_json(json: string): URL; + + /** + * @param {string} url + * @returns {URL} + */ + static new(url: string): URL; + + /** + * @returns {string} + */ + url(): string; +} +/** + */ +declare export class UnitInterval { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {UnitInterval} + */ + static from_bytes(bytes: Uint8Array): UnitInterval; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {UnitInterval} + */ + static from_hex(hex_str: string): UnitInterval; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {UnitIntervalJSON} + */ + to_js_value(): UnitIntervalJSON; + + /** + * @param {string} json + * @returns {UnitInterval} + */ + static from_json(json: string): UnitInterval; + + /** + * @returns {BigNum} + */ + numerator(): BigNum; + + /** + * @returns {BigNum} + */ + denominator(): BigNum; + + /** + * @param {BigNum} numerator + * @param {BigNum} denominator + * @returns {UnitInterval} + */ + static new(numerator: BigNum, denominator: BigNum): UnitInterval; +} +/** + */ +declare export class Update { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {Update} + */ + static from_bytes(bytes: Uint8Array): Update; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {Update} + */ + static from_hex(hex_str: string): Update; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {UpdateJSON} + */ + to_js_value(): UpdateJSON; + + /** + * @param {string} json + * @returns {Update} + */ + static from_json(json: string): Update; + + /** + * @returns {ProposedProtocolParameterUpdates} + */ + proposed_protocol_parameter_updates(): ProposedProtocolParameterUpdates; + + /** + * @returns {number} + */ + epoch(): number; + + /** + * @param {ProposedProtocolParameterUpdates} proposed_protocol_parameter_updates + * @param {number} epoch + * @returns {Update} + */ + static new( + proposed_protocol_parameter_updates: ProposedProtocolParameterUpdates, + epoch: number + ): Update; +} +/** + */ +declare export class UpdateCommitteeAction { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {UpdateCommitteeAction} + */ + static from_bytes(bytes: Uint8Array): UpdateCommitteeAction; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {UpdateCommitteeAction} + */ + static from_hex(hex_str: string): UpdateCommitteeAction; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {UpdateCommitteeActionJSON} + */ + to_js_value(): UpdateCommitteeActionJSON; + + /** + * @param {string} json + * @returns {UpdateCommitteeAction} + */ + static from_json(json: string): UpdateCommitteeAction; + + /** + * @returns {GovernanceActionId | void} + */ + gov_action_id(): GovernanceActionId | void; + + /** + * @returns {Committee} + */ + committee(): Committee; + + /** + * @returns {Credentials} + */ + members_to_remove(): Credentials; + + /** + * @param {Committee} committee + * @param {Credentials} members_to_remove + * @returns {UpdateCommitteeAction} + */ + static new( + committee: Committee, + members_to_remove: Credentials + ): UpdateCommitteeAction; + + /** + * @param {GovernanceActionId} gov_action_id + * @param {Committee} committee + * @param {Credentials} members_to_remove + * @returns {UpdateCommitteeAction} + */ + static new_with_action_id( + gov_action_id: GovernanceActionId, + committee: Committee, + members_to_remove: Credentials + ): UpdateCommitteeAction; +} +/** + */ +declare export class VRFCert { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {VRFCert} + */ + static from_bytes(bytes: Uint8Array): VRFCert; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {VRFCert} + */ + static from_hex(hex_str: string): VRFCert; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {VRFCertJSON} + */ + to_js_value(): VRFCertJSON; + + /** + * @param {string} json + * @returns {VRFCert} + */ + static from_json(json: string): VRFCert; + + /** + * @returns {Uint8Array} + */ + output(): Uint8Array; + + /** + * @returns {Uint8Array} + */ + proof(): Uint8Array; + + /** + * @param {Uint8Array} output + * @param {Uint8Array} proof + * @returns {VRFCert} + */ + static new(output: Uint8Array, proof: Uint8Array): VRFCert; +} +/** + */ +declare export class VRFKeyHash { + free(): void; + + /** + * @param {Uint8Array} bytes + * @returns {VRFKeyHash} + */ + static from_bytes(bytes: Uint8Array): VRFKeyHash; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {string} prefix + * @returns {string} + */ + to_bech32(prefix: string): string; + + /** + * @param {string} bech_str + * @returns {VRFKeyHash} + */ + static from_bech32(bech_str: string): VRFKeyHash; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex + * @returns {VRFKeyHash} + */ + static from_hex(hex: string): VRFKeyHash; +} +/** + */ +declare export class VRFVKey { + free(): void; + + /** + * @param {Uint8Array} bytes + * @returns {VRFVKey} + */ + static from_bytes(bytes: Uint8Array): VRFVKey; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {string} prefix + * @returns {string} + */ + to_bech32(prefix: string): string; + + /** + * @param {string} bech_str + * @returns {VRFVKey} + */ + static from_bech32(bech_str: string): VRFVKey; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex + * @returns {VRFVKey} + */ + static from_hex(hex: string): VRFVKey; +} +/** + */ +declare export class Value { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {Value} + */ + static from_bytes(bytes: Uint8Array): Value; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {Value} + */ + static from_hex(hex_str: string): Value; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {ValueJSON} + */ + to_js_value(): ValueJSON; + + /** + * @param {string} json + * @returns {Value} + */ + static from_json(json: string): Value; + + /** + * @param {BigNum} coin + * @returns {Value} + */ + static new(coin: BigNum): Value; + + /** + * @param {MultiAsset} multiasset + * @returns {Value} + */ + static new_from_assets(multiasset: MultiAsset): Value; + + /** + * @param {BigNum} coin + * @param {MultiAsset} multiasset + * @returns {Value} + */ + static new_with_assets(coin: BigNum, multiasset: MultiAsset): Value; + + /** + * @returns {Value} + */ + static zero(): Value; + + /** + * @returns {boolean} + */ + is_zero(): boolean; + + /** + * @returns {BigNum} + */ + coin(): BigNum; + + /** + * @param {BigNum} coin + */ + set_coin(coin: BigNum): void; + + /** + * @returns {MultiAsset | void} + */ + multiasset(): MultiAsset | void; + + /** + * @param {MultiAsset} multiasset + */ + set_multiasset(multiasset: MultiAsset): void; + + /** + * @param {Value} rhs + * @returns {Value} + */ + checked_add(rhs: Value): Value; + + /** + * @param {Value} rhs_value + * @returns {Value} + */ + checked_sub(rhs_value: Value): Value; + + /** + * @param {Value} rhs_value + * @returns {Value} + */ + clamped_sub(rhs_value: Value): Value; + + /** + * note: values are only partially comparable + * @param {Value} rhs_value + * @returns {number | void} + */ + compare(rhs_value: Value): number | void; +} +/** + */ +declare export class VersionedBlock { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {VersionedBlock} + */ + static from_bytes(bytes: Uint8Array): VersionedBlock; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {VersionedBlock} + */ + static from_hex(hex_str: string): VersionedBlock; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {VersionedBlockJSON} + */ + to_js_value(): VersionedBlockJSON; + + /** + * @param {string} json + * @returns {VersionedBlock} + */ + static from_json(json: string): VersionedBlock; + + /** + * @param {Block} block + * @param {number} era_code + * @returns {VersionedBlock} + */ + static new(block: Block, era_code: number): VersionedBlock; + + /** + * @returns {Block} + */ + block(): Block; + + /** + * @returns {$Values< + typeof + BlockEra>} + */ + era(): $Values; +} +/** + */ +declare export class Vkey { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {Vkey} + */ + static from_bytes(bytes: Uint8Array): Vkey; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {Vkey} + */ + static from_hex(hex_str: string): Vkey; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {VkeyJSON} + */ + to_js_value(): VkeyJSON; + + /** + * @param {string} json + * @returns {Vkey} + */ + static from_json(json: string): Vkey; + + /** + * @param {PublicKey} pk + * @returns {Vkey} + */ + static new(pk: PublicKey): Vkey; + + /** + * @returns {PublicKey} + */ + public_key(): PublicKey; +} +/** + */ +declare export class Vkeys { + free(): void; + + /** + * @returns {Vkeys} + */ + static new(): Vkeys; + + /** + * @returns {number} + */ + len(): number; + + /** + * @param {number} index + * @returns {Vkey} + */ + get(index: number): Vkey; + + /** + * @param {Vkey} elem + */ + add(elem: Vkey): void; +} +/** + */ +declare export class Vkeywitness { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {Vkeywitness} + */ + static from_bytes(bytes: Uint8Array): Vkeywitness; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {Vkeywitness} + */ + static from_hex(hex_str: string): Vkeywitness; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {VkeywitnessJSON} + */ + to_js_value(): VkeywitnessJSON; + + /** + * @param {string} json + * @returns {Vkeywitness} + */ + static from_json(json: string): Vkeywitness; + + /** + * @param {Vkey} vkey + * @param {Ed25519Signature} signature + * @returns {Vkeywitness} + */ + static new(vkey: Vkey, signature: Ed25519Signature): Vkeywitness; + + /** + * @returns {Vkey} + */ + vkey(): Vkey; + + /** + * @returns {Ed25519Signature} + */ + signature(): Ed25519Signature; +} +/** + */ +declare export class Vkeywitnesses { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {Vkeywitnesses} + */ + static from_bytes(bytes: Uint8Array): Vkeywitnesses; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {Vkeywitnesses} + */ + static from_hex(hex_str: string): Vkeywitnesses; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {VkeywitnessesJSON} + */ + to_js_value(): VkeywitnessesJSON; + + /** + * @param {string} json + * @returns {Vkeywitnesses} + */ + static from_json(json: string): Vkeywitnesses; + + /** + * @returns {Vkeywitnesses} + */ + static new(): Vkeywitnesses; + + /** + * @returns {number} + */ + len(): number; + + /** + * @param {number} index + * @returns {Vkeywitness} + */ + get(index: number): Vkeywitness; + + /** + * Add a new `Vkeywitness` to the set. + * Returns `true` if the element was not already present in the set. + * @param {Vkeywitness} elem + * @returns {boolean} + */ + add(elem: Vkeywitness): boolean; +} +/** + */ +declare export class VoteDelegation { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {VoteDelegation} + */ + static from_bytes(bytes: Uint8Array): VoteDelegation; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {VoteDelegation} + */ + static from_hex(hex_str: string): VoteDelegation; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {VoteDelegationJSON} + */ + to_js_value(): VoteDelegationJSON; + + /** + * @param {string} json + * @returns {VoteDelegation} + */ + static from_json(json: string): VoteDelegation; + + /** + * @returns {Credential} + */ + stake_credential(): Credential; + + /** + * @returns {DRep} + */ + drep(): DRep; + + /** + * @param {Credential} stake_credential + * @param {DRep} drep + * @returns {VoteDelegation} + */ + static new(stake_credential: Credential, drep: DRep): VoteDelegation; + + /** + * @returns {boolean} + */ + has_script_credentials(): boolean; +} +/** + */ +declare export class VoteRegistrationAndDelegation { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {VoteRegistrationAndDelegation} + */ + static from_bytes(bytes: Uint8Array): VoteRegistrationAndDelegation; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {VoteRegistrationAndDelegation} + */ + static from_hex(hex_str: string): VoteRegistrationAndDelegation; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {VoteRegistrationAndDelegationJSON} + */ + to_js_value(): VoteRegistrationAndDelegationJSON; + + /** + * @param {string} json + * @returns {VoteRegistrationAndDelegation} + */ + static from_json(json: string): VoteRegistrationAndDelegation; + + /** + * @returns {Credential} + */ + stake_credential(): Credential; + + /** + * @returns {DRep} + */ + drep(): DRep; + + /** + * @returns {BigNum} + */ + coin(): BigNum; + + /** + * @param {Credential} stake_credential + * @param {DRep} drep + * @param {BigNum} coin + * @returns {VoteRegistrationAndDelegation} + */ + static new( + stake_credential: Credential, + drep: DRep, + coin: BigNum + ): VoteRegistrationAndDelegation; + + /** + * @returns {boolean} + */ + has_script_credentials(): boolean; +} +/** + */ +declare export class Voter { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {Voter} + */ + static from_bytes(bytes: Uint8Array): Voter; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {Voter} + */ + static from_hex(hex_str: string): Voter; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {VoterJSON} + */ + to_js_value(): VoterJSON; + + /** + * @param {string} json + * @returns {Voter} + */ + static from_json(json: string): Voter; + + /** + * @param {Credential} cred + * @returns {Voter} + */ + static new_constitutional_committee_hot_credential(cred: Credential): Voter; + + /** + * @param {Credential} cred + * @returns {Voter} + */ + static new_drep_credential(cred: Credential): Voter; + + /** + * @param {Ed25519KeyHash} key_hash + * @returns {Voter} + */ + static new_stake_pool_key_hash(key_hash: Ed25519KeyHash): Voter; + + /** + * @returns {$Values< + typeof + VoterKind>} + */ + kind(): $Values; + + /** + * @returns {Credential | void} + */ + to_constitutional_committee_hot_credential(): Credential | void; + + /** + * @returns {Credential | void} + */ + to_drep_credential(): Credential | void; + + /** + * @returns {Ed25519KeyHash | void} + */ + to_stake_pool_key_hash(): Ed25519KeyHash | void; + + /** + * @returns {boolean} + */ + has_script_credentials(): boolean; + + /** + * @returns {Ed25519KeyHash | void} + */ + to_key_hash(): Ed25519KeyHash | void; +} +/** + */ +declare export class Voters { + free(): void; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {VotersJSON} + */ + to_js_value(): VotersJSON; + + /** + * @param {string} json + * @returns {Voters} + */ + static from_json(json: string): Voters; + + /** + * @returns {Voters} + */ + static new(): Voters; + + /** + * @param {Voter} voter + */ + add(voter: Voter): void; + + /** + * @param {number} index + * @returns {Voter | void} + */ + get(index: number): Voter | void; + + /** + * @returns {number} + */ + len(): number; +} +/** + */ +declare export class VotingBuilder { + free(): void; + + /** + * @returns {VotingBuilder} + */ + static new(): VotingBuilder; + + /** + * @param {Voter} voter + * @param {GovernanceActionId} gov_action_id + * @param {VotingProcedure} voting_procedure + */ + add( + voter: Voter, + gov_action_id: GovernanceActionId, + voting_procedure: VotingProcedure + ): void; + + /** + * @param {Voter} voter + * @param {GovernanceActionId} gov_action_id + * @param {VotingProcedure} voting_procedure + * @param {PlutusWitness} witness + */ + add_with_plutus_witness( + voter: Voter, + gov_action_id: GovernanceActionId, + voting_procedure: VotingProcedure, + witness: PlutusWitness + ): void; + + /** + * @param {Voter} voter + * @param {GovernanceActionId} gov_action_id + * @param {VotingProcedure} voting_procedure + * @param {NativeScriptSource} native_script_source + */ + add_with_native_script( + voter: Voter, + gov_action_id: GovernanceActionId, + voting_procedure: VotingProcedure, + native_script_source: NativeScriptSource + ): void; + + /** + * @returns {PlutusWitnesses} + */ + get_plutus_witnesses(): PlutusWitnesses; + + /** + * @returns {TransactionInputs} + */ + get_ref_inputs(): TransactionInputs; + + /** + * @returns {NativeScripts} + */ + get_native_scripts(): NativeScripts; + + /** + * @returns {boolean} + */ + has_plutus_scripts(): boolean; + + /** + * @returns {VotingProcedures} + */ + build(): VotingProcedures; +} +/** + */ +declare export class VotingProcedure { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {VotingProcedure} + */ + static from_bytes(bytes: Uint8Array): VotingProcedure; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {VotingProcedure} + */ + static from_hex(hex_str: string): VotingProcedure; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {VotingProcedureJSON} + */ + to_js_value(): VotingProcedureJSON; + + /** + * @param {string} json + * @returns {VotingProcedure} + */ + static from_json(json: string): VotingProcedure; + + /** + * @param {$Values< + typeof + VoteKind>} vote + * @returns {VotingProcedure} + */ + static new(vote: $Values): VotingProcedure; + + /** + * @param {$Values< + typeof + VoteKind>} vote + * @param {Anchor} anchor + * @returns {VotingProcedure} + */ + static new_with_anchor( + vote: $Values, + anchor: Anchor + ): VotingProcedure; + + /** + * @returns {$Values< + typeof + VoteKind>} + */ + vote_kind(): $Values; + + /** + * @returns {Anchor | void} + */ + anchor(): Anchor | void; +} +/** + */ +declare export class VotingProcedures { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {VotingProcedures} + */ + static from_bytes(bytes: Uint8Array): VotingProcedures; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {VotingProcedures} + */ + static from_hex(hex_str: string): VotingProcedures; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {VotingProceduresJSON} + */ + to_js_value(): VotingProceduresJSON; + + /** + * @param {string} json + * @returns {VotingProcedures} + */ + static from_json(json: string): VotingProcedures; + + /** + * @returns {VotingProcedures} + */ + static new(): VotingProcedures; + + /** + * @param {Voter} voter + * @param {GovernanceActionId} governance_action_id + * @param {VotingProcedure} voting_procedure + */ + insert( + voter: Voter, + governance_action_id: GovernanceActionId, + voting_procedure: VotingProcedure + ): void; + + /** + * @param {Voter} voter + * @param {GovernanceActionId} governance_action_id + * @returns {VotingProcedure | void} + */ + get( + voter: Voter, + governance_action_id: GovernanceActionId + ): VotingProcedure | void; + + /** + * @returns {Voters} + */ + get_voters(): Voters; + + /** + * @param {Voter} voter + * @returns {GovernanceActionIds} + */ + get_governance_action_ids_by_voter(voter: Voter): GovernanceActionIds; +} +/** + */ +declare export class VotingProposal { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {VotingProposal} + */ + static from_bytes(bytes: Uint8Array): VotingProposal; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {VotingProposal} + */ + static from_hex(hex_str: string): VotingProposal; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {VotingProposalJSON} + */ + to_js_value(): VotingProposalJSON; + + /** + * @param {string} json + * @returns {VotingProposal} + */ + static from_json(json: string): VotingProposal; + + /** + * @returns {GovernanceAction} + */ + governance_action(): GovernanceAction; + + /** + * @returns {Anchor} + */ + anchor(): Anchor; + + /** + * @returns {RewardAddress} + */ + reward_account(): RewardAddress; + + /** + * @returns {BigNum} + */ + deposit(): BigNum; + + /** + * @param {GovernanceAction} governance_action + * @param {Anchor} anchor + * @param {RewardAddress} reward_account + * @param {BigNum} deposit + * @returns {VotingProposal} + */ + static new( + governance_action: GovernanceAction, + anchor: Anchor, + reward_account: RewardAddress, + deposit: BigNum + ): VotingProposal; +} +/** + */ +declare export class VotingProposalBuilder { + free(): void; + + /** + * @returns {VotingProposalBuilder} + */ + static new(): VotingProposalBuilder; + + /** + * @param {VotingProposal} proposal + */ + add(proposal: VotingProposal): void; + + /** + * @param {VotingProposal} proposal + * @param {PlutusWitness} witness + */ + add_with_plutus_witness( + proposal: VotingProposal, + witness: PlutusWitness + ): void; + + /** + * @returns {PlutusWitnesses} + */ + get_plutus_witnesses(): PlutusWitnesses; + + /** + * @returns {TransactionInputs} + */ + get_ref_inputs(): TransactionInputs; + + /** + * @returns {boolean} + */ + has_plutus_scripts(): boolean; + + /** + * @returns {VotingProposals} + */ + build(): VotingProposals; +} +/** + */ +declare export class VotingProposals { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {VotingProposals} + */ + static from_bytes(bytes: Uint8Array): VotingProposals; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {VotingProposals} + */ + static from_hex(hex_str: string): VotingProposals; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {VotingProposalsJSON} + */ + to_js_value(): VotingProposalsJSON; + + /** + * @param {string} json + * @returns {VotingProposals} + */ + static from_json(json: string): VotingProposals; + + /** + * @returns {VotingProposals} + */ + static new(): VotingProposals; + + /** + * @returns {number} + */ + len(): number; + + /** + * @param {number} index + * @returns {VotingProposal} + */ + get(index: number): VotingProposal; + + /** + * Add a proposal to the set of proposals + * Returns true if the proposal was added, false if it was already present + * @param {VotingProposal} proposal + * @returns {boolean} + */ + add(proposal: VotingProposal): boolean; +} +/** + */ +declare export class Withdrawals { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {Withdrawals} + */ + static from_bytes(bytes: Uint8Array): Withdrawals; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {Withdrawals} + */ + static from_hex(hex_str: string): Withdrawals; + + /** + * @returns {string} + */ + to_json(): string; + + /** + * @returns {WithdrawalsJSON} + */ + to_js_value(): WithdrawalsJSON; + + /** + * @param {string} json + * @returns {Withdrawals} + */ + static from_json(json: string): Withdrawals; + + /** + * @returns {Withdrawals} + */ + static new(): Withdrawals; + + /** + * @returns {number} + */ + len(): number; + + /** + * @param {RewardAddress} key + * @param {BigNum} value + * @returns {BigNum | void} + */ + insert(key: RewardAddress, value: BigNum): BigNum | void; + + /** + * @param {RewardAddress} key + * @returns {BigNum | void} + */ + get(key: RewardAddress): BigNum | void; + + /** + * @returns {RewardAddresses} + */ + keys(): RewardAddresses; +} +/** + */ +declare export class WithdrawalsBuilder { + free(): void; + + /** + * @returns {WithdrawalsBuilder} + */ + static new(): WithdrawalsBuilder; + + /** + * @param {RewardAddress} address + * @param {BigNum} coin + */ + add(address: RewardAddress, coin: BigNum): void; + + /** + * @param {RewardAddress} address + * @param {BigNum} coin + * @param {PlutusWitness} witness + */ + add_with_plutus_witness( + address: RewardAddress, + coin: BigNum, + witness: PlutusWitness + ): void; + + /** + * @param {RewardAddress} address + * @param {BigNum} coin + * @param {NativeScriptSource} native_script_source + */ + add_with_native_script( + address: RewardAddress, + coin: BigNum, + native_script_source: NativeScriptSource + ): void; + + /** + * @returns {PlutusWitnesses} + */ + get_plutus_witnesses(): PlutusWitnesses; + + /** + * @returns {TransactionInputs} + */ + get_ref_inputs(): TransactionInputs; + + /** + * @returns {NativeScripts} + */ + get_native_scripts(): NativeScripts; + + /** + * @returns {Value} + */ + get_total_withdrawals(): Value; + + /** + * @returns {boolean} + */ + has_plutus_scripts(): boolean; + + /** + * @returns {Withdrawals} + */ + build(): Withdrawals; +} +export type AddressJSON = string; +export type URLJSON = string; +export interface AnchorJSON { + anchor_data_hash: string; + anchor_url: URLJSON; +} +export type AnchorDataHashJSON = string; +export type AssetNameJSON = string; +export type AssetNamesJSON = string[]; +export interface AssetsJSON { + [k: string]: string; +} +export type NativeScriptJSON = + | { + ScriptPubkey: ScriptPubkeyJSON, + ... + } + | { + ScriptAll: ScriptAllJSON, + ... + } + | { + ScriptAny: ScriptAnyJSON, + ... + } + | { + ScriptNOfK: ScriptNOfKJSON, + ... + } + | { + TimelockStart: TimelockStartJSON, + ... + } + | { + TimelockExpiry: TimelockExpiryJSON, + ... + }; +export type NativeScriptsJSON = NativeScriptJSON[]; +export type PlutusScriptsJSON = string[]; +export interface AuxiliaryDataJSON { + metadata?: { + [k: string]: string, + } | null; + native_scripts?: NativeScriptsJSON | null; + plutus_scripts?: PlutusScriptsJSON | null; + prefer_alonzo_format: boolean; +} +export interface ScriptPubkeyJSON { + addr_keyhash: string; +} +export interface ScriptAllJSON { + native_scripts: NativeScriptsJSON; +} +export interface ScriptAnyJSON { + native_scripts: NativeScriptsJSON; +} +export interface ScriptNOfKJSON { + n: number; + native_scripts: NativeScriptsJSON; +} +export interface TimelockStartJSON { + slot: string; +} +export interface TimelockExpiryJSON { + slot: string; +} +export type AuxiliaryDataHashJSON = string; +export interface AuxiliaryDataSetJSON { + [k: string]: AuxiliaryDataJSON; +} +export type BigIntJSON = string; +export type BigNumJSON = string; +export type VkeyJSON = string; +export type HeaderLeaderCertEnumJSON = + | { + /** + * @minItems 2 + * @maxItems 2 + */ + NonceAndLeader: [VRFCertJSON, VRFCertJSON], + ... + } + | { + VrfResult: VRFCertJSON, + ... + }; +export type CertificateJSON = + | { + StakeRegistration: StakeRegistrationJSON, + ... + } + | { + StakeDeregistration: StakeDeregistrationJSON, + ... + } + | { + StakeDelegation: StakeDelegationJSON, + ... + } + | { + PoolRegistration: PoolRegistrationJSON, + ... + } + | { + PoolRetirement: PoolRetirementJSON, + ... + } + | { + GenesisKeyDelegation: GenesisKeyDelegationJSON, + ... + } + | { + MoveInstantaneousRewardsCert: MoveInstantaneousRewardsCertJSON, + ... + } + | { + CommitteeHotAuth: CommitteeHotAuthJSON, + ... + } + | { + CommitteeColdResign: CommitteeColdResignJSON, + ... + } + | { + DRepDeregistration: DRepDeregistrationJSON, + ... + } + | { + DRepRegistration: DRepRegistrationJSON, + ... + } + | { + DRepUpdate: DRepUpdateJSON, + ... + } + | { + StakeAndVoteDelegation: StakeAndVoteDelegationJSON, + ... + } + | { + StakeRegistrationAndDelegation: StakeRegistrationAndDelegationJSON, + ... + } + | { + StakeVoteRegistrationAndDelegation: StakeVoteRegistrationAndDelegationJSON, + ... + } + | { + VoteDelegation: VoteDelegationJSON, + ... + } + | { + VoteRegistrationAndDelegation: VoteRegistrationAndDelegationJSON, + ... + }; +export type CredTypeJSON = + | { + Key: string, + ... + } + | { + Script: string, + ... + }; +export type RelayJSON = + | { + SingleHostAddr: SingleHostAddrJSON, + ... + } + | { + SingleHostName: SingleHostNameJSON, + ... + } + | { + MultiHostName: MultiHostNameJSON, + ... + }; +/** + * @minItems 4 + * @maxItems 4 + */ +export type Ipv4JSON = [number, number, number, number]; +/** + * @minItems 16 + * @maxItems 16 + */ +export type Ipv6JSON = [ + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number +]; +export type DNSRecordAorAAAAJSON = string; +export type DNSRecordSRVJSON = string; +export type RelaysJSON = RelayJSON[]; +export type MIRPotJSON = "Reserves" | "Treasury"; +export type MIREnumJSON = + | { + ToOtherPot: string, + ... + } + | { + ToStakeCredentials: StakeToCoinJSON[], + ... + }; +export type DRepJSON = + | ("AlwaysAbstain" | "AlwaysNoConfidence") + | { + KeyHash: string, + ... + } + | { + ScriptHash: string, + ... + }; +export type DataOptionJSON = + | { + DataHash: string, + ... + } + | { + Data: string, + ... + }; +export type ScriptRefJSON = + | { + NativeScript: NativeScriptJSON, + ... + } + | { + PlutusScript: string, + ... + }; +export type MintJSON = [string, MintAssetsJSON][]; +export type NetworkIdJSON = "Testnet" | "Mainnet"; +export type TransactionOutputsJSON = TransactionOutputJSON[]; +export type CostModelJSON = string[]; +export type VoterJSON = + | { + ConstitutionalCommitteeHotCred: CredTypeJSON, + ... + } + | { + DRep: CredTypeJSON, + ... + } + | { + StakingPool: string, + ... + }; +export type VoteKindJSON = "No" | "Yes" | "Abstain"; +export type GovernanceActionJSON = + | { + ParameterChangeAction: ParameterChangeActionJSON, + ... + } + | { + HardForkInitiationAction: HardForkInitiationActionJSON, + ... + } + | { + TreasuryWithdrawalsAction: TreasuryWithdrawalsActionJSON, + ... + } + | { + NoConfidenceAction: NoConfidenceActionJSON, + ... + } + | { + UpdateCommitteeAction: UpdateCommitteeActionJSON, + ... + } + | { + NewConstitutionAction: NewConstitutionActionJSON, + ... + } + | { + InfoAction: InfoActionJSON, + ... + }; +/** + * @minItems 0 + * @maxItems 0 + */ +export type InfoActionJSON = []; +export type TransactionBodiesJSON = TransactionBodyJSON[]; +export type RedeemerTagJSON = + | "Spend" + | "Mint" + | "Cert" + | "Reward" + | "Vote" + | "VotingProposal"; +export type TransactionWitnessSetsJSON = TransactionWitnessSetJSON[]; +export interface BlockJSON { + auxiliary_data_set: { + [k: string]: AuxiliaryDataJSON, + }; + header: HeaderJSON; + invalid_transactions: number[]; + transaction_bodies: TransactionBodiesJSON; + transaction_witness_sets: TransactionWitnessSetsJSON; +} +export interface HeaderJSON { + body_signature: string; + header_body: HeaderBodyJSON; +} +export interface HeaderBodyJSON { + block_body_hash: string; + block_body_size: number; + block_number: number; + issuer_vkey: VkeyJSON; + leader_cert: HeaderLeaderCertEnumJSON; + operational_cert: OperationalCertJSON; + prev_hash?: string | null; + protocol_version: ProtocolVersionJSON; + slot: string; + vrf_vkey: string; +} +export interface VRFCertJSON { + output: number[]; + proof: number[]; +} +export interface OperationalCertJSON { + hot_vkey: string; + kes_period: number; + sequence_number: number; + sigma: string; +} +export interface ProtocolVersionJSON { + major: number; + minor: number; +} +export interface TransactionBodyJSON { + auxiliary_data_hash?: string | null; + certs?: CertificateJSON[] | null; + collateral?: TransactionInputJSON[] | null; + collateral_return?: TransactionOutputJSON | null; + current_treasury_value?: string | null; + donation?: string | null; + fee: string; + inputs: TransactionInputJSON[]; + mint?: MintJSON | null; + network_id?: NetworkIdJSON | null; + outputs: TransactionOutputsJSON; + reference_inputs?: TransactionInputJSON[] | null; + required_signers?: string[] | null; + script_data_hash?: string | null; + total_collateral?: string | null; + ttl?: string | null; + update?: UpdateJSON | null; + validity_start_interval?: string | null; + voting_procedures?: VoterVotesJSON[] | null; + voting_proposals?: VotingProposalJSON[] | null; + withdrawals?: { + [k: string]: string, + } | null; +} +export interface StakeRegistrationJSON { + coin?: string | null; + stake_credential: CredTypeJSON; +} +export interface StakeDeregistrationJSON { + coin?: string | null; + stake_credential: CredTypeJSON; +} +export interface StakeDelegationJSON { + pool_keyhash: string; + stake_credential: CredTypeJSON; +} +export interface PoolRegistrationJSON { + pool_params: PoolParamsJSON; +} +export interface PoolParamsJSON { + cost: string; + margin: UnitIntervalJSON; + operator: string; + pledge: string; + pool_metadata?: PoolMetadataJSON | null; + pool_owners: string[]; + relays: RelaysJSON; + reward_account: string; + vrf_keyhash: string; +} +export interface UnitIntervalJSON { + denominator: string; + numerator: string; +} +export interface PoolMetadataJSON { + pool_metadata_hash: string; + url: URLJSON; +} +export interface SingleHostAddrJSON { + ipv4?: Ipv4JSON | null; + ipv6?: Ipv6JSON | null; + port?: number | null; +} +export interface SingleHostNameJSON { + dns_name: DNSRecordAorAAAAJSON; + port?: number | null; +} +export interface MultiHostNameJSON { + dns_name: DNSRecordSRVJSON; +} +export interface PoolRetirementJSON { + epoch: number; + pool_keyhash: string; +} +export interface GenesisKeyDelegationJSON { + genesis_delegate_hash: string; + genesishash: string; + vrf_keyhash: string; +} +export interface MoveInstantaneousRewardsCertJSON { + move_instantaneous_reward: MoveInstantaneousRewardJSON; +} +export interface MoveInstantaneousRewardJSON { + pot: MIRPotJSON; + variant: MIREnumJSON; +} +export interface StakeToCoinJSON { + amount: string; + stake_cred: CredTypeJSON; +} +export interface CommitteeHotAuthJSON { + committee_cold_credential: CredTypeJSON; + committee_hot_credential: CredTypeJSON; +} +export interface CommitteeColdResignJSON { + anchor?: AnchorJSON | null; + committee_cold_credential: CredTypeJSON; +} +export interface DRepDeregistrationJSON { + coin: string; + voting_credential: CredTypeJSON; +} +export interface DRepRegistrationJSON { + anchor?: AnchorJSON | null; + coin: string; + voting_credential: CredTypeJSON; +} +export interface DRepUpdateJSON { + anchor?: AnchorJSON | null; + voting_credential: CredTypeJSON; +} +export interface StakeAndVoteDelegationJSON { + drep: DRepJSON; + pool_keyhash: string; + stake_credential: CredTypeJSON; +} +export interface StakeRegistrationAndDelegationJSON { + coin: string; + pool_keyhash: string; + stake_credential: CredTypeJSON; +} +export interface StakeVoteRegistrationAndDelegationJSON { + coin: string; + drep: DRepJSON; + pool_keyhash: string; + stake_credential: CredTypeJSON; +} +export interface VoteDelegationJSON { + drep: DRepJSON; + stake_credential: CredTypeJSON; +} +export interface VoteRegistrationAndDelegationJSON { + coin: string; + drep: DRepJSON; + stake_credential: CredTypeJSON; +} +export interface TransactionInputJSON { + index: number; + transaction_id: string; +} +export interface TransactionOutputJSON { + address: string; + amount: ValueJSON; + plutus_data?: DataOptionJSON | null; + script_ref?: ScriptRefJSON | null; +} +export interface ValueJSON { + coin: string; + multiasset?: MultiAssetJSON | null; +} +export interface MultiAssetJSON { + [k: string]: AssetsJSON; +} +export interface MintAssetsJSON { + [k: string]: string; +} +export interface UpdateJSON { + epoch: number; + proposed_protocol_parameter_updates: { + [k: string]: ProtocolParamUpdateJSON, + }; +} +export interface ProtocolParamUpdateJSON { + ada_per_utxo_byte?: string | null; + collateral_percentage?: number | null; + committee_term_limit?: number | null; + cost_models?: CostmdlsJSON | null; + d?: UnitIntervalJSON | null; + drep_deposit?: string | null; + drep_inactivity_period?: number | null; + drep_voting_thresholds?: DRepVotingThresholdsJSON | null; + execution_costs?: ExUnitPricesJSON | null; + expansion_rate?: UnitIntervalJSON | null; + extra_entropy?: NonceJSON | null; + governance_action_deposit?: string | null; + governance_action_validity_period?: number | null; + key_deposit?: string | null; + max_block_body_size?: number | null; + max_block_ex_units?: ExUnitsJSON | null; + max_block_header_size?: number | null; + max_collateral_inputs?: number | null; + max_epoch?: number | null; + max_tx_ex_units?: ExUnitsJSON | null; + max_tx_size?: number | null; + max_value_size?: number | null; + min_committee_size?: number | null; + min_pool_cost?: string | null; + minfee_a?: string | null; + minfee_b?: string | null; + n_opt?: number | null; + pool_deposit?: string | null; + pool_pledge_influence?: UnitIntervalJSON | null; + pool_voting_thresholds?: PoolVotingThresholdsJSON | null; + protocol_version?: ProtocolVersionJSON | null; + ref_script_coins_per_byte?: UnitIntervalJSON | null; + treasury_growth_rate?: UnitIntervalJSON | null; +} +export interface CostmdlsJSON { + [k: string]: CostModelJSON; +} +export interface DRepVotingThresholdsJSON { + committee_no_confidence: UnitIntervalJSON; + committee_normal: UnitIntervalJSON; + hard_fork_initiation: UnitIntervalJSON; + motion_no_confidence: UnitIntervalJSON; + pp_economic_group: UnitIntervalJSON; + pp_governance_group: UnitIntervalJSON; + pp_network_group: UnitIntervalJSON; + pp_technical_group: UnitIntervalJSON; + treasury_withdrawal: UnitIntervalJSON; + update_constitution: UnitIntervalJSON; +} +export interface ExUnitPricesJSON { + mem_price: UnitIntervalJSON; + step_price: UnitIntervalJSON; } -/** - */ -declare export class WithdrawalsBuilder { - free(): void; - - /** - * @returns {WithdrawalsBuilder} - */ - static new(): WithdrawalsBuilder; - - /** - * @param {RewardAddress} address - * @param {BigNum} coin - */ - add(address: RewardAddress, coin: BigNum): void; - - /** - * @param {RewardAddress} address - * @param {BigNum} coin - * @param {PlutusWitness} witness - */ - add_with_plutus_witness( - address: RewardAddress, - coin: BigNum, - witness: PlutusWitness - ): void; - - /** - * @param {RewardAddress} address - * @param {BigNum} coin - * @param {NativeScriptSource} native_script_source - */ - add_with_native_script( - address: RewardAddress, - coin: BigNum, - native_script_source: NativeScriptSource - ): void; - - /** - * @returns {PlutusWitnesses} - */ - get_plutus_witnesses(): PlutusWitnesses; - - /** - * @returns {TransactionInputs} - */ - get_ref_inputs(): TransactionInputs; - - /** - * @returns {NativeScripts} - */ - get_native_scripts(): NativeScripts; - - /** - * @returns {Value} - */ - get_total_withdrawals(): Value; - - /** - * @returns {boolean} - */ - has_plutus_scripts(): boolean; - +export interface NonceJSON { /** - * @returns {Withdrawals} + * @minItems 32 + * @maxItems 32 */ - build(): Withdrawals; + hash?: + | [ + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number, + number + ] + | null; } -export type AddressJSON = string; -export type AssetNameJSON = string; -export type AssetNamesJSON = string[]; -export interface AssetsJSON { +export interface ExUnitsJSON { + mem: string; + steps: string; +} +export interface PoolVotingThresholdsJSON { + committee_no_confidence: UnitIntervalJSON; + committee_normal: UnitIntervalJSON; + hard_fork_initiation: UnitIntervalJSON; + motion_no_confidence: UnitIntervalJSON; + security_relevant_threshold: UnitIntervalJSON; +} +export interface VoterVotesJSON { + voter: VoterJSON; + votes: VoteJSON[]; +} +export interface VoteJSON { + action_id: GovernanceActionIdJSON; + voting_procedure: VotingProcedureJSON; +} +export interface GovernanceActionIdJSON { + index: number; + transaction_id: string; +} +export interface VotingProcedureJSON { + anchor?: AnchorJSON | null; + vote: VoteKindJSON; +} +export interface VotingProposalJSON { + anchor: AnchorJSON; + deposit: string; + governance_action: GovernanceActionJSON; + reward_account: string; +} +export interface ParameterChangeActionJSON { + gov_action_id?: GovernanceActionIdJSON | null; + policy_hash?: string | null; + protocol_param_updates: ProtocolParamUpdateJSON; +} +export interface HardForkInitiationActionJSON { + gov_action_id?: GovernanceActionIdJSON | null; + protocol_version: ProtocolVersionJSON; +} +export interface TreasuryWithdrawalsActionJSON { + policy_hash?: string | null; + withdrawals: TreasuryWithdrawalsJSON; +} +export interface TreasuryWithdrawalsJSON { [k: string]: string; } -export interface AuxiliaryDataJSON { - metadata?: { - [k: string]: string, - } | null; - native_scripts?: NativeScriptsJSON | null; - plutus_scripts?: PlutusScriptsJSON | null; - prefer_alonzo_format: boolean; +export interface NoConfidenceActionJSON { + gov_action_id?: GovernanceActionIdJSON | null; } -export type AuxiliaryDataHashJSON = string; -export interface AuxiliaryDataSetJSON { - [k: string]: AuxiliaryDataJSON; +export interface UpdateCommitteeActionJSON { + committee: CommitteeJSON; + gov_action_id?: GovernanceActionIdJSON | null; + members_to_remove: CredTypeJSON[]; } -export type BigIntJSON = string; -export type BigNumJSON = string; -export interface BlockJSON { - auxiliary_data_set: { - [k: string]: AuxiliaryDataJSON, - }; - header: HeaderJSON; - invalid_transactions: number[]; - transaction_bodies: TransactionBodiesJSON; - transaction_witness_sets: TransactionWitnessSetsJSON; +export interface CommitteeJSON { + members: CommitteeMemberJSON[]; + quorum_threshold: UnitIntervalJSON; +} +export interface CommitteeMemberJSON { + stake_credential: CredTypeJSON; + term_limit: number; +} +export interface NewConstitutionActionJSON { + constitution: ConstitutionJSON; + gov_action_id?: GovernanceActionIdJSON | null; +} +export interface ConstitutionJSON { + anchor: AnchorJSON; + script_hash?: string | null; +} +export interface TransactionWitnessSetJSON { + bootstraps?: BootstrapWitnessJSON[] | null; + native_scripts?: NativeScriptsJSON | null; + plutus_data?: PlutusListJSON | null; + plutus_scripts?: PlutusScriptsJSON | null; + redeemers?: RedeemerJSON[] | null; + vkeys?: VkeywitnessJSON[] | null; } -export type BlockHashJSON = string; export interface BootstrapWitnessJSON { attributes: number[]; chain_code: number[]; signature: string; vkey: VkeyJSON; } +export interface PlutusListJSON { + definite_encoding?: boolean | null; + elems: string[]; +} +export interface RedeemerJSON { + data: string; + ex_units: ExUnitsJSON; + index: string; + tag: RedeemerTagJSON; +} +export interface VkeywitnessJSON { + signature: string; + vkey: VkeyJSON; +} +export type BlockHashJSON = string; export type BootstrapWitnessesJSON = BootstrapWitnessJSON[]; -export type CertificateJSON = CertificateEnumJSON; export type CertificateEnumJSON = | { - StakeRegistrationJSON: StakeRegistration, + StakeRegistration: StakeRegistrationJSON, ... } | { - StakeDeregistrationJSON: StakeDeregistration, + StakeDeregistration: StakeDeregistrationJSON, ... } | { - StakeDelegationJSON: StakeDelegation, + StakeDelegation: StakeDelegationJSON, ... } | { - PoolRegistrationJSON: PoolRegistration, + PoolRegistration: PoolRegistrationJSON, ... } | { - PoolRetirementJSON: PoolRetirement, + PoolRetirement: PoolRetirementJSON, ... } | { - GenesisKeyDelegationJSON: GenesisKeyDelegation, + GenesisKeyDelegation: GenesisKeyDelegationJSON, ... } | { - MoveInstantaneousRewardsCertJSON: MoveInstantaneousRewardsCert, + MoveInstantaneousRewardsCert: MoveInstantaneousRewardsCertJSON, ... - }; -export type CertificatesJSON = CertificateJSON[]; -export type CostModelJSON = string[]; -export interface CostmdlsJSON { - [k: string]: CostModelJSON; -} -export type DNSRecordAorAAAAJSON = string; -export type DNSRecordSRVJSON = string; -export type DataHashJSON = string; -export type DataOptionJSON = + } | { - DataHashJSON: string, + CommitteeHotAuth: CommitteeHotAuthJSON, ... } | { - Data: string, + CommitteeColdResign: CommitteeColdResignJSON, ... - }; -export type Ed25519KeyHashJSON = string; -export type Ed25519KeyHashesJSON = string[]; -export type Ed25519SignatureJSON = string; -export interface ExUnitPricesJSON { - mem_price: UnitIntervalJSON; - step_price: UnitIntervalJSON; -} -export interface ExUnitsJSON { - mem: string; - steps: string; -} -export interface GeneralTransactionMetadataJSON { - [k: string]: string; -} -export type GenesisDelegateHashJSON = string; -export type GenesisHashJSON = string; -export type GenesisHashesJSON = string[]; -export interface GenesisKeyDelegationJSON { - genesis_delegate_hash: string; - genesishash: string; - vrf_keyhash: string; -} -export interface HeaderJSON { - body_signature: string; - header_body: HeaderBodyJSON; -} -export interface HeaderBodyJSON { - block_body_hash: string; - block_body_size: number; - block_number: number; - issuer_vkey: VkeyJSON; - leader_cert: HeaderLeaderCertEnumJSON; - operational_cert: OperationalCertJSON; - prev_hash?: string | null; - protocol_version: ProtocolVersionJSON; - slot: string; - vrf_vkey: string; -} -export type HeaderLeaderCertEnumJSON = + } | { - NonceAndLeader: [VRFCertJSON, VRFCert], + DRepDeregistration: DRepDeregistrationJSON, ... } | { - VrfResult: VRFCertJSON, + DRepRegistration: DRepRegistrationJSON, ... - }; -export type IntJSON = string; -export type Ipv4JSON = [number, number, number, number]; -export type Ipv6JSON = [ - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number -]; -export type KESVKeyJSON = string; -export type LanguageJSON = LanguageKindJSON; -export type LanguageKindJSON = "PlutusV1" | "PlutusV2"; -export type LanguagesJSON = LanguageJSON[]; -export type MIREnumJSON = + } + | { + DRepUpdate: DRepUpdateJSON, + ... + } + | { + StakeAndVoteDelegation: StakeAndVoteDelegationJSON, + ... + } + | { + StakeRegistrationAndDelegation: StakeRegistrationAndDelegationJSON, + ... + } + | { + StakeVoteRegistrationAndDelegation: StakeVoteRegistrationAndDelegationJSON, + ... + } + | { + VoteDelegation: VoteDelegationJSON, + ... + } | { - ToOtherPot: string, + VoteRegistrationAndDelegation: VoteRegistrationAndDelegationJSON, + ... + }; +export type CertificatesJSON = CertificateJSON[]; +export type CredentialJSON = CredTypeJSON; +export type CredentialsJSON = CredTypeJSON[]; +export type DRepEnumJSON = + | ("AlwaysAbstain" | "AlwaysNoConfidence") + | { + KeyHash: string, ... } | { - ToStakeCredentials: { - [k: string]: ProtocolParamUpdateJSON, - }, + ScriptHash: string, ... }; -export type MIRPotJSON = "Reserves" | "Treasury"; -export interface MIRToStakeCredentialsJSON { - [k: string]: ProtocolParamUpdateJSON; -} -export type MintJSON = [string, MintAssetsJSON][]; -export interface MintAssetsJSON { +export type DataHashJSON = string; +export type Ed25519KeyHashJSON = string; +export type Ed25519KeyHashesJSON = string[]; +export type Ed25519SignatureJSON = string; +export interface GeneralTransactionMetadataJSON { [k: string]: string; } -export interface MoveInstantaneousRewardJSON { - pot: MIRPotJSON; - variant: MIREnumJSON; -} -export interface MoveInstantaneousRewardsCertJSON { - move_instantaneous_reward: MoveInstantaneousRewardJSON; -} -export interface MultiAssetJSON { - [k: string]: AssetsJSON; -} -export interface MultiHostNameJSON { - dns_name: DNSRecordSRVJSON; -} -export type NativeScriptJSON = NativeScript1JSON; -export type NativeScript1JSON = +export type GenesisDelegateHashJSON = string; +export type GenesisHashJSON = string; +export type GenesisHashesJSON = string[]; +export type GovernanceActionEnumJSON = + | { + ParameterChangeAction: ParameterChangeActionJSON, + ... + } | { - ScriptPubkeyJSON: ScriptPubkey, + HardForkInitiationAction: HardForkInitiationActionJSON, ... } | { - ScriptAllJSON: ScriptAll, + TreasuryWithdrawalsAction: TreasuryWithdrawalsActionJSON, ... } | { - ScriptAnyJSON: ScriptAny, + NoConfidenceAction: NoConfidenceActionJSON, ... } | { - ScriptNOfKJSON: ScriptNOfK, + UpdateCommitteeAction: UpdateCommitteeActionJSON, ... } | { - TimelockStartJSON: TimelockStart, + NewConstitutionAction: NewConstitutionActionJSON, ... } | { - TimelockExpiryJSON: TimelockExpiry, + InfoAction: InfoActionJSON, ... }; -export type NativeScriptsJSON = NativeScriptJSON[]; -export type NetworkIdJSON = NetworkIdKindJSON; +export type GovernanceActionIdsJSON = GovernanceActionIdJSON[]; +export type IntJSON = string; +/** + * @minItems 4 + * @maxItems 4 + */ +export type KESVKeyJSON = string; +export type LanguageJSON = LanguageKindJSON; +export type LanguageKindJSON = "PlutusV1" | "PlutusV2" | "PlutusV3"; +export type LanguagesJSON = LanguageJSON[]; +export type MIRToStakeCredentialsJSON = StakeToCoinJSON[]; +export type MintsAssetsJSON = MintAssetsJSON[]; export type NetworkIdKindJSON = "Testnet" | "Mainnet"; -export interface NonceJSON { - hash?: - | [ - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number, - number - ] - | null; -} -export interface OperationalCertJSON { - hot_vkey: string; - kes_period: number; - sequence_number: number; - sigma: string; -} export type PlutusScriptJSON = string; -export type PlutusScriptsJSON = string[]; -export interface PoolMetadataJSON { - pool_metadata_hash: string; - url: URLJSON; -} export type PoolMetadataHashJSON = string; -export interface PoolParamsJSON { - cost: string; - margin: UnitIntervalJSON; - operator: string; - pledge: string; - pool_metadata?: PoolMetadataJSON | null; - pool_owners: Ed25519KeyHashesJSON; - relays: RelaysJSON; - reward_account: string; - vrf_keyhash: string; -} -export interface PoolRegistrationJSON { - pool_params: PoolParamsJSON; -} -export interface PoolRetirementJSON { - epoch: number; - pool_keyhash: string; -} export interface ProposedProtocolParameterUpdatesJSON { [k: string]: ProtocolParamUpdateJSON; } -export interface ProtocolParamUpdateJSON { - ada_per_utxo_byte?: string | null; - collateral_percentage?: number | null; - cost_models?: CostmdlsJSON | null; - d?: UnitIntervalJSON | null; - execution_costs?: ExUnitPricesJSON | null; - expansion_rate?: UnitIntervalJSON | null; - extra_entropy?: NonceJSON | null; - key_deposit?: string | null; - max_block_body_size?: number | null; - max_block_ex_units?: ExUnitsJSON | null; - max_block_header_size?: number | null; - max_collateral_inputs?: number | null; - max_epoch?: number | null; - max_tx_ex_units?: ExUnitsJSON | null; - max_tx_size?: number | null; - max_value_size?: number | null; - min_pool_cost?: string | null; - minfee_a?: string | null; - minfee_b?: string | null; - n_opt?: number | null; - pool_deposit?: string | null; - pool_pledge_influence?: UnitIntervalJSON | null; - protocol_version?: ProtocolVersionJSON | null; - treasury_growth_rate?: UnitIntervalJSON | null; -} -export interface ProtocolVersionJSON { - major: number; - minor: number; -} export type PublicKeyJSON = string; -export interface RedeemerJSON { - data: string; - ex_units: ExUnitsJSON; - index: string; - tag: RedeemerTagJSON; -} -export type RedeemerTagJSON = RedeemerTagKindJSON; -export type RedeemerTagKindJSON = "Spend" | "MintJSON" | "Cert" | "Reward"; +export type RedeemerTagKindJSON = + | "Spend" + | "Mint" + | "Cert" + | "Reward" + | "Vote" + | "VotingProposal"; export type RedeemersJSON = RedeemerJSON[]; -export type RelayJSON = RelayEnumJSON; export type RelayEnumJSON = | { - SingleHostAddrJSON: SingleHostAddr, + SingleHostAddr: SingleHostAddrJSON, ... } | { - SingleHostNameJSON: SingleHostName, + SingleHostName: SingleHostNameJSON, ... } | { - MultiHostNameJSON: MultiHostName, + MultiHostName: MultiHostNameJSON, ... }; -export type RelaysJSON = RelayJSON[]; +/** + * @minItems 4 + * @maxItems 4 + */ export type RewardAddressJSON = string; export type RewardAddressesJSON = string[]; -export interface ScriptAllJSON { - native_scripts: NativeScriptsJSON; -} -export interface ScriptAnyJSON { - native_scripts: NativeScriptsJSON; -} export type ScriptDataHashJSON = string; export type ScriptHashJSON = string; export type ScriptHashesJSON = string[]; -export interface ScriptNOfKJSON { - n: number; - native_scripts: NativeScriptsJSON; -} -export interface ScriptPubkeyJSON { - addr_keyhash: string; -} -export type ScriptRefJSON = ScriptRefEnumJSON; export type ScriptRefEnumJSON = | { - NativeScriptJSON: NativeScript, - ... - } - | { - PlutusScriptJSON: string, - ... - }; -export interface SingleHostAddrJSON { - ipv4?: Ipv4JSON | null; - ipv6?: Ipv6JSON | null; - port?: number | null; -} -export interface SingleHostNameJSON { - dns_name: DNSRecordAorAAAAJSON; - port?: number | null; -} -export type StakeCredTypeJSON = - | { - Key: string, + NativeScript: NativeScriptJSON, ... } | { - Script: string, + PlutusScript: string, ... }; -export type StakeCredentialJSON = StakeCredTypeJSON; -export type StakeCredentialsJSON = StakeCredTypeJSON[]; -export interface StakeDelegationJSON { - pool_keyhash: string; - stake_credential: StakeCredTypeJSON; -} -export interface StakeDeregistrationJSON { - stake_credential: StakeCredTypeJSON; -} -export interface StakeRegistrationJSON { - stake_credential: StakeCredTypeJSON; -} -export interface TimelockExpiryJSON { - slot: string; -} -export interface TimelockStartJSON { - slot: string; -} export interface TransactionJSON { auxiliary_data?: AuxiliaryDataJSON | null; body: TransactionBodyJSON; is_valid: boolean; witness_set: TransactionWitnessSetJSON; } -export type TransactionBodiesJSON = TransactionBodyJSON[]; -export interface TransactionBodyJSON { - auxiliary_data_hash?: string | null; - certs?: CertificatesJSON | null; - collateral?: TransactionInputsJSON | null; - collateral_return?: TransactionOutputJSON | null; - fee: string; - inputs: TransactionInputsJSON; - mint?: MintJSON | null; - network_id?: NetworkIdJSON | null; - outputs: TransactionOutputsJSON; - reference_inputs?: TransactionInputsJSON | null; - required_signers?: Ed25519KeyHashesJSON | null; - script_data_hash?: string | null; - total_collateral?: string | null; - ttl?: string | null; - update?: UpdateJSON | null; - validity_start_interval?: string | null; - withdrawals?: { - [k: string]: string, - ... - } | null; -} export type TransactionHashJSON = string; -export interface TransactionInputJSON { - index: number; - transaction_id: string; -} export type TransactionInputsJSON = TransactionInputJSON[]; export type TransactionMetadatumJSON = string; -export interface TransactionOutputJSON { - address: string; - amount: ValueJSON; - plutus_data?: DataOptionJSON | null; - script_ref?: ScriptRefJSON | null; -} -export type TransactionOutputsJSON = TransactionOutputJSON[]; export interface TransactionUnspentOutputJSON { input: TransactionInputJSON; output: TransactionOutputJSON; } export type TransactionUnspentOutputsJSON = TransactionUnspentOutputJSON[]; -export interface TransactionWitnessSetJSON { - bootstraps?: BootstrapWitnessesJSON | null; - native_scripts?: NativeScriptsJSON | null; - plutus_data?: PlutusList | null; - plutus_scripts?: PlutusScriptsJSON | null; - redeemers?: RedeemersJSON | null; - vkeys?: VkeywitnessesJSON | null; -} -export type TransactionWitnessSetsJSON = TransactionWitnessSetJSON[]; -export type URLJSON = string; -export interface UnitIntervalJSON { - denominator: string; - numerator: string; -} -export interface UpdateJSON { - epoch: number; - proposed_protocol_parameter_updates: { - [k: string]: ProtocolParamUpdateJSON, - }; -} -export interface VRFCertJSON { - output: number[]; - proof: number[]; -} export type VRFKeyHashJSON = string; export type VRFVKeyJSON = string; -export interface ValueJSON { - coin: string; - multiasset?: MultiAssetJSON | null; -} -export type VkeyJSON = string; -export interface VkeywitnessJSON { - signature: string; - vkey: VkeyJSON; +export interface VersionedBlockJSON { + block: BlockJSON; + era_code: number; } export type VkeywitnessesJSON = VkeywitnessJSON[]; +export type VoterEnumJSON = + | { + ConstitutionalCommitteeHotCred: CredTypeJSON, + ... + } + | { + DRep: CredTypeJSON, + ... + } + | { + StakingPool: string, + ... + }; +export type VotersJSON = VoterJSON[]; +export type VotingProceduresJSON = VoterVotesJSON[]; +export type VotingProposalsJSON = VotingProposalJSON[]; export interface WithdrawalsJSON { [k: string]: string; } diff --git a/rust/src/address.rs b/rust/src/address.rs deleted file mode 100644 index 59d8009a..00000000 --- a/rust/src/address.rs +++ /dev/null @@ -1,1482 +0,0 @@ -use super::*; -use crate::legacy_address::ExtendedAddr; -use bech32::ToBase32; -use ed25519_bip32::XPub; - -// returns (Number represented, bytes read) if valid encoding -// or None if decoding prematurely finished -fn variable_nat_decode(bytes: &[u8]) -> Option<(u64, usize)> { - let mut output = 0u128; - let mut bytes_read = 0; - for byte in bytes { - output = (output << 7) | (byte & 0x7F) as u128; - if output > u64::MAX.into() { - return None; - } - bytes_read += 1; - if (byte & 0x80) == 0 { - return Some((output as u64, bytes_read)); - } - } - None -} - -fn variable_nat_encode(mut num: u64) -> Vec { - let mut output = vec![num as u8 & 0x7F]; - num /= 128; - while num > 0 { - output.push((num & 0x7F) as u8 | 0x80); - num /= 128; - } - output.reverse(); - output -} - -#[wasm_bindgen] -#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct NetworkInfo { - network_id: u8, - protocol_magic: u32, -} -#[wasm_bindgen] -impl NetworkInfo { - pub fn new(network_id: u8, protocol_magic: u32) -> Self { - Self { - network_id, - protocol_magic, - } - } - pub fn network_id(&self) -> u8 { - self.network_id - } - pub fn protocol_magic(&self) -> u32 { - self.protocol_magic - } - - pub fn testnet_preview() -> NetworkInfo { - NetworkInfo { - network_id: 0b0000, - protocol_magic: 2, - } - } - pub fn testnet_preprod() -> NetworkInfo { - NetworkInfo { - network_id: 0b0000, - protocol_magic: 1, - } - } - /// !!! DEPRECATED !!! - /// This network does not exist anymore. Use `.testnet_preview()` or `.testnet_preprod()` - #[deprecated(since = "11.2.0", note = "Use `.testnet_preview` or `.testnet_preprod`")] - pub fn testnet() -> NetworkInfo { - NetworkInfo { - network_id: 0b0000, - protocol_magic: 1097911063, - } - } - pub fn mainnet() -> NetworkInfo { - NetworkInfo { - network_id: 0b0001, - protocol_magic: 764824073, - } - } -} - -#[derive( - Debug, - Clone, - Hash, - Eq, - Ord, - PartialEq, - PartialOrd, - serde::Serialize, - serde::Deserialize, - JsonSchema, -)] -pub enum StakeCredType { - Key(Ed25519KeyHash), - Script(ScriptHash), -} - -#[wasm_bindgen] -#[repr(u8)] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum StakeCredKind { - Key, - Script, -} - -#[wasm_bindgen] -#[derive( - Debug, - Clone, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - serde::Serialize, - serde::Deserialize, - JsonSchema, -)] -pub struct StakeCredential(pub(crate) StakeCredType); - -#[wasm_bindgen] -impl StakeCredential { - pub fn from_keyhash(hash: &Ed25519KeyHash) -> Self { - StakeCredential(StakeCredType::Key(hash.clone())) - } - - pub fn from_scripthash(hash: &ScriptHash) -> Self { - StakeCredential(StakeCredType::Script(hash.clone())) - } - - pub fn to_keyhash(&self) -> Option { - match &self.0 { - StakeCredType::Key(hash) => Some(hash.clone()), - StakeCredType::Script(_) => None, - } - } - - pub fn to_scripthash(&self) -> Option { - match &self.0 { - StakeCredType::Key(_) => None, - StakeCredType::Script(hash) => Some(hash.clone()), - } - } - - pub fn kind(&self) -> StakeCredKind { - match &self.0 { - StakeCredType::Key(_) => StakeCredKind::Key, - StakeCredType::Script(_) => StakeCredKind::Script, - } - } - - pub fn has_script_hash(&self) -> bool { - match &self.0 { - StakeCredType::Key(_) => false, - StakeCredType::Script(_) => true, - } - } - - fn to_raw_bytes(&self) -> Vec { - match &self.0 { - StakeCredType::Key(hash) => hash.to_bytes(), - StakeCredType::Script(hash) => hash.to_bytes(), - } - } -} - -impl_to_from!(StakeCredential); - -impl cbor_event::se::Serialize for StakeCredential { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - match &self.0 { - StakeCredType::Key(keyhash) => { - serializer.write_unsigned_integer(0u64)?; - serializer.write_bytes(keyhash.to_bytes()) - } - StakeCredType::Script(scripthash) => { - serializer.write_unsigned_integer(1u64)?; - serializer.write_bytes(scripthash.to_bytes()) - } - } - } -} - -impl Deserialize for StakeCredential { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - if let cbor_event::Len::Len(n) = len { - if n != 2 { - return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( - 2, - len, - "[id, hash]", - )) - .into()); - } - } - let cred_type = match raw.unsigned_integer()? { - 0 => StakeCredType::Key(Ed25519KeyHash::deserialize(raw)?), - 1 => StakeCredType::Script(ScriptHash::deserialize(raw)?), - n => { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(n), - // TODO: change codegen to make FixedValueMismatch support Vec or ranges or something - expected: Key::Uint(0), - } - .into()) - } - }; - if let cbor_event::Len::Indefinite = len { - if raw.special()? != CBORSpecial::Break { - return Err(DeserializeFailure::EndingBreakMissing.into()); - } - } - Ok(StakeCredential(cred_type)) - })() - .map_err(|e| e.annotate("StakeCredential")) - } -} - -#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] -pub(crate) enum AddrType { - Base(BaseAddress), - Ptr(PointerAddress), - Enterprise(EnterpriseAddress), - Reward(RewardAddress), - Byron(ByronAddress), -} - -#[wasm_bindgen] -#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] -pub struct ByronAddress(pub(crate) ExtendedAddr); -#[wasm_bindgen] -impl ByronAddress { - pub fn to_base58(&self) -> String { - format!("{}", self.0) - } - pub fn to_bytes(&self) -> Vec { - let mut addr_bytes = Serializer::new_vec(); - self.0.serialize(&mut addr_bytes).unwrap(); - addr_bytes.finalize() - } - pub fn from_bytes(bytes: Vec) -> Result { - let mut raw = Deserializer::from(std::io::Cursor::new(bytes)); - let extended_addr = ExtendedAddr::deserialize(&mut raw)?; - Ok(ByronAddress(extended_addr)) - } - /// returns the byron protocol magic embedded in the address, or mainnet id if none is present - /// note: for bech32 addresses, you need to use network_id instead - pub fn byron_protocol_magic(&self) -> u32 { - match self.0.attributes.protocol_magic { - Some(x) => x, - None => NetworkInfo::mainnet().protocol_magic(), // mainnet is implied if omitted - } - } - pub fn attributes(&self) -> Vec { - let mut attributes_bytes = Serializer::new_vec(); - self.0.attributes.serialize(&mut attributes_bytes).unwrap(); - attributes_bytes.finalize() - } - pub fn network_id(&self) -> Result { - // premise: during the Byron-era, we had one mainnet (764824073) and many many testnets - // with each testnet getting a different protocol magic - // in Shelley, this changes so that: - // 1) all testnets use the same u8 protocol magic - // 2) mainnet is re-mapped to a single u8 protocol magic - - // recall: in Byron mainnet, the network_id is omitted from the address to save a few bytes - // so here we return the mainnet id if none is found in the address - - // mainnet is implied if omitted - let protocol_magic = self.byron_protocol_magic(); - match protocol_magic { - magic if magic == NetworkInfo::mainnet().protocol_magic() => { - Ok(NetworkInfo::mainnet().network_id()) - } - magic if magic == NetworkInfo::testnet().protocol_magic() => { - Ok(NetworkInfo::testnet().network_id()) - } - magic if magic == NetworkInfo::testnet_preprod().protocol_magic() => { - Ok(NetworkInfo::testnet_preprod().network_id()) - } - magic if magic == NetworkInfo::testnet_preview().protocol_magic() => { - Ok(NetworkInfo::testnet_preview().network_id()) - } - _ => Err(JsError::from_str( - &format! {"Unknown network {}", protocol_magic}, - )), - } - } - - pub fn from_base58(s: &str) -> Result { - use std::str::FromStr; - ExtendedAddr::from_str(s) - .map_err(|e| JsError::from_str(&format! {"{:?}", e})) - .map(ByronAddress) - } - - // icarus-style address (Ae2) - pub fn icarus_from_key(key: &Bip32PublicKey, protocol_magic: u32) -> ByronAddress { - let mut out = [0u8; 64]; - out.clone_from_slice(&key.as_bytes()); - - // need to ensure we use None for mainnet since Byron-era addresses omitted the network id - let filtered_protocol_magic = if protocol_magic == NetworkInfo::mainnet().protocol_magic() { - None - } else { - Some(protocol_magic) - }; - ByronAddress(ExtendedAddr::new_simple( - &XPub::from_bytes(out), - filtered_protocol_magic, - )) - } - - pub fn is_valid(s: &str) -> bool { - use std::str::FromStr; - match ExtendedAddr::from_str(s) { - Ok(_v) => true, - Err(_err) => false, - } - } - - pub fn to_address(&self) -> Address { - Address(AddrType::Byron(self.clone())) - } - - pub fn from_address(addr: &Address) -> Option { - match &addr.0 { - AddrType::Byron(byron) => Some(byron.clone()), - _ => None, - } - } -} - -#[wasm_bindgen] -#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] -pub struct Address(pub(crate) AddrType); - -from_bytes!(Address, data, { Self::from_bytes_impl(data.as_ref()) }); - -to_from_json!(Address); - -impl serde::Serialize for Address { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let bech32 = self - .to_bech32(None) - .map_err(|e| serde::ser::Error::custom(format!("to_bech32: {:?}", e)))?; - serializer.serialize_str(&bech32) - } -} - -impl<'de> serde::de::Deserialize<'de> for Address { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let bech32 = ::deserialize(deserializer)?; - Address::from_bech32(&bech32).map_err(|_e| { - serde::de::Error::invalid_value( - serde::de::Unexpected::Str(&bech32), - &"bech32 address string", - ) - }) - } -} - -impl JsonSchema for Address { - fn schema_name() -> String { - String::from("Address") - } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - String::json_schema(gen) - } - fn is_referenceable() -> bool { - String::is_referenceable() - } -} - -// to/from_bytes() are the raw encoding without a wrapping CBOR Bytes tag -// while Serialize and Deserialize traits include that for inclusion with -// other CBOR types -#[wasm_bindgen] -impl Address { - pub fn to_hex(&self) -> String { - hex::encode(self.to_bytes()) - } - - pub fn from_hex(hex_str: &str) -> Result { - match hex::decode(hex_str) { - Ok(data) => Ok(Self::from_bytes_impl(data.as_ref())?), - Err(e) => Err(JsError::from_str(&e.to_string())), - } - } - - pub fn to_bytes(&self) -> Vec { - let mut buf = Vec::new(); - match &self.0 { - AddrType::Base(base) => { - let header: u8 = ((base.payment.kind() as u8) << 4) - | ((base.stake.kind() as u8) << 5) - | (base.network & 0xF); - buf.push(header); - buf.extend(base.payment.to_raw_bytes()); - buf.extend(base.stake.to_raw_bytes()); - } - AddrType::Ptr(ptr) => { - let header: u8 = - 0b0100_0000 | ((ptr.payment.kind() as u8) << 4) | (ptr.network & 0xF); - buf.push(header); - buf.extend(ptr.payment.to_raw_bytes()); - buf.extend(variable_nat_encode(from_bignum(&ptr.stake.slot))); - buf.extend(variable_nat_encode(from_bignum(&ptr.stake.tx_index))); - buf.extend(variable_nat_encode(from_bignum(&ptr.stake.cert_index))); - } - AddrType::Enterprise(enterprise) => { - let header: u8 = 0b0110_0000 - | ((enterprise.payment.kind() as u8) << 4) - | (enterprise.network & 0xF); - buf.push(header); - buf.extend(enterprise.payment.to_raw_bytes()); - } - AddrType::Reward(reward) => { - let header: u8 = - 0b1110_0000 | ((reward.payment.kind() as u8) << 4) | (reward.network & 0xF); - buf.push(header); - buf.extend(reward.payment.to_raw_bytes()); - } - AddrType::Byron(byron) => buf.extend(byron.to_bytes()), - } - buf - } - - fn from_bytes_impl(data: &[u8]) -> Result { - use std::convert::TryInto; - // header has 4 bits addr type discrim then 4 bits network discrim. - // Copied from shelley.cddl: - // - // shelley payment addresses: - // bit 7: 0 - // bit 6: base/other - // bit 5: pointer/enterprise [for base: stake cred is keyhash/scripthash] - // bit 4: payment cred is keyhash/scripthash - // bits 3-0: network id - // - // reward addresses: - // bits 7-5: 111 - // bit 4: credential is keyhash/scripthash - // bits 3-0: network id - // - // byron addresses: - // bits 7-4: 1000 - (|| -> Result { - let header = data[0]; - let network = header & 0x0F; - const HASH_LEN: usize = Ed25519KeyHash::BYTE_COUNT; - // should be static assert but it's maybe not worth importing a whole external crate for it now - assert_eq!(ScriptHash::BYTE_COUNT, HASH_LEN); - // checks the /bit/ bit of the header for key vs scripthash then reads the credential starting at byte position /pos/ - let read_addr_cred = |bit: u8, pos: usize| { - let hash_bytes: [u8; HASH_LEN] = data[pos..pos + HASH_LEN].try_into().unwrap(); - let x = if header & (1 << bit) == 0 { - StakeCredential::from_keyhash(&Ed25519KeyHash::from(hash_bytes)) - } else { - StakeCredential::from_scripthash(&ScriptHash::from(hash_bytes)) - }; - x - }; - let addr = match (header & 0xF0) >> 4 { - // base - 0b0000 | 0b0001 | 0b0010 | 0b0011 => { - const BASE_ADDR_SIZE: usize = 1 + HASH_LEN * 2; - if data.len() < BASE_ADDR_SIZE { - return Err(cbor_event::Error::NotEnough(data.len(), BASE_ADDR_SIZE).into()); - } - if data.len() > BASE_ADDR_SIZE { - return Err(cbor_event::Error::TrailingData.into()); - } - AddrType::Base(BaseAddress::new( - network, - &read_addr_cred(4, 1), - &read_addr_cred(5, 1 + HASH_LEN), - )) - } - // pointer - 0b0100 | 0b0101 => { - // header + keyhash + 3 natural numbers (min 1 byte each) - const PTR_ADDR_MIN_SIZE: usize = 1 + HASH_LEN + 1 + 1 + 1; - if data.len() < PTR_ADDR_MIN_SIZE { - // possibly more, but depends on how many bytes the natural numbers are for the pointer - return Err( - cbor_event::Error::NotEnough(data.len(), PTR_ADDR_MIN_SIZE).into() - ); - } - let mut byte_index = 1; - let payment_cred = read_addr_cred(4, 1); - byte_index += HASH_LEN; - let (slot, slot_bytes) = - variable_nat_decode(&data[byte_index..]).ok_or(DeserializeError::new( - "Address.Pointer.slot", - DeserializeFailure::VariableLenNatDecodeFailed, - ))?; - byte_index += slot_bytes; - let (tx_index, tx_bytes) = - variable_nat_decode(&data[byte_index..]).ok_or(DeserializeError::new( - "Address.Pointer.tx_index", - DeserializeFailure::VariableLenNatDecodeFailed, - ))?; - byte_index += tx_bytes; - let (cert_index, cert_bytes) = - variable_nat_decode(&data[byte_index..]).ok_or(DeserializeError::new( - "Address.Pointer.cert_index", - DeserializeFailure::VariableLenNatDecodeFailed, - ))?; - byte_index += cert_bytes; - if byte_index < data.len() { - return Err(cbor_event::Error::TrailingData.into()); - } - AddrType::Ptr(PointerAddress::new( - network, - &payment_cred, - &Pointer::new_pointer( - &to_bignum(slot), - &to_bignum(tx_index), - &to_bignum(cert_index), - ), - )) - } - // enterprise - 0b0110 | 0b0111 => { - const ENTERPRISE_ADDR_SIZE: usize = 1 + HASH_LEN; - if data.len() < ENTERPRISE_ADDR_SIZE { - return Err( - cbor_event::Error::NotEnough(data.len(), ENTERPRISE_ADDR_SIZE).into(), - ); - } - if data.len() > ENTERPRISE_ADDR_SIZE { - return Err(cbor_event::Error::TrailingData.into()); - } - AddrType::Enterprise(EnterpriseAddress::new(network, &read_addr_cred(4, 1))) - } - // reward - 0b1110 | 0b1111 => { - const REWARD_ADDR_SIZE: usize = 1 + HASH_LEN; - if data.len() < REWARD_ADDR_SIZE { - return Err( - cbor_event::Error::NotEnough(data.len(), REWARD_ADDR_SIZE).into() - ); - } - if data.len() > REWARD_ADDR_SIZE { - return Err(cbor_event::Error::TrailingData.into()); - } - AddrType::Reward(RewardAddress::new(network, &read_addr_cred(4, 1))) - } - // byron - 0b1000 => { - // note: 0b1000 was chosen because all existing Byron addresses actually start with 0b1000 - // Therefore you can re-use Byron addresses as-is - match ByronAddress::from_bytes(data.to_vec()) { - Ok(addr) => AddrType::Byron(addr), - Err(e) => { - return Err(cbor_event::Error::CustomError( - e.as_string().unwrap_or_default(), - ) - .into()) - } - } - } - _ => return Err(DeserializeFailure::BadAddressType(header).into()), - }; - Ok(Address(addr)) - })() - .map_err(|e| e.annotate("Address")) - } - - pub fn to_bech32(&self, prefix: Option) -> Result { - let final_prefix = match prefix { - Some(prefix) => prefix, - None => { - // see CIP5 for bech32 prefix rules - let prefix_header = match &self.0 { - AddrType::Reward(_) => "stake", - _ => "addr", - }; - let prefix_tail = match self.network_id()? { - id if id == NetworkInfo::testnet().network_id() => "_test", - id if id == NetworkInfo::testnet_preprod().network_id() => "_test", - id if id == NetworkInfo::testnet_preview().network_id() => "_test", - _ => "", - }; - format!("{}{}", prefix_header, prefix_tail) - } - }; - bech32::encode(&final_prefix, self.to_bytes().to_base32()) - .map_err(|e| JsError::from_str(&format! {"{:?}", e})) - } - - pub fn from_bech32(bech_str: &str) -> Result { - let (_hrp, u5data) = - bech32::decode(bech_str).map_err(|e| JsError::from_str(&e.to_string()))?; - let data: Vec = bech32::FromBase32::from_base32(&u5data).unwrap(); - Ok(Self::from_bytes_impl(data.as_ref())?) - } - - pub fn network_id(&self) -> Result { - match &self.0 { - AddrType::Base(a) => Ok(a.network), - AddrType::Enterprise(a) => Ok(a.network), - AddrType::Ptr(a) => Ok(a.network), - AddrType::Reward(a) => Ok(a.network), - AddrType::Byron(a) => a.network_id(), - } - } -} - -impl cbor_event::se::Serialize for Address { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_bytes(self.to_bytes()) - } -} - -impl Deserialize for Address { - fn deserialize(raw: &mut Deserializer) -> Result { - Self::from_bytes_impl(raw.bytes()?.as_ref()) - } -} - -#[wasm_bindgen] -#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] -pub struct BaseAddress { - network: u8, - payment: StakeCredential, - stake: StakeCredential, -} - -#[wasm_bindgen] -impl BaseAddress { - pub fn new(network: u8, payment: &StakeCredential, stake: &StakeCredential) -> Self { - Self { - network, - payment: payment.clone(), - stake: stake.clone(), - } - } - - pub fn payment_cred(&self) -> StakeCredential { - self.payment.clone() - } - - pub fn stake_cred(&self) -> StakeCredential { - self.stake.clone() - } - - pub fn to_address(&self) -> Address { - Address(AddrType::Base(self.clone())) - } - - pub fn from_address(addr: &Address) -> Option { - match &addr.0 { - AddrType::Base(base) => Some(base.clone()), - _ => None, - } - } -} - -#[wasm_bindgen] -#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] -pub struct EnterpriseAddress { - network: u8, - payment: StakeCredential, -} - -#[wasm_bindgen] -impl EnterpriseAddress { - pub fn new(network: u8, payment: &StakeCredential) -> Self { - Self { - network, - payment: payment.clone(), - } - } - - pub fn payment_cred(&self) -> StakeCredential { - self.payment.clone() - } - - pub fn to_address(&self) -> Address { - Address(AddrType::Enterprise(self.clone())) - } - - pub fn from_address(addr: &Address) -> Option { - match &addr.0 { - AddrType::Enterprise(enterprise) => Some(enterprise.clone()), - _ => None, - } - } -} - -#[wasm_bindgen] -#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct RewardAddress { - network: u8, - payment: StakeCredential, -} - -#[wasm_bindgen] -impl RewardAddress { - pub fn new(network: u8, payment: &StakeCredential) -> Self { - Self { - network, - payment: payment.clone(), - } - } - - pub fn payment_cred(&self) -> StakeCredential { - self.payment.clone() - } - - pub fn to_address(&self) -> Address { - Address(AddrType::Reward(self.clone())) - } - - pub fn from_address(addr: &Address) -> Option { - match &addr.0 { - AddrType::Reward(reward) => Some(reward.clone()), - _ => None, - } - } -} - -impl serde::Serialize for RewardAddress { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let bech32 = self - .to_address() - .to_bech32(None) - .map_err(|e| serde::ser::Error::custom(format!("to_bech32: {:?}", e)))?; - serializer.serialize_str(&bech32) - } -} - -impl<'de> serde::de::Deserialize<'de> for RewardAddress { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let bech32 = ::deserialize(deserializer)?; - match Address::from_bech32(&bech32) - .ok() - .map(|addr| RewardAddress::from_address(&addr)) - { - Some(Some(ra)) => Ok(ra), - _ => Err(serde::de::Error::invalid_value( - serde::de::Unexpected::Str(&bech32), - &"bech32 reward address string", - )), - } - } -} - -impl JsonSchema for RewardAddress { - fn schema_name() -> String { - String::from("RewardAddress") - } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - String::json_schema(gen) - } - fn is_referenceable() -> bool { - String::is_referenceable() - } -} - -// needed since we treat RewardAccount like RewardAddress -impl cbor_event::se::Serialize for RewardAddress { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - self.to_address().serialize(serializer) - } -} - -impl Deserialize for RewardAddress { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result { - let bytes = raw.bytes()?; - match Address::from_bytes_impl(bytes.as_ref())?.0 { - AddrType::Reward(ra) => Ok(ra), - _other_address => Err(DeserializeFailure::BadAddressType(bytes[0]).into()), - } - })() - .map_err(|e| e.annotate("RewardAddress")) - } -} - -#[wasm_bindgen] -#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] -pub struct Pointer { - slot: BigNum, - tx_index: BigNum, - cert_index: BigNum, -} - -#[wasm_bindgen] -impl Pointer { - /// !!! DEPRECATED !!! - /// This constructor uses outdated slot number format for the ttl value, tx_index and cert_index. - /// Use `.new_pointer` instead - #[deprecated( - since = "10.1.0", - note = "Underlying value capacity of ttl (BigNum u64) bigger then Slot32. Use new_pointer instead." - )] - pub fn new(slot: Slot32, tx_index: TransactionIndex, cert_index: CertificateIndex) -> Self { - Self { - slot: slot.into(), - tx_index: tx_index.into(), - cert_index: cert_index.into(), - } - } - - pub fn new_pointer(slot: &SlotBigNum, tx_index: &BigNum, cert_index: &BigNum) -> Self { - Self { - slot: slot.clone(), - tx_index: tx_index.clone(), - cert_index: cert_index.clone(), - } - } - - pub fn slot(&self) -> Result { - self.slot.clone().try_into() - } - - pub fn tx_index(&self) -> Result { - self.tx_index.clone().try_into() - } - - pub fn cert_index(&self) -> Result { - self.cert_index.clone().try_into() - } - - pub fn slot_bignum(&self) -> BigNum { - self.slot.clone() - } - - pub fn tx_index_bignum(&self) -> BigNum { - self.tx_index.clone() - } - - pub fn cert_index_bignum(&self) -> BigNum { - self.cert_index.clone() - } -} - -#[wasm_bindgen] -#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] -pub struct PointerAddress { - network: u8, - payment: StakeCredential, - stake: Pointer, -} - -#[wasm_bindgen] -impl PointerAddress { - pub fn new(network: u8, payment: &StakeCredential, stake: &Pointer) -> Self { - Self { - network, - payment: payment.clone(), - stake: stake.clone(), - } - } - - pub fn payment_cred(&self) -> StakeCredential { - self.payment.clone() - } - - pub fn stake_pointer(&self) -> Pointer { - self.stake.clone() - } - - pub fn to_address(&self) -> Address { - Address(AddrType::Ptr(self.clone())) - } - - pub fn from_address(addr: &Address) -> Option { - match &addr.0 { - AddrType::Ptr(ptr) => Some(ptr.clone()), - _ => None, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crypto::*; - - #[test] - fn variable_nat_encoding() { - let cases = [0u64, 127u64, 128u64, 255u64, 256275757658493284u64]; - for case in cases.iter() { - let encoded = variable_nat_encode(*case); - let decoded = variable_nat_decode(&encoded).unwrap().0; - assert_eq!(*case, decoded); - } - } - - #[test] - fn variable_nat_decode_too_big() { - let too_big = [129, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127]; - assert_eq!(None, variable_nat_decode(&too_big)); - } - - #[test] - fn base_serialize_consistency() { - let base = BaseAddress::new( - 5, - &StakeCredential::from_keyhash(&Ed25519KeyHash::from([23; Ed25519KeyHash::BYTE_COUNT])), - &StakeCredential::from_scripthash(&ScriptHash::from([42; ScriptHash::BYTE_COUNT])), - ); - let addr = base.to_address(); - let addr2 = Address::from_bytes(addr.to_bytes()).unwrap(); - assert_eq!(addr.to_bytes(), addr2.to_bytes()); - } - - #[test] - fn ptr_serialize_consistency() { - let ptr = PointerAddress::new( - 25, - &StakeCredential::from_keyhash(&Ed25519KeyHash::from([23; Ed25519KeyHash::BYTE_COUNT])), - &Pointer::new_pointer(&to_bignum(2354556573), &to_bignum(127), &to_bignum(0)), - ); - let addr = ptr.to_address(); - let addr2 = Address::from_bytes(addr.to_bytes()).unwrap(); - assert_eq!(addr.to_bytes(), addr2.to_bytes()); - } - - #[test] - fn enterprise_serialize_consistency() { - let enterprise = EnterpriseAddress::new( - 64, - &StakeCredential::from_keyhash(&Ed25519KeyHash::from([23; Ed25519KeyHash::BYTE_COUNT])), - ); - let addr = enterprise.to_address(); - let addr2 = Address::from_bytes(addr.to_bytes()).unwrap(); - assert_eq!(addr.to_bytes(), addr2.to_bytes()); - } - - #[test] - fn reward_serialize_consistency() { - let reward = RewardAddress::new( - 9, - &StakeCredential::from_scripthash(&ScriptHash::from([127; Ed25519KeyHash::BYTE_COUNT])), - ); - let addr = reward.to_address(); - let addr2 = Address::from_bytes(addr.to_bytes()).unwrap(); - assert_eq!(addr.to_bytes(), addr2.to_bytes()); - } - - fn root_key_12() -> Bip32PrivateKey { - // test walk nut penalty hip pave soap entry language right filter choice - let entropy = [ - 0xdf, 0x9e, 0xd2, 0x5e, 0xd1, 0x46, 0xbf, 0x43, 0x33, 0x6a, 0x5d, 0x7c, 0xf7, 0x39, - 0x59, 0x94, - ]; - Bip32PrivateKey::from_bip39_entropy(&entropy, &[]) - } - - fn root_key_15() -> Bip32PrivateKey { - // art forum devote street sure rather head chuckle guard poverty release quote oak craft enemy - let entropy = [ - 0x0c, 0xcb, 0x74, 0xf3, 0x6b, 0x7d, 0xa1, 0x64, 0x9a, 0x81, 0x44, 0x67, 0x55, 0x22, - 0xd4, 0xd8, 0x09, 0x7c, 0x64, 0x12, - ]; - Bip32PrivateKey::from_bip39_entropy(&entropy, &[]) - } - - fn root_key_24() -> Bip32PrivateKey { - let entropy = [ - 0x4e, 0x82, 0x8f, 0x9a, 0x67, 0xdd, 0xcf, 0xf0, 0xe6, 0x39, 0x1a, 0xd4, 0xf2, 0x6d, - 0xdb, 0x75, 0x79, 0xf5, 0x9b, 0xa1, 0x4b, 0x6d, 0xd4, 0xba, 0xf6, 0x3d, 0xcf, 0xdb, - 0x9d, 0x24, 0x20, 0xda, - ]; - Bip32PrivateKey::from_bip39_entropy(&entropy, &[]) - } - - fn harden(index: u32) -> u32 { - index | 0x80_00_00_00 - } - - #[test] - fn bech32_parsing() { - let addr = - Address::from_bech32("addr1u8pcjgmx7962w6hey5hhsd502araxp26kdtgagakhaqtq8sxy9w7g") - .unwrap(); - assert_eq!( - addr.to_bech32(Some("foobar".to_string())).unwrap(), - "foobar1u8pcjgmx7962w6hey5hhsd502araxp26kdtgagakhaqtq8s92n4tm" - ); - } - - #[test] - fn byron_magic_parsing() { - // mainnet address w/ protocol magic omitted - let addr = ByronAddress::from_base58( - "Ae2tdPwUPEZ4YjgvykNpoFeYUxoyhNj2kg8KfKWN2FizsSpLUPv68MpTVDo", - ) - .unwrap(); - assert_eq!( - addr.byron_protocol_magic(), - NetworkInfo::mainnet().protocol_magic() - ); - assert_eq!( - addr.network_id().unwrap(), - NetworkInfo::mainnet().network_id() - ); - - // original Byron testnet address - let addr = ByronAddress::from_base58( - "2cWKMJemoBaipzQe9BArYdo2iPUfJQdZAjm4iCzDA1AfNxJSTgm9FZQTmFCYhKkeYrede", - ) - .unwrap(); - assert_eq!( - addr.byron_protocol_magic(), - NetworkInfo::testnet().protocol_magic() - ); - assert_eq!( - addr.network_id().unwrap(), - NetworkInfo::testnet().network_id() - ); - } - - #[test] - fn bip32_12_base() { - let spend = root_key_12() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let stake = root_key_12() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - assert_eq!(addr_net_0.to_bech32(None).unwrap(), "addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp"); - let addr_net_3 = BaseAddress::new( - NetworkInfo::mainnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - assert_eq!(addr_net_3.to_bech32(None).unwrap(), "addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwqfjkjv7"); - } - - #[test] - fn bip32_12_enterprise() { - let spend = root_key_12() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let addr_net_0 = - EnterpriseAddress::new(NetworkInfo::testnet().network_id(), &spend_cred).to_address(); - assert_eq!( - addr_net_0.to_bech32(None).unwrap(), - "addr_test1vz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzerspjrlsz" - ); - let addr_net_3 = - EnterpriseAddress::new(NetworkInfo::mainnet().network_id(), &spend_cred).to_address(); - assert_eq!( - addr_net_3.to_bech32(None).unwrap(), - "addr1vx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzers66hrl8" - ); - } - - #[test] - fn bip32_12_pointer() { - let spend = root_key_12() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let addr_net_0 = PointerAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &Pointer::new_pointer(&to_bignum(1), &to_bignum(2), &to_bignum(3)), - ) - .to_address(); - assert_eq!( - addr_net_0.to_bech32(None).unwrap(), - "addr_test1gz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzerspqgpsqe70et" - ); - let addr_net_3 = PointerAddress::new( - NetworkInfo::mainnet().network_id(), - &spend_cred, - &Pointer::new_pointer(&to_bignum(24157), &to_bignum(177), &to_bignum(42)), - ) - .to_address(); - assert_eq!( - addr_net_3.to_bech32(None).unwrap(), - "addr1gx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer5ph3wczvf2w8lunk" - ); - } - - #[test] - fn bip32_15_base() { - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - assert_eq!(addr_net_0.to_bech32(None).unwrap(), "addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w"); - let addr_net_3 = BaseAddress::new( - NetworkInfo::mainnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - assert_eq!(addr_net_3.to_bech32(None).unwrap(), "addr1q9u5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qld6xc3"); - } - - #[test] - fn bip32_15_enterprise() { - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let addr_net_0 = - EnterpriseAddress::new(NetworkInfo::testnet().network_id(), &spend_cred).to_address(); - assert_eq!( - addr_net_0.to_bech32(None).unwrap(), - "addr_test1vpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5eg57c2qv" - ); - let addr_net_3 = - EnterpriseAddress::new(NetworkInfo::mainnet().network_id(), &spend_cred).to_address(); - assert_eq!( - addr_net_3.to_bech32(None).unwrap(), - "addr1v9u5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5eg0kvk0f" - ); - } - - #[test] - fn bip32_15_pointer() { - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let addr_net_0 = PointerAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &Pointer::new_pointer(&to_bignum(1), &to_bignum(2), &to_bignum(3)), - ) - .to_address(); - assert_eq!( - addr_net_0.to_bech32(None).unwrap(), - "addr_test1gpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5egpqgpsdhdyc0" - ); - let addr_net_3 = PointerAddress::new( - NetworkInfo::mainnet().network_id(), - &spend_cred, - &Pointer::new_pointer(&to_bignum(24157), &to_bignum(177), &to_bignum(42)), - ) - .to_address(); - assert_eq!( - addr_net_3.to_bech32(None).unwrap(), - "addr1g9u5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5evph3wczvf2kd5vam" - ); - } - - #[test] - fn parse_redeem_address() { - assert!(ByronAddress::is_valid( - "Ae2tdPwUPEZ3MHKkpT5Bpj549vrRH7nBqYjNXnCV8G2Bc2YxNcGHEa8ykDp" - )); - let byron_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZ3MHKkpT5Bpj549vrRH7nBqYjNXnCV8G2Bc2YxNcGHEa8ykDp", - ) - .unwrap(); - assert_eq!( - byron_addr.to_base58(), - "Ae2tdPwUPEZ3MHKkpT5Bpj549vrRH7nBqYjNXnCV8G2Bc2YxNcGHEa8ykDp" - ); - let byron_addr2 = ByronAddress::from_bytes(byron_addr.to_bytes()).unwrap(); - assert_eq!( - byron_addr2.to_base58(), - "Ae2tdPwUPEZ3MHKkpT5Bpj549vrRH7nBqYjNXnCV8G2Bc2YxNcGHEa8ykDp" - ); - } - - #[test] - fn bip32_15_byron() { - let byron_key = root_key_15() - .derive(harden(44)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let byron_addr = - ByronAddress::icarus_from_key(&byron_key, NetworkInfo::mainnet().protocol_magic()); - assert_eq!( - byron_addr.to_base58(), - "Ae2tdPwUPEZHtBmjZBF4YpMkK9tMSPTE2ADEZTPN97saNkhG78TvXdp3GDk" - ); - assert!(ByronAddress::is_valid( - "Ae2tdPwUPEZHtBmjZBF4YpMkK9tMSPTE2ADEZTPN97saNkhG78TvXdp3GDk" - )); - assert_eq!(byron_addr.network_id().unwrap(), 0b0001); - - let byron_addr_2 = - ByronAddress::from_address(&Address::from_bytes(byron_addr.to_bytes()).unwrap()) - .unwrap(); - assert_eq!(byron_addr.to_base58(), byron_addr_2.to_base58()); - } - - #[test] - fn bip32_24_base() { - let spend = root_key_24() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let stake = root_key_24() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - assert_eq!(addr_net_0.to_bech32(None).unwrap(), "addr_test1qqy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmn8k8ttq8f3gag0h89aepvx3xf69g0l9pf80tqv7cve0l33sw96paj"); - let addr_net_3 = BaseAddress::new( - NetworkInfo::mainnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - assert_eq!(addr_net_3.to_bech32(None).unwrap(), "addr1qyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmn8k8ttq8f3gag0h89aepvx3xf69g0l9pf80tqv7cve0l33sdn8p3d"); - } - - #[test] - fn bip32_24_enterprise() { - let spend = root_key_24() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let addr_net_0 = - EnterpriseAddress::new(NetworkInfo::testnet().network_id(), &spend_cred).to_address(); - assert_eq!( - addr_net_0.to_bech32(None).unwrap(), - "addr_test1vqy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqtjtf68" - ); - let addr_net_3 = - EnterpriseAddress::new(NetworkInfo::mainnet().network_id(), &spend_cred).to_address(); - assert_eq!( - addr_net_3.to_bech32(None).unwrap(), - "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z" - ); - } - - #[test] - fn bip32_24_pointer() { - let spend = root_key_24() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let addr_net_0 = PointerAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &Pointer::new_pointer(&to_bignum(1), &to_bignum(2), &to_bignum(3)), - ) - .to_address(); - assert_eq!( - addr_net_0.to_bech32(None).unwrap(), - "addr_test1gqy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqpqgps5mee0p" - ); - let addr_net_3 = PointerAddress::new( - NetworkInfo::mainnet().network_id(), - &spend_cred, - &Pointer::new_pointer(&to_bignum(24157), &to_bignum(177), &to_bignum(42)), - ) - .to_address(); - assert_eq!( - addr_net_3.to_bech32(None).unwrap(), - "addr1gyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnyph3wczvf2dqflgt" - ); - } - - #[test] - fn bip32_12_reward() { - let staking_key = root_key_12() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - let staking_cred = StakeCredential::from_keyhash(&staking_key.to_raw_key().hash()); - let addr_net_0 = - RewardAddress::new(NetworkInfo::testnet().network_id(), &staking_cred).to_address(); - assert_eq!( - addr_net_0.to_bech32(None).unwrap(), - "stake_test1uqevw2xnsc0pvn9t9r9c7qryfqfeerchgrlm3ea2nefr9hqp8n5xl" - ); - let addr_net_3 = - RewardAddress::new(NetworkInfo::mainnet().network_id(), &staking_cred).to_address(); - assert_eq!( - addr_net_3.to_bech32(None).unwrap(), - "stake1uyevw2xnsc0pvn9t9r9c7qryfqfeerchgrlm3ea2nefr9hqxdekzz" - ); - } - - #[test] - fn bip32_24_base_multisig_hd_derivation() { - let spend = root_key_24() - .derive(harden(1854)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let stake = root_key_24() - .derive(harden(1854)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - assert_eq!(addr_net_0.to_bech32(None).unwrap(), "addr_test1qz8fg2e9yn0ga6sav0760cxmx0antql96mfuhqgzcc5swugw2jqqlugnx9qjep9xvcx40z0zfyep55r2t3lav5smyjrs96cusg"); - let addr_net_3 = BaseAddress::new( - NetworkInfo::mainnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - assert_eq!(addr_net_3.to_bech32(None).unwrap(), "addr1qx8fg2e9yn0ga6sav0760cxmx0antql96mfuhqgzcc5swugw2jqqlugnx9qjep9xvcx40z0zfyep55r2t3lav5smyjrsxv9uuh"); - } - - #[test] - fn multisig_from_script() { - let spend = root_key_24() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - - let mut pubkey_native_scripts = NativeScripts::new(); - - let spending_hash = spend.to_raw_key().hash(); - pubkey_native_scripts.add(&NativeScript::new_script_pubkey(&ScriptPubkey::new( - &spending_hash, - ))); - let oneof_native_script = - NativeScript::new_script_n_of_k(&ScriptNOfK::new(1, &pubkey_native_scripts)); - - let script_hash = ScriptHash::from_bytes(oneof_native_script.hash().to_bytes()).unwrap(); - - let spend_cred = StakeCredential::from_scripthash(&script_hash); - let stake_cred = StakeCredential::from_scripthash(&script_hash); - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - assert_eq!(addr_net_0.to_bech32(None).unwrap(), "addr_test1xr0de0mz3m9xmgtlmqqzu06s0uvfsczskdec8k7v4jhr7077mjlk9rk2dkshlkqq9cl4qlccnps9pvmns0duet9w8uls8flvxc"); - let addr_net_3 = BaseAddress::new( - NetworkInfo::mainnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - assert_eq!(addr_net_3.to_bech32(None).unwrap(), "addr1x80de0mz3m9xmgtlmqqzu06s0uvfsczskdec8k7v4jhr7077mjlk9rk2dkshlkqq9cl4qlccnps9pvmns0duet9w8ulsylzv28"); - } - - #[test] - fn pointer_address_big() { - let addr = Address::from_bech32("addr_test1grqe6lg9ay8wkcu5k5e38lne63c80h3nq6xxhqfmhewf645pllllllllllll7lupllllllllllll7lupllllllllllll7lc9wayvj").unwrap(); - let ptr = PointerAddress::from_address(&addr).unwrap().stake; - assert_eq!(u64::MAX, from_bignum(&ptr.slot)); - assert_eq!(u64::MAX, from_bignum(&ptr.tx_index)); - assert_eq!(u64::MAX, from_bignum(&ptr.cert_index)); - } - - #[test] - fn point_address_old() { - let p1 = Pointer::new(10, 20, 30); - let p2 = Pointer::new_pointer(&to_bignum(10), &to_bignum(20), &to_bignum(30)); - assert_eq!(p1, p2); - } - - #[test] - fn prepod_network_id_test() { - let address = "KjgoiXJS2coTnqpCLHXFtd89Hv9ttjsE6yW4msyLXFNkykUpTsyBs85r2rDDia2uKrhdpGKCJnmFXwvPSWLe75564ixZWdTxRh7TnuaDLnHx"; - let network_id = ByronAddress::from_base58(address).unwrap().to_address().network_id().unwrap(); - assert_eq!(network_id, NetworkInfo::testnet_preprod().network_id()); - } -} diff --git a/rust/src/tx_builder/batch_tools/asset_categorizer.rs b/rust/src/builders/batch_tools/asset_categorizer.rs similarity index 81% rename from rust/src/tx_builder/batch_tools/asset_categorizer.rs rename to rust/src/builders/batch_tools/asset_categorizer.rs index 2d5f6720..2ce705b4 100644 --- a/rust/src/tx_builder/batch_tools/asset_categorizer.rs +++ b/rust/src/builders/batch_tools/asset_categorizer.rs @@ -1,10 +1,10 @@ -use std::collections::HashMap; -use crate::tx_builder::batch_tools::proposals::{TxOutputProposal, TxProposal}; use super::assets_calculator::AssetsCalculator; use super::cbor_calculator::CborCalculator; +use super::indexes::{AssetIndex, PlaneAssetId, PolicyIndex, UtxoIndex}; use super::utxo_stat::UtxosStat; -use super::indexes::{UtxoIndex, AssetIndex, PolicyIndex, PlaneAssetId}; -use super::super::*; +use crate::builders::batch_tools::proposals::{TxOutputProposal, TxProposal}; +use crate::*; +use std::collections::{HashMap, HashSet}; #[derive(Clone)] pub(crate) struct TxProposalChanges { @@ -46,12 +46,15 @@ pub struct AssetCategorizer { free_ada_utxos: Vec<(UtxoIndex, Coin)>, //utxos_with_ada_overhead: Vec<(UtxoIndex, Coin)>, - output_size: usize, } impl AssetCategorizer { - pub(crate) fn new(config: &TransactionBuilderConfig, utxos: &TransactionUnspentOutputs, address: &Address) -> Result { + pub(crate) fn new( + config: &TransactionBuilderConfig, + utxos: &TransactionUnspentOutputs, + address: &Address, + ) -> Result { let mut assets: Vec = Vec::new(); let mut utxos_ada: Vec = Vec::new(); let mut policies: Vec = Vec::new(); @@ -84,7 +87,8 @@ impl AssetCategorizer { utxos_ada.push(utxo.output.amount.coin.clone()); addresses.push(utxo.output.address.clone()); - let ada_overhead = Self::calc_utxo_output_overhead(address, &utxo.output.amount, config)?; + let ada_overhead = + Self::calc_utxo_output_overhead(address, &utxo.output.amount, config)?; if ada_overhead > Coin::zero() { utxos_with_ada_overhead.push((current_utxo_index.clone(), ada_overhead)); } @@ -100,7 +104,7 @@ impl AssetCategorizer { policy_count += 1; } - for asset in &policy.1.0 { + for asset in &policy.1 .0 { let mut current_asset_index = AssetIndex(asset_count.clone()); let plane_id = PlaneAssetId(current_policy_index.clone(), asset.0.clone()); @@ -108,8 +112,9 @@ impl AssetCategorizer { current_asset_index = asset_index.clone(); assets_counts[current_asset_index.0].1 += 1; } else { - let mut asset_name_size = CborCalculator::get_struct_size(asset.0.0.len() as u64); - asset_name_size += asset.0.0.len(); + let mut asset_name_size = + CborCalculator::get_struct_size(asset.0 .0.len() as u64); + asset_name_size += asset.0 .0.len(); assets.push(plane_id.clone()); assets_name_sizes.push(asset_name_size); asset_ids.insert(plane_id, current_asset_index.clone()); @@ -121,7 +126,8 @@ impl AssetCategorizer { let asset_utxo_amounts = &mut assets_amounts[current_asset_index.0]; asset_utxo_amounts.insert(current_utxo_index.clone(), asset.1.clone()); - asset_to_policy.insert(current_asset_index.clone(), current_policy_index.clone()); + asset_to_policy + .insert(current_asset_index.clone(), current_policy_index.clone()); if let Some(assets_set) = policy_to_asset.get_mut(¤t_policy_index) { assets_set.insert(current_asset_index.clone()); } else { @@ -191,8 +197,11 @@ impl AssetCategorizer { !self.free_ada_utxos.is_empty() } - pub(crate) fn build_value(&self, used_utxos: &HashSet, tx_output_proposal: &TxOutputProposal) - -> Result { + pub(crate) fn build_value( + &self, + used_utxos: &HashSet, + tx_output_proposal: &TxOutputProposal, + ) -> Result { let mut value = Value::new(&tx_output_proposal.total_ada); if tx_output_proposal.used_assets.is_empty() { return Ok(value); @@ -206,7 +215,11 @@ impl AssetCategorizer { asset_coins = asset_coins.checked_add(coins)?; } } - multiasset.set_asset(&self.policies[policy_index.0], &self.assets[asset_index.0].1, asset_coins); + multiasset.set_asset( + &self.policies[policy_index.0], + &self.assets[asset_index.0].1, + &asset_coins, + ); } } @@ -214,7 +227,10 @@ impl AssetCategorizer { Ok(value) } - pub(crate) fn try_append_next_utxos(&mut self, tx_proposal: &TxProposal) -> Result, JsError> { + pub(crate) fn try_append_next_utxos( + &mut self, + tx_proposal: &TxProposal, + ) -> Result, JsError> { let mut proposal_changes = None; if self.has_assets() { proposal_changes = self.try_append_next_asset_utxos(tx_proposal)?; @@ -223,7 +239,6 @@ impl AssetCategorizer { } if let Some(proposal_changes) = proposal_changes { - for utxo in proposal_changes.asset_utxo { self.remove_assets_utxo(&utxo); } @@ -238,15 +253,20 @@ impl AssetCategorizer { } } - pub(crate) fn try_append_next_asset_utxos(&self, tx_proposal: &TxProposal) -> Result, JsError> { + pub(crate) fn try_append_next_asset_utxos( + &self, + tx_proposal: &TxProposal, + ) -> Result, JsError> { let asset_intersections = self.get_asset_intersections(&tx_proposal.used_assets); - let asset_intersected_utxo = self.make_candidate(&asset_intersections, tx_proposal, false)?; + let asset_intersected_utxo = + self.make_candidate(&asset_intersections, tx_proposal, false)?; if let Some(res_utxo) = asset_intersected_utxo { return Ok(Some(res_utxo)); } let policy_intersections = self.get_policy_intersections(&tx_proposal.used_assets); - let policy_intersected_utxo = self.make_candidate(&policy_intersections, tx_proposal, false)?; + let policy_intersected_utxo = + self.make_candidate(&policy_intersections, tx_proposal, false)?; if let Some(res_utxo) = policy_intersected_utxo { return Ok(Some(res_utxo)); } @@ -254,7 +274,10 @@ impl AssetCategorizer { self.make_candidate(&self.assets_counts, tx_proposal, true) } - fn try_append_pure_ada_utxo(&self, tx_proposal: &TxProposal) -> Result, JsError> { + fn try_append_pure_ada_utxo( + &self, + tx_proposal: &TxProposal, + ) -> Result, JsError> { let mut new_proposal: TxProposal = tx_proposal.clone(); let mut used_utxos = HashSet::new(); if new_proposal.get_need_ada()? == Coin::zero() { @@ -272,9 +295,8 @@ impl AssetCategorizer { let mut new_size = self.set_min_ada_for_tx(&mut new_proposal)?; if new_proposal.get_need_ada()? > Coin::zero() { - let next_utxos = self.get_next_pure_ada_utxo_by_amount( - &new_proposal.get_need_ada()?, - &used_utxos)?; + let next_utxos = + self.get_next_pure_ada_utxo_by_amount(&new_proposal.get_need_ada()?, &used_utxos)?; for (utxo, coin) in &next_utxos { new_proposal.add_utxo(utxo, coin, &self.addresses[utxo.0])?; @@ -283,7 +305,9 @@ impl AssetCategorizer { new_size = self.set_min_ada_for_tx(&mut new_proposal)?; if new_size > (self.config.max_tx_size as usize) && tx_proposal.used_utoxs.is_empty() { - return Err(JsError::from_str("Utxo can not be places into tx, utxo value is too big.")); + return Err(JsError::from_str( + "Utxo can not be places into tx, utxo value is too big.", + )); } } @@ -306,7 +330,10 @@ impl AssetCategorizer { sizes } - fn get_asset_intersections(&self, used_assets: &HashSet) -> Vec<(AssetIndex, usize)> { + fn get_asset_intersections( + &self, + used_assets: &HashSet, + ) -> Vec<(AssetIndex, usize)> { let mut intersections = Vec::new(); for (index, asset_count) in &self.assets_counts { if used_assets.contains(index) && self.free_asset_to_utxos.contains_key(index) { @@ -316,9 +343,13 @@ impl AssetCategorizer { intersections } - fn get_policy_intersections(&self, used_assets: &HashSet) -> Vec<(AssetIndex, usize)> { + fn get_policy_intersections( + &self, + used_assets: &HashSet, + ) -> Vec<(AssetIndex, usize)> { let mut intersections = Vec::new(); - let used_policies = used_assets.iter() + let used_policies = used_assets + .iter() .filter_map(|x| self.asset_to_policy.get(x)); let available_assets: HashSet = used_policies .filter_map(|x| self.policy_to_asset.get(x)) @@ -333,9 +364,11 @@ impl AssetCategorizer { intersections } - fn prototype_append(&self, - tx_proposal: &TxProposal, - utxo: &UtxoIndex) -> Result, JsError> { + fn prototype_append( + &self, + tx_proposal: &TxProposal, + utxo: &UtxoIndex, + ) -> Result, JsError> { let utxo_assets = self.free_utxo_to_assets.get(utxo); let mut new_proposal = tx_proposal.clone(); let used_assets_in_output = match new_proposal.tx_output_proposals.last() { @@ -357,7 +390,10 @@ impl AssetCategorizer { let mut assets_for_next_output = asset_for_add; let mut create_new_output = false; while let Some(next_assets) = self.add_assets_to_proposal_output( - create_new_output, &mut new_proposal, &assets_for_next_output)? { + create_new_output, + &mut new_proposal, + &assets_for_next_output, + )? { assets_for_next_output = next_assets; create_new_output = true; } @@ -367,8 +403,10 @@ impl AssetCategorizer { if new_size > (self.config.max_tx_size as usize) { if tx_proposal.used_utoxs.is_empty() { - return Err(JsError::from_str( - &format!("Utxo can not be places into tx, utxo value is too big. Utxo index {}", utxo.0))); + return Err(JsError::from_str(&format!( + "Utxo can not be places into tx, utxo value is too big. Utxo index {}", + utxo.0 + ))); } //that means that we above limit of tx size and we cannot create that tx @@ -393,15 +431,20 @@ impl AssetCategorizer { Ok(None) } - fn add_assets_to_proposal_output(&self, create_new: bool, tx_proposal: &mut TxProposal, assets: &HashSet) - -> Result>, JsError> { + fn add_assets_to_proposal_output( + &self, + create_new: bool, + tx_proposal: &mut TxProposal, + assets: &HashSet, + ) -> Result>, JsError> { let last_output = tx_proposal.get_outputs().last(); let mut old_value_state = if create_new || last_output.is_none() { self.assets_calculator.build_empty_intermediate_value() } else { self.assets_calculator.build_intermediate_value( last_output.unwrap().get_used_assets(), - &self.asset_to_policy) + &self.asset_to_policy, + ) }; let mut new_value_state = old_value_state.clone(); @@ -413,14 +456,17 @@ impl AssetCategorizer { let new_size = self.assets_calculator.add_asset_to_intermediate_value( &mut new_value_state, asset, - &self.asset_to_policy[asset]); + &self.asset_to_policy[asset], + ); if new_size <= self.config.max_value_size as usize { asset_to_output.insert(asset.clone()); old_value_state = new_value_state.clone(); } else { if old_value_state.is_empty() { - return Err(JsError::from_str( - &format!("Asset can not be places into tx, asset size is too big. Asset index {}", asset.0))); + return Err(JsError::from_str(&format!( + "Asset can not be places into tx, asset size is too big. Asset index {}", + asset.0 + ))); } new_value_state = old_value_state.clone(); asset_to_new_output.insert(asset.clone()); @@ -442,7 +488,10 @@ impl AssetCategorizer { } } - pub(crate) fn set_min_ada_for_tx(&self, tx_proposal: &mut TxProposal) -> Result { + pub(crate) fn set_min_ada_for_tx( + &self, + tx_proposal: &mut TxProposal, + ) -> Result { self.recalculate_outputs(tx_proposal)?; let (tx_fee, tx_size) = self.estimate_fee(tx_proposal)?; tx_proposal.set_fee(&tx_fee); @@ -490,8 +539,11 @@ impl AssetCategorizer { self.free_ada_utxos.last() } - fn get_next_pure_ada_utxo_by_amount(&self, need_ada: &Coin, ignore_list: &HashSet) - -> Result, JsError> { + fn get_next_pure_ada_utxo_by_amount( + &self, + need_ada: &Coin, + ignore_list: &HashSet, + ) -> Result, JsError> { //TODO: add algo with minimal count of utxos let mut ada_left = need_ada.clone(); let mut utxos = Vec::new(); @@ -514,8 +566,12 @@ impl AssetCategorizer { } } - fn make_candidate(&self, assets: &Vec<(AssetIndex, usize)>, tx_propoasl: &TxProposal, choose_first: bool) - -> Result, JsError> { + fn make_candidate( + &self, + assets: &Vec<(AssetIndex, usize)>, + tx_propoasl: &TxProposal, + choose_first: bool, + ) -> Result, JsError> { let mut txp_with_new_output: Option = None; for (index, _) in assets.iter() { let utxos_set = self.free_asset_to_utxos.get(index); @@ -539,18 +595,24 @@ impl AssetCategorizer { Ok(txp_with_new_output) } - fn estimate_output_cost(&self, used_utoxs: &HashSet, output_proposal: &TxOutputProposal) -> Result<(Coin, usize), JsError> { + fn estimate_output_cost( + &self, + used_utoxs: &HashSet, + output_proposal: &TxOutputProposal, + ) -> Result<(Coin, usize), JsError> { let assets_size = self.assets_calculator.calc_value_size( &output_proposal.total_ada, &output_proposal.grouped_assets, used_utoxs, - &self.assets_amounts)?; + &self.assets_amounts, + )?; let mut output_size = self.output_size + assets_size; output_size += CborCalculator::get_value_struct_size(output_proposal.contains_only_ada()); CborCalculator::estimate_output_cost( &output_proposal.get_total_ada(), output_size, - &self.config.data_cost) + &self.config.data_cost, + ) } pub(crate) fn estimate_fee(&self, tx_proposal: &TxProposal) -> Result<(Coin, usize), JsError> { @@ -558,16 +620,15 @@ impl AssetCategorizer { let mut dependable_value = None; let mut min_value = None; if let Some(last_output) = tx_proposal.get_outputs().last() { - dependable_value = Some(tx_proposal.get_unused_ada()? - .checked_add(&last_output.get_total_ada())?); + dependable_value = Some( + tx_proposal + .get_unused_ada()? + .checked_add(&last_output.get_total_ada())?, + ); min_value = Some(last_output.get_min_ada()); tx_len -= CborCalculator::get_coin_size(&last_output.get_total_ada()); } - CborCalculator::estimate_fee( - tx_len, - min_value, - dependable_value, - &self.config.fee_algo) + CborCalculator::estimate_fee(tx_len, min_value, dependable_value, &self.config.fee_algo) } fn remove_assets_utxo(&mut self, utxo: &UtxoIndex) { @@ -592,12 +653,14 @@ impl AssetCategorizer { } } - fn calc_utxo_output_overhead(address: &Address, value: &Value, cfg: &TransactionBuilderConfig) - -> Result { + fn calc_utxo_output_overhead( + address: &Address, + value: &Value, + cfg: &TransactionBuilderConfig, + ) -> Result { let ada = value.coin; let output = TransactionOutput::new(address, value); let req_coin = MinOutputAdaCalculator::calc_required_coin(&output, &cfg.data_cost)?; Ok(ada.checked_sub(&req_coin).unwrap_or(Coin::zero())) } } - diff --git a/rust/src/tx_builder/batch_tools/assets_calculator.rs b/rust/src/builders/batch_tools/assets_calculator.rs similarity index 64% rename from rust/src/tx_builder/batch_tools/assets_calculator.rs rename to rust/src/builders/batch_tools/assets_calculator.rs index d3dcffd8..dcc49055 100644 --- a/rust/src/tx_builder/batch_tools/assets_calculator.rs +++ b/rust/src/builders/batch_tools/assets_calculator.rs @@ -1,9 +1,8 @@ -use std::collections::HashMap; -use super::utxo_stat::UtxosStat; use super::cbor_calculator::CborCalculator; -use super::indexes::{UtxoIndex, AssetIndex, PolicyIndex}; -use crate::utils::*; -use super::super::*; +use super::indexes::{AssetIndex, PolicyIndex, UtxoIndex}; +use super::utxo_stat::UtxosStat; +use crate::*; +use std::collections::{HashMap, HashSet}; #[derive(Clone)] struct IntermediatePolicyState { @@ -19,7 +18,12 @@ impl IntermediatePolicyState { } } - pub(super) fn add_asset(&mut self, asset_index: &AssetIndex, size: usize, coin_size: usize) -> usize { + pub(super) fn add_asset( + &mut self, + asset_index: &AssetIndex, + size: usize, + coin_size: usize, + ) -> usize { if !self.assets.contains(asset_index) { let mut new_size = self.total_size; if self.assets.len() > 0 { @@ -48,13 +52,19 @@ impl IntermediateOutputValue { } } - pub(super) fn set_coin(&mut self, coin: &Coin) -> usize{ + pub(super) fn set_coin(&mut self, coin: &Coin) -> usize { self.total_size += CborCalculator::get_coin_size(coin); self.total_size } - pub(super) fn add_asset(&mut self, policy_index: &PolicyIndex, asset_index: &AssetIndex, - policy_size: usize, asset_size: usize, coin_size: usize) -> usize { + pub(super) fn add_asset( + &mut self, + policy_index: &PolicyIndex, + asset_index: &AssetIndex, + policy_size: usize, + asset_size: usize, + coin_size: usize, + ) -> usize { if self.is_empty() { //value with assets and ada is array of 2 elements self.total_size += CborCalculator::get_struct_size(2); @@ -79,13 +89,14 @@ impl IntermediateOutputValue { } pub(super) fn is_empty(&self) -> bool { - self.multi_asset.iter() - .map(|(_, policy)| policy.assets.len()).sum::() <= 0 + self.multi_asset + .iter() + .map(|(_, policy)| policy.assets.len()) + .sum::() + <= 0 } } - - #[derive(Clone)] pub(super) struct AssetsCalculator { assets_name_sizes: Vec, @@ -94,10 +105,9 @@ pub(super) struct AssetsCalculator { } impl AssetsCalculator { - pub(super) fn new(utxo_stat: UtxosStat, assets_name_sizes: Vec) -> Self { //28 is the size of a policy id in bytes - let policy_size= 28 + CborCalculator::get_struct_size(28u64); + let policy_size = 28 + CborCalculator::get_struct_size(28u64); Self { assets_name_sizes, @@ -106,11 +116,13 @@ impl AssetsCalculator { } } - pub(super) fn calc_value_size(&self, - coin: &Coin, - grouped_assets: &HashMap>, - utxos: &HashSet, - assets_amounts: &Vec>) -> Result { + pub(super) fn calc_value_size( + &self, + coin: &Coin, + grouped_assets: &HashMap>, + utxos: &HashSet, + assets_amounts: &Vec>, + ) -> Result { let mut size = 0; size += CborCalculator::get_coin_size(coin); @@ -127,7 +139,7 @@ impl AssetsCalculator { let mut asset_coins = Coin::zero(); for (utxo, coins) in &assets_amounts[asset_in_policy.0] { if utxos.contains(utxo) { - asset_coins = asset_coins.checked_add(coins)?; + asset_coins = asset_coins.checked_add(coins)?; } } size += CborCalculator::get_coin_size(&asset_coins); @@ -136,24 +148,38 @@ impl AssetsCalculator { Ok(size) } - pub(super) fn add_asset_to_intermediate_value(&self, intermediate_value: &mut IntermediateOutputValue, asset_index: &AssetIndex, - policy_index: &PolicyIndex) -> usize { - intermediate_value.add_asset(policy_index, asset_index, - self.policy_size, - self.assets_name_sizes[asset_index.0], - CborCalculator::get_coin_size(&self.utxo_stat.coins_in_assets[asset_index])) + pub(super) fn add_asset_to_intermediate_value( + &self, + intermediate_value: &mut IntermediateOutputValue, + asset_index: &AssetIndex, + policy_index: &PolicyIndex, + ) -> usize { + intermediate_value.add_asset( + policy_index, + asset_index, + self.policy_size, + self.assets_name_sizes[asset_index.0], + CborCalculator::get_coin_size(&self.utxo_stat.coins_in_assets[asset_index]), + ) } - pub(super) fn build_intermediate_value(&self, assets_ids: &HashSet, - asset_to_policy: &HashMap) -> IntermediateOutputValue { + pub(super) fn build_intermediate_value( + &self, + assets_ids: &HashSet, + asset_to_policy: &HashMap, + ) -> IntermediateOutputValue { let mut intermediate_data = IntermediateOutputValue::new(); for asset_index in assets_ids { - let asset_coin_size = CborCalculator::get_coin_size(&self.utxo_stat.coins_in_assets[asset_index]); + let asset_coin_size = + CborCalculator::get_coin_size(&self.utxo_stat.coins_in_assets[asset_index]); let policy_index = &asset_to_policy[asset_index]; - intermediate_data.add_asset(policy_index, asset_index, - self.policy_size, - self.assets_name_sizes[asset_index.0], - asset_coin_size); + intermediate_data.add_asset( + policy_index, + asset_index, + self.policy_size, + self.assets_name_sizes[asset_index.0], + asset_coin_size, + ); } intermediate_data.set_coin(&self.utxo_stat.ada_coins); intermediate_data @@ -164,4 +190,4 @@ impl AssetsCalculator { value.set_coin(&self.utxo_stat.ada_coins); value } -} \ No newline at end of file +} diff --git a/rust/src/tx_builder/batch_tools/cbor_calculator.rs b/rust/src/builders/batch_tools/cbor_calculator.rs similarity index 75% rename from rust/src/tx_builder/batch_tools/cbor_calculator.rs rename to rust/src/builders/batch_tools/cbor_calculator.rs index 841a2c80..fec45147 100644 --- a/rust/src/tx_builder/batch_tools/cbor_calculator.rs +++ b/rust/src/builders/batch_tools/cbor_calculator.rs @@ -1,6 +1,9 @@ -use crate::fees::{LinearFee, min_fee_for_size}; -use crate::serialization_tools::map_names::{TxBodyNames, WitnessSetNames}; -use super::super::*; +use crate::fees::{min_fee_for_size, LinearFee}; +use crate::serialization::map_names::{TxBodyNames, WitnessSetNames}; +use crate::*; +use num_traits::ToPrimitive; +use std::collections::HashSet; +use crate::builders::fakes::fake_private_key; pub(super) struct CborCalculator(); @@ -52,7 +55,7 @@ impl CborCalculator { let legacy_output_size = CborCalculator::get_struct_size(2); let address_size = CborCalculator::get_address_size(address); let address_struct_size = CborCalculator::get_struct_size(address_size as u64); - return legacy_output_size + address_size + address_struct_size + return legacy_output_size + address_size + address_struct_size; } pub(super) fn get_value_struct_size(ada_only: bool) -> usize { @@ -63,21 +66,22 @@ impl CborCalculator { //value with assets and ada is array of 2 elements CborCalculator::get_struct_size(2) } - } pub(super) fn get_bare_tx_body_size(body_fields: &HashSet) -> usize { let mut size = CborCalculator::get_struct_size(body_fields.len() as u64); for field in body_fields { - size += CborCalculator::get_struct_size(field.to_number()); + size += CborCalculator::get_struct_size(field.to_u64().unwrap()); } size } - pub(super) fn get_wintnesses_set_struct_size(witnesses_fields: &HashSet) -> usize { + pub(super) fn get_wintnesses_set_struct_size( + witnesses_fields: &HashSet, + ) -> usize { let mut size = CborCalculator::get_struct_size(witnesses_fields.len() as u64); for field in witnesses_fields { - size += CborCalculator::get_struct_size(field.to_number()); + size += CborCalculator::get_struct_size(field.to_u64().unwrap()); } size } @@ -92,11 +96,12 @@ impl CborCalculator { size } - //TODO: extract iterative logic from estimate_output_cost and estimate_fee to separate function - pub(super) fn estimate_output_cost(used_coins: &Coin, - output_size: usize, - data_cost: &DataCost) -> Result<(Coin, usize), JsError> { + pub(super) fn estimate_output_cost( + used_coins: &Coin, + output_size: usize, + data_cost: &DataCost, + ) -> Result<(Coin, usize), JsError> { let mut current_cost = MinOutputAdaCalculator::calc_size_cost(data_cost, output_size)?; if current_cost <= *used_coins { return Ok((current_cost, output_size)); @@ -119,20 +124,31 @@ impl CborCalculator { Ok((pessimistic_cost, max_size)) } - - pub(super) fn estimate_fee(tx_size_without_fee: usize, - min_dependable_amount: Option, - dependable_amount: Option, - fee_algo: &LinearFee) -> Result<(Coin, usize), JsError> { + pub(super) fn estimate_fee( + tx_size_without_fee: usize, + min_dependable_amount: Option, + dependable_amount: Option, + fee_algo: &LinearFee, + ) -> Result<(Coin, usize), JsError> { let mut current_cost = min_fee_for_size(tx_size_without_fee, fee_algo)?; let mut last_size = tx_size_without_fee + CborCalculator::get_coin_size(¤t_cost); - last_size = Self::recalc_size_with_dependable_value(last_size, ¤t_cost, min_dependable_amount, dependable_amount)?; + last_size = Self::recalc_size_with_dependable_value( + last_size, + ¤t_cost, + min_dependable_amount, + dependable_amount, + )?; for _ in 0..3 { current_cost = min_fee_for_size(last_size, fee_algo)?; let mut new_size = tx_size_without_fee + CborCalculator::get_coin_size(¤t_cost); - new_size = Self::recalc_size_with_dependable_value(new_size, ¤t_cost, min_dependable_amount, dependable_amount)?; + new_size = Self::recalc_size_with_dependable_value( + new_size, + ¤t_cost, + min_dependable_amount, + dependable_amount, + )?; if new_size == last_size { return Ok((current_cost, last_size)); @@ -148,12 +164,16 @@ impl CborCalculator { //if we get ada from somewhere for fee, that means that we reduce size of it can be reduced //by this logic we try to track this - fn recalc_size_with_dependable_value(size: usize, - current_cost: &Coin, - min_dependable_amount: Option, - dependable_amount: Option, ) -> Result { + fn recalc_size_with_dependable_value( + size: usize, + current_cost: &Coin, + min_dependable_amount: Option, + dependable_amount: Option, + ) -> Result { if let Some(dependable_amount) = dependable_amount { - let mut remain_ada = dependable_amount.checked_sub(current_cost).unwrap_or(Coin::zero()); + let mut remain_ada = dependable_amount + .checked_sub(current_cost) + .unwrap_or(Coin::zero()); if let Some(min_dependable_amount) = min_dependable_amount { if remain_ada < min_dependable_amount { remain_ada = min_dependable_amount; @@ -164,4 +184,4 @@ impl CborCalculator { Ok(size) } -} \ No newline at end of file +} diff --git a/rust/src/tx_builder/batch_tools/indexes.rs b/rust/src/builders/batch_tools/indexes.rs similarity index 92% rename from rust/src/tx_builder/batch_tools/indexes.rs rename to rust/src/builders/batch_tools/indexes.rs index ed32a921..427904a1 100644 --- a/rust/src/tx_builder/batch_tools/indexes.rs +++ b/rust/src/builders/batch_tools/indexes.rs @@ -1,4 +1,4 @@ -use super::super::*; +use crate::*; #[derive(PartialEq, Eq, Hash, Clone)] pub struct UtxoIndex(pub(super) usize); @@ -10,4 +10,4 @@ pub struct AssetIndex(pub(super) usize); pub struct PolicyIndex(pub(super) usize); #[derive(PartialEq, Eq, Clone, Hash)] -pub struct PlaneAssetId(pub(super) PolicyIndex, pub(super) AssetName); \ No newline at end of file +pub struct PlaneAssetId(pub(super) PolicyIndex, pub(super) AssetName); diff --git a/rust/src/tx_builder/batch_tools/mod.rs b/rust/src/builders/batch_tools/mod.rs similarity index 88% rename from rust/src/tx_builder/batch_tools/mod.rs rename to rust/src/builders/batch_tools/mod.rs index 6548b6ee..c3015f92 100644 --- a/rust/src/tx_builder/batch_tools/mod.rs +++ b/rust/src/builders/batch_tools/mod.rs @@ -1,7 +1,7 @@ -pub mod utxo_stat; pub mod asset_categorizer; pub mod assets_calculator; -pub mod witnesses_calculator; pub mod cbor_calculator; pub mod indexes; -pub mod proposals; \ No newline at end of file +pub mod proposals; +pub mod utxo_stat; +pub mod witnesses_calculator; diff --git a/rust/src/tx_builder/batch_tools/proposals.rs b/rust/src/builders/batch_tools/proposals.rs similarity index 77% rename from rust/src/tx_builder/batch_tools/proposals.rs rename to rust/src/builders/batch_tools/proposals.rs index f711805d..4a16ebbb 100644 --- a/rust/src/tx_builder/batch_tools/proposals.rs +++ b/rust/src/builders/batch_tools/proposals.rs @@ -1,9 +1,9 @@ -use std::collections::HashMap; -use crate::serialization_tools::map_names::TxBodyNames; -use super::super::*; -use super::indexes::{UtxoIndex, AssetIndex, PolicyIndex}; use super::asset_categorizer::AssetCategorizer; +use super::indexes::{AssetIndex, PolicyIndex, UtxoIndex}; use super::witnesses_calculator::WitnessesCalculator; +use crate::serialization::map_names::TxBodyNames; +use crate::*; +use std::collections::{HashMap, HashSet}; #[derive(Clone)] pub(crate) struct TxOutputProposal { @@ -34,7 +34,9 @@ impl TxOutputProposal { pub(super) fn add_asset(&mut self, asset: &AssetIndex, policy_index: &PolicyIndex) { self.used_assets.insert(asset.clone()); - let policy = self.grouped_assets.entry(policy_index.clone()) + let policy = self + .grouped_assets + .entry(policy_index.clone()) .or_insert(HashSet::new()); policy.insert(asset.clone()); } @@ -67,9 +69,15 @@ impl TxOutputProposal { self.size = size; } - fn create_output(&self, asset_groups: &AssetCategorizer, used_utxos: &HashSet) - -> Result { - Ok(TransactionOutput::new(&self.address, &asset_groups.build_value(used_utxos, self)?)) + fn create_output( + &self, + asset_groups: &AssetCategorizer, + used_utxos: &HashSet, + ) -> Result { + Ok(TransactionOutput::new( + &self.address, + &asset_groups.build_value(used_utxos, self)?, + )) } } @@ -103,7 +111,8 @@ impl TxProposal { } pub(super) fn add_new_output(&mut self, address: &Address) { - self.tx_output_proposals.push(TxOutputProposal::new(address)); + self.tx_output_proposals + .push(TxOutputProposal::new(address)); } pub(super) fn add_asset(&mut self, asset: &AssetIndex, policy_index: &PolicyIndex) { @@ -113,7 +122,12 @@ impl TxProposal { } } - pub(super) fn add_utxo(&mut self, utxo: &UtxoIndex, ada_coins: &Coin, address: &Address) -> Result<(), JsError> { + pub(super) fn add_utxo( + &mut self, + utxo: &UtxoIndex, + ada_coins: &Coin, + address: &Address, + ) -> Result<(), JsError> { if self.used_utoxs.contains(utxo) { return Err(JsError::from_str("UTxO already used")); } @@ -143,23 +157,26 @@ impl TxProposal { self.fee = fee.clone(); } - pub(super) fn get_total_ada_for_ouputs(&self) -> Result { - self.tx_output_proposals.iter() + self.tx_output_proposals + .iter() .map(|output| output.get_total_ada()) .try_fold(Coin::zero(), |acc, ada| acc.checked_add(&ada)) } pub(super) fn get_need_ada(&self) -> Result { - let need_ada = self.get_total_ada_for_ouputs()? - .checked_add(&self.fee)?; - Ok(need_ada.checked_sub(&self.total_ada).unwrap_or(Coin::zero())) + let need_ada = self.get_total_ada_for_ouputs()?.checked_add(&self.fee)?; + Ok(need_ada + .checked_sub(&self.total_ada) + .unwrap_or(Coin::zero())) } pub(super) fn get_unused_ada(&self) -> Result { - let need_ada = self.get_total_ada_for_ouputs()? - .checked_add(&self.fee)?; - return Ok(self.total_ada.checked_sub(&need_ada).unwrap_or(Coin::zero())); + let need_ada = self.get_total_ada_for_ouputs()?.checked_add(&self.fee)?; + return Ok(self + .total_ada + .checked_sub(&need_ada) + .unwrap_or(Coin::zero())); } pub(crate) fn add_last_ada_to_last_output(&mut self) -> Result<(), JsError> { @@ -171,8 +188,11 @@ impl TxProposal { Ok(()) } - pub(crate) fn create_tx(&self, asset_groups: &AssetCategorizer, utxos: &TransactionUnspentOutputs) - -> Result { + pub(crate) fn create_tx( + &self, + asset_groups: &AssetCategorizer, + utxos: &TransactionUnspentOutputs, + ) -> Result { let mut outputs = Vec::new(); for proposal in &self.tx_output_proposals { outputs.push(proposal.create_output(asset_groups, &self.used_utoxs)?); @@ -184,7 +204,7 @@ impl TxProposal { } let body = TransactionBody::new( - &TransactionInputs(inputs), + &TransactionInputs::from_vec(inputs), &TransactionOutputs(outputs), &self.fee, None, @@ -197,4 +217,4 @@ impl TxProposal { Ok(tx) } -} \ No newline at end of file +} diff --git a/rust/src/tx_builder/batch_tools/utxo_stat.rs b/rust/src/builders/batch_tools/utxo_stat.rs similarity index 69% rename from rust/src/tx_builder/batch_tools/utxo_stat.rs rename to rust/src/builders/batch_tools/utxo_stat.rs index 5fd9071f..5bbe3f8b 100644 --- a/rust/src/tx_builder/batch_tools/utxo_stat.rs +++ b/rust/src/builders/batch_tools/utxo_stat.rs @@ -1,6 +1,6 @@ -use std::collections::{HashMap, HashSet}; +use super::indexes::{AssetIndex, PolicyIndex, UtxoIndex}; use crate::{Coin, JsError}; -use super::indexes::{UtxoIndex, AssetIndex, PolicyIndex}; +use std::collections::{HashMap, HashSet}; #[derive(Clone)] pub(super) struct UtxosStat { @@ -11,8 +11,11 @@ pub(super) struct UtxosStat { } impl UtxosStat { - pub(super) fn new(total_ada: &Coin, policy_to_asset: &HashMap>, - amounts: &Vec>) -> Result { + pub(super) fn new( + total_ada: &Coin, + policy_to_asset: &HashMap>, + amounts: &Vec>, + ) -> Result { let mut utxos_stat = UtxosStat { total_policies: 0, assets_in_policy: HashMap::new(), @@ -20,7 +23,9 @@ impl UtxosStat { ada_coins: Coin::zero(), }; for (policy_index, assets) in policy_to_asset { - utxos_stat.assets_in_policy.insert(policy_index.clone(), assets.len()); + utxos_stat + .assets_in_policy + .insert(policy_index.clone(), assets.len()); } for i in 0..amounts.len() { @@ -30,10 +35,11 @@ impl UtxosStat { let new_total = coins.checked_add(amount)?; utxos_stat.coins_in_assets.insert(asset_index, new_total); } else { - utxos_stat.coins_in_assets.insert(asset_index, amount.clone()); + utxos_stat + .coins_in_assets + .insert(asset_index, amount.clone()); } } - } utxos_stat.total_policies = policy_to_asset.len(); @@ -41,4 +47,4 @@ impl UtxosStat { Ok(utxos_stat) } -} \ No newline at end of file +} diff --git a/rust/src/tx_builder/batch_tools/witnesses_calculator.rs b/rust/src/builders/batch_tools/witnesses_calculator.rs similarity index 73% rename from rust/src/tx_builder/batch_tools/witnesses_calculator.rs rename to rust/src/builders/batch_tools/witnesses_calculator.rs index 550412ad..b0c50614 100644 --- a/rust/src/tx_builder/batch_tools/witnesses_calculator.rs +++ b/rust/src/builders/batch_tools/witnesses_calculator.rs @@ -1,6 +1,8 @@ -use crate::serialization_tools::map_names::WitnessSetNames; -use crate::tx_builder::batch_tools::cbor_calculator::CborCalculator; -use super::super::*; +use crate::builders::batch_tools::cbor_calculator::CborCalculator; +use crate::serialization::map_names::WitnessSetNames; +use crate::*; +use std::collections::HashSet; +use crate::builders::fakes::{fake_private_key, fake_raw_key_public, fake_raw_key_sig}; #[derive(Clone)] pub(super) struct WitnessesCalculator { @@ -38,11 +40,15 @@ impl WitnessesCalculator { None => (), } match &addr.payment_cred().to_scripthash() { - Some(_) => return Err(JsError::from_str("Script input is not supported for send all")), - None => () + Some(_) => { + return Err(JsError::from_str( + "Script input is not supported for send all", + )) + } + None => (), } } - None => () + None => (), } match &EnterpriseAddress::from_address(address) { Some(addr) => { @@ -51,8 +57,12 @@ impl WitnessesCalculator { None => (), } match &addr.payment_cred().to_scripthash() { - Some(_) => return Err(JsError::from_str("Script input is not supported for send all")), - None => () + Some(_) => { + return Err(JsError::from_str( + "Script input is not supported for send all", + )) + } + None => (), } } None => (), @@ -64,8 +74,12 @@ impl WitnessesCalculator { None => (), } match &addr.payment_cred().to_scripthash() { - Some(_) => return Err(JsError::from_str("Script input is not supported for send all")), - None => () + Some(_) => { + return Err(JsError::from_str( + "Script input is not supported for send all", + )) + } + None => (), } } None => (), @@ -84,16 +98,16 @@ impl WitnessesCalculator { pub(super) fn create_mock_witnesses_set(&self) -> TransactionWitnessSet { let fake_key_root = fake_private_key(); - let raw_key_public = fake_raw_key_public(); let fake_sig = fake_raw_key_sig(); // recall: this includes keys for input, certs and withdrawals let vkeys = match self.vkeys_count { 0 => None, x => { - let fake_vkey_witness = Vkeywitness::new(&Vkey::new(&raw_key_public), &fake_sig); let mut result = Vkeywitnesses::new(); - for _i in 0..x { + for i in 0..x { + let raw_key_public = fake_raw_key_public(i); + let fake_vkey_witness = Vkeywitness::new(&Vkey::new(&raw_key_public), &fake_sig); result.add(&fake_vkey_witness.clone()); } Some(result) @@ -116,20 +130,21 @@ impl WitnessesCalculator { } }; - TransactionWitnessSet { + TransactionWitnessSet::new_with_partial_dedup( vkeys, - native_scripts: None, - bootstraps: bootstrap_keys, - plutus_scripts: None, - plutus_data: None, - redeemers: None, - } + None, + bootstrap_keys, + None, + None, + None, + ) } fn add_vkey(&mut self) { if self.vkeys_count == 0 { if self.used_fields.len() > 0 { - self.total_size -= CborCalculator::get_wintnesses_set_struct_size(&self.used_fields); + self.total_size -= + CborCalculator::get_wintnesses_set_struct_size(&self.used_fields); } self.used_fields.insert(WitnessSetNames::Vkeys); @@ -148,7 +163,8 @@ impl WitnessesCalculator { self.bootsraps.push(address.clone()); if self.boostrap_count == 0 { if self.used_fields.len() > 0 { - self.total_size -= CborCalculator::get_wintnesses_set_struct_size(&self.used_fields); + self.total_size -= + CborCalculator::get_wintnesses_set_struct_size(&self.used_fields); } self.used_fields.insert(WitnessSetNames::Bootstraps); @@ -162,4 +178,4 @@ impl WitnessesCalculator { self.total_size += CborCalculator::get_struct_size(self.boostrap_count); self.total_size += CborCalculator::get_boostrap_witness_size(address); } -} \ No newline at end of file +} diff --git a/rust/src/builders/certificates_builder.rs b/rust/src/builders/certificates_builder.rs new file mode 100644 index 00000000..50eaed1b --- /dev/null +++ b/rust/src/builders/certificates_builder.rs @@ -0,0 +1,322 @@ +use hashlink::LinkedHashMap; +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct CertificatesBuilder { + certs: LinkedHashMap>, +} + +#[wasm_bindgen] +impl CertificatesBuilder { + pub fn new() -> Self { + Self { certs: LinkedHashMap::new() } + } + + pub fn add(&mut self, cert: &Certificate) -> Result<(), JsError> { + if cert.has_required_script_witness() { + return Err(JsError::from_str( + "Your certificate has a required script witness.\ + Please use .add_with_plutus_witness or .add_with_native_script instead.", + )); + } + + if self.certs.contains_key(cert) { + return Err(JsError::from_str("Certificate already exists")); + } + + self.certs.insert(cert.clone(), None); + Ok(()) + } + + pub fn add_with_plutus_witness( + &mut self, + cert: &Certificate, + witness: &PlutusWitness, + ) -> Result<(), JsError> { + if !cert.has_required_script_witness() { + return Err(JsError::from_str( + "Your certificate does not have a required script witness.\ + Please use .add instead.", + )); + } + + if self.certs.contains_key(cert) { + return Err(JsError::from_str("Certificate already exists")); + } + + self.certs.insert( + cert.clone(), + Some(ScriptWitnessType::PlutusScriptWitness(witness.clone())), + ); + Ok(()) + } + + pub fn add_with_native_script( + &mut self, + cert: &Certificate, + native_script_source: &NativeScriptSource, + ) -> Result<(), JsError> { + if !cert.has_required_script_witness() { + return Err(JsError::from_str( + "Your certificate does not have a required script witness.\ + Please use .add instead.", + )); + } + + if self.certs.contains_key(cert) { + return Err(JsError::from_str("Certificate already exists")); + } + + self.certs.insert( + cert.clone(), + Some(ScriptWitnessType::NativeScriptWitness( + native_script_source.0.clone(), + )), + ); + Ok(()) + } + + pub(crate) fn get_required_signers(&self) -> Ed25519KeyHashes { + let mut set = Ed25519KeyHashes::new(); + for (cert, script_wit) in &self.certs { + let cert_req_signers = witness_keys_for_cert(&cert); + set.extend_move(cert_req_signers); + if let Some(script_wit) = script_wit { + if let Some(signers) = script_wit.get_required_signers() { + set.extend(&signers); + } + } + } + set + } + + pub fn get_plutus_witnesses(&self) -> PlutusWitnesses { + let tag = RedeemerTag::new_cert(); + let mut scripts = PlutusWitnesses::new(); + for (i, (_, script_wit)) in self.certs.iter().enumerate() { + if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = script_wit { + let index = BigNum::from(i); + scripts.add(&s.clone_with_redeemer_index_and_tag(&index, &tag)); + } + } + scripts + } + + pub fn get_ref_inputs(&self) -> TransactionInputs { + let mut inputs = Vec::new(); + for (_, script_wit) in self.certs.iter() { + match script_wit { + Some(script_witness) => { + if let Some(input) = script_witness.get_script_ref_input() { + inputs.push(input); + } + if let Some(input) = script_witness.get_datum_ref_input() { + inputs.push(input); + } + } + None => {} + } + } + TransactionInputs::from_vec(inputs) + } + + pub fn get_native_scripts(&self) -> NativeScripts { + let mut scripts = NativeScripts::new(); + for (_, script_wit) in self.certs.iter() { + if let Some(ScriptWitnessType::NativeScriptWitness( + NativeScriptSourceEnum::NativeScript(script, _), + )) = script_wit + { + scripts.add(script); + } + } + scripts + } + + pub(crate) fn get_used_plutus_lang_versions(&self) -> BTreeSet { + let mut used_langs = BTreeSet::new(); + for (_, script_wit) in &self.certs { + if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = script_wit { + used_langs.insert(s.script.language().clone()); + } + } + used_langs + } + + #[allow(unused_variables)] + pub fn get_certificates_refund( + &self, + pool_deposit: &BigNum, + key_deposit: &BigNum, + ) -> Result { + let mut refund = Coin::zero(); + for (cert, _) in &self.certs { + match &cert.0 { + CertificateEnum::StakeDeregistration(cert) => { + if let Some(coin) = cert.coin { + refund = refund.checked_add(&coin)?; + } else { + refund = refund.checked_add(&key_deposit)?; + } + } + CertificateEnum::DRepDeregistration(cert) => { + refund = refund.checked_add(&cert.coin)?; + } + _ => {} + } + } + Ok(Value::new(&refund)) + } + + pub fn get_certificates_deposit( + &self, + pool_deposit: &BigNum, + key_deposit: &BigNum, + ) -> Result { + let mut deposit = Coin::zero(); + for (cert, _) in &self.certs { + match &cert.0 { + CertificateEnum::PoolRegistration(_) => { + deposit = deposit.checked_add(&pool_deposit)?; + } + CertificateEnum::StakeRegistration(cert) => { + if let Some(coin) = cert.coin { + deposit = deposit.checked_add(&coin)?; + } else { + deposit = deposit.checked_add(&key_deposit)?; + } + } + CertificateEnum::DRepRegistration(cert) => { + deposit = deposit.checked_add(&cert.coin)?; + } + CertificateEnum::StakeRegistrationAndDelegation(cert) => { + deposit = deposit.checked_add(&cert.coin)?; + } + CertificateEnum::VoteRegistrationAndDelegation(cert) => { + deposit = deposit.checked_add(&cert.coin)?; + } + CertificateEnum::StakeVoteRegistrationAndDelegation(cert) => { + deposit = deposit.checked_add(&cert.coin)?; + } + _ => {} + } + } + Ok(deposit) + } + + pub fn has_plutus_scripts(&self) -> bool { + for (_, script_wit) in &self.certs { + if let Some(ScriptWitnessType::PlutusScriptWitness(_)) = script_wit { + return true; + } + } + false + } + + pub fn build(&self) -> Certificates { + let certs = self.certs.iter().map(|(c, _)| c.clone()).collect(); + Certificates::from_vec(certs) + } + + //return only ref inputs that are script refs with added size + //used for calculating the fee for the transaction + //another ref input and also script ref input without size are filtered out + pub(crate) fn get_script_ref_inputs_with_size( + &self, + ) -> impl Iterator { + self.certs.iter() + .filter_map(|(_, opt_wit)| opt_wit.as_ref()) + .filter_map(|script_wit| { + script_wit.get_script_ref_input_with_size() + }) + } +} + +// comes from witsVKeyNeeded in the Ledger spec +fn witness_keys_for_cert(cert_enum: &Certificate) -> RequiredSigners { + let mut set = RequiredSigners::new(); + match &cert_enum.0 { + // stake key registrations do not require a witness + CertificateEnum::StakeRegistration(cert) => { + if cert.coin.is_some() { + if let Some(key) = cert.stake_credential().to_keyhash() { + set.add(&key); + } + } + } + CertificateEnum::StakeDeregistration(cert) => { + if let Some(key) = cert.stake_credential().to_keyhash() { + set.add(&key); + } + } + CertificateEnum::StakeDelegation(cert) => { + if let Some(key) = cert.stake_credential().to_keyhash() { + set.add(&key); + } + } + CertificateEnum::PoolRegistration(cert) => { + set.extend(&cert.pool_params().pool_owners()); + set.add(&cert.pool_params().operator()); + } + CertificateEnum::PoolRetirement(cert) => { + set.add(&cert.pool_keyhash()); + } + CertificateEnum::GenesisKeyDelegation(cert) => { + set.add(&Ed25519KeyHash::from_bytes(cert.genesis_delegate_hash().to_bytes()).unwrap()); + } + // not witness as there is no single core node or genesis key that posts the certificate + CertificateEnum::MoveInstantaneousRewardsCert(_cert) => {} + CertificateEnum::CommitteeHotAuth(cert) => { + if let CredType::Key(key_hash) = &cert.committee_cold_credential.0 { + set.add(key_hash); + } + } + CertificateEnum::CommitteeColdResign(cert) => { + if let CredType::Key(key_hash) = &cert.committee_cold_credential.0 { + set.add(key_hash); + } + } + CertificateEnum::DRepUpdate(cert) => { + if let CredType::Key(key_hash) = &cert.voting_credential.0 { + set.add(key_hash); + } + } + CertificateEnum::DRepRegistration(cert) => { + if let CredType::Key(key_hash) = &cert.voting_credential.0 { + set.add(key_hash); + } + } + CertificateEnum::DRepDeregistration(cert) => { + if let CredType::Key(key_hash) = &cert.voting_credential.0 { + set.add(key_hash); + } + } + CertificateEnum::StakeAndVoteDelegation(cert) => { + if let CredType::Key(key_hash) = &cert.stake_credential.0 { + set.add(key_hash); + } + } + CertificateEnum::VoteDelegation(cert) => { + if let CredType::Key(key_hash) = &cert.stake_credential.0 { + set.add(key_hash); + } + } + CertificateEnum::StakeRegistrationAndDelegation(cert) => { + if let CredType::Key(key_hash) = &cert.stake_credential.0 { + set.add(key_hash); + } + } + CertificateEnum::VoteRegistrationAndDelegation(cert) => { + if let CredType::Key(key_hash) = &cert.stake_credential.0 { + set.add(key_hash); + } + } + CertificateEnum::StakeVoteRegistrationAndDelegation(cert) => { + if let CredType::Key(key_hash) = &cert.stake_credential.0 { + set.add(key_hash); + } + } + } + set +} diff --git a/rust/src/builders/fakes.rs b/rust/src/builders/fakes.rs new file mode 100644 index 00000000..a35f3ddd --- /dev/null +++ b/rust/src/builders/fakes.rs @@ -0,0 +1,37 @@ +use crate::{Bip32PrivateKey, Ed25519Signature, PublicKey}; + +pub(crate) fn fake_private_key() -> Bip32PrivateKey { + Bip32PrivateKey::from_bytes(&[ + 0xb8, 0xf2, 0xbe, 0xce, 0x9b, 0xdf, 0xe2, 0xb0, 0x28, 0x2f, 0x5b, 0xad, 0x70, 0x55, 0x62, + 0xac, 0x99, 0x6e, 0xfb, 0x6a, 0xf9, 0x6b, 0x64, 0x8f, 0x44, 0x45, 0xec, 0x44, 0xf4, 0x7a, + 0xd9, 0x5c, 0x10, 0xe3, 0xd7, 0x2f, 0x26, 0xed, 0x07, 0x54, 0x22, 0xa3, 0x6e, 0xd8, 0x58, + 0x5c, 0x74, 0x5a, 0x0e, 0x11, 0x50, 0xbc, 0xce, 0xba, 0x23, 0x57, 0xd0, 0x58, 0x63, 0x69, + 0x91, 0xf3, 0x8a, 0x37, 0x91, 0xe2, 0x48, 0xde, 0x50, 0x9c, 0x07, 0x0d, 0x81, 0x2a, 0xb2, + 0xfd, 0xa5, 0x78, 0x60, 0xac, 0x87, 0x6b, 0xc4, 0x89, 0x19, 0x2c, 0x1e, 0xf4, 0xce, 0x25, + 0x3c, 0x19, 0x7e, 0xe2, 0x19, 0xa4, + ]) + .unwrap() +} + +pub(crate) fn fake_raw_key_sig() -> Ed25519Signature { + Ed25519Signature::from_bytes(vec![ + 36, 248, 153, 211, 155, 23, 253, 93, 102, 193, 146, 196, 181, 13, 52, 62, 66, 247, 35, 91, + 48, 80, 76, 138, 231, 97, 159, 147, 200, 40, 220, 109, 206, 69, 104, 221, 105, 23, 124, 85, + 24, 40, 73, 45, 119, 122, 103, 39, 253, 102, 194, 251, 204, 189, 168, 194, 174, 237, 146, + 3, 44, 153, 121, 10, + ]) + .unwrap() +} + +pub(crate) fn fake_raw_key_public(x: u64) -> PublicKey { + let mut bytes = [0u8; 64]; + for i in 0..8 { + bytes[i] = ((x >> (i * 8)) & 0xff) as u8; + } + PublicKey::from_bytes(&[ + 207, 118, 57, 154, 33, 13, 232, 114, 14, 159, 168, 148, 228, 94, 65, 226, 154, 181, 37, + 227, 11, 196, 2, 128, bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], + bytes[7] + ]) + .unwrap() +} \ No newline at end of file diff --git a/rust/src/builders/mint_builder.rs b/rust/src/builders/mint_builder.rs new file mode 100644 index 00000000..d7bc9b35 --- /dev/null +++ b/rust/src/builders/mint_builder.rs @@ -0,0 +1,448 @@ +use crate::*; +use std::collections::BTreeMap; + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +enum MintWitnessEnum { + Plutus(PlutusScriptSourceEnum, Redeemer), + NativeScript(NativeScriptSourceEnum), +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[wasm_bindgen] +pub struct MintWitness(MintWitnessEnum); + +#[wasm_bindgen] +impl MintWitness { + pub fn new_native_script(native_script: &NativeScriptSource) -> MintWitness { + MintWitness(MintWitnessEnum::NativeScript(native_script.0.clone())) + } + + pub fn new_plutus_script( + plutus_script: &PlutusScriptSource, + redeemer: &Redeemer, + ) -> MintWitness { + MintWitness(MintWitnessEnum::Plutus( + plutus_script.0.clone(), + redeemer.clone(), + )) + } + + pub(crate) fn script_hash(&self) -> ScriptHash { + match &self.0 { + MintWitnessEnum::NativeScript(native_script) => native_script.script_hash(), + MintWitnessEnum::Plutus(plutus_script, _) => plutus_script.script_hash(), + } + } +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +struct NativeMints { + script: NativeScriptSourceEnum, + mints: BTreeMap, +} + +impl NativeMints { + + #[allow(dead_code)] + fn script_hash(&self) -> PolicyID { + match &self.script { + NativeScriptSourceEnum::NativeScript(script, _) => script.hash(), + NativeScriptSourceEnum::RefInput(_, script_hash, _, _) => script_hash.clone(), + } + } + + fn ref_input(&self) -> Option<&TransactionInput> { + match &self.script { + NativeScriptSourceEnum::RefInput(input, _, _, _) => Some(input), + _ => None, + } + } + + fn native_script(&self) -> Option<&NativeScript> { + match &self.script { + NativeScriptSourceEnum::NativeScript(script, _) => Some(script), + _ => None, + } + } +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +struct PlutusMints { + script: PlutusScriptSourceEnum, + redeemer: Redeemer, + mints: BTreeMap, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +enum ScriptMint { + Plutus(PlutusMints), + Native(NativeMints), +} + +#[derive(Clone, Debug)] +#[wasm_bindgen] +pub struct MintBuilder { + mints: BTreeMap, +} + +#[wasm_bindgen] +impl MintBuilder { + pub fn new() -> MintBuilder { + MintBuilder { + mints: BTreeMap::new(), + } + } + + pub fn add_asset( + &mut self, + mint: &MintWitness, + asset_name: &AssetName, + amount: &Int, + ) -> Result<(), JsError> { + if amount.0 == 0 { + return Err(JsError::from_str("Mint cannot be zero.")); + } + self.update_mint_value(mint, asset_name, amount, false)?; + Ok(()) + } + + pub fn set_asset( + &mut self, + mint: &MintWitness, + asset_name: &AssetName, + amount: &Int, + ) -> Result<(), JsError> { + if amount.0 == 0 { + return Err(JsError::from_str("Mint cannot be zero.")); + } + self.update_mint_value(mint, asset_name, amount, true)?; + Ok(()) + } + + fn update_mint_value( + &mut self, + mint_witness: &MintWitness, + asset_name: &AssetName, + amount: &Int, + overwrite: bool, + ) -> Result<(), JsError> { + if amount.0 == 0 { + return Err(JsError::from_str("Mint cannot be zero.")); + } + let script_mint = self.mints.get(&mint_witness.script_hash()); + Self::validate_mint_witness(mint_witness, script_mint)?; + + match &mint_witness.0 { + MintWitnessEnum::NativeScript(native_script) => { + let script_mint = + self.mints + .entry(native_script.script_hash()) + .or_insert(ScriptMint::Native(NativeMints { + script: native_script.clone(), + mints: BTreeMap::new(), + })); + match script_mint { + ScriptMint::Native(native_mints) => { + let mint = native_mints + .mints + .entry(asset_name.clone()) + .or_insert(Int::new(&BigNum::zero())); + if overwrite { + mint.0 = amount.0; + } else { + mint.0 += amount.0; + } + } + _ => {} + } + } + MintWitnessEnum::Plutus(plutus_script, redeemer) => { + let script_mint = + self.mints + .entry(plutus_script.script_hash()) + .or_insert(ScriptMint::Plutus(PlutusMints { + script: plutus_script.clone(), + redeemer: redeemer.clone(), + mints: BTreeMap::new(), + })); + match script_mint { + ScriptMint::Plutus(plutus_mints) => { + let mint = plutus_mints + .mints + .entry(asset_name.clone()) + .or_insert(Int::new(&BigNum::zero())); + if overwrite { + mint.0 = amount.0; + } else { + mint.0 += amount.0; + } + } + _ => {} + } + } + } + Ok(()) + } + + fn validate_mint_witness( + mint_witness: &MintWitness, + current_script_mint: Option<&ScriptMint>, + ) -> Result<(), JsError> { + if let Some(current_script_mint) = current_script_mint { + match &mint_witness.0 { + MintWitnessEnum::NativeScript(native_script) => { + if let ScriptMint::Native(native_mints) = current_script_mint { + Self::validate_native_source_type(&native_mints.script, &native_script)?; + } else { + return Err(JsError::from_str( + &BuilderError::MintBuilderDifferentScriptType.as_str(), + )); + } + } + MintWitnessEnum::Plutus(plutus_script, redeemer) => { + if let ScriptMint::Plutus(plutus_mints) = current_script_mint { + Self::validate_plutus_script_source_type( + &plutus_mints.script, + &plutus_script, + )?; + if !plutus_mints.redeemer.partially_eq(redeemer) { + return Err(JsError::from_str( + &BuilderError::MintBuilderDifferentRedeemerDataAndExUnits( + plutus_mints.redeemer.to_json()?, + redeemer.to_json()?, + ) + .as_str(), + )); + } + } else { + return Err(JsError::from_str( + &BuilderError::MintBuilderDifferentScriptType.as_str(), + )); + } + } + } + Ok(()) + } else { + Ok(()) + } + } + + fn validate_native_source_type( + current_script_source: &NativeScriptSourceEnum, + input_script_source: &NativeScriptSourceEnum, + ) -> Result<(), JsError> { + match current_script_source { + NativeScriptSourceEnum::NativeScript(_, _) => { + if let NativeScriptSourceEnum::NativeScript(_, _) = input_script_source { + Ok(()) + } else { + Err(JsError::from_str( + &BuilderError::MintBuilderDifferentWitnessTypeNonRef.as_str(), + )) + } + } + NativeScriptSourceEnum::RefInput(_, _, _, _) => { + if let NativeScriptSourceEnum::RefInput(_, _, _, _) = input_script_source { + Ok(()) + } else { + Err(JsError::from_str( + &BuilderError::MintBuilderDifferentWitnessTypeRef.as_str(), + )) + } + } + } + } + + fn validate_plutus_script_source_type( + current_script_source: &PlutusScriptSourceEnum, + input_script_source: &PlutusScriptSourceEnum, + ) -> Result<(), JsError> { + match current_script_source { + PlutusScriptSourceEnum::RefInput(_, _) => { + if let PlutusScriptSourceEnum::RefInput(_, _) = input_script_source { + Ok(()) + } else { + Err(JsError::from_str( + &BuilderError::MintBuilderDifferentWitnessTypeRef.as_str(), + )) + } + } + PlutusScriptSourceEnum::Script(_, _) => { + if let PlutusScriptSourceEnum::Script(_, _) = input_script_source { + Ok(()) + } else { + Err(JsError::from_str( + &BuilderError::MintBuilderDifferentWitnessTypeNonRef.as_str(), + )) + } + } + } + } + + pub(crate) fn build_unchecked(&self) -> Mint { + let mut mint = Mint::new(); + for (policy, script_mint) in self.mints.iter() { + let mut mint_asset = MintAssets::new(); + match script_mint { + ScriptMint::Native(native_mints) => { + for (asset_name, amount) in &native_mints.mints { + mint_asset.insert_unchecked(asset_name, amount.clone()); + } + } + ScriptMint::Plutus(plutus_mints) => { + for (asset_name, amount) in &plutus_mints.mints { + mint_asset.insert_unchecked(asset_name, amount.clone()); + } + } + } + mint.insert(&policy, &mint_asset); + } + mint + } + + pub fn build(&self) -> Result { + let mut mint = Mint::new(); + for (policy, script_mint) in &self.mints { + let mut mint_asset = MintAssets::new(); + match script_mint { + ScriptMint::Native(native_mints) => { + for (asset_name, amount) in &native_mints.mints { + mint_asset.insert(asset_name, amount.clone())?; + } + } + ScriptMint::Plutus(plutus_mints) => { + for (asset_name, amount) in &plutus_mints.mints { + mint_asset.insert(asset_name, amount.clone())?; + } + } + } + mint.insert(&policy, &mint_asset); + } + Ok(mint) + } + + pub fn get_native_scripts(&self) -> NativeScripts { + let mut native_scripts = Vec::new(); + for script_mint in self.mints.values() { + match script_mint { + ScriptMint::Native(native_mints) => { + if let Some(script) = native_mints.native_script() { + native_scripts.push(script.clone()); + } + } + _ => {} + } + } + NativeScripts(native_scripts) + } + + pub fn get_plutus_witnesses(&self) -> PlutusWitnesses { + let mut plutus_witnesses = Vec::new(); + let tag = RedeemerTag::new_mint(); + for (index, (_, script_mint)) in self.mints.iter().enumerate() { + match script_mint { + ScriptMint::Plutus(plutus_mints) => { + plutus_witnesses.push(PlutusWitness::new_with_ref_without_datum( + &PlutusScriptSource(plutus_mints.script.clone()), + &plutus_mints.redeemer.clone_with_index_and_tag( + &BigNum::from(index), + &tag, + ), + )); + } + _ => {} + } + } + PlutusWitnesses(plutus_witnesses) + } + + pub fn get_ref_inputs(&self) -> TransactionInputs { + let mut reference_inputs = Vec::new(); + for script_mint in self.mints.values() { + match script_mint { + ScriptMint::Plutus(plutus_mints) => { + if let PlutusScriptSourceEnum::RefInput(ref_script, _) = &plutus_mints.script { + reference_inputs.push(ref_script.input_ref.clone()); + } + } + ScriptMint::Native(native_mints) => { + if let Some(input) = native_mints.ref_input() { + reference_inputs.push(input.clone()); + } + } + } + } + TransactionInputs::from_vec(reference_inputs) + } + + pub fn get_redeemers(&self) -> Result { + let tag = RedeemerTag::new_mint(); + let mut redeeemers = Vec::new(); + let mut index = BigNum::zero(); + for (_, script_mint) in &self.mints { + match script_mint { + ScriptMint::Plutus(plutus_mints) => { + redeeemers.push(plutus_mints.redeemer.clone_with_index_and_tag(&index, &tag)); + index = index.checked_add(&BigNum::one())?; + } + _ => { + index = index.checked_add(&BigNum::one())?; + } + } + } + Ok(Redeemers::from(redeeemers)) + } + + pub fn has_plutus_scripts(&self) -> bool { + for script_mint in self.mints.values() { + match script_mint { + ScriptMint::Plutus(_) => { + return true; + } + _ => {} + } + } + false + } + + pub fn has_native_scripts(&self) -> bool { + for script_mint in self.mints.values() { + match script_mint { + ScriptMint::Native(_) => { + return true; + } + _ => {} + } + } + false + } + + pub(crate) fn get_used_plutus_lang_versions(&self) -> BTreeSet { + let mut used_langs = BTreeSet::new(); + for (_, script_mint) in &self.mints { + match script_mint { + ScriptMint::Plutus(plutus_mints) => { + used_langs.insert(plutus_mints.script.language()); + } + _ => {} + } + } + used_langs + } + + //return only ref inputs that are script refs with added size + //used for calculating the fee for the transaction + //another ref input and also script ref input without size are filtered out + pub(crate) fn get_script_ref_inputs_with_size( + &self, + ) -> impl Iterator { + self.mints.iter().filter_map(|(_, script_mint)| { + if let ScriptMint::Plutus(plutus_mints) = script_mint { + if let PlutusScriptSourceEnum::RefInput(script_ref, _) = &plutus_mints.script { + return Some((&script_ref.input_ref, script_ref.script_size)); + } + } + None + }) + } +} diff --git a/rust/src/builders/mod.rs b/rust/src/builders/mod.rs new file mode 100644 index 00000000..d32aa4f5 --- /dev/null +++ b/rust/src/builders/mod.rs @@ -0,0 +1,36 @@ +pub(crate) mod batch_tools; + +mod certificates_builder; +pub use certificates_builder::*; + +mod mint_builder; +pub use mint_builder::*; + +mod script_structs; +pub use script_structs::*; + +mod tx_batch_builder; +pub use tx_batch_builder::*; + +mod tx_inputs_builder; +pub use tx_inputs_builder::*; + +mod voting_builder; +pub use voting_builder::*; + +mod voting_proposal_builder; +pub use voting_proposal_builder::*; + +mod withdrawals_builder; +pub use withdrawals_builder::*; + +mod output_builder; +pub use output_builder::*; + +mod tx_builder; +pub use tx_builder::*; + +mod tx_builder_constants; +pub use tx_builder_constants::*; + +pub mod fakes; diff --git a/rust/src/output_builder.rs b/rust/src/builders/output_builder.rs similarity index 85% rename from rust/src/output_builder.rs rename to rust/src/builders/output_builder.rs index a8bc7696..583e8ad4 100644 --- a/rust/src/output_builder.rs +++ b/rust/src/builders/output_builder.rs @@ -1,4 +1,4 @@ -use super::*; +use crate::*; /// We introduce a builder-pattern format for creating transaction outputs /// This is because: @@ -93,21 +93,6 @@ impl TransactionOutputAmountBuilder { cfg } - /// !!! DEPRECATED !!! - /// Since babbage era cardano nodes use coins per byte. Use '.with_asset_and_min_required_coin_by_utxo_cost' instead. - #[deprecated( - since = "11.0.0", - note = "Since babbage era cardano nodes use coins per byte. Use '.with_asset_and_min_required_coin_by_utxo_cost' instead." - )] - pub fn with_asset_and_min_required_coin( - &self, - multiasset: &MultiAsset, - coins_per_utxo_word: &Coin, - ) -> Result { - let data_cost = DataCost::new_coins_per_word(coins_per_utxo_word); - self.with_asset_and_min_required_coin_by_utxo_cost(multiasset, &data_cost) - } - pub fn with_asset_and_min_required_coin_by_utxo_cost( &self, multiasset: &MultiAsset, @@ -152,7 +137,7 @@ impl TransactionOutputAmountBuilder { ))?, plutus_data: self.data.clone(), script_ref: self.script_ref.clone(), - serialization_format: None + serialization_format: None, }) } } diff --git a/rust/src/builders/script_structs/datum_source.rs b/rust/src/builders/script_structs/datum_source.rs new file mode 100644 index 00000000..d0f22573 --- /dev/null +++ b/rust/src/builders/script_structs/datum_source.rs @@ -0,0 +1,22 @@ +use crate::*; + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum DatumSourceEnum { + Datum(PlutusData), + RefInput(TransactionInput), +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct DatumSource(pub(crate) DatumSourceEnum); + +#[wasm_bindgen] +impl DatumSource { + pub fn new(datum: &PlutusData) -> Self { + Self(DatumSourceEnum::Datum(datum.clone())) + } + + pub fn new_ref_input(input: &TransactionInput) -> Self { + Self(DatumSourceEnum::RefInput(input.clone())) + } +} \ No newline at end of file diff --git a/rust/src/builders/script_structs/mod.rs b/rust/src/builders/script_structs/mod.rs new file mode 100644 index 00000000..c468bec0 --- /dev/null +++ b/rust/src/builders/script_structs/mod.rs @@ -0,0 +1,20 @@ +mod plutus_script_source; +pub use plutus_script_source::*; + +mod native_script_source; +pub use native_script_source::*; + +mod datum_source; +pub use datum_source::*; + +mod plutus_witness; +pub use plutus_witness::*; + +mod plutus_witnesses; +pub use plutus_witnesses::*; + +mod plutus_script_ref; +pub(crate) use plutus_script_ref::*; + +mod script_witness_type; +pub(crate) use script_witness_type::*; diff --git a/rust/src/builders/script_structs/native_script_source.rs b/rust/src/builders/script_structs/native_script_source.rs new file mode 100644 index 00000000..39eedb62 --- /dev/null +++ b/rust/src/builders/script_structs/native_script_source.rs @@ -0,0 +1,78 @@ +use crate::*; + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub(crate) enum NativeScriptSourceEnum { + NativeScript(NativeScript, Option), + RefInput(TransactionInput, ScriptHash, Option, usize), +} + +impl NativeScriptSourceEnum { + pub fn script_hash(&self) -> ScriptHash { + match self { + NativeScriptSourceEnum::NativeScript(script, _) => script.hash(), + NativeScriptSourceEnum::RefInput(_, script_hash, _, _) => script_hash.clone(), + } + } + + pub fn required_signers(&self) -> Option { + match self { + NativeScriptSourceEnum::NativeScript(script, required_signers) => { + match required_signers { + Some(signers) => Some(signers.clone()), + None => Some(script.into()) + } + } + NativeScriptSourceEnum::RefInput(_, _, required_signers, _) => required_signers.clone(), + } + } + + pub fn set_required_signers(&mut self, key_hashes: &Ed25519KeyHashes) { + match self { + NativeScriptSourceEnum::NativeScript(_, required_signers) => { + *required_signers = Some(key_hashes.clone()); + } + NativeScriptSourceEnum::RefInput(_, _, required_signers, _) => { + *required_signers = Some(key_hashes.clone()); + } + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct NativeScriptSource(pub(crate) NativeScriptSourceEnum); + +#[wasm_bindgen] +impl NativeScriptSource { + pub fn new(script: &NativeScript) -> Self { + Self(NativeScriptSourceEnum::NativeScript(script.clone(), None)) + } + + pub fn new_ref_input( + script_hash: &ScriptHash, + input: &TransactionInput, + script_size: usize, + ) -> Self { + Self(NativeScriptSourceEnum::RefInput( + input.clone(), + script_hash.clone(), + None, + script_size + )) + } + + pub fn set_required_signers(&mut self, key_hashes: &Ed25519KeyHashes) { + self.0.set_required_signers(key_hashes) + } + + pub(crate) fn script_hash(&self) -> ScriptHash { + self.0.script_hash() + } + + pub fn get_ref_script_size(&self) -> Option { + match &self.0 { + NativeScriptSourceEnum::NativeScript(..) => None, + NativeScriptSourceEnum::RefInput(.., size) => Some(*size) + } + } +} \ No newline at end of file diff --git a/rust/src/builders/script_structs/plutus_script_ref.rs b/rust/src/builders/script_structs/plutus_script_ref.rs new file mode 100644 index 00000000..65a8b373 --- /dev/null +++ b/rust/src/builders/script_structs/plutus_script_ref.rs @@ -0,0 +1,25 @@ +use crate::*; + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub(crate) struct PlutusScriptRef { + pub(crate) input_ref: TransactionInput, + pub(crate) script_hash: ScriptHash, + pub(crate) language: Language, + pub(crate) script_size: usize, +} + +impl PlutusScriptRef { + pub(crate) fn new( + input_ref: TransactionInput, + script_hash: ScriptHash, + language: Language, + script_size: usize, + ) -> Self { + Self { + input_ref, + script_hash, + language, + script_size, + } + } +} diff --git a/rust/src/builders/script_structs/plutus_script_source.rs b/rust/src/builders/script_structs/plutus_script_source.rs new file mode 100644 index 00000000..f52f39c2 --- /dev/null +++ b/rust/src/builders/script_structs/plutus_script_source.rs @@ -0,0 +1,75 @@ +use crate::*; + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub(crate) enum PlutusScriptSourceEnum { + Script(PlutusScript, Option), + RefInput(PlutusScriptRef, Option), +} + +impl PlutusScriptSourceEnum { + pub fn script_hash(&self) -> ScriptHash { + match self { + PlutusScriptSourceEnum::Script(script, ..) => script.hash(), + PlutusScriptSourceEnum::RefInput(script_ref, ..) => script_ref.script_hash.clone() + } + } + + pub fn language(&self) -> Language { + match self { + PlutusScriptSourceEnum::Script(script, ..) => script.language_version(), + PlutusScriptSourceEnum::RefInput(script_ref, ..) => script_ref.language.clone(), + } + } + + pub(crate) fn get_required_signers(&self) -> Option { + match self { + PlutusScriptSourceEnum::Script(_, signers) => signers.clone(), + PlutusScriptSourceEnum::RefInput(_, signers) => signers.clone(), + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PlutusScriptSource(pub(crate) PlutusScriptSourceEnum); + +#[wasm_bindgen] +impl PlutusScriptSource { + pub fn new(script: &PlutusScript) -> Self { + Self(PlutusScriptSourceEnum::Script(script.clone(), None)) + } + pub fn new_ref_input( + script_hash: &ScriptHash, + input: &TransactionInput, + lang_ver: &Language, + script_size: usize, + ) -> Self { + Self(PlutusScriptSourceEnum::RefInput( + PlutusScriptRef::new( + input.clone(), + script_hash.clone(), + lang_ver.clone(), + script_size, + ), + None, + )) + } + + pub fn set_required_signers(&mut self, key_hashes: &Ed25519KeyHashes) { + match &mut self.0 { + PlutusScriptSourceEnum::Script(_, signers) => { + *signers = Some(key_hashes.clone()); + } + PlutusScriptSourceEnum::RefInput(_ , signers) => { + *signers = Some(key_hashes.clone()); + } + } + } + + pub fn get_ref_script_size(&self) -> Option { + match &self.0 { + PlutusScriptSourceEnum::Script(..) => None, + PlutusScriptSourceEnum::RefInput(script_ref, ..) => Some(script_ref.script_size) + } + } +} \ No newline at end of file diff --git a/rust/src/builders/script_structs/plutus_witness.rs b/rust/src/builders/script_structs/plutus_witness.rs new file mode 100644 index 00000000..28beb25a --- /dev/null +++ b/rust/src/builders/script_structs/plutus_witness.rs @@ -0,0 +1,82 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PlutusWitness { + pub(crate) script: PlutusScriptSourceEnum, + pub(crate) datum: Option, + pub(crate) redeemer: Redeemer, +} + +#[wasm_bindgen] +impl PlutusWitness { + pub fn new(script: &PlutusScript, datum: &PlutusData, redeemer: &Redeemer) -> Self { + Self { + script: PlutusScriptSourceEnum::Script(script.clone(), None), + datum: Some(DatumSourceEnum::Datum(datum.clone())), + redeemer: redeemer.clone(), + } + } + + pub fn new_with_ref( + script: &PlutusScriptSource, + datum: &DatumSource, + redeemer: &Redeemer, + ) -> Self { + Self { + script: script.0.clone(), + datum: Some(datum.0.clone()), + redeemer: redeemer.clone(), + } + } + + pub fn new_without_datum(script: &PlutusScript, redeemer: &Redeemer) -> Self { + Self { + script: PlutusScriptSourceEnum::Script(script.clone(), None), + datum: None, + redeemer: redeemer.clone(), + } + } + + pub fn new_with_ref_without_datum(script: &PlutusScriptSource, redeemer: &Redeemer) -> Self { + Self { + script: script.0.clone(), + datum: None, + redeemer: redeemer.clone(), + } + } + + pub fn script(&self) -> Option { + match &self.script { + PlutusScriptSourceEnum::Script(script, _) => Some(script.clone()), + _ => None, + } + } + + pub fn datum(&self) -> Option { + match &self.datum { + Some(DatumSourceEnum::Datum(datum)) => Some(datum.clone()), + _ => None, + } + } + + pub fn redeemer(&self) -> Redeemer { + self.redeemer.clone() + } + + pub(crate) fn clone_with_redeemer_index_and_tag( + &self, + index: &BigNum, + tag: &RedeemerTag, + ) -> Self { + Self { + script: self.script.clone(), + datum: self.datum.clone(), + redeemer: self.redeemer.clone_with_index_and_tag(index, tag), + } + } + + pub(crate) fn get_required_signers(&self) -> Option { + self.script.get_required_signers() + } +} \ No newline at end of file diff --git a/rust/src/builders/script_structs/plutus_witnesses.rs b/rust/src/builders/script_structs/plutus_witnesses.rs new file mode 100644 index 00000000..74e94b62 --- /dev/null +++ b/rust/src/builders/script_structs/plutus_witnesses.rs @@ -0,0 +1,64 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PlutusWitnesses(pub(crate) Vec); + +#[wasm_bindgen] +impl PlutusWitnesses { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> PlutusWitness { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &PlutusWitness) { + self.0.push(elem.clone()); + } + + pub(crate) fn collect(&self) -> (PlutusScripts, Option, Redeemers) { + let mut used_scripts = BTreeSet::new(); + let mut used_datums = BTreeSet::new(); + let mut used_redeemers = BTreeSet::new(); + let mut s = PlutusScripts::new(); + let mut d: Option = None; + let mut r = Redeemers::new(); + self.0.iter().for_each(|w| { + if let PlutusScriptSourceEnum::Script(script, ..) = &w.script { + if used_scripts.insert(script.clone()) { + s.add(script); + } + } + if let Some(DatumSourceEnum::Datum(datum)) = &w.datum { + if used_datums.insert(datum) { + match d { + Some(ref mut d) => d.add(datum), + None => { + d = { + let mut initial_list = PlutusList::new(); + initial_list.add(datum); + Some(initial_list) + } + } + } + } + } + if used_redeemers.insert(w.redeemer.clone()) { + r.add(&w.redeemer); + } + }); + (s, d, r) + } +} + +impl From> for PlutusWitnesses { + fn from(scripts: Vec) -> Self { + Self(scripts) + } +} \ No newline at end of file diff --git a/rust/src/builders/script_structs/script_witness_type.rs b/rust/src/builders/script_structs/script_witness_type.rs new file mode 100644 index 00000000..33304c74 --- /dev/null +++ b/rust/src/builders/script_structs/script_witness_type.rs @@ -0,0 +1,71 @@ +use crate::*; + +#[derive(Clone, Debug)] +pub(crate) enum ScriptWitnessType { + NativeScriptWitness(NativeScriptSourceEnum), + PlutusScriptWitness(PlutusWitness), +} + +impl ScriptWitnessType { + #[allow(dead_code)] + pub(crate) fn script_hash(&self) -> ScriptHash { + match self { + ScriptWitnessType::NativeScriptWitness(script) => script.script_hash(), + ScriptWitnessType::PlutusScriptWitness(script) => script.script.script_hash(), + } + } + + pub(crate) fn get_required_signers(&self) -> Option { + match self { + ScriptWitnessType::NativeScriptWitness(script) => script.required_signers(), + ScriptWitnessType::PlutusScriptWitness(script) => script.get_required_signers(), + } + } + + pub(crate) fn get_script_ref_input(&self) -> Option { + match self { + ScriptWitnessType::NativeScriptWitness(NativeScriptSourceEnum::RefInput(input, ..)) => { + Some(input.clone()) + } + ScriptWitnessType::PlutusScriptWitness(plutus_witness) => { + match &plutus_witness.script { + PlutusScriptSourceEnum::RefInput(script_ref, ..) => { + Some(script_ref.input_ref.clone()) + } + _ => None, + } + } + _ => None, + } + } + + pub(crate) fn get_datum_ref_input(&self) -> Option { + match self { + ScriptWitnessType::PlutusScriptWitness(plutus_witness) => match &plutus_witness.datum { + Some(DatumSourceEnum::RefInput(input)) => Some(input.clone()), + _ => None, + }, + _ => None, + } + } + + pub(crate) fn get_script_ref_input_with_size(&self) -> Option<(&TransactionInput, usize)> { + match self { + ScriptWitnessType::PlutusScriptWitness(plutus_witness) => { + match &plutus_witness.script { + PlutusScriptSourceEnum::RefInput(script_ref, ..) => { + Some((&script_ref.input_ref, script_ref.script_size)) + } + _ => None, + } + } + ScriptWitnessType::NativeScriptWitness(NativeScriptSourceEnum::RefInput( + input, + _, + _, + size, + )) => Some((input, *size)), + _ => None, + } + } +} diff --git a/rust/src/tx_builder/tx_batch_builder.rs b/rust/src/builders/tx_batch_builder.rs similarity index 70% rename from rust/src/tx_builder/tx_batch_builder.rs rename to rust/src/builders/tx_batch_builder.rs index a419972d..049511e2 100644 --- a/rust/src/tx_builder/tx_batch_builder.rs +++ b/rust/src/builders/tx_batch_builder.rs @@ -1,6 +1,6 @@ -use batch_tools::proposals::{TxProposal}; -use batch_tools::asset_categorizer::{AssetCategorizer}; -use super::*; +use crate::*; +use batch_tools::asset_categorizer::AssetCategorizer; +use batch_tools::proposals::TxProposal; #[wasm_bindgen] pub struct TransactionBatchList(Vec); @@ -18,7 +18,7 @@ impl TransactionBatchList { impl<'a> IntoIterator for &'a TransactionBatchList { type Item = &'a TransactionBatch; - type IntoIter = std::slice::Iter<'a, TransactionBatch>; + type IntoIter = std::slice::Iter<'a, TransactionBatch>; fn into_iter(self) -> std::slice::Iter<'a, TransactionBatch> { self.0.iter() @@ -69,7 +69,11 @@ struct TxBatchBuilder { } impl TxBatchBuilder { - pub fn new(utxos: &TransactionUnspentOutputs, address: &Address, config: &TransactionBuilderConfig) -> Result { + pub fn new( + utxos: &TransactionUnspentOutputs, + address: &Address, + config: &TransactionBuilderConfig, + ) -> Result { let asset_groups = AssetCategorizer::new(config, utxos, address)?; Ok(Self { asset_groups, @@ -77,19 +81,28 @@ impl TxBatchBuilder { }) } - pub fn build(&mut self, utxos: &TransactionUnspentOutputs) -> Result { + pub fn build( + &mut self, + utxos: &TransactionUnspentOutputs, + ) -> Result { while self.asset_groups.has_assets() || self.asset_groups.has_ada() { let mut current_tx_proposal = TxProposal::new(); - while let Some(tx_proposal) = self.asset_groups.try_append_next_utxos(¤t_tx_proposal)? { + while let Some(tx_proposal) = self + .asset_groups + .try_append_next_utxos(¤t_tx_proposal)? + { current_tx_proposal = tx_proposal; } - if current_tx_proposal.is_empty() && (self.asset_groups.has_assets() || self.asset_groups.has_ada()) { + if current_tx_proposal.is_empty() + && (self.asset_groups.has_assets() || self.asset_groups.has_ada()) + { return Err(JsError::from_str("Unable to build transaction batch")); } current_tx_proposal.add_last_ada_to_last_output()?; - self.asset_groups.set_min_ada_for_tx(&mut current_tx_proposal)?; + self.asset_groups + .set_min_ada_for_tx(&mut current_tx_proposal)?; self.tx_proposals.push(current_tx_proposal); } @@ -103,10 +116,12 @@ impl TxBatchBuilder { } #[wasm_bindgen] -pub fn create_send_all(address: &Address, utxos: &TransactionUnspentOutputs, config: &TransactionBuilderConfig) - -> Result { +pub fn create_send_all( + address: &Address, + utxos: &TransactionUnspentOutputs, + config: &TransactionBuilderConfig, +) -> Result { let mut tx_batch_builder = TxBatchBuilder::new(utxos, address, config)?; let batch = tx_batch_builder.build(utxos)?; Ok(TransactionBatchList(vec![batch])) } - diff --git a/rust/src/builders/tx_builder.rs b/rust/src/builders/tx_builder.rs new file mode 100644 index 00000000..d89e7384 --- /dev/null +++ b/rust/src/builders/tx_builder.rs @@ -0,0 +1,2496 @@ +#![allow(deprecated)] + +use crate::*; + +use super::*; +use crate::builders::fakes::{fake_private_key, fake_raw_key_public, fake_raw_key_sig}; +use crate::fees; +use crate::utils; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; + +fn count_needed_vkeys(tx_builder: &TransactionBuilder) -> usize { + let mut input_hashes: Ed25519KeyHashes = Ed25519KeyHashes::from(&tx_builder.inputs); + input_hashes.extend_move(Ed25519KeyHashes::from(&tx_builder.collateral)); + input_hashes.extend_move(tx_builder.required_signers.clone()); + if let Some(mint_builder) = &tx_builder.mint { + input_hashes.extend_move(Ed25519KeyHashes::from(&mint_builder.get_native_scripts())); + } + if let Some(withdrawals_builder) = &tx_builder.withdrawals { + input_hashes.extend_move(withdrawals_builder.get_required_signers()); + } + if let Some(certs_builder) = &tx_builder.certs { + input_hashes.extend_move(certs_builder.get_required_signers()); + } + if let Some(voting_builder) = &tx_builder.voting_procedures { + input_hashes.extend_move(voting_builder.get_required_signers()); + } + input_hashes.len() +} + +// tx_body must be the result of building from tx_builder +// constructs the rest of the Transaction using fake witness data of the correct length +// for use in calculating the size of the final Transaction +pub(crate) fn fake_full_tx( + tx_builder: &TransactionBuilder, + body: TransactionBody, +) -> Result { + let fake_key_root = fake_private_key(); + let fake_sig = fake_raw_key_sig(); + + // recall: this includes keys for input, certs and withdrawals + let vkeys = match count_needed_vkeys(tx_builder) { + 0 => None, + x => { + let mut result = Vkeywitnesses::new(); + for i in 0..x { + let raw_key_public = fake_raw_key_public(i as u64); + let fake_vkey_witness = Vkeywitness::new(&Vkey::new(&raw_key_public), &fake_sig); + result.add(&fake_vkey_witness.clone()); + } + Some(result) + } + }; + let bootstraps = get_bootstraps(&tx_builder.inputs); + let bootstrap_keys = match bootstraps.len() { + 0 => None, + _x => { + let mut result = BootstrapWitnesses::new(); + for addr in bootstraps { + // picking icarus over daedalus for fake witness generation shouldn't matter + result.add(&make_icarus_bootstrap_witness( + &TransactionHash::from([0u8; TransactionHash::BYTE_COUNT]), + &ByronAddress::from_bytes(addr.clone()).unwrap(), + &fake_key_root, + )); + } + Some(result) + } + }; + let (plutus_scripts, mut plutus_data, redeemers) = { + if let Some(s) = tx_builder.get_combined_plutus_scripts() { + let (s, d, r) = s.collect(); + (Some(s), d, Some(r)) + } else { + (None, None, None) + } + }; + + if let Some(extra_datums) = &tx_builder.extra_datums { + if let Some(d) = &mut plutus_data { + d.extend(extra_datums); + } else { + plutus_data = Some(extra_datums.clone()); + } + } + + let witness_set = TransactionWitnessSet::new_with_partial_dedup( + vkeys, + tx_builder.get_combined_native_scripts(), + bootstrap_keys, + plutus_scripts, + plutus_data, + redeemers, + ); + Ok(Transaction { + body, + witness_set, + is_valid: true, + auxiliary_data: tx_builder.auxiliary_data.clone(), + }) +} + +fn assert_required_mint_scripts( + mint: &Mint, + maybe_mint_scripts: Option<&NativeScripts>, +) -> Result<(), JsError> { + if maybe_mint_scripts.is_none_or_empty() { + return Err(JsError::from_str( + "Mint is present in the builder, but witness scripts are not provided!", + )); + } + let mint_scripts = maybe_mint_scripts.unwrap(); + let witness_hashes: HashSet = + mint_scripts.0.iter().map(|script| script.hash()).collect(); + for mint_hash in mint.keys().0.iter() { + if !witness_hashes.contains(mint_hash) { + return Err(JsError::from_str(&format!( + "No witness script is found for mint policy '{:?}'! Script is required!", + hex::encode(mint_hash.to_bytes()), + ))); + } + } + Ok(()) +} + +fn min_fee(tx_builder: &TransactionBuilder) -> Result { + // Commented out for performance, `min_fee` is a critical function + // This was mostly added here as a paranoid step anyways + // If someone is using `set_mint` and `add_mint*` API function, everything is expected to be intact + // TODO: figure out if assert is needed here and a better way to do it maybe only once if mint doesn't change + // if let Some(mint) = tx_builder.mint.as_ref() { + // assert_required_mint_scripts(mint, tx_builder.mint_scripts.as_ref())?; + // } + let full_tx = fake_full_tx(tx_builder, tx_builder.build()?)?; + let mut fee: Coin = fees::min_fee(&full_tx, &tx_builder.config.fee_algo)?; + + if let Some(ex_unit_prices) = &tx_builder.config.ex_unit_prices { + let script_fee: Coin = fees::min_script_fee(&full_tx, &ex_unit_prices)?; + fee = fee.checked_add(&script_fee)?; + } else { + if tx_builder.has_plutus_inputs() { + return Err(JsError::from_str( + "Plutus inputs are present but ex_unit_prices are missing in the config!", + )); + } + } + + let total_ref_script_size = tx_builder.get_total_ref_scripts_size()?; + if let Some(ref_script_coins_per_byte) = &tx_builder.config.ref_script_coins_per_byte { + let script_ref_fee = min_ref_script_fee(total_ref_script_size, ref_script_coins_per_byte)?; + fee = fee.checked_add(&script_ref_fee)?; + } else { + if total_ref_script_size > 0 { + return Err(JsError::from_str( + "Plutus scripts are present but ref_script_coins_per_byte are missing in the config!", + )); + } + } + + Ok(fee) +} + +#[wasm_bindgen] +pub enum CoinSelectionStrategyCIP2 { + /// Performs CIP2's Largest First ada-only selection. Will error if outputs contain non-ADA assets. + LargestFirst, + /// Performs CIP2's Random Improve ada-only selection. Will error if outputs contain non-ADA assets. + RandomImprove, + /// Same as LargestFirst, but before adding ADA, will insert by largest-first for each asset type. + LargestFirstMultiAsset, + /// Same as RandomImprove, but before adding ADA, will insert by random-improve for each asset type. + RandomImproveMultiAsset, +} + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct TransactionBuilderConfig { + pub(crate) fee_algo: fees::LinearFee, + pub(crate) pool_deposit: Coin, // protocol parameter + pub(crate) key_deposit: Coin, // protocol parameter + pub(crate) max_value_size: u32, // protocol parameter + pub(crate) max_tx_size: u32, // protocol parameter + pub(crate) data_cost: DataCost, // protocol parameter + pub(crate) ex_unit_prices: Option, // protocol parameter + pub(crate) ref_script_coins_per_byte: Option, // protocol parameter + pub(crate) prefer_pure_change: bool, + pub(crate) deduplicate_explicit_ref_inputs_with_regular_inputs: bool, +} + +impl TransactionBuilderConfig { + pub(crate) fn utxo_cost(&self) -> DataCost { + self.data_cost.clone() + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct TransactionBuilderConfigBuilder { + fee_algo: Option, + pool_deposit: Option, // protocol parameter + key_deposit: Option, // protocol parameter + max_value_size: Option, // protocol parameter + max_tx_size: Option, // protocol parameter + data_cost: Option, // protocol parameter + ex_unit_prices: Option, // protocol parameter + ref_script_coins_per_byte: Option, // protocol parameter + prefer_pure_change: bool, + deduplicate_explicit_ref_inputs_with_regular_inputs: bool, +} + +#[wasm_bindgen] +impl TransactionBuilderConfigBuilder { + pub fn new() -> Self { + Self { + fee_algo: None, + pool_deposit: None, + key_deposit: None, + max_value_size: None, + max_tx_size: None, + data_cost: None, + ex_unit_prices: None, + ref_script_coins_per_byte: None, + prefer_pure_change: false, + deduplicate_explicit_ref_inputs_with_regular_inputs: false, + } + } + + pub fn fee_algo(&self, fee_algo: &fees::LinearFee) -> Self { + let mut cfg = self.clone(); + cfg.fee_algo = Some(fee_algo.clone()); + cfg + } + + pub fn coins_per_utxo_byte(&self, coins_per_utxo_byte: &Coin) -> Self { + let mut cfg = self.clone(); + cfg.data_cost = Some(DataCost::new_coins_per_byte(coins_per_utxo_byte)); + cfg + } + + pub fn ex_unit_prices(&self, ex_unit_prices: &ExUnitPrices) -> Self { + let mut cfg = self.clone(); + cfg.ex_unit_prices = Some(ex_unit_prices.clone()); + cfg + } + + pub fn pool_deposit(&self, pool_deposit: &BigNum) -> Self { + let mut cfg = self.clone(); + cfg.pool_deposit = Some(pool_deposit.clone()); + cfg + } + + pub fn key_deposit(&self, key_deposit: &BigNum) -> Self { + let mut cfg = self.clone(); + cfg.key_deposit = Some(key_deposit.clone()); + cfg + } + + pub fn max_value_size(&self, max_value_size: u32) -> Self { + let mut cfg = self.clone(); + cfg.max_value_size = Some(max_value_size); + cfg + } + + pub fn max_tx_size(&self, max_tx_size: u32) -> Self { + let mut cfg = self.clone(); + cfg.max_tx_size = Some(max_tx_size); + cfg + } + + pub fn ref_script_coins_per_byte(&self, ref_script_coins_per_byte: &UnitInterval) -> Self { + let mut cfg = self.clone(); + cfg.ref_script_coins_per_byte = Some(ref_script_coins_per_byte.clone()); + cfg + } + + pub fn prefer_pure_change(&self, prefer_pure_change: bool) -> Self { + let mut cfg = self.clone(); + cfg.prefer_pure_change = prefer_pure_change; + cfg + } + + ///Removes a ref input (that was set via set_reference_inputs) if the ref inputs was presented in regular tx inputs + pub fn deduplicate_explicit_ref_inputs_with_regular_inputs(&self, deduplicate_explicit_ref_inputs_with_regular_inputs: bool) -> Self { + let mut cfg = self.clone(); + cfg.deduplicate_explicit_ref_inputs_with_regular_inputs = deduplicate_explicit_ref_inputs_with_regular_inputs; + cfg + } + + pub fn build(&self) -> Result { + let cfg: Self = self.clone(); + Ok(TransactionBuilderConfig { + fee_algo: cfg + .fee_algo + .ok_or(JsError::from_str("uninitialized field: fee_algo"))?, + pool_deposit: cfg + .pool_deposit + .ok_or(JsError::from_str("uninitialized field: pool_deposit"))?, + key_deposit: cfg + .key_deposit + .ok_or(JsError::from_str("uninitialized field: key_deposit"))?, + max_value_size: cfg + .max_value_size + .ok_or(JsError::from_str("uninitialized field: max_value_size"))?, + max_tx_size: cfg + .max_tx_size + .ok_or(JsError::from_str("uninitialized field: max_tx_size"))?, + data_cost: cfg.data_cost.ok_or(JsError::from_str( + "uninitialized field: coins_per_utxo_byte or coins_per_utxo_word", + ))?, + ex_unit_prices: cfg.ex_unit_prices, + ref_script_coins_per_byte: cfg.ref_script_coins_per_byte, + prefer_pure_change: cfg.prefer_pure_change, + deduplicate_explicit_ref_inputs_with_regular_inputs: cfg.deduplicate_explicit_ref_inputs_with_regular_inputs, + }) + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct ChangeConfig { + address: Address, + plutus_data: Option, + script_ref: Option, +} + +#[wasm_bindgen] +impl ChangeConfig { + pub fn new(address: &Address) -> Self { + Self { + address: address.clone(), + plutus_data: None, + script_ref: None, + } + } + + pub fn change_address(&self, address: &Address) -> Self { + let mut c_cfg = self.clone(); + c_cfg.address = address.clone(); + c_cfg + } + + pub fn change_plutus_data(&self, plutus_data: &OutputDatum) -> Self { + let mut c_cfg = self.clone(); + c_cfg.plutus_data = Some(plutus_data.clone()); + c_cfg + } + + pub fn change_script_ref(&self, script_ref: &ScriptRef) -> Self { + let mut c_cfg = self.clone(); + c_cfg.script_ref = Some(script_ref.clone()); + c_cfg + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct TransactionBuilder { + pub(crate) config: TransactionBuilderConfig, + pub(crate) inputs: TxInputsBuilder, + pub(crate) collateral: TxInputsBuilder, + pub(crate) outputs: TransactionOutputs, + pub(crate) fee: Option, + pub(crate) ttl: Option, // absolute slot number + pub(crate) certs: Option, + pub(crate) withdrawals: Option, + pub(crate) auxiliary_data: Option, + pub(crate) validity_start_interval: Option, + pub(crate) mint: Option, + pub(crate) script_data_hash: Option, + pub(crate) required_signers: Ed25519KeyHashes, + pub(crate) collateral_return: Option, + pub(crate) total_collateral: Option, + pub(crate) reference_inputs: HashMap, + pub(crate) extra_datums: Option, + pub(crate) voting_procedures: Option, + pub(crate) voting_proposals: Option, + pub(crate) current_treasury_value: Option, + pub(crate) donation: Option, +} + +#[wasm_bindgen] +impl TransactionBuilder { + /// This automatically selects and adds inputs from {inputs} consisting of just enough to cover + /// the outputs that have already been added. + /// This should be called after adding all certs/outputs/etc and will be an error otherwise. + /// Uses CIP2: https://github.com/cardano-foundation/CIPs/blob/master/CIP-0002/CIP-0002.md + /// Adding a change output must be called after via TransactionBuilder::add_change_if_needed() + /// This function, diverging from CIP2, takes into account fees and will attempt to add additional + /// inputs to cover the minimum fees. This does not, however, set the txbuilder's fee. + pub fn add_inputs_from( + &mut self, + inputs: &TransactionUnspentOutputs, + strategy: CoinSelectionStrategyCIP2, + ) -> Result<(), JsError> { + let mut available_inputs: Vec<&TransactionUnspentOutput> = inputs.0.iter().collect(); + let have_no_inputs_in_tx = !self.inputs.has_inputs(); + let mut input_total = self.get_total_input()?; + let mut output_total = self + .get_total_output()? + .checked_add(&Value::new(&self.min_fee()?))?; + + if (input_total.coin >= output_total.coin) && have_no_inputs_in_tx { + if available_inputs.is_empty() { + return Err(JsError::from_str("No inputs to add. Transaction should have at least one input")); + } + + //just add first input, to cover needs of one input + let input = available_inputs.pop().unwrap(); + self.add_regular_input( + &input.output.address, + &input.input, + &input.output.amount, + )?; + input_total = input_total.checked_add(&input.output.amount)?; + } + + match strategy { + CoinSelectionStrategyCIP2::LargestFirst => { + if self + .outputs + .0 + .iter() + .any(|output| output.amount.multiasset.is_some()) + { + return Err(JsError::from_str("Multiasset values not supported by LargestFirst. Please use LargestFirstMultiAsset")); + } + self.cip2_largest_first_by( + &available_inputs, + &mut (0..available_inputs.len()).collect(), + &mut input_total, + &mut output_total, + |value| Some(value.coin), + )?; + } + CoinSelectionStrategyCIP2::RandomImprove => { + if self + .outputs + .0 + .iter() + .any(|output| output.amount.multiasset.is_some()) + { + return Err(JsError::from_str("Multiasset values not supported by RandomImprove. Please use RandomImproveMultiAsset")); + } + use rand::Rng; + let mut rng = rand::thread_rng(); + let mut available_indices = + (0..available_inputs.len()).collect::>(); + self.cip2_random_improve_by( + &available_inputs, + &mut available_indices, + &mut input_total, + &mut output_total, + |value| Some(value.coin), + &mut rng, + true, + )?; + // Phase 3: add extra inputs needed for fees (not covered by CIP-2) + // We do this at the end because this new inputs won't be associated with + // a specific output, so the improvement algorithm we do above does not apply here. + while input_total.coin < output_total.coin { + if available_indices.is_empty() { + return Err(JsError::from_str("UTxO Balance Insufficient[x]")); + } + let i = *available_indices + .iter() + .nth(rng.gen_range(0..available_indices.len())) + .unwrap(); + available_indices.remove(&i); + let input = &available_inputs[i]; + let input_fee = self.fee_for_input( + &input.output.address, + &input.input, + &input.output.amount, + )?; + self.add_regular_input( + &input.output.address, + &input.input, + &input.output.amount, + )?; + input_total = input_total.checked_add(&input.output.amount)?; + output_total = output_total.checked_add(&Value::new(&input_fee))?; + } + } + CoinSelectionStrategyCIP2::LargestFirstMultiAsset => { + // indices into {available_inputs} for inputs that contain {policy_id}:{asset_name} + let mut available_indices = (0..available_inputs.len()).collect::>(); + // run largest-fist by each asset type + if let Some(ma) = output_total.multiasset.clone() { + for (policy_id, assets) in ma.0.iter() { + for (asset_name, _) in assets.0.iter() { + self.cip2_largest_first_by( + &available_inputs, + &mut available_indices, + &mut input_total, + &mut output_total, + |value| value.multiasset.as_ref()?.get(policy_id)?.get(asset_name), + )?; + } + } + } + // add in remaining ADA + self.cip2_largest_first_by( + &available_inputs, + &mut available_indices, + &mut input_total, + &mut output_total, + |value| Some(value.coin), + )?; + } + CoinSelectionStrategyCIP2::RandomImproveMultiAsset => { + use rand::Rng; + let mut rng = rand::thread_rng(); + let mut available_indices = + (0..available_inputs.len()).collect::>(); + // run random-improve by each asset type + if let Some(ma) = output_total.multiasset.clone() { + for (policy_id, assets) in ma.0.iter() { + for (asset_name, _) in assets.0.iter() { + self.cip2_random_improve_by( + &available_inputs, + &mut available_indices, + &mut input_total, + &mut output_total, + |value| value.multiasset.as_ref()?.get(policy_id)?.get(asset_name), + &mut rng, + false, + )?; + } + } + } + // add in remaining ADA + self.cip2_random_improve_by( + &available_inputs, + &mut available_indices, + &mut input_total, + &mut output_total, + |value| Some(value.coin), + &mut rng, + false, + )?; + // Phase 3: add extra inputs needed for fees (not covered by CIP-2) + // We do this at the end because this new inputs won't be associated with + // a specific output, so the improvement algorithm we do above does not apply here. + while input_total.coin < output_total.coin { + if available_indices.is_empty() { + return Err(JsError::from_str("UTxO Balance Insufficient[x]")); + } + let i = *available_indices + .iter() + .nth(rng.gen_range(0..available_indices.len())) + .unwrap(); + available_indices.remove(&i); + let input = &available_inputs[i]; + let input_fee = self.fee_for_input( + &input.output.address, + &input.input, + &input.output.amount, + )?; + self.add_regular_input( + &input.output.address, + &input.input, + &input.output.amount, + )?; + input_total = input_total.checked_add(&input.output.amount)?; + output_total = output_total.checked_add(&Value::new(&input_fee))?; + } + } + } + + Ok(()) + } + + fn cip2_largest_first_by( + &mut self, + available_inputs: &Vec<&TransactionUnspentOutput>, + available_indices: &mut Vec, + input_total: &mut Value, + output_total: &mut Value, + by: F, + ) -> Result<(), JsError> + where + F: Fn(&Value) -> Option, + { + let mut relevant_indices = available_indices.clone(); + relevant_indices.retain(|i| by(&available_inputs[*i].output.amount).is_some()); + // ordered in ascending order by predicate {by} + relevant_indices + .sort_by_key(|i| by(&available_inputs[*i].output.amount).expect("filtered above")); + + // iterate in decreasing order for predicate {by} + for i in relevant_indices.iter().rev() { + if by(input_total).unwrap_or(BigNum::zero()) + >= by(output_total).expect("do not call on asset types that aren't in the output") + { + break; + } + let input = &available_inputs[*i]; + // differing from CIP2, we include the needed fees in the targets instead of just output values + let input_fee = + self.fee_for_input(&input.output.address, &input.input, &input.output.amount)?; + self.add_regular_input(&input.output.address, &input.input, &input.output.amount)?; + *input_total = input_total.checked_add(&input.output.amount)?; + *output_total = output_total.checked_add(&Value::new(&input_fee))?; + available_indices.swap_remove(available_indices.iter().position(|j| i == j).unwrap()); + } + + if by(input_total).unwrap_or(BigNum::zero()) + < by(output_total).expect("do not call on asset types that aren't in the output") + { + return Err(JsError::from_str("UTxO Balance Insufficient")); + } + + Ok(()) + } + + fn cip2_random_improve_by( + &mut self, + available_inputs: &Vec<&TransactionUnspentOutput>, + available_indices: &mut BTreeSet, + input_total: &mut Value, + output_total: &mut Value, + by: F, + rng: &mut rand::rngs::ThreadRng, + pure_ada: bool, + ) -> Result<(), JsError> + where + F: Fn(&Value) -> Option, + { + use rand::Rng; + // Phase 1: Random Selection + let mut relevant_indices = available_indices + .iter() + .filter(|i| by(&available_inputs[**i].output.amount).is_some()) + .cloned() + .collect::>(); + let mut associated_indices: BTreeMap> = BTreeMap::new(); + let mut outputs = self + .outputs + .0 + .iter() + .filter(|output| by(&output.amount).is_some()) + .cloned() + .collect::>(); + outputs.sort_by_key(|output| by(&output.amount).expect("filtered above")); + let mut available_coins = by(input_total).unwrap_or(BigNum::zero()); + for output in outputs.iter().rev() { + // TODO: how should we adapt this to inputs being associated when running for other assets? + // if we do these two phases for each asset and don't take into account the other runs for other assets + // then we over-add (and potentially fail if we don't have plenty of inputs) + // On the other hand, the improvement phase it difficult to determine if a change is an improvement + // if we're trying to improve for multiple assets at a time without knowing how important each input is + // e.g. maybe we have lots of asset A but not much of B + // For now I will just have this be entirely separarte per-asset but we might want to in a later commit + // consider the improvements separately and have it take some kind of dot product / distance for assets + // during the improvement phase and have the improvement phase target multiple asset types at once. + // One issue with that is how to scale in between differnet assets. We could maybe normalize them by + // dividing each asset type by the sum of the required asset type in all outputs. + // Another possibility for adapting this to multiasstes is when associating an input x for asset type a + // we try and subtract all other assets b != a from the outputs we're trying to cover. + // It might make sense to diverge further and not consider it per-output and to instead just match against + // the sum of all outputs as one single value. + let mut added = available_coins.clone(); + let needed = by(&output.amount).unwrap(); + while added < needed { + if relevant_indices.is_empty() { + return Err(JsError::from_str("UTxO Balance Insufficient")); + } + let random_index = rng.gen_range(0..relevant_indices.len()); + let i = relevant_indices.swap_remove(random_index); + available_indices.remove(&i); + let input = &available_inputs[i]; + added = added.checked_add( + &by(&input.output.amount) + .expect("do not call on asset types that aren't in the output"), + )?; + associated_indices + .entry(output.clone()) + .or_default() + .push(i); + } + available_coins = added.checked_sub(&needed)?; + } + if !relevant_indices.is_empty() && pure_ada { + // Phase 2: Improvement + for output in outputs.iter_mut() { + let associated = associated_indices.get_mut(output); + if let Some(associated) = associated { + for i in associated.iter_mut() { + let random_index = rng.gen_range(0..relevant_indices.len()); + let j: &mut usize = relevant_indices.get_mut(random_index).unwrap(); + let input = &available_inputs[*i]; + let new_input = &available_inputs[*j]; + let cur: u64 = (&by(&input.output.amount).unwrap_or(BigNum::zero())).into(); + let new: u64 = (&by(&new_input.output.amount).unwrap_or(BigNum::zero())).into(); + let min: u64 = (&by(&output.amount).unwrap_or(BigNum::zero())).into(); + let ideal = 2 * min; + let max = 3 * min; + let move_closer = + (ideal as i128 - new as i128).abs() < (ideal as i128 - cur as i128).abs(); + let not_exceed_max = new < max; + if move_closer && not_exceed_max { + std::mem::swap(i, j); + available_indices.insert(*i); + available_indices.remove(j); + } + } + } + } + } + + // after finalizing the improvement we need to actually add these results to the builder + for output in outputs.iter() { + if let Some(associated) = associated_indices.get(output) { + for i in associated.iter() { + let input = &available_inputs[*i]; + let input_fee = self.fee_for_input( + &input.output.address, + &input.input, + &input.output.amount, + )?; + self.add_regular_input( + &input.output.address, + &input.input, + &input.output.amount, + )?; + *input_total = input_total.checked_add(&input.output.amount)?; + *output_total = output_total.checked_add(&Value::new(&input_fee))?; + } + } + } + + Ok(()) + } + + pub fn set_inputs(&mut self, inputs: &TxInputsBuilder) { + self.inputs = inputs.clone(); + } + + pub fn set_collateral(&mut self, collateral: &TxInputsBuilder) { + self.collateral = collateral.clone(); + } + + pub fn set_collateral_return(&mut self, collateral_return: &TransactionOutput) { + self.collateral_return = Some(collateral_return.clone()); + } + + pub fn remove_collateral_return(&mut self) { + self.collateral_return = None; + } + + /// This function will set the collateral-return value and then auto-calculate and assign + /// the total collateral coin value. Will raise an error in case no collateral inputs are set + /// or in case the total collateral value will have any assets in it except coin. + pub fn set_collateral_return_and_total( + &mut self, + collateral_return: &TransactionOutput, + ) -> Result<(), JsError> { + let collateral = &self.collateral; + if collateral.len() == 0 { + return Err(JsError::from_str( + "Cannot calculate total collateral value when collateral inputs are missing", + )); + } + let col_input_value: Value = collateral.total_value()?; + let total_col: Value = col_input_value.checked_sub(&collateral_return.amount())?; + if total_col.multiasset.is_some() { + return Err(JsError::from_str( + "Total collateral value cannot contain assets!", + )); + } + + let min_ada = min_ada_for_output(&collateral_return, &self.config.utxo_cost())?; + if min_ada > collateral_return.amount.coin { + return Err(JsError::from_str(&format!( + "Not enough coin to make return on the collateral value!\ + Increase amount of return coins. \ + Min ada for return {}, but was {}", + min_ada, collateral_return.amount.coin + ))); + } + + self.set_collateral_return(collateral_return); + self.total_collateral = Some(total_col.coin); + Ok(()) + } + + pub fn set_total_collateral(&mut self, total_collateral: &Coin) { + self.total_collateral = Some(total_collateral.clone()); + } + + pub fn remove_total_collateral(&mut self) { + self.total_collateral = None; + } + + /// This function will set the total-collateral coin and then auto-calculate and assign + /// the collateral return value. Will raise an error in case no collateral inputs are set. + /// The specified address will be the received of the collateral return + pub fn set_total_collateral_and_return( + &mut self, + total_collateral: &Coin, + return_address: &Address, + ) -> Result<(), JsError> { + let collateral = &self.collateral; + if collateral.len() == 0 { + return Err(JsError::from_str( + "Cannot calculate collateral return when collateral inputs are missing", + )); + } + let col_input_value: Value = collateral.total_value()?; + let col_return: Value = col_input_value.checked_sub(&Value::new(&total_collateral))?; + if col_return.multiasset.is_some() || col_return.coin > BigNum::zero() { + let return_output = TransactionOutput::new(return_address, &col_return); + let min_ada = min_ada_for_output(&return_output, &self.config.utxo_cost())?; + if min_ada > col_return.coin { + return Err(JsError::from_str(&format!( + "Not enough coin to make return on the collateral value!\ + Decrease the total collateral value or add more collateral inputs. \ + Min ada for return {}, but was {}", + min_ada, col_return.coin + ))); + } + self.collateral_return = Some(return_output); + } + self.set_total_collateral(total_collateral); + + Ok(()) + } + + pub fn add_reference_input(&mut self, reference_input: &TransactionInput) { + self.reference_inputs.insert(reference_input.clone(), 0); + } + + pub fn add_script_reference_input( + &mut self, + reference_input: &TransactionInput, + script_size: usize, + ) { + self.reference_inputs + .insert(reference_input.clone(), script_size); + } + + /// We have to know what kind of inputs these are to know what kind of mock witnesses to create since + /// 1) mock witnesses have different lengths depending on the type which changes the expecting fee + /// 2) Witnesses are a set so we need to get rid of duplicates to avoid over-estimating the fee + #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] + pub fn add_key_input( + &mut self, + hash: &Ed25519KeyHash, + input: &TransactionInput, + amount: &Value, + ) { + self.inputs.add_key_input(hash, input, amount); + } + + /// This method will add the input to the builder and also register the required native script witness + #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] + pub fn add_native_script_input( + &mut self, + script: &NativeScript, + input: &TransactionInput, + amount: &Value, + ) { + self.inputs.add_native_script_input( + &NativeScriptSource::new(script), + input, + amount); + } + + /// This method will add the input to the builder and also register the required plutus witness + #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] + pub fn add_plutus_script_input( + &mut self, + witness: &PlutusWitness, + input: &TransactionInput, + amount: &Value, + ) { + self.inputs.add_plutus_script_input(witness, input, amount); + } + + #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] + pub fn add_bootstrap_input( + &mut self, + hash: &ByronAddress, + input: &TransactionInput, + amount: &Value, + ) { + self.inputs.add_bootstrap_input(hash, input, amount); + } + + /// This function is replace for previous one add_input. + /// The functions adds a non script input, if it is a script input it returns an error. + /// To add script input you need to use add_native_script_input or add_plutus_script_input. + /// Also we recommend to use TxInputsBuilder and .set_inputs, because all add_*_input functions might be removed from transaction builder. + #[deprecated(since = "12.0.0", note = "Use `.set_inputs`")] + pub fn add_regular_input( + &mut self, + address: &Address, + input: &TransactionInput, + amount: &Value, + ) -> Result<(), JsError> { + self.inputs.add_regular_input(address, input, amount) + } + + // This method should be used after outputs of the transaction is defined. + // It will attempt utxo selection initially then add change, if adding change fails + // then it will attempt to use up the rest of the available inputs, attempting to add change + // after every extra input. + pub fn add_inputs_from_and_change( + &mut self, + inputs: &TransactionUnspentOutputs, + strategy: CoinSelectionStrategyCIP2, + change_config: &ChangeConfig, + ) -> Result { + self.add_inputs_from(inputs, strategy)?; + if self.fee.is_some() { + return Err(JsError::from_str( + "Cannot calculate change if fee was explicitly specified", + )) + } + let mut add_change_result = self + .add_change_if_needed_with_optional_script_and_datum( + &change_config.address, + change_config + .plutus_data + .clone() + .map_or(None, |od| Some(od.0)), + change_config.script_ref.clone(), + ); + match add_change_result { + Ok(v) => Ok(v), + Err(e) => { + let mut unused_inputs = TransactionUnspentOutputs::new(); + for input in inputs.into_iter() { + if self + .inputs + .inputs() + .inputs + .iter() + .all(|used_input| input.input() != *used_input) + { + unused_inputs.add(input) + } + } + unused_inputs.0.sort_by_key(|input| { + input + .clone() + .output + .amount + .multiasset + .map_or(0, |ma| ma.len()) + }); + unused_inputs.0.reverse(); + while unused_inputs.0.len() > 0 { + let last_input = unused_inputs.0.pop(); + match last_input { + Some(input) => { + self.add_regular_input( + &input.output().address(), + &input.input(), + &input.output().amount(), + )?; + add_change_result = self + .add_change_if_needed_with_optional_script_and_datum( + &change_config.address, + change_config + .plutus_data + .clone() + .map_or(None, |od| Some(od.0)), + change_config.script_ref.clone(), + ); + if let Ok(value) = add_change_result { + return Ok(value); + } + } + None => { + return Err(JsError::from_str( + "Unable to balance tx with available inputs", + )) + } + } + } + Err(e) + } + } + } + + // This method should be used after outputs of the transaction is defined. + // It will attempt to fill the required values using the inputs given. + // After which, it will attempt to set a collateral return output. + pub fn add_inputs_from_and_change_with_collateral_return( + &mut self, + inputs: &TransactionUnspentOutputs, + strategy: CoinSelectionStrategyCIP2, + change_config: &ChangeConfig, + collateral_percentage: &BigNum, + ) -> Result<(), JsError> { + let mut total_collateral = Value::zero(); + for collateral_input in self.collateral.iter() { + total_collateral = total_collateral.checked_add(&collateral_input.amount)?; + } + + //set fake max total collateral and return + self.set_total_collateral(&total_collateral.coin()); + self.set_collateral_return(&TransactionOutput::new( + &change_config.address, + &total_collateral, + )); + + let add_change_result = self.add_inputs_from_and_change(inputs, strategy, change_config); + + self.remove_collateral_return(); + self.remove_total_collateral(); + + //check if adding inputs and change was successful + add_change_result?; + + let fee = self.get_fee_if_set().ok_or(JsError::from_str( + "Cannot calculate collateral return if fee was not set", + ))?; + + let collateral_required = fee + .checked_mul(&collateral_percentage)? + .div_floor(&BigNum(100)) + .checked_add(&BigNum::one())?; + let set_collateral_result = + self.set_total_collateral_and_return(&collateral_required, &change_config.address); + + if let Err(e) = set_collateral_result { + self.remove_collateral_return(); + self.remove_total_collateral(); + return Err(e); + } + + Ok(()) + } + + /// Returns a copy of the current script input witness scripts in the builder + #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] + pub fn get_native_input_scripts(&self) -> Option { + self.inputs.get_native_input_scripts() + } + + /// Returns a copy of the current plutus input witness scripts in the builder. + /// NOTE: each plutus witness will be cloned with a specific corresponding input index + #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] + pub fn get_plutus_input_scripts(&self) -> Option { + self.inputs.get_plutus_input_scripts() + } + + /// calculates how much the fee would increase if you added a given output + pub fn fee_for_input( + &self, + address: &Address, + input: &TransactionInput, + amount: &Value, + ) -> Result { + let mut self_copy = self.clone(); + + // we need some value for these for it to be a a valid transaction + // but since we're only calculating the difference between the fee of two transactions + // it doesn't matter what these are set as, since it cancels out + self_copy.set_fee(&BigNum::zero()); + + let fee_before = min_fee(&self_copy)?; + + self_copy.add_regular_input(&address, &input, &amount)?; + let fee_after = min_fee(&self_copy)?; + fee_after.checked_sub(&fee_before) + } + + /// Add explicit output via a TransactionOutput object + pub fn add_output(&mut self, output: &TransactionOutput) -> Result<(), JsError> { + let value_size = output.amount.to_bytes().len(); + if value_size > self.config.max_value_size as usize { + return Err(JsError::from_str(&format!( + "Maximum value size of {} exceeded. Found: {}", + self.config.max_value_size, value_size + ))); + } + let min_ada = min_ada_for_output(&output, &self.config.utxo_cost())?; + if output.amount().coin() < min_ada { + Err(JsError::from_str(&format!( + "Value {} less than the minimum UTXO value {}", + output.amount().coin(), + min_ada + ))) + } else { + self.outputs.add(output); + Ok(()) + } + } + + /// calculates how much the fee would increase if you added a given output + pub fn fee_for_output(&self, output: &TransactionOutput) -> Result { + let mut self_copy = self.clone(); + + // we need some value for these for it to be a a valid transaction + // but since we're only calculating the different between the fee of two transactions + // it doesn't matter what these are set as, since it cancels out + self_copy.set_fee(&BigNum::zero()); + + let fee_before = min_fee(&self_copy)?; + + self_copy.add_output(&output)?; + let fee_after = min_fee(&self_copy)?; + fee_after.checked_sub(&fee_before) + } + + pub fn set_fee(&mut self, fee: &Coin) { + self.fee = Some(fee.clone()) + } + + /// !!! DEPRECATED !!! + /// Set ttl value. + #[deprecated( + since = "10.1.0", + note = "Underlying value capacity of ttl (BigNum u64) bigger then Slot32. Use set_ttl_bignum instead." + )] + pub fn set_ttl(&mut self, ttl: Slot32) { + self.ttl = Some(ttl.into()) + } + + pub fn set_ttl_bignum(&mut self, ttl: &SlotBigNum) { + self.ttl = Some(ttl.clone()) + } + + pub fn remove_ttl(&mut self) { + self.ttl = None; + } + + /// !!! DEPRECATED !!! + /// Uses outdated slot number format. + #[deprecated( + since = "10.1.0", + note = "Underlying value capacity of validity_start_interval (BigNum u64) bigger then Slot32. Use set_validity_start_interval_bignum instead." + )] + pub fn set_validity_start_interval(&mut self, validity_start_interval: Slot32) { + self.validity_start_interval = Some(validity_start_interval.into()) + } + + pub fn set_validity_start_interval_bignum(&mut self, validity_start_interval: SlotBigNum) { + self.validity_start_interval = Some(validity_start_interval.clone()) + } + + pub fn remove_validity_start_interval(&mut self) { + self.validity_start_interval = None; + } + + /// !!! DEPRECATED !!! + /// Can emit error if add a cert with script credential. + /// Use set_certs_builder instead. + #[deprecated( + since = "11.4.1", + note = "Can emit an error if you add a cert with script credential. Use set_certs_builder instead." + )] + pub fn set_certs(&mut self, certs: &Certificates) -> Result<(), JsError> { + let mut builder = CertificatesBuilder::new(); + for cert in &certs.certs { + builder.add(cert)?; + } + + self.certs = Some(builder); + + Ok(()) + } + + pub fn remove_certs(&mut self) { + self.certs = None; + } + + pub fn set_certs_builder(&mut self, certs: &CertificatesBuilder) { + self.certs = Some(certs.clone()); + } + + /// !!! DEPRECATED !!! + /// Can emit error if add a withdrawal with script credential. + /// Use set_withdrawals_builder instead. + #[deprecated( + since = "11.4.1", + note = "Can emit an error if you add a withdrawal with script credential. Use set_withdrawals_builder instead." + )] + pub fn set_withdrawals(&mut self, withdrawals: &Withdrawals) -> Result<(), JsError> { + let mut withdrawals_builder = WithdrawalsBuilder::new(); + for (withdrawal, coin) in &withdrawals.0 { + withdrawals_builder.add(&withdrawal, &coin)?; + } + + self.withdrawals = Some(withdrawals_builder); + + Ok(()) + } + + pub fn set_withdrawals_builder(&mut self, withdrawals: &WithdrawalsBuilder) { + self.withdrawals = Some(withdrawals.clone()); + } + + pub fn set_voting_builder(&mut self, voting_builder: &VotingBuilder) { + self.voting_procedures = Some(voting_builder.clone()); + } + + pub fn set_voting_proposal_builder(&mut self, voting_proposal_builder: &VotingProposalBuilder) { + self.voting_proposals = Some(voting_proposal_builder.clone()); + } + + pub fn remove_withdrawals(&mut self) { + self.withdrawals = None; + } + + pub fn get_auxiliary_data(&self) -> Option { + self.auxiliary_data.clone() + } + + /// Set explicit auxiliary data via an AuxiliaryData object + /// It might contain some metadata plus native or Plutus scripts + pub fn set_auxiliary_data(&mut self, auxiliary_data: &AuxiliaryData) { + self.auxiliary_data = Some(auxiliary_data.clone()) + } + + pub fn remove_auxiliary_data(&mut self) { + self.auxiliary_data = None; + } + + /// Set metadata using a GeneralTransactionMetadata object + /// It will be set to the existing or new auxiliary data in this builder + pub fn set_metadata(&mut self, metadata: &GeneralTransactionMetadata) { + let mut aux = self + .auxiliary_data + .as_ref() + .cloned() + .unwrap_or(AuxiliaryData::new()); + aux.set_metadata(metadata); + self.set_auxiliary_data(&aux); + } + + /// Add a single metadatum using TransactionMetadatumLabel and TransactionMetadatum objects + /// It will be securely added to existing or new metadata in this builder + pub fn add_metadatum(&mut self, key: &TransactionMetadatumLabel, val: &TransactionMetadatum) { + let mut metadata = self + .auxiliary_data + .as_ref() + .map(|aux| aux.metadata().as_ref().cloned()) + .unwrap_or(None) + .unwrap_or(GeneralTransactionMetadata::new()); + metadata.insert(key, val); + self.set_metadata(&metadata); + } + + /// Add a single JSON metadatum using a TransactionMetadatumLabel and a String + /// It will be securely added to existing or new metadata in this builder + pub fn add_json_metadatum( + &mut self, + key: &TransactionMetadatumLabel, + val: String, + ) -> Result<(), JsError> { + self.add_json_metadatum_with_schema(key, val, MetadataJsonSchema::NoConversions) + } + + /// Add a single JSON metadatum using a TransactionMetadatumLabel, a String, and a MetadataJsonSchema object + /// It will be securely added to existing or new metadata in this builder + pub fn add_json_metadatum_with_schema( + &mut self, + key: &TransactionMetadatumLabel, + val: String, + schema: MetadataJsonSchema, + ) -> Result<(), JsError> { + let metadatum = encode_json_str_to_metadatum(val, schema)?; + self.add_metadatum(key, &metadatum); + Ok(()) + } + + pub fn set_mint_builder(&mut self, mint_builder: &MintBuilder) { + self.mint = Some(mint_builder.clone()); + } + + pub fn remove_mint_builder(&mut self) { + self.mint = None; + } + + pub fn get_mint_builder(&self) -> Option { + self.mint.clone() + } + + /// !!! DEPRECATED !!! + /// Mints are defining by MintBuilder now. + /// Use `.set_mint_builder()` and `MintBuilder` instead. + #[deprecated( + since = "11.2.0", + note = "Mints are defining by MintBuilder now. Use `.set_mint_builder()` and `MintBuilder` instead." + )] + /// Set explicit Mint object and the required witnesses to this builder + /// it will replace any previously existing mint and mint scripts + /// NOTE! Error will be returned in case a mint policy does not have a matching script + pub fn set_mint(&mut self, mint: &Mint, mint_scripts: &NativeScripts) -> Result<(), JsError> { + assert_required_mint_scripts(mint, Some(mint_scripts))?; + let mut scripts_policies = HashMap::new(); + for scipt in &mint_scripts.0 { + scripts_policies.insert(scipt.hash(), scipt.clone()); + } + + let mut mint_builder = MintBuilder::new(); + + for (policy_id, asset_map) in &mint.0 { + for (asset_name, amount) in &asset_map.0 { + if let Some(script) = scripts_policies.get(policy_id) { + let native_script_source = NativeScriptSource::new(script); + let mint_witness = MintWitness::new_native_script(&native_script_source); + mint_builder.set_asset(&mint_witness, asset_name, amount)?; + } else { + return Err(JsError::from_str( + "Mint policy does not have a matching script", + )); + } + } + } + self.mint = Some(mint_builder); + Ok(()) + } + + /// !!! DEPRECATED !!! + /// Mints are defining by MintBuilder now. + /// Use `.get_mint_builder()` and `.build()` instead. + #[deprecated( + since = "11.2.0", + note = "Mints are defining by MintBuilder now. Use `.get_mint_builder()` and `.build()` instead." + )] + /// Returns a copy of the current mint state in the builder + pub fn get_mint(&self) -> Option { + match &self.mint { + Some(mint) => Some(mint.build().expect("MintBuilder is invalid")), + None => None, + } + } + + /// Returns a copy of the current mint witness scripts in the builder + pub fn get_mint_scripts(&self) -> Option { + match &self.mint { + Some(mint) => Some(mint.get_native_scripts()), + None => None, + } + } + + /// !!! DEPRECATED !!! + /// Mints are defining by MintBuilder now. + /// Use `.set_mint_builder()` and `MintBuilder` instead. + #[deprecated( + since = "11.2.0", + note = "Mints are defining by MintBuilder now. Use `.set_mint_builder()` and `MintBuilder` instead." + )] + /// Add a mint entry to this builder using a PolicyID and MintAssets object + /// It will be securely added to existing or new Mint in this builder + /// It will replace any existing mint assets with the same PolicyID + pub fn set_mint_asset(&mut self, policy_script: &NativeScript, mint_assets: &MintAssets) -> Result<(), JsError> { + let native_script_source = NativeScriptSource::new(policy_script); + let mint_witness = MintWitness::new_native_script(&native_script_source); + if let Some(mint) = &mut self.mint { + for (asset, amount) in mint_assets.0.iter() { + mint.set_asset(&mint_witness, asset, amount)?; + } + } else { + let mut mint = MintBuilder::new(); + for (asset, amount) in mint_assets.0.iter() { + mint.set_asset(&mint_witness, asset, amount)?; + } + self.mint = Some(mint); + } + Ok(()) + } + + /// !!! DEPRECATED !!! + /// Mints are defining by MintBuilder now. + /// Use `.set_mint_builder()` and `MintBuilder` instead. + #[deprecated( + since = "11.2.0", + note = "Mints are defining by MintBuilder now. Use `.set_mint_builder()` and `MintBuilder` instead." + )] + /// Add a mint entry to this builder using a PolicyID, AssetName, and Int object for amount + /// It will be securely added to existing or new Mint in this builder + /// It will replace any previous existing amount same PolicyID and AssetName + pub fn add_mint_asset( + &mut self, + policy_script: &NativeScript, + asset_name: &AssetName, + amount: &Int, + ) -> Result<(), JsError> { + let native_script_source = NativeScriptSource::new(policy_script); + let mint_witness = MintWitness::new_native_script(&native_script_source); + if let Some(mint) = &mut self.mint { + mint.add_asset(&mint_witness, asset_name, &amount)?; + } else { + let mut mint = MintBuilder::new(); + mint.add_asset(&mint_witness, asset_name, &amount)?; + self.mint = Some(mint); + } + Ok(()) + } + + /// Add a mint entry together with an output to this builder + /// Using a PolicyID, AssetName, Int for amount, Address, and Coin (BigNum) objects + /// The asset will be securely added to existing or new Mint in this builder + /// A new output will be added with the specified Address, the Coin value, and the minted asset + pub fn add_mint_asset_and_output( + &mut self, + policy_script: &NativeScript, + asset_name: &AssetName, + amount: &Int, + output_builder: &TransactionOutputAmountBuilder, + output_coin: &Coin, + ) -> Result<(), JsError> { + if !amount.is_positive() { + return Err(JsError::from_str("Output value must be positive!")); + } + let policy_id: PolicyID = policy_script.hash(); + self.add_mint_asset(policy_script, asset_name, amount)?; + let multiasset = + Mint::new_from_entry(&policy_id, &MintAssets::new_from_entry(asset_name, amount)?) + .as_positive_multiasset(); + + self.add_output( + &output_builder + .with_coin_and_asset(&output_coin, &multiasset) + .build()?, + ) + } + + /// Add a mint entry together with an output to this builder + /// Using a PolicyID, AssetName, Int for amount, and Address objects + /// The asset will be securely added to existing or new Mint in this builder + /// A new output will be added with the specified Address and the minted asset + /// The output will be set to contain the minimum required amount of Coin + pub fn add_mint_asset_and_output_min_required_coin( + &mut self, + policy_script: &NativeScript, + asset_name: &AssetName, + amount: &Int, + output_builder: &TransactionOutputAmountBuilder, + ) -> Result<(), JsError> { + if !amount.is_positive() { + return Err(JsError::from_str("Output value must be positive!")); + } + let policy_id: PolicyID = policy_script.hash(); + self.add_mint_asset(policy_script, asset_name, amount)?; + let multiasset = + Mint::new_from_entry(&policy_id, &MintAssets::new_from_entry(asset_name, amount)?) + .as_positive_multiasset(); + + self.add_output( + &output_builder + .with_asset_and_min_required_coin_by_utxo_cost( + &multiasset, + &self.config.utxo_cost(), + )? + .build()?, + ) + } + + pub fn add_extra_witness_datum(&mut self, datum: &PlutusData) { + if let Some(extra_datums) = &mut self.extra_datums { + extra_datums.add(datum); + } else { + let mut extra_datums = PlutusList::new(); + extra_datums.add(datum); + self.extra_datums = Some(extra_datums); + } + } + + pub fn get_extra_witness_datums(&self) -> Option { + self.extra_datums.clone() + } + + pub fn set_donation(&mut self, donation: &Coin) { + self.donation = Some(donation.clone()); + } + + pub fn get_donation(&self) -> Option { + self.donation.clone() + } + + pub fn set_current_treasury_value( + &mut self, + current_treasury_value: &Coin, + ) -> Result<(), JsError> { + if current_treasury_value == &Coin::zero() { + return Err(JsError::from_str("Current treasury value cannot be zero!")); + } + self.current_treasury_value = Some(current_treasury_value.clone()); + Ok(()) + } + + pub fn get_current_treasury_value(&self) -> Option { + self.current_treasury_value.clone() + } + + pub fn new(cfg: &TransactionBuilderConfig) -> Self { + Self { + config: cfg.clone(), + inputs: TxInputsBuilder::new(), + collateral: TxInputsBuilder::new(), + outputs: TransactionOutputs::new(), + fee: None, + ttl: None, + certs: None, + withdrawals: None, + auxiliary_data: None, + validity_start_interval: None, + mint: None, + script_data_hash: None, + required_signers: Ed25519KeyHashes::new(), + collateral_return: None, + total_collateral: None, + reference_inputs: HashMap::new(), + extra_datums: None, + voting_procedures: None, + voting_proposals: None, + donation: None, + current_treasury_value: None, + } + } + + pub fn get_reference_inputs(&self) -> TransactionInputs { + let mut inputs: HashSet = HashSet::new(); + + let mut add_ref_inputs_set = |ref_inputs: TransactionInputs| { + for input in ref_inputs { + if !self.inputs.has_input(&input) { + inputs.insert(input); + } + } + }; + + add_ref_inputs_set(self.inputs.get_ref_inputs()); + + if let Some(mint) = &self.mint { + add_ref_inputs_set(mint.get_ref_inputs()); + } + + if let Some(withdrawals) = &self.withdrawals { + add_ref_inputs_set(withdrawals.get_ref_inputs()); + } + + if let Some(certs) = &self.certs { + add_ref_inputs_set(certs.get_ref_inputs()); + } + + if let Some(voting_procedures) = &self.voting_procedures { + add_ref_inputs_set(voting_procedures.get_ref_inputs()); + } + + if let Some(voting_proposals) = &self.voting_proposals { + add_ref_inputs_set(voting_proposals.get_ref_inputs()); + } + + if self.config.deduplicate_explicit_ref_inputs_with_regular_inputs { + add_ref_inputs_set(TransactionInputs::from_vec( + self.reference_inputs.keys().cloned().collect()) + ) + } else { + for input in self.reference_inputs.keys().cloned() { + inputs.insert(input); + } + } + + let vec_inputs = inputs.into_iter().collect(); + TransactionInputs::from_vec(vec_inputs) + } + + fn validate_inputs_intersection(&self) -> Result<(), JsError> { + let ref_inputs = self.get_reference_inputs(); + for input in &ref_inputs { + if self.inputs.has_input(input) { + return Err(JsError::from_str(&format!( + "The reference input {:?} is also present in the regular transaction inputs set. \ + It's not allowed to have the same inputs in both the transaction's inputs set and the reference inputs set. \ + You can use the `deduplicate_explicit_ref_inputs_with_regular_inputs` parameter in the `TransactionConfigBuilder` \ + to enforce the removal of duplicate reference inputs." + , input))); + } + } + Ok(()) + } + + fn get_total_ref_scripts_size(&self) -> Result { + let mut sizes_map = HashMap::new(); + fn add_to_map<'a>( + item: (&'a TransactionInput, usize), + sizes_map: &mut HashMap<&'a TransactionInput, usize>, + ) -> Result<(), JsError> { + if sizes_map.entry(item.0).or_insert(item.1) != &item.1 { + Err(JsError::from_str(&format!( + "Different script sizes for the same ref input {}", + item.0 + ))) + } else { + Ok(()) + } + } + + for item in self.inputs.get_script_ref_inputs_with_size() { + add_to_map(item, &mut sizes_map)? + } + + for (tx_in, size) in &self.reference_inputs { + add_to_map((tx_in, *size), &mut sizes_map)? + } + + if let Some(mint) = &self.mint { + for item in mint.get_script_ref_inputs_with_size() { + add_to_map(item, &mut sizes_map)? + } + } + + if let Some(withdrawals) = &self.withdrawals { + for item in withdrawals.get_script_ref_inputs_with_size() { + add_to_map(item, &mut sizes_map)? + } + } + + if let Some(certs) = &self.certs { + for item in certs.get_script_ref_inputs_with_size() { + add_to_map(item, &mut sizes_map)? + } + } + + if let Some(voting_procedures) = &self.voting_procedures { + for item in voting_procedures.get_script_ref_inputs_with_size() { + add_to_map(item, &mut sizes_map)? + } + } + + if let Some(voting_proposals) = &self.voting_proposals { + for item in voting_proposals.get_script_ref_inputs_with_size() { + add_to_map(item, &mut sizes_map)? + } + } + + Ok(sizes_map.values().sum()) + } + + /// does not include refunds or withdrawals + pub fn get_explicit_input(&self) -> Result { + self.inputs + .iter() + .try_fold(Value::zero(), |acc, ref tx_builder_input| { + acc.checked_add(&tx_builder_input.amount) + }) + } + + /// withdrawals and refunds + pub fn get_implicit_input(&self) -> Result { + let mut implicit_input = Value::zero(); + if let Some(withdrawals) = &self.withdrawals { + implicit_input = implicit_input.checked_add(&withdrawals.get_total_withdrawals()?)?; + } + if let Some(refunds) = &self.certs { + implicit_input = implicit_input.checked_add( + &refunds + .get_certificates_refund(&self.config.pool_deposit, &self.config.key_deposit)?, + )?; + } + + Ok(implicit_input) + } + + /// Returns mint as tuple of (mint_value, burn_value) or two zero values + fn get_mint_as_values(&self) -> (Value, Value) { + self.mint + .as_ref() + .map(|m| { + let mint = m.build_unchecked(); + ( + Value::new_from_assets(&mint.as_positive_multiasset()), + Value::new_from_assets(&mint.as_negative_multiasset()), + ) + }) + .unwrap_or((Value::zero(), Value::zero())) + } + + /// Return explicit input plus implicit input plus mint + pub fn get_total_input(&self) -> Result { + let (mint_value, _) = self.get_mint_as_values(); + self.get_explicit_input()? + .checked_add(&self.get_implicit_input()?)? + .checked_add(&mint_value) + } + + /// Return explicit output plus deposit plus burn + pub fn get_total_output(&self) -> Result { + let (_, burn_value) = self.get_mint_as_values(); + let mut total = self + .get_explicit_output()? + .checked_add(&Value::new(&self.get_deposit()?))? + .checked_add(&burn_value)?; + if let Some(donation) = &self.donation { + total = total.checked_add(&Value::new(donation))?; + } + Ok(total) + } + + /// does not include fee + pub fn get_explicit_output(&self) -> Result { + self.outputs + .0 + .iter() + .try_fold(Value::new(&BigNum::zero()), |acc, ref output| { + acc.checked_add(&output.amount()) + }) + } + + pub fn get_deposit(&self) -> Result { + let mut total_deposit = Coin::zero(); + if let Some(certs) = &self.certs { + total_deposit = + total_deposit.checked_add(&certs.get_certificates_deposit( + &self.config.pool_deposit, + &self.config.key_deposit, + )?)?; + } + + if let Some(voting_proposal_builder) = &self.voting_proposals { + total_deposit = + total_deposit.checked_add(&voting_proposal_builder.get_total_deposit()?)?; + } + + Ok(total_deposit) + } + + pub fn get_fee_if_set(&self) -> Option { + self.fee.clone() + } + + /// Warning: this function will mutate the /fee/ field + /// Make sure to call this function last after setting all other tx-body properties + /// Editing inputs, outputs, mint, etc. after change been calculated + /// might cause a mismatch in calculated fee versus the required fee + pub fn add_change_if_needed(&mut self, address: &Address) -> Result { + self.add_change_if_needed_with_optional_script_and_datum(address, None, None) + } + + pub fn add_change_if_needed_with_datum( + &mut self, + address: &Address, + plutus_data: &OutputDatum, + ) -> Result { + self.add_change_if_needed_with_optional_script_and_datum( + address, + Some(plutus_data.0.clone()), + None, + ) + } + + fn add_change_if_needed_with_optional_script_and_datum( + &mut self, + address: &Address, + plutus_data: Option, + script_ref: Option, + ) -> Result { + let fee = match &self.fee { + None => self.min_fee(), + // generating the change output involves changing the fee + Some(_x) => { + return Err(JsError::from_str( + "Cannot calculate change if fee was explicitly specified", + )) + } + }?; + + let input_total = self.get_total_input()?; + let output_total = self.get_total_output()?; + + let shortage = get_input_shortage(&input_total, &output_total, &fee)?; + if let Some(shortage) = shortage { + return Err(JsError::from_str(&format!( + "Insufficient input in transaction. {}", + shortage + ))); + } + + use std::cmp::Ordering; + match &input_total.partial_cmp(&output_total.checked_add(&Value::new(&fee))?) { + Some(Ordering::Equal) => { + // recall: min_fee assumed the fee was the maximum possible so we definitely have enough input to cover whatever fee it ends up being + self.set_fee(&input_total.checked_sub(&output_total)?.coin()); + Ok(false) + } + Some(Ordering::Less) => Err(JsError::from_str("Insufficient input in transaction")), + Some(Ordering::Greater) => { + fn has_assets(ma: Option) -> bool { + ma.map(|assets| assets.len() > 0).unwrap_or(false) + } + let change_estimator = input_total.checked_sub(&output_total)?; + if has_assets(change_estimator.multiasset()) { + fn will_adding_asset_make_output_overflow( + output: &TransactionOutput, + current_assets: &Assets, + asset_to_add: (PolicyID, AssetName, BigNum), + max_value_size: u32, + data_cost: &DataCost, + ) -> Result { + let (policy, asset_name, value) = asset_to_add; + let mut current_assets_clone = current_assets.clone(); + current_assets_clone.insert(&asset_name, &value); + let mut amount_clone = output.amount.clone(); + let mut val = Value::new(&Coin::zero()); + let mut ma = MultiAsset::new(); + + ma.insert(&policy, ¤t_assets_clone); + val.set_multiasset(&ma); + amount_clone = amount_clone.checked_add(&val)?; + + // calculate minADA for more precise max value size + let mut calc = MinOutputAdaCalculator::new_empty(data_cost)?; + calc.set_amount(&val); + let min_ada = calc.calculate_ada()?; + amount_clone.set_coin(&min_ada); + + Ok(amount_clone.to_bytes().len() > max_value_size as usize) + } + fn pack_nfts_for_change( + max_value_size: u32, + data_cost: &DataCost, + change_address: &Address, + change_estimator: &Value, + plutus_data: &Option, + script_ref: &Option, + ) -> Result, JsError> { + // we insert the entire available ADA temporarily here since that could potentially impact the size + // as it could be 1, 2 3 or 4 bytes for Coin. + let mut change_assets: Vec = Vec::new(); + + let mut base_coin = Value::new(&change_estimator.coin()); + base_coin.set_multiasset(&MultiAsset::new()); + let mut output = TransactionOutput { + address: change_address.clone(), + amount: base_coin.clone(), + plutus_data: plutus_data.clone(), + script_ref: script_ref.clone(), + serialization_format: None, + }; + // If this becomes slow on large TXs we can optimize it like the following + // to avoid cloning + reserializing the entire output. + // This would probably be more relevant if we use a smarter packing algorithm + // which might need to compare more size differences than greedy + //let mut bytes_used = output.to_bytes().len(); + + // a greedy packing is done here to avoid an exponential bin-packing + // which in most cases likely shouldn't be the difference between + // having an extra change output or not unless there are gigantic + // differences in NFT policy sizes + for (policy, assets) in change_estimator.multiasset().unwrap().0.iter() { + // for simplicity we also don't split assets within a single policy since + // you would need to have a very high amoun of assets (which add 1-36 bytes each) + // in a single policy to make a difference. In the future if this becomes an issue + // we can change that here. + + // this is the other part of the optimization but we need to take into account + // the difference between CBOR encoding which can change which happens in two places: + // a) length within assets of one policy id + // b) length of the entire multiasset + // so for simplicity we will just do it the safe, naive way unless + // performance becomes an issue. + //let extra_bytes = policy.to_bytes().len() + assets.to_bytes().len() + 2 + cbor_len_diff; + //if bytes_used + extra_bytes <= max_value_size as usize { + let mut old_amount = output.amount.clone(); + let mut val = Value::new(&Coin::zero()); + let mut next_nft = MultiAsset::new(); + + let asset_names = assets.keys(); + let mut rebuilt_assets = Assets::new(); + for n in 0..asset_names.len() { + let asset_name = asset_names.get(n); + let value = assets.get(&asset_name).unwrap(); + + if will_adding_asset_make_output_overflow( + &output, + &rebuilt_assets, + (policy.clone(), asset_name.clone(), value), + max_value_size, + data_cost, + )? { + // if we got here, this means we will run into a overflow error, + // so we want to split into multiple outputs, for that we... + + // 1. insert the current assets as they are, as this won't overflow + next_nft.insert(policy, &rebuilt_assets); + val.set_multiasset(&next_nft); + output.amount = output.amount.checked_add(&val)?; + change_assets.push(output.amount.multiasset().unwrap()); + + // 2. create a new output with the base coin value as zero + base_coin = Value::new(&Coin::zero()); + base_coin.set_multiasset(&MultiAsset::new()); + output = TransactionOutput { + address: change_address.clone(), + amount: base_coin.clone(), + plutus_data: plutus_data.clone(), + script_ref: script_ref.clone(), + serialization_format: None, + }; + + // 3. continue building the new output from the asset we stopped + old_amount = output.amount.clone(); + val = Value::new(&Coin::zero()); + next_nft = MultiAsset::new(); + + rebuilt_assets = Assets::new(); + } + + rebuilt_assets.insert(&asset_name, &value); + } + + next_nft.insert(policy, &rebuilt_assets); + val.set_multiasset(&next_nft); + output.amount = output.amount.checked_add(&val)?; + + // calculate minADA for more precise max value size + let mut amount_clone = output.amount.clone(); + let mut calc = MinOutputAdaCalculator::new_empty(data_cost)?; + calc.set_amount(&val); + let min_ada = calc.calculate_ada()?; + amount_clone.set_coin(&min_ada); + + if amount_clone.to_bytes().len() > max_value_size as usize { + output.amount = old_amount; + break; + } + } + change_assets.push(output.amount.multiasset().unwrap()); + Ok(change_assets) + } + let mut change_left = input_total.checked_sub(&output_total)?; + let mut new_fee = fee.clone(); + // we might need multiple change outputs for cases where the change has many asset types + // which surpass the max UTXO size limit + let utxo_cost = self.config.utxo_cost(); + let mut calc = MinOutputAdaCalculator::new_empty(&utxo_cost)?; + if let Some(data) = &plutus_data { + match data { + DataOption::DataHash(data_hash) => calc.set_data_hash(data_hash), + DataOption::Data(datum) => calc.set_plutus_data(datum), + }; + } + if let Some(script_ref) = &script_ref { + calc.set_script_ref(script_ref); + } + let minimum_utxo_val = calc.calculate_ada()?; + while let Some(Ordering::Greater) = change_left + .multiasset + .as_ref() + .map_or_else(|| None, |ma| ma.partial_cmp(&MultiAsset::new())) + { + let nft_changes = pack_nfts_for_change( + self.config.max_value_size, + &utxo_cost, + address, + &change_left, + &plutus_data.clone(), + &script_ref.clone(), + )?; + if nft_changes.len() == 0 { + // this likely should never happen + return Err(JsError::from_str("NFTs too large for change output")); + } + // we only add the minimum needed (for now) to cover this output + let mut change_value = Value::new(&Coin::zero()); + for nft_change in nft_changes.iter() { + change_value.set_multiasset(&nft_change); + let mut calc = MinOutputAdaCalculator::new_empty(&utxo_cost)?; + //TODO add precise calculation + let mut fake_change = change_value.clone(); + fake_change.set_coin(&change_left.coin); + calc.set_amount(&fake_change); + if let Some(data) = &plutus_data { + match data { + DataOption::DataHash(data_hash) => { + calc.set_data_hash(data_hash) + } + DataOption::Data(datum) => calc.set_plutus_data(datum), + }; + } + if let Some(script_ref) = &script_ref { + calc.set_script_ref(script_ref); + } + let min_ada = calc.calculate_ada()?; + change_value.set_coin(&min_ada); + let change_output = TransactionOutput { + address: address.clone(), + amount: change_value.clone(), + plutus_data: plutus_data.clone(), + script_ref: script_ref.clone(), + serialization_format: None, + }; + + // increase fee + let fee_for_change = self.fee_for_output(&change_output)?; + new_fee = new_fee.checked_add(&fee_for_change)?; + if change_left.coin() < min_ada.checked_add(&new_fee)? { + return Err(JsError::from_str("Not enough ADA leftover to include non-ADA assets in a change address")); + } + change_left = change_left.checked_sub(&change_value)?; + self.add_output(&change_output)?; + } + } + change_left = change_left.checked_sub(&Value::new(&new_fee))?; + // add potentially a separate pure ADA change output + let left_above_minimum = change_left.coin.compare(&minimum_utxo_val) > 0; + if self.config.prefer_pure_change && left_above_minimum { + let pure_output = TransactionOutput { + address: address.clone(), + amount: change_left.clone(), + plutus_data: plutus_data.clone(), + script_ref: script_ref.clone(), + serialization_format: None, + }; + let additional_fee = self.fee_for_output(&pure_output)?; + let potential_pure_value = + change_left.checked_sub(&Value::new(&additional_fee))?; + let potential_pure_above_minimum = + potential_pure_value.coin.compare(&minimum_utxo_val) > 0; + if potential_pure_above_minimum { + new_fee = new_fee.checked_add(&additional_fee)?; + change_left = Value::zero(); + self.add_output(&TransactionOutput { + address: address.clone(), + amount: potential_pure_value.clone(), + plutus_data: plutus_data.clone(), + script_ref: script_ref.clone(), + serialization_format: None, + })?; + } + } + self.set_fee(&new_fee); + // add in the rest of the ADA + if !change_left.is_zero() { + self.outputs.0.last_mut().unwrap().amount = self + .outputs + .0 + .last() + .unwrap() + .amount + .checked_add(&change_left)?; + } + Ok(true) + } else { + let mut calc = MinOutputAdaCalculator::new_empty(&self.config.utxo_cost())?; + calc.set_amount(&change_estimator); + if let Some(data) = &plutus_data { + match data { + DataOption::DataHash(data_hash) => calc.set_data_hash(data_hash), + DataOption::Data(datum) => calc.set_plutus_data(datum), + }; + } + if let Some(script_ref) = &script_ref { + calc.set_script_ref(script_ref); + } + let min_ada = calc.calculate_ada()?; + + // no-asset case so we have no problem burning the rest if there is no other option + fn burn_extra( + builder: &mut TransactionBuilder, + burn_amount: &BigNum, + ) -> Result { + // recall: min_fee assumed the fee was the maximum possible so we definitely have enough input to cover whatever fee it ends up being + builder.set_fee(burn_amount); + Ok(false) // not enough input to covert the extra fee from adding an output so we just burn whatever is left + } + match change_estimator.coin() >= min_ada { + false => burn_extra(self, &change_estimator.coin()), + true => { + // check how much the fee would increase if we added a change output + let fee_for_change = self.fee_for_output(&TransactionOutput { + address: address.clone(), + amount: change_estimator.clone(), + plutus_data: plutus_data.clone(), + script_ref: script_ref.clone(), + serialization_format: None, + })?; + + let new_fee = fee.checked_add(&fee_for_change)?; + match change_estimator.coin() + >= min_ada.checked_add(&Value::new(&new_fee).coin())? + { + false => burn_extra(self, &change_estimator.coin()), + true => { + // recall: min_fee assumed the fee was the maximum possible so we definitely have enough input to cover whatever fee it ends up being + self.set_fee(&new_fee); + + self.add_output(&TransactionOutput { + address: address.clone(), + amount: change_estimator + .checked_sub(&Value::new(&new_fee.clone()))?, + plutus_data: plutus_data.clone(), + script_ref: script_ref.clone(), + serialization_format: None, + })?; + + Ok(true) + } + } + } + } + } + } + None => Err(JsError::from_str( + "missing input or output for some native asset", + )), + } + } + + /// This method will calculate the script hash data + /// using the plutus datums and redeemers already present in the builder + /// along with the provided cost model, and will register the calculated value + /// in the builder to be used when building the tx body. + /// In case there are no plutus input witnesses present - nothing will change + /// You can set specific hash value using `.set_script_data_hash` + /// NOTE: this function will check which language versions are used in the present scripts + /// and will assert and require for a corresponding cost-model to be present in the passed map. + /// Only the cost-models for the present language versions will be used in the hash calculation. + pub fn calc_script_data_hash(&mut self, cost_models: &Costmdls) -> Result<(), JsError> { + let mut used_langs = BTreeSet::new(); + let mut retained_cost_models = Costmdls::new(); + let mut plutus_witnesses = PlutusWitnesses::new(); + if let Some(mut inputs_plutus) = self.inputs.get_plutus_input_scripts() { + used_langs.append(&mut self.inputs.get_used_plutus_lang_versions()); + plutus_witnesses.0.append(&mut inputs_plutus.0) + } + if let Some(mut collateral_plutus) = self.collateral.get_plutus_input_scripts() { + used_langs.append(&mut self.collateral.get_used_plutus_lang_versions()); + plutus_witnesses.0.append(&mut collateral_plutus.0) + } + if let Some(mint_builder) = &self.mint { + used_langs.append(&mut mint_builder.get_used_plutus_lang_versions()); + plutus_witnesses + .0 + .append(&mut mint_builder.get_plutus_witnesses().0) + } + if let Some(certs_builder) = &self.certs { + used_langs.append(&mut certs_builder.get_used_plutus_lang_versions()); + plutus_witnesses + .0 + .append(&mut certs_builder.get_plutus_witnesses().0) + } + if let Some(withdrawals_builder) = &self.withdrawals { + used_langs.append(&mut withdrawals_builder.get_used_plutus_lang_versions()); + plutus_witnesses + .0 + .append(&mut withdrawals_builder.get_plutus_witnesses().0) + } + if let Some(voting_builder) = &self.voting_procedures { + used_langs.append(&mut voting_builder.get_used_plutus_lang_versions()); + plutus_witnesses + .0 + .append(&mut voting_builder.get_plutus_witnesses().0) + } + + if let Some(voting_proposal_builder) = &self.voting_proposals { + used_langs.append(&mut voting_proposal_builder.get_used_plutus_lang_versions()); + plutus_witnesses + .0 + .append(&mut voting_proposal_builder.get_plutus_witnesses().0) + } + + let (_scripts, mut datums, redeemers) = plutus_witnesses.collect(); + for lang in used_langs { + match cost_models.get(&lang) { + Some(cost) => { + retained_cost_models.insert(&lang, &cost); + } + _ => { + return Err(JsError::from_str(&format!( + "Missing cost model for language version: {:?}", + lang + ))) + } + } + } + + if let Some(extra_datum) = &self.extra_datums { + if datums.is_none() { + datums = Some(PlutusList::new()); + } + + for datum in extra_datum { + if let Some(datums) = &mut datums { + datums.add(datum); + } + } + } + + if datums.is_some() || redeemers.len() > 0 || retained_cost_models.len() > 0 { + self.script_data_hash = + Some(hash_script_data(&redeemers, &retained_cost_models, datums)); + } + + Ok(()) + } + + /// Sets the specified hash value. + /// Alternatively you can use `.calc_script_data_hash` to calculate the hash automatically. + /// Or use `.remove_script_data_hash` to delete the previously set value + pub fn set_script_data_hash(&mut self, hash: &ScriptDataHash) { + self.script_data_hash = Some(hash.clone()); + } + + /// Deletes any previously set plutus data hash value. + /// Use `.set_script_data_hash` or `.calc_script_data_hash` to set it. + pub fn remove_script_data_hash(&mut self) { + self.script_data_hash = None; + } + + pub fn add_required_signer(&mut self, key: &Ed25519KeyHash) { + self.required_signers.add(key); + } + + fn build_and_size(&self) -> Result<(TransactionBody, usize), JsError> { + let fee = self + .fee + .ok_or_else(|| JsError::from_str("Fee not specified"))?; + + let built = TransactionBody { + inputs: self.inputs.inputs(), + outputs: self.outputs.clone(), + fee, + ttl: self.ttl, + certs: self.certs.as_ref().map(|x| x.build()), + withdrawals: self.withdrawals.as_ref().map(|x| x.build()), + update: None, + auxiliary_data_hash: self + .auxiliary_data + .as_ref() + .map(|x| utils::hash_auxiliary_data(x)), + validity_start_interval: self.validity_start_interval, + mint: self.mint.as_ref() + .map(|x| x.build()) + .transpose()?, + script_data_hash: self.script_data_hash.clone(), + collateral: self.collateral.inputs_option(), + required_signers: self.required_signers.to_option(), + network_id: None, + collateral_return: self.collateral_return.clone(), + total_collateral: self.total_collateral.clone(), + reference_inputs: self.get_reference_inputs().to_option(), + voting_procedures: self.voting_procedures.as_ref().map(|x| x.build()), + voting_proposals: self.voting_proposals.as_ref().map(|x| x.build()), + donation: self.donation.clone(), + current_treasury_value: self.current_treasury_value.clone(), + }; + // we must build a tx with fake data (of correct size) to check the final Transaction size + let full_tx = fake_full_tx(self, built)?; + let full_tx_size = full_tx.to_bytes().len(); + return Ok((full_tx.body, full_tx_size)); + } + + pub fn full_size(&self) -> Result { + return self.build_and_size().map(|r| r.1); + } + + pub fn output_sizes(&self) -> Vec { + return self.outputs.0.iter().map(|o| o.to_bytes().len()).collect(); + } + + /// Returns object the body of the new transaction + /// Auxiliary data itself is not included + /// You can use `get_auxiliary_data` or `build_tx` + pub fn build(&self) -> Result { + let (body, full_tx_size) = self.build_and_size()?; + if full_tx_size > self.config.max_tx_size as usize { + Err(JsError::from_str(&format!( + "Maximum transaction size of {} exceeded. Found: {}", + self.config.max_tx_size, full_tx_size + ))) + } else { + Ok(body) + } + } + + fn get_combined_native_scripts(&self) -> Option { + let mut ns = NativeScripts::new(); + if let Some(input_scripts) = self.inputs.get_native_input_scripts() { + input_scripts.0.iter().for_each(|s| { + ns.add(s); + }); + } + if let Some(input_scripts) = self.collateral.get_native_input_scripts() { + input_scripts.0.iter().for_each(|s| { + ns.add(s); + }); + } + if let Some(mint_builder) = &self.mint { + mint_builder.get_native_scripts().0.iter().for_each(|s| { + ns.add(s); + }); + } + if let Some(certificates_builder) = &self.certs { + certificates_builder + .get_native_scripts() + .0 + .iter() + .for_each(|s| { + ns.add(s); + }); + } + if let Some(withdrawals_builder) = &self.withdrawals { + withdrawals_builder + .get_native_scripts() + .0 + .iter() + .for_each(|s| { + ns.add(s); + }); + } + if let Some(voting_builder) = &self.voting_procedures { + voting_builder.get_native_scripts().0.iter().for_each(|s| { + ns.add(s); + }); + } + + if ns.len() > 0 { + Some(ns) + } else { + None + } + } + + fn get_combined_plutus_scripts(&self) -> Option { + let mut res = PlutusWitnesses::new(); + if let Some(scripts) = self.inputs.get_plutus_input_scripts() { + scripts.0.iter().for_each(|s| { + res.add(s); + }) + } + if let Some(scripts) = self.collateral.get_plutus_input_scripts() { + scripts.0.iter().for_each(|s| { + res.add(s); + }) + } + if let Some(mint_builder) = &self.mint { + mint_builder.get_plutus_witnesses().0.iter().for_each(|s| { + res.add(s); + }) + } + if let Some(certificates_builder) = &self.certs { + certificates_builder + .get_plutus_witnesses() + .0 + .iter() + .for_each(|s| { + res.add(s); + }) + } + if let Some(withdrawals_builder) = &self.withdrawals { + withdrawals_builder + .get_plutus_witnesses() + .0 + .iter() + .for_each(|s| { + res.add(s); + }) + } + if let Some(voting_builder) = &self.voting_procedures { + voting_builder + .get_plutus_witnesses() + .0 + .iter() + .for_each(|s| { + res.add(s); + }) + } + if let Some(voting_proposal_builder) = &self.voting_proposals { + voting_proposal_builder + .get_plutus_witnesses() + .0 + .iter() + .for_each(|s| { + res.add(s); + }) + } + if res.len() > 0 { + Some(res) + } else { + None + } + } + + // This function should be producing the total witness-set + // that is created by the tx-builder itself, + // before the transaction is getting signed by the actual wallet. + // E.g. scripts or something else that has been used during the tx preparation + pub(crate) fn get_witness_set(&self) -> TransactionWitnessSet { + let mut wit = TransactionWitnessSet::new(); + if let Some(scripts) = self.get_combined_native_scripts() { + wit.set_native_scripts(&scripts); + } + let mut all_datums = None; + if let Some(pw) = self.get_combined_plutus_scripts() { + let (scripts, datums, redeemers) = pw.collect(); + wit.set_plutus_scripts(&scripts); + all_datums = datums; + wit.set_redeemers(&redeemers); + } + + if let Some(extra_datum) = &self.extra_datums { + if all_datums.is_none() { + all_datums = Some(PlutusList::new()); + } + + for datum in extra_datum { + if let Some(datums) = &mut all_datums { + datums.add(datum); + } + } + } + + if let Some(datums) = &all_datums { + wit.set_plutus_data(datums); + } + + wit + } + + fn has_plutus_inputs(&self) -> bool { + if self.inputs.has_plutus_scripts() { + return true; + } + if self.mint.as_ref().map_or(false, |m| m.has_plutus_scripts()) { + return true; + } + if self + .certs + .as_ref() + .map_or(false, |c| c.has_plutus_scripts()) + { + return true; + } + if self + .withdrawals + .as_ref() + .map_or(false, |w| w.has_plutus_scripts()) + { + return true; + } + if self + .voting_procedures + .as_ref() + .map_or(false, |w| w.has_plutus_scripts()) + { + return true; + } + if self + .voting_proposals + .as_ref() + .map_or(false, |w| w.has_plutus_scripts()) + { + return true; + } + + return false; + } + + /// Returns full Transaction object with the body and the auxiliary data + /// NOTE: witness_set will contain all mint_scripts if any been added or set + /// NOTE: is_valid set to true + /// NOTE: Will fail in case there are any script inputs added with no corresponding witness + pub fn build_tx(&self) -> Result { + if self.has_plutus_inputs() { + if self.script_data_hash.is_none() { + return Err(JsError::from_str( + "Plutus inputs are present, but script data hash is not specified", + )); + } + if self.collateral.len() == 0 { + return Err(JsError::from_str( + "Plutus inputs are present, but no collateral inputs are added", + )); + } + } + self.validate_inputs_intersection()?; + self.build_tx_unsafe() + } + + /// Similar to `.build_tx()` but will NOT fail in case there are missing script witnesses + pub fn build_tx_unsafe(&self) -> Result { + Ok(Transaction { + body: self.build()?, + witness_set: self.get_witness_set(), + is_valid: true, + auxiliary_data: self.auxiliary_data.clone(), + }) + } + + /// warning: sum of all parts of a transaction must equal 0. You cannot just set the fee to the min value and forget about it + /// warning: min_fee may be slightly larger than the actual minimum fee (ex: a few lovelaces) + /// this is done to simplify the library code, but can be fixed later + pub fn min_fee(&self) -> Result { + let mut self_copy = self.clone(); + self_copy.set_fee(&(0x1_00_00_00_00u64).into()); + min_fee(&self_copy) + } +} diff --git a/rust/src/builders/tx_builder_constants.rs b/rust/src/builders/tx_builder_constants.rs new file mode 100644 index 00000000..05696b14 --- /dev/null +++ b/rust/src/builders/tx_builder_constants.rs @@ -0,0 +1,138 @@ +use crate::*; + +// The first element is the cost model, which is an array of 166 operations costs, ordered by asc operaion names. +// The second value is the pre-calculated `language_views_encoding` value required for the script hash creation. +// The cost-model values are taken from the genesis block - https://github.com/input-output-hk/cardano-node/blob/master/configuration/cardano/mainnet-alonzo-genesis.json#L26-L195 +// The keys on the genesis block object (operation names) are sorted plain alphabetically. + +#[wasm_bindgen] +pub struct TxBuilderConstants(); + +#[wasm_bindgen] +impl TxBuilderConstants { + pub fn plutus_default_cost_models() -> Costmdls { + TxBuilderConstants::plutus_vasil_cost_models() + } + + pub fn plutus_alonzo_cost_models() -> Costmdls { + let mut res = Costmdls::new(); + res.insert( + &Language::new_plutus_v1(), + &CostModel::from(vec![ + 197209, 0, 1, 1, 396231, 621, 0, 1, 150000, 1000, 0, 1, 150000, 32, 2477736, 29175, + 4, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 100, + 100, 29773, 100, 150000, 32, 150000, 32, 150000, 32, 150000, 1000, 0, 1, 150000, + 32, 150000, 1000, 0, 8, 148000, 425507, 118, 0, 1, 1, 150000, 1000, 0, 8, 150000, + 112536, 247, 1, 150000, 10000, 1, 136542, 1326, 1, 1000, 150000, 1000, 1, 150000, + 32, 150000, 32, 150000, 32, 1, 1, 150000, 1, 150000, 4, 103599, 248, 1, 103599, + 248, 1, 145276, 1366, 1, 179690, 497, 1, 150000, 32, 150000, 32, 150000, 32, + 150000, 32, 150000, 32, 150000, 32, 148000, 425507, 118, 0, 1, 1, 61516, 11218, 0, + 1, 150000, 32, 148000, 425507, 118, 0, 1, 1, 148000, 425507, 118, 0, 1, 1, 2477736, + 29175, 4, 0, 82363, 4, 150000, 5000, 0, 1, 150000, 32, 197209, 0, 1, 1, 150000, 32, + 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 3345831, 1, + 1, + ]), + ); + res + } + + pub fn plutus_vasil_cost_models() -> Costmdls { + let mut res = Costmdls::new(); + res.insert( + &Language::new_plutus_v1(), + &CostModel::from(vec![ + 205665, 812, 1, 1, 1000, 571, 0, 1, 1000, 24177, 4, 1, 1000, 32, 117366, 10475, 4, + 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 100, 100, + 23000, 100, 19537, 32, 175354, 32, 46417, 4, 221973, 511, 0, 1, 89141, 32, 497525, + 14068, 4, 2, 196500, 453240, 220, 0, 1, 1, 1000, 28662, 4, 2, 245000, 216773, 62, + 1, 1060367, 12586, 1, 208512, 421, 1, 187000, 1000, 52998, 1, 80436, 32, 43249, 32, + 1000, 32, 80556, 1, 57667, 4, 1000, 10, 197145, 156, 1, 197145, 156, 1, 204924, + 473, 1, 208896, 511, 1, 52467, 32, 64832, 32, 65493, 32, 22558, 32, 16563, 32, + 76511, 32, 196500, 453240, 220, 0, 1, 1, 69522, 11687, 0, 1, 60091, 32, 196500, + 453240, 220, 0, 1, 1, 196500, 453240, 220, 0, 1, 1, 806990, 30482, 4, 1927926, + 82523, 4, 265318, 0, 4, 0, 85931, 32, 205665, 812, 1, 1, 41182, 32, 212342, 32, + 31220, 32, 32696, 32, 43357, 32, 32247, 32, 38314, 32, 57996947, 18975, 10, + ]), + ); + res.insert( + &Language::new_plutus_v2(), + &CostModel::from(vec![ + 205665, 812, 1, 1, 1000, 571, 0, 1, 1000, 24177, 4, 1, 1000, 32, 117366, 10475, 4, + 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 100, 100, + 23000, 100, 19537, 32, 175354, 32, 46417, 4, 221973, 511, 0, 1, 89141, 32, 497525, + 14068, 4, 2, 196500, 453240, 220, 0, 1, 1, 1000, 28662, 4, 2, 245000, 216773, 62, + 1, 1060367, 12586, 1, 208512, 421, 1, 187000, 1000, 52998, 1, 80436, 32, 43249, 32, + 1000, 32, 80556, 1, 57667, 4, 1000, 10, 197145, 156, 1, 197145, 156, 1, 204924, + 473, 1, 208896, 511, 1, 52467, 32, 64832, 32, 65493, 32, 22558, 32, 16563, 32, + 76511, 32, 196500, 453240, 220, 0, 1, 1, 69522, 11687, 0, 1, 60091, 32, 196500, + 453240, 220, 0, 1, 1, 196500, 453240, 220, 0, 1, 1, 1159724, 392670, 0, 2, 806990, + 30482, 4, 1927926, 82523, 4, 265318, 0, 4, 0, 85931, 32, 205665, 812, 1, 1, 41182, + 32, 212342, 32, 31220, 32, 32696, 32, 43357, 32, 32247, 32, 38314, 32, 35892428, + 10, 57996947, 18975, 10, 38887044, 32947, 10, + ]), + ); + res + } + + pub fn plutus_conway_cost_models() -> Costmdls { + let mut res = Costmdls::new(); + res.insert( + &Language::new_plutus_v1(), + &CostModel::from(vec![ + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4, + 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100, + 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, + 769, 4, 2, 85848, 228465, 122, 0, 1, 1, 1000, 42921, 4, 2, 24548, 29498, 38, 1, + 898148, 27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, 141895, 32, 83150, 32, + 15299, 32, 76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, 28999, 74, 1, 43285, 552, + 1, 44749, 541, 1, 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32, 11546, 32, + 85848, 228465, 122, 0, 1, 1, 90434, 519, 0, 1, 74433, 32, 85848, 228465, 122, 0, 1, + 1, 85848, 228465, 122, 0, 1, 1, 270652, 22588, 4, 1457325, 64566, 4, 20467, 1, 4, + 0, 141992, 32, 100788, 420, 1, 1, 81663, 32, 59498, 32, 20142, 32, 24588, 32, + 20744, 32, 25933, 32, 24623, 32, 53384111, 14333, 10, + ]), + ); + res.insert( + &Language::new_plutus_v2(), + &CostModel::from(vec![ + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4, + 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100, + 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, + 769, 4, 2, 85848, 228465, 122, 0, 1, 1, 1000, 42921, 4, 2, 24548, 29498, 38, 1, + 898148, 27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, 141895, 32, 83150, 32, + 15299, 32, 76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, 28999, 74, 1, 43285, 552, + 1, 44749, 541, 1, 33852, 32, 68246, 32, 72362, 32, 7243, 32, 7391, 32, 11546, 32, + 85848, 228465, 122, 0, 1, 1, 90434, 519, 0, 1, 74433, 32, 85848, 228465, 122, 0, 1, + 1, 85848, 228465, 122, 0, 1, 1, 955506, 213312, 0, 2, 270652, 22588, 4, 1457325, + 64566, 4, 20467, 1, 4, 0, 141992, 32, 100788, 420, 1, 1, 81663, 32, 59498, 32, + 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, 43053543, 10, 53384111, + 14333, 10, 43574283, 26308, 10, + ]), + ); + res.insert( + &Language::new_plutus_v3(), + &CostModel::from(vec![ + 100788, 420, 1, 1, 1000, 173, 0, 1, 1000, 59957, 4, 1, 11183, 32, 201305, 8356, 4, + 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 16000, 100, 100, 100, + 16000, 100, 94375, 32, 132994, 32, 61462, 4, 72010, 178, 0, 1, 22151, 32, 91189, + 769, 4, 2, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 1, 1000, 42921, + 4, 2, 24548, 29498, 38, 1, 898148, 27279, 1, 51775, 558, 1, 39184, 1000, 60594, 1, + 141895, 32, 83150, 32, 15299, 32, 76049, 1, 13169, 4, 22100, 10, 28999, 74, 1, + 28999, 74, 1, 43285, 552, 1, 44749, 541, 1, 33852, 32, 68246, 32, 72362, 32, 7243, + 32, 7391, 32, 11546, 32, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, + 90434, 519, 0, 1, 74433, 32, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, + 1, 85848, 123203, 7305, -900, 1716, 549, 57, 85848, 0, 1, 955506, 213312, 0, 2, + 270652, 22588, 4, 1457325, 64566, 4, 20467, 1, 4, 0, 141992, 32, 100788, 420, 1, 1, + 81663, 32, 59498, 32, 20142, 32, 24588, 32, 20744, 32, 25933, 32, 24623, 32, + 43053543, 10, 53384111, 14333, 10, 43574283, 26308, 10, 16000, 100, 16000, 100, + 962335, 18, 2780678, 6, 442008, 1, 52538055, 3756, 18, 267929, 18, 76433006, 8868, + 18, 52948122, 18, 1995836, 36, 3227919, 12, 901022, 1, 166917843, 4307, 36, 284546, + 36, 158221314, 26549, 36, 74698472, 36, 333849714, 1, 254006273, 72, 2174038, 72, + 2261318, 64571, 4, 207616, 8310, 4, 1293828, 28716, 63, 0, 1, 1006041, 43623, 251, + 0, 1, + ]), + ); + + return res; + } +} diff --git a/rust/src/builders/tx_inputs_builder.rs b/rust/src/builders/tx_inputs_builder.rs new file mode 100644 index 00000000..f37c03d8 --- /dev/null +++ b/rust/src/builders/tx_inputs_builder.rs @@ -0,0 +1,397 @@ +use crate::*; +use hashlink::LinkedHashMap; +use std::collections::{BTreeMap, BTreeSet}; + +#[derive(Clone, Debug)] +pub(crate) struct TxBuilderInput { + pub(crate) input: TransactionInput, + pub(crate) amount: Value, // we need to keep track of the amount in the inputs for input selection +} + +// We need to know how many of each type of witness will be in the transaction so we can calculate the tx fee +#[derive(Clone, Debug)] +pub struct InputsRequiredWitness { + vkeys: Ed25519KeyHashes, + scripts: LinkedHashMap>>, + bootstraps: BTreeSet>, +} + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct TxInputsBuilder { + inputs: BTreeMap)>, + required_witnesses: InputsRequiredWitness, +} + +pub(crate) fn get_bootstraps(inputs: &TxInputsBuilder) -> BTreeSet> { + inputs.required_witnesses.bootstraps.clone() +} + +#[wasm_bindgen] +impl TxInputsBuilder { + pub fn new() -> Self { + Self { + inputs: BTreeMap::new(), + required_witnesses: InputsRequiredWitness { + vkeys: Ed25519KeyHashes::new(), + scripts: LinkedHashMap::new(), + bootstraps: BTreeSet::new(), + }, + } + } + + fn push_input(&mut self, e: (TxBuilderInput, Option)) { + self.inputs.insert(e.0.input.clone(), e); + } + + /// We have to know what kind of inputs these are to know what kind of mock witnesses to create since + /// 1) mock witnesses have different lengths depending on the type which changes the expecting fee + /// 2) Witnesses are a set so we need to get rid of duplicates to avoid over-estimating the fee + pub fn add_key_input( + &mut self, + hash: &Ed25519KeyHash, + input: &TransactionInput, + amount: &Value, + ) { + let inp = TxBuilderInput { + input: input.clone(), + amount: amount.clone(), + }; + self.push_input((inp, None)); + self.required_witnesses.vkeys.add_move(hash.clone()); + } + + fn add_script_input(&mut self, hash: &ScriptHash, input: &TransactionInput, amount: &Value) { + let inp = TxBuilderInput { + input: input.clone(), + amount: amount.clone(), + }; + self.push_input((inp, Some(hash.clone()))); + self.insert_input_with_empty_witness(hash, input); + } + + /// This method will add the input to the builder and also register the required native script witness + pub fn add_native_script_input( + &mut self, + script: &NativeScriptSource, + input: &TransactionInput, + amount: &Value, + ) { + let hash = script.script_hash(); + self.add_script_input(&hash, input, amount); + let witness = ScriptWitnessType::NativeScriptWitness(script.0.clone()); + self.insert_input_with_witness(&hash, input, &witness); + } + + /// This method will add the input to the builder and also register the required plutus witness + pub fn add_plutus_script_input( + &mut self, + witness: &PlutusWitness, + input: &TransactionInput, + amount: &Value, + ) { + let hash = witness.script.script_hash(); + + self.add_script_input(&hash, input, amount); + let witness = ScriptWitnessType::PlutusScriptWitness(witness.clone()); + self.insert_input_with_witness(&hash, input, &witness); + } + + pub fn add_bootstrap_input( + &mut self, + address: &ByronAddress, + input: &TransactionInput, + amount: &Value, + ) { + let inp = TxBuilderInput { + input: input.clone(), + amount: amount.clone(), + }; + self.push_input((inp, None)); + self.required_witnesses.bootstraps.insert(address.to_bytes()); + } + + /// Adds non script input, in case of script or reward address input it will return an error + pub fn add_regular_input( + &mut self, + address: &Address, + input: &TransactionInput, + amount: &Value, + ) -> Result<(), JsError> { + match &address.0 { + AddrType::Base(base_addr) => match &base_addr.payment.0 { + CredType::Key(key) => { + self.add_key_input(key, input, amount); + Ok(()) + } + CredType::Script(_) => Err(JsError::from_str( + &BuilderError::RegularInputIsScript.as_str(), + )), + }, + AddrType::Enterprise(ent_aaddr) => match &ent_aaddr.payment.0 { + CredType::Key(key) => { + self.add_key_input(key, input, amount); + Ok(()) + } + CredType::Script(_) => Err(JsError::from_str( + &BuilderError::RegularInputIsScript.as_str(), + )), + }, + AddrType::Ptr(ptr_addr) => match &ptr_addr.payment.0 { + CredType::Key(key) => { + self.add_key_input(key, input, amount); + Ok(()) + } + CredType::Script(_) => Err(JsError::from_str( + &BuilderError::RegularInputIsScript.as_str(), + )), + }, + AddrType::Byron(byron_addr) => { + self.add_bootstrap_input(byron_addr, input, amount); + Ok(()) + } + AddrType::Reward(_) => Err(JsError::from_str( + &BuilderError::RegularInputIsFromRewardAddress.as_str(), + )), + AddrType::Malformed(_) => { + Err(JsError::from_str(&BuilderError::MalformedAddress.as_str())) + } + } + } + + pub fn get_ref_inputs(&self) -> TransactionInputs { + let mut inputs = Vec::new(); + for wintess in self + .required_witnesses + .scripts + .iter() + .flat_map(|(_, tx_wits)| tx_wits.values()) + .filter_map(|wit| wit.as_ref()) + { + match wintess { + ScriptWitnessType::NativeScriptWitness(NativeScriptSourceEnum::RefInput( + input, _, _, _, + )) => { + inputs.push(input.clone()); + } + ScriptWitnessType::PlutusScriptWitness(plutus_witness) => { + if let Some(DatumSourceEnum::RefInput(input)) = &plutus_witness.datum { + inputs.push(input.clone()); + } + if let PlutusScriptSourceEnum::RefInput(script_ref, _) = &plutus_witness.script + { + inputs.push(script_ref.input_ref.clone()); + } + } + _ => (), + } + } + TransactionInputs::from_vec(inputs) + } + + + /// Returns a copy of the current script input witness scripts in the builder + pub fn get_native_input_scripts(&self) -> Option { + let mut scripts = NativeScripts::new(); + self.required_witnesses + .scripts + .values() + .flat_map(|v| v) + .for_each(|tx_in_with_wit| { + if let Some(ScriptWitnessType::NativeScriptWitness( + NativeScriptSourceEnum::NativeScript(s, _), + )) = tx_in_with_wit.1 + { + scripts.add(&s); + } + }); + if scripts.len() > 0 { + Some(scripts) + } else { + None + } + } + + pub(crate) fn get_used_plutus_lang_versions(&self) -> BTreeSet { + let mut used_langs = BTreeSet::new(); + for input_with_wit in self.required_witnesses.scripts.values() { + for (_, script_wit) in input_with_wit { + if let Some(ScriptWitnessType::PlutusScriptWitness(plutus_witness)) = script_wit { + used_langs.insert(plutus_witness.script.language()); + } + } + } + used_langs + } + + /// Returns a copy of the current plutus input witness scripts in the builder. + /// NOTE: each plutus witness will be cloned with a specific corresponding input index + pub fn get_plutus_input_scripts(&self) -> Option { + /* + * === EXPLANATION === + * The `Redeemer` object contains the `.index` field which is supposed to point + * exactly to the index of the corresponding input in the inputs array. We want to + * simplify and automate this as much as possible for the user to not have to care about it. + * + * For this we store the script hash along with the input, when it was registered, and + * now we create a map of script hashes to their input indexes. + * + * The registered witnesses are then each cloned with the new correct redeemer input index. + * To avoid incorrect redeemer tag we also set the `tag` field to `spend`. + */ + let tag = RedeemerTag::new_spend(); + let script_hash_index_map: BTreeMap<&TransactionInput, BigNum> = self + .inputs + .values() + .enumerate() + .fold(BTreeMap::new(), |mut m, (i, (tx_in, hash_option))| { + if hash_option.is_some() { + m.insert(&tx_in.input, (i as u64).into()); + } + m + }); + let mut scripts = PlutusWitnesses::new(); + self.required_witnesses + .scripts + .iter() + .flat_map(|x| x.1) + .for_each(|(hash, option)| { + if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = option { + if let Some(idx) = script_hash_index_map.get(&hash) { + scripts.add(&s.clone_with_redeemer_index_and_tag(&idx, &tag)); + } + } + }); + if scripts.len() > 0 { + Some(scripts) + } else { + None + } + } + + pub(crate) fn has_plutus_scripts(&self) -> bool { + self.required_witnesses.scripts.values().any(|x| { + x.iter() + .any(|(_, w)| matches!(w, Some(ScriptWitnessType::PlutusScriptWitness(_)))) + }) + } + + pub(crate) fn iter(&self) -> impl std::iter::Iterator + '_ { + self.inputs.values().map(|(i, _)| i) + } + + pub fn len(&self) -> usize { + self.inputs.len() + } + + pub fn add_required_signer(&mut self, key: &Ed25519KeyHash) { + self.required_witnesses.vkeys.add_move(key.clone()); + } + + pub fn add_required_signers(&mut self, keys: &RequiredSigners) { + self.required_witnesses.vkeys.extend(keys); + } + + pub fn total_value(&self) -> Result { + let mut res = Value::zero(); + for (inp, _) in self.inputs.values() { + res = res.checked_add(&inp.amount)?; + } + Ok(res) + } + + pub fn inputs(&self) -> TransactionInputs { + TransactionInputs::from_vec( + self.inputs + .values() + .map(|(ref tx_builder_input, _)| tx_builder_input.input.clone()) + .collect(), + ) + } + + pub fn inputs_option(&self) -> Option { + if self.len() > 0 { + Some(self.inputs()) + } else { + None + } + } + + pub(crate) fn get_script_ref_inputs_with_size( + &self, + ) -> impl Iterator { + self.required_witnesses + .scripts + .iter() + .flat_map(|(_, tx_wits)| tx_wits.iter()) + .filter_map(|(_, wit)| wit.as_ref()) + .filter_map(|wit| wit.get_script_ref_input_with_size()) + } + + #[allow(dead_code)] + pub(crate) fn get_required_signers(&self) -> Ed25519KeyHashes { + self.into() + } + + pub(crate) fn has_inputs(&self) -> bool { + !self.inputs.is_empty() + } + + pub(crate) fn has_input(&self, input: &TransactionInput) -> bool { + self.inputs.contains_key(input) + } + + fn insert_input_with_witness( + &mut self, + script_hash: &ScriptHash, + input: &TransactionInput, + witness: &ScriptWitnessType, + ) { + let script_inputs = self + .required_witnesses + .scripts + .entry(script_hash.clone()) + .or_insert(LinkedHashMap::new()); + script_inputs.insert(input.clone(), Some(witness.clone())); + } + + fn insert_input_with_empty_witness( + &mut self, + script_hash: &ScriptHash, + input: &TransactionInput, + ) { + let script_inputs = self + .required_witnesses + .scripts + .entry(script_hash.clone()) + .or_insert(LinkedHashMap::new()); + script_inputs.insert(input.clone(), None); + } +} + +impl From<&TxInputsBuilder> for Ed25519KeyHashes { + fn from(inputs: &TxInputsBuilder) -> Self { + let mut set = inputs.required_witnesses.vkeys.clone(); + inputs + .required_witnesses + .scripts + .values() + .flat_map(|tx_wits| tx_wits.values()) + .for_each(|swt: &Option| { + match swt { + Some(ScriptWitnessType::NativeScriptWitness(script_source)) => { + if let Some(signers) = script_source.required_signers() { + set.extend_move(signers); + } + } + Some(ScriptWitnessType::PlutusScriptWitness(script_source)) => { + if let Some(signers) = script_source.get_required_signers() { + set.extend_move(signers); + } + } + None => (), + } + }); + set + } +} diff --git a/rust/src/builders/voting_builder.rs b/rust/src/builders/voting_builder.rs new file mode 100644 index 00000000..b57aeb6d --- /dev/null +++ b/rust/src/builders/voting_builder.rs @@ -0,0 +1,206 @@ +use crate::*; +use std::collections::BTreeMap; + +#[derive(Clone, Debug)] +struct VoterVotes { + script_witness: Option, + votes: BTreeMap, +} + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct VotingBuilder { + votes: BTreeMap, +} + +#[wasm_bindgen] +impl VotingBuilder { + pub fn new() -> Self { + Self { + votes: BTreeMap::new(), + } + } + + pub fn add( + &mut self, + voter: &Voter, + gov_action_id: &GovernanceActionId, + voting_procedure: &VotingProcedure, + ) -> Result<(), JsError> { + if voter.has_script_credentials() { + return Err(JsError::from_str( + "Your voter has a required script witness.\ + Please use .add_with_plutus_witness or .add_with_native_script instead.", + )); + } + + let voter_votes = self.votes.entry(voter.clone()).or_insert(VoterVotes { + script_witness: None, + votes: BTreeMap::new(), + }); + + voter_votes + .votes + .insert(gov_action_id.clone(), voting_procedure.clone()); + + Ok(()) + } + + pub fn add_with_plutus_witness( + &mut self, + voter: &Voter, + gov_action_id: &GovernanceActionId, + voting_procedure: &VotingProcedure, + witness: &PlutusWitness, + ) -> Result<(), JsError> { + if !voter.has_script_credentials() { + return Err(JsError::from_str( + "Your voter does not have a required script witness.\ + Please use .add instead.", + )); + } + + let voter_votes = self.votes.entry(voter.clone()).or_insert(VoterVotes { + script_witness: Some(ScriptWitnessType::PlutusScriptWitness(witness.clone())), + votes: BTreeMap::new(), + }); + + voter_votes + .votes + .insert(gov_action_id.clone(), voting_procedure.clone()); + + Ok(()) + } + + pub fn add_with_native_script( + &mut self, + voter: &Voter, + gov_action_id: &GovernanceActionId, + voting_procedure: &VotingProcedure, + native_script_source: &NativeScriptSource, + ) -> Result<(), JsError> { + if !voter.has_script_credentials() { + return Err(JsError::from_str( + "Your voter does not have a required script witness.\ + Please use .add instead.", + )); + } + + let voter_votes = self.votes.entry(voter.clone()).or_insert(VoterVotes { + script_witness: Some(ScriptWitnessType::NativeScriptWitness( + native_script_source.0.clone(), + )), + votes: BTreeMap::new(), + }); + + voter_votes + .votes + .insert(gov_action_id.clone(), voting_procedure.clone()); + + Ok(()) + } + + pub(crate) fn get_required_signers(&self) -> Ed25519KeyHashes { + let mut set = Ed25519KeyHashes::new(); + for (voter, voter_votes) in &self.votes { + let req_signature = voter.to_key_hash(); + if let Some(req_signature) = req_signature { + set.add_move(req_signature); + } + + if let Some(ScriptWitnessType::NativeScriptWitness(script_source)) = + &voter_votes.script_witness + { + if let Some(required_signers) = script_source.required_signers() { + set.extend_move(required_signers); + } + } + } + set + } + + pub fn get_plutus_witnesses(&self) -> PlutusWitnesses { + let tag = RedeemerTag::new_vote(); + let mut scripts = PlutusWitnesses::new(); + for (i, (_, voter_votes)) in self.votes.iter().enumerate() { + if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = &voter_votes.script_witness { + let index = BigNum::from(i); + scripts.add(&s.clone_with_redeemer_index_and_tag(&index, &tag)); + } + } + scripts + } + + pub fn get_ref_inputs(&self) -> TransactionInputs { + let mut inputs = Vec::new(); + for (_, voter_votes) in &self.votes { + match &voter_votes.script_witness { + Some(script_witness) => { + if let Some(input) = script_witness.get_script_ref_input() { + inputs.push(input); + } + if let Some(input) = script_witness.get_datum_ref_input() { + inputs.push(input); + } + } + None => {} + } + } + TransactionInputs::from_vec(inputs) + } + + pub fn get_native_scripts(&self) -> NativeScripts { + let mut scripts = NativeScripts::new(); + for (_, voter_votes) in &self.votes { + if let Some(ScriptWitnessType::NativeScriptWitness( + NativeScriptSourceEnum::NativeScript(script, _), + )) = &voter_votes.script_witness + { + scripts.add(script); + } + } + scripts + } + + pub(crate) fn get_used_plutus_lang_versions(&self) -> BTreeSet { + let mut used_langs = BTreeSet::new(); + for (_, voter_votes) in &self.votes { + if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = &voter_votes.script_witness { + used_langs.insert(s.script.language()); + } + } + used_langs + } + + //return only ref inputs that are script refs with added size + //used for calculating the fee for the transaction + //another ref input and also script ref input without size are filtered out + pub(crate) fn get_script_ref_inputs_with_size( + &self, + ) -> impl Iterator { + self.votes.iter() + .filter_map(|(_, voter_votes)| voter_votes.script_witness.as_ref()) + .filter_map(|script_witness| script_witness.get_script_ref_input_with_size()) + } + + pub fn has_plutus_scripts(&self) -> bool { + for (_, voter_votes) in &self.votes { + if let Some(ScriptWitnessType::PlutusScriptWitness(_)) = voter_votes.script_witness { + return true; + } + } + false + } + + pub fn build(&self) -> VotingProcedures { + let mut voters = BTreeMap::new(); + for (voter, voter_votes) in &self.votes { + let mut votes = BTreeMap::new(); + for (action, voting_procedure) in &voter_votes.votes { + votes.insert(action.clone(), voting_procedure.clone()); + } + voters.insert(voter.clone(), votes); + } + VotingProcedures(voters) + } +} diff --git a/rust/src/builders/voting_proposal_builder.rs b/rust/src/builders/voting_proposal_builder.rs new file mode 100644 index 00000000..d8434b99 --- /dev/null +++ b/rust/src/builders/voting_proposal_builder.rs @@ -0,0 +1,117 @@ +use crate::*; +use std::collections::BTreeMap; + +#[wasm_bindgen] +#[derive(Clone, Debug)] +pub struct VotingProposalBuilder { + proposals: BTreeMap>, +} + +#[wasm_bindgen] +impl VotingProposalBuilder { + pub fn new() -> Self { + Self { + proposals: BTreeMap::new(), + } + } + + pub fn add(&mut self, proposal: &VotingProposal) -> Result<(), JsError> { + if proposal.has_script_hash() { + return Err(JsError::from_str("Proposal has a script hash. Use add_with_plutus_witness instead.")); + } + self.proposals.insert(proposal.clone(), None); + Ok(()) + } + + pub fn add_with_plutus_witness( + &mut self, + proposal: &VotingProposal, + witness: &PlutusWitness, + ) -> Result<(), JsError> { + self.proposals.insert( + proposal.clone(), + Some(ScriptWitnessType::PlutusScriptWitness(witness.clone())), + ); + Ok(()) + } + + pub fn get_plutus_witnesses(&self) -> PlutusWitnesses { + let tag = RedeemerTag::new_voting_proposal(); + let mut scripts = PlutusWitnesses::new(); + for (i, (_, script_wit)) in self.proposals.iter().enumerate() { + if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = script_wit { + let index = BigNum::from(i); + scripts.add(&s.clone_with_redeemer_index_and_tag(&index, &tag)); + } + } + scripts + } + + pub fn get_ref_inputs(&self) -> TransactionInputs { + let mut inputs = Vec::new(); + for (_, script_wit) in &self.proposals { + match script_wit { + Some(script_witness) => { + if let Some(input) = script_witness.get_script_ref_input() { + inputs.push(input); + } + if let Some(input) = script_witness.get_datum_ref_input() { + inputs.push(input); + } + } + None => {} + } + } + TransactionInputs::from_vec(inputs) + } + + pub(crate) fn get_total_deposit(&self) -> Result { + self.proposals.iter().fold( + Ok(Coin::zero()), + |acc: Result, (proposal, _)| { + acc.and_then(|acc| { + acc.checked_add(&proposal.deposit) + .or_else(|_| Err(JsError::from_str("Overflow when calculating total deposit"))) + }) + }, + ) + } + + pub(crate) fn get_used_plutus_lang_versions(&self) -> BTreeSet { + let mut used_langs = BTreeSet::new(); + for (_, script_wit) in &self.proposals { + if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = script_wit { + used_langs.insert(s.script.language()); + } + } + used_langs + } + + //return only ref inputs that are script refs with added size + //used for calculating the fee for the transaction + //another ref input and also script ref input without size are filtered out + pub(crate) fn get_script_ref_inputs_with_size( + &self, + ) -> impl Iterator { + self.proposals.iter() + .filter_map(|(_, script_wit)| script_wit.as_ref()) + .filter_map(|script_wit| script_wit.get_script_ref_input_with_size()) + } + + pub fn has_plutus_scripts(&self) -> bool { + for (_, script_wit) in &self.proposals { + if let Some(ScriptWitnessType::PlutusScriptWitness(_)) = script_wit { + return true; + } + } + false + } + + pub fn build(&self) -> VotingProposals { + let mut proposals = Vec::new(); + for (voter, _) in &self.proposals { + proposals.push(voter.clone()); + } + VotingProposals::from_vec(proposals) + } +} diff --git a/rust/src/tx_builder/withdrawals_builder.rs b/rust/src/builders/withdrawals_builder.rs similarity index 77% rename from rust/src/tx_builder/withdrawals_builder.rs rename to rust/src/builders/withdrawals_builder.rs index 731d9c26..809468b5 100644 --- a/rust/src/tx_builder/withdrawals_builder.rs +++ b/rust/src/builders/withdrawals_builder.rs @@ -1,6 +1,5 @@ -use crate::tx_builder::script_structs::*; use crate::*; -use linked_hash_map::LinkedHashMap; +use hashlink::LinkedHashMap; #[wasm_bindgen] #[derive(Clone, Debug)] @@ -78,16 +77,18 @@ impl WithdrawalsBuilder { Ok(()) } - pub(crate) fn get_required_signers(&self) -> RequiredSignersSet { - let mut set = RequiredSignersSet::new(); + pub(crate) fn get_required_signers(&self) -> Ed25519KeyHashes { + let mut set = Ed25519KeyHashes::new(); for (address, (_, script_wit)) in &self.withdrawals { let req_signature = address.payment_cred().to_keyhash(); if let Some(req_signature) = req_signature { - set.insert(req_signature); + set.add_move(req_signature); } - if let Some(ScriptWitnessType::NativeScriptWitness(script_source)) = script_wit { - set.extend(script_source.required_signers()); + if let Some(script_wit) = script_wit { + if let Some(script_wit) = script_wit.get_required_signers() { + set.extend_move(script_wit); + } } } set @@ -109,30 +110,25 @@ impl WithdrawalsBuilder { let mut inputs = Vec::new(); for (_, (_, script_wit)) in self.withdrawals.iter() { match script_wit { - Some(ScriptWitnessType::NativeScriptWitness(script_source)) => { - if let NativeScriptSourceEnum::RefInput(input, _, _) = script_source { - inputs.push(input.clone()); - } - } - Some(ScriptWitnessType::PlutusScriptWitness(plutus_witness)) => { - if let Some(DatumSourceEnum::RefInput(input)) = &plutus_witness.datum { - inputs.push(input.clone()); + Some(script_witness) => { + if let Some(input) = script_witness.get_script_ref_input() { + inputs.push(input); } - if let PlutusScriptSourceEnum::RefInput(input, _, _) = &plutus_witness.script { - inputs.push(input.clone()); + if let Some(input) = script_witness.get_datum_ref_input() { + inputs.push(input); } } None => {} } } - TransactionInputs(inputs) + TransactionInputs::from_vec(inputs) } pub fn get_native_scripts(&self) -> NativeScripts { let mut scripts = NativeScripts::new(); for (_, (_, script_wit)) in self.withdrawals.iter() { if let Some(ScriptWitnessType::NativeScriptWitness( - NativeScriptSourceEnum::NativeScript(script), + NativeScriptSourceEnum::NativeScript(script, _), )) = script_wit { scripts.add(script); @@ -145,9 +141,7 @@ impl WithdrawalsBuilder { let mut used_langs = BTreeSet::new(); for (_, (_, script_wit)) in &self.withdrawals { if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = script_wit { - if let Some(lang) = s.script.language() { - used_langs.insert(lang.clone()); - } + used_langs.insert(s.script.language()); } } used_langs @@ -170,6 +164,17 @@ impl WithdrawalsBuilder { false } + //return only ref inputs that are script refs with added size + //used for calculating the fee for the transaction + //another ref input and also script ref input without size are filtered out + pub(crate) fn get_script_ref_inputs_with_size( + &self, + ) -> impl Iterator { + self.withdrawals.iter() + .filter_map(|(_, (_, script_wit))| script_wit.as_ref()) + .filter_map(|script_wit| script_wit.get_script_ref_input_with_size()) + } + pub fn build(&self) -> Withdrawals { let map = self .withdrawals diff --git a/rust/src/chain_crypto/algorithms/ed25519.rs b/rust/src/chain_crypto/algorithms/ed25519.rs index 36a53143..7d7c0334 100644 --- a/rust/src/chain_crypto/algorithms/ed25519.rs +++ b/rust/src/chain_crypto/algorithms/ed25519.rs @@ -10,7 +10,7 @@ use rand_os::rand_core::{CryptoRng, RngCore}; use ed25519_bip32::XPub; /// ED25519 Signing Algorithm -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub struct Ed25519; #[derive(Clone)] @@ -19,7 +19,7 @@ pub struct Priv([u8; ed25519::PRIVATE_KEY_LENGTH]); #[derive(Clone, PartialEq, Eq, Hash)] pub struct Pub(pub(crate) [u8; ed25519::PUBLIC_KEY_LENGTH]); -#[derive(Clone)] +#[derive(Clone, Hash)] pub struct Sig(pub(crate) [u8; ed25519::SIGNATURE_LENGTH]); impl Pub { diff --git a/rust/src/chain_crypto/sign.rs b/rust/src/chain_crypto/sign.rs index cadbbd1c..2df4078b 100644 --- a/rust/src/chain_crypto/sign.rs +++ b/rust/src/chain_crypto/sign.rs @@ -53,6 +53,7 @@ where fn sign(key: &Self::Secret, msg: &[u8]) -> ::Signature; } +#[derive(Hash)] pub struct Signature { signdata: A::Signature, phantom: PhantomData, diff --git a/rust/src/crypto.rs b/rust/src/crypto.rs index 38ba0254..2be98210 100644 --- a/rust/src/crypto.rs +++ b/rust/src/crypto.rs @@ -1,19 +1,5 @@ -use crate::chain_crypto as crypto; -use crate::impl_mockchain as chain; -use bech32::ToBase32; -use cbor_event::{de::Deserializer, se::Serializer}; -use chain::key; -use crypto::bech32::Bech32 as _; -use rand_os::OsRng; -use std::io::{BufRead, Seek, Write}; -use std::str::FromStr; -use std::fmt::Display; -use std::fmt; - use cryptoxide::blake2b::Blake2b; -use super::*; - pub(crate) fn blake2b224(data: &[u8]) -> [u8; 28] { let mut out = [0; 28]; Blake2b::blake2b(&mut out, data, &[]); @@ -27,1471 +13,4 @@ pub(crate) fn blake2b256(data: &[u8]) -> [u8; 32] { } // All key structs were taken from js-chain-libs: -// https://github.com/Emurgo/js-chain-libs - -#[wasm_bindgen] -pub struct Bip32PrivateKey(crypto::SecretKey); - -#[wasm_bindgen] -impl Bip32PrivateKey { - /// derive this private key with the given index. - /// - /// # Security considerations - /// - /// * hard derivation index cannot be soft derived with the public key - /// - /// # Hard derivation vs Soft derivation - /// - /// If you pass an index below 0x80000000 then it is a soft derivation. - /// The advantage of soft derivation is that it is possible to derive the - /// public key too. I.e. derivation the private key with a soft derivation - /// index and then retrieving the associated public key is equivalent to - /// deriving the public key associated to the parent private key. - /// - /// Hard derivation index does not allow public key derivation. - /// - /// This is why deriving the private key should not fail while deriving - /// the public key may fail (if the derivation index is invalid). - /// - pub fn derive(&self, index: u32) -> Bip32PrivateKey { - Bip32PrivateKey(crypto::derive::derive_sk_ed25519(&self.0, index)) - } - - /// 128-byte xprv a key format in Cardano that some software still uses or requires - /// the traditional 96-byte xprv is simply encoded as - /// prv | chaincode - /// however, because some software may not know how to compute a public key from a private key, - /// the 128-byte inlines the public key in the following format - /// prv | pub | chaincode - /// so be careful if you see the term "xprv" as it could refer to either one - /// our library does not require the pub (instead we compute the pub key when needed) - pub fn from_128_xprv(bytes: &[u8]) -> Result { - let mut buf = [0; 96]; - buf[0..64].clone_from_slice(&bytes[0..64]); - buf[64..96].clone_from_slice(&bytes[96..128]); - - Bip32PrivateKey::from_bytes(&buf) - } - /// see from_128_xprv - pub fn to_128_xprv(&self) -> Vec { - let prv_key = self.to_raw_key().as_bytes(); - let pub_key = self.to_public().to_raw_key().as_bytes(); - let cc = self.chaincode(); - - let mut buf = [0; 128]; - buf[0..64].clone_from_slice(&prv_key); - buf[64..96].clone_from_slice(&pub_key); - buf[96..128].clone_from_slice(&cc); - buf.to_vec() - } - - pub fn generate_ed25519_bip32() -> Result { - OsRng::new() - .map(crypto::SecretKey::::generate) - .map(Bip32PrivateKey) - .map_err(|e| JsError::from_str(&format!("{}", e))) - } - - pub fn to_raw_key(&self) -> PrivateKey { - PrivateKey(key::EitherEd25519SecretKey::Extended( - crypto::derive::to_raw_sk(&self.0), - )) - } - - pub fn to_public(&self) -> Bip32PublicKey { - Bip32PublicKey(self.0.to_public().into()) - } - - pub fn from_bytes(bytes: &[u8]) -> Result { - crypto::SecretKey::::from_binary(bytes) - .map_err(|e| JsError::from_str(&format!("{}", e))) - .map(Bip32PrivateKey) - } - - pub fn as_bytes(&self) -> Vec { - self.0.as_ref().to_vec() - } - - pub fn from_bech32(bech32_str: &str) -> Result { - crypto::SecretKey::try_from_bech32_str(&bech32_str) - .map(Bip32PrivateKey) - .map_err(|_| JsError::from_str("Invalid secret key")) - } - - pub fn to_bech32(&self) -> String { - self.0.to_bech32_str() - } - - pub fn from_bip39_entropy(entropy: &[u8], password: &[u8]) -> Bip32PrivateKey { - Bip32PrivateKey(crypto::derive::from_bip39_entropy(&entropy, &password)) - } - - pub fn chaincode(&self) -> Vec { - const ED25519_PRIVATE_KEY_LENGTH: usize = 64; - const XPRV_SIZE: usize = 96; - self.0.as_ref()[ED25519_PRIVATE_KEY_LENGTH..XPRV_SIZE].to_vec() - } - - pub fn to_hex(&self) -> String { - hex::encode(self.as_bytes()) - } - - pub fn from_hex(hex_str: &str) -> Result { - match hex::decode(hex_str) { - Ok(data) => Ok(Self::from_bytes(data.as_ref())?), - Err(e) => Err(JsError::from_str(&e.to_string())), - } - } -} - -#[wasm_bindgen] -pub struct Bip32PublicKey(crypto::PublicKey); - -#[wasm_bindgen] -impl Bip32PublicKey { - /// derive this public key with the given index. - /// - /// # Errors - /// - /// If the index is not a soft derivation index (< 0x80000000) then - /// calling this method will fail. - /// - /// # Security considerations - /// - /// * hard derivation index cannot be soft derived with the public key - /// - /// # Hard derivation vs Soft derivation - /// - /// If you pass an index below 0x80000000 then it is a soft derivation. - /// The advantage of soft derivation is that it is possible to derive the - /// public key too. I.e. derivation the private key with a soft derivation - /// index and then retrieving the associated public key is equivalent to - /// deriving the public key associated to the parent private key. - /// - /// Hard derivation index does not allow public key derivation. - /// - /// This is why deriving the private key should not fail while deriving - /// the public key may fail (if the derivation index is invalid). - /// - pub fn derive(&self, index: u32) -> Result { - crypto::derive::derive_pk_ed25519(&self.0, index) - .map(Bip32PublicKey) - .map_err(|e| JsError::from_str(&format! {"{:?}", e})) - } - - pub fn to_raw_key(&self) -> PublicKey { - PublicKey(crypto::derive::to_raw_pk(&self.0)) - } - - pub fn from_bytes(bytes: &[u8]) -> Result { - crypto::PublicKey::::from_binary(bytes) - .map_err(|e| JsError::from_str(&format!("{}", e))) - .map(Bip32PublicKey) - } - - pub fn as_bytes(&self) -> Vec { - self.0.as_ref().to_vec() - } - - pub fn from_bech32(bech32_str: &str) -> Result { - crypto::PublicKey::try_from_bech32_str(&bech32_str) - .map(Bip32PublicKey) - .map_err(|e| JsError::from_str(&format!("{}", e))) - } - - pub fn to_bech32(&self) -> String { - self.0.to_bech32_str() - } - - pub fn chaincode(&self) -> Vec { - const ED25519_PUBLIC_KEY_LENGTH: usize = 32; - const XPUB_SIZE: usize = 64; - self.0.as_ref()[ED25519_PUBLIC_KEY_LENGTH..XPUB_SIZE].to_vec() - } - - pub fn to_hex(&self) -> String { - hex::encode(self.as_bytes()) - } - - pub fn from_hex(hex_str: &str) -> Result { - match hex::decode(hex_str) { - Ok(data) => Ok(Self::from_bytes(data.as_ref())?), - Err(e) => Err(JsError::from_str(&e.to_string())), - } - } -} - -#[wasm_bindgen] -pub struct PrivateKey(key::EitherEd25519SecretKey); - -impl From for PrivateKey { - fn from(secret_key: key::EitherEd25519SecretKey) -> PrivateKey { - PrivateKey(secret_key) - } -} - -#[wasm_bindgen] -impl PrivateKey { - pub fn to_public(&self) -> PublicKey { - self.0.to_public().into() - } - - pub fn generate_ed25519() -> Result { - OsRng::new() - .map(crypto::SecretKey::::generate) - .map(key::EitherEd25519SecretKey::Normal) - .map(PrivateKey) - .map_err(|e| JsError::from_str(&format!("{}", e))) - } - - pub fn generate_ed25519extended() -> Result { - OsRng::new() - .map(crypto::SecretKey::::generate) - .map(key::EitherEd25519SecretKey::Extended) - .map(PrivateKey) - .map_err(|e| JsError::from_str(&format!("{}", e))) - } - - /// Get private key from its bech32 representation - /// ```javascript - /// PrivateKey.from_bech32('ed25519_sk1ahfetf02qwwg4dkq7mgp4a25lx5vh9920cr5wnxmpzz9906qvm8qwvlts0'); - /// ``` - /// For an extended 25519 key - /// ```javascript - /// PrivateKey.from_bech32('ed25519e_sk1gqwl4szuwwh6d0yk3nsqcc6xxc3fpvjlevgwvt60df59v8zd8f8prazt8ln3lmz096ux3xvhhvm3ca9wj2yctdh3pnw0szrma07rt5gl748fp'); - /// ``` - pub fn from_bech32(bech32_str: &str) -> Result { - crypto::SecretKey::try_from_bech32_str(&bech32_str) - .map(key::EitherEd25519SecretKey::Extended) - .or_else(|_| { - crypto::SecretKey::try_from_bech32_str(&bech32_str) - .map(key::EitherEd25519SecretKey::Normal) - }) - .map(PrivateKey) - .map_err(|_| JsError::from_str("Invalid secret key")) - } - - pub fn to_bech32(&self) -> String { - match self.0 { - key::EitherEd25519SecretKey::Normal(ref secret) => secret.to_bech32_str(), - key::EitherEd25519SecretKey::Extended(ref secret) => secret.to_bech32_str(), - } - } - - pub fn as_bytes(&self) -> Vec { - match self.0 { - key::EitherEd25519SecretKey::Normal(ref secret) => secret.as_ref().to_vec(), - key::EitherEd25519SecretKey::Extended(ref secret) => secret.as_ref().to_vec(), - } - } - - pub fn from_extended_bytes(bytes: &[u8]) -> Result { - crypto::SecretKey::from_binary(bytes) - .map(key::EitherEd25519SecretKey::Extended) - .map(PrivateKey) - .map_err(|_| JsError::from_str("Invalid extended secret key")) - } - - pub fn from_normal_bytes(bytes: &[u8]) -> Result { - crypto::SecretKey::from_binary(bytes) - .map(key::EitherEd25519SecretKey::Normal) - .map(PrivateKey) - .map_err(|_| JsError::from_str("Invalid normal secret key")) - } - - pub fn sign(&self, message: &[u8]) -> Ed25519Signature { - Ed25519Signature(self.0.sign(&message.to_vec())) - } - - pub fn to_hex(&self) -> String { - hex::encode(self.as_bytes()) - } - - pub fn from_hex(hex_str: &str) -> Result { - let data: Vec = match hex::decode(hex_str) { - Ok(d) => d, - Err(e) => return Err(JsError::from_str(&e.to_string())), - }; - let data_slice: &[u8] = data.as_slice(); - crypto::SecretKey::from_binary(data_slice) - .map(key::EitherEd25519SecretKey::Normal) - .or_else(|_| { - crypto::SecretKey::from_binary(data_slice) - .map(key::EitherEd25519SecretKey::Extended) - }) - .map(PrivateKey) - .map_err(|_| JsError::from_str("Invalid secret key")) - } -} - -/// ED25519 key used as public key -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct PublicKey(crypto::PublicKey); - -impl From> for PublicKey { - fn from(key: crypto::PublicKey) -> PublicKey { - PublicKey(key) - } -} - -#[wasm_bindgen] -impl PublicKey { - /// Get public key from its bech32 representation - /// Example: - /// ```javascript - /// const pkey = PublicKey.from_bech32('ed25519_pk1dgaagyh470y66p899txcl3r0jaeaxu6yd7z2dxyk55qcycdml8gszkxze2'); - /// ``` - pub fn from_bech32(bech32_str: &str) -> Result { - crypto::PublicKey::try_from_bech32_str(&bech32_str) - .map(PublicKey) - .map_err(|_| JsError::from_str("Malformed public key")) - } - - pub fn to_bech32(&self) -> String { - self.0.to_bech32_str() - } - - pub fn as_bytes(&self) -> Vec { - self.0.as_ref().to_vec() - } - - pub fn from_bytes(bytes: &[u8]) -> Result { - crypto::PublicKey::from_binary(bytes) - .map_err(|e| JsError::from_str(&format!("{}", e))) - .map(PublicKey) - } - - pub fn verify(&self, data: &[u8], signature: &Ed25519Signature) -> bool { - signature.0.verify_slice(&self.0, data) == crypto::Verification::Success - } - - pub fn hash(&self) -> Ed25519KeyHash { - Ed25519KeyHash::from(blake2b224(self.as_bytes().as_ref())) - } - - pub fn to_hex(&self) -> String { - hex::encode(self.as_bytes()) - } - - pub fn from_hex(hex_str: &str) -> Result { - match hex::decode(hex_str) { - Ok(data) => Ok(Self::from_bytes(data.as_ref())?), - Err(e) => Err(JsError::from_str(&e.to_string())), - } - } -} - -impl serde::Serialize for PublicKey { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_bech32()) - } -} - -impl<'de> serde::de::Deserialize<'de> for PublicKey { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let s = ::deserialize(deserializer)?; - PublicKey::from_bech32(&s).map_err(|_e| { - serde::de::Error::invalid_value( - serde::de::Unexpected::Str(&s), - &"bech32 public key string", - ) - }) - } -} - -impl JsonSchema for PublicKey { - fn schema_name() -> String { - String::from("PublicKey") - } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - String::json_schema(gen) - } - fn is_referenceable() -> bool { - String::is_referenceable() - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)] -pub struct Vkey(PublicKey); - -impl_to_from!(Vkey); - -#[wasm_bindgen] -impl Vkey { - pub fn new(pk: &PublicKey) -> Self { - Self(pk.clone()) - } - - pub fn public_key(&self) -> PublicKey { - self.0.clone() - } -} - -impl cbor_event::se::Serialize for Vkey { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_bytes(&self.0.as_bytes()) - } -} - -impl Deserialize for Vkey { - fn deserialize(raw: &mut Deserializer) -> Result { - Ok(Self(PublicKey(crypto::PublicKey::from_binary( - raw.bytes()?.as_ref(), - )?))) - } -} - -#[wasm_bindgen] -#[derive(Clone)] -pub struct Vkeys(Vec); - -#[wasm_bindgen] -impl Vkeys { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> Vkey { - self.0[index].clone() - } - - pub fn add(&mut self, elem: &Vkey) { - self.0.push(elem.clone()); - } -} - -impl cbor_event::se::Serialize for Vkeys { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for Vkeys { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(Vkey::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("Vkeys"))?; - Ok(Self(arr)) - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)] -pub struct Vkeywitness { - vkey: Vkey, - signature: Ed25519Signature, -} - -impl_to_from!(Vkeywitness); - -#[wasm_bindgen] -impl Vkeywitness { - pub fn new(vkey: &Vkey, signature: &Ed25519Signature) -> Self { - Self { - vkey: vkey.clone(), - signature: signature.clone(), - } - } - - pub fn vkey(&self) -> Vkey { - self.vkey.clone() - } - - pub fn signature(&self) -> Ed25519Signature { - self.signature.clone() - } -} - -impl cbor_event::se::Serialize for Vkeywitness { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.vkey.serialize(serializer)?; - self.signature.serialize(serializer) - } -} - -impl Deserialize for Vkeywitness { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let vkey = (|| -> Result<_, DeserializeError> { Ok(Vkey::deserialize(raw)?) })() - .map_err(|e| e.annotate("vkey"))?; - let signature = - (|| -> Result<_, DeserializeError> { Ok(Ed25519Signature::deserialize(raw)?) })() - .map_err(|e| e.annotate("signature"))?; - let ret = Ok(Vkeywitness::new(&vkey, &signature)); - match len { - cbor_event::Len::Len(n) => match n { - 2 => (), - _ => { - return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( - 2, len, "", - )) - .into()) - } - }, - cbor_event::Len::Indefinite => match raw.special()? { - cbor_event::Special::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("Vkeywitness")) - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)] -pub struct Vkeywitnesses(pub(crate) Vec); - -impl_to_from!(Vkeywitnesses); - -#[wasm_bindgen] -impl Vkeywitnesses { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> Vkeywitness { - self.0[index].clone() - } - - pub fn add(&mut self, elem: &Vkeywitness) { - self.0.push(elem.clone()); - } -} - -impl cbor_event::se::Serialize for Vkeywitnesses { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for Vkeywitnesses { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == cbor_event::Type::Special { - assert_eq!(raw.special()?, cbor_event::Special::Break); - break; - } - arr.push(Vkeywitness::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("Vkeywitnesses"))?; - Ok(Self(arr)) - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)] -pub struct BootstrapWitness { - vkey: Vkey, - signature: Ed25519Signature, - chain_code: Vec, - attributes: Vec, -} - -impl_to_from!(BootstrapWitness); - -#[wasm_bindgen] -impl BootstrapWitness { - pub fn vkey(&self) -> Vkey { - self.vkey.clone() - } - - pub fn signature(&self) -> Ed25519Signature { - self.signature.clone() - } - - pub fn chain_code(&self) -> Vec { - self.chain_code.clone() - } - - pub fn attributes(&self) -> Vec { - self.attributes.clone() - } - - pub fn new( - vkey: &Vkey, - signature: &Ed25519Signature, - chain_code: Vec, - attributes: Vec, - ) -> Self { - Self { - vkey: vkey.clone(), - signature: signature.clone(), - chain_code: chain_code, - attributes: attributes, - } - } -} - -impl cbor_event::se::Serialize for BootstrapWitness { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(4))?; - self.vkey.serialize(serializer)?; - self.signature.serialize(serializer)?; - serializer.write_bytes(&self.chain_code)?; - serializer.write_bytes(&self.attributes)?; - Ok(serializer) - } -} - -impl Deserialize for BootstrapWitness { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("BootstrapWitness")) - } -} - -impl DeserializeEmbeddedGroup for BootstrapWitness { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - let vkey = (|| -> Result<_, DeserializeError> { Ok(Vkey::deserialize(raw)?) })() - .map_err(|e| e.annotate("vkey"))?; - let signature = - (|| -> Result<_, DeserializeError> { Ok(Ed25519Signature::deserialize(raw)?) })() - .map_err(|e| e.annotate("signature"))?; - let chain_code = (|| -> Result<_, DeserializeError> { Ok(raw.bytes()?) })() - .map_err(|e| e.annotate("chain_code"))?; - let attributes = (|| -> Result<_, DeserializeError> { Ok(raw.bytes()?) })() - .map_err(|e| e.annotate("attributes"))?; - Ok(BootstrapWitness { - vkey, - signature, - chain_code, - attributes, - }) - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)] -pub struct BootstrapWitnesses(Vec); - -#[wasm_bindgen] -impl BootstrapWitnesses { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> BootstrapWitness { - self.0[index].clone() - } - - pub fn add(&mut self, elem: &BootstrapWitness) { - self.0.push(elem.clone()); - } -} - -impl cbor_event::se::Serialize for BootstrapWitnesses { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for BootstrapWitnesses { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == cbor_event::Type::Special { - assert_eq!(raw.special()?, cbor_event::Special::Break); - break; - } - arr.push(BootstrapWitness::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("BootstrapWitnesses"))?; - Ok(Self(arr)) - } -} - -#[wasm_bindgen] -pub struct PublicKeys(Vec); - -#[wasm_bindgen] -impl PublicKeys { - #[wasm_bindgen(constructor)] - pub fn new() -> PublicKeys { - PublicKeys(vec![]) - } - - pub fn size(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> PublicKey { - self.0[index].clone() - } - - pub fn add(&mut self, key: &PublicKey) { - self.0.push(key.clone()); - } -} - -macro_rules! impl_signature { - ($name:ident, $signee_type:ty, $verifier_type:ty) => { - #[wasm_bindgen] - #[derive(Clone, Debug, Eq, PartialEq)] - pub struct $name(crypto::Signature<$signee_type, $verifier_type>); - - #[wasm_bindgen] - impl $name { - pub fn to_bytes(&self) -> Vec { - self.0.as_ref().to_vec() - } - - pub fn to_bech32(&self) -> String { - self.0.to_bech32_str() - } - - pub fn to_hex(&self) -> String { - hex::encode(&self.0.as_ref()) - } - - pub fn from_bech32(bech32_str: &str) -> Result<$name, JsError> { - crypto::Signature::try_from_bech32_str(&bech32_str) - .map($name) - .map_err(|e| JsError::from_str(&format!("{}", e))) - } - - pub fn from_hex(input: &str) -> Result<$name, JsError> { - crypto::Signature::from_str(input) - .map_err(|e| JsError::from_str(&format!("{:?}", e))) - .map($name) - } - } - - from_bytes!($name, bytes, { - crypto::Signature::from_binary(bytes.as_ref()) - .map_err(|e| { - DeserializeError::new(stringify!($name), DeserializeFailure::SignatureError(e)) - }) - .map($name) - }); - - impl cbor_event::se::Serialize for $name { - fn serialize<'se, W: std::io::Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_bytes(self.0.as_ref()) - } - } - - impl Deserialize for $name { - fn deserialize( - raw: &mut Deserializer, - ) -> Result { - Ok(Self(crypto::Signature::from_binary(raw.bytes()?.as_ref())?)) - } - } - - impl serde::Serialize for $name { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_hex()) - } - } - - impl<'de> serde::de::Deserialize<'de> for $name { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let s = ::deserialize(deserializer)?; - $name::from_hex(&s).map_err(|_e| { - serde::de::Error::invalid_value( - serde::de::Unexpected::Str(&s), - &"hex bytes for signature", - ) - }) - } - } - - impl JsonSchema for $name { - fn schema_name() -> String { - String::from(stringify!($name)) - } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - String::json_schema(gen) - } - fn is_referenceable() -> bool { - String::is_referenceable() - } - } - }; -} - -impl_signature!(Ed25519Signature, Vec, crypto::Ed25519); -macro_rules! impl_hash_type { - ($name:ident, $byte_count:expr) => { - #[wasm_bindgen] - #[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] - pub struct $name(pub(crate) [u8; $byte_count]); - - // hash types are the only types in this library to not expect the entire CBOR structure. - // There is no CBOR binary tag here just the raw hash bytes. - from_bytes!($name, bytes, { - use std::convert::TryInto; - match bytes.len() { - $byte_count => Ok($name(bytes[..$byte_count].try_into().unwrap())), - other_len => { - let cbor_error = cbor_event::Error::WrongLen( - $byte_count, - cbor_event::Len::Len(other_len as u64), - "hash length", - ); - Err(DeserializeError::new( - stringify!($name), - DeserializeFailure::CBOR(cbor_error), - )) - } - } - }); - - #[wasm_bindgen] - impl $name { - // hash types are the only types in this library to not give the entire CBOR structure. - // There is no CBOR binary tag here just the raw hash bytes. - pub fn to_bytes(&self) -> Vec { - self.0.to_vec() - } - - pub fn to_bech32(&self, prefix: &str) -> Result { - bech32::encode(&prefix, self.to_bytes().to_base32()) - .map_err(|e| JsError::from_str(&format! {"{:?}", e})) - } - - pub fn from_bech32(bech_str: &str) -> Result<$name, JsError> { - let (_hrp, u5data) = - bech32::decode(bech_str).map_err(|e| JsError::from_str(&e.to_string()))?; - let data: Vec = bech32::FromBase32::from_base32(&u5data).unwrap(); - Ok(Self::from_bytes(data)?) - } - - pub fn to_hex(&self) -> String { - hex::encode(&self.0) - } - - pub fn from_hex(hex: &str) -> Result<$name, JsError> { - let bytes = hex::decode(hex) - .map_err(|e| JsError::from_str(&format!("hex decode failed: {}", e)))?; - Self::from_bytes(bytes).map_err(|e| JsError::from_str(&format!("{:?}", e))) - } - } - - // associated consts are not supported in wasm_bindgen - impl $name { - pub const BYTE_COUNT: usize = $byte_count; - } - - // can't expose [T; N] to wasm for new() but it's useful internally so we implement From trait - impl From<[u8; $byte_count]> for $name { - fn from(bytes: [u8; $byte_count]) -> Self { - Self(bytes) - } - } - - impl cbor_event::se::Serialize for $name { - fn serialize<'se, W: std::io::Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_bytes(self.0) - } - } - - impl Deserialize for $name { - fn deserialize( - raw: &mut Deserializer, - ) -> Result { - use std::convert::TryInto; - (|| -> Result { - let bytes = raw.bytes()?; - if bytes.len() != $byte_count { - return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( - $byte_count, - cbor_event::Len::Len(bytes.len() as u64), - "hash length", - )) - .into()); - } - Ok($name(bytes[..$byte_count].try_into().unwrap())) - })() - .map_err(|e| e.annotate(stringify!($name))) - } - } - - impl serde::Serialize for $name { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_hex()) - } - } - - impl<'de> serde::de::Deserialize<'de> for $name { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let s = ::deserialize(deserializer)?; - $name::from_hex(&s).map_err(|_e| { - serde::de::Error::invalid_value( - serde::de::Unexpected::Str(&s), - &"hex bytes for hash", - ) - }) - } - } - - impl JsonSchema for $name { - fn schema_name() -> String { - String::from(stringify!($name)) - } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - String::json_schema(gen) - } - fn is_referenceable() -> bool { - String::is_referenceable() - } - } - - impl Display for $name { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.to_hex()) - } - } - }; -} - -#[wasm_bindgen] -pub struct LegacyDaedalusPrivateKey(pub(crate) crypto::SecretKey); - -#[wasm_bindgen] -impl LegacyDaedalusPrivateKey { - pub fn from_bytes(bytes: &[u8]) -> Result { - crypto::SecretKey::::from_binary(bytes) - .map_err(|e| JsError::from_str(&format!("{}", e))) - .map(LegacyDaedalusPrivateKey) - } - - pub fn as_bytes(&self) -> Vec { - self.0.as_ref().to_vec() - } - - pub fn chaincode(&self) -> Vec { - const ED25519_PRIVATE_KEY_LENGTH: usize = 64; - const XPRV_SIZE: usize = 96; - self.0.as_ref()[ED25519_PRIVATE_KEY_LENGTH..XPRV_SIZE].to_vec() - } -} - -impl_hash_type!(Ed25519KeyHash, 28); -impl_hash_type!(ScriptHash, 28); -impl_hash_type!(TransactionHash, 32); -impl_hash_type!(GenesisDelegateHash, 28); -impl_hash_type!(GenesisHash, 28); -impl_hash_type!(AuxiliaryDataHash, 32); -impl_hash_type!(PoolMetadataHash, 32); -impl_hash_type!(VRFKeyHash, 32); -impl_hash_type!(BlockHash, 32); -impl_hash_type!(DataHash, 32); -impl_hash_type!(ScriptDataHash, 32); -// We might want to make these two vkeys normal classes later but for now it's just arbitrary bytes for us (used in block parsing) -impl_hash_type!(VRFVKey, 32); -impl_hash_type!(KESVKey, 32); -// same for this signature -//impl_hash_type!(KESSignature, 448); -// TODO: when >32 size trait implementations are out of nightly and into stable -// remove the following manual struct definition and use the above macro again if we -// don't have proper crypto implementations for it. -#[wasm_bindgen] -#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct KESSignature(pub(crate) Vec); - -#[wasm_bindgen] -impl KESSignature { - pub fn to_bytes(&self) -> Vec { - self.0.clone() - } -} - -// associated consts are not supported in wasm_bindgen -impl KESSignature { - pub const BYTE_COUNT: usize = 448; -} - -from_bytes!(KESSignature, bytes, { - match bytes.len() { - Self::BYTE_COUNT => Ok(KESSignature(bytes)), - other_len => { - let cbor_error = cbor_event::Error::WrongLen( - Self::BYTE_COUNT as u64, - cbor_event::Len::Len(other_len as u64), - "hash length", - ); - Err(DeserializeError::new( - "KESSignature", - DeserializeFailure::CBOR(cbor_error), - )) - } - } -}); - -impl cbor_event::se::Serialize for KESSignature { - fn serialize<'se, W: std::io::Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_bytes(&self.0) - } -} - -impl Deserialize for KESSignature { - fn deserialize( - raw: &mut Deserializer, - ) -> Result { - (|| -> Result { - let bytes = raw.bytes()?; - if bytes.len() != Self::BYTE_COUNT { - return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( - Self::BYTE_COUNT as u64, - cbor_event::Len::Len(bytes.len() as u64), - "hash length", - )) - .into()); - } - Ok(KESSignature(bytes)) - })() - .map_err(|e| e.annotate("KESSignature")) - } -} - -impl serde::Serialize for KESSignature { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&hex::encode(self.to_bytes())) - } -} - -impl<'de> serde::de::Deserialize<'de> for KESSignature { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let s = ::deserialize(deserializer)?; - if let Ok(hex_bytes) = hex::decode(s.clone()) { - if let Ok(sig) = KESSignature::from_bytes(hex_bytes) { - return Ok(sig); - } - } - Err(serde::de::Error::invalid_value( - serde::de::Unexpected::Str(&s), - &"hex bytes for KESSignature", - )) - } -} - -impl JsonSchema for KESSignature { - fn schema_name() -> String { - String::from("KESSignature") - } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - String::json_schema(gen) - } - fn is_referenceable() -> bool { - String::is_referenceable() - } -} - -// Evolving nonce type (used for Update's crypto) -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct Nonce { - hash: Option<[u8; 32]>, -} - -impl_to_from!(Nonce); - -// can't export consts via wasm_bindgen -impl Nonce { - pub const HASH_LEN: usize = 32; -} - -#[wasm_bindgen] -impl Nonce { - pub fn new_identity() -> Nonce { - Self { hash: None } - } - - pub fn new_from_hash(hash: Vec) -> Result { - use std::convert::TryInto; - match hash[..Self::HASH_LEN].try_into() { - Ok(bytes_correct_size) => Ok(Self { - hash: Some(bytes_correct_size), - }), - Err(e) => Err(JsError::from_str(&e.to_string())), - } - } - - pub fn get_hash(&self) -> Option> { - Some(self.hash?.to_vec()) - } -} - -impl cbor_event::se::Serialize for Nonce { - fn serialize<'se, W: std::io::Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - match &self.hash { - Some(hash) => { - serializer.write_array(cbor_event::Len::Len(2))?; - serializer.write_unsigned_integer(1)?; - serializer.write_bytes(hash) - } - None => { - serializer.write_array(cbor_event::Len::Len(1))?; - serializer.write_unsigned_integer(0) - } - } - } -} - -impl Deserialize for Nonce { - fn deserialize( - raw: &mut Deserializer, - ) -> Result { - (|| -> Result { - let len = raw.array()?; - let hash = match raw.unsigned_integer()? { - 0 => None, - 1 => { - let bytes = raw.bytes()?; - if bytes.len() != Self::HASH_LEN { - return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( - Self::HASH_LEN as u64, - cbor_event::Len::Len(bytes.len() as u64), - "hash length", - )) - .into()); - } - Some(bytes[..Self::HASH_LEN].try_into().unwrap()) - } - _ => return Err(DeserializeFailure::NoVariantMatched.into()), - }; - match len { - cbor_event::Len::Len(n) => { - let correct_len = match n { - 1 => hash.is_none(), - 2 => hash.is_some(), - _ => false, - }; - if !correct_len { - return Err(DeserializeFailure::NoVariantMatched.into()); - } - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - }; - Ok(Self { hash }) - })() - .map_err(|e| e.annotate(stringify!($name))) - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct VRFCert { - output: Vec, - proof: Vec, -} - -impl VRFCert { - pub const PROOF_LEN: usize = 80; -} - -impl_to_from!(VRFCert); - -#[wasm_bindgen] -impl VRFCert { - pub fn output(&self) -> Vec { - self.output.clone() - } - - pub fn proof(&self) -> Vec { - self.proof.clone() - } - - pub fn new(output: Vec, proof: Vec) -> Result { - if proof.len() != Self::PROOF_LEN { - return Err(JsError::from_str(&format!( - "proof len must be {} - found {}", - Self::PROOF_LEN, - proof.len() - ))); - } - Ok(Self { - output: output, - proof: proof, - }) - } -} - -impl cbor_event::se::Serialize for VRFCert { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - serializer.write_bytes(&self.output)?; - serializer.write_bytes(&self.proof)?; - Ok(serializer) - } -} - -impl Deserialize for VRFCert { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let output = (|| -> Result<_, DeserializeError> { Ok(raw.bytes()?) })() - .map_err(|e| e.annotate("output"))?; - let proof = (|| -> Result<_, DeserializeError> { Ok(raw.bytes()?) })() - .map_err(|e| e.annotate("proof"))?; - if proof.len() != Self::PROOF_LEN { - return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( - Self::PROOF_LEN as u64, - cbor_event::Len::Len(proof.len() as u64), - "proof length", - )) - .into()); - } - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - Ok(VRFCert { output, proof }) - })() - .map_err(|e| e.annotate("VRFCert")) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn nonce_identity() { - let orig = Nonce::new_identity(); - let deser = Nonce::deserialize(&mut Deserializer::from(std::io::Cursor::new( - orig.to_bytes(), - ))) - .unwrap(); - assert_eq!(orig.to_bytes(), deser.to_bytes()); - } - - #[test] - fn nonce_hash() { - let orig = Nonce::new_from_hash(vec![ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - ]) - .unwrap(); - let deser = Nonce::deserialize(&mut Deserializer::from(std::io::Cursor::new( - orig.to_bytes(), - ))) - .unwrap(); - assert_eq!(orig.to_bytes(), deser.to_bytes()); - } - - #[test] - fn xprv_128_test() { - // art forum devote street sure rather head chuckle guard poverty release quote oak craft enemy - let entropy = [ - 0x0c, 0xcb, 0x74, 0xf3, 0x6b, 0x7d, 0xa1, 0x64, 0x9a, 0x81, 0x44, 0x67, 0x55, 0x22, - 0xd4, 0xd8, 0x09, 0x7c, 0x64, 0x12, - ]; - let root_key = Bip32PrivateKey::from_bip39_entropy(&entropy, &[]); - - assert_eq!(hex::encode(&root_key.as_bytes()), "b8f2bece9bdfe2b0282f5bad705562ac996efb6af96b648f4445ec44f47ad95c10e3d72f26ed075422a36ed8585c745a0e1150bcceba2357d058636991f38a3791e248de509c070d812ab2fda57860ac876bc489192c1ef4ce253c197ee219a4"); - let xprv_128 = root_key.to_128_xprv(); - // test the 128 xprv is the right format - assert_eq!(hex::encode(&xprv_128), "b8f2bece9bdfe2b0282f5bad705562ac996efb6af96b648f4445ec44f47ad95c10e3d72f26ed075422a36ed8585c745a0e1150bcceba2357d058636991f38a37cf76399a210de8720e9fa894e45e41e29ab525e30bc402801c076250d1585bcd91e248de509c070d812ab2fda57860ac876bc489192c1ef4ce253c197ee219a4"); - let root_key_copy = Bip32PrivateKey::from_128_xprv(&xprv_128).unwrap(); - - // test converting to and back is equivalent to the identity function - assert_eq!(root_key.to_bech32(), root_key_copy.to_bech32()); - } - - #[test] - fn chaincode_gen() { - // art forum devote street sure rather head chuckle guard poverty release quote oak craft enemy - let entropy = [ - 0x0c, 0xcb, 0x74, 0xf3, 0x6b, 0x7d, 0xa1, 0x64, 0x9a, 0x81, 0x44, 0x67, 0x55, 0x22, - 0xd4, 0xd8, 0x09, 0x7c, 0x64, 0x12, - ]; - let root_key = Bip32PrivateKey::from_bip39_entropy(&entropy, &[]); - - let prv_chaincode = root_key.chaincode(); - assert_eq!( - hex::encode(&prv_chaincode), - "91e248de509c070d812ab2fda57860ac876bc489192c1ef4ce253c197ee219a4" - ); - - let pub_chaincode = root_key.to_public().chaincode(); - assert_eq!( - hex::encode(&pub_chaincode), - "91e248de509c070d812ab2fda57860ac876bc489192c1ef4ce253c197ee219a4" - ); - } - - #[test] - fn private_key_from_bech32() { - let pk = PrivateKey::generate_ed25519().unwrap(); - let pk_ext = PrivateKey::generate_ed25519extended().unwrap(); - - assert_eq!( - PrivateKey::from_bech32(&pk.to_bech32()).unwrap().as_bytes(), - pk.as_bytes(), - ); - assert_eq!( - PrivateKey::from_bech32(&pk_ext.to_bech32()) - .unwrap() - .as_bytes(), - pk_ext.as_bytes(), - ); - - let er = PrivateKey::from_bech32("qwe"); - assert!(er.is_err()); - } -} +// https://github.com/Emurgo/js-chain-libs \ No newline at end of file diff --git a/rust/src/emip3.rs b/rust/src/emip3.rs index 7bb3b04c..b69f545e 100644 --- a/rust/src/emip3.rs +++ b/rust/src/emip3.rs @@ -115,20 +115,4 @@ pub fn decrypt_with_password(password: &str, data: &str) -> Result), } impl std::fmt::Display for Key { @@ -16,6 +18,8 @@ impl std::fmt::Display for Key { match self { Key::Str(x) => write!(f, "\"{}\"", x), Key::Uint(x) => write!(f, "{}", x), + Key::OptUint(Some(x)) => write!(f, "{}", x), + Key::OptUint(None) => write!(f, "null"), } } } @@ -34,6 +38,10 @@ pub enum DeserializeFailure { found: Key, expected: Key, }, + FixedValuesMismatch { + found: Key, + expected: Vec, + }, MandatoryFieldMissing(Key), Metadata(JsError), NoVariantMatched, @@ -51,6 +59,9 @@ pub enum DeserializeFailure { UnknownKey(Key), UnexpectedKeyType(cbor_event::Type), VariableLenNatDecodeFailed, + IoError(String), + ExpectedType(String, cbor_event::Type), + CustomError(String), } #[derive(Debug)] @@ -104,6 +115,9 @@ impl std::fmt::Display for DeserializeError { DeserializeFailure::FixedValueMismatch { found, expected } => { write!(f, "Expected fixed value {} found {}", expected, found) } + DeserializeFailure::FixedValuesMismatch { found, expected } => { + write!(f, "Expected fixed value {:?} found {}", expected, found) + } DeserializeFailure::MandatoryFieldMissing(key) => { write!(f, "Mandatory field {} not found", key) } @@ -126,6 +140,11 @@ impl std::fmt::Display for DeserializeError { DeserializeFailure::VariableLenNatDecodeFailed => { write!(f, "Variable length natural number decode failed") } + DeserializeFailure::IoError(e) => write!(f, "IO error: {}", e), + DeserializeFailure::CustomError(e) => write!(f, "Deserialize error: {}", e), + DeserializeFailure::ExpectedType(expected, found) => { + write!(f, "Expected type {}, found {:?}", expected, found) + } } } } @@ -208,3 +227,27 @@ impl std::fmt::Display for JsError { #[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))] impl std::error::Error for JsError {} + +pub(crate) enum BuilderError { + RegularInputIsScript, + RegularInputIsFromRewardAddress, + MalformedAddress, + MintBuilderDifferentScriptType, + MintBuilderDifferentRedeemerDataAndExUnits(String, String), + MintBuilderDifferentWitnessTypeRef, + MintBuilderDifferentWitnessTypeNonRef +} + +impl BuilderError { + pub(crate) fn as_str(&self) -> String { + match self { + BuilderError::RegularInputIsScript => "You can't add a script input to this function. You can use `.add_native_script_input` or `.add_plutus_script_input` directly to register the input along with the witness.".to_string(), + BuilderError::RegularInputIsFromRewardAddress => "You can't use an input from reward address. To spend funds from reward address you to use withdrawal mechanism.".to_string(), + BuilderError::MalformedAddress => "The address is malformed.".to_string(), + BuilderError::MintBuilderDifferentScriptType => "You can't add a mint to the same policy id but with different script type.".to_string(), + BuilderError::MintBuilderDifferentRedeemerDataAndExUnits(redeemer1, redeemer2) => format!("You can't add a mint to the same policy id but with different redeemer data and ex units. Current redeemer {redeemer1}, your redeemer {redeemer2}"), + BuilderError::MintBuilderDifferentWitnessTypeRef => "You can't add a mint to the same policy id but with different witness type. Current witness is ref input".to_string(), + BuilderError::MintBuilderDifferentWitnessTypeNonRef => "You can't add a mint to the same policy id but with different witness type. Current witness is non ref".to_string(), + } + } +} diff --git a/rust/src/fakes.rs b/rust/src/fakes.rs deleted file mode 100644 index c846f876..00000000 --- a/rust/src/fakes.rs +++ /dev/null @@ -1,76 +0,0 @@ -#![allow(dead_code)] -use crate::{to_bignum, Address, BaseAddress, Bip32PrivateKey, DataHash, Ed25519KeyHash, Ed25519Signature, NetworkInfo, StakeCredential, TransactionHash, TransactionIndex, TransactionInput, TransactionOutput, Value, Vkey, PolicyID}; -use crate::crypto::ScriptHash; - -pub(crate) fn fake_bytes_32(x: u8) -> Vec { - vec![ - x, 239, 181, 120, 142, 135, 19, 200, 68, 223, 211, 43, 46, 145, 222, 30, 48, 159, 239, 255, - 213, 85, 248, 39, 204, 158, 225, 100, 1, 2, 3, 4, - ] -} - -pub(crate) fn fake_data_hash(x: u8) -> DataHash { - DataHash::from_bytes(fake_bytes_32(x)).unwrap() -} - -pub(crate) fn fake_key_hash(x: u8) -> Ed25519KeyHash { - Ed25519KeyHash::from_bytes((&fake_bytes_32(x)[0..28]).to_vec()).unwrap() -} - -pub(crate) fn fake_script_hash(x: u8) -> ScriptHash { - ScriptHash::from_bytes((&fake_bytes_32(x)[0..28]).to_vec()).unwrap() -} - -pub(crate) fn fake_base_address(x: u8) -> Address { - BaseAddress::new( - NetworkInfo::testnet().network_id(), - &StakeCredential::from_keyhash(&fake_key_hash(x)), - &StakeCredential::from_keyhash(&fake_key_hash(0)), - ) - .to_address() -} - -pub(crate) fn fake_tx_hash(input_hash_byte: u8) -> TransactionHash { - TransactionHash::from([input_hash_byte; 32]) -} - -pub(crate) fn fake_tx_input(input_hash_byte: u8) -> TransactionInput { - fake_tx_input2(input_hash_byte, 0) -} - -pub(crate) fn fake_tx_input2(input_hash_byte: u8, idx: TransactionIndex) -> TransactionInput { - TransactionInput::new(&fake_tx_hash(input_hash_byte), idx) -} - -pub(crate) fn fake_value() -> Value { - fake_value2(1_000_000) -} - -pub(crate) fn fake_value2(v: u64) -> Value { - Value::new(&to_bignum(v)) -} - -pub(crate) fn fake_tx_output(input_hash_byte: u8) -> TransactionOutput { - TransactionOutput::new(&fake_base_address(input_hash_byte), &fake_value()) -} - -pub(crate) fn fake_tx_output2(input_hash_byte: u8, val: u64) -> TransactionOutput { - TransactionOutput::new(&fake_base_address(input_hash_byte), &fake_value2(val)) -} - -pub(crate) fn fake_vkey() -> Vkey { - Vkey::new( - &Bip32PrivateKey::generate_ed25519_bip32() - .unwrap() - .to_public() - .to_raw_key(), - ) -} - -pub(crate) fn fake_signature(x: u8) -> Ed25519Signature { - Ed25519Signature::from_bytes([x; 64].to_vec()).unwrap() -} - -pub(crate) fn fake_policy_id(x: u8) -> PolicyID { - PolicyID::from([x; 28]) -} diff --git a/rust/src/fees.rs b/rust/src/fees.rs index 7bbf6d8e..f17eedf3 100644 --- a/rust/src/fees.rs +++ b/rust/src/fees.rs @@ -1,5 +1,5 @@ use super::*; -use utils::*; +use crate::rational::Rational; #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] @@ -42,40 +42,12 @@ pub fn calculate_ex_units_ceil_cost( ex_units: &ExUnits, ex_unit_prices: &ExUnitPrices, ) -> Result { - type Ratio = (BigInt, BigInt); - fn mult(sc: &SubCoin, x: &BigNum) -> Result { - let n: BigInt = BigInt::from_str(&sc.numerator.to_str())?; - let d: BigInt = BigInt::from_str(&sc.denominator.to_str())?; - let m: BigInt = BigInt::from_str(&x.to_str())?; - Ok((n.mul(&m), d)) - } - fn sum(a: &Ratio, b: &Ratio) -> Ratio { - // Ratio Addition: a/x + b/y = ((a*y) + (b*x))/(x*y) - let (a_num, a_denum) = &a; - let (b_num, b_denum) = &b; - if a_num.is_zero() { - return b.clone(); - } - if b_num.is_zero() { - return a.clone(); - } - let a_num_fixed = &a_num.mul(b_denum); - let b_num_fixed = &b_num.mul(a_denum); - let a_b_num_sum = a_num_fixed.add(b_num_fixed); - let common_denum = a_denum.mul(b_denum); - (a_b_num_sum, common_denum) - } - let mem_ratio: Ratio = mult(&ex_unit_prices.mem_price(), &ex_units.mem())?; - let steps_ratio: Ratio = mult(&ex_unit_prices.step_price(), &ex_units.steps())?; - let (total_num, total_denum) = sum(&mem_ratio, &steps_ratio); - match total_num.div_ceil(&total_denum).as_u64() { - Some(coin) => Ok(coin), - _ => Err(JsError::from_str(&format!( - "Failed to calculate ceil from ratio {}/{}", - total_num.to_str(), - total_denum.to_str(), - ))), - } + let mem_price: Rational = ex_unit_prices.mem_price().into(); + let steps_price: Rational = ex_unit_prices.step_price().into(); + let mem_ratio = mem_price.mul_bignum(&ex_units.mem())?; + let steps_ratio = steps_price.mul_bignum(&ex_units.steps())?; + let total = mem_ratio.add(&steps_ratio); + total.to_bignum_ceil() } #[wasm_bindgen] @@ -87,597 +59,55 @@ pub fn min_script_fee(tx: &Transaction, ex_unit_prices: &ExUnitPrices) -> Result Ok(Coin::zero()) } -#[cfg(test)] -mod tests { - use super::output_builder::TransactionOutputBuilder; - use super::*; - use address::*; - use crypto::*; - - // based off tx test vectors (https://gist.github.com/KtorZ/5a2089df0915f21aca368d12545ab230) - - // However, they don't match due to serialization differences in definite vs indefinite - // CBOR lengths for maps/arrays, thus for now we've got all the tests as >= instead. - // It's possible they're still off by a byte or two somewhere. - - #[test] - fn tx_simple_utxo() { - // # Vector #1: simple transaction - let mut inputs = TransactionInputs::new(); - inputs.add(&TransactionInput::new( - &TransactionHash::from_bytes( - hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") - .unwrap(), - ) - .unwrap(), - 0, - )); - let mut outputs = TransactionOutputs::new(); - - outputs.add( - &TransactionOutputBuilder::new() - .with_address( - &Address::from_bytes( - hex::decode("611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c") - .unwrap(), - ) - .unwrap(), - ) - .next() - .unwrap() - .with_coin(&to_bignum(1)) - .build() - .unwrap(), - ); - let body = TransactionBody::new(&inputs, &outputs, &to_bignum(94002), Some(10)); - - let mut w = TransactionWitnessSet::new(); - let mut vkw = Vkeywitnesses::new(); - vkw.add(&make_vkey_witness( - &hash_transaction(&body), - &PrivateKey::from_normal_bytes( - &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a") - .unwrap(), - ) - .unwrap(), - )); - w.set_vkeys(&vkw); - - let signed_tx = Transaction::new(&body, &w, None); - - let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); - assert_eq!( - hex::encode(signed_tx.to_bytes()), - "84a400818258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b700018182581d611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c01021a00016f32030aa10081825820f9aa3fccb7fe539e471188ccc9ee65514c5961c070b06ca185962484a4813bee5840fae5de40c94d759ce13bf9886262159c4f26a289fd192e165995b785259e503f6887bf39dfa23a47cf163784c6eee23f61440e749bc1df3c73975f5231aeda0ff5f6" - ); - assert_eq!( - min_fee(&signed_tx, &linear_fee).unwrap().to_str(), - "94502" // todo: compare to Haskell fee to make sure the diff is not too big - ); - } - - #[test] - fn tx_simple_byron_utxo() { - let mut inputs = TransactionInputs::new(); - inputs.add(&TransactionInput::new( - &TransactionHash::from_bytes( - hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") - .unwrap(), - ) - .unwrap(), - 0, - )); - let mut outputs = TransactionOutputs::new(); - - outputs.add( - &TransactionOutputBuilder::new() - .with_address( - &Address::from_bytes( - hex::decode("611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c") - .unwrap(), - ) - .unwrap(), - ) - .next() - .unwrap() - .with_coin(&to_bignum(1)) - .build() - .unwrap(), - ); - let body = TransactionBody::new(&inputs, &outputs, &to_bignum(112002), Some(10)); - - let mut w = TransactionWitnessSet::new(); - let mut bootstrap_wits = BootstrapWitnesses::new(); - bootstrap_wits.add(&make_icarus_bootstrap_witness( - &hash_transaction(&body), - &ByronAddress::from_base58("Ae2tdPwUPEZ6r6zbg4ibhFrNnyKHg7SYuPSfDpjKxgvwFX9LquRep7gj7FQ").unwrap(), - &Bip32PrivateKey::from_bytes( - &hex::decode("d84c65426109a36edda5375ea67f1b738e1dacf8629f2bb5a2b0b20f3cd5075873bf5cdfa7e533482677219ac7d639e30a38e2e645ea9140855f44ff09e60c52c8b95d0d35fe75a70f9f5633a3e2439b2994b9e2bc851c49e9f91d1a5dcbb1a3").unwrap() - ).unwrap() - )); - w.set_bootstraps(&bootstrap_wits); - - let signed_tx = Transaction::new(&body, &w, None); - - let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); - assert_eq!( - hex::encode(signed_tx.to_bytes()), - "84a400818258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b700018182581d611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c01021a0001b582030aa10281845820473811afd4d939b337c9be1a2ceeb2cb2c75108bddf224c5c21c51592a7b204a5840f0b04a852353eb23b9570df80b2aa6a61b723341ab45a2024a05b07cf58be7bdfbf722c09040db6cee61a0d236870d6ad1e1349ac999ec0db28f9471af25fb0c5820c8b95d0d35fe75a70f9f5633a3e2439b2994b9e2bc851c49e9f91d1a5dcbb1a341a0f5f6" - ); - assert_eq!( - min_fee(&signed_tx, &linear_fee).unwrap().to_str(), - "112502" // todo: compare to Haskell fee to make sure the diff is not too big - ); - } - - #[test] - fn tx_multi_utxo() { - // # Vector #2: multiple outputs and inputs - let mut inputs = TransactionInputs::new(); - inputs.add(&TransactionInput::new( - &TransactionHash::from_bytes( - hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") - .unwrap(), - ) - .unwrap(), - 42, - )); - inputs.add(&TransactionInput::new( - &TransactionHash::from_bytes( - hex::decode("82839f8200d81858248258203b40265111d8bb3c3c608d95b3a0bf83461ace32") - .unwrap(), - ) - .unwrap(), - 7, - )); - let mut outputs = TransactionOutputs::new(); - - outputs.add( - &TransactionOutputBuilder::new() - .with_address( - &Address::from_bytes( - hex::decode("611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c") - .unwrap(), - ) - .unwrap(), - ) - .next() - .unwrap() - .with_coin(&to_bignum(289)) - .build() - .unwrap(), - ); - outputs.add( - &TransactionOutputBuilder::new() - .with_address( - &Address::from_bytes( - hex::decode("61bcd18fcffa797c16c007014e2b8553b8b9b1e94c507688726243d611") - .unwrap(), - ) - .unwrap(), - ) - .next() - .unwrap() - .with_coin(&to_bignum(874551452)) - .build() - .unwrap(), - ); - let body = TransactionBody::new(&inputs, &outputs, &to_bignum(183502), Some(999)); - - let mut w = TransactionWitnessSet::new(); - let mut vkw = Vkeywitnesses::new(); - vkw.add(&make_vkey_witness( - &hash_transaction(&body), - &PrivateKey::from_normal_bytes( - &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a") - .unwrap(), - ) - .unwrap(), - )); - vkw.add(&make_vkey_witness( - &hash_transaction(&body), - &PrivateKey::from_normal_bytes( - &hex::decode("13fe79205e16c09536acb6f0524d04069f380329d13949698c5f22c65c989eb4") - .unwrap(), - ) - .unwrap(), - )); - w.set_vkeys(&vkw); - - let signed_tx = Transaction::new(&body, &w, None); - - let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); - assert_eq!( - hex::encode(signed_tx.to_bytes()), - "84a400828258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7182a82582082839f8200d81858248258203b40265111d8bb3c3c608d95b3a0bf83461ace3207018282581d611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c19012182581d61bcd18fcffa797c16c007014e2b8553b8b9b1e94c507688726243d6111a3420989c021a0002ccce031903e7a10082825820f9aa3fccb7fe539e471188ccc9ee65514c5961c070b06ca185962484a4813bee58401ec3e56008650282ba2e1f8a20e81707810b2d0973c4d42a1b4df65b732bda81567c7824904840b2554d2f33861da5d70588a29d33b2b61042e3c3445301d8008258206872b0a874acfe1cace12b20ea348559a7ecc912f2fc7f674f43481df973d92c5840a0718fb5b37d89ddf926c08e456d3f4c7f749e91f78bb3e370751d5b632cbd20d38d385805291b1ef2541b02543728a235e01911f4b400bfb50e5fce589de907f5f6" - ); - assert_eq!( - min_fee(&signed_tx, &linear_fee).unwrap().to_str(), - "184002" // todo: compare to Haskell fee to make sure the diff is not too big - ); - } - - #[test] - fn tx_register_stake() { - // # Vector #3: with stake pool registration certificate - let network = 1; - let mut inputs = TransactionInputs::new(); - inputs.add(&TransactionInput::new( - &TransactionHash::from_bytes( - hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") - .unwrap(), - ) - .unwrap(), - 0, - )); - let mut outputs = TransactionOutputs::new(); - - outputs.add( - &TransactionOutputBuilder::new() - .with_address( - &Address::from_bytes( - hex::decode("611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c") - .unwrap(), - ) - .unwrap(), - ) - .next() - .unwrap() - .with_coin(&to_bignum(1)) - .build() - .unwrap(), - ); - let mut body = TransactionBody::new(&inputs, &outputs, &to_bignum(266002), Some(10)); - - let mut certs = Certificates::new(); - - let mut pool_owners = Ed25519KeyHashes::new(); - pool_owners.add( - &PublicKey::from_bytes( - &hex::decode("54d1a9c5ad69586ceeb839c438400c376c0bd34825fb4c17cc2f58c54e1437f3") - .unwrap(), - ) - .unwrap() - .hash(), - ); - let registration_cert = PoolRegistration::new(&PoolParams::new( - &PublicKey::from_bytes( - &hex::decode("b24c040e65994bd5b0621a060166d32d356ef4be3cc1f848426a4cf386887089") - .unwrap(), - ) - .unwrap() - .hash(), // operator - &VRFKeyHash::from(blake2b256( - &hex::decode("fbf6d41985670b9041c5bf362b5262cf34add5d265975de176d613ca05f37096") - .unwrap(), - )), // vrf_keyhash - &to_bignum(1000000), // pledge - &to_bignum(1000000), // cost - &UnitInterval::new(&to_bignum(3), &to_bignum(100)), // margin - &RewardAddress::new( - network, - &StakeCredential::from_keyhash( - &PublicKey::from_bytes( - &hex::decode( - "54d1a9c5ad69586ceeb839c438400c376c0bd34825fb4c17cc2f58c54e1437f3", - ) - .unwrap(), - ) - .unwrap() - .hash(), - ), - ), // reward_address - &pool_owners, // pool_owners - &Relays::new(), // relays - None, // metadata - )); - certs.add(&Certificate::new_pool_registration(®istration_cert)); - body.set_certs(&certs); - - let mut w = TransactionWitnessSet::new(); - let mut vkw = Vkeywitnesses::new(); - // input key witness - vkw.add(&make_vkey_witness( - &hash_transaction(&body), - &PrivateKey::from_normal_bytes( - &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a") - .unwrap(), - ) - .unwrap(), - )); - // operator key witness - vkw.add(&make_vkey_witness( - &hash_transaction(&body), - &PrivateKey::from_normal_bytes( - &hex::decode("2363f3660b9f3b41685665bf10632272e2d03c258e8a5323436f0f3406293505") - .unwrap(), - ) - .unwrap(), - )); - // owner key witness - vkw.add(&make_vkey_witness( - &hash_transaction(&body), - &PrivateKey::from_normal_bytes( - &hex::decode("5ada7f4d92bce1ee1707c0a0e211eb7941287356e6ed0e76843806e307b07c8d") - .unwrap(), - ) - .unwrap(), - )); - w.set_vkeys(&vkw); - - let signed_tx = Transaction::new(&body, &w, None); - - let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); - assert_eq!( - hex::encode(signed_tx.to_bytes()), - "84a500818258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b700018182581d611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c01021a00040f12030a04818a03581c1c13374874c68016df54b1339b6cacdd801098431e7659b24928efc15820bd0000f498ccacdc917c28274cba51c415f3f21931ff41ca8dc1197499f8e1241a000f42401a000f4240d81e82031864581de151df9ba1b74a1c9608a487e114184556801e927d31d96425cb80af7081581c51df9ba1b74a1c9608a487e114184556801e927d31d96425cb80af7080f6a10083825820f9aa3fccb7fe539e471188ccc9ee65514c5961c070b06ca185962484a4813bee5840a7f305d7e46abfe0f7bea6098bdf853ab9ce8e7aa381be5a991a871852f895a718e20614e22be43494c4dc3a8c78c56cd44fd38e0e5fff3e2fbd19f70402fc02825820b24c040e65994bd5b0621a060166d32d356ef4be3cc1f848426a4cf386887089584013c372f82f1523484eab273241d66d92e1402507760e279480912aa5f0d88d656d6f25d41e65257f2f38c65ac5c918a6735297741adfc718394994f20a1cfd0082582054d1a9c5ad69586ceeb839c438400c376c0bd34825fb4c17cc2f58c54e1437f35840d326b993dfec21b9b3e1bd2f80adadc2cd673a1d8d033618cc413b0b02bc3b7efbb23d1ff99138abd05c398ce98e7983a641b50dcf0f64ed33f26c6e636b0b0ff5f6" - ); - assert_eq!( - min_fee(&signed_tx, &linear_fee).unwrap().to_str(), - "269502" // todo: compare to Haskell fee to make sure the diff is not too big - ); - } - - // #[test] - // fn tx_delegate_stake() { - // let mut inputs = TransactionInputs::new(); - // inputs.add(&TransactionInput::new(&genesis_id(), 0)); - // let mut outputs = TransactionOutputs::new(); - // outputs.add(&TransactionOutput::new(&alice_addr(), to_bignum(10))); - // let mut body = TransactionBody::new(&inputs, &outputs, to_bignum(94), 10); - // let mut certs = Certificates::new(); - // certs.add(&Certificate::new_stake_delegation(&StakeDelegation::new(&bob_stake(), &alice_pool()))); - // body.set_certs(&certs); - // let w = make_mock_witnesses_vkey(&body, vec![&alice_key(), &bob_key()]); - // let tx = Transaction::new(&body, &w, None); - // let haskell_crypto_bytes = witness_vkey_bytes_haskell(&w) + HASKELL_HLEN * 2; - // let our_crypto_bytes = witness_vkey_bytes_rust(&w) + Ed25519KeyHash::BYTE_COUNT + Ed25519KeyHash::BYTE_COUNT; - // assert!(txsize(&tx) - our_crypto_bytes + haskell_crypto_bytes >= 178); - // } - - // #[test] - // fn tx_deregister_stake() { - // let mut inputs = TransactionInputs::new(); - // inputs.add(&TransactionInput::new(&genesis_id(), 0)); - // let mut outputs = TransactionOutputs::new(); - // outputs.add(&TransactionOutput::new(&alice_addr(), to_bignum(10))); - // let mut body = TransactionBody::new(&inputs, &outputs, to_bignum(94), 10); - // let mut certs = Certificates::new(); - // certs.add(&Certificate::new_stake_deregistration(&StakeDeregistration::new(&alice_pay()))); - // body.set_certs(&certs); - // let w = make_mock_witnesses_vkey(&body, vec![&alice_key()]); - // let tx = Transaction::new(&body, &w, None); - // let haskell_crypto_bytes = witness_vkey_bytes_haskell(&w) + HASKELL_HLEN; - // let our_crypto_bytes = witness_vkey_bytes_rust(&w) + Ed25519KeyHash::BYTE_COUNT; - // assert!(txsize(&tx) - our_crypto_bytes + haskell_crypto_bytes >= 150); - // } - - // #[test] - // fn tx_register_pool() { - // let mut inputs = TransactionInputs::new(); - // inputs.add(&TransactionInput::new(&genesis_id(), 0)); - // let mut outputs = TransactionOutputs::new(); - // outputs.add(&TransactionOutput::new(&alice_addr(), to_bignum(10))); - // let mut body = TransactionBody::new(&inputs, &outputs, to_bignum(94), 10); - // let mut certs = Certificates::new(); - // let mut owners = Ed25519KeyHashes::new(); - // owners.add(&(alice_stake().to_keyhash().unwrap())); - // let mut relays = Relays::new(); - // relays.add(&Relay::new_single_host_name(&SingleHostName::new(None, String::from("relay.io")))); - // let params = PoolParams::new( - // &alice_pool(), - // &VRFKeyHash::from([0u8; VRFKeyHash::BYTE_COUNT]), - // to_bignum(1), - // to_bignum(5), - // &UnitInterval::new(to_bignum(1), to_bignum(10)), - // &RewardAddress::new(NetworkInfo::testnet().network_id(), &alice_stake()), - // &owners, - // &relays, - // Some(PoolMetadata::new(String::from("alice.pool"), &MetadataHash::from([0u8; MetadataHash::BYTE_COUNT]))) - // ); - // certs.add(&Certificate::new_pool_registration(&PoolRegistration::new(¶ms))); - // body.set_certs(&certs); - // let w = make_mock_witnesses_vkey(&body, vec![&alice_key()]); - // let tx = Transaction::new(&body, &w, None); - // let haskell_crypto_bytes = witness_vkey_bytes_haskell(&w) - // + HASKELL_HLEN // operator pool keyhash - // + HASKELL_HLEN // vrf keyhash - // + HASKELL_HLEN // reward account - // + owners.len() * HASKELL_HLEN // owners' keyhashes - // + HASKELL_HLEN; // metadata hash - // let our_crypto_bytes = witness_vkey_bytes_rust(&w) - // + Ed25519KeyHash::BYTE_COUNT - // + VRFKeyHash::BYTE_COUNT - // + Ed25519KeyHash::BYTE_COUNT - // + owners.len() * Ed25519KeyHash::BYTE_COUNT - // + MetadataHash::BYTE_COUNT; - // assert!(txsize(&tx) - our_crypto_bytes + haskell_crypto_bytes >= 200); - // } - - // #[test] - // fn tx_retire_pool() { - // let mut inputs = TransactionInputs::new(); - // inputs.add(&TransactionInput::new(&genesis_id(), 0)); - // let mut outputs = TransactionOutputs::new(); - // outputs.add(&TransactionOutput::new(&alice_addr(), to_bignum(10))); - // let mut body = TransactionBody::new(&inputs, &outputs, to_bignum(94), 10); - // let mut certs = Certificates::new(); - // certs.add(&Certificate::new_pool_retirement(&PoolRetirement::new(&alice_pool(), 5))); - // body.set_certs(&certs); - // let w = make_mock_witnesses_vkey(&body, vec![&alice_key()]); - // let tx = Transaction::new(&body, &w, None); - // let haskell_crypto_bytes = witness_vkey_bytes_haskell(&w) + HASKELL_HLEN; - // let our_crypto_bytes = witness_vkey_bytes_rust(&w) + Ed25519KeyHash::BYTE_COUNT; - // assert!(txsize(&tx) - our_crypto_bytes + haskell_crypto_bytes >= 149); - // } - - // #[test] - // fn tx_metadata() { - // let mut inputs = TransactionInputs::new(); - // inputs.add(&TransactionInput::new(&genesis_id(), 0)); - // let mut outputs = TransactionOutputs::new(); - // outputs.add(&TransactionOutput::new(&alice_addr(), to_bignum(10))); - // let mut body = TransactionBody::new(&inputs, &outputs, to_bignum(94), 10); - // body.set_metadata_hash(&MetadataHash::from([37; MetadataHash::BYTE_COUNT])); - // let w = make_mock_witnesses_vkey(&body, vec![&alice_key()]); - // let mut metadata = TransactionMetadata::new(); - // let mut md_list = TransactionMetadatums::new(); - // md_list.add(&TransactionMetadatum::new_int(&Int::new(&to_bignum(5)))); - // md_list.add(&TransactionMetadatum::new_text(String::from("hello"))); - // metadata.insert(TransactionMetadatumLabel::new(0), &TransactionMetadatum::new_arr_transaction_metadatum(&md_list)); - // let tx = Transaction::new(&body, &w, Some(metadata)); - // let haskell_crypto_bytes = witness_vkey_bytes_haskell(&w) + HASKELL_HLEN; - // let our_crypto_bytes = witness_vkey_bytes_rust(&w) + MetadataHash::BYTE_COUNT; - // assert!(txsize(&tx) - our_crypto_bytes + haskell_crypto_bytes >= 154); - // } - - // #[test] - // fn tx_multisig() { - // let mut inputs = TransactionInputs::new(); - // inputs.add(&TransactionInput::new(&genesis_id(), 0)); - // let mut outputs = TransactionOutputs::new(); - // outputs.add(&TransactionOutput::new(&alice_addr(), to_bignum(10))); - // let body = TransactionBody::new(&inputs, &outputs, to_bignum(94), 10); - // let mut w = make_mock_witnesses_vkey(&body, vec![&alice_key(), &bob_key()]); - // let mut script_witnesses = MultisigScripts::new(); - // let mut inner_scripts = MultisigScripts::new(); - // inner_scripts.add(&MultisigScript::new_msig_pubkey(&alice_pay().to_keyhash().unwrap())); - // inner_scripts.add(&MultisigScript::new_msig_pubkey(&bob_pay().to_keyhash().unwrap())); - // inner_scripts.add(&MultisigScript::new_msig_pubkey(&carl_pay().to_keyhash().unwrap())); - // script_witnesses.add(&MultisigScript::new_msig_n_of_k(2, &inner_scripts)); - // w.set_scripts(&script_witnesses); - // let tx = Transaction::new(&body, &w, None); - // let haskell_crypto_bytes = witness_vkey_bytes_haskell(&w); - // let our_crypto_bytes = witness_vkey_bytes_rust(&w); - // assert!(txsize(&tx) - our_crypto_bytes + haskell_crypto_bytes - haskell_multisig_byte_diff(&script_witnesses) >= 189); - // } - - #[test] - fn tx_withdrawal() { - // # Vector #8: with reward withdrawal - let mut inputs = TransactionInputs::new(); - inputs.add(&TransactionInput::new( - &TransactionHash::from_bytes( - hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") - .unwrap(), - ) - .unwrap(), - 0, - )); - let mut outputs = TransactionOutputs::new(); - - outputs.add( - &TransactionOutputBuilder::new() - .with_address( - &Address::from_bytes( - hex::decode("611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c") - .unwrap(), - ) - .unwrap(), - ) - .next() - .unwrap() - .with_coin(&to_bignum(1)) - .build() - .unwrap(), - ); - let mut body = TransactionBody::new(&inputs, &outputs, &to_bignum(162502), Some(10)); - let mut withdrawals = Withdrawals::new(); - withdrawals.insert( - &RewardAddress::from_address( - &Address::from_bytes( - hex::decode("e151df9ba1b74a1c9608a487e114184556801e927d31d96425cb80af70") - .unwrap(), - ) - .unwrap(), - ) - .unwrap(), - &to_bignum(1337), - ); - body.set_withdrawals(&withdrawals); +#[wasm_bindgen] +pub fn min_ref_script_fee( + total_ref_scripts_size: usize, + ref_script_coins_per_byte: &UnitInterval, +) -> Result { + let multiplier = Rational::new(BigInt::from(12), BigInt::from(10)); // 1.2 + let size_increment: usize = 25_600; // 25KiB + let ref_multiplier: Rational = ref_script_coins_per_byte.into(); + let total_fee = tier_ref_script_fee( + multiplier, + size_increment, + ref_multiplier, + total_ref_scripts_size, + )?; + + Ok(total_fee) +} - let mut w = TransactionWitnessSet::new(); - let mut vkw = Vkeywitnesses::new(); - // input key witness - vkw.add(&make_vkey_witness( - &hash_transaction(&body), - &PrivateKey::from_normal_bytes( - &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a") - .unwrap(), - ) - .unwrap(), +fn tier_ref_script_fee( + multiplier: Rational, + size_increment: usize, + base_fee: Rational, + total_size: usize, +) -> Result { + if multiplier.is_negative_or_zero() || size_increment == 0 { + return Err(JsError::from_str( + "Size increment and multiplier must be positive", )); - // withdrawal key witness - vkw.add(&make_vkey_witness( - &hash_transaction(&body), - &PrivateKey::from_normal_bytes( - &hex::decode("5ada7f4d92bce1ee1707c0a0e211eb7941287356e6ed0e76843806e307b07c8d") - .unwrap(), - ) - .unwrap(), - )); - w.set_vkeys(&vkw); - - let signed_tx = Transaction::new(&body, &w, None); - - let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); - assert_eq!( - hex::encode(signed_tx.to_bytes()), - "84a500818258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b700018182581d611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c01021a00027ac6030a05a1581de151df9ba1b74a1c9608a487e114184556801e927d31d96425cb80af70190539a10082825820f9aa3fccb7fe539e471188ccc9ee65514c5961c070b06ca185962484a4813bee5840fc0493f7121efe385d72830680e735ccdef99c3a31953fe877b89ad3a97fcdb871cc7f2cdd6a8104e52f6963bd9e10d814d4fabdbcdc8475bc63e872dcc94d0a82582054d1a9c5ad69586ceeb839c438400c376c0bd34825fb4c17cc2f58c54e1437f35840a051ba927582004aedab736b9f1f9330ff867c260f4751135d480074256e83cd23d2a4bb109f955c43afdcdc5d1841b28d5c1ea2148dfbb6252693590692bb00f5f6" - ); - assert_eq!( - min_fee(&signed_tx, &linear_fee).unwrap().to_str(), - "163002" // todo: compare to Haskell fee to make sure the diff is not too big - ); } - fn exunits(mem: u64, steps: u64) -> ExUnits { - ExUnits::new(&to_bignum(mem), &to_bignum(steps)) - } + let full_tiers = (total_size / size_increment) as u32; + let partial_tier_size = total_size % size_increment; + let tier_price = base_fee.mul_usize(size_increment); - fn subcoin(num: u64, denum: u64) -> SubCoin { - SubCoin::new(&to_bignum(num), &to_bignum(denum)) - } + let mut acc = Rational::zero(); - fn exunit_prices(mem_prices: (u64, u64), step_prices: (u64, u64)) -> ExUnitPrices { - ExUnitPrices::new( - &subcoin(mem_prices.0, mem_prices.1), - &subcoin(step_prices.0, step_prices.1), - ) + if full_tiers > 0 { + let progression_enumerator = Rational::one().sub(&multiplier.pow(full_tiers)); + let progression_denominator = Rational::one().sub(&multiplier); + let tier_progression_sum = progression_enumerator.div_ratio(&progression_denominator); + acc = acc.add(&tier_price.mul_ratio(&tier_progression_sum)); } - fn _calculate_ex_units_ceil_cost( - mem: u64, - steps: u64, - mem_prices: (u64, u64), - step_prices: (u64, u64), - ) -> Coin { - let ex_units = exunits(mem, steps); - let ex_unit_prices = exunit_prices(mem_prices, step_prices); - calculate_ex_units_ceil_cost(&ex_units, &ex_unit_prices).unwrap() + // Add the partial tier + if partial_tier_size > 0 { + let last_tier_price = base_fee.mul_ratio(&multiplier.pow(full_tiers)); + let partial_tier_fee = last_tier_price.mul_usize(partial_tier_size); + acc = acc.add(&partial_tier_fee); } - #[test] - fn test_calc_ex_units_cost() { - // 10 * (2/1) + 20 * (3/1) = 10 * 2 + 20 * 3 = 20 + 60 - assert_eq!( - _calculate_ex_units_ceil_cost(10, 20, (2, 1), (3, 1)), - to_bignum(80), - ); - // 22 * (12/6) + 33 * (33/11) = 22 * 2 + 33 * 3 = 44 + 99 = 143 - assert_eq!( - _calculate_ex_units_ceil_cost(22, 33, (12, 6), (33, 11)), - to_bignum(143), - ); - // 10 * (5/7) + 20 * (9/13) = 50/7 + 180/13 = 650/91 + 1260/91 = 1910/91 = ceil(20.98) = 21 - assert_eq!( - _calculate_ex_units_ceil_cost(10, 20, (5, 7), (9, 13)), - to_bignum(21), - ); - // 22 * (7/5) + 33 * (13/9) = 154/5 + 429/9 = 1386/45 + 2145/45 = 3531/45 = ceil(78.46) = 79 - assert_eq!( - _calculate_ex_units_ceil_cost(22, 33, (7, 5), (13, 9)), - to_bignum(79), - ); - } + acc.to_bignum_floor() } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 691e7d2e..a81cf5a3 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -15,6 +15,12 @@ extern crate quickcheck; extern crate quickcheck_macros; extern crate hex; +#[cfg(test)] +mod tests; + +#[macro_use] +extern crate num_derive; + use std::convert::TryInto; use std::io::{BufRead, Seek, Write}; @@ -22,12 +28,12 @@ use std::io::{BufRead, Seek, Write}; use noop_proc_macro::wasm_bindgen; #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] -use wasm_bindgen::prelude::{JsValue, wasm_bindgen}; +use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; // This file was code-generated using an experimental CDDL to rust tool: // https://github.com/Emurgo/cddl-codegen -use cbor_event::Special as CBORSpecial; +use cbor_event::{Len, Special as CBORSpecial}; use cbor_event::Type as CBORType; use cbor_event::{ self, @@ -35,50 +41,55 @@ use cbor_event::{ se::{Serialize, Serializer}, }; -pub mod address; +mod builders; +pub use builders::*; pub mod chain_core; pub mod chain_crypto; -pub mod crypto; -pub mod emip3; -pub mod error; -pub mod fees; +mod crypto; +pub(crate) use crypto::*; +mod emip3; +pub use emip3::*; +mod error; +pub use error::*; +mod fees; +pub use fees::*; pub mod impl_mockchain; pub mod legacy_address; -pub mod metadata; -pub mod output_builder; -pub mod plutus; -pub mod serialization; pub mod traits; -pub mod tx_builder; -pub mod tx_builder_constants; +mod protocol_types; +pub use protocol_types::*; pub mod typed_bytes; -pub mod protocol_types; -pub mod ser_info; #[macro_use] -pub mod utils; -mod fakes; -mod serialization_macros; -mod serialization_tools; +mod utils; +pub use utils::*; +mod serialization; +mod rational; + +pub use serialization::*; use crate::traits::NoneOrEmpty; -use address::*; -use crypto::*; -use error::*; -use metadata::*; -use plutus::*; use schemars::JsonSchema; use std::cmp::Ordering; use std::collections::BTreeSet; -use std::fmt::Display; use std::fmt; -use utils::*; -use ser_info::types::*; +use std::fmt::Display; +use hashlink::LinkedHashMap; type DeltaCoin = Int; #[wasm_bindgen] #[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + Default, + serde::Serialize, + serde::Deserialize, + JsonSchema, )] pub struct UnitInterval { numerator: BigNum, @@ -106,7 +117,6 @@ impl UnitInterval { } type SubCoin = UnitInterval; -type Rational = UnitInterval; type Epoch = u32; type Slot32 = u32; type SlotBigNum = BigNum; @@ -162,41 +172,7 @@ impl Transaction { type TransactionIndex = u32; // index of a cert within a tx type CertificateIndex = u32; - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct TransactionInputs(Vec); - -impl_to_from!(TransactionInputs); - -#[wasm_bindgen] -impl TransactionInputs { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> TransactionInput { - self.0[index].clone() - } - - pub fn add(&mut self, elem: &TransactionInput) { - self.0.push(elem.clone()); - } - - pub fn to_option(&self) -> Option { - if self.len() > 0 { - Some(self.clone()) - } else { - None - } - } -} +type GovernanceActionIndex = u32; #[wasm_bindgen] #[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize, JsonSchema)] @@ -232,375 +208,27 @@ impl<'a> IntoIterator for &'a TransactionOutputs { } } -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -enum DataCostEnum { - CoinsPerWord(Coin), - CoinsPerByte(Coin), -} - #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct DataCost(DataCostEnum); +pub struct DataCost{ + coins_per_byte: Coin +} #[wasm_bindgen] impl DataCost { - /// !!! DEPRECATED !!! - /// Since babbage era we should use coins per byte. Use `.new_coins_per_byte` instead. - #[deprecated( - since = "11.0.0", - note = "Since babbage era we should use coins per byte. Use `.new_coins_per_byte` instead." - )] - pub fn new_coins_per_word(coins_per_word: &Coin) -> DataCost { - if coins_per_word != &BigNum::zero() { - DataCost(DataCostEnum::CoinsPerWord(coins_per_word.clone())) - } else { - DataCost(DataCostEnum::CoinsPerByte(BigNum::zero())) - } - } - pub fn new_coins_per_byte(coins_per_byte: &Coin) -> DataCost { - DataCost(DataCostEnum::CoinsPerByte(coins_per_byte.clone())) - } - - pub fn coins_per_byte(&self) -> Coin { - match &self.0 { - DataCostEnum::CoinsPerByte(coins_per_byte) => coins_per_byte.clone(), - DataCostEnum::CoinsPerWord(coins_per_word) => { - let bytes_in_word = to_bignum(8); - coins_per_word.div_floor(&bytes_in_word) - } - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct Certificates(Vec); - -impl_to_from!(Certificates); - -#[wasm_bindgen] -impl Certificates { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> Certificate { - self.0[index].clone() - } - - pub fn add(&mut self, elem: &Certificate) { - self.0.push(elem.clone()); - } -} - -pub type RequiredSigners = Ed25519KeyHashes; -pub type RequiredSignersSet = BTreeSet; - -impl From<&Ed25519KeyHashes> for RequiredSignersSet { - fn from(keys: &Ed25519KeyHashes) -> Self { - keys.0.iter().fold(BTreeSet::new(), |mut set, k| { - set.insert(k.clone()); - set - }) - } -} - -#[wasm_bindgen] -#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize, JsonSchema)] -pub struct TransactionBody { - inputs: TransactionInputs, - outputs: TransactionOutputs, - fee: Coin, - ttl: Option, - certs: Option, - withdrawals: Option, - update: Option, - auxiliary_data_hash: Option, - validity_start_interval: Option, - mint: Option, - script_data_hash: Option, - collateral: Option, - required_signers: Option, - network_id: Option, - collateral_return: Option, - total_collateral: Option, - reference_inputs: Option, -} - -impl_to_from!(TransactionBody); - -#[wasm_bindgen] -impl TransactionBody { - pub fn inputs(&self) -> TransactionInputs { - self.inputs.clone() - } - - pub fn outputs(&self) -> TransactionOutputs { - self.outputs.clone() - } - - pub fn fee(&self) -> Coin { - self.fee.clone() - } - - /// !!! DEPRECATED !!! - /// Returns a Slot32 (u32) value in case the underlying original BigNum (u64) value is within the limits. - /// Otherwise will just raise an error. - #[deprecated( - since = "10.1.0", - note = "Possible boundary error. Use ttl_bignum instead" - )] - pub fn ttl(&self) -> Result, JsError> { - match self.ttl { - Some(ttl) => match ttl.try_into() { - Ok(ttl32) => Ok(Some(ttl32)), - Err(err) => Err(err), - }, - None => Ok(None), - } - } - - pub fn ttl_bignum(&self) -> Option { - self.ttl - } - - pub fn set_ttl(&mut self, ttl: &SlotBigNum) { - self.ttl = Some(ttl.clone()) - } - - pub fn remove_ttl(&mut self) { - self.ttl = None - } - - pub fn set_certs(&mut self, certs: &Certificates) { - self.certs = Some(certs.clone()) - } - - pub fn certs(&self) -> Option { - self.certs.clone() - } - - pub fn set_withdrawals(&mut self, withdrawals: &Withdrawals) { - self.withdrawals = Some(withdrawals.clone()) - } - - pub fn withdrawals(&self) -> Option { - self.withdrawals.clone() - } - - pub fn set_update(&mut self, update: &Update) { - self.update = Some(update.clone()) - } - - pub fn update(&self) -> Option { - self.update.clone() - } - - pub fn set_auxiliary_data_hash(&mut self, auxiliary_data_hash: &AuxiliaryDataHash) { - self.auxiliary_data_hash = Some(auxiliary_data_hash.clone()) - } - - pub fn auxiliary_data_hash(&self) -> Option { - self.auxiliary_data_hash.clone() - } - - /// !!! DEPRECATED !!! - /// Uses outdated slot number format. - #[deprecated( - since = "10.1.0", - note = "Underlying value capacity of slot (BigNum u64) bigger then Slot32. Use set_validity_start_interval_bignum instead." - )] - pub fn set_validity_start_interval(&mut self, validity_start_interval: Slot32) { - self.validity_start_interval = Some(validity_start_interval.into()) - } - - pub fn set_validity_start_interval_bignum(&mut self, validity_start_interval: SlotBigNum) { - self.validity_start_interval = Some(validity_start_interval.clone()) - } - - pub fn validity_start_interval_bignum(&self) -> Option { - self.validity_start_interval.clone() - } - - /// !!! DEPRECATED !!! - /// Returns a Option (u32) value in case the underlying original Option (u64) value is within the limits. - /// Otherwise will just raise an error. - /// Use `.validity_start_interval_bignum` instead. - #[deprecated( - since = "10.1.0", - note = "Possible boundary error. Use validity_start_interval_bignum instead" - )] - pub fn validity_start_interval(&self) -> Result, JsError> { - match self.validity_start_interval.clone() { - Some(interval) => match interval.try_into() { - Ok(internal32) => Ok(Some(internal32)), - Err(err) => Err(err), - }, - None => Ok(None), - } - } - - pub fn set_mint(&mut self, mint: &Mint) { - self.mint = Some(mint.clone()) - } - - pub fn mint(&self) -> Option { - self.mint.clone() - } - - /// This function returns the mint value of the transaction - /// Use `.mint()` instead. - #[deprecated(since = "10.0.0", note = "Weird naming. Use `.mint()`")] - pub fn multiassets(&self) -> Option { - self.mint() - } - - pub fn set_reference_inputs(&mut self, reference_inputs: &TransactionInputs) { - self.reference_inputs = Some(reference_inputs.clone()) - } - - pub fn reference_inputs(&self) -> Option { - self.reference_inputs.clone() - } - - pub fn set_script_data_hash(&mut self, script_data_hash: &ScriptDataHash) { - self.script_data_hash = Some(script_data_hash.clone()) - } - - pub fn script_data_hash(&self) -> Option { - self.script_data_hash.clone() - } - - pub fn set_collateral(&mut self, collateral: &TransactionInputs) { - self.collateral = Some(collateral.clone()) - } - - pub fn collateral(&self) -> Option { - self.collateral.clone() - } - - pub fn set_required_signers(&mut self, required_signers: &RequiredSigners) { - self.required_signers = Some(required_signers.clone()) - } - - pub fn required_signers(&self) -> Option { - self.required_signers.clone() - } - - pub fn set_network_id(&mut self, network_id: &NetworkId) { - self.network_id = Some(network_id.clone()) - } - - pub fn network_id(&self) -> Option { - self.network_id.clone() - } - - pub fn set_collateral_return(&mut self, collateral_return: &TransactionOutput) { - self.collateral_return = Some(collateral_return.clone()); - } - - pub fn collateral_return(&self) -> Option { - self.collateral_return.clone() - } - - pub fn set_total_collateral(&mut self, total_collateral: &Coin) { - self.total_collateral = Some(total_collateral.clone()); - } - - pub fn total_collateral(&self) -> Option { - self.total_collateral.clone() - } - - /// !!! DEPRECATED !!! - /// This constructor uses outdated slot number format for the ttl value. - /// Use `.new_tx_body` and then `.set_ttl` instead - #[deprecated( - since = "10.1.0", - note = "Underlying value capacity of ttl (BigNum u64) bigger then Slot32. Use new_tx_body instead." - )] - pub fn new( - inputs: &TransactionInputs, - outputs: &TransactionOutputs, - fee: &Coin, - ttl: Option, - ) -> Self { - let mut tx = Self::new_tx_body(inputs, outputs, fee); - if let Some(slot32) = ttl { - tx.set_ttl(&to_bignum(slot32 as u64)); - } - tx - } - - /// Returns a new TransactionBody. - /// In the new version of "new" we removed optional ttl for support it by wasm_bingen. - /// Your can use "set_ttl" and "remove_ttl" to set a new value for ttl or set it as None. - pub fn new_tx_body( - inputs: &TransactionInputs, - outputs: &TransactionOutputs, - fee: &Coin, - ) -> Self { - Self { - inputs: inputs.clone(), - outputs: outputs.clone(), - fee: fee.clone(), - ttl: None, - certs: None, - withdrawals: None, - update: None, - auxiliary_data_hash: None, - validity_start_interval: None, - mint: None, - script_data_hash: None, - collateral: None, - required_signers: None, - network_id: None, - collateral_return: None, - total_collateral: None, - reference_inputs: None, + DataCost { + coins_per_byte: coins_per_byte.clone() } } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct TransactionInput { - transaction_id: TransactionHash, - index: TransactionIndex, -} - -impl_to_from!(TransactionInput); - -#[wasm_bindgen] -impl TransactionInput { - pub fn transaction_id(&self) -> TransactionHash { - self.transaction_id.clone() - } - pub fn index(&self) -> TransactionIndex { - self.index.clone() - } - - pub fn new(transaction_id: &TransactionHash, index: TransactionIndex) -> Self { - Self { - transaction_id: transaction_id.clone(), - index: index, - } + pub fn coins_per_byte(&self) -> Coin { + self.coins_per_byte.clone() } } #[wasm_bindgen] -#[derive( - Debug, Clone, Eq, Ord, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] +#[derive(Debug, Clone, Eq, Ord, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema)] pub struct TransactionOutput { address: Address, amount: Value, @@ -695,733 +323,100 @@ impl PartialEq for TransactionOutput { } } +type Port = u16; + #[wasm_bindgen] #[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, )] -pub struct StakeRegistration { - stake_credential: StakeCredential, -} +pub struct Ipv4([u8; 4]); -impl_to_from!(StakeRegistration); +impl_to_from!(Ipv4); #[wasm_bindgen] -impl StakeRegistration { - pub fn stake_credential(&self) -> StakeCredential { - self.stake_credential.clone() +impl Ipv4 { + pub fn new(data: Vec) -> Result { + Self::new_impl(data).map_err(|e| JsError::from_str(&e.to_string())) } - pub fn new(stake_credential: &StakeCredential) -> Self { - Self { - stake_credential: stake_credential.clone(), - } + pub(crate) fn new_impl(data: Vec) -> Result { + data.as_slice().try_into().map(Self).map_err(|_e| { + let cbor_error = cbor_event::Error::WrongLen( + 4, + cbor_event::Len::Len(data.len() as u64), + "Ipv4 address length", + ); + DeserializeError::new("Ipv4", DeserializeFailure::CBOR(cbor_error)) + }) + } + + pub fn ip(&self) -> Vec { + self.0.to_vec() } } #[wasm_bindgen] #[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, )] -pub struct StakeDeregistration { - pub(crate) stake_credential: StakeCredential, -} +pub struct Ipv6([u8; 16]); -impl_to_from!(StakeDeregistration); +impl_to_from!(Ipv6); #[wasm_bindgen] -impl StakeDeregistration { - pub fn stake_credential(&self) -> StakeCredential { - self.stake_credential.clone() +impl Ipv6 { + pub fn new(data: Vec) -> Result { + Self::new_impl(data).map_err(|e| JsError::from_str(&e.to_string())) } - pub fn new(stake_credential: &StakeCredential) -> Self { - Self { - stake_credential: stake_credential.clone(), - } + pub(crate) fn new_impl(data: Vec) -> Result { + data.as_slice().try_into().map(Self).map_err(|_e| { + let cbor_error = cbor_event::Error::WrongLen( + 16, + cbor_event::Len::Len(data.len() as u64), + "Ipv6 address length", + ); + DeserializeError::new("Ipv6", DeserializeFailure::CBOR(cbor_error)) + }) } - pub fn has_script_credentials(&self) -> bool { - self.stake_credential.has_script_hash() + pub fn ip(&self) -> Vec { + self.0.to_vec() } } +static URL_MAX_LEN: usize = 128; + #[wasm_bindgen] #[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct StakeDelegation { - stake_credential: StakeCredential, - pool_keyhash: Ed25519KeyHash, -} - -impl_to_from!(StakeDelegation); - -#[wasm_bindgen] -impl StakeDelegation { - pub fn stake_credential(&self) -> StakeCredential { - self.stake_credential.clone() - } - - pub fn pool_keyhash(&self) -> Ed25519KeyHash { - self.pool_keyhash.clone() - } - - pub fn new(stake_credential: &StakeCredential, pool_keyhash: &Ed25519KeyHash) -> Self { - Self { - stake_credential: stake_credential.clone(), - pool_keyhash: pool_keyhash.clone(), - } - } - - pub fn has_script_credentials(&self) -> bool { - self.stake_credential.has_script_hash() - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct Ed25519KeyHashes(pub(crate) Vec); - -impl_to_from!(Ed25519KeyHashes); - -#[wasm_bindgen] -impl Ed25519KeyHashes { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> Ed25519KeyHash { - self.0[index].clone() - } - - pub fn add(&mut self, elem: &Ed25519KeyHash) { - self.0.push(elem.clone()); - } - - pub fn to_option(&self) -> Option { - if self.len() > 0 { - Some(self.clone()) - } else { - None - } - } -} - -impl IntoIterator for Ed25519KeyHashes { - type Item = Ed25519KeyHash; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct Relays(Vec); - -impl_to_from!(Relays); - -#[wasm_bindgen] -impl Relays { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> Relay { - self.0[index].clone() - } - - pub fn add(&mut self, elem: &Relay) { - self.0.push(elem.clone()); - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct PoolParams { - operator: Ed25519KeyHash, - vrf_keyhash: VRFKeyHash, - pledge: Coin, - cost: Coin, - margin: UnitInterval, - reward_account: RewardAddress, - pool_owners: Ed25519KeyHashes, - relays: Relays, - pool_metadata: Option, -} - -impl_to_from!(PoolParams); - -#[wasm_bindgen] -impl PoolParams { - pub fn operator(&self) -> Ed25519KeyHash { - self.operator.clone() - } - - pub fn vrf_keyhash(&self) -> VRFKeyHash { - self.vrf_keyhash.clone() - } - - pub fn pledge(&self) -> Coin { - self.pledge.clone() - } - - pub fn cost(&self) -> Coin { - self.cost.clone() - } - - pub fn margin(&self) -> UnitInterval { - self.margin.clone() - } - - pub fn reward_account(&self) -> RewardAddress { - self.reward_account.clone() - } - - pub fn pool_owners(&self) -> Ed25519KeyHashes { - self.pool_owners.clone() - } - - pub fn relays(&self) -> Relays { - self.relays.clone() - } - - pub fn pool_metadata(&self) -> Option { - self.pool_metadata.clone() - } - - pub fn new( - operator: &Ed25519KeyHash, - vrf_keyhash: &VRFKeyHash, - pledge: &Coin, - cost: &Coin, - margin: &UnitInterval, - reward_account: &RewardAddress, - pool_owners: &Ed25519KeyHashes, - relays: &Relays, - pool_metadata: Option, - ) -> Self { - Self { - operator: operator.clone(), - vrf_keyhash: vrf_keyhash.clone(), - pledge: pledge.clone(), - cost: cost.clone(), - margin: margin.clone(), - reward_account: reward_account.clone(), - pool_owners: pool_owners.clone(), - relays: relays.clone(), - pool_metadata: pool_metadata.clone(), - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct PoolRegistration { - pool_params: PoolParams, -} - -impl_to_from!(PoolRegistration); - -#[wasm_bindgen] -impl PoolRegistration { - pub fn pool_params(&self) -> PoolParams { - self.pool_params.clone() - } - - pub fn new(pool_params: &PoolParams) -> Self { - Self { - pool_params: pool_params.clone(), - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct PoolRetirement { - pool_keyhash: Ed25519KeyHash, - epoch: Epoch, -} - -impl_to_from!(PoolRetirement); - -#[wasm_bindgen] -impl PoolRetirement { - pub fn pool_keyhash(&self) -> Ed25519KeyHash { - self.pool_keyhash.clone() - } - - pub fn epoch(&self) -> Epoch { - self.epoch.clone() - } - - pub fn new(pool_keyhash: &Ed25519KeyHash, epoch: Epoch) -> Self { - Self { - pool_keyhash: pool_keyhash.clone(), - epoch: epoch, - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct GenesisKeyDelegation { - genesishash: GenesisHash, - genesis_delegate_hash: GenesisDelegateHash, - vrf_keyhash: VRFKeyHash, -} - -impl_to_from!(GenesisKeyDelegation); - -#[wasm_bindgen] -impl GenesisKeyDelegation { - pub fn genesishash(&self) -> GenesisHash { - self.genesishash.clone() - } - - pub fn genesis_delegate_hash(&self) -> GenesisDelegateHash { - self.genesis_delegate_hash.clone() - } - - pub fn vrf_keyhash(&self) -> VRFKeyHash { - self.vrf_keyhash.clone() - } - - pub fn new( - genesishash: &GenesisHash, - genesis_delegate_hash: &GenesisDelegateHash, - vrf_keyhash: &VRFKeyHash, - ) -> Self { - Self { - genesishash: genesishash.clone(), - genesis_delegate_hash: genesis_delegate_hash.clone(), - vrf_keyhash: vrf_keyhash.clone(), - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct MoveInstantaneousRewardsCert { - move_instantaneous_reward: MoveInstantaneousReward, -} - -impl_to_from!(MoveInstantaneousRewardsCert); - -#[wasm_bindgen] -impl MoveInstantaneousRewardsCert { - pub fn move_instantaneous_reward(&self) -> MoveInstantaneousReward { - self.move_instantaneous_reward.clone() - } - - pub fn new(move_instantaneous_reward: &MoveInstantaneousReward) -> Self { - Self { - move_instantaneous_reward: move_instantaneous_reward.clone(), - } - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum CertificateKind { - StakeRegistration, - StakeDeregistration, - StakeDelegation, - PoolRegistration, - PoolRetirement, - GenesisKeyDelegation, - MoveInstantaneousRewardsCert, -} - -#[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub enum CertificateEnum { - StakeRegistration(StakeRegistration), - StakeDeregistration(StakeDeregistration), - StakeDelegation(StakeDelegation), - PoolRegistration(PoolRegistration), - PoolRetirement(PoolRetirement), - GenesisKeyDelegation(GenesisKeyDelegation), - MoveInstantaneousRewardsCert(MoveInstantaneousRewardsCert), -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct Certificate(CertificateEnum); - -impl_to_from!(Certificate); - -#[wasm_bindgen] -impl Certificate { - pub fn new_stake_registration(stake_registration: &StakeRegistration) -> Self { - Self(CertificateEnum::StakeRegistration( - stake_registration.clone(), - )) - } - - pub fn new_stake_deregistration(stake_deregistration: &StakeDeregistration) -> Self { - Self(CertificateEnum::StakeDeregistration( - stake_deregistration.clone(), - )) - } - - pub fn new_stake_delegation(stake_delegation: &StakeDelegation) -> Self { - Self(CertificateEnum::StakeDelegation(stake_delegation.clone())) - } - - pub fn new_pool_registration(pool_registration: &PoolRegistration) -> Self { - Self(CertificateEnum::PoolRegistration(pool_registration.clone())) - } - - pub fn new_pool_retirement(pool_retirement: &PoolRetirement) -> Self { - Self(CertificateEnum::PoolRetirement(pool_retirement.clone())) - } - - pub fn new_genesis_key_delegation(genesis_key_delegation: &GenesisKeyDelegation) -> Self { - Self(CertificateEnum::GenesisKeyDelegation( - genesis_key_delegation.clone(), - )) - } - - pub fn new_move_instantaneous_rewards_cert( - move_instantaneous_rewards_cert: &MoveInstantaneousRewardsCert, - ) -> Self { - Self(CertificateEnum::MoveInstantaneousRewardsCert( - move_instantaneous_rewards_cert.clone(), - )) - } - - pub fn kind(&self) -> CertificateKind { - match &self.0 { - CertificateEnum::StakeRegistration(_) => CertificateKind::StakeRegistration, - CertificateEnum::StakeDeregistration(_) => CertificateKind::StakeDeregistration, - CertificateEnum::StakeDelegation(_) => CertificateKind::StakeDelegation, - CertificateEnum::PoolRegistration(_) => CertificateKind::PoolRegistration, - CertificateEnum::PoolRetirement(_) => CertificateKind::PoolRetirement, - CertificateEnum::GenesisKeyDelegation(_) => CertificateKind::GenesisKeyDelegation, - CertificateEnum::MoveInstantaneousRewardsCert(_) => { - CertificateKind::MoveInstantaneousRewardsCert - } - } - } - - pub fn as_stake_registration(&self) -> Option { - match &self.0 { - CertificateEnum::StakeRegistration(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_stake_deregistration(&self) -> Option { - match &self.0 { - CertificateEnum::StakeDeregistration(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_stake_delegation(&self) -> Option { - match &self.0 { - CertificateEnum::StakeDelegation(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_pool_registration(&self) -> Option { - match &self.0 { - CertificateEnum::PoolRegistration(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_pool_retirement(&self) -> Option { - match &self.0 { - CertificateEnum::PoolRetirement(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_genesis_key_delegation(&self) -> Option { - match &self.0 { - CertificateEnum::GenesisKeyDelegation(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_move_instantaneous_rewards_cert(&self) -> Option { - match &self.0 { - CertificateEnum::MoveInstantaneousRewardsCert(x) => Some(x.clone()), - _ => None, - } - } - - pub fn has_required_script_witness(&self) -> bool { - match &self.0 { - CertificateEnum::StakeDeregistration(x) => x.has_script_credentials(), - CertificateEnum::StakeDelegation(x) => x.has_script_credentials(), - _ => false, - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, - Copy, - Debug, - Hash, - Eq, - Ord, - PartialEq, - PartialOrd, - serde::Serialize, - serde::Deserialize, - JsonSchema, -)] -pub enum MIRPot { - Reserves, - Treasury, -} - -#[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub enum MIREnum { - ToOtherPot(Coin), - ToStakeCredentials(MIRToStakeCredentials), -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub enum MIRKind { - ToOtherPot, - ToStakeCredentials, -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] -pub struct MIRToStakeCredentials { - rewards: linked_hash_map::LinkedHashMap, -} - -impl_to_from!(MIRToStakeCredentials); - -#[wasm_bindgen] -impl MIRToStakeCredentials { - pub fn new() -> Self { - Self { - rewards: linked_hash_map::LinkedHashMap::new(), - } - } - - pub fn len(&self) -> usize { - self.rewards.len() - } - - pub fn insert(&mut self, cred: &StakeCredential, delta: &DeltaCoin) -> Option { - self.rewards.insert(cred.clone(), delta.clone()) - } - - pub fn get(&self, cred: &StakeCredential) -> Option { - self.rewards.get(cred).map(|v| v.clone()) - } - - pub fn keys(&self) -> StakeCredentials { - StakeCredentials( - self.rewards - .iter() - .map(|(k, _v)| k.clone()) - .collect::>(), - ) - } -} - -impl serde::Serialize for MIRToStakeCredentials { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let map = self - .rewards - .iter() - .collect::>(); - map.serialize(serializer) - } -} - -impl<'de> serde::de::Deserialize<'de> for MIRToStakeCredentials { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let map = as serde::de::Deserialize>::deserialize( - deserializer, - )?; - Ok(Self { - rewards: map.into_iter().collect(), - }) - } -} - -impl JsonSchema for MIRToStakeCredentials { - fn schema_name() -> String { - String::from("MIRToStakeCredentials") - } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - std::collections::BTreeMap::::json_schema(gen) - } - fn is_referenceable() -> bool { - std::collections::BTreeMap::::is_referenceable() - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct MoveInstantaneousReward { - pot: MIRPot, - variant: MIREnum, -} - -impl_to_from!(MoveInstantaneousReward); - -#[wasm_bindgen] -impl MoveInstantaneousReward { - pub fn new_to_other_pot(pot: MIRPot, amount: &Coin) -> Self { - Self { - pot, - variant: MIREnum::ToOtherPot(amount.clone()), - } - } - - pub fn new_to_stake_creds(pot: MIRPot, amounts: &MIRToStakeCredentials) -> Self { - Self { - pot, - variant: MIREnum::ToStakeCredentials(amounts.clone()), - } - } - - pub fn pot(&self) -> MIRPot { - self.pot - } - - pub fn kind(&self) -> MIRKind { - match &self.variant { - MIREnum::ToOtherPot(_) => MIRKind::ToOtherPot, - MIREnum::ToStakeCredentials(_) => MIRKind::ToStakeCredentials, - } - } - - pub fn as_to_other_pot(&self) -> Option { - match &self.variant { - MIREnum::ToOtherPot(amount) => Some(amount.clone()), - MIREnum::ToStakeCredentials(_) => None, - } - } - - pub fn as_to_stake_creds(&self) -> Option { - match &self.variant { - MIREnum::ToOtherPot(_) => None, - MIREnum::ToStakeCredentials(amounts) => Some(amounts.clone()), - } - } -} - -type Port = u16; - -#[wasm_bindgen] -#[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct Ipv4([u8; 4]); - -impl_to_from!(Ipv4); - -#[wasm_bindgen] -impl Ipv4 { - pub fn new(data: Vec) -> Result { - Self::new_impl(data).map_err(|e| JsError::from_str(&e.to_string())) - } - - pub(crate) fn new_impl(data: Vec) -> Result { - data.as_slice().try_into().map(Self).map_err(|_e| { - let cbor_error = cbor_event::Error::WrongLen( - 4, - cbor_event::Len::Len(data.len() as u64), - "Ipv4 address length", - ); - DeserializeError::new("Ipv4", DeserializeFailure::CBOR(cbor_error)) - }) - } - - pub fn ip(&self) -> Vec { - self.0.to_vec() - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct Ipv6([u8; 16]); - -impl_to_from!(Ipv6); - -#[wasm_bindgen] -impl Ipv6 { - pub fn new(data: Vec) -> Result { - Self::new_impl(data).map_err(|e| JsError::from_str(&e.to_string())) - } - - pub(crate) fn new_impl(data: Vec) -> Result { - data.as_slice().try_into().map(Self).map_err(|_e| { - let cbor_error = cbor_event::Error::WrongLen( - 16, - cbor_event::Len::Len(data.len() as u64), - "Ipv6 address length", - ); - DeserializeError::new("Ipv6", DeserializeFailure::CBOR(cbor_error)) - }) - } - - pub fn ip(&self) -> Vec { - self.0.to_vec() - } -} - -static URL_MAX_LEN: usize = 64; - -#[wasm_bindgen] -#[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, )] pub struct URL(String); @@ -1453,11 +448,20 @@ impl URL { } } -static DNS_NAME_MAX_LEN: usize = 64; +static DNS_NAME_MAX_LEN: usize = 128; #[wasm_bindgen] #[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, )] pub struct DNSRecordAorAAAA(String); @@ -1491,7 +495,16 @@ impl DNSRecordAorAAAA { #[wasm_bindgen] #[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, )] pub struct DNSRecordSRV(String); @@ -1525,7 +538,16 @@ impl DNSRecordSRV { #[wasm_bindgen] #[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, )] pub struct SingleHostAddr { port: Option, @@ -1560,7 +582,16 @@ impl SingleHostAddr { #[wasm_bindgen] #[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, )] pub struct SingleHostName { port: Option, @@ -1589,7 +620,16 @@ impl SingleHostName { #[wasm_bindgen] #[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, )] pub struct MultiHostName { dns_name: DNSRecordSRV, @@ -1619,7 +659,16 @@ pub enum RelayKind { } #[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, )] pub enum RelayEnum { SingleHostAddr(SingleHostAddr), @@ -1629,7 +678,16 @@ pub enum RelayEnum { #[wasm_bindgen] #[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, )] pub struct Relay(RelayEnum); @@ -1681,7 +739,16 @@ impl Relay { #[wasm_bindgen] #[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, )] pub struct PoolMetadata { url: URL, @@ -1712,34 +779,7 @@ impl PoolMetadata { #[derive( Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, )] -pub struct StakeCredentials(Vec); - -impl_to_from!(StakeCredentials); - -#[wasm_bindgen] -impl StakeCredentials { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> StakeCredential { - self.0[index].clone() - } - - pub fn add(&mut self, elem: &StakeCredential) { - self.0.push(elem.clone()); - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct RewardAddresses(Vec); +pub struct RewardAddresses(pub(crate) Vec); impl_to_from!(RewardAddresses); @@ -1764,14 +804,20 @@ impl RewardAddresses { #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct Withdrawals(linked_hash_map::LinkedHashMap); +pub struct Withdrawals(LinkedHashMap); impl_to_from!(Withdrawals); +impl NoneOrEmpty for Withdrawals { + fn is_none_or_empty(&self) -> bool { + self.0.is_empty() + } +} + #[wasm_bindgen] impl Withdrawals { pub fn new() -> Self { - Self(linked_hash_map::LinkedHashMap::new()) + Self(LinkedHashMap::new()) } pub fn len(&self) -> usize { @@ -1825,341 +871,8 @@ impl JsonSchema for Withdrawals { fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { std::collections::BTreeMap::::json_schema(gen) } - fn is_referenceable() -> bool { - std::collections::BTreeMap::::is_referenceable() - } -} - -#[wasm_bindgen] -#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize, JsonSchema)] -pub struct TransactionWitnessSet { - vkeys: Option, - native_scripts: Option, - bootstraps: Option, - plutus_scripts: Option, - plutus_data: Option, - redeemers: Option, -} - -impl_to_from!(TransactionWitnessSet); - -#[wasm_bindgen] -impl TransactionWitnessSet { - pub fn set_vkeys(&mut self, vkeys: &Vkeywitnesses) { - self.vkeys = Some(vkeys.clone()) - } - - pub fn vkeys(&self) -> Option { - self.vkeys.clone() - } - - pub fn set_native_scripts(&mut self, native_scripts: &NativeScripts) { - self.native_scripts = Some(native_scripts.clone()) - } - - pub fn native_scripts(&self) -> Option { - self.native_scripts.clone() - } - - pub fn set_bootstraps(&mut self, bootstraps: &BootstrapWitnesses) { - self.bootstraps = Some(bootstraps.clone()) - } - - pub fn bootstraps(&self) -> Option { - self.bootstraps.clone() - } - - pub fn set_plutus_scripts(&mut self, plutus_scripts: &PlutusScripts) { - self.plutus_scripts = Some(plutus_scripts.clone()) - } - - pub fn plutus_scripts(&self) -> Option { - self.plutus_scripts.clone() - } - - pub fn set_plutus_data(&mut self, plutus_data: &PlutusList) { - self.plutus_data = Some(plutus_data.clone()) - } - - pub fn plutus_data(&self) -> Option { - self.plutus_data.clone() - } - - pub fn set_redeemers(&mut self, redeemers: &Redeemers) { - self.redeemers = Some(redeemers.clone()) - } - - pub fn redeemers(&self) -> Option { - self.redeemers.clone() - } - - pub fn new() -> Self { - Self { - vkeys: None, - native_scripts: None, - bootstraps: None, - plutus_scripts: None, - plutus_data: None, - redeemers: None, - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct ScriptPubkey { - addr_keyhash: Ed25519KeyHash, -} - -impl_to_from!(ScriptPubkey); - -#[wasm_bindgen] -impl ScriptPubkey { - pub fn addr_keyhash(&self) -> Ed25519KeyHash { - self.addr_keyhash.clone() - } - - pub fn new(addr_keyhash: &Ed25519KeyHash) -> Self { - Self { - addr_keyhash: addr_keyhash.clone(), - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct ScriptAll { - native_scripts: NativeScripts, -} - -impl_to_from!(ScriptAll); - -#[wasm_bindgen] -impl ScriptAll { - pub fn native_scripts(&self) -> NativeScripts { - self.native_scripts.clone() - } - - pub fn new(native_scripts: &NativeScripts) -> Self { - Self { - native_scripts: native_scripts.clone(), - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct ScriptAny { - native_scripts: NativeScripts, -} - -impl_to_from!(ScriptAny); - -#[wasm_bindgen] -impl ScriptAny { - pub fn native_scripts(&self) -> NativeScripts { - self.native_scripts.clone() - } - - pub fn new(native_scripts: &NativeScripts) -> Self { - Self { - native_scripts: native_scripts.clone(), - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct ScriptNOfK { - n: u32, - native_scripts: NativeScripts, -} - -impl_to_from!(ScriptNOfK); - -#[wasm_bindgen] -impl ScriptNOfK { - pub fn n(&self) -> u32 { - self.n - } - - pub fn native_scripts(&self) -> NativeScripts { - self.native_scripts.clone() - } - - pub fn new(n: u32, native_scripts: &NativeScripts) -> Self { - Self { - n: n, - native_scripts: native_scripts.clone(), - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct TimelockStart { - slot: SlotBigNum, -} - -impl_to_from!(TimelockStart); - -#[wasm_bindgen] -impl TimelockStart { - /// !!! DEPRECATED !!! - /// Returns a Slot32 (u32) value in case the underlying original BigNum (u64) value is within the limits. - /// Otherwise will just raise an error. - /// Use `.slot_bignum` instead - #[deprecated( - since = "10.1.0", - note = "Possible boundary error. Use slot_bignum instead" - )] - pub fn slot(&self) -> Result { - self.slot.try_into() - } - - pub fn slot_bignum(&self) -> SlotBigNum { - self.slot - } - - /// !!! DEPRECATED !!! - /// This constructor uses outdated slot number format. - /// Use `.new_timelockstart` instead. - #[deprecated( - since = "10.1.0", - note = "Underlying value capacity (BigNum u64) bigger then Slot32. Use new_bignum instead." - )] - pub fn new(slot: Slot32) -> Self { - Self { slot: slot.into() } - } - - pub fn new_timelockstart(slot: &SlotBigNum) -> Self { - Self { slot: slot.clone() } - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct TimelockExpiry { - slot: SlotBigNum, -} - -impl_to_from!(TimelockExpiry); - -#[wasm_bindgen] -impl TimelockExpiry { - pub fn slot(&self) -> Result { - self.slot.try_into() - } - - pub fn slot_bignum(&self) -> SlotBigNum { - self.slot - } - - /// !!! DEPRECATED !!! - /// This constructor uses outdated slot number format. - /// Use `.new_timelockexpiry` instead - #[deprecated( - since = "10.1.0", - note = "Underlying value capacity (BigNum u64) bigger then Slot32. Use new_bignum instead." - )] - pub fn new(slot: Slot32) -> Self { - Self { - slot: (slot.into()), - } - } - - pub fn new_timelockexpiry(slot: &SlotBigNum) -> Self { - Self { slot: slot.clone() } - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum NativeScriptKind { - ScriptPubkey, - ScriptAll, - ScriptAny, - ScriptNOfK, - TimelockStart, - TimelockExpiry, -} - -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub enum NativeScriptEnum { - ScriptPubkey(ScriptPubkey), - ScriptAll(ScriptAll), - ScriptAny(ScriptAny), - ScriptNOfK(ScriptNOfK), - TimelockStart(TimelockStart), - TimelockExpiry(TimelockExpiry), -} - -#[derive( - Debug, Clone, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub enum ScriptRefEnum { - NativeScript(NativeScript), - PlutusScript(PlutusScript), -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct ScriptRef(ScriptRefEnum); - -impl_to_from!(ScriptRef); - -#[wasm_bindgen] -impl ScriptRef { - pub fn new_native_script(native_script: &NativeScript) -> Self { - Self(ScriptRefEnum::NativeScript(native_script.clone())) - } - - pub fn new_plutus_script(plutus_script: &PlutusScript) -> Self { - Self(ScriptRefEnum::PlutusScript(plutus_script.clone())) - } - - pub fn is_native_script(&self) -> bool { - match &self.0 { - ScriptRefEnum::NativeScript(_) => true, - _ => false, - } - } - - pub fn is_plutus_script(&self) -> bool { - match &self.0 { - ScriptRefEnum::PlutusScript(_) => true, - _ => false, - } - } - - pub fn native_script(&self) -> Option { - match &self.0 { - ScriptRefEnum::NativeScript(native_script) => Some(native_script.clone()), - _ => None, - } - } - - pub fn plutus_script(&self) -> Option { - match &self.0 { - ScriptRefEnum::PlutusScript(plutus_script) => Some(plutus_script.clone()), - _ => None, - } + fn is_referenceable() -> bool { + std::collections::BTreeMap::::is_referenceable() } } @@ -2172,12 +885,11 @@ pub enum DataOption { } #[wasm_bindgen] -#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd )] +#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] pub struct OutputDatum(pub(crate) DataOption); #[wasm_bindgen] impl OutputDatum { - pub fn new_data_hash(data_hash: &DataHash) -> Self { Self(DataOption::DataHash(data_hash.clone())) } @@ -2201,14 +913,6 @@ impl OutputDatum { } } -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct NativeScript(NativeScriptEnum); - -impl_to_from!(NativeScript); - /// Each new language uses a different namespace for hashing its script /// This is because you could have a language where the same bytes have different semantics /// So this avoids scripts in different languages mapping to the same hash @@ -2219,145 +923,7 @@ pub enum ScriptHashNamespace { NativeScript = 0, PlutusScript = 1, PlutusScriptV2 = 2, -} - -#[wasm_bindgen] -impl NativeScript { - pub fn hash(&self) -> ScriptHash { - let mut bytes = Vec::with_capacity(self.to_bytes().len() + 1); - bytes.extend_from_slice(&vec![ScriptHashNamespace::NativeScript as u8]); - bytes.extend_from_slice(&self.to_bytes()); - ScriptHash::from(blake2b224(bytes.as_ref())) - } - - pub fn new_script_pubkey(script_pubkey: &ScriptPubkey) -> Self { - Self(NativeScriptEnum::ScriptPubkey(script_pubkey.clone())) - } - - pub fn new_script_all(script_all: &ScriptAll) -> Self { - Self(NativeScriptEnum::ScriptAll(script_all.clone())) - } - - pub fn new_script_any(script_any: &ScriptAny) -> Self { - Self(NativeScriptEnum::ScriptAny(script_any.clone())) - } - - pub fn new_script_n_of_k(script_n_of_k: &ScriptNOfK) -> Self { - Self(NativeScriptEnum::ScriptNOfK(script_n_of_k.clone())) - } - - pub fn new_timelock_start(timelock_start: &TimelockStart) -> Self { - Self(NativeScriptEnum::TimelockStart(timelock_start.clone())) - } - - pub fn new_timelock_expiry(timelock_expiry: &TimelockExpiry) -> Self { - Self(NativeScriptEnum::TimelockExpiry(timelock_expiry.clone())) - } - - pub fn kind(&self) -> NativeScriptKind { - match &self.0 { - NativeScriptEnum::ScriptPubkey(_) => NativeScriptKind::ScriptPubkey, - NativeScriptEnum::ScriptAll(_) => NativeScriptKind::ScriptAll, - NativeScriptEnum::ScriptAny(_) => NativeScriptKind::ScriptAny, - NativeScriptEnum::ScriptNOfK(_) => NativeScriptKind::ScriptNOfK, - NativeScriptEnum::TimelockStart(_) => NativeScriptKind::TimelockStart, - NativeScriptEnum::TimelockExpiry(_) => NativeScriptKind::TimelockExpiry, - } - } - - pub fn as_script_pubkey(&self) -> Option { - match &self.0 { - NativeScriptEnum::ScriptPubkey(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_script_all(&self) -> Option { - match &self.0 { - NativeScriptEnum::ScriptAll(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_script_any(&self) -> Option { - match &self.0 { - NativeScriptEnum::ScriptAny(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_script_n_of_k(&self) -> Option { - match &self.0 { - NativeScriptEnum::ScriptNOfK(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_timelock_start(&self) -> Option { - match &self.0 { - NativeScriptEnum::TimelockStart(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_timelock_expiry(&self) -> Option { - match &self.0 { - NativeScriptEnum::TimelockExpiry(x) => Some(x.clone()), - _ => None, - } - } - - /// Returns an array of unique Ed25519KeyHashes - /// contained within this script recursively on any depth level. - /// The order of the keys in the result is not determined in any way. - pub fn get_required_signers(&self) -> Ed25519KeyHashes { - Ed25519KeyHashes( - RequiredSignersSet::from(self) - .iter() - .map(|k| k.clone()) - .collect(), - ) - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct NativeScripts(Vec); - -#[wasm_bindgen] -impl NativeScripts { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> NativeScript { - self.0[index].clone() - } - - pub fn add(&mut self, elem: &NativeScript) { - self.0.push(elem.clone()); - } -} - -impl From> for NativeScripts { - fn from(scripts: Vec) -> Self { - scripts.iter().fold(NativeScripts::new(), |mut scripts, s| { - scripts.add(s); - scripts - }) - } -} - -impl NoneOrEmpty for NativeScripts { - fn is_none_or_empty(&self) -> bool { - self.0.is_empty() - } + PlutusScriptV3 = 3, } #[wasm_bindgen] @@ -2449,7 +1015,7 @@ impl ScriptHashes { #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct ProposedProtocolParameterUpdates( - linked_hash_map::LinkedHashMap, + LinkedHashMap, ); impl serde::Serialize for ProposedProtocolParameterUpdates { @@ -2481,394 +1047,88 @@ impl JsonSchema for ProposedProtocolParameterUpdates { fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { std::collections::BTreeMap::::json_schema(gen) } - fn is_referenceable() -> bool { - std::collections::BTreeMap::::is_referenceable() - } -} - -impl_to_from!(ProposedProtocolParameterUpdates); - -#[wasm_bindgen] -impl ProposedProtocolParameterUpdates { - pub fn new() -> Self { - Self(linked_hash_map::LinkedHashMap::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn insert( - &mut self, - key: &GenesisHash, - value: &ProtocolParamUpdate, - ) -> Option { - self.0.insert(key.clone(), value.clone()) - } - - pub fn get(&self, key: &GenesisHash) -> Option { - self.0.get(key).map(|v| v.clone()) - } - - pub fn keys(&self) -> GenesisHashes { - GenesisHashes( - self.0 - .iter() - .map(|(k, _v)| k.clone()) - .collect::>(), - ) - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct ProtocolVersion { - major: u32, - minor: u32, -} - -impl_to_from!(ProtocolVersion); - -#[wasm_bindgen] -impl ProtocolVersion { - pub fn major(&self) -> u32 { - self.major - } - - pub fn minor(&self) -> u32 { - self.minor - } - - pub fn new(major: u32, minor: u32) -> Self { - Self { major, minor } - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct ProtocolParamUpdate { - minfee_a: Option, - minfee_b: Option, - max_block_body_size: Option, - max_tx_size: Option, - max_block_header_size: Option, - key_deposit: Option, - pool_deposit: Option, - max_epoch: Option, - // desired number of stake pools - n_opt: Option, - pool_pledge_influence: Option, - expansion_rate: Option, - treasury_growth_rate: Option, - // decentralization constant - d: Option, - extra_entropy: Option, - protocol_version: Option, - min_pool_cost: Option, - ada_per_utxo_byte: Option, - cost_models: Option, - execution_costs: Option, - max_tx_ex_units: Option, - max_block_ex_units: Option, - max_value_size: Option, - collateral_percentage: Option, - max_collateral_inputs: Option, -} - -impl_to_from!(ProtocolParamUpdate); - -#[wasm_bindgen] -impl ProtocolParamUpdate { - pub fn set_minfee_a(&mut self, minfee_a: &Coin) { - self.minfee_a = Some(minfee_a.clone()) - } - - pub fn minfee_a(&self) -> Option { - self.minfee_a.clone() - } - - pub fn set_minfee_b(&mut self, minfee_b: &Coin) { - self.minfee_b = Some(minfee_b.clone()) - } - - pub fn minfee_b(&self) -> Option { - self.minfee_b.clone() - } - - pub fn set_max_block_body_size(&mut self, max_block_body_size: u32) { - self.max_block_body_size = Some(max_block_body_size) - } - - pub fn max_block_body_size(&self) -> Option { - self.max_block_body_size.clone() - } - - pub fn set_max_tx_size(&mut self, max_tx_size: u32) { - self.max_tx_size = Some(max_tx_size) - } - - pub fn max_tx_size(&self) -> Option { - self.max_tx_size.clone() - } - - pub fn set_max_block_header_size(&mut self, max_block_header_size: u32) { - self.max_block_header_size = Some(max_block_header_size) - } - - pub fn max_block_header_size(&self) -> Option { - self.max_block_header_size.clone() - } - - pub fn set_key_deposit(&mut self, key_deposit: &Coin) { - self.key_deposit = Some(key_deposit.clone()) - } - - pub fn key_deposit(&self) -> Option { - self.key_deposit.clone() - } - - pub fn set_pool_deposit(&mut self, pool_deposit: &Coin) { - self.pool_deposit = Some(pool_deposit.clone()) - } - - pub fn pool_deposit(&self) -> Option { - self.pool_deposit.clone() - } - - pub fn set_max_epoch(&mut self, max_epoch: Epoch) { - self.max_epoch = Some(max_epoch.clone()) - } - - pub fn max_epoch(&self) -> Option { - self.max_epoch.clone() - } - - pub fn set_n_opt(&mut self, n_opt: u32) { - self.n_opt = Some(n_opt) - } - - pub fn n_opt(&self) -> Option { - self.n_opt.clone() - } - - pub fn set_pool_pledge_influence(&mut self, pool_pledge_influence: &Rational) { - self.pool_pledge_influence = Some(pool_pledge_influence.clone()) - } - - pub fn pool_pledge_influence(&self) -> Option { - self.pool_pledge_influence.clone() - } - - pub fn set_expansion_rate(&mut self, expansion_rate: &UnitInterval) { - self.expansion_rate = Some(expansion_rate.clone()) - } - - pub fn expansion_rate(&self) -> Option { - self.expansion_rate.clone() - } - - pub fn set_treasury_growth_rate(&mut self, treasury_growth_rate: &UnitInterval) { - self.treasury_growth_rate = Some(treasury_growth_rate.clone()) - } - - pub fn treasury_growth_rate(&self) -> Option { - self.treasury_growth_rate.clone() - } - - /// !!! DEPRECATED !!! - /// Since babbage era this param is outdated. But this param you can meet in a pre-babbage block. - #[deprecated( - since = "11.0.0", - note = "Since babbage era this param is outdated. But this param you can meet in a pre-babbage block." - )] - pub fn d(&self) -> Option { - self.d.clone() - } - - /// !!! DEPRECATED !!! - /// Since babbage era this param is outdated. But this param you can meet in a pre-babbage block. - #[deprecated( - since = "11.0.0", - note = "Since babbage era this param is outdated. But this param you can meet in a pre-babbage block." - )] - pub fn extra_entropy(&self) -> Option { - self.extra_entropy.clone() - } - - pub fn set_protocol_version(&mut self, protocol_version: &ProtocolVersion) { - self.protocol_version = Some(protocol_version.clone()) - } - - pub fn protocol_version(&self) -> Option { - self.protocol_version.clone() - } - - pub fn set_min_pool_cost(&mut self, min_pool_cost: &Coin) { - self.min_pool_cost = Some(min_pool_cost.clone()) - } - - pub fn min_pool_cost(&self) -> Option { - self.min_pool_cost.clone() - } - - pub fn set_ada_per_utxo_byte(&mut self, ada_per_utxo_byte: &Coin) { - self.ada_per_utxo_byte = Some(ada_per_utxo_byte.clone()) - } - - pub fn ada_per_utxo_byte(&self) -> Option { - self.ada_per_utxo_byte.clone() - } - - pub fn set_cost_models(&mut self, cost_models: &Costmdls) { - self.cost_models = Some(cost_models.clone()) - } - - pub fn cost_models(&self) -> Option { - self.cost_models.clone() - } - - pub fn set_execution_costs(&mut self, execution_costs: &ExUnitPrices) { - self.execution_costs = Some(execution_costs.clone()) - } - - pub fn execution_costs(&self) -> Option { - self.execution_costs.clone() - } - - pub fn set_max_tx_ex_units(&mut self, max_tx_ex_units: &ExUnits) { - self.max_tx_ex_units = Some(max_tx_ex_units.clone()) - } - - pub fn max_tx_ex_units(&self) -> Option { - self.max_tx_ex_units.clone() - } - - pub fn set_max_block_ex_units(&mut self, max_block_ex_units: &ExUnits) { - self.max_block_ex_units = Some(max_block_ex_units.clone()) - } - - pub fn max_block_ex_units(&self) -> Option { - self.max_block_ex_units.clone() - } - - pub fn set_max_value_size(&mut self, max_value_size: u32) { - self.max_value_size = Some(max_value_size.clone()) - } - - pub fn max_value_size(&self) -> Option { - self.max_value_size.clone() - } - - pub fn set_collateral_percentage(&mut self, collateral_percentage: u32) { - self.collateral_percentage = Some(collateral_percentage) - } - - pub fn collateral_percentage(&self) -> Option { - self.collateral_percentage.clone() - } - - pub fn set_max_collateral_inputs(&mut self, max_collateral_inputs: u32) { - self.max_collateral_inputs = Some(max_collateral_inputs) - } - - pub fn max_collateral_inputs(&self) -> Option { - self.max_collateral_inputs.clone() - } - - pub fn new() -> Self { - Self { - minfee_a: None, - minfee_b: None, - max_block_body_size: None, - max_tx_size: None, - max_block_header_size: None, - key_deposit: None, - pool_deposit: None, - max_epoch: None, - n_opt: None, - pool_pledge_influence: None, - expansion_rate: None, - treasury_growth_rate: None, - d: None, - extra_entropy: None, - protocol_version: None, - min_pool_cost: None, - ada_per_utxo_byte: None, - cost_models: None, - execution_costs: None, - max_tx_ex_units: None, - max_block_ex_units: None, - max_value_size: None, - collateral_percentage: None, - max_collateral_inputs: None, - } + fn is_referenceable() -> bool { + std::collections::BTreeMap::::is_referenceable() } } -#[wasm_bindgen] -#[derive(Clone, serde::Serialize, serde::Deserialize, JsonSchema)] -pub struct TransactionBodies(pub(crate) Vec); - -impl_to_from!(TransactionBodies); +impl_to_from!(ProposedProtocolParameterUpdates); #[wasm_bindgen] -impl TransactionBodies { +impl ProposedProtocolParameterUpdates { pub fn new() -> Self { - Self(Vec::new()) + Self(LinkedHashMap::new()) } pub fn len(&self) -> usize { self.0.len() } - pub fn get(&self, index: usize) -> TransactionBody { - self.0[index].clone() + pub fn insert( + &mut self, + key: &GenesisHash, + value: &ProtocolParamUpdate, + ) -> Option { + self.0.insert(key.clone(), value.clone()) } - pub fn add(&mut self, elem: &TransactionBody) { - self.0.push(elem.clone()); + pub fn get(&self, key: &GenesisHash) -> Option { + self.0.get(key).map(|v| v.clone()) + } + + pub fn keys(&self) -> GenesisHashes { + GenesisHashes( + self.0 + .iter() + .map(|(k, _v)| k.clone()) + .collect::>(), + ) } } #[wasm_bindgen] -#[derive(Clone, serde::Serialize, serde::Deserialize, JsonSchema)] -pub struct TransactionWitnessSets(Vec); +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct ProtocolVersion { + major: u32, + minor: u32, +} -impl_to_from!(TransactionWitnessSets); +impl_to_from!(ProtocolVersion); #[wasm_bindgen] -impl TransactionWitnessSets { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() +impl ProtocolVersion { + pub fn major(&self) -> u32 { + self.major } - pub fn get(&self, index: usize) -> TransactionWitnessSet { - self.0[index].clone() + pub fn minor(&self) -> u32 { + self.minor } - pub fn add(&mut self, elem: &TransactionWitnessSet) { - self.0.push(elem.clone()); + pub fn new(major: u32, minor: u32) -> Self { + Self { major, minor } } } -pub type TransactionIndexes = Vec; - #[wasm_bindgen] #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct AuxiliaryDataSet(linked_hash_map::LinkedHashMap); +pub struct AuxiliaryDataSet(LinkedHashMap); #[wasm_bindgen] impl AuxiliaryDataSet { pub fn new() -> Self { - Self(linked_hash_map::LinkedHashMap::new()) + Self(LinkedHashMap::new()) } pub fn len(&self) -> usize { @@ -2929,303 +1189,6 @@ impl JsonSchema for AuxiliaryDataSet { } } -#[wasm_bindgen] -#[derive(Clone, serde::Serialize, serde::Deserialize, JsonSchema)] -pub struct Block { - header: Header, - transaction_bodies: TransactionBodies, - transaction_witness_sets: TransactionWitnessSets, - auxiliary_data_set: AuxiliaryDataSet, - invalid_transactions: TransactionIndexes, -} - -impl_to_from!(Block); - -#[wasm_bindgen] -impl Block { - pub fn header(&self) -> Header { - self.header.clone() - } - - pub fn transaction_bodies(&self) -> TransactionBodies { - self.transaction_bodies.clone() - } - - pub fn transaction_witness_sets(&self) -> TransactionWitnessSets { - self.transaction_witness_sets.clone() - } - - pub fn auxiliary_data_set(&self) -> AuxiliaryDataSet { - self.auxiliary_data_set.clone() - } - - pub fn invalid_transactions(&self) -> TransactionIndexes { - self.invalid_transactions.clone() - } - - pub fn new( - header: &Header, - transaction_bodies: &TransactionBodies, - transaction_witness_sets: &TransactionWitnessSets, - auxiliary_data_set: &AuxiliaryDataSet, - invalid_transactions: TransactionIndexes, - ) -> Self { - Self { - header: header.clone(), - transaction_bodies: transaction_bodies.clone(), - transaction_witness_sets: transaction_witness_sets.clone(), - auxiliary_data_set: auxiliary_data_set.clone(), - invalid_transactions: invalid_transactions, - } - } -} - -#[wasm_bindgen] -#[derive(Clone, serde::Serialize, serde::Deserialize, JsonSchema)] -pub struct Header { - header_body: HeaderBody, - body_signature: KESSignature, -} - -impl_to_from!(Header); - -#[wasm_bindgen] -impl Header { - pub fn header_body(&self) -> HeaderBody { - self.header_body.clone() - } - - pub fn body_signature(&self) -> KESSignature { - self.body_signature.clone() - } - - pub fn new(header_body: &HeaderBody, body_signature: &KESSignature) -> Self { - Self { - header_body: header_body.clone(), - body_signature: body_signature.clone(), - } - } -} - -#[wasm_bindgen] -#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize, JsonSchema)] -pub struct OperationalCert { - hot_vkey: KESVKey, - sequence_number: u32, - kes_period: u32, - sigma: Ed25519Signature, -} - -impl_to_from!(OperationalCert); - -#[wasm_bindgen] -impl OperationalCert { - pub fn hot_vkey(&self) -> KESVKey { - self.hot_vkey.clone() - } - - pub fn sequence_number(&self) -> u32 { - self.sequence_number.clone() - } - - pub fn kes_period(&self) -> u32 { - self.kes_period.clone() - } - - pub fn sigma(&self) -> Ed25519Signature { - self.sigma.clone() - } - - pub fn new( - hot_vkey: &KESVKey, - sequence_number: u32, - kes_period: u32, - sigma: &Ed25519Signature, - ) -> Self { - Self { - hot_vkey: hot_vkey.clone(), - sequence_number: sequence_number, - kes_period: kes_period, - sigma: sigma.clone(), - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)] -pub enum HeaderLeaderCertEnum { - NonceAndLeader(VRFCert, VRFCert), - VrfResult(VRFCert), -} - -#[wasm_bindgen] -#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize, JsonSchema)] -pub struct HeaderBody { - block_number: u32, - slot: SlotBigNum, - prev_hash: Option, - issuer_vkey: Vkey, - vrf_vkey: VRFVKey, - leader_cert: HeaderLeaderCertEnum, - block_body_size: u32, - block_body_hash: BlockHash, - operational_cert: OperationalCert, - protocol_version: ProtocolVersion, -} - -impl_to_from!(HeaderBody); - -#[wasm_bindgen] -impl HeaderBody { - pub fn block_number(&self) -> u32 { - self.block_number.clone() - } - - /// !!! DEPRECATED !!! - /// Returns a Slot32 (u32) value in case the underlying original BigNum (u64) value is within the limits. - /// Otherwise will just raise an error. - #[deprecated( - since = "10.1.0", - note = "Possible boundary error. Use slot_bignum instead" - )] - pub fn slot(&self) -> Result { - self.slot.clone().try_into() - } - - pub fn slot_bignum(&self) -> SlotBigNum { - self.slot.clone() - } - - pub fn prev_hash(&self) -> Option { - self.prev_hash.clone() - } - - pub fn issuer_vkey(&self) -> Vkey { - self.issuer_vkey.clone() - } - - pub fn vrf_vkey(&self) -> VRFVKey { - self.vrf_vkey.clone() - } - - /// If this function returns true, the `.nonce_vrf_or_nothing` - /// and the `.leader_vrf_or_nothing` functions will return - /// non-empty results - pub fn has_nonce_and_leader_vrf(&self) -> bool { - match &self.leader_cert { - HeaderLeaderCertEnum::NonceAndLeader(_, _) => true, - _ => false, - } - } - - /// Might return nothing in case `.has_nonce_and_leader_vrf` returns false - pub fn nonce_vrf_or_nothing(&self) -> Option { - match &self.leader_cert { - HeaderLeaderCertEnum::NonceAndLeader(nonce, _) => Some(nonce.clone()), - _ => None, - } - } - - /// Might return nothing in case `.has_nonce_and_leader_vrf` returns false - pub fn leader_vrf_or_nothing(&self) -> Option { - match &self.leader_cert { - HeaderLeaderCertEnum::NonceAndLeader(_, leader) => Some(leader.clone()), - _ => None, - } - } - - /// If this function returns true, the `.vrf_result_or_nothing` - /// function will return a non-empty result - pub fn has_vrf_result(&self) -> bool { - match &self.leader_cert { - HeaderLeaderCertEnum::VrfResult(_) => true, - _ => false, - } - } - - /// Might return nothing in case `.has_vrf_result` returns false - pub fn vrf_result_or_nothing(&self) -> Option { - match &self.leader_cert { - HeaderLeaderCertEnum::VrfResult(cert) => Some(cert.clone()), - _ => None, - } - } - - pub fn block_body_size(&self) -> u32 { - self.block_body_size.clone() - } - - pub fn block_body_hash(&self) -> BlockHash { - self.block_body_hash.clone() - } - - pub fn operational_cert(&self) -> OperationalCert { - self.operational_cert.clone() - } - - pub fn protocol_version(&self) -> ProtocolVersion { - self.protocol_version.clone() - } - - /// !!! DEPRECATED !!! - /// This constructor uses outdated slot number format. - /// Use `.new_headerbody` instead - #[deprecated( - since = "10.1.0", - note = "Underlying value capacity of slot (BigNum u64) bigger then Slot32. Use new_bignum instead." - )] - pub fn new( - block_number: u32, - slot: Slot32, - prev_hash: Option, - issuer_vkey: &Vkey, - vrf_vkey: &VRFVKey, - vrf_result: &VRFCert, - block_body_size: u32, - block_body_hash: &BlockHash, - operational_cert: &OperationalCert, - protocol_version: &ProtocolVersion, - ) -> Self { - Self { - block_number: block_number, - slot: slot.clone().into(), - prev_hash: prev_hash.clone(), - issuer_vkey: issuer_vkey.clone(), - vrf_vkey: vrf_vkey.clone(), - leader_cert: HeaderLeaderCertEnum::VrfResult(vrf_result.clone()), - block_body_size: block_body_size, - block_body_hash: block_body_hash.clone(), - operational_cert: operational_cert.clone(), - protocol_version: protocol_version.clone(), - } - } - - pub fn new_headerbody( - block_number: u32, - slot: &SlotBigNum, - prev_hash: Option, - issuer_vkey: &Vkey, - vrf_vkey: &VRFVKey, - vrf_result: &VRFCert, - block_body_size: u32, - block_body_hash: &BlockHash, - operational_cert: &OperationalCert, - protocol_version: &ProtocolVersion, - ) -> Self { - Self { - block_number: block_number, - slot: slot.clone(), - prev_hash: prev_hash.clone(), - issuer_vkey: issuer_vkey.clone(), - vrf_vkey: vrf_vkey.clone(), - leader_cert: HeaderLeaderCertEnum::VrfResult(vrf_result.clone()), - block_body_size: block_body_size, - block_body_hash: block_body_hash.clone(), - operational_cert: operational_cert.clone(), - protocol_version: protocol_version.clone(), - } - } -} - #[wasm_bindgen] #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct AssetName(Vec); @@ -3428,12 +1391,12 @@ impl MultiAsset { &mut self, policy_id: &PolicyID, asset_name: &AssetName, - value: BigNum, + value: &BigNum, ) -> Option { self.0 .entry(policy_id.clone()) .or_default() - .insert(asset_name, &value) + .insert(asset_name, value) } /// returns the amount of asset {asset_name} under policy {policy_id} @@ -3461,7 +1424,7 @@ impl MultiAsset { match lhs_ma.0.get_mut(policy) { Some(assets) => match assets.0.get_mut(asset_name) { Some(current) => match current.checked_sub(&amount) { - Ok(new) => match new.compare(&to_bignum(0)) { + Ok(new) => match new.compare(&BigNum(0)) { 0 => { assets.0.remove(asset_name); match assets.0.len() { @@ -3508,7 +1471,7 @@ impl PartialOrd for MultiAsset { fn amount_or_zero(ma: &MultiAsset, pid: &PolicyID, aname: &AssetName) -> Coin { ma.get(&pid) .and_then(|assets| assets.get(aname)) - .unwrap_or(to_bignum(0u64)) // assume 0 if asset not present + .unwrap_or(BigNum(0u64)) // assume 0 if asset not present } // idea: if (a-b) > 0 for some asset, then a > b for at least some asset @@ -3517,7 +1480,7 @@ impl PartialOrd for MultiAsset { for (aname, amount) in assets.0.iter() { match amount .clamped_sub(&amount_or_zero(&rhs, pid, aname)) - .cmp(&to_bignum(0)) + .cmp(&BigNum::zero()) { std::cmp::Ordering::Equal => (), _ => return false, @@ -3537,20 +1500,28 @@ impl PartialOrd for MultiAsset { } #[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema)] pub struct MintsAssets(Vec); +to_from_json!(MintsAssets); + +#[wasm_bindgen] impl MintsAssets { pub fn new() -> Self { Self(Vec::new()) } - pub fn add(&mut self, mint_assets: MintAssets) { - self.0.push(mint_assets) + pub fn add(&mut self, mint_assets: &MintAssets) { + self.0.push(mint_assets.clone()) } pub fn get(&self, index: usize) -> Option { self.0.get(index).map(|v| v.clone()) } + + pub fn len(&self) -> usize { + self.0.len() + } } #[wasm_bindgen] @@ -3565,17 +1536,27 @@ impl MintAssets { Self(std::collections::BTreeMap::new()) } - pub fn new_from_entry(key: &AssetName, value: Int) -> Self { + pub fn new_from_entry(key: &AssetName, value: &Int) -> Result { + if value.0 == 0 { + return Err(JsError::from_str("MintAssets cannot be created with 0 value")); + } let mut ma = MintAssets::new(); - ma.insert(key, value); - ma + ma.insert(key, value.clone())?; + Ok(ma) } pub fn len(&self) -> usize { self.0.len() } - pub fn insert(&mut self, key: &AssetName, value: Int) -> Option { + pub fn insert(&mut self, key: &AssetName, value: Int) -> Result, JsError> { + if value.0 == 0 { + return Err(JsError::from_str("MintAssets cannot be created with 0 value")); + } + Ok(self.0.insert(key.clone(), value)) + } + + pub(crate) fn insert_unchecked(&mut self, key: &AssetName, value: Int) -> Option { self.0.insert(key.clone(), value) } @@ -3601,6 +1582,12 @@ pub struct Mint(Vec<(PolicyID, MintAssets)>); impl_to_from!(Mint); +impl NoneOrEmpty for Mint { + fn is_none_or_empty(&self) -> bool { + self.0.is_empty() + } +} + #[wasm_bindgen] impl Mint { pub fn new() -> Self { @@ -3623,23 +1610,9 @@ impl Mint { None } - /// !!! DEPRECATED !!! - /// Mint can store multiple entries for the same policy id. - /// Use `.get_all` instead. - #[deprecated( - since = "11.2.0", - note = "Mint can store multiple entries for the same policy id. Use `.get_all` instead." - )] - pub fn get(&self, key: &PolicyID) -> Option { - self.0 - .iter() - .filter(|(k, _)| k.eq(key)) - .next() - .map(|(_k, v)| v.clone()) - } - - pub fn get_all(&self, key: &PolicyID) -> Option { - let mints : Vec = self.0 + pub fn get(&self, key: &PolicyID) -> Option { + let mints: Vec = self + .0 .iter() .filter(|(k, _)| k.eq(key)) .map(|(_k, v)| v.clone()) @@ -3661,24 +1634,26 @@ impl Mint { } fn as_multiasset(&self, is_positive: bool) -> MultiAsset { - self.0.iter().fold(MultiAsset::new(), |res, e : &(PolicyID, MintAssets) | { - let assets: Assets = (e.1).0.iter().fold(Assets::new(), |res, e| { - let mut assets = res; - if e.1.is_positive() == is_positive { - let amount = match is_positive { - true => e.1.as_positive(), - false => e.1.as_negative(), - }; - assets.insert(&e.0, &amount.unwrap()); + self.0 + .iter() + .fold(MultiAsset::new(), |res, e: &(PolicyID, MintAssets)| { + let assets: Assets = (e.1).0.iter().fold(Assets::new(), |res, e| { + let mut assets = res; + if e.1.is_positive() == is_positive { + let amount = match is_positive { + true => e.1.as_positive(), + false => e.1.as_negative(), + }; + assets.insert(&e.0, &amount.unwrap()); + } + assets + }); + let mut ma = res; + if !assets.0.is_empty() { + ma.insert(&e.0, &assets); } - assets - }); - let mut ma = res; - if !assets.0.is_empty() { - ma.insert(&e.0, &assets); - } - ma - }) + ma + }) } /// Returns the multiasset where only positive (minting) entries are present @@ -3742,320 +1717,18 @@ impl NetworkId { } } -impl From<&NativeScript> for RequiredSignersSet { +impl From<&NativeScript> for Ed25519KeyHashes { fn from(script: &NativeScript) -> Self { match &script.0 { NativeScriptEnum::ScriptPubkey(spk) => { - let mut set = BTreeSet::new(); - set.insert(spk.addr_keyhash()); + let mut set = Ed25519KeyHashes::new(); + set.add_move(spk.addr_keyhash()); set } - NativeScriptEnum::ScriptAll(all) => RequiredSignersSet::from(&all.native_scripts), - NativeScriptEnum::ScriptAny(any) => RequiredSignersSet::from(&any.native_scripts), - NativeScriptEnum::ScriptNOfK(ofk) => RequiredSignersSet::from(&ofk.native_scripts), - _ => BTreeSet::new(), + NativeScriptEnum::ScriptAll(all) => Ed25519KeyHashes::from(&all.native_scripts), + NativeScriptEnum::ScriptAny(any) => Ed25519KeyHashes::from(&any.native_scripts), + NativeScriptEnum::ScriptNOfK(ofk) => Ed25519KeyHashes::from(&ofk.native_scripts), + _ => Ed25519KeyHashes::new(), } } -} - -impl From<&NativeScripts> for RequiredSignersSet { - fn from(scripts: &NativeScripts) -> Self { - scripts.0.iter().fold(BTreeSet::new(), |mut set, s| { - RequiredSignersSet::from(s).iter().for_each(|pk| { - set.insert(pk.clone()); - }); - set - }) - } -} - -#[cfg(test)] -mod tests { - use crate::tx_builder_constants::TxBuilderConstants; - use super::*; - - #[test] - fn native_script_hash() { - let keyhash = Ed25519KeyHash::from_bytes(vec![ - 143, 180, 186, 93, 223, 42, 243, 7, 81, 98, 86, 125, 97, 69, 110, 52, 130, 243, 244, - 98, 246, 13, 33, 212, 128, 168, 136, 40, - ]) - .unwrap(); - assert_eq!( - hex::encode(&keyhash.to_bytes()), - "8fb4ba5ddf2af3075162567d61456e3482f3f462f60d21d480a88828" - ); - - let script = NativeScript::new_script_pubkey(&ScriptPubkey::new(&keyhash)); - - let script_hash = script.hash(); - - assert_eq!( - hex::encode(&script_hash.to_bytes()), - "187b8d3ddcb24013097c003da0b8d8f7ddcf937119d8f59dccd05a0f" - ); - } - - #[test] - fn asset_name_ord() { - let name1 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - let name11 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - - let name2 = AssetName::new(vec![0u8, 4, 5, 6]).unwrap(); - let name22 = AssetName::new(vec![0u8, 4, 5, 6]).unwrap(); - - let name3 = AssetName::new(vec![0u8, 7, 8]).unwrap(); - let name33 = AssetName::new(vec![0u8, 7, 8]).unwrap(); - - assert_eq!(name1.cmp(&name2), Ordering::Less); - assert_eq!(name2.cmp(&name1), Ordering::Greater); - assert_eq!(name1.cmp(&name3), Ordering::Greater); - assert_eq!(name2.cmp(&name3), Ordering::Greater); - assert_eq!(name3.cmp(&name1), Ordering::Less); - assert_eq!(name3.cmp(&name2), Ordering::Less); - - assert_eq!(name1.cmp(&name11), Ordering::Equal); - assert_eq!(name2.cmp(&name22), Ordering::Equal); - assert_eq!(name3.cmp(&name33), Ordering::Equal); - - let mut map = Assets::new(); - map.insert(&name2, &to_bignum(1)); - map.insert(&name1, &to_bignum(1)); - map.insert(&name3, &to_bignum(1)); - - assert_eq!(map.keys(), AssetNames(vec![name3, name1, name2])); - - let mut map2 = MintAssets::new(); - map2.insert(&name11, Int::new_i32(1)); - map2.insert(&name33, Int::new_i32(1)); - map2.insert(&name22, Int::new_i32(1)); - - assert_eq!(map2.keys(), AssetNames(vec![name33, name11, name22])); - } - - #[test] - fn mint_to_multiasset() { - let policy_id1 = PolicyID::from([0u8; 28]); - let policy_id2 = PolicyID::from([1u8; 28]); - let name1 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - let name2 = AssetName::new(vec![0u8, 4, 5, 6]).unwrap(); - let amount1 = BigNum::from_str("1234").unwrap(); - let amount2 = BigNum::from_str("5678").unwrap(); - - let mut mass1 = MintAssets::new(); - mass1.insert(&name1, Int::new(&amount1)); - mass1.insert(&name2, Int::new(&amount2)); - - let mut mass2 = MintAssets::new(); - mass2.insert(&name1, Int::new(&amount2)); - mass2.insert(&name2, Int::new(&amount1)); - - let mut mint = Mint::new(); - mint.insert(&policy_id1, &mass1); - mint.insert(&policy_id2, &mass2); - - let multiasset = mint.as_positive_multiasset(); - assert_eq!(multiasset.len(), 2); - - let ass1 = multiasset.get(&policy_id1).unwrap(); - let ass2 = multiasset.get(&policy_id2).unwrap(); - - assert_eq!(ass1.len(), 2); - assert_eq!(ass2.len(), 2); - - assert_eq!(ass1.get(&name1).unwrap(), amount1); - assert_eq!(ass1.get(&name2).unwrap(), amount2); - - assert_eq!(ass2.get(&name1).unwrap(), amount2); - assert_eq!(ass2.get(&name2).unwrap(), amount1); - } - - #[test] - fn mint_to_negative_multiasset() { - let policy_id1 = PolicyID::from([0u8; 28]); - let policy_id2 = PolicyID::from([1u8; 28]); - let name1 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - let name2 = AssetName::new(vec![0u8, 4, 5, 6]).unwrap(); - let amount1 = BigNum::from_str("1234").unwrap(); - let amount2 = BigNum::from_str("5678").unwrap(); - - let mut mass1 = MintAssets::new(); - mass1.insert(&name1, Int::new(&amount1)); - mass1.insert(&name2, Int::new_negative(&amount2)); - - let mut mass2 = MintAssets::new(); - mass2.insert(&name1, Int::new_negative(&amount1)); - mass2.insert(&name2, Int::new(&amount2)); - - let mut mint = Mint::new(); - mint.insert(&policy_id1, &mass1); - mint.insert(&policy_id2, &mass2); - - let p_multiasset = mint.as_positive_multiasset(); - let n_multiasset = mint.as_negative_multiasset(); - - assert_eq!(p_multiasset.len(), 2); - assert_eq!(n_multiasset.len(), 2); - - let p_ass1 = p_multiasset.get(&policy_id1).unwrap(); - let p_ass2 = p_multiasset.get(&policy_id2).unwrap(); - - let n_ass1 = n_multiasset.get(&policy_id1).unwrap(); - let n_ass2 = n_multiasset.get(&policy_id2).unwrap(); - - assert_eq!(p_ass1.len(), 1); - assert_eq!(p_ass2.len(), 1); - assert_eq!(n_ass1.len(), 1); - assert_eq!(n_ass2.len(), 1); - - assert_eq!(p_ass1.get(&name1).unwrap(), amount1); - assert!(p_ass1.get(&name2).is_none()); - - assert!(p_ass2.get(&name1).is_none()); - assert_eq!(p_ass2.get(&name2).unwrap(), amount2); - - assert!(n_ass1.get(&name1).is_none()); - assert_eq!(n_ass1.get(&name2).unwrap(), amount2); - - assert_eq!(n_ass2.get(&name1).unwrap(), amount1); - assert!(n_ass2.get(&name2).is_none()); - } - - #[test] - fn mint_to_negative_multiasset_empty() { - let policy_id1 = PolicyID::from([0u8; 28]); - let name1 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - let amount1 = BigNum::from_str("1234").unwrap(); - - let mut mass1 = MintAssets::new(); - mass1.insert(&name1, Int::new(&amount1)); - - let mut mass2 = MintAssets::new(); - mass2.insert(&name1, Int::new_negative(&amount1)); - - let mut mint1 = Mint::new(); - mint1.insert(&policy_id1, &mass1); - - let mut mint2 = Mint::new(); - mint2.insert(&policy_id1, &mass2); - - let p_multiasset_some = mint1.as_positive_multiasset(); - let p_multiasset_none = mint2.as_positive_multiasset(); - - let n_multiasset_none = mint1.as_negative_multiasset(); - let n_multiasset_some = mint2.as_negative_multiasset(); - - assert_eq!(p_multiasset_some.len(), 1); - assert_eq!(p_multiasset_none.len(), 0); - - assert_eq!(n_multiasset_some.len(), 1); - assert_eq!(n_multiasset_none.len(), 0); - - let p_ass = p_multiasset_some.get(&policy_id1).unwrap(); - let n_ass = n_multiasset_some.get(&policy_id1).unwrap(); - - assert_eq!(p_ass.len(), 1); - assert_eq!(n_ass.len(), 1); - - assert_eq!(p_ass.get(&name1).unwrap(), amount1); - assert_eq!(n_ass.get(&name1).unwrap(), amount1); - } - - fn keyhash(x: u8) -> Ed25519KeyHash { - Ed25519KeyHash::from_bytes(vec![ - x, 180, 186, 93, 223, 42, 243, 7, 81, 98, 86, 125, 97, 69, 110, 52, 130, 243, 244, 98, - 246, 13, 33, 212, 128, 168, 136, 40, - ]) - .unwrap() - } - - fn pkscript(pk: &Ed25519KeyHash) -> NativeScript { - NativeScript::new_script_pubkey(&ScriptPubkey::new(pk)) - } - - fn scripts_vec(scripts: Vec<&NativeScript>) -> NativeScripts { - NativeScripts(scripts.iter().map(|s| (*s).clone()).collect()) - } - - #[test] - fn native_scripts_get_pubkeys() { - let keyhash1 = keyhash(1); - let keyhash2 = keyhash(2); - let keyhash3 = keyhash(3); - - let pks1 = RequiredSignersSet::from(&pkscript(&keyhash1)); - assert_eq!(pks1.len(), 1); - assert!(pks1.contains(&keyhash1)); - - let pks2 = - RequiredSignersSet::from(&NativeScript::new_timelock_start(&TimelockStart::new(123))); - assert_eq!(pks2.len(), 0); - - let pks3 = RequiredSignersSet::from(&NativeScript::new_script_all(&ScriptAll::new( - &scripts_vec(vec![&pkscript(&keyhash1), &pkscript(&keyhash2)]), - ))); - assert_eq!(pks3.len(), 2); - assert!(pks3.contains(&keyhash1)); - assert!(pks3.contains(&keyhash2)); - - let pks4 = RequiredSignersSet::from(&NativeScript::new_script_any(&ScriptAny::new( - &scripts_vec(vec![ - &NativeScript::new_script_n_of_k(&ScriptNOfK::new( - 1, - &scripts_vec(vec![ - &NativeScript::new_timelock_start(&TimelockStart::new(132)), - &pkscript(&keyhash3), - ]), - )), - &NativeScript::new_script_all(&ScriptAll::new(&scripts_vec(vec![ - &NativeScript::new_timelock_expiry(&TimelockExpiry::new(132)), - &pkscript(&keyhash1), - ]))), - &NativeScript::new_script_any(&ScriptAny::new(&scripts_vec(vec![ - &pkscript(&keyhash1), - &pkscript(&keyhash2), - &pkscript(&keyhash3), - ]))), - ]), - ))); - assert_eq!(pks4.len(), 3); - assert!(pks4.contains(&keyhash1)); - assert!(pks4.contains(&keyhash2)); - assert!(pks4.contains(&keyhash3)); - } - - #[test] - fn protocol_params_update_cbor_roundtrip() { - let mut orig_ppu = ProtocolParamUpdate::new(); - orig_ppu.set_max_tx_size(1234); - orig_ppu.set_max_block_body_size(5678); - orig_ppu.set_max_block_header_size(91011); - orig_ppu.set_minfee_a(&Coin::from(1u32)); - orig_ppu.set_minfee_b(&Coin::from(2u32)); - orig_ppu.set_key_deposit(&Coin::from(3u32)); - orig_ppu.set_pool_deposit(&Coin::from(4u32)); - orig_ppu.set_max_epoch(5); - orig_ppu.set_n_opt(6); - orig_ppu.set_pool_pledge_influence(&Rational::new(&BigNum::from(7u32), &BigNum::from(77u32))); - orig_ppu.set_expansion_rate(&UnitInterval::new(&BigNum::from(8u32), &BigNum::from(9u32))); - orig_ppu.set_treasury_growth_rate(&UnitInterval::new(&BigNum::from(10u32), &BigNum::from(11u32))); - orig_ppu.set_protocol_version(&ProtocolVersion::new(12u32,13u32)); - orig_ppu.set_min_pool_cost(&Coin::from(14u32)); - orig_ppu.set_ada_per_utxo_byte(&Coin::from(15u32)); - orig_ppu.set_cost_models(&TxBuilderConstants::plutus_vasil_cost_models()); - orig_ppu.set_execution_costs(&ExUnitPrices::new( - &SubCoin::new(&BigNum::from(16u32), &BigNum::from(17u32)), - &SubCoin::new(&BigNum::from(18u32), &BigNum::from(19u32)))); - orig_ppu.set_max_tx_ex_units(&ExUnits::new(&BigNum::from(20u32), &BigNum::from(21u32))); - orig_ppu.set_max_block_ex_units(&ExUnits::new(&BigNum::from(22u32), &BigNum::from(23u32))); - orig_ppu.set_max_value_size(24); - orig_ppu.set_collateral_percentage(25); - orig_ppu.set_max_collateral_inputs(25); - - let encoded = orig_ppu.to_bytes(); - let dencoded = ProtocolParamUpdate::from_bytes(encoded).unwrap(); - - assert_eq!(dencoded, orig_ppu); - assert_eq!(dencoded.to_bytes(), orig_ppu.to_bytes()); - } -} +} \ No newline at end of file diff --git a/rust/src/metadata.rs b/rust/src/metadata.rs deleted file mode 100644 index ccd81ca0..00000000 --- a/rust/src/metadata.rs +++ /dev/null @@ -1,1477 +0,0 @@ -use super::*; -use linked_hash_map::LinkedHashMap; - -const MD_MAX_LEN: usize = 64; - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct MetadataMap(LinkedHashMap); - -to_from_bytes!(MetadataMap); - -#[wasm_bindgen] -impl MetadataMap { - pub fn new() -> Self { - Self(LinkedHashMap::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn insert( - &mut self, - key: &TransactionMetadatum, - value: &TransactionMetadatum, - ) -> Option { - self.0.insert(key.clone(), value.clone()) - } - - // convenience function for inserting as a string key - pub fn insert_str( - &mut self, - key: &str, - value: &TransactionMetadatum, - ) -> Result, JsError> { - Ok(self.insert(&TransactionMetadatum::new_text(key.to_owned())?, value)) - } - - // convenience function for inserting 32-bit integers - for higher-precision integers use insert() with an Int struct - pub fn insert_i32( - &mut self, - key: i32, - value: &TransactionMetadatum, - ) -> Option { - self.insert(&TransactionMetadatum::new_int(&Int::new_i32(key)), value) - } - - pub fn get(&self, key: &TransactionMetadatum) -> Result { - self.0 - .get(key) - .map(|v| v.clone()) - .ok_or_else(|| JsError::from_str(&format!("key {:?} not found", key))) - } - - // convenience function for retrieving a string key - pub fn get_str(&self, key: &str) -> Result { - self.get(&TransactionMetadatum::new_text(key.to_owned())?) - } - - // convenience function for retrieving 32-bit integer keys - for higher-precision integers use get() with an Int struct - pub fn get_i32(&self, key: i32) -> Result { - self.get(&TransactionMetadatum::new_int(&Int::new_i32(key))) - } - - pub fn has(&self, key: &TransactionMetadatum) -> bool { - self.0.contains_key(key) - } - - pub fn keys(&self) -> MetadataList { - MetadataList( - self.0 - .iter() - .map(|(k, _v)| k.clone()) - .collect::>(), - ) - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct MetadataList(Vec); - -to_from_bytes!(MetadataList); - -#[wasm_bindgen] -impl MetadataList { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> TransactionMetadatum { - self.0[index].clone() - } - - pub fn add(&mut self, elem: &TransactionMetadatum) { - self.0.push(elem.clone()); - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum TransactionMetadatumKind { - MetadataMap, - MetadataList, - Int, - Bytes, - Text, -} - -#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -enum TransactionMetadatumEnum { - MetadataMap(MetadataMap), - MetadataList(MetadataList), - Int(Int), - Bytes(Vec), - Text(String), -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct TransactionMetadatum(TransactionMetadatumEnum); - -to_from_bytes!(TransactionMetadatum); - -#[wasm_bindgen] -impl TransactionMetadatum { - pub fn new_map(map: &MetadataMap) -> Self { - Self(TransactionMetadatumEnum::MetadataMap(map.clone())) - } - - pub fn new_list(list: &MetadataList) -> Self { - Self(TransactionMetadatumEnum::MetadataList(list.clone())) - } - - pub fn new_int(int: &Int) -> Self { - Self(TransactionMetadatumEnum::Int(int.clone())) - } - - pub fn new_bytes(bytes: Vec) -> Result { - if bytes.len() > MD_MAX_LEN { - Err(JsError::from_str(&format!( - "Max metadata bytes too long: {}, max = {}", - bytes.len(), - MD_MAX_LEN - ))) - } else { - Ok(Self(TransactionMetadatumEnum::Bytes(bytes))) - } - } - - pub fn new_text(text: String) -> Result { - if text.len() > MD_MAX_LEN { - Err(JsError::from_str(&format!( - "Max metadata string too long: {}, max = {}", - text.len(), - MD_MAX_LEN - ))) - } else { - Ok(Self(TransactionMetadatumEnum::Text(text))) - } - } - - pub fn kind(&self) -> TransactionMetadatumKind { - match &self.0 { - TransactionMetadatumEnum::MetadataMap(_) => TransactionMetadatumKind::MetadataMap, - TransactionMetadatumEnum::MetadataList(_) => TransactionMetadatumKind::MetadataList, - TransactionMetadatumEnum::Int(_) => TransactionMetadatumKind::Int, - TransactionMetadatumEnum::Bytes(_) => TransactionMetadatumKind::Bytes, - TransactionMetadatumEnum::Text(_) => TransactionMetadatumKind::Text, - } - } - - pub fn as_map(&self) -> Result { - match &self.0 { - TransactionMetadatumEnum::MetadataMap(x) => Ok(x.clone()), - _ => Err(JsError::from_str("not a map")), - } - } - - pub fn as_list(&self) -> Result { - match &self.0 { - TransactionMetadatumEnum::MetadataList(x) => Ok(x.clone()), - _ => Err(JsError::from_str("not a list")), - } - } - - pub fn as_int(&self) -> Result { - match &self.0 { - TransactionMetadatumEnum::Int(x) => Ok(x.clone()), - _ => Err(JsError::from_str("not an int")), - } - } - - pub fn as_bytes(&self) -> Result, JsError> { - match &self.0 { - TransactionMetadatumEnum::Bytes(x) => Ok(x.clone()), - _ => Err(JsError::from_str("not bytes")), - } - } - - pub fn as_text(&self) -> Result { - match &self.0 { - TransactionMetadatumEnum::Text(x) => Ok(x.clone()), - _ => Err(JsError::from_str("not text")), - } - } -} - -impl serde::Serialize for TransactionMetadatum { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let json_str = decode_metadatum_to_json_str(self, MetadataJsonSchema::DetailedSchema) - .map_err(|e| serde::ser::Error::custom(&format!("{:?}", e)))?; - serializer.serialize_str(&json_str) - } -} - -impl<'de> serde::de::Deserialize<'de> for TransactionMetadatum { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let s = ::deserialize(deserializer)?; - encode_json_str_to_metadatum(s.clone(), MetadataJsonSchema::DetailedSchema).map_err(|e| { - serde::de::Error::invalid_value( - serde::de::Unexpected::Str(&s), - &format!("{:?}", e).as_str(), - ) - }) - } -} - -// just for now we'll do json-in-json until I can figure this out better -// TODO: maybe not generate this? or how do we do this? -impl JsonSchema for TransactionMetadatum { - fn schema_name() -> String { - String::from("TransactionMetadatum") - } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - String::json_schema(gen) - } - fn is_referenceable() -> bool { - String::is_referenceable() - } -} - -pub type TransactionMetadatumLabel = BigNum; - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct TransactionMetadatumLabels(Vec); - -to_from_bytes!(TransactionMetadatumLabels); - -#[wasm_bindgen] -impl TransactionMetadatumLabels { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> TransactionMetadatumLabel { - self.0[index].clone() - } - - pub fn add(&mut self, elem: &TransactionMetadatumLabel) { - self.0.push(elem.clone()); - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct GeneralTransactionMetadata( - LinkedHashMap, -); - -impl_to_from!(GeneralTransactionMetadata); - -#[wasm_bindgen] -impl GeneralTransactionMetadata { - pub fn new() -> Self { - Self(LinkedHashMap::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn insert( - &mut self, - key: &TransactionMetadatumLabel, - value: &TransactionMetadatum, - ) -> Option { - self.0.insert(key.clone(), value.clone()) - } - - pub fn get(&self, key: &TransactionMetadatumLabel) -> Option { - self.0.get(key).map(|v| v.clone()) - } - - pub fn keys(&self) -> TransactionMetadatumLabels { - TransactionMetadatumLabels( - self.0 - .iter() - .map(|(k, _v)| k.clone()) - .collect::>(), - ) - } -} - -impl serde::Serialize for GeneralTransactionMetadata { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let map = self.0.iter().collect::>(); - map.serialize(serializer) - } -} - -impl<'de> serde::de::Deserialize<'de> for GeneralTransactionMetadata { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let map = as serde::de::Deserialize>::deserialize( - deserializer, - )?; - Ok(Self(map.into_iter().collect())) - } -} - -impl JsonSchema for GeneralTransactionMetadata { - fn schema_name() -> String { - String::from("GeneralTransactionMetadata") - } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - std::collections::BTreeMap::::json_schema( - gen, - ) - } - fn is_referenceable() -> bool { - std::collections::BTreeMap::::is_referenceable() - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Ord, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema)] -pub struct AuxiliaryData { - metadata: Option, - native_scripts: Option, - plutus_scripts: Option, - prefer_alonzo_format: bool, -} - -impl std::cmp::PartialEq for AuxiliaryData { - fn eq(&self, other: &Self) -> bool { - self.metadata.eq(&other.metadata) - && self.native_scripts.eq(&other.native_scripts) - && self.plutus_scripts.eq(&other.plutus_scripts) - } -} - -impl std::cmp::Eq for AuxiliaryData {} - -impl_to_from!(AuxiliaryData); - -#[wasm_bindgen] -impl AuxiliaryData { - pub fn new() -> Self { - Self { - metadata: None, - native_scripts: None, - plutus_scripts: None, - prefer_alonzo_format: false, - } - } - - pub fn metadata(&self) -> Option { - self.metadata.clone() - } - - pub fn set_metadata(&mut self, metadata: &GeneralTransactionMetadata) { - self.metadata = Some(metadata.clone()); - } - - pub fn native_scripts(&self) -> Option { - self.native_scripts.clone() - } - - pub fn set_native_scripts(&mut self, native_scripts: &NativeScripts) { - self.native_scripts = Some(native_scripts.clone()) - } - - pub fn plutus_scripts(&self) -> Option { - self.plutus_scripts.clone() - } - - pub fn set_plutus_scripts(&mut self, plutus_scripts: &PlutusScripts) { - self.plutus_scripts = Some(plutus_scripts.clone()) - } - - pub fn prefer_alonzo_format(&self) -> bool { - self.prefer_alonzo_format.clone() - } - - pub fn set_prefer_alonzo_format(&mut self, prefer: bool) { - self.prefer_alonzo_format = prefer - } -} - -// encodes arbitrary bytes into chunks of 64 bytes (the limit for bytes) as a list to be valid Metadata -#[wasm_bindgen] -pub fn encode_arbitrary_bytes_as_metadatum(bytes: &[u8]) -> TransactionMetadatum { - let mut list = MetadataList::new(); - for chunk in bytes.chunks(MD_MAX_LEN) { - // this should never fail as we are already chunking it - list.add(&TransactionMetadatum::new_bytes(chunk.to_vec()).unwrap()); - } - TransactionMetadatum::new_list(&list) -} - -// decodes from chunks of bytes in a list to a byte vector if that is the metadata format, otherwise returns None -#[wasm_bindgen] -pub fn decode_arbitrary_bytes_from_metadatum( - metadata: &TransactionMetadatum, -) -> Result, JsError> { - let mut bytes = Vec::new(); - for elem in metadata.as_list()?.0 { - bytes.append(&mut elem.as_bytes()?); - } - Ok(bytes) -} - -#[wasm_bindgen] -#[derive(Copy, Clone, Eq, PartialEq)] -// Different schema methods for mapping between JSON and the metadata CBOR. -// This conversion should match TxMetadataJsonSchema in cardano-node defined (at time of writing) here: -// https://github.com/input-output-hk/cardano-node/blob/master/cardano-api/src/Cardano/Api/MetaData.hs -// but has 2 additional schemas for more or less conversionse -// Note: Byte/Strings (including keys) in any schema must be at most 64 bytes in length -pub enum MetadataJsonSchema { - // Does zero implicit conversions. - // Round-trip conversions are 100% consistent - // Treats maps DIRECTLY as maps in JSON in a natural way e.g. {"key1": 47, "key2": [0, 1]]} - // From JSON: - // * null/true/false NOT supported. - // * keys treated as strings only - // To JSON - // * Bytes, non-string keys NOT supported. - // Stricter than any TxMetadataJsonSchema in cardano-node but more natural for JSON -> Metadata - NoConversions, - // Does some implicit conversions. - // Round-trip conversions MD -> JSON -> MD is NOT consistent, but JSON -> MD -> JSON is. - // Without using bytes - // Maps are treated as an array of k-v pairs as such: [{"key1": 47}, {"key2": [0, 1]}, {"key3": "0xFFFF"}] - // From JSON: - // * null/true/false NOT supported. - // * Strings parseable as bytes (0x starting hex) or integers are converted. - // To JSON: - // * Non-string keys partially supported (bytes as 0x starting hex string, integer converted to string). - // * Bytes are converted to hex strings starting with 0x for both values and keys. - // Corresponds to TxMetadataJsonSchema's TxMetadataJsonNoSchema in cardano-node - BasicConversions, - // Supports the annotated schema presented in cardano-node with tagged values e.g. {"int": 7}, {"list": [0, 1]} - // Round-trip conversions are 100% consistent - // Maps are treated as an array of k-v pairs as such: [{"key1": {"int": 47}}, {"key2": {"list": [0, 1]}}, {"key3": {"bytes": "0xFFFF"}}] - // From JSON: - // * null/true/false NOT supported. - // * Strings parseable as bytes (hex WITHOUT 0x prefix) or integers converted. - // To JSON: - // * Non-string keys are supported. Any key parseable as JSON is encoded as metadata instead of a string - // Corresponds to TxMetadataJsonSchema's TxMetadataJsonDetailedSchema in cardano-node - DetailedSchema, -} - -fn supports_tagged_values(schema: MetadataJsonSchema) -> bool { - match schema { - MetadataJsonSchema::NoConversions | MetadataJsonSchema::BasicConversions => false, - MetadataJsonSchema::DetailedSchema => true, - } -} - -fn hex_string_to_bytes(hex: &str) -> Option> { - if hex.starts_with("0x") { - hex::decode(&hex[2..]).ok() - } else { - None - } -} - -fn bytes_to_hex_string(bytes: &[u8]) -> String { - format!("0x{}", hex::encode(bytes)) -} - -// Converts JSON to Metadata according to MetadataJsonSchema -#[wasm_bindgen] -pub fn encode_json_str_to_metadatum( - json: String, - schema: MetadataJsonSchema, -) -> Result { - let value = serde_json::from_str(&json).map_err(|e| JsError::from_str(&e.to_string()))?; - encode_json_value_to_metadatum(value, schema) -} - -pub fn encode_json_value_to_metadatum( - value: serde_json::Value, - schema: MetadataJsonSchema, -) -> Result { - use serde_json::Value; - fn encode_number(x: serde_json::Number) -> Result { - if let Some(x) = x.as_u64() { - Ok(TransactionMetadatum::new_int(&Int::new(&utils::to_bignum( - x, - )))) - } else if let Some(x) = x.as_i64() { - Ok(TransactionMetadatum::new_int(&Int::new_negative( - &utils::to_bignum(-x as u64), - ))) - } else { - Err(JsError::from_str("floats not allowed in metadata")) - } - } - fn encode_string( - s: String, - schema: MetadataJsonSchema, - ) -> Result { - if schema == MetadataJsonSchema::BasicConversions { - match hex_string_to_bytes(&s) { - Some(bytes) => TransactionMetadatum::new_bytes(bytes), - None => TransactionMetadatum::new_text(s), - } - } else { - TransactionMetadatum::new_text(s) - } - } - fn encode_array( - json_arr: Vec, - schema: MetadataJsonSchema, - ) -> Result { - let mut arr = MetadataList::new(); - for value in json_arr { - arr.add(&encode_json_value_to_metadatum(value, schema)?); - } - Ok(TransactionMetadatum::new_list(&arr)) - } - match schema { - MetadataJsonSchema::NoConversions | MetadataJsonSchema::BasicConversions => match value { - Value::Null => Err(JsError::from_str("null not allowed in metadata")), - Value::Bool(_) => Err(JsError::from_str("bools not allowed in metadata")), - Value::Number(x) => encode_number(x), - Value::String(s) => encode_string(s, schema), - Value::Array(json_arr) => encode_array(json_arr, schema), - Value::Object(json_obj) => { - let mut map = MetadataMap::new(); - for (raw_key, value) in json_obj { - let key = if schema == MetadataJsonSchema::BasicConversions { - match raw_key.parse::() { - Ok(x) => TransactionMetadatum::new_int(&Int(x)), - Err(_) => encode_string(raw_key, schema)?, - } - } else { - TransactionMetadatum::new_text(raw_key)? - }; - map.insert(&key, &encode_json_value_to_metadatum(value, schema)?); - } - Ok(TransactionMetadatum::new_map(&map)) - } - }, - // we rely on tagged objects to control parsing here instead - MetadataJsonSchema::DetailedSchema => match value { - Value::Object(obj) if obj.len() == 1 => { - let (k, v) = obj.into_iter().next().unwrap(); - fn tag_mismatch() -> JsError { - JsError::from_str("key does not match type") - } - match k.as_str() { - "int" => match v { - Value::Number(x) => encode_number(x), - _ => Err(tag_mismatch()), - }, - "string" => { - encode_string(v.as_str().ok_or_else(tag_mismatch)?.to_owned(), schema) - } - "bytes" => match hex::decode(v.as_str().ok_or_else(tag_mismatch)?) { - Ok(bytes) => TransactionMetadatum::new_bytes(bytes), - Err(_) => Err(JsError::from_str( - "invalid hex string in tagged byte-object", - )), - }, - "list" => encode_array(v.as_array().ok_or_else(tag_mismatch)?.clone(), schema), - "map" => { - let mut map = MetadataMap::new(); - fn map_entry_err() -> JsError { - JsError::from_str("entry format in detailed schema map object not correct. Needs to be of form {\"k\": \"key\", \"v\": value}") - } - for entry in v.as_array().ok_or_else(tag_mismatch)? { - let entry_obj = entry.as_object().ok_or_else(map_entry_err)?; - let raw_key = entry_obj.get("k").ok_or_else(map_entry_err)?; - let value = entry_obj.get("v").ok_or_else(map_entry_err)?; - let key = encode_json_value_to_metadatum(raw_key.clone(), schema)?; - map.insert( - &key, - &encode_json_value_to_metadatum(value.clone(), schema)?, - ); - } - Ok(TransactionMetadatum::new_map(&map)) - } - invalid_key => Err(JsError::from_str(&format!( - "key '{}' in tagged object not valid", - invalid_key - ))), - } - } - _ => Err(JsError::from_str( - "DetailedSchema requires types to be tagged objects", - )), - }, - } -} - -// Converts Metadata to JSON according to MetadataJsonSchema -#[wasm_bindgen] -pub fn decode_metadatum_to_json_str( - metadatum: &TransactionMetadatum, - schema: MetadataJsonSchema, -) -> Result { - let value = decode_metadatum_to_json_value(metadatum, schema)?; - serde_json::to_string(&value).map_err(|e| JsError::from_str(&e.to_string())) -} - -pub fn decode_metadatum_to_json_value( - metadatum: &TransactionMetadatum, - schema: MetadataJsonSchema, -) -> Result { - use serde_json::Value; - use std::convert::TryFrom; - fn decode_key( - key: &TransactionMetadatum, - schema: MetadataJsonSchema, - ) -> Result { - match &key.0 { - TransactionMetadatumEnum::Text(s) => Ok(s.clone()), - TransactionMetadatumEnum::Bytes(b) if schema != MetadataJsonSchema::NoConversions => { - Ok(bytes_to_hex_string(b.as_ref())) - } - TransactionMetadatumEnum::Int(i) if schema != MetadataJsonSchema::NoConversions => { - let int_str = if i.0 >= 0 { - u64::try_from(i.0).map(|x| x.to_string()) - } else { - i64::try_from(i.0).map(|x| x.to_string()) - }; - int_str.map_err(|e| JsError::from_str(&e.to_string())) - } - TransactionMetadatumEnum::MetadataList(list) - if schema == MetadataJsonSchema::DetailedSchema => - { - decode_metadatum_to_json_str(&TransactionMetadatum::new_list(&list), schema) - } - TransactionMetadatumEnum::MetadataMap(map) - if schema == MetadataJsonSchema::DetailedSchema => - { - decode_metadatum_to_json_str(&TransactionMetadatum::new_map(&map), schema) - } - _ => Err(JsError::from_str(&format!( - "key type {:?} not allowed in JSON under specified schema", - key.0 - ))), - } - } - let (type_key, value) = match &metadatum.0 { - TransactionMetadatumEnum::MetadataMap(map) => match schema { - MetadataJsonSchema::NoConversions | MetadataJsonSchema::BasicConversions => { - // treats maps directly as JSON maps - let mut json_map = serde_json::map::Map::with_capacity(map.len()); - for (key, value) in map.0.iter() { - json_map.insert( - decode_key(key, schema)?, - decode_metadatum_to_json_value(value, schema)?, - ); - } - ("map", Value::from(json_map)) - } - - MetadataJsonSchema::DetailedSchema => ( - "map", - Value::from( - map.0 - .iter() - .map(|(key, value)| { - // must encode maps as JSON lists of objects with k/v keys - // also in these schemas we support more key types than strings - let k = decode_metadatum_to_json_value(key, schema)?; - let v = decode_metadatum_to_json_value(value, schema)?; - let mut kv_obj = serde_json::map::Map::with_capacity(2); - kv_obj.insert(String::from("k"), Value::from(k)); - kv_obj.insert(String::from("v"), v); - Ok(Value::from(kv_obj)) - }) - .collect::, JsError>>()?, - ), - ), - }, - TransactionMetadatumEnum::MetadataList(arr) => ( - "list", - Value::from( - arr.0 - .iter() - .map(|e| decode_metadatum_to_json_value(e, schema)) - .collect::, JsError>>()?, - ), - ), - TransactionMetadatumEnum::Int(x) => ( - "int", - if x.0 >= 0 { - Value::from(u64::try_from(x.0).map_err(|e| JsError::from_str(&e.to_string()))?) - } else { - Value::from(i64::try_from(x.0).map_err(|e| JsError::from_str(&e.to_string()))?) - }, - ), - TransactionMetadatumEnum::Bytes(bytes) => ( - "bytes", - match schema { - MetadataJsonSchema::NoConversions => Err(JsError::from_str( - "bytes not allowed in JSON in specified schema", - )), - // 0x prefix - MetadataJsonSchema::BasicConversions => { - Ok(Value::from(bytes_to_hex_string(bytes.as_ref()))) - } - // no prefix - MetadataJsonSchema::DetailedSchema => Ok(Value::from(hex::encode(bytes))), - }?, - ), - TransactionMetadatumEnum::Text(s) => ("string", Value::from(s.clone())), - }; - // potentially wrap value in a keyed map to represent more types - if supports_tagged_values(schema) { - let mut wrapper = serde_json::map::Map::with_capacity(1); - wrapper.insert(String::from(type_key), value); - Ok(Value::from(wrapper)) - } else { - Ok(value) - } -} - -// serialization -impl cbor_event::se::Serialize for MetadataMap { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; - for (key, value) in &self.0 { - key.serialize(serializer)?; - value.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for MetadataMap { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut table = LinkedHashMap::new(); - let mut entries: Vec<(TransactionMetadatum, TransactionMetadatum)> = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.map()?; - while match len { - cbor_event::Len::Len(n) => entries.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - let key = TransactionMetadatum::deserialize(raw)?; - let value = TransactionMetadatum::deserialize(raw)?; - entries.push((key.clone(), value)); - } - Ok(()) - })() - .map_err(|e| e.annotate("MetadataMap"))?; - entries.iter().for_each(|(k, v)| { - if table.insert(k.clone(), v.clone()).is_some() { - // Turns out this is totally possible on the actual blockchain - // return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from("some complicated/unsupported type"))).into()); - } - }); - Ok(Self(table)) - } -} - -impl cbor_event::se::Serialize for MetadataList { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for MetadataList { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(TransactionMetadatum::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("MetadataList"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for TransactionMetadatumEnum { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - match self { - TransactionMetadatumEnum::MetadataMap(x) => x.serialize(serializer), - TransactionMetadatumEnum::MetadataList(x) => x.serialize(serializer), - TransactionMetadatumEnum::Int(x) => x.serialize(serializer), - TransactionMetadatumEnum::Bytes(x) => serializer.write_bytes(&x), - TransactionMetadatumEnum::Text(x) => serializer.write_text(&x), - } - } -} - -impl Deserialize for TransactionMetadatumEnum { - fn deserialize(raw: &mut Deserializer) -> Result { - match raw.cbor_type()? { - CBORType::Array => { - MetadataList::deserialize(raw).map(TransactionMetadatumEnum::MetadataList) - } - CBORType::Map => { - MetadataMap::deserialize(raw).map(TransactionMetadatumEnum::MetadataMap) - } - CBORType::Bytes => TransactionMetadatum::new_bytes(raw.bytes()?) - .map(|m| m.0) - .map_err(|e| DeserializeFailure::Metadata(e).into()), - CBORType::Text => TransactionMetadatum::new_text(raw.text()?) - .map(|m| m.0) - .map_err(|e| DeserializeFailure::Metadata(e).into()), - CBORType::UnsignedInteger | CBORType::NegativeInteger => { - Int::deserialize(raw).map(TransactionMetadatumEnum::Int) - } - _ => Err(DeserializeError::new( - "TransactionMetadatumEnum", - DeserializeFailure::NoVariantMatched.into(), - )), - } - } -} - -impl cbor_event::se::Serialize for TransactionMetadatum { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - self.0.serialize(serializer) - } -} - -impl Deserialize for TransactionMetadatum { - fn deserialize(raw: &mut Deserializer) -> Result { - Ok(Self(TransactionMetadatumEnum::deserialize(raw)?)) - } -} - -impl cbor_event::se::Serialize for TransactionMetadatumLabels { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for TransactionMetadatumLabels { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(TransactionMetadatumLabel::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("TransactionMetadatumLabels"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for GeneralTransactionMetadata { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; - for (key, value) in &self.0 { - key.serialize(serializer)?; - value.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for GeneralTransactionMetadata { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut table = LinkedHashMap::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.map()?; - while match len { - cbor_event::Len::Len(n) => table.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - let key = TransactionMetadatumLabel::deserialize(raw)?; - let value = TransactionMetadatum::deserialize(raw)?; - if table.insert(key.clone(), value).is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( - "some complicated/unsupported type", - ))) - .into()); - } - } - Ok(()) - })() - .map_err(|e| e.annotate("GeneralTransactionMetadata"))?; - Ok(Self(table)) - } -} - -impl cbor_event::se::Serialize for AuxiliaryData { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - // we still serialize using the shelley-mary era format as it is still supported - // and it takes up less space on-chain so this should be better for scaling. - // Plus the code was already written for shelley-mary anyway - if !self.prefer_alonzo_format && self.metadata.is_some() && self.plutus_scripts.is_none() { - match &self.native_scripts() { - Some(native_scripts) => { - serializer.write_array(cbor_event::Len::Len(2))?; - self.metadata.as_ref().unwrap().serialize(serializer)?; - native_scripts.serialize(serializer) - } - None => self.metadata.as_ref().unwrap().serialize(serializer), - } - } else { - let plutus_added_length = match &self.plutus_scripts { - Some(scripts) => 1 + (scripts.has_version(&Language::new_plutus_v2()) as u64), - _ => 0, - }; - // new format with plutus support - serializer.write_tag(259u64)?; - serializer.write_map(cbor_event::Len::Len( - opt64(&self.metadata) + opt64(&self.native_scripts) + plutus_added_length, - ))?; - if let Some(metadata) = &self.metadata { - serializer.write_unsigned_integer(0)?; - metadata.serialize(serializer)?; - } - if let Some(native_scripts) = &self.native_scripts { - serializer.write_unsigned_integer(1)?; - native_scripts.serialize(serializer)?; - } - if let Some(plutus_scripts) = &self.plutus_scripts { - serializer.write_unsigned_integer(2)?; - plutus_scripts - .by_version(&Language::new_plutus_v1()) - .serialize(serializer)?; - let v2 = plutus_scripts.by_version(&Language::new_plutus_v2()); - if v2.len() > 0 { - serializer.write_unsigned_integer(3)?; - v2.serialize(serializer)?; - } - } - Ok(serializer) - } - } -} - -impl Deserialize for AuxiliaryData { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - match raw.cbor_type()? { - // alonzo format - CBORType::Tag => { - let tag = raw.tag()?; - if tag != 259 { - return Err(DeserializeError::new( - "AuxiliaryData", - DeserializeFailure::TagMismatch { - found: tag, - expected: 259, - }, - )); - } - let len = raw.map()?; - let mut read_len = CBORReadLen::new(len); - let mut metadata = None; - let mut native_scripts = None; - let mut plutus_scripts_v1 = None; - let mut plutus_scripts_v2 = None; - let mut read = 0; - while match len { - cbor_event::Len::Len(n) => read < n as usize, - cbor_event::Len::Indefinite => true, - } { - match raw.cbor_type()? { - CBORType::UnsignedInteger => match raw.unsigned_integer()? { - 0 => { - if metadata.is_some() { - return Err( - DeserializeFailure::DuplicateKey(Key::Uint(0)).into() - ); - } - metadata = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(GeneralTransactionMetadata::deserialize(raw)?) - })() - .map_err(|e| e.annotate("metadata"))?, - ); - } - 1 => { - if native_scripts.is_some() { - return Err( - DeserializeFailure::DuplicateKey(Key::Uint(1)).into() - ); - } - native_scripts = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(NativeScripts::deserialize(raw)?) - })() - .map_err(|e| e.annotate("native_scripts"))?, - ); - } - 2 => { - if plutus_scripts_v1.is_some() { - return Err( - DeserializeFailure::DuplicateKey(Key::Uint(2)).into() - ); - } - plutus_scripts_v1 = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(PlutusScripts::deserialize(raw)?) - })() - .map_err(|e| e.annotate("plutus_scripts_v1"))?, - ); - } - 3 => { - if plutus_scripts_v2.is_some() { - return Err( - DeserializeFailure::DuplicateKey(Key::Uint(3)).into() - ); - } - plutus_scripts_v2 = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(PlutusScripts::deserialize(raw)? - .map_as_version(&Language::new_plutus_v2())) - })() - .map_err(|e| e.annotate("plutus_scripts_v2"))?, - ); - } - unknown_key => { - return Err(DeserializeFailure::UnknownKey(Key::Uint( - unknown_key, - )) - .into()) - } - }, - CBORType::Text => match raw.text()?.as_str() { - unknown_key => { - return Err(DeserializeFailure::UnknownKey(Key::Str( - unknown_key.to_owned(), - )) - .into()) - } - }, - CBORType::Special => match len { - cbor_event::Len::Len(_) => { - return Err(DeserializeFailure::BreakInDefiniteLen.into()) - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => break, - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - }, - other_type => { - return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()) - } - } - read += 1; - } - read_len.finish()?; - let plutus_scripts = match (plutus_scripts_v1, plutus_scripts_v2) { - (Some(v1), Some(v2)) => Some(v1.merge(&v2)), - (Some(v1), _) => Some(v1), - (_, Some(v2)) => Some(v2), - _ => None, - }; - Ok(Self { - metadata, - native_scripts, - plutus_scripts, - prefer_alonzo_format: true, - }) - } - // shelley mary format (still valid for alonzo) - CBORType::Array => { - let len = raw.array()?; - let mut read_len = CBORReadLen::new(len); - read_len.read_elems(2)?; - let metadata = (|| -> Result<_, DeserializeError> { - Ok(GeneralTransactionMetadata::deserialize(raw)?) - })() - .map_err(|e| e.annotate("metadata"))?; - let native_scripts = (|| -> Result<_, DeserializeError> { - Ok(NativeScripts::deserialize(raw)?) - })() - .map_err(|e| e.annotate("native_scripts"))?; - match len { - cbor_event::Len::Len(_) => (), - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => (), - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - Ok(Self { - metadata: Some(metadata), - native_scripts: Some(native_scripts), - plutus_scripts: None, - prefer_alonzo_format: false, - }) - } - // shelley pre-mary format (still valid for alonzo + mary) - CBORType::Map => Ok(Self { - metadata: Some( - GeneralTransactionMetadata::deserialize(raw) - .map_err(|e| e.annotate("metadata"))?, - ), - native_scripts: None, - plutus_scripts: None, - prefer_alonzo_format: false, - }), - _ => return Err(DeserializeFailure::NoVariantMatched)?, - } - })() - .map_err(|e| e.annotate("AuxiliaryData")) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn binary_encoding() { - let input_bytes = (0..1000).map(|x| x as u8).collect::>(); - let metadata = encode_arbitrary_bytes_as_metadatum(input_bytes.as_ref()); - let output_bytes = decode_arbitrary_bytes_from_metadatum(&metadata).expect("decode failed"); - assert_eq!(input_bytes, output_bytes); - } - - #[test] - fn json_encoding_no_conversions() { - let input_str = String::from("{\"receiver_id\": \"SJKdj34k3jjKFDKfjFUDfdjkfd\",\"sender_id\": \"jkfdsufjdk34h3Sdfjdhfduf873\",\"comment\": \"happy birthday\",\"tags\": [0, 264, -1024, 32]}"); - let metadata = - encode_json_str_to_metadatum(input_str.clone(), MetadataJsonSchema::NoConversions) - .expect("encode failed"); - let map = metadata.as_map().unwrap(); - assert_eq!( - map.get_str("receiver_id").unwrap().as_text().unwrap(), - "SJKdj34k3jjKFDKfjFUDfdjkfd" - ); - assert_eq!( - map.get_str("sender_id").unwrap().as_text().unwrap(), - "jkfdsufjdk34h3Sdfjdhfduf873" - ); - assert_eq!( - map.get_str("comment").unwrap().as_text().unwrap(), - "happy birthday" - ); - let tags = map.get_str("tags").unwrap().as_list().unwrap(); - let tags_i32 = tags - .0 - .iter() - .map(|md| md.as_int().unwrap().as_i32_or_fail().unwrap()) - .collect::>(); - assert_eq!(tags_i32, vec![0, 264, -1024, 32]); - let output_str = decode_metadatum_to_json_str(&metadata, MetadataJsonSchema::NoConversions) - .expect("decode failed"); - let input_json: serde_json::Value = serde_json::from_str(&input_str).unwrap(); - let output_json: serde_json::Value = serde_json::from_str(&output_str).unwrap(); - assert_eq!(input_json, output_json); - } - - #[test] - fn json_encoding_basic() { - let input_str = String::from( - "{\"0x8badf00d\": \"0xdeadbeef\",\"9\": 5,\"obj\": {\"a\":[{\"5\": 2},{}]}}", - ); - let metadata = - encode_json_str_to_metadatum(input_str.clone(), MetadataJsonSchema::BasicConversions) - .expect("encode failed"); - json_encoding_check_example_metadatum(&metadata); - let output_str = - decode_metadatum_to_json_str(&metadata, MetadataJsonSchema::BasicConversions) - .expect("decode failed"); - let input_json: serde_json::Value = serde_json::from_str(&input_str).unwrap(); - let output_json: serde_json::Value = serde_json::from_str(&output_str).unwrap(); - assert_eq!(input_json, output_json); - } - - #[test] - fn json_encoding_detailed() { - let input_str = String::from( - "{\"map\":[ - { - \"k\":{\"bytes\":\"8badf00d\"}, - \"v\":{\"bytes\":\"deadbeef\"} - }, - { - \"k\":{\"int\":9}, - \"v\":{\"int\":5} - }, - { - \"k\":{\"string\":\"obj\"}, - \"v\":{\"map\":[ - { - \"k\":{\"string\":\"a\"}, - \"v\":{\"list\":[ - {\"map\":[ - { - \"k\":{\"int\":5}, - \"v\":{\"int\":2} - } - ]}, - {\"map\":[ - ]} - ]} - } - ]} - } - ]}", - ); - let metadata = - encode_json_str_to_metadatum(input_str.clone(), MetadataJsonSchema::DetailedSchema) - .expect("encode failed"); - json_encoding_check_example_metadatum(&metadata); - let output_str = - decode_metadatum_to_json_str(&metadata, MetadataJsonSchema::DetailedSchema) - .expect("decode failed"); - let input_json: serde_json::Value = serde_json::from_str(&input_str).unwrap(); - let output_json: serde_json::Value = serde_json::from_str(&output_str).unwrap(); - assert_eq!(input_json, output_json); - } - - fn json_encoding_check_example_metadatum(metadata: &TransactionMetadatum) { - let map = metadata.as_map().unwrap(); - assert_eq!( - map.get(&TransactionMetadatum::new_bytes(hex::decode("8badf00d").unwrap()).unwrap()) - .unwrap() - .as_bytes() - .unwrap(), - hex::decode("deadbeef").unwrap() - ); - assert_eq!( - map.get_i32(9) - .unwrap() - .as_int() - .unwrap() - .as_i32_or_fail() - .unwrap(), - 5 - ); - let inner_map = map.get_str("obj").unwrap().as_map().unwrap(); - let a = inner_map.get_str("a").unwrap().as_list().unwrap(); - let a1 = a.get(0).as_map().unwrap(); - assert_eq!( - a1.get_i32(5) - .unwrap() - .as_int() - .unwrap() - .as_i32_or_fail() - .unwrap(), - 2 - ); - let a2 = a.get(1).as_map().unwrap(); - assert_eq!(a2.keys().len(), 0); - } - - #[test] - fn json_encoding_detailed_complex_key() { - let input_str = String::from( - "{\"map\":[ - { - \"k\":{\"list\":[ - {\"map\": [ - { - \"k\": {\"int\": 5}, - \"v\": {\"int\": -7} - }, - { - \"k\": {\"string\": \"hello\"}, - \"v\": {\"string\": \"world\"} - } - ]}, - {\"bytes\": \"ff00ff00\"} - ]}, - \"v\":{\"int\":5} - } - ]}", - ); - let metadata = - encode_json_str_to_metadatum(input_str.clone(), MetadataJsonSchema::DetailedSchema) - .expect("encode failed"); - - let map = metadata.as_map().unwrap(); - let key = map.keys().get(0); - assert_eq!( - map.get(&key) - .unwrap() - .as_int() - .unwrap() - .as_i32_or_fail() - .unwrap(), - 5 - ); - let key_list = key.as_list().unwrap(); - assert_eq!(key_list.len(), 2); - let key_map = key_list.get(0).as_map().unwrap(); - assert_eq!( - key_map - .get_i32(5) - .unwrap() - .as_int() - .unwrap() - .as_i32_or_fail() - .unwrap(), - -7 - ); - assert_eq!( - key_map.get_str("hello").unwrap().as_text().unwrap(), - "world" - ); - let key_bytes = key_list.get(1).as_bytes().unwrap(); - assert_eq!(key_bytes, hex::decode("ff00ff00").unwrap()); - - let output_str = - decode_metadatum_to_json_str(&metadata, MetadataJsonSchema::DetailedSchema) - .expect("decode failed"); - let input_json: serde_json::Value = serde_json::from_str(&input_str).unwrap(); - let output_json: serde_json::Value = serde_json::from_str(&output_str).unwrap(); - assert_eq!(input_json, output_json); - } - - #[test] - fn metadata_serialize() { - let mut gmd = GeneralTransactionMetadata::new(); - let mdatum = TransactionMetadatum::new_text(String::from("string md")).unwrap(); - gmd.insert(&to_bignum(100), &mdatum); - let mut aux_data = AuxiliaryData::new(); - // alonzo (empty) - let ad0_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); - assert_eq!(aux_data.to_bytes(), ad0_deser.to_bytes()); - // pre-mary shelley - aux_data.set_metadata(&gmd); - let ad1_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); - assert_eq!(aux_data.to_bytes(), ad1_deser.to_bytes()); - // mary shelley - let mut native_scripts = NativeScripts::new(); - native_scripts.add(&NativeScript::new_timelock_start(&TimelockStart::new(20))); - aux_data.set_native_scripts(&native_scripts); - let ad2_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); - assert_eq!(aux_data.to_bytes(), ad2_deser.to_bytes()); - // alonzo - let mut plutus_scripts = PlutusScripts::new(); - plutus_scripts.add(&PlutusScript::new([61u8; 29].to_vec())); - aux_data.set_plutus_scripts(&plutus_scripts); - let ad3_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); - assert_eq!(aux_data.to_bytes(), ad3_deser.to_bytes()); - } - - #[test] - fn alonzo_metadata_round_trip() { - let bytes_alonzo = hex::decode("d90103a100a1186469737472696e67206d64").unwrap(); - let aux_alonzo = AuxiliaryData::from_bytes(bytes_alonzo.clone()).unwrap(); - assert!(aux_alonzo.prefer_alonzo_format); - assert_eq!(aux_alonzo.to_bytes(), bytes_alonzo); - - let bytes_pre_alonzo = hex::decode("a1186469737472696e67206d64").unwrap(); - let aux_pre_alonzo = AuxiliaryData::from_bytes(bytes_pre_alonzo.clone()).unwrap(); - assert!(!aux_pre_alonzo.prefer_alonzo_format); - assert_eq!(aux_pre_alonzo.to_bytes(), bytes_pre_alonzo); - } - - #[test] - fn metadatum_map_duplicate_keys() { - let bytes = hex::decode("a105a4781b232323232323232323232323232323232323232323232323232323827840232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323237840232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323236e232323232323232323232323232382a36f2323232323232323232323232323236a323030302d30312d303166232323232323784023232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323712323232323232323232323232323232323784023232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323a36f2323232323232323232323232323236a323030302d30312d303166232323232323784023232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323712323232323232323232323232323232323784023232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323752323232323232323232323232323232323232323236a323030302d30312d3031752323232323232323232323232323232323232323236a323030302d30312d3031").unwrap(); - TransactionMetadatum::from_bytes(bytes).unwrap(); - } - - #[test] - fn test_auxiliary_data_roundtrip() { - fn auxiliary_data_roundtrip(plutus_scripts: &PlutusScripts) { - let mut aux = AuxiliaryData::new(); - let mut metadata = GeneralTransactionMetadata::new(); - metadata.insert( - &to_bignum(42), - &encode_json_str_to_metadatum( - "{ \"test\": 148 }".to_string(), - MetadataJsonSchema::BasicConversions, - ) - .unwrap(), - ); - aux.set_metadata(&metadata); - aux.set_native_scripts(&NativeScripts::from(vec![ - NativeScript::new_timelock_start(&TimelockStart::new(1234556)), - ])); - aux.set_plutus_scripts(plutus_scripts); - assert_eq!(AuxiliaryData::from_bytes(aux.to_bytes()).unwrap(), aux); - } - - let bytes = hex::decode("4e4d01000033222220051200120011").unwrap(); - let script_v1 = PlutusScript::from_bytes(bytes.clone()).unwrap(); - let script_v2 = PlutusScript::from_bytes_v2(bytes.clone()).unwrap(); - - auxiliary_data_roundtrip(&PlutusScripts(vec![])); - auxiliary_data_roundtrip(&PlutusScripts(vec![script_v1.clone()])); - auxiliary_data_roundtrip(&PlutusScripts(vec![script_v2.clone()])); - auxiliary_data_roundtrip(&PlutusScripts(vec![script_v1.clone(), script_v2.clone()])); - } -} diff --git a/rust/src/plutus.rs b/rust/src/plutus.rs deleted file mode 100644 index 9e751c4a..00000000 --- a/rust/src/plutus.rs +++ /dev/null @@ -1,2602 +0,0 @@ -use std::hash::Hash; -use super::*; -use std::io::{BufRead, Seek, Write}; -use linked_hash_map::LinkedHashMap; -use core::hash::Hasher; - -// This library was code-generated using an experimental CDDL to rust tool: -// https://github.com/Emurgo/cddl-codegen - -use cbor_event::{ - self, - de::Deserializer, - se::{Serialize, Serializer}, -}; - -use schemars::JsonSchema; - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct PlutusScript { - bytes: Vec, - language: LanguageKind, -} - -to_from_bytes!(PlutusScript); - -#[wasm_bindgen] -impl PlutusScript { - /** - * Creates a new Plutus script from the RAW bytes of the compiled script. - * This does NOT include any CBOR encoding around these bytes (e.g. from "cborBytes" in cardano-cli) - * If you creating this from those you should use PlutusScript::from_bytes() instead. - */ - pub fn new(bytes: Vec) -> PlutusScript { - Self::new_with_version(bytes, &Language::new_plutus_v1()) - } - - /** - * Creates a new Plutus script from the RAW bytes of the compiled script. - * This does NOT include any CBOR encoding around these bytes (e.g. from "cborBytes" in cardano-cli) - * If you creating this from those you should use PlutusScript::from_bytes() instead. - */ - pub fn new_v2(bytes: Vec) -> PlutusScript { - Self::new_with_version(bytes, &Language::new_plutus_v2()) - } - - /** - * Creates a new Plutus script from the RAW bytes of the compiled script. - * This does NOT include any CBOR encoding around these bytes (e.g. from "cborBytes" in cardano-cli) - * If you creating this from those you should use PlutusScript::from_bytes() instead. - */ - pub fn new_with_version(bytes: Vec, language: &Language) -> PlutusScript { - Self { - bytes, - language: language.0.clone(), - } - } - - /** - * The raw bytes of this compiled Plutus script. - * If you need "cborBytes" for cardano-cli use PlutusScript::to_bytes() instead. - */ - pub fn bytes(&self) -> Vec { - self.bytes.clone() - } - - /// Same as `.from_bytes` but will consider the script as requiring the Plutus Language V2 - pub fn from_bytes_v2(bytes: Vec) -> Result { - Self::from_bytes_with_version(bytes, &Language::new_plutus_v2()) - } - - /// Same as `.from_bytes` but will consider the script as requiring the specified language version - pub fn from_bytes_with_version( - bytes: Vec, - language: &Language, - ) -> Result { - Ok(Self::new_with_version( - Self::from_bytes(bytes)?.bytes, - language, - )) - } - - /// Same as .from_hex but will consider the script as requiring the specified language version - pub fn from_hex_with_version( - hex_str: &str, - language: &Language, - ) -> Result { - Ok(Self::new_with_version( - Self::from_hex(hex_str)?.bytes, - language, - )) - } - - pub fn hash(&self) -> ScriptHash { - let mut bytes = Vec::with_capacity(self.bytes.len() + 1); - // https://github.com/input-output-hk/cardano-ledger/blob/master/eras/babbage/test-suite/cddl-files/babbage.cddl#L413 - bytes.extend_from_slice(&vec![self.script_namespace() as u8]); - bytes.extend_from_slice(&self.bytes); - ScriptHash::from(blake2b224(bytes.as_ref())) - } - - pub fn language_version(&self) -> Language { - Language(self.language.clone()) - } - - pub(crate) fn script_namespace(&self) -> ScriptHashNamespace { - match self.language { - LanguageKind::PlutusV1 => ScriptHashNamespace::PlutusScript, - LanguageKind::PlutusV2 => ScriptHashNamespace::PlutusScriptV2, - } - } - - pub(crate) fn clone_as_version(&self, language: &Language) -> PlutusScript { - Self::new_with_version(self.bytes.clone(), language) - } -} - -impl serde::Serialize for PlutusScript { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&hex::encode(&self.bytes)) - } -} - -impl<'de> serde::de::Deserialize<'de> for PlutusScript { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let s = ::deserialize(deserializer)?; - hex::decode(&s) - .map(|bytes| PlutusScript::new(bytes)) - .map_err(|_err| { - serde::de::Error::invalid_value( - serde::de::Unexpected::Str(&s), - &"PlutusScript as hex string e.g. F8AB28C2 (without CBOR bytes tag)", - ) - }) - } -} - -impl JsonSchema for PlutusScript { - fn schema_name() -> String { - String::from("PlutusScript") - } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - String::json_schema(gen) - } - fn is_referenceable() -> bool { - String::is_referenceable() - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct PlutusScripts(pub(crate) Vec); - -impl_to_from!(PlutusScripts); - -#[wasm_bindgen] -impl PlutusScripts { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> PlutusScript { - self.0[index].clone() - } - - pub fn add(&mut self, elem: &PlutusScript) { - self.0.push(elem.clone()); - } - - pub(crate) fn by_version(&self, language: &Language) -> PlutusScripts { - PlutusScripts( - self.0 - .iter() - .filter(|s| s.language_version().eq(language)) - .map(|s| s.clone()) - .collect(), - ) - } - - pub(crate) fn has_version(&self, language: &Language) -> bool { - self.0.iter().any(|s| s.language_version().eq(language)) - } - - pub(crate) fn merge(&self, other: &PlutusScripts) -> PlutusScripts { - let mut res = self.clone(); - for s in &other.0 { - res.add(s); - } - res - } - - pub(crate) fn map_as_version(&self, language: &Language) -> PlutusScripts { - let mut res = PlutusScripts::new(); - for s in &self.0 { - res.add(&s.clone_as_version(language)); - } - res - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] -pub struct ConstrPlutusData { - alternative: BigNum, - data: PlutusList, -} - -to_from_bytes!(ConstrPlutusData); - -#[wasm_bindgen] -impl ConstrPlutusData { - pub fn alternative(&self) -> BigNum { - self.alternative.clone() - } - - pub fn data(&self) -> PlutusList { - self.data.clone() - } - - pub fn new(alternative: &BigNum, data: &PlutusList) -> Self { - Self { - alternative: alternative.clone(), - data: data.clone(), - } - } -} - -impl ConstrPlutusData { - // see: https://github.com/input-output-hk/plutus/blob/1f31e640e8a258185db01fa899da63f9018c0e85/plutus-core/plutus-core/src/PlutusCore/Data.hs#L61 - // We don't directly serialize the alternative in the tag, instead the scheme is: - // - Alternatives 0-6 -> tags 121-127, followed by the arguments in a list - // - Alternatives 7-127 -> tags 1280-1400, followed by the arguments in a list - // - Any alternatives, including those that don't fit in the above -> tag 102 followed by a list containing - // an unsigned integer for the actual alternative, and then the arguments in a (nested!) list. - const GENERAL_FORM_TAG: u64 = 102; - - // None -> needs general tag serialization, not compact - fn alternative_to_compact_cbor_tag(alt: u64) -> Option { - if alt <= 6 { - Some(121 + alt) - } else if alt >= 7 && alt <= 127 { - Some(1280 - 7 + alt) - } else { - None - } - } - - // None -> General tag(=102) OR Invalid CBOR tag for this scheme - fn compact_cbor_tag_to_alternative(cbor_tag: u64) -> Option { - if cbor_tag >= 121 && cbor_tag <= 127 { - Some(cbor_tag - 121) - } else if cbor_tag >= 1280 && cbor_tag <= 1400 { - Some(cbor_tag - 1280 + 7) - } else { - None - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct CostModel(Vec); - -impl_to_from!(CostModel); - -#[wasm_bindgen] -impl CostModel { - /// Creates a new CostModels instance of an unrestricted length - pub fn new() -> Self { - Self(Vec::new()) - } - - /// Sets the cost at the specified index to the specified value. - /// In case the operation index is larger than the previous largest used index, - /// it will fill any inbetween indexes with zeroes - pub fn set(&mut self, operation: usize, cost: &Int) -> Result { - let len = self.0.len(); - let idx = operation.clone(); - if idx >= len { - for _ in 0..(idx - len + 1) { - self.0.push(Int::new_i32(0)); - } - } - let old = self.0[idx].clone(); - self.0[idx] = cost.clone(); - Ok(old) - } - - pub fn get(&self, operation: usize) -> Result { - let max = self.0.len(); - if operation >= max { - return Err(JsError::from_str(&format!( - "CostModel operation {} out of bounds. Max is {}", - operation, max - ))); - } - Ok(self.0[operation].clone()) - } - - pub fn len(&self) -> usize { - self.0.len() - } -} - -impl From> for CostModel { - fn from(values: Vec) -> Self { - CostModel(values.iter().map(|x| Int(*x)).collect()) - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct Costmdls(std::collections::BTreeMap); - -impl_to_from!(Costmdls); - -#[wasm_bindgen] -impl Costmdls { - pub fn new() -> Self { - Self(std::collections::BTreeMap::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn insert(&mut self, key: &Language, value: &CostModel) -> Option { - self.0.insert(key.clone(), value.clone()) - } - - pub fn get(&self, key: &Language) -> Option { - self.0.get(key).map(|v| v.clone()) - } - - pub fn keys(&self) -> Languages { - Languages(self.0.iter().map(|(k, _v)| k.clone()).collect::>()) - } - - pub(crate) fn language_views_encoding(&self) -> Vec { - let mut serializer = Serializer::new_vec(); - fn key_len(l: &Language) -> usize { - if l.kind() == LanguageKind::PlutusV1 { - let mut serializer = Serializer::new_vec(); - serializer.write_bytes(l.to_bytes()).unwrap(); - return serializer.finalize().len(); - } - l.to_bytes().len() - } - let mut keys: Vec = self.0.iter().map(|(k, _v)| k.clone()).collect(); - // keys must be in canonical ordering first - keys.sort_by(|lhs, rhs| match key_len(lhs).cmp(&key_len(rhs)) { - std::cmp::Ordering::Equal => lhs.cmp(&rhs), - len_order => len_order, - }); - serializer - .write_map(cbor_event::Len::Len(self.0.len() as u64)) - .unwrap(); - for key in keys.iter() { - if key.kind() == LanguageKind::PlutusV1 { - serializer.write_bytes(key.to_bytes()).unwrap(); - let cost_model = self.0.get(&key).unwrap(); - // Due to a bug in the cardano-node input-output-hk/cardano-ledger-specs/issues/2512 - // we must use indefinite length serialization in this inner bytestring to match it - let mut cost_model_serializer = Serializer::new_vec(); - cost_model_serializer - .write_array(cbor_event::Len::Indefinite) - .unwrap(); - for cost in &cost_model.0 { - cost.serialize(&mut cost_model_serializer).unwrap(); - } - cost_model_serializer - .write_special(cbor_event::Special::Break) - .unwrap(); - serializer - .write_bytes(cost_model_serializer.finalize()) - .unwrap(); - } else { - serializer.serialize(key).unwrap(); - serializer.serialize(self.0.get(&key).unwrap()).unwrap(); - } - } - serializer.finalize() - } - - pub fn retain_language_versions(&self, languages: &Languages) -> Costmdls { - let mut result = Costmdls::new(); - for lang in &languages.0 { - match self.get(&lang) { - Some(costmodel) => { result.insert(&lang, &costmodel); }, - _ => {} - } - } - result - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct ExUnitPrices { - mem_price: SubCoin, - step_price: SubCoin, -} - -impl_to_from!(ExUnitPrices); - -#[wasm_bindgen] -impl ExUnitPrices { - pub fn mem_price(&self) -> SubCoin { - self.mem_price.clone() - } - - pub fn step_price(&self) -> SubCoin { - self.step_price.clone() - } - - pub fn new(mem_price: &SubCoin, step_price: &SubCoin) -> Self { - Self { - mem_price: mem_price.clone(), - step_price: step_price.clone(), - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct ExUnits { - mem: BigNum, - steps: BigNum, -} - -impl_to_from!(ExUnits); - -#[wasm_bindgen] -impl ExUnits { - pub fn mem(&self) -> BigNum { - self.mem.clone() - } - - pub fn steps(&self) -> BigNum { - self.steps.clone() - } - - pub fn new(mem: &BigNum, steps: &BigNum) -> Self { - Self { - mem: mem.clone(), - steps: steps.clone(), - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, - Copy, - Debug, - Eq, - Ord, - PartialEq, - PartialOrd, - serde::Serialize, - serde::Deserialize, - JsonSchema, -)] -pub enum LanguageKind { - PlutusV1 = 0, - PlutusV2 = 1, -} - -impl LanguageKind { - fn from_u64(x: u64) -> Option { - match x { - 0 => Some(LanguageKind::PlutusV1), - 1 => Some(LanguageKind::PlutusV2), - _ => None, - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, - Copy, - Debug, - Eq, - Ord, - PartialEq, - PartialOrd, - serde::Serialize, - serde::Deserialize, - JsonSchema, -)] -pub struct Language(LanguageKind); - -impl_to_from!(Language); - -#[wasm_bindgen] -impl Language { - pub fn new_plutus_v1() -> Self { - Self(LanguageKind::PlutusV1) - } - - pub fn new_plutus_v2() -> Self { - Self(LanguageKind::PlutusV2) - } - - pub fn kind(&self) -> LanguageKind { - self.0.clone() - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct Languages(pub(crate) Vec); - -#[wasm_bindgen] -impl Languages { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> Language { - self.0[index] - } - - pub fn add(&mut self, elem: Language) { - self.0.push(elem); - } - - pub fn list() -> Languages { - Languages(vec![Language::new_plutus_v1(), Language::new_plutus_v2()]) - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] -pub struct PlutusMap(LinkedHashMap); - -to_from_bytes!(PlutusMap); - -#[wasm_bindgen] -impl PlutusMap { - pub fn new() -> Self { - Self(LinkedHashMap::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn insert(&mut self, key: &PlutusData, value: &PlutusData) -> Option { - self.0.insert(key.clone(), value.clone()) - } - - pub fn get(&self, key: &PlutusData) -> Option { - self.0.get(key).map(|v| v.clone()) - } - - pub fn keys(&self) -> PlutusList { - PlutusList { - elems: self.0.iter().map(|(k, _v)| k.clone()).collect::>(), - definite_encoding: None, - } - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum PlutusDataKind { - ConstrPlutusData, - Map, - List, - Integer, - Bytes, -} - -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] -pub enum PlutusDataEnum { - ConstrPlutusData(ConstrPlutusData), - Map(PlutusMap), - List(PlutusList), - Integer(BigInt), - Bytes(Vec), -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Ord, PartialOrd)] -pub struct PlutusData { - datum: PlutusDataEnum, - // We should always preserve the original datums when deserialized as this is NOT canonicized - // before computing datum hashes. So this field stores the original bytes to re-use. - original_bytes: Option>, -} - -impl std::cmp::PartialEq for PlutusData { - fn eq(&self, other: &Self) -> bool { - self.datum.eq(&other.datum) - } -} - -impl Hash for PlutusData { - fn hash(&self, state: &mut H) { - self.datum.hash(state) - } -} - -impl std::cmp::Eq for PlutusData {} - -to_from_bytes!(PlutusData); - -#[wasm_bindgen] -impl PlutusData { - pub fn new_constr_plutus_data(constr_plutus_data: &ConstrPlutusData) -> Self { - Self { - datum: PlutusDataEnum::ConstrPlutusData(constr_plutus_data.clone()), - original_bytes: None, - } - } - - /// Same as `.new_constr_plutus_data` but creates constr with empty data list - pub fn new_empty_constr_plutus_data(alternative: &BigNum) -> Self { - Self::new_constr_plutus_data(&ConstrPlutusData::new(alternative, &PlutusList::new())) - } - - pub fn new_single_value_constr_plutus_data(alternative: &BigNum, plutus_data: &PlutusData) -> Self { - let mut list = PlutusList::new(); - list.add(plutus_data); - Self::new_constr_plutus_data(&ConstrPlutusData::new(alternative, &list)) - } - - pub fn new_map(map: &PlutusMap) -> Self { - Self { - datum: PlutusDataEnum::Map(map.clone()), - original_bytes: None, - } - } - - pub fn new_list(list: &PlutusList) -> Self { - Self { - datum: PlutusDataEnum::List(list.clone()), - original_bytes: None, - } - } - - pub fn new_integer(integer: &BigInt) -> Self { - Self { - datum: PlutusDataEnum::Integer(integer.clone()), - original_bytes: None, - } - } - - pub fn new_bytes(bytes: Vec) -> Self { - Self { - datum: PlutusDataEnum::Bytes(bytes), - original_bytes: None, - } - } - - pub fn kind(&self) -> PlutusDataKind { - match &self.datum { - PlutusDataEnum::ConstrPlutusData(_) => PlutusDataKind::ConstrPlutusData, - PlutusDataEnum::Map(_) => PlutusDataKind::Map, - PlutusDataEnum::List(_) => PlutusDataKind::List, - PlutusDataEnum::Integer(_) => PlutusDataKind::Integer, - PlutusDataEnum::Bytes(_) => PlutusDataKind::Bytes, - } - } - - pub fn as_constr_plutus_data(&self) -> Option { - match &self.datum { - PlutusDataEnum::ConstrPlutusData(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_map(&self) -> Option { - match &self.datum { - PlutusDataEnum::Map(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_list(&self) -> Option { - match &self.datum { - PlutusDataEnum::List(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_integer(&self) -> Option { - match &self.datum { - PlutusDataEnum::Integer(x) => Some(x.clone()), - _ => None, - } - } - - pub fn as_bytes(&self) -> Option> { - match &self.datum { - PlutusDataEnum::Bytes(x) => Some(x.clone()), - _ => None, - } - } - - pub fn to_json(&self, schema: PlutusDatumSchema) -> Result { - decode_plutus_datum_to_json_str(self, schema) - } - - pub fn from_json(json: &str, schema: PlutusDatumSchema) -> Result { - encode_json_str_to_plutus_datum(json, schema) - } - - pub fn from_address(address: &Address) -> Result { - let payment_cred = match &address.0 { - AddrType::Base(addr) => Ok(addr.payment_cred()), - AddrType::Enterprise(addr) => Ok(addr.payment_cred()), - AddrType::Ptr(addr) => Ok(addr.payment_cred()), - AddrType::Reward(addr) => Ok(addr.payment_cred()), - AddrType::Byron(_) => - Err(JsError::from_str("Cannot convert Byron address to PlutusData")), - }?; - - let staking_data = match &address.0 { - AddrType::Base(addr) => { - let staking_bytes_data = - PlutusData::from_stake_credential(&addr.stake_cred())?; - Some(PlutusData::new_single_value_constr_plutus_data( - &BigNum::from(0u32), - &staking_bytes_data, - )) - } - _ => None, - }; - - let pointer_data = match &address.0 { - AddrType::Ptr(addr) => - Some(PlutusData::from_pointer(&addr.stake_pointer())?), - _ => None, - }; - - let payment_data = PlutusData::from_stake_credential(&payment_cred)?; - let staking_optional_data = match (staking_data, pointer_data) { - (Some(_), Some(_)) => - Err(JsError::from_str("Address can't have both staking and pointer data")), - (Some(staking_data), None) => Ok(Some(staking_data)), - (None, Some(pointer_data)) => Ok(Some(pointer_data)), - (None, None) => Ok(None) - }?; - - let mut data_list = PlutusList::new(); - data_list.add(&payment_data); - if let Some(staking_optional_data) = staking_optional_data { - data_list.add( - &PlutusData::new_single_value_constr_plutus_data( - &BigNum::from(0u32), &staking_optional_data)); - } else { - data_list.add(&PlutusData::new_empty_constr_plutus_data( - &BigNum::from(1u32))); - } - - - Ok(PlutusData::new_constr_plutus_data(&ConstrPlutusData::new( - &BigNum::from(0u32), - &data_list, - ))) - } - - fn from_stake_credential(stake_credential: &StakeCredential) -> Result { - let (bytes_plutus_data, index) = match &stake_credential.0 { - StakeCredType::Key(key_hash) => - (PlutusData::new_bytes(key_hash.to_bytes().to_vec()), BigNum::from(0u32)), - StakeCredType::Script(script_hash) => - (PlutusData::new_bytes(script_hash.to_bytes().to_vec()), BigNum::from(1u32)), - }; - - Ok(PlutusData::new_single_value_constr_plutus_data(&index, &bytes_plutus_data)) - } - - fn from_pointer(pointer: &Pointer) -> Result { - let mut data_list = PlutusList::new(); - data_list.add(&PlutusData::new_integer(&pointer.slot_bignum().into())); - data_list.add(&PlutusData::new_integer(&pointer.tx_index_bignum().into())); - data_list.add(&PlutusData::new_integer(&pointer.cert_index_bignum().into())); - - Ok(PlutusData::new_constr_plutus_data( - &ConstrPlutusData::new(&BigNum::from(1u32), &data_list))) - } -} - -//TODO: replace this by cardano-node schemas -impl JsonSchema for PlutusData { - fn is_referenceable() -> bool { - String::is_referenceable() - } - - fn schema_name() -> String { - String::from("PlutusData") - } - - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - String::json_schema(gen) - } -} - -//TODO: need to figure out what schema to use here -impl serde::Serialize for PlutusData { - fn serialize(&self, serializer: S) -> Result - where S: serde::Serializer { - let json = decode_plutus_datum_to_json_str( - self, - PlutusDatumSchema::DetailedSchema) - .map_err(|ser_err| serde::ser::Error::custom(&format!("Serialization error: {:?}", ser_err)))?; - serializer.serialize_str(&json) - } -} - -impl <'de> serde::de::Deserialize<'de> for PlutusData { - fn deserialize(deserializer: D) -> Result where - D: serde::de::Deserializer<'de> { - let datum_json = ::deserialize(deserializer)?; - encode_json_str_to_plutus_datum(&datum_json, PlutusDatumSchema::DetailedSchema) - .map_err(|ser_err| serde::de::Error::custom(&format!("Deserialization error: {:?}", ser_err))) - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Ord, PartialOrd, Hash, serde::Serialize, serde::Deserialize, JsonSchema)] -pub struct PlutusList { - elems: Vec, - // We should always preserve the original datums when deserialized as this is NOT canonicized - // before computing datum hashes. This field will default to cardano-cli behavior if None - // and will re-use the provided one if deserialized, unless the list is modified. - pub(crate) definite_encoding: Option, -} - -impl<'a> IntoIterator for &'a PlutusList { - type Item = &'a PlutusData; - type IntoIter = std::slice::Iter<'a, PlutusData>; - - fn into_iter(self) -> std::slice::Iter<'a, PlutusData> { - self.elems.iter() - } -} - -impl std::cmp::PartialEq for PlutusList { - fn eq(&self, other: &Self) -> bool { - self.elems.eq(&other.elems) - } -} - -impl std::cmp::Eq for PlutusList {} - -to_from_bytes!(PlutusList); - -#[wasm_bindgen] -impl PlutusList { - pub fn new() -> Self { - Self { - elems: Vec::new(), - definite_encoding: None, - } - } - - pub fn len(&self) -> usize { - self.elems.len() - } - - pub fn get(&self, index: usize) -> PlutusData { - self.elems[index].clone() - } - - pub fn add(&mut self, elem: &PlutusData) { - self.elems.push(elem.clone()); - self.definite_encoding = None; - } -} - -impl From> for PlutusList { - fn from(elems: Vec) -> Self { - Self { - elems, - definite_encoding: None, - } - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct Redeemer { - tag: RedeemerTag, - index: BigNum, - data: PlutusData, - ex_units: ExUnits, -} - -impl_to_from!(Redeemer); - -#[wasm_bindgen] -impl Redeemer { - pub fn tag(&self) -> RedeemerTag { - self.tag.clone() - } - - pub fn index(&self) -> BigNum { - self.index.clone() - } - - pub fn data(&self) -> PlutusData { - self.data.clone() - } - - pub fn ex_units(&self) -> ExUnits { - self.ex_units.clone() - } - - pub fn new(tag: &RedeemerTag, index: &BigNum, data: &PlutusData, ex_units: &ExUnits) -> Self { - Self { - tag: tag.clone(), - index: index.clone(), - data: data.clone(), - ex_units: ex_units.clone(), - } - } - - #[allow(dead_code)] - pub(crate) fn clone_with_index(&self, index: &BigNum) -> Self { - Self { - tag: self.tag.clone(), - index: index.clone(), - data: self.data.clone(), - ex_units: self.ex_units.clone(), - } - } - - pub(crate) fn clone_with_index_and_tag(&self, index: &BigNum, tag: &RedeemerTag) -> Self { - Self { - tag: tag.clone(), - index: index.clone(), - data: self.data.clone(), - ex_units: self.ex_units.clone(), - } - } -} - -#[wasm_bindgen] -#[derive( - Copy, - Clone, - Debug, - Hash, - Eq, - Ord, - PartialEq, - PartialOrd, - serde::Serialize, - serde::Deserialize, - JsonSchema, -)] -pub enum RedeemerTagKind { - Spend, - Mint, - Cert, - Reward, -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct RedeemerTag(RedeemerTagKind); - -impl_to_from!(RedeemerTag); - -#[wasm_bindgen] -impl RedeemerTag { - pub fn new_spend() -> Self { - Self(RedeemerTagKind::Spend) - } - - pub fn new_mint() -> Self { - Self(RedeemerTagKind::Mint) - } - - pub fn new_cert() -> Self { - Self(RedeemerTagKind::Cert) - } - - pub fn new_reward() -> Self { - Self(RedeemerTagKind::Reward) - } - - pub fn kind(&self) -> RedeemerTagKind { - self.0 - } -} - -#[wasm_bindgen] -#[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, -)] -pub struct Redeemers(pub(crate) Vec); - -impl_to_from!(Redeemers); - -#[wasm_bindgen] -impl Redeemers { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> Redeemer { - self.0[index].clone() - } - - pub fn add(&mut self, elem: &Redeemer) { - self.0.push(elem.clone()); - } - - pub fn total_ex_units(&self) -> Result { - let mut tot_mem = BigNum::zero(); - let mut tot_steps = BigNum::zero(); - for i in 0..self.0.len() { - let r: &Redeemer = &self.0[i]; - tot_mem = tot_mem.checked_add(&r.ex_units().mem())?; - tot_steps = tot_steps.checked_add(&r.ex_units().steps())?; - } - Ok(ExUnits::new(&tot_mem, &tot_steps)) - } -} - -impl From> for Redeemers { - fn from(values: Vec) -> Self { - Self(values) - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct Strings(Vec); - -#[wasm_bindgen] -impl Strings { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> String { - self.0[index].clone() - } - - pub fn add(&mut self, elem: String) { - self.0.push(elem); - } -} - -// json - -/// JSON <-> PlutusData conversion schemas. -/// Follows ScriptDataJsonSchema in cardano-cli defined at: -/// https://github.com/input-output-hk/cardano-node/blob/master/cardano-api/src/Cardano/Api/ScriptData.hs#L254 -/// -/// All methods here have the following restrictions due to limitations on dependencies: -/// * JSON numbers above u64::MAX (positive) or below i64::MIN (negative) will throw errors -/// * Hex strings for bytes don't accept odd-length (half-byte) strings. -/// cardano-cli seems to support these however but it seems to be different than just 0-padding -/// on either side when tested so proceed with caution -#[wasm_bindgen] -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum PlutusDatumSchema { - /// ScriptDataJsonNoSchema in cardano-node. - /// - /// This is the format used by --script-data-value in cardano-cli - /// This tries to accept most JSON but does not support the full spectrum of Plutus datums. - /// From JSON: - /// * null/true/false/floats NOT supported - /// * strings starting with 0x are treated as hex bytes. All other strings are encoded as their utf8 bytes. - /// To JSON: - /// * ConstrPlutusData not supported in ANY FORM (neither keys nor values) - /// * Lists not supported in keys - /// * Maps not supported in keys - //// - BasicConversions, - /// ScriptDataJsonDetailedSchema in cardano-node. - /// - /// This is the format used by --script-data-file in cardano-cli - /// This covers almost all (only minor exceptions) Plutus datums, but the JSON must conform to a strict schema. - /// The schema specifies that ALL keys and ALL values must be contained in a JSON map with 2 cases: - /// 1. For ConstrPlutusData there must be two fields "constructor" contianing a number and "fields" containing its fields - /// e.g. { "constructor": 2, "fields": [{"int": 2}, {"list": [{"bytes": "CAFEF00D"}]}]} - /// 2. For all other cases there must be only one field named "int", "bytes", "list" or "map" - /// Integer's value is a JSON number e.g. {"int": 100} - /// Bytes' value is a hex string representing the bytes WITHOUT any prefix e.g. {"bytes": "CAFEF00D"} - /// Lists' value is a JSON list of its elements encoded via the same schema e.g. {"list": [{"bytes": "CAFEF00D"}]} - /// Maps' value is a JSON list of objects, one for each key-value pair in the map, with keys "k" and "v" - /// respectively with their values being the plutus datum encoded via this same schema - /// e.g. {"map": [ - /// {"k": {"int": 2}, "v": {"int": 5}}, - /// {"k": {"map": [{"k": {"list": [{"int": 1}]}, "v": {"bytes": "FF03"}}]}, "v": {"list": []}} - /// ]} - /// From JSON: - /// * null/true/false/floats NOT supported - /// * the JSON must conform to a very specific schema - /// To JSON: - /// * all Plutus datums should be fully supported outside of the integer range limitations outlined above. - //// - DetailedSchema, -} - -#[wasm_bindgen] -pub fn encode_json_str_to_plutus_datum( - json: &str, - schema: PlutusDatumSchema, -) -> Result { - let value = serde_json::from_str(json).map_err(|e| JsError::from_str(&e.to_string()))?; - encode_json_value_to_plutus_datum(value, schema) -} - -pub fn encode_json_value_to_plutus_datum( - value: serde_json::Value, - schema: PlutusDatumSchema, -) -> Result { - use serde_json::Value; - fn encode_number(x: serde_json::Number) -> Result { - if let Some(x) = x.as_u64() { - Ok(PlutusData::new_integer(&BigInt::from(x))) - } else if let Some(x) = x.as_i64() { - Ok(PlutusData::new_integer(&BigInt::from(x))) - } else { - Err(JsError::from_str("floats not allowed in plutus datums")) - } - } - fn encode_string( - s: &str, - schema: PlutusDatumSchema, - is_key: bool, - ) -> Result { - if schema == PlutusDatumSchema::BasicConversions { - if s.starts_with("0x") { - // this must be a valid hex bytestring after - hex::decode(&s[2..]) - .map(|bytes| PlutusData::new_bytes(bytes)) - .map_err(|err| JsError::from_str(&format!("Error decoding {}: {}", s, err))) - } else if is_key { - // try as an integer - BigInt::from_str(s) - .map(|x| PlutusData::new_integer(&x)) - // if not, we use the utf8 bytes of the string instead directly - .or_else(|_err| Ok(PlutusData::new_bytes(s.as_bytes().to_vec()))) - } else { - // can only be UTF bytes if not in a key and not prefixed by 0x - Ok(PlutusData::new_bytes(s.as_bytes().to_vec())) - } - } else { - if s.starts_with("0x") { - Err(JsError::from_str("Hex byte strings in detailed schema should NOT start with 0x and should just contain the hex characters")) - } else { - hex::decode(s) - .map(|bytes| PlutusData::new_bytes(bytes)) - .map_err(|e| JsError::from_str(&e.to_string())) - } - } - } - fn encode_array( - json_arr: Vec, - schema: PlutusDatumSchema, - ) -> Result { - let mut arr = PlutusList::new(); - for value in json_arr { - arr.add(&encode_json_value_to_plutus_datum(value, schema)?); - } - Ok(PlutusData::new_list(&arr)) - } - match schema { - PlutusDatumSchema::BasicConversions => match value { - Value::Null => Err(JsError::from_str("null not allowed in plutus datums")), - Value::Bool(_) => Err(JsError::from_str("bools not allowed in plutus datums")), - Value::Number(x) => encode_number(x), - // no strings in plutus so it's all bytes (as hex or utf8 printable) - Value::String(s) => encode_string(&s, schema, false), - Value::Array(json_arr) => encode_array(json_arr, schema), - Value::Object(json_obj) => { - let mut map = PlutusMap::new(); - for (raw_key, raw_value) in json_obj { - let key = encode_string(&raw_key, schema, true)?; - let value = encode_json_value_to_plutus_datum(raw_value, schema)?; - map.insert(&key, &value); - } - Ok(PlutusData::new_map(&map)) - } - }, - PlutusDatumSchema::DetailedSchema => match value { - Value::Object(obj) => { - if obj.len() == 1 { - // all variants except tagged constructors - let (k, v) = obj.into_iter().next().unwrap(); - fn tag_mismatch() -> JsError { - JsError::from_str("key does not match type") - } - match k.as_str() { - "int" => match v { - Value::Number(x) => encode_number(x), - _ => Err(tag_mismatch()), - }, - "bytes" => { - encode_string(v.as_str().ok_or_else(tag_mismatch)?, schema, false) - } - "list" => { - encode_array(v.as_array().ok_or_else(tag_mismatch)?.clone(), schema) - } - "map" => { - let mut map = PlutusMap::new(); - fn map_entry_err() -> JsError { - JsError::from_str("entry format in detailed schema map object not correct. Needs to be of form {\"k\": {\"key_type\": key}, \"v\": {\"value_type\", value}}") - } - for entry in v.as_array().ok_or_else(tag_mismatch)? { - let entry_obj = entry.as_object().ok_or_else(map_entry_err)?; - let raw_key = entry_obj.get("k").ok_or_else(map_entry_err)?; - let value = entry_obj.get("v").ok_or_else(map_entry_err)?; - let key = - encode_json_value_to_plutus_datum(raw_key.clone(), schema)?; - map.insert( - &key, - &encode_json_value_to_plutus_datum(value.clone(), schema)?, - ); - } - Ok(PlutusData::new_map(&map)) - } - invalid_key => Err(JsError::from_str(&format!( - "key '{}' in tagged object not valid", - invalid_key - ))), - } - } else { - // constructor with tagged variant - if obj.len() != 2 { - return Err(JsError::from_str("detailed schemas must either have only one of the following keys: \"int\", \"bytes\", \"list\" or \"map\", or both of these 2 keys: \"constructor\" + \"fields\"")); - } - let variant: BigNum = obj - .get("constructor") - .and_then(|v| Some(to_bignum(v.as_u64()?))) - .ok_or_else(|| JsError::from_str("tagged constructors must contain an unsigned integer called \"constructor\""))?; - let fields_json = - obj.get("fields") - .and_then(|f| f.as_array()) - .ok_or_else(|| { - JsError::from_str( - "tagged constructors must contian a list called \"fields\"", - ) - })?; - let mut fields = PlutusList::new(); - for field_json in fields_json { - let field = encode_json_value_to_plutus_datum(field_json.clone(), schema)?; - fields.add(&field); - } - Ok(PlutusData::new_constr_plutus_data(&ConstrPlutusData::new( - &variant, &fields, - ))) - } - } - _ => Err(JsError::from_str(&format!( - "DetailedSchema requires ALL JSON to be tagged objects, found: {}", - value - ))), - }, - } -} - -//TODO: move it to serialize impl -#[wasm_bindgen] -pub fn decode_plutus_datum_to_json_str( - datum: &PlutusData, - schema: PlutusDatumSchema, -) -> Result { - let value = decode_plutus_datum_to_json_value(datum, schema)?; - serde_json::to_string(&value).map_err(|e| JsError::from_str(&e.to_string())) -} - -//TODO: move it to deserialize impl -pub fn decode_plutus_datum_to_json_value( - datum: &PlutusData, - schema: PlutusDatumSchema, -) -> Result { - use serde_json::Value; - let (type_tag, json_value) = match &datum.datum { - PlutusDataEnum::ConstrPlutusData(constr) => { - let mut obj = serde_json::map::Map::with_capacity(2); - obj.insert( - String::from("constructor"), - Value::from(from_bignum(&constr.alternative)) - ); - let mut fields = Vec::new(); - for field in constr.data.elems.iter() { - fields.push(decode_plutus_datum_to_json_value(field, schema)?); - } - obj.insert( - String::from("fields"), - Value::from(fields) - ); - (None, Value::from(obj)) - }, - PlutusDataEnum::Map(map) => match schema { - PlutusDatumSchema::BasicConversions => (None, Value::from(map.0.iter().map(|(key, value)| { - let json_key: String = match &key.datum { - PlutusDataEnum::ConstrPlutusData(_) => Err(JsError::from_str("plutus data constructors are not allowed as keys in this schema. Use DetailedSchema.")), - PlutusDataEnum::Map(_) => Err(JsError::from_str("plutus maps are not allowed as keys in this schema. Use DetailedSchema.")), - PlutusDataEnum::List(_) => Err(JsError::from_str("plutus lists are not allowed as keys in this schema. Use DetailedSchema.")), - PlutusDataEnum::Integer(x) => Ok(x.to_str()), - PlutusDataEnum::Bytes(bytes) => String::from_utf8(bytes.clone()).or_else(|_err| Ok(format!("0x{}", hex::encode(bytes)))) - }?; - let json_value = decode_plutus_datum_to_json_value(value, schema)?; - Ok((json_key, Value::from(json_value))) - }).collect::, JsError>>()?)), - PlutusDatumSchema::DetailedSchema => (Some("map"), Value::from(map.0.iter().map(|(key, value)| { - let k = decode_plutus_datum_to_json_value(key, schema)?; - let v = decode_plutus_datum_to_json_value(value, schema)?; - let mut kv_obj = serde_json::map::Map::with_capacity(2); - kv_obj.insert(String::from("k"), k); - kv_obj.insert(String::from("v"), v); - Ok(Value::from(kv_obj)) - }).collect::, JsError>>()?)), - }, - PlutusDataEnum::List(list) => { - let mut elems = Vec::new(); - for elem in list.elems.iter() { - elems.push(decode_plutus_datum_to_json_value(elem, schema)?); - } - (Some("list"), Value::from(elems)) - }, - PlutusDataEnum::Integer(bigint) => ( - Some("int"), - bigint - .as_int() - .as_ref() - .map(|int| if int.0 >= 0 { Value::from(int.0 as u64) } else { Value::from(int.0 as i64) }) - .ok_or_else(|| JsError::from_str(&format!("Integer {} too big for our JSON support", bigint.to_str())))? - ), - PlutusDataEnum::Bytes(bytes) => (Some("bytes"), Value::from(match schema { - PlutusDatumSchema::BasicConversions => { - // cardano-cli converts to a string only if bytes are utf8 and all characters are printable - String::from_utf8(bytes.clone()) - .ok() - .filter(|utf8| utf8.chars().all(|c| !c.is_control())) - // otherwise we hex-encode the bytes with a 0x prefix - .unwrap_or_else(|| format!("0x{}", hex::encode(bytes))) - }, - PlutusDatumSchema::DetailedSchema => hex::encode(bytes), - })), - }; - if type_tag.is_none() || schema != PlutusDatumSchema::DetailedSchema { - Ok(json_value) - } else { - let mut wrapper = serde_json::map::Map::with_capacity(1); - wrapper.insert(String::from(type_tag.unwrap()), json_value); - Ok(Value::from(wrapper)) - } -} - -// Serialization - -use std::io::SeekFrom; - -impl cbor_event::se::Serialize for PlutusScript { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_bytes(&self.bytes) - } -} - -impl Deserialize for PlutusScript { - fn deserialize(raw: &mut Deserializer) -> Result { - Ok(Self::new(raw.bytes()?)) - } -} - -impl cbor_event::se::Serialize for PlutusScripts { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for PlutusScripts { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(PlutusScript::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("PlutusScripts"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for ConstrPlutusData { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - if let Some(compact_tag) = - Self::alternative_to_compact_cbor_tag(from_bignum(&self.alternative)) - { - // compact form - serializer.write_tag(compact_tag as u64)?; - self.data.serialize(serializer) - } else { - // general form - serializer.write_tag(Self::GENERAL_FORM_TAG)?; - serializer.write_array(cbor_event::Len::Len(2))?; - self.alternative.serialize(serializer)?; - self.data.serialize(serializer) - } - } -} - -impl Deserialize for ConstrPlutusData { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let (alternative, data) = match raw.tag()? { - // general form - Self::GENERAL_FORM_TAG => { - let len = raw.array()?; - let mut read_len = CBORReadLen::new(len); - read_len.read_elems(2)?; - let alternative = BigNum::deserialize(raw)?; - let data = - (|| -> Result<_, DeserializeError> { Ok(PlutusList::deserialize(raw)?) })() - .map_err(|e| e.annotate("datas"))?; - match len { - cbor_event::Len::Len(_) => (), - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => (), - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - (alternative, data) - } - // concise form - tag => { - if let Some(alternative) = Self::compact_cbor_tag_to_alternative(tag) { - (to_bignum(alternative), PlutusList::deserialize(raw)?) - } else { - return Err(DeserializeFailure::TagMismatch { - found: tag, - expected: Self::GENERAL_FORM_TAG, - } - .into()); - } - } - }; - Ok(ConstrPlutusData { alternative, data }) - })() - .map_err(|e| e.annotate("ConstrPlutusData")) - } -} - -impl cbor_event::se::Serialize for CostModel { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for cost in &self.0 { - cost.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for CostModel { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(Int::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("CostModel"))?; - Ok(Self(arr.try_into().unwrap())) - } -} - -impl cbor_event::se::Serialize for Costmdls { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; - for (key, value) in &self.0 { - key.serialize(serializer)?; - value.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for Costmdls { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut table = std::collections::BTreeMap::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.map()?; - while match len { - cbor_event::Len::Len(n) => table.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - let key = Language::deserialize(raw)?; - let value = CostModel::deserialize(raw)?; - if table.insert(key.clone(), value).is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( - "some complicated/unsupported type", - ))) - .into()); - } - } - Ok(()) - })() - .map_err(|e| e.annotate("Costmdls"))?; - Ok(Self(table)) - } -} - -impl cbor_event::se::Serialize for ExUnitPrices { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.mem_price.serialize(serializer)?; - self.step_price.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for ExUnitPrices { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let mut read_len = CBORReadLen::new(len); - read_len.read_elems(2)?; - let mem_price = - (|| -> Result<_, DeserializeError> { Ok(SubCoin::deserialize(raw)?) })() - .map_err(|e| e.annotate("mem_price"))?; - let step_price = - (|| -> Result<_, DeserializeError> { Ok(SubCoin::deserialize(raw)?) })() - .map_err(|e| e.annotate("step_price"))?; - match len { - cbor_event::Len::Len(_) => (), - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => (), - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - Ok(ExUnitPrices { - mem_price, - step_price, - }) - })() - .map_err(|e| e.annotate("ExUnitPrices")) - } -} - -impl cbor_event::se::Serialize for ExUnits { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.mem.serialize(serializer)?; - self.steps.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for ExUnits { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let mut read_len = CBORReadLen::new(len); - read_len.read_elems(2)?; - let mem = (|| -> Result<_, DeserializeError> { Ok(BigNum::deserialize(raw)?) })() - .map_err(|e| e.annotate("mem"))?; - let steps = (|| -> Result<_, DeserializeError> { Ok(BigNum::deserialize(raw)?) })() - .map_err(|e| e.annotate("steps"))?; - match len { - cbor_event::Len::Len(_) => (), - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => (), - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - Ok(ExUnits { mem, steps }) - })() - .map_err(|e| e.annotate("ExUnits")) - } -} - -impl cbor_event::se::Serialize for Language { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - // https://github.com/input-output-hk/cardano-ledger/blob/master/eras/babbage/test-suite/cddl-files/babbage.cddl#L324-L327 - serializer.write_unsigned_integer(self.kind() as u64) - } -} - -impl Deserialize for Language { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - match LanguageKind::from_u64(raw.unsigned_integer()?) { - Some(kind) => Ok(Language(kind)), - _ => Err(DeserializeError::new( - "Language", - DeserializeFailure::NoVariantMatched.into(), - )), - } - })() - .map_err(|e| e.annotate("Language")) - } -} - -impl cbor_event::se::Serialize for Languages { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for Languages { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(Language::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("Languages"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for PlutusMap { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; - for (key, value) in &self.0 { - key.serialize(serializer)?; - value.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for PlutusMap { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut table = LinkedHashMap::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.map()?; - while match len { - cbor_event::Len::Len(n) => table.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - let key = PlutusData::deserialize(raw)?; - let value = PlutusData::deserialize(raw)?; - if table.insert(key.clone(), value).is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( - "some complicated/unsupported type", - ))) - .into()); - } - } - Ok(()) - })() - .map_err(|e| e.annotate("PlutusMap"))?; - Ok(Self(table)) - } -} - -impl cbor_event::se::Serialize for PlutusDataEnum { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - match self { - PlutusDataEnum::ConstrPlutusData(x) => x.serialize(serializer), - PlutusDataEnum::Map(x) => x.serialize(serializer), - PlutusDataEnum::List(x) => x.serialize(serializer), - PlutusDataEnum::Integer(x) => x.serialize(serializer), - PlutusDataEnum::Bytes(x) => write_bounded_bytes(serializer, &x), - } - } -} - -impl Deserialize for PlutusDataEnum { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(ConstrPlutusData::deserialize(raw)?) - })(raw) - { - Ok(variant) => return Ok(PlutusDataEnum::ConstrPlutusData(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(PlutusMap::deserialize(raw)?) - })(raw) - { - Ok(variant) => return Ok(PlutusDataEnum::Map(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(PlutusList::deserialize(raw)?) - })(raw) - { - Ok(variant) => return Ok(PlutusDataEnum::List(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(BigInt::deserialize(raw)?) - })(raw) - { - Ok(variant) => return Ok(PlutusDataEnum::Integer(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(read_bounded_bytes(raw)?) - })(raw) - { - Ok(variant) => return Ok(PlutusDataEnum::Bytes(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - Err(DeserializeError::new( - "PlutusDataEnum", - DeserializeFailure::NoVariantMatched.into(), - )) - })() - .map_err(|e| e.annotate("PlutusDataEnum")) - } -} - -impl cbor_event::se::Serialize for PlutusData { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - match &self.original_bytes { - Some(bytes) => serializer.write_raw_bytes(bytes), - None => self.datum.serialize(serializer), - } - } -} - -impl Deserialize for PlutusData { - fn deserialize(raw: &mut Deserializer) -> Result { - // these unwraps are fine since we're seeking the current position - let before = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); - let datum = PlutusDataEnum::deserialize(raw)?; - let after = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); - let bytes_read = (after - before) as usize; - raw.as_mut_ref().seek(SeekFrom::Start(before)).unwrap(); - // these unwraps are fine since we read the above already - let original_bytes = raw.as_mut_ref().fill_buf().unwrap()[..bytes_read].to_vec(); - raw.as_mut_ref().consume(bytes_read); - Ok(Self { - datum, - original_bytes: Some(original_bytes), - }) - } -} - -impl cbor_event::se::Serialize for PlutusList { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - let use_definite_encoding = match self.definite_encoding { - Some(definite) => definite, - None => self.elems.is_empty(), - }; - if use_definite_encoding { - serializer.write_array(cbor_event::Len::Len(self.elems.len() as u64))?; - } else { - serializer.write_array(cbor_event::Len::Indefinite)?; - } - for element in &self.elems { - element.serialize(serializer)?; - } - if !use_definite_encoding { - serializer.write_special(cbor_event::Special::Break)?; - } - Ok(serializer) - } -} - -impl Deserialize for PlutusList { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - let len = (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(PlutusData::deserialize(raw)?); - } - Ok(len) - })() - .map_err(|e| e.annotate("PlutusList"))?; - Ok(Self { - elems: arr, - definite_encoding: Some(len != cbor_event::Len::Indefinite), - }) - } -} - -impl cbor_event::se::Serialize for Redeemer { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(4))?; - self.tag.serialize(serializer)?; - self.index.serialize(serializer)?; - self.data.serialize(serializer)?; - self.ex_units.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for Redeemer { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let mut read_len = CBORReadLen::new(len); - read_len.read_elems(4)?; - let tag = (|| -> Result<_, DeserializeError> { Ok(RedeemerTag::deserialize(raw)?) })() - .map_err(|e| e.annotate("tag"))?; - let index = (|| -> Result<_, DeserializeError> { Ok(BigNum::deserialize(raw)?) })() - .map_err(|e| e.annotate("index"))?; - let data = (|| -> Result<_, DeserializeError> { Ok(PlutusData::deserialize(raw)?) })() - .map_err(|e| e.annotate("data"))?; - let ex_units = (|| -> Result<_, DeserializeError> { Ok(ExUnits::deserialize(raw)?) })() - .map_err(|e| e.annotate("ex_units"))?; - match len { - cbor_event::Len::Len(_) => (), - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => (), - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - Ok(Redeemer { - tag, - index, - data, - ex_units, - }) - })() - .map_err(|e| e.annotate("Redeemer")) - } -} - -impl cbor_event::se::Serialize for RedeemerTagKind { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - match self { - RedeemerTagKind::Spend => serializer.write_unsigned_integer(0u64), - RedeemerTagKind::Mint => serializer.write_unsigned_integer(1u64), - RedeemerTagKind::Cert => serializer.write_unsigned_integer(2u64), - RedeemerTagKind::Reward => serializer.write_unsigned_integer(3u64), - } - } -} - -impl Deserialize for RedeemerTagKind { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - match raw.unsigned_integer() { - Ok(0) => Ok(RedeemerTagKind::Spend), - Ok(1) => Ok(RedeemerTagKind::Mint), - Ok(2) => Ok(RedeemerTagKind::Cert), - Ok(3) => Ok(RedeemerTagKind::Reward), - Ok(_) | Err(_) => Err(DeserializeFailure::NoVariantMatched.into()), - } - })() - .map_err(|e| e.annotate("RedeemerTagEnum")) - } -} - -impl cbor_event::se::Serialize for RedeemerTag { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - self.0.serialize(serializer) - } -} - -impl Deserialize for RedeemerTag { - fn deserialize(raw: &mut Deserializer) -> Result { - Ok(Self(RedeemerTagKind::deserialize(raw)?)) - } -} - -impl cbor_event::se::Serialize for Redeemers { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for Redeemers { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(Redeemer::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("Redeemers"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for Strings { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - serializer.write_text(&element)?; - } - Ok(serializer) - } -} - -impl Deserialize for Strings { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(String::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("Strings"))?; - Ok(Self(arr)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use hex::*; - use crate::tx_builder_constants::TxBuilderConstants; - - #[test] - pub fn plutus_constr_data() { - let constr_0 = PlutusData::new_constr_plutus_data(&ConstrPlutusData::new( - &to_bignum(0), - &PlutusList::new(), - )); - let constr_0_hash = hex::encode(hash_plutus_data(&constr_0).to_bytes()); - assert_eq!( - constr_0_hash, - "923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44ec" - ); - // let constr_0_roundtrip = PlutusData::from_bytes(constr_0.to_bytes()).unwrap(); - // TODO: do we want semantic equality or bytewise equality? - // assert_eq!(constr_0, constr_0_roundtrip); - // let constr_1854 = PlutusData::new_constr_plutus_data( - // &ConstrPlutusData::new(&to_bignum(1854), &PlutusList::new()) - // ); - // let constr_1854_roundtrip = PlutusData::from_bytes(constr_1854.to_bytes()).unwrap(); - // assert_eq!(constr_1854, constr_1854_roundtrip); - } - - #[test] - pub fn plutus_list_serialization_cli_compatibility() { - // mimic cardano-cli array encoding, see https://github.com/Emurgo/cardano-serialization-lib/issues/227 - let datum_cli = "d8799f4100d8799fd8799fd8799f581cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8799fd8799fd8799f581cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd87a80ff1a002625a0d8799fd879801a000f4240d87a80ffff"; - let datum = PlutusData::from_bytes(Vec::from_hex(datum_cli).unwrap()).unwrap(); - assert_eq!(datum_cli, hex::encode(datum.to_bytes())); - - // encode empty arrays as fixed - assert_eq!("80", hex::encode(PlutusList::new().to_bytes())); - - // encode arrays as indefinite length array - let mut list = PlutusList::new(); - list.add(&PlutusData::new_integer(&BigInt::from_str("1").unwrap())); - assert_eq!("9f01ff", hex::encode(list.to_bytes())); - - // witness_set should have fixed length array - let mut witness_set = TransactionWitnessSet::new(); - witness_set.set_plutus_data(&list); - assert_eq!("a1049f01ff", hex::encode(witness_set.to_bytes())); - - list = PlutusList::new(); - list.add(&datum); - witness_set.set_plutus_data(&list); - assert_eq!( - format!("a1049f{}ff", datum_cli), - hex::encode(witness_set.to_bytes()) - ); - } - - #[test] - pub fn plutus_datums_respect_deserialized_encoding() { - let orig_bytes = Vec::from_hex( - "81d8799f581ce1cbb80db89e292269aeb93ec15eb963dda5176b66949fe1c2a6a38da140a1401864ff", - ) - .unwrap(); - let datums = PlutusList::from_bytes(orig_bytes.clone()).unwrap(); - let new_bytes = datums.to_bytes(); - assert_eq!(orig_bytes, new_bytes); - } - - #[test] - pub fn plutus_datum_from_json_basic() { - let json = "{ - \"5\": \"some utf8 string\", - \"0xDEADBEEF\": [ - {\"reg string\": {}}, - -9 - ] - }"; - - let datum = - encode_json_str_to_plutus_datum(json, PlutusDatumSchema::BasicConversions).unwrap(); - - let map = datum.as_map().unwrap(); - let map_5 = map - .get(&PlutusData::new_integer(&BigInt::from_str("5").unwrap())) - .unwrap(); - let utf8_bytes = "some utf8 string".as_bytes(); - assert_eq!(map_5.as_bytes().unwrap(), utf8_bytes); - let map_deadbeef: PlutusList = map - .get(&PlutusData::new_bytes(vec![222, 173, 190, 239])) - .expect("DEADBEEF key not found") - .as_list() - .expect("must be a map"); - assert_eq!(map_deadbeef.len(), 2); - let inner_map = map_deadbeef.get(0).as_map().unwrap(); - assert_eq!(inner_map.len(), 1); - let reg_string = inner_map - .get(&PlutusData::new_bytes("reg string".as_bytes().to_vec())) - .unwrap(); - assert_eq!(reg_string.as_map().expect("reg string: {}").len(), 0); - assert_eq!( - map_deadbeef.get(1).as_integer(), - BigInt::from_str("-9").ok() - ); - - // test round-trip via generated JSON - let json2 = - decode_plutus_datum_to_json_str(&datum, PlutusDatumSchema::BasicConversions).unwrap(); - let datum2 = - encode_json_str_to_plutus_datum(&json2, PlutusDatumSchema::BasicConversions).unwrap(); - assert_eq!(datum, datum2); - } - - #[test] - pub fn plutus_datum_from_json_detailed() { - let json = "{\"list\": [ - {\"map\": [ - {\"k\": {\"bytes\": \"DEADBEEF\"}, \"v\": {\"int\": 42}}, - {\"k\": {\"map\" : [ - {\"k\": {\"int\": 9}, \"v\": {\"int\": -5}} - ]}, \"v\": {\"list\": []}} - ]}, - {\"bytes\": \"CAFED00D\"}, - {\"constructor\": 0, \"fields\": [ - {\"map\": []}, - {\"int\": 23} - ]} - ]}"; - let datum = - encode_json_str_to_plutus_datum(json, PlutusDatumSchema::DetailedSchema).unwrap(); - - let list = datum.as_list().unwrap(); - assert_eq!(3, list.len()); - // map - let map = list.get(0).as_map().unwrap(); - assert_eq!(map.len(), 2); - let map_deadbeef = map - .get(&PlutusData::new_bytes(vec![222, 173, 190, 239])) - .unwrap(); - assert_eq!(map_deadbeef.as_integer(), BigInt::from_str("42").ok()); - let mut long_key = PlutusMap::new(); - long_key.insert( - &PlutusData::new_integer(&BigInt::from_str("9").unwrap()), - &PlutusData::new_integer(&BigInt::from_str("-5").unwrap()), - ); - let map_9_to_5 = map - .get(&PlutusData::new_map(&long_key)) - .unwrap() - .as_list() - .unwrap(); - assert_eq!(map_9_to_5.len(), 0); - // bytes - let bytes = list.get(1).as_bytes().unwrap(); - assert_eq!(bytes, [202, 254, 208, 13]); - // constr data - let constr = list.get(2).as_constr_plutus_data().unwrap(); - assert_eq!(to_bignum(0), constr.alternative()); - let fields = constr.data(); - assert_eq!(fields.len(), 2); - let field0 = fields.get(0).as_map().unwrap(); - assert_eq!(field0.len(), 0); - let field1 = fields.get(1); - assert_eq!(field1.as_integer(), BigInt::from_str("23").ok()); - - // test round-trip via generated JSON - let json2 = - decode_plutus_datum_to_json_str(&datum, PlutusDatumSchema::DetailedSchema).unwrap(); - let datum2 = - encode_json_str_to_plutus_datum(&json2, PlutusDatumSchema::DetailedSchema).unwrap(); - assert_eq!(datum, datum2); - } - - #[test] - pub fn test_cost_model() { - let arr = vec![ - 197209, 0, 1, 1, 396231, 621, 0, 1, 150000, 1000, 0, 1, 150000, 32, 2477736, 29175, 4, - 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 100, 100, - 29773, 100, 150000, 32, 150000, 32, 150000, 32, 150000, 1000, 0, 1, 150000, 32, 150000, - 1000, 0, 8, 148000, 425507, 118, 0, 1, 1, 150000, 1000, 0, 8, 150000, 112536, 247, 1, - 150000, 10000, 1, 136542, 1326, 1, 1000, 150000, 1000, 1, 150000, 32, 150000, 32, - 150000, 32, 1, 1, 150000, 1, 150000, 4, 103599, 248, 1, 103599, 248, 1, 145276, 1366, - 1, 179690, 497, 1, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, - 32, 148000, 425507, 118, 0, 1, 1, 61516, 11218, 0, 1, 150000, 32, 148000, 425507, 118, - 0, 1, 1, 148000, 425507, 118, 0, 1, 1, 2477736, 29175, 4, 0, 82363, 4, 150000, 5000, 0, - 1, 150000, 32, 197209, 0, 1, 1, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, - 32, 150000, 32, 150000, 32, 3345831, 1, 1, - ]; - let cm = arr - .iter() - .fold((CostModel::new(), 0), |(mut cm, i), x| { - cm.set(i, &Int::new_i32(x.clone())).unwrap(); - (cm, i + 1) - }) - .0; - let mut cms = Costmdls::new(); - cms.insert(&Language::new_plutus_v1(), &cm); - assert_eq!( - hex::encode(cms.language_views_encoding()), - "a141005901d59f1a000302590001011a00060bc719026d00011a000249f01903e800011a000249f018201a0025cea81971f70419744d186419744d186419744d186419744d186419744d186419744d18641864186419744d18641a000249f018201a000249f018201a000249f018201a000249f01903e800011a000249f018201a000249f01903e800081a000242201a00067e2318760001011a000249f01903e800081a000249f01a0001b79818f7011a000249f0192710011a0002155e19052e011903e81a000249f01903e8011a000249f018201a000249f018201a000249f0182001011a000249f0011a000249f0041a000194af18f8011a000194af18f8011a0002377c190556011a0002bdea1901f1011a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000242201a00067e23187600010119f04c192bd200011a000249f018201a000242201a00067e2318760001011a000242201a00067e2318760001011a0025cea81971f704001a000141bb041a000249f019138800011a000249f018201a000302590001011a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a00330da70101ff" - ); - } - - #[test] - fn test_plutus_script_hash() { - let hash = EnterpriseAddress::from_address( - &Address::from_bech32("addr1w896t6qnpsjs32xhw8jl3kw34pqz69kgd72l8hqw83w0k3qahx2sv") - .unwrap(), - ) - .unwrap() - .payment_cred() - .to_scripthash() - .unwrap(); - let script = PlutusScript::from_bytes( - hex::decode("590e6f590e6c0100003323332223322333222332232332233223232333222323332223233333333222222223233322232333322223232332232323332223232332233223232333332222233223322332233223322332222323232232232325335303233300a3333573466e1cd55cea8042400046664446660a40060040026eb4d5d0a8041bae35742a00e66a05046666ae68cdc39aab9d37540029000102b11931a982599ab9c04f04c04a049357426ae89401c8c98d4c124cd5ce0268250240239999ab9a3370ea0089001102b11999ab9a3370ea00a9000102c11931a982519ab9c04e04b0490480473333573466e1cd55cea8012400046601a64646464646464646464646666ae68cdc39aab9d500a480008cccccccccc06ccd40a48c8c8cccd5cd19b8735573aa0049000119810981c9aba15002302e357426ae8940088c98d4c164cd5ce02e82d02c02b89aab9e5001137540026ae854028cd40a40a8d5d0a804999aa8183ae502f35742a010666aa060eb940bcd5d0a80399a8148211aba15006335029335505304b75a6ae854014c8c8c8cccd5cd19b8735573aa0049000119a8119919191999ab9a3370e6aae7540092000233502b33504175a6ae854008c118d5d09aba25002232635305d3357380c20bc0b80b626aae7940044dd50009aba150023232323333573466e1cd55cea80124000466a05266a082eb4d5d0a80118231aba135744a004464c6a60ba66ae7018417817016c4d55cf280089baa001357426ae8940088c98d4c164cd5ce02e82d02c02b89aab9e5001137540026ae854010cd40a5d71aba15003335029335505375c40026ae854008c0e0d5d09aba2500223263530553357380b20ac0a80a626ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023232323333573466e1d4005200623020303a357426aae79400c8cccd5cd19b875002480108c07cc110d5d09aab9e500423333573466e1d400d20022301f302f357426aae7940148cccd5cd19b875004480008c088dd71aba135573ca00c464c6a60a066ae7015014413c13813413012c4d55cea80089baa001357426ae8940088c98d4c124cd5ce026825024023882489931a982419ab9c4910350543500049047135573ca00226ea80044d55ce9baa001135744a00226aae7940044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac0013200135503c2233335573e0024a01c466a01a60086ae84008c00cd5d100101811919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a605c6ae854008cd404c0a8d5d09aba25002232635303433573807006a06606426aae7940044dd50009aba150033335500b75ca0146ae854008cd403dd71aba135744a004464c6a606066ae700d00c40bc0b84d5d1280089aab9e5001137540024442466600200800600440024424660020060044002266aa002eb9d6889119118011bab00132001355036223233335573e0044a012466a01066aa05c600c6aae754008c014d55cf280118021aba200302b1357420022244004244244660020080062400224464646666ae68cdc3a800a400046a05e600a6ae84d55cf280191999ab9a3370ea00490011281791931a981399ab9c02b028026025024135573aa00226ea80048c8c8cccd5cd19b8735573aa004900011980318039aba15002375a6ae84d5d1280111931a981219ab9c028025023022135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088c98d4c080cd5ce01201080f80f09baa00112232323333573466e1d400520042500723333573466e1d4009200223500a3006357426aae7940108cccd5cd19b87500348000940288c98d4c08ccd5ce01381201101081000f89aab9d50011375400224244460060082244400422444002240024646666ae68cdc3a800a4004400c46666ae68cdc3a80124000400c464c6a603666ae7007c0700680640604d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308c98d4c080cd5ce01201080f80f00e80e00d80d00c80c09aab9d5004135573ca00626aae7940084d55cf280089baa00121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012323232323333573466e1d400520022333008375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea00490001180518059aba135573ca00c464c6a602266ae7005404804003c0384d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e5004232635300b33573801e01801401201026aae7540044dd5000909118010019091180080190008891119191999ab9a3370e6aae75400920002335500b300635742a004600a6ae84d5d1280111931a980419ab9c00c009007006135573ca00226ea800526120012001112212330010030021120014910350543100222123330010040030022001121223002003112200112001120012001122002122001200111232300100122330033002002001332323233322233322233223332223322332233322233223322332233223233322232323322323232323333222232332232323222323222325335301a5335301a333573466e1cc8cccd54c05048004c8cd406488ccd406400c004008d4058004cd4060888c00cc008004800488cdc0000a40040029000199aa98068900091299a980e299a9a81a1a98169a98131a9812001110009110019119a98188011281c11a81c8009080f880e899a8148010008800a8141a981028009111111111005240040380362038266ae712413c53686f756c642062652065786163746c79206f6e652073637269707420696e70757420746f2061766f696420646f75626c65207361742069737375650001b15335303500315335301a5335301a333573466e20ccc064ccd54c03448005402540a0cc020d4c0c00188880094004074074cdc09a9818003111001a80200d80e080e099ab9c49010f73656c6c6572206e6f7420706169640001b15335301a333573466e20ccc064cc88ccd54c03c48005402d40a8cc028004009400401c074075401006c07040704cd5ce24810d66656573206e6f7420706169640001b101b15335301a3322353022002222222222253353503e33355301f1200133502322533535040002210031001503f253353027333573466e3c0300040a40a04d41040045410000c840a4409d4004d4c0c001888800840704cd5ce2491c4f6e6c792073656c6c65722063616e2063616e63656c206f666665720001b101b135301d00122002153353016333573466e2540040d406005c40d4540044cdc199b8235302b001222003480c920d00f2235301a0012222222222333553011120012235302a002222353034003223353038002253353026333573466e3c0500040a009c4cd40cc01401c401c801d40b0024488cd54c02c480048d4d5408c00488cd54098008cd54c038480048d4d5409800488cd540a4008ccd4d540340048cc0e12000001223303900200123303800148000004cd54c02c480048d4d5408c00488cd54098008ccd4d540280048cd54c03c480048d4d5409c00488cd540a8008d5404400400488ccd5540200580080048cd54c03c480048d4d5409c00488cd540a8008d5403c004004ccd55400c044008004444888ccd54c018480054080cd54c02c480048d4d5408c00488cd54098008d54034004ccd54c0184800488d4d54090008894cd4c05cccd54c04048004c8cd405488ccd4d402c00c88008008004d4d402400488004cd4024894cd4c064008406c40040608d4d5409c00488cc028008014018400c4cd409001000d4084004cd54c02c480048d4d5408c00488c8cd5409c00cc004014c8004d540d8894cd4d40900044d5403400c884d4d540a4008894cd4c070cc0300080204cd5404801c0044c01800c00848848cc00400c00848004c8004d540b488448894cd4d40780044008884cc014008ccd54c01c480040140100044484888c00c01044884888cc0080140104484888c004010448004c8004d540a08844894cd4d406000454068884cd406cc010008cd54c01848004010004c8004d5409c88448894cd4d40600044d401800c884ccd4024014c010008ccd54c01c4800401401000448d4d400c0048800448d4d40080048800848848cc00400c0084800488ccd5cd19b8f002001006005222323230010053200135502522335350130014800088d4d54060008894cd4c02cccd5cd19b8f00200900d00c13007001130060033200135502422335350120014800088d4d5405c008894cd4c028ccd5cd19b8f00200700c00b10011300600312200212200120014881002212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220011122002122122330010040031200111221233001003002112001221233001003002200121223002003212230010032001222123330010040030022001121223002003112200112001122002122001200122337000040029040497a0088919180080091198019801001000a4411c28f07a93d7715db0bdc1766c8bd5b116602b105c02c54fc3bcd0d4680001").unwrap().clone(), - ).unwrap(); - assert_eq!(script.hash(), hash); - } - - #[test] - fn test_plutus_script_from_hex_with_version() { - let script_v1 = PlutusScript::from_hex_with_version( - "590e6f590e6c0100003323332223322333222332232332233223232333222323332223233333333222222223233322232333322223232332232323332223232332233223232333332222233223322332233223322332222323232232232325335303233300a3333573466e1cd55cea8042400046664446660a40060040026eb4d5d0a8041bae35742a00e66a05046666ae68cdc39aab9d37540029000102b11931a982599ab9c04f04c04a049357426ae89401c8c98d4c124cd5ce0268250240239999ab9a3370ea0089001102b11999ab9a3370ea00a9000102c11931a982519ab9c04e04b0490480473333573466e1cd55cea8012400046601a64646464646464646464646666ae68cdc39aab9d500a480008cccccccccc06ccd40a48c8c8cccd5cd19b8735573aa0049000119810981c9aba15002302e357426ae8940088c98d4c164cd5ce02e82d02c02b89aab9e5001137540026ae854028cd40a40a8d5d0a804999aa8183ae502f35742a010666aa060eb940bcd5d0a80399a8148211aba15006335029335505304b75a6ae854014c8c8c8cccd5cd19b8735573aa0049000119a8119919191999ab9a3370e6aae7540092000233502b33504175a6ae854008c118d5d09aba25002232635305d3357380c20bc0b80b626aae7940044dd50009aba150023232323333573466e1cd55cea80124000466a05266a082eb4d5d0a80118231aba135744a004464c6a60ba66ae7018417817016c4d55cf280089baa001357426ae8940088c98d4c164cd5ce02e82d02c02b89aab9e5001137540026ae854010cd40a5d71aba15003335029335505375c40026ae854008c0e0d5d09aba2500223263530553357380b20ac0a80a626ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023232323333573466e1d4005200623020303a357426aae79400c8cccd5cd19b875002480108c07cc110d5d09aab9e500423333573466e1d400d20022301f302f357426aae7940148cccd5cd19b875004480008c088dd71aba135573ca00c464c6a60a066ae7015014413c13813413012c4d55cea80089baa001357426ae8940088c98d4c124cd5ce026825024023882489931a982419ab9c4910350543500049047135573ca00226ea80044d55ce9baa001135744a00226aae7940044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac0013200135503c2233335573e0024a01c466a01a60086ae84008c00cd5d100101811919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a605c6ae854008cd404c0a8d5d09aba25002232635303433573807006a06606426aae7940044dd50009aba150033335500b75ca0146ae854008cd403dd71aba135744a004464c6a606066ae700d00c40bc0b84d5d1280089aab9e5001137540024442466600200800600440024424660020060044002266aa002eb9d6889119118011bab00132001355036223233335573e0044a012466a01066aa05c600c6aae754008c014d55cf280118021aba200302b1357420022244004244244660020080062400224464646666ae68cdc3a800a400046a05e600a6ae84d55cf280191999ab9a3370ea00490011281791931a981399ab9c02b028026025024135573aa00226ea80048c8c8cccd5cd19b8735573aa004900011980318039aba15002375a6ae84d5d1280111931a981219ab9c028025023022135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088c98d4c080cd5ce01201080f80f09baa00112232323333573466e1d400520042500723333573466e1d4009200223500a3006357426aae7940108cccd5cd19b87500348000940288c98d4c08ccd5ce01381201101081000f89aab9d50011375400224244460060082244400422444002240024646666ae68cdc3a800a4004400c46666ae68cdc3a80124000400c464c6a603666ae7007c0700680640604d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308c98d4c080cd5ce01201080f80f00e80e00d80d00c80c09aab9d5004135573ca00626aae7940084d55cf280089baa00121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012323232323333573466e1d400520022333008375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea00490001180518059aba135573ca00c464c6a602266ae7005404804003c0384d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e5004232635300b33573801e01801401201026aae7540044dd5000909118010019091180080190008891119191999ab9a3370e6aae75400920002335500b300635742a004600a6ae84d5d1280111931a980419ab9c00c009007006135573ca00226ea800526120012001112212330010030021120014910350543100222123330010040030022001121223002003112200112001120012001122002122001200111232300100122330033002002001332323233322233322233223332223322332233322233223322332233223233322232323322323232323333222232332232323222323222325335301a5335301a333573466e1cc8cccd54c05048004c8cd406488ccd406400c004008d4058004cd4060888c00cc008004800488cdc0000a40040029000199aa98068900091299a980e299a9a81a1a98169a98131a9812001110009110019119a98188011281c11a81c8009080f880e899a8148010008800a8141a981028009111111111005240040380362038266ae712413c53686f756c642062652065786163746c79206f6e652073637269707420696e70757420746f2061766f696420646f75626c65207361742069737375650001b15335303500315335301a5335301a333573466e20ccc064ccd54c03448005402540a0cc020d4c0c00188880094004074074cdc09a9818003111001a80200d80e080e099ab9c49010f73656c6c6572206e6f7420706169640001b15335301a333573466e20ccc064cc88ccd54c03c48005402d40a8cc028004009400401c074075401006c07040704cd5ce24810d66656573206e6f7420706169640001b101b15335301a3322353022002222222222253353503e33355301f1200133502322533535040002210031001503f253353027333573466e3c0300040a40a04d41040045410000c840a4409d4004d4c0c001888800840704cd5ce2491c4f6e6c792073656c6c65722063616e2063616e63656c206f666665720001b101b135301d00122002153353016333573466e2540040d406005c40d4540044cdc199b8235302b001222003480c920d00f2235301a0012222222222333553011120012235302a002222353034003223353038002253353026333573466e3c0500040a009c4cd40cc01401c401c801d40b0024488cd54c02c480048d4d5408c00488cd54098008cd54c038480048d4d5409800488cd540a4008ccd4d540340048cc0e12000001223303900200123303800148000004cd54c02c480048d4d5408c00488cd54098008ccd4d540280048cd54c03c480048d4d5409c00488cd540a8008d5404400400488ccd5540200580080048cd54c03c480048d4d5409c00488cd540a8008d5403c004004ccd55400c044008004444888ccd54c018480054080cd54c02c480048d4d5408c00488cd54098008d54034004ccd54c0184800488d4d54090008894cd4c05cccd54c04048004c8cd405488ccd4d402c00c88008008004d4d402400488004cd4024894cd4c064008406c40040608d4d5409c00488cc028008014018400c4cd409001000d4084004cd54c02c480048d4d5408c00488c8cd5409c00cc004014c8004d540d8894cd4d40900044d5403400c884d4d540a4008894cd4c070cc0300080204cd5404801c0044c01800c00848848cc00400c00848004c8004d540b488448894cd4d40780044008884cc014008ccd54c01c480040140100044484888c00c01044884888cc0080140104484888c004010448004c8004d540a08844894cd4d406000454068884cd406cc010008cd54c01848004010004c8004d5409c88448894cd4d40600044d401800c884ccd4024014c010008ccd54c01c4800401401000448d4d400c0048800448d4d40080048800848848cc00400c0084800488ccd5cd19b8f002001006005222323230010053200135502522335350130014800088d4d54060008894cd4c02cccd5cd19b8f00200900d00c13007001130060033200135502422335350120014800088d4d5405c008894cd4c028ccd5cd19b8f00200700c00b10011300600312200212200120014881002212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220011122002122122330010040031200111221233001003002112001221233001003002200121223002003212230010032001222123330010040030022001121223002003112200112001122002122001200122337000040029040497a0088919180080091198019801001000a4411c28f07a93d7715db0bdc1766c8bd5b116602b105c02c54fc3bcd0d4680001", - &Language::new_plutus_v1() - ).unwrap(); - assert_eq!(script_v1.language, Language::new_plutus_v1().0); - - let script_v2 = PlutusScript::from_hex_with_version( - "590e6f590e6c0100003323332223322333222332232332233223232333222323332223233333333222222223233322232333322223232332232323332223232332233223232333332222233223322332233223322332222323232232232325335303233300a3333573466e1cd55cea8042400046664446660a40060040026eb4d5d0a8041bae35742a00e66a05046666ae68cdc39aab9d37540029000102b11931a982599ab9c04f04c04a049357426ae89401c8c98d4c124cd5ce0268250240239999ab9a3370ea0089001102b11999ab9a3370ea00a9000102c11931a982519ab9c04e04b0490480473333573466e1cd55cea8012400046601a64646464646464646464646666ae68cdc39aab9d500a480008cccccccccc06ccd40a48c8c8cccd5cd19b8735573aa0049000119810981c9aba15002302e357426ae8940088c98d4c164cd5ce02e82d02c02b89aab9e5001137540026ae854028cd40a40a8d5d0a804999aa8183ae502f35742a010666aa060eb940bcd5d0a80399a8148211aba15006335029335505304b75a6ae854014c8c8c8cccd5cd19b8735573aa0049000119a8119919191999ab9a3370e6aae7540092000233502b33504175a6ae854008c118d5d09aba25002232635305d3357380c20bc0b80b626aae7940044dd50009aba150023232323333573466e1cd55cea80124000466a05266a082eb4d5d0a80118231aba135744a004464c6a60ba66ae7018417817016c4d55cf280089baa001357426ae8940088c98d4c164cd5ce02e82d02c02b89aab9e5001137540026ae854010cd40a5d71aba15003335029335505375c40026ae854008c0e0d5d09aba2500223263530553357380b20ac0a80a626ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023232323333573466e1d4005200623020303a357426aae79400c8cccd5cd19b875002480108c07cc110d5d09aab9e500423333573466e1d400d20022301f302f357426aae7940148cccd5cd19b875004480008c088dd71aba135573ca00c464c6a60a066ae7015014413c13813413012c4d55cea80089baa001357426ae8940088c98d4c124cd5ce026825024023882489931a982419ab9c4910350543500049047135573ca00226ea80044d55ce9baa001135744a00226aae7940044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac0013200135503c2233335573e0024a01c466a01a60086ae84008c00cd5d100101811919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a605c6ae854008cd404c0a8d5d09aba25002232635303433573807006a06606426aae7940044dd50009aba150033335500b75ca0146ae854008cd403dd71aba135744a004464c6a606066ae700d00c40bc0b84d5d1280089aab9e5001137540024442466600200800600440024424660020060044002266aa002eb9d6889119118011bab00132001355036223233335573e0044a012466a01066aa05c600c6aae754008c014d55cf280118021aba200302b1357420022244004244244660020080062400224464646666ae68cdc3a800a400046a05e600a6ae84d55cf280191999ab9a3370ea00490011281791931a981399ab9c02b028026025024135573aa00226ea80048c8c8cccd5cd19b8735573aa004900011980318039aba15002375a6ae84d5d1280111931a981219ab9c028025023022135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088c98d4c080cd5ce01201080f80f09baa00112232323333573466e1d400520042500723333573466e1d4009200223500a3006357426aae7940108cccd5cd19b87500348000940288c98d4c08ccd5ce01381201101081000f89aab9d50011375400224244460060082244400422444002240024646666ae68cdc3a800a4004400c46666ae68cdc3a80124000400c464c6a603666ae7007c0700680640604d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308c98d4c080cd5ce01201080f80f00e80e00d80d00c80c09aab9d5004135573ca00626aae7940084d55cf280089baa00121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012323232323333573466e1d400520022333008375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea00490001180518059aba135573ca00c464c6a602266ae7005404804003c0384d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e5004232635300b33573801e01801401201026aae7540044dd5000909118010019091180080190008891119191999ab9a3370e6aae75400920002335500b300635742a004600a6ae84d5d1280111931a980419ab9c00c009007006135573ca00226ea800526120012001112212330010030021120014910350543100222123330010040030022001121223002003112200112001120012001122002122001200111232300100122330033002002001332323233322233322233223332223322332233322233223322332233223233322232323322323232323333222232332232323222323222325335301a5335301a333573466e1cc8cccd54c05048004c8cd406488ccd406400c004008d4058004cd4060888c00cc008004800488cdc0000a40040029000199aa98068900091299a980e299a9a81a1a98169a98131a9812001110009110019119a98188011281c11a81c8009080f880e899a8148010008800a8141a981028009111111111005240040380362038266ae712413c53686f756c642062652065786163746c79206f6e652073637269707420696e70757420746f2061766f696420646f75626c65207361742069737375650001b15335303500315335301a5335301a333573466e20ccc064ccd54c03448005402540a0cc020d4c0c00188880094004074074cdc09a9818003111001a80200d80e080e099ab9c49010f73656c6c6572206e6f7420706169640001b15335301a333573466e20ccc064cc88ccd54c03c48005402d40a8cc028004009400401c074075401006c07040704cd5ce24810d66656573206e6f7420706169640001b101b15335301a3322353022002222222222253353503e33355301f1200133502322533535040002210031001503f253353027333573466e3c0300040a40a04d41040045410000c840a4409d4004d4c0c001888800840704cd5ce2491c4f6e6c792073656c6c65722063616e2063616e63656c206f666665720001b101b135301d00122002153353016333573466e2540040d406005c40d4540044cdc199b8235302b001222003480c920d00f2235301a0012222222222333553011120012235302a002222353034003223353038002253353026333573466e3c0500040a009c4cd40cc01401c401c801d40b0024488cd54c02c480048d4d5408c00488cd54098008cd54c038480048d4d5409800488cd540a4008ccd4d540340048cc0e12000001223303900200123303800148000004cd54c02c480048d4d5408c00488cd54098008ccd4d540280048cd54c03c480048d4d5409c00488cd540a8008d5404400400488ccd5540200580080048cd54c03c480048d4d5409c00488cd540a8008d5403c004004ccd55400c044008004444888ccd54c018480054080cd54c02c480048d4d5408c00488cd54098008d54034004ccd54c0184800488d4d54090008894cd4c05cccd54c04048004c8cd405488ccd4d402c00c88008008004d4d402400488004cd4024894cd4c064008406c40040608d4d5409c00488cc028008014018400c4cd409001000d4084004cd54c02c480048d4d5408c00488c8cd5409c00cc004014c8004d540d8894cd4d40900044d5403400c884d4d540a4008894cd4c070cc0300080204cd5404801c0044c01800c00848848cc00400c00848004c8004d540b488448894cd4d40780044008884cc014008ccd54c01c480040140100044484888c00c01044884888cc0080140104484888c004010448004c8004d540a08844894cd4d406000454068884cd406cc010008cd54c01848004010004c8004d5409c88448894cd4d40600044d401800c884ccd4024014c010008ccd54c01c4800401401000448d4d400c0048800448d4d40080048800848848cc00400c0084800488ccd5cd19b8f002001006005222323230010053200135502522335350130014800088d4d54060008894cd4c02cccd5cd19b8f00200900d00c13007001130060033200135502422335350120014800088d4d5405c008894cd4c028ccd5cd19b8f00200700c00b10011300600312200212200120014881002212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220011122002122122330010040031200111221233001003002112001221233001003002200121223002003212230010032001222123330010040030022001121223002003112200112001122002122001200122337000040029040497a0088919180080091198019801001000a4411c28f07a93d7715db0bdc1766c8bd5b116602b105c02c54fc3bcd0d4680001", - &Language::new_plutus_v2() - ).unwrap(); - assert_eq!(script_v2.language, Language::new_plutus_v2().0); - } - - fn redeemer_with_ex_units(mem: &BigNum, steps: &BigNum) -> Redeemer { - Redeemer::new( - &RedeemerTag::new_spend(), - &BigNum::zero(), - &PlutusData::new_integer(&BigInt::from_str("0").unwrap()), - &ExUnits::new(mem, steps), - ) - } - - #[test] - fn test_total_ex_units() { - let mut r = Redeemers::new(); - - fn assert_ex_units(eu: &ExUnits, exp_mem: u64, exp_steps: u64) { - assert_eq!(eu.mem, to_bignum(exp_mem)); - assert_eq!(eu.steps, to_bignum(exp_steps)); - } - - r.add(&redeemer_with_ex_units(&to_bignum(10), &to_bignum(100))); - assert_ex_units(&r.total_ex_units().unwrap(), 10, 100); - r.add(&redeemer_with_ex_units(&to_bignum(20), &to_bignum(200))); - assert_ex_units(&r.total_ex_units().unwrap(), 30, 300); - r.add(&redeemer_with_ex_units(&to_bignum(30), &to_bignum(300))); - assert_ex_units(&r.total_ex_units().unwrap(), 60, 600); - } - - #[test] - fn test_empty_constr_data() { - assert_eq!( - PlutusData::new_empty_constr_plutus_data(&BigNum::one()), - PlutusData::new_constr_plutus_data(&ConstrPlutusData::new( - &BigNum::from_str("1").unwrap(), - &PlutusList::new(), - ),), - ) - } - - #[test] - fn test_plutus_script_version() { - let bytes = hex::decode("4e4d01000033222220051200120011").unwrap(); - let s1: PlutusScript = PlutusScript::from_bytes(bytes.clone()).unwrap(); - let s2: PlutusScript = PlutusScript::from_bytes_v2(bytes.clone()).unwrap(); - - assert_eq!(s1.bytes(), bytes[1..]); - assert_eq!(s2.bytes(), bytes[1..]); - assert_eq!(s1.language_version(), Language::new_plutus_v1()); - assert_eq!(s2.language_version(), Language::new_plutus_v2()); - - assert_eq!( - s1, - PlutusScript::from_bytes_with_version(bytes.clone(), &Language::new_plutus_v1(),) - .unwrap() - ); - assert_eq!( - s2, - PlutusScript::from_bytes_with_version(bytes.clone(), &Language::new_plutus_v2(),) - .unwrap() - ); - } - - #[test] - fn test_language_roundtrip() { - fn deserialize_language_from_uint(x: u64) -> Result { - let mut buf = Serializer::new_vec(); - x.serialize(&mut buf).unwrap(); - Language::from_bytes(buf.finalize()) - } - - assert_eq!( - deserialize_language_from_uint(0).unwrap(), - Language::new_plutus_v1() - ); - assert_eq!( - deserialize_language_from_uint(1).unwrap(), - Language::new_plutus_v2() - ); - assert!(deserialize_language_from_uint(2).is_err()); - - assert_eq!( - Language::from_bytes(Language::new_plutus_v1().to_bytes()).unwrap(), - Language::new_plutus_v1(), - ); - assert_eq!( - Language::from_bytes(Language::new_plutus_v2().to_bytes()).unwrap(), - Language::new_plutus_v2(), - ); - } - - #[test] - fn test_cost_model_roundtrip() { - use crate::tx_builder_constants::TxBuilderConstants; - let costmodels = TxBuilderConstants::plutus_vasil_cost_models(); - assert_eq!( - costmodels, - Costmdls::from_bytes(costmodels.to_bytes()).unwrap() - ); - } - - #[test] - fn test_known_plutus_data_hash() { - use crate::tx_builder_constants::TxBuilderConstants; - let pdata = PlutusList::from(vec![PlutusData::new_constr_plutus_data( - &ConstrPlutusData::new( - &BigNum::zero(), - &PlutusList::from(vec![ - PlutusData::new_constr_plutus_data(&ConstrPlutusData::new( - &BigNum::zero(), - &PlutusList::from(vec![ - PlutusData::new_bytes( - hex::decode( - "A183BF86925F66C579A3745C9517744399679B090927B8F6E2F2E1BB", - ) - .unwrap(), - ), - PlutusData::new_bytes( - hex::decode("6164617065416D616E734576616E73").unwrap(), - ), - ]), - )), - PlutusData::new_constr_plutus_data(&ConstrPlutusData::new( - &BigNum::zero(), - &PlutusList::from(vec![ - PlutusData::new_bytes( - hex::decode( - "9A4E855293A0B9AF5E50935A331D83E7982AB5B738EA0E6FC0F9E656", - ) - .unwrap(), - ), - PlutusData::new_bytes( - hex::decode("4652414D455F38333030325F4C30").unwrap(), - ), - ]), - )), - PlutusData::new_bytes( - hex::decode("BEA1C521DF58F4EEEF60C647E5EBD88C6039915409F9FD6454A476B9") - .unwrap(), - ), - ]), - ), - )]); - let redeemers = Redeemers(vec![Redeemer::new( - &RedeemerTag::new_spend(), - &BigNum::one(), - &PlutusData::new_empty_constr_plutus_data(&BigNum::zero()), - &ExUnits::new(&to_bignum(7000000), &to_bignum(3000000000)), - )]); - let lang = Language::new_plutus_v1(); - let lang_costmodel = TxBuilderConstants::plutus_vasil_cost_models() - .get(&lang) - .unwrap(); - let mut retained_cost_models = Costmdls::new(); - retained_cost_models.insert(&lang, &lang_costmodel); - let hash = hash_script_data(&redeemers, &retained_cost_models, Some(pdata)); - assert_eq!( - hex::encode(hash.to_bytes()), - "2fd8b7e248b376314d02989c885c278796ab0e1d6e8aa0cb91f562ff5f7dbd70" - ); - } - - #[test] - fn test_same_datum_in_different_formats_with_expected_hashes() { - // This is a known datum with indefinite arrays and a known expected hash - let pdata1 = PlutusData::from_bytes(hex::decode("d8799fd8799f581ca183bf86925f66c579a3745c9517744399679b090927b8f6e2f2e1bb4f616461706541696c656e416d61746fffd8799f581c9a4e855293a0b9af5e50935a331d83e7982ab5b738ea0e6fc0f9e6564e4652414d455f36353030335f4c30ff581cbea1c521df58f4eeef60c647e5ebd88c6039915409f9fd6454a476b9ff").unwrap()).unwrap(); - assert_eq!( - hex::encode(hash_plutus_data(&pdata1).to_bytes()), - "ec3028f46325b983a470893a8bdc1b4a100695b635fb1237d301c3490b23e89b" - ); - // This is the same exact datum manually converted to definite arrays - // and it produces a different known expected hash because the format is preserved after deserialization - let pdata2 = PlutusData::from_bytes(hex::decode("d87983d87982581ca183bf86925f66c579a3745c9517744399679b090927b8f6e2f2e1bb4f616461706541696c656e416d61746fd87982581c9a4e855293a0b9af5e50935a331d83e7982ab5b738ea0e6fc0f9e6564e4652414d455f36353030335f4c30581cbea1c521df58f4eeef60c647e5ebd88c6039915409f9fd6454a476b9").unwrap()).unwrap(); - assert_eq!( - hex::encode(hash_plutus_data(&pdata2).to_bytes()), - "816cdf6d4d8cba3ad0188ca643db95ddf0e03cdfc0e75a9550a72a82cb146222" - ); - } - - #[test] - fn test_known_plutus_data_hash_with_no_datums() { - let mut costmodels = Costmdls::new(); - costmodels.insert( - &Language::new_plutus_v2(), - &TxBuilderConstants::plutus_vasil_cost_models().get(&Language::new_plutus_v2()).unwrap(), - ); - let hash = hash_script_data( - &Redeemers(vec![ - Redeemer::new( - &RedeemerTag::new_spend(), - &BigNum::zero(), - &PlutusData::new_empty_constr_plutus_data(&BigNum::zero()), - &ExUnits::new(&to_bignum(842996), &to_bignum(246100241)), - ), - ]), - &costmodels, - None, - ); - assert_eq!(hex::encode(hash.to_bytes()), "6b244f15f895fd458a02bef3a8b56f17f24150fddcb06be482f8790a600578a1"); - } - - #[test] - fn test_known_plutus_data_hash_2() { - let datums = PlutusList::from(vec![ - PlutusData::new_constr_plutus_data( - &ConstrPlutusData::new( - &BigNum::zero(), - &PlutusList::from(vec![ - PlutusData::new_bytes( - hex::decode("45F6A506A49A38263C4A8BBB2E1E369DD8732FB1F9A281F3E8838387").unwrap(), - ), - PlutusData::new_integer(&BigInt::from_str("60000000").unwrap()), - PlutusData::new_bytes( - hex::decode("EE8E37676F6EBB8E031DFF493F88FF711D24AA68666A09D61F1D3FB3").unwrap(), - ), - PlutusData::new_bytes( - hex::decode("43727970746F44696E6F3036333039").unwrap(), - ), - ]), - ) - ) - ]); - let redeemers = Redeemers(vec![ - Redeemer::new( - &RedeemerTag::new_spend(), - &BigNum::one(), - &PlutusData::new_empty_constr_plutus_data(&BigNum::one()), - &ExUnits::new(&to_bignum(61300), &to_bignum(18221176)), - ), - ]); - let hash = hash_script_data( - &redeemers, - &TxBuilderConstants::plutus_vasil_cost_models() - .retain_language_versions(&Languages(vec![Language::new_plutus_v1()])), - Some(datums), - ); - assert_eq!(hex::encode(hash.to_bytes()), "0a076247a05aacbecf72ea15b94e3d0331b21295a08d9ab7b8675c13840563a6"); - } - - #[test] - fn datum_from_enterprise_key_address() { - let address = Address::from_bech32("addr1vxy2c673nsdp0mvgq5d3tpjndngucsytug00k7k6xwlx4lg6dspk5").unwrap(); - let datum = PlutusData::from_address(&address).unwrap(); - let orig_datum = PlutusData::from_json("{\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"bytes\": \"88ac6bd19c1a17ed88051b1586536cd1cc408be21efb7ada33be6afd\"}]}, {\"constructor\": 1, \"fields\": []}]}", - PlutusDatumSchema::DetailedSchema).unwrap(); - assert_eq!(datum, orig_datum); - } - - #[test] - fn datum_from_enterprise_script_address() { - let address = Address::from_bech32("addr1w8wrk560wcsldjpnqjamn8s0gn9pdrplpyetrdfpacqrpfs3xezd8").unwrap(); - let datum = PlutusData::from_address(&address).unwrap(); - let orig_datum = PlutusData::from_json("{\"constructor\": 0, \"fields\": [{\"constructor\": 1, \"fields\": [{\"bytes\": \"dc3b534f7621f6c83304bbb99e0f44ca168c3f0932b1b521ee0030a6\"}]}, {\"constructor\": 1, \"fields\": []}]}", - PlutusDatumSchema::DetailedSchema).unwrap(); - assert_eq!(datum, orig_datum); - } - - #[test] - fn datum_from_base_key_key_address() { - let address = Address::from_bech32("addr1qxy2c673nsdp0mvgq5d3tpjndngucsytug00k7k6xwlx4lvg434ar8q6zlkcspgmzkr9xmx3e3qghcs7ldad5va7dt7s5efyer").unwrap(); - let datum = PlutusData::from_address(&address).unwrap(); - let orig_datum = PlutusData::from_json("{\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"bytes\": \"88ac6bd19c1a17ed88051b1586536cd1cc408be21efb7ada33be6afd\"}]}, {\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"bytes\": \"88ac6bd19c1a17ed88051b1586536cd1cc408be21efb7ada33be6afd\"}]}]}]}]}", - PlutusDatumSchema::DetailedSchema).unwrap(); - assert_eq!(datum, orig_datum); - } - - #[test] - fn datum_from_base_script_script_address() { - let address = Address::from_bech32("addr1x8wrk560wcsldjpnqjamn8s0gn9pdrplpyetrdfpacqrpfku8df57a3p7myrxp9mhx0q73x2z6xr7zfjkx6jrmsqxznqh8u5dz").unwrap(); - let datum = PlutusData::from_address(&address).unwrap(); - let orig_datum = PlutusData::from_json("{\"constructor\": 0, \"fields\": [{\"constructor\": 1, \"fields\": [{\"bytes\": \"dc3b534f7621f6c83304bbb99e0f44ca168c3f0932b1b521ee0030a6\"}]}, {\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"constructor\": 1, \"fields\": [{\"bytes\": \"dc3b534f7621f6c83304bbb99e0f44ca168c3f0932b1b521ee0030a6\"}]}]}]}]}", - PlutusDatumSchema::DetailedSchema).unwrap(); - assert_eq!(datum, orig_datum); - } - - #[test] - fn datum_from_base_script_key_address() { - let address = Address::from_bech32("addr1z8wrk560wcsldjpnqjamn8s0gn9pdrplpyetrdfpacqrpf5g434ar8q6zlkcspgmzkr9xmx3e3qghcs7ldad5va7dt7sqx2wxh").unwrap(); - let datum = PlutusData::from_address(&address).unwrap(); - let orig_datum = PlutusData::from_json("{\"constructor\": 0, \"fields\": [{\"constructor\": 1, \"fields\": [{\"bytes\": \"dc3b534f7621f6c83304bbb99e0f44ca168c3f0932b1b521ee0030a6\"}]}, {\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"bytes\": \"88ac6bd19c1a17ed88051b1586536cd1cc408be21efb7ada33be6afd\"}]}]}]}]}", - PlutusDatumSchema::DetailedSchema).unwrap(); - assert_eq!(datum, orig_datum); - } - - #[test] - fn datum_from_base_key_script_address() { - let address = Address::from_bech32("addr1yxy2c673nsdp0mvgq5d3tpjndngucsytug00k7k6xwlx4lwu8df57a3p7myrxp9mhx0q73x2z6xr7zfjkx6jrmsqxznqrcl7jk").unwrap(); - let datum = PlutusData::from_address(&address).unwrap(); - let orig_datum = PlutusData::from_json("{\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"bytes\": \"88ac6bd19c1a17ed88051b1586536cd1cc408be21efb7ada33be6afd\"}]}, {\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"constructor\": 1, \"fields\": [{\"bytes\": \"dc3b534f7621f6c83304bbb99e0f44ca168c3f0932b1b521ee0030a6\"}]}]}]}]}", - PlutusDatumSchema::DetailedSchema).unwrap(); - assert_eq!(datum, orig_datum); - } -} diff --git a/rust/src/protocol_types/address.rs b/rust/src/protocol_types/address.rs new file mode 100644 index 00000000..09021743 --- /dev/null +++ b/rust/src/protocol_types/address.rs @@ -0,0 +1,901 @@ +use crate::legacy_address::ExtendedAddr; +use crate::*; +use bech32::ToBase32; +use ed25519_bip32::XPub; + +// returns (Number represented, bytes read) if valid encoding +// or None if decoding prematurely finished +pub(crate) fn variable_nat_decode(bytes: &[u8]) -> Option<(u64, usize)> { + let mut output = 0u128; + let mut bytes_read = 0; + for byte in bytes { + output = (output << 7) | (byte & 0x7F) as u128; + if output > u64::MAX.into() { + return None; + } + bytes_read += 1; + if (byte & 0x80) == 0 { + return Some((output as u64, bytes_read)); + } + } + None +} + +pub(crate) fn variable_nat_encode(mut num: u64) -> Vec { + let mut output = vec![num as u8 & 0x7F]; + num /= 128; + while num > 0 { + output.push((num & 0x7F) as u8 | 0x80); + num /= 128; + } + output.reverse(); + output +} + +#[wasm_bindgen] +#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum AddressKind { + Base, + Pointer, + Enterprise, + Reward, + Byron, + Malformed, +} + +#[wasm_bindgen] +#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct NetworkInfo { + network_id: u8, + protocol_magic: u32, +} +#[wasm_bindgen] +impl NetworkInfo { + pub fn new(network_id: u8, protocol_magic: u32) -> Self { + Self { + network_id, + protocol_magic, + } + } + pub fn network_id(&self) -> u8 { + self.network_id + } + pub fn protocol_magic(&self) -> u32 { + self.protocol_magic + } + + pub fn testnet_preview() -> NetworkInfo { + NetworkInfo { + network_id: 0b0000, + protocol_magic: 2, + } + } + pub fn testnet_preprod() -> NetworkInfo { + NetworkInfo { + network_id: 0b0000, + protocol_magic: 1, + } + } + pub fn mainnet() -> NetworkInfo { + NetworkInfo { + network_id: 0b0001, + protocol_magic: 764824073, + } + } +} + +#[wasm_bindgen] +#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] +pub struct MalformedAddress(pub(crate) Vec); + +#[wasm_bindgen] +impl MalformedAddress { + pub fn original_bytes(&self) -> Vec { + self.0.clone() + } + + pub fn to_address(&self) -> Address { + Address(AddrType::Malformed(self.clone())) + } + + pub fn from_address(addr: &Address) -> Option { + match &addr.0 { + AddrType::Malformed(malformed) => Some(malformed.clone()), + _ => None, + } + } +} + +impl serde::Serialize for MalformedAddress { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let bech32 = self + .to_address() + .to_bech32(None) + .map_err(|e| serde::ser::Error::custom(format!("to_bech32: {:?}", e)))?; + serializer.serialize_str(&bech32) + } +} + +impl<'de> serde::de::Deserialize<'de> for MalformedAddress { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let bech32 = ::deserialize(deserializer)?; + match Address::from_bech32(&bech32).map(|addr| addr.0) + { + Ok(AddrType::Malformed(malformed_address)) => Ok(malformed_address), + _ => Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&bech32), + &"bech32 malformed address string", + )), + } + } +} + +impl JsonSchema for MalformedAddress { + fn schema_name() -> String { + String::from("MalformedAddress") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } + fn is_referenceable() -> bool { + String::is_referenceable() + } +} + +#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] +pub(crate) enum AddrType { + Base(BaseAddress), + Ptr(PointerAddress), + Enterprise(EnterpriseAddress), + Reward(RewardAddress), + Byron(ByronAddress), + Malformed(MalformedAddress), +} + +#[wasm_bindgen] +#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] +pub struct ByronAddress(pub(crate) ExtendedAddr); +#[wasm_bindgen] +impl ByronAddress { + pub fn to_base58(&self) -> String { + format!("{}", self.0) + } + pub fn to_bytes(&self) -> Vec { + let mut addr_bytes = Serializer::new_vec(); + self.0.serialize(&mut addr_bytes).unwrap(); + addr_bytes.finalize() + } + pub fn from_bytes(bytes: Vec) -> Result { + let mut raw = Deserializer::from(std::io::Cursor::new(bytes)); + let extended_addr = ExtendedAddr::deserialize(&mut raw)?; + Ok(ByronAddress(extended_addr)) + } + /// returns the byron protocol magic embedded in the address, or mainnet id if none is present + /// note: for bech32 addresses, you need to use network_id instead + pub fn byron_protocol_magic(&self) -> u32 { + match self.0.attributes.protocol_magic { + Some(x) => x, + None => NetworkInfo::mainnet().protocol_magic(), // mainnet is implied if omitted + } + } + pub fn attributes(&self) -> Vec { + let mut attributes_bytes = Serializer::new_vec(); + self.0.attributes.serialize(&mut attributes_bytes).unwrap(); + attributes_bytes.finalize() + } + pub fn network_id(&self) -> Result { + // premise: during the Byron-era, we had one mainnet (764824073) and many many testnets + // with each testnet getting a different protocol magic + // in Shelley, this changes so that: + // 1) all testnets use the same u8 protocol magic + // 2) mainnet is re-mapped to a single u8 protocol magic + + // recall: in Byron mainnet, the network_id is omitted from the address to save a few bytes + // so here we return the mainnet id if none is found in the address + + // mainnet is implied if omitted + let protocol_magic = self.byron_protocol_magic(); + match protocol_magic { + magic if magic == NetworkInfo::mainnet().protocol_magic() => { + Ok(NetworkInfo::mainnet().network_id()) + } + magic if magic == NetworkInfo::testnet_preprod().protocol_magic() => { + Ok(NetworkInfo::testnet_preprod().network_id()) + } + magic if magic == NetworkInfo::testnet_preview().protocol_magic() => { + Ok(NetworkInfo::testnet_preview().network_id()) + } + _ => Err(JsError::from_str( + &format! {"Unknown network {}", protocol_magic}, + )), + } + } + + pub fn from_base58(s: &str) -> Result { + use std::str::FromStr; + ExtendedAddr::from_str(s) + .map_err(|e| JsError::from_str(&format! {"{:?}", e})) + .map(ByronAddress) + } + + // icarus-style address (Ae2) + pub fn icarus_from_key(key: &Bip32PublicKey, protocol_magic: u32) -> ByronAddress { + let mut out = [0u8; 64]; + out.clone_from_slice(&key.as_bytes()); + + // need to ensure we use None for mainnet since Byron-era addresses omitted the network id + let filtered_protocol_magic = if protocol_magic == NetworkInfo::mainnet().protocol_magic() { + None + } else { + Some(protocol_magic) + }; + ByronAddress(ExtendedAddr::new_simple( + &XPub::from_bytes(out), + filtered_protocol_magic, + )) + } + + pub fn is_valid(s: &str) -> bool { + use std::str::FromStr; + match ExtendedAddr::from_str(s) { + Ok(_v) => true, + Err(_err) => false, + } + } + + pub fn to_address(&self) -> Address { + Address(AddrType::Byron(self.clone())) + } + + pub fn from_address(addr: &Address) -> Option { + match &addr.0 { + AddrType::Byron(byron) => Some(byron.clone()), + _ => None, + } + } +} + +#[wasm_bindgen] +#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] +pub struct Address(pub(crate) AddrType); + +from_bytes!(Address, data, { Self::from_bytes_impl_safe(data.as_ref()) }); + +to_from_json!(Address); + +impl serde::Serialize for Address { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let bech32 = self + .to_bech32(None) + .map_err(|e| serde::ser::Error::custom(format!("to_bech32: {:?}", e)))?; + serializer.serialize_str(&bech32) + } +} + +impl<'de> serde::de::Deserialize<'de> for Address { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let bech32 = ::deserialize(deserializer)?; + Address::from_bech32(&bech32).map_err(|_e| { + serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&bech32), + &"bech32 address string", + ) + }) + } +} + +impl JsonSchema for Address { + fn schema_name() -> String { + String::from("Address") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } + fn is_referenceable() -> bool { + String::is_referenceable() + } +} + +// to/from_bytes() are the raw encoding without a wrapping CBOR Bytes tag +// while Serialize and Deserialize traits include that for inclusion with +// other CBOR types +#[wasm_bindgen] +impl Address { + + pub fn kind(&self) -> AddressKind { + match &self.0 { + AddrType::Base(_) => AddressKind::Base, + AddrType::Ptr(_) => AddressKind::Pointer, + AddrType::Enterprise(_) => AddressKind::Enterprise, + AddrType::Reward(_) => AddressKind::Reward, + AddrType::Byron(_) => AddressKind::Byron, + AddrType::Malformed(_) => AddressKind::Malformed, + } + } + + pub fn payment_cred(&self) -> Option { + match &self.0 { + AddrType::Base(a) => Some(a.payment_cred()), + AddrType::Enterprise(a) => Some(a.payment_cred()), + AddrType::Reward(a) => Some(a.payment_cred()), + AddrType::Ptr(a) => Some(a.payment_cred()), + AddrType::Byron(_) => None, + AddrType::Malformed(_) => None, + } + } + + pub fn is_malformed(&self) -> bool { + matches!(&self.0, AddrType::Malformed(_)) + } + + pub fn to_hex(&self) -> String { + hex::encode(self.to_bytes()) + } + + pub fn from_hex(hex_str: &str) -> Result { + match hex::decode(hex_str) { + Ok(data) => Ok(Self::from_bytes_impl_safe(data.as_ref())?), + Err(e) => Err(JsError::from_str(&e.to_string())), + } + } + + pub fn to_bytes(&self) -> Vec { + let mut buf = Vec::new(); + match &self.0 { + AddrType::Base(base) => { + let header: u8 = ((base.payment.kind() as u8) << 4) + | ((base.stake.kind() as u8) << 5) + | (base.network & 0xF); + buf.push(header); + buf.extend(base.payment.to_raw_bytes()); + buf.extend(base.stake.to_raw_bytes()); + } + AddrType::Ptr(ptr) => { + let header: u8 = + 0b0100_0000 | ((ptr.payment.kind() as u8) << 4) | (ptr.network & 0xF); + buf.push(header); + buf.extend(ptr.payment.to_raw_bytes()); + buf.extend(variable_nat_encode(ptr.stake.slot.into())); + buf.extend(variable_nat_encode(ptr.stake.tx_index.into())); + buf.extend(variable_nat_encode(ptr.stake.cert_index.into())); + } + AddrType::Enterprise(enterprise) => { + let header: u8 = 0b0110_0000 + | ((enterprise.payment.kind() as u8) << 4) + | (enterprise.network & 0xF); + buf.push(header); + buf.extend(enterprise.payment.to_raw_bytes()); + } + AddrType::Reward(reward) => { + let header: u8 = + 0b1110_0000 | ((reward.payment.kind() as u8) << 4) | (reward.network & 0xF); + buf.push(header); + buf.extend(reward.payment.to_raw_bytes()); + } + AddrType::Byron(byron) => buf.extend(byron.to_bytes()), + AddrType::Malformed(malformed) => buf.extend(malformed.0.clone()), + } + buf + } + + fn from_bytes_impl_safe(data: &[u8]) -> Result { + Self::from_bytes_internal_impl(data) + } + + fn from_bytes_impl_unsafe(data: &[u8]) -> Address { + match Self::from_bytes_internal_impl(data) { + Ok(addr) => addr, + Err(_) => Address(AddrType::Malformed(MalformedAddress(data.to_vec()))) + } + } + + fn from_bytes_internal_impl(data: &[u8]) -> Result { + use std::convert::TryInto; + // header has 4 bits addr type discrim then 4 bits network discrim. + // Copied from shelley.cddl: + // + // shelley payment addresses: + // bit 7: 0 + // bit 6: base/other + // bit 5: pointer/enterprise [for base: stake cred is keyhash/scripthash] + // bit 4: payment cred is keyhash/scripthash + // bits 3-0: network id + // + // reward addresses: + // bits 7-5: 111 + // bit 4: credential is keyhash/scripthash + // bits 3-0: network id + // + // byron addresses: + // bits 7-4: 1000 + (|| -> Result { + let header = data[0]; + let network = header & 0x0F; + const HASH_LEN: usize = Ed25519KeyHash::BYTE_COUNT; + // should be static assert but it's maybe not worth importing a whole external crate for it now + assert_eq!(ScriptHash::BYTE_COUNT, HASH_LEN); + // checks the /bit/ bit of the header for key vs scripthash then reads the credential starting at byte position /pos/ + let read_addr_cred = |bit: u8, pos: usize| { + let hash_bytes: [u8; HASH_LEN] = data[pos..pos + HASH_LEN].try_into().unwrap(); + let x = if header & (1 << bit) == 0 { + Credential::from_keyhash(&Ed25519KeyHash::from(hash_bytes)) + } else { + Credential::from_scripthash(&ScriptHash::from(hash_bytes)) + }; + x + }; + let addr: Result = match (header & 0xF0) >> 4 { + // base + 0b0000 | 0b0001 | 0b0010 | 0b0011 => { + const BASE_ADDR_SIZE: usize = 1 + HASH_LEN * 2; + if data.len() < BASE_ADDR_SIZE { + Err(cbor_event::Error::NotEnough(data.len(), BASE_ADDR_SIZE).into()) + } else if data.len() > BASE_ADDR_SIZE { + Err(cbor_event::Error::TrailingData.into()) + } else { + Ok(AddrType::Base(BaseAddress::new( + network, + &read_addr_cred(4, 1), + &read_addr_cred(5, 1 + HASH_LEN), + ))) + } + } + // pointer + 0b0100 | 0b0101 => { + // header + keyhash + 3 natural numbers (min 1 byte each) + const PTR_ADDR_MIN_SIZE: usize = 1 + HASH_LEN + 1 + 1 + 1; + if data.len() < PTR_ADDR_MIN_SIZE { + // possibly more, but depends on how many bytes the natural numbers are for the pointer + Err(cbor_event::Error::NotEnough(data.len(), PTR_ADDR_MIN_SIZE).into()) + } else { + let mut byte_index = 1; + let payment_cred = read_addr_cred(4, 1); + byte_index += HASH_LEN; + match Self::decode_pointer(&data[byte_index..]) { + Ok((pointer, offset)) => { + byte_index += offset; + if byte_index < data.len() { + Err(cbor_event::Error::TrailingData.into()) + } else { + Ok(AddrType::Ptr(PointerAddress::new( + network, + &payment_cred, + &pointer, + ))) + } + } + Err(err) => Err(err) + } + } + } + // enterprise + 0b0110 | 0b0111 => { + const ENTERPRISE_ADDR_SIZE: usize = 1 + HASH_LEN; + if data.len() < ENTERPRISE_ADDR_SIZE { + Err(cbor_event::Error::NotEnough(data.len(), ENTERPRISE_ADDR_SIZE).into()) + } else { + if data.len() > ENTERPRISE_ADDR_SIZE { + Err(cbor_event::Error::TrailingData.into()) + } else { + Ok(AddrType::Enterprise(EnterpriseAddress::new( + network, + &read_addr_cred(4, 1), + ))) + } + } + } + // reward + 0b1110 | 0b1111 => { + const REWARD_ADDR_SIZE: usize = 1 + HASH_LEN; + if data.len() < REWARD_ADDR_SIZE { + Err(cbor_event::Error::NotEnough(data.len(), REWARD_ADDR_SIZE).into()) + } else { + if data.len() > REWARD_ADDR_SIZE { + Err(cbor_event::Error::TrailingData.into()) + } else { + Ok(AddrType::Reward(RewardAddress::new( + network, + &read_addr_cred(4, 1), + ))) + } + } + } + // byron + 0b1000 => { + // note: 0b1000 was chosen because all existing Byron addresses actually start with 0b1000 + // Therefore you can re-use Byron addresses as-is + match ByronAddress::from_bytes(data.to_vec()) { + Ok(addr) => Ok(AddrType::Byron(addr)), + Err(e) => Err(cbor_event::Error::CustomError( + e.as_string().unwrap_or_default(), + ) + .into()), + } + } + _ => Err(DeserializeFailure::BadAddressType(header).into()), + }; + Ok(Address(addr?)) + })() + .map_err(|e| e.annotate("Address")) + } + + fn decode_pointer(data: &[u8]) -> Result<(Pointer, usize), DeserializeError> { + let mut offset = 0; + let (slot, slot_bytes) = variable_nat_decode(&data).ok_or(DeserializeError::new( + "Address.Pointer.slot", + DeserializeFailure::VariableLenNatDecodeFailed, + ))?; + offset += slot_bytes; + let (tx_index, tx_bytes) = + variable_nat_decode(&data[offset..]).ok_or(DeserializeError::new( + "Address.Pointer.tx_index", + DeserializeFailure::VariableLenNatDecodeFailed, + ))?; + offset += tx_bytes; + let (cert_index, cert_bytes) = + variable_nat_decode(&data[offset..]).ok_or(DeserializeError::new( + "Address.Pointer.cert_index", + DeserializeFailure::VariableLenNatDecodeFailed, + ))?; + offset += cert_bytes; + Ok(( + Pointer::new_pointer( + &slot.into(), + &tx_index.into(), + &cert_index.into(), + ), + offset, + )) + } + + pub fn to_bech32(&self, prefix: Option) -> Result { + let final_prefix = match prefix { + Some(prefix) => prefix, + None => { + // see CIP5 for bech32 prefix rules + let prefix_header = match &self.0 { + AddrType::Reward(_) => "stake", + _ => "addr", + }; + let prefix_tail = if self.is_malformed() { + "_malformed" + } else { + match self.network_id()? { + id if id == NetworkInfo::testnet_preprod().network_id() => "_test", + id if id == NetworkInfo::testnet_preview().network_id() => "_test", + _ => "", + } + }; + format!("{}{}", prefix_header, prefix_tail) + } + }; + bech32::encode(&final_prefix, self.to_bytes().to_base32()) + .map_err(|e| JsError::from_str(&format! {"{:?}", e})) + } + + pub fn from_bech32(bech_str: &str) -> Result { + let (_hrp, u5data) = + bech32::decode(bech_str).map_err(|e| JsError::from_str(&e.to_string()))?; + let data: Vec = bech32::FromBase32::from_base32(&u5data).unwrap(); + Ok(Self::from_bytes_impl_safe(data.as_ref())?) + } + + pub fn network_id(&self) -> Result { + match &self.0 { + AddrType::Base(a) => Ok(a.network), + AddrType::Enterprise(a) => Ok(a.network), + AddrType::Ptr(a) => Ok(a.network), + AddrType::Reward(a) => Ok(a.network), + AddrType::Byron(a) => a.network_id(), + AddrType::Malformed(_) => Err(JsError::from_str("Malformed address")), + } + } +} + +impl cbor_event::se::Serialize for Address { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_bytes(self.to_bytes()) + } +} + +impl Deserialize for Address { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self::from_bytes_impl_unsafe(raw.bytes()?.as_ref())) + } +} + +#[wasm_bindgen] +#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] +pub struct BaseAddress { + pub(crate) network: u8, + pub(crate) payment: Credential, + pub(crate) stake: Credential, +} + +#[wasm_bindgen] +impl BaseAddress { + pub fn new(network: u8, payment: &Credential, stake: &Credential) -> Self { + Self { + network, + payment: payment.clone(), + stake: stake.clone(), + } + } + + pub fn payment_cred(&self) -> Credential { + self.payment.clone() + } + + pub fn stake_cred(&self) -> Credential { + self.stake.clone() + } + + pub fn to_address(&self) -> Address { + Address(AddrType::Base(self.clone())) + } + + pub fn from_address(addr: &Address) -> Option { + match &addr.0 { + AddrType::Base(base) => Some(base.clone()), + _ => None, + } + } + + pub fn network_id(&self) -> u8 { + self.network + } +} + +#[wasm_bindgen] +#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] +pub struct EnterpriseAddress { + pub(crate) network: u8, + pub(crate) payment: Credential, +} + +#[wasm_bindgen] +impl EnterpriseAddress { + pub fn new(network: u8, payment: &Credential) -> Self { + Self { + network, + payment: payment.clone(), + } + } + + pub fn payment_cred(&self) -> Credential { + self.payment.clone() + } + + pub fn to_address(&self) -> Address { + Address(AddrType::Enterprise(self.clone())) + } + + pub fn from_address(addr: &Address) -> Option { + match &addr.0 { + AddrType::Enterprise(enterprise) => Some(enterprise.clone()), + _ => None, + } + } + + pub fn network_id(&self) -> u8 { + self.network + } +} + +#[wasm_bindgen] +#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct RewardAddress { + pub(crate) network: u8, + pub(crate) payment: Credential, +} + +#[wasm_bindgen] +impl RewardAddress { + pub fn new(network: u8, payment: &Credential) -> Self { + Self { + network, + payment: payment.clone(), + } + } + + pub fn payment_cred(&self) -> Credential { + self.payment.clone() + } + + pub fn to_address(&self) -> Address { + Address(AddrType::Reward(self.clone())) + } + + pub fn from_address(addr: &Address) -> Option { + match &addr.0 { + AddrType::Reward(reward) => Some(reward.clone()), + _ => None, + } + } + + pub fn network_id(&self) -> u8 { + self.network + } +} + +impl serde::Serialize for RewardAddress { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let bech32 = self + .to_address() + .to_bech32(None) + .map_err(|e| serde::ser::Error::custom(format!("to_bech32: {:?}", e)))?; + serializer.serialize_str(&bech32) + } +} + +impl<'de> serde::de::Deserialize<'de> for RewardAddress { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let bech32 = ::deserialize(deserializer)?; + match Address::from_bech32(&bech32) + .ok() + .map(|addr| RewardAddress::from_address(&addr)) + { + Some(Some(ra)) => Ok(ra), + _ => Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&bech32), + &"bech32 reward address string", + )), + } + } +} + +impl JsonSchema for RewardAddress { + fn schema_name() -> String { + String::from("RewardAddress") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } + fn is_referenceable() -> bool { + String::is_referenceable() + } +} + +// needed since we treat RewardAccount like RewardAddress +impl cbor_event::se::Serialize for RewardAddress { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + self.to_address().serialize(serializer) + } +} + +impl Deserialize for RewardAddress { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result { + let bytes = raw.bytes()?; + match Address::from_bytes_impl_safe(bytes.as_ref())?.0 { + AddrType::Reward(ra) => Ok(ra), + _other_address => Err(DeserializeFailure::BadAddressType(bytes[0]).into()), + } + })() + .map_err(|e| e.annotate("RewardAddress")) + } +} + +#[wasm_bindgen] +#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] +pub struct Pointer { + pub(crate) slot: BigNum, + pub(crate) tx_index: BigNum, + pub(crate) cert_index: BigNum, +} + +#[wasm_bindgen] +impl Pointer { + /// !!! DEPRECATED !!! + /// This constructor uses outdated slot number format for the ttl value, tx_index and cert_index. + /// Use `.new_pointer` instead + #[deprecated( + since = "10.1.0", + note = "Underlying value capacity of ttl (BigNum u64) bigger then Slot32. Use new_pointer instead." + )] + pub fn new(slot: Slot32, tx_index: TransactionIndex, cert_index: CertificateIndex) -> Self { + Self { + slot: slot.into(), + tx_index: tx_index.into(), + cert_index: cert_index.into(), + } + } + + pub fn new_pointer(slot: &SlotBigNum, tx_index: &BigNum, cert_index: &BigNum) -> Self { + Self { + slot: slot.clone(), + tx_index: tx_index.clone(), + cert_index: cert_index.clone(), + } + } + + pub fn slot(&self) -> Result { + self.slot.clone().try_into() + } + + pub fn tx_index(&self) -> Result { + self.tx_index.clone().try_into() + } + + pub fn cert_index(&self) -> Result { + self.cert_index.clone().try_into() + } + + pub fn slot_bignum(&self) -> BigNum { + self.slot.clone() + } + + pub fn tx_index_bignum(&self) -> BigNum { + self.tx_index.clone() + } + + pub fn cert_index_bignum(&self) -> BigNum { + self.cert_index.clone() + } +} + +#[wasm_bindgen] +#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] +pub struct PointerAddress { + pub(crate) network: u8, + pub(crate) payment: Credential, + pub(crate) stake: Pointer, +} + +#[wasm_bindgen] +impl PointerAddress { + pub fn new(network: u8, payment: &Credential, stake: &Pointer) -> Self { + Self { + network, + payment: payment.clone(), + stake: stake.clone(), + } + } + + pub fn payment_cred(&self) -> Credential { + self.payment.clone() + } + + pub fn stake_pointer(&self) -> Pointer { + self.stake.clone() + } + + pub fn to_address(&self) -> Address { + Address(AddrType::Ptr(self.clone())) + } + + pub fn from_address(addr: &Address) -> Option { + match &addr.0 { + AddrType::Ptr(ptr) => Some(ptr.clone()), + _ => None, + } + } + + pub fn network_id(&self) -> u8 { + self.network + } +} diff --git a/rust/src/protocol_types/block/block.rs b/rust/src/protocol_types/block/block.rs new file mode 100644 index 00000000..ff1d10fe --- /dev/null +++ b/rust/src/protocol_types/block/block.rs @@ -0,0 +1,54 @@ +use crate::*; + +pub type TransactionIndexes = Vec; + +#[wasm_bindgen] +#[derive(Clone, Eq, Debug, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct Block { + pub(crate) header: Header, + pub(crate) transaction_bodies: TransactionBodies, + pub(crate) transaction_witness_sets: TransactionWitnessSets, + pub(crate) auxiliary_data_set: AuxiliaryDataSet, + pub(crate) invalid_transactions: TransactionIndexes, +} + +impl_to_from!(Block); + +#[wasm_bindgen] +impl Block { + pub fn header(&self) -> Header { + self.header.clone() + } + + pub fn transaction_bodies(&self) -> TransactionBodies { + self.transaction_bodies.clone() + } + + pub fn transaction_witness_sets(&self) -> TransactionWitnessSets { + self.transaction_witness_sets.clone() + } + + pub fn auxiliary_data_set(&self) -> AuxiliaryDataSet { + self.auxiliary_data_set.clone() + } + + pub fn invalid_transactions(&self) -> TransactionIndexes { + self.invalid_transactions.clone() + } + + pub fn new( + header: &Header, + transaction_bodies: &TransactionBodies, + transaction_witness_sets: &TransactionWitnessSets, + auxiliary_data_set: &AuxiliaryDataSet, + invalid_transactions: TransactionIndexes, + ) -> Self { + Self { + header: header.clone(), + transaction_bodies: transaction_bodies.clone(), + transaction_witness_sets: transaction_witness_sets.clone(), + auxiliary_data_set: auxiliary_data_set.clone(), + invalid_transactions: invalid_transactions, + } + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/block/fixed_block.rs b/rust/src/protocol_types/block/fixed_block.rs new file mode 100644 index 00000000..fb22a3eb --- /dev/null +++ b/rust/src/protocol_types/block/fixed_block.rs @@ -0,0 +1,44 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Eq, Debug, PartialEq)] +/// Read only view of a block with more strict structs for hash sensitive structs. +/// Warning: This is experimental and may be removed or changed in the future. +pub struct FixedBlock { + pub(crate) header: Header, + pub(crate) transaction_bodies: FixedTransactionBodies, + pub(crate) transaction_witness_sets: TransactionWitnessSets, + pub(crate) auxiliary_data_set: AuxiliaryDataSet, + pub(crate) invalid_transactions: TransactionIndexes, + pub(crate) block_hash: BlockHash, +} + +from_bytes!(FixedBlock); +from_hex!(FixedBlock); + +#[wasm_bindgen] +impl FixedBlock { + pub fn header(&self) -> Header { + self.header.clone() + } + + pub fn transaction_bodies(&self) -> FixedTransactionBodies { + self.transaction_bodies.clone() + } + + pub fn transaction_witness_sets(&self) -> TransactionWitnessSets { + self.transaction_witness_sets.clone() + } + + pub fn auxiliary_data_set(&self) -> AuxiliaryDataSet { + self.auxiliary_data_set.clone() + } + + pub fn invalid_transactions(&self) -> TransactionIndexes { + self.invalid_transactions.clone() + } + + pub fn block_hash(&self) -> BlockHash { + self.block_hash.clone() + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/block/fixed_transaction_bodies.rs b/rust/src/protocol_types/block/fixed_transaction_bodies.rs new file mode 100644 index 00000000..b480f4cd --- /dev/null +++ b/rust/src/protocol_types/block/fixed_transaction_bodies.rs @@ -0,0 +1,28 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Eq, Debug, PartialEq)] +/// Warning: This is experimental and may be removed or changed in the future. +pub struct FixedTransactionBodies(pub(crate) Vec); + +from_bytes!(FixedTransactionBodies); +from_hex!(FixedTransactionBodies); + +#[wasm_bindgen] +impl FixedTransactionBodies { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> FixedTransactionBody { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &FixedTransactionBody) { + self.0.push(elem.clone()); + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/block/fixed_tx_body.rs b/rust/src/protocol_types/block/fixed_tx_body.rs new file mode 100644 index 00000000..d1a4b4eb --- /dev/null +++ b/rust/src/protocol_types/block/fixed_tx_body.rs @@ -0,0 +1,30 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Eq, Debug, PartialEq)] +/// Read-only view of a transaction body. With correct hash and original bytes. +/// Warning: This is experimental and may be removed in the future. +pub struct FixedTransactionBody { + pub(crate) body: TransactionBody, + pub(crate) tx_hash: TransactionHash, + pub(crate) original_bytes: Vec, +} + +from_bytes!(FixedTransactionBody); +from_hex!(FixedTransactionBody); + +#[wasm_bindgen] +impl FixedTransactionBody { + + pub fn transaction_body(&self) -> TransactionBody { + self.body.clone() + } + + pub fn tx_hash(&self) -> TransactionHash { + self.tx_hash.clone() + } + + pub fn original_bytes(&self) -> Vec { + self.original_bytes.clone() + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/block/fixed_versioned_block.rs b/rust/src/protocol_types/block/fixed_versioned_block.rs new file mode 100644 index 00000000..82f3921d --- /dev/null +++ b/rust/src/protocol_types/block/fixed_versioned_block.rs @@ -0,0 +1,33 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Eq, Debug, PartialEq)] +/// Warning: This is experimental and may be removed in the future. +pub struct FixedVersionedBlock { + pub(crate) block: FixedBlock, + pub(crate) era_code: u32, +} + +from_bytes!(FixedVersionedBlock); +from_hex!(FixedVersionedBlock); + +#[wasm_bindgen] +impl FixedVersionedBlock { + pub fn block(&self) -> FixedBlock { + self.block.clone() + } + + pub fn era(&self) -> BlockEra { + match self.era_code { + 0 => BlockEra::Byron, + 1 => BlockEra::Byron, + 2 => BlockEra::Shelley, + 3 => BlockEra::Allegra, + 4 => BlockEra::Mary, + 5 => BlockEra::Alonzo, + 6 => BlockEra::Babbage, + 7 => BlockEra::Conway, + _ => BlockEra::Unknown, + } + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/block/header.rs b/rust/src/protocol_types/block/header.rs new file mode 100644 index 00000000..3a382363 --- /dev/null +++ b/rust/src/protocol_types/block/header.rs @@ -0,0 +1,28 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Eq, Debug, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct Header { + pub(crate) header_body: HeaderBody, + pub(crate) body_signature: KESSignature, +} + +impl_to_from!(Header); + +#[wasm_bindgen] +impl Header { + pub fn header_body(&self) -> HeaderBody { + self.header_body.clone() + } + + pub fn body_signature(&self) -> KESSignature { + self.body_signature.clone() + } + + pub fn new(header_body: &HeaderBody, body_signature: &KESSignature) -> Self { + Self { + header_body: header_body.clone(), + body_signature: body_signature.clone(), + } + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/block/header_body.rs b/rust/src/protocol_types/block/header_body.rs new file mode 100644 index 00000000..ae37f54e --- /dev/null +++ b/rust/src/protocol_types/block/header_body.rs @@ -0,0 +1,176 @@ +use crate::*; + +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)] +pub enum HeaderLeaderCertEnum { + NonceAndLeader(VRFCert, VRFCert), + VrfResult(VRFCert), +} + +#[wasm_bindgen] +#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct HeaderBody { + pub(crate) block_number: u32, + pub(crate) slot: SlotBigNum, + pub(crate) prev_hash: Option, + pub(crate) issuer_vkey: Vkey, + pub(crate) vrf_vkey: VRFVKey, + pub(crate) leader_cert: HeaderLeaderCertEnum, + pub(crate) block_body_size: u32, + pub(crate) block_body_hash: BlockHash, + pub(crate) operational_cert: OperationalCert, + pub(crate) protocol_version: ProtocolVersion, +} + +impl_to_from!(HeaderBody); + +#[wasm_bindgen] +impl HeaderBody { + pub fn block_number(&self) -> u32 { + self.block_number.clone() + } + + /// !!! DEPRECATED !!! + /// Returns a Slot32 (u32) value in case the underlying original BigNum (u64) value is within the limits. + /// Otherwise will just raise an error. + #[deprecated( + since = "10.1.0", + note = "Possible boundary error. Use slot_bignum instead" + )] + pub fn slot(&self) -> Result { + self.slot.clone().try_into() + } + + pub fn slot_bignum(&self) -> SlotBigNum { + self.slot.clone() + } + + pub fn prev_hash(&self) -> Option { + self.prev_hash.clone() + } + + pub fn issuer_vkey(&self) -> Vkey { + self.issuer_vkey.clone() + } + + pub fn vrf_vkey(&self) -> VRFVKey { + self.vrf_vkey.clone() + } + + /// If this function returns true, the `.nonce_vrf_or_nothing` + /// and the `.leader_vrf_or_nothing` functions will return + /// non-empty results + pub fn has_nonce_and_leader_vrf(&self) -> bool { + match &self.leader_cert { + HeaderLeaderCertEnum::NonceAndLeader(_, _) => true, + _ => false, + } + } + + /// Might return nothing in case `.has_nonce_and_leader_vrf` returns false + pub fn nonce_vrf_or_nothing(&self) -> Option { + match &self.leader_cert { + HeaderLeaderCertEnum::NonceAndLeader(nonce, _) => Some(nonce.clone()), + _ => None, + } + } + + /// Might return nothing in case `.has_nonce_and_leader_vrf` returns false + pub fn leader_vrf_or_nothing(&self) -> Option { + match &self.leader_cert { + HeaderLeaderCertEnum::NonceAndLeader(_, leader) => Some(leader.clone()), + _ => None, + } + } + + /// If this function returns true, the `.vrf_result_or_nothing` + /// function will return a non-empty result + pub fn has_vrf_result(&self) -> bool { + match &self.leader_cert { + HeaderLeaderCertEnum::VrfResult(_) => true, + _ => false, + } + } + + /// Might return nothing in case `.has_vrf_result` returns false + pub fn vrf_result_or_nothing(&self) -> Option { + match &self.leader_cert { + HeaderLeaderCertEnum::VrfResult(cert) => Some(cert.clone()), + _ => None, + } + } + + pub fn block_body_size(&self) -> u32 { + self.block_body_size.clone() + } + + pub fn block_body_hash(&self) -> BlockHash { + self.block_body_hash.clone() + } + + pub fn operational_cert(&self) -> OperationalCert { + self.operational_cert.clone() + } + + pub fn protocol_version(&self) -> ProtocolVersion { + self.protocol_version.clone() + } + + /// !!! DEPRECATED !!! + /// This constructor uses outdated slot number format. + /// Use `.new_headerbody` instead + #[deprecated( + since = "10.1.0", + note = "Underlying value capacity of slot (BigNum u64) bigger then Slot32. Use new_bignum instead." + )] + pub fn new( + block_number: u32, + slot: Slot32, + prev_hash: Option, + issuer_vkey: &Vkey, + vrf_vkey: &VRFVKey, + vrf_result: &VRFCert, + block_body_size: u32, + block_body_hash: &BlockHash, + operational_cert: &OperationalCert, + protocol_version: &ProtocolVersion, + ) -> Self { + Self { + block_number: block_number, + slot: slot.clone().into(), + prev_hash: prev_hash.clone(), + issuer_vkey: issuer_vkey.clone(), + vrf_vkey: vrf_vkey.clone(), + leader_cert: HeaderLeaderCertEnum::VrfResult(vrf_result.clone()), + block_body_size: block_body_size, + block_body_hash: block_body_hash.clone(), + operational_cert: operational_cert.clone(), + protocol_version: protocol_version.clone(), + } + } + + pub fn new_headerbody( + block_number: u32, + slot: &SlotBigNum, + prev_hash: Option, + issuer_vkey: &Vkey, + vrf_vkey: &VRFVKey, + vrf_result: &VRFCert, + block_body_size: u32, + block_body_hash: &BlockHash, + operational_cert: &OperationalCert, + protocol_version: &ProtocolVersion, + ) -> Self { + Self { + block_number: block_number, + slot: slot.clone(), + prev_hash: prev_hash.clone(), + issuer_vkey: issuer_vkey.clone(), + vrf_vkey: vrf_vkey.clone(), + leader_cert: HeaderLeaderCertEnum::VrfResult(vrf_result.clone()), + block_body_size: block_body_size, + block_body_hash: block_body_hash.clone(), + operational_cert: operational_cert.clone(), + protocol_version: protocol_version.clone(), + } + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/block/mod.rs b/rust/src/protocol_types/block/mod.rs new file mode 100644 index 00000000..1e50c4c3 --- /dev/null +++ b/rust/src/protocol_types/block/mod.rs @@ -0,0 +1,29 @@ +mod block; +pub use block::*; + +mod fixed_block; +pub use fixed_block::*; + +mod fixed_tx_body; +pub use fixed_tx_body::*; + +mod header; +pub use header::*; + +mod transaction_bodies; +pub use transaction_bodies::*; + +mod header_body; +pub use header_body::*; + +mod operational_cert; +pub use operational_cert::*; + +mod fixed_versioned_block; +pub use fixed_versioned_block::*; + +mod fixed_transaction_bodies; +pub use fixed_transaction_bodies::*; + +mod versioned_block; +pub use versioned_block::*; \ No newline at end of file diff --git a/rust/src/protocol_types/block/operational_cert.rs b/rust/src/protocol_types/block/operational_cert.rs new file mode 100644 index 00000000..28e5e7a7 --- /dev/null +++ b/rust/src/protocol_types/block/operational_cert.rs @@ -0,0 +1,45 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct OperationalCert { + pub(crate) hot_vkey: KESVKey, + pub(crate) sequence_number: u32, + pub(crate) kes_period: u32, + pub(crate) sigma: Ed25519Signature, +} + +impl_to_from!(OperationalCert); + +#[wasm_bindgen] +impl OperationalCert { + pub fn hot_vkey(&self) -> KESVKey { + self.hot_vkey.clone() + } + + pub fn sequence_number(&self) -> u32 { + self.sequence_number.clone() + } + + pub fn kes_period(&self) -> u32 { + self.kes_period.clone() + } + + pub fn sigma(&self) -> Ed25519Signature { + self.sigma.clone() + } + + pub fn new( + hot_vkey: &KESVKey, + sequence_number: u32, + kes_period: u32, + sigma: &Ed25519Signature, + ) -> Self { + Self { + hot_vkey: hot_vkey.clone(), + sequence_number: sequence_number, + kes_period: kes_period, + sigma: sigma.clone(), + } + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/block/transaction_bodies.rs b/rust/src/protocol_types/block/transaction_bodies.rs new file mode 100644 index 00000000..8e9493ce --- /dev/null +++ b/rust/src/protocol_types/block/transaction_bodies.rs @@ -0,0 +1,26 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Eq, Debug, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct TransactionBodies(pub(crate) Vec); + +impl_to_from!(TransactionBodies); + +#[wasm_bindgen] +impl TransactionBodies { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> TransactionBody { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &TransactionBody) { + self.0.push(elem.clone()); + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/block/versioned_block.rs b/rust/src/protocol_types/block/versioned_block.rs new file mode 100644 index 00000000..70a64661 --- /dev/null +++ b/rust/src/protocol_types/block/versioned_block.rs @@ -0,0 +1,51 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize, JsonSchema)] +pub enum BlockEra { + Byron, + Shelley, + Allegra, + Mary, + Alonzo, + Babbage, + Conway, + Unknown +} + +#[wasm_bindgen] +#[derive(Clone, Eq, Debug, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct VersionedBlock { + pub(crate) era_code: u32, + pub(crate) block: Block, +} + +impl_to_from!(VersionedBlock); + +#[wasm_bindgen] +impl VersionedBlock { + pub fn new(block: Block, era_code: u32) -> VersionedBlock { + VersionedBlock { + block, + era_code, + } + } + + pub fn block(&self) -> Block { + self.block.clone() + } + + pub fn era(&self) -> BlockEra { + match self.era_code { + 0 => BlockEra::Byron, + 1 => BlockEra::Byron, + 2 => BlockEra::Shelley, + 3 => BlockEra::Allegra, + 4 => BlockEra::Mary, + 5 => BlockEra::Alonzo, + 6 => BlockEra::Babbage, + 7 => BlockEra::Conway, + _ => BlockEra::Unknown, + } + } +} diff --git a/rust/src/protocol_types/certificates/certificate.rs b/rust/src/protocol_types/certificates/certificate.rs new file mode 100644 index 00000000..53660dd8 --- /dev/null +++ b/rust/src/protocol_types/certificates/certificate.rs @@ -0,0 +1,418 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum CertificateKind { + StakeRegistration, + StakeDeregistration, + StakeDelegation, + PoolRegistration, + PoolRetirement, + GenesisKeyDelegation, + MoveInstantaneousRewardsCert, + CommitteeHotAuth, + CommitteeColdResign, + DRepDeregistration, + DRepRegistration, + DRepUpdate, + StakeAndVoteDelegation, + StakeRegistrationAndDelegation, + StakeVoteRegistrationAndDelegation, + VoteDelegation, + VoteRegistrationAndDelegation, +} + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub enum CertificateEnum { + StakeRegistration(StakeRegistration), + StakeDeregistration(StakeDeregistration), + StakeDelegation(StakeDelegation), + PoolRegistration(PoolRegistration), + PoolRetirement(PoolRetirement), + GenesisKeyDelegation(GenesisKeyDelegation), + MoveInstantaneousRewardsCert(MoveInstantaneousRewardsCert), + CommitteeHotAuth(CommitteeHotAuth), + CommitteeColdResign(CommitteeColdResign), + DRepDeregistration(DRepDeregistration), + DRepRegistration(DRepRegistration), + DRepUpdate(DRepUpdate), + StakeAndVoteDelegation(StakeAndVoteDelegation), + StakeRegistrationAndDelegation(StakeRegistrationAndDelegation), + StakeVoteRegistrationAndDelegation(StakeVoteRegistrationAndDelegation), + VoteDelegation(VoteDelegation), + VoteRegistrationAndDelegation(VoteRegistrationAndDelegation), +} + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct Certificate(pub(crate) CertificateEnum); + +impl_to_from!(Certificate); + +#[wasm_bindgen] +impl Certificate { + + pub fn new_stake_registration(stake_registration: &StakeRegistration) -> Self { + Self(CertificateEnum::StakeRegistration( + stake_registration.clone(), + )) + } + + /// Since StakeRegistration can represent stake_registration certificate or reg_cert certificate, because both certificates have the same semantics. + /// And in some cases you want to create a reg_cert, this function is used to create a reg_cert. + /// The function will return an error if StakeRegistration represents a stake_registration certificate. + pub fn new_reg_cert(stake_registration: &StakeRegistration) -> Result { + if stake_registration.coin.is_none() { + return Err(JsError::from_str("coin is required")); + } else { + Ok(Self(CertificateEnum::StakeRegistration( + stake_registration.clone(), + ))) + } + } + + pub fn new_stake_deregistration(stake_deregistration: &StakeDeregistration) -> Self { + Self(CertificateEnum::StakeDeregistration( + stake_deregistration.clone(), + )) + } + + /// Since StakeDeregistration can represent stake_deregistration certificate or unreg_cert certificate, because both certificates have the same semantics. + /// And in some cases you want to create an unreg_cert, this function is used to create an unreg_cert. + /// The function will return an error if StakeDeregistration represents a stake_deregistration certificate. + pub fn new_unreg_cert(stake_deregistration: &StakeDeregistration) -> Result { + if stake_deregistration.coin.is_none() { + return Err(JsError::from_str("coin is required")); + } else { + Ok(Self(CertificateEnum::StakeDeregistration( + stake_deregistration.clone(), + ))) + } + } + + pub fn new_stake_delegation(stake_delegation: &StakeDelegation) -> Self { + Self(CertificateEnum::StakeDelegation(stake_delegation.clone())) + } + + pub fn new_pool_registration(pool_registration: &PoolRegistration) -> Self { + Self(CertificateEnum::PoolRegistration(pool_registration.clone())) + } + + pub fn new_pool_retirement(pool_retirement: &PoolRetirement) -> Self { + Self(CertificateEnum::PoolRetirement(pool_retirement.clone())) + } + + pub fn new_genesis_key_delegation(genesis_key_delegation: &GenesisKeyDelegation) -> Self { + Self(CertificateEnum::GenesisKeyDelegation( + genesis_key_delegation.clone(), + )) + } + + pub fn new_move_instantaneous_rewards_cert( + move_instantaneous_rewards_cert: &MoveInstantaneousRewardsCert, + ) -> Self { + Self(CertificateEnum::MoveInstantaneousRewardsCert( + move_instantaneous_rewards_cert.clone(), + )) + } + + pub fn new_committee_hot_auth( + committee_hot_auth: &CommitteeHotAuth, + ) -> Self { + Self(CertificateEnum::CommitteeHotAuth( + committee_hot_auth.clone(), + )) + } + + pub fn new_committee_cold_resign( + committee_cold_resign: &CommitteeColdResign, + ) -> Self { + Self(CertificateEnum::CommitteeColdResign( + committee_cold_resign.clone(), + )) + } + + pub fn new_drep_deregistration(drep_deregistration: &DRepDeregistration) -> Self { + Self(CertificateEnum::DRepDeregistration( + drep_deregistration.clone(), + )) + } + + pub fn new_drep_registration(drep_registration: &DRepRegistration) -> Self { + Self(CertificateEnum::DRepRegistration(drep_registration.clone())) + } + + pub fn new_drep_update(drep_update: &DRepUpdate) -> Self { + Self(CertificateEnum::DRepUpdate(drep_update.clone())) + } + + pub fn new_stake_and_vote_delegation( + stake_and_vote_delegation: &StakeAndVoteDelegation, + ) -> Self { + Self(CertificateEnum::StakeAndVoteDelegation( + stake_and_vote_delegation.clone(), + )) + } + + pub fn new_stake_registration_and_delegation( + stake_registration_and_delegation: &StakeRegistrationAndDelegation, + ) -> Self { + Self(CertificateEnum::StakeRegistrationAndDelegation( + stake_registration_and_delegation.clone(), + )) + } + + pub fn new_stake_vote_registration_and_delegation( + stake_vote_registration_and_delegation: &StakeVoteRegistrationAndDelegation, + ) -> Self { + Self(CertificateEnum::StakeVoteRegistrationAndDelegation( + stake_vote_registration_and_delegation.clone(), + )) + } + + pub fn new_vote_delegation(vote_delegation: &VoteDelegation) -> Self { + Self(CertificateEnum::VoteDelegation(vote_delegation.clone())) + } + + pub fn new_vote_registration_and_delegation( + vote_registration_and_delegation: &VoteRegistrationAndDelegation, + ) -> Self { + Self(CertificateEnum::VoteRegistrationAndDelegation( + vote_registration_and_delegation.clone(), + )) + } + + pub fn kind(&self) -> CertificateKind { + match &self.0 { + CertificateEnum::StakeRegistration(_) => CertificateKind::StakeRegistration, + CertificateEnum::StakeDeregistration(_) => CertificateKind::StakeDeregistration, + CertificateEnum::StakeDelegation(_) => CertificateKind::StakeDelegation, + CertificateEnum::PoolRegistration(_) => CertificateKind::PoolRegistration, + CertificateEnum::PoolRetirement(_) => CertificateKind::PoolRetirement, + CertificateEnum::GenesisKeyDelegation(_) => CertificateKind::GenesisKeyDelegation, + CertificateEnum::MoveInstantaneousRewardsCert(_) => { + CertificateKind::MoveInstantaneousRewardsCert + } + CertificateEnum::CommitteeHotAuth(_) => { + CertificateKind::CommitteeHotAuth + } + CertificateEnum::CommitteeColdResign(_) => { + CertificateKind::CommitteeColdResign + } + CertificateEnum::DRepDeregistration(_) => CertificateKind::DRepDeregistration, + CertificateEnum::DRepRegistration(_) => CertificateKind::DRepRegistration, + CertificateEnum::DRepUpdate(_) => CertificateKind::DRepUpdate, + CertificateEnum::StakeAndVoteDelegation(_) => CertificateKind::StakeAndVoteDelegation, + CertificateEnum::StakeRegistrationAndDelegation(_) => { + CertificateKind::StakeRegistrationAndDelegation + } + CertificateEnum::StakeVoteRegistrationAndDelegation(_) => { + CertificateKind::StakeVoteRegistrationAndDelegation + } + CertificateEnum::VoteDelegation(_) => CertificateKind::VoteDelegation, + CertificateEnum::VoteRegistrationAndDelegation(_) => { + CertificateKind::VoteRegistrationAndDelegation + } + } + } + + pub fn as_stake_registration(&self) -> Option { + match &self.0 { + CertificateEnum::StakeRegistration(x) => Some(x.clone()), + _ => None, + } + } + + /// Since StakeRegistration can represent stake_registration certificate or reg_cert certificate, because both certificates have the same semantics. + /// And in some cases you want to get a reg_cert, this function is used to get a reg_cert. + /// The function will return None if StakeRegistration represents a stake_registration certificate or Certificate is not a StakeRegistration. + pub fn as_reg_cert(&self) -> Option { + match &self.0 { + CertificateEnum::StakeRegistration(x) => { + return if x.coin.is_some() { + Some(x.clone()) + } else { + None + } + } + _ => None, + } + } + + pub fn as_stake_deregistration(&self) -> Option { + match &self.0 { + CertificateEnum::StakeDeregistration(x) => Some(x.clone()), + _ => None, + } + } + + /// Since StakeDeregistration can represent stake_deregistration certificate or unreg_cert certificate, because both certificates have the same semantics. + /// And in some cases you want to get an unreg_cert, this function is used to get an unreg_cert. + /// The function will return None if StakeDeregistration represents a stake_deregistration certificate or Certificate is not a StakeDeregistration. + pub fn as_unreg_cert(&self) -> Option { + match &self.0 { + CertificateEnum::StakeDeregistration(x) => { + return if x.coin.is_some() { + Some(x.clone()) + } else { + None + } + } + _ => None, + } + } + + pub fn as_stake_delegation(&self) -> Option { + match &self.0 { + CertificateEnum::StakeDelegation(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_pool_registration(&self) -> Option { + match &self.0 { + CertificateEnum::PoolRegistration(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_pool_retirement(&self) -> Option { + match &self.0 { + CertificateEnum::PoolRetirement(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_genesis_key_delegation(&self) -> Option { + match &self.0 { + CertificateEnum::GenesisKeyDelegation(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_move_instantaneous_rewards_cert(&self) -> Option { + match &self.0 { + CertificateEnum::MoveInstantaneousRewardsCert(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_committee_hot_auth(&self) -> Option { + match &self.0 { + CertificateEnum::CommitteeHotAuth(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_committee_cold_resign(&self) -> Option { + match &self.0 { + CertificateEnum::CommitteeColdResign(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_drep_deregistration(&self) -> Option { + match &self.0 { + CertificateEnum::DRepDeregistration(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_drep_registration(&self) -> Option { + match &self.0 { + CertificateEnum::DRepRegistration(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_drep_update(&self) -> Option { + match &self.0 { + CertificateEnum::DRepUpdate(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_stake_and_vote_delegation(&self) -> Option { + match &self.0 { + CertificateEnum::StakeAndVoteDelegation(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_stake_registration_and_delegation(&self) -> Option { + match &self.0 { + CertificateEnum::StakeRegistrationAndDelegation(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_stake_vote_registration_and_delegation( + &self, + ) -> Option { + match &self.0 { + CertificateEnum::StakeVoteRegistrationAndDelegation(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_vote_delegation(&self) -> Option { + match &self.0 { + CertificateEnum::VoteDelegation(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_vote_registration_and_delegation(&self) -> Option { + match &self.0 { + CertificateEnum::VoteRegistrationAndDelegation(x) => Some(x.clone()), + _ => None, + } + } + + pub fn has_required_script_witness(&self) -> bool { + match &self.0 { + CertificateEnum::StakeRegistration(x) => { + if x.coin.is_some() { + return x.has_script_credentials(); + } else { + return false; + } + } + CertificateEnum::StakeDeregistration(x) => x.has_script_credentials(), + CertificateEnum::StakeDelegation(x) => x.has_script_credentials(), + CertificateEnum::VoteDelegation(x) => x.has_script_credentials(), + CertificateEnum::StakeAndVoteDelegation(x) => x.has_script_credentials(), + CertificateEnum::StakeRegistrationAndDelegation(x) => x.has_script_credentials(), + CertificateEnum::StakeVoteRegistrationAndDelegation(x) => x.has_script_credentials(), + CertificateEnum::VoteRegistrationAndDelegation(x) => x.has_script_credentials(), + CertificateEnum::CommitteeHotAuth(x) => x.has_script_credentials(), + CertificateEnum::CommitteeColdResign(x) => x.has_script_credentials(), + CertificateEnum::DRepRegistration(x) => x.has_script_credentials(), + CertificateEnum::DRepDeregistration(x) => x.has_script_credentials(), + CertificateEnum::DRepUpdate(x) => x.has_script_credentials(), + _ => false, + } + } +} diff --git a/rust/src/protocol_types/certificates/certificates_collection.rs b/rust/src/protocol_types/certificates/certificates_collection.rs new file mode 100644 index 00000000..d316f6e1 --- /dev/null +++ b/rust/src/protocol_types/certificates/certificates_collection.rs @@ -0,0 +1,95 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, +)] +pub struct Certificates { + pub(crate) certs: Vec, + pub(crate) dedup: BTreeSet +} + +impl_to_from!(Certificates); + +impl NoneOrEmpty for Certificates { + fn is_none_or_empty(&self) -> bool { + self.certs.is_empty() + } +} + +#[wasm_bindgen] +impl Certificates { + pub fn new() -> Self { + Self { + certs: Vec::new(), + dedup: BTreeSet::new(), + } + } + + pub fn len(&self) -> usize { + self.certs.len() + } + + pub fn get(&self, index: usize) -> Certificate { + self.certs[index].clone() + } + + /// Add a new `Certificate` to the set. + /// Returns `true` if the element was not already present in the set. + pub fn add(&mut self, elem: &Certificate) -> bool { + if self.dedup.insert(elem.clone()) { + self.certs.push(elem.clone()); + true + } else { + false + } + } + + pub(crate) fn add_move(&mut self, elem: Certificate) { + if self.dedup.insert(elem.clone()) { + self.certs.push(elem); + } + } + + + pub(crate) fn from_vec(certs_vec: Vec) -> Self { + let mut certs = Self::new(); + for cert in certs_vec { + certs.add_move(cert); + } + certs + } +} + +impl serde::Serialize for Certificates { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.certs.serialize(serializer) + } +} + +impl<'de> serde::de::Deserialize<'de> for Certificates { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let vec = as serde::de::Deserialize>::deserialize( + deserializer, + )?; + Ok(Self::from_vec(vec)) + } +} + +impl JsonSchema for Certificates { + fn schema_name() -> String { + String::from("Certificates") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + Vec::::json_schema(gen) + } + fn is_referenceable() -> bool { + Vec::::is_referenceable() + } +} diff --git a/rust/src/protocol_types/certificates/committee_cold_resign.rs b/rust/src/protocol_types/certificates/committee_cold_resign.rs new file mode 100644 index 00000000..6e28d85e --- /dev/null +++ b/rust/src/protocol_types/certificates/committee_cold_resign.rs @@ -0,0 +1,50 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct CommitteeColdResign { + pub(crate) committee_cold_credential: Credential, + pub(crate) anchor: Option, +} + +impl_to_from!(CommitteeColdResign); + +#[wasm_bindgen] +impl CommitteeColdResign { + pub fn committee_cold_credential(&self) -> Credential { + self.committee_cold_credential.clone() + } + + pub fn anchor(&self) -> Option { + self.anchor.clone() + } + + pub fn new(committee_cold_credential: &Credential) -> Self { + Self { + committee_cold_credential: committee_cold_credential.clone(), + anchor: None, + } + } + + pub fn new_with_anchor(committee_cold_credential: &Credential, anchor: &Anchor) -> Self { + Self { + committee_cold_credential: committee_cold_credential.clone(), + anchor: Some(anchor.clone()), + } + } + + pub fn has_script_credentials(&self) -> bool { + self.committee_cold_credential.has_script_hash() + } +} diff --git a/rust/src/protocol_types/certificates/committee_hot_auth.rs b/rust/src/protocol_types/certificates/committee_hot_auth.rs new file mode 100644 index 00000000..d0adaa39 --- /dev/null +++ b/rust/src/protocol_types/certificates/committee_hot_auth.rs @@ -0,0 +1,43 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct CommitteeHotAuth { + pub(crate) committee_cold_credential: Credential, + pub(crate) committee_hot_credential: Credential, +} + +impl_to_from!(CommitteeHotAuth); + +#[wasm_bindgen] +impl CommitteeHotAuth { + pub fn committee_cold_credential(&self) -> Credential { + self.committee_cold_credential.clone() + } + + pub fn committee_hot_credential(&self) -> Credential { + self.committee_hot_credential.clone() + } + + pub fn new(committee_cold_credential: &Credential, committee_hot_credential: &Credential) -> Self { + Self { + committee_cold_credential: committee_cold_credential.clone(), + committee_hot_credential: committee_hot_credential.clone(), + } + } + + pub fn has_script_credentials(&self) -> bool { + self.committee_cold_credential.has_script_hash() + } +} diff --git a/rust/src/protocol_types/certificates/drep_deregistration.rs b/rust/src/protocol_types/certificates/drep_deregistration.rs new file mode 100644 index 00000000..f67eb955 --- /dev/null +++ b/rust/src/protocol_types/certificates/drep_deregistration.rs @@ -0,0 +1,43 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct DRepDeregistration { + pub(crate) voting_credential: Credential, + pub(crate) coin: Coin, +} + +impl_to_from!(DRepDeregistration); + +#[wasm_bindgen] +impl DRepDeregistration { + pub fn voting_credential(&self) -> Credential { + self.voting_credential.clone() + } + + pub fn coin(&self) -> Coin { + self.coin.clone() + } + + pub fn new(voting_credential: &Credential, coin: &Coin) -> Self { + Self { + voting_credential: voting_credential.clone(), + coin: coin.clone(), + } + } + + pub fn has_script_credentials(&self) -> bool { + self.voting_credential.has_script_hash() + } +} diff --git a/rust/src/protocol_types/certificates/drep_registration.rs b/rust/src/protocol_types/certificates/drep_registration.rs new file mode 100644 index 00000000..c0ad2d84 --- /dev/null +++ b/rust/src/protocol_types/certificates/drep_registration.rs @@ -0,0 +1,57 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct DRepRegistration { + pub(crate) voting_credential: Credential, + pub(crate) coin: Coin, + pub(crate) anchor: Option, +} + +impl_to_from!(DRepRegistration); + +#[wasm_bindgen] +impl DRepRegistration { + pub fn voting_credential(&self) -> Credential { + self.voting_credential.clone() + } + + pub fn coin(&self) -> Coin { + self.coin.clone() + } + + pub fn anchor(&self) -> Option { + self.anchor.clone() + } + + pub fn new(voting_credential: &Credential, coin: &Coin) -> Self { + Self { + voting_credential: voting_credential.clone(), + coin: coin.clone(), + anchor: None, + } + } + + pub fn new_with_anchor(voting_credential: &Credential, coin: &Coin, anchor: &Anchor) -> Self { + Self { + voting_credential: voting_credential.clone(), + coin: coin.clone(), + anchor: Some(anchor.clone()), + } + } + + pub fn has_script_credentials(&self) -> bool { + self.voting_credential.has_script_hash() + } +} diff --git a/rust/src/protocol_types/certificates/drep_update.rs b/rust/src/protocol_types/certificates/drep_update.rs new file mode 100644 index 00000000..d845aaa6 --- /dev/null +++ b/rust/src/protocol_types/certificates/drep_update.rs @@ -0,0 +1,50 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct DRepUpdate { + pub(crate) voting_credential: Credential, + pub(crate) anchor: Option, +} + +impl_to_from!(DRepUpdate); + +#[wasm_bindgen] +impl DRepUpdate { + pub fn voting_credential(&self) -> Credential { + self.voting_credential.clone() + } + + pub fn anchor(&self) -> Option { + self.anchor.clone() + } + + pub fn new(voting_credential: &Credential) -> Self { + Self { + voting_credential: voting_credential.clone(), + anchor: None, + } + } + + pub fn new_with_anchor(voting_credential: &Credential, anchor: &Anchor) -> Self { + Self { + voting_credential: voting_credential.clone(), + anchor: Some(anchor.clone()), + } + } + + pub fn has_script_credentials(&self) -> bool { + self.voting_credential.has_script_hash() + } +} diff --git a/rust/src/protocol_types/certificates/genesis_key_delegation.rs b/rust/src/protocol_types/certificates/genesis_key_delegation.rs new file mode 100644 index 00000000..93a118ad --- /dev/null +++ b/rust/src/protocol_types/certificates/genesis_key_delegation.rs @@ -0,0 +1,49 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct GenesisKeyDelegation { + pub(crate) genesishash: GenesisHash, + pub(crate) genesis_delegate_hash: GenesisDelegateHash, + pub(crate) vrf_keyhash: VRFKeyHash, +} + +impl_to_from!(GenesisKeyDelegation); + +#[wasm_bindgen] +impl GenesisKeyDelegation { + pub fn genesishash(&self) -> GenesisHash { + self.genesishash.clone() + } + + pub fn genesis_delegate_hash(&self) -> GenesisDelegateHash { + self.genesis_delegate_hash.clone() + } + + pub fn vrf_keyhash(&self) -> VRFKeyHash { + self.vrf_keyhash.clone() + } + + pub fn new( + genesishash: &GenesisHash, + genesis_delegate_hash: &GenesisDelegateHash, + vrf_keyhash: &VRFKeyHash, + ) -> Self { + Self { + genesishash: genesishash.clone(), + genesis_delegate_hash: genesis_delegate_hash.clone(), + vrf_keyhash: vrf_keyhash.clone(), + } + } +} diff --git a/rust/src/protocol_types/certificates/mod.rs b/rust/src/protocol_types/certificates/mod.rs new file mode 100644 index 00000000..57ab6175 --- /dev/null +++ b/rust/src/protocol_types/certificates/mod.rs @@ -0,0 +1,56 @@ +mod certificate; +pub use certificate::*; + +mod certificates_collection; +pub use certificates_collection::*; + +mod genesis_key_delegation; +pub use genesis_key_delegation::*; + +mod move_instantaneous_rewards_cert; +pub use move_instantaneous_rewards_cert::*; + +mod pool_registration; +pub use pool_registration::*; + +mod pool_retirement; +pub use pool_retirement::*; + +mod stake_delegation; +pub use stake_delegation::*; + +mod stake_deregistration; +pub use stake_deregistration::*; + +mod stake_registration; +pub use stake_registration::*; + +mod vote_delegation; +pub use vote_delegation::*; + +mod stake_and_vote_delegation; +pub use stake_and_vote_delegation::*; + +mod stake_registration_and_delegation; +pub use stake_registration_and_delegation::*; + +mod stake_vote_registration_and_delegation; +pub use stake_vote_registration_and_delegation::*; + +mod vote_registration_and_delegation; +pub use vote_registration_and_delegation::*; + +mod committee_hot_auth; +pub use committee_hot_auth::*; + +mod committee_cold_resign; +pub use committee_cold_resign::*; + +mod drep_registration; +pub use drep_registration::*; + +mod drep_deregistration; +pub use drep_deregistration::*; + +mod drep_update; +pub use drep_update::*; diff --git a/rust/src/protocol_types/certificates/move_instantaneous_rewards_cert.rs b/rust/src/protocol_types/certificates/move_instantaneous_rewards_cert.rs new file mode 100644 index 00000000..2acb1ed9 --- /dev/null +++ b/rust/src/protocol_types/certificates/move_instantaneous_rewards_cert.rs @@ -0,0 +1,240 @@ +use crate::*; +use std::vec::Vec; +use hashlink::LinkedHashMap; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct MoveInstantaneousRewardsCert { + pub(crate) move_instantaneous_reward: MoveInstantaneousReward, +} + +impl_to_from!(MoveInstantaneousRewardsCert); + +#[wasm_bindgen] +impl MoveInstantaneousRewardsCert { + pub fn move_instantaneous_reward(&self) -> MoveInstantaneousReward { + self.move_instantaneous_reward.clone() + } + + pub fn new(move_instantaneous_reward: &MoveInstantaneousReward) -> Self { + Self { + move_instantaneous_reward: move_instantaneous_reward.clone(), + } + } +} + +#[wasm_bindgen] +#[derive( + Clone, + Copy, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub enum MIRPot { + Reserves, + Treasury, +} + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub enum MIREnum { + ToOtherPot(Coin), + ToStakeCredentials(MIRToStakeCredentials), +} + +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub enum MIRKind { + ToOtherPot, + ToStakeCredentials, +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] +pub struct MIRToStakeCredentials { + pub(crate) rewards: LinkedHashMap, +} + +impl_to_from!(MIRToStakeCredentials); + +#[wasm_bindgen] +impl MIRToStakeCredentials { + pub fn new() -> Self { + Self { + rewards: LinkedHashMap::new(), + } + } + + pub fn len(&self) -> usize { + self.rewards.len() + } + + pub fn insert(&mut self, cred: &Credential, delta: &DeltaCoin) -> Option { + self.rewards.insert(cred.clone(), delta.clone()) + } + + pub fn get(&self, cred: &Credential) -> Option { + self.rewards.get(cred).map(|v| v.clone()) + } + + pub fn keys(&self) -> Credentials { + Credentials::from_iter( + self.rewards + .iter() + .map(|(k, _v)| k.clone()) + ) + } +} + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +struct StakeToCoin { + stake_cred: Credential, + amount: DeltaCoin, +} + +impl serde::Serialize for MIRToStakeCredentials { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let vec = self + .rewards + .iter() + .map(|(k, v)| StakeToCoin { + stake_cred: k.clone(), + amount: v.clone(), + }) + .collect::>(); + vec.serialize(serializer) + } +} + +impl<'de> serde::de::Deserialize<'de> for MIRToStakeCredentials { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let map = Vec::::deserialize(deserializer)? + .into_iter() + .map(|v| (v.stake_cred, v.amount)); + + Ok(Self { + rewards: map.collect(), + }) + } +} + +impl JsonSchema for MIRToStakeCredentials { + fn schema_name() -> String { + String::from("MIRToStakeCredentials") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + Vec::::json_schema(gen) + } + fn is_referenceable() -> bool { + Vec::::is_referenceable() + } +} + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct MoveInstantaneousReward { + pub(crate) pot: MIRPot, + pub(crate) variant: MIREnum, +} + +impl_to_from!(MoveInstantaneousReward); + +#[wasm_bindgen] +impl MoveInstantaneousReward { + pub fn new_to_other_pot(pot: MIRPot, amount: &Coin) -> Self { + Self { + pot, + variant: MIREnum::ToOtherPot(amount.clone()), + } + } + + pub fn new_to_stake_creds(pot: MIRPot, amounts: &MIRToStakeCredentials) -> Self { + Self { + pot, + variant: MIREnum::ToStakeCredentials(amounts.clone()), + } + } + + pub fn pot(&self) -> MIRPot { + self.pot + } + + pub fn kind(&self) -> MIRKind { + match &self.variant { + MIREnum::ToOtherPot(_) => MIRKind::ToOtherPot, + MIREnum::ToStakeCredentials(_) => MIRKind::ToStakeCredentials, + } + } + + pub fn as_to_other_pot(&self) -> Option { + match &self.variant { + MIREnum::ToOtherPot(amount) => Some(amount.clone()), + MIREnum::ToStakeCredentials(_) => None, + } + } + + pub fn as_to_stake_creds(&self) -> Option { + match &self.variant { + MIREnum::ToOtherPot(_) => None, + MIREnum::ToStakeCredentials(amounts) => Some(amounts.clone()), + } + } +} diff --git a/rust/src/protocol_types/certificates/pool_registration.rs b/rust/src/protocol_types/certificates/pool_registration.rs new file mode 100644 index 00000000..80904fd8 --- /dev/null +++ b/rust/src/protocol_types/certificates/pool_registration.rs @@ -0,0 +1,159 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct PoolRegistration { + pub(crate) pool_params: PoolParams, +} + +impl_to_from!(PoolRegistration); + +#[wasm_bindgen] +impl PoolRegistration { + pub fn pool_params(&self) -> PoolParams { + self.pool_params.clone() + } + + pub fn new(pool_params: &PoolParams) -> Self { + Self { + pool_params: pool_params.clone(), + } + } +} + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct Relays(pub(crate) Vec); + +impl_to_from!(Relays); + +#[wasm_bindgen] +impl Relays { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> Relay { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &Relay) { + self.0.push(elem.clone()); + } +} + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct PoolParams { + pub(crate) operator: Ed25519KeyHash, + pub(crate) vrf_keyhash: VRFKeyHash, + pub(crate) pledge: Coin, + pub(crate) cost: Coin, + pub(crate) margin: UnitInterval, + pub(crate) reward_account: RewardAddress, + pub(crate) pool_owners: Ed25519KeyHashes, + pub(crate) relays: Relays, + pub(crate) pool_metadata: Option, +} + +impl_to_from!(PoolParams); + +#[wasm_bindgen] +impl PoolParams { + pub fn operator(&self) -> Ed25519KeyHash { + self.operator.clone() + } + + pub fn vrf_keyhash(&self) -> VRFKeyHash { + self.vrf_keyhash.clone() + } + + pub fn pledge(&self) -> Coin { + self.pledge.clone() + } + + pub fn cost(&self) -> Coin { + self.cost.clone() + } + + pub fn margin(&self) -> UnitInterval { + self.margin.clone() + } + + pub fn reward_account(&self) -> RewardAddress { + self.reward_account.clone() + } + + pub fn pool_owners(&self) -> Ed25519KeyHashes { + self.pool_owners.clone() + } + + pub fn relays(&self) -> Relays { + self.relays.clone() + } + + pub fn pool_metadata(&self) -> Option { + self.pool_metadata.clone() + } + + pub fn new( + operator: &Ed25519KeyHash, + vrf_keyhash: &VRFKeyHash, + pledge: &Coin, + cost: &Coin, + margin: &UnitInterval, + reward_account: &RewardAddress, + pool_owners: &Ed25519KeyHashes, + relays: &Relays, + pool_metadata: Option, + ) -> Self { + Self { + operator: operator.clone(), + vrf_keyhash: vrf_keyhash.clone(), + pledge: pledge.clone(), + cost: cost.clone(), + margin: margin.clone(), + reward_account: reward_account.clone(), + pool_owners: pool_owners.clone(), + relays: relays.clone(), + pool_metadata: pool_metadata.clone(), + } + } +} diff --git a/rust/src/protocol_types/certificates/pool_retirement.rs b/rust/src/protocol_types/certificates/pool_retirement.rs new file mode 100644 index 00000000..f8eb060e --- /dev/null +++ b/rust/src/protocol_types/certificates/pool_retirement.rs @@ -0,0 +1,39 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct PoolRetirement { + pub(crate) pool_keyhash: Ed25519KeyHash, + pub(crate) epoch: Epoch, +} + +impl_to_from!(PoolRetirement); + +#[wasm_bindgen] +impl PoolRetirement { + pub fn pool_keyhash(&self) -> Ed25519KeyHash { + self.pool_keyhash.clone() + } + + pub fn epoch(&self) -> Epoch { + self.epoch.clone() + } + + pub fn new(pool_keyhash: &Ed25519KeyHash, epoch: Epoch) -> Self { + Self { + pool_keyhash: pool_keyhash.clone(), + epoch: epoch, + } + } +} diff --git a/rust/src/protocol_types/certificates/stake_and_vote_delegation.rs b/rust/src/protocol_types/certificates/stake_and_vote_delegation.rs new file mode 100644 index 00000000..45b264f4 --- /dev/null +++ b/rust/src/protocol_types/certificates/stake_and_vote_delegation.rs @@ -0,0 +1,49 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct StakeAndVoteDelegation { + pub(crate) stake_credential: Credential, + pub(crate) pool_keyhash: Ed25519KeyHash, + pub(crate) drep: DRep, +} + +impl_to_from!(StakeAndVoteDelegation); + +#[wasm_bindgen] +impl StakeAndVoteDelegation { + pub fn stake_credential(&self) -> Credential { + self.stake_credential.clone() + } + + pub fn pool_keyhash(&self) -> Ed25519KeyHash { + self.pool_keyhash.clone() + } + + pub fn drep(&self) -> DRep { + self.drep.clone() + } + + pub fn new(stake_credential: &Credential, pool_keyhash: &Ed25519KeyHash, drep: &DRep) -> Self { + Self { + stake_credential: stake_credential.clone(), + pool_keyhash: pool_keyhash.clone(), + drep: drep.clone(), + } + } + + pub fn has_script_credentials(&self) -> bool { + self.stake_credential.has_script_hash() + } +} diff --git a/rust/src/protocol_types/certificates/stake_delegation.rs b/rust/src/protocol_types/certificates/stake_delegation.rs new file mode 100644 index 00000000..a7d9004e --- /dev/null +++ b/rust/src/protocol_types/certificates/stake_delegation.rs @@ -0,0 +1,43 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct StakeDelegation { + pub(crate) stake_credential: Credential, + pub(crate) pool_keyhash: Ed25519KeyHash, +} + +impl_to_from!(StakeDelegation); + +#[wasm_bindgen] +impl StakeDelegation { + pub fn stake_credential(&self) -> Credential { + self.stake_credential.clone() + } + + pub fn pool_keyhash(&self) -> Ed25519KeyHash { + self.pool_keyhash.clone() + } + + pub fn new(stake_credential: &Credential, pool_keyhash: &Ed25519KeyHash) -> Self { + Self { + stake_credential: stake_credential.clone(), + pool_keyhash: pool_keyhash.clone(), + } + } + + pub fn has_script_credentials(&self) -> bool { + self.stake_credential.has_script_hash() + } +} diff --git a/rust/src/protocol_types/certificates/stake_deregistration.rs b/rust/src/protocol_types/certificates/stake_deregistration.rs new file mode 100644 index 00000000..4670148d --- /dev/null +++ b/rust/src/protocol_types/certificates/stake_deregistration.rs @@ -0,0 +1,50 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct StakeDeregistration { + pub(crate) stake_credential: Credential, + pub(crate) coin: Option, +} + +impl_to_from!(StakeDeregistration); + +#[wasm_bindgen] +impl StakeDeregistration { + pub fn stake_credential(&self) -> Credential { + self.stake_credential.clone() + } + + pub fn coin(&self) -> Option { + self.coin.clone() + } + + pub fn new(stake_credential: &Credential) -> Self { + Self { + stake_credential: stake_credential.clone(), + coin: None, + } + } + + pub fn new_with_explicit_refund(stake_credential: &Credential, coin: &Coin) -> Self { + Self { + stake_credential: stake_credential.clone(), + coin: Some(coin.clone()), + } + } + + pub fn has_script_credentials(&self) -> bool { + self.stake_credential.has_script_hash() + } +} diff --git a/rust/src/protocol_types/certificates/stake_registration.rs b/rust/src/protocol_types/certificates/stake_registration.rs new file mode 100644 index 00000000..c0cd6f77 --- /dev/null +++ b/rust/src/protocol_types/certificates/stake_registration.rs @@ -0,0 +1,50 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct StakeRegistration { + pub(crate) stake_credential: Credential, + pub(crate) coin: Option, +} + +impl_to_from!(StakeRegistration); + +#[wasm_bindgen] +impl StakeRegistration { + pub fn stake_credential(&self) -> Credential { + self.stake_credential.clone() + } + + pub fn coin(&self) -> Option { + self.coin.clone() + } + + pub fn new(stake_credential: &Credential) -> Self { + Self { + stake_credential: stake_credential.clone(), + coin: None, + } + } + + pub fn new_with_explicit_deposit(stake_credential: &Credential, coin: &Coin) -> Self { + Self { + stake_credential: stake_credential.clone(), + coin: Some(coin.clone()), + } + } + + pub fn has_script_credentials(&self) -> bool { + self.stake_credential.has_script_hash() + } +} diff --git a/rust/src/protocol_types/certificates/stake_registration_and_delegation.rs b/rust/src/protocol_types/certificates/stake_registration_and_delegation.rs new file mode 100644 index 00000000..f0105bf3 --- /dev/null +++ b/rust/src/protocol_types/certificates/stake_registration_and_delegation.rs @@ -0,0 +1,49 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct StakeRegistrationAndDelegation { + pub(crate) stake_credential: Credential, + pub(crate) pool_keyhash: Ed25519KeyHash, + pub(crate) coin: Coin, +} + +impl_to_from!(StakeRegistrationAndDelegation); + +#[wasm_bindgen] +impl StakeRegistrationAndDelegation { + pub fn stake_credential(&self) -> Credential { + self.stake_credential.clone() + } + + pub fn pool_keyhash(&self) -> Ed25519KeyHash { + self.pool_keyhash.clone() + } + + pub fn coin(&self) -> Coin { + self.coin.clone() + } + + pub fn new(stake_credential: &Credential, pool_keyhash: &Ed25519KeyHash, coin: &Coin) -> Self { + Self { + stake_credential: stake_credential.clone(), + pool_keyhash: pool_keyhash.clone(), + coin: coin.clone(), + } + } + + pub fn has_script_credentials(&self) -> bool { + self.stake_credential.has_script_hash() + } +} diff --git a/rust/src/protocol_types/certificates/stake_vote_registration_and_delegation.rs b/rust/src/protocol_types/certificates/stake_vote_registration_and_delegation.rs new file mode 100644 index 00000000..65e38734 --- /dev/null +++ b/rust/src/protocol_types/certificates/stake_vote_registration_and_delegation.rs @@ -0,0 +1,60 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct StakeVoteRegistrationAndDelegation { + pub(crate) stake_credential: Credential, + pub(crate) pool_keyhash: Ed25519KeyHash, + pub(crate) drep: DRep, + pub(crate) coin: Coin, +} + +impl_to_from!(StakeVoteRegistrationAndDelegation); + +#[wasm_bindgen] +impl StakeVoteRegistrationAndDelegation { + pub fn stake_credential(&self) -> Credential { + self.stake_credential.clone() + } + + pub fn pool_keyhash(&self) -> Ed25519KeyHash { + self.pool_keyhash.clone() + } + + pub fn drep(&self) -> DRep { + self.drep.clone() + } + + pub fn coin(&self) -> Coin { + self.coin.clone() + } + + pub fn new( + stake_credential: &Credential, + pool_keyhash: &Ed25519KeyHash, + drep: &DRep, + coin: &Coin, + ) -> Self { + Self { + stake_credential: stake_credential.clone(), + pool_keyhash: pool_keyhash.clone(), + drep: drep.clone(), + coin: coin.clone(), + } + } + + pub fn has_script_credentials(&self) -> bool { + self.stake_credential.has_script_hash() + } +} diff --git a/rust/src/protocol_types/certificates/vote_delegation.rs b/rust/src/protocol_types/certificates/vote_delegation.rs new file mode 100644 index 00000000..a0e21ea2 --- /dev/null +++ b/rust/src/protocol_types/certificates/vote_delegation.rs @@ -0,0 +1,43 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct VoteDelegation { + pub(crate) stake_credential: Credential, + pub(crate) drep: DRep, +} + +impl_to_from!(VoteDelegation); + +#[wasm_bindgen] +impl VoteDelegation { + pub fn stake_credential(&self) -> Credential { + self.stake_credential.clone() + } + + pub fn drep(&self) -> DRep { + self.drep.clone() + } + + pub fn new(stake_credential: &Credential, drep: &DRep) -> Self { + Self { + stake_credential: stake_credential.clone(), + drep: drep.clone(), + } + } + + pub fn has_script_credentials(&self) -> bool { + self.stake_credential.has_script_hash() + } +} diff --git a/rust/src/protocol_types/certificates/vote_registration_and_delegation.rs b/rust/src/protocol_types/certificates/vote_registration_and_delegation.rs new file mode 100644 index 00000000..0426b254 --- /dev/null +++ b/rust/src/protocol_types/certificates/vote_registration_and_delegation.rs @@ -0,0 +1,49 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct VoteRegistrationAndDelegation { + pub(crate) stake_credential: Credential, + pub(crate) drep: DRep, + pub(crate) coin: Coin, +} + +impl_to_from!(VoteRegistrationAndDelegation); + +#[wasm_bindgen] +impl VoteRegistrationAndDelegation { + pub fn stake_credential(&self) -> Credential { + self.stake_credential.clone() + } + + pub fn drep(&self) -> DRep { + self.drep.clone() + } + + pub fn coin(&self) -> Coin { + self.coin.clone() + } + + pub fn new(stake_credential: &Credential, drep: &DRep, coin: &Coin) -> Self { + Self { + stake_credential: stake_credential.clone(), + drep: drep.clone(), + coin: coin.clone(), + } + } + + pub fn has_script_credentials(&self) -> bool { + self.stake_credential.has_script_hash() + } +} diff --git a/rust/src/protocol_types/credential.rs b/rust/src/protocol_types/credential.rs new file mode 100644 index 00000000..02445df9 --- /dev/null +++ b/rust/src/protocol_types/credential.rs @@ -0,0 +1,89 @@ +use crate::*; + +#[derive( +Debug, +Clone, +Hash, +Eq, +Ord, +PartialEq, +PartialOrd, +serde::Serialize, +serde::Deserialize, +JsonSchema, +)] +pub enum CredType { + Key(Ed25519KeyHash), + Script(ScriptHash), +} + +#[wasm_bindgen] +#[repr(u8)] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum CredKind { + Key, + Script, +} + +#[wasm_bindgen] +#[derive( +Debug, +Clone, +Eq, +Hash, +Ord, +PartialEq, +PartialOrd, +serde::Serialize, +serde::Deserialize, +JsonSchema, +)] +pub struct Credential(pub(crate) CredType); + +#[wasm_bindgen] +impl Credential { + pub fn from_keyhash(hash: &Ed25519KeyHash) -> Self { + Credential(CredType::Key(hash.clone())) + } + + pub fn from_scripthash(hash: &ScriptHash) -> Self { + Credential(CredType::Script(hash.clone())) + } + + pub fn to_keyhash(&self) -> Option { + match &self.0 { + CredType::Key(hash) => Some(hash.clone()), + CredType::Script(_) => None, + } + } + + pub fn to_scripthash(&self) -> Option { + match &self.0 { + CredType::Key(_) => None, + CredType::Script(hash) => Some(hash.clone()), + } + } + + pub fn kind(&self) -> CredKind { + match &self.0 { + CredType::Key(_) => CredKind::Key, + CredType::Script(_) => CredKind::Script, + } + } + + pub fn has_script_hash(&self) -> bool { + match &self.0 { + CredType::Key(_) => false, + CredType::Script(_) => true, + } + } + + pub(crate) fn to_raw_bytes(&self) -> Vec { + match &self.0 { + CredType::Key(hash) => hash.to_bytes(), + CredType::Script(hash) => hash.to_bytes(), + } + } +} + +impl_to_from!(Credential); diff --git a/rust/src/protocol_types/credentials.rs b/rust/src/protocol_types/credentials.rs new file mode 100644 index 00000000..244c76da --- /dev/null +++ b/rust/src/protocol_types/credentials.rs @@ -0,0 +1,124 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, +)] +pub struct Credentials { + pub(crate) credentials: Vec, + pub(crate) dedup: BTreeSet +} + +impl_to_from!(Credentials); + +#[wasm_bindgen] +impl Credentials { + pub fn new() -> Self { + Self { + credentials: Vec::new(), + dedup: BTreeSet::new(), + } + } + + pub fn len(&self) -> usize { + self.credentials.len() + } + + pub fn get(&self, index: usize) -> Credential { + self.credentials[index].clone() + } + + /// Add a new `Credential` to the set. + /// Returns `true` if the element was not already present in the set. + pub fn add(&mut self, elem: &Credential) -> bool { + if self.dedup.insert(elem.clone()) { + self.credentials.push(elem.clone()); + true + } else { + false + } + } + + pub(crate) fn add_move(&mut self, elem: Credential) { + if self.dedup.insert(elem.clone()) { + self.credentials.push(elem); + } + } + + #[allow(dead_code)] + pub(crate) fn contains(&self, elem: &Credential) -> bool { + self.dedup.contains(elem) + } + + pub(crate) fn from_vec(vec: Vec) -> Self { + let mut dedup = BTreeSet::new(); + let mut credentials = Vec::new(); + for elem in vec { + if dedup.insert(elem.clone()) { + credentials.push(elem); + } + } + Self { + credentials, + dedup + } + } + + pub(crate) fn from_iter(iter: impl IntoIterator) -> Self { + let mut dedup = BTreeSet::new(); + let mut credentials = Vec::new(); + for elem in iter { + if dedup.insert(elem.clone()) { + credentials.push(elem); + } + } + Self { + credentials, + dedup + } + } + + pub(crate) fn to_vec(&self) -> &Vec { + &self.credentials + } +} + + +impl serde::Serialize for Credentials { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.credentials.serialize(serializer) + } +} + +impl<'de> serde::de::Deserialize<'de> for Credentials { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let vec = as serde::de::Deserialize>::deserialize( + deserializer, + )?; + Ok(Self::from_vec(vec)) + } +} + +impl JsonSchema for Credentials { + fn schema_name() -> String { + String::from("Credentials") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + Vec::::json_schema(gen) + } + fn is_referenceable() -> bool { + Vec::::is_referenceable() + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/crypto/bip32_private_key.rs b/rust/src/protocol_types/crypto/bip32_private_key.rs new file mode 100644 index 00000000..b94639ad --- /dev/null +++ b/rust/src/protocol_types/crypto/bip32_private_key.rs @@ -0,0 +1,119 @@ +use crate::*; +use crate::impl_mockchain::key; +use rand_os::OsRng; +use crate::chain_crypto::bech32::Bech32; + +#[wasm_bindgen] +pub struct Bip32PrivateKey(chain_crypto::SecretKey); + +#[wasm_bindgen] +impl Bip32PrivateKey { + /// derive this private key with the given index. + /// + /// # Security considerations + /// + /// * hard derivation index cannot be soft derived with the public key + /// + /// # Hard derivation vs Soft derivation + /// + /// If you pass an index below 0x80000000 then it is a soft derivation. + /// The advantage of soft derivation is that it is possible to derive the + /// public key too. I.e. derivation the private key with a soft derivation + /// index and then retrieving the associated public key is equivalent to + /// deriving the public key associated to the parent private key. + /// + /// Hard derivation index does not allow public key derivation. + /// + /// This is why deriving the private key should not fail while deriving + /// the public key may fail (if the derivation index is invalid). + /// + pub fn derive(&self, index: u32) -> Bip32PrivateKey { + Bip32PrivateKey(crate::chain_crypto::derive::derive_sk_ed25519(&self.0, index)) + } + + /// 128-byte xprv a key format in Cardano that some software still uses or requires + /// the traditional 96-byte xprv is simply encoded as + /// prv | chaincode + /// however, because some software may not know how to compute a public key from a private key, + /// the 128-byte inlines the public key in the following format + /// prv | pub | chaincode + /// so be careful if you see the term "xprv" as it could refer to either one + /// our library does not require the pub (instead we compute the pub key when needed) + pub fn from_128_xprv(bytes: &[u8]) -> Result { + let mut buf = [0; 96]; + buf[0..64].clone_from_slice(&bytes[0..64]); + buf[64..96].clone_from_slice(&bytes[96..128]); + + Bip32PrivateKey::from_bytes(&buf) + } + /// see from_128_xprv + pub fn to_128_xprv(&self) -> Vec { + let prv_key = self.to_raw_key().as_bytes(); + let pub_key = self.to_public().to_raw_key().as_bytes(); + let cc = self.chaincode(); + + let mut buf = [0; 128]; + buf[0..64].clone_from_slice(&prv_key); + buf[64..96].clone_from_slice(&pub_key); + buf[96..128].clone_from_slice(&cc); + buf.to_vec() + } + + pub fn generate_ed25519_bip32() -> Result { + OsRng::new() + .map(crate::chain_crypto::SecretKey::::generate) + .map(Bip32PrivateKey) + .map_err(|e| JsError::from_str(&format!("{}", e))) + } + + pub fn to_raw_key(&self) -> PrivateKey { + PrivateKey(key::EitherEd25519SecretKey::Extended( + crate::chain_crypto::derive::to_raw_sk(&self.0), + )) + } + + pub fn to_public(&self) -> Bip32PublicKey { + Bip32PublicKey(self.0.to_public().into()) + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + crate::chain_crypto::SecretKey::::from_binary(bytes) + .map_err(|e| JsError::from_str(&format!("{}", e))) + .map(Bip32PrivateKey) + } + + pub fn as_bytes(&self) -> Vec { + self.0.as_ref().to_vec() + } + + pub fn from_bech32(bech32_str: &str) -> Result { + crate::chain_crypto::SecretKey::try_from_bech32_str(&bech32_str) + .map(Bip32PrivateKey) + .map_err(|_| JsError::from_str("Invalid secret key")) + } + + pub fn to_bech32(&self) -> String { + self.0.to_bech32_str() + } + + pub fn from_bip39_entropy(entropy: &[u8], password: &[u8]) -> Bip32PrivateKey { + Bip32PrivateKey(crate::chain_crypto::derive::from_bip39_entropy(&entropy, &password)) + } + + pub fn chaincode(&self) -> Vec { + const ED25519_PRIVATE_KEY_LENGTH: usize = 64; + const XPRV_SIZE: usize = 96; + self.0.as_ref()[ED25519_PRIVATE_KEY_LENGTH..XPRV_SIZE].to_vec() + } + + pub fn to_hex(&self) -> String { + hex::encode(self.as_bytes()) + } + + pub fn from_hex(hex_str: &str) -> Result { + match hex::decode(hex_str) { + Ok(data) => Ok(Self::from_bytes(data.as_ref())?), + Err(e) => Err(JsError::from_str(&e.to_string())), + } + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/crypto/bip32_public_key.rs b/rust/src/protocol_types/crypto/bip32_public_key.rs new file mode 100644 index 00000000..8741959f --- /dev/null +++ b/rust/src/protocol_types/crypto/bip32_public_key.rs @@ -0,0 +1,79 @@ +use crate::{JsError, PublicKey, wasm_bindgen}; +use crate::chain_crypto::bech32::Bech32; + +#[wasm_bindgen] +pub struct Bip32PublicKey(pub(crate) crate::chain_crypto::PublicKey); + +#[wasm_bindgen] +impl Bip32PublicKey { + /// derive this public key with the given index. + /// + /// # Errors + /// + /// If the index is not a soft derivation index (< 0x80000000) then + /// calling this method will fail. + /// + /// # Security considerations + /// + /// * hard derivation index cannot be soft derived with the public key + /// + /// # Hard derivation vs Soft derivation + /// + /// If you pass an index below 0x80000000 then it is a soft derivation. + /// The advantage of soft derivation is that it is possible to derive the + /// public key too. I.e. derivation the private key with a soft derivation + /// index and then retrieving the associated public key is equivalent to + /// deriving the public key associated to the parent private key. + /// + /// Hard derivation index does not allow public key derivation. + /// + /// This is why deriving the private key should not fail while deriving + /// the public key may fail (if the derivation index is invalid). + /// + pub fn derive(&self, index: u32) -> Result { + crate::chain_crypto::derive::derive_pk_ed25519(&self.0, index) + .map(Bip32PublicKey) + .map_err(|e| JsError::from_str(&format! {"{:?}", e})) + } + + pub fn to_raw_key(&self) -> PublicKey { + PublicKey(crate::chain_crypto::derive::to_raw_pk(&self.0)) + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + crate::chain_crypto::PublicKey::::from_binary(bytes) + .map_err(|e| JsError::from_str(&format!("{}", e))) + .map(Bip32PublicKey) + } + + pub fn as_bytes(&self) -> Vec { + self.0.as_ref().to_vec() + } + + pub fn from_bech32(bech32_str: &str) -> Result { + crate::chain_crypto::PublicKey::try_from_bech32_str(&bech32_str) + .map(Bip32PublicKey) + .map_err(|e| JsError::from_str(&format!("{}", e))) + } + + pub fn to_bech32(&self) -> String { + self.0.to_bech32_str() + } + + pub fn chaincode(&self) -> Vec { + const ED25519_PUBLIC_KEY_LENGTH: usize = 32; + const XPUB_SIZE: usize = 64; + self.0.as_ref()[ED25519_PUBLIC_KEY_LENGTH..XPUB_SIZE].to_vec() + } + + pub fn to_hex(&self) -> String { + hex::encode(self.as_bytes()) + } + + pub fn from_hex(hex_str: &str) -> Result { + match hex::decode(hex_str) { + Ok(data) => Ok(Self::from_bytes(data.as_ref())?), + Err(e) => Err(JsError::from_str(&e.to_string())), + } + } +} diff --git a/rust/src/protocol_types/crypto/impl_hash_type_macro.rs b/rust/src/protocol_types/crypto/impl_hash_type_macro.rs new file mode 100644 index 00000000..085b77b8 --- /dev/null +++ b/rust/src/protocol_types/crypto/impl_hash_type_macro.rs @@ -0,0 +1,144 @@ +#[macro_export] +macro_rules! impl_hash_type { + ($name:ident, $byte_count:expr) => { + #[wasm_bindgen] + #[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] + pub struct $name(pub(crate) [u8; $byte_count]); + + // hash types are the only types in this library to not expect the entire CBOR structure. + // There is no CBOR binary tag here just the raw hash bytes. + from_bytes!($name, bytes, { + use std::convert::TryInto; + match bytes.len() { + $byte_count => Ok($name(bytes[..$byte_count].try_into().unwrap())), + other_len => { + let cbor_error = cbor_event::Error::WrongLen( + $byte_count, + cbor_event::Len::Len(other_len as u64), + "hash length", + ); + Err(DeserializeError::new( + stringify!($name), + DeserializeFailure::CBOR(cbor_error), + )) + } + } + }); + + #[wasm_bindgen] + impl $name { + // hash types are the only types in this library to not give the entire CBOR structure. + // There is no CBOR binary tag here just the raw hash bytes. + pub fn to_bytes(&self) -> Vec { + self.0.to_vec() + } + + pub fn to_bech32(&self, prefix: &str) -> Result { + use bech32::ToBase32; + bech32::encode(&prefix, self.to_bytes().to_base32()) + .map_err(|e| JsError::from_str(&format! {"{:?}", e})) + } + + pub fn from_bech32(bech_str: &str) -> Result<$name, JsError> { + let (_hrp, u5data) = + bech32::decode(bech_str).map_err(|e| JsError::from_str(&e.to_string()))?; + let data: Vec = bech32::FromBase32::from_base32(&u5data).unwrap(); + Ok(Self::from_bytes(data)?) + } + + pub fn to_hex(&self) -> String { + hex::encode(&self.0) + } + + pub fn from_hex(hex: &str) -> Result<$name, JsError> { + let bytes = hex::decode(hex) + .map_err(|e| JsError::from_str(&format!("hex decode failed: {}", e)))?; + Self::from_bytes(bytes).map_err(|e| JsError::from_str(&format!("{:?}", e))) + } + } + + // associated consts are not supported in wasm_bindgen + impl $name { + pub const BYTE_COUNT: usize = $byte_count; + } + + // can't expose [T; N] to wasm for new() but it's useful internally so we implement From trait + impl From<[u8; $byte_count]> for $name { + fn from(bytes: [u8; $byte_count]) -> Self { + Self(bytes) + } + } + + impl cbor_event::se::Serialize for $name { + fn serialize<'se, W: std::io::Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_bytes(self.0) + } + } + + impl Deserialize for $name { + fn deserialize( + raw: &mut Deserializer, + ) -> Result { + use std::convert::TryInto; + (|| -> Result { + let bytes = raw.bytes()?; + if bytes.len() != $byte_count { + return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( + $byte_count, + cbor_event::Len::Len(bytes.len() as u64), + "hash length", + )) + .into()); + } + Ok($name(bytes[..$byte_count].try_into().unwrap())) + })() + .map_err(|e| e.annotate(stringify!($name))) + } + } + + impl serde::Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_hex()) + } + } + + impl<'de> serde::de::Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + $name::from_hex(&s).map_err(|_e| { + serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&s), + &"hex bytes for hash", + ) + }) + } + } + + impl JsonSchema for $name { + fn schema_name() -> String { + String::from(stringify!($name)) + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } + fn is_referenceable() -> bool { + String::is_referenceable() + } + } + + impl Display for $name { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.to_hex()) + } + } + }; +} \ No newline at end of file diff --git a/rust/src/protocol_types/crypto/impl_signature_macro.rs b/rust/src/protocol_types/crypto/impl_signature_macro.rs new file mode 100644 index 00000000..5cfb1712 --- /dev/null +++ b/rust/src/protocol_types/crypto/impl_signature_macro.rs @@ -0,0 +1,99 @@ +#[macro_export] +macro_rules! impl_signature { + ($name:ident, $signee_type:ty, $verifier_type:ty) => { + #[wasm_bindgen] + #[derive(Clone, Debug, Hash, Eq, PartialEq)] + pub struct $name(pub (crate) chain_crypto::Signature<$signee_type, $verifier_type>); + + #[wasm_bindgen] + impl $name { + pub fn to_bytes(&self) -> Vec { + self.0.as_ref().to_vec() + } + + pub fn to_bech32(&self) -> String { + use crate::chain_crypto::bech32::Bech32; + self.0.to_bech32_str() + } + + pub fn to_hex(&self) -> String { + hex::encode(&self.0.as_ref()) + } + + pub fn from_bech32(bech32_str: &str) -> Result<$name, JsError> { + use crate::chain_crypto::bech32::Bech32; + chain_crypto::Signature::try_from_bech32_str(&bech32_str) + .map($name) + .map_err(|e| JsError::from_str(&format!("{}", e))) + } + + pub fn from_hex(input: &str) -> Result<$name, JsError> { + use std::str::FromStr; + chain_crypto::Signature::from_str(input) + .map_err(|e| JsError::from_str(&format!("{:?}", e))) + .map($name) + } + } + + from_bytes!($name, bytes, { + chain_crypto::Signature::from_binary(bytes.as_ref()) + .map_err(|e| { + DeserializeError::new(stringify!($name), DeserializeFailure::SignatureError(e)) + }) + .map($name) + }); + + impl cbor_event::se::Serialize for $name { + fn serialize<'se, W: std::io::Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_bytes(self.0.as_ref()) + } + } + + impl Deserialize for $name { + fn deserialize( + raw: &mut Deserializer, + ) -> Result { + Ok(Self(chain_crypto::Signature::from_binary(raw.bytes()?.as_ref())?)) + } + } + + impl serde::Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_hex()) + } + } + + impl<'de> serde::de::Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + $name::from_hex(&s).map_err(|_e| { + serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&s), + &"hex bytes for signature", + ) + }) + } + } + + impl JsonSchema for $name { + fn schema_name() -> String { + String::from(stringify!($name)) + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } + fn is_referenceable() -> bool { + String::is_referenceable() + } + } + }; +} \ No newline at end of file diff --git a/rust/src/protocol_types/crypto/kes_signature.rs b/rust/src/protocol_types/crypto/kes_signature.rs new file mode 100644 index 00000000..b04e6c1f --- /dev/null +++ b/rust/src/protocol_types/crypto/kes_signature.rs @@ -0,0 +1,73 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct KESSignature(pub(crate) Vec); + +#[wasm_bindgen] +impl KESSignature { + pub fn to_bytes(&self) -> Vec { + self.0.clone() + } +} + +// associated consts are not supported in wasm_bindgen +impl KESSignature { + pub const BYTE_COUNT: usize = 448; +} + +from_bytes!(KESSignature, bytes, { + match bytes.len() { + Self::BYTE_COUNT => Ok(KESSignature(bytes)), + other_len => { + let cbor_error = cbor_event::Error::WrongLen( + Self::BYTE_COUNT as u64, + cbor_event::Len::Len(other_len as u64), + "hash length", + ); + Err(DeserializeError::new( + "KESSignature", + DeserializeFailure::CBOR(cbor_error), + )) + } + } +}); + +impl serde::Serialize for KESSignature { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&hex::encode(self.to_bytes())) + } +} + +impl<'de> serde::de::Deserialize<'de> for KESSignature { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + if let Ok(hex_bytes) = hex::decode(s.clone()) { + if let Ok(sig) = KESSignature::from_bytes(hex_bytes) { + return Ok(sig); + } + } + Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&s), + &"hex bytes for KESSignature", + )) + } +} + +impl JsonSchema for KESSignature { + fn schema_name() -> String { + String::from("KESSignature") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } + fn is_referenceable() -> bool { + String::is_referenceable() + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/crypto/legacy_daedalus_private_key.rs b/rust/src/protocol_types/crypto/legacy_daedalus_private_key.rs new file mode 100644 index 00000000..e80f8a18 --- /dev/null +++ b/rust/src/protocol_types/crypto/legacy_daedalus_private_key.rs @@ -0,0 +1,24 @@ +use crate::*; +use crate::chain_crypto as crypto; + +#[wasm_bindgen] +pub struct LegacyDaedalusPrivateKey(pub(crate) crypto::SecretKey); + +#[wasm_bindgen] +impl LegacyDaedalusPrivateKey { + pub fn from_bytes(bytes: &[u8]) -> Result { + crypto::SecretKey::::from_binary(bytes) + .map_err(|e| JsError::from_str(&format!("{}", e))) + .map(LegacyDaedalusPrivateKey) + } + + pub fn as_bytes(&self) -> Vec { + self.0.as_ref().to_vec() + } + + pub fn chaincode(&self) -> Vec { + const ED25519_PRIVATE_KEY_LENGTH: usize = 64; + const XPRV_SIZE: usize = 96; + self.0.as_ref()[ED25519_PRIVATE_KEY_LENGTH..XPRV_SIZE].to_vec() + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/crypto/macro_implemented_hash_types.rs b/rust/src/protocol_types/crypto/macro_implemented_hash_types.rs new file mode 100644 index 00000000..f32c3cf8 --- /dev/null +++ b/rust/src/protocol_types/crypto/macro_implemented_hash_types.rs @@ -0,0 +1,22 @@ +use crate::*; + +impl_hash_type!(Ed25519KeyHash, 28); +impl_hash_type!(ScriptHash, 28); +impl_hash_type!(AnchorDataHash, 32); +impl_hash_type!(TransactionHash, 32); +impl_hash_type!(GenesisDelegateHash, 28); +impl_hash_type!(GenesisHash, 28); +impl_hash_type!(AuxiliaryDataHash, 32); +impl_hash_type!(PoolMetadataHash, 32); +impl_hash_type!(VRFKeyHash, 32); +impl_hash_type!(BlockHash, 32); +impl_hash_type!(DataHash, 32); +impl_hash_type!(ScriptDataHash, 32); +// We might want to make these two vkeys normal classes later but for now it's just arbitrary bytes for us (used in block parsing) +impl_hash_type!(VRFVKey, 32); +impl_hash_type!(KESVKey, 32); +// same for this signature +//impl_hash_type!(KESSignature, 448); +// TODO: when >32 size trait implementations are out of nightly and into stable +// remove the following manual struct definition and use the above macro again if we +// don't have proper crypto implementations for it. \ No newline at end of file diff --git a/rust/src/protocol_types/crypto/macro_implemented_signature_types.rs b/rust/src/protocol_types/crypto/macro_implemented_signature_types.rs new file mode 100644 index 00000000..94bb8986 --- /dev/null +++ b/rust/src/protocol_types/crypto/macro_implemented_signature_types.rs @@ -0,0 +1,3 @@ +use crate::*; + +impl_signature!(Ed25519Signature, Vec, chain_crypto::Ed25519); \ No newline at end of file diff --git a/rust/src/protocol_types/crypto/mod.rs b/rust/src/protocol_types/crypto/mod.rs new file mode 100644 index 00000000..2fda0a36 --- /dev/null +++ b/rust/src/protocol_types/crypto/mod.rs @@ -0,0 +1,40 @@ +mod impl_signature_macro; +mod impl_hash_type_macro; + +mod bip32_private_key; +pub use bip32_private_key::*; + +mod bip32_public_key; +pub use bip32_public_key::*; + +mod private_key; +pub use private_key::*; + +mod public_key; +pub use public_key::*; + +mod macro_implemented_signature_types; +pub use macro_implemented_signature_types::*; + +mod macro_implemented_hash_types; +pub use macro_implemented_hash_types::*; + +mod vkey; +pub use vkey::*; +mod vkeys; +pub use vkeys::*; + +mod public_keys; +pub use public_keys::*; + +mod legacy_daedalus_private_key; +pub use legacy_daedalus_private_key::*; + +mod kes_signature; +pub use kes_signature::*; + +mod nonce; +pub use nonce::*; + +mod vrf_cert; +pub use vrf_cert::*; \ No newline at end of file diff --git a/rust/src/protocol_types/crypto/nonce.rs b/rust/src/protocol_types/crypto/nonce.rs new file mode 100644 index 00000000..6296d69e --- /dev/null +++ b/rust/src/protocol_types/crypto/nonce.rs @@ -0,0 +1,47 @@ +use crate::*; + +// Evolving nonce type (used for Update's crypto) +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct Nonce { + pub(crate) hash: Option<[u8; 32]>, +} + +impl_to_from!(Nonce); + +// can't export consts via wasm_bindgen +impl Nonce { + pub const HASH_LEN: usize = 32; +} + +#[wasm_bindgen] +impl Nonce { + pub fn new_identity() -> Nonce { + Self { hash: None } + } + + pub fn new_from_hash(hash: Vec) -> Result { + use std::convert::TryInto; + match hash[..Self::HASH_LEN].try_into() { + Ok(bytes_correct_size) => Ok(Self { + hash: Some(bytes_correct_size), + }), + Err(e) => Err(JsError::from_str(&e.to_string())), + } + } + + pub fn get_hash(&self) -> Option> { + Some(self.hash?.to_vec()) + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/crypto/private_key.rs b/rust/src/protocol_types/crypto/private_key.rs new file mode 100644 index 00000000..9b011f56 --- /dev/null +++ b/rust/src/protocol_types/crypto/private_key.rs @@ -0,0 +1,107 @@ +use crate::{Ed25519Signature, JsError, PublicKey, wasm_bindgen}; +use crate::impl_mockchain::key; +use rand_os::OsRng; +use crate::chain_crypto::bech32::Bech32; + +#[wasm_bindgen] +pub struct PrivateKey(pub(crate) key::EitherEd25519SecretKey); + +impl From for PrivateKey { + fn from(secret_key: key::EitherEd25519SecretKey) -> PrivateKey { + PrivateKey(secret_key) + } +} + +#[wasm_bindgen] +impl PrivateKey { + pub fn to_public(&self) -> PublicKey { + self.0.to_public().into() + } + + pub fn generate_ed25519() -> Result { + OsRng::new() + .map(crate::chain_crypto::SecretKey::::generate) + .map(key::EitherEd25519SecretKey::Normal) + .map(PrivateKey) + .map_err(|e| JsError::from_str(&format!("{}", e))) + } + + pub fn generate_ed25519extended() -> Result { + OsRng::new() + .map(crate::chain_crypto::SecretKey::::generate) + .map(key::EitherEd25519SecretKey::Extended) + .map(PrivateKey) + .map_err(|e| JsError::from_str(&format!("{}", e))) + } + + /// Get private key from its bech32 representation + /// ```javascript + /// PrivateKey.from_bech32('ed25519_sk1ahfetf02qwwg4dkq7mgp4a25lx5vh9920cr5wnxmpzz9906qvm8qwvlts0'); + /// ``` + /// For an extended 25519 key + /// ```javascript + /// PrivateKey.from_bech32('ed25519e_sk1gqwl4szuwwh6d0yk3nsqcc6xxc3fpvjlevgwvt60df59v8zd8f8prazt8ln3lmz096ux3xvhhvm3ca9wj2yctdh3pnw0szrma07rt5gl748fp'); + /// ``` + pub fn from_bech32(bech32_str: &str) -> Result { + crate::chain_crypto::SecretKey::try_from_bech32_str(&bech32_str) + .map(key::EitherEd25519SecretKey::Extended) + .or_else(|_| { + crate::chain_crypto::SecretKey::try_from_bech32_str(&bech32_str) + .map(key::EitherEd25519SecretKey::Normal) + }) + .map(PrivateKey) + .map_err(|_| JsError::from_str("Invalid secret key")) + } + + pub fn to_bech32(&self) -> String { + match self.0 { + key::EitherEd25519SecretKey::Normal(ref secret) => secret.to_bech32_str(), + key::EitherEd25519SecretKey::Extended(ref secret) => secret.to_bech32_str(), + } + } + + pub fn as_bytes(&self) -> Vec { + match self.0 { + key::EitherEd25519SecretKey::Normal(ref secret) => secret.as_ref().to_vec(), + key::EitherEd25519SecretKey::Extended(ref secret) => secret.as_ref().to_vec(), + } + } + + pub fn from_extended_bytes(bytes: &[u8]) -> Result { + crate::chain_crypto::SecretKey::from_binary(bytes) + .map(key::EitherEd25519SecretKey::Extended) + .map(PrivateKey) + .map_err(|_| JsError::from_str("Invalid extended secret key")) + } + + pub fn from_normal_bytes(bytes: &[u8]) -> Result { + crate::chain_crypto::SecretKey::from_binary(bytes) + .map(key::EitherEd25519SecretKey::Normal) + .map(PrivateKey) + .map_err(|_| JsError::from_str("Invalid normal secret key")) + } + + pub fn sign(&self, message: &[u8]) -> Ed25519Signature { + Ed25519Signature(self.0.sign(&message.to_vec())) + } + + pub fn to_hex(&self) -> String { + hex::encode(self.as_bytes()) + } + + pub fn from_hex(hex_str: &str) -> Result { + let data: Vec = match hex::decode(hex_str) { + Ok(d) => d, + Err(e) => return Err(JsError::from_str(&e.to_string())), + }; + let data_slice: &[u8] = data.as_slice(); + crate::chain_crypto::SecretKey::from_binary(data_slice) + .map(key::EitherEd25519SecretKey::Normal) + .or_else(|_| { + crate::chain_crypto::SecretKey::from_binary(data_slice) + .map(key::EitherEd25519SecretKey::Extended) + }) + .map(PrivateKey) + .map_err(|_| JsError::from_str("Invalid secret key")) + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/crypto/public_key.rs b/rust/src/protocol_types/crypto/public_key.rs new file mode 100644 index 00000000..a29a292c --- /dev/null +++ b/rust/src/protocol_types/crypto/public_key.rs @@ -0,0 +1,98 @@ +use schemars::JsonSchema; +use crate::{Ed25519KeyHash, Ed25519Signature, JsError, wasm_bindgen}; +use crate::chain_crypto::bech32::Bech32; +use crate::crypto::blake2b224; + +/// ED25519 key used as public key +#[wasm_bindgen] +#[derive(Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] +pub struct PublicKey(pub(crate) crate::chain_crypto::PublicKey); + +impl From> for PublicKey { + fn from(key: crate::chain_crypto::PublicKey) -> PublicKey { + PublicKey(key) + } +} + +#[wasm_bindgen] +impl PublicKey { + /// Get public key from its bech32 representation + /// Example: + /// ```javascript + /// const pkey = PublicKey.from_bech32('ed25519_pk1dgaagyh470y66p899txcl3r0jaeaxu6yd7z2dxyk55qcycdml8gszkxze2'); + /// ``` + pub fn from_bech32(bech32_str: &str) -> Result { + crate::chain_crypto::PublicKey::try_from_bech32_str(&bech32_str) + .map(PublicKey) + .map_err(|_| JsError::from_str("Malformed public key")) + } + + pub fn to_bech32(&self) -> String { + self.0.to_bech32_str() + } + + pub fn as_bytes(&self) -> Vec { + self.0.as_ref().to_vec() + } + + pub fn from_bytes(bytes: &[u8]) -> Result { + crate::chain_crypto::PublicKey::from_binary(bytes) + .map_err(|e| JsError::from_str(&format!("{}", e))) + .map(PublicKey) + } + + pub fn verify(&self, data: &[u8], signature: &Ed25519Signature) -> bool { + signature.0.verify_slice(&self.0, data) == crate::chain_crypto::Verification::Success + } + + pub fn hash(&self) -> Ed25519KeyHash { + Ed25519KeyHash::from(blake2b224(self.as_bytes().as_ref())) + } + + pub fn to_hex(&self) -> String { + hex::encode(self.as_bytes()) + } + + pub fn from_hex(hex_str: &str) -> Result { + match hex::decode(hex_str) { + Ok(data) => Ok(Self::from_bytes(data.as_ref())?), + Err(e) => Err(JsError::from_str(&e.to_string())), + } + } +} + +impl serde::Serialize for PublicKey { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_bech32()) + } +} + +impl<'de> serde::de::Deserialize<'de> for PublicKey { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + PublicKey::from_bech32(&s).map_err(|_e| { + serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&s), + &"bech32 public key string", + ) + }) + } +} + +impl JsonSchema for PublicKey { + fn schema_name() -> String { + String::from("PublicKey") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } + fn is_referenceable() -> bool { + String::is_referenceable() + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/crypto/public_keys.rs b/rust/src/protocol_types/crypto/public_keys.rs new file mode 100644 index 00000000..5271d69b --- /dev/null +++ b/rust/src/protocol_types/crypto/public_keys.rs @@ -0,0 +1,24 @@ +use crate::*; + +#[wasm_bindgen] +pub struct PublicKeys(Vec); + +#[wasm_bindgen] +impl PublicKeys { + #[wasm_bindgen(constructor)] + pub fn new() -> PublicKeys { + PublicKeys(vec![]) + } + + pub fn size(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> PublicKey { + self.0[index].clone() + } + + pub fn add(&mut self, key: &PublicKey) { + self.0.push(key.clone()); + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/crypto/vkey.rs b/rust/src/protocol_types/crypto/vkey.rs new file mode 100644 index 00000000..99f6dc01 --- /dev/null +++ b/rust/src/protocol_types/crypto/vkey.rs @@ -0,0 +1,18 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Debug, Ord, PartialOrd, Eq, Hash, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct Vkey(pub(crate) PublicKey); + +impl_to_from!(Vkey); + +#[wasm_bindgen] +impl Vkey { + pub fn new(pk: &PublicKey) -> Self { + Self(pk.clone()) + } + + pub fn public_key(&self) -> PublicKey { + self.0.clone() + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/crypto/vkeys.rs b/rust/src/protocol_types/crypto/vkeys.rs new file mode 100644 index 00000000..7de34420 --- /dev/null +++ b/rust/src/protocol_types/crypto/vkeys.rs @@ -0,0 +1,24 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone)] +pub struct Vkeys(pub(crate) Vec); + +#[wasm_bindgen] +impl Vkeys { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> Vkey { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &Vkey) { + self.0.push(elem.clone()); + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/crypto/vrf_cert.rs b/rust/src/protocol_types/crypto/vrf_cert.rs new file mode 100644 index 00000000..cb2918b1 --- /dev/null +++ b/rust/src/protocol_types/crypto/vrf_cert.rs @@ -0,0 +1,41 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub struct VRFCert { + pub(crate) output: Vec, + pub(crate) proof: Vec, +} + +impl VRFCert { + pub const PROOF_LEN: usize = 80; +} + +impl_to_from!(VRFCert); + +#[wasm_bindgen] +impl VRFCert { + pub fn output(&self) -> Vec { + self.output.clone() + } + + pub fn proof(&self) -> Vec { + self.proof.clone() + } + + pub fn new(output: Vec, proof: Vec) -> Result { + if proof.len() != Self::PROOF_LEN { + return Err(JsError::from_str(&format!( + "proof len must be {} - found {}", + Self::PROOF_LEN, + proof.len() + ))); + } + Ok(Self { + output: output, + proof: proof, + }) + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/ed25519_key_hashes.rs b/rust/src/protocol_types/ed25519_key_hashes.rs new file mode 100644 index 00000000..c26083e3 --- /dev/null +++ b/rust/src/protocol_types/ed25519_key_hashes.rs @@ -0,0 +1,134 @@ +pub use crate::*; + +pub type RequiredSigners = Ed25519KeyHashes; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Eq, + Ord, + Hash, + PartialEq, + PartialOrd, +)] +pub struct Ed25519KeyHashes { + keyhashes: Vec, + dedup: BTreeSet, +} + +impl_to_from!(Ed25519KeyHashes); + +#[wasm_bindgen] +impl Ed25519KeyHashes { + pub fn new() -> Self { + Self { + keyhashes: Vec::new(), + dedup: BTreeSet::new(), + } + } + + pub fn len(&self) -> usize { + self.keyhashes.len() + } + + pub fn get(&self, index: usize) -> Ed25519KeyHash { + self.keyhashes[index].clone() + } + + /// Add a new `Ed25519KeyHash` to the set. + /// Returns `true` if the element was not already present in the set. + pub fn add(&mut self, elem: &Ed25519KeyHash) -> bool { + if self.dedup.insert(elem.clone()) { + self.keyhashes.push(elem.clone()); + true + } else { + false + } + } + + pub fn contains(&self, elem: &Ed25519KeyHash) -> bool { + self.dedup.contains(elem) + } + + pub fn to_option(&self) -> Option { + if self.keyhashes.len() > 0 { + Some(self.clone()) + } else { + None + } + } + + pub(crate) fn to_vec(&self) -> &Vec { + &self.keyhashes + } + + pub(crate) fn add_move(&mut self, elem: Ed25519KeyHash) { + if self.dedup.insert(elem.clone()) { + self.keyhashes.push(elem); + } + } + + pub(crate) fn extend(&mut self, other: &Ed25519KeyHashes) { + for keyhash in &other.keyhashes { + self.add(keyhash); + } + } + + pub(crate) fn extend_move(&mut self, other: Ed25519KeyHashes) { + for keyhash in other.keyhashes { + self.add_move(keyhash); + } + } + + pub(crate) fn from_vec(keyhash_vec: Vec) -> Self { + let mut dedup = BTreeSet::new(); + let mut keyhashes = Vec::new(); + for keyhash in keyhash_vec { + if dedup.insert(keyhash.clone()) { + keyhashes.push(keyhash); + } + } + + Self { keyhashes, dedup } + } +} + +impl NoneOrEmpty for Ed25519KeyHashes { + fn is_none_or_empty(&self) -> bool { + self.keyhashes.is_empty() + } +} + +impl serde::Serialize for Ed25519KeyHashes { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.keyhashes.serialize(serializer) + } +} + +impl<'de> serde::de::Deserialize<'de> for Ed25519KeyHashes { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let vec = as serde::de::Deserialize>::deserialize( + deserializer, + )?; + Ok(Self::from_vec(vec)) + } +} + +impl JsonSchema for Ed25519KeyHashes { + fn schema_name() -> String { + String::from("Ed25519KeyHashes") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + Vec::::json_schema(gen) + } + fn is_referenceable() -> bool { + Vec::::is_referenceable() + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/fixed_tx.rs b/rust/src/protocol_types/fixed_tx.rs index 9e258636..8733ed91 100644 --- a/rust/src/protocol_types/fixed_tx.rs +++ b/rust/src/protocol_types/fixed_tx.rs @@ -1,24 +1,22 @@ use crate::error::JsError; use crate::*; -use std::io::{Seek, SeekFrom}; #[wasm_bindgen] pub struct FixedTransaction { - body: TransactionBody, - body_bytes: Vec, + pub(crate) body: TransactionBody, + pub(crate) body_bytes: Vec, - witness_set: TransactionWitnessSet, - witness_bytes: Vec, + pub(crate) witness_set: TransactionWitnessSet, + pub(crate) witness_bytes: Vec, - is_valid: bool, + pub(crate) is_valid: bool, - auxiliary_data: Option, - auxiliary_bytes: Option>, + pub(crate) auxiliary_data: Option, + pub(crate) auxiliary_bytes: Option>, } to_from_bytes!(FixedTransaction); - #[wasm_bindgen] impl FixedTransaction { pub fn new( @@ -57,7 +55,7 @@ impl FixedTransaction { witness_bytes: raw_witness_set.to_vec(), is_valid, auxiliary_data, - auxiliary_bytes: Some(raw_auxiliary_data.to_vec()) + auxiliary_bytes: Some(raw_auxiliary_data.to_vec()), }) } @@ -114,133 +112,3 @@ impl FixedTransaction { self.auxiliary_bytes.clone() } } - -impl cbor_event::se::Serialize for FixedTransaction { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(4))?; - serializer.write_raw_bytes(&self.body_bytes)?; - serializer.write_raw_bytes(&self.witness_bytes)?; - serializer.write_special(CBORSpecial::Bool(self.is_valid))?; - match &self.auxiliary_bytes { - Some(auxiliary_bytes) => serializer.write_raw_bytes(auxiliary_bytes)?, - None => serializer.write_special(CBORSpecial::Null)?, - }; - Ok(serializer) - } -} - -impl Deserialize for FixedTransaction { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("Transaction")) - } -} - -impl DeserializeEmbeddedGroup for FixedTransaction { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - let (body, body_bytes) = - deserilized_with_orig_bytes(raw, |raw| TransactionBody::deserialize(raw)) - .map_err(|e| e.annotate("body"))?; - let (witness_set, witness_bytes) = - deserilized_with_orig_bytes(raw, |raw| TransactionWitnessSet::deserialize(raw)) - .map_err(|e| e.annotate("witness_set"))?; - let mut checked_auxiliary_data = false; - let mut auxiliary_data = None; - let mut auxiliary_bytes = None; - let is_valid = (|| -> Result<_, DeserializeError> { - match raw.cbor_type()? == CBORType::Special { - true => { - // if it's special it can be either a bool or null. if it's null, then it's empty auxiliary data, otherwise not a valid encoding - let special = raw.special()?; - if let CBORSpecial::Bool(b) = special { - return Ok(b); - } else if special == CBORSpecial::Null { - checked_auxiliary_data = true; - return Ok(true); - } else { - return Err(DeserializeFailure::ExpectedBool.into()); - } - } - false => { - let (auxiliary_data_deser, auxiliary_bytes_deser) = - deserilized_with_orig_bytes(raw, |raw| AuxiliaryData::deserialize(raw)) - .map_err(|e| e.annotate("auxiliary_data"))?; - auxiliary_data = Some(auxiliary_data_deser); - auxiliary_bytes = Some(auxiliary_bytes_deser); - // if no special symbol was detected, it must have auxiliary data - checked_auxiliary_data = true; - return Ok(true); - } - } - })() - .map_err(|e| e.annotate("is_valid"))?; - if !checked_auxiliary_data { - // this branch is reached, if the 3rd argument was a bool. then it simply follows the rules for checking auxiliary data - (auxiliary_data, auxiliary_bytes) = (|| -> Result<_, DeserializeError> { - Ok(match raw.cbor_type()? != CBORType::Special { - true => { - let (auxiliary_data_deser, auxiliary_bytes_deser) = - deserilized_with_orig_bytes(raw, |raw| AuxiliaryData::deserialize(raw))?; - (Some(auxiliary_data_deser), Some(auxiliary_bytes_deser)) - - }, - false => { - if raw.special()? != CBORSpecial::Null { - return Err(DeserializeFailure::ExpectedNull.into()); - } - (None, None) - } - }) - })() - .map_err(|e| e.annotate("auxiliary_data"))?; - } - Ok(FixedTransaction { - body, - body_bytes, - witness_set, - witness_bytes, - is_valid, - auxiliary_data, - auxiliary_bytes - }) - } -} - -fn deserilized_with_orig_bytes( - raw: &mut Deserializer, - deserilizator: fn(&mut Deserializer) -> Result, -) -> Result<(T, Vec), DeserializeError> { - let before = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); - let value = deserilizator(raw)?; - let after = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); - let bytes_read = (after - before) as usize; - raw.as_mut_ref().seek(SeekFrom::Start(before)).unwrap(); - let original_bytes = raw.as_mut_ref().fill_buf().unwrap()[..bytes_read].to_vec(); - raw.as_mut_ref().seek(SeekFrom::Start(after)).unwrap(); - Ok((value, original_bytes)) -} diff --git a/rust/src/protocol_types/governance/anchor.rs b/rust/src/protocol_types/governance/anchor.rs new file mode 100644 index 00000000..16a3c4b2 --- /dev/null +++ b/rust/src/protocol_types/governance/anchor.rs @@ -0,0 +1,39 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct Anchor { + pub(crate) anchor_url: URL, + pub(crate) anchor_data_hash: AnchorDataHash, +} + +impl_to_from!(Anchor); + +#[wasm_bindgen] +impl Anchor { + pub fn url(&self) -> URL { + self.anchor_url.clone() + } + + pub fn anchor_data_hash(&self) -> AnchorDataHash { + self.anchor_data_hash.clone() + } + + pub fn new(anchor_url: &URL, anchor_data_hash: &AnchorDataHash) -> Self { + Self { + anchor_url: anchor_url.clone(), + anchor_data_hash: anchor_data_hash.clone(), + } + } +} diff --git a/rust/src/protocol_types/governance/drep.rs b/rust/src/protocol_types/governance/drep.rs new file mode 100644 index 00000000..7d96e9eb --- /dev/null +++ b/rust/src/protocol_types/governance/drep.rs @@ -0,0 +1,135 @@ +use crate::*; +use bech32::ToBase32; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub(crate) enum DRepEnum { + KeyHash(Ed25519KeyHash), + ScriptHash(ScriptHash), + AlwaysAbstain, + AlwaysNoConfidence, +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] +pub enum DRepKind { + KeyHash, + ScriptHash, + AlwaysAbstain, + AlwaysNoConfidence, +} + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct DRep(pub(crate) DRepEnum); + +impl_to_from!(DRep); + +#[wasm_bindgen] +impl DRep { + pub fn new_key_hash(key_hash: &Ed25519KeyHash) -> Self { + Self(DRepEnum::KeyHash(key_hash.clone())) + } + + pub fn new_script_hash(script_hash: &ScriptHash) -> Self { + Self(DRepEnum::ScriptHash(script_hash.clone())) + } + + pub fn new_always_abstain() -> Self { + Self(DRepEnum::AlwaysAbstain) + } + + pub fn new_always_no_confidence() -> Self { + Self(DRepEnum::AlwaysNoConfidence) + } + + pub fn new_from_credential(cred: &Credential) -> Self { + let drep = match &cred.0 { + CredType::Key(key_hash) => DRepEnum::KeyHash(key_hash.clone()), + CredType::Script(script_hash) => DRepEnum::ScriptHash(script_hash.clone()), + }; + Self(drep) + } + + pub fn kind(&self) -> DRepKind { + match &self.0 { + DRepEnum::KeyHash(_) => DRepKind::KeyHash, + DRepEnum::ScriptHash(_) => DRepKind::ScriptHash, + DRepEnum::AlwaysAbstain => DRepKind::AlwaysAbstain, + DRepEnum::AlwaysNoConfidence => DRepKind::AlwaysNoConfidence, + } + } + + pub fn to_key_hash(&self) -> Option { + match &self.0 { + DRepEnum::KeyHash(keyhash) => Some(keyhash.clone()), + _ => None, + } + } + + pub fn to_script_hash(&self) -> Option { + match &self.0 { + DRepEnum::ScriptHash(scripthash) => Some(scripthash.clone()), + _ => None, + } + } + + pub fn to_bech32(&self) -> Result { + let (hrp, data) = match &self.0 { + DRepEnum::KeyHash(keyhash) => Ok(("drep", keyhash.to_bytes())), + DRepEnum::ScriptHash(scripthash) => Ok(("drep_script", scripthash.to_bytes())), + DRepEnum::AlwaysAbstain => { + Err(JsError::from_str("Cannot convert AlwaysAbstain to bech32")) + } + DRepEnum::AlwaysNoConfidence => Err(JsError::from_str( + "Cannot convert AlwaysNoConfidence to bech32", + )), + }?; + bech32::encode(&hrp, data.to_base32()).map_err(|e| JsError::from_str(&format! {"{:?}", e})) + } + + pub fn from_bech32(bech32_str: &str) -> Result { + let (hrp, u5data) = + bech32::decode(bech32_str).map_err(|e| JsError::from_str(&e.to_string()))?; + let data: Vec = bech32::FromBase32::from_base32(&u5data) + .map_err(|_| JsError::from_str("Malformed DRep"))?; + let kind = match hrp.as_str() { + "drep" => DRepKind::KeyHash, + "drep_script" => DRepKind::ScriptHash, + _ => return Err(JsError::from_str("Malformed DRep")), + }; + let drep = match kind { + DRepKind::KeyHash => DRepEnum::KeyHash( + Ed25519KeyHash::from_bytes(data) + .map_err(|_| JsError::from_str("Malformed DRep"))?, + ), + DRepKind::ScriptHash => DRepEnum::ScriptHash( + ScriptHash::from_bytes(data).map_err(|_| JsError::from_str("Malformed DRep"))?, + ), + DRepKind::AlwaysAbstain => DRepEnum::AlwaysAbstain, + DRepKind::AlwaysNoConfidence => DRepEnum::AlwaysNoConfidence, + }; + Ok(DRep(drep)) + } +} diff --git a/rust/src/protocol_types/governance/governance_action_id.rs b/rust/src/protocol_types/governance/governance_action_id.rs new file mode 100644 index 00000000..53269038 --- /dev/null +++ b/rust/src/protocol_types/governance/governance_action_id.rs @@ -0,0 +1,39 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Eq, + Ord, + PartialEq, + PartialOrd, + Hash, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct GovernanceActionId { + pub(crate) transaction_id: TransactionHash, + pub(crate) index: GovernanceActionIndex, +} + +impl_to_from!(GovernanceActionId); + +#[wasm_bindgen] +impl GovernanceActionId { + pub fn transaction_id(&self) -> TransactionHash { + self.transaction_id.clone() + } + + pub fn index(&self) -> GovernanceActionIndex { + self.index.clone() + } + + pub fn new(transaction_id: &TransactionHash, index: GovernanceActionIndex) -> Self { + Self { + transaction_id: transaction_id.clone(), + index: index, + } + } +} diff --git a/rust/src/protocol_types/governance/governance_action_ids.rs b/rust/src/protocol_types/governance/governance_action_ids.rs new file mode 100644 index 00000000..8271994b --- /dev/null +++ b/rust/src/protocol_types/governance/governance_action_ids.rs @@ -0,0 +1,37 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Eq, + Ord, + PartialEq, + PartialOrd, + Hash, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct GovernanceActionIds(pub(crate) Vec); + +to_from_json!(GovernanceActionIds); + +#[wasm_bindgen] +impl GovernanceActionIds { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn add(&mut self, governance_action_id: &GovernanceActionId) { + self.0.push(governance_action_id.clone()); + } + + pub fn get(&self, index: usize) -> Option { + self.0.get(index).cloned() + } + + pub fn len(&self) -> usize { + self.0.len() + } +} diff --git a/rust/src/protocol_types/governance/mod.rs b/rust/src/protocol_types/governance/mod.rs new file mode 100644 index 00000000..b0ffbabf --- /dev/null +++ b/rust/src/protocol_types/governance/mod.rs @@ -0,0 +1,26 @@ +mod drep; +pub use drep::*; + +mod anchor; +pub use anchor::*; + +mod voter; +pub use voter::*; + +mod voters; +pub use voters::*; + +mod voting_procedure; +pub use voting_procedure::*; + +mod voting_procedures; +pub use voting_procedures::*; + +mod governance_action_id; +pub use governance_action_id::*; + +mod governance_action_ids; +pub use governance_action_ids::*; + +mod proposals; +pub use proposals::*; diff --git a/rust/src/protocol_types/governance/proposals/committee.rs b/rust/src/protocol_types/governance/proposals/committee.rs new file mode 100644 index 00000000..69bd8774 --- /dev/null +++ b/rust/src/protocol_types/governance/proposals/committee.rs @@ -0,0 +1,126 @@ +use crate::*; +use schemars::gen::SchemaGenerator; +use schemars::schema::Schema; +use std::collections::BTreeMap; + +#[derive( + Clone, + Debug, + Eq, + Ord, + PartialEq, + PartialOrd, + Hash, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +struct CommitteeMember { + stake_credential: Credential, + term_limit: Epoch, +} + +#[derive( + Clone, + Debug, + Eq, + Ord, + PartialEq, + PartialOrd, + Hash, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +struct CommitteeJsonStruct { + members: Vec, + quorum_threshold: UnitInterval, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] +#[wasm_bindgen] +pub struct Committee { + pub(crate) members: BTreeMap, + pub(crate) quorum_threshold: UnitInterval, +} + +impl_to_from!(Committee); + +#[wasm_bindgen] +impl Committee { + pub fn new(quorum_threshold: &UnitInterval) -> Self { + Self { + members: BTreeMap::new(), + quorum_threshold: quorum_threshold.clone(), + } + } + + pub fn members_keys(&self) -> Credentials { + Credentials::from_iter(self.members.keys().cloned()) + } + + pub fn quorum_threshold(&self) -> UnitInterval { + self.quorum_threshold.clone() + } + + pub fn add_member(&mut self, committee_cold_credential: &Credential, epoch: Epoch) { + self.members + .insert(committee_cold_credential.clone(), epoch); + } + + pub fn get_member_epoch(&self, committee_cold_credential: &Credential) -> Option { + self.members.get(committee_cold_credential).cloned() + } +} + +impl JsonSchema for Committee { + fn is_referenceable() -> bool { + CommitteeJsonStruct::is_referenceable() + } + + fn schema_name() -> String { + "Committee".to_string() + } + + fn json_schema(gen: &mut SchemaGenerator) -> Schema { + CommitteeJsonStruct::json_schema(gen) + } +} + +impl serde::ser::Serialize for Committee { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let committee = CommitteeJsonStruct { + members: self + .members + .iter() + .map(|(k, v)| CommitteeMember { + stake_credential: k.clone(), + term_limit: v.clone(), + }) + .collect(), + quorum_threshold: self.quorum_threshold.clone(), + }; + + committee.serialize(serializer) + } +} + +impl<'de> serde::de::Deserialize<'de> for Committee { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let committee_json: CommitteeJsonStruct = + serde::de::Deserialize::deserialize(deserializer)?; + let mut committee = Committee::new(&committee_json.quorum_threshold); + let mut members = BTreeMap::new(); + for member in committee_json.members { + members.insert(member.stake_credential, member.term_limit); + } + committee.members = members; + Ok(committee) + } +} diff --git a/rust/src/protocol_types/governance/proposals/constitution.rs b/rust/src/protocol_types/governance/proposals/constitution.rs new file mode 100644 index 00000000..b273b4be --- /dev/null +++ b/rust/src/protocol_types/governance/proposals/constitution.rs @@ -0,0 +1,46 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Eq, + Ord, + PartialEq, + PartialOrd, + Hash, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct Constitution { + pub(crate) anchor: Anchor, + pub(crate) script_hash: Option, +} + +impl_to_from!(Constitution); + +#[wasm_bindgen] +impl Constitution { + pub fn anchor(&self) -> Anchor { + self.anchor.clone() + } + + pub fn script_hash(&self) -> Option { + self.script_hash.clone() + } + + pub fn new(anchor: &Anchor) -> Self { + Self { + anchor: anchor.clone(), + script_hash: None, + } + } + + pub fn new_with_script_hash(anchor: &Anchor, script_hash: &ScriptHash) -> Self { + Self { + anchor: anchor.clone(), + script_hash: Some(script_hash.clone()), + } + } +} diff --git a/rust/src/protocol_types/governance/proposals/governance_action.rs b/rust/src/protocol_types/governance/proposals/governance_action.rs new file mode 100644 index 00000000..fe3745eb --- /dev/null +++ b/rust/src/protocol_types/governance/proposals/governance_action.rs @@ -0,0 +1,183 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub(crate) enum GovernanceActionEnum { + ParameterChangeAction(ParameterChangeAction), + HardForkInitiationAction(HardForkInitiationAction), + TreasuryWithdrawalsAction(TreasuryWithdrawalsAction), + NoConfidenceAction(NoConfidenceAction), + UpdateCommitteeAction(UpdateCommitteeAction), + NewConstitutionAction(NewConstitutionAction), + InfoAction(InfoAction), +} + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub enum GovernanceActionKind { + ParameterChangeAction = 0, + HardForkInitiationAction = 1, + TreasuryWithdrawalsAction = 2, + NoConfidenceAction = 3, + UpdateCommitteeAction = 4, + NewConstitutionAction = 5, + InfoAction = 6, +} + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct GovernanceAction(pub(crate) GovernanceActionEnum); + +impl_to_from!(GovernanceAction); + +#[wasm_bindgen] +impl GovernanceAction { + pub fn new_parameter_change_action( + parameter_change_action: &ParameterChangeAction, + ) -> Self { + Self(GovernanceActionEnum::ParameterChangeAction( + parameter_change_action.clone(), + )) + } + + pub fn new_hard_fork_initiation_action( + hard_fork_initiation_action: &HardForkInitiationAction, + ) -> Self { + Self(GovernanceActionEnum::HardForkInitiationAction( + hard_fork_initiation_action.clone(), + )) + } + + pub fn new_treasury_withdrawals_action( + treasury_withdrawals_action: &TreasuryWithdrawalsAction, + ) -> Self { + Self(GovernanceActionEnum::TreasuryWithdrawalsAction( + treasury_withdrawals_action.clone(), + )) + } + + pub fn new_no_confidence_action(no_confidence_action: &NoConfidenceAction) -> Self { + Self(GovernanceActionEnum::NoConfidenceAction( + no_confidence_action.clone(), + )) + } + + pub fn new_new_committee_action(new_committee_action: &UpdateCommitteeAction) -> Self { + Self(GovernanceActionEnum::UpdateCommitteeAction( + new_committee_action.clone(), + )) + } + + pub fn new_new_constitution_action( + new_constitution_action: &NewConstitutionAction, + ) -> Self { + Self(GovernanceActionEnum::NewConstitutionAction( + new_constitution_action.clone(), + )) + } + + pub fn new_info_action(info_action: &InfoAction) -> Self { + Self(GovernanceActionEnum::InfoAction(info_action.clone())) + } + + pub fn kind(&self) -> GovernanceActionKind { + match &self.0 { + GovernanceActionEnum::ParameterChangeAction(_) => { + GovernanceActionKind::ParameterChangeAction + } + GovernanceActionEnum::HardForkInitiationAction(_) => { + GovernanceActionKind::HardForkInitiationAction + } + GovernanceActionEnum::TreasuryWithdrawalsAction(_) => { + GovernanceActionKind::TreasuryWithdrawalsAction + } + GovernanceActionEnum::NoConfidenceAction(_) => GovernanceActionKind::NoConfidenceAction, + GovernanceActionEnum::UpdateCommitteeAction(_) => GovernanceActionKind::UpdateCommitteeAction, + GovernanceActionEnum::NewConstitutionAction(_) => { + GovernanceActionKind::NewConstitutionAction + } + GovernanceActionEnum::InfoAction(_) => GovernanceActionKind::InfoAction, + } + } + + pub fn as_parameter_change_action(&self) -> Option { + match &self.0 { + GovernanceActionEnum::ParameterChangeAction(p) => Some(p.clone()), + _ => None, + } + } + + pub fn as_hard_fork_initiation_action(&self) -> Option { + match &self.0 { + GovernanceActionEnum::HardForkInitiationAction(p) => Some(p.clone()), + _ => None, + } + } + + pub fn as_treasury_withdrawals_action(&self) -> Option { + match &self.0 { + GovernanceActionEnum::TreasuryWithdrawalsAction(p) => Some(p.clone()), + _ => None, + } + } + + pub fn as_no_confidence_action(&self) -> Option { + match &self.0 { + GovernanceActionEnum::NoConfidenceAction(p) => Some(p.clone()), + _ => None, + } + } + + pub fn as_new_committee_action(&self) -> Option { + match &self.0 { + GovernanceActionEnum::UpdateCommitteeAction(p) => Some(p.clone()), + _ => None, + } + } + + pub fn as_new_constitution_action(&self) -> Option { + match &self.0 { + GovernanceActionEnum::NewConstitutionAction(p) => Some(p.clone()), + _ => None, + } + } + + pub fn as_info_action (&self) -> Option { + match &self.0 { + GovernanceActionEnum::InfoAction(p) => Some(p.clone()), + _ => None, + } + } +} diff --git a/rust/src/protocol_types/governance/proposals/hard_fork_initiation_action.rs b/rust/src/protocol_types/governance/proposals/hard_fork_initiation_action.rs new file mode 100644 index 00000000..8ebba4ee --- /dev/null +++ b/rust/src/protocol_types/governance/proposals/hard_fork_initiation_action.rs @@ -0,0 +1,49 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct HardForkInitiationAction { + pub(crate) gov_action_id: Option, + pub(crate) protocol_version: ProtocolVersion, +} + +impl_to_from!(HardForkInitiationAction); + +#[wasm_bindgen] +impl HardForkInitiationAction { + pub fn gov_action_id(&self) -> Option { + self.gov_action_id.clone() + } + + pub fn protocol_version(&self) -> ProtocolVersion { + self.protocol_version.clone() + } + + pub fn new(protocol_version: &ProtocolVersion) -> Self { + Self { + gov_action_id: None, + protocol_version: protocol_version.clone(), + } + } + + pub fn new_with_action_id( + gov_action_id: &GovernanceActionId, + protocol_version: &ProtocolVersion, + ) -> Self { + Self { + gov_action_id: Some(gov_action_id.clone()), + protocol_version: protocol_version.clone(), + } + } +} diff --git a/rust/src/protocol_types/governance/proposals/info_action.rs b/rust/src/protocol_types/governance/proposals/info_action.rs new file mode 100644 index 00000000..39c43d80 --- /dev/null +++ b/rust/src/protocol_types/governance/proposals/info_action.rs @@ -0,0 +1,23 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct InfoAction(); + +#[wasm_bindgen] +impl InfoAction { + pub fn new() -> Self { + Self() + } +} diff --git a/rust/src/protocol_types/governance/proposals/mod.rs b/rust/src/protocol_types/governance/proposals/mod.rs new file mode 100644 index 00000000..37b3c359 --- /dev/null +++ b/rust/src/protocol_types/governance/proposals/mod.rs @@ -0,0 +1,38 @@ +mod parameter_change_action; +pub use parameter_change_action::*; + +mod hard_fork_initiation_action; +pub use hard_fork_initiation_action::*; + +mod treasury_withdrawals_action; +pub use treasury_withdrawals_action::*; + +mod treasury_withdrawals; +pub use treasury_withdrawals::*; + +mod no_confidence_action; +pub use no_confidence_action::*; + +mod committee; +pub use committee::*; + +mod update_committee_action; +pub use update_committee_action::*; + +mod constitution; +pub use constitution::*; + +mod new_constitution_action; +pub use new_constitution_action::*; + +mod info_action; +pub use info_action::*; + +mod governance_action; +pub use governance_action::*; + +mod voting_proposal; +pub use voting_proposal::*; + +mod voting_proposals; +pub use voting_proposals::*; diff --git a/rust/src/protocol_types/governance/proposals/new_constitution_action.rs b/rust/src/protocol_types/governance/proposals/new_constitution_action.rs new file mode 100644 index 00000000..0776f40b --- /dev/null +++ b/rust/src/protocol_types/governance/proposals/new_constitution_action.rs @@ -0,0 +1,53 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct NewConstitutionAction { + pub(crate) gov_action_id: Option, + pub(crate) constitution: Constitution, +} + +impl_to_from!(NewConstitutionAction); + +#[wasm_bindgen] +impl NewConstitutionAction { + pub fn gov_action_id(&self) -> Option { + self.gov_action_id.clone() + } + + pub fn constitution(&self) -> Constitution { + self.constitution.clone() + } + + pub fn new(constitution: &Constitution) -> Self { + Self { + gov_action_id: None, + constitution: constitution.clone(), + } + } + + pub fn new_with_action_id( + gov_action_id: &GovernanceActionId, + constitution: &Constitution, + ) -> Self { + Self { + gov_action_id: Some(gov_action_id.clone()), + constitution: constitution.clone(), + } + } + + pub fn has_script_hash(&self) -> bool { + self.constitution.script_hash.is_some() + } +} diff --git a/rust/src/protocol_types/governance/proposals/no_confidence_action.rs b/rust/src/protocol_types/governance/proposals/no_confidence_action.rs new file mode 100644 index 00000000..b7c552a0 --- /dev/null +++ b/rust/src/protocol_types/governance/proposals/no_confidence_action.rs @@ -0,0 +1,39 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct NoConfidenceAction { + pub(crate) gov_action_id: Option, +} + +impl_to_from!(NoConfidenceAction); + +#[wasm_bindgen] +impl NoConfidenceAction { + pub fn gov_action_id(&self) -> Option { + self.gov_action_id.clone() + } + + pub fn new() -> Self { + Self { + gov_action_id: None, + } + } + + pub fn new_with_action_id(gov_action_id: &GovernanceActionId) -> Self { + Self { + gov_action_id: Some(gov_action_id.clone()), + } + } +} diff --git a/rust/src/protocol_types/governance/proposals/parameter_change_action.rs b/rust/src/protocol_types/governance/proposals/parameter_change_action.rs new file mode 100644 index 00000000..08c96289 --- /dev/null +++ b/rust/src/protocol_types/governance/proposals/parameter_change_action.rs @@ -0,0 +1,83 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct ParameterChangeAction { + pub(crate) gov_action_id: Option, + pub(crate) protocol_param_updates: ProtocolParamUpdate, + pub(crate) policy_hash: Option, +} + +impl_to_from!(ParameterChangeAction); + +#[wasm_bindgen] +impl ParameterChangeAction { + pub fn gov_action_id(&self) -> Option { + self.gov_action_id.clone() + } + + pub fn protocol_param_updates(&self) -> ProtocolParamUpdate { + self.protocol_param_updates.clone() + } + + pub fn policy_hash(&self) -> Option { + self.policy_hash.clone() + } + + pub fn new(protocol_param_updates: &ProtocolParamUpdate) -> Self { + Self { + gov_action_id: None, + protocol_param_updates: protocol_param_updates.clone(), + policy_hash: None, + } + } + + pub fn new_with_action_id( + gov_action_id: &GovernanceActionId, + protocol_param_updates: &ProtocolParamUpdate, + ) -> Self { + Self { + gov_action_id: Some(gov_action_id.clone()), + protocol_param_updates: protocol_param_updates.clone(), + policy_hash: None, + } + } + + pub fn new_with_policy_hash( + protocol_param_updates: &ProtocolParamUpdate, + policy_hash: &ScriptHash, + ) -> Self { + Self { + gov_action_id: None, + protocol_param_updates: protocol_param_updates.clone(), + policy_hash: Some(policy_hash.clone()), + } + } + + pub fn new_with_policy_hash_and_action_id( + gov_action_id: &GovernanceActionId, + protocol_param_updates: &ProtocolParamUpdate, + policy_hash: &ScriptHash, + ) -> Self { + Self { + gov_action_id: Some(gov_action_id.clone()), + protocol_param_updates: protocol_param_updates.clone(), + policy_hash: Some(policy_hash.clone()), + } + } + + pub(crate) fn has_script_hash(&self) -> bool { + self.policy_hash.is_some() + } +} diff --git a/rust/src/protocol_types/governance/proposals/treasury_withdrawals.rs b/rust/src/protocol_types/governance/proposals/treasury_withdrawals.rs new file mode 100644 index 00000000..04322057 --- /dev/null +++ b/rust/src/protocol_types/governance/proposals/treasury_withdrawals.rs @@ -0,0 +1,42 @@ +use crate::*; +use std::collections::BTreeMap; + +#[derive( + Clone, + Debug, + Eq, + Ord, + PartialEq, + PartialOrd, + Hash, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct TreasuryWithdrawals(pub(crate) BTreeMap); + +to_from_json!(TreasuryWithdrawals); + +#[wasm_bindgen] +impl TreasuryWithdrawals { + pub fn new() -> Self { + Self(BTreeMap::new()) + } + + pub fn get(&self, key: &RewardAddress) -> Option { + self.0.get(key).cloned() + } + + pub fn insert(&mut self, key: &RewardAddress, value: &Coin) { + self.0.insert(key.clone(), value.clone()); + } + + pub fn keys(&self) -> RewardAddresses { + RewardAddresses(self.0.keys().cloned().collect()) + } + + pub fn len(&self) -> usize { + self.0.len() + } +} diff --git a/rust/src/protocol_types/governance/proposals/treasury_withdrawals_action.rs b/rust/src/protocol_types/governance/proposals/treasury_withdrawals_action.rs new file mode 100644 index 00000000..9ca76337 --- /dev/null +++ b/rust/src/protocol_types/governance/proposals/treasury_withdrawals_action.rs @@ -0,0 +1,53 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct TreasuryWithdrawalsAction { + pub(crate) withdrawals: TreasuryWithdrawals, + pub(crate) policy_hash: Option, +} + +impl_to_from!(TreasuryWithdrawalsAction); + +#[wasm_bindgen] +impl TreasuryWithdrawalsAction { + pub fn withdrawals(&self) -> TreasuryWithdrawals { + self.withdrawals.clone() + } + + pub fn policy_hash(&self) -> Option { + self.policy_hash.clone() + } + + pub fn new(withdrawals: &TreasuryWithdrawals) -> Self { + Self { + withdrawals: withdrawals.clone(), + policy_hash: None, + } + } + + pub fn new_with_policy_hash( + withdrawals: &TreasuryWithdrawals, + policy_hash: &ScriptHash, + ) -> Self { + Self { + withdrawals: withdrawals.clone(), + policy_hash: Some(policy_hash.clone()), + } + } + + pub(crate) fn has_script_hash(&self) -> bool { + self.policy_hash.is_some() + } +} diff --git a/rust/src/protocol_types/governance/proposals/update_committee_action.rs b/rust/src/protocol_types/governance/proposals/update_committee_action.rs new file mode 100644 index 00000000..c36ed046 --- /dev/null +++ b/rust/src/protocol_types/governance/proposals/update_committee_action.rs @@ -0,0 +1,57 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct UpdateCommitteeAction { + pub(crate) gov_action_id: Option, + pub(crate) committee: Committee, + pub(crate) members_to_remove: Credentials, +} + +impl_to_from!(UpdateCommitteeAction); + +#[wasm_bindgen] +impl UpdateCommitteeAction { + pub fn gov_action_id(&self) -> Option { + self.gov_action_id.clone() + } + + pub fn committee(&self) -> Committee { + self.committee.clone() + } + + pub fn members_to_remove(&self) -> Credentials { + self.members_to_remove.clone() + } + + pub fn new(committee: &Committee, members_to_remove: &Credentials) -> Self { + Self { + gov_action_id: None, + committee: committee.clone(), + members_to_remove: members_to_remove.clone(), + } + } + + pub fn new_with_action_id( + gov_action_id: &GovernanceActionId, + committee: &Committee, + members_to_remove: &Credentials, + ) -> Self { + Self { + gov_action_id: Some(gov_action_id.clone()), + committee: committee.clone(), + members_to_remove: members_to_remove.clone(), + } + } +} diff --git a/rust/src/protocol_types/governance/proposals/voting_proposal.rs b/rust/src/protocol_types/governance/proposals/voting_proposal.rs new file mode 100644 index 00000000..57b95982 --- /dev/null +++ b/rust/src/protocol_types/governance/proposals/voting_proposal.rs @@ -0,0 +1,64 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct VotingProposal { + pub(crate) governance_action: GovernanceAction, + pub(crate) anchor: Anchor, + pub(crate) reward_account: RewardAddress, + pub(crate) deposit: Coin, +} + +impl_to_from!(VotingProposal); + +#[wasm_bindgen] +impl VotingProposal { + pub fn governance_action(&self) -> GovernanceAction { + self.governance_action.clone() + } + + pub fn anchor(&self) -> Anchor { + self.anchor.clone() + } + + pub fn reward_account(&self) -> RewardAddress { + self.reward_account.clone() + } + + pub fn deposit(&self) -> Coin { + self.deposit.clone() + } + + pub fn new( + governance_action: &GovernanceAction, + anchor: &Anchor, + reward_account: &RewardAddress, + deposit: &Coin, + ) -> Self { + Self { + governance_action: governance_action.clone(), + anchor: anchor.clone(), + reward_account: reward_account.clone(), + deposit: deposit.clone(), + } + } + + pub(crate) fn has_script_hash(&self) -> bool { + match self.governance_action.0 { + GovernanceActionEnum::ParameterChangeAction(ref action) => action.has_script_hash(), + GovernanceActionEnum::TreasuryWithdrawalsAction(ref action) => action.has_script_hash(), + _ => false, + } + } +} diff --git a/rust/src/protocol_types/governance/proposals/voting_proposals.rs b/rust/src/protocol_types/governance/proposals/voting_proposals.rs new file mode 100644 index 00000000..15e5413a --- /dev/null +++ b/rust/src/protocol_types/governance/proposals/voting_proposals.rs @@ -0,0 +1,106 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, +)] +#[wasm_bindgen] +pub struct VotingProposals { + pub(crate) proposals: Vec, + pub(crate) dedup: BTreeSet, +} + +impl_to_from!(VotingProposals); + +impl NoneOrEmpty for VotingProposals { + fn is_none_or_empty(&self) -> bool { + self.proposals.is_empty() + } +} + +#[wasm_bindgen] +impl VotingProposals { + pub fn new() -> Self { + Self { + proposals: Vec::new(), + dedup: BTreeSet::new(), + } + } + + pub fn len(&self) -> usize { + self.proposals.len() + } + + pub fn get(&self, index: usize) -> VotingProposal { + self.proposals[index].clone() + } + + /// Add a proposal to the set of proposals + /// Returns true if the proposal was added, false if it was already present + pub fn add(&mut self, proposal: &VotingProposal) -> bool { + if self.dedup.insert(proposal.clone()) { + self.proposals.push(proposal.clone()); + true + } else { + false + } + } + + pub(crate) fn add_move(&mut self, proposal: VotingProposal) { + if self.dedup.insert(proposal.clone()) { + self.proposals.push(proposal); + } + } + + pub(crate) fn from_vec(proposals: Vec) -> Self { + let mut voting_proposals = VotingProposals::new(); + for proposal in proposals { + voting_proposals.add_move(proposal); + } + voting_proposals + } + + #[allow(dead_code)] + pub(crate) fn contains(&self, proposal: &VotingProposal) -> bool { + self.dedup.contains(proposal) + } +} + +impl serde::Serialize for VotingProposals { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.proposals.serialize(serializer) + } +} + +impl<'de> serde::de::Deserialize<'de> for VotingProposals { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let vec = as serde::de::Deserialize>::deserialize( + deserializer, + )?; + Ok(Self::from_vec(vec)) + } +} + +impl JsonSchema for VotingProposals { + fn schema_name() -> String { + String::from("VotingProposals") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + Vec::::json_schema(gen) + } + fn is_referenceable() -> bool { + Vec::::is_referenceable() + } +} + diff --git a/rust/src/protocol_types/governance/voter.rs b/rust/src/protocol_types/governance/voter.rs new file mode 100644 index 00000000..dbd7932f --- /dev/null +++ b/rust/src/protocol_types/governance/voter.rs @@ -0,0 +1,112 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub(crate) enum VoterEnum { + ConstitutionalCommitteeHotCred(Credential), + DRep(Credential), + StakingPool(Ed25519KeyHash), +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] +pub enum VoterKind { + ConstitutionalCommitteeHotKeyHash, + ConstitutionalCommitteeHotScriptHash, + DRepKeyHash, + DRepScriptHash, + StakingPoolKeyHash, +} + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct Voter(pub(crate) VoterEnum); + +impl_to_from!(Voter); + +#[wasm_bindgen] +impl Voter { + pub fn new_constitutional_committee_hot_credential(cred: &Credential) -> Self { + Self(VoterEnum::ConstitutionalCommitteeHotCred(cred.clone())) + } + + pub fn new_drep_credential(cred: &Credential) -> Self { + Self(VoterEnum::DRep(cred.clone())) + } + + pub fn new_stake_pool_key_hash(key_hash: &Ed25519KeyHash) -> Self { + Self(VoterEnum::StakingPool(key_hash.clone())) + } + + pub fn kind(&self) -> VoterKind { + match &self.0 { + VoterEnum::ConstitutionalCommitteeHotCred(cred) => match cred.kind() { + CredKind::Key => VoterKind::ConstitutionalCommitteeHotKeyHash, + CredKind::Script => VoterKind::ConstitutionalCommitteeHotScriptHash, + }, + VoterEnum::DRep(cred) => match cred.kind() { + CredKind::Key => VoterKind::DRepKeyHash, + CredKind::Script => VoterKind::DRepScriptHash, + }, + VoterEnum::StakingPool(_) => VoterKind::StakingPoolKeyHash, + } + } + + pub fn to_constitutional_committee_hot_credential(&self) -> Option { + match &self.0 { + VoterEnum::ConstitutionalCommitteeHotCred(cred) => Some(cred.clone()), + _ => None, + } + } + + pub fn to_drep_credential(&self) -> Option { + match &self.0 { + VoterEnum::DRep(cred) => Some(cred.clone()), + _ => None, + } + } + + pub fn to_stake_pool_key_hash(&self) -> Option { + match &self.0 { + VoterEnum::StakingPool(key_hash) => Some(key_hash.clone()), + _ => None, + } + } + + pub fn has_script_credentials(&self) -> bool { + match &self.0 { + VoterEnum::ConstitutionalCommitteeHotCred(cred) => cred.has_script_hash(), + VoterEnum::DRep(cred) => cred.has_script_hash(), + VoterEnum::StakingPool(_) => false, + } + } + + pub fn to_key_hash(&self) -> Option { + match &self.0 { + VoterEnum::ConstitutionalCommitteeHotCred(cred) => cred.to_keyhash(), + VoterEnum::DRep(cred) => cred.to_keyhash(), + VoterEnum::StakingPool(key_hash) => Some(key_hash.clone()), + } + } +} diff --git a/rust/src/protocol_types/governance/voters.rs b/rust/src/protocol_types/governance/voters.rs new file mode 100644 index 00000000..8db4c401 --- /dev/null +++ b/rust/src/protocol_types/governance/voters.rs @@ -0,0 +1,37 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Eq, + Ord, + PartialEq, + PartialOrd, + Hash, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct Voters(pub(crate) Vec); + +to_from_json!(Voters); + +#[wasm_bindgen] +impl Voters { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn add(&mut self, voter: &Voter) { + self.0.push(voter.clone()); + } + + pub fn get(&self, index: usize) -> Option { + self.0.get(index).cloned() + } + + pub fn len(&self) -> usize { + self.0.len() + } +} diff --git a/rust/src/protocol_types/governance/voting_procedure.rs b/rust/src/protocol_types/governance/voting_procedure.rs new file mode 100644 index 00000000..46989bc5 --- /dev/null +++ b/rust/src/protocol_types/governance/voting_procedure.rs @@ -0,0 +1,65 @@ +use crate::*; + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub enum VoteKind { + No = 0, + Yes = 1, + Abstain = 2, +} + +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +#[wasm_bindgen] +pub struct VotingProcedure { + pub(crate) vote: VoteKind, + pub(crate) anchor: Option, +} + +impl_to_from!(VotingProcedure); + +#[wasm_bindgen] +impl VotingProcedure { + pub fn new(vote: VoteKind) -> Self { + Self { + vote: vote.clone(), + anchor: None, + } + } + + pub fn new_with_anchor(vote: VoteKind, anchor: &Anchor) -> Self { + Self { + vote: vote.clone(), + anchor: Some(anchor.clone()), + } + } + + pub fn vote_kind(&self) -> VoteKind { + self.vote.clone() + } + + pub fn anchor(&self) -> Option { + self.anchor.clone() + } +} diff --git a/rust/src/protocol_types/governance/voting_procedures.rs b/rust/src/protocol_types/governance/voting_procedures.rs new file mode 100644 index 00000000..97b978c1 --- /dev/null +++ b/rust/src/protocol_types/governance/voting_procedures.rs @@ -0,0 +1,152 @@ +use crate::*; +use schemars::gen::SchemaGenerator; +use schemars::schema::Schema; +use serde::ser::SerializeSeq; +use std::collections::BTreeMap; +use std::vec::Vec; + +#[derive( + Clone, + Debug, + Eq, + Ord, + PartialEq, + PartialOrd, + Hash, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +struct VoterVotes { + voter: Voter, + votes: BTreeSet, +} + +#[derive( + Clone, + Debug, + Eq, + Ord, + PartialEq, + PartialOrd, + Hash, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +struct Vote { + action_id: GovernanceActionId, + voting_procedure: VotingProcedure, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] +#[wasm_bindgen] +pub struct VotingProcedures( + pub(crate) BTreeMap>, +); + +impl_to_from!(VotingProcedures); + +impl NoneOrEmpty for VotingProcedures { + fn is_none_or_empty(&self) -> bool { + self.0.is_empty() || self.0.values().all(|v| v.is_empty()) + } +} + +#[wasm_bindgen] +impl VotingProcedures { + pub fn new() -> Self { + Self(BTreeMap::new()) + } + + pub fn insert( + &mut self, + voter: &Voter, + governance_action_id: &GovernanceActionId, + voting_procedure: &VotingProcedure, + ) { + self.0 + .entry(voter.clone()) + .or_insert_with(BTreeMap::new) + .insert(governance_action_id.clone(), voting_procedure.clone()); + } + + pub fn get( + &self, + voter: &Voter, + governance_action_id: &GovernanceActionId, + ) -> Option { + self.0 + .get(voter) + .and_then(|v| v.get(governance_action_id)) + .cloned() + } + + pub fn get_voters(&self) -> Voters { + Voters(self.0.keys().cloned().collect()) + } + + pub fn get_governance_action_ids_by_voter(&self, voter: &Voter) -> GovernanceActionIds { + GovernanceActionIds( + self.0 + .get(voter) + .map(|v| v.keys().cloned().collect()) + .unwrap_or_default(), + ) + } +} + +impl JsonSchema for VotingProcedures { + fn is_referenceable() -> bool { + Vec::::is_referenceable() + } + + fn schema_name() -> String { + "VotingProcedures".to_string() + } + + fn json_schema(gen: &mut SchemaGenerator) -> Schema { + Vec::::json_schema(gen) + } +} + +impl serde::ser::Serialize for VotingProcedures { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.0.len()))?; + for (voter, votes) in &self.0 { + let voter_votes = VoterVotes { + voter: voter.clone(), + votes: votes + .iter() + .map(|(action_id, voting_procedure)| Vote { + action_id: action_id.clone(), + voting_procedure: voting_procedure.clone(), + }) + .collect(), + }; + seq.serialize_element(&voter_votes)?; + } + seq.end() + } +} + +impl<'de> serde::de::Deserialize<'de> for VotingProcedures { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let all_votes: Vec = serde::de::Deserialize::deserialize(deserializer)?; + let mut voting_procedures = VotingProcedures::new(); + for votes in all_votes { + let mut voter_votes = BTreeMap::new(); + for vote in votes.votes { + voter_votes.insert(vote.action_id, vote.voting_procedure); + } + voting_procedures.0.insert(votes.voter, voter_votes); + } + Ok(voting_procedures) + } +} diff --git a/rust/src/protocol_types/metadata.rs b/rust/src/protocol_types/metadata.rs new file mode 100644 index 00000000..7ee1d429 --- /dev/null +++ b/rust/src/protocol_types/metadata.rs @@ -0,0 +1,752 @@ +use crate::*; +use hashlink::LinkedHashMap; + +const MD_MAX_LEN: usize = 64; + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct MetadataMap(pub(crate) LinkedHashMap); + +to_from_bytes!(MetadataMap); + +#[wasm_bindgen] +impl MetadataMap { + pub fn new() -> Self { + Self(LinkedHashMap::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn insert( + &mut self, + key: &TransactionMetadatum, + value: &TransactionMetadatum, + ) -> Option { + self.0.insert(key.clone(), value.clone()) + } + + // convenience function for inserting as a string key + pub fn insert_str( + &mut self, + key: &str, + value: &TransactionMetadatum, + ) -> Result, JsError> { + Ok(self.insert(&TransactionMetadatum::new_text(key.to_owned())?, value)) + } + + // convenience function for inserting 32-bit integers - for higher-precision integers use insert() with an Int struct + pub fn insert_i32( + &mut self, + key: i32, + value: &TransactionMetadatum, + ) -> Option { + self.insert(&TransactionMetadatum::new_int(&Int::new_i32(key)), value) + } + + pub fn get(&self, key: &TransactionMetadatum) -> Result { + self.0 + .get(key) + .map(|v| v.clone()) + .ok_or_else(|| JsError::from_str(&format!("key {:?} not found", key))) + } + + // convenience function for retrieving a string key + pub fn get_str(&self, key: &str) -> Result { + self.get(&TransactionMetadatum::new_text(key.to_owned())?) + } + + // convenience function for retrieving 32-bit integer keys - for higher-precision integers use get() with an Int struct + pub fn get_i32(&self, key: i32) -> Result { + self.get(&TransactionMetadatum::new_int(&Int::new_i32(key))) + } + + pub fn has(&self, key: &TransactionMetadatum) -> bool { + self.0.contains_key(key) + } + + pub fn keys(&self) -> MetadataList { + MetadataList( + self.0 + .iter() + .map(|(k, _v)| k.clone()) + .collect::>(), + ) + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct MetadataList(pub(crate) Vec); + +to_from_bytes!(MetadataList); + +#[wasm_bindgen] +impl MetadataList { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> TransactionMetadatum { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &TransactionMetadatum) { + self.0.push(elem.clone()); + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum TransactionMetadatumKind { + MetadataMap, + MetadataList, + Int, + Bytes, + Text, +} + +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub(crate) enum TransactionMetadatumEnum { + MetadataMap(MetadataMap), + MetadataList(MetadataList), + Int(Int), + Bytes(Vec), + Text(String), +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct TransactionMetadatum(pub(crate) TransactionMetadatumEnum); + +to_from_bytes!(TransactionMetadatum); + +#[wasm_bindgen] +impl TransactionMetadatum { + pub fn new_map(map: &MetadataMap) -> Self { + Self(TransactionMetadatumEnum::MetadataMap(map.clone())) + } + + pub fn new_list(list: &MetadataList) -> Self { + Self(TransactionMetadatumEnum::MetadataList(list.clone())) + } + + pub fn new_int(int: &Int) -> Self { + Self(TransactionMetadatumEnum::Int(int.clone())) + } + + pub fn new_bytes(bytes: Vec) -> Result { + if bytes.len() > MD_MAX_LEN { + Err(JsError::from_str(&format!( + "Max metadata bytes too long: {}, max = {}", + bytes.len(), + MD_MAX_LEN + ))) + } else { + Ok(Self(TransactionMetadatumEnum::Bytes(bytes))) + } + } + + pub fn new_text(text: String) -> Result { + if text.len() > MD_MAX_LEN { + Err(JsError::from_str(&format!( + "Max metadata string too long: {}, max = {}", + text.len(), + MD_MAX_LEN + ))) + } else { + Ok(Self(TransactionMetadatumEnum::Text(text))) + } + } + + pub fn kind(&self) -> TransactionMetadatumKind { + match &self.0 { + TransactionMetadatumEnum::MetadataMap(_) => TransactionMetadatumKind::MetadataMap, + TransactionMetadatumEnum::MetadataList(_) => TransactionMetadatumKind::MetadataList, + TransactionMetadatumEnum::Int(_) => TransactionMetadatumKind::Int, + TransactionMetadatumEnum::Bytes(_) => TransactionMetadatumKind::Bytes, + TransactionMetadatumEnum::Text(_) => TransactionMetadatumKind::Text, + } + } + + pub fn as_map(&self) -> Result { + match &self.0 { + TransactionMetadatumEnum::MetadataMap(x) => Ok(x.clone()), + _ => Err(JsError::from_str("not a map")), + } + } + + pub fn as_list(&self) -> Result { + match &self.0 { + TransactionMetadatumEnum::MetadataList(x) => Ok(x.clone()), + _ => Err(JsError::from_str("not a list")), + } + } + + pub fn as_int(&self) -> Result { + match &self.0 { + TransactionMetadatumEnum::Int(x) => Ok(x.clone()), + _ => Err(JsError::from_str("not an int")), + } + } + + pub fn as_bytes(&self) -> Result, JsError> { + match &self.0 { + TransactionMetadatumEnum::Bytes(x) => Ok(x.clone()), + _ => Err(JsError::from_str("not bytes")), + } + } + + pub fn as_text(&self) -> Result { + match &self.0 { + TransactionMetadatumEnum::Text(x) => Ok(x.clone()), + _ => Err(JsError::from_str("not text")), + } + } +} + +impl serde::Serialize for TransactionMetadatum { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let json_str = decode_metadatum_to_json_str(self, MetadataJsonSchema::DetailedSchema) + .map_err(|e| serde::ser::Error::custom(&format!("{:?}", e)))?; + serializer.serialize_str(&json_str) + } +} + +impl<'de> serde::de::Deserialize<'de> for TransactionMetadatum { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + encode_json_str_to_metadatum(s.clone(), MetadataJsonSchema::DetailedSchema).map_err(|e| { + serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&s), + &format!("{:?}", e).as_str(), + ) + }) + } +} + +// just for now we'll do json-in-json until I can figure this out better +// TODO: maybe not generate this? or how do we do this? +impl JsonSchema for TransactionMetadatum { + fn schema_name() -> String { + String::from("TransactionMetadatum") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } + fn is_referenceable() -> bool { + String::is_referenceable() + } +} + +pub type TransactionMetadatumLabel = BigNum; + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct TransactionMetadatumLabels(pub(crate) Vec); + +to_from_bytes!(TransactionMetadatumLabels); + +#[wasm_bindgen] +impl TransactionMetadatumLabels { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> TransactionMetadatumLabel { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &TransactionMetadatumLabel) { + self.0.push(elem.clone()); + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct GeneralTransactionMetadata( + pub(crate) LinkedHashMap, +); + +impl_to_from!(GeneralTransactionMetadata); + +#[wasm_bindgen] +impl GeneralTransactionMetadata { + pub fn new() -> Self { + Self(LinkedHashMap::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn insert( + &mut self, + key: &TransactionMetadatumLabel, + value: &TransactionMetadatum, + ) -> Option { + self.0.insert(key.clone(), value.clone()) + } + + pub fn get(&self, key: &TransactionMetadatumLabel) -> Option { + self.0.get(key).map(|v| v.clone()) + } + + pub fn keys(&self) -> TransactionMetadatumLabels { + TransactionMetadatumLabels( + self.0 + .iter() + .map(|(k, _v)| k.clone()) + .collect::>(), + ) + } +} + +impl serde::Serialize for GeneralTransactionMetadata { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let map = self.0.iter().collect::>(); + map.serialize(serializer) + } +} + +impl<'de> serde::de::Deserialize<'de> for GeneralTransactionMetadata { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let map = as serde::de::Deserialize>::deserialize( + deserializer, + )?; + Ok(Self(map.into_iter().collect())) + } +} + +impl JsonSchema for GeneralTransactionMetadata { + fn schema_name() -> String { + String::from("GeneralTransactionMetadata") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + std::collections::BTreeMap::::json_schema( + gen, + ) + } + fn is_referenceable() -> bool { + std::collections::BTreeMap::::is_referenceable() + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Ord, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct AuxiliaryData { + pub(crate) metadata: Option, + pub(crate) native_scripts: Option, + pub(crate) plutus_scripts: Option, + pub(crate) prefer_alonzo_format: bool, +} + +impl std::cmp::PartialEq for AuxiliaryData { + fn eq(&self, other: &Self) -> bool { + self.metadata.eq(&other.metadata) + && self.native_scripts.eq(&other.native_scripts) + && self.plutus_scripts.eq(&other.plutus_scripts) + } +} + +impl std::cmp::Eq for AuxiliaryData {} + +impl_to_from!(AuxiliaryData); + +#[wasm_bindgen] +impl AuxiliaryData { + pub fn new() -> Self { + Self { + metadata: None, + native_scripts: None, + plutus_scripts: None, + prefer_alonzo_format: false, + } + } + + pub fn metadata(&self) -> Option { + self.metadata.clone() + } + + pub fn set_metadata(&mut self, metadata: &GeneralTransactionMetadata) { + self.metadata = Some(metadata.clone()); + } + + pub fn native_scripts(&self) -> Option { + self.native_scripts.clone() + } + + pub fn set_native_scripts(&mut self, native_scripts: &NativeScripts) { + self.native_scripts = Some(native_scripts.clone()) + } + + pub fn plutus_scripts(&self) -> Option { + self.plutus_scripts.clone() + } + + pub fn set_plutus_scripts(&mut self, plutus_scripts: &PlutusScripts) { + self.plutus_scripts = Some(plutus_scripts.clone()) + } + + pub fn prefer_alonzo_format(&self) -> bool { + self.prefer_alonzo_format.clone() + } + + pub fn set_prefer_alonzo_format(&mut self, prefer: bool) { + self.prefer_alonzo_format = prefer + } +} + +// encodes arbitrary bytes into chunks of 64 bytes (the limit for bytes) as a list to be valid Metadata +#[wasm_bindgen] +pub fn encode_arbitrary_bytes_as_metadatum(bytes: &[u8]) -> TransactionMetadatum { + let mut list = MetadataList::new(); + for chunk in bytes.chunks(MD_MAX_LEN) { + // this should never fail as we are already chunking it + list.add(&TransactionMetadatum::new_bytes(chunk.to_vec()).unwrap()); + } + TransactionMetadatum::new_list(&list) +} + +// decodes from chunks of bytes in a list to a byte vector if that is the metadata format, otherwise returns None +#[wasm_bindgen] +pub fn decode_arbitrary_bytes_from_metadatum( + metadata: &TransactionMetadatum, +) -> Result, JsError> { + let mut bytes = Vec::new(); + for elem in metadata.as_list()?.0 { + bytes.append(&mut elem.as_bytes()?); + } + Ok(bytes) +} + +#[wasm_bindgen] +#[derive(Copy, Clone, Eq, PartialEq)] +// Different schema methods for mapping between JSON and the metadata CBOR. +// This conversion should match TxMetadataJsonSchema in cardano-node defined (at time of writing) here: +// https://github.com/input-output-hk/cardano-node/blob/master/cardano-api/src/Cardano/Api/MetaData.hs +// but has 2 additional schemas for more or less conversionse +// Note: Byte/Strings (including keys) in any schema must be at most 64 bytes in length +pub enum MetadataJsonSchema { + // Does zero implicit conversions. + // Round-trip conversions are 100% consistent + // Treats maps DIRECTLY as maps in JSON in a natural way e.g. {"key1": 47, "key2": [0, 1]]} + // From JSON: + // * null/true/false NOT supported. + // * keys treated as strings only + // To JSON + // * Bytes, non-string keys NOT supported. + // Stricter than any TxMetadataJsonSchema in cardano-node but more natural for JSON -> Metadata + NoConversions, + // Does some implicit conversions. + // Round-trip conversions MD -> JSON -> MD is NOT consistent, but JSON -> MD -> JSON is. + // Without using bytes + // Maps are treated as an array of k-v pairs as such: [{"key1": 47}, {"key2": [0, 1]}, {"key3": "0xFFFF"}] + // From JSON: + // * null/true/false NOT supported. + // * Strings parseable as bytes (0x starting hex) or integers are converted. + // To JSON: + // * Non-string keys partially supported (bytes as 0x starting hex string, integer converted to string). + // * Bytes are converted to hex strings starting with 0x for both values and keys. + // Corresponds to TxMetadataJsonSchema's TxMetadataJsonNoSchema in cardano-node + BasicConversions, + // Supports the annotated schema presented in cardano-node with tagged values e.g. {"int": 7}, {"list": [0, 1]} + // Round-trip conversions are 100% consistent + // Maps are treated as an array of k-v pairs as such: [{"key1": {"int": 47}}, {"key2": {"list": [0, 1]}}, {"key3": {"bytes": "0xFFFF"}}] + // From JSON: + // * null/true/false NOT supported. + // * Strings parseable as bytes (hex WITHOUT 0x prefix) or integers converted. + // To JSON: + // * Non-string keys are supported. Any key parseable as JSON is encoded as metadata instead of a string + // Corresponds to TxMetadataJsonSchema's TxMetadataJsonDetailedSchema in cardano-node + DetailedSchema, +} + +fn supports_tagged_values(schema: MetadataJsonSchema) -> bool { + match schema { + MetadataJsonSchema::NoConversions | MetadataJsonSchema::BasicConversions => false, + MetadataJsonSchema::DetailedSchema => true, + } +} + +fn hex_string_to_bytes(hex: &str) -> Option> { + if hex.starts_with("0x") { + hex::decode(&hex[2..]).ok() + } else { + None + } +} + +fn bytes_to_hex_string(bytes: &[u8]) -> String { + format!("0x{}", hex::encode(bytes)) +} + +// Converts JSON to Metadata according to MetadataJsonSchema +#[wasm_bindgen] +pub fn encode_json_str_to_metadatum( + json: String, + schema: MetadataJsonSchema, +) -> Result { + let value = serde_json::from_str(&json).map_err(|e| JsError::from_str(&e.to_string()))?; + encode_json_value_to_metadatum(value, schema) +} + +pub fn encode_json_value_to_metadatum( + value: serde_json::Value, + schema: MetadataJsonSchema, +) -> Result { + use serde_json::Value; + fn encode_number(x: serde_json::Number) -> Result { + if let Some(x) = x.as_u64() { + Ok(TransactionMetadatum::new_int(&Int::new(&x.into()))) + } else if let Some(x) = x.as_i64() { + Ok(TransactionMetadatum::new_int(&Int::new_negative( + &(-x as u64).into(), + ))) + } else { + Err(JsError::from_str("floats not allowed in metadata")) + } + } + fn encode_string( + s: String, + schema: MetadataJsonSchema, + ) -> Result { + if schema == MetadataJsonSchema::BasicConversions { + match hex_string_to_bytes(&s) { + Some(bytes) => TransactionMetadatum::new_bytes(bytes), + None => TransactionMetadatum::new_text(s), + } + } else { + TransactionMetadatum::new_text(s) + } + } + fn encode_array( + json_arr: Vec, + schema: MetadataJsonSchema, + ) -> Result { + let mut arr = MetadataList::new(); + for value in json_arr { + arr.add(&encode_json_value_to_metadatum(value, schema)?); + } + Ok(TransactionMetadatum::new_list(&arr)) + } + match schema { + MetadataJsonSchema::NoConversions | MetadataJsonSchema::BasicConversions => match value { + Value::Null => Err(JsError::from_str("null not allowed in metadata")), + Value::Bool(_) => Err(JsError::from_str("bools not allowed in metadata")), + Value::Number(x) => encode_number(x), + Value::String(s) => encode_string(s, schema), + Value::Array(json_arr) => encode_array(json_arr, schema), + Value::Object(json_obj) => { + let mut map = MetadataMap::new(); + for (raw_key, value) in json_obj { + let key = if schema == MetadataJsonSchema::BasicConversions { + match raw_key.parse::() { + Ok(x) => TransactionMetadatum::new_int(&Int(x)), + Err(_) => encode_string(raw_key, schema)?, + } + } else { + TransactionMetadatum::new_text(raw_key)? + }; + map.insert(&key, &encode_json_value_to_metadatum(value, schema)?); + } + Ok(TransactionMetadatum::new_map(&map)) + } + }, + // we rely on tagged objects to control parsing here instead + MetadataJsonSchema::DetailedSchema => match value { + Value::Object(obj) if obj.len() == 1 => { + let (k, v) = obj.into_iter().next().unwrap(); + fn tag_mismatch() -> JsError { + JsError::from_str("key does not match type") + } + match k.as_str() { + "int" => match v { + Value::Number(x) => encode_number(x), + _ => Err(tag_mismatch()), + }, + "string" => { + encode_string(v.as_str().ok_or_else(tag_mismatch)?.to_owned(), schema) + } + "bytes" => match hex::decode(v.as_str().ok_or_else(tag_mismatch)?) { + Ok(bytes) => TransactionMetadatum::new_bytes(bytes), + Err(_) => Err(JsError::from_str( + "invalid hex string in tagged byte-object", + )), + }, + "list" => encode_array(v.as_array().ok_or_else(tag_mismatch)?.clone(), schema), + "map" => { + let mut map = MetadataMap::new(); + fn map_entry_err() -> JsError { + JsError::from_str("entry format in detailed schema map object not correct. Needs to be of form {\"k\": \"key\", \"v\": value}") + } + for entry in v.as_array().ok_or_else(tag_mismatch)? { + let entry_obj = entry.as_object().ok_or_else(map_entry_err)?; + let raw_key = entry_obj.get("k").ok_or_else(map_entry_err)?; + let value = entry_obj.get("v").ok_or_else(map_entry_err)?; + let key = encode_json_value_to_metadatum(raw_key.clone(), schema)?; + map.insert( + &key, + &encode_json_value_to_metadatum(value.clone(), schema)?, + ); + } + Ok(TransactionMetadatum::new_map(&map)) + } + invalid_key => Err(JsError::from_str(&format!( + "key '{}' in tagged object not valid", + invalid_key + ))), + } + } + _ => Err(JsError::from_str( + "DetailedSchema requires types to be tagged objects", + )), + }, + } +} + +// Converts Metadata to JSON according to MetadataJsonSchema +#[wasm_bindgen] +pub fn decode_metadatum_to_json_str( + metadatum: &TransactionMetadatum, + schema: MetadataJsonSchema, +) -> Result { + let value = decode_metadatum_to_json_value(metadatum, schema)?; + serde_json::to_string(&value).map_err(|e| JsError::from_str(&e.to_string())) +} + +pub fn decode_metadatum_to_json_value( + metadatum: &TransactionMetadatum, + schema: MetadataJsonSchema, +) -> Result { + use serde_json::Value; + use std::convert::TryFrom; + fn decode_key( + key: &TransactionMetadatum, + schema: MetadataJsonSchema, + ) -> Result { + match &key.0 { + TransactionMetadatumEnum::Text(s) => Ok(s.clone()), + TransactionMetadatumEnum::Bytes(b) if schema != MetadataJsonSchema::NoConversions => { + Ok(bytes_to_hex_string(b.as_ref())) + } + TransactionMetadatumEnum::Int(i) if schema != MetadataJsonSchema::NoConversions => { + let int_str = if i.0 >= 0 { + u64::try_from(i.0).map(|x| x.to_string()) + } else { + i64::try_from(i.0).map(|x| x.to_string()) + }; + int_str.map_err(|e| JsError::from_str(&e.to_string())) + } + TransactionMetadatumEnum::MetadataList(list) + if schema == MetadataJsonSchema::DetailedSchema => + { + decode_metadatum_to_json_str(&TransactionMetadatum::new_list(&list), schema) + } + TransactionMetadatumEnum::MetadataMap(map) + if schema == MetadataJsonSchema::DetailedSchema => + { + decode_metadatum_to_json_str(&TransactionMetadatum::new_map(&map), schema) + } + _ => Err(JsError::from_str(&format!( + "key type {:?} not allowed in JSON under specified schema", + key.0 + ))), + } + } + let (type_key, value) = match &metadatum.0 { + TransactionMetadatumEnum::MetadataMap(map) => match schema { + MetadataJsonSchema::NoConversions | MetadataJsonSchema::BasicConversions => { + // treats maps directly as JSON maps + let mut json_map = serde_json::map::Map::with_capacity(map.len()); + for (key, value) in map.0.iter() { + json_map.insert( + decode_key(key, schema)?, + decode_metadatum_to_json_value(value, schema)?, + ); + } + ("map", Value::from(json_map)) + } + + MetadataJsonSchema::DetailedSchema => ( + "map", + Value::from( + map.0 + .iter() + .map(|(key, value)| { + // must encode maps as JSON lists of objects with k/v keys + // also in these schemas we support more key types than strings + let k = decode_metadatum_to_json_value(key, schema)?; + let v = decode_metadatum_to_json_value(value, schema)?; + let mut kv_obj = serde_json::map::Map::with_capacity(2); + kv_obj.insert(String::from("k"), Value::from(k)); + kv_obj.insert(String::from("v"), v); + Ok(Value::from(kv_obj)) + }) + .collect::, JsError>>()?, + ), + ), + }, + TransactionMetadatumEnum::MetadataList(arr) => ( + "list", + Value::from( + arr.0 + .iter() + .map(|e| decode_metadatum_to_json_value(e, schema)) + .collect::, JsError>>()?, + ), + ), + TransactionMetadatumEnum::Int(x) => ( + "int", + if x.0 >= 0 { + Value::from(u64::try_from(x.0).map_err(|e| JsError::from_str(&e.to_string()))?) + } else { + Value::from(i64::try_from(x.0).map_err(|e| JsError::from_str(&e.to_string()))?) + }, + ), + TransactionMetadatumEnum::Bytes(bytes) => ( + "bytes", + match schema { + MetadataJsonSchema::NoConversions => Err(JsError::from_str( + "bytes not allowed in JSON in specified schema", + )), + // 0x prefix + MetadataJsonSchema::BasicConversions => { + Ok(Value::from(bytes_to_hex_string(bytes.as_ref()))) + } + // no prefix + MetadataJsonSchema::DetailedSchema => Ok(Value::from(hex::encode(bytes))), + }?, + ), + TransactionMetadatumEnum::Text(s) => ("string", Value::from(s.clone())), + }; + // potentially wrap value in a keyed map to represent more types + if supports_tagged_values(schema) { + let mut wrapper = serde_json::map::Map::with_capacity(1); + wrapper.insert(String::from(type_key), value); + Ok(Value::from(wrapper)) + } else { + Ok(value) + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/mod.rs b/rust/src/protocol_types/mod.rs index 7eac86ae..3a0642ce 100644 --- a/rust/src/protocol_types/mod.rs +++ b/rust/src/protocol_types/mod.rs @@ -1,5 +1,60 @@ //TODO: move all protocol types to this module -pub mod fixed_tx; +mod fixed_tx; +pub use fixed_tx::*; -#[cfg(test)] -mod tests; \ No newline at end of file +mod certificates; +pub use certificates::*; + +mod governance; +pub use governance::*; + +mod plutus; +pub use plutus::*; + +mod metadata; +pub use metadata::*; + +mod transaction_body; +pub use transaction_body::*; + +mod protocol_param_update; +pub use protocol_param_update::*; + +mod address; +pub use address::*; + +mod tx_input; +pub use tx_input::*; + +mod tx_inputs; +pub use tx_inputs::*; + +mod credential; +pub use credential::*; + +mod credentials; +pub use credentials::*; + +mod ed25519_key_hashes; +pub use ed25519_key_hashes::*; + +mod witnesses; +pub use witnesses::*; + +mod crypto; +pub use crypto::*; + +mod native_script; +pub use native_script::*; + +mod native_scripts; +pub use native_scripts::*; + +mod numeric; +pub use numeric::*; + +mod script_ref; +pub use script_ref::*; + +mod block; +pub use block::*; diff --git a/rust/src/protocol_types/native_script.rs b/rust/src/protocol_types/native_script.rs new file mode 100644 index 00000000..4f1f7d64 --- /dev/null +++ b/rust/src/protocol_types/native_script.rs @@ -0,0 +1,306 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum NativeScriptKind { + ScriptPubkey, + ScriptAll, + ScriptAny, + ScriptNOfK, + TimelockStart, + TimelockExpiry, +} + +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub enum NativeScriptEnum { + ScriptPubkey(ScriptPubkey), + ScriptAll(ScriptAll), + ScriptAny(ScriptAny), + ScriptNOfK(ScriptNOfK), + TimelockStart(TimelockStart), + TimelockExpiry(TimelockExpiry), +} + +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub struct NativeScript(pub(crate) NativeScriptEnum); + +impl_to_from!(NativeScript); + +#[wasm_bindgen] +impl NativeScript { + pub fn hash(&self) -> ScriptHash { + let mut bytes = Vec::with_capacity(self.to_bytes().len() + 1); + bytes.extend_from_slice(&vec![ScriptHashNamespace::NativeScript as u8]); + bytes.extend_from_slice(&self.to_bytes()); + ScriptHash::from(blake2b224(bytes.as_ref())) + } + + pub fn new_script_pubkey(script_pubkey: &ScriptPubkey) -> Self { + Self(NativeScriptEnum::ScriptPubkey(script_pubkey.clone())) + } + + pub fn new_script_all(script_all: &ScriptAll) -> Self { + Self(NativeScriptEnum::ScriptAll(script_all.clone())) + } + + pub fn new_script_any(script_any: &ScriptAny) -> Self { + Self(NativeScriptEnum::ScriptAny(script_any.clone())) + } + + pub fn new_script_n_of_k(script_n_of_k: &ScriptNOfK) -> Self { + Self(NativeScriptEnum::ScriptNOfK(script_n_of_k.clone())) + } + + pub fn new_timelock_start(timelock_start: &TimelockStart) -> Self { + Self(NativeScriptEnum::TimelockStart(timelock_start.clone())) + } + + pub fn new_timelock_expiry(timelock_expiry: &TimelockExpiry) -> Self { + Self(NativeScriptEnum::TimelockExpiry(timelock_expiry.clone())) + } + + pub fn kind(&self) -> NativeScriptKind { + match &self.0 { + NativeScriptEnum::ScriptPubkey(_) => NativeScriptKind::ScriptPubkey, + NativeScriptEnum::ScriptAll(_) => NativeScriptKind::ScriptAll, + NativeScriptEnum::ScriptAny(_) => NativeScriptKind::ScriptAny, + NativeScriptEnum::ScriptNOfK(_) => NativeScriptKind::ScriptNOfK, + NativeScriptEnum::TimelockStart(_) => NativeScriptKind::TimelockStart, + NativeScriptEnum::TimelockExpiry(_) => NativeScriptKind::TimelockExpiry, + } + } + + pub fn as_script_pubkey(&self) -> Option { + match &self.0 { + NativeScriptEnum::ScriptPubkey(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_script_all(&self) -> Option { + match &self.0 { + NativeScriptEnum::ScriptAll(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_script_any(&self) -> Option { + match &self.0 { + NativeScriptEnum::ScriptAny(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_script_n_of_k(&self) -> Option { + match &self.0 { + NativeScriptEnum::ScriptNOfK(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_timelock_start(&self) -> Option { + match &self.0 { + NativeScriptEnum::TimelockStart(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_timelock_expiry(&self) -> Option { + match &self.0 { + NativeScriptEnum::TimelockExpiry(x) => Some(x.clone()), + _ => None, + } + } + + /// Returns a set of Ed25519KeyHashes + /// contained within this script recursively on any depth level. + /// The order of the keys in the result is not determined in any way. + pub fn get_required_signers(&self) -> Ed25519KeyHashes { + Ed25519KeyHashes::from(self) + } +} + +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub struct ScriptPubkey { + pub(crate) addr_keyhash: Ed25519KeyHash, +} + +impl_to_from!(ScriptPubkey); + +#[wasm_bindgen] +impl ScriptPubkey { + pub fn addr_keyhash(&self) -> Ed25519KeyHash { + self.addr_keyhash.clone() + } + + pub fn new(addr_keyhash: &Ed25519KeyHash) -> Self { + Self { + addr_keyhash: addr_keyhash.clone(), + } + } +} + +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub struct ScriptAll { + pub(crate) native_scripts: NativeScripts, +} + +impl_to_from!(ScriptAll); + +#[wasm_bindgen] +impl ScriptAll { + pub fn native_scripts(&self) -> NativeScripts { + self.native_scripts.clone() + } + + pub fn new(native_scripts: &NativeScripts) -> Self { + Self { + native_scripts: native_scripts.clone(), + } + } +} + +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub struct ScriptAny { + pub(crate) native_scripts: NativeScripts, +} + +impl_to_from!(ScriptAny); + +#[wasm_bindgen] +impl ScriptAny { + pub fn native_scripts(&self) -> NativeScripts { + self.native_scripts.clone() + } + + pub fn new(native_scripts: &NativeScripts) -> Self { + Self { + native_scripts: native_scripts.clone(), + } + } +} + +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub struct ScriptNOfK { + pub(crate) n: u32, + pub(crate) native_scripts: NativeScripts, +} + +impl_to_from!(ScriptNOfK); + +#[wasm_bindgen] +impl ScriptNOfK { + pub fn n(&self) -> u32 { + self.n + } + + pub fn native_scripts(&self) -> NativeScripts { + self.native_scripts.clone() + } + + pub fn new(n: u32, native_scripts: &NativeScripts) -> Self { + Self { + n: n, + native_scripts: native_scripts.clone(), + } + } +} + +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub struct TimelockStart { + pub(crate) slot: SlotBigNum, +} + +impl_to_from!(TimelockStart); + +#[wasm_bindgen] +impl TimelockStart { + /// !!! DEPRECATED !!! + /// Returns a Slot32 (u32) value in case the underlying original BigNum (u64) value is within the limits. + /// Otherwise will just raise an error. + /// Use `.slot_bignum` instead + #[deprecated( + since = "10.1.0", + note = "Possible boundary error. Use slot_bignum instead" + )] + pub fn slot(&self) -> Result { + self.slot.try_into() + } + + pub fn slot_bignum(&self) -> SlotBigNum { + self.slot + } + + /// !!! DEPRECATED !!! + /// This constructor uses outdated slot number format. + /// Use `.new_timelockstart` instead. + #[deprecated( + since = "10.1.0", + note = "Underlying value capacity (BigNum u64) bigger then Slot32. Use new_bignum instead." + )] + pub fn new(slot: Slot32) -> Self { + Self { slot: slot.into() } + } + + pub fn new_timelockstart(slot: &SlotBigNum) -> Self { + Self { slot: slot.clone() } + } +} + +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub struct TimelockExpiry { + pub(crate) slot: SlotBigNum, +} + +impl_to_from!(TimelockExpiry); + +#[wasm_bindgen] +impl TimelockExpiry { + pub fn slot(&self) -> Result { + self.slot.try_into() + } + + pub fn slot_bignum(&self) -> SlotBigNum { + self.slot + } + + /// !!! DEPRECATED !!! + /// This constructor uses outdated slot number format. + /// Use `.new_timelockexpiry` instead + #[deprecated( + since = "10.1.0", + note = "Underlying value capacity (BigNum u64) bigger then Slot32. Use new_bignum instead." + )] + pub fn new(slot: Slot32) -> Self { + Self { + slot: (slot.into()), + } + } + + pub fn new_timelockexpiry(slot: &SlotBigNum) -> Self { + Self { slot: slot.clone() } + } +} diff --git a/rust/src/protocol_types/native_scripts.rs b/rust/src/protocol_types/native_scripts.rs new file mode 100644 index 00000000..3e1bf99e --- /dev/null +++ b/rust/src/protocol_types/native_scripts.rs @@ -0,0 +1,85 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub struct NativeScripts(pub(crate) Vec); + +#[wasm_bindgen] +impl NativeScripts { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> NativeScript { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &NativeScript) { + self.0.push(elem.clone()); + } + + pub(crate) fn deduplicated_view(&self) -> Vec<&NativeScript> { + let mut dedup = BTreeSet::new(); + let mut scripts = Vec::new(); + for elem in &self.0 { + if dedup.insert(elem) { + scripts.push(elem); + } + } + scripts + } + + pub(crate) fn deduplicated_clone(&self) -> NativeScripts { + let mut dedup = BTreeSet::new(); + let mut scripts = Vec::new(); + for script in &self.0 { + if dedup.insert(script.clone()) { + scripts.push(script.clone()); + } + } + NativeScripts(scripts) + } + + #[allow(dead_code)] + pub(crate) fn contains(&self, script: &NativeScript) -> bool { + self.0.contains(script) + } +} + +impl_to_from!(NativeScripts); + +impl From> for NativeScripts { + fn from(scripts: Vec) -> Self { + scripts.iter().fold( + NativeScripts::new(), + |mut scripts, s| { + scripts.add(s); + scripts + }, + ) + } +} + +impl NoneOrEmpty for NativeScripts { + fn is_none_or_empty(&self) -> bool { + self.0.is_empty() + } +} + +impl From<&NativeScripts> for Ed25519KeyHashes { + fn from(scripts: &NativeScripts) -> Self { + scripts + .0 + .iter() + .fold(Ed25519KeyHashes::new(), |mut set, s| { + set.extend_move(Ed25519KeyHashes::from(s)); + set + }) + } +} diff --git a/rust/src/protocol_types/numeric/big_int.rs b/rust/src/protocol_types/numeric/big_int.rs new file mode 100644 index 00000000..4c9655f8 --- /dev/null +++ b/rust/src/protocol_types/numeric/big_int.rs @@ -0,0 +1,158 @@ +use num_bigint::Sign; +use num_integer::Integer; +use num_traits::Signed; +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] +pub struct BigInt(pub(crate) num_bigint::BigInt); + +impl_to_from!(BigInt); + +impl serde::Serialize for BigInt { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_str()) + } +} + +impl<'de> serde::de::Deserialize<'de> for BigInt { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + BigInt::from_str(&s).map_err(|_e| { + serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&s), + &"string rep of a big int", + ) + }) + } +} + +impl JsonSchema for BigInt { + fn schema_name() -> String { + String::from("BigInt") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } + fn is_referenceable() -> bool { + String::is_referenceable() + } +} + +#[wasm_bindgen] +impl BigInt { + pub fn is_zero(&self) -> bool { + self.0.sign() == Sign::NoSign + } + + pub fn as_u64(&self) -> Option { + let (sign, u64_digits) = self.0.to_u64_digits(); + if sign == Sign::Minus { + return None; + } + match u64_digits.len() { + 0 => Some(BigNum::zero()), + 1 => Some((*u64_digits.first().unwrap()).into()), + _ => None, + } + } + + pub fn as_int(&self) -> Option { + let (sign, u64_digits) = self.0.to_u64_digits(); + let u64_digit = match u64_digits.len() { + 0 => Some(BigNum::zero()), + 1 => Some((*u64_digits.first().unwrap()).into()), + _ => None, + }?; + match sign { + num_bigint::Sign::NoSign | num_bigint::Sign::Plus => Some(Int::new(&u64_digit)), + num_bigint::Sign::Minus => Some(Int::new_negative(&u64_digit)), + } + } + + pub fn from_str(text: &str) -> Result { + use std::str::FromStr; + num_bigint::BigInt::from_str(text) + .map_err(|e| JsError::from_str(&format! {"{:?}", e})) + .map(Self) + } + + pub fn to_str(&self) -> String { + self.0.to_string() + } + + pub fn add(&self, other: &BigInt) -> BigInt { + Self(&self.0 + &other.0) + } + + pub fn sub(&self, other: &BigInt) -> BigInt { + Self(&self.0 - &other.0) + } + + pub fn mul(&self, other: &BigInt) -> BigInt { + Self(&self.0 * &other.0) + } + + pub fn pow(&self, exp: u32) -> BigInt { + Self(self.0.pow(exp)) + } + + pub fn one() -> BigInt { + Self(num_bigint::BigInt::from(1)) + } + + pub fn zero() -> BigInt { + Self(num_bigint::BigInt::from(0)) + } + + pub fn abs(&self) -> BigInt { + Self(self.0.abs()) + } + + pub fn increment(&self) -> BigInt { + self.add(&Self::one()) + } + + pub fn div_ceil(&self, other: &BigInt) -> BigInt { + Self(self.0.div_ceil(&other.0)) + } + + pub fn div_floor(&self, other: &BigInt) -> BigInt { + Self(self.0.div_floor(&other.0)) + } + + pub(crate) fn is_negative(&self) -> bool { + self.0.is_negative() + } +} + +impl std::convert::From for BigInt + where + T: std::convert::Into, +{ + fn from(x: T) -> Self { + Self(x.into()) + } +} + +impl From for BigInt { + fn from(x: BigNum) -> Self { + Self(x.0.into()) + } +} + +impl From<&BigNum> for BigInt { + fn from(x: &BigNum) -> Self { + Self(x.0.into()) + } +} + +pub fn to_bigint(val: u64) -> BigInt { + BigInt::from_str(&val.to_string()).unwrap() +} \ No newline at end of file diff --git a/rust/src/protocol_types/numeric/big_num.rs b/rust/src/protocol_types/numeric/big_num.rs new file mode 100644 index 00000000..7a39df12 --- /dev/null +++ b/rust/src/protocol_types/numeric/big_num.rs @@ -0,0 +1,202 @@ +use std::convert::TryFrom; +use std::ops::Div; +use crate::*; + +// Generic u64 wrapper for platforms that don't support u64 or BigInt/etc +// This is an unsigned type - no negative numbers. +// Can be converted to/from plain rust +#[wasm_bindgen] +#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Default)] +pub struct BigNum(pub(crate) u64); + +// Specifies an amount of ADA in terms of lovelace +pub type Coin = BigNum; + +impl_to_from!(BigNum); + +impl std::fmt::Display for BigNum { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +#[wasm_bindgen] +impl BigNum { + // Create a BigNum from a standard rust string representation + pub fn from_str(string: &str) -> Result { + string + .parse::() + .map_err(|e| JsError::from_str(&format! {"{:?}", e})) + .map(BigNum) + } + + // String representation of the BigNum value for use from environments that don't support BigInt + pub fn to_str(&self) -> String { + format!("{}", self.0) + } + + pub fn zero() -> Self { + Self(0) + } + + pub fn one() -> Self { + Self(1) + } + + pub fn is_zero(&self) -> bool { + self.0 == 0 + } + + pub fn div_floor(&self, other: &BigNum) -> BigNum { + // same as (a / b) + let res = self.0.div(&other.0); + Self(res) + } + + pub fn checked_mul(&self, other: &BigNum) -> Result { + match self.0.checked_mul(other.0) { + Some(value) => Ok(BigNum(value)), + None => Err(JsError::from_str("overflow")), + } + } + + pub fn checked_add(&self, other: &BigNum) -> Result { + match self.0.checked_add(other.0) { + Some(value) => Ok(BigNum(value)), + None => Err(JsError::from_str("overflow")), + } + } + + pub fn checked_sub(&self, other: &BigNum) -> Result { + match self.0.checked_sub(other.0) { + Some(value) => Ok(BigNum(value)), + None => Err(JsError::from_str("underflow")), + } + } + + /// returns 0 if it would otherwise underflow + pub fn clamped_sub(&self, other: &BigNum) -> BigNum { + match self.0.checked_sub(other.0) { + Some(value) => BigNum(value), + None => BigNum(0), + } + } + + pub fn compare(&self, rhs_value: &BigNum) -> i8 { + match self.cmp(&rhs_value) { + std::cmp::Ordering::Equal => 0, + std::cmp::Ordering::Less => -1, + std::cmp::Ordering::Greater => 1, + } + } + + pub fn less_than(&self, rhs_value: &BigNum) -> bool { + self.compare(rhs_value) < 0 + } + + pub fn max_value() -> BigNum { + BigNum(u64::max_value()) + } + + pub fn max(a: &BigNum, b: &BigNum) -> BigNum { + if a.less_than(b) { + b.clone() + } else { + a.clone() + } + } +} + +impl TryFrom for u32 { + type Error = JsError; + + fn try_from(value: BigNum) -> Result { + if value.0 > u32::MAX.into() { + Err(JsError::from_str(&format!( + "Value {} is bigger than max u32 {}", + value.0, + u32::MAX + ))) + } else { + Ok(value.0 as u32) + } + } +} + +impl From for u64 { + fn from(value: BigNum) -> Self { + value.0 + } +} + +impl From<&BigNum> for u64 { + fn from(value: &BigNum) -> Self { + value.0 + } +} + +impl From for usize { + fn from(value: BigNum) -> Self { + value.0 as usize + } +} + +impl From for BigNum { + fn from(value: u64) -> Self { + return BigNum(value); + } +} + +impl From for BigNum { + fn from(value: usize) -> Self { + return BigNum(value as u64); + } +} + +impl From for BigNum { + fn from(value: u32) -> Self { + return BigNum(value.into()); + } +} + +impl From for BigNum { + fn from(value: u8) -> Self { + return BigNum(value.into()); + } +} + +impl serde::Serialize for BigNum { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_str()) + } +} + +impl<'de> serde::de::Deserialize<'de> for BigNum { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + Self::from_str(&s).map_err(|_e| { + serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&s), + &"string rep of a number", + ) + }) + } +} + +impl JsonSchema for BigNum { + fn schema_name() -> String { + String::from("BigNum") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } + fn is_referenceable() -> bool { + String::is_referenceable() + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/numeric/int.rs b/rust/src/protocol_types/numeric/int.rs new file mode 100644 index 00000000..08e1d54e --- /dev/null +++ b/rust/src/protocol_types/numeric/int.rs @@ -0,0 +1,137 @@ +use crate::*; + +// CBOR has int = uint / nint +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct Int(pub(crate) i128); + +impl_to_from!(Int); + +#[wasm_bindgen] +impl Int { + pub fn new(x: &BigNum) -> Self { + Self(x.0 as i128) + } + + pub fn new_negative(x: &BigNum) -> Self { + Self(-(x.0 as i128)) + } + + pub fn new_i32(x: i32) -> Self { + Self(x as i128) + } + + pub fn is_positive(&self) -> bool { + return self.0 >= 0; + } + + /// BigNum can only contain unsigned u64 values + /// + /// This function will return the BigNum representation + /// only in case the underlying i128 value is positive. + /// + /// Otherwise nothing will be returned (undefined). + pub fn as_positive(&self) -> Option { + if self.is_positive() { + Some((self.0 as u64).into()) + } else { + None + } + } + + /// BigNum can only contain unsigned u64 values + /// + /// This function will return the *absolute* BigNum representation + /// only in case the underlying i128 value is negative. + /// + /// Otherwise nothing will be returned (undefined). + pub fn as_negative(&self) -> Option { + if !self.is_positive() { + Some(((-self.0) as u64).into()) + } else { + None + } + } + + /// !!! DEPRECATED !!! + /// Returns an i32 value in case the underlying original i128 value is within the limits. + /// Otherwise will just return an empty value (undefined). + #[deprecated( + since = "10.0.0", + note = "Unsafe ignoring of possible boundary error and it's not clear from the function name. Use `as_i32_or_nothing`, `as_i32_or_fail`, or `to_str`" + )] + pub fn as_i32(&self) -> Option { + self.as_i32_or_nothing() + } + + /// Returns the underlying value converted to i32 if possible (within limits) + /// Otherwise will just return an empty value (undefined). + pub fn as_i32_or_nothing(&self) -> Option { + use std::convert::TryFrom; + i32::try_from(self.0).ok() + } + + /// Returns the underlying value converted to i32 if possible (within limits) + /// JsError in case of out of boundary overflow + pub fn as_i32_or_fail(&self) -> Result { + use std::convert::TryFrom; + i32::try_from(self.0).map_err(|e| JsError::from_str(&format!("{}", e))) + } + + /// Returns string representation of the underlying i128 value directly. + /// Might contain the minus sign (-) in case of negative value. + pub fn to_str(&self) -> String { + format!("{}", self.0) + } + + // Create an Int from a standard rust string representation + pub fn from_str(string: &str) -> Result { + let x = string + .parse::() + .map_err(|e| JsError::from_str(&format! {"{:?}", e}))?; + if x.abs() > u64::MAX as i128 { + return Err(JsError::from_str(&format!( + "{} out of bounds. Value (without sign) must fit within 4 bytes limit of {}", + x, + u64::MAX + ))); + } + Ok(Self(x)) + } +} + +impl serde::Serialize for Int { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&self.to_str()) + } +} + +impl<'de> serde::de::Deserialize<'de> for Int { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + Self::from_str(&s).map_err(|_e| { + serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&s), + &"string rep of a number", + ) + }) + } +} + +impl JsonSchema for Int { + fn schema_name() -> String { + String::from("Int") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } + fn is_referenceable() -> bool { + String::is_referenceable() + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/numeric/mod.rs b/rust/src/protocol_types/numeric/mod.rs new file mode 100644 index 00000000..f0585957 --- /dev/null +++ b/rust/src/protocol_types/numeric/mod.rs @@ -0,0 +1,8 @@ +mod big_int; +pub use big_int::*; + +mod int; +pub use int::*; + +mod big_num; +pub use big_num::*; \ No newline at end of file diff --git a/rust/src/protocol_types/plutus/cost_model.rs b/rust/src/protocol_types/plutus/cost_model.rs new file mode 100644 index 00000000..8b28ea4a --- /dev/null +++ b/rust/src/protocol_types/plutus/cost_model.rs @@ -0,0 +1,63 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct CostModel(pub(crate) Vec); + +impl_to_from!(CostModel); + +#[wasm_bindgen] +impl CostModel { + /// Creates a new CostModels instance of an unrestricted length + pub fn new() -> Self { + Self(Vec::new()) + } + + /// Sets the cost at the specified index to the specified value. + /// In case the operation index is larger than the previous largest used index, + /// it will fill any inbetween indexes with zeroes + pub fn set(&mut self, operation: usize, cost: &Int) -> Result { + let len = self.0.len(); + let idx = operation.clone(); + if idx >= len { + for _ in 0..(idx - len + 1) { + self.0.push(Int::new_i32(0)); + } + } + let old = self.0[idx].clone(); + self.0[idx] = cost.clone(); + Ok(old) + } + + pub fn get(&self, operation: usize) -> Result { + let max = self.0.len(); + if operation >= max { + return Err(JsError::from_str(&format!( + "CostModel operation {} out of bounds. Max is {}", + operation, max + ))); + } + Ok(self.0[operation].clone()) + } + + pub fn len(&self) -> usize { + self.0.len() + } +} + +impl From> for CostModel { + fn from(values: Vec) -> Self { + CostModel(values.iter().map(|x| Int(*x)).collect()) + } +} diff --git a/rust/src/protocol_types/plutus/cost_models.rs b/rust/src/protocol_types/plutus/cost_models.rs new file mode 100644 index 00000000..8b63cc15 --- /dev/null +++ b/rust/src/protocol_types/plutus/cost_models.rs @@ -0,0 +1,100 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct Costmdls(pub(crate) std::collections::BTreeMap); + +impl_to_from!(Costmdls); + +#[wasm_bindgen] +impl Costmdls { + pub fn new() -> Self { + Self(std::collections::BTreeMap::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn insert(&mut self, key: &Language, value: &CostModel) -> Option { + self.0.insert(key.clone(), value.clone()) + } + + pub fn get(&self, key: &Language) -> Option { + self.0.get(key).map(|v| v.clone()) + } + + pub fn keys(&self) -> Languages { + Languages(self.0.iter().map(|(k, _v)| k.clone()).collect::>()) + } + + pub(crate) fn language_views_encoding(&self) -> Vec { + let mut serializer = Serializer::new_vec(); + fn key_len(l: &Language) -> usize { + if l.kind() == LanguageKind::PlutusV1 { + let mut serializer = Serializer::new_vec(); + serializer.write_bytes(l.to_bytes()).unwrap(); + return serializer.finalize().len(); + } + l.to_bytes().len() + } + let mut keys: Vec = self.0.iter().map(|(k, _v)| k.clone()).collect(); + // keys must be in canonical ordering first + keys.sort_by(|lhs, rhs| match key_len(lhs).cmp(&key_len(rhs)) { + std::cmp::Ordering::Equal => lhs.cmp(&rhs), + len_order => len_order, + }); + serializer + .write_map(cbor_event::Len::Len(self.0.len() as u64)) + .unwrap(); + for key in keys.iter() { + if key.kind() == LanguageKind::PlutusV1 { + serializer.write_bytes(key.to_bytes()).unwrap(); + let cost_model = self.0.get(&key).unwrap(); + // Due to a bug in the cardano-node input-output-hk/cardano-ledger-specs/issues/2512 + // we must use indefinite length serialization in this inner bytestring to match it + let mut cost_model_serializer = Serializer::new_vec(); + cost_model_serializer + .write_array(cbor_event::Len::Indefinite) + .unwrap(); + for cost in &cost_model.0 { + cost.serialize(&mut cost_model_serializer).unwrap(); + } + cost_model_serializer + .write_special(cbor_event::Special::Break) + .unwrap(); + serializer + .write_bytes(cost_model_serializer.finalize()) + .unwrap(); + } else { + serializer.serialize(key).unwrap(); + serializer.serialize(self.0.get(&key).unwrap()).unwrap(); + } + } + serializer.finalize() + } + + pub fn retain_language_versions(&self, languages: &Languages) -> Costmdls { + let mut result = Costmdls::new(); + for lang in &languages.0 { + match self.get(&lang) { + Some(costmodel) => { + result.insert(&lang, &costmodel); + } + _ => {} + } + } + result + } +} diff --git a/rust/src/protocol_types/plutus/ex_unit_prices.rs b/rust/src/protocol_types/plutus/ex_unit_prices.rs new file mode 100644 index 00000000..d0efb4fd --- /dev/null +++ b/rust/src/protocol_types/plutus/ex_unit_prices.rs @@ -0,0 +1,39 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( +Clone, +Debug, +Hash, +Eq, +Ord, +PartialEq, +PartialOrd, +serde::Serialize, +serde::Deserialize, +JsonSchema, +)] +pub struct ExUnitPrices { + pub(crate) mem_price: SubCoin, + pub(crate) step_price: SubCoin, +} + +impl_to_from!(ExUnitPrices); + +#[wasm_bindgen] +impl ExUnitPrices { + pub fn mem_price(&self) -> SubCoin { + self.mem_price.clone() + } + + pub fn step_price(&self) -> SubCoin { + self.step_price.clone() + } + + pub fn new(mem_price: &SubCoin, step_price: &SubCoin) -> Self { + Self { + mem_price: mem_price.clone(), + step_price: step_price.clone(), + } + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/plutus/ex_units.rs b/rust/src/protocol_types/plutus/ex_units.rs new file mode 100644 index 00000000..e892ef12 --- /dev/null +++ b/rust/src/protocol_types/plutus/ex_units.rs @@ -0,0 +1,39 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct ExUnits { + pub(crate) mem: BigNum, + pub(crate) steps: BigNum, +} + +impl_to_from!(ExUnits); + +#[wasm_bindgen] +impl ExUnits { + pub fn mem(&self) -> BigNum { + self.mem.clone() + } + + pub fn steps(&self) -> BigNum { + self.steps.clone() + } + + pub fn new(mem: &BigNum, steps: &BigNum) -> Self { + Self { + mem: mem.clone(), + steps: steps.clone(), + } + } +} diff --git a/rust/src/protocol_types/plutus/language.rs b/rust/src/protocol_types/plutus/language.rs new file mode 100644 index 00000000..b09fa0ed --- /dev/null +++ b/rust/src/protocol_types/plutus/language.rs @@ -0,0 +1,69 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, + Copy, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub enum LanguageKind { + PlutusV1 = 0, + PlutusV2 = 1, + PlutusV3 = 2, +} + +impl LanguageKind { + pub(crate) fn from_u64(x: u64) -> Option { + match x { + 0 => Some(LanguageKind::PlutusV1), + 1 => Some(LanguageKind::PlutusV2), + 2 => Some(LanguageKind::PlutusV3), + _ => None, + } + } +} + +#[wasm_bindgen] +#[derive( + Clone, + Copy, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct Language(pub(crate) LanguageKind); + +impl_to_from!(Language); + +#[wasm_bindgen] +impl Language { + pub fn new_plutus_v1() -> Self { + Self(LanguageKind::PlutusV1) + } + + pub fn new_plutus_v2() -> Self { + Self(LanguageKind::PlutusV2) + } + + pub fn new_plutus_v3() -> Self { + Self(LanguageKind::PlutusV3) + } + + pub fn kind(&self) -> LanguageKind { + self.0.clone() + } +} diff --git a/rust/src/protocol_types/plutus/languages.rs b/rust/src/protocol_types/plutus/languages.rs new file mode 100644 index 00000000..57f5967f --- /dev/null +++ b/rust/src/protocol_types/plutus/languages.rs @@ -0,0 +1,30 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( +Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub struct Languages(pub(crate) Vec); + +#[wasm_bindgen] +impl Languages { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> Language { + self.0[index] + } + + pub fn add(&mut self, elem: Language) { + self.0.push(elem); + } + + pub fn list() -> Languages { + Languages(vec![Language::new_plutus_v1(), Language::new_plutus_v2()]) + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/plutus/mod.rs b/rust/src/protocol_types/plutus/mod.rs new file mode 100644 index 00000000..8d347e02 --- /dev/null +++ b/rust/src/protocol_types/plutus/mod.rs @@ -0,0 +1,37 @@ +mod plutus_script; +pub use plutus_script::*; + +mod language; +pub use language::*; + +mod languages; +pub use languages::*; +mod plutus_scripts; +pub use plutus_scripts::*; + +mod cost_model; +pub use cost_model::*; + +mod cost_models; +pub use cost_models::*; + +mod ex_unit_prices; +pub use ex_unit_prices::*; + +mod ex_units; +pub use ex_units::*; + +mod redeemer; +pub use redeemer::*; + +mod redeemer_tag; +pub use redeemer_tag::*; + +mod redeemers; +pub use redeemers::*; + +mod strings; +pub use strings::*; + +mod plutus_data; +pub use plutus_data::*; diff --git a/rust/src/protocol_types/plutus/plutus_data.rs b/rust/src/protocol_types/plutus/plutus_data.rs new file mode 100644 index 00000000..fcc4f97a --- /dev/null +++ b/rust/src/protocol_types/plutus/plutus_data.rs @@ -0,0 +1,853 @@ +use crate::*; +use core::hash::Hasher; +use hashlink::LinkedHashMap; +use std::hash::Hash; + +use cbor_event::{ + self, + de::Deserializer, + se::{Serialize, Serializer}, +}; + +use schemars::JsonSchema; +use serde_json::Number; + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] +pub struct ConstrPlutusData { + pub(crate) alternative: BigNum, + pub(crate) data: PlutusList, +} + +to_from_bytes!(ConstrPlutusData); + +#[wasm_bindgen] +impl ConstrPlutusData { + pub fn alternative(&self) -> BigNum { + self.alternative.clone() + } + + pub fn data(&self) -> PlutusList { + self.data.clone() + } + + pub fn new(alternative: &BigNum, data: &PlutusList) -> Self { + Self { + alternative: alternative.clone(), + data: data.clone(), + } + } +} + +impl ConstrPlutusData { + // see: https://github.com/input-output-hk/plutus/blob/1f31e640e8a258185db01fa899da63f9018c0e85/plutus-core/plutus-core/src/PlutusCore/Data.hs#L61 + // We don't directly serialize the alternative in the tag, instead the scheme is: + // - Alternatives 0-6 -> tags 121-127, followed by the arguments in a list + // - Alternatives 7-127 -> tags 1280-1400, followed by the arguments in a list + // - Any alternatives, including those that don't fit in the above -> tag 102 followed by a list containing + // an unsigned integer for the actual alternative, and then the arguments in a (nested!) list. + pub(crate) const GENERAL_FORM_TAG: u64 = 102; + + // None -> needs general tag serialization, not compact + pub(crate) fn alternative_to_compact_cbor_tag(alt: u64) -> Option { + if alt <= 6 { + Some(121 + alt) + } else if alt >= 7 && alt <= 127 { + Some(1280 - 7 + alt) + } else { + None + } + } + + // None -> General tag(=102) OR Invalid CBOR tag for this scheme + pub(crate) fn compact_cbor_tag_to_alternative(cbor_tag: u64) -> Option { + if cbor_tag >= 121 && cbor_tag <= 127 { + Some(cbor_tag - 121) + } else if cbor_tag >= 1280 && cbor_tag <= 1400 { + Some(cbor_tag - 1280 + 7) + } else { + None + } + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, serde::Serialize, serde::Deserialize)] +pub struct PlutusMapValues { + pub(crate) elems: Vec, +} + +#[wasm_bindgen] +impl PlutusMapValues { + pub fn new() -> Self { + Self { elems: Vec::new() } + } + + pub fn len(&self) -> usize { + self.elems.len() + } + + pub fn get(&self, index: usize) -> Option { + self.elems.get(index).cloned() + } + + pub fn add(&mut self, elem: &PlutusData) { + self.elems.push(elem.clone()); + } + + pub(crate) fn add_move(&mut self, elem: PlutusData) { + self.elems.push(elem); + } +} + + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] +pub struct PlutusMap(pub(crate) LinkedHashMap); + +to_from_bytes!(PlutusMap); + +#[wasm_bindgen] +impl PlutusMap { + pub fn new() -> Self { + Self(LinkedHashMap::new()) + } + + + /// Return count ok different keys in the map. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns the previous value associated with the key, if any. + /// Replace the values associated with the key. + pub fn insert(&mut self, key: &PlutusData, values: &PlutusMapValues) -> Option { + self.0.insert(key.clone(), values.clone()) + } + + pub fn get(&self, key: &PlutusData) -> Option { + self.0.get(key).map(|v| v.clone()) + } + + pub fn keys(&self) -> PlutusList { + PlutusList { + elems: self.0.iter().map(|(k, _v)| k.clone()).collect::>(), + definite_encoding: None, + } + } + + /// Adds a value to the list of values associated with the key. + pub(crate) fn add_value(&mut self, key: &PlutusData, value: &PlutusData) { + let values = self.0 + .entry(key.clone()) + .or_insert_with(PlutusMapValues::new); + values.add(value); + } + + pub(crate) fn add_value_move(&mut self, key: PlutusData, value: PlutusData) { + let values = self.0 + .entry(key) + .or_insert_with(PlutusMapValues::new); + values.add_move(value); + } + + pub(crate) fn total_len(&self) -> usize { + self.0.iter().map(|(_k, v)| v.len()).sum() + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum PlutusDataKind { + ConstrPlutusData, + Map, + List, + Integer, + Bytes, +} + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] +pub enum PlutusDataEnum { + ConstrPlutusData(ConstrPlutusData), + Map(PlutusMap), + List(PlutusList), + Integer(BigInt), + Bytes(Vec), +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Ord, PartialOrd)] +pub struct PlutusData { + pub(crate) datum: PlutusDataEnum, + // We should always preserve the original datums when deserialized as this is NOT canonicized + // before computing datum hashes. So this field stores the original bytes to re-use. + pub(crate) original_bytes: Option>, +} + +impl std::cmp::PartialEq for PlutusData { + fn eq(&self, other: &Self) -> bool { + self.datum.eq(&other.datum) + } +} + +impl Hash for PlutusData { + fn hash(&self, state: &mut H) { + self.datum.hash(state) + } +} + +impl std::cmp::Eq for PlutusData {} + +to_from_bytes!(PlutusData); + +#[wasm_bindgen] +impl PlutusData { + pub fn new_constr_plutus_data(constr_plutus_data: &ConstrPlutusData) -> Self { + Self { + datum: PlutusDataEnum::ConstrPlutusData(constr_plutus_data.clone()), + original_bytes: None, + } + } + + /// Same as `.new_constr_plutus_data` but creates constr with empty data list + pub fn new_empty_constr_plutus_data(alternative: &BigNum) -> Self { + Self::new_constr_plutus_data(&ConstrPlutusData::new(alternative, &PlutusList::new())) + } + + pub fn new_single_value_constr_plutus_data( + alternative: &BigNum, + plutus_data: &PlutusData, + ) -> Self { + let mut list = PlutusList::new(); + list.add(plutus_data); + Self::new_constr_plutus_data(&ConstrPlutusData::new(alternative, &list)) + } + + pub fn new_map(map: &PlutusMap) -> Self { + Self { + datum: PlutusDataEnum::Map(map.clone()), + original_bytes: None, + } + } + + pub fn new_list(list: &PlutusList) -> Self { + Self { + datum: PlutusDataEnum::List(list.clone()), + original_bytes: None, + } + } + + pub fn new_integer(integer: &BigInt) -> Self { + Self { + datum: PlutusDataEnum::Integer(integer.clone()), + original_bytes: None, + } + } + + pub fn new_bytes(bytes: Vec) -> Self { + Self { + datum: PlutusDataEnum::Bytes(bytes), + original_bytes: None, + } + } + + pub fn kind(&self) -> PlutusDataKind { + match &self.datum { + PlutusDataEnum::ConstrPlutusData(_) => PlutusDataKind::ConstrPlutusData, + PlutusDataEnum::Map(_) => PlutusDataKind::Map, + PlutusDataEnum::List(_) => PlutusDataKind::List, + PlutusDataEnum::Integer(_) => PlutusDataKind::Integer, + PlutusDataEnum::Bytes(_) => PlutusDataKind::Bytes, + } + } + + pub fn as_constr_plutus_data(&self) -> Option { + match &self.datum { + PlutusDataEnum::ConstrPlutusData(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_map(&self) -> Option { + match &self.datum { + PlutusDataEnum::Map(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_list(&self) -> Option { + match &self.datum { + PlutusDataEnum::List(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_integer(&self) -> Option { + match &self.datum { + PlutusDataEnum::Integer(x) => Some(x.clone()), + _ => None, + } + } + + pub fn as_bytes(&self) -> Option> { + match &self.datum { + PlutusDataEnum::Bytes(x) => Some(x.clone()), + _ => None, + } + } + + pub fn to_json(&self, schema: PlutusDatumSchema) -> Result { + decode_plutus_datum_to_json_str(self, schema) + } + + pub fn from_json(json: &str, schema: PlutusDatumSchema) -> Result { + encode_json_str_to_plutus_datum(json, schema) + } + + pub fn from_address(address: &Address) -> Result { + let payment_cred = match &address.0 { + AddrType::Base(addr) => Ok(addr.payment_cred()), + AddrType::Enterprise(addr) => Ok(addr.payment_cred()), + AddrType::Ptr(addr) => Ok(addr.payment_cred()), + AddrType::Reward(addr) => Ok(addr.payment_cred()), + AddrType::Byron(_) => Err(JsError::from_str( + "Cannot convert Byron address to PlutusData", + )), + AddrType::Malformed(_) => Err(JsError::from_str( + "Cannot convert Malformed address to PlutusData", + )) + }?; + + let staking_data = match &address.0 { + AddrType::Base(addr) => { + let staking_bytes_data = PlutusData::from_stake_credential(&addr.stake_cred())?; + Some(PlutusData::new_single_value_constr_plutus_data( + &BigNum::from(0u32), + &staking_bytes_data, + )) + } + _ => None, + }; + + let pointer_data = match &address.0 { + AddrType::Ptr(addr) => Some(PlutusData::from_pointer(&addr.stake_pointer())?), + _ => None, + }; + + let payment_data = PlutusData::from_stake_credential(&payment_cred)?; + let staking_optional_data = match (staking_data, pointer_data) { + (Some(_), Some(_)) => Err(JsError::from_str( + "Address can't have both staking and pointer data", + )), + (Some(staking_data), None) => Ok(Some(staking_data)), + (None, Some(pointer_data)) => Ok(Some(pointer_data)), + (None, None) => Ok(None), + }?; + + let mut data_list = PlutusList::new(); + data_list.add(&payment_data); + if let Some(staking_optional_data) = staking_optional_data { + data_list.add(&PlutusData::new_single_value_constr_plutus_data( + &BigNum::from(0u32), + &staking_optional_data, + )); + } else { + data_list.add(&PlutusData::new_empty_constr_plutus_data(&BigNum::from( + 1u32, + ))); + } + + Ok(PlutusData::new_constr_plutus_data(&ConstrPlutusData::new( + &BigNum::from(0u32), + &data_list, + ))) + } + + fn from_stake_credential(stake_credential: &Credential) -> Result { + let (bytes_plutus_data, index) = match &stake_credential.0 { + CredType::Key(key_hash) => ( + PlutusData::new_bytes(key_hash.to_bytes().to_vec()), + BigNum::from(0u32), + ), + CredType::Script(script_hash) => ( + PlutusData::new_bytes(script_hash.to_bytes().to_vec()), + BigNum::from(1u32), + ), + }; + + Ok(PlutusData::new_single_value_constr_plutus_data( + &index, + &bytes_plutus_data, + )) + } + + fn from_pointer(pointer: &Pointer) -> Result { + let mut data_list = PlutusList::new(); + data_list.add(&PlutusData::new_integer(&pointer.slot_bignum().into())); + data_list.add(&PlutusData::new_integer(&pointer.tx_index_bignum().into())); + data_list.add(&PlutusData::new_integer( + &pointer.cert_index_bignum().into(), + )); + + Ok(PlutusData::new_constr_plutus_data(&ConstrPlutusData::new( + &BigNum::from(1u32), + &data_list, + ))) + } +} + +//TODO: replace this by cardano-node schemas +impl JsonSchema for PlutusData { + fn is_referenceable() -> bool { + String::is_referenceable() + } + + fn schema_name() -> String { + String::from("PlutusData") + } + + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } +} + +//TODO: need to figure out what schema to use here +impl serde::Serialize for PlutusData { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let json = decode_plutus_datum_to_json_str(self, PlutusDatumSchema::DetailedSchema) + .map_err(|ser_err| { + serde::ser::Error::custom(&format!("Serialization error: {:?}", ser_err)) + })?; + serializer.serialize_str(&json) + } +} + +impl<'de> serde::de::Deserialize<'de> for PlutusData { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let datum_json = ::deserialize(deserializer)?; + encode_json_str_to_plutus_datum(&datum_json, PlutusDatumSchema::DetailedSchema).map_err( + |ser_err| serde::de::Error::custom(&format!("Deserialization error: {:?}", ser_err)), + ) + } +} + +#[wasm_bindgen] +#[derive(Clone, Debug, Ord, PartialOrd, Hash, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct PlutusList { + pub(crate) elems: Vec, + // We should always preserve the original datums when deserialized as this is NOT canonicized + // before computing datum hashes. This field will default to cardano-cli behavior if None + // and will re-use the provided one if deserialized, unless the list is modified. + pub(crate) definite_encoding: Option, +} + +impl NoneOrEmpty for PlutusList { + fn is_none_or_empty(&self) -> bool { + self.elems.is_empty() + } +} + +impl<'a> IntoIterator for &'a PlutusList { + type Item = &'a PlutusData; + type IntoIter = std::slice::Iter<'a, PlutusData>; + + fn into_iter(self) -> std::slice::Iter<'a, PlutusData> { + self.elems.iter() + } +} + +impl std::cmp::PartialEq for PlutusList { + fn eq(&self, other: &Self) -> bool { + self.elems.eq(&other.elems) + } +} + +impl std::cmp::Eq for PlutusList {} + +to_from_bytes!(PlutusList); + +#[wasm_bindgen] +impl PlutusList { + pub fn new() -> Self { + Self { + elems: Vec::new(), + definite_encoding: None, + } + } + + pub fn len(&self) -> usize { + self.elems.len() + } + + pub fn get(&self, index: usize) -> PlutusData { + self.elems[index].clone() + } + + pub fn add(&mut self, elem: &PlutusData) { + self.elems.push(elem.clone()); + self.definite_encoding = None; + } + + #[allow(dead_code)] + pub(crate) fn contains(&self, elem: &PlutusData) -> bool { + self.elems.contains(elem) + } + + pub(crate) fn deduplicated_view(&self) -> Vec<&PlutusData> { + let mut dedup = BTreeSet::new(); + let mut keyhashes = Vec::new(); + for elem in &self.elems { + if dedup.insert(elem) { + keyhashes.push(elem); + } + } + keyhashes + } + + pub(crate) fn to_set_bytes(&self) -> Vec { + let mut buf = Serializer::new_vec(); + self.serialize_as_set(true, &mut buf).unwrap(); + buf.finalize() + } + + pub(crate) fn deduplicated_clone(&self) -> Self { + let mut dedup = BTreeSet::new(); + let mut elems = Vec::new(); + for elem in &self.elems { + if dedup.insert(elem) { + elems.push(elem.clone()); + } + } + Self { + elems, + definite_encoding: self.definite_encoding, + } + } + + pub(crate) fn extend(&mut self, other: &PlutusList) { + self.elems.extend(other.elems.iter().cloned()); + } +} + +impl From> for PlutusList { + fn from(elems: Vec) -> Self { + Self { + elems, + definite_encoding: None, + } + } +} + +// json + +/// JSON <-> PlutusData conversion schemas. +/// Follows ScriptDataJsonSchema in cardano-cli defined at: +/// https://github.com/input-output-hk/cardano-node/blob/master/cardano-api/src/Cardano/Api/ScriptData.hs#L254 +/// +/// All methods here have the following restrictions due to limitations on dependencies: +/// * JSON numbers above u64::MAX (positive) or below i64::MIN (negative) will throw errors +/// * Hex strings for bytes don't accept odd-length (half-byte) strings. +/// cardano-cli seems to support these however but it seems to be different than just 0-padding +/// on either side when tested so proceed with caution +#[wasm_bindgen] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum PlutusDatumSchema { + /// ScriptDataJsonNoSchema in cardano-node. + /// + /// This is the format used by --script-data-value in cardano-cli + /// This tries to accept most JSON but does not support the full spectrum of Plutus datums. + /// From JSON: + /// * null/true/false/floats NOT supported + /// * strings starting with 0x are treated as hex bytes. All other strings are encoded as their utf8 bytes. + /// To JSON: + /// * ConstrPlutusData not supported in ANY FORM (neither keys nor values) + /// * Lists not supported in keys + /// * Maps not supported in keys + //// + BasicConversions, + /// ScriptDataJsonDetailedSchema in cardano-node. + /// + /// This is the format used by --script-data-file in cardano-cli + /// This covers almost all (only minor exceptions) Plutus datums, but the JSON must conform to a strict schema. + /// The schema specifies that ALL keys and ALL values must be contained in a JSON map with 2 cases: + /// 1. For ConstrPlutusData there must be two fields "constructor" contianing a number and "fields" containing its fields + /// e.g. { "constructor": 2, "fields": [{"int": 2}, {"list": [{"bytes": "CAFEF00D"}]}]} + /// 2. For all other cases there must be only one field named "int", "bytes", "list" or "map" + /// Integer's value is a JSON number e.g. {"int": 100} + /// Bytes' value is a hex string representing the bytes WITHOUT any prefix e.g. {"bytes": "CAFEF00D"} + /// Lists' value is a JSON list of its elements encoded via the same schema e.g. {"list": [{"bytes": "CAFEF00D"}]} + /// Maps' value is a JSON list of objects, one for each key-value pair in the map, with keys "k" and "v" + /// respectively with their values being the plutus datum encoded via this same schema + /// e.g. {"map": [ + /// {"k": {"int": 2}, "v": {"int": 5}}, + /// {"k": {"map": [{"k": {"list": [{"int": 1}]}, "v": {"bytes": "FF03"}}]}, "v": {"list": []}} + /// ]} + /// From JSON: + /// * null/true/false/floats NOT supported + /// * the JSON must conform to a very specific schema + /// To JSON: + /// * all Plutus datums should be fully supported outside of the integer range limitations outlined above. + //// + DetailedSchema, +} + +#[wasm_bindgen] +pub fn encode_json_str_to_plutus_datum( + json: &str, + schema: PlutusDatumSchema, +) -> Result { + let value = serde_json::from_str(json).map_err(|e| JsError::from_str(&e.to_string()))?; + encode_json_value_to_plutus_datum(value, schema) +} + +pub fn encode_json_value_to_plutus_datum( + value: serde_json::Value, + schema: PlutusDatumSchema, +) -> Result { + use serde_json::Value; + fn encode_number(x: Number) -> Result { + if let Ok(big_int) = BigInt::from_str(x.as_str()) { + Ok(PlutusData::new_integer(&big_int)) + } else { + Err(JsError::from_str(&format!("Expected an integer value but got \"{}\"", x))) + } + } + fn encode_string( + s: &str, + schema: PlutusDatumSchema, + is_key: bool, + ) -> Result { + if schema == PlutusDatumSchema::BasicConversions { + if s.starts_with("0x") { + // this must be a valid hex bytestring after + hex::decode(&s[2..]) + .map(|bytes| PlutusData::new_bytes(bytes)) + .map_err(|err| JsError::from_str(&format!("Error decoding {}: {}", s, err))) + } else if is_key { + // try as an integer + BigInt::from_str(s) + .map(|x| PlutusData::new_integer(&x)) + // if not, we use the utf8 bytes of the string instead directly + .or_else(|_err| Ok(PlutusData::new_bytes(s.as_bytes().to_vec()))) + } else { + // can only be UTF bytes if not in a key and not prefixed by 0x + Ok(PlutusData::new_bytes(s.as_bytes().to_vec())) + } + } else { + if s.starts_with("0x") { + Err(JsError::from_str("Hex byte strings in detailed schema should NOT start with 0x and should just contain the hex characters")) + } else { + hex::decode(s) + .map(|bytes| PlutusData::new_bytes(bytes)) + .map_err(|e| JsError::from_str(&e.to_string())) + } + } + } + fn encode_array( + json_arr: Vec, + schema: PlutusDatumSchema, + ) -> Result { + let mut arr = PlutusList::new(); + for value in json_arr { + arr.add(&encode_json_value_to_plutus_datum(value, schema)?); + } + Ok(PlutusData::new_list(&arr)) + } + match schema { + PlutusDatumSchema::BasicConversions => match value { + Value::Null => Err(JsError::from_str("null not allowed in plutus datums")), + Value::Bool(_) => Err(JsError::from_str("bools not allowed in plutus datums")), + Value::Number(x) => encode_number(x), + // no strings in plutus so it's all bytes (as hex or utf8 printable) + Value::String(s) => encode_string(&s, schema, false), + Value::Array(json_arr) => encode_array(json_arr, schema), + Value::Object(json_obj) => { + let mut map = PlutusMap::new(); + for (raw_key, raw_value) in json_obj { + let key = encode_string(&raw_key, schema, true)?; + let value = encode_json_value_to_plutus_datum(raw_value, schema)?; + map.add_value(&key, &value); + } + Ok(PlutusData::new_map(&map)) + } + }, + PlutusDatumSchema::DetailedSchema => match value { + Value::Object(obj) => { + if obj.len() == 1 { + // all variants except tagged constructors + let (k, v) = obj.into_iter().next().unwrap(); + fn tag_mismatch() -> JsError { + JsError::from_str("key does not match type") + } + match k.as_str() { + "int" => match v { + Value::Number(x) => encode_number(x), + _ => Err(tag_mismatch()), + }, + "bytes" => { + encode_string(v.as_str().ok_or_else(tag_mismatch)?, schema, false) + } + "list" => { + encode_array(v.as_array().ok_or_else(tag_mismatch)?.clone(), schema) + } + "map" => { + let mut map = PlutusMap::new(); + fn map_entry_err() -> JsError { + JsError::from_str("entry format in detailed schema map object not correct. Needs to be of form {\"k\": {\"key_type\": key}, \"v\": {\"value_type\", value}}") + } + for entry in v.as_array().ok_or_else(tag_mismatch)? { + let entry_obj = entry.as_object().ok_or_else(map_entry_err)?; + let raw_key = entry_obj.get("k").ok_or_else(map_entry_err)?; + let value = entry_obj.get("v").ok_or_else(map_entry_err)?; + let key = + encode_json_value_to_plutus_datum(raw_key.clone(), schema)?; + map.add_value( + &key, + &encode_json_value_to_plutus_datum(value.clone(), schema)?, + ); + } + Ok(PlutusData::new_map(&map)) + } + invalid_key => Err(JsError::from_str(&format!( + "key '{}' in tagged object not valid", + invalid_key + ))), + } + } else { + // constructor with tagged variant + if obj.len() != 2 { + return Err(JsError::from_str("detailed schemas must either have only one of the following keys: \"int\", \"bytes\", \"list\" or \"map\", or both of these 2 keys: \"constructor\" + \"fields\"")); + } + let variant: BigNum = obj + .get("constructor") + .and_then(|v| Some(v.as_u64()?.into())) + .ok_or_else(|| JsError::from_str("tagged constructors must contain an unsigned integer called \"constructor\""))?; + let fields_json = + obj.get("fields") + .and_then(|f| f.as_array()) + .ok_or_else(|| { + JsError::from_str( + "tagged constructors must contian a list called \"fields\"", + ) + })?; + let mut fields = PlutusList::new(); + for field_json in fields_json { + let field = encode_json_value_to_plutus_datum(field_json.clone(), schema)?; + fields.add(&field); + } + Ok(PlutusData::new_constr_plutus_data(&ConstrPlutusData::new( + &variant, &fields, + ))) + } + } + _ => Err(JsError::from_str(&format!( + "DetailedSchema requires ALL JSON to be tagged objects, found: {}", + value + ))), + }, + } +} + +//TODO: move it to serialize impl +#[wasm_bindgen] +pub fn decode_plutus_datum_to_json_str( + datum: &PlutusData, + schema: PlutusDatumSchema, +) -> Result { + let value = decode_plutus_datum_to_json_value(datum, schema)?; + serde_json::to_string(&value).map_err(|e| JsError::from_str(&e.to_string())) +} + +//TODO: move it to deserialize impl +pub fn decode_plutus_datum_to_json_value( + datum: &PlutusData, + schema: PlutusDatumSchema, +) -> Result { + use serde_json::Value; + let (type_tag, json_value) = match &datum.datum { + PlutusDataEnum::ConstrPlutusData(constr) => { + let mut obj = serde_json::map::Map::with_capacity(2); + obj.insert( + String::from("constructor"), + Value::from(constr.alternative.0) + ); + let mut fields = Vec::new(); + for field in constr.data.elems.iter() { + fields.push(decode_plutus_datum_to_json_value(field, schema)?); + } + obj.insert( + String::from("fields"), + Value::from(fields) + ); + (None, Value::from(obj)) + }, + PlutusDataEnum::Map(map) => match schema { + PlutusDatumSchema::BasicConversions => (None, Value::from(map.0.iter().map(|(key, values)| { + let json_key: String = match &key.datum { + PlutusDataEnum::ConstrPlutusData(_) => Err(JsError::from_str("plutus data constructors are not allowed as keys in this schema. Use DetailedSchema.")), + PlutusDataEnum::Map(_) => Err(JsError::from_str("plutus maps are not allowed as keys in this schema. Use DetailedSchema.")), + PlutusDataEnum::List(_) => Err(JsError::from_str("plutus lists are not allowed as keys in this schema. Use DetailedSchema.")), + PlutusDataEnum::Integer(x) => Ok(x.to_str()), + PlutusDataEnum::Bytes(bytes) => String::from_utf8(bytes.clone()).or_else(|_err| Ok(format!("0x{}", hex::encode(bytes)))) + }?; + if values.len() > 1 { + Err(JsError::from_str("plutus maps are not allowed to have more than one value per key in this schema. Use DetailedSchema.")) + } else if let Some(value) = values.get(0) { + let json_value = decode_plutus_datum_to_json_value(&value, schema)?; + Ok((json_key, Value::from(json_value))) + } else { + Err(JsError::from_str("plutus maps are not allowed to have empty values in this schema. Use DetailedSchema.")) + } + }).collect::, JsError>>()?)), + PlutusDatumSchema::DetailedSchema => { + let mut entries = Vec::new(); + for (key, values) in map.0.iter() { + for value in &values.elems { + let k = decode_plutus_datum_to_json_value(key, schema)?; + let v = decode_plutus_datum_to_json_value(value, schema)?; + let mut kv_obj = serde_json::map::Map::with_capacity(2); + kv_obj.insert(String::from("k"), k); + kv_obj.insert(String::from("v"), v); + entries.push(kv_obj); + } + } + (Some("map"), Value::from(entries)) + }, + }, + PlutusDataEnum::List(list) => { + let mut elems = Vec::new(); + for elem in list.elems.iter() { + elems.push(decode_plutus_datum_to_json_value(elem, schema)?); + } + (Some("list"), Value::from(elems)) + }, + PlutusDataEnum::Integer(bigint) => ( + Some("int"), + Value::Number(Number::from_string_unchecked(bigint.to_str())) + ), + PlutusDataEnum::Bytes(bytes) => (Some("bytes"), Value::from(match schema { + PlutusDatumSchema::BasicConversions => { + // cardano-cli converts to a string only if bytes are utf8 and all characters are printable + String::from_utf8(bytes.clone()) + .ok() + .filter(|utf8| utf8.chars().all(|c| !c.is_control())) + // otherwise we hex-encode the bytes with a 0x prefix + .unwrap_or_else(|| format!("0x{}", hex::encode(bytes))) + }, + PlutusDatumSchema::DetailedSchema => hex::encode(bytes), + })), + }; + if type_tag.is_none() || schema != PlutusDatumSchema::DetailedSchema { + Ok(json_value) + } else { + let mut wrapper = serde_json::map::Map::with_capacity(1); + wrapper.insert(String::from(type_tag.unwrap()), json_value); + Ok(Value::from(wrapper)) + } +} diff --git a/rust/src/protocol_types/plutus/plutus_script.rs b/rust/src/protocol_types/plutus/plutus_script.rs new file mode 100644 index 00000000..b3abc1bd --- /dev/null +++ b/rust/src/protocol_types/plutus/plutus_script.rs @@ -0,0 +1,154 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct PlutusScript { + pub(crate) bytes: Vec, + pub(crate) language: LanguageKind, +} + +to_from_bytes!(PlutusScript); + +#[wasm_bindgen] +impl PlutusScript { + /** + * Creates a new Plutus script from the RAW bytes of the compiled script. + * This does NOT include any CBOR encoding around these bytes (e.g. from "cborBytes" in cardano-cli) + * If you creating this from those you should use PlutusScript::from_bytes() instead. + */ + pub fn new(bytes: Vec) -> PlutusScript { + Self::new_with_version(bytes, &Language::new_plutus_v1()) + } + + /** + * Creates a new Plutus script from the RAW bytes of the compiled script. + * This does NOT include any CBOR encoding around these bytes (e.g. from "cborBytes" in cardano-cli) + * If you creating this from those you should use PlutusScript::from_bytes() instead. + */ + pub fn new_v2(bytes: Vec) -> PlutusScript { + Self::new_with_version(bytes, &Language::new_plutus_v2()) + } + + /** + * Creates a new Plutus script from the RAW bytes of the compiled script. + * This does NOT include any CBOR encoding around these bytes (e.g. from "cborBytes" in cardano-cli) + * If you creating this from those you should use PlutusScript::from_bytes() instead. + */ + pub fn new_v3(bytes: Vec) -> PlutusScript { + Self::new_with_version(bytes, &Language::new_plutus_v3()) + } + + /** + * Creates a new Plutus script from the RAW bytes of the compiled script. + * This does NOT include any CBOR encoding around these bytes (e.g. from "cborBytes" in cardano-cli) + * If you creating this from those you should use PlutusScript::from_bytes() instead. + */ + pub fn new_with_version(bytes: Vec, language: &Language) -> PlutusScript { + Self { + bytes, + language: language.0.clone(), + } + } + + /** + * The raw bytes of this compiled Plutus script. + * If you need "cborBytes" for cardano-cli use PlutusScript::to_bytes() instead. + */ + pub fn bytes(&self) -> Vec { + self.bytes.clone() + } + + /// Same as `.from_bytes` but will consider the script as requiring the Plutus Language V2 + pub fn from_bytes_v2(bytes: Vec) -> Result { + Self::from_bytes_with_version(bytes, &Language::new_plutus_v2()) + } + + /// Same as `.from_bytes` but will consider the script as requiring the Plutus Language V3 + pub fn from_bytes_v3(bytes: Vec) -> Result { + Self::from_bytes_with_version(bytes, &Language::new_plutus_v3()) + } + + /// Same as `.from_bytes` but will consider the script as requiring the specified language version + pub fn from_bytes_with_version( + bytes: Vec, + language: &Language, + ) -> Result { + Ok(Self::new_with_version( + Self::from_bytes(bytes)?.bytes, + language, + )) + } + + /// Same as .from_hex but will consider the script as requiring the specified language version + pub fn from_hex_with_version( + hex_str: &str, + language: &Language, + ) -> Result { + Ok(Self::new_with_version( + Self::from_hex(hex_str)?.bytes, + language, + )) + } + + pub fn hash(&self) -> ScriptHash { + let mut bytes = Vec::with_capacity(self.bytes.len() + 1); + // https://github.com/input-output-hk/cardano-ledger/blob/master/eras/babbage/test-suite/cddl-files/babbage.cddl#L413 + bytes.extend_from_slice(&vec![self.script_namespace() as u8]); + bytes.extend_from_slice(&self.bytes); + ScriptHash::from(blake2b224(bytes.as_ref())) + } + + pub fn language_version(&self) -> Language { + Language(self.language.clone()) + } + + pub(crate) fn script_namespace(&self) -> ScriptHashNamespace { + match self.language { + LanguageKind::PlutusV1 => ScriptHashNamespace::PlutusScript, + LanguageKind::PlutusV2 => ScriptHashNamespace::PlutusScriptV2, + LanguageKind::PlutusV3 => ScriptHashNamespace::PlutusScriptV3, + } + } + + pub(crate) fn clone_as_version(&self, language: &Language) -> PlutusScript { + Self::new_with_version(self.bytes.clone(), language) + } +} + +impl serde::Serialize for PlutusScript { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_str(&hex::encode(&self.bytes)) + } +} + +impl<'de> serde::de::Deserialize<'de> for PlutusScript { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + hex::decode(&s) + .map(|bytes| PlutusScript::new(bytes)) + .map_err(|_err| { + serde::de::Error::invalid_value( + serde::de::Unexpected::Str(&s), + &"PlutusScript as hex string e.g. F8AB28C2 (without CBOR bytes tag)", + ) + }) + } +} + +impl JsonSchema for PlutusScript { + fn schema_name() -> String { + String::from("PlutusScript") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + String::json_schema(gen) + } + fn is_referenceable() -> bool { + String::is_referenceable() + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/plutus/plutus_scripts.rs b/rust/src/protocol_types/plutus/plutus_scripts.rs new file mode 100644 index 00000000..4d6db98a --- /dev/null +++ b/rust/src/protocol_types/plutus/plutus_scripts.rs @@ -0,0 +1,100 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub struct PlutusScripts(pub(crate) Vec); + +impl_to_from!(PlutusScripts); + +impl NoneOrEmpty for PlutusScripts { + fn is_none_or_empty(&self) -> bool { + self.0.is_empty() + } +} + +#[wasm_bindgen] +impl PlutusScripts { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> PlutusScript { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &PlutusScript) { + self.0.push(elem.clone()); + } + + #[allow(dead_code)] + pub(crate) fn by_version(&self, language: &Language) -> PlutusScripts { + PlutusScripts( + self.0 + .iter() + .filter(|s| s.language_version().eq(language)) + .map(|s| s.clone()) + .collect(), + ) + } + + pub(crate) fn has_version(&self, language: &Language) -> bool { + self.0.iter().any(|s| s.language_version().eq(language)) + } + + pub(crate) fn merge(&self, other: &PlutusScripts) -> PlutusScripts { + let mut res = self.clone(); + for s in &other.0 { + res.add(s); + } + res + } + + pub(crate) fn view(&self, version: &Language) -> Vec<&PlutusScript> { + let mut res = Vec::new(); + for script in &self.0 { + if !script.language_version().eq(version) { + continue; + } + res.push(script); + } + res + } + + pub(crate) fn deduplicated_view(&self, version: Option<&Language>) -> Vec<&PlutusScript> { + let mut dedup = BTreeSet::new(); + let mut res = Vec::new(); + for script in &self.0 { + if let Some(version) = version { + if !script.language_version().eq(version) { + continue; + } + } + if dedup.insert(script) { + res.push(script); + } + } + res + } + + pub(crate) fn deduplicated_clone(&self) -> PlutusScripts { + let mut dedup = BTreeSet::new(); + let mut scripts = Vec::new(); + for script in &self.0 { + if dedup.insert(script.clone()) { + scripts.push(script.clone()); + } + } + PlutusScripts(scripts) + } + + #[allow(dead_code)] + pub(crate) fn contains(&self, script: &PlutusScript) -> bool { + self.0.contains(&script) + } +} diff --git a/rust/src/protocol_types/plutus/redeemer.rs b/rust/src/protocol_types/plutus/redeemer.rs new file mode 100644 index 00000000..f00fdaca --- /dev/null +++ b/rust/src/protocol_types/plutus/redeemer.rs @@ -0,0 +1,65 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub struct Redeemer { + pub(crate) tag: RedeemerTag, + pub(crate) index: BigNum, + pub(crate) data: PlutusData, + pub(crate) ex_units: ExUnits, +} + +impl_to_from!(Redeemer); + +#[wasm_bindgen] +impl Redeemer { + pub fn tag(&self) -> RedeemerTag { + self.tag.clone() + } + + pub fn index(&self) -> BigNum { + self.index.clone() + } + + pub fn data(&self) -> PlutusData { + self.data.clone() + } + + pub fn ex_units(&self) -> ExUnits { + self.ex_units.clone() + } + + pub fn new(tag: &RedeemerTag, index: &BigNum, data: &PlutusData, ex_units: &ExUnits) -> Self { + Self { + tag: tag.clone(), + index: index.clone(), + data: data.clone(), + ex_units: ex_units.clone(), + } + } + + #[allow(dead_code)] + pub(crate) fn clone_with_index(&self, index: &BigNum) -> Self { + Self { + tag: self.tag.clone(), + index: index.clone(), + data: self.data.clone(), + ex_units: self.ex_units.clone(), + } + } + + pub(crate) fn clone_with_index_and_tag(&self, index: &BigNum, tag: &RedeemerTag) -> Self { + Self { + tag: tag.clone(), + index: index.clone(), + data: self.data.clone(), + ex_units: self.ex_units.clone(), + } + } + + pub(crate) fn partially_eq(&self, other: &Redeemer) -> bool { + self.data == other.data && self.ex_units == other.ex_units + } +} diff --git a/rust/src/protocol_types/plutus/redeemer_tag.rs b/rust/src/protocol_types/plutus/redeemer_tag.rs new file mode 100644 index 00000000..6c544a92 --- /dev/null +++ b/rust/src/protocol_types/plutus/redeemer_tag.rs @@ -0,0 +1,72 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Copy, + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub enum RedeemerTagKind { + Spend, + Mint, + Cert, + Reward, + Vote, + VotingProposal, +} + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct RedeemerTag(pub(crate) RedeemerTagKind); + +impl_to_from!(RedeemerTag); + +#[wasm_bindgen] +impl RedeemerTag { + pub fn new_spend() -> Self { + Self(RedeemerTagKind::Spend) + } + + pub fn new_mint() -> Self { + Self(RedeemerTagKind::Mint) + } + + pub fn new_cert() -> Self { + Self(RedeemerTagKind::Cert) + } + + pub fn new_reward() -> Self { + Self(RedeemerTagKind::Reward) + } + + pub fn new_vote() -> Self { + Self(RedeemerTagKind::Vote) + } + + pub fn new_voting_proposal() -> Self { + Self(RedeemerTagKind::VotingProposal) + } + + pub fn kind(&self) -> RedeemerTagKind { + self.0 + } +} diff --git a/rust/src/protocol_types/plutus/redeemers.rs b/rust/src/protocol_types/plutus/redeemers.rs new file mode 100644 index 00000000..713f2d98 --- /dev/null +++ b/rust/src/protocol_types/plutus/redeemers.rs @@ -0,0 +1,112 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Debug, Ord, PartialOrd)] +pub struct Redeemers { + pub(crate) redeemers: Vec, + pub(crate) serialization_format: Option, +} + +impl_to_from!(Redeemers); + +#[wasm_bindgen] +impl Redeemers { + pub fn new() -> Self { + Self { + redeemers: Vec::new(), + serialization_format: None, + } + } + + #[allow(dead_code)] + pub(crate) fn new_with_serialization_format( + redeemers: Vec, + serialization_format: CborContainerType, + ) -> Self { + Self { + redeemers, + serialization_format: Some(serialization_format), + } + } + + pub fn len(&self) -> usize { + self.redeemers.len() + } + + pub fn get(&self, index: usize) -> Redeemer { + self.redeemers[index].clone() + } + + pub fn add(&mut self, elem: &Redeemer) { + self.redeemers.push(elem.clone()); + } + + pub fn total_ex_units(&self) -> Result { + let mut tot_mem = BigNum::zero(); + let mut tot_steps = BigNum::zero(); + for i in 0..self.redeemers.len() { + let r: &Redeemer = &self.redeemers[i]; + tot_mem = tot_mem.checked_add(&r.ex_units().mem())?; + tot_steps = tot_steps.checked_add(&r.ex_units().steps())?; + } + Ok(ExUnits::new(&tot_mem, &tot_steps)) + } +} + +impl NoneOrEmpty for Redeemers { + fn is_none_or_empty(&self) -> bool { + self.redeemers.is_empty() + } +} + +impl PartialEq for Redeemers { + fn eq(&self, other: &Redeemers) -> bool { + self.redeemers == other.redeemers + } + +} + +impl Eq for Redeemers {} + +impl From> for Redeemers { + fn from(values: Vec) -> Self { + Self { + redeemers: values, + serialization_format: None, + } + } +} + +impl serde::Serialize for Redeemers { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.redeemers.serialize(serializer) + } +} + +impl<'de> serde::de::Deserialize<'de> for Redeemers { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let vec = as serde::de::Deserialize>::deserialize(deserializer)?; + Ok(Self { + redeemers: vec, + serialization_format: None, + }) + } +} + +impl JsonSchema for Redeemers { + fn is_referenceable() -> bool { + Vec::::is_referenceable() + } + fn schema_name() -> String { + String::from("Redeemers") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + Vec::::json_schema(gen) + } +} diff --git a/rust/src/protocol_types/plutus/strings.rs b/rust/src/protocol_types/plutus/strings.rs new file mode 100644 index 00000000..843e545b --- /dev/null +++ b/rust/src/protocol_types/plutus/strings.rs @@ -0,0 +1,24 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct Strings(pub(crate) Vec); + +#[wasm_bindgen] +impl Strings { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> String { + self.0[index].clone() + } + + pub fn add(&mut self, elem: String) { + self.0.push(elem); + } +} diff --git a/rust/src/protocol_types/protocol_param_update.rs b/rust/src/protocol_types/protocol_param_update.rs new file mode 100644 index 00000000..cc38e608 --- /dev/null +++ b/rust/src/protocol_types/protocol_param_update.rs @@ -0,0 +1,569 @@ +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct PoolVotingThresholds { + pub(crate) motion_no_confidence: UnitInterval, + pub(crate) committee_normal: UnitInterval, + pub(crate) committee_no_confidence: UnitInterval, + pub(crate) hard_fork_initiation: UnitInterval, + pub(crate) security_relevant_threshold: UnitInterval, +} + +impl_to_from!(PoolVotingThresholds); + +#[wasm_bindgen] +impl PoolVotingThresholds { + pub fn new( + motion_no_confidence: &UnitInterval, + committee_normal: &UnitInterval, + committee_no_confidence: &UnitInterval, + hard_fork_initiation: &UnitInterval, + security_relevant_threshold: &UnitInterval, + ) -> Self { + Self { + motion_no_confidence: motion_no_confidence.clone(), + committee_normal: committee_normal.clone(), + committee_no_confidence: committee_no_confidence.clone(), + hard_fork_initiation: hard_fork_initiation.clone(), + security_relevant_threshold: security_relevant_threshold.clone(), + } + } + + pub fn motion_no_confidence(&self) -> UnitInterval { + self.motion_no_confidence.clone() + } + + pub fn committee_normal(&self) -> UnitInterval { + self.committee_normal.clone() + } + + pub fn committee_no_confidence(&self) -> UnitInterval { + self.committee_no_confidence.clone() + } + + pub fn hard_fork_initiation(&self) -> UnitInterval { + self.hard_fork_initiation.clone() + } + + pub fn security_relevant_threshold(&self) -> UnitInterval { + self.security_relevant_threshold.clone() + } +} + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + Default, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct DRepVotingThresholds { + pub(crate) motion_no_confidence: UnitInterval, + pub(crate) committee_normal: UnitInterval, + pub(crate) committee_no_confidence: UnitInterval, + pub(crate) update_constitution: UnitInterval, + pub(crate) hard_fork_initiation: UnitInterval, + pub(crate) pp_network_group: UnitInterval, + pub(crate) pp_economic_group: UnitInterval, + pub(crate) pp_technical_group: UnitInterval, + pub(crate) pp_governance_group: UnitInterval, + pub(crate) treasury_withdrawal: UnitInterval, +} + +impl_to_from!(DRepVotingThresholds); + +#[wasm_bindgen] +impl DRepVotingThresholds { + pub fn new( + motion_no_confidence: &UnitInterval, + committee_normal: &UnitInterval, + committee_no_confidence: &UnitInterval, + update_constitution: &UnitInterval, + hard_fork_initiation: &UnitInterval, + pp_network_group: &UnitInterval, + pp_economic_group: &UnitInterval, + pp_technical_group: &UnitInterval, + pp_governance_group: &UnitInterval, + treasury_withdrawal: &UnitInterval, + ) -> Self { + Self { + motion_no_confidence: motion_no_confidence.clone(), + committee_normal: committee_normal.clone(), + committee_no_confidence: committee_no_confidence.clone(), + update_constitution: update_constitution.clone(), + hard_fork_initiation: hard_fork_initiation.clone(), + pp_network_group: pp_network_group.clone(), + pp_economic_group: pp_economic_group.clone(), + pp_technical_group: pp_technical_group.clone(), + pp_governance_group: pp_governance_group.clone(), + treasury_withdrawal: treasury_withdrawal.clone(), + } + } + + pub fn set_motion_no_confidence(&mut self, motion_no_confidence: &UnitInterval) { + self.motion_no_confidence = motion_no_confidence.clone() + } + + pub fn set_committee_normal(&mut self, committee_normal: &UnitInterval) { + self.committee_normal = committee_normal.clone() + } + + pub fn set_committee_no_confidence(&mut self, committee_no_confidence: &UnitInterval) { + self.committee_no_confidence = committee_no_confidence.clone() + } + + pub fn set_update_constitution(&mut self, update_constitution: &UnitInterval) { + self.update_constitution = update_constitution.clone() + } + + pub fn set_hard_fork_initiation(&mut self, hard_fork_initiation: &UnitInterval) { + self.hard_fork_initiation = hard_fork_initiation.clone() + } + + pub fn set_pp_network_group(&mut self, pp_network_group: &UnitInterval) { + self.pp_network_group = pp_network_group.clone() + } + + pub fn set_pp_economic_group(&mut self, pp_economic_group: &UnitInterval) { + self.pp_economic_group = pp_economic_group.clone() + } + + pub fn set_pp_technical_group(&mut self, pp_technical_group: &UnitInterval) { + self.pp_technical_group = pp_technical_group.clone() + } + + pub fn set_pp_governance_group(&mut self, pp_governance_group: &UnitInterval) { + self.pp_governance_group = pp_governance_group.clone() + } + + pub fn set_treasury_withdrawal(&mut self, treasury_withdrawal: &UnitInterval) { + self.treasury_withdrawal = treasury_withdrawal.clone() + } + + pub fn motion_no_confidence(&self) -> UnitInterval { + self.motion_no_confidence.clone() + } + + pub fn committee_normal(&self) -> UnitInterval { + self.committee_normal.clone() + } + + pub fn committee_no_confidence(&self) -> UnitInterval { + self.committee_no_confidence.clone() + } + + pub fn update_constitution(&self) -> UnitInterval { + self.update_constitution.clone() + } + + pub fn hard_fork_initiation(&self) -> UnitInterval { + self.hard_fork_initiation.clone() + } + + pub fn pp_network_group(&self) -> UnitInterval { + self.pp_network_group.clone() + } + + pub fn pp_economic_group(&self) -> UnitInterval { + self.pp_economic_group.clone() + } + + pub fn pp_technical_group(&self) -> UnitInterval { + self.pp_technical_group.clone() + } + + pub fn pp_governance_group(&self) -> UnitInterval { + self.pp_governance_group.clone() + } + + pub fn treasury_withdrawal(&self) -> UnitInterval { + self.treasury_withdrawal.clone() + } +} + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Hash, + Eq, + Ord, + PartialEq, + PartialOrd, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct ProtocolParamUpdate { + pub(crate) minfee_a: Option, + pub(crate) minfee_b: Option, + pub(crate) max_block_body_size: Option, + pub(crate) max_tx_size: Option, + pub(crate) max_block_header_size: Option, + pub(crate) key_deposit: Option, + pub(crate) pool_deposit: Option, + pub(crate) max_epoch: Option, + // desired number of stake pools + pub(crate) n_opt: Option, + pub(crate) pool_pledge_influence: Option, + pub(crate) expansion_rate: Option, + pub(crate) treasury_growth_rate: Option, + // decentralization constant + pub(crate) d: Option, + pub(crate) extra_entropy: Option, + pub(crate) protocol_version: Option, + pub(crate) min_pool_cost: Option, + pub(crate) ada_per_utxo_byte: Option, + pub(crate) cost_models: Option, + pub(crate) execution_costs: Option, + pub(crate) max_tx_ex_units: Option, + pub(crate) max_block_ex_units: Option, + pub(crate) max_value_size: Option, + pub(crate) collateral_percentage: Option, + pub(crate) max_collateral_inputs: Option, + pub(crate) pool_voting_thresholds: Option, + pub(crate) drep_voting_thresholds: Option, + pub(crate) min_committee_size: Option, + pub(crate) committee_term_limit: Option, + pub(crate) governance_action_validity_period: Option, + pub(crate) governance_action_deposit: Option, + pub(crate) drep_deposit: Option, + pub(crate) drep_inactivity_period: Option, + pub(crate) ref_script_coins_per_byte: Option, +} + +impl_to_from!(ProtocolParamUpdate); + +#[wasm_bindgen] +impl ProtocolParamUpdate { + pub fn set_minfee_a(&mut self, minfee_a: &Coin) { + self.minfee_a = Some(minfee_a.clone()) + } + + pub fn minfee_a(&self) -> Option { + self.minfee_a.clone() + } + + pub fn set_minfee_b(&mut self, minfee_b: &Coin) { + self.minfee_b = Some(minfee_b.clone()) + } + + pub fn minfee_b(&self) -> Option { + self.minfee_b.clone() + } + + pub fn set_max_block_body_size(&mut self, max_block_body_size: u32) { + self.max_block_body_size = Some(max_block_body_size) + } + + pub fn max_block_body_size(&self) -> Option { + self.max_block_body_size.clone() + } + + pub fn set_max_tx_size(&mut self, max_tx_size: u32) { + self.max_tx_size = Some(max_tx_size) + } + + pub fn max_tx_size(&self) -> Option { + self.max_tx_size.clone() + } + + pub fn set_max_block_header_size(&mut self, max_block_header_size: u32) { + self.max_block_header_size = Some(max_block_header_size) + } + + pub fn max_block_header_size(&self) -> Option { + self.max_block_header_size.clone() + } + + pub fn set_key_deposit(&mut self, key_deposit: &Coin) { + self.key_deposit = Some(key_deposit.clone()) + } + + pub fn key_deposit(&self) -> Option { + self.key_deposit.clone() + } + + pub fn set_pool_deposit(&mut self, pool_deposit: &Coin) { + self.pool_deposit = Some(pool_deposit.clone()) + } + + pub fn pool_deposit(&self) -> Option { + self.pool_deposit.clone() + } + + pub fn set_max_epoch(&mut self, max_epoch: Epoch) { + self.max_epoch = Some(max_epoch.clone()) + } + + pub fn max_epoch(&self) -> Option { + self.max_epoch.clone() + } + + pub fn set_n_opt(&mut self, n_opt: u32) { + self.n_opt = Some(n_opt) + } + + pub fn n_opt(&self) -> Option { + self.n_opt.clone() + } + + pub fn set_pool_pledge_influence(&mut self, pool_pledge_influence: &UnitInterval) { + self.pool_pledge_influence = Some(pool_pledge_influence.clone()) + } + + pub fn pool_pledge_influence(&self) -> Option { + self.pool_pledge_influence.clone() + } + + pub fn set_expansion_rate(&mut self, expansion_rate: &UnitInterval) { + self.expansion_rate = Some(expansion_rate.clone()) + } + + pub fn expansion_rate(&self) -> Option { + self.expansion_rate.clone() + } + + pub fn set_treasury_growth_rate(&mut self, treasury_growth_rate: &UnitInterval) { + self.treasury_growth_rate = Some(treasury_growth_rate.clone()) + } + + pub fn treasury_growth_rate(&self) -> Option { + self.treasury_growth_rate.clone() + } + + /// !!! DEPRECATED !!! + /// Since babbage era this param is outdated. But this param you can meet in a pre-babbage block. + #[deprecated( + since = "11.0.0", + note = "Since babbage era this param is outdated. But this param you can meet in a pre-babbage block." + )] + pub fn d(&self) -> Option { + self.d.clone() + } + + /// !!! DEPRECATED !!! + /// Since babbage era this param is outdated. But this param you can meet in a pre-babbage block. + #[deprecated( + since = "11.0.0", + note = "Since babbage era this param is outdated. But this param you can meet in a pre-babbage block." + )] + pub fn extra_entropy(&self) -> Option { + self.extra_entropy.clone() + } + + /// !!! DEPRECATED !!! + /// Since conway era this param is outdated. But this param you can meet in a pre-conway block. + #[deprecated( + since = "12.0.0", + note = "Since conway era this param is outdated. But this param you can meet in a pre-conway block." + )] + pub fn set_protocol_version(&mut self, protocol_version: &ProtocolVersion) { + self.protocol_version = Some(protocol_version.clone()) + } + + pub fn protocol_version(&self) -> Option { + self.protocol_version.clone() + } + + pub fn set_min_pool_cost(&mut self, min_pool_cost: &Coin) { + self.min_pool_cost = Some(min_pool_cost.clone()) + } + + pub fn min_pool_cost(&self) -> Option { + self.min_pool_cost.clone() + } + + pub fn set_ada_per_utxo_byte(&mut self, ada_per_utxo_byte: &Coin) { + self.ada_per_utxo_byte = Some(ada_per_utxo_byte.clone()) + } + + pub fn ada_per_utxo_byte(&self) -> Option { + self.ada_per_utxo_byte.clone() + } + + pub fn set_cost_models(&mut self, cost_models: &Costmdls) { + self.cost_models = Some(cost_models.clone()) + } + + pub fn cost_models(&self) -> Option { + self.cost_models.clone() + } + + pub fn set_execution_costs(&mut self, execution_costs: &ExUnitPrices) { + self.execution_costs = Some(execution_costs.clone()) + } + + pub fn execution_costs(&self) -> Option { + self.execution_costs.clone() + } + + pub fn set_max_tx_ex_units(&mut self, max_tx_ex_units: &ExUnits) { + self.max_tx_ex_units = Some(max_tx_ex_units.clone()) + } + + pub fn max_tx_ex_units(&self) -> Option { + self.max_tx_ex_units.clone() + } + + pub fn set_max_block_ex_units(&mut self, max_block_ex_units: &ExUnits) { + self.max_block_ex_units = Some(max_block_ex_units.clone()) + } + + pub fn max_block_ex_units(&self) -> Option { + self.max_block_ex_units.clone() + } + + pub fn set_max_value_size(&mut self, max_value_size: u32) { + self.max_value_size = Some(max_value_size.clone()) + } + + pub fn max_value_size(&self) -> Option { + self.max_value_size.clone() + } + + pub fn set_collateral_percentage(&mut self, collateral_percentage: u32) { + self.collateral_percentage = Some(collateral_percentage) + } + + pub fn collateral_percentage(&self) -> Option { + self.collateral_percentage.clone() + } + + pub fn set_max_collateral_inputs(&mut self, max_collateral_inputs: u32) { + self.max_collateral_inputs = Some(max_collateral_inputs) + } + + pub fn max_collateral_inputs(&self) -> Option { + self.max_collateral_inputs.clone() + } + + pub fn set_pool_voting_thresholds(&mut self, pool_voting_thresholds: &PoolVotingThresholds) { + self.pool_voting_thresholds = Some(pool_voting_thresholds.clone()) + } + + pub fn pool_voting_thresholds(&self) -> Option { + self.pool_voting_thresholds.clone() + } + + pub fn set_drep_voting_thresholds(&mut self, drep_voting_thresholds: &DRepVotingThresholds) { + self.drep_voting_thresholds = Some(drep_voting_thresholds.clone()) + } + + pub fn drep_voting_thresholds(&self) -> Option { + self.drep_voting_thresholds.clone() + } + + pub fn set_min_committee_size(&mut self, min_committee_size: u32) { + self.min_committee_size = Some(min_committee_size) + } + + pub fn min_committee_size(&self) -> Option { + self.min_committee_size.clone() + } + + pub fn set_committee_term_limit(&mut self, committee_term_limit: Epoch) { + self.committee_term_limit = Some(committee_term_limit) + } + + pub fn committee_term_limit(&self) -> Option { + self.committee_term_limit.clone() + } + + pub fn set_governance_action_validity_period(&mut self, governance_action_validity_period: Epoch) { + self.governance_action_validity_period = Some(governance_action_validity_period) + } + + pub fn governance_action_validity_period(&self) -> Option { + self.governance_action_validity_period.clone() + } + + pub fn set_governance_action_deposit(&mut self, governance_action_deposit: &Coin) { + self.governance_action_deposit = Some(governance_action_deposit.clone()); + } + + pub fn governance_action_deposit(&self) -> Option { + self.governance_action_deposit.clone() + } + + pub fn set_drep_deposit(&mut self, drep_deposit: &Coin) { + self.drep_deposit = Some(drep_deposit.clone()); + } + + pub fn drep_deposit(&self) -> Option { + self.drep_deposit.clone() + } + + pub fn set_drep_inactivity_period(&mut self, drep_inactivity_period: Epoch) { + self.drep_inactivity_period = Some(drep_inactivity_period) + } + + pub fn drep_inactivity_period(&self) -> Option { + self.drep_inactivity_period.clone() + } + + pub fn set_ref_script_coins_per_byte(&mut self, ref_script_coins_per_byte: &UnitInterval) { + self.ref_script_coins_per_byte = Some(ref_script_coins_per_byte.clone()); + } + + pub fn ref_script_coins_per_byte(&self) -> Option { + self.ref_script_coins_per_byte.clone() + } + + pub fn new() -> Self { + Self { + minfee_a: None, + minfee_b: None, + max_block_body_size: None, + max_tx_size: None, + max_block_header_size: None, + key_deposit: None, + pool_deposit: None, + max_epoch: None, + n_opt: None, + pool_pledge_influence: None, + expansion_rate: None, + treasury_growth_rate: None, + d: None, + extra_entropy: None, + protocol_version: None, + min_pool_cost: None, + ada_per_utxo_byte: None, + cost_models: None, + execution_costs: None, + max_tx_ex_units: None, + max_block_ex_units: None, + max_value_size: None, + collateral_percentage: None, + max_collateral_inputs: None, + pool_voting_thresholds: None, + drep_voting_thresholds: None, + min_committee_size: None, + committee_term_limit: None, + governance_action_validity_period: None, + governance_action_deposit: None, + drep_deposit: None, + drep_inactivity_period: None, + ref_script_coins_per_byte: None, + } + } +} diff --git a/rust/src/protocol_types/script_ref.rs b/rust/src/protocol_types/script_ref.rs new file mode 100644 index 00000000..8a0aa638 --- /dev/null +++ b/rust/src/protocol_types/script_ref.rs @@ -0,0 +1,63 @@ +use crate::*; + +#[derive( + Debug, Clone, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub(crate) enum ScriptRefEnum { + NativeScript(NativeScript), + PlutusScript(PlutusScript), +} + +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, +)] +pub struct ScriptRef(pub(crate) ScriptRefEnum); + +impl_to_from!(ScriptRef); + +#[wasm_bindgen] +impl ScriptRef { + pub fn new_native_script(native_script: &NativeScript) -> Self { + Self(ScriptRefEnum::NativeScript(native_script.clone())) + } + + pub fn new_plutus_script(plutus_script: &PlutusScript) -> Self { + Self(ScriptRefEnum::PlutusScript(plutus_script.clone())) + } + + pub fn is_native_script(&self) -> bool { + match &self.0 { + ScriptRefEnum::NativeScript(_) => true, + _ => false, + } + } + + pub fn is_plutus_script(&self) -> bool { + match &self.0 { + ScriptRefEnum::PlutusScript(_) => true, + _ => false, + } + } + + pub fn native_script(&self) -> Option { + match &self.0 { + ScriptRefEnum::NativeScript(native_script) => Some(native_script.clone()), + _ => None, + } + } + + pub fn plutus_script(&self) -> Option { + match &self.0 { + ScriptRefEnum::PlutusScript(plutus_script) => Some(plutus_script.clone()), + _ => None, + } + } + + /// Return bytes array of script ref struct but without wrapping into CBOR array under the tag + /// to_bytes returns "#6.24(bytes .cbor script)" from CDDL + /// to_unwrapped_bytes return "script" from CDDL + pub fn to_unwrapped_bytes(&self) -> Vec { + to_bytes(&self.0) + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/tests.rs b/rust/src/protocol_types/tests.rs deleted file mode 100644 index 2ff510e0..00000000 --- a/rust/src/protocol_types/tests.rs +++ /dev/null @@ -1,73 +0,0 @@ -#[cfg(test)] -mod test { - use crate::protocol_types::fixed_tx::FixedTransaction; - use crate::Transaction; - use hex; - - #[test] - fn simple_round_trip() { - let original_tx = FixedTransaction::from_hex("84a700818258208b9c96823c19f2047f32210a330434b3d163e194ea17b2b702c0667f6fea7a7a000d80018182581d6138fe1dd1d91221a199ff0dacf41fdd5b87506b533d00e70fae8dae8f1abfbac06a021a0002b645031a03962de305a1581de1b3cabd3914ef99169ace1e8b545b635f809caa35f8b6c8bc69ae48061abf4009040e80a100828258207dc05ac55cdfb9cc24571d491d3a3bdbd7d48489a916d27fce3ffe5c9af1b7f55840d7eda8457f1814fe3333b7b1916e3b034e6d480f97f4f286b1443ef72383279718a3a3fddf127dae0505b01a48fd9ffe0f52d9d8c46d02bcb85d1d106c13aa048258201b3d6e1236891a921abf1a3f90a9fb1b2568b1096b6cd6d3eaaeb0ef0ee0802f58401ce4658303c3eb0f2b9705992ccd62de30423ade90219e2c4cfc9eb488c892ea28ba3110f0c062298447f4f6365499d97d31207075f9815c3fe530bd9a927402f5f6").unwrap(); - let body = hex::decode("a700818258208b9c96823c19f2047f32210a330434b3d163e194ea17b2b702c0667f6fea7a7a000d80018182581d6138fe1dd1d91221a199ff0dacf41fdd5b87506b533d00e70fae8dae8f1abfbac06a021a0002b645031a03962de305a1581de1b3cabd3914ef99169ace1e8b545b635f809caa35f8b6c8bc69ae48061abf4009040e80").unwrap(); - let wit_set = hex::decode("a100828258207dc05ac55cdfb9cc24571d491d3a3bdbd7d48489a916d27fce3ffe5c9af1b7f55840d7eda8457f1814fe3333b7b1916e3b034e6d480f97f4f286b1443ef72383279718a3a3fddf127dae0505b01a48fd9ffe0f52d9d8c46d02bcb85d1d106c13aa048258201b3d6e1236891a921abf1a3f90a9fb1b2568b1096b6cd6d3eaaeb0ef0ee0802f58401ce4658303c3eb0f2b9705992ccd62de30423ade90219e2c4cfc9eb488c892ea28ba3110f0c062298447f4f6365499d97d31207075f9815c3fe530bd9a927402").unwrap(); - let tx = FixedTransaction::new(&body, &wit_set, true) - .unwrap(); - let tx2 = FixedTransaction::from_bytes(tx.to_bytes()).unwrap(); - - assert_eq!(body, tx2.raw_body()); - assert_eq!(wit_set, tx2.raw_witness_set()); - assert_eq!(tx.raw_body(), tx2.raw_body()); - assert_eq!(tx.raw_witness_set(), tx2.raw_witness_set()); - assert_eq!(tx.is_valid(), tx2.is_valid()); - assert_eq!(tx.to_bytes(), tx2.to_bytes()); - assert_eq!(tx.raw_body(), original_tx.raw_body()); - assert_eq!(tx.raw_witness_set(), original_tx.raw_witness_set()); - assert_eq!(tx.is_valid(), original_tx.is_valid()); - assert_eq!(tx.to_bytes(), original_tx.to_bytes()); - } - - #[test] - fn round_trip_via_tx() { - let casual_tx = Transaction::from_hex("84a700818258208b9c96823c19f2047f32210a330434b3d163e194ea17b2b702c0667f6fea7a7a000d80018182581d6138fe1dd1d91221a199ff0dacf41fdd5b87506b533d00e70fae8dae8f1abfbac06a021a0002b645031a03962de305a1581de1b3cabd3914ef99169ace1e8b545b635f809caa35f8b6c8bc69ae48061abf4009040e80a100828258207dc05ac55cdfb9cc24571d491d3a3bdbd7d48489a916d27fce3ffe5c9af1b7f55840d7eda8457f1814fe3333b7b1916e3b034e6d480f97f4f286b1443ef72383279718a3a3fddf127dae0505b01a48fd9ffe0f52d9d8c46d02bcb85d1d106c13aa048258201b3d6e1236891a921abf1a3f90a9fb1b2568b1096b6cd6d3eaaeb0ef0ee0802f58401ce4658303c3eb0f2b9705992ccd62de30423ade90219e2c4cfc9eb488c892ea28ba3110f0c062298447f4f6365499d97d31207075f9815c3fe530bd9a927402f5f6").unwrap(); - let body = hex::decode("a700818258208b9c96823c19f2047f32210a330434b3d163e194ea17b2b702c0667f6fea7a7a000d80018182581d6138fe1dd1d91221a199ff0dacf41fdd5b87506b533d00e70fae8dae8f1abfbac06a021a0002b645031a03962de305a1581de1b3cabd3914ef99169ace1e8b545b635f809caa35f8b6c8bc69ae48061abf4009040e80").unwrap(); - let wit_set = hex::decode("a100828258207dc05ac55cdfb9cc24571d491d3a3bdbd7d48489a916d27fce3ffe5c9af1b7f55840d7eda8457f1814fe3333b7b1916e3b034e6d480f97f4f286b1443ef72383279718a3a3fddf127dae0505b01a48fd9ffe0f52d9d8c46d02bcb85d1d106c13aa048258201b3d6e1236891a921abf1a3f90a9fb1b2568b1096b6cd6d3eaaeb0ef0ee0802f58401ce4658303c3eb0f2b9705992ccd62de30423ade90219e2c4cfc9eb488c892ea28ba3110f0c062298447f4f6365499d97d31207075f9815c3fe530bd9a927402").unwrap(); - let tx = FixedTransaction::new(&body, &wit_set, true) - .unwrap(); - let tx2 = Transaction::from_bytes(tx.to_bytes()).unwrap(); - - assert_eq!(casual_tx.body(), tx.body()); - assert_eq!(casual_tx.witness_set(), tx.witness_set()); - assert_eq!(casual_tx.is_valid(), tx.is_valid()); - assert_eq!(casual_tx, tx2); - } - - #[test] - fn round_trip_nonstandart_body() { - let original_tx = FixedTransaction::from_hex("84a7009F8258208b9c96823c19f2047f32210a330434b3d163e194ea17b2b702c0667f6fea7a7a00FF0d80018182581d6138fe1dd1d91221a199ff0dacf41fdd5b87506b533d00e70fae8dae8f1abfbac06a021a0002b645031a03962de305a1581de1b3cabd3914ef99169ace1e8b545b635f809caa35f8b6c8bc69ae48061abf4009040e80a100828258207dc05ac55cdfb9cc24571d491d3a3bdbd7d48489a916d27fce3ffe5c9af1b7f55840d7eda8457f1814fe3333b7b1916e3b034e6d480f97f4f286b1443ef72383279718a3a3fddf127dae0505b01a48fd9ffe0f52d9d8c46d02bcb85d1d106c13aa048258201b3d6e1236891a921abf1a3f90a9fb1b2568b1096b6cd6d3eaaeb0ef0ee0802f58401ce4658303c3eb0f2b9705992ccd62de30423ade90219e2c4cfc9eb488c892ea28ba3110f0c062298447f4f6365499d97d31207075f9815c3fe530bd9a927402f5f6").unwrap(); - let casual_tx = Transaction::from_hex("84a7009F8258208b9c96823c19f2047f32210a330434b3d163e194ea17b2b702c0667f6fea7a7a00FF0d80018182581d6138fe1dd1d91221a199ff0dacf41fdd5b87506b533d00e70fae8dae8f1abfbac06a021a0002b645031a03962de305a1581de1b3cabd3914ef99169ace1e8b545b635f809caa35f8b6c8bc69ae48061abf4009040e80a100828258207dc05ac55cdfb9cc24571d491d3a3bdbd7d48489a916d27fce3ffe5c9af1b7f55840d7eda8457f1814fe3333b7b1916e3b034e6d480f97f4f286b1443ef72383279718a3a3fddf127dae0505b01a48fd9ffe0f52d9d8c46d02bcb85d1d106c13aa048258201b3d6e1236891a921abf1a3f90a9fb1b2568b1096b6cd6d3eaaeb0ef0ee0802f58401ce4658303c3eb0f2b9705992ccd62de30423ade90219e2c4cfc9eb488c892ea28ba3110f0c062298447f4f6365499d97d31207075f9815c3fe530bd9a927402f5f6").unwrap(); - let body = hex::decode("a7009F8258208b9c96823c19f2047f32210a330434b3d163e194ea17b2b702c0667f6fea7a7a00FF0d80018182581d6138fe1dd1d91221a199ff0dacf41fdd5b87506b533d00e70fae8dae8f1abfbac06a021a0002b645031a03962de305a1581de1b3cabd3914ef99169ace1e8b545b635f809caa35f8b6c8bc69ae48061abf4009040e80").unwrap(); - let wit_set = hex::decode("a100828258207dc05ac55cdfb9cc24571d491d3a3bdbd7d48489a916d27fce3ffe5c9af1b7f55840d7eda8457f1814fe3333b7b1916e3b034e6d480f97f4f286b1443ef72383279718a3a3fddf127dae0505b01a48fd9ffe0f52d9d8c46d02bcb85d1d106c13aa048258201b3d6e1236891a921abf1a3f90a9fb1b2568b1096b6cd6d3eaaeb0ef0ee0802f58401ce4658303c3eb0f2b9705992ccd62de30423ade90219e2c4cfc9eb488c892ea28ba3110f0c062298447f4f6365499d97d31207075f9815c3fe530bd9a927402").unwrap(); - let tx = FixedTransaction::new(&body, &wit_set, true) - .unwrap(); - let tx2 = Transaction::from_bytes(tx.to_bytes()).unwrap(); - let tx3 = FixedTransaction::from_bytes(tx.to_bytes()).unwrap(); - - assert_eq!(casual_tx.body(), tx.body()); - assert_eq!(casual_tx.witness_set(), tx.witness_set()); - assert_eq!(casual_tx.is_valid(), tx.is_valid()); - - assert_eq!(body, tx3.raw_body()); - assert_eq!(wit_set, tx3.raw_witness_set()); - assert_eq!(tx.raw_body(), tx3.raw_body()); - assert_eq!(tx.raw_witness_set(), tx3.raw_witness_set()); - assert_eq!(tx.is_valid(), tx3.is_valid()); - assert_eq!(tx.to_bytes(), tx3.to_bytes()); - assert_eq!(tx.raw_body(), original_tx.raw_body()); - assert_eq!(tx.raw_witness_set(), original_tx.raw_witness_set()); - assert_eq!(tx.is_valid(), original_tx.is_valid()); - assert_eq!(tx.to_bytes(), original_tx.to_bytes()); - - assert_eq!(casual_tx, tx2); - - assert_ne!(tx2.to_bytes(), original_tx.to_bytes()); - } -} \ No newline at end of file diff --git a/rust/src/protocol_types/transaction_body.rs b/rust/src/protocol_types/transaction_body.rs new file mode 100644 index 00000000..b3a93d98 --- /dev/null +++ b/rust/src/protocol_types/transaction_body.rs @@ -0,0 +1,290 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct TransactionBody { + pub(crate) inputs: TransactionInputs, + pub(crate) outputs: TransactionOutputs, + pub(crate) fee: Coin, + pub(crate) ttl: Option, + pub(crate) certs: Option, + pub(crate) withdrawals: Option, + pub(crate) update: Option, + pub(crate) auxiliary_data_hash: Option, + pub(crate) validity_start_interval: Option, + pub(crate) mint: Option, + pub(crate) script_data_hash: Option, + pub(crate) collateral: Option, + pub(crate) required_signers: Option, + pub(crate) network_id: Option, + pub(crate) collateral_return: Option, + pub(crate) total_collateral: Option, + pub(crate) reference_inputs: Option, + pub(crate) voting_procedures: Option, + pub(crate) voting_proposals: Option, + pub(crate) donation: Option, + pub(crate) current_treasury_value: Option, +} + +impl_to_from!(TransactionBody); + +#[wasm_bindgen] +impl TransactionBody { + pub fn inputs(&self) -> TransactionInputs { + self.inputs.clone() + } + + pub fn outputs(&self) -> TransactionOutputs { + self.outputs.clone() + } + + pub fn fee(&self) -> Coin { + self.fee.clone() + } + + /// !!! DEPRECATED !!! + /// Returns a Slot32 (u32) value in case the underlying original BigNum (u64) value is within the limits. + /// Otherwise will just raise an error. + #[deprecated( + since = "10.1.0", + note = "Possible boundary error. Use ttl_bignum instead" + )] + pub fn ttl(&self) -> Result, JsError> { + match self.ttl { + Some(ttl) => match ttl.try_into() { + Ok(ttl32) => Ok(Some(ttl32)), + Err(err) => Err(err), + }, + None => Ok(None), + } + } + + pub fn ttl_bignum(&self) -> Option { + self.ttl + } + + pub fn set_ttl(&mut self, ttl: &SlotBigNum) { + self.ttl = Some(ttl.clone()) + } + + pub fn remove_ttl(&mut self) { + self.ttl = None + } + + pub fn set_certs(&mut self, certs: &Certificates) { + self.certs = Some(certs.clone()) + } + + pub fn certs(&self) -> Option { + self.certs.clone() + } + + pub fn set_withdrawals(&mut self, withdrawals: &Withdrawals) { + self.withdrawals = Some(withdrawals.clone()) + } + + pub fn withdrawals(&self) -> Option { + self.withdrawals.clone() + } + + pub fn set_update(&mut self, update: &Update) { + self.update = Some(update.clone()) + } + + pub fn update(&self) -> Option { + self.update.clone() + } + + pub fn set_auxiliary_data_hash(&mut self, auxiliary_data_hash: &AuxiliaryDataHash) { + self.auxiliary_data_hash = Some(auxiliary_data_hash.clone()) + } + + pub fn auxiliary_data_hash(&self) -> Option { + self.auxiliary_data_hash.clone() + } + + /// !!! DEPRECATED !!! + /// Uses outdated slot number format. + #[deprecated( + since = "10.1.0", + note = "Underlying value capacity of slot (BigNum u64) bigger then Slot32. Use set_validity_start_interval_bignum instead." + )] + pub fn set_validity_start_interval(&mut self, validity_start_interval: Slot32) { + self.validity_start_interval = Some(validity_start_interval.into()) + } + + pub fn set_validity_start_interval_bignum(&mut self, validity_start_interval: &SlotBigNum) { + self.validity_start_interval = Some(validity_start_interval.clone()) + } + + pub fn validity_start_interval_bignum(&self) -> Option { + self.validity_start_interval.clone() + } + + /// !!! DEPRECATED !!! + /// Returns a Option (u32) value in case the underlying original Option (u64) value is within the limits. + /// Otherwise will just raise an error. + /// Use `.validity_start_interval_bignum` instead. + #[deprecated( + since = "10.1.0", + note = "Possible boundary error. Use validity_start_interval_bignum instead" + )] + pub fn validity_start_interval(&self) -> Result, JsError> { + match self.validity_start_interval.clone() { + Some(interval) => match interval.try_into() { + Ok(internal32) => Ok(Some(internal32)), + Err(err) => Err(err), + }, + None => Ok(None), + } + } + + pub fn set_mint(&mut self, mint: &Mint) { + self.mint = Some(mint.clone()) + } + + pub fn mint(&self) -> Option { + self.mint.clone() + } + + pub fn set_reference_inputs(&mut self, reference_inputs: &TransactionInputs) { + self.reference_inputs = Some(reference_inputs.clone()) + } + + pub fn reference_inputs(&self) -> Option { + self.reference_inputs.clone() + } + + pub fn set_script_data_hash(&mut self, script_data_hash: &ScriptDataHash) { + self.script_data_hash = Some(script_data_hash.clone()) + } + + pub fn script_data_hash(&self) -> Option { + self.script_data_hash.clone() + } + + pub fn set_collateral(&mut self, collateral: &TransactionInputs) { + self.collateral = Some(collateral.clone()) + } + + pub fn collateral(&self) -> Option { + self.collateral.clone() + } + + pub fn set_required_signers(&mut self, required_signers: &Ed25519KeyHashes) { + self.required_signers = Some(required_signers.clone()) + } + + pub fn required_signers(&self) -> Option { + self.required_signers.clone() + } + + pub fn set_network_id(&mut self, network_id: &NetworkId) { + self.network_id = Some(network_id.clone()) + } + + pub fn network_id(&self) -> Option { + self.network_id.clone() + } + + pub fn set_collateral_return(&mut self, collateral_return: &TransactionOutput) { + self.collateral_return = Some(collateral_return.clone()); + } + + pub fn collateral_return(&self) -> Option { + self.collateral_return.clone() + } + + pub fn set_total_collateral(&mut self, total_collateral: &Coin) { + self.total_collateral = Some(total_collateral.clone()); + } + + pub fn total_collateral(&self) -> Option { + self.total_collateral.clone() + } + + pub fn set_voting_procedures(&mut self, voting_procedures: &VotingProcedures) { + self.voting_procedures = Some(voting_procedures.clone()); + } + + pub fn voting_procedures(&self) -> Option { + self.voting_procedures.clone() + } + + pub fn set_voting_proposals(&mut self, voting_proposals: &VotingProposals) { + self.voting_proposals = Some(voting_proposals.clone()); + } + + pub fn voting_proposals(&self) -> Option { + self.voting_proposals.clone() + } + + pub fn set_donation(&mut self, donation: &Coin) { + self.donation = Some(donation.clone()); + } + + pub fn donation(&self) -> Option { + self.donation.clone() + } + + pub fn set_current_treasury_value(&mut self, current_treasury_value: &Coin) { + self.current_treasury_value = Some(current_treasury_value.clone()); + } + + pub fn current_treasury_value(&self) -> Option { + self.current_treasury_value.clone() + } + + /// !!! DEPRECATED !!! + /// This constructor uses outdated slot number format for the ttl value. + /// Use `.new_tx_body` and then `.set_ttl` instead + #[deprecated( + since = "10.1.0", + note = "Underlying value capacity of ttl (BigNum u64) bigger then Slot32. Use new_tx_body instead." + )] + pub fn new( + inputs: &TransactionInputs, + outputs: &TransactionOutputs, + fee: &Coin, + ttl: Option, + ) -> Self { + let mut tx = Self::new_tx_body(inputs, outputs, fee); + if let Some(slot32) = ttl { + tx.set_ttl(&(slot32 as u64).into()); + } + tx + } + + /// Returns a new TransactionBody. + /// In the new version of "new" we removed optional ttl for support it by wasm_bingen. + /// Your can use "set_ttl" and "remove_ttl" to set a new value for ttl or set it as None. + pub fn new_tx_body( + inputs: &TransactionInputs, + outputs: &TransactionOutputs, + fee: &Coin, + ) -> Self { + Self { + inputs: inputs.clone(), + outputs: outputs.clone(), + fee: fee.clone(), + ttl: None, + certs: None, + withdrawals: None, + update: None, + auxiliary_data_hash: None, + validity_start_interval: None, + mint: None, + script_data_hash: None, + collateral: None, + required_signers: None, + network_id: None, + collateral_return: None, + total_collateral: None, + reference_inputs: None, + voting_procedures: None, + voting_proposals: None, + donation: None, + current_treasury_value: None, + } + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/tx_input.rs b/rust/src/protocol_types/tx_input.rs new file mode 100644 index 00000000..f48824e8 --- /dev/null +++ b/rust/src/protocol_types/tx_input.rs @@ -0,0 +1,46 @@ +use std::fmt::Formatter; +use crate::*; + +#[wasm_bindgen] +#[derive( + Clone, + Debug, + Eq, + Ord, + PartialEq, + PartialOrd, + Hash, + serde::Serialize, + serde::Deserialize, + JsonSchema, +)] +pub struct TransactionInput { + pub(crate) transaction_id: TransactionHash, + pub(crate) index: TransactionIndex, +} + +impl_to_from!(TransactionInput); + +#[wasm_bindgen] +impl TransactionInput { + pub fn transaction_id(&self) -> TransactionHash { + self.transaction_id.clone() + } + + pub fn index(&self) -> TransactionIndex { + self.index.clone() + } + + pub fn new(transaction_id: &TransactionHash, index: TransactionIndex) -> Self { + Self { + transaction_id: transaction_id.clone(), + index: index, + } + } +} + +impl Display for TransactionInput { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}#{}", self.transaction_id, self.index) + } +} diff --git a/rust/src/protocol_types/tx_inputs.rs b/rust/src/protocol_types/tx_inputs.rs new file mode 100644 index 00000000..886b7952 --- /dev/null +++ b/rust/src/protocol_types/tx_inputs.rs @@ -0,0 +1,128 @@ +use crate::*; +use std::slice::Iter; +use std::vec::IntoIter; + +#[wasm_bindgen] +#[derive( + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, +)] +pub struct TransactionInputs { + pub(crate) inputs: Vec, + pub(crate) dedup: BTreeSet, +} + +impl_to_from!(TransactionInputs); + +impl NoneOrEmpty for TransactionInputs { + fn is_none_or_empty(&self) -> bool { + self.inputs.is_empty() + } +} + +#[wasm_bindgen] +impl TransactionInputs { + pub fn new() -> Self { + Self { + inputs: Vec::new(), + dedup: BTreeSet::new(), + } + } + + pub fn len(&self) -> usize { + self.inputs.len() + } + + pub fn get(&self, index: usize) -> TransactionInput { + self.inputs[index].clone() + } + + /// Add a new `TransactionInput` to the set. + /// Returns `true` if the element was not already present in the set. + /// Note that the `TransactionInput` is added to the set only if it is not already present. + pub fn add(&mut self, elem: &TransactionInput) -> bool { + if self.dedup.insert(elem.clone()) { + self.inputs.push(elem.clone()); + true + } else { + false + } + } + + #[allow(dead_code)] + pub(crate) fn contains(&self, elem: &TransactionInput) -> bool { + self.dedup.contains(elem) + } + + pub(crate) fn add_move(&mut self, elem: TransactionInput) { + if self.dedup.insert(elem.clone()) { + self.inputs.push(elem); + } + } + + pub(crate) fn from_vec(inputs_vec: Vec) -> Self { + let mut inputs = Self::new(); + for input in inputs_vec { + inputs.add_move(input); + } + inputs + } + + pub fn to_option(&self) -> Option { + if self.len() > 0 { + Some(self.clone()) + } else { + None + } + } +} + +impl<'a> IntoIterator for &'a TransactionInputs { + type Item = &'a TransactionInput; + type IntoIter = Iter<'a, TransactionInput>; + + fn into_iter(self) -> Self::IntoIter { + self.inputs.iter() + } +} + +impl IntoIterator for TransactionInputs { + type Item = TransactionInput; + type IntoIter = IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.inputs.into_iter() + } +} + +impl serde::Serialize for TransactionInputs { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.inputs.serialize(serializer) + } +} + +impl<'de> serde::de::Deserialize<'de> for TransactionInputs { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let vec = as serde::de::Deserialize>::deserialize( + deserializer, + )?; + Ok(Self::from_vec(vec)) + } +} + +impl JsonSchema for TransactionInputs { + fn schema_name() -> String { + String::from("TransactionInputs") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + Vec::::json_schema(gen) + } + fn is_referenceable() -> bool { + Vec::::is_referenceable() + } +} diff --git a/rust/src/protocol_types/witnesses/bootstrap_witness.rs b/rust/src/protocol_types/witnesses/bootstrap_witness.rs new file mode 100644 index 00000000..676fa876 --- /dev/null +++ b/rust/src/protocol_types/witnesses/bootstrap_witness.rs @@ -0,0 +1,45 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct BootstrapWitness { + pub(crate) vkey: Vkey, + pub(crate) signature: Ed25519Signature, + pub(crate) chain_code: Vec, + pub(crate) attributes: Vec, +} + +impl_to_from!(BootstrapWitness); + +#[wasm_bindgen] +impl BootstrapWitness { + pub fn vkey(&self) -> Vkey { + self.vkey.clone() + } + + pub fn signature(&self) -> Ed25519Signature { + self.signature.clone() + } + + pub fn chain_code(&self) -> Vec { + self.chain_code.clone() + } + + pub fn attributes(&self) -> Vec { + self.attributes.clone() + } + + pub fn new( + vkey: &Vkey, + signature: &Ed25519Signature, + chain_code: Vec, + attributes: Vec, + ) -> Self { + Self { + vkey: vkey.clone(), + signature: signature.clone(), + chain_code: chain_code, + attributes: attributes, + } + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/witnesses/bootstrap_witnesses.rs b/rust/src/protocol_types/witnesses/bootstrap_witnesses.rs new file mode 100644 index 00000000..c1372641 --- /dev/null +++ b/rust/src/protocol_types/witnesses/bootstrap_witnesses.rs @@ -0,0 +1,100 @@ +use crate::*; +use std::collections::HashSet; + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BootstrapWitnesses { + witnesses: Vec, + + //for deduplication purpose + dedup: HashSet, +} + +impl_to_from!(BootstrapWitnesses); + +#[wasm_bindgen] +impl BootstrapWitnesses { + pub fn new() -> Self { + Self { + witnesses: Vec::new(), + dedup: HashSet::new(), + } + } + + pub fn len(&self) -> usize { + self.witnesses.len() + } + + pub fn get(&self, index: usize) -> BootstrapWitness { + self.witnesses[index].clone() + } + + /// Add a new `BootstrapWitness` to the set. + /// Returns `true` if the element was not already present in the set. + pub fn add(&mut self, elem: &BootstrapWitness) -> bool { + if self.dedup.insert(elem.clone()) { + self.witnesses.push(elem.clone()); + true + } else { + false + } + } + + pub(crate) fn get_vec_wits(&self) -> &Vec { + &self.witnesses + } + + pub(crate) fn from_vec_wits(wits: Vec) -> Self { + let mut dedup = HashSet::new(); + let mut witnesses = Vec::new(); + for wit in wits { + if dedup.insert(wit.clone()) { + witnesses.push(wit); + } + } + + Self { + witnesses, + dedup, + } + } +} + +impl NoneOrEmpty for BootstrapWitnesses { + fn is_none_or_empty(&self) -> bool { + self.witnesses.is_empty() + } +} + +impl serde::Serialize for BootstrapWitnesses { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let wits = self.get_vec_wits(); + wits.serialize(serializer) + } +} + +impl<'de> serde::de::Deserialize<'de> for BootstrapWitnesses { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let wits = as serde::de::Deserialize>::deserialize(deserializer)?; + + Ok(Self::from_vec_wits(wits)) + } +} + +impl JsonSchema for BootstrapWitnesses { + fn is_referenceable() -> bool { + Vec::::is_referenceable() + } + fn schema_name() -> String { + String::from("BootstrapWitnesses") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + Vec::::json_schema(gen) + } +} diff --git a/rust/src/protocol_types/witnesses/mod.rs b/rust/src/protocol_types/witnesses/mod.rs new file mode 100644 index 00000000..5cfd8133 --- /dev/null +++ b/rust/src/protocol_types/witnesses/mod.rs @@ -0,0 +1,19 @@ +mod vkeywitness; +pub use vkeywitness::*; + +mod vkeywitnesses; +pub use vkeywitnesses::*; + +mod bootstrap_witness; +pub use bootstrap_witness::*; + +mod bootstrap_witnesses; + +pub use bootstrap_witnesses::*; + +mod transaction_witnesses_set; +pub use transaction_witnesses_set::*; + +mod transaction_witnesses_sets; +pub use transaction_witnesses_sets::*; + diff --git a/rust/src/protocol_types/witnesses/transaction_witnesses_set.rs b/rust/src/protocol_types/witnesses/transaction_witnesses_set.rs new file mode 100644 index 00000000..33c0bef2 --- /dev/null +++ b/rust/src/protocol_types/witnesses/transaction_witnesses_set.rs @@ -0,0 +1,112 @@ +use crate::*; +use crate::traits::EmptyToNone; + +#[wasm_bindgen] +#[derive(Clone, Eq, PartialEq, Debug, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct TransactionWitnessSet { + pub(crate) vkeys: Option, + pub(crate) native_scripts: Option, + pub(crate) bootstraps: Option, + pub(crate) plutus_scripts: Option, + pub(crate) plutus_data: Option, + pub(crate) redeemers: Option, +} + +impl_to_from!(TransactionWitnessSet); + +#[wasm_bindgen] +impl TransactionWitnessSet { + pub fn set_vkeys(&mut self, vkeys: &Vkeywitnesses) { + if vkeys.len() > 0 { + self.vkeys = Some(vkeys.clone()) + } + } + + pub fn vkeys(&self) -> Option { + self.vkeys.clone() + } + + pub fn set_native_scripts(&mut self, native_scripts: &NativeScripts) { + if native_scripts.len() > 0 { + self.native_scripts = Some(native_scripts.deduplicated_clone()) + } + } + + pub fn native_scripts(&self) -> Option { + self.native_scripts.clone() + } + + pub fn set_bootstraps(&mut self, bootstraps: &BootstrapWitnesses) { + self.bootstraps = Some(bootstraps.clone()) + } + + pub fn bootstraps(&self) -> Option { + self.bootstraps.clone() + } + + pub fn set_plutus_scripts(&mut self, plutus_scripts: &PlutusScripts) { + if plutus_scripts.len() > 0 { + self.plutus_scripts = Some(plutus_scripts.deduplicated_clone()) + } + } + + pub fn plutus_scripts(&self) -> Option { + self.plutus_scripts.clone() + } + + pub fn set_plutus_data(&mut self, plutus_data: &PlutusList) { + if plutus_data.len() > 0 { + self.plutus_data = Some(plutus_data.deduplicated_clone()) + } + } + + pub fn plutus_data(&self) -> Option { + self.plutus_data.clone() + } + + pub fn set_redeemers(&mut self, redeemers: &Redeemers) { + self.redeemers = Some(redeemers.clone()) + } + + pub fn redeemers(&self) -> Option { + self.redeemers.clone() + } + + pub fn new() -> Self { + Self { + vkeys: None, + native_scripts: None, + bootstraps: None, + plutus_scripts: None, + plutus_data: None, + redeemers: None, + } + } + + pub(crate) fn new_with_partial_dedup( + vkeys: Option, + native_scripts: Option, + bootstraps: Option, + plutus_scripts: Option, + plutus_data: Option, + redeemers: Option, + ) -> Self { + Self { + vkeys, + native_scripts: native_scripts + .map(|scripts| scripts.deduplicated_clone()) + .empty_to_none() + .flatten(), + bootstraps, + plutus_scripts: plutus_scripts + .map(|scripts| scripts.deduplicated_clone()) + .empty_to_none() + .flatten(), + plutus_data: plutus_data + .map(|data| data.deduplicated_clone()) + .empty_to_none() + .flatten(), + redeemers, + } + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/witnesses/transaction_witnesses_sets.rs b/rust/src/protocol_types/witnesses/transaction_witnesses_sets.rs new file mode 100644 index 00000000..64ffd62b --- /dev/null +++ b/rust/src/protocol_types/witnesses/transaction_witnesses_sets.rs @@ -0,0 +1,26 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Eq, Debug, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct TransactionWitnessSets(pub(crate) Vec); + +impl_to_from!(TransactionWitnessSets); + +#[wasm_bindgen] +impl TransactionWitnessSets { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> TransactionWitnessSet { + self.0[index].clone() + } + + pub fn add(&mut self, elem: &TransactionWitnessSet) { + self.0.push(elem.clone()); + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/witnesses/vkeywitness.rs b/rust/src/protocol_types/witnesses/vkeywitness.rs new file mode 100644 index 00000000..398b5cc5 --- /dev/null +++ b/rust/src/protocol_types/witnesses/vkeywitness.rs @@ -0,0 +1,28 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Hash, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, JsonSchema)] +pub struct Vkeywitness { + pub(crate) vkey: Vkey, + pub(crate) signature: Ed25519Signature, +} + +impl_to_from!(Vkeywitness); + +#[wasm_bindgen] +impl Vkeywitness { + pub fn new(vkey: &Vkey, signature: &Ed25519Signature) -> Self { + Self { + vkey: vkey.clone(), + signature: signature.clone(), + } + } + + pub fn vkey(&self) -> Vkey { + self.vkey.clone() + } + + pub fn signature(&self) -> Ed25519Signature { + self.signature.clone() + } +} \ No newline at end of file diff --git a/rust/src/protocol_types/witnesses/vkeywitnesses.rs b/rust/src/protocol_types/witnesses/vkeywitnesses.rs new file mode 100644 index 00000000..f0396fb7 --- /dev/null +++ b/rust/src/protocol_types/witnesses/vkeywitnesses.rs @@ -0,0 +1,94 @@ +use hashlink::LinkedHashSet; +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct Vkeywitnesses { + pub(crate) witnesses: Vec, + pub(crate) dedup: LinkedHashSet, +} + +impl_to_from!(Vkeywitnesses); + +#[wasm_bindgen] +impl Vkeywitnesses { + pub fn new() -> Self { + Self { + witnesses: Vec::new(), + dedup: LinkedHashSet::new(), + } + } + + pub fn len(&self) -> usize { + self.witnesses.len() + } + + pub fn get(&self, index: usize) -> Vkeywitness { + self.witnesses[index].clone() + } + + /// Add a new `Vkeywitness` to the set. + /// Returns `true` if the element was not already present in the set. + pub fn add(&mut self, elem: &Vkeywitness) -> bool { + if self.dedup.insert(elem.clone()) { + self.witnesses.push(elem.clone()); + true + } else { + false + } + } + + pub(crate) fn add_move(&mut self, elem: Vkeywitness) { + if self.dedup.insert(elem.clone()) { + self.witnesses.push(elem); + } + } + + pub(crate) fn from_vec(vec: Vec) -> Self { + let mut dedup = LinkedHashSet::new(); + let mut witnesses = Vec::new(); + for elem in vec { + if dedup.insert(elem.clone()) { + witnesses.push(elem); + } + } + Self { witnesses, dedup } + } +} + +impl NoneOrEmpty for Vkeywitnesses { + fn is_none_or_empty(&self) -> bool { + self.witnesses.is_empty() + } +} + +impl serde::Serialize for Vkeywitnesses { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.witnesses.serialize(serializer) + } +} + +impl<'de> serde::de::Deserialize<'de> for Vkeywitnesses { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let vec = as serde::de::Deserialize>::deserialize(deserializer)?; + Ok(Self::from_vec(vec)) + } +} + +impl JsonSchema for Vkeywitnesses { + fn schema_name() -> String { + String::from("Vkeywitnesses") + } + fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { + Vec::::json_schema(gen) + } + fn is_referenceable() -> bool { + Vec::::is_referenceable() + } +} diff --git a/rust/src/rational.rs b/rust/src/rational.rs new file mode 100644 index 00000000..180599bb --- /dev/null +++ b/rust/src/rational.rs @@ -0,0 +1,184 @@ +use crate::{BigInt, BigNum, JsError, UnitInterval}; + +#[derive(Clone, Debug)] +pub(crate) struct Rational { + numerator: BigInt, + denominator: BigInt, +} + +impl From for Rational { + fn from(sc: UnitInterval) -> Self { + Rational::new(BigInt::from(sc.numerator), BigInt::from(sc.denominator)) + } +} + +impl From<&UnitInterval> for Rational { + fn from(sc: &UnitInterval) -> Self { + Rational::new(BigInt::from(sc.numerator), BigInt::from(sc.denominator)) + } +} + +impl Rational { + pub(crate) fn new(n: BigInt, d: BigInt) -> Self { + Rational { + numerator: n, + denominator: d, + }.reduce_minuses() + } + + + pub (crate) fn one() -> Self { + Rational { + numerator: BigInt::one(), + denominator: BigInt::one(), + } + } + + pub(crate) fn zero() -> Self { + Rational { + numerator: BigInt::zero(), + denominator: BigInt::one(), + } + } + + pub(crate) fn numerator(&self) -> &BigInt { + &self.numerator + } + + pub(crate) fn denominator(&self) -> &BigInt { + &self.denominator + } + + pub(crate) fn mul_bignum(&self, x: &BigNum) -> Result { + let m: BigInt = x.into(); + Ok(Rational::new(self.numerator.mul(&m), self.denominator.clone())) + } + + pub(crate) fn mul_usize(&self, x: usize) -> Rational { + Rational::new(self.numerator.mul(&BigInt::from(x)), self.denominator.clone()) + } + + pub(crate) fn mul_ratio(&self, x: &Rational) -> Rational { + let a_num = &self.numerator; + let a_denum = &self.denominator; + let b_num = &x.numerator; + let b_denum = &x.denominator; + + let a_num_fixed = a_num.mul(b_num); + let a_denum_fixed = a_denum.mul(b_denum); + Rational::new(a_num_fixed, a_denum_fixed) + } + + pub(crate) fn div_ratio(&self, x: &Rational) -> Rational { + let a_num = &self.numerator; + let a_denum = &self.denominator; + let b_num = &x.numerator; + let b_denum = &x.denominator; + + let a_num_fixed = a_num.mul(b_denum); + let a_denum_fixed = a_denum.mul(b_num); + + Rational::new(a_num_fixed, a_denum_fixed) + } + + pub(crate) fn add(&self, x: &Rational) -> Rational { + let a_num = &self.numerator; + let a_denum = &self.denominator; + let b_num = &x.numerator; + let b_denum = &x.denominator; + + if a_num.is_zero() { + return x.clone(); + } + if b_num.is_zero() { + return self.clone(); + } + let a_num_fixed = &a_num.mul(b_denum); + let b_num_fixed = &b_num.mul(a_denum); + let a_b_num_sum = a_num_fixed.add(b_num_fixed); + let common_denum = a_denum.mul(b_denum); + Rational::new(a_b_num_sum, common_denum) + } + + pub(crate) fn sub(&self, x: &Rational) -> Rational { + let a_num = &self.numerator; + let a_denum = &self.denominator; + let b_num = &x.numerator; + let b_denum = &x.denominator; + + if a_num.is_zero() { + return x.clone(); + } + if b_num.is_zero() { + return self.clone(); + } + let a_num_fixed = &a_num.mul(b_denum); + let b_num_fixed = &b_num.mul(a_denum); + let a_b_num_diff = a_num_fixed.sub(b_num_fixed); + let common_denum = a_denum.mul(b_denum); + Rational::new(a_b_num_diff, common_denum) + } + + pub(crate) fn to_bignum_ceil(&self) -> Result { + let num = self.numerator(); + let denum = self.denominator(); + if denum.is_zero() { + return Err(JsError::from_str("Division by zero")); + } + let value = num.div_ceil(denum); + match value.as_u64() { + Some(coin) => Ok(coin), + _ => Err(JsError::from_str(&format!( + "Failed to calculate ceil from ratio {}/{}", + num.to_str(), + denum.to_str(), + ))), + } + } + + pub(crate) fn pow(&self, exp: u32) -> Rational { + let num = self.numerator.pow(exp); + let denum = self.denominator.pow(exp); + Rational::new(num, denum).reduce_minuses() + } + + pub(crate) fn is_zero(&self) -> bool { + self.numerator.is_zero() + } + + pub(crate) fn is_negative(&self) -> bool { + let is_num_negative = self.numerator.is_negative(); + let is_denum_negative = self.denominator.is_negative(); + is_num_negative ^ is_denum_negative + } + + pub(crate) fn is_negative_or_zero(&self) -> bool { + self.is_zero() || self.is_negative() + } + + #[allow(dead_code)] + pub(crate) fn to_bignum_floor(&self) -> Result { + let num = self.numerator(); + let denum = self.denominator(); + if denum.is_zero() { + return Err(JsError::from_str("Division by zero")); + } + let value = num.div_floor(denum); + match value.as_u64() { + Some(coin) => Ok(coin), + _ => Err(JsError::from_str(&format!( + "Failed to calculate floor from ratio {}/{}", + num.to_str(), + denum.to_str(), + ))), + } + } + + fn reduce_minuses(mut self) -> Self{ + if self.numerator.is_negative() && self.denominator.is_negative() { + self.numerator = self.numerator.abs(); + self.denominator = self.denominator.abs(); + } + self + } +} \ No newline at end of file diff --git a/rust/src/ser_info/mod.rs b/rust/src/ser_info/mod.rs deleted file mode 100644 index dd198c6d..00000000 --- a/rust/src/ser_info/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod types; \ No newline at end of file diff --git a/rust/src/serialization.rs b/rust/src/serialization.rs deleted file mode 100644 index cade295b..00000000 --- a/rust/src/serialization.rs +++ /dev/null @@ -1,5545 +0,0 @@ -use super::*; -use crate::utils::*; -use address::*; -use crypto::*; -use error::*; -use std::io::{Seek, SeekFrom}; - -// This file was code-generated using an experimental CDDL to rust tool: -// https://github.com/Emurgo/cddl-codegen - -impl cbor_event::se::Serialize for UnitInterval { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_tag(30u64)?; - serializer.write_array(cbor_event::Len::Len(2))?; - self.numerator.serialize(serializer)?; - self.denominator.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for UnitInterval { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let tag = raw.tag()?; - if tag != 30 { - return Err(DeserializeError::new( - "UnitInterval", - DeserializeFailure::TagMismatch { - found: tag, - expected: 30, - }, - )); - } - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("UnitInterval")) - } -} - -impl DeserializeEmbeddedGroup for UnitInterval { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - let numerator = (|| -> Result<_, DeserializeError> { Ok(BigNum::deserialize(raw)?) })() - .map_err(|e| e.annotate("numerator"))?; - let denominator = (|| -> Result<_, DeserializeError> { Ok(BigNum::deserialize(raw)?) })() - .map_err(|e| e.annotate("denominator"))?; - Ok(UnitInterval { - numerator, - denominator, - }) - } -} - -impl cbor_event::se::Serialize for Transaction { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(4))?; - self.body.serialize(serializer)?; - self.witness_set.serialize(serializer)?; - serializer.write_special(CBORSpecial::Bool(self.is_valid))?; - match &self.auxiliary_data { - Some(x) => x.serialize(serializer), - None => serializer.write_special(CBORSpecial::Null), - }?; - Ok(serializer) - } -} - -impl Deserialize for Transaction { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("Transaction")) - } -} - -impl DeserializeEmbeddedGroup for Transaction { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - let body = (|| -> Result<_, DeserializeError> { Ok(TransactionBody::deserialize(raw)?) })() - .map_err(|e| e.annotate("body"))?; - let witness_set = - (|| -> Result<_, DeserializeError> { Ok(TransactionWitnessSet::deserialize(raw)?) })() - .map_err(|e| e.annotate("witness_set"))?; - let mut checked_auxiliary_data = false; - let mut auxiliary_data = None; - let is_valid = (|| -> Result<_, DeserializeError> { - match raw.cbor_type()? == CBORType::Special { - true => { - // if it's special it can be either a bool or null. if it's null, then it's empty auxiliary data, otherwise not a valid encoding - let special = raw.special()?; - if let CBORSpecial::Bool(b) = special { - return Ok(b); - } else if special == CBORSpecial::Null { - checked_auxiliary_data = true; - return Ok(true); - } else { - return Err(DeserializeFailure::ExpectedBool.into()); - } - } - false => { - // if no special symbol was detected, it must have auxiliary data - auxiliary_data = (|| -> Result<_, DeserializeError> { - Ok(Some(AuxiliaryData::deserialize(raw)?)) - })() - .map_err(|e| e.annotate("auxiliary_data"))?; - checked_auxiliary_data = true; - return Ok(true); - } - } - })() - .map_err(|e| e.annotate("is_valid"))?; - if !checked_auxiliary_data { - // this branch is reached, if the 3rd argument was a bool. then it simply follows the rules for checking auxiliary data - auxiliary_data = (|| -> Result<_, DeserializeError> { - Ok(match raw.cbor_type()? != CBORType::Special { - true => Some(AuxiliaryData::deserialize(raw)?), - false => { - if raw.special()? != CBORSpecial::Null { - return Err(DeserializeFailure::ExpectedNull.into()); - } - None - } - }) - })() - .map_err(|e| e.annotate("auxiliary_data"))?; - } - Ok(Transaction { - body, - witness_set, - is_valid, - auxiliary_data, - }) - } -} - -impl cbor_event::se::Serialize for TransactionInputs { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for TransactionInputs { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(TransactionInput::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("TransactionInputs"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for TransactionOutputs { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for TransactionOutputs { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(TransactionOutput::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("TransactionOutputs"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for Certificates { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for Certificates { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(Certificate::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("Certificates"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for TransactionBody { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len( - 3 + opt64(&self.ttl) - + opt64(&self.certs) - + opt64(&self.withdrawals) - + opt64(&self.update) - + opt64(&self.auxiliary_data_hash) - + opt64(&self.validity_start_interval) - + opt64(&self.mint) - + opt64(&self.script_data_hash) - + opt64(&self.collateral) - + opt64(&self.required_signers) - + opt64(&self.network_id) - + opt64(&self.collateral_return) - + opt64(&self.total_collateral) - + opt64(&self.reference_inputs), - ))?; - serializer.write_unsigned_integer(0)?; - self.inputs.serialize(serializer)?; - serializer.write_unsigned_integer(1)?; - self.outputs.serialize(serializer)?; - serializer.write_unsigned_integer(2)?; - self.fee.serialize(serializer)?; - if let Some(field) = &self.ttl { - serializer.write_unsigned_integer(3)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.certs { - serializer.write_unsigned_integer(4)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.withdrawals { - serializer.write_unsigned_integer(5)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.update { - serializer.write_unsigned_integer(6)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.auxiliary_data_hash { - serializer.write_unsigned_integer(7)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.validity_start_interval { - serializer.write_unsigned_integer(8)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.mint { - serializer.write_unsigned_integer(9)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.script_data_hash { - serializer.write_unsigned_integer(11)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.collateral { - serializer.write_unsigned_integer(13)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.required_signers { - serializer.write_unsigned_integer(14)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.network_id { - serializer.write_unsigned_integer(15)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.collateral_return { - serializer.write_unsigned_integer(16)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.total_collateral { - serializer.write_unsigned_integer(17)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.reference_inputs { - serializer.write_unsigned_integer(18)?; - field.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for TransactionBody { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.map()?; - let mut read_len = CBORReadLen::new(len); - read_len.read_elems(3)?; - let mut inputs = None; - let mut outputs = None; - let mut fee = None; - let mut ttl = None; - let mut certs = None; - let mut withdrawals = None; - let mut update = None; - let mut auxiliary_data_hash = None; - let mut validity_start_interval = None; - let mut mint = None; - let mut script_data_hash = None; - let mut collateral = None; - let mut required_signers = None; - let mut network_id = None; - let mut collateral_return = None; - let mut total_collateral = None; - let mut reference_inputs = None; - let mut read = 0; - while match len { - cbor_event::Len::Len(n) => read < n as usize, - cbor_event::Len::Indefinite => true, - } { - match raw.cbor_type()? { - CBORType::UnsignedInteger => match raw.unsigned_integer()? { - 0 => { - if inputs.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); - } - inputs = Some( - (|| -> Result<_, DeserializeError> { - Ok(TransactionInputs::deserialize(raw)?) - })() - .map_err(|e| e.annotate("inputs"))?, - ); - } - 1 => { - if outputs.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); - } - outputs = Some( - (|| -> Result<_, DeserializeError> { - Ok(TransactionOutputs::deserialize(raw)?) - })() - .map_err(|e| e.annotate("outputs"))?, - ); - } - 2 => { - if fee.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); - } - fee = - Some( - (|| -> Result<_, DeserializeError> { - Ok(Coin::deserialize(raw)?) - })() - .map_err(|e| e.annotate("fee"))?, - ); - } - 3 => { - if ttl.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); - } - ttl = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(SlotBigNum::deserialize(raw)?) - })() - .map_err(|e| e.annotate("ttl"))?, - ); - } - 4 => { - if certs.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(4)).into()); - } - certs = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Certificates::deserialize(raw)?) - })() - .map_err(|e| e.annotate("certs"))?, - ); - } - 5 => { - if withdrawals.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(5)).into()); - } - withdrawals = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Withdrawals::deserialize(raw)?) - })() - .map_err(|e| e.annotate("withdrawals"))?, - ); - } - 6 => { - if update.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(6)).into()); - } - update = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Update::deserialize(raw)?) - })() - .map_err(|e| e.annotate("update"))?, - ); - } - 7 => { - if auxiliary_data_hash.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(7)).into()); - } - auxiliary_data_hash = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(AuxiliaryDataHash::deserialize(raw)?) - })() - .map_err(|e| e.annotate("auxiliary_data_hash"))?, - ); - } - 8 => { - if validity_start_interval.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(8)).into()); - } - validity_start_interval = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(SlotBigNum::deserialize(raw)?) - })() - .map_err(|e| e.annotate("validity_start_interval"))?, - ); - } - 9 => { - if mint.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(9)).into()); - } - mint = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Mint::deserialize(raw)?) - })() - .map_err(|e| e.annotate("mint"))?, - ); - } - 11 => { - if script_data_hash.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(11)).into()); - } - script_data_hash = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(ScriptDataHash::deserialize(raw)?) - })() - .map_err(|e| e.annotate("script_data_hash"))?, - ); - } - 13 => { - if collateral.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(13)).into()); - } - collateral = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(TransactionInputs::deserialize(raw)?) - })() - .map_err(|e| e.annotate("collateral"))?, - ); - } - 14 => { - if required_signers.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(14)).into()); - } - required_signers = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(RequiredSigners::deserialize(raw)?) - })() - .map_err(|e| e.annotate("required_signers"))?, - ); - } - 15 => { - if network_id.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(15)).into()); - } - network_id = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(NetworkId::deserialize(raw)?) - })() - .map_err(|e| e.annotate("network_id"))?, - ); - } - 16 => { - if collateral_return.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(16)).into()); - } - collateral_return = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(TransactionOutput::deserialize(raw)?) - })() - .map_err(|e| e.annotate("collateral_return"))?, - ); - } - 17 => { - if total_collateral.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(17)).into()); - } - total_collateral = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Coin::deserialize(raw)?) - })() - .map_err(|e| e.annotate("total_collateral"))?, - ); - } - 18 => { - if reference_inputs.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(18)).into()); - } - reference_inputs = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(TransactionInputs::deserialize(raw)?) - })() - .map_err(|e| e.annotate("reference_inputs"))?, - ); - } - unknown_key => { - return Err( - DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into() - ) - } - }, - CBORType::Text => match raw.text()?.as_str() { - unknown_key => { - return Err(DeserializeFailure::UnknownKey(Key::Str( - unknown_key.to_owned(), - )) - .into()) - } - }, - CBORType::Special => match len { - cbor_event::Len::Len(_) => { - return Err(DeserializeFailure::BreakInDefiniteLen.into()) - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => break, - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - }, - other_type => { - return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()) - } - } - read += 1; - } - let inputs = match inputs { - Some(x) => x, - None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Uint(0)).into()), - }; - let outputs = match outputs { - Some(x) => x, - None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Uint(1)).into()), - }; - let fee = match fee { - Some(x) => x, - None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Uint(2)).into()), - }; - read_len.finish()?; - Ok(Self { - inputs, - outputs, - fee, - ttl, - certs, - withdrawals, - update, - auxiliary_data_hash, - validity_start_interval, - mint, - script_data_hash, - collateral, - required_signers, - network_id, - collateral_return, - total_collateral, - reference_inputs, - }) - })() - .map_err(|e| e.annotate("TransactionBody")) - } -} - -impl cbor_event::se::Serialize for TransactionInput { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.transaction_id.serialize(serializer)?; - self.index.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for TransactionInput { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("TransactionInput")) - } -} - -impl DeserializeEmbeddedGroup for TransactionInput { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - let transaction_id = - (|| -> Result<_, DeserializeError> { Ok(TransactionHash::deserialize(raw)?) })() - .map_err(|e| e.annotate("transaction_id"))?; - let index = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() - .map_err(|e| e.annotate("index"))?; - Ok(TransactionInput { - transaction_id, - index, - }) - } -} - -impl cbor_event::se::Serialize for TransactionOutput { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - if self.has_plutus_data() || self.has_script_ref() { - //post alonzo output - let map_len = 2 + opt64(&self.plutus_data) + opt64(&self.script_ref); - serializer.write_map(cbor_event::Len::Len(map_len))?; - serializer.write_unsigned_integer(0)?; - self.address.serialize(serializer)?; - serializer.write_unsigned_integer(1)?; - self.amount.serialize(serializer)?; - if let Some(field) = &self.plutus_data { - serializer.write_unsigned_integer(2)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.script_ref { - serializer.write_unsigned_integer(3)?; - field.serialize(serializer)?; - } - } else { - //lagacy output - let data_hash = &self.data_hash(); - serializer.write_array(cbor_event::Len::Len(2 + opt64(&data_hash)))?; - self.address.serialize(serializer)?; - self.amount.serialize(serializer)?; - if let Some(pure_data_hash) = data_hash { - pure_data_hash.serialize(serializer)?; - } - } - Ok(serializer) - } -} - -// this is used when deserializing it on its own, but the more likely case -// is when it's done via TransactionOutputs -impl Deserialize for TransactionOutput { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - match raw.cbor_type()? { - CBORType::Array => { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - } - CBORType::Map => deserialize_as_postalonzo_output(raw), - cbor_type => Err(DeserializeFailure::UnexpectedKeyType(cbor_type).into()), - } - })() - .map_err(|e| e.annotate("TransactionOutput")) - } -} - -// this is used by both TransactionOutput (on its own)'s deserialize -// but also for TransactionOutputs -// This implementation was hand-coded since cddl-codegen doesn't support deserialization -// with array-encoded types with optional fields, due to the complexity. -// This is made worse as this is a plain group... -impl DeserializeEmbeddedGroup for TransactionOutput { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - let address = (|| -> Result<_, DeserializeError> { Ok(Address::deserialize(raw)?) })() - .map_err(|e| e.annotate("address"))?; - let amount = (|| -> Result<_, DeserializeError> { Ok(Value::deserialize(raw)?) })() - .map_err(|e| e.annotate("amount"))?; - // there are only two cases so far where this is used: - // 1) on its own inside of TransactionOutput's Deserialize trait (only used if someone calls to_bytes() on it) - // 2) from TransactionOutput's deserialization - // in 1) we would encounter an array-end (or track it for definite deserialization - which we don't do right now) - // and in 2) we would encounter the same OR we would encounter the next TransactionOutput in the array - // Unfortunately, both address and data hash are bytes type, so we can't just check the type, but instead - // must check the length, and backtrack if that wasn't the case. - let data_hash = match raw.cbor_type() { - Ok(cbor_event::Type::Bytes) => { - let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); - let bytes = raw.bytes().unwrap(); - if bytes.len() == DataHash::BYTE_COUNT { - Some(DataOption::DataHash(DataHash(bytes[..DataHash::BYTE_COUNT].try_into().unwrap()))) - } else { - // This is an address of the next output in sequence, which luckily is > 32 bytes so there's no confusion - // Go to previous place in array then carry on - raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(); - None - } - }, - // not possibly a data hash - Ok(_) | - // end of input - Err(_) => None, - }; - Ok(TransactionOutput { - address, - amount, - plutus_data: data_hash, - script_ref: None, - serialization_format: Some(CborContainerType::Array), - }) - } -} - -fn deserialize_as_postalonzo_output( - raw: &mut Deserializer, -) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.map()?; - let mut read_len = CBORReadLen::new(len); - let mut address = None; - let mut amount = None; - let mut data = None; - let mut script_ref = None; - let mut read = 0; - while match len { - cbor_event::Len::Len(n) => read < n as usize, - cbor_event::Len::Indefinite => true, - } { - match raw.cbor_type()? { - CBORType::UnsignedInteger => match raw.unsigned_integer()? { - 0 => { - if address.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); - } - address = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Address::deserialize(raw)?) - })() - .map_err(|e| e.annotate("address"))?, - ); - } - 1 => { - if amount.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); - } - amount = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Value::deserialize(raw)?) - })() - .map_err(|e| e.annotate("amount"))?, - ); - } - 2 => { - if data.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); - } - data = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(DataOption::deserialize(raw)?) - })() - .map_err(|e| e.annotate("data"))?, - ); - } - 3 => { - if script_ref.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); - } - script_ref = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(ScriptRef::deserialize(raw)?) - })() - .map_err(|e| e.annotate("script_ref"))?, - ); - } - unknown_key => { - return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()) - } - }, - other_type => return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()), - } - read += 1; - } - let address = match address { - Some(x) => x, - None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Uint(0)).into()), - }; - let amount = match amount { - Some(x) => x, - None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Uint(1)).into()), - }; - - read_len.finish()?; - Ok(TransactionOutput { - address, - amount, - plutus_data: data, - script_ref, - serialization_format: Some(CborContainerType::Map), - }) - })() - .map_err(|e| e.annotate("TransactionOutput")) -} - -impl Deserialize for DataOption { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - if let cbor_event::Len::Len(n) = len { - if n != 2 { - return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( - 2, - len, - "[id, datum_or_hash]", - )) - .into()); - } - } - let datum = match raw.unsigned_integer()? { - 0 => DataOption::DataHash(DataHash::deserialize(raw)?), - 1 => { - match raw.tag()? { - //bytes string tag - 24 => { - let data = (|| -> Result<_, DeserializeError> { - Ok(from_bytes(&raw.bytes()?)?) - })() - .map_err(|e| e.annotate("PlutusData"))?; - DataOption::Data(data) - } - tag => { - return Err(DeserializeFailure::TagMismatch { - found: tag, - expected: 24, - } - .into()); - } - } - } - n => { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(n), - expected: Key::Uint(0), - } - .into()) - } - }; - if let cbor_event::Len::Indefinite = len { - if raw.special()? != CBORSpecial::Break { - return Err(DeserializeFailure::EndingBreakMissing.into()); - } - } - Ok(datum) - })() - .map_err(|e| e.annotate("DataOption")) - } -} - -impl cbor_event::se::Serialize for DataOption { - fn serialize<'a, W: Write + Sized>( - &self, - serializer: &'a mut Serializer, - ) -> cbor_event::Result<&'a mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - match &self { - DataOption::DataHash(data_hash) => { - serializer.write_unsigned_integer(0)?; - data_hash.serialize(serializer)?; - } - DataOption::Data(data) => { - serializer.write_unsigned_integer(1)?; - let bytes = data.to_bytes(); - serializer.write_tag(24)?.write_bytes(&bytes)?; - } - } - Ok(serializer) - } -} - -impl Deserialize for ScriptRefEnum { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - if let cbor_event::Len::Len(n) = len { - if n != 2 { - return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( - 2, - len, - "[id, native_or_putus_script]", - )) - .into()); - } - } - let script_ref = match raw.unsigned_integer()? { - 0 => ScriptRefEnum::NativeScript(NativeScript::deserialize(raw)?), - 1 => ScriptRefEnum::PlutusScript(PlutusScript::deserialize(raw)?), - 2 => ScriptRefEnum::PlutusScript( - PlutusScript::deserialize(raw)?.clone_as_version(&Language::new_plutus_v2()), - ), - n => { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(n), - expected: Key::Uint(0), - } - .into()) - } - }; - if let cbor_event::Len::Indefinite = len { - if raw.special()? != CBORSpecial::Break { - return Err(DeserializeFailure::EndingBreakMissing.into()); - } - } - Ok(script_ref) - })() - .map_err(|e| e.annotate("ScriptRefEnum")) - } -} - -impl cbor_event::se::Serialize for ScriptRefEnum { - fn serialize<'a, W: Write + Sized>( - &self, - serializer: &'a mut Serializer, - ) -> cbor_event::Result<&'a mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - match &self { - ScriptRefEnum::NativeScript(native_script) => { - serializer.write_unsigned_integer(0)?; - native_script.serialize(serializer)?; - } - ScriptRefEnum::PlutusScript(plutus_script) => { - serializer.write_unsigned_integer(plutus_script.script_namespace() as u64)?; - plutus_script.serialize(serializer)?; - } - } - Ok(serializer) - } -} - -impl Deserialize for ScriptRef { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - match raw.tag()? { - //bytes string tag - 24 => Ok(ScriptRef(from_bytes(&raw.bytes()?)?)), - tag => { - return Err(DeserializeFailure::TagMismatch { - found: tag, - expected: 24, - } - .into()); - } - } - })() - .map_err(|e| e.annotate("ScriptRef")) - } -} - -impl cbor_event::se::Serialize for ScriptRef { - fn serialize<'a, W: Write + Sized>( - &self, - serializer: &'a mut Serializer, - ) -> cbor_event::Result<&'a mut Serializer> { - let bytes = to_bytes(&self.0); - serializer.write_tag(24)?.write_bytes(&bytes)?; - Ok(serializer) - } -} - -impl cbor_event::se::Serialize for StakeRegistration { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for StakeRegistration { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(0u64)?; - self.stake_credential.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for StakeRegistration { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("StakeRegistration")) - } -} - -impl DeserializeEmbeddedGroup for StakeRegistration { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 0 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(0), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let stake_credential = - (|| -> Result<_, DeserializeError> { Ok(StakeCredential::deserialize(raw)?) })() - .map_err(|e| e.annotate("stake_credential"))?; - Ok(StakeRegistration { stake_credential }) - } -} - -impl cbor_event::se::Serialize for StakeDeregistration { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for StakeDeregistration { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(1u64)?; - self.stake_credential.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for StakeDeregistration { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("StakeDeregistration")) - } -} - -impl DeserializeEmbeddedGroup for StakeDeregistration { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 1 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(1), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let stake_credential = - (|| -> Result<_, DeserializeError> { Ok(StakeCredential::deserialize(raw)?) })() - .map_err(|e| e.annotate("stake_credential"))?; - Ok(StakeDeregistration { stake_credential }) - } -} - -impl cbor_event::se::Serialize for StakeDelegation { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(3))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for StakeDelegation { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(2u64)?; - self.stake_credential.serialize(serializer)?; - self.pool_keyhash.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for StakeDelegation { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("StakeDelegation")) - } -} - -impl DeserializeEmbeddedGroup for StakeDelegation { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 2 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(2), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let stake_credential = - (|| -> Result<_, DeserializeError> { Ok(StakeCredential::deserialize(raw)?) })() - .map_err(|e| e.annotate("stake_credential"))?; - let pool_keyhash = - (|| -> Result<_, DeserializeError> { Ok(Ed25519KeyHash::deserialize(raw)?) })() - .map_err(|e| e.annotate("pool_keyhash"))?; - Ok(StakeDelegation { - stake_credential, - pool_keyhash, - }) - } -} - -impl cbor_event::se::Serialize for Ed25519KeyHashes { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for Ed25519KeyHashes { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(Ed25519KeyHash::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("Ed25519KeyHashes"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for Relays { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for Relays { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(Relay::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("Relays"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for PoolParams { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(9))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for PoolParams { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - self.operator.serialize(serializer)?; - self.vrf_keyhash.serialize(serializer)?; - self.pledge.serialize(serializer)?; - self.cost.serialize(serializer)?; - self.margin.serialize(serializer)?; - self.reward_account.serialize(serializer)?; - self.pool_owners.serialize(serializer)?; - self.relays.serialize(serializer)?; - match &self.pool_metadata { - Some(x) => x.serialize(serializer), - None => serializer.write_special(CBORSpecial::Null), - }?; - Ok(serializer) - } -} - -impl Deserialize for PoolParams { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("PoolParams")) - } -} - -impl DeserializeEmbeddedGroup for PoolParams { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - let operator = - (|| -> Result<_, DeserializeError> { Ok(Ed25519KeyHash::deserialize(raw)?) })() - .map_err(|e| e.annotate("operator"))?; - let vrf_keyhash = - (|| -> Result<_, DeserializeError> { Ok(VRFKeyHash::deserialize(raw)?) })() - .map_err(|e| e.annotate("vrf_keyhash"))?; - let pledge = (|| -> Result<_, DeserializeError> { Ok(Coin::deserialize(raw)?) })() - .map_err(|e| e.annotate("pledge"))?; - let cost = (|| -> Result<_, DeserializeError> { Ok(Coin::deserialize(raw)?) })() - .map_err(|e| e.annotate("cost"))?; - let margin = (|| -> Result<_, DeserializeError> { Ok(UnitInterval::deserialize(raw)?) })() - .map_err(|e| e.annotate("margin"))?; - let reward_account = - (|| -> Result<_, DeserializeError> { Ok(RewardAddress::deserialize(raw)?) })() - .map_err(|e| e.annotate("reward_account"))?; - let pool_owners = - (|| -> Result<_, DeserializeError> { Ok(Ed25519KeyHashes::deserialize(raw)?) })() - .map_err(|e| e.annotate("pool_owners"))?; - let relays = (|| -> Result<_, DeserializeError> { Ok(Relays::deserialize(raw)?) })() - .map_err(|e| e.annotate("relays"))?; - let pool_metadata = (|| -> Result<_, DeserializeError> { - Ok(match raw.cbor_type()? != CBORType::Special { - true => Some(PoolMetadata::deserialize(raw)?), - false => { - if raw.special()? != CBORSpecial::Null { - return Err(DeserializeFailure::ExpectedNull.into()); - } - None - } - }) - })() - .map_err(|e| e.annotate("pool_metadata"))?; - Ok(PoolParams { - operator, - vrf_keyhash, - pledge, - cost, - margin, - reward_account, - pool_owners, - relays, - pool_metadata, - }) - } -} - -impl cbor_event::se::Serialize for PoolRegistration { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(10))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for PoolRegistration { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(3u64)?; - self.pool_params.serialize_as_embedded_group(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for PoolRegistration { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("PoolRegistration")) - } -} - -impl DeserializeEmbeddedGroup for PoolRegistration { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - len: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 3 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(3), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let pool_params = (|| -> Result<_, DeserializeError> { - Ok(PoolParams::deserialize_as_embedded_group(raw, len)?) - })() - .map_err(|e| e.annotate("pool_params"))?; - Ok(PoolRegistration { pool_params }) - } -} - -impl cbor_event::se::Serialize for PoolRetirement { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(3))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for PoolRetirement { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(4u64)?; - self.pool_keyhash.serialize(serializer)?; - self.epoch.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for PoolRetirement { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("PoolRetirement")) - } -} - -impl DeserializeEmbeddedGroup for PoolRetirement { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 4 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(4), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let pool_keyhash = - (|| -> Result<_, DeserializeError> { Ok(Ed25519KeyHash::deserialize(raw)?) })() - .map_err(|e| e.annotate("pool_keyhash"))?; - let epoch = (|| -> Result<_, DeserializeError> { Ok(Epoch::deserialize(raw)?) })() - .map_err(|e| e.annotate("epoch"))?; - Ok(PoolRetirement { - pool_keyhash, - epoch, - }) - } -} - -impl cbor_event::se::Serialize for GenesisKeyDelegation { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(4))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for GenesisKeyDelegation { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(5u64)?; - self.genesishash.serialize(serializer)?; - self.genesis_delegate_hash.serialize(serializer)?; - self.vrf_keyhash.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for GenesisKeyDelegation { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("GenesisKeyDelegation")) - } -} - -impl DeserializeEmbeddedGroup for GenesisKeyDelegation { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 5 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(5), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let genesishash = - (|| -> Result<_, DeserializeError> { Ok(GenesisHash::deserialize(raw)?) })() - .map_err(|e| e.annotate("genesishash"))?; - let genesis_delegate_hash = - (|| -> Result<_, DeserializeError> { Ok(GenesisDelegateHash::deserialize(raw)?) })() - .map_err(|e| e.annotate("genesis_delegate_hash"))?; - let vrf_keyhash = - (|| -> Result<_, DeserializeError> { Ok(VRFKeyHash::deserialize(raw)?) })() - .map_err(|e| e.annotate("vrf_keyhash"))?; - Ok(GenesisKeyDelegation { - genesishash, - genesis_delegate_hash, - vrf_keyhash, - }) - } -} - -impl cbor_event::se::Serialize for MoveInstantaneousRewardsCert { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for MoveInstantaneousRewardsCert { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(6u64)?; - self.move_instantaneous_reward.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for MoveInstantaneousRewardsCert { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("MoveInstantaneousRewardsCert")) - } -} - -impl DeserializeEmbeddedGroup for MoveInstantaneousRewardsCert { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 6 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(6), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let move_instantaneous_reward = - (|| -> Result<_, DeserializeError> { Ok(MoveInstantaneousReward::deserialize(raw)?) })( - ) - .map_err(|e| e.annotate("move_instantaneous_reward"))?; - Ok(MoveInstantaneousRewardsCert { - move_instantaneous_reward, - }) - } -} - -impl cbor_event::se::Serialize for CertificateEnum { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - match self { - CertificateEnum::StakeRegistration(x) => x.serialize(serializer), - CertificateEnum::StakeDeregistration(x) => x.serialize(serializer), - CertificateEnum::StakeDelegation(x) => x.serialize(serializer), - CertificateEnum::PoolRegistration(x) => x.serialize(serializer), - CertificateEnum::PoolRetirement(x) => x.serialize(serializer), - CertificateEnum::GenesisKeyDelegation(x) => x.serialize(serializer), - CertificateEnum::MoveInstantaneousRewardsCert(x) => x.serialize(serializer), - } - } -} - -impl Deserialize for CertificateEnum { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("CertificateEnum")) - } -} - -impl DeserializeEmbeddedGroup for CertificateEnum { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - len: cbor_event::Len, - ) -> Result { - let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(StakeRegistration::deserialize_as_embedded_group(raw, len)?) - })(raw) - { - Ok(variant) => return Ok(CertificateEnum::StakeRegistration(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(StakeDeregistration::deserialize_as_embedded_group( - raw, len, - )?) - })(raw) - { - Ok(variant) => return Ok(CertificateEnum::StakeDeregistration(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(StakeDelegation::deserialize_as_embedded_group(raw, len)?) - })(raw) - { - Ok(variant) => return Ok(CertificateEnum::StakeDelegation(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(PoolRegistration::deserialize_as_embedded_group(raw, len)?) - })(raw) - { - Ok(variant) => return Ok(CertificateEnum::PoolRegistration(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(PoolRetirement::deserialize_as_embedded_group(raw, len)?) - })(raw) - { - Ok(variant) => return Ok(CertificateEnum::PoolRetirement(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(GenesisKeyDelegation::deserialize_as_embedded_group( - raw, len, - )?) - })(raw) - { - Ok(variant) => return Ok(CertificateEnum::GenesisKeyDelegation(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(MoveInstantaneousRewardsCert::deserialize_as_embedded_group( - raw, len, - )?) - })(raw) - { - Ok(variant) => return Ok(CertificateEnum::MoveInstantaneousRewardsCert(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - Err(DeserializeError::new( - "CertificateEnum", - DeserializeFailure::NoVariantMatched.into(), - )) - } -} - -impl cbor_event::se::Serialize for Certificate { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - self.0.serialize(serializer) - } -} - -impl Deserialize for Certificate { - fn deserialize(raw: &mut Deserializer) -> Result { - Ok(Self(CertificateEnum::deserialize(raw)?)) - } -} - -impl cbor_event::se::Serialize for StakeCredentials { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for StakeCredentials { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(StakeCredential::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("StakeCredentials"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for MIRToStakeCredentials { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(self.rewards.len() as u64))?; - for (key, value) in &self.rewards { - key.serialize(serializer)?; - value.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for MIRToStakeCredentials { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let mut table = linked_hash_map::LinkedHashMap::new(); - let len = raw.map()?; - while match len { - cbor_event::Len::Len(n) => table.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - let key = StakeCredential::deserialize(raw)?; - let value = DeltaCoin::deserialize(raw)?; - if table.insert(key.clone(), value).is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Str(format!( - "StakeCred: {} (hex bytes)", - hex::encode(key.to_bytes()) - ))) - .into()); - } - } - Ok(Self { rewards: table }) - })() - .map_err(|e| e.annotate("MIRToStakeCredentials")) - } -} - -impl cbor_event::se::Serialize for MoveInstantaneousReward { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - match self.pot { - MIRPot::Reserves => serializer.write_unsigned_integer(0u64), - MIRPot::Treasury => serializer.write_unsigned_integer(1u64), - }?; - match &self.variant { - MIREnum::ToOtherPot(amount) => amount.serialize(serializer), - MIREnum::ToStakeCredentials(amounts) => amounts.serialize(serializer), - } - } -} - -impl Deserialize for MoveInstantaneousReward { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let outer_len = raw.array()?; - let pot = match raw.unsigned_integer()? { - 0 => MIRPot::Reserves, - 1 => MIRPot::Treasury, - n => return Err(DeserializeFailure::UnknownKey(Key::Uint(n)).into()), - }; - let variant = match raw.cbor_type()? { - CBORType::UnsignedInteger => MIREnum::ToOtherPot(Coin::deserialize(raw)?), - CBORType::Map => { - MIREnum::ToStakeCredentials(MIRToStakeCredentials::deserialize(raw)?) - } - _ => return Err(DeserializeFailure::NoVariantMatched.into()), - }; - match outer_len { - cbor_event::Len::Len(n) => { - if n != 2 { - return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( - n, - outer_len, - "MoveInstantaneousReward", - )) - .into()); - } - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - }; - Ok(Self { pot, variant }) - })() - .map_err(|e| e.annotate("MoveInstantaneousReward")) - } -} - -impl cbor_event::se::Serialize for Ipv4 { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_bytes(&self.0) - } -} - -impl Deserialize for Ipv4 { - fn deserialize(raw: &mut Deserializer) -> Result { - Self::new_impl(raw.bytes()?) - } -} - -impl cbor_event::se::Serialize for Ipv6 { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_bytes(&self.0) - } -} - -impl Deserialize for Ipv6 { - fn deserialize(raw: &mut Deserializer) -> Result { - Self::new_impl(raw.bytes()?) - } -} - -impl cbor_event::se::Serialize for DNSRecordAorAAAA { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_text(&self.0) - } -} - -impl Deserialize for DNSRecordAorAAAA { - fn deserialize(raw: &mut Deserializer) -> Result { - Self::new_impl(raw.text()?) - } -} - -impl cbor_event::se::Serialize for DNSRecordSRV { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_text(&self.0) - } -} - -impl Deserialize for DNSRecordSRV { - fn deserialize(raw: &mut Deserializer) -> Result { - Self::new_impl(raw.text()?) - } -} - -impl cbor_event::se::Serialize for URL { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_text(&self.0) - } -} - -impl Deserialize for URL { - fn deserialize(raw: &mut Deserializer) -> Result { - Self::new_impl(raw.text()?) - } -} - -impl cbor_event::se::Serialize for SingleHostAddr { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(4))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for SingleHostAddr { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(0u64)?; - match &self.port { - Some(x) => x.serialize(serializer), - None => serializer.write_special(CBORSpecial::Null), - }?; - match &self.ipv4 { - Some(x) => x.serialize(serializer), - None => serializer.write_special(CBORSpecial::Null), - }?; - match &self.ipv6 { - Some(x) => x.serialize(serializer), - None => serializer.write_special(CBORSpecial::Null), - }?; - Ok(serializer) - } -} - -impl Deserialize for SingleHostAddr { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("SingleHostAddr")) - } -} - -impl DeserializeEmbeddedGroup for SingleHostAddr { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 0 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(0), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let port = (|| -> Result<_, DeserializeError> { - Ok(match raw.cbor_type()? != CBORType::Special { - true => Some(Port::deserialize(raw)?), - false => { - if raw.special()? != CBORSpecial::Null { - return Err(DeserializeFailure::ExpectedNull.into()); - } - None - } - }) - })() - .map_err(|e| e.annotate("port"))?; - let ipv4 = (|| -> Result<_, DeserializeError> { - Ok(match raw.cbor_type()? != CBORType::Special { - true => Some(Ipv4::deserialize(raw)?), - false => { - if raw.special()? != CBORSpecial::Null { - return Err(DeserializeFailure::ExpectedNull.into()); - } - None - } - }) - })() - .map_err(|e| e.annotate("ipv4"))?; - let ipv6 = (|| -> Result<_, DeserializeError> { - Ok(match raw.cbor_type()? != CBORType::Special { - true => Some(Ipv6::deserialize(raw)?), - false => { - if raw.special()? != CBORSpecial::Null { - return Err(DeserializeFailure::ExpectedNull.into()); - } - None - } - }) - })() - .map_err(|e| e.annotate("ipv6"))?; - Ok(SingleHostAddr { port, ipv4, ipv6 }) - } -} - -impl cbor_event::se::Serialize for SingleHostName { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(3))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for SingleHostName { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(1u64)?; - match &self.port { - Some(x) => x.serialize(serializer), - None => serializer.write_special(CBORSpecial::Null), - }?; - self.dns_name.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for SingleHostName { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("SingleHostName")) - } -} - -impl DeserializeEmbeddedGroup for SingleHostName { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 1 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(1), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let port = (|| -> Result<_, DeserializeError> { - Ok(match raw.cbor_type()? != CBORType::Special { - true => Some(Port::deserialize(raw)?), - false => { - if raw.special()? != CBORSpecial::Null { - return Err(DeserializeFailure::ExpectedNull.into()); - } - None - } - }) - })() - .map_err(|e| e.annotate("port"))?; - let dns_name = - (|| -> Result<_, DeserializeError> { Ok(DNSRecordAorAAAA::deserialize(raw)?) })() - .map_err(|e| e.annotate("dns_name"))?; - Ok(SingleHostName { port, dns_name }) - } -} - -impl cbor_event::se::Serialize for MultiHostName { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for MultiHostName { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(2u64)?; - self.dns_name.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for MultiHostName { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("MultiHostName")) - } -} - -impl DeserializeEmbeddedGroup for MultiHostName { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 2 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(2), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let dns_name = - (|| -> Result<_, DeserializeError> { Ok(DNSRecordSRV::deserialize(raw)?) })() - .map_err(|e| e.annotate("dns_name"))?; - Ok(MultiHostName { dns_name }) - } -} - -impl cbor_event::se::Serialize for RelayEnum { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - match self { - RelayEnum::SingleHostAddr(x) => x.serialize(serializer), - RelayEnum::SingleHostName(x) => x.serialize(serializer), - RelayEnum::MultiHostName(x) => x.serialize(serializer), - } - } -} - -impl Deserialize for RelayEnum { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("RelayEnum")) - } -} - -impl DeserializeEmbeddedGroup for RelayEnum { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - len: cbor_event::Len, - ) -> Result { - let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(SingleHostAddr::deserialize_as_embedded_group(raw, len)?) - })(raw) - { - Ok(variant) => return Ok(RelayEnum::SingleHostAddr(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(SingleHostName::deserialize_as_embedded_group(raw, len)?) - })(raw) - { - Ok(variant) => return Ok(RelayEnum::SingleHostName(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(MultiHostName::deserialize_as_embedded_group(raw, len)?) - })(raw) - { - Ok(variant) => return Ok(RelayEnum::MultiHostName(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - Err(DeserializeError::new( - "RelayEnum", - DeserializeFailure::NoVariantMatched.into(), - )) - } -} - -impl cbor_event::se::Serialize for Relay { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - self.0.serialize(serializer) - } -} - -impl Deserialize for Relay { - fn deserialize(raw: &mut Deserializer) -> Result { - Ok(Self(RelayEnum::deserialize(raw)?)) - } -} - -impl cbor_event::se::Serialize for PoolMetadata { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.url.serialize(serializer)?; - self.pool_metadata_hash.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for PoolMetadata { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("PoolMetadata")) - } -} - -impl DeserializeEmbeddedGroup for PoolMetadata { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - let url = (|| -> Result<_, DeserializeError> { Ok(URL::deserialize(raw)?) })() - .map_err(|e| e.annotate("url"))?; - let pool_metadata_hash = - (|| -> Result<_, DeserializeError> { Ok(PoolMetadataHash::deserialize(raw)?) })() - .map_err(|e| e.annotate("pool_metadata_hash"))?; - Ok(PoolMetadata { - url, - pool_metadata_hash, - }) - } -} - -impl cbor_event::se::Serialize for RewardAddresses { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for RewardAddresses { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(RewardAddress::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("RewardAddresses"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for Withdrawals { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; - for (key, value) in &self.0 { - key.serialize(serializer)?; - value.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for Withdrawals { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut table = linked_hash_map::LinkedHashMap::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.map()?; - while match len { - cbor_event::Len::Len(n) => table.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - let key = RewardAddress::deserialize(raw)?; - let value = Coin::deserialize(raw)?; - if table.insert(key.clone(), value).is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( - "some complicated/unsupported type", - ))) - .into()); - } - } - Ok(()) - })() - .map_err(|e| e.annotate("Withdrawals"))?; - Ok(Self(table)) - } -} - -impl cbor_event::se::Serialize for TransactionWitnessSet { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - let plutus_added_length = match &self.plutus_scripts { - Some(scripts) => 1 + (scripts.has_version(&Language::new_plutus_v2()) as u64), - _ => 0, - }; - serializer.write_map(cbor_event::Len::Len( - opt64(&self.vkeys) - + opt64(&self.native_scripts) - + opt64(&self.bootstraps) - + opt64(&self.plutus_data) - + opt64(&self.redeemers) - + plutus_added_length, - ))?; - if let Some(field) = &self.vkeys { - serializer.write_unsigned_integer(0)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.native_scripts { - serializer.write_unsigned_integer(1)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.bootstraps { - serializer.write_unsigned_integer(2)?; - field.serialize(serializer)?; - } - if let Some(plutus_scripts) = &self.plutus_scripts { - serializer.write_unsigned_integer(3)?; - plutus_scripts - .by_version(&Language::new_plutus_v1()) - .serialize(serializer)?; - if plutus_added_length > 1 { - serializer.write_unsigned_integer(6)?; - plutus_scripts - .by_version(&Language::new_plutus_v2()) - .serialize(serializer)?; - } - } - if let Some(field) = &self.plutus_data { - serializer.write_unsigned_integer(4)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.redeemers { - serializer.write_unsigned_integer(5)?; - field.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for TransactionWitnessSet { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.map()?; - let mut read_len = CBORReadLen::new(len); - let mut vkeys = None; - let mut native_scripts = None; - let mut bootstraps = None; - let mut plutus_scripts_v1 = None; - let mut plutus_scripts_v2 = None; - let mut plutus_data = None; - let mut redeemers = None; - let mut read = 0; - while match len { - cbor_event::Len::Len(n) => read < n as usize, - cbor_event::Len::Indefinite => true, - } { - match raw.cbor_type()? { - CBORType::UnsignedInteger => match raw.unsigned_integer()? { - 0 => { - if vkeys.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); - } - vkeys = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Vkeywitnesses::deserialize(raw)?) - })() - .map_err(|e| e.annotate("vkeys"))?, - ); - } - 1 => { - if native_scripts.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); - } - native_scripts = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(NativeScripts::deserialize(raw)?) - })() - .map_err(|e| e.annotate("native_scripts"))?, - ); - } - 2 => { - if bootstraps.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); - } - bootstraps = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(BootstrapWitnesses::deserialize(raw)?) - })() - .map_err(|e| e.annotate("bootstraps"))?, - ); - } - 3 => { - if plutus_scripts_v1.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); - } - plutus_scripts_v1 = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(PlutusScripts::deserialize(raw)?) - })() - .map_err(|e| e.annotate("plutus_scripts_v1"))?, - ); - } - 4 => { - if plutus_data.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(4)).into()); - } - plutus_data = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(PlutusList::deserialize(raw)?) - })() - .map_err(|e| e.annotate("plutus_data"))?, - ); - } - 5 => { - if redeemers.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(5)).into()); - } - redeemers = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Redeemers::deserialize(raw)?) - })() - .map_err(|e| e.annotate("redeemers"))?, - ); - } - 6 => { - if plutus_scripts_v2.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(6)).into()); - } - plutus_scripts_v2 = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(PlutusScripts::deserialize(raw)? - .map_as_version(&Language::new_plutus_v2())) - })() - .map_err(|e| e.annotate("plutus_scripts_v2"))?, - ); - } - unknown_key => { - return Err( - DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into() - ) - } - }, - CBORType::Text => match raw.text()?.as_str() { - unknown_key => { - return Err(DeserializeFailure::UnknownKey(Key::Str( - unknown_key.to_owned(), - )) - .into()) - } - }, - CBORType::Special => match len { - cbor_event::Len::Len(_) => { - return Err(DeserializeFailure::BreakInDefiniteLen.into()) - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => break, - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - }, - other_type => { - return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()) - } - } - read += 1; - } - read_len.finish()?; - let plutus_scripts = match (plutus_scripts_v1, plutus_scripts_v2) { - (Some(v1), Some(v2)) => Some(v1.merge(&v2)), - (Some(v1), _) => Some(v1), - (_, Some(v2)) => Some(v2), - _ => None, - }; - Ok(Self { - vkeys, - native_scripts, - bootstraps, - plutus_scripts, - plutus_data, - redeemers, - }) - })() - .map_err(|e| e.annotate("TransactionWitnessSet")) - } -} - -impl cbor_event::se::Serialize for ScriptPubkey { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for ScriptPubkey { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(0u64)?; - self.addr_keyhash.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for ScriptPubkey { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let mut read_len = CBORReadLen::new(len); - read_len.read_elems(2)?; - let ret = Self::deserialize_as_embedded_group(raw, /*mut read_len, */ len); - match len { - cbor_event::Len::Len(_) => read_len.finish()?, - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => (), - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("ScriptPubkey")) - } -} - -impl DeserializeEmbeddedGroup for ScriptPubkey { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - /*read_len: &mut CBORReadLen, */ _: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 0 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(0), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let addr_keyhash = - (|| -> Result<_, DeserializeError> { Ok(Ed25519KeyHash::deserialize(raw)?) })() - .map_err(|e| e.annotate("addr_keyhash"))?; - Ok(ScriptPubkey { addr_keyhash }) - } -} - -impl cbor_event::se::Serialize for ScriptAll { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for ScriptAll { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(1u64)?; - self.native_scripts.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for ScriptAll { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let mut read_len = CBORReadLen::new(len); - read_len.read_elems(2)?; - let ret = Self::deserialize_as_embedded_group(raw, /*mut read_len, */ len); - match len { - cbor_event::Len::Len(_) => read_len.finish()?, - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => (), - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("ScriptAll")) - } -} - -impl DeserializeEmbeddedGroup for ScriptAll { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - /*read_len: &mut CBORReadLen, */ _: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 1 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(1), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let native_scripts = - (|| -> Result<_, DeserializeError> { Ok(NativeScripts::deserialize(raw)?) })() - .map_err(|e| e.annotate("native_scripts"))?; - Ok(ScriptAll { native_scripts }) - } -} - -impl cbor_event::se::Serialize for ScriptAny { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for ScriptAny { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(2u64)?; - self.native_scripts.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for ScriptAny { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let mut read_len = CBORReadLen::new(len); - read_len.read_elems(2)?; - let ret = Self::deserialize_as_embedded_group(raw, /*mut read_len, */ len); - match len { - cbor_event::Len::Len(_) => read_len.finish()?, - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => (), - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("ScriptAny")) - } -} - -impl DeserializeEmbeddedGroup for ScriptAny { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - /*/*read_len: &mut CBORReadLen, */*/ _: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 2 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(2), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let native_scripts = - (|| -> Result<_, DeserializeError> { Ok(NativeScripts::deserialize(raw)?) })() - .map_err(|e| e.annotate("native_scripts"))?; - Ok(ScriptAny { native_scripts }) - } -} - -impl cbor_event::se::Serialize for ScriptNOfK { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(3))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for ScriptNOfK { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(3u64)?; - self.n.serialize(serializer)?; - self.native_scripts.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for ScriptNOfK { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let mut read_len = CBORReadLen::new(len); - read_len.read_elems(3)?; - let ret = Self::deserialize_as_embedded_group(raw, /*mut read_len, */ len); - match len { - cbor_event::Len::Len(_) => read_len.finish()?, - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => (), - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("ScriptNOfK")) - } -} - -impl DeserializeEmbeddedGroup for ScriptNOfK { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - /*read_len: &mut CBORReadLen, */ _: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 3 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(3), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let n = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() - .map_err(|e| e.annotate("n"))?; - let native_scripts = - (|| -> Result<_, DeserializeError> { Ok(NativeScripts::deserialize(raw)?) })() - .map_err(|e| e.annotate("native_scripts"))?; - Ok(ScriptNOfK { n, native_scripts }) - } -} - -impl cbor_event::se::Serialize for TimelockStart { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for TimelockStart { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(4u64)?; - self.slot.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for TimelockStart { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let mut read_len = CBORReadLen::new(len); - read_len.read_elems(2)?; - let ret = Self::deserialize_as_embedded_group(raw, /*mut read_len, */ len); - match len { - cbor_event::Len::Len(_) => read_len.finish()?, - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => (), - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("TimelockStart")) - } -} - -impl DeserializeEmbeddedGroup for TimelockStart { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - /*read_len: &mut CBORReadLen, */ _: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 4 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(4), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let slot = (|| -> Result<_, DeserializeError> { Ok(SlotBigNum::deserialize(raw)?) })() - .map_err(|e| e.annotate("slot"))?; - Ok(TimelockStart { slot }) - } -} - -impl cbor_event::se::Serialize for TimelockExpiry { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for TimelockExpiry { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(5u64)?; - self.slot.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for TimelockExpiry { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let mut read_len = CBORReadLen::new(len); - read_len.read_elems(2)?; - let ret = Self::deserialize_as_embedded_group(raw, /*&mut read_len, */ len); - match len { - cbor_event::Len::Len(_) => read_len.finish()?, - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => (), - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("TimelockExpiry")) - } -} - -impl DeserializeEmbeddedGroup for TimelockExpiry { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - /*read_len: &mut CBORReadLen, */ _: cbor_event::Len, - ) -> Result { - (|| -> Result<_, DeserializeError> { - let index_0_value = raw.unsigned_integer()?; - if index_0_value != 5 { - return Err(DeserializeFailure::FixedValueMismatch { - found: Key::Uint(index_0_value), - expected: Key::Uint(5), - } - .into()); - } - Ok(()) - })() - .map_err(|e| e.annotate("index_0"))?; - let slot = (|| -> Result<_, DeserializeError> { Ok(SlotBigNum::deserialize(raw)?) })() - .map_err(|e| e.annotate("slot"))?; - Ok(TimelockExpiry { slot }) - } -} - -impl cbor_event::se::Serialize for NativeScriptEnum { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - match self { - NativeScriptEnum::ScriptPubkey(x) => x.serialize(serializer), - NativeScriptEnum::ScriptAll(x) => x.serialize(serializer), - NativeScriptEnum::ScriptAny(x) => x.serialize(serializer), - NativeScriptEnum::ScriptNOfK(x) => x.serialize(serializer), - NativeScriptEnum::TimelockStart(x) => x.serialize(serializer), - NativeScriptEnum::TimelockExpiry(x) => x.serialize(serializer), - } - } -} - -impl Deserialize for NativeScriptEnum { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - //let mut read_len = CBORReadLen::new(len); - let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(ScriptPubkey::deserialize_as_embedded_group( - raw, /*&mut read_len, */ len, - )?) - })(raw) - { - Ok(variant) => return Ok(NativeScriptEnum::ScriptPubkey(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(ScriptAll::deserialize_as_embedded_group( - raw, /*mut read_len, */ len, - )?) - })(raw) - { - Ok(variant) => return Ok(NativeScriptEnum::ScriptAll(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(ScriptAny::deserialize_as_embedded_group( - raw, /*mut read_len, */ len, - )?) - })(raw) - { - Ok(variant) => return Ok(NativeScriptEnum::ScriptAny(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(ScriptNOfK::deserialize_as_embedded_group( - raw, /*mut read_len, */ len, - )?) - })(raw) - { - Ok(variant) => return Ok(NativeScriptEnum::ScriptNOfK(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(TimelockStart::deserialize_as_embedded_group( - raw, /*mut read_len, */ len, - )?) - })(raw) - { - Ok(variant) => return Ok(NativeScriptEnum::TimelockStart(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { - Ok(TimelockExpiry::deserialize_as_embedded_group( - raw, /*mut read_len, */ len, - )?) - })(raw) - { - Ok(variant) => return Ok(NativeScriptEnum::TimelockExpiry(variant)), - Err(_) => raw - .as_mut_ref() - .seek(SeekFrom::Start(initial_position)) - .unwrap(), - }; - match len { - cbor_event::Len::Len(_) => (), /*read_len.finish()?*/ - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => (), /*read_len.finish()?*/ - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - Err(DeserializeError::new( - "NativeScriptEnum", - DeserializeFailure::NoVariantMatched.into(), - )) - })() - .map_err(|e| e.annotate("NativeScriptEnum")) - } -} - -impl cbor_event::se::Serialize for NativeScript { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - self.0.serialize(serializer) - } -} - -impl Deserialize for NativeScript { - fn deserialize(raw: &mut Deserializer) -> Result { - Ok(Self(NativeScriptEnum::deserialize(raw)?)) - } -} - -impl cbor_event::se::Serialize for NativeScripts { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for NativeScripts { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(NativeScript::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("NativeScripts"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for Update { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.proposed_protocol_parameter_updates - .serialize(serializer)?; - self.epoch.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for Update { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("Update")) - } -} - -impl DeserializeEmbeddedGroup for Update { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - let proposed_protocol_parameter_updates = (|| -> Result<_, DeserializeError> { - Ok(ProposedProtocolParameterUpdates::deserialize(raw)?) - })() - .map_err(|e| e.annotate("proposed_protocol_parameter_updates"))?; - let epoch = (|| -> Result<_, DeserializeError> { Ok(Epoch::deserialize(raw)?) })() - .map_err(|e| e.annotate("epoch"))?; - Ok(Update { - proposed_protocol_parameter_updates, - epoch, - }) - } -} - -impl cbor_event::se::Serialize for GenesisHashes { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for GenesisHashes { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(GenesisHash::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("Genesishashes"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for ScriptHashes { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for ScriptHashes { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(ScriptHash::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("ScriptHashes"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for ProposedProtocolParameterUpdates { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; - for (key, value) in &self.0 { - key.serialize(serializer)?; - value.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for ProposedProtocolParameterUpdates { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut table = linked_hash_map::LinkedHashMap::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.map()?; - while match len { - cbor_event::Len::Len(n) => table.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - let key = GenesisHash::deserialize(raw)?; - let value = ProtocolParamUpdate::deserialize(raw)?; - if table.insert(key.clone(), value).is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( - "some complicated/unsupported type", - ))) - .into()); - } - } - Ok(()) - })() - .map_err(|e| e.annotate("ProposedProtocolParameterUpdates"))?; - Ok(Self(table)) - } -} - -impl cbor_event::se::Serialize for ProtocolVersion { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for ProtocolVersion { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - self.major.serialize(serializer)?; - self.minor.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for ProtocolVersion { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("ProtocolVersion")) - } -} - -impl DeserializeEmbeddedGroup for ProtocolVersion { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - let major = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() - .map_err(|e| e.annotate("major"))?; - let minor = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() - .map_err(|e| e.annotate("minor"))?; - Ok(ProtocolVersion { major, minor }) - } -} - -impl cbor_event::se::Serialize for ProtocolParamUpdate { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len( - match &self.minfee_a { - Some(_) => 1, - None => 0, - } + match &self.minfee_b { - Some(_) => 1, - None => 0, - } + match &self.max_block_body_size { - Some(_) => 1, - None => 0, - } + match &self.max_tx_size { - Some(_) => 1, - None => 0, - } + match &self.max_block_header_size { - Some(_) => 1, - None => 0, - } + match &self.key_deposit { - Some(_) => 1, - None => 0, - } + match &self.pool_deposit { - Some(_) => 1, - None => 0, - } + match &self.max_epoch { - Some(_) => 1, - None => 0, - } + match &self.n_opt { - Some(_) => 1, - None => 0, - } + match &self.pool_pledge_influence { - Some(_) => 1, - None => 0, - } + match &self.expansion_rate { - Some(_) => 1, - None => 0, - } + match &self.treasury_growth_rate { - Some(_) => 1, - None => 0, - } + match &self.d { - Some(_) => 1, - None => 0, - } + match &self.extra_entropy { - Some(_) => 1, - None => 0, - } + match &self.protocol_version { - Some(_) => 1, - None => 0, - } + match &self.min_pool_cost { - Some(_) => 1, - None => 0, - } + match &self.ada_per_utxo_byte { - Some(_) => 1, - None => 0, - } + match &self.cost_models { - Some(_) => 1, - None => 0, - } + match &self.execution_costs { - Some(_) => 1, - None => 0, - } + match &self.max_tx_ex_units { - Some(_) => 1, - None => 0, - } + match &self.max_block_ex_units { - Some(_) => 1, - None => 0, - } + match &self.max_value_size { - Some(_) => 1, - None => 0, - } + match &self.collateral_percentage { - Some(_) => 1, - None => 0, - } + match &self.max_collateral_inputs { - Some(_) => 1, - None => 0, - }, - ))?; - if let Some(field) = &self.minfee_a { - serializer.write_unsigned_integer(0)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.minfee_b { - serializer.write_unsigned_integer(1)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.max_block_body_size { - serializer.write_unsigned_integer(2)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.max_tx_size { - serializer.write_unsigned_integer(3)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.max_block_header_size { - serializer.write_unsigned_integer(4)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.key_deposit { - serializer.write_unsigned_integer(5)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.pool_deposit { - serializer.write_unsigned_integer(6)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.max_epoch { - serializer.write_unsigned_integer(7)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.n_opt { - serializer.write_unsigned_integer(8)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.pool_pledge_influence { - serializer.write_unsigned_integer(9)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.expansion_rate { - serializer.write_unsigned_integer(10)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.treasury_growth_rate { - serializer.write_unsigned_integer(11)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.d { - serializer.write_unsigned_integer(12)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.extra_entropy { - serializer.write_unsigned_integer(13)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.protocol_version { - serializer.write_unsigned_integer(14)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.min_pool_cost { - serializer.write_unsigned_integer(16)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.ada_per_utxo_byte { - serializer.write_unsigned_integer(17)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.cost_models { - serializer.write_unsigned_integer(18)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.execution_costs { - serializer.write_unsigned_integer(19)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.max_tx_ex_units { - serializer.write_unsigned_integer(20)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.max_block_ex_units { - serializer.write_unsigned_integer(21)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.max_value_size { - serializer.write_unsigned_integer(22)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.collateral_percentage { - serializer.write_unsigned_integer(23)?; - field.serialize(serializer)?; - } - if let Some(field) = &self.max_collateral_inputs { - serializer.write_unsigned_integer(24)?; - field.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for ProtocolParamUpdate { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.map()?; - let mut read_len = CBORReadLen::new(len); - let mut minfee_a = None; - let mut minfee_b = None; - let mut max_block_body_size = None; - let mut max_tx_size = None; - let mut max_block_header_size = None; - let mut key_deposit = None; - let mut pool_deposit = None; - let mut max_epoch = None; - let mut n_opt = None; - let mut pool_pledge_influence = None; - let mut expansion_rate = None; - let mut treasury_growth_rate = None; - let mut d = None; - let mut extra_entropy = None; - let mut protocol_version = None; - let mut min_pool_cost = None; - let mut ada_per_utxo_byte = None; - let mut cost_models = None; - let mut execution_costs = None; - let mut max_tx_ex_units = None; - let mut max_block_ex_units = None; - let mut max_value_size = None; - let mut collateral_percentage = None; - let mut max_collateral_inputs = None; - let mut read = 0; - while match len { - cbor_event::Len::Len(n) => read < n as usize, - cbor_event::Len::Indefinite => true, - } { - match raw.cbor_type()? { - CBORType::UnsignedInteger => match raw.unsigned_integer()? { - 0 => { - if minfee_a.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); - } - minfee_a = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Coin::deserialize(raw)?) - })() - .map_err(|e| e.annotate("minfee_a"))?, - ); - } - 1 => { - if minfee_b.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); - } - minfee_b = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Coin::deserialize(raw)?) - })() - .map_err(|e| e.annotate("minfee_b"))?, - ); - } - 2 => { - if max_block_body_size.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); - } - max_block_body_size = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(u32::deserialize(raw)?) - })() - .map_err(|e| e.annotate("max_block_body_size"))?, - ); - } - 3 => { - if max_tx_size.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); - } - max_tx_size = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(u32::deserialize(raw)?) - })() - .map_err(|e| e.annotate("max_tx_size"))?, - ); - } - 4 => { - if max_block_header_size.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(4)).into()); - } - max_block_header_size = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(u32::deserialize(raw)?) - })() - .map_err(|e| e.annotate("max_block_header_size"))?, - ); - } - 5 => { - if key_deposit.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(5)).into()); - } - key_deposit = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Coin::deserialize(raw)?) - })() - .map_err(|e| e.annotate("key_deposit"))?, - ); - } - 6 => { - if pool_deposit.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(6)).into()); - } - pool_deposit = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Coin::deserialize(raw)?) - })() - .map_err(|e| e.annotate("pool_deposit"))?, - ); - } - 7 => { - if max_epoch.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(7)).into()); - } - max_epoch = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Epoch::deserialize(raw)?) - })() - .map_err(|e| e.annotate("max_epoch"))?, - ); - } - 8 => { - if n_opt.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(8)).into()); - } - n_opt = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(u32::deserialize(raw)?) - })() - .map_err(|e| e.annotate("n_opt"))?, - ); - } - 9 => { - if pool_pledge_influence.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(9)).into()); - } - pool_pledge_influence = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Rational::deserialize(raw)?) - })() - .map_err(|e| e.annotate("pool_pledge_influence"))?, - ); - } - 10 => { - if expansion_rate.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(10)).into()); - } - expansion_rate = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(UnitInterval::deserialize(raw)?) - })() - .map_err(|e| e.annotate("expansion_rate"))?, - ); - } - 11 => { - if treasury_growth_rate.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(11)).into()); - } - treasury_growth_rate = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(UnitInterval::deserialize(raw)?) - })() - .map_err(|e| e.annotate("treasury_growth_rate"))?, - ); - } - 12 => { - if d.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(12)).into()); - } - d = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(UnitInterval::deserialize(raw)?) - })() - .map_err(|e| e.annotate("d"))?, - ); - } - 13 => { - if extra_entropy.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(13)).into()); - } - extra_entropy = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Nonce::deserialize(raw)?) - })() - .map_err(|e| e.annotate("extra_entropy"))?, - ); - } - 14 => { - if protocol_version.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(14)).into()); - } - protocol_version = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(ProtocolVersion::deserialize(raw)?) - })() - .map_err(|e| e.annotate("protocol_version"))?, - ); - } - 16 => { - if min_pool_cost.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(16)).into()); - } - min_pool_cost = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Coin::deserialize(raw)?) - })() - .map_err(|e| e.annotate("min_pool_cost"))?, - ); - } - 17 => { - if ada_per_utxo_byte.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(17)).into()); - } - ada_per_utxo_byte = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Coin::deserialize(raw)?) - })() - .map_err(|e| e.annotate("ada_per_utxo_byte"))?, - ); - } - 18 => { - if cost_models.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(18)).into()); - } - cost_models = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(Costmdls::deserialize(raw)?) - })() - .map_err(|e| e.annotate("cost_models"))?, - ); - } - 19 => { - if execution_costs.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(19)).into()); - } - execution_costs = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(ExUnitPrices::deserialize(raw)?) - })() - .map_err(|e| e.annotate("execution_costs"))?, - ); - } - 20 => { - if max_tx_ex_units.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(20)).into()); - } - max_tx_ex_units = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(ExUnits::deserialize(raw)?) - })() - .map_err(|e| e.annotate("max_tx_ex_units"))?, - ); - } - 21 => { - if max_block_ex_units.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(21)).into()); - } - max_block_ex_units = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(ExUnits::deserialize(raw)?) - })() - .map_err(|e| e.annotate("max_block_ex_units"))?, - ); - } - 22 => { - if max_value_size.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(22)).into()); - } - max_value_size = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(u32::deserialize(raw)?) - })() - .map_err(|e| e.annotate("max_value_size"))?, - ); - } - 23 => { - if collateral_percentage.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(23)).into()); - } - collateral_percentage = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(u32::deserialize(raw)?) - })() - .map_err(|e| e.annotate("collateral_percentage"))?, - ); - } - 24 => { - if max_collateral_inputs.is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Uint(24)).into()); - } - max_collateral_inputs = Some( - (|| -> Result<_, DeserializeError> { - read_len.read_elems(1)?; - Ok(u32::deserialize(raw)?) - })() - .map_err(|e| e.annotate("max_collateral_inputs"))?, - ); - } - unknown_key => { - return Err( - DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into() - ) - } - }, - CBORType::Text => match raw.text()?.as_str() { - unknown_key => { - return Err(DeserializeFailure::UnknownKey(Key::Str( - unknown_key.to_owned(), - )) - .into()) - } - }, - CBORType::Special => match len { - cbor_event::Len::Len(_) => { - return Err(DeserializeFailure::BreakInDefiniteLen.into()) - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => break, - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - }, - other_type => { - return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()) - } - } - read += 1; - } - read_len.finish()?; - Ok(Self { - minfee_a, - minfee_b, - max_block_body_size, - max_tx_size, - max_block_header_size, - key_deposit, - pool_deposit, - max_epoch, - n_opt, - pool_pledge_influence, - expansion_rate, - treasury_growth_rate, - d, - extra_entropy, - protocol_version, - min_pool_cost, - ada_per_utxo_byte, - cost_models, - execution_costs, - max_tx_ex_units, - max_block_ex_units, - max_value_size, - collateral_percentage, - max_collateral_inputs, - }) - })() - .map_err(|e| e.annotate("ProtocolParamUpdate")) - } -} - -impl cbor_event::se::Serialize for TransactionBodies { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for TransactionBodies { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(TransactionBody::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("TransactionBodies"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for TransactionWitnessSets { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for TransactionWitnessSets { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(TransactionWitnessSet::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("TransactionWitnessSets"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for AuxiliaryDataSet { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; - for (key, value) in &self.0 { - key.serialize(serializer)?; - value.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for AuxiliaryDataSet { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut table = linked_hash_map::LinkedHashMap::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.map()?; - while match len { - cbor_event::Len::Len(n) => table.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - let key = TransactionIndex::deserialize(raw)?; - let value = AuxiliaryData::deserialize(raw)?; - if table.insert(key.clone(), value).is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( - "some complicated/unsupported type", - ))) - .into()); - } - } - Ok(()) - })() - .map_err(|e| e.annotate("AuxiliaryDataSet"))?; - Ok(Self(table)) - } -} - -impl cbor_event::se::Serialize for Block { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(5))?; - self.header.serialize(serializer)?; - self.transaction_bodies.serialize(serializer)?; - self.transaction_witness_sets.serialize(serializer)?; - self.auxiliary_data_set.serialize(serializer)?; - serializer.write_array(cbor_event::Len::Len(self.invalid_transactions.len() as u64))?; - for element in self.invalid_transactions.iter() { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for Block { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let mut read_len = CBORReadLen::new(len); - read_len.read_elems(4)?; - let header = (|| -> Result<_, DeserializeError> { Ok(Header::deserialize(raw)?) })() - .map_err(|e| e.annotate("header"))?; - let transaction_bodies = - (|| -> Result<_, DeserializeError> { Ok(TransactionBodies::deserialize(raw)?) })() - .map_err(|e| e.annotate("transaction_bodies"))?; - let transaction_witness_sets = (|| -> Result<_, DeserializeError> { - Ok(TransactionWitnessSets::deserialize(raw)?) - })() - .map_err(|e| e.annotate("transaction_witness_sets"))?; - let auxiliary_data_set = - (|| -> Result<_, DeserializeError> { Ok(AuxiliaryDataSet::deserialize(raw)?) })() - .map_err(|e| e.annotate("auxiliary_data_set"))?; - let invalid_present = match len { - cbor_event::Len::Indefinite => raw.cbor_type()? == CBORType::Array, - cbor_event::Len::Len(4) => false, - _ => true, - }; - let invalid_transactions = (|| -> Result<_, DeserializeError> { - let mut arr = Vec::new(); - if invalid_present { - read_len.read_elems(1)?; - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(TransactionIndex::deserialize(raw)?); - } - } - Ok(arr) - })() - .map_err(|e| e.annotate("invalid_transactions"))?; - match len { - cbor_event::Len::Len(_) => (), - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => (), - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - Ok(Block { - header, - transaction_bodies, - transaction_witness_sets, - auxiliary_data_set, - invalid_transactions, - }) - })() - .map_err(|e| e.annotate("Block")) - } -} - -impl cbor_event::se::Serialize for Header { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(2))?; - self.header_body.serialize(serializer)?; - self.body_signature.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for Header { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("Header")) - } -} - -impl DeserializeEmbeddedGroup for Header { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - let header_body = - (|| -> Result<_, DeserializeError> { Ok(HeaderBody::deserialize(raw)?) })() - .map_err(|e| e.annotate("header_body"))?; - let body_signature = - (|| -> Result<_, DeserializeError> { Ok(KESSignature::deserialize(raw)?) })() - .map_err(|e| e.annotate("body_signature"))?; - Ok(Header { - header_body, - body_signature, - }) - } -} - -impl cbor_event::se::Serialize for OperationalCert { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(4))?; - self.serialize_as_embedded_group(serializer) - } -} - -impl SerializeEmbeddedGroup for OperationalCert { - fn serialize_as_embedded_group<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - self.hot_vkey.serialize(serializer)?; - self.sequence_number.serialize(serializer)?; - self.kes_period.serialize(serializer)?; - self.sigma.serialize(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for OperationalCert { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("OperationalCert")) - } -} - -impl DeserializeEmbeddedGroup for OperationalCert { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - _: cbor_event::Len, - ) -> Result { - let hot_vkey = (|| -> Result<_, DeserializeError> { Ok(KESVKey::deserialize(raw)?) })() - .map_err(|e| e.annotate("hot_vkey"))?; - let sequence_number = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() - .map_err(|e| e.annotate("sequence_number"))?; - let kes_period = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() - .map_err(|e| e.annotate("kes_period"))?; - let sigma = - (|| -> Result<_, DeserializeError> { Ok(Ed25519Signature::deserialize(raw)?) })() - .map_err(|e| e.annotate("sigma"))?; - Ok(OperationalCert { - hot_vkey, - sequence_number, - kes_period, - sigma, - }) - } -} - -impl cbor_event::se::Serialize for HeaderBody { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(15))?; - self.block_number.serialize(serializer)?; - self.slot.serialize(serializer)?; - match &self.prev_hash { - Some(x) => x.serialize(serializer), - None => serializer.write_special(CBORSpecial::Null), - }?; - self.issuer_vkey.serialize(serializer)?; - self.vrf_vkey.serialize(serializer)?; - match &self.leader_cert { - HeaderLeaderCertEnum::NonceAndLeader(nonce_vrf, leader_vrf) => { - nonce_vrf.serialize(serializer)?; - leader_vrf.serialize(serializer)?; - } - HeaderLeaderCertEnum::VrfResult(vrf_cert) => { - vrf_cert.serialize(serializer)?; - } - } - self.block_body_size.serialize(serializer)?; - self.block_body_hash.serialize(serializer)?; - self.operational_cert - .serialize_as_embedded_group(serializer)?; - self.protocol_version - .serialize_as_embedded_group(serializer)?; - Ok(serializer) - } -} - -impl Deserialize for HeaderBody { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - let ret = Self::deserialize_as_embedded_group(raw, len); - match len { - cbor_event::Len::Len(_) => - /* TODO: check finite len somewhere */ - { - () - } - cbor_event::Len::Indefinite => match raw.special()? { - CBORSpecial::Break => - /* it's ok */ - { - () - } - _ => return Err(DeserializeFailure::EndingBreakMissing.into()), - }, - } - ret - })() - .map_err(|e| e.annotate("HeaderBody")) - } -} - -impl DeserializeEmbeddedGroup for HeaderBody { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - len: cbor_event::Len, - ) -> Result { - let block_number = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() - .map_err(|e| e.annotate("block_number"))?; - let slot = (|| -> Result<_, DeserializeError> { Ok(SlotBigNum::deserialize(raw)?) })() - .map_err(|e| e.annotate("slot"))?; - let prev_hash = (|| -> Result<_, DeserializeError> { - Ok(match raw.cbor_type()? != CBORType::Special { - true => Some(BlockHash::deserialize(raw)?), - false => { - if raw.special()? != CBORSpecial::Null { - return Err(DeserializeFailure::ExpectedNull.into()); - } - None - } - }) - })() - .map_err(|e| e.annotate("prev_hash"))?; - let issuer_vkey = (|| -> Result<_, DeserializeError> { Ok(Vkey::deserialize(raw)?) })() - .map_err(|e| e.annotate("issuer_vkey"))?; - let vrf_vkey = (|| -> Result<_, DeserializeError> { Ok(VRFVKey::deserialize(raw)?) })() - .map_err(|e| e.annotate("vrf_vkey"))?; - let leader_cert = { - // NONCE VFR CERT, first of two certs - // or a single VRF RESULT CERT - // depending on the protocol version - let first_vrf_cert = - (|| -> Result<_, DeserializeError> { Ok(VRFCert::deserialize(raw)?) })() - .map_err(|e| e.annotate("nonce_vrf"))?; - let cbor_type: cbor_event::Type = raw.cbor_type()?; - match cbor_type { - cbor_event::Type::Array => { - // Legacy format, reading the second VRF cert - let leader_vrf = - (|| -> Result<_, DeserializeError> { Ok(VRFCert::deserialize(raw)?) })() - .map_err(|e| e.annotate("leader_vrf"))?; - HeaderLeaderCertEnum::NonceAndLeader(first_vrf_cert, leader_vrf) - } - cbor_event::Type::UnsignedInteger => { - // New format, no second VRF cert is present - HeaderLeaderCertEnum::VrfResult(first_vrf_cert) - } - t => { - return Err(DeserializeError::new( - "HeaderBody.leader_cert", - DeserializeFailure::UnexpectedKeyType(t), - )) - } - } - }; - let block_body_size = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() - .map_err(|e| e.annotate("block_body_size"))?; - let block_body_hash = - (|| -> Result<_, DeserializeError> { Ok(BlockHash::deserialize(raw)?) })() - .map_err(|e| e.annotate("block_body_hash"))?; - - let operational_cert = (|| -> Result<_, DeserializeError> { - if raw.cbor_type()? == CBORType::Array { - Ok(OperationalCert::deserialize(raw)?) - } else { - Ok(OperationalCert::deserialize_as_embedded_group(raw, len)?) - } - })() - .map_err(|e| e.annotate("operational_cert"))?; - let protocol_version = (|| -> Result<_, DeserializeError> { - if raw.cbor_type()? == CBORType::Array { - Ok(ProtocolVersion::deserialize(raw)?) - } else { - Ok(ProtocolVersion::deserialize_as_embedded_group(raw, len)?) - } - })() - .map_err(|e| e.annotate("protocol_version"))?; - Ok(HeaderBody { - block_number, - slot, - prev_hash, - issuer_vkey, - vrf_vkey, - leader_cert, - block_body_size, - block_body_hash, - operational_cert, - protocol_version, - }) - } -} - -impl cbor_event::se::Serialize for AssetName { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_bytes(&self.0) - } -} - -impl Deserialize for AssetName { - fn deserialize(raw: &mut Deserializer) -> Result { - Self::new_impl(raw.bytes()?) - } -} - -impl cbor_event::se::Serialize for AssetNames { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; - for element in &self.0 { - element.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for AssetNames { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut arr = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.array()?; - while match len { - cbor_event::Len::Len(n) => arr.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - arr.push(AssetName::deserialize(raw)?); - } - Ok(()) - })() - .map_err(|e| e.annotate("AssetNames"))?; - Ok(Self(arr)) - } -} - -impl cbor_event::se::Serialize for Assets { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; - for (key, value) in &self.0 { - key.serialize(serializer)?; - value.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for Assets { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut table = std::collections::BTreeMap::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.map()?; - while match len { - cbor_event::Len::Len(n) => table.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - let key = AssetName::deserialize(raw)?; - let value = BigNum::deserialize(raw)?; - if table.insert(key.clone(), value).is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( - "some complicated/unsupported type", - ))) - .into()); - } - } - Ok(()) - })() - .map_err(|e| e.annotate("Assets"))?; - Ok(Self(table)) - } -} - -impl cbor_event::se::Serialize for MultiAsset { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; - for (key, value) in &self.0 { - key.serialize(serializer)?; - value.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for MultiAsset { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut table = std::collections::BTreeMap::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.map()?; - while match len { - cbor_event::Len::Len(n) => table.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - let key = PolicyID::deserialize(raw)?; - let value = Assets::deserialize(raw)?; - if table.insert(key.clone(), value).is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( - "some complicated/unsupported type", - ))) - .into()); - } - } - Ok(()) - })() - .map_err(|e| e.annotate("MultiAsset"))?; - Ok(Self(table)) - } -} - -impl cbor_event::se::Serialize for MintAssets { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; - for (key, value) in &self.0 { - key.serialize(serializer)?; - value.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for MintAssets { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut table = std::collections::BTreeMap::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.map()?; - while match len { - cbor_event::Len::Len(n) => table.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - let key = AssetName::deserialize(raw)?; - let value = Int::deserialize(raw)?; - if table.insert(key.clone(), value).is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( - "some complicated/unsupported type", - ))) - .into()); - } - } - Ok(()) - })() - .map_err(|e| e.annotate("MintAssets"))?; - Ok(Self(table)) - } -} - -impl cbor_event::se::Serialize for Mint { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; - for (key, value) in &self.0 { - key.serialize(serializer)?; - value.serialize(serializer)?; - } - Ok(serializer) - } -} - -impl Deserialize for Mint { - fn deserialize(raw: &mut Deserializer) -> Result { - let mut mints = Vec::new(); - (|| -> Result<_, DeserializeError> { - let len = raw.map()?; - while match len { - cbor_event::Len::Len(n) => mints.len() < n as usize, - cbor_event::Len::Indefinite => true, - } { - if raw.cbor_type()? == CBORType::Special { - assert_eq!(raw.special()?, CBORSpecial::Break); - break; - } - let key = PolicyID::deserialize(raw)?; - let value = MintAssets::deserialize(raw)?; - mints.push((key.clone(), value)); - } - Ok(()) - })() - .map_err(|e| e.annotate("Mint"))?; - Ok(Self(mints)) - } -} - -impl cbor_event::se::Serialize for NetworkId { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - match self.0 { - NetworkIdKind::Testnet => serializer.write_unsigned_integer(0u64), - NetworkIdKind::Mainnet => serializer.write_unsigned_integer(1u64), - } - } -} - -impl Deserialize for NetworkId { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - match raw.unsigned_integer()? { - 0 => Ok(NetworkId::testnet()), - 1 => Ok(NetworkId::mainnet()), - _ => Err(DeserializeError::new( - "NetworkId", - DeserializeFailure::NoVariantMatched.into(), - )), - } - })() - .map_err(|e| e.annotate("NetworkId")) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::fakes::{ - fake_base_address, fake_bytes_32, fake_data_hash, fake_signature, fake_tx_input, - fake_tx_output, fake_value, fake_value2, fake_vkey, - }; - - #[test] - fn tx_output_deser_lagacy() { - let mut txos = TransactionOutputs::new(); - let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); - let val = &Value::new(&BigNum::from_str("435464757").unwrap()); - let txo = TransactionOutput { - address: addr.clone(), - amount: val.clone(), - plutus_data: None, - script_ref: None, - serialization_format: None - }; - let mut txo_dh = txo.clone(); - txo_dh.set_data_hash(&DataHash::from([47u8; DataHash::BYTE_COUNT])); - txos.add(&txo); - txos.add(&txo_dh); - txos.add(&txo_dh); - txos.add(&txo); - txos.add(&txo); - txos.add(&txo_dh); - let bytes = txos.to_bytes(); - let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); - let bytes_deser = txos_deser.to_bytes(); - assert_eq!(bytes, bytes_deser); - } - - #[test] - fn tx_output_deser_post_alonzo_with_plutus_script_and_datum() { - let mut txos = TransactionOutputs::new(); - let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); - let val = &Value::new(&BigNum::from_str("435464757").unwrap()); - let txo = TransactionOutput { - address: addr.clone(), - amount: val.clone(), - plutus_data: None, - script_ref: None, - serialization_format: None - }; - let mut txo_dh = txo.clone(); - txo_dh.set_plutus_data(&PlutusData::new_bytes(fake_bytes_32(11))); - txo_dh.set_script_ref(&ScriptRef::new_plutus_script(&PlutusScript::new( - [61u8; 29].to_vec(), - ))); - txos.add(&txo); - txos.add(&txo_dh); - txos.add(&txo_dh); - txos.add(&txo); - txos.add(&txo); - txos.add(&txo_dh); - let bytes = txos.to_bytes(); - let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); - let bytes_deser = txos_deser.to_bytes(); - assert_eq!(bytes, bytes_deser); - } - - #[test] - fn tx_output_deser_post_alonzo_with_plutus_script() { - let mut txos = TransactionOutputs::new(); - let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); - let val = &Value::new(&BigNum::from_str("435464757").unwrap()); - let txo = TransactionOutput { - address: addr.clone(), - amount: val.clone(), - plutus_data: None, - script_ref: None, - serialization_format: None - }; - let mut txo_dh = txo.clone(); - txo_dh.set_script_ref(&ScriptRef::new_plutus_script(&PlutusScript::new( - [61u8; 29].to_vec(), - ))); - txos.add(&txo); - txos.add(&txo_dh); - txos.add(&txo_dh); - txos.add(&txo); - txos.add(&txo); - txos.add(&txo_dh); - let bytes = txos.to_bytes(); - let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); - let bytes_deser = txos_deser.to_bytes(); - assert_eq!(bytes, bytes_deser); - } - - #[test] - fn tx_output_deser_post_alonzo_with_datum() { - let mut txos = TransactionOutputs::new(); - let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); - let val = &Value::new(&BigNum::from_str("435464757").unwrap()); - let txo = TransactionOutput { - address: addr.clone(), - amount: val.clone(), - plutus_data: None, - script_ref: None, - serialization_format: None - }; - let mut txo_dh = txo.clone(); - txo_dh.set_plutus_data(&PlutusData::new_bytes(fake_bytes_32(11))); - txos.add(&txo); - txos.add(&txo_dh); - txos.add(&txo_dh); - txos.add(&txo); - txos.add(&txo); - txos.add(&txo_dh); - let bytes = txos.to_bytes(); - let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); - let bytes_deser = txos_deser.to_bytes(); - assert_eq!(bytes, bytes_deser); - } - - #[test] - fn tx_output_deser_post_alonzo_with_native_script_and_datum() { - let mut txos = TransactionOutputs::new(); - let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); - let val = &Value::new(&BigNum::from_str("435464757").unwrap()); - let txo = TransactionOutput { - address: addr.clone(), - amount: val.clone(), - plutus_data: None, - script_ref: None, - serialization_format: None - }; - let mut txo_dh = txo.clone(); - let native_script = NativeScript::new_timelock_start(&TimelockStart::new(20)); - txo_dh.set_script_ref(&ScriptRef::new_native_script(&native_script)); - txo_dh.set_plutus_data(&PlutusData::new_bytes(fake_bytes_32(11))); - txos.add(&txo); - txos.add(&txo_dh); - txos.add(&txo_dh); - txos.add(&txo); - txos.add(&txo); - txos.add(&txo_dh); - let bytes = txos.to_bytes(); - let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); - let bytes_deser = txos_deser.to_bytes(); - assert_eq!(bytes, bytes_deser); - } - - #[test] - fn tx_output_deser_post_alonzo_with_native_script() { - let mut txos = TransactionOutputs::new(); - let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); - let val = &Value::new(&BigNum::from_str("435464757").unwrap()); - let txo = TransactionOutput { - address: addr.clone(), - amount: val.clone(), - plutus_data: None, - script_ref: None, - serialization_format: None - }; - let mut txo_dh = txo.clone(); - let native_script = NativeScript::new_timelock_start(&TimelockStart::new(20)); - txo_dh.set_script_ref(&ScriptRef::new_native_script(&native_script)); - txos.add(&txo); - txos.add(&txo_dh); - txos.add(&txo_dh); - txos.add(&txo); - txos.add(&txo); - txos.add(&txo_dh); - let bytes = txos.to_bytes(); - let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); - let bytes_deser = txos_deser.to_bytes(); - assert_eq!(bytes, bytes_deser); - } - - #[test] - fn tx_output_deser_post_alonzo_with_native_script_and_data_hash() { - let mut txos = TransactionOutputs::new(); - let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); - let val = &Value::new(&BigNum::from_str("435464757").unwrap()); - let txo = TransactionOutput { - address: addr.clone(), - amount: val.clone(), - plutus_data: None, - script_ref: None, - serialization_format: None - }; - let mut txo_dh = txo.clone(); - let native_script = NativeScript::new_timelock_start(&TimelockStart::new(20)); - let data_hash = DataHash::from_bytes(vec![ - 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, - 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, - ]) - .unwrap(); - txo_dh.set_data_hash(&data_hash); - txo_dh.set_script_ref(&ScriptRef::new_native_script(&native_script)); - txos.add(&txo); - txos.add(&txo_dh); - txos.add(&txo_dh); - txos.add(&txo); - txos.add(&txo); - txos.add(&txo_dh); - let bytes = txos.to_bytes(); - let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); - let bytes_deser = txos_deser.to_bytes(); - assert_eq!(bytes, bytes_deser); - } - - #[test] - fn tx_output_deser_lagacy_json() { - let mut txos = TransactionOutputs::new(); - let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); - let val = &Value::new(&BigNum::from_str("435464757").unwrap()); - let txo = TransactionOutput { - address: addr.clone(), - amount: val.clone(), - plutus_data: None, - script_ref: None, - serialization_format: None - }; - let mut txo_dh = txo.clone(); - txo_dh.set_data_hash(&DataHash::from([47u8; DataHash::BYTE_COUNT])); - txos.add(&txo); - txos.add(&txo_dh); - txos.add(&txo_dh); - txos.add(&txo); - txos.add(&txo); - txos.add(&txo_dh); - let json_txos = txos.to_json().unwrap(); - let deser_txos = TransactionOutputs::from_json(json_txos.as_str()).unwrap(); - - assert_eq!(deser_txos.to_bytes(), txos.to_bytes()); - assert_eq!(deser_txos.to_json().unwrap(), txos.to_json().unwrap()); - } - - #[test] - fn tx_output_deser_post_alonzo_with_plutus_script_and_datum_json() { - let mut txos = TransactionOutputs::new(); - let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); - let val = &Value::new(&BigNum::from_str("435464757").unwrap()); - let txo = TransactionOutput { - address: addr.clone(), - amount: val.clone(), - plutus_data: None, - script_ref: None, - serialization_format: None - }; - let mut txo_dh = txo.clone(); - txo_dh.set_plutus_data(&PlutusData::new_bytes(fake_bytes_32(11))); - txo_dh.set_script_ref(&ScriptRef::new_plutus_script(&PlutusScript::new( - [61u8; 29].to_vec(), - ))); - txos.add(&txo); - txos.add(&txo_dh); - txos.add(&txo_dh); - txos.add(&txo); - txos.add(&txo); - txos.add(&txo_dh); - let json_txos = txos.to_json().unwrap(); - let deser_txos = TransactionOutputs::from_json(json_txos.as_str()).unwrap(); - - assert_eq!(deser_txos.to_bytes(), txos.to_bytes()); - assert_eq!(deser_txos.to_json().unwrap(), txos.to_json().unwrap()); - } - - #[test] - fn tx_output_deser_post_alonzo_with_plutus_script_json() { - let mut txos = TransactionOutputs::new(); - let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); - let val = &Value::new(&BigNum::from_str("435464757").unwrap()); - let txo = TransactionOutput { - address: addr.clone(), - amount: val.clone(), - plutus_data: None, - script_ref: None, - serialization_format: None - }; - let mut txo_dh = txo.clone(); - txo_dh.set_script_ref(&ScriptRef::new_plutus_script(&PlutusScript::new( - [61u8; 29].to_vec(), - ))); - txos.add(&txo); - txos.add(&txo_dh); - txos.add(&txo_dh); - txos.add(&txo); - txos.add(&txo); - txos.add(&txo_dh); - let json_txos = txos.to_json().unwrap(); - let deser_txos = TransactionOutputs::from_json(json_txos.as_str()).unwrap(); - - assert_eq!(deser_txos.to_bytes(), txos.to_bytes()); - assert_eq!(deser_txos.to_json().unwrap(), txos.to_json().unwrap()); - } - - #[test] - fn tx_output_deser_post_alonzo_with_datum_json() { - let mut txos = TransactionOutputs::new(); - let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); - let val = &Value::new(&BigNum::from_str("435464757").unwrap()); - let txo = TransactionOutput { - address: addr.clone(), - amount: val.clone(), - plutus_data: None, - script_ref: None, - serialization_format: None - }; - let mut txo_dh = txo.clone(); - txo_dh.set_plutus_data(&PlutusData::new_bytes(fake_bytes_32(11))); - txos.add(&txo); - txos.add(&txo_dh); - txos.add(&txo_dh); - txos.add(&txo); - txos.add(&txo); - txos.add(&txo_dh); - let json_txos = txos.to_json().unwrap(); - let deser_txos = TransactionOutputs::from_json(json_txos.as_str()).unwrap(); - - assert_eq!(deser_txos.to_bytes(), txos.to_bytes()); - assert_eq!(deser_txos.to_json().unwrap(), txos.to_json().unwrap()); - } - - #[test] - fn tx_output_deser_post_alonzo_with_native_script_and_datum_json() { - let mut txos = TransactionOutputs::new(); - let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); - let val = &Value::new(&BigNum::from_str("435464757").unwrap()); - let txo = TransactionOutput { - address: addr.clone(), - amount: val.clone(), - plutus_data: None, - script_ref: None, - serialization_format: None - }; - let mut txo_dh = txo.clone(); - let native_script = NativeScript::new_timelock_start(&TimelockStart::new(20)); - txo_dh.set_script_ref(&ScriptRef::new_native_script(&native_script)); - txo_dh.set_plutus_data(&PlutusData::new_bytes(fake_bytes_32(11))); - txos.add(&txo); - txos.add(&txo_dh); - txos.add(&txo_dh); - txos.add(&txo); - txos.add(&txo); - txos.add(&txo_dh); - let json_txos = txos.to_json().unwrap(); - let deser_txos = TransactionOutputs::from_json(json_txos.as_str()).unwrap(); - - assert_eq!(deser_txos.to_bytes(), txos.to_bytes()); - assert_eq!(deser_txos.to_json().unwrap(), txos.to_json().unwrap()); - } - - #[test] - fn tx_output_deser_post_alonzo_with_native_script_json() { - let mut txos = TransactionOutputs::new(); - let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); - let val = &Value::new(&BigNum::from_str("435464757").unwrap()); - let txo = TransactionOutput { - address: addr.clone(), - amount: val.clone(), - plutus_data: None, - script_ref: None, - serialization_format: None - }; - let mut txo_dh = txo.clone(); - let native_script = NativeScript::new_timelock_start(&TimelockStart::new(20)); - txo_dh.set_script_ref(&ScriptRef::new_native_script(&native_script)); - txos.add(&txo); - txos.add(&txo_dh); - txos.add(&txo_dh); - txos.add(&txo); - txos.add(&txo); - txos.add(&txo_dh); - let json_txos = txos.to_json().unwrap(); - let deser_txos = TransactionOutputs::from_json(json_txos.as_str()).unwrap(); - - assert_eq!(deser_txos.to_bytes(), txos.to_bytes()); - assert_eq!(deser_txos.to_json().unwrap(), txos.to_json().unwrap()); - } - - #[test] - fn tx_output_deser_post_alonzo_with_native_script_and_data_hash_json() { - let mut txos = TransactionOutputs::new(); - let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); - let val = &Value::new(&BigNum::from_str("435464757").unwrap()); - let txo = TransactionOutput { - address: addr.clone(), - amount: val.clone(), - plutus_data: None, - script_ref: None, - serialization_format: None - }; - let mut txo_dh = txo.clone(); - let native_script = NativeScript::new_timelock_start(&TimelockStart::new(20)); - let data_hash = DataHash::from_bytes(vec![ - 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, - 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, - ]) - .unwrap(); - txo_dh.set_data_hash(&data_hash); - txo_dh.set_script_ref(&ScriptRef::new_native_script(&native_script)); - txos.add(&txo); - txos.add(&txo_dh); - txos.add(&txo_dh); - txos.add(&txo); - txos.add(&txo); - txos.add(&txo_dh); - let json_txos = txos.to_json().unwrap(); - let deser_txos = TransactionOutputs::from_json(json_txos.as_str()).unwrap(); - - assert_eq!(deser_txos.to_bytes(), txos.to_bytes()); - assert_eq!(deser_txos.to_json().unwrap(), txos.to_json().unwrap()); - } - - #[test] - fn mir_deser() { - let reserves_to_pot = MoveInstantaneousReward::new_to_other_pot( - MIRPot::Treasury, - &Coin::from_str("143546464").unwrap(), - ); - let reserves_to_pot_deser = - MoveInstantaneousReward::from_bytes(reserves_to_pot.to_bytes()).unwrap(); - assert_eq!(reserves_to_pot.to_bytes(), reserves_to_pot_deser.to_bytes()); - let treasury_to_pot = MoveInstantaneousReward::new_to_other_pot( - MIRPot::Treasury, - &Coin::from_str("0").unwrap(), - ); - let treasury_to_pot_deser = - MoveInstantaneousReward::from_bytes(treasury_to_pot.to_bytes()).unwrap(); - assert_eq!(treasury_to_pot.to_bytes(), treasury_to_pot_deser.to_bytes()); - let mut stake_creds = MIRToStakeCredentials::new(); - stake_creds.insert( - &StakeCredential::from_scripthash(&ScriptHash([54u8; ScriptHash::BYTE_COUNT])), - &Int::new_i32(-314159265), - ); - let to_stake_creds = - MoveInstantaneousReward::new_to_stake_creds(MIRPot::Treasury, &stake_creds); - let to_stake_creds_deser = - MoveInstantaneousReward::from_bytes(to_stake_creds.to_bytes()).unwrap(); - assert_eq!(to_stake_creds.to_bytes(), to_stake_creds_deser.to_bytes()); - } - - #[test] - #[ignore] - fn alonzo_block() { - // this test for some reason has 2-byte pool metadata hashes so don't run this without changing that - let bytes = hex::decode("85828f03095820bb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aa58208a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c58208a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c8258404fefc7c718693b57c87170ceba220382afbdd148c0a53b4a009ca63ad1f101483a6170c83a77f23d362a68dcb502802df7f98fa4f7a78b4082b211530e1234305850f770f6769ae9871d42b970fc6254bb927c2181fff45897f241bd72221d86d33c8df64c0a3c8cbb9aa52fef191d7202465c52df8d33727a38c7dc5d40864d753348a340f8afcbb3bb05d4a03f16b1080d825840fe682775f0fa232e909ddc9ec3210ea7a0ee6514cd8b0815190a08f7cef3985463152e10dfad9ed6c09b641b6c1824498e77814a7c12e03096a63cd62056446358500951ed3ef2065e4196d008b50a63bb3e2bdc9a64df67eff4e230b35429291def476684114e074357a5c834bf79eacf583b6fe9fcd1d17f3719a31de97aa4da5e4630b05421359e0b6e4a9bd76c6b920b190929582010c865acec05c84c2c0d0b889f7dbe9bf3b5561f8552da1eb286eac4ccdabc5e5820d298da3803eb9958f01c02e73f2410f2f9bb2ecbc346526b1b76772e1bdd7db500005840940f8a3696847e4a238705bdd27a345086282067b9bc5cb7b69847ca8756085844d576f59ab056c169a504320cc1eab4c11fd529482b3c57da6fa96d44635b0802005901c0a1b2ee63b357fe0b19c6bb8dc3fc865c0608a89626505c5f9aff3b74a0809ca2635e0c4235c247306987c7fd76a4a06210ebf74178e72a1faa78fb8865a69005cc6a5ab5c9b40f817c715df558af7d07b6186f0ccf31715ec2fb00980730ac166af657e6670608afe1bf651d496e01b1c7ff1eb44614d8cfd1b7e32b2c2939349236cc0ada145d8d8d7ad919ef1e60c8bbad31dbedf9f395849705a00c14a8785106aae31f55abc5b1f2089cbef16d9401f158704c1e4f740f7125cfc700a99d97d0332eacb33e4bbc8dab2872ec2b3df9e113addaebd156bfc64fdfc732614d2aedd10a58a34993b7b08c822af3aa615b6bbb9b267bc902e4f1075e194aed084ca18f8bcde1a6b094bf3f5295a0d454c0a083ed5b74f7092fc0a7346c03979a30eeea76d686e512ba48d21544ba874886cdd166cbf275b11f1f3881f4c4277c09a24b88fc6168f4578267bdc9d62cb9b78b8dfc888ccce226a177725f39e7d50930552861d1e88b7898971c780dc3b773321ba1854422b5cecead7d50e77783050eeae2cd9595b9cd91681c72e5d53bb7d12f28dec9b2847ee70a3d7781fb1133aea3b169f536ff5945ec0a76950e51beded0627bb78120617a2f0842e50e3981ae0081825820ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25000d81825820bb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aa01018183583900cb9358529df4729c3246a2a033cb9821abbfd16de4888005904abc410d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb06821864a1581ca646474b8f5431261506b6c273d307c7569a4eb6c96b42dd4a29520aa14a636f75747473436f696e1903e85820ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25021903e70304048382008200581c0d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb068a03581c0d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb065820c5e21ab1c9f6022d81c3b25e3436cb7f1df77f9652ae3e1310c28e621dd87b4c0105d81e82010a581de00d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb0681581c0d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb0680826e636f6e73656e7375732e706f6f6c427b7d82068200a18200581c008b47844d92812fc30d1f0ac9b6fbf38778ccba9db8312ad9079079186e05a1581de00d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb0618640682a1581ce0a714319812c3f773ba04ec5d6b3ffcd5aad85006805b047b082541a104190fa00008020e81581cf81ce66e0f52da5ca48193386e7511fde5b030a307b4c3681736c6f009a1581cb16b56f5ec064be6ac3cab6035efae86b366cc3dc4a0d571603d70e5a14a636f75747473436f696e1903e80b58209e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b0758209e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b0f0181a400818258203b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da295840815671b581b4b02a30108a799a85c7f2e5487fb667e748e8fde59e466ab987ce133ecb77ffa0dc53c5804e6706e26b94e17803235da28112bc747de48ccbd70903814c4b0100002002002002000011048118bf058184000019039782191388191388a100d90103a300a40166737472696e67024562797465730382010204a10341620181820180028148470100002002006180").unwrap(); - let block = Block::from_bytes(bytes).unwrap(); - let block2 = Block::from_bytes(block.to_bytes()).unwrap(); - assert_eq!(block.to_bytes(), block2.to_bytes()); - } - - #[test] - fn test_tx_body_roundtrip() { - let mut txb = TransactionBody::new( - &TransactionInputs(vec![fake_tx_input(0)]), - &TransactionOutputs(vec![fake_tx_output(1)]), - &to_bignum(1234567), - Some(12345678), - ); - - txb.set_collateral_return(&fake_tx_output(2)); - txb.set_total_collateral(&to_bignum(1234)); - - let txb2 = TransactionBody::from_bytes(txb.to_bytes()).unwrap(); - assert_eq!(txb, txb2); - } - - #[test] - fn test_header_body_roundtrip() { - fn fake_header_body(leader_cert: HeaderLeaderCertEnum) -> HeaderBody { - HeaderBody { - block_number: 123, - slot: to_bignum(123), - prev_hash: Some(BlockHash::from_bytes(fake_bytes_32(1)).unwrap()), - issuer_vkey: fake_vkey(), - vrf_vkey: VRFVKey::from_bytes(fake_bytes_32(2)).unwrap(), - leader_cert, - block_body_size: 123456, - block_body_hash: BlockHash::from_bytes(fake_bytes_32(4)).unwrap(), - operational_cert: OperationalCert::new( - &KESVKey::from_bytes(fake_bytes_32(5)).unwrap(), - 123, - 456, - &fake_signature(6), - ), - protocol_version: ProtocolVersion::new(12, 13), - } - } - - let hbody1 = fake_header_body(HeaderLeaderCertEnum::VrfResult( - VRFCert::new(fake_bytes_32(3), [0; 80].to_vec()).unwrap(), - )); - - assert_eq!(hbody1, HeaderBody::from_bytes(hbody1.to_bytes()).unwrap()); - - let hbody2 = fake_header_body(HeaderLeaderCertEnum::NonceAndLeader( - VRFCert::new(fake_bytes_32(4), [1; 80].to_vec()).unwrap(), - VRFCert::new(fake_bytes_32(5), [2; 80].to_vec()).unwrap(), - )); - - assert_eq!(hbody2, HeaderBody::from_bytes(hbody2.to_bytes()).unwrap()); - } - - #[test] - fn test_witness_set_roundtrip() { - fn witness_set_roundtrip(plutus_scripts: &PlutusScripts) { - let mut ws = TransactionWitnessSet::new(); - ws.set_vkeys(&Vkeywitnesses(vec![Vkeywitness::new( - &fake_vkey(), - &fake_signature(1), - )])); - ws.set_redeemers(&Redeemers(vec![Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(12), - &PlutusData::new_integer(&BigInt::one()), - &ExUnits::new(&to_bignum(123), &to_bignum(456)), - )])); - ws.set_plutus_data(&PlutusList::from(vec![PlutusData::new_integer( - &BigInt::one(), - )])); - ws.set_plutus_scripts(plutus_scripts); - - assert_eq!( - TransactionWitnessSet::from_bytes(ws.to_bytes()).unwrap(), - ws - ); - } - - let bytes = hex::decode("4e4d01000033222220051200120011").unwrap(); - let script_v1 = PlutusScript::from_bytes(bytes.clone()).unwrap(); - let script_v2 = PlutusScript::from_bytes_v2(bytes.clone()).unwrap(); - - witness_set_roundtrip(&PlutusScripts(vec![])); - witness_set_roundtrip(&PlutusScripts(vec![script_v1.clone()])); - witness_set_roundtrip(&PlutusScripts(vec![script_v2.clone()])); - witness_set_roundtrip(&PlutusScripts(vec![script_v1.clone(), script_v2.clone()])); - } - - #[test] - fn test_script_ref_roundtrip() { - let ref0 = ScriptRef::new_native_script(&NativeScript::new_timelock_start( - &TimelockStart::new(123456), - )); - assert_eq!(ScriptRef::from_bytes(ref0.to_bytes()).unwrap(), ref0); - - let bytes = hex::decode("4e4d01000033222220051200120011").unwrap(); - let script_v1 = PlutusScript::from_bytes(bytes.clone()).unwrap(); - let script_v2 = PlutusScript::from_bytes_v2(bytes.clone()).unwrap(); - - let ref1 = ScriptRef::new_plutus_script(&script_v1); - assert_eq!(ScriptRef::from_bytes(ref1.to_bytes()).unwrap(), ref1); - - let ref2 = ScriptRef::new_plutus_script(&script_v2); - assert_eq!(ScriptRef::from_bytes(ref2.to_bytes()).unwrap(), ref2); - } - - #[test] - fn legacy_output_roundtrip() { - let o1 = TransactionOutput::new(&fake_base_address(0), &fake_value()); - let mut o2 = TransactionOutput::new(&fake_base_address(1), &fake_value()); - o2.set_data_hash(&fake_data_hash(2)); - - assert_eq!(TransactionOutput::from_bytes(o1.to_bytes()).unwrap(), o1); - assert_eq!(TransactionOutput::from_bytes(o2.to_bytes()).unwrap(), o2); - } - - #[test] - fn babbage_output_roundtrip() { - let mut o1 = TransactionOutput::new(&fake_base_address(0), &fake_value2(234567)); - o1.set_plutus_data(&PlutusData::new_empty_constr_plutus_data(&to_bignum(42))); - assert_eq!(TransactionOutput::from_bytes(o1.to_bytes()).unwrap(), o1); - - let mut o2 = TransactionOutput::new(&fake_base_address(1), &fake_value2(234568)); - o2.set_script_ref(&ScriptRef::new_native_script( - &NativeScript::new_timelock_start(&TimelockStart::new(123456)), - )); - assert_eq!(TransactionOutput::from_bytes(o2.to_bytes()).unwrap(), o2); - - let bytes = hex::decode("4e4d01000033222220051200120011").unwrap(); - let script_v1 = PlutusScript::from_bytes(bytes.clone()).unwrap(); - let script_v2 = PlutusScript::from_bytes_v2(bytes.clone()).unwrap(); - - let mut o3 = TransactionOutput::new(&fake_base_address(2), &fake_value2(234569)); - o3.set_script_ref(&ScriptRef::new_plutus_script(&script_v1)); - assert_eq!(TransactionOutput::from_bytes(o3.to_bytes()).unwrap(), o3); - - let mut o4 = TransactionOutput::new(&fake_base_address(3), &fake_value2(234570)); - o4.set_script_ref(&ScriptRef::new_plutus_script(&script_v2)); - assert_eq!(TransactionOutput::from_bytes(o4.to_bytes()).unwrap(), o4); - - let mut o5 = TransactionOutput::new(&fake_base_address(4), &fake_value2(234571)); - o5.set_plutus_data(&PlutusData::new_empty_constr_plutus_data(&to_bignum(43))); - o5.set_script_ref(&ScriptRef::new_plutus_script(&script_v2)); - assert_eq!(TransactionOutput::from_bytes(o5.to_bytes()).unwrap(), o5); - - let mut o6 = TransactionOutput::new(&fake_base_address(5), &fake_value2(234572)); - o6.set_data_hash(&fake_data_hash(222)); - o6.set_script_ref(&ScriptRef::new_plutus_script(&script_v2)); - assert_eq!(TransactionOutput::from_bytes(o6.to_bytes()).unwrap(), o6); - } - - #[test] - fn pre_alonzo_block() { - let bytes = hex::decode("84828f1a002072a81a00ca44f0582070d6f38b4569ba062c09632127db13474f22c534e6d8097895403c431e57f12358204f4d7523e41e058a6cbdefb5538654ffc2a53416a7f5bb99f7eac699d42d5c1f58205e3d96cb8ef0291d2f1df6aa7b5a4496ac8de1dcce100c31274325625102796d82584065417914ca323d842c5861407a638e146e6af55f59aff95f1451839de2aa709151237e24e6db7bf94db97293da9c1e61e68d60c8e2b10a116d3c71067247458b5850dc36a5a88f09f0b7a0b5d5d52d87c7c3e3c20752176a426d182255df3d026392f407990f09e5858de6432263fc167bc890a97d07d2371cd5bb26b12242c1ff6fda184ec78d15493a38a3e0df1494f800825840df4e07d3bca43341e4297e2914ea38363ecea1c17ce9145294c4631e0f09f706cb23a5f27c6b71ae9ac46a7ca25af4d7c156f15444fa41814f7d6a0b6a4e57525850d6073f277ded1ef9e8bfe9f6325858c142fbbbbff4395c45d82f0861a6ef6116204965f807e8650fa4e9ac4aa04aeb03984ea66abb129155a78931d39bbcb7ad64afef3f4f55cfa4eb6c97698e88f1051905db5820c1b1fbd809dc06e0e2dc544312aae2a46c059249f86c24ea0689a0b0944a75f558207ce5ce3992b23cb2bf566c48aba8bfc39eb24c9b43354de0129b81bf9f1414b307186058403ac64d720227c18139132b499046a168eb1c5bdd3983385e3518f33fc7f52fd0be348fe3e14d17e1ba606708c30bda061cf23ea3294b0089d3e1e1d58a7aa50702005901c074d3c2c0b5e17b12ba829017186daa1f7f365bbe5b0e0c038cb0cc05e849f702afd349234353ee3cc8878fa31299e85562f04d3cdd74e1bc73591be48d2fbc0d043f6b41fa527b2f9fb3f77605eee528926e76cc18a1638283e5591170f7073462441d40d7cc2e13a38e7d247928cb15d2e5b2e74a12d07f858f7e922bbff8a91c16e9bb8f5ea101c50d96627fb48a03d8191b5035b5de00b9824867fdffb5a2493799e94676bf685db85517dd8a87a0ba2589b3a8a69d529ae8052680c520c5577adbb91cf931e906b1629e621d5bd5c30eaee77f35c5f0a714827b48afaa4e549c1756e94291f4b083aad9c375caf9a67aeac08f32c91cd0572192267960cd74a85148b5e99d0053804dcfb44785417725c56e0fc5caf2ae50fbf25b92c7b7ebe17aa9e289470041a06fd8986f6f9ebdb12e87a970f1d388963929367013e17513e83cab8c98460cab703d5fdd26eeb079e4db701996f73c694365080236901289c5fc96471e91fb75e0e58560f5d073c3ef79a8f5dd4b45ff7abf9c7d7564232f7897ca3d85ac7bb9ecaa75b7c062f27de8b20f301e5607563b2c904e3c7f113b1eeba8a4d1c82fc1a747c920bac6af9a9f4dae1744847232ea03289e25e482a50082825820478ad95cafe9b1660809d618870c86dda1295764e113886e2b8a1de2de5af17201825820f84508cc7674b663db84ceb9f0790f5527f3c70f2a05e4d7f783cd9890463b4e01018182583900ff7f04abbd3050c0b138c8fa3005d48aaf8b9700d4565758e91a95385667fab107f848cfd4b73a7407a7661600cf68f0efc969ece37665ae1a000f4240021a000f4240031a00ca60f1075820e845fe9180ac36cc0102f892a839ad1ed2ea9a52c605fb8e4e1c2774ef0bb65ba50081825820c4b5ad6873b8581c75b8ee52f58a3eded29acbbb92d874a64228a1ca4e68956700018182581d60daad04ed2b7f69e2a9be582e37091739fa036a14c1c22f88061d43c71b004aca96b58fd90c021a000f4240031a00d986900682a7581c0d06d2547ed371fdf95fb5c4c735eecdd53e6a5bb831561bd0fcfd3da10e820300581c2f56e87d67b8e5216582cfeb95dbdc9083110a3ef68faaa51bef3a80a10e820300581c2fca486b4d8f1a0432f5bf18ef473ee4294c795a1a32e3132bc6b90fa10e820300581c4ee98623920698b77c1c7f77288cbdac5f9011ff8970b1f507567d0da10e820300581c514e81afb082fce01678809eebd90eda4f7918354ec7d0433ad16274a10e820300581c581e23030b6038bae716e5d64b9e053db10541b12e6b0b4eff485454a10e820300581ce5f27655371b54aed91cc916b2569060978be80056768fee2cc5ce1ba10e820300186582a1008182582028364596385174f5eabc763031b8d54b18ed5d06967ff44b3abbdbaca9cb58a75840de49197fed8dd13716c88e68452fb314d418a24fee9cc194308bd47b057d161ae40cd8f49bf6b378e7343ee5d3a7b9bdb1f2e9efeef896adaa9eb7373fbb8502a1008882582032a954b521c0b19514408965831ef6839637de7a1a6168bcf8455c504ba93b9c5840ab2d59239499807e25dc8025940a70cb890a52e8f47f35004cfec623036ca9f5c3e925b32bd23a7d1d044cef915913e853dbb57438f9c92a5d5f9581caa67d098258207ec249d890d0aaf9a81207960c163ae2d6ac5e715ca6b96d5860e50d9f2b2b2a5840f2d8031ac5d79777076dd1176cb7ed91690fcfb6be498320e5de9afbf6ea8e8ced23bff69230d050523a4a7e03c2b0599e18e93b31959063249fb50274a02a068258204f4d7523e41e058a6cbdefb5538654ffc2a53416a7f5bb99f7eac699d42d5c1f5840c5844b849865fed81f67842a4697c3090cf4ecb50510f1e6b379b7c63b78417ca28ea653c016d2e733877e1605e8a1712c42404ca0686f67455c620431d54b07825820e764b0340d7b353f5f745891033774e4beab6aa1458a54ff29a1324c05bb9876584026c35f8ec2102ec8fcc3bd0a1a0760486952e147f44236a35c7d818a7024590e1395f097a0d046085ded24ec8c585008d3ffc0321ad040649ce08eb33614760e82582073ae41eca2be37fc15c55a50d668c8647e10bf222172c2d58abfa6e9310e596258402c3f197360294781841f0669822b0449515a5e0b77b23185652a1b0ea8354537b3e9335577a87fa19e9fe47f1039fa286aaa11859d631f3ff74564c6da14c806825820234fb2b8530114b461c6ca8242c8b86a226c95c4c27479ca850d1aea4a52d2985840ba751817e70695a041a5f455c08947fa4e3d6ffc332adeb25691fac4927bbaafd4b3f5f9855946ad9681083aec277766c7f90da7543e912f46aeae07fdd5b90a825820dfb615a61568d6867f45a85c32227f27025180d738a8a3d7fd3c929f624d72395840cc1f728cce6ce2fec21d2648011c14d244c35ba3cbd553593655f6f07d86b8bdf103d52b61143bc1701319517d4a24b778c02e983e02a0f3fd0cd558d472f009825820e5bc21a83616bcccfe343ec36b9dc4c06c90e913df1d8a0b046008651f42caa95840f85bc5e753beed04b3f9072da7a6adadcdb87769528c59e16162e86782b6ce11feacbd5de97e352121e9509a809f613d5bcebf7413fd55f89776c5606e4a9408a100a119534da261638158220a201f79b4d15fd971297a842ac6a4e953b82886df66c0d9723f5870e5725da6380b617601").unwrap(); - let _block = Block::from_bytes(bytes).unwrap(); - } - - #[test] - fn tx_output_ser_type() { - let array_tx_output = TransactionOutput::from_hex("8258390000efb5788e8713c844dfd32b2e91de1e309fefffd555f827cc9ee16400efb5788e8713c844dfd32b2e91de1e309fefffd555f827cc9ee1641a000f4240").unwrap(); - let map_tx_output = TransactionOutput::from_hex("a30058390000efb5788e8713c844dfd32b2e91de1e309fefffd555f827cc9ee16400efb5788e8713c844dfd32b2e91de1e309fefffd555f827cc9ee164011a00039447028201d81844d9052380").unwrap(); - assert_eq!(array_tx_output.serialization_format().unwrap(), CborContainerType::Array); - assert_eq!(map_tx_output.serialization_format().unwrap(), CborContainerType::Map); - - } -} diff --git a/rust/src/serialization/block/block.rs b/rust/src/serialization/block/block.rs new file mode 100644 index 00000000..43d656ff --- /dev/null +++ b/rust/src/serialization/block/block.rs @@ -0,0 +1,80 @@ +use crate::*; +use crate::serialization::utils::is_break_tag; + +impl cbor_event::se::Serialize for Block { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(5))?; + self.header.serialize(serializer)?; + self.transaction_bodies.serialize(serializer)?; + self.transaction_witness_sets.serialize(serializer)?; + self.auxiliary_data_set.serialize(serializer)?; + serializer.write_array(cbor_event::Len::Len(self.invalid_transactions.len() as u64))?; + for element in self.invalid_transactions.iter() { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Block { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(4)?; + let header = (|| -> Result<_, DeserializeError> { Ok(Header::deserialize(raw)?) })() + .map_err(|e| e.annotate("header"))?; + let transaction_bodies = + (|| -> Result<_, DeserializeError> { Ok(TransactionBodies::deserialize(raw)?) })() + .map_err(|e| e.annotate("transaction_bodies"))?; + let transaction_witness_sets = (|| -> Result<_, DeserializeError> { + Ok(TransactionWitnessSets::deserialize(raw)?) + })() + .map_err(|e| e.annotate("transaction_witness_sets"))?; + let auxiliary_data_set = + (|| -> Result<_, DeserializeError> { Ok(AuxiliaryDataSet::deserialize(raw)?) })() + .map_err(|e| e.annotate("auxiliary_data_set"))?; + let invalid_present = match len { + cbor_event::Len::Indefinite => raw.cbor_type()? == CBORType::Array, + cbor_event::Len::Len(4) => false, + _ => true, + }; + let invalid_transactions = (|| -> Result<_, DeserializeError> { + let mut arr = Vec::new(); + if invalid_present { + read_len.read_elems(1)?; + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "Block.invalid_transactions")? { + break; + } + arr.push(TransactionIndex::deserialize(raw)?); + } + } + Ok(arr) + })() + .map_err(|e| e.annotate("invalid_transactions"))?; + match len { + cbor_event::Len::Len(_) => (), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + Ok(Block { + header, + transaction_bodies, + transaction_witness_sets, + auxiliary_data_set, + invalid_transactions, + }) + })() + .map_err(|e| e.annotate("Block")) + } +} diff --git a/rust/src/serialization/block/fixed_block.rs b/rust/src/serialization/block/fixed_block.rs new file mode 100644 index 00000000..b8fd7f3f --- /dev/null +++ b/rust/src/serialization/block/fixed_block.rs @@ -0,0 +1,83 @@ +use crate::serialization::utils::{deserilized_with_orig_bytes, is_break_tag}; +use crate::*; + +impl Deserialize for FixedBlock { + fn deserialize(raw: &mut Deserializer) -> Result { + let (( + header, + transaction_bodies, + transaction_witness_sets, + auxiliary_data_set, + invalid_transactions, + ), orig_bytes) = deserilized_with_orig_bytes(raw, |raw| -> Result<_, DeserializeError> { + deserialize_block(raw) + }).map_err(|e| e.annotate("Block"))?; + let block_hash = BlockHash(blake2b256(orig_bytes.as_ref())); + Ok(FixedBlock { + header, + transaction_bodies, + transaction_witness_sets, + auxiliary_data_set, + invalid_transactions, + block_hash, + }) + } +} + +fn deserialize_block( + raw: &mut Deserializer, +) -> Result<(Header, FixedTransactionBodies, TransactionWitnessSets, AuxiliaryDataSet, TransactionIndexes), DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(4)?; + let header = (|| -> Result<_, DeserializeError> { Ok(Header::deserialize(raw)?) })() + .map_err(|e| e.annotate("header"))?; + let transaction_bodies = (|| -> Result<_, DeserializeError> { + Ok(FixedTransactionBodies::deserialize(raw)?) + })() + .map_err(|e| e.annotate("fixed_transaction_bodies"))?; + let transaction_witness_sets = (|| -> Result<_, DeserializeError> { + Ok(TransactionWitnessSets::deserialize(raw)?) + })() + .map_err(|e| e.annotate("transaction_witness_sets"))?; + let auxiliary_data_set = + (|| -> Result<_, DeserializeError> { Ok(AuxiliaryDataSet::deserialize(raw)?) })() + .map_err(|e| e.annotate("auxiliary_data_set"))?; + let invalid_present = match len { + Len::Indefinite => raw.cbor_type()? == CBORType::Array, + Len::Len(4) => false, + _ => true, + }; + let invalid_transactions = (|| -> Result<_, DeserializeError> { + let mut arr = Vec::new(); + if invalid_present { + read_len.read_elems(1)?; + let len = raw.array()?; + while match len { + Len::Len(n) => arr.len() < n as usize, + Len::Indefinite => true, + } { + if is_break_tag(raw, "Block.invalid_transactions")? { + break; + } + arr.push(TransactionIndex::deserialize(raw)?); + } + } + Ok(arr) + })() + .map_err(|e| e.annotate("invalid_transactions"))?; + match len { + Len::Len(_) => (), + Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + Ok(( + header, + transaction_bodies, + transaction_witness_sets, + auxiliary_data_set, + invalid_transactions, + )) +} diff --git a/rust/src/serialization/block/fixed_transaction_bodies.rs b/rust/src/serialization/block/fixed_transaction_bodies.rs new file mode 100644 index 00000000..ed8c1281 --- /dev/null +++ b/rust/src/serialization/block/fixed_transaction_bodies.rs @@ -0,0 +1,23 @@ +use crate::*; +use crate::serialization::utils::is_break_tag; + +impl Deserialize for FixedTransactionBodies { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "FixedTransactionBodies")? { + break; + } + arr.push(FixedTransactionBody::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("FixedTransactionBodies"))?; + Ok(Self(arr)) + } +} diff --git a/rust/src/serialization/block/fixed_transaction_body.rs b/rust/src/serialization/block/fixed_transaction_body.rs new file mode 100644 index 00000000..9c999215 --- /dev/null +++ b/rust/src/serialization/block/fixed_transaction_body.rs @@ -0,0 +1,17 @@ +use crate::*; +use crate::serialization::utils::{deserilized_with_orig_bytes}; + +impl Deserialize for FixedTransactionBody { + fn deserialize(raw: &mut Deserializer) -> Result { + let (body, orig_bytes) = deserilized_with_orig_bytes(raw, |raw| -> Result<_, DeserializeError> { + let body = TransactionBody::deserialize(raw)?; + Ok(body) + }).map_err(|e| e.annotate("TransactionBody"))?; + let hash = TransactionHash(blake2b256(orig_bytes.as_ref())); + Ok(FixedTransactionBody { + body, + tx_hash: hash, + original_bytes: orig_bytes, + }) + } +} diff --git a/rust/src/serialization/block/fixed_versioned_block.rs b/rust/src/serialization/block/fixed_versioned_block.rs new file mode 100644 index 00000000..eb2a085c --- /dev/null +++ b/rust/src/serialization/block/fixed_versioned_block.rs @@ -0,0 +1,16 @@ +use crate::serialization::utils::{check_len, check_len_indefinite}; +use crate::*; + +impl Deserialize for FixedVersionedBlock { + fn deserialize(raw: &mut Deserializer) -> Result { + let len = raw.array()?; + check_len(len, 2, "VersionedBlock")?; + let era_code = u32::deserialize(raw)?; + let block = FixedBlock::deserialize(raw)?; + check_len_indefinite(raw, len)?; + Ok(FixedVersionedBlock { + block, + era_code, + }) + } +} diff --git a/rust/src/serialization/block/header.rs b/rust/src/serialization/block/header.rs new file mode 100644 index 00000000..c1a5da13 --- /dev/null +++ b/rust/src/serialization/block/header.rs @@ -0,0 +1,57 @@ +use crate::*; + +impl cbor_event::se::Serialize for Header { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.header_body.serialize(serializer)?; + self.body_signature.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for Header { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("Header")) + } +} + +impl DeserializeEmbeddedGroup for Header { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + _: cbor_event::Len, + ) -> Result { + let header_body = + (|| -> Result<_, DeserializeError> { Ok(HeaderBody::deserialize(raw)?) })() + .map_err(|e| e.annotate("header_body"))?; + let body_signature = + (|| -> Result<_, DeserializeError> { Ok(KESSignature::deserialize(raw)?) })() + .map_err(|e| e.annotate("body_signature"))?; + Ok(Header { + header_body, + body_signature, + }) + } +} \ No newline at end of file diff --git a/rust/src/serialization/block/header_body.rs b/rust/src/serialization/block/header_body.rs new file mode 100644 index 00000000..6627e23c --- /dev/null +++ b/rust/src/serialization/block/header_body.rs @@ -0,0 +1,150 @@ +use crate::*; + +impl cbor_event::se::Serialize for HeaderBody { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(15))?; + self.block_number.serialize(serializer)?; + self.slot.serialize(serializer)?; + match &self.prev_hash { + Some(x) => x.serialize(serializer), + None => serializer.write_special(CBORSpecial::Null), + }?; + self.issuer_vkey.serialize(serializer)?; + self.vrf_vkey.serialize(serializer)?; + match &self.leader_cert { + HeaderLeaderCertEnum::NonceAndLeader(nonce_vrf, leader_vrf) => { + nonce_vrf.serialize(serializer)?; + leader_vrf.serialize(serializer)?; + } + HeaderLeaderCertEnum::VrfResult(vrf_cert) => { + vrf_cert.serialize(serializer)?; + } + } + self.block_body_size.serialize(serializer)?; + self.block_body_hash.serialize(serializer)?; + self.operational_cert + .serialize_as_embedded_group(serializer)?; + self.protocol_version + .serialize_as_embedded_group(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for HeaderBody { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("HeaderBody")) + } +} + +impl DeserializeEmbeddedGroup for HeaderBody { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + let block_number = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() + .map_err(|e| e.annotate("block_number"))?; + let slot = (|| -> Result<_, DeserializeError> { Ok(SlotBigNum::deserialize(raw)?) })() + .map_err(|e| e.annotate("slot"))?; + let prev_hash = (|| -> Result<_, DeserializeError> { + Ok(match raw.cbor_type()? != CBORType::Special { + true => Some(BlockHash::deserialize(raw)?), + false => { + if raw.special()? != CBORSpecial::Null { + return Err(DeserializeFailure::ExpectedNull.into()); + } + None + } + }) + })() + .map_err(|e| e.annotate("prev_hash"))?; + let issuer_vkey = (|| -> Result<_, DeserializeError> { Ok(Vkey::deserialize(raw)?) })() + .map_err(|e| e.annotate("issuer_vkey"))?; + let vrf_vkey = (|| -> Result<_, DeserializeError> { Ok(VRFVKey::deserialize(raw)?) })() + .map_err(|e| e.annotate("vrf_vkey"))?; + let leader_cert = { + // NONCE VFR CERT, first of two certs + // or a single VRF RESULT CERT + // depending on the protocol version + let first_vrf_cert = + (|| -> Result<_, DeserializeError> { Ok(VRFCert::deserialize(raw)?) })() + .map_err(|e| e.annotate("nonce_vrf"))?; + let cbor_type: cbor_event::Type = raw.cbor_type()?; + match cbor_type { + cbor_event::Type::Array => { + // Legacy format, reading the second VRF cert + let leader_vrf = + (|| -> Result<_, DeserializeError> { Ok(VRFCert::deserialize(raw)?) })() + .map_err(|e| e.annotate("leader_vrf"))?; + HeaderLeaderCertEnum::NonceAndLeader(first_vrf_cert, leader_vrf) + } + cbor_event::Type::UnsignedInteger => { + // New format, no second VRF cert is present + HeaderLeaderCertEnum::VrfResult(first_vrf_cert) + } + t => { + return Err(DeserializeError::new( + "HeaderBody.leader_cert", + DeserializeFailure::UnexpectedKeyType(t), + )) + } + } + }; + let block_body_size = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() + .map_err(|e| e.annotate("block_body_size"))?; + let block_body_hash = + (|| -> Result<_, DeserializeError> { Ok(BlockHash::deserialize(raw)?) })() + .map_err(|e| e.annotate("block_body_hash"))?; + + let operational_cert = (|| -> Result<_, DeserializeError> { + if raw.cbor_type()? == CBORType::Array { + Ok(OperationalCert::deserialize(raw)?) + } else { + Ok(OperationalCert::deserialize_as_embedded_group(raw, len)?) + } + })() + .map_err(|e| e.annotate("operational_cert"))?; + let protocol_version = (|| -> Result<_, DeserializeError> { + if raw.cbor_type()? == CBORType::Array { + Ok(ProtocolVersion::deserialize(raw)?) + } else { + Ok(ProtocolVersion::deserialize_as_embedded_group(raw, len)?) + } + })() + .map_err(|e| e.annotate("protocol_version"))?; + Ok(HeaderBody { + block_number, + slot, + prev_hash, + issuer_vkey, + vrf_vkey, + leader_cert, + block_body_size, + block_body_hash, + operational_cert, + protocol_version, + }) + } +} \ No newline at end of file diff --git a/rust/src/serialization/block/mod.rs b/rust/src/serialization/block/mod.rs new file mode 100644 index 00000000..54be5d37 --- /dev/null +++ b/rust/src/serialization/block/mod.rs @@ -0,0 +1,10 @@ +mod block; +mod header; +mod operational_cert; +mod header_body; +mod transaction_bodies; +mod fixed_block; +mod fixed_transaction_body; +mod fixed_transaction_bodies; +mod versioned_block; +mod fixed_versioned_block; diff --git a/rust/src/serialization/block/operational_cert.rs b/rust/src/serialization/block/operational_cert.rs new file mode 100644 index 00000000..1b6490ed --- /dev/null +++ b/rust/src/serialization/block/operational_cert.rs @@ -0,0 +1,73 @@ +use crate::*; + +impl cbor_event::se::Serialize for OperationalCert { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(4))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for OperationalCert { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + self.hot_vkey.serialize(serializer)?; + self.sequence_number.serialize(serializer)?; + self.kes_period.serialize(serializer)?; + self.sigma.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for OperationalCert { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("OperationalCert")) + } +} + +impl DeserializeEmbeddedGroup for OperationalCert { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + _: cbor_event::Len, + ) -> Result { + let hot_vkey = (|| -> Result<_, DeserializeError> { Ok(KESVKey::deserialize(raw)?) })() + .map_err(|e| e.annotate("hot_vkey"))?; + let sequence_number = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() + .map_err(|e| e.annotate("sequence_number"))?; + let kes_period = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() + .map_err(|e| e.annotate("kes_period"))?; + let sigma = + (|| -> Result<_, DeserializeError> { Ok(Ed25519Signature::deserialize(raw)?) })() + .map_err(|e| e.annotate("sigma"))?; + Ok(OperationalCert { + hot_vkey, + sequence_number, + kes_period, + sigma, + }) + } +} \ No newline at end of file diff --git a/rust/src/serialization/block/transaction_bodies.rs b/rust/src/serialization/block/transaction_bodies.rs new file mode 100644 index 00000000..eafe1400 --- /dev/null +++ b/rust/src/serialization/block/transaction_bodies.rs @@ -0,0 +1,36 @@ +use crate::*; +use crate::serialization::utils::is_break_tag; + +impl cbor_event::se::Serialize for TransactionBodies { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for TransactionBodies { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "TransactionBodies")? { + break; + } + arr.push(TransactionBody::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("TransactionBodies"))?; + Ok(Self(arr)) + } +} diff --git a/rust/src/serialization/block/versioned_block.rs b/rust/src/serialization/block/versioned_block.rs new file mode 100644 index 00000000..f9ba8890 --- /dev/null +++ b/rust/src/serialization/block/versioned_block.rs @@ -0,0 +1,24 @@ +use crate::serialization::utils::{check_len, check_len_indefinite}; +use crate::*; + +impl Serialize for VersionedBlock { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(Len::Len(2))?; + self.era_code.serialize(serializer)?; + self.block.serialize(serializer) + } +} + +impl Deserialize for VersionedBlock { + fn deserialize(raw: &mut Deserializer) -> Result { + let len = raw.array()?; + check_len(len, 2, "VersionedBlock")?; + let era_code = u32::deserialize(raw)?; + let block = Block::deserialize(raw)?; + check_len_indefinite(raw, len)?; + Ok(VersionedBlock { era_code, block }) + } +} diff --git a/rust/src/serialization/certificates/certificate.rs b/rust/src/serialization/certificates/certificate.rs new file mode 100644 index 00000000..08429c3c --- /dev/null +++ b/rust/src/serialization/certificates/certificate.rs @@ -0,0 +1,183 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::*; +use num_traits::FromPrimitive; +use std::io::{Seek, SeekFrom}; + +impl cbor_event::se::Serialize for CertificateEnum { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + match self { + CertificateEnum::StakeRegistration(x) => x.serialize(serializer), + CertificateEnum::StakeDeregistration(x) => x.serialize(serializer), + CertificateEnum::StakeDelegation(x) => x.serialize(serializer), + CertificateEnum::PoolRegistration(x) => x.serialize(serializer), + CertificateEnum::PoolRetirement(x) => x.serialize(serializer), + CertificateEnum::GenesisKeyDelegation(x) => x.serialize(serializer), + CertificateEnum::MoveInstantaneousRewardsCert(x) => x.serialize(serializer), + CertificateEnum::CommitteeHotAuth(x) => x.serialize(serializer), + CertificateEnum::CommitteeColdResign(x) => x.serialize(serializer), + CertificateEnum::DRepRegistration(x) => x.serialize(serializer), + CertificateEnum::DRepDeregistration(x) => x.serialize(serializer), + CertificateEnum::DRepUpdate(x) => x.serialize(serializer), + CertificateEnum::StakeAndVoteDelegation(x) => x.serialize(serializer), + CertificateEnum::StakeRegistrationAndDelegation(x) => x.serialize(serializer), + CertificateEnum::StakeVoteRegistrationAndDelegation(x) => x.serialize(serializer), + CertificateEnum::VoteDelegation(x) => x.serialize(serializer), + CertificateEnum::VoteRegistrationAndDelegation(x) => x.serialize(serializer), + } + } +} + +impl Deserialize for CertificateEnum { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("CertificateEnum")) + } +} + +impl DeserializeEmbeddedGroup for CertificateEnum { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + let cert_index = get_cert_index(raw)?; + let index_enum = + CertificateIndexNames::from_u64(cert_index).ok_or(DeserializeError::new( + "CertificateEnum", + DeserializeFailure::UnknownKey(Key::Uint(cert_index)), + ))?; + + match index_enum { + CertificateIndexNames::StakeRegistrationLegacy => { + Ok(CertificateEnum::StakeRegistration( + StakeRegistration::deserialize_as_embedded_group(raw, len)?, + )) + } + CertificateIndexNames::StakeRegistrationConway => { + Ok(CertificateEnum::StakeRegistration( + StakeRegistration::deserialize_as_embedded_group(raw, len)?, + )) + } + CertificateIndexNames::StakeDeregistrationLegacy => { + Ok(CertificateEnum::StakeDeregistration( + StakeDeregistration::deserialize_as_embedded_group(raw, len)?, + )) + } + CertificateIndexNames::StakeDeregistrationConway => { + Ok(CertificateEnum::StakeDeregistration( + StakeDeregistration::deserialize_as_embedded_group(raw, len)?, + )) + } + CertificateIndexNames::StakeDelegation => Ok(CertificateEnum::StakeDelegation( + StakeDelegation::deserialize_as_embedded_group(raw, len)?, + )), + + CertificateIndexNames::PoolRegistration => Ok(CertificateEnum::PoolRegistration( + PoolRegistration::deserialize_as_embedded_group(raw, len)?, + )), + CertificateIndexNames::PoolRetirement => Ok(CertificateEnum::PoolRetirement( + PoolRetirement::deserialize_as_embedded_group(raw, len)?, + )), + CertificateIndexNames::GenesisKeyDelegation => { + Ok(CertificateEnum::GenesisKeyDelegation( + GenesisKeyDelegation::deserialize_as_embedded_group(raw, len)?, + )) + } + CertificateIndexNames::MoveInstantaneousRewardsCert => { + Ok(CertificateEnum::MoveInstantaneousRewardsCert( + MoveInstantaneousRewardsCert::deserialize_as_embedded_group(raw, len)?, + )) + } + CertificateIndexNames::CommitteeHotAuth => { + Ok(CertificateEnum::CommitteeHotAuth( + CommitteeHotAuth::deserialize_as_embedded_group(raw, len)?, + )) + } + CertificateIndexNames::CommitteeColdResign => { + Ok(CertificateEnum::CommitteeColdResign( + CommitteeColdResign::deserialize_as_embedded_group(raw, len)?, + )) + } + CertificateIndexNames::DRepRegistration => Ok(CertificateEnum::DRepRegistration( + DRepRegistration::deserialize_as_embedded_group(raw, len)?, + )), + CertificateIndexNames::DRepDeregistration => Ok(CertificateEnum::DRepDeregistration( + DRepDeregistration::deserialize_as_embedded_group(raw, len)?, + )), + CertificateIndexNames::DRepUpdate => Ok(CertificateEnum::DRepUpdate( + DRepUpdate::deserialize_as_embedded_group(raw, len)?, + )), + CertificateIndexNames::StakeAndVoteDelegation => { + Ok(CertificateEnum::StakeAndVoteDelegation( + StakeAndVoteDelegation::deserialize_as_embedded_group(raw, len)?, + )) + } + CertificateIndexNames::StakeRegistrationAndDelegation => { + Ok(CertificateEnum::StakeRegistrationAndDelegation( + StakeRegistrationAndDelegation::deserialize_as_embedded_group(raw, len)?, + )) + } + CertificateIndexNames::StakeVoteRegistrationAndDelegation => { + Ok(CertificateEnum::StakeVoteRegistrationAndDelegation( + StakeVoteRegistrationAndDelegation::deserialize_as_embedded_group(raw, len)?, + )) + } + CertificateIndexNames::VoteDelegation => Ok(CertificateEnum::VoteDelegation( + VoteDelegation::deserialize_as_embedded_group(raw, len)?, + )), + CertificateIndexNames::VoteRegistrationAndDelegation => { + Ok(CertificateEnum::VoteRegistrationAndDelegation( + VoteRegistrationAndDelegation::deserialize_as_embedded_group(raw, len)?, + )) + } + } + } +} + +impl cbor_event::se::Serialize for Certificate { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + self.0.serialize(serializer) + } +} + +impl Deserialize for Certificate { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self(CertificateEnum::deserialize(raw)?)) + } +} + +fn get_cert_index(raw: &mut Deserializer) -> Result { + let initial_position = raw + .as_mut_ref() + .seek(SeekFrom::Current(0)) + .map_err(|err| DeserializeFailure::IoError(err.to_string()))?; + let index = raw.unsigned_integer()?; + raw.as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .map_err(|err| DeserializeFailure::IoError(err.to_string()))?; + Ok(index) +} diff --git a/rust/src/serialization/certificates/certificates_collection.rs b/rust/src/serialization/certificates/certificates_collection.rs new file mode 100644 index 00000000..06b12c7e --- /dev/null +++ b/rust/src/serialization/certificates/certificates_collection.rs @@ -0,0 +1,39 @@ +use crate::serialization::utils::{is_break_tag, skip_set_tag}; +use crate::*; + +impl Serialize for Certificates { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + //TODO: uncomment this line when we conway ero will come + //serializer.write_tag(258)?; + serializer.write_array(Len::Len(self.len() as u64))?; + for element in &self.certs { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Certificates { + fn deserialize(raw: &mut Deserializer) -> Result { + skip_set_tag(raw)?; + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "Certificates")? { + break; + } + arr.push(Certificate::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("Certificates"))?; + Ok(Self::from_vec(arr)) + } +} diff --git a/rust/src/serialization/certificates/committee_cold_resign.rs b/rust/src/serialization/certificates/committee_cold_resign.rs new file mode 100644 index 00000000..f00adcbe --- /dev/null +++ b/rust/src/serialization/certificates/committee_cold_resign.rs @@ -0,0 +1,41 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{ + check_len, deserialize_and_check_index, serialize_and_check_index, +}; +use crate::*; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for CommitteeColdResign { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(3))?; + let proposal_index = CertificateIndexNames::CommitteeColdResign.to_u64(); + serialize_and_check_index(serializer, proposal_index, "CommitteeColdResign")?; + + self.committee_cold_credential.serialize(serializer)?; + self.anchor.serialize_nullable(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(CommitteeColdResign); + +impl DeserializeEmbeddedGroup for CommitteeColdResign { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len(len, 3, "(cert_index, committee_cold_key, anchor)")?; + + let cert_index = CertificateIndexNames::CommitteeColdResign.to_u64(); + deserialize_and_check_index(raw, cert_index, "cert_index")?; + + let committee_cold_key = + Credential::deserialize(raw).map_err(|e| e.annotate("committee_cold_key"))?; + let anchor = Anchor::deserialize_nullable(raw).map_err(|e| e.annotate("anchor"))?; + + Ok(CommitteeColdResign { committee_cold_credential: committee_cold_key, anchor }) + } +} diff --git a/rust/src/serialization/certificates/committee_hot_auth.rs b/rust/src/serialization/certificates/committee_hot_auth.rs new file mode 100644 index 00000000..9f378ceb --- /dev/null +++ b/rust/src/serialization/certificates/committee_hot_auth.rs @@ -0,0 +1,51 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{ + check_len, deserialize_and_check_index, serialize_and_check_index, +}; +use crate::*; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for CommitteeHotAuth { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(3))?; + + let proposal_index = CertificateIndexNames::CommitteeHotAuth.to_u64(); + serialize_and_check_index(serializer, proposal_index, "CommitteeHotAuth")?; + + self.committee_cold_credential.serialize(serializer)?; + self.committee_hot_credential.serialize(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(CommitteeHotAuth); + +impl DeserializeEmbeddedGroup for CommitteeHotAuth { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len( + len, + 3, + "(cert_index, committee_cold_key, committee_hot_key)", + )?; + + let cert_index = CertificateIndexNames::CommitteeHotAuth.to_u64(); + deserialize_and_check_index(raw, cert_index, "cert_index")?; + + let committee_cold_key = + Credential::deserialize(raw).map_err(|e| e.annotate("committee_cold_key"))?; + + let committee_hot_key = + Credential::deserialize(raw).map_err(|e| e.annotate("committee_hot_key"))?; + + Ok(CommitteeHotAuth { + committee_cold_credential: committee_cold_key, + committee_hot_credential: committee_hot_key, + }) + } +} diff --git a/rust/src/serialization/certificates/drep_deregistration.rs b/rust/src/serialization/certificates/drep_deregistration.rs new file mode 100644 index 00000000..14381382 --- /dev/null +++ b/rust/src/serialization/certificates/drep_deregistration.rs @@ -0,0 +1,46 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{ + check_len, deserialize_and_check_index, serialize_and_check_index, +}; +use crate::*; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for DRepDeregistration { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(3))?; + + let proposal_index = CertificateIndexNames::DRepDeregistration.to_u64(); + serialize_and_check_index(serializer, proposal_index, "DRepDeregistration")?; + + self.voting_credential.serialize(serializer)?; + self.coin.serialize(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(DRepDeregistration); + +impl DeserializeEmbeddedGroup for DRepDeregistration { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len(len, 3, "(cert_index, voting_credential, coin)")?; + + let cert_index = CertificateIndexNames::DRepDeregistration.to_u64(); + deserialize_and_check_index(raw, cert_index, "cert_index")?; + + let voting_credential = + Credential::deserialize(raw).map_err(|e| e.annotate("voting_credential"))?; + + let coin = Coin::deserialize(raw).map_err(|e| e.annotate("coin"))?; + + Ok(DRepDeregistration { + voting_credential, + coin, + }) + } +} diff --git a/rust/src/serialization/certificates/drep_registration.rs b/rust/src/serialization/certificates/drep_registration.rs new file mode 100644 index 00000000..4e4da473 --- /dev/null +++ b/rust/src/serialization/certificates/drep_registration.rs @@ -0,0 +1,57 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{ + check_len, deserialize_and_check_index, serialize_and_check_index, +}; +use crate::*; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for DRepRegistration { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(4))?; + + let proposal_index = CertificateIndexNames::DRepRegistration.to_u64(); + serialize_and_check_index(serializer, proposal_index, "DRepRegistration")?; + + self.voting_credential.serialize(serializer)?; + self.coin.serialize(serializer)?; + match &self.anchor { + Some(anchor) => anchor.serialize(serializer), + None => serializer.write_special(CBORSpecial::Null), + }?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(DRepRegistration); + +impl DeserializeEmbeddedGroup for DRepRegistration { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len( + len, + 4, + "(cert_index, voting_credential, coin, anchor / null)", + )?; + + let cert_index = CertificateIndexNames::DRepRegistration.to_u64(); + deserialize_and_check_index(raw, cert_index, "cert_index")?; + + let voting_credential = + Credential::deserialize(raw).map_err(|e| e.annotate("voting_credential"))?; + + let coin = Coin::deserialize(raw).map_err(|e| e.annotate("coin"))?; + + let anchor = Anchor::deserialize_nullable(raw).map_err(|e| e.annotate("anchor"))?; + + Ok(DRepRegistration { + voting_credential, + coin, + anchor, + }) + } +} diff --git a/rust/src/serialization/certificates/drep_update.rs b/rust/src/serialization/certificates/drep_update.rs new file mode 100644 index 00000000..b5a09fac --- /dev/null +++ b/rust/src/serialization/certificates/drep_update.rs @@ -0,0 +1,49 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{ + check_len, deserialize_and_check_index, serialize_and_check_index, +}; +use crate::*; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for DRepUpdate { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(3))?; + + let proposal_index = CertificateIndexNames::DRepUpdate.to_u64(); + serialize_and_check_index(serializer, proposal_index, "DRepUpdate")?; + + self.voting_credential.serialize(serializer)?; + match &self.anchor { + Some(anchor) => anchor.serialize(serializer), + None => serializer.write_special(CBORSpecial::Null), + }?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(DRepUpdate); + +impl DeserializeEmbeddedGroup for DRepUpdate { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len(len, 3, "(cert_index, voting_credential, anchor / null)")?; + + let cert_index = CertificateIndexNames::DRepUpdate.to_u64(); + deserialize_and_check_index(raw, cert_index, "cert_index")?; + + let voting_credential = + Credential::deserialize(raw).map_err(|e| e.annotate("voting_credential"))?; + + let anchor = Anchor::deserialize_nullable(raw).map_err(|e| e.annotate("anchor"))?; + + Ok(DRepUpdate { + voting_credential, + anchor, + }) + } +} diff --git a/rust/src/serialization/certificates/genesis_key_delegation.rs b/rust/src/serialization/certificates/genesis_key_delegation.rs new file mode 100644 index 00000000..bad46599 --- /dev/null +++ b/rust/src/serialization/certificates/genesis_key_delegation.rs @@ -0,0 +1,64 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{ + check_len, deserialize_and_check_index, serialize_and_check_index, +}; +use crate::*; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for GenesisKeyDelegation { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(4))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for GenesisKeyDelegation { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + let proposal_index = CertificateIndexNames::GenesisKeyDelegation.to_u64(); + serialize_and_check_index(serializer, proposal_index, "GenesisKeyDelegation")?; + + self.genesishash.serialize(serializer)?; + self.genesis_delegate_hash.serialize(serializer)?; + self.vrf_keyhash.serialize(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(GenesisKeyDelegation); + +impl DeserializeEmbeddedGroup for GenesisKeyDelegation { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len( + len, + 4, + "(cert_index, genesishash, genesis_delegate_hash, vrf_keyhash)", + )?; + + let cert_index = CertificateIndexNames::GenesisKeyDelegation.to_u64(); + deserialize_and_check_index(raw, cert_index, "cert_index")?; + + let genesishash = + (|| -> Result<_, DeserializeError> { Ok(GenesisHash::deserialize(raw)?) })() + .map_err(|e| e.annotate("genesishash"))?; + let genesis_delegate_hash = + (|| -> Result<_, DeserializeError> { Ok(GenesisDelegateHash::deserialize(raw)?) })() + .map_err(|e| e.annotate("genesis_delegate_hash"))?; + let vrf_keyhash = + (|| -> Result<_, DeserializeError> { Ok(VRFKeyHash::deserialize(raw)?) })() + .map_err(|e| e.annotate("vrf_keyhash"))?; + Ok(GenesisKeyDelegation { + genesishash, + genesis_delegate_hash, + vrf_keyhash, + }) + } +} diff --git a/rust/src/serialization/certificates/mod.rs b/rust/src/serialization/certificates/mod.rs new file mode 100644 index 00000000..8878f390 --- /dev/null +++ b/rust/src/serialization/certificates/mod.rs @@ -0,0 +1,19 @@ +mod certificate; +mod certificates_collection; +mod committee_cold_resign; +mod committee_hot_auth; +mod drep_deregistration; +mod drep_registration; +mod drep_update; +mod genesis_key_delegation; +mod move_instantaneous_rewards_cert; +mod pool_registration; +mod pool_retirement; +mod stake_and_vote_delegation; +mod stake_delegation; +mod stake_deregistration; +mod stake_registration; +mod stake_registration_and_delegation; +mod stake_vote_registration_and_delegation; +mod vote_delegation; +mod vote_registration_and_delegation; diff --git a/rust/src/serialization/certificates/move_instantaneous_rewards_cert.rs b/rust/src/serialization/certificates/move_instantaneous_rewards_cert.rs new file mode 100644 index 00000000..6c6079e2 --- /dev/null +++ b/rust/src/serialization/certificates/move_instantaneous_rewards_cert.rs @@ -0,0 +1,151 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{check_len, deserialize_and_check_index, is_break_tag, serialize_and_check_index}; +use crate::*; +use num_traits::ToPrimitive; +use hashlink::LinkedHashMap; + +impl cbor_event::se::Serialize for MIRToStakeCredentials { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.rewards.len() as u64))?; + for (key, value) in &self.rewards { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for MIRToStakeCredentials { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let mut table = LinkedHashMap::new(); + let len = raw.map()?; + while match len { + cbor_event::Len::Len(n) => table.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "MIRToStakeCredentials")? { + break; + } + let key = Credential::deserialize(raw)?; + let value = DeltaCoin::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(format!( + "StakeCred: {} (hex bytes)", + hex::encode(key.to_bytes()) + ))) + .into()); + } + } + Ok(Self { rewards: table }) + })() + .map_err(|e| e.annotate("MIRToStakeCredentials")) + } +} + +impl cbor_event::se::Serialize for MoveInstantaneousReward { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + match self.pot { + MIRPot::Reserves => serializer.write_unsigned_integer(0u64), + MIRPot::Treasury => serializer.write_unsigned_integer(1u64), + }?; + match &self.variant { + MIREnum::ToOtherPot(amount) => amount.serialize(serializer), + MIREnum::ToStakeCredentials(amounts) => amounts.serialize(serializer), + } + } +} + +impl Deserialize for MoveInstantaneousReward { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let outer_len = raw.array()?; + let pot = match raw.unsigned_integer()? { + 0 => MIRPot::Reserves, + 1 => MIRPot::Treasury, + n => return Err(DeserializeFailure::UnknownKey(Key::Uint(n)).into()), + }; + let variant = match raw.cbor_type()? { + CBORType::UnsignedInteger => MIREnum::ToOtherPot(Coin::deserialize(raw)?), + CBORType::Map => { + MIREnum::ToStakeCredentials(MIRToStakeCredentials::deserialize(raw)?) + } + _ => return Err(DeserializeFailure::NoVariantMatched.into()), + }; + match outer_len { + cbor_event::Len::Len(n) => { + if n != 2 { + return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( + n, + outer_len, + "MoveInstantaneousReward", + )) + .into()); + } + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + }; + Ok(Self { pot, variant }) + })() + .map_err(|e| e.annotate("MoveInstantaneousReward")) + } +} + +impl cbor_event::se::Serialize for MoveInstantaneousRewardsCert { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for MoveInstantaneousRewardsCert { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + let proposal_index = CertificateIndexNames::MoveInstantaneousRewardsCert.to_u64(); + serialize_and_check_index(serializer, proposal_index, "MoveInstantaneousRewardsCert")?; + + self.move_instantaneous_reward.serialize(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(MoveInstantaneousRewardsCert); + +impl DeserializeEmbeddedGroup for MoveInstantaneousRewardsCert { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len(len, 2, "(cert_index, move_instantaneous_reward)")?; + + let cert_index = CertificateIndexNames::MoveInstantaneousRewardsCert.to_u64(); + deserialize_and_check_index(raw, cert_index, "cert_index")?; + + let move_instantaneous_reward = + (|| -> Result<_, DeserializeError> { Ok(MoveInstantaneousReward::deserialize(raw)?) })( + ) + .map_err(|e| e.annotate("move_instantaneous_reward"))?; + Ok(MoveInstantaneousRewardsCert { + move_instantaneous_reward, + }) + } +} diff --git a/rust/src/serialization/certificates/pool_registration.rs b/rust/src/serialization/certificates/pool_registration.rs new file mode 100644 index 00000000..d02f87e2 --- /dev/null +++ b/rust/src/serialization/certificates/pool_registration.rs @@ -0,0 +1,165 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{check_len, deserialize_and_check_index, is_break_tag, serialize_and_check_index}; +use crate::*; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for Relays { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Relays { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "Relays")? { + break; + } + arr.push(Relay::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("Relays"))?; + Ok(Self(arr)) + } +} + +impl cbor_event::se::Serialize for PoolParams { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(9))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for PoolParams { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + self.operator.serialize(serializer)?; + self.vrf_keyhash.serialize(serializer)?; + self.pledge.serialize(serializer)?; + self.cost.serialize(serializer)?; + self.margin.serialize(serializer)?; + self.reward_account.serialize(serializer)?; + self.pool_owners.serialize(serializer)?; + self.relays.serialize(serializer)?; + match &self.pool_metadata { + Some(x) => x.serialize(serializer), + None => serializer.write_special(CBORSpecial::Null), + }?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(PoolParams); + +impl DeserializeEmbeddedGroup for PoolParams { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + _: cbor_event::Len, + ) -> Result { + let operator = + (|| -> Result<_, DeserializeError> { Ok(Ed25519KeyHash::deserialize(raw)?) })() + .map_err(|e| e.annotate("operator"))?; + let vrf_keyhash = + (|| -> Result<_, DeserializeError> { Ok(VRFKeyHash::deserialize(raw)?) })() + .map_err(|e| e.annotate("vrf_keyhash"))?; + let pledge = (|| -> Result<_, DeserializeError> { Ok(Coin::deserialize(raw)?) })() + .map_err(|e| e.annotate("pledge"))?; + let cost = (|| -> Result<_, DeserializeError> { Ok(Coin::deserialize(raw)?) })() + .map_err(|e| e.annotate("cost"))?; + let margin = (|| -> Result<_, DeserializeError> { Ok(UnitInterval::deserialize(raw)?) })() + .map_err(|e| e.annotate("margin"))?; + let reward_account = + (|| -> Result<_, DeserializeError> { Ok(RewardAddress::deserialize(raw)?) })() + .map_err(|e| e.annotate("reward_account"))?; + let pool_owners = + (|| -> Result<_, DeserializeError> { Ok(Ed25519KeyHashes::deserialize(raw)?) })() + .map_err(|e| e.annotate("pool_owners"))?; + let relays = (|| -> Result<_, DeserializeError> { Ok(Relays::deserialize(raw)?) })() + .map_err(|e| e.annotate("relays"))?; + let pool_metadata = (|| -> Result<_, DeserializeError> { + Ok(match raw.cbor_type()? != CBORType::Special { + true => Some(PoolMetadata::deserialize(raw)?), + false => { + if raw.special()? != CBORSpecial::Null { + return Err(DeserializeFailure::ExpectedNull.into()); + } + None + } + }) + })() + .map_err(|e| e.annotate("pool_metadata"))?; + Ok(PoolParams { + operator, + vrf_keyhash, + pledge, + cost, + margin, + reward_account, + pool_owners, + relays, + pool_metadata, + }) + } +} + +impl cbor_event::se::Serialize for PoolRegistration { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(10))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for PoolRegistration { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + let proposal_index = CertificateIndexNames::PoolRegistration.to_u64(); + serialize_and_check_index(serializer, proposal_index, "PoolRegistration")?; + + self.pool_params.serialize_as_embedded_group(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(PoolRegistration); + +impl DeserializeEmbeddedGroup for PoolRegistration { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len(len, 10, "(cert_index, pool_params (without array) )")?; + + let cert_index = CertificateIndexNames::PoolRegistration.to_u64(); + deserialize_and_check_index(raw, cert_index, "cert_index")?; + + let pool_params = (|| -> Result<_, DeserializeError> { + Ok(PoolParams::deserialize_as_embedded_group(raw, len)?) + })() + .map_err(|e| e.annotate("pool_params"))?; + Ok(PoolRegistration { pool_params }) + } +} diff --git a/rust/src/serialization/certificates/pool_retirement.rs b/rust/src/serialization/certificates/pool_retirement.rs new file mode 100644 index 00000000..b1629fee --- /dev/null +++ b/rust/src/serialization/certificates/pool_retirement.rs @@ -0,0 +1,53 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{ + check_len, deserialize_and_check_index, serialize_and_check_index, +}; +use crate::*; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for PoolRetirement { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(3))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for PoolRetirement { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + let proposal_index = CertificateIndexNames::PoolRetirement.to_u64(); + serialize_and_check_index(serializer, proposal_index, "PoolRetirement")?; + + self.pool_keyhash.serialize(serializer)?; + self.epoch.serialize(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(PoolRetirement); + +impl DeserializeEmbeddedGroup for PoolRetirement { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len(len, 3, "(cert_index, pool_keyhash, epoch)")?; + let cert_index = CertificateIndexNames::PoolRetirement.to_u64(); + deserialize_and_check_index(raw, cert_index, "cert_index")?; + + let pool_keyhash = + (|| -> Result<_, DeserializeError> { Ok(Ed25519KeyHash::deserialize(raw)?) })() + .map_err(|e| e.annotate("pool_keyhash"))?; + let epoch = (|| -> Result<_, DeserializeError> { Ok(Epoch::deserialize(raw)?) })() + .map_err(|e| e.annotate("epoch"))?; + Ok(PoolRetirement { + pool_keyhash, + epoch, + }) + } +} diff --git a/rust/src/serialization/certificates/stake_and_vote_delegation.rs b/rust/src/serialization/certificates/stake_and_vote_delegation.rs new file mode 100644 index 00000000..3c88447c --- /dev/null +++ b/rust/src/serialization/certificates/stake_and_vote_delegation.rs @@ -0,0 +1,50 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{ + check_len, deserialize_and_check_index, serialize_and_check_index, +}; +use crate::*; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for StakeAndVoteDelegation { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(4))?; + + let proposal_index = CertificateIndexNames::StakeAndVoteDelegation.to_u64(); + serialize_and_check_index(serializer, proposal_index, "StakeAndVoteDelegation")?; + + self.stake_credential.serialize(serializer)?; + self.pool_keyhash.serialize(serializer)?; + self.drep.serialize(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(StakeAndVoteDelegation); + +impl DeserializeEmbeddedGroup for StakeAndVoteDelegation { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len(len, 4, "(cert_index, stake_credential, pool_keyhash, drep)")?; + let cert_index = CertificateIndexNames::StakeAndVoteDelegation.to_u64(); + deserialize_and_check_index(raw, cert_index, "cert_index")?; + + let stake_credential = + Credential::deserialize(raw).map_err(|e| e.annotate("stake_credential"))?; + + let pool_keyhash = + Ed25519KeyHash::deserialize(raw).map_err(|e| e.annotate("pool_keyhash"))?; + + let drep = DRep::deserialize(raw).map_err(|e| e.annotate("drep"))?; + + Ok(StakeAndVoteDelegation { + stake_credential, + pool_keyhash, + drep, + }) + } +} diff --git a/rust/src/serialization/certificates/stake_delegation.rs b/rust/src/serialization/certificates/stake_delegation.rs new file mode 100644 index 00000000..93122cae --- /dev/null +++ b/rust/src/serialization/certificates/stake_delegation.rs @@ -0,0 +1,54 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{ + check_len, deserialize_and_check_index, serialize_and_check_index, +}; +use crate::*; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for StakeDelegation { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(3))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for StakeDelegation { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + let proposal_index = CertificateIndexNames::StakeDelegation.to_u64(); + serialize_and_check_index(serializer, proposal_index, "StakeDelegation")?; + + self.stake_credential.serialize(serializer)?; + self.pool_keyhash.serialize(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(StakeDelegation); + +impl DeserializeEmbeddedGroup for StakeDelegation { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len(len, 3, "(cert_index, stake_credential, pool_keyhash)")?; + let cert_index = CertificateIndexNames::StakeDelegation.to_u64(); + deserialize_and_check_index(raw, cert_index, "cert_index")?; + + let stake_credential = + (|| -> Result<_, DeserializeError> { Ok(Credential::deserialize(raw)?) })() + .map_err(|e| e.annotate("stake_credential"))?; + let pool_keyhash = + (|| -> Result<_, DeserializeError> { Ok(Ed25519KeyHash::deserialize(raw)?) })() + .map_err(|e| e.annotate("pool_keyhash"))?; + Ok(StakeDelegation { + stake_credential, + pool_keyhash, + }) + } +} diff --git a/rust/src/serialization/certificates/stake_deregistration.rs b/rust/src/serialization/certificates/stake_deregistration.rs new file mode 100644 index 00000000..c597addd --- /dev/null +++ b/rust/src/serialization/certificates/stake_deregistration.rs @@ -0,0 +1,119 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{check_index, check_len, serialize_and_check_index}; +use crate::*; +use cbor_event::Len; +use num_traits::{FromPrimitive, ToPrimitive}; + +impl cbor_event::se::Serialize for StakeDeregistration { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + if self.coin.is_some() { + serialize_as_conway(self, serializer) + } else { + serialize_as_legacy(self, serializer) + } + } +} + +fn serialize_as_legacy<'se, W: Write>( + cert: &StakeDeregistration, + serializer: &'se mut Serializer, +) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + + let proposal_index = CertificateIndexNames::StakeDeregistrationLegacy.to_u64(); + serialize_and_check_index(serializer, proposal_index, "StakeDeregistrationLegacy")?; + + cert.stake_credential.serialize(serializer)?; + Ok(serializer) +} + +fn serialize_as_conway<'se, W: Write>( + cert: &StakeDeregistration, + serializer: &'se mut Serializer, +) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(3))?; + + let proposal_index = CertificateIndexNames::StakeDeregistrationConway.to_u64(); + serialize_and_check_index(serializer, proposal_index, "StakeDeregistrationConway")?; + + cert.stake_credential.serialize(serializer)?; + if let Some(coin) = cert.coin { + coin.serialize(serializer)?; + } + Ok(serializer) +} + +impl_deserialize_for_wrapped_tuple!(StakeDeregistration); + +impl DeserializeEmbeddedGroup for StakeDeregistration { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: Len, + ) -> Result { + let cert_index = raw.unsigned_integer()?; + let index_enum = CertificateIndexNames::from_u64(cert_index); + match index_enum { + Some(CertificateIndexNames::StakeDeregistrationLegacy) => { + deserialize_legacy(raw, cert_index, len) + } + Some(CertificateIndexNames::StakeDeregistrationConway) => { + deserialize_conway(raw, cert_index, len) + } + _ => Err(DeserializeFailure::FixedValuesMismatch { + found: Key::Uint(cert_index), + expected: vec![ + Key::OptUint(CertificateIndexNames::StakeDeregistrationLegacy.to_u64()), + Key::OptUint(CertificateIndexNames::StakeDeregistrationConway.to_u64()), + ], + }) + .map_err(|e| DeserializeError::from(e).annotate("cert_index")), + } + } +} + +fn deserialize_legacy( + raw: &mut Deserializer, + cert_index: u64, + len: Len, +) -> Result { + (|| -> Result<_, DeserializeError> { + check_len(len, 2, "(cert_index, stake_credential)")?; + let desired_index = CertificateIndexNames::StakeDeregistrationLegacy.to_u64(); + check_index(cert_index, desired_index, "cert_index")?; + + let stake_credential = + Credential::deserialize(raw).map_err(|e| e.annotate("stake_credential"))?; + + return Ok(StakeDeregistration { + stake_credential, + coin: None, + }); + })() + .map_err(|e| e.annotate("StakeDeregistration (legacy)")) +} + +fn deserialize_conway( + raw: &mut Deserializer, + cert_index: u64, + len: Len, +) -> Result { + (|| -> Result<_, DeserializeError> { + check_len(len, 3, "(cert_index, stake_credential, coin)")?; + let desired_index = CertificateIndexNames::StakeDeregistrationConway.to_u64(); + check_index(cert_index, desired_index, "cert_index")?; + + let stake_credential = + Credential::deserialize(raw).map_err(|e| e.annotate("stake_credential"))?; + + let coin = Coin::deserialize(raw).map_err(|e| e.annotate("coin"))?; + + return Ok(StakeDeregistration { + stake_credential, + coin: Some(coin), + }); + })() + .map_err(|e| e.annotate("StakeDeregistration (conway)")) +} diff --git a/rust/src/serialization/certificates/stake_registration.rs b/rust/src/serialization/certificates/stake_registration.rs new file mode 100644 index 00000000..bf19894b --- /dev/null +++ b/rust/src/serialization/certificates/stake_registration.rs @@ -0,0 +1,119 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{check_index, check_len, serialize_and_check_index}; +use crate::*; +use cbor_event::Len; +use num_traits::{FromPrimitive, ToPrimitive}; + +impl cbor_event::se::Serialize for StakeRegistration { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + if self.coin.is_some() { + serialize_as_conway(self, serializer) + } else { + serialize_as_legacy(self, serializer) + } + } +} + +fn serialize_as_legacy<'se, W: Write>( + cert: &StakeRegistration, + serializer: &'se mut Serializer, +) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + + let proposal_index = CertificateIndexNames::StakeRegistrationLegacy.to_u64(); + serialize_and_check_index(serializer, proposal_index, "StakeRegistrationLegacy")?; + + cert.stake_credential.serialize(serializer)?; + Ok(serializer) +} + +fn serialize_as_conway<'se, W: Write>( + cert: &StakeRegistration, + serializer: &'se mut Serializer, +) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(3))?; + + let proposal_index = CertificateIndexNames::StakeRegistrationConway.to_u64(); + serialize_and_check_index(serializer, proposal_index, "StakeRegistrationConway")?; + + cert.stake_credential.serialize(serializer)?; + if let Some(coin) = cert.coin { + coin.serialize(serializer)?; + } + Ok(serializer) +} + +impl_deserialize_for_wrapped_tuple!(StakeRegistration); + +impl DeserializeEmbeddedGroup for StakeRegistration { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: Len, + ) -> Result { + let cert_index = raw.unsigned_integer()?; + let index_enum = CertificateIndexNames::from_u64(cert_index); + match index_enum { + Some(CertificateIndexNames::StakeRegistrationLegacy) => { + deserialize_legacy(raw, cert_index, len) + } + Some(CertificateIndexNames::StakeRegistrationConway) => { + deserialize_conway(raw, cert_index, len) + } + _ => Err(DeserializeFailure::FixedValuesMismatch { + found: Key::Uint(cert_index), + expected: vec![ + Key::OptUint(CertificateIndexNames::StakeRegistrationLegacy.to_u64()), + Key::OptUint(CertificateIndexNames::StakeRegistrationConway.to_u64()), + ], + }) + .map_err(|e| DeserializeError::from(e).annotate("cert_index")), + } + } +} + +fn deserialize_legacy( + raw: &mut Deserializer, + cert_index: u64, + len: Len, +) -> Result { + (|| -> Result<_, DeserializeError> { + check_len(len, 2, "(cert_index, stake_credential)")?; + let desired_index = CertificateIndexNames::StakeRegistrationLegacy.to_u64(); + check_index(cert_index, desired_index, "cert_index")?; + + let stake_credential = + Credential::deserialize(raw).map_err(|e| e.annotate("stake_credential"))?; + + return Ok(StakeRegistration { + stake_credential, + coin: None, + }); + })() + .map_err(|e| e.annotate("StakeRegistration (legacy)")) +} + +fn deserialize_conway( + raw: &mut Deserializer, + cert_index: u64, + len: Len, +) -> Result { + (|| -> Result<_, DeserializeError> { + check_len(len, 3, "(cert_index, stake_credential, coin)")?; + let desired_index = CertificateIndexNames::StakeRegistrationConway.to_u64(); + check_index(cert_index, desired_index, "cert_index")?; + + let stake_credential = + Credential::deserialize(raw).map_err(|e| e.annotate("stake_credential"))?; + + let coin = Coin::deserialize(raw).map_err(|e| e.annotate("coin"))?; + + return Ok(StakeRegistration { + stake_credential, + coin: Some(coin), + }); + })() + .map_err(|e| e.annotate("StakeRegistration (conway)")) +} diff --git a/rust/src/serialization/certificates/stake_registration_and_delegation.rs b/rust/src/serialization/certificates/stake_registration_and_delegation.rs new file mode 100644 index 00000000..3c847d92 --- /dev/null +++ b/rust/src/serialization/certificates/stake_registration_and_delegation.rs @@ -0,0 +1,50 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{ + check_len, deserialize_and_check_index, serialize_and_check_index, +}; +use crate::*; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for StakeRegistrationAndDelegation { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(4))?; + + let proposal_index = CertificateIndexNames::StakeRegistrationAndDelegation.to_u64(); + serialize_and_check_index(serializer, proposal_index, "StakeRegistrationAndDelegation")?; + + self.stake_credential.serialize(serializer)?; + self.pool_keyhash.serialize(serializer)?; + self.coin.serialize(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(StakeRegistrationAndDelegation); + +impl DeserializeEmbeddedGroup for StakeRegistrationAndDelegation { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len(len, 4, "(cert_index, stake_credential, pool_keyhash, coin)")?; + let cert_index = CertificateIndexNames::StakeRegistrationAndDelegation.to_u64(); + deserialize_and_check_index(raw, cert_index, "cert_index")?; + + let stake_credential = + Credential::deserialize(raw).map_err(|e| e.annotate("stake_credential"))?; + + let pool_keyhash = + Ed25519KeyHash::deserialize(raw).map_err(|e| e.annotate("pool_keyhash"))?; + + let coin = Coin::deserialize(raw).map_err(|e| e.annotate("coin"))?; + + Ok(StakeRegistrationAndDelegation { + stake_credential, + pool_keyhash, + coin, + }) + } +} diff --git a/rust/src/serialization/certificates/stake_vote_registration_and_delegation.rs b/rust/src/serialization/certificates/stake_vote_registration_and_delegation.rs new file mode 100644 index 00000000..37167b91 --- /dev/null +++ b/rust/src/serialization/certificates/stake_vote_registration_and_delegation.rs @@ -0,0 +1,62 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{ + check_len, deserialize_and_check_index, serialize_and_check_index, +}; +use crate::*; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for StakeVoteRegistrationAndDelegation { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(5))?; + + let proposal_index = CertificateIndexNames::StakeVoteRegistrationAndDelegation.to_u64(); + serialize_and_check_index( + serializer, + proposal_index, + "StakeVoteRegistrationAndDelegation", + )?; + + self.stake_credential.serialize(serializer)?; + self.pool_keyhash.serialize(serializer)?; + self.drep.serialize(serializer)?; + self.coin.serialize(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(StakeVoteRegistrationAndDelegation); + +impl DeserializeEmbeddedGroup for StakeVoteRegistrationAndDelegation { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len( + len, + 5, + "(cert_index, stake_credential, pool_keyhash, drep, coin)", + )?; + let cert_index = CertificateIndexNames::StakeVoteRegistrationAndDelegation.to_u64(); + deserialize_and_check_index(raw, cert_index, "cert_index")?; + + let stake_credential = + Credential::deserialize(raw).map_err(|e| e.annotate("stake_credential"))?; + + let pool_keyhash = + Ed25519KeyHash::deserialize(raw).map_err(|e| e.annotate("pool_keyhash"))?; + + let drep = DRep::deserialize(raw).map_err(|e| e.annotate("drep"))?; + + let coin = Coin::deserialize(raw).map_err(|e| e.annotate("coin"))?; + + Ok(StakeVoteRegistrationAndDelegation { + stake_credential, + pool_keyhash, + drep, + coin, + }) + } +} diff --git a/rust/src/serialization/certificates/vote_delegation.rs b/rust/src/serialization/certificates/vote_delegation.rs new file mode 100644 index 00000000..a63e49b6 --- /dev/null +++ b/rust/src/serialization/certificates/vote_delegation.rs @@ -0,0 +1,45 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{ + check_len, deserialize_and_check_index, serialize_and_check_index, +}; +use crate::*; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for VoteDelegation { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(3))?; + + let proposal_index = CertificateIndexNames::VoteDelegation.to_u64(); + serialize_and_check_index(serializer, proposal_index, "VoteDelegation")?; + + self.stake_credential.serialize(serializer)?; + self.drep.serialize(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(VoteDelegation); + +impl DeserializeEmbeddedGroup for VoteDelegation { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len(len, 3, "(cert_index, stake_credential, drep)")?; + let cert_index = CertificateIndexNames::VoteDelegation.to_u64(); + deserialize_and_check_index(raw, cert_index, "cert_index")?; + + let stake_credential = + Credential::deserialize(raw).map_err(|e| e.annotate("stake_credential"))?; + + let drep = DRep::deserialize(raw).map_err(|e| e.annotate("drep"))?; + + Ok(VoteDelegation { + stake_credential, + drep, + }) + } +} diff --git a/rust/src/serialization/certificates/vote_registration_and_delegation.rs b/rust/src/serialization/certificates/vote_registration_and_delegation.rs new file mode 100644 index 00000000..52cd29e1 --- /dev/null +++ b/rust/src/serialization/certificates/vote_registration_and_delegation.rs @@ -0,0 +1,50 @@ +use crate::serialization::map_names::CertificateIndexNames; +use crate::serialization::utils::{ + check_len, deserialize_and_check_index, serialize_and_check_index, +}; +use crate::*; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for VoteRegistrationAndDelegation { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(4))?; + + let proposal_index = CertificateIndexNames::VoteRegistrationAndDelegation.to_u64(); + serialize_and_check_index(serializer, proposal_index, "VoteRegistrationAndDelegation")?; + + self.stake_credential.serialize(serializer)?; + self.drep.serialize(serializer)?; + self.coin.serialize(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(VoteRegistrationAndDelegation); + +impl DeserializeEmbeddedGroup for VoteRegistrationAndDelegation { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len(len, 4, "(cert_index, stake_credential, drep, coin)")?; + + let desired_index = CertificateIndexNames::VoteRegistrationAndDelegation.to_u64(); + deserialize_and_check_index(raw, desired_index, "cert_index")?; + + let stake_credential = + Credential::deserialize(raw).map_err(|e| e.annotate("stake_credential"))?; + + let drep = DRep::deserialize(raw).map_err(|e| e.annotate("drep"))?; + + let coin = Coin::deserialize(raw).map_err(|e| e.annotate("coin"))?; + + Ok(VoteRegistrationAndDelegation { + stake_credential, + drep, + coin, + }) + } +} diff --git a/rust/src/serialization/credential.rs b/rust/src/serialization/credential.rs new file mode 100644 index 00000000..ff2a886d --- /dev/null +++ b/rust/src/serialization/credential.rs @@ -0,0 +1,60 @@ +use std::io::{BufRead, Seek, Write}; +use cbor_event::de::Deserializer; +use cbor_event::se::Serializer; +use crate::{Credential, CredType, DeserializeError, DeserializeFailure, Ed25519KeyHash, Key, ScriptHash}; +use crate::protocol_types::{CBORSpecial, Deserialize}; + +impl cbor_event::se::Serialize for Credential { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + match &self.0 { + CredType::Key(keyhash) => { + serializer.write_unsigned_integer(0u64)?; + serializer.write_bytes(keyhash.to_bytes()) + } + CredType::Script(scripthash) => { + serializer.write_unsigned_integer(1u64)?; + serializer.write_bytes(scripthash.to_bytes()) + } + } + } +} + +impl Deserialize for Credential { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + if let cbor_event::Len::Len(n) = len { + if n != 2 { + return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( + 2, + len, + "[id, hash]", + )) + .into()); + } + } + let cred_type = match raw.unsigned_integer()? { + 0 => CredType::Key(Ed25519KeyHash::deserialize(raw)?), + 1 => CredType::Script(ScriptHash::deserialize(raw)?), + n => { + return Err(DeserializeFailure::FixedValuesMismatch { + found: Key::Uint(n), + expected: vec![Key::Uint(0), Key::Uint(1)], + } + .into()); + } + }; + if let cbor_event::Len::Indefinite = len { + if raw.special()? != CBORSpecial::Break { + return Err(DeserializeFailure::EndingBreakMissing.into()); + } + } + Ok(Credential(cred_type)) + })() + .map_err(|e| e.annotate("StakeCredential")) + } +} \ No newline at end of file diff --git a/rust/src/serialization/credentials.rs b/rust/src/serialization/credentials.rs new file mode 100644 index 00000000..a45086fb --- /dev/null +++ b/rust/src/serialization/credentials.rs @@ -0,0 +1,41 @@ +use crate::*; +use crate::serialization::utils::{is_break_tag, skip_set_tag}; + +impl cbor_event::se::Serialize for Credentials { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + //TODO: uncomment this line when we conway ero will come + //serializer.write_tag(258)?; + serializer.write_array(cbor_event::Len::Len(self.len() as u64))?; + for element in self.to_vec() { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Credentials { + fn deserialize(raw: &mut Deserializer) -> Result { + skip_set_tag(raw)?; + let mut creds = Credentials::new(); + let mut counter = 0u64; + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => counter < n, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "Credentials")? { + break; + } + creds.add_move(Credential::deserialize(raw)?); + counter += 1; + } + Ok(()) + })() + .map_err(|e| e.annotate("CredentialsSet"))?; + Ok(creds) + } +} \ No newline at end of file diff --git a/rust/src/serialization/crypto/kes_signature.rs b/rust/src/serialization/crypto/kes_signature.rs new file mode 100644 index 00000000..29537e58 --- /dev/null +++ b/rust/src/serialization/crypto/kes_signature.rs @@ -0,0 +1,33 @@ +use cbor_event::de::Deserializer; +use cbor_event::se::Serializer; +use crate::{DeserializeError, DeserializeFailure, KESSignature}; +use crate::protocol_types::Deserialize; + +impl cbor_event::se::Serialize for KESSignature { + fn serialize<'se, W: std::io::Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_bytes(&self.0) + } +} + +impl Deserialize for KESSignature { + fn deserialize( + raw: &mut Deserializer, + ) -> Result { + (|| -> Result { + let bytes = raw.bytes()?; + if bytes.len() != Self::BYTE_COUNT { + return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( + Self::BYTE_COUNT as u64, + cbor_event::Len::Len(bytes.len() as u64), + "hash length", + )) + .into()); + } + Ok(KESSignature(bytes)) + })() + .map_err(|e| e.annotate("KESSignature")) + } +} \ No newline at end of file diff --git a/rust/src/serialization/crypto/mod.rs b/rust/src/serialization/crypto/mod.rs new file mode 100644 index 00000000..cb68a186 --- /dev/null +++ b/rust/src/serialization/crypto/mod.rs @@ -0,0 +1,5 @@ +mod vkey; +mod vkeys; +mod kes_signature; +mod nonce; +mod vrf_cert; \ No newline at end of file diff --git a/rust/src/serialization/crypto/nonce.rs b/rust/src/serialization/crypto/nonce.rs new file mode 100644 index 00000000..22086a49 --- /dev/null +++ b/rust/src/serialization/crypto/nonce.rs @@ -0,0 +1,72 @@ +use std::convert::TryInto; +use crate::protocol_types::{CBORSpecial, Deserialize}; +use crate::{DeserializeError, DeserializeFailure, Nonce}; +use cbor_event::de::Deserializer; +use cbor_event::se::Serializer; + +impl cbor_event::se::Serialize for Nonce { + fn serialize<'se, W: std::io::Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + match &self.hash { + Some(hash) => { + serializer.write_array(cbor_event::Len::Len(2))?; + serializer.write_unsigned_integer(1)?; + serializer.write_bytes(hash) + } + None => { + serializer.write_array(cbor_event::Len::Len(1))?; + serializer.write_unsigned_integer(0) + } + } + } +} + +impl Deserialize for Nonce { + fn deserialize( + raw: &mut Deserializer, + ) -> Result { + (|| -> Result { + let len = raw.array()?; + let hash = match raw.unsigned_integer()? { + 0 => None, + 1 => { + let bytes = raw.bytes()?; + if bytes.len() != Self::HASH_LEN { + return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( + Self::HASH_LEN as u64, + cbor_event::Len::Len(bytes.len() as u64), + "hash length", + )) + .into()); + } + Some(bytes[..Self::HASH_LEN].try_into().unwrap()) + } + _ => return Err(DeserializeFailure::NoVariantMatched.into()), + }; + match len { + cbor_event::Len::Len(n) => { + let correct_len = match n { + 1 => hash.is_none(), + 2 => hash.is_some(), + _ => false, + }; + if !correct_len { + return Err(DeserializeFailure::NoVariantMatched.into()); + } + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + }; + Ok(Self { hash }) + })() + .map_err(|e| e.annotate(stringify!($name))) + } +} diff --git a/rust/src/serialization/crypto/vkey.rs b/rust/src/serialization/crypto/vkey.rs new file mode 100644 index 00000000..9801c36f --- /dev/null +++ b/rust/src/serialization/crypto/vkey.rs @@ -0,0 +1,22 @@ +use std::io::{BufRead, Seek, Write}; +use cbor_event::de::Deserializer; +use cbor_event::se::Serializer; +use crate::{DeserializeError, PublicKey, Vkey}; +use crate::protocol_types::Deserialize; + +impl cbor_event::se::Serialize for Vkey { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_bytes(&self.0.as_bytes()) + } +} + +impl Deserialize for Vkey { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self(PublicKey(crate::chain_crypto::PublicKey::from_binary( + raw.bytes()?.as_ref(), + )?))) + } +} \ No newline at end of file diff --git a/rust/src/serialization/crypto/vkeys.rs b/rust/src/serialization/crypto/vkeys.rs new file mode 100644 index 00000000..c88d789f --- /dev/null +++ b/rust/src/serialization/crypto/vkeys.rs @@ -0,0 +1,40 @@ +use std::io::{BufRead, Seek, Write}; +use cbor_event::de::Deserializer; +use cbor_event::se::Serializer; +use crate::{DeserializeError, Vkey, Vkeys}; +use crate::protocol_types::Deserialize; +use crate::serialization::utils::is_break_tag; + +impl cbor_event::se::Serialize for Vkeys { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Vkeys { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "Vkeys")? { + break; + } + arr.push(Vkey::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("Vkeys"))?; + Ok(Self(arr)) + } +} \ No newline at end of file diff --git a/rust/src/serialization/crypto/vrf_cert.rs b/rust/src/serialization/crypto/vrf_cert.rs new file mode 100644 index 00000000..85d0c85a --- /dev/null +++ b/rust/src/serialization/crypto/vrf_cert.rs @@ -0,0 +1,50 @@ +use crate::*; + +impl cbor_event::se::Serialize for VRFCert { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + serializer.write_bytes(&self.output)?; + serializer.write_bytes(&self.proof)?; + Ok(serializer) + } +} + +impl Deserialize for VRFCert { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let output = (|| -> Result<_, DeserializeError> { Ok(raw.bytes()?) })() + .map_err(|e| e.annotate("output"))?; + let proof = (|| -> Result<_, DeserializeError> { Ok(raw.bytes()?) })() + .map_err(|e| e.annotate("proof"))?; + if proof.len() != Self::PROOF_LEN { + return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( + Self::PROOF_LEN as u64, + cbor_event::Len::Len(proof.len() as u64), + "proof length", + )) + .into()); + } + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + Ok(VRFCert { output, proof }) + })() + .map_err(|e| e.annotate("VRFCert")) + } +} \ No newline at end of file diff --git a/rust/src/serialization/ed25519_key_hashes.rs b/rust/src/serialization/ed25519_key_hashes.rs new file mode 100644 index 00000000..46d6a73e --- /dev/null +++ b/rust/src/serialization/ed25519_key_hashes.rs @@ -0,0 +1,41 @@ +use crate::*; +use crate::serialization::utils::{is_break_tag, skip_set_tag}; + +impl Serialize for Ed25519KeyHashes { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + //TODO: uncomment this line when we conway ero will come + //serializer.write_tag(258)?; + serializer.write_array(cbor_event::Len::Len(self.len() as u64))?; + for element in self.to_vec() { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Ed25519KeyHashes { + fn deserialize(raw: &mut Deserializer) -> Result { + skip_set_tag(raw)?; + let mut creds = Ed25519KeyHashes::new(); + let mut total = 0u64; + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => total < n, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "Ed25519KeyHashes")? { + break; + } + creds.add_move(Ed25519KeyHash::deserialize(raw)?); + total += 1; + } + Ok(()) + })() + .map_err(|e| e.annotate("Ed25519KeyHashes"))?; + Ok(creds) + } +} \ No newline at end of file diff --git a/rust/src/serialization/fixed_tx.rs b/rust/src/serialization/fixed_tx.rs new file mode 100644 index 00000000..c4b5f58c --- /dev/null +++ b/rust/src/serialization/fixed_tx.rs @@ -0,0 +1,119 @@ +use crate::*; +use crate::serialization::utils::deserilized_with_orig_bytes; + +impl cbor_event::se::Serialize for FixedTransaction { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(4))?; + serializer.write_raw_bytes(&self.body_bytes)?; + serializer.write_raw_bytes(&self.witness_bytes)?; + serializer.write_special(CBORSpecial::Bool(self.is_valid))?; + match &self.auxiliary_bytes { + Some(auxiliary_bytes) => serializer.write_raw_bytes(auxiliary_bytes)?, + None => serializer.write_special(CBORSpecial::Null)?, + }; + Ok(serializer) + } +} + +impl Deserialize for FixedTransaction { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("Transaction")) + } +} + +impl DeserializeEmbeddedGroup for FixedTransaction { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + _: cbor_event::Len, + ) -> Result { + let (body, body_bytes) = + deserilized_with_orig_bytes(raw, |raw| TransactionBody::deserialize(raw)) + .map_err(|e| e.annotate("body"))?; + let (witness_set, witness_bytes) = + deserilized_with_orig_bytes(raw, |raw| TransactionWitnessSet::deserialize(raw)) + .map_err(|e| e.annotate("witness_set"))?; + let mut checked_auxiliary_data = false; + let mut auxiliary_data = None; + let mut auxiliary_bytes = None; + let is_valid = (|| -> Result<_, DeserializeError> { + match raw.cbor_type()? == CBORType::Special { + true => { + // if it's special it can be either a bool or null. if it's null, then it's empty auxiliary data, otherwise not a valid encoding + let special = raw.special()?; + if let CBORSpecial::Bool(b) = special { + return Ok(b); + } else if special == CBORSpecial::Null { + checked_auxiliary_data = true; + return Ok(true); + } else { + return Err(DeserializeFailure::ExpectedBool.into()); + } + } + false => { + let (auxiliary_data_deser, auxiliary_bytes_deser) = + deserilized_with_orig_bytes(raw, |raw| AuxiliaryData::deserialize(raw)) + .map_err(|e| e.annotate("auxiliary_data"))?; + auxiliary_data = Some(auxiliary_data_deser); + auxiliary_bytes = Some(auxiliary_bytes_deser); + // if no special symbol was detected, it must have auxiliary data + checked_auxiliary_data = true; + return Ok(true); + } + } + })() + .map_err(|e| e.annotate("is_valid"))?; + if !checked_auxiliary_data { + // this branch is reached, if the 3rd argument was a bool. then it simply follows the rules for checking auxiliary data + (auxiliary_data, auxiliary_bytes) = (|| -> Result<_, DeserializeError> { + Ok(match raw.cbor_type()? != CBORType::Special { + true => { + let (auxiliary_data_deser, auxiliary_bytes_deser) = + deserilized_with_orig_bytes(raw, |raw| { + AuxiliaryData::deserialize(raw) + })?; + (Some(auxiliary_data_deser), Some(auxiliary_bytes_deser)) + } + false => { + if raw.special()? != CBORSpecial::Null { + return Err(DeserializeFailure::ExpectedNull.into()); + } + (None, None) + } + }) + })() + .map_err(|e| e.annotate("auxiliary_data"))?; + } + Ok(FixedTransaction { + body, + body_bytes, + witness_set, + witness_bytes, + is_valid, + auxiliary_data, + auxiliary_bytes, + }) + } +} diff --git a/rust/src/serialization/general.rs b/rust/src/serialization/general.rs new file mode 100644 index 00000000..03d4c70a --- /dev/null +++ b/rust/src/serialization/general.rs @@ -0,0 +1,1583 @@ +use crate::*; +use std::io::{Seek, SeekFrom}; +use crate::serialization::utils::is_break_tag; +use hashlink::LinkedHashMap; + +// This file was code-generated using an experimental CDDL to rust tool: +// https://github.com/Emurgo/cddl-codegen + +impl cbor_event::se::Serialize for UnitInterval { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_tag(30u64)?; + serializer.write_array(cbor_event::Len::Len(2))?; + self.numerator.serialize(serializer)?; + self.denominator.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for UnitInterval { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let tag = raw.tag()?; + if tag != 30 { + return Err(DeserializeError::new( + "UnitInterval", + DeserializeFailure::TagMismatch { + found: tag, + expected: 30, + }, + )); + } + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("UnitInterval")) + } +} + +impl DeserializeEmbeddedGroup for UnitInterval { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + _: cbor_event::Len, + ) -> Result { + let numerator = (|| -> Result<_, DeserializeError> { Ok(BigNum::deserialize(raw)?) })() + .map_err(|e| e.annotate("numerator"))?; + let denominator = (|| -> Result<_, DeserializeError> { Ok(BigNum::deserialize(raw)?) })() + .map_err(|e| e.annotate("denominator"))?; + Ok(UnitInterval { + numerator, + denominator, + }) + } +} + +impl cbor_event::se::Serialize for Transaction { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(4))?; + self.body.serialize(serializer)?; + self.witness_set.serialize(serializer)?; + serializer.write_special(CBORSpecial::Bool(self.is_valid))?; + match &self.auxiliary_data { + Some(x) => x.serialize(serializer), + None => serializer.write_special(CBORSpecial::Null), + }?; + Ok(serializer) + } +} + +impl Deserialize for Transaction { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("Transaction")) + } +} + +impl DeserializeEmbeddedGroup for Transaction { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + _: cbor_event::Len, + ) -> Result { + let body = (|| -> Result<_, DeserializeError> { Ok(TransactionBody::deserialize(raw)?) })() + .map_err(|e| e.annotate("body"))?; + let witness_set = + (|| -> Result<_, DeserializeError> { Ok(TransactionWitnessSet::deserialize(raw)?) })() + .map_err(|e| e.annotate("witness_set"))?; + let mut checked_auxiliary_data = false; + let mut auxiliary_data = None; + let is_valid = (|| -> Result<_, DeserializeError> { + match raw.cbor_type()? == CBORType::Special { + true => { + // if it's special it can be either a bool or null. if it's null, then it's empty auxiliary data, otherwise not a valid encoding + let special = raw.special()?; + if let CBORSpecial::Bool(b) = special { + return Ok(b); + } else if special == CBORSpecial::Null { + checked_auxiliary_data = true; + return Ok(true); + } else { + return Err(DeserializeFailure::ExpectedBool.into()); + } + } + false => { + // if no special symbol was detected, it must have auxiliary data + auxiliary_data = (|| -> Result<_, DeserializeError> { + Ok(Some(AuxiliaryData::deserialize(raw)?)) + })() + .map_err(|e| e.annotate("auxiliary_data"))?; + checked_auxiliary_data = true; + return Ok(true); + } + } + })() + .map_err(|e| e.annotate("is_valid"))?; + if !checked_auxiliary_data { + // this branch is reached, if the 3rd argument was a bool. then it simply follows the rules for checking auxiliary data + auxiliary_data = (|| -> Result<_, DeserializeError> { + Ok(match raw.cbor_type()? != CBORType::Special { + true => Some(AuxiliaryData::deserialize(raw)?), + false => { + if raw.special()? != CBORSpecial::Null { + return Err(DeserializeFailure::ExpectedNull.into()); + } + None + } + }) + })() + .map_err(|e| e.annotate("auxiliary_data"))?; + } + Ok(Transaction { + body, + witness_set, + is_valid, + auxiliary_data, + }) + } +} + +impl cbor_event::se::Serialize for TransactionOutputs { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for TransactionOutputs { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "TransactionOutputs")? { + break; + } + arr.push(TransactionOutput::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("TransactionOutputs"))?; + Ok(Self(arr)) + } +} + +impl cbor_event::se::Serialize for TransactionOutput { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + if self.has_plutus_data() || self.has_script_ref() { + //post alonzo output + let map_len = 2 + opt64(&self.plutus_data) + opt64(&self.script_ref); + serializer.write_map(cbor_event::Len::Len(map_len))?; + serializer.write_unsigned_integer(0)?; + self.address.serialize(serializer)?; + serializer.write_unsigned_integer(1)?; + self.amount.serialize(serializer)?; + if let Some(field) = &self.plutus_data { + serializer.write_unsigned_integer(2)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.script_ref { + serializer.write_unsigned_integer(3)?; + field.serialize(serializer)?; + } + } else { + //lagacy output + let data_hash = &self.data_hash(); + serializer.write_array(cbor_event::Len::Len(2 + opt64(&data_hash)))?; + self.address.serialize(serializer)?; + self.amount.serialize(serializer)?; + if let Some(pure_data_hash) = data_hash { + pure_data_hash.serialize(serializer)?; + } + } + Ok(serializer) + } +} + +// this is used when deserializing it on its own, but the more likely case +// is when it's done via TransactionOutputs +impl Deserialize for TransactionOutput { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + match raw.cbor_type()? { + CBORType::Array => { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + } + CBORType::Map => deserialize_as_postalonzo_output(raw), + cbor_type => Err(DeserializeFailure::UnexpectedKeyType(cbor_type).into()), + } + })() + .map_err(|e| e.annotate("TransactionOutput")) + } +} + +// this is used by both TransactionOutput (on its own)'s deserialize +// but also for TransactionOutputs +// This implementation was hand-coded since cddl-codegen doesn't support deserialization +// with array-encoded types with optional fields, due to the complexity. +// This is made worse as this is a plain group... +impl DeserializeEmbeddedGroup for TransactionOutput { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + _: cbor_event::Len, + ) -> Result { + let address = (|| -> Result<_, DeserializeError> { Ok(Address::deserialize(raw)?) })() + .map_err(|e| e.annotate("address"))?; + let amount = (|| -> Result<_, DeserializeError> { Ok(Value::deserialize(raw)?) })() + .map_err(|e| e.annotate("amount"))?; + // there are only two cases so far where this is used: + // 1) on its own inside of TransactionOutput's Deserialize trait (only used if someone calls to_bytes() on it) + // 2) from TransactionOutput's deserialization + // in 1) we would encounter an array-end (or track it for definite deserialization - which we don't do right now) + // and in 2) we would encounter the same OR we would encounter the next TransactionOutput in the array + // Unfortunately, both address and data hash are bytes type, so we can't just check the type, but instead + // must check the length, and backtrack if that wasn't the case. + let data_hash = match raw.cbor_type() { + Ok(cbor_event::Type::Bytes) => { + let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + let bytes = raw.bytes().unwrap(); + if bytes.len() == DataHash::BYTE_COUNT { + Some(DataOption::DataHash(DataHash(bytes[..DataHash::BYTE_COUNT].try_into().unwrap()))) + } else { + // This is an address of the next output in sequence, which luckily is > 32 bytes so there's no confusion + // Go to previous place in array then carry on + raw.as_mut_ref().seek(SeekFrom::Start(initial_position)).unwrap(); + None + } + }, + // not possibly a data hash + Ok(_) | + // end of input + Err(_) => None, + }; + Ok(TransactionOutput { + address, + amount, + plutus_data: data_hash, + script_ref: None, + serialization_format: Some(CborContainerType::Array), + }) + } +} + +fn deserialize_as_postalonzo_output( + raw: &mut Deserializer, +) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + let mut read_len = CBORReadLen::new(len); + let mut address = None; + let mut amount = None; + let mut data = None; + let mut script_ref = None; + let mut read = 0; + while match len { + cbor_event::Len::Len(n) => read < n as usize, + cbor_event::Len::Indefinite => true, + } { + match raw.cbor_type()? { + CBORType::UnsignedInteger => match raw.unsigned_integer()? { + 0 => { + if address.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); + } + address = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Address::deserialize(raw)?) + })() + .map_err(|e| e.annotate("address"))?, + ); + } + 1 => { + if amount.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); + } + amount = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Value::deserialize(raw)?) + })() + .map_err(|e| e.annotate("amount"))?, + ); + } + 2 => { + if data.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); + } + data = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(DataOption::deserialize(raw)?) + })() + .map_err(|e| e.annotate("data"))?, + ); + } + 3 => { + if script_ref.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); + } + script_ref = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ScriptRef::deserialize(raw)?) + })() + .map_err(|e| e.annotate("script_ref"))?, + ); + } + unknown_key => { + return Err(DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into()) + } + }, + other_type => return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()), + } + read += 1; + } + let address = match address { + Some(x) => x, + None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Uint(0)).into()), + }; + let amount = match amount { + Some(x) => x, + None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Uint(1)).into()), + }; + + read_len.finish()?; + Ok(TransactionOutput { + address, + amount, + plutus_data: data, + script_ref, + serialization_format: Some(CborContainerType::Map), + }) + })() + .map_err(|e| e.annotate("TransactionOutput")) +} + +impl Deserialize for DataOption { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + if let cbor_event::Len::Len(n) = len { + if n != 2 { + return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( + 2, + len, + "[id, datum_or_hash]", + )) + .into()); + } + } + let datum = match raw.unsigned_integer()? { + 0 => DataOption::DataHash(DataHash::deserialize(raw)?), + 1 => { + match raw.tag()? { + //bytes string tag + 24 => { + let data = (|| -> Result<_, DeserializeError> { + Ok(from_bytes(&raw.bytes()?)?) + })() + .map_err(|e| e.annotate("PlutusData"))?; + DataOption::Data(data) + } + tag => { + return Err(DeserializeFailure::TagMismatch { + found: tag, + expected: 24, + } + .into()); + } + } + } + n => { + return Err(DeserializeFailure::FixedValueMismatch { + found: Key::Uint(n), + expected: Key::Uint(0), + } + .into()) + } + }; + if let cbor_event::Len::Indefinite = len { + if raw.special()? != CBORSpecial::Break { + return Err(DeserializeFailure::EndingBreakMissing.into()); + } + } + Ok(datum) + })() + .map_err(|e| e.annotate("DataOption")) + } +} + +impl cbor_event::se::Serialize for DataOption { + fn serialize<'a, W: Write + Sized>( + &self, + serializer: &'a mut Serializer, + ) -> cbor_event::Result<&'a mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + match &self { + DataOption::DataHash(data_hash) => { + serializer.write_unsigned_integer(0)?; + data_hash.serialize(serializer)?; + } + DataOption::Data(data) => { + serializer.write_unsigned_integer(1)?; + let bytes = data.to_bytes(); + serializer.write_tag(24)?.write_bytes(&bytes)?; + } + } + Ok(serializer) + } +} + +impl cbor_event::se::Serialize for Ipv4 { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_bytes(&self.0) + } +} + +impl Deserialize for Ipv4 { + fn deserialize(raw: &mut Deserializer) -> Result { + Self::new_impl(raw.bytes()?) + } +} + +impl cbor_event::se::Serialize for Ipv6 { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_bytes(&self.0) + } +} + +impl Deserialize for Ipv6 { + fn deserialize(raw: &mut Deserializer) -> Result { + Self::new_impl(raw.bytes()?) + } +} + +impl cbor_event::se::Serialize for DNSRecordAorAAAA { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_text(&self.0) + } +} + +impl Deserialize for DNSRecordAorAAAA { + fn deserialize(raw: &mut Deserializer) -> Result { + Self::new_impl(raw.text()?) + } +} + +impl cbor_event::se::Serialize for DNSRecordSRV { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_text(&self.0) + } +} + +impl Deserialize for DNSRecordSRV { + fn deserialize(raw: &mut Deserializer) -> Result { + Self::new_impl(raw.text()?) + } +} + +impl cbor_event::se::Serialize for URL { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_text(&self.0) + } +} + +impl Deserialize for URL { + fn deserialize(raw: &mut Deserializer) -> Result { + Self::new_impl(raw.text()?) + } +} + +impl cbor_event::se::Serialize for SingleHostAddr { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(4))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for SingleHostAddr { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_unsigned_integer(0u64)?; + match &self.port { + Some(x) => x.serialize(serializer), + None => serializer.write_special(CBORSpecial::Null), + }?; + match &self.ipv4 { + Some(x) => x.serialize(serializer), + None => serializer.write_special(CBORSpecial::Null), + }?; + match &self.ipv6 { + Some(x) => x.serialize(serializer), + None => serializer.write_special(CBORSpecial::Null), + }?; + Ok(serializer) + } +} + +impl Deserialize for SingleHostAddr { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("SingleHostAddr")) + } +} + +impl DeserializeEmbeddedGroup for SingleHostAddr { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + _: cbor_event::Len, + ) -> Result { + (|| -> Result<_, DeserializeError> { + let index_0_value = raw.unsigned_integer()?; + if index_0_value != 0 { + return Err(DeserializeFailure::FixedValueMismatch { + found: Key::Uint(index_0_value), + expected: Key::Uint(0), + } + .into()); + } + Ok(()) + })() + .map_err(|e| e.annotate("index_0"))?; + let port = (|| -> Result<_, DeserializeError> { + Ok(match raw.cbor_type()? != CBORType::Special { + true => Some(Port::deserialize(raw)?), + false => { + if raw.special()? != CBORSpecial::Null { + return Err(DeserializeFailure::ExpectedNull.into()); + } + None + } + }) + })() + .map_err(|e| e.annotate("port"))?; + let ipv4 = (|| -> Result<_, DeserializeError> { + Ok(match raw.cbor_type()? != CBORType::Special { + true => Some(Ipv4::deserialize(raw)?), + false => { + if raw.special()? != CBORSpecial::Null { + return Err(DeserializeFailure::ExpectedNull.into()); + } + None + } + }) + })() + .map_err(|e| e.annotate("ipv4"))?; + let ipv6 = (|| -> Result<_, DeserializeError> { + Ok(match raw.cbor_type()? != CBORType::Special { + true => Some(Ipv6::deserialize(raw)?), + false => { + if raw.special()? != CBORSpecial::Null { + return Err(DeserializeFailure::ExpectedNull.into()); + } + None + } + }) + })() + .map_err(|e| e.annotate("ipv6"))?; + Ok(SingleHostAddr { port, ipv4, ipv6 }) + } +} + +impl cbor_event::se::Serialize for SingleHostName { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(3))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for SingleHostName { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_unsigned_integer(1u64)?; + match &self.port { + Some(x) => x.serialize(serializer), + None => serializer.write_special(CBORSpecial::Null), + }?; + self.dns_name.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for SingleHostName { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("SingleHostName")) + } +} + +impl DeserializeEmbeddedGroup for SingleHostName { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + _: cbor_event::Len, + ) -> Result { + (|| -> Result<_, DeserializeError> { + let index_0_value = raw.unsigned_integer()?; + if index_0_value != 1 { + return Err(DeserializeFailure::FixedValueMismatch { + found: Key::Uint(index_0_value), + expected: Key::Uint(1), + } + .into()); + } + Ok(()) + })() + .map_err(|e| e.annotate("index_0"))?; + let port = (|| -> Result<_, DeserializeError> { + Ok(match raw.cbor_type()? != CBORType::Special { + true => Some(Port::deserialize(raw)?), + false => { + if raw.special()? != CBORSpecial::Null { + return Err(DeserializeFailure::ExpectedNull.into()); + } + None + } + }) + })() + .map_err(|e| e.annotate("port"))?; + let dns_name = + (|| -> Result<_, DeserializeError> { Ok(DNSRecordAorAAAA::deserialize(raw)?) })() + .map_err(|e| e.annotate("dns_name"))?; + Ok(SingleHostName { port, dns_name }) + } +} + +impl cbor_event::se::Serialize for MultiHostName { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for MultiHostName { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_unsigned_integer(2u64)?; + self.dns_name.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for MultiHostName { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("MultiHostName")) + } +} + +impl DeserializeEmbeddedGroup for MultiHostName { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + _: cbor_event::Len, + ) -> Result { + (|| -> Result<_, DeserializeError> { + let index_0_value = raw.unsigned_integer()?; + if index_0_value != 2 { + return Err(DeserializeFailure::FixedValueMismatch { + found: Key::Uint(index_0_value), + expected: Key::Uint(2), + } + .into()); + } + Ok(()) + })() + .map_err(|e| e.annotate("index_0"))?; + let dns_name = + (|| -> Result<_, DeserializeError> { Ok(DNSRecordSRV::deserialize(raw)?) })() + .map_err(|e| e.annotate("dns_name"))?; + Ok(MultiHostName { dns_name }) + } +} + +impl cbor_event::se::Serialize for RelayEnum { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + match self { + RelayEnum::SingleHostAddr(x) => x.serialize(serializer), + RelayEnum::SingleHostName(x) => x.serialize(serializer), + RelayEnum::MultiHostName(x) => x.serialize(serializer), + } + } +} + +impl Deserialize for RelayEnum { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("RelayEnum")) + } +} + +impl DeserializeEmbeddedGroup for RelayEnum { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(SingleHostAddr::deserialize_as_embedded_group(raw, len)?) + })(raw) + { + Ok(variant) => return Ok(RelayEnum::SingleHostAddr(variant)), + Err(_) => raw + .as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(SingleHostName::deserialize_as_embedded_group(raw, len)?) + })(raw) + { + Ok(variant) => return Ok(RelayEnum::SingleHostName(variant)), + Err(_) => raw + .as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(MultiHostName::deserialize_as_embedded_group(raw, len)?) + })(raw) + { + Ok(variant) => return Ok(RelayEnum::MultiHostName(variant)), + Err(_) => raw + .as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .unwrap(), + }; + Err(DeserializeError::new( + "RelayEnum", + DeserializeFailure::NoVariantMatched.into(), + )) + } +} + +impl cbor_event::se::Serialize for Relay { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + self.0.serialize(serializer) + } +} + +impl Deserialize for Relay { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self(RelayEnum::deserialize(raw)?)) + } +} + +impl cbor_event::se::Serialize for PoolMetadata { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.url.serialize(serializer)?; + self.pool_metadata_hash.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for PoolMetadata { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("PoolMetadata")) + } +} + +impl DeserializeEmbeddedGroup for PoolMetadata { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + _: cbor_event::Len, + ) -> Result { + let url = (|| -> Result<_, DeserializeError> { Ok(URL::deserialize(raw)?) })() + .map_err(|e| e.annotate("url"))?; + let pool_metadata_hash = + (|| -> Result<_, DeserializeError> { Ok(PoolMetadataHash::deserialize(raw)?) })() + .map_err(|e| e.annotate("pool_metadata_hash"))?; + Ok(PoolMetadata { + url, + pool_metadata_hash, + }) + } +} + +impl cbor_event::se::Serialize for RewardAddresses { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for RewardAddresses { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "RewardAddresses")? { + break; + } + arr.push(RewardAddress::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("RewardAddresses"))?; + Ok(Self(arr)) + } +} + +impl cbor_event::se::Serialize for Withdrawals { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Withdrawals { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = LinkedHashMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { + cbor_event::Len::Len(n) => table.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "Withdrawals")? { + break; + } + let key = RewardAddress::deserialize(raw)?; + let value = Coin::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( + "some complicated/unsupported type", + ))) + .into()); + } + } + Ok(()) + })() + .map_err(|e| e.annotate("Withdrawals"))?; + Ok(Self(table)) + } +} + +impl cbor_event::se::Serialize for Update { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.proposed_protocol_parameter_updates + .serialize(serializer)?; + self.epoch.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for Update { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("Update")) + } +} + +impl DeserializeEmbeddedGroup for Update { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + _: cbor_event::Len, + ) -> Result { + let proposed_protocol_parameter_updates = (|| -> Result<_, DeserializeError> { + Ok(ProposedProtocolParameterUpdates::deserialize(raw)?) + })() + .map_err(|e| e.annotate("proposed_protocol_parameter_updates"))?; + let epoch = (|| -> Result<_, DeserializeError> { Ok(Epoch::deserialize(raw)?) })() + .map_err(|e| e.annotate("epoch"))?; + Ok(Update { + proposed_protocol_parameter_updates, + epoch, + }) + } +} + +impl cbor_event::se::Serialize for GenesisHashes { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for GenesisHashes { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "GenesisHashes")? { + break; + } + arr.push(GenesisHash::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("Genesishashes"))?; + Ok(Self(arr)) + } +} + +impl cbor_event::se::Serialize for ScriptHashes { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for ScriptHashes { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "ScriptHashes")? { + break; + } + arr.push(ScriptHash::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("ScriptHashes"))?; + Ok(Self(arr)) + } +} + +impl cbor_event::se::Serialize for ProposedProtocolParameterUpdates { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for ProposedProtocolParameterUpdates { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = LinkedHashMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { + cbor_event::Len::Len(n) => table.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "ProposedProtocolParameterUpdates")? { + break; + } + let key = GenesisHash::deserialize(raw)?; + let value = ProtocolParamUpdate::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( + "some complicated/unsupported type", + ))) + .into()); + } + } + Ok(()) + })() + .map_err(|e| e.annotate("ProposedProtocolParameterUpdates"))?; + Ok(Self(table)) + } +} + +impl cbor_event::se::Serialize for ProtocolVersion { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for ProtocolVersion { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + self.major.serialize(serializer)?; + self.minor.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for ProtocolVersion { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("ProtocolVersion")) + } +} + +impl DeserializeEmbeddedGroup for ProtocolVersion { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + _: cbor_event::Len, + ) -> Result { + let major = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() + .map_err(|e| e.annotate("major"))?; + let minor = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() + .map_err(|e| e.annotate("minor"))?; + Ok(ProtocolVersion { major, minor }) + } +} + +impl cbor_event::se::Serialize for AuxiliaryDataSet { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for AuxiliaryDataSet { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = LinkedHashMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { + cbor_event::Len::Len(n) => table.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "AuxiliaryDataSet")? { + break; + } + let key = TransactionIndex::deserialize(raw)?; + let value = AuxiliaryData::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( + "some complicated/unsupported type", + ))) + .into()); + } + } + Ok(()) + })() + .map_err(|e| e.annotate("AuxiliaryDataSet"))?; + Ok(Self(table)) + } +} + +impl cbor_event::se::Serialize for AssetName { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_bytes(&self.0) + } +} + +impl Deserialize for AssetName { + fn deserialize(raw: &mut Deserializer) -> Result { + Self::new_impl(raw.bytes()?) + } +} + +impl cbor_event::se::Serialize for AssetNames { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for AssetNames { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "AssetNames")? { + break; + } + arr.push(AssetName::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("AssetNames"))?; + Ok(Self(arr)) + } +} + +impl cbor_event::se::Serialize for Assets { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Assets { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = std::collections::BTreeMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { + cbor_event::Len::Len(n) => table.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "Assets")? { + break; + } + let key = AssetName::deserialize(raw)?; + let value = BigNum::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( + "some complicated/unsupported type", + ))) + .into()); + } + } + Ok(()) + })() + .map_err(|e| e.annotate("Assets"))?; + Ok(Self(table)) + } +} + +impl cbor_event::se::Serialize for MultiAsset { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for MultiAsset { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = std::collections::BTreeMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { + cbor_event::Len::Len(n) => table.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "MultiAsset")? { + break; + } + let key = PolicyID::deserialize(raw)?; + let value = Assets::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( + "some complicated/unsupported type", + ))) + .into()); + } + } + Ok(()) + })() + .map_err(|e| e.annotate("MultiAsset"))?; + Ok(Self(table)) + } +} + +impl cbor_event::se::Serialize for MintAssets { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for MintAssets { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = std::collections::BTreeMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { + cbor_event::Len::Len(n) => table.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "MintAssets")? { + break; + } + let key = AssetName::deserialize(raw)?; + let value = Int::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( + "some complicated/unsupported type", + ))) + .into()); + } + } + Ok(()) + })() + .map_err(|e| e.annotate("MintAssets"))?; + Ok(Self(table)) + } +} + +impl cbor_event::se::Serialize for Mint { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Mint { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut mints = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { + cbor_event::Len::Len(n) => mints.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "Mint")? { + break; + } + let key = PolicyID::deserialize(raw)?; + let value = MintAssets::deserialize(raw)?; + mints.push((key.clone(), value)); + } + Ok(()) + })() + .map_err(|e| e.annotate("Mint"))?; + Ok(Self(mints)) + } +} + +impl cbor_event::se::Serialize for NetworkId { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + match self.0 { + NetworkIdKind::Testnet => serializer.write_unsigned_integer(0u64), + NetworkIdKind::Mainnet => serializer.write_unsigned_integer(1u64), + } + } +} + +impl Deserialize for NetworkId { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + match raw.unsigned_integer()? { + 0 => Ok(NetworkId::testnet()), + 1 => Ok(NetworkId::mainnet()), + _ => Err(DeserializeError::new( + "NetworkId", + DeserializeFailure::NoVariantMatched.into(), + )), + } + })() + .map_err(|e| e.annotate("NetworkId")) + } +} \ No newline at end of file diff --git a/rust/src/serialization/governance/anchor.rs b/rust/src/serialization/governance/anchor.rs new file mode 100644 index 00000000..1949db84 --- /dev/null +++ b/rust/src/serialization/governance/anchor.rs @@ -0,0 +1,41 @@ +use crate::serialization::utils::check_len; +use crate::*; + +impl cbor_event::se::Serialize for Anchor { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.anchor_url.serialize(serializer)?; + self.anchor_data_hash.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for Anchor { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + + check_len(len, 2, "(anchor_url, anchor_data_hash)")?; + + let anchor_url = URL::deserialize(raw).map_err(|e| e.annotate("anchor_url"))?; + + let anchor_data_hash = + AnchorDataHash::deserialize(raw).map_err(|e| e.annotate("anchor_data_hash"))?; + + if let cbor_event::Len::Indefinite = len { + if raw.special()? != CBORSpecial::Break { + return Err(DeserializeFailure::EndingBreakMissing.into()); + } + } + + return Ok(Anchor { + anchor_url, + anchor_data_hash, + }); + })() + .map_err(|e| e.annotate("Anchor")) + } +} diff --git a/rust/src/serialization/governance/drep.rs b/rust/src/serialization/governance/drep.rs new file mode 100644 index 00000000..abd4c9c5 --- /dev/null +++ b/rust/src/serialization/governance/drep.rs @@ -0,0 +1,95 @@ +use crate::*; + +impl cbor_event::se::Serialize for DRep { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + self.0.serialize(serializer) + } +} + +impl Deserialize for DRep { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let drep_enum = DRepEnum::deserialize(raw)?; + Ok(Self(drep_enum)) + })() + .map_err(|e| e.annotate("DRep")) + } +} + +impl cbor_event::se::Serialize for DRepEnum { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + match &self { + DRepEnum::KeyHash(keyhash) => { + serializer.write_array(cbor_event::Len::Len(2))?; + serializer.write_unsigned_integer(0u64)?; + serializer.write_bytes(keyhash.to_bytes()) + } + DRepEnum::ScriptHash(scripthash) => { + serializer.write_array(cbor_event::Len::Len(2))?; + serializer.write_unsigned_integer(1u64)?; + serializer.write_bytes(scripthash.to_bytes()) + } + DRepEnum::AlwaysAbstain => { + serializer.write_array(cbor_event::Len::Len(1))?; + serializer.write_unsigned_integer(2u64) + } + DRepEnum::AlwaysNoConfidence => { + serializer.write_array(cbor_event::Len::Len(1))?; + serializer.write_unsigned_integer(3u64) + } + } + } +} + +impl Deserialize for DRepEnum { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + if let cbor_event::Len::Len(n) = len { + if n != 2 && n != 1 { + return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( + 2, + len, + "[id, hash] or [id] (for abstain and no confidence)", + )) + .into()); + } + } + + let drep = match raw.unsigned_integer()? { + 0 => { + let key_hash = + Ed25519KeyHash::deserialize(raw).map_err(|e| e.annotate("key_hash"))?; + DRepEnum::KeyHash(key_hash) + } + 1 => { + let script_hash = + ScriptHash::deserialize(raw).map_err(|e| e.annotate("script_hash"))?; + DRepEnum::ScriptHash(script_hash) + } + 2 => DRepEnum::AlwaysAbstain, + 3 => DRepEnum::AlwaysNoConfidence, + n => { + return Err(DeserializeFailure::FixedValuesMismatch { + found: Key::Uint(n), + expected: vec![Key::Uint(0), Key::Uint(1), Key::Uint(2), Key::Uint(3)], + } + .into()) + } + }; + if let cbor_event::Len::Indefinite = len { + if raw.special()? != CBORSpecial::Break { + return Err(DeserializeFailure::EndingBreakMissing.into()); + } + } + Ok(drep) + })() + .map_err(|e| e.annotate("DRepEnum")) + } +} diff --git a/rust/src/serialization/governance/governance_action_id.rs b/rust/src/serialization/governance/governance_action_id.rs new file mode 100644 index 00000000..9ba76a67 --- /dev/null +++ b/rust/src/serialization/governance/governance_action_id.rs @@ -0,0 +1,48 @@ +use crate::*; + +impl cbor_event::se::Serialize for GovernanceActionId { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.transaction_id.serialize(serializer)?; + self.index.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for GovernanceActionId { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + if let cbor_event::Len::Len(n) = len { + if n != 2 { + return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( + 2, + len, + "[transaction_id, gov_action_index]", + )) + .into()); + } + } + + let transaction_id = + TransactionHash::deserialize(raw).map_err(|e| e.annotate("transaction_id"))?; + + let index = GovernanceActionIndex::deserialize(raw).map_err(|e| e.annotate("index"))?; + + if let cbor_event::Len::Indefinite = len { + if raw.special()? != CBORSpecial::Break { + return Err(DeserializeFailure::EndingBreakMissing.into()); + } + } + + Ok(Self { + transaction_id, + index, + }) + })() + .map_err(|e| e.annotate("GovernanceActionId")) + } +} diff --git a/rust/src/serialization/governance/mod.rs b/rust/src/serialization/governance/mod.rs new file mode 100644 index 00000000..670ab6af --- /dev/null +++ b/rust/src/serialization/governance/mod.rs @@ -0,0 +1,7 @@ +mod drep; +mod anchor; +mod voter; +mod voting_procedure; +mod voting_procedures; +mod governance_action_id; +mod proposals; \ No newline at end of file diff --git a/rust/src/serialization/governance/proposals/committee.rs b/rust/src/serialization/governance/proposals/committee.rs new file mode 100644 index 00000000..7b0f73d4 --- /dev/null +++ b/rust/src/serialization/governance/proposals/committee.rs @@ -0,0 +1,62 @@ +use crate::serialization::utils::{check_len, is_break_tag}; +use crate::*; +use std::collections::BTreeMap; + +impl Serialize for Committee { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.serialize_as_embedded_group(serializer)?; + Ok(serializer) + } +} + +impl SerializeEmbeddedGroup for Committee { + fn serialize_as_embedded_group<'a, W: Write + Sized>(&self, serializer: &'a mut Serializer) -> cbor_event::Result<&'a mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.members.len() as u64))?; + for (key, value) in &self.members { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + self.quorum_threshold.serialize(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(Committee); + +impl DeserializeEmbeddedGroup for Committee { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len(len, 2, "(members, quorum_threshold)")?; + + let mut table = BTreeMap::new(); + let map_len = raw.map()?; + while match map_len { + cbor_event::Len::Len(n) => table.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "Committee")? { + break; + } + let key = Credential::deserialize(raw)?; + let value = Epoch::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( + "some complicated/unsupported type", + ))) + .into()); + } + } + let quorum_threshold = UnitInterval::deserialize(raw)?; + + Ok(Committee { + quorum_threshold, + members: table, + }) + } +} diff --git a/rust/src/serialization/governance/proposals/constitution.rs b/rust/src/serialization/governance/proposals/constitution.rs new file mode 100644 index 00000000..c7dc7a27 --- /dev/null +++ b/rust/src/serialization/governance/proposals/constitution.rs @@ -0,0 +1,32 @@ +use crate::serialization::utils::check_len; +use crate::*; + +impl Serialize for Constitution { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.anchor.serialize(serializer)?; + self.script_hash.serialize_nullable(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(Constitution); + +impl DeserializeEmbeddedGroup for Constitution { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len(len, 2, "(anchor, scripthash / null)")?; + let anchor = Anchor::deserialize(raw)?; + let script_hash = ScriptHash::deserialize_nullable(raw)?; + + Ok(Constitution { + anchor, + script_hash, + }) + } +} diff --git a/rust/src/serialization/governance/proposals/governance_action.rs b/rust/src/serialization/governance/proposals/governance_action.rs new file mode 100644 index 00000000..648bcc6f --- /dev/null +++ b/rust/src/serialization/governance/proposals/governance_action.rs @@ -0,0 +1,102 @@ +use crate::serialization::map_names::VotingProposalIndexNames; +use crate::serialization::utils::check_len_indefinite; +use crate::*; +use num_traits::FromPrimitive; +use std::io::{Seek, SeekFrom}; + +impl Serialize for GovernanceAction { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + match &self.0 { + GovernanceActionEnum::ParameterChangeAction(x) => x.serialize(serializer), + GovernanceActionEnum::HardForkInitiationAction(x) => x.serialize(serializer), + GovernanceActionEnum::TreasuryWithdrawalsAction(x) => x.serialize(serializer), + GovernanceActionEnum::NoConfidenceAction(x) => x.serialize(serializer), + GovernanceActionEnum::UpdateCommitteeAction(x) => x.serialize(serializer), + GovernanceActionEnum::NewConstitutionAction(x) => x.serialize(serializer), + GovernanceActionEnum::InfoAction(x) => x.serialize(serializer), + } + } +} + +impl Deserialize for GovernanceAction { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len)?; + check_len_indefinite(raw, len)?; + Ok(ret) + })() + .map_err(|e| e.annotate("VotingProposal")) + } +} + +impl DeserializeEmbeddedGroup for GovernanceAction { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + let cert_index = get_proposal_index(raw)?; + let index_enum = + VotingProposalIndexNames::from_u64(cert_index).ok_or(DeserializeError::new( + "VotingProposal", + DeserializeFailure::UnknownKey(Key::Uint(cert_index)), + ))?; + + let proposal_enum = match index_enum { + VotingProposalIndexNames::ParameterChangeAction => { + Ok::( + GovernanceActionEnum::ParameterChangeAction( + ParameterChangeAction::deserialize_as_embedded_group(raw, len)?, + ), + ) + } + VotingProposalIndexNames::HardForkInitiationAction => { + Ok(GovernanceActionEnum::HardForkInitiationAction( + HardForkInitiationAction::deserialize_as_embedded_group(raw, len)?, + )) + } + VotingProposalIndexNames::TreasuryWithdrawalsAction => { + Ok(GovernanceActionEnum::TreasuryWithdrawalsAction( + TreasuryWithdrawalsAction::deserialize_as_embedded_group(raw, len)?, + )) + } + VotingProposalIndexNames::NoConfidenceAction => { + Ok(GovernanceActionEnum::NoConfidenceAction( + NoConfidenceAction::deserialize_as_embedded_group(raw, len)?, + )) + } + VotingProposalIndexNames::UpdateCommitteeAction => { + Ok(GovernanceActionEnum::UpdateCommitteeAction( + UpdateCommitteeAction::deserialize_as_embedded_group(raw, len)?, + )) + } + VotingProposalIndexNames::NewConstitutionAction => { + Ok(GovernanceActionEnum::NewConstitutionAction( + NewConstitutionAction::deserialize_as_embedded_group(raw, len)?, + )) + } + VotingProposalIndexNames::InfoAction => Ok(GovernanceActionEnum::InfoAction( + InfoAction::deserialize_as_embedded_group(raw, len)?, + )), + }?; + + Ok(Self(proposal_enum)) + } +} + +fn get_proposal_index( + raw: &mut Deserializer, +) -> Result { + let initial_position = raw + .as_mut_ref() + .seek(SeekFrom::Current(0)) + .map_err(|err| DeserializeFailure::IoError(err.to_string()))?; + let index = raw.unsigned_integer()?; + raw.as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .map_err(|err| DeserializeFailure::IoError(err.to_string()))?; + Ok(index) +} diff --git a/rust/src/serialization/governance/proposals/hard_fork_initiation_action.rs b/rust/src/serialization/governance/proposals/hard_fork_initiation_action.rs new file mode 100644 index 00000000..ab6dcbdd --- /dev/null +++ b/rust/src/serialization/governance/proposals/hard_fork_initiation_action.rs @@ -0,0 +1,55 @@ +use crate::serialization::utils::serialize_and_check_index; +use crate::serialization::{check_len, deserialize_and_check_index}; +use crate::*; +use map_names::VotingProposalIndexNames; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for HardForkInitiationAction { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(3))?; + + let proposal_index = VotingProposalIndexNames::HardForkInitiationAction.to_u64(); + serialize_and_check_index(serializer, proposal_index, "HardForkInitiationAction")?; + + if let Some(gov_id) = &self.gov_action_id { + gov_id.serialize(serializer)?; + } else { + serializer.write_special(CBORSpecial::Null)?; + } + + self.protocol_version.serialize(serializer)?; + + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(HardForkInitiationAction); + +impl DeserializeEmbeddedGroup for HardForkInitiationAction { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len( + len, + 3, + "(proposal_index, gov_action_id // null, [protocol_version])", + )?; + + let desired_index = VotingProposalIndexNames::HardForkInitiationAction.to_u64(); + deserialize_and_check_index(raw, desired_index, "proposal_index")?; + + let gov_action_id = GovernanceActionId::deserialize_nullable(raw) + .map_err(|e| e.annotate("gov_action_id"))?; + + let protocol_version = ProtocolVersion::deserialize(raw)?; + + return Ok(HardForkInitiationAction { + gov_action_id, + protocol_version, + }); + } +} \ No newline at end of file diff --git a/rust/src/serialization/governance/proposals/info_action.rs b/rust/src/serialization/governance/proposals/info_action.rs new file mode 100644 index 00000000..aa9f3763 --- /dev/null +++ b/rust/src/serialization/governance/proposals/info_action.rs @@ -0,0 +1,39 @@ +use crate::serialization::utils:: serialize_and_check_index; +use crate::serialization::{check_len, deserialize_and_check_index}; +use crate::*; +use map_names::VotingProposalIndexNames; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for InfoAction { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(1))?; + + let proposal_index = VotingProposalIndexNames::InfoAction.to_u64(); + serialize_and_check_index(serializer, proposal_index, "InfoAction")?; + + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(InfoAction); + +impl DeserializeEmbeddedGroup for InfoAction { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len( + len, + 1, + "(proposal_index)", + )?; + + let desired_index = VotingProposalIndexNames::InfoAction.to_u64(); + deserialize_and_check_index(raw, desired_index, "proposal_index")?; + + return Ok(InfoAction()); + } +} \ No newline at end of file diff --git a/rust/src/serialization/governance/proposals/mod.rs b/rust/src/serialization/governance/proposals/mod.rs new file mode 100644 index 00000000..3252dc46 --- /dev/null +++ b/rust/src/serialization/governance/proposals/mod.rs @@ -0,0 +1,13 @@ +mod parameter_change_action; +mod hard_fork_initiation_action; +mod treasury_withdrawals_action; +mod treasury_withdrawals; +mod no_confidence_action; +mod committee; +mod update_committee_action; +mod constitution; +mod new_constitution_action; +mod governance_action; +mod info_action; +mod voting_proposal; +mod voting_proposals; \ No newline at end of file diff --git a/rust/src/serialization/governance/proposals/new_constitution_action.rs b/rust/src/serialization/governance/proposals/new_constitution_action.rs new file mode 100644 index 00000000..38cf3a28 --- /dev/null +++ b/rust/src/serialization/governance/proposals/new_constitution_action.rs @@ -0,0 +1,52 @@ +use crate::*; + +use crate::serialization::utils::serialize_and_check_index; +use crate::serialization::{check_len, deserialize_and_check_index}; +use map_names::VotingProposalIndexNames; +use num_traits::ToPrimitive; + +impl Serialize for NewConstitutionAction { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(3))?; + + let proposal_index = VotingProposalIndexNames::NewConstitutionAction.to_u64(); + serialize_and_check_index(serializer, proposal_index, "NewConstitutionAction")?; + + self.gov_action_id.serialize_nullable(serializer)?; + self.constitution.serialize(serializer)?; + + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(NewConstitutionAction); + +impl DeserializeEmbeddedGroup for NewConstitutionAction { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len( + len, + 3, + "(proposal_index, gov_action_id / null, constitution)", + )?; + + let desired_index = VotingProposalIndexNames::NewConstitutionAction.to_u64(); + deserialize_and_check_index(raw, desired_index, "proposal_index")?; + + let gov_action_id = GovernanceActionId::deserialize_nullable(raw) + .map_err(|e| e.annotate("gov_action_id"))?; + + let constitution = + Constitution::deserialize(raw).map_err(|e| e.annotate("constitution"))?; + + return Ok(NewConstitutionAction { + gov_action_id, + constitution, + }); + } +} diff --git a/rust/src/serialization/governance/proposals/no_confidence_action.rs b/rust/src/serialization/governance/proposals/no_confidence_action.rs new file mode 100644 index 00000000..6547756d --- /dev/null +++ b/rust/src/serialization/governance/proposals/no_confidence_action.rs @@ -0,0 +1,40 @@ +use crate::serialization::utils::serialize_and_check_index; +use crate::serialization::{check_len, deserialize_and_check_index}; +use crate::*; +use map_names::VotingProposalIndexNames; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for NoConfidenceAction { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + + let proposal_index = VotingProposalIndexNames::NoConfidenceAction.to_u64(); + serialize_and_check_index(serializer, proposal_index, "NoConfidenceAction")?; + + self.gov_action_id.serialize_nullable(serializer)?; + + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(NoConfidenceAction); + +impl DeserializeEmbeddedGroup for NoConfidenceAction { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len(len, 2, "(proposal_index, gov_action_id // null)")?; + + let desired_index = VotingProposalIndexNames::NoConfidenceAction.to_u64(); + deserialize_and_check_index(raw, desired_index, "proposal_index")?; + + let gov_action_id = GovernanceActionId::deserialize_nullable(raw) + .map_err(|e| e.annotate("gov_action_id"))?; + + return Ok(NoConfidenceAction { gov_action_id }); + } +} diff --git a/rust/src/serialization/governance/proposals/parameter_change_action.rs b/rust/src/serialization/governance/proposals/parameter_change_action.rs new file mode 100644 index 00000000..5a1bb8a3 --- /dev/null +++ b/rust/src/serialization/governance/proposals/parameter_change_action.rs @@ -0,0 +1,70 @@ +use crate::serialization::utils::serialize_and_check_index; +use crate::serialization::{check_len, deserialize_and_check_index}; +use crate::*; +use map_names::VotingProposalIndexNames; +use num_traits::ToPrimitive; + +impl cbor_event::se::Serialize for ParameterChangeAction { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(4))?; + + let proposal_index = VotingProposalIndexNames::ParameterChangeAction.to_u64(); + serialize_and_check_index(serializer, proposal_index, "ParameterChangeAction")?; + + self.gov_action_id.serialize_nullable(serializer)?; + self.protocol_param_updates.serialize(serializer)?; + self.policy_hash.serialize_nullable(serializer)?; + + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(ParameterChangeAction); + +impl DeserializeEmbeddedGroup for ParameterChangeAction { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + let has_policy_hash = len == cbor_event::Len::Len(4) || len == cbor_event::Len::Indefinite; + + //for sancho backwards compatibility + if !has_policy_hash { + check_len( + len, + 3, + "(proposal_index, gov_action_id // null, protocol_param_updates)", + )?; + } else { + check_len( + len, + 4, + "(proposal_index, gov_action_id // null, protocol_param_updates, policy_hash // null)", + )?; + } + + let desired_index = VotingProposalIndexNames::ParameterChangeAction.to_u64(); + deserialize_and_check_index(raw, desired_index, "proposal_index")?; + + let gov_action_id = GovernanceActionId::deserialize_nullable(raw) + .map_err(|e| e.annotate("gov_action_id"))?; + + let protocol_param_updates = ProtocolParamUpdate::deserialize(raw) + .map_err(|e| e.annotate("protocol_param_updates"))?; + + let policy_hash = if has_policy_hash { + ScriptHash::deserialize_nullable(raw).map_err(|e| e.annotate("policy_hash"))? + } else { + None + }; + + return Ok(ParameterChangeAction { + gov_action_id, + protocol_param_updates, + policy_hash, + }); + } +} diff --git a/rust/src/serialization/governance/proposals/treasury_withdrawals.rs b/rust/src/serialization/governance/proposals/treasury_withdrawals.rs new file mode 100644 index 00000000..b2867d6a --- /dev/null +++ b/rust/src/serialization/governance/proposals/treasury_withdrawals.rs @@ -0,0 +1,45 @@ +use crate::*; +use std::collections::BTreeMap; +use crate::serialization::utils::is_break_tag; + +impl Serialize for TreasuryWithdrawals { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for TreasuryWithdrawals { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = BTreeMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { + cbor_event::Len::Len(n) => table.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "TreasuryWithdrawals")? { + break; + } + let key = RewardAddress::deserialize(raw)?; + let value = Coin::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( + "some complicated/unsupported type", + ))) + .into()); + } + } + Ok(()) + })() + .map_err(|e| e.annotate("TreasuryWithdrawals"))?; + Ok(Self(table)) + } +} diff --git a/rust/src/serialization/governance/proposals/treasury_withdrawals_action.rs b/rust/src/serialization/governance/proposals/treasury_withdrawals_action.rs new file mode 100644 index 00000000..492d1aaf --- /dev/null +++ b/rust/src/serialization/governance/proposals/treasury_withdrawals_action.rs @@ -0,0 +1,55 @@ +use crate::serialization::utils::serialize_and_check_index; +use crate::serialization::{check_len, deserialize_and_check_index}; +use crate::*; +use map_names::VotingProposalIndexNames; +use num_traits::ToPrimitive; + +impl Serialize for TreasuryWithdrawalsAction { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(3))?; + + let proposal_index = VotingProposalIndexNames::TreasuryWithdrawalsAction.to_u64(); + serialize_and_check_index(serializer, proposal_index, "TreasuryWithdrawalsAction")?; + + self.withdrawals.serialize(serializer)?; + self.policy_hash.serialize_nullable(serializer)?; + + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(TreasuryWithdrawalsAction); + +impl DeserializeEmbeddedGroup for TreasuryWithdrawalsAction { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + + let has_policy_hash = len == cbor_event::Len::Len(3) || len == cbor_event::Len::Indefinite; + + //for sancho backwards compatibility + if !has_policy_hash { + check_len(len, 2, "(proposal_index, { reward_account => coin })")?; + } else { + check_len(len, 3, "(proposal_index, { reward_account => coin }, policy_hash / null)")?; + } + + + let desired_index = VotingProposalIndexNames::TreasuryWithdrawalsAction.to_u64(); + deserialize_and_check_index(raw, desired_index, "proposal_index")?; + + let withdrawals = TreasuryWithdrawals::deserialize(raw)?; + + let policy_hash = if has_policy_hash { + ScriptHash::deserialize_nullable(raw)? + } else { + None + }; + + return Ok(TreasuryWithdrawalsAction { withdrawals , policy_hash}); + } +} diff --git a/rust/src/serialization/governance/proposals/update_committee_action.rs b/rust/src/serialization/governance/proposals/update_committee_action.rs new file mode 100644 index 00000000..4ec62d55 --- /dev/null +++ b/rust/src/serialization/governance/proposals/update_committee_action.rs @@ -0,0 +1,56 @@ +use crate::serialization::utils::serialize_and_check_index; +use crate::serialization::{check_len, deserialize_and_check_index}; +use crate::*; +use map_names::VotingProposalIndexNames; +use num_traits::ToPrimitive; + +impl Serialize for UpdateCommitteeAction { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(5))?; + + let proposal_index = VotingProposalIndexNames::UpdateCommitteeAction.to_u64(); + serialize_and_check_index(serializer, proposal_index, "UpdateCommitteeAction")?; + + self.gov_action_id.serialize_nullable(serializer)?; + self.members_to_remove.serialize(serializer)?; + self.committee.serialize_as_embedded_group(serializer)?; + + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(UpdateCommitteeAction); + +impl DeserializeEmbeddedGroup for UpdateCommitteeAction { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len( + len, + 5, + "(proposal_index, gov_action_id / null, set<$committee_cold_credential>, { committee_cold_credential => epoch }, unit_interval)", + )?; + + let desired_index = VotingProposalIndexNames::UpdateCommitteeAction.to_u64(); + deserialize_and_check_index(raw, desired_index, "proposal_index")?; + + let gov_action_id = GovernanceActionId::deserialize_nullable(raw) + .map_err(|e| e.annotate("gov_action_id"))?; + + let members_to_remove = + Credentials::deserialize(raw).map_err(|e| e.annotate("members_to_remove"))?; + + let committee = Committee::deserialize_as_embedded_group(raw, cbor_event::Len::Len(2)) + .map_err(|e| e.annotate("committee"))?; + + return Ok(UpdateCommitteeAction { + gov_action_id, + members_to_remove, + committee, + }); + } +} diff --git a/rust/src/serialization/governance/proposals/voting_proposal.rs b/rust/src/serialization/governance/proposals/voting_proposal.rs new file mode 100644 index 00000000..348bb54d --- /dev/null +++ b/rust/src/serialization/governance/proposals/voting_proposal.rs @@ -0,0 +1,47 @@ +use crate::serialization::{check_len}; +use crate::*; + +impl Serialize for VotingProposal { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(4))?; + self.deposit.serialize(serializer)?; + self.reward_account.serialize(serializer)?; + self.governance_action.serialize(serializer)?; + self.anchor.serialize(serializer)?; + Ok(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(VotingProposal); + +impl DeserializeEmbeddedGroup for VotingProposal { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len( + len, + 4, + "(deposit, reward_account, gov_action, anchor)", + )?; + + let deposit = Coin::deserialize(raw) + .map_err(|e| e.annotate("deposit"))?; + let reward_account = RewardAddress::deserialize(raw) + .map_err(|e| e.annotate("reward_account"))?; + let gov_action = GovernanceAction::deserialize(raw) + .map_err(|e| e.annotate("gov_action"))?; + let anchor = Anchor::deserialize(raw) + .map_err(|e| e.annotate("anchor"))?; + + return Ok(VotingProposal { + deposit, + reward_account, + governance_action: gov_action, + anchor, + }); + } +} diff --git a/rust/src/serialization/governance/proposals/voting_proposals.rs b/rust/src/serialization/governance/proposals/voting_proposals.rs new file mode 100644 index 00000000..64ee941a --- /dev/null +++ b/rust/src/serialization/governance/proposals/voting_proposals.rs @@ -0,0 +1,40 @@ +use crate::*; +use crate::serialization::utils::{is_break_tag, skip_set_tag}; + +impl cbor_event::se::Serialize for VotingProposals { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + //TODO: uncomment this line when we conway ero will come + //serializer.write_tag(258)?; + serializer.write_array(cbor_event::Len::Len(self.len() as u64))?; + for element in &self.proposals { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for VotingProposals { + fn deserialize(raw: &mut Deserializer) -> Result { + skip_set_tag(raw)?; + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + skip_set_tag(raw)?; + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "VotingProposals")? { + break; + } + arr.push(VotingProposal::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("VotingProposals"))?; + Ok(Self::from_vec(arr)) + } +} diff --git a/rust/src/serialization/governance/voter.rs b/rust/src/serialization/governance/voter.rs new file mode 100644 index 00000000..f4529709 --- /dev/null +++ b/rust/src/serialization/governance/voter.rs @@ -0,0 +1,109 @@ +use crate::*; + +impl cbor_event::se::Serialize for Voter { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + self.0.serialize(serializer) + } +} + +impl Deserialize for Voter { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let voter_enum = VoterEnum::deserialize(raw)?; + Ok(Self(voter_enum)) + })() + .map_err(|e| e.annotate("Voter")) + } +} + +impl cbor_event::se::Serialize for VoterEnum { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + match &self { + VoterEnum::ConstitutionalCommitteeHotCred(cred) => match &cred.0 { + CredType::Key(key_hash) => { + serializer.write_unsigned_integer(0u64)?; + key_hash.serialize(serializer)?; + } + CredType::Script(script_hash) => { + serializer.write_unsigned_integer(1u64)?; + script_hash.serialize(serializer)?; + } + }, + VoterEnum::DRep(cred) => match &cred.0 { + CredType::Key(key_hash) => { + serializer.write_unsigned_integer(2u64)?; + key_hash.serialize(serializer)?; + } + CredType::Script(script_hash) => { + serializer.write_unsigned_integer(3u64)?; + script_hash.serialize(serializer)?; + } + }, + VoterEnum::StakingPool(scripthash) => { + serializer.write_unsigned_integer(4u64)?; + scripthash.serialize(serializer)?; + } + }; + Ok(serializer) + } +} + +impl Deserialize for VoterEnum { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + if let cbor_event::Len::Len(n) = len { + if n != 2 { + return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( + 2, + len, + "[id, hash]", + )) + .into()); + } + } + let voter = match raw.unsigned_integer()? { + 0 => VoterEnum::ConstitutionalCommitteeHotCred(Credential(CredType::Key( + Ed25519KeyHash::deserialize(raw)?, + ))), + 1 => VoterEnum::ConstitutionalCommitteeHotCred(Credential(CredType::Script( + ScriptHash::deserialize(raw)?, + ))), + 2 => VoterEnum::DRep(Credential(CredType::Key(Ed25519KeyHash::deserialize( + raw, + )?))), + 3 => VoterEnum::DRep(Credential(CredType::Script(ScriptHash::deserialize( + raw, + )?))), + 4 => VoterEnum::StakingPool(Ed25519KeyHash::deserialize(raw)?), + n => { + return Err(DeserializeFailure::FixedValuesMismatch { + found: Key::Uint(n), + expected: vec![ + Key::Uint(0), + Key::Uint(1), + Key::Uint(2), + Key::Uint(3), + Key::Uint(4), + ], + } + .into()) + } + }; + if let cbor_event::Len::Indefinite = len { + if raw.special()? != CBORSpecial::Break { + return Err(DeserializeFailure::EndingBreakMissing.into()); + } + } + Ok(voter) + })() + .map_err(|e| e.annotate("VoterEnum")) + } +} diff --git a/rust/src/serialization/governance/voting_procedure.rs b/rust/src/serialization/governance/voting_procedure.rs new file mode 100644 index 00000000..699a18a5 --- /dev/null +++ b/rust/src/serialization/governance/voting_procedure.rs @@ -0,0 +1,67 @@ +use crate::*; + +impl cbor_event::se::Serialize for VotingProcedure { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + match self.vote { + VoteKind::No => { + serializer.write_unsigned_integer(0u64)?; + } + VoteKind::Yes => { + serializer.write_unsigned_integer(1u64)?; + } + VoteKind::Abstain => { + serializer.write_unsigned_integer(2u64)?; + } + }; + self.anchor.serialize_nullable(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for VotingProcedure { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + if let cbor_event::Len::Len(n) = len { + if n != 2 { + return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( + 2, + len, + "[vote, anchor / null]", + )) + .into()); + } + } + + let vote = match raw.unsigned_integer()? { + 0 => VoteKind::No, + 1 => VoteKind::Yes, + 2 => VoteKind::Abstain, + n => { + return Err( + DeserializeError::from(DeserializeFailure::FixedValuesMismatch { + found: Key::Uint(n), + expected: vec![Key::Uint(0), Key::Uint(1), Key::Uint(2)], + }) + .annotate("vote"), + ) + } + }; + + let anchor = Anchor::deserialize_nullable(raw).map_err(|e| e.annotate("anchor"))?; + + if let cbor_event::Len::Indefinite = len { + if raw.special()? != CBORSpecial::Break { + return Err(DeserializeFailure::EndingBreakMissing.into()); + } + } + + Ok(Self { vote, anchor }) + })() + .map_err(|e| e.annotate("VotingProcedure")) + } +} diff --git a/rust/src/serialization/governance/voting_procedures.rs b/rust/src/serialization/governance/voting_procedures.rs new file mode 100644 index 00000000..25f10aab --- /dev/null +++ b/rust/src/serialization/governance/voting_procedures.rs @@ -0,0 +1,90 @@ +use crate::*; +use std::collections::BTreeMap; +use crate::serialization::utils::is_break_tag; + +impl cbor_event::se::Serialize for VotingProcedures { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (voter, votes) in &self.0 { + if votes.is_empty() { + continue; + } + voter.serialize(serializer)?; + serializer.write_map(cbor_event::Len::Len(votes.len() as u64))?; + for (governance_action_id, voting_procedure) in votes { + governance_action_id.serialize(serializer)?; + voting_procedure.serialize(serializer)?; + } + } + Ok(serializer) + } +} + +impl Deserialize for VotingProcedures { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut voter_to_vote = BTreeMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + let mut total = 0; + while match len { + cbor_event::Len::Len(n) => total < n, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "voting_procedure map")? { + break; + } + + let key = Voter::deserialize(raw).map_err(|e| e.annotate("voter"))?; + + let value = deserialize_internal_map(raw) + .map_err(|e| e.annotate("voting_procedure map"))?; + + if voter_to_vote.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( + "some complicated/unsupported type", + ))) + .into()); + } + total += 1; + } + Ok(Self(voter_to_vote)) + })() + .map_err(|e| e.annotate("VotingProcedures")) + } +} + +fn deserialize_internal_map( + raw: &mut Deserializer, +) -> Result, DeserializeError> { + let mut gov_act_id_to_vote = BTreeMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + let mut total = 0; + while match len { + cbor_event::Len::Len(n) => total < n, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "gov_act_id_to_vote map")? { + break; + } + + let key = GovernanceActionId::deserialize(raw).map_err(|e| e.annotate("gov_act_id"))?; + + let value = + VotingProcedure::deserialize(raw).map_err(|e| e.annotate("voting_procedure"))?; + + if gov_act_id_to_vote.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( + "some complicated/unsupported type", + ))) + .into()); + } + total += 1; + } + Ok(gov_act_id_to_vote) + })() + .map_err(|e| e.annotate("VotingProcedures (gov_act_id to vote_procedure map)")) +} diff --git a/rust/src/serialization/map_names/certificate_index_names.rs b/rust/src/serialization/map_names/certificate_index_names.rs new file mode 100644 index 00000000..b7ad732c --- /dev/null +++ b/rust/src/serialization/map_names/certificate_index_names.rs @@ -0,0 +1,22 @@ +#[derive(Eq, Hash, PartialEq, Clone, Debug, FromPrimitive, ToPrimitive)] +pub(crate) enum CertificateIndexNames { + StakeRegistrationLegacy = 0, + StakeDeregistrationLegacy = 1, + StakeDelegation = 2, + PoolRegistration = 3, + PoolRetirement = 4, + GenesisKeyDelegation = 5, + MoveInstantaneousRewardsCert = 6, + StakeRegistrationConway = 7, + StakeDeregistrationConway = 8, + VoteDelegation = 9, + StakeAndVoteDelegation = 10, + StakeRegistrationAndDelegation = 11, + VoteRegistrationAndDelegation = 12, + StakeVoteRegistrationAndDelegation = 13, + CommitteeHotAuth = 14, + CommitteeColdResign = 15, + DRepRegistration = 16, + DRepDeregistration = 17, + DRepUpdate = 18, +} diff --git a/rust/src/serialization/map_names/mod.rs b/rust/src/serialization/map_names/mod.rs new file mode 100644 index 00000000..9231e9c1 --- /dev/null +++ b/rust/src/serialization/map_names/mod.rs @@ -0,0 +1,11 @@ +mod tx_body_names; +pub(crate) use tx_body_names::*; + +mod witness_set_names; +pub(crate) use witness_set_names::*; + +mod certificate_index_names; +pub(crate) use certificate_index_names::*; + +mod voting_proposal_index_names; +pub(crate) use voting_proposal_index_names::*; diff --git a/rust/src/serialization/map_names/tx_body_names.rs b/rust/src/serialization/map_names/tx_body_names.rs new file mode 100644 index 00000000..1ff80475 --- /dev/null +++ b/rust/src/serialization/map_names/tx_body_names.rs @@ -0,0 +1,20 @@ +#[derive(Eq, Hash, PartialEq, Clone, Debug, FromPrimitive, ToPrimitive)] +pub(crate) enum TxBodyNames { + Inputs = 0, + Outputs = 1, + Fee = 2, + Ttl = 3, + Certs = 4, + Withdrawals = 5, + Update = 6, + AuxiliaryDataHash = 7, + ValidityStartInterval = 8, + Mint = 9, + ScriptDataHash = 11, + Collateral = 13, + RequiredSigners = 14, + NetworkId = 15, + CollateralReturn = 16, + TotalCollateral = 17, + ReferenceInputs = 18, +} diff --git a/rust/src/serialization/map_names/voting_proposal_index_names.rs b/rust/src/serialization/map_names/voting_proposal_index_names.rs new file mode 100644 index 00000000..bf8c33f2 --- /dev/null +++ b/rust/src/serialization/map_names/voting_proposal_index_names.rs @@ -0,0 +1,10 @@ +#[derive(Eq, Hash, PartialEq, Clone, Debug, FromPrimitive, ToPrimitive)] +pub(crate) enum VotingProposalIndexNames { + ParameterChangeAction = 0, + HardForkInitiationAction = 1, + TreasuryWithdrawalsAction = 2, + NoConfidenceAction = 3, + UpdateCommitteeAction = 4, + NewConstitutionAction = 5, + InfoAction = 6, +} diff --git a/rust/src/serialization/map_names/witness_set_names.rs b/rust/src/serialization/map_names/witness_set_names.rs new file mode 100644 index 00000000..f7af3ef6 --- /dev/null +++ b/rust/src/serialization/map_names/witness_set_names.rs @@ -0,0 +1,11 @@ +#[derive(Eq, Hash, PartialEq, Clone, Debug, FromPrimitive, ToPrimitive)] +pub(crate) enum WitnessSetNames { + Vkeys = 0, + NativeScripts = 1, + Bootstraps = 2, + PlutusScriptsV1 = 3, + PlutusData = 4, + Redeemers = 5, + PlutusScriptsV2 = 6, + PlutusScriptsV3 = 7, +} diff --git a/rust/src/serialization/metadata.rs b/rust/src/serialization/metadata.rs new file mode 100644 index 00000000..3b73d1e4 --- /dev/null +++ b/rust/src/serialization/metadata.rs @@ -0,0 +1,459 @@ +use hashlink::LinkedHashMap; +use crate::*; +use crate::serialization::utils::{is_break_tag, merge_option_plutus_list}; + +impl cbor_event::se::Serialize for MetadataMap { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for MetadataMap { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = LinkedHashMap::new(); + let mut entries: Vec<(TransactionMetadatum, TransactionMetadatum)> = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { + cbor_event::Len::Len(n) => entries.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "MetadataMap")? { + break; + } + let key = TransactionMetadatum::deserialize(raw)?; + let value = TransactionMetadatum::deserialize(raw)?; + entries.push((key.clone(), value)); + } + Ok(()) + })() + .map_err(|e| e.annotate("MetadataMap"))?; + entries.iter().for_each(|(k, v)| { + if table.insert(k.clone(), v.clone()).is_some() { + // Turns out this is totally possible on the actual blockchain + // return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from("some complicated/unsupported type"))).into()); + } + }); + Ok(Self(table)) + } +} + +impl cbor_event::se::Serialize for MetadataList { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for MetadataList { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "MetadataList")? { + break; + } + arr.push(TransactionMetadatum::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("MetadataList"))?; + Ok(Self(arr)) + } +} + +impl cbor_event::se::Serialize for TransactionMetadatumEnum { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + match self { + TransactionMetadatumEnum::MetadataMap(x) => x.serialize(serializer), + TransactionMetadatumEnum::MetadataList(x) => x.serialize(serializer), + TransactionMetadatumEnum::Int(x) => x.serialize(serializer), + TransactionMetadatumEnum::Bytes(x) => serializer.write_bytes(&x), + TransactionMetadatumEnum::Text(x) => serializer.write_text(&x), + } + } +} + +impl Deserialize for TransactionMetadatumEnum { + fn deserialize(raw: &mut Deserializer) -> Result { + match raw.cbor_type()? { + CBORType::Array => { + MetadataList::deserialize(raw).map(TransactionMetadatumEnum::MetadataList) + } + CBORType::Map => { + MetadataMap::deserialize(raw).map(TransactionMetadatumEnum::MetadataMap) + } + CBORType::Bytes => TransactionMetadatum::new_bytes(raw.bytes()?) + .map(|m| m.0) + .map_err(|e| DeserializeFailure::Metadata(e).into()), + CBORType::Text => TransactionMetadatum::new_text(raw.text()?) + .map(|m| m.0) + .map_err(|e| DeserializeFailure::Metadata(e).into()), + CBORType::UnsignedInteger | CBORType::NegativeInteger => { + Int::deserialize(raw).map(TransactionMetadatumEnum::Int) + } + _ => Err(DeserializeError::new( + "TransactionMetadatumEnum", + DeserializeFailure::NoVariantMatched.into(), + )), + } + } +} + +impl cbor_event::se::Serialize for TransactionMetadatum { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + self.0.serialize(serializer) + } +} + +impl Deserialize for TransactionMetadatum { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self(TransactionMetadatumEnum::deserialize(raw)?)) + } +} + +impl cbor_event::se::Serialize for TransactionMetadatumLabels { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for TransactionMetadatumLabels { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "TransactionMetadatumLabels")? { + break; + } + arr.push(TransactionMetadatumLabel::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("TransactionMetadatumLabels"))?; + Ok(Self(arr)) + } +} + +impl cbor_event::se::Serialize for GeneralTransactionMetadata { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for GeneralTransactionMetadata { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = LinkedHashMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { + cbor_event::Len::Len(n) => table.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "GeneralTransactionMetadata")? { + break; + } + let key = TransactionMetadatumLabel::deserialize(raw)?; + let value = TransactionMetadatum::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( + "some complicated/unsupported type", + ))) + .into()); + } + } + Ok(()) + })() + .map_err(|e| e.annotate("GeneralTransactionMetadata"))?; + Ok(Self(table)) + } +} + +impl cbor_event::se::Serialize for AuxiliaryData { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + // we still serialize using the shelley-mary era format as it is still supported + // and it takes up less space on-chain so this should be better for scaling. + // Plus the code was already written for shelley-mary anyway + if !self.prefer_alonzo_format && self.metadata.is_some() && self.plutus_scripts.is_none() { + match &self.native_scripts() { + Some(native_scripts) => { + serializer.write_array(cbor_event::Len::Len(2))?; + self.metadata.as_ref().unwrap().serialize(serializer)?; + native_scripts.serialize(serializer) + } + None => self.metadata.as_ref().unwrap().serialize(serializer), + } + } else { + let mut has_plutus_v2 = false; + let mut has_plutus_v3 = false; + let plutus_added_length = match &self.plutus_scripts { + Some(scripts) => { + has_plutus_v2 = scripts.has_version(&Language::new_plutus_v2()); + has_plutus_v3 = scripts.has_version(&Language::new_plutus_v3()); + 1 + (has_plutus_v2 as u64) + (has_plutus_v3 as u64) + }, + _ => 0, + }; + + // new format with plutus support + serializer.write_tag(259u64)?; + serializer.write_map(cbor_event::Len::Len( + opt64(&self.metadata) + opt64(&self.native_scripts) + plutus_added_length, + ))?; + if let Some(metadata) = &self.metadata { + serializer.write_unsigned_integer(0)?; + metadata.serialize(serializer)?; + } + if let Some(native_scripts) = &self.native_scripts { + serializer.write_unsigned_integer(1)?; + native_scripts.serialize(serializer)?; + } + if let Some(plutus_scripts) = &self.plutus_scripts { + serializer.write_unsigned_integer(2)?; + plutus_scripts.serialize_by_version(&Language::new_plutus_v1(), serializer)?; + if has_plutus_v2 { + serializer.write_unsigned_integer(3)?; + plutus_scripts.serialize_by_version(&Language::new_plutus_v2(), serializer)?; + } + if has_plutus_v3 { + serializer.write_unsigned_integer(4)?; + plutus_scripts.serialize_by_version(&Language::new_plutus_v3(), serializer)?; + } + } + Ok(serializer) + } + } +} + +impl Deserialize for AuxiliaryData { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + match raw.cbor_type()? { + // alonzo format + CBORType::Tag => { + let tag = raw.tag()?; + if tag != 259 { + return Err(DeserializeError::new( + "AuxiliaryData", + DeserializeFailure::TagMismatch { + found: tag, + expected: 259, + }, + )); + } + let len = raw.map()?; + let mut read_len = CBORReadLen::new(len); + let mut metadata = None; + let mut native_scripts = None; + let mut plutus_scripts_v1 = None; + let mut plutus_scripts_v2 = None; + let mut plutus_scripts_v3 = None; + let mut read = 0; + while match len { + cbor_event::Len::Len(n) => read < n as usize, + cbor_event::Len::Indefinite => true, + } { + match raw.cbor_type()? { + CBORType::UnsignedInteger => match raw.unsigned_integer()? { + 0 => { + if metadata.is_some() { + return Err( + DeserializeFailure::DuplicateKey(Key::Uint(0)).into() + ); + } + metadata = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(GeneralTransactionMetadata::deserialize(raw)?) + })() + .map_err(|e| e.annotate("metadata"))?, + ); + } + 1 => { + if native_scripts.is_some() { + return Err( + DeserializeFailure::DuplicateKey(Key::Uint(1)).into() + ); + } + native_scripts = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(NativeScripts::deserialize(raw)?) + })() + .map_err(|e| e.annotate("native_scripts"))?, + ); + } + 2 => { + if plutus_scripts_v1.is_some() { + return Err( + DeserializeFailure::DuplicateKey(Key::Uint(2)).into() + ); + } + plutus_scripts_v1 = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(PlutusScripts::deserialize(raw)?) + })() + .map_err(|e| e.annotate("plutus_scripts_v1"))?, + ); + } + 3 => { + if plutus_scripts_v2.is_some() { + return Err( + DeserializeFailure::DuplicateKey(Key::Uint(3)).into() + ); + } + plutus_scripts_v2 = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(PlutusScripts::deserialize_with_version(raw, &Language::new_plutus_v2())?) + })() + .map_err(|e| e.annotate("plutus_scripts_v2"))?, + ); + } + 4 => { + if plutus_scripts_v3.is_some() { + return Err( + DeserializeFailure::DuplicateKey(Key::Uint(3)).into() + ); + } + plutus_scripts_v3 = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(PlutusScripts::deserialize_with_version(raw, &Language::new_plutus_v3())?) + })() + .map_err(|e| e.annotate("plutus_scripts_v3"))?, + ); + } + unknown_key => { + return Err(DeserializeFailure::UnknownKey(Key::Uint( + unknown_key, + )) + .into()) + } + }, + CBORType::Text => match raw.text()?.as_str() { + unknown_key => { + return Err(DeserializeFailure::UnknownKey(Key::Str( + unknown_key.to_owned(), + )) + .into()) + } + }, + CBORType::Special => match len { + cbor_event::Len::Len(_) => { + return Err(DeserializeFailure::BreakInDefiniteLen.into()) + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => break, + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + }, + other_type => { + return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()) + } + } + read += 1; + } + read_len.finish()?; + let mut plutus_scripts = None; + plutus_scripts = merge_option_plutus_list(plutus_scripts, plutus_scripts_v1); + plutus_scripts = merge_option_plutus_list(plutus_scripts, plutus_scripts_v2); + plutus_scripts = merge_option_plutus_list(plutus_scripts, plutus_scripts_v3); + + Ok(Self { + metadata, + native_scripts, + plutus_scripts, + prefer_alonzo_format: true, + }) + } + // shelley mary format (still valid for alonzo) + CBORType::Array => { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let metadata = (|| -> Result<_, DeserializeError> { + Ok(GeneralTransactionMetadata::deserialize(raw)?) + })() + .map_err(|e| e.annotate("metadata"))?; + let native_scripts = (|| -> Result<_, DeserializeError> { + Ok(NativeScripts::deserialize(raw)?) + })() + .map_err(|e| e.annotate("native_scripts"))?; + match len { + cbor_event::Len::Len(_) => (), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + Ok(Self { + metadata: Some(metadata), + native_scripts: Some(native_scripts), + plutus_scripts: None, + prefer_alonzo_format: false, + }) + } + // shelley pre-mary format (still valid for alonzo + mary) + CBORType::Map => Ok(Self { + metadata: Some( + GeneralTransactionMetadata::deserialize(raw) + .map_err(|e| e.annotate("metadata"))?, + ), + native_scripts: None, + plutus_scripts: None, + prefer_alonzo_format: false, + }), + _ => return Err(DeserializeFailure::NoVariantMatched)?, + } + })() + .map_err(|e| e.annotate("AuxiliaryData")) + } +} \ No newline at end of file diff --git a/rust/src/serialization/mod.rs b/rust/src/serialization/mod.rs new file mode 100644 index 00000000..4624829a --- /dev/null +++ b/rust/src/serialization/mod.rs @@ -0,0 +1,30 @@ +pub mod map_names; +pub mod traits; +pub(super) use traits::*; + +mod ser_info; +pub use ser_info::*; + +mod general; +mod serialization_macros; +mod certificates; +mod governance; +mod utils; +mod fixed_tx; +use utils::*; +mod metadata; +mod transaction_body; +mod protocol_param_update; +mod tx_inputs; +mod credentials; +mod ed25519_key_hashes; +mod witnesses; +mod credential; +mod crypto; +mod plutus; +mod native_script; +mod native_scripts; +mod numeric; +mod script_ref; +mod tx_input; +mod block; \ No newline at end of file diff --git a/rust/src/serialization/native_script.rs b/rust/src/serialization/native_script.rs new file mode 100644 index 00000000..9b5722da --- /dev/null +++ b/rust/src/serialization/native_script.rs @@ -0,0 +1,519 @@ +use std::io::SeekFrom; +use crate::*; + +impl cbor_event::se::Serialize for NativeScript { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + self.0.serialize(serializer) + } +} + +impl Deserialize for NativeScript { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self(NativeScriptEnum::deserialize(raw)?)) + } +} + +impl cbor_event::se::Serialize for NativeScriptEnum { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + match self { + NativeScriptEnum::ScriptPubkey(x) => x.serialize(serializer), + NativeScriptEnum::ScriptAll(x) => x.serialize(serializer), + NativeScriptEnum::ScriptAny(x) => x.serialize(serializer), + NativeScriptEnum::ScriptNOfK(x) => x.serialize(serializer), + NativeScriptEnum::TimelockStart(x) => x.serialize(serializer), + NativeScriptEnum::TimelockExpiry(x) => x.serialize(serializer), + } + } +} + +impl Deserialize for NativeScriptEnum { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + //let mut read_len = CBORReadLen::new(len); + let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(ScriptPubkey::deserialize_as_embedded_group( + raw, /*&mut read_len, */ len, + )?) + })(raw) + { + Ok(variant) => return Ok(NativeScriptEnum::ScriptPubkey(variant)), + Err(_) => raw + .as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(ScriptAll::deserialize_as_embedded_group( + raw, /*mut read_len, */ len, + )?) + })(raw) + { + Ok(variant) => return Ok(NativeScriptEnum::ScriptAll(variant)), + Err(_) => raw + .as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(ScriptAny::deserialize_as_embedded_group( + raw, /*mut read_len, */ len, + )?) + })(raw) + { + Ok(variant) => return Ok(NativeScriptEnum::ScriptAny(variant)), + Err(_) => raw + .as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(ScriptNOfK::deserialize_as_embedded_group( + raw, /*mut read_len, */ len, + )?) + })(raw) + { + Ok(variant) => return Ok(NativeScriptEnum::ScriptNOfK(variant)), + Err(_) => raw + .as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(TimelockStart::deserialize_as_embedded_group( + raw, /*mut read_len, */ len, + )?) + })(raw) + { + Ok(variant) => return Ok(NativeScriptEnum::TimelockStart(variant)), + Err(_) => raw + .as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(TimelockExpiry::deserialize_as_embedded_group( + raw, /*mut read_len, */ len, + )?) + })(raw) + { + Ok(variant) => return Ok(NativeScriptEnum::TimelockExpiry(variant)), + Err(_) => raw + .as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .unwrap(), + }; + match len { + cbor_event::Len::Len(_) => (), /*read_len.finish()?*/ + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), /*read_len.finish()?*/ + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + Err(DeserializeError::new( + "NativeScriptEnum", + DeserializeFailure::NoVariantMatched.into(), + )) + })() + .map_err(|e| e.annotate("NativeScriptEnum")) + } +} + + +impl cbor_event::se::Serialize for ScriptPubkey { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for ScriptPubkey { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_unsigned_integer(0u64)?; + self.addr_keyhash.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for ScriptPubkey { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let ret = Self::deserialize_as_embedded_group(raw, /*mut read_len, */ len); + match len { + cbor_event::Len::Len(_) => read_len.finish()?, + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("ScriptPubkey")) + } +} + +impl DeserializeEmbeddedGroup for ScriptPubkey { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + /*read_len: &mut CBORReadLen, */ _: cbor_event::Len, + ) -> Result { + (|| -> Result<_, DeserializeError> { + let index_0_value = raw.unsigned_integer()?; + if index_0_value != 0 { + return Err(DeserializeFailure::FixedValueMismatch { + found: Key::Uint(index_0_value), + expected: Key::Uint(0), + } + .into()); + } + Ok(()) + })() + .map_err(|e| e.annotate("index_0"))?; + let addr_keyhash = + (|| -> Result<_, DeserializeError> { Ok(Ed25519KeyHash::deserialize(raw)?) })() + .map_err(|e| e.annotate("addr_keyhash"))?; + Ok(ScriptPubkey { addr_keyhash }) + } +} + +impl cbor_event::se::Serialize for ScriptAll { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for ScriptAll { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_unsigned_integer(1u64)?; + self.native_scripts.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for ScriptAll { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let ret = Self::deserialize_as_embedded_group(raw, /*mut read_len, */ len); + match len { + cbor_event::Len::Len(_) => read_len.finish()?, + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("ScriptAll")) + } +} + +impl DeserializeEmbeddedGroup for ScriptAll { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + /*read_len: &mut CBORReadLen, */ _: cbor_event::Len, + ) -> Result { + (|| -> Result<_, DeserializeError> { + let index_0_value = raw.unsigned_integer()?; + if index_0_value != 1 { + return Err(DeserializeFailure::FixedValueMismatch { + found: Key::Uint(index_0_value), + expected: Key::Uint(1), + } + .into()); + } + Ok(()) + })() + .map_err(|e| e.annotate("index_0"))?; + let native_scripts = + (|| -> Result<_, DeserializeError> { Ok(NativeScripts::deserialize(raw)?) })() + .map_err(|e| e.annotate("native_scripts"))?; + Ok(ScriptAll { native_scripts }) + } +} + +impl cbor_event::se::Serialize for ScriptAny { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for ScriptAny { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_unsigned_integer(2u64)?; + self.native_scripts.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for ScriptAny { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let ret = Self::deserialize_as_embedded_group(raw, /*mut read_len, */ len); + match len { + cbor_event::Len::Len(_) => read_len.finish()?, + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("ScriptAny")) + } +} + +impl DeserializeEmbeddedGroup for ScriptAny { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + /*/*read_len: &mut CBORReadLen, */*/ _: cbor_event::Len, + ) -> Result { + (|| -> Result<_, DeserializeError> { + let index_0_value = raw.unsigned_integer()?; + if index_0_value != 2 { + return Err(DeserializeFailure::FixedValueMismatch { + found: Key::Uint(index_0_value), + expected: Key::Uint(2), + } + .into()); + } + Ok(()) + })() + .map_err(|e| e.annotate("index_0"))?; + let native_scripts = + (|| -> Result<_, DeserializeError> { Ok(NativeScripts::deserialize(raw)?) })() + .map_err(|e| e.annotate("native_scripts"))?; + Ok(ScriptAny { native_scripts }) + } +} + +impl cbor_event::se::Serialize for ScriptNOfK { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(3))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for ScriptNOfK { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_unsigned_integer(3u64)?; + self.n.serialize(serializer)?; + self.native_scripts.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for ScriptNOfK { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(3)?; + let ret = Self::deserialize_as_embedded_group(raw, /*mut read_len, */ len); + match len { + cbor_event::Len::Len(_) => read_len.finish()?, + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("ScriptNOfK")) + } +} + +impl DeserializeEmbeddedGroup for ScriptNOfK { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + /*read_len: &mut CBORReadLen, */ _: cbor_event::Len, + ) -> Result { + (|| -> Result<_, DeserializeError> { + let index_0_value = raw.unsigned_integer()?; + if index_0_value != 3 { + return Err(DeserializeFailure::FixedValueMismatch { + found: Key::Uint(index_0_value), + expected: Key::Uint(3), + } + .into()); + } + Ok(()) + })() + .map_err(|e| e.annotate("index_0"))?; + let n = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() + .map_err(|e| e.annotate("n"))?; + let native_scripts = + (|| -> Result<_, DeserializeError> { Ok(NativeScripts::deserialize(raw)?) })() + .map_err(|e| e.annotate("native_scripts"))?; + Ok(ScriptNOfK { n, native_scripts }) + } +} + +impl cbor_event::se::Serialize for TimelockStart { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for TimelockStart { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_unsigned_integer(4u64)?; + self.slot.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for TimelockStart { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let ret = Self::deserialize_as_embedded_group(raw, /*mut read_len, */ len); + match len { + cbor_event::Len::Len(_) => read_len.finish()?, + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("TimelockStart")) + } +} + +impl DeserializeEmbeddedGroup for TimelockStart { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + /*read_len: &mut CBORReadLen, */ _: cbor_event::Len, + ) -> Result { + (|| -> Result<_, DeserializeError> { + let index_0_value = raw.unsigned_integer()?; + if index_0_value != 4 { + return Err(DeserializeFailure::FixedValueMismatch { + found: Key::Uint(index_0_value), + expected: Key::Uint(4), + } + .into()); + } + Ok(()) + })() + .map_err(|e| e.annotate("index_0"))?; + let slot = (|| -> Result<_, DeserializeError> { Ok(SlotBigNum::deserialize(raw)?) })() + .map_err(|e| e.annotate("slot"))?; + Ok(TimelockStart { slot }) + } +} + +impl cbor_event::se::Serialize for TimelockExpiry { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.serialize_as_embedded_group(serializer) + } +} + +impl SerializeEmbeddedGroup for TimelockExpiry { + fn serialize_as_embedded_group<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_unsigned_integer(5u64)?; + self.slot.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for TimelockExpiry { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let ret = Self::deserialize_as_embedded_group(raw, /*&mut read_len, */ len); + match len { + cbor_event::Len::Len(_) => read_len.finish()?, + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("TimelockExpiry")) + } +} + +impl DeserializeEmbeddedGroup for TimelockExpiry { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + /*read_len: &mut CBORReadLen, */ _: cbor_event::Len, + ) -> Result { + (|| -> Result<_, DeserializeError> { + let index_0_value = raw.unsigned_integer()?; + if index_0_value != 5 { + return Err(DeserializeFailure::FixedValueMismatch { + found: Key::Uint(index_0_value), + expected: Key::Uint(5), + } + .into()); + } + Ok(()) + })() + .map_err(|e| e.annotate("index_0"))?; + let slot = (|| -> Result<_, DeserializeError> { Ok(SlotBigNum::deserialize(raw)?) })() + .map_err(|e| e.annotate("slot"))?; + Ok(TimelockExpiry { slot }) + } +} \ No newline at end of file diff --git a/rust/src/serialization/native_scripts.rs b/rust/src/serialization/native_scripts.rs new file mode 100644 index 00000000..8b3f5e7b --- /dev/null +++ b/rust/src/serialization/native_scripts.rs @@ -0,0 +1,61 @@ +use crate::*; +use crate::serialization::utils::{is_break_tag, skip_set_tag}; + +impl cbor_event::se::Serialize for NativeScripts { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl NativeScripts { + pub(crate) fn serialize_as_set<'se, W: Write>( + &self, + need_deduplication: bool, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + //TODO: uncomment this line when we conway ero will come + //serializer.write_tag(258)?; + if need_deduplication { + let view = self.deduplicated_view(); + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in view { + element.serialize(serializer)?; + } + } else { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + } + Ok(serializer) + } +} + +impl Deserialize for NativeScripts { + fn deserialize(raw: &mut Deserializer) -> Result { + skip_set_tag(raw)?; + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "NativeScripts")? { + break; + } + arr.push(NativeScript::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("NativeScripts"))?; + Ok(Self(arr)) + } +} \ No newline at end of file diff --git a/rust/src/serialization/numeric/big_int.rs b/rust/src/serialization/numeric/big_int.rs new file mode 100644 index 00000000..6c62d4e5 --- /dev/null +++ b/rust/src/serialization/numeric/big_int.rs @@ -0,0 +1,103 @@ +use crate::*; +use crate::serialization::utils::read_nint; + +impl Serialize for BigInt { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + let (sign, u64_digits) = self.0.to_u64_digits(); + match u64_digits.len() { + 0 => serializer.write_unsigned_integer(0), + // we use the uint/nint encodings to use a minimum of space + 1 => match sign { + // uint + num_bigint::Sign::Plus | num_bigint::Sign::NoSign => { + serializer.write_unsigned_integer(*u64_digits.first().unwrap()) + } + // nint + num_bigint::Sign::Minus => serializer + .write_negative_integer(-(*u64_digits.first().unwrap() as i128) as i64), + }, + _ => { + // Small edge case: nint's minimum is -18446744073709551616 but in this bigint lib + // that takes 2 u64 bytes so we put that as a special case here: + if sign == num_bigint::Sign::Minus && u64_digits == vec![0, 1] { + serializer.write_negative_integer(-18446744073709551616i128 as i64) + } else { + let (sign, bytes) = self.0.to_bytes_be(); + match sign { + // positive bigint + num_bigint::Sign::Plus | num_bigint::Sign::NoSign => { + serializer.write_tag(2u64)?; + write_bounded_bytes(serializer, &bytes) + } + // negative bigint + num_bigint::Sign::Minus => { + serializer.write_tag(3u64)?; + use std::ops::Neg; + // CBOR RFC defines this as the bytes of -n -1 + let adjusted = self + .0 + .clone() + .neg() + .checked_sub(&num_bigint::BigInt::from(1u32)) + .unwrap() + .to_biguint() + .unwrap(); + write_bounded_bytes(serializer, &adjusted.to_bytes_be()) + } + } + } + } + } + } +} + +impl Deserialize for BigInt { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + match raw.cbor_type()? { + // bigint + CBORType::Tag => { + let tag = raw.tag()?; + let bytes = read_bounded_bytes(raw)?; + match tag { + // positive bigint + 2 => Ok(Self(num_bigint::BigInt::from_bytes_be( + num_bigint::Sign::Plus, + &bytes, + ))), + // negative bigint + 3 => { + // CBOR RFC defines this as the bytes of -n -1 + let initial = + num_bigint::BigInt::from_bytes_be(num_bigint::Sign::Plus, &bytes); + use std::ops::Neg; + let adjusted = initial + .checked_add(&num_bigint::BigInt::from(1u32)) + .unwrap() + .neg(); + Ok(Self(adjusted)) + } + _ => { + return Err(DeserializeFailure::TagMismatch { + found: tag, + expected: 2, + } + .into()); + } + } + } + // uint + CBORType::UnsignedInteger => { + Ok(Self(num_bigint::BigInt::from(raw.unsigned_integer()?))) + } + // nint + CBORType::NegativeInteger => Ok(Self(num_bigint::BigInt::from(read_nint(raw)?))), + _ => return Err(DeserializeFailure::NoVariantMatched.into()), + } + })() + .map_err(|e| e.annotate("BigInt")) + } +} \ No newline at end of file diff --git a/rust/src/serialization/numeric/big_num.rs b/rust/src/serialization/numeric/big_num.rs new file mode 100644 index 00000000..dd10ffc2 --- /dev/null +++ b/rust/src/serialization/numeric/big_num.rs @@ -0,0 +1,19 @@ +use crate::*; + +impl Serialize for BigNum { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_unsigned_integer(self.0) + } +} + +impl Deserialize for BigNum { + fn deserialize(raw: &mut Deserializer) -> Result { + match raw.unsigned_integer() { + Ok(value) => Ok(Self(value)), + Err(e) => Err(DeserializeError::new("BigNum", DeserializeFailure::CBOR(e))), + } + } +} \ No newline at end of file diff --git a/rust/src/serialization/numeric/int.rs b/rust/src/serialization/numeric/int.rs new file mode 100644 index 00000000..a2d40f18 --- /dev/null +++ b/rust/src/serialization/numeric/int.rs @@ -0,0 +1,28 @@ +use crate::*; +use crate::serialization::utils::read_nint; + +impl cbor_event::se::Serialize for Int { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + if self.0 < 0 { + serializer.write_negative_integer(self.0 as i64) + } else { + serializer.write_unsigned_integer(self.0 as u64) + } + } +} + +impl Deserialize for Int { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + match raw.cbor_type()? { + cbor_event::Type::UnsignedInteger => Ok(Self(raw.unsigned_integer()? as i128)), + cbor_event::Type::NegativeInteger => Ok(Self(read_nint(raw)?)), + _ => Err(DeserializeFailure::NoVariantMatched.into()), + } + })() + .map_err(|e| e.annotate("Int")) + } +} \ No newline at end of file diff --git a/rust/src/serialization/numeric/mod.rs b/rust/src/serialization/numeric/mod.rs new file mode 100644 index 00000000..050422d6 --- /dev/null +++ b/rust/src/serialization/numeric/mod.rs @@ -0,0 +1,3 @@ +mod int; +mod big_int; +mod big_num; \ No newline at end of file diff --git a/rust/src/serialization/plutus/cost_model.rs b/rust/src/serialization/plutus/cost_model.rs new file mode 100644 index 00000000..4c7d0b43 --- /dev/null +++ b/rust/src/serialization/plutus/cost_model.rs @@ -0,0 +1,36 @@ +use crate::*; +use crate::serialization::utils::is_break_tag; + +impl cbor_event::se::Serialize for CostModel { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for cost in &self.0 { + cost.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for CostModel { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "CostModel")? { + break; + } + arr.push(Int::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("CostModel"))?; + Ok(Self(arr.try_into().unwrap())) + } +} \ No newline at end of file diff --git a/rust/src/serialization/plutus/cost_models.rs b/rust/src/serialization/plutus/cost_models.rs new file mode 100644 index 00000000..538b8200 --- /dev/null +++ b/rust/src/serialization/plutus/cost_models.rs @@ -0,0 +1,44 @@ +use crate::*; +use crate::serialization::utils::is_break_tag; + +impl cbor_event::se::Serialize for Costmdls { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.0.len() as u64))?; + for (key, value) in &self.0 { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Costmdls { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut table = std::collections::BTreeMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + while match len { + cbor_event::Len::Len(n) => table.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "Costmdls")? { + break; + } + let key = Language::deserialize(raw)?; + let value = CostModel::deserialize(raw)?; + if table.insert(key.clone(), value).is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( + "some complicated/unsupported type", + ))) + .into()); + } + } + Ok(()) + })() + .map_err(|e| e.annotate("Costmdls"))?; + Ok(Self(table)) + } +} \ No newline at end of file diff --git a/rust/src/serialization/plutus/ex_unit_prices.rs b/rust/src/serialization/plutus/ex_unit_prices.rs new file mode 100644 index 00000000..31c5ae1a --- /dev/null +++ b/rust/src/serialization/plutus/ex_unit_prices.rs @@ -0,0 +1,41 @@ +use crate::*; + +impl cbor_event::se::Serialize for ExUnitPrices { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.mem_price.serialize(serializer)?; + self.step_price.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for ExUnitPrices { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let mem_price = + (|| -> Result<_, DeserializeError> { Ok(SubCoin::deserialize(raw)?) })() + .map_err(|e| e.annotate("mem_price"))?; + let step_price = + (|| -> Result<_, DeserializeError> { Ok(SubCoin::deserialize(raw)?) })() + .map_err(|e| e.annotate("step_price"))?; + match len { + cbor_event::Len::Len(_) => (), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + Ok(ExUnitPrices { + mem_price, + step_price, + }) + })() + .map_err(|e| e.annotate("ExUnitPrices")) + } +} \ No newline at end of file diff --git a/rust/src/serialization/plutus/ex_units.rs b/rust/src/serialization/plutus/ex_units.rs new file mode 100644 index 00000000..9f0599fb --- /dev/null +++ b/rust/src/serialization/plutus/ex_units.rs @@ -0,0 +1,36 @@ +use crate::*; + +impl cbor_event::se::Serialize for ExUnits { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.mem.serialize(serializer)?; + self.steps.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for ExUnits { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let mem = (|| -> Result<_, DeserializeError> { Ok(BigNum::deserialize(raw)?) })() + .map_err(|e| e.annotate("mem"))?; + let steps = (|| -> Result<_, DeserializeError> { Ok(BigNum::deserialize(raw)?) })() + .map_err(|e| e.annotate("steps"))?; + match len { + cbor_event::Len::Len(_) => (), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + Ok(ExUnits { mem, steps }) + })() + .map_err(|e| e.annotate("ExUnits")) + } +} \ No newline at end of file diff --git a/rust/src/serialization/plutus/language.rs b/rust/src/serialization/plutus/language.rs new file mode 100644 index 00000000..4686a663 --- /dev/null +++ b/rust/src/serialization/plutus/language.rs @@ -0,0 +1,26 @@ +use crate::*; + +impl cbor_event::se::Serialize for Language { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + // https://github.com/input-output-hk/cardano-ledger/blob/master/eras/babbage/test-suite/cddl-files/babbage.cddl#L324-L327 + serializer.write_unsigned_integer(self.kind() as u64) + } +} + +impl Deserialize for Language { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + match LanguageKind::from_u64(raw.unsigned_integer()?) { + Some(kind) => Ok(Language(kind)), + _ => Err(DeserializeError::new( + "Language", + DeserializeFailure::NoVariantMatched.into(), + )), + } + })() + .map_err(|e| e.annotate("Language")) + } +} \ No newline at end of file diff --git a/rust/src/serialization/plutus/languages.rs b/rust/src/serialization/plutus/languages.rs new file mode 100644 index 00000000..59709b45 --- /dev/null +++ b/rust/src/serialization/plutus/languages.rs @@ -0,0 +1,36 @@ +use crate::*; +use crate::serialization::utils::is_break_tag; + +impl cbor_event::se::Serialize for Languages { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Languages { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "Languages")? { + break; + } + arr.push(Language::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("Languages"))?; + Ok(Self(arr)) + } +} \ No newline at end of file diff --git a/rust/src/serialization/plutus/mod.rs b/rust/src/serialization/plutus/mod.rs new file mode 100644 index 00000000..68b50d17 --- /dev/null +++ b/rust/src/serialization/plutus/mod.rs @@ -0,0 +1,13 @@ +mod cost_model; +mod cost_models; +mod ex_unit_prices; +mod ex_units; +mod language; +mod languages; +mod plutus_data; +mod plutus_script; +mod plutus_scripts; +mod redeemer; +mod redeemer_tag; +mod redeemers; +mod strings; diff --git a/rust/src/serialization/plutus/plutus_data.rs b/rust/src/serialization/plutus/plutus_data.rs new file mode 100644 index 00000000..f3ce5f64 --- /dev/null +++ b/rust/src/serialization/plutus/plutus_data.rs @@ -0,0 +1,296 @@ +use crate::*; +use std::io::SeekFrom; +use crate::serialization::utils::{is_break_tag, skip_set_tag}; + +impl cbor_event::se::Serialize for ConstrPlutusData { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + if let Some(compact_tag) = + Self::alternative_to_compact_cbor_tag(self.alternative.into()) + { + // compact form + serializer.write_tag(compact_tag as u64)?; + self.data.serialize(serializer) + } else { + // general form + serializer.write_tag(Self::GENERAL_FORM_TAG)?; + serializer.write_array(cbor_event::Len::Len(2))?; + self.alternative.serialize(serializer)?; + self.data.serialize(serializer) + } + } +} + +impl Deserialize for ConstrPlutusData { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let (alternative, data) = match raw.tag()? { + // general form + Self::GENERAL_FORM_TAG => { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + let alternative = BigNum::deserialize(raw)?; + let data = + (|| -> Result<_, DeserializeError> { Ok(PlutusList::deserialize(raw)?) })() + .map_err(|e| e.annotate("datas"))?; + match len { + cbor_event::Len::Len(_) => (), + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => (), + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + (alternative, data) + } + // concise form + tag => { + if let Some(alternative) = Self::compact_cbor_tag_to_alternative(tag) { + (alternative.into(), PlutusList::deserialize(raw)?) + } else { + return Err(DeserializeFailure::TagMismatch { + found: tag, + expected: Self::GENERAL_FORM_TAG, + } + .into()); + } + } + }; + Ok(ConstrPlutusData { alternative, data }) + })() + .map_err(|e| e.annotate("ConstrPlutusData")) + } +} + +impl cbor_event::se::Serialize for PlutusMap { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len(self.total_len() as u64))?; + for (key, values) in &self.0 { + for value in &values.elems { + key.serialize(serializer)?; + value.serialize(serializer)?; + } + } + Ok(serializer) + } +} + +impl Deserialize for PlutusMap { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut plutus_map = PlutusMap::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + let mut total = 0; + while match len { + cbor_event::Len::Len(n) => total < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "PlutusMap")? { + break; + } + let key = PlutusData::deserialize(raw)?; + let value = PlutusData::deserialize(raw)?; + plutus_map.add_value_move(key, value); + total += 1; + } + Ok(()) + })() + .map_err(|e| e.annotate("PlutusMap"))?; + Ok(plutus_map) + } +} + +impl cbor_event::se::Serialize for PlutusDataEnum { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + match self { + PlutusDataEnum::ConstrPlutusData(x) => x.serialize(serializer), + PlutusDataEnum::Map(x) => x.serialize(serializer), + PlutusDataEnum::List(x) => x.serialize(serializer), + PlutusDataEnum::Integer(x) => x.serialize(serializer), + PlutusDataEnum::Bytes(x) => write_bounded_bytes(serializer, &x), + } + } +} + +impl Deserialize for PlutusDataEnum { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let initial_position = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(ConstrPlutusData::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PlutusDataEnum::ConstrPlutusData(variant)), + Err(_) => raw + .as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(PlutusMap::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PlutusDataEnum::Map(variant)), + Err(_) => raw + .as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(PlutusList::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PlutusDataEnum::List(variant)), + Err(_) => raw + .as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(BigInt::deserialize(raw)?) + })(raw) + { + Ok(variant) => return Ok(PlutusDataEnum::Integer(variant)), + Err(_) => raw + .as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .unwrap(), + }; + match (|raw: &mut Deserializer<_>| -> Result<_, DeserializeError> { + Ok(read_bounded_bytes(raw)?) + })(raw) + { + Ok(variant) => return Ok(PlutusDataEnum::Bytes(variant)), + Err(_) => raw + .as_mut_ref() + .seek(SeekFrom::Start(initial_position)) + .unwrap(), + }; + Err(DeserializeError::new( + "PlutusDataEnum", + DeserializeFailure::NoVariantMatched.into(), + )) + })() + .map_err(|e| e.annotate("PlutusDataEnum")) + } +} + +impl cbor_event::se::Serialize for PlutusData { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + match &self.original_bytes { + Some(bytes) => serializer.write_raw_bytes(bytes), + None => self.datum.serialize(serializer), + } + } +} + +impl Deserialize for PlutusData { + fn deserialize(raw: &mut Deserializer) -> Result { + // these unwraps are fine since we're seeking the current position + let before = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + let datum = PlutusDataEnum::deserialize(raw)?; + let after = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + let bytes_read = (after - before) as usize; + raw.as_mut_ref().seek(SeekFrom::Start(before)).unwrap(); + // these unwraps are fine since we read the above already + let original_bytes = raw.as_mut_ref().fill_buf().unwrap()[..bytes_read].to_vec(); + raw.as_mut_ref().consume(bytes_read); + Ok(Self { + datum, + original_bytes: Some(original_bytes), + }) + } +} + +impl cbor_event::se::Serialize for PlutusList { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + let use_definite_encoding = match self.definite_encoding { + Some(definite) => definite, + None => self.elems.is_empty(), + }; + if use_definite_encoding { + serializer.write_array(cbor_event::Len::Len(self.elems.len() as u64))?; + } else { + serializer.write_array(cbor_event::Len::Indefinite)?; + } + for element in &self.elems { + element.serialize(serializer)?; + } + if !use_definite_encoding { + serializer.write_special(cbor_event::Special::Break)?; + } + Ok(serializer) + } +} + +impl PlutusList { + pub(crate) fn serialize_as_set<'se, W: Write>( + &self, + need_deduplication: bool, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + //TODO: uncomment this line when we conway ero will come + //serializer.write_tag(258)?; + let use_definite_encoding = match self.definite_encoding { + Some(definite) => definite, + None => self.elems.is_empty(), + }; + if use_definite_encoding { + serializer.write_array(cbor_event::Len::Len(self.elems.len() as u64))?; + } else { + serializer.write_array(cbor_event::Len::Indefinite)?; + } + if need_deduplication { + for element in self.deduplicated_view() { + element.serialize(serializer)?; + } + } else { + for element in &self.elems { + element.serialize(serializer)?; + } + } + if !use_definite_encoding { + serializer.write_special(cbor_event::Special::Break)?; + } + Ok(serializer) + } +} + +impl Deserialize for PlutusList { + fn deserialize(raw: &mut Deserializer) -> Result { + skip_set_tag(raw)?; + let mut arr = Vec::new(); + let len = (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "PlutusList")? { + break; + } + arr.push(PlutusData::deserialize(raw)?); + } + Ok(len) + })() + .map_err(|e| e.annotate("PlutusList"))?; + Ok(Self { + elems: arr, + definite_encoding: Some(len != cbor_event::Len::Indefinite), + }) + } +} \ No newline at end of file diff --git a/rust/src/serialization/plutus/plutus_script.rs b/rust/src/serialization/plutus/plutus_script.rs new file mode 100644 index 00000000..64250739 --- /dev/null +++ b/rust/src/serialization/plutus/plutus_script.rs @@ -0,0 +1,22 @@ +use crate::*; + +impl cbor_event::se::Serialize for PlutusScript { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_bytes(&self.bytes) + } +} + +impl Deserialize for PlutusScript { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self::new(raw.bytes()?)) + } +} + +impl PlutusScript { + pub(crate) fn deserialize_with_version(raw: &mut Deserializer, version: &Language) -> Result { + Ok(Self::new_with_version(raw.bytes()?, version)) + } +} \ No newline at end of file diff --git a/rust/src/serialization/plutus/plutus_scripts.rs b/rust/src/serialization/plutus/plutus_scripts.rs new file mode 100644 index 00000000..37f30906 --- /dev/null +++ b/rust/src/serialization/plutus/plutus_scripts.rs @@ -0,0 +1,95 @@ +use crate::*; +use crate::serialization::utils::{is_break_tag, skip_set_tag}; + +impl cbor_event::se::Serialize for PlutusScripts { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl PlutusScripts { + pub(crate) fn serialize_by_version<'se, W: Write>( + &self, + version: &Language, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + let view = self.view(version); + serializer.write_array(cbor_event::Len::Len(view.len() as u64))?; + for element in view { + element.serialize(serializer)?; + } + Ok(serializer) + } + + pub(crate) fn serialize_as_set_by_version<'se, W: Write>( + &self, + need_deduplication: bool, + version: &Language, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + //TODO: uncomment this line when we conway ero will come + //serializer.write_tag(258)?; + let view = match need_deduplication { + true => self.deduplicated_view(Some(version)), + false => self.view(version), + }; + serializer.write_array(cbor_event::Len::Len(view.len() as u64))?; + for element in view { + element.serialize(serializer)?; + } + Ok(serializer) + } + +} + +impl Deserialize for PlutusScripts { + fn deserialize(raw: &mut Deserializer) -> Result { + skip_set_tag(raw)?; + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + skip_set_tag(raw)?; + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "PlutusScripts")? { + break; + } + arr.push(PlutusScript::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("PlutusScripts"))?; + Ok(Self(arr)) + } +} + +impl PlutusScripts { + pub(crate) fn deserialize_with_version(raw: &mut Deserializer, version: &Language) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + skip_set_tag(raw)?; + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "PlutusScripts")? { + break; + } + arr.push(PlutusScript::deserialize_with_version(raw, version)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("PlutusScripts"))?; + Ok(Self(arr)) + } +} \ No newline at end of file diff --git a/rust/src/serialization/plutus/redeemer.rs b/rust/src/serialization/plutus/redeemer.rs new file mode 100644 index 00000000..522194c1 --- /dev/null +++ b/rust/src/serialization/plutus/redeemer.rs @@ -0,0 +1,128 @@ +use crate::*; +use crate::serialization::utils::check_len_indefinite; + +impl cbor_event::se::Serialize for Redeemer { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + self.serialize_as_array_item(serializer)?; + Ok(serializer) + } +} + +impl Redeemer { + pub(crate) fn serialize_as_array_item<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(Len::Len(4))?; + self.tag.serialize(serializer)?; + self.index.serialize(serializer)?; + self.data.serialize(serializer)?; + self.ex_units.serialize(serializer)?; + Ok(serializer) + } + + pub(crate) fn serialize_as_map_item<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + self.serialize_as_map_key(serializer)?; + self.serialize_as_map_value(serializer) + } + fn serialize_as_map_key<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(Len::Len(2))?; + self.tag.serialize(serializer)?; + self.index.serialize(serializer)?; + Ok(serializer) + } + + fn serialize_as_map_value<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(Len::Len(2))?; + self.data.serialize(serializer)?; + self.ex_units.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for Redeemer { + fn deserialize(raw: &mut Deserializer) -> Result { + Self::deserialize_as_array_item(raw).map_err(|e| e.annotate("Redeemer")) + } +} + +impl Redeemer { + pub(crate) fn deserialize_as_array_item( + raw: &mut Deserializer, + ) -> Result { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(4)?; + let tag = (|| -> Result<_, DeserializeError> { Ok(RedeemerTag::deserialize(raw)?) })() + .map_err(|e| e.annotate("tag"))?; + let index = (|| -> Result<_, DeserializeError> { Ok(BigNum::deserialize(raw)?) })() + .map_err(|e| e.annotate("index"))?; + let data = (|| -> Result<_, DeserializeError> { Ok(PlutusData::deserialize(raw)?) })() + .map_err(|e| e.annotate("data"))?; + let ex_units = (|| -> Result<_, DeserializeError> { Ok(ExUnits::deserialize(raw)?) })() + .map_err(|e| e.annotate("ex_units"))?; + check_len_indefinite(raw, len)?; + Ok(Redeemer { + tag, + index, + data, + ex_units, + }) + } + + pub(crate) fn deserialize_as_map_item( + raw: &mut Deserializer, + ) -> Result { + let (tag, index) = Self::deserialize_map_key(raw)?; + let (data, ex_units) = Self::deserialize_map_value(raw)?; + + Ok(Self { + tag, + index, + data, + ex_units, + }) + } + + fn deserialize_map_key( + raw: &mut Deserializer, + ) -> Result<(RedeemerTag, BigNum), DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + + let tag = RedeemerTag::deserialize(raw)?; + let index = BigNum::deserialize(raw)?; + + check_len_indefinite(raw, len)?; + + Ok((tag, index)) + } + + fn deserialize_map_value( + raw: &mut Deserializer, + ) -> Result<(PlutusData, ExUnits), DeserializeError> { + let len = raw.array()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(2)?; + + let data = PlutusData::deserialize(raw)?; + let ex_units = ExUnits::deserialize(raw)?; + + check_len_indefinite(raw, len)?; + + Ok((data, ex_units)) + } +} \ No newline at end of file diff --git a/rust/src/serialization/plutus/redeemer_tag.rs b/rust/src/serialization/plutus/redeemer_tag.rs new file mode 100644 index 00000000..e48e84af --- /dev/null +++ b/rust/src/serialization/plutus/redeemer_tag.rs @@ -0,0 +1,49 @@ +use crate::*; + +impl cbor_event::se::Serialize for RedeemerTagKind { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + match self { + RedeemerTagKind::Spend => serializer.write_unsigned_integer(0u64), + RedeemerTagKind::Mint => serializer.write_unsigned_integer(1u64), + RedeemerTagKind::Cert => serializer.write_unsigned_integer(2u64), + RedeemerTagKind::Reward => serializer.write_unsigned_integer(3u64), + RedeemerTagKind::Vote => serializer.write_unsigned_integer(4u64), + RedeemerTagKind::VotingProposal => serializer.write_unsigned_integer(5u64), + } + } +} + +impl Deserialize for RedeemerTagKind { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + match raw.unsigned_integer() { + Ok(0) => Ok(RedeemerTagKind::Spend), + Ok(1) => Ok(RedeemerTagKind::Mint), + Ok(2) => Ok(RedeemerTagKind::Cert), + Ok(3) => Ok(RedeemerTagKind::Reward), + Ok(4) => Ok(RedeemerTagKind::Vote), + Ok(5) => Ok(RedeemerTagKind::VotingProposal), + Ok(_) | Err(_) => Err(DeserializeFailure::NoVariantMatched.into()), + } + })() + .map_err(|e| e.annotate("RedeemerTagEnum")) + } +} + +impl cbor_event::se::Serialize for RedeemerTag { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + self.0.serialize(serializer) + } +} + +impl Deserialize for RedeemerTag { + fn deserialize(raw: &mut Deserializer) -> Result { + Ok(Self(RedeemerTagKind::deserialize(raw)?)) + } +} \ No newline at end of file diff --git a/rust/src/serialization/plutus/redeemers.rs b/rust/src/serialization/plutus/redeemers.rs new file mode 100644 index 00000000..1dc73c12 --- /dev/null +++ b/rust/src/serialization/plutus/redeemers.rs @@ -0,0 +1,76 @@ +use crate::*; +use crate::serialization::utils::is_break_tag; + +impl cbor_event::se::Serialize for Redeemers { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + match self.serialization_format { + Some(CborContainerType::Map) => { + serializer.write_map(Len::Len(self.redeemers.len() as u64))?; + for element in &self.redeemers { + element.serialize_as_map_item(serializer)?; + } + } + _ => { + serializer.write_array(Len::Len(self.redeemers.len() as u64))?; + for element in &self.redeemers { + element.serialize_as_array_item(serializer)?; + } + } + } + Ok(serializer) + } +} + +impl Deserialize for Redeemers { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result { + let cbor_type = raw.cbor_type()?; + match cbor_type { + cbor_event::Type::Array => Self::deserialize_as_array(raw), + cbor_event::Type::Map => Self::deserialize_as_map(raw), + _ => return Err(DeserializeFailure::ExpectedType("Array or Map".to_string(), cbor_type).into()), + } + })().map_err(|e| e.annotate("Redeemers")) + } +} + +impl Redeemers { + fn deserialize_as_map(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + let len = raw.map()?; + while match len { + Len::Len(n) => arr.len() < n as usize, + Len::Indefinite => true, + } { + if is_break_tag(raw, "Redeemers")? { + break; + } + arr.push(Redeemer::deserialize_as_map_item(raw)?); + } + Ok(Self { + redeemers: arr, + serialization_format: Some(CborContainerType::Map), + }) + } + + fn deserialize_as_array(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + let len = raw.array()?; + while match len { + Len::Len(n) => arr.len() < n as usize, + Len::Indefinite => true, + } { + if is_break_tag(raw, "Redeemers")? { + break; + } + arr.push(Redeemer::deserialize_as_array_item(raw)?); + } + Ok(Self { + redeemers: arr, + serialization_format: Some(CborContainerType::Array), + }) + } +} \ No newline at end of file diff --git a/rust/src/serialization/plutus/strings.rs b/rust/src/serialization/plutus/strings.rs new file mode 100644 index 00000000..5898ad5a --- /dev/null +++ b/rust/src/serialization/plutus/strings.rs @@ -0,0 +1,36 @@ +use crate::*; +use crate::serialization::utils::is_break_tag; + +impl cbor_event::se::Serialize for Strings { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + serializer.write_text(&element)?; + } + Ok(serializer) + } +} + +impl Deserialize for Strings { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "Strings")? { + break; + } + arr.push(String::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("Strings"))?; + Ok(Self(arr)) + } +} \ No newline at end of file diff --git a/rust/src/serialization/protocol_param_update.rs b/rust/src/serialization/protocol_param_update.rs new file mode 100644 index 00000000..03f2facc --- /dev/null +++ b/rust/src/serialization/protocol_param_update.rs @@ -0,0 +1,890 @@ +use crate::serialization::utils::check_len; +use crate::*; + +impl Serialize for PoolVotingThresholds { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(5))?; + self.motion_no_confidence.serialize(serializer)?; + self.committee_normal.serialize(serializer)?; + self.committee_no_confidence.serialize(serializer)?; + self.hard_fork_initiation.serialize(serializer)?; + self.security_relevant_threshold.serialize(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(PoolVotingThresholds); + +impl DeserializeEmbeddedGroup for PoolVotingThresholds { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len( + len, + 5, + "[\ + motion_no_confidence, \ + committee_normal, \ + committee_no_confidence, \ + hard_fork_initiation, \ + security_relevant_threshold\ + ]", + )?; + + let motion_no_confidence = + UnitInterval::deserialize(raw).map_err(|e| e.annotate("motion_no_confidence"))?; + let committee_normal = + UnitInterval::deserialize(raw).map_err(|e| e.annotate("committee_normal"))?; + let committee_no_confidence = + UnitInterval::deserialize(raw).map_err(|e| e.annotate("committee_no_confidence"))?; + let hard_fork_initiation = + UnitInterval::deserialize(raw).map_err(|e| e.annotate("hard_fork_initiation"))?; + let security_relevant_threshold = UnitInterval::deserialize(raw) + .map_err(|e| e.annotate("security_relevant_threshold"))?; + + return Ok(PoolVotingThresholds { + motion_no_confidence, + committee_normal, + committee_no_confidence, + hard_fork_initiation, + security_relevant_threshold, + }); + } +} + +impl Serialize for DRepVotingThresholds { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(10))?; + self.motion_no_confidence.serialize(serializer)?; + self.committee_normal.serialize(serializer)?; + self.committee_no_confidence.serialize(serializer)?; + self.update_constitution.serialize(serializer)?; + self.hard_fork_initiation.serialize(serializer)?; + self.pp_network_group.serialize(serializer)?; + self.pp_economic_group.serialize(serializer)?; + self.pp_technical_group.serialize(serializer)?; + self.pp_governance_group.serialize(serializer)?; + self.treasury_withdrawal.serialize(serializer) + } +} + +impl_deserialize_for_wrapped_tuple!(DRepVotingThresholds); + +impl DeserializeEmbeddedGroup for DRepVotingThresholds { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result { + check_len( + len, + 10, + "[\ + motion_no_confidence, \ + committee_normal, \ + committee_no_confidence, \ + update_constitution, \ + hard_fork_initiation, \ + pp_network_group, \ + pp_economic_group, \ + pp_technical_group, \ + pp_governance_group, \ + treasury_withdrawal\ + ]", + )?; + + let motion_no_confidence = + UnitInterval::deserialize(raw).map_err(|e| e.annotate("motion_no_confidence"))?; + let committee_normal = + UnitInterval::deserialize(raw).map_err(|e| e.annotate("committee_normal"))?; + let committee_no_confidence = + UnitInterval::deserialize(raw).map_err(|e| e.annotate("committee_no_confidence"))?; + let update_constitution = + UnitInterval::deserialize(raw).map_err(|e| e.annotate("update_constitution"))?; + let hard_fork_initiation = + UnitInterval::deserialize(raw).map_err(|e| e.annotate("hard_fork_initiation"))?; + let pp_network_group = + UnitInterval::deserialize(raw).map_err(|e| e.annotate("pp_network_group"))?; + let pp_economic_group = + UnitInterval::deserialize(raw).map_err(|e| e.annotate("pp_economic_group"))?; + let pp_technical_group = + UnitInterval::deserialize(raw).map_err(|e| e.annotate("pp_technical_group"))?; + let pp_governance_group = + UnitInterval::deserialize(raw).map_err(|e| e.annotate("pp_governance_group"))?; + let treasury_withdrawal = + UnitInterval::deserialize(raw).map_err(|e| e.annotate("treasury_withdrawal"))?; + + return Ok(DRepVotingThresholds { + motion_no_confidence, + committee_normal, + committee_no_confidence, + update_constitution, + hard_fork_initiation, + pp_network_group, + pp_economic_group, + pp_technical_group, + pp_governance_group, + treasury_withdrawal, + }); + } +} + +impl cbor_event::se::Serialize for ProtocolParamUpdate { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len( + match &self.minfee_a { + Some(_) => 1, + None => 0, + } + match &self.minfee_b { + Some(_) => 1, + None => 0, + } + match &self.max_block_body_size { + Some(_) => 1, + None => 0, + } + match &self.max_tx_size { + Some(_) => 1, + None => 0, + } + match &self.max_block_header_size { + Some(_) => 1, + None => 0, + } + match &self.key_deposit { + Some(_) => 1, + None => 0, + } + match &self.pool_deposit { + Some(_) => 1, + None => 0, + } + match &self.max_epoch { + Some(_) => 1, + None => 0, + } + match &self.n_opt { + Some(_) => 1, + None => 0, + } + match &self.pool_pledge_influence { + Some(_) => 1, + None => 0, + } + match &self.expansion_rate { + Some(_) => 1, + None => 0, + } + match &self.treasury_growth_rate { + Some(_) => 1, + None => 0, + } + match &self.d { + Some(_) => 1, + None => 0, + } + match &self.extra_entropy { + Some(_) => 1, + None => 0, + } + match &self.protocol_version { + Some(_) => 1, + None => 0, + } + match &self.min_pool_cost { + Some(_) => 1, + None => 0, + } + match &self.ada_per_utxo_byte { + Some(_) => 1, + None => 0, + } + match &self.cost_models { + Some(_) => 1, + None => 0, + } + match &self.execution_costs { + Some(_) => 1, + None => 0, + } + match &self.max_tx_ex_units { + Some(_) => 1, + None => 0, + } + match &self.max_block_ex_units { + Some(_) => 1, + None => 0, + } + match &self.max_value_size { + Some(_) => 1, + None => 0, + } + match &self.collateral_percentage { + Some(_) => 1, + None => 0, + } + match &self.max_collateral_inputs { + Some(_) => 1, + None => 0, + } + match &self.pool_voting_thresholds { + Some(_) => 1, + None => 0, + } + match &self.drep_voting_thresholds { + Some(_) => 1, + None => 0, + } + match &self.min_committee_size { + Some(_) => 1, + None => 0, + } + match &self.committee_term_limit { + Some(_) => 1, + None => 0, + } + match &self.governance_action_validity_period { + Some(_) => 1, + None => 0, + } + match &self.governance_action_deposit { + Some(_) => 1, + None => 0, + } + match &self.drep_deposit { + Some(_) => 1, + None => 0, + } + match &self.drep_inactivity_period { + Some(_) => 1, + None => 0, + } + match &self.ref_script_coins_per_byte { + Some(_) => 1, + None => 0, + } + ))?; + if let Some(field) = &self.minfee_a { + serializer.write_unsigned_integer(0)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.minfee_b { + serializer.write_unsigned_integer(1)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.max_block_body_size { + serializer.write_unsigned_integer(2)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.max_tx_size { + serializer.write_unsigned_integer(3)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.max_block_header_size { + serializer.write_unsigned_integer(4)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.key_deposit { + serializer.write_unsigned_integer(5)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.pool_deposit { + serializer.write_unsigned_integer(6)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.max_epoch { + serializer.write_unsigned_integer(7)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.n_opt { + serializer.write_unsigned_integer(8)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.pool_pledge_influence { + serializer.write_unsigned_integer(9)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.expansion_rate { + serializer.write_unsigned_integer(10)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.treasury_growth_rate { + serializer.write_unsigned_integer(11)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.d { + serializer.write_unsigned_integer(12)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.extra_entropy { + serializer.write_unsigned_integer(13)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.protocol_version { + serializer.write_unsigned_integer(14)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.min_pool_cost { + serializer.write_unsigned_integer(16)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.ada_per_utxo_byte { + serializer.write_unsigned_integer(17)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.cost_models { + serializer.write_unsigned_integer(18)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.execution_costs { + serializer.write_unsigned_integer(19)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.max_tx_ex_units { + serializer.write_unsigned_integer(20)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.max_block_ex_units { + serializer.write_unsigned_integer(21)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.max_value_size { + serializer.write_unsigned_integer(22)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.collateral_percentage { + serializer.write_unsigned_integer(23)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.max_collateral_inputs { + serializer.write_unsigned_integer(24)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.pool_voting_thresholds { + serializer.write_unsigned_integer(25)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.drep_voting_thresholds { + serializer.write_unsigned_integer(26)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.min_committee_size { + serializer.write_unsigned_integer(27)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.committee_term_limit { + serializer.write_unsigned_integer(28)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.governance_action_validity_period { + serializer.write_unsigned_integer(29)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.governance_action_deposit { + serializer.write_unsigned_integer(30)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.drep_deposit { + serializer.write_unsigned_integer(31)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.drep_inactivity_period { + serializer.write_unsigned_integer(32)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.ref_script_coins_per_byte { + serializer.write_unsigned_integer(33)?; + field.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for ProtocolParamUpdate { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + let mut read_len = CBORReadLen::new(len); + let mut minfee_a = None; + let mut minfee_b = None; + let mut max_block_body_size = None; + let mut max_tx_size = None; + let mut max_block_header_size = None; + let mut key_deposit = None; + let mut pool_deposit = None; + let mut max_epoch = None; + let mut n_opt = None; + let mut pool_pledge_influence = None; + let mut expansion_rate = None; + let mut treasury_growth_rate = None; + let mut d = None; + let mut extra_entropy = None; + let mut protocol_version = None; + let mut min_pool_cost = None; + let mut ada_per_utxo_byte = None; + let mut cost_models = None; + let mut execution_costs = None; + let mut max_tx_ex_units = None; + let mut max_block_ex_units = None; + let mut max_value_size = None; + let mut collateral_percentage = None; + let mut max_collateral_inputs = None; + let mut pool_voting_thresholds = None; + let mut drep_voting_thresholds = None; + let mut min_committee_size = None; + let mut committee_term_limit = None; + let mut governance_action_validity_period = None; + let mut governance_action_deposit = None; + let mut drep_deposit = None; + let mut drep_inactivity_period = None; + let mut ref_script_coins_per_byte = None; + + let mut read = 0; + while match len { + cbor_event::Len::Len(n) => read < n as usize, + cbor_event::Len::Indefinite => true, + } { + match raw.cbor_type()? { + CBORType::UnsignedInteger => match raw.unsigned_integer()? { + 0 => { + if minfee_a.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); + } + minfee_a = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })() + .map_err(|e| e.annotate("minfee_a"))?, + ); + } + 1 => { + if minfee_b.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); + } + minfee_b = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })() + .map_err(|e| e.annotate("minfee_b"))?, + ); + } + 2 => { + if max_block_body_size.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); + } + max_block_body_size = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })() + .map_err(|e| e.annotate("max_block_body_size"))?, + ); + } + 3 => { + if max_tx_size.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); + } + max_tx_size = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })() + .map_err(|e| e.annotate("max_tx_size"))?, + ); + } + 4 => { + if max_block_header_size.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(4)).into()); + } + max_block_header_size = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })() + .map_err(|e| e.annotate("max_block_header_size"))?, + ); + } + 5 => { + if key_deposit.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(5)).into()); + } + key_deposit = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })() + .map_err(|e| e.annotate("key_deposit"))?, + ); + } + 6 => { + if pool_deposit.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(6)).into()); + } + pool_deposit = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })() + .map_err(|e| e.annotate("pool_deposit"))?, + ); + } + 7 => { + if max_epoch.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(7)).into()); + } + max_epoch = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Epoch::deserialize(raw)?) + })() + .map_err(|e| e.annotate("max_epoch"))?, + ); + } + 8 => { + if n_opt.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(8)).into()); + } + n_opt = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })() + .map_err(|e| e.annotate("n_opt"))?, + ); + } + 9 => { + if pool_pledge_influence.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(9)).into()); + } + pool_pledge_influence = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(UnitInterval::deserialize(raw)?) + })() + .map_err(|e| e.annotate("pool_pledge_influence"))?, + ); + } + 10 => { + if expansion_rate.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(10)).into()); + } + expansion_rate = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(UnitInterval::deserialize(raw)?) + })() + .map_err(|e| e.annotate("expansion_rate"))?, + ); + } + 11 => { + if treasury_growth_rate.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(11)).into()); + } + treasury_growth_rate = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(UnitInterval::deserialize(raw)?) + })() + .map_err(|e| e.annotate("treasury_growth_rate"))?, + ); + } + 12 => { + if d.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(12)).into()); + } + d = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(UnitInterval::deserialize(raw)?) + })() + .map_err(|e| e.annotate("d"))?, + ); + } + 13 => { + if extra_entropy.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(13)).into()); + } + extra_entropy = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Nonce::deserialize(raw)?) + })() + .map_err(|e| e.annotate("extra_entropy"))?, + ); + } + 14 => { + if protocol_version.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(14)).into()); + } + protocol_version = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ProtocolVersion::deserialize(raw)?) + })() + .map_err(|e| e.annotate("protocol_version"))?, + ); + } + 16 => { + if min_pool_cost.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(16)).into()); + } + min_pool_cost = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })() + .map_err(|e| e.annotate("min_pool_cost"))?, + ); + } + 17 => { + if ada_per_utxo_byte.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(17)).into()); + } + ada_per_utxo_byte = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })() + .map_err(|e| e.annotate("ada_per_utxo_byte"))?, + ); + } + 18 => { + if cost_models.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(18)).into()); + } + cost_models = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Costmdls::deserialize(raw)?) + })() + .map_err(|e| e.annotate("cost_models"))?, + ); + } + 19 => { + if execution_costs.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(19)).into()); + } + execution_costs = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ExUnitPrices::deserialize(raw)?) + })() + .map_err(|e| e.annotate("execution_costs"))?, + ); + } + 20 => { + if max_tx_ex_units.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(20)).into()); + } + max_tx_ex_units = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ExUnits::deserialize(raw)?) + })() + .map_err(|e| e.annotate("max_tx_ex_units"))?, + ); + } + 21 => { + if max_block_ex_units.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(21)).into()); + } + max_block_ex_units = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ExUnits::deserialize(raw)?) + })() + .map_err(|e| e.annotate("max_block_ex_units"))?, + ); + } + 22 => { + if max_value_size.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(22)).into()); + } + max_value_size = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })() + .map_err(|e| e.annotate("max_value_size"))?, + ); + } + 23 => { + if collateral_percentage.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(23)).into()); + } + collateral_percentage = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })() + .map_err(|e| e.annotate("collateral_percentage"))?, + ); + } + 24 => { + if max_collateral_inputs.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(24)).into()); + } + max_collateral_inputs = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })() + .map_err(|e| e.annotate("max_collateral_inputs"))?, + ); + } + 25 => { + if pool_voting_thresholds.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(25)).into()); + } + pool_voting_thresholds = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(PoolVotingThresholds::deserialize(raw)?) + })() + .map_err(|e| e.annotate("pool_voting_thresholds"))?, + ); + } + 26 => { + if drep_voting_thresholds.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(26)).into()); + } + drep_voting_thresholds = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(DRepVotingThresholds::deserialize(raw)?) + })() + .map_err(|e| e.annotate("drep_voting_thresholds"))?, + ); + } + 27 => { + if min_committee_size.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(27)).into()); + } + min_committee_size = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(u32::deserialize(raw)?) + })() + .map_err(|e| e.annotate("min_committee_size"))?, + ); + } + 28 => { + if committee_term_limit.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(28)).into()); + } + committee_term_limit = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Epoch::deserialize(raw)?) + })() + .map_err(|e| e.annotate("committee_term_limit"))?, + ); + } + 29 => { + if governance_action_validity_period.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(29)).into()); + } + governance_action_validity_period = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Epoch::deserialize(raw)?) + })() + .map_err(|e| e.annotate("governance_action_validity_period"))?, + ); + } + 30 => { + if governance_action_deposit.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(30)).into()); + } + governance_action_deposit = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })() + .map_err(|e| e.annotate("governance_action_deposit"))?, + ); + } + 31 => { + if drep_deposit.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(31)).into()); + } + drep_deposit = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })() + .map_err(|e| e.annotate("drep_deposit"))?, + ); + } + 32 => { + if drep_inactivity_period.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(32)).into()); + } + drep_inactivity_period = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Epoch::deserialize(raw)?) + })() + .map_err(|e| e.annotate("drep_inactivity_period"))?, + ); + } + 33 => { + if ref_script_coins_per_byte.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(33)).into()); + } + ref_script_coins_per_byte = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(UnitInterval::deserialize(raw)?) + })() + .map_err(|e| e.annotate("ref_script_coins_per_byte"))?, + ); + } + unknown_key => { + return Err( + DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into() + ) + } + }, + CBORType::Text => match raw.text()?.as_str() { + unknown_key => { + return Err(DeserializeFailure::UnknownKey(Key::Str( + unknown_key.to_owned(), + )) + .into()) + } + }, + CBORType::Special => match len { + cbor_event::Len::Len(_) => { + return Err(DeserializeFailure::BreakInDefiniteLen.into()) + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => break, + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + }, + other_type => { + return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()) + } + } + read += 1; + } + read_len.finish()?; + Ok(Self { + minfee_a, + minfee_b, + max_block_body_size, + max_tx_size, + max_block_header_size, + key_deposit, + pool_deposit, + max_epoch, + n_opt, + pool_pledge_influence, + expansion_rate, + treasury_growth_rate, + d, + extra_entropy, + protocol_version, + min_pool_cost, + ada_per_utxo_byte, + cost_models, + execution_costs, + max_tx_ex_units, + max_block_ex_units, + max_value_size, + collateral_percentage, + max_collateral_inputs, + pool_voting_thresholds, + drep_voting_thresholds, + min_committee_size, + committee_term_limit, + governance_action_validity_period, + governance_action_deposit, + drep_deposit, + drep_inactivity_period, + ref_script_coins_per_byte, + }) + })() + .map_err(|e| e.annotate("ProtocolParamUpdate")) + } +} diff --git a/rust/src/serialization/script_ref.rs b/rust/src/serialization/script_ref.rs new file mode 100644 index 00000000..eba19307 --- /dev/null +++ b/rust/src/serialization/script_ref.rs @@ -0,0 +1,93 @@ +use crate::*; + +impl Deserialize for ScriptRefEnum { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + if let cbor_event::Len::Len(n) = len { + if n != 2 { + return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( + 2, + len, + "[id, native_or_putus_script]", + )) + .into()); + } + } + let script_ref = match raw.unsigned_integer()? { + 0 => ScriptRefEnum::NativeScript(NativeScript::deserialize(raw)?), + 1 => ScriptRefEnum::PlutusScript(PlutusScript::deserialize(raw)?), + 2 => ScriptRefEnum::PlutusScript( + PlutusScript::deserialize(raw)?.clone_as_version(&Language::new_plutus_v2()), + ), + 3 => ScriptRefEnum::PlutusScript( + PlutusScript::deserialize(raw)?.clone_as_version(&Language::new_plutus_v3()), + ), + n => { + return Err(DeserializeFailure::FixedValueMismatch { + found: Key::Uint(n), + expected: Key::Uint(0), + } + .into()) + } + }; + if let cbor_event::Len::Indefinite = len { + if raw.special()? != CBORSpecial::Break { + return Err(DeserializeFailure::EndingBreakMissing.into()); + } + } + Ok(script_ref) + })() + .map_err(|e| e.annotate("ScriptRefEnum")) + } +} + +impl cbor_event::se::Serialize for ScriptRefEnum { + fn serialize<'a, W: Write + Sized>( + &self, + serializer: &'a mut Serializer, + ) -> cbor_event::Result<&'a mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + match &self { + ScriptRefEnum::NativeScript(native_script) => { + serializer.write_unsigned_integer(0)?; + native_script.serialize(serializer)?; + } + ScriptRefEnum::PlutusScript(plutus_script) => { + serializer.write_unsigned_integer(plutus_script.script_namespace() as u64)?; + plutus_script.serialize(serializer)?; + } + } + Ok(serializer) + } +} + +impl Deserialize for ScriptRef { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + match raw.tag()? { + //bytes string tag + 24 => Ok(ScriptRef(from_bytes(&raw.bytes()?)?)), + tag => { + return Err(DeserializeFailure::TagMismatch { + found: tag, + expected: 24, + } + .into()); + } + } + })() + .map_err(|e| e.annotate("ScriptRef")) + } +} + +impl cbor_event::se::Serialize for ScriptRef { + fn serialize<'a, W: Write + Sized>( + &self, + serializer: &'a mut Serializer, + ) -> cbor_event::Result<&'a mut Serializer> { + let bytes = to_bytes(&self.0); + serializer.write_tag(24)?.write_bytes(&bytes)?; + Ok(serializer) + } +} \ No newline at end of file diff --git a/rust/src/serialization/ser_info/mod.rs b/rust/src/serialization/ser_info/mod.rs new file mode 100644 index 00000000..fe642b39 --- /dev/null +++ b/rust/src/serialization/ser_info/mod.rs @@ -0,0 +1,2 @@ +mod types; +pub use types::*; diff --git a/rust/src/ser_info/types.rs b/rust/src/serialization/ser_info/types.rs similarity index 98% rename from rust/src/ser_info/types.rs rename to rust/src/serialization/ser_info/types.rs index 8da2707d..30913ad0 100644 --- a/rust/src/ser_info/types.rs +++ b/rust/src/serialization/ser_info/types.rs @@ -5,4 +5,4 @@ use crate::*; pub enum CborContainerType { Array = 0, Map = 1, -} \ No newline at end of file +} diff --git a/rust/src/serialization_macros.rs b/rust/src/serialization/serialization_macros.rs similarity index 86% rename from rust/src/serialization_macros.rs rename to rust/src/serialization/serialization_macros.rs index fa0ba8e0..6d7623bc 100644 --- a/rust/src/serialization_macros.rs +++ b/rust/src/serialization/serialization_macros.rs @@ -134,4 +134,27 @@ macro_rules! impl_to_from { to_from_bytes!($name); to_from_json!($name); }; -} \ No newline at end of file +} + +#[macro_export] +macro_rules! impl_deserialize_for_wrapped_tuple { + ($type:ty) => { + impl Deserialize for $type { + fn deserialize( + raw: &mut Deserializer, + ) -> Result { + (|| -> Result<_, DeserializeError> { + use crate::serialization::utils::check_len_indefinite; + let len = raw.array()?; + + let inner_struct = Self::deserialize_as_embedded_group(raw, len)?; + + check_len_indefinite(raw, len)?; + + Ok(inner_struct) + })() + .map_err(|e| e.annotate(stringify!($type))) + } + } + }; +} diff --git a/rust/src/serialization/traits.rs b/rust/src/serialization/traits.rs new file mode 100644 index 00000000..207ef11a --- /dev/null +++ b/rust/src/serialization/traits.rs @@ -0,0 +1,79 @@ +use crate::*; +// we use the cbor_event::Serialize trait directly + +// This is only for use for plain cddl groups who need to be embedded within outer groups. +pub(crate) trait SerializeEmbeddedGroup { + fn serialize_as_embedded_group<'a, W: Write + Sized>( + &self, + serializer: &'a mut Serializer, + ) -> cbor_event::Result<&'a mut Serializer>; +} + +// same as cbor_event::de::Deserialize but with our DeserializeError +pub trait Deserialize { + fn deserialize(raw: &mut Deserializer) -> Result + where + Self: Sized; +} + +// auto-implement for all cbor_event Deserialize implementors +impl Deserialize for T { + fn deserialize(raw: &mut Deserializer) -> Result { + T::deserialize(raw).map_err(|e| DeserializeError::from(e)) + } +} + +// This is only for use for plain cddl groups who need to be embedded within outer groups. +pub trait DeserializeEmbeddedGroup { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + len: cbor_event::Len, + ) -> Result + where + Self: Sized; +} + +pub trait DeserializeNullable { + fn deserialize_nullable( + raw: &mut Deserializer, + ) -> Result, DeserializeError> + where + Self: Sized; +} + +impl DeserializeNullable for T { + fn deserialize_nullable( + raw: &mut Deserializer, + ) -> Result, DeserializeError> + where + Self: Sized, + { + if raw.cbor_type()? == CBORType::Special { + if raw.special()? != CBORSpecial::Null { + return Err(DeserializeFailure::ExpectedNull.into()); + } + Ok(None) + } else { + Ok(Some(T::deserialize(raw)?)) + } + } +} + +pub trait SerializeNullable { + fn serialize_nullable<'a, W: Write + Sized>( + &self, + serializer: &'a mut Serializer, + ) -> cbor_event::Result<&'a mut Serializer>; +} + +impl SerializeNullable for Option { + fn serialize_nullable<'a, W: Write + Sized>( + &self, + serializer: &'a mut Serializer, + ) -> cbor_event::Result<&'a mut Serializer> { + match self { + Some(x) => x.serialize(serializer), + None => serializer.write_special(CBORSpecial::Null), + } + } +} diff --git a/rust/src/serialization/transaction_body.rs b/rust/src/serialization/transaction_body.rs new file mode 100644 index 00000000..eb28a673 --- /dev/null +++ b/rust/src/serialization/transaction_body.rs @@ -0,0 +1,478 @@ +use crate::*; + +impl cbor_event::se::Serialize for TransactionBody { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_map(cbor_event::Len::Len( + 3 + opt64(&self.ttl) + + opt64_non_empty(&self.certs) + + opt64_non_empty(&self.withdrawals) + + opt64(&self.update) + + opt64(&self.auxiliary_data_hash) + + opt64(&self.validity_start_interval) + + opt64_non_empty(&self.mint) + + opt64(&self.script_data_hash) + + opt64_non_empty(&self.collateral) + + opt64_non_empty(&self.required_signers) + + opt64(&self.network_id) + + opt64(&self.collateral_return) + + opt64(&self.total_collateral) + + opt64_non_empty(&self.reference_inputs) + + opt64_non_empty(&self.voting_procedures) + + opt64_non_empty(&self.voting_proposals) + + opt64(&self.donation) + + opt64(&self.current_treasury_value), + ))?; + serializer.write_unsigned_integer(0)?; + self.inputs.serialize(serializer)?; + serializer.write_unsigned_integer(1)?; + self.outputs.serialize(serializer)?; + serializer.write_unsigned_integer(2)?; + self.fee.serialize(serializer)?; + if let Some(field) = &self.ttl { + serializer.write_unsigned_integer(3)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.certs { + if !field.is_none_or_empty() { + serializer.write_unsigned_integer(4)?; + field.serialize(serializer)?; + } + } + if let Some(field) = &self.withdrawals { + if !field.is_none_or_empty() { + serializer.write_unsigned_integer(5)?; + field.serialize(serializer)?; + } + } + if let Some(field) = &self.update { + serializer.write_unsigned_integer(6)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.auxiliary_data_hash { + serializer.write_unsigned_integer(7)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.validity_start_interval { + serializer.write_unsigned_integer(8)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.mint { + if !field.is_none_or_empty() { + serializer.write_unsigned_integer(9)?; + field.serialize(serializer)?; + } + } + if let Some(field) = &self.script_data_hash { + serializer.write_unsigned_integer(11)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.collateral { + if !field.is_none_or_empty() { + serializer.write_unsigned_integer(13)?; + field.serialize(serializer)?; + } + } + if let Some(field) = &self.required_signers { + if !field.is_none_or_empty() { + serializer.write_unsigned_integer(14)?; + field.serialize(serializer)?; + } + } + if let Some(field) = &self.network_id { + serializer.write_unsigned_integer(15)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.collateral_return { + serializer.write_unsigned_integer(16)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.total_collateral { + serializer.write_unsigned_integer(17)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.reference_inputs { + if !field.is_none_or_empty() { + serializer.write_unsigned_integer(18)?; + field.serialize(serializer)?; + } + } + if let Some(field) = &self.voting_procedures { + if !field.is_none_or_empty() { + serializer.write_unsigned_integer(19)?; + field.serialize(serializer)?; + } + } + if let Some(field) = &self.voting_proposals { + if !field.is_none_or_empty() { + serializer.write_unsigned_integer(20)?; + field.serialize(serializer)?; + } + } + if let Some(field) = &self.current_treasury_value { + serializer.write_unsigned_integer(21)?; + field.serialize(serializer)?; + } + if let Some(field) = &self.donation { + serializer.write_unsigned_integer(22)?; + field.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for TransactionBody { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + let mut read_len = CBORReadLen::new(len); + read_len.read_elems(3)?; + let mut inputs = None; + let mut outputs = None; + let mut fee = None; + let mut ttl = None; + let mut certs = None; + let mut withdrawals = None; + let mut update = None; + let mut auxiliary_data_hash = None; + let mut validity_start_interval = None; + let mut mint = None; + let mut script_data_hash = None; + let mut collateral = None; + let mut required_signers = None; + let mut network_id = None; + let mut collateral_return = None; + let mut total_collateral = None; + let mut reference_inputs = None; + let mut voting_procedures = None; + let mut voting_proposals = None; + let mut current_treasury_value = None; + let mut donation = None; + let mut read = 0; + while match len { + cbor_event::Len::Len(n) => read < n as usize, + cbor_event::Len::Indefinite => true, + } { + match raw.cbor_type()? { + CBORType::UnsignedInteger => match raw.unsigned_integer()? { + 0 => { + if inputs.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); + } + inputs = Some( + (|| -> Result<_, DeserializeError> { + Ok(TransactionInputs::deserialize(raw)?) + })() + .map_err(|e| e.annotate("inputs"))?, + ); + } + 1 => { + if outputs.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); + } + outputs = Some( + (|| -> Result<_, DeserializeError> { + Ok(TransactionOutputs::deserialize(raw)?) + })() + .map_err(|e| e.annotate("outputs"))?, + ); + } + 2 => { + if fee.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); + } + fee = + Some( + (|| -> Result<_, DeserializeError> { + Ok(Coin::deserialize(raw)?) + })() + .map_err(|e| e.annotate("fee"))?, + ); + } + 3 => { + if ttl.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); + } + ttl = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(SlotBigNum::deserialize(raw)?) + })() + .map_err(|e| e.annotate("ttl"))?, + ); + } + 4 => { + if certs.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(4)).into()); + } + certs = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Certificates::deserialize(raw)?) + })() + .map_err(|e| e.annotate("certs"))?, + ); + } + 5 => { + if withdrawals.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(5)).into()); + } + withdrawals = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Withdrawals::deserialize(raw)?) + })() + .map_err(|e| e.annotate("withdrawals"))?, + ); + } + 6 => { + if update.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(6)).into()); + } + update = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Update::deserialize(raw)?) + })() + .map_err(|e| e.annotate("update"))?, + ); + } + 7 => { + if auxiliary_data_hash.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(7)).into()); + } + auxiliary_data_hash = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(AuxiliaryDataHash::deserialize(raw)?) + })() + .map_err(|e| e.annotate("auxiliary_data_hash"))?, + ); + } + 8 => { + if validity_start_interval.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(8)).into()); + } + validity_start_interval = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(SlotBigNum::deserialize(raw)?) + })() + .map_err(|e| e.annotate("validity_start_interval"))?, + ); + } + 9 => { + if mint.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(9)).into()); + } + mint = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Mint::deserialize(raw)?) + })() + .map_err(|e| e.annotate("mint"))?, + ); + } + 11 => { + if script_data_hash.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(11)).into()); + } + script_data_hash = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(ScriptDataHash::deserialize(raw)?) + })() + .map_err(|e| e.annotate("script_data_hash"))?, + ); + } + 13 => { + if collateral.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(13)).into()); + } + collateral = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(TransactionInputs::deserialize(raw)?) + })() + .map_err(|e| e.annotate("collateral"))?, + ); + } + 14 => { + if required_signers.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(14)).into()); + } + required_signers = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Ed25519KeyHashes::deserialize(raw)?) + })() + .map_err(|e| e.annotate("required_signers"))?, + ); + } + 15 => { + if network_id.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(15)).into()); + } + network_id = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(NetworkId::deserialize(raw)?) + })() + .map_err(|e| e.annotate("network_id"))?, + ); + } + 16 => { + if collateral_return.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(16)).into()); + } + collateral_return = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(TransactionOutput::deserialize(raw)?) + })() + .map_err(|e| e.annotate("collateral_return"))?, + ); + } + 17 => { + if total_collateral.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(17)).into()); + } + total_collateral = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })() + .map_err(|e| e.annotate("total_collateral"))?, + ); + } + 18 => { + if reference_inputs.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(18)).into()); + } + reference_inputs = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(TransactionInputs::deserialize(raw)?) + })() + .map_err(|e| e.annotate("reference_inputs"))?, + ); + } + 19 => { + if voting_procedures.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(19)).into()); + } + voting_procedures = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(VotingProcedures::deserialize(raw)?) + })() + .map_err(|e| e.annotate("voting_procedures"))?, + ); + } + 20 => { + if voting_proposals.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(20)).into()); + } + voting_proposals = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(VotingProposals::deserialize(raw)?) + })() + .map_err(|e| e.annotate("voting_proposals"))?, + ); + } + 21 => { + if current_treasury_value.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(21)).into()); + } + current_treasury_value = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })() + .map_err(|e| e.annotate("current_treasury_value"))?, + ); + } + 22 => { + if donation.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(22)).into()); + } + donation = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Coin::deserialize(raw)?) + })() + .map_err(|e| e.annotate("donation"))?, + ); + } + unknown_key => { + return Err( + DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into() + ) + } + }, + CBORType::Text => match raw.text()?.as_str() { + unknown_key => { + return Err(DeserializeFailure::UnknownKey(Key::Str( + unknown_key.to_owned(), + )) + .into()) + } + }, + CBORType::Special => match len { + cbor_event::Len::Len(_) => { + return Err(DeserializeFailure::BreakInDefiniteLen.into()) + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => break, + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + }, + other_type => { + return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()) + } + } + read += 1; + } + let inputs = match inputs { + Some(x) => x, + None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Uint(0)).into()), + }; + let outputs = match outputs { + Some(x) => x, + None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Uint(1)).into()), + }; + let fee = match fee { + Some(x) => x, + None => return Err(DeserializeFailure::MandatoryFieldMissing(Key::Uint(2)).into()), + }; + read_len.finish()?; + Ok(Self { + inputs, + outputs, + fee, + ttl, + certs, + withdrawals, + update, + auxiliary_data_hash, + validity_start_interval, + mint, + script_data_hash, + collateral, + required_signers, + network_id, + collateral_return, + total_collateral, + reference_inputs, + voting_procedures, + voting_proposals, + donation, + current_treasury_value, + }) + })() + .map_err(|e| e.annotate("TransactionBody")) + } +} \ No newline at end of file diff --git a/rust/src/serialization/tx_input.rs b/rust/src/serialization/tx_input.rs new file mode 100644 index 00000000..f8118acc --- /dev/null +++ b/rust/src/serialization/tx_input.rs @@ -0,0 +1,56 @@ +use crate::*; + +impl cbor_event::se::Serialize for TransactionInput { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.transaction_id.serialize(serializer)?; + self.index.serialize(serializer)?; + Ok(serializer) + } +} + +impl Deserialize for TransactionInput { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("TransactionInput")) + } +} + +impl DeserializeEmbeddedGroup for TransactionInput { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + _: cbor_event::Len, + ) -> Result { + let transaction_id = + (|| -> Result<_, DeserializeError> { Ok(TransactionHash::deserialize(raw)?) })() + .map_err(|e| e.annotate("transaction_id"))?; + let index = (|| -> Result<_, DeserializeError> { Ok(u32::deserialize(raw)?) })() + .map_err(|e| e.annotate("index"))?; + Ok(TransactionInput { + transaction_id, + index, + }) + } +} \ No newline at end of file diff --git a/rust/src/serialization/tx_inputs.rs b/rust/src/serialization/tx_inputs.rs new file mode 100644 index 00000000..7fcc683d --- /dev/null +++ b/rust/src/serialization/tx_inputs.rs @@ -0,0 +1,39 @@ +use crate::*; +use crate::serialization::utils::{is_break_tag, skip_set_tag}; + +impl cbor_event::se::Serialize for TransactionInputs { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + //TODO: uncomment this line when we conway ero will come + //serializer.write_tag(258)?; + serializer.write_array(cbor_event::Len::Len(self.len() as u64))?; + for element in &self.inputs { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for TransactionInputs { + fn deserialize(raw: &mut Deserializer) -> Result { + skip_set_tag(raw)?; + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "TransactionInputs")? { + break; + } + arr.push(TransactionInput::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("TransactionInputs"))?; + Ok(Self::from_vec(arr)) + } +} \ No newline at end of file diff --git a/rust/src/serialization/utils.rs b/rust/src/serialization/utils.rs new file mode 100644 index 00000000..8b9b7015 --- /dev/null +++ b/rust/src/serialization/utils.rs @@ -0,0 +1,166 @@ +use std::io::SeekFrom; +use crate::*; + +/// TODO: this function can be removed in case `cbor_event` library ever gets a fix on their side +/// See https://github.com/Emurgo/cardano-serialization-lib/pull/392 +pub(crate) fn read_nint(raw: &mut Deserializer) -> Result { + let found = raw.cbor_type()?; + if found != cbor_event::Type::NegativeInteger { + return Err(cbor_event::Error::Expected(cbor_event::Type::NegativeInteger, found).into()); + } + let (len, len_sz) = raw.cbor_len()?; + match len { + cbor_event::Len::Indefinite => Err(cbor_event::Error::IndefiniteLenNotSupported( + cbor_event::Type::NegativeInteger, + ) + .into()), + cbor_event::Len::Len(v) => { + raw.advance(1 + len_sz)?; + Ok(-(v as i128) - 1) + } + } +} + +pub(super) fn deserialize_and_check_index( + raw: &mut Deserializer, + desired_index: Option, + name: &'static str, +) -> Result { + let actual_index = raw.unsigned_integer()?; + check_index(actual_index, desired_index, name)?; + Ok(actual_index) +} + +pub(super) fn check_index( + actual_index: u64, + desired_index: Option, + name: &'static str, +) -> Result<(), DeserializeError> { + let desired_index = desired_index + .ok_or(DeserializeFailure::CustomError( + "unknown desired index".to_string(), + )) + .map_err(|e| DeserializeError::from(e))?; + if actual_index != desired_index { + return Err(DeserializeFailure::FixedValueMismatch { + found: Key::Uint(actual_index), + expected: Key::Uint(desired_index), + }) + .map_err(|e| DeserializeError::from(e).annotate(name)); + } + + Ok(()) +} + +pub(super) fn serialize_and_check_index<'se, W: Write>( + serializer: &'se mut Serializer, + index: Option, + name: &'static str, +) -> cbor_event::Result<&'se mut Serializer> { + match index { + Some(index) => serializer.write_unsigned_integer(index), + None => Err(cbor_event::Error::CustomError(format!( + "unknown index of {}", + name + ))), + } +} + +pub(super) fn check_len( + len: cbor_event::Len, + expected: u64, + struct_description: &'static str, +) -> Result<(), DeserializeError> { + if let cbor_event::Len::Len(n) = len { + if n != expected { + return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( + expected as u64, + len, + struct_description, + )) + .into()); + } + } + Ok(()) +} + +pub(super) fn check_len_indefinite( + raw: &mut Deserializer, + len: cbor_event::Len, +) -> Result<(), DeserializeError> { + if let cbor_event::Len::Indefinite = len { + if raw.special()? != CBORSpecial::Break { + return Err(DeserializeFailure::EndingBreakMissing.into()); + } + } + Ok(()) +} + +pub(crate) fn merge_option_plutus_list( + left: Option, + right: Option, +) -> Option { + if let Some(left) = left { + if let Some(right) = right { + return Some(left.merge(&right)); + } else { + return Some(left); + } + } else { + return right; + } +} + +pub(super) fn skip_tag( + raw: &mut Deserializer, + tag: u64, +) -> Result<(), DeserializeError> { + if let Ok(extracted_tag) = raw.tag() { + if extracted_tag != tag { + return Err(DeserializeError::new( + "skip_tag", + DeserializeFailure::TagMismatch { + found: extracted_tag, + expected: tag, + }, + )); + } + return Ok(()); + } + Ok(()) +} + +pub(super) fn skip_set_tag( + raw: &mut Deserializer, +) -> Result<(), DeserializeError> { + skip_tag(raw, 258) +} + +pub(crate) fn is_break_tag( + raw: &mut Deserializer, + location: &str, +) -> Result { + if raw.cbor_type()? == CBORType::Special { + if raw.special()? == CBORSpecial::Break { + return Ok(true); + } + return Err( + DeserializeError::from(DeserializeFailure::EndingBreakMissing).annotate(location), + ); + } + Ok(false) +} + +pub(crate) fn deserilized_with_orig_bytes( + raw: &mut Deserializer, + deserializer: fn(&mut Deserializer) -> Result, +) -> Result<(T, Vec), DeserializeError> { + let before = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + let value = deserializer(raw)?; + let after = raw.as_mut_ref().seek(SeekFrom::Current(0)).unwrap(); + let bytes_read = (after - before) as usize; + raw.as_mut_ref().seek(SeekFrom::Start(before)).unwrap(); + let original_bytes = raw.as_mut_ref().fill_buf().unwrap()[..bytes_read].to_vec(); + raw.as_mut_ref().seek(SeekFrom::Start(after)).unwrap(); + Ok((value, original_bytes)) +} \ No newline at end of file diff --git a/rust/src/serialization/witnesses/bootstrap_witness.rs b/rust/src/serialization/witnesses/bootstrap_witness.rs new file mode 100644 index 00000000..291f841a --- /dev/null +++ b/rust/src/serialization/witnesses/bootstrap_witness.rs @@ -0,0 +1,68 @@ +use std::io::{BufRead, Seek, Write}; +use cbor_event::de::Deserializer; +use cbor_event::se::Serializer; +use crate::{BootstrapWitness, DeserializeError, DeserializeFailure, Ed25519Signature, Vkey}; +use crate::protocol_types::{CBORSpecial, Deserialize, DeserializeEmbeddedGroup}; + +impl cbor_event::se::Serialize for BootstrapWitness { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(4))?; + self.vkey.serialize(serializer)?; + self.signature.serialize(serializer)?; + serializer.write_bytes(&self.chain_code)?; + serializer.write_bytes(&self.attributes)?; + Ok(serializer) + } +} + +impl Deserialize for BootstrapWitness { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let ret = Self::deserialize_as_embedded_group(raw, len); + match len { + cbor_event::Len::Len(_) => + /* TODO: check finite len somewhere */ + { + () + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("BootstrapWitness")) + } +} + +impl DeserializeEmbeddedGroup for BootstrapWitness { + fn deserialize_as_embedded_group( + raw: &mut Deserializer, + _: cbor_event::Len, + ) -> Result { + let vkey = (|| -> Result<_, DeserializeError> { Ok(Vkey::deserialize(raw)?) })() + .map_err(|e| e.annotate("vkey"))?; + let signature = + (|| -> Result<_, DeserializeError> { Ok(Ed25519Signature::deserialize(raw)?) })() + .map_err(|e| e.annotate("signature"))?; + let chain_code = (|| -> Result<_, DeserializeError> { Ok(raw.bytes()?) })() + .map_err(|e| e.annotate("chain_code"))?; + let attributes = (|| -> Result<_, DeserializeError> { Ok(raw.bytes()?) })() + .map_err(|e| e.annotate("attributes"))?; + Ok(BootstrapWitness { + vkey, + signature, + chain_code, + attributes, + }) + } +} \ No newline at end of file diff --git a/rust/src/serialization/witnesses/bootstrap_witnesses.rs b/rust/src/serialization/witnesses/bootstrap_witnesses.rs new file mode 100644 index 00000000..d4d4464c --- /dev/null +++ b/rust/src/serialization/witnesses/bootstrap_witnesses.rs @@ -0,0 +1,44 @@ +use std::io::{BufRead, Seek, Write}; +use cbor_event::de::Deserializer; +use cbor_event::se::Serializer; +use crate::{BootstrapWitness, BootstrapWitnesses, DeserializeError}; +use crate::protocol_types::Deserialize; +use crate::serialization::utils::skip_set_tag; + +impl cbor_event::se::Serialize for BootstrapWitnesses { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + //TODO: uncomment this line when we conway ero will come + //serializer.write_tag(258)?; + serializer.write_array(cbor_event::Len::Len(self.get_vec_wits().len() as u64))?; + for element in self.get_vec_wits() { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for BootstrapWitnesses { + fn deserialize(raw: &mut Deserializer) -> Result { + skip_set_tag(raw)?; + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if raw.cbor_type()? == cbor_event::Type::Special { + assert_eq!(raw.special()?, cbor_event::Special::Break); + break; + } + arr.push(BootstrapWitness::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("BootstrapWitnesses"))?; + Ok(Self::from_vec_wits(arr)) + } +} \ No newline at end of file diff --git a/rust/src/serialization/witnesses/mod.rs b/rust/src/serialization/witnesses/mod.rs new file mode 100644 index 00000000..f23c6da7 --- /dev/null +++ b/rust/src/serialization/witnesses/mod.rs @@ -0,0 +1,6 @@ +mod vkeywitness; +mod vkeywitnesses; +mod bootstrap_witness; +mod bootstrap_witnesses; +mod transaction_witnesses_set; +mod transaction_witnesses_sets; \ No newline at end of file diff --git a/rust/src/serialization/witnesses/transaction_witnesses_set.rs b/rust/src/serialization/witnesses/transaction_witnesses_set.rs new file mode 100644 index 00000000..578fa179 --- /dev/null +++ b/rust/src/serialization/witnesses/transaction_witnesses_set.rs @@ -0,0 +1,249 @@ +use std::io::{BufRead, Seek, Write}; +use cbor_event::de::Deserializer; +use cbor_event::se::Serializer; +use crate::{BootstrapWitnesses, CBORReadLen, DeserializeError, DeserializeFailure, Key, Language, NativeScripts, PlutusList, PlutusScripts, Redeemers, TransactionWitnessSet, Vkeywitnesses}; +use crate::protocol_types::{CBORSpecial, CBORType, Deserialize, opt64}; +use crate::serialization::utils::merge_option_plutus_list; +use crate::traits::NoneOrEmpty; +use crate::utils::opt64_non_empty; + +impl cbor_event::se::Serialize for TransactionWitnessSet { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + let mut has_plutus_v1 = false; + let mut has_plutus_v2 = false; + let mut has_plutus_v3 = false; + let plutus_added_length = match &self.plutus_scripts { + Some(scripts) => { + has_plutus_v1 = scripts.has_version(&Language::new_plutus_v1()); + has_plutus_v2 = scripts.has_version(&Language::new_plutus_v2()); + has_plutus_v3 = scripts.has_version(&Language::new_plutus_v3()); + (has_plutus_v1 as u64) + (has_plutus_v2 as u64) + (has_plutus_v3 as u64) + }, + _ => 0, + }; + serializer.write_map(cbor_event::Len::Len( + opt64(&self.vkeys) + + opt64_non_empty(&self.native_scripts) + + opt64_non_empty(&self.bootstraps) + + opt64_non_empty(&self.plutus_data) + + opt64_non_empty(&self.redeemers) + + plutus_added_length, + ))?; + if let Some(field) = &self.vkeys { + if !field.is_none_or_empty() { + serializer.write_unsigned_integer(0)?; + field.serialize(serializer)?; + } + } + if let Some(field) = &self.native_scripts { + if !field.is_none_or_empty() { + serializer.write_unsigned_integer(1)?; + //transaction witness set already has deduplicated native scripts + field.serialize_as_set(false, serializer)?; + } + } + if let Some(field) = &self.bootstraps { + if !field.is_none_or_empty() { + serializer.write_unsigned_integer(2)?; + field.serialize(serializer)?; + } + } + + //no need deduplication here because transaction witness set already has deduplicated plutus scripts + if let Some(plutus_scripts) = &self.plutus_scripts { + if has_plutus_v1 { + serializer.write_unsigned_integer(3)?; + plutus_scripts.serialize_as_set_by_version(false, &Language::new_plutus_v1(), serializer)?; + } + if has_plutus_v2 { + serializer.write_unsigned_integer(6)?; + plutus_scripts.serialize_as_set_by_version(false, &Language::new_plutus_v2(), serializer)?; + } + if has_plutus_v3 { + serializer.write_unsigned_integer(7)?; + plutus_scripts.serialize_as_set_by_version(false, &Language::new_plutus_v3(), serializer)?; + } + } + if let Some(field) = &self.plutus_data { + if !field.is_none_or_empty() { + serializer.write_unsigned_integer(4)?; + //transaction witness set already has deduplicated plutus data + field.serialize_as_set(false, serializer)?; + } + } + if let Some(field) = &self.redeemers { + if !field.is_none_or_empty() { + serializer.write_unsigned_integer(5)?; + field.serialize(serializer)?; + } + } + Ok(serializer) + } +} + +impl Deserialize for TransactionWitnessSet { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.map()?; + let mut read_len = CBORReadLen::new(len); + let mut vkeys = None; + let mut native_scripts = None; + let mut bootstraps = None; + let mut plutus_scripts_v1 = None; + let mut plutus_scripts_v2 = None; + let mut plutus_scripts_v3 = None; + let mut plutus_data = None; + let mut redeemers = None; + let mut read = 0; + while match len { + cbor_event::Len::Len(n) => read < n as usize, + cbor_event::Len::Indefinite => true, + } { + match raw.cbor_type()? { + CBORType::UnsignedInteger => match raw.unsigned_integer()? { + 0 => { + if vkeys.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(0)).into()); + } + vkeys = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Vkeywitnesses::deserialize(raw)?) + })() + .map_err(|e| e.annotate("vkeys"))?, + ); + } + 1 => { + if native_scripts.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(1)).into()); + } + native_scripts = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(NativeScripts::deserialize(raw)?) + })() + .map_err(|e| e.annotate("native_scripts"))?, + ); + } + 2 => { + if bootstraps.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(2)).into()); + } + bootstraps = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(BootstrapWitnesses::deserialize(raw)?) + })() + .map_err(|e| e.annotate("bootstraps"))?, + ); + } + 3 => { + if plutus_scripts_v1.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(3)).into()); + } + plutus_scripts_v1 = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(PlutusScripts::deserialize(raw)?) + })() + .map_err(|e| e.annotate("plutus_scripts_v1"))?, + ); + } + 4 => { + if plutus_data.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(4)).into()); + } + plutus_data = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(PlutusList::deserialize(raw)?) + })() + .map_err(|e| e.annotate("plutus_data"))?, + ); + } + 5 => { + if redeemers.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(5)).into()); + } + redeemers = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(Redeemers::deserialize(raw)?) + })() + .map_err(|e| e.annotate("redeemers"))?, + ); + } + 6 => { + if plutus_scripts_v2.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(6)).into()); + } + plutus_scripts_v2 = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(PlutusScripts::deserialize_with_version(raw, &Language::new_plutus_v2())?) + })() + .map_err(|e| e.annotate("plutus_scripts_v2"))?, + ); + } + 7 => { + if plutus_scripts_v3.is_some() { + return Err(DeserializeFailure::DuplicateKey(Key::Uint(7)).into()); + } + plutus_scripts_v3 = Some( + (|| -> Result<_, DeserializeError> { + read_len.read_elems(1)?; + Ok(PlutusScripts::deserialize_with_version(raw, &Language::new_plutus_v3())?) + })() + .map_err(|e| e.annotate("plutus_scripts_v3"))?, + ); + } + unknown_key => { + return Err( + DeserializeFailure::UnknownKey(Key::Uint(unknown_key)).into() + ) + } + }, + CBORType::Text => match raw.text()?.as_str() { + unknown_key => { + return Err(DeserializeFailure::UnknownKey(Key::Str( + unknown_key.to_owned(), + )) + .into()) + } + }, + CBORType::Special => match len { + cbor_event::Len::Len(_) => { + return Err(DeserializeFailure::BreakInDefiniteLen.into()) + } + cbor_event::Len::Indefinite => match raw.special()? { + CBORSpecial::Break => break, + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + }, + other_type => { + return Err(DeserializeFailure::UnexpectedKeyType(other_type).into()) + } + } + read += 1; + } + read_len.finish()?; + let mut plutus_scripts = None; + plutus_scripts = merge_option_plutus_list(plutus_scripts, plutus_scripts_v1); + plutus_scripts = merge_option_plutus_list(plutus_scripts, plutus_scripts_v2); + plutus_scripts = merge_option_plutus_list(plutus_scripts, plutus_scripts_v3); + + Ok(Self { + vkeys, + native_scripts, + bootstraps, + plutus_scripts, + plutus_data, + redeemers, + }) + })() + .map_err(|e| e.annotate("TransactionWitnessSet")) + } +} \ No newline at end of file diff --git a/rust/src/serialization/witnesses/transaction_witnesses_sets.rs b/rust/src/serialization/witnesses/transaction_witnesses_sets.rs new file mode 100644 index 00000000..78d08fa4 --- /dev/null +++ b/rust/src/serialization/witnesses/transaction_witnesses_sets.rs @@ -0,0 +1,40 @@ +use std::io::{BufRead, Seek, Write}; +use cbor_event::de::Deserializer; +use cbor_event::se::Serializer; +use crate::{DeserializeError, TransactionWitnessSet, TransactionWitnessSets}; +use crate::protocol_types::Deserialize; +use crate::serialization::utils::is_break_tag; + +impl cbor_event::se::Serialize for TransactionWitnessSets { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(self.0.len() as u64))?; + for element in &self.0 { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for TransactionWitnessSets { + fn deserialize(raw: &mut Deserializer) -> Result { + let mut arr = Vec::new(); + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => arr.len() < n as usize, + cbor_event::Len::Indefinite => true, + } { + if is_break_tag(raw, "TransactionWitnessSets")? { + break; + } + arr.push(TransactionWitnessSet::deserialize(raw)?); + } + Ok(()) + })() + .map_err(|e| e.annotate("TransactionWitnessSets"))?; + Ok(Self(arr)) + } +} \ No newline at end of file diff --git a/rust/src/serialization/witnesses/vkeywitness.rs b/rust/src/serialization/witnesses/vkeywitness.rs new file mode 100644 index 00000000..7891109b --- /dev/null +++ b/rust/src/serialization/witnesses/vkeywitness.rs @@ -0,0 +1,51 @@ +use std::io::{BufRead, Seek, Write}; +use cbor_event::de::Deserializer; +use cbor_event::se::Serializer; +use crate::protocol_types::Deserialize; +use crate::{DeserializeError, DeserializeFailure, Ed25519Signature, Vkey, Vkeywitness}; + +impl cbor_event::se::Serialize for Vkeywitness { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + serializer.write_array(cbor_event::Len::Len(2))?; + self.vkey.serialize(serializer)?; + self.signature.serialize(serializer) + } +} + +impl Deserialize for Vkeywitness { + fn deserialize(raw: &mut Deserializer) -> Result { + (|| -> Result<_, DeserializeError> { + let len = raw.array()?; + let vkey = (|| -> Result<_, DeserializeError> { Ok(Vkey::deserialize(raw)?) })() + .map_err(|e| e.annotate("vkey"))?; + let signature = + (|| -> Result<_, DeserializeError> { Ok(Ed25519Signature::deserialize(raw)?) })() + .map_err(|e| e.annotate("signature"))?; + let ret = Ok(Vkeywitness::new(&vkey, &signature)); + match len { + cbor_event::Len::Len(n) => match n { + 2 => (), + _ => { + return Err(DeserializeFailure::CBOR(cbor_event::Error::WrongLen( + 2, len, "", + )) + .into()) + } + }, + cbor_event::Len::Indefinite => match raw.special()? { + cbor_event::Special::Break => + /* it's ok */ + { + () + } + _ => return Err(DeserializeFailure::EndingBreakMissing.into()), + }, + } + ret + })() + .map_err(|e| e.annotate("Vkeywitness")) + } +} \ No newline at end of file diff --git a/rust/src/serialization/witnesses/vkeywitnesses.rs b/rust/src/serialization/witnesses/vkeywitnesses.rs new file mode 100644 index 00000000..f3190569 --- /dev/null +++ b/rust/src/serialization/witnesses/vkeywitnesses.rs @@ -0,0 +1,47 @@ +use std::io::{BufRead, Seek, Write}; +use cbor_event::de::Deserializer; +use cbor_event::se::Serializer; +use crate::protocol_types::Deserialize; +use crate::{DeserializeError, Vkeywitness, Vkeywitnesses}; +use crate::serialization::utils::skip_set_tag; + +impl cbor_event::se::Serialize for Vkeywitnesses { + fn serialize<'se, W: Write>( + &self, + serializer: &'se mut Serializer, + ) -> cbor_event::Result<&'se mut Serializer> { + //TODO: uncomment this line when we conway ero will come + //serializer.write_tag(258)?; + serializer.write_array(cbor_event::Len::Len(self.witnesses.len() as u64))?; + for element in &self.witnesses { + element.serialize(serializer)?; + } + Ok(serializer) + } +} + +impl Deserialize for Vkeywitnesses { + fn deserialize(raw: &mut Deserializer) -> Result { + skip_set_tag(raw)?; + let mut wits = Vkeywitnesses::new(); + let mut total = 0u64; + (|| -> Result<_, DeserializeError> { + skip_set_tag(raw)?; + let len = raw.array()?; + while match len { + cbor_event::Len::Len(n) => total < n, + cbor_event::Len::Indefinite => true, + } { + if raw.cbor_type()? == cbor_event::Type::Special { + assert_eq!(raw.special()?, cbor_event::Special::Break); + break; + } + wits.add_move(Vkeywitness::deserialize(raw)?); + total += 1; + } + Ok(()) + })() + .map_err(|e| e.annotate("Vkeywitnesses"))?; + Ok(wits) + } +} \ No newline at end of file diff --git a/rust/src/serialization_tools/map_names.rs b/rust/src/serialization_tools/map_names.rs deleted file mode 100644 index 1b4000cf..00000000 --- a/rust/src/serialization_tools/map_names.rs +++ /dev/null @@ -1,44 +0,0 @@ - -#[derive(Eq, Hash, PartialEq, Clone, Debug)] -pub enum TxBodyNames { - Inputs = 0, - Outputs = 1, - Fee = 2, - // Ttl = 3, - // Certs = 4, - // Withdrawals = 5, - // Update = 6, - // AuxiliaryDataHash = 7, - // ValidityStartInterval = 8, - // Mint = 9, - // ScriptDataHash = 11, - // Collateral = 13, - // RequiredSigners = 14, - // NetworkId = 15, - // CollateralReturn = 16, - // TotalCollateral = 17, - // ReferenceInputs = 18, -} - -impl TxBodyNames { - pub fn to_number(&self) -> u64 { - self.clone() as u64 - } -} - -#[derive(Eq, Hash, PartialEq, Clone, Debug)] -pub enum WitnessSetNames { - Vkeys = 0, - // NativeScripts = 1, - Bootstraps = 2, - // PlutusScriptsV1 = 3, - // PlutusData = 4, - // Redeemers = 5, - // PlutusScriptsV2 = 6, -} - -impl WitnessSetNames { - pub fn to_number(&self) -> u64 { - self.clone() as u64 - } -} \ No newline at end of file diff --git a/rust/src/serialization_tools/mod.rs b/rust/src/serialization_tools/mod.rs deleted file mode 100644 index 06cc18f2..00000000 --- a/rust/src/serialization_tools/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod map_names; \ No newline at end of file diff --git a/rust/src/tests/address.rs b/rust/src/tests/address.rs new file mode 100644 index 00000000..b7967058 --- /dev/null +++ b/rust/src/tests/address.rs @@ -0,0 +1,698 @@ +use crate::tests::fakes::{fake_base_address, fake_enterprise_address, fake_malformed_address, fake_pointer_address, fake_reward_address, fake_key_hash}; +use crate::*; + +#[test] +fn variable_nat_encoding() { + let cases = [0u64, 127u64, 128u64, 255u64, 256275757658493284u64]; + for case in cases.iter() { + let encoded = variable_nat_encode(*case); + let decoded = variable_nat_decode(&encoded).unwrap().0; + assert_eq!(*case, decoded); + } +} + +#[test] +fn variable_nat_decode_too_big() { + let too_big = [129, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127]; + assert_eq!(None, variable_nat_decode(&too_big)); +} + +#[test] +fn base_serialize_consistency() { + let base = BaseAddress::new( + 5, + &Credential::from_keyhash(&Ed25519KeyHash::from([23; Ed25519KeyHash::BYTE_COUNT])), + &Credential::from_scripthash(&ScriptHash::from([42; ScriptHash::BYTE_COUNT])), + ); + let addr = base.to_address(); + let addr2 = Address::from_bytes(addr.to_bytes()).unwrap(); + assert_eq!(addr.to_bytes(), addr2.to_bytes()); +} + +#[test] +fn ptr_serialize_consistency() { + let ptr = PointerAddress::new( + 25, + &Credential::from_keyhash(&Ed25519KeyHash::from([23; Ed25519KeyHash::BYTE_COUNT])), + &Pointer::new_pointer(&BigNum(2354556573), &BigNum(127), &BigNum(0)), + ); + let addr = ptr.to_address(); + let addr2 = Address::from_bytes(addr.to_bytes()).unwrap(); + assert_eq!(addr.to_bytes(), addr2.to_bytes()); +} + +#[test] +fn enterprise_serialize_consistency() { + let enterprise = EnterpriseAddress::new( + 64, + &Credential::from_keyhash(&Ed25519KeyHash::from([23; Ed25519KeyHash::BYTE_COUNT])), + ); + let addr = enterprise.to_address(); + let addr2 = Address::from_bytes(addr.to_bytes()).unwrap(); + assert_eq!(addr.to_bytes(), addr2.to_bytes()); +} + +#[test] +fn reward_serialize_consistency() { + let reward = RewardAddress::new( + 9, + &Credential::from_scripthash(&ScriptHash::from([127; Ed25519KeyHash::BYTE_COUNT])), + ); + let addr = reward.to_address(); + let addr2 = Address::from_bytes(addr.to_bytes()).unwrap(); + assert_eq!(addr.to_bytes(), addr2.to_bytes()); +} + +fn root_key_12() -> Bip32PrivateKey { + // test walk nut penalty hip pave soap entry language right filter choice + let entropy = [ + 0xdf, 0x9e, 0xd2, 0x5e, 0xd1, 0x46, 0xbf, 0x43, 0x33, 0x6a, 0x5d, 0x7c, 0xf7, 0x39, 0x59, + 0x94, + ]; + Bip32PrivateKey::from_bip39_entropy(&entropy, &[]) +} + +fn root_key_15() -> Bip32PrivateKey { + // art forum devote street sure rather head chuckle guard poverty release quote oak craft enemy + let entropy = [ + 0x0c, 0xcb, 0x74, 0xf3, 0x6b, 0x7d, 0xa1, 0x64, 0x9a, 0x81, 0x44, 0x67, 0x55, 0x22, 0xd4, + 0xd8, 0x09, 0x7c, 0x64, 0x12, + ]; + Bip32PrivateKey::from_bip39_entropy(&entropy, &[]) +} + +fn root_key_24() -> Bip32PrivateKey { + let entropy = [ + 0x4e, 0x82, 0x8f, 0x9a, 0x67, 0xdd, 0xcf, 0xf0, 0xe6, 0x39, 0x1a, 0xd4, 0xf2, 0x6d, 0xdb, + 0x75, 0x79, 0xf5, 0x9b, 0xa1, 0x4b, 0x6d, 0xd4, 0xba, 0xf6, 0x3d, 0xcf, 0xdb, 0x9d, 0x24, + 0x20, 0xda, + ]; + Bip32PrivateKey::from_bip39_entropy(&entropy, &[]) +} + +fn harden(index: u32) -> u32 { + index | 0x80_00_00_00 +} + +#[test] +fn bech32_parsing() { + let addr = + Address::from_bech32("addr1u8pcjgmx7962w6hey5hhsd502araxp26kdtgagakhaqtq8sxy9w7g").unwrap(); + assert_eq!( + addr.to_bech32(Some("foobar".to_string())).unwrap(), + "foobar1u8pcjgmx7962w6hey5hhsd502araxp26kdtgagakhaqtq8s92n4tm" + ); +} + +#[test] +fn byron_magic_parsing() { + // mainnet address w/ protocol magic omitted + let addr = + ByronAddress::from_base58("Ae2tdPwUPEZ4YjgvykNpoFeYUxoyhNj2kg8KfKWN2FizsSpLUPv68MpTVDo") + .unwrap(); + assert_eq!( + addr.byron_protocol_magic(), + NetworkInfo::mainnet().protocol_magic() + ); + assert_eq!( + addr.network_id().unwrap(), + NetworkInfo::mainnet().network_id() + ); +} + +#[test] +fn bip32_12_base() { + let spend = root_key_12() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let stake = root_key_12() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + assert_eq!(addr_net_0.to_bech32(None).unwrap(), "addr_test1qz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwq2ytjqp"); + let addr_net_3 = BaseAddress::new( + NetworkInfo::mainnet().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + assert_eq!(addr_net_3.to_bech32(None).unwrap(), "addr1qx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer3jcu5d8ps7zex2k2xt3uqxgjqnnj83ws8lhrn648jjxtwqfjkjv7"); +} + +#[test] +fn bip32_12_enterprise() { + let spend = root_key_12() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let addr_net_0 = + EnterpriseAddress::new(NetworkInfo::testnet_preprod().network_id(), &spend_cred) + .to_address(); + assert_eq!( + addr_net_0.to_bech32(None).unwrap(), + "addr_test1vz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzerspjrlsz" + ); + let addr_net_3 = + EnterpriseAddress::new(NetworkInfo::mainnet().network_id(), &spend_cred).to_address(); + assert_eq!( + addr_net_3.to_bech32(None).unwrap(), + "addr1vx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzers66hrl8" + ); +} + +#[test] +fn bip32_12_pointer() { + let spend = root_key_12() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let addr_net_0 = PointerAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &Pointer::new_pointer(&BigNum(1), &BigNum(2), &BigNum(3)), + ) + .to_address(); + assert_eq!( + addr_net_0.to_bech32(None).unwrap(), + "addr_test1gz2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzerspqgpsqe70et" + ); + let addr_net_3 = PointerAddress::new( + NetworkInfo::mainnet().network_id(), + &spend_cred, + &Pointer::new_pointer(&BigNum(24157), &BigNum(177), &BigNum(42)), + ) + .to_address(); + assert_eq!( + addr_net_3.to_bech32(None).unwrap(), + "addr1gx2fxv2umyhttkxyxp8x0dlpdt3k6cwng5pxj3jhsydzer5ph3wczvf2w8lunk" + ); +} + +#[test] +fn bip32_15_base() { + let spend = root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let stake = root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + assert_eq!(addr_net_0.to_bech32(None).unwrap(), "addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w"); + let addr_net_3 = BaseAddress::new( + NetworkInfo::mainnet().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + assert_eq!(addr_net_3.to_bech32(None).unwrap(), "addr1q9u5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qld6xc3"); +} + +#[test] +fn bip32_15_enterprise() { + let spend = root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let addr_net_0 = + EnterpriseAddress::new(NetworkInfo::testnet_preprod().network_id(), &spend_cred) + .to_address(); + assert_eq!( + addr_net_0.to_bech32(None).unwrap(), + "addr_test1vpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5eg57c2qv" + ); + let addr_net_3 = + EnterpriseAddress::new(NetworkInfo::mainnet().network_id(), &spend_cred).to_address(); + assert_eq!( + addr_net_3.to_bech32(None).unwrap(), + "addr1v9u5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5eg0kvk0f" + ); +} + +#[test] +fn bip32_15_pointer() { + let spend = root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let addr_net_0 = PointerAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &Pointer::new_pointer(&BigNum(1), &BigNum(2), &BigNum(3)), + ) + .to_address(); + assert_eq!( + addr_net_0.to_bech32(None).unwrap(), + "addr_test1gpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5egpqgpsdhdyc0" + ); + let addr_net_3 = PointerAddress::new( + NetworkInfo::mainnet().network_id(), + &spend_cred, + &Pointer::new_pointer(&BigNum(24157), &BigNum(177), &BigNum(42)), + ) + .to_address(); + assert_eq!( + addr_net_3.to_bech32(None).unwrap(), + "addr1g9u5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5evph3wczvf2kd5vam" + ); +} + +#[test] +fn parse_redeem_address() { + assert!(ByronAddress::is_valid( + "Ae2tdPwUPEZ3MHKkpT5Bpj549vrRH7nBqYjNXnCV8G2Bc2YxNcGHEa8ykDp" + )); + let byron_addr = + ByronAddress::from_base58("Ae2tdPwUPEZ3MHKkpT5Bpj549vrRH7nBqYjNXnCV8G2Bc2YxNcGHEa8ykDp") + .unwrap(); + assert_eq!( + byron_addr.to_base58(), + "Ae2tdPwUPEZ3MHKkpT5Bpj549vrRH7nBqYjNXnCV8G2Bc2YxNcGHEa8ykDp" + ); + let byron_addr2 = ByronAddress::from_bytes(byron_addr.to_bytes()).unwrap(); + assert_eq!( + byron_addr2.to_base58(), + "Ae2tdPwUPEZ3MHKkpT5Bpj549vrRH7nBqYjNXnCV8G2Bc2YxNcGHEa8ykDp" + ); +} + +#[test] +fn bip32_15_byron() { + let byron_key = root_key_15() + .derive(harden(44)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let byron_addr = + ByronAddress::icarus_from_key(&byron_key, NetworkInfo::mainnet().protocol_magic()); + assert_eq!( + byron_addr.to_base58(), + "Ae2tdPwUPEZHtBmjZBF4YpMkK9tMSPTE2ADEZTPN97saNkhG78TvXdp3GDk" + ); + assert!(ByronAddress::is_valid( + "Ae2tdPwUPEZHtBmjZBF4YpMkK9tMSPTE2ADEZTPN97saNkhG78TvXdp3GDk" + )); + assert_eq!(byron_addr.network_id().unwrap(), 0b0001); + + let byron_addr_2 = + ByronAddress::from_address(&Address::from_bytes(byron_addr.to_bytes()).unwrap()).unwrap(); + assert_eq!(byron_addr.to_base58(), byron_addr_2.to_base58()); +} + +#[test] +fn bip32_24_base() { + let spend = root_key_24() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let stake = root_key_24() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + assert_eq!(addr_net_0.to_bech32(None).unwrap(), "addr_test1qqy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmn8k8ttq8f3gag0h89aepvx3xf69g0l9pf80tqv7cve0l33sw96paj"); + let addr_net_3 = BaseAddress::new( + NetworkInfo::mainnet().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + assert_eq!(addr_net_3.to_bech32(None).unwrap(), "addr1qyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmn8k8ttq8f3gag0h89aepvx3xf69g0l9pf80tqv7cve0l33sdn8p3d"); +} + +#[test] +fn bip32_24_enterprise() { + let spend = root_key_24() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let addr_net_0 = + EnterpriseAddress::new(NetworkInfo::testnet_preprod().network_id(), &spend_cred) + .to_address(); + assert_eq!( + addr_net_0.to_bech32(None).unwrap(), + "addr_test1vqy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqtjtf68" + ); + let addr_net_3 = + EnterpriseAddress::new(NetworkInfo::mainnet().network_id(), &spend_cred).to_address(); + assert_eq!( + addr_net_3.to_bech32(None).unwrap(), + "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z" + ); +} + +#[test] +fn bip32_24_pointer() { + let spend = root_key_24() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let addr_net_0 = PointerAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &Pointer::new_pointer(&BigNum(1), &BigNum(2), &BigNum(3)), + ) + .to_address(); + assert_eq!( + addr_net_0.to_bech32(None).unwrap(), + "addr_test1gqy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqpqgps5mee0p" + ); + let addr_net_3 = PointerAddress::new( + NetworkInfo::mainnet().network_id(), + &spend_cred, + &Pointer::new_pointer(&BigNum(24157), &BigNum(177), &BigNum(42)), + ) + .to_address(); + assert_eq!( + addr_net_3.to_bech32(None).unwrap(), + "addr1gyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnyph3wczvf2dqflgt" + ); +} + +#[test] +fn bip32_12_reward() { + let staking_key = root_key_12() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + let staking_cred = Credential::from_keyhash(&staking_key.to_raw_key().hash()); + let addr_net_0 = + RewardAddress::new(NetworkInfo::testnet_preprod().network_id(), &staking_cred).to_address(); + assert_eq!( + addr_net_0.to_bech32(None).unwrap(), + "stake_test1uqevw2xnsc0pvn9t9r9c7qryfqfeerchgrlm3ea2nefr9hqp8n5xl" + ); + let addr_net_3 = + RewardAddress::new(NetworkInfo::mainnet().network_id(), &staking_cred).to_address(); + assert_eq!( + addr_net_3.to_bech32(None).unwrap(), + "stake1uyevw2xnsc0pvn9t9r9c7qryfqfeerchgrlm3ea2nefr9hqxdekzz" + ); +} + +#[test] +fn bip32_24_base_multisig_hd_derivation() { + let spend = root_key_24() + .derive(harden(1854)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let stake = root_key_24() + .derive(harden(1854)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + assert_eq!(addr_net_0.to_bech32(None).unwrap(), "addr_test1qz8fg2e9yn0ga6sav0760cxmx0antql96mfuhqgzcc5swugw2jqqlugnx9qjep9xvcx40z0zfyep55r2t3lav5smyjrs96cusg"); + let addr_net_3 = BaseAddress::new( + NetworkInfo::mainnet().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + assert_eq!(addr_net_3.to_bech32(None).unwrap(), "addr1qx8fg2e9yn0ga6sav0760cxmx0antql96mfuhqgzcc5swugw2jqqlugnx9qjep9xvcx40z0zfyep55r2t3lav5smyjrsxv9uuh"); +} + +#[test] +fn multisig_from_script() { + let spend = root_key_24() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + + let mut pubkey_native_scripts = NativeScripts::new(); + + let spending_hash = spend.to_raw_key().hash(); + pubkey_native_scripts.add(&NativeScript::new_script_pubkey(&ScriptPubkey::new( + &spending_hash, + ))); + let oneof_native_script = + NativeScript::new_script_n_of_k(&ScriptNOfK::new(1, &pubkey_native_scripts)); + + let script_hash = ScriptHash::from_bytes(oneof_native_script.hash().to_bytes()).unwrap(); + + let spend_cred = Credential::from_scripthash(&script_hash); + let stake_cred = Credential::from_scripthash(&script_hash); + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + assert_eq!(addr_net_0.to_bech32(None).unwrap(), "addr_test1xr0de0mz3m9xmgtlmqqzu06s0uvfsczskdec8k7v4jhr7077mjlk9rk2dkshlkqq9cl4qlccnps9pvmns0duet9w8uls8flvxc"); + let addr_net_3 = BaseAddress::new( + NetworkInfo::mainnet().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + assert_eq!(addr_net_3.to_bech32(None).unwrap(), "addr1x80de0mz3m9xmgtlmqqzu06s0uvfsczskdec8k7v4jhr7077mjlk9rk2dkshlkqq9cl4qlccnps9pvmns0duet9w8ulsylzv28"); +} + +#[test] +fn pointer_address_big() { + let addr = Address::from_bech32("addr_test1grqe6lg9ay8wkcu5k5e38lne63c80h3nq6xxhqfmhewf645pllllllllllll7lupllllllllllll7lupllllllllllll7lc9wayvj").unwrap(); + let ptr = PointerAddress::from_address(&addr).unwrap().stake; + assert_eq!(u64::MAX, u64::from(ptr.slot)); + assert_eq!(u64::MAX, u64::from(ptr.tx_index)); + assert_eq!(u64::MAX, u64::from(ptr.cert_index)); +} + +#[test] +fn point_address_old() { + let p1 = Pointer::new(10, 20, 30); + let p2 = Pointer::new_pointer(&BigNum(10), &BigNum(20), &BigNum(30)); + assert_eq!(p1, p2); +} + +#[test] +fn prepod_network_id_test() { + let address = "KjgoiXJS2coTnqpCLHXFtd89Hv9ttjsE6yW4msyLXFNkykUpTsyBs85r2rDDia2uKrhdpGKCJnmFXwvPSWLe75564ixZWdTxRh7TnuaDLnHx"; + let network_id = ByronAddress::from_base58(address) + .unwrap() + .to_address() + .network_id() + .unwrap(); + assert_eq!(network_id, NetworkInfo::testnet_preprod().network_id()); +} + +#[test] +fn malformed_addres_deserialisation_errors() { + let address_bech32 = "addr1q9d66zzs27kppmx8qc8h43q7m4hkxp5d39377lvxefvxd8j7eukjsdqc5c97t2zg5guqadepqqx6rc9m7wtnxy6tajjvk4a0kze4ljyuvvrpexg5up2sqxj33363v35gtew"; + let address = Address::from_bech32(address_bech32); + assert!(address.is_err()); +} + +#[test] +fn malformed_addres_embedded() { + let address = MalformedAddress(vec![5u8; 32]); + let output = TransactionOutput::new(&address.to_address(), &Value::new(&Coin::from(100u64))); + let bytes = output.to_bytes(); + let output2 = TransactionOutput::from_bytes(bytes).unwrap(); + + assert!(output2.address.is_malformed()); + assert_eq!(&address.to_address(), &output2.address); +} + +#[test] +fn address_kind() { + let base_address = fake_base_address(1); + assert_eq!(base_address.kind(), AddressKind::Base); + + let pointer_address = fake_pointer_address(2); + assert_eq!(pointer_address.kind(), AddressKind::Pointer); + + let enterprise_address = fake_enterprise_address(3); + assert_eq!(enterprise_address.kind(), AddressKind::Enterprise); + + let reward_address = fake_reward_address(4).to_address(); + assert_eq!(reward_address.kind(), AddressKind::Reward); + + let byron_address = + ByronAddress::from_base58("Ae2tdPwUPEZ6r6zbg4ibhFrNnyKHg7SYuPSfDpjKxgvwFX9LquRep7gj7FQ") + .unwrap() + .to_address(); + assert_eq!(byron_address.kind(), AddressKind::Byron); + + let malformed_address = fake_malformed_address(); + assert_eq!(malformed_address.kind(), AddressKind::Malformed); +} + +#[test] +fn address_payment_cred() { + let key_hash_1 = fake_key_hash(1); + let key_hash_2 = fake_key_hash(2); + let key_hash_3 = fake_key_hash(3); + let key_hash_4 = fake_key_hash(4); + let key_hash_5 = fake_key_hash(5); + let credential_1 = Credential::from_keyhash(&key_hash_1); + let credential_2 = Credential::from_keyhash(&key_hash_2); + let credential_3 = Credential::from_keyhash(&key_hash_3); + let credential_4 = Credential::from_keyhash(&key_hash_4); + let credential_5 = Credential::from_keyhash(&key_hash_5); + + let base_address = BaseAddress::new( + 1, + &credential_1, + &credential_2, + ).to_address(); + assert_eq!(base_address.payment_cred(), Some(credential_1)); + + let pointer_address = PointerAddress::new( + 2, + &credential_3, + &Pointer::new_pointer(&BigNum(1), &BigNum(2), &BigNum(3)), + ).to_address(); + assert_eq!(pointer_address.payment_cred(), Some(credential_3)); + + let enterprise_address = EnterpriseAddress::new( + 3, + &credential_4, + ).to_address(); + assert_eq!(enterprise_address.payment_cred(), Some(credential_4)); + + let reward_address = RewardAddress::new( + 4, + &credential_5, + ).to_address(); + + assert_eq!(reward_address.payment_cred(), Some(credential_5)); + + let byron_address = + ByronAddress::from_base58("Ae2tdPwUPEZ6r6zbg4ibhFrNnyKHg7SYuPSfDpjKxgvwFX9LquRep7gj7FQ") + .unwrap() + .to_address(); + assert_eq!(byron_address.payment_cred(), None); + + let malformed_address = fake_malformed_address(); + assert_eq!(malformed_address.payment_cred(), None); +} + + +#[test] +fn addresses_network_id() { + let base_address = BaseAddress::new( + 1, + &Credential::from_keyhash(&fake_key_hash(1)), + &Credential::from_keyhash(&fake_key_hash(2)), + ); + assert_eq!(base_address.network_id(), 1); + assert_eq!(base_address.to_address().network_id().unwrap(), 1); + + let pointer_address = PointerAddress::new( + 2, + &Credential::from_keyhash(&fake_key_hash(3)), + &Pointer::new_pointer(&BigNum(1), &BigNum(2), &BigNum(3)), + ); + assert_eq!(pointer_address.network_id(), 2); + assert_eq!(pointer_address.to_address().network_id().unwrap(), 2); + + let enterprise_address = EnterpriseAddress::new( + 3, + &Credential::from_keyhash(&fake_key_hash(4)), + ); + assert_eq!(enterprise_address.network_id(), 3); + assert_eq!(enterprise_address.to_address().network_id().unwrap(), 3); + + let reward_address = RewardAddress::new( + 4, + &Credential::from_keyhash(&fake_key_hash(5)), + ); + assert_eq!(reward_address.network_id(), 4); + assert_eq!(reward_address.to_address().network_id().unwrap(), 4); + + let byron_address = + ByronAddress::from_base58("Ae2tdPwUPEZ6r6zbg4ibhFrNnyKHg7SYuPSfDpjKxgvwFX9LquRep7gj7FQ") + .unwrap(); + assert_eq!(byron_address.network_id().unwrap(), 1); + assert_eq!(byron_address.to_address().network_id().unwrap(), 1); + + let malformed_address = fake_malformed_address(); + assert!(malformed_address.network_id().is_err()); +} diff --git a/rust/src/tests/builders/batch_tools.rs b/rust/src/tests/builders/batch_tools.rs new file mode 100644 index 00000000..917d8363 --- /dev/null +++ b/rust/src/tests/builders/batch_tools.rs @@ -0,0 +1,732 @@ +use crate::fees::{min_fee, LinearFee}; +use crate::tests::fakes::{fake_policy_id, fake_base_address}; +use crate::*; + +fn generate_assets( + from_policy: usize, + from_asset: usize, + policies_count: usize, + assets_count: usize, + asset_name_size: usize, + amount_per_asset: Coin, +) -> Option { + let mut assets = MultiAsset::new(); + for i in from_policy..(policies_count + from_policy) { + let policy_id = fake_policy_id(i as u8); + for j in from_asset..(assets_count + from_asset) { + let asset_name = AssetName::new(vec![j as u8; asset_name_size]).unwrap(); + assets.set_asset(&policy_id, &asset_name, &amount_per_asset); + } + } + + if assets.0.is_empty() { + None + } else { + Some(assets) + } +} + +fn generate_utxo( + address: &Address, + index: u32, + from_policy: usize, + from_asset: usize, + policies: usize, + assets: usize, + asset_name_size: usize, + amount_per_asset: Coin, + ada: Coin, +) -> TransactionUnspentOutput { + let input = TransactionInput::new( + &TransactionHash::from_bytes( + hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") + .unwrap(), + ) + .unwrap(), + index, + ); + let assets = generate_assets( + from_policy, + from_asset, + policies, + assets, + asset_name_size, + amount_per_asset, + ); + let value = match assets { + Some(assets) => Value::new_with_assets(&ada, &assets), + None => Value::new(&ada), + }; + let output = TransactionOutput::new(&address, &value); + TransactionUnspentOutput::new(&input, &output) +} + +fn generate_big_ada_utoxs_bacth() -> TransactionUnspentOutputs { + let mut utxos = Vec::new(); + let address = fake_base_address(1); + let address_2 = fake_base_address(2); + for i in 0..10 { + let utxo = generate_utxo( + &address, + i, + (i / 2) as usize, + (i / 2) as usize, + 5, + 5, + 20, + Coin::from(500u64), + Coin::from(5000u64), + ); + utxos.push(utxo); + } + + for i in 0..10000 { + let utxo = generate_utxo( + &address_2, + i + 1000, + 0, + 0, + 0, + 0, + 20, + Coin::zero(), + Coin::from(5000000u64), + ); + utxos.push(utxo); + } + TransactionUnspentOutputs(utxos) +} + +fn generate_big_utoxs_bacth() -> TransactionUnspentOutputs { + let mut utxos = Vec::new(); + let address = fake_base_address(1); + let address_2 = fake_base_address(2); + for i in 0..200 { + let utxo = generate_utxo( + &address, + i, + (i / 2) as usize, + (i / 2) as usize, + 5, + 5, + 20, + Coin::from(500u64), + Coin::from(5000u64), + ); + utxos.push(utxo); + } + + for i in 0..10 { + let utxo = generate_utxo( + &address_2, + i + 1000, + 0, + 0, + 0, + 0, + 20, + Coin::zero(), + Coin::from(50000000u64), + ); + utxos.push(utxo); + } + TransactionUnspentOutputs(utxos) +} + +fn get_utxos_total(utxos: &TransactionUnspentOutputs) -> Value { + let mut total_value = Value::zero(); + for utxo in utxos { + total_value = total_value.checked_add(&utxo.output.amount).unwrap(); + } + + total_value +} + +fn get_batch_total(batches: &TransactionBatchList) -> Value { + let mut total_value = Value::zero(); + for batch in batches { + for tx in batch { + for output in &tx.body.outputs { + total_value = total_value.checked_add(&output.amount).unwrap(); + } + let fee = Value::new(&tx.body.fee); + total_value = total_value.checked_add(&fee).unwrap(); + } + } + + total_value +} + +fn get_tx_fee(tx: &Transaction, cfg: &TransactionBuilderConfig) -> Coin { + min_fee(tx, &cfg.fee_algo).unwrap() +} + +fn get_ada_for_output(output: &TransactionOutput, cfg: &TransactionBuilderConfig) -> Coin { + min_ada_for_output(output, &cfg.data_cost).unwrap() +} + +fn check_balance(utxos: &TransactionUnspentOutputs, batches: &TransactionBatchList) { + let utxos_total = get_utxos_total(utxos); + let batches_total = get_batch_total(batches); + assert_eq!(utxos_total, batches_total); +} + +fn check_min_adas(batches: &TransactionBatchList, cfg: &TransactionBuilderConfig) { + for batch in batches { + for tx in batch { + for output in &tx.body.outputs { + let min_ada = get_ada_for_output(output, cfg); + assert!(output.amount.coin >= min_ada); + } + } + } +} + +fn check_fees(batches: &TransactionBatchList, cfg: &TransactionBuilderConfig) { + for batch in batches { + for tx in batch { + let fee = get_tx_fee(tx, cfg); + assert_eq!(fee, tx.body.fee); + } + } +} + +fn check_value_size_limit(batches: &TransactionBatchList, cfg: &TransactionBuilderConfig) { + for batch in batches { + for tx in batch { + for output in &tx.body.outputs { + let value_size = output.amount().to_bytes().len(); + assert!(value_size <= cfg.max_value_size as usize); + } + } + } +} + +fn check_tx_size_limit(batches: &TransactionBatchList, cfg: &TransactionBuilderConfig) { + for batch in batches { + for tx in batch { + let tx_size = tx.to_bytes().len(); + assert!(tx_size <= cfg.max_tx_size as usize); + } + } +} + +#[test] +pub fn test_big_utoxs_batch() { + let utxos = generate_big_utoxs_bacth(); + let linear_fee = LinearFee::new(&BigNum(44), &BigNum(155381)); + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(500000000)) + .key_deposit(&BigNum(2000000)) + .max_value_size(4000) + .max_tx_size(8000) + .coins_per_utxo_byte(&BigNum(34_482 / 8)) + .ex_unit_prices(&ExUnitPrices::new( + &SubCoin::new(&BigNum(577), &BigNum(10000)), + &SubCoin::new(&BigNum(721), &BigNum(10000000)), + )) + .build() + .unwrap(); + + let res = create_send_all(&fake_base_address(10000), &utxos, &cfg); + assert!(res.is_ok()); + + let batches = res.unwrap(); + check_balance(&utxos, &batches); + check_min_adas(&batches, &cfg); + check_fees(&batches, &cfg); + check_value_size_limit(&batches, &cfg); + check_tx_size_limit(&batches, &cfg); +} + +#[test] +pub fn test_big_utoxs_ada_batch() { + let utxos = generate_big_ada_utoxs_bacth(); + let linear_fee = LinearFee::new(&BigNum(44), &BigNum(155381)); + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(500000000)) + .key_deposit(&BigNum(2000000)) + .max_value_size(4000) + .max_tx_size(8000) + .coins_per_utxo_byte(&BigNum(34_482 / 8)) + .ex_unit_prices(&ExUnitPrices::new( + &SubCoin::new(&BigNum(577), &BigNum(10000)), + &SubCoin::new(&BigNum(721), &BigNum(10000000)), + )) + .build() + .unwrap(); + + let res = create_send_all(&fake_base_address(10000), &utxos, &cfg); + assert!(res.is_ok()); + + let batches = res.unwrap(); + check_balance(&utxos, &batches); + check_min_adas(&batches, &cfg); + check_fees(&batches, &cfg); + check_value_size_limit(&batches, &cfg); + check_tx_size_limit(&batches, &cfg); +} + +#[test] +pub fn test_one_utxo() { + let address = fake_base_address(1); + let utxo = generate_utxo( + &address, + 1, + 0, + 0, + 3, + 2, + 20, + Coin::from(500u64), + Coin::from(5000000u64), + ); + + let utxos = TransactionUnspentOutputs(vec![utxo]); + + let linear_fee = LinearFee::new(&BigNum(44), &BigNum(155381)); + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(500000000)) + .key_deposit(&BigNum(2000000)) + .max_value_size(4000) + .max_tx_size(8000) + .coins_per_utxo_byte(&BigNum(34_482 / 8)) + .ex_unit_prices(&ExUnitPrices::new( + &SubCoin::new(&BigNum(577), &BigNum(10000)), + &SubCoin::new(&BigNum(721), &BigNum(10000000)), + )) + .build() + .unwrap(); + + let res = create_send_all(&fake_base_address(10000), &utxos, &cfg); + assert!(res.is_ok()); + + let batches = res.unwrap(); + check_balance(&utxos, &batches); + check_min_adas(&batches, &cfg); + check_fees(&batches, &cfg); + check_value_size_limit(&batches, &cfg); + check_tx_size_limit(&batches, &cfg); +} + +#[test] +pub fn test_one_utxo_one_asset_per_output() { + let address = fake_base_address(1); + let utxo_1 = generate_utxo( + &address, + 1, + 0, + 0, + 3, + 1, + 20, + Coin::from(500u64), + Coin::from(5000000u64), + ); + + let utxo_2 = generate_utxo( + &address, + 2, + 1, + 0, + 3, + 1, + 20, + Coin::from(500u64), + Coin::from(5000000u64), + ); + + let utxo_3 = generate_utxo( + &address, + 3, + 2, + 0, + 3, + 1, + 20, + Coin::from(500u64), + Coin::from(5000000u64), + ); + + let utxos = TransactionUnspentOutputs(vec![utxo_1, utxo_2, utxo_3]); + + let linear_fee = LinearFee::new(&BigNum(1), &BigNum(0)); + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(500000000)) + .key_deposit(&BigNum(2000000)) + .max_value_size(80) + .max_tx_size(8000) + .coins_per_utxo_byte(&BigNum(34_482 / 8)) + .ex_unit_prices(&ExUnitPrices::new( + &SubCoin::new(&BigNum(577), &BigNum(10000)), + &SubCoin::new(&BigNum(721), &BigNum(10000000)), + )) + .build() + .unwrap(); + + let res = create_send_all(&fake_base_address(10000), &utxos, &cfg); + assert!(res.is_ok()); + + let batches = res.unwrap(); + + for batch in &batches { + for tx in batch { + for output in &tx.body.outputs() { + assert_eq!(output.amount().multiasset.unwrap().0.len(), 1); + for asset in output.amount().multiasset.unwrap().0.values() { + assert_eq!(asset.len(), 1); + } + } + } + } + + check_balance(&utxos, &batches); + check_min_adas(&batches, &cfg); + check_fees(&batches, &cfg); + check_value_size_limit(&batches, &cfg); + check_tx_size_limit(&batches, &cfg); +} + +#[test] +pub fn test_one_utxo_one_asset_per_tx() { + let address = fake_base_address(1); + let utxo_1 = generate_utxo( + &address, + 1, + 0, + 0, + 1, + 1, + 20, + Coin::from(500u64), + Coin::from(5000000u64), + ); + + let utxo_2 = generate_utxo( + &address, + 2, + 1, + 0, + 1, + 1, + 20, + Coin::from(500u64), + Coin::from(5000000u64), + ); + + let utxo_3 = generate_utxo( + &address, + 3, + 2, + 0, + 1, + 1, + 20, + Coin::from(500u64), + Coin::from(5000000u64), + ); + + let utxos = TransactionUnspentOutputs(vec![utxo_1, utxo_2, utxo_3]); + + let linear_fee = LinearFee::new(&BigNum(1), &BigNum(0)); + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(500000000)) + .key_deposit(&BigNum(2000000)) + .max_value_size(80) + .max_tx_size(300) + .coins_per_utxo_byte(&BigNum(34_482 / 8)) + .ex_unit_prices(&ExUnitPrices::new( + &SubCoin::new(&BigNum(577), &BigNum(10000)), + &SubCoin::new(&BigNum(721), &BigNum(10000000)), + )) + .build() + .unwrap(); + + let res = create_send_all(&fake_base_address(10000), &utxos, &cfg); + assert!(res.is_ok()); + + let batches = res.unwrap(); + + for batch in &batches { + for tx in batch { + assert_eq!(tx.body.outputs().len(), 1); + for output in &tx.body.outputs() { + assert_eq!(output.amount().multiasset.unwrap().0.len(), 1); + for asset in output.amount().multiasset.unwrap().0.values() { + assert_eq!(asset.len(), 1); + } + } + } + } + + check_balance(&utxos, &batches); + check_min_adas(&batches, &cfg); + check_fees(&batches, &cfg); + check_value_size_limit(&batches, &cfg); + check_tx_size_limit(&batches, &cfg); +} + +#[test] +pub fn test_only_ada_utxo() { + let address = fake_base_address(1); + let utxo = generate_utxo( + &address, + 1, + 0, + 0, + 0, + 0, + 20, + Coin::zero(), + Coin::from(5000000u64), + ); + + let utxos = TransactionUnspentOutputs(vec![utxo]); + + let linear_fee = LinearFee::new(&BigNum(1), &BigNum(0)); + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(500000000)) + .key_deposit(&BigNum(2000000)) + .max_value_size(4000) + .max_tx_size(8000) + .coins_per_utxo_byte(&BigNum(34_482 / 8)) + .ex_unit_prices(&ExUnitPrices::new( + &SubCoin::new(&BigNum(577), &BigNum(10000)), + &SubCoin::new(&BigNum(721), &BigNum(10000000)), + )) + .build() + .unwrap(); + + let res = create_send_all(&fake_base_address(10000), &utxos, &cfg); + assert!(res.is_ok()); + + let batches = res.unwrap(); + check_balance(&utxos, &batches); + check_min_adas(&batches, &cfg); + check_fees(&batches, &cfg); + check_value_size_limit(&batches, &cfg); + check_tx_size_limit(&batches, &cfg); +} + +#[test] +pub fn test_not_enough_ada() { + let address = fake_base_address(1); + let utxo = generate_utxo(&address, 1, 0, 0, 0, 0, 20, Coin::zero(), Coin::from(1u64)); + + let utxos = TransactionUnspentOutputs(vec![utxo]); + + let linear_fee = LinearFee::new(&BigNum(44), &BigNum(155381)); + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(500000000)) + .key_deposit(&BigNum(2000000)) + .max_value_size(4000) + .max_tx_size(8000) + .coins_per_utxo_byte(&BigNum(34_482 / 8)) + .ex_unit_prices(&ExUnitPrices::new( + &SubCoin::new(&BigNum(577), &BigNum(10000)), + &SubCoin::new(&BigNum(721), &BigNum(10000000)), + )) + .build() + .unwrap(); + + let res = create_send_all(&fake_base_address(10000), &utxos, &cfg); + assert!(res.is_err()); +} + +#[test] +pub fn test_value_limit_error() { + let address = fake_base_address(1); + let utxo = generate_utxo( + &address, + 1, + 0, + 0, + 1, + 1, + 20, + Coin::from(1000000u64), + Coin::from(500000u64), + ); + + let utxos = TransactionUnspentOutputs(vec![utxo]); + + let linear_fee = LinearFee::new(&BigNum(44), &BigNum(155381)); + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(500000000)) + .key_deposit(&BigNum(2000000)) + .max_value_size(10) + .max_tx_size(8000000) + .coins_per_utxo_byte(&BigNum(34_482 / 8)) + .ex_unit_prices(&ExUnitPrices::new( + &SubCoin::new(&BigNum(577), &BigNum(10000)), + &SubCoin::new(&BigNum(721), &BigNum(10000000)), + )) + .build() + .unwrap(); + + let res = create_send_all(&fake_base_address(10000), &utxos, &cfg); + assert!(res.is_err()); +} + +#[test] +pub fn test_tx_limit_error() { + let address = fake_base_address(1); + let utxo = generate_utxo( + &address, + 1, + 0, + 0, + 10, + 10, + 20, + Coin::from(1000000u64), + Coin::from(50000000u64), + ); + + let utxos = TransactionUnspentOutputs(vec![utxo]); + + let linear_fee = LinearFee::new(&BigNum(44), &BigNum(155381)); + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(500000000)) + .key_deposit(&BigNum(2000000)) + .max_value_size(100) + .max_tx_size(2000) + .coins_per_utxo_byte(&BigNum(34_482 / 8)) + .ex_unit_prices(&ExUnitPrices::new( + &SubCoin::new(&BigNum(577), &BigNum(10000)), + &SubCoin::new(&BigNum(721), &BigNum(10000000)), + )) + .build() + .unwrap(); + + let res = create_send_all(&fake_base_address(10000), &utxos, &cfg); + assert!(res.is_err()); +} + +#[test] +pub fn test_no_utxos() { + let utxos = TransactionUnspentOutputs(vec![]); + + let linear_fee = LinearFee::new(&BigNum(44), &BigNum(155381)); + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(500000000)) + .key_deposit(&BigNum(2000000)) + .max_value_size(10) + .max_tx_size(8000000) + .coins_per_utxo_byte(&BigNum(34_482 / 8)) + .ex_unit_prices(&ExUnitPrices::new( + &SubCoin::new(&BigNum(577), &BigNum(10000)), + &SubCoin::new(&BigNum(721), &BigNum(10000000)), + )) + .build() + .unwrap(); + + let res = create_send_all(&fake_base_address(10000), &utxos, &cfg); + assert!(res.is_ok()); + + let batches = res.unwrap(); + let total_txs = batches.into_iter().fold(0, |acc, batch| acc + batch.len()); + assert_eq!(total_txs, 0); +} + +#[test] +pub fn test_script_input_error() { + let address = Address::from_hex("10798c8ce251c36c15f8bccf3306feae1218fce7503b331e6d92e666aa00efb5788e8713c844dfd32b2e91de1e309fefffd555f827cc9ee164").unwrap(); + let utxo = generate_utxo(&address, 1, 0, 0, 0, 0, 20, Coin::zero(), Coin::from(1u64)); + let utxos = TransactionUnspentOutputs(vec![utxo]); + + let linear_fee = LinearFee::new(&BigNum(44), &BigNum(155381)); + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(500000000)) + .key_deposit(&BigNum(2000000)) + .max_value_size(10) + .max_tx_size(8000000) + .coins_per_utxo_byte(&BigNum(34_482 / 8)) + .ex_unit_prices(&ExUnitPrices::new( + &SubCoin::new(&BigNum(577), &BigNum(10000)), + &SubCoin::new(&BigNum(721), &BigNum(10000000)), + )) + .build() + .unwrap(); + + let res = create_send_all(&fake_base_address(10000), &utxos, &cfg); + assert!(res.is_err()); +} + +#[test] +pub fn test_two_asset_utxo_one_ada_utxo() { + let address = fake_base_address(1); + let asset_utxo_1 = generate_utxo( + &address, + 1, + 0, + 0, + 1, + 1, + 8, + Coin::from(1u64), + Coin::from(1344798u64), + ); + + let asset_utxo_2 = generate_utxo( + &address, + 2, + 1, + 1, + 1, + 1, + 8, + Coin::from(1u64), + Coin::from(1344798u64), + ); + + let ada_utxo = generate_utxo( + &address, + 3, + 0, + 0, + 0, + 0, + 20, + Coin::from(1u64), + Coin::from(9967920528u64), + ); + + let utxos = TransactionUnspentOutputs(vec![asset_utxo_1, asset_utxo_2, ada_utxo]); + + let linear_fee = LinearFee::new(&BigNum(44), &BigNum(155381)); + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(500000000)) + .key_deposit(&BigNum(2000000)) + .max_value_size(4000) + .max_tx_size(8000) + .coins_per_utxo_byte(&BigNum(34_482 / 8)) + .ex_unit_prices(&ExUnitPrices::new( + &SubCoin::new(&BigNum(577), &BigNum(10000)), + &SubCoin::new(&BigNum(721), &BigNum(10000000)), + )) + .build() + .unwrap(); + + let res = create_send_all(&fake_base_address(10000), &utxos, &cfg); + assert!(res.is_ok()); + + let batches = res.unwrap(); + check_balance(&utxos, &batches); + check_min_adas(&batches, &cfg); + check_fees(&batches, &cfg); + check_value_size_limit(&batches, &cfg); + check_tx_size_limit(&batches, &cfg); +} diff --git a/rust/src/tests/builders/certificates_builder.rs b/rust/src/tests/builders/certificates_builder.rs new file mode 100644 index 00000000..159d4002 --- /dev/null +++ b/rust/src/tests/builders/certificates_builder.rs @@ -0,0 +1,482 @@ +use crate::*; +use crate::tests::fakes::{fake_genesis_delegate_hash, fake_genesis_hash, fake_key_hash, fake_pool_metadata_hash, fake_vrf_key_hash}; + +#[test] +fn certificates_builder_deposit_no_refund_test() { + let mut builder = CertificatesBuilder::new(); + let pool_deposit = 100u64; + let key_deposit = 200u64; + let key_deposit_form_args = 201u64; + let drep_reg_deposit = 400u64; + + let committee_hot_key_dereg_cert = + CommitteeColdResign::new(&Credential::from_keyhash(&fake_key_hash(1))); + let committee_hot_key_dereg_cert_wrapped = + Certificate::new_committee_cold_resign(&committee_hot_key_dereg_cert); + + let committee_hot_key_reg_cert = CommitteeHotAuth::new( + &Credential::from_keyhash(&fake_key_hash(2)), + &Credential::from_keyhash(&fake_key_hash(3)), + ); + let committee_hot_key_reg_cert_wrapped = + Certificate::new_committee_hot_auth(&committee_hot_key_reg_cert); + + let drep_reg_cert = DRepRegistration::new( + &Credential::from_keyhash(&fake_key_hash(4)), + &Coin::from(drep_reg_deposit), + ); + let drep_reg_cert_wrapped = Certificate::new_drep_registration(&drep_reg_cert); + + let drep_update_cert = DRepUpdate::new(&Credential::from_keyhash(&fake_key_hash(6))); + let cdrep_update_cert_wrapped = Certificate::new_drep_update(&drep_update_cert); + + let genesis_key_deleg_cert = GenesisKeyDelegation::new( + &fake_genesis_hash(7), + &fake_genesis_delegate_hash(8), + &fake_vrf_key_hash(9), + ); + let genesis_key_deleg_cert_wrapped = + Certificate::new_genesis_key_delegation(&genesis_key_deleg_cert); + + let mir_cert = MoveInstantaneousReward::new_to_other_pot(MIRPot::Reserves, &Coin::from(100u64)); + let mir_cert_wrapped = Certificate::new_move_instantaneous_rewards_cert( + &MoveInstantaneousRewardsCert::new(&mir_cert), + ); + + let staking_cred = Credential::from_keyhash(&fake_key_hash(10)); + let reward_address = + RewardAddress::new(NetworkInfo::testnet_preprod().network_id(), &staking_cred); + let mut owners = Ed25519KeyHashes::new(); + owners.add(&fake_key_hash(11)); + owners.add(&fake_key_hash(12)); + let relays = Relays::new(); + let matadata = PoolMetadata::new( + &URL::new("https://iohk.io".to_string()).unwrap(), + &fake_pool_metadata_hash(5), + ); + + let params = PoolParams::new( + &fake_key_hash(13), + &fake_vrf_key_hash(15), + &Coin::from(100u64), + &Coin::from(200u64), + &UnitInterval::new(&BigNum::from(110u64), &BigNum::from(220u64)), + &reward_address, + &owners, + &relays, + Some(matadata), + ); + + let pool_reg_cert = PoolRegistration::new(¶ms); + let pool_reg_cert_wrapped = Certificate::new_pool_registration(&pool_reg_cert); + + let pool_ret_cert = PoolRetirement::new(&fake_key_hash(16), Epoch::from(100u32)); + let pool_ret_cert_wrapped = Certificate::new_pool_retirement(&pool_ret_cert); + + let drep = DRep::new_key_hash(&fake_key_hash(17)); + let stake_vote_deleg_cert = StakeAndVoteDelegation::new( + &Credential::from_keyhash(&fake_key_hash(18)), + &fake_key_hash(19), + &drep, + ); + let stake_vote_deleg_cert_wrapped = + Certificate::new_stake_and_vote_delegation(&stake_vote_deleg_cert); + + let stake_deleg_cert = StakeDelegation::new( + &Credential::from_keyhash(&fake_key_hash(20)), + &fake_key_hash(21), + ); + let stake_deleg_cert_wrapped = Certificate::new_stake_delegation(&stake_deleg_cert); + + let stake_reg_cert = StakeRegistration::new(&Credential::from_keyhash(&fake_key_hash(23))); + let stake_reg_cert_wrapped = Certificate::new_stake_registration(&stake_reg_cert); + + let stake_reg_with_coin_cert = StakeRegistration::new_with_explicit_deposit( + &Credential::from_keyhash(&fake_key_hash(23)), + &Coin::from(key_deposit_form_args), + ); + let stake_reg_with_coint_wrapped = + Certificate::new_stake_registration(&stake_reg_with_coin_cert); + + let stake_reg_deleg_cert = StakeRegistrationAndDelegation::new( + &Credential::from_keyhash(&fake_key_hash(23)), + &fake_key_hash(24), + &Coin::from(key_deposit_form_args), + ); + let stake_reg_deleg_cert_wrapped = + Certificate::new_stake_registration_and_delegation(&stake_reg_deleg_cert); + + let drep = DRep::new_key_hash(&fake_key_hash(25)); + let stake_vote_reg_deleg_cert = StakeVoteRegistrationAndDelegation::new( + &Credential::from_keyhash(&fake_key_hash(26)), + &fake_key_hash(27), + &drep, + &Coin::from(key_deposit_form_args), + ); + let stake_vote_reg_deleg_cert_wrapped = + Certificate::new_stake_vote_registration_and_delegation(&stake_vote_reg_deleg_cert); + + let drep = DRep::new_key_hash(&fake_key_hash(28)); + let vote_deleg_cert = VoteDelegation::new(&Credential::from_keyhash(&fake_key_hash(29)), &drep); + let vote_deleg_cert_wrapped = Certificate::new_vote_delegation(&vote_deleg_cert); + + let drep = DRep::new_key_hash(&fake_key_hash(30)); + let vote_reg_deleg_cert = VoteRegistrationAndDelegation::new( + &Credential::from_keyhash(&fake_key_hash(31)), + &drep, + &Coin::from(key_deposit_form_args), + ); + let vote_reg_deleg_cert_wrapped = + Certificate::new_vote_registration_and_delegation(&vote_reg_deleg_cert); + + builder.add(&committee_hot_key_dereg_cert_wrapped).unwrap(); + builder.add(&committee_hot_key_reg_cert_wrapped).unwrap(); + builder.add(&drep_reg_cert_wrapped).unwrap(); + builder.add(&cdrep_update_cert_wrapped).unwrap(); + builder.add(&genesis_key_deleg_cert_wrapped).unwrap(); + builder.add(&mir_cert_wrapped).unwrap(); + builder.add(&pool_reg_cert_wrapped).unwrap(); + builder.add(&pool_ret_cert_wrapped).unwrap(); + builder.add(&stake_vote_deleg_cert_wrapped).unwrap(); + builder.add(&stake_deleg_cert_wrapped).unwrap(); + builder.add(&stake_reg_cert_wrapped).unwrap(); + builder.add(&stake_reg_with_coint_wrapped).unwrap(); + builder.add(&stake_reg_deleg_cert_wrapped).unwrap(); + builder.add(&stake_vote_reg_deleg_cert_wrapped).unwrap(); + builder.add(&vote_deleg_cert_wrapped).unwrap(); + builder.add(&vote_reg_deleg_cert_wrapped).unwrap(); + + let builder_deposit = builder + .get_certificates_deposit(&Coin::from(pool_deposit), &Coin::from(key_deposit)) + .unwrap(); + + let expected_deposit = + Coin::from(pool_deposit + key_deposit + drep_reg_deposit + (key_deposit_form_args * 4)); + + let refund = builder + .get_certificates_refund(&Coin::from(pool_deposit), &Coin::from(key_deposit)) + .unwrap().coin; + + let expected_refund = Coin::zero(); + + assert_eq!(builder_deposit, expected_deposit); + assert_eq!(refund, expected_refund); +} + +#[test] +fn certificates_builder_refund_no_deposit_test() { + let mut builder = CertificatesBuilder::new(); + let pool_deposit = 100u64; + let key_deposit = 200u64; + let key_deposit_form_args = 201u64; + let drep_reg_deposit = 400u64; + + let committee_hot_key_dereg_cert = + CommitteeColdResign::new(&Credential::from_keyhash(&fake_key_hash(1))); + let committee_hot_key_dereg_cert_wrapped = + Certificate::new_committee_cold_resign(&committee_hot_key_dereg_cert); + + let committee_hot_key_reg_cert = CommitteeHotAuth::new( + &Credential::from_keyhash(&fake_key_hash(2)), + &Credential::from_keyhash(&fake_key_hash(3)), + ); + let committee_hot_key_reg_cert_wrapped = + Certificate::new_committee_hot_auth(&committee_hot_key_reg_cert); + + let drep_dereg_cert = DRepDeregistration::new( + &Credential::from_keyhash(&fake_key_hash(5)), + &Coin::from(drep_reg_deposit), + ); + let drep_dereg_cert_wrapped = Certificate::new_drep_deregistration(&drep_dereg_cert); + + let drep_update_cert = DRepUpdate::new(&Credential::from_keyhash(&fake_key_hash(6))); + let cdrep_update_cert_wrapped = Certificate::new_drep_update(&drep_update_cert); + + let genesis_key_deleg_cert = GenesisKeyDelegation::new( + &fake_genesis_hash(7), + &fake_genesis_delegate_hash(8), + &fake_vrf_key_hash(9), + ); + let genesis_key_deleg_cert_wrapped = + Certificate::new_genesis_key_delegation(&genesis_key_deleg_cert); + + let mir_cert = MoveInstantaneousReward::new_to_other_pot(MIRPot::Reserves, &Coin::from(100u64)); + let mir_cert_wrapped = Certificate::new_move_instantaneous_rewards_cert( + &MoveInstantaneousRewardsCert::new(&mir_cert), + ); + + let mut owners = Ed25519KeyHashes::new(); + owners.add(&fake_key_hash(11)); + owners.add(&fake_key_hash(12)); + + let pool_ret_cert = PoolRetirement::new(&fake_key_hash(16), Epoch::from(100u32)); + let pool_ret_cert_wrapped = Certificate::new_pool_retirement(&pool_ret_cert); + + let drep = DRep::new_key_hash(&fake_key_hash(17)); + let stake_vote_deleg_cert = StakeAndVoteDelegation::new( + &Credential::from_keyhash(&fake_key_hash(18)), + &fake_key_hash(19), + &drep, + ); + let stake_vote_deleg_cert_wrapped = + Certificate::new_stake_and_vote_delegation(&stake_vote_deleg_cert); + + let stake_deleg_cert = StakeDelegation::new( + &Credential::from_keyhash(&fake_key_hash(20)), + &fake_key_hash(21), + ); + let stake_deleg_cert_wrapped = Certificate::new_stake_delegation(&stake_deleg_cert); + + let stake_dereg_cert = StakeDeregistration::new(&Credential::from_keyhash(&fake_key_hash(22))); + let stake_dereg_cert_wrapped = Certificate::new_stake_deregistration(&stake_dereg_cert); + + let stake_dereg_with_coin_cert = StakeDeregistration::new_with_explicit_refund( + &Credential::from_keyhash(&fake_key_hash(22)), + &Coin::from(key_deposit_form_args), + ); + let stake_dereg_with_coin_wrapped = + Certificate::new_stake_deregistration(&stake_dereg_with_coin_cert); + + let drep = DRep::new_key_hash(&fake_key_hash(28)); + let vote_deleg_cert = VoteDelegation::new(&Credential::from_keyhash(&fake_key_hash(29)), &drep); + let vote_deleg_cert_wrapped = Certificate::new_vote_delegation(&vote_deleg_cert); + + builder.add(&committee_hot_key_dereg_cert_wrapped).unwrap(); + builder.add(&committee_hot_key_reg_cert_wrapped).unwrap(); + builder.add(&drep_dereg_cert_wrapped).unwrap(); + builder.add(&cdrep_update_cert_wrapped).unwrap(); + builder.add(&genesis_key_deleg_cert_wrapped).unwrap(); + builder.add(&mir_cert_wrapped).unwrap(); + builder.add(&pool_ret_cert_wrapped).unwrap(); + builder.add(&stake_vote_deleg_cert_wrapped).unwrap(); + builder.add(&stake_deleg_cert_wrapped).unwrap(); + builder.add(&stake_dereg_cert_wrapped).unwrap(); + builder.add(&stake_dereg_with_coin_wrapped).unwrap(); + builder.add(&vote_deleg_cert_wrapped).unwrap(); + + let builder_deposit = builder + .get_certificates_deposit(&Coin::from(pool_deposit), &Coin::from(key_deposit)) + .unwrap(); + + let expected_deposit = Coin::zero(); + + let refund = builder + .get_certificates_refund(&Coin::from(pool_deposit), &Coin::from(key_deposit)) + .unwrap().coin; + let expected_refund = Coin::from(key_deposit + key_deposit_form_args + drep_reg_deposit); + + assert_eq!(builder_deposit, expected_deposit); + assert_eq!(refund, expected_refund); +} + +#[test] +fn certificates_builder_req_signers_test() { + let mut builder = CertificatesBuilder::new(); + let key_deposit_form_args = 201u64; + let drep_reg_deposit = 400u64; + + let key_hash_1 = fake_key_hash(1); + let key_hash_2 = fake_key_hash(2); + let key_hash_3 = fake_key_hash(3); + let key_hash_4 = fake_key_hash(4); + let key_hash_5 = fake_key_hash(5); + let key_hash_6 = fake_key_hash(6); + let key_hash_8 = fake_key_hash(8); + let key_hash_10 = fake_key_hash(10); + let key_hash_11 = fake_key_hash(11); + let key_hash_12 = fake_key_hash(12); + let key_hash_13 = fake_key_hash(13); + let key_hash_15 = fake_key_hash(15); + let key_hash_16 = fake_key_hash(16); + let key_hash_17 = fake_key_hash(17); + let key_hash_18 = fake_key_hash(18); + let key_hash_19 = fake_key_hash(19); + let key_hash_20 = fake_key_hash(20); + let key_hash_21 = fake_key_hash(21); + let key_hash_22 = fake_key_hash(22); + let key_hash_23 = fake_key_hash(23); + let key_hash_24 = fake_key_hash(24); + let key_hash_25 = fake_key_hash(25); + let key_hash_26 = fake_key_hash(26); + let key_hash_27 = fake_key_hash(27); + let key_hash_28 = fake_key_hash(28); + let key_hash_29 = fake_key_hash(29); + let key_hash_30 = fake_key_hash(30); + let key_hash_31 = fake_key_hash(31); + let key_hash_32 = fake_key_hash(32); + let key_hash_33 = fake_key_hash(33); + + let committee_hot_key_dereg_cert = + CommitteeColdResign::new(&Credential::from_keyhash(&key_hash_1)); + let committee_hot_key_dereg_cert_wrapped = + Certificate::new_committee_cold_resign(&committee_hot_key_dereg_cert); + + let committee_hot_key_reg_cert = CommitteeHotAuth::new( + &Credential::from_keyhash(&key_hash_2), + &Credential::from_keyhash(&key_hash_3), + ); + let committee_hot_key_reg_cert_wrapped = + Certificate::new_committee_hot_auth(&committee_hot_key_reg_cert); + + let drep_reg_cert = DRepRegistration::new( + &Credential::from_keyhash(&key_hash_4), + &Coin::from(drep_reg_deposit), + ); + let drep_reg_cert_wrapped = Certificate::new_drep_registration(&drep_reg_cert); + + let drep_dereg_cert = DRepDeregistration::new( + &Credential::from_keyhash(&key_hash_5), + &Coin::from(drep_reg_deposit), + ); + let drep_dereg_cert_wrapped = Certificate::new_drep_deregistration(&drep_dereg_cert); + + let drep_update_cert = DRepUpdate::new(&Credential::from_keyhash(&key_hash_6)); + let cdrep_update_cert_wrapped = Certificate::new_drep_update(&drep_update_cert); + + let genesis_key_deleg_cert = GenesisKeyDelegation::new( + &fake_genesis_hash(7), + &fake_genesis_delegate_hash(8), + &fake_vrf_key_hash(9), + ); + let genesis_key_deleg_cert_wrapped = + Certificate::new_genesis_key_delegation(&genesis_key_deleg_cert); + + let mir_cert = MoveInstantaneousReward::new_to_other_pot(MIRPot::Reserves, &Coin::from(100u64)); + let mir_cert_wrapped = Certificate::new_move_instantaneous_rewards_cert( + &MoveInstantaneousRewardsCert::new(&mir_cert), + ); + + let staking_cred = Credential::from_keyhash(&key_hash_10); + let reward_address = + RewardAddress::new(NetworkInfo::testnet_preprod().network_id(), &staking_cred); + let mut owners = Ed25519KeyHashes::new(); + owners.add(&key_hash_11); + owners.add(&key_hash_12); + let relays = Relays::new(); + let matadata = PoolMetadata::new( + &URL::new("https://iohk.io".to_string()).unwrap(), + &fake_pool_metadata_hash(5), + ); + + let params = PoolParams::new( + &key_hash_13, + &fake_vrf_key_hash(14), + &Coin::from(100u64), + &Coin::from(200u64), + &UnitInterval::new(&BigNum::from(110u64), &BigNum::from(220u64)), + &reward_address, + &owners, + &relays, + Some(matadata), + ); + + let pool_reg_cert = PoolRegistration::new(¶ms); + let pool_reg_cert_wrapped = Certificate::new_pool_registration(&pool_reg_cert); + + let pool_ret_cert = PoolRetirement::new(&key_hash_15, Epoch::from(100u32)); + let pool_ret_cert_wrapped = Certificate::new_pool_retirement(&pool_ret_cert); + + let drep = DRep::new_key_hash(&key_hash_16); + let stake_vote_deleg_cert = + StakeAndVoteDelegation::new(&Credential::from_keyhash(&key_hash_17), &key_hash_18, &drep); + let stake_vote_deleg_cert_wrapped = + Certificate::new_stake_and_vote_delegation(&stake_vote_deleg_cert); + + let stake_deleg_cert = + StakeDelegation::new(&Credential::from_keyhash(&key_hash_19), &key_hash_20); + let stake_deleg_cert_wrapped = Certificate::new_stake_delegation(&stake_deleg_cert); + + let stake_dereg_cert = StakeDeregistration::new(&Credential::from_keyhash(&key_hash_21)); + let stake_dereg_cert_wrapped = Certificate::new_stake_deregistration(&stake_dereg_cert); + + let stake_dereg_with_coin_cert = StakeDeregistration::new_with_explicit_refund( + &Credential::from_keyhash(&key_hash_22), + &Coin::from(key_deposit_form_args), + ); + let stake_dereg_with_coint_wrapped = + Certificate::new_stake_deregistration(&stake_dereg_with_coin_cert); + + let stake_reg_cert = StakeRegistration::new(&Credential::from_keyhash(&key_hash_23)); + let stake_reg_cert_wrapped = Certificate::new_stake_registration(&stake_reg_cert); + + let stake_reg_with_coin_cert = StakeRegistration::new_with_explicit_deposit( + &Credential::from_keyhash(&key_hash_24), + &Coin::from(key_deposit_form_args), + ); + let stake_reg_with_coin_wrapped = + Certificate::new_stake_registration(&stake_reg_with_coin_cert); + + let stake_reg_deleg_cert = StakeRegistrationAndDelegation::new( + &Credential::from_keyhash(&key_hash_25), + &key_hash_26, + &Coin::from(key_deposit_form_args), + ); + let stake_reg_deleg_cert_wrapped = + Certificate::new_stake_registration_and_delegation(&stake_reg_deleg_cert); + + let drep = DRep::new_key_hash(&key_hash_27); + let stake_vote_reg_deleg_cert = StakeVoteRegistrationAndDelegation::new( + &Credential::from_keyhash(&key_hash_28), + &key_hash_29, + &drep, + &Coin::from(key_deposit_form_args), + ); + let stake_vote_reg_deleg_cert_wrapped = + Certificate::new_stake_vote_registration_and_delegation(&stake_vote_reg_deleg_cert); + + let drep = DRep::new_key_hash(&key_hash_30); + let vote_deleg_cert = VoteDelegation::new(&Credential::from_keyhash(&key_hash_31), &drep); + let vote_deleg_cert_wrapped = Certificate::new_vote_delegation(&vote_deleg_cert); + + let drep = DRep::new_key_hash(&key_hash_32); + let vote_reg_deleg_cert = VoteRegistrationAndDelegation::new( + &Credential::from_keyhash(&key_hash_33), + &drep, + &Coin::from(key_deposit_form_args), + ); + let vote_reg_deleg_cert_wrapped = + Certificate::new_vote_registration_and_delegation(&vote_reg_deleg_cert); + + builder.add(&committee_hot_key_dereg_cert_wrapped).unwrap(); + builder.add(&committee_hot_key_reg_cert_wrapped).unwrap(); + builder.add(&drep_reg_cert_wrapped).unwrap(); + builder.add(&drep_dereg_cert_wrapped).unwrap(); + builder.add(&cdrep_update_cert_wrapped).unwrap(); + builder.add(&genesis_key_deleg_cert_wrapped).unwrap(); + builder.add(&mir_cert_wrapped).unwrap(); + builder.add(&pool_reg_cert_wrapped).unwrap(); + builder.add(&pool_ret_cert_wrapped).unwrap(); + builder.add(&stake_vote_deleg_cert_wrapped).unwrap(); + builder.add(&stake_deleg_cert_wrapped).unwrap(); + builder.add(&stake_dereg_cert_wrapped).unwrap(); + builder.add(&stake_dereg_with_coint_wrapped).unwrap(); + builder.add(&stake_reg_cert_wrapped).unwrap(); + builder.add(&stake_reg_with_coin_wrapped).unwrap(); + builder.add(&stake_reg_deleg_cert_wrapped).unwrap(); + builder.add(&stake_vote_reg_deleg_cert_wrapped).unwrap(); + builder.add(&vote_deleg_cert_wrapped).unwrap(); + builder.add(&vote_reg_deleg_cert_wrapped).unwrap(); + + let req_signers = builder.get_required_signers(); + + assert_eq!(req_signers.len(), 19); + assert!(req_signers.contains(&key_hash_1)); + assert!(req_signers.contains(&key_hash_2)); + assert!(req_signers.contains(&key_hash_4)); + assert!(req_signers.contains(&key_hash_5)); + assert!(req_signers.contains(&key_hash_6)); + assert!(req_signers.contains(&key_hash_8)); + assert!(req_signers.contains(&key_hash_11)); + assert!(req_signers.contains(&key_hash_12)); + assert!(req_signers.contains(&key_hash_13)); + assert!(req_signers.contains(&key_hash_15)); + assert!(req_signers.contains(&key_hash_17)); + assert!(req_signers.contains(&key_hash_19)); + assert!(req_signers.contains(&key_hash_21)); + assert!(req_signers.contains(&key_hash_22)); + assert!(req_signers.contains(&key_hash_24)); + assert!(req_signers.contains(&key_hash_25)); + assert!(req_signers.contains(&key_hash_28)); + assert!(req_signers.contains(&key_hash_31)); + assert!(req_signers.contains(&key_hash_33)); +} diff --git a/rust/src/tests/builders/mint_builder.rs b/rust/src/tests/builders/mint_builder.rs new file mode 100644 index 00000000..831dac70 --- /dev/null +++ b/rust/src/tests/builders/mint_builder.rs @@ -0,0 +1,585 @@ +use crate::*; +use crate::tests::fakes::{fake_reallistic_tx_builder, fake_redeemer, fake_plutus_script_and_hash, fake_script_hash, fake_tx_input, fake_redeemer_with_tag}; + +#[test] +fn plutus_mint_with_script_ref_test() { + let mut tx_builder = fake_reallistic_tx_builder(); + let colateral_adress = Address::from_bech32("addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w").unwrap(); + let colateral_input = fake_tx_input(0); + let tx_input = fake_tx_input(1); + let tx_input_ref = fake_tx_input(2); + + let (plutus_script, _) = fake_plutus_script_and_hash(1); + + let (plutus_script2, _) = fake_plutus_script_and_hash(2); + + let redeemer = fake_redeemer(1); + + let redeemer2 = fake_redeemer(2); + + let asset_name = AssetName::from_hex("44544e4654").unwrap(); + let mut mint_builder = MintBuilder::new(); + let plutus_script_source = PlutusScriptSource::new(&plutus_script); + let plutus_script_source_ref = PlutusScriptSource::new_ref_input( + &plutus_script2.hash(), + &tx_input_ref, + &Language::new_plutus_v2(), + 0, + ); + let mint_witnes = MintWitness::new_plutus_script(&plutus_script_source, &redeemer); + let mint_witnes_ref = MintWitness::new_plutus_script(&plutus_script_source_ref, &redeemer2); + mint_builder.add_asset(&mint_witnes, &asset_name, &Int::new(&BigNum::from(100u64))).unwrap(); + mint_builder.add_asset( + &mint_witnes_ref, + &asset_name, + &Int::new(&BigNum::from(100u64)), + ).unwrap(); + + let output_adress = Address::from_bech32("addr_test1qpm5njmgzf4t7225v6j34wl30xfrufzt3jtqtdzf3en9ahpmnhtmynpasyc8fq75zv0uaj86vzsr7g3g8q5ypgu5fwtqr9zsgj").unwrap(); + let mut output_assets = MultiAsset::new(); + let mut asset = Assets::new(); + asset.insert(&asset_name, &BigNum::from(100u64)); + output_assets.insert(&plutus_script.hash(), &asset); + let output_value = Value::new_with_assets(&Coin::from(50000000u64), &output_assets); + let output = TransactionOutput::new(&output_adress, &output_value); + + let mut col_builder = TxInputsBuilder::new(); + col_builder.add_regular_input( + &colateral_adress, + &colateral_input, + &Value::new(&Coin::from(1000000000u64)), + ).unwrap(); + tx_builder.set_collateral(&col_builder); + tx_builder.add_output(&output).unwrap(); + tx_builder.add_regular_input( + &output_adress, + &tx_input, + &Value::new(&BigNum::from(100000000000u64)), + ).unwrap(); + tx_builder.set_mint_builder(&mint_builder); + + tx_builder + .calc_script_data_hash(&TxBuilderConstants::plutus_vasil_cost_models()) + .unwrap(); + + let change_res = tx_builder.add_change_if_needed(&output_adress); + assert!(change_res.is_ok()); + + let build_res = tx_builder.build_tx(); + assert!(build_res.is_ok()); + + let tx = build_res.unwrap(); + assert_eq!(tx.witness_set.plutus_scripts.unwrap().len(), 1usize); + assert_eq!(tx.witness_set.redeemers.unwrap().len(), 2usize); + assert!(tx.witness_set.plutus_data.is_none()); + assert_eq!(tx.body.reference_inputs.unwrap().len(), 1usize); + assert!(tx.body.mint.is_some()); + assert_eq!(tx.body.mint.unwrap().len(), 2usize); +} + +#[test] +fn different_redeemers_error() { + let (plutus_script, _) = fake_plutus_script_and_hash(1); + let redeemer = fake_redeemer(1); + let redeemer2 = fake_redeemer(2); + + let asset_name = AssetName::from_hex("44544e4654").unwrap(); + let mut mint_builder = MintBuilder::new(); + let plutus_script_source = PlutusScriptSource::new(&plutus_script); + let mint_witnes = MintWitness::new_plutus_script(&plutus_script_source, &redeemer); + let mint_witnes2 = MintWitness::new_plutus_script(&plutus_script_source, &redeemer2); + + let res1 = mint_builder.add_asset(&mint_witnes, &asset_name, &Int::new(&BigNum::from(100u64))); + assert!(res1.is_ok()); + + let res1 = mint_builder.add_asset(&mint_witnes2, &asset_name, &Int::new(&BigNum::from(100u64))); + assert!(res1.is_err()); +} + +#[test] +fn same_redeemers() { + let (plutus_script, _) = fake_plutus_script_and_hash(1); + let redeemer = fake_redeemer(1); + let mut redeemer2 = redeemer.clone(); + redeemer2.index = BigNum::from(77u64); + redeemer2.tag = RedeemerTag::new_voting_proposal(); + + let asset_name1 = AssetName::from_hex("44544e4654").unwrap(); + let asset_name2 = AssetName::from_hex("44544e4655").unwrap(); + let mut mint_builder = MintBuilder::new(); + let plutus_script_source = PlutusScriptSource::new(&plutus_script); + let mint_witnes = MintWitness::new_plutus_script(&plutus_script_source, &redeemer); + let mint_witnes2 = MintWitness::new_plutus_script(&plutus_script_source, &redeemer2); + + let res1 = mint_builder.add_asset(&mint_witnes, &asset_name1, &Int::new(&BigNum::from(100u64))); + assert!(res1.is_ok()); + + let res1 = mint_builder.add_asset(&mint_witnes2, &asset_name2, &Int::new(&BigNum::from(100u64))); + assert!(res1.is_ok()); +} + +#[test] +fn plutus_mint_test() { + let mut tx_builder = fake_reallistic_tx_builder(); + let colateral_adress = Address::from_bech32("addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w").unwrap(); + let colateral_input = fake_tx_input(0); + + let tx_input = fake_tx_input(1); + let (plutus_script, _) = fake_plutus_script_and_hash(1); + + let redeemer = fake_redeemer(1); + let asset_name = AssetName::from_hex("44544e4654").unwrap(); + let mut mint_builder = MintBuilder::new(); + let plutus_script_source = PlutusScriptSource::new(&plutus_script); + let mint_witnes = MintWitness::new_plutus_script(&plutus_script_source, &redeemer); + mint_builder.add_asset(&mint_witnes, &asset_name, &Int::new(&BigNum::from(100u64))).unwrap(); + + let output_adress = Address::from_bech32("addr_test1qpm5njmgzf4t7225v6j34wl30xfrufzt3jtqtdzf3en9ahpmnhtmynpasyc8fq75zv0uaj86vzsr7g3g8q5ypgu5fwtqr9zsgj").unwrap(); + let mut output_assets = MultiAsset::new(); + let mut asset = Assets::new(); + asset.insert(&asset_name, &BigNum::from(100u64)); + output_assets.insert(&plutus_script.hash(), &asset); + let output_value = Value::new_with_assets(&Coin::from(5000000u64), &output_assets); + let output = TransactionOutput::new(&output_adress, &output_value); + + let mut col_builder = TxInputsBuilder::new(); + col_builder.add_regular_input( + &colateral_adress, + &colateral_input, + &Value::new(&Coin::from(1000000000000u64)), + ).unwrap(); + tx_builder.set_collateral(&col_builder); + tx_builder.add_output(&output).unwrap(); + tx_builder.add_regular_input( + &output_adress, + &tx_input, + &Value::new(&BigNum::from(100000000000000u64)), + ).unwrap(); + tx_builder.set_mint_builder(&mint_builder); + + tx_builder + .calc_script_data_hash(&TxBuilderConstants::plutus_vasil_cost_models()) + .unwrap(); + + let change_res = tx_builder.add_change_if_needed(&output_adress); + assert!(change_res.is_ok()); + + let build_res = tx_builder.build_tx(); + assert!(build_res.is_ok()); + + assert_eq!(mint_builder.get_plutus_witnesses().len(), 1); + + let tx = build_res.unwrap(); + assert!(tx.body.mint.is_some()); + assert_eq!( + tx.body.mint.unwrap().0.iter().next().unwrap().0, + plutus_script.hash() + ); +} + +#[test] +fn ref_inputs() { + let script_hash_1 = fake_script_hash(1); + let script_hash_2 = fake_script_hash(2); + let tx_input_ref1 = fake_tx_input(2); + let tx_input_ref2 = fake_tx_input(3); + let redeemer = fake_redeemer(1); + + let asset_name1 = AssetName::from_hex("44544e4654").unwrap(); + let asset_name2 = AssetName::from_hex("44544e4655").unwrap(); + + let mut mint_builder = MintBuilder::new(); + let plutus_script_source = PlutusScriptSource::new_ref_input( + &script_hash_1, + &tx_input_ref1, + &Language::new_plutus_v2(), + 0, + ); + let native_script_source = NativeScriptSource::new_ref_input( + &script_hash_2, + &tx_input_ref2, + 0, + ); + + let mint_witnes = MintWitness::new_plutus_script(&plutus_script_source, &redeemer); + let mint_witnes2 = MintWitness::new_native_script(&native_script_source); + + mint_builder.add_asset(&mint_witnes, &asset_name1, &Int::new(&BigNum::from(100u64))).unwrap(); + mint_builder.add_asset(&mint_witnes2, &asset_name2, &Int::new(&BigNum::from(100u64))).unwrap(); + + let ref_inputs = mint_builder.get_ref_inputs(); + + assert_eq!(ref_inputs.len(), 2); + assert!(ref_inputs.contains(&tx_input_ref1)); + assert!(ref_inputs.contains(&tx_input_ref2)); +} + +#[test] +fn multiple_mints() { + let script_hash_1 = fake_script_hash(1); + let script_hash_2 = fake_script_hash(2); + let tx_input_ref1 = fake_tx_input(2); + let tx_input_ref2 = fake_tx_input(3); + let redeemer = fake_redeemer(1); + + let asset_name1 = AssetName::from_hex("44544e4654").unwrap(); + let asset_name2 = AssetName::from_hex("44544e4655").unwrap(); + let asset_name3 = AssetName::from_hex("44544e4656").unwrap(); + + let mut mint_builder = MintBuilder::new(); + let plutus_script_source = PlutusScriptSource::new_ref_input( + &script_hash_1, + &tx_input_ref1, + &Language::new_plutus_v2(), + 0, + ); + + let native_script_source = NativeScriptSource::new_ref_input( + &script_hash_2, + &tx_input_ref2, + 0, + ); + + let mint_witnes = MintWitness::new_plutus_script(&plutus_script_source, &redeemer); + let mint_witnes2 = MintWitness::new_native_script(&native_script_source); + + mint_builder.add_asset(&mint_witnes, &asset_name1, &Int::new(&BigNum::from(100u64))).unwrap(); + mint_builder.add_asset(&mint_witnes2, &asset_name2, &Int::new(&BigNum::from(101u64))).unwrap(); + mint_builder.add_asset(&mint_witnes2, &asset_name3, &Int::new(&BigNum::from(102u64))).unwrap(); + + let mint = mint_builder.build().expect("Failed to build mint"); + assert_eq!(mint.len(), 2); + + let policy_mints_list = mint.get(&script_hash_1).unwrap(); + let policy_mints_list2 = mint.get(&script_hash_2).unwrap(); + + assert_eq!(policy_mints_list.len(), 1); + assert_eq!(policy_mints_list2.len(), 1); + + let policy_mints = policy_mints_list.get(0).unwrap(); + let policy_mints2 = policy_mints_list2.get(0).unwrap(); + + assert_eq!(policy_mints.len(), 1); + assert_eq!(policy_mints2.len(), 2); + + assert_eq!(policy_mints.get(&asset_name1).unwrap().to_str(), "100"); + assert_eq!(policy_mints2.get(&asset_name2).unwrap().to_str(), "101"); + assert_eq!(policy_mints2.get(&asset_name3).unwrap().to_str(), "102"); +} + +#[test] +fn native_script_mint() { + let native_script = NativeScript::new_timelock_start( + &TimelockStart::new_timelockstart(&BigNum::from(100u64)), + ); + let script_hash = native_script.hash(); + + let asset_name1 = AssetName::from_hex("44544e4654").unwrap(); + + let mut mint_builder = MintBuilder::new(); + + let native_script_source = NativeScriptSource::new(&native_script); + let mint_witnes = MintWitness::new_native_script(&native_script_source); + mint_builder.add_asset(&mint_witnes, &asset_name1, &Int::new(&BigNum::from(100u64))).unwrap(); + + let mint = mint_builder.build().expect("Failed to build mint"); + assert_eq!(mint.len(), 1); + + let policy_mints_list = mint.get(&script_hash).unwrap(); + assert_eq!(policy_mints_list.len(), 1); + + let policy_mints = policy_mints_list.get(0).unwrap(); + assert_eq!(policy_mints.len(), 1); + assert_eq!(policy_mints.get(&asset_name1).unwrap().to_str(), "100"); + + let native_scripts = mint_builder.get_native_scripts(); + assert_eq!(native_scripts.len(), 1); + + assert_eq!(native_scripts.get(0), native_script); +} + +#[test] +fn different_script_type_error() { + let script_hash_1 = fake_script_hash(1); + let script_hash_2 = fake_script_hash(2); + let tx_input_ref1 = fake_tx_input(2); + let tx_input_ref2 = fake_tx_input(3); + let redeemer = fake_redeemer(1); + + let asset_name1 = AssetName::from_hex("44544e4654").unwrap(); + let asset_name2 = AssetName::from_hex("44544e4655").unwrap(); + + let mut mint_builder = MintBuilder::new(); + let plutus_script_source1 = PlutusScriptSource::new_ref_input( + &script_hash_1, + &tx_input_ref1, + &Language::new_plutus_v2(), + 0, + ); + let plutus_script_source2 = PlutusScriptSource::new_ref_input( + &script_hash_2, + &tx_input_ref2, + &Language::new_plutus_v2(), + 0, + ); + + let native_script_source1 = NativeScriptSource::new_ref_input( + &script_hash_2, + &tx_input_ref2, + 0, + ); + let native_script_source2 = NativeScriptSource::new_ref_input( + &script_hash_1, + &tx_input_ref1, + 0, + ); + + let mint_witnes_plutus_1 = MintWitness::new_plutus_script(&plutus_script_source1, &redeemer); + let mint_witnes_plutus_2 = MintWitness::new_plutus_script(&plutus_script_source2, &redeemer); + let mint_witnes_native_1 = MintWitness::new_native_script(&native_script_source1); + let mint_witnes_native_2 = MintWitness::new_native_script(&native_script_source2); + + mint_builder.add_asset(&mint_witnes_plutus_1, &asset_name1, &Int::new(&BigNum::from(100u64))).unwrap(); + mint_builder.add_asset(&mint_witnes_native_1, &asset_name2, &Int::new(&BigNum::from(101u64))).unwrap(); + + let res = mint_builder.add_asset(&mint_witnes_plutus_2, &asset_name1, &Int::new(&BigNum::from(100u64))); + assert!(res.is_err()); + + let res = mint_builder.add_asset(&mint_witnes_native_2, &asset_name2, &Int::new(&BigNum::from(101u64))); + assert!(res.is_err()); +} + +#[test] +fn wrong_witness_type_ref_error() { + let native_script = NativeScript::new_timelock_start( + &TimelockStart::new_timelockstart(&BigNum::from(100u64)), + ); + let (plutus_script, script_hash_1) = fake_plutus_script_and_hash(5); + let script_hash_2 = native_script.hash(); + let tx_input_ref1 = fake_tx_input(2); + let tx_input_ref2 = fake_tx_input(3); + let redeemer = fake_redeemer(1); + + let asset_name1 = AssetName::from_hex("44544e4654").unwrap(); + let asset_name2 = AssetName::from_hex("44544e4655").unwrap(); + let asset_name3 = AssetName::from_hex("44544e4656").unwrap(); + let asset_name4 = AssetName::from_hex("44544e4657").unwrap(); + + let mut mint_builder = MintBuilder::new(); + + let plutus_script_source_1 = PlutusScriptSource::new_ref_input( + &script_hash_1, + &tx_input_ref1, + &Language::new_plutus_v2(), + 0, + ); + let plutus_script_source_2 = PlutusScriptSource::new(&plutus_script); + + let native_script_source_1 = NativeScriptSource::new_ref_input( + &script_hash_2, + &tx_input_ref2, + 0, + ); + let native_script_source_2 = NativeScriptSource::new(&native_script); + + let mint_witness_plutus_1 = MintWitness::new_plutus_script(&plutus_script_source_1, &redeemer); + let mint_witness_plutus_2 = MintWitness::new_plutus_script(&plutus_script_source_2, &redeemer); + + let mint_witness_native_1 = MintWitness::new_native_script(&native_script_source_1); + let mint_witness_native_2 = MintWitness::new_native_script(&native_script_source_2); + + let res1 = mint_builder.add_asset(&mint_witness_plutus_1, &asset_name1, &Int::new(&BigNum::from(100u64))); + assert!(res1.is_ok()); + + let res2 = mint_builder.add_asset(&mint_witness_plutus_2, &asset_name2, &Int::new(&BigNum::from(101u64))); + assert!(res2.is_err()); + + let res3 = mint_builder.add_asset(&mint_witness_native_1, &asset_name3, &Int::new(&BigNum::from(102u64))); + assert!(res3.is_ok()); + + let res4= mint_builder.add_asset(&mint_witness_native_2, &asset_name4, &Int::new(&BigNum::from(103u64))); + assert!(res4.is_err()); + + let mint = mint_builder.build().expect("Failed to build mint"); + assert_eq!(mint.len(), 2); + + let policy_mints_list = mint.get(&script_hash_1).unwrap(); + let policy_mints_list2 = mint.get(&script_hash_2).unwrap(); + + assert_eq!(policy_mints_list.len(), 1); + assert_eq!(policy_mints_list2.len(), 1); + + let policy_mints = policy_mints_list.get(0).unwrap(); + let policy_mints2 = policy_mints_list2.get(0).unwrap(); + + assert_eq!(policy_mints.len(), 1); + assert_eq!(policy_mints2.len(), 1); + + assert_eq!(policy_mints.get(&asset_name1).unwrap().to_str(), "100"); + assert_eq!(policy_mints2.get(&asset_name3).unwrap().to_str(), "102"); +} + +#[test] +fn wrong_witness_type_no_ref_error() { + let native_script = NativeScript::new_timelock_start( + &TimelockStart::new_timelockstart(&BigNum::from(100u64)), + ); + let (plutus_script, script_hash_1) = fake_plutus_script_and_hash(5); + let script_hash_2 = native_script.hash(); + let tx_input_ref1 = fake_tx_input(2); + let tx_input_ref2 = fake_tx_input(3); + let redeemer = fake_redeemer(1); + + let asset_name1 = AssetName::from_hex("44544e4654").unwrap(); + let asset_name2 = AssetName::from_hex("44544e4655").unwrap(); + let asset_name3 = AssetName::from_hex("44544e4656").unwrap(); + let asset_name4 = AssetName::from_hex("44544e4657").unwrap(); + + let mut mint_builder = MintBuilder::new(); + + let plutus_script_source_1 = PlutusScriptSource::new_ref_input( + &script_hash_1, + &tx_input_ref1, + &Language::new_plutus_v2(), + 0, + ); + let plutus_script_source_2 = PlutusScriptSource::new(&plutus_script); + + let native_script_source_1 = NativeScriptSource::new_ref_input( + &script_hash_2, + &tx_input_ref2, + 0, + ); + let native_script_source_2 = NativeScriptSource::new(&native_script); + + let mint_witness_plutus_1 = MintWitness::new_plutus_script(&plutus_script_source_2, &redeemer); + let mint_witness_plutus_2 = MintWitness::new_plutus_script(&plutus_script_source_1, &redeemer); + + let mint_witness_native_1 = MintWitness::new_native_script(&native_script_source_2); + let mint_witness_native_2 = MintWitness::new_native_script(&native_script_source_1); + + let res1 = mint_builder.add_asset(&mint_witness_plutus_1, &asset_name1, &Int::new(&BigNum::from(100u64))); + assert!(res1.is_ok()); + + let res2 = mint_builder.add_asset(&mint_witness_plutus_2, &asset_name2, &Int::new(&BigNum::from(101u64))); + assert!(res2.is_err()); + + let res3 = mint_builder.add_asset(&mint_witness_native_1, &asset_name3, &Int::new(&BigNum::from(102u64))); + assert!(res3.is_ok()); + + let res4= mint_builder.add_asset(&mint_witness_native_2, &asset_name4, &Int::new(&BigNum::from(103u64))); + assert!(res4.is_err()); + + let mint = mint_builder.build().expect("Failed to build mint"); + assert_eq!(mint.len(), 2); + + let policy_mints_list = mint.get(&script_hash_1).unwrap(); + let policy_mints_list2 = mint.get(&script_hash_2).unwrap(); + + assert_eq!(policy_mints_list.len(), 1); + assert_eq!(policy_mints_list2.len(), 1); + + let policy_mints = policy_mints_list.get(0).unwrap(); + let policy_mints2 = policy_mints_list2.get(0).unwrap(); + + assert_eq!(policy_mints.len(), 1); + assert_eq!(policy_mints2.len(), 1); + + assert_eq!(policy_mints.get(&asset_name1).unwrap().to_str(), "100"); + assert_eq!(policy_mints2.get(&asset_name3).unwrap().to_str(), "102"); +} + +#[test] +fn zero_mint_error() { + let native_script = NativeScript::new_timelock_start( + &TimelockStart::new_timelockstart(&BigNum::from(100u64)), + ); + let asset_name1 = AssetName::from_hex("44544e4654").unwrap(); + let mut mint_builder = MintBuilder::new(); + + let native_script_source = NativeScriptSource::new(&native_script); + + + let mint_witness_native = MintWitness::new_native_script(&native_script_source); + + let res= mint_builder.add_asset(&mint_witness_native, &asset_name1, &Int::new(&BigNum::from(0u64))); + assert!(res.is_err()); +} + +#[test] +fn redeemer_tag_index_test() { + let bytes1 = vec![3u8; 28]; + let bytes2 = vec![1u8; 28]; + let bytes3 = vec![2u8; 28]; + let script_hash_1 = ScriptHash::from_bytes(bytes1).expect("Failed to create script hash"); + let script_hash_2 = ScriptHash::from_bytes(bytes2).expect("Failed to create script hash"); + let script_hash_3 = ScriptHash::from_bytes(bytes3).expect("Failed to create script hash"); + let tx_input_ref1 = fake_tx_input(2); + let tx_input_ref2 = fake_tx_input(3); + let tx_input_ref3 = fake_tx_input(4); + let data_1 = PlutusData::new_empty_constr_plutus_data(&BigNum::from(1u64)); + let data_2 = PlutusData::new_empty_constr_plutus_data(&BigNum::from(2u64)); + let data_3 = PlutusData::new_empty_constr_plutus_data(&BigNum::from(3u64)); + let redeemer1 = fake_redeemer_with_tag(99, &RedeemerTag::new_vote(), &data_1); + let redeemer2 = fake_redeemer_with_tag(98, &RedeemerTag::new_cert(), &data_2); + let redeemer3 = fake_redeemer_with_tag(97, &RedeemerTag::new_spend(), &data_3); + + let asset_name1 = AssetName::from_hex("44544e4654").unwrap(); + let asset_name2 = AssetName::from_hex("44544e4655").unwrap(); + let asset_name3 = AssetName::from_hex("44544e4656").unwrap(); + + let mut mint_builder = MintBuilder::new(); + + let plutus_script_source1 = PlutusScriptSource::new_ref_input( + &script_hash_1, + &tx_input_ref1, + &Language::new_plutus_v2(), + 0 + ); + let plutus_script_source2 = PlutusScriptSource::new_ref_input( + &script_hash_2, + &tx_input_ref2, + &Language::new_plutus_v2(), + 0 + ); + + let plutus_script_source3 = PlutusScriptSource::new_ref_input( + &script_hash_3, + &tx_input_ref3, + &Language::new_plutus_v2(), + 0 + ); + + let mint_witnes1 = MintWitness::new_plutus_script(&plutus_script_source1, &redeemer1); + let mint_witnes2 = MintWitness::new_plutus_script(&plutus_script_source2, &redeemer2); + let mint_witnes3 = MintWitness::new_plutus_script(&plutus_script_source3, &redeemer3); + + mint_builder.add_asset(&mint_witnes1, &asset_name1, &Int::new(&BigNum::from(100u64))).unwrap(); + mint_builder.add_asset(&mint_witnes2, &asset_name2, &Int::new(&BigNum::from(101u64))).unwrap(); + mint_builder.add_asset(&mint_witnes3, &asset_name3, &Int::new(&BigNum::from(102u64))).unwrap(); + + let mint = mint_builder.build().expect("Failed to build mint"); + assert_eq!(mint.len(), 3); + + let policy_ids = mint.keys(); + + let first_index = policy_ids.0.iter().position(|x| *x == script_hash_1).unwrap(); + let second_index = policy_ids.0.iter().position(|x| *x == script_hash_2).unwrap(); + let third_index = policy_ids.0.iter().position(|x| *x == script_hash_3).unwrap(); + + let plutus_witnesses = mint_builder.get_plutus_witnesses(); + let redeemers = mint_builder.get_redeemers().expect("Failed to get redeemers"); + assert_eq!(plutus_witnesses.len(), 3); + + let first_redeemer = redeemers.redeemers.iter().find(|x| x.data == data_1).unwrap(); + let second_redeemer = redeemers.redeemers.iter().find(|x| x.data == data_2).unwrap(); + let third_redeemer = redeemers.redeemers.iter().find(|x| x.data == data_3).unwrap(); + + assert_eq!(first_redeemer.tag(), RedeemerTag::new_mint()); + assert_eq!(second_redeemer.tag(), RedeemerTag::new_mint()); + assert_eq!(third_redeemer.tag(), RedeemerTag::new_mint()); + + assert_eq!(first_redeemer.index(), BigNum::from(first_index as u64)); + assert_eq!(second_redeemer.index(), BigNum::from(second_index as u64)); + assert_eq!(third_redeemer.index(), BigNum::from(third_index as u64)); +} \ No newline at end of file diff --git a/rust/src/tests/builders/mod.rs b/rust/src/tests/builders/mod.rs new file mode 100644 index 00000000..0a2547f3 --- /dev/null +++ b/rust/src/tests/builders/mod.rs @@ -0,0 +1,8 @@ +mod batch_tools; +mod tx_builder; +mod voting_builder; +mod voting_proposal_builder; +mod certificates_builder; +mod mint_builder; +mod tx_inputs_builder; +mod tx_builder_constans; \ No newline at end of file diff --git a/rust/src/tests/builders/tx_builder.rs b/rust/src/tests/builders/tx_builder.rs new file mode 100644 index 00000000..22c2b8f4 --- /dev/null +++ b/rust/src/tests/builders/tx_builder.rs @@ -0,0 +1,6438 @@ +use crate::tests::helpers::harden; +use crate::tests::fakes::{fake_byron_address, fake_anchor, fake_change_address, fake_default_tx_builder, fake_linear_fee, fake_reallistic_tx_builder, fake_redeemer, fake_redeemer_zero_cost, fake_rich_tx_builder, fake_tx_builder, fake_tx_builder_with_amount, fake_tx_builder_with_fee, fake_tx_builder_with_fee_and_pure_change, fake_tx_builder_with_fee_and_val_size, fake_tx_builder_with_key_deposit, fake_base_address, fake_bytes_32, fake_data_hash, fake_key_hash, fake_plutus_script_and_hash, fake_policy_id, fake_script_hash, fake_tx_hash, fake_tx_input, fake_tx_input2, fake_value, fake_value2, fake_vkey_witness, fake_root_key_15, fake_base_address_with_payment_cred}; +use crate::*; + +use crate::builders::fakes::fake_private_key; +use fees::*; +use std::collections::{BTreeMap, HashMap, HashSet}; + +const MAX_TX_SIZE: u32 = 8000; + +fn genesis_id() -> TransactionHash { + TransactionHash::from([0u8; TransactionHash::BYTE_COUNT]) +} + +#[test] +fn check_fake_private_key() { + let fpk = fake_private_key(); + assert_eq!( + fpk.to_bech32(), + "xprv1hretan5mml3tq2p0twkhq4tz4jvka7m2l94kfr6yghkyfar6m9wppc7h9unw6p65y23kakzct3695rs32z7vaw3r2lg9scmfj8ec5du3ufydu5yuquxcz24jlkjhsc9vsa4ufzge9s00fn398svhacse5su2awrw", + ); + assert_eq!( + fpk.to_public().to_bech32(), + "xpub1eamrnx3pph58yr5l4z2wghjpu2dt2f0rp0zq9qquqa39p52ct0xercjgmegfcpcdsy4t9ld90ps2epmtcjy3jtq77n8z20qe0m3pnfqntgrgj", + ); +} + +#[test] +fn build_tx_with_change() { + let mut tx_builder = fake_default_tx_builder(); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + tx_builder.add_key_input( + &spend.to_raw_key().hash(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_coin(&BigNum(222)) + .build() + .unwrap(), + ) + .unwrap(); + tx_builder.set_ttl(1000); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + let added_change = tx_builder.add_change_if_needed(&change_addr); + assert!(added_change.unwrap()); + assert_eq!(tx_builder.outputs.len(), 2); + assert_eq!( + tx_builder + .get_explicit_input() + .unwrap() + .checked_add(&tx_builder.get_implicit_input().unwrap()) + .unwrap(), + tx_builder + .get_explicit_output() + .unwrap() + .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) + .unwrap() + ); + assert_eq!(tx_builder.full_size().unwrap(), 285); + assert_eq!(tx_builder.output_sizes(), vec![62, 65]); + let _final_tx = tx_builder.build(); // just test that it doesn't throw +} + +#[test] +fn build_tx_with_change_with_datum() { + let mut tx_builder = fake_default_tx_builder(); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + tx_builder.add_key_input( + &spend.to_raw_key().hash(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_coin(&BigNum(222)) + .build() + .unwrap(), + ) + .unwrap(); + tx_builder.set_ttl(1000); + + let datum_hash = fake_data_hash(20); + let data_option = OutputDatum::new_data_hash(&datum_hash); + let (_, script_hash) = fake_plutus_script_and_hash(15); + let change_cred = Credential::from_scripthash(&script_hash); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + let added_change = tx_builder.add_change_if_needed_with_datum(&change_addr, &data_option); + assert!(added_change.unwrap()); + assert_eq!(tx_builder.outputs.len(), 2); + assert_eq!( + tx_builder + .get_explicit_input() + .unwrap() + .checked_add(&tx_builder.get_implicit_input().unwrap()) + .unwrap(), + tx_builder + .get_explicit_output() + .unwrap() + .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) + .unwrap() + ); + assert_eq!(tx_builder.full_size().unwrap(), 319); + assert_eq!(tx_builder.output_sizes(), vec![62, 99]); + let _final_tx = tx_builder.build(); // just test that it doesn't throw +} + +#[test] +fn build_tx_without_change() { + let mut tx_builder = fake_default_tx_builder(); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + tx_builder.add_key_input( + &spend.to_raw_key().hash(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_coin(&BigNum(880_000)) + .build() + .unwrap(), + ) + .unwrap(); + tx_builder.set_ttl(1000); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + let added_change = tx_builder.add_change_if_needed(&change_addr); + assert!(!added_change.unwrap()); + assert_eq!(tx_builder.outputs.len(), 1); + assert_eq!( + tx_builder + .get_explicit_input() + .unwrap() + .checked_add(&tx_builder.get_implicit_input().unwrap()) + .unwrap(), + tx_builder + .get_explicit_output() + .unwrap() + .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) + .unwrap() + ); + let _final_tx = tx_builder.build(); // just test that it doesn't throw +} + +#[test] +fn build_tx_with_certs() { + let mut tx_builder = fake_tx_builder_with_key_deposit(1_000_000); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + tx_builder.add_key_input( + &spend.to_raw_key().hash(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(5_000_000)), + ); + tx_builder.set_ttl(1000); + + let mut certs = Certificates::new(); + certs.add(&Certificate::new_stake_registration( + &StakeRegistration::new(&stake_cred), + )); + certs.add(&Certificate::new_stake_delegation(&StakeDelegation::new( + &stake_cred, + &stake.to_raw_key().hash(), // in reality, this should be the pool owner's key, not ours + ))); + tx_builder.set_certs(&certs).unwrap(); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert_eq!(tx_builder.min_fee().unwrap().to_str(), "214002"); + assert_eq!(tx_builder.get_fee_if_set().unwrap().to_str(), "214002"); + assert_eq!(tx_builder.get_deposit().unwrap().to_str(), "1000000"); + assert_eq!(tx_builder.outputs.len(), 1); + assert_eq!( + tx_builder + .get_explicit_input() + .unwrap() + .checked_add(&tx_builder.get_implicit_input().unwrap()) + .unwrap(), + tx_builder + .get_explicit_output() + .unwrap() + .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) + .unwrap() + .checked_add(&Value::new(&tx_builder.get_deposit().unwrap())) + .unwrap() + ); + let _final_tx = tx_builder.build(); // just test that it doesn't throw +} + +#[test] +fn build_tx_exact_amount() { + // transactions where sum(input) == sum(output) exact should pass + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(0, 0)); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + tx_builder.add_key_input( + &&spend.to_raw_key().hash(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(222)), + ); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_coin(&BigNum(222)) + .build() + .unwrap(), + ) + .unwrap(); + tx_builder.set_ttl(0); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert_eq!(added_change, false); + let final_tx = tx_builder.build().unwrap(); + assert_eq!(final_tx.outputs().len(), 1); +} + +#[test] +fn build_tx_exact_change() { + // transactions where we have exactly enough ADA to add change should pass + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(0, 0)); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + tx_builder.add_key_input( + &&spend.to_raw_key().hash(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(700)), + ); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_coin(&BigNum(222)) + .build() + .unwrap(), + ) + .unwrap(); + tx_builder.set_ttl(0); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert_eq!(added_change, true); + let final_tx = tx_builder.build().unwrap(); + assert_eq!(final_tx.outputs().len(), 2); + assert_eq!(final_tx.outputs().get(1).amount().coin().to_str(), "478"); +} + +#[test] +#[should_panic] +fn build_tx_insufficient_deposit() { + // transactions should fail with insufficient fees if a deposit is required + let mut tx_builder = fake_tx_builder_with_key_deposit(5); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + tx_builder.add_key_input( + &&spend.to_raw_key().hash(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(5)), + ); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_coin(&BigNum(5)) + .build() + .unwrap(), + ) + .unwrap(); + tx_builder.set_ttl(0); + + // add a cert which requires a deposit + let mut certs = Certificates::new(); + certs.add(&Certificate::new_stake_registration( + &StakeRegistration::new(&stake_cred), + )); + tx_builder.set_certs(&certs).expect("Failed to set certs"); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + + tx_builder.add_change_if_needed(&change_addr).unwrap(); +} + +#[test] +fn build_tx_with_inputs() { + let mut tx_builder = fake_default_tx_builder(); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + + { + assert_eq!( + tx_builder + .fee_for_input( + &EnterpriseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred + ) + .to_address(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)) + ) + .unwrap() + .to_str(), + "69500" + ); + tx_builder.add_regular_input( + &EnterpriseAddress::new(NetworkInfo::testnet_preprod().network_id(), &spend_cred) + .to_address(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ).expect("Failed to add input"); + } + tx_builder.add_regular_input( + &BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(), + &TransactionInput::new(&genesis_id(), 1), + &Value::new(&BigNum(1_000_000)), + ).expect("Failed to add input"); + tx_builder.add_regular_input( + &PointerAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &Pointer::new_pointer(&BigNum(0), &BigNum(0), &BigNum(0)), + ) + .to_address(), + &TransactionInput::new(&genesis_id(), 2), + &Value::new(&BigNum(1_000_000)), + ).expect("Failed to add input"); + tx_builder.add_regular_input( + &ByronAddress::icarus_from_key(&spend, NetworkInfo::testnet_preprod().protocol_magic()) + .to_address(), + &TransactionInput::new(&genesis_id(), 3), + &Value::new(&BigNum(1_000_000)), + ).expect("Failed to add input"); + + assert_eq!(tx_builder.inputs.len(), 4); +} + +#[test] +fn add_ref_inputs_to_builder() { + let mut tx_builder = fake_default_tx_builder(); + + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 1)); + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 2)); + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 3)); + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 4)); + + assert_eq!(tx_builder.reference_inputs.len(), 4); +} + +#[test] +fn build_tx_with_script_ref() { + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(0, 1)); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 1)); + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 2)); + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 3)); + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 4)); + + tx_builder.add_regular_input( + &PointerAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &Pointer::new_pointer(&BigNum(0), &BigNum(0), &BigNum(0)), + ) + .to_address(), + &TransactionInput::new(&genesis_id(), 2), + &Value::new(&BigNum(1_000_000)), + ).expect("Failed to add input"); + + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + + let output_amount = Value::new(&BigNum(500)); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_value(&output_amount) + .build() + .unwrap(), + ) + .unwrap(); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + + let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert_eq!(added_change, true); + let final_tx = tx_builder.build().unwrap(); + assert_eq!(final_tx.outputs().len(), 2); + assert_eq!(final_tx.reference_inputs().unwrap().len(), 4); + assert_eq!(final_tx.outputs().get(1).amount().coin(), BigNum(999499)); +} + +#[test] +fn serialization_tx_body_with_script_ref() { + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(0, 1)); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 1)); + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 2)); + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 3)); + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 4)); + + tx_builder.add_regular_input( + &PointerAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &Pointer::new_pointer(&BigNum(0), &BigNum(0), &BigNum(0)), + ) + .to_address(), + &TransactionInput::new(&genesis_id(), 2), + &Value::new(&BigNum(1_000_000)), + ).expect("Failed to add input"); + + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + + let output_amount = Value::new(&BigNum(500)); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_value(&output_amount) + .build() + .unwrap(), + ) + .unwrap(); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + + tx_builder.add_change_if_needed(&change_addr).unwrap(); + let final_tx = tx_builder.build().unwrap(); + + let deser_t = TransactionBody::from_bytes(final_tx.to_bytes()).unwrap(); + + assert_eq!(deser_t.to_bytes(), final_tx.to_bytes()); +} + +#[test] +fn json_serialization_tx_body_with_script_ref() { + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(0, 1)); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 1)); + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 2)); + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 3)); + tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 4)); + + tx_builder.add_regular_input( + &PointerAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &Pointer::new_pointer(&BigNum(0), &BigNum(0), &BigNum(0)), + ) + .to_address(), + &TransactionInput::new(&genesis_id(), 2), + &Value::new(&BigNum(1_000_000)), + ).expect("Failed to add input"); + + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + + let output_amount = Value::new(&BigNum(500)); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_value(&output_amount) + .build() + .unwrap(), + ) + .unwrap(); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + + tx_builder.add_change_if_needed(&change_addr).unwrap(); + let final_tx = tx_builder.build().unwrap(); + + let json_tx_body = final_tx.to_json().unwrap(); + let deser_t = TransactionBody::from_json(json_tx_body.as_str()).unwrap(); + + assert_eq!(deser_t.to_bytes(), final_tx.to_bytes()); + assert_eq!(deser_t.to_json().unwrap(), final_tx.to_json().unwrap()); +} + +#[test] +fn build_tx_with_mint_all_sent() { + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(0, 1)); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + + // Input with 150 coins + tx_builder.add_regular_input( + &EnterpriseAddress::new(NetworkInfo::testnet_preprod().network_id(), &spend_cred) + .to_address(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(500)), + ).expect("Failed to add input"); + + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + + let (min_script, policy_id) = mint_script_and_policy(0); + let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + let amount = BigNum(1234); + + // Adding mint of the asset - which should work as an input + tx_builder.add_mint_asset(&min_script, &name, &Int::new(&amount)).expect("Failed to add mint"); + + let mut ass = Assets::new(); + ass.insert(&name, &amount); + let mut mass = MultiAsset::new(); + mass.insert(&policy_id, &ass); + + // One coin and the minted asset goes into the output + let mut output_amount = Value::new(&BigNum(264)); + output_amount.set_multiasset(&mass); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_value(&output_amount) + .build() + .unwrap(), + ) + .unwrap(); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + + let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert!(added_change); + assert_eq!(tx_builder.outputs.len(), 2); + + // Change must be one remaining coin because fee is one constant coin + let change = tx_builder.outputs.get(1).amount(); + assert_eq!(change.coin(), BigNum(235)); + assert!(change.multiasset().is_none()); +} + +#[test] +fn build_tx_with_mint_in_change() { + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(0, 1)); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + + // Input with 600 coins + tx_builder.add_regular_input( + &EnterpriseAddress::new(NetworkInfo::testnet_preprod().network_id(), &spend_cred) + .to_address(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(600)), + ).expect("Failed to add input"); + + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + + let (min_script, policy_id) = mint_script_and_policy(0); + let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + + let amount_minted = BigNum(1000); + let amount_sent = BigNum(500); + + // Adding mint of the asset - which should work as an input + tx_builder.add_mint_asset(&min_script, &name, &Int::new(&amount_minted)).expect("Failed to add mint"); + + let mut ass = Assets::new(); + ass.insert(&name, &amount_sent); + let mut mass = MultiAsset::new(); + mass.insert(&policy_id, &ass); + + // One coin and the minted asset goes into the output + let mut output_amount = Value::new(&BigNum(300)); + output_amount.set_multiasset(&mass); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_value(&output_amount) + .build() + .unwrap(), + ) + .unwrap(); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + + let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert!(added_change); + assert_eq!(tx_builder.outputs.len(), 2); + + // Change must be one remaining coin because fee is one constant coin + let change = tx_builder.outputs.get(1).amount(); + assert_eq!(change.coin(), BigNum(299)); + assert!(change.multiasset().is_some()); + + let change_assets = change.multiasset().unwrap(); + let change_asset = change_assets.get(&policy_id).unwrap(); + assert_eq!( + change_asset.get(&name).unwrap(), + amount_minted.checked_sub(&amount_sent).unwrap(), + ); +} + +#[test] +fn change_with_input_and_mint_not_enough_ada() { + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(1, 1)); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + + let (min_script, policy_id) = mint_script_and_policy(0); + let asset_name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + + let amount_minted = BigNum(1000); + let amount_sent = BigNum(500); + let amount_input_amount = BigNum(600); + + let mut asset_input = Assets::new(); + asset_input.insert(&asset_name, &amount_input_amount); + let mut mass_input = MultiAsset::new(); + mass_input.insert(&policy_id, &asset_input); + + // Input with 600 coins + tx_builder.add_regular_input( + &EnterpriseAddress::new(NetworkInfo::testnet_preprod().network_id(), &spend_cred) + .to_address(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(600)), + ).expect("Failed to add input"); + + tx_builder.add_regular_input( + &EnterpriseAddress::new(NetworkInfo::testnet_preprod().network_id(), &spend_cred) + .to_address(), + &TransactionInput::new(&genesis_id(), 1), + &Value::new_with_assets(&BigNum(1), &mass_input), + ).expect("Failed to add input"); + + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + + // Adding mint of the asset - which should work as an input + tx_builder.add_mint_asset(&min_script, &asset_name, &Int::new(&amount_minted)).expect("Failed to add mint"); + + let mut asset = Assets::new(); + asset.insert(&asset_name, &amount_sent); + let mut mass = MultiAsset::new(); + mass.insert(&policy_id, &asset); + + // One coin and the minted asset goes into the output + let mut output_amount = Value::new(&BigNum(400)); + output_amount.set_multiasset(&mass); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_value(&output_amount) + .build() + .unwrap(), + ) + .unwrap(); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + + let added_change = tx_builder.add_change_if_needed(&change_addr); + assert!(added_change.is_err()); +} + +#[test] +fn change_with_input_and_mint_not_enough_assets() { + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(1, 1)); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + + let (min_script, policy_id) = mint_script_and_policy(0); + let asset_name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + + let amount_minted = BigNum(1000); + let amount_sent = BigNum(100000); + let amount_input_amount = BigNum(600); + + let mut asset_input = Assets::new(); + asset_input.insert(&asset_name, &amount_input_amount); + let mut mass_input = MultiAsset::new(); + mass_input.insert(&policy_id, &asset_input); + + // Input with 600 coins + tx_builder.add_regular_input( + &EnterpriseAddress::new(NetworkInfo::testnet_preprod().network_id(), &spend_cred) + .to_address(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(100000)), + ).expect("Failed to add input"); + + tx_builder.add_regular_input( + &EnterpriseAddress::new(NetworkInfo::testnet_preprod().network_id(), &spend_cred) + .to_address(), + &TransactionInput::new(&genesis_id(), 1), + &Value::new_with_assets(&BigNum(1), &mass_input), + ).expect("Failed to add input"); + + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + + // Adding mint of the asset - which should work as an input + tx_builder.add_mint_asset(&min_script, &asset_name, &Int::new(&amount_minted)).expect("Failed to add mint"); + + let mut asset = Assets::new(); + asset.insert(&asset_name, &amount_sent); + let mut mass = MultiAsset::new(); + mass.insert(&policy_id, &asset); + + // One coin and the minted asset goes into the output + let mut output_amount = Value::new(&BigNum(400)); + output_amount.set_multiasset(&mass); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_value(&output_amount) + .build() + .unwrap(), + ) + .unwrap(); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + + let added_change = tx_builder.add_change_if_needed(&change_addr); + assert!(added_change.is_err()); +} + +#[test] +fn build_tx_with_native_assets_change() { + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(0, 1)); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let policy_id = &PolicyID::from([0u8; 28]); + let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + + let ma_input1 = 100; + let ma_input2 = 200; + let ma_output1 = 60; + + let multiassets = [ma_input1, ma_input2, ma_output1] + .iter() + .map(|input| { + let mut multiasset = MultiAsset::new(); + multiasset.insert(policy_id, &{ + let mut assets = Assets::new(); + assets.insert(&name, &BigNum(*input)); + assets + }); + multiasset + }) + .collect::>(); + + for (i, (multiasset, ada)) in multiassets + .iter() + .zip([100u64, 1000].iter().cloned().map(|x| BigNum(x))) + .enumerate() + { + let mut input_amount = Value::new(&ada); + input_amount.set_multiasset(multiasset); + + tx_builder.add_key_input( + &&spend.to_raw_key().hash(), + &TransactionInput::new(&genesis_id(), i as u32), + &input_amount, + ); + } + + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + + let mut output_amount = Value::new(&BigNum(500)); + output_amount.set_multiasset(&multiassets[2]); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_value(&output_amount) + .build() + .unwrap(), + ) + .unwrap(); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + + let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert_eq!(added_change, true); + let final_tx = tx_builder.build().unwrap(); + assert_eq!(final_tx.outputs().len(), 2); + assert_eq!( + final_tx + .outputs() + .get(1) + .amount() + .multiasset() + .unwrap() + .get(policy_id) + .unwrap() + .get(&name) + .unwrap(), + BigNum(ma_input1 + ma_input2 - ma_output1) + ); + assert_eq!(final_tx.outputs().get(1).amount().coin(), BigNum(599)); +} + +#[test] +fn build_tx_with_native_assets_change_and_purification() { + let coin_per_utxo_byte = BigNum(1); + // Prefer pure change! + let mut tx_builder = fake_tx_builder_with_fee_and_pure_change(&fake_linear_fee(0, 1)); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let policy_id = &PolicyID::from([0u8; 28]); + let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + + let ma_input1 = 100; + let ma_input2 = 200; + let ma_output1 = 60; + + let multiassets = [ma_input1, ma_input2, ma_output1] + .iter() + .map(|input| { + let mut multiasset = MultiAsset::new(); + multiasset.insert(policy_id, &{ + let mut assets = Assets::new(); + assets.insert(&name, &BigNum(*input)); + assets + }); + multiasset + }) + .collect::>(); + + for (i, (multiasset, ada)) in multiassets + .iter() + .zip([100u64, 1000].iter().cloned().map(|x| BigNum(x))) + .enumerate() + { + let mut input_amount = Value::new(&ada); + input_amount.set_multiasset(multiasset); + + tx_builder.add_key_input( + &&spend.to_raw_key().hash(), + &TransactionInput::new(&genesis_id(), i as u32), + &input_amount, + ); + } + + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + + let mut output_amount = Value::new(&BigNum(600)); + output_amount.set_multiasset(&multiassets[2]); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_value(&output_amount) + .build() + .unwrap(), + ) + .unwrap(); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + + let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert_eq!(added_change, true); + let final_tx = tx_builder.build().unwrap(); + assert_eq!(final_tx.outputs().len(), 3); + assert_eq!(final_tx.outputs().get(0).amount().coin(), BigNum(600)); + assert_eq!( + final_tx + .outputs() + .get(1) + .amount() + .multiasset() + .unwrap() + .get(policy_id) + .unwrap() + .get(&name) + .unwrap(), + BigNum(ma_input1 + ma_input2 - ma_output1) + ); + // The first change output that contains all the tokens contain minimum required Coin + let min_coin_for_dirty_change = min_ada_for_output( + &final_tx.outputs().get(1), + &DataCost::new_coins_per_byte(&coin_per_utxo_byte), + ) + .unwrap(); + assert_eq!( + final_tx.outputs().get(1).amount().coin(), + min_coin_for_dirty_change + ); + assert_eq!(final_tx.outputs().get(2).amount().coin(), BigNum(236)); + assert_eq!(final_tx.outputs().get(2).amount().multiasset(), None); +} + +#[test] +fn build_tx_with_native_assets_change_and_no_purification_cuz_not_enough_pure_coin() { + // Prefer pure change! + let mut tx_builder = fake_tx_builder_with_fee_and_pure_change(&fake_linear_fee(1, 1)); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let policy_id = &PolicyID::from([0u8; 28]); + let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + + let ma_input1 = 100; + let ma_input2 = 200; + let ma_output1 = 60; + + let multiassets = [ma_input1, ma_input2, ma_output1] + .iter() + .map(|input| { + let mut multiasset = MultiAsset::new(); + multiasset.insert(policy_id, &{ + let mut assets = Assets::new(); + assets.insert(&name, &BigNum(*input)); + assets + }); + multiasset + }) + .collect::>(); + + for (i, (multiasset, ada)) in multiassets + .iter() + .zip([300u64, 900].iter().cloned().map(|x| BigNum(x))) + .enumerate() + { + let mut input_amount = Value::new(&ada); + input_amount.set_multiasset(multiasset); + + tx_builder.add_key_input( + &&spend.to_raw_key().hash(), + &TransactionInput::new(&genesis_id(), i as u32), + &input_amount, + ); + } + + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + + let mut output_amount = Value::new(&BigNum(300)); + output_amount.set_multiasset(&multiassets[2]); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_value(&output_amount) + .build() + .unwrap(), + ) + .unwrap(); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + + let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert_eq!(added_change, true); + let final_tx = tx_builder.build().unwrap(); + assert_eq!(final_tx.outputs().len(), 2); + assert_eq!(final_tx.outputs().get(0).amount().coin(), BigNum(300)); + assert_eq!( + final_tx + .outputs() + .get(1) + .amount() + .multiasset() + .unwrap() + .get(policy_id) + .unwrap() + .get(&name) + .unwrap(), + BigNum(ma_input1 + ma_input2 - ma_output1) + ); + // The single change output contains more Coin then minimal utxo value + // But not enough to cover the additional fee for a separate output + assert_eq!(final_tx.outputs().get(1).amount().coin(), BigNum(499)); +} + +#[test] +#[should_panic] +fn build_tx_leftover_assets() { + let mut tx_builder = fake_default_tx_builder(); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + + // add an input that contains an asset not present in the output + let policy_id = &PolicyID::from([0u8; 28]); + let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + let mut input_amount = Value::new(&BigNum(1_000_000)); + let mut input_multiasset = MultiAsset::new(); + input_multiasset.insert(policy_id, &{ + let mut assets = Assets::new(); + assets.insert(&name, &BigNum(100)); + assets + }); + input_amount.set_multiasset(&input_multiasset); + tx_builder.add_key_input( + &spend.to_raw_key().hash(), + &TransactionInput::new(&genesis_id(), 0), + &input_amount, + ); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_coin(&BigNum(880_000)) + .build() + .unwrap(), + ) + .unwrap(); + tx_builder.set_ttl(1000); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + let added_change = tx_builder.add_change_if_needed(&change_addr); + assert!(!added_change.unwrap()); + assert_eq!(tx_builder.outputs.len(), 1); + assert_eq!( + tx_builder + .get_explicit_input() + .unwrap() + .checked_add(&tx_builder.get_implicit_input().unwrap()) + .unwrap(), + tx_builder + .get_explicit_output() + .unwrap() + .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) + .unwrap() + ); + let _final_tx = tx_builder.build(); // just test that it doesn't throw +} + +#[test] +fn build_tx_burn_less_than_min_ada() { + // with this mainnet value we should end up with a final min_ada_required of just under 1_000_000 + let mut tx_builder = fake_reallistic_tx_builder(); + + let output_addr = + ByronAddress::from_base58("Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b") + .unwrap(); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&output_addr.to_address()) + .next() + .unwrap() + .with_value(&Value::new(&BigNum(2_000_000))) + .build() + .unwrap(), + ) + .unwrap(); + + tx_builder.add_regular_input( + &ByronAddress::from_base58("Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3") + .unwrap() + .to_address(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(2_400_000)), + ).expect("Failed to add input"); + + tx_builder.set_ttl(1); + + let change_addr = + ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho") + .unwrap(); + let added_change = tx_builder.add_change_if_needed(&change_addr.to_address()); + assert!(!added_change.unwrap()); + assert_eq!(tx_builder.outputs.len(), 1); + assert_eq!( + tx_builder + .get_explicit_input() + .unwrap() + .checked_add(&tx_builder.get_implicit_input().unwrap()) + .unwrap(), + tx_builder + .get_explicit_output() + .unwrap() + .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) + .unwrap() + ); + let _final_tx = tx_builder.build(); // just test that it doesn't throw +} + +#[test] +fn build_tx_burn_empty_assets() { + let mut tx_builder = fake_reallistic_tx_builder(); + + let output_addr = + ByronAddress::from_base58("Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b") + .unwrap(); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&output_addr.to_address()) + .next() + .unwrap() + .with_value(&Value::new(&BigNum(2_000_000))) + .build() + .unwrap(), + ) + .unwrap(); + + let mut input_value = Value::new(&BigNum(2_400_000)); + input_value.set_multiasset(&MultiAsset::new()); + tx_builder.add_regular_input( + &ByronAddress::from_base58("Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3") + .unwrap() + .to_address(), + &TransactionInput::new(&genesis_id(), 0), + &input_value, + ).expect("Failed to add input"); + + tx_builder.set_ttl(1); + + let change_addr = + ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho") + .unwrap(); + let added_change = tx_builder.add_change_if_needed(&change_addr.to_address()); + assert!(!added_change.unwrap()); + assert_eq!(tx_builder.outputs.len(), 1); + assert_eq!( + tx_builder + .get_explicit_input() + .unwrap() + .checked_add(&tx_builder.get_implicit_input().unwrap()) + .unwrap() + .coin(), + tx_builder + .get_explicit_output() + .unwrap() + .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) + .unwrap() + .coin() + ); + let _final_tx = tx_builder.build(); // just test that it doesn't throw +} + +#[test] +fn build_tx_no_useless_multiasset() { + let mut tx_builder = fake_reallistic_tx_builder(); + + let policy_id = &PolicyID::from([0u8; 28]); + let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + + // add an output that uses up all the token but leaves ADA + let mut input_amount = Value::new(&BigNum(5_000_000)); + let mut input_multiasset = MultiAsset::new(); + input_multiasset.insert(policy_id, &{ + let mut assets = Assets::new(); + assets.insert(&name, &BigNum(100)); + assets + }); + input_amount.set_multiasset(&input_multiasset); + + tx_builder.add_regular_input( + &ByronAddress::from_base58("Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3") + .unwrap() + .to_address(), + &TransactionInput::new(&genesis_id(), 0), + &input_amount, + ).expect("Failed to add input"); + + // add an input that contains an asset & ADA + let mut output_amount = Value::new(&BigNum(2_000_000)); + let mut output_multiasset = MultiAsset::new(); + output_multiasset.insert(policy_id, &{ + let mut assets = Assets::new(); + assets.insert(&name, &BigNum(100)); + assets + }); + output_amount.set_multiasset(&output_multiasset); + + let output_addr = + ByronAddress::from_base58("Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b") + .unwrap(); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&output_addr.to_address()) + .next() + .unwrap() + .with_value(&output_amount) + .build() + .unwrap(), + ) + .unwrap(); + + tx_builder.set_ttl(1); + + let change_addr = + ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho") + .unwrap(); + let added_change = tx_builder.add_change_if_needed(&change_addr.to_address()); + assert!(added_change.unwrap()); + assert_eq!(tx_builder.outputs.len(), 2); + let final_tx = tx_builder.build().unwrap(); + let change_output = final_tx.outputs().get(1); + let change_assets = change_output.amount().multiasset(); + + // since all tokens got sent in the output + // the change should be only ADA and not have any multiasset struct in it + assert!(change_assets.is_none()); +} + +fn create_multiasset() -> (MultiAsset, [ScriptHash; 3], [AssetName; 3]) { + let policy_ids = [fake_policy_id(0), fake_policy_id(1), fake_policy_id(2)]; + let names = [ + AssetName::new(vec![99u8; 32]).unwrap(), + AssetName::new(vec![0u8, 1, 2, 3]).unwrap(), + AssetName::new(vec![4u8, 5, 6, 7, 8, 9]).unwrap(), + ]; + let multiasset = policy_ids.iter().zip(names.iter()).fold( + MultiAsset::new(), + |mut acc, (policy_id, name)| { + acc.insert(policy_id, &{ + let mut assets = Assets::new(); + assets.insert(&name, &BigNum(500)); + assets + }); + acc + }, + ); + return (multiasset, policy_ids, names); +} + +#[test] +fn build_tx_add_change_split_nfts() { + let max_value_size = 100; // super low max output size to test with fewer assets + let mut tx_builder = + fake_tx_builder_with_fee_and_val_size(&fake_linear_fee(0, 1), max_value_size); + + let (multiasset, policy_ids, names) = create_multiasset(); + + let mut input_value = Value::new(&BigNum(1000)); + input_value.set_multiasset(&multiasset); + + tx_builder.add_regular_input( + &ByronAddress::from_base58("Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3") + .unwrap() + .to_address(), + &TransactionInput::new(&genesis_id(), 0), + &input_value, + ).expect("Failed to add input"); + + let output_addr = + ByronAddress::from_base58("Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b") + .unwrap() + .to_address(); + let output_amount = Value::new(&BigNum(208)); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&output_addr) + .next() + .unwrap() + .with_value(&output_amount) + .build() + .unwrap(), + ) + .unwrap(); + + let change_addr = + ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho") + .unwrap() + .to_address(); + + let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert_eq!(added_change, true); + let final_tx = tx_builder.build().unwrap(); + assert_eq!(final_tx.outputs().len(), 3); + for (policy_id, asset_name) in policy_ids.iter().zip(names.iter()) { + assert!(final_tx + .outputs + .0 + .iter() + .find(|output| output.amount.multiasset.as_ref().map_or_else( + || false, + |ma| ma + .0 + .iter() + .find(|(pid, a)| *pid == policy_id + && a.0.iter().find(|(name, _)| *name == asset_name).is_some()) + .is_some() + )) + .is_some()); + } + for output in final_tx.outputs.0.iter() { + assert!(output.amount.to_bytes().len() <= max_value_size as usize); + } +} + +#[test] +fn build_tx_too_big_output() { + let mut tx_builder = fake_tx_builder_with_fee_and_val_size(&fake_linear_fee(0, 1), 10); + + tx_builder.add_regular_input( + &ByronAddress::from_base58("Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3") + .unwrap() + .to_address(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(500)), + ).expect("Failed to add input"); + + let output_addr = + ByronAddress::from_base58("Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b") + .unwrap() + .to_address(); + let mut output_amount = Value::new(&BigNum(50)); + output_amount.set_multiasset(&create_multiasset().0); + + assert!(tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&output_addr) + .next() + .unwrap() + .with_value(&output_amount) + .build() + .unwrap() + ) + .is_err()); +} + +#[test] +fn build_tx_add_change_nfts_not_enough_ada() { + let mut tx_builder = fake_tx_builder_with_fee_and_val_size( + &fake_linear_fee(0, 1), + 150, // super low max output size to test with fewer assets + ); + + let policy_ids = [ + PolicyID::from([0u8; 28]), + PolicyID::from([1u8; 28]), + PolicyID::from([2u8; 28]), + ]; + let names = [ + AssetName::new(vec![99u8; 32]).unwrap(), + AssetName::new(vec![0u8, 1, 2, 3]).unwrap(), + AssetName::new(vec![4u8, 5, 6, 7, 8, 9]).unwrap(), + ]; + + let multiasset = policy_ids.iter().zip(names.iter()).fold( + MultiAsset::new(), + |mut acc, (policy_id, name)| { + acc.insert(policy_id, &{ + let mut assets = Assets::new(); + assets.insert(&name, &BigNum(500)); + assets + }); + acc + }, + ); + + let mut input_value = Value::new(&BigNum(58)); + input_value.set_multiasset(&multiasset); + + tx_builder.add_regular_input( + &ByronAddress::from_base58("Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3") + .unwrap() + .to_address(), + &TransactionInput::new(&genesis_id(), 0), + &input_value, + ).expect("Failed to add input"); + + let output_addr = + ByronAddress::from_base58("Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b") + .unwrap() + .to_address(); + let output_amount = Value::new(&BigNum(208)); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&output_addr) + .next() + .unwrap() + .with_value(&output_amount) + .build() + .unwrap(), + ) + .unwrap(); + + let change_addr = + ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho") + .unwrap() + .to_address(); + + assert!(tx_builder.add_change_if_needed(&change_addr).is_err()) +} + +fn make_input(input_hash_byte: u8, value: Value) -> TransactionUnspentOutput { + TransactionUnspentOutput::new( + &fake_tx_input(input_hash_byte), + &TransactionOutputBuilder::new() + .with_address( + &Address::from_bech32("addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z") + .unwrap(), + ) + .next() + .unwrap() + .with_value(&value) + .build() + .unwrap(), + ) +} + +#[test] +fn tx_builder_cip2_largest_first_increasing_fees() { + // we have a = 1 to test increasing fees when more inputs are added + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(1, 0)); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address( + &Address::from_bech32( + "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z", + ) + .unwrap(), + ) + .next() + .unwrap() + .with_coin(&BigNum(9000)) + .build() + .unwrap(), + ) + .unwrap(); + let mut available_inputs = TransactionUnspentOutputs::new(); + available_inputs.add(&make_input(0u8, Value::new(&BigNum(1200)))); + available_inputs.add(&make_input(1u8, Value::new(&BigNum(1600)))); + available_inputs.add(&make_input(2u8, Value::new(&BigNum(6400)))); + available_inputs.add(&make_input(3u8, Value::new(&BigNum(2400)))); + available_inputs.add(&make_input(4u8, Value::new(&BigNum(800)))); + tx_builder + .add_inputs_from(&available_inputs, CoinSelectionStrategyCIP2::LargestFirst) + .unwrap(); + let change_addr = + ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho") + .unwrap() + .to_address(); + let change_added = tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert!(change_added); + let tx = tx_builder.build().unwrap(); + // change needed + assert_eq!(2, tx.outputs().len()); + assert_eq!(3, tx.inputs().len()); + // confirm order of only what is necessary + assert_eq!(1u8, tx.inputs().get(0).transaction_id().0[0]); + assert_eq!(2u8, tx.inputs().get(1).transaction_id().0[0]); + assert_eq!(3u8, tx.inputs().get(2).transaction_id().0[0]); +} + +#[test] +fn tx_builder_cip2_largest_first_static_fees() { + // we have a = 0 so we know adding inputs/outputs doesn't change the fee so we can analyze more + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(0, 0)); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address( + &Address::from_bech32( + "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z", + ) + .unwrap(), + ) + .next() + .unwrap() + .with_coin(&BigNum(1200)) + .build() + .unwrap(), + ) + .unwrap(); + let mut available_inputs = TransactionUnspentOutputs::new(); + available_inputs.add(&make_input(0u8, Value::new(&BigNum(150)))); + available_inputs.add(&make_input(1u8, Value::new(&BigNum(200)))); + available_inputs.add(&make_input(2u8, Value::new(&BigNum(800)))); + available_inputs.add(&make_input(3u8, Value::new(&BigNum(400)))); + available_inputs.add(&make_input(4u8, Value::new(&BigNum(100)))); + tx_builder + .add_inputs_from(&available_inputs, CoinSelectionStrategyCIP2::LargestFirst) + .unwrap(); + let change_addr = + ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho") + .unwrap() + .to_address(); + let change_added = tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert!(!change_added); + let tx = tx_builder.build().unwrap(); + // change not needed - should be exact + assert_eq!(1, tx.outputs().len()); + assert_eq!(2, tx.inputs().len()); + // confirm order of only what is necessary + assert_eq!(2u8, tx.inputs().get(0).transaction_id().0[0]); + assert_eq!(3u8, tx.inputs().get(1).transaction_id().0[0]); +} + +#[test] +fn tx_builder_cip2_largest_first_multiasset() { + // we have a = 0 so we know adding inputs/outputs doesn't change the fee so we can analyze more + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(0, 0)); + let pid1 = PolicyID::from([1u8; 28]); + let pid2 = PolicyID::from([2u8; 28]); + let asset_name1 = AssetName::new(vec![1u8; 8]).unwrap(); + let asset_name2 = AssetName::new(vec![2u8; 11]).unwrap(); + let asset_name3 = AssetName::new(vec![3u8; 9]).unwrap(); + + let mut output_value = Value::new(&BigNum(415)); + let mut output_ma = MultiAsset::new(); + output_ma.set_asset(&pid1, &asset_name1, &BigNum(5)); + output_ma.set_asset(&pid1, &asset_name2, &BigNum(1)); + output_ma.set_asset(&pid2, &asset_name2, &BigNum(2)); + output_ma.set_asset(&pid2, &asset_name3, &BigNum(4)); + output_value.set_multiasset(&output_ma); + tx_builder + .add_output(&TransactionOutput::new( + &Address::from_bech32("addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z") + .unwrap(), + &output_value, + )) + .unwrap(); + + let mut available_inputs = TransactionUnspentOutputs::new(); + // should not be taken + available_inputs.add(&make_input(0u8, Value::new(&BigNum(150)))); + + // should not be taken + let mut input1 = make_input(1u8, Value::new(&BigNum(200))); + let mut ma1 = MultiAsset::new(); + ma1.set_asset(&pid1, &asset_name1, &BigNum(10)); + ma1.set_asset(&pid1, &asset_name2, &BigNum(1)); + ma1.set_asset(&pid2, &asset_name2, &BigNum(2)); + input1.output.amount.set_multiasset(&ma1); + available_inputs.add(&input1); + + // taken first to satisfy pid1:asset_name1 (but also satisfies pid2:asset_name3) + let mut input2 = make_input(2u8, Value::new(&BigNum(10))); + let mut ma2 = MultiAsset::new(); + ma2.set_asset(&pid1, &asset_name1, &BigNum(20)); + ma2.set_asset(&pid2, &asset_name3, &BigNum(4)); + input2.output.amount.set_multiasset(&ma2); + available_inputs.add(&input2); + + // taken second to satisfy pid1:asset_name2 (but also satisfies pid2:asset_name1) + let mut input3 = make_input(3u8, Value::new(&BigNum(50))); + let mut ma3 = MultiAsset::new(); + ma3.set_asset(&pid2, &asset_name1, &BigNum(5)); + ma3.set_asset(&pid1, &asset_name2, &BigNum(15)); + input3.output.amount.multiasset = Some(ma3); + available_inputs.add(&input3); + + // should not be taken either + let mut input4 = make_input(4u8, Value::new(&BigNum(10))); + let mut ma4 = MultiAsset::new(); + ma4.set_asset(&pid1, &asset_name1, &BigNum(10)); + ma4.set_asset(&pid1, &asset_name2, &BigNum(10)); + input4.output.amount.multiasset = Some(ma4); + available_inputs.add(&input4); + + // taken third to satisfy pid2:asset_name_2 + let mut input5 = make_input(5u8, Value::new(&BigNum(10))); + let mut ma5 = MultiAsset::new(); + ma5.set_asset(&pid1, &asset_name2, &BigNum(10)); + ma5.set_asset(&pid2, &asset_name2, &BigNum(3)); + input5.output.amount.multiasset = Some(ma5); + available_inputs.add(&input5); + + // should be taken to get enough ADA + let input6 = make_input(6u8, Value::new(&BigNum(900))); + available_inputs.add(&input6); + + // should not be taken + available_inputs.add(&make_input(7u8, Value::new(&BigNum(100)))); + tx_builder + .add_inputs_from( + &available_inputs, + CoinSelectionStrategyCIP2::LargestFirstMultiAsset, + ) + .unwrap(); + let change_addr = + ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho") + .unwrap() + .to_address(); + let change_added = tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert!(change_added); + let tx = tx_builder.build().unwrap(); + + assert_eq!(2, tx.outputs().len()); + assert_eq!(4, tx.inputs().len()); + // check order expected per-asset + assert_eq!(2u8, tx.inputs().get(0).transaction_id().0[0]); + assert_eq!(3u8, tx.inputs().get(1).transaction_id().0[0]); + assert_eq!(5u8, tx.inputs().get(2).transaction_id().0[0]); + assert_eq!(6u8, tx.inputs().get(3).transaction_id().0[0]); + + let change = tx.outputs().get(1).amount; + assert_eq!(u64::from(change.coin), 555); + let change_ma = change.multiasset().unwrap(); + assert_eq!(15, u64::from(change_ma.get_asset(&pid1, &asset_name1))); + assert_eq!(24, u64::from(change_ma.get_asset(&pid1, &asset_name2))); + assert_eq!(1, u64::from(change_ma.get_asset(&pid2, &asset_name2))); + assert_eq!(0, u64::from(change_ma.get_asset(&pid2, &asset_name3))); + let expected_input = input2 + .output + .amount + .checked_add(&input3.output.amount) + .unwrap() + .checked_add(&input5.output.amount) + .unwrap() + .checked_add(&input6.output.amount) + .unwrap(); + let expected_change = expected_input.checked_sub(&output_value).unwrap(); + assert_eq!(expected_change, change); +} + +#[test] +fn tx_builder_cip2_random_improve_multiasset() { + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(0, 0)); + let pid1 = PolicyID::from([1u8; 28]); + let pid2 = PolicyID::from([2u8; 28]); + let asset_name1 = AssetName::new(vec![1u8; 8]).unwrap(); + let asset_name2 = AssetName::new(vec![2u8; 11]).unwrap(); + let asset_name3 = AssetName::new(vec![3u8; 9]).unwrap(); + + let mut output_value = Value::new(&BigNum(415)); + let mut output_ma = MultiAsset::new(); + output_ma.set_asset(&pid1, &asset_name1, &BigNum(5)); + output_ma.set_asset(&pid1, &asset_name2, &BigNum(1)); + output_ma.set_asset(&pid2, &asset_name2, &BigNum(2)); + output_ma.set_asset(&pid2, &asset_name3, &BigNum(4)); + output_value.set_multiasset(&output_ma); + tx_builder + .add_output(&TransactionOutput::new( + &Address::from_bech32("addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z") + .unwrap(), + &output_value, + )) + .unwrap(); + + let mut available_inputs = TransactionUnspentOutputs::new(); + available_inputs.add(&make_input(0u8, Value::new(&BigNum(150)))); + + let mut input1 = make_input(1u8, Value::new(&BigNum(200))); + let mut ma1 = MultiAsset::new(); + ma1.set_asset(&pid1, &asset_name1, &BigNum(10)); + ma1.set_asset(&pid1, &asset_name2, &BigNum(1)); + ma1.set_asset(&pid2, &asset_name2, &BigNum(2)); + input1.output.amount.set_multiasset(&ma1); + available_inputs.add(&input1); + + let mut input2 = make_input(2u8, Value::new(&BigNum(10))); + let mut ma2 = MultiAsset::new(); + ma2.set_asset(&pid1, &asset_name1, &BigNum(20)); + ma2.set_asset(&pid2, &asset_name3, &BigNum(4)); + input2.output.amount.set_multiasset(&ma2); + available_inputs.add(&input2); + + let mut input3 = make_input(3u8, Value::new(&BigNum(50))); + let mut ma3 = MultiAsset::new(); + ma3.set_asset(&pid2, &asset_name1, &BigNum(5)); + ma3.set_asset(&pid1, &asset_name2, &BigNum(15)); + input3.output.amount.multiasset = Some(ma3); + available_inputs.add(&input3); + + let mut input4 = make_input(4u8, Value::new(&BigNum(10))); + let mut ma4 = MultiAsset::new(); + ma4.set_asset(&pid1, &asset_name1, &BigNum(10)); + ma4.set_asset(&pid1, &asset_name2, &BigNum(10)); + input4.output.amount.multiasset = Some(ma4); + available_inputs.add(&input4); + + let mut input5 = make_input(5u8, Value::new(&BigNum(10))); + let mut ma5 = MultiAsset::new(); + ma5.set_asset(&pid1, &asset_name2, &BigNum(10)); + ma5.set_asset(&pid2, &asset_name2, &BigNum(3)); + input5.output.amount.multiasset = Some(ma5); + available_inputs.add(&input5); + + let input6 = make_input(6u8, Value::new(&BigNum(1000))); + available_inputs.add(&input6); + available_inputs.add(&make_input(7u8, Value::new(&BigNum(100)))); + + let mut input8 = make_input(8u8, Value::new(&BigNum(10))); + let mut ma8 = MultiAsset::new(); + ma8.set_asset(&pid2, &asset_name2, &BigNum(10)); + input8.output.amount.multiasset = Some(ma8); + available_inputs.add(&input8); + + let mut input9 = make_input(9u8, Value::new(&BigNum(10))); + let mut ma9 = MultiAsset::new(); + ma9.set_asset(&pid2, &asset_name3, &BigNum(10)); + input9.output.amount.multiasset = Some(ma9); + available_inputs.add(&input9); + + tx_builder + .add_inputs_from( + &available_inputs, + CoinSelectionStrategyCIP2::RandomImproveMultiAsset, + ) + .unwrap(); + + let input_for_cover_change = make_input(10u8, Value::new(&BigNum(1000))); + tx_builder.add_regular_input( + &input_for_cover_change.output.address, + &input_for_cover_change.input, + &input_for_cover_change.output.amount, + ).expect("Failed to add input"); + + let change_addr = + ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho") + .unwrap() + .to_address(); + let change_added = tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert!(change_added); + let tx = tx_builder.build().unwrap(); + + assert_eq!(2, tx.outputs().len()); + + let input_total = tx_builder.get_explicit_input().unwrap(); + assert!(input_total >= output_value); +} + +#[test] +fn tx_builder_cip2_random_improve() { + // we have a = 1 to test increasing fees when more inputs are added + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(1, 0)); + const COST: u64 = 10000; + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address( + &Address::from_bech32( + "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z", + ) + .unwrap(), + ) + .next() + .unwrap() + .with_coin(&BigNum(COST)) + .build() + .unwrap(), + ) + .unwrap(); + let mut available_inputs = TransactionUnspentOutputs::new(); + available_inputs.add(&make_input(0u8, Value::new(&BigNum(1500)))); + available_inputs.add(&make_input(1u8, Value::new(&BigNum(2000)))); + available_inputs.add(&make_input(2u8, Value::new(&BigNum(8000)))); + available_inputs.add(&make_input(3u8, Value::new(&BigNum(4000)))); + available_inputs.add(&make_input(4u8, Value::new(&BigNum(1000)))); + available_inputs.add(&make_input(5u8, Value::new(&BigNum(2000)))); + available_inputs.add(&make_input(6u8, Value::new(&BigNum(1500)))); + let add_inputs_res = + tx_builder.add_inputs_from(&available_inputs, CoinSelectionStrategyCIP2::RandomImprove); + assert!(add_inputs_res.is_ok(), "{:?}", add_inputs_res.err()); + let change_addr = + ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho") + .unwrap() + .to_address(); + let add_change_res = tx_builder.add_change_if_needed(&change_addr); + assert!(add_change_res.is_ok(), "{:?}", add_change_res.err()); + let tx_build_res = tx_builder.build(); + assert!(tx_build_res.is_ok(), "{:?}", tx_build_res.err()); + let tx = tx_build_res.unwrap(); + // we need to look up the values to ensure there's enough + let mut input_values = BTreeMap::new(); + for utxo in available_inputs.0.iter() { + input_values.insert(utxo.input.transaction_id(), utxo.output.amount.clone()); + } + let mut encountered = std::collections::HashSet::new(); + let mut input_total = Value::new(&Coin::zero()); + for input in &tx.inputs { + let txid = input.transaction_id(); + if !encountered.insert(txid.clone()) { + panic!("Input {:?} duplicated", txid); + } + let value = input_values.get(&txid).unwrap(); + input_total = input_total.checked_add(value).unwrap(); + } + assert!( + input_total + >= Value::new( + &tx_builder + .min_fee() + .unwrap() + .checked_add(&BigNum(COST)) + .unwrap() + ) + ); +} + +#[test] +fn tx_builder_cip2_random_improve_when_using_all_available_inputs() { + // we have a = 1 to test increasing fees when more inputs are added + let linear_fee = LinearFee::new(&BigNum(1), &BigNum(0)); + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(0)) + .key_deposit(&BigNum(0)) + .max_value_size(9999) + .max_tx_size(9999) + .coins_per_utxo_byte(&Coin::zero()) + .build() + .unwrap(); + let mut tx_builder = TransactionBuilder::new(&cfg); + const COST: u64 = 1000; + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address( + &Address::from_bech32( + "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z", + ) + .unwrap(), + ) + .next() + .unwrap() + .with_coin(&BigNum(COST)) + .build() + .unwrap(), + ) + .unwrap(); + let mut available_inputs = TransactionUnspentOutputs::new(); + available_inputs.add(&make_input(1u8, Value::new(&BigNum(800)))); + available_inputs.add(&make_input(2u8, Value::new(&BigNum(800)))); + let add_inputs_res = + tx_builder.add_inputs_from(&available_inputs, CoinSelectionStrategyCIP2::RandomImprove); + assert!(add_inputs_res.is_ok(), "{:?}", add_inputs_res.err()); +} + +#[test] +fn tx_builder_cip2_random_improve_adds_enough_for_fees() { + // we have a = 1 to test increasing fees when more inputs are added + let linear_fee = LinearFee::new(&BigNum(1), &BigNum(0)); + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(0)) + .key_deposit(&BigNum(0)) + .max_value_size(9999) + .max_tx_size(9999) + .coins_per_utxo_byte(&Coin::zero()) + .build() + .unwrap(); + let mut tx_builder = TransactionBuilder::new(&cfg); + const COST: u64 = 100; + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address( + &Address::from_bech32( + "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z", + ) + .unwrap(), + ) + .next() + .unwrap() + .with_coin(&BigNum(COST)) + .build() + .unwrap(), + ) + .unwrap(); + assert_eq!(tx_builder.min_fee().unwrap(), BigNum(53)); + let mut available_inputs = TransactionUnspentOutputs::new(); + available_inputs.add(&make_input(1u8, Value::new(&BigNum(150)))); + available_inputs.add(&make_input(2u8, Value::new(&BigNum(150)))); + available_inputs.add(&make_input(3u8, Value::new(&BigNum(150)))); + let add_inputs_res = + tx_builder.add_inputs_from(&available_inputs, CoinSelectionStrategyCIP2::RandomImprove); + assert!(add_inputs_res.is_ok(), "{:?}", add_inputs_res.err()); + assert_eq!(tx_builder.min_fee().unwrap(), BigNum(264)); + let change_addr = + ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho") + .unwrap() + .to_address(); + let add_change_res = tx_builder.add_change_if_needed(&change_addr); + assert!(add_change_res.is_ok(), "{:?}", add_change_res.err()); +} + +#[test] +fn build_tx_pay_to_multisig() { + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(10, 2)); + let spend = fake_root_key_15() + .derive(harden(1854)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + + tx_builder.add_key_input( + &spend.to_raw_key().hash(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_coin(&BigNum(999_000)) + .build() + .unwrap(), + ) + .unwrap(); + tx_builder.set_ttl(1000); + tx_builder.set_fee(&BigNum(1_000)); + + assert_eq!(tx_builder.outputs.len(), 1); + assert_eq!( + tx_builder + .get_explicit_input() + .unwrap() + .checked_add(&tx_builder.get_implicit_input().unwrap()) + .unwrap(), + tx_builder + .get_explicit_output() + .unwrap() + .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) + .unwrap() + ); + + let _final_tx = tx_builder.build().unwrap(); + let _deser_t = TransactionBody::from_bytes(_final_tx.to_bytes()).unwrap(); + + assert_eq!(_deser_t.to_bytes(), _final_tx.to_bytes()); +} + +fn build_full_tx( + body: &TransactionBody, + witness_set: &TransactionWitnessSet, + auxiliary_data: Option, +) -> Transaction { + return Transaction::new(body, witness_set, auxiliary_data); +} + +#[test] +fn build_tx_multisig_spend_1on1_unsigned() { + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(10, 2)); + + let spend = fake_root_key_15() //multisig + .derive(harden(1854)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let stake = fake_root_key_15() //multisig + .derive(harden(1854)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let addr_multisig = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + let addr_output = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + + tx_builder.add_regular_input( + &addr_multisig, + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ).expect("Failed to add input"); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_output) + .next() + .unwrap() + .with_coin(&BigNum(999_000)) + .build() + .unwrap(), + ) + .unwrap(); + tx_builder.set_ttl(1000); + tx_builder.set_fee(&BigNum(1_000)); + + let mut auxiliary_data = AuxiliaryData::new(); + let mut pubkey_native_scripts = NativeScripts::new(); + let mut oneof_native_scripts = NativeScripts::new(); + + let spending_hash = spend.to_raw_key().hash(); + pubkey_native_scripts.add(&NativeScript::new_script_pubkey(&ScriptPubkey::new( + &spending_hash, + ))); + oneof_native_scripts.add(&NativeScript::new_script_n_of_k(&ScriptNOfK::new( + 1, + &pubkey_native_scripts, + ))); + auxiliary_data.set_native_scripts(&oneof_native_scripts); + tx_builder.set_auxiliary_data(&auxiliary_data); + + assert_eq!(tx_builder.outputs.len(), 1); + assert_eq!( + tx_builder + .get_explicit_input() + .unwrap() + .checked_add(&tx_builder.get_implicit_input().unwrap()) + .unwrap(), + tx_builder + .get_explicit_output() + .unwrap() + .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) + .unwrap() + ); + + let _final_tx = tx_builder.build().unwrap(); + let _deser_t = TransactionBody::from_bytes(_final_tx.to_bytes()).unwrap(); + + assert_eq!(_deser_t.to_bytes(), _final_tx.to_bytes()); + assert_eq!( + _deser_t.auxiliary_data_hash.unwrap(), + utils::hash_auxiliary_data(&auxiliary_data) + ); +} + +#[test] +fn build_tx_multisig_1on1_signed() { + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(10, 2)); + let spend = fake_root_key_15() + .derive(harden(1854)) //multisig + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1854)) //multisig + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr_net_0 = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ) + .to_address(); + tx_builder.add_key_input( + &spend.to_raw_key().hash(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&addr_net_0) + .next() + .unwrap() + .with_coin(&BigNum(999_000)) + .build() + .unwrap(), + ) + .unwrap(); + tx_builder.set_ttl(1000); + tx_builder.set_fee(&BigNum(1_000)); + + let mut auxiliary_data = AuxiliaryData::new(); + let mut pubkey_native_scripts = NativeScripts::new(); + let mut oneof_native_scripts = NativeScripts::new(); + + let spending_hash = spend.to_raw_key().hash(); + pubkey_native_scripts.add(&NativeScript::new_script_pubkey(&ScriptPubkey::new( + &spending_hash, + ))); + oneof_native_scripts.add(&NativeScript::new_script_n_of_k(&ScriptNOfK::new( + 1, + &pubkey_native_scripts, + ))); + auxiliary_data.set_native_scripts(&oneof_native_scripts); + tx_builder.set_auxiliary_data(&auxiliary_data); + + let body = tx_builder.build().unwrap(); + + assert_eq!(tx_builder.outputs.len(), 1); + assert_eq!( + tx_builder + .get_explicit_input() + .unwrap() + .checked_add(&tx_builder.get_implicit_input().unwrap()) + .unwrap(), + tx_builder + .get_explicit_output() + .unwrap() + .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) + .unwrap() + ); + + let mut witness_set = TransactionWitnessSet::new(); + let mut vkw = Vkeywitnesses::new(); + vkw.add(&make_vkey_witness( + &hash_transaction(&body), + &PrivateKey::from_normal_bytes( + &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a") + .unwrap(), + ) + .unwrap(), + )); + witness_set.set_vkeys(&vkw); + + let _final_tx = build_full_tx(&body, &witness_set, None); + let _deser_t = Transaction::from_bytes(_final_tx.to_bytes()).unwrap(); + assert_eq!(_deser_t.to_bytes(), _final_tx.to_bytes()); + assert_eq!( + _deser_t.body().auxiliary_data_hash.unwrap(), + utils::hash_auxiliary_data(&auxiliary_data) + ); +} + +#[test] +fn add_change_splits_change_into_multiple_outputs_when_nfts_overflow_output_size() { + let linear_fee = LinearFee::new(&BigNum(0), &BigNum(1)); + let max_value_size = 100; // super low max output size to test with fewer assets + let mut tx_builder = TransactionBuilder::new( + &TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(0)) + .key_deposit(&BigNum(0)) + .max_value_size(max_value_size) + .max_tx_size(MAX_TX_SIZE) + .coins_per_utxo_byte(&BigNum(1)) + .prefer_pure_change(true) + .build() + .unwrap(), + ); + + let policy_id = PolicyID::from([0u8; 28]); + let names = [ + AssetName::new(vec![99u8; 32]).unwrap(), + AssetName::new(vec![0u8, 1, 2, 3]).unwrap(), + AssetName::new(vec![4u8, 5, 6, 7]).unwrap(), + AssetName::new(vec![5u8, 5, 6, 7]).unwrap(), + AssetName::new(vec![6u8, 5, 6, 7]).unwrap(), + ]; + let assets = names.iter().fold(Assets::new(), |mut a, name| { + a.insert(&name, &BigNum(500)); + a + }); + let mut multiasset = MultiAsset::new(); + multiasset.insert(&policy_id, &assets); + + let mut input_value = Value::new(&BigNum(1200)); + input_value.set_multiasset(&multiasset); + + tx_builder.add_regular_input( + &ByronAddress::from_base58("Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3") + .unwrap() + .to_address(), + &TransactionInput::new(&genesis_id(), 0), + &input_value, + ).expect("Failed to add input"); + + let output_addr = + ByronAddress::from_base58("Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b") + .unwrap() + .to_address(); + let output_amount = Value::new(&BigNum(208)); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&output_addr) + .next() + .unwrap() + .with_value(&output_amount) + .build() + .unwrap(), + ) + .unwrap(); + + let change_addr = + ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho") + .unwrap() + .to_address(); + + let add_change_result = tx_builder.add_change_if_needed(&change_addr); + assert!(add_change_result.is_ok()); + assert_eq!(tx_builder.outputs.len(), 4); + + let change1 = tx_builder.outputs.get(1); + let change2 = tx_builder.outputs.get(2); + let change3 = tx_builder.outputs.get(3); + + assert_eq!(change1.address, change_addr); + assert_eq!(change1.address, change2.address); + assert_eq!(change1.address, change3.address); + + assert_eq!(change1.amount.coin, BigNum(288)); + assert_eq!(change2.amount.coin, BigNum(293)); + assert_eq!(change3.amount.coin, BigNum(410)); + + assert!(change1.amount.multiasset.is_some()); + assert!(change2.amount.multiasset.is_some()); + assert!(change3.amount.multiasset.is_none()); // purified + + let masset1 = change1.amount.multiasset.unwrap(); + let masset2 = change2.amount.multiasset.unwrap(); + + assert_eq!(masset1.keys().len(), 1); + assert_eq!(masset1.keys(), masset2.keys()); + + let asset1 = masset1.get(&policy_id).unwrap(); + let asset2 = masset2.get(&policy_id).unwrap(); + assert_eq!(asset1.len(), 4); + assert_eq!(asset2.len(), 1); + + names.iter().for_each(|name| { + let v1 = asset1.get(name); + let v2 = asset2.get(name); + assert_ne!(v1.is_some(), v2.is_some()); + assert_eq!(v1.or(v2).unwrap(), BigNum(500)); + }); +} + +fn create_json_metadatum_string() -> String { + String::from("{ \"qwe\": 123 }") +} + +fn create_json_metadatum() -> TransactionMetadatum { + encode_json_str_to_metadatum( + create_json_metadatum_string(), + MetadataJsonSchema::NoConversions, + ) + .unwrap() +} + +fn create_aux_with_metadata(metadatum_key: &TransactionMetadatumLabel) -> AuxiliaryData { + let mut metadata = GeneralTransactionMetadata::new(); + metadata.insert(metadatum_key, &create_json_metadatum()); + + let mut aux = AuxiliaryData::new(); + aux.set_metadata(&metadata); + + let mut nats = NativeScripts::new(); + nats.add(&NativeScript::new_timelock_start(&TimelockStart::new(123))); + aux.set_native_scripts(&nats); + + return aux; +} + +fn assert_json_metadatum(dat: &TransactionMetadatum) { + let map = dat.as_map().unwrap(); + assert_eq!(map.len(), 1); + let key = TransactionMetadatum::new_text(String::from("qwe")).unwrap(); + let val = map.get(&key).unwrap(); + assert_eq!(val.as_int().unwrap(), Int::new_i32(123)); +} + +#[test] +fn set_metadata_with_empty_auxiliary() { + let mut tx_builder = fake_default_tx_builder(); + + let num = BigNum(42); + tx_builder.set_metadata(&create_aux_with_metadata(&num).metadata().unwrap()); + + assert!(tx_builder.auxiliary_data.is_some()); + + let aux = tx_builder.auxiliary_data.unwrap(); + assert!(aux.metadata().is_some()); + assert!(aux.native_scripts().is_none()); + assert!(aux.plutus_scripts().is_none()); + + let met = aux.metadata().unwrap(); + + assert_eq!(met.len(), 1); + assert_json_metadatum(&met.get(&num).unwrap()); +} + +#[test] +fn set_metadata_with_existing_auxiliary() { + let mut tx_builder = fake_default_tx_builder(); + + let num1 = BigNum(42); + tx_builder.set_auxiliary_data(&create_aux_with_metadata(&num1)); + + let num2 = BigNum(84); + tx_builder.set_metadata(&create_aux_with_metadata(&num2).metadata().unwrap()); + + let aux = tx_builder.auxiliary_data.unwrap(); + assert!(aux.metadata().is_some()); + assert!(aux.native_scripts().is_some()); + assert!(aux.plutus_scripts().is_none()); + + let met = aux.metadata().unwrap(); + assert_eq!(met.len(), 1); + assert!(met.get(&num1).is_none()); + assert_json_metadatum(&met.get(&num2).unwrap()); +} + +#[test] +fn add_metadatum_with_empty_auxiliary() { + let mut tx_builder = fake_default_tx_builder(); + + let num = BigNum(42); + tx_builder.add_metadatum(&num, &create_json_metadatum()); + + assert!(tx_builder.auxiliary_data.is_some()); + + let aux = tx_builder.auxiliary_data.unwrap(); + assert!(aux.metadata().is_some()); + assert!(aux.native_scripts().is_none()); + assert!(aux.plutus_scripts().is_none()); + + let met = aux.metadata().unwrap(); + + assert_eq!(met.len(), 1); + assert_json_metadatum(&met.get(&num).unwrap()); +} + +#[test] +fn add_metadatum_with_existing_auxiliary() { + let mut tx_builder = fake_default_tx_builder(); + + let num1 = BigNum(42); + tx_builder.set_auxiliary_data(&create_aux_with_metadata(&num1)); + + let num2 = BigNum(84); + tx_builder.add_metadatum(&num2, &create_json_metadatum()); + + let aux = tx_builder.auxiliary_data.unwrap(); + assert!(aux.metadata().is_some()); + assert!(aux.native_scripts().is_some()); + assert!(aux.plutus_scripts().is_none()); + + let met = aux.metadata().unwrap(); + assert_eq!(met.len(), 2); + assert_json_metadatum(&met.get(&num1).unwrap()); + assert_json_metadatum(&met.get(&num2).unwrap()); +} + +#[test] +fn add_json_metadatum_with_empty_auxiliary() { + let mut tx_builder = fake_default_tx_builder(); + + let num = BigNum(42); + tx_builder + .add_json_metadatum(&num, create_json_metadatum_string()) + .unwrap(); + + assert!(tx_builder.auxiliary_data.is_some()); + + let aux = tx_builder.auxiliary_data.unwrap(); + assert!(aux.metadata().is_some()); + assert!(aux.native_scripts().is_none()); + assert!(aux.plutus_scripts().is_none()); + + let met = aux.metadata().unwrap(); + + assert_eq!(met.len(), 1); + assert_json_metadatum(&met.get(&num).unwrap()); +} + +#[test] +fn add_json_metadatum_with_existing_auxiliary() { + let mut tx_builder = fake_default_tx_builder(); + + let num1 = BigNum(42); + tx_builder.set_auxiliary_data(&create_aux_with_metadata(&num1)); + + let num2 = BigNum(84); + tx_builder + .add_json_metadatum(&num2, create_json_metadatum_string()) + .unwrap(); + + let aux = tx_builder.auxiliary_data.unwrap(); + assert!(aux.metadata().is_some()); + assert!(aux.native_scripts().is_some()); + assert!(aux.plutus_scripts().is_none()); + + let met = aux.metadata().unwrap(); + assert_eq!(met.len(), 2); + assert_json_metadatum(&met.get(&num1).unwrap()); + assert_json_metadatum(&met.get(&num2).unwrap()); +} + +fn create_asset_name() -> AssetName { + AssetName::new(vec![0u8, 1, 2, 3]).unwrap() +} + +fn create_mint_asset() -> MintAssets { + MintAssets::new_from_entry(&create_asset_name(), &Int::new_i32(1234)).unwrap() +} + +fn create_assets() -> Assets { + let mut assets = Assets::new(); + assets.insert(&create_asset_name(), &BigNum(1234)); + return assets; +} + +fn create_mint_with_one_asset(policy_id: &PolicyID) -> Mint { + Mint::new_from_entry(policy_id, &create_mint_asset()) +} + +fn create_multiasset_one_asset(policy_id: &PolicyID) -> MultiAsset { + let mut mint = MultiAsset::new(); + mint.insert(policy_id, &create_assets()); + return mint; +} + +fn assert_mint_asset(mint: &Mint, policy_id: &PolicyID) { + assert!(mint.get(&policy_id).is_some()); + let result_asset = mint.get(&policy_id).unwrap(); + assert_eq!(result_asset.len(), 1); + assert_eq!( + result_asset + .get(0) + .unwrap() + .get(&create_asset_name()) + .unwrap(), + Int::new_i32(1234) + ); +} + +fn mint_script_and_policy_and_hash(x: u8) -> (NativeScript, PolicyID, Ed25519KeyHash) { + let hash = fake_key_hash(x); + let mint_script = NativeScript::new_script_pubkey(&ScriptPubkey::new(&hash)); + let policy_id = mint_script.hash(); + (mint_script, policy_id, hash) +} + +fn mint_script_and_policy(x: u8) -> (NativeScript, ScriptHash) { + let (m, p, _) = mint_script_and_policy_and_hash(x); + (m, p) +} + +#[test] +fn set_mint_asset_with_empty_mint() { + let mut tx_builder = fake_default_tx_builder(); + + let (mint_script, policy_id) = mint_script_and_policy(0); + tx_builder.set_mint_asset(&mint_script, &create_mint_asset()).expect("Failed to set mint asset"); + + assert!(tx_builder.mint.is_some()); + let mint_scripts = tx_builder.mint.as_ref().unwrap().get_native_scripts(); + assert!(mint_scripts.len() > 0); + + let mint = tx_builder.mint.unwrap().build().expect("Failed to build mint"); + + assert_eq!(mint.len(), 1); + assert_mint_asset(&mint, &policy_id); + + assert_eq!(mint_scripts.len(), 1); + assert_eq!(mint_scripts.get(0), mint_script); +} + +#[test] +fn set_mint_asset_with_existing_mint() { + let mut tx_builder = fake_default_tx_builder(); + + let (mint_script1, policy_id1) = mint_script_and_policy(0); + let (mint_script2, policy_id2) = mint_script_and_policy(1); + + tx_builder + .set_mint( + &create_mint_with_one_asset(&policy_id1), + &NativeScripts::from(vec![mint_script1.clone()]), + ) + .unwrap(); + + tx_builder.set_mint_asset(&mint_script2, &create_mint_asset()).expect("Failed to set mint asset"); + + assert!(tx_builder.mint.is_some()); + let mint_scripts = tx_builder.mint.as_ref().unwrap().get_native_scripts(); + assert!(mint_scripts.len() > 0); + + let mint = tx_builder.mint.unwrap().build().expect("Failed to build mint"); + + assert_eq!(mint.len(), 2); + assert_mint_asset(&mint, &policy_id1); + assert_mint_asset(&mint, &policy_id2); + + // Only second script is present in the scripts + assert_eq!(mint_scripts.len(), 2); + let actual_scripts = mint_scripts + .0 + .iter() + .cloned() + .collect::>(); + let expected_scripts = vec![mint_script1, mint_script2] + .iter() + .cloned() + .collect::>(); + assert_eq!(actual_scripts, expected_scripts); +} + +#[test] +fn add_mint_asset_with_empty_mint() { + let mut tx_builder = fake_default_tx_builder(); + + let (mint_script, policy_id) = mint_script_and_policy(0); + + tx_builder.add_mint_asset(&mint_script, &create_asset_name(), &Int::new_i32(1234)).expect("Failed to add mint asset"); + + assert!(tx_builder.mint.is_some()); + let mint_scripts = tx_builder.mint.as_ref().unwrap().get_native_scripts(); + assert!(mint_scripts.len() > 0); + + let mint = tx_builder.mint.unwrap().build().expect("Failed to build mint"); + + assert_eq!(mint.len(), 1); + assert_mint_asset(&mint, &policy_id); + + assert_eq!(mint_scripts.len(), 1); + assert_eq!(mint_scripts.get(0), mint_script); +} + +#[test] +fn add_mint_asset_with_existing_mint() { + let mut tx_builder = fake_default_tx_builder(); + + let (mint_script1, policy_id1) = mint_script_and_policy(0); + let (mint_script2, policy_id2) = mint_script_and_policy(1); + + tx_builder + .set_mint( + &create_mint_with_one_asset(&policy_id1), + &NativeScripts::from(vec![mint_script1.clone()]), + ) + .unwrap(); + tx_builder.add_mint_asset(&mint_script2, &create_asset_name(), &Int::new_i32(1234)).expect("Failed to add mint asset"); + + assert!(tx_builder.mint.is_some()); + let mint_scripts = tx_builder.mint.as_ref().unwrap().get_native_scripts(); + assert!(mint_scripts.len() > 0); + + let mint = tx_builder.mint.unwrap().build().expect("Failed to build mint"); + + assert_eq!(mint.len(), 2); + assert_mint_asset(&mint, &policy_id1); + assert_mint_asset(&mint, &policy_id2); + + assert_eq!(mint_scripts.len(), 2); + let actual_scripts = mint_scripts + .0 + .iter() + .cloned() + .collect::>(); + let expected_scripts = vec![mint_script1, mint_script2] + .iter() + .cloned() + .collect::>(); + assert_eq!(actual_scripts, expected_scripts); +} + +#[test] +fn add_output_amount() { + let mut tx_builder = fake_default_tx_builder(); + + let policy_id1 = PolicyID::from([0u8; 28]); + let multiasset = create_multiasset_one_asset(&policy_id1); + let mut value = Value::new(&BigNum(249)); + value.set_multiasset(&multiasset); + + let address = fake_byron_address(); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&address) + .next() + .unwrap() + .with_value(&value) + .build() + .unwrap(), + ) + .unwrap(); + + assert_eq!(tx_builder.outputs.len(), 1); + let out = tx_builder.outputs.get(0); + + assert_eq!(out.address.to_bytes(), address.to_bytes()); + assert_eq!(out.amount, value); +} + +#[test] +fn add_output_coin() { + let mut tx_builder = fake_default_tx_builder(); + + let address = fake_byron_address(); + let coin = BigNum(208); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&address) + .next() + .unwrap() + .with_coin(&coin) + .build() + .unwrap(), + ) + .unwrap(); + + assert_eq!(tx_builder.outputs.len(), 1); + let out = tx_builder.outputs.get(0); + + assert_eq!(out.address.to_bytes(), address.to_bytes()); + assert_eq!(out.amount.coin, coin); + assert!(out.amount.multiasset.is_none()); +} + +#[test] +fn add_output_coin_and_multiasset() { + let mut tx_builder = fake_default_tx_builder(); + + let policy_id1 = PolicyID::from([0u8; 28]); + let multiasset = create_multiasset_one_asset(&policy_id1); + + let address = fake_byron_address(); + let coin = BigNum(249); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&address) + .next() + .unwrap() + .with_coin_and_asset(&coin, &multiasset) + .build() + .unwrap(), + ) + .unwrap(); + + assert_eq!(tx_builder.outputs.len(), 1); + let out = tx_builder.outputs.get(0); + + assert_eq!(out.address.to_bytes(), address.to_bytes()); + assert_eq!(out.amount.coin, coin); + assert_eq!(out.amount.multiasset.unwrap(), multiasset); +} + +#[test] +fn add_output_asset_and_min_required_coin() { + let mut tx_builder = fake_reallistic_tx_builder(); + + let policy_id1 = PolicyID::from([0u8; 28]); + let multiasset = create_multiasset_one_asset(&policy_id1); + + let address = fake_byron_address(); + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&address) + .next() + .unwrap() + .with_asset_and_min_required_coin_by_utxo_cost( + &multiasset, + &tx_builder.config.utxo_cost(), + ) + .unwrap() + .build() + .unwrap(), + ) + .unwrap(); + + assert_eq!(tx_builder.outputs.len(), 1); + let out = tx_builder.outputs.get(0); + + assert_eq!(out.address.to_bytes(), address.to_bytes()); + assert_eq!(out.amount.multiasset.unwrap(), multiasset); + assert_eq!(out.amount.coin, BigNum(1146460)); +} + +#[test] +fn add_mint_asset_and_output() { + let mut tx_builder = fake_default_tx_builder(); + + let (mint_script0, policy_id0) = mint_script_and_policy(0); + let (mint_script1, policy_id1) = mint_script_and_policy(1); + + let name = create_asset_name(); + let amount = Int::new_i32(1234); + + let address = fake_byron_address(); + let coin = BigNum(249); + + // Add unrelated mint first to check it is NOT added to output later + tx_builder.add_mint_asset(&mint_script0, &name, &amount.clone()).expect("Failed to add mint asset"); + + tx_builder + .add_mint_asset_and_output( + &mint_script1, + &name, + &amount, + &TransactionOutputBuilder::new() + .with_address(&address) + .next() + .unwrap(), + &coin, + ) + .unwrap(); + + assert!(tx_builder.mint.is_some()); + let mint_scripts = &tx_builder.mint.as_ref().unwrap().get_native_scripts(); + assert!(mint_scripts.len() > 0); + + let mint = &tx_builder.mint.unwrap().build().expect("Failed to build mint"); + + // Mint contains two entries + assert_eq!(mint.len(), 2); + assert_mint_asset(mint, &policy_id0); + assert_mint_asset(mint, &policy_id1); + + assert_eq!(mint_scripts.len(), 2); + let actual_scripts = mint_scripts + .0 + .iter() + .cloned() + .collect::>(); + let expected_scripts = vec![mint_script0, mint_script1] + .iter() + .cloned() + .collect::>(); + assert_eq!(actual_scripts, expected_scripts); + + // One new output is created + assert_eq!(tx_builder.outputs.len(), 1); + let out = tx_builder.outputs.get(0); + + assert_eq!(out.address.to_bytes(), address.to_bytes()); + assert_eq!(out.amount.coin, coin); + + let multiasset = out.amount.multiasset.unwrap(); + + // Only second mint entry was added to the output + assert_eq!(multiasset.len(), 1); + assert!(multiasset.get(&policy_id0).is_none()); + assert!(multiasset.get(&policy_id1).is_some()); + + let asset = multiasset.get(&policy_id1).unwrap(); + assert_eq!(asset.len(), 1); + assert_eq!(asset.get(&name).unwrap(), BigNum(1234)); +} + +#[test] +fn add_mint_asset_and_min_required_coin() { + let mut tx_builder = fake_reallistic_tx_builder(); + + let (mint_script0, policy_id0) = mint_script_and_policy(0); + let (mint_script1, policy_id1) = mint_script_and_policy(1); + + let name = create_asset_name(); + let amount = Int::new_i32(1234); + + let address = fake_byron_address(); + + // Add unrelated mint first to check it is NOT added to output later + tx_builder.add_mint_asset(&mint_script0, &name, &amount).expect("Failed to add mint asset"); + + tx_builder + .add_mint_asset_and_output_min_required_coin( + &mint_script1, + &name, + &amount, + &TransactionOutputBuilder::new() + .with_address(&address) + .next() + .unwrap(), + ) + .unwrap(); + + assert!(tx_builder.mint.is_some()); + let mint_scripts = tx_builder.mint.as_ref().unwrap().get_native_scripts(); + assert!(mint_scripts.len() > 0); + + let mint = &tx_builder.mint.unwrap().build().expect("Failed to build mint"); + + // Mint contains two entries + assert_eq!(mint.len(), 2); + assert_mint_asset(mint, &policy_id0); + assert_mint_asset(mint, &policy_id1); + + assert_eq!(mint_scripts.len(), 2); + let actual_scripts = mint_scripts + .0 + .iter() + .cloned() + .collect::>(); + let expected_scripts = vec![mint_script0, mint_script1] + .iter() + .cloned() + .collect::>(); + assert_eq!(actual_scripts, expected_scripts); + + // One new output is created + assert_eq!(tx_builder.outputs.len(), 1); + let out = tx_builder.outputs.get(0); + + assert_eq!(out.address.to_bytes(), address.to_bytes()); + assert_eq!(out.amount.coin, BigNum(1146460)); + + let multiasset = out.amount.multiasset.unwrap(); + + // Only second mint entry was added to the output + assert_eq!(multiasset.len(), 1); + assert!(multiasset.get(&policy_id0).is_none()); + assert!(multiasset.get(&policy_id1).is_some()); + + let asset = multiasset.get(&policy_id1).unwrap(); + assert_eq!(asset.len(), 1); + assert_eq!(asset.get(&name).unwrap(), BigNum(1234)); +} + +#[test] +fn add_mint_includes_witnesses_into_fee_estimation() { + let mut tx_builder = fake_reallistic_tx_builder(); + + let hash0 = fake_key_hash(0); + + let (mint_script1, _, hash1) = mint_script_and_policy_and_hash(1); + let (mint_script2, _, _) = mint_script_and_policy_and_hash(2); + let (mint_script3, _, _) = mint_script_and_policy_and_hash(3); + + let name1 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + let name2 = AssetName::new(vec![1u8, 1, 2, 3]).unwrap(); + let name3 = AssetName::new(vec![2u8, 1, 2, 3]).unwrap(); + let name4 = AssetName::new(vec![3u8, 1, 2, 3]).unwrap(); + let amount = Int::new_i32(1234); + + // One input from unrelated address + tx_builder.add_key_input( + &hash0, + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(10_000_000)), + ); + + // One input from same address as mint + tx_builder.add_key_input( + &hash1, + &TransactionInput::new(&genesis_id(), 1), + &Value::new(&BigNum(10_000_000)), + ); + + // Original tx fee now assumes two VKey signatures for two inputs + let original_tx_fee = tx_builder.min_fee().unwrap(); + assert_eq!(original_tx_fee, BigNum(168361)); + + // Add minting four assets from three different policies + tx_builder.add_mint_asset(&mint_script1, &name1, &amount).expect("Failed to add mint asset"); + tx_builder.add_mint_asset(&mint_script2, &name2, &amount).expect("Failed to add mint asset"); + tx_builder.add_mint_asset(&mint_script3, &name3, &amount).expect("Failed to add mint asset"); + tx_builder.add_mint_asset(&mint_script3, &name4, &amount).expect("Failed to add mint asset"); + + let mint = tx_builder.get_mint().unwrap(); + let mint_len = mint.to_bytes().len(); + + let mint_scripts = tx_builder.get_witness_set(); + let mint_scripts_len = + mint_scripts.to_bytes().len() - TransactionWitnessSet::new().to_bytes().len(); + + let fee_coefficient = tx_builder.config.fee_algo.coefficient(); + + let raw_mint_fee = fee_coefficient + .checked_mul(&BigNum(mint_len as u64)) + .unwrap(); + + let raw_mint_script_fee = fee_coefficient + .checked_mul(&BigNum(mint_scripts_len as u64)) + .unwrap(); + + assert_eq!(raw_mint_fee, BigNum(5544)); + assert_eq!(raw_mint_script_fee, BigNum(4312)); + + let new_tx_fee = tx_builder.min_fee().unwrap(); + + let fee_diff_from_adding_mint = new_tx_fee.checked_sub(&original_tx_fee).unwrap(); + + let witness_fee_increase = fee_diff_from_adding_mint + .checked_sub(&raw_mint_fee) + .unwrap() + .checked_sub(&raw_mint_script_fee) + .unwrap(); + + assert_eq!(witness_fee_increase, BigNum(8932)); + + let fee_increase_bytes = u64::from(&witness_fee_increase) + .checked_div(u64::from(&fee_coefficient)) + .unwrap(); + + // Two vkey witnesses 96 bytes each (32 byte pubkey + 64 byte signature) + // Plus 11 bytes overhead for CBOR wrappers + // This is happening because we have three different minting policies + // but the same key-hash from one of them is already also used in inputs + // so no suplicate witness signature is require for that one + assert_eq!(fee_increase_bytes, 203); +} + +#[test] +fn fee_estimation_fails_on_missing_mint_scripts() { + let mut tx_builder = fake_reallistic_tx_builder(); + + // No error estimating fee without mint + assert!(tx_builder.min_fee().is_ok()); + + let (mint_script1, policy_id1) = mint_script_and_policy(0); + let (mint_script2, _) = mint_script_and_policy(1); + + let name1 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + let amount = Int::new_i32(1234); + + let mut mint = Mint::new(); + mint.insert( + &policy_id1, + &MintAssets::new_from_entry(&name1, &amount.clone()).unwrap(), + ); + + tx_builder + .set_mint(&mint, &NativeScripts::from(vec![mint_script1])) + .unwrap(); + + let est1 = tx_builder.min_fee(); + assert!(est1.is_ok()); + + tx_builder.add_mint_asset(&mint_script2, &name1, &amount).expect("Failed to add mint asset"); + + let est2 = tx_builder.min_fee(); + assert!(est2.is_ok()); + + // Native script assertion has been commented out in `.min_fee` + // Until implemented in a more performant manner + // TODO: these test parts might be returned back when it's done + + // // Remove one mint script + // tx_builder.mint_scripts = + // Some(NativeScripts::from(vec![tx_builder.mint_scripts.unwrap().get(1)])); + // + // // Now two different policies are minted but only one witness script is present + // let est3 = tx_builder.min_fee(); + // assert!(est3.is_err()); + // assert!(est3.err().unwrap().to_string().contains(&format!("{:?}", hex::encode(policy_id1.to_bytes())))); + // + // // Remove all mint scripts + // tx_builder.mint_scripts = Some(NativeScripts::new()); + // + // // Mint exists but no witness scripts at all present + // let est4 = tx_builder.min_fee(); + // assert!(est4.is_err()); + // assert!(est4.err().unwrap().to_string().contains("witness scripts are not provided")); + // + // // Remove all mint scripts + // tx_builder.mint_scripts = None; + // + // // Mint exists but no witness scripts at all present + // let est5 = tx_builder.min_fee(); + // assert!(est5.is_err()); + // assert!(est5.err().unwrap().to_string().contains("witness scripts are not provided")); +} + +#[test] +fn total_input_output_with_mint_and_burn() { + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(0, 1)); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + + let (mint_script1, policy_id1) = mint_script_and_policy(0); + let (mint_script2, policy_id2) = mint_script_and_policy(1); + + let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + + let ma_input1 = 100; + let ma_input2 = 200; + let ma_output1 = 60; + + let multiassets = [ma_input1, ma_input2, ma_output1] + .iter() + .map(|input| { + let mut multiasset = MultiAsset::new(); + multiasset.insert(&policy_id1, &{ + let mut assets = Assets::new(); + assets.insert(&name, &BigNum(*input)); + assets + }); + multiasset.insert(&policy_id2, &{ + let mut assets = Assets::new(); + assets.insert(&name, &BigNum(*input)); + assets + }); + multiasset + }) + .collect::>(); + + for (i, (multiasset, ada)) in multiassets + .iter() + .zip([100u64, 100, 100].iter().cloned().map(BigNum::from)) + .enumerate() + { + let mut input_amount = Value::new(&ada); + input_amount.set_multiasset(multiasset); + + tx_builder.add_key_input( + &&spend.to_raw_key().hash(), + &TransactionInput::new(&genesis_id(), i as u32), + &input_amount, + ); + } + + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address(&fake_byron_address()) + .next() + .unwrap() + .with_coin(&BigNum(208)) + .build() + .unwrap(), + ) + .unwrap(); + + let total_input_before_mint = tx_builder.get_total_input().unwrap(); + let total_output_before_mint = tx_builder.get_total_output().unwrap(); + + assert_eq!(total_input_before_mint.coin, BigNum(300)); + assert_eq!(total_output_before_mint.coin, BigNum(208)); + let ma1_input = total_input_before_mint.multiasset.unwrap(); + let ma1_output = total_output_before_mint.multiasset; + assert_eq!( + ma1_input.get(&policy_id1).unwrap().get(&name).unwrap(), + BigNum(360) + ); + assert_eq!( + ma1_input.get(&policy_id2).unwrap().get(&name).unwrap(), + BigNum(360) + ); + assert!(ma1_output.is_none()); + + // Adding mint + tx_builder.add_mint_asset(&mint_script1, &name, &Int::new_i32(40)).expect("Failed to add mint asset"); + + // Adding burn + tx_builder.add_mint_asset(&mint_script2, &name, &Int::new_i32(-40)).expect("Failed to add mint asset"); + + let total_input_after_mint = tx_builder.get_total_input().unwrap(); + let total_output_after_mint = tx_builder.get_total_output().unwrap(); + + assert_eq!(total_input_after_mint.coin, BigNum(300)); + assert_eq!(total_output_before_mint.coin, BigNum(208)); + let ma2_input = total_input_after_mint.multiasset.unwrap(); + let ma2_output = total_output_after_mint.multiasset.unwrap(); + assert_eq!( + ma2_input.get(&policy_id1).unwrap().get(&name).unwrap(), + BigNum(400) + ); + assert_eq!( + ma2_input.get(&policy_id2).unwrap().get(&name).unwrap(), + BigNum(360) + ); + assert_eq!( + ma2_output.get(&policy_id2).unwrap().get(&name).unwrap(), + BigNum(40) + ); +} + +#[test] +fn test_add_native_script_input() { + let mut tx_builder = fake_reallistic_tx_builder(); + let (script1, _) = mint_script_and_policy(0); + let (script2, _) = mint_script_and_policy(1); + + // Adding two script inputs using script1 and script2 hashes + tx_builder.add_native_script_input( + &script1, + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + tx_builder.add_native_script_input( + &script2, + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + + assert_eq!( + tx_builder.inputs.get_native_input_scripts().unwrap().len(), + 2 + ); +} + +#[test] +fn test_native_input_scripts_are_added_to_the_witnesses() { + let mut tx_builder = fake_reallistic_tx_builder(); + let (script1, _) = mint_script_and_policy(0); + let (script2, _) = mint_script_and_policy(1); + tx_builder.set_fee(&BigNum(42)); + + tx_builder.add_native_script_input( + &script1, + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + tx_builder.add_native_script_input( + &script2, + &TransactionInput::new(&genesis_id(), 1), + &Value::new(&BigNum(1_000_000)), + ); + + let tx: Transaction = tx_builder.build_tx_unsafe().unwrap(); + assert!(tx.witness_set.native_scripts.is_some()); + let native_scripts = tx.witness_set.native_scripts.unwrap(); + assert_eq!(native_scripts.len(), 2); + assert_eq!(native_scripts.get(0), script1); + assert_eq!(native_scripts.get(1), script2); +} + +#[test] +fn test_adding_plutus_script_input() { + let mut tx_builder = fake_reallistic_tx_builder(); + let (script1, _) = fake_plutus_script_and_hash(0); + let datum = PlutusData::new_bytes(fake_bytes_32(1)); + let redeemer_datum = PlutusData::new_bytes(fake_bytes_32(2)); + let redeemer = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &redeemer_datum, + &ExUnits::new(&BigNum(1), &BigNum(2)), + ); + tx_builder.add_plutus_script_input( + &PlutusWitness::new(&script1, &datum, &redeemer), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + tx_builder.set_fee(&BigNum(42)); + // There are no missing script witnesses + let tx: Transaction = tx_builder.build_tx_unsafe().unwrap(); + assert!(tx.witness_set.plutus_scripts.is_some()); + assert_eq!(tx.witness_set.plutus_scripts.unwrap().get(0), script1); + assert!(tx.witness_set.plutus_data.is_some()); + assert_eq!(tx.witness_set.plutus_data.unwrap().get(0), datum); + assert!(tx.witness_set.redeemers.is_some()); + assert_eq!(tx.witness_set.redeemers.unwrap().get(0), redeemer); +} + +#[test] +fn test_adding_plutus_script_witnesses() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(42)); + let (script1, _) = fake_plutus_script_and_hash(0); + let (script2, _) = fake_plutus_script_and_hash(1); + let datum1 = PlutusData::new_bytes(fake_bytes_32(10)); + let datum2 = PlutusData::new_bytes(fake_bytes_32(11)); + let redeemer1 = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &PlutusData::new_bytes(fake_bytes_32(20)), + &ExUnits::new(&BigNum(1), &BigNum(2)), + ); + let redeemer2 = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(1), + &PlutusData::new_bytes(fake_bytes_32(21)), + &ExUnits::new(&BigNum(1), &BigNum(2)), + ); + tx_builder.add_plutus_script_input( + &PlutusWitness::new(&script1, &datum1, &redeemer1), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + tx_builder.add_plutus_script_input( + &PlutusWitness::new(&script2, &datum2, &redeemer2), + &TransactionInput::new(&genesis_id(), 1), + &Value::new(&BigNum(1_000_000)), + ); + + let tx: Transaction = tx_builder.build_tx_unsafe().unwrap(); + // Check there are two correct scripts + assert!(tx.witness_set.plutus_scripts.is_some()); + let pscripts = tx.witness_set.plutus_scripts.unwrap(); + assert_eq!(pscripts.len(), 2); + assert_eq!(pscripts.get(0), script1); + assert_eq!(pscripts.get(1), script2); + // Check there are two correct datums + assert!(tx.witness_set.plutus_data.is_some()); + let datums = tx.witness_set.plutus_data.unwrap(); + assert_eq!(datums.len(), 2); + assert_eq!(datums.get(0), datum1); + assert_eq!(datums.get(1), datum2); + // Check there are two correct redeemers + assert!(tx.witness_set.redeemers.is_some()); + let redeems = tx.witness_set.redeemers.unwrap(); + assert_eq!(redeems.len(), 2); + assert_eq!(redeems.get(0), redeemer1); + assert_eq!(redeems.get(1), redeemer2); +} + +fn create_collateral() -> TxInputsBuilder { + let mut collateral_builder = TxInputsBuilder::new(); + collateral_builder + .add_regular_input( + &fake_byron_address(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ) + .unwrap(); + collateral_builder +} + +#[test] +fn test_existing_plutus_scripts_require_data_hash() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(42)); + tx_builder.set_collateral(&create_collateral()); + let (script1, _) = fake_plutus_script_and_hash(0); + let datum = PlutusData::new_bytes(fake_bytes_32(1)); + let redeemer_datum = PlutusData::new_bytes(fake_bytes_32(2)); + let redeemer = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &redeemer_datum, + &ExUnits::new(&BigNum(1), &BigNum(2)), + ); + tx_builder.add_plutus_script_input( + &PlutusWitness::new(&script1, &datum, &redeemer), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + + // Using SAFE `.build_tx` + let res = tx_builder.build_tx(); + assert!(res.is_err()); + if let Err(e) = res { + assert!(e.as_string().unwrap().contains("script data hash")); + } + + // Setting script data hash removes the error + tx_builder.set_script_data_hash(&ScriptDataHash::from_bytes(fake_bytes_32(42)).unwrap()); + // Using SAFE `.build_tx` + let res2 = tx_builder.build_tx(); + assert!(res2.is_ok()); + + // Removing script data hash will cause error again + tx_builder.remove_script_data_hash(); + // Using SAFE `.build_tx` + let res3 = tx_builder.build_tx(); + assert!(res3.is_err()); +} + +#[test] +fn test_calc_script_hash_data() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(42)); + tx_builder.set_collateral(&create_collateral()); + + let (script1, _) = fake_plutus_script_and_hash(0); + let datum = PlutusData::new_bytes(fake_bytes_32(1)); + let redeemer_datum = PlutusData::new_bytes(fake_bytes_32(2)); + let redeemer = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &redeemer_datum, + &ExUnits::new(&BigNum(1), &BigNum(2)), + ); + tx_builder.add_plutus_script_input( + &PlutusWitness::new(&script1, &datum, &redeemer), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + + // Setting script data hash removes the error + tx_builder + .calc_script_data_hash(&TxBuilderConstants::plutus_default_cost_models()) + .unwrap(); + + // Using SAFE `.build_tx` + let res2 = tx_builder.build_tx(); + assert!(res2.is_ok()); + + let mut used_langs = Languages::new(); + used_langs.add(Language::new_plutus_v1()); + + let data_hash = hash_script_data( + &Redeemers::from(vec![redeemer.clone()]), + &TxBuilderConstants::plutus_default_cost_models().retain_language_versions(&used_langs), + Some(PlutusList::from(vec![datum])), + ); + assert_eq!(tx_builder.script_data_hash.unwrap(), data_hash); +} + +#[test] +fn test_plutus_witness_redeemer_index_auto_changing() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(42)); + tx_builder.set_collateral(&create_collateral()); + let (script1, _) = fake_plutus_script_and_hash(0); + let (script2, _) = fake_plutus_script_and_hash(1); + let datum1 = PlutusData::new_bytes(fake_bytes_32(10)); + let datum2 = PlutusData::new_bytes(fake_bytes_32(11)); + + // Creating redeemers with indexes ZERO + let redeemer1 = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &PlutusData::new_bytes(fake_bytes_32(20)), + &ExUnits::new(&BigNum(1), &BigNum(2)), + ); + let redeemer2 = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &PlutusData::new_bytes(fake_bytes_32(21)), + &ExUnits::new(&BigNum(1), &BigNum(2)), + ); + + // Add a regular NON-script input first + tx_builder.add_regular_input( + &fake_byron_address(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ).expect("Failed to add input"); + + // Adding two plutus inputs then + // both have redeemers with index ZERO + tx_builder.add_plutus_script_input( + &PlutusWitness::new(&script1, &datum1, &redeemer1), + &TransactionInput::new(&genesis_id(), 1), + &Value::new(&BigNum(1_000_000)), + ); + tx_builder.add_plutus_script_input( + &PlutusWitness::new(&script2, &datum2, &redeemer2), + &TransactionInput::new(&genesis_id(), 2), + &Value::new(&BigNum(1_000_000)), + ); + + // Calc the script data hash + tx_builder + .calc_script_data_hash(&TxBuilderConstants::plutus_default_cost_models()) + .unwrap(); + + let tx: Transaction = tx_builder.build_tx().unwrap(); + assert!(tx.witness_set.redeemers.is_some()); + let redeems = tx.witness_set.redeemers.unwrap(); + assert_eq!(redeems.len(), 2); + + fn compare_redeems(r1: Redeemer, r2: Redeemer) { + assert_eq!(r1.tag(), r2.tag()); + assert_eq!(r1.data(), r2.data()); + assert_eq!(r1.ex_units(), r2.ex_units()); + } + + compare_redeems(redeems.get(0), redeemer1); + compare_redeems(redeems.get(1), redeemer2); + + // Note the redeemers from the result transaction are equal with source redeemers + // In everything EXCEPT the index field, the indexes have changed to 1 and 2 + // To match the position of their corresponding input + assert_eq!(redeems.get(0).index(), BigNum(1)); + assert_eq!(redeems.get(1).index(), BigNum(2)); +} + +#[test] +fn test_native_and_plutus_scripts_together() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(42)); + tx_builder.set_collateral(&create_collateral()); + let (pscript1, _) = fake_plutus_script_and_hash(0); + let (pscript2, _phash2) = fake_plutus_script_and_hash(1); + let (nscript1, _) = mint_script_and_policy(0); + let (nscript2, _nhash2) = mint_script_and_policy(1); + let datum1 = PlutusData::new_bytes(fake_bytes_32(10)); + let datum2 = PlutusData::new_bytes(fake_bytes_32(11)); + // Creating redeemers with indexes ZERO + let redeemer1 = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &PlutusData::new_bytes(fake_bytes_32(20)), + &ExUnits::new(&BigNum(1), &BigNum(2)), + ); + let redeemer2 = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &PlutusData::new_bytes(fake_bytes_32(21)), + &ExUnits::new(&BigNum(1), &BigNum(2)), + ); + + // Add one plutus input directly with witness + tx_builder.add_plutus_script_input( + &PlutusWitness::new(&pscript1, &datum1, &redeemer1), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + // Add one native input directly with witness + tx_builder.add_native_script_input( + &nscript1, + &TransactionInput::new(&genesis_id(), 1), + &Value::new(&BigNum(1_000_000)), + ); + // Add one plutus input generically without witness + tx_builder.add_plutus_script_input( + &PlutusWitness::new(&pscript2, &datum2, &redeemer2), + &TransactionInput::new(&genesis_id(), 2), + &Value::new(&BigNum(1_000_000)), + ); + // Add one native input generically without witness + tx_builder.add_native_script_input( + &nscript2, + &TransactionInput::new(&genesis_id(), 3), + &Value::new(&BigNum(1_000_000)), + ); + + tx_builder + .calc_script_data_hash(&TxBuilderConstants::plutus_default_cost_models()) + .unwrap(); + + let tx: Transaction = tx_builder.build_tx().unwrap(); + + let wits = tx.witness_set; + assert!(wits.native_scripts.is_some()); + assert!(wits.plutus_scripts.is_some()); + assert!(wits.plutus_data.is_some()); + assert!(wits.redeemers.is_some()); + + let nscripts = wits.native_scripts.unwrap(); + assert_eq!(nscripts.len(), 2); + assert_eq!(nscripts.get(0), nscript1); + assert_eq!(nscripts.get(1), nscript2); + + let pscripts = wits.plutus_scripts.unwrap(); + assert_eq!(pscripts.len(), 2); + assert_eq!(pscripts.get(0), pscript1); + assert_eq!(pscripts.get(1), pscript2); + + let datums = wits.plutus_data.unwrap(); + assert_eq!(datums.len(), 2); + assert_eq!(datums.get(0), datum1); + assert_eq!(datums.get(1), datum2); + + let redeems = wits.redeemers.unwrap(); + assert_eq!(redeems.len(), 2); + assert_eq!(redeems.get(0), redeemer1); + + // The second plutus input redeemer index has automatically changed to 2 + // because it was added on the third position + assert_eq!(redeems.get(1), redeemer2.clone_with_index(&BigNum(2))); +} + +#[test] +fn test_json_serialization_native_and_plutus_scripts_together() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(42)); + tx_builder.set_collateral(&create_collateral()); + let (pscript1, _) = fake_plutus_script_and_hash(0); + let (pscript2, _phash2) = fake_plutus_script_and_hash(1); + let (nscript1, _) = mint_script_and_policy(0); + let (nscript2, _nhash2) = mint_script_and_policy(1); + let datum1 = PlutusData::new_bytes(fake_bytes_32(10)); + let datum2 = PlutusData::new_bytes(fake_bytes_32(11)); + // Creating redeemers with indexes ZERO + let redeemer1 = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &PlutusData::new_bytes(fake_bytes_32(20)), + &ExUnits::new(&BigNum(1), &BigNum(2)), + ); + let redeemer2 = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &PlutusData::new_bytes(fake_bytes_32(21)), + &ExUnits::new(&BigNum(1), &BigNum(2)), + ); + + // Add one plutus input directly with witness + tx_builder.add_plutus_script_input( + &PlutusWitness::new(&pscript1, &datum1, &redeemer1), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + // Add one native input directly with witness + tx_builder.add_native_script_input( + &nscript1, + &TransactionInput::new(&genesis_id(), 1), + &Value::new(&BigNum(1_000_000)), + ); + // Add one plutus input generically without witness + tx_builder.add_plutus_script_input( + &PlutusWitness::new(&pscript2, &datum2, &redeemer2), + &TransactionInput::new(&genesis_id(), 2), + &Value::new(&BigNum(1_000_000)), + ); + // Add one native input generically without witness + tx_builder.add_native_script_input( + &nscript2, + &TransactionInput::new(&genesis_id(), 3), + &Value::new(&BigNum(1_000_000)), + ); + + tx_builder.calc_script_data_hash(&TxBuilderConstants::plutus_default_cost_models()).expect("Failed to calc script data hash"); + + let tx: Transaction = tx_builder.build_tx().unwrap(); + + let json_tx = tx.to_json().unwrap(); + let deser_tx = Transaction::from_json(json_tx.as_str()).unwrap(); + + assert_eq!(deser_tx.to_bytes(), tx.to_bytes()); + assert_eq!(deser_tx.to_json().unwrap(), tx.to_json().unwrap()); +} + +#[test] +fn test_regular_and_collateral_inputs_same_keyhash() { + let mut input_builder = TxInputsBuilder::new(); + let mut collateral_builder = TxInputsBuilder::new(); + + // Add a single input of both kinds with the SAME keyhash + input_builder.add_regular_input( + &fake_base_address(0), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ).expect("Failed to add input"); + collateral_builder.add_regular_input( + &fake_base_address(0), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ).expect("Failed to add input"); + + fn get_fake_vkeys_count(i: &TxInputsBuilder, c: &TxInputsBuilder) -> usize { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(42)); + tx_builder.set_inputs(i); + tx_builder.set_collateral(c); + let tx: Transaction = fake_full_tx(&tx_builder, tx_builder.build().unwrap()).unwrap(); + tx.witness_set.vkeys.unwrap().len() + } + + // There's only one fake witness in the builder + // because a regular and a collateral inputs both use the same keyhash + assert_eq!(get_fake_vkeys_count(&input_builder, &collateral_builder), 1); + + // Add a new input of each kind with DIFFERENT keyhashes + input_builder.add_regular_input( + &fake_base_address(1), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ).expect("Failed to add input"); + collateral_builder.add_regular_input( + &fake_base_address(2), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ).expect("Failed to add input"); + + // There are now three fake witnesses in the builder + // because all three unique keyhashes got combined + assert_eq!(get_fake_vkeys_count(&input_builder, &collateral_builder), 3); +} + +#[test] +fn test_regular_and_collateral_inputs_together() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(42)); + let (pscript1, _) = fake_plutus_script_and_hash(0); + let (pscript2, _) = fake_plutus_script_and_hash(1); + let (nscript1, _) = mint_script_and_policy(0); + let (nscript2, _) = mint_script_and_policy(1); + let datum1 = PlutusData::new_bytes(fake_bytes_32(10)); + let datum2 = PlutusData::new_bytes(fake_bytes_32(11)); + // Creating redeemers with indexes ZERO + let redeemer1 = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &PlutusData::new_bytes(fake_bytes_32(20)), + &ExUnits::new(&BigNum(1), &BigNum(2)), + ); + let redeemer2 = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &PlutusData::new_bytes(fake_bytes_32(21)), + &ExUnits::new(&BigNum(1), &BigNum(2)), + ); + + let mut input_builder = TxInputsBuilder::new(); + let mut collateral_builder = TxInputsBuilder::new(); + + input_builder.add_native_script_input( + &NativeScriptSource::new(&nscript1), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + collateral_builder.add_native_script_input( + &NativeScriptSource::new(&nscript2), + &TransactionInput::new(&genesis_id(), 1), + &Value::new(&BigNum(1_000_000)), + ); + + input_builder.add_plutus_script_input( + &PlutusWitness::new(&pscript1, &datum1, &redeemer1), + &TransactionInput::new(&genesis_id(), 2), + &Value::new(&BigNum(1_000_000)), + ); + collateral_builder.add_plutus_script_input( + &PlutusWitness::new(&pscript2, &datum2, &redeemer2), + &TransactionInput::new(&genesis_id(), 3), + &Value::new(&BigNum(1_000_000)), + ); + + tx_builder.set_inputs(&input_builder); + tx_builder.set_collateral(&collateral_builder); + + tx_builder + .calc_script_data_hash(&TxBuilderConstants::plutus_default_cost_models()) + .unwrap(); + + let w: &TransactionWitnessSet = &tx_builder.build_tx().unwrap().witness_set; + + assert!(w.native_scripts.is_some()); + let nscripts = w.native_scripts.as_ref().unwrap(); + assert_eq!(nscripts.len(), 2); + assert_eq!(nscripts.get(0), nscript1); + assert_eq!(nscripts.get(1), nscript2); + + assert!(w.plutus_scripts.is_some()); + let pscripts = w.plutus_scripts.as_ref().unwrap(); + assert_eq!(pscripts.len(), 2); + assert_eq!(pscripts.get(0), pscript1); + assert_eq!(pscripts.get(1), pscript2); + + assert!(w.plutus_data.is_some()); + let datums = w.plutus_data.as_ref().unwrap(); + assert_eq!(datums.len(), 2); + assert_eq!(datums.get(0), datum1); + assert_eq!(datums.get(1), datum2); + + assert!(w.redeemers.is_some()); + let redeemers = w.redeemers.as_ref().unwrap(); + assert_eq!(redeemers.len(), 2); + assert_eq!(redeemers.get(0), redeemer1.clone_with_index(&BigNum(1))); + assert_eq!(redeemers.get(1), redeemer2.clone_with_index(&BigNum(1))); +} + +#[test] +fn test_ex_unit_costs_are_added_to_the_fees() { + fn calc_fee_with_ex_units(mem: u64, step: u64) -> Coin { + let mut input_builder = TxInputsBuilder::new(); + let mut collateral_builder = TxInputsBuilder::new(); + + // Add a single input of both kinds with the SAME keyhash + input_builder.add_regular_input( + &fake_base_address(0), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ).expect("Failed to add input"); + collateral_builder.add_regular_input( + &fake_base_address(0), + &TransactionInput::new(&genesis_id(), 1), + &Value::new(&BigNum(1_000_000)), + ).expect("Failed to add input"); + + let (pscript1, _) = fake_plutus_script_and_hash(0); + let datum1 = PlutusData::new_bytes(fake_bytes_32(10)); + let redeemer1 = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &PlutusData::new_bytes(fake_bytes_32(20)), + &ExUnits::new(&BigNum(mem), &BigNum(step)), + ); + input_builder.add_plutus_script_input( + &PlutusWitness::new(&pscript1, &datum1, &redeemer1), + &TransactionInput::new(&genesis_id(), 2), + &Value::new(&BigNum(1_000_000)), + ); + + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_inputs(&input_builder); + tx_builder.set_collateral(&collateral_builder); + + tx_builder + .add_change_if_needed(&fake_base_address(42)) + .unwrap(); + + tx_builder.get_fee_if_set().unwrap() + } + + assert_eq!(calc_fee_with_ex_units(0, 0), BigNum(173509)); + assert_eq!(calc_fee_with_ex_units(10000, 0), BigNum(174174)); + assert_eq!(calc_fee_with_ex_units(0, 10000000), BigNum(174406)); + assert_eq!(calc_fee_with_ex_units(10000, 10000000), BigNum(175071)); +} + +#[test] +fn test_script_inputs_ordering() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(42)); + let (nscript1, _) = mint_script_and_policy(0); + let (pscript1, _) = fake_plutus_script_and_hash(0); + let (pscript2, _) = fake_plutus_script_and_hash(1); + let datum1 = PlutusData::new_bytes(fake_bytes_32(10)); + let datum2 = PlutusData::new_bytes(fake_bytes_32(11)); + // Creating redeemers with indexes ZERO + let pdata1 = PlutusData::new_bytes(fake_bytes_32(20)); + let pdata2 = PlutusData::new_bytes(fake_bytes_32(21)); + let redeemer1 = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &pdata1, + &ExUnits::new(&BigNum(1), &BigNum(2)), + ); + let redeemer2 = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &pdata2, + &ExUnits::new(&BigNum(1), &BigNum(2)), + ); + + tx_builder.add_plutus_script_input( + &PlutusWitness::new(&pscript1, &datum1, &redeemer1), + &fake_tx_input2(2, 1), + &fake_value(), + ); + tx_builder.add_native_script_input(&nscript1, &fake_tx_input2(1, 0), &fake_value()); + tx_builder.add_plutus_script_input( + &PlutusWitness::new(&pscript2, &datum2, &redeemer2), + &fake_tx_input2(2, 0), + &fake_value(), + ); + + let tx: Transaction = tx_builder.build_tx_unsafe().unwrap(); + + let ins = tx.body.inputs; + assert_eq!(ins.len(), 3); + assert_eq!(ins.get(0).transaction_id.0[0], 1); + assert_eq!(ins.get(1).transaction_id.0[0], 2); + assert_eq!(ins.get(1).index, 0); + assert_eq!(ins.get(2).transaction_id.0[0], 2); + assert_eq!(ins.get(2).index, 1); + + let r: Redeemers = tx.witness_set.redeemers.unwrap(); + assert_eq!(r.len(), 2); + + // Redeemer1 now has the index 2 even tho the input was added first + assert_eq!(r.get(0).data(), pdata1); + assert_eq!(r.get(0).index(), BigNum(2)); + + // Redeemer1 now has the index 1 even tho the input was added last + assert_eq!(r.get(1).data(), pdata2); + assert_eq!(r.get(1).index(), BigNum(1)); +} + +#[test] +fn test_required_signers() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(42)); + let tx1: TransactionBody = tx_builder.build().unwrap(); + assert!(tx1.required_signers.is_none()); + + let s1 = fake_key_hash(1); + let s2 = fake_key_hash(22); + let s3 = fake_key_hash(133); + + tx_builder.add_required_signer(&s1); + tx_builder.add_required_signer(&s3); + tx_builder.add_required_signer(&s2); + + let tx1: TransactionBody = tx_builder.build().unwrap(); + assert!(tx1.required_signers.is_some()); + + let rs: Ed25519KeyHashes = tx1.required_signers.unwrap(); + assert_eq!(rs.len(), 3); + assert!(rs.contains(&s1)); + assert!(rs.contains(&s2)); + assert!(rs.contains(&s3)); +} + +#[test] +fn test_required_signers_are_added_to_the_witness_estimate() { + fn count_fake_witnesses_with_required_signers(keys: &Ed25519KeyHashes) -> usize { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(42)); + tx_builder.add_regular_input( + &fake_base_address_with_payment_cred(Credential::from_keyhash(&fake_key_hash(0))), + &TransactionInput::new(&fake_tx_hash(0), 0), + &Value::new(&BigNum(10_000_000)), + ).expect("Failed to add input"); + + keys.to_vec().iter().for_each(|k| { + tx_builder.add_required_signer(k); + }); + + let tx: Transaction = fake_full_tx(&tx_builder, tx_builder.build().unwrap()).unwrap(); + tx.witness_set.vkeys.unwrap().len() + } + + assert_eq!( + count_fake_witnesses_with_required_signers(&Ed25519KeyHashes::new(),), + 1 + ); + + assert_eq!( + count_fake_witnesses_with_required_signers(&Ed25519KeyHashes::from_vec(vec![ + fake_key_hash(1) + ]),), + 2 + ); + + assert_eq!( + count_fake_witnesses_with_required_signers(&Ed25519KeyHashes::from_vec(vec![ + fake_key_hash(1), + fake_key_hash(2) + ]),), + 3 + ); + + // This case still produces only 3 fake signatures, because the same key is already used in the input address + assert_eq!( + count_fake_witnesses_with_required_signers(&Ed25519KeyHashes::from_vec(vec![ + fake_key_hash(1), + fake_key_hash(2), + fake_key_hash(0) + ]),), + 3 + ); + + // When a different key is used - 4 fake witnesses are produced + assert_eq!( + count_fake_witnesses_with_required_signers(&Ed25519KeyHashes::from_vec(vec![ + fake_key_hash(1), + fake_key_hash(2), + fake_key_hash(3) + ]),), + 4 + ); +} + +#[test] +fn collateral_return_and_total_collateral_setters() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(123456)); + + let mut inp = TxInputsBuilder::new(); + inp. + add_regular_input(&fake_base_address(0), &fake_tx_input(0), &fake_value()) + .expect("Failed to add input"); + + tx_builder.set_inputs(&inp); + tx_builder.set_collateral(&inp); + + let col_return = TransactionOutput::new(&fake_base_address(1), &fake_value2(123123)); + let col_total = BigNum(234234); + + tx_builder.set_collateral_return(&col_return); + tx_builder.set_total_collateral(&col_total); + + let tx: Transaction = tx_builder.build_tx_unsafe().unwrap(); + assert!(tx.body.collateral_return.is_some()); + assert_eq!(tx.body.collateral_return.unwrap(), col_return); + assert!(tx.body.total_collateral.is_some()); + assert_eq!(tx.body.total_collateral.unwrap(), col_total); +} + +fn fake_multiasset(amount: u64) -> MultiAsset { + let (_, policy_id) = mint_script_and_policy(234); + let mut assets = Assets::new(); + assets.insert( + &AssetName::new(fake_bytes_32(235)).unwrap(), + &BigNum(amount), + ); + let mut masset = MultiAsset::new(); + masset.insert(&policy_id, &assets); + masset +} + +#[test] +fn inputs_builder_total_value() { + let mut b = TxInputsBuilder::new(); + assert_eq!(b.total_value().unwrap(), Value::zero()); + + b.add_regular_input( + &fake_base_address(0), + &fake_tx_input(0), + &fake_value2(100_000), + ).expect("Failed to add input"); + assert_eq!(b.total_value().unwrap(), Value::new(&BigNum(100_000))); + + b.add_regular_input( + &fake_base_address(1), + &fake_tx_input(1), + &fake_value2(200_000), + ).expect("Failed to add input"); + assert_eq!(b.total_value().unwrap(), Value::new(&BigNum(300_000))); + + let masset = fake_multiasset(123); + + b.add_regular_input( + &fake_base_address(2), + &fake_tx_input(2), + &Value::new_with_assets(&BigNum(300_000), &masset), + ).expect("Failed to add input"); + assert_eq!( + b.total_value().unwrap(), + Value::new_with_assets(&BigNum(600_000), &masset) + ); +} + +#[test] +fn test_auto_calc_total_collateral() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(123456)); + + let mut inp = TxInputsBuilder::new(); + let collateral_input_value = 2_000_000; + inp.add_regular_input( + &fake_base_address(0), + &fake_tx_input(0), + &fake_value2(collateral_input_value.clone()), + ).expect("Failed to add input"); + + tx_builder.set_collateral(&inp); + + let collateral_return_value = 1_234_567; + let col_return = TransactionOutput::new( + &fake_base_address(1), + &fake_value2(collateral_return_value.clone()), + ); + + tx_builder + .set_collateral_return_and_total(&col_return) + .unwrap(); + + assert!(tx_builder.collateral_return.is_some()); + assert_eq!(tx_builder.collateral_return.unwrap(), col_return,); + + assert!(tx_builder.total_collateral.is_some()); + assert_eq!( + tx_builder.total_collateral.unwrap(), + BigNum(collateral_input_value - collateral_return_value), + ); +} + +#[test] +fn test_auto_calc_total_collateral_with_assets() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(123456)); + + let masset = fake_multiasset(123); + + let mut inp = TxInputsBuilder::new(); + let collateral_input_value = 2_000_000; + inp.add_regular_input( + &fake_base_address(0), + &fake_tx_input(0), + &Value::new_with_assets(&BigNum(collateral_input_value.clone()), &masset), + ).expect("Failed to add input"); + + tx_builder.set_collateral(&inp); + + let collateral_return_value = 1_345_678; + let col_return = TransactionOutput::new( + &fake_base_address(1), + &Value::new_with_assets(&BigNum(collateral_return_value.clone()), &masset), + ); + + tx_builder + .set_collateral_return_and_total(&col_return) + .unwrap(); + + assert!(tx_builder.collateral_return.is_some()); + assert_eq!(tx_builder.collateral_return.unwrap(), col_return,); + + assert!(tx_builder.total_collateral.is_some()); + assert_eq!( + tx_builder.total_collateral.unwrap(), + BigNum(collateral_input_value - collateral_return_value), + ); +} + +#[test] +fn test_auto_calc_total_collateral_fails_with_assets() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(123456)); + + let masset = fake_multiasset(123); + + let mut inp = TxInputsBuilder::new(); + let collateral_input_value = 2_000_000; + inp.add_regular_input( + &fake_base_address(0), + &fake_tx_input(0), + &Value::new_with_assets(&BigNum(collateral_input_value.clone()), &masset), + ).expect("Failed to add input"); + + tx_builder.set_collateral(&inp); + + // Collateral return does not handle ALL the assets from collateral input + let collateral_return_value = 1_345_678; + let col_return = TransactionOutput::new( + &fake_base_address(1), + &fake_value2(collateral_return_value.clone()), + ); + + let res = tx_builder.set_collateral_return_and_total(&col_return); + + // Function call returns an error + assert!(res.is_err()); + + // NEITHER total collateral nor collateral return are changed in the builder + assert!(tx_builder.total_collateral.is_none()); + assert!(tx_builder.collateral_return.is_none()); +} + +#[test] +fn test_auto_calc_total_collateral_fails_on_no_collateral() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(123456)); + + let res = tx_builder.set_collateral_return_and_total(&TransactionOutput::new( + &fake_base_address(1), + &fake_value2(1_345_678), + )); + + // Function call returns an error + assert!(res.is_err()); + + // NEITHER total collateral nor collateral return are changed in the builder + assert!(tx_builder.total_collateral.is_none()); + assert!(tx_builder.collateral_return.is_none()); +} + +#[test] +fn test_auto_calc_total_collateral_fails_on_no_ada() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(123456)); + + let mut inp = TxInputsBuilder::new(); + let collateral_input_value = 2_000_000; + inp.add_regular_input( + &fake_base_address(0), + &fake_tx_input(0), + &Value::new(&BigNum(collateral_input_value.clone())), + ).expect("Failed to add input"); + + tx_builder.set_collateral(&inp); + + let res = tx_builder.set_collateral_return_and_total(&TransactionOutput::new( + &fake_base_address(1), + &fake_value2(1), + )); + + // Function call returns an error + assert!(res.is_err()); + + // NEITHER total collateral nor collateral return are changed in the builder + assert!(tx_builder.total_collateral.is_none()); + assert!(tx_builder.collateral_return.is_none()); +} + +#[test] +fn test_auto_calc_collateral_return() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(123456)); + + let mut inp = TxInputsBuilder::new(); + let collateral_input_value = 2_000_000; + inp.add_regular_input( + &fake_base_address(0), + &fake_tx_input(0), + &fake_value2(collateral_input_value.clone()), + ).expect("Failed to add input"); + + tx_builder.set_collateral(&inp); + + let total_collateral_value = 234_567; + let collateral_return_address = fake_base_address(1); + + tx_builder + .set_total_collateral_and_return( + &BigNum(total_collateral_value.clone()), + &collateral_return_address, + ) + .unwrap(); + + assert!(tx_builder.total_collateral.is_some()); + assert_eq!( + tx_builder.total_collateral.unwrap(), + BigNum(total_collateral_value.clone()), + ); + + assert!(tx_builder.collateral_return.is_some()); + let col_return: TransactionOutput = tx_builder.collateral_return.unwrap(); + assert_eq!(col_return.address, collateral_return_address); + assert_eq!( + col_return.amount, + Value::new(&BigNum(collateral_input_value - total_collateral_value),) + ); +} + +#[test] +fn test_auto_calc_collateral_return_with_assets() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(123456)); + + let masset = fake_multiasset(123); + + let mut inp = TxInputsBuilder::new(); + let collateral_input_value = 2_000_000; + inp.add_regular_input( + &fake_base_address(0), + &fake_tx_input(0), + &Value::new_with_assets(&BigNum(collateral_input_value.clone()), &masset), + ).expect("Failed to add input"); + + tx_builder.set_collateral(&inp); + + let total_collateral_value = 345_678; + let collateral_return_address = fake_base_address(1); + + tx_builder + .set_total_collateral_and_return( + &BigNum(total_collateral_value.clone()), + &collateral_return_address, + ) + .unwrap(); + + assert!(tx_builder.total_collateral.is_some()); + assert_eq!( + tx_builder.total_collateral.unwrap(), + BigNum(total_collateral_value.clone()), + ); + + assert!(tx_builder.collateral_return.is_some()); + let col_return: TransactionOutput = tx_builder.collateral_return.unwrap(); + assert_eq!(col_return.address, collateral_return_address); + assert_eq!( + col_return.amount, + Value::new_with_assets( + &BigNum(collateral_input_value - total_collateral_value), + &masset, + ) + ); +} + +#[test] +fn test_add_collateral_return_succeed_with_border_amount() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(123456)); + + let masset = fake_multiasset(123); + + let mut inp = TxInputsBuilder::new(); + let collateral_input_value = 2_000_000; + inp.add_regular_input( + &fake_base_address(0), + &fake_tx_input(0), + &Value::new_with_assets(&BigNum(collateral_input_value.clone()), &masset), + ).expect("Failed to add input"); + + tx_builder.set_collateral(&inp); + + let collateral_return_address = fake_base_address(1); + + let possible_ret = Value::new_from_assets(&masset); + let fake_out = TransactionOutput::new(&collateral_return_address, &possible_ret); + let min_ada = min_ada_for_output(&fake_out, &tx_builder.config.utxo_cost()).unwrap(); + + let total_collateral_value = BigNum(collateral_input_value) + .checked_sub(&min_ada) + .unwrap(); + + tx_builder + .set_total_collateral_and_return(&total_collateral_value, &collateral_return_address) + .unwrap(); + + assert!(tx_builder.total_collateral.is_some()); + assert!(tx_builder.collateral_return.is_some()); + let col_return: TransactionOutput = tx_builder.collateral_return.unwrap(); + assert_eq!(col_return.address, collateral_return_address); + assert_eq!( + col_return.amount, + Value::new_with_assets(&min_ada, &masset,) + ); +} + +#[test] +fn test_add_zero_collateral_return() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(123456)); + + let mut inp = TxInputsBuilder::new(); + let collateral_input_value = 2_000_000; + inp.add_regular_input( + &fake_base_address(0), + &fake_tx_input(0), + &Value::new(&BigNum(collateral_input_value.clone())), + ).expect("Failed to add input"); + + tx_builder.set_collateral(&inp); + + let collateral_return_address = fake_base_address(1); + + tx_builder + .set_total_collateral_and_return( + &BigNum(collateral_input_value.clone()), + &collateral_return_address, + ) + .unwrap(); + + assert!(tx_builder.total_collateral.is_some()); + assert!(tx_builder.collateral_return.is_none()); +} + +#[test] +fn test_add_collateral_return_fails_no_enough_ada() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(123456)); + + let masset = fake_multiasset(123); + + let mut inp = TxInputsBuilder::new(); + let collateral_input_value = 2_000_000; + inp.add_regular_input( + &fake_base_address(0), + &fake_tx_input(0), + &Value::new_with_assets(&BigNum(collateral_input_value.clone()), &masset), + ).expect("Failed to add input"); + + tx_builder.set_collateral(&inp); + + let collateral_return_address = fake_base_address(1); + + let possible_ret = Value::new_from_assets(&masset); + let fake_out = TransactionOutput::new(&collateral_return_address, &possible_ret); + let min_ada = min_ada_for_output(&fake_out, &tx_builder.config.utxo_cost()).unwrap(); + let mut total_collateral_value = BigNum(collateral_input_value) + .checked_sub(&min_ada) + .unwrap(); + //make total collateral value bigger for make collateral return less then min ada + total_collateral_value = total_collateral_value.checked_add(&BigNum(1)).unwrap(); + + let coll_add_res = tx_builder + .set_total_collateral_and_return(&total_collateral_value, &collateral_return_address); + + assert!(coll_add_res.is_err()); + assert!(tx_builder.total_collateral.is_none()); + assert!(tx_builder.collateral_return.is_none()); +} + +#[test] +fn test_auto_calc_collateral_return_fails_on_no_collateral() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(123456)); + + let res = + tx_builder.set_total_collateral_and_return(&BigNum(345_678.clone()), &fake_base_address(1)); + + assert!(res.is_err()); + assert!(tx_builder.total_collateral.is_none()); + assert!(tx_builder.collateral_return.is_none()); +} + +#[test] +fn test_costmodel_retaining_for_v1() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(42)); + tx_builder.set_collateral(&create_collateral()); + + let (script1, _) = fake_plutus_script_and_hash(0); + let datum = PlutusData::new_integer(&BigInt::from_str("42").unwrap()); + let redeemer = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &datum, + &ExUnits::new(&BigNum(1700), &BigNum(368100)), + ); + tx_builder.add_plutus_script_input( + &PlutusWitness::new(&script1, &datum, &redeemer), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + + // Setting script data hash removes the error + tx_builder + .calc_script_data_hash(&TxBuilderConstants::plutus_vasil_cost_models()) + .unwrap(); + + // Using SAFE `.build_tx` + let res2 = tx_builder.build_tx(); + assert!(res2.is_ok()); + + let v1 = Language::new_plutus_v1(); + let v1_costmodel = TxBuilderConstants::plutus_vasil_cost_models() + .get(&v1) + .unwrap(); + let mut retained_cost_models = Costmdls::new(); + retained_cost_models.insert(&v1, &v1_costmodel); + + let data_hash = hash_script_data( + &Redeemers::from(vec![redeemer.clone()]), + &retained_cost_models, + Some(PlutusList::from(vec![datum])), + ); + assert_eq!(tx_builder.script_data_hash.unwrap(), data_hash); +} + +#[test] +fn test_costmodel_retaining_fails_on_missing_costmodel() { + let mut tx_builder = fake_reallistic_tx_builder(); + tx_builder.set_fee(&BigNum(42)); + tx_builder.set_collateral(&create_collateral()); + + let (script1, _) = fake_plutus_script_and_hash(0); + let datum = PlutusData::new_integer(&BigInt::from_str("42").unwrap()); + let redeemer = Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(0), + &datum, + &ExUnits::new(&BigNum(1700), &BigNum(368100)), + ); + tx_builder.add_plutus_script_input( + &PlutusWitness::new(&script1, &datum, &redeemer), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(1_000_000)), + ); + + let v2 = Language::new_plutus_v2(); + let v2_costmodel = TxBuilderConstants::plutus_vasil_cost_models() + .get(&v2) + .unwrap(); + let mut retained_cost_models = Costmdls::new(); + retained_cost_models.insert(&v2, &v2_costmodel); + + // Setting script data hash removes the error + let calc_result = tx_builder.calc_script_data_hash(&retained_cost_models); + assert!(calc_result.is_err()); +} + +#[test] +fn coin_selection_random_improve_multi_asset() { + let utoxs = TransactionUnspentOutputs::from_json("[ { \"input\": { + \"transaction_id\": \"96631bf40bc2ae1e10b3c9157a4c711562c664b9744ed1f580b725e0589efcd0\", + \"index\": 1 +}, +\"output\": { + \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", + \"amount\": { + \"coin\": \"661308571\", + \"multiasset\": null + }, + \"plutus_data\": null, + \"script_ref\": null +}}, +{ \"input\": { + \"transaction_id\": \"89da149fa162eca7212493f2bcc8415ed070832e053ac0ec335d3501f901ad77\", + \"index\": 1 +}, +\"output\": { + \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", + \"amount\": { + \"coin\": \"555975153\", + \"multiasset\": null + }, + \"plutus_data\": null, + \"script_ref\": null +}}, +{ \"input\": { + \"transaction_id\": \"0124993c20ea0fe626d96a644773225202fb442238c38206242d26a1131e0a6e\", + \"index\": 1 +}, +\"output\": { + \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", + \"amount\": { + \"coin\": \"1899495\", + \"multiasset\": { + \"07e8df329b724e4be48ee32738125c06000de5448aaf93ed46d59e28\": { + \"44696e6f436f696e\": \"750\" + } + } + }, + \"plutus_data\": null, + \"script_ref\": null +}}, +{ \"input\": { + \"transaction_id\": \"c15c423d624b3af3f032c079a1b390c472b8ba889b48dd581d0ea28f96a36875\", + \"index\": 0 +}, +\"output\": { + \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", + \"amount\": { + \"coin\": \"1804315\", + \"multiasset\": { + \"07e8df329b724e4be48ee32738125c06000de5448aaf93ed46d59e28\": { + \"44696e6f436f696e\": \"2000\" + } + } + }, + \"plutus_data\": null, + \"script_ref\": null +}}, +{ \"input\": { + \"transaction_id\": \"5894bf9c9125859d29770bf43e4018f4f34a69edee49a7c9488c6707ab523c9b\", + \"index\": 1 +}, +\"output\": { + \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", + \"amount\": { + \"coin\": \"440573428\", + \"multiasset\": null + }, + \"plutus_data\": null, + \"script_ref\": null +}}, +{ \"input\": { + \"transaction_id\": \"168404afd4e9927d7775c8f40c0f749fc7634832d6931c5d51a507724cf44420\", + \"index\": 0 +}, +\"output\": { + \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", + \"amount\": { + \"coin\": \"1804315\", + \"multiasset\": { + \"07e8df329b724e4be48ee32738125c06000de5448aaf93ed46d59e28\": { + \"44696e6f436f696e\": \"1000\" + } + } + }, + \"plutus_data\": null, + \"script_ref\": null +}}, +{ \"input\": { + \"transaction_id\": \"3e6138498b721ee609a4c289768b2accad39cd4f00448540a95ba3362578a2f7\", + \"index\": 4 +}, +\"output\": { + \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", + \"amount\": { + \"coin\": \"1508500\", + \"multiasset\": { + \"07e8df329b724e4be48ee32738125c06000de5448aaf93ed46d59e28\": { + \"44696e6f436f696e\": \"750\" + } + } + }, + \"plutus_data\": null, + \"script_ref\": null +}}, +{ \"input\": { + \"transaction_id\": \"3e6138498b721ee609a4c289768b2accad39cd4f00448540a95ba3362578a2f7\", + \"index\": 5 +}, +\"output\": { + \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", + \"amount\": { + \"coin\": \"664935092\", + \"multiasset\": null + }, + \"plutus_data\": null, + \"script_ref\": null +}}, +{ \"input\": { + \"transaction_id\": \"046cf1bc21c23c59975714b520dd7ed22b63dab592cb0449e0ee6cc96eefde69\", + \"index\": 2 +}, +\"output\": { + \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", + \"amount\": { + \"coin\": \"7094915\", + \"multiasset\": null + }, + \"plutus_data\": null, + \"script_ref\": null +}}, +{ \"input\": { + \"transaction_id\": \"e16f195105db5f84621af4f7ea57c7156b8699cba94d4fdb72a6fb09e31db7a8\", + \"index\": 1 +}, +\"output\": { + \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", + \"amount\": { + \"coin\": \"78400000\", + \"multiasset\": null + }, + \"plutus_data\": null, + \"script_ref\": null +}}, +{ \"input\": { + \"transaction_id\": \"e16f195105db5f84621af4f7ea57c7156b8699cba94d4fdb72a6fb09e31db7a8\", + \"index\": 2 +}, +\"output\": { + \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", + \"amount\": { + \"coin\": \"2000000\", + \"multiasset\": null + }, + \"plutus_data\": null, + \"script_ref\": null +}}, +{ \"input\": { + \"transaction_id\": \"006697ef0c9285b7001ebe5a9e356fb50441e0af803773a99b7cbb0e9b728570\", + \"index\": 1 +}, +\"output\": { + \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", + \"amount\": { + \"coin\": \"15054830\", + \"multiasset\": { + \"07e8df329b724e4be48ee32738125c06000de5448aaf93ed46d59e28\": { + \"44696e6f436f696e\": \"56250\" + }, + \"3320679b145d683b9123f0626360699fcd7408b4d3ec3bd9cc79398c\": { + \"44696e6f436f696e\": \"287000\" + }, + \"57fca08abbaddee36da742a839f7d83a7e1d2419f1507fcbf3916522\": { + \"4d494e54\": \"91051638\", + \"534245525259\": \"27198732\" + }, + \"e61bfc106338ed4aeba93036324fbea8150fd9750fcffca1cd9f1a19\": { + \"44696e6f536176696f723030303639\": \"1\", + \"44696e6f536176696f723030303936\": \"1\", + \"44696e6f536176696f723030313737\": \"1\", + \"44696e6f536176696f723030333033\": \"1\", + \"44696e6f536176696f723030333531\": \"1\", + \"44696e6f536176696f723030333931\": \"1\", + \"44696e6f536176696f723030343336\": \"1\", + \"44696e6f536176696f723030343434\": \"1\", + \"44696e6f536176696f723030353232\": \"1\", + \"44696e6f536176696f723030353337\": \"1\", + \"44696e6f536176696f723030363334\": \"1\", + \"44696e6f536176696f723030373332\": \"1\", + \"44696e6f536176696f723030373430\": \"1\", + \"44696e6f536176696f723030373435\": \"1\", + \"44696e6f536176696f723031303139\": \"1\", + \"44696e6f536176696f723031303631\": \"1\", + \"44696e6f536176696f723031333432\": \"1\", + \"44696e6f536176696f723031333832\": \"1\", + \"44696e6f536176696f723031353333\": \"1\", + \"44696e6f536176696f723031353732\": \"1\", + \"44696e6f536176696f723031363337\": \"1\", + \"44696e6f536176696f723031363430\": \"1\", + \"44696e6f536176696f723031373631\": \"1\", + \"44696e6f536176696f723031393436\": \"1\", + \"44696e6f536176696f723032313237\": \"1\", + \"44696e6f536176696f723032323232\": \"1\", + \"44696e6f536176696f723032333230\": \"1\", + \"44696e6f536176696f723032333239\": \"1\", + \"44696e6f536176696f723032333534\": \"1\", + \"44696e6f536176696f723032333631\": \"1\", + \"44696e6f536176696f723032333935\": \"1\", + \"44696e6f536176696f723032333938\": \"1\", + \"44696e6f536176696f723032343037\": \"1\", + \"44696e6f536176696f723032343434\": \"1\", + \"44696e6f536176696f723032353039\": \"1\", + \"44696e6f536176696f723032363334\": \"1\", + \"44696e6f536176696f723032363430\": \"1\", + \"44696e6f536176696f723032373537\": \"1\", + \"44696e6f536176696f723032373832\": \"1\", + \"44696e6f536176696f723032383933\": \"1\", + \"44696e6f536176696f723033323430\": \"1\", + \"44696e6f536176696f723033343937\": \"1\", + \"44696e6f536176696f723033353437\": \"1\", + \"44696e6f536176696f723033353738\": \"1\", + \"44696e6f536176696f723033363638\": \"1\", + \"44696e6f536176696f723033363836\": \"1\", + \"44696e6f536176696f723033363930\": \"1\", + \"44696e6f536176696f723033383638\": \"1\", + \"44696e6f536176696f723033383731\": \"1\", + \"44696e6f536176696f723033383931\": \"1\", + \"44696e6f536176696f723034313936\": \"1\", + \"44696e6f536176696f723034323538\": \"1\", + \"44696e6f536176696f723034323733\": \"1\", + \"44696e6f536176696f723034363235\": \"1\", + \"44696e6f536176696f723034373132\": \"1\", + \"44696e6f536176696f723034373932\": \"1\", + \"44696e6f536176696f723034383831\": \"1\", + \"44696e6f536176696f723034393936\": \"1\", + \"44696e6f536176696f723035303432\": \"1\", + \"44696e6f536176696f723035313539\": \"1\", + \"44696e6f536176696f723035333138\": \"1\", + \"44696e6f536176696f723035333532\": \"1\", + \"44696e6f536176696f723035343433\": \"1\", + \"44696e6f536176696f723035343639\": \"1\", + \"44696e6f536176696f723035373434\": \"1\", + \"44696e6f536176696f723035373638\": \"1\", + \"44696e6f536176696f723035373830\": \"1\", + \"44696e6f536176696f723035383435\": \"1\", + \"44696e6f536176696f723035383538\": \"1\", + \"44696e6f536176696f723035393632\": \"1\", + \"44696e6f536176696f723036303032\": \"1\", + \"44696e6f536176696f723036303337\": \"1\", + \"44696e6f536176696f723036303738\": \"1\", + \"44696e6f536176696f723036323033\": \"1\", + \"44696e6f536176696f723036323036\": \"1\", + \"44696e6f536176696f723036323236\": \"1\", + \"44696e6f536176696f723036333130\": \"1\", + \"44696e6f536176696f723036333935\": \"1\", + \"44696e6f536176696f723036343932\": \"1\", + \"44696e6f536176696f723036353532\": \"1\", + \"44696e6f536176696f723036363735\": \"1\", + \"44696e6f536176696f723036363839\": \"1\", + \"44696e6f536176696f723036373233\": \"1\", + \"44696e6f536176696f723036383731\": \"1\", + \"44696e6f536176696f723036383830\": \"1\", + \"44696e6f536176696f723036393137\": \"1\", + \"44696e6f536176696f723037303339\": \"1\", + \"44696e6f536176696f723037323638\": \"1\", + \"44696e6f536176696f723037333434\": \"1\", + \"44696e6f536176696f723037343232\": \"1\", + \"44696e6f536176696f723037343731\": \"1\", + \"44696e6f536176696f723037353431\": \"1\", + \"44696e6f536176696f723037363032\": \"1\", + \"44696e6f536176696f723037363136\": \"1\", + \"44696e6f536176696f723037363430\": \"1\", + \"44696e6f536176696f723037373635\": \"1\", + \"44696e6f536176696f723037373732\": \"1\", + \"44696e6f536176696f723037393039\": \"1\", + \"44696e6f536176696f723037393234\": \"1\", + \"44696e6f536176696f723037393430\": \"1\", + \"44696e6f536176696f723037393632\": \"1\", + \"44696e6f536176696f723038303130\": \"1\", + \"44696e6f536176696f723038303338\": \"1\", + \"44696e6f536176696f723038303339\": \"1\", + \"44696e6f536176696f723038303636\": \"1\", + \"44696e6f536176696f723038313735\": \"1\", + \"44696e6f536176696f723038323032\": \"1\", + \"44696e6f536176696f723038323131\": \"1\", + \"44696e6f536176696f723038323536\": \"1\", + \"44696e6f536176696f723038333532\": \"1\", + \"44696e6f536176696f723038333536\": \"1\", + \"44696e6f536176696f723038333538\": \"1\", + \"44696e6f536176696f723038333539\": \"1\", + \"44696e6f536176696f723038333830\": \"1\", + \"44696e6f536176696f723038343932\": \"1\", + \"44696e6f536176696f723038353231\": \"1\", + \"44696e6f536176696f723038353736\": \"1\", + \"44696e6f536176696f723038353836\": \"1\", + \"44696e6f536176696f723038363130\": \"1\", + \"44696e6f536176696f723039303231\": \"1\", + \"44696e6f536176696f723039303735\": \"1\", + \"44696e6f536176696f723039313039\": \"1\", + \"44696e6f536176696f723039313231\": \"1\", + \"44696e6f536176696f723039323238\": \"1\", + \"44696e6f536176696f723039333138\": \"1\", + \"44696e6f536176696f723039333731\": \"1\", + \"44696e6f536176696f723039343035\": \"1\", + \"44696e6f536176696f723039343136\": \"1\", + \"44696e6f536176696f723039353039\": \"1\", + \"44696e6f536176696f723039353635\": \"1\", + \"44696e6f536176696f723039363331\": \"1\", + \"44696e6f536176696f723039363932\": \"1\", + \"44696e6f536176696f723039383839\": \"1\", + \"44696e6f536176696f723039393038\": \"1\", + \"44696e6f536176696f723039393935\": \"1\" + }, + \"ee8e37676f6ebb8e031dff493f88ff711d24aa68666a09d61f1d3fb3\": { + \"43727970746f44696e6f3030303135\": \"1\", + \"43727970746f44696e6f3030313335\": \"1\", + \"43727970746f44696e6f3030323634\": \"1\", + \"43727970746f44696e6f3030333932\": \"1\", + \"43727970746f44696e6f3030353834\": \"1\", + \"43727970746f44696e6f3030373136\": \"1\", + \"43727970746f44696e6f3030373837\": \"1\", + \"43727970746f44696e6f3030383438\": \"1\", + \"43727970746f44696e6f3031303537\": \"1\", + \"43727970746f44696e6f3031313134\": \"1\", + \"43727970746f44696e6f3031323237\": \"1\", + \"43727970746f44696e6f3031323330\": \"1\", + \"43727970746f44696e6f3031343031\": \"1\", + \"43727970746f44696e6f3031353138\": \"1\", + \"43727970746f44696e6f3031353734\": \"1\", + \"43727970746f44696e6f3031373635\": \"1\", + \"43727970746f44696e6f3031383037\": \"1\", + \"43727970746f44696e6f3031383231\": \"1\", + \"43727970746f44696e6f3032303830\": \"1\", + \"43727970746f44696e6f3032313133\": \"1\", + \"43727970746f44696e6f3032323835\": \"1\", + \"43727970746f44696e6f3032343238\": \"1\", + \"43727970746f44696e6f3032363738\": \"1\", + \"43727970746f44696e6f3032393034\": \"1\", + \"43727970746f44696e6f3032393333\": \"1\", + \"43727970746f44696e6f3032393537\": \"1\", + \"43727970746f44696e6f3032393632\": \"1\", + \"43727970746f44696e6f3032393735\": \"1\", + \"43727970746f44696e6f3033303434\": \"1\", + \"43727970746f44696e6f3033333338\": \"1\", + \"43727970746f44696e6f3033393535\": \"1\", + \"43727970746f44696e6f3034303630\": \"1\", + \"43727970746f44696e6f3034313939\": \"1\", + \"43727970746f44696e6f3034373439\": \"1\", + \"43727970746f44696e6f3034383134\": \"1\", + \"43727970746f44696e6f3034393530\": \"1\", + \"43727970746f44696e6f3035303630\": \"1\", + \"43727970746f44696e6f3035333230\": \"1\", + \"43727970746f44696e6f2d312d3030303030\": \"1\", + \"43727970746f44696e6f2d312d3030303032\": \"1\" + } + } + }, + \"plutus_data\": null, + \"script_ref\": null +}}, +{ \"input\": { + \"transaction_id\": \"006697ef0c9285b7001ebe5a9e356fb50441e0af803773a99b7cbb0e9b728570\", + \"index\": 2 +}, +\"output\": { + \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", + \"amount\": { + \"coin\": \"2279450\", + \"multiasset\": null + }, + \"plutus_data\": null, + \"script_ref\": null +}}, +{ \"input\": { + \"transaction_id\": \"017962634cf8fa87835256a80b8374c6f75687c34d8694480cb071648551c3a7\", + \"index\": 0 +}, +\"output\": { + \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", + \"amount\": { + \"coin\": \"2000000\", + \"multiasset\": { + \"ee8e37676f6ebb8e031dff493f88ff711d24aa68666a09d61f1d3fb3\": { + \"43727970746f44696e6f3031353039\": \"1\" + } + } + }, + \"plutus_data\": null, + \"script_ref\": null +}}, +{ \"input\": { + \"transaction_id\": \"017962634cf8fa87835256a80b8374c6f75687c34d8694480cb071648551c3a7\", + \"index\": 1 +}, +\"output\": { + \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", + \"amount\": { + \"coin\": \"725669617\", + \"multiasset\": null + }, + \"plutus_data\": null, + \"script_ref\": null +}}]") + .unwrap(); + let output = TransactionOutput::from_json( + "{ + \"address\": \"addr_test1wpv93hm9sqx0ar7pgxwl9jn3xt6lwmxxy27zd932slzvghqg8fe0n\", + \"amount\": { + \"coin\": \"20000000\", + \"multiasset\": { + \"07e8df329b724e4be48ee32738125c06000de5448aaf93ed46d59e28\": { + \"44696e6f436f696e\": \"1000\" + }, + \"ee8e37676f6ebb8e031dff493f88ff711d24aa68666a09d61f1d3fb3\": { + \"43727970746f44696e6f2d312d3030303030\": \"1\", + \"43727970746f44696e6f2d312d3030303032\": \"1\" + } + } + }, + \"plutus_data\": { + \"DataHash\": \"979f68de9e070e75779f80ce5e6cc74f8d77661d65f2895c01d0a6f66eceb791\" + }, + \"script_ref\": null +}", + ) + .unwrap(); + let mut builder = fake_reallistic_tx_builder(); + builder.add_output(&output).unwrap(); + let res = builder.add_inputs_from(&utoxs, CoinSelectionStrategyCIP2::RandomImproveMultiAsset); + assert!(res.is_ok()); +} + +#[test] +fn multiple_plutus_inputs_test() { + let mut tx_builder = fake_reallistic_tx_builder(); + let (plutus_script, _) = fake_plutus_script_and_hash(1); + let redeemer1 = fake_redeemer(1); + let redeemer2 = fake_redeemer(2); + + let mut in_builder = TxInputsBuilder::new(); + let input_1 = TransactionInput::new( + &TransactionHash::from_bytes( + hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") + .unwrap(), + ) + .unwrap(), + 1, + ); + let input_2 = TransactionInput::new( + &TransactionHash::from_bytes( + hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") + .unwrap(), + ) + .unwrap(), + 2, + ); + + let colateral_adress = Address::from_bech32("addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w").unwrap(); + let colateral_input = TransactionInput::new( + &TransactionHash::from_bytes( + hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") + .unwrap(), + ) + .unwrap(), + 3, + ); + + let output_adress = Address::from_bech32("addr_test1qpm5njmgzf4t7225v6j34wl30xfrufzt3jtqtdzf3en9ahpmnhtmynpasyc8fq75zv0uaj86vzsr7g3g8q5ypgu5fwtqr9zsgj").unwrap(); + let output_value = Value::new(&Coin::from(5000000u64)); + let output = TransactionOutput::new(&output_adress, &output_value); + + tx_builder.add_output(&output).expect("Failed to add output"); + let mut col_builder = TxInputsBuilder::new(); + col_builder.add_regular_input( + &colateral_adress, + &colateral_input, + &Value::new(&Coin::from(1000000000u64)), + ).expect("Failed to add input"); + tx_builder.set_collateral(&col_builder); + + let datum = PlutusData::new_bytes(fake_bytes_32(11)); + let plutus_wit1 = PlutusWitness::new(&plutus_script, &datum, &redeemer1); + + let plutus_wit2 = PlutusWitness::new(&plutus_script, &datum, &redeemer2); + + let value = Value::new(&Coin::from(100000000u64)); + + in_builder.add_plutus_script_input(&plutus_wit1, &input_1, &value); + in_builder.add_plutus_script_input(&plutus_wit2, &input_2, &value); + + tx_builder.set_inputs(&in_builder); + tx_builder.calc_script_data_hash(&TxBuilderConstants::plutus_vasil_cost_models()).expect("Failed to calculate script data hash"); + tx_builder.add_change_if_needed(&output_adress).expect("Failed to add change"); + let build_res = tx_builder.build_tx(); + assert!(&build_res.is_ok()); + let tx = build_res.unwrap(); + assert_eq!(tx.witness_set.plutus_scripts.unwrap().len(), 1usize); + assert_eq!(tx.witness_set.redeemers.unwrap().len(), 2usize); +} + +#[test] +fn build_tx_with_certs_withdrawals_plutus_script_address() { + let mut tx_builder = fake_tx_builder_with_key_deposit(1_000_000); + let spend = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + let change_key = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + let reward = fake_root_key_15() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(3) + .derive(1) + .to_public(); + + let redeemer_cert1 = fake_redeemer(1); + let redeemer_cert2 = fake_redeemer(2); + let redeemer_cert3 = fake_redeemer(3); + let redeemer_withdraw1 = fake_redeemer(4); + let redeemer_withdraw2 = fake_redeemer(5); + + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + tx_builder.add_key_input( + &spend.to_raw_key().hash(), + &TransactionInput::new(&genesis_id(), 0), + &Value::new(&BigNum(5_000_000)), + ); + tx_builder.set_ttl(1000); + let (cert_script1, cert_script_hash1) = fake_plutus_script_and_hash(1); + let cert_script_cred1 = Credential::from_scripthash(&cert_script_hash1); + + let (cert_script2, cert_script_hash2) = fake_plutus_script_and_hash(2); + let cert_script_cred2 = Credential::from_scripthash(&cert_script_hash2); + + let cert_script_hash3 = fake_script_hash(3); + let cert_script_cred3 = Credential::from_scripthash(&cert_script_hash3); + + let (withdraw_script1, withdraw_script_hash1) = fake_plutus_script_and_hash(3); + let withdraw_script_cred1 = Credential::from_scripthash(&withdraw_script_hash1); + + let withdraw_script_hash2 = fake_script_hash(3); + let withdraw_script_cred2 = Credential::from_scripthash(&withdraw_script_hash2); + + let cert_witness_1 = PlutusWitness::new_without_datum(&cert_script1, &redeemer_cert1); + let cert_witness_2 = PlutusWitness::new_without_datum(&cert_script2, &redeemer_cert2); + + let ref_cert_script_input_3 = fake_tx_input(1); + let ref_cert_withdrawal_input_2 = fake_tx_input(2); + let plutus_cert_source = PlutusScriptSource::new_ref_input( + &cert_script_hash3, + &ref_cert_script_input_3, + &Language::new_plutus_v2(), + 0, + ); + let plutus_withdrawal_source = PlutusScriptSource::new_ref_input( + &withdraw_script_hash2, + &ref_cert_withdrawal_input_2, + &Language::new_plutus_v2(), + 0, + ); + + let cert_witness_3 = + PlutusWitness::new_with_ref_without_datum(&plutus_cert_source, &redeemer_cert3); + let withdraw_witness1 = + PlutusWitness::new_without_datum(&withdraw_script1, &redeemer_withdraw1); + let withdraw_witness2 = + PlutusWitness::new_with_ref_without_datum(&plutus_withdrawal_source, &redeemer_withdraw2); + + let mut certs = CertificatesBuilder::new(); + certs + .add(&Certificate::new_stake_registration( + &StakeRegistration::new(&stake_cred), + )) + .unwrap(); + certs + .add_with_plutus_witness( + &Certificate::new_stake_delegation(&StakeDelegation::new( + &cert_script_cred1, + &stake.to_raw_key().hash(), // in reality, this should be the pool owner's key, not ours + )), + &cert_witness_1, + ) + .unwrap(); + certs + .add_with_plutus_witness( + &Certificate::new_stake_delegation(&StakeDelegation::new( + &cert_script_cred2, + &stake.to_raw_key().hash(), // in reality, this should be the pool owner's key, not ours + )), + &cert_witness_2, + ) + .unwrap(); + certs + .add(&Certificate::new_stake_delegation(&StakeDelegation::new( + &stake_cred, + &stake.to_raw_key().hash(), // in reality, this should be the pool owner's key, not ours + ))) + .unwrap(); + certs + .add_with_plutus_witness( + &Certificate::new_stake_deregistration(&StakeDeregistration::new(&cert_script_cred3)), + &cert_witness_3, + ) + .unwrap(); + + tx_builder.set_certs_builder(&certs); + + let mut withdrawals = WithdrawalsBuilder::new(); + let reward_cred = Credential::from_keyhash(&reward.to_raw_key().hash()); + withdrawals + .add( + &RewardAddress::new(NetworkInfo::testnet_preprod().network_id(), &reward_cred), + &Coin::from(1u32), + ) + .unwrap(); + withdrawals + .add_with_plutus_witness( + &RewardAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &withdraw_script_cred1, + ), + &Coin::from(2u32), + &withdraw_witness1, + ) + .unwrap(); + withdrawals + .add_with_plutus_witness( + &RewardAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &withdraw_script_cred2, + ), + &Coin::from(3u32), + &withdraw_witness2, + ) + .unwrap(); + tx_builder.set_withdrawals_builder(&withdrawals); + + let change_cred = Credential::from_keyhash(&change_key.to_raw_key().hash()); + let change_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &change_cred, + &stake_cred, + ) + .to_address(); + let cost_models = TxBuilderConstants::plutus_default_cost_models(); + let collateral_input = fake_tx_input(1); + let collateral_addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &Credential::from_keyhash(&fake_key_hash(1)), + &Credential::from_keyhash(&fake_key_hash(2)), + ) + .to_address(); + let mut collateral_builder = TxInputsBuilder::new(); + collateral_builder.add_regular_input( + &collateral_addr, + &collateral_input, + &Value::new(&Coin::from(123u32)), + ).expect("Failed to add input"); + tx_builder.set_collateral(&collateral_builder); + tx_builder.calc_script_data_hash(&cost_models).unwrap(); + tx_builder.add_change_if_needed(&change_addr).unwrap(); + assert_eq!(tx_builder.outputs.len(), 1); + assert_eq!( + tx_builder + .get_explicit_input() + .unwrap() + .checked_add(&tx_builder.get_implicit_input().unwrap()) + .unwrap(), + tx_builder + .get_explicit_output() + .unwrap() + .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) + .unwrap() + .checked_add(&Value::new(&tx_builder.get_deposit().unwrap())) + .unwrap() + ); + let final_tx = tx_builder.build_tx().unwrap(); + let final_tx_body = final_tx.body(); + let final_tx_wits = final_tx.witness_set(); + + assert_eq!(final_tx_body.reference_inputs().unwrap().len(), 2); + assert_eq!(final_tx_body.withdrawals().unwrap().len(), 3); + assert_eq!(final_tx_body.certs().unwrap().len(), 5); + assert_eq!(final_tx_wits.plutus_scripts().unwrap().len(), 3); + assert_eq!(final_tx_wits.redeemers().unwrap().len(), 5); + + let certs = final_tx_body.certs().unwrap().certs; + let withdraws = final_tx_body + .withdrawals() + .unwrap() + .0 + .iter() + .map(|(k, _)| k.clone()) + .collect::>(); + let redeemers = final_tx_wits.redeemers().unwrap(); + let mut indexes = HashMap::new(); + indexes.insert(RedeemerTag::new_cert(), HashSet::new()); + indexes.insert(RedeemerTag::new_reward(), HashSet::new()); + for redeemer in &redeemers.redeemers { + let tag_set = indexes.get_mut(&redeemer.tag()).unwrap(); + assert_ne!(tag_set.contains(&redeemer.index()), true); + tag_set.insert(redeemer.index()); + let index: usize = redeemer.index().into(); + if redeemer.tag().kind() == RedeemerTagKind::Cert { + let cert = &certs[index]; + assert!(cert.has_required_script_witness()); + } else if redeemer.tag().kind() == RedeemerTagKind::Reward { + let withdraw = &withdraws[index]; + assert!(withdraw.payment_cred().has_script_hash()); + } + } +} + +#[test] +pub fn test_extra_datum() { + let mut tx_builder = fake_tx_builder(&fake_linear_fee(1, 100000), 1, 1, 1); + + let datum = PlutusData::new_bytes(fake_bytes_32(1)); + tx_builder.add_extra_witness_datum(&datum); + + let mut inp = TxInputsBuilder::new(); + inp.add_regular_input( + &fake_base_address(0), + &fake_tx_input(0), + &Value::new(&BigNum(100000000000000u64)), + ) + .unwrap(); + + tx_builder.set_inputs(&inp); + tx_builder + .calc_script_data_hash(&TxBuilderConstants::plutus_default_cost_models()) + .unwrap(); + + let change_address = fake_change_address(); + + tx_builder.add_change_if_needed(&change_address).unwrap(); + let res = tx_builder.build_tx(); + assert!(res.is_ok()); + + let tx = res.unwrap(); + + let tx_size = tx.to_bytes().len(); + let fake_input_wit_size = fake_vkey_witness(1).to_bytes().len(); + let real_fee = min_fee_for_size( + tx_size + fake_input_wit_size, + &LinearFee::new(&Coin::from(1u64), &Coin::from(100000u64)), + ) + .unwrap(); + + assert!(real_fee.less_than(&tx.body.fee)); + + let data_hash = hash_script_data( + &Redeemers::new(), + &Costmdls::new(), + Some(PlutusList::from(vec![datum.clone()])), + ); + + let tx_builder_script_data_hash = tx_builder.script_data_hash.clone(); + assert_eq!(tx_builder_script_data_hash.unwrap(), data_hash); + + let extra_datums = tx_builder.get_extra_witness_datums().unwrap(); + assert_eq!(&extra_datums.get(0), &datum); + assert_eq!(extra_datums.len(), 1usize); + assert_eq!( + tx_builder.get_witness_set().plutus_data().unwrap().len(), + 1usize + ); + assert_eq!(tx.witness_set().plutus_data().unwrap().len(), 1usize); + assert_eq!(tx.witness_set().plutus_data().unwrap().get(0), datum); +} + +#[test] +fn current_treasure_value_test() { + let input_amount = 10000000000; + let mut builder = fake_tx_builder_with_amount(input_amount, false); + let treasure_value = Coin::from(1000000000u64); + + assert_eq!(builder.get_current_treasury_value(), None); + + builder.set_current_treasury_value(&treasure_value).unwrap(); + builder + .add_change_if_needed(&fake_change_address()) + .unwrap(); + + assert_eq!( + builder.get_current_treasury_value().unwrap(), + treasure_value + ); + + let tx = builder.build_tx().unwrap(); + assert_eq!(tx.body().outputs().len(), 1); + + let mut total_out = tx.body().outputs().get(0).amount().coin(); + total_out = total_out.checked_add(&tx.body().fee()).unwrap(); + + assert_eq!(total_out, Coin::from(input_amount)); +} + +#[test] +fn current_treasure_value_zero_error_test() { + let mut builder = fake_rich_tx_builder(false); + let treasure_value = Coin::from(0u64); + + assert_eq!(builder.get_current_treasury_value(), None); + + let res = builder.set_current_treasury_value(&treasure_value); + assert!(res.is_err()); +} + +#[test] +fn donation_test() { + let input_amount = 10000000000; + let mut builder = fake_tx_builder_with_amount(input_amount, false); + let donation = Coin::from(1000u64); + + assert_eq!(builder.get_donation(), None); + + builder.set_donation(&donation); + builder + .add_change_if_needed(&fake_change_address()) + .unwrap(); + + assert_eq!(builder.get_donation().unwrap(), donation); + + let tx = builder.build_tx().unwrap(); + assert_eq!(tx.body().outputs().len(), 1); + + let mut total_out = tx.body().outputs().get(0).amount().coin(); + total_out = total_out.checked_add(&tx.body().fee()).unwrap(); + total_out = total_out.checked_add(&donation).unwrap(); + + assert_eq!(total_out, Coin::from(input_amount)); +} + +#[test] +fn ref_script_fee_from_all_builders() { + let mut mint_builder = MintBuilder::new(); + let mut cert_builder = CertificatesBuilder::new(); + let mut withdrawal_builder = WithdrawalsBuilder::new(); + let mut voting_builder = VotingBuilder::new(); + let mut voting_proposal_builder = VotingProposalBuilder::new(); + let mut tx_input_builder = TxInputsBuilder::new(); + + let tx_in_1 = fake_tx_input(1); + let tx_in_2 = fake_tx_input(2); + let tx_in_3 = fake_tx_input(3); + let tx_in_4 = fake_tx_input(4); + let tx_in_5 = fake_tx_input(5); + let tx_in_6 = fake_tx_input(6); + let tx_in_7 = fake_tx_input(7); + let tx_in_8 = fake_tx_input(8); + let tx_in_9 = fake_tx_input(9); + let tx_in_10 = fake_tx_input(10); + + let script_hash_1 = fake_script_hash(1); + let script_hash_2 = fake_script_hash(2); + let script_hash_3 = fake_script_hash(3); + let script_hash_4 = fake_script_hash(4); + let script_hash_5 = fake_script_hash(5); + let script_hash_6 = fake_script_hash(6); + let script_hash_7 = fake_script_hash(7); + let script_hash_8 = fake_script_hash(8); + + let redeemer_1 = fake_redeemer_zero_cost(1); + let redeemer_2 = fake_redeemer_zero_cost(2); + let redeemer_3 = fake_redeemer_zero_cost(3); + let redeemer_4 = fake_redeemer_zero_cost(4); + let redeemer_5 = fake_redeemer_zero_cost(5); + let redeemer_6 = fake_redeemer_zero_cost(6); + let redeemer_8 = fake_redeemer_zero_cost(8); + + let plutus_source_1 = PlutusScriptSource::new_ref_input(&script_hash_1, &tx_in_1, &Language::new_plutus_v2(), 10); + let plutus_source_2 = PlutusScriptSource::new_ref_input(&script_hash_2, &tx_in_2, &Language::new_plutus_v2(), 11); + let plutus_source_3 = PlutusScriptSource::new_ref_input(&script_hash_3, &tx_in_3, &Language::new_plutus_v2(), 111); + let plutus_source_4 = PlutusScriptSource::new_ref_input(&script_hash_4, &tx_in_4, &Language::new_plutus_v2(), 200); + let plutus_source_5 = PlutusScriptSource::new_ref_input(&script_hash_5, &tx_in_5, &Language::new_plutus_v2(), 3000); + let plutus_source_6 = PlutusScriptSource::new_ref_input(&script_hash_6, &tx_in_6, &Language::new_plutus_v2(), 5000); + let native_script_source = NativeScriptSource::new_ref_input(&script_hash_7, &tx_in_7, 10000); + let plutus_source_8 = PlutusScriptSource::new_ref_input(&script_hash_8, &tx_in_8, &Language::new_plutus_v2(), 50000); + + mint_builder.add_asset( + &MintWitness::new_plutus_script(&plutus_source_1, &redeemer_1), + &AssetName::from_hex("44544e4654").unwrap(), + &Int::new(&BigNum::from(100u64)) + ).unwrap(); + + mint_builder.add_asset( + &MintWitness::new_plutus_script(&plutus_source_2, &redeemer_2), + &AssetName::from_hex("44544e4654").unwrap(), + &Int::new(&BigNum::from(100u64)) + ).unwrap(); + + withdrawal_builder.add_with_plutus_witness( + &RewardAddress::new(NetworkInfo::testnet_preprod().network_id(), &Credential::from_scripthash(&script_hash_3)), + &Coin::from(1u64), + &PlutusWitness::new_with_ref_without_datum(&plutus_source_3, &redeemer_3) + ).unwrap(); + + withdrawal_builder.add_with_native_script( + &RewardAddress::new(NetworkInfo::testnet_preprod().network_id(), &Credential::from_scripthash(&script_hash_7)), + &Coin::from(1u64), + &native_script_source + ).unwrap(); + + cert_builder.add_with_plutus_witness( + &Certificate::new_stake_delegation(&StakeDelegation::new(&Credential::from_scripthash(&script_hash_4), &fake_key_hash(1))), + &PlutusWitness::new_with_ref_without_datum(&plutus_source_4, &redeemer_4) + ).unwrap(); + + voting_builder.add_with_plutus_witness( + &Voter::new_drep_credential(&Credential::from_scripthash(&script_hash_5)), + &GovernanceActionId::new(&fake_tx_hash(1), 1), + &VotingProcedure::new(VoteKind::Abstain), + &PlutusWitness::new_with_ref_without_datum(&plutus_source_5, &redeemer_5) + ).unwrap(); + + voting_proposal_builder.add_with_plutus_witness( + &VotingProposal::new( + &GovernanceAction::new_new_constitution_action( + &NewConstitutionAction::new( + &Constitution::new_with_script_hash(&fake_anchor(), &script_hash_6) + ) + ), + &fake_anchor(), + &RewardAddress::new(NetworkInfo::testnet_preprod().network_id(), &Credential::from_keyhash(&fake_key_hash(1))), + &Coin::from(0u64), + ), + &PlutusWitness::new_with_ref_without_datum(&plutus_source_6, &redeemer_6) + ).unwrap(); + + let input_coin = Coin::from(1000000000u64); + tx_input_builder.add_plutus_script_input( + &PlutusWitness::new_with_ref_without_datum(&plutus_source_8, &redeemer_8), + &tx_in_10, + &Value::new(&input_coin) + ); + + let mut tx_builder = fake_reallistic_tx_builder(); + let change_address = fake_base_address(1); + + tx_builder.set_mint_builder(&mint_builder); + tx_builder.set_certs_builder(&cert_builder); + tx_builder.set_withdrawals_builder(&withdrawal_builder); + tx_builder.set_voting_builder(&voting_builder); + tx_builder.set_voting_proposal_builder(&voting_proposal_builder); + tx_builder.set_inputs(&tx_input_builder); + tx_builder.add_script_reference_input(&tx_in_9, 16000); + + let fake_collateral = fake_tx_input(99); + let mut collateral_builder = TxInputsBuilder::new(); + collateral_builder.add_regular_input( + &fake_base_address(99), + &fake_collateral, + &Value::new(&Coin::from(1000000000u64)) + ).unwrap(); + + tx_builder.set_collateral(&collateral_builder); + tx_builder.calc_script_data_hash(&TxBuilderConstants::plutus_default_cost_models()).unwrap(); + tx_builder.add_change_if_needed(&change_address).unwrap(); + + let res = tx_builder.build_tx(); + assert!(res.is_ok()); + + let mut tx = res.unwrap(); + let mut vkey_witneses = Vkeywitnesses::new(); + vkey_witneses.add(&fake_vkey_witness(1)); + let mut wit_set = tx.witness_set(); + wit_set.set_vkeys(&vkey_witneses); + tx = Transaction::new(&tx.body(), &wit_set, tx.auxiliary_data()); + + let ref_script_fee = BigNum::from(53099u64); + let total_tx_fee = tx.body().fee(); + + //TODO: check change calculation for pessimistic size estimation. + let tx_size = tx.to_bytes().len() + 4; + + let min_tx_fee = min_fee_for_size(tx_size, &fake_linear_fee(44, 155381)).unwrap(); + let fee_leftover = total_tx_fee.checked_sub(&min_tx_fee).unwrap(); + assert_eq!(ref_script_fee, fee_leftover); + + let tx_out_coin = tx.body().outputs().get(0).amount().coin(); + let total_out = tx_out_coin + .checked_add(&total_tx_fee).unwrap() + .checked_sub(&Coin::from(2u64)).unwrap(); // withdrawals + + assert_eq!(total_out, input_coin); + + let ref_inputs = tx.body().reference_inputs().unwrap(); + assert!(ref_inputs.contains(&tx_in_1)); + assert!(ref_inputs.contains(&tx_in_2)); + assert!(ref_inputs.contains(&tx_in_3)); + assert!(ref_inputs.contains(&tx_in_4)); + assert!(ref_inputs.contains(&tx_in_5)); + assert!(ref_inputs.contains(&tx_in_6)); + assert!(ref_inputs.contains(&tx_in_7)); + assert!(ref_inputs.contains(&tx_in_8)); + assert!(ref_inputs.contains(&tx_in_9)); + assert_eq!(ref_inputs.len(), 9); +} + + +#[test] +fn utxo_selection_accounts_for_change_min_utxo_test() { + let mut tx_builder = fake_reallistic_tx_builder(); + let hex_utxos = [ + "82825820731224c9d2bc3528578009fec9f9e34a67110aca2bd4dde0f050845a2daf660d0082583900436075347d6a452eba4289ae345a8eb15e73eb80979a7e817d988fc56c8e2cfd5a9478355fa1d60759f93751237af3299d7faa947023e493821a001deabfa1581c9a5e0d55cdf4ce4e19c8acbff7b4dafc890af67a594a4c46d7dd1c0fa14001", + "82825820a04996d5ef87fdece0c74625f02ee5c1497a06e0e476c5095a6b0626b295074a00825839001772f234940519e71318bb9c5c8ad6eacfe8fd91a509050624e3855e6c8e2cfd5a9478355fa1d60759f93751237af3299d7faa947023e4931a0016e360" + ]; + let output = TransactionOutput::new(&Address::from_bech32("addr_test1qppkqaf5044y2t46g2y6udz636c4uultszte5l5p0kvgl3tv3ck06k550q64lgwkqavljd63yda0x2va074fguprujfsjre4xh").unwrap(), &Value::new(&BigNum::from_str("969750").unwrap())); + tx_builder.add_output(&output).expect("Failed to add output"); + let mut utxos = TransactionUnspentOutputs::new(); + for hex_utxo in hex_utxos { + utxos.add(&TransactionUnspentOutput::from_hex(hex_utxo).unwrap()); + } + let change_config = ChangeConfig::new(&Address::from_bech32("addr_test1qqzf7fhgm0gf370ngxgpskg5c3kgp2g0u4ltxlrmsvumaztv3ck06k550q64lgwkqavljd63yda0x2va074fguprujfs43mc83").unwrap()); + assert!(&tx_builder.add_inputs_from_and_change(&utxos, CoinSelectionStrategyCIP2::LargestFirstMultiAsset, &change_config).is_ok()); + let build_res = tx_builder.build_tx(); + assert!(&build_res.is_ok()); +} + +#[test] +fn utxo_selection_with_collateral_return_test() { + let mut tx_builder = fake_reallistic_tx_builder(); + let hex_utxos = [ + "82825820731224c9d2bc3528578009fec9f9e34a67110aca2bd4dde0f050845a2daf660d0082583900436075347d6a452eba4289ae345a8eb15e73eb80979a7e817d988fc56c8e2cfd5a9478355fa1d60759f93751237af3299d7faa947023e493821a001deabfa1581c9a5e0d55cdf4ce4e19c8acbff7b4dafc890af67a594a4c46d7dd1c0fa14001", + "82825820a04996d5ef87fdece0c74625f02ee5c1497a06e0e476c5095a6b0626b295074a00825839001772f234940519e71318bb9c5c8ad6eacfe8fd91a509050624e3855e6c8e2cfd5a9478355fa1d60759f93751237af3299d7faa947023e4931a0016e360" + ]; + + let collateral_percent = BigNum(150); + let output = TransactionOutput::new(&Address::from_bech32("addr_test1qppkqaf5044y2t46g2y6udz636c4uultszte5l5p0kvgl3tv3ck06k550q64lgwkqavljd63yda0x2va074fguprujfsjre4xh").unwrap(), &Value::new(&BigNum::from_str("969750").unwrap())); + tx_builder.add_output(&output).unwrap(); + let mut utxos = TransactionUnspentOutputs::new(); + for hex_utxo in hex_utxos { + utxos.add(&TransactionUnspentOutput::from_hex(hex_utxo).unwrap()); + } + let mut collateral_builder = TxInputsBuilder::new(); + let collateral_input = TransactionUnspentOutput::from_hex(hex_utxos[1]).unwrap(); + collateral_builder.add_regular_input(&collateral_input.output.address, &collateral_input.input, &collateral_input.output.amount).unwrap(); + tx_builder.set_collateral(&collateral_builder); + + let change_config = ChangeConfig::new(&Address::from_bech32("addr_test1qqzf7fhgm0gf370ngxgpskg5c3kgp2g0u4ltxlrmsvumaztv3ck06k550q64lgwkqavljd63yda0x2va074fguprujfs43mc83").unwrap()); + assert!(&tx_builder.add_inputs_from_and_change_with_collateral_return(&utxos, CoinSelectionStrategyCIP2::LargestFirstMultiAsset, &change_config, &collateral_percent).is_ok()); + + let build_res = tx_builder.build_tx(); + assert!(&build_res.is_ok()); + + let tx = build_res.unwrap(); + assert!(&tx.body.collateral_return().is_some()); + + let mut vkey_witneses = Vkeywitnesses::new(); + vkey_witneses.add(&fake_vkey_witness(1)); + vkey_witneses.add(&fake_vkey_witness(2)); + let mut wit_set = tx.witness_set(); + wit_set.set_vkeys(&vkey_witneses); + + let tx_with_vkeys = Transaction::new(&tx.body(), &wit_set, tx.auxiliary_data()); + let tx_size = tx_with_vkeys.to_bytes().len(); + let fee = tx.body().fee(); + let min_fee = min_fee_for_size(tx_size, &fake_linear_fee(44, 155381)).unwrap(); + assert!(fee >= min_fee); + + let collateral_amount = tx.body.total_collateral.unwrap(); + let calculated_collateral = fee. + checked_mul(&collateral_percent).unwrap() + .div_floor(&BigNum(100)) + .checked_add(&BigNum(1)).unwrap(); + + assert_eq!(collateral_amount, calculated_collateral); +} + +#[test] +fn utxo_selection_with_collateral_return_error() { + let mut tx_builder = fake_reallistic_tx_builder(); + let hex_utxos = [ + "82825820731224c9d2bc3528578009fec9f9e34a67110aca2bd4dde0f050845a2daf660d0082583900436075347d6a452eba4289ae345a8eb15e73eb80979a7e817d988fc56c8e2cfd5a9478355fa1d60759f93751237af3299d7faa947023e493821a001deabfa1581c9a5e0d55cdf4ce4e19c8acbff7b4dafc890af67a594a4c46d7dd1c0fa14001", + "82825820a04996d5ef87fdece0c74625f02ee5c1497a06e0e476c5095a6b0626b295074a00825839001772f234940519e71318bb9c5c8ad6eacfe8fd91a509050624e3855e6c8e2cfd5a9478355fa1d60759f93751237af3299d7faa947023e4931a0016e360" + ]; + + //we use big percentage to lead not enough collaterals to cover the collateral fee + let collateral_percent = BigNum(15000); + let output = TransactionOutput::new(&Address::from_bech32("addr_test1qppkqaf5044y2t46g2y6udz636c4uultszte5l5p0kvgl3tv3ck06k550q64lgwkqavljd63yda0x2va074fguprujfsjre4xh").unwrap(), &Value::new(&BigNum::from_str("969750").unwrap())); + tx_builder.add_output(&output).unwrap(); + let mut utxos = TransactionUnspentOutputs::new(); + for hex_utxo in hex_utxos { + utxos.add(&TransactionUnspentOutput::from_hex(hex_utxo).unwrap()); + } + let mut collateral_builder = TxInputsBuilder::new(); + let collateral_input = TransactionUnspentOutput::from_hex(hex_utxos[1]).unwrap(); + collateral_builder.add_regular_input(&collateral_input.output.address, &collateral_input.input, &collateral_input.output.amount).unwrap(); + tx_builder.set_collateral(&collateral_builder); + + let change_config = ChangeConfig::new(&Address::from_bech32("addr_test1qqzf7fhgm0gf370ngxgpskg5c3kgp2g0u4ltxlrmsvumaztv3ck06k550q64lgwkqavljd63yda0x2va074fguprujfs43mc83").unwrap()); + let change_res = tx_builder.add_inputs_from_and_change_with_collateral_return(&utxos, CoinSelectionStrategyCIP2::LargestFirstMultiAsset, &change_config, &collateral_percent); + assert!(change_res.is_err()); +} + + +#[test] +fn tx_builder_huge_implicit_input_coin_selection() { + // we have a = 1 to test increasing fees when more inputs are added + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(1, 0)); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address( + &Address::from_bech32( + "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z", + ) + .unwrap(), + ) + .next() + .unwrap() + .with_coin(&BigNum(10000)) + .build() + .unwrap(), + ) + .unwrap(); + let mut cert_builder = CertificatesBuilder::new(); + cert_builder + .add( + &Certificate::new_stake_deregistration(&StakeDeregistration::new_with_explicit_refund( + &Credential::from_keyhash(&fake_key_hash(1)), + &BigNum(9999999999), + )), + ) + .unwrap(); + + tx_builder.set_certs_builder(&cert_builder); + let mut available_inputs = TransactionUnspentOutputs::new(); + available_inputs.add(&make_input(0u8, Value::new(&BigNum(1500)))); + available_inputs.add(&make_input(1u8, Value::new(&BigNum(2000)))); + + let add_inputs_res = + tx_builder.add_inputs_from(&available_inputs, CoinSelectionStrategyCIP2::RandomImprove); + assert!(add_inputs_res.is_ok(), "{:?}", add_inputs_res.err()); + assert_eq!(tx_builder.inputs.len(), 1); + + let change_addr = + ByronAddress::from_base58("Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho") + .unwrap() + .to_address(); + let add_change_res = tx_builder.add_change_if_needed(&change_addr); + assert!(add_change_res.is_ok(), "{:?}", add_change_res.err()); + let tx_build_res = tx_builder.build(); + assert!(tx_build_res.is_ok(), "{:?}", tx_build_res.err()); +} + +#[test] +fn tx_builder_no_any_input_coin_selection() { + // we have a = 1 to test increasing fees when more inputs are added + let mut tx_builder = fake_tx_builder_with_fee(&fake_linear_fee(1, 0)); + tx_builder + .add_output( + &TransactionOutputBuilder::new() + .with_address( + &Address::from_bech32( + "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z", + ) + .unwrap(), + ) + .next() + .unwrap() + .with_coin(&BigNum(10000)) + .build() + .unwrap(), + ) + .unwrap(); + let mut cert_builder = CertificatesBuilder::new(); + cert_builder + .add( + &Certificate::new_stake_deregistration(&StakeDeregistration::new_with_explicit_refund( + &Credential::from_keyhash(&fake_key_hash(1)), + &BigNum(9999999999), + )), + ) + .unwrap(); + + tx_builder.set_certs_builder(&cert_builder); + let available_inputs = TransactionUnspentOutputs::new(); + + let add_inputs_res = + tx_builder.add_inputs_from(&available_inputs, CoinSelectionStrategyCIP2::RandomImprove); + assert!(add_inputs_res.is_err()); +} + + +#[test] +fn ref_inputs_debuplication_test() { + let mut mint_builder = MintBuilder::new(); + let mut cert_builder = CertificatesBuilder::new(); + let mut withdrawal_builder = WithdrawalsBuilder::new(); + let mut voting_builder = VotingBuilder::new(); + let mut voting_proposal_builder = VotingProposalBuilder::new(); + let mut tx_input_builder = TxInputsBuilder::new(); + + let tx_in_1 = fake_tx_input(1); + let tx_in_2 = fake_tx_input(2); + let tx_in_3 = fake_tx_input(3); + let tx_in_4 = fake_tx_input(4); + let tx_in_5 = fake_tx_input(5); + let tx_in_6 = fake_tx_input(6); + let tx_in_7 = fake_tx_input(7); + let tx_in_8 = fake_tx_input(8); + let tx_in_9 = fake_tx_input(9); + let tx_in_10 = fake_tx_input(10); + + let script_hash_1 = fake_script_hash(1); + let script_hash_2 = fake_script_hash(2); + let script_hash_3 = fake_script_hash(3); + let script_hash_4 = fake_script_hash(4); + let script_hash_5 = fake_script_hash(5); + let script_hash_6 = fake_script_hash(6); + let script_hash_7 = fake_script_hash(7); + let script_hash_8 = fake_script_hash(8); + + let redeemer_1 = fake_redeemer_zero_cost(1); + let redeemer_2 = fake_redeemer_zero_cost(2); + let redeemer_3 = fake_redeemer_zero_cost(3); + let redeemer_4 = fake_redeemer_zero_cost(4); + let redeemer_5 = fake_redeemer_zero_cost(5); + let redeemer_6 = fake_redeemer_zero_cost(6); + let redeemer_8 = fake_redeemer_zero_cost(8); + + let plutus_source_1 = PlutusScriptSource::new_ref_input(&script_hash_1, &tx_in_1, &Language::new_plutus_v2(), 10); + let plutus_source_2 = PlutusScriptSource::new_ref_input(&script_hash_2, &tx_in_2, &Language::new_plutus_v2(), 11); + let plutus_source_3 = PlutusScriptSource::new_ref_input(&script_hash_3, &tx_in_3, &Language::new_plutus_v2(), 111); + let plutus_source_4 = PlutusScriptSource::new_ref_input(&script_hash_4, &tx_in_4, &Language::new_plutus_v2(), 200); + let plutus_source_5 = PlutusScriptSource::new_ref_input(&script_hash_5, &tx_in_5, &Language::new_plutus_v2(), 3000); + let plutus_source_6 = PlutusScriptSource::new_ref_input(&script_hash_6, &tx_in_6, &Language::new_plutus_v2(), 5000); + let native_script_source = NativeScriptSource::new_ref_input(&script_hash_7, &tx_in_7, 10000); + let plutus_source_8 = PlutusScriptSource::new_ref_input(&script_hash_8, &tx_in_8, &Language::new_plutus_v2(), 50000); + + mint_builder.add_asset( + &MintWitness::new_plutus_script(&plutus_source_1, &redeemer_1), + &AssetName::from_hex("44544e4654").unwrap(), + &Int::new(&BigNum::from(100u64)) + ).unwrap(); + + mint_builder.add_asset( + &MintWitness::new_plutus_script(&plutus_source_2, &redeemer_2), + &AssetName::from_hex("44544e4654").unwrap(), + &Int::new(&BigNum::from(100u64)) + ).unwrap(); + + withdrawal_builder.add_with_plutus_witness( + &RewardAddress::new(NetworkInfo::testnet_preprod().network_id(), &Credential::from_scripthash(&script_hash_3)), + &Coin::from(1u64), + &PlutusWitness::new_with_ref_without_datum(&plutus_source_3, &redeemer_3) + ).unwrap(); + + withdrawal_builder.add_with_native_script( + &RewardAddress::new(NetworkInfo::testnet_preprod().network_id(), &Credential::from_scripthash(&script_hash_7)), + &Coin::from(1u64), + &native_script_source + ).unwrap(); + + cert_builder.add_with_plutus_witness( + &Certificate::new_stake_delegation(&StakeDelegation::new(&Credential::from_scripthash(&script_hash_4), &fake_key_hash(1))), + &PlutusWitness::new_with_ref_without_datum(&plutus_source_4, &redeemer_4) + ).unwrap(); + + voting_builder.add_with_plutus_witness( + &Voter::new_drep_credential(&Credential::from_scripthash(&script_hash_5)), + &GovernanceActionId::new(&fake_tx_hash(1), 1), + &VotingProcedure::new(VoteKind::Abstain), + &PlutusWitness::new_with_ref_without_datum(&plutus_source_5, &redeemer_5) + ).unwrap(); + + voting_proposal_builder.add_with_plutus_witness( + &VotingProposal::new( + &GovernanceAction::new_new_constitution_action( + &NewConstitutionAction::new( + &Constitution::new_with_script_hash(&fake_anchor(), &script_hash_6) + ) + ), + &fake_anchor(), + &RewardAddress::new(NetworkInfo::testnet_preprod().network_id(), &Credential::from_keyhash(&fake_key_hash(1))), + &Coin::from(0u64), + ), + &PlutusWitness::new_with_ref_without_datum(&plutus_source_6, &redeemer_6) + ).unwrap(); + + let input_coin = Coin::from(1000000000u64); + tx_input_builder.add_plutus_script_input( + &PlutusWitness::new_with_ref_without_datum(&plutus_source_8, &redeemer_8), + &tx_in_10, + &Value::new(&input_coin) + ); + + let mut tx_builder = fake_reallistic_tx_builder(); + let change_address = fake_base_address(1); + + tx_builder.set_mint_builder(&mint_builder); + tx_builder.set_certs_builder(&cert_builder); + tx_builder.set_withdrawals_builder(&withdrawal_builder); + tx_builder.set_voting_builder(&voting_builder); + tx_builder.set_voting_proposal_builder(&voting_proposal_builder); + tx_builder.set_inputs(&tx_input_builder); + tx_builder.add_script_reference_input(&tx_in_9, 16000); + + tx_builder.add_regular_input(&fake_base_address(1), &tx_in_1, &Value::new(&Coin::from(1000000000u64))).unwrap(); + tx_builder.add_regular_input(&fake_base_address(1), &tx_in_2, &Value::new(&Coin::from(1000000000u64))).unwrap(); + tx_builder.add_regular_input(&fake_base_address(1), &tx_in_3, &Value::new(&Coin::from(1000000000u64))).unwrap(); + tx_builder.add_regular_input(&fake_base_address(1), &tx_in_4, &Value::new(&Coin::from(1000000000u64))).unwrap(); + tx_builder.add_regular_input(&fake_base_address(1), &tx_in_5, &Value::new(&Coin::from(1000000000u64))).unwrap(); + tx_builder.add_regular_input(&fake_base_address(1), &tx_in_6, &Value::new(&Coin::from(1000000000u64))).unwrap(); + tx_builder.add_regular_input(&fake_base_address(1), &tx_in_7, &Value::new(&Coin::from(1000000000u64))).unwrap(); + tx_builder.add_regular_input(&fake_base_address(1), &tx_in_8, &Value::new(&Coin::from(1000000000u64))).unwrap(); + + + let fake_collateral = fake_tx_input(99); + let mut collateral_builder = TxInputsBuilder::new(); + collateral_builder.add_regular_input( + &fake_base_address(99), + &fake_collateral, + &Value::new(&Coin::from(1000000000u64)) + ).unwrap(); + + tx_builder.set_collateral(&collateral_builder); + tx_builder.calc_script_data_hash(&TxBuilderConstants::plutus_default_cost_models()).unwrap(); + tx_builder.add_change_if_needed(&change_address).unwrap(); + + let res = tx_builder.build_tx(); + assert!(res.is_ok()); + + let mut tx = res.unwrap(); + let mut vkey_witneses = Vkeywitnesses::new(); + vkey_witneses.add(&fake_vkey_witness(1)); + let mut wit_set = tx.witness_set(); + wit_set.set_vkeys(&vkey_witneses); + tx = Transaction::new(&tx.body(), &wit_set, tx.auxiliary_data()); + + let total_tx_fee = tx.body().fee(); + + let tx_out_coin = tx.body().outputs().get(0).amount().coin(); + let total_out = tx_out_coin + .checked_add(&total_tx_fee).unwrap() + .checked_sub(&Coin::from(2u64)).unwrap(); // withdrawals + + assert_eq!(total_out, input_coin.checked_mul(&BigNum(9)).unwrap()); + + let ref_inputs = tx.body().reference_inputs().unwrap(); + assert!(ref_inputs.contains(&tx_in_9)); + assert_eq!(ref_inputs.len(), 1); +} + +#[test] +fn ref_inputs_and_reg_inputs_intersection_error() { + let tx_in_1 = fake_tx_input(1); + let tx_in_2 = fake_tx_input(2); + + let mut tx_builder = fake_reallistic_tx_builder(); + let change_address = fake_base_address(1); + + tx_builder.add_regular_input(&fake_base_address(1), &tx_in_1, &Value::new(&Coin::from(1000000000u64))).unwrap(); + tx_builder.add_regular_input(&fake_base_address(1), &tx_in_2, &Value::new(&Coin::from(1000000000u64))).unwrap(); + + tx_builder.add_reference_input(&tx_in_1); + tx_builder.add_change_if_needed(&change_address).unwrap(); + + let res = tx_builder.build_tx(); + assert!(res.is_err()); +} + +#[test] +fn ref_inputs_and_reg_inputs_no_intersection_error() { + let tx_in_1 = fake_tx_input(1); + let tx_in_2 = fake_tx_input(2); + let tx_in_3 = fake_tx_input(3); + + let linear_fee = LinearFee::new(&BigNum(1), &BigNum(0)); + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(&linear_fee) + .pool_deposit(&BigNum(0)) + .key_deposit(&BigNum(0)) + .max_value_size(9999) + .max_tx_size(9999) + .coins_per_utxo_byte(&Coin::zero()) + .deduplicate_explicit_ref_inputs_with_regular_inputs(true) + .build() + .unwrap(); + let mut tx_builder = TransactionBuilder::new(&cfg); + let change_address = fake_base_address(1); + + tx_builder.add_regular_input(&fake_base_address(1), &tx_in_1, &Value::new(&Coin::from(1000000000u64))).unwrap(); + tx_builder.add_regular_input(&fake_base_address(1), &tx_in_2, &Value::new(&Coin::from(1000000000u64))).unwrap(); + + tx_builder.add_reference_input(&tx_in_1); + tx_builder.add_reference_input(&tx_in_3); + tx_builder.add_change_if_needed(&change_address).unwrap(); + + let res = tx_builder.build_tx(); + assert!(res.is_ok()); + + let tx = res.unwrap(); + + let ref_inputs = tx.body().reference_inputs().unwrap(); + assert!(ref_inputs.contains(&tx_in_3)); + assert_eq!(ref_inputs.len(), 1); +} \ No newline at end of file diff --git a/rust/src/tests/builders/tx_builder_constans.rs b/rust/src/tests/builders/tx_builder_constans.rs new file mode 100644 index 00000000..781bd407 --- /dev/null +++ b/rust/src/tests/builders/tx_builder_constans.rs @@ -0,0 +1,48 @@ +use crate::*; + +#[test] +pub fn cost_model_test() { + assert_eq!( + TxBuilderConstants::plutus_alonzo_cost_models() + .get(&Language::new_plutus_v1()) + .unwrap() + .len(), + 166, + ); + assert_eq!( + TxBuilderConstants::plutus_vasil_cost_models() + .get(&Language::new_plutus_v1()) + .unwrap() + .len(), + 166, + ); + assert_eq!( + TxBuilderConstants::plutus_vasil_cost_models() + .get(&Language::new_plutus_v2()) + .unwrap() + .len(), + 175, + ); + assert_eq!( + hex::encode(TxBuilderConstants::plutus_alonzo_cost_models().language_views_encoding()), + "a141005901d59f1a000302590001011a00060bc719026d00011a000249f01903e800011a000249f018201a0025cea81971f70419744d186419744d186419744d186419744d186419744d186419744d18641864186419744d18641a000249f018201a000249f018201a000249f018201a000249f01903e800011a000249f018201a000249f01903e800081a000242201a00067e2318760001011a000249f01903e800081a000249f01a0001b79818f7011a000249f0192710011a0002155e19052e011903e81a000249f01903e8011a000249f018201a000249f018201a000249f0182001011a000249f0011a000249f0041a000194af18f8011a000194af18f8011a0002377c190556011a0002bdea1901f1011a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000242201a00067e23187600010119f04c192bd200011a000249f018201a000242201a00067e2318760001011a000242201a00067e2318760001011a0025cea81971f704001a000141bb041a000249f019138800011a000249f018201a000302590001011a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a00330da70101ff", + ); + assert_eq!( + hex::encode(TxBuilderConstants::plutus_vasil_cost_models().language_views_encoding()), + "a20198af1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a0011b22c1a0005fdde00021a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201a0223accc0a1a0374f693194a1f0a1a02515e841980b30a41005901b69f1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201a0374f693194a1f0aff", + ); +} + +#[test] +pub fn from_json() { + println!( + "{}", + TxBuilderConstants::plutus_vasil_cost_models() + .to_json() + .unwrap() + ); + assert_eq!( + TxBuilderConstants::plutus_vasil_cost_models().to_hex(), + Costmdls::from_json("{ \"PlutusV1\": [\"205665\",\"812\",\"1\",\"1\",\"1000\",\"571\",\"0\",\"1\",\"1000\",\"24177\",\"4\",\"1\",\"1000\",\"32\",\"117366\",\"10475\",\"4\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"100\",\"100\",\"23000\",\"100\",\"19537\",\"32\",\"175354\",\"32\",\"46417\",\"4\",\"221973\",\"511\",\"0\",\"1\",\"89141\",\"32\",\"497525\",\"14068\",\"4\",\"2\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"1000\",\"28662\",\"4\",\"2\",\"245000\",\"216773\",\"62\",\"1\",\"1060367\",\"12586\",\"1\",\"208512\",\"421\",\"1\",\"187000\",\"1000\",\"52998\",\"1\",\"80436\",\"32\",\"43249\",\"32\",\"1000\",\"32\",\"80556\",\"1\",\"57667\",\"4\",\"1000\",\"10\",\"197145\",\"156\",\"1\",\"197145\",\"156\",\"1\",\"204924\",\"473\",\"1\",\"208896\",\"511\",\"1\",\"52467\",\"32\",\"64832\",\"32\",\"65493\",\"32\",\"22558\",\"32\",\"16563\",\"32\",\"76511\",\"32\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"69522\",\"11687\",\"0\",\"1\",\"60091\",\"32\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"806990\",\"30482\",\"4\",\"1927926\",\"82523\",\"4\",\"265318\",\"0\",\"4\",\"0\",\"85931\",\"32\",\"205665\",\"812\",\"1\",\"1\",\"41182\",\"32\",\"212342\",\"32\",\"31220\",\"32\",\"32696\",\"32\",\"43357\",\"32\",\"32247\",\"32\",\"38314\",\"32\",\"57996947\",\"18975\",\"10\"], \"PlutusV2\": [\"205665\",\"812\",\"1\",\"1\",\"1000\",\"571\",\"0\",\"1\",\"1000\",\"24177\",\"4\",\"1\",\"1000\",\"32\",\"117366\",\"10475\",\"4\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"100\",\"100\",\"23000\",\"100\",\"19537\",\"32\",\"175354\",\"32\",\"46417\",\"4\",\"221973\",\"511\",\"0\",\"1\",\"89141\",\"32\",\"497525\",\"14068\",\"4\",\"2\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"1000\",\"28662\",\"4\",\"2\",\"245000\",\"216773\",\"62\",\"1\",\"1060367\",\"12586\",\"1\",\"208512\",\"421\",\"1\",\"187000\",\"1000\",\"52998\",\"1\",\"80436\",\"32\",\"43249\",\"32\",\"1000\",\"32\",\"80556\",\"1\",\"57667\",\"4\",\"1000\",\"10\",\"197145\",\"156\",\"1\",\"197145\",\"156\",\"1\",\"204924\",\"473\",\"1\",\"208896\",\"511\",\"1\",\"52467\",\"32\",\"64832\",\"32\",\"65493\",\"32\",\"22558\",\"32\",\"16563\",\"32\",\"76511\",\"32\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"69522\",\"11687\",\"0\",\"1\",\"60091\",\"32\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"1159724\",\"392670\",\"0\",\"2\",\"806990\",\"30482\",\"4\",\"1927926\",\"82523\",\"4\",\"265318\",\"0\",\"4\",\"0\",\"85931\",\"32\",\"205665\",\"812\",\"1\",\"1\",\"41182\",\"32\",\"212342\",\"32\",\"31220\",\"32\",\"32696\",\"32\",\"43357\",\"32\",\"32247\",\"32\",\"38314\",\"32\",\"35892428\",\"10\",\"57996947\",\"18975\",\"10\",\"38887044\",\"32947\",\"10\"] }").unwrap().to_hex(), + ); +} diff --git a/rust/src/tests/builders/tx_inputs_builder.rs b/rust/src/tests/builders/tx_inputs_builder.rs new file mode 100644 index 00000000..52391d27 --- /dev/null +++ b/rust/src/tests/builders/tx_inputs_builder.rs @@ -0,0 +1,386 @@ +use crate::tests::fakes::{fake_base_address, fake_base_script_address, fake_enterprise_address, fake_enterprise_script_address, fake_plutus_script, fake_pointer_address, fake_pointer_script_address, fake_redeemer, fake_reward_address, fake_key_hash, fake_script_hash, fake_tx_input}; +use crate::*; + +#[test] +fn regular_inputs() { + let mut tx_inputs_builder = TxInputsBuilder::new(); + let base_address_1 = fake_base_address(1); + let tx_input_1 = fake_tx_input(1); + let input_value_1 = Value::new(&BigNum(100)); + tx_inputs_builder + .add_regular_input(&base_address_1, &tx_input_1, &input_value_1) + .unwrap(); + + let enterprise_address_2 = fake_enterprise_address(2); + let tx_input_2 = fake_tx_input(2); + let input_value_2 = Value::new(&BigNum(200)); + tx_inputs_builder + .add_regular_input(&enterprise_address_2, &tx_input_2, &input_value_2) + .unwrap(); + + let pointer_address_3 = fake_pointer_address(3); + let tx_input_3 = fake_tx_input(3); + let input_value_3 = Value::new(&BigNum(300)); + tx_inputs_builder + .add_regular_input(&pointer_address_3, &tx_input_3, &input_value_3) + .unwrap(); + + let byron_address_4 = + ByronAddress::from_base58("Ae2tdPwUPEZ3MHKkpT5Bpj549vrRH7nBqYjNXnCV8G2Bc2YxNcGHEa8ykDp") + .unwrap() + .to_address(); + let tx_input_4 = fake_tx_input(4); + let input_value_4 = Value::new(&BigNum(400)); + tx_inputs_builder + .add_regular_input(&byron_address_4, &tx_input_4, &input_value_4) + .unwrap(); + + let key_hash_5 = fake_key_hash(5); + let tx_input_5 = fake_tx_input(5); + let input_value_5 = Value::new(&BigNum(500)); + tx_inputs_builder.add_key_input(&key_hash_5, &tx_input_5, &input_value_5); + + let byron_address_6 = + ByronAddress::from_base58("Ae2tdPwUPEZ6r6zbg4ibhFrNnyKHg7SYuPSfDpjKxgvwFX9LquRep7gj7FQ") + .unwrap(); + let tx_input_6 = fake_tx_input(6); + let input_value_6 = Value::new(&BigNum(600)); + tx_inputs_builder.add_bootstrap_input(&byron_address_6, &tx_input_6, &input_value_6); + + let key_hash_7 = fake_key_hash(7); + tx_inputs_builder.add_required_signer(&key_hash_7); + + let ref_inputs = tx_inputs_builder.get_ref_inputs(); + assert_eq!(ref_inputs.len(), 0); + + let native_scripts = tx_inputs_builder.get_native_input_scripts(); + assert!(native_scripts.is_none()); + + let plutus_scripts = tx_inputs_builder.get_plutus_input_scripts(); + assert!(plutus_scripts.is_none()); + + assert_eq!(tx_inputs_builder.has_plutus_scripts(), false); + + let required_signatures: Ed25519KeyHashes = tx_inputs_builder.get_required_signers(); + assert_eq!(required_signatures.len(), 5); + + required_signatures.contains(&base_address_1.payment_cred().unwrap().to_keyhash().unwrap()); + required_signatures.contains( + &enterprise_address_2 + .payment_cred() + .unwrap() + .to_keyhash() + .unwrap(), + ); + required_signatures.contains( + &pointer_address_3 + .payment_cred() + .unwrap() + .to_keyhash() + .unwrap(), + ); + required_signatures.contains(&key_hash_5); + required_signatures.contains(&key_hash_7); + + let tx_inputs = tx_inputs_builder.inputs(); + assert_eq!(tx_inputs.len(), 6); + assert!(tx_inputs.contains(&tx_input_1)); + assert!(tx_inputs.contains(&tx_input_2)); + assert!(tx_inputs.contains(&tx_input_3)); + assert!(tx_inputs.contains(&tx_input_4)); + assert!(tx_inputs.contains(&tx_input_5)); + assert!(tx_inputs.contains(&tx_input_6)); + + let bootstraps = get_bootstraps(&tx_inputs_builder); + let boostrap_1 = ByronAddress::from_address(&byron_address_4) + .unwrap() + .to_bytes(); + let boostrap_2 = byron_address_6.to_bytes(); + assert_eq!(bootstraps.len(), 2); + + assert!(bootstraps.contains(&boostrap_1)); + assert!(bootstraps.contains(&boostrap_2)); +} + +#[test] +fn script_input_as_regular_input_error() { + let mut tx_inputs_builder = TxInputsBuilder::new(); + let plutus_script = fake_tx_input(1); + let input_value = Value::new(&BigNum(100)); + + let base_address_1 = fake_base_script_address(1); + let res_1 = tx_inputs_builder.add_regular_input(&base_address_1, &plutus_script, &input_value); + assert!(res_1.is_err()); + + let enterprise_address_2 = fake_enterprise_script_address(2); + let res_2 = + tx_inputs_builder.add_regular_input(&enterprise_address_2, &plutus_script, &input_value); + assert!(res_2.is_err()); + + let pointer_address_3 = fake_pointer_script_address(3); + let res_3 = + tx_inputs_builder.add_regular_input(&pointer_address_3, &plutus_script, &input_value); + assert!(res_3.is_err()); +} + +#[test] +fn rewards_address_input_as_regular_input_error() { + let mut tx_inputs_builder = TxInputsBuilder::new(); + let rewards_address = fake_reward_address(1).to_address(); + let tx_input = fake_tx_input(1); + let input_value = Value::new(&BigNum(100)); + let res = tx_inputs_builder.add_regular_input(&rewards_address, &tx_input, &input_value); + assert!(res.is_err()); +} + +#[test] +fn plutus_script_input() { + let mut tx_inputs_builder = TxInputsBuilder::new(); + let tx_input_1 = fake_tx_input(1); + let input_value_1 = Value::new(&BigNum(100)); + + let plutus_script = fake_plutus_script(1, &Language::new_plutus_v2()); + let plutus_script_source = PlutusScriptSource::new(&plutus_script); + let redeemer = fake_redeemer(1) + .clone_with_index_and_tag(&BigNum(0), &RedeemerTag::new_spend()); + + let datum = PlutusData::new_empty_constr_plutus_data(&BigNum::zero()); + let plutus_witness = + PlutusWitness::new_with_ref(&plutus_script_source, &DatumSource::new(&datum), &redeemer); + + tx_inputs_builder.add_plutus_script_input(&plutus_witness, &tx_input_1, &input_value_1); + + let plutus_scripts = tx_inputs_builder.get_plutus_input_scripts().unwrap(); + assert_eq!(plutus_scripts.len(), 1); + + let plutus_wit_from_builder = plutus_scripts.get(0); + assert_eq!(plutus_wit_from_builder.script().unwrap(), plutus_script); + assert_eq!(plutus_wit_from_builder.datum().unwrap(), datum); + assert_eq!(plutus_wit_from_builder.redeemer(), redeemer); + + assert!(tx_inputs_builder.has_plutus_scripts()); + + let req_signers = tx_inputs_builder.get_required_signers(); + assert_eq!(req_signers.len(), 0); + + let inputs = tx_inputs_builder.inputs(); + assert_eq!(inputs.len(), 1); + assert!(inputs.contains(&tx_input_1)); + + let ref_inputs = tx_inputs_builder.get_ref_inputs(); + assert_eq!(ref_inputs.len(), 0); +} + +#[test] +fn plutus_script_input_with_required_signers() { + let mut tx_inputs_builder = TxInputsBuilder::new(); + let tx_input_1 = fake_tx_input(1); + let input_value_1 = Value::new(&BigNum(100)); + + let key_hash = fake_key_hash(1); + let key_hashes = Ed25519KeyHashes::from_vec(vec![key_hash]); + + let plutus_script = fake_plutus_script(1, &Language::new_plutus_v2()); + let mut plutus_script_source = PlutusScriptSource::new(&plutus_script); + plutus_script_source.set_required_signers(&key_hashes); + + let redeemer = fake_redeemer(1) + .clone_with_index_and_tag(&BigNum(0), &RedeemerTag::new_spend()); + + let datum = PlutusData::new_empty_constr_plutus_data(&BigNum::zero()); + let plutus_witness = + PlutusWitness::new_with_ref(&plutus_script_source, &DatumSource::new(&datum), &redeemer); + + tx_inputs_builder.add_plutus_script_input(&plutus_witness, &tx_input_1, &input_value_1); + + let plutus_scripts = tx_inputs_builder.get_plutus_input_scripts().unwrap(); + assert_eq!(plutus_scripts.len(), 1); + + let plutus_wit_from_builder = plutus_scripts.get(0); + assert_eq!(plutus_wit_from_builder.script().unwrap(), plutus_script); + assert_eq!(plutus_wit_from_builder.datum().unwrap(), datum); + assert_eq!(plutus_wit_from_builder.redeemer(), redeemer); + + assert!(tx_inputs_builder.has_plutus_scripts()); + + let req_signers = tx_inputs_builder.get_required_signers(); + assert_eq!(req_signers.len(), 1); + assert_eq!(req_signers, key_hashes); + + let inputs = tx_inputs_builder.inputs(); + assert_eq!(inputs.len(), 1); + assert!(inputs.contains(&tx_input_1)); + + let ref_inputs = tx_inputs_builder.get_ref_inputs(); + assert_eq!(ref_inputs.len(), 0); +} + +#[test] +fn plutus_script_input_with_ref() { + let mut tx_inputs_builder = TxInputsBuilder::new(); + let tx_input_1 = fake_tx_input(1); + let input_value_1 = Value::new(&BigNum(100)); + + let ref_input_1 = fake_tx_input(2); + let script_hash = fake_script_hash(1); + let lang_ver = Language::new_plutus_v2(); + let script_size = 100; + + let ref_input_2 = fake_tx_input(3); + + let plutus_script_source = + PlutusScriptSource::new_ref_input(&script_hash, &ref_input_1, &lang_ver, script_size); + + let redeemer = fake_redeemer(1) + .clone_with_index_and_tag(&BigNum(0), &RedeemerTag::new_spend()); + + let plutus_witness = PlutusWitness::new_with_ref( + &plutus_script_source, + &DatumSource::new_ref_input(&ref_input_2), + &redeemer, + ); + + tx_inputs_builder.add_plutus_script_input(&plutus_witness, &tx_input_1, &input_value_1); + + let plutus_scripts = tx_inputs_builder.get_plutus_input_scripts().unwrap(); + assert_eq!(plutus_scripts.len(), 1); + + let plutus_wit_from_builder = plutus_scripts.get(0); + assert_eq!(plutus_wit_from_builder.script(), None); + assert_eq!(plutus_wit_from_builder.datum(), None); + assert_eq!(plutus_wit_from_builder.redeemer(), redeemer); + + assert!(tx_inputs_builder.has_plutus_scripts()); + + let req_signers = tx_inputs_builder.get_required_signers(); + assert_eq!(req_signers.len(), 0); + + let inputs = tx_inputs_builder.inputs(); + assert_eq!(inputs.len(), 1); + assert!(inputs.contains(&tx_input_1)); + + let ref_inputs = tx_inputs_builder.get_ref_inputs(); + assert_eq!(ref_inputs.len(), 2); + assert!(ref_inputs.contains(&ref_input_1)); + assert!(ref_inputs.contains(&ref_input_2)); +} + +#[test] +fn native_script_input() { + let mut tx_inputs_builder = TxInputsBuilder::new(); + let tx_input_1 = fake_tx_input(1); + let input_value_1 = Value::new(&BigNum(100)); + + let key_hash_1 = fake_key_hash(1); + let mut native_scripts = NativeScripts::new(); + native_scripts.add( + &NativeScript::new_script_pubkey( + &ScriptPubkey::new(&key_hash_1) + ) + ); + let native_script = NativeScript::new_script_all( + &ScriptAll::new( + &native_scripts + ), + ); + + let native_script_source = NativeScriptSource::new(&native_script); + + tx_inputs_builder.add_native_script_input(&native_script_source, &tx_input_1, &input_value_1); + + let native_scripts = tx_inputs_builder.get_native_input_scripts().unwrap(); + assert_eq!(native_scripts.len(), 1); + + let native_wit_from_builder = native_scripts.get(0); + assert_eq!(native_wit_from_builder, native_script); + + let req_signers = tx_inputs_builder.get_required_signers(); + assert_eq!(req_signers.len(), 1); + assert_eq!(req_signers.get(0), key_hash_1); + + let inputs = tx_inputs_builder.inputs(); + assert_eq!(inputs.len(), 1); + assert!(inputs.contains(&tx_input_1)); + + let ref_inputs = tx_inputs_builder.get_ref_inputs(); + assert_eq!(ref_inputs.len(), 0); +} + +#[test] +fn native_script_custom_required_witness_input() { + let mut tx_inputs_builder = TxInputsBuilder::new(); + let tx_input_1 = fake_tx_input(1); + let input_value_1 = Value::new(&BigNum(100)); + + let key_hash_1 = fake_key_hash(1); + let key_hash_2 = fake_key_hash(2); + + let mut native_scripts = NativeScripts::new(); + native_scripts.add( + &NativeScript::new_script_pubkey( + &ScriptPubkey::new(&key_hash_1) + ) + ); + let native_script = NativeScript::new_script_all( + &ScriptAll::new( + &native_scripts + ), + ); + + let mut native_script_source = NativeScriptSource::new(&native_script); + let mut key_hashes = Ed25519KeyHashes::new(); + key_hashes.add(&key_hash_2); + native_script_source.set_required_signers(&key_hashes); + + tx_inputs_builder.add_native_script_input(&native_script_source, &tx_input_1, &input_value_1); + + let native_scripts = tx_inputs_builder.get_native_input_scripts().unwrap(); + assert_eq!(native_scripts.len(), 1); + + let native_wit_from_builder = native_scripts.get(0); + assert_eq!(native_wit_from_builder, native_script); + + let req_signers = tx_inputs_builder.get_required_signers(); + assert_eq!(req_signers.len(), 1); + assert_eq!(req_signers.get(0), key_hash_2); + + let inputs = tx_inputs_builder.inputs(); + assert_eq!(inputs.len(), 1); + assert!(inputs.contains(&tx_input_1)); + + let ref_inputs = tx_inputs_builder.get_ref_inputs(); + assert_eq!(ref_inputs.len(), 0); +} + +#[test] +fn native_script_input_ref_script() { + let mut tx_inputs_builder = TxInputsBuilder::new(); + let tx_input_1 = fake_tx_input(1); + let input_value_1 = Value::new(&BigNum(100)); + + let key_hash_1 = fake_key_hash(1); + let ref_input = fake_tx_input(2); + let script_hash = fake_script_hash(1); + + let mut native_script_source = NativeScriptSource::new_ref_input(&script_hash, &ref_input, 0); + let mut key_hashes = Ed25519KeyHashes::new(); + key_hashes.add(&key_hash_1); + native_script_source.set_required_signers(&key_hashes); + + tx_inputs_builder.add_native_script_input(&native_script_source, &tx_input_1, &input_value_1); + + let native_scripts = tx_inputs_builder.get_native_input_scripts(); + assert_eq!(native_scripts, None); + + let req_signers = tx_inputs_builder.get_required_signers(); + assert_eq!(req_signers.len(), 1); + assert_eq!(req_signers.get(0), key_hash_1); + + let inputs = tx_inputs_builder.inputs(); + assert_eq!(inputs.len(), 1); + assert!(inputs.contains(&tx_input_1)); + + let ref_inputs = tx_inputs_builder.get_ref_inputs(); + assert_eq!(ref_inputs.len(), 1); + assert!(ref_inputs.contains(&ref_input)); +} \ No newline at end of file diff --git a/rust/src/tests/builders/voting_builder.rs b/rust/src/tests/builders/voting_builder.rs new file mode 100644 index 00000000..df627bf7 --- /dev/null +++ b/rust/src/tests/builders/voting_builder.rs @@ -0,0 +1,434 @@ +use crate::fees::min_fee_for_size; +use crate::tests::fakes::{fake_change_address, fake_linear_fee, fake_plutus_script, fake_rich_tx_builder, fake_key_hash, fake_script_hash, fake_tx_hash, fake_vkey}; +use crate::*; + +#[test] +fn voting_builder_key_signers_test() { + let mut builder = VotingBuilder::new(); + let key_hash_1 = fake_key_hash(1); + let key_hash_2 = fake_key_hash(2); + let key_hash_3 = fake_key_hash(3); + let action_id_1 = GovernanceActionId::new(&fake_tx_hash(1), 1); + let action_id_2 = GovernanceActionId::new(&fake_tx_hash(2), 2); + let action_id_3 = GovernanceActionId::new(&fake_tx_hash(3), 3); + let vote = VotingProcedure::new(VoteKind::No); + let voter_1 = + Voter::new_constitutional_committee_hot_credential(&Credential::from_keyhash(&key_hash_1)); + let voter_2 = Voter::new_drep_credential(&Credential::from_keyhash(&key_hash_2)); + let voter_3 = Voter::new_stake_pool_key_hash(&key_hash_3); + builder.add(&voter_1, &action_id_1, &vote).unwrap(); + builder.add(&voter_1, &action_id_2, &vote).unwrap(); + builder.add(&voter_2, &action_id_2, &vote).unwrap(); + builder.add(&voter_3, &action_id_3, &vote).unwrap(); + + let req_signers = builder.get_required_signers(); + assert_eq!(req_signers.len(), 3); + assert!(req_signers.contains(&key_hash_1)); + assert!(req_signers.contains(&key_hash_2)); + assert!(req_signers.contains(&key_hash_3)); + assert_eq!(builder.has_plutus_scripts(), false); + + let mut tx_builder = fake_rich_tx_builder(false); + tx_builder.set_voting_builder(&builder); + tx_builder + .add_change_if_needed(&fake_change_address()) + .unwrap(); + + let tx = tx_builder.build_tx().unwrap(); + let tx_len = tx.to_bytes().len(); + let vkey_size = fake_vkey().to_bytes().len(); + let rough_tx_size = tx_len + (vkey_size * 3); + + let fee_algo = fake_linear_fee(44, 155381); + let approx_fee_with_wit = min_fee_for_size(rough_tx_size, &fee_algo).unwrap(); + assert!(approx_fee_with_wit.less_than(&tx.body().fee())); + + let voting_procedures = tx.body().voting_procedures().unwrap(); + let voters = voting_procedures.get_voters(); + assert_eq!(voters.len(), 3); + assert!(voters.0.contains(&voter_1)); + assert!(voters.0.contains(&voter_2)); + assert!(voters.0.contains(&voter_3)); + + let action_ids_1 = voting_procedures.get_governance_action_ids_by_voter(&voter_1); + assert_eq!(action_ids_1.len(), 2); + assert!(action_ids_1.0.contains(&action_id_1)); + assert!(action_ids_1.0.contains(&action_id_2)); + + let action_ids_2 = voting_procedures.get_governance_action_ids_by_voter(&voter_2); + assert_eq!(action_ids_2.len(), 1); + assert!(action_ids_2.0.contains(&action_id_2)); + + let action_ids_3 = voting_procedures.get_governance_action_ids_by_voter(&voter_3); + assert_eq!(action_ids_3.len(), 1); + assert!(action_ids_3.0.contains(&action_id_3)); + + let vote_1 = voting_procedures.get(&voter_1, &action_id_1).unwrap(); + assert_eq!(vote_1, vote); + + let vote_2 = voting_procedures.get(&voter_1, &action_id_2).unwrap(); + assert_eq!(vote_2, vote); + + let vote_3 = voting_procedures.get(&voter_2, &action_id_2).unwrap(); + assert_eq!(vote_3, vote); + + let vote_4 = voting_procedures.get(&voter_3, &action_id_3).unwrap(); + assert_eq!(vote_4, vote); + + let vote_5 = voting_procedures.get(&voter_3, &action_id_1); + assert_eq!(vote_5, None); +} + +#[test] +fn voting_builder_plutus_witness() { + let mut builder = VotingBuilder::new(); + let script = fake_plutus_script(1, &Language::new_plutus_v2()); + let script_hash = script.hash(); + let redeemer = Redeemer::new( + &RedeemerTag::new_cert(), + &BigNum::zero(), + &PlutusData::new_empty_constr_plutus_data(&BigNum::zero()), + &ExUnits::new(&BigNum::zero(), &BigNum::zero()), + ); + let expected_redeemer = + redeemer.clone_with_index_and_tag(&BigNum::zero(), &RedeemerTag::new_vote()); + let voter = Voter::new_drep_credential(&Credential::from_scripthash(&script_hash)); + let action_id = GovernanceActionId::new(&fake_tx_hash(1), 1); + let vote = VotingProcedure::new(VoteKind::No); + + let witness = PlutusWitness::new_without_datum(&script, &redeemer); + builder + .add_with_plutus_witness(&voter, &action_id, &vote, &witness) + .unwrap(); + + let req_signers = builder.get_required_signers(); + assert_eq!(req_signers.len(), 0); + + let witnesses = builder.get_plutus_witnesses(); + assert_eq!(witnesses.len(), 1); + let witness_from_voting_builder = witnesses.get(0); + assert_eq!(witness_from_voting_builder.datum(), None); + assert_eq!(witness_from_voting_builder.script(), Some(script.clone())); + assert_eq!( + witness_from_voting_builder.redeemer(), + expected_redeemer.clone() + ); + + let ref_inputs = builder.get_ref_inputs(); + assert_eq!(ref_inputs.len(), 0); + + assert_eq!(builder.has_plutus_scripts(), true); + + let langs = builder.get_used_plutus_lang_versions(); + assert_eq!(langs.len(), 1); + assert!(langs.contains(&Language::new_plutus_v2())); + + let mut tx_builder = fake_rich_tx_builder(true); + tx_builder.set_voting_builder(&builder); + tx_builder + .add_change_if_needed(&fake_change_address()) + .unwrap(); + + let mut cost_models = TxBuilderConstants::plutus_default_cost_models(); + cost_models = cost_models.retain_language_versions(&Languages(vec![Language::new_plutus_v2()])); + + tx_builder.calc_script_data_hash(&cost_models).unwrap(); + + let tx = tx_builder.build_tx().unwrap(); + + let tx_witnesses = tx.witness_set(); + let tx_script = tx_witnesses.plutus_scripts().unwrap(); + + assert_eq!(tx_script.len(), 1); + assert_eq!(tx_script.get(0), script); + + let tx_redeemers = tx_witnesses.redeemers().unwrap(); + assert_eq!(tx_redeemers.len(), 1); + assert_eq!(tx_redeemers.get(0), expected_redeemer); + + assert_eq!(tx_witnesses.plutus_data(), None); + + assert_eq!(tx.body().reference_inputs(), None); + + let script_data_hash = hash_script_data(&tx_redeemers, &cost_models, None); + + assert_eq!(tx.body().script_data_hash(), Some(script_data_hash)); + + let voting_procedures = tx.body().voting_procedures().unwrap(); + let voters = voting_procedures.get_voters(); + assert_eq!(voters.len(), 1); + assert!(voters.0.contains(&voter)); + + let action_ids = voting_procedures.get_governance_action_ids_by_voter(&voter); + assert_eq!(action_ids.len(), 1); + assert!(action_ids.0.contains(&action_id)); + + let vote_from_tx = voting_procedures.get(&voter, &action_id).unwrap(); + assert_eq!(vote_from_tx, vote); +} + +#[test] +fn voting_builder_plutus_ref_witness() { + let mut builder = VotingBuilder::new(); + let script_hash = fake_script_hash(1); + let redeemer = Redeemer::new( + &RedeemerTag::new_cert(), + &BigNum::zero(), + &PlutusData::new_empty_constr_plutus_data(&BigNum::zero()), + &ExUnits::new(&BigNum::zero(), &BigNum::zero()), + ); + + let ref_input = TransactionInput::new(&fake_tx_hash(5), 0); + let expected_redeemer = + redeemer.clone_with_index_and_tag(&BigNum::zero(), &RedeemerTag::new_vote()); + let voter = Voter::new_drep_credential(&Credential::from_scripthash(&script_hash)); + let action_id = GovernanceActionId::new(&fake_tx_hash(1), 1); + let vote = VotingProcedure::new(VoteKind::No); + + let script_source = PlutusScriptSource::new_ref_input( + &script_hash, + &ref_input, + &Language::new_plutus_v2(), + 0 + ); + let witness = PlutusWitness::new_with_ref_without_datum(&script_source, &redeemer); + builder + .add_with_plutus_witness(&voter, &action_id, &vote, &witness) + .unwrap(); + + let req_signers = builder.get_required_signers(); + assert_eq!(req_signers.len(), 0); + + let witnesses = builder.get_plutus_witnesses(); + assert_eq!(witnesses.len(), 1); + let witness_from_voting_builder = witnesses.get(0); + assert_eq!(witness_from_voting_builder.datum(), None); + assert_eq!(witness_from_voting_builder.script(), None); + assert_eq!( + witness_from_voting_builder.redeemer(), + expected_redeemer.clone() + ); + + let ref_inputs = builder.get_ref_inputs(); + assert_eq!(ref_inputs.len(), 1); + assert_eq!(ref_inputs.get(0), ref_input); + + assert_eq!(builder.has_plutus_scripts(), true); + + let langs = builder.get_used_plutus_lang_versions(); + assert_eq!(langs.len(), 1); + assert!(langs.contains(&Language::new_plutus_v2())); + + let mut tx_builder = fake_rich_tx_builder(true); + tx_builder.set_voting_builder(&builder); + tx_builder + .add_change_if_needed(&fake_change_address()) + .unwrap(); + + let mut cost_models = TxBuilderConstants::plutus_default_cost_models(); + cost_models = cost_models.retain_language_versions(&Languages(vec![Language::new_plutus_v2()])); + + tx_builder.calc_script_data_hash(&cost_models).unwrap(); + + let tx = tx_builder.build_tx().unwrap(); + + let tx_witnesses = tx.witness_set(); + assert_eq!(tx_witnesses.plutus_scripts().map_or(0, |x| x.len()), 0); + + let tx_redeemers = tx_witnesses.redeemers().unwrap(); + assert_eq!(tx_redeemers.len(), 1); + assert_eq!(tx_redeemers.get(0), expected_redeemer); + + assert_eq!(tx_witnesses.plutus_data(), None); + + let tx_ref_inputs = tx.body().reference_inputs().unwrap(); + assert_eq!(tx_ref_inputs.len(), 1); + assert_eq!(tx_ref_inputs.get(0), ref_input); + + let script_data_hash = hash_script_data(&tx_redeemers, &cost_models, None); + + assert_eq!(tx.body().script_data_hash(), Some(script_data_hash)); +} + +#[test] +fn voting_builder_native_script_witness() { + let mut builder = VotingBuilder::new(); + let key_hash = fake_key_hash(10); + let native_script = NativeScript::new_script_pubkey(&ScriptPubkey::new(&key_hash)); + let script_hash = native_script.hash(); + + let voter = Voter::new_drep_credential(&Credential::from_scripthash(&script_hash)); + let action_id = GovernanceActionId::new(&fake_tx_hash(1), 1); + let vote = VotingProcedure::new(VoteKind::No); + + let script_source = NativeScriptSource::new(&native_script); + builder + .add_with_native_script(&voter, &action_id, &vote, &script_source) + .unwrap(); + + let req_signers = builder.get_required_signers(); + assert_eq!(req_signers.len(), 1); + assert!(req_signers.contains(&key_hash)); + + let native_scripts = builder.get_native_scripts(); + assert_eq!(native_scripts.len(), 1); + assert_eq!(native_scripts.get(0), native_script); + + let witnesses = builder.get_plutus_witnesses(); + assert_eq!(witnesses.len(), 0); + + let ref_inputs = builder.get_ref_inputs(); + assert_eq!(ref_inputs.len(), 0); + + assert_eq!(builder.has_plutus_scripts(), false); + + let langs = builder.get_used_plutus_lang_versions(); + assert_eq!(langs.len(), 0); + + let mut tx_builder = fake_rich_tx_builder(false); + tx_builder.set_voting_builder(&builder); + tx_builder + .add_change_if_needed(&fake_change_address()) + .unwrap(); + + let tx = tx_builder.build_tx().unwrap(); + + let tx_witnesses = tx.witness_set(); + assert_eq!(tx_witnesses.plutus_scripts(), None); + + let tx_redeemers = tx_witnesses.redeemers(); + assert_eq!(tx_redeemers, None); + assert_eq!(tx_witnesses.plutus_data(), None); + assert_eq!(tx.body().reference_inputs(), None); + assert_eq!(tx.body().script_data_hash(), None); + + let native_scripts = tx_witnesses.native_scripts().unwrap(); + assert_eq!(native_scripts.len(), 1); + assert_eq!(native_scripts.get(0), native_script); + + let voting_procedures = tx.body().voting_procedures().unwrap(); + let voters = voting_procedures.get_voters(); + assert_eq!(voters.len(), 1); + assert!(voters.0.contains(&voter)); + + let action_ids = voting_procedures.get_governance_action_ids_by_voter(&voter); + assert_eq!(action_ids.len(), 1); + assert!(action_ids.0.contains(&action_id)); + + let vote_from_tx = voting_procedures.get(&voter, &action_id).unwrap(); + assert_eq!(vote_from_tx, vote); +} + +#[test] +fn voting_builder_native_script_ref_witness() { + let mut builder = VotingBuilder::new(); + let key_hash = fake_key_hash(10); + let script_hash = fake_script_hash(1); + + let voter = Voter::new_drep_credential(&Credential::from_scripthash(&script_hash)); + let action_id = GovernanceActionId::new(&fake_tx_hash(1), 1); + let vote = VotingProcedure::new(VoteKind::No); + + let mut script_signers = RequiredSigners::new(); + script_signers.add(&key_hash); + + let ref_input = TransactionInput::new(&fake_tx_hash(5), 0); + let mut script_source = + NativeScriptSource::new_ref_input(&script_hash, &ref_input, 0); + script_source.set_required_signers(&script_signers); + builder + .add_with_native_script(&voter, &action_id, &vote, &script_source) + .unwrap(); + + let req_signers = builder.get_required_signers(); + assert_eq!(req_signers.len(), 1); + assert!(req_signers.contains(&key_hash)); + + let native_scripts = builder.get_native_scripts(); + assert_eq!(native_scripts.len(), 0); + + let witnesses = builder.get_plutus_witnesses(); + assert_eq!(witnesses.len(), 0); + + let ref_inputs = builder.get_ref_inputs(); + assert_eq!(ref_inputs.len(), 1); + assert_eq!(ref_inputs.get(0), ref_input.clone()); + + assert_eq!(builder.has_plutus_scripts(), false); + + let langs = builder.get_used_plutus_lang_versions(); + assert_eq!(langs.len(), 0); + + let mut tx_builder = fake_rich_tx_builder(false); + tx_builder.set_voting_builder(&builder); + tx_builder + .add_change_if_needed(&fake_change_address()) + .unwrap(); + + let tx = tx_builder.build_tx().unwrap(); + + let tx_witnesses = tx.witness_set(); + assert_eq!(tx_witnesses.plutus_scripts(), None); + + let tx_redeemers = tx_witnesses.redeemers(); + assert_eq!(tx_redeemers, None); + assert_eq!(tx_witnesses.plutus_data(), None); + + let ref_inputs = tx.body().reference_inputs().unwrap(); + assert_eq!(ref_inputs.len(), 1); + assert_eq!(ref_inputs.get(0), ref_input); + + assert_eq!(tx.body().script_data_hash(), None); + + assert_eq!(tx_witnesses.native_scripts(), None); + + let voting_procedures = tx.body().voting_procedures().unwrap(); + let voters = voting_procedures.get_voters(); + assert_eq!(voters.len(), 1); + assert!(voters.0.contains(&voter)); + + let action_ids = voting_procedures.get_governance_action_ids_by_voter(&voter); + assert_eq!(action_ids.len(), 1); + assert!(action_ids.0.contains(&action_id)); + + let vote_from_tx = voting_procedures.get(&voter, &action_id).unwrap(); + assert_eq!(vote_from_tx, vote); +} + +#[test] +fn voting_builder_non_script_voter_error() { + let mut builder = VotingBuilder::new(); + let key_hash = fake_key_hash(10); + let voter = Voter::new_drep_credential(&Credential::from_keyhash(&key_hash)); + let action_id = GovernanceActionId::new(&fake_tx_hash(1), 1); + let vote = VotingProcedure::new(VoteKind::No); + + let script_source = NativeScriptSource::new(&NativeScript::new_script_pubkey( + &ScriptPubkey::new(&key_hash), + )); + let result_native = builder.add_with_native_script(&voter, &action_id, &vote, &script_source); + assert!(result_native.is_err()); + + let plutus_witness = PlutusWitness::new_without_datum( + &fake_plutus_script(1, &Language::new_plutus_v2()), + &Redeemer::new( + &RedeemerTag::new_cert(), + &BigNum::zero(), + &PlutusData::new_empty_constr_plutus_data(&BigNum::zero()), + &ExUnits::new(&BigNum::zero(), &BigNum::zero()), + ), + ); + let result_plutus = builder.add_with_plutus_witness(&voter, &action_id, &vote, &plutus_witness); + assert!(result_plutus.is_err()); +} + +#[test] +fn voting_builder_key_hash_error() { + let mut builder = VotingBuilder::new(); + let voter = Voter::new_drep_credential(&Credential::from_scripthash(&fake_script_hash(1))); + let action_id = GovernanceActionId::new(&fake_tx_hash(1), 1); + let vote = VotingProcedure::new(VoteKind::No); + + let result = builder.add(&voter, &action_id, &vote); + assert!(result.is_err()); +} \ No newline at end of file diff --git a/rust/src/tests/builders/voting_proposal_builder.rs b/rust/src/tests/builders/voting_proposal_builder.rs new file mode 100644 index 00000000..6a48f17b --- /dev/null +++ b/rust/src/tests/builders/voting_proposal_builder.rs @@ -0,0 +1,396 @@ +use crate::tests::fakes::{fake_full_protocol_param_update, fake_anchor, fake_change_address, fake_plutus_script, fake_tx_builder_with_amount_and_deposit_params, fake_key_hash, fake_reward_address, fake_script_hash, fake_tx_hash}; +use crate::*; + +fn total_tx_output_with_fee(tx: &Transaction) -> Coin { + let mut total = Coin::zero(); + for output in &tx.body().outputs() { + total = total.checked_add(&output.amount().coin()).unwrap(); + } + + total.checked_add(&tx.body().fee()).unwrap() +} + +#[test] +fn voting_proposal_builder_one_proposal() { + let proposal_deposit = Coin::from(1000u64); + let action_id = GovernanceActionId::new(&fake_tx_hash(1), 0); + let action = + HardForkInitiationAction::new_with_action_id(&action_id, &ProtocolVersion::new(1, 2)); + let mut builder = VotingProposalBuilder::new(); + let wrapped_action = GovernanceAction::new_hard_fork_initiation_action(&action); + let proposal = VotingProposal::new( + &wrapped_action, + &fake_anchor(), + &fake_reward_address(1), + &proposal_deposit, + ); + builder.add(&proposal).unwrap(); + + let witnesses = builder.get_plutus_witnesses(); + assert_eq!(witnesses.len(), 0); + + let inputs = builder.get_ref_inputs(); + assert_eq!(inputs.len(), 0); + + assert_eq!(builder.has_plutus_scripts(), false); + assert_eq!( + builder.get_total_deposit().unwrap(), + proposal_deposit.clone() + ); + + let initial_amount = 1000000000u64; + let mut tx_builder = + fake_tx_builder_with_amount_and_deposit_params(initial_amount, 500, 500, false); + + tx_builder.set_voting_proposal_builder(&builder); + tx_builder + .add_change_if_needed(&fake_change_address()) + .unwrap(); + + let tx = tx_builder.build_tx().unwrap(); + + let voting_proposals = tx.body().voting_proposals().unwrap(); + assert_eq!(voting_proposals.len(), 1); + assert_eq!(voting_proposals.get(0), proposal); + + let mut total_out = total_tx_output_with_fee(&tx); + total_out = total_out.checked_add(&proposal_deposit).unwrap(); + assert_eq!(total_out, Coin::from(initial_amount)); +} + +#[test] +fn voting_proposal_builder_all_proposals() { + let proposal_deposit = Coin::from(1000u64); + let total_deposit = proposal_deposit.checked_mul(&Coin::from(7u64)).unwrap(); + + let action_id = GovernanceActionId::new(&fake_tx_hash(1), 0); + let hf_action = + HardForkInitiationAction::new_with_action_id(&action_id, &ProtocolVersion::new(1, 2)); + let mut builder = VotingProposalBuilder::new(); + let wrapped_hf_action = GovernanceAction::new_hard_fork_initiation_action(&hf_action); + let hf_proposal = VotingProposal::new( + &wrapped_hf_action, + &fake_anchor(), + &fake_reward_address(1), + &proposal_deposit, + ); + builder.add(&hf_proposal).unwrap(); + + let mut committee = + Committee::new(&UnitInterval::new(&BigNum::from(1u32), &BigNum::from(2u32))); + committee.add_member(&Credential::from_keyhash(&fake_key_hash(1)), 1); + let mut members_to_remove = Credentials::new(); + members_to_remove.add(&Credential::from_keyhash(&fake_key_hash(1))); + let committee_action = UpdateCommitteeAction::new(&committee, &members_to_remove); + let wrapped_committee_action = GovernanceAction::new_new_committee_action(&committee_action); + let committee_proposal = VotingProposal::new( + &wrapped_committee_action, + &fake_anchor(), + &fake_reward_address(2), + &proposal_deposit, + ); + builder.add(&committee_proposal).unwrap(); + + let anchor = fake_anchor(); + let constitution = Constitution::new(&anchor); + let constitution_action = NewConstitutionAction::new(&constitution); + let wrapped_constitution_action = + GovernanceAction::new_new_constitution_action(&constitution_action); + let constitution_proposal = VotingProposal::new( + &wrapped_constitution_action, + &fake_anchor(), + &fake_reward_address(3), + &proposal_deposit, + ); + builder.add(&constitution_proposal).unwrap(); + + let no_conf_action = NoConfidenceAction::new(); + let wrapped_no_conf_action = GovernanceAction::new_no_confidence_action(&no_conf_action); + let no_conf_proposal = VotingProposal::new( + &wrapped_no_conf_action, + &fake_anchor(), + &fake_reward_address(4), + &proposal_deposit, + ); + builder.add(&no_conf_proposal).unwrap(); + + let parameters_update = fake_full_protocol_param_update(); + let pp_update_action = ParameterChangeAction::new(¶meters_update); + let wrapped_pp_update_action = GovernanceAction::new_parameter_change_action(&pp_update_action); + let pp_update_proposal = VotingProposal::new( + &wrapped_pp_update_action, + &fake_anchor(), + &fake_reward_address(4), + &proposal_deposit, + ); + builder.add(&pp_update_proposal).unwrap(); + + let mut withdrawals = TreasuryWithdrawals::new(); + let addr1 = RewardAddress::new(1, &Credential::from_keyhash(&fake_key_hash(1))); + withdrawals.insert(&addr1, &Coin::from(1u32)); + let withdrawal_action = TreasuryWithdrawalsAction::new(&withdrawals); + let wrapped_withdrawal_action = + GovernanceAction::new_treasury_withdrawals_action(&withdrawal_action); + let withdrawal_proposal = VotingProposal::new( + &wrapped_withdrawal_action, + &fake_anchor(), + &fake_reward_address(5), + &proposal_deposit, + ); + builder.add(&withdrawal_proposal).unwrap(); + + let info_action = InfoAction::new(); + let wrapped_info_action = GovernanceAction::new_info_action(&info_action); + let info_proposal = VotingProposal::new( + &wrapped_info_action, + &fake_anchor(), + &fake_reward_address(5), + &proposal_deposit, + ); + builder.add(&info_proposal).unwrap(); + + let witnesses = builder.get_plutus_witnesses(); + assert_eq!(witnesses.len(), 0); + + let inputs = builder.get_ref_inputs(); + assert_eq!(inputs.len(), 0); + + assert_eq!(builder.has_plutus_scripts(), false); + assert_eq!(builder.get_total_deposit().unwrap(), total_deposit.clone()); + + let initial_amount = 1000000000u64; + let mut tx_builder = + fake_tx_builder_with_amount_and_deposit_params(initial_amount, 500, 500, false); + + tx_builder.set_voting_proposal_builder(&builder); + tx_builder + .add_change_if_needed(&fake_change_address()) + .unwrap(); + + let tx = tx_builder.build_tx().unwrap(); + + let voting_proposals = tx.body().voting_proposals().unwrap(); + assert_eq!(voting_proposals.len(), 7); + assert!(voting_proposals.contains(&hf_proposal)); + assert!(voting_proposals.contains(&committee_proposal)); + assert!(voting_proposals.contains(&constitution_proposal)); + assert!(voting_proposals.contains(&no_conf_proposal)); + assert!(voting_proposals.contains(&pp_update_proposal)); + assert!(voting_proposals.contains(&withdrawal_proposal)); + assert!(voting_proposals.contains(&info_proposal)); + + let mut total_out = total_tx_output_with_fee(&tx); + total_out = total_out.checked_add(&total_deposit).unwrap(); + assert_eq!(total_out, Coin::from(initial_amount)); +} + +#[test] +fn voting_proposal_builder_with_plutus_script_witness() { + let proposal_deposit = Coin::from(1000u64); + let total_deposit = proposal_deposit.checked_mul(&Coin::from(2u64)).unwrap(); + + let action_id = GovernanceActionId::new(&fake_tx_hash(1), 0); + let hf_action = + HardForkInitiationAction::new_with_action_id(&action_id, &ProtocolVersion::new(1, 2)); + let mut builder = VotingProposalBuilder::new(); + let wrapped_hf_action = GovernanceAction::new_hard_fork_initiation_action(&hf_action); + let hf_proposal = VotingProposal::new( + &wrapped_hf_action, + &fake_anchor(), + &fake_reward_address(1), + &proposal_deposit, + ); + builder.add(&hf_proposal).unwrap(); + + let script = fake_plutus_script(1, &Language::new_plutus_v2()); + let redeemer = Redeemer::new( + &RedeemerTag::new_cert(), + &BigNum::from(100u32), + &PlutusData::new_empty_constr_plutus_data(&BigNum::zero()), + &ExUnits::new(&BigNum::zero(), &BigNum::zero()), + ); + let expected_redeemer = + redeemer.clone_with_index_and_tag(&BigNum::from(1u64), &RedeemerTag::new_voting_proposal()); + let plutus_witness = PlutusWitness::new_without_datum(&script, &redeemer); + + let mut committee = + Committee::new(&UnitInterval::new(&BigNum::from(1u32), &BigNum::from(2u32))); + committee.add_member(&Credential::from_keyhash(&fake_key_hash(1)), 1); + let mut members_to_remove = Credentials::new(); + members_to_remove.add(&Credential::from_keyhash(&fake_key_hash(1))); + let committee_action = UpdateCommitteeAction::new(&committee, &members_to_remove); + let wrapped_committee_action = GovernanceAction::new_new_committee_action(&committee_action); + let committee_proposal = VotingProposal::new( + &wrapped_committee_action, + &fake_anchor(), + &fake_reward_address(2), + &proposal_deposit, + ); + builder + .add_with_plutus_witness(&committee_proposal, &plutus_witness) + .unwrap(); + + let witnesses = builder.get_plutus_witnesses(); + assert_eq!(witnesses.len(), 1); + + let builder_witness = witnesses.get(0); + assert_eq!(builder_witness.redeemer(), expected_redeemer.clone()); + assert_eq!(builder_witness.script(), Some(script.clone())); + assert_eq!(builder_witness.datum(), None); + + let inputs = builder.get_ref_inputs(); + assert_eq!(inputs.len(), 0); + + assert_eq!(builder.has_plutus_scripts(), true); + assert_eq!(builder.get_total_deposit().unwrap(), total_deposit.clone()); + + let initial_amount = 1000000000u64; + let mut tx_builder = + fake_tx_builder_with_amount_and_deposit_params(initial_amount, 500, 500, true); + + tx_builder.set_voting_proposal_builder(&builder); + tx_builder + .add_change_if_needed(&fake_change_address()) + .unwrap(); + + let mut cost_models = TxBuilderConstants::plutus_default_cost_models(); + cost_models = cost_models.retain_language_versions(&Languages(vec![Language::new_plutus_v2()])); + + tx_builder.calc_script_data_hash(&cost_models).unwrap(); + + let tx = tx_builder.build_tx().unwrap(); + + let voting_proposals = tx.body().voting_proposals().unwrap(); + assert_eq!(voting_proposals.len(), 2); + assert!(voting_proposals.contains(&hf_proposal)); + assert!(voting_proposals.contains(&committee_proposal)); + + let mut total_out = total_tx_output_with_fee(&tx); + total_out = total_out.checked_add(&total_deposit).unwrap(); + assert_eq!(total_out, Coin::from(initial_amount)); + + let tx_witnesses = tx.witness_set(); + let tx_script = tx_witnesses.plutus_scripts().unwrap(); + + assert_eq!(tx_script.len(), 1); + assert_eq!(tx_script.get(0), script); + + let tx_redeemers = tx_witnesses.redeemers().unwrap(); + assert_eq!(tx_redeemers.len(), 1); + assert_eq!(tx_redeemers.get(0), expected_redeemer); + + assert_eq!(tx_witnesses.plutus_data(), None); + + assert_eq!(tx.body().reference_inputs(), None); + + let script_data_hash = hash_script_data(&tx_redeemers, &cost_models, None); + + assert_eq!(tx.body().script_data_hash(), Some(script_data_hash)); +} + +#[test] +fn voting_proposal_builder_with_ref_plutus_script_witness() { + let proposal_deposit = Coin::from(1000u64); + let total_deposit = proposal_deposit.checked_mul(&Coin::from(2u64)).unwrap(); + + let action_id = GovernanceActionId::new(&fake_tx_hash(1), 0); + let hf_action = + HardForkInitiationAction::new_with_action_id(&action_id, &ProtocolVersion::new(1, 2)); + let mut builder = VotingProposalBuilder::new(); + let wrapped_hf_action = GovernanceAction::new_hard_fork_initiation_action(&hf_action); + let hf_proposal = VotingProposal::new( + &wrapped_hf_action, + &fake_anchor(), + &fake_reward_address(1), + &proposal_deposit, + ); + builder.add(&hf_proposal).unwrap(); + + let script_hash = fake_script_hash(1); + let ref_input = TransactionInput::new(&fake_tx_hash(5), 0); + let redeemer = Redeemer::new( + &RedeemerTag::new_cert(), + &BigNum::from(100u32), + &PlutusData::new_empty_constr_plutus_data(&BigNum::zero()), + &ExUnits::new(&BigNum::zero(), &BigNum::zero()), + ); + let expected_redeemer = + redeemer.clone_with_index_and_tag(&BigNum::from(1u64), &RedeemerTag::new_voting_proposal()); + let plutus_source = + PlutusScriptSource::new_ref_input(&script_hash, &ref_input, &Language::new_plutus_v2(), 0); + let plutus_witness = PlutusWitness::new_with_ref_without_datum(&plutus_source, &redeemer); + + let mut committee = + Committee::new(&UnitInterval::new(&BigNum::from(1u32), &BigNum::from(2u32))); + committee.add_member(&Credential::from_keyhash(&fake_key_hash(1)), 1); + let mut members_to_remove = Credentials::new(); + members_to_remove.add(&Credential::from_keyhash(&fake_key_hash(1))); + let committee_action = UpdateCommitteeAction::new(&committee, &members_to_remove); + let wrapped_committee_action = GovernanceAction::new_new_committee_action(&committee_action); + let committee_proposal = VotingProposal::new( + &wrapped_committee_action, + &fake_anchor(), + &fake_reward_address(2), + &proposal_deposit, + ); + builder + .add_with_plutus_witness(&committee_proposal, &plutus_witness) + .unwrap(); + + let witnesses = builder.get_plutus_witnesses(); + assert_eq!(witnesses.len(), 1); + + let builder_witness = witnesses.get(0); + assert_eq!(builder_witness.redeemer(), expected_redeemer.clone()); + assert_eq!(builder_witness.script(), None); + assert_eq!(builder_witness.datum(), None); + + let builder_ref_inputs = builder.get_ref_inputs(); + assert_eq!(builder_ref_inputs.len(), 1); + assert_eq!(builder_ref_inputs.get(0), ref_input); + + assert_eq!(builder.has_plutus_scripts(), true); + assert_eq!(builder.get_total_deposit().unwrap(), total_deposit.clone()); + + let initial_amount = 1000000000u64; + let mut tx_builder = + fake_tx_builder_with_amount_and_deposit_params(initial_amount, 500, 500, true); + + tx_builder.set_voting_proposal_builder(&builder); + tx_builder + .add_change_if_needed(&fake_change_address()) + .unwrap(); + + let mut cost_models = TxBuilderConstants::plutus_default_cost_models(); + cost_models = cost_models.retain_language_versions(&Languages(vec![Language::new_plutus_v2()])); + + tx_builder.calc_script_data_hash(&cost_models).unwrap(); + + let tx = tx_builder.build_tx().unwrap(); + + let voting_proposals = tx.body().voting_proposals().unwrap(); + assert_eq!(voting_proposals.len(), 2); + assert!(voting_proposals.contains(&hf_proposal)); + assert!(voting_proposals.contains(&committee_proposal)); + + let mut total_out = total_tx_output_with_fee(&tx); + total_out = total_out.checked_add(&total_deposit).unwrap(); + assert_eq!(total_out, Coin::from(initial_amount)); + + let tx_witnesses = tx.witness_set(); + assert_eq!(tx_witnesses.plutus_scripts().map_or(0, |x| x.len()), 0); + + let tx_redeemers = tx_witnesses.redeemers().unwrap(); + assert_eq!(tx_redeemers.len(), 1); + assert_eq!(tx_redeemers.get(0), expected_redeemer); + + assert_eq!(tx_witnesses.plutus_data(), None); + + let tx_ref_inputs = tx.body().reference_inputs().unwrap(); + assert_eq!(tx_ref_inputs.len(), 1); + assert_eq!(tx_ref_inputs.get(0), ref_input); + + let script_data_hash = hash_script_data(&tx_redeemers, &cost_models, None); + + assert_eq!(tx.body().script_data_hash(), Some(script_data_hash)); +} diff --git a/rust/src/tests/crypto.rs b/rust/src/tests/crypto.rs new file mode 100644 index 00000000..8be8742a --- /dev/null +++ b/rust/src/tests/crypto.rs @@ -0,0 +1,86 @@ +use crate::*; + +#[test] +fn nonce_identity() { + let orig = Nonce::new_identity(); + let deser = Nonce::deserialize(&mut Deserializer::from(std::io::Cursor::new( + orig.to_bytes(), + ))) + .unwrap(); + assert_eq!(orig.to_bytes(), deser.to_bytes()); +} + +#[test] +fn nonce_hash() { + let orig = Nonce::new_from_hash(vec![ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, + ]) + .unwrap(); + let deser = Nonce::deserialize(&mut Deserializer::from(std::io::Cursor::new( + orig.to_bytes(), + ))) + .unwrap(); + assert_eq!(orig.to_bytes(), deser.to_bytes()); +} + +#[test] +fn xprv_128_test() { + // art forum devote street sure rather head chuckle guard poverty release quote oak craft enemy + let entropy = [ + 0x0c, 0xcb, 0x74, 0xf3, 0x6b, 0x7d, 0xa1, 0x64, 0x9a, 0x81, 0x44, 0x67, 0x55, 0x22, 0xd4, + 0xd8, 0x09, 0x7c, 0x64, 0x12, + ]; + let root_key = Bip32PrivateKey::from_bip39_entropy(&entropy, &[]); + + assert_eq!(hex::encode(&root_key.as_bytes()), "b8f2bece9bdfe2b0282f5bad705562ac996efb6af96b648f4445ec44f47ad95c10e3d72f26ed075422a36ed8585c745a0e1150bcceba2357d058636991f38a3791e248de509c070d812ab2fda57860ac876bc489192c1ef4ce253c197ee219a4"); + let xprv_128 = root_key.to_128_xprv(); + // test the 128 xprv is the right format + assert_eq!(hex::encode(&xprv_128), "b8f2bece9bdfe2b0282f5bad705562ac996efb6af96b648f4445ec44f47ad95c10e3d72f26ed075422a36ed8585c745a0e1150bcceba2357d058636991f38a37cf76399a210de8720e9fa894e45e41e29ab525e30bc402801c076250d1585bcd91e248de509c070d812ab2fda57860ac876bc489192c1ef4ce253c197ee219a4"); + let root_key_copy = Bip32PrivateKey::from_128_xprv(&xprv_128).unwrap(); + + // test converting to and back is equivalent to the identity function + assert_eq!(root_key.to_bech32(), root_key_copy.to_bech32()); +} + +#[test] +fn chaincode_gen() { + // art forum devote street sure rather head chuckle guard poverty release quote oak craft enemy + let entropy = [ + 0x0c, 0xcb, 0x74, 0xf3, 0x6b, 0x7d, 0xa1, 0x64, 0x9a, 0x81, 0x44, 0x67, 0x55, 0x22, 0xd4, + 0xd8, 0x09, 0x7c, 0x64, 0x12, + ]; + let root_key = Bip32PrivateKey::from_bip39_entropy(&entropy, &[]); + + let prv_chaincode = root_key.chaincode(); + assert_eq!( + hex::encode(&prv_chaincode), + "91e248de509c070d812ab2fda57860ac876bc489192c1ef4ce253c197ee219a4" + ); + + let pub_chaincode = root_key.to_public().chaincode(); + assert_eq!( + hex::encode(&pub_chaincode), + "91e248de509c070d812ab2fda57860ac876bc489192c1ef4ce253c197ee219a4" + ); +} + +#[test] +fn private_key_from_bech32() { + let pk = PrivateKey::generate_ed25519().unwrap(); + let pk_ext = PrivateKey::generate_ed25519extended().unwrap(); + + assert_eq!( + PrivateKey::from_bech32(&pk.to_bech32()).unwrap().as_bytes(), + pk.as_bytes(), + ); + assert_eq!( + PrivateKey::from_bech32(&pk_ext.to_bech32()) + .unwrap() + .as_bytes(), + pk_ext.as_bytes(), + ); + + let er = PrivateKey::from_bech32("qwe"); + assert!(er.is_err()); +} diff --git a/rust/src/tests/emip3.rs b/rust/src/tests/emip3.rs new file mode 100644 index 00000000..70fdedd3 --- /dev/null +++ b/rust/src/tests/emip3.rs @@ -0,0 +1,12 @@ +use crate::*; + +#[test] +fn encryption() { + let password = String::from("70617373776f7264"); + let salt = String::from("50515253c0c1c2c3c4c5c6c750515253c0c1c2c3c4c5c6c750515253c0c1c2c3"); + let nonce = String::from("50515253c0c1c2c3c4c5c6c7"); + let data = String::from("736f6d65206461746120746f20656e6372797074"); + let encrypted_data = encrypt_with_password(&password, &salt, &nonce, &data).unwrap(); + let decrypted_data = decrypt_with_password(&password, &encrypted_data).unwrap(); + assert_eq!(data, decrypted_data); +} diff --git a/rust/src/tests/fakes.rs b/rust/src/tests/fakes.rs new file mode 100644 index 00000000..ee4f132a --- /dev/null +++ b/rust/src/tests/fakes.rs @@ -0,0 +1,631 @@ +#![allow(dead_code)] + +use crate::*; +use crate::tests::helpers::harden; + +const MAX_VALUE_SIZE: u32 = 4000; +const MAX_TX_SIZE: u32 = 8000; // might be out of date but suffices for our tests + // this is what is used in mainnet +static COINS_PER_UTXO_BYTE: u64 = 34_482 / 8; + +pub(crate) fn fake_root_key_15() -> Bip32PrivateKey { + // art forum devote street sure rather head chuckle guard poverty release quote oak craft enemy + let entropy = [ + 0x0c, 0xcb, 0x74, 0xf3, 0x6b, 0x7d, 0xa1, 0x64, 0x9a, 0x81, 0x44, 0x67, 0x55, 0x22, 0xd4, + 0xd8, 0x09, 0x7c, 0x64, 0x12, + ]; + Bip32PrivateKey::from_bip39_entropy(&entropy, &[]) +} + +pub(crate) fn fake_root_key() -> Bip32PrivateKey { + // art forum devote street sure rather head chuckle guard poverty release quote oak craft enemy + let entropy = [ + 0x0c, 0xcb, 0x74, 0xf3, 0x6b, 0x7d, 0xa1, 0x64, 0x9a, 0x81, 0x44, 0x67, 0x55, 0x22, 0xd4, + 0xd8, 0x09, 0x7c, 0x64, 0x12, + ]; + Bip32PrivateKey::from_bip39_entropy(&entropy, &[]) +} + +pub(crate) fn fake_base_address_with_payment_cred(payment_cred: Credential) -> Address { + let stake = fake_root_key() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &payment_cred, + &stake_cred, + ); + addr.to_address() +} + +pub(crate) fn fake_base_address(index: u32) -> Address { + let spend = fake_root_key() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(index) + .to_public(); + let stake = fake_root_key() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ); + addr.to_address() +} + +pub(crate) fn fake_full_protocol_param_update() -> ProtocolParamUpdate { + ProtocolParamUpdate { + minfee_a: Some(Coin::from(44_444u32)), + minfee_b: Some(Coin::from(44_444u32)), + max_block_body_size: Some(44_444u32), + max_tx_size: Some(44_444u32), + max_block_header_size: Some(44_444u32), + key_deposit: Some(Coin::from(44_444u32)), + pool_deposit: Some(Coin::from(44_444u32)), + max_epoch: Some(44_444u32), + n_opt: Some(44_444u32), + pool_pledge_influence: Some(UnitInterval::new( + &BigNum::from(44_444u32), + &BigNum::from(44_444u32), + )), + expansion_rate: Some(UnitInterval::new( + &BigNum::from(44_444u32), + &BigNum::from(44_444u32), + )), + treasury_growth_rate: Some(UnitInterval::new( + &BigNum::from(44_444u32), + &BigNum::from(44_444u32), + )), + d: Some(UnitInterval::new( + &BigNum::from(44_444u32), + &BigNum::from(44_444u32), + )), + extra_entropy: Some(Nonce::new_identity()), + protocol_version: Some(ProtocolVersion::new(1, 2)), + min_pool_cost: Some(Coin::from(44_444u32)), + ada_per_utxo_byte: Some(Coin::from(44_444u32)), + cost_models: Some(fake_cost_models()), + execution_costs: Some(ExUnitPrices::new( + &SubCoin::new(&BigNum(577), &BigNum(10000)), + &SubCoin::new(&BigNum(721), &BigNum(10000000)), + )), + max_tx_ex_units: Some(ExUnits::new(&BigNum(842996), &BigNum(246100241))), + max_block_ex_units: Some(ExUnits::new(&BigNum(842996), &BigNum(246100241))), + max_value_size: Some(44_444u32), + collateral_percentage: Some(44_444u32), + max_collateral_inputs: Some(44_444u32), + pool_voting_thresholds: Some(fake_pool_voting_thresholds()), + drep_voting_thresholds: Some(fake_drep_voting_thresholds()), + min_committee_size: Some(44_444u32), + committee_term_limit: Some(44_444u32), + governance_action_validity_period: Some(44_444u32), + governance_action_deposit: Some(Coin::from(44_444u32)), + drep_deposit: Some(Coin::from(44_444u32)), + drep_inactivity_period: Some(44_444u32), + ref_script_coins_per_byte: Some(UnitInterval::new( + &BigNum::from(44_444u32), + &BigNum::from(44_444u32), + )), + } +} + +pub(crate) fn fake_pool_voting_thresholds() -> PoolVotingThresholds { + PoolVotingThresholds::new( + &UnitInterval::new(&BigNum::from(44_401u32), &BigNum::from(44_402u32)), + &UnitInterval::new(&BigNum::from(44_403u32), &BigNum::from(44_404u32)), + &UnitInterval::new(&BigNum::from(44_405u32), &BigNum::from(44_406u32)), + &UnitInterval::new(&BigNum::from(44_406u32), &BigNum::from(44_407u32)), + &UnitInterval::new(&BigNum::from(44_408u32), &BigNum::from(44_409u32)), + ) +} + +pub(crate) fn fake_drep_voting_thresholds() -> DRepVotingThresholds { + DRepVotingThresholds::new( + &UnitInterval::new(&BigNum::from(44_401u32), &BigNum::from(44_402u32)), + &UnitInterval::new(&BigNum::from(44_403u32), &BigNum::from(44_404u32)), + &UnitInterval::new(&BigNum::from(44_405u32), &BigNum::from(44_406u32)), + &UnitInterval::new(&BigNum::from(44_406u32), &BigNum::from(44_407u32)), + &UnitInterval::new(&BigNum::from(44_408u32), &BigNum::from(44_409u32)), + &UnitInterval::new(&BigNum::from(44_410u32), &BigNum::from(44_411u32)), + &UnitInterval::new(&BigNum::from(44_412u32), &BigNum::from(44_412u32)), + &UnitInterval::new(&BigNum::from(44_414u32), &BigNum::from(44_415u32)), + &UnitInterval::new(&BigNum::from(44_416u32), &BigNum::from(44_417u32)), + &UnitInterval::new(&BigNum::from(44_418u32), &BigNum::from(44_419u32)), + ) +} + +pub(crate) fn fake_full_pool_params() -> PoolParams { + PoolParams { + operator: fake_key_hash(1), + vrf_keyhash: fake_vrf_key_hash(2), + pledge: Coin::from(44_444u32), + cost: Coin::from(44_444u32), + margin: UnitInterval::new(&BigNum::from(44_444u32), &BigNum::from(44_444u32)), + reward_account: RewardAddress::new(2, &Credential::from_keyhash(&fake_key_hash(3))), + pool_owners: Ed25519KeyHashes::from_vec(vec![fake_key_hash(4), fake_key_hash(5)].into_iter().collect()), + relays: Relays(vec![Relay::new_multi_host_name(&MultiHostName::new( + &DNSRecordSRV::new("iohk.io".to_string()).unwrap(), + ))]), + pool_metadata: Some(PoolMetadata::new( + &URL::new("https://iohk.io".to_string()).unwrap(), + &fake_pool_metadata_hash(6), + )), + } +} + +pub(crate) fn fake_cost_models() -> Costmdls { + let mut res = Costmdls::new(); + res.insert( + &Language::new_plutus_v1(), + &CostModel::from(vec![ + 197209, 0, 1, 1, 396231, 621, 0, 1, 150000, 1000, 0, 1, 150000, 32, 2477736, 29175, 4, + 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 100, 100, + 29773, 100, 150000, 32, 150000, 32, 150000, 32, 150000, 1000, 0, 1, 150000, 32, 150000, + 1000, 0, 8, 148000, 425507, 118, 0, 1, 1, 150000, 1000, 0, 8, 150000, 112536, 247, 1, + 150000, 10000, 1, 136542, 1326, 1, 1000, 150000, 1000, 1, 150000, 32, 150000, 32, + 150000, 32, 1, 1, 150000, 1, 150000, 4, 103599, 248, 1, 103599, 248, 1, 145276, 1366, + 1, 179690, 497, 1, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, + 32, 148000, 425507, 118, 0, 1, 1, 61516, 11218, 0, 1, 150000, 32, 148000, 425507, 118, + 0, 1, 1, 148000, 425507, 118, 0, 1, 1, 2477736, 29175, 4, 0, 82363, 4, 150000, 5000, 0, + 1, 150000, 32, 197209, 0, 1, 1, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, + 32, 150000, 32, 150000, 32, 3345831, 1, 1, + ]), + ); + res +} + +pub(crate) fn fake_anchor() -> Anchor { + Anchor::new( + &URL::new("https://iohk.io".to_string()).unwrap(), + &fake_anchor_data_hash(1), + ) +} + +pub(crate) fn fake_action_id() -> GovernanceActionId { + GovernanceActionId::new(&fake_tx_hash(1), 1) +} +pub(crate) fn fake_byron_address() -> Address { + ByronAddress::from_base58("Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3") + .unwrap() + .to_address() +} + +pub(crate) fn fake_linear_fee(coefficient: u64, constant: u64) -> LinearFee { + LinearFee::new(&BigNum(coefficient), &BigNum(constant)) +} + +pub(crate) fn fake_default_linear_fee() -> LinearFee { + fake_linear_fee(500, 2) +} + +pub(crate) fn fake_tx_builder_full( + linear_fee: &LinearFee, + pool_deposit: u64, + key_deposit: u64, + max_val_size: u32, + coins_per_utxo_byte: u64, +) -> TransactionBuilder { + let cfg = TransactionBuilderConfigBuilder::new() + .fee_algo(linear_fee) + .pool_deposit(&BigNum(pool_deposit)) + .key_deposit(&BigNum(key_deposit)) + .max_value_size(max_val_size) + .max_tx_size(MAX_TX_SIZE) + .coins_per_utxo_byte(&BigNum(coins_per_utxo_byte)) + .ex_unit_prices(&ExUnitPrices::new( + &SubCoin::new(&BigNum(577), &BigNum(10000)), + &SubCoin::new(&BigNum(721), &BigNum(10000000)), + )) + .ref_script_coins_per_byte( + &UnitInterval::new(&BigNum(1), &BigNum(2)), + ) + .build() + .unwrap(); + TransactionBuilder::new(&cfg) +} + +pub(crate) fn fake_tx_builder( + linear_fee: &LinearFee, + coins_per_utxo_byte: u64, + pool_deposit: u64, + key_deposit: u64, +) -> TransactionBuilder { + fake_tx_builder_full( + linear_fee, + pool_deposit, + key_deposit, + MAX_VALUE_SIZE, + coins_per_utxo_byte, + ) +} + +pub(crate) fn fake_reallistic_tx_builder() -> TransactionBuilder { + fake_tx_builder( + &fake_linear_fee(44, 155381), + COINS_PER_UTXO_BYTE, + 500000000, + 2000000, + ) +} + +pub(crate) fn fake_tx_builder_with_fee_and_val_size( + linear_fee: &LinearFee, + max_val_size: u32, +) -> TransactionBuilder { + fake_tx_builder_full(linear_fee, 1, 1, max_val_size, 1) +} + +pub(crate) fn fake_tx_builder_with_fee(linear_fee: &LinearFee) -> TransactionBuilder { + fake_tx_builder(linear_fee, 1, 1, 1) +} + +pub(crate) fn fake_tx_builder_with_fee_and_pure_change( + linear_fee: &LinearFee, +) -> TransactionBuilder { + TransactionBuilder::new( + &TransactionBuilderConfigBuilder::new() + .fee_algo(linear_fee) + .pool_deposit(&BigNum(1)) + .key_deposit(&BigNum(1)) + .max_value_size(MAX_VALUE_SIZE) + .max_tx_size(MAX_TX_SIZE) + .coins_per_utxo_byte(&BigNum(1)) + .prefer_pure_change(true) + .build() + .unwrap(), + ) +} + +pub(crate) fn fake_tx_builder_with_key_deposit(deposit: u64) -> TransactionBuilder { + fake_tx_builder(&fake_default_linear_fee(), 8, 1, deposit) +} + +pub(crate) fn fake_default_tx_builder() -> TransactionBuilder { + fake_tx_builder_with_fee(&fake_default_linear_fee()) +} + +pub(crate) fn fake_change_address() -> Address { + let spend = fake_root_key() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(0) + .to_public(); + let stake = fake_root_key() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ); + addr.to_address() +} + +pub(crate) fn fake_base_script_address(index: u8) -> Address { + let stake = fake_root_key() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(2) + .derive(0) + .to_public(); + let spend_cred = Credential::from_scripthash(&fake_script_hash(index)); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + let addr = BaseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &stake_cred, + ); + addr.to_address() +} + +pub(crate) fn fake_enterprise_address(index: u32) -> Address { + let spend = fake_root_key() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(index) + .to_public(); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let addr = EnterpriseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + ); + addr.to_address() +} + +pub(crate) fn fake_enterprise_script_address(index: u8) -> Address { + let spend_cred = Credential::from_scripthash(&fake_script_hash(index)); + let addr = EnterpriseAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + ); + addr.to_address() +} + +pub(crate) fn fake_pointer_address(index: u32) -> Address { + let spend = fake_root_key() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(index) + .to_public(); + let spend_cred = Credential::from_keyhash(&spend.to_raw_key().hash()); + let pointer = Pointer::new(1, 2, 3); + let addr = PointerAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &pointer + ); + addr.to_address() +} + +pub(crate) fn fake_pointer_script_address(index: u8) -> Address { + let spend_cred = Credential::from_scripthash(&fake_script_hash(index)); + let pointer = Pointer::new(1, 2, 3); + let addr = PointerAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &spend_cred, + &pointer + ); + addr.to_address() +} + +pub(crate) fn fake_reward_address(index: u32) -> RewardAddress { + let stake = fake_root_key() + .derive(harden(1852)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(1) + .derive(index) + .to_public(); + let stake_cred = Credential::from_keyhash(&stake.to_raw_key().hash()); + RewardAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &stake_cred, + ) +} + +pub(crate) fn fake_malformed_address() -> Address { + MalformedAddress(vec![255, 255, 255, 255, 255, 255]).to_address() +} + +pub(crate) fn fake_rich_tx_builder(with_collateral: bool) -> TransactionBuilder { + let mut tx_builder = fake_reallistic_tx_builder(); + let input = TransactionInput::new(&fake_tx_hash(1), 0); + let address = fake_base_address(1); + let mut input_builder = TxInputsBuilder::new(); + input_builder.add_regular_input(&address, &input, &Value::new(&Coin::from(u64::MAX / 2))) + .expect("should add input"); + tx_builder.set_inputs(&input_builder); + if with_collateral { + tx_builder.set_collateral(&input_builder); + } + + tx_builder +} + +pub(crate) fn fake_tx_builder_with_amount( + amount: u64, + with_collateral: bool, +) -> TransactionBuilder { + let mut tx_builder = fake_reallistic_tx_builder(); + let input = TransactionInput::new(&fake_tx_hash(1), 0); + let address = fake_base_address(1); + let mut input_builder = TxInputsBuilder::new(); + input_builder.add_regular_input(&address, &input, &Value::new(&Coin::from(amount))).expect("should add input"); + tx_builder.set_inputs(&input_builder); + if with_collateral { + let col_input = TransactionInput::new(&fake_tx_hash(1), 0); + let mut col_input_builder = TxInputsBuilder::new(); + col_input_builder.add_regular_input(&address, &col_input, &Value::new(&Coin::from(u64::MAX / 2))).expect("should add input"); + tx_builder.set_collateral(&col_input_builder); + } + + tx_builder +} + +pub(crate) fn fake_tx_builder_with_amount_and_deposit_params( + amount: u64, + pool_deposit: u64, + key_deposit: u64, + with_collateral: bool, +) -> TransactionBuilder { + let mut tx_builder = fake_tx_builder( + &fake_linear_fee(44, 155381), + COINS_PER_UTXO_BYTE, + pool_deposit, + key_deposit + ); + let input = TransactionInput::new(&fake_tx_hash(1), 0); + let address = fake_base_address(1); + let mut input_builder = TxInputsBuilder::new(); + input_builder.add_regular_input(&address, &input, &Value::new(&Coin::from(amount))).expect("should add input"); + tx_builder.set_inputs(&input_builder); + if with_collateral { + let col_input = TransactionInput::new(&fake_tx_hash(1), 0); + let mut col_input_builder = TxInputsBuilder::new(); + col_input_builder.add_regular_input(&address, &col_input, &Value::new(&Coin::from(u64::MAX / 2))).expect("should add input"); + tx_builder.set_collateral(&col_input_builder); + } + + tx_builder +} + +pub(crate) fn fake_plutus_script(x: u8, lang: &Language) -> PlutusScript { + let mut bytes = hex::decode("4e4d01000033222220051200120011").unwrap(); + let pos = bytes.len() - 1; + bytes[pos] = x; + PlutusScript::from_bytes_with_version(bytes, lang).unwrap() +} + +pub(crate) fn fake_redeemer(x: u8) -> Redeemer { + Redeemer::new( + &RedeemerTag::new_cert(), + &BigNum::from(x as u64), + &PlutusData::new_empty_constr_plutus_data(&BigNum::from(x)), + &ExUnits::new(&BigNum::from(x), &BigNum::from(x)), + ) +} + +pub(crate) fn fake_redeemer_with_tag(index: u64, tag: &RedeemerTag, data: &PlutusData) -> Redeemer { + Redeemer::new( + tag, + &BigNum::from(index), + data, + &ExUnits::new(&BigNum::from(index), &BigNum::from(index)), + ) +} + +pub(crate) fn fake_redeemer_zero_cost(x: u8) -> Redeemer { + Redeemer::new( + &RedeemerTag::new_cert(), + &BigNum::from(x as u64), + &PlutusData::new_empty_constr_plutus_data(&BigNum::from(x)), + &ExUnits::new(&BigNum::zero(), &BigNum::zero()), + ) +} + + +pub(crate) fn fake_bytes_32(x: u8) -> Vec { + vec![ + x, 239, 181, 120, 142, 135, 19, 200, 68, 223, 211, 43, 46, 145, 222, 30, 48, 159, 239, 255, + 213, 85, 248, 39, 204, 158, 225, 100, 1, 2, 3, 4, + ] +} + +pub(crate) fn fake_data_hash(x: u8) -> DataHash { + DataHash::from_bytes(fake_bytes_32(x)).unwrap() +} + +pub(crate) fn fake_anchor_data_hash(x: u8) -> AnchorDataHash { + AnchorDataHash::from_bytes(fake_bytes_32(x)).unwrap() +} + +pub(crate) fn fake_auxiliary_data_hash(x: u8) -> AuxiliaryDataHash { + AuxiliaryDataHash::from_bytes(fake_bytes_32(x)).unwrap() +} + +pub(crate) fn fake_pool_metadata_hash(x: u8) -> PoolMetadataHash { + PoolMetadataHash::from_bytes(fake_bytes_32(x)).unwrap() +} + +pub(crate) fn fake_genesis_hash(x: u8) -> GenesisHash { + GenesisHash::from_bytes((&fake_bytes_32(x)[0..28]).to_vec()).unwrap() +} + +pub(crate) fn fake_genesis_delegate_hash(x: u8) -> GenesisDelegateHash { + GenesisDelegateHash::from_bytes((&fake_bytes_32(x)[0..28]).to_vec()).unwrap() +} + +pub(crate) fn fake_vrf_key_hash(x: u8) -> VRFKeyHash { + VRFKeyHash::from_bytes(fake_bytes_32(x)).unwrap() +} + +pub(crate) fn fake_key_hash(x: u8) -> Ed25519KeyHash { + Ed25519KeyHash::from_bytes((&fake_bytes_32(x)[0..28]).to_vec()).unwrap() +} + +pub(crate) fn fake_script_hash(x: u8) -> ScriptHash { + ScriptHash::from_bytes((&fake_bytes_32(x)[0..28]).to_vec()).unwrap() +} + +pub(crate) fn fake_script_data_hash(x: u8) -> ScriptDataHash { + ScriptDataHash::from_bytes(fake_bytes_32(x)).unwrap() +} + +pub(crate) fn fake_tx_hash(input_hash_byte: u8) -> TransactionHash { + TransactionHash::from([input_hash_byte; 32]) +} + +pub(crate) fn fake_tx_input(input_hash_byte: u8) -> TransactionInput { + fake_tx_input2(input_hash_byte, 0) +} + +pub(crate) fn fake_tx_input2(input_hash_byte: u8, idx: TransactionIndex) -> TransactionInput { + TransactionInput::new(&fake_tx_hash(input_hash_byte), idx) +} + +pub(crate) fn fake_value() -> Value { + fake_value2(1_000_000) +} + +pub(crate) fn fake_value2(v: u64) -> Value { + Value::new(&BigNum(v)) +} + +pub(crate) fn fake_tx_output(input_hash_byte: u8) -> TransactionOutput { + TransactionOutput::new(&fake_base_address(input_hash_byte as u32), &fake_value()) +} + +pub(crate) fn fake_tx_output2(input_hash_byte: u8, val: u64) -> TransactionOutput { + TransactionOutput::new(&fake_base_address(input_hash_byte as u32), &fake_value2(val)) +} + +pub(crate) fn fake_vkey() -> Vkey { + Vkey::new( + &Bip32PrivateKey::generate_ed25519_bip32() + .unwrap() + .to_public() + .to_raw_key(), + ) +} + +pub(crate) fn fake_vkey_numbered(x: u8) -> Vkey { + Vkey::new(&PublicKey::from_bytes(&[x; 32]).unwrap()) +} + +pub(crate) fn fake_signature(x: u8) -> Ed25519Signature { + Ed25519Signature::from_bytes([x; 64].to_vec()).unwrap() +} + +pub(crate) fn fake_policy_id(x: u8) -> PolicyID { + PolicyID::from([x; 28]) +} + +pub(crate) fn fake_asset_name(x: u8) -> AssetName { + AssetName([x; 32].to_vec()) +} + +pub(crate) fn fake_vkey_witness(x: u8) -> Vkeywitness { + Vkeywitness::new(&fake_vkey_numbered(x), &fake_signature(x)) +} + +pub(crate) fn fake_boostrap_witness(x: u8) -> BootstrapWitness { + BootstrapWitness::new( + &fake_vkey_numbered(x), + &fake_signature(x), + vec![x; 32], + vec![x; 32], + ) +} + +pub(crate) fn fake_plutus_script_and_hash(x: u8) -> (PlutusScript, ScriptHash) { + let s = PlutusScript::new(fake_bytes_32(x)); + (s.clone(), s.hash()) +} \ No newline at end of file diff --git a/rust/src/tests/fees.rs b/rust/src/tests/fees.rs new file mode 100644 index 00000000..04de0d19 --- /dev/null +++ b/rust/src/tests/fees.rs @@ -0,0 +1,588 @@ +use crate::TransactionOutputBuilder; +use crate::*; + +// based off tx test vectors (https://gist.github.com/KtorZ/5a2089df0915f21aca368d12545ab230) + +// However, they don't match due to serialization differences in definite vs indefinite +// CBOR lengths for maps/arrays, thus for now we've got all the tests as >= instead. +// It's possible they're still off by a byte or two somewhere. + +#[test] +fn tx_simple_utxo() { + // # Vector #1: simple transaction + let mut inputs = TransactionInputs::new(); + inputs.add(&TransactionInput::new( + &TransactionHash::from_bytes( + hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") + .unwrap(), + ) + .unwrap(), + 0, + )); + let mut outputs = TransactionOutputs::new(); + + outputs.add( + &TransactionOutputBuilder::new() + .with_address( + &Address::from_bytes( + hex::decode("611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c") + .unwrap(), + ) + .unwrap(), + ) + .next() + .unwrap() + .with_coin(&BigNum(1)) + .build() + .unwrap(), + ); + let body = TransactionBody::new(&inputs, &outputs, &BigNum(94002), Some(10)); + + let mut w = TransactionWitnessSet::new(); + let mut vkw = Vkeywitnesses::new(); + vkw.add(&make_vkey_witness( + &hash_transaction(&body), + &PrivateKey::from_normal_bytes( + &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a") + .unwrap(), + ) + .unwrap(), + )); + w.set_vkeys(&vkw); + + let signed_tx = Transaction::new(&body, &w, None); + + let linear_fee = LinearFee::new(&BigNum(500), &BigNum(2)); + assert_eq!( + hex::encode(signed_tx.to_bytes()), + "84a400818258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b700018182581d611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c01021a00016f32030aa10081825820f9aa3fccb7fe539e471188ccc9ee65514c5961c070b06ca185962484a4813bee5840fae5de40c94d759ce13bf9886262159c4f26a289fd192e165995b785259e503f6887bf39dfa23a47cf163784c6eee23f61440e749bc1df3c73975f5231aeda0ff5f6" + ); + assert_eq!( + min_fee(&signed_tx, &linear_fee).unwrap().to_str(), + "94502" // todo: compare to Haskell fee to make sure the diff is not too big + ); +} + +#[test] +fn tx_simple_byron_utxo() { + let mut inputs = TransactionInputs::new(); + inputs.add(&TransactionInput::new( + &TransactionHash::from_bytes( + hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") + .unwrap(), + ) + .unwrap(), + 0, + )); + let mut outputs = TransactionOutputs::new(); + + outputs.add( + &TransactionOutputBuilder::new() + .with_address( + &Address::from_bytes( + hex::decode("611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c") + .unwrap(), + ) + .unwrap(), + ) + .next() + .unwrap() + .with_coin(&BigNum(1)) + .build() + .unwrap(), + ); + let body = TransactionBody::new(&inputs, &outputs, &BigNum(112002), Some(10)); + + let mut w = TransactionWitnessSet::new(); + let mut bootstrap_wits = BootstrapWitnesses::new(); + bootstrap_wits.add(&make_icarus_bootstrap_witness( + &hash_transaction(&body), + &ByronAddress::from_base58("Ae2tdPwUPEZ6r6zbg4ibhFrNnyKHg7SYuPSfDpjKxgvwFX9LquRep7gj7FQ").unwrap(), + &Bip32PrivateKey::from_bytes( + &hex::decode("d84c65426109a36edda5375ea67f1b738e1dacf8629f2bb5a2b0b20f3cd5075873bf5cdfa7e533482677219ac7d639e30a38e2e645ea9140855f44ff09e60c52c8b95d0d35fe75a70f9f5633a3e2439b2994b9e2bc851c49e9f91d1a5dcbb1a3").unwrap() + ).unwrap() + )); + w.set_bootstraps(&bootstrap_wits); + + let signed_tx = Transaction::new(&body, &w, None); + + let linear_fee = LinearFee::new(&BigNum(500), &BigNum(2)); + assert_eq!( + hex::encode(signed_tx.to_bytes()), + "84a400818258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b700018182581d611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c01021a0001b582030aa10281845820473811afd4d939b337c9be1a2ceeb2cb2c75108bddf224c5c21c51592a7b204a5840f0b04a852353eb23b9570df80b2aa6a61b723341ab45a2024a05b07cf58be7bdfbf722c09040db6cee61a0d236870d6ad1e1349ac999ec0db28f9471af25fb0c5820c8b95d0d35fe75a70f9f5633a3e2439b2994b9e2bc851c49e9f91d1a5dcbb1a341a0f5f6" + ); + assert_eq!( + min_fee(&signed_tx, &linear_fee).unwrap().to_str(), + "112502" // todo: compare to Haskell fee to make sure the diff is not too big + ); +} + +#[test] +fn tx_multi_utxo() { + // # Vector #2: multiple outputs and inputs + let mut inputs = TransactionInputs::new(); + inputs.add(&TransactionInput::new( + &TransactionHash::from_bytes( + hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") + .unwrap(), + ) + .unwrap(), + 42, + )); + inputs.add(&TransactionInput::new( + &TransactionHash::from_bytes( + hex::decode("82839f8200d81858248258203b40265111d8bb3c3c608d95b3a0bf83461ace32") + .unwrap(), + ) + .unwrap(), + 7, + )); + let mut outputs = TransactionOutputs::new(); + + outputs.add( + &TransactionOutputBuilder::new() + .with_address( + &Address::from_bytes( + hex::decode("611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c") + .unwrap(), + ) + .unwrap(), + ) + .next() + .unwrap() + .with_coin(&BigNum(289)) + .build() + .unwrap(), + ); + outputs.add( + &TransactionOutputBuilder::new() + .with_address( + &Address::from_bytes( + hex::decode("61bcd18fcffa797c16c007014e2b8553b8b9b1e94c507688726243d611") + .unwrap(), + ) + .unwrap(), + ) + .next() + .unwrap() + .with_coin(&BigNum(874551452)) + .build() + .unwrap(), + ); + let body = TransactionBody::new(&inputs, &outputs, &BigNum(183502), Some(999)); + + let mut w = TransactionWitnessSet::new(); + let mut vkw = Vkeywitnesses::new(); + vkw.add(&make_vkey_witness( + &hash_transaction(&body), + &PrivateKey::from_normal_bytes( + &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a") + .unwrap(), + ) + .unwrap(), + )); + vkw.add(&make_vkey_witness( + &hash_transaction(&body), + &PrivateKey::from_normal_bytes( + &hex::decode("13fe79205e16c09536acb6f0524d04069f380329d13949698c5f22c65c989eb4") + .unwrap(), + ) + .unwrap(), + )); + w.set_vkeys(&vkw); + + let signed_tx = Transaction::new(&body, &w, None); + + let linear_fee = LinearFee::new(&BigNum(500), &BigNum(2)); + assert_eq!( + hex::encode(signed_tx.to_bytes()), + "84a400828258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7182a82582082839f8200d81858248258203b40265111d8bb3c3c608d95b3a0bf83461ace3207018282581d611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c19012182581d61bcd18fcffa797c16c007014e2b8553b8b9b1e94c507688726243d6111a3420989c021a0002ccce031903e7a10082825820f9aa3fccb7fe539e471188ccc9ee65514c5961c070b06ca185962484a4813bee58401ec3e56008650282ba2e1f8a20e81707810b2d0973c4d42a1b4df65b732bda81567c7824904840b2554d2f33861da5d70588a29d33b2b61042e3c3445301d8008258206872b0a874acfe1cace12b20ea348559a7ecc912f2fc7f674f43481df973d92c5840a0718fb5b37d89ddf926c08e456d3f4c7f749e91f78bb3e370751d5b632cbd20d38d385805291b1ef2541b02543728a235e01911f4b400bfb50e5fce589de907f5f6" + ); + assert_eq!( + min_fee(&signed_tx, &linear_fee).unwrap().to_str(), + "184002" // todo: compare to Haskell fee to make sure the diff is not too big + ); +} + +#[test] +fn tx_register_stake() { + // # Vector #3: with stake pool registration certificate + let network = 1; + let mut inputs = TransactionInputs::new(); + inputs.add(&TransactionInput::new( + &TransactionHash::from_bytes( + hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") + .unwrap(), + ) + .unwrap(), + 0, + )); + let mut outputs = TransactionOutputs::new(); + + outputs.add( + &TransactionOutputBuilder::new() + .with_address( + &Address::from_bytes( + hex::decode("611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c") + .unwrap(), + ) + .unwrap(), + ) + .next() + .unwrap() + .with_coin(&BigNum(1)) + .build() + .unwrap(), + ); + let mut body = TransactionBody::new(&inputs, &outputs, &BigNum(266002), Some(10)); + + let mut certs = Certificates::new(); + + let mut pool_owners = Ed25519KeyHashes::new(); + pool_owners.add( + &PublicKey::from_bytes( + &hex::decode("54d1a9c5ad69586ceeb839c438400c376c0bd34825fb4c17cc2f58c54e1437f3") + .unwrap(), + ) + .unwrap() + .hash(), + ); + let registration_cert = PoolRegistration::new(&PoolParams::new( + &PublicKey::from_bytes( + &hex::decode("b24c040e65994bd5b0621a060166d32d356ef4be3cc1f848426a4cf386887089") + .unwrap(), + ) + .unwrap() + .hash(), // operator + &VRFKeyHash::from(blake2b256( + &hex::decode("fbf6d41985670b9041c5bf362b5262cf34add5d265975de176d613ca05f37096") + .unwrap(), + )), // vrf_keyhash + &BigNum(1000000), // pledge + &BigNum(1000000), // cost + &UnitInterval::new(&BigNum(3), &BigNum(100)), // margin + &RewardAddress::new( + network, + &Credential::from_keyhash( + &PublicKey::from_bytes( + &hex::decode( + "54d1a9c5ad69586ceeb839c438400c376c0bd34825fb4c17cc2f58c54e1437f3", + ) + .unwrap(), + ) + .unwrap() + .hash(), + ), + ), // reward_address + &pool_owners, // pool_owners + &Relays::new(), // relays + None, // metadata + )); + certs.add(&Certificate::new_pool_registration(®istration_cert)); + body.set_certs(&certs); + + let mut w = TransactionWitnessSet::new(); + let mut vkw = Vkeywitnesses::new(); + // input key witness + vkw.add(&make_vkey_witness( + &hash_transaction(&body), + &PrivateKey::from_normal_bytes( + &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a") + .unwrap(), + ) + .unwrap(), + )); + // operator key witness + vkw.add(&make_vkey_witness( + &hash_transaction(&body), + &PrivateKey::from_normal_bytes( + &hex::decode("2363f3660b9f3b41685665bf10632272e2d03c258e8a5323436f0f3406293505") + .unwrap(), + ) + .unwrap(), + )); + // owner key witness + vkw.add(&make_vkey_witness( + &hash_transaction(&body), + &PrivateKey::from_normal_bytes( + &hex::decode("5ada7f4d92bce1ee1707c0a0e211eb7941287356e6ed0e76843806e307b07c8d") + .unwrap(), + ) + .unwrap(), + )); + w.set_vkeys(&vkw); + + let signed_tx = Transaction::new(&body, &w, None); + + let linear_fee = LinearFee::new(&BigNum(500), &BigNum(2)); + assert_eq!( + hex::encode(signed_tx.to_bytes()), + "84a500818258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b700018182581d611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c01021a00040f12030a04818a03581c1c13374874c68016df54b1339b6cacdd801098431e7659b24928efc15820bd0000f498ccacdc917c28274cba51c415f3f21931ff41ca8dc1197499f8e1241a000f42401a000f4240d81e82031864581de151df9ba1b74a1c9608a487e114184556801e927d31d96425cb80af7081581c51df9ba1b74a1c9608a487e114184556801e927d31d96425cb80af7080f6a10083825820f9aa3fccb7fe539e471188ccc9ee65514c5961c070b06ca185962484a4813bee5840a7f305d7e46abfe0f7bea6098bdf853ab9ce8e7aa381be5a991a871852f895a718e20614e22be43494c4dc3a8c78c56cd44fd38e0e5fff3e2fbd19f70402fc02825820b24c040e65994bd5b0621a060166d32d356ef4be3cc1f848426a4cf386887089584013c372f82f1523484eab273241d66d92e1402507760e279480912aa5f0d88d656d6f25d41e65257f2f38c65ac5c918a6735297741adfc718394994f20a1cfd0082582054d1a9c5ad69586ceeb839c438400c376c0bd34825fb4c17cc2f58c54e1437f35840d326b993dfec21b9b3e1bd2f80adadc2cd673a1d8d033618cc413b0b02bc3b7efbb23d1ff99138abd05c398ce98e7983a641b50dcf0f64ed33f26c6e636b0b0ff5f6" + ); + assert_eq!( + min_fee(&signed_tx, &linear_fee).unwrap().to_str(), + "269502" // todo: compare to Haskell fee to make sure the diff is not too big + ); +} + +// #[test] +// fn tx_delegate_stake() { +// let mut inputs = TransactionInputs::new(); +// inputs.add(&TransactionInput::new(&genesis_id(), 0)); +// let mut outputs = TransactionOutputs::new(); +// outputs.add(&TransactionOutput::new(&alice_addr(), BigNum(10))); +// let mut body = TransactionBody::new(&inputs, &outputs, BigNum(94), 10); +// let mut certs = Certificates::new(); +// certs.add(&Certificate::new_stake_delegation(&StakeDelegation::new(&bob_stake(), &alice_pool()))); +// body.set_certs(&certs); +// let w = make_mock_witnesses_vkey(&body, vec![&alice_key(), &bob_key()]); +// let tx = Transaction::new(&body, &w, None); +// let haskell_crypto_bytes = witness_vkey_bytes_haskell(&w) + HASKELL_HLEN * 2; +// let our_crypto_bytes = witness_vkey_bytes_rust(&w) + Ed25519KeyHash::BYTE_COUNT + Ed25519KeyHash::BYTE_COUNT; +// assert!(txsize(&tx) - our_crypto_bytes + haskell_crypto_bytes >= 178); +// } + +// #[test] +// fn tx_deregister_stake() { +// let mut inputs = TransactionInputs::new(); +// inputs.add(&TransactionInput::new(&genesis_id(), 0)); +// let mut outputs = TransactionOutputs::new(); +// outputs.add(&TransactionOutput::new(&alice_addr(), BigNum(10))); +// let mut body = TransactionBody::new(&inputs, &outputs, BigNum(94), 10); +// let mut certs = Certificates::new(); +// certs.add(&Certificate::new_stake_deregistration(&StakeDeregistration::new(&alice_pay()))); +// body.set_certs(&certs); +// let w = make_mock_witnesses_vkey(&body, vec![&alice_key()]); +// let tx = Transaction::new(&body, &w, None); +// let haskell_crypto_bytes = witness_vkey_bytes_haskell(&w) + HASKELL_HLEN; +// let our_crypto_bytes = witness_vkey_bytes_rust(&w) + Ed25519KeyHash::BYTE_COUNT; +// assert!(txsize(&tx) - our_crypto_bytes + haskell_crypto_bytes >= 150); +// } + +// #[test] +// fn tx_register_pool() { +// let mut inputs = TransactionInputs::new(); +// inputs.add(&TransactionInput::new(&genesis_id(), 0)); +// let mut outputs = TransactionOutputs::new(); +// outputs.add(&TransactionOutput::new(&alice_addr(), BigNum(10))); +// let mut body = TransactionBody::new(&inputs, &outputs, BigNum(94), 10); +// let mut certs = Certificates::new(); +// let mut owners = Ed25519KeyHashes::new(); +// owners.add(&(alice_stake().to_keyhash().unwrap())); +// let mut relays = Relays::new(); +// relays.add(&Relay::new_single_host_name(&SingleHostName::new(None, String::from("relay.io")))); +// let params = PoolParams::new( +// &alice_pool(), +// &VRFKeyHash::from([0u8; VRFKeyHash::BYTE_COUNT]), +// BigNum(1), +// BigNum(5), +// &UnitInterval::new(BigNum(1), BigNum(10)), +// &RewardAddress::new(NetworkInfo::testnet_preprod().network_id(), &alice_stake()), +// &owners, +// &relays, +// Some(PoolMetadata::new(String::from("alice.pool"), &MetadataHash::from([0u8; MetadataHash::BYTE_COUNT]))) +// ); +// certs.add(&Certificate::new_pool_registration(&PoolRegistration::new(¶ms))); +// body.set_certs(&certs); +// let w = make_mock_witnesses_vkey(&body, vec![&alice_key()]); +// let tx = Transaction::new(&body, &w, None); +// let haskell_crypto_bytes = witness_vkey_bytes_haskell(&w) +// + HASKELL_HLEN // operator pool keyhash +// + HASKELL_HLEN // vrf keyhash +// + HASKELL_HLEN // reward account +// + owners.len() * HASKELL_HLEN // owners' keyhashes +// + HASKELL_HLEN; // metadata hash +// let our_crypto_bytes = witness_vkey_bytes_rust(&w) +// + Ed25519KeyHash::BYTE_COUNT +// + VRFKeyHash::BYTE_COUNT +// + Ed25519KeyHash::BYTE_COUNT +// + owners.len() * Ed25519KeyHash::BYTE_COUNT +// + MetadataHash::BYTE_COUNT; +// assert!(txsize(&tx) - our_crypto_bytes + haskell_crypto_bytes >= 200); +// } + +// #[test] +// fn tx_retire_pool() { +// let mut inputs = TransactionInputs::new(); +// inputs.add(&TransactionInput::new(&genesis_id(), 0)); +// let mut outputs = TransactionOutputs::new(); +// outputs.add(&TransactionOutput::new(&alice_addr(), BigNum(10))); +// let mut body = TransactionBody::new(&inputs, &outputs, BigNum(94), 10); +// let mut certs = Certificates::new(); +// certs.add(&Certificate::new_pool_retirement(&PoolRetirement::new(&alice_pool(), 5))); +// body.set_certs(&certs); +// let w = make_mock_witnesses_vkey(&body, vec![&alice_key()]); +// let tx = Transaction::new(&body, &w, None); +// let haskell_crypto_bytes = witness_vkey_bytes_haskell(&w) + HASKELL_HLEN; +// let our_crypto_bytes = witness_vkey_bytes_rust(&w) + Ed25519KeyHash::BYTE_COUNT; +// assert!(txsize(&tx) - our_crypto_bytes + haskell_crypto_bytes >= 149); +// } + +// #[test] +// fn tx_metadata() { +// let mut inputs = TransactionInputs::new(); +// inputs.add(&TransactionInput::new(&genesis_id(), 0)); +// let mut outputs = TransactionOutputs::new(); +// outputs.add(&TransactionOutput::new(&alice_addr(), BigNum(10))); +// let mut body = TransactionBody::new(&inputs, &outputs, BigNum(94), 10); +// body.set_metadata_hash(&MetadataHash::from([37; MetadataHash::BYTE_COUNT])); +// let w = make_mock_witnesses_vkey(&body, vec![&alice_key()]); +// let mut metadata = TransactionMetadata::new(); +// let mut md_list = TransactionMetadatums::new(); +// md_list.add(&TransactionMetadatum::new_int(&Int::new(&BigNum(5)))); +// md_list.add(&TransactionMetadatum::new_text(String::from("hello"))); +// metadata.insert(TransactionMetadatumLabel::new(0), &TransactionMetadatum::new_arr_transaction_metadatum(&md_list)); +// let tx = Transaction::new(&body, &w, Some(metadata)); +// let haskell_crypto_bytes = witness_vkey_bytes_haskell(&w) + HASKELL_HLEN; +// let our_crypto_bytes = witness_vkey_bytes_rust(&w) + MetadataHash::BYTE_COUNT; +// assert!(txsize(&tx) - our_crypto_bytes + haskell_crypto_bytes >= 154); +// } + +// #[test] +// fn tx_multisig() { +// let mut inputs = TransactionInputs::new(); +// inputs.add(&TransactionInput::new(&genesis_id(), 0)); +// let mut outputs = TransactionOutputs::new(); +// outputs.add(&TransactionOutput::new(&alice_addr(), BigNum(10))); +// let body = TransactionBody::new(&inputs, &outputs, BigNum(94), 10); +// let mut w = make_mock_witnesses_vkey(&body, vec![&alice_key(), &bob_key()]); +// let mut script_witnesses = MultisigScripts::new(); +// let mut inner_scripts = MultisigScripts::new(); +// inner_scripts.add(&MultisigScript::new_msig_pubkey(&alice_pay().to_keyhash().unwrap())); +// inner_scripts.add(&MultisigScript::new_msig_pubkey(&bob_pay().to_keyhash().unwrap())); +// inner_scripts.add(&MultisigScript::new_msig_pubkey(&carl_pay().to_keyhash().unwrap())); +// script_witnesses.add(&MultisigScript::new_msig_n_of_k(2, &inner_scripts)); +// w.set_scripts(&script_witnesses); +// let tx = Transaction::new(&body, &w, None); +// let haskell_crypto_bytes = witness_vkey_bytes_haskell(&w); +// let our_crypto_bytes = witness_vkey_bytes_rust(&w); +// assert!(txsize(&tx) - our_crypto_bytes + haskell_crypto_bytes - haskell_multisig_byte_diff(&script_witnesses) >= 189); +// } + +#[test] +fn tx_withdrawal() { + // # Vector #8: with reward withdrawal + let mut inputs = TransactionInputs::new(); + inputs.add(&TransactionInput::new( + &TransactionHash::from_bytes( + hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") + .unwrap(), + ) + .unwrap(), + 0, + )); + let mut outputs = TransactionOutputs::new(); + + outputs.add( + &TransactionOutputBuilder::new() + .with_address( + &Address::from_bytes( + hex::decode("611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c") + .unwrap(), + ) + .unwrap(), + ) + .next() + .unwrap() + .with_coin(&BigNum(1)) + .build() + .unwrap(), + ); + let mut body = TransactionBody::new(&inputs, &outputs, &BigNum(162502), Some(10)); + let mut withdrawals = Withdrawals::new(); + withdrawals.insert( + &RewardAddress::from_address( + &Address::from_bytes( + hex::decode("e151df9ba1b74a1c9608a487e114184556801e927d31d96425cb80af70").unwrap(), + ) + .unwrap(), + ) + .unwrap(), + &BigNum(1337), + ); + body.set_withdrawals(&withdrawals); + + let mut w = TransactionWitnessSet::new(); + let mut vkw = Vkeywitnesses::new(); + // input key witness + vkw.add(&make_vkey_witness( + &hash_transaction(&body), + &PrivateKey::from_normal_bytes( + &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a") + .unwrap(), + ) + .unwrap(), + )); + // withdrawal key witness + vkw.add(&make_vkey_witness( + &hash_transaction(&body), + &PrivateKey::from_normal_bytes( + &hex::decode("5ada7f4d92bce1ee1707c0a0e211eb7941287356e6ed0e76843806e307b07c8d") + .unwrap(), + ) + .unwrap(), + )); + w.set_vkeys(&vkw); + + let signed_tx = Transaction::new(&body, &w, None); + + let linear_fee = LinearFee::new(&BigNum(500), &BigNum(2)); + assert_eq!( + hex::encode(signed_tx.to_bytes()), + "84a500818258203b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b700018182581d611c616f1acb460668a9b2f123c80372c2adad3583b9c6cd2b1deeed1c01021a00027ac6030a05a1581de151df9ba1b74a1c9608a487e114184556801e927d31d96425cb80af70190539a10082825820f9aa3fccb7fe539e471188ccc9ee65514c5961c070b06ca185962484a4813bee5840fc0493f7121efe385d72830680e735ccdef99c3a31953fe877b89ad3a97fcdb871cc7f2cdd6a8104e52f6963bd9e10d814d4fabdbcdc8475bc63e872dcc94d0a82582054d1a9c5ad69586ceeb839c438400c376c0bd34825fb4c17cc2f58c54e1437f35840a051ba927582004aedab736b9f1f9330ff867c260f4751135d480074256e83cd23d2a4bb109f955c43afdcdc5d1841b28d5c1ea2148dfbb6252693590692bb00f5f6" + ); + assert_eq!( + min_fee(&signed_tx, &linear_fee).unwrap().to_str(), + "163002" // todo: compare to Haskell fee to make sure the diff is not too big + ); +} + +fn exunits(mem: u64, steps: u64) -> ExUnits { + ExUnits::new(&BigNum(mem), &BigNum(steps)) +} + +fn subcoin(num: u64, denum: u64) -> SubCoin { + SubCoin::new(&BigNum(num), &BigNum(denum)) +} + +fn exunit_prices(mem_prices: (u64, u64), step_prices: (u64, u64)) -> ExUnitPrices { + ExUnitPrices::new( + &subcoin(mem_prices.0, mem_prices.1), + &subcoin(step_prices.0, step_prices.1), + ) +} + +fn _calculate_ex_units_ceil_cost( + mem: u64, + steps: u64, + mem_prices: (u64, u64), + step_prices: (u64, u64), +) -> Coin { + let ex_units = exunits(mem, steps); + let ex_unit_prices = exunit_prices(mem_prices, step_prices); + calculate_ex_units_ceil_cost(&ex_units, &ex_unit_prices).unwrap() +} + +#[test] +fn test_calc_ex_units_cost() { + // 10 * (2/1) + 20 * (3/1) = 10 * 2 + 20 * 3 = 20 + 60 + assert_eq!( + _calculate_ex_units_ceil_cost(10, 20, (2, 1), (3, 1)), + BigNum(80), + ); + // 22 * (12/6) + 33 * (33/11) = 22 * 2 + 33 * 3 = 44 + 99 = 143 + assert_eq!( + _calculate_ex_units_ceil_cost(22, 33, (12, 6), (33, 11)), + BigNum(143), + ); + // 10 * (5/7) + 20 * (9/13) = 50/7 + 180/13 = 650/91 + 1260/91 = 1910/91 = ceil(20.98) = 21 + assert_eq!( + _calculate_ex_units_ceil_cost(10, 20, (5, 7), (9, 13)), + BigNum(21), + ); + // 22 * (7/5) + 33 * (13/9) = 154/5 + 429/9 = 1386/45 + 2145/45 = 3531/45 = ceil(78.46) = 79 + assert_eq!( + _calculate_ex_units_ceil_cost(22, 33, (7, 5), (13, 9)), + BigNum(79), + ); +} diff --git a/rust/src/tests/general.rs b/rust/src/tests/general.rs new file mode 100644 index 00000000..d6b3273f --- /dev/null +++ b/rust/src/tests/general.rs @@ -0,0 +1,854 @@ +use crate::*; +use crate::tests::helpers::harden; +use crate::tests::fakes::{fake_plutus_script, fake_boostrap_witness, fake_tx_input, fake_vkey_witness}; + +#[test] +fn native_script_hash() { + let keyhash = Ed25519KeyHash::from_bytes(vec![ + 143, 180, 186, 93, 223, 42, 243, 7, 81, 98, 86, 125, 97, 69, 110, 52, 130, 243, 244, 98, + 246, 13, 33, 212, 128, 168, 136, 40, + ]) + .unwrap(); + assert_eq!( + hex::encode(&keyhash.to_bytes()), + "8fb4ba5ddf2af3075162567d61456e3482f3f462f60d21d480a88828" + ); + + let script = NativeScript::new_script_pubkey(&ScriptPubkey::new(&keyhash)); + + let script_hash = script.hash(); + + assert_eq!( + hex::encode(&script_hash.to_bytes()), + "187b8d3ddcb24013097c003da0b8d8f7ddcf937119d8f59dccd05a0f" + ); +} + +#[test] +fn asset_name_ord() { + let name1 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + let name11 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + + let name2 = AssetName::new(vec![0u8, 4, 5, 6]).unwrap(); + let name22 = AssetName::new(vec![0u8, 4, 5, 6]).unwrap(); + + let name3 = AssetName::new(vec![0u8, 7, 8]).unwrap(); + let name33 = AssetName::new(vec![0u8, 7, 8]).unwrap(); + + assert_eq!(name1.cmp(&name2), Ordering::Less); + assert_eq!(name2.cmp(&name1), Ordering::Greater); + assert_eq!(name1.cmp(&name3), Ordering::Greater); + assert_eq!(name2.cmp(&name3), Ordering::Greater); + assert_eq!(name3.cmp(&name1), Ordering::Less); + assert_eq!(name3.cmp(&name2), Ordering::Less); + + assert_eq!(name1.cmp(&name11), Ordering::Equal); + assert_eq!(name2.cmp(&name22), Ordering::Equal); + assert_eq!(name3.cmp(&name33), Ordering::Equal); + + let mut map = Assets::new(); + map.insert(&name2, &BigNum(1)); + map.insert(&name1, &BigNum(1)); + map.insert(&name3, &BigNum(1)); + + assert_eq!(map.keys(), AssetNames(vec![name3, name1, name2])); + + let mut map2 = MintAssets::new(); + map2.insert(&name11, Int::new_i32(1)).expect("insert failed"); + map2.insert(&name33, Int::new_i32(1)).expect("insert failed"); + map2.insert(&name22, Int::new_i32(1)).expect("insert failed"); + + assert_eq!(map2.keys(), AssetNames(vec![name33, name11, name22])); +} + +#[test] +fn mint_to_multiasset() { + let policy_id1 = PolicyID::from([0u8; 28]); + let policy_id2 = PolicyID::from([1u8; 28]); + let name1 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + let name2 = AssetName::new(vec![0u8, 4, 5, 6]).unwrap(); + let amount1 = BigNum::from_str("1234").unwrap(); + let amount2 = BigNum::from_str("5678").unwrap(); + + let mut mass1 = MintAssets::new(); + mass1.insert(&name1, Int::new(&amount1)).expect("insert failed"); + mass1.insert(&name2, Int::new(&amount2)).expect("insert failed"); + + let mut mass2 = MintAssets::new(); + mass2.insert(&name1, Int::new(&amount2)).expect("insert failed"); + mass2.insert(&name2, Int::new(&amount1)).expect("insert failed"); + + let mut mint = Mint::new(); + mint.insert(&policy_id1, &mass1); + mint.insert(&policy_id2, &mass2); + + let multiasset = mint.as_positive_multiasset(); + assert_eq!(multiasset.len(), 2); + + let ass1 = multiasset.get(&policy_id1).unwrap(); + let ass2 = multiasset.get(&policy_id2).unwrap(); + + assert_eq!(ass1.len(), 2); + assert_eq!(ass2.len(), 2); + + assert_eq!(ass1.get(&name1).unwrap(), amount1); + assert_eq!(ass1.get(&name2).unwrap(), amount2); + + assert_eq!(ass2.get(&name1).unwrap(), amount2); + assert_eq!(ass2.get(&name2).unwrap(), amount1); +} + +#[test] +fn mint_to_negative_multiasset() { + let policy_id1 = PolicyID::from([0u8; 28]); + let policy_id2 = PolicyID::from([1u8; 28]); + let name1 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + let name2 = AssetName::new(vec![0u8, 4, 5, 6]).unwrap(); + let amount1 = BigNum::from_str("1234").unwrap(); + let amount2 = BigNum::from_str("5678").unwrap(); + + let mut mass1 = MintAssets::new(); + mass1.insert(&name1, Int::new(&amount1)).expect("insert failed"); + mass1.insert(&name2, Int::new_negative(&amount2)).expect("insert failed"); + + let mut mass2 = MintAssets::new(); + mass2.insert(&name1, Int::new_negative(&amount1)).expect("insert failed"); + mass2.insert(&name2, Int::new(&amount2)).expect("insert failed"); + + let mut mint = Mint::new(); + mint.insert(&policy_id1, &mass1); + mint.insert(&policy_id2, &mass2); + + let p_multiasset = mint.as_positive_multiasset(); + let n_multiasset = mint.as_negative_multiasset(); + + assert_eq!(p_multiasset.len(), 2); + assert_eq!(n_multiasset.len(), 2); + + let p_ass1 = p_multiasset.get(&policy_id1).unwrap(); + let p_ass2 = p_multiasset.get(&policy_id2).unwrap(); + + let n_ass1 = n_multiasset.get(&policy_id1).unwrap(); + let n_ass2 = n_multiasset.get(&policy_id2).unwrap(); + + assert_eq!(p_ass1.len(), 1); + assert_eq!(p_ass2.len(), 1); + assert_eq!(n_ass1.len(), 1); + assert_eq!(n_ass2.len(), 1); + + assert_eq!(p_ass1.get(&name1).unwrap(), amount1); + assert!(p_ass1.get(&name2).is_none()); + + assert!(p_ass2.get(&name1).is_none()); + assert_eq!(p_ass2.get(&name2).unwrap(), amount2); + + assert!(n_ass1.get(&name1).is_none()); + assert_eq!(n_ass1.get(&name2).unwrap(), amount2); + + assert_eq!(n_ass2.get(&name1).unwrap(), amount1); + assert!(n_ass2.get(&name2).is_none()); +} + +#[test] +fn mint_to_negative_multiasset_empty() { + let policy_id1 = PolicyID::from([0u8; 28]); + let name1 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); + let amount1 = BigNum::from_str("1234").unwrap(); + + let mut mass1 = MintAssets::new(); + mass1.insert(&name1, Int::new(&amount1)).expect("insert failed"); + + let mut mass2 = MintAssets::new(); + mass2.insert(&name1, Int::new_negative(&amount1)).expect("insert failed"); + + let mut mint1 = Mint::new(); + mint1.insert(&policy_id1, &mass1); + + let mut mint2 = Mint::new(); + mint2.insert(&policy_id1, &mass2); + + let p_multiasset_some = mint1.as_positive_multiasset(); + let p_multiasset_none = mint2.as_positive_multiasset(); + + let n_multiasset_none = mint1.as_negative_multiasset(); + let n_multiasset_some = mint2.as_negative_multiasset(); + + assert_eq!(p_multiasset_some.len(), 1); + assert_eq!(p_multiasset_none.len(), 0); + + assert_eq!(n_multiasset_some.len(), 1); + assert_eq!(n_multiasset_none.len(), 0); + + let p_ass = p_multiasset_some.get(&policy_id1).unwrap(); + let n_ass = n_multiasset_some.get(&policy_id1).unwrap(); + + assert_eq!(p_ass.len(), 1); + assert_eq!(n_ass.len(), 1); + + assert_eq!(p_ass.get(&name1).unwrap(), amount1); + assert_eq!(n_ass.get(&name1).unwrap(), amount1); +} + +fn keyhash(x: u8) -> Ed25519KeyHash { + Ed25519KeyHash::from_bytes(vec![ + x, 180, 186, 93, 223, 42, 243, 7, 81, 98, 86, 125, 97, 69, 110, 52, 130, 243, 244, 98, 246, + 13, 33, 212, 128, 168, 136, 40, + ]) + .unwrap() +} + +fn pkscript(pk: &Ed25519KeyHash) -> NativeScript { + NativeScript::new_script_pubkey(&ScriptPubkey::new(pk)) +} + +fn scripts_vec(scripts: Vec<&NativeScript>) -> NativeScripts { + NativeScripts(scripts.iter().map(|s| (*s).clone()).collect()) +} + +#[test] +fn native_scripts_get_pubkeys() { + let keyhash1 = keyhash(1); + let keyhash2 = keyhash(2); + let keyhash3 = keyhash(3); + + let pks1 = Ed25519KeyHashes::from(&pkscript(&keyhash1)); + assert_eq!(pks1.len(), 1); + assert!(pks1.contains(&keyhash1)); + + let pks2 = + Ed25519KeyHashes::from(&NativeScript::new_timelock_start(&TimelockStart::new(123))); + assert_eq!(pks2.len(), 0); + + let pks3 = Ed25519KeyHashes::from(&NativeScript::new_script_all(&ScriptAll::new( + &scripts_vec(vec![&pkscript(&keyhash1), &pkscript(&keyhash2)]), + ))); + assert_eq!(pks3.len(), 2); + assert!(pks3.contains(&keyhash1)); + assert!(pks3.contains(&keyhash2)); + + let pks4 = Ed25519KeyHashes::from(&NativeScript::new_script_any(&ScriptAny::new( + &scripts_vec(vec![ + &NativeScript::new_script_n_of_k(&ScriptNOfK::new( + 1, + &scripts_vec(vec![ + &NativeScript::new_timelock_start(&TimelockStart::new(132)), + &pkscript(&keyhash3), + ]), + )), + &NativeScript::new_script_all(&ScriptAll::new(&scripts_vec(vec![ + &NativeScript::new_timelock_expiry(&TimelockExpiry::new(132)), + &pkscript(&keyhash1), + ]))), + &NativeScript::new_script_any(&ScriptAny::new(&scripts_vec(vec![ + &pkscript(&keyhash1), + &pkscript(&keyhash2), + &pkscript(&keyhash3), + ]))), + ]), + ))); + assert_eq!(pks4.len(), 3); + assert!(pks4.contains(&keyhash1)); + assert!(pks4.contains(&keyhash2)); + assert!(pks4.contains(&keyhash3)); +} + +#[test] +fn protocol_params_update_cbor_json_roundtrip() { + let mut orig_ppu = ProtocolParamUpdate::new(); + orig_ppu.set_max_tx_size(1234); + orig_ppu.set_max_block_body_size(5678); + orig_ppu.set_max_block_header_size(91011); + orig_ppu.set_minfee_a(&Coin::from(1u32)); + orig_ppu.set_minfee_b(&Coin::from(2u32)); + orig_ppu.set_key_deposit(&Coin::from(3u32)); + orig_ppu.set_pool_deposit(&Coin::from(4u32)); + orig_ppu.set_max_epoch(5); + orig_ppu.set_n_opt(6); + orig_ppu.set_pool_pledge_influence(&UnitInterval::new(&BigNum::from(7u32), &BigNum::from(77u32))); + orig_ppu.set_expansion_rate(&UnitInterval::new(&BigNum::from(8u32), &BigNum::from(9u32))); + orig_ppu.set_treasury_growth_rate(&UnitInterval::new( + &BigNum::from(10u32), + &BigNum::from(11u32), + )); + orig_ppu.set_protocol_version(&ProtocolVersion::new(12u32, 13u32)); + orig_ppu.set_min_pool_cost(&Coin::from(14u32)); + orig_ppu.set_ada_per_utxo_byte(&Coin::from(15u32)); + orig_ppu.set_cost_models(&TxBuilderConstants::plutus_vasil_cost_models()); + orig_ppu.set_execution_costs(&ExUnitPrices::new( + &SubCoin::new(&BigNum::from(16u32), &BigNum::from(17u32)), + &SubCoin::new(&BigNum::from(18u32), &BigNum::from(19u32)), + )); + orig_ppu.set_max_tx_ex_units(&ExUnits::new(&BigNum::from(20u32), &BigNum::from(21u32))); + orig_ppu.set_max_block_ex_units(&ExUnits::new(&BigNum::from(22u32), &BigNum::from(23u32))); + orig_ppu.set_max_value_size(24); + orig_ppu.set_collateral_percentage(25); + orig_ppu.set_max_collateral_inputs(25); + orig_ppu.set_pool_voting_thresholds(&PoolVotingThresholds::new( + &UnitInterval::new(&BigNum::from(26u32), &BigNum::from(27u32)), + &UnitInterval::new(&BigNum::from(28u32), &BigNum::from(29u32)), + &UnitInterval::new(&BigNum::from(30u32), &BigNum::from(31u32)), + &UnitInterval::new(&BigNum::from(40u32), &BigNum::from(41u32)), + &UnitInterval::new(&BigNum::from(50u32), &BigNum::from(51u32)), + )); + orig_ppu.set_drep_voting_thresholds(&DRepVotingThresholds::new( + &UnitInterval::new(&BigNum::from(26u32), &BigNum::from(27u32)), + &UnitInterval::new(&BigNum::from(28u32), &BigNum::from(29u32)), + &UnitInterval::new(&BigNum::from(30u32), &BigNum::from(31u32)), + &UnitInterval::new(&BigNum::from(40u32), &BigNum::from(41u32)), + &UnitInterval::new(&BigNum::from(50u32), &BigNum::from(51u32)), + &UnitInterval::new(&BigNum::from(60u32), &BigNum::from(61u32)), + &UnitInterval::new(&BigNum::from(66u32), &BigNum::from(65u32)), + &UnitInterval::new(&BigNum::from(70u32), &BigNum::from(71u32)), + &UnitInterval::new(&BigNum::from(77u32), &BigNum::from(75u32)), + &UnitInterval::new(&BigNum::from(80u32), &BigNum::from(81u32)), + )); + orig_ppu.set_min_committee_size(32); + orig_ppu.set_committee_term_limit(33); + orig_ppu.set_governance_action_validity_period(34); + orig_ppu.set_governance_action_deposit(&Coin::from(35u32)); + orig_ppu.set_drep_deposit(&Coin::from(36u32)); + orig_ppu.set_drep_inactivity_period(37); + orig_ppu.set_ref_script_coins_per_byte(&UnitInterval::new(&BigNum::from(38u32), &BigNum::from(39u32))); + + let encoded_cbor = orig_ppu.to_bytes(); + let decoded_from_cbor = ProtocolParamUpdate::from_bytes(encoded_cbor).unwrap(); + + assert_eq!(decoded_from_cbor, orig_ppu); + assert_eq!(decoded_from_cbor.to_bytes(), orig_ppu.to_bytes()); + + let encoded_json = orig_ppu.to_json().unwrap(); + let decoded_from_json = ProtocolParamUpdate::from_json(&encoded_json).unwrap(); + + assert_eq!(decoded_from_json, orig_ppu); + assert_eq!(decoded_from_json.to_json().unwrap(), orig_ppu.to_json().unwrap()); +} + +#[test] +fn witnesses_deduplication_test(){ + let spend = tests::fakes::fake_root_key_15() + .derive(harden(1854)) + .derive(harden(1815)) + .derive(harden(0)) + .derive(0) + .derive(0) + .to_public(); + + let spending_hash = spend.to_raw_key().hash(); + + let native_scripts_1 = NativeScript::new_script_pubkey(&ScriptPubkey::new( + &spending_hash, + )); + + let mut internal_scripts = NativeScripts::new(); + internal_scripts.add(&native_scripts_1); + let native_scripts_2 = NativeScript::new_script_n_of_k(&ScriptNOfK::new( + 1, + &internal_scripts, + )); + + + let native_scripts_1_1 = native_scripts_1.clone(); + + let mut native_scripts = NativeScripts::new(); + native_scripts.add(&native_scripts_1); + native_scripts.add(&native_scripts_2); + native_scripts.add(&native_scripts_1_1); + + + // recall: this includes keys for input, certs and withdrawals + let vkey_witness_1 = fake_vkey_witness(1); + let vkey_witness_1_1 = fake_vkey_witness(1); + let vkey_witness_2 = fake_vkey_witness(2); + + let mut vkey_witnesses = Vkeywitnesses::new(); + vkey_witnesses.add(&vkey_witness_1); + vkey_witnesses.add(&vkey_witness_1_1); + vkey_witnesses.add(&vkey_witness_2); + + let plutus_v1_1 = fake_plutus_script(1, &Language::new_plutus_v1()); + let plutus_v1_1_1 = fake_plutus_script(1, &Language::new_plutus_v1()); + let plutus_v1_2 = fake_plutus_script(2, &Language::new_plutus_v1()); + + let plutus_v2_1 = fake_plutus_script(1, &Language::new_plutus_v2()); + let plutus_v2_1_1 = fake_plutus_script(1, &Language::new_plutus_v2()); + let plutus_v2_2 = fake_plutus_script(2, &Language::new_plutus_v2()); + + let plutus_v3_1 = fake_plutus_script(1, &Language::new_plutus_v3()); + let plutus_v3_1_1 = fake_plutus_script(1, &Language::new_plutus_v3()); + let plutus_v3_2 = fake_plutus_script(2, &Language::new_plutus_v3()); + + let mut plutus_scripts = PlutusScripts::new(); + plutus_scripts.add(&plutus_v1_1); + plutus_scripts.add(&plutus_v1_1_1); + plutus_scripts.add(&plutus_v1_2); + plutus_scripts.add(&plutus_v2_1); + plutus_scripts.add(&plutus_v2_1_1); + plutus_scripts.add(&plutus_v2_2); + plutus_scripts.add(&plutus_v3_1); + plutus_scripts.add(&plutus_v3_1_1); + plutus_scripts.add(&plutus_v3_2); + + let mut datums = PlutusList::new(); + + let datum_1 = PlutusData::new_integer(&BigInt::from(1)); + let datum_1_1 = PlutusData::new_integer(&BigInt::from(1)); + let datum_2 = PlutusData::new_integer(&BigInt::from(2)); + datums.add(&datum_1); + datums.add(&datum_1_1); + datums.add(&datum_2); + + let mut tx_wits_set = TransactionWitnessSet::new(); + tx_wits_set.set_vkeys(&vkey_witnesses); + tx_wits_set.set_native_scripts(&native_scripts); + tx_wits_set.set_plutus_scripts(&plutus_scripts); + tx_wits_set.set_plutus_data(&datums); + + let roundtrip_tx_wits_set = TransactionWitnessSet::from_bytes(tx_wits_set.to_bytes()).unwrap(); + let roundtrip_vkeys = roundtrip_tx_wits_set.vkeys().unwrap(); + assert_eq!(roundtrip_vkeys.len(), 2); + assert_eq!(roundtrip_vkeys.get(0), vkey_witness_1); + assert_eq!(roundtrip_vkeys.get(1), vkey_witness_2); + + let roundtrip_native_scripts = roundtrip_tx_wits_set.native_scripts().unwrap(); + assert_eq!(roundtrip_native_scripts.len(), 2); + assert_eq!(roundtrip_native_scripts.get(0), native_scripts_1); + assert_eq!(roundtrip_native_scripts.get(1), native_scripts_2); + + let roundtrip_plutus_scripts = roundtrip_tx_wits_set.plutus_scripts().unwrap(); + assert_eq!(roundtrip_plutus_scripts.len(), 6); + assert_eq!(roundtrip_plutus_scripts.get(0), plutus_v1_1); + assert_eq!(roundtrip_plutus_scripts.get(1), plutus_v1_2); + assert_eq!(roundtrip_plutus_scripts.get(2), plutus_v2_1); + assert_eq!(roundtrip_plutus_scripts.get(3), plutus_v2_2); + assert_eq!(roundtrip_plutus_scripts.get(4), plutus_v3_1); + assert_eq!(roundtrip_plutus_scripts.get(5), plutus_v3_2); + + let roundtrip_plutus_data = roundtrip_tx_wits_set.plutus_data().unwrap(); + assert_eq!(roundtrip_plutus_data.len(), 2); + assert_eq!(roundtrip_plutus_data.get(0), datum_1); + assert_eq!(roundtrip_plutus_data.get(1), datum_2); +} + +#[test] +fn min_ref_script_fee_test(){ + let cost = UnitInterval::new(&BigNum::from(1u32), &BigNum::from(2u32)); + let total_size = 500; + let min_fee = min_ref_script_fee(total_size, &cost).unwrap(); + assert_eq!(min_fee, BigNum(250)); +} + +#[test] +fn min_ref_script_fee_test_fail(){ + let cost = UnitInterval::new(&BigNum::from(1u32), &BigNum::from(0u32)); + let total_size = 500; + let min_fee = min_ref_script_fee(total_size, &cost); + assert!(min_fee.is_err()); +} + +#[test] +fn ed25519_key_hashes_dedup() { + let mut key_hashes = Ed25519KeyHashes::new(); + let key_hash1 = keyhash(1); + let key_hash2 = keyhash(2); + + assert!(key_hashes.add(&key_hash1)); + assert!(key_hashes.add(&key_hash2)); + assert_eq!(key_hashes.len(), 2); + + assert!(!key_hashes.add(&key_hash1)); + assert_eq!(key_hashes.len(), 2); +} + +#[test] +fn bootstrap_witnesses_dedup() { + let mut bootstrap_witnesses = BootstrapWitnesses::new(); + let bootstrap_witness1 = fake_boostrap_witness(1); + let bootstrap_witness2 = fake_boostrap_witness(2); + + assert!(bootstrap_witnesses.add(&bootstrap_witness1)); + assert!(bootstrap_witnesses.add(&bootstrap_witness2)); + assert_eq!(bootstrap_witnesses.len(), 2); + + assert!(!bootstrap_witnesses.add(&bootstrap_witness1)); + assert_eq!(bootstrap_witnesses.len(), 2); +} + +#[test] +fn credential_dedup() { + let mut credentials = Credentials::new(); + let credential1 = Credential::from_keyhash(&keyhash(1)); + let credential2 = Credential::from_keyhash(&keyhash(2)); + + assert!(credentials.add(&credential1)); + assert!(credentials.add(&credential2)); + assert_eq!(credentials.len(), 2); + + assert!(!credentials.add(&credential1)); + assert_eq!(credentials.len(), 2); +} + +#[test] +fn vkeywitneses_dedup() { + let mut vkeywitnesses = Vkeywitnesses::new(); + let vkeywitness1 = fake_vkey_witness(1); + let vkeywitness2 = fake_vkey_witness(2); + + assert!(vkeywitnesses.add(&vkeywitness1)); + assert!(vkeywitnesses.add(&vkeywitness2)); + assert_eq!(vkeywitnesses.len(), 2); + + assert!(!vkeywitnesses.add(&vkeywitness1)); + assert_eq!(vkeywitnesses.len(), 2); +} + +#[test] +fn plutus_scripts_dedup_on_tx_witnesses_set() { + let plutus_script_v1_1 = fake_plutus_script(1, &Language::new_plutus_v1()); + let plutus_script_v1_2 = fake_plutus_script(2, &Language::new_plutus_v1()); + + let plutus_script_v2_1 = fake_plutus_script(1, &Language::new_plutus_v2()); + let plutus_script_v2_2 = fake_plutus_script(2, &Language::new_plutus_v2()); + + let plutus_script_v3_1 = fake_plutus_script(1, &Language::new_plutus_v3()); + let plutus_script_v3_2 = fake_plutus_script(2, &Language::new_plutus_v3()); + + let mut plutus_scrips = PlutusScripts::new(); + plutus_scrips.add(&plutus_script_v1_1); + plutus_scrips.add(&plutus_script_v1_2); + plutus_scrips.add(&plutus_script_v1_1); + + plutus_scrips.add(&plutus_script_v2_1); + plutus_scrips.add(&plutus_script_v2_2); + plutus_scrips.add(&plutus_script_v2_1); + + plutus_scrips.add(&plutus_script_v3_1); + plutus_scrips.add(&plutus_script_v3_2); + plutus_scrips.add(&plutus_script_v3_1); + assert_eq!(plutus_scrips.len(), 9); + + let mut tx_wit_set = TransactionWitnessSet::new(); + tx_wit_set.set_plutus_scripts(&plutus_scrips); + + let plutus_scripts_from = tx_wit_set.plutus_scripts().unwrap(); + assert_eq!(plutus_scripts_from.len(), 6); + assert!(plutus_scripts_from.contains(&plutus_script_v1_1)); + assert!(plutus_scripts_from.contains(&plutus_script_v1_2)); + assert!(plutus_scripts_from.contains(&plutus_script_v2_1)); + assert!(plutus_scripts_from.contains(&plutus_script_v2_2)); + assert!(plutus_scripts_from.contains(&plutus_script_v3_1)); + assert!(plutus_scripts_from.contains(&plutus_script_v3_2)); + + let tx_wit_set_bytes = tx_wit_set.to_bytes(); + let tx_wit_set_from_bytes = TransactionWitnessSet::from_bytes(tx_wit_set_bytes).unwrap(); + let plutus_scripts_from_bytes = tx_wit_set_from_bytes.plutus_scripts().unwrap(); + assert_eq!(plutus_scripts_from_bytes.len(), 6); + assert!(plutus_scripts_from_bytes.contains(&plutus_script_v1_1)); + assert!(plutus_scripts_from_bytes.contains(&plutus_script_v1_2)); + assert!(plutus_scripts_from_bytes.contains(&plutus_script_v2_1)); + assert!(plutus_scripts_from_bytes.contains(&plutus_script_v2_2)); + assert!(plutus_scripts_from_bytes.contains(&plutus_script_v3_1)); + assert!(plutus_scripts_from_bytes.contains(&plutus_script_v3_2)); +} + +#[test] +fn plutus_scripts_no_dedup_on_auxdata() { + let plutus_script_v1_1 = fake_plutus_script(1, &Language::new_plutus_v1()); + let plutus_script_v1_2 = fake_plutus_script(2, &Language::new_plutus_v1()); + + let plutus_script_v2_1 = fake_plutus_script(1, &Language::new_plutus_v2()); + let plutus_script_v2_2 = fake_plutus_script(2, &Language::new_plutus_v2()); + + let plutus_script_v3_1 = fake_plutus_script(1, &Language::new_plutus_v3()); + let plutus_script_v3_2 = fake_plutus_script(2, &Language::new_plutus_v3()); + + let mut plutus_scrips = PlutusScripts::new(); + plutus_scrips.add(&plutus_script_v1_1); + plutus_scrips.add(&plutus_script_v1_2); + plutus_scrips.add(&plutus_script_v1_1); + + plutus_scrips.add(&plutus_script_v2_1); + plutus_scrips.add(&plutus_script_v2_2); + plutus_scrips.add(&plutus_script_v2_1); + + plutus_scrips.add(&plutus_script_v3_1); + plutus_scrips.add(&plutus_script_v3_2); + plutus_scrips.add(&plutus_script_v3_1); + assert_eq!(plutus_scrips.len(), 9); + + let mut aux_data = AuxiliaryData::new(); + aux_data.set_plutus_scripts(&plutus_scrips); + + let plutus_scripts_from = aux_data.plutus_scripts().unwrap(); + assert_eq!(plutus_scripts_from.len(), 9); + assert!(plutus_scripts_from.contains(&plutus_script_v1_1)); + assert!(plutus_scripts_from.contains(&plutus_script_v1_2)); + assert!(plutus_scripts_from.contains(&plutus_script_v2_1)); + assert!(plutus_scripts_from.contains(&plutus_script_v2_2)); + assert!(plutus_scripts_from.contains(&plutus_script_v3_1)); + assert!(plutus_scripts_from.contains(&plutus_script_v3_2)); + + let aux_data_bytes = aux_data.to_bytes(); + let aux_data_from_bytes = AuxiliaryData::from_bytes(aux_data_bytes).unwrap(); + let plutus_scripts_from_bytes = aux_data_from_bytes.plutus_scripts().unwrap(); + assert_eq!(plutus_scripts_from_bytes.len(), 9); + assert!(plutus_scripts_from_bytes.contains(&plutus_script_v1_1)); + assert!(plutus_scripts_from_bytes.contains(&plutus_script_v1_2)); + assert!(plutus_scripts_from_bytes.contains(&plutus_script_v2_1)); + assert!(plutus_scripts_from_bytes.contains(&plutus_script_v2_2)); + assert!(plutus_scripts_from_bytes.contains(&plutus_script_v3_1)); + assert!(plutus_scripts_from_bytes.contains(&plutus_script_v3_2)); +} + +#[test] +fn native_scripts_dedup_on_tx_witnesses_set() { + let keyhash1 = keyhash(1); + + let native_scripts_1 = NativeScript::new_script_pubkey(&ScriptPubkey::new( + &keyhash1, + )); + + let mut internal_scripts = NativeScripts::new(); + internal_scripts.add(&native_scripts_1); + let native_scripts_2 = NativeScript::new_script_n_of_k(&ScriptNOfK::new( + 1, + &internal_scripts, + )); + + let mut native_scripts = NativeScripts::new(); + native_scripts.add(&native_scripts_1); + native_scripts.add(&native_scripts_2); + native_scripts.add(&native_scripts_1); + assert_eq!(native_scripts.len(), 3); + + let mut tx_wit_set = TransactionWitnessSet::new(); + tx_wit_set.set_native_scripts(&native_scripts); + + let native_scripts_from = tx_wit_set.native_scripts().unwrap(); + assert_eq!(native_scripts_from.len(), 2); + assert!(native_scripts_from.contains(&native_scripts_1)); + assert!(native_scripts_from.contains(&native_scripts_2)); + + let tx_wit_set_bytes = tx_wit_set.to_bytes(); + let tx_wit_set_from_bytes = TransactionWitnessSet::from_bytes(tx_wit_set_bytes).unwrap(); + let native_scripts_from_bytes = tx_wit_set_from_bytes.native_scripts().unwrap(); + assert_eq!(native_scripts_from_bytes.len(), 2); + assert!(native_scripts_from_bytes.contains(&native_scripts_1)); + assert!(native_scripts_from_bytes.contains(&native_scripts_2)); +} + +#[test] +fn native_scripts_no_dedup_on_auxdata() { + let keyhash1 = keyhash(1); + + let native_scripts_1 = NativeScript::new_script_pubkey(&ScriptPubkey::new( + &keyhash1, + )); + + let mut internal_scripts = NativeScripts::new(); + internal_scripts.add(&native_scripts_1); + let native_scripts_2 = NativeScript::new_script_n_of_k(&ScriptNOfK::new( + 1, + &internal_scripts, + )); + + let mut native_scripts = NativeScripts::new(); + native_scripts.add(&native_scripts_1); + native_scripts.add(&native_scripts_2); + native_scripts.add(&native_scripts_1); + assert_eq!(native_scripts.len(), 3); + + let mut aux_data = AuxiliaryData::new(); + aux_data.set_native_scripts(&native_scripts); + + let native_scripts_from = aux_data.native_scripts().unwrap(); + assert_eq!(native_scripts_from.len(), 3); + assert!(native_scripts_from.contains(&native_scripts_1)); + assert!(native_scripts_from.contains(&native_scripts_2)); + + let aux_data_bytes = aux_data.to_bytes(); + let aux_data_from_bytes = AuxiliaryData::from_bytes(aux_data_bytes).unwrap(); + let native_scripts_from_bytes = aux_data_from_bytes.native_scripts().unwrap(); + assert_eq!(native_scripts_from_bytes.len(), 3); + assert!(native_scripts_from_bytes.contains(&native_scripts_1)); + assert!(native_scripts_from_bytes.contains(&native_scripts_2)); +} + +#[test] +fn plutus_data_dedup_on_tx_witnesses_set() { + let datum_1 = PlutusData::new_integer(&BigInt::from(1)); + let datum_2 = PlutusData::new_integer(&BigInt::from(2)); + + let mut datum = PlutusList::new(); + datum.add(&datum_1); + datum.add(&datum_2); + datum.add(&datum_1); + assert_eq!(datum.len(), 3); + + let mut tx_wit_set = TransactionWitnessSet::new(); + tx_wit_set.set_plutus_data(&datum); + + let datums_from = tx_wit_set.plutus_data().unwrap(); + assert_eq!(datums_from.len(), 2); + assert!(datums_from.contains(&datum_1)); + assert!(datums_from.contains(&datum_2)); + + let tx_wit_set_bytes = tx_wit_set.to_bytes(); + let tx_wit_set_from_bytes = TransactionWitnessSet::from_bytes(tx_wit_set_bytes).unwrap(); + let datums_from_bytes = tx_wit_set_from_bytes.plutus_data().unwrap(); + assert_eq!(datums_from_bytes.len(), 2); + assert!(datums_from_bytes.contains(&datum_1)); + assert!(datums_from_bytes.contains(&datum_2)); +} + +#[test] +fn plutus_data_no_dedup_serialization() { + let datum_1 = PlutusData::new_integer(&BigInt::from(1)); + let datum_2 = PlutusData::new_integer(&BigInt::from(2)); + + let mut datum = PlutusList::new(); + datum.add(&datum_1); + datum.add(&datum_2); + datum.add(&datum_1); + assert_eq!(datum.len(), 3); + + let datum_bytes = datum.to_bytes(); + let datum_from_bytes = PlutusList::from_bytes(datum_bytes).unwrap(); + assert_eq!(datum_from_bytes.len(), 3); + assert!(datum_from_bytes.contains(&datum_1)); + assert!(datum_from_bytes.contains(&datum_2)); +} + +#[test] +fn tx_inputs_deduplication() { + let tx_in1 = fake_tx_input(1); + let tx_in2 = fake_tx_input(2); + let tx_in3 = fake_tx_input(3); + + let mut txins = TransactionInputs::new(); + assert!(txins.add(&tx_in1)); + assert!(txins.add(&tx_in2)); + assert!(txins.add(&tx_in3)); + assert!(!txins.add(&tx_in1)); + + assert_eq!(txins.len(), 3); + + let txins_bytes = txins.to_bytes(); + let txins_from_bytes = TransactionInputs::from_bytes(txins_bytes).unwrap(); + assert_eq!(txins_from_bytes.len(), 3); + assert!(txins_from_bytes.contains(&tx_in1)); + assert!(txins_from_bytes.contains(&tx_in2)); + assert!(txins_from_bytes.contains(&tx_in3)); +} + +// Helper function to create a UnitInterval from a fraction +fn new_uinternal(numerator: u64, denominator: u64) -> UnitInterval { + UnitInterval::new(&BigNum::from(numerator), &BigNum::from(denominator)) +} + +#[test] +fn min_ref_script_fee_zero_size_test() { + let result = min_ref_script_fee(0, &new_uinternal(1, 1000)).unwrap(); + assert_eq!(result.to_str(), "0"); +} + +#[test] +fn min_ref_script_fee_small_size_test() { + let result = min_ref_script_fee(1000, &new_uinternal(1, 1000)).unwrap(); + assert_eq!(result.to_str(), "1"); +} + +#[test] +fn min_ref_script_fee_exactly_one_tier_test() { + let result = min_ref_script_fee(25600, &new_uinternal(1, 1000)).unwrap(); + assert_eq!(result.to_str(), "25"); +} + +#[test] +fn min_ref_script_fee_multiple_full_tiers_test() { + let result = min_ref_script_fee(25600 * 2, &new_uinternal(1, 1000)).unwrap(); + let expected = ((25600f64 / 1000f64) + (25600f64 * 0.0012f64)) as u64; + assert_eq!(result, BigNum(expected)); +} + +#[test] +fn min_ref_script_fee_partial_tier_test() { + let result = min_ref_script_fee(30000, &new_uinternal(1, 1000)).unwrap(); + assert_eq!(result.to_str(), "30"); +} + +#[test] +fn min_ref_script_fee_large_size_test() { + let result = min_ref_script_fee(1000000, &new_uinternal(1, 1000)).unwrap(); + assert_eq!(result.to_str(), "158607"); +} + +#[test] +fn min_ref_script_fee_different_cost_per_byte_test() { + let result = min_ref_script_fee(50000, &new_uinternal(5, 1000)).unwrap(); + assert_eq!(result.to_str(), "274"); +} + +#[test] +fn min_ref_script_fee_one_cost_per_byte_test() { + let result = min_ref_script_fee(10000, &new_uinternal(1, 1)).unwrap(); + assert_eq!(result.to_str(), "10000"); +} + +#[test] +fn min_ref_script_fee_zero_cost_per_byte_test() { + let fee = min_ref_script_fee(10000, &new_uinternal(0, 1)).unwrap(); + assert_eq!(fee.to_str(), "0"); +} + +#[test] +fn test_multiple_tiers() { + // Test cases with different numbers of tiers + let test_cases = [ + (25600, "25"), // Exactly 1 tier + (25601, "25"), // 1 full tier + 1 byte (at 1.2x price) + (51200, "56"), // Exactly 2 tiers + (76800, "93"), // Exactly 3 tiers + (80000, "98"), // 3 full tiers + partial tier + (100000, "133"), // 3 full tiers + larger partial tier + (128000, "190"), // Exactly 5 tiers + (179200, "330"), // 7 full tiers + ]; + + for (size, expected) in test_cases.iter() { + let result = min_ref_script_fee(*size, &new_uinternal(1, 1000)).unwrap(); + assert_eq!(result.to_str(), *expected, "Failed for size {}", size); + } +} + +#[test] +fn plutus_map_keys_duplication_test() { + let mut map = PlutusMap::new(); + let key1 = PlutusData::new_integer(&BigInt::from(1)); + let key2 = PlutusData::new_integer(&BigInt::from(2)); + let value1 = PlutusData::new_integer(&BigInt::from(3)); + let value2 = PlutusData::new_integer(&BigInt::from(4)); + let value3 = PlutusData::new_integer(&BigInt::from(5)); + + assert_eq!(map.len(), 0); + assert_eq!(map.total_len(), 0); + + let mut plutus_map_value1 = PlutusMapValues::new(); + plutus_map_value1.add(&value1); + plutus_map_value1.add(&value2); + + let mut plutus_map_value2 = PlutusMapValues::new(); + plutus_map_value2.add(&value3); + + map.insert(&key1, &plutus_map_value1); + map.insert(&key2, &plutus_map_value2); + + assert_eq!(map.len(), 2); + assert_eq!(map.total_len(), 3); + + let map_bytes = map.to_bytes(); + let map_from_bytes = PlutusMap::from_bytes(map_bytes).unwrap(); + assert_eq!(map_from_bytes.len(), 2); + assert_eq!(map_from_bytes.total_len(), 3); + + assert_eq!(map, map_from_bytes) +} \ No newline at end of file diff --git a/rust/src/tests/helpers.rs b/rust/src/tests/helpers.rs new file mode 100644 index 00000000..c61ff8a2 --- /dev/null +++ b/rust/src/tests/helpers.rs @@ -0,0 +1,3 @@ +pub(crate) fn harden(index: u32) -> u32 { + index | 0x80_00_00_00 +} diff --git a/rust/src/tests/metadata.rs b/rust/src/tests/metadata.rs new file mode 100644 index 00000000..6bcce071 --- /dev/null +++ b/rust/src/tests/metadata.rs @@ -0,0 +1,281 @@ +use crate::*; + +#[test] +fn binary_encoding() { + let input_bytes = (0..1000).map(|x| x as u8).collect::>(); + let metadata = encode_arbitrary_bytes_as_metadatum(input_bytes.as_ref()); + let output_bytes = decode_arbitrary_bytes_from_metadatum(&metadata).expect("decode failed"); + assert_eq!(input_bytes, output_bytes); +} + +#[test] +fn json_encoding_no_conversions() { + let input_str = String::from("{\"receiver_id\": \"SJKdj34k3jjKFDKfjFUDfdjkfd\",\"sender_id\": \"jkfdsufjdk34h3Sdfjdhfduf873\",\"comment\": \"happy birthday\",\"tags\": [0, 264, -1024, 32]}"); + let metadata = + encode_json_str_to_metadatum(input_str.clone(), MetadataJsonSchema::NoConversions) + .expect("encode failed"); + let map = metadata.as_map().unwrap(); + assert_eq!( + map.get_str("receiver_id").unwrap().as_text().unwrap(), + "SJKdj34k3jjKFDKfjFUDfdjkfd" + ); + assert_eq!( + map.get_str("sender_id").unwrap().as_text().unwrap(), + "jkfdsufjdk34h3Sdfjdhfduf873" + ); + assert_eq!( + map.get_str("comment").unwrap().as_text().unwrap(), + "happy birthday" + ); + let tags = map.get_str("tags").unwrap().as_list().unwrap(); + let tags_i32 = tags + .0 + .iter() + .map(|md| md.as_int().unwrap().as_i32_or_fail().unwrap()) + .collect::>(); + assert_eq!(tags_i32, vec![0, 264, -1024, 32]); + let output_str = decode_metadatum_to_json_str(&metadata, MetadataJsonSchema::NoConversions) + .expect("decode failed"); + let input_json: serde_json::Value = serde_json::from_str(&input_str).unwrap(); + let output_json: serde_json::Value = serde_json::from_str(&output_str).unwrap(); + assert_eq!(input_json, output_json); +} + +#[test] +fn json_encoding_basic() { + let input_str = + String::from("{\"0x8badf00d\": \"0xdeadbeef\",\"9\": 5,\"obj\": {\"a\":[{\"5\": 2},{}]}}"); + let metadata = + encode_json_str_to_metadatum(input_str.clone(), MetadataJsonSchema::BasicConversions) + .expect("encode failed"); + json_encoding_check_example_metadatum(&metadata); + let output_str = decode_metadatum_to_json_str(&metadata, MetadataJsonSchema::BasicConversions) + .expect("decode failed"); + let input_json: serde_json::Value = serde_json::from_str(&input_str).unwrap(); + let output_json: serde_json::Value = serde_json::from_str(&output_str).unwrap(); + assert_eq!(input_json, output_json); +} + +#[test] +fn json_encoding_detailed() { + let input_str = String::from( + "{\"map\":[ + { + \"k\":{\"bytes\":\"8badf00d\"}, + \"v\":{\"bytes\":\"deadbeef\"} + }, + { + \"k\":{\"int\":9}, + \"v\":{\"int\":5} + }, + { + \"k\":{\"string\":\"obj\"}, + \"v\":{\"map\":[ + { + \"k\":{\"string\":\"a\"}, + \"v\":{\"list\":[ + {\"map\":[ + { + \"k\":{\"int\":5}, + \"v\":{\"int\":2} + } + ]}, + {\"map\":[ + ]} + ]} + } + ]} + } + ]}", + ); + let metadata = + encode_json_str_to_metadatum(input_str.clone(), MetadataJsonSchema::DetailedSchema) + .expect("encode failed"); + json_encoding_check_example_metadatum(&metadata); + let output_str = decode_metadatum_to_json_str(&metadata, MetadataJsonSchema::DetailedSchema) + .expect("decode failed"); + let input_json: serde_json::Value = serde_json::from_str(&input_str).unwrap(); + let output_json: serde_json::Value = serde_json::from_str(&output_str).unwrap(); + assert_eq!(input_json, output_json); +} + +fn json_encoding_check_example_metadatum(metadata: &TransactionMetadatum) { + let map = metadata.as_map().unwrap(); + assert_eq!( + map.get(&TransactionMetadatum::new_bytes(hex::decode("8badf00d").unwrap()).unwrap()) + .unwrap() + .as_bytes() + .unwrap(), + hex::decode("deadbeef").unwrap() + ); + assert_eq!( + map.get_i32(9) + .unwrap() + .as_int() + .unwrap() + .as_i32_or_fail() + .unwrap(), + 5 + ); + let inner_map = map.get_str("obj").unwrap().as_map().unwrap(); + let a = inner_map.get_str("a").unwrap().as_list().unwrap(); + let a1 = a.get(0).as_map().unwrap(); + assert_eq!( + a1.get_i32(5) + .unwrap() + .as_int() + .unwrap() + .as_i32_or_fail() + .unwrap(), + 2 + ); + let a2 = a.get(1).as_map().unwrap(); + assert_eq!(a2.keys().len(), 0); +} + +#[test] +fn json_encoding_detailed_complex_key() { + let input_str = String::from( + "{\"map\":[ + { + \"k\":{\"list\":[ + {\"map\": [ + { + \"k\": {\"int\": 5}, + \"v\": {\"int\": -7} + }, + { + \"k\": {\"string\": \"hello\"}, + \"v\": {\"string\": \"world\"} + } + ]}, + {\"bytes\": \"ff00ff00\"} + ]}, + \"v\":{\"int\":5} + } + ]}", + ); + let metadata = + encode_json_str_to_metadatum(input_str.clone(), MetadataJsonSchema::DetailedSchema) + .expect("encode failed"); + + let map = metadata.as_map().unwrap(); + let key = map.keys().get(0); + assert_eq!( + map.get(&key) + .unwrap() + .as_int() + .unwrap() + .as_i32_or_fail() + .unwrap(), + 5 + ); + let key_list = key.as_list().unwrap(); + assert_eq!(key_list.len(), 2); + let key_map = key_list.get(0).as_map().unwrap(); + assert_eq!( + key_map + .get_i32(5) + .unwrap() + .as_int() + .unwrap() + .as_i32_or_fail() + .unwrap(), + -7 + ); + assert_eq!( + key_map.get_str("hello").unwrap().as_text().unwrap(), + "world" + ); + let key_bytes = key_list.get(1).as_bytes().unwrap(); + assert_eq!(key_bytes, hex::decode("ff00ff00").unwrap()); + + let output_str = decode_metadatum_to_json_str(&metadata, MetadataJsonSchema::DetailedSchema) + .expect("decode failed"); + let input_json: serde_json::Value = serde_json::from_str(&input_str).unwrap(); + let output_json: serde_json::Value = serde_json::from_str(&output_str).unwrap(); + assert_eq!(input_json, output_json); +} + +#[test] +fn metadata_serialize() { + let mut gmd = GeneralTransactionMetadata::new(); + let mdatum = TransactionMetadatum::new_text(String::from("string md")).unwrap(); + gmd.insert(&BigNum(100), &mdatum); + let mut aux_data = AuxiliaryData::new(); + // alonzo (empty) + let ad0_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); + assert_eq!(aux_data.to_bytes(), ad0_deser.to_bytes()); + // pre-mary shelley + aux_data.set_metadata(&gmd); + let ad1_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); + assert_eq!(aux_data.to_bytes(), ad1_deser.to_bytes()); + // mary shelley + let mut native_scripts = NativeScripts::new(); + native_scripts.add(&NativeScript::new_timelock_start(&TimelockStart::new(20))); + aux_data.set_native_scripts(&native_scripts); + let ad2_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); + assert_eq!(aux_data.to_bytes(), ad2_deser.to_bytes()); + // alonzo + let mut plutus_scripts = PlutusScripts::new(); + plutus_scripts.add(&PlutusScript::new([61u8; 29].to_vec())); + aux_data.set_plutus_scripts(&plutus_scripts); + let ad3_deser = AuxiliaryData::from_bytes(aux_data.to_bytes()).unwrap(); + assert_eq!(aux_data.to_bytes(), ad3_deser.to_bytes()); +} + +#[test] +fn alonzo_metadata_round_trip() { + let bytes_alonzo = hex::decode("d90103a100a1186469737472696e67206d64").unwrap(); + let aux_alonzo = AuxiliaryData::from_bytes(bytes_alonzo.clone()).unwrap(); + assert!(aux_alonzo.prefer_alonzo_format); + assert_eq!(aux_alonzo.to_bytes(), bytes_alonzo); + + let bytes_pre_alonzo = hex::decode("a1186469737472696e67206d64").unwrap(); + let aux_pre_alonzo = AuxiliaryData::from_bytes(bytes_pre_alonzo.clone()).unwrap(); + assert!(!aux_pre_alonzo.prefer_alonzo_format); + assert_eq!(aux_pre_alonzo.to_bytes(), bytes_pre_alonzo); +} + +#[test] +fn metadatum_map_duplicate_keys() { + let bytes = hex::decode("a105a4781b232323232323232323232323232323232323232323232323232323827840232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323237840232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323236e232323232323232323232323232382a36f2323232323232323232323232323236a323030302d30312d303166232323232323784023232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323712323232323232323232323232323232323784023232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323a36f2323232323232323232323232323236a323030302d30312d303166232323232323784023232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323712323232323232323232323232323232323784023232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323752323232323232323232323232323232323232323236a323030302d30312d3031752323232323232323232323232323232323232323236a323030302d30312d3031").unwrap(); + TransactionMetadatum::from_bytes(bytes).unwrap(); +} + +#[test] +fn test_auxiliary_data_roundtrip() { + fn auxiliary_data_roundtrip(plutus_scripts: &PlutusScripts) { + let mut aux = AuxiliaryData::new(); + let mut metadata = GeneralTransactionMetadata::new(); + metadata.insert( + &BigNum(42), + &encode_json_str_to_metadatum( + "{ \"test\": 148 }".to_string(), + MetadataJsonSchema::BasicConversions, + ) + .unwrap(), + ); + aux.set_metadata(&metadata); + aux.set_native_scripts(&NativeScripts::from(vec![ + NativeScript::new_timelock_start(&TimelockStart::new(1234556)), + ])); + aux.set_plutus_scripts(plutus_scripts); + assert_eq!(AuxiliaryData::from_bytes(aux.to_bytes()).unwrap(), aux); + } + + let bytes = hex::decode("4e4d01000033222220051200120011").unwrap(); + let script_v1 = PlutusScript::from_bytes(bytes.clone()).unwrap(); + let script_v2 = PlutusScript::from_bytes_v2(bytes.clone()).unwrap(); + let script_v3 = PlutusScript::from_bytes_v3(bytes.clone()).unwrap(); + + auxiliary_data_roundtrip(&PlutusScripts(vec![])); + auxiliary_data_roundtrip(&PlutusScripts(vec![script_v1.clone()])); + auxiliary_data_roundtrip(&PlutusScripts(vec![script_v2.clone()])); + auxiliary_data_roundtrip(&PlutusScripts(vec![script_v3.clone()])); + auxiliary_data_roundtrip(&PlutusScripts(vec![ + script_v1.clone(), + script_v2.clone(), + script_v3.clone(), + ])); +} diff --git a/rust/src/tests/mod.rs b/rust/src/tests/mod.rs new file mode 100644 index 00000000..e49187ce --- /dev/null +++ b/rust/src/tests/mod.rs @@ -0,0 +1,13 @@ +mod address; +mod builders; +mod general; +mod helpers; +mod fakes; +mod protocol_types; +mod serialization; +mod plutus; +mod metadata; +mod crypto; +mod utils; +mod fees; +mod emip3; \ No newline at end of file diff --git a/rust/src/tests/plutus.rs b/rust/src/tests/plutus.rs new file mode 100644 index 00000000..4e0ae3dc --- /dev/null +++ b/rust/src/tests/plutus.rs @@ -0,0 +1,576 @@ +use crate::*; +use hex::*; + +#[test] +pub fn plutus_constr_data() { + let constr_0 = PlutusData::new_constr_plutus_data(&ConstrPlutusData::new( + &BigNum(0), + &PlutusList::new(), + )); + let constr_0_hash = hex::encode(hash_plutus_data(&constr_0).to_bytes()); + assert_eq!( + constr_0_hash, + "923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44ec" + ); + // let constr_0_roundtrip = PlutusData::from_bytes(constr_0.to_bytes()).unwrap(); + // TODO: do we want semantic equality or bytewise equality? + // assert_eq!(constr_0, constr_0_roundtrip); + // let constr_1854 = PlutusData::new_constr_plutus_data( + // &ConstrPlutusData::new(&BigNum(1854), &PlutusList::new()) + // ); + // let constr_1854_roundtrip = PlutusData::from_bytes(constr_1854.to_bytes()).unwrap(); + // assert_eq!(constr_1854, constr_1854_roundtrip); +} + +#[test] +pub fn plutus_list_serialization_cli_compatibility() { + // mimic cardano-cli array encoding, see https://github.com/Emurgo/cardano-serialization-lib/issues/227 + let datum_cli = "d8799f4100d8799fd8799fd8799f581cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd8799fd8799fd8799f581cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd87a80ff1a002625a0d8799fd879801a000f4240d87a80ffff"; + let datum = PlutusData::from_bytes(Vec::from_hex(datum_cli).unwrap()).unwrap(); + assert_eq!(datum_cli, hex::encode(datum.to_bytes())); + + // encode empty arrays as fixed + assert_eq!("80", hex::encode(PlutusList::new().to_bytes())); + + // encode arrays as indefinite length array + let mut list = PlutusList::new(); + list.add(&PlutusData::new_integer(&BigInt::from_str("1").unwrap())); + assert_eq!("9f01ff", hex::encode(list.to_bytes())); + + // witness_set should have fixed length array + let mut witness_set = TransactionWitnessSet::new(); + witness_set.set_plutus_data(&list); + assert_eq!("a1049f01ff", hex::encode(witness_set.to_bytes())); + + list = PlutusList::new(); + list.add(&datum); + witness_set.set_plutus_data(&list); + assert_eq!( + format!("a1049f{}ff", datum_cli), + hex::encode(witness_set.to_bytes()) + ); +} + +#[test] +pub fn plutus_datums_respect_deserialized_encoding() { + let orig_bytes = Vec::from_hex( + "81d8799f581ce1cbb80db89e292269aeb93ec15eb963dda5176b66949fe1c2a6a38da140a1401864ff", + ) + .unwrap(); + let datums = PlutusList::from_bytes(orig_bytes.clone()).unwrap(); + let new_bytes = datums.to_bytes(); + assert_eq!(orig_bytes, new_bytes); +} + +#[test] +pub fn plutus_datum_from_json_basic() { + let json = "{ + \"5\": \"some utf8 string\", + \"0xDEADBEEF\": [ + {\"reg string\": {}}, + -9 + ] + }"; + + let datum = encode_json_str_to_plutus_datum(json, PlutusDatumSchema::BasicConversions).unwrap(); + + let map = datum.as_map().unwrap(); + let map_5 = map + .get(&PlutusData::new_integer(&BigInt::from_str("5").unwrap())) + .unwrap(); + let utf8_bytes = "some utf8 string".as_bytes(); + assert_eq!(map_5.get(0).unwrap().as_bytes().unwrap(), utf8_bytes); + let map_deadbeef: PlutusList = map + .get(&PlutusData::new_bytes(vec![222, 173, 190, 239])) + .expect("DEADBEEF key not found") + .get(0) + .unwrap() + .as_list() + .expect("must be a map"); + assert_eq!(map_deadbeef.len(), 2); + let inner_map = map_deadbeef.get(0).as_map().unwrap(); + assert_eq!(inner_map.len(), 1); + let reg_string = inner_map + .get(&PlutusData::new_bytes("reg string".as_bytes().to_vec())) + .unwrap() + .get(0) + .unwrap(); + assert_eq!(reg_string.as_map().expect("reg string: {}").len(), 0); + assert_eq!( + map_deadbeef.get(1).as_integer(), + BigInt::from_str("-9").ok() + ); + + // test round-trip via generated JSON + let json2 = + decode_plutus_datum_to_json_str(&datum, PlutusDatumSchema::BasicConversions).unwrap(); + let datum2 = + encode_json_str_to_plutus_datum(&json2, PlutusDatumSchema::BasicConversions).unwrap(); + assert_eq!(datum, datum2); +} + +#[test] +pub fn plutus_datum_from_json_detailed_duplicated_keys() { + let json = "{\"list\": [ + {\"map\": [ + {\"k\": {\"bytes\": \"DEADBEEF\"}, \"v\": {\"int\": 42}}, + {\"k\": {\"bytes\": \"DEADBEEF\"}, \"v\": {\"int\": 43}}, + {\"k\": {\"map\" : [ + {\"k\": {\"int\": 9}, \"v\": {\"int\": -5}} + ]}, \"v\": {\"list\": []}} + ]} + ]}"; + let datum = encode_json_str_to_plutus_datum(json, PlutusDatumSchema::DetailedSchema).unwrap(); + + let list = datum.as_list().unwrap(); + assert_eq!(1, list.len()); + // map + let map = list.get(0).as_map().unwrap(); + assert_eq!(map.len(), 2); + let map_deadbeef = map + .get(&PlutusData::new_bytes(vec![222, 173, 190, 239])) + .unwrap(); + assert_eq!(map_deadbeef.len(), 2); + assert_eq!(map_deadbeef.get(0).unwrap().as_integer(), BigInt::from_str("42").ok()); + assert_eq!(map_deadbeef.get(1).unwrap().as_integer(), BigInt::from_str("43").ok()); + let mut long_key = PlutusMap::new(); + long_key.add_value( + &PlutusData::new_integer(&BigInt::from_str("9").unwrap()), + &PlutusData::new_integer(&BigInt::from_str("-5").unwrap()), + ); + let map_9_to_5 = map + .get(&PlutusData::new_map(&long_key)) + .unwrap() + .get(0) + .unwrap() + .as_list() + .unwrap(); + assert_eq!(map_9_to_5.len(), 0); + + // test round-trip via generated JSON + let json2 = decode_plutus_datum_to_json_str(&datum, PlutusDatumSchema::DetailedSchema).unwrap(); + let datum2 = + encode_json_str_to_plutus_datum(&json2, PlutusDatumSchema::DetailedSchema).unwrap(); + assert_eq!(datum, datum2); +} + +#[test] +pub fn plutus_datum_from_json_detailed() { + let json = "{\"list\": [ + {\"map\": [ + {\"k\": {\"bytes\": \"DEADBEEF\"}, \"v\": {\"int\": 42}}, + {\"k\": {\"map\" : [ + {\"k\": {\"int\": 9}, \"v\": {\"int\": -5}} + ]}, \"v\": {\"list\": []}} + ]}, + {\"bytes\": \"CAFED00D\"}, + {\"constructor\": 0, \"fields\": [ + {\"map\": []}, + {\"int\": 23} + ]} + ]}"; + let datum = encode_json_str_to_plutus_datum(json, PlutusDatumSchema::DetailedSchema).unwrap(); + + let list = datum.as_list().unwrap(); + assert_eq!(3, list.len()); + // map + let map = list.get(0).as_map().unwrap(); + assert_eq!(map.len(), 2); + let map_deadbeef = map + .get(&PlutusData::new_bytes(vec![222, 173, 190, 239])) + .unwrap(); + assert_eq!(map_deadbeef.get(0).unwrap().as_integer(), BigInt::from_str("42").ok()); + let mut long_key = PlutusMap::new(); + long_key.add_value( + &PlutusData::new_integer(&BigInt::from_str("9").unwrap()), + &PlutusData::new_integer(&BigInt::from_str("-5").unwrap()), + ); + let map_9_to_5 = map + .get(&PlutusData::new_map(&long_key)) + .unwrap() + .get(0) + .unwrap() + .as_list() + .unwrap(); + assert_eq!(map_9_to_5.len(), 0); + // bytes + let bytes = list.get(1).as_bytes().unwrap(); + assert_eq!(bytes, [202, 254, 208, 13]); + // constr data + let constr = list.get(2).as_constr_plutus_data().unwrap(); + assert_eq!(BigNum(0), constr.alternative()); + let fields = constr.data(); + assert_eq!(fields.len(), 2); + let field0 = fields.get(0).as_map().unwrap(); + assert_eq!(field0.len(), 0); + let field1 = fields.get(1); + assert_eq!(field1.as_integer(), BigInt::from_str("23").ok()); + + // test round-trip via generated JSON + let json2 = decode_plutus_datum_to_json_str(&datum, PlutusDatumSchema::DetailedSchema).unwrap(); + let datum2 = + encode_json_str_to_plutus_datum(&json2, PlutusDatumSchema::DetailedSchema).unwrap(); + assert_eq!(datum, datum2); +} + +#[test] +pub fn test_cost_model() { + let arr = vec![ + 197209, 0, 1, 1, 396231, 621, 0, 1, 150000, 1000, 0, 1, 150000, 32, 2477736, 29175, 4, + 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 100, 100, 29773, + 100, 150000, 32, 150000, 32, 150000, 32, 150000, 1000, 0, 1, 150000, 32, 150000, 1000, 0, + 8, 148000, 425507, 118, 0, 1, 1, 150000, 1000, 0, 8, 150000, 112536, 247, 1, 150000, 10000, + 1, 136542, 1326, 1, 1000, 150000, 1000, 1, 150000, 32, 150000, 32, 150000, 32, 1, 1, + 150000, 1, 150000, 4, 103599, 248, 1, 103599, 248, 1, 145276, 1366, 1, 179690, 497, 1, + 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 148000, 425507, + 118, 0, 1, 1, 61516, 11218, 0, 1, 150000, 32, 148000, 425507, 118, 0, 1, 1, 148000, 425507, + 118, 0, 1, 1, 2477736, 29175, 4, 0, 82363, 4, 150000, 5000, 0, 1, 150000, 32, 197209, 0, 1, + 1, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, + 3345831, 1, 1, + ]; + let cm = arr + .iter() + .fold((CostModel::new(), 0), |(mut cm, i), x| { + cm.set(i, &Int::new_i32(x.clone())).unwrap(); + (cm, i + 1) + }) + .0; + let mut cms = Costmdls::new(); + cms.insert(&Language::new_plutus_v1(), &cm); + assert_eq!( + hex::encode(cms.language_views_encoding()), + "a141005901d59f1a000302590001011a00060bc719026d00011a000249f01903e800011a000249f018201a0025cea81971f70419744d186419744d186419744d186419744d186419744d186419744d18641864186419744d18641a000249f018201a000249f018201a000249f018201a000249f01903e800011a000249f018201a000249f01903e800081a000242201a00067e2318760001011a000249f01903e800081a000249f01a0001b79818f7011a000249f0192710011a0002155e19052e011903e81a000249f01903e8011a000249f018201a000249f018201a000249f0182001011a000249f0011a000249f0041a000194af18f8011a000194af18f8011a0002377c190556011a0002bdea1901f1011a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000242201a00067e23187600010119f04c192bd200011a000249f018201a000242201a00067e2318760001011a000242201a00067e2318760001011a0025cea81971f704001a000141bb041a000249f019138800011a000249f018201a000302590001011a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a00330da70101ff" + ); +} + +#[test] +fn test_plutus_script_hash() { + let hash = EnterpriseAddress::from_address( + &Address::from_bech32("addr1w896t6qnpsjs32xhw8jl3kw34pqz69kgd72l8hqw83w0k3qahx2sv") + .unwrap(), + ) + .unwrap() + .payment_cred() + .to_scripthash() + .unwrap(); + let script = PlutusScript::from_bytes( + hex::decode("590e6f590e6c0100003323332223322333222332232332233223232333222323332223233333333222222223233322232333322223232332232323332223232332233223232333332222233223322332233223322332222323232232232325335303233300a3333573466e1cd55cea8042400046664446660a40060040026eb4d5d0a8041bae35742a00e66a05046666ae68cdc39aab9d37540029000102b11931a982599ab9c04f04c04a049357426ae89401c8c98d4c124cd5ce0268250240239999ab9a3370ea0089001102b11999ab9a3370ea00a9000102c11931a982519ab9c04e04b0490480473333573466e1cd55cea8012400046601a64646464646464646464646666ae68cdc39aab9d500a480008cccccccccc06ccd40a48c8c8cccd5cd19b8735573aa0049000119810981c9aba15002302e357426ae8940088c98d4c164cd5ce02e82d02c02b89aab9e5001137540026ae854028cd40a40a8d5d0a804999aa8183ae502f35742a010666aa060eb940bcd5d0a80399a8148211aba15006335029335505304b75a6ae854014c8c8c8cccd5cd19b8735573aa0049000119a8119919191999ab9a3370e6aae7540092000233502b33504175a6ae854008c118d5d09aba25002232635305d3357380c20bc0b80b626aae7940044dd50009aba150023232323333573466e1cd55cea80124000466a05266a082eb4d5d0a80118231aba135744a004464c6a60ba66ae7018417817016c4d55cf280089baa001357426ae8940088c98d4c164cd5ce02e82d02c02b89aab9e5001137540026ae854010cd40a5d71aba15003335029335505375c40026ae854008c0e0d5d09aba2500223263530553357380b20ac0a80a626ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023232323333573466e1d4005200623020303a357426aae79400c8cccd5cd19b875002480108c07cc110d5d09aab9e500423333573466e1d400d20022301f302f357426aae7940148cccd5cd19b875004480008c088dd71aba135573ca00c464c6a60a066ae7015014413c13813413012c4d55cea80089baa001357426ae8940088c98d4c124cd5ce026825024023882489931a982419ab9c4910350543500049047135573ca00226ea80044d55ce9baa001135744a00226aae7940044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac0013200135503c2233335573e0024a01c466a01a60086ae84008c00cd5d100101811919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a605c6ae854008cd404c0a8d5d09aba25002232635303433573807006a06606426aae7940044dd50009aba150033335500b75ca0146ae854008cd403dd71aba135744a004464c6a606066ae700d00c40bc0b84d5d1280089aab9e5001137540024442466600200800600440024424660020060044002266aa002eb9d6889119118011bab00132001355036223233335573e0044a012466a01066aa05c600c6aae754008c014d55cf280118021aba200302b1357420022244004244244660020080062400224464646666ae68cdc3a800a400046a05e600a6ae84d55cf280191999ab9a3370ea00490011281791931a981399ab9c02b028026025024135573aa00226ea80048c8c8cccd5cd19b8735573aa004900011980318039aba15002375a6ae84d5d1280111931a981219ab9c028025023022135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088c98d4c080cd5ce01201080f80f09baa00112232323333573466e1d400520042500723333573466e1d4009200223500a3006357426aae7940108cccd5cd19b87500348000940288c98d4c08ccd5ce01381201101081000f89aab9d50011375400224244460060082244400422444002240024646666ae68cdc3a800a4004400c46666ae68cdc3a80124000400c464c6a603666ae7007c0700680640604d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308c98d4c080cd5ce01201080f80f00e80e00d80d00c80c09aab9d5004135573ca00626aae7940084d55cf280089baa00121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012323232323333573466e1d400520022333008375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea00490001180518059aba135573ca00c464c6a602266ae7005404804003c0384d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e5004232635300b33573801e01801401201026aae7540044dd5000909118010019091180080190008891119191999ab9a3370e6aae75400920002335500b300635742a004600a6ae84d5d1280111931a980419ab9c00c009007006135573ca00226ea800526120012001112212330010030021120014910350543100222123330010040030022001121223002003112200112001120012001122002122001200111232300100122330033002002001332323233322233322233223332223322332233322233223322332233223233322232323322323232323333222232332232323222323222325335301a5335301a333573466e1cc8cccd54c05048004c8cd406488ccd406400c004008d4058004cd4060888c00cc008004800488cdc0000a40040029000199aa98068900091299a980e299a9a81a1a98169a98131a9812001110009110019119a98188011281c11a81c8009080f880e899a8148010008800a8141a981028009111111111005240040380362038266ae712413c53686f756c642062652065786163746c79206f6e652073637269707420696e70757420746f2061766f696420646f75626c65207361742069737375650001b15335303500315335301a5335301a333573466e20ccc064ccd54c03448005402540a0cc020d4c0c00188880094004074074cdc09a9818003111001a80200d80e080e099ab9c49010f73656c6c6572206e6f7420706169640001b15335301a333573466e20ccc064cc88ccd54c03c48005402d40a8cc028004009400401c074075401006c07040704cd5ce24810d66656573206e6f7420706169640001b101b15335301a3322353022002222222222253353503e33355301f1200133502322533535040002210031001503f253353027333573466e3c0300040a40a04d41040045410000c840a4409d4004d4c0c001888800840704cd5ce2491c4f6e6c792073656c6c65722063616e2063616e63656c206f666665720001b101b135301d00122002153353016333573466e2540040d406005c40d4540044cdc199b8235302b001222003480c920d00f2235301a0012222222222333553011120012235302a002222353034003223353038002253353026333573466e3c0500040a009c4cd40cc01401c401c801d40b0024488cd54c02c480048d4d5408c00488cd54098008cd54c038480048d4d5409800488cd540a4008ccd4d540340048cc0e12000001223303900200123303800148000004cd54c02c480048d4d5408c00488cd54098008ccd4d540280048cd54c03c480048d4d5409c00488cd540a8008d5404400400488ccd5540200580080048cd54c03c480048d4d5409c00488cd540a8008d5403c004004ccd55400c044008004444888ccd54c018480054080cd54c02c480048d4d5408c00488cd54098008d54034004ccd54c0184800488d4d54090008894cd4c05cccd54c04048004c8cd405488ccd4d402c00c88008008004d4d402400488004cd4024894cd4c064008406c40040608d4d5409c00488cc028008014018400c4cd409001000d4084004cd54c02c480048d4d5408c00488c8cd5409c00cc004014c8004d540d8894cd4d40900044d5403400c884d4d540a4008894cd4c070cc0300080204cd5404801c0044c01800c00848848cc00400c00848004c8004d540b488448894cd4d40780044008884cc014008ccd54c01c480040140100044484888c00c01044884888cc0080140104484888c004010448004c8004d540a08844894cd4d406000454068884cd406cc010008cd54c01848004010004c8004d5409c88448894cd4d40600044d401800c884ccd4024014c010008ccd54c01c4800401401000448d4d400c0048800448d4d40080048800848848cc00400c0084800488ccd5cd19b8f002001006005222323230010053200135502522335350130014800088d4d54060008894cd4c02cccd5cd19b8f00200900d00c13007001130060033200135502422335350120014800088d4d5405c008894cd4c028ccd5cd19b8f00200700c00b10011300600312200212200120014881002212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220011122002122122330010040031200111221233001003002112001221233001003002200121223002003212230010032001222123330010040030022001121223002003112200112001122002122001200122337000040029040497a0088919180080091198019801001000a4411c28f07a93d7715db0bdc1766c8bd5b116602b105c02c54fc3bcd0d4680001").unwrap().clone(), + ).unwrap(); + assert_eq!(script.hash(), hash); +} + +#[test] +fn test_plutus_script_from_hex_with_version() { + let script_v1 = PlutusScript::from_hex_with_version( + "590e6f590e6c0100003323332223322333222332232332233223232333222323332223233333333222222223233322232333322223232332232323332223232332233223232333332222233223322332233223322332222323232232232325335303233300a3333573466e1cd55cea8042400046664446660a40060040026eb4d5d0a8041bae35742a00e66a05046666ae68cdc39aab9d37540029000102b11931a982599ab9c04f04c04a049357426ae89401c8c98d4c124cd5ce0268250240239999ab9a3370ea0089001102b11999ab9a3370ea00a9000102c11931a982519ab9c04e04b0490480473333573466e1cd55cea8012400046601a64646464646464646464646666ae68cdc39aab9d500a480008cccccccccc06ccd40a48c8c8cccd5cd19b8735573aa0049000119810981c9aba15002302e357426ae8940088c98d4c164cd5ce02e82d02c02b89aab9e5001137540026ae854028cd40a40a8d5d0a804999aa8183ae502f35742a010666aa060eb940bcd5d0a80399a8148211aba15006335029335505304b75a6ae854014c8c8c8cccd5cd19b8735573aa0049000119a8119919191999ab9a3370e6aae7540092000233502b33504175a6ae854008c118d5d09aba25002232635305d3357380c20bc0b80b626aae7940044dd50009aba150023232323333573466e1cd55cea80124000466a05266a082eb4d5d0a80118231aba135744a004464c6a60ba66ae7018417817016c4d55cf280089baa001357426ae8940088c98d4c164cd5ce02e82d02c02b89aab9e5001137540026ae854010cd40a5d71aba15003335029335505375c40026ae854008c0e0d5d09aba2500223263530553357380b20ac0a80a626ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023232323333573466e1d4005200623020303a357426aae79400c8cccd5cd19b875002480108c07cc110d5d09aab9e500423333573466e1d400d20022301f302f357426aae7940148cccd5cd19b875004480008c088dd71aba135573ca00c464c6a60a066ae7015014413c13813413012c4d55cea80089baa001357426ae8940088c98d4c124cd5ce026825024023882489931a982419ab9c4910350543500049047135573ca00226ea80044d55ce9baa001135744a00226aae7940044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac0013200135503c2233335573e0024a01c466a01a60086ae84008c00cd5d100101811919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a605c6ae854008cd404c0a8d5d09aba25002232635303433573807006a06606426aae7940044dd50009aba150033335500b75ca0146ae854008cd403dd71aba135744a004464c6a606066ae700d00c40bc0b84d5d1280089aab9e5001137540024442466600200800600440024424660020060044002266aa002eb9d6889119118011bab00132001355036223233335573e0044a012466a01066aa05c600c6aae754008c014d55cf280118021aba200302b1357420022244004244244660020080062400224464646666ae68cdc3a800a400046a05e600a6ae84d55cf280191999ab9a3370ea00490011281791931a981399ab9c02b028026025024135573aa00226ea80048c8c8cccd5cd19b8735573aa004900011980318039aba15002375a6ae84d5d1280111931a981219ab9c028025023022135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088c98d4c080cd5ce01201080f80f09baa00112232323333573466e1d400520042500723333573466e1d4009200223500a3006357426aae7940108cccd5cd19b87500348000940288c98d4c08ccd5ce01381201101081000f89aab9d50011375400224244460060082244400422444002240024646666ae68cdc3a800a4004400c46666ae68cdc3a80124000400c464c6a603666ae7007c0700680640604d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308c98d4c080cd5ce01201080f80f00e80e00d80d00c80c09aab9d5004135573ca00626aae7940084d55cf280089baa00121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012323232323333573466e1d400520022333008375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea00490001180518059aba135573ca00c464c6a602266ae7005404804003c0384d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e5004232635300b33573801e01801401201026aae7540044dd5000909118010019091180080190008891119191999ab9a3370e6aae75400920002335500b300635742a004600a6ae84d5d1280111931a980419ab9c00c009007006135573ca00226ea800526120012001112212330010030021120014910350543100222123330010040030022001121223002003112200112001120012001122002122001200111232300100122330033002002001332323233322233322233223332223322332233322233223322332233223233322232323322323232323333222232332232323222323222325335301a5335301a333573466e1cc8cccd54c05048004c8cd406488ccd406400c004008d4058004cd4060888c00cc008004800488cdc0000a40040029000199aa98068900091299a980e299a9a81a1a98169a98131a9812001110009110019119a98188011281c11a81c8009080f880e899a8148010008800a8141a981028009111111111005240040380362038266ae712413c53686f756c642062652065786163746c79206f6e652073637269707420696e70757420746f2061766f696420646f75626c65207361742069737375650001b15335303500315335301a5335301a333573466e20ccc064ccd54c03448005402540a0cc020d4c0c00188880094004074074cdc09a9818003111001a80200d80e080e099ab9c49010f73656c6c6572206e6f7420706169640001b15335301a333573466e20ccc064cc88ccd54c03c48005402d40a8cc028004009400401c074075401006c07040704cd5ce24810d66656573206e6f7420706169640001b101b15335301a3322353022002222222222253353503e33355301f1200133502322533535040002210031001503f253353027333573466e3c0300040a40a04d41040045410000c840a4409d4004d4c0c001888800840704cd5ce2491c4f6e6c792073656c6c65722063616e2063616e63656c206f666665720001b101b135301d00122002153353016333573466e2540040d406005c40d4540044cdc199b8235302b001222003480c920d00f2235301a0012222222222333553011120012235302a002222353034003223353038002253353026333573466e3c0500040a009c4cd40cc01401c401c801d40b0024488cd54c02c480048d4d5408c00488cd54098008cd54c038480048d4d5409800488cd540a4008ccd4d540340048cc0e12000001223303900200123303800148000004cd54c02c480048d4d5408c00488cd54098008ccd4d540280048cd54c03c480048d4d5409c00488cd540a8008d5404400400488ccd5540200580080048cd54c03c480048d4d5409c00488cd540a8008d5403c004004ccd55400c044008004444888ccd54c018480054080cd54c02c480048d4d5408c00488cd54098008d54034004ccd54c0184800488d4d54090008894cd4c05cccd54c04048004c8cd405488ccd4d402c00c88008008004d4d402400488004cd4024894cd4c064008406c40040608d4d5409c00488cc028008014018400c4cd409001000d4084004cd54c02c480048d4d5408c00488c8cd5409c00cc004014c8004d540d8894cd4d40900044d5403400c884d4d540a4008894cd4c070cc0300080204cd5404801c0044c01800c00848848cc00400c00848004c8004d540b488448894cd4d40780044008884cc014008ccd54c01c480040140100044484888c00c01044884888cc0080140104484888c004010448004c8004d540a08844894cd4d406000454068884cd406cc010008cd54c01848004010004c8004d5409c88448894cd4d40600044d401800c884ccd4024014c010008ccd54c01c4800401401000448d4d400c0048800448d4d40080048800848848cc00400c0084800488ccd5cd19b8f002001006005222323230010053200135502522335350130014800088d4d54060008894cd4c02cccd5cd19b8f00200900d00c13007001130060033200135502422335350120014800088d4d5405c008894cd4c028ccd5cd19b8f00200700c00b10011300600312200212200120014881002212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220011122002122122330010040031200111221233001003002112001221233001003002200121223002003212230010032001222123330010040030022001121223002003112200112001122002122001200122337000040029040497a0088919180080091198019801001000a4411c28f07a93d7715db0bdc1766c8bd5b116602b105c02c54fc3bcd0d4680001", + &Language::new_plutus_v1() + ).unwrap(); + assert_eq!(script_v1.language, Language::new_plutus_v1().0); + + let script_v2 = PlutusScript::from_hex_with_version( + "590e6f590e6c0100003323332223322333222332232332233223232333222323332223233333333222222223233322232333322223232332232323332223232332233223232333332222233223322332233223322332222323232232232325335303233300a3333573466e1cd55cea8042400046664446660a40060040026eb4d5d0a8041bae35742a00e66a05046666ae68cdc39aab9d37540029000102b11931a982599ab9c04f04c04a049357426ae89401c8c98d4c124cd5ce0268250240239999ab9a3370ea0089001102b11999ab9a3370ea00a9000102c11931a982519ab9c04e04b0490480473333573466e1cd55cea8012400046601a64646464646464646464646666ae68cdc39aab9d500a480008cccccccccc06ccd40a48c8c8cccd5cd19b8735573aa0049000119810981c9aba15002302e357426ae8940088c98d4c164cd5ce02e82d02c02b89aab9e5001137540026ae854028cd40a40a8d5d0a804999aa8183ae502f35742a010666aa060eb940bcd5d0a80399a8148211aba15006335029335505304b75a6ae854014c8c8c8cccd5cd19b8735573aa0049000119a8119919191999ab9a3370e6aae7540092000233502b33504175a6ae854008c118d5d09aba25002232635305d3357380c20bc0b80b626aae7940044dd50009aba150023232323333573466e1cd55cea80124000466a05266a082eb4d5d0a80118231aba135744a004464c6a60ba66ae7018417817016c4d55cf280089baa001357426ae8940088c98d4c164cd5ce02e82d02c02b89aab9e5001137540026ae854010cd40a5d71aba15003335029335505375c40026ae854008c0e0d5d09aba2500223263530553357380b20ac0a80a626ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba150023232323333573466e1d4005200623020303a357426aae79400c8cccd5cd19b875002480108c07cc110d5d09aab9e500423333573466e1d400d20022301f302f357426aae7940148cccd5cd19b875004480008c088dd71aba135573ca00c464c6a60a066ae7015014413c13813413012c4d55cea80089baa001357426ae8940088c98d4c124cd5ce026825024023882489931a982419ab9c4910350543500049047135573ca00226ea80044d55ce9baa001135744a00226aae7940044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac0013200135503c2233335573e0024a01c466a01a60086ae84008c00cd5d100101811919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a605c6ae854008cd404c0a8d5d09aba25002232635303433573807006a06606426aae7940044dd50009aba150033335500b75ca0146ae854008cd403dd71aba135744a004464c6a606066ae700d00c40bc0b84d5d1280089aab9e5001137540024442466600200800600440024424660020060044002266aa002eb9d6889119118011bab00132001355036223233335573e0044a012466a01066aa05c600c6aae754008c014d55cf280118021aba200302b1357420022244004244244660020080062400224464646666ae68cdc3a800a400046a05e600a6ae84d55cf280191999ab9a3370ea00490011281791931a981399ab9c02b028026025024135573aa00226ea80048c8c8cccd5cd19b8735573aa004900011980318039aba15002375a6ae84d5d1280111931a981219ab9c028025023022135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088c98d4c080cd5ce01201080f80f09baa00112232323333573466e1d400520042500723333573466e1d4009200223500a3006357426aae7940108cccd5cd19b87500348000940288c98d4c08ccd5ce01381201101081000f89aab9d50011375400224244460060082244400422444002240024646666ae68cdc3a800a4004400c46666ae68cdc3a80124000400c464c6a603666ae7007c0700680640604d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308c98d4c080cd5ce01201080f80f00e80e00d80d00c80c09aab9d5004135573ca00626aae7940084d55cf280089baa00121222222230070082212222222330060090082122222223005008122222220041222222200322122222223300200900822122222223300100900820012323232323333573466e1d400520022333008375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea00490001180518059aba135573ca00c464c6a602266ae7005404804003c0384d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e5004232635300b33573801e01801401201026aae7540044dd5000909118010019091180080190008891119191999ab9a3370e6aae75400920002335500b300635742a004600a6ae84d5d1280111931a980419ab9c00c009007006135573ca00226ea800526120012001112212330010030021120014910350543100222123330010040030022001121223002003112200112001120012001122002122001200111232300100122330033002002001332323233322233322233223332223322332233322233223322332233223233322232323322323232323333222232332232323222323222325335301a5335301a333573466e1cc8cccd54c05048004c8cd406488ccd406400c004008d4058004cd4060888c00cc008004800488cdc0000a40040029000199aa98068900091299a980e299a9a81a1a98169a98131a9812001110009110019119a98188011281c11a81c8009080f880e899a8148010008800a8141a981028009111111111005240040380362038266ae712413c53686f756c642062652065786163746c79206f6e652073637269707420696e70757420746f2061766f696420646f75626c65207361742069737375650001b15335303500315335301a5335301a333573466e20ccc064ccd54c03448005402540a0cc020d4c0c00188880094004074074cdc09a9818003111001a80200d80e080e099ab9c49010f73656c6c6572206e6f7420706169640001b15335301a333573466e20ccc064cc88ccd54c03c48005402d40a8cc028004009400401c074075401006c07040704cd5ce24810d66656573206e6f7420706169640001b101b15335301a3322353022002222222222253353503e33355301f1200133502322533535040002210031001503f253353027333573466e3c0300040a40a04d41040045410000c840a4409d4004d4c0c001888800840704cd5ce2491c4f6e6c792073656c6c65722063616e2063616e63656c206f666665720001b101b135301d00122002153353016333573466e2540040d406005c40d4540044cdc199b8235302b001222003480c920d00f2235301a0012222222222333553011120012235302a002222353034003223353038002253353026333573466e3c0500040a009c4cd40cc01401c401c801d40b0024488cd54c02c480048d4d5408c00488cd54098008cd54c038480048d4d5409800488cd540a4008ccd4d540340048cc0e12000001223303900200123303800148000004cd54c02c480048d4d5408c00488cd54098008ccd4d540280048cd54c03c480048d4d5409c00488cd540a8008d5404400400488ccd5540200580080048cd54c03c480048d4d5409c00488cd540a8008d5403c004004ccd55400c044008004444888ccd54c018480054080cd54c02c480048d4d5408c00488cd54098008d54034004ccd54c0184800488d4d54090008894cd4c05cccd54c04048004c8cd405488ccd4d402c00c88008008004d4d402400488004cd4024894cd4c064008406c40040608d4d5409c00488cc028008014018400c4cd409001000d4084004cd54c02c480048d4d5408c00488c8cd5409c00cc004014c8004d540d8894cd4d40900044d5403400c884d4d540a4008894cd4c070cc0300080204cd5404801c0044c01800c00848848cc00400c00848004c8004d540b488448894cd4d40780044008884cc014008ccd54c01c480040140100044484888c00c01044884888cc0080140104484888c004010448004c8004d540a08844894cd4d406000454068884cd406cc010008cd54c01848004010004c8004d5409c88448894cd4d40600044d401800c884ccd4024014c010008ccd54c01c4800401401000448d4d400c0048800448d4d40080048800848848cc00400c0084800488ccd5cd19b8f002001006005222323230010053200135502522335350130014800088d4d54060008894cd4c02cccd5cd19b8f00200900d00c13007001130060033200135502422335350120014800088d4d5405c008894cd4c028ccd5cd19b8f00200700c00b10011300600312200212200120014881002212330010030022001222222222212333333333300100b00a009008007006005004003002200122123300100300220012221233300100400300220011122002122122330010040031200111221233001003002112001221233001003002200121223002003212230010032001222123330010040030022001121223002003112200112001122002122001200122337000040029040497a0088919180080091198019801001000a4411c28f07a93d7715db0bdc1766c8bd5b116602b105c02c54fc3bcd0d4680001", + &Language::new_plutus_v2() + ).unwrap(); + assert_eq!(script_v2.language, Language::new_plutus_v2().0); +} + +fn redeemer_with_ex_units(mem: &BigNum, steps: &BigNum) -> Redeemer { + Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum::zero(), + &PlutusData::new_integer(&BigInt::from_str("0").unwrap()), + &ExUnits::new(mem, steps), + ) +} + +#[test] +fn test_total_ex_units() { + let mut r = Redeemers::new(); + + fn assert_ex_units(eu: &ExUnits, exp_mem: u64, exp_steps: u64) { + assert_eq!(eu.mem, BigNum(exp_mem)); + assert_eq!(eu.steps, BigNum(exp_steps)); + } + + r.add(&redeemer_with_ex_units(&BigNum(10), &BigNum(100))); + assert_ex_units(&r.total_ex_units().unwrap(), 10, 100); + r.add(&redeemer_with_ex_units(&BigNum(20), &BigNum(200))); + assert_ex_units(&r.total_ex_units().unwrap(), 30, 300); + r.add(&redeemer_with_ex_units(&BigNum(30), &BigNum(300))); + assert_ex_units(&r.total_ex_units().unwrap(), 60, 600); +} + +#[test] +fn test_empty_constr_data() { + assert_eq!( + PlutusData::new_empty_constr_plutus_data(&BigNum::one()), + PlutusData::new_constr_plutus_data(&ConstrPlutusData::new( + &BigNum::from_str("1").unwrap(), + &PlutusList::new(), + ),), + ) +} + +#[test] +fn test_plutus_script_version() { + let bytes = hex::decode("4e4d01000033222220051200120011").unwrap(); + let s1: PlutusScript = PlutusScript::from_bytes(bytes.clone()).unwrap(); + let s2: PlutusScript = PlutusScript::from_bytes_v2(bytes.clone()).unwrap(); + let s3: PlutusScript = PlutusScript::from_bytes_v3(bytes.clone()).unwrap(); + + assert_eq!(s1.bytes(), bytes[1..]); + assert_eq!(s2.bytes(), bytes[1..]); + assert_eq!(s3.bytes(), bytes[1..]); + assert_eq!(s1.language_version(), Language::new_plutus_v1()); + assert_eq!(s2.language_version(), Language::new_plutus_v2()); + assert_eq!(s3.language_version(), Language::new_plutus_v3()); + + assert_eq!( + s1, + PlutusScript::from_bytes_with_version(bytes.clone(), &Language::new_plutus_v1(),).unwrap() + ); + assert_eq!( + s2, + PlutusScript::from_bytes_with_version(bytes.clone(), &Language::new_plutus_v2(),).unwrap() + ); + assert_eq!( + s3, + PlutusScript::from_bytes_with_version(bytes.clone(), &Language::new_plutus_v3(),).unwrap() + ); +} + +#[test] +fn test_language_roundtrip() { + fn deserialize_language_from_uint(x: u64) -> Result { + let mut buf = Serializer::new_vec(); + x.serialize(&mut buf).unwrap(); + Language::from_bytes(buf.finalize()) + } + + assert_eq!( + deserialize_language_from_uint(0).unwrap(), + Language::new_plutus_v1() + ); + assert_eq!( + deserialize_language_from_uint(1).unwrap(), + Language::new_plutus_v2() + ); + assert_eq!( + deserialize_language_from_uint(2).unwrap(), + Language::new_plutus_v3() + ); + assert!(deserialize_language_from_uint(3).is_err()); + + assert_eq!( + Language::from_bytes(Language::new_plutus_v1().to_bytes()).unwrap(), + Language::new_plutus_v1(), + ); + assert_eq!( + Language::from_bytes(Language::new_plutus_v2().to_bytes()).unwrap(), + Language::new_plutus_v2(), + ); + assert_eq!( + Language::from_bytes(Language::new_plutus_v3().to_bytes()).unwrap(), + Language::new_plutus_v3(), + ); +} + +#[test] +fn test_cost_model_roundtrip() { + use crate::TxBuilderConstants; + let costmodels = TxBuilderConstants::plutus_vasil_cost_models(); + assert_eq!( + costmodels, + Costmdls::from_bytes(costmodels.to_bytes()).unwrap() + ); +} + +#[test] +fn test_known_plutus_data_hash() { + use crate::TxBuilderConstants; + let pdata = PlutusList::from(vec![PlutusData::new_constr_plutus_data( + &ConstrPlutusData::new( + &BigNum::zero(), + &PlutusList::from(vec![ + PlutusData::new_constr_plutus_data(&ConstrPlutusData::new( + &BigNum::zero(), + &PlutusList::from(vec![ + PlutusData::new_bytes( + hex::decode("A183BF86925F66C579A3745C9517744399679B090927B8F6E2F2E1BB") + .unwrap(), + ), + PlutusData::new_bytes( + hex::decode("6164617065416D616E734576616E73").unwrap(), + ), + ]), + )), + PlutusData::new_constr_plutus_data(&ConstrPlutusData::new( + &BigNum::zero(), + &PlutusList::from(vec![ + PlutusData::new_bytes( + hex::decode("9A4E855293A0B9AF5E50935A331D83E7982AB5B738EA0E6FC0F9E656") + .unwrap(), + ), + PlutusData::new_bytes(hex::decode("4652414D455F38333030325F4C30").unwrap()), + ]), + )), + PlutusData::new_bytes( + hex::decode("BEA1C521DF58F4EEEF60C647E5EBD88C6039915409F9FD6454A476B9") + .unwrap(), + ), + ]), + ), + )]); + let redeemers = Redeemers::from(vec![Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum::one(), + &PlutusData::new_empty_constr_plutus_data(&BigNum::zero()), + &ExUnits::new(&BigNum(7000000), &BigNum(3000000000)), + )]); + let lang = Language::new_plutus_v1(); + let lang_costmodel = TxBuilderConstants::plutus_vasil_cost_models() + .get(&lang) + .unwrap(); + let mut retained_cost_models = Costmdls::new(); + retained_cost_models.insert(&lang, &lang_costmodel); + let hash = hash_script_data(&redeemers, &retained_cost_models, Some(pdata)); + assert_eq!( + hex::encode(hash.to_bytes()), + "2fd8b7e248b376314d02989c885c278796ab0e1d6e8aa0cb91f562ff5f7dbd70" + ); +} + +#[test] +fn test_same_datum_in_different_formats_with_expected_hashes() { + // This is a known datum with indefinite arrays and a known expected hash + let pdata1 = PlutusData::from_bytes(hex::decode("d8799fd8799f581ca183bf86925f66c579a3745c9517744399679b090927b8f6e2f2e1bb4f616461706541696c656e416d61746fffd8799f581c9a4e855293a0b9af5e50935a331d83e7982ab5b738ea0e6fc0f9e6564e4652414d455f36353030335f4c30ff581cbea1c521df58f4eeef60c647e5ebd88c6039915409f9fd6454a476b9ff").unwrap()).unwrap(); + assert_eq!( + hex::encode(hash_plutus_data(&pdata1).to_bytes()), + "ec3028f46325b983a470893a8bdc1b4a100695b635fb1237d301c3490b23e89b" + ); + // This is the same exact datum manually converted to definite arrays + // and it produces a different known expected hash because the format is preserved after deserialization + let pdata2 = PlutusData::from_bytes(hex::decode("d87983d87982581ca183bf86925f66c579a3745c9517744399679b090927b8f6e2f2e1bb4f616461706541696c656e416d61746fd87982581c9a4e855293a0b9af5e50935a331d83e7982ab5b738ea0e6fc0f9e6564e4652414d455f36353030335f4c30581cbea1c521df58f4eeef60c647e5ebd88c6039915409f9fd6454a476b9").unwrap()).unwrap(); + assert_eq!( + hex::encode(hash_plutus_data(&pdata2).to_bytes()), + "816cdf6d4d8cba3ad0188ca643db95ddf0e03cdfc0e75a9550a72a82cb146222" + ); +} + +#[test] +fn test_known_plutus_data_hash_with_no_datums() { + let mut costmodels = Costmdls::new(); + costmodels.insert( + &Language::new_plutus_v2(), + &TxBuilderConstants::plutus_vasil_cost_models() + .get(&Language::new_plutus_v2()) + .unwrap(), + ); + let hash = hash_script_data( + &Redeemers::from(vec![Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum::zero(), + &PlutusData::new_empty_constr_plutus_data(&BigNum::zero()), + &ExUnits::new(&BigNum(842996), &BigNum(246100241)), + )]), + &costmodels, + None, + ); + assert_eq!( + hex::encode(hash.to_bytes()), + "6b244f15f895fd458a02bef3a8b56f17f24150fddcb06be482f8790a600578a1" + ); +} + +#[test] +fn test_known_plutus_data_hash_2() { + let datums = PlutusList::from(vec![PlutusData::new_constr_plutus_data( + &ConstrPlutusData::new( + &BigNum::zero(), + &PlutusList::from(vec![ + PlutusData::new_bytes( + hex::decode("45F6A506A49A38263C4A8BBB2E1E369DD8732FB1F9A281F3E8838387") + .unwrap(), + ), + PlutusData::new_integer(&BigInt::from_str("60000000").unwrap()), + PlutusData::new_bytes( + hex::decode("EE8E37676F6EBB8E031DFF493F88FF711D24AA68666A09D61F1D3FB3") + .unwrap(), + ), + PlutusData::new_bytes(hex::decode("43727970746F44696E6F3036333039").unwrap()), + ]), + ), + )]); + let redeemers = Redeemers::from(vec![Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum::one(), + &PlutusData::new_empty_constr_plutus_data(&BigNum::one()), + &ExUnits::new(&BigNum(61300), &BigNum(18221176)), + )]); + let hash = hash_script_data( + &redeemers, + &TxBuilderConstants::plutus_vasil_cost_models() + .retain_language_versions(&Languages(vec![Language::new_plutus_v1()])), + Some(datums), + ); + assert_eq!( + hex::encode(hash.to_bytes()), + "0a076247a05aacbecf72ea15b94e3d0331b21295a08d9ab7b8675c13840563a6" + ); +} + +#[test] +fn datum_from_enterprise_key_address() { + let address = + Address::from_bech32("addr1vxy2c673nsdp0mvgq5d3tpjndngucsytug00k7k6xwlx4lg6dspk5").unwrap(); + let datum = PlutusData::from_address(&address).unwrap(); + let orig_datum = PlutusData::from_json("{\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"bytes\": \"88ac6bd19c1a17ed88051b1586536cd1cc408be21efb7ada33be6afd\"}]}, {\"constructor\": 1, \"fields\": []}]}", + PlutusDatumSchema::DetailedSchema).unwrap(); + assert_eq!(datum, orig_datum); +} + +#[test] +fn datum_from_enterprise_script_address() { + let address = + Address::from_bech32("addr1w8wrk560wcsldjpnqjamn8s0gn9pdrplpyetrdfpacqrpfs3xezd8").unwrap(); + let datum = PlutusData::from_address(&address).unwrap(); + let orig_datum = PlutusData::from_json("{\"constructor\": 0, \"fields\": [{\"constructor\": 1, \"fields\": [{\"bytes\": \"dc3b534f7621f6c83304bbb99e0f44ca168c3f0932b1b521ee0030a6\"}]}, {\"constructor\": 1, \"fields\": []}]}", + PlutusDatumSchema::DetailedSchema).unwrap(); + assert_eq!(datum, orig_datum); +} + +#[test] +fn datum_from_base_key_key_address() { + let address = Address::from_bech32("addr1qxy2c673nsdp0mvgq5d3tpjndngucsytug00k7k6xwlx4lvg434ar8q6zlkcspgmzkr9xmx3e3qghcs7ldad5va7dt7s5efyer").unwrap(); + let datum = PlutusData::from_address(&address).unwrap(); + let orig_datum = PlutusData::from_json("{\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"bytes\": \"88ac6bd19c1a17ed88051b1586536cd1cc408be21efb7ada33be6afd\"}]}, {\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"bytes\": \"88ac6bd19c1a17ed88051b1586536cd1cc408be21efb7ada33be6afd\"}]}]}]}]}", + PlutusDatumSchema::DetailedSchema).unwrap(); + assert_eq!(datum, orig_datum); +} + +#[test] +fn datum_from_base_script_script_address() { + let address = Address::from_bech32("addr1x8wrk560wcsldjpnqjamn8s0gn9pdrplpyetrdfpacqrpfku8df57a3p7myrxp9mhx0q73x2z6xr7zfjkx6jrmsqxznqh8u5dz").unwrap(); + let datum = PlutusData::from_address(&address).unwrap(); + let orig_datum = PlutusData::from_json("{\"constructor\": 0, \"fields\": [{\"constructor\": 1, \"fields\": [{\"bytes\": \"dc3b534f7621f6c83304bbb99e0f44ca168c3f0932b1b521ee0030a6\"}]}, {\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"constructor\": 1, \"fields\": [{\"bytes\": \"dc3b534f7621f6c83304bbb99e0f44ca168c3f0932b1b521ee0030a6\"}]}]}]}]}", + PlutusDatumSchema::DetailedSchema).unwrap(); + assert_eq!(datum, orig_datum); +} + +#[test] +fn datum_from_base_script_key_address() { + let address = Address::from_bech32("addr1z8wrk560wcsldjpnqjamn8s0gn9pdrplpyetrdfpacqrpf5g434ar8q6zlkcspgmzkr9xmx3e3qghcs7ldad5va7dt7sqx2wxh").unwrap(); + let datum = PlutusData::from_address(&address).unwrap(); + let orig_datum = PlutusData::from_json("{\"constructor\": 0, \"fields\": [{\"constructor\": 1, \"fields\": [{\"bytes\": \"dc3b534f7621f6c83304bbb99e0f44ca168c3f0932b1b521ee0030a6\"}]}, {\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"bytes\": \"88ac6bd19c1a17ed88051b1586536cd1cc408be21efb7ada33be6afd\"}]}]}]}]}", + PlutusDatumSchema::DetailedSchema).unwrap(); + assert_eq!(datum, orig_datum); +} + +#[test] +fn datum_from_base_key_script_address() { + let address = Address::from_bech32("addr1yxy2c673nsdp0mvgq5d3tpjndngucsytug00k7k6xwlx4lwu8df57a3p7myrxp9mhx0q73x2z6xr7zfjkx6jrmsqxznqrcl7jk").unwrap(); + let datum = PlutusData::from_address(&address).unwrap(); + let orig_datum = PlutusData::from_json("{\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"bytes\": \"88ac6bd19c1a17ed88051b1586536cd1cc408be21efb7ada33be6afd\"}]}, {\"constructor\": 0, \"fields\": [{\"constructor\": 0, \"fields\": [{\"constructor\": 1, \"fields\": [{\"bytes\": \"dc3b534f7621f6c83304bbb99e0f44ca168c3f0932b1b521ee0030a6\"}]}]}]}]}", + PlutusDatumSchema::DetailedSchema).unwrap(); + assert_eq!(datum, orig_datum); +} \ No newline at end of file diff --git a/rust/src/tests/protocol_types/certificates.rs b/rust/src/tests/protocol_types/certificates.rs new file mode 100644 index 00000000..3476d3cc --- /dev/null +++ b/rust/src/tests/protocol_types/certificates.rs @@ -0,0 +1,308 @@ +use crate::tests::fakes::{fake_full_pool_params, fake_anchor, fake_genesis_delegate_hash, fake_genesis_hash, fake_key_hash, fake_script_hash, fake_vrf_key_hash}; +use crate::*; + +#[test] +fn committee_cold_resign_setters_getters_test() { + let cred_key_hash = Credential::from_keyhash(&fake_key_hash(1)); + let cred_script_hash = Credential::from_scripthash(&fake_script_hash(2)); + let committee_cold_resign_1 = CommitteeColdResign::new(&cred_key_hash); + + let committee_cold_resign_2 = CommitteeColdResign::new(&cred_script_hash); + + assert_eq!( + committee_cold_resign_1.committee_cold_credential(), + cred_key_hash + ); + assert!(!committee_cold_resign_1.has_script_credentials()); + assert_eq!( + committee_cold_resign_2.committee_cold_credential(), + cred_script_hash + ); + assert!(committee_cold_resign_2.has_script_credentials()); +} + +#[test] +fn committee_hot_auth_setters_getters_test() { + let cold_cred_key_hash = Credential::from_keyhash(&fake_key_hash(1)); + let hot_cred_key_hash = Credential::from_keyhash(&fake_key_hash(1)); + let committee_hot_auth = + CommitteeHotAuth::new(&cold_cred_key_hash, &hot_cred_key_hash); + + assert_eq!( + committee_hot_auth.committee_cold_credential(), + cold_cred_key_hash + ); + assert_eq!( + committee_hot_auth.committee_hot_credential(), + hot_cred_key_hash + ); + assert!(!committee_hot_auth.has_script_credentials()); +} + +#[test] +fn drep_deregistration_setters_getters_test() { + let cred_key_hash = Credential::from_keyhash(&fake_key_hash(1)); + let cred_script_hash = Credential::from_scripthash(&fake_script_hash(2)); + let coin = Coin::from(100u32); + let drep_deregistration_1 = DRepDeregistration::new(&cred_key_hash, &coin); + + let drep_deregistration_2 = DRepDeregistration::new(&cred_script_hash, &coin); + + assert_eq!(drep_deregistration_1.voting_credential(), cred_key_hash); + assert_eq!(drep_deregistration_1.coin(), coin); + assert!(!drep_deregistration_1.has_script_credentials()); + assert_eq!(drep_deregistration_2.voting_credential(), cred_script_hash); + assert_eq!(drep_deregistration_2.coin(), coin); + assert!(drep_deregistration_2.has_script_credentials()); +} + +#[test] +fn drep_registration_setters_getters_test() { + let cred_key_hash = Credential::from_keyhash(&fake_key_hash(1)); + let cred_script_hash = Credential::from_scripthash(&fake_script_hash(2)); + let coin = Coin::from(100u32); + let drep_registration_1 = DRepRegistration::new(&cred_key_hash, &coin); + + let anchor = fake_anchor(); + let drep_registration_2 = DRepRegistration::new_with_anchor(&cred_script_hash, &coin, &anchor); + + assert_eq!(drep_registration_1.voting_credential(), cred_key_hash); + assert_eq!(drep_registration_1.coin(), coin); + assert_eq!(drep_registration_2.voting_credential(), cred_script_hash); + assert_eq!(drep_registration_2.coin(), coin); +} + +#[test] +fn drep_update_setters_getters_test() { + let cred_key_hash = Credential::from_keyhash(&fake_key_hash(1)); + let cred_script_hash = Credential::from_scripthash(&fake_script_hash(2)); + let drep_update_1 = DRepUpdate::new(&cred_key_hash); + + let anchor = fake_anchor(); + let drep_update_2 = DRepUpdate::new_with_anchor(&cred_script_hash, &anchor); + + assert_eq!(drep_update_1.voting_credential(), cred_key_hash); + assert_eq!(drep_update_1.anchor(), None); + assert_eq!(drep_update_2.voting_credential(), cred_script_hash); + assert_eq!(drep_update_2.anchor(), Some(anchor)); +} + +#[test] +fn genesis_key_delegation_setters_getters_test() { + let genesishash = fake_genesis_hash(1); + let genesis_delegate_hash = fake_genesis_delegate_hash(2); + let vrf_keyhash = fake_vrf_key_hash(3); + + let genesis_key_delegation = + GenesisKeyDelegation::new(&genesishash, &genesis_delegate_hash, &vrf_keyhash); + + assert_eq!(genesis_key_delegation.genesishash(), genesishash); + assert_eq!( + genesis_key_delegation.genesis_delegate_hash(), + genesis_delegate_hash + ); + assert_eq!(genesis_key_delegation.vrf_keyhash(), vrf_keyhash); +} + +#[test] +fn move_instantaneous_rewards_setters_getters_test() { + let mir_to_other_pot = + MoveInstantaneousReward::new_to_other_pot(MIRPot::Reserves, &Coin::from(100u32)); + + let mut rewards = MIRToStakeCredentials::new(); + rewards.insert( + &Credential::from_keyhash(&fake_key_hash(1)), + &DeltaCoin::new_i32(100), + ); + rewards.insert( + &Credential::from_keyhash(&fake_key_hash(2)), + &DeltaCoin::new_i32(200), + ); + + let mir_to_stake_credentials = + MoveInstantaneousReward::new_to_stake_creds(MIRPot::Treasury, &rewards); + + assert_eq!(mir_to_other_pot.kind(), MIRKind::ToOtherPot); + assert_eq!(mir_to_other_pot.pot(), MIRPot::Reserves); + assert_eq!(mir_to_other_pot.as_to_other_pot(), Some(Coin::from(100u32))); + assert_eq!(mir_to_other_pot.as_to_stake_creds(), None); + + assert_eq!(mir_to_stake_credentials.kind(), MIRKind::ToStakeCredentials); + assert_eq!(mir_to_stake_credentials.pot(), MIRPot::Treasury); + assert_eq!(mir_to_stake_credentials.as_to_other_pot(), None); + assert_eq!(mir_to_stake_credentials.as_to_stake_creds(), Some(rewards)); +} + +#[test] +fn pool_registration_setters_getters_test() { + let pool_params = fake_full_pool_params(); + let pool_registration = PoolRegistration::new(&pool_params); + + assert_eq!(pool_registration.pool_params(), pool_params); +} + +#[test] +fn pool_retirement_setters_getters_test() { + let pool_key_hash = fake_key_hash(1); + let epoch = Epoch::from(100u32); + let pool_retirement = PoolRetirement::new(&pool_key_hash, epoch); + + assert_eq!(pool_retirement.pool_keyhash(), pool_key_hash); + assert_eq!(pool_retirement.epoch(), epoch); +} + +#[test] +fn stake_and_vote_delegation_setters_getters_test() { + let cred_key_hash = Credential::from_keyhash(&fake_key_hash(1)); + let pool_key_hash = fake_key_hash(2); + let drep = DRep::new_always_no_confidence(); + let stake_and_vote_delegation = + StakeAndVoteDelegation::new(&cred_key_hash, &pool_key_hash, &drep); + + assert_eq!(stake_and_vote_delegation.stake_credential(), cred_key_hash); + assert_eq!(stake_and_vote_delegation.pool_keyhash(), pool_key_hash); + assert_eq!(stake_and_vote_delegation.drep(), drep); + assert_eq!(stake_and_vote_delegation.has_script_credentials(), false); +} + +#[test] +fn stake_delegation_setters_getters_test() { + let cred_key_hash = Credential::from_keyhash(&fake_key_hash(1)); + let pool_key_hash = fake_key_hash(2); + let stake_delegation = StakeDelegation::new(&cred_key_hash, &pool_key_hash); + assert_eq!(stake_delegation.stake_credential(), cred_key_hash); + assert_eq!(stake_delegation.pool_keyhash(), pool_key_hash); + assert_eq!(stake_delegation.has_script_credentials(), false); +} + +#[test] +fn stake_deregisration_setters_getters_test() { + let cred_key_hash = Credential::from_keyhash(&fake_key_hash(1)); + let stake_deregistration_1 = StakeDeregistration::new(&cred_key_hash); + + let coin = Coin::from(100u32); + let stake_deregistration_2 = StakeDeregistration::new_with_explicit_refund(&cred_key_hash, &coin); + + assert_eq!(stake_deregistration_1.stake_credential(), cred_key_hash); + assert_eq!(stake_deregistration_1.coin(), None); + assert_eq!(stake_deregistration_1.has_script_credentials(), false); + assert_eq!(stake_deregistration_2.stake_credential(), cred_key_hash); + assert_eq!(stake_deregistration_2.coin(), Some(coin)); + assert_eq!(stake_deregistration_2.has_script_credentials(), false); +} + +#[test] +fn stake_regisration_setters_getters_test() { + let cred_key_hash = Credential::from_keyhash(&fake_key_hash(1)); + let coin = Coin::from(100u32); + let stake_registration_1 = StakeRegistration::new(&cred_key_hash); + let stake_registration_2 = StakeRegistration::new_with_explicit_deposit(&cred_key_hash, &coin); + + assert_eq!(stake_registration_1.stake_credential(), cred_key_hash); + assert_eq!(stake_registration_1.coin(), None); + assert_eq!(stake_registration_2.stake_credential(), cred_key_hash); + assert_eq!(stake_registration_2.coin(), Some(coin)); +} + +#[test] +fn stake_registration_and_delegation_setters_getters_test() { + let cred_key_hash = Credential::from_keyhash(&fake_key_hash(1)); + let pool_key_hash = fake_key_hash(2); + let coin = Coin::from(100u32); + let stake_registration_and_delegation = + StakeRegistrationAndDelegation::new(&cred_key_hash, &pool_key_hash, &coin); + + assert_eq!( + stake_registration_and_delegation.stake_credential(), + cred_key_hash + ); + assert_eq!( + stake_registration_and_delegation.pool_keyhash(), + pool_key_hash + ); + assert_eq!(stake_registration_and_delegation.coin(), coin); +} + +#[test] +fn stake_vote_registration_and_delegation_setters_getters_test() { + let cred_key_hash = Credential::from_keyhash(&fake_key_hash(1)); + let pool_key_hash = fake_key_hash(2); + let drep = DRep::new_always_no_confidence(); + let coin = Coin::from(100u32); + let stake_vote_registration_and_delegation = + StakeVoteRegistrationAndDelegation::new(&cred_key_hash, &pool_key_hash, &drep, &coin); + + assert_eq!( + stake_vote_registration_and_delegation.stake_credential(), + cred_key_hash + ); + assert_eq!( + stake_vote_registration_and_delegation.pool_keyhash(), + pool_key_hash + ); + assert_eq!(stake_vote_registration_and_delegation.drep(), drep); + assert_eq!(stake_vote_registration_and_delegation.coin(), coin); + assert_eq!( + stake_vote_registration_and_delegation.has_script_credentials(), + false + ); +} + +#[test] +fn vote_delegation_setters_getters_test() { + let cred_key_hash = Credential::from_keyhash(&fake_key_hash(1)); + let drep = DRep::new_always_no_confidence(); + let vote_delegation = VoteDelegation::new(&cred_key_hash, &drep); + + assert_eq!(vote_delegation.stake_credential(), cred_key_hash); + assert_eq!(vote_delegation.drep(), drep); + assert_eq!(vote_delegation.has_script_credentials(), false); +} + +#[test] +fn vote_registration_and_delegation_setters_getters_test() { + let cred_key_hash = Credential::from_keyhash(&fake_key_hash(1)); + let drep = DRep::new_always_no_confidence(); + let coin = Coin::from(100u32); + let vote_registration_and_delegation = + VoteRegistrationAndDelegation::new(&cred_key_hash, &drep, &coin); + + assert_eq!( + vote_registration_and_delegation.stake_credential(), + cred_key_hash + ); + assert_eq!(vote_registration_and_delegation.drep(), drep); + assert_eq!(vote_registration_and_delegation.coin(), coin); + assert_eq!( + vote_registration_and_delegation.has_script_credentials(), + false + ); +} + +#[test] +fn certificates_deduplication_test() { + let mut certs = Certificates::new(); + let cert1 = Certificate::new_stake_registration(&StakeRegistration::new( + &Credential::from_keyhash(&fake_key_hash(1)), + )); + let cert2 = Certificate::new_stake_registration(&StakeRegistration::new( + &Credential::from_keyhash(&fake_key_hash(2)), + )); + let cert3 = Certificate::new_stake_registration(&StakeRegistration::new( + &Credential::from_keyhash(&fake_key_hash(1)), + )); + + assert_eq!(certs.len(), 0); + assert!(certs.add(&cert1)); + assert_eq!(certs.len(), 1); + assert!(certs.add(&cert2)); + assert_eq!(certs.len(), 2); + assert!(!certs.add(&cert3)); + assert_eq!(certs.len(), 2); + assert_eq!(certs.get(0), cert1); + assert_eq!(certs.get(1), cert2); + + let bytes = certs.to_bytes(); + let certs2 = Certificates::from_bytes(bytes).unwrap(); + assert_eq!(certs, certs2); +} diff --git a/rust/src/tests/protocol_types/fixed_tx.rs b/rust/src/tests/protocol_types/fixed_tx.rs new file mode 100644 index 00000000..cb9b09e0 --- /dev/null +++ b/rust/src/tests/protocol_types/fixed_tx.rs @@ -0,0 +1,66 @@ +use crate::*; +use hex; + +#[test] +fn simple_round_trip() { + let original_tx = FixedTransaction::from_hex("84a700818258208b9c96823c19f2047f32210a330434b3d163e194ea17b2b702c0667f6fea7a7a000d80018182581d6138fe1dd1d91221a199ff0dacf41fdd5b87506b533d00e70fae8dae8f1abfbac06a021a0002b645031a03962de305a1581de1b3cabd3914ef99169ace1e8b545b635f809caa35f8b6c8bc69ae48061abf4009040e80a100828258207dc05ac55cdfb9cc24571d491d3a3bdbd7d48489a916d27fce3ffe5c9af1b7f55840d7eda8457f1814fe3333b7b1916e3b034e6d480f97f4f286b1443ef72383279718a3a3fddf127dae0505b01a48fd9ffe0f52d9d8c46d02bcb85d1d106c13aa048258201b3d6e1236891a921abf1a3f90a9fb1b2568b1096b6cd6d3eaaeb0ef0ee0802f58401ce4658303c3eb0f2b9705992ccd62de30423ade90219e2c4cfc9eb488c892ea28ba3110f0c062298447f4f6365499d97d31207075f9815c3fe530bd9a927402f5f6").unwrap(); + let body = hex::decode("a700818258208b9c96823c19f2047f32210a330434b3d163e194ea17b2b702c0667f6fea7a7a000d80018182581d6138fe1dd1d91221a199ff0dacf41fdd5b87506b533d00e70fae8dae8f1abfbac06a021a0002b645031a03962de305a1581de1b3cabd3914ef99169ace1e8b545b635f809caa35f8b6c8bc69ae48061abf4009040e80").unwrap(); + let wit_set = hex::decode("a100828258207dc05ac55cdfb9cc24571d491d3a3bdbd7d48489a916d27fce3ffe5c9af1b7f55840d7eda8457f1814fe3333b7b1916e3b034e6d480f97f4f286b1443ef72383279718a3a3fddf127dae0505b01a48fd9ffe0f52d9d8c46d02bcb85d1d106c13aa048258201b3d6e1236891a921abf1a3f90a9fb1b2568b1096b6cd6d3eaaeb0ef0ee0802f58401ce4658303c3eb0f2b9705992ccd62de30423ade90219e2c4cfc9eb488c892ea28ba3110f0c062298447f4f6365499d97d31207075f9815c3fe530bd9a927402").unwrap(); + let tx = FixedTransaction::new(&body, &wit_set, true).unwrap(); + let tx2 = FixedTransaction::from_bytes(tx.to_bytes()).unwrap(); + + assert_eq!(body, tx2.raw_body()); + assert_eq!(wit_set, tx2.raw_witness_set()); + assert_eq!(tx.raw_body(), tx2.raw_body()); + assert_eq!(tx.raw_witness_set(), tx2.raw_witness_set()); + assert_eq!(tx.is_valid(), tx2.is_valid()); + assert_eq!(tx.to_bytes(), tx2.to_bytes()); + assert_eq!(tx.raw_body(), original_tx.raw_body()); + assert_eq!(tx.raw_witness_set(), original_tx.raw_witness_set()); + assert_eq!(tx.is_valid(), original_tx.is_valid()); + assert_eq!(tx.to_bytes(), original_tx.to_bytes()); +} + +#[test] +fn round_trip_via_tx() { + let casual_tx = Transaction::from_hex("84a700818258208b9c96823c19f2047f32210a330434b3d163e194ea17b2b702c0667f6fea7a7a000d80018182581d6138fe1dd1d91221a199ff0dacf41fdd5b87506b533d00e70fae8dae8f1abfbac06a021a0002b645031a03962de305a1581de1b3cabd3914ef99169ace1e8b545b635f809caa35f8b6c8bc69ae48061abf4009040e80a100828258207dc05ac55cdfb9cc24571d491d3a3bdbd7d48489a916d27fce3ffe5c9af1b7f55840d7eda8457f1814fe3333b7b1916e3b034e6d480f97f4f286b1443ef72383279718a3a3fddf127dae0505b01a48fd9ffe0f52d9d8c46d02bcb85d1d106c13aa048258201b3d6e1236891a921abf1a3f90a9fb1b2568b1096b6cd6d3eaaeb0ef0ee0802f58401ce4658303c3eb0f2b9705992ccd62de30423ade90219e2c4cfc9eb488c892ea28ba3110f0c062298447f4f6365499d97d31207075f9815c3fe530bd9a927402f5f6").unwrap(); + let body = hex::decode("a700818258208b9c96823c19f2047f32210a330434b3d163e194ea17b2b702c0667f6fea7a7a000d80018182581d6138fe1dd1d91221a199ff0dacf41fdd5b87506b533d00e70fae8dae8f1abfbac06a021a0002b645031a03962de305a1581de1b3cabd3914ef99169ace1e8b545b635f809caa35f8b6c8bc69ae48061abf4009040e80").unwrap(); + let wit_set = hex::decode("a100828258207dc05ac55cdfb9cc24571d491d3a3bdbd7d48489a916d27fce3ffe5c9af1b7f55840d7eda8457f1814fe3333b7b1916e3b034e6d480f97f4f286b1443ef72383279718a3a3fddf127dae0505b01a48fd9ffe0f52d9d8c46d02bcb85d1d106c13aa048258201b3d6e1236891a921abf1a3f90a9fb1b2568b1096b6cd6d3eaaeb0ef0ee0802f58401ce4658303c3eb0f2b9705992ccd62de30423ade90219e2c4cfc9eb488c892ea28ba3110f0c062298447f4f6365499d97d31207075f9815c3fe530bd9a927402").unwrap(); + let tx = FixedTransaction::new(&body, &wit_set, true).unwrap(); + let tx2 = Transaction::from_bytes(tx.to_bytes()).unwrap(); + + assert_eq!(casual_tx.body(), tx.body()); + assert_eq!(casual_tx.witness_set(), tx.witness_set()); + assert_eq!(casual_tx.is_valid(), tx.is_valid()); + assert_eq!(casual_tx, tx2); +} + +#[test] +fn round_trip_nonstandart_body() { + let original_tx = FixedTransaction::from_hex("84a7009F8258208b9c96823c19f2047f32210a330434b3d163e194ea17b2b702c0667f6fea7a7a00FF0d80018182581d6138fe1dd1d91221a199ff0dacf41fdd5b87506b533d00e70fae8dae8f1abfbac06a021a0002b645031a03962de305a1581de1b3cabd3914ef99169ace1e8b545b635f809caa35f8b6c8bc69ae48061abf4009040e80a100828258207dc05ac55cdfb9cc24571d491d3a3bdbd7d48489a916d27fce3ffe5c9af1b7f55840d7eda8457f1814fe3333b7b1916e3b034e6d480f97f4f286b1443ef72383279718a3a3fddf127dae0505b01a48fd9ffe0f52d9d8c46d02bcb85d1d106c13aa048258201b3d6e1236891a921abf1a3f90a9fb1b2568b1096b6cd6d3eaaeb0ef0ee0802f58401ce4658303c3eb0f2b9705992ccd62de30423ade90219e2c4cfc9eb488c892ea28ba3110f0c062298447f4f6365499d97d31207075f9815c3fe530bd9a927402f5f6").unwrap(); + let casual_tx = Transaction::from_hex("84a7009F8258208b9c96823c19f2047f32210a330434b3d163e194ea17b2b702c0667f6fea7a7a00FF0d80018182581d6138fe1dd1d91221a199ff0dacf41fdd5b87506b533d00e70fae8dae8f1abfbac06a021a0002b645031a03962de305a1581de1b3cabd3914ef99169ace1e8b545b635f809caa35f8b6c8bc69ae48061abf4009040e80a100828258207dc05ac55cdfb9cc24571d491d3a3bdbd7d48489a916d27fce3ffe5c9af1b7f55840d7eda8457f1814fe3333b7b1916e3b034e6d480f97f4f286b1443ef72383279718a3a3fddf127dae0505b01a48fd9ffe0f52d9d8c46d02bcb85d1d106c13aa048258201b3d6e1236891a921abf1a3f90a9fb1b2568b1096b6cd6d3eaaeb0ef0ee0802f58401ce4658303c3eb0f2b9705992ccd62de30423ade90219e2c4cfc9eb488c892ea28ba3110f0c062298447f4f6365499d97d31207075f9815c3fe530bd9a927402f5f6").unwrap(); + let body = hex::decode("a7009F8258208b9c96823c19f2047f32210a330434b3d163e194ea17b2b702c0667f6fea7a7a00FF0d80018182581d6138fe1dd1d91221a199ff0dacf41fdd5b87506b533d00e70fae8dae8f1abfbac06a021a0002b645031a03962de305a1581de1b3cabd3914ef99169ace1e8b545b635f809caa35f8b6c8bc69ae48061abf4009040e80").unwrap(); + let wit_set = hex::decode("a100828258207dc05ac55cdfb9cc24571d491d3a3bdbd7d48489a916d27fce3ffe5c9af1b7f55840d7eda8457f1814fe3333b7b1916e3b034e6d480f97f4f286b1443ef72383279718a3a3fddf127dae0505b01a48fd9ffe0f52d9d8c46d02bcb85d1d106c13aa048258201b3d6e1236891a921abf1a3f90a9fb1b2568b1096b6cd6d3eaaeb0ef0ee0802f58401ce4658303c3eb0f2b9705992ccd62de30423ade90219e2c4cfc9eb488c892ea28ba3110f0c062298447f4f6365499d97d31207075f9815c3fe530bd9a927402").unwrap(); + let tx = FixedTransaction::new(&body, &wit_set, true).unwrap(); + let tx2 = Transaction::from_bytes(tx.to_bytes()).unwrap(); + let tx3 = FixedTransaction::from_bytes(tx.to_bytes()).unwrap(); + + assert_eq!(casual_tx.body(), tx.body()); + assert_eq!(casual_tx.witness_set(), tx.witness_set()); + assert_eq!(casual_tx.is_valid(), tx.is_valid()); + + assert_eq!(body, tx3.raw_body()); + assert_eq!(wit_set, tx3.raw_witness_set()); + assert_eq!(tx.raw_body(), tx3.raw_body()); + assert_eq!(tx.raw_witness_set(), tx3.raw_witness_set()); + assert_eq!(tx.is_valid(), tx3.is_valid()); + assert_eq!(tx.to_bytes(), tx3.to_bytes()); + assert_eq!(tx.raw_body(), original_tx.raw_body()); + assert_eq!(tx.raw_witness_set(), original_tx.raw_witness_set()); + assert_eq!(tx.is_valid(), original_tx.is_valid()); + assert_eq!(tx.to_bytes(), original_tx.to_bytes()); + + assert_eq!(casual_tx, tx2); + + assert_ne!(tx2.to_bytes(), original_tx.to_bytes()); +} diff --git a/rust/src/tests/protocol_types/governance/common.rs b/rust/src/tests/protocol_types/governance/common.rs new file mode 100644 index 00000000..c88964b1 --- /dev/null +++ b/rust/src/tests/protocol_types/governance/common.rs @@ -0,0 +1,267 @@ +use crate::tests::fakes::{fake_anchor, fake_anchor_data_hash, fake_key_hash, fake_script_hash, fake_tx_hash}; +use crate::*; + +#[test] +fn drep_abstain_setters_getters_test() { + let drep = DRep::new_always_abstain(); + assert_eq!(drep.kind(), DRepKind::AlwaysAbstain); + assert_eq!(drep.to_key_hash(), None); + assert_eq!(drep.to_script_hash(), None); +} + +#[test] +fn drep_no_confidence_setters_getters_test() { + let drep = DRep::new_always_no_confidence(); + assert_eq!(drep.kind(), DRepKind::AlwaysNoConfidence); + assert_eq!(drep.to_key_hash(), None); + assert_eq!(drep.to_script_hash(), None); +} + +#[test] +fn drep_key_hash_setters_getters_test() { + let key_hash = fake_key_hash(1); + let drep = DRep::new_key_hash(&key_hash); + assert_eq!(drep.kind(), DRepKind::KeyHash); + assert_eq!(drep.to_key_hash(), Some(key_hash)); + assert_eq!(drep.to_script_hash(), None); +} + +#[test] +fn drep_script_hash_setters_getters_test() { + let script_hash = fake_script_hash(1); + let drep = DRep::new_script_hash(&script_hash); + assert_eq!(drep.kind(), DRepKind::ScriptHash); + assert_eq!(drep.to_key_hash(), None); + assert_eq!(drep.to_script_hash(), Some(script_hash)); +} + +#[test] +fn drep_from_cred_test() { + let key_hash = fake_key_hash(1); + let cred = Credential::from_keyhash(&key_hash); + let drep = DRep::new_from_credential(&cred); + assert_eq!(drep.kind(), DRepKind::KeyHash); + assert_eq!(drep.to_key_hash(), Some(key_hash)); + assert_eq!(drep.to_script_hash(), None); + + let script_hash = fake_script_hash(1); + let cred = Credential::from_scripthash(&script_hash); + let drep = DRep::new_from_credential(&cred); + assert_eq!(drep.kind(), DRepKind::ScriptHash); + assert_eq!(drep.to_key_hash(), None); + assert_eq!(drep.to_script_hash(), Some(script_hash)); +} + +#[test] +fn anchor_setters_getters_test() { + let data_hash = fake_anchor_data_hash(1); + let url = URL::new("https://example.com".to_string()).unwrap(); + let anchor = Anchor::new(&url, &data_hash); + assert_eq!(anchor.url(), url); + assert_eq!(anchor.anchor_data_hash(), data_hash); +} + +#[test] +fn governance_action_id_setters_getters_test() { + let index = 1; + let tx_hash = fake_tx_hash(1); + let governance_action_id = GovernanceActionId::new(&tx_hash, index); + assert_eq!(governance_action_id.transaction_id(), tx_hash); + assert_eq!(governance_action_id.index(), index); +} + +#[test] +fn governance_action_ids_setters_getters_test() { + let index_1 = 1; + let tx_hash_1 = fake_tx_hash(1); + let index_2 = 2; + let tx_hash_2 = fake_tx_hash(2); + let governance_action_id_1 = GovernanceActionId::new(&tx_hash_1, index_1); + let governance_action_id_2 = GovernanceActionId::new(&tx_hash_2, index_2); + let mut governance_action_ids = GovernanceActionIds::new(); + governance_action_ids.add(&governance_action_id_1); + governance_action_ids.add(&governance_action_id_2); + assert_eq!(governance_action_ids.len(), 2); + assert_eq!(governance_action_ids.get(0), Some(governance_action_id_1)); + assert_eq!(governance_action_ids.get(1), Some(governance_action_id_2)); + assert_eq!(governance_action_ids.get(2), None); +} + +#[test] +fn voter_drep_key_hash_setters_getters_test() { + let key_hash = fake_key_hash(1); + let voter = Voter::new_drep_credential(&Credential::from_keyhash(&key_hash)); + assert_eq!(voter.kind(), VoterKind::DRepKeyHash); + assert_eq!( + voter.to_drep_credential(), + Some(Credential::from_keyhash(&key_hash)) + ); + assert_eq!(voter.to_stake_pool_key_hash(), None); + assert_eq!(voter.to_constitutional_committee_hot_credential(), None); + assert_eq!(voter.has_script_credentials(), false); + assert_eq!(voter.to_key_hash(), Some(key_hash)); +} + +#[test] +fn voter_drep_script_hash_setters_getters_test() { + let script_hash = fake_script_hash(1); + let voter = Voter::new_drep_credential(&Credential::from_scripthash(&script_hash)); + assert_eq!(voter.kind(), VoterKind::DRepScriptHash); + assert_eq!( + voter.to_drep_credential(), + Some(Credential::from_scripthash(&script_hash)) + ); + assert_eq!(voter.to_stake_pool_key_hash(), None); + assert_eq!(voter.to_constitutional_committee_hot_credential(), None); + assert_eq!(voter.has_script_credentials(), true); + assert_eq!(voter.to_key_hash(), None); +} + +#[test] +fn voter_constitutional_committee_hot_key_hash_setters_getters_test() { + let key_hash = fake_key_hash(1); + let voter = Voter::new_constitutional_committee_hot_credential(&Credential::from_keyhash(&key_hash)); + assert_eq!(voter.kind(), VoterKind::ConstitutionalCommitteeHotKeyHash); + assert_eq!( + voter.to_constitutional_committee_hot_credential(), + Some(Credential::from_keyhash(&key_hash)) + ); + assert_eq!(voter.to_stake_pool_key_hash(), None); + assert_eq!(voter.to_drep_credential(), None); + assert_eq!(voter.has_script_credentials(), false); + assert_eq!(voter.to_key_hash(), Some(key_hash)); +} + +#[test] +fn voter_constitutional_committee_hot_script_hash_setters_getters_test() { + let script_hash = fake_script_hash(1); + let voter = + Voter::new_constitutional_committee_hot_credential(&Credential::from_scripthash(&script_hash)); + assert_eq!( + voter.kind(), + VoterKind::ConstitutionalCommitteeHotScriptHash + ); + assert_eq!( + voter.to_constitutional_committee_hot_credential(), + Some(Credential::from_scripthash(&script_hash)) + ); + assert_eq!(voter.to_stake_pool_key_hash(), None); + assert_eq!(voter.to_drep_credential(), None); + assert_eq!(voter.has_script_credentials(), true); + assert_eq!(voter.to_key_hash(), None); +} + +#[test] +fn voter_staking_pool_key_hash_setters_getters_test() { + let key_hash = fake_key_hash(1); + let voter = Voter::new_stake_pool_key_hash(&key_hash); + assert_eq!(voter.kind(), VoterKind::StakingPoolKeyHash); + assert_eq!(voter.to_stake_pool_key_hash(), Some(key_hash.clone())); + assert_eq!(voter.to_constitutional_committee_hot_credential(), None); + assert_eq!(voter.to_drep_credential(), None); + assert_eq!(voter.has_script_credentials(), false); + assert_eq!(voter.to_key_hash(), Some(key_hash)); +} + +#[test] +fn voters_setters_getters_test() { + let key_hash_1 = fake_key_hash(1); + let voter_1 = Voter::new_stake_pool_key_hash(&key_hash_1); + let key_hash_2 = fake_key_hash(2); + let voter_2 = Voter::new_stake_pool_key_hash(&key_hash_2); + let mut voters = Voters::new(); + voters.add(&voter_1); + voters.add(&voter_2); + assert_eq!(voters.len(), 2); + assert_eq!(voters.get(0), Some(voter_1)); + assert_eq!(voters.get(1), Some(voter_2)); + assert_eq!(voters.get(2), None); +} + +#[test] +fn voting_procedure_setters_getters_test() { + let yes_procedure = VotingProcedure::new(VoteKind::Yes); + assert_eq!(yes_procedure.vote_kind(), VoteKind::Yes); + assert_eq!(yes_procedure.anchor(), None); + + let no_procedure = VotingProcedure::new(VoteKind::No); + assert_eq!(no_procedure.vote_kind(), VoteKind::No); + assert_eq!(no_procedure.anchor(), None); + + let abstain_procedure = VotingProcedure::new(VoteKind::Abstain); + assert_eq!(abstain_procedure.vote_kind(), VoteKind::Abstain); + assert_eq!(abstain_procedure.anchor(), None); +} + +#[test] +fn voting_procedure_with_anchor_setters_getters_test() { + let anchor = fake_anchor(); + let yes_procedure = VotingProcedure::new_with_anchor(VoteKind::Yes, &anchor); + assert_eq!(yes_procedure.vote_kind(), VoteKind::Yes); + assert_eq!(yes_procedure.anchor(), Some(anchor.clone())); + + let no_procedure = VotingProcedure::new_with_anchor(VoteKind::No, &anchor); + assert_eq!(no_procedure.vote_kind(), VoteKind::No); + assert_eq!(no_procedure.anchor(), Some(anchor.clone())); + + let abstain_procedure = VotingProcedure::new_with_anchor(VoteKind::Abstain, &anchor); + assert_eq!(abstain_procedure.vote_kind(), VoteKind::Abstain); + assert_eq!(abstain_procedure.anchor(), Some(anchor)); +} + +#[test] +fn voting_procedures_setters_getters_test() { + let key_hash_1 = fake_key_hash(1); + let voter_1 = Voter::new_stake_pool_key_hash(&key_hash_1); + let key_hash_2 = fake_key_hash(2); + let voter_2 = Voter::new_stake_pool_key_hash(&key_hash_2); + let governance_action_id_1 = GovernanceActionId::new(&fake_tx_hash(1), 1); + let governance_action_id_2 = GovernanceActionId::new(&fake_tx_hash(2), 2); + let governance_action_id_3 = GovernanceActionId::new(&fake_tx_hash(3), 3); + let voting_procedure_1 = VotingProcedure::new(VoteKind::Yes); + let voting_procedure_2 = VotingProcedure::new(VoteKind::No); + let voting_procedure_3 = VotingProcedure::new(VoteKind::Abstain); + let mut voting_procedures = VotingProcedures::new(); + voting_procedures.insert(&voter_1, &governance_action_id_1, &voting_procedure_1); + voting_procedures.insert(&voter_2, &governance_action_id_2, &voting_procedure_2); + voting_procedures.insert(&voter_2, &governance_action_id_3, &voting_procedure_3); + + assert_eq!( + voting_procedures.get(&voter_1, &governance_action_id_1), + Some(voting_procedure_1) + ); + assert_eq!( + voting_procedures.get(&voter_2, &governance_action_id_2), + Some(voting_procedure_2) + ); + assert_eq!( + voting_procedures.get(&voter_2, &governance_action_id_3), + Some(voting_procedure_3) + ); + assert_eq!( + voting_procedures.get(&voter_1, &governance_action_id_2), + None + ); + assert_eq!( + voting_procedures.get(&voter_1, &governance_action_id_3), + None + ); + assert_eq!( + voting_procedures.get(&voter_2, &governance_action_id_1), + None + ); + + let voters = voting_procedures.get_voters(); + assert_eq!(voters.len(), 2); + assert!(voters.0.contains(&voter_1)); + assert!(voters.0.contains(&voter_2)); + + let governance_action_ids_1 = voting_procedures.get_governance_action_ids_by_voter(&voter_1); + assert_eq!(governance_action_ids_1.len(), 1); + assert!(governance_action_ids_1.0.contains(&governance_action_id_1)); + + let governance_action_ids_2 = voting_procedures.get_governance_action_ids_by_voter(&voter_2); + assert_eq!(governance_action_ids_2.len(), 2); + assert!(governance_action_ids_2.0.contains(&governance_action_id_2)); + assert!(governance_action_ids_2.0.contains(&governance_action_id_3)); +} diff --git a/rust/src/tests/protocol_types/governance/mod.rs b/rust/src/tests/protocol_types/governance/mod.rs new file mode 100644 index 00000000..70de6435 --- /dev/null +++ b/rust/src/tests/protocol_types/governance/mod.rs @@ -0,0 +1,2 @@ +mod common; +mod proposals; diff --git a/rust/src/tests/protocol_types/governance/proposals.rs b/rust/src/tests/protocol_types/governance/proposals.rs new file mode 100644 index 00000000..7d7b3056 --- /dev/null +++ b/rust/src/tests/protocol_types/governance/proposals.rs @@ -0,0 +1,200 @@ +use crate::tests::fakes::{fake_full_protocol_param_update, fake_action_id, fake_anchor, fake_key_hash, fake_reward_address, fake_script_hash}; +use crate::*; +use itertools::Itertools; + +#[test] +fn committee_setters_getters_test() { + let threshold = UnitInterval::new(&BigNum::from(1u32), &BigNum::from(2u32)); + let mut committee = Committee::new(&threshold); + let cred_1 = Credential::from_keyhash(&fake_key_hash(1)); + let epoch_1 = Epoch::from(100u32); + let cred_2 = Credential::from_scripthash(&fake_script_hash(2)); + let epoch_2 = Epoch::from(200u32); + let cred_3 = Credential::from_scripthash(&fake_script_hash(3)); + + committee.add_member(&cred_1, epoch_1); + committee.add_member(&cred_2, epoch_2); + + let keys = committee.members_keys(); + assert_eq!(committee.quorum_threshold(), threshold); + assert_eq!(keys.len(), 2); + assert!(keys.contains(&cred_1)); + assert!(keys.contains(&cred_2)); + assert_eq!(committee.get_member_epoch(&cred_1), Some(epoch_1)); + assert_eq!(committee.get_member_epoch(&cred_2), Some(epoch_2)); + assert_eq!(committee.get_member_epoch(&cred_3), None); +} + +#[test] +fn constitution_setters_getters_test() { + let anchor = fake_anchor(); + let constitution = Constitution::new(&anchor); + assert_eq!(constitution.anchor(), anchor); + assert_eq!(constitution.script_hash(), None); + + let script_hash = fake_script_hash(1); + let constitution = Constitution::new_with_script_hash(&anchor, &script_hash); + assert_eq!(constitution.anchor(), anchor); + assert_eq!(constitution.script_hash(), Some(script_hash)); +} + +#[test] +fn hard_fork_initiation_action_setters_getters_test() { + let protocol_version = ProtocolVersion::new(1, 2); + let proposal = HardForkInitiationAction::new(&protocol_version); + let action_id = fake_action_id(); + let proposal_with_action_id = + HardForkInitiationAction::new_with_action_id(&action_id, &protocol_version); + assert_eq!(proposal.gov_action_id(), None); + assert_eq!(proposal.protocol_version(), protocol_version); + assert_eq!(proposal_with_action_id.gov_action_id(), Some(action_id)); + assert_eq!(proposal_with_action_id.protocol_version(), protocol_version); +} + +#[test] +fn new_committee_action_setters_getters_test() { + let action_id = fake_action_id(); + let committee = Committee::new(&UnitInterval::new(&BigNum::from(1u32), &BigNum::from(2u32))); + let members_to_remove = Credentials::from_iter( + vec![ + Credential::from_keyhash(&fake_key_hash(1)), + Credential::from_keyhash(&fake_key_hash(2)), + ] + .into_iter(), + ); + + let proposal = UpdateCommitteeAction::new(&committee, &members_to_remove); + let proposal_with_action_id = + UpdateCommitteeAction::new_with_action_id(&action_id, &committee, &members_to_remove); + assert_eq!(proposal.gov_action_id(), None); + assert_eq!(proposal.committee(), committee); + assert_eq!(proposal.members_to_remove(), members_to_remove); + assert_eq!(proposal_with_action_id.gov_action_id(), Some(action_id)); + assert_eq!(proposal_with_action_id.committee(), committee); + assert_eq!( + proposal_with_action_id.members_to_remove(), + members_to_remove + ); +} + +#[test] +fn new_constitution_action_setters_getters_test() { + let action_id = fake_action_id(); + let constitution = Constitution::new(&fake_anchor()); + let proposal = NewConstitutionAction::new(&constitution); + let proposal_with_action_id = + NewConstitutionAction::new_with_action_id(&action_id, &constitution); + assert_eq!(proposal.gov_action_id(), None); + assert_eq!(proposal.constitution(), constitution); + assert_eq!(proposal_with_action_id.gov_action_id(), Some(action_id)); + assert_eq!(proposal_with_action_id.constitution(), constitution); +} + +#[test] +fn no_confidence_action_setters_getters_test() { + let action_id = fake_action_id(); + let proposal = NoConfidenceAction::new(); + let proposal_with_action_id = NoConfidenceAction::new_with_action_id(&action_id); + assert_eq!(proposal.gov_action_id(), None); + assert_eq!(proposal_with_action_id.gov_action_id(), Some(action_id)); +} + +#[test] +fn parameter_change_action_setters_getters_test() { + let protocol_params = fake_full_protocol_param_update(); + let action_id = fake_action_id(); + let policy_hash = fake_script_hash(1); + let proposal = ParameterChangeAction::new(&protocol_params); + let proposal_with_action_id = ParameterChangeAction::new_with_policy_hash_and_action_id( + &action_id, + &protocol_params, + &policy_hash, + ); + assert_eq!(proposal.gov_action_id(), None); + assert_eq!(proposal.protocol_param_updates(), protocol_params); + assert_eq!(proposal_with_action_id.gov_action_id(), Some(action_id)); + assert_eq!(proposal_with_action_id.policy_hash(), Some(policy_hash)); +} + +#[test] +fn treasury_withdrawals_setters_getters_test() { + let mut withdrawals = TreasuryWithdrawals::new(); + let addr1 = RewardAddress::new(1, &Credential::from_keyhash(&fake_key_hash(1))); + let addr2 = RewardAddress::new(1, &Credential::from_keyhash(&fake_key_hash(2))); + let coin1 = Coin::from(100u32); + let coin2 = Coin::from(200u32); + withdrawals.insert(&addr1, &coin1); + withdrawals.insert(&addr2, &coin2); + + let keys = withdrawals.keys(); + assert_eq!(keys.len(), 2); + assert!(keys.0.iter().contains(&addr1)); + assert!(keys.0.iter().contains(&addr2)); + assert_eq!(withdrawals.get(&addr1), Some(coin1)); + assert_eq!(withdrawals.get(&addr2), Some(coin2)); +} + +#[test] +fn treasury_withdrawals_action() { + let mut withdrawals = TreasuryWithdrawals::new(); + let addr = RewardAddress::new(1, &Credential::from_keyhash(&fake_key_hash(1))); + let coin = Coin::from(100u32); + withdrawals.insert(&addr, &coin); + let proposal = TreasuryWithdrawalsAction::new(&withdrawals); + assert_eq!(proposal.withdrawals(), withdrawals); +} + +#[test] +fn voting_proposals_setters_getters_test() { + let mut proposals = VotingProposals::new(); + let no_confidence_action = NoConfidenceAction::new(); + let parameter_change_action = ParameterChangeAction::new(&fake_full_protocol_param_update()); + + let proposal1 = VotingProposal::new( + &GovernanceAction::new_no_confidence_action(&no_confidence_action), + &fake_anchor(), + &fake_reward_address(1), + &Coin::from(100u32), + ); + let proposal2 = VotingProposal::new( + &GovernanceAction::new_parameter_change_action(¶meter_change_action), + &fake_anchor(), + &fake_reward_address(2), + &Coin::from(100u32), + ); + proposals.add(&proposal1); + proposals.add(&proposal2); + assert_eq!(proposals.len(), 2); + assert_eq!(proposals.get(0), proposal1); + assert_eq!(proposals.get(1), proposal2); +} + +#[test] +fn voting_proposals_deduplication_test() { + let mut proposals = VotingProposals::new(); + let no_confidence_action = NoConfidenceAction::new(); + let parameter_change_action = ParameterChangeAction::new(&fake_full_protocol_param_update()); + + let proposal1 = VotingProposal::new( + &GovernanceAction::new_no_confidence_action(&no_confidence_action), + &fake_anchor(), + &fake_reward_address(1), + &Coin::from(100u32), + ); + let proposal2 = VotingProposal::new( + &GovernanceAction::new_parameter_change_action(¶meter_change_action), + &fake_anchor(), + &fake_reward_address(2), + &Coin::from(100u32), + ); + proposals.add(&proposal1); + proposals.add(&proposal2); + proposals.add(&proposal1); + assert_eq!(proposals.len(), 2); + assert_eq!(proposals.get(0), proposal1); + assert_eq!(proposals.get(1), proposal2); + + let bytes = proposals.to_bytes(); + let proposals_decoded = VotingProposals::from_bytes(bytes).unwrap(); + assert_eq!(proposals, proposals_decoded); +} \ No newline at end of file diff --git a/rust/src/tests/protocol_types/mod.rs b/rust/src/tests/protocol_types/mod.rs new file mode 100644 index 00000000..a1f44ad9 --- /dev/null +++ b/rust/src/tests/protocol_types/mod.rs @@ -0,0 +1,4 @@ +mod certificates; +mod fixed_tx; +mod governance; +mod protocol_param_update; diff --git a/rust/src/tests/protocol_types/protocol_param_update.rs b/rust/src/tests/protocol_types/protocol_param_update.rs new file mode 100644 index 00000000..903bf8bd --- /dev/null +++ b/rust/src/tests/protocol_types/protocol_param_update.rs @@ -0,0 +1,251 @@ +use crate::*; + +#[test] +fn ppu_setters_getters_test() { + let mut ppu = ProtocolParamUpdate::new(); + + assert!(ppu.max_tx_size().is_none()); + let max_tx_size = 1234; + ppu.set_max_tx_size(max_tx_size); + assert_eq!(ppu.max_tx_size().unwrap(), max_tx_size); + + assert!(ppu.max_block_body_size().is_none()); + let max_block_body_size = 5678; + ppu.set_max_block_body_size(max_block_body_size); + assert_eq!(ppu.max_block_body_size().unwrap(), max_block_body_size); + + assert!(ppu.max_block_header_size().is_none()); + let max_block_header_size = 91011; + ppu.set_max_block_header_size(max_block_header_size); + assert_eq!(ppu.max_block_header_size().unwrap(), max_block_header_size); + + assert!(ppu.minfee_a().is_none()); + let minfee_a = Coin::from(1u32); + ppu.set_minfee_a(&minfee_a); + assert_eq!(ppu.minfee_a().unwrap(), minfee_a); + + assert!(ppu.minfee_b().is_none()); + let minfee_b = Coin::from(2u32); + ppu.set_minfee_b(&minfee_b); + assert_eq!(ppu.minfee_b().unwrap(), minfee_b); + + assert!(ppu.key_deposit().is_none()); + let key_deposit = Coin::from(3u32); + ppu.set_key_deposit(&key_deposit); + assert_eq!(ppu.key_deposit().unwrap(), key_deposit); + + assert!(ppu.pool_deposit().is_none()); + let pool_deposit = Coin::from(4u32); + ppu.set_pool_deposit(&pool_deposit); + assert_eq!(ppu.pool_deposit().unwrap(), pool_deposit); + + assert!(ppu.max_epoch().is_none()); + let max_epoch = 5; + ppu.set_max_epoch(max_epoch); + assert_eq!(ppu.max_epoch().unwrap(), max_epoch); + + assert!(ppu.n_opt().is_none()); + let n_opt = 6; + ppu.set_n_opt(n_opt); + assert_eq!(ppu.n_opt().unwrap(), n_opt); + + assert!(ppu.pool_pledge_influence().is_none()); + let pool_pledge_influence = UnitInterval::new(&BigNum::from(7u32), &BigNum::from(77u32)); + ppu.set_pool_pledge_influence(&pool_pledge_influence); + assert_eq!(ppu.pool_pledge_influence().unwrap(), pool_pledge_influence); + + assert!(ppu.expansion_rate().is_none()); + let expansion_rate = UnitInterval::new(&BigNum::from(8u32), &BigNum::from(9u32)); + ppu.set_expansion_rate(&expansion_rate); + assert_eq!(ppu.expansion_rate().unwrap(), expansion_rate); + + assert!(ppu.treasury_growth_rate().is_none()); + let treasury_growth_rate = UnitInterval::new(&BigNum::from(10u32), &BigNum::from(11u32)); + ppu.set_treasury_growth_rate(&treasury_growth_rate); + assert_eq!(ppu.treasury_growth_rate().unwrap(), treasury_growth_rate); + + assert!(ppu.protocol_version().is_none()); + let protocol_version = ProtocolVersion::new(12u32, 13u32); + ppu.set_protocol_version(&protocol_version); + assert_eq!(ppu.protocol_version().unwrap(), protocol_version); + + assert!(ppu.min_pool_cost().is_none()); + let min_pool_cost = Coin::from(14u32); + ppu.set_min_pool_cost(&min_pool_cost); + assert_eq!(ppu.min_pool_cost().unwrap(), min_pool_cost); + + assert!(ppu.ada_per_utxo_byte().is_none()); + let ada_per_utxo_byte = Coin::from(15u32); + ppu.set_ada_per_utxo_byte(&ada_per_utxo_byte); + assert_eq!(ppu.ada_per_utxo_byte().unwrap(), ada_per_utxo_byte); + + assert!(ppu.cost_models().is_none()); + let cost_models = TxBuilderConstants::plutus_vasil_cost_models(); + ppu.set_cost_models(&cost_models); + assert_eq!(ppu.cost_models().unwrap(), cost_models); + + assert!(ppu.execution_costs().is_none()); + let execution_costs = ExUnitPrices::new( + &SubCoin::new(&BigNum::from(16u32), &BigNum::from(17u32)), + &SubCoin::new(&BigNum::from(18u32), &BigNum::from(19u32)), + ); + ppu.set_execution_costs(&execution_costs); + assert_eq!(ppu.execution_costs().unwrap(), execution_costs); + + assert!(ppu.max_tx_ex_units().is_none()); + let max_tx_ex_units = ExUnits::new(&BigNum::from(20u32), &BigNum::from(21u32)); + ppu.set_max_tx_ex_units(&max_tx_ex_units); + assert_eq!(ppu.max_tx_ex_units().unwrap(), max_tx_ex_units); + + assert!(ppu.max_block_ex_units().is_none()); + let max_block_ex_units = ExUnits::new(&BigNum::from(22u32), &BigNum::from(23u32)); + ppu.set_max_block_ex_units(&max_block_ex_units); + assert_eq!(ppu.max_block_ex_units().unwrap(), max_block_ex_units); + + assert!(ppu.max_value_size().is_none()); + let max_value_size = 24; + ppu.set_max_value_size(max_value_size); + assert_eq!(ppu.max_value_size().unwrap(), max_value_size); + + assert!(ppu.collateral_percentage().is_none()); + let collateral_percentage = 25; + ppu.set_collateral_percentage(collateral_percentage); + assert_eq!(ppu.collateral_percentage().unwrap(), collateral_percentage); + + assert!(ppu.max_collateral_inputs().is_none()); + let max_collateral_inputs = 25; + ppu.set_max_collateral_inputs(max_collateral_inputs); + assert_eq!(ppu.max_collateral_inputs().unwrap(), max_collateral_inputs); + + assert!(ppu.pool_voting_thresholds().is_none()); + let pool_voting_thresholds = PoolVotingThresholds::new( + &UnitInterval::new(&BigNum::from(26u32), &BigNum::from(27u32)), + &UnitInterval::new(&BigNum::from(28u32), &BigNum::from(29u32)), + &UnitInterval::new(&BigNum::from(30u32), &BigNum::from(31u32)), + &UnitInterval::new(&BigNum::from(40u32), &BigNum::from(41u32)), + &UnitInterval::new(&BigNum::from(50u32), &BigNum::from(51u32)), + ); + ppu.set_pool_voting_thresholds(&pool_voting_thresholds); + assert_eq!(ppu.pool_voting_thresholds().unwrap(), pool_voting_thresholds); + + assert!(ppu.drep_voting_thresholds().is_none()); + let drep_voting_thresholds = DRepVotingThresholds::new( + &UnitInterval::new(&BigNum::from(26u32), &BigNum::from(27u32)), + &UnitInterval::new(&BigNum::from(28u32), &BigNum::from(29u32)), + &UnitInterval::new(&BigNum::from(30u32), &BigNum::from(31u32)), + &UnitInterval::new(&BigNum::from(40u32), &BigNum::from(41u32)), + &UnitInterval::new(&BigNum::from(50u32), &BigNum::from(51u32)), + &UnitInterval::new(&BigNum::from(60u32), &BigNum::from(61u32)), + &UnitInterval::new(&BigNum::from(66u32), &BigNum::from(65u32)), + &UnitInterval::new(&BigNum::from(70u32), &BigNum::from(71u32)), + &UnitInterval::new(&BigNum::from(77u32), &BigNum::from(75u32)), + &UnitInterval::new(&BigNum::from(80u32), &BigNum::from(81u32)), + ); + ppu.set_drep_voting_thresholds(&drep_voting_thresholds); + assert_eq!(ppu.drep_voting_thresholds().unwrap(), drep_voting_thresholds); + + assert!(ppu.min_committee_size().is_none()); + let min_committee_size = 32; + ppu.set_min_committee_size(min_committee_size); + assert_eq!(ppu.min_committee_size().unwrap(), min_committee_size); + + assert!(ppu.committee_term_limit().is_none()); + let committee_term_limit = 33; + ppu.set_committee_term_limit(committee_term_limit); + assert_eq!(ppu.committee_term_limit().unwrap(), committee_term_limit); + + assert!(ppu.governance_action_validity_period().is_none()); + let governance_action_validity_period = 34; + ppu.set_governance_action_validity_period(governance_action_validity_period); + assert_eq!(ppu.governance_action_validity_period().unwrap(), governance_action_validity_period); + + assert!(ppu.governance_action_deposit().is_none()); + let governance_action_deposit = Coin::from(35u32); + ppu.set_governance_action_deposit(&governance_action_deposit); + assert_eq!(ppu.governance_action_deposit().unwrap(), governance_action_deposit); + + assert!(ppu.drep_deposit().is_none()); + let drep_deposit = Coin::from(36u32); + ppu.set_drep_deposit(&drep_deposit); + assert_eq!(ppu.drep_deposit().unwrap(), drep_deposit); + + assert!(ppu.drep_inactivity_period().is_none()); + let drep_inactivity_period = 37; + ppu.set_drep_inactivity_period(drep_inactivity_period); + assert_eq!(ppu.drep_inactivity_period().unwrap(), drep_inactivity_period); + + assert!(ppu.ref_script_coins_per_byte().is_none()); + let ref_script_coins_per_byte = UnitInterval::new(&BigNum::from(38u32), &BigNum::from(39u32)); + ppu.set_ref_script_coins_per_byte(&ref_script_coins_per_byte); + assert_eq!(ppu.ref_script_coins_per_byte().unwrap(), ref_script_coins_per_byte); + + //since it is deprecated + assert!(ppu.d().is_none()); +} + +#[test] +fn pool_voting_thresholds_test() { + // Creating unit intervals for testing + let motion_no_confidence = UnitInterval::new(&BigNum::from(1u32), &BigNum::from(100u32)); + let committee_normal = UnitInterval::new(&BigNum::from(2u32), &BigNum::from(100u32)); + let committee_no_confidence = UnitInterval::new(&BigNum::from(3u32), &BigNum::from(100u32)); + let hard_fork_initiation = UnitInterval::new(&BigNum::from(4u32), &BigNum::from(100u32)); + let security_relevant_threshold = UnitInterval::new(&BigNum::from(5u32), &BigNum::from(100u32)); + + // Creating a new PoolVotingThresholds instance + let pvt = PoolVotingThresholds::new( + &motion_no_confidence, + &committee_normal, + &committee_no_confidence, + &hard_fork_initiation, + &security_relevant_threshold, + ); + + // Asserting that the getters return the expected values + assert_eq!(pvt.motion_no_confidence(), motion_no_confidence); + assert_eq!(pvt.committee_normal(), committee_normal); + assert_eq!(pvt.committee_no_confidence(), committee_no_confidence); + assert_eq!(pvt.hard_fork_initiation(), hard_fork_initiation); + assert_eq!(pvt.security_relevant_threshold(), security_relevant_threshold); +} + +#[test] +fn drep_voting_thresholds_test() { + // Creating unit intervals for testing + let motion_no_confidence = UnitInterval::new(&BigNum::from(1u32), &BigNum::from(100u32)); + let committee_normal = UnitInterval::new(&BigNum::from(2u32), &BigNum::from(100u32)); + let committee_no_confidence = UnitInterval::new(&BigNum::from(3u32), &BigNum::from(100u32)); + let update_constitution = UnitInterval::new(&BigNum::from(4u32), &BigNum::from(100u32)); + let hard_fork_initiation = UnitInterval::new(&BigNum::from(5u32), &BigNum::from(100u32)); + let pp_network_group = UnitInterval::new(&BigNum::from(6u32), &BigNum::from(100u32)); + let pp_economic_group = UnitInterval::new(&BigNum::from(7u32), &BigNum::from(100u32)); + let pp_technical_group = UnitInterval::new(&BigNum::from(8u32), &BigNum::from(100u32)); + let pp_governance_group = UnitInterval::new(&BigNum::from(9u32), &BigNum::from(100u32)); + let treasury_withdrawal = UnitInterval::new(&BigNum::from(10u32), &BigNum::from(100u32)); + + // Creating a new DRepVotingThresholds instance + let dvt = DRepVotingThresholds::new( + &motion_no_confidence, + &committee_normal, + &committee_no_confidence, + &update_constitution, + &hard_fork_initiation, + &pp_network_group, + &pp_economic_group, + &pp_technical_group, + &pp_governance_group, + &treasury_withdrawal, + ); + + // Asserting that the getters return the expected values + assert_eq!(dvt.motion_no_confidence(), motion_no_confidence); + assert_eq!(dvt.committee_normal(), committee_normal); + assert_eq!(dvt.committee_no_confidence(), committee_no_confidence); + assert_eq!(dvt.update_constitution(), update_constitution); + assert_eq!(dvt.hard_fork_initiation(), hard_fork_initiation); + assert_eq!(dvt.pp_network_group(), pp_network_group); + assert_eq!(dvt.pp_economic_group(), pp_economic_group); + assert_eq!(dvt.pp_technical_group(), pp_technical_group); + assert_eq!(dvt.pp_governance_group(), pp_governance_group); + assert_eq!(dvt.treasury_withdrawal(), treasury_withdrawal); +} \ No newline at end of file diff --git a/rust/src/tests/serialization/certificates.rs b/rust/src/tests/serialization/certificates.rs new file mode 100644 index 00000000..404afaf9 --- /dev/null +++ b/rust/src/tests/serialization/certificates.rs @@ -0,0 +1,464 @@ +use crate::tests::fakes::{fake_anchor, fake_anchor_data_hash, fake_genesis_delegate_hash, fake_genesis_hash, fake_key_hash, fake_pool_metadata_hash, fake_script_hash, fake_vrf_key_hash}; +use crate::*; + +macro_rules! to_from_test { + ($cert_type: ty, $variable_name: ident, $variable_wrapped_name: ident) => { + let json = $variable_name.to_json().unwrap(); + let cbor = $variable_name.to_bytes(); + let hex_cbor = $variable_name.to_hex(); + + assert_eq!($variable_name, <$cert_type>::from_json(&json).unwrap()); + assert_eq!($variable_name, <$cert_type>::from_bytes(cbor).unwrap()); + assert_eq!($variable_name, <$cert_type>::from_hex(&hex_cbor).unwrap()); + + let json_wrapped = $variable_wrapped_name.to_json().unwrap(); + let cbor_wrapped = $variable_wrapped_name.to_bytes(); + let hex_cbor_wrapped = $variable_wrapped_name.to_hex(); + + assert_eq!( + $variable_wrapped_name, + Certificate::from_json(&json_wrapped).unwrap() + ); + assert_eq!( + $variable_wrapped_name, + Certificate::from_bytes(cbor_wrapped).unwrap() + ); + assert_eq!( + $variable_wrapped_name, + Certificate::from_hex(&hex_cbor_wrapped).unwrap() + ); + }; +} + +#[test] +fn committee_cold_resign_key_hash_ser_round_trip() { + let cert = CommitteeColdResign::new(&Credential::from_keyhash(&fake_key_hash(1))); + let cert_wrapped = Certificate::new_committee_cold_resign(&cert); + to_from_test!(CommitteeColdResign, cert, cert_wrapped); + assert_eq!( + cert, + cert_wrapped.as_committee_cold_resign().unwrap() + ); +} + +#[test] +fn committee_cold_resign_with_anchor_ser_round_trip() { + let anchor = fake_anchor(); + let cert = + CommitteeColdResign::new_with_anchor(&Credential::from_keyhash(&fake_key_hash(1)), &anchor); + let cert_wrapped = Certificate::new_committee_cold_resign(&cert); + to_from_test!(CommitteeColdResign, cert, cert_wrapped); + assert_eq!( + cert, + cert_wrapped.as_committee_cold_resign().unwrap() + ); +} + +#[test] +fn committee_cold_resign_script_hash_ser_round_trip() { + let cert = CommitteeColdResign::new(&Credential::from_scripthash(&fake_script_hash(1))); + let cert_wrapped = Certificate::new_committee_cold_resign(&cert); + to_from_test!(CommitteeColdResign, cert, cert_wrapped); + assert_eq!( + cert, + cert_wrapped.as_committee_cold_resign().unwrap() + ); +} + +#[test] +fn committee_hot_auth_ser_round_trip() { + let cert = CommitteeHotAuth::new( + &Credential::from_keyhash(&fake_key_hash(1)), + &Credential::from_keyhash(&fake_key_hash(2)), + ); + let cert_wrapped = Certificate::new_committee_hot_auth(&cert); + to_from_test!(CommitteeHotAuth, cert, cert_wrapped); + assert_eq!( + cert, + cert_wrapped.as_committee_hot_auth().unwrap() + ); +} + +#[test] +fn drep_registration_ser_round_trip() { + let cert = DRepRegistration::new( + &Credential::from_keyhash(&fake_key_hash(1)), + &Coin::from(100u64), + ); + let cert_wrapped = Certificate::new_drep_registration(&cert); + to_from_test!(DRepRegistration, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_drep_registration().unwrap()); +} + +#[test] +fn drep_registration_with_anchor_ser_round_trip() { + let url = URL::new("https://iohk.io".to_string()).unwrap(); + let anchor = Anchor::new(&url, &fake_anchor_data_hash(255)); + + let cert = DRepRegistration::new_with_anchor( + &Credential::from_keyhash(&fake_key_hash(1)), + &Coin::from(100u64), + &anchor, + ); + let cert_wrapped = Certificate::new_drep_registration(&cert); + to_from_test!(DRepRegistration, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_drep_registration().unwrap()); +} + +#[test] +fn drep_deregistration_ser_round_trip() { + let cert = DRepDeregistration::new( + &Credential::from_keyhash(&fake_key_hash(1)), + &Coin::from(100u64), + ); + let cert_wrapped = Certificate::new_drep_deregistration(&cert); + to_from_test!(DRepDeregistration, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_drep_deregistration().unwrap()); +} + +#[test] +fn drep_update_ser_round_trip() { + let cert = DRepUpdate::new(&Credential::from_keyhash(&fake_key_hash(1))); + let cert_wrapped = Certificate::new_drep_update(&cert); + to_from_test!(DRepUpdate, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_drep_update().unwrap()); +} + +#[test] +fn drep_update_with_anchor_ser_round_trip() { + let url = URL::new("https://iohk.io".to_string()).unwrap(); + let anchor = Anchor::new(&url, &fake_anchor_data_hash(255)); + let cert = DRepUpdate::new_with_anchor(&Credential::from_keyhash(&fake_key_hash(1)), &anchor); + let cert_wrapped = Certificate::new_drep_update(&cert); + to_from_test!(DRepUpdate, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_drep_update().unwrap()); +} + +#[test] +fn genesis_key_delegation_ser_round_trip() { + let cert = GenesisKeyDelegation::new( + &fake_genesis_hash(1), + &fake_genesis_delegate_hash(2), + &fake_vrf_key_hash(3), + ); + let cert_wrapped = Certificate::new_genesis_key_delegation(&cert); + to_from_test!(GenesisKeyDelegation, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_genesis_key_delegation().unwrap()); +} + +#[test] +fn move_instantaneous_reward_to_pot_ser_round_trip() { + let cert = MoveInstantaneousReward::new_to_other_pot(MIRPot::Reserves, &Coin::from(100u64)); + let cert_wrapped = + Certificate::new_move_instantaneous_rewards_cert(&MoveInstantaneousRewardsCert::new(&cert)); + to_from_test!(MoveInstantaneousReward, cert, cert_wrapped); + assert_eq!( + cert, + cert_wrapped + .as_move_instantaneous_rewards_cert() + .unwrap() + .move_instantaneous_reward + ); +} + +#[test] +fn move_instantaneous_reward_to_stake_creds_ser_round_trip() { + let mut amounts = MIRToStakeCredentials::new(); + amounts.insert( + &Credential::from_keyhash(&fake_key_hash(1)), + &DeltaCoin::new(&BigNum::from(100u64)), + ); + let mut amounts = MIRToStakeCredentials::new(); + amounts.insert( + &Credential::from_keyhash(&fake_key_hash(2)), + &DeltaCoin::new(&BigNum::from(1200u64)), + ); + let cert = MoveInstantaneousReward::new_to_stake_creds(MIRPot::Treasury, &amounts); + let cert_wrapped = + Certificate::new_move_instantaneous_rewards_cert(&MoveInstantaneousRewardsCert::new(&cert)); + to_from_test!(MoveInstantaneousReward, cert, cert_wrapped); + assert_eq!( + cert, + cert_wrapped + .as_move_instantaneous_rewards_cert() + .unwrap() + .move_instantaneous_reward + ); +} + +#[test] +fn pool_registration_ser_round_trip() { + let staking_cred = Credential::from_keyhash(&fake_key_hash(1)); + let reward_address = RewardAddress::new(NetworkInfo::testnet_preprod().network_id(), &staking_cred); + let mut owners = Ed25519KeyHashes::new(); + owners.add(&fake_key_hash(2)); + owners.add(&fake_key_hash(3)); + let mut relays = Relays::new(); + relays.add(&Relay::new_single_host_addr(&SingleHostAddr::new( + Some(123), + Some(Ipv4::new([127u8, 0, 0, 1].to_vec()).unwrap()), + Some(Ipv6::new([127u8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1].to_vec()).unwrap()), + ))); + relays.add(&Relay::new_multi_host_name(&MultiHostName::new( + &DNSRecordSRV::new("hi there".to_string()).unwrap(), + ))); + relays.add(&Relay::new_single_host_name(&SingleHostName::new( + Some(123), + &DNSRecordAorAAAA::new("hi there".to_string()).unwrap(), + ))); + let matadata = PoolMetadata::new( + &URL::new("https://iohk.io".to_string()).unwrap(), + &fake_pool_metadata_hash(5), + ); + + let params = PoolParams::new( + &fake_key_hash(1), + &fake_vrf_key_hash(2), + &Coin::from(100u64), + &Coin::from(200u64), + &UnitInterval::new(&BigNum::from(110u64), &BigNum::from(220u64)), + &reward_address, + &owners, + &relays, + Some(matadata), + ); + + let cert = PoolRegistration::new(¶ms); + let cert_wrapped = Certificate::new_pool_registration(&cert); + to_from_test!(PoolRegistration, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_pool_registration().unwrap()); +} + +#[test] +fn pool_retirement_ser_round_trip() { + let cert = PoolRetirement::new(&fake_key_hash(1), Epoch::from(100u32)); + let cert_wrapped = Certificate::new_pool_retirement(&cert); + to_from_test!(PoolRetirement, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_pool_retirement().unwrap()); +} + +#[test] +fn stake_and_vote_delegation_ser_round_trip() { + let drep = DRep::new_key_hash(&fake_key_hash(3)); + + let cert = StakeAndVoteDelegation::new( + &Credential::from_keyhash(&fake_key_hash(1)), + &fake_key_hash(2), + &drep, + ); + let cert_wrapped = Certificate::new_stake_and_vote_delegation(&cert); + to_from_test!(StakeAndVoteDelegation, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_stake_and_vote_delegation().unwrap()); +} + +#[test] +fn stake_delegation_ser_round_trip() { + let cert = StakeDelegation::new( + &Credential::from_keyhash(&fake_key_hash(1)), + &fake_key_hash(2), + ); + let cert_wrapped = Certificate::new_stake_delegation(&cert); + to_from_test!(StakeDelegation, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_stake_delegation().unwrap()); +} + +#[test] +fn stake_deregistration_ser_round_trip() { + let cert = StakeDeregistration::new(&Credential::from_keyhash(&fake_key_hash(1))); + let cert_wrapped = Certificate::new_stake_deregistration(&cert); + to_from_test!(StakeDeregistration, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_stake_deregistration().unwrap()); +} + +#[test] +fn stake_deregistration_with_coin_ser_round_trip() { + let cert = StakeDeregistration::new_with_explicit_refund( + &Credential::from_keyhash(&fake_key_hash(1)), + &Coin::from(100u64), + ); + let cert_wrapped = Certificate::new_stake_deregistration(&cert); + to_from_test!(StakeDeregistration, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_stake_deregistration().unwrap()); +} + +#[test] +fn stake_deregistration_getter_test() { + let cert = StakeDeregistration::new( + &Credential::from_keyhash(&fake_key_hash(1)) + ); + let cert_wrapped = Certificate::new_stake_deregistration(&cert); + to_from_test!(StakeDeregistration, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_stake_deregistration().unwrap()); + assert_eq!(None, cert_wrapped.as_unreg_cert()); +} + +#[test] +fn unreg_cert_getter_test() { + let cert = StakeDeregistration::new_with_explicit_refund( + &Credential::from_keyhash(&fake_key_hash(1)), + &Coin::from(100u64), + ); + let cert_wrapped = Certificate::new_unreg_cert(&cert).unwrap(); + to_from_test!(StakeDeregistration, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_stake_deregistration().unwrap()); + assert_eq!(cert, cert_wrapped.as_unreg_cert().unwrap()); +} + +#[test] +fn unreg_cert_error_test() { + let cert = StakeDeregistration::new( + &Credential::from_keyhash(&fake_key_hash(1)) + ); + let res = Certificate::new_unreg_cert(&cert); + assert!(res.is_err()); +} + +#[test] +fn stake_registration_ser_round_trip() { + let cert = StakeRegistration::new(&Credential::from_keyhash(&fake_key_hash(1))); + let cert_wrapped = Certificate::new_stake_registration(&cert); + to_from_test!(StakeRegistration, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_stake_registration().unwrap()); + assert_eq!(None, cert_wrapped.as_reg_cert()) +} + +#[test] +fn stake_registration_with_coin_ser_round_trip() { + let cert = StakeRegistration::new_with_explicit_deposit( + &Credential::from_keyhash(&fake_key_hash(1)), + &Coin::from(100u64), + ); + let cert_wrapped = Certificate::new_stake_registration(&cert); + to_from_test!(StakeRegistration, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_stake_registration().unwrap()); +} + +#[test] +fn reg_cert_getter_test() { + let cert = StakeRegistration::new_with_explicit_deposit( + &Credential::from_keyhash(&fake_key_hash(1)), + &Coin::from(100u64), + ); + let cert_wrapped = Certificate::new_reg_cert(&cert).unwrap(); + to_from_test!(StakeRegistration, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_stake_registration().unwrap()); + assert_eq!(cert, cert_wrapped.as_reg_cert().unwrap()); +} + +#[test] +fn reg_cert_error_test() { + let cert = StakeRegistration::new(&Credential::from_keyhash(&fake_key_hash(1))); + let res = Certificate::new_reg_cert(&cert); + assert!(res.is_err()); +} + +#[test] +fn stake_registration_and_delegation_ser_round_trip() { + let cert = StakeRegistrationAndDelegation::new( + &Credential::from_keyhash(&fake_key_hash(1)), + &fake_key_hash(2), + &Coin::from(100u64), + ); + let cert_wrapped = Certificate::new_stake_registration_and_delegation(&cert); + to_from_test!(StakeRegistrationAndDelegation, cert, cert_wrapped); + assert_eq!( + cert, + cert_wrapped.as_stake_registration_and_delegation().unwrap() + ); +} + +#[test] +fn stake_vote_registration_and_delegation_ser_round_trip() { + let drep = DRep::new_key_hash(&fake_key_hash(3)); + let cert = StakeVoteRegistrationAndDelegation::new( + &Credential::from_keyhash(&fake_key_hash(1)), + &fake_key_hash(2), + &drep, + &Coin::from(100u64), + ); + let cert_wrapped = Certificate::new_stake_vote_registration_and_delegation(&cert); + to_from_test!(StakeVoteRegistrationAndDelegation, cert, cert_wrapped); + assert_eq!( + cert, + cert_wrapped + .as_stake_vote_registration_and_delegation() + .unwrap() + ); +} + +#[test] +fn vote_delegation_ser_round_trip() { + let drep = DRep::new_key_hash(&fake_key_hash(3)); + let cert = VoteDelegation::new(&Credential::from_keyhash(&fake_key_hash(1)), &drep); + let cert_wrapped = Certificate::new_vote_delegation(&cert); + to_from_test!(VoteDelegation, cert, cert_wrapped); + assert_eq!(cert, cert_wrapped.as_vote_delegation().unwrap()); +} + +#[test] +fn vote_registration_and_delegation_ser_round_trip() { + let drep = DRep::new_key_hash(&fake_key_hash(3)); + let cert = VoteRegistrationAndDelegation::new( + &Credential::from_keyhash(&fake_key_hash(1)), + &drep, + &Coin::from(100u64), + ); + let cert_wrapped = Certificate::new_vote_registration_and_delegation(&cert); + to_from_test!(VoteRegistrationAndDelegation, cert, cert_wrapped); + assert_eq!( + cert, + cert_wrapped.as_vote_registration_and_delegation().unwrap() + ); +} + +#[test] +fn tx_with_drep_reg_deser_test() { + let cbor = "84a4008182582038e88b8b95dc13639c2c0adc6a159316bd795da6672d4025f5f2bc50f122438f010181a20058390013ca2480e9651a5c504b36eda271ec171cdd404cfe349097524a48bd8bee57ce33c7c1f711bc5801986d89dd68078f5922b83812cc86f65f011b0000000253f7736e021a00029d59048184108200581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce800f6a1008182582072fe72c3f2506a2b88cf9c6388535d98f90d481aa734e0e3553792cb9984ffcc5840509a64b3e450f8b338ba3f759e8cf91273493d425a027a7373071c166de6ab83ed3af6b98415c6372906aeaba9269ecf1c40dccbebf8050b4e9ad5e2a5346503f5f6"; + let tx_deser = Transaction::from_hex(cbor); + assert!(tx_deser.is_ok()); + let cert = tx_deser.unwrap().body().certs().unwrap().get(0); + assert!(cert.as_drep_registration().is_some()); +} + +#[test] +fn tx_with_drep_reg_deleg_test() { + let cbor = "84a400818258201e3f301eee4c02377c137eff0260a33b67ea421e3524ce8818e4c5184fa440d2000181a2005839002d745f050a8f7e263f4d0749a82284ed9cc065018c1f4f6a7c1b764882293a49e3ef29a4f9c32e4c18f202f5324182db7790f48dccf7a6dd011b0000000253e3e5ad021a0002a281048183098200581c82293a49e3ef29a4f9c32e4c18f202f5324182db7790f48dccf7a6dd8200581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8a0f5f6"; + let tx_deser = Transaction::from_hex(cbor); + assert!(tx_deser.is_ok()); + let cert = tx_deser.unwrap().body().certs().unwrap().get(0); + assert!(cert.as_vote_delegation().is_some()); +} + +#[test] +fn block_with_tx_with_certs_under_tag_set() { + let cbor = "85828a1a00093ff71a00b90a7e582030cf3798ec016ed63988b2e413fdadf4bda64e5b78587b74dec3e8807b3fd28058204e6f414dc8f402a977ef31062cae0e1251db537980f84c0e8623696975083fc15820f2768101e877acd0e08764765552a36c0f1a25e86d461c277bc19d0e476253fd825840622a847f3c0e77b608aa2aa1cff5c389348e80db4aa0d63b42d89753d5630cb0fcabbfd7293ee65a6b795c52cb4bd6b338f9d11fd007406fcbe89d06bb34f1145850d2c8f8179a8009b0979762ac10c6a514d8d0bc6b6d0d4413a0edbd5dbe888a8e72ba874d0f692ec940e58513c5b8ccb5072839ea1fa00776b4dcb493be8131b1a0b21bf5b5be5ff264e209fef448a30419016c5820388135462552cc6045399bd62876b28b768a760dd9a64adedbf7499bdb7cd1be8458202495446005fecca3a7ede95738dad5fd87393e81c815bde94a405be5779368c30218425840979096c8f12db5dc84a483626c966b64c10e16453b14f33b9648c250b096e391f6e9e6773017134a39c080d77f132950f43522015e9fa265695ee939625f89078209005901c0681c0f99e6f0d09b66b3e8e6eaed6a92649b635225f7d374a92af1a7ba2771880d14719c229892943bb85ba51699ae50bf7ae174e2e9869af7e289355aab000e741588d3b8c82efa6063c83fe326b72eb93f93bea07c5b5b9a720e9f9ecc20e47598b3ce56f370b268a2e2e075f24942e547d29182cecefdb9e7e45108e2261dd3d006ab778cba4cf0bced84f41fc61afcd79591d988eb401e2f870122ae590aec3467f464cebca50c5434d2491f631ebb3d835f43682244bcc839bd83e1c48950bcc73cfe5feaaa211d964bd9bdb4f9acd23fde11f469f6e0fb8bcc9aa4130a54ccab7381968e67ad1291bcdb8528228bbbb9fe15f72cf125b4de1cfdf3dd2b0d9189347a6964f17ce5063b75df8dd20f0fceeefb0d2f5781d34a03f14361ad4b9acb6c40c33ae366906f69dd422e5f2e00afd6bbecba078aebc53a69c567a864548da0205ed92937b4efbb12ab49273e598e3ec5f55abfcaae36c856024c6de779e8f2e28d997b94e116a7b6438cc04fb25b1dbb494b32e1eb97139e6d62e6a70fb2b480dc356225977a6b6b0a5bb9822d2dd5e0012c5de011219b8adc70af87304ab2c98c457078e0b859f50f69bd5eaaaf44e62d34776c8a6bcffd3c1bae81a40081825820b45589419cdc8218dabf8805fd09dded29c795939588fd2f1c397fcd29207307000181825839002844d663085837e4620b9114498f2a4577f2942d84573f95255cdea32134ccb579707f289dc83bc0fb386199a9cea4f1b0177d8384adbb1d1b0000000253eac253021a00029d2d04d901028182008200581c2134ccb579707f289dc83bc0fb386199a9cea4f1b0177d8384adbb1d81a100828258206f92479dc8d89cae74346be2e31997f8c04c977fabb3c806fd1740e7af20874b5840c3d442ba9e0c0915917eb64045603e6b90e0c2cf11c796b7c327f79a4c6e971149bcbbeae79339db8b557345c08de1f103e11c2032348825de9bc5e44150d1018258207be0e76fe15de98ecc3f02e95d4ec171ef884a6ca22b7623c75f66a07f16f3f458409644a34175257eec09a2b0ab52cc36b5bcfeea590d1ae7ead57604ce30be1ad79ecea7c07eadb7973c0c3fd99d63303b47f156fb767a4aa3180c4ed436233f05a080"; + let block = Block::from_hex(cbor); + assert!(block.is_ok()); + + let certs = block.unwrap().transaction_bodies.0[0].certs().unwrap(); + assert_eq!(certs.len(), 1); +} + +#[test] +fn certificates_collection_ser_round_trip() { + let mut certs = Certificates::new(); + let cert_1 = StakeRegistration::new(&Credential::from_keyhash(&fake_key_hash(1))); + certs.add(&Certificate::new_stake_registration(&cert_1)); + let cert_2 = StakeDeregistration::new(&Credential::from_keyhash(&fake_key_hash(2))); + certs.add(&Certificate::new_stake_deregistration(&cert_2)); + let cert_3 = StakeDelegation::new( + &Credential::from_keyhash(&fake_key_hash(3)), + &fake_key_hash(4), + ); + certs.add(&Certificate::new_stake_delegation(&cert_3)); + + assert_eq!(certs.len(), 3); + + let json = certs.to_json().unwrap(); + let cbor = certs.to_bytes(); + let hex_cbor = certs.to_hex(); + + assert_eq!(certs, Certificates::from_json(&json).unwrap()); + assert_eq!(certs, Certificates::from_bytes(cbor).unwrap()); + assert_eq!(certs, Certificates::from_hex(&hex_cbor).unwrap()); +} diff --git a/rust/src/tests/serialization/general.rs b/rust/src/tests/serialization/general.rs new file mode 100644 index 00000000..eeb067bd --- /dev/null +++ b/rust/src/tests/serialization/general.rs @@ -0,0 +1,911 @@ +use crate::{Address, BigInt, BigNum, Block, BlockHash, CborContainerType, Coin, Credential, DataHash, ExUnits, HeaderBody, HeaderLeaderCertEnum, Int, KESVKey, MIRPot, MIRToStakeCredentials, MoveInstantaneousReward, NativeScript, OperationalCert, PlutusData, PlutusList, PlutusScript, PlutusScripts, ProtocolVersion, Redeemer, RedeemerTag, Redeemers, ScriptHash, ScriptRef, TimelockStart, TransactionBody, TransactionInputs, TransactionOutput, TransactionOutputs, TransactionWitnessSet, VRFCert, VRFVKey, Value, Vkeywitness, Vkeywitnesses, VersionedBlock, BlockEra, to_bytes, BootstrapWitnesses, Credentials, Ed25519KeyHashes}; + +use crate::protocol_types::ScriptRefEnum; +use crate::tests::fakes::{fake_base_address, fake_boostrap_witness, fake_bytes_32, fake_data_hash, fake_key_hash, fake_signature, fake_tx_input, fake_tx_output, fake_value, fake_value2, fake_vkey, fake_vkey_witness}; + +#[test] +fn tx_output_deser_lagacy() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput { + address: addr.clone(), + amount: val.clone(), + plutus_data: None, + script_ref: None, + serialization_format: None, + }; + let mut txo_dh = txo.clone(); + txo_dh.set_data_hash(&DataHash::from([47u8; DataHash::BYTE_COUNT])); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let bytes = txos.to_bytes(); + let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); + let bytes_deser = txos_deser.to_bytes(); + assert_eq!(bytes, bytes_deser); +} + +#[test] +fn tx_output_deser_post_alonzo_with_plutus_script_and_datum() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput { + address: addr.clone(), + amount: val.clone(), + plutus_data: None, + script_ref: None, + serialization_format: None, + }; + let mut txo_dh = txo.clone(); + txo_dh.set_plutus_data(&PlutusData::new_bytes(fake_bytes_32(11))); + txo_dh.set_script_ref(&ScriptRef::new_plutus_script(&PlutusScript::new( + [61u8; 29].to_vec(), + ))); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let bytes = txos.to_bytes(); + let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); + let bytes_deser = txos_deser.to_bytes(); + assert_eq!(bytes, bytes_deser); +} + +#[test] +fn tx_output_deser_post_alonzo_with_plutus_script() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput { + address: addr.clone(), + amount: val.clone(), + plutus_data: None, + script_ref: None, + serialization_format: None, + }; + let mut txo_dh = txo.clone(); + txo_dh.set_script_ref(&ScriptRef::new_plutus_script(&PlutusScript::new( + [61u8; 29].to_vec(), + ))); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let bytes = txos.to_bytes(); + let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); + let bytes_deser = txos_deser.to_bytes(); + assert_eq!(bytes, bytes_deser); +} + +#[test] +fn tx_output_deser_post_alonzo_with_datum() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput { + address: addr.clone(), + amount: val.clone(), + plutus_data: None, + script_ref: None, + serialization_format: None, + }; + let mut txo_dh = txo.clone(); + txo_dh.set_plutus_data(&PlutusData::new_bytes(fake_bytes_32(11))); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let bytes = txos.to_bytes(); + let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); + let bytes_deser = txos_deser.to_bytes(); + assert_eq!(bytes, bytes_deser); +} + +#[test] +fn tx_output_deser_post_alonzo_with_native_script_and_datum() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput { + address: addr.clone(), + amount: val.clone(), + plutus_data: None, + script_ref: None, + serialization_format: None, + }; + let mut txo_dh = txo.clone(); + let native_script = NativeScript::new_timelock_start(&TimelockStart::new(20)); + txo_dh.set_script_ref(&ScriptRef::new_native_script(&native_script)); + txo_dh.set_plutus_data(&PlutusData::new_bytes(fake_bytes_32(11))); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let bytes = txos.to_bytes(); + let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); + let bytes_deser = txos_deser.to_bytes(); + assert_eq!(bytes, bytes_deser); +} + +#[test] +fn tx_output_deser_post_alonzo_with_native_script() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput { + address: addr.clone(), + amount: val.clone(), + plutus_data: None, + script_ref: None, + serialization_format: None, + }; + let mut txo_dh = txo.clone(); + let native_script = NativeScript::new_timelock_start(&TimelockStart::new(20)); + txo_dh.set_script_ref(&ScriptRef::new_native_script(&native_script)); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let bytes = txos.to_bytes(); + let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); + let bytes_deser = txos_deser.to_bytes(); + assert_eq!(bytes, bytes_deser); +} + +#[test] +fn tx_output_deser_post_alonzo_with_native_script_and_data_hash() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput { + address: addr.clone(), + amount: val.clone(), + plutus_data: None, + script_ref: None, + serialization_format: None, + }; + let mut txo_dh = txo.clone(); + let native_script = NativeScript::new_timelock_start(&TimelockStart::new(20)); + let data_hash = DataHash::from_bytes(vec![ + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, + 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + ]) + .unwrap(); + txo_dh.set_data_hash(&data_hash); + txo_dh.set_script_ref(&ScriptRef::new_native_script(&native_script)); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let bytes = txos.to_bytes(); + let txos_deser = TransactionOutputs::from_bytes(bytes.clone()).unwrap(); + let bytes_deser = txos_deser.to_bytes(); + assert_eq!(bytes, bytes_deser); +} + +#[test] +fn tx_output_deser_lagacy_json() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput { + address: addr.clone(), + amount: val.clone(), + plutus_data: None, + script_ref: None, + serialization_format: None, + }; + let mut txo_dh = txo.clone(); + txo_dh.set_data_hash(&DataHash::from([47u8; DataHash::BYTE_COUNT])); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let json_txos = txos.to_json().unwrap(); + let deser_txos = TransactionOutputs::from_json(json_txos.as_str()).unwrap(); + + assert_eq!(deser_txos.to_bytes(), txos.to_bytes()); + assert_eq!(deser_txos.to_json().unwrap(), txos.to_json().unwrap()); +} + +#[test] +fn tx_output_deser_post_alonzo_with_plutus_script_and_datum_json() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput { + address: addr.clone(), + amount: val.clone(), + plutus_data: None, + script_ref: None, + serialization_format: None, + }; + let mut txo_dh = txo.clone(); + txo_dh.set_plutus_data(&PlutusData::new_bytes(fake_bytes_32(11))); + txo_dh.set_script_ref(&ScriptRef::new_plutus_script(&PlutusScript::new( + [61u8; 29].to_vec(), + ))); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let json_txos = txos.to_json().unwrap(); + let deser_txos = TransactionOutputs::from_json(json_txos.as_str()).unwrap(); + + assert_eq!(deser_txos.to_bytes(), txos.to_bytes()); + assert_eq!(deser_txos.to_json().unwrap(), txos.to_json().unwrap()); +} + +#[test] +fn tx_output_deser_post_alonzo_with_plutus_script_json() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput { + address: addr.clone(), + amount: val.clone(), + plutus_data: None, + script_ref: None, + serialization_format: None, + }; + let mut txo_dh = txo.clone(); + txo_dh.set_script_ref(&ScriptRef::new_plutus_script(&PlutusScript::new( + [61u8; 29].to_vec(), + ))); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let json_txos = txos.to_json().unwrap(); + let deser_txos = TransactionOutputs::from_json(json_txos.as_str()).unwrap(); + + assert_eq!(deser_txos.to_bytes(), txos.to_bytes()); + assert_eq!(deser_txos.to_json().unwrap(), txos.to_json().unwrap()); +} + +#[test] +fn tx_output_deser_post_alonzo_with_datum_json() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput { + address: addr.clone(), + amount: val.clone(), + plutus_data: None, + script_ref: None, + serialization_format: None, + }; + let mut txo_dh = txo.clone(); + txo_dh.set_plutus_data(&PlutusData::new_bytes(fake_bytes_32(11))); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let json_txos = txos.to_json().unwrap(); + let deser_txos = TransactionOutputs::from_json(json_txos.as_str()).unwrap(); + + assert_eq!(deser_txos.to_bytes(), txos.to_bytes()); + assert_eq!(deser_txos.to_json().unwrap(), txos.to_json().unwrap()); +} + +#[test] +fn tx_output_deser_post_alonzo_with_native_script_and_datum_json() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput { + address: addr.clone(), + amount: val.clone(), + plutus_data: None, + script_ref: None, + serialization_format: None, + }; + let mut txo_dh = txo.clone(); + let native_script = NativeScript::new_timelock_start(&TimelockStart::new(20)); + txo_dh.set_script_ref(&ScriptRef::new_native_script(&native_script)); + txo_dh.set_plutus_data(&PlutusData::new_bytes(fake_bytes_32(11))); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let json_txos = txos.to_json().unwrap(); + let deser_txos = TransactionOutputs::from_json(json_txos.as_str()).unwrap(); + + assert_eq!(deser_txos.to_bytes(), txos.to_bytes()); + assert_eq!(deser_txos.to_json().unwrap(), txos.to_json().unwrap()); +} + +#[test] +fn tx_output_deser_post_alonzo_with_native_script_json() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput { + address: addr.clone(), + amount: val.clone(), + plutus_data: None, + script_ref: None, + serialization_format: None, + }; + let mut txo_dh = txo.clone(); + let native_script = NativeScript::new_timelock_start(&TimelockStart::new(20)); + txo_dh.set_script_ref(&ScriptRef::new_native_script(&native_script)); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let json_txos = txos.to_json().unwrap(); + let deser_txos = TransactionOutputs::from_json(json_txos.as_str()).unwrap(); + + assert_eq!(deser_txos.to_bytes(), txos.to_bytes()); + assert_eq!(deser_txos.to_json().unwrap(), txos.to_json().unwrap()); +} + +#[test] +fn tx_output_deser_post_alonzo_with_native_script_and_data_hash_json() { + let mut txos = TransactionOutputs::new(); + let addr = Address::from_bech32("addr1qyxwnq9kylzrtqprmyu35qt8gwylk3eemq53kqd38m9kyduv2q928esxmrz4y5e78cvp0nffhxklfxsqy3vdjn3nty9s8zygkm").unwrap(); + let val = &Value::new(&BigNum::from_str("435464757").unwrap()); + let txo = TransactionOutput { + address: addr.clone(), + amount: val.clone(), + plutus_data: None, + script_ref: None, + serialization_format: None, + }; + let mut txo_dh = txo.clone(); + let native_script = NativeScript::new_timelock_start(&TimelockStart::new(20)); + let data_hash = DataHash::from_bytes(vec![ + 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, + 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + ]) + .unwrap(); + txo_dh.set_data_hash(&data_hash); + txo_dh.set_script_ref(&ScriptRef::new_native_script(&native_script)); + txos.add(&txo); + txos.add(&txo_dh); + txos.add(&txo_dh); + txos.add(&txo); + txos.add(&txo); + txos.add(&txo_dh); + let json_txos = txos.to_json().unwrap(); + let deser_txos = TransactionOutputs::from_json(json_txos.as_str()).unwrap(); + + assert_eq!(deser_txos.to_bytes(), txos.to_bytes()); + assert_eq!(deser_txos.to_json().unwrap(), txos.to_json().unwrap()); +} + +#[test] +fn mir_deser() { + let reserves_to_pot = MoveInstantaneousReward::new_to_other_pot( + MIRPot::Treasury, + &Coin::from_str("143546464").unwrap(), + ); + let reserves_to_pot_deser = + MoveInstantaneousReward::from_bytes(reserves_to_pot.to_bytes()).unwrap(); + assert_eq!(reserves_to_pot.to_bytes(), reserves_to_pot_deser.to_bytes()); + let treasury_to_pot = + MoveInstantaneousReward::new_to_other_pot(MIRPot::Treasury, &Coin::from_str("0").unwrap()); + let treasury_to_pot_deser = + MoveInstantaneousReward::from_bytes(treasury_to_pot.to_bytes()).unwrap(); + assert_eq!(treasury_to_pot.to_bytes(), treasury_to_pot_deser.to_bytes()); + let mut stake_creds = MIRToStakeCredentials::new(); + stake_creds.insert( + &Credential::from_scripthash(&ScriptHash([54u8; ScriptHash::BYTE_COUNT])), + &Int::new_i32(-314159265), + ); + let to_stake_creds = + MoveInstantaneousReward::new_to_stake_creds(MIRPot::Treasury, &stake_creds); + let to_stake_creds_deser = + MoveInstantaneousReward::from_bytes(to_stake_creds.to_bytes()).unwrap(); + assert_eq!(to_stake_creds.to_bytes(), to_stake_creds_deser.to_bytes()); +} + +#[test] +#[ignore] +fn alonzo_block() { + // this test for some reason has 2-byte pool metadata hashes so don't run this without changing that + let bytes = hex::decode("85828f03095820bb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aa58208a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c58208a88e3dd7409f195fd52db2d3cba5d72ca6709bf1d94121bf3748801b40f6f5c8258404fefc7c718693b57c87170ceba220382afbdd148c0a53b4a009ca63ad1f101483a6170c83a77f23d362a68dcb502802df7f98fa4f7a78b4082b211530e1234305850f770f6769ae9871d42b970fc6254bb927c2181fff45897f241bd72221d86d33c8df64c0a3c8cbb9aa52fef191d7202465c52df8d33727a38c7dc5d40864d753348a340f8afcbb3bb05d4a03f16b1080d825840fe682775f0fa232e909ddc9ec3210ea7a0ee6514cd8b0815190a08f7cef3985463152e10dfad9ed6c09b641b6c1824498e77814a7c12e03096a63cd62056446358500951ed3ef2065e4196d008b50a63bb3e2bdc9a64df67eff4e230b35429291def476684114e074357a5c834bf79eacf583b6fe9fcd1d17f3719a31de97aa4da5e4630b05421359e0b6e4a9bd76c6b920b190929582010c865acec05c84c2c0d0b889f7dbe9bf3b5561f8552da1eb286eac4ccdabc5e5820d298da3803eb9958f01c02e73f2410f2f9bb2ecbc346526b1b76772e1bdd7db500005840940f8a3696847e4a238705bdd27a345086282067b9bc5cb7b69847ca8756085844d576f59ab056c169a504320cc1eab4c11fd529482b3c57da6fa96d44635b0802005901c0a1b2ee63b357fe0b19c6bb8dc3fc865c0608a89626505c5f9aff3b74a0809ca2635e0c4235c247306987c7fd76a4a06210ebf74178e72a1faa78fb8865a69005cc6a5ab5c9b40f817c715df558af7d07b6186f0ccf31715ec2fb00980730ac166af657e6670608afe1bf651d496e01b1c7ff1eb44614d8cfd1b7e32b2c2939349236cc0ada145d8d8d7ad919ef1e60c8bbad31dbedf9f395849705a00c14a8785106aae31f55abc5b1f2089cbef16d9401f158704c1e4f740f7125cfc700a99d97d0332eacb33e4bbc8dab2872ec2b3df9e113addaebd156bfc64fdfc732614d2aedd10a58a34993b7b08c822af3aa615b6bbb9b267bc902e4f1075e194aed084ca18f8bcde1a6b094bf3f5295a0d454c0a083ed5b74f7092fc0a7346c03979a30eeea76d686e512ba48d21544ba874886cdd166cbf275b11f1f3881f4c4277c09a24b88fc6168f4578267bdc9d62cb9b78b8dfc888ccce226a177725f39e7d50930552861d1e88b7898971c780dc3b773321ba1854422b5cecead7d50e77783050eeae2cd9595b9cd91681c72e5d53bb7d12f28dec9b2847ee70a3d7781fb1133aea3b169f536ff5945ec0a76950e51beded0627bb78120617a2f0842e50e3981ae0081825820ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25000d81825820bb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aa01018183583900cb9358529df4729c3246a2a033cb9821abbfd16de4888005904abc410d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb06821864a1581ca646474b8f5431261506b6c273d307c7569a4eb6c96b42dd4a29520aa14a636f75747473436f696e1903e85820ee155ace9c40292074cb6aff8c9ccdd273c81648ff1149ef36bcea6ebb8a3e25021903e70304048382008200581c0d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb068a03581c0d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb065820c5e21ab1c9f6022d81c3b25e3436cb7f1df77f9652ae3e1310c28e621dd87b4c0105d81e82010a581de00d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb0681581c0d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb0680826e636f6e73656e7375732e706f6f6c427b7d82068200a18200581c008b47844d92812fc30d1f0ac9b6fbf38778ccba9db8312ad9079079186e05a1581de00d6a577e9441ad8ed9663931906e4d43ece8f82c712b1d0235affb0618640682a1581ce0a714319812c3f773ba04ec5d6b3ffcd5aad85006805b047b082541a104190fa00008020e81581cf81ce66e0f52da5ca48193386e7511fde5b030a307b4c3681736c6f009a1581cb16b56f5ec064be6ac3cab6035efae86b366cc3dc4a0d571603d70e5a14a636f75747473436f696e1903e80b58209e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b0758209e1199a988ba72ffd6e9c269cadb3b53b5f360ff99f112d9b2ee30c4d74ad88b0f0181a400818258203b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da295840815671b581b4b02a30108a799a85c7f2e5487fb667e748e8fde59e466ab987ce133ecb77ffa0dc53c5804e6706e26b94e17803235da28112bc747de48ccbd70903814c4b0100002002002002000011048118bf058184000019039782191388191388a100d90103a300a40166737472696e67024562797465730382010204a10341620181820180028148470100002002006180").unwrap(); + let block = Block::from_bytes(bytes).unwrap(); + let block2 = Block::from_bytes(block.to_bytes()).unwrap(); + assert_eq!(block.to_bytes(), block2.to_bytes()); +} + +#[test] +fn test_tx_body_roundtrip() { + let mut txb = TransactionBody::new( + &TransactionInputs::from_vec(vec![fake_tx_input(0)]), + &TransactionOutputs(vec![fake_tx_output(1)]), + &BigNum(1234567), + Some(12345678), + ); + + txb.set_collateral_return(&fake_tx_output(2)); + txb.set_total_collateral(&BigNum(1234)); + + let txb2 = TransactionBody::from_bytes(txb.to_bytes()).unwrap(); + assert_eq!(txb, txb2); +} + +#[test] +fn test_header_body_roundtrip() { + fn fake_header_body(leader_cert: HeaderLeaderCertEnum) -> HeaderBody { + HeaderBody { + block_number: 123, + slot: BigNum(123), + prev_hash: Some(BlockHash::from_bytes(fake_bytes_32(1)).unwrap()), + issuer_vkey: fake_vkey(), + vrf_vkey: VRFVKey::from_bytes(fake_bytes_32(2)).unwrap(), + leader_cert, + block_body_size: 123456, + block_body_hash: BlockHash::from_bytes(fake_bytes_32(4)).unwrap(), + operational_cert: OperationalCert::new( + &KESVKey::from_bytes(fake_bytes_32(5)).unwrap(), + 123, + 456, + &fake_signature(6), + ), + protocol_version: ProtocolVersion::new(12, 13), + } + } + + let hbody1 = fake_header_body(HeaderLeaderCertEnum::VrfResult( + VRFCert::new(fake_bytes_32(3), [0; 80].to_vec()).unwrap(), + )); + + assert_eq!(hbody1, HeaderBody::from_bytes(hbody1.to_bytes()).unwrap()); + + let hbody2 = fake_header_body(HeaderLeaderCertEnum::NonceAndLeader( + VRFCert::new(fake_bytes_32(4), [1; 80].to_vec()).unwrap(), + VRFCert::new(fake_bytes_32(5), [2; 80].to_vec()).unwrap(), + )); + + assert_eq!(hbody2, HeaderBody::from_bytes(hbody2.to_bytes()).unwrap()); +} + +#[test] +fn test_witness_set_roundtrip() { + fn witness_set_roundtrip(plutus_scripts: &PlutusScripts) { + let mut ws = TransactionWitnessSet::new(); + ws.set_vkeys(&Vkeywitnesses::from_vec(vec![Vkeywitness::new( + &fake_vkey(), + &fake_signature(1), + )])); + ws.set_redeemers(&Redeemers::from(vec![Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(12), + &PlutusData::new_integer(&BigInt::one()), + &ExUnits::new(&BigNum(123), &BigNum(456)), + )])); + ws.set_plutus_data(&PlutusList::from(vec![PlutusData::new_integer( + &BigInt::one(), + )])); + ws.set_plutus_scripts(plutus_scripts); + + assert_eq!( + TransactionWitnessSet::from_bytes(ws.to_bytes()).unwrap(), + ws + ); + } + + let bytes = hex::decode("4e4d01000033222220051200120011").unwrap(); + let script_v1 = PlutusScript::from_bytes(bytes.clone()).unwrap(); + let script_v2 = PlutusScript::from_bytes_v2(bytes.clone()).unwrap(); + let script_v3 = PlutusScript::from_bytes_v3(bytes.clone()).unwrap(); + + witness_set_roundtrip(&PlutusScripts(vec![])); + witness_set_roundtrip(&PlutusScripts(vec![script_v1.clone()])); + witness_set_roundtrip(&PlutusScripts(vec![script_v2.clone()])); + witness_set_roundtrip(&PlutusScripts(vec![script_v3.clone()])); + witness_set_roundtrip(&PlutusScripts(vec![script_v1.clone(), script_v2.clone()])); + witness_set_roundtrip(&PlutusScripts(vec![ + script_v1.clone(), + script_v2.clone(), + script_v3.clone(), + ])); +} + +#[test] +fn test_script_ref_roundtrip() { + let ref0 = ScriptRef::new_native_script(&NativeScript::new_timelock_start( + &TimelockStart::new(123456), + )); + assert_eq!(ScriptRef::from_bytes(ref0.to_bytes()).unwrap(), ref0); + + let bytes = hex::decode("4e4d01000033222220051200120011").unwrap(); + let script_v1 = PlutusScript::from_bytes(bytes.clone()).unwrap(); + let script_v2 = PlutusScript::from_bytes_v2(bytes.clone()).unwrap(); + let script_v3 = PlutusScript::from_bytes_v3(bytes.clone()).unwrap(); + + let ref1 = ScriptRef::new_plutus_script(&script_v1); + assert_eq!(ScriptRef::from_bytes(ref1.to_bytes()).unwrap(), ref1); + + let ref2 = ScriptRef::new_plutus_script(&script_v2); + assert_eq!(ScriptRef::from_bytes(ref2.to_bytes()).unwrap(), ref2); + + let ref3 = ScriptRef::new_plutus_script(&script_v3); + assert_eq!(ScriptRef::from_bytes(ref3.to_bytes()).unwrap(), ref3); +} + +#[test] +fn legacy_output_roundtrip() { + let o1 = TransactionOutput::new(&fake_base_address(0), &fake_value()); + let mut o2 = TransactionOutput::new(&fake_base_address(1), &fake_value()); + o2.set_data_hash(&fake_data_hash(2)); + + assert_eq!(TransactionOutput::from_bytes(o1.to_bytes()).unwrap(), o1); + assert_eq!(TransactionOutput::from_bytes(o2.to_bytes()).unwrap(), o2); +} + +#[test] +fn babbage_output_roundtrip() { + let mut o1 = TransactionOutput::new(&fake_base_address(0), &fake_value2(234567)); + o1.set_plutus_data(&PlutusData::new_empty_constr_plutus_data(&BigNum(42))); + assert_eq!(TransactionOutput::from_bytes(o1.to_bytes()).unwrap(), o1); + + let mut o2 = TransactionOutput::new(&fake_base_address(1), &fake_value2(234568)); + o2.set_script_ref(&ScriptRef::new_native_script( + &NativeScript::new_timelock_start(&TimelockStart::new(123456)), + )); + assert_eq!(TransactionOutput::from_bytes(o2.to_bytes()).unwrap(), o2); + + let bytes = hex::decode("4e4d01000033222220051200120011").unwrap(); + let script_v1 = PlutusScript::from_bytes(bytes.clone()).unwrap(); + let script_v2 = PlutusScript::from_bytes_v2(bytes.clone()).unwrap(); + let script_v3 = PlutusScript::from_bytes_v3(bytes.clone()).unwrap(); + + let mut o3 = TransactionOutput::new(&fake_base_address(2), &fake_value2(234569)); + o3.set_script_ref(&ScriptRef::new_plutus_script(&script_v1)); + assert_eq!(TransactionOutput::from_bytes(o3.to_bytes()).unwrap(), o3); + + let mut o4 = TransactionOutput::new(&fake_base_address(3), &fake_value2(234570)); + o4.set_script_ref(&ScriptRef::new_plutus_script(&script_v2)); + assert_eq!(TransactionOutput::from_bytes(o4.to_bytes()).unwrap(), o4); + + let mut o5 = TransactionOutput::new(&fake_base_address(4), &fake_value2(234571)); + o5.set_plutus_data(&PlutusData::new_empty_constr_plutus_data(&BigNum(43))); + o5.set_script_ref(&ScriptRef::new_plutus_script(&script_v2)); + assert_eq!(TransactionOutput::from_bytes(o5.to_bytes()).unwrap(), o5); + + let mut o6 = TransactionOutput::new(&fake_base_address(5), &fake_value2(234572)); + o6.set_data_hash(&fake_data_hash(222)); + o6.set_script_ref(&ScriptRef::new_plutus_script(&script_v2)); + assert_eq!(TransactionOutput::from_bytes(o6.to_bytes()).unwrap(), o6); + + let mut o7 = TransactionOutput::new(&fake_base_address(6), &fake_value2(234573)); + o7.set_script_ref(&ScriptRef::new_plutus_script(&script_v3)); + assert_eq!(TransactionOutput::from_bytes(o7.to_bytes()).unwrap(), o7); +} + +#[test] +fn pre_alonzo_block() { + let bytes = hex::decode("84828f1a002072a81a00ca44f0582070d6f38b4569ba062c09632127db13474f22c534e6d8097895403c431e57f12358204f4d7523e41e058a6cbdefb5538654ffc2a53416a7f5bb99f7eac699d42d5c1f58205e3d96cb8ef0291d2f1df6aa7b5a4496ac8de1dcce100c31274325625102796d82584065417914ca323d842c5861407a638e146e6af55f59aff95f1451839de2aa709151237e24e6db7bf94db97293da9c1e61e68d60c8e2b10a116d3c71067247458b5850dc36a5a88f09f0b7a0b5d5d52d87c7c3e3c20752176a426d182255df3d026392f407990f09e5858de6432263fc167bc890a97d07d2371cd5bb26b12242c1ff6fda184ec78d15493a38a3e0df1494f800825840df4e07d3bca43341e4297e2914ea38363ecea1c17ce9145294c4631e0f09f706cb23a5f27c6b71ae9ac46a7ca25af4d7c156f15444fa41814f7d6a0b6a4e57525850d6073f277ded1ef9e8bfe9f6325858c142fbbbbff4395c45d82f0861a6ef6116204965f807e8650fa4e9ac4aa04aeb03984ea66abb129155a78931d39bbcb7ad64afef3f4f55cfa4eb6c97698e88f1051905db5820c1b1fbd809dc06e0e2dc544312aae2a46c059249f86c24ea0689a0b0944a75f558207ce5ce3992b23cb2bf566c48aba8bfc39eb24c9b43354de0129b81bf9f1414b307186058403ac64d720227c18139132b499046a168eb1c5bdd3983385e3518f33fc7f52fd0be348fe3e14d17e1ba606708c30bda061cf23ea3294b0089d3e1e1d58a7aa50702005901c074d3c2c0b5e17b12ba829017186daa1f7f365bbe5b0e0c038cb0cc05e849f702afd349234353ee3cc8878fa31299e85562f04d3cdd74e1bc73591be48d2fbc0d043f6b41fa527b2f9fb3f77605eee528926e76cc18a1638283e5591170f7073462441d40d7cc2e13a38e7d247928cb15d2e5b2e74a12d07f858f7e922bbff8a91c16e9bb8f5ea101c50d96627fb48a03d8191b5035b5de00b9824867fdffb5a2493799e94676bf685db85517dd8a87a0ba2589b3a8a69d529ae8052680c520c5577adbb91cf931e906b1629e621d5bd5c30eaee77f35c5f0a714827b48afaa4e549c1756e94291f4b083aad9c375caf9a67aeac08f32c91cd0572192267960cd74a85148b5e99d0053804dcfb44785417725c56e0fc5caf2ae50fbf25b92c7b7ebe17aa9e289470041a06fd8986f6f9ebdb12e87a970f1d388963929367013e17513e83cab8c98460cab703d5fdd26eeb079e4db701996f73c694365080236901289c5fc96471e91fb75e0e58560f5d073c3ef79a8f5dd4b45ff7abf9c7d7564232f7897ca3d85ac7bb9ecaa75b7c062f27de8b20f301e5607563b2c904e3c7f113b1eeba8a4d1c82fc1a747c920bac6af9a9f4dae1744847232ea03289e25e482a50082825820478ad95cafe9b1660809d618870c86dda1295764e113886e2b8a1de2de5af17201825820f84508cc7674b663db84ceb9f0790f5527f3c70f2a05e4d7f783cd9890463b4e01018182583900ff7f04abbd3050c0b138c8fa3005d48aaf8b9700d4565758e91a95385667fab107f848cfd4b73a7407a7661600cf68f0efc969ece37665ae1a000f4240021a000f4240031a00ca60f1075820e845fe9180ac36cc0102f892a839ad1ed2ea9a52c605fb8e4e1c2774ef0bb65ba50081825820c4b5ad6873b8581c75b8ee52f58a3eded29acbbb92d874a64228a1ca4e68956700018182581d60daad04ed2b7f69e2a9be582e37091739fa036a14c1c22f88061d43c71b004aca96b58fd90c021a000f4240031a00d986900682a7581c0d06d2547ed371fdf95fb5c4c735eecdd53e6a5bb831561bd0fcfd3da10e820300581c2f56e87d67b8e5216582cfeb95dbdc9083110a3ef68faaa51bef3a80a10e820300581c2fca486b4d8f1a0432f5bf18ef473ee4294c795a1a32e3132bc6b90fa10e820300581c4ee98623920698b77c1c7f77288cbdac5f9011ff8970b1f507567d0da10e820300581c514e81afb082fce01678809eebd90eda4f7918354ec7d0433ad16274a10e820300581c581e23030b6038bae716e5d64b9e053db10541b12e6b0b4eff485454a10e820300581ce5f27655371b54aed91cc916b2569060978be80056768fee2cc5ce1ba10e820300186582a1008182582028364596385174f5eabc763031b8d54b18ed5d06967ff44b3abbdbaca9cb58a75840de49197fed8dd13716c88e68452fb314d418a24fee9cc194308bd47b057d161ae40cd8f49bf6b378e7343ee5d3a7b9bdb1f2e9efeef896adaa9eb7373fbb8502a1008882582032a954b521c0b19514408965831ef6839637de7a1a6168bcf8455c504ba93b9c5840ab2d59239499807e25dc8025940a70cb890a52e8f47f35004cfec623036ca9f5c3e925b32bd23a7d1d044cef915913e853dbb57438f9c92a5d5f9581caa67d098258207ec249d890d0aaf9a81207960c163ae2d6ac5e715ca6b96d5860e50d9f2b2b2a5840f2d8031ac5d79777076dd1176cb7ed91690fcfb6be498320e5de9afbf6ea8e8ced23bff69230d050523a4a7e03c2b0599e18e93b31959063249fb50274a02a068258204f4d7523e41e058a6cbdefb5538654ffc2a53416a7f5bb99f7eac699d42d5c1f5840c5844b849865fed81f67842a4697c3090cf4ecb50510f1e6b379b7c63b78417ca28ea653c016d2e733877e1605e8a1712c42404ca0686f67455c620431d54b07825820e764b0340d7b353f5f745891033774e4beab6aa1458a54ff29a1324c05bb9876584026c35f8ec2102ec8fcc3bd0a1a0760486952e147f44236a35c7d818a7024590e1395f097a0d046085ded24ec8c585008d3ffc0321ad040649ce08eb33614760e82582073ae41eca2be37fc15c55a50d668c8647e10bf222172c2d58abfa6e9310e596258402c3f197360294781841f0669822b0449515a5e0b77b23185652a1b0ea8354537b3e9335577a87fa19e9fe47f1039fa286aaa11859d631f3ff74564c6da14c806825820234fb2b8530114b461c6ca8242c8b86a226c95c4c27479ca850d1aea4a52d2985840ba751817e70695a041a5f455c08947fa4e3d6ffc332adeb25691fac4927bbaafd4b3f5f9855946ad9681083aec277766c7f90da7543e912f46aeae07fdd5b90a825820dfb615a61568d6867f45a85c32227f27025180d738a8a3d7fd3c929f624d72395840cc1f728cce6ce2fec21d2648011c14d244c35ba3cbd553593655f6f07d86b8bdf103d52b61143bc1701319517d4a24b778c02e983e02a0f3fd0cd558d472f009825820e5bc21a83616bcccfe343ec36b9dc4c06c90e913df1d8a0b046008651f42caa95840f85bc5e753beed04b3f9072da7a6adadcdb87769528c59e16162e86782b6ce11feacbd5de97e352121e9509a809f613d5bcebf7413fd55f89776c5606e4a9408a100a119534da261638158220a201f79b4d15fd971297a842ac6a4e953b82886df66c0d9723f5870e5725da6380b617601").unwrap(); + let _block = Block::from_bytes(bytes).unwrap(); +} + +#[test] +fn tx_output_ser_type() { + let array_tx_output = TransactionOutput::from_hex("8258390000efb5788e8713c844dfd32b2e91de1e309fefffd555f827cc9ee16400efb5788e8713c844dfd32b2e91de1e309fefffd555f827cc9ee1641a000f4240").unwrap(); + let map_tx_output = TransactionOutput::from_hex("a30058390000efb5788e8713c844dfd32b2e91de1e309fefffd555f827cc9ee16400efb5788e8713c844dfd32b2e91de1e309fefffd555f827cc9ee164011a00039447028201d81844d9052380").unwrap(); + assert_eq!( + array_tx_output.serialization_format().unwrap(), + CborContainerType::Array + ); + assert_eq!( + map_tx_output.serialization_format().unwrap(), + CborContainerType::Map + ); +} + +#[test] +fn versioned_block_deser_test() { + let versioned_block_hex ="820785828a1a00101e2c1a0143a1b35820cee15d6daecaeaf320a4ddb1f7c437846f798e4a9cd08d12fb7821b175c980115820e3c87f196ce9fc40a8d929f3365e247f8f71e1981bffaa7cbdb0aa3a83dc790d582054a580ddf99f67818e0312374cef1f7dcdd59450930898d4d2d10e606b963e49825840ca5d1f988222919982b6a20f4f54ce59626fece7d7c607487762129d5196c731bcd11dfefee94ce5a60a733478970631d41bfc0620769fa7b66ebc16c8a89e5c58502855f21ba12fb101d175c376e19496e464bf37c92ec21395e5bffb35e1ae8f433f2139de166161f2b2b26afe656d3d170acfd11a535a80fca6325479d2262e208b0a4b98a01f4845c45a58fb84cb58011952de5820f2e4c6554da5b773c3f7889944fdb5b1791f8552dcafe2916041a531860e912284582039b66a10f6b78c541ea5ed6ecec4c6dd385b869026ec16c4e48414cb39cac38b0018a258409ccd6cf71a5c337f71a41904c0ea0a889a2321c94374c3a8402d8a7dd25b222abe6cb325c6b39bd63bc99fa84c094fdac2523b72f1a22081903dd047be9be9078209005901c006b35937aba451d4738486ea3ba5644d9306651f09b2012de8acc5136771fc725164ad669dd716f2726dfe138137d09feddf9450b3c51a601577bff35d0d2202c887a260855dd8310fc9365f56a4757ea7d81103d409ea0a8ad51c6ae52fc7fcf4d3d456384b7566b70a2b7bd4e21010a1ad5df12bf5d332e82c1a4a5cca39740252e0ea163f206cacf193e59ebbd0e20d621fa9913c60efe1c035d8ebaa354fbe45768339d53a4e8e04fdea79d00b869a973cfa3eeba2e2668b1dee5fcd7d13762dceb4da804fd749e5fa977ead0003a9739837aa68b80bc5a32ee015f667574a7fbe03b4bf5b027c945fa4497c01efb4ec51f3da2fb2dda33ea7dc1dedcfd2ea2c0a4da5a1c553d033033f4986e2ef5c09bbe326a25e5082c1eec406aeec8105869a9d46a83689a2e026e6e31d4037e700ffeb2920bcab88d1a400976881d17cd84582521482db0be460fb43de88e40a4ee24745ac92ab8b40329bde1d855404478c9f59b05e6322f3640ad6f40d7a771fc6d58e94f8fd0006d54272e36a30034b14327c2e6ffb92ead2f8a4165a3e4a1c44de677829e8e797547b3c0bac4b5ea89cb86c01d5b1e67aee3ba36b8cf9617484db2e4d1bfc37fed1fabb73ce3c9fa600d901028182582088c310befd2e8c9b33b340a56f4ea8141689c16eddef5d9c606055ca35897bd600018182581d6052e63f22c5107ed776b70f7b92248b02552fd08f3e747bc745099441821b00000001f09cac72a1581c34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518a1494154414441636f696e1916d6021a00030739031a0145283409a1581c34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518a1494154414441636f696e01075820e2ea39e82586fa40304df3c2cfc753c6ba8aca62e780f01a0519c34c6d7c25f5a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe185b0181a2005839007ce8986f5f3fb526a6d35d32edac0b6c8624daab6928df1964459c2723bcf2892e8182a68e3aac6f9f42ed3317d115ebad12a17232681175011b00000002540be400021a00030d40a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe185c0181a200583900f7fa5ddf2c3c46ed4d913812d38dd43d585adfa884938adaa7a075dd1bf1e138f2f8beabc963c94cc28ee8ed4b41744601f2edaf50b21efd011b00000002540be400021a00030d40a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe185d0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe18600181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe18620181a200583900189f009d9536b1f52f0629bea3323f48df0eacdff68726f1a32edc49db89995ed3aa88dcfb43790e2e51761fcba7e8594bcc67684f52d524011b00000002540be400021a00030d40a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe18630181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820a944bb37a59451f9a47d5c8888a8a1145527ffb5d45a17c1df40926a42ad08330001888258390038be1948e77426eaca9f967becc5455b11d3d40fb06b99dd3a817d5e75c7a5e1120727f24236cfb5981ec30fd50a2684a5aca866a123a1361a05f5e10082583900bb17dbac8d4a3452dbb6d0c664e804deb14a0b95ded5274007189c3641868c2b4e5289022a3a1f6f47f86823bc605c609d2c47a2db58e04a1a05f5e10082583900f8e61d5f13ab575771af475ac599ad88c7116339f82d2ea969b0e601d6d84c6a5b05cb8f89d24e9d46926975fa1dc08a58b3c26e96c06df71a05f5e10082583900693e466f25213254e061fdc95f8a5f07bf6ef0de0478adbf89a3308f7c4641296645e557c0a6426e140a09d4ba423d158aba1eae06aba7971a05f5e10082583900d93170064d82eab9dea2b3141bc88503ec80e93c8691fb6b223fe310877c17de5bd978526e288334114fada629f699c4e799394aa45c2aad1a05f5e1008258390093ab1cf6cececd048265573176355a322b7732299bbd624f655af2f674984fae4ca1715fa1f8759f9d871015ac87f449a85dea6cf9956da11a05f5e10082583900bc032a8614a84f5d9ee772f2788954e9d664b4264226cd36d0c4ddaeaa22f3a63400c1d96ad118b5cdd300cd039e83ae1957a35b764881941a05f5e10082583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b000000022a4fe9af021a0002d351a400818258206a7e3a926eafa74f72c0d6a721dfdee7a7202b1fac4eee12d8c6dd030217890b07018182583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b00000001eeb2890a021a000296a514d9010281841a3b9aca00581de0db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab238106827668747470733a2f2f6269742e6c792f337a434832484c58201111111111111111111111111111111111111111111111111111111111111111a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe18640181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820a439638a9f8e0f52e153126e8b794b7514f3a0921b08b611f3866a1fc75b7a560001888258390038be1948e77426eaca9f967becc5455b11d3d40fb06b99dd3a817d5e75c7a5e1120727f24236cfb5981ec30fd50a2684a5aca866a123a1361a05f5e10082583900bb17dbac8d4a3452dbb6d0c664e804deb14a0b95ded5274007189c3641868c2b4e5289022a3a1f6f47f86823bc605c609d2c47a2db58e04a1a05f5e10082583900f8e61d5f13ab575771af475ac599ad88c7116339f82d2ea969b0e601d6d84c6a5b05cb8f89d24e9d46926975fa1dc08a58b3c26e96c06df71a05f5e10082583900693e466f25213254e061fdc95f8a5f07bf6ef0de0478adbf89a3308f7c4641296645e557c0a6426e140a09d4ba423d158aba1eae06aba7971a05f5e10082583900d93170064d82eab9dea2b3141bc88503ec80e93c8691fb6b223fe310877c17de5bd978526e288334114fada629f699c4e799394aa45c2aad1a05f5e1008258390093ab1cf6cececd048265573176355a322b7732299bbd624f655af2f674984fae4ca1715fa1f8759f9d871015ac87f449a85dea6cf9956da11a05f5e10082583900bc032a8614a84f5d9ee772f2788954e9d664b4264226cd36d0c4ddaeaa22f3a63400c1d96ad118b5cdd300cd039e83ae1957a35b764881941a05f5e10082583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b000000022a4fe9af021a0002d351a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4010181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4030181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4020181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4040181a2005839002b11f0e68a65cd6a243f1a5ec9d597ba972675a00bd3172a7ddc0293b1d312a60b3824d1820dec5ec769c4af7d7598387c16ca5ba6259f46011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4080181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a400818258203298fb7878ab004c1a4b369eae7fc89abca6342f06557cebf6c89f2d8c21aa9900018182583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b00000001b3152865021a000296a514d9010281841a3b9aca00581de0db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab238106827668747470733a2f2f6269742e6c792f337a434832484c58201111111111111111111111111111111111111111111111111111111111111111a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a40a0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a40d0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a40f0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a40081825820dcdbb7a98286f5d48673c95b05f441bc40731b1e4c3429d192f0c6b7fc3749d100018182583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b00000002186e835b021a000296a514d9010281841a3b9aca00581de0db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab238106827668747470733a2f2f6269742e6c792f337a434832484c58201111111111111111111111111111111111111111111111111111111111111111a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4130181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a300818258207e4fddb60c2034bff37bb7069f9318735fcf4de03e01e9f92251c96dc59318750001888258390038be1948e77426eaca9f967becc5455b11d3d40fb06b99dd3a817d5e75c7a5e1120727f24236cfb5981ec30fd50a2684a5aca866a123a1361a05f5e10082583900bb17dbac8d4a3452dbb6d0c664e804deb14a0b95ded5274007189c3641868c2b4e5289022a3a1f6f47f86823bc605c609d2c47a2db58e04a1a05f5e10082583900f8e61d5f13ab575771af475ac599ad88c7116339f82d2ea969b0e601d6d84c6a5b05cb8f89d24e9d46926975fa1dc08a58b3c26e96c06df71a05f5e10082583900693e466f25213254e061fdc95f8a5f07bf6ef0de0478adbf89a3308f7c4641296645e557c0a6426e140a09d4ba423d158aba1eae06aba7971a05f5e10082583900d93170064d82eab9dea2b3141bc88503ec80e93c8691fb6b223fe310877c17de5bd978526e288334114fada629f699c4e799394aa45c2aad1a05f5e1008258390093ab1cf6cececd048265573176355a322b7732299bbd624f655af2f674984fae4ca1715fa1f8759f9d871015ac87f449a85dea6cf9956da11a05f5e10082583900bc032a8614a84f5d9ee772f2788954e9d664b4264226cd36d0c4ddaeaa22f3a63400c1d96ad118b5cdd300cd039e83ae1957a35b764881941a05f5e10082583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b000000022a4fe9af021a0002d351a40081825820705ab68071f9af1d314e74a053e39a52f3fdf96f9a1280dab30d45f04c05436d07018182583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b00000001eeb2890a021a000296a514d9010281841a3b9aca00581de0db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab238106827668747470733a2f2f6269742e6c792f337a434832484c58201111111111111111111111111111111111111111111111111111111111111111a400818258206fe0c3eae23f779b0694747ed28612f47271b45e84bb3d23c11c1ef2e90fa12100018182583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b00000001dcd122b6021a000296a514d9010281841a3b9aca00581de0db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab238106827668747470733a2f2f6269742e6c792f337a434832484c58201111111111111111111111111111111111111111111111111111111111111111a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4150181a200583900e698ee1c7d4361c6faf62716dca0d435eafd0b25e369a5d68455beaa0f5c16e3e747e7c5a9eb3ff189c0e330683665de9326d2ffe35d0631011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4160181a2005839005bed6070c549f1560cb89361564cd2be7b36536e8da868a218d514e5fd2e3e48dbc0278cc58e47ed50a1ba90cee61ab22c8f4a639c913d4b011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418180181a200583900ab3cd541317d83d072bcc38e1294166dea5d97ce453424b84c547cfc101c5bfa799985a6e96adbb5859e90cbe4a0e4edcbef408a3622558b011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4181a0181a2005839003b3ff2a2d98519fcf53c7abb15b6c4dfe76209c52e4c2065b33b97bc465f9e3a6c6c3a8eac01d39f519b9bf3bc031480936156b7cb2e45c8011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4181d0181a20058390050fc315c9c141b4da62b10525cf5049e8ab1bb8bd96903a6d87c5272bc616bee900ed3135eb065a11faf2100670f0182ae86827df52dba96011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4181c0181a2005839006087b2d29b8a424d7e3a756d08cb078ecb5215fa9344343ac2c6bfb02bdca5a48eca12260be94aecf81b9f21ca41871e06cdc4d12b5aa2e3011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418200181a2005839005deef04c1b44c606775db03444beae0f10c75f437c131628d264b17c439dc3dbc39b8bb91832384d44263001591fd806df73b413da861fd3011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418210181a2005839006087b2d29b8a424d7e3a756d08cb078ecb5215fa9344343ac2c6bfb02bdca5a48eca12260be94aecf81b9f21ca41871e06cdc4d12b5aa2e3011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418220181a2005839006087b2d29b8a424d7e3a756d08cb078ecb5215fa9344343ac2c6bfb02bdca5a48eca12260be94aecf81b9f21ca41871e06cdc4d12b5aa2e3011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418230181a2005839006087b2d29b8a424d7e3a756d08cb078ecb5215fa9344343ac2c6bfb02bdca5a48eca12260be94aecf81b9f21ca41871e06cdc4d12b5aa2e3011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418270181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418280181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418290181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4182d0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418330181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418340181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418350181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418360181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418370181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4183a0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4183c0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418460181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418470181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418490181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4184a0181a200583900189f009d9536b1f52f0629bea3323f48df0eacdff68726f1a32edc49db89995ed3aa88dcfb43790e2e51761fcba7e8594bcc67684f52d524011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418520181a2005839005c85eb9c0aa544a6bb5d1577c7a588c39caca885c8a3a9fceb0933a2cd1a02667d16df1e109350555c325023dbfa31fd9a4a8b99ff904d96011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418530181a20058390030a33756d8cbf4d18ce8c9995feca1ea1fc70093943c17bd96d65fed0aed6caa1cfe93f03f6ef1d9701df8024494d0b3b8a53a1ee37c5ab2011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418540181a2005839001da17fce0b5ae3e7feae117785eb78c77b6272be34a3d381a2722154d29c294b138005ca78de7b329ed6d2763a74a3fa1710a403e18fcb4a011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418560181a2005839001da17fce0b5ae3e7feae117785eb78c77b6272be34a3d381a2722154d29c294b138005ca78de7b329ed6d2763a74a3fa1710a403e18fcb4a011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418570181a20058390098bebc68cf6f12a7aca6531cef75d83c1b6e323485146195ffdd727dd99bbe7f44fd382de2ca6d9e5e9cc26f940decdb1b12b1a98e343274011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4185a0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4185c0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418610181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418620181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564050181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564070181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d00045640a0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d00045640b0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564181a0181a2005839005746c1b032f826b5e5256357a713a7ca63988fe2ff862e0396993b97ef0cbd5199d0e460725b3e79d371deb42110d40b778d3bf162777d4c011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564181b0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564181e0181a20058390020de866f290f45141315081d903f3eb3c06f3735e2a5b70f6a138462ada99823bc02291029853dc5338bc6e62b0540dbea54d9384f372639011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418200181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418210181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418230181a200583900057fd21bf903c585ea95dd927dee373b4cc1febc61874c48571dfb88a0a307af2a3e6a55a238fe323f9e54be10c54a8a8b25939a4f9ab35a011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418240181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582043c59e533d0934a6878a81403ec71a2225bb22d0764471cac8b5545120b475760001888258390038be1948e77426eaca9f967becc5455b11d3d40fb06b99dd3a817d5e75c7a5e1120727f24236cfb5981ec30fd50a2684a5aca866a123a1361a05f5e10082583900bb17dbac8d4a3452dbb6d0c664e804deb14a0b95ded5274007189c3641868c2b4e5289022a3a1f6f47f86823bc605c609d2c47a2db58e04a1a05f5e10082583900f8e61d5f13ab575771af475ac599ad88c7116339f82d2ea969b0e601d6d84c6a5b05cb8f89d24e9d46926975fa1dc08a58b3c26e96c06df71a05f5e10082583900693e466f25213254e061fdc95f8a5f07bf6ef0de0478adbf89a3308f7c4641296645e557c0a6426e140a09d4ba423d158aba1eae06aba7971a05f5e10082583900d93170064d82eab9dea2b3141bc88503ec80e93c8691fb6b223fe310877c17de5bd978526e288334114fada629f699c4e799394aa45c2aad1a05f5e1008258390093ab1cf6cececd048265573176355a322b7732299bbd624f655af2f674984fae4ca1715fa1f8759f9d871015ac87f449a85dea6cf9956da11a05f5e10082583900bc032a8614a84f5d9ee772f2788954e9d664b4264226cd36d0c4ddaeaa22f3a63400c1d96ad118b5cdd300cd039e83ae1957a35b764881941a05f5e10082583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b000000022a4fe9af021a0002d351a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418250181a2005839008f221a3021b0c09c57336733b0411d9d664e5d5e259096033a9d4bbecbce4335fa28195472386e53f0c3ab74d5cd254797d1100e4b1a33b8011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418290181a200583900d804dcdd0b0ec6ed0d8d2cd210a03b14f87c6849024930a8d6c91cf551a6a756817f0a3e1a1410730acf27202e7a9b63de26087e5cf466a5011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564182b0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582072af166d0cd8883c9abb1189ae93acb1fce482ca57cad9b14711bca9627b98d80001888258390038be1948e77426eaca9f967becc5455b11d3d40fb06b99dd3a817d5e75c7a5e1120727f24236cfb5981ec30fd50a2684a5aca866a123a1361a05f5e10082583900bb17dbac8d4a3452dbb6d0c664e804deb14a0b95ded5274007189c3641868c2b4e5289022a3a1f6f47f86823bc605c609d2c47a2db58e04a1a05f5e10082583900f8e61d5f13ab575771af475ac599ad88c7116339f82d2ea969b0e601d6d84c6a5b05cb8f89d24e9d46926975fa1dc08a58b3c26e96c06df71a05f5e10082583900693e466f25213254e061fdc95f8a5f07bf6ef0de0478adbf89a3308f7c4641296645e557c0a6426e140a09d4ba423d158aba1eae06aba7971a05f5e10082583900d93170064d82eab9dea2b3141bc88503ec80e93c8691fb6b223fe310877c17de5bd978526e288334114fada629f699c4e799394aa45c2aad1a05f5e1008258390093ab1cf6cececd048265573176355a322b7732299bbd624f655af2f674984fae4ca1715fa1f8759f9d871015ac87f449a85dea6cf9956da11a05f5e10082583900bc032a8614a84f5d9ee772f2788954e9d664b4264226cd36d0c4ddaeaa22f3a63400c1d96ad118b5cdd300cd039e83ae1957a35b764881941a05f5e10082583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b000000022a4fe9af021a0002d351a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564182d0181a2005839001c4595c4f3180180c9e822f1ac0f2955dd329eeeb94752a84281ff5295558528c6e1f7f2e16d94b74d227b9fd709edd2aeb0ab1556db75fc011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564182e0181a200583900c008dd489b67e0a774fe18d79ee8d1e280264933d3b31ba44cb37755dca94fb45aa2192ab26eff8409ea010fa2d4761efa92437e0d5b6b60011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564182f0181a200581d60fc38cce3448bf3d2790ca85d6b09026f7c86f21095c31f9925cf49a0011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418300181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418340181a20058390023a6fcbc8affc61518cff034c013aecf083dc64fe673ffc95cc9fd9e1fad7e0b1d0dd8820703a4f59c2488d148193a48d8fdc23a6dca8137011b00000002540be400021a00030d40ff9fa200d90102828258201287e9ce9e00a603d250b557146aa0581fc4edf277a244ce39d3b2f2ced5072f5840ae4cc1168265e2f60fec9ca9b644eaa42a77e65a39176e04aef29b01e25653a307d39ba61761f8d1ca44696e1d6bdf7a0679413ea3c448f76268e6eb02074102825820742d8af3543349b5b18f3cba28f23b2d6e465b9c136c42e1fae6b2390f5654275840112c95c93013e63fa73ee6a645fd522808d4dee019626e395a8042755c15fb1824e1503c17ea843a838809f55822366b05bce2e378d0b955e66d625c8e9acf0001d90102818200581c45d70e54f3b5e9c5a2b0cd417028197bd6f5fa5378c2f5eba896678da100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584005383334e98e9263b93ffeb3e5908dbd5657caa67d32f9964d7f91dbda76fff164cbeabb981beef470d0d3e1724b85847e6fbc1c039084a817110eabf9d29e08a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258406151f0d0cae4ef0ace62dc03300dcee276765c1b493ac61f770d0633f0f71fe0d642ef29b593c511034a847dd569c56a0278e063c46d6d060abad4e6baf4b705a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840cf518f30d291871a0277e367534557205cc015d3a0d78070e1aee298aeaae3e81d70b42c464a63fa741b5300b48c1699dfc39fdf07f96b8eb240c7d6d3267805a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584056185de4c1d012d322e7e82ae98f24887ed7264f262db53f019e5900b9110a439e5a462a75be036f9f04b0ddcf09decb0894c7b6b9ff17ab4cae8184072d690fa100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840ab78c606155b6aacbcf6692a18d97dd8773b4ae4c96684e4abb9cc59233023f67650ef7259069deddb65ba770ac3a1401d169ce33ec9483b8ebb9e83472e2c06a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840e21574b02002efcfe81382326aa98a0a971327ad4049690a04985400fcb14db7adc8149a0ce4dbfb5afa0d240ed9da23f15c1020d2826f50fc579a10a3662d0da10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840e64e3c19644edb6e788112ac75b4426ef94d535f1ffd9a34e86745777feaf083dc8e847a62634fef320a08b566c24ea26e8dc9e7b49fc456554215cedc0d3508a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840f49fd8eeaa366873aeb2530b2bbcbf7c5970866162ae7250c4b913e19062de1396ed70d1e32a4605071bac11c2cde3fec1dc5b37044cbea073668fe5c478400ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840f0ddd023e0dbda32d296b359db809b0088246e512fd34c7c0cc4b5ae974361873e02330e955eaaf97117525bcb3cd014bb70810f8d0d62a28c3242c86d8c3a08a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840615ee0444f039f02b26791872d6cd5562728cdc6dad02acc71475567b09f3d4b4655c601bf816ef6d11b2f3f81eeb6db09d800bf1bf4e2daf29493338c232901a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840d62bfd428359f4cd04950cc73f574f0ec1c613284fdff8028ed3d876b18b69335beee9792410c6dbdc1b196b4703f250fbaeb66569869ae97d7ee843b9053405a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840d813a836cc44c70647b2e7941fb72a4f720f16aca17e155a6c6a6f9bf175b1e49a3beff6edcfb0c442cc24790a12ee0b1d499a32fdbfc0a850f4846708ea340da100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840aae6ac20cd419eaa7f3a95144f9ccdb46401a0db295d544e920a54b5c24fb63197fde03f12174800c3cf5318a73d92ebc53c2ba97803766892add32fd9feb400a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584071d223fd255df1e912a9c0a8230ee9f0ac95d0aa325cd31e50701ac355dfb5f3fbb27983b372c1410156eeba9163aa0f8a9787dab8c44e7afd4e2d07459a4708a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840b7f821c0ff66fcbbe7c61f43725aa2234297d6765e002d0130301cba13465fe89f59a596549725542445c76d17cedc9c9cfea8b8862d41405646d725dabc7d08a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e4584045830e5de108b9353b0c4c561af296e79eddb26b8ccfb18c5bd9fac1baf8d477691229c0bb9ea212ab56d9ae76c92de6ae50686fc0619510b8c35fb69c6b4402a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258400635ac784fe71d2f7927114edfc709dcb56013617df4edb9b6b770b067e7709e8abfd4cdcdd61512894fcf02f16e1d72bfe60fbfb86b815631d791bab132b909a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584032d67fe72feaf2175455815bbb624ee1b93f5efce905280158db94bbb2b5371d9eaff1bed6eddf9eafe8ff64b55f1d7213294bdb459e0b00c437edbcabf4cf07a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258404533de52e9e3f51e39951c9e197f6900081e98f38f3af5c4a7fe9219f8c311eaa43942b7a290ecbbbdd0bf4ef4ef1d11c39e6de4083c86892a6026c27bcf2509a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840d46541920ce2c31ffaa00cb20e8f5f00f48b6b8aa5cda67d22ea4bf12fd318461a0d8c25ee04cd7446e18f0de59b0fd4f6631e29bc8207073f2404793ae5f108a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584041bcd52ae44a3ffaa1abe1cab6c8b21f8010e2f1aee1d8651b9f6e94aabf5b2dbcedb45dd154b78dce1c5b9679956dd25153a0d945d3eb83c1de3b713e721008a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840027da3169c9c1fb9a67104061698bb0dadb2f58b660af4b461e7353fab1545a3d03157e077a388ec8556176239df3241255feb1f13b5e406bf7c3ad3af7d4202a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e458401ae63dc54e511965f7010971de7fb99585afe492cb8084b395ee01555c1e5657ab08a24be0f70d4e9cd1bde2a6ae31815c5f64025c0415afe2d503b2cb5b3e0ca10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840d2211679ca8e9cc5de71b21bac2b58fd355f5cbd2b42ff31ec37af77b776fb77c64fa76a830f240c29a4b95ae6bff9c58fc6bc2b3d18b57a2af11710ae6e3006a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584084bb7bf64ecea446d6beca88bfa2c7de71d8899e96006920c4c18e52f042aa71e1d27e60bdb6d9d6b1aa2e3330f59ee95b2c001909ff8994ea1fe4e5cd7a760aa100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840c6f3d6194a2fdb2a50417f80dd020adf866c91a102f22eb6bc66f5131292a1a42e9a3550e18e06cb17bd153c08f55a6cce3a1c82052ec9197495900f3ca4f407a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258401a28cac7e80a657563e1433459f369bb0cb05e7e3ead78378dfc2ad15baa344e76e1ac0ca631c67848e81896fd6838b3821961928911635ca069f12c05772a08a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840d201ce4ca5572db792d1563ef3614f2e2b27072e4751327f4a8f75201183a189ac57cdd9399474850e87031c7545c896ebab3983434bb6005690b9ad8fd9d50aa100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258403f45e44aa7457c714599f0100702ec265e91493e30c57ba4f1f74d912858bae8fb71fdf2faddf865c816cb0218eda0db17b707c8f429290f1a1c02b6a4450a0ea100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840e16b1d8f5992fda268c9d7e5c0ab6c5d38b8abaa6b92ccae5b0d2f3079d098ab67ba9a15b27807746f3c7695091ec5bb74ba8772baae14d2786eb8a512d70201a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840628a5491c5d0b4e0202b7aae87a343afd642345b823252355f6d392d8398d2174c141622e3de167b4f82c3cb8b4e8105b341851005d2ec0c1e35c354006a910ba100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258408ad2ee919f520a903764e0322800f7c086f870374f063d2e62ad1dfbf54e71305d90371abe3a196132e123b9248281f2d676fb29442f80249f644ce1185dfc03a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840d53bf84fe8712171151bb6d5f988d76428292639737d817986b46d629aea6eac2a90675cbf0347ec004ce23f9ca0b2dcff5c6d1be91ab478634de8ba8ab96102a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258404702623a2a94b9efbc03dc7d506c4bd70c1e0fea8b21f3b76c592883f0c364ffc12215e59f9ea4d2eed2e786376e6128650b4c9c3f6ad9419f070fb458efa10ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584072bb89ee81a7bcc4a866ae498d3ca076d5d5a885547c7f5899b8b59b3077310f58df420e470bf36d4ed5beaaf30eb361f04ed578cdbd0ef04f7cb573f0c0770ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840e112bb46921840820f4d5b40ec45699bc1b818ca8fe77fcc222a6fa1edb2425487f32e2039e2cf6077ce1e8e2e0b0d0581c64fb866c1c183344af131ccb9390ba100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840039329e261d8386f81a28f5ef5062196a46b5d4389b06bde97e662f69e37812c3ee75352f392121f58e76e5c1e1649656632b01ea46f932ccedcee102d625001a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840c1dab5e2482101ad1bd04f4018425c7133171aaf1274573ed35305f4e37bddadb3636f0aa098d2c0b5f615e8eb629bb94afac5d4c4c0743dba16a847d898b905a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840abb898b3e1ae3c4c9d068a4517b83a070d4037f979b6365ea5eb368e7d43b3fd2152fb93a462fdc553f973d90ab136332057fb66ea529d4fbc53e7f082b6fe03a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258404a09ccfd9c09c6a453f46c721d280065081d89aa4b84fc809d75db1b923e78963bcbf36d64786d8c09c527e90da744e83116617b2e18d9145bac6cf66f876c08a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258408df2408adbd8b4c990a913b9ed2455c9de72d561ddb8f3ec0da5d1513f417a2fcee9ea9ace30cb840d37950c41455bd3655d12d534b70a6eac7034950f821108a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840534b54cc145722e3f7a62935d84c025e17e31c4b08d3f3fb16bb7673d37e9afb07fbdb5ffce5aeef743376bac161973e565e1c12db97bcd879cd7e9030c2a00ea100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840664fd5d8bc5d93509d02104f39e7a22c6cd894f49935cac9e662a9202b9a64baa9f55cd8aa07d3d1e095e9b974c59c0a8c50d14b0d077d70e236ad5cf52ac104a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840469cdadb48349224303d14980cab5c2ae5dacd0f6e36714d8dcb9ca85fa4eb688bd7b1334e30f8718178f7f36d8c5b204e0f9cdce5f88762fc2cbe2cb28c1d03a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840f330820de229a476e1b3f84dfcf9ad98264070212e6e2d61d8b05afb1e12a1426cfd7cb0b284db237d5882cecd6e8f1fe2cf9ddc06783242812178bcb053a105a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584066b89001a28583bed59dbd07d359333207eb74d39ee092c0bf1da4351da64d38c9938a3682bb52a4253dc76074767b4cc2bc1eb2a31bbe6be3c45a5c52cbdf04a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840f365b299297ade5117d72156050cb81a76ba0b859cb46d0f2326c4071110440108b20390f878ba082d41217b2a8fd5f1435b9ba48d176ad5dcb6faff54976b0da100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258407fb0b1f28d6ca64a29c6c7a51a2ab3436809b5038c06b8204104e3b98afa915246f742e2f1bd569f5132d2bbdcaeae68215a0b0f17f6367ce4eea37ed951ec01a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258406e6d2263c440486fc1b3c2aa930e519f20b70f70c28cb532d031f63cefc55c56f4647b10dd6390aa0d0f2ba75bf6cbe3ce2fc6d928dc4db74388f1e5e3057b0ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840dbb299923c24a70ae10dc637d862b750b3e6548e64c590674d2ceb87b7535199ea8dfd024694d26ae1dbbca683b1a4ba90af7d1680a9b8e4819a2ee6229e2408a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258405b2397d031c48f56b43416daea99dd3d8bd1733cb83c2a688dbe8b5dd9bfe64d596280d71973d7d540e929262dafd79b14954b855635fe845642090241003503a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840482c9089f2d60eb069432bf7f7213178a6fe3d52d37a4fa5aec677875bccdac64de7a45c6eb0bd4996414412b12d3e887a1b391e775ef56c47d53f9c944d020ba100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258401972d18c1e9c62837121efafddc2ec778a3a8f9ec5f534c9922778905d8f809609d6c92e427852d8b5f822ad590fdeacf3877c8056f0768b44b025a2b79e7704a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258409b7141b69a493bc4d597a645ed488325492772ad4c3cd5c5c7805a5d951a4b6ed960ea27428d1add867fe7c209f4e65000bdfa878bd7a4357b223e9c55af450da100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258407ac2d7823887de83bca86216e424ccb63fe9f4fa1f106bffc6afa388e91845c97177c410f1a8ca5ecd9f2701c42f5f9dd2faeb0ecf2163a37521badc8a6c1b03a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840c49b6714ccbbdebebb9461a2efb39b9ac5e00a389aadfb2a1b4fe384733610c45e1f03825f65e182988da97659a71e378d49d17fb93d76b80d579b7d49399b06a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840b53ea3508cbd7da47fef05e98c0b31b13ec33de4596ba4061a8e04d91b1015c49f328da58084a6f573d93cdb7aa0972a1a1936a69ee7362adf65df3eae4e2400a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840b1c6b15b311423aa83dfaebe118d1a2a3ff006399f2a63fa82f0d0e0c12bc2b844ec78f5bc8baeef588d15b2237db48cbfa48361a6d630052c9b68e62e035601a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840bb544721cd95655497375390da19fbd87b3c63a4edf333688e2fee7935e96b6572f84b81d80fee5239062be6d3c6a75a5f0c50696d3c6663d26cecdfd8fdc808a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840a7018dfacec705459856bc706b7d05d04c56867fb64dfd0cf97fa980e881cc609e91cf6df204fb6906f860e5cf390e0290d302b6231664aad8c2b4cb30090609a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258403ddf27cce21fbf1a361233d2bcff92ecc9d3cce64c3d8186495e3509b843a0a326f528be8241b8557bf3cdac9c304fcf0fa8fd2a8e389d6acf9fc62b5626d705a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584013bd40ae7383feb674c2cd3357122aec3f6efe17b9b4f25c47cd3dfec194d0c4f20a52cc30fb92245c1a20a962772808f3dc6ee51261c86af16879a1fed2210ba100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840a1cf100aff45ee45c0f868214187640c8c29cb005c7aab7100ff86338d78f972733a611b4e0dae436fe9e1493d6ece69c35ada3cc6506e730ea1bae277468108a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840667d6d2987788454c41f2e86867fe98e1fd48aa789fbf9cf2208f927a8f9941d0384ebf3e3e45ee80c49934aad9b6ccaa13179b69f35b9acd21b55f56caff80da100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258401cd5c48fa46d8f0fb07c7737b7719d1fba5729478be3eef3e2e19942c4d6d54b01a569eb34d4f4be84a2f6961832ec17bade0511cbc01f5db5749a09bb4d8808a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258403bb5ddd91b5f5d7366b41330bf5bbbf7cf7d703bd50376ac21b07c6da696562266361678c06247a57111c63bc1fe58463a8c125e0d117bdf05cd4fe57bb8b90aa100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840397067045ffe0cf7126a5f73f228e1bc8721c617ebb7c41c1bc2f7b6c8cc50bf2370bc1ee679bcb0581e11da1e186504f2e3f3322fddf40a1863067ffc5e2903a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840831f5ea5dd354f5a522e044b53aa1966d036871d5a3b7d2323e404996907a33aff5aabb9db22301064033045cbf14c91d29de84b8fbbb75683ff1cb51fd0920aa100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258401e8cc65db39cb5e9d54dac50cda84c55fd2f989f667a11dc46521768ac2f46a27a70173a92e849ee621ebe3025d87088528e7450b8312d678b6249f5a124f70fa10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840f4835bbcf5459db0826e27ea95d3ac31f7bea56c0253716212ef421995c7546a963ac89dc6cffad75692a149372cbdeaa19a9dcd171ac423711e8d71c495d703a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258408039411659145a9fb3863b2ae2f3890009bf004332f58daa6662368a7378d215cc7f40569284d5b86c5a7be210cdcb5551633762b5a7d3f8ad2095a220fec609a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584002c9ec1d95d0464eba5f4a656d72b55a92a80078e83f2f47682729af0fc782a68f3f31db7c64018ae8fbd36c5ac72d5573357a7578359056b9d3f9a29467d80ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258406f176d0fdb8768aad902c8fc115eb797719ef62a93938d7f09bbb213a88d61294143ec1d508a2a450f0e16ab3e2ffb7e0ed4cd7f75b3f2ff9f70cfaa77764506a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840f4262eeb9183eec1e896b9be61984ed9d4192b38825ba1b560ea23fe6e3224d3c94f4cc64174c1d9166e665e1a1ff8f0c84024bb8b9068b853fe4ce683d96906a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840df64e327056e335f484582fa0e4188e62620968e955431fc3576da2c1935b15ec605bb5d738f5000dcdc022646f9545d6932c2c79611dccab116295ca03c2b04a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840132cce5cea5d8bf7e9b802e4477eff041ebe1c12a8b8658a42ae91727cbf4f39b0d23831c70923a68ad7a023092bcecb61ac6253fdd17be00cecc37a71301300a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840842ecaf1b907cb34799d78d6f35eb349a986559a396840aeba5f6e8dc7e4172c16bddcb1f926a21175531478773046e9484aeb4ca83c1cbaf25f5a4813afdd0ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840a417cdf9e02755e53d6061ce0c93cacb7de24ce33e3fda9ac3a85414a7bf62676446822875972867d5647e46c22e21099e5dd8e4114949b30b86146b0dba1b05a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258401f2e86831349fa458c3e2403e2caacbf046fae3c513575e8ce2d34037d34337f7e58cc98cadf034e8bce930335285220624945b316fe6af71e8ef0d12ef05001ffa100d90103a100a11902a2a1636d73678f78264175746f2d4c6f6f702d5472616e73616374696f6e202336313138303020627920415441444160783c4c6976652045706f6368203234352c207765206861766520303132682032386d20323773206c65667420756e74696c20746865206e657874206f6e6578374974277320446f6e6e657273746167202d20313520466562727561722032303234202d2031333a30313a333320696e20417573747269616060607820412072616e646f6d205a656e2d51756f746520666f7220796f753a20f09f998f783b4265206b696e642c20666f722065766572796f6e6520796f75206d656574206973206669676874696e6720612068617264657220626174746c652e68202d20506c61746f6078374e6f64652d5265766973696f6e3a203462623230343864623737643632336565366533363738363138633264386236633436373633333360782953616e63686f4e657420697320617765736f6d652c206861766520736f6d652066756e2120f09f988d7819204265737420726567617264732c204d617274696e203a2d2980"; + let block_hex ="85828a1a00101e2c1a0143a1b35820cee15d6daecaeaf320a4ddb1f7c437846f798e4a9cd08d12fb7821b175c980115820e3c87f196ce9fc40a8d929f3365e247f8f71e1981bffaa7cbdb0aa3a83dc790d582054a580ddf99f67818e0312374cef1f7dcdd59450930898d4d2d10e606b963e49825840ca5d1f988222919982b6a20f4f54ce59626fece7d7c607487762129d5196c731bcd11dfefee94ce5a60a733478970631d41bfc0620769fa7b66ebc16c8a89e5c58502855f21ba12fb101d175c376e19496e464bf37c92ec21395e5bffb35e1ae8f433f2139de166161f2b2b26afe656d3d170acfd11a535a80fca6325479d2262e208b0a4b98a01f4845c45a58fb84cb58011952de5820f2e4c6554da5b773c3f7889944fdb5b1791f8552dcafe2916041a531860e912284582039b66a10f6b78c541ea5ed6ecec4c6dd385b869026ec16c4e48414cb39cac38b0018a258409ccd6cf71a5c337f71a41904c0ea0a889a2321c94374c3a8402d8a7dd25b222abe6cb325c6b39bd63bc99fa84c094fdac2523b72f1a22081903dd047be9be9078209005901c006b35937aba451d4738486ea3ba5644d9306651f09b2012de8acc5136771fc725164ad669dd716f2726dfe138137d09feddf9450b3c51a601577bff35d0d2202c887a260855dd8310fc9365f56a4757ea7d81103d409ea0a8ad51c6ae52fc7fcf4d3d456384b7566b70a2b7bd4e21010a1ad5df12bf5d332e82c1a4a5cca39740252e0ea163f206cacf193e59ebbd0e20d621fa9913c60efe1c035d8ebaa354fbe45768339d53a4e8e04fdea79d00b869a973cfa3eeba2e2668b1dee5fcd7d13762dceb4da804fd749e5fa977ead0003a9739837aa68b80bc5a32ee015f667574a7fbe03b4bf5b027c945fa4497c01efb4ec51f3da2fb2dda33ea7dc1dedcfd2ea2c0a4da5a1c553d033033f4986e2ef5c09bbe326a25e5082c1eec406aeec8105869a9d46a83689a2e026e6e31d4037e700ffeb2920bcab88d1a400976881d17cd84582521482db0be460fb43de88e40a4ee24745ac92ab8b40329bde1d855404478c9f59b05e6322f3640ad6f40d7a771fc6d58e94f8fd0006d54272e36a30034b14327c2e6ffb92ead2f8a4165a3e4a1c44de677829e8e797547b3c0bac4b5ea89cb86c01d5b1e67aee3ba36b8cf9617484db2e4d1bfc37fed1fabb73ce3c9fa600d901028182582088c310befd2e8c9b33b340a56f4ea8141689c16eddef5d9c606055ca35897bd600018182581d6052e63f22c5107ed776b70f7b92248b02552fd08f3e747bc745099441821b00000001f09cac72a1581c34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518a1494154414441636f696e1916d6021a00030739031a0145283409a1581c34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518a1494154414441636f696e01075820e2ea39e82586fa40304df3c2cfc753c6ba8aca62e780f01a0519c34c6d7c25f5a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe185b0181a2005839007ce8986f5f3fb526a6d35d32edac0b6c8624daab6928df1964459c2723bcf2892e8182a68e3aac6f9f42ed3317d115ebad12a17232681175011b00000002540be400021a00030d40a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe185c0181a200583900f7fa5ddf2c3c46ed4d913812d38dd43d585adfa884938adaa7a075dd1bf1e138f2f8beabc963c94cc28ee8ed4b41744601f2edaf50b21efd011b00000002540be400021a00030d40a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe185d0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe18600181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe18620181a200583900189f009d9536b1f52f0629bea3323f48df0eacdff68726f1a32edc49db89995ed3aa88dcfb43790e2e51761fcba7e8594bcc67684f52d524011b00000002540be400021a00030d40a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe18630181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820a944bb37a59451f9a47d5c8888a8a1145527ffb5d45a17c1df40926a42ad08330001888258390038be1948e77426eaca9f967becc5455b11d3d40fb06b99dd3a817d5e75c7a5e1120727f24236cfb5981ec30fd50a2684a5aca866a123a1361a05f5e10082583900bb17dbac8d4a3452dbb6d0c664e804deb14a0b95ded5274007189c3641868c2b4e5289022a3a1f6f47f86823bc605c609d2c47a2db58e04a1a05f5e10082583900f8e61d5f13ab575771af475ac599ad88c7116339f82d2ea969b0e601d6d84c6a5b05cb8f89d24e9d46926975fa1dc08a58b3c26e96c06df71a05f5e10082583900693e466f25213254e061fdc95f8a5f07bf6ef0de0478adbf89a3308f7c4641296645e557c0a6426e140a09d4ba423d158aba1eae06aba7971a05f5e10082583900d93170064d82eab9dea2b3141bc88503ec80e93c8691fb6b223fe310877c17de5bd978526e288334114fada629f699c4e799394aa45c2aad1a05f5e1008258390093ab1cf6cececd048265573176355a322b7732299bbd624f655af2f674984fae4ca1715fa1f8759f9d871015ac87f449a85dea6cf9956da11a05f5e10082583900bc032a8614a84f5d9ee772f2788954e9d664b4264226cd36d0c4ddaeaa22f3a63400c1d96ad118b5cdd300cd039e83ae1957a35b764881941a05f5e10082583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b000000022a4fe9af021a0002d351a400818258206a7e3a926eafa74f72c0d6a721dfdee7a7202b1fac4eee12d8c6dd030217890b07018182583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b00000001eeb2890a021a000296a514d9010281841a3b9aca00581de0db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab238106827668747470733a2f2f6269742e6c792f337a434832484c58201111111111111111111111111111111111111111111111111111111111111111a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe18640181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820a439638a9f8e0f52e153126e8b794b7514f3a0921b08b611f3866a1fc75b7a560001888258390038be1948e77426eaca9f967becc5455b11d3d40fb06b99dd3a817d5e75c7a5e1120727f24236cfb5981ec30fd50a2684a5aca866a123a1361a05f5e10082583900bb17dbac8d4a3452dbb6d0c664e804deb14a0b95ded5274007189c3641868c2b4e5289022a3a1f6f47f86823bc605c609d2c47a2db58e04a1a05f5e10082583900f8e61d5f13ab575771af475ac599ad88c7116339f82d2ea969b0e601d6d84c6a5b05cb8f89d24e9d46926975fa1dc08a58b3c26e96c06df71a05f5e10082583900693e466f25213254e061fdc95f8a5f07bf6ef0de0478adbf89a3308f7c4641296645e557c0a6426e140a09d4ba423d158aba1eae06aba7971a05f5e10082583900d93170064d82eab9dea2b3141bc88503ec80e93c8691fb6b223fe310877c17de5bd978526e288334114fada629f699c4e799394aa45c2aad1a05f5e1008258390093ab1cf6cececd048265573176355a322b7732299bbd624f655af2f674984fae4ca1715fa1f8759f9d871015ac87f449a85dea6cf9956da11a05f5e10082583900bc032a8614a84f5d9ee772f2788954e9d664b4264226cd36d0c4ddaeaa22f3a63400c1d96ad118b5cdd300cd039e83ae1957a35b764881941a05f5e10082583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b000000022a4fe9af021a0002d351a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4010181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4030181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4020181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4040181a2005839002b11f0e68a65cd6a243f1a5ec9d597ba972675a00bd3172a7ddc0293b1d312a60b3824d1820dec5ec769c4af7d7598387c16ca5ba6259f46011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4080181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a400818258203298fb7878ab004c1a4b369eae7fc89abca6342f06557cebf6c89f2d8c21aa9900018182583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b00000001b3152865021a000296a514d9010281841a3b9aca00581de0db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab238106827668747470733a2f2f6269742e6c792f337a434832484c58201111111111111111111111111111111111111111111111111111111111111111a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a40a0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a40d0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a40f0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a40081825820dcdbb7a98286f5d48673c95b05f441bc40731b1e4c3429d192f0c6b7fc3749d100018182583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b00000002186e835b021a000296a514d9010281841a3b9aca00581de0db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab238106827668747470733a2f2f6269742e6c792f337a434832484c58201111111111111111111111111111111111111111111111111111111111111111a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4130181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a300818258207e4fddb60c2034bff37bb7069f9318735fcf4de03e01e9f92251c96dc59318750001888258390038be1948e77426eaca9f967becc5455b11d3d40fb06b99dd3a817d5e75c7a5e1120727f24236cfb5981ec30fd50a2684a5aca866a123a1361a05f5e10082583900bb17dbac8d4a3452dbb6d0c664e804deb14a0b95ded5274007189c3641868c2b4e5289022a3a1f6f47f86823bc605c609d2c47a2db58e04a1a05f5e10082583900f8e61d5f13ab575771af475ac599ad88c7116339f82d2ea969b0e601d6d84c6a5b05cb8f89d24e9d46926975fa1dc08a58b3c26e96c06df71a05f5e10082583900693e466f25213254e061fdc95f8a5f07bf6ef0de0478adbf89a3308f7c4641296645e557c0a6426e140a09d4ba423d158aba1eae06aba7971a05f5e10082583900d93170064d82eab9dea2b3141bc88503ec80e93c8691fb6b223fe310877c17de5bd978526e288334114fada629f699c4e799394aa45c2aad1a05f5e1008258390093ab1cf6cececd048265573176355a322b7732299bbd624f655af2f674984fae4ca1715fa1f8759f9d871015ac87f449a85dea6cf9956da11a05f5e10082583900bc032a8614a84f5d9ee772f2788954e9d664b4264226cd36d0c4ddaeaa22f3a63400c1d96ad118b5cdd300cd039e83ae1957a35b764881941a05f5e10082583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b000000022a4fe9af021a0002d351a40081825820705ab68071f9af1d314e74a053e39a52f3fdf96f9a1280dab30d45f04c05436d07018182583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b00000001eeb2890a021a000296a514d9010281841a3b9aca00581de0db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab238106827668747470733a2f2f6269742e6c792f337a434832484c58201111111111111111111111111111111111111111111111111111111111111111a400818258206fe0c3eae23f779b0694747ed28612f47271b45e84bb3d23c11c1ef2e90fa12100018182583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b00000001dcd122b6021a000296a514d9010281841a3b9aca00581de0db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab238106827668747470733a2f2f6269742e6c792f337a434832484c58201111111111111111111111111111111111111111111111111111111111111111a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4150181a200583900e698ee1c7d4361c6faf62716dca0d435eafd0b25e369a5d68455beaa0f5c16e3e747e7c5a9eb3ff189c0e330683665de9326d2ffe35d0631011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4160181a2005839005bed6070c549f1560cb89361564cd2be7b36536e8da868a218d514e5fd2e3e48dbc0278cc58e47ed50a1ba90cee61ab22c8f4a639c913d4b011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418180181a200583900ab3cd541317d83d072bcc38e1294166dea5d97ce453424b84c547cfc101c5bfa799985a6e96adbb5859e90cbe4a0e4edcbef408a3622558b011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4181a0181a2005839003b3ff2a2d98519fcf53c7abb15b6c4dfe76209c52e4c2065b33b97bc465f9e3a6c6c3a8eac01d39f519b9bf3bc031480936156b7cb2e45c8011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4181d0181a20058390050fc315c9c141b4da62b10525cf5049e8ab1bb8bd96903a6d87c5272bc616bee900ed3135eb065a11faf2100670f0182ae86827df52dba96011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4181c0181a2005839006087b2d29b8a424d7e3a756d08cb078ecb5215fa9344343ac2c6bfb02bdca5a48eca12260be94aecf81b9f21ca41871e06cdc4d12b5aa2e3011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418200181a2005839005deef04c1b44c606775db03444beae0f10c75f437c131628d264b17c439dc3dbc39b8bb91832384d44263001591fd806df73b413da861fd3011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418210181a2005839006087b2d29b8a424d7e3a756d08cb078ecb5215fa9344343ac2c6bfb02bdca5a48eca12260be94aecf81b9f21ca41871e06cdc4d12b5aa2e3011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418220181a2005839006087b2d29b8a424d7e3a756d08cb078ecb5215fa9344343ac2c6bfb02bdca5a48eca12260be94aecf81b9f21ca41871e06cdc4d12b5aa2e3011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418230181a2005839006087b2d29b8a424d7e3a756d08cb078ecb5215fa9344343ac2c6bfb02bdca5a48eca12260be94aecf81b9f21ca41871e06cdc4d12b5aa2e3011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418270181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418280181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418290181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4182d0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418330181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418340181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418350181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418360181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418370181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4183a0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4183c0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418460181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418470181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418490181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4184a0181a200583900189f009d9536b1f52f0629bea3323f48df0eacdff68726f1a32edc49db89995ed3aa88dcfb43790e2e51761fcba7e8594bcc67684f52d524011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418520181a2005839005c85eb9c0aa544a6bb5d1577c7a588c39caca885c8a3a9fceb0933a2cd1a02667d16df1e109350555c325023dbfa31fd9a4a8b99ff904d96011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418530181a20058390030a33756d8cbf4d18ce8c9995feca1ea1fc70093943c17bd96d65fed0aed6caa1cfe93f03f6ef1d9701df8024494d0b3b8a53a1ee37c5ab2011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418540181a2005839001da17fce0b5ae3e7feae117785eb78c77b6272be34a3d381a2722154d29c294b138005ca78de7b329ed6d2763a74a3fa1710a403e18fcb4a011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418560181a2005839001da17fce0b5ae3e7feae117785eb78c77b6272be34a3d381a2722154d29c294b138005ca78de7b329ed6d2763a74a3fa1710a403e18fcb4a011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418570181a20058390098bebc68cf6f12a7aca6531cef75d83c1b6e323485146195ffdd727dd99bbe7f44fd382de2ca6d9e5e9cc26f940decdb1b12b1a98e343274011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4185a0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4185c0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418610181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418620181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564050181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564070181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d00045640a0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d00045640b0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564181a0181a2005839005746c1b032f826b5e5256357a713a7ca63988fe2ff862e0396993b97ef0cbd5199d0e460725b3e79d371deb42110d40b778d3bf162777d4c011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564181b0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564181e0181a20058390020de866f290f45141315081d903f3eb3c06f3735e2a5b70f6a138462ada99823bc02291029853dc5338bc6e62b0540dbea54d9384f372639011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418200181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418210181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418230181a200583900057fd21bf903c585ea95dd927dee373b4cc1febc61874c48571dfb88a0a307af2a3e6a55a238fe323f9e54be10c54a8a8b25939a4f9ab35a011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418240181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582043c59e533d0934a6878a81403ec71a2225bb22d0764471cac8b5545120b475760001888258390038be1948e77426eaca9f967becc5455b11d3d40fb06b99dd3a817d5e75c7a5e1120727f24236cfb5981ec30fd50a2684a5aca866a123a1361a05f5e10082583900bb17dbac8d4a3452dbb6d0c664e804deb14a0b95ded5274007189c3641868c2b4e5289022a3a1f6f47f86823bc605c609d2c47a2db58e04a1a05f5e10082583900f8e61d5f13ab575771af475ac599ad88c7116339f82d2ea969b0e601d6d84c6a5b05cb8f89d24e9d46926975fa1dc08a58b3c26e96c06df71a05f5e10082583900693e466f25213254e061fdc95f8a5f07bf6ef0de0478adbf89a3308f7c4641296645e557c0a6426e140a09d4ba423d158aba1eae06aba7971a05f5e10082583900d93170064d82eab9dea2b3141bc88503ec80e93c8691fb6b223fe310877c17de5bd978526e288334114fada629f699c4e799394aa45c2aad1a05f5e1008258390093ab1cf6cececd048265573176355a322b7732299bbd624f655af2f674984fae4ca1715fa1f8759f9d871015ac87f449a85dea6cf9956da11a05f5e10082583900bc032a8614a84f5d9ee772f2788954e9d664b4264226cd36d0c4ddaeaa22f3a63400c1d96ad118b5cdd300cd039e83ae1957a35b764881941a05f5e10082583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b000000022a4fe9af021a0002d351a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418250181a2005839008f221a3021b0c09c57336733b0411d9d664e5d5e259096033a9d4bbecbce4335fa28195472386e53f0c3ab74d5cd254797d1100e4b1a33b8011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418290181a200583900d804dcdd0b0ec6ed0d8d2cd210a03b14f87c6849024930a8d6c91cf551a6a756817f0a3e1a1410730acf27202e7a9b63de26087e5cf466a5011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564182b0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582072af166d0cd8883c9abb1189ae93acb1fce482ca57cad9b14711bca9627b98d80001888258390038be1948e77426eaca9f967becc5455b11d3d40fb06b99dd3a817d5e75c7a5e1120727f24236cfb5981ec30fd50a2684a5aca866a123a1361a05f5e10082583900bb17dbac8d4a3452dbb6d0c664e804deb14a0b95ded5274007189c3641868c2b4e5289022a3a1f6f47f86823bc605c609d2c47a2db58e04a1a05f5e10082583900f8e61d5f13ab575771af475ac599ad88c7116339f82d2ea969b0e601d6d84c6a5b05cb8f89d24e9d46926975fa1dc08a58b3c26e96c06df71a05f5e10082583900693e466f25213254e061fdc95f8a5f07bf6ef0de0478adbf89a3308f7c4641296645e557c0a6426e140a09d4ba423d158aba1eae06aba7971a05f5e10082583900d93170064d82eab9dea2b3141bc88503ec80e93c8691fb6b223fe310877c17de5bd978526e288334114fada629f699c4e799394aa45c2aad1a05f5e1008258390093ab1cf6cececd048265573176355a322b7732299bbd624f655af2f674984fae4ca1715fa1f8759f9d871015ac87f449a85dea6cf9956da11a05f5e10082583900bc032a8614a84f5d9ee772f2788954e9d664b4264226cd36d0c4ddaeaa22f3a63400c1d96ad118b5cdd300cd039e83ae1957a35b764881941a05f5e10082583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b000000022a4fe9af021a0002d351a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564182d0181a2005839001c4595c4f3180180c9e822f1ac0f2955dd329eeeb94752a84281ff5295558528c6e1f7f2e16d94b74d227b9fd709edd2aeb0ab1556db75fc011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564182e0181a200583900c008dd489b67e0a774fe18d79ee8d1e280264933d3b31ba44cb37755dca94fb45aa2192ab26eff8409ea010fa2d4761efa92437e0d5b6b60011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564182f0181a200581d60fc38cce3448bf3d2790ca85d6b09026f7c86f21095c31f9925cf49a0011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418300181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418340181a20058390023a6fcbc8affc61518cff034c013aecf083dc64fe673ffc95cc9fd9e1fad7e0b1d0dd8820703a4f59c2488d148193a48d8fdc23a6dca8137011b00000002540be400021a00030d40ff9fa200d90102828258201287e9ce9e00a603d250b557146aa0581fc4edf277a244ce39d3b2f2ced5072f5840ae4cc1168265e2f60fec9ca9b644eaa42a77e65a39176e04aef29b01e25653a307d39ba61761f8d1ca44696e1d6bdf7a0679413ea3c448f76268e6eb02074102825820742d8af3543349b5b18f3cba28f23b2d6e465b9c136c42e1fae6b2390f5654275840112c95c93013e63fa73ee6a645fd522808d4dee019626e395a8042755c15fb1824e1503c17ea843a838809f55822366b05bce2e378d0b955e66d625c8e9acf0001d90102818200581c45d70e54f3b5e9c5a2b0cd417028197bd6f5fa5378c2f5eba896678da100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584005383334e98e9263b93ffeb3e5908dbd5657caa67d32f9964d7f91dbda76fff164cbeabb981beef470d0d3e1724b85847e6fbc1c039084a817110eabf9d29e08a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258406151f0d0cae4ef0ace62dc03300dcee276765c1b493ac61f770d0633f0f71fe0d642ef29b593c511034a847dd569c56a0278e063c46d6d060abad4e6baf4b705a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840cf518f30d291871a0277e367534557205cc015d3a0d78070e1aee298aeaae3e81d70b42c464a63fa741b5300b48c1699dfc39fdf07f96b8eb240c7d6d3267805a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584056185de4c1d012d322e7e82ae98f24887ed7264f262db53f019e5900b9110a439e5a462a75be036f9f04b0ddcf09decb0894c7b6b9ff17ab4cae8184072d690fa100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840ab78c606155b6aacbcf6692a18d97dd8773b4ae4c96684e4abb9cc59233023f67650ef7259069deddb65ba770ac3a1401d169ce33ec9483b8ebb9e83472e2c06a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840e21574b02002efcfe81382326aa98a0a971327ad4049690a04985400fcb14db7adc8149a0ce4dbfb5afa0d240ed9da23f15c1020d2826f50fc579a10a3662d0da10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840e64e3c19644edb6e788112ac75b4426ef94d535f1ffd9a34e86745777feaf083dc8e847a62634fef320a08b566c24ea26e8dc9e7b49fc456554215cedc0d3508a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840f49fd8eeaa366873aeb2530b2bbcbf7c5970866162ae7250c4b913e19062de1396ed70d1e32a4605071bac11c2cde3fec1dc5b37044cbea073668fe5c478400ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840f0ddd023e0dbda32d296b359db809b0088246e512fd34c7c0cc4b5ae974361873e02330e955eaaf97117525bcb3cd014bb70810f8d0d62a28c3242c86d8c3a08a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840615ee0444f039f02b26791872d6cd5562728cdc6dad02acc71475567b09f3d4b4655c601bf816ef6d11b2f3f81eeb6db09d800bf1bf4e2daf29493338c232901a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840d62bfd428359f4cd04950cc73f574f0ec1c613284fdff8028ed3d876b18b69335beee9792410c6dbdc1b196b4703f250fbaeb66569869ae97d7ee843b9053405a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840d813a836cc44c70647b2e7941fb72a4f720f16aca17e155a6c6a6f9bf175b1e49a3beff6edcfb0c442cc24790a12ee0b1d499a32fdbfc0a850f4846708ea340da100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840aae6ac20cd419eaa7f3a95144f9ccdb46401a0db295d544e920a54b5c24fb63197fde03f12174800c3cf5318a73d92ebc53c2ba97803766892add32fd9feb400a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584071d223fd255df1e912a9c0a8230ee9f0ac95d0aa325cd31e50701ac355dfb5f3fbb27983b372c1410156eeba9163aa0f8a9787dab8c44e7afd4e2d07459a4708a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840b7f821c0ff66fcbbe7c61f43725aa2234297d6765e002d0130301cba13465fe89f59a596549725542445c76d17cedc9c9cfea8b8862d41405646d725dabc7d08a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e4584045830e5de108b9353b0c4c561af296e79eddb26b8ccfb18c5bd9fac1baf8d477691229c0bb9ea212ab56d9ae76c92de6ae50686fc0619510b8c35fb69c6b4402a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258400635ac784fe71d2f7927114edfc709dcb56013617df4edb9b6b770b067e7709e8abfd4cdcdd61512894fcf02f16e1d72bfe60fbfb86b815631d791bab132b909a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584032d67fe72feaf2175455815bbb624ee1b93f5efce905280158db94bbb2b5371d9eaff1bed6eddf9eafe8ff64b55f1d7213294bdb459e0b00c437edbcabf4cf07a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258404533de52e9e3f51e39951c9e197f6900081e98f38f3af5c4a7fe9219f8c311eaa43942b7a290ecbbbdd0bf4ef4ef1d11c39e6de4083c86892a6026c27bcf2509a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840d46541920ce2c31ffaa00cb20e8f5f00f48b6b8aa5cda67d22ea4bf12fd318461a0d8c25ee04cd7446e18f0de59b0fd4f6631e29bc8207073f2404793ae5f108a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584041bcd52ae44a3ffaa1abe1cab6c8b21f8010e2f1aee1d8651b9f6e94aabf5b2dbcedb45dd154b78dce1c5b9679956dd25153a0d945d3eb83c1de3b713e721008a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840027da3169c9c1fb9a67104061698bb0dadb2f58b660af4b461e7353fab1545a3d03157e077a388ec8556176239df3241255feb1f13b5e406bf7c3ad3af7d4202a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e458401ae63dc54e511965f7010971de7fb99585afe492cb8084b395ee01555c1e5657ab08a24be0f70d4e9cd1bde2a6ae31815c5f64025c0415afe2d503b2cb5b3e0ca10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840d2211679ca8e9cc5de71b21bac2b58fd355f5cbd2b42ff31ec37af77b776fb77c64fa76a830f240c29a4b95ae6bff9c58fc6bc2b3d18b57a2af11710ae6e3006a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584084bb7bf64ecea446d6beca88bfa2c7de71d8899e96006920c4c18e52f042aa71e1d27e60bdb6d9d6b1aa2e3330f59ee95b2c001909ff8994ea1fe4e5cd7a760aa100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840c6f3d6194a2fdb2a50417f80dd020adf866c91a102f22eb6bc66f5131292a1a42e9a3550e18e06cb17bd153c08f55a6cce3a1c82052ec9197495900f3ca4f407a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258401a28cac7e80a657563e1433459f369bb0cb05e7e3ead78378dfc2ad15baa344e76e1ac0ca631c67848e81896fd6838b3821961928911635ca069f12c05772a08a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840d201ce4ca5572db792d1563ef3614f2e2b27072e4751327f4a8f75201183a189ac57cdd9399474850e87031c7545c896ebab3983434bb6005690b9ad8fd9d50aa100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258403f45e44aa7457c714599f0100702ec265e91493e30c57ba4f1f74d912858bae8fb71fdf2faddf865c816cb0218eda0db17b707c8f429290f1a1c02b6a4450a0ea100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840e16b1d8f5992fda268c9d7e5c0ab6c5d38b8abaa6b92ccae5b0d2f3079d098ab67ba9a15b27807746f3c7695091ec5bb74ba8772baae14d2786eb8a512d70201a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840628a5491c5d0b4e0202b7aae87a343afd642345b823252355f6d392d8398d2174c141622e3de167b4f82c3cb8b4e8105b341851005d2ec0c1e35c354006a910ba100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258408ad2ee919f520a903764e0322800f7c086f870374f063d2e62ad1dfbf54e71305d90371abe3a196132e123b9248281f2d676fb29442f80249f644ce1185dfc03a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840d53bf84fe8712171151bb6d5f988d76428292639737d817986b46d629aea6eac2a90675cbf0347ec004ce23f9ca0b2dcff5c6d1be91ab478634de8ba8ab96102a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258404702623a2a94b9efbc03dc7d506c4bd70c1e0fea8b21f3b76c592883f0c364ffc12215e59f9ea4d2eed2e786376e6128650b4c9c3f6ad9419f070fb458efa10ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584072bb89ee81a7bcc4a866ae498d3ca076d5d5a885547c7f5899b8b59b3077310f58df420e470bf36d4ed5beaaf30eb361f04ed578cdbd0ef04f7cb573f0c0770ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840e112bb46921840820f4d5b40ec45699bc1b818ca8fe77fcc222a6fa1edb2425487f32e2039e2cf6077ce1e8e2e0b0d0581c64fb866c1c183344af131ccb9390ba100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840039329e261d8386f81a28f5ef5062196a46b5d4389b06bde97e662f69e37812c3ee75352f392121f58e76e5c1e1649656632b01ea46f932ccedcee102d625001a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840c1dab5e2482101ad1bd04f4018425c7133171aaf1274573ed35305f4e37bddadb3636f0aa098d2c0b5f615e8eb629bb94afac5d4c4c0743dba16a847d898b905a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840abb898b3e1ae3c4c9d068a4517b83a070d4037f979b6365ea5eb368e7d43b3fd2152fb93a462fdc553f973d90ab136332057fb66ea529d4fbc53e7f082b6fe03a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258404a09ccfd9c09c6a453f46c721d280065081d89aa4b84fc809d75db1b923e78963bcbf36d64786d8c09c527e90da744e83116617b2e18d9145bac6cf66f876c08a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258408df2408adbd8b4c990a913b9ed2455c9de72d561ddb8f3ec0da5d1513f417a2fcee9ea9ace30cb840d37950c41455bd3655d12d534b70a6eac7034950f821108a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840534b54cc145722e3f7a62935d84c025e17e31c4b08d3f3fb16bb7673d37e9afb07fbdb5ffce5aeef743376bac161973e565e1c12db97bcd879cd7e9030c2a00ea100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840664fd5d8bc5d93509d02104f39e7a22c6cd894f49935cac9e662a9202b9a64baa9f55cd8aa07d3d1e095e9b974c59c0a8c50d14b0d077d70e236ad5cf52ac104a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840469cdadb48349224303d14980cab5c2ae5dacd0f6e36714d8dcb9ca85fa4eb688bd7b1334e30f8718178f7f36d8c5b204e0f9cdce5f88762fc2cbe2cb28c1d03a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840f330820de229a476e1b3f84dfcf9ad98264070212e6e2d61d8b05afb1e12a1426cfd7cb0b284db237d5882cecd6e8f1fe2cf9ddc06783242812178bcb053a105a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584066b89001a28583bed59dbd07d359333207eb74d39ee092c0bf1da4351da64d38c9938a3682bb52a4253dc76074767b4cc2bc1eb2a31bbe6be3c45a5c52cbdf04a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840f365b299297ade5117d72156050cb81a76ba0b859cb46d0f2326c4071110440108b20390f878ba082d41217b2a8fd5f1435b9ba48d176ad5dcb6faff54976b0da100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258407fb0b1f28d6ca64a29c6c7a51a2ab3436809b5038c06b8204104e3b98afa915246f742e2f1bd569f5132d2bbdcaeae68215a0b0f17f6367ce4eea37ed951ec01a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258406e6d2263c440486fc1b3c2aa930e519f20b70f70c28cb532d031f63cefc55c56f4647b10dd6390aa0d0f2ba75bf6cbe3ce2fc6d928dc4db74388f1e5e3057b0ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840dbb299923c24a70ae10dc637d862b750b3e6548e64c590674d2ceb87b7535199ea8dfd024694d26ae1dbbca683b1a4ba90af7d1680a9b8e4819a2ee6229e2408a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258405b2397d031c48f56b43416daea99dd3d8bd1733cb83c2a688dbe8b5dd9bfe64d596280d71973d7d540e929262dafd79b14954b855635fe845642090241003503a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840482c9089f2d60eb069432bf7f7213178a6fe3d52d37a4fa5aec677875bccdac64de7a45c6eb0bd4996414412b12d3e887a1b391e775ef56c47d53f9c944d020ba100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258401972d18c1e9c62837121efafddc2ec778a3a8f9ec5f534c9922778905d8f809609d6c92e427852d8b5f822ad590fdeacf3877c8056f0768b44b025a2b79e7704a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258409b7141b69a493bc4d597a645ed488325492772ad4c3cd5c5c7805a5d951a4b6ed960ea27428d1add867fe7c209f4e65000bdfa878bd7a4357b223e9c55af450da100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258407ac2d7823887de83bca86216e424ccb63fe9f4fa1f106bffc6afa388e91845c97177c410f1a8ca5ecd9f2701c42f5f9dd2faeb0ecf2163a37521badc8a6c1b03a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840c49b6714ccbbdebebb9461a2efb39b9ac5e00a389aadfb2a1b4fe384733610c45e1f03825f65e182988da97659a71e378d49d17fb93d76b80d579b7d49399b06a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840b53ea3508cbd7da47fef05e98c0b31b13ec33de4596ba4061a8e04d91b1015c49f328da58084a6f573d93cdb7aa0972a1a1936a69ee7362adf65df3eae4e2400a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840b1c6b15b311423aa83dfaebe118d1a2a3ff006399f2a63fa82f0d0e0c12bc2b844ec78f5bc8baeef588d15b2237db48cbfa48361a6d630052c9b68e62e035601a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840bb544721cd95655497375390da19fbd87b3c63a4edf333688e2fee7935e96b6572f84b81d80fee5239062be6d3c6a75a5f0c50696d3c6663d26cecdfd8fdc808a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840a7018dfacec705459856bc706b7d05d04c56867fb64dfd0cf97fa980e881cc609e91cf6df204fb6906f860e5cf390e0290d302b6231664aad8c2b4cb30090609a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258403ddf27cce21fbf1a361233d2bcff92ecc9d3cce64c3d8186495e3509b843a0a326f528be8241b8557bf3cdac9c304fcf0fa8fd2a8e389d6acf9fc62b5626d705a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584013bd40ae7383feb674c2cd3357122aec3f6efe17b9b4f25c47cd3dfec194d0c4f20a52cc30fb92245c1a20a962772808f3dc6ee51261c86af16879a1fed2210ba100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840a1cf100aff45ee45c0f868214187640c8c29cb005c7aab7100ff86338d78f972733a611b4e0dae436fe9e1493d6ece69c35ada3cc6506e730ea1bae277468108a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840667d6d2987788454c41f2e86867fe98e1fd48aa789fbf9cf2208f927a8f9941d0384ebf3e3e45ee80c49934aad9b6ccaa13179b69f35b9acd21b55f56caff80da100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258401cd5c48fa46d8f0fb07c7737b7719d1fba5729478be3eef3e2e19942c4d6d54b01a569eb34d4f4be84a2f6961832ec17bade0511cbc01f5db5749a09bb4d8808a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258403bb5ddd91b5f5d7366b41330bf5bbbf7cf7d703bd50376ac21b07c6da696562266361678c06247a57111c63bc1fe58463a8c125e0d117bdf05cd4fe57bb8b90aa100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840397067045ffe0cf7126a5f73f228e1bc8721c617ebb7c41c1bc2f7b6c8cc50bf2370bc1ee679bcb0581e11da1e186504f2e3f3322fddf40a1863067ffc5e2903a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840831f5ea5dd354f5a522e044b53aa1966d036871d5a3b7d2323e404996907a33aff5aabb9db22301064033045cbf14c91d29de84b8fbbb75683ff1cb51fd0920aa100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258401e8cc65db39cb5e9d54dac50cda84c55fd2f989f667a11dc46521768ac2f46a27a70173a92e849ee621ebe3025d87088528e7450b8312d678b6249f5a124f70fa10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840f4835bbcf5459db0826e27ea95d3ac31f7bea56c0253716212ef421995c7546a963ac89dc6cffad75692a149372cbdeaa19a9dcd171ac423711e8d71c495d703a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258408039411659145a9fb3863b2ae2f3890009bf004332f58daa6662368a7378d215cc7f40569284d5b86c5a7be210cdcb5551633762b5a7d3f8ad2095a220fec609a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584002c9ec1d95d0464eba5f4a656d72b55a92a80078e83f2f47682729af0fc782a68f3f31db7c64018ae8fbd36c5ac72d5573357a7578359056b9d3f9a29467d80ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258406f176d0fdb8768aad902c8fc115eb797719ef62a93938d7f09bbb213a88d61294143ec1d508a2a450f0e16ab3e2ffb7e0ed4cd7f75b3f2ff9f70cfaa77764506a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840f4262eeb9183eec1e896b9be61984ed9d4192b38825ba1b560ea23fe6e3224d3c94f4cc64174c1d9166e665e1a1ff8f0c84024bb8b9068b853fe4ce683d96906a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840df64e327056e335f484582fa0e4188e62620968e955431fc3576da2c1935b15ec605bb5d738f5000dcdc022646f9545d6932c2c79611dccab116295ca03c2b04a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840132cce5cea5d8bf7e9b802e4477eff041ebe1c12a8b8658a42ae91727cbf4f39b0d23831c70923a68ad7a023092bcecb61ac6253fdd17be00cecc37a71301300a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840842ecaf1b907cb34799d78d6f35eb349a986559a396840aeba5f6e8dc7e4172c16bddcb1f926a21175531478773046e9484aeb4ca83c1cbaf25f5a4813afdd0ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840a417cdf9e02755e53d6061ce0c93cacb7de24ce33e3fda9ac3a85414a7bf62676446822875972867d5647e46c22e21099e5dd8e4114949b30b86146b0dba1b05a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258401f2e86831349fa458c3e2403e2caacbf046fae3c513575e8ce2d34037d34337f7e58cc98cadf034e8bce930335285220624945b316fe6af71e8ef0d12ef05001ffa100d90103a100a11902a2a1636d73678f78264175746f2d4c6f6f702d5472616e73616374696f6e202336313138303020627920415441444160783c4c6976652045706f6368203234352c207765206861766520303132682032386d20323773206c65667420756e74696c20746865206e657874206f6e6578374974277320446f6e6e657273746167202d20313520466562727561722032303234202d2031333a30313a333320696e20417573747269616060607820412072616e646f6d205a656e2d51756f746520666f7220796f753a20f09f998f783b4265206b696e642c20666f722065766572796f6e6520796f75206d656574206973206669676874696e6720612068617264657220626174746c652e68202d20506c61746f6078374e6f64652d5265766973696f6e3a203462623230343864623737643632336565366533363738363138633264386236633436373633333360782953616e63686f4e657420697320617765736f6d652c206861766520736f6d652066756e2120f09f988d7819204265737420726567617264732c204d617274696e203a2d2980"; + let versioned_block = VersionedBlock::from_hex(versioned_block_hex); + assert!(versioned_block.is_ok()); + + let unwrapped_versioned_block = versioned_block.unwrap(); + assert_eq!(unwrapped_versioned_block.era(), BlockEra::Conway); + + let block = Block::from_hex(block_hex).unwrap(); + assert_eq!(unwrapped_versioned_block.block(), block) +} + +#[test] +fn versioned_block_round_trip_test() { + let versioned_block_hex ="820785828a1a00101e2c1a0143a1b35820cee15d6daecaeaf320a4ddb1f7c437846f798e4a9cd08d12fb7821b175c980115820e3c87f196ce9fc40a8d929f3365e247f8f71e1981bffaa7cbdb0aa3a83dc790d582054a580ddf99f67818e0312374cef1f7dcdd59450930898d4d2d10e606b963e49825840ca5d1f988222919982b6a20f4f54ce59626fece7d7c607487762129d5196c731bcd11dfefee94ce5a60a733478970631d41bfc0620769fa7b66ebc16c8a89e5c58502855f21ba12fb101d175c376e19496e464bf37c92ec21395e5bffb35e1ae8f433f2139de166161f2b2b26afe656d3d170acfd11a535a80fca6325479d2262e208b0a4b98a01f4845c45a58fb84cb58011952de5820f2e4c6554da5b773c3f7889944fdb5b1791f8552dcafe2916041a531860e912284582039b66a10f6b78c541ea5ed6ecec4c6dd385b869026ec16c4e48414cb39cac38b0018a258409ccd6cf71a5c337f71a41904c0ea0a889a2321c94374c3a8402d8a7dd25b222abe6cb325c6b39bd63bc99fa84c094fdac2523b72f1a22081903dd047be9be9078209005901c006b35937aba451d4738486ea3ba5644d9306651f09b2012de8acc5136771fc725164ad669dd716f2726dfe138137d09feddf9450b3c51a601577bff35d0d2202c887a260855dd8310fc9365f56a4757ea7d81103d409ea0a8ad51c6ae52fc7fcf4d3d456384b7566b70a2b7bd4e21010a1ad5df12bf5d332e82c1a4a5cca39740252e0ea163f206cacf193e59ebbd0e20d621fa9913c60efe1c035d8ebaa354fbe45768339d53a4e8e04fdea79d00b869a973cfa3eeba2e2668b1dee5fcd7d13762dceb4da804fd749e5fa977ead0003a9739837aa68b80bc5a32ee015f667574a7fbe03b4bf5b027c945fa4497c01efb4ec51f3da2fb2dda33ea7dc1dedcfd2ea2c0a4da5a1c553d033033f4986e2ef5c09bbe326a25e5082c1eec406aeec8105869a9d46a83689a2e026e6e31d4037e700ffeb2920bcab88d1a400976881d17cd84582521482db0be460fb43de88e40a4ee24745ac92ab8b40329bde1d855404478c9f59b05e6322f3640ad6f40d7a771fc6d58e94f8fd0006d54272e36a30034b14327c2e6ffb92ead2f8a4165a3e4a1c44de677829e8e797547b3c0bac4b5ea89cb86c01d5b1e67aee3ba36b8cf9617484db2e4d1bfc37fed1fabb73ce3c9fa600d901028182582088c310befd2e8c9b33b340a56f4ea8141689c16eddef5d9c606055ca35897bd600018182581d6052e63f22c5107ed776b70f7b92248b02552fd08f3e747bc745099441821b00000001f09cac72a1581c34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518a1494154414441636f696e1916d6021a00030739031a0145283409a1581c34250edd1e9836f5378702fbf9416b709bc140e04f668cc355208518a1494154414441636f696e01075820e2ea39e82586fa40304df3c2cfc753c6ba8aca62e780f01a0519c34c6d7c25f5a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe185b0181a2005839007ce8986f5f3fb526a6d35d32edac0b6c8624daab6928df1964459c2723bcf2892e8182a68e3aac6f9f42ed3317d115ebad12a17232681175011b00000002540be400021a00030d40a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe185c0181a200583900f7fa5ddf2c3c46ed4d913812d38dd43d585adfa884938adaa7a075dd1bf1e138f2f8beabc963c94cc28ee8ed4b41744601f2edaf50b21efd011b00000002540be400021a00030d40a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe185d0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe18600181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe18620181a200583900189f009d9536b1f52f0629bea3323f48df0eacdff68726f1a32edc49db89995ed3aa88dcfb43790e2e51761fcba7e8594bcc67684f52d524011b00000002540be400021a00030d40a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe18630181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820a944bb37a59451f9a47d5c8888a8a1145527ffb5d45a17c1df40926a42ad08330001888258390038be1948e77426eaca9f967becc5455b11d3d40fb06b99dd3a817d5e75c7a5e1120727f24236cfb5981ec30fd50a2684a5aca866a123a1361a05f5e10082583900bb17dbac8d4a3452dbb6d0c664e804deb14a0b95ded5274007189c3641868c2b4e5289022a3a1f6f47f86823bc605c609d2c47a2db58e04a1a05f5e10082583900f8e61d5f13ab575771af475ac599ad88c7116339f82d2ea969b0e601d6d84c6a5b05cb8f89d24e9d46926975fa1dc08a58b3c26e96c06df71a05f5e10082583900693e466f25213254e061fdc95f8a5f07bf6ef0de0478adbf89a3308f7c4641296645e557c0a6426e140a09d4ba423d158aba1eae06aba7971a05f5e10082583900d93170064d82eab9dea2b3141bc88503ec80e93c8691fb6b223fe310877c17de5bd978526e288334114fada629f699c4e799394aa45c2aad1a05f5e1008258390093ab1cf6cececd048265573176355a322b7732299bbd624f655af2f674984fae4ca1715fa1f8759f9d871015ac87f449a85dea6cf9956da11a05f5e10082583900bc032a8614a84f5d9ee772f2788954e9d664b4264226cd36d0c4ddaeaa22f3a63400c1d96ad118b5cdd300cd039e83ae1957a35b764881941a05f5e10082583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b000000022a4fe9af021a0002d351a400818258206a7e3a926eafa74f72c0d6a721dfdee7a7202b1fac4eee12d8c6dd030217890b07018182583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b00000001eeb2890a021a000296a514d9010281841a3b9aca00581de0db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab238106827668747470733a2f2f6269742e6c792f337a434832484c58201111111111111111111111111111111111111111111111111111111111111111a300818258200580612292c60a12689142d795c39d577aac4083c63a8b572fc10a69c0ae51fe18640181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820a439638a9f8e0f52e153126e8b794b7514f3a0921b08b611f3866a1fc75b7a560001888258390038be1948e77426eaca9f967becc5455b11d3d40fb06b99dd3a817d5e75c7a5e1120727f24236cfb5981ec30fd50a2684a5aca866a123a1361a05f5e10082583900bb17dbac8d4a3452dbb6d0c664e804deb14a0b95ded5274007189c3641868c2b4e5289022a3a1f6f47f86823bc605c609d2c47a2db58e04a1a05f5e10082583900f8e61d5f13ab575771af475ac599ad88c7116339f82d2ea969b0e601d6d84c6a5b05cb8f89d24e9d46926975fa1dc08a58b3c26e96c06df71a05f5e10082583900693e466f25213254e061fdc95f8a5f07bf6ef0de0478adbf89a3308f7c4641296645e557c0a6426e140a09d4ba423d158aba1eae06aba7971a05f5e10082583900d93170064d82eab9dea2b3141bc88503ec80e93c8691fb6b223fe310877c17de5bd978526e288334114fada629f699c4e799394aa45c2aad1a05f5e1008258390093ab1cf6cececd048265573176355a322b7732299bbd624f655af2f674984fae4ca1715fa1f8759f9d871015ac87f449a85dea6cf9956da11a05f5e10082583900bc032a8614a84f5d9ee772f2788954e9d664b4264226cd36d0c4ddaeaa22f3a63400c1d96ad118b5cdd300cd039e83ae1957a35b764881941a05f5e10082583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b000000022a4fe9af021a0002d351a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4010181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4030181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4020181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4040181a2005839002b11f0e68a65cd6a243f1a5ec9d597ba972675a00bd3172a7ddc0293b1d312a60b3824d1820dec5ec769c4af7d7598387c16ca5ba6259f46011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4080181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a400818258203298fb7878ab004c1a4b369eae7fc89abca6342f06557cebf6c89f2d8c21aa9900018182583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b00000001b3152865021a000296a514d9010281841a3b9aca00581de0db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab238106827668747470733a2f2f6269742e6c792f337a434832484c58201111111111111111111111111111111111111111111111111111111111111111a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a40a0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a40d0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a40f0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a40081825820dcdbb7a98286f5d48673c95b05f441bc40731b1e4c3429d192f0c6b7fc3749d100018182583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b00000002186e835b021a000296a514d9010281841a3b9aca00581de0db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab238106827668747470733a2f2f6269742e6c792f337a434832484c58201111111111111111111111111111111111111111111111111111111111111111a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4130181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a300818258207e4fddb60c2034bff37bb7069f9318735fcf4de03e01e9f92251c96dc59318750001888258390038be1948e77426eaca9f967becc5455b11d3d40fb06b99dd3a817d5e75c7a5e1120727f24236cfb5981ec30fd50a2684a5aca866a123a1361a05f5e10082583900bb17dbac8d4a3452dbb6d0c664e804deb14a0b95ded5274007189c3641868c2b4e5289022a3a1f6f47f86823bc605c609d2c47a2db58e04a1a05f5e10082583900f8e61d5f13ab575771af475ac599ad88c7116339f82d2ea969b0e601d6d84c6a5b05cb8f89d24e9d46926975fa1dc08a58b3c26e96c06df71a05f5e10082583900693e466f25213254e061fdc95f8a5f07bf6ef0de0478adbf89a3308f7c4641296645e557c0a6426e140a09d4ba423d158aba1eae06aba7971a05f5e10082583900d93170064d82eab9dea2b3141bc88503ec80e93c8691fb6b223fe310877c17de5bd978526e288334114fada629f699c4e799394aa45c2aad1a05f5e1008258390093ab1cf6cececd048265573176355a322b7732299bbd624f655af2f674984fae4ca1715fa1f8759f9d871015ac87f449a85dea6cf9956da11a05f5e10082583900bc032a8614a84f5d9ee772f2788954e9d664b4264226cd36d0c4ddaeaa22f3a63400c1d96ad118b5cdd300cd039e83ae1957a35b764881941a05f5e10082583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b000000022a4fe9af021a0002d351a40081825820705ab68071f9af1d314e74a053e39a52f3fdf96f9a1280dab30d45f04c05436d07018182583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b00000001eeb2890a021a000296a514d9010281841a3b9aca00581de0db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab238106827668747470733a2f2f6269742e6c792f337a434832484c58201111111111111111111111111111111111111111111111111111111111111111a400818258206fe0c3eae23f779b0694747ed28612f47271b45e84bb3d23c11c1ef2e90fa12100018182583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b00000001dcd122b6021a000296a514d9010281841a3b9aca00581de0db1bc3c3f99ce68977ceaf27ab4dd917123ef9e73f85c304236eab238106827668747470733a2f2f6269742e6c792f337a434832484c58201111111111111111111111111111111111111111111111111111111111111111a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4150181a200583900e698ee1c7d4361c6faf62716dca0d435eafd0b25e369a5d68455beaa0f5c16e3e747e7c5a9eb3ff189c0e330683665de9326d2ffe35d0631011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4160181a2005839005bed6070c549f1560cb89361564cd2be7b36536e8da868a218d514e5fd2e3e48dbc0278cc58e47ed50a1ba90cee61ab22c8f4a639c913d4b011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418180181a200583900ab3cd541317d83d072bcc38e1294166dea5d97ce453424b84c547cfc101c5bfa799985a6e96adbb5859e90cbe4a0e4edcbef408a3622558b011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4181a0181a2005839003b3ff2a2d98519fcf53c7abb15b6c4dfe76209c52e4c2065b33b97bc465f9e3a6c6c3a8eac01d39f519b9bf3bc031480936156b7cb2e45c8011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4181d0181a20058390050fc315c9c141b4da62b10525cf5049e8ab1bb8bd96903a6d87c5272bc616bee900ed3135eb065a11faf2100670f0182ae86827df52dba96011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4181c0181a2005839006087b2d29b8a424d7e3a756d08cb078ecb5215fa9344343ac2c6bfb02bdca5a48eca12260be94aecf81b9f21ca41871e06cdc4d12b5aa2e3011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418200181a2005839005deef04c1b44c606775db03444beae0f10c75f437c131628d264b17c439dc3dbc39b8bb91832384d44263001591fd806df73b413da861fd3011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418210181a2005839006087b2d29b8a424d7e3a756d08cb078ecb5215fa9344343ac2c6bfb02bdca5a48eca12260be94aecf81b9f21ca41871e06cdc4d12b5aa2e3011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418220181a2005839006087b2d29b8a424d7e3a756d08cb078ecb5215fa9344343ac2c6bfb02bdca5a48eca12260be94aecf81b9f21ca41871e06cdc4d12b5aa2e3011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418230181a2005839006087b2d29b8a424d7e3a756d08cb078ecb5215fa9344343ac2c6bfb02bdca5a48eca12260be94aecf81b9f21ca41871e06cdc4d12b5aa2e3011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418270181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418280181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418290181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4182d0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418330181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418340181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418350181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418360181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418370181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4183a0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4183c0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418460181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418470181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418490181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4184a0181a200583900189f009d9536b1f52f0629bea3323f48df0eacdff68726f1a32edc49db89995ed3aa88dcfb43790e2e51761fcba7e8594bcc67684f52d524011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418520181a2005839005c85eb9c0aa544a6bb5d1577c7a588c39caca885c8a3a9fceb0933a2cd1a02667d16df1e109350555c325023dbfa31fd9a4a8b99ff904d96011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418530181a20058390030a33756d8cbf4d18ce8c9995feca1ea1fc70093943c17bd96d65fed0aed6caa1cfe93f03f6ef1d9701df8024494d0b3b8a53a1ee37c5ab2011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418540181a2005839001da17fce0b5ae3e7feae117785eb78c77b6272be34a3d381a2722154d29c294b138005ca78de7b329ed6d2763a74a3fa1710a403e18fcb4a011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418560181a2005839001da17fce0b5ae3e7feae117785eb78c77b6272be34a3d381a2722154d29c294b138005ca78de7b329ed6d2763a74a3fa1710a403e18fcb4a011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418570181a20058390098bebc68cf6f12a7aca6531cef75d83c1b6e323485146195ffdd727dd99bbe7f44fd382de2ca6d9e5e9cc26f940decdb1b12b1a98e343274011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4185a0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a4185c0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418610181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a30081825820058e5c03e1b0e08f8710cbbd59ea8589ef0cacf031727146d53e9c1067bf54a418620181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564050181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564070181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d00045640a0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d00045640b0181a200583900003ad89c3e2ed8e98d2cdb207afda3a49cf73d7df70cff6f35d5a5afb7137b5e2a626ebed51ef0261692b21cfab50bf053e989f24d65f48f011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564181a0181a2005839005746c1b032f826b5e5256357a713a7ca63988fe2ff862e0396993b97ef0cbd5199d0e460725b3e79d371deb42110d40b778d3bf162777d4c011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564181b0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564181e0181a20058390020de866f290f45141315081d903f3eb3c06f3735e2a5b70f6a138462ada99823bc02291029853dc5338bc6e62b0540dbea54d9384f372639011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418200181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418210181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418230181a200583900057fd21bf903c585ea95dd927dee373b4cc1febc61874c48571dfb88a0a307af2a3e6a55a238fe323f9e54be10c54a8a8b25939a4f9ab35a011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418240181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582043c59e533d0934a6878a81403ec71a2225bb22d0764471cac8b5545120b475760001888258390038be1948e77426eaca9f967becc5455b11d3d40fb06b99dd3a817d5e75c7a5e1120727f24236cfb5981ec30fd50a2684a5aca866a123a1361a05f5e10082583900bb17dbac8d4a3452dbb6d0c664e804deb14a0b95ded5274007189c3641868c2b4e5289022a3a1f6f47f86823bc605c609d2c47a2db58e04a1a05f5e10082583900f8e61d5f13ab575771af475ac599ad88c7116339f82d2ea969b0e601d6d84c6a5b05cb8f89d24e9d46926975fa1dc08a58b3c26e96c06df71a05f5e10082583900693e466f25213254e061fdc95f8a5f07bf6ef0de0478adbf89a3308f7c4641296645e557c0a6426e140a09d4ba423d158aba1eae06aba7971a05f5e10082583900d93170064d82eab9dea2b3141bc88503ec80e93c8691fb6b223fe310877c17de5bd978526e288334114fada629f699c4e799394aa45c2aad1a05f5e1008258390093ab1cf6cececd048265573176355a322b7732299bbd624f655af2f674984fae4ca1715fa1f8759f9d871015ac87f449a85dea6cf9956da11a05f5e10082583900bc032a8614a84f5d9ee772f2788954e9d664b4264226cd36d0c4ddaeaa22f3a63400c1d96ad118b5cdd300cd039e83ae1957a35b764881941a05f5e10082583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b000000022a4fe9af021a0002d351a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418250181a2005839008f221a3021b0c09c57336733b0411d9d664e5d5e259096033a9d4bbecbce4335fa28195472386e53f0c3ab74d5cd254797d1100e4b1a33b8011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418290181a200583900d804dcdd0b0ec6ed0d8d2cd210a03b14f87c6849024930a8d6c91cf551a6a756817f0a3e1a1410730acf27202e7a9b63de26087e5cf466a5011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564182b0181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582072af166d0cd8883c9abb1189ae93acb1fce482ca57cad9b14711bca9627b98d80001888258390038be1948e77426eaca9f967becc5455b11d3d40fb06b99dd3a817d5e75c7a5e1120727f24236cfb5981ec30fd50a2684a5aca866a123a1361a05f5e10082583900bb17dbac8d4a3452dbb6d0c664e804deb14a0b95ded5274007189c3641868c2b4e5289022a3a1f6f47f86823bc605c609d2c47a2db58e04a1a05f5e10082583900f8e61d5f13ab575771af475ac599ad88c7116339f82d2ea969b0e601d6d84c6a5b05cb8f89d24e9d46926975fa1dc08a58b3c26e96c06df71a05f5e10082583900693e466f25213254e061fdc95f8a5f07bf6ef0de0478adbf89a3308f7c4641296645e557c0a6426e140a09d4ba423d158aba1eae06aba7971a05f5e10082583900d93170064d82eab9dea2b3141bc88503ec80e93c8691fb6b223fe310877c17de5bd978526e288334114fada629f699c4e799394aa45c2aad1a05f5e1008258390093ab1cf6cececd048265573176355a322b7732299bbd624f655af2f674984fae4ca1715fa1f8759f9d871015ac87f449a85dea6cf9956da11a05f5e10082583900bc032a8614a84f5d9ee772f2788954e9d664b4264226cd36d0c4ddaeaa22f3a63400c1d96ad118b5cdd300cd039e83ae1957a35b764881941a05f5e10082583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a41b000000022a4fe9af021a0002d351a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564182d0181a2005839001c4595c4f3180180c9e822f1ac0f2955dd329eeeb94752a84281ff5295558528c6e1f7f2e16d94b74d227b9fd709edd2aeb0ab1556db75fc011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564182e0181a200583900c008dd489b67e0a774fe18d79ee8d1e280264933d3b31ba44cb37755dca94fb45aa2192ab26eff8409ea010fa2d4761efa92437e0d5b6b60011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d0004564182f0181a200581d60fc38cce3448bf3d2790ca85d6b09026f7c86f21095c31f9925cf49a0011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418300181a200583900b5187cdefbc5b49ddc17b423c079f0717721a03882a3b265bd4c12e080f326af300273d19d5a541d45baa42ebc04265816735b026b5f34a4011b00000002540be400021a00030d40a3008182582005fb50ceafb7ec5392f24b830572c949bf5de8396ea862298285b7a4d000456418340181a20058390023a6fcbc8affc61518cff034c013aecf083dc64fe673ffc95cc9fd9e1fad7e0b1d0dd8820703a4f59c2488d148193a48d8fdc23a6dca8137011b00000002540be400021a00030d40ff9fa200d90102828258201287e9ce9e00a603d250b557146aa0581fc4edf277a244ce39d3b2f2ced5072f5840ae4cc1168265e2f60fec9ca9b644eaa42a77e65a39176e04aef29b01e25653a307d39ba61761f8d1ca44696e1d6bdf7a0679413ea3c448f76268e6eb02074102825820742d8af3543349b5b18f3cba28f23b2d6e465b9c136c42e1fae6b2390f5654275840112c95c93013e63fa73ee6a645fd522808d4dee019626e395a8042755c15fb1824e1503c17ea843a838809f55822366b05bce2e378d0b955e66d625c8e9acf0001d90102818200581c45d70e54f3b5e9c5a2b0cd417028197bd6f5fa5378c2f5eba896678da100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584005383334e98e9263b93ffeb3e5908dbd5657caa67d32f9964d7f91dbda76fff164cbeabb981beef470d0d3e1724b85847e6fbc1c039084a817110eabf9d29e08a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258406151f0d0cae4ef0ace62dc03300dcee276765c1b493ac61f770d0633f0f71fe0d642ef29b593c511034a847dd569c56a0278e063c46d6d060abad4e6baf4b705a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840cf518f30d291871a0277e367534557205cc015d3a0d78070e1aee298aeaae3e81d70b42c464a63fa741b5300b48c1699dfc39fdf07f96b8eb240c7d6d3267805a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584056185de4c1d012d322e7e82ae98f24887ed7264f262db53f019e5900b9110a439e5a462a75be036f9f04b0ddcf09decb0894c7b6b9ff17ab4cae8184072d690fa100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840ab78c606155b6aacbcf6692a18d97dd8773b4ae4c96684e4abb9cc59233023f67650ef7259069deddb65ba770ac3a1401d169ce33ec9483b8ebb9e83472e2c06a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840e21574b02002efcfe81382326aa98a0a971327ad4049690a04985400fcb14db7adc8149a0ce4dbfb5afa0d240ed9da23f15c1020d2826f50fc579a10a3662d0da10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840e64e3c19644edb6e788112ac75b4426ef94d535f1ffd9a34e86745777feaf083dc8e847a62634fef320a08b566c24ea26e8dc9e7b49fc456554215cedc0d3508a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840f49fd8eeaa366873aeb2530b2bbcbf7c5970866162ae7250c4b913e19062de1396ed70d1e32a4605071bac11c2cde3fec1dc5b37044cbea073668fe5c478400ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840f0ddd023e0dbda32d296b359db809b0088246e512fd34c7c0cc4b5ae974361873e02330e955eaaf97117525bcb3cd014bb70810f8d0d62a28c3242c86d8c3a08a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840615ee0444f039f02b26791872d6cd5562728cdc6dad02acc71475567b09f3d4b4655c601bf816ef6d11b2f3f81eeb6db09d800bf1bf4e2daf29493338c232901a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840d62bfd428359f4cd04950cc73f574f0ec1c613284fdff8028ed3d876b18b69335beee9792410c6dbdc1b196b4703f250fbaeb66569869ae97d7ee843b9053405a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840d813a836cc44c70647b2e7941fb72a4f720f16aca17e155a6c6a6f9bf175b1e49a3beff6edcfb0c442cc24790a12ee0b1d499a32fdbfc0a850f4846708ea340da100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840aae6ac20cd419eaa7f3a95144f9ccdb46401a0db295d544e920a54b5c24fb63197fde03f12174800c3cf5318a73d92ebc53c2ba97803766892add32fd9feb400a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584071d223fd255df1e912a9c0a8230ee9f0ac95d0aa325cd31e50701ac355dfb5f3fbb27983b372c1410156eeba9163aa0f8a9787dab8c44e7afd4e2d07459a4708a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840b7f821c0ff66fcbbe7c61f43725aa2234297d6765e002d0130301cba13465fe89f59a596549725542445c76d17cedc9c9cfea8b8862d41405646d725dabc7d08a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e4584045830e5de108b9353b0c4c561af296e79eddb26b8ccfb18c5bd9fac1baf8d477691229c0bb9ea212ab56d9ae76c92de6ae50686fc0619510b8c35fb69c6b4402a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258400635ac784fe71d2f7927114edfc709dcb56013617df4edb9b6b770b067e7709e8abfd4cdcdd61512894fcf02f16e1d72bfe60fbfb86b815631d791bab132b909a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584032d67fe72feaf2175455815bbb624ee1b93f5efce905280158db94bbb2b5371d9eaff1bed6eddf9eafe8ff64b55f1d7213294bdb459e0b00c437edbcabf4cf07a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258404533de52e9e3f51e39951c9e197f6900081e98f38f3af5c4a7fe9219f8c311eaa43942b7a290ecbbbdd0bf4ef4ef1d11c39e6de4083c86892a6026c27bcf2509a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840d46541920ce2c31ffaa00cb20e8f5f00f48b6b8aa5cda67d22ea4bf12fd318461a0d8c25ee04cd7446e18f0de59b0fd4f6631e29bc8207073f2404793ae5f108a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584041bcd52ae44a3ffaa1abe1cab6c8b21f8010e2f1aee1d8651b9f6e94aabf5b2dbcedb45dd154b78dce1c5b9679956dd25153a0d945d3eb83c1de3b713e721008a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840027da3169c9c1fb9a67104061698bb0dadb2f58b660af4b461e7353fab1545a3d03157e077a388ec8556176239df3241255feb1f13b5e406bf7c3ad3af7d4202a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e458401ae63dc54e511965f7010971de7fb99585afe492cb8084b395ee01555c1e5657ab08a24be0f70d4e9cd1bde2a6ae31815c5f64025c0415afe2d503b2cb5b3e0ca10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840d2211679ca8e9cc5de71b21bac2b58fd355f5cbd2b42ff31ec37af77b776fb77c64fa76a830f240c29a4b95ae6bff9c58fc6bc2b3d18b57a2af11710ae6e3006a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584084bb7bf64ecea446d6beca88bfa2c7de71d8899e96006920c4c18e52f042aa71e1d27e60bdb6d9d6b1aa2e3330f59ee95b2c001909ff8994ea1fe4e5cd7a760aa100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840c6f3d6194a2fdb2a50417f80dd020adf866c91a102f22eb6bc66f5131292a1a42e9a3550e18e06cb17bd153c08f55a6cce3a1c82052ec9197495900f3ca4f407a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258401a28cac7e80a657563e1433459f369bb0cb05e7e3ead78378dfc2ad15baa344e76e1ac0ca631c67848e81896fd6838b3821961928911635ca069f12c05772a08a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840d201ce4ca5572db792d1563ef3614f2e2b27072e4751327f4a8f75201183a189ac57cdd9399474850e87031c7545c896ebab3983434bb6005690b9ad8fd9d50aa100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258403f45e44aa7457c714599f0100702ec265e91493e30c57ba4f1f74d912858bae8fb71fdf2faddf865c816cb0218eda0db17b707c8f429290f1a1c02b6a4450a0ea100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840e16b1d8f5992fda268c9d7e5c0ab6c5d38b8abaa6b92ccae5b0d2f3079d098ab67ba9a15b27807746f3c7695091ec5bb74ba8772baae14d2786eb8a512d70201a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840628a5491c5d0b4e0202b7aae87a343afd642345b823252355f6d392d8398d2174c141622e3de167b4f82c3cb8b4e8105b341851005d2ec0c1e35c354006a910ba100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258408ad2ee919f520a903764e0322800f7c086f870374f063d2e62ad1dfbf54e71305d90371abe3a196132e123b9248281f2d676fb29442f80249f644ce1185dfc03a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840d53bf84fe8712171151bb6d5f988d76428292639737d817986b46d629aea6eac2a90675cbf0347ec004ce23f9ca0b2dcff5c6d1be91ab478634de8ba8ab96102a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258404702623a2a94b9efbc03dc7d506c4bd70c1e0fea8b21f3b76c592883f0c364ffc12215e59f9ea4d2eed2e786376e6128650b4c9c3f6ad9419f070fb458efa10ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584072bb89ee81a7bcc4a866ae498d3ca076d5d5a885547c7f5899b8b59b3077310f58df420e470bf36d4ed5beaaf30eb361f04ed578cdbd0ef04f7cb573f0c0770ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840e112bb46921840820f4d5b40ec45699bc1b818ca8fe77fcc222a6fa1edb2425487f32e2039e2cf6077ce1e8e2e0b0d0581c64fb866c1c183344af131ccb9390ba100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840039329e261d8386f81a28f5ef5062196a46b5d4389b06bde97e662f69e37812c3ee75352f392121f58e76e5c1e1649656632b01ea46f932ccedcee102d625001a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840c1dab5e2482101ad1bd04f4018425c7133171aaf1274573ed35305f4e37bddadb3636f0aa098d2c0b5f615e8eb629bb94afac5d4c4c0743dba16a847d898b905a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840abb898b3e1ae3c4c9d068a4517b83a070d4037f979b6365ea5eb368e7d43b3fd2152fb93a462fdc553f973d90ab136332057fb66ea529d4fbc53e7f082b6fe03a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258404a09ccfd9c09c6a453f46c721d280065081d89aa4b84fc809d75db1b923e78963bcbf36d64786d8c09c527e90da744e83116617b2e18d9145bac6cf66f876c08a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258408df2408adbd8b4c990a913b9ed2455c9de72d561ddb8f3ec0da5d1513f417a2fcee9ea9ace30cb840d37950c41455bd3655d12d534b70a6eac7034950f821108a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840534b54cc145722e3f7a62935d84c025e17e31c4b08d3f3fb16bb7673d37e9afb07fbdb5ffce5aeef743376bac161973e565e1c12db97bcd879cd7e9030c2a00ea100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840664fd5d8bc5d93509d02104f39e7a22c6cd894f49935cac9e662a9202b9a64baa9f55cd8aa07d3d1e095e9b974c59c0a8c50d14b0d077d70e236ad5cf52ac104a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840469cdadb48349224303d14980cab5c2ae5dacd0f6e36714d8dcb9ca85fa4eb688bd7b1334e30f8718178f7f36d8c5b204e0f9cdce5f88762fc2cbe2cb28c1d03a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840f330820de229a476e1b3f84dfcf9ad98264070212e6e2d61d8b05afb1e12a1426cfd7cb0b284db237d5882cecd6e8f1fe2cf9ddc06783242812178bcb053a105a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584066b89001a28583bed59dbd07d359333207eb74d39ee092c0bf1da4351da64d38c9938a3682bb52a4253dc76074767b4cc2bc1eb2a31bbe6be3c45a5c52cbdf04a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840f365b299297ade5117d72156050cb81a76ba0b859cb46d0f2326c4071110440108b20390f878ba082d41217b2a8fd5f1435b9ba48d176ad5dcb6faff54976b0da100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258407fb0b1f28d6ca64a29c6c7a51a2ab3436809b5038c06b8204104e3b98afa915246f742e2f1bd569f5132d2bbdcaeae68215a0b0f17f6367ce4eea37ed951ec01a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258406e6d2263c440486fc1b3c2aa930e519f20b70f70c28cb532d031f63cefc55c56f4647b10dd6390aa0d0f2ba75bf6cbe3ce2fc6d928dc4db74388f1e5e3057b0ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840dbb299923c24a70ae10dc637d862b750b3e6548e64c590674d2ceb87b7535199ea8dfd024694d26ae1dbbca683b1a4ba90af7d1680a9b8e4819a2ee6229e2408a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258405b2397d031c48f56b43416daea99dd3d8bd1733cb83c2a688dbe8b5dd9bfe64d596280d71973d7d540e929262dafd79b14954b855635fe845642090241003503a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840482c9089f2d60eb069432bf7f7213178a6fe3d52d37a4fa5aec677875bccdac64de7a45c6eb0bd4996414412b12d3e887a1b391e775ef56c47d53f9c944d020ba100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258401972d18c1e9c62837121efafddc2ec778a3a8f9ec5f534c9922778905d8f809609d6c92e427852d8b5f822ad590fdeacf3877c8056f0768b44b025a2b79e7704a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258409b7141b69a493bc4d597a645ed488325492772ad4c3cd5c5c7805a5d951a4b6ed960ea27428d1add867fe7c209f4e65000bdfa878bd7a4357b223e9c55af450da100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258407ac2d7823887de83bca86216e424ccb63fe9f4fa1f106bffc6afa388e91845c97177c410f1a8ca5ecd9f2701c42f5f9dd2faeb0ecf2163a37521badc8a6c1b03a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840c49b6714ccbbdebebb9461a2efb39b9ac5e00a389aadfb2a1b4fe384733610c45e1f03825f65e182988da97659a71e378d49d17fb93d76b80d579b7d49399b06a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840b53ea3508cbd7da47fef05e98c0b31b13ec33de4596ba4061a8e04d91b1015c49f328da58084a6f573d93cdb7aa0972a1a1936a69ee7362adf65df3eae4e2400a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840b1c6b15b311423aa83dfaebe118d1a2a3ff006399f2a63fa82f0d0e0c12bc2b844ec78f5bc8baeef588d15b2237db48cbfa48361a6d630052c9b68e62e035601a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840bb544721cd95655497375390da19fbd87b3c63a4edf333688e2fee7935e96b6572f84b81d80fee5239062be6d3c6a75a5f0c50696d3c6663d26cecdfd8fdc808a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840a7018dfacec705459856bc706b7d05d04c56867fb64dfd0cf97fa980e881cc609e91cf6df204fb6906f860e5cf390e0290d302b6231664aad8c2b4cb30090609a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258403ddf27cce21fbf1a361233d2bcff92ecc9d3cce64c3d8186495e3509b843a0a326f528be8241b8557bf3cdac9c304fcf0fa8fd2a8e389d6acf9fc62b5626d705a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584013bd40ae7383feb674c2cd3357122aec3f6efe17b9b4f25c47cd3dfec194d0c4f20a52cc30fb92245c1a20a962772808f3dc6ee51261c86af16879a1fed2210ba100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840a1cf100aff45ee45c0f868214187640c8c29cb005c7aab7100ff86338d78f972733a611b4e0dae436fe9e1493d6ece69c35ada3cc6506e730ea1bae277468108a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840667d6d2987788454c41f2e86867fe98e1fd48aa789fbf9cf2208f927a8f9941d0384ebf3e3e45ee80c49934aad9b6ccaa13179b69f35b9acd21b55f56caff80da100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258401cd5c48fa46d8f0fb07c7737b7719d1fba5729478be3eef3e2e19942c4d6d54b01a569eb34d4f4be84a2f6961832ec17bade0511cbc01f5db5749a09bb4d8808a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258403bb5ddd91b5f5d7366b41330bf5bbbf7cf7d703bd50376ac21b07c6da696562266361678c06247a57111c63bc1fe58463a8c125e0d117bdf05cd4fe57bb8b90aa100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840397067045ffe0cf7126a5f73f228e1bc8721c617ebb7c41c1bc2f7b6c8cc50bf2370bc1ee679bcb0581e11da1e186504f2e3f3322fddf40a1863067ffc5e2903a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840831f5ea5dd354f5a522e044b53aa1966d036871d5a3b7d2323e404996907a33aff5aabb9db22301064033045cbf14c91d29de84b8fbbb75683ff1cb51fd0920aa100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258401e8cc65db39cb5e9d54dac50cda84c55fd2f989f667a11dc46521768ac2f46a27a70173a92e849ee621ebe3025d87088528e7450b8312d678b6249f5a124f70fa10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840f4835bbcf5459db0826e27ea95d3ac31f7bea56c0253716212ef421995c7546a963ac89dc6cffad75692a149372cbdeaa19a9dcd171ac423711e8d71c495d703a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258408039411659145a9fb3863b2ae2f3890009bf004332f58daa6662368a7378d215cc7f40569284d5b86c5a7be210cdcb5551633762b5a7d3f8ad2095a220fec609a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb82584002c9ec1d95d0464eba5f4a656d72b55a92a80078e83f2f47682729af0fc782a68f3f31db7c64018ae8fbd36c5ac72d5573357a7578359056b9d3f9a29467d80ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258406f176d0fdb8768aad902c8fc115eb797719ef62a93938d7f09bbb213a88d61294143ec1d508a2a450f0e16ab3e2ffb7e0ed4cd7f75b3f2ff9f70cfaa77764506a10081825820b6a42d4ccc4d26adaec67e8578bf31f13b1b7e640527356248f2ec547f9de6e45840f4262eeb9183eec1e896b9be61984ed9d4192b38825ba1b560ea23fe6e3224d3c94f4cc64174c1d9166e665e1a1ff8f0c84024bb8b9068b853fe4ce683d96906a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840df64e327056e335f484582fa0e4188e62620968e955431fc3576da2c1935b15ec605bb5d738f5000dcdc022646f9545d6932c2c79611dccab116295ca03c2b04a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840132cce5cea5d8bf7e9b802e4477eff041ebe1c12a8b8658a42ae91727cbf4f39b0d23831c70923a68ad7a023092bcecb61ac6253fdd17be00cecc37a71301300a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840842ecaf1b907cb34799d78d6f35eb349a986559a396840aeba5f6e8dc7e4172c16bddcb1f926a21175531478773046e9484aeb4ca83c1cbaf25f5a4813afdd0ca100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb825840a417cdf9e02755e53d6061ce0c93cacb7de24ce33e3fda9ac3a85414a7bf62676446822875972867d5647e46c22e21099e5dd8e4114949b30b86146b0dba1b05a100818258206ca065df8b220ae79a96e871f92e53b7e816200b789749ab5f38e105a436eb8258401f2e86831349fa458c3e2403e2caacbf046fae3c513575e8ce2d34037d34337f7e58cc98cadf034e8bce930335285220624945b316fe6af71e8ef0d12ef05001ffa100d90103a100a11902a2a1636d73678f78264175746f2d4c6f6f702d5472616e73616374696f6e202336313138303020627920415441444160783c4c6976652045706f6368203234352c207765206861766520303132682032386d20323773206c65667420756e74696c20746865206e657874206f6e6578374974277320446f6e6e657273746167202d20313520466562727561722032303234202d2031333a30313a333320696e20417573747269616060607820412072616e646f6d205a656e2d51756f746520666f7220796f753a20f09f998f783b4265206b696e642c20666f722065766572796f6e6520796f75206d656574206973206669676874696e6720612068617264657220626174746c652e68202d20506c61746f6078374e6f64652d5265766973696f6e3a203462623230343864623737643632336565366533363738363138633264386236633436373633333360782953616e63686f4e657420697320617765736f6d652c206861766520736f6d652066756e2120f09f988d7819204265737420726567617264732c204d617274696e203a2d2980"; + let versioned_block = VersionedBlock::from_hex(versioned_block_hex).unwrap(); + + let bytes = versioned_block.to_bytes(); + let json = versioned_block.to_json().unwrap(); + + let from_bytes = VersionedBlock::from_bytes(bytes).unwrap(); + let from_json = VersionedBlock::from_json(&json).unwrap(); + + assert_eq!(from_json, versioned_block); + assert_eq!(from_bytes, versioned_block); +} + +#[test] +fn redeemers_default_array_round_trip() { + let redeemers = Redeemers::from(vec![ + Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(12), + &PlutusData::new_integer(&BigInt::one()), + &ExUnits::new(&BigNum(123), &BigNum(456)), + ), + Redeemer::new( + &RedeemerTag::new_cert(), + &BigNum(2), + &PlutusData::new_integer(&BigInt::from(22)), + &ExUnits::new(&BigNum(23), &BigNum(45)), + ) + ]); + + let bytes = redeemers.to_bytes(); + let new_redeemers = Redeemers::from_bytes(bytes.clone()).unwrap(); + + assert_eq!(new_redeemers.serialization_format, Some(CborContainerType::Array)); + assert_eq!(redeemers.serialization_format, None); + assert_eq!(redeemers, new_redeemers); + assert_eq!(bytes, new_redeemers.to_bytes()) +} + +#[test] +fn redeemers_array_round_trip() { + let redeemers_vec = vec![ + Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(12), + &PlutusData::new_integer(&BigInt::one()), + &ExUnits::new(&BigNum(123), &BigNum(456)), + ), + Redeemer::new( + &RedeemerTag::new_cert(), + &BigNum(2), + &PlutusData::new_integer(&BigInt::from(22)), + &ExUnits::new(&BigNum(23), &BigNum(45)), + ) + ]; + + let redeemers = Redeemers::new_with_serialization_format(redeemers_vec, CborContainerType::Array); + + let bytes = redeemers.to_bytes(); + let new_redeemers = Redeemers::from_bytes(bytes.clone()).unwrap(); + + assert_eq!(new_redeemers.serialization_format, Some(CborContainerType::Array)); + assert_eq!(redeemers, new_redeemers); + assert_eq!(bytes, new_redeemers.to_bytes()) +} + +#[test] +fn redeemers_map_round_trip() { + let redeemers_vec = vec![ + Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(12), + &PlutusData::new_integer(&BigInt::one()), + &ExUnits::new(&BigNum(123), &BigNum(456)), + ), + Redeemer::new( + &RedeemerTag::new_cert(), + &BigNum(2), + &PlutusData::new_integer(&BigInt::from(22)), + &ExUnits::new(&BigNum(23), &BigNum(45)), + ) + ]; + + let redeemers = Redeemers::new_with_serialization_format(redeemers_vec, CborContainerType::Map); + + let bytes = redeemers.to_bytes(); + let new_redeemers = Redeemers::from_bytes(bytes.clone()).unwrap(); + + assert_eq!(new_redeemers.serialization_format, Some(CborContainerType::Map)); + assert_eq!(redeemers, new_redeemers); + assert_eq!(bytes, new_redeemers.to_bytes()) +} + +#[test] +fn redeemers_map_array_round_trip() { + let redeemers_vec = vec![ + Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum(12), + &PlutusData::new_integer(&BigInt::one()), + &ExUnits::new(&BigNum(123), &BigNum(456)), + ), + Redeemer::new( + &RedeemerTag::new_cert(), + &BigNum(2), + &PlutusData::new_integer(&BigInt::from(22)), + &ExUnits::new(&BigNum(23), &BigNum(45)), + ) + ]; + + let redeemers_array = Redeemers::new_with_serialization_format(redeemers_vec.clone(), CborContainerType::Array); + let redeemers_map = Redeemers::new_with_serialization_format(redeemers_vec, CborContainerType::Map); + + let bytes_array = redeemers_array.to_bytes(); + let new_redeemers_array = Redeemers::from_bytes(bytes_array.clone()).unwrap(); + + let bytes_map = redeemers_map.to_bytes(); + let new_redeemers_map = Redeemers::from_bytes(bytes_map.clone()).unwrap(); + + assert_eq!(new_redeemers_array.serialization_format, Some(CborContainerType::Array)); + assert_eq!(redeemers_array, new_redeemers_array); + assert_eq!(bytes_array, new_redeemers_array.to_bytes()); + + assert_eq!(new_redeemers_map.serialization_format, Some(CborContainerType::Map)); + assert_eq!(redeemers_map, new_redeemers_map); + assert_eq!(bytes_map, new_redeemers_map.to_bytes()); + + assert_eq!(new_redeemers_map, new_redeemers_array); + assert_ne!(bytes_array, bytes_map) +} + +#[test] +fn redeemers_map_deserialization() { + let hex = "a282000082d8799f0102030405ff821821182c82040082d8799f0102030405ff8218371842"; + let redeemers = Redeemers::from_hex(hex); + assert!(redeemers.is_ok()); +} + +#[test] +fn ref_script_serialization() { + let plutus_script = PlutusScript::new([61u8; 29].to_vec()); + let script_ref = ScriptRef::new_plutus_script(&plutus_script); + let script_enum = ScriptRefEnum::PlutusScript(plutus_script); + let unwrapped_bytes = to_bytes(&script_enum); + let wrapped_bytes = script_ref.to_bytes(); + + assert_eq!(unwrapped_bytes, script_ref.to_unwrapped_bytes()); + assert_ne!(unwrapped_bytes, wrapped_bytes); + + let new_script_ref = ScriptRef::from_bytes(wrapped_bytes).unwrap(); + assert_eq!(script_ref, new_script_ref); +} + +#[test] +fn boostrap_witnesses_round_trip() { + let mut witnesses = BootstrapWitnesses::new(); + let bootstrap_witness_1 = fake_boostrap_witness(1); + let bootstrap_witness_2 = fake_boostrap_witness(2); + let bootstrap_witness_3 = fake_boostrap_witness(3); + + witnesses.add(&bootstrap_witness_1); + witnesses.add(&bootstrap_witness_2); + witnesses.add(&bootstrap_witness_3); + + let bytes = witnesses.to_bytes(); + let new_witnesses = BootstrapWitnesses::from_bytes(bytes.clone()).unwrap(); + + let json = witnesses.to_json().unwrap(); + let new_witnesses_json = BootstrapWitnesses::from_json(&json).unwrap(); + + assert_eq!(witnesses, new_witnesses); + assert_eq!(witnesses, new_witnesses_json); + assert_eq!(bytes, new_witnesses.to_bytes()); + assert_eq!(json, new_witnesses_json.to_json().unwrap()); +} + +#[test] +fn credential_round_trip() { + let mut credentials = Credentials::new(); + let credential_1 = Credential::from_keyhash(&fake_key_hash(1)); + let credential_2 = Credential::from_keyhash(&fake_key_hash(2)); + let credential_3 = Credential::from_keyhash(&fake_key_hash(3)); + + credentials.add(&credential_1); + credentials.add(&credential_2); + credentials.add(&credential_3); + + let bytes = credentials.to_bytes(); + let new_credentials = Credentials::from_bytes(bytes.clone()).unwrap(); + + let json = credentials.to_json().unwrap(); + let new_credentials_json = Credentials::from_json(&json).unwrap(); + + assert_eq!(credentials, new_credentials); + assert_eq!(credentials, new_credentials_json); + assert_eq!(bytes, new_credentials.to_bytes()); + assert_eq!(json, new_credentials_json.to_json().unwrap()); +} + +#[test] +fn ed25519_key_hashes_round_trip() { + let mut keyhashes = Ed25519KeyHashes::new(); + let keyhash_1 = fake_key_hash(1); + let keyhash_2 = fake_key_hash(2); + let keyhash_3 = fake_key_hash(3); + + keyhashes.add(&keyhash_1); + keyhashes.add(&keyhash_2); + keyhashes.add(&keyhash_3); + + let bytes = keyhashes.to_bytes(); + let new_keyhashes = Ed25519KeyHashes::from_bytes(bytes.clone()).unwrap(); + + let json = keyhashes.to_json().unwrap(); + let new_keyhashes_json = Ed25519KeyHashes::from_json(&json).unwrap(); + + assert_eq!(keyhashes, new_keyhashes); + assert_eq!(keyhashes, new_keyhashes_json); + assert_eq!(bytes, new_keyhashes.to_bytes()); + assert_eq!(json, new_keyhashes_json.to_json().unwrap()); +} + +#[test] +fn vkey_witnesses_round_trip() { + let mut witnesses = Vkeywitnesses::new(); + let vkey_witness_1 = fake_vkey_witness(1); + let vkey_witness_2 = fake_vkey_witness(2); + let vkey_witness_3 = fake_vkey_witness(3); + + witnesses.add(&vkey_witness_1); + witnesses.add(&vkey_witness_2); + witnesses.add(&vkey_witness_3); + + let bytes = witnesses.to_bytes(); + let new_witnesses = Vkeywitnesses::from_bytes(bytes.clone()).unwrap(); + + let json = witnesses.to_json().unwrap(); + let new_witnesses_json = Vkeywitnesses::from_json(&json).unwrap(); + + assert_eq!(witnesses, new_witnesses); + assert_eq!(witnesses, new_witnesses_json); + assert_eq!(bytes, new_witnesses.to_bytes()); + assert_eq!(json, new_witnesses_json.to_json().unwrap()); +} + +#[test] +fn tx_inputs_round_trip() { + let mut inputs = TransactionInputs::new(); + let input_1 = fake_tx_input(1); + let input_2 = fake_tx_input(2); + let input_3 = fake_tx_input(3); + + inputs.add(&input_1); + inputs.add(&input_2); + inputs.add(&input_3); + + let bytes = inputs.to_bytes(); + let new_inputs = TransactionInputs::from_bytes(bytes.clone()).unwrap(); + + let json = inputs.to_json().unwrap(); + let new_inputs_json = TransactionInputs::from_json(&json).unwrap(); + + assert_eq!(inputs, new_inputs); + assert_eq!(inputs, new_inputs_json); + assert_eq!(bytes, new_inputs.to_bytes()); + assert_eq!(json, new_inputs_json.to_json().unwrap()); +} diff --git a/rust/src/tests/serialization/governance/common.rs b/rust/src/tests/serialization/governance/common.rs new file mode 100644 index 00000000..930f1476 --- /dev/null +++ b/rust/src/tests/serialization/governance/common.rs @@ -0,0 +1,372 @@ +use crate::*; +use crate::tests::fakes::{fake_anchor_data_hash, fake_key_hash, fake_script_hash, fake_tx_hash}; + +#[test] +fn anchor_ser_round_trip() { + let anchor = Anchor::new( + &URL::new("https://iohk.io".to_string()).unwrap(), + &fake_anchor_data_hash(1), + ); + + let cbor = anchor.to_bytes(); + let cbor_hex = anchor.to_hex(); + let json = anchor.to_json().unwrap(); + + assert_eq!(anchor, Anchor::from_bytes(cbor).unwrap()); + assert_eq!(anchor, Anchor::from_hex(&cbor_hex).unwrap()); + assert_eq!(anchor, Anchor::from_json(&json).unwrap()); +} + +#[test] +fn drep_key_hash_ser_round_trip() { + let drep = DRep::new_key_hash(&fake_key_hash(1)); + + let cbor = drep.to_bytes(); + let cbor_hex = drep.to_hex(); + let json = drep.to_json().unwrap(); + + assert_eq!(drep, DRep::from_bytes(cbor).unwrap()); + assert_eq!(drep, DRep::from_hex(&cbor_hex).unwrap()); + assert_eq!(drep, DRep::from_json(&json).unwrap()); + assert_eq!(drep.kind(), DRepKind::KeyHash); +} + +#[test] +fn drep_script_hash_ser_round_trip() { + let drep = DRep::new_script_hash(&fake_script_hash(1)); + + let cbor = drep.to_bytes(); + let cbor_hex = drep.to_hex(); + let json = drep.to_json().unwrap(); + + assert_eq!(drep, DRep::from_bytes(cbor).unwrap()); + assert_eq!(drep, DRep::from_hex(&cbor_hex).unwrap()); + assert_eq!(drep, DRep::from_json(&json).unwrap()); + assert_eq!(drep.kind(), DRepKind::ScriptHash); +} + +#[test] +fn drep_always_abstain_ser_round_trip() { + let drep = DRep::new_always_abstain(); + + let cbor = drep.to_bytes(); + let cbor_hex = drep.to_hex(); + let json = drep.to_json().unwrap(); + + assert_eq!(drep, DRep::from_bytes(cbor).unwrap()); + assert_eq!(drep, DRep::from_hex(&cbor_hex).unwrap()); + assert_eq!(drep, DRep::from_json(&json).unwrap()); + assert_eq!(drep.kind(), DRepKind::AlwaysAbstain); +} + +#[test] +fn drep_always_no_confidence_ser_round_trip() { + let drep = DRep::new_always_no_confidence(); + + let cbor = drep.to_bytes(); + let cbor_hex = drep.to_hex(); + let json = drep.to_json().unwrap(); + + assert_eq!(drep, DRep::from_bytes(cbor).unwrap()); + assert_eq!(drep, DRep::from_hex(&cbor_hex).unwrap()); + assert_eq!(drep, DRep::from_json(&json).unwrap()); + assert_eq!(drep.kind(), DRepKind::AlwaysNoConfidence); +} + +#[test] +fn drep_to_from_bech32_keshhash() { + let drep = DRep::new_key_hash(&fake_key_hash(1)); + let bech32 = drep.to_bech32().unwrap(); + let drep_deser = DRep::from_bech32(&bech32).unwrap(); + assert_eq!(drep, drep_deser); +} + +#[test] +fn drep_to_from_bech32_script_hash() { + let drep = DRep::new_script_hash(&fake_script_hash(1)); + let bech32 = drep.to_bech32().unwrap(); + let drep_deser = DRep::from_bech32(&bech32).unwrap(); + assert_eq!(drep, drep_deser); +} + +#[test] +fn drep_to_from_bech32_always_abstain() { + let drep = DRep::new_always_abstain(); + let bech32 = drep.to_bech32(); + assert!(bech32.is_err()); +} + +#[test] +fn drep_to_from_bech32_always_no_confidence() { + let drep = DRep::new_always_no_confidence(); + let bech32 = drep.to_bech32(); + assert!(bech32.is_err()); +} + +#[test] +fn governance_action_id_ser_round_trip() { + let gov_action_id = + GovernanceActionId::new(&fake_tx_hash(1), GovernanceActionIndex::from(42u32)); + + let cbor = gov_action_id.to_bytes(); + let cbor_hex = gov_action_id.to_hex(); + let json = gov_action_id.to_json().unwrap(); + + assert_eq!(gov_action_id, GovernanceActionId::from_bytes(cbor).unwrap()); + assert_eq!( + gov_action_id, + GovernanceActionId::from_hex(&cbor_hex).unwrap() + ); + assert_eq!(gov_action_id, GovernanceActionId::from_json(&json).unwrap()); +} + +#[test] +fn voter_constitutional_committee_hot_key_hash_ser_round_trip() { + let voter = + Voter::new_constitutional_committee_hot_credential(&Credential::from_keyhash(&fake_key_hash(1))); + + let cbor = voter.to_bytes(); + let cbor_hex = voter.to_hex(); + let json = voter.to_json().unwrap(); + + assert_eq!(voter, Voter::from_bytes(cbor).unwrap()); + assert_eq!(voter, Voter::from_hex(&cbor_hex).unwrap()); + assert_eq!(voter, Voter::from_json(&json).unwrap()); + assert_eq!(voter.kind(), VoterKind::ConstitutionalCommitteeHotKeyHash); +} + +#[test] +fn voter_constitutional_committee_hot_script_hash_ser_round_trip() { + let voter = Voter::new_constitutional_committee_hot_credential(&Credential::from_scripthash( + &fake_script_hash(1), + )); + + let cbor = voter.to_bytes(); + let cbor_hex = voter.to_hex(); + let json = voter.to_json().unwrap(); + + assert_eq!(voter, Voter::from_bytes(cbor).unwrap()); + assert_eq!(voter, Voter::from_hex(&cbor_hex).unwrap()); + assert_eq!(voter, Voter::from_json(&json).unwrap()); + assert_eq!( + voter.kind(), + VoterKind::ConstitutionalCommitteeHotScriptHash + ); +} + +#[test] +fn voter_drep_key_hash_ser_round_trip() { + let voter = Voter::new_drep_credential(&Credential::from_keyhash(&fake_key_hash(1))); + + let cbor = voter.to_bytes(); + let cbor_hex = voter.to_hex(); + let json = voter.to_json().unwrap(); + + assert_eq!(voter, Voter::from_bytes(cbor).unwrap()); + assert_eq!(voter, Voter::from_hex(&cbor_hex).unwrap()); + assert_eq!(voter, Voter::from_json(&json).unwrap()); + assert_eq!(voter.kind(), VoterKind::DRepKeyHash); +} + +#[test] +fn voter_drep_script_hash_ser_round_trip() { + let voter = Voter::new_drep_credential(&Credential::from_scripthash(&fake_script_hash(1))); + + let cbor = voter.to_bytes(); + let cbor_hex = voter.to_hex(); + let json = voter.to_json().unwrap(); + + assert_eq!(voter, Voter::from_bytes(cbor).unwrap()); + assert_eq!(voter, Voter::from_hex(&cbor_hex).unwrap()); + assert_eq!(voter, Voter::from_json(&json).unwrap()); + assert_eq!(voter.kind(), VoterKind::DRepScriptHash); +} + +#[test] +fn voter_staking_pool_ser_round_trip() { + let voter = Voter::new_stake_pool_key_hash(&fake_key_hash(1)); + + let cbor = voter.to_bytes(); + let cbor_hex = voter.to_hex(); + let json = voter.to_json().unwrap(); + + assert_eq!(voter, Voter::from_bytes(cbor).unwrap()); + assert_eq!(voter, Voter::from_hex(&cbor_hex).unwrap()); + assert_eq!(voter, Voter::from_json(&json).unwrap()); + assert_eq!(voter.kind(), VoterKind::StakingPoolKeyHash); +} + +#[test] +fn voting_procedure_no_ser_round_trip() { + let voting_procedure = VotingProcedure::new(VoteKind::No); + + let cbor = voting_procedure.to_bytes(); + let cbor_hex = voting_procedure.to_hex(); + let json = voting_procedure.to_json().unwrap(); + + assert_eq!(voting_procedure, VotingProcedure::from_bytes(cbor).unwrap()); + assert_eq!( + voting_procedure, + VotingProcedure::from_hex(&cbor_hex).unwrap() + ); + assert_eq!(voting_procedure, VotingProcedure::from_json(&json).unwrap()); + assert_eq!(voting_procedure.vote_kind(), VoteKind::No); +} + +#[test] +fn voting_procedure_yes_ser_round_trip() { + let voting_procedure = VotingProcedure::new(VoteKind::Yes); + + let cbor = voting_procedure.to_bytes(); + let cbor_hex = voting_procedure.to_hex(); + let json = voting_procedure.to_json().unwrap(); + + assert_eq!(voting_procedure, VotingProcedure::from_bytes(cbor).unwrap()); + assert_eq!( + voting_procedure, + VotingProcedure::from_hex(&cbor_hex).unwrap() + ); + assert_eq!(voting_procedure, VotingProcedure::from_json(&json).unwrap()); + assert_eq!(voting_procedure.vote_kind(), VoteKind::Yes); +} + +#[test] +fn voting_procedure_abstain_ser_round_trip() { + let voting_procedure = VotingProcedure::new(VoteKind::Abstain); + + let cbor = voting_procedure.to_bytes(); + let cbor_hex = voting_procedure.to_hex(); + let json = voting_procedure.to_json().unwrap(); + + assert_eq!(voting_procedure, VotingProcedure::from_bytes(cbor).unwrap()); + assert_eq!( + voting_procedure, + VotingProcedure::from_hex(&cbor_hex).unwrap() + ); + assert_eq!(voting_procedure, VotingProcedure::from_json(&json).unwrap()); + assert_eq!(voting_procedure.vote_kind(), VoteKind::Abstain); +} + +#[test] +fn voting_procedures_single_item_ser_round_trip() { + let mut voting_procedures = VotingProcedures::new(); + + voting_procedures.insert( + &Voter::new_constitutional_committee_hot_credential(&Credential::from_keyhash(&fake_key_hash(1))), + &GovernanceActionId::new(&fake_tx_hash(1), GovernanceActionIndex::from(42u32)), + &VotingProcedure::new(VoteKind::Yes), + ); + + let cbor = voting_procedures.to_bytes(); + let cbor_hex = voting_procedures.to_hex(); + let json = voting_procedures.to_json().unwrap(); + + assert_eq!( + voting_procedures, + VotingProcedures::from_bytes(cbor).unwrap() + ); + assert_eq!( + voting_procedures, + VotingProcedures::from_hex(&cbor_hex).unwrap() + ); + assert_eq!( + voting_procedures, + VotingProcedures::from_json(&json).unwrap() + ); +} + +#[test] +fn voting_procedures_muiltiple_items_ser_round_trip() { + let mut voting_procedures = VotingProcedures::new(); + + voting_procedures.insert( + &Voter::new_constitutional_committee_hot_credential(&Credential::from_keyhash(&fake_key_hash(1))), + &GovernanceActionId::new(&fake_tx_hash(1), GovernanceActionIndex::from(42u32)), + &VotingProcedure::new(VoteKind::Yes), + ); + + voting_procedures.insert( + &Voter::new_constitutional_committee_hot_credential(&Credential::from_keyhash(&fake_key_hash(2))), + &GovernanceActionId::new(&fake_tx_hash(2), GovernanceActionIndex::from(43u32)), + &VotingProcedure::new(VoteKind::No), + ); + + voting_procedures.insert( + &Voter::new_constitutional_committee_hot_credential(&Credential::from_keyhash(&fake_key_hash(3))), + &GovernanceActionId::new(&fake_tx_hash(3), GovernanceActionIndex::from(44u32)), + &VotingProcedure::new(VoteKind::Abstain), + ); + + let cbor = voting_procedures.to_bytes(); + let cbor_hex = voting_procedures.to_hex(); + let json = voting_procedures.to_json().unwrap(); + + assert_eq!( + voting_procedures, + VotingProcedures::from_bytes(cbor).unwrap() + ); + assert_eq!( + voting_procedures, + VotingProcedures::from_hex(&cbor_hex).unwrap() + ); + assert_eq!( + voting_procedures, + VotingProcedures::from_json(&json).unwrap() + ); +} + +#[test] +fn tx_with_vote_deser_test() { + let cbor = "84a400818258204547c077e8f3a9184438e36503f78b634eb416658c336c2d017d9912a7c493c7000181a20058390013ca2480e9651a5c504b36eda271ec171cdd404cfe349097524a48bd8bee57ce33c7c1f711bc5801986d89dd68078f5922b83812cc86f65f011b0000000253d3ae64021a0002a38913a18202581c1033bbc7db733c057fed63fa085113dfb570566eb708d548d2f7cce8a1825820787142668a73c7c3ca6003571f429393f2d6dad8886bbcd0a9ba7aca07cc895e008201f6a0f5f6"; + let tx_deser = Transaction::from_hex(cbor); + assert!(tx_deser.is_ok()); + let tx = tx_deser.unwrap(); + let procedures = tx.body().voting_procedures().unwrap(); + assert_eq!(procedures.0.len(), 1); + + let voter = procedures.get_voters().get(0).unwrap(); + let gov_action_ids = procedures.get_governance_action_ids_by_voter(&voter); + assert_eq!(gov_action_ids.0.len(), 1); + let gov_action_id = gov_action_ids.get(0).unwrap(); + let voting_procedure = procedures.get(&voter, &gov_action_id); + assert!(voting_procedure.is_some()); +} + +#[test] +fn voting_proposals_round_trip() { + let mut voting_proposals = VotingProposals::new(); + + let info_action = InfoAction::new(); + let action = GovernanceAction::new_info_action(&info_action); + let proposal = VotingProposal::new( + &action, + &Anchor::new( + &URL::new("https://iohk.io".to_string()).unwrap(), + &fake_anchor_data_hash(1), + ), + &RewardAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &Credential::from_keyhash(&fake_key_hash(1)), + ), + &Coin::from(1_000_011u64), + ); + + voting_proposals.add(&proposal); + + let cbor = voting_proposals.to_bytes(); + let cbor_hex = voting_proposals.to_hex(); + let json = voting_proposals.to_json().unwrap(); + + assert_eq!( + voting_proposals, + VotingProposals::from_bytes(cbor).unwrap() + ); + assert_eq!( + voting_proposals, + VotingProposals::from_hex(&cbor_hex).unwrap() + ); + assert_eq!( + voting_proposals, + VotingProposals::from_json(&json).unwrap() + ); +} \ No newline at end of file diff --git a/rust/src/tests/serialization/governance/mod.rs b/rust/src/tests/serialization/governance/mod.rs new file mode 100644 index 00000000..65163834 --- /dev/null +++ b/rust/src/tests/serialization/governance/mod.rs @@ -0,0 +1,2 @@ +pub mod common; +pub mod proposals; diff --git a/rust/src/tests/serialization/governance/proposals.rs b/rust/src/tests/serialization/governance/proposals.rs new file mode 100644 index 00000000..f8623e5b --- /dev/null +++ b/rust/src/tests/serialization/governance/proposals.rs @@ -0,0 +1,452 @@ +use crate::tests::fakes::{fake_full_protocol_param_update, fake_anchor, fake_anchor_data_hash, fake_key_hash, fake_reward_address, fake_script_hash, fake_tx_hash}; +use crate::*; + +macro_rules! to_from_test { + ($proposal_type: ty, $variable_name: ident, $variable_wrapped_name: ident) => { + let json = $variable_name.to_json().unwrap(); + let cbor = $variable_name.to_bytes(); + let hex_cbor = $variable_name.to_hex(); + + assert_eq!($variable_name, <$proposal_type>::from_json(&json).unwrap()); + assert_eq!($variable_name, <$proposal_type>::from_bytes(cbor).unwrap()); + assert_eq!( + $variable_name, + <$proposal_type>::from_hex(&hex_cbor).unwrap() + ); + + let json_wrapped = $variable_wrapped_name.to_json().unwrap(); + let cbor_wrapped = $variable_wrapped_name.to_bytes(); + let hex_cbor_wrapped = $variable_wrapped_name.to_hex(); + + assert_eq!( + $variable_wrapped_name, + GovernanceAction::from_json(&json_wrapped).unwrap() + ); + assert_eq!( + $variable_wrapped_name, + GovernanceAction::from_bytes(cbor_wrapped).unwrap() + ); + assert_eq!( + $variable_wrapped_name, + GovernanceAction::from_hex(&hex_cbor_wrapped).unwrap() + ); + }; +} + +#[test] +fn committee_ser_round_trip() { + let mut committee = + Committee::new(&UnitInterval::new(&BigNum::from(1u32), &BigNum::from(2u32))); + committee.add_member(&Credential::from_keyhash(&fake_key_hash(1)), 1); + committee.add_member(&Credential::from_scripthash(&fake_script_hash(2)), 2); + + let cbor = committee.to_bytes(); + let cbor_hex = committee.to_hex(); + let json = committee.to_json().unwrap(); + + assert_eq!(committee, Committee::from_bytes(cbor).unwrap()); + assert_eq!(committee, Committee::from_hex(&cbor_hex).unwrap()); + assert_eq!(committee, Committee::from_json(&json).unwrap()); +} + +#[test] +fn committee_empty_ser_round_trip() { + let committee = Committee::new(&UnitInterval::new(&BigNum::from(1u32), &BigNum::from(2u32))); + + let cbor = committee.to_bytes(); + let cbor_hex = committee.to_hex(); + let json = committee.to_json().unwrap(); + + assert_eq!(committee, Committee::from_bytes(cbor).unwrap()); + assert_eq!(committee, Committee::from_hex(&cbor_hex).unwrap()); + assert_eq!(committee, Committee::from_json(&json).unwrap()); +} + +#[test] +fn constitution_ser_round_trip() { + let anchor = Anchor::new( + &URL::new("https://iohk.io".to_string()).unwrap(), + &fake_anchor_data_hash(1), + ); + + let constitution = Constitution::new(&anchor); + + let cbor = constitution.to_bytes(); + let cbor_hex = constitution.to_hex(); + let json = constitution.to_json().unwrap(); + + assert_eq!(constitution, Constitution::from_bytes(cbor).unwrap()); + assert_eq!(constitution, Constitution::from_hex(&cbor_hex).unwrap()); + assert_eq!(constitution, Constitution::from_json(&json).unwrap()); +} + +#[test] +fn constitution_with_script_hash_ser_round_trip() { + let anchor = Anchor::new( + &URL::new("https://iohk.io".to_string()).unwrap(), + &fake_anchor_data_hash(1), + ); + + let constitution = Constitution::new_with_script_hash(&anchor, &fake_script_hash(1)); + + let cbor = constitution.to_bytes(); + let cbor_hex = constitution.to_hex(); + let json = constitution.to_json().unwrap(); + + assert_eq!(constitution, Constitution::from_bytes(cbor).unwrap()); + assert_eq!(constitution, Constitution::from_hex(&cbor_hex).unwrap()); + assert_eq!(constitution, Constitution::from_json(&json).unwrap()); +} + +#[test] +fn hard_fork_initiation_action_ser_round_trip() { + let proposal = HardForkInitiationAction::new(&ProtocolVersion::new(1, 2)); + + let proposal_wrapped = GovernanceAction::new_hard_fork_initiation_action(&proposal); + + to_from_test!(HardForkInitiationAction, proposal, proposal_wrapped); + assert_eq!( + proposal, + proposal_wrapped.as_hard_fork_initiation_action().unwrap() + ); +} + +#[test] +fn hard_fork_initiation_action_with_action_id_ser_round_trip() { + let action_id = GovernanceActionId::new(&fake_tx_hash(1), 0); + let proposal = + HardForkInitiationAction::new_with_action_id(&action_id, &ProtocolVersion::new(1, 2)); + + let proposal_wrapped = GovernanceAction::new_hard_fork_initiation_action(&proposal); + + to_from_test!(HardForkInitiationAction, proposal, proposal_wrapped); + assert_eq!( + proposal, + proposal_wrapped.as_hard_fork_initiation_action().unwrap() + ); +} + +#[test] +fn new_committee_action_ser_round_trip() { + let mut committee = + Committee::new(&UnitInterval::new(&BigNum::from(1u32), &BigNum::from(2u32))); + committee.add_member(&Credential::from_keyhash(&fake_key_hash(1)), 1); + committee.add_member(&Credential::from_scripthash(&fake_script_hash(2)), 2); + + let mut members_to_remove = Credentials::new(); + members_to_remove.add(&Credential::from_keyhash(&fake_key_hash(1))); + members_to_remove.add(&Credential::from_scripthash(&fake_script_hash(2))); + + let proposal = UpdateCommitteeAction::new(&committee, &members_to_remove); + + let proposal_wrapped = GovernanceAction::new_new_committee_action(&proposal); + + to_from_test!(UpdateCommitteeAction, proposal, proposal_wrapped); + assert_eq!( + proposal, + proposal_wrapped.as_new_committee_action().unwrap() + ); +} + +#[test] +fn new_committee_action_with_action_id_ser_round_trip() { + let action_id = GovernanceActionId::new(&fake_tx_hash(1), 0); + let mut committee = + Committee::new(&UnitInterval::new(&BigNum::from(1u32), &BigNum::from(2u32))); + committee.add_member(&Credential::from_keyhash(&fake_key_hash(1)), 1); + committee.add_member(&Credential::from_scripthash(&fake_script_hash(2)), 2); + + let mut members_to_remove = Credentials::new(); + members_to_remove.add(&Credential::from_keyhash(&fake_key_hash(1))); + members_to_remove.add(&Credential::from_scripthash(&fake_script_hash(2))); + + let proposal = + UpdateCommitteeAction::new_with_action_id(&action_id, &committee, &members_to_remove); + + let proposal_wrapped = GovernanceAction::new_new_committee_action(&proposal); + + to_from_test!(UpdateCommitteeAction, proposal, proposal_wrapped); + assert_eq!( + proposal, + proposal_wrapped.as_new_committee_action().unwrap() + ); +} + +#[test] +fn new_committee_action_with_empty_ser_round_trip() { + let committee = Committee::new(&UnitInterval::new(&BigNum::from(1u32), &BigNum::from(2u32))); + let members_to_remove = Credentials::new(); + let proposal = UpdateCommitteeAction::new(&committee, &members_to_remove); + + let proposal_wrapped = GovernanceAction::new_new_committee_action(&proposal); + + to_from_test!(UpdateCommitteeAction, proposal, proposal_wrapped); + assert_eq!( + proposal, + proposal_wrapped.as_new_committee_action().unwrap() + ); +} + +#[test] +fn new_constitution_action_ser_round_trip() { + let anchor = Anchor::new( + &URL::new("https://iohk.io".to_string()).unwrap(), + &fake_anchor_data_hash(1), + ); + + let constitution = Constitution::new(&anchor); + let proposal = NewConstitutionAction::new(&constitution); + + let proposal_wrapped = GovernanceAction::new_new_constitution_action(&proposal); + + to_from_test!(NewConstitutionAction, proposal, proposal_wrapped); + assert_eq!( + proposal, + proposal_wrapped.as_new_constitution_action().unwrap() + ); +} + +#[test] +fn new_constitution_action_with_action_id_ser_round_trip() { + let anchor = Anchor::new( + &URL::new("https://iohk.io".to_string()).unwrap(), + &fake_anchor_data_hash(1), + ); + + let action_id = GovernanceActionId::new(&fake_tx_hash(1), 0); + let constitution = Constitution::new(&anchor); + let proposal = NewConstitutionAction::new_with_action_id(&action_id, &constitution); + + let proposal_wrapped = GovernanceAction::new_new_constitution_action(&proposal); + + to_from_test!(NewConstitutionAction, proposal, proposal_wrapped); + assert_eq!( + proposal, + proposal_wrapped.as_new_constitution_action().unwrap() + ); +} + +#[test] +fn no_confidence_action_ser_round_trip() { + let proposal = NoConfidenceAction::new(); + + let proposal_wrapped = GovernanceAction::new_no_confidence_action(&proposal); + + to_from_test!(NoConfidenceAction, proposal, proposal_wrapped); + assert_eq!( + proposal, + proposal_wrapped.as_no_confidence_action().unwrap() + ); +} + +#[test] +fn no_confidence_action_with_action_id_ser_round_trip() { + let action_id = GovernanceActionId::new(&fake_tx_hash(1), 0); + let proposal = NoConfidenceAction::new_with_action_id(&action_id); + + let proposal_wrapped = GovernanceAction::new_no_confidence_action(&proposal); + + to_from_test!(NoConfidenceAction, proposal, proposal_wrapped); + assert_eq!( + proposal, + proposal_wrapped.as_no_confidence_action().unwrap() + ); +} + +#[test] +fn parameter_change_action_ser_round_trip() { + let parameters_update = fake_full_protocol_param_update(); + let proposal = ParameterChangeAction::new(¶meters_update); + let proposal_wrapped = GovernanceAction::new_parameter_change_action(&proposal); + to_from_test!(ParameterChangeAction, proposal, proposal_wrapped); + assert_eq!( + proposal, + proposal_wrapped.as_parameter_change_action().unwrap() + ); +} + +#[test] +fn parameter_change_action_with_action_id_ser_round_trip() { + let action_id = GovernanceActionId::new(&fake_tx_hash(1), 0); + let parameters_update = fake_full_protocol_param_update(); + let proposal = ParameterChangeAction::new_with_action_id(&action_id, ¶meters_update); + let proposal_wrapped = GovernanceAction::new_parameter_change_action(&proposal); + to_from_test!(ParameterChangeAction, proposal, proposal_wrapped); + assert_eq!( + proposal, + proposal_wrapped.as_parameter_change_action().unwrap() + ); +} + +#[test] +fn parameter_change_action_with_script_hash() { + let parameters_update = fake_full_protocol_param_update(); + let script_hash = ScriptHash::from(fake_script_hash(1)); + let proposal = ParameterChangeAction::new_with_policy_hash(¶meters_update, &script_hash); + let proposal_wrapped = GovernanceAction::new_parameter_change_action(&proposal); + to_from_test!(ParameterChangeAction, proposal, proposal_wrapped); + assert_eq!( + proposal, + proposal_wrapped.as_parameter_change_action().unwrap() + ); +} + +#[test] +fn treasury_withdrawals_ser_round_trip() { + let mut withdrawals = TreasuryWithdrawals::new(); + let addr1 = RewardAddress::new(1, &Credential::from_keyhash(&fake_key_hash(1))); + let addr2 = RewardAddress::new(2, &Credential::from_keyhash(&fake_key_hash(2))); + withdrawals.insert(&addr1, &Coin::from(1u32)); + withdrawals.insert(&addr2, &Coin::from(2u32)); + + let json = withdrawals.to_json().unwrap(); + + assert_eq!(withdrawals, TreasuryWithdrawals::from_json(&json).unwrap()); +} + +#[test] +fn treasury_withdrawals_action_ser_round_trip() { + let mut withdrawals = TreasuryWithdrawals::new(); + let addr1 = RewardAddress::new(1, &Credential::from_keyhash(&fake_key_hash(1))); + let addr2 = RewardAddress::new(2, &Credential::from_keyhash(&fake_key_hash(2))); + withdrawals.insert(&addr1, &Coin::from(1u32)); + withdrawals.insert(&addr2, &Coin::from(2u32)); + + let proposal = TreasuryWithdrawalsAction::new(&withdrawals); + + let proposal_wrapped = GovernanceAction::new_treasury_withdrawals_action(&proposal); + + to_from_test!(TreasuryWithdrawalsAction, proposal, proposal_wrapped); + assert_eq!( + proposal, + proposal_wrapped.as_treasury_withdrawals_action().unwrap() + ); +} + +#[test] +fn treasury_withdrawals_action_with_script_hash_ser_round_trip() { + let mut withdrawals = TreasuryWithdrawals::new(); + let addr1 = RewardAddress::new(1, &Credential::from_keyhash(&fake_key_hash(1))); + let addr2 = RewardAddress::new(2, &Credential::from_keyhash(&fake_key_hash(2))); + withdrawals.insert(&addr1, &Coin::from(1u32)); + withdrawals.insert(&addr2, &Coin::from(2u32)); + + let script_hash = ScriptHash::from(fake_script_hash(1)); + let proposal = TreasuryWithdrawalsAction::new_with_policy_hash(&withdrawals, &script_hash); + + let proposal_wrapped = GovernanceAction::new_treasury_withdrawals_action(&proposal); + + assert_eq!(proposal.policy_hash(), Some(script_hash)); + assert_eq!(proposal.withdrawals(), withdrawals); + + to_from_test!(TreasuryWithdrawalsAction, proposal, proposal_wrapped); + assert_eq!( + proposal, + proposal_wrapped.as_treasury_withdrawals_action().unwrap() + ); +} + +#[test] +fn voting_proposals_ser_round_trip() { + let mut proposals = VotingProposals::new(); + let mut withdrawals = TreasuryWithdrawals::new(); + let addr1 = RewardAddress::new(1, &Credential::from_keyhash(&fake_key_hash(1))); + let addr2 = RewardAddress::new(2, &Credential::from_keyhash(&fake_key_hash(2))); + withdrawals.insert(&addr1, &Coin::from(1u32)); + withdrawals.insert(&addr2, &Coin::from(2u32)); + + let action1 = GovernanceAction::new_treasury_withdrawals_action( + &TreasuryWithdrawalsAction::new(&withdrawals), + ); + let action2 = GovernanceAction::new_no_confidence_action(&NoConfidenceAction::new()); + let action3 = GovernanceAction::new_info_action(&InfoAction::new()); + + let proposal1 = VotingProposal::new( + &action1, + &fake_anchor(), + &fake_reward_address(1), + &Coin::from(100u32), + ); + let proposal2 = VotingProposal::new( + &action2, + &fake_anchor(), + &fake_reward_address(2), + &Coin::from(200u32), + ); + let proposal3 = VotingProposal::new( + &action3, + &fake_anchor(), + &fake_reward_address(3), + &Coin::from(300u32), + ); + + proposals.add(&proposal1); + proposals.add(&proposal2); + proposals.add(&proposal3); + + let cbor = proposals.to_bytes(); + let cbor_hex = proposals.to_hex(); + let json = proposals.to_json().unwrap(); + + assert_eq!(proposals, VotingProposals::from_bytes(cbor).unwrap()); + assert_eq!(proposals, VotingProposals::from_hex(&cbor_hex).unwrap()); + assert_eq!(proposals, VotingProposals::from_json(&json).unwrap()); +} + +#[test] +fn voting_proposal_round_trip_test() +{ + let mut withdrawals = TreasuryWithdrawals::new(); + let addr1 = RewardAddress::new(1, &Credential::from_keyhash(&fake_key_hash(1))); + let addr2 = RewardAddress::new(2, &Credential::from_keyhash(&fake_key_hash(2))); + withdrawals.insert(&addr1, &Coin::from(1u32)); + withdrawals.insert(&addr2, &Coin::from(2u32)); + + let action1 = GovernanceAction::new_treasury_withdrawals_action( + &TreasuryWithdrawalsAction::new(&withdrawals), + ); + + let proposal = VotingProposal::new( + &action1, + &fake_anchor(), + &fake_reward_address(1), + &Coin::from(100u32), + ); + + let cbor = proposal.to_bytes(); + let cbor_hex = proposal.to_hex(); + let json = proposal.to_json().unwrap(); + + assert_eq!(proposal, VotingProposal::from_bytes(cbor).unwrap()); + assert_eq!(proposal, VotingProposal::from_hex(&cbor_hex).unwrap()); + assert_eq!(proposal, VotingProposal::from_json(&json).unwrap()); +} + +#[test] +fn tx_with_voting_proposal_deser_test() { + let cbor = "84a40081825820017b91576a79a3602a02a65b600665ab71037ad14aa162538a26e64b3f5069fc000181a2005839002d745f050a8f7e263f4d0749a82284ed9cc065018c1f4f6a7c1b764882293a49e3ef29a4f9c32e4c18f202f5324182db7790f48dccf7a6dd011b0000000253d1efbc021a0002b3b11481841a000f4240581de082293a49e3ef29a4f9c32e4c18f202f5324182db7790f48dccf7a6dd8305f68282781968747470733a2f2f73686f727475726c2e61742f6173494a365820ee90ece16c47bf812b88edb89a01539e6683d6549a80b15383a4fb218ab9412df682781968747470733a2f2f73686f727475726c2e61742f784d53313558206f890de0c6e418e6526e2b1aa821850cb87aee94a6d77dc2a2e440116abc8e09a0f5f6"; + let tx_deser = Transaction::from_hex(cbor); + assert!(tx_deser.is_ok()); + + let proposals = tx_deser.unwrap().body().voting_proposals(); + assert!(proposals.is_some()); + let proposal = proposals.unwrap().get(0); + let expected_coin = Coin::from(1000000u32); + assert_eq!(proposal.deposit(), expected_coin); +} + +#[test] +fn tx_with_info_proposal_deser_test() { + let cbor = "84a40081825820f83bdffcbc203eec54dc71208aa7974c538414898673cd7af900149e8c8e392b0001818258390030a33756d8cbf4d18ce8c9995feca1ea1fc70093943c17bd96d65fed0aed6caa1cfe93f03f6ef1d9701df8024494d0b3b8a53a1ee37c5ab21b0000000253cd778c021a0002a75114818400581de00aed6caa1cfe93f03f6ef1d9701df8024494d0b3b8a53a1ee37c5ab2810682781868747470733a2f2f73686f727475726c2e61742f7279616e582013b0234dab754774e4530a0918d8272491541a8d2f6cf8ab0a10abdaa81f2440a10081825820684cb4218cb7e943e5f728ec09ed7f9486b6c164f332312c095067e21db9592b5840a3294bdea8fd49c8e7bd965d02b37033285db1907d1fab13cce281686cae7b23ee7c8aa534f229aade6b0bacfd71a518a24aeb73d08d879aaaee14aa16abf30af5f6"; + let tx_deser = Transaction::from_hex(cbor); + assert!(tx_deser.is_ok()); + + let proposals = tx_deser.unwrap().body().voting_proposals(); + assert!(proposals.is_some()); + let proposal = proposals.unwrap().get(0); + let expected_coin = Coin::zero(); + assert_eq!(proposal.deposit(), expected_coin); + + let info = proposal.governance_action().as_info_action(); + assert!(info.is_some()); +} \ No newline at end of file diff --git a/rust/src/tests/serialization/mod.rs b/rust/src/tests/serialization/mod.rs new file mode 100644 index 00000000..b7b9a424 --- /dev/null +++ b/rust/src/tests/serialization/mod.rs @@ -0,0 +1,5 @@ +pub mod certificates; +pub mod governance; +pub mod transaction_body; +pub mod protocol_param_update; +pub mod general; diff --git a/rust/src/tests/serialization/protocol_param_update.rs b/rust/src/tests/serialization/protocol_param_update.rs new file mode 100644 index 00000000..cfc4daec --- /dev/null +++ b/rust/src/tests/serialization/protocol_param_update.rs @@ -0,0 +1,122 @@ +use crate::*; +use crate::tests::fakes::{fake_cost_models, fake_drep_voting_thresholds, fake_pool_voting_thresholds}; + +#[test] +fn protocol_param_update_ser_round_trip() { + let pp_update= ProtocolParamUpdate { + minfee_a: Some(Coin::from(1_444u32)), + minfee_b: Some(Coin::from(2_444u32)), + max_block_body_size: Some(3_444u32), + max_tx_size: Some(4_444u32), + max_block_header_size: Some(5_444u32), + key_deposit: Some(Coin::from(6_444u32)), + pool_deposit: Some(Coin::from(7_444u32)), + max_epoch: Some(8_444u32), + n_opt: Some(9_444u32), + pool_pledge_influence: Some(UnitInterval::new( + &BigNum::from(10_444u32), + &BigNum::from(11_444u32), + )), + expansion_rate: Some(UnitInterval::new( + &BigNum::from(12_444u32), + &BigNum::from(13_444u32), + )), + treasury_growth_rate: Some(UnitInterval::new( + &BigNum::from(14_444u32), + &BigNum::from(15_444u32), + )), + d: Some(UnitInterval::new( + &BigNum::from(16_444u32), + &BigNum::from(17_444u32), + )), + extra_entropy: Some(Nonce::new_identity()), + protocol_version: Some(ProtocolVersion::new(1, 2)), + min_pool_cost: Some(Coin::from(18_444u32)), + ada_per_utxo_byte: Some(Coin::from(19_444u32)), + cost_models: Some(fake_cost_models()), + execution_costs: Some(ExUnitPrices::new( + &SubCoin::new(&BigNum(577), &BigNum(10000)), + &SubCoin::new(&BigNum(721), &BigNum(10000000)), + )), + max_tx_ex_units: Some(ExUnits::new(&BigNum(842996), &BigNum(246100241))), + max_block_ex_units: Some(ExUnits::new(&BigNum(842996), &BigNum(246100241))), + max_value_size: Some(20_444u32), + collateral_percentage: Some(21_444u32), + max_collateral_inputs: Some(22_444u32), + pool_voting_thresholds: Some(fake_pool_voting_thresholds()), + drep_voting_thresholds: Some(fake_drep_voting_thresholds()), + min_committee_size: Some(23_444u32), + committee_term_limit: Some(24_444u32), + governance_action_validity_period: Some(25_444u32), + governance_action_deposit: Some(Coin::from(26_444u32)), + drep_deposit: Some(Coin::from(27_444u32)), + drep_inactivity_period: Some(28_444u32), + ref_script_coins_per_byte: Some(UnitInterval::new( + &BigNum::from(29_444u32), + &BigNum::from(30_444u32), + )), + }; + + let cbor = pp_update.to_bytes(); + let hex = pp_update.to_hex(); + let json = pp_update.to_json().unwrap(); + + let pp_update_from_cbor = ProtocolParamUpdate::from_bytes(cbor).unwrap(); + let pp_update_from_hex = ProtocolParamUpdate::from_hex(&hex).unwrap(); + let pp_update_from_json = ProtocolParamUpdate::from_json(&json).unwrap(); + + assert_eq!(pp_update, pp_update_from_cbor); + assert_eq!(pp_update, pp_update_from_hex); + assert_eq!(pp_update, pp_update_from_json); +} + +#[test] +fn pool_voting_thresholds_ser_round_trip() { + let thresholds = PoolVotingThresholds::new( + &UnitInterval::new(&BigNum::from(44_401u32), &BigNum::from(44_402u32)), + &UnitInterval::new(&BigNum::from(44_403u32), &BigNum::from(44_404u32)), + &UnitInterval::new(&BigNum::from(44_405u32), &BigNum::from(44_406u32)), + &UnitInterval::new(&BigNum::from(44_406u32), &BigNum::from(44_407u32)), + &UnitInterval::new(&BigNum::from(44_408u32), &BigNum::from(44_409u32)), + ); + + let cbor = thresholds.to_bytes(); + let hex = thresholds.to_hex(); + let json = thresholds.to_json().unwrap(); + + let thresholds_from_cbor = PoolVotingThresholds::from_bytes(cbor).unwrap(); + let thresholds_from_hex = PoolVotingThresholds::from_hex(&hex).unwrap(); + let thresholds_from_json = PoolVotingThresholds::from_json(&json).unwrap(); + + assert_eq!(thresholds, thresholds_from_cbor); + assert_eq!(thresholds, thresholds_from_hex); + assert_eq!(thresholds, thresholds_from_json); +} + +#[test] +fn drep_voting_thresholds_ser_round_trip() { + let thresholds = DRepVotingThresholds::new( + &UnitInterval::new(&BigNum::from(44_401u32), &BigNum::from(44_402u32)), + &UnitInterval::new(&BigNum::from(44_403u32), &BigNum::from(44_404u32)), + &UnitInterval::new(&BigNum::from(44_405u32), &BigNum::from(44_406u32)), + &UnitInterval::new(&BigNum::from(44_406u32), &BigNum::from(44_407u32)), + &UnitInterval::new(&BigNum::from(44_408u32), &BigNum::from(44_409u32)), + &UnitInterval::new(&BigNum::from(44_410u32), &BigNum::from(44_411u32)), + &UnitInterval::new(&BigNum::from(44_412u32), &BigNum::from(44_412u32)), + &UnitInterval::new(&BigNum::from(44_414u32), &BigNum::from(44_415u32)), + &UnitInterval::new(&BigNum::from(44_416u32), &BigNum::from(44_417u32)), + &UnitInterval::new(&BigNum::from(44_418u32), &BigNum::from(44_419u32)), + ); + + let cbor = thresholds.to_bytes(); + let hex = thresholds.to_hex(); + let json = thresholds.to_json().unwrap(); + + let thresholds_from_cbor = DRepVotingThresholds::from_bytes(cbor).unwrap(); + let thresholds_from_hex = DRepVotingThresholds::from_hex(&hex).unwrap(); + let thresholds_from_json = DRepVotingThresholds::from_json(&json).unwrap(); + + assert_eq!(thresholds, thresholds_from_cbor); + assert_eq!(thresholds, thresholds_from_hex); + assert_eq!(thresholds, thresholds_from_json); +} \ No newline at end of file diff --git a/rust/src/tests/serialization/transaction_body.rs b/rust/src/tests/serialization/transaction_body.rs new file mode 100644 index 00000000..827a7b96 --- /dev/null +++ b/rust/src/tests/serialization/transaction_body.rs @@ -0,0 +1,86 @@ +use crate::*; +use crate::tests::fakes::{fake_anchor, fake_asset_name, fake_auxiliary_data_hash, fake_base_address, fake_key_hash, fake_policy_id, fake_reward_address, fake_script_data_hash, fake_tx_hash, fake_tx_input}; + +#[test] +fn transaction_round_trip_test() { + let input = fake_tx_input(1); + let output = TransactionOutput::new(&fake_base_address(2), &Value::new(&BigNum(1_000_001))); + let inputs = TransactionInputs::from_vec(vec![input]); + let outputs = TransactionOutputs(vec![output]); + let fee = Coin::from(1_000_002u64); + let mut body = TransactionBody::new_tx_body(&inputs, &outputs, &fee); + let mut mint = Mint::new(); + let mint_asset = + MintAssets::new_from_entry(&fake_asset_name(4), &Int::new(&BigNum(1_000_003u64))) + .unwrap(); + mint.insert(&fake_policy_id(3), &mint_asset); + + let mut req_signers = Ed25519KeyHashes::new(); + req_signers.add(&fake_key_hash(5)); + + let mut collateral_inputs = TransactionInputs::new(); + collateral_inputs.add(&fake_tx_input(6)); + + let mut ref_inputs = TransactionInputs::new(); + ref_inputs.add(&fake_tx_input(7)); + + let mut certs = Certificates::new(); + let stake_registration = StakeRegistration::new(&Credential::from_keyhash(&fake_key_hash(8))); + certs.add(&Certificate::new_stake_registration(&stake_registration)); + + let mut withdrawals = Withdrawals::new(); + withdrawals.insert( + &RewardAddress::new( + NetworkInfo::testnet_preprod().network_id(), + &Credential::from_keyhash(&fake_key_hash(9)), + ), + &Coin::from(1_000_010u64), + ); + + let mut voting_procedures = VotingProcedures::new(); + let voter = Voter::new_drep_credential(&Credential::from_keyhash(&fake_key_hash(1))); + let gov_action_id = GovernanceActionId::new(&fake_tx_hash(2), 0); + let procedure = VotingProcedure::new(VoteKind::Abstain); + voting_procedures.insert(&voter, &gov_action_id, &procedure); + + let mut voting_proposals = VotingProposals::new(); + let info_action = InfoAction::new(); + let action = GovernanceAction::new_info_action(&info_action); + let proposal = VotingProposal::new( + &action, + &fake_anchor(), + &fake_reward_address(3), + &Coin::from(1_000_011u64), + ); + voting_proposals.add(&proposal); + + body.set_ttl(&BigNum(1_000_003u64)); + body.set_certs(&certs); + body.set_withdrawals(&withdrawals); + body.set_update(&Update::new(&ProposedProtocolParameterUpdates::new(), 1)); + body.set_auxiliary_data_hash(&fake_auxiliary_data_hash(2)); + body.set_validity_start_interval_bignum(&SlotBigNum::from(1_000_004u64)); + body.set_mint(&mint); + body.set_reference_inputs(&ref_inputs); + body.set_script_data_hash(&fake_script_data_hash(3)); + body.set_collateral(&collateral_inputs); + body.set_required_signers(&req_signers); + body.set_network_id(&NetworkId::testnet()); + body.set_collateral_return(&TransactionOutput::new( + &fake_base_address(4), + &Value::new(&BigNum(1_000_005u64)), + )); + body.set_total_collateral(&Coin::from(1_000_006u64)); + body.set_voting_procedures(&voting_procedures); + body.set_voting_proposals(&voting_proposals); + body.set_donation(&Coin::from(1_000_007u64)); + body.set_current_treasury_value(&Coin::from(1_000_008u64)); + + let body_cbor = body.to_bytes(); + let body_hex_cbor = body.to_hex(); + let body_json = body.to_json().unwrap(); + + assert_eq!(TransactionBody::from_bytes(body_cbor).unwrap(), body); + assert_eq!(TransactionBody::from_hex(&body_hex_cbor).unwrap(), body); + assert_eq!(TransactionBody::from_json(&body_json).unwrap(), body); +} diff --git a/rust/src/tests/utils.rs b/rust/src/tests/utils.rs new file mode 100644 index 00000000..c8dabac1 --- /dev/null +++ b/rust/src/tests/utils.rs @@ -0,0 +1,753 @@ +use crate::*; + +#[test] +fn subtract_values() { + let policy1 = PolicyID::from([0; ScriptHash::BYTE_COUNT]); + let policy2 = PolicyID::from([1; ScriptHash::BYTE_COUNT]); + + let asset1 = AssetName(vec![1]); + let asset2 = AssetName(vec![2]); + let asset3 = AssetName(vec![3]); + let asset4 = AssetName(vec![4]); + + let mut token_bundle1 = MultiAsset::new(); + { + let mut asset_list1 = Assets::new(); + asset_list1.insert(&asset1, &BigNum(1)); + asset_list1.insert(&asset2, &BigNum(1)); + asset_list1.insert(&asset3, &BigNum(1)); + asset_list1.insert(&asset4, &BigNum(2)); + token_bundle1.insert(&policy1, &asset_list1); + + let mut asset_list2 = Assets::new(); + asset_list2.insert(&asset1, &BigNum(1)); + token_bundle1.insert(&policy2, &asset_list2); + } + let assets1 = Value { + coin: BigNum(1555554), + multiasset: Some(token_bundle1), + }; + + let mut token_bundle2 = MultiAsset::new(); + { + let mut asset_list2 = Assets::new(); + // more than asset1 bundle + asset_list2.insert(&asset1, &BigNum(2)); + // exactly equal to asset1 bundle + asset_list2.insert(&asset2, &BigNum(1)); + // skip asset 3 + // less than in asset1 bundle + asset_list2.insert(&asset4, &BigNum(1)); + token_bundle2.insert(&policy1, &asset_list2); + + // this policy should be removed entirely + let mut asset_list2 = Assets::new(); + asset_list2.insert(&asset1, &BigNum(1)); + token_bundle2.insert(&policy2, &asset_list2); + } + + let assets2 = Value { + coin: BigNum(2555554), + multiasset: Some(token_bundle2), + }; + + let result = assets1.clamped_sub(&assets2); + assert_eq!(result.coin().to_str(), "0"); + assert_eq!( + result.multiasset().unwrap().len(), + 1 // policy 2 was deleted successfully + ); + let policy1_content = result.multiasset().unwrap().get(&policy1).unwrap(); + assert_eq!(policy1_content.len(), 2); + assert_eq!(policy1_content.get(&asset3).unwrap().to_str(), "1"); + assert_eq!(policy1_content.get(&asset4).unwrap().to_str(), "1"); +} + +#[test] +fn compare_values() { + let policy1 = PolicyID::from([0; ScriptHash::BYTE_COUNT]); + + let asset1 = AssetName(vec![1]); + let asset2 = AssetName(vec![2]); + + // testing cases with no assets + { + let a = Value::new(&BigNum(1)); + let b = Value::new(&BigNum(1)); + assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Equal); + } + { + let a = Value::new(&BigNum(2)); + let b = Value::new(&BigNum(1)); + assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Greater); + } + { + let a = Value::new(&BigNum(1)); + let b = Value::new(&BigNum(2)); + assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Less); + } + // testing case where one side has assets + { + let mut token_bundle1 = MultiAsset::new(); + let mut asset_list1 = Assets::new(); + asset_list1.insert(&asset1, &BigNum(1)); + token_bundle1.insert(&policy1, &asset_list1); + let a = Value { + coin: BigNum(1), + multiasset: Some(token_bundle1), + }; + let b = Value::new(&BigNum(1)); + assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Greater); + } + { + let mut token_bundle1 = MultiAsset::new(); + let mut asset_list1 = Assets::new(); + asset_list1.insert(&asset1, &BigNum(1)); + token_bundle1.insert(&policy1, &asset_list1); + let a = Value::new(&BigNum(1)); + let b = Value { + coin: BigNum(1), + multiasset: Some(token_bundle1), + }; + assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Less); + } + // testing case where both sides has assets + { + let mut token_bundle1 = MultiAsset::new(); + let mut asset_list1 = Assets::new(); + asset_list1.insert(&asset1, &BigNum(1)); + token_bundle1.insert(&policy1, &asset_list1); + let a = Value { + coin: BigNum(1), + multiasset: Some(token_bundle1), + }; + + let mut token_bundle2 = MultiAsset::new(); + let mut asset_list2 = Assets::new(); + asset_list2.insert(&asset1, &BigNum(1)); + token_bundle2.insert(&policy1, &asset_list2); + let b = Value { + coin: BigNum(1), + multiasset: Some(token_bundle2), + }; + assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Equal); + } + { + let mut token_bundle1 = MultiAsset::new(); + let mut asset_list1 = Assets::new(); + asset_list1.insert(&asset1, &BigNum(1)); + token_bundle1.insert(&policy1, &asset_list1); + let a = Value { + coin: BigNum(2), + multiasset: Some(token_bundle1), + }; + + let mut token_bundle2 = MultiAsset::new(); + let mut asset_list2 = Assets::new(); + asset_list2.insert(&asset1, &BigNum(1)); + token_bundle2.insert(&policy1, &asset_list2); + let b = Value { + coin: BigNum(1), + multiasset: Some(token_bundle2), + }; + assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Greater); + } + { + let mut token_bundle1 = MultiAsset::new(); + let mut asset_list1 = Assets::new(); + asset_list1.insert(&asset1, &BigNum(1)); + token_bundle1.insert(&policy1, &asset_list1); + let a = Value { + coin: BigNum(1), + multiasset: Some(token_bundle1), + }; + + let mut token_bundle2 = MultiAsset::new(); + let mut asset_list2 = Assets::new(); + asset_list2.insert(&asset1, &BigNum(1)); + token_bundle2.insert(&policy1, &asset_list2); + let b = Value { + coin: BigNum(2), + multiasset: Some(token_bundle2), + }; + assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Less); + } + { + let mut token_bundle1 = MultiAsset::new(); + let mut asset_list1 = Assets::new(); + asset_list1.insert(&asset1, &BigNum(2)); + token_bundle1.insert(&policy1, &asset_list1); + let a = Value { + coin: BigNum(1), + multiasset: Some(token_bundle1), + }; + + let mut token_bundle2 = MultiAsset::new(); + let mut asset_list2 = Assets::new(); + asset_list2.insert(&asset1, &BigNum(1)); + token_bundle2.insert(&policy1, &asset_list2); + let b = Value { + coin: BigNum(1), + multiasset: Some(token_bundle2), + }; + assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Greater); + } + { + let mut token_bundle1 = MultiAsset::new(); + let mut asset_list1 = Assets::new(); + asset_list1.insert(&asset1, &BigNum(2)); + token_bundle1.insert(&policy1, &asset_list1); + let a = Value { + coin: BigNum(2), + multiasset: Some(token_bundle1), + }; + + let mut token_bundle2 = MultiAsset::new(); + let mut asset_list2 = Assets::new(); + asset_list2.insert(&asset1, &BigNum(1)); + token_bundle2.insert(&policy1, &asset_list2); + let b = Value { + coin: BigNum(1), + multiasset: Some(token_bundle2), + }; + assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Greater); + } + { + let mut token_bundle1 = MultiAsset::new(); + let mut asset_list1 = Assets::new(); + asset_list1.insert(&asset1, &BigNum(2)); + token_bundle1.insert(&policy1, &asset_list1); + let a = Value { + coin: BigNum(1), + multiasset: Some(token_bundle1), + }; + + let mut token_bundle2 = MultiAsset::new(); + let mut asset_list2 = Assets::new(); + asset_list2.insert(&asset1, &BigNum(1)); + token_bundle2.insert(&policy1, &asset_list2); + let b = Value { + coin: BigNum(2), + multiasset: Some(token_bundle2), + }; + assert_eq!(a.partial_cmp(&b), None); + } + { + let mut token_bundle1 = MultiAsset::new(); + let mut asset_list1 = Assets::new(); + asset_list1.insert(&asset1, &BigNum(1)); + token_bundle1.insert(&policy1, &asset_list1); + let a = Value { + coin: BigNum(1), + multiasset: Some(token_bundle1), + }; + + let mut token_bundle2 = MultiAsset::new(); + let mut asset_list2 = Assets::new(); + asset_list2.insert(&asset1, &BigNum(2)); + token_bundle2.insert(&policy1, &asset_list2); + let b = Value { + coin: BigNum(1), + multiasset: Some(token_bundle2), + }; + assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Less); + } + { + let mut token_bundle1 = MultiAsset::new(); + let mut asset_list1 = Assets::new(); + asset_list1.insert(&asset1, &BigNum(1)); + token_bundle1.insert(&policy1, &asset_list1); + let a = Value { + coin: BigNum(1), + multiasset: Some(token_bundle1), + }; + + let mut token_bundle2 = MultiAsset::new(); + let mut asset_list2 = Assets::new(); + asset_list2.insert(&asset1, &BigNum(2)); + token_bundle2.insert(&policy1, &asset_list2); + let b = Value { + coin: BigNum(2), + multiasset: Some(token_bundle2), + }; + assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Less); + } + { + let mut token_bundle1 = MultiAsset::new(); + let mut asset_list1 = Assets::new(); + asset_list1.insert(&asset1, &BigNum(1)); + token_bundle1.insert(&policy1, &asset_list1); + let a = Value { + coin: BigNum(2), + multiasset: Some(token_bundle1), + }; + + let mut token_bundle2 = MultiAsset::new(); + let mut asset_list2 = Assets::new(); + asset_list2.insert(&asset1, &BigNum(2)); + token_bundle2.insert(&policy1, &asset_list2); + let b = Value { + coin: BigNum(1), + multiasset: Some(token_bundle2), + }; + assert_eq!(a.partial_cmp(&b), None); + } + { + let mut token_bundle1 = MultiAsset::new(); + let mut asset_list1 = Assets::new(); + asset_list1.insert(&asset1, &BigNum(1)); + token_bundle1.insert(&policy1, &asset_list1); + let a = Value { + coin: BigNum(1), + multiasset: Some(token_bundle1), + }; + + let mut token_bundle2 = MultiAsset::new(); + let mut asset_list2 = Assets::new(); + asset_list2.insert(&asset2, &BigNum(1)); + token_bundle2.insert(&policy1, &asset_list2); + let b = Value { + coin: BigNum(1), + multiasset: Some(token_bundle2), + }; + assert_eq!(a.partial_cmp(&b), None); + } +} + +#[test] +fn bigint_serialization() { + let zero = BigInt::from_str("0").unwrap(); + let zero_rt = BigInt::from_bytes(zero.to_bytes()).unwrap(); + assert_eq!(zero.to_str(), zero_rt.to_str()); + assert_eq!(zero.to_bytes(), vec![0x00]); + + let pos_small = BigInt::from_str("100").unwrap(); + let pos_small_rt = BigInt::from_bytes(pos_small.to_bytes()).unwrap(); + assert_eq!(pos_small.to_str(), pos_small_rt.to_str()); + + let pos_big = BigInt::from_str("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890").unwrap(); + let pos_big_rt = BigInt::from_bytes(pos_big.to_bytes()).unwrap(); + assert_eq!(pos_big.to_str(), pos_big_rt.to_str()); + + let neg_small = BigInt::from_str("-100").unwrap(); + let neg_small_rt = BigInt::from_bytes(neg_small.to_bytes()).unwrap(); + assert_eq!(neg_small.to_str(), neg_small_rt.to_str()); + + let neg_big = BigInt::from_str("-123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890").unwrap(); + let neg_big_rt = BigInt::from_bytes(neg_big.to_bytes()).unwrap(); + assert_eq!(neg_big.to_str(), neg_big_rt.to_str()); + + // taken from CBOR RFC examples + // negative big int + assert_eq!( + hex::decode("c349010000000000000000").unwrap(), + BigInt::from_str("-18446744073709551617") + .unwrap() + .to_bytes() + ); + // positive big int + assert_eq!( + hex::decode("c249010000000000000000").unwrap(), + BigInt::from_str("18446744073709551616").unwrap().to_bytes() + ); + // uint + assert_eq!( + hex::decode("1b000000e8d4a51000").unwrap(), + BigInt::from_str("1000000000000").unwrap().to_bytes() + ); + // nint (lowest possible - used to be unsupported but works now) + assert_eq!( + hex::decode("3bffffffffffffffff").unwrap(), + BigInt::from_str("-18446744073709551616") + .unwrap() + .to_bytes() + ); + // this one fits in an i64 though + assert_eq!( + hex::decode("3903e7").unwrap(), + BigInt::from_str("-1000").unwrap().to_bytes() + ); + + let x = BigInt::from_str("-18446744073709551617").unwrap(); + let x_rt = BigInt::from_bytes(x.to_bytes()).unwrap(); + assert_eq!(x.to_str(), x_rt.to_str()); +} + +#[test] +fn bounded_bytes_read_chunked() { + use std::io::Cursor; + let chunks = vec![ + vec![ + 0x52, 0x73, 0x6F, 0x6D, 0x65, 0x20, 0x72, 0x61, 0x6E, 0x64, 0x6F, 0x6D, 0x20, 0x73, + 0x74, 0x72, 0x69, 0x6E, 0x67, + ], + vec![0x44, 0x01, 0x02, 0x03, 0x04], + ]; + let mut expected = Vec::new(); + for chunk in chunks.iter() { + expected.extend_from_slice(&chunk[1..]); + } + let mut vec = vec![0x5f]; + for mut chunk in chunks { + vec.append(&mut chunk); + } + vec.push(0xff); + let mut raw = Deserializer::from(Cursor::new(vec.clone())); + let found = read_bounded_bytes(&mut raw).unwrap(); + assert_eq!(found, expected); +} + +#[test] +fn bounded_bytes_write_chunked() { + let mut chunk_64 = vec![0x58, crate::utils::BOUNDED_BYTES_CHUNK_SIZE as u8]; + chunk_64.extend(std::iter::repeat(37).take(crate::utils::BOUNDED_BYTES_CHUNK_SIZE)); + let chunks = vec![chunk_64, vec![0x44, 0x01, 0x02, 0x03, 0x04]]; + let mut input = Vec::new(); + input.extend_from_slice(&chunks[0][2..]); + input.extend_from_slice(&chunks[1][1..]); + let mut serializer = cbor_event::se::Serializer::new_vec(); + write_bounded_bytes(&mut serializer, &input).unwrap(); + let written = serializer.finalize(); + let mut expected = vec![0x5f]; + for mut chunk in chunks { + expected.append(&mut chunk); + } + expected.push(0xff); + assert_eq!(expected, written); +} + +#[test] +fn correct_script_data_hash() { + let mut datums = PlutusList::new(); + datums.add(&PlutusData::new_integer(&BigInt::from_str("1000").unwrap())); + let mut redeemers = Redeemers::new(); + redeemers.add(&Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum::from_str("1").unwrap(), + &PlutusData::new_integer(&BigInt::from_str("2000").unwrap()), + &ExUnits::new( + &BigNum::from_str("0").unwrap(), + &BigNum::from_str("0").unwrap(), + ), + )); + let plutus_cost_model = CostModel::from_bytes(vec![ + 159, 26, 0, 3, 2, 89, 0, 1, 1, 26, 0, 6, 11, 199, 25, 2, 109, 0, 1, 26, 0, 2, 73, 240, 25, + 3, 232, 0, 1, 26, 0, 2, 73, 240, 24, 32, 26, 0, 37, 206, 168, 25, 113, 247, 4, 25, 116, 77, + 24, 100, 25, 116, 77, 24, 100, 25, 116, 77, 24, 100, 25, 116, 77, 24, 100, 25, 116, 77, 24, + 100, 25, 116, 77, 24, 100, 24, 100, 24, 100, 25, 116, 77, 24, 100, 26, 0, 2, 73, 240, 24, + 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 25, 3, 232, 0, + 1, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 25, 3, 232, 0, 8, 26, 0, 2, 66, 32, 26, 0, + 6, 126, 35, 24, 118, 0, 1, 1, 26, 0, 2, 73, 240, 25, 3, 232, 0, 8, 26, 0, 2, 73, 240, 26, + 0, 1, 183, 152, 24, 247, 1, 26, 0, 2, 73, 240, 25, 39, 16, 1, 26, 0, 2, 21, 94, 25, 5, 46, + 1, 25, 3, 232, 26, 0, 2, 73, 240, 25, 3, 232, 1, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, + 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 1, 1, 26, 0, 2, 73, 240, 1, 26, 0, 2, 73, 240, 4, + 26, 0, 1, 148, 175, 24, 248, 1, 26, 0, 1, 148, 175, 24, 248, 1, 26, 0, 2, 55, 124, 25, 5, + 86, 1, 26, 0, 2, 189, 234, 25, 1, 241, 1, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, + 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, + 2, 73, 240, 24, 32, 26, 0, 2, 66, 32, 26, 0, 6, 126, 35, 24, 118, 0, 1, 1, 25, 240, 76, 25, + 43, 210, 0, 1, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 66, 32, 26, 0, 6, 126, 35, 24, 118, 0, + 1, 1, 26, 0, 2, 66, 32, 26, 0, 6, 126, 35, 24, 118, 0, 1, 1, 26, 0, 37, 206, 168, 25, 113, + 247, 4, 0, 26, 0, 1, 65, 187, 4, 26, 0, 2, 73, 240, 25, 19, 136, 0, 1, 26, 0, 2, 73, 240, + 24, 32, 26, 0, 3, 2, 89, 0, 1, 1, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, + 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, + 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 51, 13, 167, 1, 1, 255, + ]) + .unwrap(); + let mut cost_models = Costmdls::new(); + cost_models.insert(&Language::new_plutus_v1(), &plutus_cost_model); + let script_data_hash = hash_script_data(&redeemers, &cost_models, Some(datums)); + + assert_eq!( + hex::encode(script_data_hash.to_bytes()), + "4415e6667e6d6bbd992af5092d48e3c2ba9825200d0234d2470068f7f0f178b3" + ); +} + +#[test] +fn native_scripts_from_wallet_json() { + let cosigner0_hex = "1423856bc91c49e928f6f30f4e8d665d53eb4ab6028bd0ac971809d514c92db11423856bc91c49e928f6f30f4e8d665d53eb4ab6028bd0ac971809d514c92db1"; + let cosigner1_hex = "a48d97f57ce49433f347d44ee07e54a100229b4f8e125d25f7bca9ad66d9707a25cd1331f46f7d6e279451637ca20802a25c441ba9436abf644fe5410d1080e3"; + let self_key_hex = "6ce83a12e9d4c783f54c0bb511303b37160a6e4f3f96b8e878a7c1f7751e18c4ccde3fb916d330d07f7bd51fb6bd99aa831d925008d3f7795033f48abd6df7f6"; + let native_script = encode_json_str_to_native_script( + &format!( + r#" + {{ + "cosigners": {{ + "cosigner#0": "{}", + "cosigner#1": "{}", + "cosigner#2": "self" + }}, + "template": {{ + "some": {{ + "at_least": 2, + "from": [ + {{ + "all": [ + "cosigner#0", + {{ "active_from": 120 }} + ] + }}, + {{ + "any": [ + "cosigner#1", + {{ "active_until": 1000 }} + ] + }}, + "cosigner#2" + ] + }} + }} + }}"#, + cosigner0_hex, cosigner1_hex + ), + self_key_hex, + ScriptSchema::Wallet, + ); + + let n_of_k = native_script.unwrap().as_script_n_of_k().unwrap(); + let from = n_of_k.native_scripts(); + assert_eq!(n_of_k.n(), 2); + assert_eq!(from.len(), 3); + let all = from.get(0).as_script_all().unwrap().native_scripts(); + assert_eq!(all.len(), 2); + let all_0 = all.get(0).as_script_pubkey().unwrap(); + assert_eq!( + all_0.addr_keyhash(), + Bip32PublicKey::from_bytes(&hex::decode(cosigner0_hex).unwrap()) + .unwrap() + .to_raw_key() + .hash() + ); + let all_1 = all.get(1).as_timelock_start().unwrap(); + assert_eq!(all_1.slot().unwrap(), 120); + let any = from.get(1).as_script_any().unwrap().native_scripts(); + assert_eq!(all.len(), 2); + let any_0 = any.get(0).as_script_pubkey().unwrap(); + assert_eq!( + any_0.addr_keyhash(), + Bip32PublicKey::from_bytes(&hex::decode(cosigner1_hex).unwrap()) + .unwrap() + .to_raw_key() + .hash() + ); + let any_1 = any.get(1).as_timelock_expiry().unwrap(); + assert_eq!(any_1.slot().unwrap(), 1000); + let self_key = from.get(2).as_script_pubkey().unwrap(); + assert_eq!( + self_key.addr_keyhash(), + Bip32PublicKey::from_bytes(&hex::decode(self_key_hex).unwrap()) + .unwrap() + .to_raw_key() + .hash() + ); +} + +#[test] +fn int_to_str() { + assert_eq!( + Int::new(&BigNum(u64::max_value())).to_str(), + u64::max_value().to_string() + ); + assert_eq!( + Int::new(&BigNum(u64::min_value())).to_str(), + u64::min_value().to_string() + ); + assert_eq!( + Int::new_negative(&BigNum(u64::max_value())).to_str(), + (-(u64::max_value() as i128)).to_string() + ); + assert_eq!( + Int::new_negative(&BigNum(u64::min_value())).to_str(), + (-(u64::min_value() as i128)).to_string() + ); + assert_eq!(Int::new_i32(142).to_str(), "142"); + assert_eq!(Int::new_i32(-142).to_str(), "-142"); +} + +#[test] +fn int_as_i32_or_nothing() { + let over_pos_i32 = (i32::max_value() as i64) + 1; + assert!(Int::new(&BigNum(over_pos_i32 as u64)) + .as_i32_or_nothing() + .is_none()); + + let valid_pos_i32 = i32::max_value() as i64; + assert_eq!( + Int::new(&BigNum(valid_pos_i32 as u64)) + .as_i32_or_nothing() + .unwrap(), + i32::max_value() + ); + + let over_neg_i32 = (i32::min_value() as i64) - 1; + assert!(Int::new_negative(&BigNum((-over_neg_i32) as u64)) + .as_i32_or_nothing() + .is_none()); + + let valid_neg_i32 = i32::min_value() as i64; + assert_eq!( + Int::new_negative(&BigNum((-valid_neg_i32) as u64)) + .as_i32_or_nothing() + .unwrap(), + i32::min_value() + ); + + assert!(Int::new(&BigNum(u64::max_value())) + .as_i32_or_nothing() + .is_none()); + assert_eq!( + Int::new(&BigNum(i32::max_value() as u64)) + .as_i32_or_nothing() + .unwrap(), + i32::max_value() + ); + assert_eq!( + Int::new_negative(&BigNum(i32::max_value() as u64)) + .as_i32_or_nothing() + .unwrap(), + -i32::max_value() + ); + + assert_eq!(Int::new_i32(42).as_i32_or_nothing().unwrap(), 42); + assert_eq!(Int::new_i32(-42).as_i32_or_nothing().unwrap(), -42); +} + +#[test] +fn int_as_i32_or_fail() { + let over_pos_i32 = (i32::max_value() as i64) + 1; + assert!(Int::new(&BigNum(over_pos_i32 as u64)) + .as_i32_or_fail() + .is_err()); + + let valid_pos_i32 = i32::max_value() as i64; + assert_eq!( + Int::new(&BigNum(valid_pos_i32 as u64)) + .as_i32_or_fail() + .unwrap(), + i32::max_value() + ); + + let over_neg_i32 = (i32::min_value() as i64) - 1; + assert!(Int::new_negative(&BigNum((-over_neg_i32) as u64)) + .as_i32_or_fail() + .is_err()); + + let valid_neg_i32 = i32::min_value() as i64; + assert_eq!( + Int::new_negative(&BigNum((-valid_neg_i32) as u64)) + .as_i32_or_fail() + .unwrap(), + i32::min_value() + ); + + assert!(Int::new(&BigNum(u64::max_value())) + .as_i32_or_fail() + .is_err()); + assert_eq!( + Int::new(&BigNum(i32::max_value() as u64)) + .as_i32_or_fail() + .unwrap(), + i32::max_value() + ); + assert_eq!( + Int::new_negative(&BigNum(i32::max_value() as u64)) + .as_i32_or_fail() + .unwrap(), + -i32::max_value() + ); + + assert_eq!(Int::new_i32(42).as_i32_or_fail().unwrap(), 42); + assert_eq!(Int::new_i32(-42).as_i32_or_fail().unwrap(), -42); +} + +#[test] +fn int_full_range() { + // cbor_event's nint API worked via i64 but we now have a workaround for it + // so these tests are here to make sure that workaround works. + + // first nint below of i64::MIN + let bytes_x = vec![0x3b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + let x = Int::from_bytes(bytes_x.clone()).unwrap(); + assert_eq!(x.to_str(), "-9223372036854775809"); + assert_eq!(bytes_x, x.to_bytes()); + + // smallest possible nint which is -u64::MAX - 1 + let bytes_y = vec![0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; + let y = Int::from_bytes(bytes_y.clone()).unwrap(); + assert_eq!(y.to_str(), "-18446744073709551616"); + assert_eq!(bytes_y, y.to_bytes()); +} + +#[test] +fn test_bigint_add() { + assert_eq!(to_bigint(10).add(&to_bigint(20)), to_bigint(30),); + assert_eq!(to_bigint(500).add(&to_bigint(800)), to_bigint(1300),); +} + +#[test] +fn test_bigint_mul() { + assert_eq!(to_bigint(10).mul(&to_bigint(20)), to_bigint(200),); + assert_eq!(to_bigint(500).mul(&to_bigint(800)), to_bigint(400000),); + assert_eq!(to_bigint(12).mul(&to_bigint(22)), to_bigint(264),); +} + +#[test] +fn test_bigint_div_ceil() { + assert_eq!(to_bigint(20).div_ceil(&to_bigint(10)), to_bigint(2),); + assert_eq!(to_bigint(20).div_ceil(&to_bigint(2)), to_bigint(10),); + assert_eq!(to_bigint(21).div_ceil(&to_bigint(2)), to_bigint(11),); + assert_eq!(to_bigint(6).div_ceil(&to_bigint(3)), to_bigint(2),); + assert_eq!(to_bigint(5).div_ceil(&to_bigint(3)), to_bigint(2),); + assert_eq!(to_bigint(7).div_ceil(&to_bigint(3)), to_bigint(3),); +} + +#[test] +fn test_bignum_div() { + assert_eq!(BigNum(10).div_floor(&BigNum(1)), BigNum(10),); + assert_eq!(BigNum(10).div_floor(&BigNum(3)), BigNum(3),); + assert_eq!(BigNum(10).div_floor(&BigNum(4)), BigNum(2),); + assert_eq!(BigNum(10).div_floor(&BigNum(5)), BigNum(2),); + assert_eq!(BigNum(10).div_floor(&BigNum(6)), BigNum(1),); + assert_eq!(BigNum(10).div_floor(&BigNum(12)), BigNum::zero(),); +} + +#[test] +fn test_vasil_v1_costmodel_hashing() { + let v1 = Language::new_plutus_v1(); + let v1_cost_model = TxBuilderConstants::plutus_vasil_cost_models() + .get(&v1) + .unwrap(); + let mut costmodels = Costmdls::new(); + costmodels.insert(&v1, &v1_cost_model); + let hash = hash_script_data( + &Redeemers::from(vec![Redeemer::new( + &RedeemerTag::new_spend(), + &BigNum::zero(), + &PlutusData::new_integer(&BigInt::from_str("42").unwrap()), + &ExUnits::new(&BigNum(1700), &BigNum(368100)), + )]), + &costmodels, + Some(PlutusList::from(vec![PlutusData::new_integer( + &BigInt::from_str("42").unwrap(), + )])), + ); + assert_eq!( + hex::encode(hash.to_bytes()), + "887e1b6416d750d871c0f5b7136b54f7b8e8b0e293379d090f38f8f821d08a29" + ); +} + +#[test] +fn bigint_as_int() { + let zero = BigInt::from_str("0").unwrap(); + let zero_int = zero.as_int().unwrap(); + assert_eq!(zero_int.0, 0i128); + + let pos = BigInt::from_str("1024").unwrap(); + let pos_int = pos.as_int().unwrap(); + assert_eq!(pos_int.0, 1024i128); + + let neg = BigInt::from_str("-1024").unwrap(); + let neg_int = neg.as_int().unwrap(); + assert_eq!(neg_int.0, -1024i128); +} diff --git a/rust/src/traits.rs b/rust/src/traits.rs index 8a1b2815..e2ced435 100644 --- a/rust/src/traits.rs +++ b/rust/src/traits.rs @@ -2,6 +2,10 @@ pub trait NoneOrEmpty { fn is_none_or_empty(&self) -> bool; } +pub trait EmptyToNone: Sized { + fn empty_to_none(self) -> Option; +} + impl NoneOrEmpty for &T { fn is_none_or_empty(&self) -> bool { (*self).is_none_or_empty() @@ -10,6 +14,19 @@ impl NoneOrEmpty for &T { impl NoneOrEmpty for Option { fn is_none_or_empty(&self) -> bool { - self.is_none() || self.as_ref().unwrap().is_none_or_empty() + match &self { + Some(x) => x.is_none_or_empty(), + None => true, + } } } + +impl EmptyToNone for T { + fn empty_to_none(self) -> Option { + if self.is_none_or_empty() { + None + } else { + Some(self) + } + } +} \ No newline at end of file diff --git a/rust/src/tx_builder.rs b/rust/src/tx_builder.rs deleted file mode 100644 index 8cacee06..00000000 --- a/rust/src/tx_builder.rs +++ /dev/null @@ -1,8546 +0,0 @@ -#![allow(deprecated)] - -#[cfg(test)] -mod test_batch; - -pub mod tx_inputs_builder; -pub mod tx_batch_builder; -pub mod mint_builder; -pub mod certificates_builder; -pub mod withdrawals_builder; -mod batch_tools; -mod script_structs; - -use super::fees; -use super::output_builder::TransactionOutputAmountBuilder; -use super::utils; -use super::*; -use crate::tx_builder::tx_inputs_builder::{get_bootstraps, TxInputsBuilder}; -use linked_hash_map::LinkedHashMap; -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; -use crate::tx_builder::certificates_builder::CertificatesBuilder; -use crate::tx_builder::withdrawals_builder::WithdrawalsBuilder; -use crate::tx_builder::script_structs::{PlutusWitness, PlutusWitnesses}; -use crate::tx_builder::mint_builder::{MintBuilder, MintWitness}; - -pub(crate) fn fake_private_key() -> Bip32PrivateKey { - Bip32PrivateKey::from_bytes(&[ - 0xb8, 0xf2, 0xbe, 0xce, 0x9b, 0xdf, 0xe2, 0xb0, 0x28, 0x2f, 0x5b, 0xad, 0x70, 0x55, 0x62, - 0xac, 0x99, 0x6e, 0xfb, 0x6a, 0xf9, 0x6b, 0x64, 0x8f, 0x44, 0x45, 0xec, 0x44, 0xf4, 0x7a, - 0xd9, 0x5c, 0x10, 0xe3, 0xd7, 0x2f, 0x26, 0xed, 0x07, 0x54, 0x22, 0xa3, 0x6e, 0xd8, 0x58, - 0x5c, 0x74, 0x5a, 0x0e, 0x11, 0x50, 0xbc, 0xce, 0xba, 0x23, 0x57, 0xd0, 0x58, 0x63, 0x69, - 0x91, 0xf3, 0x8a, 0x37, 0x91, 0xe2, 0x48, 0xde, 0x50, 0x9c, 0x07, 0x0d, 0x81, 0x2a, 0xb2, - 0xfd, 0xa5, 0x78, 0x60, 0xac, 0x87, 0x6b, 0xc4, 0x89, 0x19, 0x2c, 0x1e, 0xf4, 0xce, 0x25, - 0x3c, 0x19, 0x7e, 0xe2, 0x19, 0xa4, - ]) - .unwrap() -} - -pub(crate) fn fake_raw_key_sig() -> Ed25519Signature { - Ed25519Signature::from_bytes(vec![ - 36, 248, 153, 211, 155, 23, 253, 93, 102, 193, 146, 196, 181, 13, 52, 62, 66, 247, 35, 91, - 48, 80, 76, 138, 231, 97, 159, 147, 200, 40, 220, 109, 206, 69, 104, 221, 105, 23, 124, 85, - 24, 40, 73, 45, 119, 122, 103, 39, 253, 102, 194, 251, 204, 189, 168, 194, 174, 237, 146, - 3, 44, 153, 121, 10, - ]) - .unwrap() -} - -pub(crate) fn fake_raw_key_public() -> PublicKey { - PublicKey::from_bytes(&[ - 207, 118, 57, 154, 33, 13, 232, 114, 14, 159, 168, 148, 228, 94, 65, 226, 154, 181, 37, - 227, 11, 196, 2, 128, 28, 7, 98, 80, 209, 88, 91, 205, - ]) - .unwrap() -} - -fn count_needed_vkeys(tx_builder: &TransactionBuilder) -> usize { - let mut input_hashes: RequiredSignersSet = RequiredSignersSet::from(&tx_builder.inputs); - input_hashes.extend(RequiredSignersSet::from(&tx_builder.collateral)); - input_hashes.extend(RequiredSignersSet::from(&tx_builder.required_signers)); - if let Some(mint_builder) = &tx_builder.mint { - input_hashes.extend(RequiredSignersSet::from(&mint_builder.get_native_scripts())); - } - if let Some(withdrawals_builder) = &tx_builder.withdrawals { - input_hashes.extend(withdrawals_builder.get_required_signers()); - } - if let Some(certs_builder) = &tx_builder.certs { - input_hashes.extend(certs_builder.get_required_signers()); - } - input_hashes.len() -} - -// tx_body must be the result of building from tx_builder -// constructs the rest of the Transaction using fake witness data of the correct length -// for use in calculating the size of the final Transaction -fn fake_full_tx( - tx_builder: &TransactionBuilder, - body: TransactionBody, -) -> Result { - let fake_key_root = fake_private_key(); - let raw_key_public = fake_raw_key_public(); - let fake_sig = fake_raw_key_sig(); - - // recall: this includes keys for input, certs and withdrawals - let vkeys = match count_needed_vkeys(tx_builder) { - 0 => None, - x => { - let fake_vkey_witness = Vkeywitness::new(&Vkey::new(&raw_key_public), &fake_sig); - let mut result = Vkeywitnesses::new(); - for _i in 0..x { - result.add(&fake_vkey_witness.clone()); - } - Some(result) - } - }; - let bootstraps = get_bootstraps(&tx_builder.inputs); - let bootstrap_keys = match bootstraps.len() { - 0 => None, - _x => { - let mut result = BootstrapWitnesses::new(); - for addr in bootstraps { - // picking icarus over daedalus for fake witness generation shouldn't matter - result.add(&make_icarus_bootstrap_witness( - &TransactionHash::from([0u8; TransactionHash::BYTE_COUNT]), - &ByronAddress::from_bytes(addr.clone()).unwrap(), - &fake_key_root, - )); - } - Some(result) - } - }; - let (plutus_scripts, plutus_data, redeemers) = { - if let Some(s) = tx_builder.get_combined_plutus_scripts() { - let (s, d, r) = s.collect(); - (Some(s), d, Some(r)) - } else { - (None, None, None) - } - }; - let witness_set = TransactionWitnessSet { - vkeys, - native_scripts: tx_builder.get_combined_native_scripts(), - bootstraps: bootstrap_keys, - plutus_scripts, - plutus_data, - redeemers, - }; - Ok(Transaction { - body, - witness_set, - is_valid: true, - auxiliary_data: tx_builder.auxiliary_data.clone(), - }) -} - -fn assert_required_mint_scripts( - mint: &Mint, - maybe_mint_scripts: Option<&NativeScripts>, -) -> Result<(), JsError> { - if maybe_mint_scripts.is_none_or_empty() { - return Err(JsError::from_str( - "Mint is present in the builder, but witness scripts are not provided!", - )); - } - let mint_scripts = maybe_mint_scripts.unwrap(); - let witness_hashes: HashSet = - mint_scripts.0.iter().map(|script| script.hash()).collect(); - for mint_hash in mint.keys().0.iter() { - if !witness_hashes.contains(mint_hash) { - return Err(JsError::from_str(&format!( - "No witness script is found for mint policy '{:?}'! Script is required!", - hex::encode(mint_hash.to_bytes()), - ))); - } - } - Ok(()) -} - -fn min_fee(tx_builder: &TransactionBuilder) -> Result { - // Commented out for performance, `min_fee` is a critical function - // This was mostly added here as a paranoid step anyways - // If someone is using `set_mint` and `add_mint*` API function, everything is expected to be intact - // TODO: figure out if assert is needed here and a better way to do it maybe only once if mint doesn't change - // if let Some(mint) = tx_builder.mint.as_ref() { - // assert_required_mint_scripts(mint, tx_builder.mint_scripts.as_ref())?; - // } - let full_tx = fake_full_tx(tx_builder, tx_builder.build()?)?; - let fee: Coin = fees::min_fee(&full_tx, &tx_builder.config.fee_algo)?; - if let Some(ex_unit_prices) = &tx_builder.config.ex_unit_prices { - let script_fee: Coin = fees::min_script_fee(&full_tx, &ex_unit_prices)?; - return fee.checked_add(&script_fee); - } - if tx_builder.has_plutus_inputs() { - return Err(JsError::from_str( - "Plutus inputs are present but ex unit prices are missing in the config!", - )); - } - Ok(fee) -} - -#[wasm_bindgen] -pub enum CoinSelectionStrategyCIP2 { - /// Performs CIP2's Largest First ada-only selection. Will error if outputs contain non-ADA assets. - LargestFirst, - /// Performs CIP2's Random Improve ada-only selection. Will error if outputs contain non-ADA assets. - RandomImprove, - /// Same as LargestFirst, but before adding ADA, will insert by largest-first for each asset type. - LargestFirstMultiAsset, - /// Same as RandomImprove, but before adding ADA, will insert by random-improve for each asset type. - RandomImproveMultiAsset, -} - -#[wasm_bindgen] -#[derive(Clone, Debug)] -pub struct TransactionBuilderConfig { - fee_algo: fees::LinearFee, - pool_deposit: Coin, // protocol parameter - key_deposit: Coin, // protocol parameter - max_value_size: u32, // protocol parameter - max_tx_size: u32, // protocol parameter - data_cost: DataCost, // protocol parameter - ex_unit_prices: Option, // protocol parameter - prefer_pure_change: bool, -} - -impl TransactionBuilderConfig { - fn utxo_cost(&self) -> DataCost { - self.data_cost.clone() - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug)] -pub struct TransactionBuilderConfigBuilder { - fee_algo: Option, - pool_deposit: Option, // protocol parameter - key_deposit: Option, // protocol parameter - max_value_size: Option, // protocol parameter - max_tx_size: Option, // protocol parameter - data_cost: Option, // protocol parameter - ex_unit_prices: Option, // protocol parameter - prefer_pure_change: bool, -} - -#[wasm_bindgen] -impl TransactionBuilderConfigBuilder { - pub fn new() -> Self { - Self { - fee_algo: None, - pool_deposit: None, - key_deposit: None, - max_value_size: None, - max_tx_size: None, - data_cost: None, - ex_unit_prices: None, - prefer_pure_change: false, - } - } - - pub fn fee_algo(&self, fee_algo: &fees::LinearFee) -> Self { - let mut cfg = self.clone(); - cfg.fee_algo = Some(fee_algo.clone()); - cfg - } - - /// !!! DEPRECATED !!! - /// Since babbage era cardano nodes use coins per byte. Use '.coins_per_utxo_byte' instead. - #[deprecated( - since = "11.0.0", - note = "Since babbage era cardano nodes use coins per byte. Use '.coins_per_utxo_byte' instead." - )] - pub fn coins_per_utxo_word(&self, coins_per_utxo_word: &Coin) -> Self { - let mut cfg = self.clone(); - cfg.data_cost = Some(DataCost::new_coins_per_word(coins_per_utxo_word)); - cfg - } - - pub fn coins_per_utxo_byte(&self, coins_per_utxo_byte: &Coin) -> Self { - let mut cfg = self.clone(); - cfg.data_cost = Some(DataCost::new_coins_per_byte(coins_per_utxo_byte)); - cfg - } - - pub fn ex_unit_prices(&self, ex_unit_prices: &ExUnitPrices) -> Self { - let mut cfg = self.clone(); - cfg.ex_unit_prices = Some(ex_unit_prices.clone()); - cfg - } - - pub fn pool_deposit(&self, pool_deposit: &BigNum) -> Self { - let mut cfg = self.clone(); - cfg.pool_deposit = Some(pool_deposit.clone()); - cfg - } - - pub fn key_deposit(&self, key_deposit: &BigNum) -> Self { - let mut cfg = self.clone(); - cfg.key_deposit = Some(key_deposit.clone()); - cfg - } - - pub fn max_value_size(&self, max_value_size: u32) -> Self { - let mut cfg = self.clone(); - cfg.max_value_size = Some(max_value_size); - cfg - } - - pub fn max_tx_size(&self, max_tx_size: u32) -> Self { - let mut cfg = self.clone(); - cfg.max_tx_size = Some(max_tx_size); - cfg - } - - pub fn prefer_pure_change(&self, prefer_pure_change: bool) -> Self { - let mut cfg = self.clone(); - cfg.prefer_pure_change = prefer_pure_change; - cfg - } - - pub fn build(&self) -> Result { - let cfg: Self = self.clone(); - Ok(TransactionBuilderConfig { - fee_algo: cfg - .fee_algo - .ok_or(JsError::from_str("uninitialized field: fee_algo"))?, - pool_deposit: cfg - .pool_deposit - .ok_or(JsError::from_str("uninitialized field: pool_deposit"))?, - key_deposit: cfg - .key_deposit - .ok_or(JsError::from_str("uninitialized field: key_deposit"))?, - max_value_size: cfg - .max_value_size - .ok_or(JsError::from_str("uninitialized field: max_value_size"))?, - max_tx_size: cfg - .max_tx_size - .ok_or(JsError::from_str("uninitialized field: max_tx_size"))?, - data_cost: cfg.data_cost.ok_or(JsError::from_str( - "uninitialized field: coins_per_utxo_byte or coins_per_utxo_word", - ))?, - ex_unit_prices: cfg.ex_unit_prices, - prefer_pure_change: cfg.prefer_pure_change, - }) - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug)] -pub struct TransactionBuilder { - config: TransactionBuilderConfig, - inputs: TxInputsBuilder, - collateral: TxInputsBuilder, - outputs: TransactionOutputs, - fee: Option, - ttl: Option, // absolute slot number - certs: Option, - withdrawals: Option, - auxiliary_data: Option, - validity_start_interval: Option, - mint: Option, - script_data_hash: Option, - required_signers: Ed25519KeyHashes, - collateral_return: Option, - total_collateral: Option, - reference_inputs: HashSet, - extra_datums: Option -} - -#[wasm_bindgen] -impl TransactionBuilder { - /// This automatically selects and adds inputs from {inputs} consisting of just enough to cover - /// the outputs that have already been added. - /// This should be called after adding all certs/outputs/etc and will be an error otherwise. - /// Uses CIP2: https://github.com/cardano-foundation/CIPs/blob/master/CIP-0002/CIP-0002.md - /// Adding a change output must be called after via TransactionBuilder::add_change_if_needed() - /// This function, diverging from CIP2, takes into account fees and will attempt to add additional - /// inputs to cover the minimum fees. This does not, however, set the txbuilder's fee. - pub fn add_inputs_from( - &mut self, - inputs: &TransactionUnspentOutputs, - strategy: CoinSelectionStrategyCIP2, - ) -> Result<(), JsError> { - let available_inputs = &inputs.0.clone(); - let mut input_total = self.get_total_input()?; - let mut output_total = self - .get_total_output()? - .checked_add(&Value::new(&self.min_fee()?))?; - match strategy { - CoinSelectionStrategyCIP2::LargestFirst => { - if self - .outputs - .0 - .iter() - .any(|output| output.amount.multiasset.is_some()) - { - return Err(JsError::from_str("Multiasset values not supported by LargestFirst. Please use LargestFirstMultiAsset")); - } - self.cip2_largest_first_by( - available_inputs, - &mut (0..available_inputs.len()).collect(), - &mut input_total, - &mut output_total, - |value| Some(value.coin), - )?; - } - CoinSelectionStrategyCIP2::RandomImprove => { - if self - .outputs - .0 - .iter() - .any(|output| output.amount.multiasset.is_some()) - { - return Err(JsError::from_str("Multiasset values not supported by RandomImprove. Please use RandomImproveMultiAsset")); - } - use rand::Rng; - let mut rng = rand::thread_rng(); - let mut available_indices = - (0..available_inputs.len()).collect::>(); - self.cip2_random_improve_by( - available_inputs, - &mut available_indices, - &mut input_total, - &mut output_total, - |value| Some(value.coin), - &mut rng, - true, - )?; - // Phase 3: add extra inputs needed for fees (not covered by CIP-2) - // We do this at the end because this new inputs won't be associated with - // a specific output, so the improvement algorithm we do above does not apply here. - while input_total.coin < output_total.coin { - if available_indices.is_empty() { - return Err(JsError::from_str("UTxO Balance Insufficient[x]")); - } - let i = *available_indices - .iter() - .nth(rng.gen_range(0..available_indices.len())) - .unwrap(); - available_indices.remove(&i); - let input = &available_inputs[i]; - let input_fee = self.fee_for_input( - &input.output.address, - &input.input, - &input.output.amount, - )?; - self.add_input(&input.output.address, &input.input, &input.output.amount); - input_total = input_total.checked_add(&input.output.amount)?; - output_total = output_total.checked_add(&Value::new(&input_fee))?; - } - } - CoinSelectionStrategyCIP2::LargestFirstMultiAsset => { - // indices into {available_inputs} for inputs that contain {policy_id}:{asset_name} - let mut available_indices = (0..available_inputs.len()).collect::>(); - // run largest-fist by each asset type - if let Some(ma) = output_total.multiasset.clone() { - for (policy_id, assets) in ma.0.iter() { - for (asset_name, _) in assets.0.iter() { - self.cip2_largest_first_by( - available_inputs, - &mut available_indices, - &mut input_total, - &mut output_total, - |value| value.multiasset.as_ref()?.get(policy_id)?.get(asset_name), - )?; - } - } - } - // add in remaining ADA - self.cip2_largest_first_by( - available_inputs, - &mut available_indices, - &mut input_total, - &mut output_total, - |value| Some(value.coin), - )?; - } - CoinSelectionStrategyCIP2::RandomImproveMultiAsset => { - use rand::Rng; - let mut rng = rand::thread_rng(); - let mut available_indices = - (0..available_inputs.len()).collect::>(); - // run random-improve by each asset type - if let Some(ma) = output_total.multiasset.clone() { - for (policy_id, assets) in ma.0.iter() { - for (asset_name, _) in assets.0.iter() { - self.cip2_random_improve_by( - available_inputs, - &mut available_indices, - &mut input_total, - &mut output_total, - |value| value.multiasset.as_ref()?.get(policy_id)?.get(asset_name), - &mut rng, - false, - )?; - } - } - } - // add in remaining ADA - self.cip2_random_improve_by( - available_inputs, - &mut available_indices, - &mut input_total, - &mut output_total, - |value| Some(value.coin), - &mut rng, - false, - )?; - // Phase 3: add extra inputs needed for fees (not covered by CIP-2) - // We do this at the end because this new inputs won't be associated with - // a specific output, so the improvement algorithm we do above does not apply here. - while input_total.coin < output_total.coin { - if available_indices.is_empty() { - return Err(JsError::from_str("UTxO Balance Insufficient[x]")); - } - let i = *available_indices - .iter() - .nth(rng.gen_range(0..available_indices.len())) - .unwrap(); - available_indices.remove(&i); - let input = &available_inputs[i]; - let input_fee = self.fee_for_input( - &input.output.address, - &input.input, - &input.output.amount, - )?; - self.add_input(&input.output.address, &input.input, &input.output.amount); - input_total = input_total.checked_add(&input.output.amount)?; - output_total = output_total.checked_add(&Value::new(&input_fee))?; - } - } - } - - Ok(()) - } - - fn cip2_largest_first_by( - &mut self, - available_inputs: &Vec, - available_indices: &mut Vec, - input_total: &mut Value, - output_total: &mut Value, - by: F, - ) -> Result<(), JsError> - where - F: Fn(&Value) -> Option, - { - let mut relevant_indices = available_indices.clone(); - relevant_indices.retain(|i| by(&available_inputs[*i].output.amount).is_some()); - // ordered in ascending order by predicate {by} - relevant_indices - .sort_by_key(|i| by(&available_inputs[*i].output.amount).expect("filtered above")); - - // iterate in decreasing order for predicate {by} - for i in relevant_indices.iter().rev() { - if by(input_total).unwrap_or(BigNum::zero()) - >= by(output_total).expect("do not call on asset types that aren't in the output") - { - break; - } - let input = &available_inputs[*i]; - // differing from CIP2, we include the needed fees in the targets instead of just output values - let input_fee = - self.fee_for_input(&input.output.address, &input.input, &input.output.amount)?; - self.add_input(&input.output.address, &input.input, &input.output.amount); - *input_total = input_total.checked_add(&input.output.amount)?; - *output_total = output_total.checked_add(&Value::new(&input_fee))?; - available_indices.swap_remove(available_indices.iter().position(|j| i == j).unwrap()); - } - - if by(input_total).unwrap_or(BigNum::zero()) - < by(output_total).expect("do not call on asset types that aren't in the output") - { - return Err(JsError::from_str("UTxO Balance Insufficient")); - } - - Ok(()) - } - - fn cip2_random_improve_by( - &mut self, - available_inputs: &Vec, - available_indices: &mut BTreeSet, - input_total: &mut Value, - output_total: &mut Value, - by: F, - rng: &mut rand::rngs::ThreadRng, - pure_ada: bool, - ) -> Result<(), JsError> - where - F: Fn(&Value) -> Option, - { - use rand::Rng; - // Phase 1: Random Selection - let mut relevant_indices = available_indices - .iter() - .filter(|i| by(&available_inputs[**i].output.amount).is_some()) - .cloned() - .collect::>(); - let mut associated_indices: BTreeMap> = BTreeMap::new(); - let mut outputs = self - .outputs - .0 - .iter() - .filter(|output| by(&output.amount).is_some()) - .cloned() - .collect::>(); - outputs.sort_by_key(|output| by(&output.amount).expect("filtered above")); - let mut available_coins = by(input_total).unwrap_or(BigNum::zero()); - for output in outputs.iter().rev() { - // TODO: how should we adapt this to inputs being associated when running for other assets? - // if we do these two phases for each asset and don't take into account the other runs for other assets - // then we over-add (and potentially fail if we don't have plenty of inputs) - // On the other hand, the improvement phase it difficult to determine if a change is an improvement - // if we're trying to improve for multiple assets at a time without knowing how important each input is - // e.g. maybe we have lots of asset A but not much of B - // For now I will just have this be entirely separarte per-asset but we might want to in a later commit - // consider the improvements separately and have it take some kind of dot product / distance for assets - // during the improvement phase and have the improvement phase target multiple asset types at once. - // One issue with that is how to scale in between differnet assets. We could maybe normalize them by - // dividing each asset type by the sum of the required asset type in all outputs. - // Another possibility for adapting this to multiasstes is when associating an input x for asset type a - // we try and subtract all other assets b != a from the outputs we're trying to cover. - // It might make sense to diverge further and not consider it per-output and to instead just match against - // the sum of all outputs as one single value. - let mut added = available_coins.clone(); - let needed = by(&output.amount).unwrap(); - while added < needed { - if relevant_indices.is_empty() { - return Err(JsError::from_str("UTxO Balance Insufficient")); - } - let random_index = rng.gen_range(0..relevant_indices.len()); - let i = relevant_indices.swap_remove(random_index); - available_indices.remove(&i); - let input = &available_inputs[i]; - added = added.checked_add( - &by(&input.output.amount) - .expect("do not call on asset types that aren't in the output"), - )?; - associated_indices - .entry(output.clone()) - .or_default() - .push(i); - } - available_coins = added.checked_sub(&needed)?; - } - if !relevant_indices.is_empty() && pure_ada { - // Phase 2: Improvement - for output in outputs.iter_mut() { - let associated = associated_indices.get_mut(output).unwrap(); - for i in associated.iter_mut() { - let random_index = rng.gen_range(0..relevant_indices.len()); - let j: &mut usize = relevant_indices.get_mut(random_index).unwrap(); - let input = &available_inputs[*i]; - let new_input = &available_inputs[*j]; - let cur = from_bignum(&by(&input.output.amount).unwrap_or(BigNum::zero())); - let new = from_bignum(&by(&new_input.output.amount).unwrap_or(BigNum::zero())); - let min = from_bignum(&by(&output.amount).unwrap_or(BigNum::zero())); - let ideal = 2 * min; - let max = 3 * min; - let move_closer = - (ideal as i128 - new as i128).abs() < (ideal as i128 - cur as i128).abs(); - let not_exceed_max = new < max; - if move_closer && not_exceed_max { - std::mem::swap(i, j); - available_indices.insert(*i); - available_indices.remove(j); - } - } - } - } - - // after finalizing the improvement we need to actually add these results to the builder - for output in outputs.iter() { - if let Some(associated) = associated_indices.get(output) { - for i in associated.iter() { - let input = &available_inputs[*i]; - let input_fee = - self.fee_for_input(&input.output.address, &input.input, &input.output.amount)?; - self.add_input(&input.output.address, &input.input, &input.output.amount); - *input_total = input_total.checked_add(&input.output.amount)?; - *output_total = output_total.checked_add(&Value::new(&input_fee))?; - } - } - } - - Ok(()) - } - - pub fn set_inputs(&mut self, inputs: &TxInputsBuilder) { - self.inputs = inputs.clone(); - } - - pub fn set_collateral(&mut self, collateral: &TxInputsBuilder) { - self.collateral = collateral.clone(); - } - - pub fn set_collateral_return(&mut self, collateral_return: &TransactionOutput) { - self.collateral_return = Some(collateral_return.clone()); - } - - /// This function will set the collateral-return value and then auto-calculate and assign - /// the total collateral coin value. Will raise an error in case no collateral inputs are set - /// or in case the total collateral value will have any assets in it except coin. - pub fn set_collateral_return_and_total( - &mut self, - collateral_return: &TransactionOutput, - ) -> Result<(), JsError> { - let collateral = &self.collateral; - if collateral.len() == 0 { - return Err(JsError::from_str( - "Cannot calculate total collateral value when collateral inputs are missing", - )); - } - let col_input_value: Value = collateral.total_value()?; - let total_col: Value = col_input_value.checked_sub(&collateral_return.amount())?; - if total_col.multiasset.is_some() { - return Err(JsError::from_str( - "Total collateral value cannot contain assets!", - )); - } - - let min_ada = min_ada_for_output(&collateral_return, &self.config.utxo_cost())?; - if min_ada > collateral_return.amount.coin { - return Err(JsError::from_str(&format!( - "Not enough coin to make return on the collateral value!\ - Increase amount of return coins. \ - Min ada for return {}, but was {}", - min_ada, collateral_return.amount.coin - ))); - } - - self.set_collateral_return(collateral_return); - self.total_collateral = Some(total_col.coin); - Ok(()) - } - - pub fn set_total_collateral(&mut self, total_collateral: &Coin) { - self.total_collateral = Some(total_collateral.clone()); - } - - /// This function will set the total-collateral coin and then auto-calculate and assign - /// the collateral return value. Will raise an error in case no collateral inputs are set. - /// The specified address will be the received of the collateral return - pub fn set_total_collateral_and_return( - &mut self, - total_collateral: &Coin, - return_address: &Address, - ) -> Result<(), JsError> { - let collateral = &self.collateral; - if collateral.len() == 0 { - return Err(JsError::from_str( - "Cannot calculate collateral return when collateral inputs are missing", - )); - } - let col_input_value: Value = collateral.total_value()?; - let col_return: Value = col_input_value.checked_sub(&Value::new(&total_collateral))?; - if col_return.multiasset.is_some() || col_return.coin > BigNum::zero() { - let return_output = TransactionOutput::new(return_address, &col_return); - let min_ada = min_ada_for_output(&return_output, &self.config.utxo_cost())?; - if min_ada > col_return.coin { - return Err(JsError::from_str(&format!( - "Not enough coin to make return on the collateral value!\ - Decrease the total collateral value or add more collateral inputs. \ - Min ada for return {}, but was {}", - min_ada, col_return.coin - ))); - } - self.collateral_return = Some(return_output); - } - self.set_total_collateral(total_collateral); - - Ok(()) - } - - pub fn add_reference_input(&mut self, reference_input: &TransactionInput) { - self.reference_inputs.insert(reference_input.clone()); - } - - /// We have to know what kind of inputs these are to know what kind of mock witnesses to create since - /// 1) mock witnesses have different lengths depending on the type which changes the expecting fee - /// 2) Witnesses are a set so we need to get rid of duplicates to avoid over-estimating the fee - #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] - pub fn add_key_input( - &mut self, - hash: &Ed25519KeyHash, - input: &TransactionInput, - amount: &Value, - ) { - self.inputs.add_key_input(hash, input, amount); - } - - /// This method adds the input to the builder BUT leaves a missing spot for the witness native script - /// - /// After adding the input with this method, use `.add_required_native_input_scripts` - /// and `.add_required_plutus_input_scripts` to add the witness scripts - /// - /// Or instead use `.add_native_script_input` and `.add_plutus_script_input` - /// to add inputs right along with the script, instead of the script hash - #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] - pub fn add_script_input( - &mut self, - hash: &ScriptHash, - input: &TransactionInput, - amount: &Value, - ) { - self.inputs.add_script_input(hash, input, amount); - } - - /// This method will add the input to the builder and also register the required native script witness - #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] - pub fn add_native_script_input( - &mut self, - script: &NativeScript, - input: &TransactionInput, - amount: &Value, - ) { - self.inputs.add_native_script_input(script, input, amount); - } - - /// This method will add the input to the builder and also register the required plutus witness - #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] - pub fn add_plutus_script_input( - &mut self, - witness: &PlutusWitness, - input: &TransactionInput, - amount: &Value, - ) { - self.inputs.add_plutus_script_input(witness, input, amount); - } - - #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] - pub fn add_bootstrap_input( - &mut self, - hash: &ByronAddress, - input: &TransactionInput, - amount: &Value, - ) { - self.inputs.add_bootstrap_input(hash, input, amount); - } - - /// Note that for script inputs this method will use underlying generic `.add_script_input` - /// which leaves a required empty spot for the script witness (or witnesses in case of Plutus). - /// You can use `.add_native_script_input` or `.add_plutus_script_input` directly to register the input along with the witness. - #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] - pub fn add_input(&mut self, address: &Address, input: &TransactionInput, amount: &Value) { - self.inputs.add_input(address, input, amount); - } - - /// Returns the number of still missing input scripts (either native or plutus) - /// Use `.add_required_native_input_scripts` or `.add_required_plutus_input_scripts` to add the missing scripts - #[deprecated(since = "10.2.0", note = "Use `.count_missing_input_scripts` from `TxInputsBuilder`")] - pub fn count_missing_input_scripts(&self) -> usize { - self.inputs.count_missing_input_scripts() - } - - /// Try adding the specified scripts as witnesses for ALREADY ADDED script inputs - /// Any scripts that don't match any of the previously added inputs will be ignored - /// Returns the number of remaining required missing witness scripts - /// Use `.count_missing_input_scripts` to find the number of still missing scripts - #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] - pub fn add_required_native_input_scripts(&mut self, scripts: &NativeScripts) -> usize { - self.inputs.add_required_native_input_scripts(scripts) - } - - /// Try adding the specified scripts as witnesses for ALREADY ADDED script inputs - /// Any scripts that don't match any of the previously added inputs will be ignored - /// Returns the number of remaining required missing witness scripts - /// Use `.count_missing_input_scripts` to find the number of still missing scripts - #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] - pub fn add_required_plutus_input_scripts(&mut self, scripts: &PlutusWitnesses) -> usize { - self.inputs.add_required_plutus_input_scripts(scripts) - } - - /// Returns a copy of the current script input witness scripts in the builder - #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] - pub fn get_native_input_scripts(&self) -> Option { - self.inputs.get_native_input_scripts() - } - - /// Returns a copy of the current plutus input witness scripts in the builder. - /// NOTE: each plutus witness will be cloned with a specific corresponding input index - #[deprecated(since = "10.2.0", note = "Use `.set_inputs`")] - pub fn get_plutus_input_scripts(&self) -> Option { - self.inputs.get_plutus_input_scripts() - } - - /// calculates how much the fee would increase if you added a given output - pub fn fee_for_input( - &self, - address: &Address, - input: &TransactionInput, - amount: &Value, - ) -> Result { - let mut self_copy = self.clone(); - - // we need some value for these for it to be a a valid transaction - // but since we're only calculating the difference between the fee of two transactions - // it doesn't matter what these are set as, since it cancels out - self_copy.set_fee(&to_bignum(0)); - - let fee_before = min_fee(&self_copy)?; - - self_copy.add_input(&address, &input, &amount); - let fee_after = min_fee(&self_copy)?; - fee_after.checked_sub(&fee_before) - } - - /// Add explicit output via a TransactionOutput object - pub fn add_output(&mut self, output: &TransactionOutput) -> Result<(), JsError> { - let value_size = output.amount.to_bytes().len(); - if value_size > self.config.max_value_size as usize { - return Err(JsError::from_str(&format!( - "Maximum value size of {} exceeded. Found: {}", - self.config.max_value_size, value_size - ))); - } - let min_ada = min_ada_for_output(&output, &self.config.utxo_cost())?; - if output.amount().coin() < min_ada { - Err(JsError::from_str(&format!( - "Value {} less than the minimum UTXO value {}", - from_bignum(&output.amount().coin()), - from_bignum(&min_ada) - ))) - } else { - self.outputs.add(output); - Ok(()) - } - } - - /// calculates how much the fee would increase if you added a given output - pub fn fee_for_output(&self, output: &TransactionOutput) -> Result { - let mut self_copy = self.clone(); - - // we need some value for these for it to be a a valid transaction - // but since we're only calculating the different between the fee of two transactions - // it doesn't matter what these are set as, since it cancels out - self_copy.set_fee(&to_bignum(0)); - - let fee_before = min_fee(&self_copy)?; - - self_copy.add_output(&output)?; - let fee_after = min_fee(&self_copy)?; - fee_after.checked_sub(&fee_before) - } - - pub fn set_fee(&mut self, fee: &Coin) { - self.fee = Some(fee.clone()) - } - - /// !!! DEPRECATED !!! - /// Set ttl value. - #[deprecated( - since = "10.1.0", - note = "Underlying value capacity of ttl (BigNum u64) bigger then Slot32. Use set_ttl_bignum instead." - )] - pub fn set_ttl(&mut self, ttl: Slot32) { - self.ttl = Some(ttl.into()) - } - - pub fn set_ttl_bignum(&mut self, ttl: &SlotBigNum) { - self.ttl = Some(ttl.clone()) - } - - /// !!! DEPRECATED !!! - /// Uses outdated slot number format. - #[deprecated( - since = "10.1.0", - note = "Underlying value capacity of validity_start_interval (BigNum u64) bigger then Slot32. Use set_validity_start_interval_bignum instead." - )] - pub fn set_validity_start_interval(&mut self, validity_start_interval: Slot32) { - self.validity_start_interval = Some(validity_start_interval.into()) - } - - pub fn set_validity_start_interval_bignum(&mut self, validity_start_interval: SlotBigNum) { - self.validity_start_interval = Some(validity_start_interval.clone()) - } - - /// !!! DEPRECATED !!! - /// Can emit error if add a cert with script credential. - /// Use set_certs_builder instead. - #[deprecated( - since = "11.4.1", - note = "Can emit an error if you add a cert with script credential. Use set_certs_builder instead." - )] - pub fn set_certs(&mut self, certs: &Certificates) -> Result<(), JsError> { - let mut builder = CertificatesBuilder::new(); - for cert in &certs.0 { - builder.add(cert)?; - } - - self.certs = Some(builder); - - Ok(()) - } - - pub fn set_certs_builder(&mut self, certs: &CertificatesBuilder) { - self.certs = Some(certs.clone()); - } - - /// !!! DEPRECATED !!! - /// Can emit error if add a withdrawal with script credential. - /// Use set_withdrawals_builder instead. - #[deprecated( - since = "11.4.1", - note = "Can emit an error if you add a withdrawal with script credential. Use set_withdrawals_builder instead." - )] - pub fn set_withdrawals(&mut self, withdrawals: &Withdrawals) -> Result<(), JsError>{ - let mut withdrawals_builder = WithdrawalsBuilder::new(); - for(withdrawal, coin) in &withdrawals.0 { - withdrawals_builder.add(&withdrawal, &coin)?; - } - - self.withdrawals = Some(withdrawals_builder); - - Ok(()) - } - - pub fn set_withdrawals_builder(&mut self, withdrawals: &WithdrawalsBuilder) { - self.withdrawals = Some(withdrawals.clone()); - } - - pub fn get_auxiliary_data(&self) -> Option { - self.auxiliary_data.clone() - } - - /// Set explicit auxiliary data via an AuxiliaryData object - /// It might contain some metadata plus native or Plutus scripts - pub fn set_auxiliary_data(&mut self, auxiliary_data: &AuxiliaryData) { - self.auxiliary_data = Some(auxiliary_data.clone()) - } - - /// Set metadata using a GeneralTransactionMetadata object - /// It will be set to the existing or new auxiliary data in this builder - pub fn set_metadata(&mut self, metadata: &GeneralTransactionMetadata) { - let mut aux = self - .auxiliary_data - .as_ref() - .cloned() - .unwrap_or(AuxiliaryData::new()); - aux.set_metadata(metadata); - self.set_auxiliary_data(&aux); - } - - /// Add a single metadatum using TransactionMetadatumLabel and TransactionMetadatum objects - /// It will be securely added to existing or new metadata in this builder - pub fn add_metadatum(&mut self, key: &TransactionMetadatumLabel, val: &TransactionMetadatum) { - let mut metadata = self - .auxiliary_data - .as_ref() - .map(|aux| aux.metadata().as_ref().cloned()) - .unwrap_or(None) - .unwrap_or(GeneralTransactionMetadata::new()); - metadata.insert(key, val); - self.set_metadata(&metadata); - } - - /// Add a single JSON metadatum using a TransactionMetadatumLabel and a String - /// It will be securely added to existing or new metadata in this builder - pub fn add_json_metadatum( - &mut self, - key: &TransactionMetadatumLabel, - val: String, - ) -> Result<(), JsError> { - self.add_json_metadatum_with_schema(key, val, MetadataJsonSchema::NoConversions) - } - - /// Add a single JSON metadatum using a TransactionMetadatumLabel, a String, and a MetadataJsonSchema object - /// It will be securely added to existing or new metadata in this builder - pub fn add_json_metadatum_with_schema( - &mut self, - key: &TransactionMetadatumLabel, - val: String, - schema: MetadataJsonSchema, - ) -> Result<(), JsError> { - let metadatum = encode_json_str_to_metadatum(val, schema)?; - self.add_metadatum(key, &metadatum); - Ok(()) - } - - pub fn set_mint_builder(&mut self, mint_builder: &MintBuilder) { - self.mint = Some(mint_builder.clone()); - } - - pub fn get_mint_builder(&self) -> Option { - self.mint.clone() - } - - /// !!! DEPRECATED !!! - /// Mints are defining by MintBuilder now. - /// Use `.set_mint_builder()` and `MintBuilder` instead. - #[deprecated( - since = "11.2.0", - note = "Mints are defining by MintBuilder now. Use `.set_mint_builder()` and `MintBuilder` instead." - )] - /// Set explicit Mint object and the required witnesses to this builder - /// it will replace any previously existing mint and mint scripts - /// NOTE! Error will be returned in case a mint policy does not have a matching script - pub fn set_mint(&mut self, mint: &Mint, mint_scripts: &NativeScripts) -> Result<(), JsError> { - assert_required_mint_scripts(mint, Some(mint_scripts))?; - let mut scripts_policies = HashMap::new(); - for scipt in &mint_scripts.0 { - scripts_policies.insert(scipt.hash(), scipt.clone()); - } - - let mut mint_builder = MintBuilder::new(); - - for (policy_id, asset_map) in &mint.0 { - for (asset_name, amount) in &asset_map.0 { - if let Some(script) = scripts_policies.get(policy_id) { - let mint_witness = MintWitness::new_native_script(script); - mint_builder.set_asset(&mint_witness, asset_name, amount); - } else { - return Err(JsError::from_str("Mint policy does not have a matching script")); - } - - } - } - self.mint = Some(mint_builder); - Ok(()) - } - - /// !!! DEPRECATED !!! - /// Mints are defining by MintBuilder now. - /// Use `.get_mint_builder()` and `.build()` instead. - #[deprecated( - since = "11.2.0", - note = "Mints are defining by MintBuilder now. Use `.get_mint_builder()` and `.build()` instead." - )] - /// Returns a copy of the current mint state in the builder - pub fn get_mint(&self) -> Option { - match &self.mint { - Some(mint) => Some(mint.build()), - None => None, - } - } - - /// Returns a copy of the current mint witness scripts in the builder - pub fn get_mint_scripts(&self) -> Option { - match &self.mint { - Some(mint) => Some(mint.get_native_scripts()), - None => None, - } - } - - /// !!! DEPRECATED !!! - /// Mints are defining by MintBuilder now. - /// Use `.set_mint_builder()` and `MintBuilder` instead. - #[deprecated( - since = "11.2.0", - note = "Mints are defining by MintBuilder now. Use `.set_mint_builder()` and `MintBuilder` instead." - )] - /// Add a mint entry to this builder using a PolicyID and MintAssets object - /// It will be securely added to existing or new Mint in this builder - /// It will replace any existing mint assets with the same PolicyID - pub fn set_mint_asset(&mut self, policy_script: &NativeScript, mint_assets: &MintAssets) { - let mint_witness = MintWitness::new_native_script(policy_script); - if let Some(mint) = &mut self.mint { - for (asset, amount) in mint_assets.0.iter() { - mint.set_asset(&mint_witness, asset, amount); - } - } else { - let mut mint = MintBuilder::new(); - for (asset, amount) in mint_assets.0.iter() { - mint.set_asset(&mint_witness, asset, amount); - } - self.mint = Some(mint); - } - } - - /// !!! DEPRECATED !!! - /// Mints are defining by MintBuilder now. - /// Use `.set_mint_builder()` and `MintBuilder` instead. - #[deprecated( - since = "11.2.0", - note = "Mints are defining by MintBuilder now. Use `.set_mint_builder()` and `MintBuilder` instead." - )] - /// Add a mint entry to this builder using a PolicyID, AssetName, and Int object for amount - /// It will be securely added to existing or new Mint in this builder - /// It will replace any previous existing amount same PolicyID and AssetName - pub fn add_mint_asset( - &mut self, - policy_script: &NativeScript, - asset_name: &AssetName, - amount: Int, - ) { - let mint_witness = MintWitness::new_native_script(policy_script); - if let Some(mint) = &mut self.mint { - mint.add_asset(&mint_witness, asset_name, &amount); - } else { - let mut mint = MintBuilder::new(); - mint.add_asset(&mint_witness, asset_name, &amount); - self.mint = Some(mint); - } - } - - /// Add a mint entry together with an output to this builder - /// Using a PolicyID, AssetName, Int for amount, Address, and Coin (BigNum) objects - /// The asset will be securely added to existing or new Mint in this builder - /// A new output will be added with the specified Address, the Coin value, and the minted asset - pub fn add_mint_asset_and_output( - &mut self, - policy_script: &NativeScript, - asset_name: &AssetName, - amount: Int, - output_builder: &TransactionOutputAmountBuilder, - output_coin: &Coin, - ) -> Result<(), JsError> { - if !amount.is_positive() { - return Err(JsError::from_str("Output value must be positive!")); - } - let policy_id: PolicyID = policy_script.hash(); - self.add_mint_asset(policy_script, asset_name, amount.clone()); - let multiasset = Mint::new_from_entry( - &policy_id, - &MintAssets::new_from_entry(asset_name, amount.clone()), - ) - .as_positive_multiasset(); - - self.add_output( - &output_builder - .with_coin_and_asset(&output_coin, &multiasset) - .build()?, - ) - } - - /// Add a mint entry together with an output to this builder - /// Using a PolicyID, AssetName, Int for amount, and Address objects - /// The asset will be securely added to existing or new Mint in this builder - /// A new output will be added with the specified Address and the minted asset - /// The output will be set to contain the minimum required amount of Coin - pub fn add_mint_asset_and_output_min_required_coin( - &mut self, - policy_script: &NativeScript, - asset_name: &AssetName, - amount: Int, - output_builder: &TransactionOutputAmountBuilder, - ) -> Result<(), JsError> { - if !amount.is_positive() { - return Err(JsError::from_str("Output value must be positive!")); - } - let policy_id: PolicyID = policy_script.hash(); - self.add_mint_asset(policy_script, asset_name, amount.clone()); - let multiasset = Mint::new_from_entry( - &policy_id, - &MintAssets::new_from_entry(asset_name, amount.clone()), - ) - .as_positive_multiasset(); - - self.add_output( - &output_builder - .with_asset_and_min_required_coin_by_utxo_cost( - &multiasset, - &self.config.utxo_cost(), - )? - .build()?, - ) - } - - pub fn add_extra_witness_datum(&mut self, datum: &PlutusData) { - if let Some(extra_datums) = &mut self.extra_datums { - extra_datums.add(datum); - } else { - let mut extra_datums = PlutusList::new(); - extra_datums.add(datum); - self.extra_datums = Some(extra_datums); - } - } - - pub fn get_extra_witness_datums(&self) -> Option { - self.extra_datums.clone() - } - - pub fn new(cfg: &TransactionBuilderConfig) -> Self { - Self { - config: cfg.clone(), - inputs: TxInputsBuilder::new(), - collateral: TxInputsBuilder::new(), - outputs: TransactionOutputs::new(), - fee: None, - ttl: None, - certs: None, - withdrawals: None, - auxiliary_data: None, - validity_start_interval: None, - mint: None, - script_data_hash: None, - required_signers: Ed25519KeyHashes::new(), - collateral_return: None, - total_collateral: None, - reference_inputs: HashSet::new(), - extra_datums: None, - } - } - - pub fn get_reference_inputs(&self) -> TransactionInputs { - let mut inputs = self.reference_inputs.clone(); - for input in self.inputs.get_ref_inputs().0 { - inputs.insert(input); - } - - if let Some(mint) = &self.mint { - for input in mint.get_ref_inputs().0 { - inputs.insert(input); - } - } - - if let Some(withdrawals) = &self.withdrawals { - for input in withdrawals.get_ref_inputs().0 { - inputs.insert(input); - } - } - - if let Some(certs) = &self.certs { - for input in certs.get_ref_inputs().0 { - inputs.insert(input); - } - } - - let vec_inputs = inputs.into_iter().collect(); - TransactionInputs(vec_inputs) - } - - /// does not include refunds or withdrawals - pub fn get_explicit_input(&self) -> Result { - self.inputs - .iter() - .try_fold(Value::zero(), |acc, ref tx_builder_input| { - acc.checked_add(&tx_builder_input.amount) - }) - } - - /// withdrawals and refunds - pub fn get_implicit_input(&self) -> Result { - let mut implicit_input = Value::zero(); - if let Some(withdrawals) = &self.withdrawals { - implicit_input = implicit_input.checked_add(&withdrawals.get_total_withdrawals()?)?; - } - if let Some(refunds) = &self.certs { - implicit_input = implicit_input.checked_add(&refunds.get_certificates_refund( - &self.config.pool_deposit, - &self.config.key_deposit - )?)?; - } - - Ok(implicit_input) - } - - /// Returns mint as tuple of (mint_value, burn_value) or two zero values - fn get_mint_as_values(&self) -> (Value, Value) { - self.mint - .as_ref() - .map(|m| { - ( - Value::new_from_assets(&m.build().as_positive_multiasset()), - Value::new_from_assets(&m.build().as_negative_multiasset()), - ) - }) - .unwrap_or((Value::zero(), Value::zero())) - } - - /// Return explicit input plus implicit input plus mint - pub fn get_total_input(&self) -> Result { - let (mint_value, _) = self.get_mint_as_values(); - self.get_explicit_input()? - .checked_add(&self.get_implicit_input()?)? - .checked_add(&mint_value) - } - - /// Return explicit output plus deposit plus burn - pub fn get_total_output(&self) -> Result { - let (_, burn_value) = self.get_mint_as_values(); - self.get_explicit_output()? - .checked_add(&Value::new(&self.get_deposit()?))? - .checked_add(&burn_value) - } - - /// does not include fee - pub fn get_explicit_output(&self) -> Result { - self.outputs - .0 - .iter() - .try_fold(Value::new(&to_bignum(0)), |acc, ref output| { - acc.checked_add(&output.amount()) - }) - } - - pub fn get_deposit(&self) -> Result { - if let Some(certs) = &self.certs { - Ok(certs.get_certificates_deposit( - &self.config.pool_deposit, - &self.config.key_deposit, - )?) - } else { - Ok(Coin::zero()) - } - } - - pub fn get_fee_if_set(&self) -> Option { - self.fee.clone() - } - - /// Warning: this function will mutate the /fee/ field - /// Make sure to call this function last after setting all other tx-body properties - /// Editing inputs, outputs, mint, etc. after change been calculated - /// might cause a mismatch in calculated fee versus the required fee - pub fn add_change_if_needed(&mut self, address: &Address) -> Result { - self.add_change_if_needed_with_optional_script_and_datum(address, None, None) - } - - pub fn add_change_if_needed_with_datum(&mut self, - address: &Address, - plutus_data: &OutputDatum) - -> Result - { - self.add_change_if_needed_with_optional_script_and_datum( - address, - Some(plutus_data.0.clone()), - None) - } - - - fn add_change_if_needed_with_optional_script_and_datum(&mut self, address: &Address, - plutus_data: Option, - script_ref: Option) - -> Result - { - let fee = match &self.fee { - None => self.min_fee(), - // generating the change output involves changing the fee - Some(_x) => { - return Err(JsError::from_str( - "Cannot calculate change if fee was explicitly specified", - )) - } - }?; - - let input_total = self.get_total_input()?; - let output_total = self.get_total_output()?; - - let shortage = get_input_shortage(&input_total, &output_total, &fee)?; - if let Some(shortage) = shortage { - return Err(JsError::from_str(&format!("Insufficient input in transaction. {}", shortage))); - } - - use std::cmp::Ordering; - match &input_total.partial_cmp(&output_total.checked_add(&Value::new(&fee))?) { - Some(Ordering::Equal) => { - // recall: min_fee assumed the fee was the maximum possible so we definitely have enough input to cover whatever fee it ends up being - self.set_fee(&input_total.checked_sub(&output_total)?.coin()); - Ok(false) - } - Some(Ordering::Less) => Err(JsError::from_str("Insufficient input in transaction")), - Some(Ordering::Greater) => { - fn has_assets(ma: Option) -> bool { - ma.map(|assets| assets.len() > 0).unwrap_or(false) - } - let change_estimator = input_total.checked_sub(&output_total)?; - if has_assets(change_estimator.multiasset()) { - fn will_adding_asset_make_output_overflow( - output: &TransactionOutput, - current_assets: &Assets, - asset_to_add: (PolicyID, AssetName, BigNum), - max_value_size: u32, - data_cost: &DataCost, - ) -> Result { - let (policy, asset_name, value) = asset_to_add; - let mut current_assets_clone = current_assets.clone(); - current_assets_clone.insert(&asset_name, &value); - let mut amount_clone = output.amount.clone(); - let mut val = Value::new(&Coin::zero()); - let mut ma = MultiAsset::new(); - - ma.insert(&policy, ¤t_assets_clone); - val.set_multiasset(&ma); - amount_clone = amount_clone.checked_add(&val)?; - - // calculate minADA for more precise max value size - let mut calc = MinOutputAdaCalculator::new_empty(data_cost)?; - calc.set_amount(&val); - let min_ada = calc.calculate_ada()?; - amount_clone.set_coin(&min_ada); - - Ok(amount_clone.to_bytes().len() > max_value_size as usize) - } - fn pack_nfts_for_change( - max_value_size: u32, - data_cost: &DataCost, - change_address: &Address, - change_estimator: &Value, - plutus_data: &Option, - script_ref: &Option, - ) -> Result, JsError> { - // we insert the entire available ADA temporarily here since that could potentially impact the size - // as it could be 1, 2 3 or 4 bytes for Coin. - let mut change_assets: Vec = Vec::new(); - - let mut base_coin = Value::new(&change_estimator.coin()); - base_coin.set_multiasset(&MultiAsset::new()); - let mut output = TransactionOutput { - address: change_address.clone(), - amount: base_coin.clone(), - plutus_data: plutus_data.clone(), - script_ref: script_ref.clone(), - serialization_format: None, - }; - // If this becomes slow on large TXs we can optimize it like the following - // to avoid cloning + reserializing the entire output. - // This would probably be more relevant if we use a smarter packing algorithm - // which might need to compare more size differences than greedy - //let mut bytes_used = output.to_bytes().len(); - - // a greedy packing is done here to avoid an exponential bin-packing - // which in most cases likely shouldn't be the difference between - // having an extra change output or not unless there are gigantic - // differences in NFT policy sizes - for (policy, assets) in change_estimator.multiasset().unwrap().0.iter() { - // for simplicity we also don't split assets within a single policy since - // you would need to have a very high amoun of assets (which add 1-36 bytes each) - // in a single policy to make a difference. In the future if this becomes an issue - // we can change that here. - - // this is the other part of the optimization but we need to take into account - // the difference between CBOR encoding which can change which happens in two places: - // a) length within assets of one policy id - // b) length of the entire multiasset - // so for simplicity we will just do it the safe, naive way unless - // performance becomes an issue. - //let extra_bytes = policy.to_bytes().len() + assets.to_bytes().len() + 2 + cbor_len_diff; - //if bytes_used + extra_bytes <= max_value_size as usize { - let mut old_amount = output.amount.clone(); - let mut val = Value::new(&Coin::zero()); - let mut next_nft = MultiAsset::new(); - - let asset_names = assets.keys(); - let mut rebuilt_assets = Assets::new(); - for n in 0..asset_names.len() { - let asset_name = asset_names.get(n); - let value = assets.get(&asset_name).unwrap(); - - if will_adding_asset_make_output_overflow( - &output, - &rebuilt_assets, - (policy.clone(), asset_name.clone(), value), - max_value_size, - data_cost, - )? { - // if we got here, this means we will run into a overflow error, - // so we want to split into multiple outputs, for that we... - - // 1. insert the current assets as they are, as this won't overflow - next_nft.insert(policy, &rebuilt_assets); - val.set_multiasset(&next_nft); - output.amount = output.amount.checked_add(&val)?; - change_assets.push(output.amount.multiasset().unwrap()); - - // 2. create a new output with the base coin value as zero - base_coin = Value::new(&Coin::zero()); - base_coin.set_multiasset(&MultiAsset::new()); - output = TransactionOutput { - address: change_address.clone(), - amount: base_coin.clone(), - plutus_data: plutus_data.clone(), - script_ref: script_ref.clone(), - serialization_format: None, - }; - - // 3. continue building the new output from the asset we stopped - old_amount = output.amount.clone(); - val = Value::new(&Coin::zero()); - next_nft = MultiAsset::new(); - - rebuilt_assets = Assets::new(); - } - - rebuilt_assets.insert(&asset_name, &value); - } - - next_nft.insert(policy, &rebuilt_assets); - val.set_multiasset(&next_nft); - output.amount = output.amount.checked_add(&val)?; - - // calculate minADA for more precise max value size - let mut amount_clone = output.amount.clone(); - let mut calc = MinOutputAdaCalculator::new_empty(data_cost)?; - calc.set_amount(&val); - let min_ada = calc.calculate_ada()?; - amount_clone.set_coin(&min_ada); - - if amount_clone.to_bytes().len() > max_value_size as usize { - output.amount = old_amount; - break; - } - } - change_assets.push(output.amount.multiasset().unwrap()); - Ok(change_assets) - } - let mut change_left = input_total.checked_sub(&output_total)?; - let mut new_fee = fee.clone(); - // we might need multiple change outputs for cases where the change has many asset types - // which surpass the max UTXO size limit - let utxo_cost = self.config.utxo_cost(); - let mut calc = MinOutputAdaCalculator::new_empty(&utxo_cost)?; - if let Some(data) = &plutus_data { - match data { - DataOption::DataHash(data_hash) => calc.set_data_hash(data_hash), - DataOption::Data(datum) => calc.set_plutus_data(datum), - }; - } - if let Some(script_ref) = &script_ref { - calc.set_script_ref(script_ref); - } - let minimum_utxo_val = calc.calculate_ada()?; - while let Some(Ordering::Greater) = change_left - .multiasset - .as_ref() - .map_or_else(|| None, |ma| ma.partial_cmp(&MultiAsset::new())) - { - let nft_changes = pack_nfts_for_change( - self.config.max_value_size, - &utxo_cost, - address, - &change_left, - &plutus_data.clone(), - &script_ref.clone(), - )?; - if nft_changes.len() == 0 { - // this likely should never happen - return Err(JsError::from_str("NFTs too large for change output")); - } - // we only add the minimum needed (for now) to cover this output - let mut change_value = Value::new(&Coin::zero()); - for nft_change in nft_changes.iter() { - change_value.set_multiasset(&nft_change); - let mut calc = MinOutputAdaCalculator::new_empty(&utxo_cost)?; - //TODO add precise calculation - let mut fake_change = change_value.clone(); - fake_change.set_coin(&change_left.coin); - calc.set_amount(&fake_change); - if let Some(data) = &plutus_data { - match data { - DataOption::DataHash(data_hash) => { - calc.set_data_hash(data_hash) - } - DataOption::Data(datum) => calc.set_plutus_data(datum), - }; - } - if let Some(script_ref) = &script_ref { - calc.set_script_ref(script_ref); - } - let min_ada = calc.calculate_ada()?; - change_value.set_coin(&min_ada); - let change_output = TransactionOutput { - address: address.clone(), - amount: change_value.clone(), - plutus_data: plutus_data.clone(), - script_ref: script_ref.clone(), - serialization_format: None, - }; - - // increase fee - let fee_for_change = self.fee_for_output(&change_output)?; - new_fee = new_fee.checked_add(&fee_for_change)?; - if change_left.coin() < min_ada.checked_add(&new_fee)? { - return Err(JsError::from_str("Not enough ADA leftover to include non-ADA assets in a change address")); - } - change_left = change_left.checked_sub(&change_value)?; - self.add_output(&change_output)?; - } - } - change_left = change_left.checked_sub(&Value::new(&new_fee))?; - // add potentially a separate pure ADA change output - let left_above_minimum = change_left.coin.compare(&minimum_utxo_val) > 0; - if self.config.prefer_pure_change && left_above_minimum { - let pure_output = TransactionOutput { - address: address.clone(), - amount: change_left.clone(), - plutus_data: plutus_data.clone(), - script_ref: script_ref.clone(), - serialization_format: None, - }; - let additional_fee = self.fee_for_output(&pure_output)?; - let potential_pure_value = - change_left.checked_sub(&Value::new(&additional_fee))?; - let potential_pure_above_minimum = - potential_pure_value.coin.compare(&minimum_utxo_val) > 0; - if potential_pure_above_minimum { - new_fee = new_fee.checked_add(&additional_fee)?; - change_left = Value::zero(); - self.add_output(&TransactionOutput { - address: address.clone(), - amount: potential_pure_value.clone(), - plutus_data: plutus_data.clone(), - script_ref: script_ref.clone(), - serialization_format: None, - })?; - } - } - self.set_fee(&new_fee); - // add in the rest of the ADA - if !change_left.is_zero() { - self.outputs.0.last_mut().unwrap().amount = self - .outputs - .0 - .last() - .unwrap() - .amount - .checked_add(&change_left)?; - } - Ok(true) - } else { - let mut calc = MinOutputAdaCalculator::new_empty(&self.config.utxo_cost())?; - calc.set_amount(&change_estimator); - if let Some(data) = &plutus_data { - match data { - DataOption::DataHash(data_hash) => calc.set_data_hash(data_hash), - DataOption::Data(datum) => calc.set_plutus_data(datum), - }; - } - if let Some(script_ref) = &script_ref { - calc.set_script_ref(script_ref); - } - let min_ada = calc.calculate_ada()?; - - // no-asset case so we have no problem burning the rest if there is no other option - fn burn_extra( - builder: &mut TransactionBuilder, - burn_amount: &BigNum, - ) -> Result { - // recall: min_fee assumed the fee was the maximum possible so we definitely have enough input to cover whatever fee it ends up being - builder.set_fee(burn_amount); - Ok(false) // not enough input to covert the extra fee from adding an output so we just burn whatever is left - } - match change_estimator.coin() >= min_ada { - false => burn_extra(self, &change_estimator.coin()), - true => { - // check how much the fee would increase if we added a change output - let fee_for_change = self.fee_for_output(&TransactionOutput { - address: address.clone(), - amount: change_estimator.clone(), - plutus_data: plutus_data.clone(), - script_ref: script_ref.clone(), - serialization_format: None, - })?; - - let new_fee = fee.checked_add(&fee_for_change)?; - match change_estimator.coin() - >= min_ada.checked_add(&Value::new(&new_fee).coin())? - { - false => burn_extra(self, &change_estimator.coin()), - true => { - // recall: min_fee assumed the fee was the maximum possible so we definitely have enough input to cover whatever fee it ends up being - self.set_fee(&new_fee); - - self.add_output(&TransactionOutput { - address: address.clone(), - amount: change_estimator - .checked_sub(&Value::new(&new_fee.clone()))?, - plutus_data: plutus_data.clone(), - script_ref: script_ref.clone(), - serialization_format: None, - })?; - - Ok(true) - } - } - } - } - } - } - None => Err(JsError::from_str( - "missing input or output for some native asset", - )), - } - } - - - /// This method will calculate the script hash data - /// using the plutus datums and redeemers already present in the builder - /// along with the provided cost model, and will register the calculated value - /// in the builder to be used when building the tx body. - /// In case there are no plutus input witnesses present - nothing will change - /// You can set specific hash value using `.set_script_data_hash` - /// NOTE: this function will check which language versions are used in the present scripts - /// and will assert and require for a corresponding cost-model to be present in the passed map. - /// Only the cost-models for the present language versions will be used in the hash calculation. - pub fn calc_script_data_hash(&mut self, cost_models: &Costmdls) -> Result<(), JsError> { - let mut used_langs = BTreeSet::new(); - let mut retained_cost_models = Costmdls::new(); - let mut plutus_witnesses = PlutusWitnesses::new(); - if let Some(mut inputs_plutus) = self.inputs.get_plutus_input_scripts() { - used_langs.append(&mut self.inputs.get_used_plutus_lang_versions()); - plutus_witnesses.0.append(&mut inputs_plutus.0) - } - if let Some(mut collateral_plutus) = self.collateral.get_plutus_input_scripts() { - used_langs.append(&mut self.collateral.get_used_plutus_lang_versions()); - plutus_witnesses.0.append(&mut collateral_plutus.0) - } - if let Some(mint_builder) = &self.mint { - used_langs.append(&mut mint_builder.get_used_plutus_lang_versions()); - plutus_witnesses.0.append(&mut mint_builder.get_plutus_witnesses().0) - } - if let Some(certs_builder) = &self.certs { - used_langs.append(&mut certs_builder.get_used_plutus_lang_versions()); - plutus_witnesses.0.append(&mut certs_builder.get_plutus_witnesses().0) - } - if let Some(withdrawals_builder) = self.withdrawals.clone() { - used_langs.append(&mut withdrawals_builder.get_used_plutus_lang_versions()); - plutus_witnesses.0.append(&mut withdrawals_builder.get_plutus_witnesses().0) - } - - let (_scripts, mut datums, redeemers) = plutus_witnesses.collect(); - for lang in used_langs { - match cost_models.get(&lang) { - Some(cost) => { - retained_cost_models.insert(&lang, &cost); - } - _ => { - return Err(JsError::from_str(&format!( - "Missing cost model for language version: {:?}", - lang - ))) - } - } - } - - if let Some(extra_datum) = &self.extra_datums { - if datums.is_none() { - datums = Some(PlutusList::new()); - } - - for datum in extra_datum { - if let Some(datums) = &mut datums { - datums.add(datum); - } - } - } - - if datums.is_some() || redeemers.len() > 0 || retained_cost_models.len() > 0 { - self.script_data_hash = Some(hash_script_data( - &redeemers, - &retained_cost_models, - datums, - )); - } - - Ok(()) - } - - /// Sets the specified hash value. - /// Alternatively you can use `.calc_script_data_hash` to calculate the hash automatically. - /// Or use `.remove_script_data_hash` to delete the previously set value - pub fn set_script_data_hash(&mut self, hash: &ScriptDataHash) { - self.script_data_hash = Some(hash.clone()); - } - - /// Deletes any previously set plutus data hash value. - /// Use `.set_script_data_hash` or `.calc_script_data_hash` to set it. - pub fn remove_script_data_hash(&mut self) { - self.script_data_hash = None; - } - - pub fn add_required_signer(&mut self, key: &Ed25519KeyHash) { - self.required_signers.add(key); - } - - fn build_and_size(&self) -> Result<(TransactionBody, usize), JsError> { - let fee = self - .fee - .ok_or_else(|| JsError::from_str("Fee not specified"))?; - - let built = TransactionBody { - inputs: self.inputs.inputs(), - outputs: self.outputs.clone(), - fee, - ttl: self.ttl, - certs: self.certs.as_ref().map(|x| x.build()), - withdrawals: self.withdrawals.as_ref().map(|x| x.build()), - update: None, - auxiliary_data_hash: self.auxiliary_data.as_ref().map(|x| utils::hash_auxiliary_data(x)), - validity_start_interval: self.validity_start_interval, - mint: self.mint.as_ref().map(|x| x.build()), - script_data_hash: self.script_data_hash.clone(), - collateral: self.collateral.inputs_option(), - required_signers: self.required_signers.to_option(), - network_id: None, - collateral_return: self.collateral_return.clone(), - total_collateral: self.total_collateral.clone(), - reference_inputs: self.get_reference_inputs().to_option(), - }; - // we must build a tx with fake data (of correct size) to check the final Transaction size - let full_tx = fake_full_tx(self, built)?; - let full_tx_size = full_tx.to_bytes().len(); - return Ok((full_tx.body, full_tx_size)); - } - - pub fn full_size(&self) -> Result { - return self.build_and_size().map(|r| r.1); - } - - pub fn output_sizes(&self) -> Vec { - return self.outputs.0.iter().map(|o| o.to_bytes().len()).collect(); - } - - /// Returns object the body of the new transaction - /// Auxiliary data itself is not included - /// You can use `get_auxiliary_data` or `build_tx` - pub fn build(&self) -> Result { - let (body, full_tx_size) = self.build_and_size()?; - if full_tx_size > self.config.max_tx_size as usize { - Err(JsError::from_str(&format!( - "Maximum transaction size of {} exceeded. Found: {}", - self.config.max_tx_size, full_tx_size - ))) - } else { - Ok(body) - } - } - - fn get_combined_native_scripts(&self) -> Option { - let mut ns = NativeScripts::new(); - if let Some(input_scripts) = self.inputs.get_native_input_scripts() { - input_scripts.0.iter().for_each(|s| { - ns.add(s); - }); - } - if let Some(input_scripts) = self.collateral.get_native_input_scripts() { - input_scripts.0.iter().for_each(|s| { - ns.add(s); - }); - } - if let Some(mint_builder) = &self.mint { - mint_builder.get_native_scripts().0.iter().for_each(|s| { - ns.add(s); - }); - } - if let Some(certificates_builder) = &self.certs { - certificates_builder.get_native_scripts().0.iter().for_each(|s| { - ns.add(s); - }); - } - if let Some(withdrawals_builder) = &self.withdrawals { - withdrawals_builder.get_native_scripts().0.iter().for_each(|s| { - ns.add(s); - }); - } - - if ns.len() > 0 { - Some(ns) - } else { - None - } - } - - fn get_combined_plutus_scripts(&self) -> Option { - let mut res = PlutusWitnesses::new(); - if let Some(scripts) = self.inputs.get_plutus_input_scripts() { - scripts.0.iter().for_each(|s| { - res.add(s); - }) - } - if let Some(scripts) = self.collateral.get_plutus_input_scripts() { - scripts.0.iter().for_each(|s| { - res.add(s); - }) - } - if let Some(mint_builder) = &self.mint { - mint_builder.get_plutus_witnesses().0.iter().for_each(|s| { - res.add(s); - }) - } - if let Some(certificates_builder) = &self.certs { - certificates_builder.get_plutus_witnesses().0.iter().for_each(|s| { - res.add(s); - }) - } - if let Some(withdrawals_builder) = &self.withdrawals { - withdrawals_builder.get_plutus_witnesses().0.iter().for_each(|s| { - res.add(s); - }) - } - if res.len() > 0 { - Some(res) - } else { - None - } - } - - // This function should be producing the total witness-set - // that is created by the tx-builder itself, - // before the transaction is getting signed by the actual wallet. - // E.g. scripts or something else that has been used during the tx preparation - fn get_witness_set(&self) -> TransactionWitnessSet { - let mut wit = TransactionWitnessSet::new(); - if let Some(scripts) = self.get_combined_native_scripts() { - wit.set_native_scripts(&scripts); - } - let mut all_datums = None; - if let Some(pw) = self.get_combined_plutus_scripts() { - let (scripts, datums, redeemers) = pw.collect(); - wit.set_plutus_scripts(&scripts); - all_datums = datums; - wit.set_redeemers(&redeemers); - } - - if let Some(extra_datum) = &self.extra_datums { - if all_datums.is_none() { - all_datums = Some(PlutusList::new()); - } - - for datum in extra_datum { - if let Some(datums) = &mut all_datums { - datums.add(datum); - } - } - } - - if let Some(datums) = &all_datums { - wit.set_plutus_data(datums); - } - - wit - } - - fn has_plutus_inputs(&self) -> bool { - if self.inputs.has_plutus_scripts() { - return true; - } - if self.mint.as_ref().map_or(false, |m| m.has_plutus_scripts()) { - return true; - } - if self.certs.as_ref().map_or(false, |c| c.has_plutus_scripts()) { - return true; - } - if self.withdrawals.as_ref().map_or(false, |w| w.has_plutus_scripts()) { - return true; - } - - return false; - - } - - /// Returns full Transaction object with the body and the auxiliary data - /// NOTE: witness_set will contain all mint_scripts if any been added or set - /// NOTE: is_valid set to true - /// NOTE: Will fail in case there are any script inputs added with no corresponding witness - pub fn build_tx(&self) -> Result { - if self.count_missing_input_scripts() > 0 { - return Err(JsError::from_str( - "There are some script inputs added that don't have the corresponding script provided as a witness!", - )); - } - if self.has_plutus_inputs() { - if self.script_data_hash.is_none() { - return Err(JsError::from_str( - "Plutus inputs are present, but script data hash is not specified", - )); - } - if self.collateral.len() == 0 { - return Err(JsError::from_str( - "Plutus inputs are present, but no collateral inputs are added", - )); - } - } - self.build_tx_unsafe() - } - - /// Similar to `.build_tx()` but will NOT fail in case there are missing script witnesses - pub fn build_tx_unsafe(&self) -> Result { - Ok(Transaction { - body: self.build()?, - witness_set: self.get_witness_set(), - is_valid: true, - auxiliary_data: self.auxiliary_data.clone(), - }) - } - - /// warning: sum of all parts of a transaction must equal 0. You cannot just set the fee to the min value and forget about it - /// warning: min_fee may be slightly larger than the actual minimum fee (ex: a few lovelaces) - /// this is done to simplify the library code, but can be fixed later - pub fn min_fee(&self) -> Result { - let mut self_copy = self.clone(); - self_copy.set_fee(&to_bignum(0x1_00_00_00_00)); - min_fee(&self_copy) - } -} - -#[cfg(test)] -mod tests { - use super::output_builder::TransactionOutputBuilder; - use super::*; - use crate::fakes::{fake_base_address, fake_bytes_32, fake_data_hash, fake_key_hash, fake_policy_id, fake_script_hash, fake_tx_hash, fake_tx_input, fake_tx_input2, fake_value, fake_value2}; - use crate::tx_builder_constants::TxBuilderConstants; - use fees::*; - use crate::tx_builder::script_structs::{PlutusScriptSource}; - use crate::tx_builder::tx_inputs_builder::{InputsWithScriptWitness, InputWithScriptWitness }; - - const MAX_VALUE_SIZE: u32 = 4000; - const MAX_TX_SIZE: u32 = 8000; // might be out of date but suffices for our tests - // this is what is used in mainnet - static COINS_PER_UTXO_WORD: u64 = 34_482; - - fn genesis_id() -> TransactionHash { - TransactionHash::from([0u8; TransactionHash::BYTE_COUNT]) - } - - fn root_key_15() -> Bip32PrivateKey { - // art forum devote street sure rather head chuckle guard poverty release quote oak craft enemy - let entropy = [ - 0x0c, 0xcb, 0x74, 0xf3, 0x6b, 0x7d, 0xa1, 0x64, 0x9a, 0x81, 0x44, 0x67, 0x55, 0x22, - 0xd4, 0xd8, 0x09, 0x7c, 0x64, 0x12, - ]; - Bip32PrivateKey::from_bip39_entropy(&entropy, &[]) - } - - fn harden(index: u32) -> u32 { - index | 0x80_00_00_00 - } - - #[test] - fn check_fake_private_key() { - let fpk = fake_private_key(); - assert_eq!( - fpk.to_bech32(), - "xprv1hretan5mml3tq2p0twkhq4tz4jvka7m2l94kfr6yghkyfar6m9wppc7h9unw6p65y23kakzct3695rs32z7vaw3r2lg9scmfj8ec5du3ufydu5yuquxcz24jlkjhsc9vsa4ufzge9s00fn398svhacse5su2awrw", - ); - assert_eq!( - fpk.to_public().to_bech32(), - "xpub1eamrnx3pph58yr5l4z2wghjpu2dt2f0rp0zq9qquqa39p52ct0xercjgmegfcpcdsy4t9ld90ps2epmtcjy3jtq77n8z20qe0m3pnfqntgrgj", - ); - } - - fn byron_address() -> Address { - ByronAddress::from_base58("Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3") - .unwrap() - .to_address() - } - - fn create_linear_fee(coefficient: u64, constant: u64) -> LinearFee { - LinearFee::new(&to_bignum(coefficient), &to_bignum(constant)) - } - - fn create_default_linear_fee() -> LinearFee { - create_linear_fee(500, 2) - } - - fn create_tx_builder_full( - linear_fee: &LinearFee, - pool_deposit: u64, - key_deposit: u64, - max_val_size: u32, - coins_per_utxo_word: u64, - ) -> TransactionBuilder { - let cfg = TransactionBuilderConfigBuilder::new() - .fee_algo(linear_fee) - .pool_deposit(&to_bignum(pool_deposit)) - .key_deposit(&to_bignum(key_deposit)) - .max_value_size(max_val_size) - .max_tx_size(MAX_TX_SIZE) - .coins_per_utxo_word(&to_bignum(coins_per_utxo_word)) - .ex_unit_prices(&ExUnitPrices::new( - &SubCoin::new(&to_bignum(577), &to_bignum(10000)), - &SubCoin::new(&to_bignum(721), &to_bignum(10000000)), - )) - .build() - .unwrap(); - TransactionBuilder::new(&cfg) - } - - fn create_tx_builder( - linear_fee: &LinearFee, - coins_per_utxo_word: u64, - pool_deposit: u64, - key_deposit: u64, - ) -> TransactionBuilder { - create_tx_builder_full( - linear_fee, - pool_deposit, - key_deposit, - MAX_VALUE_SIZE, - coins_per_utxo_word, - ) - } - - fn create_reallistic_tx_builder() -> TransactionBuilder { - create_tx_builder( - &create_linear_fee(44, 155381), - COINS_PER_UTXO_WORD, - 500000000, - 2000000, - ) - } - - fn create_tx_builder_with_fee_and_val_size( - linear_fee: &LinearFee, - max_val_size: u32, - ) -> TransactionBuilder { - create_tx_builder_full(linear_fee, 1, 1, max_val_size, 8) - } - - fn create_tx_builder_with_fee(linear_fee: &LinearFee) -> TransactionBuilder { - create_tx_builder(linear_fee, 8, 1, 1) - } - - fn create_tx_builder_with_fee_and_pure_change(linear_fee: &LinearFee) -> TransactionBuilder { - TransactionBuilder::new( - &TransactionBuilderConfigBuilder::new() - .fee_algo(linear_fee) - .pool_deposit(&to_bignum(1)) - .key_deposit(&to_bignum(1)) - .max_value_size(MAX_VALUE_SIZE) - .max_tx_size(MAX_TX_SIZE) - .coins_per_utxo_word(&to_bignum(8)) - .prefer_pure_change(true) - .build() - .unwrap(), - ) - } - - fn create_tx_builder_with_key_deposit(deposit: u64) -> TransactionBuilder { - create_tx_builder(&create_default_linear_fee(), 8, 1, deposit) - } - - fn create_default_tx_builder() -> TransactionBuilder { - create_tx_builder_with_fee(&create_default_linear_fee()) - } - - #[test] - fn build_tx_with_change() { - let mut tx_builder = create_default_tx_builder(); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - tx_builder.add_key_input( - &spend.to_raw_key().hash(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_coin(&to_bignum(222)) - .build() - .unwrap(), - ) - .unwrap(); - tx_builder.set_ttl(1000); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - let added_change = tx_builder.add_change_if_needed(&change_addr); - assert!(added_change.unwrap()); - assert_eq!(tx_builder.outputs.len(), 2); - assert_eq!( - tx_builder - .get_explicit_input() - .unwrap() - .checked_add(&tx_builder.get_implicit_input().unwrap()) - .unwrap(), - tx_builder - .get_explicit_output() - .unwrap() - .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) - .unwrap() - ); - assert_eq!(tx_builder.full_size().unwrap(), 285); - assert_eq!(tx_builder.output_sizes(), vec![62, 65]); - let _final_tx = tx_builder.build(); // just test that it doesn't throw - } - - #[test] - fn build_tx_with_change_with_datum() { - let mut tx_builder = create_default_tx_builder(); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - tx_builder.add_key_input( - &spend.to_raw_key().hash(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_coin(&to_bignum(222)) - .build() - .unwrap(), - ) - .unwrap(); - tx_builder.set_ttl(1000); - - let datum_hash = fake_data_hash(20); - let data_option = OutputDatum::new_data_hash(&datum_hash); - let (_, script_hash) = plutus_script_and_hash(15); - let change_cred = StakeCredential::from_scripthash(&script_hash); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - let added_change = tx_builder.add_change_if_needed_with_datum(&change_addr, &data_option); - assert!(added_change.unwrap()); - assert_eq!(tx_builder.outputs.len(), 2); - assert_eq!( - tx_builder - .get_explicit_input() - .unwrap() - .checked_add(&tx_builder.get_implicit_input().unwrap()) - .unwrap(), - tx_builder - .get_explicit_output() - .unwrap() - .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) - .unwrap() - ); - assert_eq!(tx_builder.full_size().unwrap(), 319); - assert_eq!(tx_builder.output_sizes(), vec![62, 99]); - let _final_tx = tx_builder.build(); // just test that it doesn't throw - } - - #[test] - fn build_tx_without_change() { - let mut tx_builder = create_default_tx_builder(); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - tx_builder.add_key_input( - &spend.to_raw_key().hash(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_coin(&to_bignum(880_000)) - .build() - .unwrap(), - ) - .unwrap(); - tx_builder.set_ttl(1000); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - let added_change = tx_builder.add_change_if_needed(&change_addr); - assert!(!added_change.unwrap()); - assert_eq!(tx_builder.outputs.len(), 1); - assert_eq!( - tx_builder - .get_explicit_input() - .unwrap() - .checked_add(&tx_builder.get_implicit_input().unwrap()) - .unwrap(), - tx_builder - .get_explicit_output() - .unwrap() - .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) - .unwrap() - ); - let _final_tx = tx_builder.build(); // just test that it doesn't throw - } - - #[test] - fn build_tx_with_certs() { - let mut tx_builder = create_tx_builder_with_key_deposit(1_000_000); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - tx_builder.add_key_input( - &spend.to_raw_key().hash(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(5_000_000)), - ); - tx_builder.set_ttl(1000); - - let mut certs = Certificates::new(); - certs.add(&Certificate::new_stake_registration( - &StakeRegistration::new(&stake_cred), - )); - certs.add(&Certificate::new_stake_delegation(&StakeDelegation::new( - &stake_cred, - &stake.to_raw_key().hash(), // in reality, this should be the pool owner's key, not ours - ))); - tx_builder.set_certs(&certs).unwrap(); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - tx_builder.add_change_if_needed(&change_addr).unwrap(); - assert_eq!(tx_builder.min_fee().unwrap().to_str(), "214002"); - assert_eq!(tx_builder.get_fee_if_set().unwrap().to_str(), "214002"); - assert_eq!(tx_builder.get_deposit().unwrap().to_str(), "1000000"); - assert_eq!(tx_builder.outputs.len(), 1); - assert_eq!( - tx_builder - .get_explicit_input() - .unwrap() - .checked_add(&tx_builder.get_implicit_input().unwrap()) - .unwrap(), - tx_builder - .get_explicit_output() - .unwrap() - .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) - .unwrap() - .checked_add(&Value::new(&tx_builder.get_deposit().unwrap())) - .unwrap() - ); - let _final_tx = tx_builder.build(); // just test that it doesn't throw - } - - #[test] - fn build_tx_exact_amount() { - // transactions where sum(input) == sum(output) exact should pass - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(0, 0)); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - tx_builder.add_key_input( - &&spend.to_raw_key().hash(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(222)), - ); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_coin(&to_bignum(222)) - .build() - .unwrap(), - ) - .unwrap(); - tx_builder.set_ttl(0); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); - assert_eq!(added_change, false); - let final_tx = tx_builder.build().unwrap(); - assert_eq!(final_tx.outputs().len(), 1); - } - - #[test] - fn build_tx_exact_change() { - // transactions where we have exactly enough ADA to add change should pass - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(0, 0)); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - tx_builder.add_key_input( - &&spend.to_raw_key().hash(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(700)), - ); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_coin(&to_bignum(222)) - .build() - .unwrap(), - ) - .unwrap(); - tx_builder.set_ttl(0); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); - assert_eq!(added_change, true); - let final_tx = tx_builder.build().unwrap(); - assert_eq!(final_tx.outputs().len(), 2); - assert_eq!(final_tx.outputs().get(1).amount().coin().to_str(), "478"); - } - - #[test] - #[should_panic] - fn build_tx_insufficient_deposit() { - // transactions should fail with insufficient fees if a deposit is required - let mut tx_builder = create_tx_builder_with_key_deposit(5); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - tx_builder.add_key_input( - &&spend.to_raw_key().hash(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(5)), - ); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_coin(&to_bignum(5)) - .build() - .unwrap(), - ) - .unwrap(); - tx_builder.set_ttl(0); - - // add a cert which requires a deposit - let mut certs = Certificates::new(); - certs.add(&Certificate::new_stake_registration( - &StakeRegistration::new(&stake_cred), - )); - tx_builder.set_certs(&certs); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - - tx_builder.add_change_if_needed(&change_addr).unwrap(); - } - - #[test] - fn build_tx_with_inputs() { - let mut tx_builder = create_default_tx_builder(); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - - { - assert_eq!( - tx_builder - .fee_for_input( - &EnterpriseAddress::new(NetworkInfo::testnet().network_id(), &spend_cred) - .to_address(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)) - ) - .unwrap() - .to_str(), - "69500" - ); - tx_builder.add_input( - &EnterpriseAddress::new(NetworkInfo::testnet().network_id(), &spend_cred) - .to_address(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - } - tx_builder.add_input( - &BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(), - &TransactionInput::new(&genesis_id(), 1), - &Value::new(&to_bignum(1_000_000)), - ); - tx_builder.add_input( - &PointerAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &Pointer::new_pointer(&to_bignum(0), &to_bignum(0), &to_bignum(0)), - ) - .to_address(), - &TransactionInput::new(&genesis_id(), 2), - &Value::new(&to_bignum(1_000_000)), - ); - tx_builder.add_input( - &ByronAddress::icarus_from_key(&spend, NetworkInfo::testnet().protocol_magic()) - .to_address(), - &TransactionInput::new(&genesis_id(), 3), - &Value::new(&to_bignum(1_000_000)), - ); - - assert_eq!(tx_builder.inputs.len(), 4); - } - - #[test] - fn add_ref_inputs_to_builder() { - let mut tx_builder = create_default_tx_builder(); - - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 1)); - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 2)); - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 3)); - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 4)); - - assert_eq!(tx_builder.reference_inputs.len(), 4); - } - - #[test] - fn build_tx_with_script_ref() { - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(0, 1)); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 1)); - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 2)); - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 3)); - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 4)); - - tx_builder.add_input( - &PointerAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &Pointer::new_pointer(&to_bignum(0), &to_bignum(0), &to_bignum(0)), - ) - .to_address(), - &TransactionInput::new(&genesis_id(), 2), - &Value::new(&to_bignum(1_000_000)), - ); - - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - - let output_amount = Value::new(&to_bignum(500)); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_value(&output_amount) - .build() - .unwrap(), - ) - .unwrap(); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - - let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); - assert_eq!(added_change, true); - let final_tx = tx_builder.build().unwrap(); - assert_eq!(final_tx.outputs().len(), 2); - assert_eq!(final_tx.reference_inputs().unwrap().len(), 4); - assert_eq!(final_tx.outputs().get(1).amount().coin(), to_bignum(999499)); - } - - #[test] - fn serialization_tx_body_with_script_ref() { - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(0, 1)); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 1)); - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 2)); - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 3)); - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 4)); - - tx_builder.add_input( - &PointerAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &Pointer::new_pointer(&to_bignum(0), &to_bignum(0), &to_bignum(0)), - ) - .to_address(), - &TransactionInput::new(&genesis_id(), 2), - &Value::new(&to_bignum(1_000_000)), - ); - - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - - let output_amount = Value::new(&to_bignum(500)); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_value(&output_amount) - .build() - .unwrap(), - ) - .unwrap(); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - - tx_builder.add_change_if_needed(&change_addr).unwrap(); - let final_tx = tx_builder.build().unwrap(); - - let deser_t = TransactionBody::from_bytes(final_tx.to_bytes()).unwrap(); - - assert_eq!(deser_t.to_bytes(), final_tx.to_bytes()); - } - - #[test] - fn json_serialization_tx_body_with_script_ref() { - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(0, 1)); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 1)); - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 2)); - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 3)); - tx_builder.add_reference_input(&TransactionInput::new(&genesis_id(), 4)); - - tx_builder.add_input( - &PointerAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &Pointer::new_pointer(&to_bignum(0), &to_bignum(0), &to_bignum(0)), - ) - .to_address(), - &TransactionInput::new(&genesis_id(), 2), - &Value::new(&to_bignum(1_000_000)), - ); - - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - - let output_amount = Value::new(&to_bignum(500)); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_value(&output_amount) - .build() - .unwrap(), - ) - .unwrap(); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - - tx_builder.add_change_if_needed(&change_addr).unwrap(); - let final_tx = tx_builder.build().unwrap(); - - let json_tx_body = final_tx.to_json().unwrap(); - let deser_t = TransactionBody::from_json(json_tx_body.as_str()).unwrap(); - - assert_eq!(deser_t.to_bytes(), final_tx.to_bytes()); - assert_eq!(deser_t.to_json().unwrap(), final_tx.to_json().unwrap()); - } - - #[test] - fn build_tx_with_mint_all_sent() { - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(0, 1)); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - - // Input with 150 coins - tx_builder.add_input( - &EnterpriseAddress::new(NetworkInfo::testnet().network_id(), &spend_cred).to_address(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(500)), - ); - - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - - let (min_script, policy_id) = mint_script_and_policy(0); - let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - let amount = to_bignum(1234); - - // Adding mint of the asset - which should work as an input - tx_builder.add_mint_asset(&min_script, &name, Int::new(&amount)); - - let mut ass = Assets::new(); - ass.insert(&name, &amount); - let mut mass = MultiAsset::new(); - mass.insert(&policy_id, &ass); - - // One coin and the minted asset goes into the output - let mut output_amount = Value::new(&to_bignum(264)); - output_amount.set_multiasset(&mass); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_value(&output_amount) - .build() - .unwrap(), - ) - .unwrap(); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - - let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); - assert!(added_change); - assert_eq!(tx_builder.outputs.len(), 2); - - // Change must be one remaining coin because fee is one constant coin - let change = tx_builder.outputs.get(1).amount(); - assert_eq!(change.coin(), to_bignum(235)); - assert!(change.multiasset().is_none()); - } - - #[test] - fn build_tx_with_mint_in_change() { - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(0, 1)); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - - // Input with 600 coins - tx_builder.add_input( - &EnterpriseAddress::new(NetworkInfo::testnet().network_id(), &spend_cred).to_address(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(600)), - ); - - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - - let (min_script, policy_id) = mint_script_and_policy(0); - let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - - let amount_minted = to_bignum(1000); - let amount_sent = to_bignum(500); - - // Adding mint of the asset - which should work as an input - tx_builder.add_mint_asset(&min_script, &name, Int::new(&amount_minted)); - - let mut ass = Assets::new(); - ass.insert(&name, &amount_sent); - let mut mass = MultiAsset::new(); - mass.insert(&policy_id, &ass); - - // One coin and the minted asset goes into the output - let mut output_amount = Value::new(&to_bignum(300)); - output_amount.set_multiasset(&mass); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_value(&output_amount) - .build() - .unwrap(), - ) - .unwrap(); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - - let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); - assert!(added_change); - assert_eq!(tx_builder.outputs.len(), 2); - - // Change must be one remaining coin because fee is one constant coin - let change = tx_builder.outputs.get(1).amount(); - assert_eq!(change.coin(), to_bignum(299)); - assert!(change.multiasset().is_some()); - - let change_assets = change.multiasset().unwrap(); - let change_asset = change_assets.get(&policy_id).unwrap(); - assert_eq!( - change_asset.get(&name).unwrap(), - amount_minted.checked_sub(&amount_sent).unwrap(), - ); - } - - #[test] - fn change_with_input_and_mint_not_enough_ada() { - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(1, 1)); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - - let (min_script, policy_id) = mint_script_and_policy(0); - let asset_name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - - let amount_minted = to_bignum(1000); - let amount_sent = to_bignum(500); - let amount_input_amount = to_bignum(600); - - let mut asset_input = Assets::new(); - asset_input.insert(&asset_name, &amount_input_amount); - let mut mass_input = MultiAsset::new(); - mass_input.insert(&policy_id, &asset_input); - - // Input with 600 coins - tx_builder.add_input( - &EnterpriseAddress::new(NetworkInfo::testnet().network_id(), &spend_cred).to_address(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(600)), - ); - - tx_builder.add_input( - &EnterpriseAddress::new(NetworkInfo::testnet().network_id(), &spend_cred).to_address(), - &TransactionInput::new(&genesis_id(), 1), - &Value::new_with_assets(&to_bignum(1), &mass_input), - ); - - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ).to_address(); - - // Adding mint of the asset - which should work as an input - tx_builder.add_mint_asset(&min_script, &asset_name, Int::new(&amount_minted)); - - let mut asset = Assets::new(); - asset.insert(&asset_name, &amount_sent); - let mut mass = MultiAsset::new(); - mass.insert(&policy_id, &asset); - - // One coin and the minted asset goes into the output - let mut output_amount = Value::new(&to_bignum(400)); - output_amount.set_multiasset(&mass); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_value(&output_amount) - .build() - .unwrap(), - ) - .unwrap(); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - - let added_change = tx_builder.add_change_if_needed(&change_addr); - assert!(added_change.is_err()); - } - - #[test] - fn change_with_input_and_mint_not_enough_assets() { - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(1, 1)); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - - let (min_script, policy_id) = mint_script_and_policy(0); - let asset_name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - - let amount_minted = to_bignum(1000); - let amount_sent = to_bignum(100000); - let amount_input_amount = to_bignum(600); - - let mut asset_input = Assets::new(); - asset_input.insert(&asset_name, &amount_input_amount); - let mut mass_input = MultiAsset::new(); - mass_input.insert(&policy_id, &asset_input); - - // Input with 600 coins - tx_builder.add_input( - &EnterpriseAddress::new(NetworkInfo::testnet().network_id(), &spend_cred).to_address(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(100000)), - ); - - tx_builder.add_input( - &EnterpriseAddress::new(NetworkInfo::testnet().network_id(), &spend_cred).to_address(), - &TransactionInput::new(&genesis_id(), 1), - &Value::new_with_assets(&to_bignum(1), &mass_input), - ); - - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ).to_address(); - - // Adding mint of the asset - which should work as an input - tx_builder.add_mint_asset(&min_script, &asset_name, Int::new(&amount_minted)); - - let mut asset = Assets::new(); - asset.insert(&asset_name, &amount_sent); - let mut mass = MultiAsset::new(); - mass.insert(&policy_id, &asset); - - // One coin and the minted asset goes into the output - let mut output_amount = Value::new(&to_bignum(400)); - output_amount.set_multiasset(&mass); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_value(&output_amount) - .build() - .unwrap(), - ) - .unwrap(); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - - let added_change = tx_builder.add_change_if_needed(&change_addr); - assert!(added_change.is_err()); - } - - #[test] - fn build_tx_with_native_assets_change() { - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(0, 1)); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let policy_id = &PolicyID::from([0u8; 28]); - let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - - let ma_input1 = 100; - let ma_input2 = 200; - let ma_output1 = 60; - - let multiassets = [ma_input1, ma_input2, ma_output1] - .iter() - .map(|input| { - let mut multiasset = MultiAsset::new(); - multiasset.insert(policy_id, &{ - let mut assets = Assets::new(); - assets.insert(&name, &to_bignum(*input)); - assets - }); - multiasset - }) - .collect::>(); - - for (i, (multiasset, ada)) in multiassets - .iter() - .zip([100u64, 1000].iter().cloned().map(to_bignum)) - .enumerate() - { - let mut input_amount = Value::new(&ada); - input_amount.set_multiasset(multiasset); - - tx_builder.add_key_input( - &&spend.to_raw_key().hash(), - &TransactionInput::new(&genesis_id(), i as u32), - &input_amount, - ); - } - - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - - let mut output_amount = Value::new(&to_bignum(500)); - output_amount.set_multiasset(&multiassets[2]); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_value(&output_amount) - .build() - .unwrap(), - ) - .unwrap(); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - - let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); - assert_eq!(added_change, true); - let final_tx = tx_builder.build().unwrap(); - assert_eq!(final_tx.outputs().len(), 2); - assert_eq!( - final_tx - .outputs() - .get(1) - .amount() - .multiasset() - .unwrap() - .get(policy_id) - .unwrap() - .get(&name) - .unwrap(), - to_bignum(ma_input1 + ma_input2 - ma_output1) - ); - assert_eq!(final_tx.outputs().get(1).amount().coin(), to_bignum(599)); - } - - #[test] - fn build_tx_with_native_assets_change_and_purification() { - let coin_per_utxo_word = to_bignum(8); - // Prefer pure change! - let mut tx_builder = create_tx_builder_with_fee_and_pure_change(&create_linear_fee(0, 1)); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let policy_id = &PolicyID::from([0u8; 28]); - let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - - let ma_input1 = 100; - let ma_input2 = 200; - let ma_output1 = 60; - - let multiassets = [ma_input1, ma_input2, ma_output1] - .iter() - .map(|input| { - let mut multiasset = MultiAsset::new(); - multiasset.insert(policy_id, &{ - let mut assets = Assets::new(); - assets.insert(&name, &to_bignum(*input)); - assets - }); - multiasset - }) - .collect::>(); - - for (i, (multiasset, ada)) in multiassets - .iter() - .zip([100u64, 1000].iter().cloned().map(to_bignum)) - .enumerate() - { - let mut input_amount = Value::new(&ada); - input_amount.set_multiasset(multiasset); - - tx_builder.add_key_input( - &&spend.to_raw_key().hash(), - &TransactionInput::new(&genesis_id(), i as u32), - &input_amount, - ); - } - - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - - let mut output_amount = Value::new(&to_bignum(600)); - output_amount.set_multiasset(&multiassets[2]); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_value(&output_amount) - .build() - .unwrap(), - ) - .unwrap(); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - - let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); - assert_eq!(added_change, true); - let final_tx = tx_builder.build().unwrap(); - assert_eq!(final_tx.outputs().len(), 3); - assert_eq!(final_tx.outputs().get(0).amount().coin(), to_bignum(600)); - assert_eq!( - final_tx - .outputs() - .get(1) - .amount() - .multiasset() - .unwrap() - .get(policy_id) - .unwrap() - .get(&name) - .unwrap(), - to_bignum(ma_input1 + ma_input2 - ma_output1) - ); - // The first change output that contains all the tokens contain minimum required Coin - let min_coin_for_dirty_change = min_ada_required( - &final_tx.outputs().get(1).amount(), - false, - &coin_per_utxo_word, - ) - .unwrap(); - assert_eq!( - final_tx.outputs().get(1).amount().coin(), - min_coin_for_dirty_change - ); - assert_eq!(final_tx.outputs().get(2).amount().coin(), to_bignum(236)); - assert_eq!(final_tx.outputs().get(2).amount().multiasset(), None); - } - - #[test] - fn build_tx_with_native_assets_change_and_no_purification_cuz_not_enough_pure_coin() { - // Prefer pure change! - let mut tx_builder = create_tx_builder_with_fee_and_pure_change(&create_linear_fee(1, 1)); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let policy_id = &PolicyID::from([0u8; 28]); - let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - - let ma_input1 = 100; - let ma_input2 = 200; - let ma_output1 = 60; - - let multiassets = [ma_input1, ma_input2, ma_output1] - .iter() - .map(|input| { - let mut multiasset = MultiAsset::new(); - multiasset.insert(policy_id, &{ - let mut assets = Assets::new(); - assets.insert(&name, &to_bignum(*input)); - assets - }); - multiasset - }) - .collect::>(); - - for (i, (multiasset, ada)) in multiassets - .iter() - .zip([300u64, 900].iter().cloned().map(to_bignum)) - .enumerate() - { - let mut input_amount = Value::new(&ada); - input_amount.set_multiasset(multiasset); - - tx_builder.add_key_input( - &&spend.to_raw_key().hash(), - &TransactionInput::new(&genesis_id(), i as u32), - &input_amount, - ); - } - - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - - let mut output_amount = Value::new(&to_bignum(300)); - output_amount.set_multiasset(&multiassets[2]); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_value(&output_amount) - .build() - .unwrap(), - ) - .unwrap(); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - - let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); - assert_eq!(added_change, true); - let final_tx = tx_builder.build().unwrap(); - assert_eq!(final_tx.outputs().len(), 2); - assert_eq!(final_tx.outputs().get(0).amount().coin(), to_bignum(300)); - assert_eq!( - final_tx - .outputs() - .get(1) - .amount() - .multiasset() - .unwrap() - .get(policy_id) - .unwrap() - .get(&name) - .unwrap(), - to_bignum(ma_input1 + ma_input2 - ma_output1) - ); - // The single change output contains more Coin then minimal utxo value - // But not enough to cover the additional fee for a separate output - assert_eq!(final_tx.outputs().get(1).amount().coin(), to_bignum(499)); - } - - #[test] - #[should_panic] - fn build_tx_leftover_assets() { - let mut tx_builder = create_default_tx_builder(); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - - // add an input that contains an asset not present in the output - let policy_id = &PolicyID::from([0u8; 28]); - let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - let mut input_amount = Value::new(&to_bignum(1_000_000)); - let mut input_multiasset = MultiAsset::new(); - input_multiasset.insert(policy_id, &{ - let mut assets = Assets::new(); - assets.insert(&name, &to_bignum(100)); - assets - }); - input_amount.set_multiasset(&input_multiasset); - tx_builder.add_key_input( - &spend.to_raw_key().hash(), - &TransactionInput::new(&genesis_id(), 0), - &input_amount, - ); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_coin(&to_bignum(880_000)) - .build() - .unwrap(), - ) - .unwrap(); - tx_builder.set_ttl(1000); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - let added_change = tx_builder.add_change_if_needed(&change_addr); - assert!(!added_change.unwrap()); - assert_eq!(tx_builder.outputs.len(), 1); - assert_eq!( - tx_builder - .get_explicit_input() - .unwrap() - .checked_add(&tx_builder.get_implicit_input().unwrap()) - .unwrap(), - tx_builder - .get_explicit_output() - .unwrap() - .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) - .unwrap() - ); - let _final_tx = tx_builder.build(); // just test that it doesn't throw - } - - #[test] - fn build_tx_burn_less_than_min_ada() { - // with this mainnet value we should end up with a final min_ada_required of just under 1_000_000 - let mut tx_builder = create_reallistic_tx_builder(); - - let output_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b", - ) - .unwrap(); - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&output_addr.to_address()) - .next() - .unwrap() - .with_value(&Value::new(&to_bignum(2_000_000))) - .build() - .unwrap(), - ) - .unwrap(); - - tx_builder.add_input( - &ByronAddress::from_base58( - "Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3", - ) - .unwrap() - .to_address(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(2_400_000)), - ); - - tx_builder.set_ttl(1); - - let change_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho", - ) - .unwrap(); - let added_change = tx_builder.add_change_if_needed(&change_addr.to_address()); - assert!(!added_change.unwrap()); - assert_eq!(tx_builder.outputs.len(), 1); - assert_eq!( - tx_builder - .get_explicit_input() - .unwrap() - .checked_add(&tx_builder.get_implicit_input().unwrap()) - .unwrap(), - tx_builder - .get_explicit_output() - .unwrap() - .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) - .unwrap() - ); - let _final_tx = tx_builder.build(); // just test that it doesn't throw - } - - #[test] - fn build_tx_burn_empty_assets() { - let mut tx_builder = create_reallistic_tx_builder(); - - let output_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b", - ) - .unwrap(); - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&output_addr.to_address()) - .next() - .unwrap() - .with_value(&Value::new(&to_bignum(2_000_000))) - .build() - .unwrap(), - ) - .unwrap(); - - let mut input_value = Value::new(&to_bignum(2_400_000)); - input_value.set_multiasset(&MultiAsset::new()); - tx_builder.add_input( - &ByronAddress::from_base58( - "Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3", - ) - .unwrap() - .to_address(), - &TransactionInput::new(&genesis_id(), 0), - &input_value, - ); - - tx_builder.set_ttl(1); - - let change_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho", - ) - .unwrap(); - let added_change = tx_builder.add_change_if_needed(&change_addr.to_address()); - assert!(!added_change.unwrap()); - assert_eq!(tx_builder.outputs.len(), 1); - assert_eq!( - tx_builder - .get_explicit_input() - .unwrap() - .checked_add(&tx_builder.get_implicit_input().unwrap()) - .unwrap() - .coin(), - tx_builder - .get_explicit_output() - .unwrap() - .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) - .unwrap() - .coin() - ); - let _final_tx = tx_builder.build(); // just test that it doesn't throw - } - - #[test] - fn build_tx_no_useless_multiasset() { - let mut tx_builder = create_reallistic_tx_builder(); - - let policy_id = &PolicyID::from([0u8; 28]); - let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - - // add an output that uses up all the token but leaves ADA - let mut input_amount = Value::new(&to_bignum(5_000_000)); - let mut input_multiasset = MultiAsset::new(); - input_multiasset.insert(policy_id, &{ - let mut assets = Assets::new(); - assets.insert(&name, &to_bignum(100)); - assets - }); - input_amount.set_multiasset(&input_multiasset); - - tx_builder.add_input( - &ByronAddress::from_base58( - "Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3", - ) - .unwrap() - .to_address(), - &TransactionInput::new(&genesis_id(), 0), - &input_amount, - ); - - // add an input that contains an asset & ADA - let mut output_amount = Value::new(&to_bignum(2_000_000)); - let mut output_multiasset = MultiAsset::new(); - output_multiasset.insert(policy_id, &{ - let mut assets = Assets::new(); - assets.insert(&name, &to_bignum(100)); - assets - }); - output_amount.set_multiasset(&output_multiasset); - - let output_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b", - ) - .unwrap(); - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&output_addr.to_address()) - .next() - .unwrap() - .with_value(&output_amount) - .build() - .unwrap(), - ) - .unwrap(); - - tx_builder.set_ttl(1); - - let change_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho", - ) - .unwrap(); - let added_change = tx_builder.add_change_if_needed(&change_addr.to_address()); - assert!(added_change.unwrap()); - assert_eq!(tx_builder.outputs.len(), 2); - let final_tx = tx_builder.build().unwrap(); - let change_output = final_tx.outputs().get(1); - let change_assets = change_output.amount().multiasset(); - - // since all tokens got sent in the output - // the change should be only ADA and not have any multiasset struct in it - assert!(change_assets.is_none()); - } - - fn create_multiasset() -> (MultiAsset, [ScriptHash; 3], [AssetName; 3]) { - let policy_ids = [ - fake_policy_id(0), - fake_policy_id(1), - fake_policy_id(2), - ]; - let names = [ - AssetName::new(vec![99u8; 32]).unwrap(), - AssetName::new(vec![0u8, 1, 2, 3]).unwrap(), - AssetName::new(vec![4u8, 5, 6, 7, 8, 9]).unwrap(), - ]; - let multiasset = policy_ids.iter().zip(names.iter()).fold( - MultiAsset::new(), - |mut acc, (policy_id, name)| { - acc.insert(policy_id, &{ - let mut assets = Assets::new(); - assets.insert(&name, &to_bignum(500)); - assets - }); - acc - }, - ); - return (multiasset, policy_ids, names); - } - - #[test] - fn build_tx_add_change_split_nfts() { - let max_value_size = 100; // super low max output size to test with fewer assets - let mut tx_builder = - create_tx_builder_with_fee_and_val_size(&create_linear_fee(0, 1), max_value_size); - - let (multiasset, policy_ids, names) = create_multiasset(); - - let mut input_value = Value::new(&to_bignum(1000)); - input_value.set_multiasset(&multiasset); - - tx_builder.add_input( - &ByronAddress::from_base58( - "Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3", - ) - .unwrap() - .to_address(), - &TransactionInput::new(&genesis_id(), 0), - &input_value, - ); - - let output_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b", - ) - .unwrap() - .to_address(); - let output_amount = Value::new(&to_bignum(208)); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&output_addr) - .next() - .unwrap() - .with_value(&output_amount) - .build() - .unwrap(), - ) - .unwrap(); - - let change_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho", - ) - .unwrap() - .to_address(); - - let added_change = tx_builder.add_change_if_needed(&change_addr).unwrap(); - assert_eq!(added_change, true); - let final_tx = tx_builder.build().unwrap(); - assert_eq!(final_tx.outputs().len(), 3); - for (policy_id, asset_name) in policy_ids.iter().zip(names.iter()) { - assert!(final_tx - .outputs - .0 - .iter() - .find(|output| output.amount.multiasset.as_ref().map_or_else( - || false, - |ma| ma - .0 - .iter() - .find(|(pid, a)| *pid == policy_id - && a.0.iter().find(|(name, _)| *name == asset_name).is_some()) - .is_some() - )) - .is_some()); - } - for output in final_tx.outputs.0.iter() { - assert!(output.amount.to_bytes().len() <= max_value_size as usize); - } - } - - #[test] - fn build_tx_too_big_output() { - let mut tx_builder = create_tx_builder_with_fee_and_val_size(&create_linear_fee(0, 1), 10); - - tx_builder.add_input( - &ByronAddress::from_base58( - "Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3", - ) - .unwrap() - .to_address(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(500)), - ); - - let output_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b", - ) - .unwrap() - .to_address(); - let mut output_amount = Value::new(&to_bignum(50)); - output_amount.set_multiasset(&create_multiasset().0); - - assert!(tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&output_addr) - .next() - .unwrap() - .with_value(&output_amount) - .build() - .unwrap() - ) - .is_err()); - } - - #[test] - fn build_tx_add_change_nfts_not_enough_ada() { - let mut tx_builder = create_tx_builder_with_fee_and_val_size( - &create_linear_fee(0, 1), - 150, // super low max output size to test with fewer assets - ); - - let policy_ids = [ - PolicyID::from([0u8; 28]), - PolicyID::from([1u8; 28]), - PolicyID::from([2u8; 28]), - ]; - let names = [ - AssetName::new(vec![99u8; 32]).unwrap(), - AssetName::new(vec![0u8, 1, 2, 3]).unwrap(), - AssetName::new(vec![4u8, 5, 6, 7, 8, 9]).unwrap(), - ]; - - let multiasset = policy_ids.iter().zip(names.iter()).fold( - MultiAsset::new(), - |mut acc, (policy_id, name)| { - acc.insert(policy_id, &{ - let mut assets = Assets::new(); - assets.insert(&name, &to_bignum(500)); - assets - }); - acc - }, - ); - - let mut input_value = Value::new(&to_bignum(58)); - input_value.set_multiasset(&multiasset); - - tx_builder.add_input( - &ByronAddress::from_base58( - "Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3", - ) - .unwrap() - .to_address(), - &TransactionInput::new(&genesis_id(), 0), - &input_value, - ); - - let output_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b", - ) - .unwrap() - .to_address(); - let output_amount = Value::new(&to_bignum(208)); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&output_addr) - .next() - .unwrap() - .with_value(&output_amount) - .build() - .unwrap(), - ) - .unwrap(); - - let change_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho", - ) - .unwrap() - .to_address(); - - assert!(tx_builder.add_change_if_needed(&change_addr).is_err()) - } - - fn make_input(input_hash_byte: u8, value: Value) -> TransactionUnspentOutput { - TransactionUnspentOutput::new( - &fake_tx_input(input_hash_byte), - &TransactionOutputBuilder::new() - .with_address( - &Address::from_bech32( - "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z", - ) - .unwrap(), - ) - .next() - .unwrap() - .with_value(&value) - .build() - .unwrap(), - ) - } - - #[test] - fn tx_builder_cip2_largest_first_increasing_fees() { - // we have a = 1 to test increasing fees when more inputs are added - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(1, 0)); - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address( - &Address::from_bech32( - "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z", - ) - .unwrap(), - ) - .next() - .unwrap() - .with_coin(&to_bignum(9000)) - .build() - .unwrap(), - ) - .unwrap(); - let mut available_inputs = TransactionUnspentOutputs::new(); - available_inputs.add(&make_input(0u8, Value::new(&to_bignum(1200)))); - available_inputs.add(&make_input(1u8, Value::new(&to_bignum(1600)))); - available_inputs.add(&make_input(2u8, Value::new(&to_bignum(6400)))); - available_inputs.add(&make_input(3u8, Value::new(&to_bignum(2400)))); - available_inputs.add(&make_input(4u8, Value::new(&to_bignum(800)))); - tx_builder - .add_inputs_from(&available_inputs, CoinSelectionStrategyCIP2::LargestFirst) - .unwrap(); - let change_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho", - ) - .unwrap() - .to_address(); - let change_added = tx_builder.add_change_if_needed(&change_addr).unwrap(); - assert!(change_added); - let tx = tx_builder.build().unwrap(); - // change needed - assert_eq!(2, tx.outputs().len()); - assert_eq!(3, tx.inputs().len()); - // confirm order of only what is necessary - assert_eq!(1u8, tx.inputs().get(0).transaction_id().0[0]); - assert_eq!(2u8, tx.inputs().get(1).transaction_id().0[0]); - assert_eq!(3u8, tx.inputs().get(2).transaction_id().0[0]); - } - - #[test] - fn tx_builder_cip2_largest_first_static_fees() { - // we have a = 0 so we know adding inputs/outputs doesn't change the fee so we can analyze more - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(0, 0)); - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address( - &Address::from_bech32( - "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z", - ) - .unwrap(), - ) - .next() - .unwrap() - .with_coin(&to_bignum(1200)) - .build() - .unwrap(), - ) - .unwrap(); - let mut available_inputs = TransactionUnspentOutputs::new(); - available_inputs.add(&make_input(0u8, Value::new(&to_bignum(150)))); - available_inputs.add(&make_input(1u8, Value::new(&to_bignum(200)))); - available_inputs.add(&make_input(2u8, Value::new(&to_bignum(800)))); - available_inputs.add(&make_input(3u8, Value::new(&to_bignum(400)))); - available_inputs.add(&make_input(4u8, Value::new(&to_bignum(100)))); - tx_builder - .add_inputs_from(&available_inputs, CoinSelectionStrategyCIP2::LargestFirst) - .unwrap(); - let change_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho", - ) - .unwrap() - .to_address(); - let change_added = tx_builder.add_change_if_needed(&change_addr).unwrap(); - assert!(!change_added); - let tx = tx_builder.build().unwrap(); - // change not needed - should be exact - assert_eq!(1, tx.outputs().len()); - assert_eq!(2, tx.inputs().len()); - // confirm order of only what is necessary - assert_eq!(2u8, tx.inputs().get(0).transaction_id().0[0]); - assert_eq!(3u8, tx.inputs().get(1).transaction_id().0[0]); - } - - #[test] - fn tx_builder_cip2_largest_first_multiasset() { - // we have a = 0 so we know adding inputs/outputs doesn't change the fee so we can analyze more - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(0, 0)); - let pid1 = PolicyID::from([1u8; 28]); - let pid2 = PolicyID::from([2u8; 28]); - let asset_name1 = AssetName::new(vec![1u8; 8]).unwrap(); - let asset_name2 = AssetName::new(vec![2u8; 11]).unwrap(); - let asset_name3 = AssetName::new(vec![3u8; 9]).unwrap(); - - let mut output_value = Value::new(&to_bignum(415)); - let mut output_ma = MultiAsset::new(); - output_ma.set_asset(&pid1, &asset_name1, to_bignum(5)); - output_ma.set_asset(&pid1, &asset_name2, to_bignum(1)); - output_ma.set_asset(&pid2, &asset_name2, to_bignum(2)); - output_ma.set_asset(&pid2, &asset_name3, to_bignum(4)); - output_value.set_multiasset(&output_ma); - tx_builder - .add_output(&TransactionOutput::new( - &Address::from_bech32("addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z") - .unwrap(), - &output_value, - )) - .unwrap(); - - let mut available_inputs = TransactionUnspentOutputs::new(); - // should not be taken - available_inputs.add(&make_input(0u8, Value::new(&to_bignum(150)))); - - // should not be taken - let mut input1 = make_input(1u8, Value::new(&to_bignum(200))); - let mut ma1 = MultiAsset::new(); - ma1.set_asset(&pid1, &asset_name1, to_bignum(10)); - ma1.set_asset(&pid1, &asset_name2, to_bignum(1)); - ma1.set_asset(&pid2, &asset_name2, to_bignum(2)); - input1.output.amount.set_multiasset(&ma1); - available_inputs.add(&input1); - - // taken first to satisfy pid1:asset_name1 (but also satisfies pid2:asset_name3) - let mut input2 = make_input(2u8, Value::new(&to_bignum(10))); - let mut ma2 = MultiAsset::new(); - ma2.set_asset(&pid1, &asset_name1, to_bignum(20)); - ma2.set_asset(&pid2, &asset_name3, to_bignum(4)); - input2.output.amount.set_multiasset(&ma2); - available_inputs.add(&input2); - - // taken second to satisfy pid1:asset_name2 (but also satisfies pid2:asset_name1) - let mut input3 = make_input(3u8, Value::new(&to_bignum(50))); - let mut ma3 = MultiAsset::new(); - ma3.set_asset(&pid2, &asset_name1, to_bignum(5)); - ma3.set_asset(&pid1, &asset_name2, to_bignum(15)); - input3.output.amount.multiasset = Some(ma3); - available_inputs.add(&input3); - - // should not be taken either - let mut input4 = make_input(4u8, Value::new(&to_bignum(10))); - let mut ma4 = MultiAsset::new(); - ma4.set_asset(&pid1, &asset_name1, to_bignum(10)); - ma4.set_asset(&pid1, &asset_name2, to_bignum(10)); - input4.output.amount.multiasset = Some(ma4); - available_inputs.add(&input4); - - // taken third to satisfy pid2:asset_name_2 - let mut input5 = make_input(5u8, Value::new(&to_bignum(10))); - let mut ma5 = MultiAsset::new(); - ma5.set_asset(&pid1, &asset_name2, to_bignum(10)); - ma5.set_asset(&pid2, &asset_name2, to_bignum(3)); - input5.output.amount.multiasset = Some(ma5); - available_inputs.add(&input5); - - // should be taken to get enough ADA - let input6 = make_input(6u8, Value::new(&to_bignum(900))); - available_inputs.add(&input6); - - // should not be taken - available_inputs.add(&make_input(7u8, Value::new(&to_bignum(100)))); - tx_builder - .add_inputs_from( - &available_inputs, - CoinSelectionStrategyCIP2::LargestFirstMultiAsset, - ) - .unwrap(); - let change_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho", - ) - .unwrap() - .to_address(); - let change_added = tx_builder.add_change_if_needed(&change_addr).unwrap(); - assert!(change_added); - let tx = tx_builder.build().unwrap(); - - assert_eq!(2, tx.outputs().len()); - assert_eq!(4, tx.inputs().len()); - // check order expected per-asset - assert_eq!(2u8, tx.inputs().get(0).transaction_id().0[0]); - assert_eq!(3u8, tx.inputs().get(1).transaction_id().0[0]); - assert_eq!(5u8, tx.inputs().get(2).transaction_id().0[0]); - assert_eq!(6u8, tx.inputs().get(3).transaction_id().0[0]); - - let change = tx.outputs().get(1).amount; - assert_eq!(from_bignum(&change.coin), 555); - let change_ma = change.multiasset().unwrap(); - assert_eq!(15, from_bignum(&change_ma.get_asset(&pid1, &asset_name1))); - assert_eq!(24, from_bignum(&change_ma.get_asset(&pid1, &asset_name2))); - assert_eq!(1, from_bignum(&change_ma.get_asset(&pid2, &asset_name2))); - assert_eq!(0, from_bignum(&change_ma.get_asset(&pid2, &asset_name3))); - let expected_input = input2 - .output - .amount - .checked_add(&input3.output.amount) - .unwrap() - .checked_add(&input5.output.amount) - .unwrap() - .checked_add(&input6.output.amount) - .unwrap(); - let expected_change = expected_input.checked_sub(&output_value).unwrap(); - assert_eq!(expected_change, change); - } - - #[test] - fn tx_builder_cip2_random_improve_multiasset() { - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(0, 0)); - let pid1 = PolicyID::from([1u8; 28]); - let pid2 = PolicyID::from([2u8; 28]); - let asset_name1 = AssetName::new(vec![1u8; 8]).unwrap(); - let asset_name2 = AssetName::new(vec![2u8; 11]).unwrap(); - let asset_name3 = AssetName::new(vec![3u8; 9]).unwrap(); - - let mut output_value = Value::new(&to_bignum(415)); - let mut output_ma = MultiAsset::new(); - output_ma.set_asset(&pid1, &asset_name1, to_bignum(5)); - output_ma.set_asset(&pid1, &asset_name2, to_bignum(1)); - output_ma.set_asset(&pid2, &asset_name2, to_bignum(2)); - output_ma.set_asset(&pid2, &asset_name3, to_bignum(4)); - output_value.set_multiasset(&output_ma); - tx_builder - .add_output(&TransactionOutput::new( - &Address::from_bech32("addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z") - .unwrap(), - &output_value, - )) - .unwrap(); - - let mut available_inputs = TransactionUnspentOutputs::new(); - available_inputs.add(&make_input(0u8, Value::new(&to_bignum(150)))); - - let mut input1 = make_input(1u8, Value::new(&to_bignum(200))); - let mut ma1 = MultiAsset::new(); - ma1.set_asset(&pid1, &asset_name1, to_bignum(10)); - ma1.set_asset(&pid1, &asset_name2, to_bignum(1)); - ma1.set_asset(&pid2, &asset_name2, to_bignum(2)); - input1.output.amount.set_multiasset(&ma1); - available_inputs.add(&input1); - - let mut input2 = make_input(2u8, Value::new(&to_bignum(10))); - let mut ma2 = MultiAsset::new(); - ma2.set_asset(&pid1, &asset_name1, to_bignum(20)); - ma2.set_asset(&pid2, &asset_name3, to_bignum(4)); - input2.output.amount.set_multiasset(&ma2); - available_inputs.add(&input2); - - let mut input3 = make_input(3u8, Value::new(&to_bignum(50))); - let mut ma3 = MultiAsset::new(); - ma3.set_asset(&pid2, &asset_name1, to_bignum(5)); - ma3.set_asset(&pid1, &asset_name2, to_bignum(15)); - input3.output.amount.multiasset = Some(ma3); - available_inputs.add(&input3); - - let mut input4 = make_input(4u8, Value::new(&to_bignum(10))); - let mut ma4 = MultiAsset::new(); - ma4.set_asset(&pid1, &asset_name1, to_bignum(10)); - ma4.set_asset(&pid1, &asset_name2, to_bignum(10)); - input4.output.amount.multiasset = Some(ma4); - available_inputs.add(&input4); - - let mut input5 = make_input(5u8, Value::new(&to_bignum(10))); - let mut ma5 = MultiAsset::new(); - ma5.set_asset(&pid1, &asset_name2, to_bignum(10)); - ma5.set_asset(&pid2, &asset_name2, to_bignum(3)); - input5.output.amount.multiasset = Some(ma5); - available_inputs.add(&input5); - - let input6 = make_input(6u8, Value::new(&to_bignum(1000))); - available_inputs.add(&input6); - available_inputs.add(&make_input(7u8, Value::new(&to_bignum(100)))); - - let mut input8 = make_input(8u8, Value::new(&to_bignum(10))); - let mut ma8 = MultiAsset::new(); - ma8.set_asset(&pid2, &asset_name2, to_bignum(10)); - input8.output.amount.multiasset = Some(ma8); - available_inputs.add(&input8); - - let mut input9 = make_input(9u8, Value::new(&to_bignum(10))); - let mut ma9 = MultiAsset::new(); - ma9.set_asset(&pid2, &asset_name3, to_bignum(10)); - input9.output.amount.multiasset = Some(ma9); - available_inputs.add(&input9); - - tx_builder - .add_inputs_from( - &available_inputs, - CoinSelectionStrategyCIP2::RandomImproveMultiAsset, - ) - .unwrap(); - - let input_for_cover_change = make_input(10u8, Value::new(&to_bignum(1000))); - tx_builder.add_input( - &input_for_cover_change.output.address, - &input_for_cover_change.input, - &input_for_cover_change.output.amount); - - let change_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho", - ) - .unwrap() - .to_address(); - let change_added = tx_builder.add_change_if_needed(&change_addr).unwrap(); - assert!(change_added); - let tx = tx_builder.build().unwrap(); - - assert_eq!(2, tx.outputs().len()); - - let input_total = tx_builder.get_explicit_input().unwrap(); - assert!(input_total >= output_value); - } - - #[test] - fn tx_builder_cip2_random_improve() { - // we have a = 1 to test increasing fees when more inputs are added - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(1, 0)); - const COST: u64 = 10000; - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address( - &Address::from_bech32( - "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z", - ) - .unwrap(), - ) - .next() - .unwrap() - .with_coin(&to_bignum(COST)) - .build() - .unwrap(), - ) - .unwrap(); - let mut available_inputs = TransactionUnspentOutputs::new(); - available_inputs.add(&make_input(0u8, Value::new(&to_bignum(1500)))); - available_inputs.add(&make_input(1u8, Value::new(&to_bignum(2000)))); - available_inputs.add(&make_input(2u8, Value::new(&to_bignum(8000)))); - available_inputs.add(&make_input(3u8, Value::new(&to_bignum(4000)))); - available_inputs.add(&make_input(4u8, Value::new(&to_bignum(1000)))); - available_inputs.add(&make_input(5u8, Value::new(&to_bignum(2000)))); - available_inputs.add(&make_input(6u8, Value::new(&to_bignum(1500)))); - let add_inputs_res = - tx_builder.add_inputs_from(&available_inputs, CoinSelectionStrategyCIP2::RandomImprove); - assert!(add_inputs_res.is_ok(), "{:?}", add_inputs_res.err()); - let change_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho", - ) - .unwrap() - .to_address(); - let add_change_res = tx_builder.add_change_if_needed(&change_addr); - assert!(add_change_res.is_ok(), "{:?}", add_change_res.err()); - let tx_build_res = tx_builder.build(); - assert!(tx_build_res.is_ok(), "{:?}", tx_build_res.err()); - let tx = tx_build_res.unwrap(); - // we need to look up the values to ensure there's enough - let mut input_values = BTreeMap::new(); - for utxo in available_inputs.0.iter() { - input_values.insert(utxo.input.transaction_id(), utxo.output.amount.clone()); - } - let mut encountered = std::collections::HashSet::new(); - let mut input_total = Value::new(&Coin::zero()); - for input in tx.inputs.0.iter() { - let txid = input.transaction_id(); - if !encountered.insert(txid.clone()) { - panic!("Input {:?} duplicated", txid); - } - let value = input_values.get(&txid).unwrap(); - input_total = input_total.checked_add(value).unwrap(); - } - assert!( - input_total - >= Value::new( - &tx_builder - .min_fee() - .unwrap() - .checked_add(&to_bignum(COST)) - .unwrap() - ) - ); - } - - #[test] - fn tx_builder_cip2_random_improve_when_using_all_available_inputs() { - // we have a = 1 to test increasing fees when more inputs are added - let linear_fee = LinearFee::new(&to_bignum(1), &to_bignum(0)); - let cfg = TransactionBuilderConfigBuilder::new() - .fee_algo(&linear_fee) - .pool_deposit(&to_bignum(0)) - .key_deposit(&to_bignum(0)) - .max_value_size(9999) - .max_tx_size(9999) - .coins_per_utxo_word(&Coin::zero()) - .build() - .unwrap(); - let mut tx_builder = TransactionBuilder::new(&cfg); - const COST: u64 = 1000; - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address( - &Address::from_bech32( - "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z", - ) - .unwrap(), - ) - .next() - .unwrap() - .with_coin(&to_bignum(COST)) - .build() - .unwrap(), - ) - .unwrap(); - let mut available_inputs = TransactionUnspentOutputs::new(); - available_inputs.add(&make_input(1u8, Value::new(&to_bignum(800)))); - available_inputs.add(&make_input(2u8, Value::new(&to_bignum(800)))); - let add_inputs_res = - tx_builder.add_inputs_from(&available_inputs, CoinSelectionStrategyCIP2::RandomImprove); - assert!(add_inputs_res.is_ok(), "{:?}", add_inputs_res.err()); - } - - #[test] - fn tx_builder_cip2_random_improve_adds_enough_for_fees() { - // we have a = 1 to test increasing fees when more inputs are added - let linear_fee = LinearFee::new(&to_bignum(1), &to_bignum(0)); - let cfg = TransactionBuilderConfigBuilder::new() - .fee_algo(&linear_fee) - .pool_deposit(&to_bignum(0)) - .key_deposit(&to_bignum(0)) - .max_value_size(9999) - .max_tx_size(9999) - .coins_per_utxo_word(&Coin::zero()) - .build() - .unwrap(); - let mut tx_builder = TransactionBuilder::new(&cfg); - const COST: u64 = 100; - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address( - &Address::from_bech32( - "addr1vyy6nhfyks7wdu3dudslys37v252w2nwhv0fw2nfawemmnqs6l44z", - ) - .unwrap(), - ) - .next() - .unwrap() - .with_coin(&to_bignum(COST)) - .build() - .unwrap(), - ) - .unwrap(); - assert_eq!(tx_builder.min_fee().unwrap(), to_bignum(53)); - let mut available_inputs = TransactionUnspentOutputs::new(); - available_inputs.add(&make_input(1u8, Value::new(&to_bignum(150)))); - available_inputs.add(&make_input(2u8, Value::new(&to_bignum(150)))); - available_inputs.add(&make_input(3u8, Value::new(&to_bignum(150)))); - let add_inputs_res = - tx_builder.add_inputs_from(&available_inputs, CoinSelectionStrategyCIP2::RandomImprove); - assert!(add_inputs_res.is_ok(), "{:?}", add_inputs_res.err()); - assert_eq!(tx_builder.min_fee().unwrap(), to_bignum(264)); - let change_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho", - ) - .unwrap() - .to_address(); - let add_change_res = tx_builder.add_change_if_needed(&change_addr); - assert!(add_change_res.is_ok(), "{:?}", add_change_res.err()); - } - - #[test] - fn build_tx_pay_to_multisig() { - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(10, 2)); - let spend = root_key_15() - .derive(harden(1854)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - - tx_builder.add_key_input( - &spend.to_raw_key().hash(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_coin(&to_bignum(999_000)) - .build() - .unwrap(), - ) - .unwrap(); - tx_builder.set_ttl(1000); - tx_builder.set_fee(&to_bignum(1_000)); - - assert_eq!(tx_builder.outputs.len(), 1); - assert_eq!( - tx_builder - .get_explicit_input() - .unwrap() - .checked_add(&tx_builder.get_implicit_input().unwrap()) - .unwrap(), - tx_builder - .get_explicit_output() - .unwrap() - .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) - .unwrap() - ); - - let _final_tx = tx_builder.build().unwrap(); - let _deser_t = TransactionBody::from_bytes(_final_tx.to_bytes()).unwrap(); - - assert_eq!(_deser_t.to_bytes(), _final_tx.to_bytes()); - } - - fn build_full_tx( - body: &TransactionBody, - witness_set: &TransactionWitnessSet, - auxiliary_data: Option, - ) -> Transaction { - return Transaction::new(body, witness_set, auxiliary_data); - } - - #[test] - fn build_tx_multisig_spend_1on1_unsigned() { - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(10, 2)); - - let spend = root_key_15() //multisig - .derive(harden(1854)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let stake = root_key_15() //multisig - .derive(harden(1854)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let addr_multisig = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - let addr_output = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - - tx_builder.add_input( - &addr_multisig, - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_output) - .next() - .unwrap() - .with_coin(&to_bignum(999_000)) - .build() - .unwrap(), - ) - .unwrap(); - tx_builder.set_ttl(1000); - tx_builder.set_fee(&to_bignum(1_000)); - - let mut auxiliary_data = AuxiliaryData::new(); - let mut pubkey_native_scripts = NativeScripts::new(); - let mut oneof_native_scripts = NativeScripts::new(); - - let spending_hash = spend.to_raw_key().hash(); - pubkey_native_scripts.add(&NativeScript::new_script_pubkey(&ScriptPubkey::new( - &spending_hash, - ))); - oneof_native_scripts.add(&NativeScript::new_script_n_of_k(&ScriptNOfK::new( - 1, - &pubkey_native_scripts, - ))); - auxiliary_data.set_native_scripts(&oneof_native_scripts); - tx_builder.set_auxiliary_data(&auxiliary_data); - - assert_eq!(tx_builder.outputs.len(), 1); - assert_eq!( - tx_builder - .get_explicit_input() - .unwrap() - .checked_add(&tx_builder.get_implicit_input().unwrap()) - .unwrap(), - tx_builder - .get_explicit_output() - .unwrap() - .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) - .unwrap() - ); - - let _final_tx = tx_builder.build().unwrap(); - let _deser_t = TransactionBody::from_bytes(_final_tx.to_bytes()).unwrap(); - - assert_eq!(_deser_t.to_bytes(), _final_tx.to_bytes()); - assert_eq!( - _deser_t.auxiliary_data_hash.unwrap(), - utils::hash_auxiliary_data(&auxiliary_data) - ); - } - - #[test] - fn build_tx_multisig_1on1_signed() { - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(10, 2)); - let spend = root_key_15() - .derive(harden(1854)) //multisig - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1854)) //multisig - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let addr_net_0 = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ) - .to_address(); - tx_builder.add_key_input( - &spend.to_raw_key().hash(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&addr_net_0) - .next() - .unwrap() - .with_coin(&to_bignum(999_000)) - .build() - .unwrap(), - ) - .unwrap(); - tx_builder.set_ttl(1000); - tx_builder.set_fee(&to_bignum(1_000)); - - let mut auxiliary_data = AuxiliaryData::new(); - let mut pubkey_native_scripts = NativeScripts::new(); - let mut oneof_native_scripts = NativeScripts::new(); - - let spending_hash = spend.to_raw_key().hash(); - pubkey_native_scripts.add(&NativeScript::new_script_pubkey(&ScriptPubkey::new( - &spending_hash, - ))); - oneof_native_scripts.add(&NativeScript::new_script_n_of_k(&ScriptNOfK::new( - 1, - &pubkey_native_scripts, - ))); - auxiliary_data.set_native_scripts(&oneof_native_scripts); - tx_builder.set_auxiliary_data(&auxiliary_data); - - let body = tx_builder.build().unwrap(); - - assert_eq!(tx_builder.outputs.len(), 1); - assert_eq!( - tx_builder - .get_explicit_input() - .unwrap() - .checked_add(&tx_builder.get_implicit_input().unwrap()) - .unwrap(), - tx_builder - .get_explicit_output() - .unwrap() - .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) - .unwrap() - ); - - let mut witness_set = TransactionWitnessSet::new(); - let mut vkw = Vkeywitnesses::new(); - vkw.add(&make_vkey_witness( - &hash_transaction(&body), - &PrivateKey::from_normal_bytes( - &hex::decode("c660e50315d76a53d80732efda7630cae8885dfb85c46378684b3c6103e1284a") - .unwrap(), - ) - .unwrap(), - )); - witness_set.set_vkeys(&vkw); - - let _final_tx = build_full_tx(&body, &witness_set, None); - let _deser_t = Transaction::from_bytes(_final_tx.to_bytes()).unwrap(); - assert_eq!(_deser_t.to_bytes(), _final_tx.to_bytes()); - assert_eq!( - _deser_t.body().auxiliary_data_hash.unwrap(), - utils::hash_auxiliary_data(&auxiliary_data) - ); - } - - #[test] - fn add_change_splits_change_into_multiple_outputs_when_nfts_overflow_output_size() { - let linear_fee = LinearFee::new(&to_bignum(0), &to_bignum(1)); - let max_value_size = 100; // super low max output size to test with fewer assets - let mut tx_builder = TransactionBuilder::new( - &TransactionBuilderConfigBuilder::new() - .fee_algo(&linear_fee) - .pool_deposit(&to_bignum(0)) - .key_deposit(&to_bignum(0)) - .max_value_size(max_value_size) - .max_tx_size(MAX_TX_SIZE) - .coins_per_utxo_word(&to_bignum(8)) - .prefer_pure_change(true) - .build() - .unwrap(), - ); - - let policy_id = PolicyID::from([0u8; 28]); - let names = [ - AssetName::new(vec![99u8; 32]).unwrap(), - AssetName::new(vec![0u8, 1, 2, 3]).unwrap(), - AssetName::new(vec![4u8, 5, 6, 7]).unwrap(), - AssetName::new(vec![5u8, 5, 6, 7]).unwrap(), - AssetName::new(vec![6u8, 5, 6, 7]).unwrap(), - ]; - let assets = names.iter().fold(Assets::new(), |mut a, name| { - a.insert(&name, &to_bignum(500)); - a - }); - let mut multiasset = MultiAsset::new(); - multiasset.insert(&policy_id, &assets); - - let mut input_value = Value::new(&to_bignum(1200)); - input_value.set_multiasset(&multiasset); - - tx_builder.add_input( - &ByronAddress::from_base58( - "Ae2tdPwUPEZ5uzkzh1o2DHECiUi3iugvnnKHRisPgRRP3CTF4KCMvy54Xd3", - ) - .unwrap() - .to_address(), - &TransactionInput::new(&genesis_id(), 0), - &input_value, - ); - - let output_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZD9QQf2ZrcYV34pYJwxK4vqXaF8EXkup1eYH73zUScHReM42b", - ) - .unwrap() - .to_address(); - let output_amount = Value::new(&to_bignum(208)); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&output_addr) - .next() - .unwrap() - .with_value(&output_amount) - .build() - .unwrap(), - ) - .unwrap(); - - let change_addr = ByronAddress::from_base58( - "Ae2tdPwUPEZGUEsuMAhvDcy94LKsZxDjCbgaiBBMgYpR8sKf96xJmit7Eho", - ) - .unwrap() - .to_address(); - - let add_change_result = tx_builder.add_change_if_needed(&change_addr); - assert!(add_change_result.is_ok()); - assert_eq!(tx_builder.outputs.len(), 4); - - let change1 = tx_builder.outputs.get(1); - let change2 = tx_builder.outputs.get(2); - let change3 = tx_builder.outputs.get(3); - - assert_eq!(change1.address, change_addr); - assert_eq!(change1.address, change2.address); - assert_eq!(change1.address, change3.address); - - assert_eq!(change1.amount.coin, to_bignum(288)); - assert_eq!(change2.amount.coin, to_bignum(293)); - assert_eq!(change3.amount.coin, to_bignum(410)); - - assert!(change1.amount.multiasset.is_some()); - assert!(change2.amount.multiasset.is_some()); - assert!(change3.amount.multiasset.is_none()); // purified - - let masset1 = change1.amount.multiasset.unwrap(); - let masset2 = change2.amount.multiasset.unwrap(); - - assert_eq!(masset1.keys().len(), 1); - assert_eq!(masset1.keys(), masset2.keys()); - - let asset1 = masset1.get(&policy_id).unwrap(); - let asset2 = masset2.get(&policy_id).unwrap(); - assert_eq!(asset1.len(), 4); - assert_eq!(asset2.len(), 1); - - names.iter().for_each(|name| { - let v1 = asset1.get(name); - let v2 = asset2.get(name); - assert_ne!(v1.is_some(), v2.is_some()); - assert_eq!(v1.or(v2).unwrap(), to_bignum(500)); - }); - } - - fn create_json_metadatum_string() -> String { - String::from("{ \"qwe\": 123 }") - } - - fn create_json_metadatum() -> TransactionMetadatum { - encode_json_str_to_metadatum( - create_json_metadatum_string(), - MetadataJsonSchema::NoConversions, - ) - .unwrap() - } - - fn create_aux_with_metadata(metadatum_key: &TransactionMetadatumLabel) -> AuxiliaryData { - let mut metadata = GeneralTransactionMetadata::new(); - metadata.insert(metadatum_key, &create_json_metadatum()); - - let mut aux = AuxiliaryData::new(); - aux.set_metadata(&metadata); - - let mut nats = NativeScripts::new(); - nats.add(&NativeScript::new_timelock_start(&TimelockStart::new(123))); - aux.set_native_scripts(&nats); - - return aux; - } - - fn assert_json_metadatum(dat: &TransactionMetadatum) { - let map = dat.as_map().unwrap(); - assert_eq!(map.len(), 1); - let key = TransactionMetadatum::new_text(String::from("qwe")).unwrap(); - let val = map.get(&key).unwrap(); - assert_eq!(val.as_int().unwrap(), Int::new_i32(123)); - } - - #[test] - fn set_metadata_with_empty_auxiliary() { - let mut tx_builder = create_default_tx_builder(); - - let num = to_bignum(42); - tx_builder.set_metadata(&create_aux_with_metadata(&num).metadata().unwrap()); - - assert!(tx_builder.auxiliary_data.is_some()); - - let aux = tx_builder.auxiliary_data.unwrap(); - assert!(aux.metadata().is_some()); - assert!(aux.native_scripts().is_none()); - assert!(aux.plutus_scripts().is_none()); - - let met = aux.metadata().unwrap(); - - assert_eq!(met.len(), 1); - assert_json_metadatum(&met.get(&num).unwrap()); - } - - #[test] - fn set_metadata_with_existing_auxiliary() { - let mut tx_builder = create_default_tx_builder(); - - let num1 = to_bignum(42); - tx_builder.set_auxiliary_data(&create_aux_with_metadata(&num1)); - - let num2 = to_bignum(84); - tx_builder.set_metadata(&create_aux_with_metadata(&num2).metadata().unwrap()); - - let aux = tx_builder.auxiliary_data.unwrap(); - assert!(aux.metadata().is_some()); - assert!(aux.native_scripts().is_some()); - assert!(aux.plutus_scripts().is_none()); - - let met = aux.metadata().unwrap(); - assert_eq!(met.len(), 1); - assert!(met.get(&num1).is_none()); - assert_json_metadatum(&met.get(&num2).unwrap()); - } - - #[test] - fn add_metadatum_with_empty_auxiliary() { - let mut tx_builder = create_default_tx_builder(); - - let num = to_bignum(42); - tx_builder.add_metadatum(&num, &create_json_metadatum()); - - assert!(tx_builder.auxiliary_data.is_some()); - - let aux = tx_builder.auxiliary_data.unwrap(); - assert!(aux.metadata().is_some()); - assert!(aux.native_scripts().is_none()); - assert!(aux.plutus_scripts().is_none()); - - let met = aux.metadata().unwrap(); - - assert_eq!(met.len(), 1); - assert_json_metadatum(&met.get(&num).unwrap()); - } - - #[test] - fn add_metadatum_with_existing_auxiliary() { - let mut tx_builder = create_default_tx_builder(); - - let num1 = to_bignum(42); - tx_builder.set_auxiliary_data(&create_aux_with_metadata(&num1)); - - let num2 = to_bignum(84); - tx_builder.add_metadatum(&num2, &create_json_metadatum()); - - let aux = tx_builder.auxiliary_data.unwrap(); - assert!(aux.metadata().is_some()); - assert!(aux.native_scripts().is_some()); - assert!(aux.plutus_scripts().is_none()); - - let met = aux.metadata().unwrap(); - assert_eq!(met.len(), 2); - assert_json_metadatum(&met.get(&num1).unwrap()); - assert_json_metadatum(&met.get(&num2).unwrap()); - } - - #[test] - fn add_json_metadatum_with_empty_auxiliary() { - let mut tx_builder = create_default_tx_builder(); - - let num = to_bignum(42); - tx_builder - .add_json_metadatum(&num, create_json_metadatum_string()) - .unwrap(); - - assert!(tx_builder.auxiliary_data.is_some()); - - let aux = tx_builder.auxiliary_data.unwrap(); - assert!(aux.metadata().is_some()); - assert!(aux.native_scripts().is_none()); - assert!(aux.plutus_scripts().is_none()); - - let met = aux.metadata().unwrap(); - - assert_eq!(met.len(), 1); - assert_json_metadatum(&met.get(&num).unwrap()); - } - - #[test] - fn add_json_metadatum_with_existing_auxiliary() { - let mut tx_builder = create_default_tx_builder(); - - let num1 = to_bignum(42); - tx_builder.set_auxiliary_data(&create_aux_with_metadata(&num1)); - - let num2 = to_bignum(84); - tx_builder - .add_json_metadatum(&num2, create_json_metadatum_string()) - .unwrap(); - - let aux = tx_builder.auxiliary_data.unwrap(); - assert!(aux.metadata().is_some()); - assert!(aux.native_scripts().is_some()); - assert!(aux.plutus_scripts().is_none()); - - let met = aux.metadata().unwrap(); - assert_eq!(met.len(), 2); - assert_json_metadatum(&met.get(&num1).unwrap()); - assert_json_metadatum(&met.get(&num2).unwrap()); - } - - fn create_asset_name() -> AssetName { - AssetName::new(vec![0u8, 1, 2, 3]).unwrap() - } - - fn create_mint_asset() -> MintAssets { - MintAssets::new_from_entry(&create_asset_name(), Int::new_i32(1234)) - } - - fn create_assets() -> Assets { - let mut assets = Assets::new(); - assets.insert(&create_asset_name(), &to_bignum(1234)); - return assets; - } - - fn create_mint_with_one_asset(policy_id: &PolicyID) -> Mint { - Mint::new_from_entry(policy_id, &create_mint_asset()) - } - - fn create_multiasset_one_asset(policy_id: &PolicyID) -> MultiAsset { - let mut mint = MultiAsset::new(); - mint.insert(policy_id, &create_assets()); - return mint; - } - - fn assert_mint_asset(mint: &Mint, policy_id: &PolicyID) { - assert!(mint.get(&policy_id).is_some()); - let result_asset = mint.get(&policy_id).unwrap(); - assert_eq!(result_asset.len(), 1); - assert_eq!( - result_asset.get(&create_asset_name()).unwrap(), - Int::new_i32(1234) - ); - } - - fn mint_script_and_policy_and_hash(x: u8) -> (NativeScript, PolicyID, Ed25519KeyHash) { - let hash = fake_key_hash(x); - let mint_script = NativeScript::new_script_pubkey(&ScriptPubkey::new(&hash)); - let policy_id = mint_script.hash(); - (mint_script, policy_id, hash) - } - - fn mint_script_and_policy(x: u8) -> (NativeScript, ScriptHash) { - let (m, p, _) = mint_script_and_policy_and_hash(x); - (m, p) - } - - fn plutus_script_and_hash(x: u8) -> (PlutusScript, ScriptHash) { - let s = PlutusScript::new(fake_bytes_32(x)); - (s.clone(), s.hash()) - } - - #[test] - fn set_mint_asset_with_empty_mint() { - let mut tx_builder = create_default_tx_builder(); - - let (mint_script, policy_id) = mint_script_and_policy(0); - tx_builder.set_mint_asset(&mint_script, &create_mint_asset()); - - assert!(tx_builder.mint.is_some()); - let mint_scripts = tx_builder.mint.as_ref().unwrap().get_native_scripts(); - assert!(mint_scripts.len() > 0); - - let mint = tx_builder.mint.unwrap().build(); - - assert_eq!(mint.len(), 1); - assert_mint_asset(&mint, &policy_id); - - assert_eq!(mint_scripts.len(), 1); - assert_eq!(mint_scripts.get(0), mint_script); - } - - #[test] - fn set_mint_asset_with_existing_mint() { - let mut tx_builder = create_default_tx_builder(); - - let (mint_script1, policy_id1) = mint_script_and_policy(0); - let (mint_script2, policy_id2) = mint_script_and_policy(1); - - tx_builder - .set_mint( - &create_mint_with_one_asset(&policy_id1), - &NativeScripts::from(vec![mint_script1.clone()]), - ) - .unwrap(); - - tx_builder.set_mint_asset(&mint_script2, &create_mint_asset()); - - assert!(tx_builder.mint.is_some()); - let mint_scripts = tx_builder.mint.as_ref().unwrap().get_native_scripts(); - assert!(mint_scripts.len() > 0); - - let mint = tx_builder.mint.unwrap().build(); - - assert_eq!(mint.len(), 2); - assert_mint_asset(&mint, &policy_id1); - assert_mint_asset(&mint, &policy_id2); - - // Only second script is present in the scripts - assert_eq!(mint_scripts.len(), 2); - let actual_scripts = mint_scripts.0.iter().cloned().collect::>(); - let expected_scripts = vec![mint_script1, mint_script2].iter().cloned().collect::>(); - assert_eq!(actual_scripts, expected_scripts); - } - - #[test] - fn add_mint_asset_with_empty_mint() { - let mut tx_builder = create_default_tx_builder(); - - let (mint_script, policy_id) = mint_script_and_policy(0); - - tx_builder.add_mint_asset(&mint_script, &create_asset_name(), Int::new_i32(1234)); - - assert!(tx_builder.mint.is_some()); - let mint_scripts = tx_builder.mint.as_ref().unwrap().get_native_scripts(); - assert!(mint_scripts.len() > 0); - - let mint = tx_builder.mint.unwrap().build(); - - assert_eq!(mint.len(), 1); - assert_mint_asset(&mint, &policy_id); - - assert_eq!(mint_scripts.len(), 1); - assert_eq!(mint_scripts.get(0), mint_script); - } - - #[test] - fn add_mint_asset_with_existing_mint() { - let mut tx_builder = create_default_tx_builder(); - - let (mint_script1, policy_id1) = mint_script_and_policy(0); - let (mint_script2, policy_id2) = mint_script_and_policy(1); - - tx_builder - .set_mint( - &create_mint_with_one_asset(&policy_id1), - &NativeScripts::from(vec![mint_script1.clone()]), - ) - .unwrap(); - tx_builder.add_mint_asset(&mint_script2, &create_asset_name(), Int::new_i32(1234)); - - assert!(tx_builder.mint.is_some()); - let mint_scripts = tx_builder.mint.as_ref().unwrap().get_native_scripts(); - assert!(mint_scripts.len() > 0); - - let mint = tx_builder.mint.unwrap().build(); - - assert_eq!(mint.len(), 2); - assert_mint_asset(&mint, &policy_id1); - assert_mint_asset(&mint, &policy_id2); - - assert_eq!(mint_scripts.len(), 2); - let actual_scripts = mint_scripts.0.iter().cloned().collect::>(); - let expected_scripts = vec![mint_script1, mint_script2].iter().cloned().collect::>(); - assert_eq!(actual_scripts, expected_scripts); - } - - #[test] - fn add_output_amount() { - let mut tx_builder = create_default_tx_builder(); - - let policy_id1 = PolicyID::from([0u8; 28]); - let multiasset = create_multiasset_one_asset(&policy_id1); - let mut value = Value::new(&to_bignum(249)); - value.set_multiasset(&multiasset); - - let address = byron_address(); - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&address) - .next() - .unwrap() - .with_value(&value) - .build() - .unwrap(), - ) - .unwrap(); - - assert_eq!(tx_builder.outputs.len(), 1); - let out = tx_builder.outputs.get(0); - - assert_eq!(out.address.to_bytes(), address.to_bytes()); - assert_eq!(out.amount, value); - } - - #[test] - fn add_output_coin() { - let mut tx_builder = create_default_tx_builder(); - - let address = byron_address(); - let coin = to_bignum(208); - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&address) - .next() - .unwrap() - .with_coin(&coin) - .build() - .unwrap(), - ) - .unwrap(); - - assert_eq!(tx_builder.outputs.len(), 1); - let out = tx_builder.outputs.get(0); - - assert_eq!(out.address.to_bytes(), address.to_bytes()); - assert_eq!(out.amount.coin, coin); - assert!(out.amount.multiasset.is_none()); - } - - #[test] - fn add_output_coin_and_multiasset() { - let mut tx_builder = create_default_tx_builder(); - - let policy_id1 = PolicyID::from([0u8; 28]); - let multiasset = create_multiasset_one_asset(&policy_id1); - - let address = byron_address(); - let coin = to_bignum(249); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&address) - .next() - .unwrap() - .with_coin_and_asset(&coin, &multiasset) - .build() - .unwrap(), - ) - .unwrap(); - - assert_eq!(tx_builder.outputs.len(), 1); - let out = tx_builder.outputs.get(0); - - assert_eq!(out.address.to_bytes(), address.to_bytes()); - assert_eq!(out.amount.coin, coin); - assert_eq!(out.amount.multiasset.unwrap(), multiasset); - } - - #[test] - fn add_output_asset_and_min_required_coin() { - let mut tx_builder = create_reallistic_tx_builder(); - - let policy_id1 = PolicyID::from([0u8; 28]); - let multiasset = create_multiasset_one_asset(&policy_id1); - - let address = byron_address(); - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&address) - .next() - .unwrap() - .with_asset_and_min_required_coin_by_utxo_cost( - &multiasset, - &tx_builder.config.utxo_cost(), - ) - .unwrap() - .build() - .unwrap(), - ) - .unwrap(); - - assert_eq!(tx_builder.outputs.len(), 1); - let out = tx_builder.outputs.get(0); - - assert_eq!(out.address.to_bytes(), address.to_bytes()); - assert_eq!(out.amount.multiasset.unwrap(), multiasset); - assert_eq!(out.amount.coin, to_bignum(1146460)); - } - - #[test] - fn add_mint_asset_and_output() { - let mut tx_builder = create_default_tx_builder(); - - let (mint_script0, policy_id0) = mint_script_and_policy(0); - let (mint_script1, policy_id1) = mint_script_and_policy(1); - - let name = create_asset_name(); - let amount = Int::new_i32(1234); - - let address = byron_address(); - let coin = to_bignum(249); - - // Add unrelated mint first to check it is NOT added to output later - tx_builder.add_mint_asset(&mint_script0, &name, amount.clone()); - - tx_builder - .add_mint_asset_and_output( - &mint_script1, - &name, - amount.clone(), - &TransactionOutputBuilder::new() - .with_address(&address) - .next() - .unwrap(), - &coin, - ) - .unwrap(); - - assert!(tx_builder.mint.is_some()); - let mint_scripts = &tx_builder.mint.as_ref().unwrap().get_native_scripts(); - assert!(mint_scripts.len() > 0); - - let mint = &tx_builder.mint.unwrap().build(); - - // Mint contains two entries - assert_eq!(mint.len(), 2); - assert_mint_asset(mint, &policy_id0); - assert_mint_asset(mint, &policy_id1); - - assert_eq!(mint_scripts.len(), 2); - let actual_scripts = mint_scripts.0.iter().cloned().collect::>(); - let expected_scripts = vec![mint_script0, mint_script1].iter().cloned().collect::>(); - assert_eq!(actual_scripts, expected_scripts); - - // One new output is created - assert_eq!(tx_builder.outputs.len(), 1); - let out = tx_builder.outputs.get(0); - - assert_eq!(out.address.to_bytes(), address.to_bytes()); - assert_eq!(out.amount.coin, coin); - - let multiasset = out.amount.multiasset.unwrap(); - - // Only second mint entry was added to the output - assert_eq!(multiasset.len(), 1); - assert!(multiasset.get(&policy_id0).is_none()); - assert!(multiasset.get(&policy_id1).is_some()); - - let asset = multiasset.get(&policy_id1).unwrap(); - assert_eq!(asset.len(), 1); - assert_eq!(asset.get(&name).unwrap(), to_bignum(1234)); - } - - #[test] - fn add_mint_asset_and_min_required_coin() { - let mut tx_builder = create_reallistic_tx_builder(); - - let (mint_script0, policy_id0) = mint_script_and_policy(0); - let (mint_script1, policy_id1) = mint_script_and_policy(1); - - let name = create_asset_name(); - let amount = Int::new_i32(1234); - - let address = byron_address(); - - // Add unrelated mint first to check it is NOT added to output later - tx_builder.add_mint_asset(&mint_script0, &name, amount.clone()); - - tx_builder - .add_mint_asset_and_output_min_required_coin( - &mint_script1, - &name, - amount.clone(), - &TransactionOutputBuilder::new() - .with_address(&address) - .next() - .unwrap(), - ) - .unwrap(); - - assert!(tx_builder.mint.is_some()); - let mint_scripts = tx_builder.mint.as_ref().unwrap().get_native_scripts(); - assert!(mint_scripts.len() > 0); - - let mint = &tx_builder.mint.unwrap().build(); - - // Mint contains two entries - assert_eq!(mint.len(), 2); - assert_mint_asset(mint, &policy_id0); - assert_mint_asset(mint, &policy_id1); - - assert_eq!(mint_scripts.len(), 2); - let actual_scripts = mint_scripts.0.iter().cloned().collect::>(); - let expected_scripts = vec![mint_script0, mint_script1].iter().cloned().collect::>(); - assert_eq!(actual_scripts, expected_scripts); - - // One new output is created - assert_eq!(tx_builder.outputs.len(), 1); - let out = tx_builder.outputs.get(0); - - assert_eq!(out.address.to_bytes(), address.to_bytes()); - assert_eq!(out.amount.coin, to_bignum(1146460)); - - let multiasset = out.amount.multiasset.unwrap(); - - // Only second mint entry was added to the output - assert_eq!(multiasset.len(), 1); - assert!(multiasset.get(&policy_id0).is_none()); - assert!(multiasset.get(&policy_id1).is_some()); - - let asset = multiasset.get(&policy_id1).unwrap(); - assert_eq!(asset.len(), 1); - assert_eq!(asset.get(&name).unwrap(), to_bignum(1234)); - } - - #[test] - fn add_mint_includes_witnesses_into_fee_estimation() { - let mut tx_builder = create_reallistic_tx_builder(); - - let hash0 = fake_key_hash(0); - - let (mint_script1, _, hash1) = mint_script_and_policy_and_hash(1); - let (mint_script2, _, _) = mint_script_and_policy_and_hash(2); - let (mint_script3, _, _) = mint_script_and_policy_and_hash(3); - - let name1 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - let name2 = AssetName::new(vec![1u8, 1, 2, 3]).unwrap(); - let name3 = AssetName::new(vec![2u8, 1, 2, 3]).unwrap(); - let name4 = AssetName::new(vec![3u8, 1, 2, 3]).unwrap(); - let amount = Int::new_i32(1234); - - // One input from unrelated address - tx_builder.add_key_input( - &hash0, - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(10_000_000)), - ); - - // One input from same address as mint - tx_builder.add_key_input( - &hash1, - &TransactionInput::new(&genesis_id(), 1), - &Value::new(&to_bignum(10_000_000)), - ); - - // Original tx fee now assumes two VKey signatures for two inputs - let original_tx_fee = tx_builder.min_fee().unwrap(); - assert_eq!(original_tx_fee, to_bignum(168361)); - - // Add minting four assets from three different policies - tx_builder.add_mint_asset(&mint_script1, &name1, amount.clone()); - tx_builder.add_mint_asset(&mint_script2, &name2, amount.clone()); - tx_builder.add_mint_asset(&mint_script3, &name3, amount.clone()); - tx_builder.add_mint_asset(&mint_script3, &name4, amount.clone()); - - let mint = tx_builder.get_mint().unwrap(); - let mint_len = mint.to_bytes().len(); - - let mint_scripts = tx_builder.get_witness_set(); - let mint_scripts_len = - mint_scripts.to_bytes().len() - TransactionWitnessSet::new().to_bytes().len(); - - let fee_coefficient = tx_builder.config.fee_algo.coefficient(); - - let raw_mint_fee = fee_coefficient - .checked_mul(&to_bignum(mint_len as u64)) - .unwrap(); - - let raw_mint_script_fee = fee_coefficient - .checked_mul(&to_bignum(mint_scripts_len as u64)) - .unwrap(); - - assert_eq!(raw_mint_fee, to_bignum(5544)); - assert_eq!(raw_mint_script_fee, to_bignum(4312)); - - let new_tx_fee = tx_builder.min_fee().unwrap(); - - let fee_diff_from_adding_mint = new_tx_fee.checked_sub(&original_tx_fee).unwrap(); - - let witness_fee_increase = fee_diff_from_adding_mint - .checked_sub(&raw_mint_fee) - .unwrap() - .checked_sub(&raw_mint_script_fee) - .unwrap(); - - assert_eq!(witness_fee_increase, to_bignum(8932)); - - let fee_increase_bytes = from_bignum(&witness_fee_increase) - .checked_div(from_bignum(&fee_coefficient)) - .unwrap(); - - // Two vkey witnesses 96 bytes each (32 byte pubkey + 64 byte signature) - // Plus 11 bytes overhead for CBOR wrappers - // This is happening because we have three different minting policies - // but the same key-hash from one of them is already also used in inputs - // so no suplicate witness signature is require for that one - assert_eq!(fee_increase_bytes, 203); - } - - #[test] - fn fee_estimation_fails_on_missing_mint_scripts() { - let mut tx_builder = create_reallistic_tx_builder(); - - // No error estimating fee without mint - assert!(tx_builder.min_fee().is_ok()); - - let (mint_script1, policy_id1) = mint_script_and_policy(0); - let (mint_script2, _) = mint_script_and_policy(1); - - let name1 = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - let amount = Int::new_i32(1234); - - let mut mint = Mint::new(); - mint.insert( - &policy_id1, - &MintAssets::new_from_entry(&name1, amount.clone()), - ); - - tx_builder - .set_mint(&mint, &NativeScripts::from(vec![mint_script1])) - .unwrap(); - - let est1 = tx_builder.min_fee(); - assert!(est1.is_ok()); - - tx_builder.add_mint_asset(&mint_script2, &name1, amount.clone()); - - let est2 = tx_builder.min_fee(); - assert!(est2.is_ok()); - - // Native script assertion has been commented out in `.min_fee` - // Until implemented in a more performant manner - // TODO: these test parts might be returned back when it's done - - // // Remove one mint script - // tx_builder.mint_scripts = - // Some(NativeScripts::from(vec![tx_builder.mint_scripts.unwrap().get(1)])); - // - // // Now two different policies are minted but only one witness script is present - // let est3 = tx_builder.min_fee(); - // assert!(est3.is_err()); - // assert!(est3.err().unwrap().to_string().contains(&format!("{:?}", hex::encode(policy_id1.to_bytes())))); - // - // // Remove all mint scripts - // tx_builder.mint_scripts = Some(NativeScripts::new()); - // - // // Mint exists but no witness scripts at all present - // let est4 = tx_builder.min_fee(); - // assert!(est4.is_err()); - // assert!(est4.err().unwrap().to_string().contains("witness scripts are not provided")); - // - // // Remove all mint scripts - // tx_builder.mint_scripts = None; - // - // // Mint exists but no witness scripts at all present - // let est5 = tx_builder.min_fee(); - // assert!(est5.is_err()); - // assert!(est5.err().unwrap().to_string().contains("witness scripts are not provided")); - } - - #[test] - fn total_input_output_with_mint_and_burn() { - let mut tx_builder = create_tx_builder_with_fee(&create_linear_fee(0, 1)); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - - let (mint_script1, policy_id1) = mint_script_and_policy(0); - let (mint_script2, policy_id2) = mint_script_and_policy(1); - - let name = AssetName::new(vec![0u8, 1, 2, 3]).unwrap(); - - let ma_input1 = 100; - let ma_input2 = 200; - let ma_output1 = 60; - - let multiassets = [ma_input1, ma_input2, ma_output1] - .iter() - .map(|input| { - let mut multiasset = MultiAsset::new(); - multiasset.insert(&policy_id1, &{ - let mut assets = Assets::new(); - assets.insert(&name, &to_bignum(*input)); - assets - }); - multiasset.insert(&policy_id2, &{ - let mut assets = Assets::new(); - assets.insert(&name, &to_bignum(*input)); - assets - }); - multiasset - }) - .collect::>(); - - for (i, (multiasset, ada)) in multiassets - .iter() - .zip([100u64, 100, 100].iter().cloned().map(to_bignum)) - .enumerate() - { - let mut input_amount = Value::new(&ada); - input_amount.set_multiasset(multiasset); - - tx_builder.add_key_input( - &&spend.to_raw_key().hash(), - &TransactionInput::new(&genesis_id(), i as u32), - &input_amount, - ); - } - - tx_builder - .add_output( - &TransactionOutputBuilder::new() - .with_address(&byron_address()) - .next() - .unwrap() - .with_coin(&to_bignum(208)) - .build() - .unwrap(), - ) - .unwrap(); - - let total_input_before_mint = tx_builder.get_total_input().unwrap(); - let total_output_before_mint = tx_builder.get_total_output().unwrap(); - - assert_eq!(total_input_before_mint.coin, to_bignum(300)); - assert_eq!(total_output_before_mint.coin, to_bignum(208)); - let ma1_input = total_input_before_mint.multiasset.unwrap(); - let ma1_output = total_output_before_mint.multiasset; - assert_eq!( - ma1_input.get(&policy_id1).unwrap().get(&name).unwrap(), - to_bignum(360) - ); - assert_eq!( - ma1_input.get(&policy_id2).unwrap().get(&name).unwrap(), - to_bignum(360) - ); - assert!(ma1_output.is_none()); - - // Adding mint - tx_builder.add_mint_asset(&mint_script1, &name, Int::new_i32(40)); - - // Adding burn - tx_builder.add_mint_asset(&mint_script2, &name, Int::new_i32(-40)); - - let total_input_after_mint = tx_builder.get_total_input().unwrap(); - let total_output_after_mint = tx_builder.get_total_output().unwrap(); - - assert_eq!(total_input_after_mint.coin, to_bignum(300)); - assert_eq!(total_output_before_mint.coin, to_bignum(208)); - let ma2_input = total_input_after_mint.multiasset.unwrap(); - let ma2_output = total_output_after_mint.multiasset.unwrap(); - assert_eq!( - ma2_input.get(&policy_id1).unwrap().get(&name).unwrap(), - to_bignum(400) - ); - assert_eq!( - ma2_input.get(&policy_id2).unwrap().get(&name).unwrap(), - to_bignum(360) - ); - assert_eq!( - ma2_output.get(&policy_id2).unwrap().get(&name).unwrap(), - to_bignum(40) - ); - } - - fn create_base_address_from_script_hash(sh: &ScriptHash) -> Address { - BaseAddress::new( - NetworkInfo::testnet().network_id(), - &StakeCredential::from_scripthash(sh), - &StakeCredential::from_keyhash(&fake_key_hash(0)), - ) - .to_address() - } - - #[test] - fn test_set_input_scripts() { - let mut tx_builder = create_reallistic_tx_builder(); - let (script1, hash1) = mint_script_and_policy(0); - let (script2, hash2) = mint_script_and_policy(1); - let (script3, _hash3) = mint_script_and_policy(2); - // Trying to set native scripts to the builder - let rem0 = tx_builder.add_required_native_input_scripts(&NativeScripts::from(vec![ - script1.clone(), - script2.clone(), - script3.clone(), - ])); - assert_eq!(rem0, 0); - let missing0 = tx_builder.count_missing_input_scripts(); - assert_eq!(missing0, 0); - // Adding two script inputs using script1 and script2 hashes - tx_builder.add_input( - &create_base_address_from_script_hash(&hash1), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - tx_builder.add_input( - &create_base_address_from_script_hash(&hash2), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - // Setting a non-matching script will not change anything - let rem1 = tx_builder - .add_required_native_input_scripts(&NativeScripts::from(vec![script3.clone()])); - assert_eq!(rem1, 2); - let missing1 = tx_builder.count_missing_input_scripts(); - assert_eq!(missing1, 2); - // Setting one of the required scripts leaves one to be required - let rem2 = tx_builder.add_required_native_input_scripts(&NativeScripts::from(vec![ - script1.clone(), - script3.clone(), - ])); - assert_eq!(rem2, 1); - let missing2 = tx_builder.count_missing_input_scripts(); - assert_eq!(missing2, 1); - // Setting one non-required script again does not change anything - // But shows the state has changed - let rem3 = tx_builder - .add_required_native_input_scripts(&NativeScripts::from(vec![script3.clone()])); - assert_eq!(rem3, 1); - let missing3 = tx_builder.count_missing_input_scripts(); - assert_eq!(missing3, 1); - // Setting two required scripts will show both of them added - // And the remainder required is zero - let rem4 = tx_builder.add_required_native_input_scripts(&NativeScripts::from(vec![ - script1.clone(), - script2.clone(), - ])); - assert_eq!(rem4, 0); - let missing4 = tx_builder.count_missing_input_scripts(); - assert_eq!(missing4, 0); - // Setting empty scripts does not change anything - // But shows the state has changed - let rem5 = tx_builder.add_required_native_input_scripts(&NativeScripts::new()); - assert_eq!(rem5, 0); - } - - #[test] - fn test_add_native_script_input() { - let mut tx_builder = create_reallistic_tx_builder(); - let (script1, _hash1) = mint_script_and_policy(0); - let (script2, _hash2) = mint_script_and_policy(1); - let (script3, hash3) = mint_script_and_policy(2); - // Adding two script inputs directly with their witness - tx_builder.add_native_script_input( - &script1, - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - tx_builder.add_native_script_input( - &script2, - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - // Adding one script input indirectly via hash3 address - tx_builder.add_input( - &create_base_address_from_script_hash(&hash3), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - // Checking missing input scripts shows one - // Because first two inputs already have their witness - let missing1 = tx_builder.count_missing_input_scripts(); - assert_eq!(missing1, 1); - // Setting the required script leaves none to be required` - let rem1 = tx_builder - .add_required_native_input_scripts(&NativeScripts::from(vec![script3.clone()])); - assert_eq!(rem1, 0); - let missing2 = tx_builder.count_missing_input_scripts(); - assert_eq!(missing2, 0); - } - - fn unsafe_tx_len(b: &TransactionBuilder) -> usize { - b.build_tx_unsafe().unwrap().to_bytes().len() - } - - #[test] - fn test_native_input_scripts_are_added_to_the_witnesses() { - let mut tx_builder = create_reallistic_tx_builder(); - let (script1, _hash1) = mint_script_and_policy(0); - let (script2, hash2) = mint_script_and_policy(1); - tx_builder.set_fee(&to_bignum(42)); - tx_builder.add_native_script_input( - &script1, - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - let tx_len_before_new_script_input = unsafe_tx_len(&tx_builder); - tx_builder.add_input( - &create_base_address_from_script_hash(&hash2), - &TransactionInput::new(&genesis_id(), 1), - &Value::new(&to_bignum(1_000_000)), - ); - let tx_len_after_new_script_input = unsafe_tx_len(&tx_builder); - // Tx size increased cuz input is added even without the witness - assert!(tx_len_after_new_script_input > tx_len_before_new_script_input); - tx_builder.add_required_native_input_scripts(&NativeScripts::from(vec![script2.clone()])); - let tx_len_after_adding_script_witness = unsafe_tx_len(&tx_builder); - // Tx size increased cuz the witness is added to the witnesses - assert!(tx_len_after_adding_script_witness > tx_len_after_new_script_input); - tx_builder.add_required_native_input_scripts(&NativeScripts::from(vec![ - script1.clone(), - script2.clone(), - ])); - let tx_len_after_adding_script_witness_again = unsafe_tx_len(&tx_builder); - // Tx size did not change because calling to add same witnesses again doesn't change anything - assert!(tx_len_after_adding_script_witness == tx_len_after_adding_script_witness_again); - let tx: Transaction = tx_builder.build_tx_unsafe().unwrap(); - assert!(tx.witness_set.native_scripts.is_some()); - let native_scripts = tx.witness_set.native_scripts.unwrap(); - assert_eq!(native_scripts.len(), 2); - assert_eq!(native_scripts.get(0), script1); - assert_eq!(native_scripts.get(1), script2); - } - - #[test] - fn test_building_with_missing_witness_script_fails() { - let mut tx_builder = create_reallistic_tx_builder(); - let (script1, _hash1) = mint_script_and_policy(0); - let (script2, hash2) = mint_script_and_policy(1); - tx_builder.set_fee(&to_bignum(42)); - // Ok to build before any inputs - assert!(tx_builder.build_tx().is_ok()); - // Adding native script input which adds the witness right away - tx_builder.add_native_script_input( - &script1, - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - // Ok to build when witness is added along with the input - assert!(tx_builder.build_tx().is_ok()); - // Adding script input without the witness - tx_builder.add_input( - &create_base_address_from_script_hash(&hash2), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - // Not ok to build when missing a witness - assert!(tx_builder.build_tx().is_err()); - // Can force to build using unsafe - assert!(tx_builder.build_tx_unsafe().is_ok()); - // Adding the missing witness script - tx_builder.add_required_native_input_scripts(&NativeScripts::from(vec![script2.clone()])); - // Ok to build when all witnesses are added - assert!(tx_builder.build_tx().is_ok()); - } - - #[test] - fn test_adding_plutus_script_input() { - let mut tx_builder = create_reallistic_tx_builder(); - let (script1, _) = plutus_script_and_hash(0); - let datum = PlutusData::new_bytes(fake_bytes_32(1)); - let redeemer_datum = PlutusData::new_bytes(fake_bytes_32(2)); - let redeemer = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &redeemer_datum, - &ExUnits::new(&to_bignum(1), &to_bignum(2)), - ); - tx_builder.add_plutus_script_input( - &PlutusWitness::new(&script1, &datum, &redeemer), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - tx_builder.set_fee(&to_bignum(42)); - // There are no missing script witnesses - assert_eq!(tx_builder.count_missing_input_scripts(), 0); - let tx: Transaction = tx_builder.build_tx_unsafe().unwrap(); - assert!(tx.witness_set.plutus_scripts.is_some()); - assert_eq!(tx.witness_set.plutus_scripts.unwrap().get(0), script1); - assert!(tx.witness_set.plutus_data.is_some()); - assert_eq!(tx.witness_set.plutus_data.unwrap().get(0), datum); - assert!(tx.witness_set.redeemers.is_some()); - assert_eq!(tx.witness_set.redeemers.unwrap().get(0), redeemer); - } - - #[test] - fn test_adding_plutus_script_witnesses() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(42)); - let (script1, hash1) = plutus_script_and_hash(0); - let (script2, hash2) = plutus_script_and_hash(1); - let (script3, _hash3) = plutus_script_and_hash(3); - let datum1 = PlutusData::new_bytes(fake_bytes_32(10)); - let datum2 = PlutusData::new_bytes(fake_bytes_32(11)); - let redeemer1 = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &PlutusData::new_bytes(fake_bytes_32(20)), - &ExUnits::new(&to_bignum(1), &to_bignum(2)), - ); - let redeemer2 = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(1), - &PlutusData::new_bytes(fake_bytes_32(21)), - &ExUnits::new(&to_bignum(1), &to_bignum(2)), - ); - tx_builder.add_input( - &create_base_address_from_script_hash(&hash1), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - tx_builder.add_input( - &create_base_address_from_script_hash(&hash2), - &TransactionInput::new(&genesis_id(), 1), - &Value::new(&to_bignum(1_000_000)), - ); - // There are TWO missing script witnesses - assert_eq!(tx_builder.count_missing_input_scripts(), 2); - // Calling to add two plutus witnesses, one of which is irrelevant - tx_builder.add_required_plutus_input_scripts(&PlutusWitnesses::from(vec![ - PlutusWitness::new(&script1, &datum1, &redeemer1), - PlutusWitness::new(&script3, &datum2, &redeemer2), - ])); - // There is now ONE missing script witnesses - assert_eq!(tx_builder.count_missing_input_scripts(), 1); - // Calling to add the one remaining relevant plutus witness now - tx_builder.add_required_plutus_input_scripts(&PlutusWitnesses::from(vec![ - PlutusWitness::new(&script2, &datum2, &redeemer2), - ])); - // There is now no missing script witnesses - assert_eq!(tx_builder.count_missing_input_scripts(), 0); - let tx: Transaction = tx_builder.build_tx_unsafe().unwrap(); - // Check there are two correct scripts - assert!(tx.witness_set.plutus_scripts.is_some()); - let pscripts = tx.witness_set.plutus_scripts.unwrap(); - assert_eq!(pscripts.len(), 2); - assert_eq!(pscripts.get(0), script1); - assert_eq!(pscripts.get(1), script2); - // Check there are two correct datums - assert!(tx.witness_set.plutus_data.is_some()); - let datums = tx.witness_set.plutus_data.unwrap(); - assert_eq!(datums.len(), 2); - assert_eq!(datums.get(0), datum1); - assert_eq!(datums.get(1), datum2); - // Check there are two correct redeemers - assert!(tx.witness_set.redeemers.is_some()); - let redeems = tx.witness_set.redeemers.unwrap(); - assert_eq!(redeems.len(), 2); - assert_eq!(redeems.get(0), redeemer1); - assert_eq!(redeems.get(1), redeemer2); - } - - fn create_collateral() -> TxInputsBuilder { - let mut collateral_builder = TxInputsBuilder::new(); - collateral_builder.add_input( - &byron_address(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - collateral_builder - } - - #[test] - fn test_existing_plutus_scripts_require_data_hash() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(42)); - tx_builder.set_collateral(&create_collateral()); - let (script1, _) = plutus_script_and_hash(0); - let datum = PlutusData::new_bytes(fake_bytes_32(1)); - let redeemer_datum = PlutusData::new_bytes(fake_bytes_32(2)); - let redeemer = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &redeemer_datum, - &ExUnits::new(&to_bignum(1), &to_bignum(2)), - ); - tx_builder.add_plutus_script_input( - &PlutusWitness::new(&script1, &datum, &redeemer), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - - // Using SAFE `.build_tx` - let res = tx_builder.build_tx(); - assert!(res.is_err()); - if let Err(e) = res { - assert!(e.as_string().unwrap().contains("script data hash")); - } - - // Setting script data hash removes the error - tx_builder.set_script_data_hash(&ScriptDataHash::from_bytes(fake_bytes_32(42)).unwrap()); - // Using SAFE `.build_tx` - let res2 = tx_builder.build_tx(); - assert!(res2.is_ok()); - - // Removing script data hash will cause error again - tx_builder.remove_script_data_hash(); - // Using SAFE `.build_tx` - let res3 = tx_builder.build_tx(); - assert!(res3.is_err()); - } - - #[test] - fn test_calc_script_hash_data() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(42)); - tx_builder.set_collateral(&create_collateral()); - - let (script1, _) = plutus_script_and_hash(0); - let datum = PlutusData::new_bytes(fake_bytes_32(1)); - let redeemer_datum = PlutusData::new_bytes(fake_bytes_32(2)); - let redeemer = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &redeemer_datum, - &ExUnits::new(&to_bignum(1), &to_bignum(2)), - ); - tx_builder.add_plutus_script_input( - &PlutusWitness::new(&script1, &datum, &redeemer), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - - // Setting script data hash removes the error - tx_builder - .calc_script_data_hash(&TxBuilderConstants::plutus_default_cost_models()) - .unwrap(); - - // Using SAFE `.build_tx` - let res2 = tx_builder.build_tx(); - assert!(res2.is_ok()); - - let mut used_langs = Languages::new(); - used_langs.add(Language::new_plutus_v1()); - - let data_hash = hash_script_data( - &Redeemers::from(vec![redeemer.clone()]), - &TxBuilderConstants::plutus_default_cost_models().retain_language_versions(&used_langs), - Some(PlutusList::from(vec![datum])), - ); - assert_eq!(tx_builder.script_data_hash.unwrap(), data_hash); - } - - #[test] - fn test_plutus_witness_redeemer_index_auto_changing() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(42)); - tx_builder.set_collateral(&create_collateral()); - let (script1, _) = plutus_script_and_hash(0); - let (script2, _) = plutus_script_and_hash(1); - let datum1 = PlutusData::new_bytes(fake_bytes_32(10)); - let datum2 = PlutusData::new_bytes(fake_bytes_32(11)); - - // Creating redeemers with indexes ZERO - let redeemer1 = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &PlutusData::new_bytes(fake_bytes_32(20)), - &ExUnits::new(&to_bignum(1), &to_bignum(2)), - ); - let redeemer2 = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &PlutusData::new_bytes(fake_bytes_32(21)), - &ExUnits::new(&to_bignum(1), &to_bignum(2)), - ); - - // Add a regular NON-script input first - tx_builder.add_input( - &byron_address(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - - // Adding two plutus inputs then - // both have redeemers with index ZERO - tx_builder.add_plutus_script_input( - &PlutusWitness::new(&script1, &datum1, &redeemer1), - &TransactionInput::new(&genesis_id(), 1), - &Value::new(&to_bignum(1_000_000)), - ); - tx_builder.add_plutus_script_input( - &PlutusWitness::new(&script2, &datum2, &redeemer2), - &TransactionInput::new(&genesis_id(), 2), - &Value::new(&to_bignum(1_000_000)), - ); - - // Calc the script data hash - tx_builder - .calc_script_data_hash(&TxBuilderConstants::plutus_default_cost_models()) - .unwrap(); - - let tx: Transaction = tx_builder.build_tx().unwrap(); - assert!(tx.witness_set.redeemers.is_some()); - let redeems = tx.witness_set.redeemers.unwrap(); - assert_eq!(redeems.len(), 2); - - fn compare_redeems(r1: Redeemer, r2: Redeemer) { - assert_eq!(r1.tag(), r2.tag()); - assert_eq!(r1.data(), r2.data()); - assert_eq!(r1.ex_units(), r2.ex_units()); - } - - compare_redeems(redeems.get(0), redeemer1); - compare_redeems(redeems.get(1), redeemer2); - - // Note the redeemers from the result transaction are equal with source redeemers - // In everything EXCEPT the index field, the indexes have changed to 1 and 2 - // To match the position of their corresponding input - assert_eq!(redeems.get(0).index(), to_bignum(1)); - assert_eq!(redeems.get(1).index(), to_bignum(2)); - } - - #[test] - fn test_native_and_plutus_scripts_together() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(42)); - tx_builder.set_collateral(&create_collateral()); - let (pscript1, _) = plutus_script_and_hash(0); - let (pscript2, phash2) = plutus_script_and_hash(1); - let (nscript1, _) = mint_script_and_policy(0); - let (nscript2, nhash2) = mint_script_and_policy(1); - let datum1 = PlutusData::new_bytes(fake_bytes_32(10)); - let datum2 = PlutusData::new_bytes(fake_bytes_32(11)); - // Creating redeemers with indexes ZERO - let redeemer1 = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &PlutusData::new_bytes(fake_bytes_32(20)), - &ExUnits::new(&to_bignum(1), &to_bignum(2)), - ); - let redeemer2 = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &PlutusData::new_bytes(fake_bytes_32(21)), - &ExUnits::new(&to_bignum(1), &to_bignum(2)), - ); - - // Add one plutus input directly with witness - tx_builder.add_plutus_script_input( - &PlutusWitness::new(&pscript1, &datum1, &redeemer1), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - // Add one native input directly with witness - tx_builder.add_native_script_input( - &nscript1, - &TransactionInput::new(&genesis_id(), 1), - &Value::new(&to_bignum(1_000_000)), - ); - // Add one plutus input generically without witness - tx_builder.add_input( - &create_base_address_from_script_hash(&phash2), - &TransactionInput::new(&genesis_id(), 2), - &Value::new(&to_bignum(1_000_000)), - ); - // Add one native input generically without witness - tx_builder.add_input( - &create_base_address_from_script_hash(&nhash2), - &TransactionInput::new(&genesis_id(), 3), - &Value::new(&to_bignum(1_000_000)), - ); - - // There are two missing script witnesses - assert_eq!(tx_builder.count_missing_input_scripts(), 2); - - let remaining1 = - tx_builder.add_required_plutus_input_scripts(&PlutusWitnesses::from(vec![ - PlutusWitness::new(&pscript2, &datum2, &redeemer2), - ])); - - // There is one missing script witness now - assert_eq!(remaining1, 1); - assert_eq!(tx_builder.count_missing_input_scripts(), 1); - - let remaining2 = tx_builder - .add_required_native_input_scripts(&NativeScripts::from(vec![nscript2.clone()])); - - // There are no missing script witnesses now - assert_eq!(remaining2, 0); - assert_eq!(tx_builder.count_missing_input_scripts(), 0); - - tx_builder - .calc_script_data_hash(&TxBuilderConstants::plutus_default_cost_models()) - .unwrap(); - - let tx: Transaction = tx_builder.build_tx().unwrap(); - - let wits = tx.witness_set; - assert!(wits.native_scripts.is_some()); - assert!(wits.plutus_scripts.is_some()); - assert!(wits.plutus_data.is_some()); - assert!(wits.redeemers.is_some()); - - let nscripts = wits.native_scripts.unwrap(); - assert_eq!(nscripts.len(), 2); - assert_eq!(nscripts.get(0), nscript1); - assert_eq!(nscripts.get(1), nscript2); - - let pscripts = wits.plutus_scripts.unwrap(); - assert_eq!(pscripts.len(), 2); - assert_eq!(pscripts.get(0), pscript1); - assert_eq!(pscripts.get(1), pscript2); - - let datums = wits.plutus_data.unwrap(); - assert_eq!(datums.len(), 2); - assert_eq!(datums.get(0), datum1); - assert_eq!(datums.get(1), datum2); - - let redeems = wits.redeemers.unwrap(); - assert_eq!(redeems.len(), 2); - assert_eq!(redeems.get(0), redeemer1); - - // The second plutus input redeemer index has automatically changed to 2 - // because it was added on the third position - assert_eq!(redeems.get(1), redeemer2.clone_with_index(&to_bignum(2))); - } - - #[test] - fn test_json_serialization_native_and_plutus_scripts_together() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(42)); - tx_builder.set_collateral(&create_collateral()); - let (pscript1, _) = plutus_script_and_hash(0); - let (pscript2, phash2) = plutus_script_and_hash(1); - let (nscript1, _) = mint_script_and_policy(0); - let (nscript2, nhash2) = mint_script_and_policy(1); - let datum1 = PlutusData::new_bytes(fake_bytes_32(10)); - let datum2 = PlutusData::new_bytes(fake_bytes_32(11)); - // Creating redeemers with indexes ZERO - let redeemer1 = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &PlutusData::new_bytes(fake_bytes_32(20)), - &ExUnits::new(&to_bignum(1), &to_bignum(2)), - ); - let redeemer2 = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &PlutusData::new_bytes(fake_bytes_32(21)), - &ExUnits::new(&to_bignum(1), &to_bignum(2)), - ); - - // Add one plutus input directly with witness - tx_builder.add_plutus_script_input( - &PlutusWitness::new(&pscript1, &datum1, &redeemer1), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - // Add one native input directly with witness - tx_builder.add_native_script_input( - &nscript1, - &TransactionInput::new(&genesis_id(), 1), - &Value::new(&to_bignum(1_000_000)), - ); - // Add one plutus input generically without witness - tx_builder.add_input( - &create_base_address_from_script_hash(&phash2), - &TransactionInput::new(&genesis_id(), 2), - &Value::new(&to_bignum(1_000_000)), - ); - // Add one native input generically without witness - tx_builder.add_input( - &create_base_address_from_script_hash(&nhash2), - &TransactionInput::new(&genesis_id(), 3), - &Value::new(&to_bignum(1_000_000)), - ); - - // There are two missing script witnesses - assert_eq!(tx_builder.count_missing_input_scripts(), 2); - - let remaining1 = - tx_builder.add_required_plutus_input_scripts(&PlutusWitnesses::from(vec![ - PlutusWitness::new(&pscript2, &datum2, &redeemer2), - ])); - - // There is one missing script witness now - assert_eq!(remaining1, 1); - assert_eq!(tx_builder.count_missing_input_scripts(), 1); - - let remaining2 = tx_builder - .add_required_native_input_scripts(&NativeScripts::from(vec![nscript2.clone()])); - - // There are no missing script witnesses now - assert_eq!(remaining2, 0); - assert_eq!(tx_builder.count_missing_input_scripts(), 0); - - tx_builder.calc_script_data_hash(&TxBuilderConstants::plutus_default_cost_models()); - - let tx: Transaction = tx_builder.build_tx().unwrap(); - - let json_tx = tx.to_json().unwrap(); - let deser_tx = Transaction::from_json(json_tx.as_str()).unwrap(); - - assert_eq!(deser_tx.to_bytes(), tx.to_bytes()); - assert_eq!(deser_tx.to_json().unwrap(), tx.to_json().unwrap()); - } - - #[test] - fn test_regular_and_collateral_inputs_same_keyhash() { - let mut input_builder = TxInputsBuilder::new(); - let mut collateral_builder = TxInputsBuilder::new(); - - // Add a single input of both kinds with the SAME keyhash - input_builder.add_input( - &fake_base_address(0), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - collateral_builder.add_input( - &fake_base_address(0), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - - fn get_fake_vkeys_count(i: &TxInputsBuilder, c: &TxInputsBuilder) -> usize { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(42)); - tx_builder.set_inputs(i); - tx_builder.set_collateral(c); - let tx: Transaction = fake_full_tx(&tx_builder, tx_builder.build().unwrap()).unwrap(); - tx.witness_set.vkeys.unwrap().len() - } - - // There's only one fake witness in the builder - // because a regular and a collateral inputs both use the same keyhash - assert_eq!(get_fake_vkeys_count(&input_builder, &collateral_builder), 1); - - // Add a new input of each kind with DIFFERENT keyhashes - input_builder.add_input( - &fake_base_address(1), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - collateral_builder.add_input( - &fake_base_address(2), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - - // There are now three fake witnesses in the builder - // because all three unique keyhashes got combined - assert_eq!(get_fake_vkeys_count(&input_builder, &collateral_builder), 3); - } - - #[test] - fn test_regular_and_collateral_inputs_together() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(42)); - let (pscript1, _) = plutus_script_and_hash(0); - let (pscript2, _) = plutus_script_and_hash(1); - let (nscript1, _) = mint_script_and_policy(0); - let (nscript2, _) = mint_script_and_policy(1); - let datum1 = PlutusData::new_bytes(fake_bytes_32(10)); - let datum2 = PlutusData::new_bytes(fake_bytes_32(11)); - // Creating redeemers with indexes ZERO - let redeemer1 = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &PlutusData::new_bytes(fake_bytes_32(20)), - &ExUnits::new(&to_bignum(1), &to_bignum(2)), - ); - let redeemer2 = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &PlutusData::new_bytes(fake_bytes_32(21)), - &ExUnits::new(&to_bignum(1), &to_bignum(2)), - ); - - let mut input_builder = TxInputsBuilder::new(); - let mut collateral_builder = TxInputsBuilder::new(); - - input_builder.add_native_script_input( - &nscript1, - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - collateral_builder.add_native_script_input( - &nscript2, - &TransactionInput::new(&genesis_id(), 1), - &Value::new(&to_bignum(1_000_000)), - ); - - input_builder.add_plutus_script_input( - &PlutusWitness::new(&pscript1, &datum1, &redeemer1), - &TransactionInput::new(&genesis_id(), 2), - &Value::new(&to_bignum(1_000_000)), - ); - collateral_builder.add_plutus_script_input( - &PlutusWitness::new(&pscript2, &datum2, &redeemer2), - &TransactionInput::new(&genesis_id(), 3), - &Value::new(&to_bignum(1_000_000)), - ); - - tx_builder.set_inputs(&input_builder); - tx_builder.set_collateral(&collateral_builder); - - tx_builder - .calc_script_data_hash(&TxBuilderConstants::plutus_default_cost_models()) - .unwrap(); - - let w: &TransactionWitnessSet = &tx_builder.build_tx().unwrap().witness_set; - - assert!(w.native_scripts.is_some()); - let nscripts = w.native_scripts.as_ref().unwrap(); - assert_eq!(nscripts.len(), 2); - assert_eq!(nscripts.get(0), nscript1); - assert_eq!(nscripts.get(1), nscript2); - - assert!(w.plutus_scripts.is_some()); - let pscripts = w.plutus_scripts.as_ref().unwrap(); - assert_eq!(pscripts.len(), 2); - assert_eq!(pscripts.get(0), pscript1); - assert_eq!(pscripts.get(1), pscript2); - - assert!(w.plutus_data.is_some()); - let datums = w.plutus_data.as_ref().unwrap(); - assert_eq!(datums.len(), 2); - assert_eq!(datums.get(0), datum1); - assert_eq!(datums.get(1), datum2); - - assert!(w.redeemers.is_some()); - let redeemers = w.redeemers.as_ref().unwrap(); - assert_eq!(redeemers.len(), 2); - assert_eq!(redeemers.get(0), redeemer1.clone_with_index(&to_bignum(1))); - assert_eq!(redeemers.get(1), redeemer2.clone_with_index(&to_bignum(1))); - } - - #[test] - fn test_ex_unit_costs_are_added_to_the_fees() { - fn calc_fee_with_ex_units(mem: u64, step: u64) -> Coin { - let mut input_builder = TxInputsBuilder::new(); - let mut collateral_builder = TxInputsBuilder::new(); - - // Add a single input of both kinds with the SAME keyhash - input_builder.add_input( - &fake_base_address(0), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - collateral_builder.add_input( - &fake_base_address(0), - &TransactionInput::new(&genesis_id(), 1), - &Value::new(&to_bignum(1_000_000)), - ); - - let (pscript1, _) = plutus_script_and_hash(0); - let datum1 = PlutusData::new_bytes(fake_bytes_32(10)); - let redeemer1 = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &PlutusData::new_bytes(fake_bytes_32(20)), - &ExUnits::new(&to_bignum(mem), &to_bignum(step)), - ); - input_builder.add_plutus_script_input( - &PlutusWitness::new(&pscript1, &datum1, &redeemer1), - &TransactionInput::new(&genesis_id(), 2), - &Value::new(&to_bignum(1_000_000)), - ); - - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_inputs(&input_builder); - tx_builder.set_collateral(&collateral_builder); - - tx_builder - .add_change_if_needed(&fake_base_address(42)) - .unwrap(); - - tx_builder.get_fee_if_set().unwrap() - } - - assert_eq!(calc_fee_with_ex_units(0, 0), to_bignum(173509)); - assert_eq!(calc_fee_with_ex_units(10000, 0), to_bignum(174174)); - assert_eq!(calc_fee_with_ex_units(0, 10000000), to_bignum(174406)); - assert_eq!(calc_fee_with_ex_units(10000, 10000000), to_bignum(175071)); - } - - #[test] - fn test_script_inputs_ordering() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(42)); - let (nscript1, _) = mint_script_and_policy(0); - let (pscript1, _) = plutus_script_and_hash(0); - let (pscript2, _) = plutus_script_and_hash(1); - let datum1 = PlutusData::new_bytes(fake_bytes_32(10)); - let datum2 = PlutusData::new_bytes(fake_bytes_32(11)); - // Creating redeemers with indexes ZERO - let pdata1 = PlutusData::new_bytes(fake_bytes_32(20)); - let pdata2 = PlutusData::new_bytes(fake_bytes_32(21)); - let redeemer1 = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &pdata1, - &ExUnits::new(&to_bignum(1), &to_bignum(2)), - ); - let redeemer2 = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &pdata2, - &ExUnits::new(&to_bignum(1), &to_bignum(2)), - ); - - tx_builder.add_plutus_script_input( - &PlutusWitness::new(&pscript1, &datum1, &redeemer1), - &fake_tx_input2(2, 1), - &fake_value(), - ); - tx_builder.add_native_script_input(&nscript1, &fake_tx_input2(1, 0), &fake_value()); - tx_builder.add_plutus_script_input( - &PlutusWitness::new(&pscript2, &datum2, &redeemer2), - &fake_tx_input2(2, 0), - &fake_value(), - ); - - let tx: Transaction = tx_builder.build_tx_unsafe().unwrap(); - - let ins = tx.body.inputs; - assert_eq!(ins.len(), 3); - assert_eq!(ins.get(0).transaction_id.0[0], 1); - assert_eq!(ins.get(1).transaction_id.0[0], 2); - assert_eq!(ins.get(1).index, 0); - assert_eq!(ins.get(2).transaction_id.0[0], 2); - assert_eq!(ins.get(2).index, 1); - - let r: Redeemers = tx.witness_set.redeemers.unwrap(); - assert_eq!(r.len(), 2); - - // Redeemer1 now has the index 2 even tho the input was added first - assert_eq!(r.get(0).data(), pdata1); - assert_eq!(r.get(0).index(), to_bignum(2)); - - // Redeemer1 now has the index 1 even tho the input was added last - assert_eq!(r.get(1).data(), pdata2); - assert_eq!(r.get(1).index(), to_bignum(1)); - } - - #[test] - fn test_required_signers() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(42)); - let tx1: TransactionBody = tx_builder.build().unwrap(); - assert!(tx1.required_signers.is_none()); - - let s1 = fake_key_hash(1); - let s2 = fake_key_hash(22); - let s3 = fake_key_hash(133); - - tx_builder.add_required_signer(&s1); - tx_builder.add_required_signer(&s3); - tx_builder.add_required_signer(&s2); - - let tx1: TransactionBody = tx_builder.build().unwrap(); - assert!(tx1.required_signers.is_some()); - - let rs: RequiredSigners = tx1.required_signers.unwrap(); - assert_eq!(rs.len(), 3); - assert_eq!(rs.get(0), s1); - assert_eq!(rs.get(1), s3); - assert_eq!(rs.get(2), s2); - } - - #[test] - fn test_required_signers_are_added_to_the_witness_estimate() { - fn count_fake_witnesses_with_required_signers(keys: &Ed25519KeyHashes) -> usize { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(42)); - tx_builder.add_input( - &fake_base_address(0), - &TransactionInput::new(&fake_tx_hash(0), 0), - &Value::new(&to_bignum(10_000_000)), - ); - - keys.0.iter().for_each(|k| { - tx_builder.add_required_signer(k); - }); - - let tx: Transaction = fake_full_tx(&tx_builder, tx_builder.build().unwrap()).unwrap(); - tx.witness_set.vkeys.unwrap().len() - } - - assert_eq!( - count_fake_witnesses_with_required_signers(&Ed25519KeyHashes::new(),), - 1 - ); - - assert_eq!( - count_fake_witnesses_with_required_signers(&Ed25519KeyHashes(vec![fake_key_hash(1)]),), - 2 - ); - - assert_eq!( - count_fake_witnesses_with_required_signers(&Ed25519KeyHashes(vec![ - fake_key_hash(1), - fake_key_hash(2) - ]),), - 3 - ); - - // This case still produces only 3 fake signatures, because the same key is already used in the input address - assert_eq!( - count_fake_witnesses_with_required_signers(&Ed25519KeyHashes(vec![ - fake_key_hash(1), - fake_key_hash(2), - fake_key_hash(0) - ]),), - 3 - ); - - // When a different key is used - 4 fake witnesses are produced - assert_eq!( - count_fake_witnesses_with_required_signers(&Ed25519KeyHashes(vec![ - fake_key_hash(1), - fake_key_hash(2), - fake_key_hash(3) - ]),), - 4 - ); - } - - #[test] - fn collateral_return_and_total_collateral_setters() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(123456)); - - let mut inp = TxInputsBuilder::new(); - inp.add_input(&fake_base_address(0), &fake_tx_input(0), &fake_value()); - - tx_builder.set_inputs(&inp); - tx_builder.set_collateral(&inp); - - let col_return = TransactionOutput::new(&fake_base_address(1), &fake_value2(123123)); - let col_total = to_bignum(234234); - - tx_builder.set_collateral_return(&col_return); - tx_builder.set_total_collateral(&col_total); - - let tx: Transaction = tx_builder.build_tx_unsafe().unwrap(); - assert!(tx.body.collateral_return.is_some()); - assert_eq!(tx.body.collateral_return.unwrap(), col_return); - assert!(tx.body.total_collateral.is_some()); - assert_eq!(tx.body.total_collateral.unwrap(), col_total); - } - - fn fake_multiasset(amount: u64) -> MultiAsset { - let (_, policy_id) = mint_script_and_policy(234); - let mut assets = Assets::new(); - assets.insert( - &AssetName::new(fake_bytes_32(235)).unwrap(), - &to_bignum(amount), - ); - let mut masset = MultiAsset::new(); - masset.insert(&policy_id, &assets); - masset - } - - #[test] - fn inputs_builder_total_value() { - let mut b = TxInputsBuilder::new(); - assert_eq!(b.total_value().unwrap(), Value::zero()); - - b.add_input( - &fake_base_address(0), - &fake_tx_input(0), - &fake_value2(100_000), - ); - assert_eq!(b.total_value().unwrap(), Value::new(&to_bignum(100_000))); - - b.add_input( - &fake_base_address(1), - &fake_tx_input(1), - &fake_value2(200_000), - ); - assert_eq!(b.total_value().unwrap(), Value::new(&to_bignum(300_000))); - - let masset = fake_multiasset(123); - - b.add_input( - &fake_base_address(2), - &fake_tx_input(2), - &Value::new_with_assets(&to_bignum(300_000), &masset), - ); - assert_eq!( - b.total_value().unwrap(), - Value::new_with_assets(&to_bignum(600_000), &masset) - ); - } - - #[test] - fn test_auto_calc_total_collateral() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(123456)); - - let mut inp = TxInputsBuilder::new(); - let collateral_input_value = 2_000_000; - inp.add_input( - &fake_base_address(0), - &fake_tx_input(0), - &fake_value2(collateral_input_value.clone()), - ); - - tx_builder.set_collateral(&inp); - - let collateral_return_value = 1_234_567; - let col_return = TransactionOutput::new( - &fake_base_address(1), - &fake_value2(collateral_return_value.clone()), - ); - - tx_builder - .set_collateral_return_and_total(&col_return) - .unwrap(); - - assert!(tx_builder.collateral_return.is_some()); - assert_eq!(tx_builder.collateral_return.unwrap(), col_return,); - - assert!(tx_builder.total_collateral.is_some()); - assert_eq!( - tx_builder.total_collateral.unwrap(), - to_bignum(collateral_input_value - collateral_return_value), - ); - } - - #[test] - fn test_auto_calc_total_collateral_with_assets() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(123456)); - - let masset = fake_multiasset(123); - - let mut inp = TxInputsBuilder::new(); - let collateral_input_value = 2_000_000; - inp.add_input( - &fake_base_address(0), - &fake_tx_input(0), - &Value::new_with_assets(&to_bignum(collateral_input_value.clone()), &masset), - ); - - tx_builder.set_collateral(&inp); - - let collateral_return_value = 1_345_678; - let col_return = TransactionOutput::new( - &fake_base_address(1), - &Value::new_with_assets(&to_bignum(collateral_return_value.clone()), &masset), - ); - - tx_builder - .set_collateral_return_and_total(&col_return) - .unwrap(); - - assert!(tx_builder.collateral_return.is_some()); - assert_eq!(tx_builder.collateral_return.unwrap(), col_return,); - - assert!(tx_builder.total_collateral.is_some()); - assert_eq!( - tx_builder.total_collateral.unwrap(), - to_bignum(collateral_input_value - collateral_return_value), - ); - } - - #[test] - fn test_auto_calc_total_collateral_fails_with_assets() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(123456)); - - let masset = fake_multiasset(123); - - let mut inp = TxInputsBuilder::new(); - let collateral_input_value = 2_000_000; - inp.add_input( - &fake_base_address(0), - &fake_tx_input(0), - &Value::new_with_assets(&to_bignum(collateral_input_value.clone()), &masset), - ); - - tx_builder.set_collateral(&inp); - - // Collateral return does not handle ALL the assets from collateral input - let collateral_return_value = 1_345_678; - let col_return = TransactionOutput::new( - &fake_base_address(1), - &fake_value2(collateral_return_value.clone()), - ); - - let res = tx_builder.set_collateral_return_and_total(&col_return); - - // Function call returns an error - assert!(res.is_err()); - - // NEITHER total collateral nor collateral return are changed in the builder - assert!(tx_builder.total_collateral.is_none()); - assert!(tx_builder.collateral_return.is_none()); - } - - #[test] - fn test_auto_calc_total_collateral_fails_on_no_collateral() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(123456)); - - let res = tx_builder.set_collateral_return_and_total(&TransactionOutput::new( - &fake_base_address(1), - &fake_value2(1_345_678), - )); - - // Function call returns an error - assert!(res.is_err()); - - // NEITHER total collateral nor collateral return are changed in the builder - assert!(tx_builder.total_collateral.is_none()); - assert!(tx_builder.collateral_return.is_none()); - } - - #[test] - fn test_auto_calc_total_collateral_fails_on_no_ada() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(123456)); - - let mut inp = TxInputsBuilder::new(); - let collateral_input_value = 2_000_000; - inp.add_input( - &fake_base_address(0), - &fake_tx_input(0), - &Value::new(&to_bignum(collateral_input_value.clone())), - ); - - tx_builder.set_collateral(&inp); - - let res = tx_builder.set_collateral_return_and_total(&TransactionOutput::new( - &fake_base_address(1), - &fake_value2(1), - )); - - // Function call returns an error - assert!(res.is_err()); - - // NEITHER total collateral nor collateral return are changed in the builder - assert!(tx_builder.total_collateral.is_none()); - assert!(tx_builder.collateral_return.is_none()); - } - - #[test] - fn test_auto_calc_collateral_return() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(123456)); - - let mut inp = TxInputsBuilder::new(); - let collateral_input_value = 2_000_000; - inp.add_input( - &fake_base_address(0), - &fake_tx_input(0), - &fake_value2(collateral_input_value.clone()), - ); - - tx_builder.set_collateral(&inp); - - let total_collateral_value = 234_567; - let collateral_return_address = fake_base_address(1); - - tx_builder - .set_total_collateral_and_return( - &to_bignum(total_collateral_value.clone()), - &collateral_return_address, - ) - .unwrap(); - - assert!(tx_builder.total_collateral.is_some()); - assert_eq!( - tx_builder.total_collateral.unwrap(), - to_bignum(total_collateral_value.clone()), - ); - - assert!(tx_builder.collateral_return.is_some()); - let col_return: TransactionOutput = tx_builder.collateral_return.unwrap(); - assert_eq!(col_return.address, collateral_return_address); - assert_eq!( - col_return.amount, - Value::new(&to_bignum(collateral_input_value - total_collateral_value),) - ); - } - - #[test] - fn test_auto_calc_collateral_return_with_assets() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(123456)); - - let masset = fake_multiasset(123); - - let mut inp = TxInputsBuilder::new(); - let collateral_input_value = 2_000_000; - inp.add_input( - &fake_base_address(0), - &fake_tx_input(0), - &Value::new_with_assets(&to_bignum(collateral_input_value.clone()), &masset), - ); - - tx_builder.set_collateral(&inp); - - let total_collateral_value = 345_678; - let collateral_return_address = fake_base_address(1); - - tx_builder - .set_total_collateral_and_return( - &to_bignum(total_collateral_value.clone()), - &collateral_return_address, - ) - .unwrap(); - - assert!(tx_builder.total_collateral.is_some()); - assert_eq!( - tx_builder.total_collateral.unwrap(), - to_bignum(total_collateral_value.clone()), - ); - - assert!(tx_builder.collateral_return.is_some()); - let col_return: TransactionOutput = tx_builder.collateral_return.unwrap(); - assert_eq!(col_return.address, collateral_return_address); - assert_eq!( - col_return.amount, - Value::new_with_assets( - &to_bignum(collateral_input_value - total_collateral_value), - &masset, - ) - ); - } - - #[test] - fn test_add_collateral_return_succeed_with_border_amount() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(123456)); - - let masset = fake_multiasset(123); - - let mut inp = TxInputsBuilder::new(); - let collateral_input_value = 2_000_000; - inp.add_input( - &fake_base_address(0), - &fake_tx_input(0), - &Value::new_with_assets(&to_bignum(collateral_input_value.clone()), &masset), - ); - - tx_builder.set_collateral(&inp); - - let collateral_return_address = fake_base_address(1); - - let possible_ret = Value::new_from_assets(&masset); - let fake_out = TransactionOutput::new(&collateral_return_address, &possible_ret); - let min_ada = min_ada_for_output(&fake_out, &tx_builder.config.utxo_cost()).unwrap(); - - let total_collateral_value = to_bignum(collateral_input_value) - .checked_sub(&min_ada) - .unwrap(); - - tx_builder - .set_total_collateral_and_return(&total_collateral_value, &collateral_return_address) - .unwrap(); - - assert!(tx_builder.total_collateral.is_some()); - assert!(tx_builder.collateral_return.is_some()); - let col_return: TransactionOutput = tx_builder.collateral_return.unwrap(); - assert_eq!(col_return.address, collateral_return_address); - assert_eq!( - col_return.amount, - Value::new_with_assets(&min_ada, &masset,) - ); - } - - #[test] - fn test_add_zero_collateral_return() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(123456)); - - let mut inp = TxInputsBuilder::new(); - let collateral_input_value = 2_000_000; - inp.add_input( - &fake_base_address(0), - &fake_tx_input(0), - &Value::new(&to_bignum(collateral_input_value.clone())), - ); - - tx_builder.set_collateral(&inp); - - let collateral_return_address = fake_base_address(1); - - tx_builder - .set_total_collateral_and_return( - &to_bignum(collateral_input_value.clone()), - &collateral_return_address, - ) - .unwrap(); - - assert!(tx_builder.total_collateral.is_some()); - assert!(tx_builder.collateral_return.is_none()); - } - - #[test] - fn test_add_collateral_return_fails_no_enough_ada() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(123456)); - - let masset = fake_multiasset(123); - - let mut inp = TxInputsBuilder::new(); - let collateral_input_value = 2_000_000; - inp.add_input( - &fake_base_address(0), - &fake_tx_input(0), - &Value::new_with_assets(&to_bignum(collateral_input_value.clone()), &masset), - ); - - tx_builder.set_collateral(&inp); - - let collateral_return_address = fake_base_address(1); - - let possible_ret = Value::new_from_assets(&masset); - let fake_out = TransactionOutput::new(&collateral_return_address, &possible_ret); - let min_ada = min_ada_for_output(&fake_out, &tx_builder.config.utxo_cost()).unwrap(); - let mut total_collateral_value = to_bignum(collateral_input_value) - .checked_sub(&min_ada) - .unwrap(); - //make total collateral value bigger for make collateral return less then min ada - total_collateral_value = total_collateral_value.checked_add(&to_bignum(1)).unwrap(); - - let coll_add_res = tx_builder - .set_total_collateral_and_return(&total_collateral_value, &collateral_return_address); - - assert!(coll_add_res.is_err()); - assert!(tx_builder.total_collateral.is_none()); - assert!(tx_builder.collateral_return.is_none()); - } - - #[test] - fn test_auto_calc_collateral_return_fails_on_no_collateral() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(123456)); - - let res = tx_builder - .set_total_collateral_and_return(&to_bignum(345_678.clone()), &fake_base_address(1)); - - assert!(res.is_err()); - assert!(tx_builder.total_collateral.is_none()); - assert!(tx_builder.collateral_return.is_none()); - } - - #[test] - fn test_costmodel_retaining_for_v1() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(42)); - tx_builder.set_collateral(&create_collateral()); - - let (script1, _) = plutus_script_and_hash(0); - let datum = PlutusData::new_integer(&BigInt::from_str("42").unwrap()); - let redeemer = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &datum, - &ExUnits::new(&to_bignum(1700), &to_bignum(368100)), - ); - tx_builder.add_plutus_script_input( - &PlutusWitness::new(&script1, &datum, &redeemer), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - - // Setting script data hash removes the error - tx_builder - .calc_script_data_hash(&TxBuilderConstants::plutus_vasil_cost_models()) - .unwrap(); - - // Using SAFE `.build_tx` - let res2 = tx_builder.build_tx(); - assert!(res2.is_ok()); - - let v1 = Language::new_plutus_v1(); - let v1_costmodel = TxBuilderConstants::plutus_vasil_cost_models() - .get(&v1) - .unwrap(); - let mut retained_cost_models = Costmdls::new(); - retained_cost_models.insert(&v1, &v1_costmodel); - - let data_hash = hash_script_data( - &Redeemers::from(vec![redeemer.clone()]), - &retained_cost_models, - Some(PlutusList::from(vec![datum])), - ); - assert_eq!(tx_builder.script_data_hash.unwrap(), data_hash); - } - - #[test] - fn test_costmodel_retaining_fails_on_missing_costmodel() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(42)); - tx_builder.set_collateral(&create_collateral()); - - let (script1, _) = plutus_script_and_hash(0); - let datum = PlutusData::new_integer(&BigInt::from_str("42").unwrap()); - let redeemer = Redeemer::new( - &RedeemerTag::new_spend(), - &to_bignum(0), - &datum, - &ExUnits::new(&to_bignum(1700), &to_bignum(368100)), - ); - tx_builder.add_plutus_script_input( - &PlutusWitness::new(&script1, &datum, &redeemer), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(1_000_000)), - ); - - let v2 = Language::new_plutus_v2(); - let v2_costmodel = TxBuilderConstants::plutus_vasil_cost_models() - .get(&v2) - .unwrap(); - let mut retained_cost_models = Costmdls::new(); - retained_cost_models.insert(&v2, &v2_costmodel); - - // Setting script data hash removes the error - let calc_result = tx_builder.calc_script_data_hash(&retained_cost_models); - assert!(calc_result.is_err()); - } - - #[test] - fn coin_selection_random_improve_multi_asset() { - let utoxs = TransactionUnspentOutputs::from_json("[ { \"input\": { - \"transaction_id\": \"96631bf40bc2ae1e10b3c9157a4c711562c664b9744ed1f580b725e0589efcd0\", - \"index\": 1 -}, -\"output\": { - \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", - \"amount\": { - \"coin\": \"661308571\", - \"multiasset\": null - }, - \"plutus_data\": null, - \"script_ref\": null -}}, -{ \"input\": { - \"transaction_id\": \"89da149fa162eca7212493f2bcc8415ed070832e053ac0ec335d3501f901ad77\", - \"index\": 1 -}, -\"output\": { - \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", - \"amount\": { - \"coin\": \"555975153\", - \"multiasset\": null - }, - \"plutus_data\": null, - \"script_ref\": null -}}, -{ \"input\": { - \"transaction_id\": \"0124993c20ea0fe626d96a644773225202fb442238c38206242d26a1131e0a6e\", - \"index\": 1 -}, -\"output\": { - \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", - \"amount\": { - \"coin\": \"1899495\", - \"multiasset\": { - \"07e8df329b724e4be48ee32738125c06000de5448aaf93ed46d59e28\": { - \"44696e6f436f696e\": \"750\" - } - } - }, - \"plutus_data\": null, - \"script_ref\": null -}}, -{ \"input\": { - \"transaction_id\": \"c15c423d624b3af3f032c079a1b390c472b8ba889b48dd581d0ea28f96a36875\", - \"index\": 0 -}, -\"output\": { - \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", - \"amount\": { - \"coin\": \"1804315\", - \"multiasset\": { - \"07e8df329b724e4be48ee32738125c06000de5448aaf93ed46d59e28\": { - \"44696e6f436f696e\": \"2000\" - } - } - }, - \"plutus_data\": null, - \"script_ref\": null -}}, -{ \"input\": { - \"transaction_id\": \"5894bf9c9125859d29770bf43e4018f4f34a69edee49a7c9488c6707ab523c9b\", - \"index\": 1 -}, -\"output\": { - \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", - \"amount\": { - \"coin\": \"440573428\", - \"multiasset\": null - }, - \"plutus_data\": null, - \"script_ref\": null -}}, -{ \"input\": { - \"transaction_id\": \"168404afd4e9927d7775c8f40c0f749fc7634832d6931c5d51a507724cf44420\", - \"index\": 0 -}, -\"output\": { - \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", - \"amount\": { - \"coin\": \"1804315\", - \"multiasset\": { - \"07e8df329b724e4be48ee32738125c06000de5448aaf93ed46d59e28\": { - \"44696e6f436f696e\": \"1000\" - } - } - }, - \"plutus_data\": null, - \"script_ref\": null -}}, -{ \"input\": { - \"transaction_id\": \"3e6138498b721ee609a4c289768b2accad39cd4f00448540a95ba3362578a2f7\", - \"index\": 4 -}, -\"output\": { - \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", - \"amount\": { - \"coin\": \"1508500\", - \"multiasset\": { - \"07e8df329b724e4be48ee32738125c06000de5448aaf93ed46d59e28\": { - \"44696e6f436f696e\": \"750\" - } - } - }, - \"plutus_data\": null, - \"script_ref\": null -}}, -{ \"input\": { - \"transaction_id\": \"3e6138498b721ee609a4c289768b2accad39cd4f00448540a95ba3362578a2f7\", - \"index\": 5 -}, -\"output\": { - \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", - \"amount\": { - \"coin\": \"664935092\", - \"multiasset\": null - }, - \"plutus_data\": null, - \"script_ref\": null -}}, -{ \"input\": { - \"transaction_id\": \"046cf1bc21c23c59975714b520dd7ed22b63dab592cb0449e0ee6cc96eefde69\", - \"index\": 2 -}, -\"output\": { - \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", - \"amount\": { - \"coin\": \"7094915\", - \"multiasset\": null - }, - \"plutus_data\": null, - \"script_ref\": null -}}, -{ \"input\": { - \"transaction_id\": \"e16f195105db5f84621af4f7ea57c7156b8699cba94d4fdb72a6fb09e31db7a8\", - \"index\": 1 -}, -\"output\": { - \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", - \"amount\": { - \"coin\": \"78400000\", - \"multiasset\": null - }, - \"plutus_data\": null, - \"script_ref\": null -}}, -{ \"input\": { - \"transaction_id\": \"e16f195105db5f84621af4f7ea57c7156b8699cba94d4fdb72a6fb09e31db7a8\", - \"index\": 2 -}, -\"output\": { - \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", - \"amount\": { - \"coin\": \"2000000\", - \"multiasset\": null - }, - \"plutus_data\": null, - \"script_ref\": null -}}, -{ \"input\": { - \"transaction_id\": \"006697ef0c9285b7001ebe5a9e356fb50441e0af803773a99b7cbb0e9b728570\", - \"index\": 1 -}, -\"output\": { - \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", - \"amount\": { - \"coin\": \"15054830\", - \"multiasset\": { - \"07e8df329b724e4be48ee32738125c06000de5448aaf93ed46d59e28\": { - \"44696e6f436f696e\": \"56250\" - }, - \"3320679b145d683b9123f0626360699fcd7408b4d3ec3bd9cc79398c\": { - \"44696e6f436f696e\": \"287000\" - }, - \"57fca08abbaddee36da742a839f7d83a7e1d2419f1507fcbf3916522\": { - \"4d494e54\": \"91051638\", - \"534245525259\": \"27198732\" - }, - \"e61bfc106338ed4aeba93036324fbea8150fd9750fcffca1cd9f1a19\": { - \"44696e6f536176696f723030303639\": \"1\", - \"44696e6f536176696f723030303936\": \"1\", - \"44696e6f536176696f723030313737\": \"1\", - \"44696e6f536176696f723030333033\": \"1\", - \"44696e6f536176696f723030333531\": \"1\", - \"44696e6f536176696f723030333931\": \"1\", - \"44696e6f536176696f723030343336\": \"1\", - \"44696e6f536176696f723030343434\": \"1\", - \"44696e6f536176696f723030353232\": \"1\", - \"44696e6f536176696f723030353337\": \"1\", - \"44696e6f536176696f723030363334\": \"1\", - \"44696e6f536176696f723030373332\": \"1\", - \"44696e6f536176696f723030373430\": \"1\", - \"44696e6f536176696f723030373435\": \"1\", - \"44696e6f536176696f723031303139\": \"1\", - \"44696e6f536176696f723031303631\": \"1\", - \"44696e6f536176696f723031333432\": \"1\", - \"44696e6f536176696f723031333832\": \"1\", - \"44696e6f536176696f723031353333\": \"1\", - \"44696e6f536176696f723031353732\": \"1\", - \"44696e6f536176696f723031363337\": \"1\", - \"44696e6f536176696f723031363430\": \"1\", - \"44696e6f536176696f723031373631\": \"1\", - \"44696e6f536176696f723031393436\": \"1\", - \"44696e6f536176696f723032313237\": \"1\", - \"44696e6f536176696f723032323232\": \"1\", - \"44696e6f536176696f723032333230\": \"1\", - \"44696e6f536176696f723032333239\": \"1\", - \"44696e6f536176696f723032333534\": \"1\", - \"44696e6f536176696f723032333631\": \"1\", - \"44696e6f536176696f723032333935\": \"1\", - \"44696e6f536176696f723032333938\": \"1\", - \"44696e6f536176696f723032343037\": \"1\", - \"44696e6f536176696f723032343434\": \"1\", - \"44696e6f536176696f723032353039\": \"1\", - \"44696e6f536176696f723032363334\": \"1\", - \"44696e6f536176696f723032363430\": \"1\", - \"44696e6f536176696f723032373537\": \"1\", - \"44696e6f536176696f723032373832\": \"1\", - \"44696e6f536176696f723032383933\": \"1\", - \"44696e6f536176696f723033323430\": \"1\", - \"44696e6f536176696f723033343937\": \"1\", - \"44696e6f536176696f723033353437\": \"1\", - \"44696e6f536176696f723033353738\": \"1\", - \"44696e6f536176696f723033363638\": \"1\", - \"44696e6f536176696f723033363836\": \"1\", - \"44696e6f536176696f723033363930\": \"1\", - \"44696e6f536176696f723033383638\": \"1\", - \"44696e6f536176696f723033383731\": \"1\", - \"44696e6f536176696f723033383931\": \"1\", - \"44696e6f536176696f723034313936\": \"1\", - \"44696e6f536176696f723034323538\": \"1\", - \"44696e6f536176696f723034323733\": \"1\", - \"44696e6f536176696f723034363235\": \"1\", - \"44696e6f536176696f723034373132\": \"1\", - \"44696e6f536176696f723034373932\": \"1\", - \"44696e6f536176696f723034383831\": \"1\", - \"44696e6f536176696f723034393936\": \"1\", - \"44696e6f536176696f723035303432\": \"1\", - \"44696e6f536176696f723035313539\": \"1\", - \"44696e6f536176696f723035333138\": \"1\", - \"44696e6f536176696f723035333532\": \"1\", - \"44696e6f536176696f723035343433\": \"1\", - \"44696e6f536176696f723035343639\": \"1\", - \"44696e6f536176696f723035373434\": \"1\", - \"44696e6f536176696f723035373638\": \"1\", - \"44696e6f536176696f723035373830\": \"1\", - \"44696e6f536176696f723035383435\": \"1\", - \"44696e6f536176696f723035383538\": \"1\", - \"44696e6f536176696f723035393632\": \"1\", - \"44696e6f536176696f723036303032\": \"1\", - \"44696e6f536176696f723036303337\": \"1\", - \"44696e6f536176696f723036303738\": \"1\", - \"44696e6f536176696f723036323033\": \"1\", - \"44696e6f536176696f723036323036\": \"1\", - \"44696e6f536176696f723036323236\": \"1\", - \"44696e6f536176696f723036333130\": \"1\", - \"44696e6f536176696f723036333935\": \"1\", - \"44696e6f536176696f723036343932\": \"1\", - \"44696e6f536176696f723036353532\": \"1\", - \"44696e6f536176696f723036363735\": \"1\", - \"44696e6f536176696f723036363839\": \"1\", - \"44696e6f536176696f723036373233\": \"1\", - \"44696e6f536176696f723036383731\": \"1\", - \"44696e6f536176696f723036383830\": \"1\", - \"44696e6f536176696f723036393137\": \"1\", - \"44696e6f536176696f723037303339\": \"1\", - \"44696e6f536176696f723037323638\": \"1\", - \"44696e6f536176696f723037333434\": \"1\", - \"44696e6f536176696f723037343232\": \"1\", - \"44696e6f536176696f723037343731\": \"1\", - \"44696e6f536176696f723037353431\": \"1\", - \"44696e6f536176696f723037363032\": \"1\", - \"44696e6f536176696f723037363136\": \"1\", - \"44696e6f536176696f723037363430\": \"1\", - \"44696e6f536176696f723037373635\": \"1\", - \"44696e6f536176696f723037373732\": \"1\", - \"44696e6f536176696f723037393039\": \"1\", - \"44696e6f536176696f723037393234\": \"1\", - \"44696e6f536176696f723037393430\": \"1\", - \"44696e6f536176696f723037393632\": \"1\", - \"44696e6f536176696f723038303130\": \"1\", - \"44696e6f536176696f723038303338\": \"1\", - \"44696e6f536176696f723038303339\": \"1\", - \"44696e6f536176696f723038303636\": \"1\", - \"44696e6f536176696f723038313735\": \"1\", - \"44696e6f536176696f723038323032\": \"1\", - \"44696e6f536176696f723038323131\": \"1\", - \"44696e6f536176696f723038323536\": \"1\", - \"44696e6f536176696f723038333532\": \"1\", - \"44696e6f536176696f723038333536\": \"1\", - \"44696e6f536176696f723038333538\": \"1\", - \"44696e6f536176696f723038333539\": \"1\", - \"44696e6f536176696f723038333830\": \"1\", - \"44696e6f536176696f723038343932\": \"1\", - \"44696e6f536176696f723038353231\": \"1\", - \"44696e6f536176696f723038353736\": \"1\", - \"44696e6f536176696f723038353836\": \"1\", - \"44696e6f536176696f723038363130\": \"1\", - \"44696e6f536176696f723039303231\": \"1\", - \"44696e6f536176696f723039303735\": \"1\", - \"44696e6f536176696f723039313039\": \"1\", - \"44696e6f536176696f723039313231\": \"1\", - \"44696e6f536176696f723039323238\": \"1\", - \"44696e6f536176696f723039333138\": \"1\", - \"44696e6f536176696f723039333731\": \"1\", - \"44696e6f536176696f723039343035\": \"1\", - \"44696e6f536176696f723039343136\": \"1\", - \"44696e6f536176696f723039353039\": \"1\", - \"44696e6f536176696f723039353635\": \"1\", - \"44696e6f536176696f723039363331\": \"1\", - \"44696e6f536176696f723039363932\": \"1\", - \"44696e6f536176696f723039383839\": \"1\", - \"44696e6f536176696f723039393038\": \"1\", - \"44696e6f536176696f723039393935\": \"1\" - }, - \"ee8e37676f6ebb8e031dff493f88ff711d24aa68666a09d61f1d3fb3\": { - \"43727970746f44696e6f3030303135\": \"1\", - \"43727970746f44696e6f3030313335\": \"1\", - \"43727970746f44696e6f3030323634\": \"1\", - \"43727970746f44696e6f3030333932\": \"1\", - \"43727970746f44696e6f3030353834\": \"1\", - \"43727970746f44696e6f3030373136\": \"1\", - \"43727970746f44696e6f3030373837\": \"1\", - \"43727970746f44696e6f3030383438\": \"1\", - \"43727970746f44696e6f3031303537\": \"1\", - \"43727970746f44696e6f3031313134\": \"1\", - \"43727970746f44696e6f3031323237\": \"1\", - \"43727970746f44696e6f3031323330\": \"1\", - \"43727970746f44696e6f3031343031\": \"1\", - \"43727970746f44696e6f3031353138\": \"1\", - \"43727970746f44696e6f3031353734\": \"1\", - \"43727970746f44696e6f3031373635\": \"1\", - \"43727970746f44696e6f3031383037\": \"1\", - \"43727970746f44696e6f3031383231\": \"1\", - \"43727970746f44696e6f3032303830\": \"1\", - \"43727970746f44696e6f3032313133\": \"1\", - \"43727970746f44696e6f3032323835\": \"1\", - \"43727970746f44696e6f3032343238\": \"1\", - \"43727970746f44696e6f3032363738\": \"1\", - \"43727970746f44696e6f3032393034\": \"1\", - \"43727970746f44696e6f3032393333\": \"1\", - \"43727970746f44696e6f3032393537\": \"1\", - \"43727970746f44696e6f3032393632\": \"1\", - \"43727970746f44696e6f3032393735\": \"1\", - \"43727970746f44696e6f3033303434\": \"1\", - \"43727970746f44696e6f3033333338\": \"1\", - \"43727970746f44696e6f3033393535\": \"1\", - \"43727970746f44696e6f3034303630\": \"1\", - \"43727970746f44696e6f3034313939\": \"1\", - \"43727970746f44696e6f3034373439\": \"1\", - \"43727970746f44696e6f3034383134\": \"1\", - \"43727970746f44696e6f3034393530\": \"1\", - \"43727970746f44696e6f3035303630\": \"1\", - \"43727970746f44696e6f3035333230\": \"1\", - \"43727970746f44696e6f2d312d3030303030\": \"1\", - \"43727970746f44696e6f2d312d3030303032\": \"1\" - } - } - }, - \"plutus_data\": null, - \"script_ref\": null -}}, -{ \"input\": { - \"transaction_id\": \"006697ef0c9285b7001ebe5a9e356fb50441e0af803773a99b7cbb0e9b728570\", - \"index\": 2 -}, -\"output\": { - \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", - \"amount\": { - \"coin\": \"2279450\", - \"multiasset\": null - }, - \"plutus_data\": null, - \"script_ref\": null -}}, -{ \"input\": { - \"transaction_id\": \"017962634cf8fa87835256a80b8374c6f75687c34d8694480cb071648551c3a7\", - \"index\": 0 -}, -\"output\": { - \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", - \"amount\": { - \"coin\": \"2000000\", - \"multiasset\": { - \"ee8e37676f6ebb8e031dff493f88ff711d24aa68666a09d61f1d3fb3\": { - \"43727970746f44696e6f3031353039\": \"1\" - } - } - }, - \"plutus_data\": null, - \"script_ref\": null -}}, -{ \"input\": { - \"transaction_id\": \"017962634cf8fa87835256a80b8374c6f75687c34d8694480cb071648551c3a7\", - \"index\": 1 -}, -\"output\": { - \"address\": \"addr_test1qp03v9yeg0vcfdhyn65ets2juearxpc3pmdhr0sxs0w6wh3sjf67h3yhrpxpv00zqfc7rtmr6mnmrcplfdkw5zhnl49qmyf0q5\", - \"amount\": { - \"coin\": \"725669617\", - \"multiasset\": null - }, - \"plutus_data\": null, - \"script_ref\": null -}}]") - .unwrap(); - let output = TransactionOutput::from_json("{ - \"address\": \"addr_test1wpv93hm9sqx0ar7pgxwl9jn3xt6lwmxxy27zd932slzvghqg8fe0n\", - \"amount\": { - \"coin\": \"20000000\", - \"multiasset\": { - \"07e8df329b724e4be48ee32738125c06000de5448aaf93ed46d59e28\": { - \"44696e6f436f696e\": \"1000\" - }, - \"ee8e37676f6ebb8e031dff493f88ff711d24aa68666a09d61f1d3fb3\": { - \"43727970746f44696e6f2d312d3030303030\": \"1\", - \"43727970746f44696e6f2d312d3030303032\": \"1\" - } - } - }, - \"plutus_data\": { - \"DataHash\": \"979f68de9e070e75779f80ce5e6cc74f8d77661d65f2895c01d0a6f66eceb791\" - }, - \"script_ref\": null -}").unwrap(); - let mut builder = create_reallistic_tx_builder(); - builder.add_output(&output).unwrap(); - let res = builder.add_inputs_from(&utoxs, CoinSelectionStrategyCIP2::RandomImproveMultiAsset); - assert!(res.is_ok()); - } - - #[test] - fn plutus_mint_test() { - let mut tx_builder = create_reallistic_tx_builder(); - let colateral_adress = Address::from_bech32("addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w").unwrap(); - let colateral_input = TransactionInput::from_json("\ - { - \"transaction_id\": \"69b0b867056a2d4fdc3827e23aa7069b125935e2def774941ca8cc7f9e0de774\", - \"index\": 1 - }").unwrap(); - - let tx_input = TransactionInput::from_json("\ - { - \"transaction_id\": \"f58a5bc761b1efdcf4b5684f6ad5495854a0d64b866e2f0f525d134750d3511b\", - \"index\": 1 - }").unwrap(); - let plutus_script = plutus::PlutusScript::from_hex("5907d2010000332323232323232323232323232323322323232323222232325335332201b3333573466e1cd55ce9baa0044800080608c98c8060cd5ce00c80c00b1999ab9a3370e6aae7540092000233221233001003002323232323232323232323232323333573466e1cd55cea8062400046666666666664444444444442466666666666600201a01801601401201000e00c00a00800600466a02a02c6ae854030cd4054058d5d0a80599a80a80b9aba1500a3335501975ca0306ae854024ccd54065d7280c1aba1500833501502035742a00e666aa032042eb4d5d0a8031919191999ab9a3370e6aae75400920002332212330010030023232323333573466e1cd55cea8012400046644246600200600466a056eb4d5d0a80118161aba135744a004464c6405c66ae700bc0b80b04d55cf280089baa00135742a0046464646666ae68cdc39aab9d5002480008cc8848cc00400c008cd40add69aba15002302c357426ae8940088c98c80b8cd5ce01781701609aab9e5001137540026ae84d5d1280111931901519ab9c02b02a028135573ca00226ea8004d5d0a80299a80abae35742a008666aa03203a40026ae85400cccd54065d710009aba15002301f357426ae8940088c98c8098cd5ce01381301209aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba15002300f357426ae8940088c98c8060cd5ce00c80c00b080b89931900b99ab9c4910350543500017135573ca00226ea800448c88c008dd6000990009aa80a911999aab9f0012500a233500930043574200460066ae880080508c8c8cccd5cd19b8735573aa004900011991091980080180118061aba150023005357426ae8940088c98c8050cd5ce00a80a00909aab9e5001137540024646464646666ae68cdc39aab9d5004480008cccc888848cccc00401401000c008c8c8c8cccd5cd19b8735573aa0049000119910919800801801180a9aba1500233500f014357426ae8940088c98c8064cd5ce00d00c80b89aab9e5001137540026ae854010ccd54021d728039aba150033232323333573466e1d4005200423212223002004357426aae79400c8cccd5cd19b875002480088c84888c004010dd71aba135573ca00846666ae68cdc3a801a400042444006464c6403666ae7007006c06406005c4d55cea80089baa00135742a00466a016eb8d5d09aba2500223263201533573802c02a02626ae8940044d5d1280089aab9e500113754002266aa002eb9d6889119118011bab00132001355012223233335573e0044a010466a00e66442466002006004600c6aae754008c014d55cf280118021aba200301213574200222440042442446600200800624464646666ae68cdc3a800a40004642446004006600a6ae84d55cf280191999ab9a3370ea0049001109100091931900819ab9c01101000e00d135573aa00226ea80048c8c8cccd5cd19b875001480188c848888c010014c01cd5d09aab9e500323333573466e1d400920042321222230020053009357426aae7940108cccd5cd19b875003480088c848888c004014c01cd5d09aab9e500523333573466e1d40112000232122223003005375c6ae84d55cf280311931900819ab9c01101000e00d00c00b135573aa00226ea80048c8c8cccd5cd19b8735573aa004900011991091980080180118029aba15002375a6ae84d5d1280111931900619ab9c00d00c00a135573ca00226ea80048c8cccd5cd19b8735573aa002900011bae357426aae7940088c98c8028cd5ce00580500409baa001232323232323333573466e1d4005200c21222222200323333573466e1d4009200a21222222200423333573466e1d400d2008233221222222233001009008375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c4664424444444660040120106eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc8848888888cc018024020c030d5d0a8049bae357426ae8940248cccd5cd19b875006480088c848888888c01c020c034d5d09aab9e500b23333573466e1d401d2000232122222223005008300e357426aae7940308c98c804ccd5ce00a00980880800780700680600589aab9d5004135573ca00626aae7940084d55cf280089baa0012323232323333573466e1d400520022333222122333001005004003375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea0049000119091180100198041aba135573ca00c464c6401866ae700340300280244d55cea80189aba25001135573ca00226ea80048c8c8cccd5cd19b875001480088c8488c00400cdd71aba135573ca00646666ae68cdc3a8012400046424460040066eb8d5d09aab9e500423263200933573801401200e00c26aae7540044dd500089119191999ab9a3370ea00290021091100091999ab9a3370ea00490011190911180180218031aba135573ca00846666ae68cdc3a801a400042444004464c6401466ae7002c02802001c0184d55cea80089baa0012323333573466e1d40052002200723333573466e1d40092000212200123263200633573800e00c00800626aae74dd5000a4c24002920103505431001220021123230010012233003300200200133351222335122335004335500248811c2b194b7d10a3d2d3152c5f3a628ff50cb9fc11e59453e8ac7a1aea4500488104544e4654005005112212330010030021120011122002122122330010040031200101").unwrap(); - let redeemer = Redeemer::from_json("\ - { - \"tag\": \"Mint\", - \"index\": \"0\", - \"data\": \"{\\\"constructor\\\":0,\\\"fields\\\":[]}\", - \"ex_units\": { - \"mem\": \"1042996\", - \"steps\": \"446100241\" - } - }").unwrap(); - let asset_name = AssetName::from_hex("44544e4654").unwrap(); - let mut mint_builder = MintBuilder::new(); - let plutus_script_source = PlutusScriptSource::new(&plutus_script); - let mint_witnes = MintWitness::new_plutus_script(&plutus_script_source, &redeemer); - mint_builder.add_asset(&mint_witnes, &asset_name, &Int::new(&BigNum::from(100u64))); - - let output_adress = Address::from_bech32("addr_test1qpm5njmgzf4t7225v6j34wl30xfrufzt3jtqtdzf3en9ahpmnhtmynpasyc8fq75zv0uaj86vzsr7g3g8q5ypgu5fwtqr9zsgj").unwrap(); - let mut output_assets = MultiAsset::new(); - let mut asset = Assets::new(); - asset.insert(&asset_name, &BigNum::from(100u64)); - output_assets.insert(&plutus_script.hash(), &asset); - let output_value = Value::new_with_assets(&Coin::from(50000u64), &output_assets); - let output = TransactionOutput::new(&output_adress, &output_value); - - let mut col_builder = TxInputsBuilder::new(); - col_builder.add_input(&colateral_adress, &colateral_input, &Value::new(&Coin::from(1000000000u64))); - tx_builder.set_collateral(&col_builder); - tx_builder.add_output(&output); - tx_builder.add_input(&output_adress, &tx_input, &Value::new(&BigNum::from(100000000000u64))); - tx_builder.set_mint_builder(&mint_builder); - - tx_builder.calc_script_data_hash(&TxBuilderConstants::plutus_vasil_cost_models()).unwrap(); - - let change_res = tx_builder.add_change_if_needed(&output_adress); - assert!(change_res.is_ok()); - - let build_res = tx_builder.build_tx(); - assert!(build_res.is_ok()); - - assert!(mint_builder.get_plutus_witnesses().len() == 1); - - let tx = build_res.unwrap(); - assert!(tx.body.mint.is_some()); - assert_eq!(tx.body.mint.unwrap().0.iter().next().unwrap().0, plutus_script.hash()); - } - - #[test] - fn plutus_mint_with_script_ref_test() { - let mut tx_builder = create_reallistic_tx_builder(); - let colateral_adress = Address::from_bech32("addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w").unwrap(); - let colateral_input = TransactionInput::from_json("\ - { - \"transaction_id\": \"69b0b867056a2d4fdc3827e23aa7069b125935e2def774941ca8cc7f9e0de774\", - \"index\": 1 - }").unwrap(); - - let tx_input = TransactionInput::from_json("\ - { - \"transaction_id\": \"f58a5bc761b1efdcf4b5684f6ad5495854a0d64b866e2f0f525d134750d3511b\", - \"index\": 1 - }").unwrap(); - let tx_input_ref = TransactionInput::from_json("\ - { - \"transaction_id\": \"f58a5bc7adaadadcf4b5684f6ad5495854a0d64b866e2f0f525d134750d3511b\", - \"index\": 2 - }").unwrap(); - let plutus_script = plutus::PlutusScript::from_hex("5907d2010000332323232323232323232323232323322323232323222232325335332201b3333573466e1cd55ce9baa0044800080608c98c8060cd5ce00c80c00b1999ab9a3370e6aae7540092000233221233001003002323232323232323232323232323333573466e1cd55cea8062400046666666666664444444444442466666666666600201a01801601401201000e00c00a00800600466a02a02c6ae854030cd4054058d5d0a80599a80a80b9aba1500a3335501975ca0306ae854024ccd54065d7280c1aba1500833501502035742a00e666aa032042eb4d5d0a8031919191999ab9a3370e6aae75400920002332212330010030023232323333573466e1cd55cea8012400046644246600200600466a056eb4d5d0a80118161aba135744a004464c6405c66ae700bc0b80b04d55cf280089baa00135742a0046464646666ae68cdc39aab9d5002480008cc8848cc00400c008cd40add69aba15002302c357426ae8940088c98c80b8cd5ce01781701609aab9e5001137540026ae84d5d1280111931901519ab9c02b02a028135573ca00226ea8004d5d0a80299a80abae35742a008666aa03203a40026ae85400cccd54065d710009aba15002301f357426ae8940088c98c8098cd5ce01381301209aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba15002300f357426ae8940088c98c8060cd5ce00c80c00b080b89931900b99ab9c4910350543500017135573ca00226ea800448c88c008dd6000990009aa80a911999aab9f0012500a233500930043574200460066ae880080508c8c8cccd5cd19b8735573aa004900011991091980080180118061aba150023005357426ae8940088c98c8050cd5ce00a80a00909aab9e5001137540024646464646666ae68cdc39aab9d5004480008cccc888848cccc00401401000c008c8c8c8cccd5cd19b8735573aa0049000119910919800801801180a9aba1500233500f014357426ae8940088c98c8064cd5ce00d00c80b89aab9e5001137540026ae854010ccd54021d728039aba150033232323333573466e1d4005200423212223002004357426aae79400c8cccd5cd19b875002480088c84888c004010dd71aba135573ca00846666ae68cdc3a801a400042444006464c6403666ae7007006c06406005c4d55cea80089baa00135742a00466a016eb8d5d09aba2500223263201533573802c02a02626ae8940044d5d1280089aab9e500113754002266aa002eb9d6889119118011bab00132001355012223233335573e0044a010466a00e66442466002006004600c6aae754008c014d55cf280118021aba200301213574200222440042442446600200800624464646666ae68cdc3a800a40004642446004006600a6ae84d55cf280191999ab9a3370ea0049001109100091931900819ab9c01101000e00d135573aa00226ea80048c8c8cccd5cd19b875001480188c848888c010014c01cd5d09aab9e500323333573466e1d400920042321222230020053009357426aae7940108cccd5cd19b875003480088c848888c004014c01cd5d09aab9e500523333573466e1d40112000232122223003005375c6ae84d55cf280311931900819ab9c01101000e00d00c00b135573aa00226ea80048c8c8cccd5cd19b8735573aa004900011991091980080180118029aba15002375a6ae84d5d1280111931900619ab9c00d00c00a135573ca00226ea80048c8cccd5cd19b8735573aa002900011bae357426aae7940088c98c8028cd5ce00580500409baa001232323232323333573466e1d4005200c21222222200323333573466e1d4009200a21222222200423333573466e1d400d2008233221222222233001009008375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c4664424444444660040120106eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc8848888888cc018024020c030d5d0a8049bae357426ae8940248cccd5cd19b875006480088c848888888c01c020c034d5d09aab9e500b23333573466e1d401d2000232122222223005008300e357426aae7940308c98c804ccd5ce00a00980880800780700680600589aab9d5004135573ca00626aae7940084d55cf280089baa0012323232323333573466e1d400520022333222122333001005004003375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea0049000119091180100198041aba135573ca00c464c6401866ae700340300280244d55cea80189aba25001135573ca00226ea80048c8c8cccd5cd19b875001480088c8488c00400cdd71aba135573ca00646666ae68cdc3a8012400046424460040066eb8d5d09aab9e500423263200933573801401200e00c26aae7540044dd500089119191999ab9a3370ea00290021091100091999ab9a3370ea00490011190911180180218031aba135573ca00846666ae68cdc3a801a400042444004464c6401466ae7002c02802001c0184d55cea80089baa0012323333573466e1d40052002200723333573466e1d40092000212200123263200633573800e00c00800626aae74dd5000a4c24002920103505431001220021123230010012233003300200200133351222335122335004335500248811c2b194b7d10a3d2d3152c5f3a628ff50cb9fc11e59453e8ac7a1aea4500488104544e4654005005112212330010030021120011122002122122330010040031200101").unwrap(); - let plutus_script2 = plutus::PlutusScript::from_hex("5907adaada00332323232323232323232323232323322323232323222232325335332201b3333573466e1cd55ce9baa0044800080608c98c8060cd5ce00c80c00b1999ab9a3370e6aae7540092000233221233001003002323232323232323232323232323333573466e1cd55cea8062400046666666666664444444444442466666666666600201a01801601401201000e00c00a00800600466a02a02c6ae854030cd4054058d5d0a80599a80a80b9aba1500a3335501975ca0306ae854024ccd54065d7280c1aba1500833501502035742a00e666aa032042eb4d5d0a8031919191999ab9a3370e6aae75400920002332212330010030023232323333573466e1cd55cea8012400046644246600200600466a056eb4d5d0a80118161aba135744a004464c6405c66ae700bc0b80b04d55cf280089baa00135742a0046464646666ae68cdc39aab9d5002480008cc8848cc00400c008cd40add69aba15002302c357426ae8940088c98c80b8cd5ce01781701609aab9e5001137540026ae84d5d1280111931901519ab9c02b02a028135573ca00226ea8004d5d0a80299a80abae35742a008666aa03203a40026ae85400cccd54065d710009aba15002301f357426ae8940088c98c8098cd5ce01381301209aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba15002300f357426ae8940088c98c8060cd5ce00c80c00b080b89931900b99ab9c4910350543500017135573ca00226ea800448c88c008dd6000990009aa80a911999aab9f0012500a233500930043574200460066ae880080508c8c8cccd5cd19b8735573aa004900011991091980080180118061aba150023005357426ae8940088c98c8050cd5ce00a80a00909aab9e5001137540024646464646666ae68cdc39aab9d5004480008cccc888848cccc00401401000c008c8c8c8cccd5cd19b8735573aa0049000119910919800801801180a9aba1500233500f014357426ae8940088c98c8064cd5ce00d00c80b89aab9e5001137540026ae854010ccd54021d728039aba150033232323333573466e1d4005200423212223002004357426aae79400c8cccd5cd19b875002480088c84888c004010dd71aba135573ca00846666ae68cdc3a801a400042444006464c6403666ae7007006c06406005c4d55cea80089baa00135742a00466a016eb8d5d09aba2500223263201533573802c02a02626ae8940044d5d1280089aab9e500113754002266aa002eb9d6889119118011bab00132001355012223233335573e0044a010466a00e66442466002006004600c6aae754008c014d55cf280118021aba200301213574200222440042442446600200800624464646666ae68cdc3a800a40004642446004006600a6ae84d55cf280191999ab9a3370ea0049001109100091931900819ab9c01101000e00d135573aa00226ea80048c8c8cccd5cd19b875001480188c848888c010014c01cd5d09aab9e500323333573466e1d400920042321222230020053009357426aae7940108cccd5cd19b875003480088c848888c004014c01cd5d09aab9e500523333573466e1d40112000232122223003005375c6ae84d55cf280311931900819ab9c01101000e00d00c00b135573aa00226ea80048c8c8cccd5cd19b8735573aa004900011991091980080180118029aba15002375a6ae84d5d1280111931900619ab9c00d00c00a135573ca00226ea80048c8cccd5cd19b8735573aa002900011bae357426aae7940088c98c8028cd5ce00580500409baa001232323232323333573466e1d4005200c21222222200323333573466e1d4009200a21222222200423333573466e1d400d2008233221222222233001009008375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c4664424444444660040120106eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc8848888888cc018024020c030d5d0a8049bae357426ae8940248cccd5cd19b875006480088c848888888c01c020c034d5d09aab9e500b23333573466e1d401d2000232122222223005008300e357426aae7940308c98c804ccd5ce00a00980880800780700680600589aab9d5004135573ca00626aae7940084d55cf280089baa0012323232323333573466e1d400520022333222122333001005004003375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea0049000119091180100198041aba135573ca00c464c6401866ae700340300280244d55cea80189aba25001135573ca00226ea80048c8c8cccd5cd19b875001480088c8488c00400cdd71aba135573ca00646666ae68cdc3a8012400046424460040066eb8d5d09aab9e500423263200933573801401200e00c26aae7540044dd500089119191999ab9a3370ea00290021091100091999ab9a3370ea00490011190911180180218031aba135573ca00846666ae68cdc3a801a400042444004464c6401466ae7002c02802001c0184d55cea80089baa0012323333573466e1d40052002200723333573466e1d40092000212200123263200633573800e00c00800626aae74dd5000a4c24002920103505431001220021123230010012233003300200200133351222335122335004335500248811c2b194b7d10a3d2d3152c5f3a628ff50cb9fc11e59453e8ac7a1aea4500488104544e4654005005112212330010030021120011122002122122330010040031200101").unwrap(); - - let redeemer = Redeemer::from_json("\ - { - \"tag\": \"Mint\", - \"index\": \"0\", - \"data\": \"{\\\"constructor\\\":0,\\\"fields\\\":[]}\", - \"ex_units\": { - \"mem\": \"1042996\", - \"steps\": \"446100241\" - } - }").unwrap(); - - let redeemer2 = Redeemer::from_json("\ - { - \"tag\": \"Mint\", - \"index\": \"0\", - \"data\": \"{\\\"constructor\\\":0,\\\"fields\\\":[]}\", - \"ex_units\": { - \"mem\": \"2929292\", - \"steps\": \"446188888\" - } - }").unwrap(); - - let asset_name = AssetName::from_hex("44544e4654").unwrap(); - let asset_name2 = AssetName::from_hex("44544e4ada").unwrap(); - let mut mint_builder = MintBuilder::new(); - let plutus_script_source = PlutusScriptSource::new(&plutus_script); - let plutus_script_source_ref = PlutusScriptSource::new_ref_input_with_lang_ver(&plutus_script2.hash(), &tx_input_ref, &Language::new_plutus_v2()); - let mint_witnes = MintWitness::new_plutus_script(&plutus_script_source, &redeemer); - let mint_witnes_ref = MintWitness::new_plutus_script(&plutus_script_source_ref, &redeemer2); - mint_builder.add_asset(&mint_witnes, &asset_name, &Int::new(&BigNum::from(100u64))); - mint_builder.add_asset(&mint_witnes_ref, &asset_name, &Int::new(&BigNum::from(100u64))); - - let output_adress = Address::from_bech32("addr_test1qpm5njmgzf4t7225v6j34wl30xfrufzt3jtqtdzf3en9ahpmnhtmynpasyc8fq75zv0uaj86vzsr7g3g8q5ypgu5fwtqr9zsgj").unwrap(); - let mut output_assets = MultiAsset::new(); - let mut asset = Assets::new(); - asset.insert(&asset_name, &BigNum::from(100u64)); - output_assets.insert(&plutus_script.hash(), &asset); - let output_value = Value::new_with_assets(&Coin::from(50000u64), &output_assets); - let output = TransactionOutput::new(&output_adress, &output_value); - - let mut col_builder = TxInputsBuilder::new(); - col_builder.add_input(&colateral_adress, &colateral_input, &Value::new(&Coin::from(1000000000u64))); - tx_builder.set_collateral(&col_builder); - tx_builder.add_output(&output); - tx_builder.add_input(&output_adress, &tx_input, &Value::new(&BigNum::from(100000000000u64))); - tx_builder.set_mint_builder(&mint_builder); - - tx_builder.calc_script_data_hash(&TxBuilderConstants::plutus_vasil_cost_models()).unwrap(); - - let change_res = tx_builder.add_change_if_needed(&output_adress); - assert!(change_res.is_ok()); - - let build_res = tx_builder.build_tx(); - assert!(build_res.is_ok()); - - let tx = build_res.unwrap(); - assert_eq!(tx.witness_set.plutus_scripts.unwrap().len(), 1usize); - assert_eq!(tx.witness_set.redeemers.unwrap().len(), 2usize); - assert!(tx.witness_set.plutus_data.is_none()); - assert_eq!(tx.body.reference_inputs.unwrap().len(), 1usize); - assert!(tx.body.mint.is_some()); - assert_eq!(tx.body.mint.unwrap().len(), 2usize); - } - - #[test] - fn plutus_mint_defferent_redeemers_test() { - let mut tx_builder = create_reallistic_tx_builder(); - let colateral_adress = Address::from_bech32("addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w").unwrap(); - let colateral_input = TransactionInput::from_json("\ - { - \"transaction_id\": \"69b0b867056a2d4fdc3827e23aa7069b125935e2def774941ca8cc7f9e0de774\", - \"index\": 1 - }").unwrap(); - - let tx_input = TransactionInput::from_json("\ - { - \"transaction_id\": \"f58a5bc761b1efdcf4b5684f6ad5495854a0d64b866e2f0f525d134750d3511b\", - \"index\": 1 - }").unwrap(); - let tx_input_ref = TransactionInput::from_json("\ - { - \"transaction_id\": \"f58a5bc7adaadadcf4b5684f6ad5495854a0d64b866e2f0f525d134750d3511b\", - \"index\": 2 - }").unwrap(); - let plutus_script = plutus::PlutusScript::from_hex("5907d2010000332323232323232323232323232323322323232323222232325335332201b3333573466e1cd55ce9baa0044800080608c98c8060cd5ce00c80c00b1999ab9a3370e6aae7540092000233221233001003002323232323232323232323232323333573466e1cd55cea8062400046666666666664444444444442466666666666600201a01801601401201000e00c00a00800600466a02a02c6ae854030cd4054058d5d0a80599a80a80b9aba1500a3335501975ca0306ae854024ccd54065d7280c1aba1500833501502035742a00e666aa032042eb4d5d0a8031919191999ab9a3370e6aae75400920002332212330010030023232323333573466e1cd55cea8012400046644246600200600466a056eb4d5d0a80118161aba135744a004464c6405c66ae700bc0b80b04d55cf280089baa00135742a0046464646666ae68cdc39aab9d5002480008cc8848cc00400c008cd40add69aba15002302c357426ae8940088c98c80b8cd5ce01781701609aab9e5001137540026ae84d5d1280111931901519ab9c02b02a028135573ca00226ea8004d5d0a80299a80abae35742a008666aa03203a40026ae85400cccd54065d710009aba15002301f357426ae8940088c98c8098cd5ce01381301209aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba15002300f357426ae8940088c98c8060cd5ce00c80c00b080b89931900b99ab9c4910350543500017135573ca00226ea800448c88c008dd6000990009aa80a911999aab9f0012500a233500930043574200460066ae880080508c8c8cccd5cd19b8735573aa004900011991091980080180118061aba150023005357426ae8940088c98c8050cd5ce00a80a00909aab9e5001137540024646464646666ae68cdc39aab9d5004480008cccc888848cccc00401401000c008c8c8c8cccd5cd19b8735573aa0049000119910919800801801180a9aba1500233500f014357426ae8940088c98c8064cd5ce00d00c80b89aab9e5001137540026ae854010ccd54021d728039aba150033232323333573466e1d4005200423212223002004357426aae79400c8cccd5cd19b875002480088c84888c004010dd71aba135573ca00846666ae68cdc3a801a400042444006464c6403666ae7007006c06406005c4d55cea80089baa00135742a00466a016eb8d5d09aba2500223263201533573802c02a02626ae8940044d5d1280089aab9e500113754002266aa002eb9d6889119118011bab00132001355012223233335573e0044a010466a00e66442466002006004600c6aae754008c014d55cf280118021aba200301213574200222440042442446600200800624464646666ae68cdc3a800a40004642446004006600a6ae84d55cf280191999ab9a3370ea0049001109100091931900819ab9c01101000e00d135573aa00226ea80048c8c8cccd5cd19b875001480188c848888c010014c01cd5d09aab9e500323333573466e1d400920042321222230020053009357426aae7940108cccd5cd19b875003480088c848888c004014c01cd5d09aab9e500523333573466e1d40112000232122223003005375c6ae84d55cf280311931900819ab9c01101000e00d00c00b135573aa00226ea80048c8c8cccd5cd19b8735573aa004900011991091980080180118029aba15002375a6ae84d5d1280111931900619ab9c00d00c00a135573ca00226ea80048c8cccd5cd19b8735573aa002900011bae357426aae7940088c98c8028cd5ce00580500409baa001232323232323333573466e1d4005200c21222222200323333573466e1d4009200a21222222200423333573466e1d400d2008233221222222233001009008375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c4664424444444660040120106eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc8848888888cc018024020c030d5d0a8049bae357426ae8940248cccd5cd19b875006480088c848888888c01c020c034d5d09aab9e500b23333573466e1d401d2000232122222223005008300e357426aae7940308c98c804ccd5ce00a00980880800780700680600589aab9d5004135573ca00626aae7940084d55cf280089baa0012323232323333573466e1d400520022333222122333001005004003375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea0049000119091180100198041aba135573ca00c464c6401866ae700340300280244d55cea80189aba25001135573ca00226ea80048c8c8cccd5cd19b875001480088c8488c00400cdd71aba135573ca00646666ae68cdc3a8012400046424460040066eb8d5d09aab9e500423263200933573801401200e00c26aae7540044dd500089119191999ab9a3370ea00290021091100091999ab9a3370ea00490011190911180180218031aba135573ca00846666ae68cdc3a801a400042444004464c6401466ae7002c02802001c0184d55cea80089baa0012323333573466e1d40052002200723333573466e1d40092000212200123263200633573800e00c00800626aae74dd5000a4c24002920103505431001220021123230010012233003300200200133351222335122335004335500248811c2b194b7d10a3d2d3152c5f3a628ff50cb9fc11e59453e8ac7a1aea4500488104544e4654005005112212330010030021120011122002122122330010040031200101").unwrap(); - - let redeemer = Redeemer::from_json("\ - { - \"tag\": \"Mint\", - \"index\": \"0\", - \"data\": \"{\\\"constructor\\\":0,\\\"fields\\\":[]}\", - \"ex_units\": { - \"mem\": \"1042996\", - \"steps\": \"446100241\" - } - }").unwrap(); - - let redeemer2 = Redeemer::from_json("\ - { - \"tag\": \"Mint\", - \"index\": \"0\", - \"data\": \"{\\\"constructor\\\":0,\\\"fields\\\":[]}\", - \"ex_units\": { - \"mem\": \"2929292\", - \"steps\": \"446188888\" - } - }").unwrap(); - - let asset_name = AssetName::from_hex("44544e4654").unwrap(); - let asset_name2 = AssetName::from_hex("44544e4ada").unwrap(); - let mut mint_builder = MintBuilder::new(); - let plutus_script_source = PlutusScriptSource::new(&plutus_script); - let mint_witnes = MintWitness::new_plutus_script(&plutus_script_source, &redeemer); - let mint_witnes2 = MintWitness::new_plutus_script(&plutus_script_source, &redeemer2); - mint_builder.add_asset(&mint_witnes, &asset_name, &Int::new(&BigNum::from(100u64))); - mint_builder.add_asset(&mint_witnes2, &asset_name, &Int::new(&BigNum::from(100u64))); - - let output_adress = Address::from_bech32("addr_test1qpm5njmgzf4t7225v6j34wl30xfrufzt3jtqtdzf3en9ahpmnhtmynpasyc8fq75zv0uaj86vzsr7g3g8q5ypgu5fwtqr9zsgj").unwrap(); - let mut output_assets = MultiAsset::new(); - let mut asset = Assets::new(); - asset.insert(&asset_name, &BigNum::from(100u64)); - output_assets.insert(&plutus_script.hash(), &asset); - let output_value = Value::new_with_assets(&Coin::from(50000u64), &output_assets); - let output = TransactionOutput::new(&output_adress, &output_value); - - let mut col_builder = TxInputsBuilder::new(); - col_builder.add_input(&colateral_adress, &colateral_input, &Value::new(&Coin::from(1000000000u64))); - tx_builder.set_collateral(&col_builder); - tx_builder.add_output(&output); - tx_builder.add_input(&output_adress, &tx_input, &Value::new(&BigNum::from(100000000000u64))); - tx_builder.set_mint_builder(&mint_builder); - - tx_builder.calc_script_data_hash(&TxBuilderConstants::plutus_vasil_cost_models()).unwrap(); - - let change_res = tx_builder.add_change_if_needed(&output_adress); - assert!(change_res.is_ok()); - - let build_res = tx_builder.build_tx(); - assert!(build_res.is_ok()); - - let tx = build_res.unwrap(); - assert_eq!(tx.witness_set.plutus_scripts.unwrap().len(), 1usize); - assert_eq!(tx.witness_set.redeemers.unwrap().len(), 2usize); - assert!(tx.witness_set.plutus_data.is_none()); - assert!(tx.body.reference_inputs.is_none()); - assert!(tx.body.mint.is_some()); - assert_eq!(tx.body.mint.unwrap().len(), 2usize); - } - - #[test] - fn multiple_plutus_inputs_test() { - let mut tx_builder = create_reallistic_tx_builder(); - let plutus_script = plutus::PlutusScript::from_hex("5907d2010000332323232323232323232323232323322323232323222232325335332201b3333573466e1cd55ce9baa0044800080608c98c8060cd5ce00c80c00b1999ab9a3370e6aae7540092000233221233001003002323232323232323232323232323333573466e1cd55cea8062400046666666666664444444444442466666666666600201a01801601401201000e00c00a00800600466a02a02c6ae854030cd4054058d5d0a80599a80a80b9aba1500a3335501975ca0306ae854024ccd54065d7280c1aba1500833501502035742a00e666aa032042eb4d5d0a8031919191999ab9a3370e6aae75400920002332212330010030023232323333573466e1cd55cea8012400046644246600200600466a056eb4d5d0a80118161aba135744a004464c6405c66ae700bc0b80b04d55cf280089baa00135742a0046464646666ae68cdc39aab9d5002480008cc8848cc00400c008cd40add69aba15002302c357426ae8940088c98c80b8cd5ce01781701609aab9e5001137540026ae84d5d1280111931901519ab9c02b02a028135573ca00226ea8004d5d0a80299a80abae35742a008666aa03203a40026ae85400cccd54065d710009aba15002301f357426ae8940088c98c8098cd5ce01381301209aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba15002300f357426ae8940088c98c8060cd5ce00c80c00b080b89931900b99ab9c4910350543500017135573ca00226ea800448c88c008dd6000990009aa80a911999aab9f0012500a233500930043574200460066ae880080508c8c8cccd5cd19b8735573aa004900011991091980080180118061aba150023005357426ae8940088c98c8050cd5ce00a80a00909aab9e5001137540024646464646666ae68cdc39aab9d5004480008cccc888848cccc00401401000c008c8c8c8cccd5cd19b8735573aa0049000119910919800801801180a9aba1500233500f014357426ae8940088c98c8064cd5ce00d00c80b89aab9e5001137540026ae854010ccd54021d728039aba150033232323333573466e1d4005200423212223002004357426aae79400c8cccd5cd19b875002480088c84888c004010dd71aba135573ca00846666ae68cdc3a801a400042444006464c6403666ae7007006c06406005c4d55cea80089baa00135742a00466a016eb8d5d09aba2500223263201533573802c02a02626ae8940044d5d1280089aab9e500113754002266aa002eb9d6889119118011bab00132001355012223233335573e0044a010466a00e66442466002006004600c6aae754008c014d55cf280118021aba200301213574200222440042442446600200800624464646666ae68cdc3a800a40004642446004006600a6ae84d55cf280191999ab9a3370ea0049001109100091931900819ab9c01101000e00d135573aa00226ea80048c8c8cccd5cd19b875001480188c848888c010014c01cd5d09aab9e500323333573466e1d400920042321222230020053009357426aae7940108cccd5cd19b875003480088c848888c004014c01cd5d09aab9e500523333573466e1d40112000232122223003005375c6ae84d55cf280311931900819ab9c01101000e00d00c00b135573aa00226ea80048c8c8cccd5cd19b8735573aa004900011991091980080180118029aba15002375a6ae84d5d1280111931900619ab9c00d00c00a135573ca00226ea80048c8cccd5cd19b8735573aa002900011bae357426aae7940088c98c8028cd5ce00580500409baa001232323232323333573466e1d4005200c21222222200323333573466e1d4009200a21222222200423333573466e1d400d2008233221222222233001009008375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c4664424444444660040120106eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc8848888888cc018024020c030d5d0a8049bae357426ae8940248cccd5cd19b875006480088c848888888c01c020c034d5d09aab9e500b23333573466e1d401d2000232122222223005008300e357426aae7940308c98c804ccd5ce00a00980880800780700680600589aab9d5004135573ca00626aae7940084d55cf280089baa0012323232323333573466e1d400520022333222122333001005004003375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea0049000119091180100198041aba135573ca00c464c6401866ae700340300280244d55cea80189aba25001135573ca00226ea80048c8c8cccd5cd19b875001480088c8488c00400cdd71aba135573ca00646666ae68cdc3a8012400046424460040066eb8d5d09aab9e500423263200933573801401200e00c26aae7540044dd500089119191999ab9a3370ea00290021091100091999ab9a3370ea00490011190911180180218031aba135573ca00846666ae68cdc3a801a400042444004464c6401466ae7002c02802001c0184d55cea80089baa0012323333573466e1d40052002200723333573466e1d40092000212200123263200633573800e00c00800626aae74dd5000a4c24002920103505431001220021123230010012233003300200200133351222335122335004335500248811c2b194b7d10a3d2d3152c5f3a628ff50cb9fc11e59453e8ac7a1aea4500488104544e4654005005112212330010030021120011122002122122330010040031200101").unwrap(); - let redeemer1 = Redeemer::from_json("\ - { - \"tag\": \"Mint\", - \"index\": \"0\", - \"data\": \"{\\\"constructor\\\":0,\\\"fields\\\":[]}\", - \"ex_units\": { - \"mem\": \"1042996\", - \"steps\": \"446100241\" - } - }").unwrap(); - - let redeemer2 = Redeemer::from_json("\ - { - \"tag\": \"Mint\", - \"index\": \"0\", - \"data\": \"{\\\"constructor\\\":0,\\\"fields\\\":[]}\", - \"ex_units\": { - \"mem\": \"1042996\", - \"steps\": \"446100241\" - } - }").unwrap(); - - let mut in_builder = TxInputsBuilder::new(); - let input_1 = TransactionInput::new( - &TransactionHash::from_bytes( - hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") - .unwrap(), - ) - .unwrap(), - 1, - ); - let input_2 = TransactionInput::new( - &TransactionHash::from_bytes( - hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") - .unwrap(), - ) - .unwrap(), - 2, - ); - - let colateral_adress = Address::from_bech32("addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w").unwrap(); - let colateral_input = TransactionInput::new( - &TransactionHash::from_bytes( - hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") - .unwrap(), - ) - .unwrap(), - 3 - ); - - let output_adress = Address::from_bech32("addr_test1qpm5njmgzf4t7225v6j34wl30xfrufzt3jtqtdzf3en9ahpmnhtmynpasyc8fq75zv0uaj86vzsr7g3g8q5ypgu5fwtqr9zsgj").unwrap(); - let output_value = Value::new(&Coin::from(500000u64)); - let output = TransactionOutput::new(&output_adress, &output_value); - - tx_builder.add_output(&output); - let mut col_builder = TxInputsBuilder::new(); - col_builder.add_input(&colateral_adress, &colateral_input, &Value::new(&Coin::from(1000000000u64))); - tx_builder.set_collateral(&col_builder); - - let datum = PlutusData::new_bytes(fake_bytes_32(11)); - let plutus_wit1 = PlutusWitness::new( - &plutus_script, - &datum, - &redeemer1 - ); - - let plutus_wit2 = PlutusWitness::new( - &plutus_script, - &datum, - &redeemer2 - ); - - let value = Value::new(&Coin::from(100000000u64)); - - in_builder.add_plutus_script_input(&plutus_wit1, &input_1, &value); - in_builder.add_plutus_script_input(&plutus_wit2, &input_2, &value); - - tx_builder.set_inputs(&in_builder); - tx_builder.calc_script_data_hash(&TxBuilderConstants::plutus_vasil_cost_models()); - tx_builder.add_change_if_needed(&output_adress); - let build_res = tx_builder.build_tx(); - assert!(&build_res.is_ok()); - let tx = build_res.unwrap(); - assert_eq!(tx.witness_set.plutus_scripts.unwrap().len(), 1usize); - assert_eq!(tx.witness_set.redeemers.unwrap().len(), 2usize); - } - - #[test] - fn multiple_plutus_inputs_with_missed_wit_test() { - let mut tx_builder = create_reallistic_tx_builder(); - let plutus_script = plutus::PlutusScript::from_hex("5907d2010000332323232323232323232323232323322323232323222232325335332201b3333573466e1cd55ce9baa0044800080608c98c8060cd5ce00c80c00b1999ab9a3370e6aae7540092000233221233001003002323232323232323232323232323333573466e1cd55cea8062400046666666666664444444444442466666666666600201a01801601401201000e00c00a00800600466a02a02c6ae854030cd4054058d5d0a80599a80a80b9aba1500a3335501975ca0306ae854024ccd54065d7280c1aba1500833501502035742a00e666aa032042eb4d5d0a8031919191999ab9a3370e6aae75400920002332212330010030023232323333573466e1cd55cea8012400046644246600200600466a056eb4d5d0a80118161aba135744a004464c6405c66ae700bc0b80b04d55cf280089baa00135742a0046464646666ae68cdc39aab9d5002480008cc8848cc00400c008cd40add69aba15002302c357426ae8940088c98c80b8cd5ce01781701609aab9e5001137540026ae84d5d1280111931901519ab9c02b02a028135573ca00226ea8004d5d0a80299a80abae35742a008666aa03203a40026ae85400cccd54065d710009aba15002301f357426ae8940088c98c8098cd5ce01381301209aba25001135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135744a00226aae7940044dd50009aba15002300f357426ae8940088c98c8060cd5ce00c80c00b080b89931900b99ab9c4910350543500017135573ca00226ea800448c88c008dd6000990009aa80a911999aab9f0012500a233500930043574200460066ae880080508c8c8cccd5cd19b8735573aa004900011991091980080180118061aba150023005357426ae8940088c98c8050cd5ce00a80a00909aab9e5001137540024646464646666ae68cdc39aab9d5004480008cccc888848cccc00401401000c008c8c8c8cccd5cd19b8735573aa0049000119910919800801801180a9aba1500233500f014357426ae8940088c98c8064cd5ce00d00c80b89aab9e5001137540026ae854010ccd54021d728039aba150033232323333573466e1d4005200423212223002004357426aae79400c8cccd5cd19b875002480088c84888c004010dd71aba135573ca00846666ae68cdc3a801a400042444006464c6403666ae7007006c06406005c4d55cea80089baa00135742a00466a016eb8d5d09aba2500223263201533573802c02a02626ae8940044d5d1280089aab9e500113754002266aa002eb9d6889119118011bab00132001355012223233335573e0044a010466a00e66442466002006004600c6aae754008c014d55cf280118021aba200301213574200222440042442446600200800624464646666ae68cdc3a800a40004642446004006600a6ae84d55cf280191999ab9a3370ea0049001109100091931900819ab9c01101000e00d135573aa00226ea80048c8c8cccd5cd19b875001480188c848888c010014c01cd5d09aab9e500323333573466e1d400920042321222230020053009357426aae7940108cccd5cd19b875003480088c848888c004014c01cd5d09aab9e500523333573466e1d40112000232122223003005375c6ae84d55cf280311931900819ab9c01101000e00d00c00b135573aa00226ea80048c8c8cccd5cd19b8735573aa004900011991091980080180118029aba15002375a6ae84d5d1280111931900619ab9c00d00c00a135573ca00226ea80048c8cccd5cd19b8735573aa002900011bae357426aae7940088c98c8028cd5ce00580500409baa001232323232323333573466e1d4005200c21222222200323333573466e1d4009200a21222222200423333573466e1d400d2008233221222222233001009008375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c4664424444444660040120106eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc8848888888cc018024020c030d5d0a8049bae357426ae8940248cccd5cd19b875006480088c848888888c01c020c034d5d09aab9e500b23333573466e1d401d2000232122222223005008300e357426aae7940308c98c804ccd5ce00a00980880800780700680600589aab9d5004135573ca00626aae7940084d55cf280089baa0012323232323333573466e1d400520022333222122333001005004003375a6ae854010dd69aba15003375a6ae84d5d1280191999ab9a3370ea0049000119091180100198041aba135573ca00c464c6401866ae700340300280244d55cea80189aba25001135573ca00226ea80048c8c8cccd5cd19b875001480088c8488c00400cdd71aba135573ca00646666ae68cdc3a8012400046424460040066eb8d5d09aab9e500423263200933573801401200e00c26aae7540044dd500089119191999ab9a3370ea00290021091100091999ab9a3370ea00490011190911180180218031aba135573ca00846666ae68cdc3a801a400042444004464c6401466ae7002c02802001c0184d55cea80089baa0012323333573466e1d40052002200723333573466e1d40092000212200123263200633573800e00c00800626aae74dd5000a4c24002920103505431001220021123230010012233003300200200133351222335122335004335500248811c2b194b7d10a3d2d3152c5f3a628ff50cb9fc11e59453e8ac7a1aea4500488104544e4654005005112212330010030021120011122002122122330010040031200101").unwrap(); - let redeemer1 = Redeemer::from_json("\ - { - \"tag\": \"Mint\", - \"index\": \"0\", - \"data\": \"{\\\"constructor\\\":0,\\\"fields\\\":[]}\", - \"ex_units\": { - \"mem\": \"1042996\", - \"steps\": \"446100241\" - } - }").unwrap(); - - let redeemer2 = Redeemer::from_json("\ - { - \"tag\": \"Mint\", - \"index\": \"0\", - \"data\": \"{\\\"constructor\\\":0,\\\"fields\\\":[]}\", - \"ex_units\": { - \"mem\": \"1042996\", - \"steps\": \"446100241\" - } - }").unwrap(); - - let mut in_builder = TxInputsBuilder::new(); - let input_1 = TransactionInput::new( - &TransactionHash::from_bytes( - hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") - .unwrap(), - ) - .unwrap(), - 1, - ); - let input_2 = TransactionInput::new( - &TransactionHash::from_bytes( - hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") - .unwrap(), - ) - .unwrap(), - 2, - ); - - let colateral_adress = Address::from_bech32("addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w").unwrap(); - let colateral_input = TransactionInput::new( - &TransactionHash::from_bytes( - hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7") - .unwrap(), - ) - .unwrap(), - 3 - ); - - let output_adress = Address::from_bech32("addr_test1qpm5njmgzf4t7225v6j34wl30xfrufzt3jtqtdzf3en9ahpmnhtmynpasyc8fq75zv0uaj86vzsr7g3g8q5ypgu5fwtqr9zsgj").unwrap(); - let output_value = Value::new(&Coin::from(5000000u64)); - let output = TransactionOutput::new(&output_adress, &output_value); - - tx_builder.add_output(&output).unwrap(); - let mut col_builder = TxInputsBuilder::new(); - col_builder.add_input(&colateral_adress, &colateral_input, &Value::new(&Coin::from(1000000000u64))); - tx_builder.set_collateral(&col_builder); - - let datum = PlutusData::new_bytes(fake_bytes_32(11)); - let plutus_wit1 = PlutusWitness::new( - &plutus_script, - &datum, - &redeemer1 - ); - - let plutus_wit2 = PlutusWitness::new( - &plutus_script, - &datum, - &redeemer2 - ); - - let value = Value::new(&Coin::from(100000000u64)); - - in_builder.add_plutus_script_input(&plutus_wit1, &input_1, &value); - let script_addr = create_base_address_from_script_hash(&plutus_script.hash()); - in_builder.add_input(&script_addr, &input_2, &value); - - assert_eq!(in_builder.count_missing_input_scripts(), 1usize); - let mut inputs_with_wit = InputsWithScriptWitness::new(); - let in_with_wit = InputWithScriptWitness::new_with_plutus_witness(&input_2, &plutus_wit2); - inputs_with_wit.add(&in_with_wit); - in_builder.add_required_script_input_witnesses(&inputs_with_wit); - - tx_builder.set_inputs(&in_builder); - - - tx_builder.calc_script_data_hash(&TxBuilderConstants::plutus_vasil_cost_models()).unwrap(); - tx_builder.add_change_if_needed(&output_adress).unwrap(); - let build_res = tx_builder.build_tx(); - assert!(&build_res.is_ok()); - let tx = build_res.unwrap(); - assert_eq!(tx.witness_set.plutus_scripts.unwrap().len(), 1usize); - assert_eq!(tx.witness_set.redeemers.unwrap().len(), 2usize); - } - - #[test] - fn build_tx_with_certs_withdrawals_plutus_script_address() { - let mut tx_builder = create_tx_builder_with_key_deposit(1_000_000); - let spend = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(0) - .to_public(); - let change_key = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(1) - .derive(0) - .to_public(); - let stake = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - let reward = root_key_15() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(3) - .derive(1) - .to_public(); - - let redeemer_cert1 = Redeemer::from_json("\ - { - \"tag\": \"Spend\", - \"index\": \"0\", - \"data\": \"{\\\"constructor\\\":0,\\\"fields\\\":[]}\", - \"ex_units\": { - \"mem\": \"1\", - \"steps\": \"1\" - } - }").unwrap(); - - let redeemer_cert2 = Redeemer::from_json("\ - { - \"tag\": \"Spend\", - \"index\": \"0\", - \"data\": \"{\\\"constructor\\\":0,\\\"fields\\\":[]}\", - \"ex_units\": { - \"mem\": \"2\", - \"steps\": \"2\" - } - }").unwrap(); - - let redeemer_cert3 = Redeemer::from_json("\ - { - \"tag\": \"Spend\", - \"index\": \"0\", - \"data\": \"{\\\"constructor\\\":0,\\\"fields\\\":[]}\", - \"ex_units\": { - \"mem\": \"2\", - \"steps\": \"2\" - } - }").unwrap(); - - let redeemer_withdraw1 = Redeemer::from_json("\ - { - \"tag\": \"Spend\", - \"index\": \"0\", - \"data\": \"{\\\"constructor\\\":0,\\\"fields\\\":[]}\", - \"ex_units\": { - \"mem\": \"4\", - \"steps\": \"4\" - } - }").unwrap(); - - let redeemer_withdraw2 = Redeemer::from_json("\ - { - \"tag\": \"Spend\", - \"index\": \"0\", - \"data\": \"{\\\"constructor\\\":0,\\\"fields\\\":[]}\", - \"ex_units\": { - \"mem\": \"5\", - \"steps\": \"5\" - } - }").unwrap(); - - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - tx_builder.add_key_input( - &spend.to_raw_key().hash(), - &TransactionInput::new(&genesis_id(), 0), - &Value::new(&to_bignum(5_000_000)), - ); - tx_builder.set_ttl(1000); - let (cert_script1, cert_script_hash1) = plutus_script_and_hash(1); - let cert_script_cred1 = StakeCredential::from_scripthash(&cert_script_hash1); - - let (cert_script2, cert_script_hash2) = plutus_script_and_hash(2); - let cert_script_cred2 = StakeCredential::from_scripthash(&cert_script_hash2); - - let cert_script_hash3 = fake_script_hash(3); - let cert_script_cred3 = StakeCredential::from_scripthash(&cert_script_hash3); - - let (withdraw_script1, withdraw_script_hash1) = plutus_script_and_hash(3); - let withdraw_script_cred1 = StakeCredential::from_scripthash(&withdraw_script_hash1); - - let withdraw_script_hash2 = fake_script_hash(3); - let withdraw_script_cred2 = StakeCredential::from_scripthash(&withdraw_script_hash2); - - let cert_witness_1 = PlutusWitness::new_without_datum( - &cert_script1, - &redeemer_cert1 - ); - let cert_witness_2= PlutusWitness::new_without_datum( - &cert_script2, - &redeemer_cert2 - ); - - let ref_cert_script_input_3 = fake_tx_input(1); - let ref_cert_withdrawal_input_2 = fake_tx_input(2); - let plutus_cert_source = PlutusScriptSource::new_ref_input_with_lang_ver( - &cert_script_hash3, - &ref_cert_script_input_3, - &Language::new_plutus_v2() - ); - let plutus_withdrawal_source = PlutusScriptSource::new_ref_input_with_lang_ver( - &withdraw_script_hash2, - &ref_cert_withdrawal_input_2, - &Language::new_plutus_v2() - ); - - let cert_witness_3 = PlutusWitness::new_with_ref_without_datum( - &plutus_cert_source, - &redeemer_cert3 - ); - let withdraw_witness1 = PlutusWitness::new_without_datum( - &withdraw_script1, - &redeemer_withdraw1 - ); - let withdraw_witness2 = PlutusWitness::new_with_ref_without_datum( - &plutus_withdrawal_source, - &redeemer_withdraw2 - ); - - - let mut certs = CertificatesBuilder::new(); - certs.add(&Certificate::new_stake_registration( - &StakeRegistration::new(&stake_cred), - )).unwrap(); - certs.add_with_plutus_witness( - &Certificate::new_stake_delegation(&StakeDelegation::new( - &cert_script_cred1, - &stake.to_raw_key().hash(), // in reality, this should be the pool owner's key, not ours - )), - &cert_witness_1 - ).unwrap(); - certs.add_with_plutus_witness( - &Certificate::new_stake_delegation(&StakeDelegation::new( - &cert_script_cred2, - &stake.to_raw_key().hash(), // in reality, this should be the pool owner's key, not ours - )), - &cert_witness_2 - ).unwrap(); - certs.add(&Certificate::new_stake_delegation(&StakeDelegation::new( - &stake_cred, - &stake.to_raw_key().hash(), // in reality, this should be the pool owner's key, not ours - ))).unwrap(); - certs.add_with_plutus_witness( - &Certificate::new_stake_deregistration(&StakeDeregistration::new( - &cert_script_cred3 - )), - &cert_witness_3 - ).unwrap(); - - tx_builder.set_certs_builder(&certs); - - let mut withdrawals = WithdrawalsBuilder::new(); - let reward_cred = StakeCredential::from_keyhash(&reward.to_raw_key().hash()); - withdrawals.add( - &RewardAddress::new(NetworkInfo::testnet().network_id(), &reward_cred), - &Coin::from(1u32), - ).unwrap(); - withdrawals.add_with_plutus_witness( - &RewardAddress::new(NetworkInfo::testnet().network_id(), &withdraw_script_cred1), - &Coin::from(2u32), - &withdraw_witness1 - ).unwrap(); - withdrawals.add_with_plutus_witness( - &RewardAddress::new(NetworkInfo::testnet().network_id(), &withdraw_script_cred2), - &Coin::from(3u32), - &withdraw_witness2 - ).unwrap(); - tx_builder.set_withdrawals_builder(&withdrawals); - - let change_cred = StakeCredential::from_keyhash(&change_key.to_raw_key().hash()); - let change_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &change_cred, - &stake_cred, - ) - .to_address(); - let cost_models = TxBuilderConstants::plutus_default_cost_models(); - let collateral_input = fake_tx_input(1); - let collateral_addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &StakeCredential::from_keyhash(&fake_key_hash(1)), - &StakeCredential::from_keyhash(&fake_key_hash(2)), - ).to_address(); - let mut collateral_builder = TxInputsBuilder::new(); - collateral_builder.add_input(&collateral_addr, &collateral_input, &Value::new(&Coin::from(123u32))); - tx_builder.set_collateral(&collateral_builder); - tx_builder.calc_script_data_hash(&cost_models).unwrap(); - tx_builder.add_change_if_needed(&change_addr).unwrap(); - assert_eq!(tx_builder.outputs.len(), 1); - assert_eq!( - tx_builder - .get_explicit_input() - .unwrap() - .checked_add(&tx_builder.get_implicit_input().unwrap()) - .unwrap(), - tx_builder - .get_explicit_output() - .unwrap() - .checked_add(&Value::new(&tx_builder.get_fee_if_set().unwrap())) - .unwrap() - .checked_add(&Value::new(&tx_builder.get_deposit().unwrap())) - .unwrap() - ); - let final_tx = tx_builder.build_tx().unwrap(); - let final_tx_body = final_tx.body(); - let final_tx_wits = final_tx.witness_set(); - - assert_eq!(final_tx_body.reference_inputs().unwrap().len(), 2); - assert_eq!(final_tx_body.withdrawals().unwrap().len(), 3); - assert_eq!(final_tx_body.certs().unwrap().len(), 5); - assert_eq!(final_tx_wits.plutus_scripts().unwrap().len(), 3); - assert_eq!(final_tx_wits.redeemers().unwrap().len(), 5); - - let certs = final_tx_body.certs().unwrap().0; - let withdraws = final_tx_body.withdrawals().unwrap().0 - .iter() - .map(|(k, _)| k.clone()) - .collect::>(); - let redeemers = final_tx_wits.redeemers().unwrap(); - let mut indexes = HashMap::new(); - indexes.insert(RedeemerTag::new_cert(), HashSet::new()); - indexes.insert(RedeemerTag::new_reward(), HashSet::new()); - for redeemer in &redeemers.0 { - let tag_set = indexes.get_mut(&redeemer.tag()).unwrap(); - assert_ne!(tag_set.contains(&redeemer.index()), true); - tag_set.insert(redeemer.index()); - let index: usize = redeemer.index().into(); - if redeemer.tag().kind() == RedeemerTagKind::Cert { - let cert = &certs[index]; - assert!(cert.has_required_script_witness()); - } else if redeemer.tag().kind() == RedeemerTagKind::Reward { - let withdraw = &withdraws[index]; - assert!(withdraw.payment_cred().has_script_hash()); - } - } - } - - #[test] - pub fn test_extra_datum() { - let mut tx_builder = create_reallistic_tx_builder(); - tx_builder.set_fee(&to_bignum(42)); - - let datum = PlutusData::new_bytes(fake_bytes_32(1)); - tx_builder.add_extra_witness_datum(&datum); - - let mut inp = TxInputsBuilder::new(); - inp.add_input( - &fake_base_address(0), - &fake_tx_input(0), - &Value::new(&to_bignum(1000000u64)), - ); - - tx_builder.set_inputs(&inp); - tx_builder - .calc_script_data_hash(&TxBuilderConstants::plutus_default_cost_models()) - .unwrap(); - - - let res = tx_builder.build_tx(); - assert!(res.is_ok()); - - let tx = res.unwrap(); - - let data_hash = hash_script_data( - &Redeemers::new(), - &Costmdls::new(), - Some(PlutusList::from(vec![datum.clone()])), - ); - - let tx_builder_script_data_hash = tx_builder.script_data_hash.clone(); - assert_eq!(tx_builder_script_data_hash.unwrap(), data_hash); - - let extra_datums = tx_builder.get_extra_witness_datums().unwrap(); - assert_eq!(&extra_datums.get(0), &datum); - assert_eq!(extra_datums.len(), 1usize); - assert_eq!(tx_builder.get_witness_set().plutus_data().unwrap().len(), 1usize); - assert_eq!(tx.witness_set().plutus_data().unwrap().len(), 1usize); - assert_eq!(tx.witness_set().plutus_data().unwrap().get(0), datum); - } -} diff --git a/rust/src/tx_builder/certificates_builder.rs b/rust/src/tx_builder/certificates_builder.rs deleted file mode 100644 index 789d4aad..00000000 --- a/rust/src/tx_builder/certificates_builder.rs +++ /dev/null @@ -1,227 +0,0 @@ -use crate::tx_builder::script_structs::*; -use crate::*; - -#[wasm_bindgen] -#[derive(Clone, Debug)] -pub struct CertificatesBuilder { - certs: Vec<(Certificate, Option)>, -} - -#[wasm_bindgen] -impl CertificatesBuilder { - pub fn new() -> Self { - Self { certs: Vec::new() } - } - - pub fn add(&mut self, cert: &Certificate) -> Result<(), JsError> { - if cert.has_required_script_witness() { - return Err(JsError::from_str( - "Your certificate has a required script witness.\ - Please use .add_with_plutus_witness or .add_with_native_script instead.", - )); - } - - self.certs.push((cert.clone(), None)); - Ok(()) - } - - pub fn add_with_plutus_witness( - &mut self, - cert: &Certificate, - witness: &PlutusWitness, - ) -> Result<(), JsError> { - if !cert.has_required_script_witness() { - return Err(JsError::from_str( - "Your certificate does not have a required script witness.\ - Please use .add instead.", - )); - } - - self.certs.push(( - cert.clone(), - Some(ScriptWitnessType::PlutusScriptWitness(witness.clone())), - )); - Ok(()) - } - - pub fn add_with_native_script( - &mut self, - cert: &Certificate, - native_script_source: &NativeScriptSource, - ) -> Result<(), JsError> { - if !cert.has_required_script_witness() { - return Err(JsError::from_str( - "Your certificate does not have a required script witness.\ - Please use .add instead.", - )); - } - - self.certs.push(( - cert.clone(), - Some(ScriptWitnessType::NativeScriptWitness( - native_script_source.0.clone(), - )), - )); - Ok(()) - } - - pub(crate) fn get_required_signers(&self) -> RequiredSignersSet { - let mut set = RequiredSignersSet::new(); - for (cert, script_wit) in &self.certs { - let cert_req_signers = witness_keys_for_cert(&cert); - set.extend(cert_req_signers); - if let Some(ScriptWitnessType::NativeScriptWitness(script_source)) = script_wit { - set.extend(script_source.required_signers()); - } - } - set - } - - pub fn get_plutus_witnesses(&self) -> PlutusWitnesses { - let tag = RedeemerTag::new_cert(); - let mut scripts = PlutusWitnesses::new(); - for (i, (_, script_wit)) in self.certs.iter().enumerate() { - if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = script_wit { - let index = BigNum::from(i); - scripts.add(&s.clone_with_redeemer_index_and_tag(&index, &tag)); - } - } - scripts - } - - pub fn get_ref_inputs(&self) -> TransactionInputs { - let mut inputs = Vec::new(); - for (_, script_wit) in self.certs.iter() { - match script_wit { - Some(ScriptWitnessType::NativeScriptWitness(script_source)) => { - if let NativeScriptSourceEnum::RefInput(input, _, _) = script_source { - inputs.push(input.clone()); - } - } - Some(ScriptWitnessType::PlutusScriptWitness(plutus_witness)) => { - if let Some(DatumSourceEnum::RefInput(input)) = &plutus_witness.datum { - inputs.push(input.clone()); - } - if let PlutusScriptSourceEnum::RefInput(input, _, _) = &plutus_witness.script { - inputs.push(input.clone()); - } - } - None => {} - } - } - TransactionInputs(inputs) - } - - pub fn get_native_scripts(&self) -> NativeScripts { - let mut scripts = NativeScripts::new(); - for (_, script_wit) in self.certs.iter() { - if let Some(ScriptWitnessType::NativeScriptWitness( - NativeScriptSourceEnum::NativeScript(script), - )) = script_wit - { - scripts.add(script); - } - } - scripts - } - - pub(crate) fn get_used_plutus_lang_versions(&self) -> BTreeSet { - let mut used_langs = BTreeSet::new(); - for (_, script_wit) in &self.certs { - if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = script_wit { - if let Some(lang) = s.script.language() { - used_langs.insert(lang.clone()); - } - } - } - used_langs - } - - pub fn get_certificates_refund( - &self, - pool_deposit: &BigNum, - key_deposit: &BigNum, - ) -> Result { - let mut refund = Coin::zero(); - for (cert, _) in &self.certs { - match &cert.0 { - CertificateEnum::StakeDeregistration(_cert) => { - refund = refund.checked_add(&key_deposit)?; - } - CertificateEnum::PoolRetirement(_cert) => { - refund = refund.checked_add(&pool_deposit)?; - } - _ => {} - } - } - Ok(Value::new(&refund)) - } - - pub fn get_certificates_deposit( - &self, - pool_deposit: &BigNum, - key_deposit: &BigNum, - ) -> Result { - let mut deposit = Coin::zero(); - for (cert, _) in &self.certs { - match &cert.0 { - CertificateEnum::PoolRegistration(_cert) => { - deposit = deposit.checked_add(&pool_deposit)?; - } - CertificateEnum::StakeRegistration(_cert) => { - deposit = deposit.checked_add(&key_deposit)?; - } - _ => {} - } - } - Ok(deposit) - } - - pub fn has_plutus_scripts(&self) -> bool { - for (_, script_wit) in &self.certs { - if let Some(ScriptWitnessType::PlutusScriptWitness(_)) = script_wit { - return true; - } - } - false - } - - pub fn build(&self) -> Certificates { - let certs = self.certs.iter().map(|(c, _)| c.clone()).collect(); - Certificates(certs) - } -} - -// comes from witsVKeyNeeded in the Ledger spec -fn witness_keys_for_cert(cert_enum: &Certificate) -> RequiredSigners { - let mut set = RequiredSigners::new(); - match &cert_enum.0 { - // stake key registrations do not require a witness - CertificateEnum::StakeRegistration(_cert) => {} - CertificateEnum::StakeDeregistration(cert) => { - if let Some(key) = cert.stake_credential().to_keyhash() { - set.add(&key); - } - } - CertificateEnum::StakeDelegation(cert) => { - if let Some(key) = cert.stake_credential().to_keyhash() { - set.add(&key); - } - } - CertificateEnum::PoolRegistration(cert) => { - for owner in &cert.pool_params().pool_owners().0 { - set.add(&owner.clone()); - } - set.add(&Ed25519KeyHash::from_bytes(cert.pool_params().operator().to_bytes()).unwrap()); - } - CertificateEnum::PoolRetirement(cert) => { - set.add(&Ed25519KeyHash::from_bytes(cert.pool_keyhash().to_bytes()).unwrap()); - } - CertificateEnum::GenesisKeyDelegation(cert) => { - set.add(&Ed25519KeyHash::from_bytes(cert.genesis_delegate_hash().to_bytes()).unwrap()); - } - // not witness as there is no single core node or genesis key that posts the certificate - CertificateEnum::MoveInstantaneousRewardsCert(_cert) => {} - } - set -} diff --git a/rust/src/tx_builder/mint_builder.rs b/rust/src/tx_builder/mint_builder.rs deleted file mode 100644 index 67d11d59..00000000 --- a/rust/src/tx_builder/mint_builder.rs +++ /dev/null @@ -1,238 +0,0 @@ -use super::*; -use crate::plutus::Redeemer; -use crate::tx_builder::script_structs::PlutusScriptSourceEnum; -use crate::tx_builder::script_structs::PlutusScriptSource; - -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -enum MintWitnessEnum { - Plutus(PlutusScriptSourceEnum, Redeemer), - NativeScript(NativeScript), -} - -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -#[wasm_bindgen] -pub struct MintWitness(MintWitnessEnum); - -#[wasm_bindgen] -impl MintWitness { - pub fn new_native_script(native_script: &NativeScript) -> MintWitness { - MintWitness(MintWitnessEnum::NativeScript(native_script.clone())) - } - - pub fn new_plutus_script(plutus_script: &PlutusScriptSource, redeemer: &Redeemer) -> MintWitness { - MintWitness(MintWitnessEnum::Plutus(plutus_script.0.clone(), redeemer.clone())) - } -} - -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -struct NativeMints { - script: NativeScript, - mints: BTreeMap -} - -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -struct PlutusMints { - script: PlutusScriptSourceEnum, - redeemer_mints: BTreeMap>, -} - -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -enum ScriptMint { - Plutus(PlutusMints), - Native(NativeMints), -} - -#[derive(Clone, Debug)] -#[wasm_bindgen] -pub struct MintBuilder { - mints: BTreeMap -} - -#[wasm_bindgen] -impl MintBuilder { - pub fn new() -> MintBuilder { - MintBuilder { - mints: BTreeMap::new(), - } - } - - pub fn add_asset(&mut self, mint: &MintWitness, asset_name: &AssetName, amount: &Int) { - self.update_mint_value(mint, asset_name, amount, false); - } - - pub fn set_asset(&mut self, mint: &MintWitness, asset_name: &AssetName, amount: &Int) { - self.update_mint_value(mint, asset_name, amount, true); - } - - fn update_mint_value(&mut self, mint: &MintWitness, asset_name: &AssetName, amount: &Int, overwrite: bool) { - match &mint.0 { - MintWitnessEnum::NativeScript(native_script) => { - let script_mint = self.mints.entry(native_script.hash()).or_insert(ScriptMint::Native(NativeMints { - script: native_script.clone(), - mints: BTreeMap::new(), - })); - match script_mint { - ScriptMint::Native(native_mints) => { - let mint = native_mints.mints.entry(asset_name.clone()).or_insert(Int::new(&BigNum::zero())); - if overwrite { - mint.0 = amount.0; - } else { - mint.0 += amount.0; - } - }, - _ => {}, - } - }, - MintWitnessEnum::Plutus(plutus_script, redeemer) => { - let script_mint = self.mints.entry(plutus_script.script_hash()).or_insert(ScriptMint::Plutus(PlutusMints { - script: plutus_script.clone(), - redeemer_mints: BTreeMap::new(), - })); - match script_mint { - ScriptMint::Plutus(plutus_mints) => { - let redeemer_mints = plutus_mints.redeemer_mints.entry(redeemer.clone()).or_insert(BTreeMap::new()); - let mint = redeemer_mints.entry(asset_name.clone()).or_insert(Int::new(&BigNum::zero())); - if overwrite { - mint.0 = amount.0; - } else { - mint.0 += amount.0; - } - }, - _ => {}, - } - }, - } - } - - pub fn build(&self) -> Mint { - let mut mint = Mint::new(); - for (_, script_mint) in self.mints.iter() { - match script_mint { - ScriptMint::Native(native_mints) => { - let mut mint_asset = MintAssets::new(); - for (asset_name, amount) in &native_mints.mints { - mint_asset.insert(asset_name, amount.clone()); - } - mint.insert(&native_mints.script.hash(), &mint_asset); - }, - ScriptMint::Plutus(plutus_mints) => { - for (_, redeemer_mints) in &plutus_mints.redeemer_mints { - let mut mint_asset = MintAssets::new(); - for (asset_name, amount) in redeemer_mints { - mint_asset.insert(asset_name, amount.clone()); - - } - mint.insert(&plutus_mints.script.script_hash(), &mint_asset); - } - } - } - } - mint - } - - pub fn get_native_scripts(&self) -> NativeScripts { - let mut native_scripts = Vec::new(); - for script_mint in self.mints.values() { - match script_mint { - ScriptMint::Native(native_mints) => { - native_scripts.push(native_mints.script.clone()); - }, - _ => {}, - } - } - NativeScripts(native_scripts) - } - - pub fn get_plutus_witnesses(&self) -> PlutusWitnesses { - let mut plutus_witnesses = Vec::new(); - for script_mint in self.mints.values() { - match script_mint { - ScriptMint::Plutus(plutus_mints) => { - for (redeemer, _) in &plutus_mints.redeemer_mints { - plutus_witnesses.push( - PlutusWitness::new_with_ref_without_datum( - &PlutusScriptSource(plutus_mints.script.clone()), - redeemer) - ); - } - }, - _ => {}, - } - } - PlutusWitnesses(plutus_witnesses) - } - - pub fn get_ref_inputs(&self) -> TransactionInputs { - let mut reference_inputs = Vec::new(); - for script_mint in self.mints.values() { - match script_mint { - ScriptMint::Plutus(plutus_mints) => { - if let PlutusScriptSourceEnum::RefInput(ref_input, _, _) = &plutus_mints.script { - reference_inputs.push(ref_input.clone()); - } - }, - _ => {}, - } - } - TransactionInputs(reference_inputs) - } - - pub fn get_redeeemers(&self) -> Result { - let tag = RedeemerTag::new_mint(); - let mut redeeemers = Vec::new(); - let mut index = BigNum::zero(); - for (_, script_mint) in &self.mints { - match script_mint { - ScriptMint::Plutus(plutus_mints) => { - for (redeemer, _) in &plutus_mints.redeemer_mints { - redeeemers.push(redeemer.clone_with_index_and_tag(&index, &tag)); - index = index.checked_add(&BigNum::one())?; - } - }, - _ => { - index = index.checked_add(&BigNum::one())?; - }, - } - } - Ok(Redeemers(redeeemers)) - } - - pub fn has_plutus_scripts(&self) -> bool { - for script_mint in self.mints.values() { - match script_mint { - ScriptMint::Plutus(_) => { - return true; - }, - _ => {}, - } - } - false - } - - pub fn has_native_scripts(&self) -> bool { - for script_mint in self.mints.values() { - match script_mint { - ScriptMint::Native(_) => { - return true; - }, - _ => {}, - } - } - false - } - - pub(crate) fn get_used_plutus_lang_versions(&self) -> BTreeSet { - let mut used_langs = BTreeSet::new(); - for (_, script_mint) in &self.mints { - match script_mint { - ScriptMint::Plutus(plutus_mints) => { - if let Some(lang) = plutus_mints.script.language() { - used_langs.insert(lang); - } - }, - _ => {}, - } - } - used_langs - } -} \ No newline at end of file diff --git a/rust/src/tx_builder/script_structs.rs b/rust/src/tx_builder/script_structs.rs deleted file mode 100644 index 085c70cd..00000000 --- a/rust/src/tx_builder/script_structs.rs +++ /dev/null @@ -1,292 +0,0 @@ -use crate::*; - -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum PlutusScriptSourceEnum { - Script(PlutusScript), - RefInput(TransactionInput, ScriptHash, Option), -} - -impl PlutusScriptSourceEnum { - pub fn script_hash(&self) -> ScriptHash { - match self { - PlutusScriptSourceEnum::Script(script) => script.hash(), - PlutusScriptSourceEnum::RefInput(_, script_hash, _) => script_hash.clone(), - } - } - - pub fn language(&self) -> Option { - match self { - PlutusScriptSourceEnum::Script(script) => Some(script.language_version()), - PlutusScriptSourceEnum::RefInput(_, _, language) => language.clone(), - } - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct PlutusScriptSource(pub(crate) PlutusScriptSourceEnum); - -#[wasm_bindgen] -impl PlutusScriptSource { - pub fn new(script: &PlutusScript) -> Self { - Self(PlutusScriptSourceEnum::Script(script.clone())) - } - - /// !!! DEPRECATED !!! - /// This constructor has missed information about plutus script language vesrion. That can affect - /// the script data hash calculation. - /// Use `.new_ref_input_with_lang_ver` instead - #[deprecated( - since = "11.3.0", - note = "This constructor has missed information about plutus script language vesrion. That can affect the script data hash calculation. Use `.new_ref_input_with_lang_ver` instead." - )] - pub fn new_ref_input(script_hash: &ScriptHash, input: &TransactionInput) -> Self { - Self(PlutusScriptSourceEnum::RefInput( - input.clone(), - script_hash.clone(), - None, - )) - } - - pub fn new_ref_input_with_lang_ver( - script_hash: &ScriptHash, - input: &TransactionInput, - lang_ver: &Language, - ) -> Self { - Self(PlutusScriptSourceEnum::RefInput( - input.clone(), - script_hash.clone(), - Some(lang_ver.clone()), - )) - } -} - -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum DatumSourceEnum { - Datum(PlutusData), - RefInput(TransactionInput), -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct DatumSource(DatumSourceEnum); - -#[wasm_bindgen] -impl DatumSource { - pub fn new(datum: &PlutusData) -> Self { - Self(DatumSourceEnum::Datum(datum.clone())) - } - - pub fn new_ref_input(input: &TransactionInput) -> Self { - Self(DatumSourceEnum::RefInput(input.clone())) - } -} - -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub(crate) enum NativeScriptSourceEnum { - NativeScript(NativeScript), - RefInput(TransactionInput, ScriptHash, RequiredSigners), -} - -impl NativeScriptSourceEnum { - pub fn script_hash(&self) -> ScriptHash { - match self { - NativeScriptSourceEnum::NativeScript(script) => script.hash(), - NativeScriptSourceEnum::RefInput(_, script_hash, _) => script_hash.clone(), - } - } - - pub fn required_signers(&self) -> RequiredSignersSet { - match self { - NativeScriptSourceEnum::NativeScript(script) => RequiredSignersSet::from(script), - NativeScriptSourceEnum::RefInput(_, _, required_signers) => required_signers.into(), - } - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct NativeScriptSource(pub(crate) NativeScriptSourceEnum); - -#[wasm_bindgen] -impl NativeScriptSource { - pub fn new(script: &NativeScript) -> Self { - Self(NativeScriptSourceEnum::NativeScript(script.clone())) - } - - pub fn new_ref_input( - script_hash: &ScriptHash, - input: &TransactionInput, - required_signers: &RequiredSigners, - ) -> Self { - Self(NativeScriptSourceEnum::RefInput( - input.clone(), - script_hash.clone(), - required_signers.clone(), - )) - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct PlutusWitness { - pub(crate) script: PlutusScriptSourceEnum, - pub(crate) datum: Option, - pub(crate) redeemer: Redeemer, -} - -#[wasm_bindgen] -impl PlutusWitness { - pub fn new(script: &PlutusScript, datum: &PlutusData, redeemer: &Redeemer) -> Self { - Self { - script: PlutusScriptSourceEnum::Script(script.clone()), - datum: Some(DatumSourceEnum::Datum(datum.clone())), - redeemer: redeemer.clone(), - } - } - - pub fn new_with_ref( - script: &PlutusScriptSource, - datum: &DatumSource, - redeemer: &Redeemer, - ) -> Self { - Self { - script: script.0.clone(), - datum: Some(datum.0.clone()), - redeemer: redeemer.clone(), - } - } - - pub fn new_without_datum(script: &PlutusScript, redeemer: &Redeemer) -> Self { - Self { - script: PlutusScriptSourceEnum::Script(script.clone()), - datum: None, - redeemer: redeemer.clone(), - } - } - - pub fn new_with_ref_without_datum(script: &PlutusScriptSource, redeemer: &Redeemer) -> Self { - Self { - script: script.0.clone(), - datum: None, - redeemer: redeemer.clone(), - } - } - - pub fn script(&self) -> Option { - match &self.script { - PlutusScriptSourceEnum::Script(script) => Some(script.clone()), - _ => None, - } - } - - pub fn datum(&self) -> Option { - match &self.datum { - Some(DatumSourceEnum::Datum(datum)) => Some(datum.clone()), - _ => None, - } - } - - pub fn redeemer(&self) -> Redeemer { - self.redeemer.clone() - } - - #[allow(dead_code)] - pub(crate) fn clone_with_redeemer_index(&self, index: &BigNum) -> Self { - Self { - script: self.script.clone(), - datum: self.datum.clone(), - redeemer: self.redeemer.clone_with_index(index), - } - } - - pub(crate) fn clone_with_redeemer_index_and_tag( - &self, - index: &BigNum, - tag: &RedeemerTag, - ) -> Self { - Self { - script: self.script.clone(), - datum: self.datum.clone(), - redeemer: self.redeemer.clone_with_index_and_tag(index, tag), - } - } -} - -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct PlutusWitnesses(pub(crate) Vec); - -#[wasm_bindgen] -impl PlutusWitnesses { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn len(&self) -> usize { - self.0.len() - } - - pub fn get(&self, index: usize) -> PlutusWitness { - self.0[index].clone() - } - - pub fn add(&mut self, elem: &PlutusWitness) { - self.0.push(elem.clone()); - } - - pub(crate) fn collect(&self) -> (PlutusScripts, Option, Redeemers) { - let mut used_scripts = BTreeSet::new(); - let mut used_datums = BTreeSet::new(); - let mut used_redeemers = BTreeSet::new(); - let mut s = PlutusScripts::new(); - let mut d: Option = None; - let mut r = Redeemers::new(); - self.0.iter().for_each(|w| { - if let PlutusScriptSourceEnum::Script(script) = &w.script { - if used_scripts.insert(script.clone()) { - s.add(script); - } - } - if let Some(DatumSourceEnum::Datum(datum)) = &w.datum { - if used_datums.insert(datum) { - match d { - Some(ref mut d) => d.add(datum), - None => { - d = { - let mut initial_list = PlutusList::new(); - initial_list.add(datum); - Some(initial_list) - } - } - } - } - } - if used_redeemers.insert(w.redeemer.clone()) { - r.add(&w.redeemer); - } - }); - (s, d, r) - } -} - -impl From> for PlutusWitnesses { - fn from(scripts: Vec) -> Self { - Self(scripts) - } -} - -#[derive(Clone, Debug)] -pub(crate) enum ScriptWitnessType { - NativeScriptWitness(NativeScriptSourceEnum), - PlutusScriptWitness(PlutusWitness), -} - -impl ScriptWitnessType { - pub fn script_hash(&self) -> ScriptHash { - match self { - ScriptWitnessType::NativeScriptWitness(script) => script.script_hash(), - ScriptWitnessType::PlutusScriptWitness(script) => script.script.script_hash(), - } - } -} diff --git a/rust/src/tx_builder/test_batch.rs b/rust/src/tx_builder/test_batch.rs deleted file mode 100644 index 951d2168..00000000 --- a/rust/src/tx_builder/test_batch.rs +++ /dev/null @@ -1,762 +0,0 @@ -#[cfg(test)] -mod test { - use crate::*; - use crate::fakes::fake_policy_id; - use crate::fees::{LinearFee, min_fee}; - use crate::tx_builder::{TransactionBuilderConfig, TransactionBuilderConfigBuilder}; - use crate::tx_builder::tx_batch_builder::{create_send_all, TransactionBatchList}; - - fn root_key() -> Bip32PrivateKey { - // art forum devote street sure rather head chuckle guard poverty release quote oak craft enemy - let entropy = [ - 0x0c, 0xcb, 0x74, 0xf3, 0x6b, 0x7d, 0xa1, 0x64, 0x9a, 0x81, 0x44, 0x67, 0x55, 0x22, - 0xd4, 0xd8, 0x09, 0x7c, 0x64, 0x12, - ]; - Bip32PrivateKey::from_bip39_entropy(&entropy, &[]) - } - - fn harden(index: u32) -> u32 { - index | 0x80_00_00_00 - } - - fn generate_address(index: u32) -> Address { - let spend = root_key() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(0) - .derive(index) - .to_public(); - let stake = root_key() - .derive(harden(1852)) - .derive(harden(1815)) - .derive(harden(0)) - .derive(2) - .derive(0) - .to_public(); - let spend_cred = StakeCredential::from_keyhash(&spend.to_raw_key().hash()); - let stake_cred = StakeCredential::from_keyhash(&stake.to_raw_key().hash()); - let addr = BaseAddress::new( - NetworkInfo::testnet().network_id(), - &spend_cred, - &stake_cred, - ); - addr.to_address() - } - - fn generate_assets(from_policy: usize, - from_asset: usize, - policies_count: usize, - assets_count: usize, - asset_name_size: usize, - amount_per_asset: Coin) -> Option { - let mut assets = MultiAsset::new(); - for i in from_policy..(policies_count + from_policy) { - let policy_id = fake_policy_id(i as u8); - for j in from_asset..(assets_count + from_asset) { - let asset_name = AssetName::new(vec![j as u8; asset_name_size]).unwrap(); - assets.set_asset(&policy_id, &asset_name, amount_per_asset); - } - } - - if assets.0.is_empty() { - None - } else { - Some(assets) - } - } - - fn generate_utxo(address: &Address, - index: u32, - from_policy: usize, - from_asset: usize, - policies: usize, - assets: usize, - asset_name_size: usize, - amount_per_asset: Coin, - ada: Coin) -> TransactionUnspentOutput { - let input = TransactionInput::new( - &TransactionHash::from_bytes( - hex::decode("3b40265111d8bb3c3c608d95b3a0bf83461ace32d79336579a1939b3aad1c0b7").unwrap()) - .unwrap(), index); - let assets = generate_assets(from_policy, from_asset, policies, assets, asset_name_size, amount_per_asset); - let value = match assets { - Some(assets) => Value::new_with_assets(&ada, &assets), - None => Value::new(&ada), - }; - let output = TransactionOutput::new(&address, &value); - TransactionUnspentOutput::new(&input, &output) - } - - fn generate_big_ada_utoxs_bacth() -> TransactionUnspentOutputs { - let mut utxos = Vec::new(); - let address = generate_address(1); - let address_2 = generate_address(2); - for i in 0..10 { - let utxo = generate_utxo( - &address, - i, - (i / 2) as usize, - (i / 2) as usize, - 5, - 5, - 20, - Coin::from(500u64), - Coin::from(5000u64)); - utxos.push(utxo); - } - - for i in 0..10000 { - let utxo = generate_utxo( - &address_2, - i + 1000, - 0, - 0, - 0, - 0, - 20, - Coin::zero(), - Coin::from(5000000u64)); - utxos.push(utxo); - } - TransactionUnspentOutputs(utxos) - } - - fn generate_big_utoxs_bacth() -> TransactionUnspentOutputs { - let mut utxos = Vec::new(); - let address = generate_address(1); - let address_2 = generate_address(2); - for i in 0..200 { - let utxo = generate_utxo( - &address, - i, - (i / 2) as usize, - (i / 2) as usize, - 5, - 5, - 20, - Coin::from(500u64), - Coin::from(5000u64)); - utxos.push(utxo); - } - - for i in 0..10 { - let utxo = generate_utxo( - &address_2, - i + 1000, - 0, - 0, - 0, - 0, - 20, - Coin::zero(), - Coin::from(50000000u64)); - utxos.push(utxo); - } - TransactionUnspentOutputs(utxos) - } - - fn get_utxos_total(utxos: &TransactionUnspentOutputs) -> Value { - let mut total_value = Value::zero(); - for utxo in utxos { - total_value = total_value.checked_add(&utxo.output.amount).unwrap(); - } - - total_value - } - - fn get_batch_total(batches: &TransactionBatchList) -> Value { - let mut total_value = Value::zero(); - for batch in batches { - for tx in batch { - for output in &tx.body.outputs { - total_value = total_value.checked_add(&output.amount).unwrap(); - } - let fee = Value::new(&tx.body.fee); - total_value = total_value.checked_add(&fee).unwrap(); - } - } - - total_value - } - - fn get_tx_fee(tx: &Transaction, cfg: &TransactionBuilderConfig) -> Coin { - min_fee(tx, &cfg.fee_algo).unwrap() - } - - fn get_ada_for_output(output: &TransactionOutput, cfg: &TransactionBuilderConfig) -> Coin { - min_ada_for_output(output, &cfg.data_cost).unwrap() - } - - fn check_balance(utxos: &TransactionUnspentOutputs, batches: &TransactionBatchList) { - let utxos_total = get_utxos_total(utxos); - let batches_total = get_batch_total(batches); - assert_eq!(utxos_total, batches_total); - } - - fn check_min_adas(batches: &TransactionBatchList, cfg: &TransactionBuilderConfig) { - for batch in batches { - for tx in batch { - for output in &tx.body.outputs { - let min_ada = get_ada_for_output(output, cfg); - assert!(output.amount.coin >= min_ada); - } - } - } - } - - fn check_fees(batches: &TransactionBatchList, cfg: &TransactionBuilderConfig) { - for batch in batches { - for tx in batch { - let fee = get_tx_fee(tx, cfg); - assert_eq!(fee, tx.body.fee); - } - } - } - - fn check_value_size_limit(batches: &TransactionBatchList, cfg: &TransactionBuilderConfig) { - for batch in batches { - for tx in batch { - for output in &tx.body.outputs { - let value_size = output.amount().to_bytes().len(); - assert!(value_size <= cfg.max_value_size as usize); - } - } - } - } - - fn check_tx_size_limit(batches: &TransactionBatchList, cfg: &TransactionBuilderConfig) { - for batch in batches { - for tx in batch { - let tx_size = tx.to_bytes().len(); - assert!(tx_size <= cfg.max_tx_size as usize); - } - } - } - - #[test] - pub fn test_big_utoxs_batch() { - let utxos = generate_big_utoxs_bacth(); - let linear_fee = LinearFee::new(&to_bignum(44), &to_bignum(155381)); - let cfg = TransactionBuilderConfigBuilder::new() - .fee_algo(&linear_fee) - .pool_deposit(&to_bignum(500000000)) - .key_deposit(&to_bignum(2000000)) - .max_value_size(4000) - .max_tx_size(8000) - .coins_per_utxo_word(&to_bignum(34_482)) - .ex_unit_prices(&ExUnitPrices::new( - &SubCoin::new(&to_bignum(577), &to_bignum(10000)), - &SubCoin::new(&to_bignum(721), &to_bignum(10000000)), - )) - .build() - .unwrap(); - - let res = create_send_all(&generate_address(10000), &utxos, &cfg); - assert!(res.is_ok()); - - let batches = res.unwrap(); - check_balance(&utxos, &batches); - check_min_adas(&batches, &cfg); - check_fees(&batches, &cfg); - check_value_size_limit(&batches, &cfg); - check_tx_size_limit(&batches, &cfg); - } - - #[test] - pub fn test_big_utoxs_ada_batch() { - let utxos = generate_big_ada_utoxs_bacth(); - let linear_fee = LinearFee::new(&to_bignum(44), &to_bignum(155381)); - let cfg = TransactionBuilderConfigBuilder::new() - .fee_algo(&linear_fee) - .pool_deposit(&to_bignum(500000000)) - .key_deposit(&to_bignum(2000000)) - .max_value_size(4000) - .max_tx_size(8000) - .coins_per_utxo_word(&to_bignum(34_482)) - .ex_unit_prices(&ExUnitPrices::new( - &SubCoin::new(&to_bignum(577), &to_bignum(10000)), - &SubCoin::new(&to_bignum(721), &to_bignum(10000000)), - )) - .build() - .unwrap(); - - let res = create_send_all(&generate_address(10000), &utxos, &cfg); - assert!(res.is_ok()); - - let batches = res.unwrap(); - check_balance(&utxos, &batches); - check_min_adas(&batches, &cfg); - check_fees(&batches, &cfg); - check_value_size_limit(&batches, &cfg); - check_tx_size_limit(&batches, &cfg); - } - - #[test] - pub fn test_one_utxo() { - let address = generate_address(1); - let utxo = generate_utxo( - &address, - 1, - 0, - 0, - 3, - 2, - 20, - Coin::from(500u64), - Coin::from(5000000u64)); - - let utxos = TransactionUnspentOutputs(vec![utxo]); - - let linear_fee = LinearFee::new(&to_bignum(44), &to_bignum(155381)); - let cfg = TransactionBuilderConfigBuilder::new() - .fee_algo(&linear_fee) - .pool_deposit(&to_bignum(500000000)) - .key_deposit(&to_bignum(2000000)) - .max_value_size(4000) - .max_tx_size(8000) - .coins_per_utxo_word(&to_bignum(34_482)) - .ex_unit_prices(&ExUnitPrices::new( - &SubCoin::new(&to_bignum(577), &to_bignum(10000)), - &SubCoin::new(&to_bignum(721), &to_bignum(10000000)), - )) - .build() - .unwrap(); - - let res = create_send_all(&generate_address(10000), &utxos, &cfg); - assert!(res.is_ok()); - - let batches = res.unwrap(); - check_balance(&utxos, &batches); - check_min_adas(&batches, &cfg); - check_fees(&batches, &cfg); - check_value_size_limit(&batches, &cfg); - check_tx_size_limit(&batches, &cfg); - } - - #[test] - pub fn test_one_utxo_one_asset_per_output() { - let address = generate_address(1); - let utxo_1 = generate_utxo( - &address, - 1, - 0, - 0, - 3, - 1, - 20, - Coin::from(500u64), - Coin::from(5000000u64)); - - let utxo_2 = generate_utxo( - &address, - 2, - 1, - 0, - 3, - 1, - 20, - Coin::from(500u64), - Coin::from(5000000u64)); - - let utxo_3 = generate_utxo( - &address, - 3, - 2, - 0, - 3, - 1, - 20, - Coin::from(500u64), - Coin::from(5000000u64)); - - let utxos = TransactionUnspentOutputs(vec![utxo_1, utxo_2, utxo_3]); - - let linear_fee = LinearFee::new(&to_bignum(1), &to_bignum(0)); - let cfg = TransactionBuilderConfigBuilder::new() - .fee_algo(&linear_fee) - .pool_deposit(&to_bignum(500000000)) - .key_deposit(&to_bignum(2000000)) - .max_value_size(80) - .max_tx_size(8000) - .coins_per_utxo_word(&to_bignum(34_482)) - .ex_unit_prices(&ExUnitPrices::new( - &SubCoin::new(&to_bignum(577), &to_bignum(10000)), - &SubCoin::new(&to_bignum(721), &to_bignum(10000000)), - )) - .build() - .unwrap(); - - let res = create_send_all(&generate_address(10000), &utxos, &cfg); - assert!(res.is_ok()); - - let batches = res.unwrap(); - - for batch in &batches { - for tx in batch { - for output in &tx.body.outputs() { - assert_eq!(output.amount().multiasset.unwrap().0.len(), 1); - for asset in output.amount().multiasset.unwrap().0.values() { - assert_eq!(asset.len(), 1); - } - } - } - } - - check_balance(&utxos, &batches); - check_min_adas(&batches, &cfg); - check_fees(&batches, &cfg); - check_value_size_limit(&batches, &cfg); - check_tx_size_limit(&batches, &cfg); - } - - #[test] - pub fn test_one_utxo_one_asset_per_tx() { - let address = generate_address(1); - let utxo_1 = generate_utxo( - &address, - 1, - 0, - 0, - 1, - 1, - 20, - Coin::from(500u64), - Coin::from(5000000u64)); - - let utxo_2 = generate_utxo( - &address, - 2, - 1, - 0, - 1, - 1, - 20, - Coin::from(500u64), - Coin::from(5000000u64)); - - let utxo_3 = generate_utxo( - &address, - 3, - 2, - 0, - 1, - 1, - 20, - Coin::from(500u64), - Coin::from(5000000u64)); - - let utxos = TransactionUnspentOutputs(vec![utxo_1, utxo_2, utxo_3]); - - let linear_fee = LinearFee::new(&to_bignum(1), &to_bignum(0)); - let cfg = TransactionBuilderConfigBuilder::new() - .fee_algo(&linear_fee) - .pool_deposit(&to_bignum(500000000)) - .key_deposit(&to_bignum(2000000)) - .max_value_size(80) - .max_tx_size(300) - .coins_per_utxo_word(&to_bignum(34_482)) - .ex_unit_prices(&ExUnitPrices::new( - &SubCoin::new(&to_bignum(577), &to_bignum(10000)), - &SubCoin::new(&to_bignum(721), &to_bignum(10000000)), - )) - .build() - .unwrap(); - - let res = create_send_all(&generate_address(10000), &utxos, &cfg); - assert!(res.is_ok()); - - let batches = res.unwrap(); - - for batch in &batches { - for tx in batch { - assert_eq!(tx.body.outputs().len(), 1); - for output in &tx.body.outputs() { - assert_eq!(output.amount().multiasset.unwrap().0.len(), 1); - for asset in output.amount().multiasset.unwrap().0.values() { - assert_eq!(asset.len(), 1); - } - } - } - } - - check_balance(&utxos, &batches); - check_min_adas(&batches, &cfg); - check_fees(&batches, &cfg); - check_value_size_limit(&batches, &cfg); - check_tx_size_limit(&batches, &cfg); - } - - #[test] - pub fn test_only_ada_utxo() { - let address = generate_address(1); - let utxo = generate_utxo( - &address, - 1, - 0, - 0, - 0, - 0, - 20, - Coin::zero(), - Coin::from(5000000u64)); - - let utxos = TransactionUnspentOutputs(vec![utxo]); - - let linear_fee = LinearFee::new(&to_bignum(1), &to_bignum(0)); - let cfg = TransactionBuilderConfigBuilder::new() - .fee_algo(&linear_fee) - .pool_deposit(&to_bignum(500000000)) - .key_deposit(&to_bignum(2000000)) - .max_value_size(4000) - .max_tx_size(8000) - .coins_per_utxo_word(&to_bignum(34_482)) - .ex_unit_prices(&ExUnitPrices::new( - &SubCoin::new(&to_bignum(577), &to_bignum(10000)), - &SubCoin::new(&to_bignum(721), &to_bignum(10000000)), - )) - .build() - .unwrap(); - - let res = create_send_all(&generate_address(10000), &utxos, &cfg); - assert!(res.is_ok()); - - let batches = res.unwrap(); - check_balance(&utxos, &batches); - check_min_adas(&batches, &cfg); - check_fees(&batches, &cfg); - check_value_size_limit(&batches, &cfg); - check_tx_size_limit(&batches, &cfg); - } - - #[test] - pub fn test_not_enough_ada() { - let address = generate_address(1); - let utxo = generate_utxo( - &address, - 1, - 0, - 0, - 0, - 0, - 20, - Coin::zero(), - Coin::from(1u64)); - - let utxos = TransactionUnspentOutputs(vec![utxo]); - - let linear_fee = LinearFee::new(&to_bignum(44), &to_bignum(155381)); - let cfg = TransactionBuilderConfigBuilder::new() - .fee_algo(&linear_fee) - .pool_deposit(&to_bignum(500000000)) - .key_deposit(&to_bignum(2000000)) - .max_value_size(4000) - .max_tx_size(8000) - .coins_per_utxo_word(&to_bignum(34_482)) - .ex_unit_prices(&ExUnitPrices::new( - &SubCoin::new(&to_bignum(577), &to_bignum(10000)), - &SubCoin::new(&to_bignum(721), &to_bignum(10000000)), - )) - .build() - .unwrap(); - - let res = create_send_all(&generate_address(10000), &utxos, &cfg); - assert!(res.is_err()); - } - - #[test] - pub fn test_value_limit_error() { - let address = generate_address(1); - let utxo = generate_utxo( - &address, - 1, - 0, - 0, - 1, - 1, - 20, - Coin::from(1000000u64), - Coin::from(500000u64)); - - let utxos = TransactionUnspentOutputs(vec![utxo]); - - let linear_fee = LinearFee::new(&to_bignum(44), &to_bignum(155381)); - let cfg = TransactionBuilderConfigBuilder::new() - .fee_algo(&linear_fee) - .pool_deposit(&to_bignum(500000000)) - .key_deposit(&to_bignum(2000000)) - .max_value_size(10) - .max_tx_size(8000000) - .coins_per_utxo_word(&to_bignum(34_482)) - .ex_unit_prices(&ExUnitPrices::new( - &SubCoin::new(&to_bignum(577), &to_bignum(10000)), - &SubCoin::new(&to_bignum(721), &to_bignum(10000000)), - )) - .build() - .unwrap(); - - let res = create_send_all(&generate_address(10000), &utxos, &cfg); - assert!(res.is_err()); - } - - #[test] - pub fn test_tx_limit_error() { - let address = generate_address(1); - let utxo = generate_utxo( - &address, - 1, - 0, - 0, - 10, - 10, - 20, - Coin::from(1000000u64), - Coin::from(50000000u64)); - - let utxos = TransactionUnspentOutputs(vec![utxo]); - - let linear_fee = LinearFee::new(&to_bignum(44), &to_bignum(155381)); - let cfg = TransactionBuilderConfigBuilder::new() - .fee_algo(&linear_fee) - .pool_deposit(&to_bignum(500000000)) - .key_deposit(&to_bignum(2000000)) - .max_value_size(100) - .max_tx_size(2000) - .coins_per_utxo_word(&to_bignum(34_482)) - .ex_unit_prices(&ExUnitPrices::new( - &SubCoin::new(&to_bignum(577), &to_bignum(10000)), - &SubCoin::new(&to_bignum(721), &to_bignum(10000000)), - )) - .build() - .unwrap(); - - let res = create_send_all(&generate_address(10000), &utxos, &cfg); - assert!(res.is_err()); - } - - #[test] - pub fn test_no_utxos() { - let utxos = TransactionUnspentOutputs(vec!()); - - let linear_fee = LinearFee::new(&to_bignum(44), &to_bignum(155381)); - let cfg = TransactionBuilderConfigBuilder::new() - .fee_algo(&linear_fee) - .pool_deposit(&to_bignum(500000000)) - .key_deposit(&to_bignum(2000000)) - .max_value_size(10) - .max_tx_size(8000000) - .coins_per_utxo_word(&to_bignum(34_482)) - .ex_unit_prices(&ExUnitPrices::new( - &SubCoin::new(&to_bignum(577), &to_bignum(10000)), - &SubCoin::new(&to_bignum(721), &to_bignum(10000000)), - )) - .build() - .unwrap(); - - let res = create_send_all(&generate_address(10000), &utxos, &cfg); - assert!(res.is_ok()); - - let batches = res.unwrap(); - let total_txs = batches.into_iter().fold(0, |acc, batch| acc + batch.len()); - assert_eq!(total_txs, 0); - - } - - #[test] - pub fn test_script_input_error() { - let address = Address::from_hex("10798c8ce251c36c15f8bccf3306feae1218fce7503b331e6d92e666aa00efb5788e8713c844dfd32b2e91de1e309fefffd555f827cc9ee164").unwrap(); - let utxo = generate_utxo( - &address, - 1, - 0, - 0, - 0, - 0, - 20, - Coin::zero(), - Coin::from(1u64)); - let utxos = TransactionUnspentOutputs(vec![utxo]); - - let linear_fee = LinearFee::new(&to_bignum(44), &to_bignum(155381)); - let cfg = TransactionBuilderConfigBuilder::new() - .fee_algo(&linear_fee) - .pool_deposit(&to_bignum(500000000)) - .key_deposit(&to_bignum(2000000)) - .max_value_size(10) - .max_tx_size(8000000) - .coins_per_utxo_word(&to_bignum(34_482)) - .ex_unit_prices(&ExUnitPrices::new( - &SubCoin::new(&to_bignum(577), &to_bignum(10000)), - &SubCoin::new(&to_bignum(721), &to_bignum(10000000)), - )) - .build() - .unwrap(); - - let res = create_send_all(&generate_address(10000), &utxos, &cfg); - assert!(res.is_err()); - } - - #[test] - pub fn test_two_asset_utxo_one_ada_utxo() { - let address = generate_address(1); - let asset_utxo_1 = generate_utxo( - &address, - 1, - 0, - 0, - 1, - 1, - 8, - Coin::from(1u64), - Coin::from(1344798u64)); - - let asset_utxo_2 = generate_utxo( - &address, - 2, - 1, - 1, - 1, - 1, - 8, - Coin::from(1u64), - Coin::from(1344798u64)); - - let ada_utxo = generate_utxo( - &address, - 3, - 0, - 0, - 0, - 0, - 20, - Coin::from(1u64), - Coin::from(9967920528u64)); - - let utxos = TransactionUnspentOutputs(vec![asset_utxo_1, asset_utxo_2, ada_utxo]); - - let linear_fee = LinearFee::new(&to_bignum(44), &to_bignum(155381)); - let cfg = TransactionBuilderConfigBuilder::new() - .fee_algo(&linear_fee) - .pool_deposit(&to_bignum(500000000)) - .key_deposit(&to_bignum(2000000)) - .max_value_size(4000) - .max_tx_size(8000) - .coins_per_utxo_word(&to_bignum(34_482)) - .ex_unit_prices(&ExUnitPrices::new( - &SubCoin::new(&to_bignum(577), &to_bignum(10000)), - &SubCoin::new(&to_bignum(721), &to_bignum(10000000)), - )) - .build() - .unwrap(); - - let res = create_send_all(&generate_address(10000), &utxos, &cfg); - assert!(res.is_ok()); - - let batches = res.unwrap(); - check_balance(&utxos, &batches); - check_min_adas(&batches, &cfg); - check_fees(&batches, &cfg); - check_value_size_limit(&batches, &cfg); - check_tx_size_limit(&batches, &cfg); - } -} \ No newline at end of file diff --git a/rust/src/tx_builder/tx_inputs_builder.rs b/rust/src/tx_builder/tx_inputs_builder.rs deleted file mode 100644 index bc3e9dd6..00000000 --- a/rust/src/tx_builder/tx_inputs_builder.rs +++ /dev/null @@ -1,494 +0,0 @@ -use super::*; -use std::collections::{BTreeMap, BTreeSet}; -use crate::tx_builder::script_structs::*; - -#[derive(Clone, Debug)] -pub(crate) struct TxBuilderInput { - pub(crate) input: TransactionInput, - pub(crate) amount: Value, // we need to keep track of the amount in the inputs for input selection -} - -#[wasm_bindgen] -#[derive(Clone, Debug)] -pub struct InputWithScriptWitness { - pub(crate) input: TransactionInput, - pub(crate) witness: ScriptWitnessType -} - -#[wasm_bindgen] -impl InputWithScriptWitness { - pub fn new_with_native_script_witness(input: &TransactionInput, witness: &NativeScript) -> Self { - Self { - input: input.clone(), - witness: ScriptWitnessType::NativeScriptWitness( - NativeScriptSourceEnum::NativeScript(witness.clone())) - } - } - - pub fn new_with_plutus_witness(input: &TransactionInput, witness: &PlutusWitness) -> Self { - Self { - input: input.clone(), - witness: ScriptWitnessType::PlutusScriptWitness(witness.clone()) - } - } - - pub fn input(&self) -> TransactionInput { - self.input.clone() - } -} - -#[wasm_bindgen] -pub struct InputsWithScriptWitness(Vec); - -#[wasm_bindgen] -impl InputsWithScriptWitness { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn add(&mut self, input: &InputWithScriptWitness) { - self.0.push(input.clone()); - } - - pub fn get(&self, index: usize) -> InputWithScriptWitness { - self.0[index].clone() - } - - pub fn len(&self) -> usize { - self.0.len() - } -} - -// We need to know how many of each type of witness will be in the transaction so we can calculate the tx fee -#[derive(Clone, Debug)] -pub struct RequiredWitnessSet { - vkeys: RequiredSignersSet, - scripts: LinkedHashMap>>, - bootstraps: BTreeSet>, -} - -#[wasm_bindgen] -#[derive(Clone, Debug)] -pub struct TxInputsBuilder { - inputs: BTreeMap)>, - required_witnesses: RequiredWitnessSet, -} - -pub(crate) fn get_bootstraps(inputs: &TxInputsBuilder) -> BTreeSet> { - inputs.required_witnesses.bootstraps.clone() -} - -#[wasm_bindgen] -impl TxInputsBuilder { - pub fn new() -> Self { - Self { - inputs: BTreeMap::new(), - required_witnesses: RequiredWitnessSet { - vkeys: BTreeSet::new(), - scripts: LinkedHashMap::new(), - bootstraps: BTreeSet::new(), - }, - } - } - - fn push_input(&mut self, e: (TxBuilderInput, Option)) { - self.inputs.insert(e.0.input.clone(), e); - } - - /// We have to know what kind of inputs these are to know what kind of mock witnesses to create since - /// 1) mock witnesses have different lengths depending on the type which changes the expecting fee - /// 2) Witnesses are a set so we need to get rid of duplicates to avoid over-estimating the fee - pub fn add_key_input( - &mut self, - hash: &Ed25519KeyHash, - input: &TransactionInput, - amount: &Value, - ) { - let inp = TxBuilderInput { - input: input.clone(), - amount: amount.clone(), - }; - self.push_input((inp, None)); - self.required_witnesses.vkeys.insert(hash.clone()); - } - - #[deprecated( - since = "11.2.0", - note = "Use `.add_native_script_input` or `.add_plutus_script_input` instead." - )] - /// !!! DEPRECATED !!! - /// This function can make a mistake in choosing right input index. Use `.add_native_script_input` or `.add_plutus_script_input` instead. - /// This method adds the input to the builder BUT leaves a missing spot for the witness native script - /// - /// After adding the input with this method, use `.add_required_native_input_scripts` - /// and `.add_required_plutus_input_scripts` to add the witness scripts - /// - /// Or instead use `.add_native_script_input` and `.add_plutus_script_input` - /// to add inputs right along with the script, instead of the script hash - pub fn add_script_input( - &mut self, - hash: &ScriptHash, - input: &TransactionInput, - amount: &Value, - ) { - let inp = TxBuilderInput { - input: input.clone(), - amount: amount.clone(), - }; - self.push_input((inp, Some(hash.clone()))); - self.insert_input_with_empty_witness(hash, input); - } - - /// This method will add the input to the builder and also register the required native script witness - pub fn add_native_script_input( - &mut self, - script: &NativeScript, - input: &TransactionInput, - amount: &Value, - ) { - let hash = script.hash(); - self.add_script_input(&hash, input, amount); - let witness = ScriptWitnessType::NativeScriptWitness( - NativeScriptSourceEnum::NativeScript(script.clone())); - self.insert_input_with_witness(&hash, input, &witness); - } - - /// This method will add the input to the builder and also register the required plutus witness - pub fn add_plutus_script_input( - &mut self, - witness: &PlutusWitness, - input: &TransactionInput, - amount: &Value, - ) { - let hash = witness.script.script_hash(); - - self.add_script_input(&hash, input, amount); - let witness = ScriptWitnessType::PlutusScriptWitness(witness.clone()); - self.insert_input_with_witness(&hash, input, &witness); - } - - pub fn add_bootstrap_input( - &mut self, - hash: &ByronAddress, - input: &TransactionInput, - amount: &Value, - ) { - let inp = TxBuilderInput { - input: input.clone(), - amount: amount.clone(), - }; - self.push_input((inp, None)); - self.required_witnesses.bootstraps.insert(hash.to_bytes()); - } - - /// Note that for script inputs this method will use underlying generic `.add_script_input` - /// which leaves a required empty spot for the script witness (or witnesses in case of Plutus). - /// You can use `.add_native_script_input` or `.add_plutus_script_input` directly to register the input along with the witness. - pub fn add_input(&mut self, address: &Address, input: &TransactionInput, amount: &Value) { - match &BaseAddress::from_address(address) { - Some(addr) => { - match &addr.payment_cred().to_keyhash() { - Some(hash) => return self.add_key_input(hash, input, amount), - None => (), - } - match &addr.payment_cred().to_scripthash() { - Some(hash) => return self.add_script_input(hash, input, amount), - None => (), - } - } - None => (), - } - match &EnterpriseAddress::from_address(address) { - Some(addr) => { - match &addr.payment_cred().to_keyhash() { - Some(hash) => return self.add_key_input(hash, input, amount), - None => (), - } - match &addr.payment_cred().to_scripthash() { - Some(hash) => return self.add_script_input(hash, input, amount), - None => (), - } - } - None => (), - } - match &PointerAddress::from_address(address) { - Some(addr) => { - match &addr.payment_cred().to_keyhash() { - Some(hash) => return self.add_key_input(hash, input, amount), - None => (), - } - match &addr.payment_cred().to_scripthash() { - Some(hash) => return self.add_script_input(hash, input, amount), - None => (), - } - } - None => (), - } - match &ByronAddress::from_address(address) { - Some(addr) => { - return self.add_bootstrap_input(addr, input, amount); - } - None => (), - } - } - - /// Returns the number of still missing input scripts (either native or plutus) - /// Use `.add_required_native_input_scripts` or `.add_required_plutus_input_scripts` to add the missing scripts - pub fn count_missing_input_scripts(&self) -> usize { - self.required_witnesses - .scripts - .values() - .flat_map(|v| v.values()) - .filter(|s| s.is_none()) - .count() - } - - /// Try adding the specified scripts as witnesses for ALREADY ADDED script inputs - /// Any scripts that don't match any of the previously added inputs will be ignored - /// Returns the number of remaining required missing witness scripts - /// Use `.count_missing_input_scripts` to find the number of still missing scripts - pub fn add_required_native_input_scripts(&mut self, scripts: &NativeScripts) -> usize { - scripts.0.iter().for_each(|s: &NativeScript| { - let hash = s.hash(); - if let Some(script_wits) = self.required_witnesses.scripts.get_mut(&hash) { - let mut tx_in = None; - for script_wit in script_wits { - if script_wit.1.is_none() { - tx_in = Some(script_wit.0.clone()); - break; - } - } - - if let Some(tx_in) = tx_in { - let witness = ScriptWitnessType::NativeScriptWitness( - NativeScriptSourceEnum::NativeScript(s.clone())); - self.insert_input_with_witness(&hash, &tx_in, &witness); - } - } - }); - self.count_missing_input_scripts() - } - - #[deprecated( - since = "11.2.0", - note = "This function can make a mistake in choosing right input index. Use `.add_required_script_input_witnesses` instead." - )] - /// !!! DEPRECATED !!! - /// This function can make a mistake in choosing right input index. Use `.add_required_script_input_witnesses` instead. - /// Try adding the specified scripts as witnesses for ALREADY ADDED script inputs - /// Any scripts that don't match any of the previously added inputs will be ignored - /// Returns the number of remaining required missing witness scripts - /// Use `.count_missing_input_scripts` to find the number of still missing scripts - pub fn add_required_plutus_input_scripts(&mut self, scripts: &PlutusWitnesses) -> usize { - scripts.0.iter().for_each(|s: &PlutusWitness| { - let hash = s.script.script_hash(); - if let Some(script_wits) = self.required_witnesses.scripts.get_mut(&hash) { - let mut tx_in = None; - for script_wit in script_wits { - if script_wit.1.is_none() { - tx_in = Some(script_wit.0.clone()); - break; - } - } - - if let Some(tx_in) = tx_in { - let witness = ScriptWitnessType::PlutusScriptWitness(s.clone()); - self.insert_input_with_witness(&hash, &tx_in, &witness); - } - } - }); - self.count_missing_input_scripts() - } - - /// Try adding the specified scripts as witnesses for ALREADY ADDED script inputs - /// Any scripts that don't match any of the previously added inputs will be ignored - /// Returns the number of remaining required missing witness scripts - /// Use `.count_missing_input_scripts` to find the number of still missing scripts - pub fn add_required_script_input_witnesses(&mut self, inputs_with_wit: &InputsWithScriptWitness) -> usize { - inputs_with_wit.0.iter().for_each(|input_with_wit: &InputWithScriptWitness| { - let hash = input_with_wit.witness.script_hash(); - if let Some(script_wits) = self.required_witnesses.scripts.get_mut(&hash) { - if script_wits.contains_key(&input_with_wit.input) { - script_wits.insert(input_with_wit.input.clone(), Some(input_with_wit.witness.clone())); - } - } }); - self.count_missing_input_scripts() - } - - pub fn get_ref_inputs(&self) -> TransactionInputs { - let mut inputs = Vec::new(); - for wintess in self.required_witnesses.scripts.iter() - .flat_map(|(_, tx_wits)| tx_wits.values()) - .filter_map(|wit| wit.as_ref()) { - match wintess { - ScriptWitnessType::NativeScriptWitness(NativeScriptSourceEnum::RefInput(input, _, _)) => { - inputs.push(input.clone()); - }, - ScriptWitnessType::PlutusScriptWitness(plutus_witness) => { - if let Some(DatumSourceEnum::RefInput(input)) = &plutus_witness.datum { - inputs.push(input.clone()); - } - if let PlutusScriptSourceEnum::RefInput(input, _, _) = &plutus_witness.script { - inputs.push(input.clone()); - } - }, - _ => () - } - } - TransactionInputs(inputs) - } - - /// Returns a copy of the current script input witness scripts in the builder - pub fn get_native_input_scripts(&self) -> Option { - let mut scripts = NativeScripts::new(); - self.required_witnesses.scripts - .values() - .flat_map(|v| v) - .for_each(|tx_in_with_wit| { - if let Some(ScriptWitnessType::NativeScriptWitness( - NativeScriptSourceEnum::NativeScript(s))) = tx_in_with_wit.1 { - scripts.add(&s); - } - }); - if scripts.len() > 0 { - Some(scripts) - } else { - None - } - } - - pub(crate) fn get_used_plutus_lang_versions(&self) -> BTreeSet { - let mut used_langs = BTreeSet::new(); - self.required_witnesses.scripts.values().for_each(|input_with_wit| { - for (_, script_wit) in input_with_wit { - if let Some(ScriptWitnessType::PlutusScriptWitness(PlutusWitness { script, .. })) = script_wit { - if let Some(lang) = script.language() { - used_langs.insert(lang); - } - } - } - }); - used_langs - } - - /// Returns a copy of the current plutus input witness scripts in the builder. - /// NOTE: each plutus witness will be cloned with a specific corresponding input index - pub fn get_plutus_input_scripts(&self) -> Option { - /* - * === EXPLANATION === - * The `Redeemer` object contains the `.index` field which is supposed to point - * exactly to the index of the corresponding input in the inputs array. We want to - * simplify and automate this as much as possible for the user to not have to care about it. - * - * For this we store the script hash along with the input, when it was registered, and - * now we create a map of script hashes to their input indexes. - * - * The registered witnesses are then each cloned with the new correct redeemer input index. - * To avoid incorrect redeemer tag we also set the `tag` field to `spend`. - */ - let tag = RedeemerTag::new_spend(); - let script_hash_index_map: BTreeMap<&TransactionInput, BigNum> = self - .inputs - .values() - .enumerate() - .fold(BTreeMap::new(), |mut m, (i, (tx_in, hash_option))| { - if hash_option.is_some() { - m.insert(&tx_in.input, to_bignum(i as u64)); - } - m - }); - let mut scripts = PlutusWitnesses::new(); - self.required_witnesses.scripts - .iter() - .flat_map(|x| x.1) - .for_each(|(hash, option)| { - if let Some(ScriptWitnessType::PlutusScriptWitness(s)) = option { - if let Some(idx) = script_hash_index_map.get(&hash) { - scripts.add(&s.clone_with_redeemer_index_and_tag(&idx, &tag)); - } - } - }); - if scripts.len() > 0 { - Some(scripts) - } else { - None - } - } - - pub(crate) fn has_plutus_scripts(&self) -> bool { - self.required_witnesses.scripts.values() - .any(|x| x.iter() - .any(|(_, w)| - matches!(w, Some(ScriptWitnessType::PlutusScriptWitness(_))))) - } - - pub(crate) fn iter(&self) -> impl std::iter::Iterator + '_ { - self.inputs.values().map(|(i, _)| i) - } - - pub fn len(&self) -> usize { - self.inputs.len() - } - - pub fn add_required_signer(&mut self, key: &Ed25519KeyHash) { - self.required_witnesses.vkeys.insert(key.clone()); - } - - pub fn add_required_signers(&mut self, keys: &RequiredSigners) { - keys.0.iter().for_each(|k| self.add_required_signer(k)); - } - - pub fn total_value(&self) -> Result { - let mut res = Value::zero(); - for (inp, _) in self.inputs.values() { - res = res.checked_add(&inp.amount)?; - } - Ok(res) - } - - pub fn inputs(&self) -> TransactionInputs { - TransactionInputs( - self.inputs - .values() - .map(|(ref tx_builder_input, _)| tx_builder_input.input.clone()) - .collect(), - ) - } - - pub fn inputs_option(&self) -> Option { - if self.len() > 0 { - Some(self.inputs()) - } else { - None - } - } - - fn insert_input_with_witness(&mut self, script_hash: &ScriptHash, input: &TransactionInput, witness: &ScriptWitnessType) { - let script_inputs = - self.required_witnesses.scripts.entry(script_hash.clone()).or_insert(LinkedHashMap::new()); - script_inputs.insert(input.clone(), Some(witness.clone())); - } - - fn insert_input_with_empty_witness(&mut self, script_hash: &ScriptHash, input: &TransactionInput) { - let script_inputs = - self.required_witnesses.scripts.entry(script_hash.clone()).or_insert(LinkedHashMap::new()); - script_inputs.insert(input.clone(), None); - } -} - -impl From<&TxInputsBuilder> for RequiredSignersSet { - fn from(inputs: &TxInputsBuilder) -> Self { - let mut set = inputs.required_witnesses.vkeys.clone(); - inputs - .required_witnesses - .scripts - .values() - .flat_map(|tx_wits| tx_wits.values()) - .for_each(|swt: &Option| { - if let Some(ScriptWitnessType::NativeScriptWitness(script_source)) = swt { - set.extend(script_source.required_signers()); - } - }); - set - } -} diff --git a/rust/src/tx_builder_constants.rs b/rust/src/tx_builder_constants.rs deleted file mode 100644 index ad25f860..00000000 --- a/rust/src/tx_builder_constants.rs +++ /dev/null @@ -1,286 +0,0 @@ -use super::*; - -// The first element is the cost model, which is an array of 166 operations costs, ordered by asc operaion names. -// The second value is the pre-calculated `language_views_encoding` value required for the script hash creation. -// The cost-model values are taken from the genesis block - https://github.com/input-output-hk/cardano-node/blob/master/configuration/cardano/mainnet-alonzo-genesis.json#L26-L195 -// The keys on the genesis block object (operation names) are sorted plain alphabetically. - -#[wasm_bindgen] -pub struct TxBuilderConstants(); - -#[wasm_bindgen] -impl TxBuilderConstants { - pub fn plutus_default_cost_models() -> Costmdls { - TxBuilderConstants::plutus_vasil_cost_models() - } - - pub fn plutus_alonzo_cost_models() -> Costmdls { - let mut res = Costmdls::new(); - res.insert( - &Language::new_plutus_v1(), - &CostModel::from(vec![ - 197209, 0, 1, 1, 396231, 621, 0, 1, 150000, 1000, 0, 1, 150000, 32, 2477736, 29175, - 4, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 29773, 100, 100, - 100, 29773, 100, 150000, 32, 150000, 32, 150000, 32, 150000, 1000, 0, 1, 150000, - 32, 150000, 1000, 0, 8, 148000, 425507, 118, 0, 1, 1, 150000, 1000, 0, 8, 150000, - 112536, 247, 1, 150000, 10000, 1, 136542, 1326, 1, 1000, 150000, 1000, 1, 150000, - 32, 150000, 32, 150000, 32, 1, 1, 150000, 1, 150000, 4, 103599, 248, 1, 103599, - 248, 1, 145276, 1366, 1, 179690, 497, 1, 150000, 32, 150000, 32, 150000, 32, - 150000, 32, 150000, 32, 150000, 32, 148000, 425507, 118, 0, 1, 1, 61516, 11218, 0, - 1, 150000, 32, 148000, 425507, 118, 0, 1, 1, 148000, 425507, 118, 0, 1, 1, 2477736, - 29175, 4, 0, 82363, 4, 150000, 5000, 0, 1, 150000, 32, 197209, 0, 1, 1, 150000, 32, - 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 150000, 32, 3345831, 1, - 1, - ]), - ); - res - } - - pub fn plutus_vasil_cost_models() -> Costmdls { - let mut res = Costmdls::new(); - res.insert( - &Language::new_plutus_v1(), - &CostModel::from(vec![ - 205665, 812, 1, 1, 1000, 571, 0, 1, 1000, 24177, 4, 1, 1000, 32, 117366, 10475, 4, - 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 23000, 100, 100, 100, - 23000, 100, 19537, 32, 175354, 32, 46417, 4, 221973, 511, 0, 1, 89141, 32, 497525, - 14068, 4, 2, 196500, 453240, 220, 0, 1, 1, 1000, 28662, 4, 2, 245000, 216773, 62, - 1, 1060367, 12586, 1, 208512, 421, 1, 187000, 1000, 52998, 1, 80436, 32, 43249, 32, - 1000, 32, 80556, 1, 57667, 4, 1000, 10, 197145, 156, 1, 197145, 156, 1, 204924, - 473, 1, 208896, 511, 1, 52467, 32, 64832, 32, 65493, 32, 22558, 32, 16563, 32, - 76511, 32, 196500, 453240, 220, 0, 1, 1, 69522, 11687, 0, 1, 60091, 32, 196500, - 453240, 220, 0, 1, 1, 196500, 453240, 220, 0, 1, 1, 806990, 30482, 4, 1927926, - 82523, 4, 265318, 0, 4, 0, 85931, 32, 205665, 812, 1, 1, 41182, 32, 212342, 32, - 31220, 32, 32696, 32, 43357, 32, 32247, 32, 38314, 32, 57996947, 18975, 10, - ]), - ); - res.insert( - &Language::new_plutus_v2(), - &CostModel::from(vec![ - 205665, - 812, - 1, - 1, - 1000, - 571, - 0, - 1, - 1000, - 24177, - 4, - 1, - 1000, - 32, - 117366, - 10475, - 4, - 23000, - 100, - 23000, - 100, - 23000, - 100, - 23000, - 100, - 23000, - 100, - 23000, - 100, - 100, - 100, - 23000, - 100, - 19537, - 32, - 175354, - 32, - 46417, - 4, - 221973, - 511, - 0, - 1, - 89141, - 32, - 497525, - 14068, - 4, - 2, - 196500, - 453240, - 220, - 0, - 1, - 1, - 1000, - 28662, - 4, - 2, - 245000, - 216773, - 62, - 1, - 1060367, - 12586, - 1, - 208512, - 421, - 1, - 187000, - 1000, - 52998, - 1, - 80436, - 32, - 43249, - 32, - 1000, - 32, - 80556, - 1, - 57667, - 4, - 1000, - 10, - 197145, - 156, - 1, - 197145, - 156, - 1, - 204924, - 473, - 1, - 208896, - 511, - 1, - 52467, - 32, - 64832, - 32, - 65493, - 32, - 22558, - 32, - 16563, - 32, - 76511, - 32, - 196500, - 453240, - 220, - 0, - 1, - 1, - 69522, - 11687, - 0, - 1, - 60091, - 32, - 196500, - 453240, - 220, - 0, - 1, - 1, - 196500, - 453240, - 220, - 0, - 1, - 1, - 1159724, - 392670, - 0, - 2, - 806990, - 30482, - 4, - 1927926, - 82523, - 4, - 265318, - 0, - 4, - 0, - 85931, - 32, - 205665, - 812, - 1, - 1, - 41182, - 32, - 212342, - 32, - 31220, - 32, - 32696, - 32, - 43357, - 32, - 32247, - 32, - 38314, - 32, - 35892428, - 10, - 57996947, - 18975, - 10, - 38887044, - 32947, - 10 - ]), - ); - res - } -} - -#[cfg(test)] -mod tests { - use crate::tx_builder_constants::*; - - #[test] - pub fn cost_model_test() { - assert_eq!( - TxBuilderConstants::plutus_alonzo_cost_models() - .get(&Language::new_plutus_v1()) - .unwrap() - .len(), - 166, - ); - assert_eq!( - TxBuilderConstants::plutus_vasil_cost_models() - .get(&Language::new_plutus_v1()) - .unwrap() - .len(), - 166, - ); - assert_eq!( - TxBuilderConstants::plutus_vasil_cost_models() - .get(&Language::new_plutus_v2()) - .unwrap() - .len(), - 175, - ); - assert_eq!( - hex::encode(TxBuilderConstants::plutus_alonzo_cost_models().language_views_encoding()), - "a141005901d59f1a000302590001011a00060bc719026d00011a000249f01903e800011a000249f018201a0025cea81971f70419744d186419744d186419744d186419744d186419744d186419744d18641864186419744d18641a000249f018201a000249f018201a000249f018201a000249f01903e800011a000249f018201a000249f01903e800081a000242201a00067e2318760001011a000249f01903e800081a000249f01a0001b79818f7011a000249f0192710011a0002155e19052e011903e81a000249f01903e8011a000249f018201a000249f018201a000249f0182001011a000249f0011a000249f0041a000194af18f8011a000194af18f8011a0002377c190556011a0002bdea1901f1011a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000242201a00067e23187600010119f04c192bd200011a000249f018201a000242201a00067e2318760001011a000242201a00067e2318760001011a0025cea81971f704001a000141bb041a000249f019138800011a000249f018201a000302590001011a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a000249f018201a00330da70101ff", - ); - assert_eq!( - hex::encode(TxBuilderConstants::plutus_vasil_cost_models().language_views_encoding()), - "a20198af1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a0011b22c1a0005fdde00021a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201a0223accc0a1a0374f693194a1f0a1a02515e841980b30a41005901b69f1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201a0374f693194a1f0aff", - ); - } - - #[test] - pub fn from_json() { - println!("{}", TxBuilderConstants::plutus_vasil_cost_models().to_json().unwrap()); - assert_eq!( - TxBuilderConstants::plutus_vasil_cost_models().to_hex(), - Costmdls::from_json("{ \"PlutusV1\": [\"205665\",\"812\",\"1\",\"1\",\"1000\",\"571\",\"0\",\"1\",\"1000\",\"24177\",\"4\",\"1\",\"1000\",\"32\",\"117366\",\"10475\",\"4\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"100\",\"100\",\"23000\",\"100\",\"19537\",\"32\",\"175354\",\"32\",\"46417\",\"4\",\"221973\",\"511\",\"0\",\"1\",\"89141\",\"32\",\"497525\",\"14068\",\"4\",\"2\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"1000\",\"28662\",\"4\",\"2\",\"245000\",\"216773\",\"62\",\"1\",\"1060367\",\"12586\",\"1\",\"208512\",\"421\",\"1\",\"187000\",\"1000\",\"52998\",\"1\",\"80436\",\"32\",\"43249\",\"32\",\"1000\",\"32\",\"80556\",\"1\",\"57667\",\"4\",\"1000\",\"10\",\"197145\",\"156\",\"1\",\"197145\",\"156\",\"1\",\"204924\",\"473\",\"1\",\"208896\",\"511\",\"1\",\"52467\",\"32\",\"64832\",\"32\",\"65493\",\"32\",\"22558\",\"32\",\"16563\",\"32\",\"76511\",\"32\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"69522\",\"11687\",\"0\",\"1\",\"60091\",\"32\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"806990\",\"30482\",\"4\",\"1927926\",\"82523\",\"4\",\"265318\",\"0\",\"4\",\"0\",\"85931\",\"32\",\"205665\",\"812\",\"1\",\"1\",\"41182\",\"32\",\"212342\",\"32\",\"31220\",\"32\",\"32696\",\"32\",\"43357\",\"32\",\"32247\",\"32\",\"38314\",\"32\",\"57996947\",\"18975\",\"10\"], \"PlutusV2\": [\"205665\",\"812\",\"1\",\"1\",\"1000\",\"571\",\"0\",\"1\",\"1000\",\"24177\",\"4\",\"1\",\"1000\",\"32\",\"117366\",\"10475\",\"4\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"23000\",\"100\",\"100\",\"100\",\"23000\",\"100\",\"19537\",\"32\",\"175354\",\"32\",\"46417\",\"4\",\"221973\",\"511\",\"0\",\"1\",\"89141\",\"32\",\"497525\",\"14068\",\"4\",\"2\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"1000\",\"28662\",\"4\",\"2\",\"245000\",\"216773\",\"62\",\"1\",\"1060367\",\"12586\",\"1\",\"208512\",\"421\",\"1\",\"187000\",\"1000\",\"52998\",\"1\",\"80436\",\"32\",\"43249\",\"32\",\"1000\",\"32\",\"80556\",\"1\",\"57667\",\"4\",\"1000\",\"10\",\"197145\",\"156\",\"1\",\"197145\",\"156\",\"1\",\"204924\",\"473\",\"1\",\"208896\",\"511\",\"1\",\"52467\",\"32\",\"64832\",\"32\",\"65493\",\"32\",\"22558\",\"32\",\"16563\",\"32\",\"76511\",\"32\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"69522\",\"11687\",\"0\",\"1\",\"60091\",\"32\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"196500\",\"453240\",\"220\",\"0\",\"1\",\"1\",\"1159724\",\"392670\",\"0\",\"2\",\"806990\",\"30482\",\"4\",\"1927926\",\"82523\",\"4\",\"265318\",\"0\",\"4\",\"0\",\"85931\",\"32\",\"205665\",\"812\",\"1\",\"1\",\"41182\",\"32\",\"212342\",\"32\",\"31220\",\"32\",\"32696\",\"32\",\"43357\",\"32\",\"32247\",\"32\",\"38314\",\"32\",\"35892428\",\"10\",\"57996947\",\"18975\",\"10\",\"38887044\",\"32947\",\"10\"] }").unwrap().to_hex(), - ); - } -} diff --git a/rust/src/utils.rs b/rust/src/utils.rs index 1272505c..1b0804cf 100644 --- a/rust/src/utils.rs +++ b/rust/src/utils.rs @@ -4,19 +4,15 @@ use cbor_event::{ se::{Serialize, Serializer}, }; use hex::FromHex; -use num_bigint::Sign; use serde_json; -use std::convert::TryFrom; -use std::ops::Div; +use std::fmt::Display; use std::{ collections::HashMap, io::{BufRead, Seek, Write}, }; -use std::fmt::Display; use super::*; use crate::error::{DeserializeError, DeserializeFailure}; -use crate::fakes::fake_data_hash; use schemars::JsonSchema; pub fn to_bytes(data_item: &T) -> Vec { @@ -30,10 +26,8 @@ pub fn from_bytes(data: &Vec) -> Result T::deserialize(&mut raw) } - - #[wasm_bindgen] -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, JsonSchema,)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, JsonSchema)] pub struct TransactionUnspentOutput { pub(crate) input: TransactionInput, pub(crate) output: TransactionOutput, @@ -79,19 +73,19 @@ impl Deserialize for TransactionUnspentOutput { let input = (|| -> Result<_, DeserializeError> { Ok(TransactionInput::deserialize(raw)?) })() - .map_err(|e| e.annotate("input"))?; + .map_err(|e| e.annotate("input"))?; let output = (|| -> Result<_, DeserializeError> { Ok(TransactionOutput::deserialize(raw)?) })() - .map_err(|e| e.annotate("output"))?; + .map_err(|e| e.annotate("output"))?; let ret = Ok(Self { input, output }); match len { cbor_event::Len::Len(n) => match n { 2 => /* it's ok */ - { - () - } + { + () + } n => { return Err( DeserializeFailure::DefiniteLenMismatch(n, Some(2)).into() @@ -101,9 +95,9 @@ impl Deserialize for TransactionUnspentOutput { cbor_event::Len::Indefinite => match raw.special()? { CBORSpecial::Break => /* it's ok */ - { - () - } + { + () + } _ => return Err(DeserializeFailure::EndingBreakMissing.into()), }, } @@ -112,12 +106,12 @@ impl Deserialize for TransactionUnspentOutput { _ => Err(DeserializeFailure::NoVariantMatched.into()), } })() - .map_err(|e| e.annotate("TransactionUnspentOutput")) + .map_err(|e| e.annotate("TransactionUnspentOutput")) } } #[wasm_bindgen] -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, JsonSchema,)] +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, JsonSchema)] pub struct TransactionUnspentOutputs(pub(crate) Vec); to_from_json!(TransactionUnspentOutputs); @@ -150,231 +144,16 @@ impl<'a> IntoIterator for &'a TransactionUnspentOutputs { } } -// Generic u64 wrapper for platforms that don't support u64 or BigInt/etc -// This is an unsigned type - no negative numbers. -// Can be converted to/from plain rust -#[wasm_bindgen] -#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct BigNum(u64); - -impl_to_from!(BigNum); - -impl std::fmt::Display for BigNum { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", self.0) - } -} - -#[wasm_bindgen] -impl BigNum { - // Create a BigNum from a standard rust string representation - pub fn from_str(string: &str) -> Result { - string - .parse::() - .map_err(|e| JsError::from_str(&format! {"{:?}", e})) - .map(BigNum) - } - - // String representation of the BigNum value for use from environments that don't support BigInt - pub fn to_str(&self) -> String { - format!("{}", self.0) - } - - pub fn zero() -> Self { - Self(0) - } - - pub fn one() -> Self { - Self(1) - } - - pub fn is_zero(&self) -> bool { - self.0 == 0 - } - - pub fn div_floor(&self, other: &BigNum) -> BigNum { - // same as (a / b) - let res = self.0.div(&other.0); - Self(res) - } - - pub fn checked_mul(&self, other: &BigNum) -> Result { - match self.0.checked_mul(other.0) { - Some(value) => Ok(BigNum(value)), - None => Err(JsError::from_str("overflow")), - } - } - - pub fn checked_add(&self, other: &BigNum) -> Result { - match self.0.checked_add(other.0) { - Some(value) => Ok(BigNum(value)), - None => Err(JsError::from_str("overflow")), - } - } - - pub fn checked_sub(&self, other: &BigNum) -> Result { - match self.0.checked_sub(other.0) { - Some(value) => Ok(BigNum(value)), - None => Err(JsError::from_str("underflow")), - } - } - - /// returns 0 if it would otherwise underflow - pub fn clamped_sub(&self, other: &BigNum) -> BigNum { - match self.0.checked_sub(other.0) { - Some(value) => BigNum(value), - None => BigNum(0), - } - } - - pub fn compare(&self, rhs_value: &BigNum) -> i8 { - match self.cmp(&rhs_value) { - std::cmp::Ordering::Equal => 0, - std::cmp::Ordering::Less => -1, - std::cmp::Ordering::Greater => 1, - } - } - - pub fn less_than(&self, rhs_value: &BigNum) -> bool { - self.compare(rhs_value) < 0 - } - - pub fn max_value() -> BigNum { - BigNum(u64::max_value()) - } - - pub fn max(a: &BigNum, b: &BigNum) -> BigNum { - if a.less_than(b) { b.clone() } else { a.clone() } - } -} - -impl TryFrom for u32 { - type Error = JsError; - - fn try_from(value: BigNum) -> Result { - if value.0 > u32::MAX.into() { - Err(JsError::from_str(&format!( - "Value {} is bigger than max u32 {}", - value.0, - u32::MAX - ))) - } else { - Ok(value.0 as u32) - } - } -} - -impl From for u64 { - - fn from(value: BigNum) -> Self { - value.0 - } -} - -impl From for usize { - - fn from(value: BigNum) -> Self { - value.0 as usize - } -} - -impl From for BigNum { - fn from(value: u64) -> Self { - return BigNum(value); - } -} - -impl From for BigNum { - fn from(value: usize) -> Self { - return BigNum(value as u64); - } -} - -impl From for BigNum { - fn from(value: u32) -> Self { - return BigNum(value.into()); - } -} - -impl cbor_event::se::Serialize for BigNum { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - serializer.write_unsigned_integer(self.0) - } -} - -impl Deserialize for BigNum { - fn deserialize(raw: &mut Deserializer) -> Result { - match raw.unsigned_integer() { - Ok(value) => Ok(Self(value)), - Err(e) => Err(DeserializeError::new("BigNum", DeserializeFailure::CBOR(e))), - } - } -} - -impl serde::Serialize for BigNum { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_str()) - } -} - -impl<'de> serde::de::Deserialize<'de> for BigNum { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let s = ::deserialize(deserializer)?; - Self::from_str(&s).map_err(|_e| { - serde::de::Error::invalid_value( - serde::de::Unexpected::Str(&s), - &"string rep of a number", - ) - }) - } -} - -impl JsonSchema for BigNum { - fn schema_name() -> String { - String::from("BigNum") - } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - String::json_schema(gen) - } - fn is_referenceable() -> bool { - String::is_referenceable() - } -} - -pub fn to_bignum(val: u64) -> BigNum { - BigNum(val) -} - -pub fn from_bignum(val: &BigNum) -> u64 { - val.0 -} - -pub fn to_bigint(val: u64) -> BigInt { - BigInt::from_str(&val.to_string()).unwrap() -} - -// Specifies an amount of ADA in terms of lovelace -pub type Coin = BigNum; - #[wasm_bindgen] #[derive( -Clone, -Debug, -Eq, -/*Hash,*/ Ord, -PartialEq, -serde::Serialize, -serde::Deserialize, -JsonSchema, + Clone, + Debug, + Eq, + /*Hash,*/ Ord, + PartialEq, + serde::Serialize, + serde::Deserialize, + JsonSchema, )] pub struct Value { pub(crate) coin: Coin, @@ -413,10 +192,10 @@ impl Value { pub fn is_zero(&self) -> bool { self.coin.is_zero() && self - .multiasset - .as_ref() - .map(|m| m.len() == 0) - .unwrap_or(true) + .multiasset + .as_ref() + .map(|m| m.len() == 0) + .unwrap_or(true) } pub fn coin(&self) -> Coin { @@ -555,14 +334,22 @@ impl cbor_event::se::Serialize for Value { &self, serializer: &'se mut Serializer, ) -> cbor_event::Result<&'se mut Serializer> { - match &self.multiasset { - Some(multiasset) => { - serializer.write_array(cbor_event::Len::Len(2))?; - self.coin.serialize(serializer)?; - multiasset.serialize(serializer) + let multiasset_len = match &self.multiasset { + Some(multiasset) => multiasset.len(), + None => 0, + }; + + if multiasset_len == 0 { + self.coin.serialize(serializer)?; + } else { + serializer.write_array(cbor_event::Len::Len(2))?; + self.coin.serialize(serializer)?; + if let Some(multiasset) = &self.multiasset { + multiasset.serialize(serializer)?; } - None => self.coin.serialize(serializer), } + + Ok(serializer) } } @@ -587,9 +374,9 @@ impl Deserialize for Value { cbor_event::Len::Len(n) => match n { 2 => /* it's ok */ - { - () - } + { + () + } n => { return Err( DeserializeFailure::DefiniteLenMismatch(n, Some(2)).into() @@ -599,9 +386,9 @@ impl Deserialize for Value { cbor_event::Len::Indefinite => match raw.special()? { CBORSpecial::Break => /* it's ok */ - { - () - } + { + () + } _ => return Err(DeserializeFailure::EndingBreakMissing.into()), }, } @@ -610,193 +397,11 @@ impl Deserialize for Value { _ => Err(DeserializeFailure::NoVariantMatched.into()), } })() - .map_err(|e| e.annotate("Value")) - } -} - -// CBOR has int = uint / nint -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct Int(pub(crate) i128); - -impl_to_from!(Int); - -#[wasm_bindgen] -impl Int { - pub fn new(x: &BigNum) -> Self { - Self(x.0 as i128) - } - - pub fn new_negative(x: &BigNum) -> Self { - Self(-(x.0 as i128)) - } - - pub fn new_i32(x: i32) -> Self { - Self(x as i128) - } - - pub fn is_positive(&self) -> bool { - return self.0 >= 0; - } - - /// BigNum can only contain unsigned u64 values - /// - /// This function will return the BigNum representation - /// only in case the underlying i128 value is positive. - /// - /// Otherwise nothing will be returned (undefined). - pub fn as_positive(&self) -> Option { - if self.is_positive() { - Some(to_bignum(self.0 as u64)) - } else { - None - } - } - - /// BigNum can only contain unsigned u64 values - /// - /// This function will return the *absolute* BigNum representation - /// only in case the underlying i128 value is negative. - /// - /// Otherwise nothing will be returned (undefined). - pub fn as_negative(&self) -> Option { - if !self.is_positive() { - Some(to_bignum((-self.0) as u64)) - } else { - None - } - } - - /// !!! DEPRECATED !!! - /// Returns an i32 value in case the underlying original i128 value is within the limits. - /// Otherwise will just return an empty value (undefined). - #[deprecated( - since = "10.0.0", - note = "Unsafe ignoring of possible boundary error and it's not clear from the function name. Use `as_i32_or_nothing`, `as_i32_or_fail`, or `to_str`" - )] - pub fn as_i32(&self) -> Option { - self.as_i32_or_nothing() - } - - /// Returns the underlying value converted to i32 if possible (within limits) - /// Otherwise will just return an empty value (undefined). - pub fn as_i32_or_nothing(&self) -> Option { - use std::convert::TryFrom; - i32::try_from(self.0).ok() - } - - /// Returns the underlying value converted to i32 if possible (within limits) - /// JsError in case of out of boundary overflow - pub fn as_i32_or_fail(&self) -> Result { - use std::convert::TryFrom; - i32::try_from(self.0).map_err(|e| JsError::from_str(&format!("{}", e))) - } - - /// Returns string representation of the underlying i128 value directly. - /// Might contain the minus sign (-) in case of negative value. - pub fn to_str(&self) -> String { - format!("{}", self.0) - } - - // Create an Int from a standard rust string representation - pub fn from_str(string: &str) -> Result { - let x = string - .parse::() - .map_err(|e| JsError::from_str(&format! {"{:?}", e}))?; - if x.abs() > u64::MAX as i128 { - return Err(JsError::from_str(&format!( - "{} out of bounds. Value (without sign) must fit within 4 bytes limit of {}", - x, - u64::MAX - ))); - } - Ok(Self(x)) - } -} - -impl cbor_event::se::Serialize for Int { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - if self.0 < 0 { - serializer.write_negative_integer(self.0 as i64) - } else { - serializer.write_unsigned_integer(self.0 as u64) - } - } -} - -impl Deserialize for Int { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - match raw.cbor_type()? { - cbor_event::Type::UnsignedInteger => Ok(Self(raw.unsigned_integer()? as i128)), - cbor_event::Type::NegativeInteger => Ok(Self(read_nint(raw)?)), - _ => Err(DeserializeFailure::NoVariantMatched.into()), - } - })() - .map_err(|e| e.annotate("Int")) - } -} - -impl serde::Serialize for Int { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_str()) + .map_err(|e| e.annotate("Value")) } } -impl<'de> serde::de::Deserialize<'de> for Int { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let s = ::deserialize(deserializer)?; - Self::from_str(&s).map_err(|_e| { - serde::de::Error::invalid_value( - serde::de::Unexpected::Str(&s), - &"string rep of a number", - ) - }) - } -} - -impl JsonSchema for Int { - fn schema_name() -> String { - String::from("Int") - } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - String::json_schema(gen) - } - fn is_referenceable() -> bool { - String::is_referenceable() - } -} - -/// TODO: this function can be removed in case `cbor_event` library ever gets a fix on their side -/// See https://github.com/Emurgo/cardano-serialization-lib/pull/392 -fn read_nint(raw: &mut Deserializer) -> Result { - let found = raw.cbor_type()?; - if found != cbor_event::Type::NegativeInteger { - return Err(cbor_event::Error::Expected(cbor_event::Type::NegativeInteger, found).into()); - } - let (len, len_sz) = raw.cbor_len()?; - match len { - cbor_event::Len::Indefinite => Err(cbor_event::Error::IndefiniteLenNotSupported( - cbor_event::Type::NegativeInteger, - ) - .into()), - cbor_event::Len::Len(v) => { - raw.advance(1 + len_sz)?; - Ok(-(v as i128) - 1) - } - } -} - -const BOUNDED_BYTES_CHUNK_SIZE: usize = 64; +pub(crate) const BOUNDED_BYTES_CHUNK_SIZE: usize = 64; pub(crate) fn write_bounded_bytes<'se, W: Write>( serializer: &'se mut Serializer, @@ -832,7 +437,7 @@ pub(crate) fn read_bounded_bytes( max: BOUNDED_BYTES_CHUNK_SIZE, found: bytes.len(), } - .into()); + .into()); } Ok(bytes) } @@ -859,7 +464,7 @@ pub(crate) fn read_bounded_bytes( return Err(cbor_event::Error::CustomError(String::from( "Illegal CBOR: Indefinite string found inside indefinite string", )) - .into()); + .into()); } cbor_event::Len::Len(len) => { if len as usize > BOUNDED_BYTES_CHUNK_SIZE { @@ -868,7 +473,7 @@ pub(crate) fn read_bounded_bytes( max: BOUNDED_BYTES_CHUNK_SIZE, found: len as usize, } - .into()); + .into()); } raw.advance(1 + chunk_len_sz)?; raw.as_mut_ref() @@ -887,270 +492,6 @@ pub(crate) fn read_bounded_bytes( } } -#[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] -pub struct BigInt(num_bigint::BigInt); - -impl_to_from!(BigInt); - -impl serde::Serialize for BigInt { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_str()) - } -} - -impl<'de> serde::de::Deserialize<'de> for BigInt { - fn deserialize(deserializer: D) -> Result - where - D: serde::de::Deserializer<'de>, - { - let s = ::deserialize(deserializer)?; - BigInt::from_str(&s).map_err(|_e| { - serde::de::Error::invalid_value( - serde::de::Unexpected::Str(&s), - &"string rep of a big int", - ) - }) - } -} - -impl JsonSchema for BigInt { - fn schema_name() -> String { - String::from("BigInt") - } - fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema { - String::json_schema(gen) - } - fn is_referenceable() -> bool { - String::is_referenceable() - } -} - -#[wasm_bindgen] -impl BigInt { - pub fn is_zero(&self) -> bool { - self.0.sign() == Sign::NoSign - } - - pub fn as_u64(&self) -> Option { - let (sign, u64_digits) = self.0.to_u64_digits(); - if sign == num_bigint::Sign::Minus { - return None; - } - match u64_digits.len() { - 0 => Some(to_bignum(0)), - 1 => Some(to_bignum(*u64_digits.first().unwrap())), - _ => None, - } - } - - pub fn as_int(&self) -> Option { - let (sign, u64_digits) = self.0.to_u64_digits(); - let u64_digit = match u64_digits.len() { - 0 => Some(to_bignum(0)), - 1 => Some(to_bignum(*u64_digits.first().unwrap())), - _ => None, - }?; - match sign { - num_bigint::Sign::NoSign | num_bigint::Sign::Plus => Some(Int::new(&u64_digit)), - num_bigint::Sign::Minus => Some(Int::new_negative(&u64_digit)), - } - } - - pub fn from_str(text: &str) -> Result { - use std::str::FromStr; - num_bigint::BigInt::from_str(text) - .map_err(|e| JsError::from_str(&format! {"{:?}", e})) - .map(Self) - } - - pub fn to_str(&self) -> String { - self.0.to_string() - } - - pub fn add(&self, other: &BigInt) -> BigInt { - Self(&self.0 + &other.0) - } - - pub fn mul(&self, other: &BigInt) -> BigInt { - Self(&self.0 * &other.0) - } - - pub fn one() -> BigInt { - use std::str::FromStr; - Self(num_bigint::BigInt::from_str("1").unwrap()) - } - - pub fn increment(&self) -> BigInt { - self.add(&Self::one()) - } - - pub fn div_ceil(&self, other: &BigInt) -> BigInt { - use num_integer::Integer; - let (res, rem) = self.0.div_rem(&other.0); - let result = Self(res); - if Self(rem).is_zero() { - result - } else { - result.increment() - } - } -} - -impl cbor_event::se::Serialize for BigInt { - fn serialize<'se, W: Write>( - &self, - serializer: &'se mut Serializer, - ) -> cbor_event::Result<&'se mut Serializer> { - let (sign, u64_digits) = self.0.to_u64_digits(); - match u64_digits.len() { - 0 => serializer.write_unsigned_integer(0), - // we use the uint/nint encodings to use a minimum of space - 1 => match sign { - // uint - num_bigint::Sign::Plus | num_bigint::Sign::NoSign => { - serializer.write_unsigned_integer(*u64_digits.first().unwrap()) - } - // nint - num_bigint::Sign::Minus => serializer - .write_negative_integer(-(*u64_digits.first().unwrap() as i128) as i64), - }, - _ => { - // Small edge case: nint's minimum is -18446744073709551616 but in this bigint lib - // that takes 2 u64 bytes so we put that as a special case here: - if sign == num_bigint::Sign::Minus && u64_digits == vec![0, 1] { - serializer.write_negative_integer(-18446744073709551616i128 as i64) - } else { - let (sign, bytes) = self.0.to_bytes_be(); - match sign { - // positive bigint - num_bigint::Sign::Plus | num_bigint::Sign::NoSign => { - serializer.write_tag(2u64)?; - write_bounded_bytes(serializer, &bytes) - } - // negative bigint - num_bigint::Sign::Minus => { - serializer.write_tag(3u64)?; - use std::ops::Neg; - // CBOR RFC defines this as the bytes of -n -1 - let adjusted = self - .0 - .clone() - .neg() - .checked_sub(&num_bigint::BigInt::from(1u32)) - .unwrap() - .to_biguint() - .unwrap(); - write_bounded_bytes(serializer, &adjusted.to_bytes_be()) - } - } - } - } - } - } -} - -impl Deserialize for BigInt { - fn deserialize(raw: &mut Deserializer) -> Result { - (|| -> Result<_, DeserializeError> { - match raw.cbor_type()? { - // bigint - CBORType::Tag => { - let tag = raw.tag()?; - let bytes = read_bounded_bytes(raw)?; - match tag { - // positive bigint - 2 => Ok(Self(num_bigint::BigInt::from_bytes_be( - num_bigint::Sign::Plus, - &bytes, - ))), - // negative bigint - 3 => { - // CBOR RFC defines this as the bytes of -n -1 - let initial = - num_bigint::BigInt::from_bytes_be(num_bigint::Sign::Plus, &bytes); - use std::ops::Neg; - let adjusted = initial - .checked_add(&num_bigint::BigInt::from(1u32)) - .unwrap() - .neg(); - Ok(Self(adjusted)) - } - _ => { - return Err(DeserializeFailure::TagMismatch { - found: tag, - expected: 2, - } - .into()); - } - } - } - // uint - CBORType::UnsignedInteger => { - Ok(Self(num_bigint::BigInt::from(raw.unsigned_integer()?))) - } - // nint - CBORType::NegativeInteger => Ok(Self(num_bigint::BigInt::from(read_nint(raw)?))), - _ => return Err(DeserializeFailure::NoVariantMatched.into()), - } - })() - .map_err(|e| e.annotate("BigInt")) - } -} - -impl std::convert::From for BigInt - where - T: std::convert::Into, -{ - fn from(x: T) -> Self { - Self(x.into()) - } -} - -impl From for BigInt - where -{ - fn from(x: BigNum) -> Self { - Self(x.0.into()) - } -} - -// we use the cbor_event::Serialize trait directly - -// This is only for use for plain cddl groups who need to be embedded within outer groups. -pub(crate) trait SerializeEmbeddedGroup { - fn serialize_as_embedded_group<'a, W: Write + Sized>( - &self, - serializer: &'a mut Serializer, - ) -> cbor_event::Result<&'a mut Serializer>; -} - -// same as cbor_event::de::Deserialize but with our DeserializeError -pub trait Deserialize { - fn deserialize(raw: &mut Deserializer) -> Result - where - Self: Sized; -} - -// auto-implement for all cbor_event Deserialize implementors -impl Deserialize for T { - fn deserialize(raw: &mut Deserializer) -> Result { - T::deserialize(raw).map_err(|e| DeserializeError::from(e)) - } -} - -// This is only for use for plain cddl groups who need to be embedded within outer groups. -pub trait DeserializeEmbeddedGroup { - fn deserialize_as_embedded_group( - raw: &mut Deserializer, - len: cbor_event::Len, - ) -> Result - where - Self: Sized; -} pub struct CBORReadLen { deser_len: cbor_event::Len, @@ -1264,7 +605,7 @@ pub fn hash_script_data( */ buf.push(0x80); if let Some(d) = &datums { - buf.extend(d.to_bytes()); + buf.extend(d.to_set_bytes()); } buf.push(0xA0); } else { @@ -1277,7 +618,7 @@ pub fn hash_script_data( */ buf.extend(redeemers.to_bytes()); if let Some(d) = &datums { - buf.extend(d.to_bytes()); + buf.extend(d.to_set_bytes()); } buf.extend(cost_models.language_views_encoding()); } @@ -1292,22 +633,29 @@ pub fn internal_get_implicit_input( key_deposit: &BigNum, // protocol parameter ) -> Result { let withdrawal_sum = match &withdrawals { - None => to_bignum(0), + None => BigNum::zero(), Some(x) => { x.0.values() - .try_fold(to_bignum(0), |acc, ref withdrawal_amt| { + .try_fold(BigNum::zero(), |acc, ref withdrawal_amt| { acc.checked_add(&withdrawal_amt) })? } }; let certificate_refund = match &certs { - None => to_bignum(0), + None => BigNum::zero(), Some(certs) => certs - .0 + .certs .iter() - .try_fold(to_bignum(0), |acc, ref cert| match &cert.0 { - CertificateEnum::PoolRetirement(_cert) => acc.checked_add(&pool_deposit), - CertificateEnum::StakeDeregistration(_cert) => acc.checked_add(&key_deposit), + .try_fold(BigNum::zero(), |acc, ref cert| match &cert.0 { + CertificateEnum::StakeDeregistration(cert) => { + if let Some(coin) = cert.coin { + acc.checked_add(&coin) + } else { + acc.checked_add(&key_deposit) + } + } + CertificateEnum::PoolRetirement(_) => acc.checked_add(&pool_deposit), + CertificateEnum::DRepDeregistration(cert) => acc.checked_add(&cert.coin), _ => Ok(acc), })?, }; @@ -1322,18 +670,32 @@ pub fn internal_get_deposit( pool_deposit: &BigNum, // // protocol parameter key_deposit: &BigNum, // protocol parameter ) -> Result { - let certificate_refund = match &certs { - None => to_bignum(0), + let certificate_deposit = match &certs { + None => BigNum::zero(), Some(certs) => certs - .0 + .certs .iter() - .try_fold(to_bignum(0), |acc, ref cert| match &cert.0 { - CertificateEnum::PoolRegistration(_cert) => acc.checked_add(&pool_deposit), - CertificateEnum::StakeRegistration(_cert) => acc.checked_add(&key_deposit), + .try_fold(BigNum::zero(), |acc, ref cert| match &cert.0 { + CertificateEnum::PoolRegistration(_) => acc.checked_add(&pool_deposit), + CertificateEnum::StakeRegistration(cert) => { + if let Some(coin) = cert.coin { + acc.checked_add(&coin) + } else { + acc.checked_add(&key_deposit) + } + } + CertificateEnum::DRepRegistration(cert) => acc.checked_add(&cert.coin), + CertificateEnum::StakeRegistrationAndDelegation(cert) => { + acc.checked_add(&cert.coin) + } + CertificateEnum::VoteRegistrationAndDelegation(cert) => acc.checked_add(&cert.coin), + CertificateEnum::StakeVoteRegistrationAndDelegation(cert) => { + acc.checked_add(&cert.coin) + } _ => Ok(acc), })?, }; - Ok(certificate_refund) + Ok(certificate_deposit) } #[wasm_bindgen] @@ -1410,27 +772,31 @@ impl MinOutputAdaCalculator { return Ok(required_coin); } } - output.amount.coin = to_bignum(u64::MAX); + output.amount.coin = BigNum(u64::MAX); Ok(Self::calc_required_coin(&output, &self.data_cost)?) } fn create_fake_output() -> Result { let fake_base_address: Address = Address::from_bech32("addr_test1qpu5vlrf4xkxv2qpwngf6cjhtw542ayty80v8dyr49rf5ewvxwdrt70qlcpeeagscasafhffqsxy36t90ldv06wqrk2qum8x5w")?; - let fake_value: Value = Value::new(&to_bignum(1000000)); + let fake_value: Value = Value::new(&BigNum(1000000)); Ok(TransactionOutput::new(&fake_base_address, &fake_value)) } pub fn calc_size_cost(data_cost: &DataCost, size: usize) -> Result { //according to https://hydra.iohk.io/build/15339994/download/1/babbage-changes.pdf //See on the page 9 getValue txout - to_bignum(size as u64).checked_add(&to_bignum(160))? + BigNum(size as u64) + .checked_add(&BigNum(160))? .checked_mul(&data_cost.coins_per_byte()) } - pub fn calc_required_coin(output: &TransactionOutput, data_cost: &DataCost) -> Result { + pub fn calc_required_coin( + output: &TransactionOutput, + data_cost: &DataCost, + ) -> Result { //according to https://hydra.iohk.io/build/15339994/download/1/babbage-changes.pdf //See on the page 9 getValue txout - Self::calc_size_cost(data_cost,output.to_bytes().len()) + Self::calc_size_cost(data_cost, output.to_bytes().len()) } } @@ -1443,25 +809,6 @@ pub fn min_ada_for_output( MinOutputAdaCalculator::new(output, data_cost).calculate_ada() } -/// !!! DEPRECATED !!! -/// This function uses outdated set of arguments. -/// Use `min_ada_for_output` instead -#[wasm_bindgen] -#[deprecated(since = "11.0.0", note = "Use `min_ada_for_output` instead")] -pub fn min_ada_required( - assets: &Value, - has_data_hash: bool, // whether the output includes a data hash - coins_per_utxo_word: &BigNum, // protocol parameter (in lovelace) -) -> Result { - let data_cost = DataCost::new_coins_per_word(coins_per_utxo_word); - let mut calc = MinOutputAdaCalculator::new_empty(&data_cost)?; - calc.set_amount(assets); - if has_data_hash { - calc.set_data_hash(&fake_data_hash(0)); - } - calc.calculate_ada() -} - /// Used to choosed the schema for a script JSON string #[wasm_bindgen] pub enum ScriptSchema { @@ -1500,32 +847,32 @@ fn encode_wallet_value_to_native_script( ) -> Result { match value { serde_json::Value::Object(map) - if map.contains_key("cosigners") && map.contains_key("template") => - { - let mut cosigners = HashMap::new(); - - if let serde_json::Value::Object(cosigner_map) = map.get("cosigners").unwrap() { - for (key, value) in cosigner_map.iter() { - if let serde_json::Value::String(xpub) = value { - if xpub == "self" { - cosigners.insert(key.to_owned(), self_xpub.to_owned()); - } else { - cosigners.insert(key.to_owned(), xpub.to_owned()); - } + if map.contains_key("cosigners") && map.contains_key("template") => + { + let mut cosigners = HashMap::new(); + + if let serde_json::Value::Object(cosigner_map) = map.get("cosigners").unwrap() { + for (key, value) in cosigner_map.iter() { + if let serde_json::Value::String(xpub) = value { + if xpub == "self" { + cosigners.insert(key.to_owned(), self_xpub.to_owned()); } else { - return Err(JsError::from_str("cosigner value must be a string")); + cosigners.insert(key.to_owned(), xpub.to_owned()); } + } else { + return Err(JsError::from_str("cosigner value must be a string")); } - } else { - return Err(JsError::from_str("cosigners must be a map")); } + } else { + return Err(JsError::from_str("cosigners must be a map")); + } - let template = map.get("template").unwrap(); + let template = map.get("template").unwrap(); - let template_native_script = encode_template_to_native_script(template, &cosigners)?; + let template_native_script = encode_template_to_native_script(template, &cosigners)?; - Ok(template_native_script) - } + Ok(template_native_script) + } _ => Err(JsError::from_str( "top level must be an object. cosigners and template keys are required", )), @@ -1583,7 +930,7 @@ fn encode_template_to_native_script( if let serde_json::Value::Object(some) = map.get("some").unwrap() { if some.contains_key("at_least") && some.contains_key("from") { let n = if let serde_json::Value::Number(at_least) = - some.get("at_least").unwrap() + some.get("at_least").unwrap() { if let Some(n) = at_least.as_u64() { n as u32 @@ -1657,6 +1004,10 @@ pub(crate) fn opt64(o: &Option) -> u64 { o.is_some() as u64 } +pub(crate) fn opt64_non_empty(o: &Option) -> u64 { + (!o.is_none_or_empty()) as u64 +} + pub struct ValueShortage { pub(crate) ada_shortage: Option<(Coin, Coin, Coin)>, pub(crate) asset_shortage: Vec<(PolicyID, AssetName, Coin, Coin)>, @@ -1666,30 +1017,48 @@ impl Display for ValueShortage { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "shortage: {{")?; if let Some((input_data, out_data, fee)) = self.ada_shortage { - writeln!(f, "ada in inputs: {}, ada in outputs: {}, fee {}", input_data, out_data, fee)?; + writeln!( + f, + "ada in inputs: {}, ada in outputs: {}, fee {}", + input_data, out_data, fee + )?; writeln!(f, "NOTE! \"ada in inputs\" must be >= (\"ada in outputs\" + fee) before adding change")?; - writeln!(f, "and \"ada in inputs\" must be == (\"ada in outputs\" + fee) after adding change")?; - } - for (policy_id, asset_name, asset_shortage, asset_available) in - &self.asset_shortage - { - write!(f, "policy id: \"{}\", asset name: \"{}\" ", policy_id, asset_name)?; - writeln!(f, "coins in inputs: {}, coins in outputs: {}", asset_shortage, asset_available)?; + writeln!( + f, + "and \"ada in inputs\" must be == (\"ada in outputs\" + fee) after adding change" + )?; + } + for (policy_id, asset_name, asset_shortage, asset_available) in &self.asset_shortage { + write!( + f, + "policy id: \"{}\", asset name: \"{}\" ", + policy_id, asset_name + )?; + writeln!( + f, + "coins in inputs: {}, coins in outputs: {}", + asset_shortage, asset_available + )?; } write!(f, " }}") } } -pub(crate) fn get_input_shortage(all_inputs_value: &Value, all_outputs_value: &Value, fee: &Coin) - -> Result, JsError> { - let mut shortage = ValueShortage{ +pub(crate) fn get_input_shortage( + all_inputs_value: &Value, + all_outputs_value: &Value, + fee: &Coin, +) -> Result, JsError> { + let mut shortage = ValueShortage { ada_shortage: None, - asset_shortage: Vec::new()}; + asset_shortage: Vec::new(), + }; if all_inputs_value.coin < all_outputs_value.coin.checked_add(fee)? { shortage.ada_shortage = Some(( all_inputs_value.coin.clone(), all_outputs_value.coin.clone(), - fee.clone())); + fee.clone(), + )); } if let Some(policies) = &all_outputs_value.multiasset { @@ -1697,11 +1066,16 @@ pub(crate) fn get_input_shortage(all_inputs_value: &Value, all_outputs_value: &V for (asset_name, coins) in &assets.0 { let inputs_coins = match &all_inputs_value.multiasset { Some(multiasset) => multiasset.get_asset(policy_id, asset_name), - None => Coin::zero() + None => Coin::zero(), }; if inputs_coins < *coins { - shortage.asset_shortage.push((policy_id.clone(), asset_name.clone(), inputs_coins, coins.clone())); + shortage.asset_shortage.push(( + policy_id.clone(), + asset_name.clone(), + inputs_coins, + coins.clone(), + )); } } } @@ -1712,1011 +1086,4 @@ pub(crate) fn get_input_shortage(all_inputs_value: &Value, all_outputs_value: &V } else { Ok(None) } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::tx_builder_constants::TxBuilderConstants; - - // this is what is used in mainnet - const COINS_PER_UTXO_WORD: u64 = 34_482; - - // taken from https://github.com/input-output-hk/cardano-ledger-specs/blob/master/doc/explanations/min-utxo-alonzo.rst - fn one_policy_one_0_char_asset() -> Value { - let mut token_bundle = MultiAsset::new(); - let mut asset_list = Assets::new(); - asset_list.insert(&AssetName(vec![]), &BigNum(1)); - token_bundle.insert(&PolicyID::from([0; ScriptHash::BYTE_COUNT]), &asset_list); - Value { - coin: BigNum(0), - multiasset: Some(token_bundle), - } - } - - fn one_policy_one_1_char_asset() -> Value { - let mut token_bundle = MultiAsset::new(); - let mut asset_list = Assets::new(); - asset_list.insert(&AssetName(vec![1]), &BigNum(1)); - token_bundle.insert(&PolicyID::from([0; ScriptHash::BYTE_COUNT]), &asset_list); - Value { - coin: BigNum(1407406), - multiasset: Some(token_bundle), - } - } - - fn one_policy_three_1_char_assets() -> Value { - let mut token_bundle = MultiAsset::new(); - let mut asset_list = Assets::new(); - asset_list.insert(&AssetName(vec![1]), &BigNum(1)); - asset_list.insert(&AssetName(vec![2]), &BigNum(1)); - asset_list.insert(&AssetName(vec![3]), &BigNum(1)); - token_bundle.insert(&PolicyID::from([0; ScriptHash::BYTE_COUNT]), &asset_list); - Value { - coin: BigNum(1555554), - multiasset: Some(token_bundle), - } - } - - fn two_policies_one_0_char_asset() -> Value { - let mut token_bundle = MultiAsset::new(); - let mut asset_list = Assets::new(); - asset_list.insert(&AssetName(vec![]), &BigNum(1)); - token_bundle.insert(&PolicyID::from([0; ScriptHash::BYTE_COUNT]), &asset_list); - token_bundle.insert(&PolicyID::from([1; ScriptHash::BYTE_COUNT]), &asset_list); - Value { - coin: BigNum(1592591), - multiasset: Some(token_bundle), - } - } - - fn two_policies_one_1_char_asset() -> Value { - let mut token_bundle = MultiAsset::new(); - let mut asset_list = Assets::new(); - asset_list.insert(&AssetName(vec![1]), &BigNum(1)); - token_bundle.insert(&PolicyID::from([0; ScriptHash::BYTE_COUNT]), &asset_list); - token_bundle.insert(&PolicyID::from([1; ScriptHash::BYTE_COUNT]), &asset_list); - Value { - coin: BigNum(1592591), - multiasset: Some(token_bundle), - } - } - - fn three_policies_96_1_char_assets() -> Value { - let mut token_bundle = MultiAsset::new(); - fn add_policy(token_bundle: &mut MultiAsset, index: u8) -> () { - let mut asset_list = Assets::new(); - - for i in 0..32 { - asset_list.insert(&AssetName(vec![index * 32 + i]), &BigNum(1)); - } - token_bundle.insert( - &PolicyID::from([index; ScriptHash::BYTE_COUNT]), - &asset_list, - ); - } - add_policy(&mut token_bundle, 1); - add_policy(&mut token_bundle, 2); - add_policy(&mut token_bundle, 3); - Value { - coin: BigNum(7592585), - multiasset: Some(token_bundle), - } - } - - fn one_policy_three_32_char_assets() -> Value { - let mut token_bundle = MultiAsset::new(); - let mut asset_list = Assets::new(); - asset_list.insert(&AssetName(vec![1; 32]), &BigNum(1)); - asset_list.insert(&AssetName(vec![2; 32]), &BigNum(1)); - asset_list.insert(&AssetName(vec![3; 32]), &BigNum(1)); - token_bundle.insert(&PolicyID::from([0; ScriptHash::BYTE_COUNT]), &asset_list); - Value { - coin: BigNum(1555554), - multiasset: Some(token_bundle), - } - } - - #[test] - fn min_ada_value_no_multiasset() { - assert_eq!( - from_bignum( - &min_ada_required( - &Value::new(&Coin::zero()), - false, - &to_bignum(COINS_PER_UTXO_WORD), - ) - .unwrap() - ), - 969750, - ); - } - - #[test] - fn min_ada_value_one_policy_one_0_char_asset() { - assert_eq!( - from_bignum( - &min_ada_required( - &one_policy_one_0_char_asset(), - false, - &to_bignum(COINS_PER_UTXO_WORD), - ) - .unwrap() - ), - 1_120_600, - ); - } - - #[test] - fn min_ada_value_one_policy_one_1_char_asset() { - assert_eq!( - from_bignum( - &min_ada_required( - &one_policy_one_1_char_asset(), - false, - &to_bignum(COINS_PER_UTXO_WORD), - ) - .unwrap() - ), - 1_124_910, - ); - } - - #[test] - fn min_ada_value_one_policy_three_1_char_assets() { - assert_eq!( - from_bignum( - &min_ada_required( - &one_policy_three_1_char_assets(), - false, - &to_bignum(COINS_PER_UTXO_WORD), - ) - .unwrap() - ), - 1_150_770, - ); - } - - #[test] - fn min_ada_value_two_policies_one_0_char_asset() { - assert_eq!( - from_bignum( - &min_ada_required( - &two_policies_one_0_char_asset(), - false, - &to_bignum(COINS_PER_UTXO_WORD), - ) - .unwrap() - ), - 1_262_830, - ); - } - - #[test] - fn min_ada_value_two_policies_one_1_char_asset() { - assert_eq!( - from_bignum( - &min_ada_required( - &two_policies_one_1_char_asset(), - false, - &to_bignum(COINS_PER_UTXO_WORD), - ) - .unwrap() - ), - 1_271_450, - ); - } - - #[test] - fn min_ada_value_three_policies_96_1_char_assets() { - assert_eq!( - from_bignum( - &min_ada_required( - &three_policies_96_1_char_assets(), - false, - &to_bignum(COINS_PER_UTXO_WORD), - ) - .unwrap() - ), - 2_633_410, - ); - } - - #[test] - fn min_ada_value_one_policy_one_0_char_asset_datum_hash() { - assert_eq!( - from_bignum( - &min_ada_required( - &one_policy_one_0_char_asset(), - true, - &to_bignum(COINS_PER_UTXO_WORD), - ) - .unwrap() - ), - 1_267_140, - ); - } - - #[test] - fn min_ada_value_one_policy_three_32_char_assets_datum_hash() { - assert_eq!( - from_bignum( - &min_ada_required( - &one_policy_three_32_char_assets(), - true, - &to_bignum(COINS_PER_UTXO_WORD), - ) - .unwrap() - ), - 1_711_070, - ); - } - - #[test] - fn min_ada_value_two_policies_one_0_char_asset_datum_hash() { - assert_eq!( - from_bignum( - &min_ada_required( - &two_policies_one_0_char_asset(), - true, - &to_bignum(COINS_PER_UTXO_WORD), - ) - .unwrap() - ), - 1_409_370, - ); - } - - #[test] - fn subtract_values() { - let policy1 = PolicyID::from([0; ScriptHash::BYTE_COUNT]); - let policy2 = PolicyID::from([1; ScriptHash::BYTE_COUNT]); - - let asset1 = AssetName(vec![1]); - let asset2 = AssetName(vec![2]); - let asset3 = AssetName(vec![3]); - let asset4 = AssetName(vec![4]); - - let mut token_bundle1 = MultiAsset::new(); - { - let mut asset_list1 = Assets::new(); - asset_list1.insert(&asset1, &BigNum(1)); - asset_list1.insert(&asset2, &BigNum(1)); - asset_list1.insert(&asset3, &BigNum(1)); - asset_list1.insert(&asset4, &BigNum(2)); - token_bundle1.insert(&policy1, &asset_list1); - - let mut asset_list2 = Assets::new(); - asset_list2.insert(&asset1, &BigNum(1)); - token_bundle1.insert(&policy2, &asset_list2); - } - let assets1 = Value { - coin: BigNum(1555554), - multiasset: Some(token_bundle1), - }; - - let mut token_bundle2 = MultiAsset::new(); - { - let mut asset_list2 = Assets::new(); - // more than asset1 bundle - asset_list2.insert(&asset1, &BigNum(2)); - // exactly equal to asset1 bundle - asset_list2.insert(&asset2, &BigNum(1)); - // skip asset 3 - // less than in asset1 bundle - asset_list2.insert(&asset4, &BigNum(1)); - token_bundle2.insert(&policy1, &asset_list2); - - // this policy should be removed entirely - let mut asset_list2 = Assets::new(); - asset_list2.insert(&asset1, &BigNum(1)); - token_bundle2.insert(&policy2, &asset_list2); - } - - let assets2 = Value { - coin: BigNum(2555554), - multiasset: Some(token_bundle2), - }; - - let result = assets1.clamped_sub(&assets2); - assert_eq!(result.coin().to_str(), "0"); - assert_eq!( - result.multiasset().unwrap().len(), - 1 // policy 2 was deleted successfully - ); - let policy1_content = result.multiasset().unwrap().get(&policy1).unwrap(); - assert_eq!(policy1_content.len(), 2); - assert_eq!(policy1_content.get(&asset3).unwrap().to_str(), "1"); - assert_eq!(policy1_content.get(&asset4).unwrap().to_str(), "1"); - } - - #[test] - fn compare_values() { - let policy1 = PolicyID::from([0; ScriptHash::BYTE_COUNT]); - - let asset1 = AssetName(vec![1]); - let asset2 = AssetName(vec![2]); - - // testing cases with no assets - { - let a = Value::new(&to_bignum(1)); - let b = Value::new(&to_bignum(1)); - assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Equal); - } - { - let a = Value::new(&to_bignum(2)); - let b = Value::new(&to_bignum(1)); - assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Greater); - } - { - let a = Value::new(&to_bignum(1)); - let b = Value::new(&to_bignum(2)); - assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Less); - } - // testing case where one side has assets - { - let mut token_bundle1 = MultiAsset::new(); - let mut asset_list1 = Assets::new(); - asset_list1.insert(&asset1, &BigNum(1)); - token_bundle1.insert(&policy1, &asset_list1); - let a = Value { - coin: BigNum(1), - multiasset: Some(token_bundle1), - }; - let b = Value::new(&to_bignum(1)); - assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Greater); - } - { - let mut token_bundle1 = MultiAsset::new(); - let mut asset_list1 = Assets::new(); - asset_list1.insert(&asset1, &BigNum(1)); - token_bundle1.insert(&policy1, &asset_list1); - let a = Value::new(&to_bignum(1)); - let b = Value { - coin: BigNum(1), - multiasset: Some(token_bundle1), - }; - assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Less); - } - // testing case where both sides has assets - { - let mut token_bundle1 = MultiAsset::new(); - let mut asset_list1 = Assets::new(); - asset_list1.insert(&asset1, &BigNum(1)); - token_bundle1.insert(&policy1, &asset_list1); - let a = Value { - coin: BigNum(1), - multiasset: Some(token_bundle1), - }; - - let mut token_bundle2 = MultiAsset::new(); - let mut asset_list2 = Assets::new(); - asset_list2.insert(&asset1, &BigNum(1)); - token_bundle2.insert(&policy1, &asset_list2); - let b = Value { - coin: BigNum(1), - multiasset: Some(token_bundle2), - }; - assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Equal); - } - { - let mut token_bundle1 = MultiAsset::new(); - let mut asset_list1 = Assets::new(); - asset_list1.insert(&asset1, &BigNum(1)); - token_bundle1.insert(&policy1, &asset_list1); - let a = Value { - coin: BigNum(2), - multiasset: Some(token_bundle1), - }; - - let mut token_bundle2 = MultiAsset::new(); - let mut asset_list2 = Assets::new(); - asset_list2.insert(&asset1, &BigNum(1)); - token_bundle2.insert(&policy1, &asset_list2); - let b = Value { - coin: BigNum(1), - multiasset: Some(token_bundle2), - }; - assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Greater); - } - { - let mut token_bundle1 = MultiAsset::new(); - let mut asset_list1 = Assets::new(); - asset_list1.insert(&asset1, &BigNum(1)); - token_bundle1.insert(&policy1, &asset_list1); - let a = Value { - coin: BigNum(1), - multiasset: Some(token_bundle1), - }; - - let mut token_bundle2 = MultiAsset::new(); - let mut asset_list2 = Assets::new(); - asset_list2.insert(&asset1, &BigNum(1)); - token_bundle2.insert(&policy1, &asset_list2); - let b = Value { - coin: BigNum(2), - multiasset: Some(token_bundle2), - }; - assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Less); - } - { - let mut token_bundle1 = MultiAsset::new(); - let mut asset_list1 = Assets::new(); - asset_list1.insert(&asset1, &BigNum(2)); - token_bundle1.insert(&policy1, &asset_list1); - let a = Value { - coin: BigNum(1), - multiasset: Some(token_bundle1), - }; - - let mut token_bundle2 = MultiAsset::new(); - let mut asset_list2 = Assets::new(); - asset_list2.insert(&asset1, &BigNum(1)); - token_bundle2.insert(&policy1, &asset_list2); - let b = Value { - coin: BigNum(1), - multiasset: Some(token_bundle2), - }; - assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Greater); - } - { - let mut token_bundle1 = MultiAsset::new(); - let mut asset_list1 = Assets::new(); - asset_list1.insert(&asset1, &BigNum(2)); - token_bundle1.insert(&policy1, &asset_list1); - let a = Value { - coin: BigNum(2), - multiasset: Some(token_bundle1), - }; - - let mut token_bundle2 = MultiAsset::new(); - let mut asset_list2 = Assets::new(); - asset_list2.insert(&asset1, &BigNum(1)); - token_bundle2.insert(&policy1, &asset_list2); - let b = Value { - coin: BigNum(1), - multiasset: Some(token_bundle2), - }; - assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Greater); - } - { - let mut token_bundle1 = MultiAsset::new(); - let mut asset_list1 = Assets::new(); - asset_list1.insert(&asset1, &BigNum(2)); - token_bundle1.insert(&policy1, &asset_list1); - let a = Value { - coin: BigNum(1), - multiasset: Some(token_bundle1), - }; - - let mut token_bundle2 = MultiAsset::new(); - let mut asset_list2 = Assets::new(); - asset_list2.insert(&asset1, &BigNum(1)); - token_bundle2.insert(&policy1, &asset_list2); - let b = Value { - coin: BigNum(2), - multiasset: Some(token_bundle2), - }; - assert_eq!(a.partial_cmp(&b), None); - } - { - let mut token_bundle1 = MultiAsset::new(); - let mut asset_list1 = Assets::new(); - asset_list1.insert(&asset1, &BigNum(1)); - token_bundle1.insert(&policy1, &asset_list1); - let a = Value { - coin: BigNum(1), - multiasset: Some(token_bundle1), - }; - - let mut token_bundle2 = MultiAsset::new(); - let mut asset_list2 = Assets::new(); - asset_list2.insert(&asset1, &BigNum(2)); - token_bundle2.insert(&policy1, &asset_list2); - let b = Value { - coin: BigNum(1), - multiasset: Some(token_bundle2), - }; - assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Less); - } - { - let mut token_bundle1 = MultiAsset::new(); - let mut asset_list1 = Assets::new(); - asset_list1.insert(&asset1, &BigNum(1)); - token_bundle1.insert(&policy1, &asset_list1); - let a = Value { - coin: BigNum(1), - multiasset: Some(token_bundle1), - }; - - let mut token_bundle2 = MultiAsset::new(); - let mut asset_list2 = Assets::new(); - asset_list2.insert(&asset1, &BigNum(2)); - token_bundle2.insert(&policy1, &asset_list2); - let b = Value { - coin: BigNum(2), - multiasset: Some(token_bundle2), - }; - assert_eq!(a.partial_cmp(&b).unwrap(), std::cmp::Ordering::Less); - } - { - let mut token_bundle1 = MultiAsset::new(); - let mut asset_list1 = Assets::new(); - asset_list1.insert(&asset1, &BigNum(1)); - token_bundle1.insert(&policy1, &asset_list1); - let a = Value { - coin: BigNum(2), - multiasset: Some(token_bundle1), - }; - - let mut token_bundle2 = MultiAsset::new(); - let mut asset_list2 = Assets::new(); - asset_list2.insert(&asset1, &BigNum(2)); - token_bundle2.insert(&policy1, &asset_list2); - let b = Value { - coin: BigNum(1), - multiasset: Some(token_bundle2), - }; - assert_eq!(a.partial_cmp(&b), None); - } - { - let mut token_bundle1 = MultiAsset::new(); - let mut asset_list1 = Assets::new(); - asset_list1.insert(&asset1, &BigNum(1)); - token_bundle1.insert(&policy1, &asset_list1); - let a = Value { - coin: BigNum(1), - multiasset: Some(token_bundle1), - }; - - let mut token_bundle2 = MultiAsset::new(); - let mut asset_list2 = Assets::new(); - asset_list2.insert(&asset2, &BigNum(1)); - token_bundle2.insert(&policy1, &asset_list2); - let b = Value { - coin: BigNum(1), - multiasset: Some(token_bundle2), - }; - assert_eq!(a.partial_cmp(&b), None); - } - } - - #[test] - fn bigint_serialization() { - let zero = BigInt::from_str("0").unwrap(); - let zero_rt = BigInt::from_bytes(zero.to_bytes()).unwrap(); - assert_eq!(zero.to_str(), zero_rt.to_str()); - assert_eq!(zero.to_bytes(), vec![0x00]); - - let pos_small = BigInt::from_str("100").unwrap(); - let pos_small_rt = BigInt::from_bytes(pos_small.to_bytes()).unwrap(); - assert_eq!(pos_small.to_str(), pos_small_rt.to_str()); - - let pos_big = BigInt::from_str("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890").unwrap(); - let pos_big_rt = BigInt::from_bytes(pos_big.to_bytes()).unwrap(); - assert_eq!(pos_big.to_str(), pos_big_rt.to_str()); - - let neg_small = BigInt::from_str("-100").unwrap(); - let neg_small_rt = BigInt::from_bytes(neg_small.to_bytes()).unwrap(); - assert_eq!(neg_small.to_str(), neg_small_rt.to_str()); - - let neg_big = BigInt::from_str("-123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890").unwrap(); - let neg_big_rt = BigInt::from_bytes(neg_big.to_bytes()).unwrap(); - assert_eq!(neg_big.to_str(), neg_big_rt.to_str()); - - // taken from CBOR RFC examples - // negative big int - assert_eq!( - hex::decode("c349010000000000000000").unwrap(), - BigInt::from_str("-18446744073709551617") - .unwrap() - .to_bytes() - ); - // positive big int - assert_eq!( - hex::decode("c249010000000000000000").unwrap(), - BigInt::from_str("18446744073709551616").unwrap().to_bytes() - ); - // uint - assert_eq!( - hex::decode("1b000000e8d4a51000").unwrap(), - BigInt::from_str("1000000000000").unwrap().to_bytes() - ); - // nint (lowest possible - used to be unsupported but works now) - assert_eq!( - hex::decode("3bffffffffffffffff").unwrap(), - BigInt::from_str("-18446744073709551616") - .unwrap() - .to_bytes() - ); - // this one fits in an i64 though - assert_eq!( - hex::decode("3903e7").unwrap(), - BigInt::from_str("-1000").unwrap().to_bytes() - ); - - let x = BigInt::from_str("-18446744073709551617").unwrap(); - let x_rt = BigInt::from_bytes(x.to_bytes()).unwrap(); - assert_eq!(x.to_str(), x_rt.to_str()); - } - - #[test] - fn bounded_bytes_read_chunked() { - use std::io::Cursor; - let chunks = vec![ - vec![ - 0x52, 0x73, 0x6F, 0x6D, 0x65, 0x20, 0x72, 0x61, 0x6E, 0x64, 0x6F, 0x6D, 0x20, 0x73, - 0x74, 0x72, 0x69, 0x6E, 0x67, - ], - vec![0x44, 0x01, 0x02, 0x03, 0x04], - ]; - let mut expected = Vec::new(); - for chunk in chunks.iter() { - expected.extend_from_slice(&chunk[1..]); - } - let mut vec = vec![0x5f]; - for mut chunk in chunks { - vec.append(&mut chunk); - } - vec.push(0xff); - let mut raw = Deserializer::from(Cursor::new(vec.clone())); - let found = read_bounded_bytes(&mut raw).unwrap(); - assert_eq!(found, expected); - } - - #[test] - fn bounded_bytes_write_chunked() { - let mut chunk_64 = vec![0x58, BOUNDED_BYTES_CHUNK_SIZE as u8]; - chunk_64.extend(std::iter::repeat(37).take(BOUNDED_BYTES_CHUNK_SIZE)); - let chunks = vec![chunk_64, vec![0x44, 0x01, 0x02, 0x03, 0x04]]; - let mut input = Vec::new(); - input.extend_from_slice(&chunks[0][2..]); - input.extend_from_slice(&chunks[1][1..]); - let mut serializer = cbor_event::se::Serializer::new_vec(); - write_bounded_bytes(&mut serializer, &input).unwrap(); - let written = serializer.finalize(); - let mut expected = vec![0x5f]; - for mut chunk in chunks { - expected.append(&mut chunk); - } - expected.push(0xff); - assert_eq!(expected, written); - } - - #[test] - fn correct_script_data_hash() { - let mut datums = PlutusList::new(); - datums.add(&PlutusData::new_integer(&BigInt::from_str("1000").unwrap())); - let mut redeemers = Redeemers::new(); - redeemers.add(&Redeemer::new( - &RedeemerTag::new_spend(), - &BigNum::from_str("1").unwrap(), - &PlutusData::new_integer(&BigInt::from_str("2000").unwrap()), - &ExUnits::new( - &BigNum::from_str("0").unwrap(), - &BigNum::from_str("0").unwrap(), - ), - )); - let plutus_cost_model = CostModel::from_bytes(vec![ - 159, 26, 0, 3, 2, 89, 0, 1, 1, 26, 0, 6, 11, 199, 25, 2, 109, 0, 1, 26, 0, 2, 73, 240, - 25, 3, 232, 0, 1, 26, 0, 2, 73, 240, 24, 32, 26, 0, 37, 206, 168, 25, 113, 247, 4, 25, - 116, 77, 24, 100, 25, 116, 77, 24, 100, 25, 116, 77, 24, 100, 25, 116, 77, 24, 100, 25, - 116, 77, 24, 100, 25, 116, 77, 24, 100, 24, 100, 24, 100, 25, 116, 77, 24, 100, 26, 0, - 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, - 240, 25, 3, 232, 0, 1, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 25, 3, 232, 0, 8, - 26, 0, 2, 66, 32, 26, 0, 6, 126, 35, 24, 118, 0, 1, 1, 26, 0, 2, 73, 240, 25, 3, 232, - 0, 8, 26, 0, 2, 73, 240, 26, 0, 1, 183, 152, 24, 247, 1, 26, 0, 2, 73, 240, 25, 39, 16, - 1, 26, 0, 2, 21, 94, 25, 5, 46, 1, 25, 3, 232, 26, 0, 2, 73, 240, 25, 3, 232, 1, 26, 0, - 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 1, 1, 26, 0, - 2, 73, 240, 1, 26, 0, 2, 73, 240, 4, 26, 0, 1, 148, 175, 24, 248, 1, 26, 0, 1, 148, - 175, 24, 248, 1, 26, 0, 2, 55, 124, 25, 5, 86, 1, 26, 0, 2, 189, 234, 25, 1, 241, 1, - 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, - 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 66, - 32, 26, 0, 6, 126, 35, 24, 118, 0, 1, 1, 25, 240, 76, 25, 43, 210, 0, 1, 26, 0, 2, 73, - 240, 24, 32, 26, 0, 2, 66, 32, 26, 0, 6, 126, 35, 24, 118, 0, 1, 1, 26, 0, 2, 66, 32, - 26, 0, 6, 126, 35, 24, 118, 0, 1, 1, 26, 0, 37, 206, 168, 25, 113, 247, 4, 0, 26, 0, 1, - 65, 187, 4, 26, 0, 2, 73, 240, 25, 19, 136, 0, 1, 26, 0, 2, 73, 240, 24, 32, 26, 0, 3, - 2, 89, 0, 1, 1, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, - 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 2, 73, 240, - 24, 32, 26, 0, 2, 73, 240, 24, 32, 26, 0, 51, 13, 167, 1, 1, 255, - ]) - .unwrap(); - let mut cost_models = Costmdls::new(); - cost_models.insert(&Language::new_plutus_v1(), &plutus_cost_model); - let script_data_hash = hash_script_data(&redeemers, &cost_models, Some(datums)); - - assert_eq!( - hex::encode(script_data_hash.to_bytes()), - "4415e6667e6d6bbd992af5092d48e3c2ba9825200d0234d2470068f7f0f178b3" - ); - } - - #[test] - fn native_scripts_from_wallet_json() { - let cosigner0_hex = "1423856bc91c49e928f6f30f4e8d665d53eb4ab6028bd0ac971809d514c92db11423856bc91c49e928f6f30f4e8d665d53eb4ab6028bd0ac971809d514c92db1"; - let cosigner1_hex = "a48d97f57ce49433f347d44ee07e54a100229b4f8e125d25f7bca9ad66d9707a25cd1331f46f7d6e279451637ca20802a25c441ba9436abf644fe5410d1080e3"; - let self_key_hex = "6ce83a12e9d4c783f54c0bb511303b37160a6e4f3f96b8e878a7c1f7751e18c4ccde3fb916d330d07f7bd51fb6bd99aa831d925008d3f7795033f48abd6df7f6"; - let native_script = encode_json_str_to_native_script( - &format!( - r#" - {{ - "cosigners": {{ - "cosigner#0": "{}", - "cosigner#1": "{}", - "cosigner#2": "self" - }}, - "template": {{ - "some": {{ - "at_least": 2, - "from": [ - {{ - "all": [ - "cosigner#0", - {{ "active_from": 120 }} - ] - }}, - {{ - "any": [ - "cosigner#1", - {{ "active_until": 1000 }} - ] - }}, - "cosigner#2" - ] - }} - }} - }}"#, - cosigner0_hex, cosigner1_hex - ), - self_key_hex, - ScriptSchema::Wallet, - ); - - let n_of_k = native_script.unwrap().as_script_n_of_k().unwrap(); - let from = n_of_k.native_scripts(); - assert_eq!(n_of_k.n(), 2); - assert_eq!(from.len(), 3); - let all = from.get(0).as_script_all().unwrap().native_scripts(); - assert_eq!(all.len(), 2); - let all_0 = all.get(0).as_script_pubkey().unwrap(); - assert_eq!( - all_0.addr_keyhash(), - Bip32PublicKey::from_bytes(&hex::decode(cosigner0_hex).unwrap()) - .unwrap() - .to_raw_key() - .hash() - ); - let all_1 = all.get(1).as_timelock_start().unwrap(); - assert_eq!(all_1.slot().unwrap(), 120); - let any = from.get(1).as_script_any().unwrap().native_scripts(); - assert_eq!(all.len(), 2); - let any_0 = any.get(0).as_script_pubkey().unwrap(); - assert_eq!( - any_0.addr_keyhash(), - Bip32PublicKey::from_bytes(&hex::decode(cosigner1_hex).unwrap()) - .unwrap() - .to_raw_key() - .hash() - ); - let any_1 = any.get(1).as_timelock_expiry().unwrap(); - assert_eq!(any_1.slot().unwrap(), 1000); - let self_key = from.get(2).as_script_pubkey().unwrap(); - assert_eq!( - self_key.addr_keyhash(), - Bip32PublicKey::from_bytes(&hex::decode(self_key_hex).unwrap()) - .unwrap() - .to_raw_key() - .hash() - ); - } - - #[test] - fn int_to_str() { - assert_eq!( - Int::new(&BigNum(u64::max_value())).to_str(), - u64::max_value().to_string() - ); - assert_eq!( - Int::new(&BigNum(u64::min_value())).to_str(), - u64::min_value().to_string() - ); - assert_eq!( - Int::new_negative(&BigNum(u64::max_value())).to_str(), - (-(u64::max_value() as i128)).to_string() - ); - assert_eq!( - Int::new_negative(&BigNum(u64::min_value())).to_str(), - (-(u64::min_value() as i128)).to_string() - ); - assert_eq!(Int::new_i32(142).to_str(), "142"); - assert_eq!(Int::new_i32(-142).to_str(), "-142"); - } - - #[test] - fn int_as_i32_or_nothing() { - let over_pos_i32 = (i32::max_value() as i64) + 1; - assert!(Int::new(&BigNum(over_pos_i32 as u64)) - .as_i32_or_nothing() - .is_none()); - - let valid_pos_i32 = i32::max_value() as i64; - assert_eq!( - Int::new(&BigNum(valid_pos_i32 as u64)) - .as_i32_or_nothing() - .unwrap(), - i32::max_value() - ); - - let over_neg_i32 = (i32::min_value() as i64) - 1; - assert!(Int::new_negative(&BigNum((-over_neg_i32) as u64)) - .as_i32_or_nothing() - .is_none()); - - let valid_neg_i32 = i32::min_value() as i64; - assert_eq!( - Int::new_negative(&BigNum((-valid_neg_i32) as u64)) - .as_i32_or_nothing() - .unwrap(), - i32::min_value() - ); - - assert!(Int::new(&BigNum(u64::max_value())) - .as_i32_or_nothing() - .is_none()); - assert_eq!( - Int::new(&BigNum(i32::max_value() as u64)) - .as_i32_or_nothing() - .unwrap(), - i32::max_value() - ); - assert_eq!( - Int::new_negative(&BigNum(i32::max_value() as u64)) - .as_i32_or_nothing() - .unwrap(), - -i32::max_value() - ); - - assert_eq!(Int::new_i32(42).as_i32_or_nothing().unwrap(), 42); - assert_eq!(Int::new_i32(-42).as_i32_or_nothing().unwrap(), -42); - } - - #[test] - fn int_as_i32_or_fail() { - let over_pos_i32 = (i32::max_value() as i64) + 1; - assert!(Int::new(&BigNum(over_pos_i32 as u64)) - .as_i32_or_fail() - .is_err()); - - let valid_pos_i32 = i32::max_value() as i64; - assert_eq!( - Int::new(&BigNum(valid_pos_i32 as u64)) - .as_i32_or_fail() - .unwrap(), - i32::max_value() - ); - - let over_neg_i32 = (i32::min_value() as i64) - 1; - assert!(Int::new_negative(&BigNum((-over_neg_i32) as u64)) - .as_i32_or_fail() - .is_err()); - - let valid_neg_i32 = i32::min_value() as i64; - assert_eq!( - Int::new_negative(&BigNum((-valid_neg_i32) as u64)) - .as_i32_or_fail() - .unwrap(), - i32::min_value() - ); - - assert!(Int::new(&BigNum(u64::max_value())) - .as_i32_or_fail() - .is_err()); - assert_eq!( - Int::new(&BigNum(i32::max_value() as u64)) - .as_i32_or_fail() - .unwrap(), - i32::max_value() - ); - assert_eq!( - Int::new_negative(&BigNum(i32::max_value() as u64)) - .as_i32_or_fail() - .unwrap(), - -i32::max_value() - ); - - assert_eq!(Int::new_i32(42).as_i32_or_fail().unwrap(), 42); - assert_eq!(Int::new_i32(-42).as_i32_or_fail().unwrap(), -42); - } - - #[test] - fn int_full_range() { - // cbor_event's nint API worked via i64 but we now have a workaround for it - // so these tests are here to make sure that workaround works. - - // first nint below of i64::MIN - let bytes_x = vec![0x3b, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - let x = Int::from_bytes(bytes_x.clone()).unwrap(); - assert_eq!(x.to_str(), "-9223372036854775809"); - assert_eq!(bytes_x, x.to_bytes()); - - // smallest possible nint which is -u64::MAX - 1 - let bytes_y = vec![0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]; - let y = Int::from_bytes(bytes_y.clone()).unwrap(); - assert_eq!(y.to_str(), "-18446744073709551616"); - assert_eq!(bytes_y, y.to_bytes()); - } - - #[test] - fn test_bigint_add() { - assert_eq!(to_bigint(10).add(&to_bigint(20)), to_bigint(30), ); - assert_eq!(to_bigint(500).add(&to_bigint(800)), to_bigint(1300), ); - } - - #[test] - fn test_bigint_mul() { - assert_eq!(to_bigint(10).mul(&to_bigint(20)), to_bigint(200), ); - assert_eq!(to_bigint(500).mul(&to_bigint(800)), to_bigint(400000), ); - assert_eq!(to_bigint(12).mul(&to_bigint(22)), to_bigint(264), ); - } - - #[test] - fn test_bigint_div_ceil() { - assert_eq!(to_bigint(20).div_ceil(&to_bigint(10)), to_bigint(2), ); - assert_eq!(to_bigint(20).div_ceil(&to_bigint(2)), to_bigint(10), ); - assert_eq!(to_bigint(21).div_ceil(&to_bigint(2)), to_bigint(11), ); - assert_eq!(to_bigint(6).div_ceil(&to_bigint(3)), to_bigint(2), ); - assert_eq!(to_bigint(5).div_ceil(&to_bigint(3)), to_bigint(2), ); - assert_eq!(to_bigint(7).div_ceil(&to_bigint(3)), to_bigint(3), ); - } - - #[test] - fn test_bignum_div() { - assert_eq!(to_bignum(10).div_floor(&to_bignum(1)), to_bignum(10), ); - assert_eq!(to_bignum(10).div_floor(&to_bignum(3)), to_bignum(3), ); - assert_eq!(to_bignum(10).div_floor(&to_bignum(4)), to_bignum(2), ); - assert_eq!(to_bignum(10).div_floor(&to_bignum(5)), to_bignum(2), ); - assert_eq!(to_bignum(10).div_floor(&to_bignum(6)), to_bignum(1), ); - assert_eq!(to_bignum(10).div_floor(&to_bignum(12)), to_bignum(0), ); - } - - #[test] - fn test_vasil_v1_costmodel_hashing() { - let v1 = Language::new_plutus_v1(); - let v1_cost_model = TxBuilderConstants::plutus_vasil_cost_models() - .get(&v1) - .unwrap(); - let mut costmodels = Costmdls::new(); - costmodels.insert(&v1, &v1_cost_model); - let hash = hash_script_data( - &Redeemers(vec![Redeemer::new( - &RedeemerTag::new_spend(), - &BigNum::zero(), - &PlutusData::new_integer(&BigInt::from_str("42").unwrap()), - &ExUnits::new(&to_bignum(1700), &to_bignum(368100)), - )]), - &costmodels, - Some(PlutusList::from(vec![PlutusData::new_integer( - &BigInt::from_str("42").unwrap(), - )])), - ); - assert_eq!( - hex::encode(hash.to_bytes()), - "887e1b6416d750d871c0f5b7136b54f7b8e8b0e293379d090f38f8f821d08a29" - ); - } - - #[test] - fn bigint_as_int() { - let zero = BigInt::from_str("0").unwrap(); - let zero_int = zero.as_int().unwrap(); - assert_eq!(zero_int.0, 0i128); - - let pos = BigInt::from_str("1024").unwrap(); - let pos_int = pos.as_int().unwrap(); - assert_eq!(pos_int.0, 1024i128); - - let neg = BigInt::from_str("-1024").unwrap(); - let neg_int = neg.as_int().unwrap(); - assert_eq!(neg_int.0, -1024i128); - } -} +} \ No newline at end of file diff --git a/scripts/publish-helper.js b/scripts/publish-helper.js index 3a54acf6..1a663de5 100644 --- a/scripts/publish-helper.js +++ b/scripts/publish-helper.js @@ -7,6 +7,10 @@ if (oldPkg.files.find(entry => entry === flowFile) == null) { } if (oldPkg.name === 'cardano-serialization-lib') { oldPkg.name = '@emurgo/' + oldPkg.name + process.argv.slice(2)[0]; + let optionalArg = process.argv.slice(2)[1]; + if (optionalArg) { + oldPkg.name += optionalArg; + } } if (process.argv.slice(2)[0] === '-browser' || process.argv.slice(2)[0] === '-asmjs') { // due to a bug in wasm-pack, this file is missing from browser builds diff --git a/scripts/run-json2ts.js b/scripts/run-json2ts.js index f97fa288..4b94ea07 100644 --- a/scripts/run-json2ts.js +++ b/scripts/run-json2ts.js @@ -5,23 +5,92 @@ const path = require('path'); const schemasDir = path.join('rust', 'json-gen', 'schemas'); const schemaFiles = fs.readdirSync(schemasDir).filter(file => path.extname(file) === '.json'); -function replaceRef(obj) { - if (obj['$ref'] != null && typeof obj['$ref'] === 'string' && obj['$ref'].startsWith('#/definitions/')) { - obj['$ref'] = obj['$ref'].replace(/^(#\/definitions\/)/, '') + '.json';//`file:`) + '.json#'; - console.log(`replacing: ${obj['$ref']}`); +function renameSchema(obj, visited) { + if (visited != null && visited.has(obj)) { + return; } + visited = visited || new Set(); + visited.add(obj); + if (obj.hasOwnProperty("title")) { + if (!obj.title.endsWith("JSON")) { + obj.title = obj.title + "JSON"; + } + } + renameDefinitions(obj, visited); + renameRef(obj); + goOverProperties(obj, visited); + goOverMultiType(obj, visited); +} + +function renameRef(obj) { + if (obj.hasOwnProperty("$ref")) { + const ref = obj["$ref"]; + if (!ref.endsWith("JSON")) { + obj["$ref"] = ref + "JSON"; + } + } } -function replaceRefs(node) { - Object.entries(node).forEach(([k, v]) => { - if (typeof v === 'object') { - replaceRef(v); - replaceRefs(v); - /*if (v.additionalProperties != null) { - replaceRef(v.additionalProperties); - }*/ +function renameDefinitions(obj, visited) { + if (obj.hasOwnProperty("definitions")) { + for (const [key, value] of Object.entries(obj.definitions)) { + if (!key.endsWith("JSON") && !key.startsWith("__")) { + renameObjectKey(obj.definitions, key, key + "JSON"); + renameSchema(value, visited) + } + visited.add(value); } - }); + } +} + +function goOverProperties(obj, visited) { + if (obj.hasOwnProperty("properties")) { + for (const [key, value] of Object.entries(obj.properties)) { + renameSchema(value, visited); + } + } + if (obj.hasOwnProperty("items")) { + if (Array.isArray(obj.items)) { + for (const [key, value] of Object.entries(obj.items)) { + renameSchema(value, visited); + } + } else if (typeof obj.items === "object") { + renameSchema(obj.items, visited); + } + } + + if (obj.hasOwnProperty("additionalProperties")) { + if (Array.isArray(obj.additionalProperties)) { + for (const [key, value] of Object.entries(obj.additionalProperties)) { + renameSchema(value, visited); + } + } else if (typeof obj.additionalProperties === "object") { + renameSchema(obj.additionalProperties, visited); + } + + } +} + +function isMultiType(obj) { + return obj.hasOwnProperty("anyOf") || obj.hasOwnProperty("oneOf") || obj.hasOwnProperty("allOf"); +} + +function goOverMultiType(obj, visited) { + if (isMultiType(obj)) { + for (const [key, value] of Object.entries(obj)) { + if (key === "anyOf" || key === "oneOf" || key === "allOf") { + for (const [index, type] of Object.entries(value)) { + renameSchema(type, visited); + } + } + } + } +} + +function renameObjectKey(obj, oldKey, newKey) { + if (obj.hasOwnProperty(oldKey)) { + delete Object.assign(obj, {[newKey]: obj[oldKey]})[oldKey]; + } } Promise.all(schemaFiles.map(schemaFile => { @@ -38,31 +107,11 @@ Promise.all(schemaFiles.map(schemaFile => { schemaObj.additionalProperties = false; } - // NOTE: Due to infinite recursion in recursive types (e.g. NativeScript) - // possibly due to a bug in the schema->ts lib, we instead just remove the - // duplicates after the fact. Self-referencing is confirmed not supported, - // but there has been mentions in those github issues of support for other - // recursive types correctly. - // - // It's possibly related to directly using "Foo.json" in replaceRefs() - // but I was not able to get this working using the $ref external file: syntax - // that seemed to be valid online. - // We can also take advantage of the post-removal to easily remove 2 weird unused - // outputs that shouldn't have been generated to begin with (String and MapOf_*) - - /* - // we need to make all references by external so we don't duplicate declarations - if (schemaObj.definitions != null) { - // eliminate in-file definitions - schemaObj.definitions = []; - // change all refs from local to external - replaceRefs(schemaObj); - } - */ - - //console.log(`NEW: ${JSON.stringify(schemaObj)}\n\n\n\n\n`); + renameSchema(schemaObj, null); + // console.log(`NEW: ${JSON.stringify(schemaObj)}\n\n\n\n\n`); return json2ts.compile(schemaObj, schemaFile, { - declareExternallyReferenced: false, + declareExternallyReferenced: true, + additionalProperties: false, cwd: schemasDir,//path.join(process.cwd(), schemasDir), bannerComment: '' }).catch(e => { console.error(`${schemaFile}: ${e}`); }); @@ -70,6 +119,10 @@ Promise.all(schemaFiles.map(schemaFile => { })).then(tsDefs => { fs.mkdirSync(path.join('rust', 'json-gen', 'output'), { recursive: true }); const defs = tsDefs.join('').split(/\r?\n/); + //replace all auto-deduped defs with the first one + for(let i = 0; i < defs.length; ++i) { + defs[i] = defs[i].replace(/JSON\d+/, 'JSON'); + } let dedupedDefs = []; let start = null; let added = new Set(); @@ -99,12 +152,6 @@ Promise.all(schemaFiles.map(schemaFile => { } } addDef(defs.length); - // prepend 'JSON' to all identifiers here so they don't conflict with main .ts types - for (let i = 0; i < dedupedDefs.length; ++i) { - for (let id of added) { - dedupedDefs[i] = dedupedDefs[i].replace(new RegExp(`\\b${id}\\b`), id + 'JSON'); - } - } return fs.writeFileSync(path.join('rust', 'json-gen', 'output', 'json-types.d.ts'), dedupedDefs.join('\n')); });