-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
268 additions
and
2 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# -*- coding: utf-8 -*- | ||
from ..exceptions import AssetDoesNotExistsException | ||
from ..asset import Asset as SyncAsset | ||
from .blockchainobject import BlockchainObject | ||
|
||
|
||
class Asset(BlockchainObject, SyncAsset): | ||
async def __init__(self, *args, **kwargs): | ||
self.define_classes() | ||
assert self.type_id | ||
|
||
self.full = kwargs.pop("full", False) | ||
await BlockchainObject.__init__(self, *args, **kwargs) | ||
|
||
async def refresh(self): | ||
""" Refresh the data from the API server | ||
""" | ||
asset = await self.blockchain.rpc.get_asset(self.identifier) | ||
if not asset: | ||
raise AssetDoesNotExistsException(self.identifier) | ||
await super(Asset, self).__init__(asset, blockchain_instance=self.blockchain) | ||
if self.full: | ||
if "bitasset_data_id" in asset: | ||
self["bitasset_data"] = await self.blockchain.rpc.get_object( | ||
asset["bitasset_data_id"] | ||
) | ||
self["dynamic_asset_data"] = await self.blockchain.rpc.get_object( | ||
asset["dynamic_asset_data_id"] | ||
) | ||
|
||
async def update_cer(self, cer, account=None, **kwargs): | ||
""" Update the Core Exchange Rate (CER) of an asset | ||
""" | ||
assert callable(self.blockchain.update_cer) | ||
return await self.blockchain.update_cer( | ||
self["symbol"], cer, account=account, **kwargs | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
# -*- coding: utf-8 -*- | ||
from asyncinit import asyncinit | ||
|
||
from ..blockchainobject import ( | ||
Caching as SyncCaching, | ||
BlockchainObjects as SyncBlockchainObjects, | ||
BlockchainObject as SyncBlockchainObject, | ||
) | ||
|
||
|
||
class Caching(SyncCaching): | ||
def __getitem__(self, key): | ||
return dict.__getitem__(self, key) | ||
|
||
async def items(self): | ||
""" This overwrites items() so that refresh() is called it the | ||
object is not already fetched | ||
""" | ||
if not self._fetched: | ||
await self.refresh() | ||
return dict.items(self) | ||
|
||
def __contains__(self, key): | ||
if not self._fetched: | ||
self.refresh() | ||
return dict.__contains__(self, key) | ||
|
||
|
||
class BlockchainObjects(Caching, list): | ||
def __init__(self, *args, **kwargs): | ||
Caching.__init__(self, *args, **kwargs) | ||
# Some lists are specific to some key value that is then provided as | ||
# first argument | ||
if len(args) > 0 and isinstance(args[0], str): | ||
key = self._cache_key(args[0]) | ||
else: | ||
key = self._cache_key() | ||
if self.incached(key): | ||
list.__init__(self, self.getfromcache(key)) | ||
else: | ||
if kwargs.get("refresh", True): | ||
self.refresh(*args, **kwargs) | ||
|
||
|
||
@asyncinit | ||
class BlockchainObject(Caching, SyncBlockchainObject): | ||
async def __init__( | ||
self, data, klass=None, lazy=False, use_cache=True, *args, **kwargs | ||
): | ||
Caching.__init__(self, *args, **kwargs) | ||
self._use_cache = use_cache | ||
if self.perform_id_tests: | ||
assert self.type_id or self.type_ids, "Need type_id or type_ids" | ||
self._fetched = False | ||
self._lazy = lazy | ||
|
||
if "_cache_expiration" in kwargs: | ||
self.set_expiration(kwargs["_cache_expiration"]) | ||
|
||
# We don't read lists, sets, or tuples | ||
if isinstance(data, (list, set, tuple)): | ||
raise ValueError( | ||
"Cannot interpret lists! Please load elements individually!" | ||
) | ||
|
||
if klass and isinstance(data, klass): | ||
self.identifier = data.get("id") | ||
dict.__init__(self, data) | ||
elif isinstance(data, dict): | ||
self.identifier = data.get("id") | ||
dict.__init__(self, data) | ||
elif isinstance(data, int): | ||
# This is only for block number bascially | ||
self.identifier = data | ||
if self.incached(str(data)): | ||
dict.__init__(self, self.getfromcache(str(data))) | ||
self._fetched = True | ||
if not self._lazy and not self._fetched: | ||
await self.refresh() | ||
# make sure to store the blocknumber for caching | ||
self["id"] = str(data) | ||
# Set identifier again as it is overwritten in super() in refresh() | ||
self.identifier = data | ||
else: | ||
self.identifier = data | ||
if self.perform_id_tests and self.test_valid_objectid(self.identifier): | ||
# Here we assume we deal with an id | ||
self.testid(self.identifier) | ||
|
||
if self.incached(data): | ||
dict.__init__(self, dict(self.getfromcache(data))) | ||
elif not self._lazy and not self._fetched: | ||
await self.refresh() | ||
|
||
if self._use_cache and not self._lazy: | ||
self._store_item() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
# -*- coding: utf-8 -*- | ||
import logging | ||
|
||
from ..chain import AbstractGrapheneChain | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
class AbstractGrapheneChain(AbstractGrapheneChain): | ||
async def info(self): | ||
""" Returns the global properties | ||
""" | ||
return await self.rpc.get_dynamic_global_properties() | ||
|
||
async def finalizeOp(self, ops, account, permission, **kwargs): | ||
""" This method obtains the required private keys if present in | ||
the wallet, finalizes the transaction, signs it and | ||
broadacasts it | ||
:param operation ops: The operation (or list of operaions) to | ||
broadcast | ||
:param operation account: The account that authorizes the | ||
operation | ||
:param string permission: The required permission for | ||
signing (active, owner, posting) | ||
:param object append_to: This allows to provide an instance of | ||
ProposalsBuilder (see :func:`new_proposal`) or | ||
TransactionBuilder (see :func:`new_tx()`) to specify | ||
where to put a specific operation. | ||
... note:: ``append_to`` is exposed to every method used in the | ||
this class | ||
... note:: | ||
If ``ops`` is a list of operation, they all need to be | ||
signable by the same key! Thus, you cannot combine ops | ||
that require active permission with ops that require | ||
posting permission. Neither can you use different | ||
accounts for different operations! | ||
... note:: This uses ``txbuffer`` as instance of | ||
:class:`transactionbuilder.TransactionBuilder`. | ||
You may want to use your own txbuffer | ||
""" | ||
if "append_to" in kwargs and kwargs["append_to"]: | ||
if self.proposer: | ||
log.warning( | ||
"You may not use append_to and self.proposer at " | ||
"the same time. Append new_proposal(..) instead" | ||
) | ||
# Append to the append_to and return | ||
append_to = kwargs["append_to"] | ||
parent = append_to.get_parent() | ||
assert isinstance( | ||
append_to, (self.transactionbuilder_class, self.proposalbuilder_class) | ||
) | ||
append_to.appendOps(ops) | ||
# Add the signer to the buffer so we sign the tx properly | ||
if isinstance(append_to, self.proposalbuilder_class): | ||
parent.appendSigner(append_to.proposer, permission) | ||
else: | ||
parent.appendSigner(account, permission) | ||
# This returns as we used append_to, it does NOT broadcast, or sign | ||
return append_to.get_parent() | ||
elif self.proposer: | ||
# Legacy proposer mode! | ||
proposal = self.proposal() | ||
proposal.set_proposer(self.proposer) | ||
proposal.set_expiration(self.proposal_expiration) | ||
proposal.set_review(self.proposal_review) | ||
proposal.appendOps(ops) | ||
# Go forward to see what the other options do ... | ||
else: | ||
# Append tot he default buffer | ||
self.txbuffer.appendOps(ops) | ||
|
||
# The API that obtains the fee only allows to specify one particular | ||
# fee asset for all operations in that transaction even though the | ||
# blockchain itself could allow to pay multiple operations with | ||
# different fee assets. | ||
if "fee_asset" in kwargs and kwargs["fee_asset"]: | ||
self.txbuffer.set_fee_asset(kwargs["fee_asset"]) | ||
|
||
# Add signing information, signer, sign and optionally broadcast | ||
if self.unsigned: | ||
# In case we don't want to sign anything | ||
self.txbuffer.addSigningInformation(account, permission) | ||
return self.txbuffer | ||
elif self.bundle: | ||
# In case we want to add more ops to the tx (bundle) | ||
self.txbuffer.appendSigner(account, permission) | ||
return self.txbuffer.json() | ||
else: | ||
# default behavior: sign + broadcast | ||
self.txbuffer.appendSigner(account, permission) | ||
self.txbuffer.sign() | ||
return await self.txbuffer.broadcast() | ||
|
||
async def broadcast(self, tx=None): | ||
""" Broadcast a transaction to the Blockchain | ||
:param tx tx: Signed transaction to broadcast | ||
""" | ||
if tx: | ||
# If tx is provided, we broadcast the tx | ||
return await self.transactionbuilder_class( | ||
tx, blockchain_instance=self | ||
).broadcast() | ||
else: | ||
return await self.txbuffer.broadcast() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# -*- coding: utf-8 -*- | ||
from ..instance import ( | ||
AbstractBlockchainInstanceProvider as SyncAbstractBlockchainInstanceProvider, | ||
) | ||
|
||
|
||
class AbstractBlockchainInstanceProvider(SyncAbstractBlockchainInstanceProvider): | ||
@classmethod | ||
def inject(slf, cls): | ||
class NewClass(slf, cls): | ||
blockchain_instance_class = slf | ||
|
||
async def __init__(self, *args, **kwargs): | ||
slf.__init__(self, *args, **kwargs) | ||
await cls.__init__(self, *args, **kwargs) | ||
|
||
NewClass.__name__ = cls.__name__ | ||
NewClass.__qualname__ = cls.__qualname__ | ||
NewClass.__doc__ = cls.__doc__ | ||
NewClass.__module__ = cls.__module__ | ||
return NewClass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters