Skip to content
Snippets Groups Projects
Unverified Commit f6d49352 authored by relativityboy's avatar relativityboy Committed by GitHub
Browse files

Merge pull request #175 from steemit/develop

Update Master with latest develop
parents 964cb6e2 7e9b82db
No related branches found
No related tags found
1 merge request!1steem/tinman:develop
Showing with 1212 additions and 22 deletions
# editor related
.idea/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
*.pyc *.pyc
*~* *~*
*#* *#*
...@@ -5,4 +14,47 @@ py3env ...@@ -5,4 +14,47 @@ py3env
dist dist
build build
*.egg* *.egg*
*.com
*.class
*.dll
*.exe
*.o
*.so
# temporary files
.DS_Store
*.swp
*.swo
*.json
*.conf
# Packages
# it's better to unpack these files and commit the raw source
# git has its own built in compression methods
*.7z
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
# Logs and databases
*.log
*.sql
*.sqlite
# OS generated files #
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
...@@ -105,5 +105,134 @@ ...@@ -105,5 +105,134 @@
"vesting": {"amount": "250000000", "precision": 3, "nai": "@@000000021"}, "vesting": {"amount": "250000000", "precision": 3, "nai": "@@000000021"},
"creator": "initminer" "creator": "initminer"
} }
],
"feeds": [
{
"publisher": "init-0",
"exchange_rate": {
"base": {"amount": "153", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "187", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-1",
"exchange_rate": {
"base": {"amount": "133", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "197", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-2",
"exchange_rate": {
"base": {"amount": "1481", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "500", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-3",
"exchange_rate": {
"base": {"amount": "681", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "1000", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-4",
"exchange_rate": {
"base": {"amount": "153", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "187", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-5",
"exchange_rate": {
"base": {"amount": "155", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "176", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-6",
"exchange_rate": {
"base": {"amount": "154", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "180", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-7",
"exchange_rate": {
"base": {"amount": "151", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "167", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-8",
"exchange_rate": {
"base": {"amount": "155", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "189", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-9",
"exchange_rate": {
"base": {"amount": "151", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "187", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-10",
"exchange_rate": {
"base": {"amount": "153", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "187", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-11",
"exchange_rate": {
"base": {"amount": "153", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "187", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-12",
"exchange_rate": {
"base": {"amount": "153", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "187", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-13",
"exchange_rate": {
"base": {"amount": "153", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "187", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-14",
"exchange_rate": {
"base": {"amount": "153", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "187", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-15",
"exchange_rate": {
"base": {"amount": "153", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "187", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-16",
"exchange_rate": {
"base": {"amount": "153", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "187", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-17",
"exchange_rate": {
"base": {"amount": "153", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "187", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-18",
"exchange_rate": {
"base": {"amount": "153", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "187", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-19",
"exchange_rate": {
"base": {"amount": "153", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "187", "precision": 3, "nai": "@@000000021"}
}
}, {
"publisher": "init-20",
"exchange_rate": {
"base": {"amount": "153", "precision": 3, "nai": "@@000000013"},
"quote": {"amount": "187", "precision": 3, "nai": "@@000000021"}
}
}
] ]
} }
{
"shared_secret": "xyz-",
"account_creator": "initminer",
"transaction_target": {
"node": "https://api.steemit.com",
"appbase": "True"
}
}
from setuptools import setup from setuptools import setup
import shutil
import os
setup(name="tinman", setup(name="tinman",
version = __import__('tinman').__version__, version = __import__('tinman').__version__,
description = "Testnet management scripts.", description = "Testnet management scripts.",
url = "https://github.com/steemit/tinman", url = "https://github.com/steemit/tinman",
author = "Steemit", author = "Steemit",
packages = ["tinman", "simple_steem_client"], packages = ["tinman", "simple_steem_client"],
install_requires = [], install_requires = ["flask", "wtforms"],
entry_points = {"console_scripts" : [ entry_points = {"console_scripts" : [
"tinman=tinman.main:sys_main", "tinman=tinman.main:sys_main",
]} ]}
) )
template_source = 'templates'
template_target = '/tmp/tinman-templates'
static_source = 'static'
static_target = '/tmp/tinman-static'
if os.path.exists(template_target):
shutil.rmtree(template_target)
shutil.copytree(template_source, template_target)
if os.path.exists(static_target):
shutil.rmtree(static_target)
shutil.copytree(static_source, static_target)
This diff is collapsed.
This diff is collapsed.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
<head>
<title>Account Create</title>
<link rel="stylesheet" media="screen" href ="static/bootstrap.min.css">
<link rel="stylesheet" href="static/bootstrap-theme.min.css">
<meta name="viewport" content = "width=device-width, initial-scale=1.0">
</head>
<body>
<div class="container">
<div class="row">
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message[1] }}</li>
{% endfor %}
</ul>
{% endif %}
{% endwith %}
</div>
<div class="row">
<h3>Account Create</h3>
<form action="" method="post">
{{ form.csrf }}
<div class="input text">
{{ form.new_account_name.label }} {{ form.new_account_name }}
</div>
<div class="input submit">
<input type="submit" value="Submit" />
</div>
</form>
</div>
</div>
</body>
</html>
import unittest import unittest
import json import json
import shutil
from tinman import keysub from tinman import keysub
class KeysubTest(unittest.TestCase): class KeysubTest(unittest.TestCase):
def test_process_esc(self): def test_process_esc(self):
# Note, resolver needs to be mocked to properly test.
self.assertRaises(AttributeError, keysub.process_esc, 'Bpublickey:owner-initminerB', 'B') self.assertRaises(AttributeError, keysub.process_esc, 'Bpublickey:owner-initminerB', 'B')
def test_process_esc_ignored(self): def test_process_esc_ignored(self):
...@@ -14,6 +14,12 @@ class KeysubTest(unittest.TestCase): ...@@ -14,6 +14,12 @@ class KeysubTest(unittest.TestCase):
self.assertEqual(result, expected_result) self.assertEqual(result, expected_result)
def test_compute_keypair_from_seed(self): def test_compute_keypair_from_seed(self):
# Note, resolver needs to be mocked to properly test. try:
self.assertRaises(FileNotFoundError, keysub.compute_keypair_from_seed, '1234', 'secret') # Try in case the binary is in the path environment.
self.assertRaises(json.decoder.JSONDecodeError, keysub.compute_keypair_from_seed, '1234', 'secret', '/usr/bin/true') result = keysub.compute_keypair_from_seed('1234', 'secret')
expected_result = "('TST6n6jNUngRVCkh3GKBEZVe6r8reBPHmi8bRkwFZ1yh83iKfGcSN', '5JFQtrsidduA79M523UZ2yKub4383BUykWthPkmTD2TAiVfDrA6')"
self.assertEqual(result, expected_result)
except FileNotFoundError:
# Note, resolver needs to be mocked to properly test.
true_exe = shutil.which("true")
self.assertRaises(json.decoder.JSONDecodeError, keysub.compute_keypair_from_seed, '1234', 'secret', true_exe)
import unittest
import json
import shutil
from tinman import prefixsub
class PrefixsubTest(unittest.TestCase):
def test_transform_prefix_str(self):
object = "STM6LLegbAgLAy28EHrffBVuANFWcFgmqRMW13wBmTExqFE9SCkg4"
result = prefixsub.transform_prefix(object)
expected_result = "TST6LLegbAgLAy28EHrffBVuANFWcFgmqRMW13wBmTExqFE9SCkg4"
self.assertEqual(result, expected_result)
def test_transform_prefix_str_wrong_length(self):
object = "STM6LLeg"
result = prefixsub.transform_prefix(object)
expected_result = "STM6LLeg"
self.assertEqual(result, expected_result)
def test_transform_prefix_list(self):
object = ["STM6LLegbAgLAy28EHrffBVuANFWcFgmqRMW13wBmTExqFE9SCkg4"]
prefixsub.transform_prefix(object)
expected_result = ["TST6LLegbAgLAy28EHrffBVuANFWcFgmqRMW13wBmTExqFE9SCkg4"]
self.assertEqual(object, expected_result)
def test_transform_prefix_dict(self):
object = {"public_key": "STM6LLegbAgLAy28EHrffBVuANFWcFgmqRMW13wBmTExqFE9SCkg4"}
prefixsub.transform_prefix(object)
expected_result = {"public_key": "TST6LLegbAgLAy28EHrffBVuANFWcFgmqRMW13wBmTExqFE9SCkg4"}
self.assertEqual(object, expected_result)
def test_transform_prefix_trivial_account_update_operation(self):
object = {
"account":"alice",
"json_metadata":"{\"profile\":{\"about\":\"Curiousness\",\"location\":\"Wonderland\",\"name\":\"Alice\"}}",
"memo_key":"STM6XzTJphLvDCifPvmQ2WtUWxgQk9AZiFEMucPiTKikJCNMZabAq"
}
prefixsub.transform_prefix(object)
expected_result = {
"account":"alice",
"json_metadata":"{\"profile\":{\"about\":\"Curiousness\",\"location\":\"Wonderland\",\"name\":\"Alice\"}}",
"memo_key":"TST6XzTJphLvDCifPvmQ2WtUWxgQk9AZiFEMucPiTKikJCNMZabAq"
}
self.assertEqual(object, expected_result)
def test_transform_prefix_complex_account_update_operation(self):
"""
Note, this test contains a public key pattern in the json_metadata field
which should be ignored for the test to pass.
"""
object = {
"account":"bob",
"active":{
"account_auths":[],
"key_auths":[["STM714aBC2zNkqfrrWSC1dVnZKPeFiXZg4RAHPRNzdr7Asue3mtnF", 1]],
"weight_threshold":1
},
"json_metadata":"{\"profile\":{\"cover_image\":\"https://imgur.org/STM714aBC2zNkqfrrWSC1dVnZKPeFiXZg4RAHPRNzdr7Asue3mtnF.jpg\"}}",
"memo_key":"STM5AjkXufK1oDPNqRVyLoj3uYoHTnwyP1pcbKCZGkRLscSToh2xV",
"owner":{
"account_auths":[],
"key_auths":[["STM8QVEwTJG6NZuhZcR8fT66yKAfYG6ep8hT8eTLgAGMaq2RXPVCW", 1]],
"weight_threshold":1
},
"posting":{
"account_auths":[
["alice", 1],
["charlie", 1]
],
"key_auths":[["STM7vBuapLzXVi9qo9vu8VUD2YieNTZ6w8iMjGUGf2j3eePYA2Y5k", 1]],
"weight_threshold":1
}
}
prefixsub.transform_prefix(object)
expected_result = {
"account":"bob",
"active":{
"account_auths":[],
"key_auths":[["TST714aBC2zNkqfrrWSC1dVnZKPeFiXZg4RAHPRNzdr7Asue3mtnF", 1]],
"weight_threshold":1
},
"json_metadata":"{\"profile\":{\"cover_image\":\"https://imgur.org/STM714aBC2zNkqfrrWSC1dVnZKPeFiXZg4RAHPRNzdr7Asue3mtnF.jpg\"}}",
"memo_key":"TST5AjkXufK1oDPNqRVyLoj3uYoHTnwyP1pcbKCZGkRLscSToh2xV",
"owner":{
"account_auths":[],
"key_auths":[["TST8QVEwTJG6NZuhZcR8fT66yKAfYG6ep8hT8eTLgAGMaq2RXPVCW", 1]],
"weight_threshold":1
},
"posting":{
"account_auths":[
["alice", 1],
["charlie", 1]
],
"key_auths":[["TST7vBuapLzXVi9qo9vu8VUD2YieNTZ6w8iMjGUGf2j3eePYA2Y5k", 1]],
"weight_threshold":1
}
}
self.assertEqual(object, expected_result)
def test_transform_prefix_transaction_ignore(self):
object = ["submit_transaction", {
"tx": {
"wif_sigs": ["zprivatekey:posting-tnmanz"],
"operations": [{
"type": "custom_json_operation",
"value": {
"json": "[\"follow\",{\"follower\":\"alice\",\"following\":\"bob\",\"what\":[\"blog\"]}]",
"required_auths": [],
"id": "follow",
"required_posting_auths": ["alice"]
}
}]
},
"esc": "z"
}]
prefixsub.transform_prefix(object)
expected_result = ["submit_transaction", {
"tx": {
"wif_sigs": ["zprivatekey:posting-tnmanz"],
"operations": [{
"type": "custom_json_operation",
"value": {
"json": "[\"follow\",{\"follower\":\"alice\",\"following\":\"bob\",\"what\":[\"blog\"]}]",
"required_auths": [],
"id": "follow",
"required_posting_auths": ["alice"]
}
}]
},
"esc": "z"
}]
self.assertEqual(object, expected_result)
import unittest
import json
import shutil
from tinman import snapshot
from simple_steem_client.client import SteemRemoteBackend, SteemInterface, SteemRPCException
class SnapshotTest(unittest.TestCase):
def test_list_all_accounts(self):
backend = SteemRemoteBackend(nodes=["http://test.com"], appbase=True)
steemd = SteemInterface(backend)
self.assertIsNotNone(snapshot.list_all_accounts(steemd))
["submit_transaction", {"esc": "j", "tx": {"operations": [{"type": "transfer_operation", "value": {"amount": {"amount": "102", "nai": "@@000000013", "precision": 3}, "from": "zoelfahmi", "memo": "https://steemit.com/animal/@zoelfahmi/the-smallest-animal-in-the-world-that-is-unique-bilingual-3e76e7cabe4e3", "to": "upvotewhale"}}], "wif_sigs": ["jprivatekey:active-tnmanj"]}}]
["submit_transaction", {"esc": "h", "tx": {"operations": [{"type": "limit_order_create_operation", "value": {"amount_to_sell": {"amount": "4002", "nai": "@@000000013", "precision": 3}, "expiration": "1969-12-31T23:59:59", "fill_or_kill": false, "min_to_receive": {"amount": "5230", "nai": "@@000000021", "precision": 3}, "orderid": 1536678065, "owner": "afril"}}], "wif_sigs": ["hprivatekey:active-tnmanh"]}}]
["submit_transaction", {"esc": "h", "tx": {"operations": [{"type": "limit_order_cancel_operation", "value": {"orderid": 2923913328, "owner": "stmdev"}}], "wif_sigs": ["hprivatekey:active-tnmanh"]}}]
["submit_transaction", {"esc": "h", "tx": {"operations": [{"type": "limit_order_create_operation", "value": {"amount_to_sell": {"amount": "647", "nai": "@@000000021", "precision": 3}, "expiration": "2018-09-18T15:01:26", "fill_or_kill": false, "min_to_receive": {"amount": "500", "nai": "@@000000013", "precision": 3}, "orderid": 1926478291, "owner": "stmdev"}}], "wif_sigs": ["hprivatekey:active-tnmanh"]}}]
["submit_transaction", {"esc": "d", "tx": {"operations": [{"type": "transfer_operation", "value": {"amount": {"amount": "1", "nai": "@@000000013", "precision": 3}, "from": "tipu", "memo": "@tipU will upvote this post in 14 minutes :)", "to": "upvotebank"}}], "wif_sigs": ["dprivatekey:active-tnmand"]}}]
["submit_transaction", {"esc": "q", "tx": {"operations": [{"type": "transfer_operation", "value": {"amount": {"amount": "1", "nai": "@@000000013", "precision": 3}, "from": "byresteem", "memo": "Campaign !!! Byresteem is much stronger now. !!! Hello users , My account with the most followers . I can promote your post.28.000 Followers + Byresteem Upvote 7600Sp + Upvote min +250 Different accounts + New followers + Loyality bonus FREE and more. Send 2 SBD or 2 STEEM To ByResteem URL as Memo Service ACTIVE", "to": "jedigeiss"}}], "wif_sigs": ["qprivatekey:active-tnmanq"]}}]
["submit_transaction", {"esc": "j", "tx": {"operations": [{"type": "transfer_operation", "value": {"amount": {"amount": "5000", "nai": "@@000000013", "precision": 3}, "from": "mikehamm", "memo": "https://steemit.com/politics/@mikehamm/fast-and-furious", "to": "smartmarket"}}], "wif_sigs": ["jprivatekey:active-tnmanj"]}}]
["submit_transaction", {"esc": "d", "tx": {"operations": [{"type": "transfer_to_vesting_operation", "value": {"amount": {"amount": "863", "nai": "@@000000021", "precision": 3}, "from": "arcange", "to": ""}}], "wif_sigs": ["dprivatekey:active-tnmand"]}}]
["submit_transaction", {"esc": "h", "tx": {"operations": [{"type": "transfer_operation", "value": {"amount": {"amount": "269494", "nai": "@@000000021", "precision": 3}, "from": "sm-usd", "memo": "P-GKTYR0QJXS", "to": "steemmonsters"}}], "wif_sigs": ["hprivatekey:active-tnmanh"]}}]
["submit_transaction", {"esc": "d", "tx": {"operations": [{"type": "transfer_operation", "value": {"amount": {"amount": "1", "nai": "@@000000021", "precision": 3}, "from": "sm-voter", "memo": "Successful Steemmonsters registration.", "to": "wombykus"}}], "wif_sigs": ["dprivatekey:active-tnmand"]}}]
...@@ -2890,6 +2890,483 @@ ...@@ -2890,6 +2890,483 @@
"withdraw_routes":0, "withdraw_routes":0,
"withdrawn":0, "withdrawn":0,
"witnesses_voted_for":0 "witnesses_voted_for":0
},
{
"active":{
"account_auths":[
[
"ekitcho",
1
],
[
"fabien",
1
],
[
"steem",
1
]
],
"key_auths":[
[
"STM4tcBtAKuaHwxZQVQm3crNu3fnntQgqGuCY8FB5ToPaqfudBRbL",
1
],
[
"STM4u9AsZJatTUoKgsTozMgpAggEC1v1viZbfNLMQEGvC9w7wYgJp",
1
],
[
"STM4zL2ghUfdoUXhZo3QJgfC3jECjocSdZjhBVFjmp9Nk28CLhtqV",
1
],
[
"STM55GQUShKKqnMrXyrYo3nvwBe7YUGtxELreHnAWbMYqSNiAopRh",
1
],
[
"STM563dEnF4JNwt2uoU4kxh8kBH1qRhQ7QU7oRBifi1KmhgwdVneD",
1
],
[
"STM56QCnp8za1QeUwNBxnhHBsG3Mv2Y1wneh1KbRU9GBr65jZJowk",
1
],
[
"STM58dUdo79DowQMFHJA48wwseypEXdNdcL38racTjdvh9Ak76Yqn",
1
],
[
"STM5C1BA2fB3X6j3hUdpoRQhKreBNeVeaooPxYt7XAsMt1DkyfVMY",
1
],
[
"STM5CUcAQdpqmBshuntCek5PQxi4tXE6ARfv589TN6DQGG5DHaZgE",
1
],
[
"STM5DjUjnXM9yGuko3ATAcmW68mnSanVfTwGqvumg1qdmm5mrgepH",
1
],
[
"STM5EjvYyhZDD4QwgdXLGetoTg23WbyzS1eFxBNPsYv8pfufas4Bk",
1
],
[
"STM5HJWutvgCo2KK6XaPGS5FoLbvGueZvkLerZm3rJDU8pK4Rig6m",
1
],
[
"STM5Nk5SorkgXv5Qd4DDVu5g8YR8UsGEwVR6tXaNmZ5TTERADJyx2",
1
],
[
"STM5cQbv4HhMQ76CGiE9GE87RE2Zkyojju3GqQHTX4MePxU7JhH4S",
1
],
[
"STM5h7kQegkxY9Y6CYxqYxNw5VGANaTMMy9D8LKeydJ76QEzBhuhr",
1
],
[
"STM5i4xRh8vvan8HaiawsHggH54qhy9nokARtdH4SAXa1VzTZ2D36",
1
],
[
"STM5reWks7gqnD8HSAacZWGJURRnKR3az5WEUsW7RtRsSL7ea5rQx",
1
],
[
"STM5wXYGhMogrBt6M5w7jmLiyUWhxDPrSwyZWnMaysHsG8od9paP7",
1
],
[
"STM5zHg31CwmjZUkxh6p5LiB1ZzStt8JvjwSkjkKUbJS2t33BYKLs",
1
],
[
"STM5zwtu9A53pHjmq7cVHnW2Apzw4FPoY4FNdLPko7dMopuKYtMFg",
1
],
[
"STM62gdsooY8PMfVCbfmEACMR8765zh4tzxzrmMwiB5MmL6BBpiPs",
1
],
[
"STM66QBmAmDbFPfZgADubhQr7wux5vjPKxqUvHfDotzj3UPGPXDfM",
1
],
[
"STM68kA4cf2FWav8NPvZ3VCECH1hi8g2QSCZsh2GzzqDsYtAnMYPK",
1
],
[
"STM6C2Lsy6wYNaGVESD3QZsgDnGCGQxVFeo9gttbhCqbbb2Evkp2Y",
1
],
[
"STM6DDLsiLY4Hf8vEkm9Z9RRykUhkZMwZuweG7MYhJSTv6XAohUEK",
1
],
[
"STM6DhX9Xmtj4vwJfdPuPSCJvdjzQPWqtD63owLZszBdhw58hNcmf",
1
],
[
"STM6FJSPJHpyfG93z6MiK9DA2uVUn97y19h44evf9sWNwMnV8b4Tu",
1
],
[
"STM6H9nfMsCdbthBLDFQAKHkASUr4M6i5qzDm3S2RicajHwuCUmUz",
1
],
[
"STM6V2cPXD8PrZCtCGpFX5fBwSuD6yF3xeDbzmohSs62VsdvxAy4x",
1
],
[
"STM6XGhvfH1RSvVPLjk4fom6h7ffvh78XDtzKzQs8mshHnykWkHMz",
1
],
[
"STM6a5Nzsc7pZJjp3FRbGe1dK5TW8ttMkKYsndTHZnfKw7u1m5oqG",
1
],
[
"STM6aHqhdQNJVHpZ9TKGgks351FE5MSzzqyNfxYnetHNG3h2J8bsY",
1
],
[
"STM6dd43qaCWXGqbBbKWLK4bDfrpt4YoTwoUUsA7f1fh628dXGoiP",
1
],
[
"STM6i3W78RoZRU7snEJHG3ViqBkqiY32DSCB9mQQVDGZmRp2BdNvC",
1
],
[
"STM6i7k75ADDnwW3N5FGNPpXC4ipDg9ThwcV5f7medMKQaU4pudyr",
1
],
[
"STM6rbjX6cTw48wZpUyWM5KmzJJ4EMPCgDeU7SAixYrAwwpzL3bxS",
1
],
[
"STM6sdKrA3R1fV4QUChhTFXPB82oBW1ufJoxjxsAbYPpKs7Q3pxr4",
1
],
[
"STM6uLwZAXS6urHx1TmcCxThrG6oFoWgLUwsLZhPxQa4GnfpZ8Gka",
1
],
[
"STM6uRchNpg8oRyUyoFsdTxvuU7WpaNYum4cT6duHAjUGdfFdEKhZ",
1
],
[
"STM6xACU35hf6gmAGfhpKjqGcRv3e8HYXo3riG5CbxF8z59Awidvy",
1
],
[
"STM71V7tfo3CuUm5MNfeYjX1szQS45znWPBh1JExXmrHgU5DSGXxW",
1
],
[
"STM74P2mgetcPgHB4w3jYaEuHaE6htuLSKPq6F4ZKxa1UTvcqnwiX",
1
],
[
"STM75N5f5bMzHzLiCV6wKvSnkoajWZXQJgA7qycznMp14VQiVPHuH",
1
],
[
"STM7Cfu3HZZEucHu9hcA2tBNA4bBUUWWjKdVVqGDKwZEpDM1wpEgS",
1
],
[
"STM7F2FZAv6ZfWsLoDg6oVarP9oKxmNEmJ67j6f74XGmeyirene5x",
1
],
[
"STM7N5pVrWzPtmjZoUqakRD4iqPhf5d6hbnAx74JLsJNH2CmjnSYL",
1
],
[
"STM7QPQQ6D8jNN5U2TEzpVH3Q2848ecTXzE6w93rRgfdHTbdbQqqH",
1
],
[
"STM7SU2ZSAJGuRqJjVALMXfiuJgXMBkKTeK1zwtHajqM6yEd7jHuZ",
1
],
[
"STM7SXSn7rkRErdiR8BnhTUEXnfiqr2nevwjhHmZ7iVMpn8DuxA68",
1
],
[
"STM7TrSwQM7FGeiytbYpVJLE96qCPjXA8ZpYVHWH4kiaVkwCnKzKp",
1
],
[
"STM7UTrrLFquSwaLMkJkyvt44i6nGnQGyQGW8K2vjaT1TCw7t5Wjm",
1
],
[
"STM7XHyvqP7NwWZ8Ft3zGfr2oTQ35zanmQSzG8spyydLwG3xX8F3c",
1
],
[
"STM7XwYmNSkN3xFe5TeENWjzbz4hy8Lz9XzwJP3NvrdFKwHG3hF1b",
1
],
[
"STM7edo93354X8UVTqNXEZRPR48YKgHVE4Fgf5JQbXJxYtHvjhr1x",
1
],
[
"STM7hYhvDdWbFRB3L6jgj13jFEvtwdsbdCJnP5pxh6og2YqLH7Gu6",
1
],
[
"STM7t6XnMzLSH8phY9AY6vsxLUbawmMwAMLFZqP2LcoJFMG7Wue57",
1
],
[
"STM7wCGWxdXMDq47Wsad9mkaxYgBPfqXN43B5K7AhMmp8JHrmTaGo",
1
],
[
"STM7xUGhNipU9FKybaZTuFpPv9vtS873x5aDan1Pt8svUJYZiMS7a",
1
],
[
"STM82JZUq2zMUfhhvQvYCorLgUdYuQwt6iXvdew5ThXMAUSyjNEoZ",
1
],
[
"STM84ey7nf3VM7j87bxuqQJQYbPgE9b1VDAcHYgtkTKRfY47NZVQm",
1
],
[
"STM8AnBmk5uAS5pWbok325FBZM7u8AbXpnQ8be3s1S8rsmYAnWVfp",
1
],
[
"STM8H65ShwA2iG1986FPMBkrYNEexQGJsirZstB48QgiJtMFvByeT",
1
],
[
"STM8PoAYdLuwacRiMCUMASvcyY74zrU5tVm7MuWdyVoRkP5TAQeXh",
1
],
[
"STM8UAUfKoMGnhoK7yD6teXHoUafHwgPmcLX8kT3wFqSSVYvrMPMx",
1
],
[
"STM8a5Qj5XSppRb6qjUthq5WuSBsRY2vGJ9occp9CZZ7rfvUAXFMD",
1
],
[
"STM8b4ycgYEFbAnPxcHAkqGij27qEPKihmATB6HdMA5Z157rBFDVS",
1
],
[
"STM8fHPtUMWdk26LngMRap1nAVZc8STQ1bgaRftKCQUiN7RaYBSX9",
1
],
[
"STM8fReN7Bnb6ecRGRVpFVLKEufyUbypZ8MDHcyXSrqhhwyAEM7ji",
1
],
[
"STM8gPJtitsyiRRmrkS1MSiG55d3WntfoAugGEXyNCnFbCPvXyKxp",
1
],
[
"STM8jThuCTSRMtuuTdN8QTnvXTyZjzVFbeTMtK3zAMDjtQ4EmVRxz",
1
],
[
"STM8jjJFbDszEDGrARLjDDrZuuL2iG4k1Xn3mxHrJS34SpCoCnaA9",
1
]
],
"weight_threshold":1
},
"balance":{
"amount":"19799",
"nai":"@@000000021",
"precision":3
},
"can_vote":true,
"comment_count":0,
"created":"2017-05-27T15:32:21",
"curation_rewards":1259,
"delegated_vesting_shares":{
"amount":"2030000000",
"nai":"@@000000037",
"precision":6
},
"id":171090,
"is_smt":false,
"json_metadata":"{\"profile\":{\"name\":\"HelloSteem!\",\"signature\":\"Hello World!\",\"dtalk_public_key\":\"8Yk2lMncEilOJ3JEjTndDOXz6PLkSgxUIOC0EIUXqoU.UyyD67WGkF04-KCikJMxLvmk4oAu--M378CdAr27DAI\"}}",
"last_account_recovery":"2017-06-02T17:25:03",
"last_account_update":"2018-07-23T21:13:45",
"last_owner_update":"2017-06-02T17:25:03",
"last_post":"2018-07-30T12:04:21",
"last_root_post":"2018-07-30T12:04:21",
"last_vote_time":"2018-07-30T12:04:21",
"lifetime_vote_count":0,
"memo_key":"STM5bGSQZNZqUuxxuRpJs2P662ouCLMdnwtP36MvUX453Bn5ePvr5",
"mined":false,
"name":"hellosteem",
"next_vesting_withdrawal":"2018-08-20T17:24:48",
"owner":{
"account_auths":[
],
"key_auths":[
[
"STM5z3YriE9Jf3vX3tSVcUuU9Ug57QtEY7pgbsKTu6AdnzbfrsEK9",
1
]
],
"weight_threshold":1
},
"post_count":990,
"posting":{
"account_auths":[
[
"bsteem",
1
],
[
"busy-mobile",
1
],
[
"busy.app",
1
],
[
"dtube.app",
1
],
[
"esteem-app",
1
],
[
"fabien",
1
],
[
"partiko-steemcon",
1
],
[
"sisilafamille",
1
],
[
"steemguest",
1
],
[
"steemprojectsapp",
1
],
[
"utopian.app",
1
],
[
"wisevote.app",
1
]
],
"key_auths":[
[
"STM6PiQjjEoF6N2hDfzSMLUw4qLebjQuyk9nnLBppNaZvX5qUkh8A",
1
]
],
"weight_threshold":1
},
"posting_rewards":12060,
"proxied_vsf_votes":[
0,
0,
0,
0
],
"proxy":"",
"received_vesting_shares":{
"amount":"0",
"nai":"@@000000037",
"precision":6
},
"recovery_account":"fabien",
"reset_account":"null",
"reward_sbd_balance":{
"amount":"0",
"nai":"@@000000013",
"precision":3
},
"reward_steem_balance":{
"amount":"0",
"nai":"@@000000021",
"precision":3
},
"reward_vesting_balance":{
"amount":"0",
"nai":"@@000000037",
"precision":6
},
"reward_vesting_steem":{
"amount":"0",
"nai":"@@000000021",
"precision":3
},
"savings_balance":{
"amount":"0",
"nai":"@@000000021",
"precision":3
},
"savings_sbd_balance":{
"amount":"0",
"nai":"@@000000013",
"precision":3
},
"savings_sbd_last_interest_payment":"2017-12-08T04:54:03",
"savings_sbd_seconds":"0",
"savings_sbd_seconds_last_update":"2017-12-08T04:54:03",
"savings_withdraw_requests":0,
"sbd_balance":{
"amount":"2657",
"nai":"@@000000013",
"precision":3
},
"sbd_last_interest_payment":"2018-07-28T13:47:33",
"sbd_seconds":"0",
"sbd_seconds_last_update":"2018-07-28T13:47:33",
"to_withdraw":2435811079,
"vesting_shares":{
"amount":"4885559226",
"nai":"@@000000037",
"precision":6
},
"vesting_withdraw_rate":{
"amount":"187370083",
"nai":"@@000000037",
"precision":6
},
"voting_power":9800,
"withdraw_routes":0,
"withdrawn":1124220498,
"witnesses_voted_for":0
} }
], ],
"witnesses":[ "witnesses":[
......
...@@ -6,7 +6,14 @@ from tinman import txgen ...@@ -6,7 +6,14 @@ from tinman import txgen
FULL_CONF = { FULL_CONF = {
"transactions_per_block" : 40, "transactions_per_block" : 40,
"steem_block_interval" : 3,
"num_blocks_to_clear_witness_round" : 21,
"transaction_witness_setup_pad" : 100,
"steem_max_authority_membership" : 10,
"steem_address_prefix" : "TST",
"steem_init_miner_name" : 'initminer',
"snapshot_file" : "/tmp/test-snapshot.json", "snapshot_file" : "/tmp/test-snapshot.json",
"backfill_file" : "/tmp/test-backfill.actions",
"min_vesting_per_account" : {"amount" : "1", "precision" : 3, "nai" : "@@000000021"}, "min_vesting_per_account" : {"amount" : "1", "precision" : 3, "nai" : "@@000000021"},
"total_port_balance" : {"amount" : "200000000000", "precision" : 3, "nai" : "@@000000021"}, "total_port_balance" : {"amount" : "200000000000", "precision" : 3, "nai" : "@@000000021"},
"accounts" : { "accounts" : {
...@@ -134,12 +141,12 @@ class TxgenTest(unittest.TestCase): ...@@ -134,12 +141,12 @@ class TxgenTest(unittest.TestCase):
"upbitsteemhot", "blocktrades", "steemit2", "ned", "holiday", "upbitsteemhot", "blocktrades", "steemit2", "ned", "holiday",
"imadev", "muchfun", "poloniex", "gopax-deposit", "dan", "imadev", "muchfun", "poloniex", "gopax-deposit", "dan",
"bithumb.sunshine", "ben", "dantheman", "openledger-dex", "bittrex", "bithumb.sunshine", "ben", "dantheman", "openledger-dex", "bittrex",
"huobi-withdrawal", "korbit3" "huobi-withdrawal", "korbit3", "hellosteem"
} }
self.assertEqual(account_stats["account_names"], expected_account_names) self.assertEqual(account_stats["account_names"], expected_account_names)
self.assertEqual(account_stats["total_vests"], 103927115336403598) self.assertEqual(account_stats["total_vests"], 103927120221962824)
self.assertEqual(account_stats["total_steem"], 60859712641) self.assertEqual(account_stats["total_steem"], 60859732440)
def test_get_proportions(self): def test_get_proportions(self):
shutil.copyfile("test-snapshot.json", "/tmp/test-snapshot.json") shutil.copyfile("test-snapshot.json", "/tmp/test-snapshot.json")
...@@ -154,7 +161,7 @@ class TxgenTest(unittest.TestCase): ...@@ -154,7 +161,7 @@ class TxgenTest(unittest.TestCase):
self.assertEqual(proportions["min_vesting_per_account"], 1) self.assertEqual(proportions["min_vesting_per_account"], 1)
self.assertEqual(proportions["vest_conversion_factor"], 1469860) self.assertEqual(proportions["vest_conversion_factor"], 1469860)
self.assertEqual(proportions["steem_conversion_factor"], 776237988251) self.assertEqual(proportions["steem_conversion_factor"], 776237928593)
def test_create_accounts(self): def test_create_accounts(self):
shutil.copyfile("test-snapshot.json", "/tmp/test-snapshot.json") shutil.copyfile("test-snapshot.json", "/tmp/test-snapshot.json")
...@@ -203,25 +210,45 @@ class TxgenTest(unittest.TestCase): ...@@ -203,25 +210,45 @@ class TxgenTest(unittest.TestCase):
for op in account["operations"]: for op in account["operations"]:
value = op["value"] value = op["value"]
self.assertIn(["tnman", 1], value["owner"]["account_auths"]) self.assertIn(["tnman", 1], value["owner"]["account_auths"])
self.assertLessEqual(len(value["owner"]["account_auths"]), txgen.STEEM_MAX_AUTHORITY_MEMBERSHIP)
self.assertLessEqual(len(value["active"]["account_auths"]), txgen.STEEM_MAX_AUTHORITY_MEMBERSHIP)
self.assertLessEqual(len(value["posting"]["account_auths"]), txgen.STEEM_MAX_AUTHORITY_MEMBERSHIP)
self.assertLessEqual(len(value["owner"]["key_auths"]), txgen.STEEM_MAX_AUTHORITY_MEMBERSHIP)
self.assertLessEqual(len(value["active"]["key_auths"]), txgen.STEEM_MAX_AUTHORITY_MEMBERSHIP)
self.assertLessEqual(len(value["posting"]["key_auths"]), txgen.STEEM_MAX_AUTHORITY_MEMBERSHIP)
def test_build_actions(self): def test_build_actions(self):
shutil.copyfile("test-snapshot.json", "/tmp/test-snapshot.json") shutil.copyfile("test-snapshot.json", "/tmp/test-snapshot.json")
shutil.copyfile("test-backfill.actions", "/tmp/test-backfill.actions")
for action in txgen.build_actions(FULL_CONF): for action in txgen.build_actions(FULL_CONF):
cmd, args = action cmd, args = action
if cmd == "metadata": if cmd == "metadata":
self.assertEqual(args["txgen:semver"], "0.2") if not args.get("post_backfill"):
self.assertEqual(args["txgen:transactions_per_block"], 40) self.assertEqual(args["txgen:semver"], "0.2")
self.assertIsNotNone(args["epoch:created"]) self.assertEqual(args["txgen:transactions_per_block"], 40)
self.assertEqual(args["actions:count"], 60) self.assertIsNotNone(args["epoch:created"])
self.assertGreater(args["recommend:miss_blocks"], 28968013) self.assertEqual(args["actions:count"], 73)
self.assertEqual(args["snapshot:semver"], "0.2") self.assertGreater(args["recommend:miss_blocks"], 28631339)
self.assertEqual(args["snapshot:origin_api"], "http://calculon.local") self.assertEqual(args["snapshot:semver"], "0.2")
self.assertEqual(args["snapshot:origin_api"], "http://calculon.local")
elif cmd == "wait_blocks": elif cmd == "wait_blocks":
self.assertGreater(args["count"], 0) self.assertGreater(args["count"], 0)
elif cmd == "submit_transaction": elif cmd == "submit_transaction":
self.assertGreater(len(args["tx"]["operations"]), 0) self.assertGreater(len(args["tx"]["operations"]), 0)
self.assertIsInstance(args["tx"]["wif_sigs"], list)
for wif in args["tx"]["wif_sigs"]:
if isinstance(wif, str):
esc = args.get("esc", None)
if esc and len(wif) < 51:
self.assertEqual(esc, wif[0])
self.assertEqual(esc, wif[-1])
else:
self.assertEqual(len(wif), 51)
else:
self.assertIsInstance(wif, prockey.ProceduralPrivateKey)
else: else:
self.fail("Unexpected action: %s" % cmd) self.fail("Unexpected action: %s" % cmd)
......
...@@ -69,3 +69,8 @@ class UtilTest(unittest.TestCase): ...@@ -69,3 +69,8 @@ class UtilTest(unittest.TestCase):
action = ["metadata", {}] action = ["metadata", {}]
result = util.action_to_str(action) result = util.action_to_str(action)
self.assertEqual(result, '["metadata",{"esc":"b"}]') self.assertEqual(result, '["metadata",{"esc":"b"}]')
def test_action_to_str_with_esc(self):
action = ["metadata", {"esc": "C"}]
result = util.action_to_str(action)
self.assertEqual(result, '["metadata",{"esc":"C"}]')
...@@ -11,7 +11,7 @@ def build_account_tx(account, keydb, silent=True): ...@@ -11,7 +11,7 @@ def build_account_tx(account, keydb, silent=True):
name = account["name"] name = account["name"]
return {"operations" : [{"type" : "account_create_operation", "value" : { return {"operations" : [{"type" : "account_create_operation", "value" : {
"fee" : account["vesting"], "fee" : {"amount" : "0", "precision" : 3, "nai" : "@@000000021"},
"creator" : account["creator"], "creator" : account["creator"],
"new_account_name" : name, "new_account_name" : name,
"owner" : keydb.get_authority(name, "owner"), "owner" : keydb.get_authority(name, "owner"),
...@@ -19,16 +19,31 @@ def build_account_tx(account, keydb, silent=True): ...@@ -19,16 +19,31 @@ def build_account_tx(account, keydb, silent=True):
"posting" : keydb.get_authority(name, "posting"), "posting" : keydb.get_authority(name, "posting"),
"memo_key" : keydb.get_pubkey(name, "memo"), "memo_key" : keydb.get_pubkey(name, "memo"),
"json_metadata" : "", "json_metadata" : "",
}}, {"type" : "transfer_to_vesting_operation", "value" : {
"from" : "initminer",
"to" : name,
"amount" : account["vesting"],
}}], }}],
"wif_sigs" : [keydb.get_privkey(account["creator"])]} "wif_sigs" : [keydb.get_privkey(account["creator"])]}
def build_feed_tx(feed, keydb, silent=True):
return {"operations" : [{"type" : "feed_publish_operation", "value" : {
"publisher" : feed["publisher"],
"exchange_rate" : feed["exchange_rate"]
}}],
"wif_sigs" : [keydb.get_privkey(feed["publisher"])]}
def build_actions(conf, silent=True): def build_actions(conf, silent=True):
keydb = prockey.ProceduralKeyDatabase() keydb = prockey.ProceduralKeyDatabase()
accounts = conf["accounts"] accounts = conf["accounts"]
feeds = conf["feeds"]
for account in accounts: for account in accounts:
yield ["submit_transaction", {"tx" : build_account_tx(account, keydb, silent)}] yield ["submit_transaction", {"tx" : build_account_tx(account, keydb, silent)}]
for feed in feeds:
yield ["submit_transaction", {"tx" : build_feed_tx(feed, keydb, silent)}]
return return
def main(argv): def main(argv):
......
...@@ -14,7 +14,8 @@ TRANSACTION_SOURCE_RETRYABLE_ERRORS = [ ...@@ -14,7 +14,8 @@ TRANSACTION_SOURCE_RETRYABLE_ERRORS = [
"Unable to acquire database lock", "Unable to acquire database lock",
"Internal Error", "Internal Error",
"Server error", "Server error",
"Upstream response error" "Upstream response error",
"Request Timeout"
] ]
MAX_RETRY = 30 MAX_RETRY = 30
...@@ -25,7 +26,7 @@ def str2bool(str_arg): ...@@ -25,7 +26,7 @@ def str2bool(str_arg):
""" """
return True if str_arg.lower() == 'true' else (False if str_arg.lower() == 'false' else None) return True if str_arg.lower() == 'true' else (False if str_arg.lower() == 'false' else None)
def repack_operations(conf, keydb, min_block, max_block): def repack_operations(conf, keydb, min_block, max_block, from_blocks_ago, to_blocks_ago):
""" """
Uses configuration file data to acquire operations from source node Uses configuration file data to acquire operations from source node
blocks/transactions and repack them in new transactions one to one. blocks/transactions and repack them in new transactions one to one.
...@@ -39,6 +40,12 @@ def repack_operations(conf, keydb, min_block, max_block): ...@@ -39,6 +40,12 @@ def repack_operations(conf, keydb, min_block, max_block):
if min_block == 0: if min_block == 0:
min_block = dgpo["head_block_number"] min_block = dgpo["head_block_number"]
if from_blocks_ago != -1:
min_block = dgpo["head_block_number"] - from_blocks_ago
if to_blocks_ago != -1:
max_block = dgpo["head_block_number"] - to_blocks_ago
ported_operations = conf["ported_operations"] ported_operations = conf["ported_operations"]
ported_types = set([op["type"] for op in ported_operations]) ported_types = set([op["type"] for op in ported_operations])
...@@ -91,7 +98,7 @@ def op_for_role(op, conf, keydb, ported_operations): ...@@ -91,7 +98,7 @@ def op_for_role(op, conf, keydb, ported_operations):
# Assume it's "active" as a fallback. # Assume it's "active" as a fallback.
return {"operations" : [op], "wif_sigs" : [keydb.get_privkey(tx_signer, "active")]} return {"operations" : [op], "wif_sigs" : [keydb.get_privkey(tx_signer, "active")]}
def build_actions(conf, min_block, max_block): def build_actions(conf, min_block, max_block, from_blocks_ago, to_blocks_ago):
""" """
Packs transactions rebuilt with operations acquired from source node into blocks of configured size. Packs transactions rebuilt with operations acquired from source node into blocks of configured size.
""" """
...@@ -102,7 +109,7 @@ def build_actions(conf, min_block, max_block): ...@@ -102,7 +109,7 @@ def build_actions(conf, min_block, max_block):
retry_count += 1 retry_count += 1
try: try:
for b in util.batch(repack_operations(conf, keydb, min_block, max_block), conf["transactions_per_block"]): for b in util.batch(repack_operations(conf, keydb, min_block, max_block, from_blocks_ago, to_blocks_ago), conf["transactions_per_block"]):
for tx in b: for tx in b:
yield ["submit_transaction", {"tx" : tx}] yield ["submit_transaction", {"tx" : tx}]
retry_count = 0 retry_count = 0
...@@ -130,6 +137,8 @@ def main(argv): ...@@ -130,6 +137,8 @@ def main(argv):
parser.add_argument("-c", "--conffile", default="gatling.conf", dest="conffile", metavar="FILE", help="Specify configuration file") parser.add_argument("-c", "--conffile", default="gatling.conf", dest="conffile", metavar="FILE", help="Specify configuration file")
parser.add_argument("-f", "--from_block", default=-1, dest="min_block_num", metavar="INT", help="Stream from block_num") parser.add_argument("-f", "--from_block", default=-1, dest="min_block_num", metavar="INT", help="Stream from block_num")
parser.add_argument("-t", "--to_block", default=-1, dest="max_block_num", metavar="INT", help="Stream to block_num") parser.add_argument("-t", "--to_block", default=-1, dest="max_block_num", metavar="INT", help="Stream to block_num")
parser.add_argument("-fb", "--from_blocks_ago", default=-1, dest="from_blocks_ago", metavar="INT", help="Stream from relative block_num")
parser.add_argument("-tb", "--to_blocks_ago", default=-1, dest="to_blocks_ago", metavar="INT", help="Stream to relative block_num")
parser.add_argument("-o", "--outfile", default="-", dest="outfile", metavar="FILE", help="Specify output file, - means stdout") parser.add_argument("-o", "--outfile", default="-", dest="outfile", metavar="FILE", help="Specify output file, - means stdout")
args = parser.parse_args(argv[1:]) args = parser.parse_args(argv[1:])
...@@ -143,6 +152,8 @@ def main(argv): ...@@ -143,6 +152,8 @@ def main(argv):
min_block_num = int(args.min_block_num) min_block_num = int(args.min_block_num)
max_block_num = int(args.max_block_num) max_block_num = int(args.max_block_num)
from_blocks_ago = int(args.from_blocks_ago)
to_blocks_ago = int(args.to_blocks_ago)
if min_block_num == -1: if min_block_num == -1:
min_block_num = int(conf["min_block_number"]) min_block_num = int(conf["min_block_number"])
...@@ -150,7 +161,7 @@ def main(argv): ...@@ -150,7 +161,7 @@ def main(argv):
if max_block_num == -1: if max_block_num == -1:
max_block_num = int(conf["max_block_number"]) max_block_num = int(conf["max_block_number"])
for action in build_actions(conf, min_block_num, max_block_num): for action in build_actions(conf, min_block_num, max_block_num, from_blocks_ago, to_blocks_ago):
outfile.write(util.action_to_str(action)) outfile.write(util.action_to_str(action))
outfile.write("\n") outfile.write("\n")
......
...@@ -12,6 +12,8 @@ from . import submit ...@@ -12,6 +12,8 @@ from . import submit
from . import warden from . import warden
from . import amountsub from . import amountsub
from . import durables from . import durables
from . import prefixsub
from . import server
class Help(object): class Help(object):
...@@ -33,6 +35,8 @@ commands = collections.OrderedDict(( ...@@ -33,6 +35,8 @@ commands = collections.OrderedDict((
("warden" , warden ), ("warden" , warden ),
("amountsub" , amountsub ), ("amountsub" , amountsub ),
("durables" , durables ), ("durables" , durables ),
("prefixsub", prefixsub),
("server", server),
("help" , Help ), ("help" , Help ),
)) ))
......
#!/usr/bin/env python3
from . import util
import argparse
import json
import sys
import math
MAINNET_PREFIX = "STM"
TESTNET_PREFIX = "TST"
PUB_KEY_LEN = 53
MAINNET_DISABLED_WITNESS_KEY = "STM1111111111111111111111111111111114T1Anm"
TESTNET_DISABLED_WITNESS_KEY = "TST1111111111111111111111111111111114T1Anm"
def transform_prefix(object):
if isinstance(object, list):
for i, e in enumerate(object):
if isinstance(e, str):
object[i] = transform_prefix(e)
else:
transform_prefix(e)
elif isinstance(object, dict):
for key in object.keys():
field = object[key]
if isinstance(field, str):
object[key] = transform_prefix(field)
else:
transform_prefix(field)
elif isinstance(object, str):
if len(object) == PUB_KEY_LEN and object[:3] == MAINNET_PREFIX:
return TESTNET_PREFIX + object[3:]
elif object == MAINNET_DISABLED_WITNESS_KEY:
return TESTNET_DISABLED_WITNESS_KEY
else:
return object
def main(argv):
parser = argparse.ArgumentParser(prog=argv[0], description="Substitute prefix")
parser.add_argument("-i", "--input-file", default="-", dest="input_file", metavar="FILE", help="File to read actions from")
parser.add_argument("-o", "--output-file", default="-", dest="output_file", metavar="FILE", help="File to write actions to")
args = parser.parse_args(argv[1:])
if args.output_file == "-":
output_file = sys.stdout
else:
output_file = open(args.output_file, "w")
if args.input_file == "-":
input_file = sys.stdin
else:
input_file = open(args.input_file, "r")
for line in input_file:
line = line.strip()
act, act_args = json.loads(line)
if act != "submit_transaction":
continue
if not act_args["tx"]:
continue
for op in act_args["tx"]["operations"]:
transform_prefix(op["value"])
transformed_line = json.dumps([act, act_args])
output_file.write(transformed_line)
output_file.write("\n")
output_file.flush()
if args.input_file != "-":
input_file.close()
if args.output_file != "-":
output_file.close()
if __name__ == "__main__":
main(sys.argv)
#!/usr/bin/env python3
import argparse
import sys
import os
import hashlib
import json
import subprocess
import struct
import time
import datetime
from flask import Flask, render_template, flash, request
from wtforms import Form, TextField, TextAreaField, validators, StringField, SubmitField
from binascii import hexlify, unhexlify
from simple_steem_client.client import SteemRemoteBackend, SteemInterface, SteemRPCException
from . import submit
class ReusableForm(Form):
new_account_name = TextField('New Account Name:', validators=[validators.required()])
def main(argv):
parser = argparse.ArgumentParser(prog=argv[0], description="Web Server")
parser.add_argument("-c", "--conffile", default="server.conf", dest="conffile", metavar="FILE", help="Specify configuration file")
parser.add_argument("--signer", default="sign_transaction", dest="sign_transaction_exe", metavar="FILE", help="Specify path to sign_transaction tool")
parser.add_argument("--get-dev-key", default="get_dev_key", dest="get_dev_key_exe", metavar="FILE", help="Specify path to get_dev_key tool")
parser.add_argument("-n", "--chain-name", default="", dest="chain_name", metavar="CN", help="Specify chain name")
parser.add_argument("-cid", "--chain-id", default="", dest="chain_id", metavar="CID", help="Specify chain ID")
parser.add_argument("--timeout", default=5.0, type=float, dest="timeout", metavar="SECONDS", help="API timeout")
args = parser.parse_args(argv[1:])
with open(args.conffile, "r") as f:
conf = json.load(f)
timeout = args.timeout
node = conf["transaction_target"]["node"]
shared_secret = conf["shared_secret"]
account_creator = conf["account_creator"]
result_bytes = subprocess.check_output([args.get_dev_key_exe, shared_secret, "active-" + account_creator])
result_str = result_bytes.decode("utf-8")
result_json = json.loads(result_str.strip())
account_creator_wif = result_json[0]["private_key"]
backend = SteemRemoteBackend(nodes=[node], appbase=True, min_timeout=timeout, max_timeout=timeout)
steemd = SteemInterface(backend)
sign_transaction_exe = args.sign_transaction_exe
if args.chain_name != "":
chain_id = hashlib.sha256(str.encode(args.chain_name.strip())).digest().hex()
else:
chain_id = None
if args.chain_id != "":
chain_id = args.chain_id.strip()
signer = submit.TransactionSigner(sign_transaction_exe=sign_transaction_exe, chain_id=chain_id)
template_dir = '/tmp/tinman-templates'
static_dir = '/tmp/tinman-static'
app = Flask(__name__, template_folder=template_dir, static_folder=static_dir, static_url_path='/static')
app.debug = True
# Temporary development secret key (for web forms).
app.config['SECRET_KEY'] = '5333d026583fdd09f413d472b29ed39e'
@app.route("/account_create", methods=['GET', 'POST'])
def account_create():
form = ReusableForm(request.form)
print(form.errors)
if request.method == 'POST':
new_account_name = request.form['new_account_name']
if form.validate():
key_types = ["owner", "active", "posting", "memo"]
keys = {}
for key_type in key_types:
result_bytes = subprocess.check_output([args.get_dev_key_exe, shared_secret, key_type + "-" + new_account_name])
result_str = result_bytes.decode("utf-8")
result_json = json.loads(result_str.strip())
keys[key_type] = result_json[0]
tx = {
"operations":[
{"type":"account_create_operation","value":{
"creator":account_creator,
"new_account_name":new_account_name,
"fee":{"amount":"0","nai":"@@000000021","precision":3},
"owner":{"account_auths":[["tnman",1]],"key_auths":[[keys["owner"]["public_key"],1]],"weight_threshold":1},
"active":{"account_auths":[["tnman",1]],"key_auths":[[keys["active"]["public_key"],1]],"weight_threshold":1},
"posting":{"account_auths":[["tnman",1]],"key_auths":[[keys["posting"]["public_key"],1]],"weight_threshold":1},
"memo_key":keys["memo"]["public_key"],
"json_metadata":""
}}, {"type":"transfer_to_vesting_operation","value":{
"amount":{"amount":"1000000","nai":"@@000000021","precision":3},
"from":account_creator,
"to":new_account_name
}}
],
"signatures":[]
}
cached_dgpo = submit.CachedDgpo(steemd=steemd)
dgpo = cached_dgpo.get()
tx["ref_block_num"] = dgpo["head_block_number"] & 0xFFFF
tx["ref_block_prefix"] = struct.unpack_from("<I", unhexlify(dgpo["head_block_id"]), 4)[0]
head_block_time = datetime.datetime.strptime(dgpo["time"], "%Y-%m-%dT%H:%M:%S")
expiration = head_block_time+datetime.timedelta(minutes=1)
expiration_str = expiration.strftime("%Y-%m-%dT%H:%M:%S")
tx["expiration"] = expiration_str
result = signer.sign_transaction(tx, account_creator_wif)
if "error" in result:
print("could not sign transaction", tx, "due to error:", result["error"])
else:
tx["signatures"].append(result["result"]["sig"])
print("bcast:", json.dumps(tx, separators=(",", ":")))
try:
steemd.network_broadcast_api.broadcast_transaction(trx=tx)
flash("Account Created: " + new_account_name)
for key in keys:
flash(key + ": " + keys[key]["private_key"])
except SteemRPCException as e:
cause = e.args[0].get("error")
if cause:
message = cause.get("message")
data = cause.get("data")
else:
message = str(e)
print(str(e))
flash("Unable to create account: " + message)
else:
flash('All the form fields are required.')
return render_template('account_create.html', form=form)
app.run()
if __name__ == "__main__":
main(sys.argv)
...@@ -13,6 +13,7 @@ from simple_steem_client.client import SteemRemoteBackend, SteemInterface, Steem ...@@ -13,6 +13,7 @@ from simple_steem_client.client import SteemRemoteBackend, SteemInterface, Steem
from . import __version__ from . import __version__
DATABASE_API_SINGLE_QUERY_LIMIT = 1000 DATABASE_API_SINGLE_QUERY_LIMIT = 1000
MAX_RETRY = 30
# Whitelist of exceptions from transaction source (Mainnet). # Whitelist of exceptions from transaction source (Mainnet).
TRANSACTION_SOURCE_RETRYABLE_ERRORS = [ TRANSACTION_SOURCE_RETRYABLE_ERRORS = [
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment