Skip to content

Commit

Permalink
major improvements, cleaning up the API
Browse files Browse the repository at this point in the history
  • Loading branch information
izelnakri committed Sep 7, 2017
1 parent 6ac02ce commit cac79a4
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 90 deletions.
133 changes: 55 additions & 78 deletions lib/eth/transaction.ex
Original file line number Diff line number Diff line change
@@ -1,15 +1,38 @@
require IEx
defmodule ETH.Transaction do
# NOTE: add ExRLP.encode() / .serialize() to complete this
import ETH.Utils

alias ETH.Query

defdelegate parse(data), to: ETH.Transaction.Parser
defdelegate to_list(data), to: ETH.Transaction.Parser
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
# TODO: allow ETH.Transaction.Wallet
defdelegate sign_transaction(transaction, private_key), to: ETH.Transaction.Signer
defdelegate sign_transaction_list(transaction_list, private_key), to: ETH.Transaction.Signer
defdelegate hash(transaction_list), to: ETH.Transaction.Signer
defdelegate hash(transaction_list, include_signature), to: ETH.Transaction.Signer

def hash(transaction, include_signature \\ true)
def hash(transaction, include_signature) when is_list(transaction) do
ETH.Transaction.Signer.hash_transaction_list(transaction, include_signature)
end
def hash(transaction=%{}, include_signature) do
ETH.Transaction.Signer.hash_transaction(transaction, include_signature)
end

# def send_transaction(params = [from: _from, to: _to, value: _value], private_key) do
# set(params)
# |> sign_transaction_list(private_key)
# |> send
# end
# def send_transaction(params = %{from: _from, to: _to, value: _value}, private_key) do
# Map.to_list(params)
# |> set
# |> sign_transaction_list(private_key)
# |> send
# end

# def set(params = [from: from, to: to, value: value]) do
# gas_price = Keyword.get(params, :gas_price, ETH.Query.gas_price())
Expand All @@ -29,12 +52,38 @@ defmodule ETH.Transaction do
# end)
# |> Enum.into(%{})
# end

# def sign_transaction(transaction=%{}) do
# transaction_list =
# end

def get_sender_address(transaction_list = [nonce, gas_price, gas_limit, to, value, data, v, r, s]) do
message_hash = hash(transaction_list, false)
def get_senders_public_key("0x" <> rlp_encoded_transaction_list) do # NOTE: not tested
rlp_encoded_transaction_list
|> 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
]), do: to_senders_public_key(transaction_list)

def get_sender_address("0x" <> rlp_encoded_transaction_list) do # NOTE: not tested
rlp_encoded_transaction_list
|> get_senders_public_key
|> get_address
end
def get_sender_address(transaction_list = [
nonce, gas_price, gas_limit, to, value, data, v, r, s
]), do: get_senders_public_key(transaction_list) |> get_address

def send(signature), do: Ethereumex.HttpClient.eth_send_raw_transaction([signature])

# NOTE: maybe add encode / decode


defp to_senders_public_key(transaction_list = [
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))
v_int = buffer_to_int(v)
target_v = if chain_id > 0, do: v_int - (chain_id * 2 + 8), else: v_int
Expand All @@ -43,80 +92,8 @@ defmodule ETH.Transaction do
recovery_id = target_v - 27

{:ok, public_key} = :libsecp256k1.ecdsa_recover_compact(message_hash, signature, :uncompressed, recovery_id)
get_address(public_key)
end

# def sign_transaction_list(transaction = %{
# to: _to, value: _value, data: _data, gas_price: _gas_price, gas_limit: _gas_limit,
# nonce: _nonce
# }, << private_key :: binary-size(32) >>), do: sign_transaction(transaction, private_key)
# def sign_transaction_list(transaction = %{
# to: _to, value: _value, data: _data, gas_price: _gas_price, gas_limit: _gas_limit,
# nonce: _nonce
# }, << encoded_private_key :: binary-size(64) >>) do
# decoded_private_key = Base.decode16!(encoded_private_key, case: :mixed)
# sign_transaction(transaction, decoded_private_key)
# end

def send(signature), do: Ethereumex.HttpClient.eth_send_raw_transaction([signature])

# def send_transaction(params = [from: _from, to: _to, value: _value], private_key) do
# set(params)
# |> sign_transaction_list(private_key)
# |> send
# end
# def send_transaction(params = %{from: _from, to: _to, value: _value}, private_key) do
# Map.to_list(params)
# |> set
# |> sign_transaction_list(private_key)
# |> send
# end

def hash_transaction(transaction, include_signature \\ true)
def hash_transaction(transaction = %{
to: _to, value: _value, data: _data, gas_price: _gas_price, gas_limit: _gas_limit,
nonce: _nonce
}, include_signature) do
chain_id = get_chain_id(Map.get(transaction, :v, <<28>>), Map.get(transaction, :chain_id))

transaction
|> Map.delete(:chain_id)
|> to_list
|> List.insert_at(-1, chain_id)
|> hash(include_signature)
end
def hash_transaction(transaction=%{}, include_signature) do
chain_id = get_chain_id(Map.get(transaction, :v, <<28>>), Map.get(transaction, :chain_id))

transaction
|> Map.delete(:chain_id)
|> to_list
|> List.insert_at(-1, chain_id)
|> hash(include_signature)
public_key
end

# defp sign_transaction(transaction = %{
# to: _to, value: _value, data: _data, gas_price: _gas_price, gas_limit: _gas_limit,
# nonce: _nonce
# }, << private_key :: binary-size(32) >>) do
# hash = hash_transaction(transaction)
# IO.puts("hash is")
# IO.puts(hash)
# [signature: signature, recovery: recovery] = secp256k1_signature(hash, private_key)
#
# << r :: binary-size(32) >> <> << s :: binary-size(32) >> = signature
#
# # this will change v, r, s
# transaction
# # |> Map.merge(%{r: encode16(r), s: encode16(s), v: encode16(<<recovery + 27>>)})
# |> adjust_v_for_chain_id
# |> to_list
# |> Enum.map(fn(x) ->
# # IEx.pry
# Base.decode16!(x, case: :mixed)
# end)
# |> ExRLP.encode
# end
end


Expand Down
58 changes: 46 additions & 12 deletions lib/eth/transaction/signer.ex
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
defmodule ETH.Transaction.Signer do
import ETH.Utils

def sign_transaction_list(transaction_list = [
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
], << 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)
alias ETH.Transaction.Parser, as: TransactionParser

def hash_transaction(transaction, include_signature \\ true)
def hash_transaction(transaction = %{
to: _to, value: _value, data: _data, gas_price: _gas_price, gas_limit: _gas_limit,
nonce: _nonce
}, include_signature) do
chain_id = get_chain_id(Map.get(transaction, :v, <<28>>), Map.get(transaction, :chain_id))

transaction
|> Map.delete(:chain_id)
|> TransactionParser.to_list
|> List.insert_at(-1, chain_id)
|> hash_transaction_list(include_signature)
end
# def hash_transaction(transaction=%{}, include_signature) do # TODO: check if this is necessary
# chain_id = get_chain_id(Map.get(transaction, :v, <<28>>), Map.get(transaction, :chain_id))
#
# transaction
# |> Map.delete(:chain_id)
# |> TransactionParser.to_list
# |> List.insert_at(-1, chain_id)
# |> hash_transaction_list(include_signature)
# end

def hash(transaction_list, include_signature \\ true) when is_list(transaction_list) do # NOTE: use internally
def hash_transaction_list(
transaction_list, include_signature \\ true
) when is_list(transaction_list) do # NOTE: usage is generally internal
target_list = case include_signature do
true -> transaction_list
false ->
Expand All @@ -32,11 +47,30 @@ defmodule ETH.Transaction.Signer do
|> keccak256
end

def sign_transaction(transaction, private_key) when is_map(transaction) do
transaction
|> ETH.Transaction.to_list
|> sign_transaction_list(private_key)
|> ExRLP.encode
end

def sign_transaction_list(transaction_list = [
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
], << 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
chain_id = get_chain_id(v, Enum.at(transaction_list, 9))
message_hash = hash(transaction_list, false)
message_hash = hash_transaction_list(transaction_list, false)

[signature: signature, recovery: recovery] = secp256k1_signature(message_hash, private_key)

Expand Down

0 comments on commit cac79a4

Please # to comment.