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

Commit

Permalink
Add function that allows to change the ethereum address that owns a b…
Browse files Browse the repository at this point in the history
…alance.
  • Loading branch information
elmato committed Sep 17, 2018
1 parent 01b85e2 commit e53ef55
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 260 deletions.
50 changes: 11 additions & 39 deletions eosio.unregd/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
![Alt Text](https://i.imgur.com/6F5aHWH.png)

# Build

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

# Setup

Expand All @@ -14,45 +13,18 @@ The setup script will install one contract (besides the defaults ones):

You need to have nodeos running.

# Dependecies

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

# Test
# Add test data

```./test.sh```
```./add_test_data.sh```

The test step will:
# Claim

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.
```shell
python claim.py eostest11125 EOS7jUtjvK61eWM38RyHS3WFM7q41pSYMP7cpjQWWjVaaxH5J9Cb7 thisisatesta@active
```

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

* A signature for the message "$lib_block_num,$lib_block_prefix" generated with the ETH privkey
* The desired EOS account name (random in this case)
* An EOS pubkey (used to set the active/owner permission)
# Dependecies

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
```pip install bitcoin --user```
```pip install requests --user```
```sudo apt-get install python-pysha3```
20 changes: 20 additions & 0 deletions eosio.unregd/add_test_data.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/sh
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;35m'
NC='\033[0m' # No Color

PRIV_HEX=$(openssl rand -hex 32)
ADDY=$(python get_eth_address.py $PRIV_HEX)
AMOUNT=$(python -c 'import random; print "%.4f" % (random.random()*1000)')

echo "* Generated ETH privkey $BLUE$PRIV_HEX$NC"
echo "* Adding $GREEN$ADDY$NC to eosio.unregd db with $GREEN$AMOUNT EOS$NC"
cleos push action eosio.unregd add '["'$ADDY'","'$AMOUNT' EOS"]' -p eosio.unregd

if [ $? -eq 0 ]; then
echo $GREEN OK $NC
else
echo $RED ERROR $NC
fi
136 changes: 59 additions & 77 deletions eosio.unregd/claim.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,108 +2,90 @@
import sys
import json
import time
import struct
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
from getpass import getpass

def url_for(url):
return 'http://127.0.0.1:8888{0}'.format(url)
API_URL = os.environ.get("API_URL", "http://127.0.0.1:8888")

def ref_block(block_id):
block_num = block_id[0:8]
block_prefix = block_id[16:16+8]
def url_for(url):
return '{0}{1}'.format(API_URL, url)

def endian_reverse_u32(x):
x = x & 0xFFFFFFFF
return (((x >> 0x18) & 0xFF) )\
| (((x >> 0x10) & 0xFF) << 0x08)\
| (((x >> 0x08) & 0xFF) << 0x10)\
| (((x ) & 0xFF) << 0x18)

def is_canonical( sig ):
return not (sig[1] & 0x80)\
and not (sig[1] == 0 and not (sig[2] & 0x80))\
and not (sig[33] & 0x80)\
and not (sig[33] == 0 and not (sig[34] & 0x80))

def get_tapos_info(block_id):
block_id_bin = block_id.decode('hex')

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)
hash0 = struct.unpack("<Q", block_id_bin[0:8])[0]
hash1 = struct.unpack("<Q", block_id_bin[8:16])[0]

ref_block_num = endian_reverse_u32(hash0) & 0xFFFF
ref_block_prefix = hash1 & 0xFFFFFFFF

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 "claim.py EOSACCOUNT EOSPUBKEY SIGNTXPRIV [ETHPRIV]"
print " EOSACCOUNT: Desired EOS account name"
print " EOSPUBKEY: Desired EOS pubkey"
print " PUSHER: account@permission used to sign and push the claim transaction"
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)
eos_account = sys.argv[1]
eos_pub = sys.argv[2]
pusher = sys.argv[3]
priv = getpass("Enter ETH private key (Wif or Hex format)")

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']
while True:
block_id = req.get(url_for('/v1/chain/get_info')).json()['last_irreversible_block_id']
ref_block_num, ref_block_prefix = get_tapos_info(block_id)
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()

v, r, s = ecdsa_raw_sign(msghash, encode_privkey(priv,'hex').decode('hex'))
signature = '00%x%x%x' % (v,r,s)

if is_canonical(bytearray(signature.decode('hex'))):
break

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
)
time.sleep(1)

tmpf = mktemp()
with open(tmpf,"w") as f:
f.write(tx_json)
tempf = mktemp()
with open(tempf,'w') as fp:
fp.write(json.dumps({
'signature' : signature,
'account' : eos_account,
'eos_pubkey' : eos_pub
}))

with open(os.devnull, 'w') as devnull:
cmd = ["cleos","sign","-p","-k","5JNxyTXH4Uu4nxfBG97aQKoKYxTcmeyGjqds5LHHNh88xCTjSTw",tmpf]
p = Popen(cmd, stdout=PIPE, stderr=devnull)
cmd = ["cleos","-u", API_URL, "push", "action", "eosio.unregd", "regaccount", tempf, "-p", pusher]
p = Popen(cmd)
output, err = p.communicate("")

if p.returncode:
print "Error sending tx"
sys.exit(1)

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

print tmpf
print "tx sent"
sys.exit(0)
17 changes: 16 additions & 1 deletion eosio.unregd/eosio.unregd.abi
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"____comment": "This file was generated by eosio-abigen. DO NOT EDIT - 2018-08-29T05:10:08",
"____comment": "This file was generated by eosio-abigen. DO NOT EDIT - 2018-09-02T08:20:41",
"version": "eosio::abi/1.0",
"types": [{
"new_type_name": "ethereum_address",
Expand Down Expand Up @@ -64,6 +64,17 @@
"type": "asset"
}
]
},{
"name": "chngaddress",
"base": "",
"fields": [{
"name": "old_address",
"type": "ethereum_address"
},{
"name": "new_address",
"type": "ethereum_address"
}
]
}
],
"actions": [{
Expand All @@ -78,6 +89,10 @@
"name": "setmaxeos",
"type": "setmaxeos",
"ricardian_contract": ""
},{
"name": "chngaddress",
"type": "chngaddress",
"ricardian_contract": ""
}
],
"tables": [{
Expand Down
21 changes: 20 additions & 1 deletion eosio.unregd/eosio.unregd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include <eosiolib/crypto.h>
using eosio::unregd;

EOSIO_ABI(eosio::unregd, (add)(regaccount)(setmaxeos))
EOSIO_ABI(eosio::unregd, (add)(regaccount)(setmaxeos)(chngaddress))

/**
* Add a mapping between an ethereum_address and an initial EOS token balance.
Expand All @@ -21,6 +21,25 @@ void unregd::add(const ethereum_address& ethereum_address, const asset& balance)
});
}

/**
* Change the ethereum address that owns a balance
*/
void unregd::chngaddress(const ethereum_address& old_address, const ethereum_address& new_address) {
require_auth(_self);

eosio_assert(old_address.length() == 42, "Old Ethereum address should have exactly 42 characters");
eosio_assert(new_address.length() == 42, "New Ethereum address should have exactly 42 characters");

auto index = addresses.template get_index<N(ethereum_address)>();
auto itr = index.find(compute_ethereum_address_key256(old_address));

eosio_assert( itr != index.end(), "Old Ethereum address not found");

index.modify(itr, _self, [&](auto& address) {
address.ethereum_address = new_address;
});
}

/**
* Sets the maximum amount of EOS this contract is willing to pay when creating a new account
*/
Expand Down
1 change: 1 addition & 0 deletions eosio.unregd/eosio.unregd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class unregd : public contract {
void add(const ethereum_address& ethereum_address, const asset& balance);
void regaccount(const bytes& signature, const string& account, const string& eos_pubkey);
void setmaxeos(const asset& maxeos);
void chngaddress(const ethereum_address& old_address, const ethereum_address& new_address);

private:
static uint8_t hex_char_to_uint(char character) {
Expand Down
11 changes: 2 additions & 9 deletions eosio.unregd/get_eth_address.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import sys
from sha3 import keccak_256
from bitcoin import *
from bitcoin import privtopub

priv = sys.argv[1]
pub = privtopub(priv)

# sys.stderr.write("PUB----\n")
# sys.stderr.write(pub[2:]+"\n")

addy = keccak_256(pub[2:].decode('hex')).digest()[12:].encode('hex')

print '0x'+addy

# sys.stderr.write("ADDY----\n")
# sys.stderr.write(addy+"\n")
print '0x'+addy
6 changes: 3 additions & 3 deletions eosio.unregd/setup.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/bin/sh
rm -rf ~/eosio-wallet/./default.wallet
cleos wallet create 2>&1 | tail -n1 | tail -c+2 | head -c-2 > /tmp/pass
cleos wallet create --to-console 2>&1 | tail -n1 | tail -c+2 | head -c-2 > /tmp/pass

cleos wallet import --private-key 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
cleos wallet import --private-key 5KFDkQMpWu9chAEfgQNiEDwRdJePEVJiK92jx6vvUrQA8qFBXUd
Expand Down Expand Up @@ -43,8 +43,8 @@ cleos system newaccount \
--stake-net "2.5000 EOS" \
--stake-cpu "2.5000 EOS" \
eosio thisisatesta \
EOS6dS7ERirP2UKoBwVt3C29DVZ1u9Jj1CH9EAVqhzUvgmykncEcY \
EOS6dS7ERirP2UKoBwVt3C29DVZ1u9Jj1CH9EAVqhzUvgmykncEcY
EOS6qKoPuTnXK1bh3wHZ6jcuSnJT5T2Ruhkoou8fFKGwRrWqUtB8h \
EOS6qKoPuTnXK1bh3wHZ6jcuSnJT5T2Ruhkoou8fFKGwRrWqUtB8h

#Add eosio.unregd@eosio.code to eosio.unreg@active
tmp=$(mktemp)
Expand Down
Loading

0 comments on commit e53ef55

Please # to comment.