Caver-java valueTransfer 예제를 service-chain에서 돌렸는데 에러가 발생합니다

안녕하세요?
caver-java로 valueTransfer 예제를 service-chain에서 돌렸는데 에러가 발생합니다.

service chain은 수수료 무료 (gas-price = 0)로 설정되어 있습니다.

에러 위치는 transaction 서명하는 부분
valueTransfer.sign(keyring);
이며, 상세 위치는 다음과 같습니다.

at com.klaytn.caver.transaction.AbstractTransaction.fillTransaction(AbstractTransaction.java:455)

if(this.nonce.equals(“0x”) || this.chainId.equals(“0x”) || this.gasPrice.equals(“0x”)) {
throw new RuntimeException(“Cannot fill transaction data.(nonce, chainId, gasPrice). klaytnCall must be set in Transaction instance to automatically fill the nonce, chainId or gasPrice. Please call the setKlaytnCall to set klaytnCall in the Transaction instance.”);
}

위 코드에서 gas-price가 0x일 경우는 예외를 반환하도록 되어 있는데
수수료 무료인 service-chain에서는 value transfer를 수행할 수 없나요?

그리고 contract deploy나 execution transaction에서도 “서명(sign)” 수행 시 위 코드를 타지 않나요?

감사합니다

gradle로 설치한 caver-java 버전은 1.6.1 입니다.

implementation 'com.klaytn.caver:core:1.6.1'
implementation "ch.qos.logback:logback-classic:1.2.3"
implementation 'com.squareup.okhttp3:okhttp:4.3.1'

안녕하세요.
sign은 말하신대로, contract deploy, execute할때도 위 코드를 타게 됩니다.

테스트 해보신 ValueTransfser 예제 코드를 알려주시면 확인하는데 도움이 될 것 같습니다.

감사합니다.

아래 코드 발췌하여 송부드립니다.

@Test
public void KlayConnectionTest() throws CipherException, IOException, TransactionException {
Caver caver = new Caver(“http://service chain URL:8551”);

    //Read keystore json file.
    File file = new File("keystore 파일");

    //Decrypt keystore.
    ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
    KeyStore keyStore = objectMapper.readValue(file, KeyStore.class);
    AbstractKeyring keyring = KeyringFactory.decrypt(keyStore, "");

    //Add to caver wallet.
    caver.wallet.add(keyring);

    BigInteger value = new BigInteger(Utils.convertToPeb(new BigDecimal(BigInteger.valueOf(1000)), "peb"));

    //Create a value transfer transaction
    ValueTransfer valueTransfer = new ValueTransfer.Builder()
            .setKlaytnCall(caver.rpc.getKlay())
            .setFrom(keyring.getAddress())
            .setTo("2b827ae52a8d8852a47dc955ba847b3646e3c7da")
            .setValue(value)
            .setGas(BigInteger.valueOf(25000))
            .build();

    //Sign to the transaction
    valueTransfer.sign(keyring);

    //Send a transaction to the klaytn blockchain platform (Klaytn)
    Bytes32 result = caver.rpc.klay.sendRawTransaction(valueTransfer.getRawTransaction()).send();
    if(result.hasError()) {
        throw new RuntimeException(result.getError().getMessage());
    }

    //Check transaction receipt.
    TransactionReceiptProcessor transactionReceiptProcessor = new PollingTransactionReceiptProcessor(caver, 1000, 15);
    TransactionReceipt.TransactionReceiptData transactionReceipt = transactionReceiptProcessor.waitForTransactionReceipt(result.getResult());
    System.out.println("transactionReceipt = " + transactionReceipt);
}

상기 코드를 baobab node에서 돌려도 동일하게 에러가 발생합니다.
서비스체인 환경문제가 아닌 거 같은데 원인을 못찾겠네요.

도움 주시면 감사드리겠습니다.

안녕하세요.

저는 동일한 코드로 실행했는데 잘 되었습니다.
private key는 Klatyn Wallet을 통해 발급받은 account prvivate key를 사용하였고 faucet으로 test용 클레이를 받았습니다.

아래 링크는 아래 코드를 실행하고 난 결과를 Klatyn scope로 확인한 것입니다.

        Caver caver = new Caver("https://api.baobab.klaytn.net:8651/");
        SingleKeyring keyring = KeyringFactory.createFromPrivateKey("private Key");
        caver.wallet.add(keyring);

        BigInteger value = new BigInteger(Utils.convertToPeb(BigDecimal.ONE, "KLAY"));

        //Create a value transfer transaction
        ValueTransfer valueTransfer = new ValueTransfer.Builder()
                .setKlaytnCall(caver.rpc.getKlay())
                .setFrom(keyring.getAddress())
                .setTo("0x8084fed6b1847448c24692470fc3b2ed87f9eb47")
                .setValue(value)
                .setGas(BigInteger.valueOf(25000))
                .build();

        //Sign to the transaction
        valueTransfer.sign(keyring);

        //Send a transaction to the klaytn blockchain platform (Klaytn)
        Bytes32 result = caver.rpc.klay.sendRawTransaction(valueTransfer.getRawTransaction()).send();
        if(result.hasError()) {
            throw new RuntimeException(result.getError().getMessage());
        }

        //Check transaction receipt.
        TransactionReceiptProcessor transactionReceiptProcessor = new PollingTransactionReceiptProcessor(caver, 1000, 15);
        TransactionReceipt.TransactionReceiptData transactionReceipt = transactionReceiptProcessor.waitForTransactionReceipt(result.getResult());

동일하게 에러가 나는 부분은 어디인지? babob test node는 어떻게 구성하셨는지? 테스트 구축 환경과정을 상세히 알려주시면 이슈를 찾고 해결하여 빠르게 도움을 드릴 수 있을 것 같습니다.

감사합니다.

안녕하세요? 먼저 빠르고 상세한 답변에 정말 감사드립니다.

매니저님께서 보내주신 답안 code와 제 코드를 비교해 본 결과 keyring 추출을
KeyringFactory.decrypt 로 하느냐 KeyringFactory.createFromPrivateKey 로 하느냐의 차이로 보여서
제가 테스트 하고 있는 계정의 keystore에서 private key를 추출하여 KeyringFactory.createFromPrivateKey 로 호출해보았더니 정상적으로 동작합니다.

그러나 github에 있는 예제 코드

//Decrypt keystore.
ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
KeyStore keyStore = objectMapper.readValue(file, KeyStore.class);
AbstractKeyring keyring = KeyringFactory.decrypt(keyStore, “password”);

로 돌려보면 여전히 동작하지 않습니다.

KeyringFactory.decrypt의 반환값은 AbstractKeyring,
KeyringFactory.createFromPrivateKey SingleKeyring 로 서로 달라서

KeyringFactory.decrypt 함수 내부를 확인해보니 일반적인 account의 경우, KeyringFactory.createWithSingleKey을 호출하여 SingleKeyring type을 반환하는 것 같아
아래와 같이 SingleKeyring으로 강제 형변환을 하여 실행해 보았지만 역시 실패하였습니다.
SingleKeyring keyring = (SingleKeyring)KeyringFactory.decrypt(keyStore, “password”);

이번에는 위 두가지 case를 통해 얻어지는 keyring 의 내용을 출력해보았습니다.
ObjectMapper objectMapper = ObjectMapperFactory. getObjectMapper ();
KeyStore keyStore = objectMapper.readValue(file, KeyStore.class);
SingleKeyring keyring = (SingleKeyring)KeyringFactory. decrypt (keyStore, “password”);
System. out .println("keyring.toString() = " + keyring.toString());
System. out .println("keyring.getAddress() = " + keyring.getAddress());
System. out .println("pubkey = " + keyring.getPublicKey());
System. out .println("privkey = " + keyring.getKey().getPrivateKey());

address 출력 부분에 0x가 있고 없고의 차이가 있었지만, private key 내용은 동일하게 나왔습니다.

그래서 위 코드를 통해 얻어진 private key로 다시 SingleKeyring을 생성하도록 아래와 같이 코딩하였습니다.
SingleKeyring keyring2 = KeyringFactory. createFromPrivateKey (keyring.getKey().getPrivateKey());

상기 코드로 인해 생성된 keyring2로 valueTransfer를 수행한 결과 정상적으로 처리됨을 확인하였습니다.

그러나 여전히
//Decrypt keystore.
ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
KeyStore keyStore = objectMapper.readValue(file, KeyStore.class);
AbstractKeyring keyring = KeyringFactory.decrypt(keyStore, “password”);

코드가 왜 정상동작 하지 않는지에 대한 의문은 해결되지 않았습니다.

혹시 도움을 받을 수 있으면 정말 감사드리겠습니다.

연관된 질문 하나 더 올리겠습니다.

klaytn docs

에서 아래 내용을 보게 되었는데요.

Caver caver = new Caver(Caver.MAINNET_URL);

String password = “password”;
String keyStoreJsonString = “{\n” +
" “version”: 4,\n" +
" “id”: “9c12de05-0153-41c7-a8b7-849472eb5de7”,\n" +
" “address”: “0xc02cec4d0346bf4124deeb55c5216a4138a40a8c”,\n" +
" “keyring”: [\n" +
" {\n" +
" “ciphertext”: “eacf496cea5e80eca291251b3743bf93cdbcf7072efc3a74efeaf518e2796b15”,\n" +
" “cipherparams”: {\n" +
" “iv”: “d688a4319342e872cefcf51aef3ec2da”\n" +
" },\n" +
" “cipher”: “aes-128-ctr”,\n" +
" “kdf”: “scrypt”,\n" +
" “kdfparams”: {\n" +
" “dklen”: 32,\n" +
" “salt”: “c3cee502c7157e0faa42386c6d666116ffcdf093c345166c502e23bc34e6ba40”,\n" +
" “n”: 4096,\n" +
" “r”: 8,\n" +
" “p”: 1\n" +
" },\n" +
" “mac”: “4b49574f3d3356fa0d04f73e07d5a2a6bbfdd185bedfa31f37f347bc98f2ef26”\n" +
" }\n" +
" ]\n" +
“}”;

SingleKeyring decrypt = (SingleKeyring)KeyringFactory.decrypt(keyStoreJsonString, password);
System.out.println("Decrypted address : " + decrypt.getAddress());
System.out.println("Decrypted key : " + decrypt.getKey());

AbstractKeyring addedKeyring = caver.wallet.add(decrypt);
System.out.println("address : " + addedKeyring.getAddress());
System.out.println("key : " + addedKeyring.getKey());

addedKeyring 이 AbstractKeyring type이라 getKey()가 없어서 compile 에러가 발생합니다.

@hlib

안녕하세요 :slight_smile:
아래 코드가 잘 동작하지 않는다고 하셨는데 혹시 어떤 에러가 발생했는지 에러 메시지 공유 가능하실까요?

//Decrypt keystore.
ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
KeyStore keyStore = objectMapper.readValue(file, KeyStore.class);
AbstractKeyring keyring = KeyringFactory.decrypt(keyStore, “password”);

안되는 이유가 여러가지 일 수 있습니다.

  • KeyStore 클래스를 잘못 import 하셨을 경우
    • import com.klaytn.caver.wallet.keyring.KeyStore; 가 아닌 import java.security.KeyStore; 등 다른 클래스를 import 하신 건 아닌지 확인 부탁드립니다.
  • 생성하신 KeyStore파일의 Password가 틀렸을 경우
    • Password가 맞는지 한 번 더 확인 부탁드립니다.

@hlib 님께서 사용하셨다고 했던 의존성을 저도 그대로 활용하여 재현을 시도해보았으나 재현되지 않았습니다.

implementation 'com.klaytn.caver:core:1.6.1'
implementation "ch.qos.logback:logback-classic:1.2.3"
implementation 'com.squareup.okhttp3:okhttp:4.3.1'

제가 사용한 테스트 코드는 아래와 같습니다.

import com.fasterxml.jackson.databind.ObjectMapper;
import com.klaytn.caver.Caver;
import com.klaytn.caver.methods.response.Bytes32;
import com.klaytn.caver.methods.response.TransactionReceipt;
import com.klaytn.caver.transaction.response.PollingTransactionReceiptProcessor;
import com.klaytn.caver.transaction.response.TransactionReceiptProcessor;
import com.klaytn.caver.transaction.type.ValueTransfer;
import com.klaytn.caver.wallet.keyring.AbstractKeyring;
import com.klaytn.caver.wallet.keyring.KeyStore;
import com.klaytn.caver.wallet.keyring.KeyringFactory;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.web3j.crypto.CipherException;
import org.web3j.protocol.ObjectMapperFactory;
import org.web3j.protocol.exceptions.TransactionException;

import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
@DisplayName("TxTest")
class TXTest {
    @Test
    void testSomething() {
        Caver caver = new Caver(Caver.DEFAULT_URL);

        File file = new File("/Users/denver.lee/Downloads/keystore-0xf697cc521c1b4d1b2dad04a8381083e0975f47b3-2021-6-8.json");


        ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
        KeyStore keyStore = null;
        try {
            keyStore = objectMapper.readValue(file, KeyStore.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
        AbstractKeyring keyring = null;
        try {
            keyring = KeyringFactory.decrypt(keyStore, "mypassword");
        } catch (CipherException e) {
            e.printStackTrace();
        }

        System.out.println("keyring.toString() = " + keyring.toString());
        System.out.println("keyring.getAddress() = " + keyring.getAddress());
        System.out.println(keyring.getKlaytnWalletKey());

        //Create a value transfer transaction
        ValueTransfer valueTransfer = new ValueTransfer.Builder()
                .setKlaytnCall(caver.rpc.getKlay())
                .setFrom(keyring.getAddress())
                .setTo("0x8084fed6b1847448c24692470fc3b2ed87f9eb47")
                .setValue(BigInteger.valueOf(1))
                .setGas(BigInteger.valueOf(25000))
                .build();

        //Sign to the transaction
        try {
            valueTransfer.sign(keyring);
        } catch (IOException e) {
            e.printStackTrace();
        }

        //Send a transaction to the klaytn blockchain platform (Klaytn)
        Bytes32 result = null;
        try {
            result = caver.rpc.klay.sendRawTransaction(valueTransfer.getRawTransaction()).send();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(result.hasError()) {
            throw new RuntimeException(result.getError().getMessage());
        }

        //Check transaction receipt.
        TransactionReceiptProcessor transactionReceiptProcessor = new PollingTransactionReceiptProcessor(caver, 1000, 15);
        try {
            TransactionReceipt.TransactionReceiptData transactionReceipt = transactionReceiptProcessor.waitForTransactionReceipt(result.getResult());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TransactionException e) {
            e.printStackTrace();
        }
    }
}

매니저님 보내주신 코드에서 연결할 노드 정보랑 keystore 파일정보만 교체해서 돌려보았으나 동일한 에러가 발생합니다.

에러는 맨 첫 글에 공유드렸던 내용인데요. 아래에 스샷을 첨부하겠습니다.

@hlib
안녕하세요 :slight_smile:
사진으로 에러를 첨부해주셨는데, 다시 실행해주신 뒤 출력된 로그 전부를 답글로 달아주실 수 있으실까요?

또한 사용하신 소스코드는 제가 첨부드린 소스코드와
노드 정보, Keystore 파일 경로, 비밀 번호를 제외하고는 전부 일치하는 게 맞나요?

이 부분 확인 부탁드립니다.

아래 로그 내용입니다.

Task :compileJava UP-TO-DATE

Task :processResources UP-TO-DATE
Task :classes UP-TO-DATE
Task :compileTestJava UP-TO-DATE
Task :processTestResources NO-SOURCE
Task :testClasses UP-TO-DATE
Task :test
6월 08, 2021 6:14:12 오후 org.junit.platform.launcher.core.EngineDiscoveryOrchestrator lambda$logTestDescriptorExclusionReasons$7
INFO: 0 containers and 4 tests were Method or class mismatch
keyring.toString() = com.klaytn.caver.wallet.keyring.SingleKeyring@71a60453
keyring.getAddress() = ab3eaedca89561e3836d37a0cdc961a84c766778
0x790e2616ec6ccfaf1cbb244fa50f945f435b8a9cbca2af712db6149e36e613fd0x00ab3eaedca89561e3836d37a0cdc961a84c766778
18:14:14.374 [Test worker] DEBUG org.web3j.protocol.http.HttpService - → POST http://45.63.124.134:8551/
18:14:14.376 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Type: application/json; charset=utf-8
18:14:14.376 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Length: 124
18:14:14.376 [Test worker] DEBUG org.web3j.protocol.http.HttpService -
18:14:14.376 [Test worker] DEBUG org.web3j.protocol.http.HttpService - {“jsonrpc”:“2.0”,“method”:“klay_getTransactionCount”,“params”:[“ab3eaedca89561e3836d37a0cdc961a84c766778”,“pending”],“id”:0}
18:14:14.377 [Test worker] DEBUG org.web3j.protocol.http.HttpService - → END POST (124-byte body)
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService - ← 200 OK http://45.63.124.134:8551/ (121ms)
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Server: fasthttp
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Date: Tue, 08 Jun 2021 09:14:14 GMT
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Type: application/json
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Length: 170
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Vary: Origin
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService -
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService - {“jsonrpc”:“2.0”,“id”:0,“error”:{“code”:-32602,“message”:“invalid argument 0: json: cannot unmarshal hex string without 0x prefix into Go value of type common.Address”}}

18:14:14.499 [Test worker] DEBUG org.web3j.protocol.http.HttpService - ← END HTTP (170-byte body)
18:14:14.504 [Test worker] DEBUG org.web3j.protocol.http.HttpService - → POST http://45.63.124.134:8551/
18:14:14.504 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Type: application/json; charset=utf-8
18:14:14.504 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Length: 60
18:14:14.504 [Test worker] DEBUG org.web3j.protocol.http.HttpService -
18:14:14.504 [Test worker] DEBUG org.web3j.protocol.http.HttpService - {“jsonrpc”:“2.0”,“method”:“klay_chainID”,“params”:,“id”:1}
18:14:14.504 [Test worker] DEBUG org.web3j.protocol.http.HttpService - → END POST (60-byte body)
18:14:14.552 [Test worker] DEBUG org.web3j.protocol.http.HttpService - ← 200 OK http://45.63.124.134:8551/ (47ms)
18:14:14.552 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Server: fasthttp
18:14:14.552 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Date: Tue, 08 Jun 2021 09:14:14 GMT
18:14:14.552 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Type: application/json
18:14:14.552 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Length: 42
18:14:14.552 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Vary: Origin
18:14:14.552 [Test worker] DEBUG org.web3j.protocol.http.HttpService -
18:14:14.552 [Test worker] DEBUG org.web3j.protocol.http.HttpService - {“jsonrpc”:“2.0”,“id”:1,“result”:“0x3e9”}

18:14:14.553 [Test worker] DEBUG org.web3j.protocol.http.HttpService - ← END HTTP (42-byte body)
18:14:14.553 [Test worker] DEBUG org.web3j.protocol.http.HttpService - → POST http://45.63.124.134:8551/
18:14:14.553 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Type: application/json; charset=utf-8
18:14:14.553 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Length: 61
18:14:14.553 [Test worker] DEBUG org.web3j.protocol.http.HttpService -
18:14:14.554 [Test worker] DEBUG org.web3j.protocol.http.HttpService - {“jsonrpc”:“2.0”,“method”:“klay_gasPrice”,“params”:,“id”:2}
18:14:14.554 [Test worker] DEBUG org.web3j.protocol.http.HttpService - → END POST (61-byte body)
18:14:14.601 [Test worker] DEBUG org.web3j.protocol.http.HttpService - ← 200 OK http://45.63.124.134:8551/ (47ms)
18:14:14.601 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Server: fasthttp
18:14:14.601 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Date: Tue, 08 Jun 2021 09:14:14 GMT
18:14:14.602 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Type: application/json
18:14:14.602 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Length: 48
18:14:14.602 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Vary: Origin
18:14:14.602 [Test worker] DEBUG org.web3j.protocol.http.HttpService -
18:14:14.602 [Test worker] DEBUG org.web3j.protocol.http.HttpService - {“jsonrpc”:“2.0”,“id”:2,“result”:“0x5d21dba00”}

18:14:14.602 [Test worker] DEBUG org.web3j.protocol.http.HttpService - ← END HTTP (48-byte body)

java.lang.NullPointerException
at com.klaytn.caver.transaction.AbstractTransaction.fillTransaction(AbstractTransaction.java:455)
at com.klaytn.caver.transaction.AbstractTransaction.sign(AbstractTransaction.java:282)
at com.klaytn.caver.transaction.AbstractTransaction.sign(AbstractTransaction.java:259)
at io.dkargo.excaverjava.TestCaverJavaDefault.testSomething(TestCaverJavaDefault.java:214)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at com.sun.proxy.$Proxy2.stop(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.stop(TestWorker.java:135)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:414)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
at java.base/java.lang.Thread.run(Thread.java:834)

TestCaverJavaDefault > testSomething() FAILED
java.lang.NullPointerException at TestCaverJavaDefault.java:214
1 test completed, 1 failed

Task :test FAILED
FAILURE: Build failed with an exception.

  • What went wrong:
    Execution failed for task ‘:test’.

There were failing tests. See the report at: file:///Users/hongjonghyeon/klaytn/programs/ex-caverjava/build/reports/tests/test/index.html

  • Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
  • Get more help at https://help.gradle.org
    BUILD FAILED in 3s
    4 actionable tasks: 1 executed, 3 up-to-date

그리고 노드 정보, Keystore 파일 경로, 비밀 번호외에 수정한 내용은

수신자 주소
.setTo(“0xea9792f5b9dd232692d22908d47032ec108d13da”)
와 전송한 klay 양
BigInteger value = new BigInteger(Utils.convertToPeb(new BigDecimal(BigInteger.valueOf(1000)), “peb”));
만 바꿨습니다.

정말 감사드립니다.

@hlib

안녕하세요 :slight_smile:
원인을 찾은 거 같습니다.

먼저 사용하시는 Keystore 파일의 내용 중 “address” 필드의 값 맨 앞에 0x 를 붙여주시기 바랍니다.
Keystore 파일의 내용을 변경하기 곤란하시다면, 소스코드 상에서 앞에 0x를 붙여주시기 바랍니다.

AbstractTransaction.fillTransaction() 에서 java.lang.NullPointerException 에러가 발생한 이유는 첨부해주신 로그 중 아래의 로그와 관련이 있습니다.

18:14:14.374 [Test worker] DEBUG org.web3j.protocol.http.HttpService - → POST http://45.63.124.134:8551/ 
18:14:14.376 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Type: application/json; charset=utf-8 
18:14:14.376 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Length: 124 
18:14:14.376 [Test worker] DEBUG org.web3j.protocol.http.HttpService - 
18:14:14.376 [Test worker] DEBUG org.web3j.protocol.http.HttpService - {“jsonrpc”:“2.0”,“method”:“klay_getTransactionCount”,“params”:[“ab3eaedca89561e3836d37a0cdc961a84c766778”,“pending”],“id”:0} 
18:14:14.377 [Test worker] DEBUG org.web3j.protocol.http.HttpService - → END POST (124-byte body) 
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService - ← 200 OK http://45.63.124.134:8551/ (121ms) 
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Server: fasthttp
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Date: Tue, 08 Jun 2021 09:14:14 GMT
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Type: application/json 
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Content-Length: 170 
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService - Vary: Origin 
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService - 
18:14:14.498 [Test worker] DEBUG org.web3j.protocol.http.HttpService - {“jsonrpc”:“2.0”,“id”:0,“error”:{“code”:-32602,“message”:“invalid argument 0: json: cannot unmarshal hex string without 0x prefix into Go value of type common.Address”}}
18:14:14.499 [Test worker] DEBUG org.web3j.protocol.http.HttpService - ← END HTTP (170-byte body)

AbstractTransaction.fillTransaction() 의 코드를 보시면 아래와 같이 @hlib 님께서 사용하고 계신 Address 0xab3eaedca89561e3836d37a0cdc961a84c766778 의 Nonce값 (= Transaction Count)를 블록체인으로부터 읽어들이는 RPC 콜을 가장 먼저 호출합니다.

        if(klaytnCall != null) {
            if(this.nonce.equals("0x")) {
                this.nonce = klaytnCall.getTransactionCount(this.from, DefaultBlockParameterName.PENDING).send().getResult();
            }
        // ... 생략
        }

이 때 호출할 때 넘어가는 주소 값으로 Hex String이 사용되어야 하는데, @hlib 님의 주소 값은 앞에 0x 빠져 있는 형태로 올바른 Hex String 포맷이 아닙니다.

이렇게 될 경우 RPC 콜은 실패하게 되고 this.nonce 필드의 값은 제대로 할당되지 못한 채 null이 되어버립니다.
AbstractTransaction.fillTransaction 메서드의 가장 마지막 라인의 코드 블록에서는 this.nonce.equals("0x") 로 값의 정합성을 판단하는 조건문이 있는데, null에 대해서 equals라는 메서드를 호출하게 되면서 발생하는 에러입니다.

안녕하십니까?

답변 정말 감사드립니다.

keystore 파일의 address 부분에 0x를 추가하고 나니 정상적으로 동작함을 확인하였습니다.

0x가 붙어있지 않은 keystore는 endpoint node 콘솔에서 ken account new 명령을 사용하여 만든 것이었습니다.
혹시나 해서 다시 account를 생성해보았는데 새로 생성한 address에도 역시 0x가 붙어있지 않았습니다.

그렇지만 klaytn wallet에서 생성한 keystore 파일에는 address에 0x가 붙어 있었습니다.

caver-js의 경우, 어떠한 방식으로 만든 keystore file이든 (ken console, klaytn wallet…) 모두 정상동작 하였었는데, caver-java에서는 keystore file 읽을 때 address 필드가 0x로 시작하는지의 여부를 확인한 후 0x로 시작하지 않을 경우 0x를 붙여서 작업해야 하는 것으로 이해하면 될까요?

감사드립니다.

1 Like

다음 버전 릴리즈시, 0x prefix를 붙이든 안붙이든 동작하도록 수정해서 알려 드리겠습니다. 불편을 드려 죄송합니다.

2 Likes