heraj 메뉴얼에 오신 것을 환영합니다!

Heraj는 heraj의 Java 구현체입니다. Hera는 고대 그리스 신화에서 Argo 배의 축복을 빌어준 여신입니다. Aergo project에서는 client에 대해 hera 접두사를 사용합니다. 현재는 heraj, herajs, herapy가 있습니다. 각 client들은 비슷한 api를 각기 언어에 맞는 형식으로 제공합니다.

Introduction

Heraj는 aergo 에 대한 java framework입니다.

Install

maven이나 gradle을 사용해서 설치할 수 있습니다.

Maven

<repositories>
  <repository>
    <id>jcenter</id>
    <url>https://jcenter.bintray.com</url>
  </repository>
</repositories>

...

<dependencies>
  <dependency>
    <groupId>io.aergo</groupId>
    <artifactId>heraj-transport</artifactId>
    <version>${herajVersion}</version>
  </dependency>
  <dependency>
    <groupId>io.aergo</groupId>
    <artifactId>heraj-wallet</artifactId>
    <version>${herajVersion}</version>
  </dependency>
  <dependency>
    <groupId>io.aergo</groupId>
    <artifactId>heraj-smart-contract</artifactId>
    <version>${herajVersion}</version>
  </dependency>
</dependencies>

Gradle

repositories {
  jcenter()
}

...

dependencies {
  implementation "io.aergo:heraj-transport:${herajVersion}"
  implementation "io.aergo:heraj-wallet:${herajVersion}"
  implementation "io.aergo:heraj-smart-contract:${herajVersion}"
}

Compatibility

Heraj는 jdk7 이상의 버전과 호환됩니다. 하지만 jdk8을 사용하는 것을 추천합니다.

heraj aergo jdk android
1.4.x 2.2.x 7 or higher 3.0 (API 11) or higher
1.3.x 2.0.x, 2.1.x 7 or higher 3.0 (API 11) or higher
1.2.2 1.3.x 7 or higher 3.0 (API 11) or higher

Annotations

4가지 종류의 annotation이 있습니다.

  • @ApiAudience.Public : Heraj 사용자용 api를 나타냅니다. 그래서 가능하면 변경되지 않습니다.
  • @ApiAudience.Private : Heraj 내부적으로 사용될 api를 나타냅니다. 언제든지 변경될 수 있기 때문에 사용하지 않는 것을 권장합니다.
  • @ApiStability.Stable : Major버전에서만 변경될 수 있는 안정적인 api입니다.
  • @ApiStability.Unstable : 가능하면 바꾸지 않지만 하위 호환이 되지 않을 수도 있는 api입니다.

Heraj를 사용할 때 클래스에 적혀 있는 annotation들의 의미를 잘 생각해주세요.

Model

AergoKey

AergoKey는 아르고 계정의 개인키를 관리하는 핵심 객체입니다.

New

AergoKeyGenerator를 통해 키를 생성할 수 있습니다.

AergoKey aergoKey = new AergoKeyGenerator().create();
System.out.println(aergoKey);

Export

키를 다양한 형식으로 배포 할 수 있습니다.

Wallet import format으로 배포 할 수 있습니다.

AergoKey aergoKey = new AergoKeyGenerator().create();
EncryptedPrivateKey wif = aergoKey.exportAsWif("password");
System.out.println("Wallet Import Format: " + wif);

Key format으로 배포 할 수 있습니다.

AergoKey aergoKey = new AergoKeyGenerator().create();
KeyFormat keyFormat = aergoKey.exportAsKeyFormat("password");
System.out.println("KeyFormat: " + keyFormat);

Import

다양한 형식으로된 키를 가져올 수 있습니다.

Wallet import format형식의 키를 가져올 수 있습니다.

EncryptedPrivateKey importedWif = EncryptedPrivateKey
    .of("47btMyQmmWddJmEigUp8HjUPam94Jjtf6eG6SW74r61YmbcJGyoxhwTBa8XhVBQ9wYm468DED");
AergoKey imported = AergoKey.of(importedWif, "password");
System.out.println("Imported from wif: " + imported);

Key format으로 되어 있는 키를 가져올 수 있습니다.

String keystore = loadResource(
    "/AmPo7xZJoKNfZXg4NMt9n2saXpKRSkMXwEzqEAfzbVWC71HQL3hn__keystore.txt");
KeyFormat keyFormat = KeyFormat.of(BytesValue.of(keystore.getBytes()));
AergoKey imported = AergoKey.of(keyFormat, "password");
System.out.println("Imported from keyformat: " + imported);

Sign and Verify

키로 transaction과 message에 서명을 할 수 있습니다. Heraj는 서명에 대한 검증 유틸도 제공합니다.

transaction에 대해 서명할 수 있습니다.

// prepare aergo key
AergoKey aergoKey = new AergoKeyGenerator().create();

// sign transaction
RawTransaction rawTransaction = RawTransaction.newBuilder(ChainIdHash.of(BytesValue.EMPTY))
    .from(aergoKey.getAddress())
    .to(aergoKey.getAddress())
    .amount(Aer.AERGO_ONE)
    .nonce(1L)
    .build();
Transaction transaction = aergoKey.sign(rawTransaction);
System.out.println("Signed transaction: " + transaction);

// verify transaction
Verifier verifier = new AergoSignVerifier();
boolean result = verifier.verify(transaction);
System.out.println("Verify result: " + result);

Plain message에 대해 서명할 수 있습니다. plain message에 대해 hash를 한 후 서명을 합니다.

// prepare aergo key
AergoKey aergoKey = new AergoKeyGenerator().create();

// sign message
BytesValue plainMessage = BytesValue.of("test".getBytes());
Signature signature = aergoKey.signMessage(plainMessage);
System.out.println("Signature: " + signature);

// verify signature
Verifier verifier = new AergoSignVerifier();
boolean result = verifier.verify(aergoKey.getAddress(), plainMessage, signature);
System.out.println("Verify result: " + result);

Hashed message에 대해 서명할 수 있습니다. 별도의 hashing처리를 하지 많고 바로 서명을 합니다.

// prepare aergo key
AergoKey aergoKey = new AergoKeyGenerator().create();

// sign sha-256 hashed message
BytesValue plainMessage = BytesValue.of("test".getBytes());
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
byte[] rawHashed = messageDigest.digest(plainMessage.getValue());
Hash hashedMessage = Hash.of(BytesValue.of(rawHashed));
Signature signature = aergoKey.signMessage(hashedMessage);
System.out.println("Signature: " + signature);

// verify signature
Verifier verifier = new AergoSignVerifier();
boolean result = verifier.verify(aergoKey.getAddress(), hashedMessage, signature);
System.out.println("Verify result: " + result);

Encoding

Heraj는 BytesValue에 대한 encoding/decoding을 제공합니다. 제공되는 타입은 다음과 같습니다.

  • Hex
  • Base58
  • Base58 with Checksum
  • Base64

Encode

Hex로 encoding.

BytesValue bytesValue = BytesValue.of("test".getBytes());
String encoded = bytesValue.getEncoded(Encoder.Hex);
System.out.println(encoded)

Base58로 encoding.

BytesValue bytesValue = BytesValue.of("test".getBytes());
String encoded = bytesValue.getEncoded(Encoder.Base58);
System.out.println(encoded);

Base58 with Checksum으로 encoding.

BytesValue bytesValue = BytesValue.of("test".getBytes());
String encoded = bytesValue.getEncoded(Encoder.Base58Check);
System.out.println(encoded);

Base64로 encoding.

BytesValue bytesValue = BytesValue.of("test".getBytes());
String encoded = bytesValue.getEncoded(Encoder.Base64);
System.out.println(encoded);

Decode

Hex형식의 문자열을 decoding.

String encoded = "74657374";
BytesValue bytesValue = BytesValue.of(encoded, Decoder.Hex);
System.out.println(bytesValue);

Base58형식의 문자열을 decoding.

String encoded = "3yZe7d";
BytesValue bytesValue = BytesValue.of(encoded, Decoder.Base58);
System.out.println(bytesValue);

Base58 with Checksum형식의 문자열을 decoding.

String encoded = "LUC1eAJa5jW";
BytesValue bytesValue = BytesValue.of(encoded, Decoder.Base58Check);
System.out.println(bytesValue);

Base64형식의 문자열을 decoding.

String encoded = "dGVzdA==";
BytesValue bytesValue = BytesValue.of(encoded, Decoder.Base64);
System.out.println(bytesValue);

Example

Base64로 인코딩된 서명을 읽음.

String encoded = "MEUCIQDP3ywVXX1DP42nTgM6cF95GFfpoEcl4D9ZP+MHO7SgoQIgdq2UiEiSp23lcPFzCHtDmh7pVzsow5x1s8p5Kz0aN7I=";
BytesValue rawSignature = BytesValue.of(encoded, Decoder.Base64);
Signature signature = Signature.of(rawSignature);
System.out.println(signature);

Transaction

Transaction은 blockchain의 가장 작은 단위입니다. 거의 모든 연산이 transaction을 통해서 이루어집니다.

Make a transaction

Heraj는 aergo transaction을 만들기 위한 dsl을 제공합니다.

Plain Transaction
// make a plain transaction
AergoKey aergoKey = new AergoKeyGenerator().create();
ChainIdHash chainIdHash = ChainIdHash.of("6YCMGJu3UN66ULzUuS5R7GTxXLDsSjRdjWPB94EiqMJc");
RawTransaction rawTransaction = RawTransaction.newBuilder()
    .chainIdHash(chainIdHash)
    .from(aergoKey.getAddress())
    .to(aergoKey.getAddress())
    .amount(Aer.AERGO_ONE)
    .nonce(1L)
    .fee(Fee.ZERO)
    .payload(BytesValue.of("contract_payload".getBytes()))
    .build();
Transaction transaction = aergoKey.sign(rawTransaction);
System.out.println("Plain transaction: " + transaction);
Deploy Contract Transaction
// make a contract definition
ContractDefinition definition = ContractDefinition.newBuilder()
    .encodedContract(
        "FppTEQaroys1N4P8RcAYYiEhHaQaRE9fzANUx4q2RHDXaRo6TYiTa61n25JcV19grEhpg8qdCWVdsDE2yVfuTKxxcdsTQA2B5zTfxA4GqeRqYGYgWJpj1geuLJAn1RjotdRRxSS1BFA6CAftxjcgiP6WUHacmgtNzoWViYESykhjqVLdmTfV12d44wfh9YAgQ57aRkLNCPkujbnJhdhHEtY1hrJYLCxUDBveqVcDhrrvcHtjDAUcZ5UMzbg6qR1kthGB1Lua6ymw1BmfySNtqb1b6Hp92UPMa7gi5FpAXF5XgpQtEbYDXMbtgu5XtXNhNejrtArcekmjrmPXRoTnMDGUQFcALtnNCrgSv2z5PiXP1coGEbHLTTbxkmJmJz6arEfsb6J1Dv7wnvgysDFVApcpABfwMjHLmnEGvUCLthRfHNBDGydx9jvJQvismqdpDfcEaNBCo5SRMCqGS1FtKtpXjRaHGGFGcTfo9axnsJgAGxLk")
    .amount(Aer.ZERO)
    .constructorArgs(1, 2)
    .build();

// make a contract deployment transaction
AergoKey aergoKey = new AergoKeyGenerator().create();
ChainIdHash chainIdHash = ChainIdHash.of("6YCMGJu3UN66ULzUuS5R7GTxXLDsSjRdjWPB94EiqMJc");
RawTransaction rawTransaction = RawTransaction.newDeployContractBuilder()
    .chainIdHash(chainIdHash)
    .from(aergoKey.getAddress())
    .definition(definition)
    .nonce(1L)
    .fee(Fee.ZERO)
    .build();
Transaction transaction = aergoKey.sign(rawTransaction);
System.out.println("Contract deployment transaction: " + transaction);
Invoke Contract Transaction
// make a contract invocation
ContractInterface contractInterface = dummyContractInterface();
ContractInvocation invocation = contractInterface.newInvocationBuilder()
    .function("set")
    .args("key", "123")
    .delegateFee(false)
    .build();

// make a contract invocation transaction
AergoKey aergoKey = new AergoKeyGenerator().create();
ChainIdHash chainIdHash = ChainIdHash.of("6YCMGJu3UN66ULzUuS5R7GTxXLDsSjRdjWPB94EiqMJc");
RawTransaction rawTransaction = RawTransaction.newInvokeContractBuilder()
    .chainIdHash(chainIdHash)
    .from(aergoKey.getAddress())
    .invocation(invocation)
    .nonce(1L)
    .fee(Fee.ZERO)
    .build();
Transaction transaction = aergoKey.sign(rawTransaction);
System.out.println("Invoke contract transaction: " + transaction);
Redeploy Contract
// make an new contract definition
ContractDefinition reDeployTarget = ContractDefinition.newBuilder()
    .encodedContract(
        "FppTEQaroys1N4P8RcAYYiEhHaQaRE9fzANUx4q2RHDXaRo6TYiTa61n25JcV19grEhpg8qdCWVdsDE2yVfuTKxxcdsTQA2B5zTfxA4GqeRqYGYgWJpj1geuLJAn1RjotdRRxSS1BFA6CAftxjcgiP6WUHacmgtNzoWViYESykhjqVLdmTfV12d44wfh9YAgQ57aRkLNCPkujbnJhdhHEtY1hrJYLCxUDBveqVcDhrrvcHtjDAUcZ5UMzbg6qR1kthGB1Lua6ymw1BmfySNtqb1b6Hp92UPMa7gi5FpAXF5XgpQtEbYDXMbtgu5XtXNhNejrtArcekmjrmPXRoTnMDGUQFcALtnNCrgSv2z5PiXP1coGEbHLTTbxkmJmJz6arEfsb6J1Dv7wnvgysDFVApcpABfwMjHLmnEGvUCLthRfHNBDGydx9jvJQvismqdpDfcEaNBCo5SRMCqGS1FtKtpXjRaHGGFGcTfo9axnsJgAGxLk")
    .amount(Aer.ZERO)
    .constructorArgs(1, 2)
    .build();

// make a contract redeployment transaction
AergoKey aergoKey = new AergoKeyGenerator().create();
ChainIdHash chainIdHash = ChainIdHash.of("6YCMGJu3UN66ULzUuS5R7GTxXLDsSjRdjWPB94EiqMJc");
RawTransaction rawTransaction = RawTransaction.newReDeployContractBuilder()
    .chainIdHash(chainIdHash)
    .creator(aergoKey.getAddress()) // must be creator
    .contractAddress(
        ContractAddress.of("AmJaNDXoPbBRn9XHh9onKbDKuAzj88n5Bzt7KniYA78qUEc5EwBd"))
    .definition(reDeployTarget)
    .nonce(1L)
    .fee(Fee.ZERO)
    .build();
Transaction transaction = aergoKey.sign(rawTransaction);
System.out.println("Contarct redeployment transaction: " + transaction);
Create Name
// make an name creation transaction
AergoKey aergoKey = new AergoKeyGenerator().create();
ChainIdHash chainIdHash = ChainIdHash.of("6YCMGJu3UN66ULzUuS5R7GTxXLDsSjRdjWPB94EiqMJc");
RawTransaction rawTransaction = RawTransaction.newCreateNameTxBuilder()
    .chainIdHash(chainIdHash)
    .from(aergoKey.getAddress())
    .name(Name.of("namenamename"))
    .nonce(1L)
    .build();
Transaction transaction = aergoKey.sign(rawTransaction);
System.out.println("Create name transaction: " + transaction);
Update Name
// make an name update transaction
AergoKey aergoKey = new AergoKeyGenerator().create();
ChainIdHash chainIdHash = ChainIdHash.of("6YCMGJu3UN66ULzUuS5R7GTxXLDsSjRdjWPB94EiqMJc");
RawTransaction rawTransaction = RawTransaction.newUpdateNameTxBuilder()
    .chainIdHash(chainIdHash)
    .from(aergoKey.getAddress())
    .name("namenamename")
    .nextOwner(AccountAddress.of("AmgVbUZiReUVFXdYb4UVMru4ZqyicSsFPqumRx8LfwMKLFk66SNw"))
    .nonce(1L)
    .build();
Transaction transaction = aergoKey.sign(rawTransaction);
System.out.println("Update name transaction: " + transaction);
Stake
// make a stake transaction
AergoKey aergoKey = new AergoKeyGenerator().create();
ChainIdHash chainIdHash = ChainIdHash.of("6YCMGJu3UN66ULzUuS5R7GTxXLDsSjRdjWPB94EiqMJc");
RawTransaction rawTransaction = RawTransaction.newStakeTxBuilder()
    .chainIdHash(chainIdHash)
    .from(aergoKey.getAddress())
    .amount(Aer.of("10000", Unit.AERGO))
    .nonce(1L)
    .build();
Transaction transaction = aergoKey.sign(rawTransaction);
System.out.println("Stake transaction: " + transaction);
Unstake
// make a unstake transaction
AergoKey aergoKey = new AergoKeyGenerator().create();
ChainIdHash chainIdHash = ChainIdHash.of("6YCMGJu3UN66ULzUuS5R7GTxXLDsSjRdjWPB94EiqMJc");
RawTransaction rawTransaction = RawTransaction.newUnstakeTxBuilder()
    .chainIdHash(chainIdHash)
    .from(aergoKey.getAddress())
    .amount(Aer.of("10000", Unit.AERGO))
    .nonce(1L)
    .build();
Transaction transaction = aergoKey.sign(rawTransaction);
System.out.println("Unstake transaction: " + transaction);
Vote
// make a vote transaction
AergoKey aergoKey = new AergoKeyGenerator().create();
ChainIdHash chainIdHash = ChainIdHash.of("6YCMGJu3UN66ULzUuS5R7GTxXLDsSjRdjWPB94EiqMJc");
RawTransaction rawTransaction = RawTransaction.newVoteTxBuilder()
    .chainIdHash(chainIdHash)
    .from(aergoKey.getAddress())
    .voteId("voteBP")
    .candidates(asList("123", "456"))
    .nonce(1L)
    .build();
Transaction transaction = aergoKey.sign(rawTransaction);
System.out.println("Vote transaction: " + transaction);

Parse Payload to Model

Heraj는 특정 연산을 발생시키기 위한 transaction의 payload형식을 parsing하는 유틸을 제공합니다. 현재는 ContractInvocation만 지원됩니다.

Contract Invocation
// make a contract invocation
ContractInterface contractInterface = dummyContractInterface();
ContractInvocation invocation = contractInterface.newInvocationBuilder()
    .function("set")
    .args("key", "123")
    .delegateFee(false)
    .build();

// make a contract invocation transaction
AergoKey aergoKey = new AergoKeyGenerator().create();
ChainIdHash chainIdHash = ChainIdHash.of("6YCMGJu3UN66ULzUuS5R7GTxXLDsSjRdjWPB94EiqMJc");
RawTransaction rawTransaction = RawTransaction.newInvokeContractBuilder()
    .chainIdHash(chainIdHash)
    .from(aergoKey.getAddress())
    .invocation(invocation)
    .nonce(1L)
    .fee(Fee.ZERO)
    .build();

// parse contract invocation info
PayloadConverter<ContractInvocation> invocationConverter =
    new ContractInvocationPayloadConverter();
ContractInvocation parsedInvocation = invocationConverter
    .parseToModel(rawTransaction.getPayload());
System.out.println("Parsed contract invocation: " + parsedInvocation.getAddress());

ContractDefinition

ContractDefinition은 lua로 작성한 smart contract에 대한 model입니다. lua smart contract 작성에 대해 더 자세한 정보를 원하시면, Programming Guide 를 보세요.

Make

인자 없이.

// made by aergoluac --payload {some_contract}.lua
String encodedContract = contractPayload;

// make a contract definition
ContractDefinition contractDefinition = ContractDefinition.newBuilder()
    .encodedContract(encodedContract)
    .build();
System.out.println("Contract definition: " + contractDefinition);

인자를 설정.

// made by aergoluac --payload {some_contract}.lua
String encodedContract = contractPayload;

// make a contract definition
ContractDefinition contractDefinition = ContractDefinition.newBuilder()
    .encodedContract(encodedContract)
    .constructorArgs("key", 123, "test")
    .build();
System.out.println("Contract definition: " + contractDefinition);

인자와 송금할 아르고를 설정.

// made by aergoluac --payload {some_contract}.lua
String encodedContract = contractPayload;

// make a contract definition
ContractDefinition contractDefinition = ContractDefinition.newBuilder()
    .encodedContract(encodedContract)
    .constructorArgs("key", 123, "test")
    .amount(Aer.AERGO_ONE)
    .build();
System.out.println("Contract definition: " + contractDefinition);

ContractInvocation

ContractInvocation은 smart contract 실행에 대한 model입니다. 이것은 컨트랙트 실행 또는 상태 조회가 될 수 있습니다. ContractInvocation을 만들기 위해서는 ContractInterface가 필요합니다.

Make

인자 없이.

// make a contract invocation
ContractInterface contractInterface = contractInterfaceKeep;
ContractInvocation contractInvocation = contractInterface.newInvocationBuilder()
    .function("set")
    .build();
System.out.println("Contract invocation: " + contractInvocation);

실행 인자를 설정하고.

// make a contract invocation
ContractInterface contractInterface = contractInterfaceKeep;
ContractInvocation contractInvocation = contractInterface.newInvocationBuilder()
    .function("set")
    .args("key", 333, "test2")
    .build();
System.out.println("Contract invocation: " + contractInvocation);

실행 인자와 송금할 아르고를 설정하고.

// make a contract invocation
ContractInterface contractInterface = contractInterfaceKeep;
ContractInvocation contractInvocation = contractInterface.newInvocationBuilder()
    .function("set")
    .args("key", 333, "test2")
    .amount(Aer.AERGO_ONE)
    .build();
System.out.println("Contract invocation: " + contractInvocation);

실행 인자와 fee delegation을 설정하고.

// make a contract invocation
ContractInterface contractInterface = contractInterfaceKeep;
ContractInvocation contractInvocation = contractInterface.newInvocationBuilder()
    .function("set")
    .args("key", 333, "test2")
    .delegateFee(true)
    .build();
System.out.println("Contract invocation: " + contractInvocation);

EventFilter

Aergo smart contract에는 각 블록에 발생여부가 등록되는 event가 있습니다. Heraj는 이러한 이벤트를 filter를 사용해서 조회하는 기능을 제공합니다.

Make

Block number filter 설정.

// set event filter for specific address in block 1 ~ 10
ContractAddress contractAddress = ContractAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
EventFilter eventFilter = EventFilter.newBuilder(contractAddress)
    .fromBlockNumber(1L)
    .toBlockNumber(10L)
    .build();
System.out.println("Event filter: " + eventFilter);

최근 block filter 설정.

// set event filter for specific address in recent 1000 block
ContractAddress contractAddress = ContractAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
EventFilter eventFilter = EventFilter.newBuilder(contractAddress)
    .eventName("set")
    .recentBlockCount(1000)
    .build();
System.out.println("Event filter: " + eventFilter);

Event 이름과 인자로 filter 설정.

// set event filter for specific address with name "set" and args "key" in recent 1000 block
ContractAddress contractAddress = ContractAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
EventFilter eventFilter = EventFilter.newBuilder(contractAddress)
    .eventName("set")
    .args("key")
    .recentBlockCount(1000)
    .build();
System.out.println("Event filter: " + eventFilter);

Aergo Client

Build

Aergo client를 builder를 사용해서 설정할 수 있습니다. 같은 목적을 가진 다른 설정은 overridden됩니다.

AergoClient aergoClient = new AergoClientBuilder()
    .withEndpoint("localhost:7845")
    .withNonBlockingConnect()  // ignored
    .withBlockingConnect()     // applied
    .build();

Endpoint

연결할 노드의 endpoint를 설정할 수 있습니다. 디폴트는 localhost:7845 입니다.

// connect to 'localhost:7845'
AergoClient aergoClient = new AergoClientBuilder()
    .withEndpoint("localhost:7845")
    .build();

Connect Strategy

연결 전략을 설정할 수 있습니다.

Non-Blocking 연결은 내부적으로 netty를 사용합니다.

// connect to 'localhost:7845' with non-blocking connect
AergoClient aergoClient = new AergoClientBuilder()
    .withEndpoint("localhost:7845")
    .withNonBlockingConnect()
    .build();

Blocking 연결은 내부적ㅈ으로 okhttp를 사용합니다.

// connect to 'localhost:7845' with blocking connect
AergoClient aergoClient = new AergoClientBuilder()
    .withEndpoint("localhost:7845")
    .withBlockingConnect()
    .build();

Connect Type

Plaintext로 연결합니다. 별도 동작이 없는 경우 plaintext로 설정됩니다.

// connect with plain text
AergoClient aergoClient = new AergoClientBuilder()
    .withEndpoint("localhost:7845")
    .withPlainText()
    .build();

Tls로 연결합니다. client key는 pkcs8형식이어야 합니다.

// prepare cert files
InputStream serverCert = loadResourceAsStream("/cert/server.crt");
InputStream clientCert = loadResourceAsStream("/cert/client.crt");
InputStream clientKey = loadResourceAsStream("/cert/client.pem"); // must be pkcs8 format

// connect with plain text
AergoClient aergoClient = new AergoClientBuilder()
    .withEndpoint("localhost:7845")
    .withTransportSecurity("servername", serverCert, clientCert, clientKey)
    .build();

Retry

client로 하는 연산들에 해새 재시도 횟수를 설정할 수 있습니다. 실패할 경우 설정된 기간만큼 기다렸다가 같은 인자로 재시도합니다.

// retry 3 count with a 1000ms interval
AergoClient aergoClient = new AergoClientBuilder()
    .withRetry(3, 1000L, TimeUnit.MILLISECONDS)
    .build();

Timeout

client로 하는 연산들에 대해 timeout을 설정할 수 있습니다.

// set timeout as 5000ms for each request.
AergoClient aergoClient = new AergoClientBuilder()
    .withTimeout(5000L, TimeUnit.MILLISECONDS)
    .build();

Close

Aergo client를 close합니다. Memory leak을 방지하기 위해서 필요합니다.

close method를 직접 호출함으로써 close할 수 있습니다.

// create
AergoClient aergoClient = new AergoClientBuilder()
    .withEndpoint("localhost:7845")
    .withBlockingConnect()
    .withTimeout(10000L, TimeUnit.MILLISECONDS)
    .build();

// ... do some operations

// close
aergoClient.close();

Java 7 이후 나온 try-with-resources 구문을 사용해서 close 할 수 도 있습니다.

// try-with-resources block
try (AergoClient aergoClient = new AergoClientBuilder()
    .withEndpoint("localhost:7845")
    .build()) {

  // ... do some operations
}

NonceProvider

NonceProvider는 nonce를 저장하고 새로 사용할 값을 제공해주는 interface입니다. Heraj는 thread-safe하고 memmory leak을 방지하는 기능을 가지고 있는 SimpleNonceProvider를 제공합니다. SimpleNonceProvider는 용량이 가득찼을 때 새로운 값이 들어오는 경우 가장 오래 전에 사용된 주소에 해당하는 값을 삭제합니다.

Create

SimpleNonceProvider를 생성합니다.

저장 용량을 지정해서 생성합니다.

// create nonce provider with capacity 100
NonceProvider nonceProvider = new SimpleNonceProvider(100);

저장 용량을 지정하지 않고 생성합니다.

// create nonce provider with capacity 1000
NonceProvider nonceProvider = new SimpleNonceProvider();

Bind

계정의 주소에 해당하는 nonce를 설정합니다. 용량이 가득찬 경우 가장 오래 전에 사용된 주소에 해당하는 정보가 삭제됩니다.

주소를 가지고 설정합니다.

AccountAddress accountAddress = AccountAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
NonceProvider nonceProvider = new SimpleNonceProvider();
nonceProvider.bindNonce(accountAddress, 30L);
System.out.println("Binded nonce: " + nonceProvider.getLastUsedNonce(accountAddress));

계정 상태를 가지고 설정합니다. 계정의 상태에 들어있는 주소로 설정됩니다.

AccountAddress accountAddress = AccountAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
AccountState accountState = client.getAccountOperation().getState(accountAddress);
NonceProvider nonceProvider = new SimpleNonceProvider();
System.out.println("Binded nonce: " + nonceProvider.getLastUsedNonce(accountAddress));

Use

nonce를 증가시키고 새롭게 사용할 값을 리턴합니다. 이 연산은 thread-safe합니다.

AergoKey signer = richKey;
NonceProvider nonceProvider = new SimpleNonceProvider();
long nonce = nonceProvider.incrementAndGetNonce(signer.getAddress());
System.out.println("Next nonce: " + nonce);

가장 최근에 사용된 nonce값을 리턴합니다.

AergoKey signer = richKey;
NonceProvider nonceProvider = new SimpleNonceProvider();
long nonce = nonceProvider.getLastUsedNonce(signer.getAddress());
System.out.println("Last used nonce: " + nonce);

Example

// prepare signer
AergoKey signer = richKey;

// create an nonce provider
AccountState accountState = client.getAccountOperation().getState(signer.getAddress());
NonceProvider nonceProvider = new SimpleNonceProvider();
nonceProvider.bindNonce(accountState);

// print current
long currentNonce = nonceProvider.getLastUsedNonce(signer.getAddress());
System.out.println("Current nonce: " + currentNonce);

// request using thread pool
AccountAddress accountAddress = AccountAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
ExecutorService service = Executors.newCachedThreadPool();
IntStream.range(0, 1000).forEach(i -> {
  service.submit(() -> {
    // get nonce to use
    long nonce = nonceProvider.incrementAndGetNonce(signer.getAddress());
    client.getTransactionOperation().sendTx(signer, accountAddress, Aer.ONE, nonce,
        Fee.INFINITY, BytesValue.EMPTY);
  });
});

// stop the service
service.awaitTermination(3000L, TimeUnit.MILLISECONDS);
service.shutdown();

// should print 1000
long lastUsedNonce = nonceProvider.getLastUsedNonce(signer.getAddress());
System.out.println("Nonce difference: " + (lastUsedNonce - currentNonce));

AccountOperation

계정과 관련되어 있는 연산들을 제공합니다.

Get Account State

계정의 상태 조회.

AccountAddress accountAddress = AccountAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
AccountState accountState = client.getAccountOperation().getState(accountAddress);
System.out.println("AccountState: " + accountState);

Create Name

Transaction을 서명한 계정의 소유가 되는 이름을 생성.

// prepare a signer
AergoKey signer = richKey;

// make a naming transaction
Name name = randomName();
long nonce = nonceProvider.incrementAndGetNonce(signer.getAddress());
TxHash txHash = client.getAccountOperation().createNameTx(signer, name, nonce);
System.out.println("Create name tx hash: " + txHash);

Update Name

이미 생성되어 있는 이름의 소유권을 다른 계정으로 넘김. 이 연산은 원래 소유자만 할 수 있습니다.

// prepare a signer
AergoKey signer = richKey;

// create an name
Name name = randomName();
long nonce1 = nonceProvider.incrementAndGetNonce(signer.getAddress());
client.getAccountOperation().createNameTx(signer, name, nonce1);

// sleep
Thread.sleep(2000L);

// update an name
AccountAddress nextOwner = AccountAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
long nonce2 = nonceProvider.incrementAndGetNonce(signer.getAddress());
TxHash txHash = client.getAccountOperation().updateNameTx(signer, name, nextOwner, nonce2);
System.out.println("Update name tx hash: " + txHash);

Get Name Owner

이름의 소유 계정을 조회합니다.

현재 블록 기준으로 조회합니다.

// get name owner at current block
Name name = Name.of("samplename11");
AccountAddress nameOwner = client.getAccountOperation().getNameOwner(name);
System.out.println("Nonce owner: " + nameOwner);

특정 블록 기준으로 조회합니다.

// get name owner at block 3
Name name = Name.of("samplename11");
AccountAddress nameOwner = client.getAccountOperation().getNameOwner(name, 3);
System.out.println("Nonce owner: " + nameOwner);

Stake

Aergo를 staking합니다.

// prepare a signer
AergoKey signer = richKey;

// stake 10000 aergo
Aer amount = Aer.of("10000", Unit.AERGO);
long nonce = nonceProvider.incrementAndGetNonce(signer.getAddress());
TxHash txHash = client.getAccountOperation().stakeTx(signer, amount, nonce);
System.out.println("Stake tx hash: " + txHash);

Unstake

Staking되어 있는 Aergo를 UnStaking합니다.

// prepare a signer
AergoKey signer = richKey;

// unstake 10000 aergo
Aer amount = Aer.of("10000", Unit.AERGO);
long nonce = nonceProvider.incrementAndGetNonce(signer.getAddress());
TxHash txHash = client.getAccountOperation().unstakeTx(signer, amount, nonce);
System.out.println("Unstake tx hash: " + txHash);

Get Stake Info

계정의 staking 정보를 조회합니다.

AccountAddress accountAddress = AccountAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
StakeInfo stakeInfo = client.getAccountOperation().getStakingInfo(accountAddress);
System.out.println("Stake info: " + stakeInfo);

Vote

Vote id에 해당되는 candidate에 투표합니다.

// prepare a signer
AergoKey signer = richKey;

// vote to "voteBP"
List<String> candidates = asList("16Uiu2HAkwWbv8nKx7S6S5NMvUpTLNeXMVCPr3NTnrx6rBPYYiQ4K");
long nonce = nonceProvider.incrementAndGetNonce(signer.getAddress());
TxHash txHash = client.getAccountOperation().voteTx(signer, "voteBp", candidates, nonce);
System.out.println("Vote tx hash: " + txHash);

Get Vote of Account

계정의 투표 정보를 조회합니다.

AccountAddress accountAddress = AccountAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
AccountTotalVote voteInfo = client.getAccountOperation().getVotesOf(accountAddress);
System.out.println("Vote info: " + voteInfo);

Get Vote Result

Vote id에 해당되는 투표 결과를 조회합니다.

// get vote result for vote id "voteBP" for top 23 candidates.
List<ElectedCandidate> elected = client.getAccountOperation().listElected("voteBP", 23);
System.out.println("Elected: " + elected);

BlockOperation

Block 자체에 연관되어 있는 연산들을 제공합니다.

Get Block Metadata

Block의 메타정보를 조회합니다. 해당되는 정보가 없는 경우 null을 리턴합니다.

해쉬를 사용해서 조회.

BlockHash blockHash = BlockHash.of("DN9TvryaThbJneSpzaXp5ZsS4gE3UMzKfaXC4x8L5qR1");
BlockMetadata blockMetadata = client.getBlockOperation().getBlockMetadata(blockHash);
System.out.println("Block metadata by hash: " + blockMetadata);

높이를 사용해서 조회.

long height = 27_066_653L;
BlockMetadata blockMetadata = client.getBlockOperation().getBlockMetadata(height);
System.out.println("Block metadata by height: " + blockMetadata);

List Block Metadata

Block의 메타정보들을 조회합니다. 한번에 조회할 수 있는 최고 크기는 1000입니다.

해쉬를 사용해서 조회.

// block metadatas by from hash to previous 100 block
BlockHash blockHash = BlockHash.of("DN9TvryaThbJneSpzaXp5ZsS4gE3UMzKfaXC4x8L5qR1");
List<BlockMetadata> blockMetadatas = client.getBlockOperation()
    .listBlockMetadatas(blockHash, 100);
System.out.println("Block metadatas by hash: " + blockMetadatas);

높이를 사용해서 조회.

// block metadatas by from height to previous 100 block
long height = 27_066_653L;
List<BlockMetadata> blockMetadatas = client.getBlockOperation()
    .listBlockMetadatas(height, 100);
System.out.println("Block metadatas by height: " + blockMetadatas);

Get Block

Block정보를 조회합니다. 해당되는 정보가 없을 경우 null을 리턴합니다.

해쉬를 사용해서 조회.

BlockHash blockHash = BlockHash.of("DN9TvryaThbJneSpzaXp5ZsS4gE3UMzKfaXC4x8L5qR1");
Block block = client.getBlockOperation().getBlock(blockHash);
System.out.println("Block by hash: " + block);

높이를 사용해서 조회.

long height = 27_066_653L;
Block block = client.getBlockOperation().getBlock(height);
System.out.println("Block by height: " + block);

Block Metadata Subscription

새롭게 생성된 Block 메타정보를 구독합니다.

// make a subscription
Subscription<BlockMetadata> subscription = client
    .getBlockOperation().subscribeBlockMetadata(new StreamObserver<BlockMetadata>() {
      @Override
      public void onNext(BlockMetadata value) {
        System.out.println("Next block metadata: " + value);
      }

      @Override
      public void onError(Throwable t) {

      }

      @Override
      public void onCompleted() {
      }
    });

// wait for a while
Thread.sleep(2000L);

// unsubscribe it
subscription.unsubscribe();

Block Subscription

새롭게 생성된 Block 정보를 구독합니다.

// make a subscription
Subscription<Block> subscription = client.getBlockOperation()
    .subscribeBlock(new StreamObserver<Block>() {
      @Override
      public void onNext(Block value) {
        System.out.println("Next block: " + value);
      }

      @Override
      public void onError(Throwable t) {
      }

      @Override
      public void onCompleted() {
      }
    });

// wait for a while
Thread.sleep(2000L);

// unsubscribe it
subscription.unsubscribe();

BlockchainOperation

Blockchain과 Node에 관계되어 있는 연산을 제공합니다.

Get Chain Id Hash

현재 연결되어 있는 node의 chain id hash를 조회합니다.

ChainIdHash chainIdHash = client.getBlockchainOperation().getChainIdHash();
System.out.println("Chain id hash: " + chainIdHash);

Get Blockchain Status

현재 연결되어 있는 node의 blockchain 상태를 조회합니다.

BlockchainStatus blockchainStatus = client.getBlockchainOperation().getBlockchainStatus();
System.out.println("Blockchain status: " + blockchainStatus);

Get Chain Info

현재 연결되어 있는 node의 chain 정보를 조회합니다.

ChainInfo chainInfo = client.getBlockchainOperation().getChainInfo();
System.out.println("Chain info: " + chainInfo);

Get Chain Stats

현재 연결되어 있는 node의 chain 상태를 조회합니다.

ChainStats chainStats = client.getBlockchainOperation().getChainStats();
System.out.println("Chain stats: " + chainStats);

List Peers

현재 연결되어 있는 node의 peer정보를 조회합니다.

자기 자신과 hidden peer를 제외하고 조회합니다.

List<Peer> peers = client.getBlockchainOperation().listPeers(false, false);
System.out.println("Peers: " + peers);

자기 자신과 hidden peer를 제외하지 않고 조회합니다.

List<Peer> peers = client.getBlockchainOperation().listPeers(true, true);
System.out.println("Peers: " + peers);

List Peers Metrics

현재 연결되어 있는 node의 peer 상태를 조회합니다.

List<PeerMetric> peerMetrics = client.getBlockchainOperation().listPeerMetrics();
System.out.println("PeerMetrics: " + peerMetrics);

Get Server Info

현재 연결되어 있는 node의 서버 정보를 조회합니다. Category는 아직 구현되어 있지 않습니다.

List<String> categories = emptyList();
ServerInfo serverInfo = client.getBlockchainOperation().getServerInfo(categories);
System.out.println("Server info: " + serverInfo);

Get Node Status

현재 연결되어 있는 node자체의 상태를 조회합니다.

NodeStatus nodeStatus = client.getBlockchainOperation().getNodeStatus();
System.out.println("Node status: " + nodeStatus);

TrasactionOperation

Transaction에 관계되어 있는 연산을 제공합니다.

Get Transaction

Transaction 정보를 조회합니다. 해당되는 정보가 없을 경우 null을 리턴합니다.

TxHash txHash = TxHash.of("39vLyMqsg1mTT9mF5NbADgNB2YUiRVsT6SUkDujBZme8");
Transaction transaction = client.getTransactionOperation().getTransaction(txHash);
System.out.println("Transaction: " + transaction);

Get Transaction Receipt

Transaction의 영수증을 조회합니다. 해당되는 정보가 없을 경우 null을 리턴합니다.

TxHash txHash = TxHash.of("39vLyMqsg1mTT9mF5NbADgNB2YUiRVsT6SUkDujBZme8");
TxReceipt txReceipt = client.getTransactionOperation().getTxReceipt(txHash);
System.out.println("Transaction receipt: " + txReceipt);

Commit

서명된 transaction을 commit합니다. 서명된 transaction에 대해 더 자세히 알고 싶으시면 다음의 문서를 참고하세요 Transaction.

// get chain id hash
ChainIdHash chainIdHash = client.getBlockchainOperation().getChainIdHash();

// prepare signer
AergoKey signer = richKey;

// make a transaction
long nonce = nonceProvider.incrementAndGetNonce(signer.getAddress());
RawTransaction rawTransaction = RawTransaction.newBuilder()
    .chainIdHash(chainIdHash)
    .from(signer.getAddress())
    .to(signer.getAddress())
    .amount(Aer.AERGO_ONE)
    .nonce(nonce)
    .fee(Fee.ZERO)
    .payload(BytesValue.of("contract_payload".getBytes()))
    .build();

// sign raw transaction
Transaction transaction = signer.sign(rawTransaction);

// commit signed one
TxHash txHash = client.getTransactionOperation().commit(transaction);
System.out.println("Commit tx hash: " + txHash);

Send

Aergo를 전송합니다.

계정의 주소를 사용해서 전송합니다.

// prepare signer
AergoKey signer = richKey;

// make a send transaction
AccountAddress accountAddress = AccountAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
long nonce = nonceProvider.incrementAndGetNonce(signer.getAddress());
TxHash txHash = client.getTransactionOperation()
    .sendTx(signer, accountAddress, Aer.ONE, nonce, Fee.INFINITY, BytesValue.EMPTY);
System.out.println("Send tx hash: " + txHash);

계정의 이름을 사용해서 전송합니다.

// prepare signer
AergoKey signer = richKey;

// create an name
Name name = randomName();
long nonce1 = nonceProvider.incrementAndGetNonce(signer.getAddress());
client.getAccountOperation().createNameTx(signer, name, nonce1);

// sleep
Thread.sleep(2000L);

// make a send transaction
long nonce2 = nonceProvider.incrementAndGetNonce(signer.getAddress());
TxHash txHash = client.getTransactionOperation()
    .sendTx(signer, name, Aer.ONE, nonce2, Fee.INFINITY, BytesValue.EMPTY);
System.out.println("Send tx hash: " + txHash);

ContractOperation

Provides contract related operations. For more about writing smart contract, see Aergo Smart Contract.

Deploy

Smart contract를 배포합니다. 보통 배포 절차는 다음과 같습니다.

배포 -> confirm기다림 -> contract tx receipt 받음 -> contract 주소 확인 -> contract interface 조회

Contract definition에 대해 더 자세히 알고 싶으시면 다음의 문서를 참고하세요 ContractDefinition.

AergoKey signer = richKey;

// made by aergoluac --payload {some_contract}.lua
String encodedContract = contractPayload;

// make a contract definition
ContractDefinition contractDefinition = ContractDefinition.newBuilder()
    .encodedContract(encodedContract)
    .build();

// deploy
long nonce = nonceProvider.incrementAndGetNonce(signer.getAddress());
TxHash txHash = client.getContractOperation().deployTx(signer, contractDefinition,
    nonce, Fee.ZERO);
System.out.println("Contract deployment tx hash: " + txHash);

// wait deploy contract to be confirmed
Thread.sleep(2200L);

// get contract tx receipt
ContractTxReceipt contractTxReceipt = client.getContractOperation()
    .getContractTxReceipt(txHash);
System.out.println("Contract tx receipt: " + contractTxReceipt);

// find a contract address
ContractAddress contractAddress = contractTxReceipt.getContractAddress();

// get contract interface
ContractInterface contractInterface = client.getContractOperation()
    .getContractInterface(contractAddress);
System.out.println("Contract interface: " + contractInterface);

Re-Deploy

이미 배포된 contract에 재배포합니다. 재배포 하게 되면 contract의 상태는 바꾸지 않고 logic을 바꿀 수 있습니다. 이 연산은 private mode로 돌고 있는 aergo에서만 동작합니다. Contract definition에 대해 더 자세히 알고 싶으시면 다음의 문서를 참고하세요 ContractDefinition.

// prepare signer
AergoKey signer = richKey;

// made by aergoluac --payload {some_contract}.lua
String encodedContract = contractPayload;

// make a contract definition
ContractDefinition newDefinition = ContractDefinition.newBuilder()
    .encodedContract(encodedContract)
    .build();

// redeploy
ContractAddress contractAddress = contractAddressKeep;
long nonce = nonceProvider.incrementAndGetNonce(signer.getAddress());
TxHash txHash = client.getContractOperation()
    .redeployTx(signer, contractAddress, newDefinition, nonce, Fee.ZERO);
System.out.println("Redeploy tx hash: " + txHash);

Get Contract Tx Receipt

contract tx에 대한 영수증을 조회합니다. 해당되는 정보가 없을 경우 null을 리턴합니다.

TxHash txHash = TxHash.of("EGXNDgjY2vQ6uuP3UF3dNXud54dF4FNVY181kaeQ26H9");
ContractTxReceipt contractTxReceipt = client.getContractOperation()
    .getContractTxReceipt(txHash);
System.out.println("ContractTxReceipt: " + contractTxReceipt);

Get Contract Interface

contract에 대한 interface를 조회합니다. 해당되는 정보가 없을 경우 null을 리턴합니다.

ContractAddress contractAddress = ContractAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
ContractInterface contractInterface = client.getContractOperation()
    .getContractInterface(contractAddress);
System.out.println("ContractInterface: " + contractInterface);

Execute

배포되어 있는 contract를 실행합니다. Contract invocation에 대해 더 자세히 알고 싶으시면 다음의 문서를 참고하세요 ContractInvocation.

// prepare signer
AergoKey signer = richKey;

// make a contract invocation
ContractInterface contractInterface = contractInterfaceKeep;
ContractInvocation invocation = contractInterface.newInvocationBuilder()
    .function("set")
    .args("key", 333, "test2")
    .build();

// execute
long nonce = nonceProvider.incrementAndGetNonce(signer.getAddress());
TxHash txHash = client.getContractOperation()
    .executeTx(signer, invocation, nonce, Fee.ZERO);
System.out.println("Execute tx hash: " + txHash);

Query

배포되어 있는 contract의 상태를 조회합니다. Contract invocation에 대해 더 자세히 알고 싶으시면 다음의 문서를 참고하세요 ContractInvocation.

// java bean
public class Data {

  protected int intVal;

  protected String stringVal;

  public int getIntVal() {
    return intVal;
  }

  public void setIntVal(int intVal) {
    this.intVal = intVal;
  }

  public String getStringVal() {
    return stringVal;
  }

  public void setStringVal(String stringVal) {
    this.stringVal = stringVal;
  }

  @Override
  public String toString() {
    return "Data [intVal=" + intVal + ", stringVal=" + stringVal + "]";
  }

}
// make a contract invocation
ContractInterface contractInterface = contractInterfaceKeep;
ContractInvocation query = contractInterface.newInvocationBuilder()
    .function("get")
    .args("key")
    .build();

// query contract
ContractResult queryResult = client.getContractOperation().query(query);
Data data = queryResult.bind(Data.class);
System.out.println("Raw contract result: " + queryResult); // { "intVal": 123, "stringVal": "test" }
System.out.println("Binded data: " + data);

List Event

특정 block에 발생한 event정보를 조회합니다. Event filter에 대해 더 자세히 알고 싶으시면 다음의 문서를 참고하세요 EventFilter.

ContractAddress contractAddress = contractAddressKeep;
EventFilter eventFilter = EventFilter.newBuilder(contractAddress)
    .eventName("set")
    .args("key")
    .recentBlockCount(1000)
    .build();
List<Event> events = client.getContractOperation().listEvents(eventFilter);
System.out.println("Events: " + events);

Event Subscription

새롭게 발행하는 event정보를 구독합니다. Event filter에 대해 더 자세히 알고 싶으시면 다음의 문서를 참고하세요 EventFilter.

// prepare signer
AergoKey signer = richKey;

// subscribe event
ContractAddress contractAddress = contractAddressKeep;
EventFilter eventFilter = EventFilter.newBuilder(contractAddress).build();
Subscription<Event> subscription = client.getContractOperation()
    .subscribeEvent(eventFilter, new StreamObserver<Event>() {
      @Override
      public void onNext(Event value) {
        System.out.println("Next event: " + value);
      }

      @Override
      public void onError(Throwable t) {
      }

      @Override
      public void onCompleted() {
      }
    });

// execute
ContractInterface contractInterface = contractInterfaceKeep;
ContractInvocation run = contractInterface.newInvocationBuilder()
    .function("set")
    .args("key", 333, "test2")
    .build();
long nonce = nonceProvider.incrementAndGetNonce(signer.getAddress());
client.getContractOperation().executeTx(signer, run, nonce, Fee.ZERO);
Thread.sleep(2200L);

// unsubscribe event
subscription.unsubscribe();

Wallet

KeyStore

KeyStore는 AergoKey를 관리하는 기능을 제공합니다. KeyStore를 사용해서 저장, 로드, 삭제를 할 수 있습니다. 제공되는 타입은 다음과 같습니다.

  • InMemoryKeystore
  • JavaKeyStore
  • AergoKeyStore

Create

Heraj는 KeyStore를 생성하기 위한 factory method를 제공합니다.

InMemoryKeystore

InMemoryKeystore는 AergoKey를 메모리에 저장합니다.

// make a keystore
KeyStore keyStore = KeyStores.newInMemoryKeyStore();
System.out.println("InMemoryKeystore: " + keyStore);
System.out.println("Stored keys: " + keyStore.listIdentities());
JavaKeyStore

JavaKeyStore는 java.security.keystore를 사용합니다. 생성 시 해당 객체를 factory method에 전달해야 합니다

// create a java keystore
java.security.KeyStore delegate = java.security.KeyStore.getInstance("PKCS12");
delegate.load(new FileInputStream(someDir + "/keystore.p12"), "password".toCharArray());

// make a keystore
KeyStore keyStore = KeyStores.newJavaKeyStore(delegate);
System.out.println("JavaKeyStore: " + keyStore);
System.out.println("Stored keys: " + keyStore.listIdentities());
AergoKeyStore

AergoKeyStore는 AergoKey를 aergo만의 방법으로 관리합니다. 만들어진 keystore는 aergocli로 생성한 keystore와 호환됩니다. aergocli로 keystore를 만드는 방법은 다음에 나와있습니다 Creating Accounts.

// make a keystore
String root = someDir + "/aergo_keystore";
KeyStore keyStore = KeyStores.newAergoKeyStore(root);
System.out.println("AergoKeyStore: " + keyStore);
System.out.println("Stored keys: " + keyStore.listIdentities());

Save and Load

AergoKey에 대해 저장 및 로드를 할 수 있습니다.

alias를 사용해서 할 수 있습니다.

// create a keystore
KeyStore keyStore = KeyStores.newInMemoryKeyStore();

// create an new key
AergoKey key = new AergoKeyGenerator().create();

// save
Authentication authentication = Authentication.of(KeyAlias.of("myalias"), "password");
keyStore.save(authentication, key);

주소 자체를 사용해서 할 수 있습니다.

// create an keystore
KeyStore keyStore = KeyStores.newInMemoryKeyStore();

// create an new key
AergoKey key = new AergoKeyGenerator().create();

// save
Authentication authentication = Authentication.of(key.getAddress(), "password");
keyStore.save(authentication, key);

Remove

keystore에 저장되어 있는 AergoKey를 삭제할 수 있습니다.

alias를 사용해서 할 수 있습니다.

// create a keystore
KeyStore keyStore = KeyStores.newInMemoryKeyStore();

// create an new key
AergoKey key = new AergoKeyGenerator().create();

// save
Authentication authentication = Authentication.of(KeyAlias.of("myalias"), "password");
keyStore.save(authentication, key);

// remove
System.out.println("Before remove: " + keyStore.listIdentities());
keyStore.remove(authentication);
System.out.println("After remove: " + keyStore.listIdentities());

주소 자체를 사용해서 할 수 있습니다.

// create a keystore
KeyStore keyStore = KeyStores.newInMemoryKeyStore();

// create an new key
AergoKey key = new AergoKeyGenerator().create();

// save
Authentication authentication = Authentication.of(key.getAddress(), "password");
keyStore.save(authentication, key);

// remove
System.out.println("Before remove: " + keyStore.listIdentities());
keyStore.remove(authentication);
System.out.println("After remove: " + keyStore.listIdentities());

Export

keystore에 저장되어 있는 AergoKey를 wallet import format으로 export할 수 있습니다.

alias를 사용해서 할 수 있습니다.

// create a keystore
KeyStore keyStore = KeyStores.newInMemoryKeyStore();

// create an new key
AergoKey key = new AergoKeyGenerator().create();

// save
Authentication authentication = Authentication.of(KeyAlias.of("myalias"), "password");
keyStore.save(authentication, key);

// export
EncryptedPrivateKey exported = keyStore.export(authentication, "newpassword");
System.out.println("Exported: " + exported);

주소 자체를 사용해서 할 수 있습니다.

// create a keystore
KeyStore keyStore = KeyStores.newInMemoryKeyStore();

// create an new key
AergoKey key = new AergoKeyGenerator().create();

// save
Authentication authentication = Authentication.of(key.getAddress(), "password");
keyStore.save(authentication, key);

// export
EncryptedPrivateKey exported = keyStore.export(authentication, "newpassword");
System.out.println("Exported: " + exported);

List Stored Identities

keystore에 저장되어 있는 키에 대한 이름들을 확인할 수 있습니다.

// create a keystore
KeyStore keyStore = KeyStores.newInMemoryKeyStore();

// create an new key
AergoKey key = new AergoKeyGenerator().create();

// save
Authentication authentication = Authentication.of(KeyAlias.of("myalias"), "password");
keyStore.save(authentication, key);

// list
List<Identity> identities = keyStore.listIdentities();
System.out.println("Stored identities: " + identities);

Store

keystore를 특정 파일로 저장힙니다. 이 기능은 JavaKeyStore에서만 동작합니다. 다른 타입에 대해서는 아무 것도 하지 않습니다.

// prepare a java keystore
java.security.KeyStore delegate = java.security.KeyStore.getInstance("PKCS12");
delegate.load(null, null);

// create a java keystore
KeyStore keyStore = KeyStores.newJavaKeyStore(delegate);

// store
String path = someDir + "/" + randomUUID().toString();
keyStore.store(path, "password".toCharArray());

Wallet Api

WalletApi는 KeyStore 와 상호작용하는 기능을 제공합니다. WalletApi를 사용해서 keystore에 저장되어 있는 key에 대한 unlock, lock을 할 수 있습니다. WalletApi는 또한 aergo client를 사용하는 high-level api를 제공합니다. TransactionApi의 경우 nonce 실패일 때 자동으로 최신 nonce를 가져와서 재시도를 합니다.

WalletApi는 한번에 하나의 unlock된 계정만 가질 수 있습니다. 만약 한개의 계정을 unlock한 후 다른 계정을 unlock하면 이전의 계정은 자동으로 lock됩니다.

Create

WalletApi를 만들기 위해서는 KeyStore가 필요합니다.

nonce 실패 시 재시도 횟수와 간격을 명시적으로 설정해서 생성.

// create a keystore
KeyStore keyStore = KeyStores.newInMemoryKeyStore();

// create a wallet api
WalletApi walletApi = new WalletApiFactory().create(keyStore);
System.out.println("WalletApi: " + walletApi);

nonce 실패 시 재시도 횟수와 간격을 기본 설정으로 해서 생성.

// create a keystore
KeyStore keyStore = KeyStores.newInMemoryKeyStore();

// create a wallet api with retry count 5 and interval 1s
TryCountAndInterval tryCountAndInterval = TryCountAndInterval
    .of(5, Time.of(1L, TimeUnit.SECONDS));
WalletApi walletApi = new WalletApiFactory().create(keyStore, tryCountAndInterval);
System.out.println("WalletApi: " + walletApi);

Unlock and Lock

Unlock을 함으로써 해당 계정으로 transaction을 발생시킬 수 있습니다.

// create a keystore
KeyStore keyStore = KeyStores.newInMemoryKeyStore();

// store new key to keystore
AergoKey aergoKey = new AergoKeyGenerator().create();
Authentication authentication = Authentication.of(aergoKey.getAddress(), "password");
keyStore.save(authentication, aergoKey);

// create a wallet api
WalletApi walletApi = new WalletApiFactory().create(keyStore);

// unlock account
boolean unlockResult = walletApi.unlock(authentication);
System.out.println("Unlock result: " + unlockResult);
System.out.println("Currently locked one: " + walletApi.getPrincipal());

// do something..
Signature signature = walletApi.signMessage(BytesValue.of("test".getBytes()));
System.out.println("Signature: " + signature);

// lock account
boolean lockResult = walletApi.lock();
System.out.println("Lock result: " + lockResult);

High Level Api

WalletApi는 aergo node와 상호작용 하기 위한 high-level api를 제공합니다. TransactionApi를 사용하기 위해서는 계정을 unlock해야합니다. QueryApi는 별도의 unlock된 계정이 필요하지 않습니다.

// prepare client
AergoClient aergoClient = new AergoClientBuilder().build();

// create a keystore
KeyStore keyStore = KeyStores.newInMemoryKeyStore();

// create a wallet api
WalletApi walletApi = new WalletApiFactory().create(keyStore);
System.out.println("WalletApi: " + walletApi);

// transaction api
TransactionApi transactionApi = walletApi.with(aergoClient).transaction();
System.out.println("Transaction Api: " + transactionApi);

// query api
QueryApi queryApi = walletApi.with(aergoClient).query();
System.out.println("Query Api: " + queryApi);

Transaction Api

TransactionApi는 transaction생성을 위한 high-level api를 제공합니다. transaction생성을 위해서는 unlock된 계정이 필요합니다.

Create Name

Unlock된 계정으로 이름 생성을 할 수 있습니다.

// unlock specific account with authentication
walletApi.unlock(authentication);

Name name = randomName();
TxHash txHash = walletApi.with(client).transaction().createName(name);
System.out.println("Create name tx hash: " + txHash);

// lock an account
walletApi.lock();

Update Name

Unlock된 계정으로 이름 소유권 전송을 할 수 있습니다.

// unlock specific account with authentication
walletApi.unlock(authentication);

// create an name
Name name = randomName();
walletApi.with(client).transaction().createName(name);

// sleep
Thread.sleep(2000L);

// update an name
AccountAddress nextOwner = AccountAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
TxHash txHash = walletApi.with(client).transaction().updateName(name, nextOwner);
System.out.println("Update name tx hash: " + txHash);

// lock an account
walletApi.lock();

Stake

Unlock된 계정으로 staking을 할 수 있습니다.

// unlock specific account with authentication
walletApi.unlock(authentication);

// stake
TxHash txHash = walletApi.with(client).transaction().stake(Aer.of("10000", Unit.AERGO));
System.out.println("Stake tx hash: " + txHash);

// lock an account
walletApi.lock();

Unstake

Unlock된 계정으로 unstaking을 할 수 있습니다.

// unlock specific account with authentication
walletApi.unlock(authentication);

// unstake
TxHash txHash = walletApi.with(client).transaction().unstake(Aer.of("10000", Unit.AERGO));
System.out.println("Unstake tx hash: " + txHash);

// lock an account
walletApi.lock();

Vote

Unlock된 계정으로 투표를 할 수 있습니다.

// unlock specific account with authentication
walletApi.unlock(authentication);

// vote to "voteBP"
List<String> candidates = asList("16Uiu2HAkwWbv8nKx7S6S5NMvUpTLNeXMVCPr3NTnrx6rBPYYiQ4K");
TxHash txHash = walletApi.with(client).transaction().vote("voteBp", candidates);
System.out.println("Vote tx hash: " + txHash);

// lock an account
walletApi.lock();

Send

Unlock된 계정으로 aergo 전송을 할 수 있습니다.

Payload없이 주소를 통해 전송.

// unlock specific account with authentication
walletApi.unlock(authentication);

// send
AccountAddress accountAddress = AccountAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
TxHash txHash = walletApi.with(client).transaction()
    .send(accountAddress, Aer.AERGO_ONE, Fee.INFINITY);
System.out.println("Send tx hash: " + txHash);

// lock an account
walletApi.lock();

Payload와 함께 주소를 통해 전송.

// unlock specific account with authentication
walletApi.unlock(authentication);

// send
AccountAddress accountAddress = AccountAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
BytesValue payload = BytesValue.of("test".getBytes());
TxHash txHash = walletApi.with(client).transaction()
    .send(accountAddress, Aer.AERGO_ONE, Fee.INFINITY, payload);
System.out.println("Send tx hash: " + txHash);

// lock an account
walletApi.lock();

Payload없이 이름을 통해 전송.

// unlock specific account with authentication
walletApi.unlock(authentication);

// create an name
Name name = randomName();
walletApi.with(client).transaction().createName(name);

// sleep
Thread.sleep(2000L);

// send
TxHash txHash = walletApi.with(client).transaction()
    .send(name, Aer.AERGO_ONE, Fee.INFINITY);
System.out.println("Send tx hash: " + txHash);

// lock an account
walletApi.lock();

Payload와 함께 이름을 통해 전송.

// unlock specific account with authentication
walletApi.unlock(authentication);

// create an name
Name name = randomName();
walletApi.with(client).transaction().createName(name);

// sleep
Thread.sleep(2000L);

// send
BytesValue payload = BytesValue.of("test".getBytes());
TxHash txHash = walletApi.with(client).transaction()
    .send(name, Aer.AERGO_ONE, Fee.INFINITY, payload);
System.out.println("Send tx hash: " + txHash);

// lock an account
walletApi.lock();

Commit

Unlock된 계정으로 서명을 한 후 commit을 할 수 있습니다.

// unlock specific account with authentication
walletApi.unlock(authentication);

// create a raw transaction
AccountAddress current = walletApi.getPrincipal();
ChainIdHash chainIdHash = walletApi.with(client).query().getChainIdHash();
AccountState currentState = walletApi.with(client).query().getAccountState(current);
RawTransaction rawTransaction = RawTransaction.newBuilder()
    .chainIdHash(chainIdHash)
    .from(current)
    .to(current)
    .amount(Aer.AERGO_ONE)
    .nonce(currentState.getNonce() + 1L)
    .build();

// commit
TxHash txHash = walletApi.with(client).transaction().commit(rawTransaction);
System.out.println("Commit tx hash: " + txHash);

// lock an account
walletApi.lock();

서명된 transaction으로 commit을 할 수 있습니다.

// unlock specific account with authentication
walletApi.unlock(authentication);

// create a signed transaction
AccountAddress current = walletApi.getPrincipal();
ChainIdHash chainIdHash = walletApi.with(client).query().getChainIdHash();
AccountState currentState = walletApi.with(client).query().getAccountState(current);
RawTransaction rawTransaction = RawTransaction.newBuilder()
    .chainIdHash(chainIdHash)
    .from(current)
    .to(current)
    .amount(Aer.AERGO_ONE)
    .nonce(currentState.getNonce() + 1L)
    .build();
Transaction signed = walletApi.sign(rawTransaction);

// commit
TxHash txHash = walletApi.with(client).transaction().commit(signed);
System.out.println("Commit tx hash: " + txHash);

// lock an account
walletApi.lock();

Deploy

Unlock된 계정으로 contract 배포를 할 수 있습니다.contract definition에 대해 더 자세히 알고 싶으시면 다음의 문서를 참고하세요 ContractDefinition.

// unlock specific account with authentication
walletApi.unlock(authentication);

// make a contract definition
String encodedContract = contractPayload;
ContractDefinition contractDefinition = ContractDefinition.newBuilder()
    .encodedContract(encodedContract)
    .build();

// deploy contract
TxHash txHash = walletApi.with(client).transaction().deploy(contractDefinition, Fee.INFINITY);
System.out.println("Deploy tx hash: " + txHash);

// sleep
Thread.sleep(2000L);

// get ContractTxReceipt
ContractTxReceipt contractTxReceipt = walletApi.with(client).query()
    .getContractTxReceipt(txHash);
System.out.println("Deployed contract tx receipt: " + contractTxReceipt);

// get contract interface
ContractAddress contractAddress = contractTxReceipt.getContractAddress();
ContractInterface contractInterface = walletApi.with(client).query()
    .getContractInterface(contractAddress);
System.out.println("Deployed contract interface: " + contractInterface);

// lock an account
walletApi.lock();

Re-Deploy

Unlock된 계정으로 contract 재배포를 할 수 있습니다.contract definition에 대해 더 자세히 알고 싶으시면 다음의 문서를 참고하세요 ContractDefinition.

// unlock specific account with authentication
walletApi.unlock(authentication);

// made by aergoluac --compiledContract {some_contract}.lua
String encodedContract = contractPayload;

// make a contract definition
ContractDefinition newDefinition = ContractDefinition.newBuilder()
    .encodedContract(encodedContract)
    .build();

// redeploy
ContractAddress contractAddress = contractAddressKeep;
TxHash txHash = walletApi.with(client).transaction()
    .redeploy(contractAddress, newDefinition, Fee.INFINITY);
System.out.println("Redeploy tx hash: " + txHash);

// lock an account
walletApi.lock();

Execute

Unlock된 계정으로 contract 실행을 할 수 있습니다.contract invocation에 대해 더 자세히 알고 싶으시면 다음의 문서를 참고하세요 ContractInvocation.

// unlock specific account with authentication
walletApi.unlock(authentication);

// make a contract invocation
ContractInterface contractInterface = contractInterfaceKeep;
ContractInvocation contractInvocation = contractInterface.newInvocationBuilder()
    .function("set")
    .args("key", 333, "test2")
    .build();

// execute
TxHash txHash = walletApi.with(client).transaction()
    .execute(contractInvocation, Fee.INFINITY);
System.out.println("Execute tx hash: " + txHash);

// lock an account
walletApi.lock();

Query Api

QueryApi는 조회를 위한 high-level api를 제공합니다. 별도의 unlock된 계정이 필요하지 않습니다.

Get Account State

계정의 상태 조회.

// get account state
AccountAddress accountAddress = AccountAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
AccountState state = walletApi.with(client).query()
    .getAccountState(accountAddress);
System.out.println("Account state: " + state);

Get Name Owner

이름의 소유 계정을 조회합니다.

현재 블록 기준으로 조회합니다.

// get name owner
Name name = Name.of("namenamename");
AccountAddress nameOwner = walletApi.with(client).query().getNameOwner(name);
System.out.println("Name owner: " + nameOwner);

특정 블록 기준으로 조회합니다.

// get name owner at block 10
Name name = Name.of("namenamename");
AccountAddress nameOwner = walletApi.with(client).query().getNameOwner(name, 10);
System.out.println("Name owner: " + nameOwner);

Get Stake Info

계정의 staking 정보를 조회합니다.

// get stake info
AccountAddress accountAddress = AccountAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
StakeInfo stakeInfo = walletApi.with(client).query().getStakeInfo(accountAddress);
System.out.println("Stake info: " + stakeInfo);

List Elected Bps

Block producers로 선정된 후보 노드들을 조회합니다.

// list elected bps
List<ElectedCandidate> candidates = walletApi.with(client).query().listElectedBps(23);
System.out.println("Elected bps: " + candidates);

List Elected

Vote id에 해당되는 투표 결과를 조회합니다.

// list elected for "voteBP"
List<ElectedCandidate> candidates = walletApi.with(client).query()
    .listElected("voteBP", 23);
System.out.println("Elected candidates: " + candidates);

Get Vote Info

계정의 투표 정보를 조회합니다.

// get vote info
AccountAddress accountAddress = AccountAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
AccountTotalVote accountTotalVote = walletApi.with(client).query().getVotesOf(accountAddress);
System.out.println("Account total vote: " + accountTotalVote);

Get Best Block Hash

현재 node의 최고 블록의 해쉬를 조회합니다.

// get best block hash
BlockHash blockHash = walletApi.with(client).query().getBestBlockHash();
System.out.println("Best block hash: " + blockHash);

Get Best Block Height

현재 node의 최고 블록의 높이를 조회합니다.

// get best block hash
long blockHeight = walletApi.with(client).query().getBestBlockHeight();
System.out.println("Best block height: " + blockHeight);

Get Chain Id Hash

현재 연결되어 있는 node의 chain id hash를 조회합니다.

// get chain id hash
ChainIdHash chainIdHash = walletApi.with(client).query().getChainIdHash();
System.out.println("Chain id hash: " + chainIdHash);

Get Blockchain Status

현재 연결되어 있는 node의 blockchain 상태를 조회합니다.

// get blockchain status
BlockchainStatus blockchainStatus = walletApi.with(client).query().getBlockchainStatus();
System.out.println("Blockchain status: " + blockchainStatus);

Get Chain Info

현재 연결되어 있는 node의 chain 정보를 조회합니다.

// get chain info
ChainInfo chainInfo = walletApi.with(client).query().getChainInfo();
System.out.println("ChainInfo: " + chainInfo);

Get Chain Stats

현재 연결되어 있는 node의 chain 상태를 조회합니다.

// get chain stats
ChainStats chainStats = walletApi.with(client).query().getChainStats();
System.out.println("ChainStats: " + chainStats);

List Peers

현재 연결되어 있는 node의 peer정보를 조회합니다.

자기 자신과 hidden peer를 제외하고 조회합니다.

// list peers
List<Peer> peers = walletApi.with(client).query().listPeers();
System.out.println("Peers: " + peers);

자기 자신과 hidden peer를 제외하지 않고 조회합니다.

// list peers
List<Peer> peers = walletApi.with(client).query().listPeers(true, true);
System.out.println("Peers: " + peers);

List Peer Metrics

현재 연결되어 있는 node의 peer 상태를 조회합니다.

// list peer metrics
List<PeerMetric> peerMetrics = walletApi.with(client).query().listPeerMetrics();
System.out.println("Peer metrics: " + peerMetrics);

Get Server Info

현재 연결되어 있는 node의 서버 정보를 조회합니다. Category는 아직 구현되어 있지 않습니다.

// get server info
List<String> categories = emptyList();
ServerInfo serverInfo = walletApi.with(client).query().getServerInfo(categories);
System.out.println("Server info: " + serverInfo);

Get Node Status

현재 연결되어 있는 node자체의 상태를 조회합니다.

// get node status
NodeStatus nodeStatus = walletApi.with(client).query().getNodeStatus();
System.out.println("Node status: " + nodeStatus);

Get Block Metadata

Block의 메타정보를 조회합니다. 해당되는 정보가 없는 경우 null을 리턴합니다.

해쉬를 사용해서 조회.

// get block metadata
BlockHash blockHash = BlockHash.of("DN9TvryaThbJneSpzaXp5ZsS4gE3UMzKfaXC4x8L5qR1");
BlockMetadata blockMetadata = walletApi.with(client).query().getBlockMetadata(blockHash);
System.out.println("Block metadata by hash: " + blockMetadata);

높이를 사용해서 조회.

// get block metadata
long height = 27_066_653L;
BlockMetadata blockMetadata = walletApi.with(client).query().getBlockMetadata(height);
System.out.println("Block metadata by height: " + blockMetadata);

List Block Metadata

Block의 메타정보들을 조회합니다. 한번에 조회할 수 있는 최고 크기는 1000입니다.

해쉬를 사용해서 조회.

// block metadatas by from hash to previous 100 block
BlockHash blockHash = BlockHash.of("DN9TvryaThbJneSpzaXp5ZsS4gE3UMzKfaXC4x8L5qR1");
List<BlockMetadata> blockMetadatas = walletApi.with(client).query()
    .listBlockMetadatas(blockHash, 100);
System.out.println("Block metadatas by hash: " + blockMetadatas);

높이를 사용해서 조회.

// block metadatas by from height to previous 100 block
long height = 27_066_653L;
List<BlockMetadata> blockMetadatas = walletApi.with(client).query()
    .listBlockMetadatas(height, 100);
System.out.println("Block metadatas by height: " + blockMetadatas);

Get Block

Block정보를 조회합니다. 해당되는 정보가 없을 경우 null을 리턴합니다.

해쉬를 사용해서 조회.

// get block by hash
BlockHash blockHash = BlockHash.of("DN9TvryaThbJneSpzaXp5ZsS4gE3UMzKfaXC4x8L5qR1");
Block block = walletApi.with(client).query().getBlock(blockHash);
System.out.println("Block by hash: " + block);

높이를 사용해서 조회.

// get block by height
long height = 27_066_653L;
Block block = walletApi.with(client).query().getBlock(height);
System.out.println("Block by hash: " + block);

Block Metadata Subscription

새롭게 생성된 Block 메타정보를 구독합니다.

// make a subscription
Subscription<BlockMetadata> metadataSubscription = walletApi.with(client).query()
    .subscribeBlockMetadata(new StreamObserver<BlockMetadata>() {
      @Override
      public void onNext(BlockMetadata value) {
        System.out.println("Next block metadata: " + value);
      }

      @Override
      public void onError(Throwable t) {

      }

      @Override
      public void onCompleted() {
      }
    });

// wait for a while
Thread.sleep(2000L);

// unsubscribe it
metadataSubscription.unsubscribe();

Block Subscription

새롭게 생성된 Block 정보를 구독합니다.

// make a subscription
Subscription<Block> subscription = walletApi.with(client).query()
    .subscribeBlock(new StreamObserver<Block>() {
      @Override
      public void onNext(Block value) {
        System.out.println("Next block: " + value);
      }

      @Override
      public void onError(Throwable t) {
      }

      @Override
      public void onCompleted() {
      }
    });

// wait for a while
Thread.sleep(2000L);

// unsubscribe it
subscription.unsubscribe();

Get Transaction

Transaction 정보를 조회합니다. 해당되는 정보가 없을 경우 null을 리턴합니다.

// get transaction
TxHash txHash = TxHash.of("39vLyMqsg1mTT9mF5NbADgNB2YUiRVsT6SUkDujBZme8");
Transaction transaction = walletApi.with(client).query().getTransaction(txHash);
System.out.println("Transaction: " + transaction);

Get Transaction Receipt

Transaction의 영수증을 조회합니다. 해당되는 정보가 없을 경우 null을 리턴합니다.

// get tx receipt
TxHash txHash = TxHash.of("39vLyMqsg1mTT9mF5NbADgNB2YUiRVsT6SUkDujBZme8");
TxReceipt txReceipt = walletApi.with(client).query().getTxReceipt(txHash);
System.out.println("Transaction receipt: " + txReceipt);

Get Contract Tx Receipt

contract tx에 대한 영수증을 조회합니다. 해당되는 정보가 없을 경우 null을 리턴합니다.

// get contract tx receipt
TxHash txHash = TxHash.of("EGXNDgjY2vQ6uuP3UF3dNXud54dF4FNVY181kaeQ26H9");
ContractTxReceipt contractTxReceipt = walletApi.with(client).query()
    .getContractTxReceipt(txHash);
System.out.println("Contract tx receipt: " + contractTxReceipt);

Get Contract Interface

contract에 대한 interface를 조회합니다. 해당되는 정보가 없을 경우 null을 리턴합니다.

// get contract interface
ContractAddress contractAddress = ContractAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
ContractInterface contractInterface = walletApi.with(client).query()
    .getContractInterface(contractAddress);
System.out.println("ContractInterface: " + contractInterface);

Query Contract

배포되어 있는 contract의 상태를 조회합니다. Contract invocation에 대해 더 자세히 알고 싶으시면 다음의 문서를 참고하세요 ContractInvocation.

// make a contract invocation
ContractInterface contractInterface = contractInterfaceKeep;
ContractInvocation query = contractInterface.newInvocationBuilder()
    .function("get")
    .args("key")
    .build();

// query contract
ContractResult queryResult = client.getContractOperation().query(query);
Data data = queryResult.bind(Data.class);
System.out.println("Raw contract result: " + queryResult);
System.out.println("Binded data: " + data);

List Event

특정 block에 발생한 event정보를 조회합니다. Event filter에 대해 더 자세히 알고 싶으시면 다음의 문서를 참고하세요 EventFilter.

// list events with a filter
ContractAddress contractAddress = contractAddressKeep;
EventFilter eventFilter = EventFilter.newBuilder(contractAddress)
    .eventName("set")
    .args("key")
    .recentBlockCount(1000)
    .build();
List<Event> events = client.getContractOperation().listEvents(eventFilter);
System.out.println("Events: " + events);

Event Subscription

새롭게 발행하는 event정보를 구독합니다. Event filter에 대해 더 자세히 알고 싶으시면 다음의 문서를 참고하세요 EventFilter.

// subscribe event
ContractAddress contractAddress = ContractAddress
    .of("AmNrsAqkXhQfE6sGxTutQkf9ekaYowaJFLekEm8qvDr1RB1AnsiM");
EventFilter eventFilter = EventFilter.newBuilder(contractAddress)
    .recentBlockCount(1000)
    .build();
Subscription<Event> subscription = client.getContractOperation()
    .subscribeEvent(eventFilter, new StreamObserver<Event>() {
      @Override
      public void onNext(Event value) {
        System.out.println("Next event: " + value);
      }

      @Override
      public void onError(Throwable t) {
      }

      @Override
      public void onCompleted() {
      }
    });

Thread.sleep(2200L);

// unsubscribe event
subscription.unsubscribe();

Contract

Contract Api

ContractApi는 java interface로 contract call을 할 수 있는 기능을 제공합니다. 실행 할 때 ContractApi는 자동으로 nonce를 채워줍니다. nonce에러로 실패할 경우 올바른 nonce를 조회한 후 그 nonce로 재시도합니다.

Prepare

ContractApi를 사용하기 위해서, 우선 smart contract를 배포해야 합니다. 그 후 smart contract의 함수에 해당하는 interface를 작성하면 됩니다.

smart contract를 작성합니다. 작성 방법에 대해 더 자세히 알고 싶으시면 다음의 문서를 참고하시면 됩니다 Programming Guide.

function constructor(key, arg1, arg2)
  if key ~= nil then
    system.setItem(key, {intVal=arg1, stringVal=arg2})
  end
end

function set(key, arg1, arg2)
  contract.event("set", key, arg1, arg2)
  system.setItem(key, {intVal=arg1, stringVal=arg2})
end

function get(key)
  return system.getItem(key)
end

function check_delegation()
  return true
end

abi.register_view(get)
abi.register(set)
abi.fee_delegation(set)
abi.payable(set)

smart contract를 배포합니다.

// make a contract definition
String encodedContract = contractPayload;
ContractDefinition contractDefinition = ContractDefinition.newBuilder()
    .encodedContract(encodedContract)
    .build();

// deploy contract
walletApi.unlock(authentication);
TxHash txHash = walletApi.with(client).transaction()
    .deploy(contractDefinition, Fee.INFINITY);
walletApi.lock();

// sleep
Thread.sleep(2000L);

// get ContractTxReceipt
ContractTxReceipt contractTxReceipt = walletApi.with(client).query()
    .getContractTxReceipt(txHash);

// get contract address
ContractAddress contractAddress = contractTxReceipt.getContractAddress();
System.out.println("Deployed contract address: " + contractPayload);

interface를 작성합니다. method 이름은 smart contract의 함수와 일치해야 합니다.

// interface for smart contract
interface CustomInterface1 {

  /*
    Matches with

      function set(key, arg1, arg2)
        ...
      end

      ...

      abi.register(set)

    And it also uses provided fee when making transaction.
   */
  TxHash set(String key, int arg1, String args2, Fee fee);

  /*
    Matches with

      function set(key, arg1, arg2)
        ...
      end

      ...

      abi.register(set)

    And it also uses Fee.INFINITY when making transaction.
   */
  TxHash set(String key, int arg1, String args2);

  /*
    Matches with

      function get(key)
        ...
        -- returns lua table which can be binded with Data class
        return someVal
      end

      ...

      abi.register_view(get)
   */
  Data get(String key);

}

// java bean
class Data {

  protected int intVal;

  protected String stringVal;

  public int getIntVal() {
    return intVal;
  }

  public void setIntVal(int intVal) {
    this.intVal = intVal;
  }

  public String getStringVal() {
    return stringVal;
  }

  public void setStringVal(String stringVal) {
    this.stringVal = stringVal;
  }

  @Override
  public String toString() {
    return "Data{" +
        "intVal=" + intVal +
        ", stringVal=" + stringVal +
        '}';
  }
}

Make

배포된 smart contract와 해당되는 interface를 사용하면 ContractApi를 만들 수 있습니다.

nonce 실패 시 재시도 횟수와 간격을 명시적으로 설정해서 생성.

// create a contract api
ContractAddress contractAddress = deployedContractAddress;
ContractApi<CustomInterface1> contractApi = new ContractApiFactory()
    .create(contractAddress, CustomInterface1.class);
System.out.println("ContractApi: " + contractApi);

nonce 실패 시 재시도 횟수와 간격을 기본 설정으로 해서 생성.

// create a contract api with retry count 5 and interval 1000ms
ContractAddress contractAddress = deployedContractAddress;
TryCountAndInterval tryCountAndInterval = TryCountAndInterval.of(5, Time.of(1000L));
ContractApi<CustomInterface1> contractApi = new ContractApiFactory()
    .create(contractAddress, CustomInterface1.class, tryCountAndInterval);
System.out.println("ContractApi: " + contractApi);

Execute

AergoKey를 사용해서 실행.

// prepare an signer
AergoKey signer = richKey;

// create a contract api
ContractAddress contractAddress = deployedContractAddress;
ContractApi<CustomInterface1> contractApi = new ContractApiFactory()
    .create(contractAddress, CustomInterface1.class);

// execute contract with a contract api
TxHash executeTxHash = contractApi.with(client).execution(signer)
    .set("key", 123, "test", Fee.INFINITY);
System.out.println("Execute tx hash: " + executeTxHash);

WalletApi를 사용해서 실행.

// create a contract api
ContractAddress contractAddress = deployedContractAddress;
ContractApi<CustomInterface1> contractApi = new ContractApiFactory()
    .create(contractAddress, CustomInterface1.class);

// execute contract with a contract api
walletApi.unlock(authentication);
TxHash executeTxHash = contractApi.with(client).execution(walletApi)
    .set("key", 123, "test", Fee.INFINITY);
walletApi.lock();
System.out.println("Execute tx hash: " + executeTxHash);

Query

Binging할 model을 사용해서 실행.

// create a contract api
ContractAddress contractAddress = deployedContractAddress;
ContractApi<CustomInterface1> contractApi = new ContractApiFactory()
    .create(contractAddress, CustomInterface1.class);

// query contract with a contract api
Data data = contractApi.with(client).query().get("key");
System.out.println("Queried data: " + data);

Binging할 model을 사용하지 않고 실행.

// create a contract api
ContractAddress contractAddress = deployedContractAddress;
ContractApi<CustomInterface2> contractApi = new ContractApiFactory()
    .create(contractAddress, CustomInterface2.class);

// query contract with a contract api
ContractResult contractResult = contractApi.with(client).query().get("key");
System.out.println("Queried data: " + contractResult);