cosmos / cosmos-sdk

:chains: A Framework for Building High Value Public Blockchains :sparkles:
https://cosmos.network/
Apache License 2.0
6.23k stars 3.6k forks source link

Improved multisig handling #5661

Closed zmanian closed 11 months ago

zmanian commented 4 years ago

At the moment, multisig is a bit of a usability nightmare. The user flow is

  1. Generate an unsigned tx.
  2. Share an unsigned tx with the signer set
  3. Each signer signs.
  4. Share the individual signatures
  5. Combine the signatures
  6. Broadcast signed tx.

Transport for the unsigned tx and individual sigs are external and require users to bring their own transport.

Here is a proposed alternative.

Incorporate golang magic wormhole into gaia.

  1. Start up the process with a proposed tx and a set of signers.
  2. Print a set of outgoing wormholes one for each signer.
  3. On each signer, start a process that accepts an incoming wormhole and signs it.
  4. Print an outgoing wormhole on each signer.
  5. Enter the incoming wormholes on each signer in the original process.
  6. Combine the incoming signatures and broadcast.

For Admin Use

alexanderbez commented 4 years ago

Neat! Is the e2e encryption just an add-on bonus? We don't technically need the transfer to be encrypted, but it's nice.

Also, step (6), who does the actual broadcasting? It needs to be a single entity. Is that agreed upon during step (1)?

alexanderbez commented 4 years ago

Being that this logic (command) is actually in the SDK, I'll move the issue there.

noandrea commented 4 years ago

Hello, we have been looking into this issue before and we consider the same technology as @zmanian proposed. After some thinking we came out with a solution (here the original WIP specs) that I am going to paste here in the current version:

The proposal is to use a modified magic-wormhole implementation as an input to the SPAKE2 protocol and as a network transport layer to provide a more convenient transport layer for exchanging unsigned transactions and their signatures.

Execution

The multisigning session initiator shares a weak session key to all signing peers. The weak session key looks like this: revolver (OpenIssue #1).

A Cosmos address and a generated channel id will be transparently added to this session key before deriving the stronger session key for communication with the signing peer via SPAKE2.

This allows the multisigning session initiator to keep track of only 1 weak session key, but still have different weak session keys per signing user.

It also helps distinguish between multisigning sessions in the unlikely event that two signing parties initiate multisigning sessions independently. One caveat is that all signing parties must be online and use the same SPAKE2 method of signing the multisignature transaction.

Interaction

Given the accounts C1^k^~p~, C2^k^~p~, C3^k^~p~ and the multisig account CM^k^~p~, and assuming that CM^k^~p~ initiates the process for multisig we have the following interaction:

CM^k^~p~ requests the multisignature generation using the command line client:

$ gaiacli tx multisign tx.json CMk C1p C2p C3p 

multisig session id: bookseller
channel 312  open with C1p
channel 1241 open with C2p
channel 634  open with C3p
...

the commmand will create 3 channels (as much as public keys are required for the multisig), and will print the session ID for this interaction.

the actual communication string will be composed of:

CHANNEL_ID-SESSION_ID-CXp

where

The SESSION_ID must be communicated by the CM to the CX and it is the actual weak password.

The signers (CX) will on the other side have to run the command line client for signing a transaction providing the SESSION_ID:

$ gaiacli tx sign sendtx2.json --from $ALICE --multisig=$MULTI --chain-id=$CHAINID --session-id=bookseller

do you want to inspect the tx? [no]
signature executed successfully 
do you want to save the signed tx? [no]

$

at this point the intiator of the multisig process will receive confirmations from the signer and will be able to verify the signatures:

$ gaiacli tx multisign tx.json CMk C1p C2p C3p 

multisig session id: bookseller
channel 312  open with C1p
channel 1241 open with C2p
channel 634  open with C3p
...
got valid signature from C1p
got valid signature from C3p
got valid signature from C2p

Required infrastructure

The partecipant to the multisig need to connect to lighthouse/relay to communicate to each other. The lighthouse has to be hosted as a centralized service and provides websocket connectivity to the parties (Open Issue #2).

OPEN ISSUES

  1. How to choose the channel id and what is the format of the pre-shared password? the channel id is equivalent to a tcp port, it must be unique for each key exchange but for ease-of-use it needs to be deterministic so both parties can derive it. it cant be based only on the receiver public key since will preclude the possibility to have multiple sessions running. maybe CM~p~ + CX~p~ ?

  2. where to host the lightouse service? explore the possibility to use lambda functions for this service. the urls could be something like: lighthouse.multisig.cosomos.network

Resources

what do you think about it?

alexanderbez commented 4 years ago

@zmanian do you have thoughts on the rough protocol stated above? I'm also a bit uneasy about introducing such complexities directly into the SDK. What are your thoughts on wallet providers enabling such a protocol instead?

zmanian commented 4 years ago

Maybe it doesn't belong directly in the SDK but in a stand alone binary that uses the SDK as dependency.

I think a more usable multisig is an imperative.

noandrea commented 4 years ago

I understand that to include a somewhat complex component in the SDK is always a delicate choice and delegate the functionality seems a more safe approach. On the other end in my experience there is a trade off to be considered by delegating certain functionalities to the "clients", since, for example, they might choose different approaches that will create confusion and incompatibilities.

We can start with a standalone implementation, also to evaluate whenever the proposed solution works well and improve significantly the multisig experience, but, if it does, it would have to be included in the SDK eventually, since to add another tool it will go against the very goal to make it easier (from my point of view).

could this be a safer way to approach the issue @alexanderbez @zmanian ?

jackzampolin commented 4 years ago

@noandrea I would say a standalone implementation would be great. The relayer has some of this functionality and you should see some code examples in there.

https://github.com/cosmos/relayer

alessio commented 4 years ago

@zmanian dixit:

Maybe it doesn't belong directly in the SDK but in a stand alone binary that uses the SDK as dependency.

I think a more usable multisig is an imperative.

I fully agree. A better multisig UX is needed, whilst additional complexity should not be put on the SDK. A standalone tool seems the most sensible solution.

randomshinichi commented 4 years ago

@noandrea I would say a standalone implementation would be great. The relayer has some of this functionality and you should see some code examples in there.

https://github.com/cosmos/relayer

Hi, I'm working together with @noandrea on this. I couldn't find multisig functionality in the relayer. Did you mean the general structure of a off-chain CLI program that connects with a node?

okwme commented 4 years ago

I also hesitate a bit on the complexity of this solution as being maybe unnecessary. Having e2e encryption is a nice to have, but these messages will all be published publicly as soon as the interaction is complete, so i'm not sure it's actually needed.

The problem this solution seems to address the most is the actual transport of messages instead of the actual composition and coordination of multiple signatures within a single multisig transaction. This seems to be the part that is the currently the most difficult, manually opening a JSON file and adding a signature that was manually created previously is the painful part, isn't it?

Just a command which can properly assess a JSON object that may already contain signatures, and properly add new ones (or check that it would not be a redundant signature) would be clear improvements that don't even require networking work.

Getting all signers online at the same time is also a potential problem I see. I would imagine asynchronous emails with JSON attachments is actually preferable to getting everyone online at the same time. It also avoids the problem of running a server that needs to be used as the lighthouse in this scenario. However, if there was a way to use something like a dedicated signing server for asynchronous signature sharing I could see the benefit. Maybe if the CLI was able to reference the JSON object from a remote URL as well as locally it would already create a better user flow than email. Maybe the ability to post it to IPFS? Ensure it stays pinned?

I imagine an ideal userflow could be something like:

gaiacli tx send $(gaiacli keys show me -a) $(gaiacli keys show you -a) 1000uatoms --generate-only > multi.json
# multi.json generated with no signatures
gaiacli tx add-sig multi.json --from me
# multi.json modified to include signature from me
gaiacli tx add-sig multi.json --from me
# Error: Signature from <cosmosaddress> already included
# At this point the multi.json  file can be sent via email, published via IPFS or on a publicly available URL
gaiacli tx add-sig https://pastebin.com/raw/abcdefg123 --from him > multi.sig
# after confirming this is the correct transaction signing a remote JSON object could be saved locally
gaiacli tx broadcast multi.json

I don't do multisig transactions terribly often though, @zmanian @ebuchman what do you all think?

alessio commented 4 years ago

I like this idea @okwme, I'll prepare a POC ASAP

zmanian commented 4 years ago

I think e2e encryption is actually pretty important here. Concealing the identity of the participants in the multisig from a server which is easily compromised is pretty key to making this a safe solution.

You can't remove e2e from the requirements. The current best practice is to use Signal attachments for assembling signatures.

alessio commented 4 years ago

@zmanian dixit:

I think e2e encryption is actually pretty important here.

It surely is! It'd be out of scope of gaia binaries to handle it though. gaiacli should assist users to package/rebuild tx+sigs transmitted securely. gaiacli does most of it already, it's just that the UX is fairly suboptimal (for history's sake, most work was carried out in https://github.com/cosmos/cosmos-sdk/pull/3264)

noandrea commented 4 years ago

I agree with @zmanian that securing the channel for communication is very important, especially while transmitting transaction that will be eventually signed. @okwme about the synchronicity of the process I see it both as a pro and as a cons: ideally when requesting multiple party to perform a collective action that might be important in term of value to make sure that everybody is aware of what is happening I see it as a potential plus; If I am starting the multisig process, I would like to know that my peers are aware of it, I it will be evident in a synchronous scenario. On the other end it might be to strong to take for granted that every party is online and connected at the same time, but since we were talking about an external tool (without direct impact on the sdk / gaiacli) a further development can be to explore an async scenario.

okwme commented 4 years ago

Yea it seems like if there are extensive new dependencies a standalone solution would make more sense. I wonder if all of the more minimal implementations have been fully explored though? For example using just SSL to use a common AWS bucket to post / share message & signatures? Maybe even just SFTP credentials to a common endpoint? @ebuchman do you have opinions about any of this?

ebuchman commented 4 years ago

So we use multisig all the time and find it not overtly terrible, but it could defntly be improved if sharing the unsigned tx and sig were native to the tool. I would opt for a simple way to do this with minimal dependencies, eg. using an AWS S3 bucket where only the signers and can read and write. What comes to mind is something like the following:

This is of course just a high level sketch. Actual implementation would be a bit more involved:

As an iterative way to approach this, we could build a simple standalone tool that just pushes and pulls unsigned txs and signatures to an S3 bucket, and otherwise leaves gaiacli exactly as is.

I don't know much about what kind of setup wormhole requires, but I would suspect S3 buckets would adequately serve the need here and might be simpler to configure. Not sure if there's something extra wormhole otherwise provides.

alessio commented 4 years ago

@ebuchman dixit:

add an extra flag to complement --generate-only for a url that the unsigned tx could be pushed to (eg an amazon bucket)

A --upload=ENDPOINT flag should do it I guess.

add an extra flag to gaiacli tx sign for a url that an unsigned tx can be fetch from (rather than it being on the command line), and that the signed tx can be pushed back to add an extra flag to gaiacli tx multisign for a url that the unsigned tx and all signatures can be fetched from

Alternatively, just make the command able to automatically identify which protocol needs to be used to retrieve the file (I'd start supporting local file and https). Doable even w/o extra flags I think - will certainly give it a stab.

the unsigned file is currently expected as an argument, not a flag,

Yes, that was my design decision inspired by how UNIX programs usually work - if something is required, it should be passed as positional argument (flags are optional by design). Happy to revisit that though.

the url should be a high level url, not a full specific path. the tool should establish some convention for where to put unsigned txs and sigs and where to look for them based on the from address and the nonce

YES! Like that. We should expect a specific interface contract to be met, and throw away anything that does not comply. Doable.

eventually the url should be configurable in the config on a per multisig address basis so you don't have to provide it everytime and you can configure different buckets for different addresses

IMHO this config should be app-specific, e.g. for what concerns gaia, it should live in .gaiacli/config/.

noandrea commented 4 years ago

@ebuchman @alessio I have a doubt s3, if anybody can store their tx and signatures in a bucket, does it mean that the everyone on the internet has read and write permission on that bucket? The second aspect is that likely a S3 compliant service has to be hosted and replicated somewhere since AWS is not available everywhere, specifically in those regions that are under sanctions from the USA.

The advantage of the wormhole protocol is that the lighthouse service is a very lightweight service that has the only function to connect two peers.

ebuchman commented 4 years ago

does it mean that the everyone on the internet has read and write permission on that bucket?

Don't think so, I thought you could have private read and write.

The advantage of the wormhole protocol is that the lighthouse service is a very lightweight service that has the only function to connect two peers.

Might be worth looking into more. We'd certainly want a solution that doesn't just depend on AWS, it's just one option that seems straight forward/simple. Wormhole seems cool too, I just don't know enough about it. In any case, it should be easy for folks to experiment with tools that can be used in conjunction with the gaiacli signing tool to try out different options here.

alessio commented 4 years ago

does it mean that the everyone on the internet has read and write permission on that bucket?

Unsigned Txs are just JSON files, and I tend to think that reading an unsigned Tx JSON from a remote endpoint should be as easy as possible.

{Down,up}load of signatures to/from S3 private buckets could be done piping outputs to the aws command line tool; by doing so we would keep gaiacli features consistent with program's main scope of usage and avoid making it bloated with optional dependencies on third-party libs that'd be functional only to the implementation of features that many users may never use.

noandrea commented 4 years ago

my question is, if the S3 bucket used to exchange the tx/sig is provided by Cosmos, then it has to allow read/write publicly, doesn't it? The issue about having a publicly writable storage is the potential abuse of it, meaning it will be trivial for an attacker to disrupt the service by wiping the content of the bucket or more easily use the bucket to store and distribute arbitrary data (that will likely cause legal problems). If we are instead talking about relying to personal accounts, then, imho, using S3 or sftp complicates the process instead of simplify it: the user will have then to take care of setting up the storage, permissions etc; probably easier to just email or message the json files.

An alternative approach could be to use mozilla send as a hosted service configured only to accept small files. The upside will be that the exchanges could be likely highly automated with no cognitive overload for the user. The downside that is that it is still a fairly easy target for abuse.

From my point of view negotiating the transport layer on behalf of the user (on top of combining signatures and transactions) delivers a significant better user xp, but maybe I misunderstood the scope of the issue.

alessio commented 4 years ago

my question is, if the S3 bucket used to exchange the tx/sig is provided by Cosmos, then it has to allow read/write publicly, doesn't it?

Why are we assuming this? This is not gaia, the tool should be configurable and not designed with a single use case/chain/community in mind. I think the objective here is to design a more ergonomic solution that makes easy for a multisig's signers set to exchange signatures with the ultimate objective to combine them all and broadcast the signed transaction.

zmanian commented 4 years ago

I think we assume that signer groups would BYO S3 bucket.

noandrea commented 4 years ago

hello, looks like this one got stuck :)

summing up, there are 2 approaches proposed for this issue:

  1. wormhole protocol:
    • pro: secure, completely transparent to the user, very high level
    • cons: complex, requires a lighthouse service (hosted by cosmos)
  2. generic file transfer (ipfs, http(s), sftp, ...)
    • pro: well-known and supported protocols, do not require a hosted service (by cosmos)
    • cons: error prone, requires additional setup from the users

The 1. assumes no prior setup or knowledge from the user side, where the tool takes care of every aspect of transport, while the 2. assumes that the users have enough know-how to set-up, secure and give access to the service that will be used to transfer the tx files.

From a purely user perspective, I like the first approach since personally I like when I use a tool that let me get to achieve my goal without any obstacles in between (in this case, eg for aws, would be to set up the bucket, send or receive the access keys, test that it works, etc..), but I also agree that the solution 2. is easier to implement (@alessio already has a pr for retrieving files from an http url) and everybody that has used a computer is a bit used to suffer to make them do what you want (lol).

Since @alexanderbez asked for a resolve of this issue before going forward with coding let's vote with

alessio commented 4 years ago

I agree, and have voted 2.

Plus wrt 1., I'd add to the contros that if we were to go for a specific protocol, we'd restrict users freedom. Conversely, if we got 2. well implemented and simple, it would not necessarily be error prone or requiring any special or particularly complicated setup from the users.

github-actions[bot] commented 4 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

okwme commented 4 years ago

Reopening this to record some thoughts around what is really a multisig coordinator service.

Prior art include:

This seems like it could be a hackathon scale project possibly:

IPFS is not a requirement it just means that no one is responsible for keeping a server online. Similarly pastebin could actually be used.

What would actually be useful is if the app interfaced with keplr or lunie so that the transactions could be signed without having to rely on the command line. These capabilities should be available soon...

ebuchman commented 2 years ago

So I built a prototype tool for this here: https://github.com/informalsystems/multisig

It uses an s3 bucket and supports multiple binaries and keys.

Of course this is going to be much less relevant when authz starts going live everywhere (eg. starting next week even), but I'm sure it will still come in handy.

raynaudoe commented 11 months ago

Just checked how the process of building a multisig tx looks like and to be honest, if we take aside the fact of having to share the unsigned tx to the rest of the signers, the process doesn't feel as painful. How up in the priorities scale would be building an external app to automate all this process @tac0turtle?

tac0turtle commented 11 months ago

this will be worked on as part of the accounts module, should be out in the next couple weeks

tac0turtle commented 11 months ago

we will be incorporating a onchain multisig with the new accounts module. The current multisig will still exist but ideally people move on chain. Closing this issue as we are tracking this in the accounts project board. An issue will be opened when the work commences

https://github.com/orgs/cosmos/projects/26/views/42