Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: return blknum and transaction index on transaction submit #133

Merged
merged 1 commit into from
Oct 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions apps/api/lib/api/v1/controllers/transaction_controller.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule API.V1.Controller.TransactionController do
@moduledoc """
Contains transaction related API functions.
Transactions related API functions.
"""

use Spandex.Decorators
Expand All @@ -12,13 +12,12 @@ defmodule API.V1.Controller.TransactionController do
@doc """
Validate and insert the tx_bytes.
"""
@spec submit(String.t()) :: {:ok, TransactionView.serialized_hash()} | {:error, atom() | Ecto.Changeset.t()}
@spec submit(String.t()) :: {:ok, TransactionView.serialized_transaction()} | {:error, atom() | Ecto.Changeset.t()}
@decorate trace(service: :ecto, type: :backend)
def submit(hex_tx_bytes) do
with {:ok, binary} <- Encoding.to_binary(hex_tx_bytes),
{:ok, changeset} <- Transaction.decode(binary),
{:ok, transaction} <- Transaction.insert(changeset) do
{:ok, TransactionView.serialize_hash(transaction)}
with {:ok, tx_bytes} <- Encoding.to_binary(hex_tx_bytes),
{:ok, %{transaction: transaction}} <- Transaction.insert(tx_bytes) do
{:ok, TransactionView.serialize(transaction)}
end
end
end
12 changes: 8 additions & 4 deletions apps/api/lib/api/v1/views/transaction_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ defmodule API.V1.View.TransactionView do

alias ExPlasma.Encoding

@type serialized_hash() :: %{required(:tx_hash) => String.t()}
@type serialized_transaction() :: %{
required(:tx_hash) => String.t(),
required(:blknum) => pos_integer(),
required(:tx_index) => non_neg_integer()
}

@spec serialize_hash(map()) :: serialized_hash()
def serialize_hash(transaction) do
%{tx_hash: Encoding.to_hex(transaction.tx_hash)}
@spec serialize(map()) :: serialized_transaction()
def serialize(transaction) do
%{tx_hash: Encoding.to_hex(transaction.tx_hash), blknum: transaction.block.blknum, tx_index: transaction.tx_index}
end
end
6 changes: 1 addition & 5 deletions apps/api/test/api/v1/controllers/block_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@ defmodule API.V1.Controller.BlockControllerTest do
use Engine.DB.DataCase, async: true

alias API.V1.Controller.BlockController
alias Engine.DB.Block
alias Engine.DB.Transaction
alias ExPlasma.Encoding

describe "get_by_hash/1" do
test "it returns a matching block" do
%{id: id} = insert(:payment_v1_transaction)
Block.form()
transaction = Transaction |> Repo.get(id) |> Repo.preload(:block)
transaction = insert(:payment_v1_transaction, %{block: insert(:block)})

hash = Encoding.to_hex(transaction.block.hash)
hex_tx_bytes = [Encoding.to_hex(transaction.tx_bytes)]
Expand Down
58 changes: 40 additions & 18 deletions apps/api/test/api/v1/controllers/transaction_controller_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,37 @@ defmodule API.V1.Controllere.TransactionControllerTest do
use Engine.DB.DataCase, async: true

alias API.V1.Controller.TransactionController
alias Engine.DB.Block
alias Engine.Support.TestEntity
alias ExPlasma.Builder
alias ExPlasma.Encoding

setup do
insert(:merged_fee)
_ = insert(:merged_fee)
block = insert(:block)

:ok
{:ok, %{blknum: block.blknum}}
end

describe "submit/1" do
test "decodes and inserts a tx_bytes into the DB" do
entity = TestEntity.alice()
test "after a block is formed, incoming transaction is associated with a new block", %{blknum: blknum} do
{tx_bytes1, tx_hash1} = tx_bytes_and_hash()

%{output_id: output_id} = insert(:deposit_output, amount: 10, output_guard: entity.addr)
%{output_data: output_data} = build(:output, output_guard: entity.addr, amount: 9)
assert TransactionController.submit(tx_bytes1) ==
{:ok, %{tx_hash: Encoding.to_hex(tx_hash1), blknum: blknum, tx_index: 0}}

transaction =
Builder.new(ExPlasma.payment_v1(), %{
inputs: [ExPlasma.Output.decode_id!(output_id)],
outputs: [ExPlasma.Output.decode!(output_data)]
})
{tx_bytes2, tx_hash2} = tx_bytes_and_hash()

tx_bytes =
transaction
|> Builder.sign!([entity.priv_encoded])
|> ExPlasma.encode!()
|> Encoding.to_hex()
assert TransactionController.submit(tx_bytes2) ==
{:ok, %{tx_hash: Encoding.to_hex(tx_hash2), blknum: blknum, tx_index: 1}}

{tx_bytes3, tx_hash3} = tx_bytes_and_hash()

{:ok, tx_hash} = ExPlasma.Transaction.hash(transaction)
_ = Block.form()
expected_blknum = blknum + 1_000

assert TransactionController.submit(tx_bytes) == {:ok, %{tx_hash: Encoding.to_hex(tx_hash)}}
assert TransactionController.submit(tx_bytes3) ==
{:ok, %{tx_hash: Encoding.to_hex(tx_hash3), blknum: expected_blknum, tx_index: 0}}
end

test "it raises an error if the transaction is invalid" do
Expand All @@ -49,4 +48,27 @@ defmodule API.V1.Controllere.TransactionControllerTest do
assert "Cannot be zero" in errors_on(changeset).amount
end
end

defp tx_bytes_and_hash() do
entity = TestEntity.alice()

%{output_id: input_output_id} = insert(:deposit_output, amount: 10, output_guard: entity.addr)
%{output_data: output_data} = build(:output, output_guard: entity.addr, amount: 9)

transaction =
Builder.new(ExPlasma.payment_v1(), %{
inputs: [ExPlasma.Output.decode_id!(input_output_id)],
outputs: [ExPlasma.Output.decode!(output_data)]
})

tx_bytes =
transaction
|> Builder.sign!([entity.priv_encoded])
|> ExPlasma.encode!()
|> Encoding.to_hex()

{:ok, tx_hash} = ExPlasma.Transaction.hash(transaction)

{tx_bytes, tx_hash}
end
end
82 changes: 56 additions & 26 deletions apps/api/test/api/v1/router_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ defmodule API.V1.RouterTest do

alias API.V1.Router
alias Engine.DB.Block
alias Engine.DB.Transaction
alias Engine.Repo
alias Engine.Support.TestEntity
alias ExPlasma.Builder
alias ExPlasma.Encoding
Expand Down Expand Up @@ -122,14 +120,19 @@ defmodule API.V1.RouterTest do
end

describe "block.get" do
test "that it returns a block" do
%{id: id} = insert(:payment_v1_transaction)
Block.form()
transaction = Transaction |> Repo.get(id) |> Repo.preload(:block)
setup do
block = insert(:block)
{:ok, %{forming_block: block}}
end

test "it returns a block", %{forming_block: block} do
transaction = insert(:payment_v1_transaction, %{block: block})

{:ok, %{block_for_submission: formed_block}} = Block.form()

tx_bytes = Encoding.to_hex(transaction.tx_bytes)
hash = Encoding.to_hex(transaction.block.hash)
number = transaction.block.blknum
hash = Encoding.to_hex(formed_block.hash)
number = formed_block.blknum
{:ok, payload} = post("block.get", %{hash: hash})

assert_payload_data(payload, %{
Expand All @@ -139,7 +142,7 @@ defmodule API.V1.RouterTest do
})
end

test "that it returns an error if missing hash params" do
test "it returns an error if missing hash params" do
{:ok, payload} = post("block.get", %{})

assert_payload_data(payload, %{
Expand All @@ -149,7 +152,7 @@ defmodule API.V1.RouterTest do
})
end

test "that it returns an error if hash param is not a hex" do
test "it returns an error if hash param is not a hex" do
{:ok, payload} = post("block.get", %{hash: "12345"})

assert_payload_data(payload, %{
Expand All @@ -161,29 +164,34 @@ defmodule API.V1.RouterTest do
end

describe "transaction.submit" do
test "decodes a transaction and inserts it" do
setup do
block = insert(:block)
{:ok, %{blknum: block.blknum}}
end

test "after a block is formed, incoming transaction is associated with a new block", %{blknum: blknum} do
insert(:merged_fee)

entity = TestEntity.alice()
%{output_id: output_id} = insert(:deposit_output, amount: 10, output_guard: entity.addr)
%{output_data: output_data} = build(:output, output_guard: entity.addr, amount: 9)
{tx_bytes1, tx_hash1} = tx_bytes_and_hash()

{:ok, payload1} = post("transaction.submit", %{transaction: Encoding.to_hex(tx_bytes1)})

assert_payload_data(payload1, %{"tx_hash" => Encoding.to_hex(tx_hash1), "blknum" => blknum, "tx_index" => 0})

{tx_bytes2, tx_hash2} = tx_bytes_and_hash()

{:ok, payload2} = post("transaction.submit", %{transaction: Encoding.to_hex(tx_bytes2)})

transaction =
Builder.new(ExPlasma.payment_v1(), %{
inputs: [ExPlasma.Output.decode_id!(output_id)],
outputs: [ExPlasma.Output.decode!(output_data)]
})
assert_payload_data(payload2, %{"tx_hash" => Encoding.to_hex(tx_hash2), "blknum" => blknum, "tx_index" => 1})

tx_bytes =
transaction
|> Builder.sign!([entity.priv_encoded])
|> ExPlasma.encode!()
_ = Block.form()
next_blknum = blknum + 1_000

{:ok, tx_hash} = ExPlasma.Transaction.hash(transaction)
{tx_bytes3, tx_hash3} = tx_bytes_and_hash()

{:ok, payload} = post("transaction.submit", %{transaction: Encoding.to_hex(tx_bytes)})
{:ok, payload3} = post("transaction.submit", %{transaction: Encoding.to_hex(tx_bytes3)})

assert_payload_data(payload, %{"tx_hash" => Encoding.to_hex(tx_hash)})
mederic-p marked this conversation as resolved.
Show resolved Hide resolved
assert_payload_data(payload3, %{"tx_hash" => Encoding.to_hex(tx_hash3), "blknum" => next_blknum, "tx_index" => 0})
end

test "that it returns an error if missing transaction params" do
Expand Down Expand Up @@ -221,4 +229,26 @@ defmodule API.V1.RouterTest do
assert payload["version"] == "1.0"
assert payload["data"] == data
end

defp tx_bytes_and_hash() do
entity = TestEntity.alice()

%{output_id: input_output_id} = insert(:deposit_output, amount: 10, output_guard: entity.addr)
%{output_data: output_data} = build(:output, output_guard: entity.addr, amount: 9)

transaction =
Builder.new(ExPlasma.payment_v1(), %{
inputs: [ExPlasma.Output.decode_id!(input_output_id)],
outputs: [ExPlasma.Output.decode!(output_data)]
})

tx_bytes =
transaction
|> Builder.sign!([entity.priv_encoded])
|> ExPlasma.encode!()

{:ok, tx_hash} = ExPlasma.Transaction.hash(transaction)

{tx_bytes, tx_hash}
end
end
9 changes: 6 additions & 3 deletions apps/api/test/api/v1/views/transaction_view_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ defmodule API.V1.View.TransactionViewTest do

describe "serialize/1" do
test "serialize a transaction" do
transaction = build(:payment_v1_transaction)
block = build(:block)
transaction = build(:payment_v1_transaction, %{block: block})

assert TransactionView.serialize_hash(transaction) == %{
tx_hash: Encoding.to_hex(transaction.tx_hash)
assert TransactionView.serialize(transaction) == %{
tx_hash: Encoding.to_hex(transaction.tx_hash),
blknum: block.blknum,
tx_index: 0
}
end
end
Expand Down
Loading