diff --git a/apps/explorer/lib/explorer/chain/internal_transaction/type.ex b/apps/explorer/lib/explorer/chain/internal_transaction/type.ex index 3f7f1eee81..4133dcf45f 100644 --- a/apps/explorer/lib/explorer/chain/internal_transaction/type.ex +++ b/apps/explorer/lib/explorer/chain/internal_transaction/type.ex @@ -38,6 +38,13 @@ defmodule Explorer.Chain.InternalTransaction.Type do iex> Explorer.Chain.InternalTransaction.Type.cast("selfdestruct") {:ok, :selfdestruct} + Deprecated values are not allowed for incoming data. + + iex> Explorer.Chain.InternalTransaction.Type.cast(:suicide) + :error + iex> Explorer.Chain.InternalTransaction.Type.cast("suicide") + :error + Unsupported `String.t` return an `:error`. iex> Explorer.Chain.InternalTransaction.Type.cast("hard-fork") @@ -65,6 +72,11 @@ defmodule Explorer.Chain.InternalTransaction.Type do iex> Explorer.Chain.InternalTransaction.Type.dump(:selfdestruct) {:ok, "selfdestruct"} + Deprecated values are not allowed to be dumped to the database as old values should only be read, not written. + + iex> Explorer.Chain.InternalTransaction.Type.dump(:suicide) + :error + Other atoms return an error iex> Explorer.Chain.InternalTransaction.Type.dump(:other) @@ -91,6 +103,11 @@ defmodule Explorer.Chain.InternalTransaction.Type do iex> Explorer.Chain.InternalTransaction.Type.load("selfdestruct") {:ok, :selfdestruct} + Converts deprecated value on load to the corresponding `t:t/0`. + + iex> Explorer.Chain.InternalTransaction.Type.load("suicide") + {:ok, :selfdestruct} + Other `t:String.t/0` return `:error` iex> Explorer.Chain.InternalTransaction.Type.load("other") @@ -103,6 +120,8 @@ defmodule Explorer.Chain.InternalTransaction.Type do def load("create"), do: {:ok, :create} def load("reward"), do: {:ok, :reward} def load("selfdestruct"), do: {:ok, :selfdestruct} + # deprecated + def load("suicide"), do: {:ok, :selfdestruct} def load(_), do: :error @doc """ diff --git a/apps/explorer/priv/repo/migrations/20181107164103_eip6.exs b/apps/explorer/priv/repo/migrations/20181107164103_eip6.exs index 099262696e..783561be1f 100644 --- a/apps/explorer/priv/repo/migrations/20181107164103_eip6.exs +++ b/apps/explorer/priv/repo/migrations/20181107164103_eip6.exs @@ -1,28 +1,31 @@ defmodule Explorer.Repo.Migrations.EIP6 do + @moduledoc """ + Use `priv/repo/migrations/scripts/20181107164103_eip6.sql` to migrate data and validate constraint. + + ```sh + mix ecto.migrate + psql -d $DATABASE -a -f priv/repo/migrations/scripts/20181107164103_eip6.sql + ``` + """ + use Ecto.Migration def up do execute("ALTER TABLE internal_transactions DROP CONSTRAINT suicide_has_from_and_to_address_hashes") - execute("UPDATE internal_transactions SET type = 'selfdestruct' WHERE type = 'suicide'") - - create( - constraint( - :internal_transactions, - :selfdestruct_has_from_and_to_address_hashes, - check: """ - type != 'selfdestruct' OR - (from_address_hash IS NOT NULL AND gas IS NULL AND to_address_hash IS NOT NULL) - """ - ) - ) + # `NOT VALID` skips checking pre-existing rows. Use `priv/repo/migrations/scripts/20181107164103_eip6.sql` to + # migrate data and validate constraints + execute(""" + ALTER TABLE internal_transactions + ADD CONSTRAINT selfdestruct_has_from_and_to_address + CHECK (type != 'selfdestruct' OR (from_address_hash IS NOT NULL AND gas IS NULL AND to_address_hash IS NOT NULL)) + NOT VALID + """) end def down do execute("ALTER TABLE internal_transactions DROP CONSTRAINT selfdestruct_has_from_and_to_address_hashes") - execute("UPDATE internal_transactions SET type = 'suicide' WHERE type = 'selfdestruct'") - create( constraint( :internal_transactions, diff --git a/apps/explorer/priv/repo/migrations/scripts/20181107164103_eip6.sql b/apps/explorer/priv/repo/migrations/scripts/20181107164103_eip6.sql new file mode 100644 index 0000000000..998b12ad02 --- /dev/null +++ b/apps/explorer/priv/repo/migrations/scripts/20181107164103_eip6.sql @@ -0,0 +1,64 @@ +DO $$ +DECLARE + row_count integer := 1; + batch_size integer := 50000; -- HOW MANY ITEMS WILL BE UPDATED AT TIME + iterator integer := batch_size; + max_row_number integer; + next_iterator integer; + updated_row_count integer; + deleted_row_count integer; +BEGIN + DROP TABLE IF EXISTS current_suicide_internal_transactions_temp; + -- CREATES TEMP TABLE TO STORE DATA TO BE UPDATED + CREATE TEMP TABLE current_suicide_internal_transactions_temp( + transaction_hash bytea NOT NULL, + index bigint NOT NULL, + row_number integer + ); + INSERT INTO current_suicide_internal_transactions_temp + SELECT DISTINCT ON (transaction_hash, index) + transaction_hash, + index, + ROW_NUMBER () OVER () + FROM internal_transactions + WHERE type = 'suicide' + ORDER BY transaction_hash, index DESC; + + max_row_number := (SELECT MAX(row_number) FROM current_suicide_internal_transactions_temp); + RAISE NOTICE '% items to be updated', max_row_number + 1; + + -- ITERATES THROUGH THE ITEMS UNTIL THE TEMP TABLE IS EMPTY + WHILE iterator <= max_row_number LOOP + next_iterator := iterator + batch_size; + + RAISE NOTICE '-> suicide internal transactions % to % to be updated', iterator, next_iterator - 1; + + UPDATE internal_transactions + SET type = 'selfdestruct' + FROM current_suicide_internal_transactions_temp + WHERE internal_transactions.transaction_hash = current_suicide_internal_transactions_temp.transaction_hash AND + internal_transactions.index = current_suicide_internal_transactions_temp.index AND + current_suicide_internal_transactions_temp.row_number < next_iterator; + + GET DIAGNOSTICS updated_row_count = ROW_COUNT; + + RAISE NOTICE '-> % internal transactions updated from suicide to selfdesruct', updated_row_count; + + DELETE FROM current_suicide_internal_transactions_temp + WHERE row_number < next_iterator; + + GET DIAGNOSTICS deleted_row_count = ROW_COUNT; + + ASSERT updated_row_count = deleted_row_count; + + -- COMMITS THE BATCH UPDATES + CHECKPOINT; + + -- UPDATES THE COUNTER SO IT DOESN'T TURN INTO AN INFINITE LOOP + iterator := next_iterator; + END LOOP; + + RAISE NOTICE 'All suicide type internal transactions updated to selfdestruct. Validating constraint.'; + + ALTER TABLE internal_transactions VALIDATE CONSTRAINT selfdestruct_has_from_and_to_address; +END $$;