cic-cli/cic/extension.py

215 lines
6.7 KiB
Python
Raw Normal View History

2021-10-12 08:39:20 +02:00
# standard imports
import logging
2022-02-21 06:34:25 +01:00
from typing import TYPE_CHECKING
2021-10-12 08:39:20 +02:00
# external imports
from hexathon import valid as valid_hex
2021-10-15 12:48:14 +02:00
# local imports
from cic.output import StdoutWriter
2022-02-21 06:34:25 +01:00
from cic.token import Token
2021-10-15 12:48:14 +02:00
2021-10-12 08:39:20 +02:00
logg = logging.getLogger(__name__)
class Extension:
2021-11-29 13:18:28 +01:00
"""Base class adapter to initialize, serialize and publish extension-specific token resources.
:param chain_spec: Chain Spec that extension will operate for
:type chain_spec: chainlib.chain.ChainSpec
:param resources: Chain application resources to deploy or interface with
:type resources: dict
:param proof: Proof object to publish
:type proof: cic.proof.Proof
:param signer: Signer capable of generating signatures for chain aplication deployments
:type signer: funga.signer.Signer
:param rpc: RPC adapter capable of submitting and querying the chain network node
:type rpc: chainlib.connection.RPCConnection
:param writer: Writer interface receiving the output of the processor
:type writer: cic.output.OutputWriter
"""
2022-02-21 06:34:25 +01:00
def __init__(
self,
chain_spec,
resources,
proof,
signer=None,
rpc=None,
outputs_writer=StdoutWriter(),
):
2021-10-12 08:39:20 +02:00
self.resources = resources
self.proof = proof
self.chain_spec = chain_spec
self.signer = signer
self.rpc = rpc
self.token_details = None
self.token_address = None
self.token_code = None
self.outputs = []
2021-10-15 12:48:14 +02:00
self.outputs_writer = outputs_writer
2021-10-12 08:39:20 +02:00
# TODO: apply / prepare token can be factored out
2022-02-21 06:34:25 +01:00
def apply_token(self, token: Token):
2021-11-29 13:18:28 +01:00
"""Initialize extension with token data from settings.
:param token: Token object
:type token: cic.token.Token
:rtype: dict
:returns: Token data state of extension after load
"""
2022-02-21 06:34:25 +01:00
return self.prepare_token(
token.name,
token.symbol,
token.precision,
token.code,
token.supply,
token.extra_args,
token.extra_args_types,
)
def prepare_token(
self,
name,
symbol,
precision,
code,
supply,
extra=[],
extra_types=[],
positions=None,
):
2021-11-29 13:18:28 +01:00
"""Initialize extension token data.
:param name: Token name
:type name: str
:param symbol: Token symbol
:type symbol: str
:param precision: Token value precision (number of decimals)
:type precision: int
:param code: Bytecode for token chain application
:type code: str (hex)
:param supply: Token supply (in smallest precision units)
:type supply: int
:param extra: Extra parameters to pass to token application constructor
2022-02-21 06:34:25 +01:00
:type extra: list
2021-11-29 13:18:28 +01:00
:param extra_types: Type specifications for extra parameters
:type extra_types: list
:param positions: Sequence of parameter indices to pass to application constructor
:type positions: list
:rtype: dict
:returns: Token data state of extension after load
"""
2021-10-12 08:39:20 +02:00
self.token_details = {
2022-02-21 06:34:25 +01:00
"name": name,
"symbol": symbol,
"precision": precision,
"code": code,
"supply": supply,
"extra": extra,
"extra_types": extra_types,
"positions": positions,
}
logg.debug(f"token details: {self.token_details}")
2021-11-29 13:18:28 +01:00
return self.token_details
2021-10-12 08:39:20 +02:00
def prepare_extension(self):
2022-02-21 06:34:25 +01:00
"""Prepare extension for publishing (noop)"""
2021-10-12 08:39:20 +02:00
pass
def parse_code_as_file(self, v):
2021-11-29 13:18:28 +01:00
"""Helper method to load application bytecode from file into extensions token data state.
Client code should call load_code instead.
:param v: File path
:type v: str
"""
2021-10-12 08:39:20 +02:00
try:
2022-02-21 06:34:25 +01:00
f = open(v, "r")
2021-10-12 08:39:20 +02:00
r = f.read()
f.close()
self.parse_code_as_hex(r)
2021-12-15 14:00:18 +01:00
except FileNotFoundError as e:
2022-02-21 06:34:25 +01:00
logg.debug("could not parse code as file: {}".format(e))
2021-10-12 08:39:20 +02:00
pass
2021-12-15 14:00:18 +01:00
except IsADirectoryError as e:
2022-02-21 06:34:25 +01:00
logg.debug("could not parse code as file: {}".format(e))
2021-10-12 08:39:20 +02:00
pass
def parse_code_as_hex(self, v):
2021-11-29 13:18:28 +01:00
"""Helper method to load application bytecode from hex data into extension token data state.
Client code should call load_code instead.
:param v: Bytecode as hex
:type v: str
"""
2021-12-15 14:00:18 +01:00
try:
2021-10-12 08:39:20 +02:00
self.token_code = valid_hex(v)
except ValueError as e:
2022-02-21 06:34:25 +01:00
logg.debug("could not parse code as hex: {}".format(e))
2021-10-12 08:39:20 +02:00
pass
def load_code(self, hint=None):
2021-11-29 13:18:28 +01:00
"""Attempt to load token application bytecode using token settings.
:param hint: If "hex", will interpret code in settings as literal bytecode
:type hint: str
:rtype: str (hex)
:return: Bytecode loaded into extension token data state
"""
2022-02-21 06:34:25 +01:00
code = self.token_details["code"]
if hint == "hex":
2021-10-12 08:39:20 +02:00
self.token_code = valid_hex(code)
2021-12-15 14:00:18 +01:00
2021-10-12 08:39:20 +02:00
for m in [
2022-02-21 06:34:25 +01:00
self.parse_code_as_hex,
self.parse_code_as_file,
]:
2021-10-12 08:39:20 +02:00
m(code)
if self.token_code != None:
break
2021-12-15 14:00:18 +01:00
2021-10-12 08:39:20 +02:00
if self.token_code == None:
2022-02-21 06:34:25 +01:00
raise RuntimeError("could not successfully parse token code")
2021-10-12 08:39:20 +02:00
return self.token_code
def process(self, writer=None):
2021-11-29 13:18:28 +01:00
"""Adapter used by Processor to process the extensions implementing the Extension base class.
Requires either token address or a valid token code reference to have been included in settings. If token address is not set, the token application code will be deployed.
:param writer: Writer to use for publishing.
:type writer: cic.output.OutputWriter
:rtype: tuple
:return: Token address, token symbol
"""
2021-10-12 08:39:20 +02:00
if writer == None:
writer = self.outputs_writer
2021-12-15 14:00:18 +01:00
tasks = []
2022-02-21 06:34:25 +01:00
self.token_address = self.resources["token"]["reference"]
2021-12-15 14:00:18 +01:00
2021-10-21 15:11:05 +02:00
# TODO: get token details when token address is not none
2021-10-12 08:39:20 +02:00
if self.token_address == None:
2022-02-21 06:34:25 +01:00
if self.token_details["code"] == None:
raise RuntimeError("neither token address nor token code has been set")
2021-10-12 08:39:20 +02:00
self.load_code()
2022-02-21 06:34:25 +01:00
tasks.append("token")
2021-10-12 08:39:20 +02:00
for k in self.resources.keys():
2022-02-21 06:34:25 +01:00
if k == "token":
2021-10-12 08:39:20 +02:00
continue
2022-02-21 06:34:25 +01:00
if self.resources[k]["reference"] != None:
2021-10-12 08:39:20 +02:00
tasks.append(k)
2021-12-15 14:00:18 +01:00
2021-10-12 08:39:20 +02:00
self.prepare_extension()
for task in tasks:
2022-02-21 06:34:25 +01:00
logg.debug("extension adapter process {}".format(task))
r = getattr(self, "process_" + task)(writer=writer)
2021-10-12 08:39:20 +02:00
2022-02-21 06:34:25 +01:00
return (self.token_address, self.token_details.get("symbol"))