Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions eth_client/.iex.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ bin_path = "../contracts/src/bin/Storage.bin"
abi_path = "../contracts/src/bin/Storage.abi"

# Uncomment and fill in for Rinkeby
Context.set_infura_api_key("your_infura_api_key")
Context.set_etherscan_api_key("your_eth_scan_api_key")
EthClient.set_chain("rinkeby")
# Context.set_infura_api_key("your_infura_api_key")
# Context.set_etherscan_api_key("your_eth_scan_api_key")
# EthClient.set_chain("rinkeby")
2 changes: 2 additions & 0 deletions eth_client/lib/eth_client.ex
Original file line number Diff line number Diff line change
Expand Up @@ -219,4 +219,6 @@ defmodule EthClient do
use Rustler, otp_app: :eth_client, crate: "ethclient_signer"

def sign_transaction(_raw_transaction, _private_key), do: :erlang.nif_error(:nif_not_loaded)
def send_ping(), do: :erlang.nif_error(:nif_not_loaded)
def sign_raw_bytes(_raw_bytes, _private_key), do: :erlang.nif_error(:nif_not_loaded)
end
56 changes: 56 additions & 0 deletions eth_client/lib/eth_client/packet.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
defmodule EthClient.Packet do
@moduledoc """
"""
defstruct [:version, :from, :to, :expiration]

def new(version, from, to, expiration) do
%__MODULE__{
version: version,
from: from,
to: to,
expiration: expiration
}
end

defimpl ExRLP.Encode, for: EthClient.Packet do
alias ExRLP.Encode

def encode(raw_transaction, options \\ []) do
raw_transaction
|> to_list()
|> Encode.encode(options)
end

defp to_list(raw_transaction) do
# NOTE: The order of fields here is PARAMOUNT. If you rearrange them
# the RLP encoding will be incorrect.
[:version, :from, :to, :expiration]
|> Enum.map(fn field ->
Map.get(raw_transaction, field)
end)
end
end

def encode(packet) do
packet_data = ExRLP.encode(packet)
to_sign = <<1>> <> packet_data

signature =
EthClient.sign_raw_bytes(
:erlang.binary_to_list(to_sign),
"e90d75baafee04b3d9941bd8d76abe799b391aec596515dee11a9bd55f05709c"
)
|> :erlang.list_to_binary()

hash = ExKeccak.hash_256(signature <> <<1>> <> packet_data)
packet_header = hash <> signature <> <<1>>
packet_header <> packet_data
end
end

## packet = EthClient.Packet.new(4, ["127.0.0.1", 30335, 8000], ["127.0.0.1", 30303, 0], 1754192626)
## packet = EthClient.Packet.new(4, [<<127,0,0,1>>, 30335, 8000], [<<127,0,0,1>>, 30303, 0], 1754192626)
## signature = sign(packet-type || packet-data)
## hash = keccak256(signature || packet-type || packet-data)
## packet-header = hash || signature || packet-type
## 7F000001
3 changes: 2 additions & 1 deletion eth_client/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ defmodule EthClient.MixProject do
{:rustler, "~> 0.25.0"},
{:ex_rlp, "~> 0.5.4"},
{:dialyxir, "~> 1.1", only: [:dev, :test], runtime: false},
{:excoveralls, "~> 0.14.5", only: [:test], runtime: false}
{:excoveralls, "~> 0.14.5", only: [:test], runtime: false},
{:ex_keccak, "~> 0.4.0"}
]
end
end
1 change: 1 addition & 0 deletions eth_client/mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "2.0.2", "0b9e1a4c840eafb68d820b0e2158ef5c49385d17fb36855ac6e7e087d4b1dcc5", [:mix], [], "hexpm", "e6a3f76b4c277739e36c2e21a2c640778ba4c3846189d5ab19f97f126df5f9b7"},
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
"net_address": {:hex, :net_address, "0.3.0", "0fd8bdccdcb74986b7e808bc1f99a7cf4bbc8232bffd6958e18a963500adb541", [:mix], [], "hexpm", "678886a834e031009eda8a45f3e2cbda94a20a1e5fbc174e88e3f031eeb62c5f"},
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
"rustler": {:hex, :rustler, "0.25.0", "32526b51af7e58a740f61941bf923486ce6415a91c3934cc16c281aa201a2240", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:toml, "~> 0.6", [hex: :toml, repo: "hexpm", optional: false]}], "hexpm", "6b43a11a37fe79c6234d88c4102ab5dfede7a6a764dc5c7b539956cfa02f3cf4"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
Expand Down
85 changes: 84 additions & 1 deletion eth_client/native/ethclient_signer/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
mod raw_transaction;
mod raw_ping_packet;
use ethereum_types::{H160, H256, U256};
use raw_transaction::RawTransaction;
use raw_ping_packet::{RawPingPacket,Endpoint};
use rlp::Rlp;
use std::net::{UdpSocket, Ipv4Addr};
use std::time::{SystemTime, Duration};
use secp256k1::{key::SecretKey, Message, Secp256k1};
use tiny_keccak::{Hasher, Keccak};

/// Signs an ethereum payload. This library assumes that the provided payload string
/// is the RLP encoding of the following list:
Expand Down Expand Up @@ -45,4 +51,81 @@ pub fn sign_transaction(payload_str: String, private_key: String) -> String {
return transaction_prefix + &signed_payload;
}

rustler::init!("Elixir.EthClient", [sign_transaction]);
fn expiration() -> u64 {
let time = SystemTime::now().checked_add(Duration::from_secs(20)).unwrap().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
return time;
}

fn from_endpoint() -> Endpoint {
let from_addr = Ipv4Addr::new(127, 0, 0, 1);
let from_addr_u32: u32 = from_addr.into();
Endpoint{address: from_addr_u32, udp_port: 30302, tcp_port: 0}
}

fn to_endpoint() -> Endpoint {
let to_addr = Ipv4Addr::new(127, 0, 0, 1);
let to_addr_u32: u32 = to_addr.into();
Endpoint{address: to_addr_u32, udp_port: 30303, tcp_port: 30303}
}

fn get_priv_key() -> H256{
let mut pkey_data: [u8; 32] = Default::default();
pkey_data.copy_from_slice(&hex::decode("e90d75baafee04b3d9941bd8d76abe799b391aec596515dee11a9bd55f05709c".to_string()).unwrap());
H256(pkey_data)
}

#[rustler::nif]
pub fn send_ping() -> Vec<u8> {
let pkey = get_priv_key();

let raw_ping = RawPingPacket {
version: 4,
from: from_endpoint(),
to: to_endpoint(),
expiration: expiration()
};

let encoded_packet = raw_ping.encode_packet(&pkey);


let socket = UdpSocket::bind("127.0.0.1:30302").expect("couldn't bind to address");
socket.send_to(&encoded_packet, "127.0.0.1:30303").expect("couldn't send data");
// let mut buf = [0; 1000];
// let (number_of_bytes, src_addr) = socket.recv_from(&mut buf).expect("Didn't receive data");
// println!("{}", number_of_bytes);
encoded_packet
}

#[rustler::nif]
pub fn sign_raw_bytes(raw_bytes: Vec<u8>, private_key: String) -> Vec<u8> {
let mut pkey_data: [u8; 32] = Default::default();
pkey_data.copy_from_slice(&hex::decode(private_key).unwrap());
ecdsa_sign(&raw_bytes, &pkey_data)
}

fn ecdsa_sign(payload: &[u8], private_key: &[u8]) -> Vec<u8> {
let s = Secp256k1::signing_only();
let hashed = keccak256_hash(payload);
let msg = Message::from_slice(&hashed).unwrap();
let key = SecretKey::from_slice(private_key).unwrap();
let (v, sig_bytes) = s.sign_recoverable(&msg, &key).serialize_compact();

let mut r = sig_bytes[0..32].to_vec();
let mut s = sig_bytes[32..64].to_vec();
let mut ret : Vec<u8> = vec![];
ret.append(&mut r);
ret.append(&mut s);
ret.push(v.to_i32() as u8);

ret
}

fn keccak256_hash(bytes: &[u8]) -> Vec<u8> {
let mut hasher = Keccak::v256();
hasher.update(bytes);
let mut resp: [u8; 32] = Default::default();
hasher.finalize(&mut resp);
resp.iter().cloned().collect()
}

rustler::init!("Elixir.EthClient", [sign_transaction, sign_raw_bytes, send_ping]);
113 changes: 113 additions & 0 deletions eth_client/native/ethclient_signer/src/raw_ping_packet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
extern crate ethereum_types;
extern crate num_traits;
extern crate rlp;
extern crate secp256k1;
extern crate serde;
extern crate serde_json;
extern crate tiny_keccak;

use ethereum_types::H256;
use rlp::RlpStream;
use secp256k1::{key::SecretKey, Message, Secp256k1};
use serde_derive::{Deserialize, Serialize};
use tiny_keccak::{Hasher, Keccak};

const PING: u8 = 1;

#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
pub struct RawPingPacket {
pub version: u8,
pub from: Endpoint,
pub to: Endpoint,
pub expiration: u64
}

#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize)]
pub struct Endpoint {
pub address: u32,
pub udp_port: u16,
pub tcp_port: u16,
}



// 0 | 32 | 32+65 |
// hash(sign + body) | firma(body) | body
impl RawPingPacket {
pub fn encode_packet(&self, private_key: &H256) -> Vec<u8> {
let mut encoded_body = encode_body(self);
let mut signed_body = sign_body(&mut encoded_body, private_key);
let mut packet = build_hash(&mut encoded_body.clone(),&mut signed_body.clone());
packet.append(&mut signed_body);
packet.append(&mut encoded_body);
packet
}
}


fn encode_body(raw_packet: &RawPingPacket) -> Vec<u8> {
let mut s = RlpStream::new();
s.begin_unbounded_list();
s.append(&raw_packet.version);
let mut s1 = RlpStream::new();
s1.begin_unbounded_list();
s1.append(&raw_packet.from.address);
s1.append(&raw_packet.from.udp_port);
s1.append(&raw_packet.from.tcp_port);
s1.finalize_unbounded_list();
s.append(&s1.out().to_vec());
let mut s2 = RlpStream::new();
s2.begin_unbounded_list();
s2.append(&raw_packet.to.address);
s2.append(&raw_packet.to.udp_port);
s2.append(&raw_packet.to.tcp_port);
s2.finalize_unbounded_list();
s.append(&s2.out().to_vec());
s.append(&raw_packet.expiration);
s.finalize_unbounded_list();
let mut body = s.out().to_vec();
body.insert(0, PING);
body
}

fn sign_body(encoded_body: &mut Vec<u8>, private_key: &H256) -> Vec<u8> {
let hash_body = keccak256_hash(&encoded_body);
let mut signed_body = ecdsa_sign(&hash_body, &private_key.0);
let mut vec: Vec<u8> = Vec::new();
let v = signed_body.v as u8;
vec.append(&mut signed_body.r);
vec.append(&mut signed_body.s);
vec.push(v);
vec
}

fn build_hash(encoded_body: &mut Vec<u8>, signed_body: &mut Vec<u8>) -> Vec<u8> {
signed_body.append(encoded_body);
keccak256_hash(&signed_body)
}

pub struct EcdsaSig {
pub v: u64,
pub r: Vec<u8>,
pub s: Vec<u8>,
}

fn keccak256_hash(bytes: &[u8]) -> Vec<u8> {
let mut hasher = Keccak::v256();
hasher.update(bytes);
let mut resp: [u8; 32] = Default::default();
hasher.finalize(&mut resp);
resp.iter().cloned().collect()
}

fn ecdsa_sign(hash: &[u8], private_key: &[u8]) -> EcdsaSig {
let s = Secp256k1::signing_only();
let msg = Message::from_slice(hash).unwrap();
let key = SecretKey::from_slice(private_key).unwrap();
let (v, sig_bytes) = s.sign_recoverable(&msg, &key).serialize_compact();
EcdsaSig {
v: v.to_i32() as u64,
r: sig_bytes[0..32].to_vec(),
s: sig_bytes[32..64].to_vec(),
}
}