A Transaction.Fork is a Transaction recorded in a non-consensus block, such an uncle.pull/802/head
parent
4b140c87df
commit
818f84a424
@ -0,0 +1,63 @@ |
|||||||
|
defmodule Explorer.Chain.Transaction.Fork do |
||||||
|
@moduledoc """ |
||||||
|
A transaction fork has the same `hash` as a `t:Explorer.Chain.Transaction.t/0`, but associates that `hash` with a |
||||||
|
non-consensus uncle `t:Explorer.Chain.Block.t/0` instead of the consensus block linked in the |
||||||
|
`t:Explorer.Chain.Transaction.t/0` `block_hash`. |
||||||
|
""" |
||||||
|
|
||||||
|
use Explorer.Schema |
||||||
|
|
||||||
|
alias Explorer.Chain.{Block, Hash, Transaction} |
||||||
|
|
||||||
|
@optional_attrs ~w()a |
||||||
|
@required_attrs ~w(hash index uncle_hash)a |
||||||
|
@allowed_attrs @optional_attrs ++ @required_attrs |
||||||
|
|
||||||
|
@typedoc """ |
||||||
|
* `hash` - hash of contents of this transaction |
||||||
|
* `index` - index of this transaction in `uncle`. |
||||||
|
* `transaction` - the data shared between all forks and the consensus transaction. |
||||||
|
* `uncle` - the block in which this transaction was mined/validated. |
||||||
|
* `uncle_hash` - `uncle` foreign key. |
||||||
|
""" |
||||||
|
@type t :: %__MODULE__{ |
||||||
|
hash: Hash.t(), |
||||||
|
index: Transaction.transaction_index(), |
||||||
|
transaction: %Ecto.Association.NotLoaded{} | Transaction.t(), |
||||||
|
uncle: %Ecto.Association.NotLoaded{} | Block.t(), |
||||||
|
uncle_hash: Hash.t() |
||||||
|
} |
||||||
|
|
||||||
|
@primary_key false |
||||||
|
schema "transaction_forks" do |
||||||
|
field(:index, :integer) |
||||||
|
|
||||||
|
timestamps() |
||||||
|
|
||||||
|
belongs_to(:transaction, Transaction, foreign_key: :hash, references: :hash, type: Hash.Full) |
||||||
|
belongs_to(:uncle, Block, foreign_key: :uncle_hash, references: :hash, type: Hash.Full) |
||||||
|
end |
||||||
|
|
||||||
|
@doc """ |
||||||
|
All fields are required for transaction fork |
||||||
|
|
||||||
|
iex> changeset = Fork.changeset( |
||||||
|
...> %Fork{}, |
||||||
|
...> %{ |
||||||
|
...> hash: "0x3a3eb134e6792ce9403ea4188e5e79693de9e4c94e499db132be086400da79e6", |
||||||
|
...> index: 1, |
||||||
|
...> uncle_hash: "0xe52d77084cab13a4e724162bcd8c6028e5ecfaa04d091ee476e96b9958ed6b48" |
||||||
|
...> } |
||||||
|
...> ) |
||||||
|
iex> changeset.valid? |
||||||
|
true |
||||||
|
|
||||||
|
""" |
||||||
|
def changeset(%__MODULE__{} = fork, attrs \\ %{}) do |
||||||
|
fork |
||||||
|
|> cast(attrs, @allowed_attrs) |
||||||
|
|> validate_required(@required_attrs) |
||||||
|
|> assoc_constraint(:transaction) |
||||||
|
|> assoc_constraint(:uncle) |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,16 @@ |
|||||||
|
defmodule Explorer.Repo.Migrations.CreateTransactionBlockUncles do |
||||||
|
use Ecto.Migration |
||||||
|
|
||||||
|
def change do |
||||||
|
create table(:transaction_forks, primary_key: false) do |
||||||
|
add(:hash, references(:transactions, column: :hash, on_delete: :delete_all, type: :bytea), null: false) |
||||||
|
add(:index, :integer, null: false) |
||||||
|
add(:uncle_hash, references(:blocks, column: :hash, on_delete: :delete_all, type: :bytea), null: false) |
||||||
|
|
||||||
|
timestamps() |
||||||
|
end |
||||||
|
|
||||||
|
create(index(:transaction_forks, :uncle_hash)) |
||||||
|
create(unique_index(:transaction_forks, [:uncle_hash, :index])) |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,23 @@ |
|||||||
|
defmodule Explorer.Chain.Transaction.ForkTest do |
||||||
|
use Explorer.DataCase |
||||||
|
|
||||||
|
alias Ecto.Changeset |
||||||
|
alias Explorer.Chain.Transaction.Fork |
||||||
|
|
||||||
|
doctest Fork |
||||||
|
|
||||||
|
test "a transaction fork cannot be inserted if the corresponding transaction does not exist" do |
||||||
|
assert %Changeset{valid?: true} = changeset = Fork.changeset(%Fork{}, params_for(:transaction_fork)) |
||||||
|
|
||||||
|
assert {:error, %Changeset{errors: [transaction: {"does not exist", []}]}} = Repo.insert(changeset) |
||||||
|
end |
||||||
|
|
||||||
|
test "a transaction fork cannot be inserted if the corresponding uncle does not exist" do |
||||||
|
transaction = insert(:transaction) |
||||||
|
|
||||||
|
assert %Changeset{valid?: true} = |
||||||
|
changeset = Fork.changeset(%Fork{}, %{hash: transaction.hash, index: 0, uncle_hash: block_hash()}) |
||||||
|
|
||||||
|
assert {:error, %Changeset{errors: [uncle: {"does not exist", []}]}} = Repo.insert(changeset) |
||||||
|
end |
||||||
|
end |
Loading…
Reference in new issue