feat: zilliqa consensus data related to block (#10699)

* refactor: DRY declaration of chain type and feature dependent repos

* feat: add `:zilliqa` chain type

* chore: add `zilliqa` to cspell.json

* feat: import consensus data related to block

* fix: cspell

* fix: ethereum_jsonrpc tests

* fix: format & credo

* feat: render consensus data in `/api/v2/blocks/:block_hash_or_number` response

* fix: add missing `view` field to block db schema

* feat: add workflow to publish docker image

* fix: cast `zilliqa_view` field of a block

* refactor: view

* refactor: define spec only once

* refactor: DRY definition of other repos

* fix: remove not null constraint for `zilliqa_view`

* fix: dialyzer

* fix: `@spec` for `chain_type_fields/2`

* feat: github workflows

* fix: debug

* fix: add missing indexer and API workflows
pull/11144/head
Fedor Ivanov 2 weeks ago committed by GitHub
parent 94745592ac
commit 0dfd4c6ca2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 19
      .github/workflows/config.yml
  2. 167
      .github/workflows/pre-release-zilliqa.yml
  3. 162
      .github/workflows/publish-docker-image-for-zilliqa.yml
  4. 164
      .github/workflows/release-zilliqa.yml
  5. 8
      apps/block_scout_web/lib/block_scout_web/controllers/api/v2/block_controller.ex
  6. 6
      apps/block_scout_web/lib/block_scout_web/views/api/v2/block_view.ex
  7. 88
      apps/block_scout_web/lib/block_scout_web/views/api/v2/zilliqa_view.ex
  8. 111
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/block.ex
  9. 61
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/blocks.ex
  10. 20
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa.ex
  11. 186
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/aggregate_quorum_certificate.ex
  12. 111
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/helper.ex
  13. 187
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/nested_quorum_certificates.ex
  14. 100
      apps/ethereum_jsonrpc/lib/ethereum_jsonrpc/zilliqa/quorum_certificate.ex
  15. 2
      apps/ethereum_jsonrpc/mix.exs
  16. 4
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc/block_test.exs
  17. 7
      apps/ethereum_jsonrpc/test/ethereum_jsonrpc/zilliqa_test.exs
  18. 74
      apps/explorer/config/dev.exs
  19. 128
      apps/explorer/config/prod.exs
  20. 21
      apps/explorer/config/test.exs
  21. 19
      apps/explorer/lib/explorer/application.ex
  22. 23
      apps/explorer/lib/explorer/chain/block.ex
  23. 79
      apps/explorer/lib/explorer/chain/import/runner/zilliqa/aggregate_quorum_certificates.ex
  24. 86
      apps/explorer/lib/explorer/chain/import/runner/zilliqa/nested_quorum_certificates.ex
  25. 79
      apps/explorer/lib/explorer/chain/import/runner/zilliqa/quorum_certificates.ex
  26. 5
      apps/explorer/lib/explorer/chain/import/stage/chain_type_specific.ex
  27. 64
      apps/explorer/lib/explorer/chain/zilliqa/aggregate_quorum_certificate.ex
  28. 60
      apps/explorer/lib/explorer/chain/zilliqa/hash/signature.ex
  29. 64
      apps/explorer/lib/explorer/chain/zilliqa/nested_quorum_certificate.ex
  30. 56
      apps/explorer/lib/explorer/chain/zilliqa/quorum_certificate.ex
  31. 209
      apps/explorer/lib/explorer/repo.ex
  32. 20
      apps/explorer/priv/zilliqa/migrations/20240927123039_create_quorum_certificate.exs
  33. 19
      apps/explorer/priv/zilliqa/migrations/20240927123101_create_aggregate_quorum_certificate.exs
  34. 21
      apps/explorer/priv/zilliqa/migrations/20240927123113_create_nested_quorum_certificate.exs
  35. 9
      apps/explorer/priv/zilliqa/migrations/20241015095021_add_view_to_block.exs
  36. 4
      apps/explorer/test/explorer/migrator/sanitize_duplicated_log_index_logs_test.exs
  37. 49
      apps/indexer/lib/indexer/block/fetcher.ex
  38. 48
      config/config_helper.exs
  39. 155
      config/runtime/dev.exs
  40. 141
      config/runtime/prod.exs
  41. 5
      cspell.json

@ -49,7 +49,24 @@ jobs:
// Add/remove CI matrix chain types here // Add/remove CI matrix chain types here
const defaultChainTypes = ["default"]; const defaultChainTypes = ["default"];
const chainTypes = ["ethereum", "polygon_zkevm", "rsk", "stability", "filecoin", "optimism", "arbitrum", "celo", "zetachain", "zksync", "shibarium", "blackfort", "scroll"];
const chainTypes = [
"arbitrum",
"blackfort",
"celo",
"ethereum",
"filecoin",
"optimism",
"polygon_zkevm",
"rsk",
"scroll",
"shibarium",
"stability",
"zetachain",
"zilliqa",
"zksync"
];
const extraChainTypes = ["suave", "polygon_edge"]; const extraChainTypes = ["suave", "polygon_edge"];
// Chain type matrix we use in master branch // Chain type matrix we use in master branch

@ -0,0 +1,167 @@
name: Pre-release for Zilliqa
on:
workflow_dispatch:
inputs:
number:
type: number
required: true
env:
OTP_VERSION: ${{ vars.OTP_VERSION }}
ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }}
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
env:
RELEASE_VERSION: 6.9.0
steps:
- uses: actions/checkout@v4
- name: Setup repo
uses: ./.github/actions/setup-repo
id: setup
with:
docker-username: ${{ secrets.DOCKER_USERNAME }}
docker-password: ${{ secrets.DOCKER_PASSWORD }}
docker-remote-multi-platform: true
docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }}
docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }}
- name: Build and push Docker image (indexer + API)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
DISABLE_WEBAPP=false
API_V1_READ_METHODS_DISABLED=false
API_V1_WRITE_METHODS_DISABLED=false
CACHE_EXCHANGE_RATES_PERIOD=
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
ADMIN_PANEL_ENABLED=false
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=zilliqa
- name: Build and push Docker image (indexer)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-indexer
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
DISABLE_API=true
DISABLE_WEBAPP=true
CACHE_EXCHANGE_RATES_PERIOD=
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
ADMIN_PANEL_ENABLED=false
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=zilliqa
- name: Build and push Docker image (API)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-api
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
DISABLE_INDEXER=true
DISABLE_WEBAPP=true
CACHE_EXCHANGE_RATES_PERIOD=
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
ADMIN_PANEL_ENABLED=false
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=zilliqa
- name: Build and push Docker image (indexer + API + shrink internal transactions)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
CACHE_EXCHANGE_RATES_PERIOD=
API_V1_READ_METHODS_DISABLED=false
DISABLE_WEBAPP=false
API_V1_WRITE_METHODS_DISABLED=false
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
ADMIN_PANEL_ENABLED=false
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=zilliqa
SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true
- name: Build and push Docker image (indexer + shrink internal transactions)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-indexer
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
CACHE_EXCHANGE_RATES_PERIOD=
DISABLE_WEBAPP=true
DISABLE_API=true
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
ADMIN_PANEL_ENABLED=false
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=zilliqa
SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true
- name: Build and push Docker image (API + shrink internal transactions)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}-shrink-internal-txs-api
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
CACHE_EXCHANGE_RATES_PERIOD=
DISABLE_WEBAPP=true
DISABLE_INDEXER=true
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
ADMIN_PANEL_ENABLED=false
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-alpha.${{ inputs.number }}
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=zilliqa
SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true

@ -0,0 +1,162 @@
name: Zilliqa publish Docker image
on:
workflow_dispatch:
push:
branches:
- production-zilliqa
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
env:
RELEASE_VERSION: 6.9.0
DOCKER_CHAIN_NAME: zilliqa
steps:
- uses: actions/checkout@v4
- name: Setup repo
uses: ./.github/actions/setup-repo
id: setup
with:
docker-username: ${{ secrets.DOCKER_USERNAME }}
docker-password: ${{ secrets.DOCKER_PASSWORD }}
docker-remote-multi-platform: true
docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }}
docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }}
- name: Build and push Docker image (indexer + API)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
CACHE_EXCHANGE_RATES_PERIOD=
API_V1_READ_METHODS_DISABLED=false
DISABLE_WEBAPP=false
API_V1_WRITE_METHODS_DISABLED=false
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
ADMIN_PANEL_ENABLED=false
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }}
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }}
- name: Build and push Docker image (indexer)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-indexer
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
CACHE_EXCHANGE_RATES_PERIOD=
DISABLE_WEBAPP=true
DISABLE_API=true
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
ADMIN_PANEL_ENABLED=false
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }}
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }}
- name: Build and push Docker image (API)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-api
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
CACHE_EXCHANGE_RATES_PERIOD=
DISABLE_WEBAPP=true
DISABLE_INDEXER=true
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
ADMIN_PANEL_ENABLED=false
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }}
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }}
- name: Build and push Docker image (indexer + API + shrink internal transactions)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
CACHE_EXCHANGE_RATES_PERIOD=
API_V1_READ_METHODS_DISABLED=false
DISABLE_WEBAPP=false
API_V1_WRITE_METHODS_DISABLED=false
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
ADMIN_PANEL_ENABLED=false
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }}
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }}
SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true
- name: Build and push Docker image (indexer + shrink internal transactions)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-indexer
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
CACHE_EXCHANGE_RATES_PERIOD=
DISABLE_WEBAPP=true
DISABLE_API=true
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
ADMIN_PANEL_ENABLED=false
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }}
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }}
SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true
- name: Build and push Docker image (API + shrink internal transactions)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-${{ env.DOCKER_CHAIN_NAME }}:${{ env.RELEASE_VERSION }}-postrelease-${{ env.SHORT_SHA }}-shrink-internal-txs-api
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
CACHE_EXCHANGE_RATES_PERIOD=
DISABLE_WEBAPP=true
DISABLE_INDEXER=true
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
ADMIN_PANEL_ENABLED=false
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta.+commit.${{ env.SHORT_SHA }}
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=${{ env.DOCKER_CHAIN_NAME }}
SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true

@ -0,0 +1,164 @@
name: Release for Zilliqa
on:
release:
types: [published]
env:
OTP_VERSION: ${{ vars.OTP_VERSION }}
ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }}
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
env:
RELEASE_VERSION: 6.9.0
steps:
- uses: actions/checkout@v4
- name: Setup repo
uses: ./.github/actions/setup-repo
id: setup
with:
docker-username: ${{ secrets.DOCKER_USERNAME }}
docker-password: ${{ secrets.DOCKER_PASSWORD }}
docker-remote-multi-platform: true
docker-arm-host: ${{ secrets.ARM_RUNNER_HOSTNAME }}
docker-arm-host-key: ${{ secrets.ARM_RUNNER_KEY }}
- name: Build and push Docker image (indexer + API)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-zilliqa:latest, blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
DISABLE_WEBAPP=false
API_V1_READ_METHODS_DISABLED=false
API_V1_WRITE_METHODS_DISABLED=false
CACHE_EXCHANGE_RATES_PERIOD=
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
ADMIN_PANEL_ENABLED=false
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=zilliqa
- name: Build and push Docker image (indexer)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-indexer
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
DISABLE_API=true
DISABLE_WEBAPP=true
CACHE_EXCHANGE_RATES_PERIOD=
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
ADMIN_PANEL_ENABLED=false
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=zilliqa
- name: Build and push Docker image (API)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-api
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
DISABLE_INDEXER=true
DISABLE_WEBAPP=true
CACHE_EXCHANGE_RATES_PERIOD=
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
ADMIN_PANEL_ENABLED=false
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=zilliqa
- name: Build and push Docker image (indexer + API + shrink internal transactions)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-shrink-internal-txs
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
CACHE_EXCHANGE_RATES_PERIOD=
API_V1_READ_METHODS_DISABLED=false
DISABLE_WEBAPP=false
API_V1_WRITE_METHODS_DISABLED=false
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
ADMIN_PANEL_ENABLED=false
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=zilliqa
SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true
- name: Build and push Docker image (indexer + shrink internal transactions)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-shrink-internal-txs-indexer
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
CACHE_EXCHANGE_RATES_PERIOD=
DISABLE_WEBAPP=true
DISABLE_API=true
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
ADMIN_PANEL_ENABLED=false
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=zilliqa
SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true
- name: Build and push Docker image (API + shrink internal transactions)
uses: docker/build-push-action@v5
with:
context: .
file: ./docker/Dockerfile
push: true
tags: blockscout/blockscout-zilliqa:${{ env.RELEASE_VERSION }}-shrink-internal-txs-api
labels: ${{ steps.setup.outputs.docker-labels }}
platforms: |
linux/amd64
linux/arm64/v8
build-args: |
CACHE_EXCHANGE_RATES_PERIOD=
DISABLE_WEBAPP=true
DISABLE_INDEXER=true
CACHE_TOTAL_GAS_USAGE_COUNTER_ENABLED=
ADMIN_PANEL_ENABLED=false
CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=
BLOCKSCOUT_VERSION=v${{ env.RELEASE_VERSION }}-beta
RELEASE_VERSION=${{ env.RELEASE_VERSION }}
CHAIN_TYPE=zilliqa
SHRINK_INTERNAL_TRANSACTIONS_ENABLED=true

@ -76,6 +76,14 @@ defmodule BlockScoutWeb.API.V2.BlockController do
:arbitrum_confirmation_transaction => :optional :arbitrum_confirmation_transaction => :optional
} }
:zilliqa ->
@chain_type_transaction_necessity_by_association %{}
@chain_type_block_necessity_by_association %{
:zilliqa_quorum_certificate => :optional,
:zilliqa_aggregate_quorum_certificate => :optional,
[zilliqa_aggregate_quorum_certificate: [:nested_quorum_certificates]] => :optional
}
_ -> _ ->
@chain_type_transaction_necessity_by_association %{} @chain_type_transaction_necessity_by_association %{}
@chain_type_block_necessity_by_association %{} @chain_type_block_necessity_by_association %{}

@ -156,6 +156,12 @@ defmodule BlockScoutWeb.API.V2.BlockView do
BlockScoutWeb.API.V2.CeloView.extend_block_json_response(result, block, single_block?) BlockScoutWeb.API.V2.CeloView.extend_block_json_response(result, block, single_block?)
end end
:zilliqa ->
defp chain_type_fields(result, block, single_block?) do
# credo:disable-for-next-line Credo.Check.Design.AliasUsage
BlockScoutWeb.API.V2.ZilliqaView.extend_block_json_response(result, block, single_block?)
end
_ -> _ ->
defp chain_type_fields(result, _block, _single_block?) do defp chain_type_fields(result, _block, _single_block?) do
result result

@ -0,0 +1,88 @@
if Application.compile_env(:explorer, :chain_type) == :zilliqa do
defmodule BlockScoutWeb.API.V2.ZilliqaView do
@moduledoc """
View functions for rendering Zilliqa-related data in JSON format.
"""
alias Explorer.Chain.Block
alias Explorer.Chain.Zilliqa.{AggregateQuorumCertificate, QuorumCertificate}
@doc """
Extends the JSON output with a sub-map containing information related to Zilliqa,
such as the quorum certificate and aggregate quorum certificate.
## Parameters
- `out_json`: A map defining the output JSON which will be extended.
- `block`: The block structure containing Zilliqa-related data.
- `single_block?`: A boolean indicating if it is a single block.
## Returns
- A map extended with data related to Zilliqa.
"""
@spec extend_block_json_response(map(), Block.t(), boolean()) :: map()
def extend_block_json_response(out_json, %Block{}, false),
do: out_json
def extend_block_json_response(out_json, %Block{zilliqa_view: zilliqa_view} = block, true) do
zilliqa_json =
%{view: zilliqa_view}
|> add_quorum_certificate(block)
|> add_aggregate_quorum_certificate(block)
Map.put(out_json, :zilliqa, zilliqa_json)
end
@spec add_quorum_certificate(map(), Block.t()) :: map()
defp add_quorum_certificate(
zilliqa_json,
%Block{
zilliqa_quorum_certificate: %QuorumCertificate{
view: view,
signature: signature,
signers: signers
}
}
) do
zilliqa_json
|> Map.put(:quorum_certificate, %{
view: view,
signature: signature,
signers: signers
})
end
defp add_quorum_certificate(zilliqa_json, _block), do: zilliqa_json
@spec add_aggregate_quorum_certificate(map(), Block.t()) :: map()
defp add_aggregate_quorum_certificate(zilliqa_json, %Block{
zilliqa_aggregate_quorum_certificate: %AggregateQuorumCertificate{
view: view,
signature: signature,
nested_quorum_certificates: nested_quorum_certificates
}
})
when is_list(nested_quorum_certificates) do
zilliqa_json
|> Map.put(:aggregate_quorum_certificate, %{
view: view,
signature: signature,
signers:
Enum.map(
nested_quorum_certificates,
& &1.proposed_by_validator_index
),
nested_quorum_certificates:
Enum.map(
nested_quorum_certificates,
&%{
view: &1.view,
signature: &1.signature,
proposed_by_validator_index: &1.proposed_by_validator_index
}
)
})
end
defp add_aggregate_quorum_certificate(zilliqa_json, _block), do: zilliqa_json
end
end

@ -8,6 +8,9 @@ defmodule EthereumJSONRPC.Block do
alias EthereumJSONRPC.{Transactions, Uncles, Withdrawals} alias EthereumJSONRPC.{Transactions, Uncles, Withdrawals}
alias EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate, as: ZilliqaAggregateQuorumCertificate
alias EthereumJSONRPC.Zilliqa.QuorumCertificate, as: ZilliqaQuorumCertificate
# Because proof of stake does not naturally produce uncles like proof of work, # Because proof of stake does not naturally produce uncles like proof of work,
# the list of these in each block is empty, and the hash of this list # the list of these in each block is empty, and the hash of this list
# (sha3Uncles) is the RLP-encoded hash of an empty list. # (sha3Uncles) is the RLP-encoded hash of an empty list.
@ -17,29 +20,36 @@ defmodule EthereumJSONRPC.Block do
:rsk -> :rsk ->
@chain_type_fields quote( @chain_type_fields quote(
do: [ do: [
bitcoin_merged_mining_header: EthereumJSONRPC.data(), {optional(:bitcoin_merged_mining_header), EthereumJSONRPC.data()},
bitcoin_merged_mining_coinbase_transaction: EthereumJSONRPC.data(), {optional(:bitcoin_merged_mining_coinbase_transaction), EthereumJSONRPC.data()},
bitcoin_merged_mining_merkle_proof: EthereumJSONRPC.data(), {optional(:bitcoin_merged_mining_merkle_proof), EthereumJSONRPC.data()},
hash_for_merged_mining: EthereumJSONRPC.data(), {optional(:hash_for_merged_mining), EthereumJSONRPC.data()},
minimum_gas_price: non_neg_integer() {optional(:minimum_gas_price), non_neg_integer()}
] ]
) )
:ethereum -> :ethereum ->
@chain_type_fields quote( @chain_type_fields quote(
do: [ do: [
withdrawals_root: EthereumJSONRPC.hash(), {optional(:withdrawals_root), EthereumJSONRPC.hash()},
blob_gas_used: non_neg_integer(), {optional(:blob_gas_used), non_neg_integer()},
excess_blob_gas: non_neg_integer() {optional(:excess_blob_gas), non_neg_integer()}
] ]
) )
:arbitrum -> :arbitrum ->
@chain_type_fields quote( @chain_type_fields quote(
do: [ do: [
send_count: non_neg_integer(), {optional(:send_count), non_neg_integer()},
send_root: EthereumJSONRPC.hash(), {optional(:send_root), EthereumJSONRPC.hash()},
l1_block_number: non_neg_integer() {optional(:l1_block_number), non_neg_integer()}
]
)
:zilliqa ->
@chain_type_fields quote(
do: [
{optional(:zilliqa_view), non_neg_integer()}
] ]
) )
@ -47,7 +57,14 @@ defmodule EthereumJSONRPC.Block do
@chain_type_fields quote(do: []) @chain_type_fields quote(do: [])
end end
@type elixir :: %{String.t() => non_neg_integer | DateTime.t() | String.t() | nil} @type elixir :: %{
String.t() =>
non_neg_integer
| DateTime.t()
| String.t()
| map()
| nil
}
@type params :: %{ @type params :: %{
unquote_splicing(@chain_type_fields), unquote_splicing(@chain_type_fields),
difficulty: pos_integer(), difficulty: pos_integer(),
@ -191,6 +208,15 @@ defmodule EthereumJSONRPC.Block do
"sendCount" => 91,\ "sendCount" => 91,\
"l1BlockNumber" => 19828534,\ "l1BlockNumber" => 19828534,\
""" """
:zilliqa -> """
"view" => "0x115cca",\
"quorumCertificate" => %{\
"block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa",\
"cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]",\
"signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",\
"view" => "0x115cc7"\
},\
"""
_ -> "" _ -> ""
end} end}
...> "uncles" => [] ...> "uncles" => []
@ -233,6 +259,9 @@ defmodule EthereumJSONRPC.Block do
send_count: 91,\ send_count: 91,\
l1_block_number: 19828534,\ l1_block_number: 19828534,\
""" """
:zilliqa -> """
zilliqa_view: "0x115cca",\
"""
_ -> "" _ -> ""
end} end}
uncles: [] uncles: []
@ -301,6 +330,9 @@ defmodule EthereumJSONRPC.Block do
send_count: nil,\ send_count: nil,\
l1_block_number: nil,\ l1_block_number: nil,\
""" """
:zilliqa -> """
zilliqa_view: nil,\
"""
_ -> "" _ -> ""
end} end}
uncles: [] uncles: []
@ -490,6 +522,7 @@ defmodule EthereumJSONRPC.Block do
} }
end end
@spec chain_type_fields(params, elixir) :: params
case Application.compile_env(:explorer, :chain_type) do case Application.compile_env(:explorer, :chain_type) do
:rsk -> :rsk ->
defp chain_type_fields(params, elixir) do defp chain_type_fields(params, elixir) do
@ -524,6 +557,14 @@ defmodule EthereumJSONRPC.Block do
}) })
end end
:zilliqa ->
defp chain_type_fields(params, elixir) do
params
|> Map.merge(%{
zilliqa_view: Map.get(elixir, "view")
})
end
_ -> _ ->
defp chain_type_fields(params, _), do: params defp chain_type_fields(params, _), do: params
end end
@ -733,6 +774,30 @@ defmodule EthereumJSONRPC.Block do
def elixir_to_withdrawals(%{"withdrawals" => withdrawals}), do: withdrawals def elixir_to_withdrawals(%{"withdrawals" => withdrawals}), do: withdrawals
def elixir_to_withdrawals(_), do: [] def elixir_to_withdrawals(_), do: []
@doc """
Get `t:EthereumJSONRPC.Zilliqa.QuorumCertificate.elixir/0` from `t:elixir/0`.
"""
@spec elixir_to_zilliqa_quorum_certificate(elixir()) :: ZilliqaQuorumCertificate.t() | nil
def elixir_to_zilliqa_quorum_certificate(%{"quorumCertificate" => quorum_certificate}),
do: quorum_certificate
# WARN: This clause is introduced as a workaround to fix tests. HOWEVER, it
# allows the block with a `quorumCertificate` field to be successfully
# imported. This is a temporary solution and should be addressed in the future.
def elixir_to_zilliqa_quorum_certificate(_), do: nil
@doc """
Get `t:EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate.elixir/0` from `t:elixir/0`.
"""
@spec elixir_to_zilliqa_aggregate_quorum_certificate(elixir()) ::
ZilliqaAggregateQuorumCertificate.t() | nil
def elixir_to_zilliqa_aggregate_quorum_certificate(%{
"aggregateQuorumCertificate" => aggregate_quorum_certificate
}),
do: aggregate_quorum_certificate
def elixir_to_zilliqa_aggregate_quorum_certificate(_), do: nil
@doc """ @doc """
Decodes the stringly typed numerical fields to `t:non_neg_integer/0` and the timestamps to `t:DateTime.t/0` Decodes the stringly typed numerical fields to `t:non_neg_integer/0` and the timestamps to `t:DateTime.t/0`
@ -834,7 +899,7 @@ defmodule EthereumJSONRPC.Block do
defp entry_to_elixir({key, quantity}, _block) defp entry_to_elixir({key, quantity}, _block)
when key in ~w(difficulty gasLimit gasUsed minimumGasPrice baseFeePerGas number size when key in ~w(difficulty gasLimit gasUsed minimumGasPrice baseFeePerGas number size
cumulativeDifficulty totalDifficulty paidFees minimumGasPrice blobGasUsed cumulativeDifficulty totalDifficulty paidFees blobGasUsed
excessBlobGas l1BlockNumber sendCount) and excessBlobGas l1BlockNumber sendCount) and
not is_nil(quantity) do not is_nil(quantity) do
{key, quantity_to_integer(quantity)} {key, quantity_to_integer(quantity)}
@ -872,6 +937,26 @@ defmodule EthereumJSONRPC.Block do
{key, Withdrawals.to_elixir(withdrawals, block_hash, quantity_to_integer(block_number))} {key, Withdrawals.to_elixir(withdrawals, block_hash, quantity_to_integer(block_number))}
end end
case Application.compile_env(:explorer, :chain_type) do
:zilliqa ->
defp entry_to_elixir({"view" = key, quantity}, _block) when not is_nil(quantity) do
{key, quantity_to_integer(quantity)}
end
defp entry_to_elixir({"quorumCertificate" = key, entry}, %{"hash" => block_hash}) do
# credo:disable-for-next-line Credo.Check.Design.AliasUsage
{key, EthereumJSONRPC.Zilliqa.QuorumCertificate.new(entry, block_hash)}
end
defp entry_to_elixir({"aggregateQuorumCertificate" = key, entry}, %{"hash" => block_hash}) do
# credo:disable-for-next-line Credo.Check.Design.AliasUsage
{key, EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate.new(entry, block_hash)}
end
_ ->
:ok
end
# bitcoinMergedMiningCoinbaseTransaction bitcoinMergedMiningHeader bitcoinMergedMiningMerkleProof hashForMergedMining - RSK https://github.com/blockscout/blockscout/pull/2934 # bitcoinMergedMiningCoinbaseTransaction bitcoinMergedMiningHeader bitcoinMergedMiningMerkleProof hashForMergedMining - RSK https://github.com/blockscout/blockscout/pull/2934
# committedSeals committee pastCommittedSeals proposerSeal round - Autonity network https://github.com/blockscout/blockscout/pull/3480 # committedSeals committee pastCommittedSeals proposerSeal round - Autonity network https://github.com/blockscout/blockscout/pull/3480
# blockGasCost extDataGasUsed - sgb/ava https://github.com/blockscout/blockscout/pull/5301 # blockGasCost extDataGasUsed - sgb/ava https://github.com/blockscout/blockscout/pull/5301

@ -8,7 +8,44 @@ defmodule EthereumJSONRPC.Blocks do
@type elixir :: [Block.elixir()] @type elixir :: [Block.elixir()]
@type params :: [Block.params()] @type params :: [Block.params()]
@default_struct_fields [
blocks_params: [],
block_second_degree_relations_params: [],
transactions_params: [],
withdrawals_params: [],
errors: []
]
case Application.compile_env(:explorer, :chain_type) do
:zilliqa ->
@chain_type_fields quote(
do: [
zilliqa_quorum_certificates_params: [
EthereumJSONRPC.Zilliqa.QuorumCertificate.params()
],
zilliqa_aggregate_quorum_certificates_params: [
EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate.params()
],
zilliqa_nested_quorum_certificates_params: [
EthereumJSONRPC.Zilliqa.NestedQuorumCertificates.params()
]
]
)
@chain_type_struct_fields [
zilliqa_quorum_certificates_params: [],
zilliqa_aggregate_quorum_certificates_params: [],
zilliqa_nested_quorum_certificates_params: []
]
_ ->
@chain_type_struct_fields []
@chain_type_fields quote(do: [])
end
@type t :: %__MODULE__{ @type t :: %__MODULE__{
unquote_splicing(@chain_type_fields),
blocks_params: [map()], blocks_params: [map()],
block_second_degree_relations_params: [map()], block_second_degree_relations_params: [map()],
transactions_params: [map()], transactions_params: [map()],
@ -16,11 +53,7 @@ defmodule EthereumJSONRPC.Blocks do
errors: [Transport.error()] errors: [Transport.error()]
} }
defstruct blocks_params: [], defstruct @default_struct_fields ++ @chain_type_struct_fields
block_second_degree_relations_params: [],
transactions_params: [],
withdrawals_params: [],
errors: []
def requests(id_to_params, request) when is_map(id_to_params) and is_function(request, 1) do def requests(id_to_params, request) when is_map(id_to_params) and is_function(request, 1) do
Enum.map(id_to_params, fn {id, params} -> Enum.map(id_to_params, fn {id, params} ->
@ -62,6 +95,21 @@ defmodule EthereumJSONRPC.Blocks do
transactions_params: transactions_params, transactions_params: transactions_params,
withdrawals_params: withdrawals_params withdrawals_params: withdrawals_params
} }
|> extend_with_chain_type_fields(elixir_blocks)
end
@spec extend_with_chain_type_fields(t(), elixir()) :: t()
case Application.compile_env(:explorer, :chain_type) do
:zilliqa ->
defp extend_with_chain_type_fields(%__MODULE__{} = blocks, elixir_blocks) do
# credo:disable-for-next-line Credo.Check.Design.AliasUsage
EthereumJSONRPC.Zilliqa.Helper.extend_blocks_struct(blocks, elixir_blocks)
end
_ ->
defp extend_with_chain_type_fields(%__MODULE__{} = blocks, _elixir_blocks) do
blocks
end
end end
@doc """ @doc """
@ -134,6 +182,9 @@ defmodule EthereumJSONRPC.Blocks do
send_count: nil,\ send_count: nil,\
l1_block_number: nil,\ l1_block_number: nil,\
""" """
:zilliqa -> """
zilliqa_view: nil,\
"""
_ -> "" _ -> ""
end} end}
uncles: ["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273311"] uncles: ["0xe670ec64341771606e55d6b4ca35a1a6b75ee3d5145a99d05921026d15273311"]

@ -0,0 +1,20 @@
defmodule EthereumJSONRPC.Zilliqa do
@moduledoc """
Zilliqa type definitions.
"""
alias EthereumJSONRPC.Zilliqa.{
AggregateQuorumCertificate,
NestedQuorumCertificates,
QuorumCertificate
}
@type consensus_data_params :: %{
zilliqa_quorum_certificates_params: [QuorumCertificate.params()],
zilliqa_aggregate_quorum_certificates_params: [AggregateQuorumCertificate.params()],
zilliqa_nested_quorum_certificates_params: [NestedQuorumCertificates.params()]
}
@type bit_vector :: String.t()
@type validator_index :: non_neg_integer()
@type signers :: [validator_index()]
end

@ -0,0 +1,186 @@
defmodule EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate do
@moduledoc """
Represents an aggregate quorum certificate associated with the block.
"""
import EthereumJSONRPC, only: [quantity_to_integer: 1]
alias EthereumJSONRPC.Zilliqa.NestedQuorumCertificates
@type elixir :: %{
String.t() =>
EthereumJSONRPC.quantity()
| EthereumJSONRPC.hash()
| EthereumJSONRPC.Zilliqa.bit_vector()
| NestedQuorumCertificates.elixir()
}
@type t :: %__MODULE__{
block_hash: EthereumJSONRPC.hash(),
view: non_neg_integer(),
signature: EthereumJSONRPC.hash(),
quorum_certificates: NestedQuorumCertificates.t()
}
@type params :: %{
block_hash: EthereumJSONRPC.hash(),
view: non_neg_integer(),
signature: EthereumJSONRPC.hash()
}
defstruct [:block_hash, :view, :signature, :quorum_certificates]
@doc """
Decodes the JSON object returned by JSONRPC node into the `t:t/0` format.
## Examples
iex> aqc_json = %{
...> "cosigned" => "[0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]",
...> "quorum_certificates" => [
...> %{
...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa",
...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]",
...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> "view" => "0x115cc7"
...> },
...> %{
...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa",
...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]",
...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> "view" => "0x115cc7"
...> },
...> %{
...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa",
...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]",
...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> "view" => "0x115cc7"
...> },
...> %{
...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa",
...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]",
...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> "view" => "0x115cc7"
...> }
...> ],
...> "signature" => "0x820f591cd78b29a69ba25bc85c4327fa3b0adb61a73a4f0bd943b4ab0b97e061eae9ac032d19fbfab7efb89fac2454ab0b89fea83185c0dac749ff55b0e2c21535a2b712872491577728db868d11939461a6bfde0d94d238f46b643bbe19767e",
...> "view" => "0x115cca"
...> }
iex> block_hash = "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d"
iex> EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate.new(aqc_json, block_hash)
%EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate{
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137866,
signature: "0x820f591cd78b29a69ba25bc85c4327fa3b0adb61a73a4f0bd943b4ab0b97e061eae9ac032d19fbfab7efb89fac2454ab0b89fea83185c0dac749ff55b0e2c21535a2b712872491577728db868d11939461a6bfde0d94d238f46b643bbe19767e",
quorum_certificates: %EthereumJSONRPC.Zilliqa.NestedQuorumCertificates{
signers: [1, 2, 3, 8],
items: [
%EthereumJSONRPC.Zilliqa.QuorumCertificate{
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137863,
signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
signers: [0, 1, 3, 8]
},
%EthereumJSONRPC.Zilliqa.QuorumCertificate{
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137863,
signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
signers: [0, 1, 3, 8]
},
%EthereumJSONRPC.Zilliqa.QuorumCertificate{
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137863,
signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
signers: [0, 1, 3, 8]
},
%EthereumJSONRPC.Zilliqa.QuorumCertificate{
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137863,
signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
signers: [0, 1, 3, 8]
}
]
}
}
"""
@spec new(elixir(), EthereumJSONRPC.hash()) :: t()
def new(
%{
"view" => view,
"signature" => signature,
"cosigned" => bit_vector,
"quorum_certificates" => quorum_certificates
},
block_hash
) do
%__MODULE__{
block_hash: block_hash,
view: quantity_to_integer(view),
signature: signature,
quorum_certificates:
NestedQuorumCertificates.new(
quorum_certificates,
bit_vector,
block_hash
)
}
end
@doc """
Converts `t:t/0` format to params used in `Explorer.Chain`.
## Examples
iex> aggregate_quorum_certificate = %EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate{
...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
...> view: 1137866,
...> signature: "0x820f591cd78b29a69ba25bc85c4327fa3b0adb61a73a4f0bd943b4ab0b97e061eae9ac032d19fbfab7efb89fac2454ab0b89fea83185c0dac749ff55b0e2c21535a2b712872491577728db868d11939461a6bfde0d94d238f46b643bbe19767e",
...> quorum_certificates: %EthereumJSONRPC.Zilliqa.NestedQuorumCertificates{
...> signers: [1, 2, 3, 8],
...> items: [
...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{
...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
...> view: 1137863,
...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> signers: [0, 1, 3, 8]
...> },
...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{
...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
...> view: 1137863,
...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> signers: [0, 1, 3, 8]
...> },
...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{
...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
...> view: 1137863,
...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> signers: [0, 1, 3, 8]
...> },
...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{
...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
...> view: 1137863,
...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> signers: [0, 1, 3, 8]
...> }
...> ]
...> }
...> }
iex> EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate.to_params(aggregate_quorum_certificate)
%{
signature: "0x820f591cd78b29a69ba25bc85c4327fa3b0adb61a73a4f0bd943b4ab0b97e061eae9ac032d19fbfab7efb89fac2454ab0b89fea83185c0dac749ff55b0e2c21535a2b712872491577728db868d11939461a6bfde0d94d238f46b643bbe19767e",
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137866
}
"""
@spec to_params(t()) :: params()
def to_params(%__MODULE__{
block_hash: block_hash,
view: view,
signature: signature
}) do
%{
block_hash: block_hash,
view: view,
signature: signature
}
end
end

@ -0,0 +1,111 @@
defmodule EthereumJSONRPC.Zilliqa.Helper do
@moduledoc """
Helper functions for processing consensus data.
"""
alias EthereumJSONRPC.Zilliqa
alias EthereumJSONRPC.{Block, Blocks}
alias EthereumJSONRPC.Zilliqa.{
AggregateQuorumCertificate,
NestedQuorumCertificates,
QuorumCertificate
}
@initial_acc %{
zilliqa_quorum_certificates_params: [],
zilliqa_aggregate_quorum_certificates_params: [],
zilliqa_nested_quorum_certificates_params: []
}
@type consensus_data_params :: %{
zilliqa_quorum_certificates_params: [QuorumCertificate.params()],
zilliqa_aggregate_quorum_certificates_params: [AggregateQuorumCertificate.params()],
zilliqa_nested_quorum_certificates_params: [NestedQuorumCertificates.params()]
}
@spec extend_blocks_struct(Blocks.t(), Blocks.elixir()) :: Blocks.t()
def extend_blocks_struct(%Blocks{} = module, elixir_blocks) do
consensus_data_fields =
Enum.reduce(
elixir_blocks,
@initial_acc,
&reduce_to_consensus_data/2
)
Map.merge(module, consensus_data_fields)
end
@doc """
Converts a bit vector string to a list of indexes where the bit corresponding
to signing validator is 1.
## Examples
iex> bit_vector_to_signers("[1, 0, 1, 0]")
[0, 2]
iex> bit_vector_to_signers("[1, 1, 1, 1]")
[0, 1, 2, 3]
"""
@spec bit_vector_to_signers(Zilliqa.bit_vector()) :: Zilliqa.signers()
def bit_vector_to_signers(bit_vector_string) do
bit_vector_string
|> Jason.decode!()
|> Enum.with_index()
|> Enum.filter(fn {bit, _} -> bit == 1 end)
|> Enum.map(fn {_, index} -> index end)
end
@spec reduce_to_consensus_data(
Block.elixir(),
consensus_data_params()
) :: consensus_data_params()
defp reduce_to_consensus_data(
elixir_block,
%{
zilliqa_quorum_certificates_params: quorum_certificates_params_acc,
zilliqa_aggregate_quorum_certificates_params: aggregate_quorum_certificates_params_acc,
zilliqa_nested_quorum_certificates_params: aggregate_nested_quorum_certificates_params_acc
}
) do
quorum_certificates_map =
elixir_block
|> Block.elixir_to_zilliqa_quorum_certificate()
|> case do
nil ->
%{zilliqa_quorum_certificates_params: quorum_certificates_params_acc}
quorum_certificate ->
quorum_certificate_params = QuorumCertificate.to_params(quorum_certificate)
%{zilliqa_quorum_certificates_params: [quorum_certificate_params | quorum_certificates_params_acc]}
end
aggregated_quorum_certificate_map =
elixir_block
|> Block.elixir_to_zilliqa_aggregate_quorum_certificate()
|> case do
nil ->
%{
zilliqa_aggregate_quorum_certificates_params: aggregate_quorum_certificates_params_acc,
zilliqa_nested_quorum_certificates_params: aggregate_nested_quorum_certificates_params_acc
}
aggregate_quorum_certificates ->
aggregate_quorum_certificate_params =
AggregateQuorumCertificate.to_params(aggregate_quorum_certificates)
aggregate_nested_quorum_certificates_params =
NestedQuorumCertificates.to_params(aggregate_quorum_certificates.quorum_certificates)
%{
zilliqa_aggregate_quorum_certificates_params: [
aggregate_quorum_certificate_params | aggregate_quorum_certificates_params_acc
],
zilliqa_nested_quorum_certificates_params:
aggregate_nested_quorum_certificates_params ++ aggregate_nested_quorum_certificates_params_acc
}
end
Map.merge(quorum_certificates_map, aggregated_quorum_certificate_map)
end
end

@ -0,0 +1,187 @@
defmodule EthereumJSONRPC.Zilliqa.NestedQuorumCertificates do
@moduledoc """
Represents a list of quorum certificates that were proposed by different
validators in the aggregate quorum certificate.
"""
import EthereumJSONRPC.Zilliqa.Helper,
only: [bit_vector_to_signers: 1]
alias EthereumJSONRPC.Zilliqa
alias EthereumJSONRPC.Zilliqa.QuorumCertificate
defstruct [:signers, :items]
@type elixir :: [QuorumCertificate.elixir()]
@type t :: %__MODULE__{
signers: [non_neg_integer()],
items: [QuorumCertificate.t()]
}
@type params :: %{
proposed_by_validator_index: Zilliqa.validator_index(),
block_hash: EthereumJSONRPC.hash(),
view: non_neg_integer(),
signature: EthereumJSONRPC.hash(),
signers: Zilliqa.signers()
}
@doc """
Decodes the JSON object returned by JSONRPC node into the `t:t/0` format.
## Examples
iex> aqc_json = %{
...> "cosigned" => "[0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]",
...> "quorum_certificates" => [
...> %{
...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa",
...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]",
...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> "view" => "0x115cc7"
...> },
...> %{
...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa",
...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]",
...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> "view" => "0x115cc7"
...> },
...> %{
...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa",
...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]",
...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> "view" => "0x115cc7"
...> },
...> %{
...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa",
...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]",
...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> "view" => "0x115cc7"
...> }
...> ],
...> "signature" => "0x820f591cd78b29a69ba25bc85c4327fa3b0adb61a73a4f0bd943b4ab0b97e061eae9ac032d19fbfab7efb89fac2454ab0b89fea83185c0dac749ff55b0e2c21535a2b712872491577728db868d11939461a6bfde0d94d238f46b643bbe19767e",
...> "view" => "0x115cca"
...> }
iex> quorum_certificates = aqc_json["quorum_certificates"]
iex> bit_vector = aqc_json["cosigned"]
iex> block_hash = "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d"
iex> EthereumJSONRPC.Zilliqa.NestedQuorumCertificates.new(quorum_certificates, bit_vector, block_hash)
%EthereumJSONRPC.Zilliqa.NestedQuorumCertificates{
signers: [1, 2, 3, 8],
items: [
%EthereumJSONRPC.Zilliqa.QuorumCertificate{
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137863,
signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
signers: [0, 1, 3, 8]
},
%EthereumJSONRPC.Zilliqa.QuorumCertificate{
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137863,
signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
signers: [0, 1, 3, 8]
},
%EthereumJSONRPC.Zilliqa.QuorumCertificate{
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137863,
signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
signers: [0, 1, 3, 8]
},
%EthereumJSONRPC.Zilliqa.QuorumCertificate{
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137863,
signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
signers: [0, 1, 3, 8]
}
]
}
"""
@spec new([QuorumCertificate.elixir()], Zilliqa.bit_vector(), EthereumJSONRPC.hash()) :: t()
def new(quorum_certificates, bit_vector, block_hash) do
signers = bit_vector_to_signers(bit_vector)
items = Enum.map(quorum_certificates, &QuorumCertificate.new(&1, block_hash))
%__MODULE__{
signers: signers,
items: items
}
end
@doc """
Converts `t:t/0` format to params used in `Explorer.Chain`.
## Examples
iex> nested_quorum_certificates = %EthereumJSONRPC.Zilliqa.NestedQuorumCertificates{
...> signers: [1, 2, 3, 8],
...> items: [
...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{
...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
...> view: 1137863,
...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> signers: [0, 1, 3, 8]
...> },
...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{
...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
...> view: 1137863,
...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> signers: [0, 1, 3, 8]
...> },
...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{
...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
...> view: 1137863,
...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> signers: [0, 1, 3, 8]
...> },
...> %EthereumJSONRPC.Zilliqa.QuorumCertificate{
...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
...> view: 1137863,
...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> signers: [0, 1, 3, 8]
...> }
...> ]
...> }
iex> EthereumJSONRPC.Zilliqa.NestedQuorumCertificates.to_params(nested_quorum_certificates)
[
%{
signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137863,
signers: [0, 1, 3, 8],
proposed_by_validator_index: 1
},
%{
signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137863,
signers: [0, 1, 3, 8],
proposed_by_validator_index: 2
},
%{
signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137863,
signers: [0, 1, 3, 8],
proposed_by_validator_index: 3
},
%{
signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137863,
signers: [0, 1, 3, 8],
proposed_by_validator_index: 8
}
]
"""
@spec to_params(t()) :: [params()]
def to_params(%__MODULE__{signers: signers, items: items}) do
signers
|> Enum.zip(items)
|> Enum.map(fn {validator_index, cert} ->
cert
|> QuorumCertificate.to_params()
|> Map.put(:proposed_by_validator_index, validator_index)
end)
end
end

@ -0,0 +1,100 @@
defmodule EthereumJSONRPC.Zilliqa.QuorumCertificate do
@moduledoc """
Represents a quorum certificate associated with the block.
"""
import EthereumJSONRPC, only: [quantity_to_integer: 1]
import EthereumJSONRPC.Zilliqa.Helper,
only: [bit_vector_to_signers: 1]
@type elixir :: %{
String.t() => EthereumJSONRPC.quantity() | EthereumJSONRPC.Zilliqa.bit_vector() | EthereumJSONRPC.hash()
}
@type t :: %__MODULE__{
block_hash: EthereumJSONRPC.hash(),
view: non_neg_integer(),
signature: EthereumJSONRPC.hash(),
signers: [non_neg_integer()]
}
@type params :: %{
block_hash: EthereumJSONRPC.hash(),
view: non_neg_integer(),
signature: EthereumJSONRPC.hash(),
signers: [non_neg_integer()]
}
defstruct [:block_hash, :view, :signature, :signers]
@doc """
Decodes the JSON object returned by JSONRPC node into the `t:t/0` format.
## Examples
iex> qc_json = %{
...> "block_hash" => "0x4b8939a7fb0d7de4b288bafd4d5caa02f53abf3c1e348fca5038eebbf68248fa",
...> "cosigned" => "[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]",
...> "signature" => "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> "view" => "0x115cc7"
...> }
iex> block_hash = "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d"
iex> EthereumJSONRPC.Zilliqa.QuorumCertificate.new(qc_json, block_hash)
%EthereumJSONRPC.Zilliqa.QuorumCertificate{
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137863,
signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
signers: [0, 1, 3, 8]
}
"""
@spec new(elixir(), EthereumJSONRPC.hash()) :: t()
def new(
%{
"view" => view,
"cosigned" => bit_vector,
"signature" => signature
},
block_hash
) do
%__MODULE__{
block_hash: block_hash,
view: quantity_to_integer(view),
signature: signature,
signers: bit_vector_to_signers(bit_vector)
}
end
@doc """
Converts `t:t/0` format to params used in `Explorer.Chain`.
## Examples
iex> qc = %EthereumJSONRPC.Zilliqa.QuorumCertificate{
...> block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
...> view: 1137863,
...> signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
...> signers: [0, 1, 3, 8]
...> }
iex> EthereumJSONRPC.Zilliqa.QuorumCertificate.to_params(qc)
%{
block_hash: "0x9c8a047e40ea975cb14c5ccff232a2210fbf5d77b10c748b3559ada0d4adad9d",
view: 1137863,
signature: "0xa78c7f3e07e1df963ddeda17a1e5afd97c7c8a6fc8e0616249c22a2a1cc91f8eef6073cab8ba22b50cc7b38090f1ad9109473d30f24d57858d1f28c6679b3c4deeb800e5572b5e15604596594d506d3103a44d8b707da581f1a4b82310aeecb6",
signers: [0, 1, 3, 8]
}
"""
@spec to_params(t()) :: params()
def to_params(%__MODULE__{
block_hash: block_hash,
view: view,
signature: signature,
signers: signers
}) do
%{
block_hash: block_hash,
view: view,
signature: signature,
signers: signers
}
end
end

@ -1,4 +1,4 @@
defmodule EthereumJsonrpc.MixProject do defmodule EthereumJSONRPC.MixProject do
use Mix.Project use Mix.Project
def project do def project do

@ -85,6 +85,10 @@ defmodule EthereumJSONRPC.BlockTest do
l1_block_number: nil l1_block_number: nil
} }
:zilliqa ->
defp chain_type_fields,
do: %{zilliqa_view: nil}
_ -> _ ->
defp chain_type_fields, do: %{} defp chain_type_fields, do: %{}
end end

@ -0,0 +1,7 @@
defmodule EthereumJSONRPC.ZilliqaTest do
use ExUnit.Case, async: true
doctest EthereumJSONRPC.Zilliqa.AggregateQuorumCertificate
doctest EthereumJSONRPC.Zilliqa.NestedQuorumCertificates
doctest EthereumJSONRPC.Zilliqa.QuorumCertificate
end

@ -5,51 +5,35 @@ config :explorer, Explorer.Repo,
timeout: :timer.seconds(80), timeout: :timer.seconds(80),
migration_lock: nil migration_lock: nil
# Configure API database for repo <- [
config :explorer, Explorer.Repo.Replica1, timeout: :timer.seconds(80) # Configure API database
Explorer.Repo.Replica1,
# Configure Account database
config :explorer, Explorer.Repo.Account, timeout: :timer.seconds(80) # Feature dependent repos
Explorer.Repo.Account,
# Configure Optimism database Explorer.Repo.BridgedTokens,
config :explorer, Explorer.Repo.Optimism, timeout: :timer.seconds(80) Explorer.Repo.ShrunkInternalTransactions,
# Configure Polygon Edge database # Chain-type dependent repos
config :explorer, Explorer.Repo.PolygonEdge, timeout: :timer.seconds(80) Explorer.Repo.Arbitrum,
Explorer.Repo.Beacon,
# Configure Polygon zkEVM database Explorer.Repo.Blackfort,
config :explorer, Explorer.Repo.PolygonZkevm, timeout: :timer.seconds(80) Explorer.Repo.Celo,
Explorer.Repo.Filecoin,
# Configure Scroll database Explorer.Repo.Mud,
config :explorer, Explorer.Repo.Scroll, timeout: :timer.seconds(80) Explorer.Repo.Optimism,
Explorer.Repo.PolygonEdge,
# Configure ZkSync database Explorer.Repo.PolygonZkevm,
config :explorer, Explorer.Repo.ZkSync, timeout: :timer.seconds(80) Explorer.Repo.RSK,
Explorer.Repo.Scroll,
config :explorer, Explorer.Repo.Celo, timeout: :timer.seconds(80) Explorer.Repo.Shibarium,
Explorer.Repo.Stability,
config :explorer, Explorer.Repo.RSK, timeout: :timer.seconds(80) Explorer.Repo.Suave,
Explorer.Repo.Zilliqa,
config :explorer, Explorer.Repo.Shibarium, timeout: :timer.seconds(80) Explorer.Repo.ZkSync
] do
config :explorer, Explorer.Repo.Suave, timeout: :timer.seconds(80) config :explorer, repo, timeout: :timer.seconds(80)
end
config :explorer, Explorer.Repo.Beacon, timeout: :timer.seconds(80)
# Configure Arbitrum database
config :explorer, Explorer.Repo.Arbitrum, timeout: :timer.seconds(80)
config :explorer, Explorer.Repo.BridgedTokens, timeout: :timer.seconds(80)
config :explorer, Explorer.Repo.Filecoin, timeout: :timer.seconds(80)
config :explorer, Explorer.Repo.Stability, timeout: :timer.seconds(80)
config :explorer, Explorer.Repo.Mud, timeout: :timer.seconds(80)
config :explorer, Explorer.Repo.ShrunkInternalTransactions, timeout: :timer.seconds(80)
config :explorer, Explorer.Repo.Blackfort, timeout: :timer.seconds(80)
config :explorer, Explorer.Tracer, env: "dev", disabled?: true config :explorer, Explorer.Tracer, env: "dev", disabled?: true

@ -7,102 +7,38 @@ config :explorer, Explorer.Repo,
migration_lock: nil, migration_lock: nil,
ssl_opts: [verify: :verify_none] ssl_opts: [verify: :verify_none]
# Configures API the database for repo <- [
config :explorer, Explorer.Repo.Replica1, # Configures API the database
prepare: :unnamed, Explorer.Repo.Replica1,
timeout: :timer.seconds(60),
ssl_opts: [verify: :verify_none] # Feature dependent repos
Explorer.Repo.Account,
# Configures Account database Explorer.Repo.BridgedTokens,
config :explorer, Explorer.Repo.Account, Explorer.Repo.ShrunkInternalTransactions,
prepare: :unnamed,
timeout: :timer.seconds(60), # Chain-type dependent repos
ssl_opts: [verify: :verify_none] Explorer.Repo.Arbitrum,
Explorer.Repo.Beacon,
config :explorer, Explorer.Repo.Optimism, Explorer.Repo.Blackfort,
prepare: :unnamed, Explorer.Repo.Celo,
timeout: :timer.seconds(60), Explorer.Repo.Filecoin,
ssl_opts: [verify: :verify_none] Explorer.Repo.Mud,
Explorer.Repo.Optimism,
config :explorer, Explorer.Repo.PolygonEdge, Explorer.Repo.PolygonEdge,
prepare: :unnamed, Explorer.Repo.PolygonZkevm,
timeout: :timer.seconds(60), Explorer.Repo.RSK,
ssl_opts: [verify: :verify_none] Explorer.Repo.Scroll,
Explorer.Repo.Shibarium,
config :explorer, Explorer.Repo.PolygonZkevm, Explorer.Repo.Stability,
prepare: :unnamed, Explorer.Repo.Suave,
timeout: :timer.seconds(60), Explorer.Repo.Zilliqa,
ssl_opts: [verify: :verify_none] Explorer.Repo.ZkSync
] do
config :explorer, Explorer.Repo.Scroll, config :explorer, repo,
prepare: :unnamed, prepare: :unnamed,
timeout: :timer.seconds(60), timeout: :timer.seconds(60),
ssl_opts: [verify: :verify_none] ssl_opts: [verify: :verify_none]
end
config :explorer, Explorer.Repo.ZkSync,
prepare: :unnamed,
timeout: :timer.seconds(60),
ssl_opts: [verify: :verify_none]
config :explorer, Explorer.Repo.Celo,
prepare: :unnamed,
timeout: :timer.seconds(60),
ssl_opts: [verify: :verify_none]
config :explorer, Explorer.Repo.RSK,
prepare: :unnamed,
timeout: :timer.seconds(60),
ssl_opts: [verify: :verify_none]
config :explorer, Explorer.Repo.Shibarium,
prepare: :unnamed,
timeout: :timer.seconds(60),
ssl_opts: [verify: :verify_none]
config :explorer, Explorer.Repo.Suave,
prepare: :unnamed,
timeout: :timer.seconds(60),
ssl_opts: [verify: :verify_none]
config :explorer, Explorer.Repo.Beacon,
prepare: :unnamed,
timeout: :timer.seconds(60),
ssl_opts: [verify: :verify_none]
config :explorer, Explorer.Repo.Arbitrum,
prepare: :unnamed,
timeout: :timer.seconds(60),
ssl_opts: [verify: :verify_none]
config :explorer, Explorer.Repo.BridgedTokens,
prepare: :unnamed,
timeout: :timer.seconds(60),
ssl_opts: [verify: :verify_none]
config :explorer, Explorer.Repo.Filecoin,
prepare: :unnamed,
timeout: :timer.seconds(60),
ssl_opts: [verify: :verify_none]
config :explorer, Explorer.Repo.Stability,
prepare: :unnamed,
timeout: :timer.seconds(60),
ssl_opts: [verify: :verify_none]
config :explorer, Explorer.Repo.Mud,
prepare: :unnamed,
timeout: :timer.seconds(60),
ssl_opts: [verify: :verify_none]
config :explorer, Explorer.Repo.ShrunkInternalTransactions,
prepare: :unnamed,
timeout: :timer.seconds(60),
ssl_opts: [verify: :verify_none]
config :explorer, Explorer.Repo.Blackfort,
prepare: :unnamed,
timeout: :timer.seconds(60),
ssl_opts: [verify: :verify_none]
config :explorer, Explorer.Tracer, env: "production", disabled?: true config :explorer, Explorer.Tracer, env: "production", disabled?: true

@ -54,23 +54,24 @@ config :explorer, Explorer.Repo.Account,
log: false log: false
for repo <- [ for repo <- [
Explorer.Repo.Arbitrum,
Explorer.Repo.Beacon, Explorer.Repo.Beacon,
Explorer.Repo.Blackfort,
Explorer.Repo.BridgedTokens,
Explorer.Repo.Celo,
Explorer.Repo.Filecoin,
Explorer.Repo.Mud,
Explorer.Repo.Optimism, Explorer.Repo.Optimism,
Explorer.Repo.PolygonEdge, Explorer.Repo.PolygonEdge,
Explorer.Repo.PolygonZkevm, Explorer.Repo.PolygonZkevm,
Explorer.Repo.Scroll,
Explorer.Repo.ZkSync,
Explorer.Repo.Celo,
Explorer.Repo.RSK, Explorer.Repo.RSK,
Explorer.Repo.Scroll,
Explorer.Repo.Shibarium, Explorer.Repo.Shibarium,
Explorer.Repo.Suave,
Explorer.Repo.Arbitrum,
Explorer.Repo.BridgedTokens,
Explorer.Repo.Filecoin,
Explorer.Repo.Stability,
Explorer.Repo.Mud,
Explorer.Repo.ShrunkInternalTransactions, Explorer.Repo.ShrunkInternalTransactions,
Explorer.Repo.Blackfort Explorer.Repo.Stability,
Explorer.Repo.Suave,
Explorer.Repo.Zilliqa,
Explorer.Repo.ZkSync
] do ] do
config :explorer, repo, config :explorer, repo,
database: database, database: database,

@ -166,22 +166,23 @@ defmodule Explorer.Application do
defp repos_by_chain_type do defp repos_by_chain_type do
if Mix.env() == :test do if Mix.env() == :test do
[ [
Explorer.Repo.Arbitrum,
Explorer.Repo.Beacon, Explorer.Repo.Beacon,
Explorer.Repo.Blackfort,
Explorer.Repo.BridgedTokens,
Explorer.Repo.Celo,
Explorer.Repo.Filecoin,
Explorer.Repo.Optimism, Explorer.Repo.Optimism,
Explorer.Repo.PolygonEdge, Explorer.Repo.PolygonEdge,
Explorer.Repo.PolygonZkevm, Explorer.Repo.PolygonZkevm,
Explorer.Repo.Scroll,
Explorer.Repo.ZkSync,
Explorer.Repo.Celo,
Explorer.Repo.RSK, Explorer.Repo.RSK,
Explorer.Repo.Scroll,
Explorer.Repo.Shibarium, Explorer.Repo.Shibarium,
Explorer.Repo.Suave,
Explorer.Repo.Arbitrum,
Explorer.Repo.BridgedTokens,
Explorer.Repo.Filecoin,
Explorer.Repo.Stability,
Explorer.Repo.ShrunkInternalTransactions, Explorer.Repo.ShrunkInternalTransactions,
Explorer.Repo.Blackfort Explorer.Repo.Stability,
Explorer.Repo.Suave,
Explorer.Repo.Zilliqa,
Explorer.Repo.ZkSync
] ]
else else
[] []

@ -19,6 +19,8 @@ defmodule Explorer.Chain.Block.Schema do
alias Explorer.Chain.Block.{Reward, SecondDegreeRelation} alias Explorer.Chain.Block.{Reward, SecondDegreeRelation}
alias Explorer.Chain.Celo.EpochReward, as: CeloEpochReward alias Explorer.Chain.Celo.EpochReward, as: CeloEpochReward
alias Explorer.Chain.Optimism.TransactionBatch, as: OptimismTransactionBatch alias Explorer.Chain.Optimism.TransactionBatch, as: OptimismTransactionBatch
alias Explorer.Chain.Zilliqa.AggregateQuorumCertificate, as: ZilliqaAggregateQuorumCertificate
alias Explorer.Chain.Zilliqa.QuorumCertificate, as: ZilliqaQuorumCertificate
alias Explorer.Chain.ZkSync.BatchBlock, as: ZkSyncBatchBlock alias Explorer.Chain.ZkSync.BatchBlock, as: ZkSyncBatchBlock
@chain_type_fields (case Application.compile_env(:explorer, :chain_type) do @chain_type_fields (case Application.compile_env(:explorer, :chain_type) do
@ -106,6 +108,24 @@ defmodule Explorer.Chain.Block.Schema do
2 2
) )
:zilliqa ->
elem(
quote do
field(:zilliqa_view, :integer)
has_one(:zilliqa_quorum_certificate, ZilliqaQuorumCertificate,
foreign_key: :block_hash,
references: :hash
)
has_one(:zilliqa_aggregate_quorum_certificate, ZilliqaAggregateQuorumCertificate,
foreign_key: :block_hash,
references: :hash
)
end,
2
)
_ -> _ ->
[] []
end) end)
@ -183,6 +203,9 @@ defmodule Explorer.Chain.Block do
:arbitrum -> :arbitrum ->
~w(send_count send_root l1_block_number)a ~w(send_count send_root l1_block_number)a
:zilliqa ->
~w(zilliqa_view)a
_ -> _ ->
~w()a ~w()a
end) end)

@ -0,0 +1,79 @@
defmodule Explorer.Chain.Import.Runner.Zilliqa.AggregateQuorumCertificates do
@moduledoc """
Bulk imports `t:Explorer.Chain.Zilliqa.AggregateQuorumCertificate.t/0`.
"""
require Ecto.Query
alias Ecto.{Changeset, Multi, Repo}
alias Explorer.Chain.Import
alias Explorer.Chain.Zilliqa.AggregateQuorumCertificate
alias Explorer.Prometheus.Instrumenter
@behaviour Import.Runner
# milliseconds
@timeout 60_000
@type imported :: [AggregateQuorumCertificate.t()]
@impl Import.Runner
def ecto_schema_module, do: AggregateQuorumCertificate
@impl Import.Runner
def option_key, do: :zilliqa_aggregate_quorum_certificates
@impl Import.Runner
@spec imported_table_row() :: %{:value_description => binary(), :value_type => binary()}
def imported_table_row do
%{
value_type: "[#{ecto_schema_module()}.t()]",
value_description: "List of `t:#{ecto_schema_module()}.t/0`s"
}
end
@impl Import.Runner
@spec run(Multi.t(), list(), map()) :: Multi.t()
def run(multi, changes_list, %{timestamps: timestamps} = options) do
insert_options =
options
|> Map.get(option_key(), %{})
|> Map.take(~w(on_conflict timeout)a)
|> Map.put_new(:timeout, @timeout)
|> Map.put(:timestamps, timestamps)
Multi.run(multi, :insert_zilliqa_aggregate_quorum_certificates, fn repo, _ ->
Instrumenter.block_import_stage_runner(
fn -> insert(repo, changes_list, insert_options) end,
:block_referencing,
:zilliqa_aggregate_quorum_certificates,
:zilliqa_aggregate_quorum_certificates
)
end)
end
@impl Import.Runner
def timeout, do: @timeout
@spec insert(Repo.t(), [map()], %{required(:timeout) => timeout(), required(:timestamps) => Import.timestamps()}) ::
{:ok, [AggregateQuorumCertificate.t()]}
| {:error, [Changeset.t()]}
def insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = _options) when is_list(changes_list) do
# Enforce Zilliqa.AggregateQuorumCertificate ShareLocks order (see docs: sharelock.md)
ordered_changes_list = Enum.sort_by(changes_list, & &1.block_hash)
{:ok, inserted} =
Import.insert_changes_list(
repo,
ordered_changes_list,
for: AggregateQuorumCertificate,
returning: true,
timeout: timeout,
timestamps: timestamps,
conflict_target: :block_hash,
on_conflict: :nothing
)
{:ok, inserted}
end
end

@ -0,0 +1,86 @@
defmodule Explorer.Chain.Import.Runner.Zilliqa.NestedQuorumCertificates do
@moduledoc """
Bulk imports `t:Explorer.Chain.Zilliqa.NestedQuorumCertificate.t/0`.
"""
require Ecto.Query
alias Ecto.{Changeset, Multi, Repo}
alias Explorer.Chain.Import
alias Explorer.Chain.Zilliqa.NestedQuorumCertificate
alias Explorer.Prometheus.Instrumenter
@behaviour Import.Runner
# milliseconds
@timeout 60_000
@type imported :: [NestedQuorumCertificate.t()]
@impl Import.Runner
def ecto_schema_module, do: NestedQuorumCertificate
@impl Import.Runner
def option_key, do: :zilliqa_nested_quorum_certificates
@impl Import.Runner
@spec imported_table_row() :: %{:value_description => binary(), :value_type => binary()}
def imported_table_row do
%{
value_type: "[#{ecto_schema_module()}.t()]",
value_description: "List of `t:#{ecto_schema_module()}.t/0`s"
}
end
@impl Import.Runner
@spec run(Multi.t(), list(), map()) :: Multi.t()
def run(multi, changes_list, %{timestamps: timestamps} = options) do
insert_options =
options
|> Map.get(option_key(), %{})
|> Map.take(~w(on_conflict timeout)a)
|> Map.put_new(:timeout, @timeout)
|> Map.put(:timestamps, timestamps)
Multi.run(multi, :insert_zilliqa_nested_quorum_certificates, fn repo, _ ->
Instrumenter.block_import_stage_runner(
fn -> insert(repo, changes_list, insert_options) end,
:block_referencing,
:zilliqa_nested_quorum_certificates,
:zilliqa_nested_quorum_certificates
)
end)
end
@impl Import.Runner
def timeout, do: @timeout
@spec insert(Repo.t(), [map()], %{required(:timeout) => timeout(), required(:timestamps) => Import.timestamps()}) ::
{:ok, [NestedQuorumCertificate.t()]}
| {:error, [Changeset.t()]}
def insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = _options) when is_list(changes_list) do
# Enforce Zilliqa.NestedQuorumCertificate ShareLocks order (see docs: sharelock.md)
ordered_changes_list =
Enum.sort_by(
changes_list,
&{&1.block_hash, &1.proposed_by_validator_index}
)
{:ok, inserted} =
Import.insert_changes_list(
repo,
ordered_changes_list,
for: NestedQuorumCertificate,
returning: true,
timeout: timeout,
timestamps: timestamps,
conflict_target: [
:block_hash,
:proposed_by_validator_index
],
on_conflict: :nothing
)
{:ok, inserted}
end
end

@ -0,0 +1,79 @@
defmodule Explorer.Chain.Import.Runner.Zilliqa.QuorumCertificates do
@moduledoc """
Bulk imports `t:Explorer.Chain.Zilliqa.QuorumCertificate.t/0`.
"""
require Ecto.Query
alias Ecto.{Changeset, Multi, Repo}
alias Explorer.Chain.Import
alias Explorer.Chain.Zilliqa.QuorumCertificate
alias Explorer.Prometheus.Instrumenter
@behaviour Import.Runner
# milliseconds
@timeout 60_000
@type imported :: [QuorumCertificate.t()]
@impl Import.Runner
def ecto_schema_module, do: QuorumCertificate
@impl Import.Runner
def option_key, do: :zilliqa_quorum_certificates
@impl Import.Runner
@spec imported_table_row() :: %{:value_description => binary(), :value_type => binary()}
def imported_table_row do
%{
value_type: "[#{ecto_schema_module()}.t()]",
value_description: "List of `t:#{ecto_schema_module()}.t/0`s"
}
end
@impl Import.Runner
@spec run(Multi.t(), list(), map()) :: Multi.t()
def run(multi, changes_list, %{timestamps: timestamps} = options) do
insert_options =
options
|> Map.get(option_key(), %{})
|> Map.take(~w(on_conflict timeout)a)
|> Map.put_new(:timeout, @timeout)
|> Map.put(:timestamps, timestamps)
Multi.run(multi, :insert_zilliqa_quorum_certificates, fn repo, _ ->
Instrumenter.block_import_stage_runner(
fn -> insert(repo, changes_list, insert_options) end,
:block_referencing,
:zilliqa_quorum_certificates,
:zilliqa_quorum_certificates
)
end)
end
@impl Import.Runner
def timeout, do: @timeout
@spec insert(Repo.t(), [map()], %{required(:timeout) => timeout(), required(:timestamps) => Import.timestamps()}) ::
{:ok, [QuorumCertificate.t()]}
| {:error, [Changeset.t()]}
def insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = _options) when is_list(changes_list) do
# Enforce Zilliqa.QuorumCertificate ShareLocks order (see docs: sharelock.md)
ordered_changes_list = Enum.sort_by(changes_list, & &1.block_hash)
{:ok, inserted} =
Import.insert_changes_list(
repo,
ordered_changes_list,
for: QuorumCertificate,
returning: true,
timeout: timeout,
timestamps: timestamps,
conflict_target: :block_hash,
on_conflict: :nothing
)
{:ok, inserted}
end
end

@ -62,6 +62,11 @@ defmodule Explorer.Chain.Import.Stage.ChainTypeSpecific do
Runner.Celo.ValidatorGroupVotes, Runner.Celo.ValidatorGroupVotes,
Runner.Celo.ElectionRewards, Runner.Celo.ElectionRewards,
Runner.Celo.EpochRewards Runner.Celo.EpochRewards
],
zilliqa: [
Runner.Zilliqa.AggregateQuorumCertificates,
Runner.Zilliqa.NestedQuorumCertificates,
Runner.Zilliqa.QuorumCertificates
] ]
} }

@ -0,0 +1,64 @@
defmodule Explorer.Chain.Zilliqa.AggregateQuorumCertificate do
@moduledoc """
A stored representation of a Zilliqa aggregate quorum certificate in the
context of PBFT consensus.
In PBFT (Practical Byzantine Fault Tolerance) consensus, an aggregate quorum
certificate combines multiple quorum certificates into one, providing proof
that a block has been approved across multiple consensus rounds or by multiple
subsets of validators. It includes aggregated signatures and references to
nested quorum certificates.
Changes in the schema should be reflected in the bulk import module:
- `Explorer.Chain.Import.Runner.Zilliqa.AggregateQuorumCertificate`
"""
use Explorer.Schema
alias Explorer.Chain.{Block, Hash}
alias Explorer.Chain.Zilliqa.Hash.Signature, as: SignatureHash
alias Explorer.Chain.Zilliqa.NestedQuorumCertificate
@required_attrs ~w(block_hash view signature)a
@typedoc """
* `view` - the view number associated with the quorum certificate, indicating
the consensus round.
* `signature` - the aggregated BLS (BonehLynnShacham) signature representing
the validators' agreement.
* `block_hash` - the hash of the block associated with this aggregate quorum
certificate.
* `nested_quorum_certificates` - the list of nested quorum certificates that
are part of this aggregate.
"""
@primary_key false
typed_schema "zilliqa_aggregate_quorum_certificates" do
field(:view, :integer)
field(:signature, SignatureHash)
belongs_to(:block, Block,
foreign_key: :block_hash,
primary_key: true,
references: :hash,
type: Hash.Full,
null: false
)
has_many(
:nested_quorum_certificates,
NestedQuorumCertificate,
foreign_key: :block_hash,
references: :block_hash
)
timestamps()
end
@spec changeset(Ecto.Schema.t(), map()) :: Ecto.Changeset.t()
def changeset(%__MODULE__{} = cert, attrs) do
cert
|> cast(attrs, @required_attrs)
|> validate_required(@required_attrs)
|> foreign_key_constraint(:block_hash)
|> unique_constraint(:block_hash, name: :aggregate_quorum_certificates_pkey)
end
end

@ -0,0 +1,60 @@
defmodule Explorer.Chain.Zilliqa.Hash.Signature do
@moduledoc """
A 96-byte BLS signature of the supermajority of the validators.
"""
alias Explorer.Chain.Hash
use Ecto.Type
@behaviour Hash
@byte_count 96
@typedoc """
A #{@byte_count}-byte BLS signature hash of the
`t:Explorer.Chain.Zilliqa.QuorumCertificate.t/0` or
`t:Explorer.Chain.Zilliqa.AggregateQuorumCertificate.t/0` or
`t:Explorer.Chain.Zilliqa.NestedQuorumCertificate.t/0`.
"""
@type t :: %Hash{byte_count: unquote(@byte_count), bytes: <<_::unquote(@byte_count * Hash.bits_per_byte())>>}
@doc """
Casts a term to a `t`.
"""
@impl Ecto.Type
@spec cast(term()) :: {:ok, t()} | :error
def cast(term) do
Hash.cast(__MODULE__, term)
end
@doc """
Dumps a `t` to a binary.
"""
@impl Ecto.Type
@spec dump(term()) :: {:ok, binary} | :error
def dump(term) do
Hash.dump(__MODULE__, term)
end
@doc """
Loads a binary to a `t`.
"""
@impl Ecto.Type
@spec load(term()) :: {:ok, t()} | :error
def load(term) do
Hash.load(__MODULE__, term)
end
@doc """
Returns the type of the `t`.
"""
@impl Ecto.Type
@spec type() :: :binary
def type, do: :binary
@doc """
Returns the byte count of the `t`.
"""
@impl Hash
def byte_count, do: @byte_count
end

@ -0,0 +1,64 @@
defmodule Explorer.Chain.Zilliqa.NestedQuorumCertificate do
@moduledoc """
A stored representation of a nested quorum certificate in Zilliqa's PBFT
consensus.
In Zilliqa's PBFT (Practical Byzantine Fault Tolerance) consensus, an
aggregate quorum certificate may include multiple nested quorum certificates.
Each nested quorum certificate represents a quorum certificate proposed by a
specific validator and contains its own aggregated signatures and participant
information.
Changes in the schema should be reflected in the bulk import module:
- `Explorer.Chain.Import.Runner.Zilliqa.AggregateQuorumCertificate`
"""
use Explorer.Schema
alias Explorer.Chain.Hash
alias Explorer.Chain.Zilliqa.AggregateQuorumCertificate
alias Explorer.Chain.Zilliqa.Hash.Signature, as: SignatureHash
@required_attrs ~w(block_hash proposed_by_validator_index view signature signers)a
@typedoc """
* `proposed_by_validator_index` - the index of the validator who proposed this
nested quorum certificate.
* `view` - the view number associated with the quorum certificate, indicating
the consensus round.
* `signature` - the aggregated BLS (BonehLynnShacham) signature representing
the validators' agreement.
* `signers` - the array of integers representing the indices of validators who
participated in the quorum (as indicated by the `cosigned` bit vector).
* `block_hash` - the hash of the block associated with the aggregate quorum
certificate to which this nested quorum certificate belongs.
"""
@primary_key false
typed_schema "zilliqa_nested_quorum_certificates" do
field(:proposed_by_validator_index, :integer, primary_key: true)
field(:view, :integer)
field(:signature, SignatureHash)
field(:signers, {:array, :integer})
belongs_to(:aggregate_quorum_certificate, AggregateQuorumCertificate,
foreign_key: :block_hash,
references: :block_hash,
primary_key: true,
type: Hash.Full,
null: false
)
timestamps()
end
@spec changeset(Ecto.Schema.t(), map()) :: Ecto.Changeset.t()
def changeset(%__MODULE__{} = cert, attrs) do
cert
|> cast(attrs, @required_attrs)
|> validate_required(@required_attrs)
|> foreign_key_constraint(:block_hash)
|> unique_constraint(
[:proposed_by_validator_index, :block_hash],
name: :nested_quorum_certificates_pkey
)
end
end

@ -0,0 +1,56 @@
defmodule Explorer.Chain.Zilliqa.QuorumCertificate do
@moduledoc """
A stored representation of a Zilliqa quorum certificate in the context of PBFT
consensus.
In PBFT (Practical Byzantine Fault Tolerance) consensus, a quorum certificate
is a data structure that serves as proof that a block has been approved by a
supermajority of validators. It includes aggregated signatures and a bitmap
indicating which validators participated in the consensus.
Changes in the schema should be reflected in the bulk import module:
- `Explorer.Chain.Import.Runner.Zilliqa.AggregateQuorumCertificate`
"""
use Explorer.Schema
alias Explorer.Chain.{Block, Hash}
alias Explorer.Chain.Zilliqa.Hash.Signature, as: SignatureHash
@required_attrs ~w(block_hash view signature signers)a
@typedoc """
* `view` - the view number associated with the quorum certificate, indicating
the consensus round.
* `signature` - the aggregated BLS (BonehLynnShacham) signature representing
the validators' agreement.
* `signers` - the array of integers representing the indices of validators who
participated in the quorum (as indicated by the `cosigned` bit vector).
* `block_hash` - the hash of the block associated with this quorum
certificate.
"""
@primary_key false
typed_schema "zilliqa_quorum_certificates" do
field(:view, :integer)
field(:signature, SignatureHash)
field(:signers, {:array, :integer})
belongs_to(:block, Block,
foreign_key: :block_hash,
primary_key: true,
references: :hash,
type: Hash.Full,
null: false
)
timestamps()
end
@spec changeset(Ecto.Schema.t(), map()) :: Ecto.Schema.t()
def changeset(%__MODULE__{} = cert, attrs) do
cert
|> cast(attrs, @required_attrs)
|> validate_required(@required_attrs)
|> foreign_key_constraint(:block_hash)
|> unique_constraint(:block_hash, name: :quorum_certificates_pkey)
end
end

@ -127,183 +127,38 @@ defmodule Explorer.Repo do
end end
end end
defmodule Account do for repo <- [
use Ecto.Repo, # Feature dependent repos
otp_app: :explorer, Explorer.Repo.Account,
adapter: Ecto.Adapters.Postgres Explorer.Repo.BridgedTokens,
Explorer.Repo.ShrunkInternalTransactions,
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts) # Chain-type dependent repos
end Explorer.Repo.Arbitrum,
end Explorer.Repo.Beacon,
Explorer.Repo.Blackfort,
defmodule Optimism do Explorer.Repo.Celo,
use Ecto.Repo, Explorer.Repo.Filecoin,
otp_app: :explorer, Explorer.Repo.Mud,
adapter: Ecto.Adapters.Postgres Explorer.Repo.Optimism,
Explorer.Repo.PolygonEdge,
def init(_, opts) do Explorer.Repo.PolygonZkevm,
ConfigHelper.init_repo_module(__MODULE__, opts) Explorer.Repo.RSK,
end Explorer.Repo.Scroll,
end Explorer.Repo.Shibarium,
Explorer.Repo.Stability,
defmodule PolygonEdge do Explorer.Repo.Suave,
use Ecto.Repo, Explorer.Repo.Zilliqa,
otp_app: :explorer, Explorer.Repo.ZkSync
adapter: Ecto.Adapters.Postgres ] do
defmodule repo do
def init(_, opts) do use Ecto.Repo,
ConfigHelper.init_repo_module(__MODULE__, opts) otp_app: :explorer,
end adapter: Ecto.Adapters.Postgres
end
def init(_, opts) do
defmodule PolygonZkevm do ConfigHelper.init_repo_module(__MODULE__, opts)
use Ecto.Repo, end
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts)
end
end
defmodule Scroll do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts)
end
end
defmodule ZkSync do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts)
end
end
defmodule Celo do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts)
end
end
defmodule RSK do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts)
end
end
defmodule Shibarium do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts)
end
end
defmodule Suave do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts)
end
end
defmodule Beacon do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts)
end
end
defmodule Arbitrum do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts)
end
end
defmodule BridgedTokens do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts)
end
end
defmodule Filecoin do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts)
end
end
defmodule Stability do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts)
end
end
defmodule Mud do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts)
end
end
defmodule ShrunkInternalTransactions do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts)
end
end
defmodule Blackfort do
use Ecto.Repo,
otp_app: :explorer,
adapter: Ecto.Adapters.Postgres
def init(_, opts) do
ConfigHelper.init_repo_module(__MODULE__, opts)
end end
end end
end end

@ -0,0 +1,20 @@
defmodule Explorer.Repo.Zilliqa.Migrations.CreateQuorumCertificate do
use Ecto.Migration
def change do
create table(:zilliqa_quorum_certificates, primary_key: false) do
add(
:block_hash,
references(:blocks, column: :hash, type: :bytea, on_delete: :delete_all),
null: false,
primary_key: true
)
add(:view, :integer, null: false)
add(:signature, :binary, null: false)
add(:signers, {:array, :smallint}, null: false)
timestamps()
end
end
end

@ -0,0 +1,19 @@
defmodule Explorer.Repo.Zilliqa.Migrations.CreateAggregateQuorumCertificate do
use Ecto.Migration
def change do
create table(:zilliqa_aggregate_quorum_certificates, primary_key: false) do
add(
:block_hash,
references(:blocks, column: :hash, type: :bytea, on_delete: :delete_all),
null: false,
primary_key: true
)
add(:view, :integer, null: false)
add(:signature, :binary, null: false)
timestamps()
end
end
end

@ -0,0 +1,21 @@
defmodule Explorer.Repo.Zilliqa.Migrations.CreateNestedQuorumCertificate do
use Ecto.Migration
def change do
create table(:zilliqa_nested_quorum_certificates, primary_key: false) do
add(
:block_hash,
references(:blocks, column: :hash, type: :bytea, on_delete: :delete_all),
null: false,
primary_key: true
)
add(:proposed_by_validator_index, :smallint, primary_key: true)
add(:view, :integer, null: false)
add(:signature, :binary, null: false)
add(:signers, {:array, :smallint}, null: false)
timestamps()
end
end
end

@ -0,0 +1,9 @@
defmodule Explorer.Repo.Zilliqa.Migrations.AddViewToBlock do
use Ecto.Migration
def change do
alter table(:blocks) do
add(:zilliqa_view, :integer)
end
end
end

@ -32,6 +32,8 @@ defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogsTest do
updated_logs = updated_logs =
Repo.all(Log |> where([log], log.block_number == ^block.number) |> order_by([log], asc: log.index)) Repo.all(Log |> where([log], log.block_number == ^block.number) |> order_by([log], asc: log.index))
Process.sleep(300)
assert match?( assert match?(
[ [
%{index: 0, data: %Explorer.Chain.Data{bytes: <<2>>}}, %{index: 0, data: %Explorer.Chain.Data{bytes: <<2>>}},
@ -108,6 +110,8 @@ defmodule Explorer.Migrator.SanitizeDuplicatedLogIndexLogsTest do
assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed" assert MigrationStatus.get_status("sanitize_duplicated_log_index_logs") == "completed"
assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true assert BackgroundMigrations.get_sanitize_duplicated_log_index_logs_finished() == true
Process.sleep(300)
updated_logs = updated_logs =
Repo.all(Log |> where([log], log.block_number == ^block.number) |> order_by([log], asc: log.index)) Repo.all(Log |> where([log], log.block_number == ^block.number) |> order_by([log], asc: log.index))

@ -145,7 +145,7 @@ defmodule Indexer.Block.Fetcher do
additional_options \\ %{} additional_options \\ %{}
) )
when callback_module != nil do when callback_module != nil do
{fetch_time, fetched_blocks} = {fetch_time, fetch_result} =
:timer.tc(fn -> EthereumJSONRPC.fetch_blocks_by_range(range, json_rpc_named_arguments) end) :timer.tc(fn -> EthereumJSONRPC.fetch_blocks_by_range(range, json_rpc_named_arguments) end)
with {:blocks, with {:blocks,
@ -156,7 +156,7 @@ defmodule Indexer.Block.Fetcher do
withdrawals_params: withdrawals_params, withdrawals_params: withdrawals_params,
block_second_degree_relations_params: block_second_degree_relations_params, block_second_degree_relations_params: block_second_degree_relations_params,
errors: blocks_errors errors: blocks_errors
}}} <- {:blocks, fetched_blocks}, } = fetched_blocks}} <- {:blocks, fetch_result},
blocks = TransformBlocks.transform_blocks(blocks_params), blocks = TransformBlocks.transform_blocks(blocks_params),
{:receipts, {:ok, receipt_params}} <- {:receipts, Receipts.fetch(state, transactions_params_without_receipts)}, {:receipts, {:ok, receipt_params}} <- {:receipts, Receipts.fetch(state, transactions_params_without_receipts)},
%{logs: receipt_logs, receipts: receipts} = receipt_params, %{logs: receipt_logs, receipts: receipts} = receipt_params,
@ -247,17 +247,19 @@ defmodule Indexer.Block.Fetcher do
token_instances: %{params: token_instances}, token_instances: %{params: token_instances},
signed_authorizations: %{params: SignedAuthorizations.parse(transactions_with_receipts)} signed_authorizations: %{params: SignedAuthorizations.parse(transactions_with_receipts)}
}, },
chain_type_import_options = %{ chain_type_import_options =
transactions_with_receipts: transactions_with_receipts, %{
optimism_withdrawals: optimism_withdrawals, transactions_with_receipts: transactions_with_receipts,
polygon_edge_withdrawals: polygon_edge_withdrawals, optimism_withdrawals: optimism_withdrawals,
polygon_edge_deposit_executes: polygon_edge_deposit_executes, polygon_edge_withdrawals: polygon_edge_withdrawals,
polygon_zkevm_bridge_operations: polygon_zkevm_bridge_operations, polygon_edge_deposit_executes: polygon_edge_deposit_executes,
scroll_l1_fee_params: scroll_l1_fee_params, polygon_zkevm_bridge_operations: polygon_zkevm_bridge_operations,
shibarium_bridge_operations: shibarium_bridge_operations, scroll_l1_fee_params: scroll_l1_fee_params,
celo_gas_tokens: celo_gas_tokens, shibarium_bridge_operations: shibarium_bridge_operations,
arbitrum_messages: arbitrum_xlevel_messages celo_gas_tokens: celo_gas_tokens,
}, arbitrum_messages: arbitrum_xlevel_messages
}
|> extend_with_zilliqa_import_options(fetched_blocks),
{:ok, inserted} <- {:ok, inserted} <-
__MODULE__.import( __MODULE__.import(
state, state,
@ -350,12 +352,33 @@ defmodule Indexer.Block.Fetcher do
|> Map.put_new(:arbitrum_messages, %{params: arbitrum_xlevel_messages}) |> Map.put_new(:arbitrum_messages, %{params: arbitrum_xlevel_messages})
end end
:zilliqa ->
defp import_options(basic_import_options, %{
zilliqa_quorum_certificates: zilliqa_quorum_certificates,
zilliqa_aggregate_quorum_certificates: zilliqa_aggregate_quorum_certificates,
zilliqa_nested_quorum_certificates: zilliqa_nested_quorum_certificates
}) do
basic_import_options
|> Map.put_new(:zilliqa_quorum_certificates, %{params: zilliqa_quorum_certificates})
|> Map.put_new(:zilliqa_aggregate_quorum_certificates, %{params: zilliqa_aggregate_quorum_certificates})
|> Map.put_new(:zilliqa_nested_quorum_certificates, %{params: zilliqa_nested_quorum_certificates})
end
_ -> _ ->
defp import_options(basic_import_options, _) do defp import_options(basic_import_options, _) do
basic_import_options basic_import_options
end end
end end
defp extend_with_zilliqa_import_options(chain_type_import_options, fetched_blocks) do
chain_type_import_options
|> Map.merge(%{
zilliqa_quorum_certificates: Map.get(fetched_blocks, :zilliqa_quorum_certificates_params, []),
zilliqa_aggregate_quorum_certificates: Map.get(fetched_blocks, :zilliqa_aggregate_quorum_certificates_params, []),
zilliqa_nested_quorum_certificates: Map.get(fetched_blocks, :zilliqa_nested_quorum_certificates_params, [])
})
end
defp update_block_cache([]), do: :ok defp update_block_cache([]), do: :ok
defp update_block_cache(blocks) when is_list(blocks) do defp update_block_cache(blocks) when is_list(blocks) do

@ -9,24 +9,27 @@ defmodule ConfigHelper do
def repos do def repos do
base_repos = [Explorer.Repo, Explorer.Repo.Account] base_repos = [Explorer.Repo, Explorer.Repo.Account]
repos = chain_type_repo =
case chain_type() do %{
:ethereum -> base_repos ++ [Explorer.Repo.Beacon] arbitrum: Explorer.Repo.Arbitrum,
:optimism -> base_repos ++ [Explorer.Repo.Optimism] blackfort: Explorer.Repo.Blackfort,
:polygon_edge -> base_repos ++ [Explorer.Repo.PolygonEdge] celo: Explorer.Repo.Celo,
:polygon_zkevm -> base_repos ++ [Explorer.Repo.PolygonZkevm] ethereum: Explorer.Repo.Beacon,
:rsk -> base_repos ++ [Explorer.Repo.RSK] filecoin: Explorer.Repo.Filecoin,
:scroll -> base_repos ++ [Explorer.Repo.Scroll] optimism: Explorer.Repo.Optimism,
:shibarium -> base_repos ++ [Explorer.Repo.Shibarium] polygon_edge: Explorer.Repo.PolygonEdge,
:suave -> base_repos ++ [Explorer.Repo.Suave] polygon_zkevm: Explorer.Repo.PolygonZkevm,
:filecoin -> base_repos ++ [Explorer.Repo.Filecoin] rsk: Explorer.Repo.RSK,
:stability -> base_repos ++ [Explorer.Repo.Stability] scroll: Explorer.Repo.Scroll,
:zksync -> base_repos ++ [Explorer.Repo.ZkSync] shibarium: Explorer.Repo.Shibarium,
:celo -> base_repos ++ [Explorer.Repo.Celo] stability: Explorer.Repo.Stability,
:arbitrum -> base_repos ++ [Explorer.Repo.Arbitrum] suave: Explorer.Repo.Suave,
:blackfort -> base_repos ++ [Explorer.Repo.Blackfort] zilliqa: Explorer.Repo.Zilliqa,
_ -> base_repos zksync: Explorer.Repo.ZkSync
end }
|> Map.get(chain_type())
chain_type_repos = (chain_type_repo && [chain_type_repo]) || []
ext_repos = ext_repos =
[ [
@ -37,7 +40,7 @@ defmodule ConfigHelper do
|> Enum.filter(&elem(&1, 0)) |> Enum.filter(&elem(&1, 0))
|> Enum.map(&elem(&1, 1)) |> Enum.map(&elem(&1, 1))
repos ++ ext_repos base_repos ++ chain_type_repos ++ ext_repos
end end
@spec hackney_options() :: any() @spec hackney_options() :: any()
@ -305,6 +308,8 @@ defmodule ConfigHelper do
@supported_chain_types [ @supported_chain_types [
"default", "default",
"arbitrum", "arbitrum",
"blackfort",
"celo",
"ethereum", "ethereum",
"filecoin", "filecoin",
"optimism", "optimism",
@ -316,9 +321,8 @@ defmodule ConfigHelper do
"stability", "stability",
"suave", "suave",
"zetachain", "zetachain",
"zksync", "zilliqa",
"celo", "zksync"
"blackfort"
] ]
@spec chain_type() :: atom() | nil @spec chain_type() :: atom() | nil

@ -74,120 +74,6 @@ config :explorer, Explorer.Repo.Account,
pool_size: ConfigHelper.parse_integer_env_var("ACCOUNT_POOL_SIZE", 10), pool_size: ConfigHelper.parse_integer_env_var("ACCOUNT_POOL_SIZE", 10),
queue_target: queue_target queue_target: queue_target
# Configure Beacon Chain database
config :explorer, Explorer.Repo.Beacon,
database: database,
hostname: hostname,
url: System.get_env("DATABASE_URL"),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1
# Configures BridgedTokens database
config :explorer, Explorer.Repo.BridgedTokens,
database: database,
hostname: hostname,
url: System.get_env("DATABASE_URL"),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1
# Configure Optimism database
config :explorer, Explorer.Repo.Optimism,
database: database,
hostname: hostname,
url: System.get_env("DATABASE_URL"),
pool_size: 1
# Configure PolygonEdge database
config :explorer, Explorer.Repo.PolygonEdge,
database: database,
hostname: hostname,
url: System.get_env("DATABASE_URL"),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1
# Configure PolygonZkevm database
config :explorer, Explorer.Repo.PolygonZkevm,
database: database,
hostname: hostname,
url: System.get_env("DATABASE_URL"),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1
# Configure Scroll database
config :explorer, Explorer.Repo.Scroll,
database: database,
hostname: hostname,
url: System.get_env("DATABASE_URL"),
pool_size: 1
# Configure ZkSync database
config :explorer, Explorer.Repo.ZkSync,
database: database,
hostname: hostname,
url: System.get_env("DATABASE_URL"),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1
# Configure Celo database
config :explorer, Explorer.Repo.Celo,
database: database,
hostname: hostname,
url: System.get_env("DATABASE_URL"),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1
# Configure Rootstock database
config :explorer, Explorer.Repo.RSK,
database: database,
hostname: hostname,
url: System.get_env("DATABASE_URL"),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1
# Configure Shibarium database
config :explorer, Explorer.Repo.Shibarium,
database: database,
hostname: hostname,
url: System.get_env("DATABASE_URL"),
pool_size: 1
# Configure Suave database
config :explorer, Explorer.Repo.Suave,
database: database,
hostname: hostname,
url: ExplorerConfigHelper.get_suave_db_url(),
pool_size: 1
# Configure Filecoin database
config :explorer, Explorer.Repo.Filecoin,
database: database,
hostname: hostname,
url: System.get_env("DATABASE_URL"),
pool_size: 1
# Configure Arbitrum database
config :explorer, Explorer.Repo.Arbitrum,
database: database,
hostname: hostname,
url: System.get_env("DATABASE_URL"),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1
# Configures Stability database
config :explorer, Explorer.Repo.Stability,
database: database,
hostname: hostname,
url: System.get_env("DATABASE_URL"),
pool_size: 1
database_mud = if System.get_env("MUD_DATABASE_URL"), do: nil, else: database database_mud = if System.get_env("MUD_DATABASE_URL"), do: nil, else: database
hostname_mud = if System.get_env("MUD_DATABASE_URL"), do: nil, else: hostname hostname_mud = if System.get_env("MUD_DATABASE_URL"), do: nil, else: hostname
@ -199,19 +85,42 @@ config :explorer, Explorer.Repo.Mud,
pool_size: ConfigHelper.parse_integer_env_var("MUD_POOL_SIZE", 10), pool_size: ConfigHelper.parse_integer_env_var("MUD_POOL_SIZE", 10),
queue_target: queue_target queue_target: queue_target
# Configures ShrunkInternalTransactions database # Configure Suave database
config :explorer, Explorer.Repo.ShrunkInternalTransactions, config :explorer, Explorer.Repo.Suave,
database: database, database: database,
hostname: hostname, hostname: hostname,
url: System.get_env("DATABASE_URL"), url: ExplorerConfigHelper.get_suave_db_url(),
pool_size: 1 pool_size: 1
# Configures Blackfort database # Actually the following repos are not started, and its pool size remains
config :explorer, Explorer.Repo.Blackfort, # unused. Separating repos for different CHAIN_TYPE is implemented only for the
database: database, # sake of keeping DB schema update relevant to the current chain type
hostname: hostname, for repo <- [
url: System.get_env("DATABASE_URL"), # Chain-type dependent repos
pool_size: 1 Explorer.Repo.Arbitrum,
Explorer.Repo.Beacon,
Explorer.Repo.Blackfort,
Explorer.Repo.Celo,
Explorer.Repo.Filecoin,
Explorer.Repo.Optimism,
Explorer.Repo.PolygonEdge,
Explorer.Repo.PolygonZkevm,
Explorer.Repo.RSK,
Explorer.Repo.Scroll,
Explorer.Repo.Shibarium,
Explorer.Repo.Stability,
Explorer.Repo.Zilliqa,
Explorer.Repo.ZkSync,
# Feature dependent repos
Explorer.Repo.BridgedTokens,
Explorer.Repo.ShrunkInternalTransactions
] do
config :explorer, repo,
database: database,
hostname: hostname,
url: System.get_env("DATABASE_URL"),
pool_size: 1
end
variant = Variant.get() variant = Variant.get()

@ -52,104 +52,6 @@ config :explorer, Explorer.Repo.Account,
ssl: ExplorerConfigHelper.ssl_enabled?(), ssl: ExplorerConfigHelper.ssl_enabled?(),
queue_target: queue_target queue_target: queue_target
# Configure Beacon Chain database
config :explorer, Explorer.Repo.Beacon,
url: System.get_env("DATABASE_URL"),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?()
# Configures BridgedTokens database
config :explorer, Explorer.Repo.BridgedTokens,
url: System.get_env("DATABASE_URL"),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?()
# Configures Optimism database
config :explorer, Explorer.Repo.Optimism,
url: System.get_env("DATABASE_URL"),
pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?()
# Configures PolygonEdge database
config :explorer, Explorer.Repo.PolygonEdge,
url: System.get_env("DATABASE_URL"),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?()
# Configures PolygonZkevm database
config :explorer, Explorer.Repo.PolygonZkevm,
url: System.get_env("DATABASE_URL"),
pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?()
# Configures Scroll database
config :explorer, Explorer.Repo.Scroll,
url: System.get_env("DATABASE_URL"),
pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?()
# Configures ZkSync database
config :explorer, Explorer.Repo.ZkSync,
url: System.get_env("DATABASE_URL"),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?()
# Configures Celo database
config :explorer, Explorer.Repo.Celo,
url: System.get_env("DATABASE_URL"),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?()
# Configures Rootstock database
config :explorer, Explorer.Repo.RSK,
url: System.get_env("DATABASE_URL"),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?()
# Configures Shibarium database
config :explorer, Explorer.Repo.Shibarium,
url: System.get_env("DATABASE_URL"),
pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?()
# Configures Suave database
config :explorer, Explorer.Repo.Suave,
url: ExplorerConfigHelper.get_suave_db_url(),
pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?()
# Configures Filecoin database
config :explorer, Explorer.Repo.Filecoin,
url: System.get_env("DATABASE_URL"),
pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?()
# Configures Arbitrum database
config :explorer, Explorer.Repo.Arbitrum,
url: System.get_env("DATABASE_URL"),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?()
# Configures Stability database
config :explorer, Explorer.Repo.Stability,
url: System.get_env("DATABASE_URL"),
pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?()
# Configures Mud database # Configures Mud database
config :explorer, Explorer.Repo.Mud, config :explorer, Explorer.Repo.Mud,
url: ExplorerConfigHelper.get_mud_db_url(), url: ExplorerConfigHelper.get_mud_db_url(),
@ -157,19 +59,42 @@ config :explorer, Explorer.Repo.Mud,
ssl: ExplorerConfigHelper.ssl_enabled?(), ssl: ExplorerConfigHelper.ssl_enabled?(),
queue_target: queue_target queue_target: queue_target
# Configures ShrunkInternalTransactions database # Configures Suave database
config :explorer, Explorer.Repo.ShrunkInternalTransactions, config :explorer, Explorer.Repo.Suave,
url: System.get_env("DATABASE_URL"), url: ExplorerConfigHelper.get_suave_db_url(),
# actually this repo is not started, and its pool size remains unused.
# separating repos for different CHAIN_TYPE is implemented only for the sake of keeping DB schema update relevant to the current chain type
pool_size: 1, pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?() ssl: ExplorerConfigHelper.ssl_enabled?()
# Configures Blackfort database # Actually the following repos are not started, and its pool size remains
config :explorer, Explorer.Repo.Blackfort, # unused. Separating repos for different chain type or feature flag is
url: System.get_env("DATABASE_URL"), # implemented only for the sake of keeping DB schema update relevant to the
pool_size: 1, # current chain type
ssl: ExplorerConfigHelper.ssl_enabled?() for repo <- [
# Feature dependent repos
Explorer.Repo.BridgedTokens,
Explorer.Repo.ShrunkInternalTransactions,
# Chain-type dependent repos
Explorer.Repo.Arbitrum,
Explorer.Repo.Beacon,
Explorer.Repo.Blackfort,
Explorer.Repo.Celo,
Explorer.Repo.Filecoin,
Explorer.Repo.Optimism,
Explorer.Repo.PolygonEdge,
Explorer.Repo.PolygonZkevm,
Explorer.Repo.RSK,
Explorer.Repo.Scroll,
Explorer.Repo.Shibarium,
Explorer.Repo.Stability,
Explorer.Repo.Zilliqa,
Explorer.Repo.ZkSync
] do
config :explorer, repo,
url: System.get_env("DATABASE_URL"),
pool_size: 1,
ssl: ExplorerConfigHelper.ssl_enabled?()
end
variant = Variant.get() variant = Variant.get()

@ -64,6 +64,7 @@
"blockscout", "blockscout",
"blockscoutuser", "blockscoutuser",
"bools", "bools",
"Boneh",
"bridgedtokenlist", "bridgedtokenlist",
"brotli", "brotli",
"browserconfig", "browserconfig",
@ -388,6 +389,7 @@
"pawesome", "pawesome",
"paych", "paych",
"pbcopy", "pbcopy",
"PBFT",
"peeker", "peeker",
"peekers", "peekers",
"pendingtxlist", "pendingtxlist",
@ -477,6 +479,7 @@
"SENDGRID", "SENDGRID",
"Sepolia", "Sepolia",
"Sérgio", "Sérgio",
"Shacham",
"sharelock", "sharelock",
"sharelocks", "sharelocks",
"shibarium", "shibarium",
@ -521,6 +524,7 @@
"subtraces", "subtraces",
"successa", "successa",
"successb", "successb",
"supermajority",
"supernet", "supernet",
"sushiswap", "sushiswap",
"swal", "swal",
@ -636,6 +640,7 @@
"zetachain", "zetachain",
"zftv", "zftv",
"ziczr", "ziczr",
"zilliqa",
"zindex", "zindex",
"zipcode", "zipcode",
"zkatana", "zkatana",

Loading…
Cancel
Save