Ejemplo n.º 1
0
    def transfer(self, entity: Entity, to: AddressLike, amount: int, fee: int):
        """
        Transfers wealth from one account to another account

        :param private_key_bin: The bytes of the private key of the source address
        :param to_address: The bytes of the targeted address to send funds to
        :param amount: The amount of funds being transfered
        :param fee: The fee associated with the transfer
        :return: The digest of the submitted transaction
        :raises: ApiError on any failures
        """
        ENDPOINT = 'transfer'

        # format the data to be closed by the transaction

        # wildcard for the moment
        shard_mask = BitVector()

        # build up the basic transaction information
        tx = Transaction()
        tx.from_address = Address(entity)
        tx.valid_until = 10000
        tx.charge_rate = 1
        tx.charge_limit = fee
        tx.add_transfer(to, amount)
        tx.add_signer(entity)

        # encode and sign the transaction
        encoded_tx = encode_transaction(tx, [entity])

        # submit the transaction
        return self._post_tx_json(encoded_tx, ENDPOINT)
Ejemplo n.º 2
0
    def test_simple_transfer(self):
        EXPECTED_DIGEST = "257c5e1de3a0a895d66d57792f1a44425336bb05bc6c8479be83b4572e1b4d45"
        EXPECTED_PAYLOAD = \
            "a1640000532398dd883d1990f7dad3fde6a53a53347afc2680a04748f7f15ad03cadc4d44235130ac5aab442e39f" \
            "9aa27118956695229212dd2f1ab5b714e9f6bd581511c1010000000000000000000000000418c2a33af8bd2cba7f" \
            "a714a840a308a217aa4483880b1ef14b4fdffe08ab956e3f4b921cec33be7c258cfd7025a2b9a942770e5b17758b" \
            "cc4961bbdc75a0251c"

        # build the payload bytes for the transaction
        with mock.patch('random.getrandbits') as mock_counter:
            mock_counter.side_effect = [0]
            payload = Transaction()
        payload.from_address = IDENTITIES[0]
        payload.add_transfer(IDENTITIES[1], 256)
        payload.add_signer(IDENTITIES[0])

        # sign the final transaction
        payload.sign(ENTITIES[0])

        transaction_bytes = transaction.encode_transaction(payload)
        self.assertIsExpectedTx(payload, transaction_bytes, EXPECTED_PAYLOAD)

        # attempt to decode a transaction from the generated bytes
        buffer = io.BytesIO(transaction_bytes)
        success, tx = transaction.decode_transaction(buffer)
        self.assertTrue(success)
        self.assertTxAreEqual(payload, tx)

        # Check payload digest
        self.assertEqual(sha256_hex(payload.encode_payload()), EXPECTED_DIGEST)
Ejemplo n.º 3
0
    def test_multiple_transfers(self):
        EXPECTED_DIGEST = "35c19ceff72218a36f9807c370e5625efc36e43ce4e9f2047f1b820162b8b3d9"
        EXPECTED_PAYLOAD = \
            "a1660000532398dd883d1990f7dad3fde6a53a53347afc2680a04748f7f15ad03cadc4d4014235130ac5aab442e3" \
            "9f9aa27118956695229212dd2f1ab5b714e9f6bd581511c1010020f478c7f74b50c187bf9a8836f382bd62977bae" \
            "eaf19625608e7e912aa60098c10200da2e9c3191e3768d1c59ea43f6318367ed9b21e6974f46a60d0dd8976740af" \
            "6dc2000186a000000000000000000000000418c2a33af8bd2cba7fa714a840a308a217aa4483880b1ef14b4fdffe" \
            "08ab956e3f4b921cec33be7c258cfd7025a2b9a942770e5b17758bcc4961bbdc75a0251c"

        # build the payload bytes for the transaction
        with mock.patch('random.getrandbits') as mock_counter:
            mock_counter.side_effect = [0]
            payload = Transaction()
        payload.from_address = IDENTITIES[0]
        payload.add_transfer(IDENTITIES[1], 256)
        payload.add_transfer(IDENTITIES[2], 512)
        payload.add_transfer(IDENTITIES[3], 100000)
        payload.add_signer(IDENTITIES[0])

        # sign the final transaction
        payload.sign(ENTITIES[0])

        transaction_bytes = transaction.encode_transaction(payload)
        self.assertIsExpectedTx(payload, transaction_bytes, EXPECTED_PAYLOAD)

        # attempt to decode a transaction from the generated bytes
        buffer = io.BytesIO(transaction_bytes)
        success, tx = transaction.decode_transaction(buffer)
        self.assertTrue(success)
        self.assertTxAreEqual(payload, tx)

        # Check payload digest
        self.assertEqual(sha256_hex(payload.encode_payload()), EXPECTED_DIGEST)
Ejemplo n.º 4
0
    def test_simple_transfer(self):
        EXPECTED_DIGEST = "7fff24ca77fbb23b9b3c460104ab1a74011bafe3965e15dc6ea6f25a4ac44392"
        EXPECTED_PAYLOAD = \
            "a1440000532398dd883d1990f7dad3fde6a53a53347afc2680a04748f7f15ad03cadc4d44235130ac5aab442e39f" \
            "9aa27118956695229212dd2f1ab5b714e9f6bd581511c1010000000000000000000000000418c2a33af8bd2cba7f" \
            "a714a840a308a217aa4483880b1ef14b4fdffe08ab956e3f4b921cec33be7c258cfd7025a2b9a942770e5b17758b" \
            "cc4961bbdc75a0251c"

        # build the payload bytes for the transaction
        with mock.patch('random.getrandbits') as mock_counter:
            mock_counter.side_effect = [0]
            payload = Transaction()
        payload.from_address = IDENTITIES[0]
        payload.add_transfer(IDENTITIES[1], 256)
        payload.add_signer(IDENTITIES[0])

        # sign the final transaction
        transaction_bytes = encode_transaction(payload, [ENTITIES[0]])

        self.assertIsExpectedTx(payload, transaction_bytes, EXPECTED_PAYLOAD)

        # attempt to decode a transaction from the generated bytes
        buffer = io.BytesIO(transaction_bytes)
        success, tx = decode_transaction(buffer)

        self.assertTrue(success)
        self.assertTxAreEqual(payload, tx)

        # Check payload digest
        buffer = io.BytesIO()
        encode_payload(buffer, payload)
        self.assertEqual(sha256_hash(buffer.getvalue()), EXPECTED_DIGEST)
Ejemplo n.º 5
0
    def test_validity_ranges(self):
        EXPECTED_PAYLOAD = "a12700532398dd883d1990f7dad3fde6a53a53347afc2680a04748f7f15ad03cadc4d4024235130ac5aab442e39f9aa27118956695229212dd2f1ab5b714e9f6bd581511c103e820f478c7f74b50c187bf9a8836f382bd62977baeeaf19625608e7e912aa60098c103e8da2e9c3191e3768d1c59ea43f6318367ed9b21e6974f46a60d0dd8976740af6dc103e8e6672a9d98da667e5dc25b2bca8acf9644a7ac0797f01cb5968abf39de011df2c103e864c0c8c103e8c2000f42400418c2a33af8bd2cba7fa714a840a308a217aa4483880b1ef14b4fdffe08ab956e3f4b921cec33be7c258cfd7025a2b9a942770e5b17758bcc4961bbdc75a0251c"

        # build the payload bytes for the transaction
        payload = Transaction()
        payload.from_address = IDENTITIES[0]
        payload.add_transfer(IDENTITIES[1], 1000)
        payload.add_transfer(IDENTITIES[2], 1000)
        payload.add_transfer(IDENTITIES[3], 1000)
        payload.add_transfer(IDENTITIES[4], 1000)
        payload.add_signer(IDENTITIES[0])
        payload.charge_rate = 1000
        payload.charge_limit = 1000000
        payload.valid_from = 100
        payload.valid_until = 200

        # sign the final transaction
        transaction_bytes = encode_transaction(payload, [ENTITIES[0]])

        self.assertIsExpectedTx(payload, transaction_bytes, EXPECTED_PAYLOAD)

        # attempt to decode a transaction from the generated bytes
        buffer = io.BytesIO(transaction_bytes)
        success, tx = decode_transaction(buffer)

        self.assertTrue(success)
        self.assertTxAreEqual(payload, tx)
Ejemplo n.º 6
0
    def test_simple_transfer(self):
        EXPECTED_PAYLOAD = "a12400532398dd883d1990f7dad3fde6a53a53347afc2680a04748f7f15ad03cadc4d44235130ac5aab442e39f9aa27118956695229212dd2f1ab5b714e9f6bd581511c101000000000418c2a33af8bd2cba7fa714a840a308a217aa4483880b1ef14b4fdffe08ab956e3f4b921cec33be7c258cfd7025a2b9a942770e5b17758bcc4961bbdc75a0251c"

        # build the payload bytes for the transaction
        payload = Transaction()
        payload.from_address = IDENTITIES[0]
        payload.add_transfer(IDENTITIES[1], 256)
        payload.add_signer(IDENTITIES[0])

        # sign the final transaction
        transaction_bytes = encode_transaction(payload, [ENTITIES[0]])

        self.assertIsExpectedTx(payload, transaction_bytes, EXPECTED_PAYLOAD)

        # attempt to decode a transaction from the generated bytes
        buffer = io.BytesIO(transaction_bytes)
        success, tx = decode_transaction(buffer)

        self.assertTrue(success)
        self.assertTxAreEqual(payload, tx)
Ejemplo n.º 7
0
    def test_validity_ranges(self):
        EXPECTED_DIGEST = "98f10e8aa0bf4507db9eca66f0ff0b6a3eff35fe4def9ed86150c7ce72e71e80"
        EXPECTED_PAYLOAD = \
            "a1470000532398dd883d1990f7dad3fde6a53a53347afc2680a04748f7f15ad03cadc4d4024235130ac5aab442e3" \
            "9f9aa27118956695229212dd2f1ab5b714e9f6bd581511c103e820f478c7f74b50c187bf9a8836f382bd62977bae" \
            "eaf19625608e7e912aa60098c103e8da2e9c3191e3768d1c59ea43f6318367ed9b21e6974f46a60d0dd8976740af" \
            "6dc103e8e6672a9d98da667e5dc25b2bca8acf9644a7ac0797f01cb5968abf39de011df2c103e864c0c8c103e8c2" \
            "000f424000000000000000000418c2a33af8bd2cba7fa714a840a308a217aa4483880b1ef14b4fdffe08ab956e3f" \
            "4b921cec33be7c258cfd7025a2b9a942770e5b17758bcc4961bbdc75a0251c"

        # build the payload bytes for the transaction
        with mock.patch('random.getrandbits') as mock_counter:
            mock_counter.side_effect = [0]
            payload = Transaction()
        payload.from_address = IDENTITIES[0]
        payload.add_transfer(IDENTITIES[1], 1000)
        payload.add_transfer(IDENTITIES[2], 1000)
        payload.add_transfer(IDENTITIES[3], 1000)
        payload.add_transfer(IDENTITIES[4], 1000)
        payload.add_signer(IDENTITIES[0])
        payload.charge_rate = 1000
        payload.charge_limit = 1000000
        payload.valid_from = 100
        payload.valid_until = 200

        # sign the final transaction
        transaction_bytes = encode_transaction(payload, [ENTITIES[0]])

        self.assertIsExpectedTx(payload, transaction_bytes, EXPECTED_PAYLOAD)

        # attempt to decode a transaction from the generated bytes
        buffer = io.BytesIO(transaction_bytes)
        success, tx = decode_transaction(buffer)

        self.assertTrue(success)
        self.assertTxAreEqual(payload, tx)

        # Check payload digest
        buffer = io.BytesIO()
        encode_payload(buffer, payload)
        self.assertEqual(sha256_hash(buffer.getvalue()), EXPECTED_DIGEST)
Ejemplo n.º 8
0
    def test_multiple_transfers(self):
        EXPECTED_PAYLOAD = "a12600532398dd883d1990f7dad3fde6a53a53347afc2680a04748f7f15ad03cadc4d4014235130ac5aab442e39f9aa27118956695229212dd2f1ab5b714e9f6bd581511c1010020f478c7f74b50c187bf9a8836f382bd62977baeeaf19625608e7e912aa60098c10200da2e9c3191e3768d1c59ea43f6318367ed9b21e6974f46a60d0dd8976740af6dc2000186a00000000418c2a33af8bd2cba7fa714a840a308a217aa4483880b1ef14b4fdffe08ab956e3f4b921cec33be7c258cfd7025a2b9a942770e5b17758bcc4961bbdc75a0251c"

        # build the payload bytes for the transaction
        payload = Transaction()
        payload.from_address = IDENTITIES[0]
        payload.add_transfer(IDENTITIES[1], 256)
        payload.add_transfer(IDENTITIES[2], 512)
        payload.add_transfer(IDENTITIES[3], 100000)
        payload.add_signer(IDENTITIES[0])

        # sign the final transaction
        transaction_bytes = encode_transaction(payload, [ENTITIES[0]])

        self.assertIsExpectedTx(payload, transaction_bytes, EXPECTED_PAYLOAD)

        # attempt to decode a transaction from the generated bytes
        buffer = io.BytesIO(transaction_bytes)
        success, tx = decode_transaction(buffer)

        self.assertTrue(success)
        self.assertTxAreEqual(payload, tx)
Ejemplo n.º 9
0
def decode_transaction(stream: io.BytesIO) -> (bool, Transaction):
    # ensure the at the magic is correctly configured
    magic = stream.read(1)[0]
    if magic != MAGIC:
        raise RuntimeError('Unable to parse transaction from stream, invalid magic')

    # extract the header bytes
    header = stream.read(2)

    # parse the header types
    version = (header[0] & 0xE0) >> 5
    charge_unit_flag = bool((header[0] & 0x08) >> 3)
    transfer_flag = bool((header[0] & 0x04) >> 2)
    multiple_transfers_flag = bool((header[0] & 0x02) >> 1)
    valid_from_flag = bool((header[0] & 0x01))

    contract_type = (header[1] & 0xC0) >> 6
    signature_count_minus1 = (header[1] & 0x3F)

    num_signatures = signature_count_minus1 + 1

    # ensure that the version is correct
    if version != VERSION:
        raise RuntimeError('Unable to parse transaction from stream, incompatible version')

    # Ready empty reserved byte
    stream.read(1)

    tx = Transaction()

    # decode the address from the thread
    tx.from_address = address.decode(stream)

    if transfer_flag:

        # determine the number of transfers that are present in the transaction
        if multiple_transfers_flag:
            transfer_count = integer.decode(stream) + 2
        else:
            transfer_count = 1

        for n in range(transfer_count):
            to = address.decode(stream)
            amount = integer.decode(stream)

            tx.add_transfer(to, amount)

    if valid_from_flag:
        tx.valid_from = integer.decode(stream)

    tx.valid_until = integer.decode(stream)
    tx.charge_rate = integer.decode(stream)

    assert not charge_unit_flag, "Currently the charge unit field is not supported"

    tx.charge_limit = integer.decode(stream)

    if contract_type != NO_CONTRACT:
        contract_header = int(stream.read(1)[0])

        wildcard = bool(contract_header & 0x80)

        shard_mask = BitVector()
        if not wildcard:
            extended_shard_mask_flag = bool(contract_header & 0x40)

            if not extended_shard_mask_flag:

                if contract_header & 0x10:
                    mask = 0xf
                    bit_size = 4
                else:
                    mask = 0x3
                    bit_size = 2

                # extract the shard mask from the header
                shard_mask = BitVector.from_bytes(bytes([contract_header & mask]), bit_size)

            else:
                bit_length = 1 << ((contract_header & 0x3F) + 3)
                byte_length = bit_length // 8

                assert (bit_length % 8) == 0  # this should be enforced as part of the spec

                # extract the mask from the next N bytes
                shard_mask = BitVector.from_bytes(stream.read(byte_length), bit_length)

        if contract_type == SMART_CONTRACT or contract_type == SYNERGETIC:
            contract_digest = address.decode(stream)
            contract_address = address.decode(stream)

            tx.target_contract(contract_digest, contract_address, shard_mask)

        elif contract_type == CHAIN_CODE:
            encoded_chain_code_name = bytearray.decode(stream)

            tx.target_chain_code(encoded_chain_code_name.decode('ascii'), shard_mask)

        else:
            # this is mostly a guard against a desync between this function and `_map_contract_mode`
            raise RuntimeError("Unhandled contract type")

        tx.action = bytearray.decode(stream).decode('ascii')
        tx.data = bytearray.decode(stream)

    # Read counter value
    tx.counter = struct.unpack('<Q', stream.read(8))[0]

    if signature_count_minus1 == 0x3F:
        additional_signatures = integer.decode(stream)
        num_signatures += additional_signatures

    # extract all the signing public keys from the stream
    public_keys = [identity.decode(stream) for _ in range(num_signatures)]

    # extract full copy of the payload
    payload_bytes = stream.getvalue()[:stream.tell()]

    verified = []
    for ident in public_keys:
        # for n in range(num_signatures):

        # extract the signature from the stream
        signature = bytearray.decode(stream)

        # verify if this signature is correct
        verified.append(ident.verify(payload_bytes, signature))

        # build a metadata object to store in the tx
        tx._signers[ident] = {
            'signature': signature,
            'verified': verified[-1],
        }

    return all(verified), tx