如何用python和flask以太坊智能合约开发

将数据存储在数据库中是任何软件应用程序不可或缺的一部分。无论如何控制该数据库都有一个该数据的主控。区块链技术将数据存储到区块链网络内的区块中。因此,只要某个节点与网络同步,它们就会获得区块中数据的副本。因此,该技术中没有特定的数据主控。

在本教程中,我们将编写一份智能合约(我将进一步解释),以便在区块链上保留用户数据。我们将使用python web3(web3的python库)来开发和部署智能合约。一旦我们在区块链上部署了智能合约。我们将使用flask API与智能合约进行交互以存储一些数据/信息。我们将它存储在区块链上,它是不可变的。

环境要求

Python 3.6

安装

1.创建一个python虚拟环境。

Virtualenv将你的Python软件包本地化保存在你项目的虚拟环境中,而不是强迫你在系统范围内安装软件包。

$ virtualenv -p /usr/bin/python3.6 venv
$ source venv/bin/activate

2.现在我们需要Ganache那样的以太坊测试链。

Ganache是以太坊开发的个人区块链,可用于部署合约,开发应用程序和运行测试。

$ npm install -g ganache-cli

3.安装python web3

Web3.py是一个用于与以太坊交互的python库。它的API源自Web3.js Javascript API,对于使用过web3.js的人来说应该很熟悉。

$ pip3 install web3

4.Flask

Flask是一个python轻量级框架。

$ pip3 install flask

5.Flask Restful

Flask-RESTful是Flask的扩展,增加了对快速构建REST API的支持。

$ pip3 install flask-restful
  1. Flask Marshmallow

Flask marshmallow是一个对象序列化/反序列化库。

$ pip3 install flask-marshmallow

启动以太坊测试区块链服务器

要部署智能合约,我们应该启动测试以太坊服务器。我们正在使用ganache进行测试。在终端中键入以下命令:

$ ganache-cli

Ganache为我们提供了10个默认测试帐户,每个帐户中有100个假ether,用于交易。我们将使用这些帐户在合约中部署和设置各种值。

我们可以看到gas价格和限制以及部署ganachehost:port。我们在部署合约时需要这个。

创建user.sol文件

现在我们将用Solidity编写智能合约。Solidity是在ethereum上编写智能合约的语言。智能合约包括我们将在区块链上存储的数据,数据和getter方法的可选验证函数,访问数据的setter方法。

例如,要在区块链上进行考勤注册,你将拥有一组用户对象。它将可以访问用户的getter,setter方法。由于每个用户每天只能标记一次出勤,因此你需要一个验证功能来检查,智能合约与我们通常用其他任何语言开发的应用程序非常相似。

在下面的文件中,我们使用getter,setter函数构建简单的用户合约。

1.在.sol文件中声明solidity编译器版本。

pragma solidity ^ 0.4.21;

了解使用的编译器版本。

$ solidity?—?version

2.导入库文件Import library。我们应该将库用于常用的实用程序函数。库可以只编译一次并反复使用(点击这里获取一些好的库资源)。

import“stringUtils.sol”;

3.为用户声明合约

contract userRecords {}

4.现在,对于基本演示,我们将存储有关用户的名称和性别信息。因此,使用struct和enum数据类型初始化这两个变量。

//枚举类型变量来存储用户性别
enum genderType { male, female }
//我们将存储在以太坊合约中的实际用户对象
struct user{
    string name; genderType gender;
}

5.现在我们将声明user(struct)类型的用户对象。也可以将其声明为public,以便从合约外部访问它(有关可见范围,请单击此处)。

user user_obj;

6.现在为用户对象添加getter,setter方法。我们将在区块链上保留每个用户的信息。我们应该始终公开此方法,因为我们将从合约外部访问它们。

//设置用户公共功能
//这类似于db中的持久对象。
function setUser(string name, string gender) public {
    genderType gender_type = getGenderFromString(gender);
    user_obj = user({name:name, gender: gender_type});
}
//获取用户公共功能
//这类似于从db获取对象。
function getUser() public returns (string, string) {
    return (user_obj.name, getGenderToString(user_obj.gender));
}

7.请注意,我们使用了两个内部函数getGenderFromString()getGenderToString()。让我们添加这个内部函数。将它们声明为内部,因为我们不会在外面使用它们。

//用于从string中转换genderType枚举的内部函数
function getGenderFromString(string gender) internal returns(genderType) {
    if(StringUtils.equal(gender, "male")) {
        return genderType.male;
    } else {
        return genderType.female;
    }
}
//将genderType枚举转换为字符串的内部函数
(string) {
    if(gender == genderType.male) {
        return "male";
    } else {
        return "female";
    }
}

我们正在使用stringUtils.equal()库函数。由于此版本的solidity不支持使用(==)进行字符串比较。

8.现在我们的user.sol文件合约如下所示:

pragma solidity ^0.4.21;
// import library file
import "stringUtils.sol";
contract userRecords {
  // enum type variable to store user gender
  enum genderType { male, female };
  // Actual user object which we will store
  struct user{
    string name;
    genderType gender;
  }
  // user object
  user user_obj;
  //Internal function to conver genderType enum from string
  function getGenderFromString(string gender) internal returns   (genderType) {
    if(StringUtils.equal(gender, "male")) {
      return genderType.male;
    } else {
      return genderType.female;
    }
  }
  //Internal function to convert genderType enum to string
  function getGenderToString(genderType gender) internal returns (string) {
    if(gender == genderType.male) {
      return "male";
    } else {
      return "female";
    }
  }
  // set user public function
  // This is similar to persisting object in db.
  function setUser(string name, string gender) public {
    genderType gender_type = getGenderFromString(gender);
    user_obj = user({name:name, gender: gender_type});
  }

  // get user public function
  // This is similar to getting object from db.
  function getUser() public returns (string, string) {
    return (user_obj.name, getGenderToString(user_obj.gender));
  }
}

使用python脚本编译和部署solidity文件。

1.在下面的python脚本中,我们需要实例化python-web3测试以太坊节点。我们将设置ganche url为测试以太坊节点。我们将使用下面的w3对象来部署合约。

from web3 import Web3
# web3.py instance
w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))

2.现在我们将编译solidity代码。为了编译solidity代码,我们使用py-solc,它是用于solidity编译器的python扩展。

from solc import compile_files
# 编译所有合约文件
contracts = compile_files([‘user.sol‘, ‘stringUtils.sol‘])
# 单独的主文件和链接文件
main_contract = contracts.pop("user.sol:userRecords")
library_link = contracts.pop("stringUtils.sol:StringUtils")

3.每当使用import语句编译.sol文件时。我们还需要链接导入文件的部署地址以及主合约。 因此,对于部署所有链接首先通过编译它(如果已经部署然后保存地址)请参见下图主合约的bin。

当你编译主合约时,如果你看到它的bin部分,你将找到我们正在导入的库的_stringUtils.sol:StringUtils ___________(它也可以用于合约)。 这部分我们应该通过在部署合约之前的库地址来替换它。

4.然后我们将库地址与主合约相关联。

from solc import link_code
def deploy_contract(contract_interface):
    #实例化和部署合约
    contract = w3.eth.contract(
        abi=contract_interface[‘abi‘],
        bytecode=contract_interface[‘bin‘]
    )
    #从已部署的合约中获取交易哈希
    tx_hash = contract.deploy(
        transaction={‘from‘: w3.eth.accounts[1]}
    )
    #获取tx收据以获取合约地址
    tx_receipt = w3.eth.getTransactionReceipt(tx_hash)
    return tx_receipt[‘contractAddress‘]
library_address = {
    "stringUtils.sol:StringUtils": deploy_contract(library_link)
}
main_contract[‘bin‘] = link_code(
    main_contract[‘bin‘], library_address
)

链接后主合约bin的见下图:

你将看到导入库的bin已添加。

5.现在使用我们的w3对象部署主合约。使用ethereum account {‘from‘:w3.eth.accounts [1]}的默认地址进行部署。

def deploy_contract(contract_interface):
    # 实例化和部署合约
    contract = w3.eth.contract(
        abi=contract_interface[‘abi‘],
        bytecode=contract_interface[‘bin‘]
    )
    # 从部署的合约中获取交易哈希
    tx_hash = contract.deploy(
        transaction={‘from‘: w3.eth.accounts[1]}
    )
    # 获取tx收据以获取合同地址
    tx_receipt = w3.eth.getTransactionReceipt(tx_hash)
    return tx_receipt[‘contractAddress‘]
contract_address = deploy_contract(main_contract)

你将在运行ganache测试服务器的选项卡中看到以下这行:

这与合约部署后在tx_receipt中获得的信息相同。

6.现在将abi和contract_address存储在json文件中。这样我们以后可以在flask api中使用它来存储合约中的用户对象。

# 在json文件中添加abi(应用程序二进制接口)和交易收据
with open(‘data.json‘, ‘w‘) as outfile:
    data = {
       "abi": main_contract[‘abi‘],
       "contract_address": deploy_contract(main_contract)
    }
    json.dump(data, outfile, indent=4, sort_keys=True)

7.现在我们的完整脚本如下所示:

import json
from web3 import Web3
from solc import compile_files, link_code, compile_source
# web3.py instance
w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))
def deploy_contract(contract_interface):
    # Instantiate and deploy contract
    contract = w3.eth.contract(
        abi=contract_interface[‘abi‘],
        bytecode=contract_interface[‘bin‘]
    )
    # Get transaction hash from deployed contract
    tx_hash =contract.deploy(transaction{‘from‘:w3.eth.accounts[1]})
    # Get tx receipt to get contract address
    tx_receipt = w3.eth.getTransactionReceipt(tx_hash)
    return tx_receipt[‘contractAddress‘]
# compile all contract files
contracts = compile_files([‘user.sol‘, ‘stringUtils.sol‘])
# separate main file and link file
main_contract = contracts.pop("user.sol:userRecords")
library_link = contracts.pop("stringUtils.sol:StringUtils")
# print bin part in  console you will see ‘stringUtils‘ in that we need to link library address in that bin code.
# to that we have to deploy library code first then link it
library_address = {
    "stringUtils.sol:StringUtils": deploy_contract(library_link)
}
main_contract[‘bin‘] = link_code(
    main_contract[‘bin‘], library_address)
# add abi(application binary interface) and transaction reciept in json file
with open(‘data.json‘, ‘w‘) as outfile:
    data = {
        "abi": main_contract[‘abi‘],
        "contract_address": deploy_contract(main_contract)
    }
    json.dump(data, outfile, indent=4, sort_keys=True)

创建flask api以为用户存储不同的值

你只需部署一次合约。但是使用它的地址,你会一次又一次地存储数据。同样,在db的世界中,你只需定义一次模型/模式,但你将在db中添加不同的行/文档。

我们将使用flask post api来获取用户的用户信息并返回成功。

from flask import Flask, Response, request, jsonify
from marshmallow import Schema, fields, ValidationError
def check_gender(data):
    valid_list = ["male", "female"]
    if data not in valid_list:
        raise ValidationError(
            ‘Invalid gender. Valid choices are‘+ valid_list
        )
#For api validations
class UserSchema(Schema):
    name = fields.String(required=True)
    gender = fields.String(required=True, validate=check_gender)
# Initializing flask app
app = Flask(__name__)
# api to set new user every api call
@app.route("/blockchain/user", methods=[‘POST‘])
def user():
    body = request.get_json()
    result, error = UserSchema().load(body)
    if error:
        return jsonify(error), 422
    return jsonify({"data": result}), 200

由于这不是flask教程,我不会详细说明这一点,如果flask不熟悉可以看这个flask教程学习下。我们的API用户将从客户端获取数据(curl请求)并对其进行验证将其返回给客户端(curl请求)

2.现在我们将初始化web3对象以与已部署的用户合约进行通信。

from web3 import Web3
# web3.py instance
w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))

3.现在我们将获得之前存储在data.json文件中的abi和合约地址。

with open("data.json", ‘r‘) as f:
     datastore = json.load(f)
     abi = datastore["abi"]
     contract_address = datastore["contract_address"]

4.选择交易的默认帐户地址。每次在合约中为用户设置新值。你会从钱包里拿出一些gas。

w3.eth.defaultAccount = w3.eth.accounts[1]

5.最后,你将在以太坊合约中设置api调用用户对象时获得的值。

@app.route("/blockchain/user", methods=[‘POST‘])
def user():
    # Create the contract instance with the newly-deployed address
    user = w3.eth.contract(address=contract_address, abi=abi)
    body = request.get_json()
    result, error = UserSchema().load(body)
    if error:
        return jsonify(error), 422
    tx_hash = user.functions.setUser(
        result[‘name‘],result[‘gender‘]
    )
    tx_hash = tx_hash.transact()
    # Wait for transaction to be mined...
    w3.eth.waitForTransactionReceipt(tx_hash)
    user_data = user.functions.getUser().call()
    return jsonify({"data": user_data}), 200

我们首先使用abi和contract_address获得部署合约。

user = w3.eth.contract(address=contract_address, abi=abi)

然后我们可以使用合约实例调用任何合约公共函数。在为用户设置值之后,我们将使用transact方法将其公之于众。这将在以太坊区块中添加新的用户值。

tx_hash = user.functions.setUser(
    result[‘name‘],result[‘gender‘]
).transact()

现在我们可以使用call方法获得已在合约中设置的值,这将调用合约函数而不在区块链中添加任何区块。

user_data = user.functions.getUser().call()

我们的api文件的最终代码如下所示。将其另存为app.py

import json
from flask import Flask, Response, request, jsonify
from marshmallow import Schema, fields, ValidationError
from web3 import Web3
# web3.py instance
w3 = Web3(Web3.HTTPProvider("http://127.0.0.1:8545"))
w3.eth.defaultAccount = w3.eth.accounts[1]
# Get stored abi and contract_address
with open("data.json", ‘r‘) as f:
    datastore = json.load(f)
    abi = datastore["abi"]
    contract_address = datastore["contract_address"]
def check_gender(data):
    valid_list = ["male", "female"]
    if data not in valid_list:
        raise ValidationError(
            ‘Invalid gender. Valid choices are‘+ valid_list
        )
#For api validations
class UserSchema(Schema):
    name = fields.String(required=True)
    gender = fields.String(required=True, validate=check_gender)
# Initializing flask app
app = Flask(__name__)
# api to set new user every api call
@app.route("/blockchain/user", methods=[‘POST‘])
def user():
    # Create the contract instance with the newly-deployed address
    user = w3.eth.contract(address=contract_address, abi=abi)
    body = request.get_json()
    result, error = UserSchema().load(body)
    if error:
        return jsonify(error), 422
    tx_hash = user.functions.setUser(
        result[‘name‘],result[‘gender‘]
    ).transact()
    # Wait for transaction to be mined...
    receipt = w3.eth.waitForTransactionReceipt(tx_hash)
    user_data = user.functions.getUser().call()
    return jsonify({"data": user_data}), 200

运行以下命令以启动服务器。

$ FLASK_APP=app.py flask run

用curl调用api

$ curl -H "Content-Type: application/json" --request POST -d ‘{"name":"John Doe","gender":"male"}‘ http://localhost:5000/blockchain/user

你也可以在这里找到完整代码。

python用web3.py库开发以太坊来说非常的方便,有兴趣的用户可以关注python以太坊教程,主要是针对python工程师使用web3.py进行区块链以太坊开发的详解。

原文地址:http://blog.51cto.com/13697184/2165757

时间: 2024-10-24 22:27:49

如何用python和flask以太坊智能合约开发的相关文章

2018年以太坊智能合约开发语言Solidity最佳IDEs

Solidity是一种以智能合约为导向的编程语言.这是一种只有四年的年轻语言,旨在帮助开发基于以太坊数字货币的智能合约. 理解它官方文档应该是学习Solidity的最佳来源:solidity.readthedocs.io 想在以太坊的网络上建立自己的加密货币吗?想拥有自己的初始代码产品吗?以下是您今天可以使用的最佳Solidity IDE. Remix IDE Remix IDE是基于浏览器的,并且集成了编译器.没有服务器端组件. 官网: https://remix.ethereum.orggi

以太坊智能合约开发工具 Truffle 最佳入门指导1

Truffle是以太坊(Ethereum)智能合约开发的瑞士军刀,小巧好用,上手简单. 本篇文章主要展示如何用Truffle 开发第一个Ethereum智能合约. 1.准备工作:(本人针对window环境,如果是mac 或linux可以自行搜索其他教程) a.安装git bash :http://gitforwindows.org/ b.安装npm:https://jingyan.baidu.com/article/a17d528506d7f58098c8f2b0.html 2.安装Truffl

深入以太坊智能合约 ABI

开发 DApp 时要调用在区块链上的以太坊智能合约,就需要智能合约的 ABI.本文希望更多了解 ABI,如为什么需要 ABI?如何解读 Ethereum 的智能合约 ABI?以及如何取得合约的 ABI? 数字猫合约 ABI ABI(Application Binary Interface) 如果理解 API 就很容易了解 ABI.简单来说,API 是程序与程序间互动的接口.这个接口包含程序提供外界存取所需的 functions.variables 等.ABI 也是程序间互动的接口,但程序是被编译

web3j开发以太坊智能合约快速入门(特别适合java和android开发者)

web3j简介 web3j是一个轻量级.高度模块化.响应式.类型安全的Java和Android类库提供丰富API,用于处理以太坊智能合约及与以太坊网络上的客户端(节点)进行集成. 可以通过它进行以太坊区块链的开发,而无需为你的应用平台编写集成代码. 可以快速启动dmeo示例 想要快速启动的话,有一个Web3j demo示例项目可用,演示了通过Web3j开发以太坊的许多核心特征,其中包括: 连接到以太网网络上的节点 加载一个以太坊钱包文件 将以太币从一个地址发送到另一个地址 向网络部署智能合约 从

以太坊智能合约入门项目-众筹项目

一.智能合约 智能合约(英语:Smart Contract)是一种旨在以信息化方式传播.验证或执行合同的计算机协议.智能合约允许在没有第三方的情况下进行可信交易.这些交易可追踪且不可逆转.智能合约概念于1994年由Nick Szabo首次提出. 以上解释来自于维基百科 由于缺少可信的执行环境,智能合约并没有被应用到实际产业中,自比特币诞生后,人们认识到比特币的底层技术区块链天生可以为智能合约提供可信的执行环境,以太坊首先看到了区块链和智能合约的契合,发布了白皮书<以太坊:下一代智能合约和去中心化

rpc接口调用以太坊智能合约

rpc接口调用以太坊智能合约 传送门: 柏链项目学院 ??在以太坊摸爬滚打有些日子了,也遇到了各种各样的问题.这几天主要研究了一下如何通过rpc接口编译.部署和调用合约.也遇到了一些困难和问题,下面将向大家分享. rpc接口调用智能合约 先来编写一个简单的智能合约 contract Multiply7 { event Print(uint); function multiply(uint input) returns (uint) { Print(input * 7); return input

以太坊智能合约及应用开发简介

在这个入门教程中我们将建立以太坊应用开发环境并学习编写一个投票智能合约. 在这个教程中,让我们构建一个简单的"Hello World!" 应用程序, 这是一个投票应用程序. 该应用程序非常简单,它所做的只是初始化一组候选人,让任何人投票给候选人,并显示每个候选人收到的总票数. 我有意避免使用任何DAPP框架构建这个应用程序,因为框架抽象掉很多细节,你不了解系统的内部.此外,当你使用框架时,将对框架所做的繁重工作有更多的体会! 1. 设置开发环境 我们使用一个模拟的内存区块链(ganac

以太坊智能合约编程之菜鸟教程

基本概念 了解这些名词是一个不错的开始: 公钥加密系统. Alice有一把公钥和一把私钥.她可以用她的私钥创建数字签名,而Bob可以用她的公钥来验证这个签名确实是用Alice的私钥创建的,也就是说,确实是Alice的签名.当你创建一个以太坊或者比特币钱包的时候,那长长的0xdf...5f地址实质上是个公钥,对应的私钥保存某处.类似于Coinbase的在线钱包可以帮你保管私钥,你也可以自己保管.如果你弄丢了存有资金的钱包的私钥,你就等于永远失去了那笔资金,因此你最好对私钥做好备份.过来人表示:通过

经典:浅谈以太坊智能合约的设计模式与升级方法

目录 1. 最佳实践 2. 实用设计案例 2.1 控制器合约与数据合约: 1->1 2.2 控制器合约与数据合约: 1->N 2.3 控制器合约与数据合约: N->1 2.4 控制器合约与数据合约: N->N 2.5 总结 3. 升级 3.1 控制器合约升级,数据合约不升级 3.2 控制器合约不升级,数据合约升级 3.3 控制器合约升级,数据合约升级 4. 数据迁移 4.1 硬编码迁移法 4.2 硬拷贝迁移法 4.3 默克尔树迁移法 以太坊EVM是当前区块链行业应用最为广泛的虚拟机