3年前 (2021-10-06)  相关技术 |   抢沙发  264 
文章评分 0 次,平均分 0.0

欢迎您是全球710万活跃的Java开发人员之一,也可能是1200万在一生中学习过Java语言的开发人员之一。

在本文中,我将指导您使用Corda构建第一个区块链应用程序,Corda是一个在JVM中运行的区块链平台,仅使用Java。准备好在Java同行中脱颖而出!

如果您只懂Java,如何开发区块链应用程序?

你需要什么…

在本文中,我不会详细介绍区块链是什么以及人们为什么需要区块链。我将直接进入编码,并在进行过程中解释去中心化的逻辑。因此,以下是您需要遵循的内容:

1. 您可以选择IDE或代码编辑器。

2. 具有最小2GB RAM的JVM环境。(大多数台式机/笔记本电脑/虚拟机都能够运行Corda。我建议您在Pi上运行应用程序!)。

3. 最后,但并非最不重要的是,您的Java知识!

我们应该建造什么…

当然,我们将从“Hello World”应用程序开始!我们将构建一个应用程序,从区块链上的一方向另一方发送“Hello World”消息。

Corda世界中,您编写的区块链应用程序称为CorDapp。它仅由三个部分组成:

  • 状态:创建、更新、存储和使用的真实资产/对象。例如,一本借书、一辆标记化自行车或一个游戏板。
  • 合同:规范/验证国家交易、更新和消费的规则。
  • 流程:指示如何执行每个事务的指令。

不用多说,让我们开始编码吧。

步骤1:Corda 状态

在此处克隆Cordapp代码(https://github.com/corda/cordapp-template-java)。

使用您选择的代码编辑器打开它,然后导航到:

/contracts/src/main/java/com/template/states/TemplateState.java

@BelongsToContract(TemplateContract.class)
public class TemplateState implements ContractState {
/* Constructor of your Corda state */
public TemplateState() {
    }
/* This method will indicate who are the participants and required signers when 
     * this state is used in a transaction. */
@Override
public List<AbstractParty> getParticipants() {
return Arrays.asList();
    }
}

在Corda世界中,大多数数据都是在一个名为State的对象中处理的。每个状态都有一个@BelongsToContract()注释,指示将使用哪个合同文件在事务中验证此状态。State和合同是1比1挂钩的。继续我们的Hello world CorDapp,我们只需添加几行代码,使上面的TemplateState包含一条消息。我们的州将有3个属性。

  • 字符串消息
  • 发信人
  • 信息接收者
@BelongsToContract(TemplateContract.class)
public class TemplateState implements ContractState {

    //private variables
    private String msg;
    private Party sender;
    private Party receiver;

    /* Constructor of your Corda state */
    public TemplateState(String msg, Party sender, Party receiver) {
        this.msg = msg;
        this.sender = sender;
        this.receiver = receiver;
    }

    //getters
    public String getMsg() { return msg; }
    public Party getSender() { return sender; }
    public Party getReceiver() { return receiver; }

    /* This method will indicate who are the participants and required signers when
     * this state is used in a transaction. */
    @Override
    public List<AbstractParty> getParticipants() {
        return Arrays.asList(sender,receiver);
    }
}

创建三个变量,实例化变量,创建getter,填写参与者的列表,然后就是Corda状态。创建区块链应用程序的第一步已经完成。

步骤2:Corda合同

导航到/contracts/src/main/java/com/template/contracts/TemplateContract.java

public class TemplateContract implements Contract {
    // This is used to identify our contract when building a transaction.
    public static final String ID = "com.template.contracts.TemplateContract";

    // A transaction is valid if the verify() function of the contract of all the transaction's input and output states
    // does not throw an exception.
    @Override
    public void verify(LedgerTransaction tx) {}

    // Used to indicate the transaction's intent.
    public interface Commands extends CommandData {
        class Action implements Commands {}
    }
}

在Corda世界中,涉及一个国家的每一笔交易都必须通过合同进行验证。每份合同由三个必要部分组成:

  • 用于以后参考的合同Id
  • 检查事务的verify()方法
  • 一个命令界面,用于指示事务关于状态的意图。请注意,所有标准契约(不包括令牌契约,因为它们是预先编写的)都必须使用命令,为了方便起见,我们通常在接口中定义它们。
public class TemplateContract implements Contract {
    // This is used to identify our contract when building a transaction.
    public static final String ID = "com.template.contracts.TemplateContract";

    // A transaction is valid if the verify() function of the contract of all the transaction's input and output states
    // does not throw an exception.
    @Override
    public void verify(LedgerTransaction tx) {

        /* We can use the requireSingleCommand function to extract command data from transaction.
         * However, it is possible to have multiple commands in a signle transaction.*/
        final CommandWithParties<Commands> command = requireSingleCommand(tx.getCommands(), Commands.class);
        final Commands commandData = command.getValue();

        if (commandData.equals(new Commands.Send())) {
            //Retrieve the output state of the transaction
            TemplateState output = tx.outputsOfType(TemplateState.class).get(0);

            //Using Corda DSL function requireThat to replicate conditions-checks
            requireThat(require -> {
                require.using("The message must be Hello-World", output.getMsg().equals("Hello-World"));
                return null;
            });
        }
    }

    // Used to indicate the transaction's intent.
    public interface Commands extends CommandData {
        //In our hello-world app, We will only have one command.
        class Send implements Commands {}
    }
}

在Commands界面中,我们创建了一个名为send的命令,稍后我们将使用该命令指示事务的意图。

在verify()方法中,我们询问:

事务内部状态中携带的消息字符串必须是“Hello World”。

到这一步结束时,您将已经编写了合同。

步骤3:Corda流程

拥有可操作区块链应用程序之前的最后一步!导航到/workflows/src/main/java/com/template/flows/Initiator.java

@InitiatingFlow
@StartableByRPC
public class Initiator extends FlowLogic<Void> {
    private final ProgressTracker progressTracker = new ProgressTracker();

    @Override
    public ProgressTracker getProgressTracker() {
        return progressTracker;
    }

    @Suspendable
    @Override
    public Void call() throws FlowException {
        // Initiator flow logic goes here.

        return null;
    }
}

在Corda世界中,启动器流触发应用程序中的业务操作。无论是发送事务还是传递消息,都必须有一个启动器来发出操作信号。除了私有变量和构造函数之外,发起程序流还包括一个名为call()的签名方法。

每当调用启动器时,此方法将自动运行。这是将有效负载添加到事务、签名和签名集合的地方。现在,让我们根据Hello World的情况填写一下。

@InitiatingFlow
@StartableByRPC
public class Initiator extends FlowLogic<SignedTransaction> {

    // We will not use these ProgressTracker for this Hello-World sample
    private final ProgressTracker progressTracker = new ProgressTracker();
    @Override
    public ProgressTracker getProgressTracker() {
        return progressTracker;
    }

    //private variables
    private Party sender ;
    private Party receiver;

    //public constructor
    public Initiator(Party sendTo){
        this.receiver = sendTo;
    }

    @Suspendable
    @Override
    public SignedTransaction call() throws FlowException {
        //Hello World message
        String msg = "Hello-World";
        this.sender = getOurIdentity();

        // Step 1. Get a reference to the notary service on our network and our key pair.
        // Note: ongoing work to support multiple notary identities is still in progress.
        final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);

        //Compose the State that carries the Hello World message
        final TemplateState output = new TemplateState(msg,sender,receiver);

        // Step 3. Create a new TransactionBuilder object, and add the iou as an output state, 
        //as well as a command to the transaction builder.
        final TransactionBuilder builder = new TransactionBuilder(notary);
        builder.addOutputState(output);
        builder.addCommand(new TemplateContract.Commands.Send(), Arrays.asList(this.sender.getOwningKey(),this.receiver.getOwningKey()) );


        // Step 4. Verify and sign it with our KeyPair.
        builder.verify(getServiceHub());
        final SignedTransaction ptx = getServiceHub().signInitialTransaction(builder);


        // Step 5. Collect the other party's signature using the SignTransactionFlow.
        List<Party> otherParties = output.getParticipants().stream().map(el -> (Party)el).collect(Collectors.toList());
        otherParties.remove(getOurIdentity());
        List<FlowSession> sessions = otherParties.stream().map(el -> initiateFlow(el)).collect(Collectors.toList());

        SignedTransaction stx = subFlow(new CollectSignaturesFlow(ptx, sessions));

        // Step 6. Assuming no exceptions, we can now finalise the transaction
        return subFlow(new FinalityFlow(stx, sessions));
    }
}

在这个Hello World启动器流中,我们必须:

1. 获得公证处的证明。[防止双重开支]

2. 撰写包含Hello World消息的状态。[产生输出]

3. 创建一个新的TransactionBuilder对象,将带有Hello World消息的状态添加为输出状态,并将命令添加到事务生成器中。[组成交易]

4. 验证并用我们的密钥对签名。[自行核实并签字]

5. 使用SignTransactionFlow收集另一方的签名。[在交易对手之间达成共识]

6. 完成交易并向所有相关方广播。[将交易存储到分类账]

这将是向交易对手发送Hello World的发起方流程。但是,我们还没有结束。我们必须注意该启动器的响应部分。现在,导航到/workflows/src/main/java/com/template/flows/Responder.java

@InitiatedBy(Initiator.class)
public class Responder extends FlowLogic<Void> {

    //private variable
    private FlowSession counterpartySession;
    
    //Constructor
    public Responder(FlowSession counterpartySession) {
        this.counterpartySession = counterpartySession;
    }

    @Suspendable
    @Override
    public Void call() throws FlowException {
        // Responder flow logic goes here.

        return null;
    }
}

响应程序流,如其名称所示,将从对方方响应相应的发起程序流。这个annotation@InitiatedBy显示它与哪个启动器关联。

同样,响应者也有一个自动运行调用(),该方法将在本地重新检查并保存事务(在对方节点端,因为响应者是在对方端触发的)。

@InitiatedBy(Initiator.class)
public class Responder extends FlowLogic<Void> {

    //private variable
    private FlowSession counterpartySession;

    //Constructor
    public Responder(FlowSession counterpartySession) {
        this.counterpartySession = counterpartySession;
    }

    @Suspendable
    @Override
    public Void call() throws FlowException {
        SignedTransaction signedTransaction = subFlow(new SignTransactionFlow(counterpartySession) {
            @Suspendable
            @Override
            protected void checkTransaction(SignedTransaction stx) throws FlowException {
                /*
                * SignTransactionFlow will automatically verify the transaction and its signatures before signing it.
                * However, just because a transaction is contractually valid doesn’t mean we necessarily want to sign.
                * What if we don’t want to deal with the counterparty in question, or the value is too high,
                * or we’re not happy with the transaction’s structure? checkTransaction
                * allows us to define these additional checks. If any of these conditions are not met,
                * we will not sign the transaction - even if the transaction and its signatures are contractually valid.
                * ----------
                * For this hello-world cordapp, we will not implement any aditional checks.
                * */
            }
        });
        //Stored the transaction into data base.
        subFlow(new ReceiveFinalityFlow(counterpartySession, signedTransaction.getId()));
        return null;
    }
}

到目前为止,您已经完成了第一个区块链应用程序的编写。我们要试着运行它们吗?

正在运行Hello World区块链应用程序…

转到终端,导航到项目文件夹/cordapp template java/并运行以下代码来部署应用程序。

./gradlew clean deployNodes(适用于Unix)和gradlew.bat clean deployNodes(适用于Windows)

成功引导将产生以下信息:

如果您只懂Java,如何开发区块链应用程序?

现在,运行以下代码以运行引导环境:

/build/nodes/runnodes(适用于Unix)和.buildnodesrunnodes.bat(适用于Windows)。

我们应该期待三个节点在不同的选项卡中启动并启动它们的shell。

如果您只懂Java,如何开发区块链应用程序?

现在让我们从PartyA向PartyB发送“Hello world”。

转到PartyA的shell并运行:

流启动启动器发送到:PartyB

我们应该期待以下信息:

如果您只懂Java,如何开发区块链应用程序?

到目前为止,我们已经通过分布式系统从一方向另一方发送了“Hello World”消息!但是是吗?让我们检查一下!

导航到PartyB的节点外壳并运行:

运行Vault Query contractStateType:com.template.states.TemplateState

如果您只懂Java,如何开发区块链应用程序?

就在那里!携带“Hello World”并从PartyA发送到PartyB的模板状态。

这个应用程序的“区块链”是什么?

你可能会想,等一下,我只看到一个在各方之间发送消息的应用程序,块在哪里?链条在哪里?通过部署和运行该应用程序,您已经成功启动了一个通常称为“区块链”的本地分布式账本系统。

携带消息字符串的状态是块。您将通过事务连接与先前生成的状态相关的后续状态,我们可以将其视为“链”。

这种状态交易链仅在参与方之间需要了解的基础上共享。(回忆步骤1中的参与者列表)。因此,在Corda世界中,数据是分布式共享的。不会有一个记录各方交易的集中数据库。

回到Hello World示例,事务(txHash:736AA884909645171B56AD965D3B…94E4F457C)带有genesis块(状态),可以用作另一个事务的输入,该链将继续运行。与区块链的概念类似:

Corda状态(块)由Corda事务(链)链接

想了解更多关于在Corda上构建出色区块链应用程序的信息吗?请务必访问corda.net,查看我们的社区页面,了解如何与其他CorDapp开发人员联系,或注册我们的最新更新时事通讯。

原文地址:https://www.corda.net/blog/how-to-develop-a-blockchain-application-if-i-only-know-java/

 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/2417.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册