Added Create and Transfer as separate transactions

This commit is contained in:
Sangat Das 2022-03-09 17:12:36 +00:00
parent 64f5ec01f1
commit a421533e16
7 changed files with 181 additions and 14 deletions

View File

@ -25,13 +25,13 @@ try:
except ImportError:
from sha3 import sha3_256
from planetmint.common.crypto import PrivateKey, hash_data
from planetmint.common.exceptions import (KeypairMismatchException,
from planetmint.transactions.common.crypto import PrivateKey, hash_data
from planetmint.transactions.common.exceptions import (KeypairMismatchException,
InputDoesNotExist, DoubleSpend,
InvalidHash, InvalidSignature,
AmountError, AssetIdMismatch,
ThresholdTooDeep)
from planetmint.common.utils import serialize
from planetmint.transactions.common.utils import serialize
from .memoize import memoize_from_dict, memoize_to_dict

View File

@ -0,0 +1,78 @@
# Copyright © 2020 Interplanetary Database Association e.V.,
# Planetmint and IPDB software contributors.
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0
from planetmint.transactions.common.transaction import (Transaction, Input, Output)
class Create(Transaction):
OPERATION = 'CREATE'
# NOTE: This class inherits TRANSFER txn type. The `TRANSFER` property is
# overriden to re-use methods from parent class
TRANSFER = OPERATION
ALLOWED_OPERATIONS = (OPERATION,)
@classmethod
def validate(self, tx_signers, recipients, asset, metadata):
if not isinstance(tx_signers, list):
raise TypeError('`tx_signers` must be a list instance')
if not isinstance(recipients, list):
raise TypeError('`recipients` must be a list instance')
if len(tx_signers) == 0:
raise ValueError('`tx_signers` list cannot be empty')
if len(recipients) == 0:
raise ValueError('`recipients` list cannot be empty')
if not (asset is None or isinstance(asset, dict)):
raise TypeError('`asset` must be a dict or None')
if not (metadata is None or isinstance(metadata, dict)):
raise TypeError('`metadata` must be a dict or None')
inputs = []
outputs = []
# generate_outputs
for recipient in recipients:
if not isinstance(recipient, tuple) or len(recipient) != 2:
raise ValueError(('Each `recipient` in the list must be a'
' tuple of `([<list of public keys>],'
' <amount>)`'))
pub_keys, amount = recipient
outputs.append(Output.generate(pub_keys, amount))
# generate inputs
inputs.append(Input.generate(tx_signers))
return (inputs, outputs)
@classmethod
def generate(cls, tx_signers, recipients, metadata=None, asset=None):
"""A simple way to generate a `CREATE` transaction.
Note:
This method currently supports the following Cryptoconditions
use cases:
- Ed25519
- ThresholdSha256
Additionally, it provides support for the following Planetmint
use cases:
- Multiple inputs and outputs.
Args:
tx_signers (:obj:`list` of :obj:`str`): A list of keys that
represent the signers of the CREATE Transaction.
recipients (:obj:`list` of :obj:`tuple`): A list of
([keys],amount) that represent the recipients of this
Transaction.
metadata (dict): The metadata to be stored along with the
Transaction.
asset (dict): The metadata associated with the asset that will
be created in this Transaction.
Returns:
:class:`~planetmint.common.transaction.Transaction`
"""
(inputs, outputs) = cls.validate(tx_signers, recipients, asset, metadata)
return cls(cls.OPERATION, {'data': asset}, inputs, outputs, metadata)

View File

@ -0,0 +1,85 @@
# Copyright © 2020 Interplanetary Database Association e.V.,
# Planetmint and IPDB software contributors.
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0
from planetmint.transactions.common.transaction import (Transaction, Input, Output)
from planetmint.transactions.common.schema import (_validate_schema,
TX_SCHEMA_COMMON,
TX_SCHEMA_TRANSFER)
from copy import deepcopy
class Transfer(Transaction):
OPERATION = 'TRANSFER'
# NOTE: This class inherits TRANSFER txn type. The `TRANSFER` property is
# overriden to re-use methods from parent class
TRANSFER = OPERATION
ALLOWED_OPERATIONS = (OPERATION,)
@classmethod
def validate(cls, inputs, recipients, asset_id, metadata):
if not isinstance(inputs, list):
raise TypeError('`inputs` must be a list instance')
if len(inputs) == 0:
raise ValueError('`inputs` must contain at least one item')
if not isinstance(recipients, list):
raise TypeError('`recipients` must be a list instance')
if len(recipients) == 0:
raise ValueError('`recipients` list cannot be empty')
outputs = []
for recipient in recipients:
if not isinstance(recipient, tuple) or len(recipient) != 2:
raise ValueError(('Each `recipient` in the list must be a'
' tuple of `([<list of public keys>],'
' <amount>)`'))
pub_keys, amount = recipient
outputs.append(Output.generate(pub_keys, amount))
if not isinstance(asset_id, str):
raise TypeError('`asset_id` must be a string')
return (deepcopy(inputs), outputs)
@classmethod
def generate(cls, inputs, recipients, asset_id, metadata=None):
"""A simple way to generate a `TRANSFER` transaction.
Note:
Different cases for threshold conditions:
Combining multiple `inputs` with an arbitrary number of
`recipients` can yield interesting cases for the creation of
threshold conditions we'd like to support. The following
notation is proposed:
1. The index of a `recipient` corresponds to the index of
an input:
e.g. `transfer([input1], [a])`, means `input1` would now be
owned by user `a`.
2. `recipients` can (almost) get arbitrary deeply nested,
creating various complex threshold conditions:
e.g. `transfer([inp1, inp2], [[a, [b, c]], d])`, means
`a`'s signature would have a 50% weight on `inp1`
compared to `b` and `c` that share 25% of the leftover
weight respectively. `inp2` is owned completely by `d`.
Args:
inputs (:obj:`list` of :class:`~planetmint.common.transaction.
Input`): Converted `Output`s, intended to
be used as inputs in the transfer to generate.
recipients (:obj:`list` of :obj:`tuple`): A list of
([keys],amount) that represent the recipients of this
Transaction.
asset_id (str): The asset ID of the asset to be transferred in
this Transaction.
metadata (dict): Python dictionary to be stored along with the
Transaction.
Returns:
:class:`~planetmint.common.transaction.Transaction`
"""
(inputs, outputs) = cls.validate_transfer(inputs, recipients, asset_id, metadata)
return cls(cls.OPERATION, {'id': asset_id}, inputs, outputs, metadata)

View File

@ -8,16 +8,18 @@ import base58
from uuid import uuid4
from planetmint import backend
from planetmint.elections.vote import Vote
from planetmint.common.exceptions import (InvalidSignature,
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
from planetmint.transactions.types.elections.vote import Vote
from planetmint.transactions.common.exceptions import (InvalidSignature,
MultipleInputsError,
InvalidProposer,
UnequalValidatorSet,
DuplicateTransaction)
from planetmint.tendermint_utils import key_from_base64, public_key_to_base64
from planetmint.common.crypto import (public_key_from_ed25519_key)
from planetmint.common.transaction import Transaction
from planetmint.common.schema import (_validate_schema,
from planetmint.transactions.common.crypto import (public_key_from_ed25519_key)
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.schema import (_validate_schema,
TX_SCHEMA_COMMON,
TX_SCHEMA_CREATE)
@ -161,11 +163,11 @@ class Election(Transaction):
@classmethod
def create(cls, tx_signers, recipients, metadata=None, asset=None):
raise NotImplementedError
Create.generate(tx_signers, recipients, metadata=None, asset=None)
@classmethod
def transfer(cls, tx_signers, recipients, metadata=None, asset=None):
raise NotImplementedError
Transfer.generate(tx_signers, recipients, metadata=None, asset=None)
@classmethod
def to_public_key(cls, election_id):

View File

@ -3,8 +3,10 @@
# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0)
# Code is Apache-2.0 and docs are CC-BY-4.0
from planetmint.common.transaction import Transaction
from planetmint.common.schema import (_validate_schema,
from planetmint.transactions.types.assets.create import Create
from planetmint.transactions.types.assets.transfer import Transfer
from planetmint.transactions.common.transaction import Transaction
from planetmint.transactions.common.schema import (_validate_schema,
TX_SCHEMA_COMMON,
TX_SCHEMA_TRANSFER,
TX_SCHEMA_VOTE)
@ -57,8 +59,8 @@ class Vote(Transaction):
@classmethod
def create(cls, tx_signers, recipients, metadata=None, asset=None):
raise NotImplementedError
return Create.generate(tx_signers, recipients, metadata=None, asset=None)
@classmethod
def transfer(cls, tx_signers, recipients, metadata=None, asset=None):
raise NotImplementedError
return Transfer.generate(tx_signers, recipients, metadata=None, asset=None)