使用Java与以太坊智能合约交互

在本教程中,您将学习如何使用Web3j Java库部署智能合约,以及如何与智能合约的功能进行交互。

作为先决条件,您应该熟悉帐户管理和智能合约java wrapper生成,如本系列前一篇文章中所述。为了保持连续性,我们将部署相同的DocumentRegistry智能合约。区块链研究实验室|从您的智能合同生成Java封装器。

DocumentRegistry.sol

pragma solidity ^0.5.6;


/**


*  @dev Smart Contract resposible to notarize documents on the Ethereum Blockchain


*/


contract DocumentRegistry {

    struct Document {


        address signer; // Notary


        uint date; // Date of notarization


        string hash; // Document Hash


    }

    /**


     *  @dev Storage space used to record all documents notarized with metadata


     */


    mapping(bytes32 => Document) registry;

    /**


     *  @dev Notarize a document identified by the hash of the document hash, the sender and date in the registry


     *  @dev Emit an event Notarized in case of success


     *  @param _documentHash Document hash


     */


    function notarizeDocument(string calldata _documentHash) external returns (bool) {


        bytes32 id = keccak256(abi.encodePacked(_documentHash));

        registry[id].signer = msg.sender;


        registry[id].date = now;


        registry[id].hash = _documentHash;

        emit Notarized(msg.sender, _documentHash);

        return true;


    }

    /**


     *  @dev Verify a document identified by its has was noterized in the registry previsouly.


     *  @param _documentHash Document hash


     *  @return bool if document was noterized previsouly in the registry


     */


    function isNotarized(string calldata _documentHash) external view returns (bool) {


        return registry[keccak256(abi.encodePacked(_documentHash))].signer != address(0);


    }

    /**


     *  @dev Definition of the event triggered when a document is successfully notarized in the registry


     */


    event Notarized(address indexed _signer, string _documentHash);


}

Mining与Gas的简介

Mining

与以太坊网络的任何更新EVM状态的交互必须由广播到区块链的交易触发。一些示例交互包括将以太发送到另一个帐户、部署智能合约和一些智能合约函数调用。

矿工是通过不断尝试计算复杂数学难题(一种称为工作证明共识的机制)的答案来确保以太坊网络安全的实体。

矿工的工作是(从mempool)收集一组挂起的事务,并创建一个包含这些事务的区块。一旦一个事务包含在一个挖掘的区块中,它就被认为是已执行的,并且任何相关的状态更改都将被应用。

Gas

以太币,以太坊的原生加密货币,由交易发送方支付给在一个区块内包含交易的矿工,这是激励矿工的方法之一。

gas是以太坊网络中计算工作的一个单位,执行交易时支付的以太数量取决于消耗的gas量,以及gasPrice交易属性,该属性定义了发送方每消耗一个gas单位将支付多少以太。重要的是要了解不同的交易将需要不同数量的gas,这取决于操作,每笔交易至少需要21,000个gas。

还可以通过指定gaslimit属性来定义事务发送方为了执行事务而愿意消耗的绝对最大gas量。

部署

部署永久存在的智能合约的能力是以太坊的秘密!智能合约是一段代码,其功能可由任何相关方执行。它们在网络中以字节码的形式存在,但通常使用Solidity或Vyper等语言编写,然后进行编码和部署。

到目前为止,部署DocumentRegistry智能合约的最简单方法是使用由Web3j生成的wrapper。此wrapper提供智能合约的本机java类表示,提供了两种(非弃用的)deploy方法,可用于将代码部署到以太坊网络:

public static RemoteCall<DocumentRegistry> deploy(Web3j web3j, 


Credentials credentials, ContractGasProvider contractGasProvider)

public static RemoteCall<DocumentRegistry> deploy(Web3j web3j, 


TransactionManager transactionManager, ContractGasProvider contractGasProvider)

后者允许指定TransactionManager; 控制Web3j如何连接到以太坊客户端的对象。我们很高兴在此示例中使用默认的RawTransactionManager,因此我们将使用前一种方法,该方法将钱包凭据作为参数。我们还必须创建一个ContractGasProvider,它为交易提供gas价格和gas限制; 间接指定合同在Ether中部署的成本。

DocumentRegistry部署代码

//Create credentials from private key


Credentials creds = Credentials.create(“


0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63″);

DocumentRegistry registryContract = DocumentRegistry.deploy(web3j, 


creds, new DefaultGasProvider()).send();

String contractAddress = registryContract.getContractAddress();

deploy方法返回RemoteCall对象。在RemoteCall上同时调用send()会将智能合约部署到以太坊网络,并返回与此部署代码链接的DocumentRegistry实例。每个部署的智能合约都有一个与之关联的唯一以太坊地址,并且可以通过在部署后调用合同包装器上的getContractAddress()方法来访问此地址。

在此片段中,凭据由硬编码的私钥构成(对于地址0xfe3b557e8fb62b89f4916b721be55ceb828dbd73)。这适用于测试和演示目的,但生产实现永远不应对私钥进行硬编码,因为攻击者将能够控制您的帐户。解决此问题的一种方法是将密钥设置为服务器上的环境变量,并将其加载到代码中。

在此示例中使用提供的DefaultGasProvider,它将gas价格和限制设置为硬编码值,但可以通过实现以下接口来构建自定义版本:

public interface ContractGasProvider {


    BigInteger getGasPrice(String contractFunc);

    @Deprecated


    BigInteger getGasPrice();

    BigInteger getGasLimit(String contractFunc);

    @Deprecated


    BigInteger getGasLimit();


}

为已经部署的合约创建wrapper实例

通常情况下,您要与之交互的智能合约将已部署到以太坊网络。在这种情况下,可以使用静态加载(..)方法:

public static DocumentRegistry load(String contractAddress, Web3j web3j, 


Credentials credentials, ContractGasProvider contractGasProvider)

public static DocumentRegistry load(String contractAddress, Web3j web3j, 


TransactionManager transactionManager, ContractGasProvider contractGasProvider)

例如,如果我们的DocumentRegistry部署地址为0x10c7dc2b84b6c8e6df5a749655830e70adca3a2b,我们可以获得已部署合同的java wrapper,如下所示:

//Create credentials from private key


Credentials creds = Credentials.create(“


0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63″);

DocumentRegistry registryContract = DocumentRegistry.load(web3j, 


creds, new DefaultGasProvider());

调用智能合约功能

交易与调用

根据函数的行为,可以通过两种不同的方式调用智能合约函数。

交易

要调用可能更改合同状态(添加/更新/删除值)的智能合同功能,必须将事务广播到以太坊网络。函数调用细节(如函数名和参数值)以众所周知的格式编码在事务的数据字段中,与常规的ether值事务非常相似,调用将消耗气体。

矿工必须选择将事务包含在区块中,以便进行函数调用,因此事务执行本质上是异步的。广播事务后,将返回一个唯一的哈希,该哈希随后可用于从以太坊客户端请求事务回执(一旦它包含在一个区块中)。

调用

您服务所连接的以太坊客户机的本地调用,不会向更广泛的以太坊网络广播任何内容。因此合约调用可以自由执行;他们不消耗任何gas。但是调用操作是只读的,这意味着在智能合约函数中发生的任何状态更改都不会持久化,并在执行后回滚。不涉及挖掘,因此执行是同步的。

使用合约wrapper

与部署一样,使用Web3j生成的合约wrapper调用函数是迄今为止最简单的方法。棘手的数据编码在封面下为您封装和处理。

生成一个对应于您的智能合约中的每个函数的Java方法。web3j根据函数的关键字,在wrapper生成时确定是否应该通过事务或调用自动调用函数。例如,包含view或pure关键字的函数定义将通过调用执行,否则它假定将发生一些潜在的状态更改,并使用事务方法。

调用公证文件(..)

在我们的documentregistry示例smart contract中,公证文档(..)功能将文档详细信息存储在smart contract状态中,因此应通过异步事务触发。生成的函数签名是:

public RemoteCall<TransactionReceipt> notarizeDocument(String _documentHash)

有趣的是,即使在后台,事务也会异步地广播到网络并包含在一个块中,web3j代表您处理事务接收轮询,因此该方法返回的远程调用实际上是同步的,并且会一直阻塞到事务如已开采,随后返回交易凭证。如果您的应用程序中不需要这种行为,您将不得不在不借助wrapper的情况下手动发送事务,或者在其他线程上进行远程调用。

因此,使用wrapper调用notarizeDocument函数非常简单,如下所示:

DocumentRegistry documentRegistry = deployDocumentRegistryContract();


TransactionReceipt receipt = documentRegistry.notarizeDocument(


        “QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco”).send();

String txHash = receipt.getTransactionHash();

如果事务失败,则抛出TransactionException。

调用isNotarized(..)

由于此函数被标记为view函数,因此表示它是只读的,因此可以在本地调用。生成的方法签名是:

public RemoteCall<Boolean> isNotarized(String _documentHash)

这种方法与notarizeDocument(..)方法非常相似,只有一个主要区别; 返回的RemoteCall是布尔类型而不是TransactionReceipt。这是因为没有发送事务,而是同步返回智能合约函数的返回值(在这种情况下为bool,转换为布尔值)。

手动交易发送

如果出于某种原因,不希望使用智能合约wrapper,web3j提供了许多助手类来简化广播函数调用事务的过程,例如编码事务的数据字段和签名过程。

手动交易发送代码

Function function = new Function(“notarizeDocument”,


                Arrays.asList(new Utf8String(“


QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco”)),


 Collections.emptyList());

//Encode function values in transaction data format


String txData = FunctionEncoder.encode(function);

TransactionManager txManager = new FastRawTransactionManager(web3j, 


creds);

String txHash = txManager.sendTransaction(DefaultGasProvider.GAS_PRICE, 


DefaultGasProvider.GAS_LIMIT,


                documentRegistry.getContractAddress(), txData, 


BigInteger.ZERO).getTransactionHash();

· 首先创建一个Function对象。这定义了notarizeDocument函数调用,并包含函数名称,输入参数列表(Web3j提供了所有可靠智能合约类型的java等价物),以及返回类型列表(在我们的例子中为空)。

· 接下来,FunctionEncoder用于将函数调用定义编码为事务数据字段格式。实际编码超出了本文的范围,但如果感兴趣,可以在此处找到详细信息。

· 构建了一个TransactionManager,它将用于构建和签署事务,并广播到以太坊网络。在这种情况下,我们使用FastRawTransactionManager,它支持每个块的多个事务,并将Web3j和Credentials对象作为参数。

· 一旦我们有了事务管理器和编码数据,调用notarizeDocument函数只需要调用事务管理器的sendTransaction方法。在幕后,这将构建一个事务对象,并使用凭据中定义的私钥对其进行签名,然后通过连接的客户端将事务广播到以太坊网络。虽然GasProvider的工作是在wrapper中设置气体值,但我们使用此方法手动指定它们。我们在此示例中使用了默认值,但您可以根据需要更改这些值。由于智能合约函数可以在调用期间接收以太(可支付函数),因此最后一个参数可用于指定应从发送方帐户发送到的发送方帐户的以太网(最小面额,wei)的数量。聪明的合同。在我们的情况下不应传输以太,因此该值设置为零。

您可能已经注意到上面代码中的sendTransaction方法返回事务哈希,而不是事务接收。 这是因为本指南前面已提到的事务处理的异步性质。 幸运的是,web3j还提供了一种简单的方法来轮询网络,并等待一个矿工(TransactionReceiptProcessor)将事务包含在一个块中:

TransactionReceiptProcessor receiptProcessor =


                new PollingTransactionReceiptProcessor(web3j, 


TransactionManager.DEFAULT_POLLING_FREQUENCY,


           TransactionManager.DEFAULT_POLLING_ATTEMPTS_PER_TX_HASH);

TransactionReceipt txReceipt = receiptProcessor.waitForTransactionReceipt(txHash);

总结

在本指南中,您已经学会了如何在Java中执行与Ethunm BaseCu链的一些最常见的交互,即部署智能合约,然后通过事务和调用来调用该合约上的函数。使用生成的智能合同Java wrapper是迄今为止执行这些任务最简单的方法,但如果需要更多粒度,则还有其他选项。


关键词: Java  以太坊智能合约  

该内容来自于互联网公开内容,非区块链原创内容,如若转载,请注明出处:https://htzkw.com/archives/28556

联系我们

aliyinhang@gmail.com