Cómo Cosmos IBC es heterogéneo a través de las cadenas

1. Introducción

El equipo de Datachain se compromete a desarrollar un marco y un módulo de cadena cruzada heterogéneo a través de IBC (Comunicación entre cadenas de bloques) - "YUI":

Las bases de código relevantes son:

2. Resumen de IBC

IBC es un protocolo de interoperabilidad entre diferentes libros de contabilidad. Inicialmente era un módulo central de Cosmos, que permitía que varios libros de contabilidad basados ​​o no en tendermint se comunicaran entre sí.
En teoría, cualquier cadena puede comunicarse entre sí a través de IBC.
IBC está estandarizado con el estándar Interchain (ICS):

3. Diseño de arquitectura IBC

El protocolo IBC adopta un diseño en capas y se divide principalmente en 2 capas:

  • IBC/TAO: La capa subyacente de transporte, autenticación y pedidos.
  • IBC/APP: La capa de aplicación superior basada en TAO.

La mayor parte del trabajo para implementar el protocolo IBC se centra en la capa TAO. Una vez que se ha implementado la capa TAO del libro mayor de destino, es fácil implementar diferentes protocolos de capa APP sobre la capa TAO.
inserte la descripción de la imagen aquí

3.1 Capa IBC/TAO (contrato)

La función principal de la capa IBC/TAO es retransmitir paquetes entre las dos cadenas de forma fiable, ordenada y autenticada.

  • confiable: significa que la cadena de origen solo envía un paquete, y la cadena de destino solo lo recibe una vez, y los dos no necesitan confiar en ningún tercero. De hecho, las cadenas no se comunican entre sí. Por lo tanto, se necesita un "retransmisor" para retransmitir paquetes de uno a otro, pero el retransmisor no tiene permiso, cualquiera puede ejecutar el retransmisor.
  • ordenado: significa que el orden en que el enlace de destino recibe los paquetes es el mismo orden en que el enlace de origen envía los paquetes.
  • Autenticado: significa que los paquetes de retransmisión IBC adoptan la abstracción de "canal", y cada extremo del canal se asigna especialmente a un contrato inteligente específico. Por lo tanto, si la cadena de destino recibe un paquete a través del canal, significa que el contrato inteligente específico asignado al canal en el extremo de la cadena de origen envió el paquete. Ningún otro contrato inteligente puede usar este canal para enviar paquetes.

IBC/TAO se implementa como contratos inteligentes que se ejecutan en dos cadenas de bloques conectadas entre sí a través de IBC, y estos contratos inteligentes se denominan "módulos IBC/TAO". El contrato inteligente (módulo IBC/TAO) contiene los siguientes elementos:

  • Cliente ligero en cadena: como base de IBC/TAO, puede verificar que un cierto estado existe en la cadena de la otra parte sin confiar en un tercero.
  • abstracción de conexión
  • abstracción del canal

Sobre la base del cliente ligero en cadena, se definen la abstracción de conexión y la abstracción de canal, que se utilizan para conectar contratos inteligentes en dos cadenas y retransmitir paquetes entre ellos.

3.1.1 Cliente ligero en cadena en el contrato IBC/TAO

La sintaxis y la semántica del cliente ligero en cadena en el contrato IBC/TAO se pueden encontrar en el estándar ICS-2 .

Cuando hay un nuevo encabezado de bloque en la "cadena opuesta", el repetidor consultará el encabezado del bloque y lo enviará al contrato IBC/TAO de la "cadena local". Luego, el contrato IBC/TAO ejecutará el protocolo de cliente ligero de la "cadena opuesta" para verificar si el acuerdo es válido y, si es válido, actualizará su ClientState para reflejar el estado de la "cadena opuesta".

Después de que ClientState se actualice al último encabezado de bloque de la "cadena de oponentes", el contrato IBC/TAO puede verificar si hay un estado presentado en la "cadena de oponentes".
Por ejemplo, si la "cadena opuesta" usa un árbol merkle para almacenar su estado mundial e incluye el hash raíz del árbol merkle en cada encabezado de bloque (similar a Ethereum), la prueba merkle se puede usar para probar el estado del contrato inteligente en el árbol.
inserte la descripción de la imagen aquí

3.1.2 Abstracción de conexión en contrato IBC/TAO

Para conocer la sintaxis semántica de la conexión en el contrato IBC/TAO, consulte ICS-3 .

La "conexión" en el contexto IBC se representa como un par conectado compuesto por 2 ClientStates en diferentes cadenas.
Antes de comenzar a usar los paquetes de retransmisión de "canal", el IBC/TAO en ambas cadenas debe determinar y verificar el ClientState para comunicarse con . La abstracción de conexión se utiliza para este propósito. El mecanismo de establecimiento de conexión entre cadenas es similar al protocolo de enlace de 3 vías de TCP.
inserte la descripción de la imagen aquí
Los cambios de estado del proceso de protocolo de enlace de conexión son los siguientes: [Todas las operaciones son desencadenadas por transacciones iniciadas por el repetidor]

  • 1) connOpenInit: se creará y almacenará una nueva conexión en estado INIT en la cadena de inicio.
  • 2) connOpenTry: si la cadena de contraparte verifica que la conexión en la cadena de inicio está en estado INIT, creará y almacenará una nueva conexión en estado TRYOPEN en su propia cadena.
  • 3) connOpenAck: si la cadena de inicio verifica que la conexión en la cadena opuesta está en estado TRYOPEN, actualizará el estado de la conexión en su propia cadena de INIT a OPEN.
  • 4) connOpenConfirm: si la cadena de pares verifica que el estado de conexión en la cadena de inicio se actualizó de INIT a OPEN, actualizará el estado de conexión en su propia cadena de TRYOPEN a OPEN.

inserte la descripción de la imagen aquí

3.1.3 Abstracción de canales en el contrato IBC/TAO

Para conocer la sintaxis semántica del canal en el contrato IBC/TAO, consulte ICS-4 .

La abstracción de canal en IBC se usa para representar el par de conexión de dos contratos inteligentes en diferentes cadenas.
El mecanismo de apretón de manos establecido por canal es similar al de conexión. Una vez establecido, un canal se puede utilizar para retransmitir paquetes entre cadenas.
El propio proceso de retransmisión de paquetes también utiliza un mecanismo de reconocimiento de tres vías similar a TCP.
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Los cambios de estado del proceso de protocolo de enlace del canal son los siguientes: [Todas las operaciones son desencadenadas por transacciones iniciadas por el repetidor]

  • 1) chanOpenInit: se creará un nuevo canal en estado INIT y se almacenará en la cadena de inicio.
  • 2) chanOpenTry: si la cadena de la contraparte verifica que el canal en la cadena de inicio está en estado INIT, creará y almacenará un nuevo canal en estado TRYOPEN en su propia cadena.
  • 3) chanOpenAck: si la cadena de inicio verifica que el canal en la cadena opuesta está en estado TRYOPEN, actualizará el estado del canal en su propia cadena de INIT a OPEN.
  • 4) chanOpenConfirm: si la cadena de contraparte verifica que el estado del canal en la cadena de inicio se ha actualizado de INIT a OPEN, actualizará el estado del canal en su propia cadena de TRYOPEN a OPEN.
    inserte la descripción de la imagen aquí

3.1.4 retransmisión de paquetes

Una vez que se establece el canal, los dos contratos inteligentes pueden enviar y recibir paquetes (el contenido del paquete es cualquier secuencia de bytes):

  • 1) sendPacket: crea y almacena un nuevo paquete cuyo número de secuencia es N= nextSequenceNumber en la cadena de origen . Luego nextSequenceNumber aumentará en 1. [ No activado directamente por entidades fuera de la cadena, pero activado por contratos de aplicaciones.
  • 2) recvPacket: si la cadena de destino verifica que el paquete fue efectivamente enviado (creado) en la cadena de origen, creará y almacenará un nuevo paquete con el número de secuencia N en su propia cadena. [Activado por repetidor.
  • 3) BeckPacket: borra el paquete cuyo número de secuencia es N. [Activado por repetidor.

inserte la descripción de la imagen aquí

3.2 Capa IBC/APP (contrato)

Un único y simple mecanismo de retransmisión de paquetes (IBC/TAO) admite cualquier protocolo de cadena cruzada. Los protocolos de aplicación construidos sobre IBC/TAO se conocen colectivamente como IBC/APP.
inserte la descripción de la imagen aquí
Ejemplo de transferencia de tokens entre cadenas:
ICS-20 es un ejemplo de protocolo IBC/APP, que admite la transferencia de tokens entre cadenas. Los implementadores de IBC/APP no necesitan diseñar o implementar el mecanismo de interoperabilidad de toda la cadena, solo necesitan implementar sendPackety complementos relacionados. El proceso de transferencia de tokens de la cadena A a la cadena B a través de ICS-20 es el siguiente:recvPacketacknowledgePacket

  • 1) Los átomos en la cadena A realizan las siguientes operaciones:
    • Fichas de bloqueo en el módulo ICS-20。
    • 然后执行una sendPacketoperación para un paquete que especifica la cantidad y la denominación de los tokens bloqueados。
  • 2) Los átomos en la cadena B realizan las siguientes operaciones:
    • Ejecute recvPacketla operación para el paquete.
    • 然后monedas de vales equivalentes a las fichas bloqueadas en el módulo ICS-20。
  • 3) Funciona normalmente en chainA acknowledgePacket.

inserte la descripción de la imagen aquí
El proceso de transferencia del token de la cadena B a la cadena A es el siguiente:

  • 1) Los átomos en la cadena B realizan las siguientes operaciones:
    • Quema de tokens de vales en el módulo ICS-20。
    • 然后执行una sendPacketoperación para un paquete que especifica la cantidad y la denominación de los tokens bloqueados。
  • 2) Los átomos en la cadena A realizan las siguientes operaciones:
    • Ejecute recvPacketla operación para el paquete.
    • 然后desbloquee tokens de vales que sean equivalentes a los vales quemados en el módulo ICS-20。
  • 3) Funciona normalmente en chainB acknowledgePacket.

inserte la descripción de la imagen aquí
Si se ha implementado IBC/TAO, el módulo ICS-20 mencionado anteriormente solo necesita tener las siguientes funciones:

  • fichas de bloqueo y desbloqueo
  • cupones de acuñación y quema

4. Análisis del contrato IBC yui-ibc-solidity

Análisis del contrato IBC en https://github.com/hyperledger-labs/yui-ibc-solidity [puede soportar Ethereum y otras cadenas compatibles con solidity EVM]:

  • 1) Los contratos de capa IBC/TAO incluyen: 【Hay principalmente 3 contratos: contrato de cliente ligero, contrato de IBCHost y contrato de IBCHandler.

    • IBCIdentifier: para biblioteca. Es principalmente la operación keccak256, que se utiliza para generar una clave de compromiso relacionada con el cliente, el consenso, la conexión, el canal, el paquete, el reconocimiento del paquete, la ranura de compromiso relacionada con el estado del cliente, el estado del consenso, la conexión, el canal, el paquete, el reconocimiento del paquete y la ruta de capacidad relacionada con la parte y canal.
    • IBCHeight: para biblioteca. Principalmente para varias operaciones en Height.Data.revision_number.
    library Height {
      //struct definition
      struct Data {
        uint64 revision_number;
        uint64 revision_height;
      }
    }
    
    • IBCMsgs: para biblioteca. Define estructuras de mensajes relacionados, como cliente, protocolo de enlace de conexión, protocolo de enlace de canal, cierre de canal y retransmisión de paquetes en ICS-026 .
    • IClient: para interfaz. Se definen funciones de interfaz como getTimestampAtHeight, getLatestHeight, checkHeaderAndUpdateState, verificarClientState, verificarClientConsensusState, verificarConexiónEstado, verificarChannelState, verificarPacketCommitment y verificarPacketAcknowledgment.
    • MockClient: para el contrato. Para la implementación simulada de la interfaz IClient, en realidad no se realiza ninguna verificación y se usa para escenarios de prueba.
    • IBFT2Client: Es un contrato. Es un cliente ligero para verificar el algoritmo de consenso IBFT 2.0 (Prueba de autoridad (PoA) Byzantine-Fault-Tolerant (BFT)) de Hyperledger Besu. [Hyperledger Besu es una solución de cadena de consorcio de Ethereum que admite un conjunto de validadores dinámicos. Hay un campo de datos adicional en el bloque para almacenar el resultado del consenso: [32 bytes Vanity, List<Validators>, Votes, Round number, Commit Seals], donde el segundo elemento es una lista de cada dirección en un conjunto de validadores de este bloque, el quinto elemento es sellos de confirmación para este bloque por parte del conjunto de validadores, cada uno de los El nodo de bloque verifica los sellos de confirmación para validar el bloque de acuerdo con el Algoritmo 1 .
    • IBCHost: para el contrato. Una vez completada la implementación, el propietario debe llamarlo más tarde setIBCModuley el parámetro es la dirección del contrato IBCHandler. 使得generateClientIdentifier、generateConnectionIdentifier、generateChannelIdentifier、setClientImpl、setClientType、setClientState、setConsensusState、setProcessedTime、setProcessedHeight、setConnection、setChannel、setNextSequenceSend、setNextSequenceRecv、setNextSequenceAck、setPacketCommitment、deletePacketCommitment、setPacketAcknowledgementCommitment、setPacketReceipt、setExpectedTimePerBlock、claimCapability、authenticateCapability等操作仅能由IBCHandler合约调用.
    • IBCClient: para biblioteca. Funciones relacionadas con el cliente implementadas, operaciones como crear y actualizar solo pueden ser llamadas por el contrato IBCHandler.
    • Conexión IBC: para biblioteca. Funciones relacionadas con la conexión implementadas, que solo son llamadas por el contrato IBCHandler. Y varias funciones de verificación de estado, compromiso y reconocimiento.
    • Canal IBC: para biblioteca. Se implementaron funciones relacionadas con el canal, así como las funciones sendPacket y recvPacket, writeAcknowledgment y acceptPacket, que solo son llamadas por el contrato IBCHandler.
    • IBCHandler: para el contrato. Al implementar, debe especificar la dirección del contrato IBCHost. Proporciona registerClient (tipo de cliente registrado, como cliente simulado o ibft2.0, que debe ser llamado por el propietario) y la implementación de la función de respuesta a cada mensaje de IBCMsgs.
    • IBCModule: para la interfaz, abstrae las funciones de interfaz onChanOpenInit, onChanOpenTry, onChanOpenAck, onChanOpenConfirm, onChanCloseInit, onChanCloseConfirm, onRecvPacket y onAcknowledgmentPacket.
  • 2) Los contratos de la capa IBC/APP incluyen: [Hay principalmente 3 contratos: contratos ICS20Bank, ICS20TransferBank y SimpleToken.

    • ICS20Bank: El contrato ICS20Bank es un contrato APP, además de implementar la interfaz abstracta definida en IICS20Bank, también implementa las funciones de depósito y retiro.
    • ICS20Transfer: las direcciones de contrato de IBCHost e IBCHandler deben especificarse durante la construcción, que es un subcontrato de ICS20TransferBank Además de implementar la interfaz abstracta definida en IBCModule, también implementa la función sendTransfer en IICS20Transfer.
    • ICS20TransferBank: las direcciones de contrato de IBCHost, IBCHandler e ICS20Bank deben especificarse durante la construcción, que es el contrato de ICS20TransferBank del contrato de la aplicación.
    • IICS20Bank: para la interfaz, abstrae las funciones de interfaz transferFrom, mint, burn.
    • IICS20Transfer: Es una interfaz que abstrae la función de interfaz sendTransfer.
    • SimpleToken: Es el contrato token del contrato APP. Se define aquí como un contrato ERC20.

El contenido de sendPacket es:

library Packet {
  //struct definition
  struct Data {
    uint64 sequence;
    string source_port;
    string source_channel;
    string destination_port;
    string destination_channel;
    bytes data;
    Height.Data timeout_height;
    uint64 timeout_timestamp;
  }
  .......
}

4.1 contrato de capa IBC/TAO yui-ibc-solidity

IBC/TAO层合约 IBCHost、IBCHandler、IBCClient、IBCConnection、IBCChannel、IBFT2Client、MockClient、IClient、IBCHeight、IBCModule、IBCMsgs、IBCIdentifier之间的关系为:
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
IBC/TAO层主要有3个合约:IBCHost合约、IBCHandler合约、IBFT2Client合约(和(或)MockClient测试合约)。

令:

//portID
const PortTransfer = "transfer"
//light client类型
const BesuIBFT2ClientType = "hyperledger-besu-ibft2"
const MockClientType = "mock-client"

IBC/TAO层部署及调用基本流程为:

  • 1)部署IBFT2Client合约。
  • 2)部署IBCHost合约。
  • 3)部署IBCHandler合约,部署时,需指定IBCHost合约地址。
  • 4)IBCHost合约owner 调用其自身的setIBCModule函数,将ibcModule设置为IBCHandler合约地址,使得其generateClientIdentifier、generateConnectionIdentifier、generateChannelIdentifier、setClientImpl、setClientType、setClientState、setConsensusState、setProcessedTime、setProcessedHeight、setConnection、setChannel、setNextSequenceSend、setNextSequenceRecv、setNextSequenceAck、setPacketCommitment、deletePacketCommitment、setPacketAcknowledgementCommitment、setPacketReceipt、setExpectedTimePerBlock、claimCapability、authenticateCapability等操作仅能由IBCHandler合约调用。
  • 5) El propietario del contrato IBCHandler llama a su propia bindPortfunción y los parámetros son ID de puerto y dirección del contrato DAPP ICS20TransferBank. La función es: llamar al contrato IBCHost e insertar capabilities[portID]la dirección del contrato ICS20TransferBanke en la matriz. [La matriz de capacidades del mismo ID de puerto admite la configuración de varias direcciones de contrato de aplicación DAPP. Pero en getModuleOwner, solo se devuelve la primera dirección DAPP de forma predeterminada y, en realidad, un ID de puerto corresponde a un contrato DAPP. 】【En el protocolo de protocolo de enlace del canal, capabilities[portID]se llamará a onChanOpenInit, channelOpenTry, channelOpenAck, onChanOpenConfirm, onChanCloseInit, onChanCloseConfirm y otras funciones en el contrato de aplicación DAPP correspondiente.
  • 6) El propietario del contrato IBCHandler llama a su propia registerClientfunción y los parámetros son el tipo de cliente ligero (como BesuIBFT2ClientType) y la dirección del contrato del cliente ligero correspondiente (como la dirección del contrato IBFT2Client). La función es: llamar al contrato IBCHost, establecer clientRegistry[clientType]=clientAddress. [Cada tipo de cliente solo se puede registrar una vez]

4.2 contrato de capa IBC/APP de yui-ibc-solidity

La relación entre los contratos de capa IBC/APP ICS20Bank, ICS20Transfer, ICS20TransferBank, IICS20Bank, IICS20Transfer y SimpleToken es: [El módulo IBCAppModule en la siguiente figura corresponde al contrato ICS20TransferBank.
inserte la descripción de la imagen aquí
Los contratos de la capa IBC/APP incluyen: 【Hay principalmente 3 contratos: contratos ICS20Bank, ICS20TransferBank y SimpleToken.

  • ICS20Bank: El contrato ICS20Bank es un contrato APP, además de implementar la interfaz abstracta definida en IICS20Bank, también implementa las funciones de depósito y retiro.
  • ICS20Transfer: las direcciones de contrato de IBCHost e IBCHandler deben especificarse durante la construcción, que es un subcontrato de ICS20TransferBank Además de implementar la interfaz abstracta definida en IBCModule, también implementa la función sendTransfer en IICS20Transfer.
  • ICS20TransferBank: las direcciones de contrato de IBCHost, IBCHandler e ICS20Bank deben especificarse durante la construcción, que es el contrato de ICS20TransferBank del contrato de la aplicación.
  • IICS20Bank: para la interfaz, abstrae las funciones de interfaz transferFrom, mint, burn.
  • IICS20Transfer: Es una interfaz que abstrae la función de interfaz sendTransfer.
  • SimpleToken: Es el contrato token del contrato APP. Se define aquí como un contrato ERC20.
const (
	DefaultChannelVersion        = "ics20-1"
	BlockTime             uint64 = 1000 * 1000 * 1000 // 1[sec]
	DefaultDelayPeriod    uint64 = 3 * BlockTime
	DefaultPrefix                = "ibc"
	TransferPort                 = "transfer"

	RelayerKeyIndex uint32 = 0
)

yui-ibc-solidity/tests/e2e/chains_test.gopara SetupTest:

func (suite *ChainTestSuite) SetupTest() {
    
    
	//信任的A链FullNode,对应的RPC接口为http://127.0.0.1:8645
	chainClientA, err := client.NewBesuClient("http://127.0.0.1:8645", clienttypes.BesuIBFT2Client)
	suite.Require().NoError(err)
	//信任的B链FullNode,对应的RPC接口为http://127.0.0.1:8745
	chainClientB, err := client.NewBesuClient("http://127.0.0.1:8745", clienttypes.BesuIBFT2Client)
	suite.Require().NoError(err)
	// A链和B链之间的ibcID
	ibcID := uint64(time.Now().UnixNano())
	// 2018/3018为全局的chainID。chainA和chianB分别维护各链信任的FullNode信息,以及各自所部署的TAO和APP合约信息。
	suite.chainA = ibctesting.NewChain(suite.T(), 2018, *chainClientA, testchain0.Contract, mnemonicPhrase, ibcID)
	suite.chainB = ibctesting.NewChain(suite.T(), 3018, *chainClientB, testchain1.Contract, mnemonicPhrase, ibcID)
	// 会分别对链A和链B进行UpdateHeader操作
	suite.coordinator = ibctesting.NewCoordinator(suite.T(), suite.chainA, suite.chainB)
}

UpdateHeaderEl proceso es:

  • 1) Temporización durante 30 segundos-"GetIBFT2ContractState: lea el bloque actual del FullNode de confianza, llame y get_ethProoflea la prueba correspondiente del bloque, incluidas la prueba de cuenta y la prueba de almacenamiento, almacenada en la estructura state.ethProof:
type ETHProof struct {
	AccountProofRLP []byte
	StorageProofRLP [][]byte
}
  • 2) Analice la información del encabezado del bloque actual y guárdela en state.ParsedHeader, verifique que haya más de 2/3 firmas de validador en el encabezado del bloque actual, una la información del sello de cada validador y guárdela en state.CommitSeals.
  • 3) Si la altura del bloque actual es mayor que la altura del encabezado registrada en el contrato de cliente ligero en la cadena, actualice chain.LastContractState al estado actual. (Al iniciar por primera vez, si la información del encabezado del contrato del cliente ligero está vacía, actualice directamente chain.LastContractState al estado actual) [En este momento, no se llama a la actualización del contrato del cliente ligero.

UpdateClientEl proceso es:

  • 1) ConstruirIBFT2MsgUpdateClient: llame a la función del contrato IBCHost getClientState, lea la altura del estado del cliente almacenado en la cadena trustedHeighty empaquete la nueva información de encabezado en la cadena de la otra parte:
func (chain *Chain) ConstructIBFT2MsgUpdateClient(counterparty *Chain, clientID string) ibchandler.IBCMsgsMsgUpdateClient {
    
    
	trustedHeight := chain.GetIBFT2ClientState(clientID).LatestHeight
	cs := counterparty.LastContractState.(client.IBFT2ContractState)
	var header = ibft2clienttypes.Header{
    
    
		BesuHeaderRlp:     cs.SealingHeaderRLP(),
		Seals:             cs.CommitSeals,
		TrustedHeight:     trustedHeight,
		AccountStateProof: cs.ETHProof().AccountProofRLP,
	}
	bz, err := MarshalWithAny(&header)
	if err != nil {
    
    
		panic(err)
	}
	return ibchandler.IBCMsgsMsgUpdateClient{
    
    
		ClientId: clientID,
		Header:   bz,
	}
}
  • 2) Llame a updateClientla función del contrato IBCHandler para actualizar el estado de consenso correspondiente y la raíz. [ checkHeaderAndUpdateStateStorageProof y stateProof se verificarán por separado.
function updateClient(IBCHost host, IBCMsgs.MsgUpdateClient calldata msg_) external {
        host.onlyIBCModule();
        bytes memory clientStateBytes;
        bytes memory consensusStateBytes;
        Height.Data memory height;
        bool found;
    
        (clientStateBytes, found) = host.getClientState(msg_.clientId);
        require(found, "clientState not found");

        (clientStateBytes, consensusStateBytes, height) = getClient(host, msg_.clientId).checkHeaderAndUpdateState(host, msg_.clientId, clientStateBytes, msg_.header);
    
         persist states 
        host.setClientState(msg_.clientId, clientStateBytes);
        host.setConsensusState(msg_.clientId, height, consensusStateBytes);
        host.setProcessedTime(msg_.clientId, height, block.timestamp);
        host.setProcessedHeight(msg_.clientId, height, block.number);
    }

TestChannelEl proceso es:

  • 1) SetupClients: Cree el cliente de la otra cadena en las dos cadenas: [Se devolverá el ID de cliente asignado en la cadena: clienteA y clienteB]
    • 1.1) ConstructIBFT2MsgCreateClient: registre el chainID de la cadena de la otra parte, la dirección del contrato IBCHost y UpdateHeaderla información de encabezado, raíz y validadores registrada en chain.LastContractState durante el proceso.
    func (chain *Chain) ConstructIBFT2MsgCreateClient(counterparty *Chain) ibchandler.IBCMsgsMsgCreateClient {
          
          
    	clientState := ibft2clienttypes.ClientState{
          
          
    		ChainId:         counterparty.ChainIDString(),
    		IbcStoreAddress: counterparty.ContractConfig.GetIBCHostAddress().Bytes(),
    		LatestHeight:    ibcclient.NewHeightFromBN(counterparty.LastHeader().Number),
    	}
    	consensusState := ibft2clienttypes.ConsensusState{
          
          
    		Timestamp:  counterparty.LastHeader().Time,
    		Root:       counterparty.LastHeader().Root.Bytes(),
    		Validators: counterparty.LastContractState.(client.IBFT2ContractState).Validators(),
    	}
    	clientStateBytes, err := MarshalWithAny(&clientState)
    	if err != nil {
          
          
    		panic(err)
    	}
    	consensusStateBytes, err := MarshalWithAny(&consensusState)
    	if err != nil {
          
          
    		panic(err)
    	}
    	return ibchandler.IBCMsgsMsgCreateClient{
          
          
    		ClientType:          ibcclient.BesuIBFT2Client,
    		Height:              clientState.LatestHeight.ToCallData(),
    		ClientStateBytes:    clientStateBytes,
    		ConsensusStateBytes: consensusStateBytes,
    	}
    }
    
    • 1.2) Llamar a la función del contrato IBCHandler de esta cadena createClientgenerará el ID de cliente correspondiente en el contrato de IBCHost y, en función del ID de cliente, almacenará el tipo de cliente correspondiente, el estado del cliente, el estado de consenso, el bloque.marca de tiempo, el número de bloque y otra información en el IBCHost. contrato.
    function createClient(IBCHost host, IBCMsgs.MsgCreateClient calldata msg_) external {
        host.onlyIBCModule();
        (, bool found) = getClientByType(host, msg_.clientType);
        require(found, "unregistered client type");
    
        string memory clientId = host.generateClientIdentifier(msg_.clientType);
        host.setClientType(clientId, msg_.clientType);
        host.setClientState(clientId, msg_.clientStateBytes);
        host.setConsensusState(clientId, msg_.height, msg_.consensusStateBytes);
        host.setProcessedTime(clientId, msg_.height, block.timestamp);
        host.setProcessedHeight(clientId, msg_.height, block.number);
    }
    
  • 2) CreateConnection: entre la cadena A y la cadena B, hay un cliente B en la cadena A, un cliente A en la cadena B, siga el protocolo de protocolo de enlace de conexión:
    • 2.1) Llamar a la función del contrato IBCHandler de la cadena A ConnectionOpenInitgenerará el identificador de conexión correspondiente en el contrato IBCHost y almacenará la información de conexión correspondiente en el contrato IBCHost. [Obtenga el ID de conexión creado escuchando el evento GeneratedClientIdentifier]
    function connectionOpenInit(IBCHost host, IBCMsgs.MsgConnectionOpenInit memory msg_) public returns (string memory) {
        host.onlyIBCModule();
        ConnectionEnd.Data memory connection = ConnectionEnd.Data({
            client_id: msg_.clientId,
            versions: getVersions(),
            state: ConnectionEnd.State.STATE_INIT,
            delay_period: msg_.delayPeriod,
            counterparty: msg_.counterparty
        });
        string memory connectionId = host.generateConnectionIdentifier();
        host.setConnection(connectionId, connection);
        return connectionId;
    }
    
    UpdateHeader: Para asegurarse de que el identificador de conexión se cree correctamente en la cadena A, obtenga un nuevo bloque y actualice el estado de la cadena A local del programa.
    UpdateClient: Actualice el estado de la cadena A al contrato de cliente ligero de la cadena B.
    • 2.2) Consulta la conexión de prueba y el cliente de prueba en la cadena A, empaquetalos como parámetros, llama a la función del contrato IBCHandler de la cadena B, realiza la verificación de estado de conexión connectionOpenTryy estado de cliente correspondiente, genera el ID de conexión correspondiente en el contrato IBCHost y almacena el contrato IBCHost correspondiente en la información de conexión del contrato IBCHost. [Obtenga el ID de conexión creado escuchando el evento GeneratedClientIdentifier] [ verifyConnectionState和verifyClientStatePuede mirar más de cerca]
      UpdateHeader: para asegurarse de que el ID de conexión se haya creado correctamente en la cadena B, obtenga nuevos bloques y actualice el estado de la cadena B local del programa.
      UpdateClient: Actualice el estado de la cadena B al contrato de cliente ligero de la cadena A.

    • 2.3) Consulta la conexión de prueba y el cliente de prueba en la cadena B, empaquetalos como parámetros, llama a la función del contrato IBCHandler de la cadena A connectionOpenAck, verifica el estado de conexión y el estado de cliente correspondientes, y actualiza la información de conexión correspondiente en el contrato IBCHost. [ verifyConnectionState和verifyClientStatePuede echar un vistazo más de cerca]
      UpdateHeader: para garantizar el éxito de la transacción en la cadena A, obtenga nuevos bloques y actualice el estado de la cadena B local del programa.
      UpdateClient: Actualice el estado de la cadena A al contrato de cliente ligero de la cadena B.

    • 2.4) Consulta la conexión de prueba en la cadena A, empaquétala como un parámetro, llama a la función del contrato IBCHandler de la cadena B connectionOpenConfirm, realiza la verificación del estado de conexión correspondiente y actualiza la información de conexión correspondiente en el contrato IBCHhost. [ verifyConnectionStatePuede echar un vistazo más de cerca]
      UpdateHeader: para garantizar el éxito de la transacción en la cadena B, obtenga nuevos bloques y actualice el estado de la cadena A local del programa.
      UpdateClient: Actualice el estado de la cadena B al contrato de cliente ligero de la cadena A.

  • 3) CreateChannel: el proceso general es similar a CreateConnection, excepto que se llaman las funciones relacionadas con el canal en el contrato. [ La diferencia con el protocolo de enlace de conexión: en el protocolo de protocolo de enlace de canal, capabilities[portID]se llamará a onChanOpenInit, channelOpenTry, channelOpenAck, onChanOpenConfirm, onChanCloseInit, onChanCloseConfirm y otras funciones en el contrato de aplicación DAPP correspondiente. La función actual es establecer la dirección de depósito en garantía del canal en la dirección del contrato ICS20TransferBank.

yui-ibc-solidity/tests/e2e/chains_test.goEn el ejemplo:

  • 1) Al implementar el contrato SimpleToken en chainA, todos los tokens se asignarán al deploymenter deploymentrA de forma predeterminada.
  • 2) Implementar el contrato ICS20Bank.
  • 3) Para implementar el contrato ICS20TransferBank, debe especificar las direcciones de contrato IBCHost, IBCHandler e ICS20Bank al implementar.
  • 4) El implementador del contrato ICS20Bank llama setOperatora la función y establece la dirección del contrato ICS20TransferBank como el rol de OPERADOR.
  • 5) Autorizar a la dirección del contrato de IBCBank a gastar 100 tokens en nombre de deploymentA.
  • 6) El implementador del contrato SimpleToken deploymentA llama deposita la función del contrato ICS20Bank y deposita 100 tokens en la dirección del contrato ICS20Bank.En el contrato ICS20Banke, el saldo de aliceA se mantendrá: _balances[id][account] += amount;.
  • 7) aliceA llama sendTransfera la función del contrato ICS20TransferBank y transfiere sus 100 tokens en chainA a bobB en chainB a través de chanA.PortID, chanA.ID (sourcePort y sourceChannel). Establezca timeoutHeight en 当前区块高度+1000.
    en sendTransfer:
    • 7.1) Juzgará si el parámetro de denominación entrante es una dirección de contrato SimpleToken simple o tiene el prefijo sourcePort+sourceChannel. Si es una dirección de contrato simple, transfiera los 100 tokens directamente a la dirección de depósito del canal de origen (es decir, la dirección de contrato de ICS20TransferBank) y mantenga el estado _balances[id][account]del contrato de ICS20Bank; si hay un prefijo, significa que los tokens de otras cadenas recibidas antes de que se transfieran, restará directamente esos 100 tokens del contrato ICS20Bank _balances[id][account].
    • 7.2) Encapsulación FungibleTokenPacketData, y más encapsulación Packet.Data, llame sendPacketa la función del contrato IBCHandler. Verificará la dirección del bindPortcontrato DAPP anterior; se asegurará de que los permisos de conexión y canal sean normales; actualice el número de envío de secuencia de IBCHost y establezca el mapa de confirmaciones en el contrato de IBCHost en [ commitments[IBCIdentifier.packetCommitmentKey(portId, channelId, sequence)] = makePacketCommitment(packet);Si el paquete se procesó, se eliminará para evitar ataques de repetición]. Eventualmente, se lanzará el evento sendPacket.
    function _sendPacket(FungibleTokenPacketData.Data memory data, string memory sourcePort, string memory sourceChannel, uint64 timeoutHeight) virtual internal {
        (Channel.Data memory channel, bool found) = ibcHost.getChannel(sourcePort, sourceChannel);
        require(found, "channel not found");
        ibcHandler.sendPacket(Packet.Data({
            sequence: ibcHost.getNextSequenceSend(sourcePort, sourceChannel),
            source_port: sourcePort,
            source_channel: sourceChannel,
            destination_port: channel.counterparty.port_id,
            destination_channel: channel.counterparty.channel_id,
            data: FungibleTokenPacketData.encode(data),
            timeout_height: Height.Data({revision_number: 0, revision_height: timeoutHeight}),
            timeout_timestamp: 0
        }));
    }
    function sendPacket(IBCHost host, Packet.Data calldata packet) external {
        host.onlyIBCModule();
        Channel.Data memory channel;
        ConnectionEnd.Data memory connection;
        IClient client;
        Height.Data memory latestHeight;
        uint64 latestTimestamp;
        uint64 nextSequenceSend;
        bool found;
    
        channel = mustGetChannel(host, packet.source_port, packet.source_channel);
        require(channel.state == Channel.State.STATE_OPEN, "channel state must be OPEN");
        require(hashString(packet.destination_port) == hashString(channel.counterparty.port_id), "packet destination port doesn't match the counterparty's port");
        require(hashString(packet.destination_channel) == hashString(channel.counterparty.channel_id), "packet destination channel doesn't match the counterparty's channel");
        connection = mustGetConnection(host, channel);
        client = IBCClient.getClient(host, connection.client_id);
        (latestHeight, found) = client.getLatestHeight(host, connection.client_id);
        require(packet.timeout_height.isZero() || latestHeight.lt(packet.timeout_height), "receiving chain block height >= packet timeout height");
        (latestTimestamp, found) = client.getTimestampAtHeight(host, connection.client_id, latestHeight);
        require(found, "consensusState not found");
        require(packet.timeout_timestamp == 0 || latestTimestamp < packet.timeout_timestamp, "receiving chain block timestamp >= packet timeout timestamp");
    
        nextSequenceSend = host.getNextSequenceSend(packet.source_port, packet.source_channel);
        require(nextSequenceSend > 0, "sequenceSend not found");
        require(packet.sequence == nextSequenceSend, "packet sequence != next send sequence");
    
        nextSequenceSend++;
        host.setNextSequenceSend(packet.source_port, packet.source_channel, nextSequenceSend);
        host.setPacketCommitment(packet.source_port, packet.source_channel, packet.sequence, packet);
    
        // TODO emit an event that includes a packet
    }
    
  • 8) UpdateHeader&UpdateClient: actualice el estado local del programa y actualice el estado del contrato de cliente ligero en la cadena B.
  • 9) Llame al contrato IBCHost de la cadena A, lea la última secuenciaSend, en base a esto, escuche el evento sendPacket lanzado por el contrato IBCHandler y filtre el evento sendPacket que coincida con el número de secuencia correspondiente, sourcePort y sourceChannel.
  • 10) Espere el DelayPeriod (1S aquí), si no espera la operación directa al siguiente paso, se informará un error.
  • 11) Llame eth_getProofpara obtener la prueba de almacenamiento del paquete en la cadena A. Luego llame a la función del contrato IBCHandler de la cadena B recvPacket: llamará a la función del contrato ICS20TransferBank onRecvPacket, acuñará la cantidad correspondiente al receptor y devolverá el acuse de recibo correspondiente al mismo tiempo. Luego verifique si el canal, la conexión y el paquete son válidos; verifique el compromiso del paquete; configure el recibo del paquete en el contrato IBCHost; configure el Compromiso del reconocimiento del paquete en el contrato IBCHost; libere el evento WriteAcknowledgment y el evento RecvPacket.
	function onRecvPacket(Packet.Data calldata packet) external virtual override returns (bytes memory acknowledgement) {
        FungibleTokenPacketData.Data memory data = FungibleTokenPacketData.decode(packet.data);
        strings.slice memory denom = data.denom.toSlice();
        strings.slice memory trimedDenom = data.denom.toSlice().beyond(
            _makeDenomPrefix(packet.source_port, packet.source_channel)
        );
        if (!denom.equals(trimedDenom)) { // receiver is source chain
            return _newAcknowledgement(
                _transferFrom(_getEscrowAddress(packet.destination_channel), data.receiver.toAddress(), trimedDenom.toString(), data.amount)
            );
        } else {
            string memory prefixedDenom = _makeDenomPrefix(packet.destination_port, packet.destination_channel).concat(denom);
            return _newAcknowledgement(
                _mint(data.receiver.toAddress(), prefixedDenom, data.amount)
            );
        }
    }
  • 12) UpdateHeader&UpdateClient: actualice el estado local del programa y actualice el estado del contrato del cliente ligero en la cadena B.
  • 13) Espere DelayPeriod (aquí 1S), si no espera la operación directa al siguiente paso, se informará un error.
  • 14) Llamada eth_getProofpara obtener (packet.DestinationPort, packet.DestinationChannel, packet.Sequence)la prueba de almacenamiento correspondiente al compromiso de acuse de recibo del paquete en la cadena B. Llame a la función del contrato IBCHandler de la cadena A acknowledgePacket: se llamará a la función del contrato ICS20TransferBank.Si onAcknowledgementPacketel reconocimiento contiene información de falla, significa que la transferencia del token falló y el token se devolverá al remitente; si tiene éxito, no se hará nada. Luego verifique el canal y la conexión; verifique el acuse de recibo del paquete; si es un canal ORDENADO, actualice el número de acuse de recibo de la secuencia; elimine el compromiso del paquete procesado para evitar ataques de reproducción.

Referencias

[1] Cómo funciona IBC de Cosmos para lograr la interoperabilidad entre cadenas de bloques
[2] IBFT 2.0 Light Client

Apéndice A - Cliente ligero IBFT2.0 de Hyperledger Besu

Hyperledger Besu es una solución de cadena de consorcio de Ethereum que admite un conjunto de validadores dinámicos.
Hyperledger Besu admite una variedad de algoritmos de consenso, entre los cuales el algoritmo de consenso IBFT 2.0 (Prueba de autoridad (PoA) Byzantine-Fault-Tolerant (BFT)) es el más popular.

Cuando se usa el consenso IBFT2.0, hay un campo de datos adicional en el bloque para almacenar el resultado del consenso: [32 bytes Vanity, List<Validators>, Votes, Round number, Commit Seals], donde el segundo elemento es una lista de cada dirección en un conjunto de validadores de este bloque, y el quinto elemento son sellos de confirmación para este bloque por el conjunto de validadores, cada nodo del bloque verificará los sellos de compromiso para validar el bloque de acuerdo con el Algoritmo 1 .

附录A.1 fuente confiable de cliente ligero y período de confianza

Cuando se inicializa IBFT2.0 Light Client, se requiere una fuente confiable.

Teniendo en cuenta que el conjunto de validadores se actualizará, se introduce un período de confianza, es decir, se puede confiar en un período del conjunto de validadores de altura:

  • El nuevo bloque debe ser verificado por el conjunto de validadores de un bloque generado dentro de un tiempo de duración Tanterior al tiempo actual.

Por lo tanto, cuando se inicializa el cliente ligero, es necesario especificar el parámetro del período de confianza como T, si T=0, significa que el conjunto de validadores de confianza no cambiará y se puede confiar en él todo el tiempo. Cada bloque solo puede aumentar o disminuir 1 validador.

Apéndice A.2 verificación de cliente ligero

Cuando el cliente ligero inicializa el período de confianza y un encabezado de una fuente confiable, debe verificar el encabezado entrante según el encabezado confiable y los validadores confiables correspondientes.
Para el bloque recién enviado, validation functionse deben verificar las siguientes condiciones: [Suponga que B h B_hBhpara la altura hhbloque de h , V h V_hVhPara bloque B h B_hBh的conjunto de validadores,BT h BT_hBT _hPara bloque B h B_hBh的marca de tiempo,Ahora ( ) Ahora()Ahora ( ) es la hora actual, TP TPT P es el período de confianza.
Supongamos que la altura del bloque de confianza actual esnnn , la altura del bloque que no es de confianza esn + m n+mnorte+m ,n > 0 y m > 0 n > 0 y m > 0norte>0 y m>0

  • 1) La hora actual está en el período de confianza del último bloque de confianza: BT n < Now ( ) < BT n + TP BT_n<Now()<BT_n+TPBT _n<Ahora ( ) _ _<BT _n+TP _
  • 2)B n + m B_{n+m}Bn + mcon V n V_nVn1/3 de las firmas. Porque el número bizantino máximo es f ( n ) = ( n − 1 ) / 3 f(n)=(n-1)/3f ( n )=( n1 ) / 3 , este requisito puede garantizar que haya al menos un validador honesto.
  • 3)B n + m B_{n+m}Bn + mcon VN + M V_{N+M}VN + M2/3+ firmas de . Garantizado n+m n+mnorte+El bloque polimerizado de altura m es válido.
  • 4) Además, verifique también StorageProof. Para obtener más información yui-ibc-solidity/contracts/lib/TrieProofs.sol, consulte https://github.com/lorenzb/proveth/blob/master/onchain/ProvethVerifier.sol .

Apéndice A.3 análisis de vitalidad del cliente ligero

La vitalidad del cliente ligero depende de la transformación del conjunto de validadores. IBFT2.0 admite Dynamic ValidatorSet, cada bloque solo puede aumentar o disminuir un validador.

Por lo tanto, es necesario asegurarse de que existe una cierta altura, y los bloques que pueden verificar esta altura en la sección anterior validation functionson generados por el protocolo IBFT2.0. Obviamente, siempre hay una altura comprobable para el aumento en el número de conjuntos de validadores. A continuación, solo considere la reducción en el número de conjuntos de validadores:
deje que el conjunto de validadores sea VVV , la disminución esΔ \DeltaΔ,有Δ ⊆ V \Delta\subseteq VDV
∣ V ∧ V − Δ ∣ ≥ ∣ V ∣ ∗ 1 / 3 |V \cuña V-\Delta|\geq |V| * 1/3VVΔ V 1 / 3

Si esta ecuación se cumple, siempre hay una altura de bloque con un conjunto de validadores que puede validar el bloque a validar. En IBFT2.0, esta ecuación se cumplirá porque ∣ V − Δ ∣ ≥ 1 |V-\Delta|\geq 1VΔ =1 \Delta=1D=1 _

Apéndice A.4 Detección de bifurcación de cliente ligero

Si se viola el modelo de falla de IBFT 2.0 y hay más de (n-1)/3 nodos fallidos, se pueden generar múltiples bloques confirmados válidos.
El cliente ligero debería poder detectar tales fallas o ataques. Esta parte del trabajo se realizará en el futuro.

Supongo que te gusta

Origin blog.csdn.net/mutourend/article/details/122576435
Recomendado
Clasificación