Pular para o conteúdo

Scripts Plutus da Cadeia Principal

Por volta de 3 min

A cadeia principal utiliza os seguintes componentes para lidar com interações com uma sidechain:

  • SidechainTokenMintingPolicy: uma política de cunhagem que valida a cunhagem ou queima de tokens da sidechain (SC_Token) na cadeia principal.
  • MPTRootTokenMintingPolicy: uma política de cunhagem para armazenar raízes MPT de pacotes de transações entre cadeias.
  • CommitteeCandidateValidator: um endereço de script para candidatos a comitê.
  • MPTRootTokenValidator: um endereço de script para armazenar MPTRootTokens.
  • CommitteeHashValidator: um endereço de script para o hash dos membros do comitê.

Todas essas políticas e validadores são parametrizados pelos parâmetros da sidechain, o que permite obter hashes de política de cunhagem e script de validador únicos.

data SidechainParams = SidechainParams
  { chainId :: Integer
  , genesisHash :: GenesisHash
    -- ^ 'GenesisHash' é um alias de tipo para ByteString
  , genesisMint :: Maybe TxOutRef
    -- ^ 'genesisMint' é um 'TxOutRef' arbitrário usado na configuração da Ponte Passiva, onde
    -- a cunhagem do SC_Token só pode acontecer uma vez. Este parâmetro será removido no produto final.
  , genesisUtxo :: TxOutRef
    -- ^ 'genesisUtxo' é um 'TxOutRef' arbitrário usado para identificar internamente
    -- 'AssetClass's (por exemplo, veja [6.](#6-update-committee-hash)) da
    -- sidechain
  }

1. Inicializar um contrato

Para inicialização, utilize um NFT (consumindo algum UTXO arbitrário) para identificar de forma única os membros atuais do comitê armazenando o hash das chaves públicas concatenadas na cadeia (veja 6.1). Esse comitê é utilizado para verificar assinaturas de transferências da sidechain para a cadeia principal (veja 3.1).

Fluxo de trabalho:

  1. Chame o endpoint de inicialização da sidechain para gerar os SidechainParams para uma nova sidechain.
  2. Use os parâmetros da sidechain fornecidos para o restante dos endpoints trabalhar com esta sidechain específica.

Parâmetros do endpoint:

data InitSidechainParams = InitSidechainParams
  { initChainId :: Integer
  , initGenesisHash :: GenesisHash
    -- ^ 'GenesisHash' é um alias de tipo para ByteString
  , initUtxo :: TxOutRef
    -- ^ 'initUtxo' é utilizado para criar o NFT do comitê
  , initCommittee :: [PubKey]
    -- ^ 'initCommittee' é o comitê inicial da sidechain
  , initSidechainEpoch :: Integer
    -- ^ 'initSidechainEpoch' é a época inicial da sidechain
  , initMint :: Maybe TxOutRef
    -- ^ 'initMint' é utilizado apenas na Ponte Passiva e será removido no produto final
  }

### 2. Transferência de tokens SC_Token da cadeia principal para a sidechain

Os tokens SC_Token na rede Cardano representam ativos nativos bloqueados na rede da sidechain. Quando alguns tokens estão bloqueados em um determinado contrato da sidechain, você pode cunhar a quantidade equivalente no Cardano. Por outro lado, queimar esses tokens na rede Cardano (veja 3) liberará esses tokens e os enviará ao proprietário.

**Fluxo de trabalho:**

1. Chame o endpoint de queima do contrato com BurnParams.
2. Uma transação será submetida à cadeia principal, queimando a quantidade especificada de tokens SC_Token e o endereço correspondente da sidechain no redentor.
3. O componente Bridge (observando a cadeia principal onde a política de cunhagem é tratada) verificará a transação e criará uma transação apropriada da sidechain.

**Parâmetros do endpoint:**

```haskell
data UpdateCommitteeHashParams = UpdateCommitteeHashParams
  { newCommitteePubKeys :: [SidechainPubKey]
    -- ^ The public keys of the new committee.
  , committeeSignatures :: [(SidechainPubKey, Maybe ByteString)]
    -- ^ Public keys of all committee members with their corresponding signatures if there's one
  , sidechainParams :: SidechainParams
    -- ^ Parameters identifying the Sidechain
  , previousMerkleRoot :: Maybe ByteString
    -- ^ last merkle root inserted on chain, unless there is no Merkle root inserted yet
  , sidechainEpoch :: Integer
    -- ^ sidechain epoch of the new committee
  }

O script de validação verifica o seguinte:

  • o hash das chaves públicas do comitê corresponde ao hash salvo na cadeia
  • todas as assinaturas fornecidas são válidas
  • tamanho das assinaturas > 2/3 * tamanho das chaves públicas do comitê
  • o NFT do UTXO que contém a antiga chave de verificação no endereço do script
  • consome o UTXO mencionado acima
  • a época da sidechain do novo hash do comitê é maior que a época da sidechain do UTXO do hash do comitê consumido
  • emite um novo UTXO com o hash do comitê atualizado contendo o NFT para o mesmo endereço do script
  • a referência para o último Merkle root é incluída na transação

Datum:

data UpdateCommitteeHash = UpdateCommitteeHash
  { committeePubKeysHash :: ByteString
    -- ^ Hash of all lexicographically sorted public keys of the current committee members
  , sidechainEpoch :: Integer
    -- ^ sidechain epoch of the committee
  }
committeePubKeys = sort([key1, key2, ..., keyN])
committeePubKeysHash = blake2b(concat(committeePubKeys))
keyN - 33 bytes compressed ecdsa public key of a committee member

plutus4

Committee handover (updating committee hash)

Redeemer:

data UpdateCommitteeRedeemer = UpdateCommitteeRedeemer
  { signatures :: [ByteString]
  , newCommitteePubKeys :: [SidechainPubKey]
  , committeePubKeys :: [SidechainPubKey]
  , previousMerkleRoot :: Maybe ByteString
    -- ^ last merkle root inserted on chain, unless there is no Merkle root inserted yet
  }

Signatures are constructed as follow:

SidechainPubKey - 33 bytes compressed ecdsa public key
data UpdateCommitteeMessage = UpdateCommitteeMessage
  { sidechainParams :: SidechainParams
  , newCommitteePubKeys :: [SidechainPubKey] -- sorted lexicographically
  , previousMerkleRoot :: Maybe ByteString
    -- ^ last Merkle root inserted on chain (Merkle root for the last sidechain epoch)
  , sidechainEpoch :: Integer
    -- ^ sidechain epoch of the newly registered committee
  }
signature = ecdsa.sign(data: blake2b(cbor(UpdateCommitteeMessage)), key: committeeMemberPrvKey)

6.2. Encadeamento de Merkle root

Como descrito na seção 6 'Transferência de Comitê', a ordem correta das inserções de Merkle root e atualizações de hash do comitê deve ser mantida. Uma nova cadeia de Merkle root faz isso. Cada Merkle root tem uma referência ao seu antecessor (se existir), além disso, todas as atualizações de hash do comitê fazem referência ao último Merkle root inserido (se existir).

01

Encadeamento de Merkle root (SC ep = época da sidechain)

Como visto no gráfico acima, o primeiro Merkle root não tem referência, o que é completamente válido. A existência do último Merkle root não é obrigatória.

Caso uma época da sidechain passe sem nenhuma transação cross-chain, nenhum Merkle root é inserido, resultando em duas atualizações de hash do comitê fazendo referência ao mesmo Merkle root.

02

Encadeamento de Merkle root - época sem Merkle root (SC ep = época da sidechain)

No futuro, pode haver múltiplos Merkle roots por época da sidechain, então o resultado poderia ser como o seguinte:

03

Encadeamento de Merkle root - múltiplos Merkle roots por época (SC ep = época da sidechain)

Última atualização:
Contribuidores: cauechianca