NEO-Python

NEOGO發表於2019-01-25

概述

neo-python 目前功能

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

即將支援功能

  • 共識節點
  • 完整的智慧合約除錯和檢查

入門指南

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

此專案的主要功能包含在 cli 應用程式的 np-prompt 命令中。詳情請參見 使用命令列與 NEO 區塊鏈互動

我們釋出了一個 Youtube 視訊 幫助你快速上手。在CityOfZion 的 Youtube 頻道下還有其它視訊可供參考。

相關專案

安裝(Ubuntu/OSX)

需要安裝 libleveldb 庫。安裝 Python 3.6Python 3.7 以免因為與當前維護者版本不同遇到問題。注意不支援 Python 3.5 及更低版本。

安裝 neo-python 前需要安裝平臺相關專案。

平臺相關操作

Ubuntu/Debian 16.10+

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

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

sudo apt-get update && sudo apt-get upgrade
複製程式碼

你可以使用以下命令安裝 Python 3.7 和所有系統依賴:

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

你也可以使用以下命令直接安裝 Python 3.6 和所有系統依賴:

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 NetTest Net
JSON-RPC via HTTPS1033120331
JSON-RPC via HTTP1033220332
P2P via TCP1033320333
P2P via WebSocket1033420334

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

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 NetTest Net
JSON-RPC via HTTPS1033120331
JSON-RPC via HTTP1033220332

使用 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_typestrneo.EventHub.SmartContractEvent中的一個事件型別
contract_hashUInt160合約雜湊值
tx_hashUInt256交易雜湊值
block_numberint收到此事件的區塊號
event_payloadobject[]物件列表,取決於智慧合約發出的資料型別(例如,使用Runtime.Notify)。
execution_successbool方法呼叫是否成功
test_modebool這個事件是否由本地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_notifyRuntime.Notify
@on_logRuntime.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,連結到新的映像和新建的測試裝置。