diff --git a/.github/workflows/gh-issue-to-jira-task.yml b/.github/workflows/gh-issue-to-jira-task.yml new file mode 100644 index 0000000..d8b0a14 --- /dev/null +++ b/.github/workflows/gh-issue-to-jira-task.yml @@ -0,0 +1,32 @@ +on: + issues: + types: [opened] + +name: Jira Actions + +jobs: + build: + runs-on: ubuntu-latest + name: Create Jira task + steps: + - name: Login + uses: atlassian/gajira-login@master + env: + JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} + + - name: Jira Create issue + uses: atlassian/gajira-create@v3 + id: create + with: + project: SERLIB + issuetype: Task + summary: GH-${{ github.event.issue.number }}. ${{ github.event.issue.title }} + description: | + ${{ github.event.issue.html_url }} + ${{ github.event.issue.body }} + + + - name: Log created issue + run: echo "Issue ${{ steps.create.outputs.issue }} was created" \ No newline at end of file diff --git a/README.md b/README.md index ad335f3..5c0dae0 100644 --- a/README.md +++ b/README.md @@ -18,4 +18,4 @@ This is a library, written in Rust, for serialization & deserialization of data ## Documentation -You can find documentation [here](https://docs.cardano.org/cardano-components/cardano-serialization-lib) +You can find documentation [here](https://developers.cardano.org/docs/get-started/cardano-serialization-lib/overview) diff --git a/example/package-lock.json b/example/package-lock.json index 61f82b3..2021138 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -1195,9 +1195,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true }, "mkdirp": { diff --git a/package-lock.json b/package-lock.json index 6210d15..9433dbd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -249,9 +249,9 @@ "dev": true }, "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "dev": true }, "compare-versions": { @@ -385,19 +385,58 @@ } }, "flowgen": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/flowgen/-/flowgen-1.11.0.tgz", - "integrity": "sha512-WpoBjzcZadnAw5FatlUbvFWUWXkI2/LjrwTl5fl3MVDh+KdvYgFzgRXDDKH/O2uUlwjfpveiJJJx8TwL7Se84A==", + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/flowgen/-/flowgen-1.21.0.tgz", + "integrity": "sha512-pFNFFyMLRmW6njhOIm5TrbGUDTv64aujmys2KrkRE2NYD8sXwJUyicQRwU5SPRBRJnFSD/FNlnHo2NnHI5eJSw==", "dev": true, "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/highlight": "^7.9.0", - "commander": "^5.1.0", - "lodash": "^4.17.15", - "prettier": "^2.0.5", + "@babel/code-frame": "^7.16.7", + "@babel/highlight": "^7.16.7", + "commander": "^6.1.0", + "lodash": "^4.17.20", + "prettier": "^2.5.1", "shelljs": "^0.8.4", - "typescript": "^3.4", + "typescript": "~4.4.4", "typescript-compiler": "^1.4.1-2" + }, + "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" + } + } } }, "fs.realpath": { @@ -406,6 +445,12 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, + "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", @@ -435,6 +480,15 @@ "@types/glob": "*" } }, + "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": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -497,6 +551,15 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, + "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": { + "has": "^1.0.3" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -598,9 +661,9 @@ "dev": true }, "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, "lru-queue": { @@ -646,9 +709,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true }, "mkdirp": { @@ -738,9 +801,9 @@ "dev": true }, "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "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": { @@ -804,27 +867,29 @@ } }, "prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true }, "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": { "resolve": "^1.1.6" } }, "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", "dev": true, "requires": { - "path-parse": "^1.0.6" + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" } }, "resolve-from": { @@ -855,9 +920,9 @@ "dev": true }, "shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "dev": true, "requires": { "glob": "^7.0.0", @@ -880,6 +945,12 @@ "has-flag": "^3.0.0" } }, + "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 + }, "thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -915,15 +986,15 @@ "dev": true }, "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.4.tgz", + "integrity": "sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==", "dev": true }, "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": { diff --git a/package.json b/package.json index 73c0d02..0a0215e 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "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: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", + "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", @@ -34,7 +34,7 @@ "url": "git+https://github.com/minswap/cardano-serialization-lib.git" }, "devDependencies": { - "flowgen": "1.11.0", + "flowgen": "1.21.0", "husky": "4.2.5", "json-schema-to-typescript": "^10.1.5", "rimraf": "3.0.2" diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 7ce9072..c58f0f2 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -4,18 +4,18 @@ version = 3 [[package]] name = "aho-corasick" -version = "0.7.15" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arrayvec" @@ -25,15 +25,18 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" [[package]] name = "autocfg" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +checksum = "0dde43e75fd43e8a1bf86103336bc699aa8d17ad1be60c76c0bdfd4828e19b78" +dependencies = [ + "autocfg 1.1.0", +] [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base16ct" @@ -67,9 +70,9 @@ checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "block-buffer" @@ -91,9 +94,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.4.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "cardano-serialization-lib" @@ -101,12 +104,12 @@ version = "11.1.0-minswap.13" dependencies = [ "bech32 0.7.3", "cbor_event", - "cfg-if 1.0.0", + "cfg-if", "clear_on_drop", "cryptoxide", "digest 0.9.0", "ed25519-bip32", - "getrandom 0.2.3", + "getrandom 0.2.10", "hex", "itertools", "js-sys", @@ -116,35 +119,32 @@ dependencies = [ "num-integer", "quickcheck", "quickcheck_macros", - "rand 0.8.4", + "rand 0.8.5", "rand_chacha 0.1.1", "rand_os", "schemars", "serde", "serde-wasm-bindgen", "serde_json", - "sha2 0.9.2", + "sha2 0.9.9", "uplc", "wasm-bindgen", ] [[package]] name = "cbor_event" -version = "2.1.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b6cda8a789815488ee290d106bc97dba47785dae73d63576fc42c126912a451" +checksum = "089a0261d1bc59e54e8e11860031efd88593f0e61b921172c474f1f38c2f2d3c" [[package]] name = "cc" -version = "1.0.62" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40" - -[[package]] -name = "cfg-if" -version = "0.1.10" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -154,9 +154,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clear_on_drop" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9cc5db465b294c3fa986d5bbb0f3017cd850bff6dd6c52f9ccff8b4d21b7b08" +checksum = "38508a63f4979f0048febc9966fadbd48e5dab31fd0ec6a3f151bbf4a74f7423" dependencies = [ "cc", ] @@ -172,30 +172,24 @@ dependencies = [ [[package]] name = "const-oid" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" [[package]] name = "cpufeatures" -version = "0.2.6" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4" dependencies = [ "libc", ] -[[package]] -name = "cpuid-bool" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" - [[package]] name = "crypto-bigint" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" +checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" dependencies = [ "generic-array", "rand_core 0.6.4", @@ -219,21 +213,11 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "382ce8820a5bb815055d3553a610e8cb542b2d767bbacea99038afda96cd760d" -[[package]] -name = "ctor" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096" -dependencies = [ - "quote", - "syn 1.0.109", -] - [[package]] name = "der" -version = "0.7.3" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b10af9f9f9f2134a42d3f8aa74658660f2e0234b0eb81bd171df8aa32779ed" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", "zeroize", @@ -256,9 +240,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "const-oid", @@ -268,21 +252,22 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.4" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee2626afccd7561a06cf1367e2950c4718ea04565e20fb5029b6c7d8ad09abcf" +checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" [[package]] name = "ecdsa" -version = "0.16.6" +version = "0.16.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd" +checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" dependencies = [ "der", - "digest 0.10.6", + "digest 0.10.7", "elliptic-curve", "rfc6979", "signature", + "spki", ] [[package]] @@ -296,19 +281,19 @@ dependencies = [ [[package]] name = "either" -version = "1.6.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "elliptic-curve" -version = "0.13.4" +version = "0.13.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" +checksum = "d97ca172ae9dc9f9b779a6e3a65d308f2af74e5b8c921299075bdb4a0370e914" dependencies = [ "base16ct", "crypto-bigint", - "digest 0.10.6", + "digest 0.10.7", "ff", "generic-array", "group", @@ -367,25 +352,25 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" dependencies = [ - "cfg-if 0.1.10", + "cfg-if", "libc", "wasi 0.9.0+wasi-snapshot-preview1", ] [[package]] name = "getrandom" -version = "0.2.3" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi 0.11.0+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -430,7 +415,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -439,7 +424,7 @@ version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "hashbrown", ] @@ -454,15 +439,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.55" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -473,52 +458,43 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cadb76004ed8e97623117f3df85b17aaa6626ab0b0831e6573f104df16cd1bcc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "ecdsa", "elliptic-curve", "once_cell", - "sha2 0.10.6", + "sha2 0.10.8", "signature", ] -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" -version = "0.2.141" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "linked-hash-map" -version = "0.5.3" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.3.4" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "miette" -version = "5.7.0" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abdc09c381c9336b9f2e9bd6067a9a5290d20e2d2e2296f275456121c33ae89" +checksum = "59bb584eaeeab6bd0226ccf3509a69d7936d148cf3d036ad350abe35e8c6856e" dependencies = [ "miette-derive", "once_cell", @@ -528,13 +504,13 @@ dependencies = [ [[package]] name = "miette-derive" -version = "5.7.0" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8842972f23939443013dfd3720f46772b743e86f1a81d120d4b6fb090f87de1c" +checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.38", ] [[package]] @@ -566,11 +542,11 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "num-integer", "num-traits", ] @@ -581,24 +557,24 @@ version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ - "autocfg 1.0.1", + "autocfg 1.1.0", ] [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "opaque-debug" @@ -606,20 +582,11 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "output_vt100" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" -dependencies = [ - "winapi", -] - [[package]] name = "pallas-addresses" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8af9e9683d7adb7c45b58bf8949c16af0556214c14b01cac92761b3c221377b9" +checksum = "8db28c4050dea032d497555bc68c269ae8e691486d8ec83f02b090487da0d0be" dependencies = [ "base58", "bech32 0.9.1", @@ -631,9 +598,9 @@ dependencies = [ [[package]] name = "pallas-codec" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "848a3cd192a1afa0e8c7121e40e0c5991715722399c51359dbf9246443a649fb" +checksum = "8b6e03d05d42a663526d78c8b1d4f2554f09bbf4cc846e1a9e839c558bf6103c" dependencies = [ "hex", "minicbor", @@ -642,9 +609,9 @@ dependencies = [ [[package]] name = "pallas-crypto" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d7e5248d3c81960fa2a1a430edc54d99601cf963ad7a4da8f9e13808d98c7f" +checksum = "4a35fc93b3613c0a628d0820f8d5d9a52709d795b59a1754a337aee0fca289dd" dependencies = [ "cryptoxide", "hex", @@ -656,9 +623,9 @@ dependencies = [ [[package]] name = "pallas-primitives" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "286a2d69437c9b3deaa58b8832edce3b80868ca6afeee8aff7d78f338ada9854" +checksum = "cc5fdf328f41971e0b1457e2377abeb09143fa50ab79f1a6a6ab5740bc94dc4b" dependencies = [ "base58", "bech32 0.9.1", @@ -672,9 +639,9 @@ dependencies = [ [[package]] name = "pallas-traverse" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "519f950d87a031770d302fdd510dc0b744c283b65d99a1a9896f399122a9c182" +checksum = "c58c353ecb175a63422386c80301493db9fc448407bc63322534522579e22879" dependencies = [ "hex", "pallas-addresses", @@ -686,9 +653,9 @@ dependencies = [ [[package]] name = "peg" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07f2cafdc3babeebc087e499118343442b742cc7c31b4d054682cc598508554" +checksum = "400bcab7d219c38abf8bd7cc2054eb9bbbd4312d66f6a5557d572a203f646f61" dependencies = [ "peg-macros", "peg-runtime", @@ -696,9 +663,9 @@ dependencies = [ [[package]] name = "peg-macros" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a90084dc05cf0428428e3d12399f39faad19b0909f64fb9170c9fdd6d9cd49b" +checksum = "46e61cce859b76d19090f62da50a9fe92bab7c2a5f09e183763559a2ac392c90" dependencies = [ "peg-runtime", "proc-macro2", @@ -707,9 +674,9 @@ dependencies = [ [[package]] name = "peg-runtime" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa00462b37ead6d11a82c9d568b26682d78e0477dc02d1966c013af80969739" +checksum = "36bae92c60fa2398ce4678b98b2c4b5a7c61099961ca1fa305aec04a9ad28922" [[package]] name = "pkcs8" @@ -723,9 +690,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.10" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "pretty" @@ -741,21 +708,19 @@ dependencies = [ [[package]] name = "pretty_assertions" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" +checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" dependencies = [ - "ctor", "diff", - "output_vt100", "yansi", ] [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -785,9 +750,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -798,23 +763,22 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" dependencies = [ - "getrandom 0.1.15", + "getrandom 0.1.16", "libc", "rand_chacha 0.2.2", "rand_core 0.5.1", - "rand_hc 0.2.0", + "rand_hc", ] [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", "rand_core 0.6.4", - "rand_hc 0.3.1", ] [[package]] @@ -823,7 +787,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" dependencies = [ - "autocfg 0.1.7", + "autocfg 0.1.8", "rand_core 0.3.1", ] @@ -868,7 +832,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" dependencies = [ - "getrandom 0.1.15", + "getrandom 0.1.16", ] [[package]] @@ -877,7 +841,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.3", + "getrandom 0.2.10", ] [[package]] @@ -889,15 +853,6 @@ dependencies = [ "rand_core 0.5.1", ] -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core 0.6.4", -] - [[package]] name = "rand_os" version = "0.1.3" @@ -924,21 +879,32 @@ dependencies = [ [[package]] name = "regex" -version = "1.4.2" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", "regex-syntax", - "thread_local", ] [[package]] name = "regex-syntax" -version = "0.6.21" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rfc6979" @@ -952,21 +918,21 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "schemars" -version = "0.8.8" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6b5a3c80cea1ab61f4260238409510e814e38b4b563c06044edf91e7dc070e3" +checksum = "1f7b0ce13155372a76ee2e1c5ffba1fe61ede73fbea5630d61eee6fac4929c0c" dependencies = [ "dyn-clone", "schemars_derive", @@ -976,9 +942,9 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.8" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ae4dce13e8614c46ac3c38ef1c0d668b101df6ac39817aebdaa26642ddae9b" +checksum = "e85e2a16b12bdb763244c69ab79363d71db2b4b918a2def53f80b02e0574b13c" dependencies = [ "proc-macro2", "quote", @@ -988,9 +954,9 @@ dependencies = [ [[package]] name = "sec1" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ "base16ct", "der", @@ -1020,18 +986,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.160" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] [[package]] name = "serde-wasm-bindgen" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfc62771e7b829b517cb213419236475f434fb480eddd76112ae182d274434a" +checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" dependencies = [ "js-sys", "serde", @@ -1040,20 +1006,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.38", ] [[package]] name = "serde_derive_internals" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dbab34ca63057a1f15280bdf3c39f2b1eb1b54c17e98360e511637aef7418c6" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" dependencies = [ "proc-macro2", "quote", @@ -1062,9 +1028,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -1073,26 +1039,26 @@ dependencies = [ [[package]] name = "sha2" -version = "0.9.2" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", - "cfg-if 1.0.0", - "cpuid-bool", + "cfg-if", + "cpufeatures", "digest 0.9.0", "opaque-debug", ] [[package]] name = "sha2" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", - "digest 0.10.6", + "digest 0.10.7", ] [[package]] @@ -1101,15 +1067,15 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" dependencies = [ - "digest 0.10.6", + "digest 0.10.7", "rand_core 0.6.4", ] [[package]] name = "spki" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", "der", @@ -1136,9 +1102,9 @@ dependencies = [ [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -1153,9 +1119,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -1164,31 +1130,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.40" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.40" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", -] - -[[package]] -name = "thread_local" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -dependencies = [ - "lazy_static", + "syn 2.0.38", ] [[package]] @@ -1199,15 +1156,15 @@ checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] name = "typenum" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" @@ -1217,9 +1174,9 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "uplc" @@ -1255,9 +1212,9 @@ dependencies = [ [[package]] name = "version_check" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" @@ -1267,40 +1224,40 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.78" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.78" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.38", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.78" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1308,22 +1265,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.78" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.38", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.78" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "winapi" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index c9da6e1..df50c92 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -45,8 +45,8 @@ getrandom = "0.2.3" # wasm [target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] -serde-wasm-bindgen = "0.4.3" -wasm-bindgen = "=0.2.78" +serde-wasm-bindgen = "0.4.5" +wasm-bindgen = "=0.2.87" 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 401c650..e91f3e0 100644 --- a/rust/json-gen/Cargo.lock +++ b/rust/json-gen/Cargo.lock @@ -869,9 +869,9 @@ dependencies = [ [[package]] name = "serde-wasm-bindgen" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfc62771e7b829b517cb213419236475f434fb480eddd76112ae182d274434a" +checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" dependencies = [ "js-sys", "serde", @@ -1104,9 +1104,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wasm-bindgen" -version = "0.2.78" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1114,13 +1114,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.78" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn 1.0.85", @@ -1129,9 +1129,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.78" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1139,9 +1139,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.78" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", @@ -1152,9 +1152,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.78" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "winapi" diff --git a/rust/pkg/cardano_serialization_lib.js.flow b/rust/pkg/cardano_serialization_lib.js.flow index d748ff5..a8f95c8 100644 --- a/rust/pkg/cardano_serialization_lib.js.flow +++ b/rust/pkg/cardano_serialization_lib.js.flow @@ -1,7 +1,7 @@ /** * Flowtype definitions for cardano_serialization_lib * Generated by Flowgen from a Typescript Definition - * Flowgen v1.11.0 + * Flowgen v1.21.0 * @flow */ @@ -152,6 +152,18 @@ 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 {TransactionHash} tx_body_hash * @param {ByronAddress} addr @@ -407,8 +419,8 @@ declare export var RedeemerTagKind: {| * 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 + * 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: {| @@ -789,6 +801,16 @@ declare export class AuxiliaryData { * @param {PlutusScripts} plutus_scripts */ set_plutus_scripts(plutus_scripts: PlutusScripts): void; + + /** + * @returns {boolean} + */ + prefer_alonzo_format(): boolean; + + /** + * @param {boolean} prefer + */ + set_prefer_alonzo_format(prefer: boolean): void; } /** */ @@ -1109,6 +1131,11 @@ declare export class BigNum { */ less_than(rhs_value: BigNum): boolean; + /** + * @returns {BigNum} + */ + static max_value(): BigNum; + /** * @param {BigNum} a * @param {BigNum} b @@ -2474,6 +2501,114 @@ declare export class ExUnits { */ static new(mem: BigNum, steps: BigNum): ExUnits; } +/** + */ +declare export class FixedTransaction { + free(): void; + + /** + * @returns {Uint8Array} + */ + to_bytes(): Uint8Array; + + /** + * @param {Uint8Array} bytes + * @returns {FixedTransaction} + */ + static from_bytes(bytes: Uint8Array): FixedTransaction; + + /** + * @returns {string} + */ + to_hex(): string; + + /** + * @param {string} hex_str + * @returns {FixedTransaction} + */ + static from_hex(hex_str: string): FixedTransaction; + + /** + * @param {Uint8Array} raw_body + * @param {Uint8Array} raw_witness_set + * @param {boolean} is_valid + * @returns {FixedTransaction} + */ + static new( + raw_body: Uint8Array, + raw_witness_set: Uint8Array, + is_valid: boolean + ): FixedTransaction; + + /** + * @param {Uint8Array} raw_body + * @param {Uint8Array} raw_witness_set + * @param {Uint8Array} raw_auxiliary_data + * @param {boolean} is_valid + * @returns {FixedTransaction} + */ + static new_with_auxiliary( + raw_body: Uint8Array, + raw_witness_set: Uint8Array, + raw_auxiliary_data: Uint8Array, + is_valid: boolean + ): FixedTransaction; + + /** + * @returns {TransactionBody} + */ + body(): TransactionBody; + + /** + * @returns {Uint8Array} + */ + raw_body(): Uint8Array; + + /** + * @param {Uint8Array} raw_body + */ + set_body(raw_body: Uint8Array): void; + + /** + * @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} + */ + raw_auxiliary_data(): Uint8Array | void; +} /** */ declare export class GeneralTransactionMetadata { @@ -3001,6 +3136,62 @@ declare export class HeaderBody { protocol_version: ProtocolVersion ): HeaderBody; } +/** + */ +declare export class InputWithScriptWitness { + free(): void; + + /** + * @param {TransactionInput} input + * @param {NativeScript} witness + * @returns {InputWithScriptWitness} + */ + static new_with_native_script_witness( + input: TransactionInput, + witness: NativeScript + ): InputWithScriptWitness; + + /** + * @param {TransactionInput} input + * @param {PlutusWitness} witness + * @returns {InputWithScriptWitness} + */ + static new_with_plutus_witness( + input: TransactionInput, + witness: PlutusWitness + ): InputWithScriptWitness; + + /** + * @returns {TransactionInput} + */ + input(): TransactionInput; +} +/** + */ +declare export class InputsWithScriptWitness { + free(): void; + + /** + * @returns {InputsWithScriptWitness} + */ + static new(): InputsWithScriptWitness; + + /** + * @param {InputWithScriptWitness} input + */ + add(input: InputWithScriptWitness): void; + + /** + * @param {number} index + * @returns {InputWithScriptWitness} + */ + get(index: number): InputWithScriptWitness; + + /** + * @returns {number} + */ + len(): number; +} /** */ declare export class Int { @@ -3370,6 +3561,11 @@ declare export class Languages { * @param {Language} elem */ add(elem: Language): void; + + /** + * @returns {Languages} + */ + static list(): Languages; } /** */ @@ -3697,11 +3893,20 @@ declare export class Mint { insert(key: ScriptHash, value: MintAssets): MintAssets | void; /** + * !!! DEPRECATED !!! + * Mint can store multiple entries for the same policy id. + * Use `.get_all` instead. * @param {ScriptHash} key * @returns {MintAssets | void} */ get(key: ScriptHash): MintAssets | void; + /** + * @param {ScriptHash} key + * @returns {MintsAssets | void} + */ + get_all(key: ScriptHash): MintsAssets | void; + /** * @returns {ScriptHashes} */ @@ -3759,6 +3964,91 @@ declare export class MintAssets { */ keys(): AssetNames; } +/** + */ +declare export class MintBuilder { + free(): void; + + /** + * @returns {MintBuilder} + */ + static new(): MintBuilder; + + /** + * @param {MintWitness} mint + * @param {AssetName} asset_name + * @param {Int} amount + */ + add_asset(mint: MintWitness, asset_name: AssetName, amount: Int): void; + + /** + * @param {MintWitness} mint + * @param {AssetName} asset_name + * @param {Int} amount + */ + set_asset(mint: MintWitness, asset_name: AssetName, amount: Int): void; + + /** + * @returns {Mint} + */ + build(): Mint; + + /** + * @returns {NativeScripts} + */ + get_native_scripts(): NativeScripts; + + /** + * @returns {PlutusWitnesses} + */ + get_plutus_witnesses(): PlutusWitnesses; + + /** + * @returns {TransactionInputs} + */ + get_ref_inputs(): TransactionInputs; + + /** + * @returns {Redeemers} + */ + get_redeeemers(): Redeemers; + + /** + * @returns {boolean} + */ + has_plutus_scripts(): boolean; + + /** + * @returns {boolean} + */ + has_native_scripts(): boolean; +} +/** + */ +declare export class MintWitness { + free(): void; + + /** + * @param {NativeScript} native_script + * @returns {MintWitness} + */ + static new_native_script(native_script: NativeScript): MintWitness; + + /** + * @param {PlutusScriptSource} plutus_script + * @param {Redeemer} redeemer + * @returns {MintWitness} + */ + static new_plutus_script( + plutus_script: PlutusScriptSource, + redeemer: Redeemer + ): MintWitness; +} +/** + */ +declare export class MintsAssets { + free(): void; +} /** */ declare export class MoveInstantaneousReward { @@ -4291,6 +4581,18 @@ declare export class NetworkInfo { /** * @returns {NetworkInfo} */ + static testnet_preview(): NetworkInfo; + + /** + * @returns {NetworkInfo} + */ + static testnet_preprod(): NetworkInfo; + + /** + * !!! DEPRECATED !!! + * This network does not exist anymore. Use `.testnet_preview()` or `.testnet_preprod()` + * @returns {NetworkInfo} + */ static testnet(): NetworkInfo; /** @@ -4434,6 +4736,33 @@ declare export class OperationalCert { sigma: Ed25519Signature ): OperationalCert; } +/** + */ +declare export class OutputDatum { + free(): void; + + /** + * @param {DataHash} data_hash + * @returns {OutputDatum} + */ + static new_data_hash(data_hash: DataHash): OutputDatum; + + /** + * @param {PlutusData} data + * @returns {OutputDatum} + */ + static new_data(data: PlutusData): OutputDatum; + + /** + * @returns {DataHash | void} + */ + data_hash(): DataHash | void; + + /** + * @returns {PlutusData | void} + */ + data(): PlutusData | void; +} /** */ declare export class PlutusData { @@ -4476,6 +4805,16 @@ declare export class PlutusData { */ static new_empty_constr_plutus_data(alternative: BigNum): PlutusData; + /** + * @param {BigNum} alternative + * @param {PlutusData} plutus_data + * @returns {PlutusData} + */ + static new_single_value_constr_plutus_data( + alternative: BigNum, + plutus_data: PlutusData + ): PlutusData; + /** * @param {PlutusMap} map * @returns {PlutusData} @@ -4542,6 +4881,12 @@ declare export class PlutusData { * @returns {PlutusData} */ static from_json(json: string, schema: number): PlutusData; + + /** + * @param {Address} address + * @returns {PlutusData} + */ + static from_address(address: Address): PlutusData; } /** */ @@ -4674,6 +5019,7 @@ declare export class PlutusScript { static from_hex(hex_str: string): 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. @@ -4683,6 +5029,7 @@ declare export class PlutusScript { 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. @@ -4692,6 +5039,7 @@ declare export class 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. @@ -4702,6 +5050,7 @@ declare export class PlutusScript { static new_with_version(bytes: Uint8Array, language: Language): PlutusScript; /** + * * * The raw bytes of this compiled Plutus script. * * If you need "cborBytes" for cardano-cli use PlutusScript::to_bytes() instead. * @returns {Uint8Array} @@ -4759,6 +5108,10 @@ declare export class PlutusScriptSource { static new(script: PlutusScript): PlutusScriptSource; /** + * !!! 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} @@ -4767,6 +5120,18 @@ declare export class PlutusScriptSource { script_hash: ScriptHash, input: TransactionInput ): PlutusScriptSource; + + /** + * @param {ScriptHash} script_hash + * @param {TransactionInput} input + * @param {Language} lang_ver + * @returns {PlutusScriptSource} + */ + static new_ref_input_with_lang_ver( + script_hash: ScriptHash, + input: TransactionInput, + lang_ver: Language + ): PlutusScriptSource; } /** */ @@ -4861,6 +5226,26 @@ declare export class PlutusWitness { redeemer: Redeemer ): PlutusWitness; + /** + * @param {PlutusScript} script + * @param {Redeemer} redeemer + * @returns {PlutusWitness} + */ + static new_without_datum( + script: PlutusScript, + redeemer: Redeemer + ): PlutusWitness; + + /** + * @param {PlutusScriptSource} script + * @param {Redeemer} redeemer + * @returns {PlutusWitness} + */ + static new_with_ref_without_datum( + script: PlutusScriptSource, + redeemer: Redeemer + ): PlutusWitness; + /** * @returns {PlutusScript | void} */ @@ -7529,6 +7914,38 @@ declare export class Transaction { witness_set: TransactionWitnessSet ): Transaction; } +/** + */ +declare export class TransactionBatch { + free(): void; + + /** + * @returns {number} + */ + len(): number; + + /** + * @param {number} index + * @returns {Transaction} + */ + get(index: number): Transaction; +} +/** + */ +declare export class TransactionBatchList { + free(): void; + + /** + * @returns {number} + */ + len(): number; + + /** + * @param {number} index + * @returns {TransactionBatch} + */ + get(index: number): TransactionBatch; +} /** */ declare export class TransactionBodies { @@ -8202,6 +8619,9 @@ declare export class TransactionBuilder { 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} */ @@ -8214,6 +8634,9 @@ declare export class TransactionBuilder { 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 @@ -8223,6 +8646,9 @@ declare export class TransactionBuilder { 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 @@ -8334,6 +8760,16 @@ declare export class TransactionBuilder { */ 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 @@ -8918,6 +9354,11 @@ declare export class TransactionOutput { * @returns {TransactionOutput} */ static new(address: Address, amount: Value): TransactionOutput; + + /** + * @returns {number | void} + */ + serialization_format(): number | void; } /** */ @@ -9403,6 +9844,8 @@ declare export class TxInputsBuilder { ): void; /** + * !!! 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` @@ -9483,6 +9926,8 @@ declare export class TxInputsBuilder { add_required_native_input_scripts(scripts: NativeScripts): number; /** + * !!! 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 @@ -9492,6 +9937,18 @@ declare export class TxInputsBuilder { */ add_required_plutus_input_scripts(scripts: PlutusWitnesses): 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 {InputsWithScriptWitness} inputs_with_wit + * @returns {number} + */ + add_required_script_input_witnesses( + inputs_with_wit: InputsWithScriptWitness + ): number; + /** * @returns {TransactionInputs} */ @@ -10117,6 +10574,44 @@ declare export class Vkeywitness { 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} */ @@ -10218,7 +10713,6 @@ export interface AssetsJSON { export interface AuxiliaryDataJSON { metadata?: { [k: string]: string, - ... } | null; native_scripts?: NativeScriptsJSON | null; plutus_scripts?: PlutusScriptsJSON | null; @@ -10233,7 +10727,6 @@ export type BigNumJSON = string; export interface BlockJSON { auxiliary_data_set: { [k: string]: AuxiliaryDataJSON, - ... }; header: HeaderJSON; invalid_transactions: number[]; @@ -10374,7 +10867,6 @@ export type MIREnumJSON = | { ToStakeCredentials: { [k: string]: ProtocolParamUpdateJSON, - ... }, ... }; @@ -10382,9 +10874,7 @@ export type MIRPotJSON = "Reserves" | "Treasury"; export interface MIRToStakeCredentialsJSON { [k: string]: ProtocolParamUpdateJSON; } -export interface MintJSON { - [k: string]: MintAssetsJSON; -} +export type MintJSON = [string, MintAssetsJSON][]; export interface MintAssetsJSON { [k: string]: string; } @@ -10651,7 +11141,6 @@ export interface TransactionBodyJSON { validity_start_interval?: string | null; withdrawals?: { [k: string]: ProtocolParamUpdateJSON, - ... } | null; } export type TransactionHashJSON = string; @@ -10691,7 +11180,6 @@ export interface UpdateJSON { epoch: number; proposed_protocol_parameter_updates: { [k: string]: ProtocolParamUpdateJSON, - ... }; } export interface VRFCertJSON { diff --git a/rust/src/address.rs b/rust/src/address.rs index e6c725b..4cfea39 100644 --- a/rust/src/address.rs +++ b/rust/src/address.rs @@ -53,6 +53,21 @@ impl NetworkInfo { 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, @@ -105,7 +120,7 @@ pub enum StakeCredKind { serde::Deserialize, JsonSchema, )] -pub struct StakeCredential(StakeCredType); +pub struct StakeCredential(pub(crate) StakeCredType); #[wasm_bindgen] impl StakeCredential { @@ -205,7 +220,7 @@ impl Deserialize for StakeCredential { } #[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] -enum AddrType { +pub(crate) enum AddrType { Base(BaseAddress), Ptr(PointerAddress), Enterprise(EnterpriseAddress), @@ -263,6 +278,12 @@ impl ByronAddress { 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}, )), @@ -315,7 +336,7 @@ impl ByronAddress { #[wasm_bindgen] #[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)] -pub struct Address(AddrType); +pub struct Address(pub(crate) AddrType); from_bytes!(Address, data, { Self::from_bytes_impl(data.as_ref()) }); @@ -567,6 +588,8 @@ impl Address { }; 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) @@ -1442,4 +1465,11 @@ mod tests { 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/crypto.rs b/rust/src/crypto.rs index c5c699a..38ba025 100644 --- a/rust/src/crypto.rs +++ b/rust/src/crypto.rs @@ -587,6 +587,8 @@ impl Deserialize for Vkeywitness { #[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 { diff --git a/rust/src/fees.rs b/rust/src/fees.rs index 4601261..1ef6c76 100644 --- a/rust/src/fees.rs +++ b/rust/src/fees.rs @@ -28,7 +28,11 @@ impl LinearFee { #[wasm_bindgen] pub fn min_fee(tx: &Transaction, linear_fee: &LinearFee) -> Result { - to_bignum(tx.to_bytes().len() as u64) + min_fee_for_size(tx.to_bytes().len(), linear_fee) +} + +pub fn min_fee_for_size(size: usize, linear_fee: &LinearFee) -> Result { + BigNum::from(size) .checked_mul(&linear_fee.coefficient())? .checked_add(&linear_fee.constant()) } @@ -146,7 +150,7 @@ mod tests { )); w.set_vkeys(&vkw); - let signed_tx = Transaction::new(&body, &w); + let signed_tx = Transaction::new(&body, &w, None); let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); assert_eq!( @@ -200,7 +204,7 @@ mod tests { )); w.set_bootstraps(&bootstrap_wits); - let signed_tx = Transaction::new(&body, &w); + let signed_tx = Transaction::new(&body, &w, None); let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); assert_eq!( @@ -287,7 +291,7 @@ mod tests { )); w.set_vkeys(&vkw); - let signed_tx = Transaction::new(&body, &w); + let signed_tx = Transaction::new(&body, &w, None); let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); assert_eq!( @@ -408,7 +412,7 @@ mod tests { )); w.set_vkeys(&vkw); - let signed_tx = Transaction::new(&body, &w); + let signed_tx = Transaction::new(&body, &w, None); let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); assert_eq!( @@ -621,7 +625,7 @@ mod tests { )); w.set_vkeys(&vkw); - let signed_tx = Transaction::new(&body, &w); + let signed_tx = Transaction::new(&body, &w, None); let linear_fee = LinearFee::new(&to_bignum(500), &to_bignum(2)); assert_eq!( diff --git a/rust/src/lib.rs b/rust/src/lib.rs index b0c00e4..3c1bf52 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -22,7 +22,7 @@ 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::*; +use wasm_bindgen::prelude::{JsValue, wasm_bindgen}; // This file was code-generated using an experimental CDDL to rust tool: // https://github.com/Emurgo/cddl-codegen @@ -52,10 +52,13 @@ pub mod traits; pub mod tx_builder; pub mod tx_builder_constants; 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; use crate::traits::NoneOrEmpty; use address::*; @@ -69,6 +72,7 @@ use std::collections::BTreeSet; use std::fmt::Display; use std::fmt; use utils::*; +use ser_info::types::*; type DeltaCoin = Int; @@ -108,7 +112,7 @@ type Slot32 = u32; type SlotBigNum = BigNum; #[wasm_bindgen] -#[derive(Clone, serde::Serialize, serde::Deserialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, JsonSchema)] pub struct Transaction { body: TransactionBody, witness_set: TransactionWitnessSet, @@ -147,12 +151,13 @@ impl Transaction { pub fn new( body: &TransactionBody, witness_set: &TransactionWitnessSet, + auxiliary_data: Option, ) -> Self { Self { body: body.clone(), witness_set: witness_set.clone(), is_valid: true, - auxiliary_data: None, + auxiliary_data: auxiliary_data.clone(), } } } @@ -222,6 +227,15 @@ impl TransactionOutputs { } } +impl<'a> IntoIterator for &'a TransactionOutputs { + type Item = &'a TransactionOutput; + type IntoIter = std::slice::Iter<'a, TransactionOutput>; + + fn into_iter(self) -> std::slice::Iter<'a, TransactionOutput> { + self.0.iter() + } +} + #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] enum DataCostEnum { CoinsPerWord(Coin), @@ -589,13 +603,16 @@ impl TransactionInput { #[wasm_bindgen] #[derive( - Debug, Clone, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, + Debug, Clone, Eq, Ord, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, )] pub struct TransactionOutput { address: Address, amount: Value, plutus_data: Option, script_ref: Option, + + #[serde(skip)] + serialization_format: Option, } impl_to_from!(TransactionOutput); @@ -664,8 +681,22 @@ impl TransactionOutput { amount: amount.clone(), plutus_data: None, script_ref: None, + serialization_format: None, } } + + pub fn serialization_format(&self) -> Option { + self.serialization_format.clone() + } +} + +impl PartialEq for TransactionOutput { + fn eq(&self, other: &Self) -> bool { + self.address == other.address + && self.amount == other.amount + && self.plutus_data == other.plutus_data + && self.script_ref == other.script_ref + } } #[wasm_bindgen] @@ -2098,6 +2129,36 @@ pub enum DataOption { Data(PlutusData), } +#[wasm_bindgen] +#[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())) + } + + pub fn new_data(data: &PlutusData) -> Self { + Self(DataOption::Data(data.clone())) + } + + pub fn data_hash(&self) -> Option { + match &self.0 { + DataOption::DataHash(data_hash) => Some(data_hash.clone()), + _ => None, + } + } + + pub fn data(&self) -> Option { + match &self.0 { + DataOption::Data(data) => Some(data.clone()), + _ => None, + } + } +} + #[wasm_bindgen] #[derive( Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, @@ -3124,7 +3185,7 @@ impl HeaderBody { } #[wasm_bindgen] -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct AssetName(Vec); impl Display for AssetName { @@ -3433,6 +3494,23 @@ impl PartialOrd for MultiAsset { } } +#[wasm_bindgen] +pub struct MintsAssets(Vec); + +impl MintsAssets { + pub fn new() -> Self { + Self(Vec::new()) + } + + pub fn add(&mut self, mint_assets: MintAssets) { + self.0.push(mint_assets) + } + + pub fn get(&self, index: usize) -> Option { + self.0.get(index).map(|v| v.clone()) + } +} + #[wasm_bindgen] #[derive( Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, @@ -3477,14 +3555,14 @@ impl MintAssets { #[derive( Clone, Debug, Eq, Ord, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema, )] -pub struct Mint(std::collections::BTreeMap); +pub struct Mint(Vec<(PolicyID, MintAssets)>); impl_to_from!(Mint); #[wasm_bindgen] impl Mint { pub fn new() -> Self { - Self(std::collections::BTreeMap::new()) + Self(Vec::new()) } pub fn new_from_entry(key: &PolicyID, value: &MintAssets) -> Self { @@ -3497,25 +3575,51 @@ impl Mint { self.0.len() } + //always returns None, because insert doesn't replace an old value pub fn insert(&mut self, key: &PolicyID, value: &MintAssets) -> Option { - self.0.insert(key.clone(), value.clone()) + self.0.push((key.clone(), value.clone())); + 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.get(key).map(|v| v.clone()) + 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 + .iter() + .filter(|(k, _)| k.eq(key)) + .map(|(_k, v)| v.clone()) + .collect(); + if mints.is_empty() { + None + } else { + Some(MintsAssets(mints)) + } } pub fn keys(&self) -> PolicyIDs { ScriptHashes( self.0 .iter() - .map(|(k, _v)| k.clone()) + .map(|(k, _)| k.clone()) .collect::>(), ) } fn as_multiasset(&self, is_positive: bool) -> MultiAsset { - self.0.iter().fold(MultiAsset::new(), |res, e| { + 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 { @@ -3523,13 +3627,13 @@ impl Mint { true => e.1.as_positive(), false => e.1.as_negative(), }; - assets.insert(e.0, &amount.unwrap()); + assets.insert(&e.0, &amount.unwrap()); } assets }); let mut ma = res; if !assets.0.is_empty() { - ma.insert(e.0, &assets); + ma.insert(&e.0, &assets); } ma }) @@ -3625,6 +3729,7 @@ impl From<&NativeScripts> for RequiredSignersSet { #[cfg(test)] mod tests { + use crate::tx_builder_constants::TxBuilderConstants; use super::*; #[test] @@ -3876,4 +3981,39 @@ mod tests { 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()); + } } diff --git a/rust/src/metadata.rs b/rust/src/metadata.rs index 54bec7c..ccd81ca 100644 --- a/rust/src/metadata.rs +++ b/rust/src/metadata.rs @@ -408,6 +408,14 @@ impl AuxiliaryData { 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 diff --git a/rust/src/output_builder.rs b/rust/src/output_builder.rs index 56dd816..a8bc769 100644 --- a/rust/src/output_builder.rs +++ b/rust/src/output_builder.rs @@ -152,6 +152,7 @@ impl TransactionOutputAmountBuilder { ))?, plutus_data: self.data.clone(), script_ref: self.script_ref.clone(), + serialization_format: None }) } } diff --git a/rust/src/plutus.rs b/rust/src/plutus.rs index c851bf7..fa5a46b 100644 --- a/rust/src/plutus.rs +++ b/rust/src/plutus.rs @@ -1,5 +1,8 @@ +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 @@ -288,7 +291,7 @@ impl PlutusScripts { } #[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd,)] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] pub struct ConstrPlutusData { alternative: BigNum, data: PlutusList, @@ -629,21 +632,21 @@ impl Languages { self.0.push(elem); } - pub(crate) fn list() -> Languages { + pub fn list() -> Languages { Languages(vec![Language::new_plutus_v1(), Language::new_plutus_v2()]) } } #[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd,)] -pub struct PlutusMap(std::collections::BTreeMap); +#[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(std::collections::BTreeMap::new()) + Self(LinkedHashMap::new()) } pub fn len(&self) -> usize { @@ -677,7 +680,7 @@ pub enum PlutusDataKind { } #[derive( - Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] + Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] pub enum PlutusDataEnum { ConstrPlutusData(ConstrPlutusData), Map(PlutusMap), @@ -701,6 +704,12 @@ impl std::cmp::PartialEq for PlutusData { } } +impl Hash for PlutusData { + fn hash(&self, state: &mut H) { + self.datum.hash(state) + } +} + impl std::cmp::Eq for PlutusData {} to_from_bytes!(PlutusData); @@ -719,6 +728,12 @@ impl PlutusData { 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()), @@ -799,6 +814,82 @@ impl PlutusData { 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 @@ -838,7 +929,7 @@ impl <'de> serde::de::Deserialize<'de> for PlutusData { } #[wasm_bindgen] -#[derive(Clone, Debug, Ord, PartialOrd, serde::Serialize, serde::Deserialize, JsonSchema)] +#[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 @@ -1769,7 +1860,7 @@ impl cbor_event::se::Serialize for PlutusMap { impl Deserialize for PlutusMap { fn deserialize(raw: &mut Deserializer) -> Result { - let mut table = std::collections::BTreeMap::new(); + let mut table = LinkedHashMap::new(); (|| -> Result<_, DeserializeError> { let len = raw.map()?; while match len { @@ -2499,7 +2590,7 @@ mod tests { let hash = hash_script_data(&redeemers, &retained_cost_models, Some(pdata)); assert_eq!( hex::encode(hash.to_bytes()), - "357041b88b914670a3b5e3b0861d47f2ac05ed4935ea73886434d8944aa6dfe0" + "2fd8b7e248b376314d02989c885c278796ab0e1d6e8aa0cb91f562ff5f7dbd70" ); } @@ -2539,7 +2630,7 @@ mod tests { &costmodels, None, ); - assert_eq!(hex::encode(hash.to_bytes()), "ac71f2adcaecd7576fa658098b12001dec03ce5c27dbb890e16966e3e135b3e2"); + assert_eq!(hex::encode(hash.to_bytes()), "6b244f15f895fd458a02bef3a8b56f17f24150fddcb06be482f8790a600578a1"); } #[test] @@ -2577,6 +2668,60 @@ mod tests { .retain_language_versions(&Languages(vec![Language::new_plutus_v1()])), Some(datums), ); - assert_eq!(hex::encode(hash.to_bytes()), "e6129f50a866d19d95bc9c95ee87b57a9e695c05d92ba2746141b03c15cf5f70"); + 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/fixed_tx.rs b/rust/src/protocol_types/fixed_tx.rs new file mode 100644 index 0000000..9e25863 --- /dev/null +++ b/rust/src/protocol_types/fixed_tx.rs @@ -0,0 +1,246 @@ +use crate::error::JsError; +use crate::*; +use std::io::{Seek, SeekFrom}; + +#[wasm_bindgen] +pub struct FixedTransaction { + body: TransactionBody, + body_bytes: Vec, + + witness_set: TransactionWitnessSet, + witness_bytes: Vec, + + is_valid: bool, + + auxiliary_data: Option, + auxiliary_bytes: Option>, +} + +to_from_bytes!(FixedTransaction); + + +#[wasm_bindgen] +impl FixedTransaction { + pub fn new( + raw_body: &[u8], + raw_witness_set: &[u8], + is_valid: bool, + ) -> Result { + let body = TransactionBody::from_bytes(raw_body.to_vec())?; + let witness_set = TransactionWitnessSet::from_bytes(raw_witness_set.to_vec())?; + + Ok(FixedTransaction { + body, + body_bytes: raw_body.to_vec(), + witness_set, + witness_bytes: raw_witness_set.to_vec(), + is_valid, + auxiliary_data: None, + auxiliary_bytes: None, + }) + } + + pub fn new_with_auxiliary( + raw_body: &[u8], + raw_witness_set: &[u8], + raw_auxiliary_data: &[u8], + is_valid: bool, + ) -> Result { + let body = TransactionBody::from_bytes(raw_body.to_vec())?; + let witness_set = TransactionWitnessSet::from_bytes(raw_witness_set.to_vec())?; + let auxiliary_data = Some(AuxiliaryData::from_bytes(raw_auxiliary_data.to_vec())?); + + Ok(FixedTransaction { + body, + body_bytes: raw_body.to_vec(), + witness_set, + witness_bytes: raw_witness_set.to_vec(), + is_valid, + auxiliary_data, + auxiliary_bytes: Some(raw_auxiliary_data.to_vec()) + }) + } + + pub fn body(&self) -> TransactionBody { + self.body.clone() + } + + pub fn raw_body(&self) -> Vec { + self.body_bytes.clone() + } + + pub fn set_body(&mut self, raw_body: &[u8]) -> Result<(), JsError> { + let body = TransactionBody::from_bytes(raw_body.to_vec())?; + self.body = body; + self.body_bytes = raw_body.to_vec(); + Ok(()) + } + + pub fn set_witness_set(&mut self, raw_witness_set: &[u8]) -> Result<(), JsError> { + let witness_set = TransactionWitnessSet::from_bytes(raw_witness_set.to_vec())?; + self.witness_set = witness_set; + self.witness_bytes = raw_witness_set.to_vec(); + Ok(()) + } + + pub fn witness_set(&self) -> TransactionWitnessSet { + self.witness_set.clone() + } + + pub fn raw_witness_set(&self) -> Vec { + self.witness_bytes.clone() + } + + pub fn set_is_valid(&mut self, valid: bool) { + self.is_valid = valid + } + + pub fn is_valid(&self) -> bool { + self.is_valid.clone() + } + + pub fn set_auxiliary_data(&mut self, raw_auxiliary_data: &[u8]) -> Result<(), JsError> { + let auxiliary_data = AuxiliaryData::from_bytes(raw_auxiliary_data.to_vec())?; + self.auxiliary_data = Some(auxiliary_data); + self.auxiliary_bytes = Some(raw_auxiliary_data.to_vec()); + Ok(()) + } + + pub fn auxiliary_data(&self) -> Option { + self.auxiliary_data.clone() + } + + pub fn raw_auxiliary_data(&self) -> Option> { + 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/mod.rs b/rust/src/protocol_types/mod.rs new file mode 100644 index 0000000..7eac86a --- /dev/null +++ b/rust/src/protocol_types/mod.rs @@ -0,0 +1,5 @@ +//TODO: move all protocol types to this module +pub mod fixed_tx; + +#[cfg(test)] +mod tests; \ No newline at end of file diff --git a/rust/src/protocol_types/tests.rs b/rust/src/protocol_types/tests.rs new file mode 100644 index 0000000..2ff510e --- /dev/null +++ b/rust/src/protocol_types/tests.rs @@ -0,0 +1,73 @@ +#[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/ser_info/mod.rs b/rust/src/ser_info/mod.rs new file mode 100644 index 0000000..dd198c6 --- /dev/null +++ b/rust/src/ser_info/mod.rs @@ -0,0 +1 @@ +pub mod types; \ No newline at end of file diff --git a/rust/src/ser_info/types.rs b/rust/src/ser_info/types.rs new file mode 100644 index 0000000..8da2707 --- /dev/null +++ b/rust/src/ser_info/types.rs @@ -0,0 +1,8 @@ +use crate::*; + +#[wasm_bindgen] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum CborContainerType { + Array = 0, + Map = 1, +} \ No newline at end of file diff --git a/rust/src/serialization.rs b/rust/src/serialization.rs index 6943f67..b9b02c9 100644 --- a/rust/src/serialization.rs +++ b/rust/src/serialization.rs @@ -836,6 +836,7 @@ impl DeserializeEmbeddedGroup for TransactionOutput { amount, plutus_data: data_hash, script_ref: None, + serialization_format: Some(CborContainerType::Array), }) } } @@ -928,6 +929,7 @@ fn deserialize_as_postalonzo_output( amount, plutus_data: data, script_ref, + serialization_format: Some(CborContainerType::Map), }) })() .map_err(|e| e.annotate("TransactionOutput")) @@ -3666,6 +3668,12 @@ impl cbor_event::se::Serialize for ProtocolParamUpdate { } + 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 { @@ -4808,11 +4816,11 @@ impl cbor_event::se::Serialize for Mint { impl Deserialize for Mint { fn deserialize(raw: &mut Deserializer) -> Result { - let mut table = std::collections::BTreeMap::new(); + let mut mints = Vec::new(); (|| -> Result<_, DeserializeError> { let len = raw.map()?; while match len { - cbor_event::Len::Len(n) => table.len() < n as usize, + cbor_event::Len::Len(n) => mints.len() < n as usize, cbor_event::Len::Indefinite => true, } { if raw.cbor_type()? == CBORType::Special { @@ -4821,17 +4829,12 @@ impl Deserialize for Mint { } let key = PolicyID::deserialize(raw)?; let value = MintAssets::deserialize(raw)?; - if table.insert(key.clone(), value).is_some() { - return Err(DeserializeFailure::DuplicateKey(Key::Str(String::from( - "some complicated/unsupported type", - ))) - .into()); - } + mints.push((key.clone(), value)); } Ok(()) })() .map_err(|e| e.annotate("Mint"))?; - Ok(Self(table)) + Ok(Self(mints)) } } @@ -4883,6 +4886,7 @@ mod tests { 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])); @@ -4908,6 +4912,7 @@ mod tests { 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))); @@ -4936,6 +4941,7 @@ mod tests { 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( @@ -4963,6 +4969,7 @@ mod tests { 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))); @@ -4988,6 +4995,7 @@ mod tests { 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)); @@ -5015,6 +5023,7 @@ mod tests { 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)); @@ -5041,6 +5050,7 @@ mod tests { 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)); @@ -5073,6 +5083,7 @@ mod tests { 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])); @@ -5099,6 +5110,7 @@ mod tests { 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))); @@ -5128,6 +5140,7 @@ mod tests { 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( @@ -5156,6 +5169,7 @@ mod tests { 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))); @@ -5182,6 +5196,7 @@ mod tests { 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)); @@ -5210,6 +5225,7 @@ mod tests { 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)); @@ -5237,6 +5253,7 @@ mod tests { 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)); @@ -5468,4 +5485,13 @@ mod tests { 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_tools/map_names.rs b/rust/src/serialization_tools/map_names.rs new file mode 100644 index 0000000..1b4000c --- /dev/null +++ b/rust/src/serialization_tools/map_names.rs @@ -0,0 +1,44 @@ + +#[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 new file mode 100644 index 0000000..06cc18f --- /dev/null +++ b/rust/src/serialization_tools/mod.rs @@ -0,0 +1 @@ +pub mod map_names; \ No newline at end of file diff --git a/rust/src/tx_builder.rs b/rust/src/tx_builder.rs index 569d322..25e0c70 100644 --- a/rust/src/tx_builder.rs +++ b/rust/src/tx_builder.rs @@ -1,6 +1,12 @@ #![allow(deprecated)] +#[cfg(test)] +mod test_batch; + pub mod tx_inputs_builder; +pub mod tx_batch_builder; +pub mod mint_builder; +mod batch_tools; use super::fees; use super::output_builder::TransactionOutputAmountBuilder; @@ -8,8 +14,10 @@ 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, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use tx_inputs_builder::{PlutusWitness, PlutusWitnesses}; +use crate::tx_builder::mint_builder::{MintBuilder, MintWitness}; + use uplc::tx::eval_phase_two_raw; #[wasm_bindgen] @@ -105,7 +113,7 @@ fn witness_keys_for_cert(cert_enum: &Certificate) -> RequiredSigners { set } -fn fake_private_key() -> Bip32PrivateKey { +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, @@ -118,7 +126,7 @@ fn fake_private_key() -> Bip32PrivateKey { .unwrap() } -fn fake_raw_key_sig() -> Ed25519Signature { +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, @@ -128,7 +136,7 @@ fn fake_raw_key_sig() -> Ed25519Signature { .unwrap() } -fn fake_raw_key_public() -> PublicKey { +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, @@ -140,8 +148,8 @@ 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(scripts) = &tx_builder.mint_scripts { - input_hashes.extend(RequiredSignersSet::from(scripts)); + if let Some(mint_builder) = &tx_builder.mint { + input_hashes.extend(RequiredSignersSet::from(&mint_builder.get_native_scripts())); } input_hashes.len() } @@ -190,7 +198,7 @@ fn fake_full_tx( (tx_builder.plutus_scripts.clone(), tx_builder.plutus_data.clone(), tx_builder.redeemers.clone()) } else if let Some(s) = tx_builder.get_combined_plutus_scripts() { let (s, d, r) = s.collect(); - (Some(s), Some(d), Some(r)) + (Some(s), d, Some(r)) } else { (None, None, None) } @@ -425,8 +433,7 @@ pub struct TransactionBuilder { withdrawals: Option, auxiliary_data: Option, validity_start_interval: Option, - mint: Option, - mint_scripts: Option, + mint: Option, script_data_hash: Option, required_signers: Ed25519KeyHashes, collateral_return: Option, @@ -924,7 +931,7 @@ impl TransactionBuilder { /// 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 `.set_inputs`")] + #[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() } @@ -1125,16 +1132,16 @@ impl TransactionBuilder { Ok(()) } - pub fn get_plutus_scripts(&self) -> Option { - self.plutus_scripts.clone() + pub fn set_mint_builder(&mut self, mint_builder: &MintBuilder) { + self.mint = Some(mint_builder.clone()); } - pub fn set_plutus_scripts(&mut self, plutus_scripts: &PlutusScripts) { - self.plutus_scripts = Some(plutus_scripts.clone()) + pub fn get_mint_builder(&self) -> Option { + self.mint.clone() } - pub fn get_plutus_data(&self) -> Option { - self.plutus_data.clone() + pub fn set_plutus_scripts(&mut self, plutus_scripts: &PlutusScripts) { + self.plutus_scripts = Some(plutus_scripts.clone()) } pub fn set_plutus_data(&mut self, plutus_data: &PlutusList) { @@ -1155,86 +1162,110 @@ impl TransactionBuilder { inputs.0.iter().position(|i| i == input).unwrap() } - pub fn get_inputs(&self) -> TransactionInputs { - self.inputs.inputs().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> { - if !self.minswap_mode { - assert_required_mint_scripts(mint, Some(mint_scripts))?; + 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()); } - self.mint = Some(mint.clone()); - self.mint_scripts = Some(mint_scripts.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(()) } + /// 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> { + // if !self.minswap_mode { + // assert_required_mint_scripts(mint, Some(mint_scripts))?; + // } + // self.mint = Some(mint.clone()); + // self.mint_scripts = Some(mint_scripts.clone()); + // 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 { - self.mint.clone() + 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 { - self.mint_scripts.clone() - } - - fn _set_mint_asset( - &mut self, - policy_id: &PolicyID, - policy_script: &NativeScript, - mint_assets: &MintAssets, - ) { - let mut mint = self.mint.as_ref().cloned().unwrap_or(Mint::new()); - let is_new_policy = mint.insert(&policy_id, mint_assets).is_none(); - let mint_scripts = { - let mut witness_scripts = self - .mint_scripts - .as_ref() - .cloned() - .unwrap_or(NativeScripts::new()); - if is_new_policy { - // If policy has not been encountered before - insert the script into witnesses - witness_scripts.add(&policy_script.clone()); - } - witness_scripts - }; - self.mint = Some(mint); - self.mint_scripts = Some(mint_scripts.clone()); + 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 policy_id: PolicyID = policy_script.hash(); - self._set_mint_asset(&policy_id, policy_script, mint_assets); - } - - fn _add_mint_asset( - &mut self, - policy_id: &PolicyID, - policy_script: &NativeScript, - asset_name: &AssetName, - amount: Int, - ) { - let mut asset = self - .mint - .as_ref() - .map(|m| m.get(&policy_id).as_ref().cloned()) - .unwrap_or(None) - .unwrap_or(MintAssets::new()); - if let Some(mint_amount) = asset.get(asset_name) { - let new_amount = mint_amount.0 + amount.0; - asset.insert(asset_name, Int(new_amount)); + 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 { - asset.insert(asset_name, amount); + let mut mint = MintBuilder::new(); + for (asset, amount) in mint_assets.0.iter() { + mint.set_asset(&mint_witness, asset, amount); + } + self.mint = Some(mint); } - self._set_mint_asset(&policy_id, policy_script, &asset); } + /// !!! 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 @@ -1244,8 +1275,14 @@ impl TransactionBuilder { asset_name: &AssetName, amount: Int, ) { - let policy_id: PolicyID = policy_script.hash(); - self._add_mint_asset(&policy_id, policy_script, asset_name, amount); + 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 @@ -1264,7 +1301,7 @@ impl TransactionBuilder { return Err(JsError::from_str("Output value must be positive!")); } let policy_id: PolicyID = policy_script.hash(); - self._add_mint_asset(&policy_id, policy_script, asset_name, amount.clone()); + 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()), @@ -1294,7 +1331,7 @@ impl TransactionBuilder { return Err(JsError::from_str("Output value must be positive!")); } let policy_id: PolicyID = policy_script.hash(); - self._add_mint_asset(&policy_id, policy_script, asset_name, amount.clone()); + 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()), @@ -1324,7 +1361,6 @@ impl TransactionBuilder { auxiliary_data: None, validity_start_interval: None, mint: None, - mint_scripts: None, script_data_hash: None, required_signers: Ed25519KeyHashes::new(), collateral_return: None, @@ -1343,6 +1379,12 @@ impl TransactionBuilder { inputs.insert(input); } + if let Some(mint) = &self.mint { + for input in mint.get_ref_inputs().0 { + inputs.insert(input); + } + } + let vec_inputs = inputs.into_iter().collect(); TransactionInputs(vec_inputs) } @@ -1372,8 +1414,8 @@ impl TransactionBuilder { .as_ref() .map(|m| { ( - Value::new_from_assets(&m.as_positive_multiasset()), - Value::new_from_assets(&m.as_negative_multiasset()), + Value::new_from_assets(&m.build().as_positive_multiasset()), + Value::new_from_assets(&m.build().as_negative_multiasset()), ) }) .unwrap_or((Value::zero(), Value::zero())) @@ -1422,6 +1464,26 @@ impl TransactionBuilder { /// 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 @@ -1432,11 +1494,6 @@ impl TransactionBuilder { } }?; - // note: can't add plutus data or data hash and script to change - // because we don't know how many change outputs will need to be created - let plutus_data: Option = None; - let script_ref: Option = None; - let input_total = self.get_total_input()?; let output_total = self.get_total_output()?; @@ -1504,6 +1561,7 @@ impl TransactionBuilder { 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. @@ -1563,6 +1621,7 @@ impl TransactionBuilder { 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 @@ -1655,6 +1714,7 @@ impl TransactionBuilder { amount: change_value.clone(), plutus_data: plutus_data.clone(), script_ref: script_ref.clone(), + serialization_format: None, }; // increase fee @@ -1676,6 +1736,7 @@ impl TransactionBuilder { 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 = @@ -1690,6 +1751,7 @@ impl TransactionBuilder { amount: potential_pure_value.clone(), plutus_data: plutus_data.clone(), script_ref: script_ref.clone(), + serialization_format: None, })?; } } @@ -1737,6 +1799,7 @@ impl TransactionBuilder { 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)?; @@ -1754,6 +1817,7 @@ impl TransactionBuilder { .checked_sub(&Value::new(&new_fee.clone()))?, plutus_data: plutus_data.clone(), script_ref: script_ref.clone(), + serialization_format: None, })?; Ok(true) @@ -1769,6 +1833,7 @@ impl TransactionBuilder { } } + /// 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 @@ -1779,28 +1844,41 @@ impl TransactionBuilder { /// 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(); - if let Some(pw) = self.inputs.get_plutus_input_scripts() { - let (scripts, datums, redeemers) = pw.collect(); - for lang in Languages::list().0 { - if scripts.has_version(&lang) { - 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 - ))) - } + 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 plutus_witnesses.len() > 0 { + let (_scripts, 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 + ))) } } } self.script_data_hash = Some(hash_script_data( &redeemers, &retained_cost_models, - Some(datums), + datums, )); } Ok(()) @@ -1840,7 +1918,10 @@ impl TransactionBuilder { Some(x) => Some(utils::hash_auxiliary_data(x)), }, validity_start_interval: self.validity_start_interval, - mint: self.mint.clone(), + mint: match &self.mint { + None => None, + Some(mint_builder) => Some(mint_builder.build()), + }, script_data_hash: self.script_data_hash.clone(), collateral: self.collateral.inputs_option(), required_signers: self.required_signers.to_option(), @@ -1898,8 +1979,8 @@ impl TransactionBuilder { ns.add(s); }); } - if let Some(mint_scripts) = &self.mint_scripts { - mint_scripts.0.iter().for_each(|s| { + if let Some(mint_builder) = &self.mint { + mint_builder.get_native_scripts().0.iter().for_each(|s| { ns.add(s); }); } @@ -1922,6 +2003,11 @@ impl TransactionBuilder { res.add(s); }) } + if let Some(mint_builder) = &self.mint { + mint_builder.get_plutus_witnesses().0.iter().for_each(|s| { + res.add(s); + }) + } if res.len() > 0 { Some(res) } else { @@ -1952,7 +2038,9 @@ impl TransactionBuilder { if let Some(pw) = self.get_combined_plutus_scripts() { let (scripts, datums, redeemers) = pw.collect(); wit.set_plutus_scripts(&scripts); - wit.set_plutus_data(&datums); + if let Some(datums) = &datums { + wit.set_plutus_data(datums); + } wit.set_redeemers(&redeemers); } } @@ -1960,7 +2048,9 @@ impl TransactionBuilder { } fn has_plutus_inputs(&self) -> bool { - self.inputs.get_plutus_input_scripts().is_some() + let has_in_inputs = self.inputs.get_plutus_input_scripts().is_some(); + let has_in_mint = self.mint.as_ref().map_or(false, |m| m.has_plutus_scripts()); + return has_in_inputs || has_in_mint; } /// Returns full Transaction object with the body and the auxiliary data @@ -2006,19 +2096,19 @@ impl TransactionBuilder { self_copy.set_fee(&to_bignum(0x1_00_00_00_00)); min_fee(&self_copy) } - - pub fn fake_tx_full_size(&self) -> Result { - let mut self_copy = self.clone(); - self_copy.set_fee(&to_bignum(0x1_00_00_00_00)); - Ok(self_copy.build_and_size()?.1) - } } #[cfg(test)] mod tests { use super::output_builder::TransactionOutputBuilder; use super::*; - use crate::fakes::{fake_base_address, fake_bytes_32, fake_key_hash, fake_policy_id, fake_tx_hash, fake_tx_input, fake_tx_input2, fake_value, fake_value2}; + use crate::fakes::{ + fake_base_address, fake_bytes_32, fake_data_hash, fake_key_hash, fake_policy_id, + fake_tx_hash, fake_tx_input, fake_tx_input2, fake_value, fake_value2, + }; + use crate::tx_builder::tx_inputs_builder::{ + InputWithScriptWitness, InputsWithScriptWitness, PlutusScriptSource, + }; use crate::tx_builder_constants::TxBuilderConstants; use fees::*; @@ -2230,6 +2320,87 @@ mod tests { 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(); @@ -4674,6 +4845,14 @@ mod tests { 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)); @@ -4864,7 +5043,7 @@ mod tests { )); witness_set.set_vkeys(&vkw); - let _final_tx = Transaction::new(&body, &witness_set); + 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!( @@ -5202,10 +5381,10 @@ mod tests { tx_builder.set_mint_asset(&mint_script, &create_mint_asset()); assert!(tx_builder.mint.is_some()); - assert!(tx_builder.mint_scripts.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(); - let mint_scripts = tx_builder.mint_scripts.unwrap(); + let mint = tx_builder.mint.unwrap().build(); assert_eq!(mint.len(), 1); assert_mint_asset(&mint, &policy_id); @@ -5231,10 +5410,10 @@ mod tests { tx_builder.set_mint_asset(&mint_script2, &create_mint_asset()); assert!(tx_builder.mint.is_some()); - assert!(tx_builder.mint_scripts.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(); - let mint_scripts = tx_builder.mint_scripts.unwrap(); + let mint = tx_builder.mint.unwrap().build(); assert_eq!(mint.len(), 2); assert_mint_asset(&mint, &policy_id1); @@ -5242,8 +5421,9 @@ mod tests { // Only second script is present in the scripts assert_eq!(mint_scripts.len(), 2); - assert_eq!(mint_scripts.get(0), mint_script1); - assert_eq!(mint_scripts.get(1), mint_script2); + 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] @@ -5255,10 +5435,10 @@ mod tests { tx_builder.add_mint_asset(&mint_script, &create_asset_name(), Int::new_i32(1234)); assert!(tx_builder.mint.is_some()); - assert!(tx_builder.mint_scripts.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(); - let mint_scripts = tx_builder.mint_scripts.unwrap(); + let mint = tx_builder.mint.unwrap().build(); assert_eq!(mint.len(), 1); assert_mint_asset(&mint, &policy_id); @@ -5283,18 +5463,19 @@ mod tests { tx_builder.add_mint_asset(&mint_script2, &create_asset_name(), Int::new_i32(1234)); assert!(tx_builder.mint.is_some()); - assert!(tx_builder.mint_scripts.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(); - let mint_scripts = tx_builder.mint_scripts.unwrap(); + 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); - assert_eq!(mint_scripts.get(0), mint_script1); - assert_eq!(mint_scripts.get(1), mint_script2); + 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] @@ -5445,10 +5626,10 @@ mod tests { .unwrap(); assert!(tx_builder.mint.is_some()); - assert!(tx_builder.mint_scripts.is_some()); + let mint_scripts = &tx_builder.mint.as_ref().unwrap().get_native_scripts(); + assert!(mint_scripts.len() > 0); - let mint = tx_builder.mint.as_ref().unwrap(); - let mint_scripts = tx_builder.mint_scripts.as_ref().unwrap(); + let mint = &tx_builder.mint.unwrap().build(); // Mint contains two entries assert_eq!(mint.len(), 2); @@ -5456,8 +5637,9 @@ mod tests { assert_mint_asset(mint, &policy_id1); assert_eq!(mint_scripts.len(), 2); - assert_eq!(mint_scripts.get(0), mint_script0); - assert_eq!(mint_scripts.get(1), mint_script1); + 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); @@ -5506,10 +5688,10 @@ mod tests { .unwrap(); assert!(tx_builder.mint.is_some()); - assert!(tx_builder.mint_scripts.is_some()); + let mint_scripts = tx_builder.mint.as_ref().unwrap().get_native_scripts(); + assert!(mint_scripts.len() > 0); - let mint = tx_builder.mint.as_ref().unwrap(); - let mint_scripts = tx_builder.mint_scripts.as_ref().unwrap(); + let mint = &tx_builder.mint.unwrap().build(); // Mint contains two entries assert_eq!(mint.len(), 2); @@ -5517,8 +5699,9 @@ mod tests { assert_mint_asset(mint, &policy_id1); assert_eq!(mint_scripts.len(), 2); - assert_eq!(mint_scripts.get(0), mint_script0); - assert_eq!(mint_scripts.get(1), mint_script1); + 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); @@ -6182,9 +6365,12 @@ mod tests { 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(), + &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); @@ -7692,10 +7878,434 @@ mod tests { }, \"script_ref\": null }").unwrap(); - let addr= Address::from_bech32("addr_test1wpv93hm9sqx0ar7pgxwl9jn3xt6lwmxxy27zd932slzvghqg8fe0n").unwrap(); let mut builder = create_reallistic_tx_builder(); - builder.add_output(&output); + 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(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); + 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()); + 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); + } } diff --git a/rust/src/tx_builder/batch_tools/asset_categorizer.rs b/rust/src/tx_builder/batch_tools/asset_categorizer.rs new file mode 100644 index 0000000..2d5f672 --- /dev/null +++ b/rust/src/tx_builder/batch_tools/asset_categorizer.rs @@ -0,0 +1,603 @@ +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::utxo_stat::UtxosStat; +use super::indexes::{UtxoIndex, AssetIndex, PolicyIndex, PlaneAssetId}; +use super::super::*; + +#[derive(Clone)] +pub(crate) struct TxProposalChanges { + tx_proposal: TxProposal, + makes_new_outputs: bool, + asset_utxo: Vec, + ada_utxos: Vec, +} + +impl TxProposalChanges { + pub(crate) fn new(tx_proposal: TxProposal, makes_new_outputs: bool) -> Self { + TxProposalChanges { + tx_proposal, + makes_new_outputs, + asset_utxo: Vec::new(), + ada_utxos: Vec::new(), + } + } +} + +pub struct AssetCategorizer { + address: Address, + config: TransactionBuilderConfig, + assets: Vec, + policies: Vec, + assets_calculator: AssetsCalculator, + assets_amounts: Vec>, + assets_counts: Vec<(AssetIndex, usize)>, + utxos_ada: Vec, + addresses: Vec
, + + //assets and utoxs that can be used + free_utxo_to_assets: HashMap>, + free_asset_to_utxos: HashMap>, + + asset_to_policy: HashMap, + policy_to_asset: HashMap>, + inputs_sizes: Vec, + + 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 { + let mut assets: Vec = Vec::new(); + let mut utxos_ada: Vec = Vec::new(); + let mut policies: Vec = Vec::new(); + let mut assets_name_sizes: Vec = Vec::new(); + let mut assets_amounts: Vec> = Vec::new(); + //let mut assets_counts: Vec<(AssetIndex, usize)> = Vec::new(); + let mut free_utxo_to_assets: HashMap> = HashMap::new(); + let mut free_asset_to_utxos: HashMap> = HashMap::new(); + let mut asset_to_policy: HashMap = HashMap::new(); + let mut policy_to_asset: HashMap> = HashMap::new(); + + let mut asset_ids: HashMap = HashMap::new(); + let mut policy_ids: HashMap = HashMap::new(); + let mut assets_counts: Vec<(AssetIndex, usize)> = Vec::new(); + + let mut current_utxo_num = 0usize; + let mut asset_count = 0usize; + let mut policy_count = 0usize; + let mut total_ada = Coin::zero(); + + let mut free_ada_utxos = Vec::new(); + let mut addresses = Vec::new(); + + let mut utxos_with_ada_overhead = Vec::new(); + + for utxo in &utxos.0 { + total_ada = total_ada.checked_add(&utxo.output.amount.coin)?; + + let current_utxo_index = UtxoIndex(current_utxo_num.clone()); + 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)?; + if ada_overhead > Coin::zero() { + utxos_with_ada_overhead.push((current_utxo_index.clone(), ada_overhead)); + } + + if let Some(assests) = &utxo.output.amount.multiasset { + for policy in &assests.0 { + let mut current_policy_index = PolicyIndex(policy_count.clone()); + if let Some(policy_index) = policy_ids.get(policy.0) { + current_policy_index = policy_index.clone() + } else { + policies.push(policy.0.clone()); + policy_ids.insert(policy.0.clone(), current_policy_index.clone()); + policy_count += 1; + } + + 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()); + + if let Some(asset_index) = asset_ids.get(&plane_id) { + 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(); + assets.push(plane_id.clone()); + assets_name_sizes.push(asset_name_size); + asset_ids.insert(plane_id, current_asset_index.clone()); + assets_counts.push((current_asset_index.clone(), 0)); + assets_amounts.push(HashMap::new()); + asset_count += 1; + } + + 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()); + if let Some(assets_set) = policy_to_asset.get_mut(¤t_policy_index) { + assets_set.insert(current_asset_index.clone()); + } else { + let mut assets_set = HashSet::new(); + assets_set.insert(current_asset_index.clone()); + policy_to_asset.insert(current_policy_index.clone(), assets_set); + } + + if let Some(utxo_set) = free_asset_to_utxos.get_mut(¤t_asset_index) { + utxo_set.insert(current_utxo_index.clone()); + } else { + let mut utxo_set = HashSet::new(); + utxo_set.insert(current_utxo_index.clone()); + free_asset_to_utxos.insert(current_asset_index.clone(), utxo_set); + } + + if let Some(assets_set) = free_utxo_to_assets.get_mut(¤t_utxo_index) { + assets_set.insert(current_asset_index.clone()); + } else { + let mut assets_set = HashSet::new(); + assets_set.insert(current_asset_index.clone()); + free_utxo_to_assets.insert(current_utxo_index.clone(), assets_set); + } + } + } + } else { + free_ada_utxos.push((current_utxo_index.clone(), utxo.output.amount.coin.clone())); + } + current_utxo_num += 1; + } + + let utxos_stat = UtxosStat::new(&total_ada, &policy_to_asset, &assets_amounts)?; + let assets_calculator = AssetsCalculator::new(utxos_stat, assets_name_sizes); + let inputs_sizes = Self::get_inputs_sizes(&utxos); + + assets_counts.sort_by(|a, b| b.1.cmp(&a.1)); + free_ada_utxos.sort_by(|a, b| a.1.cmp(&b.1)); + utxos_with_ada_overhead.sort_by(|a, b| a.1.cmp(&b.1)); + let output_size = CborCalculator::get_output_size(address); + + Ok(Self { + address: address.clone(), + config: config.clone(), + addresses, + assets, + policies, + assets_calculator, + assets_amounts, + assets_counts, + utxos_ada, + free_utxo_to_assets, + free_asset_to_utxos, + asset_to_policy, + policy_to_asset, + inputs_sizes, + free_ada_utxos, + //utxos_with_ada_overhead, + output_size, + }) + } + + pub(crate) fn has_assets(&self) -> bool { + !self.free_asset_to_utxos.is_empty() + } + + pub(crate) fn has_ada(&self) -> bool { + !self.free_ada_utxos.is_empty() + } + + 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); + } + let mut multiasset = MultiAsset::new(); + for (policy_index, assets) in &tx_output_proposal.grouped_assets { + for asset_index in assets { + let mut asset_coins = Coin::zero(); + for utxo in used_utxos { + if let Some(coins) = self.assets_amounts[asset_index.0].get(utxo) { + asset_coins = asset_coins.checked_add(coins)?; + } + } + multiasset.set_asset(&self.policies[policy_index.0], &self.assets[asset_index.0].1, asset_coins); + } + } + + value.set_multiasset(&multiasset); + Ok(value) + } + + 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)?; + } else if self.has_ada() { + proposal_changes = self.try_append_pure_ada_utxo(tx_proposal)?; + } + + if let Some(proposal_changes) = proposal_changes { + + for utxo in proposal_changes.asset_utxo { + self.remove_assets_utxo(&utxo); + } + + for utxo in proposal_changes.ada_utxos { + self.remove_pure_ada_utxo(&utxo); + } + + Ok(Some(proposal_changes.tx_proposal)) + } else { + Ok(None) + } + } + + 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)?; + 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)?; + if let Some(res_utxo) = policy_intersected_utxo { + return Ok(Some(res_utxo)); + } + + self.make_candidate(&self.assets_counts, tx_proposal, true) + } + + 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() { + if let Some((utxo, coin)) = &self.get_next_pure_ada_utxo() { + if new_proposal.get_outputs().is_empty() { + new_proposal.add_new_output(&self.address); + } + new_proposal.add_utxo(utxo, coin, &self.addresses[utxo.0])?; + used_utxos.insert(utxo.clone()); + } else { + return Ok(None); + } + } + + 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)?; + + for (utxo, coin) in &next_utxos { + new_proposal.add_utxo(utxo, coin, &self.addresses[utxo.0])?; + used_utxos.insert(utxo.clone()); + } + + 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.")); + } + } + + if new_size > (self.config.max_tx_size as usize) { + return Ok(None); + } + + let mut changes = TxProposalChanges::new(new_proposal, false); + changes.ada_utxos = used_utxos.into_iter().collect(); + + Ok(Some(changes)) + } + + fn get_inputs_sizes(utoxs: &TransactionUnspentOutputs) -> Vec { + let mut sizes = Vec::with_capacity(utoxs.0.len()); + for utxo in &utoxs.0 { + let len = utxo.input.to_bytes().len(); + sizes.push(len); + } + sizes + } + + 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) { + intersections.push((index.clone(), asset_count.clone())); + } + } + intersections + } + + fn get_policy_intersections(&self, used_assets: &HashSet) -> Vec<(AssetIndex, usize)> { + let mut intersections = Vec::new(); + 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)) + .flatten() + .cloned() + .collect(); + for (index, asset_count) in &self.assets_counts { + if available_assets.contains(index) && self.free_asset_to_utxos.contains_key(index) { + intersections.push((index.clone(), asset_count.clone())); + } + } + intersections + } + + 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() { + Some(output) => output.used_assets.clone(), + None => HashSet::new(), + }; + + let used_assets = new_proposal.get_used_assets(); + if let Some(utxo_assets) = utxo_assets { + let output_intersection = &used_assets_in_output & utxo_assets; + let rest_assets = utxo_assets - &output_intersection; + let asset_for_old_ouputs = &rest_assets & used_assets; + let asset_for_add = &(utxo_assets - &output_intersection) - &asset_for_old_ouputs; + + if new_proposal.get_outputs().is_empty() { + new_proposal.add_new_output(&self.address); + } + + 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)? { + assets_for_next_output = next_assets; + create_new_output = true; + } + + new_proposal.add_utxo(utxo, &self.utxos_ada[utxo.0], &self.addresses[utxo.0])?; + let new_size = self.set_min_ada_for_tx(&mut new_proposal)?; + + 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))); + } + + //that means that we above limit of tx size and we cannot create that tx + return Ok(None); + } + + if new_proposal.get_need_ada()? > Coin::zero() { + if let Some(mut proposal_with_ada) = self.try_append_pure_ada_utxo(&new_proposal)? { + proposal_with_ada.makes_new_outputs = create_new_output; + proposal_with_ada.asset_utxo.push(utxo.clone()); + return Ok(Some(proposal_with_ada)); + } else { + return Ok(None); + } + } + + let mut changes = TxProposalChanges::new(new_proposal, create_new_output); + changes.asset_utxo.push(utxo.clone()); + return Ok(Some(changes)); + } + + Ok(None) + } + + 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) + }; + + let mut new_value_state = old_value_state.clone(); + + let mut asset_to_output = HashSet::new(); + let mut asset_to_new_output = HashSet::new(); + + for asset in assets { + let new_size = self.assets_calculator.add_asset_to_intermediate_value( + &mut new_value_state, + 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))); + } + new_value_state = old_value_state.clone(); + asset_to_new_output.insert(asset.clone()); + } + } + + if create_new { + tx_proposal.add_new_output(&self.address); + } + + for asset in &asset_to_output { + tx_proposal.add_asset(asset, &self.asset_to_policy[asset]); + } + + if asset_to_new_output.is_empty() { + Ok(None) + } else { + Ok(Some(asset_to_new_output)) + } + } + + 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); + + Ok(tx_size) + } + + fn recalculate_outputs(&self, tx_proposal: &mut TxProposal) -> Result<(), JsError> { + let used_utxos = &tx_proposal.used_utoxs; + for output in tx_proposal.tx_output_proposals.iter_mut() { + let (min_output_ada, output_size) = self.estimate_output_cost(&used_utxos, output)?; + output.set_min_ada(&min_output_ada); + output.set_size(output_size); + if output.get_total_ada() < min_output_ada { + output.set_total_ada(&min_output_ada); + } + } + Ok(()) + } + + pub(super) fn get_tx_proposal_size(&self, tx_proposal: &TxProposal, with_fee: bool) -> usize { + let mut size = CborCalculator::get_bare_tx_size(false); + size += CborCalculator::get_bare_tx_body_size(&tx_proposal.used_body_fields); + size += tx_proposal.witnesses_calculator.get_full_size(); + if !tx_proposal.get_outputs().is_empty() { + size += CborCalculator::get_struct_size(tx_proposal.get_outputs().len() as u64); + for output in tx_proposal.get_outputs() { + size += output.size; + } + } + + if with_fee { + size += CborCalculator::get_coin_size(tx_proposal.get_fee()); + } + + //input list size + size += CborCalculator::get_struct_size(tx_proposal.used_utoxs.len() as u64); + for utxo in &tx_proposal.used_utoxs { + size += self.inputs_sizes[utxo.0]; + } + size + } + + fn get_next_pure_ada_utxo(&self) -> Option<&(UtxoIndex, Coin)> { + self.free_ada_utxos.last() + } + + 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(); + for (utxo, utxo_ada) in self.free_ada_utxos.iter().rev() { + if ignore_list.contains(&utxo) { + continue; + } + ada_left = ada_left.checked_sub(utxo_ada).unwrap_or(Coin::zero()); + utxos.push((utxo.clone(), utxo_ada.clone())); + + if ada_left.is_zero() { + break; + } + } + + if ada_left.is_zero() { + Ok(utxos) + } else { + Err(JsError::from_str("Not enough funds")) + } + } + + 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); + if let Some(utxos) = utxos_set { + for utxo in utxos { + if let Some(new_txp) = self.prototype_append(tx_propoasl, utxo)? { + if new_txp.makes_new_outputs { + if choose_first { + return Ok(Some(new_txp)); + } else { + txp_with_new_output = Some(new_txp); + } + } else { + return Ok(Some(new_txp)); + } + } + } + } + } + + Ok(txp_with_new_output) + } + + 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)?; + 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) + } + + pub(crate) fn estimate_fee(&self, tx_proposal: &TxProposal) -> Result<(Coin, usize), JsError> { + let mut tx_len = self.get_tx_proposal_size(tx_proposal, false); + 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())?); + 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) + } + + fn remove_assets_utxo(&mut self, utxo: &UtxoIndex) { + if let Some(assets) = self.free_utxo_to_assets.get(utxo) { + for asset in assets { + if let Some(utxos) = self.free_asset_to_utxos.get_mut(asset) { + utxos.remove(utxo); + if utxos.is_empty() { + self.free_asset_to_utxos.remove(asset); + } + } + } + self.free_utxo_to_assets.remove(utxo); + } + } + + fn remove_pure_ada_utxo(&mut self, utxo: &UtxoIndex) { + let index = self.free_ada_utxos.iter().rev().position(|x| x.0 == *utxo); + if let Some(mut index) = index { + index = self.free_ada_utxos.len() - index - 1; + self.free_ada_utxos.remove(index); + } + } + + 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/tx_builder/batch_tools/assets_calculator.rs new file mode 100644 index 0000000..d3dcffd --- /dev/null +++ b/rust/src/tx_builder/batch_tools/assets_calculator.rs @@ -0,0 +1,167 @@ +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::*; + +#[derive(Clone)] +struct IntermediatePolicyState { + assets: HashSet, + total_size: usize, +} + +impl IntermediatePolicyState { + fn new() -> Self { + IntermediatePolicyState { + assets: HashSet::new(), + total_size: 0, + } + } + + 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 { + new_size -= CborCalculator::get_struct_size(self.assets.len() as u64); + } + self.assets.insert(asset_index.clone()); + new_size += CborCalculator::get_struct_size(self.assets.len() as u64); + self.total_size = new_size + size + coin_size; + } + + self.total_size + } +} + +#[derive(Clone)] +pub(super) struct IntermediateOutputValue { + multi_asset: HashMap, + total_size: usize, +} + +impl IntermediateOutputValue { + pub(super) fn new() -> Self { + IntermediateOutputValue { + multi_asset: HashMap::new(), + total_size: 0, + } + } + + 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 { + if self.is_empty() { + //value with assets and ada is array of 2 elements + self.total_size += CborCalculator::get_struct_size(2); + } + if let Some(assets) = self.multi_asset.get_mut(policy_index) { + let old_size = self.total_size - assets.total_size; + self.total_size = old_size + assets.add_asset(asset_index, asset_size, coin_size); + } else { + let mut new_size = self.total_size; + if self.multi_asset.len() > 0 { + new_size -= CborCalculator::get_struct_size(self.multi_asset.len() as u64); + } + + let mut policy_state = IntermediatePolicyState::new(); + new_size += policy_state.add_asset(asset_index, asset_size, coin_size); + self.multi_asset.insert(policy_index.clone(), policy_state); + new_size += CborCalculator::get_struct_size(self.multi_asset.len() as u64); + self.total_size = new_size + policy_size; + } + + self.total_size + } + + pub(super) fn is_empty(&self) -> bool { + self.multi_asset.iter() + .map(|(_, policy)| policy.assets.len()).sum::() <= 0 + } +} + + + +#[derive(Clone)] +pub(super) struct AssetsCalculator { + assets_name_sizes: Vec, + policy_size: usize, + utxo_stat: UtxosStat, +} + +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); + + Self { + assets_name_sizes, + policy_size, + utxo_stat, + } + } + + 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); + + if grouped_assets.len() > 0 { + size += CborCalculator::get_struct_size(grouped_assets.len() as u64); + } + + for (_, assets_in_policy) in grouped_assets { + size += self.policy_size; + size += CborCalculator::get_struct_size(assets_in_policy.len() as u64); + for asset_in_policy in assets_in_policy { + size += self.assets_name_sizes[asset_in_policy.0]; + 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)?; + } + } + size += CborCalculator::get_coin_size(&asset_coins); + } + } + 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 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 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.set_coin(&self.utxo_stat.ada_coins); + intermediate_data + } + + pub(super) fn build_empty_intermediate_value(&self) -> IntermediateOutputValue { + let mut value = IntermediateOutputValue::new(); + 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/tx_builder/batch_tools/cbor_calculator.rs new file mode 100644 index 0000000..841a2c8 --- /dev/null +++ b/rust/src/tx_builder/batch_tools/cbor_calculator.rs @@ -0,0 +1,167 @@ +use crate::fees::{LinearFee, min_fee_for_size}; +use crate::serialization_tools::map_names::{TxBodyNames, WitnessSetNames}; +use super::super::*; + +pub(super) struct CborCalculator(); + +const MAX_INLINE_ENCODING: u64 = 23; + +impl CborCalculator { + // According to the CBOR spec, the maximum size of a inlined CBOR value is 23 bytes. + // Otherwise, the value is encoded as pair of type and value. + pub(super) fn get_struct_size(items_count: u64) -> usize { + if items_count <= MAX_INLINE_ENCODING { + return 1; + } else if items_count < 0x1_00 { + return 2; + } else if items_count < 0x1_00_00 { + return 3; + } else if items_count < 0x1_00_00_00_00 { + return 5; + } else { + return 9; + } + } + + pub(super) fn get_coin_size(coin: &Coin) -> usize { + Self::get_struct_size(coin.clone().into()) + } + + pub(super) fn get_address_size(address: &Address) -> usize { + address.to_bytes().len() + } + + pub(super) fn get_fake_vkey_size() -> usize { + //precalculater fake vkey size + //TODO: try to add const calculation + 101 + } + + pub(super) fn get_boostrap_witness_size(address: &ByronAddress) -> usize { + //TODO: add precalculated boostrap witness size + let witness = make_icarus_bootstrap_witness( + &TransactionHash::from([0u8; TransactionHash::BYTE_COUNT]), + address, + &fake_private_key(), + ); + witness.to_bytes().len() + } + + pub(super) fn get_output_size(address: &Address) -> usize { + //pre babbage output size is array of 2 elements address and value + 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 + } + + pub(super) fn get_value_struct_size(ada_only: bool) -> usize { + if ada_only { + //only ada value is encoded as coin without struct overhead + 0 + } else { + //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 + } + + 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 + } + + pub(super) fn get_bare_tx_size(has_auxiliary: bool) -> usize { + //tx is array of 4 elements, tx_body, witnesses, is_valid and auxiliary + let mut size = CborCalculator::get_struct_size(4); + size += 1; //1 byte for bool is_valid + if !has_auxiliary { + size += 1; //1 byte for None auxiliary + } + 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> { + let mut current_cost = MinOutputAdaCalculator::calc_size_cost(data_cost, output_size)?; + if current_cost <= *used_coins { + return Ok((current_cost, output_size)); + } + + let size_without_coin = output_size - CborCalculator::get_coin_size(used_coins); + let mut last_size = size_without_coin + CborCalculator::get_coin_size(¤t_cost); + for _ in 0..3 { + current_cost = MinOutputAdaCalculator::calc_size_cost(data_cost, last_size)?; + let new_size = size_without_coin + CborCalculator::get_coin_size(¤t_cost); + if new_size == last_size { + return Ok((current_cost, last_size)); + } else { + last_size = new_size; + } + } + + let max_size = output_size + CborCalculator::get_coin_size(&Coin::max_value()); + let pessimistic_cost = MinOutputAdaCalculator::calc_size_cost(data_cost, max_size)?; + 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> { + 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)?; + + 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)?; + + if new_size == last_size { + return Ok((current_cost, last_size)); + } else { + last_size = new_size; + } + } + + let max_size = tx_size_without_fee + CborCalculator::get_coin_size(&Coin::max_value()); + let pessimistic_cost = min_fee_for_size(max_size, fee_algo)?; + Ok((pessimistic_cost, max_size)) + } + + //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 { + if let Some(dependable_amount) = dependable_amount { + 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; + } + } + return Ok(size + CborCalculator::get_coin_size(&remain_ada)); + } + + Ok(size) + } +} \ No newline at end of file diff --git a/rust/src/tx_builder/batch_tools/indexes.rs b/rust/src/tx_builder/batch_tools/indexes.rs new file mode 100644 index 0000000..ed32a92 --- /dev/null +++ b/rust/src/tx_builder/batch_tools/indexes.rs @@ -0,0 +1,13 @@ +use super::super::*; + +#[derive(PartialEq, Eq, Hash, Clone)] +pub struct UtxoIndex(pub(super) usize); + +#[derive(PartialEq, Eq, Hash, Clone)] +pub struct AssetIndex(pub(super) usize); + +#[derive(PartialEq, Eq, Hash, Clone)] +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 diff --git a/rust/src/tx_builder/batch_tools/mod.rs b/rust/src/tx_builder/batch_tools/mod.rs new file mode 100644 index 0000000..6548b6e --- /dev/null +++ b/rust/src/tx_builder/batch_tools/mod.rs @@ -0,0 +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 diff --git a/rust/src/tx_builder/batch_tools/proposals.rs b/rust/src/tx_builder/batch_tools/proposals.rs new file mode 100644 index 0000000..f711805 --- /dev/null +++ b/rust/src/tx_builder/batch_tools/proposals.rs @@ -0,0 +1,200 @@ +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::witnesses_calculator::WitnessesCalculator; + +#[derive(Clone)] +pub(crate) struct TxOutputProposal { + pub(super) used_assets: HashSet, + pub(super) grouped_assets: HashMap>, + pub(super) address: Address, + pub(super) min_ada: Coin, + pub(super) total_ada: Coin, + pub(super) size: usize, +} + +impl TxOutputProposal { + pub(super) fn new(address: &Address) -> Self { + TxOutputProposal { + used_assets: HashSet::new(), + grouped_assets: HashMap::new(), + address: address.clone(), + min_ada: Coin::zero(), + total_ada: Coin::zero(), + size: 0, + } + } + + pub(super) fn add_ada(&mut self, ada_coins: &Coin) -> Result<(), JsError> { + self.total_ada = self.total_ada.checked_add(ada_coins)?; + Ok(()) + } + + 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()) + .or_insert(HashSet::new()); + policy.insert(asset.clone()); + } + + pub(super) fn contains_only_ada(&self) -> bool { + self.used_assets.is_empty() + } + + pub(super) fn get_used_assets(&self) -> &HashSet { + &self.used_assets + } + + pub(super) fn get_total_ada(&self) -> Coin { + self.total_ada + } + + pub(super) fn set_total_ada(&mut self, ada_coins: &Coin) { + self.total_ada = ada_coins.clone(); + } + + pub(super) fn get_min_ada(&self) -> Coin { + self.min_ada + } + + pub(super) fn set_min_ada(&mut self, min_ada: &Coin) { + self.min_ada = min_ada.clone(); + } + + pub(super) fn set_size(&mut self, size: usize) { + 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)?)) + } +} + +#[derive(Clone)] +pub(crate) struct TxProposal { + pub(super) used_body_fields: HashSet, + pub(super) tx_output_proposals: Vec, + pub(super) used_utoxs: HashSet, + pub(super) used_assets: HashSet, + pub(super) total_ada: Coin, + pub(super) fee: Coin, + pub(super) witnesses_calculator: WitnessesCalculator, +} + +impl TxProposal { + pub(crate) fn new() -> Self { + let mut body_fields = HashSet::new(); + body_fields.insert(TxBodyNames::Inputs); + body_fields.insert(TxBodyNames::Outputs); + body_fields.insert(TxBodyNames::Fee); + + Self { + used_body_fields: body_fields, + tx_output_proposals: Vec::new(), + used_utoxs: HashSet::new(), + used_assets: HashSet::new(), + total_ada: Coin::zero(), + fee: Coin::zero(), + witnesses_calculator: WitnessesCalculator::new(), + } + } + + pub(super) fn add_new_output(&mut self, address: &Address) { + self.tx_output_proposals.push(TxOutputProposal::new(address)); + } + + pub(super) fn add_asset(&mut self, asset: &AssetIndex, policy_index: &PolicyIndex) { + self.used_assets.insert(asset.clone()); + if let Some(output) = self.tx_output_proposals.last_mut() { + output.add_asset(asset, policy_index); + } + } + + 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")); + } + self.used_utoxs.insert(utxo.clone()); + self.total_ada = self.total_ada.checked_add(ada_coins)?; + self.witnesses_calculator.add_address(address)?; + Ok(()) + } + + pub(crate) fn is_empty(&self) -> bool { + self.used_utoxs.is_empty() + } + + pub(super) fn get_used_assets(&self) -> &HashSet { + &self.used_assets + } + + pub(super) fn get_outputs(&self) -> &Vec { + &self.tx_output_proposals + } + + pub(super) fn get_fee(&self) -> &Coin { + &self.fee + } + + pub(super) fn set_fee(&mut self, fee: &Coin) { + self.fee = fee.clone(); + } + + + pub(super) fn get_total_ada_for_ouputs(&self) -> Result { + 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())) + } + + 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())); + } + + pub(crate) fn add_last_ada_to_last_output(&mut self) -> Result<(), JsError> { + let unused_ada = self.get_unused_ada()?; + if let Some(output) = self.tx_output_proposals.last_mut() { + output.add_ada(&unused_ada)?; + } + + Ok(()) + } + + 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)?); + } + + let mut inputs = Vec::new(); + for utxo in &self.used_utoxs { + inputs.push(utxos.0[utxo.0].input.clone()); + } + + let body = TransactionBody::new( + &TransactionInputs(inputs), + &TransactionOutputs(outputs), + &self.fee, + None, + ); + let tx = Transaction::new( + &body, + &self.witnesses_calculator.create_mock_witnesses_set(), + None, + ); + + Ok(tx) + } +} \ No newline at end of file diff --git a/rust/src/tx_builder/batch_tools/utxo_stat.rs b/rust/src/tx_builder/batch_tools/utxo_stat.rs new file mode 100644 index 0000000..5fd9071 --- /dev/null +++ b/rust/src/tx_builder/batch_tools/utxo_stat.rs @@ -0,0 +1,44 @@ +use std::collections::{HashMap, HashSet}; +use crate::{Coin, JsError}; +use super::indexes::{UtxoIndex, AssetIndex, PolicyIndex}; + +#[derive(Clone)] +pub(super) struct UtxosStat { + pub(super) total_policies: usize, + pub(super) assets_in_policy: HashMap, + pub(super) coins_in_assets: HashMap, + pub(super) ada_coins: Coin, +} + +impl UtxosStat { + 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(), + coins_in_assets: HashMap::new(), + ada_coins: Coin::zero(), + }; + for (policy_index, assets) in policy_to_asset { + utxos_stat.assets_in_policy.insert(policy_index.clone(), assets.len()); + } + + for i in 0..amounts.len() { + for (_, amount) in &amounts[i] { + let asset_index = AssetIndex(i); + if let Some(coins) = utxos_stat.coins_in_assets.get(&asset_index) { + 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.total_policies = policy_to_asset.len(); + utxos_stat.ada_coins = total_ada.clone(); + + Ok(utxos_stat) + } +} \ No newline at end of file diff --git a/rust/src/tx_builder/batch_tools/witnesses_calculator.rs b/rust/src/tx_builder/batch_tools/witnesses_calculator.rs new file mode 100644 index 0000000..550412a --- /dev/null +++ b/rust/src/tx_builder/batch_tools/witnesses_calculator.rs @@ -0,0 +1,165 @@ +use crate::serialization_tools::map_names::WitnessSetNames; +use crate::tx_builder::batch_tools::cbor_calculator::CborCalculator; +use super::super::*; + +#[derive(Clone)] +pub(super) struct WitnessesCalculator { + adresses: BTreeSet
, + vkeys_count: u64, + boostrap_count: u64, + bootsraps: Vec, + used_fields: HashSet, + total_size: usize, +} + +impl WitnessesCalculator { + pub(super) fn new() -> Self { + Self { + adresses: BTreeSet::new(), + vkeys_count: 0, + boostrap_count: 0, + bootsraps: Vec::new(), + used_fields: HashSet::new(), + total_size: 0, + } + } + + pub(super) fn add_address(&mut self, address: &Address) -> Result<(), JsError> { + if self.adresses.contains(address) { + return Ok(()); + } + + self.adresses.insert(address.clone()); + + match &BaseAddress::from_address(address) { + Some(addr) => { + match &addr.payment_cred().to_keyhash() { + Some(_) => self.add_vkey(), + None => (), + } + match &addr.payment_cred().to_scripthash() { + Some(_) => return Err(JsError::from_str("Script input is not supported for send all")), + None => () + } + } + None => () + } + match &EnterpriseAddress::from_address(address) { + Some(addr) => { + match &addr.payment_cred().to_keyhash() { + Some(_) => self.add_vkey(), + None => (), + } + match &addr.payment_cred().to_scripthash() { + Some(_) => return Err(JsError::from_str("Script input is not supported for send all")), + None => () + } + } + None => (), + } + match &PointerAddress::from_address(address) { + Some(addr) => { + match &addr.payment_cred().to_keyhash() { + Some(_) => self.add_vkey(), + None => (), + } + match &addr.payment_cred().to_scripthash() { + Some(_) => return Err(JsError::from_str("Script input is not supported for send all")), + None => () + } + } + None => (), + } + match &ByronAddress::from_address(address) { + Some(addr) => self.add_boostrap(addr), + None => (), + } + + Ok(()) + } + + pub(super) fn get_full_size(&self) -> usize { + self.total_size + } + + 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 { + result.add(&fake_vkey_witness.clone()); + } + Some(result) + } + }; + + let bootstrap_keys = match self.boostrap_count { + 0 => None, + _x => { + let mut result = BootstrapWitnesses::new(); + for boostrap_address in &self.bootsraps { + // picking icarus over daedalus for fake witness generation shouldn't matter + result.add(&make_icarus_bootstrap_witness( + &TransactionHash::from([0u8; TransactionHash::BYTE_COUNT]), + boostrap_address, + &fake_key_root, + )); + } + Some(result) + } + }; + + TransactionWitnessSet { + vkeys, + native_scripts: None, + bootstraps: bootstrap_keys, + plutus_scripts: None, + plutus_data: None, + redeemers: 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.used_fields.insert(WitnessSetNames::Vkeys); + self.total_size += CborCalculator::get_wintnesses_set_struct_size(&self.used_fields); + } + + if self.vkeys_count != 0 { + self.total_size -= CborCalculator::get_struct_size(self.vkeys_count); + } + self.vkeys_count += 1; + self.total_size += CborCalculator::get_struct_size(self.vkeys_count); + self.total_size += CborCalculator::get_fake_vkey_size(); + } + + fn add_boostrap(&mut self, address: &ByronAddress) { + 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.used_fields.insert(WitnessSetNames::Bootstraps); + self.total_size += CborCalculator::get_wintnesses_set_struct_size(&self.used_fields); + } + + if self.boostrap_count != 0 { + self.total_size -= CborCalculator::get_struct_size(self.boostrap_count); + } + self.boostrap_count += 1; + 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/tx_builder/mint_builder.rs b/rust/src/tx_builder/mint_builder.rs new file mode 100644 index 0000000..e91c9b3 --- /dev/null +++ b/rust/src/tx_builder/mint_builder.rs @@ -0,0 +1,236 @@ +use super::*; +use crate::plutus::Redeemer; +use crate::tx_builder::tx_inputs_builder::{PlutusScriptSource, PlutusScriptSourceEnum}; + +#[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 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(&index)); + 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/test_batch.rs b/rust/src/tx_builder/test_batch.rs new file mode 100644 index 0000000..951d216 --- /dev/null +++ b/rust/src/tx_builder/test_batch.rs @@ -0,0 +1,762 @@ +#[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_batch_builder.rs b/rust/src/tx_builder/tx_batch_builder.rs new file mode 100644 index 0000000..a419972 --- /dev/null +++ b/rust/src/tx_builder/tx_batch_builder.rs @@ -0,0 +1,112 @@ +use batch_tools::proposals::{TxProposal}; +use batch_tools::asset_categorizer::{AssetCategorizer}; +use super::*; + +#[wasm_bindgen] +pub struct TransactionBatchList(Vec); + +#[wasm_bindgen] +impl TransactionBatchList { + pub fn len(&self) -> usize { + self.0.len() + } + + pub fn get(&self, index: usize) -> TransactionBatch { + self.0[index].clone() + } +} + +impl<'a> IntoIterator for &'a TransactionBatchList { + type Item = &'a TransactionBatch; + type IntoIter = std::slice::Iter<'a, TransactionBatch>; + + fn into_iter(self) -> std::slice::Iter<'a, TransactionBatch> { + self.0.iter() + } +} + +#[wasm_bindgen] +#[derive(Clone)] +pub struct TransactionBatch { + transactions: Vec, +} + +#[wasm_bindgen] +impl TransactionBatch { + pub fn len(&self) -> usize { + self.transactions.len() + } + + pub fn get(&self, index: usize) -> Transaction { + self.transactions[index].clone() + } +} + +impl<'a> IntoIterator for &'a TransactionBatch { + type Item = &'a Transaction; + type IntoIter = std::slice::Iter<'a, Transaction>; + + fn into_iter(self) -> std::slice::Iter<'a, Transaction> { + self.transactions.iter() + } +} + +impl TransactionBatch { + pub fn new() -> Self { + Self { + transactions: Vec::new(), + } + } + + pub fn add_transaction(&mut self, transaction: Transaction) { + self.transactions.push(transaction); + } +} + +struct TxBatchBuilder { + asset_groups: AssetCategorizer, + tx_proposals: Vec, +} + +impl TxBatchBuilder { + pub fn new(utxos: &TransactionUnspentOutputs, address: &Address, config: &TransactionBuilderConfig) -> Result { + let asset_groups = AssetCategorizer::new(config, utxos, address)?; + Ok(Self { + asset_groups, + tx_proposals: Vec::new(), + }) + } + + 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)? { + current_tx_proposal = tx_proposal; + } + + 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.tx_proposals.push(current_tx_proposal); + } + + let mut batch = TransactionBatch::new(); + for tx_proposal in self.tx_proposals.iter_mut() { + batch.add_transaction(tx_proposal.create_tx(&self.asset_groups, utxos)?); + } + + Ok(batch) + } +} + +#[wasm_bindgen] +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/tx_builder/tx_inputs_builder.rs b/rust/src/tx_builder/tx_inputs_builder.rs index 8ac5fa0..c3cebbb 100644 --- a/rust/src/tx_builder/tx_inputs_builder.rs +++ b/rust/src/tx_builder/tx_inputs_builder.rs @@ -7,24 +7,81 @@ pub(crate) struct TxBuilderInput { 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(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() + } +} + #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub enum PlutusScriptSourceEnum { Script(PlutusScript), - RefInput(TransactionInput, ScriptHash), + 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(), + 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(PlutusScriptSourceEnum); +pub struct PlutusScriptSource(pub(crate) PlutusScriptSourceEnum); #[wasm_bindgen] impl PlutusScriptSource { @@ -32,8 +89,24 @@ impl PlutusScriptSource { 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())) + 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()))) } } @@ -62,7 +135,7 @@ impl DatumSource { #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct PlutusWitness { script: PlutusScriptSourceEnum, - datum: DatumSourceEnum, + datum: Option, redeemer: Redeemer, } @@ -71,7 +144,7 @@ impl PlutusWitness { pub fn new(script: &PlutusScript, datum: &PlutusData, redeemer: &Redeemer) -> Self { Self { script: PlutusScriptSourceEnum::Script(script.clone()), - datum: DatumSourceEnum::Datum(datum.clone()), + datum: Some(DatumSourceEnum::Datum(datum.clone())), redeemer: redeemer.clone(), } } @@ -79,7 +152,23 @@ impl PlutusWitness { pub fn new_with_ref(script: &PlutusScriptSource, datum: &DatumSource, redeemer: &Redeemer) -> Self { Self { script: script.0.clone(), - datum: datum.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(), } } @@ -93,7 +182,7 @@ impl PlutusWitness { pub fn datum(&self) -> Option { match &self.datum { - DatumSourceEnum::Datum(datum) => Some(datum.clone()), + Some(DatumSourceEnum::Datum(datum)) => Some(datum.clone()), _ => None, } } @@ -133,18 +222,34 @@ impl PlutusWitnesses { self.0.push(elem.clone()); } - pub(crate) fn collect(&self) -> (PlutusScripts, PlutusList, Redeemers) { + 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 = PlutusList::new(); + let mut d : Option = None; let mut r = Redeemers::new(); self.0.iter().for_each(|w| { if let PlutusScriptSourceEnum::Script(script) = &w.script { - s.add(script); + if used_scripts.insert(script.clone()) { + s.add(script); + } } - if let DatumSourceEnum::Datum(datum) = &w.datum { - d.add(datum); + 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); } - r.add(&w.redeemer); }); (s, d, r) } @@ -162,11 +267,20 @@ pub enum ScriptWitnessType { PlutusScriptWitness(PlutusWitness), } +impl ScriptWitnessType { + pub fn script_hash(&self) -> ScriptHash { + match self { + ScriptWitnessType::NativeScriptWitness(script) => script.hash(), + ScriptWitnessType::PlutusScriptWitness(script) => script.script.script_hash(), + } + } +} + // 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 MockWitnessSet { +pub struct RequiredWitnessSet { vkeys: RequiredSignersSet, - scripts: LinkedHashMap>, + scripts: LinkedHashMap>>, bootstraps: BTreeSet>, } @@ -174,11 +288,11 @@ pub struct MockWitnessSet { #[derive(Clone, Debug)] pub struct TxInputsBuilder { inputs: BTreeMap)>, - input_types: MockWitnessSet, + required_witnesses: RequiredWitnessSet, } pub(crate) fn get_bootstraps(inputs: &TxInputsBuilder) -> BTreeSet> { - inputs.input_types.bootstraps.clone() + inputs.required_witnesses.bootstraps.clone() } #[wasm_bindgen] @@ -186,7 +300,7 @@ impl TxInputsBuilder { pub fn new() -> Self { Self { inputs: BTreeMap::new(), - input_types: MockWitnessSet { + required_witnesses: RequiredWitnessSet { vkeys: BTreeSet::new(), scripts: LinkedHashMap::new(), bootstraps: BTreeSet::new(), @@ -212,9 +326,15 @@ impl TxInputsBuilder { amount: amount.clone(), }; self.push_input((inp, None)); - self.input_types.vkeys.insert(hash.clone()); + 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` @@ -233,9 +353,7 @@ impl TxInputsBuilder { amount: amount.clone(), }; self.push_input((inp, Some(hash.clone()))); - if !self.input_types.scripts.contains_key(hash) { - self.input_types.scripts.insert(hash.clone(), None); - } + 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 @@ -247,10 +365,8 @@ impl TxInputsBuilder { ) { let hash = script.hash(); self.add_script_input(&hash, input, amount); - self.input_types.scripts.insert( - hash, - Some(ScriptWitnessType::NativeScriptWitness(script.clone())), - ); + let witness = ScriptWitnessType::NativeScriptWitness(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 @@ -263,10 +379,8 @@ impl TxInputsBuilder { let hash = witness.script.script_hash(); self.add_script_input(&hash, input, amount); - self.input_types.scripts.insert( - hash, - Some(ScriptWitnessType::PlutusScriptWitness(witness.clone())), - ); + let witness = ScriptWitnessType::PlutusScriptWitness(witness.clone()); + self.insert_input_with_witness(&hash, input, &witness); } pub fn add_bootstrap_input( @@ -280,7 +394,7 @@ impl TxInputsBuilder { amount: amount.clone(), }; self.push_input((inp, None)); - self.input_types.bootstraps.insert(hash.to_bytes()); + self.required_witnesses.bootstraps.insert(hash.to_bytes()); } /// Note that for script inputs this method will use underlying generic `.add_script_input` @@ -337,9 +451,10 @@ impl TxInputsBuilder { /// 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.input_types + self.required_witnesses .scripts .values() + .flat_map(|v| v.values()) .filter(|s| s.is_none()) .count() } @@ -351,16 +466,30 @@ impl TxInputsBuilder { pub fn add_required_native_input_scripts(&mut self, scripts: &NativeScripts) -> usize { scripts.0.iter().for_each(|s: &NativeScript| { let hash = s.hash(); - if self.input_types.scripts.contains_key(&hash) { - self.input_types.scripts.insert( - hash, - Some(ScriptWitnessType::NativeScriptWitness(s.clone())), - ); + 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(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 @@ -368,25 +497,49 @@ impl TxInputsBuilder { 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 self.input_types.scripts.contains_key(&hash) { - self.input_types.scripts.insert( - hash, - Some(ScriptWitnessType::PlutusScriptWitness(s.clone())), - ); + 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.input_types.scripts.iter() - .filter_map(|(_, wit)| wit.as_ref() ) { + for wintess in self.required_witnesses.scripts.iter() + .flat_map(|(_, tx_wits)| tx_wits.values()) + .filter_map(|wit| wit.as_ref()) { if let ScriptWitnessType::PlutusScriptWitness(plutus_witness) = wintess { - if let DatumSourceEnum::RefInput(input) = &plutus_witness.datum { + if let Some(DatumSourceEnum::RefInput(input)) = &plutus_witness.datum { inputs.push(input.clone()); } - if let PlutusScriptSourceEnum::RefInput(input, _) = &plutus_witness.script { + if let PlutusScriptSourceEnum::RefInput(input, _, _) = &plutus_witness.script { inputs.push(input.clone()); } } @@ -398,8 +551,11 @@ impl TxInputsBuilder { /// 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.input_types.scripts.values().for_each(|option| { - if let Some(ScriptWitnessType::NativeScriptWitness(s)) = option { + self.required_witnesses.scripts + .values() + .flat_map(|v| v) + .for_each(|tx_in_with_wit| { + if let Some(ScriptWitnessType::NativeScriptWitness(s)) = tx_in_with_wit.1 { scripts.add(&s); } }); @@ -410,6 +566,20 @@ impl TxInputsBuilder { } } + 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 { @@ -424,18 +594,21 @@ impl TxInputsBuilder { * * The registered witnesses are then each cloned with the new correct redeemer input index. */ - let script_hash_index_map: BTreeMap<&ScriptHash, BigNum> = self + let script_hash_index_map: BTreeMap<&TransactionInput, BigNum> = self .inputs .values() .enumerate() - .fold(BTreeMap::new(), |mut m, (i, (_, hash_option))| { - if let Some(hash) = hash_option { - m.insert(hash, to_bignum(i as u64)); + .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.input_types.scripts.iter().for_each(|(hash, option)| { + 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(&idx)); @@ -458,7 +631,7 @@ impl TxInputsBuilder { } pub fn add_required_signer(&mut self, key: &Ed25519KeyHash) { - self.input_types.vkeys.insert(key.clone()); + self.required_witnesses.vkeys.insert(key.clone()); } pub fn add_required_signers(&mut self, keys: &RequiredSigners) { @@ -489,15 +662,28 @@ impl TxInputsBuilder { 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.input_types.vkeys.clone(); + let mut set = inputs.required_witnesses.vkeys.clone(); inputs - .input_types + .required_witnesses .scripts .values() + .flat_map(|tx_wits| tx_wits.values()) .for_each(|swt: &Option| { if let Some(ScriptWitnessType::NativeScriptWitness(s)) = swt { RequiredSignersSet::from(s).iter().for_each(|k| { diff --git a/rust/src/tx_builder_constants.rs b/rust/src/tx_builder_constants.rs index 1f7c62a..41e9129 100644 --- a/rust/src/tx_builder_constants.rs +++ b/rust/src/tx_builder_constants.rs @@ -11,7 +11,7 @@ pub struct TxBuilderConstants(); #[wasm_bindgen] impl TxBuilderConstants { pub fn plutus_default_cost_models() -> Costmdls { - TxBuilderConstants::plutus_alonzo_cost_models() + TxBuilderConstants::plutus_vasil_cost_models() } pub fn plutus_alonzo_cost_models() -> Costmdls { @@ -51,7 +51,7 @@ impl TxBuilderConstants { 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, 9462713, 1021, 10, + 31220, 32, 32696, 32, 43357, 32, 32247, 32, 38314, 32, 57996947, 18975, 10, ]), ); res.insert( @@ -224,14 +224,14 @@ impl TxBuilderConstants { 32, 38314, 32, - 20000000000, - 20000000000, - 9462713, - 1021, + 35892428, 10, - 20000000000, - 0, - 20000000000, + 57996947, + 18975, + 10, + 38887044, + 32947, + 10 ]), ); res @@ -271,7 +271,7 @@ mod tests { ); assert_eq!( hex::encode(TxBuilderConstants::plutus_vasil_cost_models().language_views_encoding()), - "a20198af1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a0011b22c1a0005fdde00021a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201b00000004a817c8001b00000004a817c8001a009063b91903fd0a1b00000004a817c800001b00000004a817c80041005901b69f1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201a009063b91903fd0aff", + "a20198af1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a0011b22c1a0005fdde00021a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201a0223accc0a1a0374f693194a1f0a1a02515e841980b30a41005901b69f1a0003236119032c01011903e819023b00011903e8195e7104011903e818201a0001ca761928eb041959d818641959d818641959d818641959d818641959d818641959d81864186418641959d81864194c5118201a0002acfa182019b551041a000363151901ff00011a00015c3518201a000797751936f404021a0002ff941a0006ea7818dc0001011903e8196ff604021a0003bd081a00034ec5183e011a00102e0f19312a011a00032e801901a5011a0002da781903e819cf06011a00013a34182019a8f118201903e818201a00013aac0119e143041903e80a1a00030219189c011a00030219189c011a0003207c1901d9011a000330001901ff0119ccf3182019fd40182019ffd5182019581e18201940b318201a00012adf18201a0002ff941a0006ea7818dc0001011a00010f92192da7000119eabb18201a0002ff941a0006ea7818dc0001011a0002ff941a0006ea7818dc0001011a000c504e197712041a001d6af61a0001425b041a00040c660004001a00014fab18201a0003236119032c010119a0de18201a00033d7618201979f41820197fb8182019a95d1820197df718201995aa18201a0374f693194a1f0aff", ); } } diff --git a/rust/src/utils.rs b/rust/src/utils.rs index 748ede8..42fe66b 100644 --- a/rust/src/utils.rs +++ b/rust/src/utils.rs @@ -141,6 +141,15 @@ impl TransactionUnspentOutputs { } } +impl<'a> IntoIterator for &'a TransactionUnspentOutputs { + type Item = &'a TransactionUnspentOutput; + type IntoIter = std::slice::Iter<'a, TransactionUnspentOutput>; + + fn into_iter(self) -> std::slice::Iter<'a, TransactionUnspentOutput> { + self.0.iter() + } +} + // 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 @@ -230,6 +239,10 @@ impl BigNum { 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() } } @@ -251,6 +264,13 @@ impl TryFrom for u32 { } } +impl From for u64 { + + fn from(value: BigNum) -> Self { + value.0 + } +} + impl From for BigNum { fn from(value: u64) -> Self { return BigNum(value); @@ -861,7 +881,7 @@ pub(crate) fn read_bounded_bytes( } #[wasm_bindgen] -#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Hash)] pub struct BigInt(num_bigint::BigInt); impl_to_from!(BigInt); @@ -1083,6 +1103,14 @@ impl std::convert::From for BigInt } } +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. @@ -1366,21 +1394,9 @@ impl MinOutputAdaCalculator { } pub fn calculate_ada(&self) -> Result { - let coins_per_byte = self.data_cost.coins_per_byte(); - // let mut output: TransactionOutput = self.output.clone(); - fn calc_required_coin( - output: &TransactionOutput, - coins_per_byte: &Coin, - ) -> Result { - //according to https://hydra.iohk.io/build/15339994/download/1/babbage-changes.pdf - //See on the page 9 getValue txout - Ok(BigNum::from(output.to_bytes().len()) - .checked_add(&to_bignum(160))? - .checked_mul(&coins_per_byte)?) - } for _ in 0..3 { - let required_coin = calc_required_coin(&output, &coins_per_byte)?; + let required_coin = Self::calc_required_coin(&output, &self.data_cost)?; if output.amount.coin.less_than(&required_coin) { output.amount.coin = required_coin.clone(); } else { @@ -1388,7 +1404,7 @@ impl MinOutputAdaCalculator { } } output.amount.coin = to_bignum(u64::MAX); - calc_required_coin(&output, &coins_per_byte) + Ok(Self::calc_required_coin(&output, &self.data_cost)?) } fn create_fake_output() -> Result { @@ -1396,6 +1412,19 @@ impl MinOutputAdaCalculator { let fake_value: Value = Value::new(&to_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))? + .checked_mul(&data_cost.coins_per_byte()) + } + + 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()) + } } ///returns minimal amount of ada for the output for case when the amount is included to the output @@ -2665,7 +2694,7 @@ mod tests { ); assert_eq!( hex::encode(hash.to_bytes()), - "f4e4522ff98b6ba0ab5042d44da2458cd5fa6f97dc42aca1def58193f17a1375" + "887e1b6416d750d871c0f5b7136b54f7b8e8b0e293379d090f38f8f821d08a29" ); } diff --git a/scripts/fix-buffer-ref.js b/scripts/fix-buffer-ref.js new file mode 100644 index 0000000..292138a --- /dev/null +++ b/scripts/fix-buffer-ref.js @@ -0,0 +1,21 @@ +const fs = require('fs'); + +const inputFile = fs.readFileSync('./rust/pkg/cardano_serialization_lib_bg.js', 'utf8').split(/\r?\n/); + +const regex = /(\s*if \(cached[A-Za-z0-9]+Memory[0-9]* === null ||) (cached[A-Za-z0-9]+Memory[0-9]*)\.byteLength === 0\) {/; +const replacer = '$1 $2.buffer !== wasm.memory.buffer) {'; + +for (let i = 0; i < inputFile.length; ++i) { + let line = inputFile[i]; + inputFile[i] = line.replace(regex, replacer); +} + +fs.writeFile( + './rust/pkg/cardano_serialization_lib_bg.js', + inputFile.join('\n'), + (err) => { + if (err != null) { + console.log(`err writing file: ${err}`) + } + } +);