From c31f9376801443afc62b9a0bea9ac5d01552f64b Mon Sep 17 00:00:00 2001 From: Alexander Kolotov Date: Wed, 12 Jun 2024 20:47:02 +0300 Subject: [PATCH] fix: batch transactions view recovered and support of proofs through ZkSync Hyperchain (#10234) * unified tx_hash field with Arbitrum batch transaction * Supported yet another contract function to submit batches proofs * fixed formatting issue --- .../runner/zksync/batch_transactions.ex | 4 +- .../lib/explorer/chain/transaction.ex | 2 +- .../chain/zksync/batch_transaction.ex | 38 ++++-- ...814_rename_field_in_batch_transactions.exs | 7 + .../fetcher/zksync/discovery/batches_data.ex | 2 +- .../fetcher/zksync/status_tracking/proven.ex | 123 ++++++++++++------ 6 files changed, 122 insertions(+), 54 deletions(-) create mode 100644 apps/explorer/priv/zk_sync/migrations/20240611091814_rename_field_in_batch_transactions.exs diff --git a/apps/explorer/lib/explorer/chain/import/runner/zksync/batch_transactions.ex b/apps/explorer/lib/explorer/chain/import/runner/zksync/batch_transactions.ex index 720519a100..39804aa0f9 100644 --- a/apps/explorer/lib/explorer/chain/import/runner/zksync/batch_transactions.ex +++ b/apps/explorer/lib/explorer/chain/import/runner/zksync/batch_transactions.ex @@ -60,7 +60,7 @@ defmodule Explorer.Chain.Import.Runner.ZkSync.BatchTransactions do | {:error, [Changeset.t()]} def insert(repo, changes_list, %{timeout: timeout, timestamps: timestamps} = _options) when is_list(changes_list) do # Enforce ZkSync.BatchTransaction ShareLocks order (see docs: sharelock.md) - ordered_changes_list = Enum.sort_by(changes_list, & &1.hash) + ordered_changes_list = Enum.sort_by(changes_list, & &1.tx_hash) {:ok, inserted} = Import.insert_changes_list( @@ -70,7 +70,7 @@ defmodule Explorer.Chain.Import.Runner.ZkSync.BatchTransactions do returning: true, timeout: timeout, timestamps: timestamps, - conflict_target: :hash, + conflict_target: :tx_hash, on_conflict: :nothing ) diff --git a/apps/explorer/lib/explorer/chain/transaction.ex b/apps/explorer/lib/explorer/chain/transaction.ex index 9c0e2c0299..448299893c 100644 --- a/apps/explorer/lib/explorer/chain/transaction.ex +++ b/apps/explorer/lib/explorer/chain/transaction.ex @@ -110,7 +110,7 @@ defmodule Explorer.Chain.Transaction.Schema do elem( quote do has_one(:zksync_batch_transaction, ZkSyncBatchTransaction, - foreign_key: :hash, + foreign_key: :tx_hash, references: :hash ) diff --git a/apps/explorer/lib/explorer/chain/zksync/batch_transaction.ex b/apps/explorer/lib/explorer/chain/zksync/batch_transaction.ex index ef3cfb0af8..e6fb1fc64a 100644 --- a/apps/explorer/lib/explorer/chain/zksync/batch_transaction.ex +++ b/apps/explorer/lib/explorer/chain/zksync/batch_transaction.ex @@ -1,24 +1,38 @@ defmodule Explorer.Chain.ZkSync.BatchTransaction do - @moduledoc "Models a list of transactions related to a batch for ZkSync." + @moduledoc """ + Models a list of transactions related to a batch for ZkSync. + + Changes in the schema should be reflected in the bulk import module: + - Explorer.Chain.Import.Runner.ZkSync.BatchTransactions + + Migrations: + - Explorer.Repo.ZkSync.Migrations.CreateZkSyncTables + - Explorer.Repo.ZkSync.Migrations.RenameFieldInBatchTransactions + """ use Explorer.Schema alias Explorer.Chain.{Hash, Transaction} alias Explorer.Chain.ZkSync.TransactionBatch - @required_attrs ~w(batch_number hash)a - - @type t :: %__MODULE__{ - batch_number: non_neg_integer(), - batch: %Ecto.Association.NotLoaded{} | TransactionBatch.t() | nil, - hash: Hash.t(), - l2_transaction: %Ecto.Association.NotLoaded{} | Transaction.t() | nil - } + @required_attrs ~w(batch_number tx_hash)a + @typedoc """ + * `tx_hash` - The hash of the rollup transaction. + * `l2_transaction` - An instance of `Explorer.Chain.Transaction` referenced by `tx_hash`. + * `batch_number` - The number of the ZkSync batch. + * `batch` - An instance of `Explorer.Chain.ZkSync.TransactionBatch` referenced by `batch_number`. + """ @primary_key false - schema "zksync_batch_l2_transactions" do + typed_schema "zksync_batch_l2_transactions" do belongs_to(:batch, TransactionBatch, foreign_key: :batch_number, references: :number, type: :integer) - belongs_to(:l2_transaction, Transaction, foreign_key: :hash, primary_key: true, references: :hash, type: Hash.Full) + + belongs_to(:l2_transaction, Transaction, + foreign_key: :tx_hash, + primary_key: true, + references: :hash, + type: Hash.Full + ) timestamps() end @@ -32,6 +46,6 @@ defmodule Explorer.Chain.ZkSync.BatchTransaction do |> cast(attrs, @required_attrs) |> validate_required(@required_attrs) |> foreign_key_constraint(:batch_number) - |> unique_constraint(:hash) + |> unique_constraint(:tx_hash) end end diff --git a/apps/explorer/priv/zk_sync/migrations/20240611091814_rename_field_in_batch_transactions.exs b/apps/explorer/priv/zk_sync/migrations/20240611091814_rename_field_in_batch_transactions.exs new file mode 100644 index 0000000000..185a07079d --- /dev/null +++ b/apps/explorer/priv/zk_sync/migrations/20240611091814_rename_field_in_batch_transactions.exs @@ -0,0 +1,7 @@ +defmodule Explorer.Repo.ZkSync.Migrations.RenameFieldInBatchTransactions do + use Ecto.Migration + + def change do + rename(table(:zksync_batch_l2_transactions), :hash, to: :tx_hash) + end +end diff --git a/apps/indexer/lib/indexer/fetcher/zksync/discovery/batches_data.ex b/apps/indexer/lib/indexer/fetcher/zksync/discovery/batches_data.ex index 75b514ba74..3c68a0ff4c 100644 --- a/apps/indexer/lib/indexer/fetcher/zksync/discovery/batches_data.ex +++ b/apps/indexer/lib/indexer/fetcher/zksync/discovery/batches_data.ex @@ -400,7 +400,7 @@ defmodule Indexer.Fetcher.ZkSync.Discovery.BatchesData do [ %{ batch_number: block.batch_number, - hash: l2_tx_hash + tx_hash: l2_tx_hash } | l2_txs ] diff --git a/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/proven.ex b/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/proven.ex index 52165ef8f0..ad2bb986c8 100644 --- a/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/proven.ex +++ b/apps/indexer/lib/indexer/fetcher/zksync/status_tracking/proven.ex @@ -12,7 +12,7 @@ defmodule Indexer.Fetcher.ZkSync.StatusTracking.Proven do associate_and_import_or_prepare_for_recovery: 4 ] - import Indexer.Fetcher.ZkSync.Utils.Logging, only: [log_info: 1] + import Indexer.Fetcher.ZkSync.Utils.Logging, only: [log_error: 1, log_info: 1] @doc """ Checks if the oldest unproven batch in the database has the associated L1 proving transaction @@ -68,8 +68,6 @@ defmodule Indexer.Fetcher.ZkSync.StatusTracking.Proven do end defp get_proven_batches_from_calldata(calldata) do - "0x7f61885c" <> encoded_params = calldata - # /// @param batchNumber Rollup batch number # /// @param batchHash Hash of L2 batch # /// @param indexRepeatedStorageChanges The serial number of the shortcut index that's used as a unique identifier for storage keys that were used twice or more @@ -93,41 +91,90 @@ defmodule Indexer.Fetcher.ZkSync.StatusTracking.Proven do # uint256[] recursiveAggregationInput; # uint256[] serializedProof; # } - # proveBatches(StoredBatchInfo calldata _prevBatch, StoredBatchInfo[] calldata _committedBatches, ProofInput calldata _proof) - - # IO.inspect(FunctionSelector.decode("proveBatches((uint64,bytes32,uint64,uint256,bytes32,bytes32,uint256,bytes32),(uint64,bytes32,uint64,uint256,bytes32,bytes32,uint256,bytes32)[],(uint256[],uint256[]))")) - [_prev_batch, proven_batches, _proof] = - TypeDecoder.decode( - Base.decode16!(encoded_params, case: :lower), - %FunctionSelector{ - function: "proveBatches", - types: [ - tuple: [ - uint: 64, - bytes: 32, - uint: 64, - uint: 256, - bytes: 32, - bytes: 32, - uint: 256, - bytes: 32 - ], - array: - {:tuple, - [ - uint: 64, - bytes: 32, - uint: 64, - uint: 256, - bytes: 32, - bytes: 32, - uint: 256, - bytes: 32 - ]}, - tuple: [array: {:uint, 256}, array: {:uint, 256}] - ] - } - ) + proven_batches = + case calldata do + "0x7f61885c" <> encoded_params -> + # proveBatches(StoredBatchInfo calldata _prevBatch, StoredBatchInfo[] calldata _committedBatches, ProofInput calldata _proof) + # IO.inspect(FunctionSelector.decode("proveBatches((uint64,bytes32,uint64,uint256,bytes32,bytes32,uint256,bytes32),(uint64,bytes32,uint64,uint256,bytes32,bytes32,uint256,bytes32)[],(uint256[],uint256[]))")) + [_prev_batch, proven_batches, _proof] = + TypeDecoder.decode( + Base.decode16!(encoded_params, case: :lower), + %FunctionSelector{ + function: "proveBatches", + types: [ + tuple: [ + uint: 64, + bytes: 32, + uint: 64, + uint: 256, + bytes: 32, + bytes: 32, + uint: 256, + bytes: 32 + ], + array: + {:tuple, + [ + uint: 64, + bytes: 32, + uint: 64, + uint: 256, + bytes: 32, + bytes: 32, + uint: 256, + bytes: 32 + ]}, + tuple: [array: {:uint, 256}, array: {:uint, 256}] + ] + } + ) + + proven_batches + + "0xc37533bb" <> encoded_params -> + # proveBatchesSharedBridge(uint256 _chainId, StoredBatchInfo calldata _prevBatch, StoredBatchInfo[] calldata _committedBatches, ProofInput calldata _proof) + # IO.inspect(FunctionSelector.decode("proveBatchesSharedBridge(uint256,(uint64,bytes32,uint64,uint256,bytes32,bytes32,uint256,bytes32),(uint64,bytes32,uint64,uint256,bytes32,bytes32,uint256,bytes32)[],(uint256[],uint256[]))")) + [_chainid, _prev_batch, proven_batches, _proof] = + TypeDecoder.decode( + Base.decode16!(encoded_params, case: :lower), + %FunctionSelector{ + function: "proveBatchesSharedBridge", + types: [ + {:uint, 256}, + tuple: [ + uint: 64, + bytes: 32, + uint: 64, + uint: 256, + bytes: 32, + bytes: 32, + uint: 256, + bytes: 32 + ], + array: + {:tuple, + [ + uint: 64, + bytes: 32, + uint: 64, + uint: 256, + bytes: 32, + bytes: 32, + uint: 256, + bytes: 32 + ]}, + tuple: [array: {:uint, 256}, array: {:uint, 256}] + ] + } + ) + + proven_batches + + _ -> + log_error("Unknown calldata format: #{calldata}") + + [] + end log_info("Discovered #{length(proven_batches)} proven batches in the prove tx")