NEO-Python

概述

neo-python 目前功能

  • 此項目的目標是成爲原始 C# NEO 項目 的全功能移植
  • 運行基於 Python 的 P2P 節點
  • 提供可交互 CLI 用於配置節點和檢測區塊鏈
  • 編譯、測試、部署以及運行以 python 編寫的智能合約或任意 .avm 格式的合約
  • 符合 NEP2NEP5 標準的錢包功能
  • RPC 客戶端
  • RPC 服務器
  • 通知服務器 (用於查看 NEP5 通證的轉帳)
  • Runtime.LogRuntime.Notify 事件監控

即將支持功能

  • 共識節點
  • 完整的智能合約調試和檢查

入門指南

請按照 安裝 章節的說明進行開始操做。html

此項目的主要功能包含在 cli 應用程序的 np-prompt 命令中。詳情請參見 使用命令行與 NEO 區塊鏈交互node

咱們發佈了一個 Youtube 視頻 幫助你快速上手。在CityOfZion 的 Youtube 頻道下還有其它視頻可供參考。python

相關項目

安裝(Ubuntu/OSX)

須要安裝 libleveldb 庫。安裝 Python 3.6Python 3.7 以避免由於與當前維護者版本不一樣遇到問題。注意不支持 Python 3.5 及更低版本。c++

安裝 neo-python 前須要安裝平臺相關項目。git

平臺相關操做

Ubuntu/Debian 16.10+

Ubuntu 從16.10 開始在官方存儲庫中支持 Python 3.6 。github

首先,請使用如下命令確保 Ubuntu 是最新的:web

sudo apt-get update && sudo apt-get upgrade
複製代碼

你可使用如下命令安裝 Python 3.7 和全部系統依賴:docker

sudo apt-get install python3.7 python3.7-dev python3.7-venv python3-pip libleveldb-dev libssl-dev g++
複製代碼

你也可使用如下命令直接安裝 Python 3.6 和全部系統依賴:macos

sudo apt-get install python3.6 python3.6-dev python3.6-venv python3-pip libleveldb-dev libssl-dev g++
複製代碼

舊版 Ubuntu(如16.04)

對於較舊的 Ubuntu 版本,你須要使用一個像 Felix Krull 的 deadsnakes PPA 這樣的外部存儲庫 (更多內容,閱讀 here):編程

(本文檔中第三方軟件連接的使用由您自行決定並承擔風險,且您贊成對計算機系統的任何損壞或此類活動致使的數據丟失承擔所有責任。)

apt-get install software-properties-common python-software-properties
add-apt-repository ppa:deadsnakes/ppa
apt-get update
apt-get install python3.6 python3.6-dev python3.6-venv python3-pip libleveldb-dev libssl-dev g++
複製代碼

Centos/Redhat/Fedora

# Install Python 3.6:
yum install -y centos-release-scl
yum install -y rh-python36
scl enable rh-python36 bash

# Install dependencies:
yum install -y epel-release
yum install -y readline-devel leveldb-devel libffi-devel gcc-c++ redhat-rpm-config gcc python-devel openssl-devel
複製代碼

Windows

在 Windows 操做系統上安裝 neo-python 須要進行特別的操做,還可能遇到一些常見問題須要解決,相關內容請查看 Installation (Windows)

OSX

brew install leveldb
複製代碼

關於 OSX 的常見問題

若是您遇到與此相似的問題:

from ._plyvel import (  # noqa
ImportError: dlopen(neo-python/venv/lib/python3.6/site-packages/plyvel/_plyvel.cpython-35m-darwin.so, 2): Symbol not found: __ZN7leveldb2DB4OpenERKNS_7Options
ERKSsPPS0_
Referenced from: neo-python/venv/lib/python3.6/site-packages/plyvel/_plyvel.cpython-35m-darwin.so
Expected in: flat namespace
複製代碼

解決方案:更新到 plyvel 1.0.4: pip install -r requirements.txt

在 OSX 上安裝 pycrypto 模塊時可能會遇到問題:

src/_fastmath.c:36:11: fatal error: 'gmp.h' file not found
# include <gmp.h>
          ^~~~~~~
330 warnings and 1 error generated.
error: command 'clang' failed with exit status 1
複製代碼

要解決此問題,可使用 homebrew 安裝 gmp 庫,並使用如下命令行運行pip install:

brew install gmp
CFLAGS='-mmacosx-version-min=10.7 -stdlib=libc++' pip install --no-use-wheel pycrypto --no-cache-dir --global-option=build_ext --global-option="-I/usr/local/Cellar/gmp/6.1.2/include/" --global-option="-L/usr/local/lib"
複製代碼

import scrypt / Reason: image not found

若是遇到以下錯誤:

import scrypt
File "/project_dir/venv/lib/python3.6/site-packages/scrypt.py", line 11, in
_scrypt = cdll.LoadLibrary(imp.find_module('_scrypt')[1])
File "/project_dir/venv/lib/python3.6/ctypes/init.py", line 429, in LoadLibrary
return self._dlltype(name)
File "/project_dir/venv/lib/python3.6/ctypes/init.py", line 351, in init
self._handle = _dlopen(self._name, mode)
OSError: dlopen(/project_dir/venv/lib/python3.6/site-packages/_scrypt.cpython-36m-darwin.so, 6): Library not loaded: /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib
Referenced from: /project_dir/venv/lib/python3.6/site-packages/_scrypt.cpython-36m-darwin.so
Reason: image not found
複製代碼

能夠嘗試如下命令:

brew reinstall openssl
複製代碼

從 PyPi 安裝

在計算機上安裝 neo-python 的最簡單方法下載後使用 pip 從 PyPi 安裝。建議您先建立一個虛擬環境,將此安裝與系統目錄隔離,而後像往常同樣安裝:

# create project dir
mkdir myproject
cd myproject

# create virtual environment and activate

python3.6 -m venv venv # this can also be python3 -m venv venv depending on your environment
source venv/bin/activate

(venv) pip install neo-python
複製代碼

從 Git 安裝

github.com/CityOfZion/… clone 存儲庫並導航到項目目錄。建立一個 Python 3 虛擬環境並激活:

git clone https://github.com/CityOfZion/neo-python.git
cd neo-python

# if you want to use the development branch, switch now
git checkout development

# create virtual environment using Python 3.7 and activate or skip to the next step for Python 3.6
python3.7 -m venv venv
source venv/bin/activate

# create virtual environment using Python 3.6 and activate
python3.6 -m venv venv
source venv/bin/activate

# install the package in an editable form
(venv) pip install -e .
複製代碼

從 Git 更新 neo-python

若是要使用 git pull 更新 neo-python ,還須要使用 pip install -r requirements.txt 更新依賴。

同步區塊鏈

第一次使用 neo-python 時須要同步區塊鏈。這將花費較長時間。此項目包含的 bootstrap.py 能夠自動下載一個鏈目錄。

要同步測試網,運行 np-bootstrap

要同步主網,運行 np-bootstrap -m 並等待同步完成 (3.3 GB 文件)。

在 Windows 系統中安裝

如下安裝說明針對帶有 MSYS2 環境和 Visual Studio 2017 的 Windows 7 x64 系統,也適用於大多數 Windows 發行版。你也能夠選擇使用 Ubuntu 安裝一個 Linux 子系統( 更多信息參見 這裏)。

NOTE

從 Microsoft Store 安裝的 Ubuntu 是 Ubuntu 16.04。你須要從這裏下載 Ubuntu 18.04:www.microsoft.com/en-us/p/ubu…

構建 LevelDB

在 Windows 系統中安裝 leveldb 最簡單的辦法是使用 VC++ 打包工具。若是你使用的是 windows x64 系統,還須要先設置環境變量:set VCPKG_DEFAULT_TRIPLET=x64-windows

git clone https://github.com/Microsoft/vcpkg
cd vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg integrate install
.\vcpkg install leveldb
複製代碼

安裝 python 依賴項

安裝 Anaconda package manager,並激活 python 虛擬環境。

conda create -n neo python=3.6.4
activate neo
複製代碼

(可選)激活 Visual Studio 構建環境,以下所示:

"e:\Program Files\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
複製代碼

構建 plyvel (1.0.4)

確認編譯器能夠訪問 .lib 和 leveldb 頭文件,而後將它們複製到 MSVC 構建工具目錄下:

vcpkg\installed\x64-windows\include\ 複製到 e:\Program Files\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.12.25827\include

vcpkg\installed\x64-windows\lib\libleveldb.lib 複製到 e:\Program Files\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.12.25827\lib\x64\leveldb.lib

而後克隆存儲庫並進入所需版本,安裝 cython,從 C++ 文件構建 python 擴展,最後安裝 plyvel,以下所示:

git clone https://github.com/wbolster/plyvel
cd plyvel
git checkout e3887a5fae5d7b8414eac4c185c1e3b0cebbdba8
pip install cython
cython --cplus --fast-fail --annotate plyvel/_plyvel.pyx
python setup.py build_ext --inplace --force
python setup.py install
複製代碼

構建 peewee (2.10.2)

git clone https://github.com/coleifer/peewee
cd peewee
git checkout 761f9144a0e17381147a81658019cffe14c118ca
python setup.py install
複製代碼

構建 mmh3 (2.5.1)

git clone https://github.com/hajimes/mmh3
cd mmh3
git checkout a73b373858dedfdb6d362f5ca985ae1bb6bc2161
python setup.py install
複製代碼

從 Anaconda 安裝依賴項

某些依賴項沒法正確地從 pip 安裝,但能夠從 Anaconda 安裝。

conda install twisted==17.9.0
conda install pycrypto==2.6.1
複製代碼

安裝 neo-python

導航到 neo-python 目錄並安裝其它依賴項,使用如下命令:

pip install -r requirements.txt
複製代碼

檢查安裝: python prompt.py

故障排除

如下列出了在安裝過程當中可能遇到的問題和解決方案。

LINK : fatal error LNK1181: cannot open input file ‘leveldb.lib’

確保編譯器能夠訪問 .lib 與 leveldb 頭文件。

error LNK2001: unresolved external symbol __imp_PathFileExistsW

找到庫 shlwapi.lib,它可能在你的文件系統中。 將其合併到 leveldb.lib:lib.exe /OUT:newleveldb.lib leveldb.lib ShLwApi.Lib 並替換原始文件。

ImportError: No module named ‘winrandom’

導航到你的 python 發行版包目錄,例如: e:\Programs\Anaconda3\envs\neo\Lib\site-packages

在 Crypto\Random\OSRNG\nt.py 中將 import winrandom 修改成 from . import winrandom

ImportError: No module named ‘win32api’

安裝模塊: pip install pypiwin32

NEO-Python SeedList

介紹

關於 SeedList

SeedList 是一個 URL 列表,是 NEO-Python 在啓動時嘗試鏈接的節點。在 NEO-Python 目錄下 (/neo/data) 的 protcol.*.json 文件中能夠找到 SeedList。有三個經常使用 protocol.*.json 文件:

protocol.mainnet.json
protocol.testnet.json
protocol.privnet.json
複製代碼

本文指的是 protocol.mainnet.json ,但這些信息是通用的。

json
{
    "ProtocolConfiguration": {
    "Magic": ...,
    "AddressVersion": ...,
    "SecondsPerBlock": ...,
    "StandbyValidators": [
    ...
    ],
    "SeedList": [
      "seed1.neo.org:10333",
      "seed2.neo.org:10333",
      "seed3.neo.org:10333",
      "seed4.neo.org:10333",
      "seed5.neo.org:10333"
    ],
    "RPCList":[
    ...
    ],
    "SystemFee": {
    ...
    }
  }
}複製代碼

在上面的文件中,NEO-Python 配置爲經過PORT:10333 鏈接 seed1.neo.org, seed2.neo.org 等節點 。

潛在問題

假如以上列表中的每一個節點故障,NEO-Python 會嘗試鏈接相鄰節點。可是這種方法存在不少未知因素,例如相鄰節點出問題了,可能致使等待時間至關漫長。

更新 SeedList

經過使用咱們肯定的活躍節點更新 SeedList,能夠避免上述潛在問題中提到的漫長等待風險。

更新 Windows WSL (Ubuntu) 上的 NEO-Python 節點列表

若是是在 Ubuntu 上運行 neo-python ,你應該使用了 venv。你須要修改 venv 目錄裏 lib/python3.6/site-packages/neo/data 路徑下的 protocol.mainnet.json 文件。若是該路徑不存在,說明你還沒有在當前 venv 中使用該文件,那麼你能夠到 neo/data 路徑下的 neo-python 文件夾中修改此文件。或者你也能夠刪除 venv 文件夾,修改父文件,新建一個 venv 文件夾(python -m venv venv)並激活 venv,而後使用 pip install e . 從新安裝。

更新 SeedList

活躍節點

要尋找活躍節點,使用 NEO 網絡狀態監測網。要查看更詳細的信息,查看該監測網 Github 上的 存儲庫

seedlist

如上圖所示,列表中顯示了可用節點。最新節點顯示在最上方。 圖中

2
標識的地方表示節點是否響應。一般,綠色並顯示 yes 的節點爲活躍節點。

咱們會根據如下端口的標準協議進行選擇,好比咱們會選擇上圖中的節點 1 而不選節點3,由於節點 1符合標準而節點 3 不符合。

Main Net Test Net
JSON-RPC via HTTPS 10331 20331
JSON-RPC via HTTP 10332 20332
P2P via TCP 10333 20333
P2P via WebSocket 10334 20334

如下是選出的活躍節點地址:

seed3.aphelion-neo.com
seed4.aphelion-neo.com
node2.ams2.bridgeprotocol.io
pyrpc1.nodeneo.ch
node2.nyc3.bridgeprotocol.io
複製代碼

編輯 protocol 文件

爲了告知 NEO-Python 新的節點列表,須要將前面幾個地址粘貼到 protocol.mainnet.json,以下所示:

json
{
    "ProtocolConfiguration": {
    "Magic": ...,
    "AddressVersion": ...,
    "SecondsPerBlock": ...,
    "StandbyValidators": [
    ...
    ],
    "SeedList": [
      "seed1.neo.org:10333",
      "seed2.neo.org:10333",
      "seed3.neo.org:10333",
      "seed4.neo.org:10333",
      "seed5.neo.org:10333",
      "seed4.aphelion-neo.com:10333",
      "node2.sgp1.bridgeprotocol.io:10333",
      "seed2.aphelion-neo.com:10333",
      "seed3.aphelion-neo.com:10333",
      "node2.ams2.bridgeprotocol.io:10333",
      "pyrpc1.narrative.network:10333",
      "node2.nyc3.bridgeprotocol.io:10333",
      "pyrpc4.narrative.network:10333",
      "pyrpc2.narrative.network:10333",
      "pyrpc3.narrative.network:10333",
      "seed1.aphelion-neo.com:10333",
      "seed1.switcheo.network:10333",
      "seed2.switcheo.network:10333",
      "seed5.cityofzion.io:10333",
      "seed3.cityofzion.io:10333",
      "seed3.switcheo.network:10333",
      "seed1.o3node.org:10333",
      "seed3.travala.com:10333",
      "seed4.cityofzion.io:10333",
      "seed2.cityofzion.io:10333",
      "seed2.o3node.org:10333",
      "seed3.o3node.org:10333",
      "node1.sgp1.bridgeprotocol.io:10333",
      "seed2.travala.com:10333",
      "seed4.switcheo.network:10333",
      "seed1.spotcoin.com:10333",
      "node1.nyc3.bridgeprotocol.io:10333"
    ],
    "RPCList":[
    ...
    ],
    "SystemFee": {
    ...
    }
  }
}
複製代碼

請注意,上例中的每一個地址後加上了 :10333 以便告知 NEO-Python 使用 P2P 協議鏈接。

如今就能夠像往常同樣啓動 neo-python 了。

JSON 和 REST API 服務器

建議你在啓動任意 API 服務器以前先更新節點列表以保證最大鏈接數。有關更多API服務器信息,請查看 這裏

基本用法

使用 neo-python 主要有兩種方式:運行 np-prompt 和運行自定義代碼的節點。

np-prompt

在測試網運行 np-prompt :

$ np-prompt
複製代碼

顯示 help 的全部可用參數:

$ np-prompt -h
usage: np-prompt [-h] [-m | -p [host] | --coznet | -c CONFIG]
                 [-t {dark,light}] [-v] [--datadir DATADIR] [--version]

optional arguments:
  -h, --help            show this help message and exit
  -m, --mainnet         Use MainNet instead of the default TestNet
  -p [host], --privnet [host]
                        Use a private net instead of the default TestNet,
                        optionally using a custom host (default: 127.0.0.1)
  --coznet              Use the CoZ network instead of the default TestNet
  -c CONFIG, --config CONFIG
                        Use a specific config file
  -t {dark,light}, --set-default-theme {dark,light}
                        Set the default theme to be loaded from the config
                        file. Default: 'dark'
  -v, --verbose         Show smart-contract events by default
  --datadir DATADIR     Absolute path to use for database directories
  --version             show program's version number and exit 複製代碼

自定義代碼節點

查看 /examples 目錄下的示例:github.com/CityOfZion/…

另外可參考 「Settings and Logging」 and 「Interacting with Smart Contracts」.

API 服務器 (JSON / REST)

在主網啓動 JSON 和 REST API 服務器:

$ np-api-server --mainnet --port-rpc 10332 --port-rest 80
複製代碼

示例通知和 help 下的全部可用參數:

$ np-api-server --testnet --port-rpc 8080 --port-rest 8088
[I 180315 09:27:09 NotificationDB:44] Created Notification DB At /Users/thomassaunders/.neopython/Chains/Test_Notif
[I 180315 09:27:09 threading:864] [TestNet] Block 5644 / 53999
[I 180315 09:27:09 np-api-server:11] Starting json-rpc api server on http://0.0.0.0:8080
[I 180315 09:27:09 _observer:131] Site starting on 8080
[I 180315 09:27:09 _observer:131] Starting factory <twisted.web.server.Site object at 0x110619828>
[I 180315 09:27:09 np-api-server:11] Starting REST api server on http://0.0.0.0:8088

# view help
$ np-api-server -h
usage: np-api-server [-h]
                   (--mainnet | --testnet | --privnet | --coznet | --config CONFIG)
                   [--port-rpc PORT_RPC] [--port-rest PORT_REST]
                   [--logfile LOGFILE] [--syslog] [--syslog-local [0-7]]
                   [--disable-stderr] [--datadir DATADIR]
                   [--maxpeers MAXPEERS] [--wallet WALLET] [--host HOST]

  optional arguments:
  -h, --help            show this help message and exit
  --datadir DATADIR     Absolute path to use for database directories
  --maxpeers MAXPEERS   Max peers to use for P2P Joining
  --wallet WALLET       Open wallet. Will allow you to use methods that
                        require an open wallet
  --host HOST           Hostname ( for example 127.0.0.1)

  Network options:
  --mainnet             Use MainNet
  --testnet             Use TestNet
  --privnet             Use PrivNet
  --coznet              Use CozNet
  --config CONFIG       Use a specific config file

  Mode(s):
  --port-rpc PORT_RPC     port to use for the json-rpc api (eg. 10332)
  --port-rest PORT_REST   port to use for the rest api (eg. 80)

  Logging options:
  --logfile LOGFILE     Logfile
  --syslog              Log to syslog instead of to log file ('user' is the
                        default facility)
  --syslog-local [0-7]  Log to a local syslog facility instead of 'user'.
                        Value must be between 0 and 7 (e.g. 0 for 'local0').
  --disable-stderr      Disable stderr logger
複製代碼

端口描述

要使外部程序能訪問你的 API 服務器,須要打開防火牆端口。下表顯示的端口能夠設置爲所有打開或按需打開。

Main Net Test Net
JSON-RPC via HTTPS 10331 20331
JSON-RPC via HTTP 10332 20332

使用 Windows WSL (Ubuntu) 運行 API 服務器

若是在 Windows WSL (Ubuntu) 上運行 neo-python, 除了打開路由器上的相應端口,還須要參考 這裏 爲你的 Windows 防火牆添加一個入站策略。

使用命令行與 NEO 區塊鏈交互

Prompt 是用於運行和與 NEO 區塊鏈交互的默認界面。

以下所示:

$ np-prompt
NEO cli. Type 'help' to get started

neo>
複製代碼

錢包操做

下表列出了全部可用的錢包命令。

命令 描述
create wallet <wallet_path> 建立錢包文件
open wallet <wallet_path> 打開錢包文件
wallet 檢查錢包
wallet <verbose> < rebuild> <rebuild block_height> 重建錢包索引
wallet migrated 遷移你的錢包
export wif <address> 導出私鑰
export nep2 <address> 將地址導出爲NEP2加密私鑰
import wif <WIF> 導入私鑰
import nep2 <address> 將地址導入爲NEP2加密私鑰
import watch_addr <address> 導入 watch only 地址
import contract_addr <script_hash> <pubkey> 導入智能合約地址
send <asset_ID> <address> <amount> [from_address] 將資產發送到指定的地址
wallet delete_addr <address> 刪除地址

解釋與示例

建立錢包

neo> create wallet path/to/walletfile
[Password 1]> **********
[Password 2]> **********
Wallet {
    "addresses": [
        "AayaivCAcYnM8q79JCrfpRGXrCEHJRN5bV"
    ],
    "claims": {
        "available": 0.0,
        "unavailable": 0.0
    },
    "tokens": [],
    "height": 0,
    "synced_balances": [],
    "path": "Wallets/blahblah.db3",
    "public_keys": [
        {
            "Address": "AayaivCAcYnM8q79JCrfpRGXrCEHJRN5bV",
            "Public Key": "027973267230b7cba0724589653e667ddea7aa8479c01a82bf8dd398cec93508ef"
        }
    ],
    "percent_synced": 0
}
neo>
複製代碼

打開錢包

neo> open wallet path/to/walletfile
[Password]> ***********
Opened wallet at path/to/walletfile
neo>
複製代碼

檢查錢包

neo> wallet
Wallet {
    "addresses": [
        "AayaivCAcYnM8q79JCrfpRGXrCEHJRN5bV"
    ],
    "claims": {
        "available": 0.0,
        "unavailable": 0.0
    },
    "tokens": [],
    "height": 75500,
    "synced_balances": [],
    "path": "Wallets/blahblah.db3",
    "public_keys": [
        {
            "Address": "AayaivCAcYnM8q79JCrfpRGXrCEHJRN5bV",
            "Public Key": "027973267230b7cba0724589653e667ddea7aa8479c01a82bf8dd398cec93508ef"
        }
    ],
    "percent_synced": 9
}
複製代碼

重建錢包索引

若是您的錢包出現異常,或者您已將新地址導入錢包,則可能須要重建錢包索引,從區塊頭同步錢包。也能夠指定一個區塊號開始從新同步,以下所示:

neo> wallet rebuild 700000
restarting at 700000
neo>
複製代碼

遷移錢包

若是錢包數據模型發生更改,則可能須要遷移錢包,以下所示:

neo> wallet migrated
migrated wallet
neo>
複製代碼

從新加密錢包

若是你打開錢包時收到以下信息,則必須從新加密以防止以前的錢包漏洞。

Could not open wallet: This wallet is currently vulnerable. Please execute the "reencrypt_wallet.py" script on this wallet before continuing
複製代碼

要修復此問題,先使用命令 exit 退出 neo prompt,而後運行從新加密腳本:

python reencrypt_wallet.py path/to/mywallet.db3
複製代碼

你須要輸入密碼並使用新名稱path/to/new_mywallet.db3保存從新加密的錢包。

導入私鑰

您可能須要導入一個WIF私鑰來添加地址到您的錢包,以下所示:

neo> import wif KxP97gujib35PBEnTq78e5NmYVbeaosU4AdguDzZ4tyf6a7W32UM
Imported key KxP97gujib35PBEnTq78e5NmYVbeaosU4AdguDzZ4tyf6a7W32UM
Pubkey: 303263383231666338336465373331313039633435653034346136353863386631313337623730303461396232323237613335653262353566613061313630323731
neo>
複製代碼

導出私鑰

您可能須要從錢包導出 WIF 私鑰用於另外一個程序。指定要導出的 WIF 地址,以下所示:

neo> export wif AXjaFSP23Jkbe6Pk9pPGT6NBDs1HVdqaXK
[Wallet Password]> ***********
WIF key export: KxP97gujib35PBEnTq78e5NmYVbeaosU4AdguDzZ4tyf6a7W32UM
neo>
複製代碼

導出 NEP2 密碼保護的私鑰

您能夠將地址導出爲 NEP2 加密私鑰,以下所示:

neo> export nep2 AStZHy8E6StCqYQbzMqi4poH7YNDHQKxvt
[Wallet Password]> ***********
[Key Password 1]> ******************
[Key Password 2]> ******************
NEP2 key export: 6PYVPVe1fQznphjbUxXP9KZJqPMVnVwCx5s5pr5axRJ8uHkMtZg97eT5kL
neo>
複製代碼

導入NEP2密碼保護的私鑰

您能夠將地址導入爲 NEP2 加密私鑰,以下所示:

neo> import nep2 6PYVPVe1fQznphjbUxXP9KZJqPMVnVwCx5s5pr5axRJ8uHkMtZg97eT5kL
[Key Password]> ******************
Imported nep2 key: 6PYVPVe1fQznphjbUxXP9KZJqPMVnVwCx5s5pr5axRJ8uHkMtZg97eT5kL
Pubkey: 303236323431653765323662333862623731353462386164343934353862393766623163343739373434336463393231633563613537373466353131613262626663
複製代碼

導入 watch only 地址

watch only 是一種您沒有公鑰但想觀察的地址。watch only 地址能夠像普通地址同樣刪除。

neo> import watch_addr AStZHy8E6StCqYQbzMqi4poH7YNDHQKxvt
neo>
複製代碼

導入智能合約地址

您可能想要使用已部署的智能合約中的資金。根據合約編程,若是其容許您像使用本身的資金同樣使用合約中的資金,則您能夠經過指定合約的script_hash,以及您但願與合約關聯的錢包中地址的公鑰來導入合約地址。合約地址能夠像錢包中的普通地址同樣刪除。

# import contract_addr {script_hash} {pubkey}
neo> import contract_addr 3c62006802d895974069a1d96398a04b4703f0f8 027973267230b7cba0724589653e667ddea7aa8479c01a82bf8dd398cec93508ef
Added contract addres AeU8kTJxynwkT3q9ao8aDFuaRJBkU3AfFG to wallet
neo>
複製代碼

刪除地址

neo> wallet delete_addr AStZHy8E6StCqYQbzMqi4poH7YNDHQKxvt
Deleted address AStZHy8E6StCqYQbzMqi4poH7YNDHQKxvt
neo>
複製代碼

發送資產

從錢包發送

使用如下命令能夠從你的錢包發送資產。使用此命令,發送的資產來自於你的某個地址或者多個地址。 change_address 是你錢包的中的某個地址。

# syntax send {asset_name} {address to} {amount} ( optional: --from-addr={from_addr})
neo> send gas AeU8kTJxynwkT3q9ao8aDFuaRJBkU3AfFG 11
[Password]> ***********
Relayed Tx: 468e294b11a9f65cc5e2c372124877472eebf121befb77ceed23a84862a606d3
neo>
複製代碼

從指定地址發送

也能夠指定一個特定的地址來發送資產,如從合約地址發送資產。

# syntax send {asset_name} {address to} {amount} ( optional: --from-addr={from_addr})
neo> send gas AeU8kTJxynwkT3q9ao8aDFuaRJBkU3AfFG 11 --from-addr=AXjaFSP23Jkbe6Pk9pPGT6NBDs1HVdqaXK
[Password]> ***********
Relayed Tx: a43dfb30af63bd0e5a510b05f02b3d40932af26d4564e040e3812ce78e76ce71
neo>
複製代碼

NEP5 Tokens

導入 NEP5 代幣

您可使用錢包觀察 NEP5 代幣並與之交互,爲此,您須要首先註冊錢包以觀察代幣,以下所示:

neo> import token f8d448b227991cf07cb96a6f9c0322437f1599b9
added token {
    "name": "NEP5 Standard",
    "script_hash": "f8d448b227991cf07cb96a6f9c0322437f1599b9",
    "decimals": 8,
    "symbol": "NEP5",
    "contract address": "AYhE3Svuqdfh1RtzvE8hUhNR7HSpaSDFQg"
}
neo> wallet
Wallet {
    # truncated ...

    "percent_synced": 100,
    "addresses": [
        "AayaivCAcYnM8q79JCrfpRGXrCEHJRN5bV",
        {
            "balances": {
                "c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b": "4051.0",
                "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7": "897.48372409"
            },
            "script_hash": "AXjaFSP23Jkbe6Pk9pPGT6NBDs1HVdqaXK",
            "votes": [],
            "version": 0,
            "is_watch_only": false,
            "tokens": [
                "[f8d448b227991cf07cb96a6f9c0322437f1599b9] NEP5 : 4519175.65580000"
            ],
            "frozen": false
        },
        {
    }
}
複製代碼

在 prompt 中與智能合約交互

查看在 prompt 中與智能合約交互的完整說明: Smart Contracts within the Prompt

獲取 NEO 測試網資產

本節介紹如何經過 NEO 官方申請表 獲取測試網資產。

獲取測試網資產須要兩步:

  1. 在錢包中添加多方簽名地址。
  2. 將資產轉移到你的地址。

添加多方簽名地址

你須要準備好如下信息:

  1. 你從 NEO 收到的電子郵件中提示的公鑰
  2. 你本身錢包裏的公鑰。 打開錢包並在提示符下輸入 wallet 獲取所需信息。
neo> wallet
Wallet {
    ...
    "public_keys": [
        {
            "Address": "ANFLgwKG8Eni9gJmKfM7yFXEaWwoGkSUid",
            "Public Key": "037b8992e8384212f82e05c8836816c0f14dff9528397138731638b17d6357021e" <--- take this
        }
    ],
    ...
}
複製代碼

而後使用以下命令建立多方簽名地址:

neo> import multisig_addr
please specify multisig contract like such: 'import multisig {pubkey in wallet} {minimum # of signatures required} {signing pubkey 1} {signing pubkey 2}...'

neo> import multisig_addr 037b8992e8384212f82e05c8836816c0f14dff9528397138731638b17d6357021e 1 037b8992e8384212f82e05c8836816c0f14dff9528397138731638b17d6357021e 02883118351f8f47107c83ab634dc7e4
ffe29d274e7d3dcf70159c8935ff769beb
[I 180310 16:49:19 UserWallet:191] contract does not exist yet
Added multi-sig contract address ALXEKioZntX73QawcnfcHUDvTVm8qXjAxf to wallet
複製代碼

再次檢查你的錢包,應該能查看到餘額(特別是查看 synced_balances )。若是沒有看到新增餘額,請運行wallet rebuild 並等待它徹底同步後再次嘗試。

neo> wallet
Wallet {
    "path": "test",
    "addresses": [
        {
            "address": "ANFLgwKG8Eni9gJmKfM7yFXEaWwoGkSUid",
            "script_hash": "47028f2a3d33466f29fba10e65c90fd8f3d01e1f",
            "tokens": null
        },
        {
            "version": 0,
            "script_hash": "ALXEKioZntX73QawcnfcHUDvTVm8qXjAxf",
            "frozen": false,
            "votes": [],
            "balances": {
                "0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b": "50.0",
                "0x602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7": "50.0"
            },
            "is_watch_only": false
        }
    ],
    ...
    "synced_balances": [
        "[NEO]: 50.0 ",
        "[NEOGas]: 50.0 "
    ],
    "public_keys": [
        {
            "Address": "ANFLgwKG8Eni9gJmKfM7yFXEaWwoGkSUid",
            "Public Key": "037b8992e8384212f82e05c8836816c0f14dff9528397138731638b17d6357021e"
        },
        {
            "Address": "ALXEKioZntX73QawcnfcHUDvTVm8qXjAxf",
            "Public Key": "037b8992e8384212f82e05c8836816c0f14dff9528397138731638b17d6357021e"
        }
    ],
    ...
}
複製代碼

將資產轉帳到你本身的地址

如今就能夠安裝如下操做將資產轉移到你本身的地址:

neo> send NEO ANFLgwKG8Eni9gJmKfM7yFXEaWwoGkSUid 5 --from-addr=ALXEKioZntX73QawcnfcHUDvTVm8qXjAxf
[Password]> **********
[I 180310 17:02:42 Transaction:611] Verifying transaction: b'c32b0e3d9adbef6720abfad5106dcd2dacb17b31d4f9d32cbcf8ed6e7f566ef3'
Relayed Tx: c32b0e3d9adbef6720abfad5106dcd2dacb17b31d4f9d32cbcf8ed6e7f566ef3
複製代碼

注意參數 --from-addr 指定的是提取資產的多方簽名合約地址。

設置和日誌

neo-python提供一個設置模塊,能夠用來配置如下事情:

  • 網絡:主網、測試網、私網或自定義配置
  • 日誌:

    • 智能合約事件日誌
    • 日誌文件(可切換)
    • Loglevel

要更改設置,導入設置示例:

from neo.Settings import settings
複製代碼

設置網絡

您可使用如下設置方法配置網絡:

settings.setup_mainnet()
settings.setup_testnet()
settings.setup_privnet()
settings.setup(config_file)
複製代碼

neo-python 默認使用測試網。

數據保存路徑

默認狀況下, neo-python 將鏈數據保存在 ~/.neopython/Chains。若是想指定 Chains 目錄放置的路徑,能夠將 --datadir 標誌傳遞給如下任意命令:np-prompt, np-api-server, 或 np-bootstrap。也可使用 settings 模塊手動設置:

settings.set_data_dir('your/path')
複製代碼

日誌

neo-python 使用如下默認方式:

  • 來自全部智能合約的事件都使用loglevel INFO進行記錄
  • loglevel 設置爲 INFO
  • 記錄到 logfile 被取消激活 (prompt.py 記錄到 prompt.log)

智能合約事件

要禁用全部智能合約事件的記錄,參照如下示例:

settings.set_log_smart_contract_events(False)
複製代碼

更改日誌級別

要更改日誌級別,如也顯示DEBUG日誌或只顯示錯誤日誌,參照如下示例:

import logging

# Show everything, including debug logs:
settings.set_loglevel(logging.DEBUG)

# Only show errors:
settings.set_loglevel(logging.ERROR)
複製代碼

在 prompt 中更改

要在 prompt 中更改 log 級別,使用如下命令:

neo> config sc-events on
neo> config sc-events off
複製代碼

配置日誌文件

要啓動日誌文件的記錄功能,參照如下示例:

# Just a single logfile, with no limits or rotation:
settings.set_logfile(your_logfile_path)

# To enable rotation with a maximum of 10MB per file and 3 rotations:
settings.set_logfile(your_logfile_path, 1e7, 3)
複製代碼

記錄自定義代碼

neo-python 使用 logzero 進行記錄。要在現有的 neo 日誌記錄配置中使用 logger,只需從logzero 導入 logger,以下所示:

from logzero import logger

# These log messages are sent to the console
logger.debug("hello")
logger.info("info")
logger.warn("warn")
logger.error("error")

# This is how you'd log an exception
try:
    raise Exception("this is a demo exception")
except Exception as e:
    logger.exception(e)複製代碼

neo.Core.TX.Transaction

本文將詳細介紹 neo.Core.TX.Transaction 模塊。

描述:

交易基本類

用法

從 neo.Core.Transaction 導入交易

class neo.Core.TX.Transaction.ContractTransaction(*args, **kwargs) [source]

class neo.Core.TX.Transaction.Transaction(inputs=[], outputs=[], attributes=[], scripts=[])[source]

Deserialize(reader) [source]

反序列化完整的對象。

參數reader (

neo.IO.BinaryReader

) –

static DeserializeFrom(reader) [source]

反序列化完整的對象。

參數: reader (

neo.IO.BinaryReader

) –

返回值

返回類型Transaction

static DeserializeFromBufer (buffer, offset=0) [source]

從指定的緩衝區反序列化對象實例。

參數

buffer (

bytes
,
bytearray
,
BytesIO

) – (可選)用於建立數據流的數據。

offset – 不使用

返回值

返回類型Transaction

DeserializeUnsigned(reader) [source]

反序列化對象。

參數reader (

neo.IO.BinaryReader

) –

拋出Exception – 若是交易類型錯誤。

DeserializeUnsignedWithoutType(

reader

) [source]

不讀取交易類型數據的反序列化對象。

參數reader (

neo.IO.BinaryReader

) –

GetHashData() [source]

獲取用於散列的數據。

返回值

返回類型:bytes

GetMessage() [source]

獲取用於散列的數據。

返回值

返回類型:bytes

GetScriptHashesForVerifying() [source]

獲取用於驗證交易的腳本散列。

拋出Exception – 若是交易中沒有有效資產。

返回值:UInt160 類型腳本散列。

返回類型:list

GetTransactionResults() [source]

獲取交易的執行結果。

返回值:若是交易沒有引用 list: TransactionResult 對象。

返回類型:無

Hash

獲取交易的散列值。

返回值

返回類型:UInt256

NetworkFee() [source]

獲取網絡手續費

返回值

返回類型:Fixed8

References

獲取全部引用

返回值:Key (UInt256): input PrevHash Value (TransactionOutput): object.

返回類型:dict

ResetHashData() [source]

復位本地保存的散列數據

ResetReferences() [source]

復位本地保存的引用

Scripts

獲取腳本

返回值

返回類型:list

Serialize(

writer

) [source]

序列化對象

參數writer (

neo.IO.BinaryWriter

) –

SerializeUnsigned(

writer

) [source]

序列化對象

參數writer (

neo.IO.BinaryWriter

) –

Size() [source]

獲取對象的總大小(以字節爲單位)。

返回值:大小

返回類型:int

SystemFee() [source]

獲取系統手續費。

返回值:目前爲 0。

返回類型:Fixed8

ToArray() [source]

獲取 self 的字節數據。

返回值

返回類型:byte

ToJson() [source]

將對象成員轉換爲能夠解析爲JSON的字典編碼。

返回值

返回類型:dict

Verify(

mempool

) [source]

驗證交易。

參數mempool

返回值:驗證經過返回 True,不然返回 False。

返回類型:bool

getAllInputs() [source]

獲取輸入

返回值

返回類型:list

withdraw_hold

= None

交易的 docstring

class neo.Core.TX.Transaction.TransactionInput(prevHash=None, prevIndex=None) [source]

TransactionInput 的 docstring

Deserialize(

reader

) [source]

反序列化全對象

參數reader (

neo.IO.BinaryReader

) –

Serialize(

writer

) [source]

序列化對象

參數writer (

neo.IO.BinaryWriter

) –

ToJson() [source]

將對象成員轉換爲能夠解析爲JSON的字典編碼。

返回值

返回類型:dict

ToString() [source]

獲取對象的字符串表示形式。

返回:PrevHash:PrevIndexReturn type:str

class neo.Core.TX.Transaction.TransactionOutput (AssetId=None, Value=None, script_hash=None)[source]

Address

獲取交易的公共地址。

返回值:表明地址的 base58 編碼字符串。

返回類型:str

AddressBytes

獲取交易的公共地址。

返回值:base58 編碼的字符串。

返回類型:bytes

AssetId

= None

docstring for TransactionOutput

Deserialize(

reader

) [source]

反序列化完整對象

參數reader (

neo.IO.BinaryReader

) –

Serialize(

writer

) [source]

序列化對象

參數writer (

neo.IO.BinaryWriter

) –

ToJson(

index

) [source]

將對象成員轉換爲能夠解析爲JSON的字典編碼。 :param index: 交易輸出的索引 :type index: int

返回值

返回類型:dict

與智能合約交互

neo-python 的一個常見用例是與智能合約進行交互。典型的智能合約事件包括 Runtime.Notify, Runtime.Log, 執行成功或失敗,以及 Storage.GET/PUT/DELETE

事件類型

如下列出了當前 NEO-Python 能夠處理的智能合約事件類型:

RUNTIME_NOTIFY = "SmartContract.Runtime.Notify"
RUNTIME_LOG = "SmartContract.Runtime.Log"

EXECUTION = "SmartContract.Execution.*"
EXECUTION_INVOKE = "SmartContract.Execution.Invoke"
EXECUTION_SUCCESS = "SmartContract.Execution.Success"
EXECUTION_FAIL = "SmartContract.Execution.Fail"

VERIFICATION = "SmartContract.Verification.*"
VERIFICATION_SUCCESS = "SmartContract.Verification.Success"
VERIFICATION_FAIL = "SmartContract.Verification.Fail"

STORAGE = "SmartContract.Storage.*"
STORAGE_GET = "SmartContract.Storage.Get"
STORAGE_PUT = "SmartContract.Storage.Put"
STORAGE_DELETE = "SmartContract.Storage.Delete"

CONTRACT = "SmartContract.Contract.*"
CONTRACT_CREATED = "SmartContract.Contract.Create"
CONTRACT_MIGRATED = "SmartContract.Contract.Migrate"
CONTRACT_DESTROY = "SmartContract.Contract.Destroy"
複製代碼

當這些事件發生在接收區塊時,由neo.EventHub調度一個 SmartContractEvent實例。

SmartContractEvent

事件處理程序老是收到參數 neo.EventHub.SmartContractEvent 的一個實例,其包含有關當前事件的全部信息。SmartContractEvent 具備如下屬性:

屬性 數據類型 描述
event_type str neo.EventHub.SmartContractEvent中的一個事件類型
contract_hash UInt160 合約散列值
tx_hash UInt256 交易散列值
block_number int 收到此事件的區塊號
event_payload object[] 對象列表,取決於智能合約發出的數據類型(例如,使用Runtime.Notify)。
execution_success bool 方法調用是否成功
test_mode bool 這個事件是否由本地TestInvoke調度,而不是從區塊連接收

neo.contrib.smartcontract.SmartContract

開發人員可使用neo.contrib.smartcontract.SmartContract 輕鬆訂閱這些事件。如下是一個使用散列6537b4bd100e514119e3a7ab49d520d20ef2c2a4監聽 Runtime.Notify智能合約事件的示例:

from neo.contrib.smartcontract import SmartContract

smart_contract = SmartContract("6537b4bd100e514119e3a7ab49d520d20ef2c2a4")

@smart_contract.on_notify
def sc_notify(event):
    print("SmartContract Runtime.Notify event:", event)

    # Make sure that the event payload list has at least one element.
    if not len(event.event_payload):
        return

    # The event payload list has at least one element. As developer of the smart contract
    # you should know what data-type is in the bytes, and how to decode it. In this example,
    # it's just a string, so we decode it with utf-8:
    print("- payload part 1:", event.event_payload[0].decode("utf-8"))
複製代碼

目前可用的裝飾器以下:

裝飾器 智能合約事件
@on_any 全部事件
@on_notify Runtime.Notify
@on_log Runtime.Log
@on_storage 存儲 PUT, GET 和 DELETE
@on_execution 方法調用,成功或失敗

如下示例顯示如何監聽全部事件並區分代碼中的事件類型:

from neo.contrib.smartcontract import SmartContract
from neo.EventHub import SmartContractEvent

smart_contract = SmartContract("6537b4bd100e514119e3a7ab49d520d20ef2c2a4")

@smart_contract.on_all
def handle_sc_event(event):
    print("SmartContract Runtime.Notify event:", event)

    # Check if it is a Runtime.Notify event
    if event.event_type == SmartContractEvent.RUNTIME_NOTIFY:
        # Exit if an empty payload list
        if not len(event.event_payload):
            return

        # Decode the first payload item and print it
        print("- payload part 1:", event.event_payload[0].decode("utf-8"))
複製代碼

Prompt 中的智能合約交互

neo-python 最使人欣喜的功能之一是可以在 NEO 平臺上快速構建、測試、導入以及調用智能合約。本節將提供在 Prompt 裏操做智能合約的基本指南。

建立合約

首先,你須要在 prompt 裏建立一個智能合約。該方法使用 neo-boa 編譯器編譯智能合約並將其保存爲 .avm 格式。

在 prompt 裏建立或導入智能合約時,最好使用相對路徑 (相對於 neo-python 安裝目錄),儘管絕對路徑可能也有效。

如下是一個示例: sample1.py

def Main():
  print("Hello World")
  return True
neo> build docs/source/example/sample1.py
Saved output to docs/source/example/sample1.avm
複製代碼

以上命令只是簡單地編譯文件,以後你就能夠在 Prompt 或者 NEO-GUI 中導入已編譯的 .avm 文件。

建立並測試合約

建立並測試命令更加經常使用,它能夠在編譯文件後執行和檢測結果,只是其語法要複雜些。

查看 ContractParameterType 列表:ContractParameterTypes

該命令語法爲:

build path/to/file.py test {input_params} {return_type} {needs_storage} {needs_dynamic_invoke} param1 param2 etc..where {input_params} and {return_type}

  • {input_params} :輸入一個或一系列 ContractParameterType,例如 0710 表示智能合約接收一個字符串和列表。
  • {return_type}:輸入一個 ContractParameterType, 例如 02 表示智能合約返回一個整數。
  • {needs_storage} :輸入布爾值 TrueFalse 指示智能合約是否使用 Storage.Get/Put/Delete 互操做 API。
  • {needs_dynamic_invoke} :輸入布爾值TrueFalse ,用於指示智能合約是否調用另外一個運行時才知道地址的合約,一般爲 False
  • params1 params2 etc... :輸入你用來測試的參數。

所以要建立並測試 sample1.py,命令格式爲 build docs/source/example/sample1.py test '' 01 False False,其中 '' 表示不接收參數,01 表示返回一個布爾值。在 Prompt 中輸入以下:

neo> build docs/source/example/sample1.py test '' 01 False false
Saved output to docs/source/example/sample1.avm
please open a wallet to test built contract
neo>
複製代碼

如今咱們能夠打開錢包來測試以前建立的合約了。注意,打開錢包後,你可使用向上箭頭鍵選擇以前輸入過的命令。

neo> open wallet Wallets/awesome
[password]> ***********
Opened wallet at Wallets/awesome
neo> build docs/source/example/sample1.py test '' 01 False false
Saved output to docs/source/example/sample1.avm
[I 180302 22:22:58 Invoke:482] Used 0.016 Gas

-----------------------------------------------------------
Calling docs/source/example/sample1.py with arguments []
Test deploy invoke successful
Used total of 11 operations
Result [{'type': 'Boolean', 'value': True}]
Invoke TX gas cost: 0.0001
-------------------------------------------------------------

neo>
複製代碼

到此咱們就完成了第一個智能合約的建立和測試。若是想要查看該智能合約做爲整數時的結果,能夠更改 return_type ,你會獲得以下所示的輸出:

neo> build docs/source/example/sample1.py test '' 02 False False
Saved output to docs/source/example/sample1.avm
[I 180302 22:25:09 Invoke:482] Used 0.016 Gas

-----------------------------------------------------------
Calling docs/source/example/sample1.py with arguments []
Test deploy invoke successful
Used total of 11 operations
Result [{'type': 'Integer', 'value': 1}]
Invoke TX gas cost: 0.0001
-------------------------------------------------------------

neo>
複製代碼

在上例中你會發現,儘管合約中包含了 print 命令,卻並無打印輸出任何字符。要解決這個問題,讓咱們打開智能合約事件並再次運行一遍。

neo>
neo> config sc-events on
Smart contract event logging is now enabled
neo> build docs/source/example/sample1.py test '' 01 False False
Saved output to docs/source/example/sample1.avm
[I 180302 22:56:19 EventHub:71] [test_mode][SmartContract.Contract.Create] [09a129673c61917593cb4b57dce066688f539d15] ['{\n "version": 0,\n "code": {\n "hash": "0x09a129673c61917593cb4b57dce066688f539d15",\n "script": "54c56b0b48656c6c6f20576f726c64680f4e656f2e52756e74696d652e4c6f67516c7566",\n "parameters": "",\n "returntype": 1\n },\n "name": "test",\n "code_version": "test",\n "author": "test",\n "email": "test",\n "description": "test",\n "properties": {\n "storage": false,\n "dynamic_invoke": false\n }\n}']
[I 180302 22:56:19 EventHub:71] [test_mode][SmartContract.Runtime.Log] [09a129673c61917593cb4b57dce066688f539d15] [b'Hello World']
[I 180302 22:56:19 EventHub:71] [test_mode][SmartContract.Execution.Success] [09a129673c61917593cb4b57dce066688f539d15] [1]
[I 180302 22:56:20 Invoke:482] Used 0.016 Gas

-----------------------------------------------------------
Calling docs/source/example/sample1.py with arguments []
Test deploy invoke successful
Used total of 11 operations
Result [{'type': 'Boolean', 'value': True}]
Invoke TX gas cost: 0.0001
-------------------------------------------------------------

neo>
複製代碼

當咱們在 prompt 裏使用 config sc-events on命令打開 SmartContractEvent 日誌功能後,再次運行相同的命令會發現此次輸出結果中多出了三行。

  • SmartContract.Contract.Create 是在 VM 中建立了你的智能合約事件的事件
  • SmartContract.Runtime.Log 是輸出 Hello World 的事件
  • SmartContract.Execution.Success 表示智能合約成功執行完成

下面讓咱們嘗試一個複雜點的合約,sample2.py:

def Main(operation, a, b):

    if operation == 'add':
        return a + b

    elif operation == 'sub':
        return a - b

    elif operation == 'mul':
        return a * b

    elif operation == 'div':
        return a / b

    else:
        return -1
複製代碼

建立並指定幾個參數運行該合約:

neo> build docs/source/example/sample2.py test 070202 02 False False
Saved output to docs/source/example/sample2.avm
[E 180302 22:30:01 ExecutionEngine:825] COULD NOT EXECUTE OP: Invalid list operation b'z' ROLL
[E 180302 22:30:01 ExecutionEngine:826] Invalid list operation
Traceback (most recent call last):
  File "/Users/thomassaunders/Workshop/neo-python/neo/VM/ExecutionEngine.py", line 823, in StepInto
    self.ExecuteOp(op, self.CurrentContext)
  File "/Users/thomassaunders/Workshop/neo-python/neo/VM/ExecutionEngine.py", line 276, in ExecuteOp
    estack.PushT(estack.Remove(n))
  File "/Users/thomassaunders/Workshop/neo-python/neo/VM/RandomAccessStack.py", line 57, in Remove
    raise Exception("Invalid list operation")
Exception: Invalid list operation
[I 180302 22:30:01 InteropService:93] Trying to get big integer Array: ['None', 'None', 'None', 'None', 'None', 'None', 'None', 'None', 'None', 'None', 'None', 'None', 'None', 'None']
複製代碼

出現以上結果的緣由是,咱們測試的合約要求提供更多的參數。若是你在建立並測試合約時遇到類似的錯誤信息,頗有多是相同的緣由。讓咱們輸入一些參數再嘗試一次:

neo> build docs/source/example/sample2.py test 070202 02 False False add 1 2
Saved output to docs/source/example/sample2.avm
[I 180302 22:32:06 Invoke:482] Used 0.033 Gas

-----------------------------------------------------------
Calling docs/source/example/sample2.py with arguments ['add', '1', '2']
Test deploy invoke successful
Used total of 39 operations
Result [{'type': 'Integer', 'value': 3}]
Invoke TX gas cost: 0.0001
-------------------------------------------------------------

neo>
neo> build docs/source/example/sample2.py test 070202 02 False False mul -1 20000
Saved output to docs/source/example/sample2.avm
[I 180302 22:33:36 Invoke:482] Used 0.041 Gas

-----------------------------------------------------------
Calling docs/source/example/sample2.py with arguments ['mul', '-1', '20000']
Test deploy invoke successful
Used total of 53 operations
Result [{'type': 'Integer', 'value': -20000}]
Invoke TX gas cost: 0.0001
-------------------------------------------------------------

neo>
複製代碼

此次好多了。接下來讓咱們作些更有用的嘗試,咱們將作一個簡單的地址餘額跟蹤器。

from boa.interop.Neo.Storage import Get,Put,Delete,GetContext

def Main(operation, addr, value):


    if not is_valid_addr(addr):
        return False

    ctx = GetContext()

    if operation == 'add':
        balance = Get(ctx, addr)
        new_balance = balance + value
        Put(ctx, addr, new_balance)
        return new_balance

    elif operation == 'remove':
        balance = Get(ctx, addr)
        Put(ctx, addr, balance - value)
        return balance - value

    elif operation == 'balance':
        return Get(ctx, addr)

    return False

def is_valid_addr(addr):

    if len(addr) == 20:
        return True
    return False
複製代碼

咱們將使用 add 進行一個測試,給錢包裏的一個地址添加一些值。你會注意到,當你在錢包裏輸入任意地址時會自動完成輸入,這可能會產生誤導。當經過 prompt 將地址發送到 SC 時,它會自動轉換爲 ByteArray 以便使用。 所以方法簽名看起來像 070502 或 StringByteArrayInteger

咱們使用 True 來表示使用智能合約的 Storage API。

neo> build docs/source/example/sample3.py test 070502 02 True False add AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy 3
Saved output to docs/source/example/sample3.avm
[I 180302 23:04:33 Invoke:482] Used 1.174 Gas

-----------------------------------------------------------
Calling docs/source/example/sample3.py with arguments ['add', 'AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy', '3']
Test deploy invoke successful
Used total of 106 operations
Result [{'type': 'Integer', 'value': 3}]
Invoke TX gas cost: 0.0001
-------------------------------------------------------------

neo>
複製代碼

再次調用,將看到咱們的測試調用保留了存儲庫中的值。

neo> build docs/source/example/sample3.py test 070502 02 True False add AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy 3
Saved output to docs/source/example/sample3.avm
[I 180302 23:04:33 Invoke:482] Used 1.174 Gas

-----------------------------------------------------------
Calling docs/source/example/sample3.py with arguments ['add', 'AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy', '3']
Test deploy invoke successful
Used total of 106 operations
Result [{'type': 'Integer', 'value': 6}]
Invoke TX gas cost: 0.0001
-------------------------------------------------------------

neo>
複製代碼

如今除去一些值:

neo> build docs/source/example/sample3.py test 070502 02 True False remove AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy 2
Saved output to docs/source/example/sample3.avm
[I 180302 23:09:21 Invoke:482] Used 1.176 Gas

-----------------------------------------------------------
Calling docs/source/example/sample3.py with arguments ['remove', 'AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy', '2']
Test deploy invoke successful
Used total of 109 operations
Result [{'type': 'Integer', 'value': 4}]
Invoke TX gas cost: 0.0001
-------------------------------------------------------------

neo>
複製代碼

你也能夠爲地址傳入一個 ByteArray 對象,並測試是否 is_valid_addr 在任何事情發生前返回 False,這會被解析爲 0:

neo> build docs/source/example/sample3.py test 070502 02 True False add bytearray(b'\x00\x01\x02\x03') 4
Saved output to docs/source/example/sample3.avm
[I 180302 23:12:43 Invoke:482] Used 0.041 Gas

-----------------------------------------------------------
Calling docs/source/example/sample3.py with arguments ['add', "bytearray(b'\\x00\\x01\\x02\\x03')", '4']
Test deploy invoke successful
Used total of 52 operations
Result [{'type': 'Integer', 'value': 0}]
Invoke TX gas cost: 0.0001
-------------------------------------------------------------

neo>
複製代碼

請注意,以可讀格式 (

AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy

) 發送地址與發送地址的腳本哈希是同樣的。咱們將經過獲取餘額進行嘗試。因爲智能合約指望第三個參數,我在最後添加了一個額外的 0 做爲最後一個參數:

neo> build docs/source/example/sample3.py test 070502 02 True False balance bytearray(b'\x03\x19\xe0)\xb9%\x85w\x90\xe4\x17\x85\xbe\x9c\xce\xc6\xca\xb1\x98\x96') 0
Saved output to docs/source/example/sample3.avm
[I 180302 23:16:23 Invoke:482] Used 0.162 Gas

-----------------------------------------------------------
Calling docs/source/example/sample3.py with arguments ['balance', "bytearray(b'\\x03\\x19\\xe0)\\xb9%\\x85w\\x90\\xe4\\x17\\x85\\xbe\\x9c\\xce\\xc6\\xca\\xb1\\x98\\x96')", '0']
Test deploy invoke successful
Used total of 87 operations
Result [{'type': 'Integer', 'value': 4}]
Invoke TX gas cost: 0.0001
-------------------------------------------------------------

neo>
複製代碼

導入智能合約

導入智能合約有點相似 build .. test 命令,但你不須要發送任何參數。命令格式爲:

import contract path/to/sample2.avm {input_params} {return_type} {needs_storage} {needs_dynamic_invoke}

運行該命令後,若是一切正常系統會提示你添加合約相關的元數據。完成後你就能夠選擇在網絡中實際部署將該合約。請注意部署合約將花費 GAS。

neo>
neo> import contract docs/source/example/sample2.avm 070202 02 False False
Please fill out the following contract details:
[Contract Name] > Sample Calculator
[Contract Version] > .01
[Contract Author] > Thomas Saunders
[Contract Email] > tom@cityofzion.io
[Contract Description] > A test calculator contract
Creating smart contract....
               Name: A test calculator contract
            Version: .01
             Author: tom@cityofzion.io
              Email: tom@cityofzion.io
        Description: A test calculator contract
      Needs Storage: False
Needs Dynamic Invoke: False
{
  "hash": "0x86d58778c8d29e03182f38369f0d97782d303cc0",
  "script": "5ec56b6a00527ac46a51527ac46a52527ac46a00c3036164649c640d006a51c36a52c3936c7566616a00c3037375629c640d006a51c36a52c3946c7566616a00c3036d756c9c640d006a51c36a52c3956c7566616a00c3036469769c640d006a51c36a52c3966c7566614f6c7566006c7566",
  "parameters": "070202",
  "returntype": "02"
}
Used 100.0 Gas

-------------------------------------------------------------------------------------------------------------------------------------
Test deploy invoke successful
Total operations executed: 11
Results:
[<neo.Core.State.ContractState.ContractState object at 0x11435d2e8>]
Deploy Invoke TX GAS cost: 90.0
Deploy Invoke TX Fee: 0.0
-------------------------------------------------------------------------------------------------------------------------------------

Enter your password to continue and deploy this contract
[password]>
複製代碼

從這裏開始,若是你肯定要花費 GAS 來部署合約,輸入密碼:

Enter your password to continue and deploy this contract
[password]> ***********
[I 180302 23:46:23 Transaction:611] Verifying transaction: b'f8ad261d28bf4bc5544e47f9bc3fff85f85ee674f14162dac81dd56bf73cf0a3'
Relayed Tx: f8ad261d28bf4bc5544e47f9bc3fff85f85ee674f14162dac81dd56bf73cf0a3
neo>
複製代碼

如今你已將合約部署到網絡。 若是一切順利,將很快部署。 要肯定什麼時候部署完成,在區塊鏈中搜索 txid 或合約哈希。

neo> tx f8ad261d28bf4bc5544e47f9bc3fff85f85ee674f14162dac81dd56bf73cf0a3
{
  "txid": "0xf8ad261d28bf4bc5544e47f9bc3fff85f85ee674f14162dac81dd56bf73cf0a3",
  "type": "InvocationTransaction",
  "version": 1,
  "attributes": [],
  [ MORE Output Omitted ]

neo> contract 0x86d58778c8d29e03182f38369f0d97782d303cc0
{
    "version": 0,
    "code": {
        "hash": "0x86d58778c8d29e03182f38369f0d97782d303cc0",
        "script": "5ec56b6a00527ac46a51527ac46a52527ac46a00c3036164649c640d006a51c36a52c3936c7566616a00c3037375629c640d006a51c36a52c3946c7566616a00c3036d756c9c640d006a51c36a52c3956c7566616a00c3036469769c640d006a51c36a52c3966c7566614f6c7566006c7566",
        "parameters": "070202",
        "returntype": 2
    },
    "name": "A test calculator contract",
    "code_version": ".01",
    "author": "tom@cityofzion.io",
    "email": "tom@cityofzion.io",
    "description": "A test calculator contract",
    "properties": {
        "storage": false,
        "dynamic_invoke": false
    }
}

neo>
複製代碼

如今你已經在網絡上部署了合同,可使用真正的 InvocationTransactions 與它進行交互。

測試調用合約

一旦部署了合約,你就沒法再像使用 build .. test 命令那樣進行交互,更改和構建,但最好是使用 testinvoke 來肯定鏈上的工做。

如今咱們已經部署了

Calculator Contract

,只要知道它的腳本哈希,就可使用 testinvoke 命令與它進行交互。命令格式是 testinvoke {contract_hash} param1 param2 ..

neo> testinvoke 0x86d58778c8d29e03182f38369f0d97782d303cc0 add 1 2
Used 0.033 Gas

-------------------------------------------------------------------------------------------------------------------------------------
Test invoke successful
Total operations: 39
Results ['Integer: 3 ']
Invoke TX GAS cost: 0.0
Invoke TX fee: 0.0001
-------------------------------------------------------------------------------------------------------------------------------------

Enter your password to continue and invoke on the network

[password]>
複製代碼

這個調用只在本地完成,只有在你輸入密碼後才能運行。若是你不但願在網絡中調用,只需輸入錯誤密碼便可取消。這裏咱們取消調用,而後設置 config sc-events on 以確切地查看測試調用時發生的狀況,而後將其發送到網絡:

Enter your password to continue and invoke on the network

[password]> **
Incorrect password
neo>
neo> config sc-events on
Smart contract event logging is now enabled
neo>
neo> testinvoke 0x86d58778c8d29e03182f38369f0d97782d303cc0 add 1 2
[I 180303 07:38:58 EventHub:71] [test_mode][SmartContract.Execution.Success] [86d58778c8d29e03182f38369f0d97782d303cc0] [3]
Used 0.033 Gas

-------------------------------------------------------------------------------------------------------------------------------------
Test invoke successful
Total operations: 39
Results ['Integer: 3 ']
Invoke TX GAS cost: 0.0
Invoke TX fee: 0.0001
-------------------------------------------------------------------------------------------------------------------------------------

Enter your password to continue and invoke on the network

[password]> ***********
[I 180303 07:39:04 Transaction:611] Verifying transaction: b'e0f4251a83f7081fb6fd94ce884d12b0bb597c1c1b3f1a89f07db68e114f4fa2'
[I 180303 07:39:04 EventHub:89] [SmartContract.Verification.Success][433121] [4c896601a99d58e22c32dcadd24974ca24c10587] [tx e0f4251a83f7081fb6fd94ce884d12b0bb597c1c1b3f1a89f07db68e114f4fa2] [True]
Relayed Tx: e0f4251a83f7081fb6fd94ce884d12b0bb597c1c1b3f1a89f07db68e114f4fa2
neo>
neo> [I 180303 07:39:31 EventHub:89] [SmartContract.Execution.Success][433122] [86d58778c8d29e03182f38369f0d97782d303cc0] [tx e0f4251a83f7081fb6fd94ce884d12b0bb597c1c1b3f1a89f07db68e114f4fa2] [3]
neo>
複製代碼

在這裏請注意如下幾點:

  1. 當使用 sc-events on 測試調用時,將看到
    SmartContract.Execution.Success
    事件,而且將看到該事件顯示已在 test_mode 完成測試。
  2. 如今你將看到 SmartContract.Verification.Success 事件。 這說明 TX 已正確簽名並將經過驗證,由於它被傳遞到其餘節點並最終將達成一致。
  3. 將 InvocationTransaction 發送到網絡後,你將得到一個TX ID,能夠用來查找調用。
  4. 在網絡處理完 TX 以後,本地 VM 會運行你的調用,此次不在 test_mode 中,你將再次看到SmartContract.Execution.Success 事件。

Application vs. Verification

如今,你已經完成了合約的構建、測試和測試調用,你可能會問,這個驗證步驟是什麼,如何與之程序化的交互?讓咱們從以前一個跟蹤餘額的例子開始,限制僅有一個全部者地址能夠對其執行操做。如下是智能合約代碼:

from boa.interop.Neo.Runtime import GetTrigger,CheckWitness
from boa.interop.Neo.Storage import Get,Put,Delete,GetContext
from boa.interop.Neo.TriggerType import Application, Verification

OWNER = b'\x03\x19\xe0)\xb9%\x85w\x90\xe4\x17\x85\xbe\x9c\xce\xc6\xca\xb1\x98\x96'

def Main(operation, addr, value):

    print("Running Sample v4")
    trigger = GetTrigger()

    # This determines that the SC is runnning in Verification mode
    # This determines whether the TX will be relayed to the rest of the network
    # The `Verification` portion of SC is *read-only*, so calls to `Storage.Put` will fail.
    # You can, however, use `Storage.Get`
    if trigger == Verification():

        print("Running Verification!")

        # This routine is: if the invoker ( or the Address that signed the contract ) is not OWNER,
        # Then we return False, and the TX will not be relayed to the network
        # Otherwise, we know the owner address signed the TX and return True
        is_owner = CheckWitness(OWNER)

        if is_owner:
            print("Is Owner!")
            return True

        print("Not Owner")

        return False

    elif trigger == Application():

        print("Running Application!")

          if not is_valid_addr(addr):
              print("Not Valid Address")
              return False

          ctx = GetContext()

          if operation == 'add':
              balance = Get(ctx, addr)
              new_balance = balance + value
              Put(ctx, addr, new_balance)
              return new_balance

          elif operation == 'remove':
              balance = Get(ctx, addr)
              Put(ctx, addr, balance - value)
              return balance - value

          elif operation == 'balance':
              return Get(ctx, addr)

          return False

    return False


def is_valid_addr(addr):

  if len(addr) == 20:
      return True
  return False
複製代碼

OWNER 就是咱們以前用過的ByteArray, 是咱們使用的錢包裏的地址。上例將使用boa.interop.Neo.Runtime.CheckWitness 方法來驗證簽名 InvocationTransaction 的錢包是否與 OWNER 的同樣。首先,構建合約並測試其是否正常執行。咱們將打開 sc-events以便準確地看到執行過程。

neo>
neo> build docs/source/neo/example/sample4.py test 070202 02 True False add AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy 7
Saved output to docs/source/neo/example/sample4.avm
[I 180303 08:25:12 EventHub:71] [test_mode][SmartContract.Contract.Create] [562d6c29209dc96432c6868621fe489cedd05222] ['{\n "version": 0,\n "code": {\n "hash": "0x562d6c29209dc96432c6868621fe489cedd05222",\n "script": "0122c56b6a00527ac46a51527ac46a52527ac4140319e029b925857790e41785be9ccec6cab198966a53527ac41152756e6e696e672053616d706c65207634680f4e656f2e52756e74696d652e4c6f6768164e656f2e52756e74696d652e47657454726967676572616a54527ac46a54c301009c6492001552756e6e696e6720566572696669636174696f6e21680f4e656f2e52756e74696d652e4c6f676a53c368184e656f2e52756e74696d652e436865636b5769746e657373616a55527ac46a55c3642200094973204f776e657221680f4e656f2e52756e74696d652e4c6f67516c756661094e6f74204f776e6572680f4e656f2e52756e74696d652e4c6f67006c7566616a54c301109c6454011452756e6e696e67204170706c69636174696f6e21680f4e656f2e52756e74696d652e4c6f676a51c3652d01632a00114e6f742056616c69642041646472657373680f4e656f2e52756e74696d652e4c6f67006c75666168164e656f2e53746f726167652e476574436f6e74657874616a56527ac46a00c3036164649c6450006a56c36a51c37c680f4e656f2e53746f726167652e476574616a57527ac46a57c36a52c3936a58527ac46a56c36a51c36a58c35272680f4e656f2e53746f726167652e507574616a58c36c7566616a00c30672656d6f76659c644c006a56c36a51c37c680f4e656f2e53746f726167652e476574616a57527ac46a56c36a51c36a57c36a52c3945272680f4e656f2e53746f726167652e507574616a57c36a52c3946c7566616a00c30762616c616e63659c641f006a56c36a51c37c680f4e656f2e53746f726167652e476574616c756661006c756656c56b6a00527ac46a00c3c001149c640700516c756661006c7566",\n "parameters": "070202",\n "returntype": 2\n },\n "name": "test",\n "code_version": "test",\n "author": "test",\n "email": "test",\n "description": "test",\n "properties": {\n "storage": true,\n "dynamic_invoke": false\n }\n}']
[I 180303 08:25:12 EventHub:71] [test_mode][SmartContract.Runtime.Log] [562d6c29209dc96432c6868621fe489cedd05222] [b'Running Sample v4']
[I 180303 08:25:12 EventHub:71] [test_mode][SmartContract.Runtime.Log] [562d6c29209dc96432c6868621fe489cedd05222] [b'Running Application!']
[I 180303 08:25:12 EventHub:71] [test_mode][SmartContract.Storage.Get] [562d6c29209dc96432c6868621fe489cedd05222] ['AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy -> 0']
[I 180303 08:25:12 EventHub:71] [test_mode][SmartContract.Storage.Put] [562d6c29209dc96432c6868621fe489cedd05222] ['AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy -> 7']
[I 180303 08:25:12 EventHub:71] [test_mode][SmartContract.Execution.Success] [562d6c29209dc96432c6868621fe489cedd05222] [7]
[I 180303 08:25:12 Invoke:482] Used 1.191 Gas

-----------------------------------------------------------
Calling docs/source/neo/example/sample4.py with arguments ['add', 'AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy', '7']
Test deploy invoke successful
Used total of 136 operations
Result [{'type': 'Integer', 'value': 7}]
Invoke TX gas cost: 0.0001
-------------------------------------------------------------

neo>
複製代碼

到這裏執行過程跟以前差很少。咱們添加了一些新的 print 語句,經過這些語句能看到智能合約的 Verification部分從未執行。另外,合約中的 SmartContract.Storage.\* 事件對調試很是有用。目前,爲了與智能合約的Verification 階段交互,你須要部署並使用 testinvoke。假設你已經構建並導入合約,將獲得以下結果:

neo> contract 2e80ee491a0a54c9bbb0f791672050f9ab367767
{
    "version": 0,
    "code": {
        "hash": "0x2e80ee491a0a54c9bbb0f791672050f9ab367767",
        "script": "0123c56b6a00527ac46a51527ac46a52527ac4140319e029b925857790e41785be9ccec6cab198966a53527ac41152756e6e696e672053616d706c65207634680f4e656f2e52756e74696d652e4c6f6768164e656f2e52756e74696d652e47657454726967676572616a54527ac46a54c3680f4e656f2e52756e74696d652e4c6f676a54c301009c6492001552756e6e696e6720566572696669636174696f6e21680f4e656f2e52756e74696d652e4c6f676a53c368184e656f2e52756e74696d652e436865636b5769746e657373616a55527ac46a55c3642200094973204f776e657221680f4e656f2e52756e74696d652e4c6f67516c756661094e6f74204f776e6572680f4e656f2e52756e74696d652e4c6f67006c7566616a54c301109c6454011452756e6e696e67204170706c69636174696f6e21680f4e656f2e52756e74696d652e4c6f676a51c3652d01632a00114e6f742056616c69642041646472657373680f4e656f2e52756e74696d652e4c6f67006c75666168164e656f2e53746f726167652e476574436f6e74657874616a56527ac46a00c3036164649c6450006a56c36a51c37c680f4e656f2e53746f726167652e476574616a57527ac46a57c36a52c3936a58527ac46a56c36a51c36a58c35272680f4e656f2e53746f726167652e507574616a58c36c7566616a00c30672656d6f76659c644c006a56c36a51c37c680f4e656f2e53746f726167652e476574616a57527ac46a56c36a51c36a57c36a52c3945272680f4e656f2e53746f726167652e507574616a57c36a52c3946c7566616a00c30762616c616e63659c641f006a56c36a51c37c680f4e656f2e53746f726167652e476574616c756661006c756656c56b6a00527ac46a00c3c001149c640700516c756661006c7566",
        "parameters": "070202",
        "returntype": 2
    },
    "name": "test",
    "code_version": "test",
    "author": "test",
    "email": "test",
    "description": "test",
    "properties": {
        "storage": true,
        "dynamic_invoke": false
    }
}

neo>
複製代碼

再次測試調用一遍。

neo> testinvoke 0x2e80ee491a0a54c9bbb0f791672050f9ab367767 add AMUUgxnLhGxNSATinNp8gKmndqM1BxDZHR 42
[I 180303 09:08:14 EventHub:71] [test_mode][SmartContract.Runtime.Log] [2e80ee491a0a54c9bbb0f791672050f9ab367767] [b'Running Sample v4']
[I 180303 09:08:14 EventHub:71] [test_mode][SmartContract.Runtime.Log] [2e80ee491a0a54c9bbb0f791672050f9ab367767] [b'\x10']
[I 180303 09:08:14 EventHub:71] [test_mode][SmartContract.Runtime.Log] [2e80ee491a0a54c9bbb0f791672050f9ab367767] [b'Running Application!']
[I 180303 09:08:14 EventHub:71] [test_mode][SmartContract.Storage.Get] [2e80ee491a0a54c9bbb0f791672050f9ab367767] ['AMUUgxnLhGxNSATinNp8gKmndqM1BxDZHR -> 0']
[I 180303 09:08:14 EventHub:71] [test_mode][SmartContract.Storage.Put] [2e80ee491a0a54c9bbb0f791672050f9ab367767] ['AMUUgxnLhGxNSATinNp8gKmndqM1BxDZHR -> 42']
[I 180303 09:08:14 EventHub:71] [test_mode][SmartContract.Execution.Success] [2e80ee491a0a54c9bbb0f791672050f9ab367767] [42]
Used 1.194 Gas

-------------------------------------------------------------------------------------------------------------------------------------
Test invoke successful
Total operations: 140
Results ['Integer: 42 ']
Invoke TX GAS cost: 0.0
Invoke TX fee: 0.0001
-------------------------------------------------------------------------------------------------------------------------------------

Enter your password to continue and invoke on the network

[password]>
複製代碼

這裏仍是沒有看到任何驗證事件。一旦咱們輸入密碼將其轉發到網絡,將看到以下結果:

[password]> ***********
[I 180303 08:36:52 Transaction:611] Verifying transaction: b'0fd755e847a5ce54f9894c0c0bbf9303730ac28d8aeacdaddb2f912a2a3fcd40'
[I 180303 08:35:38 EventHub:71] [test_mode][SmartContract.Runtime.Log] [562d6c29209dc96432c6868621fe489cedd05222] [b'Running Sample v4']
[I 180303 08:35:38 EventHub:71] [test_mode][SmartContract.Runtime.Log] [562d6c29209dc96432c6868621fe489cedd05222] [b'Running Verification!']
[I 180303 08:35:38 EventHub:71] [test_mode][SmartContract.Runtime.Log] [562d6c29209dc96432c6868621fe489cedd05222] [b'Is Owner!']
[I 180303 08:36:52 EventHub:89] [SmartContract.Verification.Success][433331] [f64d628af19f53a6b8226a44c93182eff6fcb222] [tx 0fd755e847a5ce54f9894c0c0bbf9303730ac28d8aeacdaddb2f912a2a3fcd40] [True]
Relayed Tx: 0fd755e847a5ce54f9894c0c0bbf9303730ac28d8aeacdaddb2f912a2a3fcd40
neo>
neo>
neo>
neo> [I 180303 08:37:29 EventHub:89] [SmartContract.Runtime.Log][433333] [562d6c29209dc96432c6868621fe489cedd05222] [tx 0fd755e847a5ce54f9894c0c0bbf9303730ac28d8aeacdaddb2f912a2a3fcd40] [b'Running Sample v4']
[I 180303 08:37:29 EventHub:89] [SmartContract.Runtime.Log][433333] [562d6c29209dc96432c6868621fe489cedd05222] [tx 0fd755e847a5ce54f9894c0c0bbf9303730ac28d8aeacdaddb2f912a2a3fcd40] [b'Running Application!']
[I 180303 08:37:29 EventHub:89] [SmartContract.Storage.Get][433333] [562d6c29209dc96432c6868621fe489cedd05222] [tx 0fd755e847a5ce54f9894c0c0bbf9303730ac28d8aeacdaddb2f912a2a3fcd40] ['AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy -> 0']
[I 180303 08:37:29 EventHub:89] [SmartContract.Storage.Put][433333] [562d6c29209dc96432c6868621fe489cedd05222] [tx 0fd755e847a5ce54f9894c0c0bbf9303730ac28d8aeacdaddb2f912a2a3fcd40] ['AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy -> 17']
[I 180303 08:37:29 EventHub:89] [SmartContract.Execution.Success][433333] [562d6c29209dc96432c6868621fe489cedd05222] [tx 0fd755e847a5ce54f9894c0c0bbf9303730ac28d8aeacdaddb2f912a2a3fcd40] [17]
複製代碼

和預期的同樣。如今咱們將打開不一樣的錢包並嘗試調用相同的東西。

NEO-Python 數據類型

在使用 NEO-python 或 NEO 區塊鏈時,你須要熟悉一些數據類型,這能夠幫助你識別在系統的各部分以不一樣格式出現的這些數據類型,以及正確使用它們。本章將簡要描述每種數據類型及其通常用法。

請注意,這些數據類型是在 neocore 項目中實現的,但在 neo-python 中用的不少。

KeyPair / 地址

NEO 中的地址其實是一對公鑰/私鑰。當建立一個錢包時,會根據錢包密碼建立一個32位的私鑰,這個私鑰只能你本身知道和保存。此私鑰與公鑰配對,用於標識網絡上的地址。只有簽名交易時才須要用到私鑰。

若是在 prompt 中打開一個錢包,運行 wallet 命令,在輸出的信息中能夠查看到錢包的公鑰,例如:

"public_keys": [
  {
      "Address": "AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy",
      "Public Key": "036d4de3e05057df18b82718d635795cb67d9c19001e998d76c77b86081be5f160"
  }
],
複製代碼

上例中 Public Key 以壓縮格式表示 ECDSA 曲線上的 x 和 y 座標,特別是 *SECP256R1 曲線。咱們經過一系列步驟推導出 Address

  • 建立一個以 21 開頭 ac 結尾的公鑰 UInt160ScriptHash

    >>> from neocore.Cryptography.Crypto import Crypto
    >>> pubkey_hex = '036d4de3e05057df18b82718d635795cb67d9c19001e998d76c77b86081be5f160'
    >>> pubkey_hex_for_addr = '21' + pubkey_hex + 'ac'
    >>> pubkey_hex_for_addr
    '21036d4de3e05057df18b82718d635795cb67d9c19001e998d76c77b86081be5f160ac'
    >>> script_hash = Crypto.ToScriptHash(pubkey_hex_for_addr, unhex=True)
    >>> script_hash
    <neocore.UInt160.UInt160 object at 0x10d33e908>
    >>> script_hash.Data
    bytearray(b'\x03\x19\xe0)\xb9%\x85w\x90\xe4\x17\x85\xbe\x9c\xce\xc6\xca\xb1\x98\x96')
    複製代碼
  • 接着,從如下腳本散列建立一個地址:

    >>> addr = Crypto.ToAddress(script_hash)
    >>> addr
    'AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy'
    >>>
    複製代碼

若是對 KeyPairUInt160、或 Crypto 包的實現細節感興趣,能夠查看 neocore repository

UInt256

UInt256 表示 32位 的散列。它一般是一個 Transaction 對象或 Block的散列。 它通常顯示爲 64 字符的字符串或帶有 0x 十六進制說明符的 66 個字符的字符串。如下是一個與 UInt256 交互的例子。

>>>
>>> from neocore.UInt256 import UInt256
>>>
>>> hash = "0x99e2be05956027b884cbf11cddbf9d2e5a8fb97ab18d5cde44d5ae2d4c980d18"
>>>
>>> uint = UInt256.ParseString(hash)
>>> uint
<neocore.UInt256.UInt256 object at 0x10cb9b240>
>>> uint.ToString()
'99e2be05956027b884cbf11cddbf9d2e5a8fb97ab18d5cde44d5ae2d4c980d18'
>>> uint.To0xString()
'0x99e2be05956027b884cbf11cddbf9d2e5a8fb97ab18d5cde44d5ae2d4c980d18'
>>> uint.Data
bytearray(b"\x18\r\x98L-\xae\xd5D\xde\\\x8d\xb1z\xb9\x8fZ.\x9d\xbf\xdd\x1c\xf1\xcb\x84\xb8\'`\x95\x05\xbe\xe2\x99")
>>>
>>> uint.ToBytes()
b'99e2be05956027b884cbf11cddbf9d2e5a8fb97ab18d5cde44d5ae2d4c980d18'
>>>
>>> data = uint.Data
>>> data
bytearray(b"\x18\r\x98L-\xae\xd5D\xde\\\x8d\xb1z\xb9\x8fZ.\x9d\xbf\xdd\x1c\xf1\xcb\x84\xb8\'`\x95\x05\xbe\xe2\x99")
>>>
>>> copy = UInt256(data=data)
>>>
>>> copy.To0xString()
'0x99e2be05956027b884cbf11cddbf9d2e5a8fb97ab18d5cde44d5ae2d4c980d18'
>>>
複製代碼

須要注意的是,咱們一般看到的是字符串,或者 UInt256 的 0x 字符串版本。

UInt160

UInt160 表示 20 位的散列,也可稱爲一個 ScriptHash。它用於顯示 NEO 中的 Address 對象是普通地址仍是合約地址。如下是一個與 UInt160交互的例子。

>>>
>>> data = bytearray(b'\x03\x19\xe0)\xb9%\x85w\x90\xe4\x17\x85\xbe\x9c\xce\xc6\xca\xb1\x98\x96')
>>>
>>> from neocore.UInt160 import UInt160
>>>
>>> new_sh = UInt160(data=data)
>>> new_sh
<neocore.UInt160.UInt160 object at 0x10d3460b8>
>>> new_sh.Data
bytearray(b'\x03\x19\xe0)\xb9%\x85w\x90\xe4\x17\x85\xbe\x9c\xce\xc6\xca\xb1\x98\x96')
>>>
>>> new_sh.To0xString()
'0x9698b1cac6ce9cbe8517e490778525b929e01903'
>>>
>>> sh_again = UInt160.ParseString( new_sh.To0xString() )
>>> sh_again.Data
bytearray(b'\x03\x19\xe0)\xb9%\x85w\x90\xe4\x17\x85\xbe\x9c\xce\xc6\xca\xb1\x98\x96')
>>>
>>> Crypto.ToAddress( sh_again)
'AG4GfwjnvydAZodm4xEDivguCtjCFzLcJy'
>>>
複製代碼

Fixed8

Fixed8 用於表示整數格式的 8 位小數。如下是一個使用 Fixed8的基本示例。

>>> from neocore.Fixed8 import Fixed8
>>>
>>> three = Fixed8.FromDecimal(3)
>>> three.value
300000000
>>> three.ToInt()
3
>>> three.ToString()
'3.0'
>>>
>>>
>>> point5 = Fixed8(50000000)
>>> point5.ToString()
'0.5'
>>>
>>> point5 + three
<neocore.Fixed8.Fixed8 object at 0x10cd48ba8>
>>> threepoint5 = point5 + three
>>> threepoint5.value
350000000
>>>
>>> threepoint5.ToString()
'3.5'
>>>
>>>
>>> threepoint5 * 2
Traceback (most recent call last):
File "<input>", line 1, in <module>
  threepoint5 * 2
File "/Users/thomassaunders/Workshop/neo-python/venv/lib/python3.6/site-packages/neocore/Fixed8.py", line 85, in __mul__
  return Fixed8(self.value * other.value)
AttributeError: 'int' object has no attribute 'value'
>>>
>>>
複製代碼

如下是一些總結:

  • 若是你想建立一個 Fixed8 而且你有一個小數,最簡單的作法是使用 Fixed8.FromDecimal 方法。
  • 假設每一個操做數是一個 Fixed8,你能夠對 Fixed8 對象進行數學運算。
  • 在一個 Fixed8 和另外一種類型的數字之間進行數學運算會引起錯誤。
  • 你能夠經過訪問 value 屬性來訪問 Fixed8 對象的完整值。

BigInteger

BigInteger 用於存儲和對任意大小的整數進行運算,包括負數和正數。這對將數字序列化爲字節和返回很是有用。如下是 BigInteger 的一些示例用法。

>>> from neocore.BigInteger import BigInteger
>>>
>>> bi = BigInteger(10000)
>>>
>>> bi.ToByteArray()
b"\x10'"
>>>
>>> bi2 = BigInteger.FromBytes( bi.ToByteArray() )
>>> bi2
10000
>>>
>>> bi3 = BigInteger(-3)
>>>
>>> bi4 = bi2 * bi3
>>> bi4
-30000
>>>
>>> bi4 += 100000
>>> bi4
70000
>>> bi4.ToByteArray()
b'p\x11\x01'
>>>
複製代碼

BigInteger 實現中須要注意的一點是它與 Fixed8 不一樣,所以你能夠在 BigInteger 和普通整數之間進行數學運算。

ContractParameterTypes

如下是建立和調用智能合約中用到的 ContractParameterTypes。

描述

neo.Wallets 中的合約參數

用法:

從 neo.SmartContract.ContractParameterType 導入 ContractParameterType

class neo.SmartContract.ContractParameterType.ContractParameterType

Contract Parameter Types are used to denote different types of objects used in the VM

Signature

00

Boolean

01

Integer

02

Hash160

03

Hash256

04

ByteArray

05

PublicKey

06

String

07

Array

10

InteropInterface

F0

Void

FF

neo.SmartContract.ContractParameterType.``ToName (param_type)

根據其值 param_type 獲取 ContractParameterType 的名稱。

測試

您可使用此命令開始測試:

make test
複製代碼

使用此命令運行樣式檢查:

make lint
複製代碼

使用此命令運行 neo-python 測試

python -m unittest discover neo
複製代碼

使用此命令運行 neo-boa 項目的測試:

python -m unittest discover boa_test
複製代碼

若是要添加測試或更改功能,能夠只運行一個測試:

python -m unittest neo/test_settings.py複製代碼

測試裝置指南

如下指南與全部依賴於 BlockChainFixtureTestCaseWalletFixtureTestCaseunit-tests 相關。這種測試依賴於 neo-python-privnet-unittest 映像生成的裝置。該映像位於這裏

  • 直接依賴 BlockChainFixtureTestCase 的測試是使用 BC 錢包(neo-test-bc.walletneo-test2-bc.wallet)裏的地址之間的交易生成的。有時在少數測試中也使用 neo-test-coz.wallet 裏的默認地址。COZ 錢包很特別,它是原始的 COZ 私網錢包,擁有 100000000 NEO。
  • 直接依賴 WalletFixtureTestCase 的測試是使用 W 錢包 (neo-test1-w.wallet, neo-test2-w.wallet, neo-test3-w.wallet) 裏的地址之間的交易生成的。

    下圖能夠更好的說明測試 BC 和 W:

向私網測試裝置新增測試時請遵循如下方針,以便最小化單元測試間的依賴:

  • 從便於維護的角度來講,儘可能不要在測試中直接使用 neo-test-coz.wallet,由於該錢包是其它測試錢包的 NEO 和 GAS的來源,會常常被更改。若是測試中使用了neo-test-coz.wallet,你將隨時須要進行更新。
  • 儘可能使用 privnet fixture 中現成的交易建立新測試。
  • 僅在必要時才向 privnet fixture 添加新交易,由於這會有破壞現有測試的風險。
  • 若是想要隔離你的測試,請建立你本身的通證。
  • 只有在絕對必要的狀況下,才能創造新的錢包。

上述最後三條要求更新測試裝置,操做以下:

  1. 拉取最新 neo-python-privnet-unittest image:

    docker pull cityofzion/neo-python-privnet-unittest:v0.0.xx
    複製代碼
  2. 運行 image:

    docker run --rm -d --name neo-privnet-unittest -p 20333-20336:20333-20336/tcp -p 30333-30336:30333-30336/tcp dautt/neo-privnet-unittest:v0.0.xx``
    複製代碼
  3. 清除當前 unittest 鏈:

    rm -rf ~/.neopython/Chains/unittest
    rm -rf ~/.neopython/Chains/unittest_notif
    複製代碼
  4. 激活你的虛擬環境:

    source venv/bin/activate
    複製代碼
  5. 啓動 NEO 節點:

    python prompt.py -u
    複製代碼
  6. 使用如下錢包生成交易:

    neo-test-coz.wallet     (pwd = coz)
    neo-test1-bc.wallet     (pwd = 1234567890)
    neo-test2-bc.wallet     (pwd = 1234567890)
    neo-test1-w.wallet      (pwd = 1234567890)
    neo-test2-w.wallet      (pwd = 1234567890)
    neo-test3-w.wallet      (pwd = 1234567890)
    複製代碼

    (要添加直接依賴 BlockchainFixtureTestCase 的新測試,請使用 -bc.wallet 類型的錢包。要添加直接依賴 WalletFixtureTestCase 的新測試,請使用 -w.wallet 類型的錢包)

  7. 若是你須要建立一個新智能合約,考慮使用這裏現有的合約:

    fixtures/UnitTest-SM.zip
    複製代碼

    (若是以上 zip 包中沒有新合約的源代碼,請添加。)

  8. 若是你已經在 neo-python-privnet-unittest 映像上部署了一個新合約,請在指定合約名稱時使用 test 爲前綴,這樣可使用如下命令查找出全部部署在映像上的合約:

    contract search test
    複製代碼
  9. 當你對新的單元測試滿意後,保存測試,而後重啓 docker 映像並從新部署你的測試。而後經過增長版本號 (xx+1) 建立一個新的映像:

    docker commit  neo-privnet-unittest dautt/neo-privnet-unittest:v0.0.xx+1
    複製代碼

    這樣作的緣由是咱們須要使映像儘量小。你的映像文件可能在不經意間積累了幾天或幾周的新塊,例如,在分階段執行新測試時,這會沒必要要地增長映像的大小。咱們的測試裝置在構建系統中被重置和提取20屢次,因此任何尺寸的增長都會增長延遲 20倍或更多。

  10. 經過增長數字後綴建立測試裝置 (x+1):

    notif_fixtures_vx+1.tar.gz
    fixtures_vx+1.tar.gz
    複製代碼
  11. 在以下文件的靜態類變量中更新裝置名稱:

    neo.Utils.BlockchainFixtureTestCase.py
    neo.api.REST.test_rest_api.py
    複製代碼
  12. 建立一個新的PR,連接到新的映像和新建的測試裝置。

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息