From 31074e4cd66c87289e33da44a1da2f7cc0720de9 Mon Sep 17 00:00:00 2001
From: Anthony Martin <github@martin-studio.com>
Date: Thu, 11 Oct 2018 13:16:18 -0700
Subject: [PATCH] added prefixsub module for dealing with mainnet prefix #143

---
 test/prefixsub_test.py | 99 ++++++++++++++++++++++++++++++++++++++++++
 tinman/main.py         |  2 +
 tinman/prefixsub.py    | 79 +++++++++++++++++++++++++++++++++
 3 files changed, 180 insertions(+)
 create mode 100644 test/prefixsub_test.py
 create mode 100644 tinman/prefixsub.py

diff --git a/test/prefixsub_test.py b/test/prefixsub_test.py
new file mode 100644
index 0000000..85601d1
--- /dev/null
+++ b/test/prefixsub_test.py
@@ -0,0 +1,99 @@
+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)
diff --git a/tinman/main.py b/tinman/main.py
index 6e9611c..67ba1da 100755
--- a/tinman/main.py
+++ b/tinman/main.py
@@ -12,6 +12,7 @@ from . import submit
 from . import warden
 from . import amountsub
 from . import durables
+from . import prefixsub
 
 class Help(object):
 
@@ -33,6 +34,7 @@ commands = collections.OrderedDict((
             ("warden"  , warden  ),
             ("amountsub"  , amountsub  ),
             ("durables"  , durables  ),
+            ("prefixsub", prefixsub),
             ("help"    , Help    ),
            ))
 
diff --git a/tinman/prefixsub.py b/tinman/prefixsub.py
new file mode 100644
index 0000000..37e8c29
--- /dev/null
+++ b/tinman/prefixsub.py
@@ -0,0 +1,79 @@
+#!/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)
-- 
GitLab