-
Notifications
You must be signed in to change notification settings - Fork 34
EIP-0042 Multi-Signature Wallet #88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
3fc9ea1
4596740
ac31fee
d89ff56
b416ada
8f5caab
6304ebb
7667389
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
# EIP-41: Multi-Signature Wallet | ||
|
||
- Author: lazypinkpatrick, vorujack, mhs_sam | ||
- Status: Completed | ||
- Created: 21-Jan-2023 | ||
- License: MIT | ||
- Forking: not needed | ||
|
||
This EIP defines a standard for multi-signature wallets based on EIP-11, which defines the multi-signature signing process. | ||
|
||
## Introduction | ||
|
||
On top of EIP-11, this EIP implements the following additional operations: | ||
|
||
* Wallet creation and address derivation. | ||
* Transaction signing based on EIP-11. | ||
|
||
## Wallet Creation and Address Derivation | ||
|
||
These parameters define an `K-out-of-N` multi-signature wallet: | ||
|
||
* `N`: number of public keys. | ||
* `K`: minimum signature required. | ||
|
||
Each signer stores the set of extended public keys of all signers and a local copy of their secret. | ||
|
||
An address is derived for each signer using their extended public key over a specified derivation path. The list of derived addresses for all signers is then used to compile a multi-sig contract that looks like this: | ||
|
||
``` | ||
atLeast( | ||
K, | ||
Coll( | ||
PK(Address1), | ||
PK(Address2), | ||
. | ||
. | ||
. | ||
PK(AddressM) | ||
) | ||
) | ||
``` | ||
|
||
Please note, this contract results in different addresses when the order of signers differs. | ||
|
||
We must guarantee that all signers will have a unique and reproducible multi-signature wallet address. To do so, we use the same derivation path and sort the byte array of public keys in ascending order. | ||
|
||
Then we will manually create the ErgoTree; since using the compiler could cause the resulting address to vary. | ||
|
||
The address derivation algorithm is as follows: | ||
|
||
Sort the list of signers' public keys before being used in the contract. | ||
Create the required ergoTree array as follows: | ||
|
||
- The first byte is header. `00` | ||
- A byte with value `98` indicates **"atLeast"** | ||
- Encoded `K` as: | ||
- A byte with value `04` denotes the integer type, | ||
- followed by the value of `K` encoded as VLQ. | ||
- A constant byte with value `83` indicates **"ConcreteCollection"**, | ||
- The VLQ-encoded value of `N`, | ||
- Constant byte with value `08` indicates element types in collection as **"sigmaProp"** | ||
- For each public key 35 bytes as: | ||
- One byte with value `08` indicates **"sigmaProp"** | ||
- One byte with value `CD` indicates **"proveDlog"** | ||
- followed by 33 bytes representing the public key. | ||
|
||
Therefore, for each derivation path, all wallets are able to generate a unique and similar byte array as an ErgoTree. | ||
|
||
 | ||
|
||
## Transaction Signing | ||
|
||
An unsigned transaction is generated on one wallet. This transaction may have been generated through ErgoPay or the dApp connector, or it could be an outgoing transaction to another address through the wallet itself. | ||
|
||
After an unsigned transaction is generated, the wallet | ||
|
||
Generates a commitment. | ||
Stores its private part locally. | ||
Shares a commitment as below | ||
|
||
``` | ||
{ | ||
tx:<reduced transaction bytes encoded as base64>, | ||
boxes:[encoded boxes as base64], | ||
aslesarenko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
commitment: commitments encoded | ||
} | ||
``` | ||
|
||
The input `boxes:` are only used to display transactions on wallets. | ||
|
||
The general commitment format is as follows. However, this general-purpose commitment can be used for any contract. | ||
``` | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Differs from EIP-11, see below There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree, if EIP-11 is not suitable "as-is", then please explain why and if motivated, then EIP-11 should be updated. Then this EIP should follow EIP-11. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this format is used in the node, sigma-rust and other libs, see /wallet/generateCommitments. |
||
"hint": "cmtReal", | ||
"position": "0-0", | ||
"type": "dlog", | ||
"pubkey": { | ||
"op": "205", | ||
"h": "03a73c66970f14fc6450c6ab1a167cb4ba3baa64b20f731e22ec6840c70d27ef1c", | ||
} | ||
"a": "0253db866791af521ba4ab009509b6db89d272d9461636ee65eaa3e316884b21a4" | ||
} | ||
``` | ||
|
||
In a multi-signature wallet, a simplified version of commitment is used. | ||
|
||
``` | ||
commitments: [ | ||
[<base64 string of commitment for public key index 0 in input 0>, ...] | ||
[<base64 string of commitment for public key index 0 in input 1>, ...] | ||
aslesarenko marked this conversation as resolved.
Show resolved
Hide resolved
|
||
] | ||
``` | ||
|
||
In the multi-signature wallet, a commitment is represented as a list of base64 strings to reduce the commitment size. Also, an empty string is used wherever a commitment is unavailable. | ||
|
||
[//]: # (and we can transform these commitments to standard format:) | ||
This reduced format can be transformed into the standard format: | ||
|
||
``` | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This format differs from the actual format in EIP-11 (index/position, pubkey/pk, a/commitment). Is EIP-11 outdated? |
||
"hint": "cmtReal", | ||
"position": "0-<commitment_index>", | ||
"type": "dlog", | ||
"pubkey": { | ||
"op": "205", | ||
"h": "<public key used in address generation at index commitment_index>" | ||
}, | ||
"a": "<base16 encoded of commitment at index commitment_index>" | ||
} | ||
``` | ||
|
||
[//]: # (Because wallets want to pass this code via QRCode, each wallet can split it into chunks) | ||
|
||
To transfer these commitments using QRCode, we can split the data into several chunks: | ||
|
||
``` | ||
{ | ||
MSR: "{\"tx\": ...}", | ||
n: <number of chunks> | ||
p: <page number> | ||
} | ||
``` | ||
|
||
This data is transferred between multi-signature wallets until one wallet has at least `K` commitments (including its own). | ||
|
||
This wallet creates a partially signed transaction with this encoding: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why does the last wallet create a "partially signed transaction" and not a fully signed transaction if it already has N commitments? Or does this sentence apply until N-1 commitments are collected? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see https://github.com/ergoplatform/eips/blob/9906f403f61d8592ab955494cc4310563f4067c6/eip-0011.md#signing-procedure. |
||
|
||
``` | ||
{ | ||
partialTx: "<partially signed transaction encoded as base64>", | ||
commitments: <encoded commitments> | ||
signed: [<base64 encoded of signer public keys>], | ||
simulated: [<base64 encoded of simulated public keys>], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What means simulated? This is only the list of missing keys, which can be determined by the wallet address. Why do we need it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see https://storage.googleapis.com/ergo-cms-media/docs/ErgoScript.pdf, Appendix A2. Proving. |
||
} | ||
``` | ||
|
||
It can also be transferred using QRCode in several chunks: | ||
|
||
``` | ||
{ | ||
MTX: "{\"partialTx\": ... }", | ||
n: <number of chunks> | ||
p: <page number> | ||
} | ||
``` | ||
|
||
The first signer creates a list of simulated signers, and other signers use this list. | ||
|
||
Each signer: | ||
Signs the partially signed transaction (if they still need to). | ||
Adds their public key to the list. | ||
Shares the data with the next signer. | ||
|
||
The transaction will be broadcasted whenever the signer determines that the transaction has enough signatures. | ||
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a mistake. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if I got it, please elaborate. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. On your picture, the "Collection Element Data Type (SigmaProp)" shown under "Repeated for each public key". This is not correct. That 08 value is stored only once. And you already showing it as "Collection Data Type (SigmaProp)" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. according to the doc, it seems you're right since Expression Serializations requires opCode and body; however, in the tests I'm getting parsing error if I eliminate those types and surprisingly it's working when type is added. |
Uh oh!
There was an error while loading. Please reload this page.