multisign ledger, tx formats - edits from review

This commit is contained in:
mDuo13
2016-04-04 10:20:29 -07:00
parent 1f3eeeb5a0
commit 2045bc55f5
4 changed files with 55 additions and 39 deletions

View File

@@ -4,20 +4,20 @@ The point of the Ripple software is to maintain a shared, global ledger that is
![Diagram: Each ledger is the result of applying transactions to the previous ledger version.](img/ledger-process.png) ![Diagram: Each ledger is the result of applying transactions to the previous ledger version.](img/ledger-process.png)
The shared global ledger is actually a series of individual ledgers, or ledger versions, which `rippled` keeps in its internal database. Every ledger version has a sequence number (also called a ledger index), starting at 1 and incrementing with each new version. Every closed ledger also has an identifying hash value, which uniquely identifies the contents of that ledger. At any given time, a `rippled` instance has an in-progress "current" open ledger, plus some number of closed ledgers that have not yet been approved by consensus, and any number of historical ledgers that have been validated by consensus. Only the validated ledgers are certain to be accurate and immutable. The shared global ledger is actually a series of individual ledgers, or ledger versions, which `rippled` keeps in its internal database. Every ledger version has a [ledger index](#ledger-index) which identifies the order in which ledgers occur. Each closed ledger version also has an identifying hash value, which uniquely identifies the contents of that ledger. At any given time, a `rippled` instance has an in-progress "current" open ledger, plus some number of closed ledgers that have not yet been approved by consensus, and any number of historical ledgers that have been validated by consensus. Only the validated ledgers are certain to be accurate and immutable.
A single ledger version consists of several components: A single ledger version consists of several components:
![Diagram: A ledger has transactions, a state node, and a header with the close time and validation info](img/ledger-components.png) ![Diagram: A ledger has transactions, a state node, and a header with the close time and validation info](img/ledger-components.png)
* A **header** - The ledger's unique index (sequence number), hashes of the other contents, and other metadata. * A **header** - The [ledger index](#ledger-index), hashes of its other contents, and other metadata.
* A **transaction tree** - The [transactions](reference-transaction-format.html) that were applied to the previous ledger to make this one. Transactions are the _only_ way to modify the ledger. * A **transaction tree** - The [transactions](reference-transaction-format.html) that were applied to the previous ledger to make this one. Transactions are the _only_ way to modify the ledger.
* A **state tree** - All the [ledger nodes](#ledger-node-types) that contain the settings, balances, and objects in the ledger as of this version. * A **state tree** - All the [ledger nodes](#ledger-node-types) that contain the settings, balances, and objects in the ledger as of this version.
## Tree Format ## ## Tree Format ##
As its name might suggest, a ledger's state tree is a tree data structure, with each node identified by a 256-bit value called an `index`. In JSON, a ledger node's index value is represented as a 64-character hexadecimal string like `"193C591BF62482468422313F9D3274B5927CA80B4DD3707E42015DD609E39C94"`. Every node in the state tree has an index that you can use as a key to look up the node in the state tree; every transaction has an indentifying hash that you can use to look up the transaction in the transaction tree. Do not confuse the `index` (key) of a ledger node with the `ledger_index` (sequence number) of a ledger. As its name might suggest, a ledger's state tree is a tree data structure, with each node identified by a 256-bit value called an `index`. In JSON, a ledger node's index value is represented as a 64-character hexadecimal string like `"193C591BF62482468422313F9D3274B5927CA80B4DD3707E42015DD609E39C94"`. Every node in the state tree has an index that you can use as a key to look up the node in the state tree; every transaction has an indentifying hash that you can use to look up the transaction in the transaction tree. Do not confuse the `index` (key) of a ledger node with the [`ledger_index` (sequence number) of a ledger](#ledger-index).
In the case of transactions, the identifying hash is based on the signed transaction instructions, but the contents of the transaction object when you look it up also contain the results and metadata of the transaction, which are not taken into account when generating the hash. In the case of transactions, the identifying hash is based on the signed transaction instructions, but the contents of the transaction object when you look it up also contain the results and metadata of the transaction, which are not taken into account when generating the hash.
@@ -33,19 +33,24 @@ Every ledger version has a unique header that describes the contents. You can lo
| Field | JSON Type | [Internal Type][] | Description | | Field | JSON Type | [Internal Type][] | Description |
|-----------------|-----------|-------------------|-------------| |-----------------|-----------|-------------------|-------------|
| ledger\_index | String | UInt32 | The sequence number of the ledger. Some API methods display this as a quoted integer; some display it as a native JSON number. | | [ledger\_index](#ledger-index) | String | UInt32 | The sequence number of the ledger. Some API methods display this as a quoted integer; some display it as a native JSON number. |
| ledger\_hash | String | Hash256 | The SHA-512Half of the ledger header, excluding the `ledger_hash` itself. This serves as a unique identifier for this ledger and all its contents. | | ledger\_hash | String | Hash256 | The SHA-512Half of the ledger header, excluding the `ledger_hash` itself. This serves as a unique identifier for this ledger and all its contents. |
| account\_hash | String | Hash256 | The SHA-512Half of this ledger's state tree information. | | account\_hash | String | Hash256 | The SHA-512Half of this ledger's state tree information. |
| close\_time | Number | UInt32 | The approximate time this ledger closed, as the number of seconds since the Ripple Epoch of 2000-01-01 00:00:00. This value is rounded based on the `close_time_resolution`, so it is possible for subsequent ledgers to have the same value. | | close\_time | Number | UInt32 | The approximate time this ledger closed, as the number of seconds since the Ripple Epoch of 2000-01-01 00:00:00. This value is rounded based on the `close_time_resolution`, so it is possible for subsequent ledgers to have the same value. |
| closed | Boolean | bool | If true, this transaction is no longer accepting new transactions. (However, unless this ledger is validated, it might be replaced by a different ledger with a different set of transactions.) | | closed | Boolean | bool | If true, this transaction is no longer accepting new transactions. (However, unless this ledger is validated, it might be replaced by a different ledger with a different set of transactions.) |
| parent\_hash | String | Hash256 | The `ledger_hash` value of the previous ledger that was used to build this one. If there are different versions of the previous ledger by sequence number, this indicates from which one the ledger was derived. | | parent\_hash | String | Hash256 | The `ledger_hash` value of the previous ledger that was used to build this one. If there are different versions of the previous ledger index, this indicates from which one the ledger was derived. |
| total\_coins | String | UInt64 | The total number of drops of XRP owned by accounts in the ledger. This subtracts XRP that has been destroyed by transaction fees. The actual amount of XRP in circulation is lower because some accounts are "black holes" whose keys are not known by anyone. | | total\_coins | String | UInt64 | The total number of drops of XRP owned by accounts in the ledger. This subtracts XRP that has been destroyed by transaction fees. The actual amount of XRP in circulation is lower because some accounts are "black holes" whose keys are not known by anyone. |
| transaction\_hash | String | Hash256 | The SHA-512Half of the transactions included in this ledger. | | transaction\_hash | String | Hash256 | The SHA-512Half of the transactions included in this ledger. |
| close\_time\_resolution | Number | Uint8 | An integer in the range \[2,120\] indicating the maximum number of seconds by which the `close_time` could be rounded. | | close\_time\_resolution | Number | Uint8 | An integer in the range \[2,120\] indicating the maximum number of seconds by which the `close_time` could be rounded. |
| closeFlags | (Omitted) | UInt8 | A bit-map of flags relating to the closing of this ledger. | | [closeFlags](#close-flags) | (Omitted) | UInt8 | A bit-map of flags relating to the closing of this ledger. |
[Internal Type]: https://wiki.ripple.com/Binary_Format [Internal Type]: https://wiki.ripple.com/Binary_Format
### Ledger Index ###
{% include 'data_types/ledger_index.md' %}
[Hash]: reference-rippled.html#hashes
### Close Flags ### ### Close Flags ###
Currently, the ledger has only one flag defined for closeFlags: **sLCF_NoConsensusTime** (value `1`). If this flag is enabled, it means that validators were in conflict regarding the correct close time for the ledger, but built otherwise the same ledger, so they declared consensus while "agreeing to disagree" on the close time. In this case, the consensus ledger contains a `close_time` value that is 1 second after that of the previous ledger. (In this case, there is no official close time, but the actual real-world close time is probably 3-6 seconds later than the specified `close_time`.) Currently, the ledger has only one flag defined for closeFlags: **sLCF_NoConsensusTime** (value `1`). If this flag is enabled, it means that validators were in conflict regarding the correct close time for the ledger, but built otherwise the same ledger, so they declared consensus while "agreeing to disagree" on the close time. In this case, the consensus ledger contains a `close_time` value that is 1 second after that of the previous ledger. (In this case, there is no official close time, but the actual real-world close time is probably 3-6 seconds later than the specified `close_time`.)
@@ -100,7 +105,7 @@ The `AccountRoot` node has the following fields:
| Balance | String | Amount | The account's current XRP balance in drops, represented as a string. | | Balance | String | Amount | The account's current XRP balance in drops, represented as a string. |
| OwnerCount | Number | UInt32 | The number of objects this account owns in the ledger, which contributes to its owner reserve. | | OwnerCount | Number | UInt32 | The number of objects this account owns in the ledger, which contributes to its owner reserve. |
| PreviousTxnID | String | Hash256 | The identifying hash of the transaction that most recently modified this node. | | PreviousTxnID | String | Hash256 | The identifying hash of the transaction that most recently modified this node. |
| PreviousTxnLgrSeq | Number | UInt32 | The sequence number (`ledger_index`) of the ledger that contains the transaction that most recently modified this node. | | PreviousTxnLgrSeq | Number | UInt32 | The [index of the ledger](#ledger-index) that contains the transaction that most recently modified this node. |
| AccountTxnID | String | Hash256 | (Optional) The identifying hash of the transaction most recently submitted by this account. | | AccountTxnID | String | Hash256 | (Optional) The identifying hash of the transaction most recently submitted by this account. |
| RegularKey | String | AccountID | (Optional) The address of a keypair that can be used to sign transactions for this account instead of the master key. Use a [SetRegularKey transaction](reference-transaction-format.html#setregularkey) to change this value. | | RegularKey | String | AccountID | (Optional) The address of a keypair that can be used to sign transactions for this account instead of the master key. Use a [SetRegularKey transaction](reference-transaction-format.html#setregularkey) to change this value. |
| EmailHash | String | Hash128 | (Optional) The md5 hash of an email address. Clients can use this to look up an avatar through services such as [Gravatar](https://en.gravatar.com/). | | EmailHash | String | Hash128 | (Optional) The md5 hash of an email address. Clients can use this to look up an avatar through services such as [Gravatar](https://en.gravatar.com/). |
@@ -127,7 +132,7 @@ AccountRoot nodes can have the following flag values:
| lsfGlobalFreeze | 0x00400000 | 4194304 | All assets issued by this account are frozen. | asfGlobalFreeze | | lsfGlobalFreeze | 0x00400000 | 4194304 | All assets issued by this account are frozen. | asfGlobalFreeze |
| lsfDefaultRipple | 0x00800000 | 8388608 | Enable [rippling](https://ripple.com/knowledge_center/understanding-the-noripple-flag/) on this account's trust lines by default. Required for gateways; discouraged for other accounts. | asfDefaultRipple | | lsfDefaultRipple | 0x00800000 | 8388608 | Enable [rippling](https://ripple.com/knowledge_center/understanding-the-noripple-flag/) on this account's trust lines by default. Required for gateways; discouraged for other accounts. | asfDefaultRipple |
### AccountRoot index format ### ### AccountRoot Index Format ###
The `index` of an AccountRoot node is the SHA-512Half of the following values put together: The `index` of an AccountRoot node is the SHA-512Half of the following values put together:
@@ -201,7 +206,7 @@ Example Directories:
| TakerGetsCurrency | String | Hash160 | (Offer Directories only) The currency code of the TakerGets amount from the offers in this directory. | | TakerGetsCurrency | String | Hash160 | (Offer Directories only) The currency code of the TakerGets amount from the offers in this directory. |
| TakerGetsIssuer | String | Hash160 | (Offer Directories only) The issuer of the TakerGets amount from the offers in this directory. | | TakerGetsIssuer | String | Hash160 | (Offer Directories only) The issuer of the TakerGets amount from the offers in this directory. |
### Directory index formats ### ### Directory Index Formats ###
There are three different formulas for creating the index of a DirectoryNode, depending on whether the DirectoryNode represents: There are three different formulas for creating the index of a DirectoryNode, depending on whether the DirectoryNode represents:
@@ -275,7 +280,7 @@ An Offer node has the following fields:
| BookNode | String | UInt64 | A hint indicating which page of the offer directory links to this node, in case the directory consists of multiple nodes. | | BookNode | String | UInt64 | A hint indicating which page of the offer directory links to this node, in case the directory consists of multiple nodes. |
| OwnerNode | String | UInt64 | A hint indicating which page of the owner directory links to this node, in case the directory consists of multiple nodes. **Note:** The offer does not contain a direct link to the owner directory containing it, since that value can be derived from the `Account`. | | OwnerNode | String | UInt64 | A hint indicating which page of the owner directory links to this node, in case the directory consists of multiple nodes. **Note:** The offer does not contain a direct link to the owner directory containing it, since that value can be derived from the `Account`. |
| PreviousTxnID | String | Hash256 | The identifying hash of the transaction that most recently modified this node. | | PreviousTxnID | String | Hash256 | The identifying hash of the transaction that most recently modified this node. |
| PreviousTxnLgrSeq | Number | UInt32 | The sequence number (`ledger_index`) of the ledger that contains the transaction that most recently modified this node. | | PreviousTxnLgrSeq | Number | UInt32 | The [index of the ledger](#ledger-index) that contains the transaction that most recently modified this node. |
| Expiration | Number | UInt32 | (Optional) Indicates the time after which this offer will be considered unfunded. See [Specifying Time](reference-rippled.html#specifying-time) for details. | | Expiration | Number | UInt32 | (Optional) Indicates the time after which this offer will be considered unfunded. See [Specifying Time](reference-rippled.html#specifying-time) for details. |
### Offer Flags ### ### Offer Flags ###
@@ -289,7 +294,7 @@ Offer nodes can have the following flag values:
| lsfPassive | 0x00010000 | 65536 | The node was placed as a passive offer. This has no effect on the node in the ledger. | tfPassive | | lsfPassive | 0x00010000 | 65536 | The node was placed as a passive offer. This has no effect on the node in the ledger. | tfPassive |
| lsfSell | 0x00020000 | 131072 | The node was placed as a sell offer. This has no effect on the node in the ledger (because tfSell only matters if you get a better rate than you asked for, which cannot happen after the node enters the ledger). | tfSell | | lsfSell | 0x00020000 | 131072 | The node was placed as a sell offer. This has no effect on the node in the ledger (because tfSell only matters if you get a better rate than you asked for, which cannot happen after the node enters the ledger). | tfSell |
### Offer index format ### ### Offer Index Format ###
The `index` of an Offer node is the SHA-512Half of the following values put together: The `index` of an Offer node is the SHA-512Half of the following values put together:
@@ -344,7 +349,7 @@ A RippleState node has the following fields:
| LowLimit | Object | Amount | The limit that the low account has set on the trust line. The `issuer` is the address of the low account that set this limit. | | LowLimit | Object | Amount | The limit that the low account has set on the trust line. The `issuer` is the address of the low account that set this limit. |
| HighLimit | Object | Amount | The limit that the high account has set on the trust line. The `issuer` is the address of the high account that set this limit. | | HighLimit | Object | Amount | The limit that the high account has set on the trust line. The `issuer` is the address of the high account that set this limit. |
| PreviousTxnID | String | Hash256 | The identifying hash of the transaction that most recently modified this node. | | PreviousTxnID | String | Hash256 | The identifying hash of the transaction that most recently modified this node. |
| PreviousTxnLgrSeq | Number | UInt32 | The sequence number (`ledger_index`) of the ledger that contains the transaction that most recently modified this node. | | PreviousTxnLgrSeq | Number | UInt32 | The [index of the ledger](#ledger-index) that contains the transaction that most recently modified this node. |
| LowNode | String | UInt64 | (Omitted in some historical ledgers) A hint indicating which page of the low account's owner directory links to this node, in case the directory consists of multiple nodes. | | LowNode | String | UInt64 | (Omitted in some historical ledgers) A hint indicating which page of the low account's owner directory links to this node, in case the directory consists of multiple nodes. |
| HighNode | String | UInt64 | (Omitted in some historical ledgers) A hint indicating which page of the high account's owner directory links to this node, in case the directory consists of multiple nodes. | | HighNode | String | UInt64 | (Omitted in some historical ledgers) A hint indicating which page of the high account's owner directory links to this node, in case the directory consists of multiple nodes. |
| LowQualityIn | Number | UInt32 | (Optional) The inbound quality set by the low account, as an integer in the implied ratio LowQualityIn:1,000,000,000. The value 0 is equivalent to 1 billion, or face value. | | LowQualityIn | Number | UInt32 | (Optional) The inbound quality set by the low account, as an integer in the implied ratio LowQualityIn:1,000,000,000. The value 0 is equivalent to 1 billion, or face value. |
@@ -390,7 +395,7 @@ The default state of the two NoRipple flags depends on the state of the [lsfDefa
Fortunately, `rippled` uses lazy evaluation to calculate the owner reserve. This means that even if an account changes the default state of all its trust lines by changing the DefaultRipple flag, that account's reserve stays the same initially. If an account modifies a trust line, `rippled` re-evaluates whether that individual trust line is in its default state and should contribute the owner reserve. Fortunately, `rippled` uses lazy evaluation to calculate the owner reserve. This means that even if an account changes the default state of all its trust lines by changing the DefaultRipple flag, that account's reserve stays the same initially. If an account modifies a trust line, `rippled` re-evaluates whether that individual trust line is in its default state and should contribute the owner reserve.
### RippleState index format ### ### RippleState Index Format ###
The `index` of a RippleState node is the SHA-512Half of the following values put together: The `index` of a RippleState node is the SHA-512Half of the following values put together:
@@ -447,23 +452,23 @@ A SignerList node has the following fields:
| OwnerNode | String | UInt64 | A hint indicating which page of the owner directory links to this node, in case the directory consists of multiple nodes. | | OwnerNode | String | UInt64 | A hint indicating which page of the owner directory links to this node, in case the directory consists of multiple nodes. |
| SignerQuorum | Number | UInt32 | A target number for signer weights. To produce a valid signature for the owner of this SignerList, the signers must provide valid signatures whose weights sum to this value or more. | | SignerQuorum | Number | UInt32 | A target number for signer weights. To produce a valid signature for the owner of this SignerList, the signers must provide valid signatures whose weights sum to this value or more. |
| SignerEntries | Array | Array | An array of SignerEntry objects representing the parties who are part of this signer list. | | SignerEntries | Array | Array | An array of SignerEntry objects representing the parties who are part of this signer list. |
| SignerListID | Number | UInt32 | An ID for this signer list. Currently always set to `0`. If a future update allows multiple signer lists for an account, this may change. | | SignerListID | Number | UInt32 | An ID for this signer list. Currently always set to `0`. If a future [amendment](concept-amendments.html) allows multiple signer lists for an account, this may change. |
| PreviousTxnID | String | Hash256 | The identifying hash of the transaction that most recently modified this node. | | PreviousTxnID | String | Hash256 | The identifying hash of the transaction that most recently modified this node. |
| PreviousTxnLgrSeq | Number | UInt32 | The sequence number (`ledger_index`) of the ledger that contains the transaction that most recently modified this node. | | PreviousTxnLgrSeq | Number | UInt32 | The [index of the ledger](#ledger-index) that contains the transaction that most recently modified this node. |
The `SignerEntries` may be any combination of funded and unfunded addresses that use either secp256k1 or ed25519 keys. The `SignerEntries` may be any combination of funded and unfunded addresses that use either secp256k1 or ed25519 keys.
### SignerEntry object ### ### SignerEntry Object ###
Each member of the `SignerEntries` field is an object that describes that signer in the list. A SignerEntry has the following fields: Each member of the `SignerEntries` field is an object that describes that signer in the list. A SignerEntry has the following fields:
| Name | JSON Type | Internal Type | Description | | Name | JSON Type | Internal Type | Description |
|-----------------|-----------|---------------|-------------| |-----------------|-----------|---------------|-------------|
| Account | String | AccountID | An address whose signature contributes to the multi-signature. This does not need to be a funded Ripple account. | | Account | String | AccountID | A Ripple address whose signature contributes to the multi-signature. It does not need to be a funded address in the ledger. |
| SignerWeight | Number | UInt16 | The weight of signatures from this signer. A multi-signature is only valid of the sum weight of the signatures provided meets or exceeds the SignerList's `SignerQuorum` value. | | SignerWeight | Number | UInt16 | The weight of a signature from this signer. A multi-signature is only valid if the sum weight of the signatures provided meets or exceeds the SignerList's `SignerQuorum` value. |
When processing a multi-signed transaction, the server dereferences the `Account` values with respect to the ledger at the time of transaction execution. If the address _does not_ correspond to a funded [AccountRoot node](#accountroot), then only the master secret associated with that address can be used to produce a valid signature. If the account _does_ exist in the ledger, then it depends on the state of that account. If the account has a Regular Key configured, the Regular Key can be used. The account's master key can only be used if it is not disabled. Even if the account has a SignerList configured, a multi-signature cannot be used as a valid component to another multi-signature. (In other words, "multi-level" multi-signing is disallowed.) When processing a multi-signed transaction, the server dereferences the `Account` values with respect to the ledger at the time of transaction execution. If the address _does not_ correspond to a funded [AccountRoot node](#accountroot), then only the master secret associated with that address can be used to produce a valid signature. If the account _does_ exist in the ledger, then it depends on the state of that account. If the account has a Regular Key configured, the Regular Key can be used. The account's master key can only be used if it is not disabled. A multi-signature cannot be used as a component of another multi-signature.
### SignerLists and Reserves ### ### SignerLists and Reserves ###
A SignerList contributes to the [Account Reserve](https://wiki.ripple.com/Reserves). The SignerList itself counts as two objects, and each member of the list counts as one, so that the total owner reserve associated with a SignerList is anywhere from 3 times to 10 times the reserve required by a single trust line ([RippleState](#ripplestate)) or [Offer](#offer) node in the ledger. A SignerList contributes to its owner's [reserve requirement](concept-reserves.html). The SignerList itself counts as two objects, and each member of the list counts as one. As a result, the total owner reserve associated with a SignerList is anywhere from 3 times to 10 times the reserve required by a single trust line ([RippleState](#ripplestate)) or [Offer](#offer) node in the ledger.

View File

@@ -31,6 +31,8 @@ If you want to test multi-signing before it becomes available in the production
Setting up Multi-Signing Setting up Multi-Signing
------------------------ ------------------------
Before you can set up multi-signing, first check that [multi-signing is available](#availability-of-multi-signing).
To multi-sign transactions from a particular address, you must create a list of addresses that can contribute to a multi-signature for your address. This list is stored in the Ripple Consensus Ledger as a [SignerList node](reference-ledger-format.html#signerlist). The following procedure demonstrates how to set up a SignerList for your address: To multi-sign transactions from a particular address, you must create a list of addresses that can contribute to a multi-signature for your address. This list is stored in the Ripple Consensus Ledger as a [SignerList node](reference-ledger-format.html#signerlist). The following procedure demonstrates how to set up a SignerList for your address:
@@ -232,7 +234,7 @@ At this point, your address is ready to [send a multi-signed transaction](#sendi
Sending a Multi-Signed Transaction Sending a Multi-Signed Transaction
---------------------------------- ----------------------------------
Before you can multi-sign a transaction, first check that [multi-sign is available](#availability-of-multi-signing) and [set up multi-signing](#set-up-multi-sign) for your address. The following procedure demonstrates how to create, sign, and submit a multi-signed transaction. Before you can multi-sign a transaction, first [set up multi-signing](#set-up-multi-sign) for your address. The following procedure demonstrates how to create, sign, and submit a multi-signed transaction.
### 1. Create the transaction ## ### 1. Create the transaction ##

View File

@@ -130,16 +130,16 @@
<h1 id="the-ledger">The Ledger</h1> <h1 id="the-ledger">The Ledger</h1>
<p>The point of the Ripple software is to maintain a shared, global ledger that is open to all, so that individual participants can trust the integrity of the ledger without having to trust any single institution to manage it. The <code>rippled</code> server software accomplishes this by maintaining a ledger database that can only be updated according to very specific rules. Each instance of <code>rippled</code> maintains a full copy of the ledger, and the peer-to-peer network of <code>rippled</code> servers distributes candidate transactions among themselves. The consensus process determines which transactions get applied to each new version of the ledger. See also: <a href="https://ripple.com/knowledge_center/the-ripple-ledger-consensus-process/">The Consensus Process</a>.</p> <p>The point of the Ripple software is to maintain a shared, global ledger that is open to all, so that individual participants can trust the integrity of the ledger without having to trust any single institution to manage it. The <code>rippled</code> server software accomplishes this by maintaining a ledger database that can only be updated according to very specific rules. Each instance of <code>rippled</code> maintains a full copy of the ledger, and the peer-to-peer network of <code>rippled</code> servers distributes candidate transactions among themselves. The consensus process determines which transactions get applied to each new version of the ledger. See also: <a href="https://ripple.com/knowledge_center/the-ripple-ledger-consensus-process/">The Consensus Process</a>.</p>
<p><img alt="Diagram: Each ledger is the result of applying transactions to the previous ledger version." src="img/ledger-process.png"/></p> <p><img alt="Diagram: Each ledger is the result of applying transactions to the previous ledger version." src="img/ledger-process.png"/></p>
<p>The shared global ledger is actually a series of individual ledgers, or ledger versions, which <code>rippled</code> keeps in its internal database. Every ledger version has a sequence number (also called a ledger index), starting at 1 and incrementing with each new version. Every closed ledger also has an identifying hash value, which uniquely identifies the contents of that ledger. At any given time, a <code>rippled</code> instance has an in-progress "current" open ledger, plus some number of closed ledgers that have not yet been approved by consensus, and any number of historical ledgers that have been validated by consensus. Only the validated ledgers are certain to be accurate and immutable.</p> <p>The shared global ledger is actually a series of individual ledgers, or ledger versions, which <code>rippled</code> keeps in its internal database. Every ledger version has a <a href="#ledger-index">ledger index</a> which identifies the order in which ledgers occur. Each closed ledger version also has an identifying hash value, which uniquely identifies the contents of that ledger. At any given time, a <code>rippled</code> instance has an in-progress "current" open ledger, plus some number of closed ledgers that have not yet been approved by consensus, and any number of historical ledgers that have been validated by consensus. Only the validated ledgers are certain to be accurate and immutable.</p>
<p>A single ledger version consists of several components:</p> <p>A single ledger version consists of several components:</p>
<p><img alt="Diagram: A ledger has transactions, a state node, and a header with the close time and validation info" src="img/ledger-components.png"/></p> <p><img alt="Diagram: A ledger has transactions, a state node, and a header with the close time and validation info" src="img/ledger-components.png"/></p>
<ul> <ul>
<li>A <strong>header</strong> - The ledger's unique index (sequence number), hashes of the other contents, and other metadata.</li> <li>A <strong>header</strong> - The <a href="#ledger-index">ledger index</a>, hashes of its other contents, and other metadata.</li>
<li>A <strong>transaction tree</strong> - The <a href="reference-transaction-format.html">transactions</a> that were applied to the previous ledger to make this one. Transactions are the <em>only</em> way to modify the ledger.</li> <li>A <strong>transaction tree</strong> - The <a href="reference-transaction-format.html">transactions</a> that were applied to the previous ledger to make this one. Transactions are the <em>only</em> way to modify the ledger.</li>
<li>A <strong>state tree</strong> - All the <a href="#ledger-node-types">ledger nodes</a> that contain the settings, balances, and objects in the ledger as of this version.</li> <li>A <strong>state tree</strong> - All the <a href="#ledger-node-types">ledger nodes</a> that contain the settings, balances, and objects in the ledger as of this version.</li>
</ul> </ul>
<h2 id="tree-format">Tree Format</h2> <h2 id="tree-format">Tree Format</h2>
<p>As its name might suggest, a ledger's state tree is a tree data structure, with each node identified by a 256-bit value called an <code>index</code>. In JSON, a ledger node's index value is represented as a 64-character hexadecimal string like <code>"193C591BF62482468422313F9D3274B5927CA80B4DD3707E42015DD609E39C94"</code>. Every node in the state tree has an index that you can use as a key to look up the node in the state tree; every transaction has an indentifying hash that you can use to look up the transaction in the transaction tree. Do not confuse the <code>index</code> (key) of a ledger node with the <code>ledger_index</code> (sequence number) of a ledger.</p> <p>As its name might suggest, a ledger's state tree is a tree data structure, with each node identified by a 256-bit value called an <code>index</code>. In JSON, a ledger node's index value is represented as a 64-character hexadecimal string like <code>"193C591BF62482468422313F9D3274B5927CA80B4DD3707E42015DD609E39C94"</code>. Every node in the state tree has an index that you can use as a key to look up the node in the state tree; every transaction has an indentifying hash that you can use to look up the transaction in the transaction tree. Do not confuse the <code>index</code> (key) of a ledger node with the <a href="#ledger-index"><code>ledger_index</code> (sequence number) of a ledger</a>.</p>
<p>In the case of transactions, the identifying hash is based on the signed transaction instructions, but the contents of the transaction object when you look it up also contain the results and metadata of the transaction, which are not taken into account when generating the hash.</p> <p>In the case of transactions, the identifying hash is based on the signed transaction instructions, but the contents of the transaction object when you look it up also contain the results and metadata of the transaction, which are not taken into account when generating the hash.</p>
<p>In the case of state nodes, <code>rippled</code> usually includes the <code>index</code> of the node along with its contents. However, the index itself is not part of the contents. The index is derived by hashing important contents of the node, along with a <a href="https://github.com/ripple/rippled/blob/ceff6bc2713eaf80feafe56a02f4d636827b89a9/src/ripple/protocol/LedgerFormats.h#L94">namespace identifier</a>. The ledger node type determines which namespace identifier to use as well as which contents to include in the hash. This ensures every index is unique. For a hash function, <code>rippled</code> uses SHA-512 and then truncates the result to the first 256 bytes. This algorithm, informally called SHA-512Half, provides an output that has comparable security to SHA-256, but runs faster on 64-bit processors.</p> <p>In the case of state nodes, <code>rippled</code> usually includes the <code>index</code> of the node along with its contents. However, the index itself is not part of the contents. The index is derived by hashing important contents of the node, along with a <a href="https://github.com/ripple/rippled/blob/ceff6bc2713eaf80feafe56a02f4d636827b89a9/src/ripple/protocol/LedgerFormats.h#L94">namespace identifier</a>. The ledger node type determines which namespace identifier to use as well as which contents to include in the hash. This ensures every index is unique. For a hash function, <code>rippled</code> uses SHA-512 and then truncates the result to the first 256 bytes. This algorithm, informally called SHA-512Half, provides an output that has comparable security to SHA-256, but runs faster on 64-bit processors.</p>
<p><img alt="Diagram: rippled uses SHA-512Half to generate indexes for ledger nodes. The space key prevents indexes for different node types from colliding." src="img/ledger-indexes.png"/></p> <p><img alt="Diagram: rippled uses SHA-512Half to generate indexes for ledger nodes. The space key prevents indexes for different node types from colliding." src="img/ledger-indexes.png"/></p>
@@ -157,7 +157,7 @@
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td>ledger_index</td> <td><a href="#ledger-index">ledger_index</a></td>
<td>String</td> <td>String</td>
<td>UInt32</td> <td>UInt32</td>
<td>The sequence number of the ledger. Some API methods display this as a quoted integer; some display it as a native JSON number.</td> <td>The sequence number of the ledger. Some API methods display this as a quoted integer; some display it as a native JSON number.</td>
@@ -190,7 +190,7 @@
<td>parent_hash</td> <td>parent_hash</td>
<td>String</td> <td>String</td>
<td>Hash256</td> <td>Hash256</td>
<td>The <code>ledger_hash</code> value of the previous ledger that was used to build this one. If there are different versions of the previous ledger by sequence number, this indicates from which one the ledger was derived.</td> <td>The <code>ledger_hash</code> value of the previous ledger that was used to build this one. If there are different versions of the previous ledger index, this indicates from which one the ledger was derived.</td>
</tr> </tr>
<tr> <tr>
<td>total_coins</td> <td>total_coins</td>
@@ -211,13 +211,21 @@
<td>An integer in the range [2,120] indicating the maximum number of seconds by which the <code>close_time</code> could be rounded.</td> <td>An integer in the range [2,120] indicating the maximum number of seconds by which the <code>close_time</code> could be rounded.</td>
</tr> </tr>
<tr> <tr>
<td>closeFlags</td> <td><a href="#close-flags">closeFlags</a></td>
<td>(Omitted)</td> <td>(Omitted)</td>
<td>UInt8</td> <td>UInt8</td>
<td>A bit-map of flags relating to the closing of this ledger.</td> <td>A bit-map of flags relating to the closing of this ledger.</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<h3 id="ledger-index">Ledger Index</h3>
<p>A ledger index is a 32-bit unsigned integer used to identify a ledger. The ledger index is also known as the ledger's sequence number. The very first ledger was ledger index 1, and each subsequent ledger has a ledger index 1 higher than that of the ledger immediately before it.</p>
<p>The ledger index indicates the order of the ledgers; the <a href="reference-rippled.html#hashes">Hash</a> value identifies the exact contents of the ledger. Two ledgers with the same hash are always identical. For validated ledgers, hash values and sequence numbers are equally valid and correlate 1:1. However, this is not true for in-progress ledgers:</p>
<ul>
<li>Two different <code>rippled</code> servers may have different contents for a current ledger with the same ledger index, due to latency in propagating transactions throughout the network.</li>
<li>There may be multiple closed ledger versions competing to be validated by consensus. These ledger versions have the same sequence number but different contents (and different hashes). Only one of these closed ledgers can become validated.</li>
<li>A current ledger's contents change over time, which would cause its hash to change, even though its ledger index number stays the same. Therefore, the hash of a ledger is not calculated until the ledger is closed.</li>
</ul>
<h3 id="close-flags">Close Flags</h3> <h3 id="close-flags">Close Flags</h3>
<p>Currently, the ledger has only one flag defined for closeFlags: <strong>sLCF_NoConsensusTime</strong> (value <code>1</code>). If this flag is enabled, it means that validators were in conflict regarding the correct close time for the ledger, but built otherwise the same ledger, so they declared consensus while "agreeing to disagree" on the close time. In this case, the consensus ledger contains a <code>close_time</code> value that is 1 second after that of the previous ledger. (In this case, there is no official close time, but the actual real-world close time is probably 3-6 seconds later than the specified <code>close_time</code>.)</p> <p>Currently, the ledger has only one flag defined for closeFlags: <strong>sLCF_NoConsensusTime</strong> (value <code>1</code>). If this flag is enabled, it means that validators were in conflict regarding the correct close time for the ledger, but built otherwise the same ledger, so they declared consensus while "agreeing to disagree" on the close time. In this case, the consensus ledger contains a <code>close_time</code> value that is 1 second after that of the previous ledger. (In this case, there is no official close time, but the actual real-world close time is probably 3-6 seconds later than the specified <code>close_time</code>.)</p>
<p>The <code>closeFlags</code> field is not included in any JSON representations of a ledger, but it is a part of the binary representation of a ledger, and is one of the fields that determine the ledger's hash.</p> <p>The <code>closeFlags</code> field is not included in any JSON representations of a ledger, but it is a part of the binary representation of a ledger, and is one of the fields that determine the ledger's hash.</p>
@@ -307,7 +315,7 @@
<td>PreviousTxnLgrSeq</td> <td>PreviousTxnLgrSeq</td>
<td>Number</td> <td>Number</td>
<td>UInt32</td> <td>UInt32</td>
<td>The sequence number (<code>ledger_index</code>) of the ledger that contains the transaction that most recently modified this node.</td> <td>The <a href="#ledger-index">index of the ledger</a> that contains the transaction that most recently modified this node.</td>
</tr> </tr>
<tr> <tr>
<td>AccountTxnID</td> <td>AccountTxnID</td>
@@ -431,7 +439,7 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<h3 id="accountroot-index-format">AccountRoot index format</h3> <h3 id="accountroot-index-format">AccountRoot Index Format</h3>
<p>The <code>index</code> of an AccountRoot node is the SHA-512Half of the following values put together:</p> <p>The <code>index</code> of an AccountRoot node is the SHA-512Half of the following values put together:</p>
<ul> <ul>
<li>The Account space key (<code>a</code>)</li> <li>The Account space key (<code>a</code>)</li>
@@ -561,7 +569,7 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<h3 id="directory-index-formats">Directory index formats</h3> <h3 id="directory-index-formats">Directory Index Formats</h3>
<p>There are three different formulas for creating the index of a DirectoryNode, depending on whether the DirectoryNode represents:</p> <p>There are three different formulas for creating the index of a DirectoryNode, depending on whether the DirectoryNode represents:</p>
<ul> <ul>
<li>The first page (also called the root) of an Owner Directory,</li> <li>The first page (also called the root) of an Owner Directory,</li>
@@ -687,7 +695,7 @@
<td>PreviousTxnLgrSeq</td> <td>PreviousTxnLgrSeq</td>
<td>Number</td> <td>Number</td>
<td>UInt32</td> <td>UInt32</td>
<td>The sequence number (<code>ledger_index</code>) of the ledger that contains the transaction that most recently modified this node.</td> <td>The <a href="#ledger-index">index of the ledger</a> that contains the transaction that most recently modified this node.</td>
</tr> </tr>
<tr> <tr>
<td>Expiration</td> <td>Expiration</td>
@@ -727,7 +735,7 @@
</tr> </tr>
</tbody> </tbody>
</table> </table>
<h3 id="offer-index-format">Offer index format</h3> <h3 id="offer-index-format">Offer Index Format</h3>
<p>The <code>index</code> of an Offer node is the SHA-512Half of the following values put together:</p> <p>The <code>index</code> of an Offer node is the SHA-512Half of the following values put together:</p>
<ul> <ul>
<li>The Offer space key (<code>o</code>)</li> <li>The Offer space key (<code>o</code>)</li>
@@ -815,7 +823,7 @@
<td>PreviousTxnLgrSeq</td> <td>PreviousTxnLgrSeq</td>
<td>Number</td> <td>Number</td>
<td>UInt32</td> <td>UInt32</td>
<td>The sequence number (<code>ledger_index</code>) of the ledger that contains the transaction that most recently modified this node.</td> <td>The <a href="#ledger-index">index of the ledger</a> that contains the transaction that most recently modified this node.</td>
</tr> </tr>
<tr> <tr>
<td>LowNode</td> <td>LowNode</td>
@@ -967,7 +975,7 @@
<p>The <strong>lsfLowAuth</strong> and <strong>lsfHighAuth</strong> flags do not count against the default state, because they cannot be disabled.</p> <p>The <strong>lsfLowAuth</strong> and <strong>lsfHighAuth</strong> flags do not count against the default state, because they cannot be disabled.</p>
<p>The default state of the two NoRipple flags depends on the state of the <a href="#accountroot-flags">lsfDefaultRipple flag</a> in their corresponding AccountRoot nodes. If DefaultRipple is disabled (the default), then the default state of the lsfNoRipple flag is <em>enabled</em> for all of an account's trust lines. If an account enables DefaultRipple, then the lsfNoRipple flag is <em>disabled</em> (rippling is enabled) for an account's trust lines by default. <strong>Note:</strong> Prior to the introduction of the DefaultRipple flags in <code>rippled</code> version 0.27.3 (March 10, 2015), the default state for all trust lines was with lsfNoRipple disabled (rippling enabled).</p> <p>The default state of the two NoRipple flags depends on the state of the <a href="#accountroot-flags">lsfDefaultRipple flag</a> in their corresponding AccountRoot nodes. If DefaultRipple is disabled (the default), then the default state of the lsfNoRipple flag is <em>enabled</em> for all of an account's trust lines. If an account enables DefaultRipple, then the lsfNoRipple flag is <em>disabled</em> (rippling is enabled) for an account's trust lines by default. <strong>Note:</strong> Prior to the introduction of the DefaultRipple flags in <code>rippled</code> version 0.27.3 (March 10, 2015), the default state for all trust lines was with lsfNoRipple disabled (rippling enabled).</p>
<p>Fortunately, <code>rippled</code> uses lazy evaluation to calculate the owner reserve. This means that even if an account changes the default state of all its trust lines by changing the DefaultRipple flag, that account's reserve stays the same initially. If an account modifies a trust line, <code>rippled</code> re-evaluates whether that individual trust line is in its default state and should contribute the owner reserve.</p> <p>Fortunately, <code>rippled</code> uses lazy evaluation to calculate the owner reserve. This means that even if an account changes the default state of all its trust lines by changing the DefaultRipple flag, that account's reserve stays the same initially. If an account modifies a trust line, <code>rippled</code> re-evaluates whether that individual trust line is in its default state and should contribute the owner reserve.</p>
<h3 id="ripplestate-index-format">RippleState index format</h3> <h3 id="ripplestate-index-format">RippleState Index Format</h3>
<p>The <code>index</code> of a RippleState node is the SHA-512Half of the following values put together:</p> <p>The <code>index</code> of a RippleState node is the SHA-512Half of the following values put together:</p>
<ul> <ul>
<li>The RippleState space key (<code>r</code>)</li> <li>The RippleState space key (<code>r</code>)</li>
@@ -1043,7 +1051,7 @@
<td>SignerListID</td> <td>SignerListID</td>
<td>Number</td> <td>Number</td>
<td>UInt32</td> <td>UInt32</td>
<td>An ID for this signer list. Currently always set to <code>0</code>. If a future update allows multiple signer lists for an account, this may change.</td> <td>An ID for this signer list. Currently always set to <code>0</code>. If a future <a href="concept-amendments.html">amendment</a> allows multiple signer lists for an account, this may change.</td>
</tr> </tr>
<tr> <tr>
<td>PreviousTxnID</td> <td>PreviousTxnID</td>
@@ -1055,12 +1063,12 @@
<td>PreviousTxnLgrSeq</td> <td>PreviousTxnLgrSeq</td>
<td>Number</td> <td>Number</td>
<td>UInt32</td> <td>UInt32</td>
<td>The sequence number (<code>ledger_index</code>) of the ledger that contains the transaction that most recently modified this node.</td> <td>The <a href="#ledger-index">index of the ledger</a> that contains the transaction that most recently modified this node.</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
<p>The <code>SignerEntries</code> may be any combination of funded and unfunded addresses that use either secp256k1 or ed25519 keys.</p> <p>The <code>SignerEntries</code> may be any combination of funded and unfunded addresses that use either secp256k1 or ed25519 keys.</p>
<h3 id="signerentry-object">SignerEntry object</h3> <h3 id="signerentry-object">SignerEntry Object</h3>
<p>Each member of the <code>SignerEntries</code> field is an object that describes that signer in the list. A SignerEntry has the following fields:</p> <p>Each member of the <code>SignerEntries</code> field is an object that describes that signer in the list. A SignerEntry has the following fields:</p>
<table> <table>
<thead> <thead>
@@ -1088,7 +1096,7 @@
</table> </table>
<p>When processing a multi-signed transaction, the server dereferences the <code>Account</code> values with respect to the ledger at the time of transaction execution. If the address <em>does not</em> correspond to a funded <a href="#accountroot">AccountRoot node</a>, then only the master secret associated with that address can be used to produce a valid signature. If the account <em>does</em> exist in the ledger, then it depends on the state of that account. If the account has a Regular Key configured, the Regular Key can be used. The account's master key can only be used if it is not disabled. Even if the account has a SignerList configured, a multi-signature cannot be used as a valid component to another multi-signature. (In other words, "multi-level" multi-signing is disallowed.)</p> <p>When processing a multi-signed transaction, the server dereferences the <code>Account</code> values with respect to the ledger at the time of transaction execution. If the address <em>does not</em> correspond to a funded <a href="#accountroot">AccountRoot node</a>, then only the master secret associated with that address can be used to produce a valid signature. If the account <em>does</em> exist in the ledger, then it depends on the state of that account. If the account has a Regular Key configured, the Regular Key can be used. The account's master key can only be used if it is not disabled. Even if the account has a SignerList configured, a multi-signature cannot be used as a valid component to another multi-signature. (In other words, "multi-level" multi-signing is disallowed.)</p>
<h3 id="signerlists-and-reserves">SignerLists and Reserves</h3> <h3 id="signerlists-and-reserves">SignerLists and Reserves</h3>
<p>A SignerList contributes to the <a href="https://wiki.ripple.com/Reserves">Account Reserve</a>. The SignerList itself counts as two objects, and each member of the list counts as one, so that the total owner reserve associated with a SignerList is anywhere from 3 times to 10 times the reserve required by a single trust line (<a href="#ripplestate">RippleState</a>) or <a href="#offer">Offer</a> node in the ledger.</p> <p>A SignerList contributes to its owner's <a href="concept-reserves.html">reserve requirement</a>. The SignerList itself counts as two objects, and each member of the list counts as one, so that the total owner reserve associated with a SignerList is anywhere from 3 times to 10 times the reserve required by a single trust line (<a href="#ripplestate">RippleState</a>) or <a href="#offer">Offer</a> node in the ledger.</p>
</div> </div>
</main> </main>
</div> </div>

View File

@@ -148,6 +148,7 @@
MultiSign MultiSign
</code></pre> </code></pre>
<h2 id="setting-up-multi-signing">Setting up Multi-Signing</h2> <h2 id="setting-up-multi-signing">Setting up Multi-Signing</h2>
<p>Before you can set up multi-signing, first check that <a href="#availability-of-multi-signing">multi-signing is available</a>.</p>
<p>To multi-sign transactions from a particular address, you must create a list of addresses that can contribute to a multi-signature for your address. This list is stored in the Ripple Consensus Ledger as a <a href="reference-ledger-format.html#signerlist">SignerList node</a>. The following procedure demonstrates how to set up a SignerList for your address:</p> <p>To multi-sign transactions from a particular address, you must create a list of addresses that can contribute to a multi-signature for your address. This list is stored in the Ripple Consensus Ledger as a <a href="reference-ledger-format.html#signerlist">SignerList node</a>. The following procedure demonstrates how to set up a SignerList for your address:</p>
<h3 id="1-prepare-a-funded-address">1. Prepare a funded address</h3> <h3 id="1-prepare-a-funded-address">1. Prepare a funded address</h3>
<p>You need a Ripple address that can send transactions, and has enough XRP available. Multi-signing requires more than the usual amount of XRP for the <a href="concept-reserves.html">account reserve</a> and <a href="concept-transaction-cost.html">transaction cost</a>, increasing with the number of signers and signatures you use.</p> <p>You need a Ripple address that can send transactions, and has enough XRP available. Multi-signing requires more than the usual amount of XRP for the <a href="concept-reserves.html">account reserve</a> and <a href="concept-transaction-cost.html">transaction cost</a>, increasing with the number of signers and signatures you use.</p>
@@ -321,7 +322,7 @@ Connecting to 127.0.0.1:5005
<li>Remove the address's regular key (if you previously set one) by sending a <a href="reference-transaction-format.html#setregularkey">SetRegularKey transaction</a>.</li> <li>Remove the address's regular key (if you previously set one) by sending a <a href="reference-transaction-format.html#setregularkey">SetRegularKey transaction</a>.</li>
</ul> </ul>
<h2 id="sending-a-multi-signed-transaction">Sending a Multi-Signed Transaction</h2> <h2 id="sending-a-multi-signed-transaction">Sending a Multi-Signed Transaction</h2>
<p>Before you can multi-sign a transaction, first check that <a href="#availability-of-multi-signing">multi-sign is available</a> and <a href="#set-up-multi-sign">set up multi-signing</a> for your address. The following procedure demonstrates how to create, sign, and submit a multi-signed transaction.</p> <p>Before you can multi-sign a transaction, first <a href="#set-up-multi-sign">set up multi-signing</a> for your address. The following procedure demonstrates how to create, sign, and submit a multi-signed transaction.</p>
<h3 id="1-create-the-transaction">1. Create the transaction</h3> <h3 id="1-create-the-transaction">1. Create the transaction</h3>
<p>Create a JSON object that represents the transaction you want to submit. You have to specify <em>everything</em> about this transaction, including <code>Fee</code> and <code>Sequence</code>. Also include the field <code>SigningPubKey</code> as an empty string, to indicate that the transaction is multi-signed.</p> <p>Create a JSON object that represents the transaction you want to submit. You have to specify <em>everything</em> about this transaction, including <code>Fee</code> and <code>Sequence</code>. Also include the field <code>SigningPubKey</code> as an empty string, to indicate that the transaction is multi-signed.</p>
<p>Keep in mind that the <code>Fee</code> for multi-signed transactions is significantly higher than for regularly-signed transactions. It should be (N+1) times the normal <a href="concept-transaction-cost.html">transaction cost</a>, where N is the number of signatures you plan to provide. Given that it sometimes takes a while to collect signatures from multiple sources, you may want to include additional buffer in case the <a href="concept-transaction-cost.html#load-scaling">transaction cost's load scaling</a> increases in that time.</p> <p>Keep in mind that the <code>Fee</code> for multi-signed transactions is significantly higher than for regularly-signed transactions. It should be (N+1) times the normal <a href="concept-transaction-cost.html">transaction cost</a>, where N is the number of signatures you plan to provide. Given that it sometimes takes a while to collect signatures from multiple sources, you may want to include additional buffer in case the <a href="concept-transaction-cost.html#load-scaling">transaction cost's load scaling</a> increases in that time.</p>