知世金融网专注于股票行情,期货开户,外汇储备等最新相关资讯信息提供投资者参考学习!

当前位置:网站首页 > 区块链 > 正文

Java开发的智能合约单元测试教程

原创
文章作者
知世-金融领域资深作者
知名金融领域作者,从事金融超过十余年,在行业内有一定影响力。
金融风险管理师认证证书 常识职业资格认证 特许金融分析师 国际金融理财师认证证书
发布时间:2020-02-16 20:49:14 发布来源:区块链研究实验室 文章点击:114

在本教程中,我们将实现一个简单的合约,为其编写单元测试,并运行调试过程以查找Bug。 运行该示例所需的全部是Java和IDE。 只需创建一个依赖AVM最新工具库的Java项目作为库。 1.编写...

目录

    本文标题Java开发的智能合约单元测试教程,作者:知世,本文有1810个文字,大小约为7KB,预计阅读时间5分钟,请您欣赏。知世金融网众多优秀文章,如果想要浏览更多相关文章,请使用网站导航的搜索进行搜索。本站虽然不乏优秀之作,但仅作为投资者学习参考。

    在本教程中,我们将实现一个简单的合约,为其编写单元测试,并运行调试过程以查找Bug。

    运行该示例所需的全部是Java和IDE。 只需创建一个依赖AVM最新工具库的Java项目作为库。

    1.编写智能合约

    首先创建智能合约。以下是可用于简单投票DApp的智能合约的示例。

    智能合约的功能:

    1. 只有合约所有者(部署合约的帐户)可以添加或删除成员。
    2. 只有会员可以介绍新的提案。
    3. 只有会员可以对提案进行投票。
    4. 如果超过50%的成员赞成该提案并对其投票,该提案将通过。

    package org.aion.avm.embed.temp;

    import avm.Blockchain;
    import org.aion.avm.tooling.abi.Callable;
    import org.aion.avm.userlib.AionMap;
    import org.aion.avm.userlib.AionSet;
    import avm.Address;
    import org.aion.avm.userlib.abi.ABIDecoder;

    public class Voting {

    private static AionSet<Address> members = new AionSet<>();
    private static AionMap<Integer,

    Proposal> proposals = new AionMap<>();
    private static Address owner;
    private static int minimumQuorum;

    static {
    ABIDecoder decoder = new ABIDecoder(Blockchain.getData());

    Address[] arg = decoder.decodeOneAddressArray();
    for (Address addr : arg) {
    members.add(addr);
    }
    updateQuorum();
    owner = Blockchain.getCaller();
    }

    @Callable
    public static void addProposal(String description) {
    Address caller = Blockchain.getCaller();
    Blockchain.require(isMember(caller));

    Proposal proposal = new Proposal(description, caller);
    int proposalId = proposals.size();
    proposals.put(proposalId, proposal);

    Blockchain.log("NewProposalAdded".getBytes(),

    Integer.toString(proposalId).getBytes(), caller.toByteArray(),

    description.getBytes());
    }

    @Callable
    public static void vote(int proposalId) {
    Address caller = Blockchain.getCaller();
    Blockchain.require(isMember(caller) && proposals.containsKey(

    proposalId));

    Proposal votedProposal = proposals.get(proposalId);
    votedProposal.voters.add(caller);

    Blockchain.log("Voted".getBytes(),

    Integer.toString(proposalId).getBytes(), caller.toByteArray());

    if (!votedProposal.isPassed && votedProposal.voters.size() ==

    minimumQuorum) {
    votedProposal.isPassed = true;
    Blockchain.log("ProposalPassed".getBytes(),

    Integer.toString(proposalId).getBytes());
    }
    }

    @Callable
    public static void addMember(Address newMember) {
    onlyOwner();
    members.add(newMember);
    updateQuorum();
    Blockchain.log("MemberAdded".getBytes(),

    newMember.toByteArray());
    }

    @Callable
    public static void removeMember(Address member) {
    onlyOwner();
    members.remove(member);
    updateQuorum();
    Blockchain.log("MemberRemoved".getBytes(),

    member.toByteArray());
    }

    @Callable
    public static String getProposalDescription(int proposalId) {
    return proposals.containsKey(proposalId) ?

    proposals.get(proposalId).description : null;
    }

    @Callable
    public static Address getProposalOwner(int proposalId) {
    return proposals.containsKey(proposalId) ?

    proposals.get(proposalId).owner : null;
    }

    @Callable
    public static boolean hasProposalPassed(int proposalId) {
    return proposals.containsKey(proposalId) &&

    proposals.get(proposalId).isPassed;
    }

    @Callable
    public static int getMinimumQuorum() {
    return minimumQuorum;
    }

    @Callable
    public static boolean isMember(Address address) {
    return members.contains(address);
    }

    private static void onlyOwner() {
    Blockchain.require(owner.equals(Blockchain.getCaller()));
    }

    private static void updateQuorum() {
    minimumQuorum = members.size() / 2 + 1;
    }

    private static class Proposal {
    String description;
    Address owner;
    boolean isPassed;
    AionSet<Address> voters = new AionSet<>();

    Proposal(String description, Address owner) {
    this.description = description;
    this.owner = owner;
    }
    }
    }

    合约中的static块在部署时仅执行一次。 我们在此块中设置初始成员,minimumQuorum和所有者。尽管我们与一组成员启动了合约,但所有者随后也可以添加和删除成员。

    我们使用AionSet和AionMap跟踪成员及其提案。可以使用其唯一标识符从地图访问每个建议。

    智能合约的主要职能是:

    · addProposal,允许成员添加提案说明以进行投票。
    · vote,允许成员通过传递其ID对可用提案进行投票。赢得多数成员投票的提案将通过。请注意,将生成ProposalPassed事件以记录已通过投标的ID。

    2.编写单元测试

    我们将使用AvmRule编写测试。 AvmRule是用于在嵌入式AVM上测试合约的Junit规则。 它创建Aion内核和AVM的内存中表示形式。每次我们运行测试时,都会刷新内置的内核和AVM实例。

    在测试我们的合约之前,我们需要将其部署到内存中的Aion区块链中,并且我们将使用AvmRule完成此任务。

    A.实例化AvmRule

    您可以看到该规则包含一个布尔参数,该布尔参数启用/禁用调试模式。 最好在启用调试器的情况下编写测试。您可以在下一部分中查看如何调试合约。

    @Rule
    public AvmRule avmRule = new AvmRule(true);

    注意:此行将为每种测试方法实例化嵌入式AVM。如果将规则定义为@classRule,则将仅为测试类实例化AVM和内核的一个实例。

    B.获取合约字节

    现在,我们必须获取与合约jar的内存表示相对应的字节。为了获取字节,我们将使用AvmRule中的getDappBytes方法。

    getDappBytes采用以下参数:

    1. 合约的主要类别。
    2. 合约构造函数参数,可以在静态块中对其进行访问和解码。
    3. DApp jar中需要包含其他类。

    public byte[] getDappBytes(Class<?> mainClass, byte[] arguments,
    Class<?>... otherClasses)

    C.部署您的智能合约

    使用部署功能可以轻松完成智能合约的部署。

    public ResultWrapper deploy(Address from, BigInteger value,
    byte[] dappBytes)

    AvmRule还提供了在Aion内核中创建具有初始余额的帐户的功能。

    以下是由3名成员组成的小组部署投票智能合约的方法。

    public class VotingContractTest {

    @Rule
    public AvmRule avmRule = new AvmRule(true);

    public Address dappAddress;
    public Address owner = avmRule.getPreminedAccount();
    public Address[] members = new Address[3];

    @Before
    public void setup() {
    for (int i = 0; i < members.length; i++) {
    // create accounts with initial balance
    members[i] = avmRule.getRandomAddress(

    BigInteger.valueOf(10_000_000L));
    }

    // encode members array as an argument
    byte[] deployArgument = ABIUtil.encodeOneObject(members);
    // get the bytes representing the in memory jar
    byte[] dappBytes = avmRule.getDappBytes(

    Voting.class, deployArgument);
    //deploy and get the contract address
    dappAddress = avmRule.deploy(

    owner, BigInteger.ZERO, dappBytes).getDappAddress();
    }
    }

    D.调用方法

    我们可以通过以下方式调用合同中的方法:

    1. 编码方法名称及其参数。
    2. 将编码后的字节传递给AvmRule的call方法。

    public ResultWrapper call(Address from, Address dappAddress,
    BigInteger value, byte[] transactionData)

    例如我们创建一个新的提案。我们将通过检查是否生成了NewProposalAdded事件以及事件主题和数据正确来验证提案。

    @Test
    public void addProposalTest() {
    String description = "new proposal description";
    byte[] txData = ABIUtil.encodeMethodArguments(

    "addProposal", description);
    AvmRule.ResultWrapper result = avmRule.call(

    members[0], dappAddress, BigInteger.ZERO, txData);
    // assert the transaction was successful
    Assert.assertTrue(result.getReceiptStatus().isSuccess());

    // assert the event is generated
    Assert.assertEquals(1, result.getLogs().size());
    Log log = result.getLogs().get(0);

    // validate the topics and data
    Assert.assertArrayEquals(LogSizeUtils.truncatePadTopic(

    "NewProposalAdded".getBytes()), log.copyOfTopics().get(0));
    Assert.assertArrayEquals(LogSizeUtils.truncatePadTopic(

    Integer.toString(0).getBytes()), log.copyOfTopics().get(1));
    Assert.assertArrayEquals(LogSizeUtils.truncatePadTopic(

    members[0].toByteArray()), log.copyOfTopics().get(2));
    Assert.assertArrayEquals(description.getBytes(), log.copyOfData());
    }

    现在我们将提交一个提案以及两个投票。由于有两个不同的成员以ID 0对提案进行了投票,提案应通过。因此我们希望为最后一个事务生成两个不同的事件-Voted和ProposalPassed。

    您还可以通过提案ID查询提案的状态。您会看到返回的解码数据为true,表示提案已通过。

    @Test
    public void voteTest() {
    String description = "new proposal description";
    byte[] txData = ABIUtil.encodeMethodArguments(

    "addProposal", description);
    AvmRule.ResultWrapper result = avmRule.call(

    members[0], dappAddress, BigInteger.ZERO, txData);
    Assert.assertTrue(result.getReceiptStatus().isSuccess());
    Assert.assertEquals(1, result.getLogs().size());
    //vote #1
    txData = ABIUtil.encodeMethodArguments("vote", 0);
    result = avmRule.call(

    members[1], dappAddress, BigInteger.ZERO, txData);
    Assert.assertTrue(result.getReceiptStatus().isSuccess());
    Assert.assertEquals(1, result.getLogs().size());
    //vote #2
    txData = ABIUtil.encodeMethodArguments("vote", 0);
    result = avmRule.call(

    members[2], dappAddress, BigInteger.ZERO, txData);
    Assert.assertTrue(result.getReceiptStatus().isSuccess());
    Assert.assertEquals(2, result.getLogs().size());
    //validate that the proposal is stored as passed
    txData = ABIUtil.encodeMethodArguments("hasProposalPassed", 0);
    result = avmRule.call(

    members[2], dappAddress, BigInteger.ZERO, txData);
    //decode the return data as boolean
    Assert.assertTrue((boolean) result.getDecodedReturnData());
    }

    3.调试智能合约

    调试我们的智能合约非常容易,只需在源代码中设置断点即可!由于我们在启用调试的情况下创建了AvmRule,因此在达到断点时将停止执行。让我们来看一个例子。

    这是部署后智能合约的状态。

    您可以看到该智能合约具有:

    1. 3名成员。
    2. 0个提案。
    3. minimumQuorum = 2。

    您也可以检查每个集合的内容。例如通过调用addProposal,您将能够看到更新的AionMap。

    让我们实际对调试器进行测试。我们将故意在评估提案通过方式时造成一个简单的错误。我将修改提案通过条件,如下所示。 请注意,等于条件已更改为小于或等于。

    if (!votedProposal.isPassed && votedProposal.voters.size()
    <= minimumQuorum)

    现在当第一个所有者提交投票时,提案将通过。

    让我们调试方法调用并逐步执行该函数。

    您可以看到,尽管minimumQuorum等于2,但是该提案的投票者计数仅为1。我们修改了if语句(来自上面),并且第51行的isPassed标志设置为true。

    从那里,您可以轻松地确定错误在代码中的位置。

    结论

    如果您曾经为以太坊开发过智能合约,就会知道用一种陌生的专用语言编写合约并对其进行调试的痛苦。

    任何熟悉Java的人都会感觉像在家中一样使用AVM编写智能合约。另外市场上任何IDE中的所有调试功能都可用于测试和调试Java智能合约。

    本文相关推荐: 永续合约的杠杆倍数

    以上便是知世金融网给大家分享的关于Java开发的智能合约单元测试教程/qkl/28977.html的相关信息了,希望能帮助到大家,更多金融相关信息,敬请关注知世金融网!

    网站内容均来自互联网,如侵害您的利益联系客服进行删除!

    关键词:合约
    (0)
    (0)

    上一篇:闲谈哈耶克、中本聪与biteb

    下一篇:论区块链原教旨主义

    本文标题:Java开发的智能合约单元测试教程

    本文地址:/index.php?s=article&c=search&keyword=%E5%90%88%E7%BA%A6

    金融知名领域

    南方财富网 | 金融界 | 金融界 |

    更多推荐

    • 茅台吃饱,经销商哭倒
      茅台吃饱,经销商哭倒
    • 汇金的五次增持从短期看具有一定的“稳定器“作用,但从市场表现看效果逐次递减
      汇金的五次增持从短期看具有一定的“稳定器“作用,但从市场表现看效果逐次递减
    • 158亿元!比亚迪收购!
      158亿元!比亚迪收购!
    • 9月价格回落近五成 “冷静期”酒店业备战“十一”市场
      9月价格回落近五成 “冷静期”酒店业备战“十一”市场
    • 2023哈马博览会哈尔滨银行展区精彩纷呈
      2023哈马博览会哈尔滨银行展区精彩纷呈
    • 大额解禁撂倒股价 医疗影像龙头跌出千亿俱乐部 葛兰二季度大幅减仓
      大额解禁撂倒股价 医疗影像龙头跌出千亿俱乐部 葛兰二季度大幅减仓
    • A股,又上了热搜!数字要素概念走高多股涨停,锂电池板块走低恩捷股份大举跌停
      A股,又上了热搜!数字要素概念走高多股涨停,锂电池板块走低恩捷股份大举跌停
    • 最新!巨头出手,加仓宁王51%
      最新!巨头出手,加仓宁王51%
    • 600亿巨头暴雷
      600亿巨头暴雷
    • 一天32家!科创板回购潮涌来
      一天32家!科创板回购潮涌来
    • 提振信心实招来了!30余家上市公司密集出手 最高要买10亿
      提振信心实招来了!30余家上市公司密集出手 最高要买10亿
    • 高盛再发50年后预测:2075年印度股市全球市值占比将升4倍 中国升3成
      高盛再发50年后预测:2075年印度股市全球市值占比将升4倍 中国升3成

    新闻资讯栏目

    站长QQ: 2397470084