From 0260304fabf8fdf9acbec8b475e8b58c620f52ff Mon Sep 17 00:00:00 2001
From: Holger Nahrstaedt <holgernahrstaedt@gmx.de>
Date: Sat, 9 Jun 2018 09:01:29 +0200
Subject: [PATCH] New operation structure for appbase

Transactionbuilder
* Prepare broadcasting in new appbase format
* Force condenser_api by now
RPCUtils
* Improve detection of conenser_api
Streemnoderpc
* Improved error message for Assert Exception:v.is_object()
beembase.Object
* Add new appbase Operation format
beemgraphenebase.object
* Add handling of new appbase operation format
---
 beem/transactionbuilder.py  | 26 ++++++++++++++++++++------
 beemapi/rpcutils.py         |  2 +-
 beemapi/steemnoderpc.py     |  2 ++
 beembase/objects.py         |  6 +++++-
 beemgraphenebase/objects.py | 13 +++++++++++++
 5 files changed, 41 insertions(+), 8 deletions(-)

diff --git a/beem/transactionbuilder.py b/beem/transactionbuilder.py
index 02505c6c..69ea7504 100644
--- a/beem/transactionbuilder.py
+++ b/beem/transactionbuilder.py
@@ -78,7 +78,12 @@ class TransactionBuilder(dict):
 
     def list_operations(self):
         """List all ops"""
-        return [Operation(o) for o in self.ops]
+        if self.steem.is_connected() and self.steem.rpc.get_use_appbase():
+            # appbase disabled by now
+            appbase = False
+        else:
+            appbase = False
+        return [Operation(o, appbase=appbase) for o in self.ops]
 
     def _is_signed(self):
         """Check if signatures exists"""
@@ -230,12 +235,17 @@ class TransactionBuilder(dict):
 
         """
         ops = list()
+        if self.steem.is_connected() and self.steem.rpc.get_use_appbase():
+            # appbase disabled by now
+            # broadcasting does not work at the moment
+            appbase = False
+        else:
+            appbase = False
         for op in self.ops:
             # otherwise, we simply wrap ops into Operations
-            ops.extend([Operation(op)])
+            ops.extend([Operation(op, appbase=appbase)])
 
         # We no wrap everything into an actual transaction
-        # ops = transactions.addRequiredFees(self.steem.rpc, ops)
         expiration = formatTimeFromNow(
             self.expiration or self.steem.expiration
         )
@@ -370,9 +380,13 @@ class TransactionBuilder(dict):
             return
         ret = self.json()
         if self.steem.is_connected() and self.steem.rpc.get_use_appbase():
-            args = {'trx': self.json(), 'max_block_age': max_block_age}
+            # Returns an internal Error at the moment
+            # args = {'trx': self.json(), 'max_block_age': max_block_age}
+            args = self.json()
+            broadcast_api = "condenser"
         else:
             args = self.json()
+            broadcast_api = "network_broadcast"
 
         if self.steem.nobroadcast:
             log.warning("Not broadcasting anything!")
@@ -385,11 +399,11 @@ class TransactionBuilder(dict):
                 ret = self.steem.steemconnect.broadcast(self["operations"])
             elif self.steem.blocking:
                 ret = self.steem.rpc.broadcast_transaction_synchronous(
-                    args, api="network_broadcast")
+                    args, api=broadcast_api)
                 ret.update(**ret.get("trx"))
             else:
                 self.steem.rpc.broadcast_transaction(
-                    args, api="network_broadcast")
+                    args, api=broadcast_api)
         except Exception as e:
             # log.error("Could Not broadcasting anything!")
             self.clear()
diff --git a/beemapi/rpcutils.py b/beemapi/rpcutils.py
index cc0dde51..8e35e4c2 100644
--- a/beemapi/rpcutils.py
+++ b/beemapi/rpcutils.py
@@ -26,7 +26,7 @@ def is_network_appbase_ready(props):
 
 def get_query(appbase, request_id, api_name, name, args):
     query = []
-    if not appbase:
+    if not appbase or api_name == "condenser_api":
         query = {"method": "call",
                  "params": [api_name, name, list(args)],
                  "jsonrpc": "2.0",
diff --git a/beemapi/steemnoderpc.py b/beemapi/steemnoderpc.py
index f1b2d5eb..15b12d2a 100644
--- a/beemapi/steemnoderpc.py
+++ b/beemapi/steemnoderpc.py
@@ -140,6 +140,8 @@ class SteemNodeRPC(GrapheneRPC):
             doRetry = True
         elif re.search("out_of_rangeEEEE: unknown key", msg) or re.search("unknown key:unknown key", msg):
             raise exceptions.UnkownKey(msg)
+        elif re.search("Assert Exception:v.is_object(): Input data have to treated as object", msg):
+            raise exceptions.UnhandledRPCError("Use Operation(op, appbase=True) to prevent error: " + msg)
         elif msg:
             raise exceptions.UnhandledRPCError(msg)
         else:
diff --git a/beembase/objects.py b/beembase/objects.py
index 68acf439..db812309 100644
--- a/beembase/objects.py
+++ b/beembase/objects.py
@@ -70,6 +70,7 @@ class Amount(object):
 @python_2_unicode_compatible
 class Operation(GPHOperation):
     def __init__(self, *args, **kwargs):
+        self.appbase = kwargs.pop("appbase", False)
         super(Operation, self).__init__(*args, **kwargs)
 
     def _getklass(self, name):
@@ -96,7 +97,10 @@ class Operation(GPHOperation):
         return py23_bytes(Id(self.opId)) + py23_bytes(self.op)
 
     def __str__(self):
-        return json.dumps([self.name.lower(), self.op.toJson()])
+        if self.appbase:
+            return json.dumps({'type': self.name.lower() + '_operation', 'value': self.op.toJson()})
+        else:
+            return json.dumps([self.name.lower(), self.op.toJson()])
 
 
 class Memo(GrapheneObject):
diff --git a/beemgraphenebase/objects.py b/beemgraphenebase/objects.py
index d7f80f47..cb08b642 100644
--- a/beemgraphenebase/objects.py
+++ b/beemgraphenebase/objects.py
@@ -38,6 +38,19 @@ class Operation(object):
             except Exception:
                 raise NotImplementedError("Unimplemented Operation %s" % self.name)
             self.op = klass(op[1])
+            self.appbase = False
+        elif isinstance(op, dict):
+            name = op["type"][:-10]
+            self.opId = self.operations().get(name, None)
+            if self.opId is None:
+                raise ValueError("Unknown operation")
+            self.name = name[0].upper() + name[1:]  # klassname
+            try:
+                klass = self._getklass(self.name)
+            except Exception:
+                raise NotImplementedError("Unimplemented Operation %s" % self.name)
+            self.op = klass(op["value"])
+            self.appbase = True
         else:
             self.op = op
             self.name = type(self.op).__name__.lower()  # also store name
-- 
GitLab