From e1fc0ba79d0a2787a7dac81802c293a166d0657d Mon Sep 17 00:00:00 2001 From: Andrea Date: Mon, 11 Mar 2024 17:35:54 -0700 Subject: [PATCH] Multisig: verify the consistency of commitments checksum --- ironfish-rust-nodejs/index.d.ts | 1 + ironfish-rust-nodejs/src/frost.rs | 14 + .../createSigningPackage.test.ts.fixture | 154 ++++++-- .../multisig/createSigningPackage.test.ts | 332 +++++++++++++----- .../wallet/multisig/createSigningPackage.ts | 29 +- 5 files changed, 417 insertions(+), 113 deletions(-) diff --git a/ironfish-rust-nodejs/index.d.ts b/ironfish-rust-nodejs/index.d.ts index 70ac75887d..047508765a 100644 --- a/ironfish-rust-nodejs/index.d.ts +++ b/ironfish-rust-nodejs/index.d.ts @@ -118,6 +118,7 @@ export type NativeSigningCommitment = SigningCommitment export class SigningCommitment { constructor(jsBytes: Buffer) identity(): Buffer + verifyChecksum(transactionHash: Buffer, signerIdentities: Array): boolean } export type NativeSigningPackage = SigningPackage export class SigningPackage { diff --git a/ironfish-rust-nodejs/src/frost.rs b/ironfish-rust-nodejs/src/frost.rs index f312a86911..969e402ac4 100644 --- a/ironfish-rust-nodejs/src/frost.rs +++ b/ironfish-rust-nodejs/src/frost.rs @@ -304,6 +304,20 @@ impl NativeSigningCommitment { pub fn identity(&self) -> Buffer { Buffer::from(self.signing_commitment.identity().serialize().as_slice()) } + + #[napi] + pub fn verify_checksum( + &self, + transaction_hash: JsBuffer, + signer_identities: Vec, + ) -> Result { + let transaction_hash = transaction_hash.into_value()?; + let signer_identities = try_deserialize_identities(signer_identities)?; + Ok(self + .signing_commitment + .verify_checksum(&transaction_hash, &signer_identities) + .is_ok()) + } } #[napi(js_name = "SigningPackage")] diff --git a/ironfish/src/rpc/routes/wallet/multisig/__fixtures__/createSigningPackage.test.ts.fixture b/ironfish/src/rpc/routes/wallet/multisig/__fixtures__/createSigningPackage.test.ts.fixture index 0027f82258..12f1d8cc94 100644 --- a/ironfish/src/rpc/routes/wallet/multisig/__fixtures__/createSigningPackage.test.ts.fixture +++ b/ironfish/src/rpc/routes/wallet/multisig/__fixtures__/createSigningPackage.test.ts.fixture @@ -2,13 +2,13 @@ "Route multisig/createSigningPackage should create signing package": [ { "version": 4, - "id": "5bd50c3c-6539-444e-a65f-7576df689ec7", + "id": "50b67b6d-8a54-450b-b9af-50f8651146fd", "name": "test", - "spendingKey": "fe1e0c55cf4a6528b8c0a7765c98be0e895a6744500593ba9c796507322a7dac", - "viewKey": "f461e2eda7d920c3eec1afca71eac6fe176d61bc5e72778a5d51189fb0f5a44b43643eff7a3f29f6c76ee8e0c4e800f1aaf9febeffac26cc08d36cecbaacbc2d", - "incomingViewKey": "9bc32b94f00c3bf8198434a1ba7a501d8c9b64423cc5c920392e7ae038b74e00", - "outgoingViewKey": "c20bd7e169306324e3050087b8f1a44795bd5866e227926b8104872f6bfd46b9", - "publicAddress": "0531c3796cd0e0e5db4118ad16662413c3f41b490cf51f531d94a3d04f3821cd", + "spendingKey": "8322f28377136d6bbd25eec41c5db1176a47ff8f2ac0b45e509717d396dd3711", + "viewKey": "060aac7f398ff10ac221b0a19d2548bb273f27a0cf79ffde85a563b8f3d81f59b768f016dacbd72b1f3091694047aa4a8dfdd7c3f41d1fa87e240d677b4e1899", + "incomingViewKey": "75be181d6151a86d6612e4e35b83a7227aceb62e5f5ebb191bb9cd0a5c5d4107", + "outgoingViewKey": "9a8e0acb94da01737b2a5be65dce398082c06f54330a82699fd4d8197f6a17cd", + "publicAddress": "9413d3e00d7ca35eb28f650acc37e4ff07baa92cd12c23f8235649898b44e3ad", "createdAt": { "hash": { "type": "Buffer", @@ -16,7 +16,7 @@ }, "sequence": 1 }, - "proofAuthorizingKey": "cfb8683f14041deb798c49c9d704e8ad08b50967f847b9b6ae4b4e9ba84c640a" + "proofAuthorizingKey": "4ceb6b9c04c69cf692a8a56700557d4baebd41ec389acfd52682ecfc03511502" }, { "header": { @@ -24,15 +24,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:0NKojeVsZLsIciUPhfVX+PmuFh73r+MXTo29XXdFEBY=" + "data": "base64:0EdKov9dcUfMYbiE+6PUVxmr+oD/4bMTBPidIRhSAiA=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:prOD+qubJ06SCDKIIHzBgU56ivmiZca+oxnqgfhiH/g=" + "data": "base64:3V3KvI613pMMdrglnV1P+Y52Auqrdo4ehmWeL+UfheM=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1709591815765, + "timestamp": 1710203895214, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -40,25 +40,25 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAADViTHO69UYSpUNzZtsYC7iL8+75ckf7EXC/dKMMB+nGsHPEK1i0FZTjIvT9udrBhfEQCxL5iaY4CMRG5JcP+qEHjCPM84bVx5Y66n9Lav1KS+8h+5Hpy185DQ6seI4TadSCzTbnDgW+f+vEwISh9l0tCw2cnOpthfy16lvTzOm4JbglshDQpaYdJlyAojswIoasccBPrwLlAkqRrzt9UE0rx5AmW/Y1SywQmDrj1tiCZHjcV5M+hQKnSed484Glp1od2cZLTsiy/XNULj8Uwp8PlGK3bfPfudwCq8sSXVDFRvbgUirh0BIbEEjg+nn3gDkYLqvorUSXbPGQjU/SabvKyCU5gxXsa9o+Hsn+7F+9roW8r2OPgfRuUYTaXulkOfAZokFtIipHNUUMsgOOfWFMlTVbO3E+m9DjSZ710vW0yMXigoUBcHSFgGl6IaljuMUqA9VR6byASLC0zXd7wk+sxS897RfJpPT9OMXuHZ2ueYteat8G1N35CPOnGM3KubMfrw0l8qeXoIFeyAJ0+Spa+R0PabzIZj1VnT88aUR2WyfQcwRorwca7eSPkugGXbIsqmx076glhcqqaYC7rubphcDCiiUPIv/B6EPIe8i5UcOIJd2EMSklyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw842XiOqgQYieqFJCCK7D7CHaDCmEkuNF/o8RGe4qt1T7AVXhcIG3e3AXib8Q6f1LyWhSjbBkRzlqH5gT95paBQ==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAA41JEalTDDdlUGDlyJNQ7DJjl5skgZC8dzG49ZNMAVraIh8xKLNjjoR9Y4P3rl0BlN6mwjfJTRJhEGXGB7hWFoSi+wgt9SJxjUtwy7lfqc1CU4pASyQM7FE2MbICkhsdCyBqlQ62TRNJnd8fMsVLulXzPPNsMdSxdhpczjPSuD2kPvnXoRdbm4zCBwAiPkwfVcnVFaN4t2lHo6tIdlMvuzku98HFSs1YVQ2ZV9EiPlhWnY6d/SFjYZa5dqKpxT/np4alPfopgsj9YJzCECDl8+gTgaOZxlskeTHpgfD3vOpkAk4Xb4ewEHSk7twczsLMHjHw5nB5xdeN7FqKyrf++ATiWDiy/RJM5xkK0DE5ecxm0iwkjuc24icyDHxnAl6ASKk5tyNin6yViiHQELg9Fb85Or/LIwIlPaerhrCyz0ucD2mRg9hPFOEdoBHOd4cbORfyHazngsXm2Ab7aRp/H17KTDgZo9vc7h+Xi0Vs2VblG9D8SIzNjzd8M1O3Y7bMyE0u0TrVn61T6jMxSpISqqlIdswwqQ1JS0xEnhtmqM56sfTyL2aSaGAnoVCQ2LjevXiPmZ9sAEGCOZ+UWdpDFVQWNPGRtCGZS4E3fwFSpE59l+zcJ9W7y0Elyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwqCJMp9pcsFW6EselDQELfkL9v8Y1W7eMkHOPaJDuppyHI70s4W3a+rlCprnHsSOFtcVrnMD8I9tRln+Ud1VoCw==" } ] }, { "type": "Buffer", - "data": "base64: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" + "data": "base64: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" } ], - "Route multisig/createSigningPackage should verify commitment identities": [ + "Route multisig/createSigningPackage should verify commitment consistency of identities": [ { "version": 4, - "id": "453094d4-6348-48a1-b625-75f68461fe72", + "id": "9bae7509-3fc2-4cca-bdb3-ab79dc43c67c", "name": "test", - "spendingKey": "4f0ec9dbc204d53c3e960f0cf935e11e08ed00a11bfaac2fa043dd67dff37ef8", - "viewKey": "27b0cbbf1bbb319fb83fd6b423bd64872ddb9f97dbd7b53a40e65f98cf320f4c1d2f5efa957061251cb560e506327363c2353ffdc03d9aa7fbbfc19c4bd0e0a4", - "incomingViewKey": "c6e34c9247139a84bb4336f61a7ee32b3f151c983e0f717f388db6598718f204", - "outgoingViewKey": "631c569fa8f09e783a238c21bbc3cbcffe0aa482a7b35d507958e10b18009e05", - "publicAddress": "e2bdb8edaddf79665a291557806198b91261d93a28de4bb81334ed90b4875445", + "spendingKey": "920715ad9ebbedb8ddb6def1440c080fcc4ebaae5ed97bedd1aa65f4461334c4", + "viewKey": "2c1e157087a207516ef2f941bab0809c34223609f44a6569b2dff8053aad0f1dcf66e488ba7155bfd9ff921a01e87348dff9a19e8095a7dc7f9d21fcaf907a1f", + "incomingViewKey": "ccdd6ede898c9f76dcb856d3e31301beb57292597cd0a6f87f4647d5290aa200", + "outgoingViewKey": "28b454f8f451d895260fda3fc3e4ce11410880d26bdfa56213aa13d41fd6fc68", + "publicAddress": "b2b06f5534985b031661ab403079400a6a4398f3f1d54c0d907bc88a4d91c217", "createdAt": { "hash": { "type": "Buffer", @@ -66,7 +66,7 @@ }, "sequence": 1 }, - "proofAuthorizingKey": "fc4f4bbb17d69002b1cf28de960645ba5ef26d29d9a54dee457c9d8d9b164b05" + "proofAuthorizingKey": "2cffae476b9ec2f8d2d506c187406123b66f2456ddd0a1d03f9c10244dab470b" }, { "header": { @@ -74,15 +74,15 @@ "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", "noteCommitment": { "type": "Buffer", - "data": "base64:9BbvmAKv3VDcfPSUfr7JPoS3JE0upFIirARc091abgg=" + "data": "base64:7eU3qQYIUzZ5TzhxkHebLF+a671BuV51/7/3tkNWLQ0=" }, "transactionCommitment": { "type": "Buffer", - "data": "base64:0P7hihq6K71YDMYpa/JKZuBoPeq6en2WpdpREIscfNQ=" + "data": "base64:CHP2Um3A7uEVY6RTLadBopGKRdLueQtEeYv97Ly5Y90=" }, "target": "9282972777491357380673661573939192202192629606981189395159182914949423", "randomness": "0", - "timestamp": 1709591818640, + "timestamp": 1710203897374, "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", "noteSize": 4, "work": "0" @@ -90,13 +90,117 @@ "transactions": [ { "type": "Buffer", - "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAEFCQ6P/SypxBxhs9f0L8NVdta8XhV0B1NAbs8YNTrQGUY5IWm0YsBIEddvJ/fc83F+VYvICguW4N+HZfp9znMT639wCeM6v+PhebnVAZpKuMBevQ9kFhpVdPStPfjdSCb+KG83/svf3fLD0SM5Lc8rsvyvLqQphEZ3cG1aWuG/YWPye+0kYUAEEOpeG/ybau24N2FG5VLk9S1y05q7kjkel/wqah9qydOuxy+TlRQJ6pkXfS6XU8hnAamhQ41+hDWTnttRHu/RjVyRiYCFQsXGmzHy9iQdkgG4D93+EiCYqELTE0p3o5g9F/88kwVcn2EQ/uKBqPlBFY9QdgPDnKieatXC0XdxTK0vXAu3vUfXChJ9OKQJeNN9go5HEfNsQBiQLOad0ruMnWs/2qd8/0807IF8LGvnZRpgc2XWmEmI+Gn4vu/sddMNqGl/uSr5KH7XPZbDfrmqVvfOBNN0MlGnkePpFmNTQGcx4MDKYg82+Wav4I9NgrTwyJ6Hs4OeJHRnh74c6ZPvSTOJGmxhwq7JifEQA0USRBHX17hr4SFqgKqy5oHCVstvFWPiPqdO5ErxtWWxNJZN9LDA/MtvBdDXxT/Itljt54bGi2zAlM27aWWewebN+9tElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwugkZaNFTWRvmGTppiQTRIpa5WTA0BUttHJjwHfbiKzTSfbF3i4rBJFbdP/BEBz5MoJK94Lf2FS3g4rl81c4pBw==" + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAD/skSeTarqCqe0vjYTUvCoawVtnPDhPHtPpjqXK6wdKv2wxJAE64Auyklx4Y3uX8RztZ0ckw3/+Mjg1/51HYUTVpKLMmCpf1rEvzQnvV92m3jSj+IzuEsTZRkljD/pIX1CdVMYRBnh/XzNsVIzRzGEVplkwfEqkQpSpUux8E+JgF3Ti0AZAznSB1c6+x/Wmh5qe1uw/77V+C5KBroeWjy5NFUAIYZ9u87EsFBzv2BZCBhyI2LkfEaysqiP9ubrOKgn+onVA/RZm8B5z65XdHI3nnSLhwIYFSQDD8PQmI0aergB/Uz8SP7x4u2H7V3vChcQjwjVgVCY6DyhC4y7Dml5CVTXRGGgt3YCZ63AviT2osCh4qcskNOvQOl/5NMMIOjLifaGvA8IcZuG11KJaBU4INPGExt99M4OlI7fyJETIWKF0cY3+aBCC/WTefDkLruW8QZRuzbFpwDksXa4RdQJ3dIVXCAtZxDghjEdP+TNP7RPBLvxYRVcgHPcMMwTj4wJry3VS7Ok+7W3S9E5H2k1TMcYYKPGSXTR3xrNZ6kR1S8+cnU1hrU29JPARVEMt8/pSWBBKjXJbdgIJXj3VsXFeAlTjiA4gRghJgSaJbEM1JSz5h5YeFZElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwlp5gzQJP+7MqrO86KBbpf6dCN8r60DlUmPRF24xcy1cGDYrj+svj3naKTkRvvpf+ToosXMPzELJhIWqvm+4+BQ==" } ] }, { "type": "Buffer", - "data": "base64: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" + "data": "base64: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" + } + ], + "Route multisig/createSigningPackage should verify commitment consistency of transactions": [ + { + "version": 4, + "id": "0379b1fd-2499-4343-aab9-c64f4906a4e1", + "name": "test", + "spendingKey": "f30290a4aa6b9cf9453d6b1fe8c7cff10c5fe2e4cfdbd3f9d215e0b413c437bc", + "viewKey": "ebb379cf8516ea35348150b3265926f53f11decd0b87c4827abf328f92f8830af566575b7f2268bb1a2a498df062b70711ccc3d6f5f69de0fac42bccce828a23", + "incomingViewKey": "db71bb9324a0ee17fe3e03d6d65c9e2ffbd2a4eacce9309953429e81bc453400", + "outgoingViewKey": "4fb5cd86117e7afb274a044bddbb91a4b00be2c6c5ae677e800b5a4efd72e154", + "publicAddress": "04eabb613609395afe1ece0b2358a4d01a341712f9a6ac30975600c327a94829", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "proofAuthorizingKey": "10dfc1d93f218884408853665ae561ee2de8af5f3b0a6773f759ab8a8c0d7308" + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:GqH3sVMbIt5MAryBPNQqs0ZqgbGh+rJlBGt3A02Mdyw=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:JO8B35op0DI5hSzoGFvZVoDfPgN8zR88w9oOEf+lnTo=" + }, + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1710203899545, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAMiNfmRNruNZf+R8/c4FkodqwywRUWVYNcD+/4s5mk6m1q1ndYqHzABdgLzKUxsQkvBwLfSacoVsnbT4/fjou2wO9hzXZ8mFKjLRp02JqoFiOCpy1PMxabkUkiP2/Zvh82qkW/ABiyMcJikBUL7XMu5/4iiCkABv0I1qjVOgdsCsKU3ShA9z22BAqSIszYjqe8m5WBa2bX4Gg6AjXv27PE7pjN1capbI6mNoMcINYpnKMdW9lIO7Z4tUxWVv+uHyLbtrvKZG18NjJC3kPywYwXxAwZV82hIG47GqZZ02kU6z2VUzm8lz/q/cHZcBgflWGvhPdqK/kX1+PkINX5DgTCSIYF4oe5j3LZgg6nheI/f7vOTKIyqADiTYnqjCyEY0YWTkjEGqkoU4K6wLcDf1BSEboONYGELIXbBLcaqoMSm53uAmhhThKQ3MPEGZmqkPZQBHj754EWY2t9j9ssZaZ9FnGlJpr5PFeX+ljFZ6t+Jak+BL39grw5q38tkAUVAjsiBBWLOUSmWD5dJEOOhJbTm2nBzFyEFhNEvpFr2yyxAoYdOaLNroh9T8c47CDMOp4HfmRcp2DZZESDs8UdJ+yPzeZOKke7nvvirCtWYvO7NBASrCCrzJ2Q0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwWT246C2oy8KoJR+y9tTWCNkpvcoKXg7NoghrP6Wgde1zVSMI463Y7bTyJDUe6OkZo1vg+jj8riO6wAzCCC/bAg==" + } + ] + }, + { + "type": "Buffer", + "data": "base64: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" + }, + { + "type": "Buffer", + "data": "base64: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" + } + ], + "Route multisig/createSigningPackage should verify commitment consistency of signer set": [ + { + "version": 4, + "id": "9357293c-68e6-4c29-9968-0922f478a210", + "name": "test", + "spendingKey": "4618b19e7337351a1b1fb08da52f817887d5a9e18a336d0dc7b6be5bc48d46f9", + "viewKey": "b4b4cb192a730e0dcb5535ac158e35f62977dc007f19ff13836c42e0114ced958e53a69abb1da99167ddee884706476e873e5a04a610c8d007ea94138e04514a", + "incomingViewKey": "8baa8ebcff5517769a52bcb84402d88a838b2142ea4fffb0b479afc76f9c9f07", + "outgoingViewKey": "79ba62760c699242b85daba33b96a54095bea04c5c8045551883e1afd2c6adda", + "publicAddress": "1c2102bcc1bd0b858f31dc586e4ea4086a7811f8dd764b6af2704fcbd7fa368e", + "createdAt": { + "hash": { + "type": "Buffer", + "data": "base64:R5HXrp+X3xAO8VWOhHctagm0N2I4goP3XG8goyqIqoY=" + }, + "sequence": 1 + }, + "proofAuthorizingKey": "bf5a357446888cb624475d7a2aea6b8c3a08a3933c884bcd486310fa84158608" + }, + { + "header": { + "sequence": 2, + "previousBlockHash": "4791D7AE9F97DF100EF1558E84772D6A09B43762388283F75C6F20A32A88AA86", + "noteCommitment": { + "type": "Buffer", + "data": "base64:UCVw6UmLkCp9pbhvcCklK+guwyINWPeO+uuu9kq6YmU=" + }, + "transactionCommitment": { + "type": "Buffer", + "data": "base64:zLnZjKE6YqflTGr7scM4QSNK7qkcS+p8/JaPzuO2Occ=" + }, + "target": "9282972777491357380673661573939192202192629606981189395159182914949423", + "randomness": "0", + "timestamp": 1710203903331, + "graffiti": "0000000000000000000000000000000000000000000000000000000000000000", + "noteSize": 4, + "work": "0" + }, + "transactions": [ + { + "type": "Buffer", + "data": "base64:AQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAD8u94pZlUKCKUb3D7gO/9EQRYmwQ3h8r3o+e175jbdmK9gvWcnOaPiVFuvKGbwZmkMxS4zyoVOBMFA1CrU9Anco6e7HQE/1Zrw+kcfj8tCOLzuQLtiMslAMWUcEmEYKoV2I58uGnQtmYn4MORY+pd2sWC9oNyNkZFe+3oU58okEJkmmtbdKApC0hRkvSK6XUOX3g4YMFh6k+caeHzMb13+NwniHgZCfKeqDd/nDIh4CyZRSCnmZaJtOzqZGwJM7pjy35uzwl9XIhGcvQAaqYTFrM06idEfq6bkNHxNIjU2hP5hWvXMOejHR1iZymdgktpeoWlxam1UgjBWFGg1CnqLhxjtuRM5lYl+Zh4aGzVVbhWrBErxkZolTfTT9F+TEH2An76oREu2FkChQT/5DwculRTG/zZj5sJ+MK8UiSqcQxyIxtu+9ETlNEVfXY5+q75q00KAvw0XxD1VDAPSCIt5NbTCNI66Y+umCZDemx3EduKhmcMEtSpd1tlmmQZNMAt4Mj/uOZ/M8lKFeKQ8TXwS9wSUaVwIdCBDypr/+hBplRwAVr1bSSEBgHQVi4E2JBes1BZocE3zLtFRSpB6tLoE1BK+o1EG1SBJqT0kUfowOv8Tz3CK4Bx0lyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwYuE4RTjMR2F/ied3J5hDzENPh9dMXga/WRCbKpdsvcun8Q14GCceL09C0tiU3k0rC0q1BrVRCzQZctfpwJNkBw==" + } + ] + }, + { + "type": "Buffer", + "data": "base64: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" } ] } \ No newline at end of file diff --git a/ironfish/src/rpc/routes/wallet/multisig/createSigningPackage.test.ts b/ironfish/src/rpc/routes/wallet/multisig/createSigningPackage.test.ts index 3c10f280fb..00dbbc27ff 100644 --- a/ironfish/src/rpc/routes/wallet/multisig/createSigningPackage.test.ts +++ b/ironfish/src/rpc/routes/wallet/multisig/createSigningPackage.test.ts @@ -3,7 +3,6 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { useAccountAndAddFundsFixture, useUnsignedTxFixture } from '../../../../testUtilities' import { createRouteTest } from '../../../../testUtilities/routeTest' -import { RpcRequestError } from '../../../clients' import { CreateParticipantResponse } from './createParticipant' describe('Route multisig/createSigningPackage', () => { @@ -72,97 +71,268 @@ describe('Route multisig/createSigningPackage', () => { }) }) - it('should verify commitment identities', async () => { - // Create a bunch of multisig identities - const accountNames = Array.from({ length: 4 }, (_, index) => `test-account-${index}`) - const participants = await Promise.all( - accountNames.map(async (name) => { - const identity = ( - await routeTest.client - .request('wallet/multisig/createParticipant', { name }) - .waitForEnd() - ).content.identity - return { name, identity } - }), - ) + describe('should verify commitment consistency', () => { + it('of identities', async () => { + // Create a bunch of multisig identities + const accountNames = Array.from({ length: 4 }, (_, index) => `test-account-${index}`) + const participants = await Promise.all( + accountNames.map(async (name) => { + const identity = ( + await routeTest.client + .request('wallet/multisig/createParticipant', { name }) + .waitForEnd() + ).content.identity + return { name, identity } + }), + ) - // Split the participants in two groups - const participantsGroup1 = participants.slice(0, 2) - const participantsGroup2 = participants.slice(2) + // Split the participants in two groups + const participantsGroup1 = participants.slice(0, 2) + const participantsGroup2 = participants.slice(2) - // Initialize the first group though TDK and import the accounts generated - const trustedDealerPackage1 = ( - await routeTest.client.wallet.multisig.createTrustedDealerKeyPackage({ - minSigners: 2, - participants: participantsGroup1, - }) - ).content - for (const { name, identity } of participantsGroup1) { - const importAccount = trustedDealerPackage1.participantAccounts.find( - (account) => account.identity === identity, + // Initialize the first group though TDK and import the accounts generated + const trustedDealerPackage1 = ( + await routeTest.client.wallet.multisig.createTrustedDealerKeyPackage({ + minSigners: 2, + participants: participantsGroup1, + }) + ).content + for (const { name, identity } of participantsGroup1) { + const importAccount = trustedDealerPackage1.participantAccounts.find( + (account) => account.identity === identity, + ) + expect(importAccount).not.toBeUndefined() + await routeTest.client.wallet.importAccount({ + name, + account: importAccount!.account, + }) + } + + // Initialize the second group though TDK and import the accounts generated + const trustedDealerPackage2 = ( + await routeTest.client.wallet.multisig.createTrustedDealerKeyPackage({ + minSigners: 2, + participants: participantsGroup2, + }) + ).content + for (const { name, identity } of participantsGroup2) { + const importAccount = trustedDealerPackage2.participantAccounts.find( + (account) => account.identity === identity, + ) + expect(importAccount).not.toBeUndefined() + await routeTest.client.wallet.importAccount({ + name, + account: importAccount!.account, + }) + } + + // Create an unsigned transaction + const txAccount = await useAccountAndAddFundsFixture(routeTest.wallet, routeTest.chain) + const unsignedTransaction = ( + await useUnsignedTxFixture(routeTest.wallet, txAccount, txAccount) ) - expect(importAccount).not.toBeUndefined() - await routeTest.client.wallet.importAccount({ - name, - account: importAccount!.account, - }) - } + .serialize() + .toString('hex') - // Initialize the second group though TDK and import the accounts generated - const trustedDealerPackage2 = ( - await routeTest.client.wallet.multisig.createTrustedDealerKeyPackage({ - minSigners: 2, - participants: participantsGroup2, - }) - ).content - for (const { name, identity } of participantsGroup2) { - const importAccount = trustedDealerPackage2.participantAccounts.find( - (account) => account.identity === identity, + // Create signing commitments mixing participants from different groups + const mixedParticipants = [participantsGroup1[0], participantsGroup2[0]] + const commitments = await Promise.all( + mixedParticipants.map(async ({ name }) => { + const signingCommitment = + await routeTest.client.wallet.multisig.createSigningCommitment({ + account: name, + unsignedTransaction, + signers: participants, + }) + return signingCommitment.content.commitment + }), ) - expect(importAccount).not.toBeUndefined() - await routeTest.client.wallet.importAccount({ - name, - account: importAccount!.account, - }) - } - // Create an unsigned transaction - const txAccount = await useAccountAndAddFundsFixture(routeTest.wallet, routeTest.chain) - const unsignedTransaction = ( - await useUnsignedTxFixture(routeTest.wallet, txAccount, txAccount) - ) - .serialize() - .toString('hex') + // Try to create the signing package + await expect(async () => + routeTest.client.wallet.multisig.createSigningPackage({ + account: mixedParticipants[0].name, + commitments, + unsignedTransaction, + }), + ).rejects.toThrow( + expect.objectContaining({ + message: expect.stringMatching( + /Commitment 1 is from identity .+, which is not part of the multsig group for account .+/, + ), + status: 400, + }), + ) - // Create signing commitments mixing participants from different groups - const mixedParticipants = [participantsGroup1[0], participantsGroup2[0]] - const commitments = await Promise.all( - mixedParticipants.map(async ({ name }) => { - const signingCommitment = + await expect(async () => + routeTest.client.wallet.multisig.createSigningPackage({ + account: mixedParticipants[1].name, + commitments, + unsignedTransaction, + }), + ).rejects.toThrow( + expect.objectContaining({ + message: expect.stringMatching( + /Commitment 0 is from identity .+, which is not part of the multsig group for account .+/, + ), + status: 400, + }), + ) + }) + + it('of transactions', async () => { + // Create a bunch of multisig identities + const accountNames = Array.from({ length: 4 }, (_, index) => `test-account-${index}`) + const participants = await Promise.all( + accountNames.map(async (name) => { + const identity = ( + await routeTest.client + .request('wallet/multisig/createParticipant', { name }) + .waitForEnd() + ).content.identity + return { name, identity } + }), + ) + + // Initialize the group though TDK and import the accounts generated + const trustedDealerPackage = ( + await routeTest.client.wallet.multisig.createTrustedDealerKeyPackage({ + minSigners: 2, + participants, + }) + ).content + for (const { name, identity } of participants) { + const importAccount = trustedDealerPackage.participantAccounts.find( + (account) => account.identity === identity, + ) + expect(importAccount).not.toBeUndefined() + await routeTest.client.wallet.importAccount({ + name, + account: importAccount!.account, + }) + } + + // Create two unsigned transactions + const txAccount = await useAccountAndAddFundsFixture(routeTest.wallet, routeTest.chain) + const unsignedTransaction1 = ( + await useUnsignedTxFixture(routeTest.wallet, txAccount, txAccount) + ) + .serialize() + .toString('hex') + const unsignedTransaction2 = ( + await useUnsignedTxFixture(routeTest.wallet, txAccount, txAccount) + ) + .serialize() + .toString('hex') + + // Create signing commitments mixing participants from different groups + const commitments = [ + ( await routeTest.client.wallet.multisig.createSigningCommitment({ - account: name, - unsignedTransaction, + account: participants[0].name, + unsignedTransaction: unsignedTransaction1, signers: participants, }) - return signingCommitment.content.commitment - }), - ) + ).content.commitment, + ( + await routeTest.client.wallet.multisig.createSigningCommitment({ + account: participants[1].name, + unsignedTransaction: unsignedTransaction2, + signers: participants, + }) + ).content.commitment, + ] - // Try to create the signing package - await expect(async () => - routeTest.client.wallet.multisig.createSigningPackage({ - account: mixedParticipants[0].name, - commitments, - unsignedTransaction, - }), - ).rejects.toThrow(RpcRequestError) + // Try to create the signing package + await expect(async () => + routeTest.client.wallet.multisig.createSigningPackage({ + account: participants[0].name, + commitments, + unsignedTransaction: unsignedTransaction1, + }), + ).rejects.toThrow( + expect.objectContaining({ + message: expect.stringContaining( + 'Commitment 1 was not generated for the given unsigned transaction and signer set', + ), + status: 400, + }), + ) + }) - await expect(async () => - routeTest.client.wallet.multisig.createSigningPackage({ - account: mixedParticipants[1].name, - commitments, - unsignedTransaction, - }), - ).rejects.toThrow(RpcRequestError) + it('of signer set', async () => { + // Create a bunch of multisig identities + const accountNames = Array.from({ length: 4 }, (_, index) => `test-account-${index}`) + const participants = await Promise.all( + accountNames.map(async (name) => { + const identity = ( + await routeTest.client + .request('wallet/multisig/createParticipant', { name }) + .waitForEnd() + ).content.identity + return { name, identity } + }), + ) + + // Initialize the group though TDK and import the accounts generated + const trustedDealerPackage = ( + await routeTest.client.wallet.multisig.createTrustedDealerKeyPackage({ + minSigners: 2, + participants, + }) + ).content + for (const { name, identity } of participants) { + const importAccount = trustedDealerPackage.participantAccounts.find( + (account) => account.identity === identity, + ) + expect(importAccount).not.toBeUndefined() + await routeTest.client.wallet.importAccount({ + name, + account: importAccount!.account, + }) + } + + // Create an unsigned transactions + const txAccount = await useAccountAndAddFundsFixture(routeTest.wallet, routeTest.chain) + const unsignedTransaction = ( + await useUnsignedTxFixture(routeTest.wallet, txAccount, txAccount) + ) + .serialize() + .toString('hex') + + // Create signing commitments mixing participants from different groups + const commitments = [ + ( + await routeTest.client.wallet.multisig.createSigningCommitment({ + account: participants[0].name, + unsignedTransaction, + signers: participants, + }) + ).content.commitment, + ( + await routeTest.client.wallet.multisig.createSigningCommitment({ + account: participants[1].name, + unsignedTransaction, + signers: [participants[0], participants[1]], + }) + ).content.commitment, + ] + + // Try to create the signing package + await expect(async () => + routeTest.client.wallet.multisig.createSigningPackage({ + account: participants[0].name, + commitments, + unsignedTransaction, + }), + ).rejects.toThrow( + expect.objectContaining({ + message: expect.stringContaining( + 'Commitment 1 was not generated for the given unsigned transaction and signer set', + ), + status: 400, + }), + ) + }) }) }) diff --git a/ironfish/src/rpc/routes/wallet/multisig/createSigningPackage.ts b/ironfish/src/rpc/routes/wallet/multisig/createSigningPackage.ts index eadf32f0bd..6063ec69fa 100644 --- a/ironfish/src/rpc/routes/wallet/multisig/createSigningPackage.ts +++ b/ironfish/src/rpc/routes/wallet/multisig/createSigningPackage.ts @@ -45,25 +45,40 @@ routes.register identity.toString('hex')), + const identitySet = publicKeyPackage + .identities() + .map((identity) => identity.toString('hex')) + + const commitments = request.data.commitments.map( + (commitment) => new SigningCommitment(Buffer.from(commitment, 'hex')), ) - for (const commitment of request.data.commitments) { - const signingCommitment = new SigningCommitment(Buffer.from(commitment, 'hex')) - const identity = signingCommitment.identity().toString('hex') - if (!identitySet.has(identity)) { + // Verify the consistency of commitments. Loop twice because the first loop + // gives a more specific error message (easier to debug) + for (const [index, commitment] of commitments.entries()) { + const identity = commitment.identity().toString('hex') + if (!identitySet.includes(identity)) { throw new RpcValidationError( - `Received commitment from identity (${identity}) that is not part of the multsig group for account ${account.name}`, + `Commitment ${index} is from identity ${identity}, which is not part of the multsig group for account ${account.name}`, 400, ) } } + for (const [index, commitment] of commitments.entries()) { + if (!commitment.verifyChecksum(transactionHash, identitySet)) { + throw new RpcValidationError( + `Commitment ${index} was not generated for the given unsigned transaction and signer set`, + 400, + ) + } + } + const signingPackage = unsignedTransaction.signingPackage(request.data.commitments) request.end({