From f13b24798e79ec6808c9bffad99405d4d4f0da67 Mon Sep 17 00:00:00 2001
From: Louis Holbrook <accounts-gitlab@holbrook.no>
Date: Mon, 18 Oct 2021 11:25:00 +0000
Subject: [PATCH 01/18] refactor: Rename chain spec members

---
 chainlib/chain.py               | 24 ++++++++++++------------
 chainlib/data/config/config.ini |  1 -
 setup.cfg                       |  2 +-
 tests/test_chain.py             | 20 ++++++++++++++++++++
 4 files changed, 33 insertions(+), 14 deletions(-)

diff --git a/chainlib/chain.py b/chainlib/chain.py
index cb8fb52..cdd46d8 100644
--- a/chainlib/chain.py
+++ b/chainlib/chain.py
@@ -16,12 +16,12 @@ class ChainSpec:
     :param tag: Descriptive tag
     :type tag: str
     """
-    def __init__(self, engine, common_name, network_id, tag=None):
+    def __init__(self, arch, fork, network_id, common_name=None):
         self.o = {
-                'engine': engine,
-                'common_name': common_name,
+                'arch': arch,
+                'fork': fork,
                 'network_id': network_id,
-                'tag': tag,
+                'common_name': common_name,
                 }
 
     def network_id(self):
@@ -48,7 +48,7 @@ class ChainSpec:
         :rtype: str
         :returns: engine
         """
-        return self.o['engine']
+        return self.o['arch']
 
 
     def common_name(self):
@@ -78,10 +78,10 @@ class ChainSpec:
         o = chain_str.split(':')
         if len(o) < 3:
             raise ValueError('Chain string must have three sections, got {}'.format(len(o)))
-        tag = None
+        common_name = None
         if len(o) == 4:
-            tag = o[3]
-        return ChainSpec(o[0], o[1], int(o[2]), tag)
+            common_name = o[3]
+        return ChainSpec(o[0], o[1], int(o[2]), common_name)
 
 
     @staticmethod
@@ -100,7 +100,7 @@ class ChainSpec:
         :rtype: chainlib.chain.ChainSpec
         :returns: Resulting chain spec
         """
-        return ChainSpec(o['engine'], o['common_name'], o['network_id'], tag=o['tag'])
+        return ChainSpec(o['arch'], o['fork'], o['network_id'], common_name=o['common_name'])
 
   
     def asdict(self):
@@ -113,7 +113,7 @@ class ChainSpec:
 
 
     def __str__(self):
-        s = '{}:{}:{}'.format(self.o['engine'], self.o['common_name'], self.o['network_id'])
-        if self.o['tag'] != None:
-            s += ':' + self.o['tag']
+        s = '{}:{}:{}'.format(self.o['arch'], self.o['fork'], self.o['network_id'])
+        if self.o['common_name'] != None:
+            s += ':' + self.o['common_name']
         return s
diff --git a/chainlib/data/config/config.ini b/chainlib/data/config/config.ini
index 5b68c8e..3b03253 100644
--- a/chainlib/data/config/config.ini
+++ b/chainlib/data/config/config.ini
@@ -1,5 +1,4 @@
 [rpc]
-http_provider = 
 provider =
 auth = 
 credentials =
diff --git a/setup.cfg b/setup.cfg
index 255edb3..14066db 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = chainlib
-version = 0.0.9rc1
+version = 0.0.10a1
 description = Generic blockchain access library and tooling
 author = Louis Holbrook
 author_email = dev@holbrook.no
diff --git a/tests/test_chain.py b/tests/test_chain.py
index 1c2bc95..3e83654 100644
--- a/tests/test_chain.py
+++ b/tests/test_chain.py
@@ -7,6 +7,13 @@ from tests.base import TestBase
 
 class TestChain(TestBase):
 
+    def test_chain_spec_str(self):
+        s = ChainSpec('foo', 'bar', 3, 'baz')
+        self.assertEqual('foo:bar:3:baz', str(s))
+
+        s = ChainSpec('foo', 'bar', 3)
+        self.assertEqual('foo:bar:3', str(s))
+
     def test_chain_spec(self):
 
         s = ChainSpec.from_chain_str('foo:bar:3')
@@ -18,5 +25,18 @@ class TestChain(TestBase):
             s = ChainSpec.from_chain_str('foo')
 
 
+    def test_chain_spec_dict(self):
+        s = 'foo:bar:3:baz'
+        c = ChainSpec.from_chain_str('foo:bar:3:baz')
+        d = c.asdict()
+        self.assertEqual(d['arch'], 'foo')
+        self.assertEqual(d['fork'], 'bar')
+        self.assertEqual(d['network_id'], 3)
+        self.assertEqual(d['common_name'], 'baz')
+        cc = ChainSpec.from_dict(d)
+        self.assertEqual(s, str(cc))
+
+
+
 if __name__ == '__main__':
     unittest.main()

From d2505f19d2afde25888ca1952f54a53ebae22529 Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Mon, 18 Oct 2021 14:28:15 +0200
Subject: [PATCH 02/18] Remove crypto_dev_signer, add funga

---
 chainlib/cli/wallet.py | 5 +----
 requirements.txt       | 2 +-
 setup.cfg              | 2 +-
 3 files changed, 3 insertions(+), 6 deletions(-)

diff --git a/chainlib/cli/wallet.py b/chainlib/cli/wallet.py
index ee7cab1..3448e8f 100644
--- a/chainlib/cli/wallet.py
+++ b/chainlib/cli/wallet.py
@@ -1,9 +1,6 @@
 # standard imports
 import logging
 
-# external imports
-from crypto_dev_signer.keystore.dict import DictKeystore
-
 logg = logging.getLogger(__name__)
 
 
@@ -19,7 +16,7 @@ class Wallet:
     :todo: sign_transaction_to_rlp from chainlib-eth must be renamed to sign_transaction_to_wire, and included as part of signer interface
     """
     
-    def __init__(self, signer_cls, keystore=DictKeystore(), checksummer=None):
+    def __init__(self, signer_cls, keystore=None, checksummer=None):
         self.signer_constructor = signer_cls
         self.keystore = keystore
         self.signer = None
diff --git a/requirements.txt b/requirements.txt
index 1a6813d..135a972 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,3 @@
-crypto-dev-signer>=0.4.15rc2,<=0.4.15
+funga>=0.5.1a1,<0.6.0
 pysha3==1.0.2
 hexathon~=0.0.1a8
diff --git a/setup.cfg b/setup.cfg
index 14066db..9be4c3d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = chainlib
-version = 0.0.10a1
+version = 0.0.10a3
 description = Generic blockchain access library and tooling
 author = Louis Holbrook
 author_email = dev@holbrook.no

From 5518bf83a32baf94dc66f4a0bb60ba0ebfbdf009 Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Tue, 19 Oct 2021 19:39:38 +0200
Subject: [PATCH 03/18] Convert urlerror in rpc do, allow appends for pos args
 in cli module

---
 chainlib/cli/arg.py    | 20 +++++++++++++++-----
 chainlib/connection.py | 12 +++++++++---
 setup.cfg              |  2 +-
 3 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/chainlib/cli/arg.py b/chainlib/cli/arg.py
index 2036e9a..17f5eca 100644
--- a/chainlib/cli/arg.py
+++ b/chainlib/cli/arg.py
@@ -57,7 +57,7 @@ class ArgumentParser(argparse.ArgumentParser):
         self.pos_args = []
 
 
-    def add_positional(self, name, type=str, help=None, required=True):
+    def add_positional(self, name, type=str, help=None, append=False, required=True):
         """Add a positional argument.
 
         Stdin piping will only be possible in the event a single positional argument is defined.
@@ -73,7 +73,7 @@ class ArgumentParser(argparse.ArgumentParser):
         :param required: If true, argument will be set to required
         :type required: bool
         """
-        self.pos_args.append((name, type, help, required,))
+        self.pos_args.append((name, type, help, required, append,))
 
 
     def parse_args(self, argv=sys.argv[1:]):
@@ -88,13 +88,23 @@ class ArgumentParser(argparse.ArgumentParser):
         """
         if len(self.pos_args) == 1:
             arg = self.pos_args[0]
-            self.add_argument(arg[0], nargs='?', type=arg[1], default=stdin_arg(), help=arg[2])
+            if arg[4]:
+                self.add_argument(arg[0], nargs='*', type=arg[1], default=stdin_arg(), help=arg[2])
+            else:
+                self.add_argument(arg[0], nargs='?', type=arg[1], default=stdin_arg(), help=arg[2])
         else:
             for arg in self.pos_args:
                 if arg[3]:
-                    self.add_argument(arg[0], type=arg[1], help=arg[2])
+                    if arg[4]:
+                        logg.debug('argumen')
+                        self.add_argument(arg[0], nargs='+', type=arg[1], help=arg[2])
+                    else:
+                        self.add_argument(arg[0], type=arg[1], help=arg[2])
                 else:
-                    self.add_argument(arg[0], nargs='?', type=arg[1], help=arg[2])
+                    if arg[4]:
+                        self.add_argument(arg[0], nargs='*', type=arg[1], help=arg[2])
+                    else:
+                        self.add_argument(arg[0], type=arg[1], help=arg[2])
         args = super(ArgumentParser, self).parse_args(args=argv)
 
         if args.dumpconfig:
diff --git a/chainlib/connection.py b/chainlib/connection.py
index d4244fa..eea9d6e 100644
--- a/chainlib/connection.py
+++ b/chainlib/connection.py
@@ -23,7 +23,10 @@ from .jsonrpc import (
         ErrorParser,
         )
 from .http import PreemptiveBasicAuthHandler
-from .error import JSONRPCException
+from .error import (
+        JSONRPCException,
+        RPCException,
+        )
 from .auth import Auth
 
 logg = logging.getLogger(__name__)
@@ -308,8 +311,11 @@ class JSONRPCHTTPConnection(HTTPConnection):
                     )
             ho = build_opener(handler)
             install_opener(ho)
-       
-        r = urlopen(req, data=data.encode('utf-8'))
+
+        try: 
+            r = urlopen(req, data=data.encode('utf-8'))
+        except urllib.error.URLError as e:
+            raise RPCException(e)
 
         result = json.load(r)
         logg.debug('(HTTP) recv {}'.format(result))
diff --git a/setup.cfg b/setup.cfg
index 9be4c3d..02884ee 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = chainlib
-version = 0.0.10a3
+version = 0.0.10a5
 description = Generic blockchain access library and tooling
 author = Louis Holbrook
 author_email = dev@holbrook.no

From 52e748a02e4a4b6effd1c57cc18118f096dcc741 Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Thu, 21 Oct 2021 07:19:42 +0200
Subject: [PATCH 04/18] Fix urllib error reference

---
 chainlib/connection.py | 2 +-
 setup.cfg              | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/chainlib/connection.py b/chainlib/connection.py
index eea9d6e..786fda0 100644
--- a/chainlib/connection.py
+++ b/chainlib/connection.py
@@ -314,7 +314,7 @@ class JSONRPCHTTPConnection(HTTPConnection):
 
         try: 
             r = urlopen(req, data=data.encode('utf-8'))
-        except urllib.error.URLError as e:
+        except URLError as e:
             raise RPCException(e)
 
         result = json.load(r)
diff --git a/setup.cfg b/setup.cfg
index 02884ee..1640d09 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = chainlib
-version = 0.0.10a5
+version = 0.0.10a6
 description = Generic blockchain access library and tooling
 author = Louis Holbrook
 author_email = dev@holbrook.no

From 3e6d0c691279a22fcbe175dc35d07d7377ebf234 Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Tue, 26 Oct 2021 15:37:08 +0200
Subject: [PATCH 05/18] Rename index texi file

---
 chainlib/cli/config.py                   | 1 -
 doc/texinfo/{index.texi => content.texi} | 0
 setup.cfg                                | 2 +-
 3 files changed, 1 insertion(+), 2 deletions(-)
 rename doc/texinfo/{index.texi => content.texi} (100%)

diff --git a/chainlib/cli/config.py b/chainlib/cli/config.py
index 8716958..49b8271 100644
--- a/chainlib/cli/config.py
+++ b/chainlib/cli/config.py
@@ -170,7 +170,6 @@ class Config(confini.Config):
         args_override = {}
 
         if arg_flags & Flag.PROVIDER:
-            args_override['RPC_HTTP_PROVIDER'] = getattr(args, 'p')
             args_override['RPC_PROVIDER'] = getattr(args, 'p')
             args_override['RPC_DIALECT'] = getattr(args, 'rpc_dialect')
         if arg_flags & Flag.CHAIN_SPEC:
diff --git a/doc/texinfo/index.texi b/doc/texinfo/content.texi
similarity index 100%
rename from doc/texinfo/index.texi
rename to doc/texinfo/content.texi
diff --git a/setup.cfg b/setup.cfg
index 1640d09..ca145a0 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = chainlib
-version = 0.0.10a6
+version = 0.0.10a7
 description = Generic blockchain access library and tooling
 author = Louis Holbrook
 author_email = dev@holbrook.no

From 095c0810f32b7017040c39264675d89d5644959a Mon Sep 17 00:00:00 2001
From: Louis Holbrook <accounts-gitlab@holbrook.no>
Date: Tue, 26 Oct 2021 14:15:59 +0000
Subject: [PATCH 06/18] feat: chainspec, positional args, docfix

---
 chainlib/chain.py                        | 24 ++++++++++++------------
 chainlib/cli/arg.py                      | 20 +++++++++++++++-----
 chainlib/cli/config.py                   |  1 -
 chainlib/cli/wallet.py                   |  5 +----
 chainlib/connection.py                   | 12 +++++++++---
 chainlib/data/config/config.ini          |  1 -
 doc/texinfo/{index.texi => content.texi} |  0
 requirements.txt                         |  2 +-
 setup.cfg                                |  2 +-
 tests/test_chain.py                      | 20 ++++++++++++++++++++
 10 files changed, 59 insertions(+), 28 deletions(-)
 rename doc/texinfo/{index.texi => content.texi} (100%)

diff --git a/chainlib/chain.py b/chainlib/chain.py
index cb8fb52..cdd46d8 100644
--- a/chainlib/chain.py
+++ b/chainlib/chain.py
@@ -16,12 +16,12 @@ class ChainSpec:
     :param tag: Descriptive tag
     :type tag: str
     """
-    def __init__(self, engine, common_name, network_id, tag=None):
+    def __init__(self, arch, fork, network_id, common_name=None):
         self.o = {
-                'engine': engine,
-                'common_name': common_name,
+                'arch': arch,
+                'fork': fork,
                 'network_id': network_id,
-                'tag': tag,
+                'common_name': common_name,
                 }
 
     def network_id(self):
@@ -48,7 +48,7 @@ class ChainSpec:
         :rtype: str
         :returns: engine
         """
-        return self.o['engine']
+        return self.o['arch']
 
 
     def common_name(self):
@@ -78,10 +78,10 @@ class ChainSpec:
         o = chain_str.split(':')
         if len(o) < 3:
             raise ValueError('Chain string must have three sections, got {}'.format(len(o)))
-        tag = None
+        common_name = None
         if len(o) == 4:
-            tag = o[3]
-        return ChainSpec(o[0], o[1], int(o[2]), tag)
+            common_name = o[3]
+        return ChainSpec(o[0], o[1], int(o[2]), common_name)
 
 
     @staticmethod
@@ -100,7 +100,7 @@ class ChainSpec:
         :rtype: chainlib.chain.ChainSpec
         :returns: Resulting chain spec
         """
-        return ChainSpec(o['engine'], o['common_name'], o['network_id'], tag=o['tag'])
+        return ChainSpec(o['arch'], o['fork'], o['network_id'], common_name=o['common_name'])
 
   
     def asdict(self):
@@ -113,7 +113,7 @@ class ChainSpec:
 
 
     def __str__(self):
-        s = '{}:{}:{}'.format(self.o['engine'], self.o['common_name'], self.o['network_id'])
-        if self.o['tag'] != None:
-            s += ':' + self.o['tag']
+        s = '{}:{}:{}'.format(self.o['arch'], self.o['fork'], self.o['network_id'])
+        if self.o['common_name'] != None:
+            s += ':' + self.o['common_name']
         return s
diff --git a/chainlib/cli/arg.py b/chainlib/cli/arg.py
index 2036e9a..17f5eca 100644
--- a/chainlib/cli/arg.py
+++ b/chainlib/cli/arg.py
@@ -57,7 +57,7 @@ class ArgumentParser(argparse.ArgumentParser):
         self.pos_args = []
 
 
-    def add_positional(self, name, type=str, help=None, required=True):
+    def add_positional(self, name, type=str, help=None, append=False, required=True):
         """Add a positional argument.
 
         Stdin piping will only be possible in the event a single positional argument is defined.
@@ -73,7 +73,7 @@ class ArgumentParser(argparse.ArgumentParser):
         :param required: If true, argument will be set to required
         :type required: bool
         """
-        self.pos_args.append((name, type, help, required,))
+        self.pos_args.append((name, type, help, required, append,))
 
 
     def parse_args(self, argv=sys.argv[1:]):
@@ -88,13 +88,23 @@ class ArgumentParser(argparse.ArgumentParser):
         """
         if len(self.pos_args) == 1:
             arg = self.pos_args[0]
-            self.add_argument(arg[0], nargs='?', type=arg[1], default=stdin_arg(), help=arg[2])
+            if arg[4]:
+                self.add_argument(arg[0], nargs='*', type=arg[1], default=stdin_arg(), help=arg[2])
+            else:
+                self.add_argument(arg[0], nargs='?', type=arg[1], default=stdin_arg(), help=arg[2])
         else:
             for arg in self.pos_args:
                 if arg[3]:
-                    self.add_argument(arg[0], type=arg[1], help=arg[2])
+                    if arg[4]:
+                        logg.debug('argumen')
+                        self.add_argument(arg[0], nargs='+', type=arg[1], help=arg[2])
+                    else:
+                        self.add_argument(arg[0], type=arg[1], help=arg[2])
                 else:
-                    self.add_argument(arg[0], nargs='?', type=arg[1], help=arg[2])
+                    if arg[4]:
+                        self.add_argument(arg[0], nargs='*', type=arg[1], help=arg[2])
+                    else:
+                        self.add_argument(arg[0], type=arg[1], help=arg[2])
         args = super(ArgumentParser, self).parse_args(args=argv)
 
         if args.dumpconfig:
diff --git a/chainlib/cli/config.py b/chainlib/cli/config.py
index 8716958..49b8271 100644
--- a/chainlib/cli/config.py
+++ b/chainlib/cli/config.py
@@ -170,7 +170,6 @@ class Config(confini.Config):
         args_override = {}
 
         if arg_flags & Flag.PROVIDER:
-            args_override['RPC_HTTP_PROVIDER'] = getattr(args, 'p')
             args_override['RPC_PROVIDER'] = getattr(args, 'p')
             args_override['RPC_DIALECT'] = getattr(args, 'rpc_dialect')
         if arg_flags & Flag.CHAIN_SPEC:
diff --git a/chainlib/cli/wallet.py b/chainlib/cli/wallet.py
index ee7cab1..3448e8f 100644
--- a/chainlib/cli/wallet.py
+++ b/chainlib/cli/wallet.py
@@ -1,9 +1,6 @@
 # standard imports
 import logging
 
-# external imports
-from crypto_dev_signer.keystore.dict import DictKeystore
-
 logg = logging.getLogger(__name__)
 
 
@@ -19,7 +16,7 @@ class Wallet:
     :todo: sign_transaction_to_rlp from chainlib-eth must be renamed to sign_transaction_to_wire, and included as part of signer interface
     """
     
-    def __init__(self, signer_cls, keystore=DictKeystore(), checksummer=None):
+    def __init__(self, signer_cls, keystore=None, checksummer=None):
         self.signer_constructor = signer_cls
         self.keystore = keystore
         self.signer = None
diff --git a/chainlib/connection.py b/chainlib/connection.py
index d4244fa..786fda0 100644
--- a/chainlib/connection.py
+++ b/chainlib/connection.py
@@ -23,7 +23,10 @@ from .jsonrpc import (
         ErrorParser,
         )
 from .http import PreemptiveBasicAuthHandler
-from .error import JSONRPCException
+from .error import (
+        JSONRPCException,
+        RPCException,
+        )
 from .auth import Auth
 
 logg = logging.getLogger(__name__)
@@ -308,8 +311,11 @@ class JSONRPCHTTPConnection(HTTPConnection):
                     )
             ho = build_opener(handler)
             install_opener(ho)
-       
-        r = urlopen(req, data=data.encode('utf-8'))
+
+        try: 
+            r = urlopen(req, data=data.encode('utf-8'))
+        except URLError as e:
+            raise RPCException(e)
 
         result = json.load(r)
         logg.debug('(HTTP) recv {}'.format(result))
diff --git a/chainlib/data/config/config.ini b/chainlib/data/config/config.ini
index 5b68c8e..3b03253 100644
--- a/chainlib/data/config/config.ini
+++ b/chainlib/data/config/config.ini
@@ -1,5 +1,4 @@
 [rpc]
-http_provider = 
 provider =
 auth = 
 credentials =
diff --git a/doc/texinfo/index.texi b/doc/texinfo/content.texi
similarity index 100%
rename from doc/texinfo/index.texi
rename to doc/texinfo/content.texi
diff --git a/requirements.txt b/requirements.txt
index 1a6813d..135a972 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,3 @@
-crypto-dev-signer>=0.4.15rc2,<=0.4.15
+funga>=0.5.1a1,<0.6.0
 pysha3==1.0.2
 hexathon~=0.0.1a8
diff --git a/setup.cfg b/setup.cfg
index 255edb3..ca145a0 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = chainlib
-version = 0.0.9rc1
+version = 0.0.10a7
 description = Generic blockchain access library and tooling
 author = Louis Holbrook
 author_email = dev@holbrook.no
diff --git a/tests/test_chain.py b/tests/test_chain.py
index 1c2bc95..3e83654 100644
--- a/tests/test_chain.py
+++ b/tests/test_chain.py
@@ -7,6 +7,13 @@ from tests.base import TestBase
 
 class TestChain(TestBase):
 
+    def test_chain_spec_str(self):
+        s = ChainSpec('foo', 'bar', 3, 'baz')
+        self.assertEqual('foo:bar:3:baz', str(s))
+
+        s = ChainSpec('foo', 'bar', 3)
+        self.assertEqual('foo:bar:3', str(s))
+
     def test_chain_spec(self):
 
         s = ChainSpec.from_chain_str('foo:bar:3')
@@ -18,5 +25,18 @@ class TestChain(TestBase):
             s = ChainSpec.from_chain_str('foo')
 
 
+    def test_chain_spec_dict(self):
+        s = 'foo:bar:3:baz'
+        c = ChainSpec.from_chain_str('foo:bar:3:baz')
+        d = c.asdict()
+        self.assertEqual(d['arch'], 'foo')
+        self.assertEqual(d['fork'], 'bar')
+        self.assertEqual(d['network_id'], 3)
+        self.assertEqual(d['common_name'], 'baz')
+        cc = ChainSpec.from_dict(d)
+        self.assertEqual(s, str(cc))
+
+
+
 if __name__ == '__main__':
     unittest.main()

From 64dbcfcddf5f1cc1793b4360df48476b44b4f4e4 Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Tue, 26 Oct 2021 18:20:28 +0200
Subject: [PATCH 07/18] WIP explicit node links in texinfo docs

---
 doc/texinfo/cli.texi     | 3 ++-
 doc/texinfo/code.texi    | 3 ++-
 doc/texinfo/config.texi  | 3 ++-
 doc/texinfo/content.texi | 2 --
 doc/texinfo/intro.texi   | 3 ++-
 5 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/doc/texinfo/cli.texi b/doc/texinfo/cli.texi
index 3a0cb4e..cf80fbb 100644
--- a/doc/texinfo/cli.texi
+++ b/doc/texinfo/cli.texi
@@ -1,4 +1,5 @@
-@node chainlib-cli
+@node chainlib-cli, chainlib-config, chainlib-intro, Top
+@comment this, next, prev, up
 
 @section Command line interface provisions
 
diff --git a/doc/texinfo/code.texi b/doc/texinfo/code.texi
index cfc2a7e..414b8b1 100644
--- a/doc/texinfo/code.texi
+++ b/doc/texinfo/code.texi
@@ -1,4 +1,5 @@
-@node chainlib-lib
+@node chainlib-lib,,chainlib-config,Top
+@comment this, next, prev, up
 
 @section Base library contents
 
diff --git a/doc/texinfo/config.texi b/doc/texinfo/config.texi
index 031f3e4..e7ffab1 100644
--- a/doc/texinfo/config.texi
+++ b/doc/texinfo/config.texi
@@ -1,4 +1,5 @@
-@node chainlib-config
+@node chainlib-config, chainlib-lib, chainlib-cli, Top
+@comment this, next, prev, up
 
 @section Rendering configurations
 
diff --git a/doc/texinfo/content.texi b/doc/texinfo/content.texi
index 631a89f..a372751 100644
--- a/doc/texinfo/content.texi
+++ b/doc/texinfo/content.texi
@@ -1,5 +1,3 @@
-@top chainlib
-
 @chapter Chainlib
 
 @include intro.texi
diff --git a/doc/texinfo/intro.texi b/doc/texinfo/intro.texi
index 5f67033..6ef5828 100644
--- a/doc/texinfo/intro.texi
+++ b/doc/texinfo/intro.texi
@@ -1,4 +1,5 @@
-@node chainlib-intro
+@node chainlib-intro, chainlib-cli,,Top
+@comment this, next, prev, up
 
 Chainlib is an attempt at employing a universal interface to manipulate and access blockchains regardless of underlying architecture.
 

From 1373cb10e67ca16bf60b85b4de42e8302af6813a Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Tue, 26 Oct 2021 19:44:54 +0200
Subject: [PATCH 08/18] Remove unnecessary nodes

---
 doc/texinfo/cli.texi     | 3 ---
 doc/texinfo/code.texi    | 3 ---
 doc/texinfo/config.texi  | 4 +---
 doc/texinfo/content.texi | 1 +
 doc/texinfo/intro.texi   | 3 +--
 5 files changed, 3 insertions(+), 11 deletions(-)

diff --git a/doc/texinfo/cli.texi b/doc/texinfo/cli.texi
index cf80fbb..9257ddd 100644
--- a/doc/texinfo/cli.texi
+++ b/doc/texinfo/cli.texi
@@ -1,6 +1,3 @@
-@node chainlib-cli, chainlib-config, chainlib-intro, Top
-@comment this, next, prev, up
-
 @section Command line interface provisions
 
 The base CLI provisions of @code{chainlib} simplifies the generation of a some base object instances by command line arguments, environment variables and configuration schemas.
diff --git a/doc/texinfo/code.texi b/doc/texinfo/code.texi
index 414b8b1..9fb8f5e 100644
--- a/doc/texinfo/code.texi
+++ b/doc/texinfo/code.texi
@@ -1,6 +1,3 @@
-@node chainlib-lib,,chainlib-config,Top
-@comment this, next, prev, up
-
 @section Base library contents
 
 
diff --git a/doc/texinfo/config.texi b/doc/texinfo/config.texi
index e7ffab1..89cc6ee 100644
--- a/doc/texinfo/config.texi
+++ b/doc/texinfo/config.texi
@@ -1,6 +1,4 @@
-@node chainlib-config, chainlib-lib, chainlib-cli, Top
-@comment this, next, prev, up
-
+@anchor{chainlib-config}
 @section Rendering configurations
 
 Configurations in @code{chainlib} are processed, rendered and interfaced using the @code{confini} python package.
diff --git a/doc/texinfo/content.texi b/doc/texinfo/content.texi
index a372751..ea39a53 100644
--- a/doc/texinfo/content.texi
+++ b/doc/texinfo/content.texi
@@ -1,3 +1,4 @@
+@node chainlib
 @chapter Chainlib
 
 @include intro.texi
diff --git a/doc/texinfo/intro.texi b/doc/texinfo/intro.texi
index 6ef5828..5eaee35 100644
--- a/doc/texinfo/intro.texi
+++ b/doc/texinfo/intro.texi
@@ -1,5 +1,4 @@
-@node chainlib-intro, chainlib-cli,,Top
-@comment this, next, prev, up
+@section Overview
 
 Chainlib is an attempt at employing a universal interface to manipulate and access blockchains regardless of underlying architecture.
 

From 7d7209dd31e88d5eb9df55bd36545f49f4f0a7a3 Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Wed, 27 Oct 2021 20:53:07 +0200
Subject: [PATCH 09/18] Handle missing dumpconfig arg

---
 chainlib/cli/arg.py    | 2 +-
 chainlib/cli/config.py | 2 +-
 setup.cfg              | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/chainlib/cli/arg.py b/chainlib/cli/arg.py
index 17f5eca..9658b43 100644
--- a/chainlib/cli/arg.py
+++ b/chainlib/cli/arg.py
@@ -107,7 +107,7 @@ class ArgumentParser(argparse.ArgumentParser):
                         self.add_argument(arg[0], type=arg[1], help=arg[2])
         args = super(ArgumentParser, self).parse_args(args=argv)
 
-        if args.dumpconfig:
+        if getattr(args, 'dumpconfig', None) != None:
             return args
 
         if len(self.pos_args) == 1:
diff --git a/chainlib/cli/config.py b/chainlib/cli/config.py
index 49b8271..fdc788b 100644
--- a/chainlib/cli/config.py
+++ b/chainlib/cli/config.py
@@ -236,7 +236,7 @@ class Config(confini.Config):
             if existing_r == None or r != None:
                 config.add(r, v, exists_ok=True)
 
-        if getattr(args, 'dumpconfig'):
+        if getattr(args, 'dumpconfig', None) != None:
             config_keys = config.all()
             with_values = not config.get('_RAW')
             for k in config_keys:
diff --git a/setup.cfg b/setup.cfg
index ca145a0..2efaaca 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = chainlib
-version = 0.0.10a7
+version = 0.0.10a8
 description = Generic blockchain access library and tooling
 author = Louis Holbrook
 author_email = dev@holbrook.no

From 5bfdb51676a8a19c7c179edcbe4188528a6363ba Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Thu, 28 Oct 2021 12:16:23 +0200
Subject: [PATCH 10/18] Add custom fields to chain spec, harden custom field
 imports, improve dumpconfig handle

---
 chainlib/chain.py      | 100 ++++++++++++++++++++++++++++++++++++-----
 chainlib/cli/config.py |   3 +-
 setup.cfg              |   2 +-
 tests/test_chain.py    |  55 ++++++++++++++++++++---
 tests/test_cli.py      |   3 ++
 5 files changed, 142 insertions(+), 21 deletions(-)

diff --git a/chainlib/chain.py b/chainlib/chain.py
index cdd46d8..71c5e1f 100644
--- a/chainlib/chain.py
+++ b/chainlib/chain.py
@@ -1,5 +1,15 @@
 # standard imports
 import copy
+import re
+
+
+def is_valid_label(v, alpha_only=False):
+    re_m = None
+    if alpha_only:
+        re_m = r'^[a-zA-Z]+$'
+    else:
+        re_m = r'^[a-zA-Z0-9]+$'
+    return re.match(re_m, v)
 
 
 class ChainSpec:
@@ -16,13 +26,37 @@ class ChainSpec:
     :param tag: Descriptive tag
     :type tag: str
     """
-    def __init__(self, arch, fork, network_id, common_name=None):
+    def __init__(self, arch, fork, network_id, common_name=None, custom=[], safe=True):
+        if custom == None:
+            custom = []
+        elif not isinstance(custom, list):
+            raise ValueError('custom value must be list')
+
         self.o = {
-                'arch': arch,
-                'fork': fork,
-                'network_id': network_id,
-                'common_name': common_name,
-                }
+        'arch': arch,
+        'fork': fork,
+        'network_id': network_id,
+        'common_name': common_name,
+        'custom': custom,
+        }
+
+        if safe:
+            self.validate()
+
+
+    def validate(self):
+        self.o['network_id'] = int(self.o['network_id'])
+        if not is_valid_label(self.o['arch'], alpha_only=True):
+            raise ValueError('arch: ' + self.o['arch'])
+        if not is_valid_label(self.o['fork'], alpha_only=True):
+            raise ValueError('fork: ' + self.o['fork'])
+        if self.o.get('common_name') and not is_valid_label(self.o['common_name']):
+            raise ValueError('common_name: ' + self.o['common_name'])
+        if self.o.get('custom'):
+            for i, v in enumerate(self.o['custom']):
+                if not is_valid_label(v):
+                    raise ValueError('common_name {}: {}'.format(i, v))
+
 
     def network_id(self):
         """Returns the network id part of the spec.
@@ -43,6 +77,12 @@ class ChainSpec:
 
 
     def engine(self):
+        """Alias of self.arch()
+        """
+        return self.arch()
+
+
+    def arch(self):
         """Returns the chain architecture part of the spec
 
         :rtype: str
@@ -51,6 +91,15 @@ class ChainSpec:
         return self.o['arch']
 
 
+    def fork(self):
+        """Returns the fork part of the spec
+
+        :rtype: str
+        :returns: fork
+        """
+        return self.o['fork']
+
+
     def common_name(self):
         """Returns the common name part of the spec
 
@@ -60,6 +109,21 @@ class ChainSpec:
         return self.o['common_name']
 
 
+    def is_same_as(self, chain_spec_cmp, use_common_name=False, use_custom=False):
+        a = ['arch', 'fork', 'network_id']
+        if use_common_name:
+            a += ['common_name']
+        if use_custom:
+            a += ['custom']
+        try:
+            for k in a:
+                assert(chain_spec_cmp.o[k] == self.o[k])
+        except AssertionError:
+            return False
+        return True
+                
+
+
     @staticmethod
     def from_chain_str(chain_str):
         """Create a new ChainSpec object from a colon-separated string, as output by the string representation of the ChainSpec object.
@@ -79,9 +143,13 @@ class ChainSpec:
         if len(o) < 3:
             raise ValueError('Chain string must have three sections, got {}'.format(len(o)))
         common_name = None
-        if len(o) == 4:
+        if len(o) > 3:
             common_name = o[3]
-        return ChainSpec(o[0], o[1], int(o[2]), common_name)
+        custom = []
+        if len(o) > 4:
+            for i in range(4, len(o)):
+                custom.append(o[i])
+        return ChainSpec(o[0], o[1], int(o[2]), common_name=common_name, custom=custom)
 
 
     @staticmethod
@@ -100,20 +168,28 @@ class ChainSpec:
         :rtype: chainlib.chain.ChainSpec
         :returns: Resulting chain spec
         """
-        return ChainSpec(o['arch'], o['fork'], o['network_id'], common_name=o['common_name'])
+        return ChainSpec(o['arch'], o['fork'], o['network_id'], common_name=o.get('common_name'), custom=o.get('custom'))
 
   
-    def asdict(self):
+    def asdict(self, use_common_name=True, use_custom=True):
         """Create a dictionary representation of the chain spec.
 
         :rtype: dict
         :returns: Chain spec dictionary
         """
-        return copy.copy(self.o)
+        r = copy.copy(self.o)
+        if not use_common_name:
+            del r['common_name']
+            del r['custom']
+        if not use_custom:
+            del r['custom']
+        return r
 
 
     def __str__(self):
         s = '{}:{}:{}'.format(self.o['arch'], self.o['fork'], self.o['network_id'])
-        if self.o['common_name'] != None:
+        if self.o.get('common_name'):
             s += ':' + self.o['common_name']
+        if self.o.get('custom'):
+            s += ':' + ':'.join(self.o['custom'])
         return s
diff --git a/chainlib/cli/config.py b/chainlib/cli/config.py
index fdc788b..19d5feb 100644
--- a/chainlib/cli/config.py
+++ b/chainlib/cli/config.py
@@ -236,7 +236,8 @@ class Config(confini.Config):
             if existing_r == None or r != None:
                 config.add(r, v, exists_ok=True)
 
-        if getattr(args, 'dumpconfig', None) != None:
+        logg.debug('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>' + str(getattr(args, 'dumpconfig')))
+        if getattr(args, 'dumpconfig', None):
             config_keys = config.all()
             with_values = not config.get('_RAW')
             for k in config_keys:
diff --git a/setup.cfg b/setup.cfg
index 2efaaca..618818f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = chainlib
-version = 0.0.10a8
+version = 0.0.10a9
 description = Generic blockchain access library and tooling
 author = Louis Holbrook
 author_email = dev@holbrook.no
diff --git a/tests/test_chain.py b/tests/test_chain.py
index 3e83654..9e8f6e9 100644
--- a/tests/test_chain.py
+++ b/tests/test_chain.py
@@ -1,40 +1,81 @@
+# standard imports
 import unittest
+import logging
 
+# local imports
 from chainlib.chain import ChainSpec
 
+# test imports
 from tests.base import TestBase
 
+logging.basicConfig(level=logging.DEBUG)
+logg = logging.getLogger()
+logg.setLevel(logging.DEBUG)
+
 
 class TestChain(TestBase):
 
     def test_chain_spec_str(self):
-        s = ChainSpec('foo', 'bar', 3, 'baz')
-        self.assertEqual('foo:bar:3:baz', str(s))
-
         s = ChainSpec('foo', 'bar', 3)
         self.assertEqual('foo:bar:3', str(s))
 
-    def test_chain_spec(self):
+        s = ChainSpec('foo', 'bar', 3, 'baz')
+        self.assertEqual('foo:bar:3:baz', str(s))
 
+
+        s = ChainSpec('foo', 'bar', 3, 'baz', ['inky', 'pinky', 'blinky'])
+        self.assertEqual('foo:bar:3:baz:inky:pinky:blinky', str(s))
+
+
+    def test_chain_spec(self):
         s = ChainSpec.from_chain_str('foo:bar:3')
         s = ChainSpec.from_chain_str('foo:bar:3:baz')
+        s = ChainSpec.from_chain_str('foo:bar:3:baz:inky:pinky:blinky')
 
         with self.assertRaises(ValueError):
             s = ChainSpec.from_chain_str('foo:bar:a')
             s = ChainSpec.from_chain_str('foo:bar')
             s = ChainSpec.from_chain_str('foo')
+            s = ChainSpec.from_chain_str('foo1:bar:3')
+            s = ChainSpec.from_chain_str('foo:bar2:3')
 
 
     def test_chain_spec_dict(self):
-        s = 'foo:bar:3:baz'
-        c = ChainSpec.from_chain_str('foo:bar:3:baz')
+        ss = 'foo:bar:3:baz:inky:pinky:blinky'
+        c = ChainSpec.from_chain_str(ss)
         d = c.asdict()
         self.assertEqual(d['arch'], 'foo')
         self.assertEqual(d['fork'], 'bar')
         self.assertEqual(d['network_id'], 3)
         self.assertEqual(d['common_name'], 'baz')
+        self.assertEqual(d['custom'], ['inky', 'pinky', 'blinky'])
         cc = ChainSpec.from_dict(d)
-        self.assertEqual(s, str(cc))
+        self.assertEqual(ss, str(cc))
+
+        d = c.asdict(use_custom=False)
+        cc = ChainSpec.from_dict(d)
+        self.assertEqual(str(cc), 'foo:bar:3:baz')
+
+        d = c.asdict(use_common_name=False)
+        cc = ChainSpec.from_dict(d)
+        self.assertEqual(str(cc), 'foo:bar:3')
+
+    def test_chain_spec_compare(self):
+        a = 'foo:bar:42:baz'
+        b = 'foo:bar:42:barbar'
+        c = 'foo:bar:42:baz:inky:pinky:blinky'
+
+        ca = ChainSpec.from_chain_str(a)
+        cb = ChainSpec.from_chain_str(b)
+
+        self.assertTrue(ca.is_same_as(cb))
+        self.assertFalse(ca.is_same_as(cb, use_common_name=True))
+
+        cc = ChainSpec.from_chain_str(c)
+        logg.debug('chain_spec_cmp ' + str(cc.o))
+        self.assertTrue(ca.is_same_as(cc))
+        self.assertTrue(ca.is_same_as(cc, use_common_name=True))
+        self.assertFalse(ca.is_same_as(cc, use_common_name=True, use_custom=True))
 
 
 
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 0f54fc4..bc1b6e5 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -1,6 +1,7 @@
 # standard imports
 import unittest
 import os
+import logging
 
 # local imports
 import chainlib.cli
@@ -10,6 +11,8 @@ script_dir = os.path.dirname(os.path.realpath(__file__))
 data_dir = os.path.join(script_dir, 'testdata')
 config_dir = os.path.join(data_dir, 'config')
 
+logging.basicConfig(level=logging.DEBUG)
+
 
 class TestCli(unittest.TestCase):
     

From d914f647976595277881f469e1899e88f3da2f94 Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Thu, 28 Oct 2021 13:27:30 +0200
Subject: [PATCH 11/18] Remove logline span

---
 chainlib/cli/config.py | 1 -
 1 file changed, 1 deletion(-)

diff --git a/chainlib/cli/config.py b/chainlib/cli/config.py
index 19d5feb..ef00d24 100644
--- a/chainlib/cli/config.py
+++ b/chainlib/cli/config.py
@@ -236,7 +236,6 @@ class Config(confini.Config):
             if existing_r == None or r != None:
                 config.add(r, v, exists_ok=True)
 
-        logg.debug('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>' + str(getattr(args, 'dumpconfig')))
         if getattr(args, 'dumpconfig', None):
             config_keys = config.all()
             with_values = not config.get('_RAW')

From 70d8ab6a33b5bfe3a7b88dc78cda1b6615d0ac50 Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Sat, 30 Oct 2021 14:10:19 +0200
Subject: [PATCH 12/18] Add signer initializer

---
 chainlib/cli/wallet.py | 4 ++++
 setup.cfg              | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/chainlib/cli/wallet.py b/chainlib/cli/wallet.py
index 3448e8f..4b5667b 100644
--- a/chainlib/cli/wallet.py
+++ b/chainlib/cli/wallet.py
@@ -27,6 +27,10 @@ class Wallet:
         self.use_checksum = False
 
 
+    def init(self):
+        self.signer = self.signer_constructor(self.keystore)
+
+
     def from_config(self, config):
         """Instantiates a signer from the registered signer class, using parameters from a processed configuration.
 
diff --git a/setup.cfg b/setup.cfg
index 618818f..02379b7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = chainlib
-version = 0.0.10a9
+version = 0.0.10a10
 description = Generic blockchain access library and tooling
 author = Louis Holbrook
 author_email = dev@holbrook.no

From 24d7c078e85bc574c938dc95d37e0187b251ea25 Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Sat, 6 Nov 2021 15:29:59 +0100
Subject: [PATCH 13/18] Remove prerelease tag

---
 chainlib/cli/config.py | 2 ++
 chainlib/encode.py     | 2 --
 requirements.txt       | 4 ++--
 setup.cfg              | 3 ++-
 4 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/chainlib/cli/config.py b/chainlib/cli/config.py
index ef00d24..6fdbc18 100644
--- a/chainlib/cli/config.py
+++ b/chainlib/cli/config.py
@@ -224,6 +224,7 @@ class Config(confini.Config):
             config.add(getattr(args, 'rpc_credentials'), 'RPC_CREDENTIALS')
 
         for k in extra_args.keys():
+            logg.debug('extra_agrs {}'.format(k))
             v = extra_args[k]
             if v == None:
                 v = '_' + k.upper()
@@ -235,6 +236,7 @@ class Config(confini.Config):
                 pass
             if existing_r == None or r != None:
                 config.add(r, v, exists_ok=True)
+                logg.debug('added {} to {}'.format(r, v))
 
         if getattr(args, 'dumpconfig', None):
             config_keys = config.all()
diff --git a/chainlib/encode.py b/chainlib/encode.py
index 7275fd6..763cabc 100644
--- a/chainlib/encode.py
+++ b/chainlib/encode.py
@@ -30,9 +30,7 @@ class TxHexNormalizer:
 
 
     def __hex_normalize(self, data, context):
-        #r = add_0x(hex_uniform(strip_0x(data)))
         r = hex_uniform(strip_0x(data))
-        logg.debug('normalize {} {} -> {}'.format(context, data, r))
         return r
 
 
diff --git a/requirements.txt b/requirements.txt
index 135a972..7718bf2 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,3 @@
-funga>=0.5.1a1,<0.6.0
+funga~=0.5.1
 pysha3==1.0.2
-hexathon~=0.0.1a8
+hexathon~=0.1.0
diff --git a/setup.cfg b/setup.cfg
index 02379b7..b2435eb 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = chainlib
-version = 0.0.10a10
+version = 0.0.10
 description = Generic blockchain access library and tooling
 author = Louis Holbrook
 author_email = dev@holbrook.no
@@ -13,6 +13,7 @@ classifiers =
 	Programming Language :: Python :: 3
 	Operating System :: OS Independent
 	Development Status :: 3 - Alpha
+	Topic :: Software Development :: Libraries
 	Environment :: Console
 	Intended Audience :: Developers
 	License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)

From 39478d9c3c30210b64a1f3f3f26f33fef2946a40 Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Mon, 8 Nov 2021 07:49:02 +0100
Subject: [PATCH 14/18] Add bluto package info renderer

---
 .bluto/bluto.deb.ini | 17 +++++++++++++++++
 .bluto/bluto.ini     | 18 ++++++++++++++++++
 .bluto/bluto.py.ini  |  6 ++++++
 .bluto/bluto.tag     | 11 +++++++++++
 setup.cfg            | 29 ++++++++++++-----------------
 setup.py             |  9 ++++++++-
 6 files changed, 72 insertions(+), 18 deletions(-)
 create mode 100644 .bluto/bluto.deb.ini
 create mode 100644 .bluto/bluto.ini
 create mode 100644 .bluto/bluto.py.ini
 create mode 100644 .bluto/bluto.tag

diff --git a/.bluto/bluto.deb.ini b/.bluto/bluto.deb.ini
new file mode 100644
index 0000000..2339df1
--- /dev/null
+++ b/.bluto/bluto.deb.ini
@@ -0,0 +1,17 @@
+[main:deb]
+standards_version=3.8.5
+priority=optional
+engine=9
+
+[dep:deb-build]
+debhelper=>=9
+python3=>=3.8
+python3-setuptools=0
+dh-python=0
+
+[dep:deb-install]
+dpkg=0
+python3=>=3.8
+
+[dep:deb-exec]
+make=0
diff --git a/.bluto/bluto.ini b/.bluto/bluto.ini
new file mode 100644
index 0000000..949c162
--- /dev/null
+++ b/.bluto/bluto.ini
@@ -0,0 +1,18 @@
+[main]
+name = chainlib
+version = 0.0.10
+summary = Generic blockchain access library and tooling
+
+[author:lash]
+name = Louis Holbrook
+email = dev@holbrook.no
+pgp=0826EDA1702D1E87C6E2875121D2E7BB88C2A746
+
+[locate:git]
+url = https://gitlab.com/chaintools/chainlib
+
+[locate:lash]
+url=https://holbrook.no
+
+[license]
+WTFPL=2
diff --git a/.bluto/bluto.py.ini b/.bluto/bluto.py.ini
new file mode 100644
index 0000000..6dd2e00
--- /dev/null
+++ b/.bluto/bluto.py.ini
@@ -0,0 +1,6 @@
+[main:py]
+include_data = 1
+packages = chainlib,chainlib.cli
+
+[dep:py-exec]
+python=>=3.6
diff --git a/.bluto/bluto.tag b/.bluto/bluto.tag
new file mode 100644
index 0000000..e27c07a
--- /dev/null
+++ b/.bluto/bluto.tag
@@ -0,0 +1,11 @@
+dlt
+blockchain
+cryptocurrency
+Programming Language :: Python :: 3
+Operating System :: OS Independent
+Development Status :: 3 - Alpha
+Topic :: Software Development :: Libraries
+Environment :: Console
+Intended Audience :: Developers
+License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
+Topic :: Internet
diff --git a/setup.cfg b/setup.cfg
index b2435eb..66600b4 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,10 +1,16 @@
+; Config::Simple 4.59
+; Mon Nov  8 05:19:17 2021
+
 [metadata]
-name = chainlib
-version = 0.0.10
-description = Generic blockchain access library and tooling
-author = Louis Holbrook
-author_email = dev@holbrook.no
-url = https://gitlab.com/chaintools/chainlib
+name=chainlib
+license=WTFPL2
+author_email=dev@holbrook.no
+description=Generic blockchain access library and tooling
+version=0.0.10
+url=https://gitlab.com/chaintools/chainlib
+author=Louis Holbrook
+
+
 keywords =
 	dlt
 	blockchain
@@ -18,14 +24,3 @@ classifiers =
 	Intended Audience :: Developers
 	License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
 	Topic :: Internet
-license = GPL3
-licence_files =
-	LICENSE.txt
-
-
-[options]
-python_requires = >= 3.6
-include_package_data = True
-packages = 
-	chainlib
-	chainlib.cli
diff --git a/setup.py b/setup.py
index 172e0b5..4e9c807 100644
--- a/setup.py
+++ b/setup.py
@@ -16,5 +16,12 @@ setup(
         install_requires=requirements,
         extras_require={
              'xdg': "pyxdg~=0.27",
-             }
+             },
+        license_files= ('LICENSE.txt',),
+        python_requires = '>=3.8',
+        include_package_data = True,
+        packages = [
+            'chainlib',
+            'chainlib.cli',
+            ],
     )

From 1067c8494e72805a3ea8f1f39c22ed037349aef2 Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Wed, 10 Nov 2021 09:56:51 +0100
Subject: [PATCH 15/18] Add omit sections for config exporter

---
 .bluto/bluto.ini       |  2 +-
 chainlib/cli/arg.py    |  2 +-
 chainlib/cli/config.py | 32 ++++++++++++++++++++------------
 setup.cfg              |  2 +-
 4 files changed, 23 insertions(+), 15 deletions(-)

diff --git a/.bluto/bluto.ini b/.bluto/bluto.ini
index 949c162..7d1352a 100644
--- a/.bluto/bluto.ini
+++ b/.bluto/bluto.ini
@@ -15,4 +15,4 @@ url = https://gitlab.com/chaintools/chainlib
 url=https://holbrook.no
 
 [license]
-WTFPL=2
+GPL=3+
diff --git a/chainlib/cli/arg.py b/chainlib/cli/arg.py
index 9658b43..e3337be 100644
--- a/chainlib/cli/arg.py
+++ b/chainlib/cli/arg.py
@@ -144,7 +144,7 @@ class ArgumentParser(argparse.ArgumentParser):
         if arg_flags & Flag.CONFIG:
             self.add_argument('-c', '--config', type=str, default=env.get('CONFINI_DIR'), help='Configuration directory')
             self.add_argument('-n', '--namespace', type=str, help='Configuration namespace')
-            self.add_argument('--dumpconfig', action='store_true', help='Output configuration and quit. Use with --raw to omit values and output schema only.')
+            self.add_argument('--dumpconfig', type=str, choices=['env', 'ini'], help='Output configuration and quit. Use with --raw to omit values and output schema only.')
         if arg_flags & Flag.WAIT:
             self.add_argument('-w', action='store_true', help='Wait for the last transaction to be confirmed')
             self.add_argument('-ww', action='store_true', help='Wait for every transaction to be confirmed')
diff --git a/chainlib/cli/config.py b/chainlib/cli/config.py
index 6fdbc18..21ef81f 100644
--- a/chainlib/cli/config.py
+++ b/chainlib/cli/config.py
@@ -239,18 +239,26 @@ class Config(confini.Config):
                 logg.debug('added {} to {}'.format(r, v))
 
         if getattr(args, 'dumpconfig', None):
-            config_keys = config.all()
-            with_values = not config.get('_RAW')
-            for k in config_keys:
-                if k[0] == '_':
-                    continue
-                s = k + '='
-                if with_values:
-                    v = config.get(k)
-                    if v != None:
-                        s += str(v)
-                s += '\n'
-                dump_writer.write(s)
+            if args.dumpconfig == 'ini':
+                from confini.export import ConfigExporter
+                exporter = ConfigExporter(config, target=sys.stdout, doc=False)
+                exporter.export(exclude_sections=[config])
+            elif args.dumpconfig == 'env':
+                from confini.env import export_env
+                export_env(config)
+
+#            config_keys = config.all()
+#            with_values = not config.get('_RAW')
+#            for k in config_keys:
+#                if k[0] == '_':
+#                    continue
+#                s = k + '='
+#                if with_values:
+#                    v = config.get(k)
+#                    if v != None:
+#                        s += str(v)
+#                s += '\n'
+#                dump_writer.write(s)
             sys.exit(0)
 
         if load_callback != None:
diff --git a/setup.cfg b/setup.cfg
index 66600b4..12e1b06 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -6,7 +6,7 @@ name=chainlib
 license=WTFPL2
 author_email=dev@holbrook.no
 description=Generic blockchain access library and tooling
-version=0.0.10
+version=0.0.11
 url=https://gitlab.com/chaintools/chainlib
 author=Louis Holbrook
 

From 06c6b2562ad0dd631dd8b297bf2b20f7d63a9884 Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Wed, 10 Nov 2021 11:14:02 +0100
Subject: [PATCH 16/18] Replace string with object in config export suppress

---
 .bluto/bluto.ini       | 2 +-
 chainlib/cli/config.py | 2 +-
 setup.cfg              | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/.bluto/bluto.ini b/.bluto/bluto.ini
index 7d1352a..0792b56 100644
--- a/.bluto/bluto.ini
+++ b/.bluto/bluto.ini
@@ -1,6 +1,6 @@
 [main]
 name = chainlib
-version = 0.0.10
+version = 0.0.12
 summary = Generic blockchain access library and tooling
 
 [author:lash]
diff --git a/chainlib/cli/config.py b/chainlib/cli/config.py
index 21ef81f..94871bd 100644
--- a/chainlib/cli/config.py
+++ b/chainlib/cli/config.py
@@ -242,7 +242,7 @@ class Config(confini.Config):
             if args.dumpconfig == 'ini':
                 from confini.export import ConfigExporter
                 exporter = ConfigExporter(config, target=sys.stdout, doc=False)
-                exporter.export(exclude_sections=[config])
+                exporter.export(exclude_sections=['config'])
             elif args.dumpconfig == 'env':
                 from confini.env import export_env
                 export_env(config)
diff --git a/setup.cfg b/setup.cfg
index 12e1b06..503fcee 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -6,7 +6,7 @@ name=chainlib
 license=WTFPL2
 author_email=dev@holbrook.no
 description=Generic blockchain access library and tooling
-version=0.0.11
+version=0.0.12
 url=https://gitlab.com/chaintools/chainlib
 author=Louis Holbrook
 

From becd6744f6b628c819590ffdd49d9c19081351f3 Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Mon, 6 Dec 2021 18:55:36 +0100
Subject: [PATCH 17/18] Add option to skip ssl validation on rpc

---
 CHANGELOG                       |  4 +++-
 chainlib/chain.py               |  9 ++++++++-
 chainlib/cli/rpc.py             |  2 +-
 chainlib/connection.py          | 16 ++++++++++++++--
 chainlib/data/config/config.ini |  1 +
 setup.cfg                       |  2 +-
 6 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 39b43be..57ce577 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,6 @@
-- 0.0.5-pending
+- 0.0.14
+	* Add option to skip ssl verification on rpc
+- 0.0.5
 	* Move eth code to separate package
 - 0.0.4-unreleased
 	* Add pack tx from already signed tx struct
diff --git a/chainlib/chain.py b/chainlib/chain.py
index 71c5e1f..e72f806 100644
--- a/chainlib/chain.py
+++ b/chainlib/chain.py
@@ -186,10 +186,17 @@ class ChainSpec:
         return r
 
 
-    def __str__(self):
+    def as_string(self, skip_optional=False):
         s = '{}:{}:{}'.format(self.o['arch'], self.o['fork'], self.o['network_id'])
+        if skip_optional:
+            return s
+
         if self.o.get('common_name'):
             s += ':' + self.o['common_name']
         if self.o.get('custom'):
             s += ':' + ':'.join(self.o['custom'])
         return s
+
+
+    def __str__(self):
+        return self.as_string()        
diff --git a/chainlib/cli/rpc.py b/chainlib/cli/rpc.py
index fd76b01..69ead3b 100644
--- a/chainlib/cli/rpc.py
+++ b/chainlib/cli/rpc.py
@@ -61,7 +61,7 @@ class Rpc:
             self.id_generator = IntSequenceGenerator()
 
         self.chain_spec = config.get('CHAIN_SPEC')
-        self.conn = self.constructor(url=config.get('RPC_PROVIDER'), chain_spec=self.chain_spec, auth=auth)
+        self.conn = self.constructor(url=config.get('RPC_PROVIDER'), chain_spec=self.chain_spec, auth=auth, verify_identity=config.true('RPC_VERIFY'))
 
         return self.conn
 
diff --git a/chainlib/connection.py b/chainlib/connection.py
index 786fda0..b56b403 100644
--- a/chainlib/connection.py
+++ b/chainlib/connection.py
@@ -102,10 +102,13 @@ class RPCConnection:
         }
     __constructors_for_chains = {}
 
-    def __init__(self, url=None, chain_spec=None, auth=None):
+    def __init__(self, url=None, chain_spec=None, auth=None, verify_identity=True):
         self.chain_spec = chain_spec
         self.location = None
         self.basic = None
+        self.verify_identity = verify_identity
+        if not self.verify_identity:
+            logg.warning('RPC host identity verification is OFF. Beware, you will be easy to cheat')
         if url == None:
             return
         self.auth = auth
@@ -287,6 +290,11 @@ class JSONRPCHTTPConnection(HTTPConnection):
         :returns: Result value part of JSON RPC response
         :todo: Invalid response exception from invalid json response
         """
+        ssl_ctx = None
+        if not self.verify_identity:
+            import ssl
+            ssl_ctx = ssl.SSLContext()
+            ssl_ctx.verify_mode = ssl.CERT_NONE
         req = Request(
                 self.location,
                 method='POST',
@@ -313,7 +321,11 @@ class JSONRPCHTTPConnection(HTTPConnection):
             install_opener(ho)
 
         try: 
-            r = urlopen(req, data=data.encode('utf-8'))
+            r = urlopen(
+                req,
+                data=data.encode('utf-8'),
+                context=ssl_ctx,
+                )
         except URLError as e:
             raise RPCException(e)
 
diff --git a/chainlib/data/config/config.ini b/chainlib/data/config/config.ini
index 3b03253..fe99281 100644
--- a/chainlib/data/config/config.ini
+++ b/chainlib/data/config/config.ini
@@ -4,6 +4,7 @@ auth =
 credentials =
 dialect = default
 scheme = http
+verify = 1
 
 [chain]
 spec = 
diff --git a/setup.cfg b/setup.cfg
index 503fcee..156ad40 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -6,7 +6,7 @@ name=chainlib
 license=WTFPL2
 author_email=dev@holbrook.no
 description=Generic blockchain access library and tooling
-version=0.0.12
+version=0.0.14
 url=https://gitlab.com/chaintools/chainlib
 author=Louis Holbrook
 

From 6a94208c682ccfb04234a1a4d4f4a05a20b0af9f Mon Sep 17 00:00:00 2001
From: nolash <dev@holbrook.no>
Date: Tue, 21 Dec 2021 14:58:09 +0000
Subject: [PATCH 18/18] Remove bluto

---
 .bluto/bluto.deb.ini | 17 -----------------
 .bluto/bluto.ini     | 18 ------------------
 .bluto/bluto.py.ini  |  6 ------
 .bluto/bluto.tag     | 11 -----------
 4 files changed, 52 deletions(-)
 delete mode 100644 .bluto/bluto.deb.ini
 delete mode 100644 .bluto/bluto.ini
 delete mode 100644 .bluto/bluto.py.ini
 delete mode 100644 .bluto/bluto.tag

diff --git a/.bluto/bluto.deb.ini b/.bluto/bluto.deb.ini
deleted file mode 100644
index 2339df1..0000000
--- a/.bluto/bluto.deb.ini
+++ /dev/null
@@ -1,17 +0,0 @@
-[main:deb]
-standards_version=3.8.5
-priority=optional
-engine=9
-
-[dep:deb-build]
-debhelper=>=9
-python3=>=3.8
-python3-setuptools=0
-dh-python=0
-
-[dep:deb-install]
-dpkg=0
-python3=>=3.8
-
-[dep:deb-exec]
-make=0
diff --git a/.bluto/bluto.ini b/.bluto/bluto.ini
deleted file mode 100644
index 0792b56..0000000
--- a/.bluto/bluto.ini
+++ /dev/null
@@ -1,18 +0,0 @@
-[main]
-name = chainlib
-version = 0.0.12
-summary = Generic blockchain access library and tooling
-
-[author:lash]
-name = Louis Holbrook
-email = dev@holbrook.no
-pgp=0826EDA1702D1E87C6E2875121D2E7BB88C2A746
-
-[locate:git]
-url = https://gitlab.com/chaintools/chainlib
-
-[locate:lash]
-url=https://holbrook.no
-
-[license]
-GPL=3+
diff --git a/.bluto/bluto.py.ini b/.bluto/bluto.py.ini
deleted file mode 100644
index 6dd2e00..0000000
--- a/.bluto/bluto.py.ini
+++ /dev/null
@@ -1,6 +0,0 @@
-[main:py]
-include_data = 1
-packages = chainlib,chainlib.cli
-
-[dep:py-exec]
-python=>=3.6
diff --git a/.bluto/bluto.tag b/.bluto/bluto.tag
deleted file mode 100644
index e27c07a..0000000
--- a/.bluto/bluto.tag
+++ /dev/null
@@ -1,11 +0,0 @@
-dlt
-blockchain
-cryptocurrency
-Programming Language :: Python :: 3
-Operating System :: OS Independent
-Development Status :: 3 - Alpha
-Topic :: Software Development :: Libraries
-Environment :: Console
-Intended Audience :: Developers
-License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
-Topic :: Internet