-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathlocal.ex
93 lines (75 loc) · 2.83 KB
/
local.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
defmodule Ethers.Signer.Local do
@moduledoc """
Local signer works with a private key.
IMPORTANT: Please note that using this signer is discouraged in production
environment since handling private keys in those environment can be challenging
if you don't know what you are doing.
## Signer Options
- `:private_key`: The private key to use for signing and calculating account address.
Private key can either be in binary format (32 bytes) or it's hex encoded format with or
without `0x` prefix.
"""
@behaviour Ethers.Signer
import Ethers, only: [secp256k1_module: 0, keccak_module: 0]
alias Ethers.Transaction
alias Ethers.Transaction.Signed
alias Ethers.Utils
if not Code.ensure_loaded?(secp256k1_module()) do
@impl true
def sign_transaction(_tx, _opts), do: {:error, :secp256k1_module_not_loaded}
@impl true
def accounts(_opts), do: {:error, :secp256k1_module_not_loaded}
end
@impl true
def sign_transaction(transaction, opts) do
with {:ok, private_key} <- private_key(opts),
:ok <- validate_private_key(private_key, Keyword.get(opts, :from)),
encoded = Transaction.encode(transaction),
sign_hash = keccak_module().hash_256(encoded),
{:ok, {r, s, recovery_id}} <- secp256k1_module().sign(sign_hash, private_key) do
signed_transaction =
%Signed{
payload: transaction,
signature_r: r,
signature_s: s,
signature_y_parity_or_v: Signed.calculate_y_parity_or_v(transaction, recovery_id)
}
encoded_signed_transaction = Transaction.encode(signed_transaction)
{:ok, Utils.hex_encode(encoded_signed_transaction)}
end
end
@impl true
def accounts(opts) do
with {:ok, private_key} <- private_key(opts),
{:ok, address} <- do_get_address(private_key) do
{:ok, [address]}
end
end
defp do_get_address(private_key) do
with {:ok, pub} <- priv_to_pub(private_key) do
{:ok, Utils.public_key_to_address(pub)}
end
end
defp validate_private_key(_private_key, nil), do: :ok
defp validate_private_key(private_key, address) do
with {:ok, private_key_address} <- do_get_address(private_key) do
private_key_address_bin = Utils.decode_address!(private_key_address)
address_bin = Utils.decode_address!(address)
if address_bin == private_key_address_bin do
:ok
else
{:error, :wrong_key}
end
end
end
defp priv_to_pub(private_key), do: secp256k1_module().create_public_key(private_key)
defp private_key(opts) do
case Keyword.get(opts, :private_key) do
<<key::binary-32>> -> {:ok, key}
<<"0x", _::binary-64>> = key -> Ethers.Utils.hex_decode(key)
<<key::binary-64>> -> Ethers.Utils.hex_decode(key)
nil -> {:error, :no_private_key}
_ -> {:error, :invalid_private_key}
end
end
end