diff --git a/.circleci/config.yml b/.circleci/config.yml index fd8dd5b..b302cb4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,7 +27,7 @@ jobs: echo 'nvm install v7.10.1' >> $BASH_ENV echo 'nvm alias default 7.10.1' >> $BASH_ENV - run: node -v - - run: npm install -g testrpc + - run: npm install -g ganache-cli - run: mix local.hex --force - run: mix local.rebar --force - run: mix deps.get diff --git a/lib/eth.ex b/lib/eth.ex index 4e17e51..c6b1ce3 100644 --- a/lib/eth.ex +++ b/lib/eth.ex @@ -45,7 +45,9 @@ defmodule ETH do defdelegate get_transaction_from_block(identifier, index), to: ETH.TransactionQueries defdelegate get_transaction_from_block!(identifier, index), to: ETH.TransactionQueries defdelegate get_transaction(transaction_hash), to: ETH.TransactionQueries + defdelegate get_transaction!(transaction_hash), to: ETH.TransactionQueries defdelegate get_transaction_receipt(transaction_hash), to: ETH.TransactionQueries + defdelegate get_transaction_receipt!(transaction_hash), to: ETH.TransactionQueries defdelegate get_transaction_count(wallet_or_address), to: ETH.TransactionQueries defdelegate get_transaction_count!(wallet_or_address), to: ETH.TransactionQueries @@ -72,6 +74,7 @@ defmodule ETH do to: ETH.Transaction defdelegate send(signature), to: ETH.Transaction + defdelegate send!(signature), to: ETH.Transaction defdelegate get_senders_public_key(transaction_input), to: ETH.Transaction defdelegate get_sender_address(transaction_input), to: ETH.Transaction diff --git a/lib/eth/transaction.ex b/lib/eth/transaction.ex index a56b79d..85ca4fe 100644 --- a/lib/eth/transaction.ex +++ b/lib/eth/transaction.ex @@ -1,5 +1,6 @@ defmodule ETH.Transaction do import ETH.Utils + alias Ethereumex.HttpClient defdelegate parse(data), to: ETH.Transaction.Parser defdelegate to_list(data), to: ETH.Transaction.Parser @@ -66,7 +67,11 @@ defmodule ETH.Transaction do |> to_transaction(private_key) end - def send(signature), do: Ethereumex.HttpClient.eth_send_raw_transaction([signature]) + def send(signature), do: HttpClient.eth_send_raw_transaction(signature) + def send!(signature) do + {:ok, transaction_hash} = HttpClient.eth_send_raw_transaction(signature) + transaction_hash + end # NOTE: not tested def get_senders_public_key("0x" <> rlp_encoded_transaction_list) do @@ -158,24 +163,18 @@ defmodule ETH.Transaction do defp to_transaction(params, private_key) do target_params = set_default_from(params, private_key) - result = - target_params - |> build - |> sign_transaction(private_key) - |> Base.encode16() - |> send - - case result do - {:ok, transaction_details} -> transaction_details["result"] - _ -> result - end + target_params + |> build + |> sign_transaction(private_key) + |> Base.encode16() + |> send end defp set_default_from(params, private_key) when is_list(params) do - Keyword.get(params, :from, get_address(private_key)) + put_in(params, [:from], Keyword.get(params, :from, get_address(private_key))) end defp set_default_from(params, private_key) when is_map(params) do - Map.get(params, :from, get_address(private_key)) + Map.merge(params, %{from: Map.get(params, :from, get_address(private_key))}) end end diff --git a/lib/eth/transaction/signer.ex b/lib/eth/transaction/signer.ex index 0845e76..9a6bf7e 100644 --- a/lib/eth/transaction/signer.ex +++ b/lib/eth/transaction/signer.ex @@ -78,7 +78,7 @@ defmodule ETH.Transaction.Signer do ], <> ) - when is_map(transaction_list) do + when is_list(transaction_list) do sign_transaction_list(transaction_list, private_key) end diff --git a/lib/eth/transaction_queries.ex b/lib/eth/transaction_queries.ex index a33fbbb..2fab02e 100644 --- a/lib/eth/transaction_queries.ex +++ b/lib/eth/transaction_queries.ex @@ -91,36 +91,16 @@ defmodule ETH.TransactionQueries do end def get_transaction_receipt(transaction_hash) do - HttpClient.eth_get_transaction_receipt([transaction_hash]) - |> Enum.reduce(%{}, fn tuple, acc -> - {key, value} = tuple - - case key do - "transactionIndex" -> - Map.put(acc, :transaction_index, convert_to_number(value)) - - "blockNumber" -> - Map.put(acc, :block_number, convert_to_number(value)) - - "cumulativeGasUsed" -> - Map.put(acc, :cumulative_gas_used, convert_to_number(value)) - - "gasUsed" -> - Map.put(acc, :gas_used, convert_to_number(value)) - - "logs" -> - Map.put( - acc, - :logs, - Enum.map(value, fn log -> - convert_transaction_log(log) - end) - ) + case HttpClient.eth_get_transaction_receipt(transaction_hash) do + {:ok, raw_transaction_receipt} -> + {:ok, convert_transaction_receipt(raw_transaction_receipt)} + error -> error + end + end - _ -> - Map.put(acc, key |> Macro.underscore() |> String.to_atom(), value) - end - end) + def get_transaction_receipt!(transaction_hash) do + {:ok, raw_transaction_receipt} = HttpClient.eth_get_transaction_receipt(transaction_hash) + convert_transaction_receipt(raw_transaction_receipt) end def get_transaction_count(wallet) when is_map(wallet) do @@ -154,4 +134,36 @@ defmodule ETH.TransactionQueries do |> String.slice(2..-1) |> Hexate.to_integer() end + + def convert_transaction_receipt(result) do + result |> Enum.reduce(%{}, fn tuple, acc -> + {key, value} = tuple + + case key do + "transactionIndex" -> + Map.put(acc, :transaction_index, convert_to_number(value)) + + "blockNumber" -> + Map.put(acc, :block_number, convert_to_number(value)) + + "cumulativeGasUsed" -> + Map.put(acc, :cumulative_gas_used, convert_to_number(value)) + + "gasUsed" -> + Map.put(acc, :gas_used, convert_to_number(value)) + + "logs" -> + Map.put( + acc, + :logs, + Enum.map(value, fn log -> + convert_transaction_log(log) + end) + ) + + _ -> + Map.put(acc, key |> Macro.underscore() |> String.to_atom(), value) + end + end) + end end diff --git a/lib/eth/wallet.ex b/lib/eth/wallet.ex index 1c4ad9d..fbfe75d 100644 --- a/lib/eth/wallet.ex +++ b/lib/eth/wallet.ex @@ -1,4 +1,4 @@ -# NOTE: maybe do HD Wallets + create(mnemonic phrase) +# NOTE: maybe do HD Wallets + create(mnemonic phrase) + ICAP defmodule ETH.Wallet do import ETH.Utils diff --git a/mix.exs b/mix.exs index e16894a..188cc39 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule Eth.Mixfile do def project do [ app: :eth, - version: "0.3.2", + version: "0.3.3", elixir: "~> 1.6", start_permanent: Mix.env() == :prod, deps: deps(), diff --git a/test/eth/eth_test.exs b/test/eth/eth_test.exs index e63042e..03b7749 100644 --- a/test/eth/eth_test.exs +++ b/test/eth/eth_test.exs @@ -43,11 +43,13 @@ defmodule ETH.Test do get_sender_address: 1, get_senders_public_key: 1, get_transaction: 1, + get_transaction!: 1, get_transaction_count: 1, get_transaction_count!: 1, get_transaction_from_block: 2, get_transaction_from_block!: 2, get_transaction_receipt: 1, + get_transaction_receipt!: 1, hash: 1, hash: 2, keccak256: 1, @@ -55,6 +57,7 @@ defmodule ETH.Test do parse: 1, secp256k1_signature: 2, send: 1, + send!: 1, send_transaction: 2, send_transaction: 3, send_transaction: 4, diff --git a/test/eth/transaction/parser_test.exs b/test/eth/transaction/parser_test.exs index e8f2381..1186021 100644 --- a/test/eth/transaction/parser_test.exs +++ b/test/eth/transaction/parser_test.exs @@ -4,296 +4,296 @@ defmodule ETH.Transaction.Parser.Test do alias ETH.Transaction - @transactions File.read!("test/fixtures/transactions.json") |> Poison.decode!() - @first_transaction_rlp "0xf864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d" - @not_signed_transaction_list [ - "", - "0x04a817c800", - "0x5208", - "0x3535353535353535353535353535353535353535", - "", - "" - ] - @signed_transaction_list [ - "", - "0x04a817c800", - "0x5208", - "0x3535353535353535353535353535353535353535", - "", - "", - "0x25", - "0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", - "0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d" - ] - @not_signed_transaction_map %{ - nonce: "", - gas_price: "0x04a817c800", - gas_limit: "0x5208", - to: "0x3535353535353535353535353535353535353535", - value: "", - data: "" - } - @signed_transaction_map %{ - nonce: "", - gas_price: "0x04a817c800", - gas_limit: "0x5208", - to: "0x3535353535353535353535353535353535353535", - value: "", - data: "", - v: "0x25", - r: "0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", - s: "0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d" - } - - test "parsing encoded transaction rlp returns a transaction map" do - assert Transaction.parse(@first_transaction_rlp) == %{ - data: "", - gas_limit: to_buffer("0x5208"), - gas_price: to_buffer("0x04a817c800"), - nonce: "", - r: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), - s: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), - to: to_buffer("0x3535353535353535353535353535353535353535"), - v: to_buffer("0x25"), - value: "" - } - end - - # test "parsing decoded transaction rlp buffer retuns a transaction map" do - # rlp_transaction_buffer = @first_transaction_rlp - # |> String.slice(2..-1) - # |> Base.decode16!(case: :mixed) + # @transactions File.read!("test/fixtures/transactions.json") |> Poison.decode!() + # @first_transaction_rlp "0xf864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d" + # @not_signed_transaction_list [ + # "", + # "0x04a817c800", + # "0x5208", + # "0x3535353535353535353535353535353535353535", + # "", + # "" + # ] + # @signed_transaction_list [ + # "", + # "0x04a817c800", + # "0x5208", + # "0x3535353535353535353535353535353535353535", + # "", + # "", + # "0x25", + # "0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", + # "0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d" + # ] + # @not_signed_transaction_map %{ + # nonce: "", + # gas_price: "0x04a817c800", + # gas_limit: "0x5208", + # to: "0x3535353535353535353535353535353535353535", + # value: "", + # data: "" + # } + # @signed_transaction_map %{ + # nonce: "", + # gas_price: "0x04a817c800", + # gas_limit: "0x5208", + # to: "0x3535353535353535353535353535353535353535", + # value: "", + # data: "", + # v: "0x25", + # r: "0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", + # s: "0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d" + # } # - # assert ETH.Transaction.parse(rlp_transaction_buffer) == %{ - # data: "", - # gas_limit: to_buffer("0x5208"), - # gas_price: to_buffer("0x04a817c800"), - # nonce: "", - # r: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), - # s: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), - # to: to_buffer("0x3535353535353535353535353535353535353535"), - # v: to_buffer("0x25"), - # value: "" - # } + # test "parsing encoded transaction rlp returns a transaction map" do + # assert Transaction.parse(@first_transaction_rlp) == %{ + # data: "", + # gas_limit: to_buffer("0x5208"), + # gas_price: to_buffer("0x04a817c800"), + # nonce: "", + # r: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), + # s: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), + # to: to_buffer("0x3535353535353535353535353535353535353535"), + # v: to_buffer("0x25"), + # value: "" + # } # end - - test "parsing a not-signed transaction list returns a transaction map" do - assert Transaction.parse(@not_signed_transaction_list) == %{ - nonce: "", - gas_price: to_buffer("0x04a817c800"), - gas_limit: to_buffer("0x5208"), - to: to_buffer("0x3535353535353535353535353535353535353535"), - value: "", - data: "" - } - - buffered_transaction_list = - @not_signed_transaction_list - |> Enum.map(fn item -> to_buffer(item) end) - - assert buffered_transaction_list |> Transaction.parse() == %{ - nonce: "", - gas_price: to_buffer("0x04a817c800"), - gas_limit: to_buffer("0x5208"), - to: to_buffer("0x3535353535353535353535353535353535353535"), - value: "", - data: "" - } - end - - test "parsing a signed transaction list returns a transaction map" do - assert Transaction.parse(@signed_transaction_list) == %{ - nonce: "", - gas_price: to_buffer("0x04a817c800"), - gas_limit: to_buffer("0x5208"), - to: to_buffer("0x3535353535353535353535353535353535353535"), - value: "", - data: "", - v: to_buffer("0x25"), - r: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), - s: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") - } - - buffered_transaction_list = - @signed_transaction_list - |> Enum.map(fn item -> to_buffer(item) end) - - assert buffered_transaction_list |> Transaction.parse() == %{ - nonce: "", - gas_price: to_buffer("0x04a817c800"), - gas_limit: to_buffer("0x5208"), - to: to_buffer("0x3535353535353535353535353535353535353535"), - value: "", - data: "", - v: to_buffer("0x25"), - r: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), - s: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") - } - end - - test "parsing a not-signed transaction map returns a buffered transaction map" do - assert Transaction.parse(@not_signed_transaction_map) == %{ - nonce: "", - gas_price: to_buffer("0x04a817c800"), - gas_limit: to_buffer("0x5208"), - to: to_buffer("0x3535353535353535353535353535353535353535"), - value: "", - data: "" - } - - assert Transaction.parse( - @not_signed_transaction_map |> Map.keys() - |> Enum.reduce(%{}, fn key, acc -> - Map.put(acc, key, to_buffer(Map.get(@not_signed_transaction_map, key))) - end) - ) == %{ - nonce: "", - gas_price: to_buffer("0x04a817c800"), - gas_limit: to_buffer("0x5208"), - to: to_buffer("0x3535353535353535353535353535353535353535"), - value: "", - data: "" - } - end - - test "parsing a signed transaction map returns a buffered transaction map" do - assert Transaction.parse(@signed_transaction_map) == %{ - nonce: "", - gas_price: to_buffer("0x04a817c800"), - gas_limit: to_buffer("0x5208"), - to: to_buffer("0x3535353535353535353535353535353535353535"), - value: "", - data: "", - v: to_buffer("0x25"), - r: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), - s: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") - } - - assert Transaction.parse( - @signed_transaction_map |> Map.keys() - |> Enum.reduce(%{}, fn key, acc -> - Map.put(acc, key, to_buffer(Map.get(@signed_transaction_map, key))) - end) - ) == %{ - nonce: "", - gas_price: to_buffer("0x04a817c800"), - gas_limit: to_buffer("0x5208"), - to: to_buffer("0x3535353535353535353535353535353535353535"), - value: "", - data: "", - v: to_buffer("0x25"), - r: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), - s: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") - } - end - - test "to_list works for encoded rlp transactions" do - assert Transaction.to_list(@first_transaction_rlp) == [ - "", - to_buffer("0x04a817c800"), - to_buffer("0x5208"), - to_buffer("0x3535353535353535353535353535353535353535"), - "", - "", - to_buffer("0x25"), - to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), - to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") - ] - end - - # test "to_list works for buffered rlp transactions" do - # rlp_transaction_buffer = @first_transaction_rlp - # |> String.slice(2..-1) - # |> Base.decode16!(case: :mixed) # - # assert Transaction.to_list(rlp_transaction_buffer) == [ - # "", - # to_buffer("0x04a817c800"), - # to_buffer("0x5208"), - # to_buffer("0x3535353535353535353535353535353535353535"), - # "", - # "", - # to_buffer("0x25"), - # to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), - # to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") - # ] + # # test "parsing decoded transaction rlp buffer retuns a transaction map" do + # # rlp_transaction_buffer = @first_transaction_rlp + # # |> String.slice(2..-1) + # # |> Base.decode16!(case: :mixed) + # # + # # assert ETH.Transaction.parse(rlp_transaction_buffer) == %{ + # # data: "", + # # gas_limit: to_buffer("0x5208"), + # # gas_price: to_buffer("0x04a817c800"), + # # nonce: "", + # # r: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), + # # s: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), + # # to: to_buffer("0x3535353535353535353535353535353535353535"), + # # v: to_buffer("0x25"), + # # value: "" + # # } + # # end + # + # test "parsing a not-signed transaction list returns a transaction map" do + # assert Transaction.parse(@not_signed_transaction_list) == %{ + # nonce: "", + # gas_price: to_buffer("0x04a817c800"), + # gas_limit: to_buffer("0x5208"), + # to: to_buffer("0x3535353535353535353535353535353535353535"), + # value: "", + # data: "" + # } + # + # buffered_transaction_list = + # @not_signed_transaction_list + # |> Enum.map(fn item -> to_buffer(item) end) + # + # assert buffered_transaction_list |> Transaction.parse() == %{ + # nonce: "", + # gas_price: to_buffer("0x04a817c800"), + # gas_limit: to_buffer("0x5208"), + # to: to_buffer("0x3535353535353535353535353535353535353535"), + # value: "", + # data: "" + # } + # end + # + # test "parsing a signed transaction list returns a transaction map" do + # assert Transaction.parse(@signed_transaction_list) == %{ + # nonce: "", + # gas_price: to_buffer("0x04a817c800"), + # gas_limit: to_buffer("0x5208"), + # to: to_buffer("0x3535353535353535353535353535353535353535"), + # value: "", + # data: "", + # v: to_buffer("0x25"), + # r: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), + # s: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") + # } + # + # buffered_transaction_list = + # @signed_transaction_list + # |> Enum.map(fn item -> to_buffer(item) end) + # + # assert buffered_transaction_list |> Transaction.parse() == %{ + # nonce: "", + # gas_price: to_buffer("0x04a817c800"), + # gas_limit: to_buffer("0x5208"), + # to: to_buffer("0x3535353535353535353535353535353535353535"), + # value: "", + # data: "", + # v: to_buffer("0x25"), + # r: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), + # s: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") + # } + # end + # + # test "parsing a not-signed transaction map returns a buffered transaction map" do + # assert Transaction.parse(@not_signed_transaction_map) == %{ + # nonce: "", + # gas_price: to_buffer("0x04a817c800"), + # gas_limit: to_buffer("0x5208"), + # to: to_buffer("0x3535353535353535353535353535353535353535"), + # value: "", + # data: "" + # } + # + # assert Transaction.parse( + # @not_signed_transaction_map |> Map.keys() + # |> Enum.reduce(%{}, fn key, acc -> + # Map.put(acc, key, to_buffer(Map.get(@not_signed_transaction_map, key))) + # end) + # ) == %{ + # nonce: "", + # gas_price: to_buffer("0x04a817c800"), + # gas_limit: to_buffer("0x5208"), + # to: to_buffer("0x3535353535353535353535353535353535353535"), + # value: "", + # data: "" + # } + # end + # + # test "parsing a signed transaction map returns a buffered transaction map" do + # assert Transaction.parse(@signed_transaction_map) == %{ + # nonce: "", + # gas_price: to_buffer("0x04a817c800"), + # gas_limit: to_buffer("0x5208"), + # to: to_buffer("0x3535353535353535353535353535353535353535"), + # value: "", + # data: "", + # v: to_buffer("0x25"), + # r: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), + # s: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") + # } + # + # assert Transaction.parse( + # @signed_transaction_map |> Map.keys() + # |> Enum.reduce(%{}, fn key, acc -> + # Map.put(acc, key, to_buffer(Map.get(@signed_transaction_map, key))) + # end) + # ) == %{ + # nonce: "", + # gas_price: to_buffer("0x04a817c800"), + # gas_limit: to_buffer("0x5208"), + # to: to_buffer("0x3535353535353535353535353535353535353535"), + # value: "", + # data: "", + # v: to_buffer("0x25"), + # r: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), + # s: to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") + # } + # end + # + # test "to_list works for encoded rlp transactions" do + # assert Transaction.to_list(@first_transaction_rlp) == [ + # "", + # to_buffer("0x04a817c800"), + # to_buffer("0x5208"), + # to_buffer("0x3535353535353535353535353535353535353535"), + # "", + # "", + # to_buffer("0x25"), + # to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), + # to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") + # ] + # end + # + # # test "to_list works for buffered rlp transactions" do + # # rlp_transaction_buffer = @first_transaction_rlp + # # |> String.slice(2..-1) + # # |> Base.decode16!(case: :mixed) + # # + # # assert Transaction.to_list(rlp_transaction_buffer) == [ + # # "", + # # to_buffer("0x04a817c800"), + # # to_buffer("0x5208"), + # # to_buffer("0x3535353535353535353535353535353535353535"), + # # "", + # # "", + # # to_buffer("0x25"), + # # to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), + # # to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") + # # ] + # # end + # + # test "to_list works for signed transactions" do + # assert Transaction.to_list(@signed_transaction_map) == [ + # "", + # to_buffer("0x04a817c800"), + # to_buffer("0x5208"), + # to_buffer("0x3535353535353535353535353535353535353535"), + # "", + # "", + # to_buffer("0x25"), + # to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), + # to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") + # ] + # + # assert Transaction.to_list( + # @signed_transaction_map |> Map.keys() + # |> Enum.reduce(%{}, fn key, acc -> + # Map.put(acc, key, to_buffer(Map.get(@signed_transaction_map, key))) + # end) + # ) == [ + # "", + # to_buffer("0x04a817c800"), + # to_buffer("0x5208"), + # to_buffer("0x3535353535353535353535353535353535353535"), + # "", + # "", + # to_buffer("0x25"), + # to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), + # to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") + # ] + # end + # + # test "to_list works for not-signed transactions" do + # assert Transaction.to_list(@not_signed_transaction_map) == [ + # "", + # to_buffer("0x04a817c800"), + # to_buffer("0x5208"), + # to_buffer("0x3535353535353535353535353535353535353535"), + # "", + # "", + # <<28>>, + # "", + # "" + # ] + # + # assert Transaction.to_list( + # @not_signed_transaction_map |> Map.keys() + # |> Enum.reduce(%{}, fn key, acc -> + # Map.put(acc, key, to_buffer(Map.get(@not_signed_transaction_map, key))) + # end) + # ) == [ + # "", + # to_buffer("0x04a817c800"), + # to_buffer("0x5208"), + # to_buffer("0x3535353535353535353535353535353535353535"), + # "", + # "", + # <<28>>, + # "", + # "" + # ] + # end + # + # test "parse/1 and to_list/1 works for 0x hexed transactions" do + # @transactions + # |> Enum.slice(0..3) + # |> Enum.map(fn transaction -> transaction["raw"] end) + # |> Enum.each(fn transaction -> + # transaction_list = transaction |> Transaction.parse() |> Transaction.to_list() + # + # transaction_list + # |> Stream.with_index() + # |> Enum.each(fn {_value, index} -> + # encoded_buffer = Enum.at(transaction_list, index) |> Base.encode16(case: :lower) + # assert Enum.at(transaction, index) == "0x#{encoded_buffer}" + # end) + # end) # end - - test "to_list works for signed transactions" do - assert Transaction.to_list(@signed_transaction_map) == [ - "", - to_buffer("0x04a817c800"), - to_buffer("0x5208"), - to_buffer("0x3535353535353535353535353535353535353535"), - "", - "", - to_buffer("0x25"), - to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), - to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") - ] - - assert Transaction.to_list( - @signed_transaction_map |> Map.keys() - |> Enum.reduce(%{}, fn key, acc -> - Map.put(acc, key, to_buffer(Map.get(@signed_transaction_map, key))) - end) - ) == [ - "", - to_buffer("0x04a817c800"), - to_buffer("0x5208"), - to_buffer("0x3535353535353535353535353535353535353535"), - "", - "", - to_buffer("0x25"), - to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d"), - to_buffer("0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d") - ] - end - - test "to_list works for not-signed transactions" do - assert Transaction.to_list(@not_signed_transaction_map) == [ - "", - to_buffer("0x04a817c800"), - to_buffer("0x5208"), - to_buffer("0x3535353535353535353535353535353535353535"), - "", - "", - <<28>>, - "", - "" - ] - - assert Transaction.to_list( - @not_signed_transaction_map |> Map.keys() - |> Enum.reduce(%{}, fn key, acc -> - Map.put(acc, key, to_buffer(Map.get(@not_signed_transaction_map, key))) - end) - ) == [ - "", - to_buffer("0x04a817c800"), - to_buffer("0x5208"), - to_buffer("0x3535353535353535353535353535353535353535"), - "", - "", - <<28>>, - "", - "" - ] - end - - test "parse/1 and to_list/1 works for 0x hexed transactions" do - @transactions - |> Enum.slice(0..3) - |> Enum.map(fn transaction -> transaction["raw"] end) - |> Enum.each(fn transaction -> - transaction_list = transaction |> Transaction.parse() |> Transaction.to_list() - - transaction_list - |> Stream.with_index() - |> Enum.each(fn {_value, index} -> - encoded_buffer = Enum.at(transaction_list, index) |> Base.encode16(case: :lower) - assert Enum.at(transaction, index) == "0x#{encoded_buffer}" - end) - end) - end end diff --git a/test/eth/transaction/signer_test.exs b/test/eth/transaction/signer_test.exs index f1b7c8a..1245b14 100644 --- a/test/eth/transaction/signer_test.exs +++ b/test/eth/transaction/signer_test.exs @@ -1,3 +1,5 @@ +require IEx + defmodule ETH.Transaction.Signer.Test do use ExUnit.Case import ETH.Utils @@ -15,18 +17,46 @@ defmodule ETH.Transaction.Signer.Test do # EIP 155 chainId - mainnet: 1, ropsten: 3 chain_id: 3 } + @encoded_example_private_key "75c3b11e480f8ba3db792424bebda1fc8dea2b254287e3a9af9ed50c7d255720" + @decoded_example_private_key Base.decode16!("75c3b11e480f8ba3db792424bebda1fc8dea2b254287e3a9af9ed50c7d255720", case: :mixed) + + setup_all do + ETH.TestClient.start - # TODO: rewrite the tests below + on_exit fn -> + ETH.TestClient.stop + end + + :ok + end test "hash/2 works" do target_hash = "DF2A7CB6D05278504959987A144C116DBD11CBDC50D6482C5BAE84A7F41E2113" + assert Transaction.hash(@first_example_transaction, false) |> Base.encode16() == target_hash assert @first_example_transaction |> Transaction.to_list() |> List.insert_at(-1, @first_example_transaction.chain_id) |> Transaction.hash(false) |> Base.encode16() == target_hash + next_target_hash = "B89EE1E3B4FF893AC8C435BE40EA94A1BA0EB3F64B48382DA967780BAFC8DBB1" + + assert Transaction.hash(@first_example_transaction) |> Base.encode16() == next_target_hash + assert @first_example_transaction + |> Transaction.to_list() + |> List.insert_at(-1, @first_example_transaction.chain_id) + |> Transaction.hash() + |> Base.encode16() == next_target_hash + + assert Transaction.hash(@first_example_transaction, true) |> Base.encode16() == next_target_hash + assert @first_example_transaction + |> Transaction.to_list() + |> List.insert_at(-1, @first_example_transaction.chain_id) + |> Transaction.hash(true) + |> Base.encode16() == next_target_hash + + first_transaction_list = @transactions |> Enum.at(2) @@ -60,28 +90,82 @@ defmodule ETH.Transaction.Signer.Test do decode16("f97c73fdca079da7652dbc61a46cd5aeef804008e057be3e712c43eac389aaf0") end - test "hash_transaction/2 works" do - result = - @first_example_transaction - |> Transaction.hash(false) + test "sign_transaction works for transaction maps with encoded private keys" do + output = + Transaction.build(%{ + nonce: 1, + to: "0x0dcd857b3c5db88cb7c025f0ef229331cfadffe5", + value: 22, + gas_limit: 100_000, + gas_price: 1000, + from: "0x42c343d8b77a9106d7112b71ba6b3030a34ba560" + }) + |> Transaction.sign_transaction(@encoded_example_private_key) |> Base.encode16(case: :lower) - assert result == "df2a7cb6d05278504959987a144c116dbd11cbdc50d6482c5bae84a7f41e2113" + serialized_hash = + "f862018203e8830186a0940dcd857b3c5db88cb7c025f0ef229331cfadffe516801ba09b35467cf48151683b41ed8425d59317716f4f639126d7eb69167ac95c8c3ba3a00d5d21f4c6fc400202dadc09a192b011cc16aefa6155d4e5df15d77d9f6c8f9f" + + assert output == serialized_hash end - test "sign_transaction/2 works" do - @transactions - |> Enum.slice(0..2) - |> Enum.each(fn transaction -> - signed_transaction_list = - transaction - |> Map.get("raw") - |> Transaction.parse() - |> Transaction.to_list() - |> Transaction.sign_transaction(transaction["privateKey"]) - - result = Transaction.get_sender_address(signed_transaction_list) - assert result == "0x" <> String.upcase(transaction["sendersAddress"]) - end) + test "sign_transaction works for transaction maps with decoded private keys" do + output = + Transaction.build(%{ + nonce: 1, + to: "0x0dcd857b3c5db88cb7c025f0ef229331cfadffe5", + value: 22, + gas_limit: 100_000, + gas_price: 1000, + from: "0x42c343d8b77a9106d7112b71ba6b3030a34ba560" + }) + |> Transaction.sign_transaction(@decoded_example_private_key) + |> Base.encode16(case: :lower) + + serialized_hash = + "f862018203e8830186a0940dcd857b3c5db88cb7c025f0ef229331cfadffe516801ba09b35467cf48151683b41ed8425d59317716f4f639126d7eb69167ac95c8c3ba3a00d5d21f4c6fc400202dadc09a192b011cc16aefa6155d4e5df15d77d9f6c8f9f" + + assert output == serialized_hash end + + # TODO: should I add ExRLP.encode() to sign_transaction(transaction_lists) it also changes the test results of above + # test "sign_transaction works for transaction lists with encoded private keys" do + # output = + # Transaction.build(%{ + # nonce: 1, + # to: "0x0dcd857b3c5db88cb7c025f0ef229331cfadffe5", + # value: 22, + # gas_limit: 100_000, + # gas_price: 1000, + # from: "0x42c343d8b77a9106d7112b71ba6b3030a34ba560" + # }) + # |> Transaction.to_list() + # |> Transaction.sign_transaction(@encoded_example_private_key) + # |> Base.encode16(case: :lower) + # + # serialized_hash = + # "f862018203e8830186a0940dcd857b3c5db88cb7c025f0ef229331cfadffe516801ba09b35467cf48151683b41ed8425d59317716f4f639126d7eb69167ac95c8c3ba3a00d5d21f4c6fc400202dadc09a192b011cc16aefa6155d4e5df15d77d9f6c8f9f" + # + # assert output == serialized_hash + # end + # + # test "sign_transaction works for transaction lists with decoded private keys" do + # output = + # Transaction.build(%{ + # nonce: 1, + # to: "0x0dcd857b3c5db88cb7c025f0ef229331cfadffe5", + # value: 22, + # gas_limit: 100_000, + # gas_price: 1000, + # from: "0x42c343d8b77a9106d7112b71ba6b3030a34ba560" + # }) + # |> Transaction.to_list() + # |> Transaction.sign_transaction(@decoded_example_private_key) + # |> Base.encode16(case: :lower) + # + # serialized_hash = + # "f862018203e8830186a0940dcd857b3c5db88cb7c025f0ef229331cfadffe516801ba09b35467cf48151683b41ed8425d59317716f4f639126d7eb69167ac95c8c3ba3a00d5d21f4c6fc400202dadc09a192b011cc16aefa6155d4e5df15d77d9f6c8f9f" + # + # assert output == serialized_hash + # end end diff --git a/test/eth/transaction_test.exs b/test/eth/transaction_test.exs index 0ce6a7c..8a5ca78 100644 --- a/test/eth/transaction_test.exs +++ b/test/eth/transaction_test.exs @@ -46,27 +46,53 @@ defmodule TransactionTest do @transactions File.read!("test/fixtures/transactions.json") |> Poison.decode!() @eip155_transactions File.read!("test/fixtures/eip155_vitalik_tests.json") |> Poison.decode!() + test "send_transaction(wallet, params) works" do + result = Transaction.send_transaction(@first_wallet_in_client, %{ + to: @first_random_wallet.eth_address, + value: 22 + }) + {:ok, transaction_hash} = result + + assert result == {:ok, "0x5c1cf004a7d239c65e1ef582826258b7835b0301063605c238947682fe3303d8"} + + Process.sleep(3850) + + assert ETH.get_transaction!(transaction_hash) |> Map.drop([:block_hash, :block_number]) == %{ + from: "0x051d51ba1e1d58db72efea63549a6792c8f5cb13", + gas: 21000, + gas_price: 20000000000, + hash: "0x5c1cf004a7d239c65e1ef582826258b7835b0301063605c238947682fe3303d8", + input: "0x0", + nonce: 0, + to: "0xdf7a2dc05778d1b507e921fb8ad78cb431590ba7", + transaction_index: 0, + value: 22 + } + end + + # Transaction.build(%{ + # nonce: 1, + # to: "0x0dcd857b3c5db88cb7c025f0ef229331cfadffe5", + # value: 22, + # gas_limit: 100_000, + # gas_price: 1000, + # from: "0x42c343d8b77a9106d7112b71ba6b3030a34ba560" + # }) + # |> Transaction.sign_transaction( + # "75c3b11e480f8ba3db792424bebda1fc8dea2b254287e3a9af9ed50c7d255720" + # ) + # |> Base.encode16(case: :lower) + + test "send works" do + result = Transaction.send("f862018203e8830186a0940dcd857b3c5db88cb7c025f0ef229331cfadffe516801ba09b35467cf48151683b41ed8425d59317716f4f639126d7eb69167ac95c8c3ba3a00d5d21f4c6fc400202dadc09a192b011cc16aefa6155d4e5df15d77d9f6c8f9f") + + assert result == {:ok, "0xfa19fa6afd6c5b5ef9979ecf3b437e0b844484cc3a3b6f97082be60799767510"} + end + + test "send! works" do + result = Transaction.send!("f862018203e8830186a0940dcd857b3c5db88cb7c025f0ef229331cfadffe516801ba09b35467cf48151683b41ed8425d59317716f4f639126d7eb69167ac95c8c3ba3a00d5d21f4c6fc400202dadc09a192b011cc16aefa6155d4e5df15d77d9f6c8f9f") - # NOTE: this should probably go somewhere else - test "send_transaction_works" do - output = - Transaction.build(%{ - nonce: 1, - to: "0x0dcd857b3c5db88cb7c025f0ef229331cfadffe5", - value: 22, - gas_limit: 100_000, - gas_price: 1000, - from: "0x42c343d8b77a9106d7112b71ba6b3030a34ba560" - }) - |> Transaction.sign_transaction( - "75c3b11e480f8ba3db792424bebda1fc8dea2b254287e3a9af9ed50c7d255720" - ) - |> Base.encode16(case: :lower) - - serialized_hash = - "f862018203e8830186a0940dcd857b3c5db88cb7c025f0ef229331cfadffe516801ba09b35467cf48151683b41ed8425d59317716f4f639126d7eb69167ac95c8c3ba3a00d5d21f4c6fc400202dadc09a192b011cc16aefa6155d4e5df15d77d9f6c8f9f" - - assert output == serialized_hash + assert result == "0xfa19fa6afd6c5b5ef9979ecf3b437e0b844484cc3a3b6f97082be60799767510" end # test "send_transaction(wallet, params) works when params is a wallet" do @@ -87,12 +113,15 @@ defmodule TransactionTest do # TODO: get_sender_address works on all variations - test "get_sender_address/1 works" do + test "get_sender_adress/1 works" do @transactions |> Enum.slice(0..2) |> Enum.each(fn transaction -> transaction_list = - transaction |> Map.get("raw") |> Transaction.parse() |> Transaction.to_list() + transaction + |> Map.get("raw") + |> Transaction.parse() + |> Transaction.to_list() result = Transaction.get_sender_address(transaction_list) assert result == "0x" <> String.upcase(transaction["sendersAddress"]) diff --git a/test/support/eth_client.exs b/test/support/eth_client.exs index f093e7b..f4843ca 100644 --- a/test/support/eth_client.exs +++ b/test/support/eth_client.exs @@ -1,7 +1,7 @@ defmodule ETH.TestClient do def start do spawn(fn -> - "testrpc -b=1 -m=\" parent leopard beauty edit tilt what blast next huge need print advice evolve move explain govern grab raccoon gown gravity gloom walnut silver reopen\"" + "ganache-cli -b=1 -m=\" parent leopard beauty edit tilt what blast next huge need print advice evolve move explain govern grab raccoon gown gravity gloom walnut silver reopen\"" |> String.to_charlist |> :os.cmd() end) @@ -10,7 +10,7 @@ defmodule ETH.TestClient do end def stop do - "pkill -f testrpc" + "pkill -f ganache-cli" |> String.to_charlist |> :os.cmd() end