Ledger Proofs

Introduction

The FinP2P network enables secure, decentralized financial transactions across disparate ledgers. Central to this is the
Execution Plan (EP), a mechanism for orchestrating multi-step operations. This document details additional capabilities
in the EP trust model for allowing ledger adapters to provide proofs of execution of EP instructions.

Ledger Operation Proofs

Ledger operations produce a Receipt, which serves as a guarantee from the underlying platform of a transaction that
occurred in its ledger. This receipt is provided by an adapter to the router to record transactions, either as part of
the EP instruction completion proposal phase or individually to record transactions that were triggered externally to
FinP2P and affect the investors' accounts.

To further enhance this guarantee, an additional proof can be attached to the Receipt. By supplying the proof as part of
the Receipt, the organization adds another layer of trust and a guarantee that a transaction was recorded in the ledger.
This can increase the trust of other parties that may not have direct access to the ledger.

Organizations can provide segregated private keys for ledger proofs. The information about the key used for verification
is provided by the FinP2P asset policy, which provides general information about the tokenization platform capabilities
and is published as metadata on specific asset profiles. These keys can be further pinned by other parties to reduce the
need to trust external configuration dependencies.

Overview of Proof Types

  • Receipt Signature Based:
    • Ledgers may apply a digital signing algorithm encoding the Receipt transaction information into a digest, which
      will be signed by a private key owned exclusively by the organization controlling the ledger or tokenization
      platform. The additional signature-based proof adds another layer of reliability for the guarantee provided by the
      organization that the Receipt transaction was indeed recorded in its ledger, and the data was not tampered with.
    • The proof is a digital signature applied to the Receipt data and signed by a private key owned by the controlling
      organization of the tokenization platform, the structure of the message to be signed can follow the below EIP712
      structure of Hashlist structure.
    • The key may differ from the Organization's Router private key, which is associated with the public key identity of
      the organization in the FinP2P network (AKA FinID).

Other proof types to be added soon:

  • Zero Knowledge:
    • For ledgers and tokenization platforms that support Zero Knowledge Proofs (ZKP), the ledger may provide a ZKP for
      the existence of the transaction within a blockchain block and that the network has reached consensus about the
      block.
  • Ledger Specific Proofs:
    • Ledgers may supply proof of a transaction being recorded on a ledger by providing ledger-specific cryptographic
      proofs of this action. This may require additional key pre-setup to ensure other parties can validate the proof.

Receipt Signature Based proof

Currently, two signature formats are supported for the Receipt Signature Based proof:

  • Hashlist
  • EIP712

Generating a Signature

  • Constructing the Values:

    Gather the necessary values based on the message fields specifications below. These values typically include essential details that need to be signed.

  • Hashing:

    Given the signature format, structure and generate a hash using the configured cryptographic hashing algorithm. The current implementation supports sha3-256, keccak-256, and blake2b. This hash represents a fixed-size, unique fingerprint of the original data.

  • Signing the Hash:

    The signature is produced using the ECDSA (Elliptic Curve Digital Signature Algorithm) over the secp256k1 curve.
    Use the private secp256k1 key ,to sign the hash. This cryptographic signature ensures that the hash (and thus the original data) has not been tampered with and verifies the identity of the signer.
    The signature format is represented as the Ethereum 65-byte ECDSA signature format (r, s and v).

EIP712 Proof Structure

The EIP712 Proof Structure provides a standardized approach for signing structured data. This format ensures compatibility across wallets and dApps, offering enhanced security and flexibility for off-chain data signatures.
EIP712 enables users to sign structured data, which is then hashed and verified by smart contracts. By separating the hashing and signing processes, EIP712 prevents phishing attacks that target the ambiguity of raw message signatures.

Type Definitions

The following table describes the EIP-712 type definitions:

OrderType NameField NameField TypeDescription
1EIP712DomainnamestringName of the signing domain
2versionstringVersion of the signing domain
3chainIduint256Chain ID where the contract is deployed
4verifyingContractaddressAddress of the verifying contract
5AssetidstringAsset identifier
6AssetledgerInfoLedgerIdentifierLedger-specific asset identifier
7LedgerIdentifiertypestringLedger identifier type
8LedgerIdentifieridstringLedger identifier value
9SourceaccountIdstringSource account identifier
10SourceaccountTypestringType of the source account
11SourceassetAssetSource asset
12DestinationaccountIdstringDestination account identifier
13DestinationaccountTypestringType of the destination account
14DestinationassetAssetDestination asset
15TransactionDetailsoperationIdstringOperation ID of the transaction
16TransactionDetailstransactionIdstringTransaction ID on the ledger
17ExecutionContextexecutionPlanIdstringExecution plan ID
18ExecutionContextinstructionSequenceNumberstringInstruction sequence number
19TradeDetailsexecutionContextExecutionContextExecution plan context
20ReceiptidstringID of the receipt
21ReceiptoperationTypestringThe operation type (e.g., hold, release)
22ReceiptsourceSourceThe source account
23ReceiptdestinationDestinationThe destination account
24ReceipttradeDetailsTradeDetailsThe trade details relating to the EP
25ReceipttransactionDetailsTransactionDetailsThe transaction details
26ReceiptquantitystringTransaction quantity

Message Fields

The following table describes the fields included in the EIP-712 message:

Field NameField TypeDescription
idstringID of the receipt
operationTypestringThe operation type (e.g., hold, release)
source.accountIdstringSource account identifier
source.accountTypestringType of the source account
source.asset.idstringSource asset identifier
source.asset.ledgerInfo.typestringSource asset ledger identifier type
source.asset.ledgerInfo.idstringSource asset ledger identifier value
destination.accountIdstringDestination account identifier
destination.accountTypestringType of the destination account
destination.asset.idstringDestination asset identifier
destination.asset.ledgerInfo.typestringDestination asset ledger identifier type
destination.asset.ledgerInfo.idstringDestination asset ledger identifier value
tradeDetails.executionContext.executionPlanIdstringExecution plan ID
tradeDetails.executionContext.instructionSequenceNumberstringInstruction sequence number
transactionDetails.operationIdstringOperation ID of the transaction
transactionDetails.transactionIdstringTransaction ID
quantitystringTransaction quantity

ledgerInfo is derived from the receipt asset ledger identifier. At present, the implementation supports only CAIP-19 values: ledgerInfo.type is set to caip19, and ledgerInfo.id is constructed as {network}:{standard}:{tokenId}. The field is modeled as a generic ledger identifier because additional identifier standards may be supported in the future.

Signature and Hashing Process

The signature is produced using the ECDSA (Elliptic Curve Digital Signature Algorithm) over the secp256k1 curve.
The signature format is represented as the Ethereum 65-byte ECDSA signature (r, s, v).

The EIP-712 signature format involves hashing typed structured data composed of a domain, types, primaryType, and message.

The following steps describe how the hash is created and signed:

  • Prepare Typed Data:

    • Define the domain, types, primaryType, and message exactly as specified for this proof type.
    • String fields are represented in UTF-8.
    • Numeric and fixed-size types follow their standard EIP-712/ABI encodings.
    • Do not stringify JSON for hashing — libraries handle encoding automatically.
  • Compute the Domain Separator:

    • domainSeparator = keccak256(encodeDomain(domain))
  • Compute the Message Struct Hash:

    • messageHash = keccak256(encodeTypeAndData(primaryType, types, message))
  • Compute the Final EIP-712 Digest:

    • EIP712Digest = keccak256( 0x1901 || domainSeparator || messageHash )
  • Sign the Final Digest:

    • The final digest is signed using the private key (secp256k1) with the ECDSA algorithm, producing (r, s, v).
Hashing Formula
# All hashes use Keccak-256

domainSeparator = keccak256( encodeDomain(domain) )
messageHash     = keccak256( encode(primaryType, types, message) )
EIP712Digest    = keccak256( 0x1901 || domainSeparator || messageHash )
Signature       = sign(sender private secp256k1 key, EIP712Digest)  # 0x<r><s><v>

Notes

  • Keep field order and types identical to the definitions used for signing; any change will alter the digest.
  • Libraries may return v as 27/28 or 0/1; both are acceptable — normalize internally if needed.
  • The hash function for EIP-712 is Keccak-256 (not SHA3-256).

Encoding and Serialization Notes

In EIP-712, the domain, types, and message are structured JSON objects that must be serialized in a deterministic way before hashing and signing.
This process is defined by the EIP-712 standard.

The serialization is handled conceptually through two functions: encodeDomain and encodeTypeAndData.


1. encodeDomain(domain)

Encodes the domain separator that defines the signing context (for example, verifying contract, chain ID, version).

Steps:

  1. Build the type definition string, e.g.

    EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)
  2. Compute its type hash:
    domainTypeHash = keccak256(typeDefinition)

  3. Encode each field in order:

    • string or byteskeccak256(UTF-8 bytes)
    • uint*, int*, address, bool → standard 32-byte ABI encoding (big-endian)
  4. Concatenate the type hash and field encodings, then hash again with Keccak-256.
    The result is the domainSeparator.


2. encodeTypeAndData(primaryType, types, message)

Encodes the structured message data to be signed.

Steps:

  1. Build the full type definition string for the primary struct and any nested structs.
    Example:

    Receipt(uint256 id,address sender,uint256 amount)
  2. Compute the type hash:
    typeHash = keccak256(typeDefinition)

  3. For each field in the message:

    • Primitives (uint, int, address, bool) → 32-byte encoding
    • string, byteskeccak256(value)
    • Nested struct → recursively call encodeTypeAndData and hash the result
  4. Concatenate typeHash and all encoded field values, then hash with Keccak-256.
    The result is the messageHash.


3. Combine and Sign
EIP712Digest = keccak256(
  0x1901 ||
  domainSeparator ||
  messageHash
)

This EIP712Digest is the exact value that is signed with the private key (secp256k1) to produce the (r, s, v) signature.

JSON Samples:

EIP712 Template
{
  "domain": {
    "chainId": 1,
    "name": "FinP2P",
    "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
    "version": "1"
  },
  "message": {
    "id": "receipt123456",
    "operationType": "hold",
    "source": {
      "accountId": "sourceAccountId",
      "accountType": "sourceAccountType",
      "asset": {
        "id": "sourceAssetId",
        "ledgerInfo": {
          "type": "caip19",
          "id": "eip155:1:erc20:0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
        }
      }
    },
    "destination": {
      "accountId": "destinationAccountId",
      "accountType": "destinationAccountType",
      "asset": {
        "id": "destinationAssetId",
        "ledgerInfo": {
          "type": "caip19",
          "id": "eip155:1:erc20:0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
        }
      }
    },
    "tradeDetails": {
      "executionContext": {
        "executionPlanId": "ep123",
        "instructionSequenceNumber": "1"
      }
    },
    "transactionDetails": {
      "operationId": "op123",
      "transactionId": "tx123"
    },
    "quantity": "20"
  },
  "primaryType": "Receipt",
  "types": {
    "EIP712Domain": [
      {
        "name": "name",
        "type": "string"
      },
      {
        "name": "version",
        "type": "string"
      },
      {
        "name": "chainId",
        "type": "uint256"
      },
      {
        "name": "verifyingContract",
        "type": "address"
      }
    ],
    "Asset": [
      {
        "name": "id",
        "type": "string"
      },
      {
        "name": "ledgerInfo",
        "type": "LedgerIdentifier"
      }
    ],
    "LedgerIdentifier": [
      {
        "name": "type",
        "type": "string"
      },
      {
        "name": "id",
        "type": "string"
      }
    ],
    "Source": [
      {
        "name": "accountId",
        "type": "string"
      },
      {
        "name": "accountType",
        "type": "string"
      },
      {
        "name": "asset",
        "type": "Asset"
      }
    ],
    "Destination": [
      {
        "name": "accountId",
        "type": "string"
      },
      {
        "name": "accountType",
        "type": "string"
      },
      {
        "name": "asset",
        "type": "Asset"
      }
    ],
    "TransactionDetails": [
      {
        "name": "operationId",
        "type": "string"
      },
      {
        "name": "transactionId",
        "type": "string"
      }
    ],
    "ExecutionContext": [
      {
        "name": "executionPlanId",
        "type": "string"
      },
      {
        "name": "instructionSequenceNumber",
        "type": "string"
      }
    ],
    "TradeDetails": [
      {
        "name": "executionContext",
        "type": "ExecutionContext"
      }
    ],
    "Receipt": [
      {
        "name": "id",
        "type": "string"
      },
      {
        "name": "operationType",
        "type": "string"
      },
      {
        "name": "source",
        "type": "Source"
      },
      {
        "name": "destination",
        "type": "Destination"
      },
      {
        "name": "tradeDetails",
        "type": "TradeDetails"
      },
      {
        "name": "transactionDetails",
        "type": "TransactionDetails"
      },
      {
        "name": "quantity",
        "type": "string"
      }
    ]
  }
}
Ledger adapter API template type
{
  "signature": {
    "hashFunc": "keccak_256",
    "signature": "56f471d1010b3cad0e1461886c2bb9c85bdb0bcc288f857bf8e877f147a3a2e97d21053fcaff61593df9cac2701c889e0e43e7872d38d8696739e3fbf6479eb2",
    "template": {
      "domain": {
        "chainId": 1,
        "name": "FinP2P",
        "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
        "version": "1"
      },
      "hash": "b55a46f6fa8188ab834de7bd4c9105b5ede3c7accc9eb320c423fce8e2d7e255",
      "message": {
        "id": "receipt123456",
        "operationType": "hold",
        "source": {
          "accountId": "sourceAccountId",
          "accountType": "sourceAccountType",
          "asset": {
            "id": "sourceAssetId",
            "ledgerInfo": {
              "type": "caip19",
              "id": "eip155:1:erc20:0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
            }
          }
        },
        "destination": {
          "accountId": "destinationAccountId",
          "accountType": "destinationAccountType",
          "asset": {
            "id": "destinationAssetId",
            "ledgerInfo": {
              "type": "caip19",
              "id": "eip155:1:erc20:0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
            }
          }
        },
        "tradeDetails": {
          "executionContext": {
            "executionPlanId": "ep123",
            "instructionSequenceNumber": "1"
          }
        },
        "transactionDetails": {
          "operationId": "op123",
          "transactionId": "tx123"
        },
        "quantity": "20"
      },
      "primaryType": "Receipt",
      "type": "EIP712",
      "types": {
        "definitions": [
          {
            "fields": [
              {
                "name": "name",
                "type": "string"
              },
              {
                "name": "version",
                "type": "string"
              },
              {
                "name": "chainId",
                "type": "uint256"
              },
              {
                "name": "verifyingContract",
                "type": "address"
              }
            ],
            "name": "EIP712Domain"
          },
          {
            "fields": [
              {
                "name": "id",
                "type": "string"
              },
              {
                "name": "ledgerInfo",
                "type": "LedgerIdentifier"
              }
            ],
            "name": "Asset"
          },
          {
            "fields": [
              {
                "name": "type",
                "type": "string"
              },
              {
                "name": "id",
                "type": "string"
              }
            ],
            "name": "LedgerIdentifier"
          },
          {
            "fields": [
              {
                "name": "accountId",
                "type": "string"
              },
              {
                "name": "accountType",
                "type": "string"
              },
              {
                "name": "asset",
                "type": "Asset"
              }
            ],
            "name": "Source"
          },
          {
            "fields": [
              {
                "name": "accountId",
                "type": "string"
              },
              {
                "name": "accountType",
                "type": "string"
              },
              {
                "name": "asset",
                "type": "Asset"
              }
            ],
            "name": "Destination"
          },
          {
            "fields": [
              {
                "name": "operationId",
                "type": "string"
              },
              {
                "name": "transactionId",
                "type": "string"
              }
            ],
            "name": "TransactionDetails"
          },
          {
            "fields": [
              {
                "name": "executionPlanId",
                "type": "string"
              },
              {
                "name": "instructionSequenceNumber",
                "type": "string"
              }
            ],
            "name": "ExecutionContext"
          },
          {
            "fields": [
              {
                "name": "executionContext",
                "type": "ExecutionContext"
              }
            ],
            "name": "TradeDetails"
          },
          {
            "fields": [
              {
                "name": "id",
                "type": "string"
              },
              {
                "name": "operationType",
                "type": "string"
              },
              {
                "name": "source",
                "type": "Source"
              },
              {
                "name": "destination",
                "type": "Destination"
              },
              {
                "name": "tradeDetails",
                "type": "TradeDetails"
              },
              {
                "name": "transactionDetails",
                "type": "TransactionDetails"
              },
              {
                "name": "quantity",
                "type": "string"
              }
            ],
            "name": "Receipt"
          }
        ]
      }
    }
  },
  "type": "signatureProofPolicy"
}

Hashlist Proof Structure

The Hashlist signature format involves creating an array of hashes, where each hash represents a group of values. This structure is commonly used for operations involving multiple data points that need to be signed together. The benefit of this format is that during validation, one can construct only the necessary hash group from the original values while taking the other groups as is. This is somewhat analogous to a simplified Merkle tree, allowing for more efficient validation processes.

Field Definitions

The following table describes the fields included in the HashList message in the order they should be hashed:

Field NameField TypeDescription
idstringID of the receipt
operationTypestringThe operation type (e.g., hold, release)
transactionOperationIdstringOperation ID of the transaction
srcAssetIdstringSource asset identifier
srcAssetLedgerInfoTypestringSource asset ledger identifier type
srcAssetLedgerInfoIdstringSource asset ledger identifier value
srcAccountstringSource account identifier
srcAccountTypestringType of the source account
dstAssetIdstringDestination asset identifier
dstAssetLedgerInfoTypestringDestination asset ledger identifier type
dstAssetLedgerInfoIdstringDestination asset ledger identifier value
destAccountstringDestination account identifier
dstAccountTypestringType of the destination account
transactionIdstringTransaction ID
amountstringTransaction amount
execPlanIdstringExecution plan ID
instructionSeqstringInstruction sequence number

For the asset ledger identifier fields, the current implementation supports only CAIP-19 values. In that case, srcAssetLedgerInfoType and dstAssetLedgerInfoType are set to caip19, while srcAssetLedgerInfoId and dstAssetLedgerInfoId are constructed as {network}:{standard}:{tokenId}. The separate type and id fields are retained so additional ledger identifier standards can be added later without changing the proof structure.

Signature and Hashing Process

The signature is produced using the ECDSA (Elliptic Curve Digital Signature Algorithm) over the secp256k1 curve:
The signature format is represented as the Ethereum 65-byte ECDSA signature format (r, s and v).

The Hashlist signature format involves creating an array of hashes, where each hash represents a group of values.

The following steps describe how the hash is created and signed:

  • Hash The Fields to produce the hash group(HG):
    • Each group of fields as defined in the Field Definitions above is concatenated in its bytes representation and specified order and hashed using the hash algorithm.
    • Use the configured cryptographic hashing algorithm. The current implementation supports sha3-256, keccak-256, and blake2b. This hash represents a fixed-size, unique fingerprint of the original data.
    • The string fields included in the hash are represented in UTF-8
  • Concatenate Group Hashes:
    • Combine the hashes of all field groups, in this case the single hash created in previous step, to create the Hashlist hash.
  • Sign the Final Hash:
    • The final hash is signed using the private key (secp256k1) with the ECDSA algorithm.

Hashing Formula:

HG = hash(configured hash function, [fields by order]);
hashList = hash(configured hash function, [concat(hg1,hg2,hgN...)]);
Signature = sign(sender private secp256k1 key, hashList)
HashList Template
{
  "signature": {
    "hashFunc": "keccak_256",
    "signature": "b5b209250e143b3159146dde1ea7da276e86b8a00bd3e60eb4b69e18e7185dfc7847d566dcede2c9c0b9d9ef2a5e903241b85cf801a62739eb47dffe08a75f44",
    "template": {
      "hash": "9f7a82d3a6176eed70ecd730ce74d5eaa78f395be005a612ca9023d44067ef84",
      "hashGroups": [
        {
          "fields": [
            {
              "name": "id",
              "type": "string",
              "value": "0ef24ef3-15ce-4241-ae15-a7802a4aac06"
            },
            {
              "name": "operationType",
              "type": "string",
              "value": "issue"
            },
            {
              "name": "transactionOperationId",
              "type": "string",
              "value": ""
            },
            {
              "name": "srcAssetId",
              "type": "string",
              "value": "bank-us:102:2fc13e4d-ad15-46af-976f-f3d4018aa266"
            },
            {
              "name": "srcAssetLedgerInfoType",
              "type": "string",
              "value": "caip19"
            },
            {
              "name": "srcAssetLedgerInfoId",
              "type": "string",
              "value": "eip155:1:erc20:0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
            },
            {
              "name": "srcAccount",
              "type": "string",
              "value": ""
            },
            {
              "name": "srcAccountType",
              "type": "string",
              "value": ""
            },
            {
              "name": "dstAssetId",
              "type": "string",
              "value": "bank-us:102:2fc13e4d-ad15-46af-976f-f3d4018aa266"
            },
            {
              "name": "dstAssetLedgerInfoType",
              "type": "string",
              "value": "caip19"
            },
            {
              "name": "dstAssetLedgerInfoId",
              "type": "string",
              "value": "eip155:1:erc20:0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
            },
            {
              "name": "destAccount",
              "type": "string",
              "value": "0207faa9a69dcb332c7ab1a53ac47e1061c0a1045a398dd05ebba34bede787e40b"
            },
            {
              "name": "dstAccountType",
              "type": "string",
              "value": "finId"
            },
            {
              "name": "transactionId",
              "type": "string",
              "value": "0ef24ef3-15ce-4241-ae15-a7802a4aac06"
            },
            {
              "name": "amount",
              "type": "string",
              "value": "50"
            },
            {
              "name": "execPlanId",
              "type": "string",
              "value": "bank-us:106:80178f6a-0a2c-4aa9-b939-35c40c888a48"
            },
            {
              "name": "instructionSeq",
              "type": "string",
              "value": "2"
            }
          ],
          "hash": "d97eccf9bcd2c0e2bf35b512fe4c380c0a34bb8aa0126c41659445d09d4115e7"
        }
      ],
      "type": "hashList"
    }
  },
  "type": "signatureProofPolicy"
}