bCamp 是由國內區塊鏈頂級開發者、極客與投資人共同發起,旨在發現和培養國內頂級的區塊鏈技術合夥人,圍繞區塊鏈核心技術打造國內頂級的區塊鏈開發者社羣。
點擊文末 \” 閱讀原文 \” 即可回聽課程分享
1
分享嘉賓
莫韜
NEO 社區技術 Reddit 者、佈道者深度參與 NEO 社區貢獻
參與了 Github 文檔的翻譯維護和 NEO 英文社區的建設運營,活躍於 Reddit 和 Discord 社區
2
分享實錄
準備
教程基於 macOS 或 Linux Ubuntu,windows 用戶需要使用 linux 虛擬機。
- Linux Ubuntu
- 首先確認基本的 apt 和 curl 都已安裝 / 更新,執行
sudo apt-get update sudo apt-get install curl
- 安裝 docker,以備運行私鏈容器 (neo-privatenet),執行
sudo apt install docker.io
- 安裝 docker compose,執行
sudo curl -L https://github.com/docker/compose/releases/download/1.22.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
(__因版本可能更新,具體命令以 docker 的 Github 頁面爲準)
此命令可能需要翻牆,如果下載時間過長,見 3.0.1, 否則見 3.1
3.0.1. 手動從 Github 下載此項目,並將文件重命名爲 docker-compose
,放在 /usr/local/bin/
sudo cp *docker-compose 的下載路徑*/docker-compose usr/local/bin
3.1. 設定權限
sudo chmod +x /usr/local/bin/docker-compose
- Mac OS
下載 Docker for Mac,Docker 以及 Docker Compose 均整合在安裝文件裏。
環境搭建
NEO-Local
- 進入想要把 neo-local 下載到的文件夾,比如:
cd Documents/neo/
- 下載 Github 上的 NEO-Local, COZ 製作的整合包,整合了 NEO-Python 錢包的所有依賴包,可以一鍵搭建 neo-python 和私鏈。執行:
git clone https://github.com/CityOfZion/neo-local.git
- 進入 neo-local 所在的路徑
cd ./neo-local
- 啓動 docker
sudo service docker start
- 運行 neo-local:
sudo docker-compose up -d --build --remove-orphans
注:第一次運行時需要下載 4.4G 的內容,可能需要翻牆
- 運行 neo-python 錢包:
sudo docker exec -it neo-python np-prompt -p -v
出現如下界面進入了 NEO CLI 命令行,那麼恭喜你已經成功搭建好所有環境。
- 退出錢包 :
quit
- 停止運行 neo-local:
docker-compose down
Tip: 當 neo-local 停止運行時目前的私鏈也會停止運行
注:若想要不使用整合包的搭建環境方法,可見此教程_https://blog.gallifrey.cn/?p=419_
_
_
錢包使用
打開錢包
open wallet neo-privnet.sample.wallet
創建自己的錢包
create wallet workshop-example
轉賬
send neo {address} 10000 # 發送 neo send gas {address} 10000 # 發送 gas
合約測試+部署
編譯和測試
打開合約事件日誌 config sc-events on
合約編譯命令格式
build path/to/file.py test {input_params} {return_type} {needs_storage} {needs_dynamic_invoke} param1 param2 etc..
{input_params}
輸入參數類型 (參數類型表的值之一)
{return_type}
返回值類型 (參數類型表的值之一)
{needs_storage}
True/False: 合約是否需要用到存儲
{needs_dynamic_invoke}
合約是否需要用到動態調用(絕大多數情況下是 False)
is also a boolean, indicating whether or not the SC will be calling another contract whose address it will not know until runtime. This will most always be False
-
動態調用可以允許調用直到 runtime 之前都沒有發佈的合約。
-
params1 params2 etc...
傳入合約的參數
參數類型表
參數類型 | 參數類型表示值 |
---|---|
Signature | 00 |
Boolean | 01 |
Integer | 02 |
Hash160 | 03 |
Hash256 | 04 |
ByteArray | 05 |
PublicKey | 06 |
String | 07 |
Array | 10 |
InteropInterface | f0 |
void | ff |
部署和調用
部署 .avm 格式合約 :
import contract path/to/sample2.avm {input_params} {return_type} {needs_storage} {needs_dynamic_invoke}
-
注意無需傳入參數
-
輸入合約的一些信息
-
輸入密碼以確認部署
-
部署完成後的合約存儲於區塊鏈上,無法改變
**
**
調用
**
**
調用合約時需要知道這個合約的 hash,通過
contract search {contract_info}
可以查找合約。此外 contract_hash 也可以在合約完成發佈時的日誌裏找到。
通過 hash 調用合約:
testinvoke {contract_hash} {input_parameters}
例子
可從這裏下載 []。放入 ./neo-local/smart-contracts/
文件夾內
- Print and Notify
def Main(): # Print translates to a `Log` call, and is best used with simple strings for # development info. To print variables such as lists and objects, use `Notify`. print(\"log via print (1)\"07) Log(\"normal log (2)\") Notify(\"notify (3)\") # Sending multiple arguments as notify payload: msg = [\"a\", 1, 2, b\"3\"] Notify(msg)
print()
是 Python 內置的打印函數,Log()
和 Notify()
同樣是打印輸出,不同的是 Notify()
可以打印 object
對象,上面我們用 Notify()
打印了一個數組。
- Calculator
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 == \'p\': return a / b else: return -1
070202 02
- Storage
note: NEO 智能合約的存儲是通過鍵-值存儲。
from boa.interop.Neo.Storage import Get,Put,Delete,GetContextdef Main(operation, addr, value): if not is_valid_addr(addr): return False context = GetContext() if operation == \'add\': balance = Get(context, addr) new_balance = balance + value Put(context, addr, new_balance) return new_balance elif operation == \'remove\': balance = Get(context, addr) Put(context, addr, balance - value) return balance - value elif operation == \'balance\': return Get(context, addr) return Falsedef is_valid_addr(addr): if len(addr) == 20: return True return False
Get(context, key)
是通過上下文使用指定 key 獲取值,Put(context, key, value)
是通過上下文用指定 key 將 value 給存儲或更新,Delete(context, key)
是通過上下文用刪除指定 key 存儲的值。
輸入參數爲字符串字節數組 整數。輸出參數爲整數
注意當處理地址時,可以輸入地址的字符串 (string) 形式或字節數組 (bytearray) 形式
- Domain 域名服務
此智能合約通過使用區塊鏈的存儲系統,給錢包地址提供域名服務。每個註冊的域名對應一個地址。
合約有以下功能:
from boa.interop.Neo.Runtime import Log, Notifyfrom boa.interop.Neo.Storage import Get, Put, GetContextfrom boa.interop.Neo.Runtime import GetTrigger,CheckWitnessfrom boa.builtins import concatdef Main(operation, args): nargs = len(args) if nargs == 0: print(\"No domain name supplied\") return 0 if operation == \'query\': domain_name = args[0] return QueryDomain(domain_name) elif operation == \'delete\': domain_name = args[0] return DeleteDomain(domain_name) elif operation == \'register\': if nargs < 2: print(\"required arguments: [domain_name] [owner]\") return 0 domain_name = args[0] owner = args[1] return RegisterDomain(domain_name, owner) elif operation == \'transfer\': if nargs < 2: print(\"required arguments: [domain_name] [to_address]\") return 0 domain_name = args[0] to_address = args[1] return TransferDomain(domain_name, to_address)def QueryDomain(domain_name): msg = concat(\"QueryDomain: \", domain_name) Notify(msg) context = GetContext() owner = Get(context, domain_name) if not owner: Notify(\"Domain is not yet registered\") return False Notify(owner) return ownerdef RegisterDomain(domain_name, owner): msg = concat(\"RegisterDomain: \", domain_name) Notify(msg) if not CheckWitness(owner): Notify(\"Owner argument is not the same as the sender\") return False context = GetContext() exists = Get(context, domain_name) if exists: Notify(\"Domain is already registered\") return False Put(context, domain_name, owner) return True def TransferDomain(domain_name, to_address): msg = concat(\"TransferDomain: \", domain_name) Notify(msg) context = GetContext() owner = Get(context, domain_name) if not owner: Notify(\"Domain is not yet registered\") return False if not CheckWitness(owner): Notify(\"Sender is not the owner, cannot transfer\") return False if not len(to_address) != 34: Notify(\"Invalid new owner address. Must be exactly 34 characters\") return False Put(context, domain_name, to_address) return Truedef DeleteDomain(domain_name): msg = concat(\"DeleteDomain: \", domain_name) Notify(msg) context = GetContext() owner = Get(context, domain_name) if not owner: Notify(\"Domain is not yet registered\") return False if not CheckWitness(owner): Notify(\"Sender is not the owner, cannot transfer\") return False Delete(context, domain_name) return True
-
在
Main(operation, args)
函數中,通過不同的operation
來調用不同的函數進行操作,並進行參數校驗。 -
QueryDomain(domain_name)
:進行域名查詢操作,通過domain_name
查詢所對應的地址,若對應地址存在,則返回查詢到的地址,否則返回False
。 -
RegisterDomain(domain_name, owner)
:註冊域名,我們應該只能自己當前錢包的地址進行註冊等操作,所以使用CheckWitness(owner)
進行覈驗,覈驗地址是否爲當前錢包所有,然後判斷域名是否已經被註冊。 -
TransferDomain(domain_name, to_address)
:域名轉讓,將域名domain_name
轉讓到新地址to_address
上。首先校驗被轉讓的域名是否已經註冊存在,不存在的域名是無法轉讓的、然後檢驗新地址是否爲當前錢包所有,不是自己的域名是無法轉讓的、最後檢驗新地址長度是否合法。 -
DeleteDomain(domain_name)
:刪除域名,首先校驗域名是否存在,然後校驗域名是否爲當前錢包所有,否則不能刪除。
編譯:注意輸入參數爲字符串和數組,輸出參數爲字節數組
build smart-contracts/5-domain.py 0710 05 True False
**
**
部署
import contract smart-contracts/5-domain.avm 0710 05 True False
調用
testinvoke {contract_hash} register [\'domain.com\', \'ATZE8izvnGGuxLYRZUDPwFr6yqyaBavVWV\']
因爲合約返回的地址爲字節數組,所以需要用一個線上工具來轉換:
https://peterlinx.github.io/DataTransformationTools/
_
_
以下是我們的知識星球,歡迎有興趣的小夥伴加入我們,共同成長 !
_
_
__