Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

Latest commit

 

History

History
468 lines (380 loc) · 33.4 KB

lip-0043.md

File metadata and controls

468 lines (380 loc) · 33.4 KB
LIP: 0043
Title: Introduce chain registration mechanism
Author: Iker Alustiza <iker@lightcurve.io>
Discussions-To: https://research.lisk.com/t/chain-registration
Status: Draft
Type: Standards Track
Created: 2021-05-22
Updated: 2021-12-01
Requires: 0038, 0045, 0049, 0056

Abstract

This LIP introduces the concept of chain registration in the Lisk ecosystem. The chain registration is a necessary step to make a sidechain interoperable with the Lisk mainchain. Specifically, for the Lisk mainchain, this LIP specifies a new command for the sidechain registration. This command creates a sidechain account in the Lisk mainchain with some specific properties given by the user submitting the transaction. Similarly for sidechains, this LIP specifies the mainchain registration command.

Copyright

This LIP is licensed under the Creative Commons Zero 1.0 Universal.

Motivation

The Lisk ecosystem is a permissionless network of blockchains where a sidechain following the standard protocol can interoperate with any other Lisk compatible chain. In particular, the Lisk mainchain serves as a central node or relayer for the entire ecosystem and every cross-chain interaction has to be sent through it. This implies there should exist a standardized protocol for the Lisk mainchain to maintain a cross-chain channel to communicate with every sidechain.

The first step for establishing this cross-chain channel protocol is the chain registration process, which can be thought of as the creation/opening of the channel between two chains. This process defines the data structures, and protocol rules that every chain needs to implement in the ecosystem if they want to interoperate with another specific chain.

For a general picture of the Lisk interoperability architecture, please refer to LIP 0045.

Rationale

In this LIP, the registration process introduced in the previous section is specified from the point of view of both the Lisk mainchain and sidechains. For the Lisk mainchain, this is done by the sidechain registration command, whereas for sidechains, it is done by the mainchain registration command.

Sidechain Registration Process

As mentioned above, for a sidechain to be interoperable in the Lisk ecosystem, it has to be registered in the Lisk Mainchain via a sidechain registration command. A transaction with this command can be sent by any user account in the Lisk Mainchain with enough funds to pay the required fee. The processing of this command implies the creation of a sidechain account in the mainchain state associated with a unique network identifier and a name. This means that every new sidechain occupies a certain namespace in the ecosystem. Additionally, every newly registered sidechain can increase the size of every cross-chain update command posted on the mainchain (due to the increasing size of the outboxRootWitness property of the command). For these two reasons, the minimum fee for this command has an added constant similar to the extra fee in a delegate registration command. The value of this extra registration fee is 10 LSK.

Once the sidechain registration command is processed, the sidechain account status is set to registered. In this state, the cross-chain channel is still not active, so the users on the mainchain or other chains cannot send cross-chain messages (CCMs) to this sidechain yet. Moreover, the liveness requirement to maintain the channel is not enforced, this means that there is no specific time requirement for a sidechain to be activated on the mainchain, it can stay in the registered status for any period of time. When a first valid cross-chain update command from this sidechain is processed, the sidechain status is changed to active, making it active in the ecosystem. Now it is possible to send CCMs to the sidechain and the liveness condition is enforced.

Sidechain Structures on Mainchain

When a new sidechain is registered on the mainchain via a sidechain registration command, it is assigned a chain ID, a unique integer that identifies the sidechain in the ecosystem. Furthermore, new data structures are inserted for the sidechain in the Lisk mainchain state. Specifically, a new entry is created in six different substores of the interoperability module store (see Figure 1): outbox root substore, chain data substore, channel data substore, chain validators substore, registered names, and registered network IDs. The values of these entries are initialized as specified in LIP 0045.

interoperability_store

Figure 1: A summary of the Interoperability module store: Each box represents a substore, where we indicate the storeKey --> storeValue relation. For the Lisk mainchain, the 'own chain' substore exists by default in the state whereas there is one entry per registered sidechain for six other substores (outbox root, chain data, channel data, chain validators, registered names, and registered network IDs) created by a sidechain registration command. For sidechains, the 'own chain' and one entry for the mainchain account for four other substores (outbox root, chain data, channel data, and chain validators) are created by the mainchain registration command.

Sidechain Registration Command

The sidechain registration command contains the following parameters used to connect a new sidechain in the ecosystem.

name

The name property sets the name of the sidechain as a string of characters. It has to be unique in the ecosystem.

genesisBlockId

The ID of the genesis block (as defined in LIP 0034). It is computed from the SHA-256 digest of the serialized bytes of the sidechain genesis block. It can also help future sidechain node operators to identify the sidechain genesis block with respect to its value.

initValidators

This property defines the set of eligible BLS public keys with their respective BFT weights required to sign the first certificate from the sidechain.

certificateThreshold

The certificateThreshold property is an integer setting the required cumulative weight needed for a certificate signature from the sidechain to be valid.

Chain ID

The chain ID uniquely identifies a chain in the Lisk ecosystem. It serves a similar purpose for chains as addresses do for user accounts. The chain ID for a sidechain is deterministically computed when processing the sidechain registration command. Specifically, the chain ID of a new sidechain is assigned as an incremental integer similar to transaction nonces. For example, if there are 41 chains already registered in the Lisk ecosystem (the mainchain and 40 sidechains), the next registered sidechain will have chainID = 42. The chain ID has to be stated in every cross-chain interaction. For example, it has to be specified in the receivingChainID property of a CCM to this sidechain and in the sendingChainId property of a cross-chain update command from this sidechain.

The format of chain IDs aims to provide an efficient and compact way to uniquely identify chains in the ecosystem. This has another advantage in terms of usability: Users can easily remember the integer assigned as chain ID for their favorite blockchain applications.

Sidechain Network Identifier

The network identifier, or network ID, is a byte sequence unique to a chain that has to be prepended to the input of the signing function of every transaction, block, or message of the chain. It is necessary to avoid transaction replays between different chains in the ecosystem.

In the Lisk ecosystem, the network ID for a sidechain is computed as the hash digest of the sidechain genesis block ID given in a transaction with the registration command and the address of the account sending this transaction. This is convenient for two reasons:

  1. The sidechain developers can pre-compute the network ID of their sidechain as soon as they set the genesis block and an account to send the transaction on the mainchain. The sidechain can be started already from this moment without being registered on the mainchain.
  2. The network ID is known to the mainchain as soon as the sidechain is registered, thus it can validate cross-chain update commands coming from the sidechain without further context.

This LIP overwrites the network ID definition given in LIP 0009 and solves the open problem given in the Rationale section of that LIP. Note that in the case of a sidechain undergoing a community hard fork, one of the competing forks will need to register their fork of the sidechain again on the Lisk Mainchain. Regardless of the genesis block ID set by this sidechain fork, it has to be registered from a different user account which implies that the chain ID and network ID of the forked chain will be different from the original one.

Mainchain Registration on a Sidechain

Once the sidechain has been registered on the mainchain, a similar registration process should happen in the sidechain before the interoperable channel is opened between the two chains. This is done by submitting a transaction with the mainchain registration command in the sidechain, which implies the creation of a mainchain account in the sidechain state associated with the Lisk mainchain and other structures needed for interoperability. This mainchain account has a similar structure as the one depicted in Figure 1. By protocol, the chain ID of the mainchain is a constant equal to 1 in the ecosystem. The network ID of the mainchain is also a constant since it has to be known to validate cross-chain update commands.

This registration process has to happen always after the sidechain registration on the mainchain since the sidechain has no prior knowledge of the current mainchain validators or its own chain ID and name. Similar to the sidechain registration case, the mainchain account status will not change to active until a valid cross-chain update command from the mainchain containing a valid registration CCM is processed.

Mainchain Registration Command

The mainchain registration command sets certain parameters in the sidechain related to the interoperability module and initializes the corresponding mainchain data structures. This command requires the approval of the sidechain validators. They have to agree on the content of this command and add their aggregated signatures accordingly. It is important that the sidechain validators make sure that they are signing the registration command with the right information from the mainchain, otherwise, the sidechain interoperable functionality may be unusable.

This command has no requirement for a minimum fee since it should be submitted only once in a sidechain and approved by a majority of validators. For this reason, a transaction with this command should be treated differently in terms of priority in case it is included in a sidechain node’s transaction pool. The recommendation is that, once the transaction is properly signed by the validators and ready to be submitted, a validator simply includes it in its next forged block without including it in the transaction pool. The command has the following parameters:

mainchainValidators

Similar to the initValidators property in the sidechain registration command, it defines the set of mainchain validators with their respective BFT weight expected to sign the first certificate from the mainchain.

ownChainID

The chain ID assigned to this sidechain on the mainchain after processing the corresponding sidechain registration command.

ownName

The ownName property sets the name of the sidechain in its own state according to the name given in the mainchain.

Specification

Constants and Notation

Name Type Value Description
Interoperability Constants
MODULE_ID_INTEROPERABILITY uint32 64 ID of the interoperability module.
MAINCHAIN_ID bytes 1 Chain ID of the Lisk mainchain.
MAINCHAIN_NAME string "lisk-mainchain" Name of the Lisk mainchain.
MAINCHAIN_NETWORK_ID bytes TBD Network identifier of the Lisk mainchain.
Interoperability Store
STORE_PREFIX_OUTBOX_ROOT bytes 0x0000 Store prefix of the outbox root substore.
STORE_PREFIX_CHAIN_DATA bytes 0x8000 Store prefix of the chain data substore.
STORE_PREFIX_CHANNEL_DATA bytes 0xa000 Store prefix of the channel data substore.
STORE_PREFIX_CHAIN_VALIDATORS bytes 0xb000 Store prefix of the chain validators substore.
STORE_PREFIX_TERMINATED_STATE bytes 0xc000 Store prefix of the terminated state substore.
STORE_PREFIX_TERMINATED_OUTBOX bytes 0xd000 Store prefix of the terminated outbox substore.
STORE_PREFIX_REGISTERED_NAMES bytes 0xe000 Store prefix of the chain names substore.
STORE_PREFIX_REGISTERED_NETWORK_IDS bytes 0xf000 Store prefix of the chain network IDs substore.
Interoperability Command IDs
COMMAND_ID_SIDECHAIN_REG uint32 0 Command ID of sidechain registration command.
COMMAND_ID_MAINCHAIN_REG uint32 1 Command ID of mainchain registration command.
CROSS_CHAIN_COMMAND_ID_REGISTRATION uint32 0 Cross-chain command ID of chain registration CCM.
General Constants
REGISTRATION_FEE uint64 1000000000 Fee to pay for a sidechain registration command.
MAX_NUM_VALIDATORS uint32 199 Maximum number of validators in a sidechain.
MAX_LENGTH_NAME uint32 40 Maximum allowed name for a sidechain.
MAX_UINT32 uint32 4294967295 Maximum value for a 32-bit unsigned integer.
MAX_UINT64 uint64 18446744073709551615 Maximum value for a 64-bit unsigned integer.
THRESHOLD_MAINCHAIN uint32 68 The certificate threshold used for Lisk mainchain.
TAG_CHAIN_REG_MESSAGE bytes ASCII encoded string “LSK_CHAIN_REGISTRATION_”. Message tag for chain registration message

Functions from Other Modules

Calling a function fct from a module module is represented by module.fct(required inputs). In this LIP, functions from the Validators module and the BFT module are used.

Sidechain Registration Command

The command ID of this transaction is COMMAND_ID_SIDECHAIN_REG.

Fee

This command has an extra fee:

extraCommandFee(MODULE_ID_INTEROPERABILITY, COMMAND_ID_SIDECHAIN_REG) = REGISTRATION_FEE

where REGISTRATION_FEE is a constant given in the protocol.

Parameters

sidechainRegParams = {
    "type": "object",
    "required": [
        "name",
        "genesisBlockID",
        "initValidators",
        "certificateThreshold"
    ],
    "properties": {
        "name": {
            "dataType": "string",
            "fieldNumber": 1
        },
        "genesisBlockID": {
            "dataType": "bytes",
            "fieldNumber": 2
        },
        "initValidators": {
            "type": "array",
            "fieldNumber": 3,
            "items": {
                "type": "object",
                "required": ["blsKey", "bftWeight"],
                "properties": {
                    "blsKey": {
                        "dataType": "bytes",
                        "fieldNumber": 1
                    },
                    "bftWeight": {
                        "dataType": "uint64",
                        "fieldNumber": 2
                    }
                }
            }
        },
        "certificateThreshold": {
            "dataType": "uint64",
            "fieldNumber": 4
        }
    }
}

Verification

Let trs be a transaction with module ID MODULE_ID_INTEROPERABILITY and command ID COMMAND_ID_SIDECHAIN_REG to be verified. Then the set of validity rules to validate trs.params are:

  • The trs.params.name property has to contain only characters from the set [a-z0-9!@$&_.] and has to be at most MAX_LENGTH_NAME characters long.
  • The trs.params.name property has to be unique with respect to the set of already registered sidechain names in the blockchain state, such that the entry with store prefix equal to STORE_PREFIX_REGISTERED_NAMES and store key equal to trs.params.name (serialized as a utf-8 encoded string) does not exist in the store.
  • Let netId be the hash digest of SHA-256(trs.params.genesisBlockId || senderAddress) where || indicates bytes concatenation and senderAddress is the address of the user account corresponding to the trs.senderPublicKey property:
    • Then netId has to be unique with respect to the set of already registered sidechain network IDs in the blockchain state. That is the entry with store prefix equal to STORE_PREFIX_NETWORK_ID and store key equal to trs.params.netID does not exist in the store.
  • Rules for trs.params.initValidators array:
    • The array must have at least 1 element and at most MAX_NUM_VALIDATORS elements.
    • The array must be ordered lexicographically by blsKey property.
    • The blsKey property of each element has a length of 48 bytes.
    • The blsKey property of each element is unique within the array.
    • The bftWeight property of each element is a positive integer.
    • Let totalWeight be the sum of the bftWeight property of every element in the trs.params.initValidators array. Then totalWeight has to be less than or equal to MAX_UINT64.
  • The range of valid values of the trs.params.certificateThreshold property is given by the total sum of the validators weights:
    • Minimum value: floor(1/3 * totalWeight) + 1.
    • Maximum value: totalWeight.

Execution

When a sidechain registration command is executed, new entries for the Interoperability module are created in the mainchain state (see Figure 1). In particular, let trs be a transaction with module ID MODULE_ID_INTEROPERABILITY and command ID COMMAND_ID_SIDECHAIN_REG to be executed. Then:

  • The chain ID of the sidechain is assigned as chainID = currentHighestChainID + 1, where currentHighestChainID is the highest chain ID among all registered chains on the Lisk mainchain (included).

  • An entry in the chain data substore as specified in LIP 0045 is created as:

    • storePrefix: STORE_PREFIX_CHAIN_DATA
    • storeKey: uint32be(chainID).
    • storeValue: sidechainAccount where sidechainAccount is the chain account object, serialized according to the chainAccountSchema schema as defined in LIP 0045, and initialized as:
      • The sidechainAccount.name property is equal to the trs.params.name property.
      • The sidechainAccount.networkID property is calculated as the output of SHA-256(trs.params.genesisBlockId || senderAddress) where || indicates bytes concatenation and senderAddress is the address of the user account corresponding to the trs.senderPublicKey property.
      • The sidechainAccount.lastCertificate.validatorsHash property is set to bft.computeValidatorsHash(trs.params.initValidators, trs.params.certificateThreshold).
      • The rest of the properties are initialized to their default values.
  • An entry in the channel data substore as specified in LIP 0045 is created as:

    • storePrefix: STORE_PREFIX_CHANNEL_DATA

    • storeKey: uint32be(chainID).

    • storeValue: sidechainChannel where sidechainChannel is the channel object, serialized according to the channelSchema schema as defined in LIP 0045, and initialized as:

      • All properties are initialized to their default values.
      • A registration CCM is sent to the sidechain by calling sendInternal(EMPTY_FEE_ADDRESS, MODULE_ID_INTEROPERABILITY, CROSS_CHAIN_COMMAND_ID_REGISTRATION, chainID, 0, CCM_STATUS_OK, registrationCCMParams), where
      registrationCCMParams = {
          "networkID": SHA-256(trs.params.genesisBlockId || senderAddress),
          "name": trs.params.name,
          "messageFeeTokenID": sidechainChannel.messageFeeTokenID
      }
  • An entry in the chain validators substore as specified in LIP 0045 is created as:

    • storePrefix: STORE_PREFIX_CHAIN_VALIDATORS
    • storeKey: uint32be(chainID).
    • storeValue: sidechainValidators where sidechainValidators is the validators object, serialized according to the validatorsSchema schema as defined in LIP 0045, and initialized as:
      • The sidechainValidators.activeValidators property is equal to the trs.params.initValidators property.
      • The sidechainValidators.certificateThreshold property is equal to the trs.params.certificateThreshold property.
  • An entry in the outbox root substore as specified in LIP 0045 is created as:

    • storePrefix: STORE_PREFIX_OUTBOX_ROOT.
    • storeKey: uint32be(chainID).
    • storeValue: The property sidechainChannel.outbox.root serialized according to the outboxRootSchema schema defined in Introduce Interoperability module LIP.
  • An entry in the registered names substore as specified in LIP 0045 is created as:

    • storePrefix: STORE_PREFIX_REGISTERED_NAMES.
    • storeKey: sidechainAccount.name serialized as a utf-8 encoded string.
    • storeValue: chainID serialized according to chainIDSchema schema defined in Introduce Interoperability module LIP.
  • An entry in the registered network IDs substore as specified in LIP 0045 is created as:

    • storePrefix: STORE_PREFIX_NETWORK_ID.
    • storeKey: sidechainAccount.networkID serialized as bytes.
    • storeValue: chainID serialized according to chainIDSchema schema as defined in LIP 0045.

Mainchain Registration Command

The command ID is COMMAND_ID_MAINCHAIN_REG.

Fee

This command does not have an extra fee, i.e., extra fee = 0.

Parameters

mainchainRegParams = {
    "type": "object",
    "required": [
        "ownChainID",
        "ownName",
        "mainchainValidators",
        "signature",
        "aggregationBits"
    ],
    "properties": {
        "ownChainID": {
            "dataType": "uint32",
            "fieldNumber": 1
        },
        "ownName": {
            "dataType": "string",
            "fieldNumber": 2
        },
        "mainchainValidators": {
            "type": "array",
            "fieldNumber": 3,
            "items": {
                "type": "object",
                "required": ["blsKey", "bftWeight"],
                "properties": {
                    "blsKey": {
                        "dataType": "bytes",
                        "fieldNumber": 1
                    },
                    "bftWeight": {
                        "dataType": "uint64",
                        "fieldNumber": 2
                    }
                }
            }
        },
        "signature": {
            "dataType": "bytes",
            "fieldNumber": 4
        },
        "aggregationBits": {
            "dataType": "bytes",
            "fieldNumber": 5
        }
    }
}

Verification

Let trs be a transaction with module ID MODULE_ID_INTEROPERABILITY and command ID COMMAND_ID_MAINCHAIN_REG to be verified. The set of validity rules to validate trs.params are:

  • The trs.params.ownChainID property has to be an integer less than or equal to MAX_UINT32.
  • The trs.params.ownName property has to contain only characters from the set [a-z0-9!@$&_.] and has to be at most MAX_LENGTH_NAME characters long.
  • Rules for trs.params.mainchainValidators array:
    • The array must contain 101 elements (exact number of active delegates on the Lisk mainchain).
    • The array must be ordered lexicographically by blsKey property.
    • The blsKey property of each element has a length of 48 bytes.
    • The blsKey property of each element is unique within the array.
    • The bftWeight property of each element is equal to 1.
  • The properties trs.params.aggregationBits and trs.params.signature are validated as follows:
    • The function verifyWeightedAggSig(keyList, tag, netID, aggregationBits, signature, weights, certificateThreshold, message) specified in LIP 0038, must return VALID where:

      • Let b be the current block and validators be an array of objects equal to bftModule.getBFTParameters(b.header.height).validators, then:
        • keyList = [ validatorsModule.getValidatorAccount(validator.address).blsKey for validator in validators ] sorted lexicographically.
        • weights = [ validator.bftWeight for validator in validators ] sorted according keyList.
      • The tag is equal to TAG_CHAIN_REG_MESSAGE.
      • The netID byte array corresponds to the network ID of the chain.
      • The aggregationBits argument is the byte array given in trs.params.aggregationBits.
      • The signature argument is the aggregate signature given in trs.params.signature.
      • The certificateThreshold argument is equal to getBFTParameters(b.header.height).certificateThreshold where b is the current block.
      • The message argument is the output bytes of the serialization, as specified in LIP 0027, of trs.params.ownChainID, trs.params.ownName and trs.params.mainchainValidators properties according to the following schema:
      registrationSignatureMessage = {  
          "type": "object",
          "required": ["ownChainID", "ownName", "mainchainValidators"],
          "properties": {
              "ownChainID": {
                  "dataType": "uint32",
                  "fieldNumber": 1
              },
              "ownName": {
                  "dataType": "string",
                  "fieldNumber": 2
              },
              "mainchainValidators": {
                  "type": "array",
                  "fieldNumber": 3,
                  "items": {
                      "type": "object",
                      "required": ["blsKey", "bftWeight"],
                      "properties": {
                          "blsKey": {
                              "dataType": "bytes",
                              "fieldNumber": 1
                          },
                          "bftWeight": {
                              "dataType": "uint64",
                              "fieldNumber": 2
                          }
                      }
                  }
              }
          }
      }

Execution

When a mainchain registration command is applied, new entries of the Interoperability module are created in the sidechain state. In particular, let trs be a transaction with module ID MODULE_ID_INTEROPERABILITY and command ID COMMAND_ID_MAINCHAIN_REG to be executed. Then:

  • An entry in the chain data substore as specified in LIP 0045 is created as:

    • storePrefix: STORE_PREFIX_CHAIN_DATA.
    • storeKey: uint32be(MAINCHAIN_ID).
    • storeValue: mainchainAccount where mainchainAccount is the chain account object, serialized according to the chainAccountSchema schema as defined in LIP 0045, and initialized as:
      • mainchainAccount.name = MAINCHAIN_NAME.
      • mainchainAccount.networkID = MAINCHAIN_NETWORK_ID.
      • The mainchainAccount.lastCertificate.validatorsHash property is set to bft.computeValidatorsHash(trs.params.mainchainValidators, THRESHOLD_MAINCHAIN).
      • The rest of the properties are initialized to their default values.
  • An entry in the channel data substore as specified in LIP 0045 is created as:

    • storePrefix: STORE_PREFIX_CHANNEL_DATA

    • storeKey: uint32be(chainID).

    • storeValue: mainchainChannel where mainchainChannel is the channel object, serialized according to the channelSchema schema as defined in LIP 0045, and initialized as:

      • All properties are initialized to their default values.
      • A registration CCM is sent to the mainchain by calling sendInternal(EMPTY_FEE_ADDRESS, MODULE_ID_INTEROPERABILITY, CROSS_CHAIN_COMMAND_ID_REGISTRATION, MAINCHAIN_ID, 0, CCM_STATUS_OK, registrationCCMParams), where
      registrationCCMParams = {
          "networkID": MAINCHAIN_NETWORK_ID,
          "name": MAINCHAIN_NAME,
          "messageFeeTokenID": mainchainChannel.messageFeeTokenID
      }
  • An entry in the chain validators substore as specified in LIP 0045 is created as:

    • storePrefix: STORE_PREFIX_CHAIN_VALIDATORS
    • storeKey: uint32be(MAINCHAIN_ID).
    • storeValue: mainchainValidators where mainchainValidators is the validators object, serialized according to the validatorsSchema schema as defined in LIP 0045, and initialized as:
      • The mainchainValidators.activeValidators property is equal to the trs.params.mainchainValidators property.
      • The mainchainValidators.certificateThreshold property is equal to the THRESHOLD_MAINCHAIN.
  • An entry in the outbox root substore as specified in LIP 0045 is created as:

    • storePrefix: STORE_PREFIX_OUTBOX_ROOT.
    • storeKey: uint32be(MAINCHAIN_ID).
    • storeValue: The property mainchainChannel.outbox.root serialized according to the outboxRootSchema schema defined in Introduce Interoperability module LIP.
  • An entry in the own chain account data substore as specified in LIP 0045 is created as:

    • storePrefix : STORE_PREFIX_CHAIN_DATA
    • storeKey: uint32be(0).
    • storeValue: ownChainAccount where ownChainAccount is the own chain account object, serialized according to the ownChainAccountSchema schema defined in Introduce Interoperability module LIP, and initialized as:
      • ownChainAccount.ID = trs.params.ownChainID.
      • ownChainAccount.name = trs.params.ownName.
      • ownChainAccount.nonce = 0.

Backwards Compatibility

This proposal, together with LIP 0045, LIP 0053, LIP 0049, and LIP 0054, is part of the Interoperability module. Chains adding this module will need to do so with a hardfork.

Reference Implementation

TBA