Blockchain explorer for Ethereum based network and a tool for inspecting and analyzing EVM based blockchains.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
blockscout/test/explorer/importers/transaction_importer_test.exs

379 lines
12 KiB

defmodule Explorer.TransactionImporterTest do
use Explorer.DataCase
alias Explorer.Address
alias Explorer.BlockTransaction
alias Explorer.FromAddress
alias Explorer.ToAddress
alias Explorer.Transaction
alias Explorer.TransactionImporter
@raw_transaction %{
"creates" => nil,
"hash" => "pepino",
"value" => "0xde0b6b3a7640000",
"from" => "0x34d0ef2c",
"gas" => "0x21000",
"gasPrice" => "0x10000",
"input" => "0x5c8eff12",
"nonce" => "0x31337",
"publicKey" => "0xb39af9c",
"r" => "0x9",
"s" => "0x10",
"to" => "0x7a33b7d",
"standardV" => "0x11",
"transactionIndex" => "0x12",
"v" => "0x13"
}
@processed_transaction %{
hash: "pepino",
value: 1_000_000_000_000_000_000,
gas: 135_168,
gas_price: 65536,
input: "0x5c8eff12",
nonce: 201_527,
public_key: "0xb39af9c",
r: "0x9",
s: "0x10",
standard_v: "0x11",
transaction_index: "0x12",
v: "0x13"
}
describe "import/1" do
test "imports and saves a transaction to the database" do
use_cassette "transaction_importer_import_saves_the_transaction" do
TransactionImporter.import(
"0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291"
)
transaction = Transaction |> order_by(desc: :inserted_at) |> Repo.one()
assert transaction.hash ==
"0xdc3a0dfd0bbffd5eabbe40fb13afbe35ac5f5c030bff148f3e50afe32974b291"
end
end
test "when the transaction has previously been saved does not update it" do
use_cassette "transaction_importer_updates_the_association" do
insert(
:transaction,
hash: "0x170baac4eca26076953370dd603c68eab340c0135b19b585010d3158a5dbbf23",
gas: 5
)
TransactionImporter.import(
"0x170baac4eca26076953370dd603c68eab340c0135b19b585010d3158a5dbbf23"
)
transaction = Transaction |> order_by(desc: :inserted_at) |> Repo.one()
assert transaction.gas == Decimal.new(5)
end
end
test "binds an association to an existing block" do
use_cassette "transaction_importer_saves_the_association" do
block =
insert(
:block,
hash: "0xfce13392435a8e7dab44c07d482212efb9dc39a9bea1915a9ead308b55a617f9"
)
TransactionImporter.import(
"0x64d851139325479c3bb7ccc6e6ab4cde5bc927dce6810190fe5d770a4c1ac333"
)
transaction =
Transaction
|> Repo.get_by(
hash: "0x64d851139325479c3bb7ccc6e6ab4cde5bc927dce6810190fe5d770a4c1ac333"
)
block_transaction = BlockTransaction |> Repo.get_by(transaction_id: transaction.id)
assert block_transaction.block_id == block.id
end
end
test "when there is no block it does not save a block transaction" do
use_cassette "transaction_importer_txn_without_block" do
TransactionImporter.import(
"0xc6aa189827c14880f012a65292f7add7b5310094f8773a3d85b66303039b9dcf"
)
transaction =
Transaction
|> Repo.get_by(
hash: "0xc6aa189827c14880f012a65292f7add7b5310094f8773a3d85b66303039b9dcf"
)
block_transaction = BlockTransaction |> Repo.get_by(transaction_id: transaction.id)
refute block_transaction
end
end
test "creates a from address" do
use_cassette "transaction_importer_creates_a_from_address" do
TransactionImporter.import(
"0xc445f5410912458c480d992dd93355ae3dad64d9f65db25a3cf43a9c609a2e0d"
)
transaction =
Transaction
|> Repo.get_by(
hash: "0xc445f5410912458c480d992dd93355ae3dad64d9f65db25a3cf43a9c609a2e0d"
)
address = Address |> Repo.get_by(hash: "0xa5b4b372112ab8dbbb48c8d0edd89227e24ec785")
from_address =
FromAddress |> Repo.get_by(transaction_id: transaction.id, address_id: address.id)
assert from_address
end
end
test "binds an existing from address" do
insert(:address, hash: "0xa5b4b372112ab8dbbb48c8d0edd89227e24ec785")
use_cassette "transaction_importer_creates_a_from_address" do
TransactionImporter.import(
"0xc445f5410912458c480d992dd93355ae3dad64d9f65db25a3cf43a9c609a2e0d"
)
transaction =
Transaction
|> Repo.get_by(
hash: "0xc445f5410912458c480d992dd93355ae3dad64d9f65db25a3cf43a9c609a2e0d"
)
address = Address |> Repo.get_by(hash: "0xa5b4b372112ab8dbbb48c8d0edd89227e24ec785")
from_address =
FromAddress |> Repo.get_by(transaction_id: transaction.id, address_id: address.id)
assert from_address
end
end
test "creates a to address" do
use_cassette "transaction_importer_creates_a_to_address" do
TransactionImporter.import(
"0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c"
)
transaction =
Transaction
|> Repo.get_by(
hash: "0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c"
)
address = Address |> Repo.get_by(hash: "0x24e5b8528fe83257d5fe3497ef616026713347f8")
to_address =
ToAddress |> Repo.get_by(transaction_id: transaction.id, address_id: address.id)
assert(to_address)
end
end
test "binds an existing to address" do
insert(:address, hash: "0x24e5b8528fe83257d5fe3497ef616026713347f8")
use_cassette "transaction_importer_creates_a_to_address" do
TransactionImporter.import(
"0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c"
)
transaction =
Transaction
|> Repo.get_by(
hash: "0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c"
)
address = Address |> Repo.get_by(hash: "0x24e5b8528fe83257d5fe3497ef616026713347f8")
to_address =
ToAddress |> Repo.get_by(transaction_id: transaction.id, address_id: address.id)
assert(to_address)
end
end
test "creates a to address using creates when to is nil" do
use_cassette "transaction_importer_creates_a_to_address_from_creates" do
TransactionImporter.import(
"0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c"
)
transaction =
Transaction
|> Repo.get_by(
hash: "0xdc533d4227734a7cacd75a069e8dc57ac571b865ed97bae5ea4cb74b54145f4c"
)
address = Address |> Repo.get_by(hash: "0x24e5b8528fe83257d5fe3497ef616026713347f8")
to_address =
ToAddress |> Repo.get_by(transaction_id: transaction.id, address_id: address.id)
assert(to_address)
end
end
test "processes a map of transaction attributes" do
insert(:block, hash: "0xtakis")
TransactionImporter.import(
Map.merge(@raw_transaction, %{"hash" => "0xmunchos", "blockHash" => "0xtakis"})
)
last_transaction = Transaction |> order_by(desc: :inserted_at) |> limit(1) |> Repo.one()
assert last_transaction.hash == "0xmunchos"
end
end
describe "find/1" do
test "returns an empty transaction when there is no transaction with the given hash" do
assert TransactionImporter.find("0xC001") == %Transaction{}
end
test "returns the transaction with the requested hash" do
transaction = insert(:transaction, hash: "0xBEA75")
assert TransactionImporter.find("0xBEA75").id == transaction.id
end
end
describe "download_transaction/1" do
test "downloads a transaction" do
use_cassette "transaction_importer_download_transaction" do
raw_transaction =
TransactionImporter.download_transaction(
"0x170baac4eca26076953370dd603c68eab340c0135b19b585010d3158a5dbbf23"
)
assert(raw_transaction["from"] == "0xbe96ef1d056c97323e210fd0dd818aa027e57143")
end
end
test "when it has an invalid hash" do
use_cassette "transaction_importer_download_transaction_with_a_bad_hash" do
assert_raise MatchError, fn ->
TransactionImporter.download_transaction("0xdecafisbadzzzz")
end
end
end
end
describe "extract_attrs/1" do
test "returns a changeset-friendly list of transaction attributes" do
transaction_attrs = TransactionImporter.extract_attrs(@raw_transaction)
assert transaction_attrs == @processed_transaction
end
end
describe "create_block_transaction/2" do
test "inserts a block transaction" do
block = insert(:block)
transaction = insert(:transaction)
TransactionImporter.create_block_transaction(transaction, block.hash)
block_transaction =
BlockTransaction
|> Repo.get_by(transaction_id: transaction.id, block_id: block.id)
assert block_transaction
end
test "updates an already existing block transaction" do
block = insert(:block)
transaction = insert(:transaction)
the_seventies = Timex.parse!("1970-01-01T00:00:18-00:00", "{ISO:Extended}")
block_transaction =
insert(:block_transaction, %{
block_id: block.id,
transaction_id: transaction.id,
inserted_at: the_seventies,
updated_at: the_seventies
})
update_block = insert(:block)
TransactionImporter.create_block_transaction(transaction, update_block.hash)
updated_block_transaction =
BlockTransaction
|> Repo.get_by(transaction_id: transaction.id)
refute block_transaction.block_id == updated_block_transaction.block_id
refute block_transaction.updated_at == updated_block_transaction.updated_at
end
end
describe "create_from_address/2" do
test "that it creates a new address when one does not exist" do
transaction = insert(:transaction)
TransactionImporter.create_from_address(transaction, "0xbb8")
last_address = Address |> order_by(desc: :inserted_at) |> Repo.one()
assert last_address.hash == "0xbb8"
end
test "that it joins transaction and from address" do
transaction = insert(:transaction)
TransactionImporter.create_from_address(transaction, "0xFreshPrince")
address = Address |> order_by(desc: :inserted_at) |> Repo.one()
from_address = FromAddress |> order_by(desc: :inserted_at) |> Repo.one()
assert from_address.transaction_id == transaction.id
assert from_address.address_id == address.id
end
test "when the address already exists it does not insert a new address" do
transaction = insert(:transaction)
insert(:address, hash: "0xbb8")
TransactionImporter.create_from_address(transaction, "0xbb8")
assert Address |> Repo.all() |> length == 1
end
end
describe "create_to_address/2" do
test "that it creates a new address when one does not exist" do
transaction = insert(:transaction)
TransactionImporter.create_to_address(transaction, "0xFreshPrince")
last_address = Address |> order_by(desc: :inserted_at) |> Repo.one()
assert last_address.hash == "0xfreshprince"
end
test "that it joins transaction and address" do
transaction = insert(:transaction)
TransactionImporter.create_to_address(transaction, "0xFreshPrince")
address = Address |> order_by(desc: :inserted_at) |> Repo.one()
to_address = ToAddress |> order_by(desc: :inserted_at) |> Repo.one()
assert to_address.transaction_id == transaction.id
assert to_address.address_id == address.id
end
test "when the address already exists it does not insert a new address" do
transaction = insert(:transaction)
insert(:address, hash: "bigmouthbillybass")
TransactionImporter.create_to_address(transaction, "bigmouthbillybass")
assert Address |> Repo.all() |> length == 1
end
end
describe "decode_integer_field/1" do
test "returns the integer value of a hex value" do
assert(TransactionImporter.decode_integer_field("0x7f2fb") == 520_955)
end
end
end