From 0a95beb7ac7697a0f38b8da70676e63e04993fd4 Mon Sep 17 00:00:00 2001 From: youwenbusi <1245549353@qq.com> Date: Tue, 28 Jul 2020 20:26:25 +0800 Subject: [PATCH] console --- commands/constants.py | 2 +- commands/event_sync.py | 136 +++++++++++ commands/utils.py | 56 +++-- commands/zeth_deposit.py | 74 ++++++ commands/zeth_mix.py | 25 +- python_web3/.cache/HelloWorld | 3 + python_web3/README.md | 331 +++++++++++++++++++++++++++ python_web3/client/contractnote.py | 2 +- python_web3/client/event_callback.py | 10 +- zeth/contracts.py | 17 +- 10 files changed, 604 insertions(+), 52 deletions(-) create mode 100644 commands/event_sync.py create mode 100644 commands/zeth_deposit.py create mode 100644 python_web3/.cache/HelloWorld create mode 100644 python_web3/README.md diff --git a/commands/constants.py b/commands/constants.py index 5db19f4..b72e6a5 100644 --- a/commands/constants.py +++ b/commands/constants.py @@ -15,6 +15,6 @@ #INSTANCE_FILE_DEFAULT = "zeth-instance.json" #ETH_ADDRESS_DEFAULT = "eth-address" -WALLET_DIR_DEFAULT = "./wallet" +WALLET_DIR_DEFAULT = "wallet" WALLET_USERNAME = "zbac" USER_DIR = "user" diff --git a/commands/event_sync.py b/commands/event_sync.py new file mode 100644 index 0000000..2c9069e --- /dev/null +++ b/commands/event_sync.py @@ -0,0 +1,136 @@ +import sys +from python_web3.client.bcosclient import BcosClient +from python_web3.client.datatype_parser import DatatypeParser +from python_web3.client.contractnote import ContractNote +import json +import time +from python_web3.client.channel_push_dispatcher import ChannelPushHandler +from python_web3.client.event_callback import BcosEventCallback +from python_web3.client.event_callback import EventCallbackHandler +from click import command, argument, option, pass_context, ClickException, Context +from zeth.contracts import _event_args_to_mix_result +import os +from commands.constants import WALLET_USERNAME, FISCO_ADDRESS_FILE, USER_DIR, ADDRESS_FILE_DEFAULT, WALLET_DIR_DEFAULT +from click import command, argument, option, pass_context, ClickException, Context +from zeth.wallet import Wallet, ZethNoteDescription +from commands.utils import load_zeth_address +from typing import List +''' +def usage(): + usagetext = '\nUsage:\nparams: contractname address event_name indexed\n' \ + '\t1. contractname :\t合约的文件名,不需要带sol后缀,默认在当前目录的contracts目录下\n' \ + '\t2. address :\t十六进制的合约地址,或者可以为:last,表示采用bin/contract.ini里的记录\n' \ + '\t3. event_name :\t可选,如不设置监听所有事件 \n' \ + '\t4. indexed :\t可选,根据event定义里的indexed字段,作为过滤条件)\n\n' + usagetext = usagetext + "\teg: for contract sample [contracts/HelloEvent.sol], use cmdline:\n\n" + + usagetext = usagetext + "\tpython demo_event_callback.py HelloEvent last \n" + usagetext = usagetext + "\t--listen all event at all indexed : \n\n" + + usagetext = usagetext + "\tpython demo_event_callback.py HelloEvent last on_set \n" + usagetext = usagetext + "\t--listen event on_set(string newname) (no indexed): \n\n" + + usagetext = usagetext + \ + "\tpython demo_event_callback.py HelloEvent last on_number 5\n" + usagetext = usagetext + \ + "\t--listen event on_number(string name,int indexed age), age ONLY 5 : \n" + usagetext = usagetext + "\n...(and other events)" + print(usagetext) +''' + +class LogMixEvent(object): + def __init__( + self, + root: bytes, + nullifiers: bytes(2), + commitments: bytes(2), + ciphertexts: bytes(2)): + self.root = root + self.nullifiers = nullifiers + self.commitments = commitments + self.ciphertexts = ciphertexts + +def make_wallet() -> List[Wallet]: + ''' + Return all the wallet in local server + ''' + wallet_list = [] + for username in os.listdir(USER_DIR): + wallet_dir = "{}/{}/{}".format(USER_DIR, username, WALLET_DIR_DEFAULT) + if os.path.exists(wallet_dir) is False: + raise ClickException(f"invalid wallet_dir: {wallet_dir}") + zeth_address = load_zeth_address(username) + wallet_list.append(Wallet(None, username, wallet_dir, zeth_address.addr_sk)) + return wallet_list + +class EventCallbackImpl(EventCallbackHandler): + """sample event push handler for application level, + user can make a class base on "ChannelPushHandler" ,implement the on_push interface + handle the message from nodes,message in ChannelPack type #see client/channelpack.py + EVENT_LOG_PUSH type is 0x1002 + message in pack.data decode by utf-8 + EVENT_LOG format see https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/sdk/java_sdk.html#id19 + """ + abiparser: DatatypeParser = None + + def on_event(self, eventdata): + logresult = self.abiparser.parse_event_logs(eventdata["logs"]) + print("--------------------EventCallbackImpl--------------------\n") + logMix = logresult[0]['eventdata'] + logMixEvent = LogMixEvent(logMix[0],logMix[1], logMix[2], logMix[3]) + mix_result = _event_args_to_mix_result(logMixEvent) + new_merkle_root = mix_result.new_merkle_root + print("new_merkle_root in log: ", new_merkle_root) + for wallet in make_wallet(): + received_notes = wallet.receive_notes(mix_result.output_events) + print(f"{wallet.username} received notes: {received_notes}") + wallet.update_and_save_state() + update_merkle_root = wallet.merkle_tree.get_root() + print(f"The update_merkle_root in wallet of {wallet.username} is {update_merkle_root}") + + +@command() +@option("--mixer-addr", help="The Groth16Mixer contract address you want to listen") +def event_sync(mixer_addr: str): + + indexed_value = None + try: + bcos_event = BcosEventCallback() + bcos_event.setclient(BcosClient()) + print(bcos_event.client.getinfo()) + ''' + print("usage input {},{},{},{}".format(contractname, address, event_name, indexed_value)) + if address == "last": + cn = ContractNote() + address = cn.get_last(contractname) + print("hex address :", address) + ''' + abifile = "contract/Groth16Mixer.abi" + abiparser = DatatypeParser(abifile) + eventcallback = EventCallbackImpl() + eventcallback.abiparser = abiparser + + result = bcos_event.register_eventlog_filter( + eventcallback, abiparser, [mixer_addr], "LogMix", indexed_value) + #result = bcos_event.register_eventlog_filter(eventcallback02,abiparser, [address], "on_number") + + print( + "after register LogMix,result:{},all:{}".format( + result['result'], result)) + + while True: + print("waiting event...") + time.sleep(10) + except Exception as e: + print("Exception!") + import traceback + traceback.print_exc() + finally: + print("event callback finished!") + if bcos_event.client is not None: + bcos_event.client.finish() + sys.exit(-1) + + +if __name__ == "__main__": + event_sync() diff --git a/commands/utils.py b/commands/utils.py index b766667..33196e1 100644 --- a/commands/utils.py +++ b/commands/utils.py @@ -3,7 +3,7 @@ # SPDX-License-Identifier: LGPL-3.0+ from __future__ import annotations -from commands.constants import WALLET_USERNAME +from commands.constants import WALLET_USERNAME, FISCO_ADDRESS_FILE, USER_DIR, ADDRESS_FILE_DEFAULT, WALLET_DIR_DEFAULT from zeth.zeth_address import ZethAddressPub, ZethAddressPriv, ZethAddress from zeth.contracts import \ get_mix_results @@ -18,7 +18,7 @@ #from web3 import Web3 # type: ignore from contract.Groth16Mixer import Groth16Mixer from contract.ERC20Mintable import ERC20Mintable - +from python_web3.eth_account.account import Account from python_web3.client.bcoskeypair import BcosKeyPair #todo @@ -111,15 +111,16 @@ def load_mixer_description_from_ctx(ctx: ClientConfig) -> MixerDescription: return load_mixer_description(ctx.instance_file) ''' -def get_zeth_address_file(ctx: ClientConfig) -> str: - return ctx.address_file +def get_zeth_address_file(username: str) -> str: + addr_file = "{}/{}/{}".format(USER_DIR, username, ADDRESS_FILE_DEFAULT) + return addr_file -def load_zeth_address_public(ctx: ClientConfig) -> ZethAddressPub: +def load_zeth_address_public(username: str) -> ZethAddressPub: """ Load a ZethAddressPub from a key file. """ - secret_key_file = get_zeth_address_file(ctx) + secret_key_file = get_zeth_address_file(username) pub_addr_file = pub_address_file(secret_key_file) with open(pub_addr_file, "r") as pub_addr_f: return ZethAddressPub.parse(pub_addr_f.read()) @@ -134,11 +135,11 @@ def write_zeth_address_public( pub_addr_f.write(str(pub_addr)) -def load_zeth_address_secret(ctx: ClientConfig) -> ZethAddressPriv: +def load_zeth_address_secret(username: str) -> ZethAddressPriv: """ Read ZethAddressPriv """ - addr_file = get_zeth_address_file(ctx) + addr_file = get_zeth_address_file(username) with open(addr_file, "r") as addr_f: return ZethAddressPriv.from_json(addr_f.read()) @@ -152,32 +153,31 @@ def write_zeth_address_secret( addr_f.write(secret_addr.to_json()) -def load_zeth_address(ctx: ClientConfig) -> ZethAddress: +def load_zeth_address(username: str) -> ZethAddress: """ Load a ZethAddress secret from a file, and the associated public address, and return as a ZethAddress. """ return ZethAddress.from_secret_public( - load_zeth_address_secret(ctx), - load_zeth_address_public(ctx)) + load_zeth_address_secret(username), + load_zeth_address_public(username)) def open_wallet( mixer_instance: Any, js_secret: ZethAddressPriv, - ctx: ClientConfig) -> Wallet: + username: str + ) -> Wallet: """ Load a wallet using a secret key. """ - wallet_dir = ctx.wallet_dir + wallet_dir = "{}/{}/{}".format(USER_DIR, username, WALLET_DIR_DEFAULT) return Wallet(mixer_instance, WALLET_USERNAME, wallet_dir, js_secret) def do_sync( - #web3: Any, wallet: Wallet, receipt: Any, - #wait_tx: Optional[str], callback: Optional[Callable[[ZethNoteDescription], None]] = None) -> int: """ Implementation of sync, reused by several commands. Returns the @@ -192,16 +192,15 @@ def _do_sync() -> int: #print(f"SYNCHING blocks ({wallet_next_block} - {chain_block_number})") mixer_instance = wallet.mixer_instance - for mix_result in get_mix_results( - mixer_instance, receipt): - new_merkle_root = mix_result.new_merkle_root - for note_desc in wallet.receive_notes(mix_result.output_events): - if callback: - callback(note_desc) + mix_result = get_mix_results(mixer_instance, receipt) + new_merkle_root = mix_result.new_merkle_root + for note_desc in wallet.receive_notes(mix_result.output_events): + if callback: + callback(note_desc) - spent_commits = wallet.mark_nullifiers_used(mix_result.nullifiers) - for commit in spent_commits: - print(f" SPENT: {commit}") + spent_commits = wallet.mark_nullifiers_used(mix_result.nullifiers) + for commit in spent_commits: + print(f" SPENT: {commit}") wallet.update_and_save_state() @@ -258,16 +257,15 @@ def create_mixer_client(ctx: ClientConfig) -> MixerClient: ''' def create_zeth_client_and_mixer_desc( - ctx: ClientConfig, mixer_addr: str, password: str) -> Tuple[MixerClient, MixerDescription]: + prover_server_endpoint: str, mixer_addr: str, username: str, password: str) -> Tuple[MixerClient, MixerDescription]: """ Create a MixerClient and MixerDescription object, for an existing deployment. """ #web3 = open_web3_from_ctx(ctx) #mixer_desc = load_mixer_description_from_ctx(ctx) mixer_instance = Groth16Mixer(mixer_addr) - keystore_file = "pyaccount.keystore" - mixer_instance.client.keystore_file = "pyaccount.keystore" - if os.path.exists(keystore_file) is False: + keystore_file = "{}/{}/{}".format(USER_DIR, username, FISCO_ADDRESS_FILE) + if exists(keystore_file) is False: raise ClickException(f"invalid output spec: {keystore_file}") with open(keystore_file, "r") as dump_f: keytext = json.load(dump_f) @@ -279,7 +277,7 @@ def create_zeth_client_and_mixer_desc( keypair.address = mixer_instance.client.ecdsa_account.address mixer_instance.client.keypair = keypair zeth_client = MixerClient.open( - ctx.prover_server_endpoint, mixer_instance) + prover_server_endpoint, mixer_instance) return (zeth_client) diff --git a/commands/zeth_deposit.py b/commands/zeth_deposit.py new file mode 100644 index 0000000..81d2ce2 --- /dev/null +++ b/commands/zeth_deposit.py @@ -0,0 +1,74 @@ +# Copyright (c) 2015-2020 Clearmatics Technologies Ltd +# +# SPDX-License-Identifier: LGPL-3.0+ + +from commands.utils import create_zeth_client_and_mixer_desc, \ + load_zeth_address, open_wallet, parse_output, do_sync +from zeth.constants import JS_INPUTS, JS_OUTPUTS +from commands.constants import PROVER_SERVER_ENDPOINT_DEFAULT +from zeth.mixer_client import ZethAddressPub +from zeth.utils import EtherValue, from_zeth_units +from api.zeth_messages_pb2 import ZethNote +from click import command, option, pass_context, ClickException, Context +from typing import List, Tuple, Optional +import sys +sys.path.append('../') +from contract.Groth16Mixer import Groth16Mixer +from python_web3.eth_account.account import Account + +@command() +@option("--mixer-addr", help="The Groth16Mixer contract address you want to use") +@option("--username", help="The account you want to use") +@option("--password", help="the password of you keystore") +@option("--vin", default="0", help="public in value") +@option("--out", "output_specs", multiple=True, help=",") +def deposit( + mixer_addr: str, + username: str, + password: str, + vin: str, + output_specs: List[str] + ) -> None: + """ + Generic mix function + """ + # Some sanity checks + if len(output_specs) > JS_OUTPUTS: + raise ClickException(f"too many outputs (max {JS_OUTPUTS})") + + print(f"vin = {vin}") + + vin_pub = EtherValue(vin) + zeth_client = create_zeth_client_and_mixer_desc(PROVER_SERVER_ENDPOINT_DEFAULT, mixer_addr, username, password) + + zeth_address = load_zeth_address(username) + wallet = open_wallet( + zeth_client.mixer_instance, zeth_address.addr_sk, username) + + outputs: List[Tuple[ZethAddressPub, EtherValue]] = [ + parse_output(out_spec) for out_spec in output_specs] + + # Compute input and output value total and check that they match + output_note_sum = sum([value for _, value in outputs], EtherValue(0)) + if vin_pub != output_note_sum: + raise ClickException("input and output value mismatch") + + #eth_address = load_eth_address(eth_addr) + fisco_bcos_address = zeth_client.mixer_instance.client.ecdsa_account.address + # If instance uses an ERC20 token, tx_value can be 0 not default vin_pub. + tx_value: Optional[EtherValue] = EtherValue(0) + #if mixer_desc.token: + # tx_value = EtherValue(0) + + (outputresult, receipt) = zeth_client.deposit( + wallet.merkle_tree, + zeth_address, + fisco_bcos_address, + vin_pub, + outputs, + tx_value) + + print("receipt output :", outputresult) + #do_sync(wallet, receipt) +if __name__ == '__main__': + deposit() \ No newline at end of file diff --git a/commands/zeth_mix.py b/commands/zeth_mix.py index f697abb..5bf7405 100644 --- a/commands/zeth_mix.py +++ b/commands/zeth_mix.py @@ -5,6 +5,7 @@ from commands.utils import create_zeth_client_and_mixer_desc, \ load_zeth_address, open_wallet, parse_output, do_sync from zeth.constants import JS_INPUTS, JS_OUTPUTS +from commands.constants import PROVER_SERVER_ENDPOINT_DEFAULT from zeth.mixer_client import ZethAddressPub from zeth.utils import EtherValue, from_zeth_units from api.zeth_messages_pb2 import ZethNote @@ -17,24 +18,21 @@ @command() @option("--mixer-addr", help="The Groth16Mixer contract address you want to use") +@option("--username", help="The account you want to use") @option("--password", help="the password of you keystore") @option("--vin", default="0", help="public in value") @option("--vout", default="0", help="public out value") @option("--in", "input_notes", multiple=True) @option("--out", "output_specs", multiple=True, help=",") -#@option("--eth-addr", help="Sender eth address or address filename") -@option("--wait", is_flag=True) -@pass_context def mix( - ctx: Context, mixer_addr: str, + username: str, password: str, vin: str, vout: str, input_notes: List[str], - output_specs: List[str], - #eth_addr: Optional[str], - wait: bool) -> None: + output_specs: List[str] + ) -> None: """ Generic mix function """ @@ -49,12 +47,11 @@ def mix( vin_pub = EtherValue(vin) vout_pub = EtherValue(vout) - client_ctx = ctx.obj - zeth_client = create_zeth_client_and_mixer_desc(client_ctx, mixer_addr, password) + zeth_client = create_zeth_client_and_mixer_desc(PROVER_SERVER_ENDPOINT_DEFAULT, mixer_addr, username, password) - zeth_address = load_zeth_address(client_ctx) + zeth_address = load_zeth_address(username) wallet = open_wallet( - zeth_client.mixer_instance, zeth_address.addr_sk, client_ctx) + zeth_client.mixer_instance, zeth_address.addr_sk, username) inputs: List[Tuple[int, ZethNote]] = [ wallet.find_note(note_id).as_input() for note_id in input_notes] @@ -69,7 +66,7 @@ def mix( raise ClickException("input and output value mismatch") #eth_address = load_eth_address(eth_addr) - fisco_bcos_address = mixer_instance.client.ecdsa_account.address + fisco_bcos_address = zeth_client.mixer_instance.client.ecdsa_account.address # If instance uses an ERC20 token, tx_value can be 0 not default vin_pub. tx_value: Optional[EtherValue] = EtherValue(0) #if mixer_desc.token: @@ -86,4 +83,6 @@ def mix( tx_value) print("receipt output :", outputresult) - do_sync(wallet, receipt) \ No newline at end of file + #do_sync(wallet, receipt) +if __name__ == '__main__': + mix() \ No newline at end of file diff --git a/python_web3/.cache/HelloWorld b/python_web3/.cache/HelloWorld new file mode 100644 index 0000000..daec568 --- /dev/null +++ b/python_web3/.cache/HelloWorld @@ -0,0 +1,3 @@ +0x2d1c577e41809453c50e7e5c3f57d06f3cdd90ce +0x1f494c56c3ad1e6738f3500d19499cd3541160ea +0x84139e0d46160aa2dd2541f499049095596891c9 diff --git a/python_web3/README.md b/python_web3/README.md new file mode 100644 index 0000000..c5b818b --- /dev/null +++ b/python_web3/README.md @@ -0,0 +1,331 @@ +# Python SDK + +![](./images/FISCO_BCOS_Logo.svg) + + +[![Build Status](https://travis-ci.org/FISCO-BCOS/python-sdk.svg?branch=master)](https://travis-ci.org/FISCO-BCOS/python-sdk) +[![CodeFactor](https://www.codefactor.io/repository/github/fisco-bcos/python-sdk/badge)](https://www.codefactor.io/repository/github/fisco-bcos/python-sdk) +[![GitHub license](https://img.shields.io/github/license/FISCO-BCOS/python-sdk.svg)](https://github.com/FISCO-BCOS/python-sdk/blob/master/LICENSE) +[![GitHub issues](https://img.shields.io/github/issues/FISCO-BCOS/python-sdk.svg)](https://github.com/FISCO-BCOS/python-sdk/issues) +--- + +Python SDK为[FISCO BCOS](https://github.com/FISCO-BCOS/FISCO-BCOS/tree/master)提供Python API,使用FISCO BCOS Python SDK可以简单快捷的基于FISCO-BCOS进行区块链应用开发。**此版本只支持**[FISCO BCOS 2.0](https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/)。 + + +## 关键特性 + +- 提供调用FISCO BCOS 2.0 [JSON-RPC](https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/api.html)的Python API。 +- 可基于[Channel协议](https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/design/protocol_description.html#channelmessage)与FISCO BCOS进行通信,保证节点与SDK安全加密通信的同时,可接收节点推送的消息。 +- 支持交易解析功能:包括交易输入、交易输出、Event Log等ABI数据的拼装和解析。 +- 支持合约编译,将`sol`合约编译成`abi`和`bin`文件。 +- 支持基于keystore的账户管理。 +- 支持合约历史查询。 +- 支持国密(SM2,SM3,SM4) +- 支持event回调监听 +## 部署Python SDK + +### 环境要求 +- Python环境:python 3.6.3, 3.7.x +- FISCO BCOS节点:请参考[FISCO BCOS安装](https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/installation.html#fisco-bcos)搭建 + +### 依赖软件 + +- **Ubuntu**: `sudo apt install -y zlib1g-dev libffi6 libffi-dev wget git` +- **CentOS**:`sudo yum install -y zlib-devel libffi-devel wget git` +- **MacOs**: `brew install wget npm git` + +### 初始化环境(若python环境符合要求,可跳过) + +#### **Linux环境初始化** + +> 拉取源代码 +```bash +git clone https://github.com/FISCO-BCOS/python-sdk +``` + +> 配置环境(安装pyenv和python,若python版本>=3.6.3可跳过本步) +```bash +# 获取python版本 +python --version + +## --------若python版本小于3.6.3,执行下面流程-------------------------------------- +# 判断python版本,并为不符合条件的python环境安装python 3.7.3的虚拟环境,命名为python-sdk +# 若python环境符合要求,可以跳过此步 +# 若脚本执行出错,请检查是否参考[依赖软件]说明安装了依赖 +# 提示:安装python-3.7.3可能耗时比较久 +cd python-sdk && bash init_env.sh -p + +## --------若通过bash init_env.sh -p安装了python-sdk虚拟环境,执行下面流程------------- +# 激活python-sdk虚拟环境 +source ~/.bashrc && pyenv activate python-sdk && pip install --upgrade pip +``` + +#### **Windows环境初始化** + +在Windows运行Python SDK,需要按照以下步骤安装依赖软件并配置合约编译器: + +**安装依赖软件** + +- 直接安装[Python-3.7.x](https://www.python.org/downloads/release/python-373/)和[git](https://git-scm.com/download/win)软件 +> python环境变量配置可参考[这里](https://jingyan.baidu.com/article/b0b63dbff271e24a4830708d.html) + +- [Visual C++ 14.0库](https://visualstudio.microsoft.com/downloads) +> (注:Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools"解决方法: https://visualstudio.microsoft.com/downloads (注意选择vs 2005即14.0版)或 https://pan.baidu.com/s/1ZmDUGZjZNgFJ8D14zBu9og 提取码: zrby) + +- 下载Windows版本solc, 点击[这里](https://github.com/ethereum/solidity/releases/download/v0.4.25/solidity-windows.zip)下载 +> solc编译器下载成功后,解压,将其中的 solc.exe 文件复制 ${python-sdk}\bin 目录下。若 python-sdk 路为 D:\\open-source\\python-sdk, 则 solc.exe 文件复制路径为D:\\open-source\\python-sdk\\bin\\solc.exe + +**拉取源代码** + +打开 git,在任意目录执行如下命令 +```bash +git clone https://github.com/FISCO-BCOS/python-sdk +``` + +**配置solc编译器路径** +```bash +# 修改client_config.py.template: +# 配置solc编译器路径,若solc存放路径为D:\\open-source\\python-sdk\\bin\\solc.exe,则solc_path配置如下: +solc_path = "D:\\open-source\\python-sdk\\bin\\solc.exe" + +# 将client_config.py.template拷贝到client_config.py +``` + +### **安装Python SDK依赖** + +```bash +cd python-sdk +pip install -r requirements.txt +``` + +> **若因网络原因,安装依赖失败,可使用清华的pip源下载,安装命令如下:** + +```bash +pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt +``` + +### 初始化配置(Windows环境可跳过) + +```bash +# 该脚本执行操作如下: +# 1. 拷贝client_config.py.template->client_config.py +# 2. 安装solc编译器 +bash init_env.sh -i +``` + +> **若MacOS环境solc安装较慢,可在python-sdk目录下执行如下命令安装solcjs**,python-sdk自动加载nodejs编译器: + +```bash +# 安装编译器 +npm install solc@v0.4.25 +``` + +> 若没有执行以上初始化步骤,需要将`contracts/`目录下的`sol`代码手动编译成`bin`和`abi`文件并放置于`contracts`目录,才可以部署和调用相应合约。合约编译可以使用[remix](https://remix.ethereum.org) + + +## 配置Channel通信协议 + +Python SDK支持使用[Channel协议](https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/design/protocol_description.html#channelmessage-v1)与FISCO BCOS节点通信,通过SSL加密通信保障SDK与节点通信的机密性。 + +设SDK连接的节点部署在目录`~/fisco/nodes/127.0.0.1`目录下,则通过如下步骤使用Channel协议: + +**配置Channel信息** + +在节点目录下的 config.ini 文件中获取 channel_listen_port, 这里为20200 +```bash +[rpc] + listen_ip=0.0.0.0 + channel_listen_port=20200 + jsonrpc_listen_port=8545 +``` + +切换到python-sdk目录,修改 client_config.py 文件中`channel_host`为实际的IP,`channel_port`为上步获取的`channel_listen_port`: + +```bash +channel_host = "127.0.0.1" +channel_port = 20200 +``` + +**配置证书** + +```bash +# 若节点与python-sdk位于不同机器,请将节点sdk目录下所有相关文件拷贝到bin目录 +# 若节点与sdk位于相同机器,直接拷贝节点证书到SDK配置目录 +cp ~/fisco/nodes/127.0.0.1/sdk/* bin/ +``` + +**配置证书路径** + + - `client_config.py`的`channel_node_cert`和`channel_node_key`选项分别用于配置SDK证书和私钥 + - `release-2.1.0`版本开始,SDK证书和私钥更新为`sdk.crt`和`sdk.key`,配置证书路径前,请先检查上步拷贝的证书名和私钥名,并将`channel_node_cert`配置为SDK证书路径,将`channel_node_key`配置为SDK私钥路径 + +检查从节点拷贝的sdk证书路径,若sdk证书和私钥路径分别为`bin/sdk.crt`和`bin/sdk.key`,则`client_config.py`中相关配置项如下: + +```bash +channel_node_cert = "bin/sdk.crt" # 采用channel协议时,需要设置sdk证书,如采用rpc协议通信,这里可以留空 +channel_node_key = "bin/sdk.key" # 采用channel协议时,需要设置sdk私钥,如采用rpc协议通信,这里可以留空 +``` + +若sdk证书和私钥路径分别为`bin/node.crt`和`bin/node.key`,则`client_config.py`中相关配置项如下: +```bash +channel_node_cert = "bin/node.crt" # 采用channel协议时,需要设置sdk证书,如采用rpc协议通信,这里可以留空 +channel_node_key = "bin/node.key" # 采用channel协议时,需要设置sdk私钥,如采用rpc协议通信,这里可以留空 +``` + +**国密支持** + - 支持国密版本的非对称加密、签名验签(SM2), HASH算法(SM3),对称加解密(SM4) + - 国密版本在使用上和非国密版本基本一致,主要是配置差异。 + - 国密版本sdk同一套代码可以连接国密和非国密的节点,需要根据不同的节点配置相应的IP端口和证书 + - 因为当前版本的实现里,账户文件格式有差异,所以国密的账户文件和ECDSA的账户文件采用不同的配置 + +连接国密节点时,有以下相关的配置项需要修改和确认,IP端口也需要确认是指向国密版本节点 +```bash +crypto_type = "GM" #密码算法选择: 大小写不敏感:"GM" 标识国密, "ECDSA" 或其他是椭圆曲线默认实现。 +gm_account_keyfile = "gm_account.json" #国密账号的存储文件,可以加密存储,如果留空则不加载 +gm_account_password = "123456" #如果不设密码,置为None或""则不加密 +gm_solc_path = "./bin/solc/v0.4.25/solc-gm" #合约编译器配置,通过执行bash init_env.sh -i命令下载 +``` + + +**使用Channel协议访问节点** + +```bash +# 获取FISCO BCOS节点版本号 +./console.py getNodeVersion +``` + +**Event事件回调** + - 针对已经部署在链上某个地址的合约,先注册要监听的事件,当合约被交易调用,且生成事件时,节点可以向客户端推送相应的事件 + - 事件定义如有indexed类型的输入,可以指定监听某个特定值作为过滤,如事件定义为 on_set(string name,int indexed value),可以增加一个针对value的topic监听,只监听value=5的事件 + - 具体实现参考demo_event_callback.py,使用的命令行为: +```bash +params: contractname address event_name indexed + 1. contractname : 合约的文件名,不需要带sol后缀,默认在当前目录的contracts目录下 + 2. address : 十六进制的合约地址,或者可以为:last,表示采用bin/contract.ini里的记录 + 3. event_name : 可选,如不设置监听所有事件 + 4. indexed : 可选,根据event定义里的indexed字段,作为过滤条件) + + eg: for contract sample [contracts/HelloEvent.sol], use cmdline: + + python demo_event_callback.py HelloEvent last + --listen all event at all indexed : + + python demo_event_callback.py HelloEvent last on_set + --listen event on_set(string newname) (no indexed): + + python demo_event_callback.py HelloEvent last on_number 5 + --listen event on_number(string name,int indexed age), age ONLY 5 : + +``` + + +## SDK使用示例 + +**查看SDK使用方法** + +> **windows环境下执行console.py请使用`.\console.py`或者`python console.py`** + +```bash +# 查看SDK使用方法 +./console.py usage + +# 获取节点版本 +./console.py getNodeVersion +``` + +**部署HelloWorld合约** +```bash +$ ./console.py deploy HelloWorld save + +INFO >> user input : ['deploy', 'HelloWorld', 'save'] + +backup [contracts/HelloWorld.abi] to [contracts/HelloWorld.abi.20190807102912] +backup [contracts/HelloWorld.bin] to [contracts/HelloWorld.bin.20190807102912] +INFO >> compile with solc compiler +deploy result for [HelloWorld] is: + { + "blockHash": "0x3912605dde5f7358fee40a85a8b97ba6493848eae7766a8c317beecafb2e279d", + "blockNumber": "0x1", + "contractAddress": "0x2d1c577e41809453c50e7e5c3f57d06f3cdd90ce", + "from": "0x95198b93705e394a916579e048c8a32ddfb900f7", + "gasUsed": "0x44ab3", + "input": "0x6080604052...省略若干行...c6f2c20576f726c642100000000000000000000000000", + "logs": [], + "logsBloom": "0x000...省略若干行...0000", + "output": "0x", + "status": "0x0", + "to": "0x0000000000000000000000000000000000000000", + "transactionHash": "0xb291e9ca38b53c897340256b851764fa68a86f2a53cb14b2ecdcc332e850bb91", + "transactionIndex": "0x0" +} +on block : 1,address: 0x2d1c577e41809453c50e7e5c3f57d06f3cdd90ce +address save to file: bin/contract.ini +``` + +**调用HelloWorld合约** + +```bash +# 合约地址:0x2d1c577e41809453c50e7e5c3f57d06f3cdd90ce +# 调用接口:get +$./console.py call HelloWorld 0x2d1c577e41809453c50e7e5c3f57d06f3cdd90ce get + +INFO >> user input : ['call', 'HelloWorld', '0x2d1c577e41809453c50e7e5c3f57d06f3cdd90ce', 'get'] + +INFO >> call HelloWorld , address: 0x2d1c577e41809453c50e7e5c3f57d06f3cdd90ce, func: get, args:[] +INFO >> call result: ('Hello, World!',) + +# 合约名:HelloWorld +# 合约地址:0x2d1c577e41809453c50e7e5c3f57d06f3cdd90ce +# 调用接口:set +# 参数:"Hello, FISCO" +$ ./console.py sendtx HelloWorld 0x2d1c577e41809453c50e7e5c3f57d06f3cdd90ce set "Hello, FISCO" + +INFO >> user input : ['sendtx', 'HelloWorld', '0x2d1c577e41809453c50e7e5c3f57d06f3cdd90ce', 'set', 'Hello, FISCO'] + +INFO >> sendtx HelloWorld , address: 0x2d1c577e41809453c50e7e5c3f57d06f3cdd90ce, func: set, args:['Hello, FISCO'] + +INFO >> receipt logs : +INFO >> transaction hash : 0xc20cbc6b0f28ad8fe1c560c8ce28c0e7eb7719a4a618a81604ac87ac46cc60f0 +tx input data detail: + {'name': 'set', 'args': ('Hello, FISCO',), 'signature': 'set(string)'} +receipt output : () + +# 调用get接口获取更新后字符串 +$./console.py call HelloWorld 0x2d1c577e41809453c50e7e5c3f57d06f3cdd90ce get + +INFO >> user input : ['call', 'HelloWorld', '0x42883e01ac97a3a5ef8a70c290abe0f67913964e', 'get'] + +INFO >> call HelloWorld , address: 0x42883e01ac97a3a5ef8a70c290abe0f67913964e, func: get, args:[] +INFO >> call result: 'Hello, FISCO!' +``` + +## 开启命令行自动补全 + +Python SDK引入[argcomplete](https://argcomplete.readthedocs.io/en/latest/)支持命令行补全,运行如下命令开启此功能(**bashrc仅需设置一次,设置之后每次登陆自动生效**),**目前仅支持bash,不支持zsh**: + +```bash +echo "eval \"\$(register-python-argcomplete ./console.py)\"" >> ~/.bashrc +source ~/.bashrc +``` + +## 文档 + +[**中文**](https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/sdk/python_sdk/index.html) + +## 贡献代码 + +- 我们欢迎并非常感谢您的贡献,请参阅[代码贡献流程](https://mp.weixin.qq.com/s/hEn2rxqnqp0dF6OKH6Ua-A +)。 +- 如项目对您有帮助,欢迎star支持! + + +## 加入社区 + +**FISCO BCOS开源社区**是国内活跃的开源社区,社区长期为机构和个人开发者提供各类支持与帮助。已有来自各行业的数千名技术爱好者在研究和使用FISCO BCOS。如您对FISCO BCOS开源技术及应用感兴趣,欢迎加入社区获得更多支持与帮助。 + +![](https://media.githubusercontent.com/media/FISCO-BCOS/LargeFiles/master/images/QR_image.png) + +## License +![license](https://img.shields.io/github/license/FISCO-BCOS/python-sdk.svg) + +Python SDK的开源协议为[MIT License](https://opensource.org/licenses/MIT). 详情参考[LICENSE](./LICENSE)。 diff --git a/python_web3/client/contractnote.py b/python_web3/client/contractnote.py index 66997d3..17933e8 100644 --- a/python_web3/client/contractnote.py +++ b/python_web3/client/contractnote.py @@ -11,7 +11,7 @@ @author: kentzhang @date: 2019-06 ''' -from client_config import client_config +from python_web3.client_config import client_config from configobj import ConfigObj import time import os diff --git a/python_web3/client/event_callback.py b/python_web3/client/event_callback.py index 30527cc..b87262d 100644 --- a/python_web3/client/event_callback.py +++ b/python_web3/client/event_callback.py @@ -12,14 +12,14 @@ @date: 2019-06 ''' -from client.bcosclient import BcosClient -from client.datatype_parser import DatatypeParser +from python_web3.client.bcosclient import BcosClient +from python_web3.client.datatype_parser import DatatypeParser import uuid import json import threading -from utils.encoding import FriendlyJsonSerde -from client.channelpack import ChannelPack -from client.channel_push_dispatcher import ChannelPushHandler +from python_web3.utils.encoding import FriendlyJsonSerde +from python_web3.client.channelpack import ChannelPack +from python_web3.client.channel_push_dispatcher import ChannelPushHandler class EventCallbackHandler: diff --git a/zeth/contracts.py b/zeth/contracts.py index 5ef02e6..1f5c14c 100644 --- a/zeth/contracts.py +++ b/zeth/contracts.py @@ -275,6 +275,17 @@ def mix( ''' return _create_web3_mixer_call(zksnark, mixer_instance, mix_parameters) +class LogMixEvent(object): + def __init__( + self, + root: bytes, + nullifiers: bytes(2), + commitments: bytes(2), + ciphertexts: bytes(2)): + self.root = root + self.nullifiers = nullifiers + self.commitments = commitments + self.ciphertexts = ciphertexts def parse_mix_call( mixer_instance: Any, @@ -323,7 +334,7 @@ def get_mix_results( web3.eth.uninstallFilter(log_mix_filter.filter_id) ''' logresult = mixer_instance.data_parser.parse_event_logs(receipt["logs"]) - for log in logresult: - if log['eventname'] == 'LogMix': - yield _event_args_to_mix_result(log['eventdata']) + logMix = logresult[0]['eventdata'] + logMixEvent = LogMixEvent(logMix[0],logMix[1], logMix[2], logMix[3]) + result = _event_args_to_mix_result(logMixEvent)