Skip to content

Commit

Permalink
major progress on hashing
Browse files Browse the repository at this point in the history
  • Loading branch information
izelnakri committed Sep 5, 2017
1 parent 95bcf71 commit 60cbaf9
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 43 deletions.
62 changes: 37 additions & 25 deletions lib/eth/transaction.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ defmodule ETH.Transaction do
r: to_buffer(r), s: to_buffer(s)
}
end
# def parse(params) do
# # add default values
# end

def buffer_to_map(_transaction_buffer = [nonce, gas_price, gas_limit, to, value, data, v, r, s]) do
%{
Expand All @@ -48,11 +51,6 @@ defmodule ETH.Transaction do
}
end

# def parse(params) do
# # add default values
# end


def set(params = [from: from, to: to, value: value]) do
gas_price = Keyword.get(params, :gas_price, ETH.Query.gas_price())
data = Keyword.get(params, :data, "")
Expand Down Expand Up @@ -103,16 +101,14 @@ defmodule ETH.Transaction do
def hash_transaction(transaction = %{
to: _to, value: _value, data: _data, gas_price: _gas_price, gas_limit: _gas_limit,
nonce: _nonce
}) do
chain_id = Map.get(transaction, :chain_id, 0)
}, include_signature \\ true) do
chain_id = get_chain_id(Map.get(transaction, :v, <<28>>), Map.get(transaction, :chain_id))

transaction
|> Map.merge(%{v: encode16(<<chain_id>>), r: <<>>, s: <<>>})
# |> Map.merge(%{v: Map.get(transaction, :v, <<28>>)})
|> to_list
|> Enum.map(fn(x) ->
# IEx.pry
Base.decode16!(x, case: :mixed)
end)
|> hash
|> List.insert_at(-1, chain_id)
|> hash(include_signature)
end

def hash(transaction_list, include_signature \\ true) do # NOTE: use internally
Expand All @@ -123,14 +119,13 @@ defmodule ETH.Transaction do
# when computing the hash of a transaction for purposes of signing or recovering,
# instead of hashing only the first six elements (ie. nonce, gasprice, startgas, to, value, data),
# hash nine elements, with v replaced by CHAIN_ID, r = 0 and s = 0
list = transaction_list |> Enum.take(6)
v = transaction_list |> Enum.at(6)
chain_id = get_chain_id(v)
if chain_id > 0, do: list ++ [chain_id, "", ""], else: list # NOTE: this part is dangerous: in JS we change state(v: chainId, r: 0, s: 0)
list = Enum.take(transaction_list, 6)
v = Enum.at(transaction_list, 6) || <<28>>
chain_id = get_chain_id(v, Enum.at(transaction_list, 9))
if chain_id > 0, do: list ++ [chain_id, 0, 0], else: list
end

target_list
|> Enum.map(fn(value) -> to_buffer(value) end)
|> ExRLP.encode
|> keccak256
end
Expand All @@ -141,13 +136,14 @@ defmodule ETH.Transaction do
}, << private_key :: binary-size(32) >>) do
hash = hash_transaction(transaction)
IO.puts("hash is")
# IO.puts(hash)
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>>)})
# |> Map.merge(%{r: encode16(r), s: encode16(s), v: encode16(<<recovery + 27>>)})
|> adjust_v_for_chain_id
|> to_list
|> Enum.map(fn(x) ->
Expand All @@ -171,14 +167,21 @@ defmodule ETH.Transaction do
end
end

def to_list(transaction = %{
nonce: nonce, gas_price: gas_price, gas_limit: gas_limit, to: to, value: value, data: data,
v: v, r: r, s: s
}) 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 = %{
nonce: nonce, gas_price: gas_price, gas_limit: gas_limit, to: to, value: value, data: data
}) do
v = Map.get(transaction, :v, Base.encode16(<<28>>, case: :lower)) # this probably needs to change
v = Map.get(transaction, :v, <<28>>)
r = Map.get(transaction, :r, "")
s = Map.get(transaction, :s, "")

[nonce, gas_price, gas_limit, to, value, data, v, r, s] # NOTE: maybe this should turn things toBuffer
[nonce, gas_price, gas_limit, to, value, data, v, r, s]
|> Enum.map(fn(value) -> to_buffer(value) end)
end

Expand Down Expand Up @@ -208,12 +211,18 @@ defmodule ETH.Transaction do
def to_hex(value), do: HexPrefix.encode(value)
# def to_json() # transaction_map or transaction_list

defp get_chain_id("0x" <> v) do


def get_chain_id(v, chain_id \\ nil) 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)
if chain_id < 0, do: 0, else: Kernel.trunc(chain_id)
end
defp get_chain_id(v) do
defp compute_chain_id(v) do
sig_v = buffer_to_int(v)
chain_id = Float.floor((sig_v - 35) / 2)
if chain_id < 0, do: 0, else: Kernel.trunc(chain_id)
Expand All @@ -234,8 +243,11 @@ defmodule ETH.Transaction do

def to_buffer(nil), do: ""
def to_buffer(data) when is_number(data) do
pad_to_even(Integer.to_string(data, 16)) |> Base.decode16!(case: :mixed)
padded_data = pad_to_even(Integer.to_string(data, 16))
# IEx.pry
padded_data
end
def to_buffer("0x00"), do: ""
def to_buffer("0x" <> data) do
padded_data = pad_to_even(data)
case Base.decode16(padded_data, case: :mixed) do
Expand Down
35 changes: 17 additions & 18 deletions test/eth/transaction_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,28 @@ defmodule ETH.TransactionTest do
import ETH.Utils
# TODO: "it should decode transactions"

@first_transaction_list [
"", "09184e72a000", "2710", "0000000000000000000000000000000000000000", "",
"7f7465737432000000000000000000000000000000000000000000000000000000600057", "29",
"f2d54d3399c9bcd3ac3482a5ffaeddfe68e9a805375f626b4f2f8cf530c2d95a",
"5b3bb54e6e8db52083a9b674e578c843a87c292f0383ddba168573808d36dc8e"
] |> Enum.map(fn(x) -> decode16(x) end)

@first_example_wallet %{
private_key: "e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109"
}
@first_example_transaction %{
nonce: "",
gas_price: "09184e72a000",
gas_limit: "2710",
to: "0000000000000000000000000000000000000000",
value: "",
data: "7f7465737432000000000000000000000000000000000000000000000000000000600057",
nonce: "0x00",
gas_price: "0x09184e72a000",
gas_limit: "0x2710",
to: "0x0000000000000000000000000000000000000000",
value: "0x00",
data: "0x7f7465737432000000000000000000000000000000000000000000000000000000600057",
chain_id: 3 # EIP 155 chainId - mainnet: 1, ropsten: 3
}
@transactions File.read!("test/fixtures/transactions.json") |> Poison.decode!
@eip155_transactions File.read!("test/fixtures/eip155_vitalik_tests.json") |> Poison.decode!

test "hash/1 works" do
target_hash = "5C207A650B59A8C2D1271F5CBDA78A658CB411A87271D68062E61AB1A3F85CF9"
assert ETH.Transaction.hash(@first_transaction_list) |> Base.encode16 == target_hash
target_hash = "DF2A7CB6D05278504959987A144C116DBD11CBDC50D6482C5BAE84A7F41E2113"
assert @first_example_transaction
|> ETH.Transaction.to_list
|> List.insert_at(-1, @first_example_transaction.chain_id)
|> ETH.Transaction.hash(false)
|> Base.encode16 == target_hash

first_transaction_list = @transactions
|> Enum.at(2)
Expand Down Expand Up @@ -64,9 +61,11 @@ defmodule ETH.TransactionTest do
end

test "hash_transaction/2 works" do
result = ETH.Transaction.hash_transaction(@first_example_transaction) |> Base.encode16(case: :lower)
target_digest = "df2a7cb6d05278504959987a144c116dbd11cbdc50d6482c5bae84a7f41e2113"
assert result == target_digest
result = @first_example_transaction
|> ETH.Transaction.hash_transaction(false)
|> Base.encode16(case: :lower)

assert result == "df2a7cb6d05278504959987a144c116dbd11cbdc50d6482c5bae84a7f41e2113"
end

test "sign/2 works" do
Expand Down

0 comments on commit 60cbaf9

Please # to comment.