智能合约从入门到精通:完整范例

简介:前几篇文章我们一直在讨论Solidity语言的相关语法,从本文开始,我们将介绍智能合约开发。今天我们将介绍一个完整范例。
此章节将介绍一个完整案例来帮助开发者快速了解合约的开发规范及流程。
注意:
在进行案例编写前,请先前往JUICE开放服务平台,完成用户注册,JUICE区块链账户创建;并下载、安装、配置好JUICE客户端。
场景描述
在案例实践前请确保已拥有可用的JUICE区块链平台环境!!!
现假设一个场景,编写一个顾客管理合约。主要实现以下功能:

  • 提供增加顾客信息功能,手机号作为唯一KEY;
  • 提供根据手机号删除顾客信息的功能;
  • 提供输出所有顾客信息的功能;

接口定义
说明:此接口定义了顾客管理合约的基本操作,接口的定义可以开放给三方进行调用而不暴露源码;
文件目录:${workspace}/contracts/interfaces 用于存放抽象合约目录

pragma solidity ^0.4.2;

contract IConsumerManager {

    function add(string _mobile, string _name, string _account, string _remark) public returns(uint);

    function deleteByMobile(string _mobile) public returns(uint);

    function listAll() constant public returns (string _json);

}
  • add(string _mobile, string _name, string _account, string _remark) 新增一个顾客信息
  • deleteByMobile(string_mobile) 根据手机号删除顾客信息
  • listAll() 输出所有顾客信息,此方法不影响变量状态,因此使用constant修饰;

数据结构定义
说明:当接口中的输入输出数据项比较多,或者存储在链上的数据项比较多时,开发者可以定义一个结构化数据,来简化数据项的声明。并且在这个结构化数据,还可以封装对数据的序列化操作,主要包括通过将json格式转为结构化数据 或 反序列化为json格式。
可以把结构化数据,看成面向对象编程中的对象。
文件目录:${workspace}/contracts/librarys 用于存放数据结构的定义

pragma solidity ^0.4.2;

import "../utillib/LibInt.sol";
import "../utillib/LibString.sol";
import "../utillib/LibStack.sol";
import "../utillib/LibJson.sol";

library LibConsumer {

    using LibInt for *;
    using LibString for *;
    using LibJson for *;
    using LibConsumer for *;

    struct Consumer {
        string mobile;
        string name;
        string account;
        string remark;
    }

    /**
    *@desc fromJson for Consumer
    *      Generated by juzhen SolidityStructTool automatically.
    *      Not to edit this code manually.
    */
    function fromJson(Consumer storage _self, string _json) internal returns(bool succ) {
        _self.reset();

        if (!_json.isJson())
            return false;

        _self.mobile = _json.jsonRead("mobile");
        _self.name = _json.jsonRead("name");
        _self.account = _json.jsonRead("account");
        _self.remark = _json.jsonRead("remark");

        return true;
    }

    /**
    *@desc toJson for Consumer
    *      Generated by juzhen SolidityStructTool automatically.
    *      Not to edit this code manually.
    */
    function toJson(Consumer storage _self) internal constant returns (string _json) {
        LibStack.push("{");
        LibStack.appendKeyValue("mobile", _self.mobile);
        LibStack.appendKeyValue("name", _self.name);
        LibStack.appendKeyValue("account", _self.account);
        LibStack.appendKeyValue("remark", _self.remark);
        LibStack.append("}");
        _json = LibStack.pop();
    }

    /**
    *@desc fromJsonArray for Consumer
    *      Generated by juzhen SolidityStructTool automatically.
    *      Not to edit this code manually.
    */
    function fromJsonArray(Consumer[] storage _self, string _json) internal returns(bool succ) {
        _self.length = 0;

        if (!_json.isJson())
            return false;

        while (true) {
            string memory key = "[".concat(_self.length.toString(), "]");
            if (!_json.jsonKeyExists(key))
                break;

            _self.length++;
            _self[_self.length-1].fromJson(_json.jsonRead(key));
        }

        return true;
    }

    /**
    *@desc toJsonArray for Consumer
    *      Generated by juzhen SolidityStructTool automatically.
    *      Not to edit this code manually.
    */
    function toJsonArray(Consumer[] storage _self) internal constant returns(string _json) {
        _json = _json.concat("[");
        for (uint i=0; i<_self.length; ++i) {
            if (i == 0)
                _json = _json.concat(_self[i].toJson());
            else
                _json = _json.concat(",", _self[i].toJson());
        }
        _json = _json.concat("]");
    }

    /**
    *@desc update for Consumer
    *      Generated by juzhen SolidityStructTool automatically.
    *      Not to edit this code manually.
    */
    function update(Consumer storage _self, string _json) internal returns(bool succ) {
        if (!_json.isJson())
            return false;

        if (_json.jsonKeyExists("mobile"))
            _self.mobile = _json.jsonRead("mobile");
        if (_json.jsonKeyExists("name"))
            _self.name = _json.jsonRead("name");
        if (_json.jsonKeyExists("account"))
            _self.account = _json.jsonRead("account");
        if (_json.jsonKeyExists("remark"))
            _self.remark = _json.jsonRead("remark");

        return true;
    }

    /**
    *@desc reset for Consumer
    *      Generated by juzhen SolidityStructTool automatically.
    *      Not to edit this code manually.
    */
    function reset(Consumer storage _self) internal {
        delete _self.mobile;
        delete _self.name;
        delete _self.account;
        delete _self.remark;
    }

}
  • toJson(Consumer storage _self) 将struct结构序列化为JSON格式:{"mobile":"xxx",...}.
  • fromJson(Consumer storage _self, string _json) 将一个JSON串反序列为struct结构.
  • fromJsonArray(Consumer[] storage _self, string _json),将一个数组形式的JSON串转为数据struct结构
  • toJsonArray(Consumer[] storage _self) 数组结构反序列化,eg.[{"mobile":"xxx",...},...]
  • reset(Consumer _self) 重置struct中为默认值.

业务合约编写
说明:顾客管理合约的主要业务逻辑,即合约接口的实现类. ConsumerManager.sol,该合约继承了基础合约OwnerNamed以及抽象合约IConsumerManager。

  • OwnerNamed 主要提供一些基础操作,主要包含模块注册、合约注册、数据写入DB等操作,所有业务合约需按规定继承该合约。

文件目录:${workspace}/contracts 用于存放业务合约主体逻辑

pragma solidity ^0.4.2;

import "./library/LibConsumer.sol";
import "./sysbase/OwnerNamed.sol";
import "./interfaces/IConsumerManager.sol";
import "./interfaces/IUserManager.sol";
import "./utillib/LibLog.sol";

contract ConsumerManager is OwnerNamed, IConsumerManager {

    using LibConsumer
    for * ;
    using LibString
    for * ;
    using LibInt
    for * ;
    using LibLog
    for * ;

    event Notify(uint _errno, string _info);

    LibConsumer.Consumer[] consumerList;
    mapping(string => uint) keyMap;

    //定义错误信息
    enum ErrorNo {
        NO_ERROR,
        BAD_PARAMETER,
        MOBILE_EMPTY,
        USER_NOT_EXISTS,
        MOBILE_ALREADY_EXISTS,
        ACCOUNT_ALREDY_EXISTS,
        NO_PERMISSION
    }

    // 构造函数,在合约发布时会被触发调用
    function ConsumerManager() {
        LibLog.log("deploy ConsumerModule....");

        //把合约注册到JUICE链上, 参数必须和ConsumerModule.sol中的保持一致
        register("ConsumerModule", "0.0.1.0", "ConsumerManager", "0.0.1.0");

        //或者注册到特殊的模块"juzix.io.debugModule",这样用户就不需要编写模块合约了
        //register("juzix.io.debugModule", "0.0.1.0", "ConsumerManager", "0.0.1.0");
    }

    function add(string _mobile, string _name, string _account, string _remark) public returns(uint) {
        LibLog.log("into add..", "ConsumerManager");
        LibLog.log("ConsumerManager into add..");

        if (_mobile.equals("")) {
            LibLog.log("Invalid mobile.", "ConsumerManager");
            errno = 15200 + uint(ErrorNo.MOBILE_EMPTY);
            Notify(errno, "顾客手机号为空,插入失败.");
            return errno;
        }

        if (keyMap[_mobile] == 0) {
            if (consumerList.length > 0) {
                if (_mobile.equals(consumerList[0].mobile)) {
                    LibLog.log("mobile aready exists", "ConsumerManager");
                    errno = 15200 + uint(ErrorNo.MOBILE_ALREADY_EXISTS);
                    Notify(errno, "顾客手机号已存在,插入失败.");
                    return errno;
                }
            }
        } else {
            LibLog.log("mobile aready exists", "ConsumerManager");
            errno = 15200 + uint(ErrorNo.MOBILE_ALREADY_EXISTS);
            Notify(errno, "顾客手机号已存在,插入失败.");
            return errno;
        }

        uint idx = consumerList.length;
        consumerList.push(LibConsumer.Consumer(_mobile, _name, _account, _remark));

        keyMap[_mobile] = idx;

        errno = uint(ErrorNo.NO_ERROR);

        LibLog.log("add a consumer success", "ConsumerManager");
        Notify(errno, "add a consumer success");
        return errno;
    }

    function deleteByMobile(string _mobile) public returns(uint) {
        LibLog.log("into delete..", "ConsumerManager");

        //合约拥有者,才能删除顾客信息
        if (tx.origin != owner) {
            LibLog.log("msg.sender is not owner", "ConsumerManager");
            LibLog.log("operator no permission");
            errno = 15200 + uint(ErrorNo.NO_PERMISSION);
            Notify(errno, "无操作权限,非管理员");
            return;
        }

        //顾客列表不为空
        if (consumerList.length > 0) {
            if (keyMap[_mobile] == 0) {
                //_mobile不存在,或者是数组第一个元素
                if (!_mobile.equals(consumerList[0].mobile)) {
                    LibLog.log("consumer not exists: ", _mobile);
                    errno = 15200 + uint(ErrorNo.USER_NOT_EXISTS);
                    Notify(errno, "顾客手机号不存在,删除失败.");
                    return;
                }
            }
        } else {
            LibLog.log("consumer list is empty: ", _mobile);
            errno = 15200 + uint(ErrorNo.USER_NOT_EXISTS);
            Notify(errno, "顾客列表为空,删除失败.");
            return;
        }

        //数组总长度
        uint len = consumerList.length;

        //此用户在数组中的序号
        uint idx = keyMap[_mobile];

        if (idx >= len) return;
        for (uint i = idx; i < len - 1; i++) {
            //从待删除的数组element开始,把后一个element移动到前一个位置
            consumerList[i] = consumerList[i + 1];
            //同时修改keyMap中,对应key的在数组中的序号
            keyMap[consumerList[i].mobile] = i;
        }
        //删除数组最后一个元素(和倒数第二个重复了)
        delete consumerList[len - 1];
        //删除mapping中元素,实际上是设置value为0
        delete keyMap[_mobile];

        //数组总长度-1
        consumerList.length--;

        LibLog.log("delete user success.", "ConsumerManager");
        errno = uint(ErrorNo.NO_ERROR);

        Notify(errno, "删除顾客成功.");
    }

    function listAll() constant public returns(string _json) {
        uint len = 0;
        uint counter = 0;
        len = LibStack.push("");
        for (uint i = 0; i < consumerList.length; i++) {
            if (counter > 0) {
                len = LibStack.append(",");
            }
            len = LibStack.append(consumerList[i].toJson());
            counter++;
        }
        len = itemsStackPush(LibStack.popex(len), counter);
        _json = LibStack.popex(len);
    }

    function itemsStackPush(string _items, uint _total) constant private returns(uint len) {
        len = 0;
        len = LibStack.push("{");
        len = LibStack.appendKeyValue("result", uint(0));
        len = LibStack.appendKeyValue("total", _total);
        len = LibStack.append(",\"data\":[");
        len = LibStack.append(_items);
        len = LibStack.append("]");
        len = LibStack.append("}");
        return len;
    }
}

模块合约
说明:模块合约是JUICE区块链中,为了管理用户的业务合约,以及为了管理DAPP和业务的关系而引入的。开发者在实现业务合约后,必须编写一个或多个模块合约,并在模块合约中说明本模块中用到的业务合约。从DAPP的角度来理解,就是一个DAPP必须对应一个模块,一个DAPP能调用的业务合约,必须在DAPP对应的模块合约中说明。
模块合约继承了基础模块合约BaseModule

  • BaseModule 主要提供一些基础操作,主要包含:模块新增、合约新增、角色新增等操作.

文件目录:${workspace}/contracts 用于存放业务模块合约主体逻辑

/**
 * @file      ConsumerModule.sol
 * @author    JUZIX.IO
 * @time      2017-12-11
 * @desc      给用户展示如何编写一个自己的模块。
 *            ConsumerModule本身也是一个合约,它需要部署到链上;同时,它又负责管理用户的合约。只有添加到模块中的用户合约,用户才能在dapp中调用这些合约
 */
pragma solidity ^ 0.4 .2;

//juice的管理库,必须引入
import "./sysbase/OwnerNamed.sol";
import "./sysbase/BaseModule.sol";

//juice提供的模块库,必须引入
import "./library/LibModule.sol";

//juice提供的合约库,必须引入
import "./library/LibContract.sol";

//juice提供的string库
import "./utillib/LibString.sol";

//juice提供的log库
import "./utillib/LibLog.sol";

contract ConsumerModule is BaseModule {

    using LibModule
    for * ;
    using LibContract
    for * ;
    using LibString
    for * ;
    using LibInt
    for * ;
    using LibLog
    for * ;

    LibModule.Module tmpModule;
    LibContract.Contract tmpContract;

    //定义Demo模块中的错误信息
    enum MODULE_ERROR {
        NO_ERROR
    }

    //定义Demo模块中用的事件,可以用于返回错误信息,也可以返回其他信息
    event Notify(uint _code, string _info);

    // module : predefined data
    function ConsumerModule() {

        //定义模块合约名称
        string memory moduleName = "ConsumerModule";

        //定义模块合约名称
        string memory moduleDesc = "顾客模块";

        //定义模块合约版本号
        string memory moduleVersion = "0.0.1.0";

        //指定模块合约ID
        //moduleId = moduleName.concat("_", moduleVersion);
        string memory moduleId = moduleName.concat("_", moduleVersion);

        //把合约注册到JUICE链上
        LibLog.log("register DemoModule");
        register(moduleName, moduleVersion);

        //模块名称,只是JUICE区块链内部管理模块使用,和moduleText有区别
        tmpModule.moduleName = moduleName;
        tmpModule.moduleVersion = moduleVersion;
        tmpModule.moduleEnable = 0;
        tmpModule.moduleDescription = moduleDesc;
        //显示JUICE开放平台,我的应用列表中的DAPP名字
        tmpModule.moduleText = moduleDesc;

        uint nowTime = now * 1000;
        tmpModule.moduleCreateTime = nowTime;
        tmpModule.moduleUpdateTime = nowTime;

        tmpModule.moduleCreator = msg.sender;

        //这里设置用户DAPP的连接地址(目前DAPP需要有用户自己发布、部署到公网上)
        tmpModule.moduleUrl = "http://host.domain.com/youDapp/";

        tmpModule.icon = "";
        tmpModule.publishTime = nowTime;

        //把模块合约本身添加到系统的模块管理合约中。这一步是必须的,只有这样,用户的dapp才能调用添加到此模块合约的相关合约。
        //并在用户的“我的应用”中展示出来
        LibLog.log("add ConsumerModule to SysModule");
        uint ret = addModule(tmpModule.toJson());

        if (ret != 0) {
            LibLog.log("add ConsumerModule to SysModule failed");
            return;
        }

        //添加用户合约到模块合约中
        LibLog.log("add ConsumerManager to ConsumerModule");
        ret = initContract(moduleName, moduleVersion, "ConsumerManager", "顾客管理合约", "0.0.1.0");
        if (ret != 0) {
            LibLog.log("add ConsumerManager to ConsumerModule failed");
            return;
        }

        //返回消息,以便控制台能看到是否部署成功
        Notify(1, "deploy ConsumerModule success");
    }

    /**
     * 初始化用户自定义合约。
     * 如果用户有多个合约文件,则需要多次调用此方法。
     * @param moduleName        约合所属模块名
     * @param moduleVersion     约合所属模块版本
     * @param contractName      约合名
     * @param contractDesc      约合描述
     * @param contractVersion   约合版本
     * @return return 0 if success;
     */
    function initContract(string moduleName, string moduleVersion, string contractName, string contractDesc, string contractVersion) private returns(uint) {
        tmpContract.moduleName = moduleName;
        tmpContract.moduleVersion = moduleVersion;

        //合约名称
        tmpContract.cctName = contractName;
        //合约描述
        tmpContract.description = contractDesc;
        //合约版本
        tmpContract.cctVersion = contractVersion;

        //保持false
        tmpContract.deleted = false;
        //保持0
        tmpContract.enable = 0;

        uint nowTime = now * 1000;
        //合约创建时间
        tmpContract.createTime = nowTime;
        //合约修改时间
        tmpContract.updateTime = nowTime;

        //合约创建人
        tmpContract.creator = msg.sender;
        //预约块高
        tmpContract.blockNum = block.number;

        uint ret = addContract(tmpContract.toJson());
        return ret;
    }

}
  • 模块合约作用:当进行一个新的DAPP开发时会伴随着一些合约的业务服务的编写,即,合约为DAPP应用提供业务逻辑的服务,我们将这一类(或一组)合约统一归属到一个模块中(eg:HelloWorldModuleMgr)。在JUICE区块链平台上有一套鉴权体系,一个合约要被成功调用需要经过多层鉴权:

o 校验模块开关,开:继续鉴权,关:直接通过
o 校验合约开关,开:继续鉴权,关:直接通过
o 检验函数开关,开:继续鉴权,关:直接通过
o 校验用户是否存在,存在则访问通过,不存在则鉴权失败
注意:如果是合约发布者owner(超级管理员)则不需要鉴权可直接通过。

  • HelloWorldModuleMgr该合约的主要功能就是做数据的初始化操作,当合约被发布时触发构造函数的调用。

o 添加一个新的模块到角色过滤器(默认过滤器)
o 添加绑定合约与模块的关系
o 添加菜单(新的DAPP如果需要菜单-如:用户管理)
o 添加权限,合约中的每个函数操作都是一个Action,如果需要访问就需要进行配置;
o 添加角色,初始化某些角色到模块中,并绑定对应的权限到角色上;
编译部署、测试
编译部署

业务合约,模块合约编写完成后

  • 首先,处理业务合约
    1.编译业务合约,编译成功后,在控制台分别复制出ABI,BIN,并分别保存到contracts/ConsumerManager.abi,contracts/ConsumerManager.bin文本文件中。这两个文件,可以用web3j生成调用业务合约的JAVA代理类,这个在编写DAPP时有用,因此在编译阶段就先保存这两个文件。(注:JUICE客户端的后续版本中,将在编译业务合约时,直接生成JAVA代理类,开发者不用再手工保存bin/abi,再手工生成JAVA代理类)
    2.部署业务合约
  • 然后,处理模块合约

2.1.编译模块合约。编译成功后的的bin/abi,不需要保存。
2.部署模块合约

测试
在JUICE客户端中,选择需要测试的业务合约,以及相应的业务方法,然后填写输入参数,即可运行。用户可观察控制台的日志输出,来判断业务方法是否执行成功。

参考内容:https://open.juzix.net/doc
智能合约开发教程视频:区块链系列视频课程之智能合约简介

原文地址:http://blog.51cto.com/13544628/2137202

时间: 2024-11-06 03:51:19

智能合约从入门到精通:完整范例的相关文章

智能合约从入门到精通:用Solidity开发一个“Hello World”

简介:上一章中我们聊到了智能合约的应用场景,在了解区块链技术目前的发展情况.智能合约的概念以及其应用场景之后,我们将在后续的文章中共同学习智能合约的编成语言.今天,我们就来简单地谈一谈用于编写智能合约的语言--Solidity语言. 那么,什么是Solidity语言呢?在前面的文章中我们反复提到过以太坊,以太坊做区块链2.0的典型代表,Solidity在以太坊中是撰写智能合约最受欢迎的语言,因此今天我们就和大家介绍一下这个作为智能合约开发中最主流的语言. Solidity是运行于Ethereum

智能合约从入门到精通:Solidity语法之内存变量的布局和状态变量的存储模型

简介:在前面我们已经讲过Solidity语言的一些语法内容,在矩阵元JUICE开放平台的JIDE开发时,一定要注意Layout in Memory和Layout of State Variables in Storage,即内存变量的布局和状态变量的存储模型.内存变量的布局(Layout in Memory) Solidity预留了3个32字节大小的槽位: 0-64:哈希方法的暂存空间(scratch space) 64-96:当前已分配内存大小(也称空闲内存指针(free memory poi

智能合约从入门到精通:Solidity Assembly

简介:上一节,我们讲过Solidity 汇编语言,这个汇编语言,可以不同Solidity一起使用.这个汇编语言还可以嵌入到Solidity源码中,以内联汇编的方式使用.下面我们将从内联汇编如何使用着手,介绍其与独立使用的汇编语言的不同,最后再介绍这门汇编语言.Solidity Assembly内联汇编通常我们通过库代码,来增强语言我,实现一些精细化的控制,Solidity为我们提供了一种接近于EVM底层的语言,内联汇编,允许与Solidity结合使用.由于EVM是栈式的,所以有时定位栈比较麻烦,

智能合约从入门到精通:JIDE集成开发工具

简介:前面两节,我们介绍了JIDE的工具库,本节我们将介绍智能合约开发最后一段内容,JIDE集成开发工具.除了JIDE的安装,我们还将介绍智能合约的编写.编译.部署.运行以及查看日志等.您看过所有的内容之后,将可以完整的开发出一个智能合约.JIDE简介JIDE是JUICE开放服务平台提供用来编写智能合约及DApp的免费开发工具,JIDE集成了JUICE底层测试链.编辑器和Solidity运行时环境,用户无需搭建任何环境就可以快速地编写.编译.部署.运行智能合约与发布DApp.目前JIDE支持So

智能合约从入门到精通:智能合约的前世今生

简介:区块链技术由来已久.2008年,神秘的日裔美国人中本聪天才设计出来的电子货币系统:比特币,为人类打开了区块链的大门.2009年,他发布了首个比特币软件,并正式启动了比特币金融系统.依靠POW算法的比特币随着人们认知,2016年以后区块链也为人们所追捧.而以太坊的快速发展,区块链已经进化出智能合约.DApp等新的技术,区块链2.0.3.0时代也正在来临.本文将详细介绍智能合约是什么.区块链是分布式数据存储.点对点传输.共识机制.加密算法等计算机技术的新型应用模式.所谓共识机制是区块链系统中实

智能合约从入门到精通:智能合约的应用场景

简介:上一章我们简单介绍了区块链技术的发展情况,并详细介绍智能合约的基础概念.可以说,区块链1.0时代(比特币)除了代币功能,与现实生活相去甚远.智能合约的出现,给区块链带来了巨大的变化,区块链也从一种账本形式,变成了下一代计算网络的雏形.这一章我们将聊聊智能合约的应用场景.在上一篇文章中我们聊过,尼克·萨博对智能合约定义已经说的很清楚,智能合约不是一定要用区块链来实现,很久之前就已经出现了:比如微信和支付宝的信用卡自动还款,您可以认为他是一种智能合约.当还款日到了,还款条件也满足(支付宝的余额

智能合约从入门到精通:Solidity语言的开发规范和开发流程

简介:上面介绍的在Solidity中嵌入的内联汇编语言也可以单独使用.实际上,它是被计划用来作为编译器的一种中间语言.本文我们将介绍开发智能合约过程中Solidity语言的开发规范和开发流程. Solidity作为编译器的一种中间语言.在开发智能合约时需要遵守相应的开发规范和开发流程. 开发规范 命名规范 目录和文件 目录使用小写,请勿使用特殊符号: 库文件和合约文件统一以.sol为后缀: 合约文件名保持与合约名一致: 文件名采用驼峰命名(首字母大写): 合约.库文件命名 合约名采用驼峰命名(首

智能合约从入门到精通:Solidity汇编语言

简介:上一节,我们讲过在JUICE平台开发智能合约的开发规范,本节我们将继续就Solidity定义的汇编语言进行更加深入的讨论.Solidity定义的汇编语言可以达到下述的目标:1.使用它编写的代码要可读,即使代码是从Solidity编译得到的.2.从汇编语言转为字节码应该尽可能的少坑.3.控制流应该容易检测来帮助进行形式验证与优化.为了达到第一条和最后一条的目标,Solidity汇编语言提供了高层级的组件比如,for循环,switch语句和函数调用.这样的话,可以不直接使用SWAP,DUP,J

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

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