diff --git a/bigchaindb/common/schema/transaction.yaml b/bigchaindb/common/schema/transaction.yaml
index 86e5947b..f63b652e 100644
--- a/bigchaindb/common/schema/transaction.yaml
+++ b/bigchaindb/common/schema/transaction.yaml
@@ -132,7 +132,8 @@ definitions:
     - public_keys
     properties:
       amount:
-        type: integer
+        type: string
+        pattern: "^[0-9]{1,20}$"
         description: |
             Integral amount of the asset represented by this output.
             In the case of a non divisible asset, this will always be 1.
@@ -158,10 +159,6 @@ definitions:
         "$ref": "#/definitions/public_keys"
         description: |
             List of public keys associated with the conditions on an output.
-      amount:
-        type: integer
-        description: |
-            Integral amount of the asset represented by this condition.
   input:
     type: "object"
     description:
diff --git a/bigchaindb/common/transaction.py b/bigchaindb/common/transaction.py
index d0b24dbb..6e271d60 100644
--- a/bigchaindb/common/transaction.py
+++ b/bigchaindb/common/transaction.py
@@ -209,6 +209,8 @@ class Output(object):
                 owners before a Transaction was confirmed.
     """
 
+    MAX_AMOUNT = 9 * 10 ** 18
+
     def __init__(self, fulfillment, public_keys=None, amount=1):
         """Create an instance of a :class:`~.Output`.
 
@@ -229,6 +231,8 @@ class Output(object):
             raise TypeError('`amount` must be an int')
         if amount < 1:
             raise AmountError('`amount` must be greater than 0')
+        if amount > self.MAX_AMOUNT:
+            raise AmountError('`amount` must be <= %s' % self.MAX_AMOUNT)
 
         self.fulfillment = fulfillment
         self.amount = amount
@@ -264,7 +268,7 @@ class Output(object):
         output = {
             'public_keys': self.public_keys,
             'condition': condition,
-            'amount': self.amount
+            'amount': str(self.amount),
         }
         return output
 
@@ -381,7 +385,11 @@ class Output(object):
         except KeyError:
             # NOTE: Hashlock condition case
             fulfillment = data['condition']['uri']
-        return cls(fulfillment, data['public_keys'], data['amount'])
+        try:
+            amount = int(data['amount'])
+        except ValueError:
+            raise AmountError('Invalid amount: %s' % data['amount'])
+        return cls(fulfillment, data['public_keys'], amount)
 
 
 class Transaction(object):
diff --git a/tests/assets/test_digital_assets.py b/tests/assets/test_digital_assets.py
index c31ec3da..1e63dbec 100644
--- a/tests/assets/test_digital_assets.py
+++ b/tests/assets/test_digital_assets.py
@@ -1,4 +1,3 @@
-from bigchaindb.common.exceptions import ValidationError
 import pytest
 import random
 
@@ -19,18 +18,6 @@ def test_asset_transfer(b, user_pk, user_sk):
     assert tx_transfer_signed.asset['id'] == tx_create.id
 
 
-def test_validate_bad_asset_creation(b, user_pk):
-    from bigchaindb.models import Transaction
-
-    # `data` needs to be a dictionary
-    tx = Transaction.create([b.me], [([user_pk], 1)])
-    tx.asset['data'] = 'a'
-    tx_signed = tx.sign([b.me_private])
-
-    with pytest.raises(ValidationError):
-        Transaction.from_dict(tx_signed.to_dict())
-
-
 @pytest.mark.bdb
 @pytest.mark.usefixtures('inputs')
 def test_validate_transfer_asset_id_mismatch(b, user_pk, user_sk):
@@ -91,19 +78,6 @@ def test_asset_id_mismatch(b, user_pk):
         Transaction.get_asset_id([tx1, tx2])
 
 
-def test_create_invalid_divisible_asset(b, user_pk, user_sk):
-    from bigchaindb.models import Transaction
-    from bigchaindb.common.exceptions import ValidationError
-
-    # Asset amount must be more than 0
-    tx = Transaction.create([user_pk], [([user_pk], 1)])
-    tx.outputs[0].amount = 0
-    tx.sign([user_sk])
-
-    with pytest.raises(ValidationError):
-        Transaction.from_dict(tx.to_dict())
-
-
 def test_create_valid_divisible_asset(b, user_pk, user_sk):
     from bigchaindb.models import Transaction
 
diff --git a/tests/assets/test_divisible_assets.py b/tests/assets/test_divisible_assets.py
index 87a29c2b..e1ea726f 100644
--- a/tests/assets/test_divisible_assets.py
+++ b/tests/assets/test_divisible_assets.py
@@ -635,88 +635,3 @@ def test_divide(b, user_pk, user_sk):
     assert len(tx_transfer_signed.outputs) == 3
     for output in tx_transfer_signed.outputs:
         assert output.amount == 1
-
-
-# Check that negative inputs are caught when creating a TRANSFER transaction
-@pytest.mark.skip(reason='part of tx structural tests')
-@pytest.mark.bdb
-@pytest.mark.usefixtures('inputs')
-def test_non_positive_amounts_on_transfer(b, user_pk):
-    from bigchaindb.models import Transaction
-    from bigchaindb.common.exceptions import AmountError
-
-    # CREATE divisible asset with 1 output with amount 3
-    tx_create = Transaction.create([b.me], [([user_pk], 3)])
-    tx_create_signed = tx_create.sign([b.me_private])
-    # create block
-    block = b.create_block([tx_create_signed])
-    assert block.validate(b) == block
-    b.write_block(block)
-    # vote
-    vote = b.vote(block.id, b.get_last_voted_block().id, True)
-    b.write_vote(vote)
-
-    with pytest.raises(AmountError):
-        Transaction.transfer(tx_create.to_inputs(),
-                             [([b.me], 4), ([b.me], -1)],
-                             asset_id=tx_create.id)
-
-
-# Check that negative inputs are caught when validating a TRANSFER transaction
-@pytest.mark.skip(reason='part of tx structural tests')
-@pytest.mark.bdb
-@pytest.mark.usefixtures('inputs')
-def test_non_positive_amounts_on_transfer_validate(b, user_pk, user_sk):
-    from bigchaindb.models import Transaction
-    from bigchaindb.common.exceptions import AmountError
-
-    # CREATE divisible asset with 1 output with amount 3
-    tx_create = Transaction.create([b.me], [([user_pk], 3)])
-    tx_create_signed = tx_create.sign([b.me_private])
-    # create block
-    block = b.create_block([tx_create_signed])
-    assert block.validate(b) == block
-    b.write_block(block)
-    # vote
-    vote = b.vote(block.id, b.get_last_voted_block().id, True)
-    b.write_vote(vote)
-
-    # create a transfer transaction with 3 outputs and check if the amount
-    # of each output is 1
-    tx_transfer = Transaction.transfer(tx_create.to_inputs(),
-                                       [([b.me], 4), ([b.me], 1)],
-                                       asset_id=tx_create.id)
-    tx_transfer.outputs[1].amount = -1
-    tx_transfer_signed = tx_transfer.sign([user_sk])
-
-    with pytest.raises(AmountError):
-        tx_transfer_signed.validate(b)
-
-
-# Check that negative inputs are caught when creating a CREATE transaction
-@pytest.mark.bdb
-@pytest.mark.usefixtures('inputs')
-def test_non_positive_amounts_on_create(b, user_pk):
-    from bigchaindb.models import Transaction
-    from bigchaindb.common.exceptions import AmountError
-
-    # CREATE divisible asset with 1 output with amount 3
-    with pytest.raises(AmountError):
-        Transaction.create([b.me], [([user_pk], -3)])
-
-
-# Check that negative inputs are caught when validating a CREATE transaction
-@pytest.mark.skip(reason='part of tx structural tests')
-@pytest.mark.bdb
-@pytest.mark.usefixtures('inputs')
-def test_non_positive_amounts_on_create_validate(b, user_pk):
-    from bigchaindb.models import Transaction
-    from bigchaindb.common.exceptions import AmountError
-
-    # CREATE divisible asset with 1 output with amount 3
-    tx_create = Transaction.create([b.me], [([user_pk], 3)])
-    tx_create.outputs[0].amount = -3
-    tx_create_signed = tx_create.sign([b.me_private])
-
-    with pytest.raises(AmountError):
-        tx_create_signed.validate(b)
diff --git a/tests/common/schema/test_transaction_schema.py b/tests/common/schema/test_transaction_schema.py
deleted file mode 100644
index dca10e70..00000000
--- a/tests/common/schema/test_transaction_schema.py
+++ /dev/null
@@ -1,60 +0,0 @@
-from pytest import raises
-
-from bigchaindb.common.exceptions import SchemaValidationError
-from bigchaindb.common.schema import validate_transaction_schema
-
-
-def test_validate_transaction_create(create_tx):
-    validate_transaction_schema(create_tx.to_dict())
-
-
-def test_validate_transaction_signed_create(signed_create_tx):
-    validate_transaction_schema(signed_create_tx.to_dict())
-
-
-def test_validate_transaction_signed_transfer(signed_transfer_tx):
-    validate_transaction_schema(signed_transfer_tx.to_dict())
-
-
-def test_validate_transaction_fails():
-    with raises(SchemaValidationError):
-        validate_transaction_schema({})
-
-
-def test_validate_fails_metadata_empty_dict(create_tx):
-    create_tx.metadata = {'a': 1}
-    validate_transaction_schema(create_tx.to_dict())
-    create_tx.metadata = None
-    validate_transaction_schema(create_tx.to_dict())
-    create_tx.metadata = {}
-    with raises(SchemaValidationError):
-        validate_transaction_schema(create_tx.to_dict())
-
-
-def test_transfer_asset_schema(signed_transfer_tx):
-    tx = signed_transfer_tx.to_dict()
-    validate_transaction_schema(tx)
-    tx['asset']['data'] = {}
-    with raises(SchemaValidationError):
-        validate_transaction_schema(tx)
-    del tx['asset']['data']
-    tx['asset']['id'] = 'b' * 63
-    with raises(SchemaValidationError):
-        validate_transaction_schema(tx)
-
-
-def test_create_single_input(create_tx):
-    tx = create_tx.to_dict()
-    tx['inputs'] += tx['inputs']
-    with raises(SchemaValidationError):
-        validate_transaction_schema(tx)
-    tx['inputs'] = []
-    with raises(SchemaValidationError):
-        validate_transaction_schema(tx)
-
-
-def test_create_tx_no_fulfills(create_tx):
-    tx = create_tx.to_dict()
-    tx['inputs'][0]['fulfills'] = {'tx': 'a' * 64, 'output': 0}
-    with raises(SchemaValidationError):
-        validate_transaction_schema(tx)
diff --git a/tests/common/schema/test_vote_schema.py b/tests/common/schema/test_vote_schema.py
deleted file mode 100644
index a9de9492..00000000
--- a/tests/common/schema/test_vote_schema.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from pytest import raises
-
-from bigchaindb.common.exceptions import SchemaValidationError
-from bigchaindb.common.schema import validate_vote_schema
-
-
-def test_validate_vote(structurally_valid_vote):
-    validate_vote_schema(structurally_valid_vote)
-
-
-def test_validate_vote_fails():
-    with raises(SchemaValidationError):
-        validate_vote_schema({})
diff --git a/tests/common/schema/test_schema.py b/tests/common/test_schema.py
similarity index 58%
rename from tests/common/schema/test_schema.py
rename to tests/common/test_schema.py
index 3116fa7d..1db17170 100644
--- a/tests/common/schema/test_schema.py
+++ b/tests/common/test_schema.py
@@ -1,6 +1,18 @@
-from bigchaindb.common.schema import (
-    TX_SCHEMA_COMMON, VOTE_SCHEMA, drop_schema_descriptions)
+"""
+This module is tests related to schema checking, but _not_ of granular schematic
+properties related to validation.
+"""
 
+from pytest import raises
+
+from bigchaindb.common.exceptions import SchemaValidationError
+from bigchaindb.common.schema import (
+    TX_SCHEMA_COMMON, VOTE_SCHEMA, drop_schema_descriptions,
+    validate_transaction_schema, validate_vote_schema)
+
+
+################################################################################
+# Test of schema utils
 
 def _test_additionalproperties(node, path=''):
     """
@@ -67,3 +79,37 @@ def test_drop_descriptions():
     }
     drop_schema_descriptions(node)
     assert node == expected
+
+
+################################################################################
+# Test call transaction schema
+
+
+def test_validate_transaction_create(create_tx):
+    validate_transaction_schema(create_tx.to_dict())
+
+
+def test_validate_transaction_signed_create(signed_create_tx):
+    validate_transaction_schema(signed_create_tx.to_dict())
+
+
+def test_validate_transaction_signed_transfer(signed_transfer_tx):
+    validate_transaction_schema(signed_transfer_tx.to_dict())
+
+
+def test_validate_transaction_fails():
+    with raises(SchemaValidationError):
+        validate_transaction_schema({})
+
+
+################################################################################
+# Test call vote schema
+
+
+def test_validate_vote(structurally_valid_vote):
+    validate_vote_schema(structurally_valid_vote)
+
+
+def test_validate_vote_fails():
+    with raises(SchemaValidationError):
+        validate_vote_schema({})
diff --git a/tests/common/test_transaction.py b/tests/common/test_transaction.py
index 205009ac..fc2a437e 100644
--- a/tests/common/test_transaction.py
+++ b/tests/common/test_transaction.py
@@ -1,3 +1,8 @@
+"""
+These are tests of the API of the Transaction class and associated classes.
+Tests for transaction validation are separate.
+"""
+
 from pytest import raises
 
 
@@ -78,7 +83,7 @@ def test_output_serialization(user_Ed25519, user_pub):
             'details': user_Ed25519.to_dict(),
         },
         'public_keys': [user_pub],
-        'amount': 1,
+        'amount': '1',
     }
 
     cond = Output(user_Ed25519, [user_pub], 1)
@@ -96,7 +101,7 @@ def test_output_deserialization(user_Ed25519, user_pub):
             'details': user_Ed25519.to_dict()
         },
         'public_keys': [user_pub],
-        'amount': 1,
+        'amount': '1',
     }
     cond = Output.from_dict(cond)
 
@@ -115,7 +120,7 @@ def test_output_hashlock_serialization():
             'uri': hashlock,
         },
         'public_keys': None,
-        'amount': 1,
+        'amount': '1',
     }
     cond = Output(hashlock, amount=1)
 
@@ -135,7 +140,7 @@ def test_output_hashlock_deserialization():
             'uri': hashlock
         },
         'public_keys': None,
-        'amount': 1,
+        'amount': '1',
     }
     cond = Output.from_dict(cond)
 
@@ -341,28 +346,6 @@ def test_transaction_deserialization(user_input, user_output, data):
     validate_transaction_model(tx)
 
 
-def test_tx_serialization_with_incorrect_hash(utx):
-    from bigchaindb.common.transaction import Transaction
-    from bigchaindb.common.exceptions import InvalidHash
-
-    utx_dict = utx.to_dict()
-    utx_dict['id'] = 'a' * 64
-    with raises(InvalidHash):
-        Transaction.from_dict(utx_dict)
-    utx_dict.pop('id')
-
-
-def test_tx_serialization_hash_function(tx):
-    import sha3
-    import json
-    tx_dict = tx.to_dict()
-    tx_dict['inputs'][0]['fulfillment'] = None
-    del tx_dict['id']
-    payload = json.dumps(tx_dict, skipkeys=False, sort_keys=True,
-                         separators=(',', ':'))
-    assert sha3.sha3_256(payload.encode()).hexdigest() == tx.id
-
-
 def test_invalid_input_initialization(user_input, user_pub):
     from bigchaindb.common.transaction import Input
 
@@ -984,35 +967,11 @@ def test_cant_add_empty_input():
         tx.add_input(None)
 
 
-def test_validate_version(utx):
-    import re
-    import bigchaindb.version
-    from .utils import validate_transaction_model
-    from bigchaindb.common.exceptions import SchemaValidationError
+def test_output_from_dict_invalid_amount(user_output):
+    from bigchaindb.common.transaction import Output
+    from bigchaindb.common.exceptions import AmountError
 
-    short_ver = bigchaindb.version.__short_version__
-    assert utx.version == re.match(r'^(.*\d)', short_ver).group(1)
-
-    validate_transaction_model(utx)
-
-    # At version 1, transaction version will break step with server version.
-    utx.version = '1.0.0'
-    with raises(SchemaValidationError):
-        validate_transaction_model(utx)
-
-
-def test_create_tx_no_asset_id(b, utx):
-    from bigchaindb.common.exceptions import SchemaValidationError
-    from .utils import validate_transaction_model
-    utx.asset['id'] = 'b' * 64
-    with raises(SchemaValidationError):
-        validate_transaction_model(utx)
-
-
-def test_transfer_tx_asset_schema(transfer_utx):
-    from bigchaindb.common.exceptions import SchemaValidationError
-    from .utils import validate_transaction_model
-    tx = transfer_utx
-    tx.asset['data'] = {}
-    with raises(SchemaValidationError):
-        validate_transaction_model(tx)
+    out = user_output.to_dict()
+    out['amount'] = 'a'
+    with raises(AmountError):
+        Output.from_dict(out)
diff --git a/tests/test_models.py b/tests/test_block_model.py
similarity index 100%
rename from tests/test_models.py
rename to tests/test_block_model.py
diff --git a/tests/validation/test_transaction_structure.py b/tests/validation/test_transaction_structure.py
new file mode 100644
index 00000000..9edfd367
--- /dev/null
+++ b/tests/validation/test_transaction_structure.py
@@ -0,0 +1,159 @@
+"""
+All tests of transaction structure. The concern here is that transaction
+structural / schematic issues are caught when reading a transaction
+(ie going from dict -> transaction).
+"""
+
+import pytest
+
+from bigchaindb.common.exceptions import (AmountError, InvalidHash,
+                                          SchemaValidationError)
+from bigchaindb.models import Transaction
+
+
+################################################################################
+# Helper functions
+
+
+def validate(tx):
+    if isinstance(tx, Transaction):
+        tx = tx.to_dict()
+    Transaction.from_dict(tx)
+
+
+def validate_raises(tx, exc=SchemaValidationError):
+    with pytest.raises(exc):
+        validate(tx)
+
+
+# We should test that validation works when we expect it to
+def test_validation_passes(create_tx):
+    validate(create_tx)
+
+
+################################################################################
+# ID
+
+
+def test_tx_serialization_hash_function(create_tx):
+    import sha3
+    import json
+    tx = create_tx.to_dict()
+    tx['inputs'][0]['fulfillment'] = None
+    del tx['id']
+    payload = json.dumps(tx, skipkeys=False, sort_keys=True,
+                         separators=(',', ':'))
+    assert sha3.sha3_256(payload.encode()).hexdigest() == create_tx.id
+
+
+def test_tx_serialization_with_incorrect_hash(create_tx):
+    tx = create_tx.to_dict()
+    tx['id'] = 'a' * 64
+    validate_raises(tx, InvalidHash)
+
+
+################################################################################
+# Operation
+
+def test_validate_invalid_operation(create_tx):
+    create_tx.operation = 'something invalid'
+    validate_raises(create_tx)
+
+
+################################################################################
+# Metadata
+
+def test_validate_fails_metadata_empty_dict(create_tx):
+    create_tx.metadata = {'a': 1}
+    validate(create_tx)
+    create_tx.metadata = None
+    validate(create_tx)
+    create_tx.metadata = {}
+    validate_raises(create_tx)
+
+
+################################################################################
+# Asset
+
+def test_transfer_asset_schema(signed_transfer_tx):
+    tx = signed_transfer_tx.to_dict()
+    validate(tx)
+    tx['asset']['data'] = {}
+    validate_raises(tx)
+    del tx['asset']['data']
+    tx['asset']['id'] = 'b' * 63
+    validate_raises(tx)
+
+
+def test_create_tx_no_asset_id(create_tx):
+    create_tx.asset['id'] = 'b' * 64
+    validate_raises(create_tx)
+
+
+def test_create_tx_asset_type(create_tx):
+    create_tx.asset['data'] = 'a'
+    validate_raises(create_tx)
+
+
+################################################################################
+# Inputs
+
+def test_no_inputs(create_tx):
+    create_tx.inputs = []
+    validate_raises(create_tx)
+
+
+def test_create_single_input(create_tx):
+    tx = create_tx.to_dict()
+    tx['inputs'] += tx['inputs']
+    validate_raises(tx)
+    tx['inputs'] = []
+    validate_raises(tx)
+
+
+def test_create_tx_no_fulfills(create_tx):
+    tx = create_tx.to_dict()
+    tx['inputs'][0]['fulfills'] = {'tx': 'a' * 64, 'output': 0}
+    validate_raises(tx)
+
+
+################################################################################
+# Outputs
+
+
+def test_low_amounts(create_tx, signed_transfer_tx):
+    for tx in [create_tx, signed_transfer_tx]:
+        tx.outputs[0].amount = 0
+        validate_raises(tx, AmountError)
+        tx.outputs[0].amount = -1
+        validate_raises(tx)
+
+
+def test_high_amounts(create_tx):
+    # Should raise a SchemaValidationError - don't want to allow ridiculously
+    # large numbers to get converted to int
+    create_tx.outputs[0].amount = 10 ** 21
+    validate_raises(create_tx)
+    # Should raise AmountError
+    create_tx.outputs[0].amount = 9 * 10 ** 18 + 1
+    validate_raises(create_tx, AmountError)
+    # Should pass
+    create_tx.outputs[0].amount -= 1
+    validate(create_tx)
+
+
+################################################################################
+# Version
+
+def test_validate_version(create_tx):
+    import re
+    import bigchaindb.version
+
+    short_ver = bigchaindb.version.__short_version__
+    assert create_tx.version == re.match(r'^(.*\d)', short_ver).group(1)
+
+    validate(create_tx)
+
+    # At version 1, transaction version will break step with server version.
+    create_tx.version = '1.0.0'
+    validate_raises(create_tx)