I read Luigi's post about how multisig worked when he released the first test version, but I found it very complicated and hard to understand. Maybe it was the format, or maybe it was just more complex than I was expecting. In any case, I was wondering if there is already a more didactic and detailed explanation on how things work under the hood, or if not then maybe someone could try to explain it here?

Thanks in advance!

  • which post? this one?: github.com/moneromooo-monero/bitmonero/commit/…
    – JollyMort
    Commented Jul 24, 2017 at 17:38
  • @JollyMort: Yes, that is the one. I thought it was Luigi's for some reason.
    – user141
    Commented Jul 26, 2017 at 17:58
  • well, it is "Scheme by luigi1111:". ok, I'll take a shot at answering these days .. it's a long one :) N/N is pretty simple, M/N i gotta have a better look
    – JollyMort
    Commented Jul 26, 2017 at 20:03
  • @JollyMort: Sure :) // I have a feeling that I could go over it again and try to get it. I could probably do it tbh, but I was hoping for something more didatic not just for my own sake, but for others as well. // Thanks
    – user141
    Commented Jul 26, 2017 at 20:20
  • @JollyMort: Ok, I was just looking into that post again, and I was at least able to pin down some of the problems I have reading it: 1) A and B denote both users and public keys. 2) I_D = I1_D + I2_D is different from what I would expect from a regular key image built from the address (A, B+C), which should be just (b+c)*H(D). 3) The explanation uses terms that non-developers like myself are likely not familiar with, such as MLSAG_Gen, ss, ecdhInfo, cc etc.
    – user141
    Commented Jul 29, 2017 at 8:57

2 Answers 2


I can only explain it to the extent I understand myself.

As written in the original post, we have user 1 with address (B, A) and user 2 with address (C, A), where B and C are individual public spend keys and A is the shared public view key (further on, small-caps will mean secret keys).

So, the 2/2 multisig address is obtained by simply summing the public spend keys of individual participants. The address is then (B+C, A). That's the address, but who can spend from it once some funds are received?

To an outsider it's just like any other address (F, A), where F = B+C. If you give the address to someone and say "send to this", he has no idea that the public spend key is actually composed of 2 (or more) keys, he sees only the combined key F. In fact, there exists a wallet which is able to spend from the multisig address alone. It can be constructed by whomever obtains the required number of individual secret keys. For doing this, there's a tool being added in PR-2218 (currently only N/N). Even without any special way of constructing TX-es, this allows for an one-time use multisig wallet. User 1 gives the private spend key to user 2 who constructs the master wallet and does whatever he wants with it further on.

But we want to sign individual transactions and avoid handing over control of the entire wallet. That makes it a bit more complicated, as the TX construction has to be done in steps which don't reveal the secret key to the other pary.

So, both parties already have the shared secret view key a and the combined public spend key F. This lets them both have a watch-only wallet and scan for incoming transfer. Recall how one-time public keys destined for this wallet are constructed:

P = Hs(rA||i)G + F,

and the recipient (any 1 of them) can check:

P' = Hs(aR||i)G + F, P = P' ?.

That's the output. To spend, we must produce a key image (also to check spent status) and produce a signature with the corresponding one-time secret key, but without revealing the "owning" wallet secret spend key.

As described in Luigi's write-up, fist step is key image creation.

Normally, the key image would be:

I = xHp(P),

but to construct it you need to know x, and you can't know x without the "master wallet" secret spend key, because:

x = Hs(aR||i) + f.

However, we can build it in parts:

User 1 does: I_1' = bHp(P), and

user 2 does: I_2' = cHp(P).

Any user can compute the complete key image from the partial pieces:

I = Hs(aR||i)Hp(P) + I_1 + I_2,

which works because:

I = Hs(aR||i)*Hp(P) + I_1 + I_2 =

= Hs(aR||i)Hp(P) + bHp(P) + cHp(P)

= (Hs(aR||i) + b + c)Hp(P)

= (Hs(aR||i) + f)Hp(P)

= xHp(P)

Further on, we construct the ring signature, etc. but that part I don't understand well, so can't continue.

  • Thanks, that is indeed helpful. But what are the is that are concatenated inside of the hashes?
    – user141
    Commented Aug 2, 2017 at 0:41
  • Output index in the TX. It's something that's missing in the CN whitepaper too.
    – JollyMort
    Commented Aug 2, 2017 at 6:12
  • Could you add what Hs and what Hp is?
    – onefox
    Commented Sep 5, 2018 at 11:15
  • Hs - hash to scalar function (hashes the input with keccak and performs modulo l operation on it); Hp - hash to point function (not sure how exactly it works, but outputs a random EC point)
    – JollyMort
    Commented Sep 13, 2018 at 18:56

Apparently there is lack of information on internet about how monero multisignatures work, so we issued an article on Medium about it's inner workings. You may check it out - https://medium.com/@exantech/monero-multisignatures-explained-46b247b098a7

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.