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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# editor related

.idea/
.idea/*

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
16 changes: 8 additions & 8 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from setuptools import setup

setup(name="tinman",
version = __import__('tinman').__version__,
description = "Testnet management scripts.",
url = "https://github.com/steemit/tinman",
author = "Steemit",
packages = ["tinman", "simple_steem_client"],
install_requires = [],
entry_points = {"console_scripts" : [
version=__import__('tinman').__version__,
description="Testnet management scripts.",
url="https://github.com/steemit/tinman",
author= "Steemit",
packages=["tinman", "simple_steem_client"],
install_requires=[],
entry_points={"console_scripts" : [
"tinman=tinman.main:sys_main",
]}
)
)
15 changes: 11 additions & 4 deletions tinman/amountsub.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import sys
import math


def transform_amounts(object, ratio, floor_satoshi=1):
def intersection(a, b):
c = [value for value in a if value in b]
Expand Down Expand Up @@ -35,12 +36,17 @@ def intersection(a, b):

field["amount"] = str(new_amount)


def main(argv):
parser = argparse.ArgumentParser(prog=argv[0], description="Adjust amount fields by ratio")
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")
parser.add_argument("-r", "--ratio", default="1.0", dest="ratio", metavar="FLOAT", help="Adjust amounts in op to ratio")
parser.add_argument("-f", "--floor-satoshi", default="1", dest="floor_satoshi", metavar="INT", help="Minimum amount after ratio is applied")
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")
parser.add_argument("-r", "--ratio", default="1.0", dest="ratio", metavar="FLOAT",
help="Adjust amounts in op to ratio")
parser.add_argument("-f", "--floor-satoshi", default="1", dest="floor_satoshi", metavar="INT",
help="Minimum amount after ratio is applied")
args = parser.parse_args(argv[1:])

if args.output_file == "-":
Expand Down Expand Up @@ -81,5 +87,6 @@ def main(argv):
if args.output_file != "-":
output_file.close()


if __name__ == "__main__":
main(sys.argv)
27 changes: 17 additions & 10 deletions tinman/durables.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from . import prockey
from . import util


def build_account_tx(account, keydb, silent=True):
name = account["name"]

Expand All @@ -25,31 +26,36 @@ def build_account_tx(account, keydb, silent=True):
"amount" : account["vesting"],
}}],
"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"]
return {"operations" : [{"type" : "feed_publish_operation", "value": {
"publisher": feed["publisher"],
"exchange_rate": feed["exchange_rate"]
}}],
"wif_sigs" : [keydb.get_privkey(feed["publisher"])]}

"wif_sigs": [keydb.get_privkey(feed["publisher"])]}


def build_actions(conf, silent=True):
keydb = prockey.ProceduralKeyDatabase()
accounts = conf["accounts"]
feeds = conf["feeds"]

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)}]
yield ["submit_transaction", {"tx" : build_feed_tx(feed, keydb, silent)}]

return


def main(argv):
parser = argparse.ArgumentParser(prog=argv[0], description="Generate durable objects for Steem testnet")
parser.add_argument("-c", "--conffile", default="durables.conf", dest="conffile", metavar="FILE", help="Specify configuration file")
parser.add_argument("-o", "--outfile", default="-", dest="outfile", metavar="FILE", help="Specify output file, - means stdout")
parser.add_argument("-c", "--conffile", default="durables.conf", dest="conffile", metavar="FILE",
help="Specify configuration file")
parser.add_argument("-o", "--outfile", default="-", dest="outfile", metavar="FILE",
help="Specify output file, - means stdout")
args = parser.parse_args(argv[1:])

with open(args.conffile, "r") as f:
Expand All @@ -68,5 +74,6 @@ def main(argv):
if args.outfile != "-":
outfile.close()


if __name__ == "__main__":
main(sys.argv)
45 changes: 30 additions & 15 deletions tinman/gatling.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@

MAX_RETRY = 30


def str2bool(str_arg):
"""
Returns boolean True/False if recognized in string argument, None otherwise.
"""
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, from_blocks_ago, to_blocks_ago):
"""
Uses configuration file data to acquire operations from source node
Expand Down Expand Up @@ -62,13 +64,14 @@ def repack_operations(conf, keydb, min_block, max_block, from_blocks_ago, to_blo
dgpo = steemd.database_api.get_dynamic_global_properties()
new_head_block = dgpo["head_block_number"]
while old_head_block == new_head_block:
time.sleep(1) # Theoretically 3 seconds, but most probably we won't have to wait that long.
time.sleep(1) # Theoretically 3 seconds, but most probably we won't have to wait that long.
dgpo = steemd.database_api.get_dynamic_global_properties()
new_head_block = dgpo["head_block_number"]
for op in util.iterate_operations_from(steemd, is_appbase, old_head_block, new_head_block, ported_types):
yield op_for_role(op, conf, keydb, ported_operations)
old_head_block = new_head_block
return
return # unreachable code


def op_for_role(op, conf, keydb, ported_operations):
"""
Expand All @@ -80,22 +83,25 @@ def op_for_role(op, conf, keydb, ported_operations):
for ported_op in ported_operations:
if ported_op["type"] == op["type"]:
roles = ported_op["roles"]


# TODO try: roles except NameError: Do something.

if roles and len(roles) == 1:
# It's a trivial role that know about right in config.
return {"operations" : [op], "wif_sigs" : [keydb.get_privkey(tx_signer, roles[0])]}
return {"operations": [op], "wif_sigs": [keydb.get_privkey(tx_signer, roles[0])]}
else:
# custom_json_operation is usually posting, but sometimes there's an elevated role.
if op["type"] in ["custom_json_operation", "custom_binary_operation", "custom_operation"]:
if len(op["value"]["required_posting_auths"]) > 0:
# The role is "posting" because required_posting_auths has keys.
return {"operations" : [op], "wif_sigs" : [keydb.get_privkey(tx_signer, "posting")]}
return {"operations" : [op], "wif_sigs": [keydb.get_privkey(tx_signer, "posting")]}
else:
# Assume "active" because there's nothing in required_posting_auths.
return {"operations" : [op], "wif_sigs" : [keydb.get_privkey(tx_signer, "active")]}
return {"operations": [op], "wif_sigs" : [keydb.get_privkey(tx_signer, "active")]}
else:
# 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, from_blocks_ago, to_blocks_ago):
"""
Expand All @@ -108,9 +114,10 @@ def build_actions(conf, min_block, max_block, from_blocks_ago, to_blocks_ago):
retry_count += 1

try:
for b in util.batch(repack_operations(conf, keydb, min_block, max_block, from_blocks_ago, to_blocks_ago), 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:
yield ["submit_transaction", {"tx" : tx}]
yield ["submit_transaction", {"tx": tx}]
retry_count = 0
break
except SteemRPCException as e:
Expand All @@ -131,14 +138,21 @@ def build_actions(conf, min_block, max_block, from_blocks_ago, to_blocks_ago):
raise e
return


def main(argv):
parser = argparse.ArgumentParser(prog=argv[0], description="Port transactions for Steem testnet")
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("-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("-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("-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")
args = parser.parse_args(argv[1:])

with open(args.conffile, "r") as f:
Expand Down Expand Up @@ -168,5 +182,6 @@ def main(argv):
if args.outfile != "-":
outfile.close()


if __name__ == "__main__":
main(sys.argv)
18 changes: 13 additions & 5 deletions tinman/keysub.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import subprocess
import sys


def process_esc(s, esc="", resolver=None):
result = []
for e, is_escaped in util.tag_escape_sequences(s, esc):
Expand All @@ -16,19 +17,21 @@ def process_esc(s, esc="", resolver=None):
continue
ktype, seed = e.split(":", 1)
if ktype == "publickey":
result.append( json.dumps(resolver.get_pubkey(seed))[1:-1] )
result.append(json.dumps(resolver.get_pubkey(seed))[1:-1])
elif ktype == "privatekey":
result.append( json.dumps(resolver.get_privkey(seed))[1:-1] )
result.append(json.dumps(resolver.get_privkey(seed))[1:-1])
else:
raise RuntimeError("invalid input")
return "".join(result)


def compute_keypair_from_seed(seed, secret, get_dev_key_exe="get_dev_key"):
result_bytes = subprocess.check_output([get_dev_key_exe, secret, seed])
result_str = result_bytes.decode("utf-8")
result_json = json.loads(result_str.strip())
return (result_json[0]["public_key"], result_json[0]["private_key"])


class ProceduralKeyResolver(object):
"""
Every synthetic testnet key is generated by concatenating the name, secret and role.
Expand All @@ -54,11 +57,15 @@ def get_pubkey(self, seed):
def get_privkey(self, seed):
return self.get(seed)[1]


def main(argv):
parser = argparse.ArgumentParser(prog=argv[0], description="Resolve procedural keys")
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")
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("-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")
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")
args = parser.parse_args(argv[1:])

if args.output_file == "-":
Expand Down Expand Up @@ -93,5 +100,6 @@ def main(argv):
if args.output_file != "-":
output_file.close()


if __name__ == "__main__":
main(sys.argv)
10 changes: 7 additions & 3 deletions tinman/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ def main(argv):
("sample" , sample ),
("submit" , submit ),
("warden" , warden ),
("amountsub" , amountsub ),
("durables" , durables ),
("amountsub", amountsub),
("durables", durables),
("prefixsub", prefixsub),
("help" , Help ),
("help" , Help),
))


def main(argv):

if len(argv) == 0:
argv = list(argv) + ["tinman"]
if len(argv) == 1:
Expand All @@ -53,11 +55,13 @@ def main(argv):
return 1
return module.main(argv[1:])


def sys_main():
result = main(sys.argv)
if result is None:
result = 0
sys.exit(result)


if __name__ == "__main__":
sys_main()
9 changes: 7 additions & 2 deletions tinman/prefixsub.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
MAINNET_DISABLED_WITNESS_KEY = "STM1111111111111111111111111111111114T1Anm"
TESTNET_DISABLED_WITNESS_KEY = "TST1111111111111111111111111111111114T1Anm"


def transform_prefix(object):
if isinstance(object, list):
for i, e in enumerate(object):
Expand All @@ -37,10 +38,13 @@ def transform_prefix(object):
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")
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 == "-":
Expand Down Expand Up @@ -75,5 +79,6 @@ def main(argv):
if args.output_file != "-":
output_file.close()


if __name__ == "__main__":
main(sys.argv)
3 changes: 3 additions & 0 deletions tinman/prockey.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ def __init__(self, name):
self.name = name
return


class ProceduralPrivateKey(object):
"""
A procedural private key to be generated by get_dev_key
Expand All @@ -17,6 +18,7 @@ def __init__(self, name):
self.name = name
return


class ProceduralKeyDatabase(object):
"""
Every synthetic testnet key is generated by concatenating the name, secret and role.
Expand Down Expand Up @@ -47,6 +49,7 @@ def get_authority(self, name, role="active"):
"key_auths":[[self.get_pubkey(name, role),1]],
}


class PubkeySerializer(object):
"""
We want to defer substitution of public keys to a later
Expand Down
Loading