diff --git a/lib/eth.ex b/lib/eth.ex index d56b026..922479f 100644 --- a/lib/eth.ex +++ b/lib/eth.ex @@ -1,5 +1,3 @@ -require IEx -# elliptic curve cryptography library for signing transactions in Ethereum defmodule ETH do @moduledoc """ Documentation for Eth. @@ -14,4 +12,55 @@ defmodule ETH do :world """ + Application.put_env(:ethereumex, :scheme, Application.get_env(:eth, :scheme, "http")) + Application.put_env(:ethereumex, :host, Application.get_env(:eth, :host, "localhost")) + Application.put_env(:ethereumex, :port, Application.get_env(:eth, :port, 8545)) + + defdelegate block_number, to: ETH.Query + defdelegate syncing, to: ETH.Query + defdelegate get_accounts, to: ETH.Query + defdelegate gas_price, to: ETH.Query + defdelegate call(call_params), to: ETH.Query + defdelegate get_balance(wallet_or_address, denomination), to: ETH.Query + defdelegate get_transaction(transaction_hash), to: ETH.Query + defdelegate get_transaction_receipt(transaction_hash), to: ETH.Query + defdelegate get_transaction_count(wallet_or_address), to: ETH.Query + defdelegate estimate_gas(transaction), to: ETH.Query + + defdelegate parse(data), to: ETH.Transaction.Parser + defdelegate to_list(data), to: ETH.Transaction.Parser + + defdelegate set(params), to: ETH.Transaction.Setter + defdelegate set(wallet, params), to: ETH.Transaction.Setter + defdelegate set(sender_wallet, receiver_wallet, params_or_value), to: ETH.Transaction.Setter + + defdelegate hash_transaction(transaction), to: ETH.Transaction.Signer + defdelegate hash_transaction(transaction, include_signature), to: ETH.Transaction.Signer + defdelegate hash_transaction_list(transaction_list), to: ETH.Transaction.Signer + defdelegate hash_transaction_list(transaction_list, include_signature), to: ETH.Transaction.Signer + defdelegate sign_transaction(transaction, private_key), to: ETH.Transaction.Signer + defdelegate sign_transaction_list(transaction_list, private_key), to: ETH.Transaction.Signer + defdelegate decode(rlp_encoded_transaction), to: ETH.Transaction.Signer + defdelegate encode(signed_transaction_list), to: ETH.Transaction.Signer + + defdelegate hash(transaction, include_signature), to: ETH.Transaction + defdelegate send_transaction(params_or_wallet, private_key_or_params), to: ETH.Transaction + defdelegate send_transaction(sender_wallet, receiver_wallet, value_or_params), to: ETH.Transaction + defdelegate send_transaction(sender_wallet, receiver_wallet, value_or_params, private_key), 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 + + defdelegate get_private_key, to: ETH.Utils + defdelegate get_public_key(private_key), to: ETH.Utils + defdelegate get_address(private_or_public_key), to: ETH.Utils + defdelegate convert(value, denomination), to: ETH.Utils + defdelegate secp256k1_signature(hash, private_key), to: ETH.Utils + defdelegate keccak256(data), to: ETH.Utils + defdelegate encode16(data), to: ETH.Utils + defdelegate decode16(decoded_data), to: ETH.Utils + defdelegate to_buffer(data), to: ETH.Utils + defdelegate buffer_to_int(data), to: ETH.Utils + defdelegate pad_to_even(data), to: ETH.Utils + defdelegate get_chain_id(v, chain_id), to: ETH.Utils end diff --git a/lib/eth/query.ex b/lib/eth/query.ex index 5cd5fea..43b94c8 100644 --- a/lib/eth/query.ex +++ b/lib/eth/query.ex @@ -16,6 +16,11 @@ defmodule ETH.Query do |> get_result end + def gas_price do + Ethereumex.HttpClient.eth_gas_price() + |> get_number_result + end + def call(call_params) do Ethereumex.HttpClient.eth_call([call_params]) |> get_result @@ -83,13 +88,8 @@ defmodule ETH.Query do |> get_number_result end - def gas_price do - Ethereumex.HttpClient.eth_gas_price() - |> get_number_result - end - def estimate_gas(transaction \\ %{data: ""}) - def estimate_gas(transaction = %{to: to, data: data}) do + def estimate_gas(transaction = %{to: _to, data: _data}) do Ethereumex.HttpClient.eth_estimate_gas([transaction]) |> get_number_result end diff --git a/lib/eth/transaction.ex b/lib/eth/transaction.ex index 79a06dc..4e76de9 100644 --- a/lib/eth/transaction.ex +++ b/lib/eth/transaction.ex @@ -1,13 +1,13 @@ defmodule ETH.Transaction do import ETH.Utils - alias ETH.Query + defdelegate parse(data), to: ETH.Transaction.Parser + defdelegate to_list(data), to: ETH.Transaction.Parser defdelegate set(params), to: ETH.Transaction.Setter defdelegate set(wallet, params), to: ETH.Transaction.Setter - defdelegate set(sender_wallet, receiver_wallet, params), to: ETH.Transaction.Setter - defdelegate parse(data), to: ETH.Transaction.Parser # NOTE: improve this one - defdelegate to_list(data), to: ETH.Transaction.Parser + defdelegate set(sender_wallet, receiver_wallet, params_or_value), to: ETH.Transaction.Setter + defdelegate hash_transaction(transaction), to: ETH.Transaction.Signer defdelegate hash_transaction(transaction, include_signature), to: ETH.Transaction.Signer defdelegate hash_transaction_list(transaction_list), to: ETH.Transaction.Signer @@ -71,17 +71,17 @@ defmodule ETH.Transaction do def get_senders_public_key("0x" <> rlp_encoded_transaction_list) do # NOTE: not tested rlp_encoded_transaction_list - |> ExRLP.decode + |> Base.decode16!(case: :mixed) |> to_senders_public_key end def get_senders_public_key(<>) do encoded_tx - |> Base.decode(case: :mixed) + |> Base.decode16!(case: :mixed) |> ExRLP.decode |> to_senders_public_key end def get_senders_public_key(transaction_list = [ - nonce, gas_price, gas_limit, to, value, data, v, r, s + _nonce, _gas_price, _gas_limit, _to, _value, _data, _v, _r, _s ]), do: to_senders_public_key(transaction_list) def get_sender_address("0x" <> rlp_encoded_transaction_list) do # NOTE: not tested @@ -92,17 +92,17 @@ defmodule ETH.Transaction do end def get_sender_address(<>) do encoded_tx - |> Base.decode(case: :mixed) + |> Base.decode16!(case: :mixed) |> ExRLP.decode |> to_senders_public_key |> get_address end def get_sender_address(transaction_list = [ - nonce, gas_price, gas_limit, to, value, data, v, r, s + _nonce, _gas_price, _gas_limit, _to, _value, _data, _v, _r, _s ]), do: get_senders_public_key(transaction_list) |> get_address defp to_senders_public_key(transaction_list = [ - nonce, gas_price, gas_limit, to, value, data, v, r, s + _nonce, _gas_price, _gas_limit, _to, _value, _data, v, r, s ]) do message_hash = hash_transaction_list(transaction_list, false) chain_id = get_chain_id(v, Enum.at(transaction_list, 9)) diff --git a/lib/eth/transaction/parser.ex b/lib/eth/transaction/parser.ex index a9e5676..3a489be 100644 --- a/lib/eth/transaction/parser.ex +++ b/lib/eth/transaction/parser.ex @@ -74,9 +74,4 @@ defmodule ETH.Transaction.Parser do [nonce, gas_price, gas_limit, to, value, data, v, r, s] |> Enum.map(fn(value) -> to_buffer(value) end) end - # def to_list(transaction = %{}) do # TODO: adjust this probably - # v = Map.get(transaction, :v, <<28>>) - # ["", "", "", "", "", "", v, "", ""] - # |> Enum.map(fn(value) -> to_buffer(value) end) - # end end diff --git a/lib/eth/transaction/setter.ex b/lib/eth/transaction/setter.ex index e29a8c1..80abfbc 100644 --- a/lib/eth/transaction/setter.ex +++ b/lib/eth/transaction/setter.ex @@ -13,8 +13,12 @@ defmodule ETH.Transaction.Setter do end def set(wallet, params) do result = cond do - is_map(params) -> set_params_from_map(params) - is_list(params) -> set_params_from_list(params) + is_map(params) -> + target_params = params |> Map.merge(%{from: wallet.eth_address}) + set_params_from_map(target_params) + is_list(params) -> + target_params = params |> Keyword.merge([from: wallet.eth_address]) + set_params_from_list(target_params) end parse(result) diff --git a/lib/eth/transaction/signer.ex b/lib/eth/transaction/signer.ex index d1d18dd..b29da88 100644 --- a/lib/eth/transaction/signer.ex +++ b/lib/eth/transaction/signer.ex @@ -52,20 +52,20 @@ defmodule ETH.Transaction.Signer do end def sign_transaction_list(transaction_list = [ - nonce, gas_price, gas_limit, to, value, data, v, r, s + _nonce, _gas_price, _gas_limit, _to, _value, _data, _v, _r, _s ], << private_key :: binary-size(32) >>) do to_signed_transaction_list(transaction_list, private_key) end def sign_transaction_list(transaction_list = [ - nonce, gas_price, gas_limit, to, value, data, v, r, s + _nonce, _gas_price, _gas_limit, _to, _value, _data, _v, _r, _s ], << encoded_private_key :: binary-size(64) >>) do decoded_private_key = Base.decode16!(encoded_private_key, case: :mixed) to_signed_transaction_list(transaction_list, decoded_private_key) end defp to_signed_transaction_list(transaction_list = [ - nonce, gas_price, gas_limit, to, value, data, v, r, s - ], << private_key :: binary-size(32) >>) do # NOTE: this part is problematic + nonce, gas_price, gas_limit, to, value, data, v, _r, _s + ], << private_key :: binary-size(32) >>) do chain_id = get_chain_id(v, Enum.at(transaction_list, 9)) message_hash = hash_transaction_list(transaction_list, false) diff --git a/lib/eth/utils.ex b/lib/eth/utils.ex index d727e14..f73ff97 100644 --- a/lib/eth/utils.ex +++ b/lib/eth/utils.ex @@ -33,9 +33,8 @@ defmodule ETH.Utils do "0x#{Base.encode16(eth_address)}" end - # NOTE: not tested area: - def convert(number, magnitute \\ :ether) do - denomination = [ + def convert(number, denomination \\ :ether) do # NOTE: not tested area: + denom = [ wei: 1, kwei: 1000, mwei: 1000000, @@ -47,9 +46,9 @@ defmodule ETH.Utils do finney: 1000000000000000, milli: 1000000000000000, ether: 1000000000000000000, - ] |> List.keyfind(magnitute, 0) |> elem(1) + ] |> List.keyfind(denomination, 0) |> elem(1) - number / denomination + number / denom end def secp256k1_signature(hash, private_key) do @@ -86,10 +85,6 @@ defmodule ETH.Utils do number end - defp buffer_to_json_value(data) do - "0x" <> Base.encode16(data, case: :mixed) - end - def pad_to_even(data) do if rem(String.length(data), 2) == 1, do: "0#{data}", else: data end @@ -98,6 +93,7 @@ defmodule ETH.Utils do computed_chain_id = compute_chain_id(v) if computed_chain_id == 0, do: (chain_id || 0), else: computed_chain_id end + defp compute_chain_id("0x" <> v) do sig_v = buffer_to_int(v) chain_id = Float.floor((sig_v - 35) / 2) @@ -108,6 +104,10 @@ defmodule ETH.Utils do chain_id = Float.floor((sig_v - 35) / 2) if chain_id < 0, do: 0, else: Kernel.trunc(chain_id) end + + # defp buffer_to_json_value(buffer) do + # "0x" <> Base.encode16(buffer, case: :mixed) + # end end # NOTE: old version that is error-prone: diff --git a/test/eth/transaction_test.exs b/test/eth/transaction_test.exs index 19a6260..3654e7a 100644 --- a/test/eth/transaction_test.exs +++ b/test/eth/transaction_test.exs @@ -3,9 +3,6 @@ defmodule ETH.TransactionTest do use ExUnit.Case import ETH.Utils - @first_example_wallet %{ - private_key: "e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109" - } @first_example_transaction %{ nonce: "0x00", gas_price: "0x09184e72a000", @@ -27,7 +24,7 @@ defmodule ETH.TransactionTest do transaction_list |> Stream.with_index - |> Enum.each(fn({value, 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) @@ -84,7 +81,7 @@ defmodule ETH.TransactionTest do end test "get_sender_address/1 works" do - transactons = @transactions + @transactions |> Enum.slice(0..2) |> Enum.each(fn(transaction) -> transaction_list = transaction |> Map.get("raw") |> ETH.Transaction.parse |> ETH.Transaction.to_list @@ -95,7 +92,7 @@ defmodule ETH.TransactionTest do end test "sign/2 works" do - transactons = @transactions + @transactions |> Enum.slice(0..2) |> Enum.each(fn(transaction) -> signed_transaction_list = transaction @@ -120,8 +117,6 @@ defmodule ETH.TransactionTest do end test "send_transaction_works" do - client_accounts = ETH.Query.get_accounts - output = ETH.Transaction.set(%{ nonce: 1, to: "0x0dcd857b3c5db88cb7c025f0ef229331cfadffe5", value: 22, gas_limit: 100000, gas_price: 1000, from: "0x42c343d8b77a9106d7112b71ba6b3030a34ba560" diff --git a/test/eth/transactions_test.exs b/test/eth/transactions_test.exs index 2b517a9..fc8d9d3 100644 --- a/test/eth/transactions_test.exs +++ b/test/eth/transactions_test.exs @@ -1,9 +1,9 @@ -require IEx +# require IEx defmodule ETH.TransactionsTest do use ExUnit.Case - @transactions File.read!("test/fixtures/transactions.json") |> Poison.decode! - @eip155_transactions File.read!("test/fixtures/eip155_vitalik_tests.json") |> Poison.decode! + # @transactions File.read!("test/fixtures/transactions.json") |> Poison.decode! + # @eip155_transactions File.read!("test/fixtures/eip155_vitalik_tests.json") |> Poison.decode! # test "verify signature" do # @transactions |> Enum.each(fn(transaction) ->