diff --git a/fatips/0.md b/fatips/0.md index a53fc69..df0c2e8 100644 --- a/fatips/0.md +++ b/fatips/0.md @@ -208,36 +208,46 @@ entry. #### External IDs -Transaction Entries must include two External IDs for every input address listed in the transaction: the RCD -that hashes to the corresponding input address, and a signature. +Transaction Entries must include exactly two External IDs for every input +address listed in the transaction: the RCD that hashes to the corresponding +input address, and a signature. -Implementations should ignore any External IDs after the first `2n`, where `n` -is the number of input addresses with respect to Factoid address signing & verification process. This allows a user to optionally append any additional External IDs. +In the case of normal transaction, all inputs must have a corresponding RCD in +an even External ID. -In the case of coinbase transactions, the last External ID is used to hold a signature of the issuing identity as per [FATIP-101](101.md). The `idNonce` being signed shall be the transaction `chain ID` concatenated with the entry content. The `IDKey` shall be the issuing identity's IDKey at the time of entry into Factom. +In the case of coinbase transactions, the RCD in the 0th External ID must hash +to the ID Key of the issuing Identity as per [FATIP-101](101.md). + +Since the Transaction Entry Hash MUST be unique, implementations must ignore +any entries that do not contain EXACTLY twice as many External IDs as inputs. +This prevents replay of transactions through manipulating the Transaction Entry +Hash by appending additional External IDs to a previously posted valid +transaction. In the following table `X` is any even number less than `2n`. | External ID Index | External ID Value (RAW DATA) | | -------------------------- | ------------------------------------------------------------ | -| 0 | RCD corresponding to `input[0].address` | +| 0 | RCD corresponding to one of the inputs or Issuer ID Key | | 1 | ed25519 signature by key of RCD in preceding External ID | +| 2 | RCD corresponding to one of the inputs | +| 3 | ed25519 signature by key of RCD in preceding External ID | | ... | | -| `X` | RCD corresponding to `input[X/2].address` | +| `X` | RCD corresponding to one of the inputs | | `X + 1` | ed25519 signature by key of RCD in preceding External ID | -| `ExtIds[ExtID.length - 1]` | Raw binary ed25519 signature output of signing transaction `chainID` + entry content. ed25519 signature validation (`chainID`+ entry content, `idKey`, `ExtID[0]`) | - ##### Encoding All of the RCD and Signature External IDs in a Transaction Entry shall be raw -data and not use any encoding. The [Factom Explorer](https://explorer.factom.com/) now properly displays External IDs -that contain raw data as hex encoded data. So there is little reason to encode -the data we put in the External IDs. +data and not use any encoding. The [Factom +Explorer](https://explorer.factom.com/) now properly displays External IDs that +contain raw data as hex encoded data. So there is little reason to encode the +data we put in the External IDs. ##### RCD + The RCD is a data structure that hashes to the payload of a Factoid Address. The RCD data structure is 33 bytes long. The first byte is the RCD type (`0x01`), the remaining 32 bytes are a ed25519 public key. @@ -255,7 +265,8 @@ To validate that an RCD "corresponds" to a Factoid Address, ##### Signature -The signature is the same as that used in a [Factoid + +The cryptographic signature algorithm is the same as that used in a [Factoid Transaction](https://github.com/FactomProject/FactomDocs/blob/master/factomDataStructureDetails.md#factoid-transaction). From the Factom Documentation: @@ -266,31 +277,47 @@ From the Factom Documentation: > [canonical](https://github.com/FactomProject/ed25519/blob/master/ed25519.go#L143). > This will limit malleability by attackers without the private key. -The signed data shall be the concatenation of the raw bytes of the chain ID and -the content of the entry. +A signed transaction should only be valid once, so signed data must be +protected against replay within a given FAT chain and across various FAT +chains. For this reason both the index of the External ID and the chain id is +used to salt the signature. This prevents reordering the External IDs and +replaying a transaction on a different chain. -``` -signature = ed25519_sign(priv_key, chain_id + entry_content) -``` +Let `i` be the integer index of the External ID that the signature will be +place in the Factom Entry. -The chain ID is included to prevent replay attacks across FAT token chains. +The signed data shall be the concatenation of the following: +1. The shortest (no leading zeros) string decimal representation of `int(i/2)` +2. The raw bytes of the chain ID +3. The content of the entry +For example, if there were two inputs, and `+` is concatenation of bytes, then +the valid External IDs would be, +``` +ExtID[0] = 0x01 + pub_key0 +ExtID[1] = ed25519_sign(priv_key0, "0" + chain_id + entry_content) +ExtID[2] = 0x01 + pub_key1 +ExtID[3] = ed25519_sign(priv_key1, "1" + chain_id + entry_content) +``` ### Transaction Validation and State There are two types of transactions which have slightly different validation requirements: Normal transactions between Factoid Addresses, and Coinbase -transactions that mint tokens. Coinbase transactions require an additional signature of the entry content by the issuer, stored in the transaction's ExtIds. +transactions that mint tokens. Coinbase transactions require an additional +signature of the entry content by the issuer, stored in the transaction's +ExtIds. A Coinbase transaction is identified by whether it has an input from the Coinbase address: `FA1zT4aFpEvcnPqPCigB3fvGu4Q4mTXY22iiuV69DqE1pNhdF2MC` #### Computing the current state + Implementations must maintain the state of the balances of all addresses in order to evaluate the validity of a transaction. The current state can be built -by iterating through all entries in the token chain and sequentially, -updating the state for any transaction that meets all of the necessary -Transaction Validation Requirements. +by iterating through all entries in the token chain and sequentially, updating +the state for any transaction that meets all of the necessary Transaction +Validation Requirements. The following pseudo code describes how to compute the current state of all balances. A transaction must be applied entirely or not at all. Entries that @@ -312,9 +339,9 @@ for entry in transaction_chain.entries: All Transactions must meet all of the T.x requirements. -Normal Transactions must also meet all of the N.x requirements. +Normal Transactions must additionally meet all of the N.x requirements. -Coinbase Transactions must also meet all of the C.x requirements. +Coinbase Transactions must additionally meet all of the C.x requirements. In general, requirements are ordered by the computational and programmatic ease of checking. @@ -331,15 +358,15 @@ The x.3.x requirements are generally related to cryptographic validations. - T.1.2: The JSON must contain all required transaction fields listed in the above table. The fields must comply with all stated validation criteria. No unspecified fields may be present. -- T.1.3: The sum of all values(amounts) in the `inputs` object must be equal to - the sum of all values(amounts) in the `outputs` object. +- T.1.3: The entry MUST contain exactly `2n` External IDs, where `n` is the + number of keys in the `inputs` object. This is required to prevent replay +attacks. - T.1.4: A Factoid Address may only appear ONCE in the `inputs` and `outputs` objects combined. This means that the "inputs" and "outputs" may not share any common keys (Factoid Addresses), nor each have any duplicates. -- N.1.1: The entry MUST contain exactly `2n` External IDs, where `n` is the - number of keys in the `inputs` object. This is required to prevent replay -attacks. -- T.2.1: The entry hash of the transaction entry must be unique among all +- T.2.1: The sum of all values(amounts) in the `inputs` object must be equal to + the sum of all values(amounts) in the `outputs` object. +- T.2.2: The entry hash of the transaction entry must be unique among all transactions belonging to this token. - T.3.1: If `i` is odd then the `i`th External ID must be the raw data of a valid signature verified against the public key stored in the last 32 bytes @@ -363,9 +390,9 @@ All references to External ID indexes use `0`-based indexing. key in `inputs`. - N.2.2: The Factoid Addresses (keys) in `inputs` must all have balances greater than or equal to their respective input amounts. -- N.3.1: If `i` is even then the `i`th External ID must be the raw data of an - RCD which hashes to the raw data payload of the human readable Factoid -Address in `inputs[i/2].address`. +- N.3.1: For each input address, there exists an External ID with even index + which is the raw data of an RCD which hashes to the raw data payload of the +human readable Factoid Address input. ##### C.x Requirements for Coinbase distribution transactions