diff --git a/README.md b/README.md
index 11b8208..9fdc477 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,7 @@ The compiler supports a subset of the Python language ( in the same way that a _
#### Get Help or give help
- Open a new [issue](https://github.com/CityOfZion/neo-boa/issues/new) if you encounter a problem.
-- Or ping **@localhuman** on the [NEO Slack](https://join.slack.com/t/neoblockchainteam/shared_invite/MjE3ODMxNDUzMDE1LTE1MDA4OTY3NDQtNTMwM2MyMTc2NA).
+- Or ping **@localhuman** on the [NEO official community chatroom](https://discord.gg/R8v48YA).
- Pull requests welcome. New features, writing tests and documentation are all needed.
@@ -124,7 +124,7 @@ Tests are important.
## License
-- Open-source [MIT](https://github.com/CityOfZion/neo-python/blob/master/LICENSE.md).
+- Open-source [MIT](LICENSE.md).
- Main author is [@localhuman](https://github.com/localhuman).
diff --git a/README.rst b/README.rst
index 5cbc427..fa1c87a 100644
--- a/README.rst
+++ b/README.rst
@@ -3,24 +3,24 @@
Python compiler for the Neo Virtual Machine
===========================================
-
-- `Overview`_
-- `Installation`_
-- `Usage`_
-- `License`_
-- `Tests`_
-- `Donatitons`_
-
Overview
--------
-A Python compiler for the Neo Virtual Machine
+The ``neo-boa`` compiler is a tool for compiling Python files to the
+``.avm`` format for usage in the `Neo Virtual
+Machine `__ which is used to
+execute contracts on the `Neo
+Blockchain `__.
+
+The compiler supports a subset of the Python language ( in the same way
+that a *boa constrictor* is a subset of the Python snake species)
What does it currently do
^^^^^^^^^^^^^^^^^^^^^^^^^
- Compiles a subset of the Python language to the ``.avm`` format for
- use in the `Neo Virtual Machine`_
+ use in the `Neo Virtual
+ Machine `__
- Works for Python 3.4 and 3.5
What will it do
@@ -32,8 +32,11 @@ What will it do
Get Help or give help
^^^^^^^^^^^^^^^^^^^^^
-- Open a new `issue`_ if you encounter a problem.
-- Or ping **@localhuman** on the `NEO Slack`_.
+- Open a new
+ `issue `__ if you
+ encounter a problem.
+- Or ping **@localhuman** on the `NEO official community
+ chatroom `__.
- Pull requests welcome. New features, writing tests and documentation
are all needed.
@@ -47,25 +50,48 @@ Pip
pip install neo-boa
+Docker
+^^^^^^
+
+This project contains a Dockerfile to batch compile Python smart
+contracts. Clone the repository and navigate into the docker sub
+directory of the project. Run the following command to build the
+container:
+
+::
+
+ docker build -t neo-boa .
+
+The neo-boa Docker container takes a directory on the host containing
+python smart contracts as an input and a directory to compile the .avm
+files to as an output. It can be executed like this:
+
+::
+
+ docker run -it -v /absolute/path/input_dir:/python-contracts -v /absolute/path/output_dir:/compiled-contracts neo-boa
+
+The -v (volume) command maps the directories on the host to the
+directories within the container.
+
Manual
^^^^^^
Clone the repository and navigate into the project directory. Make a
-Python 3 virtual environment and activate it via
+Python 3 virtual environment and activate it via:
::
python3 -m venv venv
source venv/bin/activate
-or to install Python 3.5 specifically
+or, to install Python 3.5 specifically:
::
virtualenv -p /usr/local/bin/python3.5 venv
source venv/bin/activate
-Then install requirements
+Then, install the requirements:
::
@@ -74,7 +100,7 @@ Then install requirements
Usage
-----
-The compiler may be used like the following
+The compiler may be used like in the following example:
::
@@ -82,6 +108,12 @@ The compiler may be used like the following
Compiler.load_and_save('path/to/your/file.py')
+Docs
+----
+
+You can `read the docs
+here `__.
+
Tests
-----
@@ -90,21 +122,10 @@ Tests are important.
License
-------
-- Open-source `MIT`_.
-- Main author is **localhuman** [ https://github.com/localhuman ].
+- Open-source `MIT `__.
+- Main author is `localhuman `__.
Donations
---------
Accepted at **ATEMNPSjRVvsXmaJW4ZYJBSVuJ6uR2mjQU**
-
-.. _Overview: #overview
-.. _Installation: #installation
-.. _Usage: #usage
-.. _License: #license
-.. _Tests: #tests
-.. _Donatitons: #donations
-.. _Neo Virtual Machine: https://github.com/neo-project/neo-vm
-.. _issue: https://github.com/CityOfZion/neo-boa/issues/new
-.. _NEO Slack: https://join.slack.com/t/neoblockchainteam/shared_invite/MjE3ODMxNDUzMDE1LTE1MDA4OTY3NDQtNTMwM2MyMTc2NA
-.. _MIT: https://github.com/CityOfZion/neo-python/blob/master/LICENSE.md
\ No newline at end of file
diff --git a/boa/blockchain/vm/Neo/Account.py b/boa/blockchain/vm/Neo/Account.py
index c9a5a2f..0cfdd43 100644
--- a/boa/blockchain/vm/Neo/Account.py
+++ b/boa/blockchain/vm/Neo/Account.py
@@ -1,5 +1,5 @@
-class Account():
+class Account:
@property
def ScriptHash(self):
diff --git a/boa/blockchain/vm/Neo/Asset.py b/boa/blockchain/vm/Neo/Asset.py
index 5d24d82..e6f7eb0 100644
--- a/boa/blockchain/vm/Neo/Asset.py
+++ b/boa/blockchain/vm/Neo/Asset.py
@@ -1,5 +1,5 @@
-class Asset():
+class Asset:
@property
def AssetId(self):
diff --git a/boa/blockchain/vm/Neo/Block.py b/boa/blockchain/vm/Neo/Block.py
index f1b515b..48bdd88 100644
--- a/boa/blockchain/vm/Neo/Block.py
+++ b/boa/blockchain/vm/Neo/Block.py
@@ -1,8 +1,7 @@
-from boa.blockchain.vm.Neo.Transaction import *
from boa.blockchain.vm.Neo.Header import GetIndex, GetHash, GetPrevHash, GetTimestamp, GetVersion, GetNextConsensus, GetMerkleRoot, GetConsensusData
-class Block():
+class Block:
@property
def TransactionCount(self):
@@ -22,6 +21,11 @@ def Transactions(self):
@property
def Index(self):
+ """
+
+ Returns:
+
+ """
return GetIndex(self)
@property
@@ -81,7 +85,7 @@ def NextConsensus(self):
return GetNextConsensus(self)
-def GetTransactionCount(block: Block) -> int:
+def GetTransactionCount(block):
"""
returns the number of transactions in a block
@@ -91,7 +95,7 @@ def GetTransactionCount(block: Block) -> int:
pass
-def GetTransactions(block: Block) -> list:
+def GetTransactions(block):
"""
returns a list of transactions contained in a block
@@ -100,7 +104,7 @@ def GetTransactions(block: Block) -> list:
pass
-def GetTransaction(block: Block, index: int) -> Transaction:
+def GetTransaction(block, index):
"""
:param block: the block to get the transaction from
diff --git a/boa/blockchain/vm/Neo/Blockchain.py b/boa/blockchain/vm/Neo/Blockchain.py
index da8f71b..5d11213 100644
--- a/boa/blockchain/vm/Neo/Blockchain.py
+++ b/boa/blockchain/vm/Neo/Blockchain.py
@@ -1,19 +1,13 @@
-from boa.blockchain.vm.Neo.Header import *
-from boa.blockchain.vm.Neo.Block import *
-from boa.blockchain.vm.Neo.Transaction import *
-from boa.blockchain.vm.Neo.Account import *
-from boa.blockchain.vm.Neo.Asset import *
-from boa.blockchain.vm.Neo.Contract import *
-def GetHeight() -> int:
+def GetHeight():
"""
"""
pass
-def GetHeader(height_or_hash) -> Header:
+def GetHeader(height_or_hash):
"""
:param height_or_hash:
@@ -21,7 +15,7 @@ def GetHeader(height_or_hash) -> Header:
pass
-def GetBlock(height_or_hash) -> Block:
+def GetBlock(height_or_hash):
"""
:param height_or_hash:
@@ -29,7 +23,7 @@ def GetBlock(height_or_hash) -> Block:
pass
-def GetTransaction(hash) -> Transaction:
+def GetTransaction(hash):
"""
:param hash:
@@ -37,7 +31,7 @@ def GetTransaction(hash) -> Transaction:
pass
-def GetAccount(script_hash) -> Account:
+def GetAccount(script_hash):
"""
:param script_hash:
@@ -45,14 +39,14 @@ def GetAccount(script_hash) -> Account:
pass
-def GetValidators() -> []:
+def GetValidators():
"""
"""
pass
-def GetAsset(asset_id) -> Asset:
+def GetAsset(asset_id):
"""
:param asset_id:
@@ -60,7 +54,7 @@ def GetAsset(asset_id) -> Asset:
pass
-def GetContract(script_hash) -> Contract:
+def GetContract(script_hash):
"""
:param script_hash:
diff --git a/boa/blockchain/vm/Neo/Contract.py b/boa/blockchain/vm/Neo/Contract.py
index 9106f6c..c502e3b 100644
--- a/boa/blockchain/vm/Neo/Contract.py
+++ b/boa/blockchain/vm/Neo/Contract.py
@@ -1,5 +1,5 @@
-class Contract():
+class Contract:
@property
def Script(self):
diff --git a/boa/blockchain/vm/Neo/Header.py b/boa/blockchain/vm/Neo/Header.py
index 66f5a3c..b7492d8 100644
--- a/boa/blockchain/vm/Neo/Header.py
+++ b/boa/blockchain/vm/Neo/Header.py
@@ -1,6 +1,6 @@
-class Header():
+class Header:
def getmyhash(self):
return GetHash(self)
@@ -66,7 +66,7 @@ def NextConsensus(self):
return GetNextConsensus(self)
-def GetIndex(header: Header) -> int:
+def GetIndex(header):
"""
Returns the height/index of a header
Args:
@@ -78,7 +78,7 @@ def GetIndex(header: Header) -> int:
pass
-def GetHash(header: Header) -> bytearray:
+def GetHash(header):
"""
gets the hash of the header
@@ -87,7 +87,7 @@ def GetHash(header: Header) -> bytearray:
pass
-def GetVersion(header: Header) -> int:
+def GetVersion(header):
"""
gets the version of the header
@@ -96,7 +96,7 @@ def GetVersion(header: Header) -> int:
pass
-def GetPrevHash(header: Header) -> bytearray:
+def GetPrevHash(header):
"""
gets the hash of the previous header in the blockchain
@@ -105,7 +105,7 @@ def GetPrevHash(header: Header) -> bytearray:
pass
-def GetMerkleRoot(header: Header) -> bytearray:
+def GetMerkleRoot(header):
"""
gets the merkle root of the transactions contained in the block
@@ -114,7 +114,7 @@ def GetMerkleRoot(header: Header) -> bytearray:
pass
-def GetTimestamp(header: Header) -> int:
+def GetTimestamp(header):
"""
gets the timestamp of when the header was created
@@ -123,7 +123,7 @@ def GetTimestamp(header: Header) -> int:
pass
-def GetConsensusData(header: Header) -> bytearray:
+def GetConsensusData(header):
"""
gets the address of the consensus
@@ -132,7 +132,7 @@ def GetConsensusData(header: Header) -> bytearray:
pass
-def GetNextConsensus(header: Header) -> bytearray:
+def GetNextConsensus(header):
"""
gets the address where the next consensus will occur
diff --git a/boa/blockchain/vm/Neo/Input.py b/boa/blockchain/vm/Neo/Input.py
index 904f238..bfa2347 100644
--- a/boa/blockchain/vm/Neo/Input.py
+++ b/boa/blockchain/vm/Neo/Input.py
@@ -1,5 +1,5 @@
-class TransactionInput():
+class TransactionInput:
@property
def Hash(self):
diff --git a/boa/blockchain/vm/Neo/Output.py b/boa/blockchain/vm/Neo/Output.py
index 1c8032a..b804acf 100644
--- a/boa/blockchain/vm/Neo/Output.py
+++ b/boa/blockchain/vm/Neo/Output.py
@@ -1,5 +1,5 @@
-class TransactionOutput():
+class TransactionOutput:
@property
def AssetId(self):
diff --git a/boa/blockchain/vm/Neo/Transaction.py b/boa/blockchain/vm/Neo/Transaction.py
index 52187d9..a12e9f6 100644
--- a/boa/blockchain/vm/Neo/Transaction.py
+++ b/boa/blockchain/vm/Neo/Transaction.py
@@ -1,5 +1,5 @@
-class Transaction():
+class Transaction:
@property
def Hash(self):
diff --git a/boa/blockchain/vm/Neo/TransactionAttribute.py b/boa/blockchain/vm/Neo/TransactionAttribute.py
index 9b735ea..f534d30 100644
--- a/boa/blockchain/vm/Neo/TransactionAttribute.py
+++ b/boa/blockchain/vm/Neo/TransactionAttribute.py
@@ -1,5 +1,5 @@
-class TransactionAttribute():
+class TransactionAttribute:
@property
def Usage(self):
diff --git a/boa/blockchain/vm/Neo/Validator.py b/boa/blockchain/vm/Neo/Validator.py
index 678a211..4313c79 100644
--- a/boa/blockchain/vm/Neo/Validator.py
+++ b/boa/blockchain/vm/Neo/Validator.py
@@ -1,5 +1,5 @@
-class Validator():
+class Validator:
pass
diff --git a/boa/blockchain/vm/SmartContract.py b/boa/blockchain/vm/SmartContract.py
index 594c34e..7e1e9b1 100644
--- a/boa/blockchain/vm/SmartContract.py
+++ b/boa/blockchain/vm/SmartContract.py
@@ -1,5 +1,5 @@
-class SmartContract():
+class SmartContract:
def Sha1(data):
"""
diff --git a/boa/blockchain/vm/System/ExecutionEngine.py b/boa/blockchain/vm/System/ExecutionEngine.py
index 57135ee..705317f 100644
--- a/boa/blockchain/vm/System/ExecutionEngine.py
+++ b/boa/blockchain/vm/System/ExecutionEngine.py
@@ -1,5 +1,5 @@
-class ExecutionEngine():
+class ExecutionEngine:
"""
Not used.
diff --git a/boa/blockchain/vm/VMOp.py b/boa/blockchain/vm/VMOp.py
index 9286ee5..b0233b1 100644
--- a/boa/blockchain/vm/VMOp.py
+++ b/boa/blockchain/vm/VMOp.py
@@ -128,6 +128,8 @@
NEWSTRUCT = b'\xC6' # 用作值類型
APPEND = b'\xC8'
REVERSE = b'\xC9'
+REMOVE = b'\xCA'
+
DEBUGOP = b'\xFB'
@@ -138,7 +140,7 @@
items = dir(sys.modules[__name__])
-def ToName(op):
+def to_name(op):
"""
:param op:
@@ -151,18 +153,10 @@ def ToName(op):
n = getattr(module, item)
try:
- nn = int(binascii.hexlify(n))
-
+ nn = int.from_bytes(n, 'little')
if op == nn:
return item
- except Exception as e:
- pass
-
- try:
- nn2 = int.from_bytes(n, 'little')
- if op == nn2:
- return item
- except Exception as e:
+ except Exception:
pass
return None
diff --git a/boa/code/block.py b/boa/code/block.py
index f8e3b48..185d1c3 100644
--- a/boa/code/block.py
+++ b/boa/code/block.py
@@ -1,12 +1,12 @@
-from byteplay3 import Opcode, Label
+from byteplay3 import Opcode
from boa.code.pytoken import PyToken
from boa.code import pyop
from boa.code.vmtoken import NEO_SC_FRAMEWORK
import pdb
-class Block():
+class Block(object):
"""
@@ -82,7 +82,7 @@ def has_load_attr(self):
"""
for token in self.oplist:
if token.py_op == pyop.LOAD_ATTR and token.instance_type is None:
- if token.args not in ['reverse', 'append', ]:
+ if token.args not in ['reverse', 'append', 'remove', ]:
return True
return False
@@ -529,13 +529,12 @@ def process_iter_body(self, setup_block):
def lookup_return_types(self, orig_method):
ivars = {}
klass_type = None
- return_type = None
for index, token in enumerate(self.oplist):
if token.py_op == pyop.CALL_FUNCTION:
param_count = token.args
# why would param count be 256 when calling w/ kwargs?
- # when keyword args are sent, the param count is 256 * num paramms?
+ # when keyword args are sent, the param count is 256 * num params?
if param_count % 256 == 0:
param_count = 2 * int(param_count / 256)
diff --git a/boa/code/items.py b/boa/code/items.py
index 1db744e..99ba769 100644
--- a/boa/code/items.py
+++ b/boa/code/items.py
@@ -1,27 +1,19 @@
-from byteplay3 import Code, Opcode
-from boa.code.method import Method
+from byteplay3 import Opcode
from boa.code.pytoken import PyToken
-from boa.code import pyop
import importlib
import binascii
import sys
import os
-from byteplay3 import Code, SetLinenoType, Label
+from byteplay3 import Code, SetLinenoType
from boa.code import pyop
from boa.code.line import Line
from boa.code.method import Method
-from boa.blockchain.vm import VMOp
-
-from collections import OrderedDict
-
-import pdb
-
-class Item():
+class Item(object):
"""
"""
@@ -163,10 +155,10 @@ def script_hash_addr(self):
:return:
"""
- return SmartContractAppCall.ToScriptHashData(self.script_hash)
+ return SmartContractAppCall.to_script_hash_data(self.script_hash)
@staticmethod
- def ToScriptHashData(item):
+ def to_script_hash_data(item):
"""
:return:
diff --git a/boa/code/line.py b/boa/code/line.py
index 53c8cc9..459e20d 100644
--- a/boa/code/line.py
+++ b/boa/code/line.py
@@ -3,7 +3,7 @@
from boa.code import pyop
-class Line():
+class Line(object):
"""
@@ -30,9 +30,9 @@ def is_constant(self):
:return:
"""
-
- return (len(self.items) == 3 or len(self.items) == 5) and self.items[1][0] == pyop.LOAD_CONST and self.items[2][0] == pyop.STORE_NAME
-# return False
+ is_correct_length = len(self.items) == 3 or len(self.items) == 5
+ is_storing_constant = self.items[1][0] == pyop.LOAD_CONST and self.items[2][0] == pyop.STORE_NAME
+ return is_correct_length and is_storing_constant
@property
def is_module_method_call(self):
diff --git a/boa/code/method.py b/boa/code/method.py
index acd1e84..ae18ea8 100644
--- a/boa/code/method.py
+++ b/boa/code/method.py
@@ -10,12 +10,9 @@
import dis
import collections
-import pdb
-from byteplay3 import object_attributes, print_attr_values, printcodelist
-
-class Method():
+class Method(object):
"""
The method is the main unit of functionality. Any method can take 0 to many arguments and return 1 value
@@ -32,8 +29,9 @@ class Method():
The VMTokenizer is responsible for turning PyToken objects into VMToken objects.
- When the method has been tokenized, each token then has an address within the method. Once these addresses are complete,
- the ``convert_jumps`` method is called to tell each flow control operation where (which address) it will need to jump to.
+ When the method has been tokenized, each token then has an address within the method. Once these addresses are
+ complete, the ``convert_jumps`` method is called to tell each flow control operation where (which address) it will
+ need to jump.
"""
bp = None
@@ -356,7 +354,6 @@ def read_initial_tokens(self):
current_label = op
else:
- instance_type = None
if op in [pyop.STORE_FAST, pyop.STORE_NAME, pyop.STORE_GLOBAL] and arg not in self.local_stores.keys():
self._check_for_type(arg, total_lines)
diff --git a/boa/code/module.py b/boa/code/module.py
index cbfb736..84adc53 100644
--- a/boa/code/module.py
+++ b/boa/code/module.py
@@ -9,10 +9,8 @@
from collections import OrderedDict
-import pdb
-
-class Module():
+class Module(object):
"""
A Module is the top level component which contains code objects.
When, for example, compiling ``path/to/my/file.py``, the items contained in ``file.py`` are the module.
@@ -491,6 +489,9 @@ def to_s(self):
61 242 [data]
62 RETURN_VALUE [data]
"""
+ # Initialize if needed
+ if self.all_vm_tokens is None:
+ self.link_methods()
lineno = 0
pstart = True
@@ -534,13 +535,27 @@ def to_s(self):
pass
if pt.py_op == pyop.CALL_FUNCTION:
- old = to_label
- to_label = '%s %s %s' % (pt.func_name, pt.func_params, old)
+ if to_label is None:
+ old = ""
+ else:
+ old = to_label
+ param_string = "("
+ for param in pt.func_params:
+ param_string += str(param.args) + ", "
+ param_string = param_string.rstrip(", ") + ")"
+ to_label = '%s %s %s' % (pt.func_name, param_string, old)
lno = "{:<10}".format(
pt.line_no if do_print_line_no or pstart else '')
addr = "{:<5}".format(key)
op = "{:<20}".format(str(pt.py_op))
+
+ # If this is a number, it is likely a custom python opcode, get the name
+ if str(pt.py_op).isnumeric():
+ opname = pyop.to_name(int(str(pt.py_op)))
+ if opname is not None:
+ op = "{:<20}".format(opname)
+
arg = "{:<50}".format(
to_label if to_label is not None else pt.arg_s)
data = "[data] {:<20}".format(ds)
diff --git a/boa/code/pyop.py b/boa/code/pyop.py
index c45c36b..53dcf35 100644
--- a/boa/code/pyop.py
+++ b/boa/code/pyop.py
@@ -1,4 +1,5 @@
-
+import importlib
+import sys
# the following are python opcodes taken from the `opcode` module
# these have been constantized for easier access
# these are the opcodes used by python
@@ -180,3 +181,22 @@
LOAD_CLASS_ATTR = 249
DEBUG_OP = 250
+
+# the following is a convienience method
+# for a human readable version of the ops
+
+module = importlib.import_module('boa.code.pyop')
+items = dir(sys.modules[__name__])
+
+
+def to_name(op):
+ """
+
+ :param op:
+ :return:
+ """
+ for item in items:
+ n = getattr(module, item)
+ if op == n:
+ return item
+ return None
diff --git a/boa/code/pytoken.py b/boa/code/pytoken.py
index ea96076..f813700 100644
--- a/boa/code/pytoken.py
+++ b/boa/code/pytoken.py
@@ -1,21 +1,18 @@
from boa.code import pyop
-from byteplay3 import Label, isopcode, haslocal, Code
+from byteplay3 import Label, isopcode, haslocal
from opcode import opname
from boa.blockchain.vm import VMOp
-import pdb
-import inspect
-
NON_RETURN_SYS_CALLS = ['Notify', 'print', 'Log', 'Put', 'Register',
- 'reverse', 'append',
+ 'reverse', 'append', 'remove',
'Delete', 'SetVotes', 'ContractDestroy',
'MerkleRoot', 'Hash', 'PrevHash', 'GetHeader', ]
-class PyToken():
+class PyToken(object):
"""
@@ -111,8 +108,6 @@ def __init__(self, op, lineno, index=None, args=None, array_item=None):
self.array_item = array_item
def __str__(self):
- arg = ''
-
if self.args:
if type(self.args) is Label:
arg = str(self.args)
@@ -225,7 +220,8 @@ def to_vm(self, tokenizer, prev_token=None):
elif op in [pyop.BINARY_MULTIPLY, pyop.INPLACE_MULTIPLY]:
token = tokenizer.convert1(VMOp.MUL, self)
- elif op in [pyop.BINARY_FLOOR_DIVIDE, pyop.BINARY_TRUE_DIVIDE, pyop.INPLACE_FLOOR_DIVIDE, pyop.INPLACE_TRUE_DIVIDE]:
+ elif op in [pyop.BINARY_FLOOR_DIVIDE, pyop.BINARY_TRUE_DIVIDE,
+ pyop.INPLACE_FLOOR_DIVIDE, pyop.INPLACE_TRUE_DIVIDE]:
token = tokenizer.convert1(VMOp.DIV, self)
elif op in [pyop.BINARY_MODULO, pyop.INPLACE_MODULO]:
@@ -267,7 +263,7 @@ def to_vm(self, tokenizer, prev_token=None):
# arrays
elif op == pyop.BUILD_LIST:
- token = tokenizer.convert_new_array(VMOp.NEWARRAY, self)
+ token = tokenizer.convert_new_array(self)
elif op == pyop.SETITEM:
token = tokenizer.convert_set_element(self, self.args)
# token = tokenizer.convert1(VMOp.SETITEM,self, data=self.args)
diff --git a/boa/code/vmtoken.py b/boa/code/vmtoken.py
index 815e89e..887850d 100644
--- a/boa/code/vmtoken.py
+++ b/boa/code/vmtoken.py
@@ -1,18 +1,14 @@
+import pdb
+from collections import OrderedDict
from boa.code import pyop
-
from byteplay3 import Label
-
from boa.blockchain.vm import VMOp
from boa.blockchain.vm.BigInteger import BigInteger
-from collections import OrderedDict
-
NEO_SC_FRAMEWORK = 'boa.blockchain.vm.'
-import pdb
-
-class VMToken():
+class VMToken(object):
"""
"""
@@ -61,7 +57,7 @@ def __init__(self, vm_op=None, pytoken=None, addr=None, data=None):
self.is_annotation = False
-class VMTokenizer():
+class VMTokenizer(object):
"""
"""
@@ -122,7 +118,7 @@ def to_s(self):
if type(ds) is not int and len(ds) < 1:
try:
ds = value.data.decode('utf-8')
- except Exception as e:
+ except Exception:
pass
if pt.py_op == pyop.CALL_FUNCTION:
@@ -255,7 +251,7 @@ def update_push_integer(self, vmtoken, i):
return self.update1(vmtoken, VMOp.PUSH0)
elif i == -1:
return self.insert1(vmtoken, VMOp.PUSHM1)
- elif i > 0 and i <= 16:
+ elif 0 < i <= 16:
out = 0x50 + i
return self.update1(vmtoken, out)
@@ -324,7 +320,7 @@ def insert_push_integer(self, i):
return self.insert1(VMOp.PUSH0)
elif i == -1:
return self.insert1(VMOp.PUSHM1)
- elif i > 0 and i <= 16:
+ elif 0 < i <= 16:
out = 0x50 + i
return self.insert1(out)
@@ -355,14 +351,12 @@ def convert1(self, vm_op, py_token=None, data=None):
return vmtoken
- def convert_new_array(self, vm_op, py_token=None, data=None):
+ def convert_new_array(self, py_token=None):
# push the length of the array
"""
- :param vm_op:
:param py_token:
- :param data:
"""
if type(py_token.args) is int:
@@ -379,7 +373,6 @@ def convert_pop_jmp_if(self, pytoken):
return token
def convert_load_const(self, pytoken):
- token = None
if type(pytoken.args) is int:
token = self.convert_push_integer(pytoken.args, pytoken)
elif type(pytoken.args) is str:
@@ -392,12 +385,11 @@ def convert_load_const(self, pytoken):
token = self.convert_push_data(bytes(pytoken.args), pytoken)
elif type(pytoken.args) is bool:
token = self.convert_push_integer(pytoken.args)
- elif type(pytoken.args) == type(None):
+ elif isinstance(pytoken.args, type(None)):
token = self.convert_push_data(bytearray(0))
# elif type(pytoken.args) == Code:
# pass
else:
-
raise Exception("Could not load type %s for item %s " % (
type(pytoken.args), pytoken.args))
return token
@@ -440,7 +432,7 @@ def convert_push_integer(self, i, py_token=None):
return self.convert1(VMOp.PUSH0, py_token=py_token)
elif i == -1:
return self.convert1(VMOp.PUSHM1, py_token=py_token)
- elif i > 0 and i <= 16:
+ elif 0 < i <= 16:
out = 0x50 + i
return self.convert1(out, py_token=py_token)
@@ -542,7 +534,7 @@ def insert_unknown_type(self, item):
elif type(item) is bool:
self.insert_push_data(item)
- elif type(item) == type(None):
+ elif isinstance(item, type(None)):
self.insert_push_data(bytearray(0))
else:
raise Exception("Could not load type %s for item %s " %
@@ -598,7 +590,6 @@ def convert_built_in_list(self, pytoken):
:param pytoken:
"""
- new_array_len = 0
lenfound = False
for index, token in enumerate(pytoken.func_params):
@@ -740,14 +731,15 @@ def convert_method_call(self, pytoken):
return vmtoken
- def is_op_call(self, op):
+ @staticmethod
+ def is_op_call(op):
"""
:param op:
:return:
"""
if op in ['len', 'abs', 'min', 'max', 'concat', 'take', 'substr',
- 'reverse', 'append',
+ 'reverse', 'append', 'remove',
'sha1', 'sha256', 'hash160', 'hash256',
'verify_signature', 'verify_signatures']:
return True
@@ -789,12 +781,13 @@ def convert_op_call(self, op, pytoken=None):
elif op == 'reverse':
return self.convert1(VMOp.REVERSE, pytoken)
elif op == 'append':
- # pdb.set_trace()
return self.convert1(VMOp.APPEND, pytoken)
-
+ elif op == 'remove':
+ return self.convert1(VMOp.REMOVE, pytoken)
return None
- def is_sys_call(self, op):
+ @staticmethod
+ def is_sys_call(op):
"""
:param op:
@@ -820,7 +813,8 @@ def convert_sys_call(self, op, pytoken=None):
self.insert1(VMOp.NOP)
return vmtoken
- def is_built_in(self, op):
+ @staticmethod
+ def is_built_in(op):
"""
:param op:
diff --git a/boa/compiler.py b/boa/compiler.py
index 1bf7f23..dc4581f 100644
--- a/boa/compiler.py
+++ b/boa/compiler.py
@@ -2,7 +2,7 @@
from boa.code.module import Module
-class Compiler():
+class Compiler(object):
"""
The main compiler interface class.
@@ -54,7 +54,7 @@ def default(self):
try:
return self.modules[0]
- except Exception as e:
+ except Exception:
pass
@staticmethod
diff --git a/boa/tests/src/AppendTest.py b/boa/tests/src/AppendTest.py
index 746092b..50c0d20 100644
--- a/boa/tests/src/AppendTest.py
+++ b/boa/tests/src/AppendTest.py
@@ -6,7 +6,7 @@ def Main():
:return:
"""
- m = [1, 2, 4, 'blah']
+ m = [1, 2, 2]
m.append(7)
diff --git a/boa/tests/src/ArrayRemoveTest.py b/boa/tests/src/ArrayRemoveTest.py
new file mode 100644
index 0000000..0db7b70
--- /dev/null
+++ b/boa/tests/src/ArrayRemoveTest.py
@@ -0,0 +1,13 @@
+from boa.blockchain.vm.Neo.Runtime import Notify
+
+
+def Main():
+ """
+
+ :return:
+ """
+ m = [1, 2, 3, 4]
+
+ m.remove(1)
+
+ return m
diff --git a/boa/tests/src/ConcatTest2.py b/boa/tests/src/ConcatTest2.py
new file mode 100644
index 0000000..a12b518
--- /dev/null
+++ b/boa/tests/src/ConcatTest2.py
@@ -0,0 +1,53 @@
+"""
+Data Concatenation Test
+
+A simple utilise contract that attempts to concatenate 2 values together, disregard of their data types.
+
+Test Command:
+ build [FILE_PATH] test 0710 05 False False concat ['lorem','ipsum']
+Example Executions:
+ testinvoke [CONTRACT_HASH] concat ['lorem','ipsum']
+ testinvoke [CONTRACT_HASH] concat ['cloud',9']
+ testinvoke [CONTRACT_HASH] concat ['text',b'1010']
+Invalid Examples:
+ testinvoke [CONTRACT_HASH] concat [true,false]
+ testinvoke [CONTRACT_HASH] concat ['null',null]
+False Positive Examples:
+ testinvoke [CONTRACT_HASH] concat [0.9,1.23]
+"""
+from boa.blockchain.vm.Neo.Runtime import Notify
+from boa.code.builtins import concat
+
+
+def Main(operation, args):
+ """
+
+ :param operation: The name of the operation to perform
+ :param args: A list of arguments along with the operation
+ :type operation: str
+ :type args: list
+ :return: The result of the operation
+ :rtype: bytearray
+ """
+ if operation == 'concat':
+ return do_concat(args)
+ else:
+ Notify('unknown operation')
+ return False
+
+
+def do_concat(args):
+ """
+
+ :param args: A list of arguments along with the operation
+ :type args: list
+ :return: result of combined values
+ :rtype: Union[bool, bytearray]
+ """
+ if len(args) > 1:
+ a = args[0]
+ b = args[1]
+ output = concat(a, b)
+ return output
+ Notify('invalid argument length')
+ return False
diff --git a/boa/tests/src/LargeArrayStorageTest.py b/boa/tests/src/LargeArrayStorageTest.py
new file mode 100644
index 0000000..96f08b1
--- /dev/null
+++ b/boa/tests/src/LargeArrayStorageTest.py
@@ -0,0 +1,221 @@
+"""
+Large Array Storage Test
+
+A simple utilise contract that allows you to manipulate a stored list for stress tests and GAS cost evaluation.
+
+Test Command:
+ build [FILE_PATH] test 0710 05 True False init
+
+Example Executions:
+ testinvoke [CONTRACT_HASH] init
+ testinvoke [CONTRACT_HASH] delete
+ testinvoke [CONTRACT_HASH] fetch
+ testinvoke [CONTRACT_HASH] count
+ testinvoke [CONTRACT_HASH] append_1
+ testinvoke [CONTRACT_HASH] append_10
+"""
+from boa.blockchain.vm.Neo.Storage import Get, Put, Delete, GetContext
+from boa.blockchain.vm.Neo.Runtime import Log, Notify
+from boa.code.builtins import concat, list, range, take, substr
+
+# -- Global variables
+KEY = 'test_array'
+
+
+def Main(operation, args):
+ """
+
+ :param operation: The name of the operation to perform
+ :param args: A list of arguments along with the operation
+ :type operation: str
+ :type args: list
+ :return: The result of the operation
+ :rtype: bytearray
+ """
+ if operation == 'init':
+ return do_init()
+ elif operation == 'delete':
+ return do_delete()
+ elif operation == 'fetch':
+ return do_fetch()
+ elif operation == 'count':
+ return do_count()
+ elif operation == 'append_1':
+ return do_append_1()
+ elif operation == 'append_10':
+ return do_append_10()
+ else:
+ Notify('unknown operation')
+ return False
+
+
+def do_init():
+ """
+ Initialize the storage by setting an empty list.
+
+ :return: indication success execution of the command
+ :rtype: bool
+ """
+ context = GetContext()
+ init_list_bytes = serialize_array([])
+ Put(context, KEY, init_list_bytes)
+ return True
+
+
+def do_delete():
+ """
+ Delete the storage and reset back to its default state.
+
+ :return: indication success execution of the command
+ :rtype: bool
+ """
+ context = GetContext()
+ Delete(context, KEY)
+ return True
+
+
+def do_fetch():
+ """
+ Fetch current list value from storage.
+
+ :return: the stored list value
+ :rtype: list
+ """
+ context = GetContext()
+ list_bytes = Get(context, KEY)
+ return deserialize_bytearray(list_bytes)
+
+
+def do_count():
+ """
+ Fetch length of the stored list.
+
+ :return: the stored list value
+ :rtype: int
+ """
+ context = GetContext()
+ list_bytes = Get(context, KEY)
+ item_list = deserialize_bytearray(list_bytes)
+ return len(item_list)
+
+
+def do_append_1():
+ """
+ Add 1 item into the list in storage.
+
+ :return: indication success execution of the command
+ :rtype: bool
+ """
+ context = GetContext()
+ list_bytes = Get(context, KEY)
+ item_list = deserialize_bytearray(list_bytes)
+ item_list.append('single item')
+ list_length = len(item_list)
+ Log('new list length:')
+ Log(list_length)
+ list_bytes = serialize_array(item_list)
+ Put(context, KEY, list_bytes)
+ return True
+
+
+def do_append_10():
+ """
+ Add 10 items into the list in storage.
+
+ :return: indication success execution of the command
+ :rtype: bool
+ """
+ context = GetContext()
+ list_bytes = Get(context, KEY)
+ item_list = deserialize_bytearray(list_bytes)
+ addition_list = [
+ '1 of 10', '1 of 10', '1 of 10', '1 of 10', '1 of 10', '1 of 10', '1 of 10', '1 of 10', '1 of 10', '1 of 10'
+ ]
+ for item in addition_list:
+ item_list.append(item)
+ list_length = len(item_list)
+ Log('new list length:')
+ Log(list_length)
+ list_bytes = serialize_array(item_list)
+ Put(context, KEY, list_bytes)
+ return True
+
+
+def deserialize_bytearray(data):
+
+ # get length of length
+ collection_length_length = data[0:1]
+
+ # get length of collection
+ collection_len = data[1:collection_length_length + 1]
+
+ # create a new collection
+ new_collection = list(length=collection_len)
+
+ # trim the length data
+ offset = 1 + collection_length_length
+
+ for i in range(0, collection_len):
+
+ # get the data length length
+ itemlen_len = data[offset:offset + 1]
+
+ # get the length of the data
+ item_len = data[offset + 1:offset + 1 + itemlen_len]
+
+ # get the data
+ item = data[offset + 1 + itemlen_len: offset + 1 + itemlen_len + item_len]
+
+ # store it in collection
+ new_collection[i] = item
+
+ offset = offset + item_len + itemlen_len + 1
+
+ return new_collection
+
+
+def serialize_array(items):
+
+ # serialize the length of the list
+ itemlength = serialize_var_length_item(items)
+
+ output = itemlength
+
+ # now go through and append all your stuff
+ for item in items:
+
+ # get the variable length of the item
+ # to be serialized
+ itemlen = serialize_var_length_item(item)
+
+ # add that indicator
+ output = concat(output, itemlen)
+
+ # now add the item
+ output = concat(output, item)
+
+ # return the stuff
+ return output
+
+
+def serialize_var_length_item(item):
+
+ # get the length of your stuff
+ stuff_len = len(item)
+
+ # now we need to know how many bytes the length of the array
+ # will take to store
+
+ # this is one byte
+ if stuff_len <= 255:
+ byte_len = b'\x01'
+ # two byte
+ elif stuff_len <= 65535:
+ byte_len = b'\x02'
+ # hopefully 4 byte
+ else:
+ byte_len = b'\x04'
+
+ out = concat(byte_len, stuff_len)
+
+ return out
diff --git a/boa/tests/src/NEP5Test.py b/boa/tests/src/NEP5Test.py
index d80b802..91ded8c 100644
--- a/boa/tests/src/NEP5Test.py
+++ b/boa/tests/src/NEP5Test.py
@@ -27,7 +27,7 @@
from boa.blockchain.vm.Neo.TriggerType import Application, Verification
from boa.blockchain.vm.Neo.Output import GetScriptHash, GetValue, GetAssetId
from boa.blockchain.vm.Neo.Storage import GetContext, Get, Put, Delete
-
+from boa.blockchain.vm.Neo.Header import GetTimestamp, GetNextConsensus
# -------------------------------------------
# TOKEN SETTINGS
diff --git a/boa/tests/src/blockchain/AttrTest.py b/boa/tests/src/blockchain/AttrTest.py
index eb71693..33167af 100644
--- a/boa/tests/src/blockchain/AttrTest.py
+++ b/boa/tests/src/blockchain/AttrTest.py
@@ -1,5 +1,7 @@
from boa.blockchain.vm.Neo.Blockchain import GetHeader, GetBlock
from boa.blockchain.vm.Neo.Runtime import Notify
+from boa.blockchain.vm.Neo.Header import GetTimestamp
+from boa.blockchain.vm.Neo.Block import GetTransactions
def Main():
diff --git a/boa/tests/src/blockchain/BlockTest.py b/boa/tests/src/blockchain/BlockTest.py
index 7e7d907..fd8fc52 100644
--- a/boa/tests/src/blockchain/BlockTest.py
+++ b/boa/tests/src/blockchain/BlockTest.py
@@ -1,5 +1,6 @@
from boa.blockchain.vm.Neo.Blockchain import GetBlock
-from boa.blockchain.vm.Neo.Runtime import Notify, Log
+from boa.blockchain.vm.Neo.Runtime import Notify
+from boa.blockchain.vm.Neo.Block import GetTransactions
def Main():
diff --git a/boa/tests/src/blockchain/ExScriptHashTest.py b/boa/tests/src/blockchain/ExScriptHashTest.py
index 63edc50..743b392 100644
--- a/boa/tests/src/blockchain/ExScriptHashTest.py
+++ b/boa/tests/src/blockchain/ExScriptHashTest.py
@@ -1,6 +1,6 @@
from boa.blockchain.vm.Neo.Blockchain import GetAccount, GetAsset
from boa.blockchain.vm.System.ExecutionEngine import GetExecutingScriptHash
-from boa.blockchain.vm.Neo.Account import GetBalance
+from boa.blockchain.vm.Neo.Account import GetBalance, GetVotes
from boa.blockchain.vm.Neo.Runtime import Notify
GAS = b'\xe7-(iy\xeel\xb1\xb7\xe6]\xfd\xdf\xb2\xe3\x84\x10\x0b\x8d\x14\x8ewX\xdeB\xe4\x16\x8bqy,`'
diff --git a/boa/tests/src/blockchain/HeaderTest.py b/boa/tests/src/blockchain/HeaderTest.py
index f4b4537..ed196e6 100644
--- a/boa/tests/src/blockchain/HeaderTest.py
+++ b/boa/tests/src/blockchain/HeaderTest.py
@@ -1,5 +1,5 @@
from boa.blockchain.vm.Neo.Blockchain import GetHeader
-from boa.blockchain.vm.Neo.Header import GetMerkleRoot, GetTimestamp, GetHash, GetVersion
+from boa.blockchain.vm.Neo.Header import GetMerkleRoot, GetTimestamp, GetHash, GetVersion, GetNextConsensus
from boa.blockchain.vm.Neo.Runtime import Notify, Log
diff --git a/boa/tests/src/blockchain/ModulePathTest.py b/boa/tests/src/blockchain/ModulePathTest.py
index 1da12e4..6ed01c0 100644
--- a/boa/tests/src/blockchain/ModulePathTest.py
+++ b/boa/tests/src/blockchain/ModulePathTest.py
@@ -1,5 +1,7 @@
from boa.blockchain.vm.Neo.Runtime import Notify
from boa.blockchain.vm.Neo.Blockchain import GetHeader, GetBlock
+from boa.blockchain.vm.Neo.Header import GetTimestamp
+from boa.blockchain.vm.Neo.Block import GetTransactions
from boa.blockchain.vm.Neo.Transaction import *
INVOKE_TX_TYPE = b'\xd1'
diff --git a/boa/tests/src/blockchain/TXUnspentCoins.py b/boa/tests/src/blockchain/TXUnspentCoins.py
index 8062ed2..9394982 100644
--- a/boa/tests/src/blockchain/TXUnspentCoins.py
+++ b/boa/tests/src/blockchain/TXUnspentCoins.py
@@ -1,7 +1,7 @@
from boa.blockchain.vm.Neo.Transaction import *
from boa.blockchain.vm.Neo.Blockchain import GetTransaction
from boa.blockchain.vm.Neo.Runtime import Notify
-from boa.blockchain.vm.Neo.Output import GetValue
+from boa.blockchain.vm.Neo.Output import GetValue, GetScriptHash
def Main(txhash):
diff --git a/boa/tests/src/objects/stuff/storage_api.py b/boa/tests/src/objects/stuff/storage_api.py
index e117899..f0d3fc6 100644
--- a/boa/tests/src/objects/stuff/storage_api.py
+++ b/boa/tests/src/objects/stuff/storage_api.py
@@ -2,7 +2,7 @@
from boa.blockchain.vm.Neo.Storage import GetContext, Get, Put, Delete
-class StorageAPI():
+class StorageAPI(object):
ctx = GetContext()
diff --git a/boa/tests/src/objects/stuff/things.py b/boa/tests/src/objects/stuff/things.py
index 74145ac..68aac96 100644
--- a/boa/tests/src/objects/stuff/things.py
+++ b/boa/tests/src/objects/stuff/things.py
@@ -1,5 +1,5 @@
-class Awesome():
+class Awesome(object):
"""
A very basic object
"""
@@ -20,7 +20,7 @@ def awesome_name(self):
return self.myname
-class MoreAwesome():
+class MoreAwesome(object):
"""
A little more complicated object
"""
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 75171a2..e9f1f2b 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -67,9 +67,9 @@
# built documents.
#
# The short X.Y version.
-version = '0.2.1'
+version = '0.2.2'
# The full version, including alpha/beta/rc tags.
-release = '0.2.1'
+release = '0.2.2'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff --git a/setup.py b/setup.py
index 7926d92..57b20e0 100644
--- a/setup.py
+++ b/setup.py
@@ -19,7 +19,7 @@
# Versions should comply with PEP440. For a discussion on single-sourcing
# the version across setup.py and the project code, see
# https://packaging.python.org/en/latest/single_source_version.html
- version='0.2.1',
+ version='0.2.2',
description='A Python compiler for the Neo Virtual Machine',
long_description=long_description,