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

Add chngaddress function #38

Merged
merged 2 commits into from
Sep 19, 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
54 changes: 15 additions & 39 deletions eosio.unregd/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
![Alt Text](https://i.imgur.com/6F5aHWH.png)

# Build

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

# Setup

Expand All @@ -14,45 +15,20 @@ 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
```shell
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