2021-10-12 08:39:20 +02:00
|
|
|
# standard imports
|
|
|
|
import logging
|
|
|
|
|
|
|
|
# external imports
|
|
|
|
from hexathon import valid as valid_hex
|
|
|
|
|
2021-10-15 12:48:14 +02:00
|
|
|
# local imports
|
2022-03-01 08:01:56 +01:00
|
|
|
from cic.writers import StdoutWriter
|
|
|
|
from cic.contract.components.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
|
2022-03-01 08:01:56 +01:00
|
|
|
:type writer: cic.writers.OutputWriter
|
2021-11-29 13:18:28 +01:00
|
|
|
"""
|
2022-03-01 08:01:56 +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-03-01 08:01:56 +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-03-01 08:01:56 +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=None,
|
|
|
|
extra_types=None,
|
|
|
|
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-03-01 08:01:56 +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-03-01 08:01:56 +01:00
|
|
|
"name": name,
|
|
|
|
"symbol": symbol,
|
|
|
|
"precision": precision,
|
|
|
|
"code": code,
|
|
|
|
"supply": supply,
|
|
|
|
"extra": extra or [],
|
|
|
|
"extra_types": extra_types or [],
|
|
|
|
"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-03-01 08:01:56 +01:00
|
|
|
"""Prepare extension for publishing (noop)"""
|
2021-10-12 08:39:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
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-03-01 08:01:56 +01:00
|
|
|
f = open(v, "r", encoding="utf-8")
|
2021-10-12 08:39:20 +02:00
|
|
|
r = f.read()
|
|
|
|
f.close()
|
|
|
|
self.parse_code_as_hex(r)
|
2022-03-01 08:01:56 +01:00
|
|
|
except FileNotFoundError as e:
|
|
|
|
logg.debug(f"could not parse code as file: {e}")
|
|
|
|
except IsADirectoryError as e:
|
|
|
|
logg.debug(f"could not parse code as file: {e}")
|
2021-10-12 08:39:20 +02:00
|
|
|
|
|
|
|
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
|
|
|
|
"""
|
2022-03-01 08:01:56 +01:00
|
|
|
try:
|
2021-10-12 08:39:20 +02:00
|
|
|
self.token_code = valid_hex(v)
|
|
|
|
except ValueError as e:
|
2022-03-01 08:01:56 +01:00
|
|
|
logg.debug(f"could not parse code as hex: {e}")
|
2021-10-12 08:39:20 +02:00
|
|
|
|
|
|
|
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-03-01 08:01:56 +01:00
|
|
|
code = self.token_details["code"]
|
|
|
|
if hint == "hex":
|
2021-10-12 08:39:20 +02:00
|
|
|
self.token_code = valid_hex(code)
|
2022-03-01 08:01:56 +01:00
|
|
|
|
2021-10-12 08:39:20 +02:00
|
|
|
for m in [
|
2022-03-01 08:01:56 +01:00
|
|
|
self.parse_code_as_hex,
|
|
|
|
self.parse_code_as_file,
|
|
|
|
]:
|
2021-10-12 08:39:20 +02:00
|
|
|
m(code)
|
2022-03-01 08:01:56 +01:00
|
|
|
if self.token_code is not None:
|
2021-10-12 08:39:20 +02:00
|
|
|
break
|
|
|
|
|
2022-03-01 08:01:56 +01:00
|
|
|
if self.token_code is None:
|
|
|
|
raise RuntimeError("could not successfully parse token code")
|
2021-10-12 08:39:20 +02:00
|
|
|
|
2022-03-01 08:01:56 +01:00
|
|
|
return self.token_code
|
2021-10-12 08:39:20 +02:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
2022-03-01 08:01:56 +01:00
|
|
|
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.
|
2021-11-29 13:18:28 +01:00
|
|
|
|
|
|
|
:param writer: Writer to use for publishing.
|
2022-03-01 08:01:56 +01:00
|
|
|
:type writer: cic.writers.OutputWriter
|
2021-11-29 13:18:28 +01:00
|
|
|
:rtype: tuple
|
|
|
|
:return: Token address, token symbol
|
|
|
|
"""
|
2022-03-01 08:01:56 +01:00
|
|
|
if writer is None:
|
2021-10-12 08:39:20 +02:00
|
|
|
writer = self.outputs_writer
|
|
|
|
|
2022-03-01 08:01:56 +01:00
|
|
|
tasks = []
|
|
|
|
self.token_address = self.resources["token"]["reference"]
|
|
|
|
|
2021-10-21 15:11:05 +02:00
|
|
|
# TODO: get token details when token address is not none
|
2022-03-01 08:01:56 +01:00
|
|
|
if self.token_address is None:
|
|
|
|
if self.token_details["code"] is None:
|
|
|
|
raise RuntimeError("neither token address nor token code has been set")
|
2021-10-12 08:39:20 +02:00
|
|
|
self.load_code()
|
2022-03-01 08:01:56 +01:00
|
|
|
tasks.append("token")
|
2021-10-12 08:39:20 +02:00
|
|
|
|
|
|
|
for k in self.resources.keys():
|
2022-03-01 08:01:56 +01:00
|
|
|
if k == "token":
|
2021-10-12 08:39:20 +02:00
|
|
|
continue
|
2022-03-01 08:01:56 +01:00
|
|
|
if self.resources[k]["reference"] is not None:
|
2021-10-12 08:39:20 +02:00
|
|
|
tasks.append(k)
|
2022-03-01 08:01:56 +01:00
|
|
|
|
2021-10-12 08:39:20 +02:00
|
|
|
self.prepare_extension()
|
|
|
|
|
|
|
|
for task in tasks:
|
2022-03-01 08:01:56 +01:00
|
|
|
logg.debug(f"extension adapter process {task}")
|
|
|
|
_r = getattr(self, "process_" + task)(writer=writer)
|
2021-10-12 08:39:20 +02:00
|
|
|
|
2022-03-01 08:01:56 +01:00
|
|
|
return (self.token_address, self.token_details.get("symbol"))
|