Skip to content
This repository was archived by the owner on Mar 5, 2020. It is now read-only.

Add regaccount function #33

Merged
merged 7 commits into from
Aug 30, 2018
Merged
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
58 changes: 58 additions & 0 deletions eosio.unregd/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
![Alt Text](https://i.imgur.com/6F5aHWH.png)

# Build

eosiocpp -o eosio.unregd.wast eosio.unregd.cpp

# Setup

```./setup.sh```

The setup script will install one contract (besides the defaults ones):

`eosio.unregd` (empty)

You need to have nodeos running.

# Dependecies

```pip install bitcoin --user```
```pip install requests --user```
```sudo apt-get install python-pysha3```

# Test

```./test.sh```

The test step will:

0. Generate a new ETH address with a random privkey.

1. Add the ETH address (`eosio.unregd::add`) and transfers a random amount of
EOS to the `eosio.unregd` contract.
This is to simulate a user that contributed to the ERC20 but
didn't register their EOS pubkey.

2. Call `eosio.unregd::regaccount` with:

* A signature for the message "$lib_block_num,$lib_block_prefix" generated with the ETH privkey

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this message is insufficient. Unless I've missed some other secure commitment to it, this allows an attacker to copy the signature parameter and use the same LIB/tapos information to forge a competing claim that would route funds to a different account and/or eos-pubkey.

at a minimum this message should include the eos-pubkey. I think it should include the account name as well but so long as the rightful owner has control that is less of an issue.

* The desired EOS account name (random in this case)
* An EOS pubkey (used to set the active/owner permission)

4. The function will :

* Verify that the destination account name is valid
* Verify that the account does not exists
* Extract the pubkey (compressed) from the signature using a message hash composed from the current TX block prefix/num
* Uncompress the pubkey
* Calculate ETH address based on the uncompressed publickey
* Verify that the ETH address exists in eosio.unregd contract
* Find and split the contribution into cpu/net/liquid
* Calculate the amount of EOS to purchase 8k of RAM
* Use the provided EOS key to build the owner/active authority to be used for the account
* Issue to eosio.unregd the necesary EOS to buy 8K of RAM
* Create the desired account
* Buy RAM for the account (8k)
* Delegate bandwith
* Transfer remaining if any (liquid EOS)
* Remove information for the ETH address from the eosio.unregd DB
84 changes: 84 additions & 0 deletions eosio.unregd/abieos_numeric.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// copyright defined in abieos/LICENSE.txt

#include <algorithm>
#include <array>
#include <stdexcept>
#include <stdint.h>
#include <string>
#include <string_view>
//#include "ripemd160.hpp"

namespace abieos {

const char base58_chars[] = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";

bool map_initialized = false;
std::array<int8_t, 256> base58_map{{0}};
auto get_base58_map() {
if(!map_initialized) {
for (unsigned i = 0; i < base58_map.size(); ++i)
base58_map[i] = -1;
for (unsigned i = 0; i < sizeof(base58_chars); ++i)
base58_map[base58_chars[i]] = i;
map_initialized = true;
}
return base58_map;
}

template <size_t size>
std::array<uint8_t, size> base58_to_binary(std::string_view s) {
std::array<uint8_t, size> result{{0}};
for (auto& src_digit : s) {
int carry = get_base58_map()[src_digit];
if (carry < 0)
eosio_assert(0, "invalid base-58 value");
for (auto& result_byte : result) {
int x = result_byte * 58 + carry;
result_byte = x;
carry = x >> 8;
}
if (carry)
eosio_assert(0, "base-58 value is out of range");
}
std::reverse(result.begin(), result.end());
return result;
}

enum class key_type : uint8_t {
k1 = 0,
r1 = 1,
};

struct public_key {
key_type type{};
std::array<char, 33> data{};
};


template <typename Key, int suffix_size>
Key string_to_key(std::string_view s, key_type type, const char (&suffix)[suffix_size]) {
static const auto size = std::tuple_size<decltype(Key::data)>::value;
auto whole = base58_to_binary<size + 4>(s);
Key result{type};
memcpy(result.data.data(), whole.data(), result.data.size());
return result;
}


public_key string_to_public_key(std::string_view s) {
if (s.size() >= 3 && s.substr(0, 3) == "EOS") {
auto whole = base58_to_binary<37>(s.substr(3));
public_key key{key_type::k1};
static_assert(whole.size() == key.data.size() + 4, "Error: whole.size() != key.data.size() + 4");
memcpy(key.data.data(), whole.data(), key.data.size());
return key;
} else if (s.size() >= 7 && s.substr(0, 7) == "PUB_R1_") {
return string_to_key<public_key>(s.substr(7), key_type::r1, "R1");
} else {
eosio_assert(0, "unrecognized public key format");
}
return public_key{};
}

} // namespace abieos

109 changes: 109 additions & 0 deletions eosio.unregd/claim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import os
import sys
import json
import time
import requests as req
from datetime import datetime, timedelta
from hashlib import sha256
from bitcoin import ecdsa_raw_sign, encode_privkey
from tempfile import mktemp
from subprocess import Popen, PIPE
from sha3 import keccak_256

def url_for(url):
return 'http://127.0.0.1:8888{0}'.format(url)

def ref_block(block_id):
block_num = block_id[0:8]
block_prefix = block_id[16:16+8]

ref_block_num = int(block_num,16)
ref_block_prefix = int("".join(reversed([block_prefix[i:i+2] for i in range(0, len(block_prefix), 2)])),16)

return ref_block_num, ref_block_prefix

if len(sys.argv) < 3:
print "claim.py ETHPRIV EOSACCOUNT"
print " ETHPRIV : Ethereum private key. (can be in Wif or hex format)"
print " EOSACCOUNT: Desired EOS account name"
sys.exit(1)

block_id = req.get(url_for('/v1/chain/get_info')).json()['last_irreversible_block_id']

ref_block_num, ref_block_prefix = ref_block( block_id )

priv = sys.argv[1]
eos_account = sys.argv[2]
eos_pub = sys.argv[3]

msg = '%d,%d,%s,%s' % (ref_block_num, ref_block_prefix, eos_pub, eos_account)
msg = '%s%s%d%s' % ("\x19", "Ethereum Signed Message:\n", len(msg), msg)

msghash = keccak_256(msg).digest()
# sys.stderr.write("HASH----\n")
# sys.stderr.write(msghash.encode('hex')+"\n")

v, r, s = ecdsa_raw_sign(msghash, encode_privkey(priv,'hex').decode('hex'))
signature = '00%x%x%x' % (v,r,s)
# sys.stderr.write("SIG----\n")
# sys.stderr.write(signature+"\n")

binargs = req.post(url_for('/v1/chain/abi_json_to_bin'),json.dumps({
"code" : "eosio.unregd",
"action" : "regaccount",
"args" : {
"signature" : signature,
"account" : eos_account,
"eos_pubkey" : eos_pub
}
})).json()['binargs']

tx_json = """
{
"expiration": "%s",
"ref_block_num": %d,
"ref_block_prefix": %d,
"max_net_usage_words": 0,
"max_cpu_usage_ms": 0,
"delay_sec": 0,
"context_free_actions": [],
"actions": [{
"account": "eosio.unregd",
"name": "regaccount",
"authorization": [{
"actor": "%s",
"permission": "active"
}
],
"data": %s
}
],
"transaction_extensions": [],
"signatures": [],
"context_free_data": []
}
""" % (
(datetime.utcnow() + timedelta(minutes=3)).strftime("%Y-%m-%dT%T"),
ref_block_num,
ref_block_prefix,
"thisisatesta",
binargs
)

tmpf = mktemp()
with open(tmpf,"w") as f:
f.write(tx_json)

with open(os.devnull, 'w') as devnull:
cmd = ["cleos","sign","-p","-k","5JNxyTXH4Uu4nxfBG97aQKoKYxTcmeyGjqds5LHHNh88xCTjSTw",tmpf]
p = Popen(cmd, stdout=PIPE, stderr=devnull)
output, err = p.communicate("")

if p.returncode:
sys.exit(1)

with open(tmpf,"w") as f:
f.write(output)

print tmpf
sys.exit(0)
Loading