Ring Signatures in the MLSAG era

Let's understand what are MLSAG (Multilayer Linkable Spontaneous Anonymous Group) signatures, how it differs from the Pre-RingCT and why the Monero developers implemented it on 05 January 2017. A simple description in layman's terms and a comprehensive explanation of the verification code is provided here.

MLSAG in Layman's terms

Before talking about MLSAG signatures, let's introduce some definitions and vocabulary to facilitate the understanding and motivation behind it.

Two innovations were introduced at this hardfork (v4):

  1. A generalization of the ring signature scheme used before.
  2. Let's add one more dimension into our visual understanding of the ring-signatures. Let's pick our ring from the Pre-RingCT era and add on top of it another ring that contains 'Differences of Commitments' $C$ as illustrated in figure 1. The exact meaning of C is explained also in the Amounts section. We can see now that instead of controlling just one private key, we will control one key vector, which is the pair [output, commitment] and we will generate a matrix (M-1)x2 of decoys, where M is the quantity of ring members.

    Fig. 1 - Multilayer Ring (Public keys and Commitments)

  3. RingCT (Ring Confidential Transactions): which hides the amounts transacted by setting it to zero (in the Pre-RingCT era, all the amounts are visible to anyone). You will find more details about it in the Amounts section. For now, let's try to understand how we hide the sender of the "0" XMR by understanding how this generalized version of the ring signatures work.

The biggest intention of the MLSAG signatures is to prove that one signer knows the private key of the entire key vector and as a result he owns the ouput being spent and the commitment amounts are valid. The proof alongside with the verification of a transaction is left to the next section.

Now you have an idea what MLSAG means:

MLSAG Signatures in practice

Before understanding the generation and verification function of a MLSAG signature (generate_MLSAG(m,PK,sk,index) and verify_MLSAG(m,PK, I, $c_0$, ss)), we need to understand its inputs arguments and to what they relate to.

Message (m)

The message represents all the information of a transaction (version, unlock time, inputs, outputs, extra, rct_signatures and rct_prunable) except the ring signatures (MGs).

Here we have the message that the person is signing with his private key and it will also be verified using a verification function that proves that this message could only be signed by someone with the knowledge of a private key related to the published public key.

In the MLSAG era, the message is divided into three parts:

The final message $m$ is the hash of the sum of the hashes of each part, i.e. $m =\mathcal{H} (\mathcal{H}(P1) + \mathcal{H}(P2) +\mathcal{H}(P3))$

Let's get the transaction 2790982c9a44ae9516e395735c58f607d13741f8fd054bc096997aebc0a280d3 as example.

Here we have:

P1 = 020002020006e4ec6cccdd03b4c006bce819f1e70f8b51cc4b72bc6db48f7e203d8f0dea1a0e62ca5a19ef35ecff60574b22af49aea2d8020006d7fd608dc10be2dd0fef8e228a388bf202b1bdff909d41301ff5300d0a356d82e71ec3a6e9e439abed5b43b32cea103a6e020002d8a31b9e352117fcb6330f22e5e5f91c87f18d332bfe36ab5ec3964d4787f37b00027cd1843b657583cc97106d7b2f767198dd34b0bba0f274356aee852b03565fc721011704fac2a6ff81920b4fcecae3913a1d50a56aaa2e7f3b1996b2abfb1608e8c2

P2 = 0280d6f0ba3585de4f299dd2539b2dbfcfee053a2cbf5a9603f912db63b1e480bea628ea8f69fdc80cf66a9bf9015722fb12949b4b97f418ab016546e827fc52848f86a6802be0de0f2b619a498432095c3276b3997ab57d27a3fe7a16695613db824bb09e0d96767c987732a7fa713d191f0320c45655ba4af89bf609fc9ebb3028204ca30a050bbc289f30b863a11eb53022e072a69a609ff3d6841326fe286e3bc8aa1a05766bf1862a9dfa2fb1f56635422b30473a9f49175e7f0c8e5e6f36138367aa0e0c4e72cf7d55dcbdb300539980d5efb0f23b75531aa08def2b4b73e12d1fd6d154a18c2830ca3484189a95d907176f6c78d67916841348a5f54e1356590adad1

P3 = 1ce3efd48d13542790fcdbabacc6fb8775327595be641739856d13d1b05ed90e36a8551d9506eb1c519dc106c89332f933924271889cb1ab047c76140490230a26ee30b1b54de0684b897cdc6848bd29ae8005ce5fcc835a622630b6b6a5a100de3d01b85b7ed28911cb3bc32d7877146a4c5d7e6a31506bd076726e2f9ef206b1aa9a2160a2116e39137887f453e4c678069dff1bf4f02cc7a8606056e6af0d671b7b94b0a7de06437a160bb4b6396ec1902f14babb42806305c48c8f64590c2d68f0bc69286f71ed09d6222cd703594c69715e7eba8f1f92f6b032230aa30571a5c906963f58229af5f66ba3eec5cac26b582b08db8c53b34528a5b6e75c0baac281bd3879bcc53d8b2f722c2e17a53c1f3a7edcba44a966235c5404307401f4677c614fe13f93c51dfa656136919c5c609ca6e1654389e4c89c710cc35c0038283a9672a2a8d308869b7c6cad01ddc33675ee3b690874bee0a6b9ad03570bec10400ff549cd8b39b021d31909edab595ac149ffd645e54d09f6182403a809750d24a00d12dbd6123c9ed9e6f6232d5570cdb893e19ecc8539241be3a1e8029cedbe62276274a458e7a008f1ee05d7ace565943d7858b5b4bfa3b15079f404862e08cff6f748f22102e67f7160b2c8e98d8cf7427eddb1a87391b8224d99018d6b2f836784518d923a58d399c605bff786e1f062fa11f24a05a9405f3ebf0735743066348c714a20b9d8dbc2106392455d72474b7e67ed6eaa2dd45d6a9f066c7bca888e2e1932405ac9661feef6471c2a34b6bd50ebd877a5f1f36e4e6b082b531be3e63e42a798ed65bb8fd6c48f75f1d23730aad6171d8fb5461fd8d80a158686a53ebbabb4f7c37ff46eef365955df82665f8fe844b68ce105ef141201f70c9eed4bdbeddcb6485f60d74e06ffc626c70165b518f4439e741feb24c10775521b328cebc41269394c92ffb14a7fc9505cfa6cfb7465df0bd8f819e0c50ec3bc2204e781e1f2838303763a4e2035a267a76ea1ccc82afcd9ad04fee10c0da8f923ab16b6cfe4271a5f3c741a17e63e9b080da331434287807ea41bfccf0a6843a41e37738eca495397f578ec2ab6b88b42cb68f0cc9ae1905b3c179aca0fe43a03d89b85e98e76abe5fcd089bc7c664d63ee968cce3f468d20debb68c006f8e442f56e4f313af2d502c7bf0378f697e9bb009b8daa5e6a38f113a89d9d010963fbf24caab6488fabff5203b12eef5ac3a2a3c023cf33aa5ceec81edbe20f126119141778585b0d170d4f87ccaa543c0cfb715c335c012f1bd78d2c60ad011720bce33ded72d39fa73068c96901135ce89c7ed519c5472103b855ed33b102e7fa306f6673bc9c742ca425640706466cc7b6a25add5dbfc0d408f0d0c65e064364a402cc731c341e13d3b5822715c51d09939223366b9c538fcb6c0ea17d02bdec7ee833ebae1a10604dcc9c74875fea3192c007c9ecef7cc233995f98a005e5f7718a2f8fc6672804830b1563eea59a87aac9f26cab08b48c39315727f900681a0d2b3939a1c7c36981df328622247ed11365e4bff3fb5388e1d3761abf061b55b2e17f1180f09d565aebdea6e9a50be4565c7dcc22bddba9898653f3c0054f7d350209f60622c7f85a36dc38be8fc2a4c019be8b9145742dc8eaf885b5068b1a42159545702af04e6330c181d4a2a15405ba31f121b74748a5d5ca1dd90debd460fa08640085326e603e0b13752b69869350789e0541f667589988df6d04f84c25d614ffc42c843895237dcd345353900ba8b50d188c58a4bc025f69200aa4ec43335841e9bfdb45c4a29999e0b23dbc32d9831a50c2dda51349fe99de0de6d5a59b7d6e8c950f09bce450504e8dc6c19bbc6b24973845e4438fb78765092da8779a6b2f4b99dd1abbbc58a3c32d67098790f2ea6915d25c3060cc29770c0984322ed1f505d195ffeac66685bb22120a27751e08e14bc2f43bce33bf2708dbd574f1b6b67cec79dad89738826cd087a5cfbd21aa22611affe7f411ffab0bd97a81246d0d58c73953b228c73ee7c745f78ec752294c09d1a779add7dbd409f568b6208a00b2c6cb29ce843a81e262e3f94693223581574154edcc6478da072349847cc1fa8dfda8ec7422e9c87f0ce63430663eac71b1c8522cd8aaee130ffc4bf4f1ccf3aecc0a9d537dfeaae19a21422a0fc044771055116be2b63a900a4f846cda54dc8964693dae0bf83c00b8c7d84f92f469bfe2729cf7c4a1655409acfe39db76a0fcc50fb289f1673a412fe669a25a1264e64945defdc9b0a1d30d62817e01adcd07d87c5fffa2ede822c1b0274465a65ff89b476ff0cb16ba1901a91d1b7d7c0996afed5badc4967b772e3d3483a9a42b1ad87ad7af0dd9fa3506c724756095325d148807e542d7c4dfb1081d319e55866c159e552f3a3508530a79b0b6408ca55401e9f4c0041fff7a1d9f0d64de61286dfd399968c1e283f40faf79e9de44fa5b1f47c54a1034fd1ad58ca7650c96ff721e134ec4f65989310e406273091fcc558e3b5ad993c9303a3a6e0d3ee7c6b336aea64fb27214f2640af969e7bb866a24b5e282ad8a69b9310530bcee7d3fb8fd855368500b9174ee0dec40cfdd604d8c68533df000793b4b27f3b4603ebd9845e67547f7405f0c310ef8eb6deefa992d88b0eb0fd557e0e9a395f5f2c93189e711c781e8e9de818108d992efb0351fbf8e5ebb021062995d6604e6ebc5f6891032ef529926a404260ad9cc42b2d0415570d31bd418990f0ca91867e57ca190b9bc3230093c57662702dd6ec4306be43cbe3ba6b6b84aabaae3593ad2a740ac505bf70e9fb244dbd20791a4886a0045820ef01b3dbdb6d171afcf696495baa98cca2f8f8c367100bd09945320f92904f93160a95291c68c0bcac4c0bd7c78b976b0969c85d721fa7b02b4dfcb535c428948c17e00ae672ef409bf48e60e83e9c6f7fa6c76c0c5f38a0bf6bc75bdcbd872694b95b062f512c1ea3e4e0db2ce04e827cf40e1dc3c58780b0eee11013853dcfdd1030525ab22fb021745235faff40487f41f7cd74220fd0ac50e14642e64febde11823ac1f17b67f594406627398f861e0848c8ff6356008dc6a5de0f829846bf62d46097d8d01a15cdc11eebca333441113fde3aac0790e811e6f37b6294a5a4add43900b77116b8f101d4bca9088a91dc354c0df73f60f42f72b70db3709cd60a0878736d5eb80b5f6bba2d1c312e8fe2a767a10415802e8aea4826f1b68c25b6b97a0b99a4fdb3369cd81ed4a32aac5d4502aecb23e00f1193d077fa013373927dcda9538209f553afa68ea62d69eec8d48c6b604fd0cda9e8e5d86fda9041dfe3bb2957f332026727821d8f723ead77a77899b773b0f96cdfa76940f91dbc2cddb72d166c3786b487ed33484329ccb08443406cca1052cd437a22fb872c5f586251f2bbcd709428ec889f7f6092e6b5b3b490773fe0503b05443961918f0b6cd1859fa7a2fc80c3cb4e2e8cfe3dac98fe822b0f18a040452467bd8dc4701a2417cce6c1e2614e4a2d52f073fc1c6616ff59d8ee8c10cd2369ebfefa29f36f94a0e12a2bbc9cf7b1ac2f43c216794b2b51dc5737ea20ca95eb4ac8475ab46b5144fbf2853ed8f5f9f70e3198febd559fe159f763b09006d1e59394fcc2a851ce31813fd3ff57f5665cb91ac53da1619204b8c1c0a9906e2ae08418020eceb6965a04df7bed5d6cca5302de59d5c1054878d73c87ed705755a62c9a5e62ec50e419639020e4334d52133ffb0a621bd5089055a41ceaf0abddf6c37b76c65e5f013f2803fabf15c818323e891757bc06e0f684aba340701e27548c29fea7789651354195197d3ace966be6450b34f2941fcea2e8adec30dda4defc8dd33f7bc57f9a814d2caa0e87a6ff11bb08c2813fd0b963e6309560e2ed58586a0e4c097faa1e654eed30784871dfcb2163f669a162f4e7db72eaf0eff277651748d4808b85f42b7f0ebf15c3899557e121e73fadb6aab70369891012747c35790fbc02bd3a1c0efb86f8f44dfb93cbb134a128aeed506db588a6c0fe98db96519c08615a01dd0056eaa18c90fb1353dbb86c12e44bafc6c8dcd280451d5891d11400c023efaaba1fbf7608ac27f1b80c3cc1fa33a09aedb34482d060a3e3d812c249f25b3baa3c5ef5397977defdeb8ce091aab1158b568dd9bf9016141f5b0544493083aa59c48fe46e90715710d64919659265bc3220a32a27c080c63cb47aa9589b39ac640eda72eda11ff406b36a27dfc29c2a9be058e23080cbde020b985916b854a5b1e08689b1cddee1c5f9e743eac86231b16592a5db90423fc2e2fc515209921d82ae752ed5a4f6184089eaab667ed0d08d38920fcea013b9ef749aa4da422ddcc697184632c4e9e4ba185c32a72ca5f513b2594c8320ebfa26447b71f673ce00c3aed65e60e09420161b2fcf431dca14f5e2d73d9c306fb685ee0f8634db58e27b620cc626527e7f93308d2a24d88de4d2ef33bad540d9491e21a3265892dcc01ff235f5301ba2bede8e4f20fcdf60ea85457ab1fc708e6a39e4c254a8a2224361d53f25c142243395a0f027da9934fd50e7be3270401cb1c30e5158771b091b9870d8bb89a5a2e31bf5f66a685cd65fdb1f0fa06650606c7174ab8d91f2bd7b8b1f63137b107f723dac417fe8d9b13dfaca6586c8900281abddea9ee150c718e5745f7d4a526c520a848ec7403234d33d29e2bd8a000911f1c2582c8d8fb364c81e547b52b3b0abae1dec37db80db68380bbc8e9e207682134a559bb389eac9427364a3a9faf64aa27b89f6df40102054f6c9304120374d3566671e96095576cea6e51b1af24bd0a6592c30af77fcf5f19fadea35505003d9092668bcd57526a73f3a889e273fc2a54730930d436fc9aaf6e723b100fc95901917e10cf45a774cf9a59ae2c98bfcb9495497fdb6edabc02308697900acbb12e4e34dbb0f1d5214603794942007e7aa7dbb499c5063cad955f3f1c200deb1038a07c7ed0fd969f3984f7b049b9a59341731bded192f926cebaa401db0381811a05036049ff8ce8ad17eb2dab452f642b1ae3d6a7a760ad208d262a540a01ebfcbc7a26632a939867b633f9b2b634d66bfea673c73c4910a4738777da0a2cc41edcde3d0dd638a5b0373ed28698cdf481ed62248bdced1d3408a4cf4f0d2ce187e296fa3006ff938dd6d95edf4715eb7a2030626a95cd647d15a42ab20c25ccb7a40dbe65a72cae5fb4d0f2124c6b9ffdd984b61f93015794628c905a0e46e25efe0d2cf60d4bdbcb268b3585bcd28c6af70bdb6444ccf64f3330a8a607a7113efbb19c2e62012b76754287d3b35260dc8bed9df3ee60f709187f47520cbc3e724ffe3989a90344c62378660c30a4e9b3badad04ecb1b752d6ccff1370098e05168cfc7bff967fd1ac14135c60517f083a2d9910cb7d9a7ac8089be870eed8d8523c4dec23fb10f469861aa071dab71e0998fd9bfe90e938553a35f46052d9c7a17cefa315b0d2c17556286961f414106c31677372722a59a13ab45890c65eaa24e843ac0697e16537188c73a78aa586aad58dd7cc89f3bbbb0d942f50b943697dd824e79b631cfb60833a74cab5079d7f9e96c8d5aa3bc741ca48dd30133a63dca5e7622094e0f0e2de4ba549ca779e62b125369e245a4bbdb9a3afd065582705d336232ab0cd52509f69acf2db17074be8da9cfa702ef14add5e2d809536f6c03945031d817e96efbbf64e21ad10ecbbb2b849e8102f6c83168eff901fbf79d3f88993a7b68f2873c8db0678ee68737e1aa09df9e5b38d680a82ef70f5fa98896ac6926266ee9c7454b0db4fb92206b1d4c4cdfd6f9ba27eaa7d28663d314359f7d10b921aadd49be68d6b327be821f896ca45b48492a880db11936fd0b6caea5a9a24eee9e71afd214e5a9d78ada32b8dd4095f89a2d5266119eba6279c0a13bf47b0ac4d37f458f62d22a720895c2eff2b9fe4ba826958592bcd4ae6cd4b526f2ce712ee712f39acc4c17ac7677cb1bc8f496e1bbd51a5d2e7d8534ba15d06580f798054617e088e9888814d538da7f0bcb967def9c17c1e8a6d1e3c58217521bc7e884afdd2c6477bf496f475d35cbc48a15b6471ed1d108f3eadd919c4de0dabad02a4c2005dad8259e3720afccb80c4e42f061ecdc3c0e6fde8a569fea4fffd572265802de23e85621c4f5e333fb1b22742992f17a275e8d9897c9cd287e22ad31838621463b513f554409dee99866cbe79dbbba3f22374f25a34e272c182cf82883736f68d46819f0cd99b152c888ddfa3a011ddc1cc23991f6dba78e96020999920cb797f8dcd4db6f9e4bcb2759e06565de07aa6d3c25720cdba8a9a5722eaa332789c266e0784bd00ba1cb4ec358cd3bf696c01f8d4d1100a90bce6a2b366402ed45282acf0de183c07eac10a03a8f31398cb3307efd13ba125ddf4b2f8ea0fc4e4571fe368f28f3dda58275b9a41f25cace61cca90e6d1a66b91469c09273f5702d32a1441390e001fce1c3e5a4dcd868153b872f0e0dc3dea13e3c3985b44260e6e5dd46d9ec63c3e631fb30b2d7a0fa51be9a56594ee7263397ee0402e8b85313f9b153ff2539e5ddbacfd87e3127f683e6202fd26b8cbf72983f90af61a6b7542f76179d694f4f052ea5e728d123aaf8ce50c0ca58303b974c3bfc89bb488187f3411f2d06b0c03d90fc3af654647c88d644cf41362514abdfa2d8cac153df6b4d01744e24180be4b16ec3daae3a21d386312f57503e7a7221ae7171734f69a4e7c742d85c47b449bd706f870ab350bd26f5d8ae63473ea7769d1349fd0c4fce9cd04b091e21d767a9f0578ed1f4303d0c89f6ba318a55f13797b967bff0c4cd7225eddb310a7631d4717270bf9b89c6dbc86517a8c4db8a4fdff023b84b673d003e1e171397e040de878bda09b14616904a143aa51a5597b7525c090d3bd6194d78a239deb83f11fcf4f1d4245a9d632681f713610fe10ffe2560572cb06f55aad1a1b88d9c4df6d5095ba04b96b9553aec4232533c41e4e489c9208245dfa77a0ada32d3d38f9c946307833f74001e27c8dbbc2f09a244045a459c8768dfd3c912cc7d6f8364233b9f710b888217804c2993de799af312717831490da04bf8ede6a4d5496faedb172e1eb93dd0d195e77ea81fa11d0a545f786f1e532ebee90060d84530eac73f83cf8af14bb818ab119b845df51623901701fe889259b959e924eb445e24678f9803d4a0a42b869a315090cc179a1e2eab0f083724e21ca43117fcfddfe6f7d5473da3a19f7295deb5a9cf512e74bc541e9ae51398cf3e66255828697279829c8cf890394c23f40c4ce1e15d917ecc28363ed87bd7bb6a101120546932426589e389bee254f8e8bcb56dd55196672c6474e8f8febd46cb9d1b0cf537d629242910fc59c021789c8510ee1d9b53903c92dcd7fc611c3d3f7179c9ef14f83314a83b4dd68ebcb0a7e24c55fb34b54c6219014f843047657f14ef557cee573047241e5d9bda45124b4fccfcdb73d9f7d202090d8bf431b11fce0c40dffc69bde9e7cea3641df2dc1c23b53902131b920aa3e21205321367102aa5d8b58bd4064c17ce9dcf05aa0556407c038e0d3e982a1b30795ded94b5d7a2e04edf61e2722b2ec428ba2e6a0828456c6d984ef5ffb13e8e11203e282933966e802203ee7ae5570ba2b3408a80ddd0ff21a1cdeb678f711a4639e890c9eaa9f9d0834e83a7c5a535122ba628bd2cd2adbd1c264f83f0856084b972c4ed5c9a498f7bccd6db2dc599234c7f82d2fd50f1202f8745aab27bd677f9ac6a2101dac6b19b20acd871306bac2c7794b31e83f575e2b893eb9d99d67e2a4734b42cd485705df2c0447b0cd6cf50f091c0a9103c67b510e35466305c55fe0161558912a6bdcded1e0f05127c33ddb7a40a41115396543696d51aa46ff381a3fca6a8ecce35ae5a1afad11101bdaa0582866bf2e909b6ccc5d6855c09df272f68203d72e923e3f426efba4697755ce760f4e64bed6246cf01e9003c61367b6668fa59ba3cc3294549dac24a192063f31385f70f6a643971a99980f1d89310981cf2128935eaa470e0fe543cc98d376c7507ba8ef1b874acc980ab5927ffada9cb64bd2e5932975c6ba9c8727f085e0ccd76c0b03add2be9623e1ba1500c0c504d3a7ed3e83c3f477bdf97f2c9c1a5c2ad175e60cb57686b2cbdc92159621f9bc9b92fd8ca7fbfb3bbd5eb7ddd1438fcfaf1986f6638efaeae527b28e0b9038043417fe61c146b1086d60c0805eb1d6c5d51890aae07bf5c63ea07861150c47c7e94ed04abd38c4d3fc0380bde32af1358ac0bedbc5465d5e54940a691f7a597b33dced83b233c96e2b415b08f267261783c9690f7db30f2a94aae193ae74b1b2d11843aeb4670d0428f7d82e5d7504a38cac62543717321ee2303515f88257fb37d0edfe008bbfbd84968357fb6b9c778640e31a3f1de4467b7544e048de09e778344656321d45816a539b878af4d2984be877b2d06ef555b2df4c1cc16038439cb22327be8c1d0545b39abd2512664cc4a6d7a4f638b8c7b5f05c909c089f5082428e9e785447a2d6f5151628311af1ba370a81f087a5c170af7895431e2024c66e20510d8e97349a03a4009049f175fd2cd97faec27bdbf6fb47c11189ba6745304a6b08f55b2fdcc158f119e76b63134d9c3e743f115dd1bd17035cb393d73c2de9eb0cc8313adcfc47e558f82e3e69d3d09f3d6e794f4013a27e8e5dbbf811391855e684c44ebf223f24e87ed5a4d72658fa1e1622ab1046307807937481cc37a80c3d8ab4ec9d20d2418547c005f73205f7b4728d43307df145f58c090caefa74a8c5475c37b75c7c74dab4f27be9cc12671428083200a274c4d856cda57afbab7ef26e7b16795d26c9fe9b4243a65b0b600544134720dd750a58d976b9a48d478262711c1b2e525fdb40904ef422238737d54a759130311561dc39dd98eb3e05c48347d780f5af790164ced537ac3661983609a30930435925d0295e8390022546e6cf194242a141f2c6a820204b4ff132c5618d2c00edaab7c4e12dbfccc5d4e01cdafb1b4b4be92b28d8ba180fb5d1ff1979255380e4fbd0d54e53d95e0bd14b08b5ad956854801fef9f8d168c7f0c3c9a867f657018e188f4611d336a257ce1d6851883cd85aa97f82aab326b02babb6dd9b6e510317ea41191a481f69903ef4af389804564d2d047a76837f31f33e5fb216e1e70fc42c8a6267d564fd9da5b769e75f881bcdd855236f493d9ca2327e5e0bef0a0126d2345ac944a5f3af19f2331f2bafd35a7a2b781615256c61e7629b3dec4c081dc46ce018f5acbe594b2e261cf240ed905f633bdae77156237edb3ea0708409f3d37dfd7fe455b351ccaf8fdb23b59138371700f847a09c99d6a18421c02603a6e72c9d8ca9a0bdb4b26fe29dc20549bf01866cdc040d36243dd84f7a1bf004b1907754fa661e1765eb70663581f3e74df197f386c14a6078c20e32c876e006c460af4837db8e9f67b8f53458f7350585411b31941c350a9f55c409037eee0b54076853c596fdc97116d0aa90053726ac6fbc4da3300c599b29716c3c1f0200061113b0fc6ef7c6f001b911a520565e37a2f288a54a518cccd547513f7f520220b64b8440c68eb5ddc02f4199a47e1befa9931b973fb7ab227e9f3c6df2620232abd1f8970a4f3528e2deefcfda6c5075b73d4addab49f4bfa52120cc9d900c2e253b816493ba0471fe2cd1ed77ce4ded9cc5f215e323399df7529b6768520f6d6bb66a18fe7d512166b0482f8080304b3db2dc4da85680e41ec867e2c56e0822bfe156f9ac46b2b5bacb5397f6f448ce3b694c697c85355fbc2dd766425f05c7405b6633a358fe0e0353a53b7ec463e421f01e35bc0e3e764b965f0ea298066b4dc594d7de34b396117f75546e6265fba7155b21e7b57b3baff96a3845bd00bf8e73060a50517707a5fe326b5084020aa705b5f00a2d2aac8301d428773f0794bd82e738b69791c418b920fce2a0e34949d9412c22ec371e196c465c911a0e005e11874907ca9fa5e3f531de0fb00c227447d8ea2d4ca3d695b24d0da4f5021c18bf76d63ad0b536dea40cb24ad749c478ae4c3474b925b0e8a01199bdce0bb98735503922048a8cfaba207ece0ad1734f38d54cb8c46650b1144c1f9bf50c46c4011bcba36b0de2ce4475e2cf09c60d3444010f01aed540f4b54cdb4e150c6c3f1ab0f8f2a1b16154bc346f6d5e3c93fafe957faec4798bfd5e34aff2f60eaf362097cd9e5d7e9529a074479563bc81e57062c96f1efef7dabcfda099970a3bb9b8d386fdf925039e9153a3b749e10b0f4df1c60f0b3c4daeeb22c3775a00465098a468ba03ba01b5a2b7bdf3d4a0b9767972c3dd89b7da8336ad5452fb0180d062f9b83f330a81acecabddf2c8d93bdbf6d7e7126cc167658669a0ff6107e7a3535782cf3a8c2d6ab105d789842f99db2dd181b79ca5f3e8390b378ec30f1d26087f62427d7246efdb90e00df8203636e790ee0265d8a962da30b70cf2020ea0e4ad5ccce7e3d97d99d39c7650e6466254ec844b48069a82f81b7284a104a02b6e2e473e5b5b74817f29c02b5e491a37a93106a5aa70f828e9f9af531306deb672c98beb4e8526131e78db18f5c279110f54d31ce340248ae89e9fbbaf06889c0db8039054be395180c896edfb0441e2455d4d6389548eb6a3da28af510428487679e82972a085d0de2d17675f0d14dd6eb6d4b4523c4fe4a4f73bb3110dfd0c2018b5e3862b0d81bfef90a4e85ccfa98959261d2661ea2ec1c211ba850eec61d0a8c865a5f610f0fa1b0531cb18f6d2015893aeb819c74b35bd6140ac05345faefa05f0ad9a6148d1e1205280ef76c4d4401e7b5022cee9ba0795ae3b06710375b10b9213ced28ad8727cdef0416e0313bb3404767d0ad90c5625483304227cfe82be53768b63ddbd3cd2e3f68cefdce99a4029302110de3e43dae9270890f3fa2b5681ef362b6746ba08872c4d1cd57116fda78952aeb9f34a0215ac06ef87df9e31f6ba4e444b7c86dc64c3425492a443f7fe72a9e0cb136d4de7a80f146dfcf1003aacc223ecd9dc76eed8cfc315480a51c37782b822a68f291c8e0c93ef9633ac769e5c8a8e14563feed027e973577c692e92c869fccd97f6e12d09125604118777d56190dcb9f1c82842841a46d21cec99cae0d1261c7c740ac3087eab9711d1f47f0f0fa911394a39a53ba7d452ba9f80d9da55f040842dbaaf0fdc7e7ff11f7311aa232aa295a205ff1a44fad45789c009306638af729169ab049d33f8333cf2af999600ffb3a7c7dfcd4c6b2b018db4bc0cfe4cbb8709dfde09ee9df36392da1fd8135728ab69dbeb6048a2947f39daea7f0f76f5e8ecf83d0bcf8431f8a77f4969d72a092f1dadf139d2287601dbac188e4513bd5041956203f07ad0ca086c385c87e9473a02cb2e248fd10dd490bedafa1766729165112f011c9e133e5fd0ae0eceba227eeb3276bc2161b1b15ecfd33c4738106e1475970a45e4078964b1ec9b312f2f28bf08a82bdb7640f5c9d137245723b53d7107320ea3a6208630178bc9bff74785a96a80c8c7984e6581a5cb5f07857d5c9af5790b34152bfe880139b155c1096663e29df76756409ad7b51400f04aa431fbb8d60747e044d7e475ea7cf475e1398eb4ad38649dfcd7a03f9574e55090d40eab480ae4b3b263e373f0fa3cce70260012bc5282be252806009e43d186cd1c13ebbb0a45e714736bd8ad6c236d6f2ac371d4dd0f007b38f5b58fae228b253999d5810cee543bb0137255872599852d5e7f75d6a830427ea2a83c5d629c52c81516150af09bb64bb634eef7943b80201e464d29eec3b91bdb02dc1f3bb9982bb18f840450614e1fd5501be9a6c0432ad51d5d7677a53c5e7f77ab6e8d9fe497f2be3d041f99249b8d2de0828209af6e0d3259f26b4de0aca20d1d0b922375c81fd2f30445eeef8a74c0da5c2931a9d7cfcdabfda78186008d4dcf164810ea9d58dc140686aa143e5245e0f181a052c5331413c884a2d6b9f21c290111ab2a9a5236d80d5e94aaabb67c4ebec277d6f23a845934b383a371e1ce6a3715f3283a9aa5f60ce15bffabf77ec3306763c27f0e349339a326c2d159f44a191f68ae70f570fe0d9af4877f74a35384852d12ca402344caf42cd1f62cf225d6ffec0b994bc2ad07ac5ba22af141b6f79a63efae2f545094188209920f5bc84555452b5b04979105bb5cd14a34bdc1334887ec36f5c0205acc37e97c616c9bfb5a21166b2cad03095c614b9fb32f3a9f758f4ae74f9395d0c1769a425216e98fbaf2b2606d21f0048d30544ad02a98b66a602ddded2178a2e9e2e8d1943ca8a306f3123c9b37ae01efef847721afc1e78cd6961a460d3be3424e83fe069a20423dd2731152da810a1e843967c15c2c033235a138b8147c9edec26de39229d34bedb0b7f2f91b9f0fc8d73ee401777561d7f7428d100891e59bbdc54184065bad7fccf78e6525e70be066bb348047f11416b5b37834c71c7fc66a6fa4840077edbf3b634d64399506fc4763dcce617b32568b8ddb7889dc27f15e038e87569d219a0a7d2d49521f0e727f0e4f484cf8254ebf0756718408a85cdbce7a83399ce9579c2b9b1592030b2d87f60658da99c2a6da935031657995b4c2714befd4e51d1833f4b386fcc7063375f6e1b5c53d8e5f62ff0d2ecdf8dcf87008e834ef74f5a20570b3eee7e50ed12c438c7303fa31dce6b5c8fe0cf7b69ba539566a6ac1ef0a5469b578926a024ecef6490696fbb5ab6bd2397a4713a1bd236a82075e72bb566f438400956f0f0f75260984656cbcb69c492e8b9f8882c7bd562f88713e6a818232d940b8a904136578496cc5b0c0ec33479f2d33c3f0c9493c8452475a4d631b827408c38b06206830f65bee5d730fee2183f4ab7020b693a45561ba95bf997854371868d00fda6fed00e540bcadc850e5cd38e1e90d6795f47ace23702c951fd8f65b39a70a9f818c7340c305d853da033ff04608c164b364b1992f7f0de0d1c414deb9b30f264e7c2a636d2160ec37f42bb59e963c185b9a22b429e3f291873234768f830ff8044ff296619da1dda9033b36487a4613856bc7af4c9a3f6f4ac949ed95fe0fa7bbe29d034f303e90749cc062610fad822c81d5410eba78ece6b3ac06a51e0571739244d640daa825ec98e7ae73c590702c526be4a2d84948555a6d53bda7061859e9a84bbec412c564992890b6c9b54d39073a45295c74b52a24c6b7c920004582975eeddb0f13ed07cd946664c48a22b6a99827b992bae9bc03fa49c0ad087b85717de0989aed447c968afa5766a47c65630773234fd697d8d157dedf18033dbc40e6cc1b29ee4a197748f636e61e94c8cdeb2e4b8a89e648e48e663d0a0bf7a7a1f16ebc381d1f6af366ded7b6e2cbe556f6a06dc5322150b9244f1bd904b9d156ea30f8948635cea2dbe79e8c1c158b8008138e0cece0fd6e4de8404309c560665ad5507543dadf5313a95896213dfdc9427dc37a409083bc22f395170767772f93b00ae197f8f1fb4d214fb453e3af902be8cf8757d3916c80371bc7011b528b972ac9293ac4a0edc04e61991e9be04f3ec8d58711668432a48d5d2c0d247be0f003f0efd3d84a6cb72160610beafd462fc361f53e9071f961bf79e909ebc5502e8729d88f99f4388527679c9403d941ddc5791477e3ebc574ac009d03fe7259f269cfea70c1a63c81a3d8345a2684d6b515bbe3f0551ff67bc3270c0ff3636d0c6672e4e1aa0db541174212a5e286d770d8eebc08acb25ac26c705f01be37d2da326e4ff97ac159c98325ed56b22e1540d8d54286d19eb7c547434c0fd32f814b94ceb9539fde3bdb7c8aa45cb61fe50c5326f7661c09d1d50af5c901f03304fa9ad614f3302dca004c2d1e395f0e815c5b9f59a49aa50b6a0950260dfbb67018ec224198e0fcb06f82b4f1ebfabcefd74f2210e1d220b2850d9ecf09890f2f13bef584baadd0a1de1cd9cee5e75ea0108161148771293d33bb27050b91af6a1d483a8116bfabac50092207ce741e6936f6d6bf8fc8c70ce5346050068d848b1025603aa270106c45449f30fac8d2d8a49d401c39fa17ab6a2c5780015a9f2a03d58fe4d01cea101c88031363aad71cee601e483807793357b60796091fd28100ac7d2d96f5c99073d02005508c2b1cfd5ae53db44a9269a6515f7a051fb91756d6e90a2e92904ceeed84efb03b2a358090c72cc803ce82600aa8a8055eb1af8622492dfcb9b4d425e2d115ca07384f67fbb4c2f157426a45f21c1808042fd764137c5e0967f76056a576528ee6eccd89274bf4996df103d94523da0e4d6a281e7c427e666c3405a3be849a828f352ca40c3be5b5f06c8606611a0605077612a4bfeb3492c6f084895592f6aa1bff374507230b9a08f41e97c1e8ff08fe90a3fe397549e21e38825317b2d0ae15cb1b6e7e94f8b986da05b1380206019699eea0ffea357632ef17a645002e273330db567b6753da6be9c96bee4f877112fe97d0fe045e6f443e2dc1d1e7033f05ab68d53a2ab7d534632f54eeeb28779c2dbaad9a27c9662093faaa8d14daf6c58981e60dfd378ff54207303a1ceae5a7ba9e81513adf4af09a95a99380db31163de06a4aa70a20484bdb6f67eff785f17ebc5440a655d9dad44fe715c77bedb6a2598b572c665dc76c083c9d630eb2d20a0c71cc66d60c18f9235a75ea7418743a1e7ee8752b46ddbce06086911483ddf6cd0e5292ecf762e7e07892748eae386ed7ef37f63556cda4630d10ec2a14e78b8eb9e1942f5b64d5c0f28e604b5727123fee93f4ed3f2939f0556f6c775331c80cfc9705bdc29e185859fb5ca731ffa62f866b8d5c44aa854e2277e34ace1e0258a69bdde20a33edff6b62ba545a6e345a09a85ce09baec4245087949dba1fd2677a15e054c920db74298f5bbd7f865eaa4831cfd4c43d0b7e929a97c4c24782696b5467ffba4b4a491ee63b3585aa71e0b12f06ac7a1fc85edc0a51e75efb993b8eba9578183186538c8b72b2738f0dda591e6275458193a1043e21e55d0447fc907ee7f584655958228abf72e9f5ef267626be7be060d744daa3b742bc9abf470cb85e34bc040b65c8063e45ebecdf32b9aaa141dda93204a234d832370fdea072a4e122d77c8c2bc9bad4a02b36917c52cb82db2be7d3dc8fdb6bc049d85d8be4463ab827981cad4d40d053ed0df35d84f66504af8a483e81b2fa0e116085e74402a93570101b0eaf1a0da7f4e987507667fdd8b428a03eb3482970e5a7a2794664c88d47f25fe84059630f6ba26fd331440d9d711cdfa21a7a90fc0d1607c5d33e6a7f683ebdd3cb777cb7c5f1914ffe49799d0ff41e58256dfef5c9c959711556ea6134119a1372d4e1f94dcae3c8a245fb15ed2348820eed88a3ee98d14e11d281028fed3c549fad3b7bc934697eab687080d9ad0d9d12e92269ed13cca7105fb6a913e10e41b1461a94c37bb5f8af77dd6f78b26a28b9aea02a58f60ee62387612800b379f30762ad0efa9cd5ed8140a61f224d7f68684254c2d10706fc4e30eeaa8624908fb5e5df1817e389fd1f157a58c454166fcaec4039dc8b64c6059451be3aaee997bdc950826dcd56ebc565a7a7d639eadb5b551356bfca5d9e7ec65c032c1efdda58026d23cff8b2877c6dc16ecb69846774569dba7f3504113767982a463e53a977fbcda94e9c0fadc196080f989d9f3710b334fe1ea9a33ed7245e7f49fff0124dc17df4f2d6f58272a8f6b973bd765a6379d04ebc270ebb8fe70683a98503cc35dbe4a964298bc84d0b3f16ed4937e3e8f7b6e3eacc2e81483fc6b871362e78f973c0d128de6d7b4c2e3bcc7dc5b5152400b77d870baca6c0039171f255ffb0153979ff9905c21f48a11359a3d5116a70e0e9487d2e5fcd041a77eea3eadb5ea058c531d0a7f6d7040b07f4a0f9c4a0b26d0378187c44e120c39f08b3eb4624d61de87606c04bb96069f4c9aae947e3e83fad3b1b88f49c0e915148a8bedb51382e2ab09112188b77ef050b7d1b0ad8b69e59d42aeed336389e03fa2b217e369aa6cffcfb0dd6518d4a31581939ce21202ff3f8f5b226f036eec5ed1ba7b2e561c9cc5af8e6d473e14285e5b96c2a3061056ff26d354674e3a66ad0969c9338f502ab3f6a04cc99cd7195b4337c1378de9904ca8959e39d7273152fa787a75e27cc3b7c70128124f396a12a7023eadecc8c9d44b8d2ab3b26719370552aa2852687ec04958ece62fe28b8b124bccb3b370ff4eafa4a1796eca3a51e23dde6cc6b6d7eda96a897498481637e7309f9fc8707f0429ee2aacdc931d49d289f52617de26111f4378b2292021f6420385732c1119d183dfe1fbb5e584355e25e35b1ccb85faf8d3adf69b3b56e868d964e2850f492f9d07699b7c018f3426919c9f9012b73e5bcf4d09f4c168318690038e85ea6519fc1d310495291555d30fb096fc273a154fab11a58fd90a6f44b4fbb983f30444383ce653d2c0ceb7d1e5f8691199e4cc78b9ad77e9c28d021fa4966774137f01c2e170cf872fb1840ffe6f210a61118e6c46d489f9a4aa223afa410107212f22ccb06f7c2ec058657b1f511057363a407f79b0b7310e421d01c5330cb441d253f002a79c2e2a285e34e9dcd63bf915b5aafe952c7dc48d411da2dc98034d8b026268dc47d402259d27f4199fe64645b49df7dd43fa8982bbe1854362fd2e1da03cbda3a577238093892e6cc80d52a648a15dedb9a66cbc04b9ae0c4abf02e3c1c510b383b5e31d928ff597ee581743890094728158458f95da21e621d29bbf13ae013c9929352dcfb1ec7f9af9f9a7dae82eeba32a01e938a5dace564ffb82fe50cd57d9ece19fa812b878d1cd165e4cc4db564752f35c7cb5cd3e53e0c2d7ed381e2a729def6c99d2cb6b0c9ca91e27ba35ccb1efdd2b52d68c969001f40df0a18640bd460d41ac0c6381b27d95e150bbf8119ec72fcec177c648b578267b00afdc9cf3cb5c752b7b5b769a198b7ab0582537f4ba74cc62fd401ecf1005a855f28dc97114b306e4ec42108df8e05184e01f9a96a9c08c6f36fce91eb28100029ef689ecbf02b613ae643010eb4b4b8494bc0b47a1cfee9b7f460705774a04a37b54acb8022e29836ac9725ad01bea9c571064c610cbad5593f8946a2cac3fb8bc372df49d07485e8bdba0e5b1795ecd7c6cead26dbdb66e2ba47b5ffa1f04d23c51c6cbc3eca7aca6560e5892bc7b16f2ac26b7a576ae8f8c4bea2529dd0de9c0864a17cc35c378d30208ced4efae6d40784234f9d72e5bdf847d6c55a952c03af8abd3ea33694979b6f124f542ea40e315223b2bfd5b4e0a24e13ad427b8da1ce

hash(P1) = 19cf606e647c4f9f88c1eaaa360be27a39dc1cf59f65703c68a188793b3a67a6

hash(P2) = 146f708d0b1afcc27733d252847deb126e66e3f6108f5143a29c210b97a9af87

hash(P3) = 01076fb850a93e9b544dce62dc47a4997120664db4cf4052132b1ba0f58e71ec

message = hash(19cf606e647c4f9f88c1eaaa360be27a39dc1cf59f65703c68a188793b3a67a6146f708d0b1afcc27733d252847deb126e66e3f6108f5143a29c210b97a9af8701076fb850a93e9b544dce62dc47a4997120664db4cf4052132b1ba0f58e71ec) = 319b9b3bfaab268d80b43e3d12289cafa42e8674247c883810ad106494db5496

Public Key Matrix (PK)

Let's build our Public Key Matrix, which means, let's get the participants (members) of our two-dimensional ring as represented in figure 1.

The members of both rings can be retrieved by looking at the field ['vin']['key_offsets'] stored in a transaction. The members are previous transactions outputs (stealth addresses and commitments) that contains funds to be spent from.

It is important to notice that every output (stealth address and commitment) is stored by its index number (which is the index position where that output can be found in the pile of its respective amount). This index number is what the key_offset represents. Alongside with that, we can also retrieve the block_height and tx_id.

In the case of a RCTTypeSimple transaction, we also have one pseudo output for each input (or key_image). Which can be retrieved from a transaction at the 'pseudoOuts' field. Its meaning is better explained at the Amounts section.

Finally, from the key_offsets, let's retrieve what we need to build our Public Key Matrix which has the dimensions $n x 2$, where $n$ is the number of participants in one ring which is the length of the key_offsets vector.

For the first dimension, we need to extract the 'key' which represents the stealth address of a previous transaction.

For the second dimension, we need to extract the 'mask' which represents the commitment to a value of a previous transaction.

Therefore, our algorithm to build the Public Key Matrix, with $n$ being the length of the key_offsets, looks like:

RCTTypeFull (Type 1)

For $i = 0 .. n$:

$PK[i][0] = \text{get_outs(key_offsets[}i\text{])["key"]}$

$PK[i][1] = \text{get_outs(key_offsets[}i\text{])["mask"]}-(\sum \text{outPk}) - fee*H $

RCTTypeSimple (Type 2)

For $i = 0 .. n$:

$PK[i][0] = \text{get_outs(key_offsets[}i\text{])["key"]}$

$PK[i][1] = \text{get_outs(key_offsets[}i\text{])["mask"]}-\text{pseudoOuts} $

Notice that we are only verifying one input or key_image at a time. We need to repeat this procedure for as many inputs that a transaction has.

See an example below.

Let's look at transaction 2790982c9a44ae9516e395735c58f607d13741f8fd054bc096997aebc0a280d3

It is a RCTTypeSimple and we have two inputs, therefore, we need to build two PK matrices.

First matrix:

Stealth addresses ring members: 
[4722616327f02e9b1327f12af217107a7b8213183303c96841aaa6dc78941698, fc5dba9a66c68f455aff200f73e32a1fd567e87269de6fc994d89216b5ff3dea, 5836fc6029949042b2d7960db8303143c1621098fc3ffc94cb48fb1dda5c2f43, 351091e994f09b3c5b0c01e1829a74870aad824e142f85611073238029e3f085, ba4ea9f7a1a59d4651b58782807c2166a5354c229e97373540eeb33cb4301a42, 49162f6aa2ac7c0e3d0b2927505affe703f6ab9b739bdcefced70bc5c1aad704]

Masks ring members: 
[6c379d0e366daf16d96fd6c42f6e778d68958825f5867c545f97b7d559e769f3, fdf9fec30cbe45de5dec9a2a8544aad7231d67c9aeb948f0c2d3d92f46057e9f, 7c45284cbf00390ae5c98264a7996681291bda7b2df85c395c4c59e345abd65c, 14cd7e674a5befe1d6e055b9a96bddf4e05d8d91f065c7996b3df86de65dc05b, b086ec07d65db1dd94255d899585b877f37c56776d4d235f718eb58fe0e35eb9, 762f4d6c5f3a3d07b8f64b11d05f8eadefd8aec09b9e870e3d1dfc87b8a9a6af]

PseudoOuts: 
85de4f299dd2539b2dbfcfee053a2cbf5a9603f912db63b1e480bea628ea8f69

PK:
PK[0][0] = 4722616327f02e9b1327f12af217107a7b8213183303c96841aaa6dc78941698
PK[0][1] = 0c952bf4efc128048303e2f3fdaf8de5679140f8d35857cfd2d199a6c27f5fd9
PK[1][0] = fc5dba9a66c68f455aff200f73e32a1fd567e87269de6fc994d89216b5ff3dea
PK[1][1] = b6216ba799ae1e92b386bcd52850427b4b702b369234b88dbe983c48fd6203e2
PK[2][0] = 5836fc6029949042b2d7960db8303143c1621098fc3ffc94cb48fb1dda5c2f43
PK[2][1] = 72a1486731b7d11a38252ceabcb107292a71fb3c7d97b8e830f7da34cb54db62
PK[3][0] = 351091e994f09b3c5b0c01e1829a74870aad824e142f85611073238029e3f085
PK[3][1] = 740d5ea5b36b5764609539122067b3034a8c249bfb6e18a8c3af1b8f8f11a5b4
PK[4][0] = ba4ea9f7a1a59d4651b58782807c2166a5354c229e97373540eeb33cb4301a42
PK[4][1] = 6b19f56cb7eccfef450da65cdb98cb58e8a801cac54c66fa6099584650357b33
PK[5][0] = 49162f6aa2ac7c0e3d0b2927505affe703f6ab9b739bdcefced70bc5c1aad704
PK[5][1] = 0799c43b3d4874352b068f8e1b019b03a49bfb0465f47352773a4a556cfd1769

Second matrix:

Stealth addresses ring members: 
[91a60d549d2dba92cf5c9f4a997e0001d22cd464392b71201c6aacd5fbfbb1df, acd55ff140c8a017407bba04b665eb8e56c116eb59d61621a618e28df42fbcb4, 9278721a78e9acead7271fe711a51307cb5f54fa60bbe89ffd314e7440a43e49, ba75ab77e640f2144c76da796b802a0f6dc1f5061315ce15f8b5b8d2c31bd348, e94ef2edefdc4026d76a84b38e2fa00d741a721450bd9323ca9f2293778f799a, 71c7abb785e5e43b7cf47deb73d5da899b9c56b97ceabbdcab77c697640c40b2]

Masks ring members: 
[910db5457f88211ccc1e20c102563482edd18905535ddf05af6d8463c62b228c, 69b7aad52592bae52171770846086ec59f1aa0a5b9ec25ce62715fb7300bde4f, 09b6a063b8f014fe149de558f98d65fc71575342f900a3919660c967d04a8f37, f411724e485a3f13f9c26491bbb98c4a679283861068baf5966e205b1c1bd41b, 04819318286d2af6b97c48c6ddf6bc29d7d4aaa22894a436b0133812eb2e3f3a, 5c23d55c642a544faaa3a5917d07fc10cd95d15c95ffd06259c485d927a33a50]

PseudoOuts: 
fdc80cf66a9bf9015722fb12949b4b97f418ab016546e827fc52848f86a6802b

PK:
PK[0][0] = 91a60d549d2dba92cf5c9f4a997e0001d22cd464392b71201c6aacd5fbfbb1df
PK[0][1] = d31ee380b58b70516e45c10f85b574131f5d24b0e51f7f328abf24982b95f2cd
PK[1][0] = acd55ff140c8a017407bba04b665eb8e56c116eb59d61621a618e28df42fbcb4
PK[1][1] = d15e03051bc9269100236469d859f3713f588fd05e9c103f4b9968522669a9a2
PK[2][0] = 9278721a78e9acead7271fe711a51307cb5f54fa60bbe89ffd314e7440a43e49
PK[2][1] = 6c21d6d56f13186b022566b2df9c20cab81459b145afcfb7eaee7d8291180489
PK[3][0] = ba75ab77e640f2144c76da796b802a0f6dc1f5061315ce15f8b5b8d2c31bd348
PK[3][1] = b4661901a1ca90c3ac4430dc8bcd770ec56f1cb2605e30e1da6603db9ac93b85
PK[4][0] = e94ef2edefdc4026d76a84b38e2fa00d741a721450bd9323ca9f2293778f799a
PK[4][1] = 8dff76b0eda19abd441fad02caa4d6eb6c9c4eb077aa18547d789a5fd54a046a
PK[5][0] = 71c7abb785e5e43b7cf47deb73d5da899b9c56b97ceabbdcab77c697640c40b2
PK[5][1] = 9c78d40e583ddb2abcd1e0a7b171f43369ef87be494611b50b53b12ec6d198bc

Now let's look at transaction 618ae0d58ab6432e7438bf2dce33784bb540a0f3d9ebddf1f3ad7fb303380ca3. It is a RCTTypeFull and we have only one input.

Stealth addresses ring members: 
[e715dfec4f888c0081da33298b3713c087351067d83855629c64f027cd1515c3, e3b4b0c6662ecff7973ba1068783edeacef5bebc202c75b161ce83cec3189bc2, c04c6495f563e8d2e6097fc0f135aad740104a50c3efb85e777d80108b4dbe5d, c96ae9b6863a83bc4ee18081671b473073318872402296e2fc946a8f430e6639, 37e4af39b9264b59a4d2e9efebbffc56f97b5711bd9a50bae5060b5ba3a8efe5]

Masks ring members: 
[aa55a9ce873f5b73473b6ffb050467c4decdaca818e48514eed8453e6dc4fb05, 23974a446babff52dd66f999200e326d1311716188bfccb950a30a9ca7b454ca, 97d1883f7c427dfe78a3aafa33690775b8fff6dc326e301b6a4d5dcc18e556ed, 5a8c1e1ea221e903267a8f51d181c30009ad48936d2814d94c404933b9493252, 67af690b112a26b1e8503d426bae8a9afc56a0776bd2b9961bc07a75b9751690]

PseudoOuts: 
2998e7613c139311a6c5feb9a71adcb683539c5cacc16b848ab1a2a3e83d0e40

PK:
PK[0][0] = e715dfec4f888c0081da33298b3713c087351067d83855629c64f027cd1515c3
PK[0][1] = cddf0671d90f40f502246ca70ab0cc5634ca9eb6ee6fbfd2c2fb60862c59aed5
PK[1][0] = e3b4b0c6662ecff7973ba1068783edeacef5bebc202c75b161ce83cec3189bc2
PK[1][1] = e59efdb626eff76239e301de5328c29157102152057b487a760bfcd0653ff66f
PK[2][0] = c04c6495f563e8d2e6097fc0f135aad740104a50c3efb85e777d80108b4dbe5d
PK[2][1] = 48304371716c495d565a2291746e19a6ef40e99fab4fd50751ffa5e7784f9077
PK[3][0] = c96ae9b6863a83bc4ee18081671b473073318872402296e2fc946a8f430e6639
PK[3][1] = 00da6b103e96dd79f25fa5db77e499dd1bf2152c5be781c02c370e8937f06adb
PK[4][0] = 37e4af39b9264b59a4d2e9efebbffc56f97b5711bd9a50bae5060b5ba3a8efe5
PK[4][1] = c7fa9c8a65dd45e66c3bdde2ba2f1a87db9ed4b77f7398977d2e5cdcee435696

Where PseudoOuts = $(\sum \text{outPk}) - fee*H $

Key Image (KI)

Although we have a two-dimensional ring, we only need the image key of the ring-members corresponding to the stealth-addresses. There is no key_image associated to the Commitments.

The key image is just the secret key times the hash of the public key. It can be defined by the equation: $KI = x\mathcal{H_p}(P)$. Where $KI$ is the key image, $x$ is the secret key and $\mathcal{H_p}(P)$ is the hash mapped to a point of the public key generated by $x$.

Notice that this 'secret key' in the context of Monero is actually the secret key to unlock an output (stealth address) and it is a function that depends on the transaction public key, the secret view key, the secret spend key and the output index. Therefore, one person can create multiple key_images with the same secret view and spend keys, if this person have multiple outputs addressed to him/her, of course. The equation below shows what this secret key represents but please check how 'One-time addresses' are created, if you want to further investigate it. You will find a good resource here.

\[ k_t^O = \mathcal{H_s}(rK_t^v,t) + k_t^s \qquad \text{---> secret key} \\ \] \[ K_t^O = k_t^O G \qquad \text{---> public key}\\ \]

Let's suppose that we have the following private (secret) key: x = 09321db315661e54fe0d606faffc2437506d6594db804cddd5b5ce27970f2e09

From it, we can derive the public key by the definition of a point in an elliptic curve.

The public key (P) is defined by: P = xG

Using the Monero elliptic curve definitions (Edwards25519), we have P = cd48cd05ee40c3d42dfd9d39e812cbe7021141d1357eb4316f25ced372a9d695

In order to make it possible to perform another point multiplication, we need to treat P as a hash and map it to a point in the elliptic curve. The $\mathcal{H_p}$ is responsible for that. In our case, $\mathcal{H_p}(P)$ is the Point $\mathcal{H_p}(P)$ = c530057dc18b4a216cc15ab76e53720865058b76791ff8c9cef3303d73ae5628

Finally, we need to perform another point multiplication, which is $KI = x\mathcal{H_p}(P)$. The key image in our case is $KI$ = d9a248bf031a2157a5a63991c00848a5879e42b7388458b4716c836bb96d96c0

Generating a MLSAG signature

The idea behind the MLSAG signatures is also simple. We want to obfuscate the sender by proving that someone in the ring set signed the message and transferred the funds without being able to specify exactly who.

In order to generate a MLSAG signature, let's see what we need generate_MLSAG(m,PK,sk,index)

Let's consider two random values ($\alpha_0$ and $\alpha_1$) and a set of random values $ss_{i,j}$ for $ i = \{ 0,1,2,...,n-1 \; | \; i \neq \pi \} $ and $j = \{0,1\}$.

Let's define

\[ c_{\pi+1} = \mathcal{H}(m,[\alpha_0 G], [\alpha_0 \mathcal{H_p}(PK_{\pi,0})],[\alpha_1 G]) \]

and for $i = \{\pi+1,\pi+2,...,n-1,0,1,2,...\pi -1 \} $

\[ c_{i+1} = \mathcal{H}(m,[ss_{i,0} G + c_iPK_{i,0}], [ss_{i,0} \mathcal{H_p}(PK_{i,0})+c_iI_0],[ss_{i,1} G + c_i PK_{i,1}]) \]

Now let's define our signature as:

\[ ss_{\pi,0} = \alpha_0 - c_\pi k_{\pi,0} \qquad \text{---> where $k_{\pi,0}$ is the secret key corresponding to the key_image $K_o$} \\ \] \[ ss_{\pi,1} = \alpha_1 - c_\pi k_{\pi,1} \qquad \text{---> where $k_{\pi,1}$ is the secret key corresponding to commitment $Z$} \\ \]

The commitment $Z$ corresponds to the value commited in the transaction and it is only possible to create this transaction if you know the information from the ECDH info sent to you. Moreover, this proves that the amounts balances as the commitment zG is a commitment to 0.

As you noticed, the signature is basically composed by the random numbers that we generated except at the index where we control the funds of one of the members. But of course, for an external reader, he cannot tell where the secret index is.

\[ \sigma = \{c_0,ss_{0,0},ss_{0,1},ss_{1,0},ss_{1,1},...,ss_{n-1,0},ss_{n-1,1} \} \]

Let's suppose that we have the following parameters as inputs:

message = '06bc62dbfc5a9b2d408a6a68a8d3d949fd0732f8a08067faef29e58b41c73c78'

PK = [[Point('a9a0a41d7241649cf4f77f953287680f90a30c8878d49362b91cafd398be817c'),Point('ff4db1aee8d53b5d477ea200f20b4e4cb3615c3d8daf1cd45fe6e0d97bdd3316'),],[Point('0ada433a024c1cb115c70064b9e8e9379389c87759ab960754774689a0415721'),Point('3b7193bcb749c4bd0a5470309af38d88fee121a705a59232a6ff2f55ae5a1533'),],[Point('9855e371c4baa11f96f8afdbcf001e344a4f8ed20723a92b8bc13498d03f0750'),Point('aa12b65f356cd53f27ffcb0928846dcf43d095a346dda9e456289eac16f46e2e'),],[Point('59d87e0e3460085ce87e49322f3150dd1f8c085ee9a5b045d97179383dfe3937'),Point('3e3155e3bd7cb14a8c3dd820785ad1f32c810e078727a466bc236ffc5f7c2176'),],[Point('a91516d1fdb30eb9b37a61dba36e77caead71e276697b941d1fd84d0f25f7f6c'),Point('dfeb624430f3e81a4b7112043b8535a9b9c9193b0942f4103355500cb4f30880'),]]

sk = [Scalar('ce3c86ee5e0174ff4314975ab48be6298801a7bfdb230de767d1847f6663fa05'),Scalar('1d4db898a3ece8d3a4982ca58b6c4deac8a54a72193570906f97d93db4d90108'),]

index = 3

Let's also suppose that we execute the following code to produce the signatures $c_0$ and $ss$:

def generate_MLSAG(m,PK,sk,index):
    rows = len(PK)
    cols = len(PK[0])
    msg0 = ''
    msg0 += str(m)

    alpha0 = dumber25519.random_scalar()
    aG0 = alpha0 * dumber25519.G
    aHP = alpha0 * dumber25519.hash_to_point(str(PK[index][0]))
    msg0 += str(PK[index][0])
    msg0 += str(aG0)
    msg0 += str(aHP)

    alpha1 = dumber25519.random_scalar()
    aG1 = alpha1 * dumber25519.G
    msg0 += str(PK[index][1])
    msg0 += str(aG1)

    I0 = sk[0]*dumber25519.hash_to_point(str(PK[index][0]))

    # import ipdb;ipdb.set_trace()
    c_old = dumber25519.hash_to_scalar(msg0)
    i = (index + 1) % rows
    if i==0:
        cc = copy.copy(c_old)
    
    ss = misc_func.scalar_matrix(rows,cols,0) 

    while (i!=index):
        # print('i: ',i)
        msg = ''
        msg += str(m)

        ss[i][0] = dumber25519.random_scalar() 
        ss[i][1] = dumber25519.random_scalar() 

        L1 = ss[i][0]*dumber25519.G + c_old*PK[i][0]
        R = ss[i][0]*dumber25519.hash_to_point(str(PK[i][0]))+c_old*I0
        msg += str(PK[i][0])
        msg += str(L1)
        msg += str(R)

        L2 = ss[i][1]*dumber25519.G + c_old*PK[i][1]
        msg += str(PK[i][1])
        msg += str(L2)

        c_old = dumber25519.hash_to_scalar(msg)
        # print(c_old)
        i = (i+1)%rows
        if i==0:
            cc = copy.copy(c_old)

    # import ipdb;ipdb.set_trace()
    ss[index][0] = alpha0 - c_old*sk[0]
    ss[index][1] = alpha1 - c_old*sk[1] 

    return ss, cc, I0

In the end, we would obtain:

c_0: 
095a5f663f82ab5072cee9b775fa4d2e5d89f66c1afad6c379343e2ac085a10e

ss: 
[[6203f5aac614406c7052ec433f4423d1367ff566ea6758f7d9e6df051442d105, 4dfdaee16dec3737b9c12a309b22265b755fc19f51f57984f3e3eef63a43ca04], [2c365272a76037e8c421877007cb19c43844d204d24918058decc62611a2f80d, fe677a4d98362960afc2e47e8a8463eb0c138def4789f2a4dcd35d65d8de1d0f], [977ba37b55a29f57157bee9053999e55f8a8610c38b140681dcea8d9fed34301, 4684efb5025f1c2c7e6263405355a7485ab0cd42b289c23db8a788ea4610850f], [7e70783dc9a008e106d1de8caa6b9075e357ba1cb757c3532149fb2a6a9d3e07, 0ca6a8814cd40184a78334a62334f2dc6ea38883d2b0ee86df9c6d5a1d768609], [3ef1fbc550cfb36a7bd42921154c1e609981b70d0f61b4b69f7f57dd6dddec05, ff25e54dbca09f28598644fa018efd05187b363ac51821a56329f62a6eafc107]]

I: 
a54aee2c132cc5611eeb8bb5f6f55965a5b94eafc9c05c500be3d187d1cd56a7

Checking a MLSAG signature

To check if the signature is valid let's first understand the math behind it. The verification function looks like check_MLSAG(m,PK, I, $c_0$, ss), where $m$ is the message being signed, $PK$ is the matrix of public keys containing the ring members of both rings, $I$ is the key image of the first ring (the one hiding the sender), $c_0$ and $ss$ are the signatures.

  1. Check if $lI_j = 0 \qquad$ (make sure that the key_image is in the correct subgroup).
  2. For $i = 1..n$, where i is the quantity of members in the ring, do: \[ L1^`= ss_{i,0}G + c_{i-1}PK_{i,0} \\ \] \[ R^` = ss_{i,0}\mathcal{H_p}(PK_{i,0}) + c_{i-1}I\\ \] \[ L2^` = ss_{i,1}G + c_{i-1}PK_{i,1} \\ \] \[ c_i = \mathcal{H_s}(PK_{i,0}+L1^`+R^`+PK_{i,1}+L2^`) \]
  3. If $c_n = c_0$ then the signature is valid.

Let's try to check our generated signature from the example above

We have as inputs:

message: 
06bc62dbfc5a9b2d408a6a68a8d3d949fd0732f8a08067faef29e58b41c73c78

PK: 
[[a9a0a41d7241649cf4f77f953287680f90a30c8878d49362b91cafd398be817c, ff4db1aee8d53b5d477ea200f20b4e4cb3615c3d8daf1cd45fe6e0d97bdd3316], [0ada433a024c1cb115c70064b9e8e9379389c87759ab960754774689a0415721, 3b7193bcb749c4bd0a5470309af38d88fee121a705a59232a6ff2f55ae5a1533], [9855e371c4baa11f96f8afdbcf001e344a4f8ed20723a92b8bc13498d03f0750, aa12b65f356cd53f27ffcb0928846dcf43d095a346dda9e456289eac16f46e2e], [59d87e0e3460085ce87e49322f3150dd1f8c085ee9a5b045d97179383dfe3937, 3e3155e3bd7cb14a8c3dd820785ad1f32c810e078727a466bc236ffc5f7c2176], [a91516d1fdb30eb9b37a61dba36e77caead71e276697b941d1fd84d0f25f7f6c, dfeb624430f3e81a4b7112043b8535a9b9c9193b0942f4103355500cb4f30880]]

I: 
a54aee2c132cc5611eeb8bb5f6f55965a5b94eafc9c05c500be3d187d1cd56a7

c_0: 
d91fd7517d1a9597978c4ff6413428543ee7b16de5c283daaaa1ee274250d405

ss: 
[[2d38d5637132bec1e58595bf243bb20c27136e2a5eee597bf2ce085e7c77a003, b751fee954fa9afd96275c4aa9ff42898c162e44fb11f275c9e0e4e0717ddb07], [3ccd9e5dee926705c023beba4607444127856e51d08a6cced8446bd75bf97c05, c2af0bca31d553c8d1a5952c9f747919b9f7e422c1c954b0802ad1fdbf2f3905], [0e0b446caa96eb1ffca84526f7b1db597b834178f493a5741fb1bcc90f096c09, 5850e845ad218c6cf37c7cca7660c5cc95f89bd2928315c2cef0372c02923609], [6821026ffd0375e7f2443406d40ada11f1307ccc649d2434412bc1bfd9a1b507, c1e73986470f1fb983def91d3f8f5248e1624ce4919e327967bc3a7ace326700], [45625e3bafe84726ae1a7a80c6da3c144017cbb4771c536d1c61673c959ea608, fc350db44d7a1b3243ecfbf97c58ecdcc51107338c5ee8508ebba4486aad8e03]]

If we execute the following code:

def check_MLSAG(m,PK, I, c, ss,details=0):
    rows = len(PK)
    cols = len(PK[0])
    c_old = copy.copy(c)

    str_out = '\n'
    str_out += '--------------------------------------------------------'
    str_out += '\n'
    str_out += 'Arguments of check_ring_signature: '
    str_out += 'Prefix: ' + str(m)
    str_out += '\n'
    str_out += 'Key_image: ' + str(I)
    str_out += '\n'
    str_out += 'Public keys: ' + str(PK)
    str_out += '\n'
    str_out += 'Signature ss: ' + str(ss)
    str_out += '\n'
    str_out += 'Signature c: ' + str(c)
    str_out += '\n'

    i = 0
    msg = ''
    msg += str(m)
    # import ipdb;ipdb.set_trace()
    while i < rows:
        toHash = ''
        toHash += str(m)

        str_out += 'Calculating L1 = ss[i][0] * G + ci * P[i][0]   for index = ' + str(i)
        str_out += '\n'
        str_out += 'Calculating R = ss[i][0] * H(P[i][0]) + ci * I   for index = ' + str(i)
        str_out += '\n'

        L1 = ss[i][0]*dumber25519.G + c_old*PK[i][0]
        R = ss[i][0]*dumber25519.hash_to_point(str(PK[i][0]))+c_old*I

        str_out += 'L1 calculated for index = ' + str(i)
        str_out += '\n'
        str_out += str(L1)
        str_out += '\n'
        str_out += 'R calculated for index = ' + str(i)
        str_out += '\n'
        str_out += str(R)
        str_out += '\n'

        toHash += str(PK[i][0])
        toHash += str(L1)
        toHash += str(R)

        L2 = ss[i][1]*dumber25519.G + c_old*PK[i][1]
        toHash += str(PK[i][1])
        toHash += str(L2)

        str_out += 'Calculating L2 = ss[i][1] * G + ci * P[i][1]   for index = ' + str(i)
        str_out += '\n'
        str_out += 'L2 calculated for index = ' + str(i)
        str_out += '\n'
        str_out += str(L2)
        str_out += '\n'

        c_old = dumber25519.hash_to_scalar(toHash)
        i = i + 1
        str_out += 'Calculating c_old: ' + str(c_old)
        str_out += '\n'

    
    str_out += 'Calculating c_old - c :' 
    str_out += '\n'
    res = (c_old-c) == Scalar(0)
    str_out += str(c_old-c)
    str_out += '\n'
    if res:
        str_out += 'Transaction is valid. The signature matches the data.'
    else:
        str_out += 'Transaction is invalid. The signature does not match the data.'

    str_out += '\n'
    str_out += '--------------------------------------------------------'
    str_out += '\n'
    return res, str_out

We would get:


--------------------------------------------------------
Arguments of check_ring_signature: Prefix: 06bc62dbfc5a9b2d408a6a68a8d3d949fd0732f8a08067faef29e58b41c73c78
Key_image: a54aee2c132cc5611eeb8bb5f6f55965a5b94eafc9c05c500be3d187d1cd56a7
Public keys: [[a9a0a41d7241649cf4f77f953287680f90a30c8878d49362b91cafd398be817c, ff4db1aee8d53b5d477ea200f20b4e4cb3615c3d8daf1cd45fe6e0d97bdd3316], [0ada433a024c1cb115c70064b9e8e9379389c87759ab960754774689a0415721, 3b7193bcb749c4bd0a5470309af38d88fee121a705a59232a6ff2f55ae5a1533], [9855e371c4baa11f96f8afdbcf001e344a4f8ed20723a92b8bc13498d03f0750, aa12b65f356cd53f27ffcb0928846dcf43d095a346dda9e456289eac16f46e2e], [59d87e0e3460085ce87e49322f3150dd1f8c085ee9a5b045d97179383dfe3937, 3e3155e3bd7cb14a8c3dd820785ad1f32c810e078727a466bc236ffc5f7c2176], [a91516d1fdb30eb9b37a61dba36e77caead71e276697b941d1fd84d0f25f7f6c, dfeb624430f3e81a4b7112043b8535a9b9c9193b0942f4103355500cb4f30880]]
Signature ss: [[9242ca08be9b89bb5a326059bb27815ba4e096f1a8f2a52f86ab7929d9e45c0a, 4bd4e908762366ffd1a03dc7079d7b3a1ffcdc9078e6e3b3cf7f180a75baca06], [6d8c9062d1516481009489272cc544afe71624219d26785c9457f998caf52c07, aed9038c60a4eb1b3a606462107f0c2e049218ed856f72c59acba4bdd851f304], [c7fc29859250f07aea8a0c8fb4617e773b7336225e0363f0ca61fad9658c1e04, 83b5a71a70a13a05f78ca6229d6da13f3ea9f9bf5109b7052ed26f28615e5f0f], [e08e7a161847f964fed3e7261da24eed4826bd7a66bb92995318fc119b7dde08, 25f75cf7ec8d9b1d19bb08eda32525a358211ac375d3268dc801cd493d44dc0f], [59ec219e9a7af904b0ce2fde6d77b074ad3d427057ee30ae5459610b3890af0b, 2c7efebab20d7a1fafeb534974c9b8ef40acd18bca3f07eab0a1e6fb0d432505]]
Signature c: 1f5d8125aa39484dc7ba932f541f3435b88cd7d3330a62486dc2b0f5785c0d0e
Calculating L1 = ss[i][0] * G + ci * P[i][0]   for index = 0
Calculating R = ss[i][0] * H(P[i][0]) + ci * I   for index = 0
L1 calculated for index = 0
0e30f8c8ae8ce1d5679391bea6b93ae9d5f5af951c30d5ba2a96371310ca153c
R calculated for index = 0
4901a141b46530251147a4023ae7dfdee150b1de3b27364f38adaf24e2addccc
Calculating L2 = ss[i][1] * G + ci * P[i][1]   for index = 0
L2 calculated for index = 0
e1e63f51d1581366e48a14b36bc9249a49a4cbc8f2be2bd02ea5ed8e582d7212
Calculating c_old: 4f3f5a513e1fad1b91c05170166468c3e5713053a8e99fa0d035d6ad896b2e04
Calculating L1 = ss[i][0] * G + ci * P[i][0]   for index = 1
Calculating R = ss[i][0] * H(P[i][0]) + ci * I   for index = 1
L1 calculated for index = 1
9e96536a163df0b1a571616f2d1bfe4da2e2477c320477fc46f3155c41865031
R calculated for index = 1
2d0f7013e6bb584a0f8c894b32d64504de90b4e766f5b659969e6514d64a2379
Calculating L2 = ss[i][1] * G + ci * P[i][1]   for index = 1
L2 calculated for index = 1
1683d1df51a3343d6a631c366dbe79c03471799264b016459088c99d6276df65
Calculating c_old: 40bc611e9f93cbe4cc495cffa7ec1d1261ef45e5650cfb2c1072518232023408
Calculating L1 = ss[i][0] * G + ci * P[i][0]   for index = 2
Calculating R = ss[i][0] * H(P[i][0]) + ci * I   for index = 2
L1 calculated for index = 2
1e50be25ec7c2396b3ea0bdb338cbc0fe6194bea351be159651d2febadf1cc0d
R calculated for index = 2
7f1b8d59f34849b4a42a1f729556b4e5af8538cc3b65bfd317e78a9c93a78031
Calculating L2 = ss[i][1] * G + ci * P[i][1]   for index = 2
L2 calculated for index = 2
2338878931c204ac396cf2749546aae9107082f00ed52f721ae42a6576b5562d
Calculating c_old: cb486b66f020e68a45b95a1cbab37a42bf3df9de502ac6398db53af89021ca02
Calculating L1 = ss[i][0] * G + ci * P[i][0]   for index = 3
Calculating R = ss[i][0] * H(P[i][0]) + ci * I   for index = 3
L1 calculated for index = 3
db1dafa40e7bace38059431fb82e7cf94562cc28b186709fdfa7978274011fa7
R calculated for index = 3
87782e0fe91131529d016b167de160b389ac0ea65a6e7beeedd33145fdf60b29
Calculating L2 = ss[i][1] * G + ci * P[i][1]   for index = 3
L2 calculated for index = 3
c0d0e1340b774cebadf8c12672030783268b2bb656541c75e6ce3b838770e566
Calculating c_old: 6ac15205e388ab0a22c36bec5a472f05861a489afc901f083639a61ed9a14301
Calculating L1 = ss[i][0] * G + ci * P[i][0]   for index = 4
Calculating R = ss[i][0] * H(P[i][0]) + ci * I   for index = 4
L1 calculated for index = 4
ba7663eed964fc76953a72b381b053d718e796c8daaf993f7fbcaeeb30b99d50
R calculated for index = 4
406c97b76741617d57b1c986d6374593e6d1f3898310c8b463e9af9b3015b412
Calculating L2 = ss[i][1] * G + ci * P[i][1]   for index = 4
L2 calculated for index = 4
c5a23a6c711800a9f50067868f41334f15280344f3816aeaeab16eb8a3e56ab3
Calculating c_old: 1f5d8125aa39484dc7ba932f541f3435b88cd7d3330a62486dc2b0f5785c0d0e
Calculating c_old - c :
0000000000000000000000000000000000000000000000000000000000000000
Transaction is valid. The signature matches the data.
--------------------------------------------------------