Skip to content

Commit

Permalink
feat: return block number and transaction index in response for submi…
Browse files Browse the repository at this point in the history
…tting a transaction
  • Loading branch information
pgebal committed Oct 9, 2020
1 parent 42b9b98 commit 47c8215
Show file tree
Hide file tree
Showing 29 changed files with 1,085 additions and 304 deletions.
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)})
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

0 comments on commit 47c8215

Please sign in to comment.