Alexander Wade
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # Actor Spec: Multisig [Back to Master Tracking Doc](https://hackmd.io/LOZjAsz-THelSD5lWqSVlw) ## Contents [TOC] ## At a Glance The Multisig actor is a single actor representing a group of Signers. Signers may be external users, other Multisigs, or even the Multisig itself. ## State ```go= type State struct { Signers []address.Address NumApprovalsThreshold uint64 NextTxnID TxnID InitialBalance abi.TokenAmount StartEpoch abi.ChainEpoch UnlockDuration abi.ChainEpoch PendingTxns cid.Cid } ``` **`Signers`**: A slice of addresses representing the Multisig's Signers. * Notes: * ALL address protocols allowed. * Each Signer should be unique. No entries within Signers should resolve to other entries. * Each Signer should have equivalent permissions to `Multisig.Propose` and `Multisig.Approve` within the Multisig. * Can be modified via `Multisig.AddSigner`, `Multisig.RemoveSigner`, and `Multisig.SwapSigner` * Invariants: * `len(Signers) != 0` **`NumApprovalsThreshold`**: The number of Signers that must Approve a Transaction in order for it to be executed. * Notes: * Can be modified via `ChangeNumApprovalsThreshold` * Invariants: * `NumApprovalsThreshold <= len(Signers)` * `NumApprovalsThreshold != 0` **`NextTxnID`**: The TxnID that will be assigned to the next proposed Transaction. * Notes: * `NextTxnID` should be equal to the total number of proposed Transactions in the Multisig's history. * Invariants: * `NextTxnID >= 0` **`InitialBalance`**: Denotes a TokenAmount that is locked in the Multisig until `UnlockDuration` epochs elapse. * Notes: * `InitialBalance` is unused unless an `UnlockDuration` is specified on construction. * `InitialBalance` is unlocked gradually over the `UnlockDuration` * Invariants: * `InitialBalance >= 0` **`StartEpoch`**: Denotes the epoch in which `InitialBalance` was locked for `UnlockDuration`. * Notes: * `StartEpoch` is unused unless an `UnlockDuration` is specfied on construction. * If set, `StartEpoch` is the CurrEpoch in which the Multisig was created. * Invariants: * `StartEpoch > 0` * `StartEpoch <= rt.CurrEpoch()` **`UnlockDuration`**: Denotes the number of epochs after `StartEpoch` at which point the full amount `InitialBalance` becomes available for use by the Multisig. * Notes: * Before `UnlockDuration` elapses, the Multisig should ensure that the correct portion of `InitialBalance` is not spent by any executed proposals. * Invariants: * `UnlockDuration >= 0` **`PendingTxns`**: Before Transactions are executed by the Multisig, they're stored in `PendingTxns`. * Notes: * Cid type: HAMT, `map[TxnID]Transaction` * Fully approved and executed Transactions are deleted from `PendingTxns` * If `NumApprovalsThreshold` decreases, `PendingTxns` may contain fully-approved but un-executed Transactions. * These Transactions should be executable via `Multisig.Approve`. #### Transaction Resolved from `state.PendingTxns` ```go= type Transaction struct { To addr.Address Value abi.TokenAmount Method abi.MethodNum Params []byte Approved []addr.Address } ``` **`To`**: The destination of the Send. * Notes: * ALL address protocols allowed. * No restrictions currently exist for this value. * If `To == Receiver`, the Multisig will `Send` to itself. This is used to access functions like `Multisig.AddSigner`, `Multisig.RemoveSigner`, `Multisig.SwapSigner`, and `Multisig.ChangeNumApprovalsThreshold`. **`Value`**: The TokenAmount that will be provided via Send. * Notes: * Whether the Multisig has `Value` available to Send is checked when the Transaction is fully approved for execution. * Invariants: * `Value >= 0` **`Method`**: The MethodNum that will be invoked via Send. * Notes: * No restrictions currently exist for this value. **`Params`**: The parameters that will be provided to the invoked method via Send. * Notes: * No restrictions currently exist for this value. **`Approved`**: A slice of addresses that have registered approval for this Transaction. * Notes: * The address at index 0 is the Proposer of the Transaction. Only this address may Cancel the pending Transaction before its execution. * Invariants: * `len(Approved) >= 1` ## Exported Methods #### 1. Constructor ```go= func (a Actor) Constructor(rt vmr.Runtime, params *ConstructorParams) *adt.EmptyValue ``` Initializes the Multisig's `State` with a slice of Signers and a threshold approval count before proposals may be passed. Additionally, if `params.UnlockDuration` is nonzero, any `ValueReceived` vests over a period of time denoted by `params.StartEpoch` and `params.UnlockDuration`. ##### Parameters ```go= type ConstructorParams struct { Signers []addr.Address NumApprovalsThreshold uint64 UnlockDuration abi.ChainEpoch StartEpoch abi.ChainEpoch } ``` **`Signers`**: A slice of addresses to initialize as the Multisig's `Signers` * Requirements: * `len(Signers) != 0 && len(Signers) <= SignersMax` (256) * All resolved addresses in `Signers` must be unique. **`NumApprovalsThreshold`**: The number of `Signers` that must call `Approve` in order to execute a Transaction * Requirements: * `NumApprovalsThreshold <= len(Signers)` * `NumApprovalsThreshold != 0` **`UnlockDuration`**: If this value is nonzero, specifies that any `ValueReceived` vests linearly over a period of epochs `UnlockDuration`. * Requirements: * `UnlockDuration >= 0` **`StartEpoch`**: If `UnlockDuration` is nonzero, the `CurrentBalance` of the multisig vests linearly over a period of epochs starting at `StartEpoch` and lasting `UnlockDuration` epochs. ##### Failure conditions * `params.Signers` was empty, or contained duplicate members * `params.NumApprovalsThreshold` was greater than the number of supplied Signers, or was 0 * `params.UnlockDuration` was negative. #### 2. Propose ```go= func (a Actor) Propose(rt vmr.Runtime, params *ProposeParams) *ProposeReturn ``` One of the Multisig's Signers proposes a Transaction to be executed. * The Caller's approval for the newly pending Transaction is registered as well. * If the newly pending Transaction already has enough approvals for execution, it is executed. ##### Parameters ```go= type ProposeParams struct { To addr.Address Value abi.TokenAmount Method abi.MethodNum Params []byte } ``` **`To`**: The destination of the Send. * Notes: * ALL address protocols allowed. * No restrictions currently exist for this value. * If `To == Receiver`, the Multisig will `Send` to itself. This is used to access functions like `Multisig.AddSigner`, `Multisig.RemoveSigner`, `Multisig.SwapSigner`, and `Multisig.ChangeNumApprovalsThreshold`. **`Value`**: The TokenAmount that will be provided via Send. * Notes: * Whether the Multisig has `Value` available to Send is checked when the Transaction is fully approved for execution. * Requirements: * `Value >= 0` **`Method`**: The MethodNum that will be invoked via Send. * Notes: * No restrictions currently exist for this value. **`Params`**: The parameters that will be provided to the invoked method via Send. * Notes: * No restrictions currently exist for this value. ##### Return ```go= type ProposeReturn struct { TxnID TxnID Applied bool Code exitcode.ExitCode Ret []byte } ``` **`TxnID`**: The `TxnID` associated with the newly-proposed Transaction. * Notes: * Should be equal to `st.NextTxnID` before being incremented. * Invariants: * `TxnID >= 0` **`Applied`**: Whether or not the proposal was executed. * Notes: * This should only be the case if `NumApprovalsThreshold == 1`, as it's only possible to have 1 Approval on a newly-proposed Transaction. **`Code`**: If the Transaction was Applied, `Code` is the ExitCode of the Send. * Notes: * This value only matters if `Applied == true` **`Ret`**: If the Transaction was Applied, `Ret` is the return data of the Send. * Notes: * This value only matters if `Applied == true` ##### Failure conditions * Caller is not a Signable type actor * Caller is not a Signer in the Multisig * Specified `Value` to send is negative #### 3. Approve ```go= func (a Actor) Approve(rt vmr.Runtime, params *TxnIDParams) *ApproveReturn ``` One of the Multisig's Signers marks their approval for a specific pending Transaction. `Approve` follows roughly these steps: * If the transaction has sufficient approvals BEFORE the Caller's approval is registered, the Transaction is executed. * Then, the Caller's approval is registered. * Finally, if the Transaction has sufficient approvals AFTER the Caller's approval is registered, the Transaction is executed. In the case that some Transaction `t` has reached the `NumApprovalThreshold` but has not yet been executed, the above order allows a Signer that has already approved `t` to invoke `Approve(t)` to execute the Transaction. ##### Parameters ```go= type TxnIDParams struct { ID TxnID ProposalHash []byte } ``` **`ID`**: The TxnID of the pending Transaction. * Requirements: * Must correspond to an existing Transaction in `st.PendingTxns` **`ProposalHash`**: OPTIONAL. If not `nil`, this is compared to the hash of the Transaction referenced by `ID`. If the two hashes do not match, execution aborts. * Notes: * This is primarily used so the Caller can validate that the TxnID they provide is referencing the expected Transaction. (In case of re-org) ##### Return ```go= type ApproveReturn struct { Applied bool Code exitcode.ExitCode Ret []byte } ``` **`Applied`**: Whether or not the proposal was executed. * Notes: * If `true`, the other fields in the Return provide information about the execution. **`Code`**: If the Transaction was Applied, `Code` is the ExitCode of the Send. * Notes: * This value only matters if `Applied == true` **`Ret`**: If the Transaction was Applied, `Ret` is the return data of the Send. * Notes: * This value only matters if `Applied == true` ##### Failure conditions * Caller is not a Signable type actor * Caller is not a Signer in the Multisig * Referenced `ID` does not exist in `st.PendingTxns` * `ProposalHash` is not `nil` AND does not match calculated hash of referenced Transaction. * Caller has already approved the Transaction AND there are not enough approvals for execution * Enough approvals exist for execution, but the Multisig does not have the available balance for the Transaction's specified `Value` #### 4. Cancel ```go= func (a Actor) Cancel(rt vmr.Runtime, params *TxnIDParams) *adt.EmptyValue ``` Allows the original proposer of a Transaction to cancel its pending status, after which it cannot be approved or executed. * The "original proposer" of a Transaction is the first `Address` in `Transaction.Approved` * Once cancelled, the pending Transaction is completely deleted from `st.PendingTxns`. * Transactions can only be cancelled once. ##### Parameters ```go= type TxnIDParams struct { ID TxnID ProposalHash []byte } ``` **`ID`**: The TxnID of the pending Transaction. * Requirements: * Must correspond to an existing Transaction in `st.PendingTxns` **`ProposalHash`**: OPTIONAL. If not `nil`, this is compared to the hash of the Transaction referenced by `ID`. If the two hashes do not match, execution aborts. * Notes: * This is primarily used so the Caller can validate that the TxnID they provide is referencing the expected Transaction. (In case of re-org) ##### Failure conditions * Caller is not a Signable type actor * Caller is not a Signer in the Multisig * Referenced `ID` does not exist in `st.PendingTxns` * Caller is not the Proposer of the Transaction in question. * (`st.PendingTxns[ID].Approved[0] != Caller`) * `ProposalHash` is not `nil` AND does not match calculated hash of referenced Transaction. #### 5. AddSigner ```go= func (a Actor) AddSigner(rt vmr.Runtime, params *AddSignerParams) *adt.EmptyValue ``` Provides the Multisig functionality to add a Signer to `st.Signers`. * Can only be invoked by the Multisig itself, meaning this is only reachable through an approved and executed Transaction. * Optionally, may increment `st.NumApprovalsThreshold` by 1. ##### Parameters ```go= type AddSignerParams struct { Signer addr.Address Increase bool } ``` **`Signer`**: The address that will be added to `st.Signers`. * Notes: * ALL address protocols allowed. * Requirements: * `Signer` must not exist in `st.Signers` already. **`Increase`**: If `true`, `st.NumApprovalsThreshold` will be increased by 1. ##### Failure conditions * Caller is not `Message().Receiver()` (the Multisig itself) * `params.Signer` is already a Signer * The additional Signer increases the total number of Signers beyond `SignersMax` (256) #### 6. RemoveSigner ```go= func (a Actor) RemoveSigner(rt vmr.Runtime, params *RemoveSignerParams) *adt.EmptyValue ``` Provides the Multisig functionality to remove a Signer from `st.Signers`. * Can only be invoked by the Multisig itself, meaning this is only reachable through an approved and executed Transaction. * Optionally, may decrement `st.NumApprovalsThreshold` by 1. * Only succeeds if the Multisig currently has more than 1 Signer. ##### Parameters ```go= type RemoveSignerParams struct { Signer addr.Address Decrease bool } ``` **`Signer`**: The address that will be removed from `st.Signers`. * Notes: * ALL address protocols allowed. * Requirements: * `Signer` must exist in `st.Signers` **`Decrease`**: If `true`, `st.NumApprovalsThreshold` will be decreased by 1. * Requirements: * If removing `Signer` would result in `len(st.Signers) < st.NumApprovalsThreshold`, `Decrease` must be `true` ##### Failure conditions * Caller is not `Message().Receiver()` (the Multisig itself) * `params.Signer` is not a Signer * There is only 1 Signer in the Multisig * (`len(st.Signers) == 1`) * `params.Decrease` is `false` AND the removal of `params.Signer` would result in `len(st.Signers) < st.NumApprovalsThreshold` #### 7. SwapSigner ```go= func (a Actor) SwapSigner(rt vmr.Runtime, params *SwapSignerParams) *adt.EmptyValue ``` Provides the Multisig functionality to atomically remove one Signer AND add a new Signer. * Can only be invoked by the Multisig itself, meaning this is only reachable through an approved and executed Transaction. * Successful execution should leave the number of Signers unchanged. ##### Parameters ```go= type SwapSignerParams struct { From addr.Address To addr.Address } ``` **`From`**: The address that will be removed from `st.Signers`. * Notes: * ALL address protocols allowed. * Requirements: * `From` must already be a Signer **`To`**: The address that will be added to `st.Signers` * Notes: * ALL address protocols allowed. * Requirements: * `To` must not already be a Signer ##### Failure conditions * Caller is not `Message().Receiver()` (the Multisig itself) * `params.From` is not a Signer * `params.To` is already a Signer #### 8. ChangeNumApprovalsThreshold ```go= func (a Actor) ChangeNumApprovalsThreshold(rt vmr.Runtime, params *ChangeNumApprovalsThresholdParams) *adt.EmptyValue ``` Provides the Multisig functionality to adjust the number of Signers needed to pass proposals. * Can only be invoked by the Multisig itself, meaning this is only reachable through an approved and executed Transaction. ##### Parameters ```go= type ChangeNumApprovalsThresholdParams struct { NewThreshold uint64 } ``` **`NewThreshold`**: The new `NumApprovalsThreshold` * Requirements: * `NewThreshold != 0` * `NewThreshold <= len(st.Signers)` ##### Failure conditions * Caller is not `Message().Receiver()` (the Multisig itself) * `params.NewThreshold == 0` * `params.NewThreshold > len(st.Signers)` ## Open Questions * If a Signer is added, should they immediately be able to Approve prior `PendingTxns`? * If a Signer is removed, should the `PendingTxns` they approved reflect this by removing the Signer from `Approved`? * If `NumApprovalsThreshold` is changed, should prior `PendingTxns` use the updated value? * Should it be possible to `Propose` a transaction, even if it sends more value than is currently available to the multisig? * Should it be possible to `Approve` a transaction, even if it sends more value than is currently available to the multisig? * If a proposal meets the approval threshold (but has not yet been executed), should the proposal be able to be cancelled? * Is Multisig.SwapSigner intended specifically for the scenario where a Signer wishes to use a different public key? * Should Multisig.ChangeNumApprovalsThreshold fail if the new threshold is the same as the old? ## Questions * Given that actor state is loaded all-at-once, is it possible that this object may get too large to load (with arbitrarily-large `Signers`)? * anorth: Yes. * Does a `Signer` need to exist on chain before being added to a multisig's `Signers`? * anorth: No. * Can a multisig be one of its own `Signers`? * anorth: Yes. * If the original proposer is removed from the multisig, can the proposal be cancelled? * anorth: No (but convince me we should change that) * Can a multisig send value without executing a method? What would `Method` be in this case? * anorth: method number 0 is a pure value send

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully