diff --git a/README.md b/README.md index 4a401b15..c530dcbc 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,41 @@ caver-java-ext-kas is [caver-java](https://github.com/klaytn/caver-java)'s exten * [Use Token History API](#use-token-history-api) * [Use Wallet API](#use-wallet-api) * [Use Anchor API](#use-anchor-api) + * [Introduced KASWallet](#introduced-kaswallet) * [Test](#test) ## Installation -#### maven +### Installation + +#### add a Repository + +To install caver-java-ext-kas, you need to install caver-java. +To install the latest version of caver-java, you should add a jitpack repository for IPFS feature. + +**maven** +```groovy + + + jitpack.io + https://jitpack.io + + +``` + +**gradle** +```groovy +allprojects { + repositories { + ... //mavenCentral() or jcenter() + maven { url 'https://jitpack.io' } + } +} +``` + +#### add a dependency + +**maven** ```groovy xyz.groundx.caver @@ -32,7 +62,8 @@ caver-java-ext-kas is [caver-java](https://github.com/klaytn/caver-java)'s exten pom ``` -#### gradle + +**gradle** ```groovy implementation 'xyz.groundx.caver:caver-java-ext-kas:X.X.X' ``` @@ -179,6 +210,90 @@ options.setFromTimestamp(); options.setToTimesatamp(); ``` +### Introduced KASWallet +KASWallet allows you to handle transaction instance in caver-java by using KAS Wallet API. + - Generate and manage accounts by using KAS Wallet API. + - Sign a transaction instance in caver-java by using KAS Wallet API. + +KASWallet can be used as a member called `wallet` of the `CaverExtKAS` class. +The `CaverExtKAS` class can provide the same usability as the 'wallet' of `Caver` class in caver-java through KASWallet. +Also, `Contract`, `KIP7`, `KIP17` classes in caver-java can be used the same as the existing caver-java. + +Here we introduced a simple example using Contract, KIP7 and KIP17 respectively. Please refer to Contract, KIP7 and KIP17 of [Klaytn Docs](https://docs.klaytn.com/bapp/sdk/caver-java/getting-started#smart-contract) for detailed usage. + +#### Use Contract class with KASWallet +```java +final String ABI = "[{\"constant\":true,\"inputs\":[{\"name\":\"key\",\"type\":\"string\"}],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},...]"; +final String BINARY = "Smart contract binary Data"; +String accessKey = "your access key"; +String secretAccessKey = "your secret access key"; +CaverExtKAS caver = new CaverExtKAS(ChainId.BAOBAB_TESTNET, accessKey, secretAccessKey); + +Contract contract = new Contract(caver, abi); + +//Deploy Contract +String account = "0x{address}"; +BigInteger gas = BigInteger.valueOf(10000000); +SendOptions sendOptions = new SendOptions(account, gas); +contract.deploy(sendOptions, BINARY); + +//Execute contract's "set" function. +SendOptions sendOptions = new SendOptions(account, BigInteger.valueOf(5000000)); +TransactionReceipt.TransactionReceiptData receiptData = contract.send(sendOptions, "set", "key", "value"); +``` + +#### Use KIP7 class with KASWallet +```java +String accessKey = "your access key"; +String secretAccessKey = "your secret access key"; +CaverExtKAS caver = new CaverExtKAS(ChainId.BAOBAB_TESTNET, accessKey, secretAccessKey); + +String from = "0x{from address}"; +String to = "0x{to address}"; + +//deploy KIP7 contract +BigInteger initialSupply = BigInteger.TEN.multiply(BigInteger.TEN.pow(18)); // 10 * 10^18 +KIP7 kip7 = KIP7.deploy(caver, from, "KAS", "SDK", 18, iniinitialSupply); + +//execute KIP7's transfer function. +BigInteger transferAmount = BigInteger.ONE.multiply(BigInteger.TEN.pow(18)); +SendOptions sendOptions = new SendOptions(from, (String)null); +TransactionReceipt.TransactionReceiptData receiptData = kip7.transfer(to, amount, sendOptions); +``` + +#### Use KIP17 class with KASWallet +```java +String accessKey = "your access key"; +String secretAccessKey = "your secret access key"; +CaverExtKAS caver = new CaverExtKAS(ChainId.BAOBAB_TESTNET, accessKey, secretAccessKey); + +//deploy KIP17 contract. +String from = "0x{from address}"; +KIP17 kip17 = KIP17.deploy(caver, from, name, symbol); + +//execute KIP17's mint function. +SendOptions sendOptions = new SendOptions(from, (String)null); +TransactionReceipt.TransactionReceiptData receiptData = kip17.mint(kip17, from, BigInteger.ZERO); +``` + +#### Handling KASAPIException +When an error occurs while using KAS Wallet API in `KASWallet` class, it throws `KASAPIException`(extends RuntimeException). +KASAPIException has HTTP Error code and message and also it contains response body. + +Below is an example code that handles the error that occurred while executing the KAS Wallet API. + +```java +try { + String unKnownAddress = "0x785ba1146cc1bed97b9c8d73e9293cc3b6bc3691"; + Account account = caver.wallet.getAccount(unKnownAddress); +} catch (KASAPIException e) { + System.out.println(e.getcode()); // 400 + System.out.println(e.getMessage()); // "Bad Request" + System.out.println(e.getResponseBody().getCode()); // 1061010 + System.out.println(e.getResponseBody().getMessage()); // "data don't exist" +} +``` + ## Test Before testing, you need to either modify the data in "Config.java" or use ".env" to set the data required for testing. If you use ".env", you must set variable below. diff --git a/build.gradle b/build.gradle index 26adf6a4..c0c8a4e7 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.caver_version = '1.5.3-rc.2' + ext.caver_version = '1.5.6-rc.6' } plugins { @@ -13,7 +13,7 @@ plugins { } allprojects { - version '1.0.1' + version '1.0.2' group 'xyz.groundx.caver' description 'An extension library of caver-java for using KAS (Klaytn API Service).' @@ -27,7 +27,8 @@ allprojects { repositories { - mavenCentral() + jcenter() + maven { url 'https://jitpack.io' } } javadoc { @@ -156,8 +157,8 @@ allprojects { testCompile group: 'junit', name: 'junit', version: '4.12' + testCompile 'org.mockito:mockito-core:3.6.28' testCompile "ch.qos.logback:logback-core:1.2.3", "ch.qos.logback:logback-classic:1.2.3" } } - diff --git a/src/main/java/xyz/groundx/caver_ext_kas/CaverExtKAS.java b/src/main/java/xyz/groundx/caver_ext_kas/CaverExtKAS.java index 0690fb11..ed3daf23 100644 --- a/src/main/java/xyz/groundx/caver_ext_kas/CaverExtKAS.java +++ b/src/main/java/xyz/groundx/caver_ext_kas/CaverExtKAS.java @@ -18,9 +18,11 @@ import com.klaytn.caver.Caver; import com.klaytn.caver.rpc.RPC; +import com.klaytn.caver.wallet.IWallet; import com.squareup.okhttp.Credentials; import org.web3j.protocol.http.HttpService; import xyz.groundx.caver_ext_kas.kas.KAS; +import xyz.groundx.caver_ext_kas.wallet.KASWallet; /** * Representing wrapping class that can use Klaytn API Service @@ -37,12 +39,42 @@ public class CaverExtKAS extends Caver { public KAS kas; /** - * Creates a CaverExtKAS instance. + * The KAS wallet instance. + */ + public KASWallet wallet; + + /** + * Creates a CaverExtKAS instance.
+ * It need to init each KAS API manually. */ public CaverExtKAS() { this.kas = new KAS(); } + /** + * Creates a CaverExtKAS instance.
+ * It init all supported KAS API(Node API, Anchor API, Wallet API) + * @param chainId The Klaytn network chain id. + * @param accessKeyId The access key provided by KAS console. + * @param secretAccessKey The secret key provided by KAS console. + */ + public CaverExtKAS(int chainId, String accessKeyId, String secretAccessKey) { + this.kas = new KAS(); + initKASAPI(chainId, accessKeyId, secretAccessKey); + } + + /** + * Creates a CaverExtKAS instance.
+ * It init all supported KAS API(Node API, Anchor API, Wallet API) + * @param chainId The Klaytn network chain id. + * @param accessKeyId The access key provided by KAS console. + * @param secretAccessKey The secret key provided by KAS console. + */ + public CaverExtKAS(String chainId, String accessKeyId, String secretAccessKey) { + this.kas = new KAS(); + initKASAPI(chainId, accessKeyId, secretAccessKey); + } + /** * Initialize all KAS API. * @param chainId The Klaytn network chain id. @@ -137,7 +169,7 @@ public void initAnchorAPI(int chainId, String accessKeyId, String secretAccessKe * @param secretAccessKey The secret key provided by KAS console. */ public void initAnchorAPI(String chainId, String accessKeyId, String secretAccessKey) { - kas.initAnchorAPI(chainId, accessKeyId, secretAccessKey, URL_ANCHOR_API); + initAnchorAPI(chainId, accessKeyId, secretAccessKey, URL_ANCHOR_API); } /** @@ -181,7 +213,7 @@ public void initWalletAPI(int chainId, String accessKeyId, String secretAccessKe * @param secretAccessKey The secret key provided by KAS console. */ public void initWalletAPI(String chainId, String accessKeyId, String secretAccessKey) { - kas.initWalletAPI(chainId, accessKeyId, secretAccessKey, URL_WALLET_API); + initWalletAPI(chainId, accessKeyId, secretAccessKey, URL_WALLET_API); } /** @@ -204,6 +236,7 @@ public void initWalletAPI(int chainId, String accessKeyId, String secretAccessKe */ public void initWalletAPI(String chainId, String accessKeyId, String secretAccessKey, String url) { kas.initWalletAPI(chainId, accessKeyId, secretAccessKey, url); + setWallet(new KASWallet(this.kas.wallet)); } /** @@ -225,7 +258,7 @@ public void initTokenHistoryAPI(int chainId, String accessKeyId, String secretAc * @param secretAccessKey The secret key provided by KAS console. */ public void initTokenHistoryAPI(String chainId, String accessKeyId, String secretAccessKey) { - kas.initTokenHistoryAPI(chainId, accessKeyId, secretAccessKey, URL_TH_API); + initTokenHistoryAPI(chainId, accessKeyId, secretAccessKey, URL_TH_API); } /** @@ -258,6 +291,11 @@ public KAS getKas() { return kas; } + @Override + public IWallet getWallet() { + return this.wallet; + } + /** * Setter function for KAS instance * @param kas The KAS instance. @@ -265,4 +303,8 @@ public KAS getKas() { public void setKas(KAS kas) { this.kas = kas; } + + public void setWallet(KASWallet wallet) { + this.wallet = wallet; + } } diff --git a/src/main/java/xyz/groundx/caver_ext_kas/exception/ExceptionDetail.java b/src/main/java/xyz/groundx/caver_ext_kas/exception/ExceptionDetail.java new file mode 100644 index 00000000..93bb0717 --- /dev/null +++ b/src/main/java/xyz/groundx/caver_ext_kas/exception/ExceptionDetail.java @@ -0,0 +1,17 @@ +package xyz.groundx.caver_ext_kas.exception; + +public class ExceptionDetail { + int code; + String message = ""; + + public ExceptionDetail() { + } + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/xyz/groundx/caver_ext_kas/exception/KASAPIException.java b/src/main/java/xyz/groundx/caver_ext_kas/exception/KASAPIException.java new file mode 100644 index 00000000..f33dce5c --- /dev/null +++ b/src/main/java/xyz/groundx/caver_ext_kas/exception/KASAPIException.java @@ -0,0 +1,88 @@ +package xyz.groundx.caver_ext_kas.exception; + +import com.fasterxml.jackson.databind.ObjectMapper; +import xyz.groundx.caver_ext_kas.rest_client.io.swagger.client.ApiException; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +public class KASAPIException extends RuntimeException { + private int code = 0; + private Map> responseHeaders = null; + private ExceptionDetail responseBody = null; + + public KASAPIException() {} + + public KASAPIException(Throwable throwable) { + super(throwable); + } + + public KASAPIException(String message) { + super(message); + } + + public KASAPIException(String message, Throwable throwable) { + super(message, throwable); + } + + public KASAPIException(ApiException e) { + this(e.getMessage(), e.getCause(), e.getCode(), e.getResponseHeaders(), e.getResponseBody()); + } + + public KASAPIException(String message, Throwable throwable, int code, Map> responseHeaders, String responseBody) { + super(message, throwable); + this.code = code; + this.responseHeaders = responseHeaders; + if(responseBody != null && !responseBody.isEmpty()) { + setResponseBody(responseBody); + } + } + + public KASAPIException(String message, int code, Map> responseHeaders, String responseBody) { + this(message, (Throwable) null, code, responseHeaders, responseBody); + } + + public KASAPIException(String message, Throwable throwable, int code, Map> responseHeaders) { + this(message, throwable, code, responseHeaders, null); + } + + public KASAPIException(int code, Map> responseHeaders, String responseBody) { + this((String) null, (Throwable) null, code, responseHeaders, responseBody); + } + + public KASAPIException(int code, String message) { + super(message); + this.code = code; + } + + public KASAPIException(int code, String message, Map> responseHeaders, String responseBody) { + this(code, message); + this.responseHeaders = responseHeaders; + + if(responseBody != null && !responseBody.isEmpty()) { + setResponseBody(responseBody); + } + } + + public int getCode() { + return code; + } + + public Map> getResponseHeaders() { + return responseHeaders; + } + + public ExceptionDetail getResponseBody() { + return responseBody; + } + + public void setResponseBody(String responseBody) { + try { + ExceptionDetail detail = new ObjectMapper().readValue(responseBody, ExceptionDetail.class); + this.responseBody = detail; + } catch (IOException e){ + throw new RuntimeException("Failed to parsed json string : " + responseBody); + } + } +} diff --git a/src/main/java/xyz/groundx/caver_ext_kas/wallet/KASWallet.java b/src/main/java/xyz/groundx/caver_ext_kas/wallet/KASWallet.java new file mode 100644 index 00000000..60149a60 --- /dev/null +++ b/src/main/java/xyz/groundx/caver_ext_kas/wallet/KASWallet.java @@ -0,0 +1,393 @@ +/* + * Copyright 2020 The caver-java-ext-kas Authors + * + * Licensed under the Apache License, Version 2.0 (the “License”); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an “AS IS” BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package xyz.groundx.caver_ext_kas.wallet; + +import com.klaytn.caver.account.AccountKeyRoleBased; +import com.klaytn.caver.account.AccountKeyWeightedMultiSig; +import com.klaytn.caver.methods.response.AccountKey; +import com.klaytn.caver.transaction.AbstractFeeDelegatedTransaction; +import com.klaytn.caver.transaction.AbstractFeeDelegatedWithRatioTransaction; +import com.klaytn.caver.transaction.AbstractTransaction; +import com.klaytn.caver.transaction.TransactionDecoder; +import com.klaytn.caver.transaction.type.AccountUpdate; +import com.klaytn.caver.transaction.type.LegacyTransaction; +import com.klaytn.caver.utils.Utils; +import com.klaytn.caver.wallet.IWallet; +import com.klaytn.caver.wallet.keyring.SignatureData; +import xyz.groundx.caver_ext_kas.exception.KASAPIException; +import xyz.groundx.caver_ext_kas.kas.wallet.Wallet; +import xyz.groundx.caver_ext_kas.rest_client.io.swagger.client.ApiException; +import xyz.groundx.caver_ext_kas.rest_client.io.swagger.client.api.wallet.model.*; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Representing a wallet class that uses KAS Wallet API.
+ * KASWallet used as a member of CaverExtKAS named wallet.
+ * If you called sign() to sign Caver's Transaction(ValueTransfer, SmartContractExecution..), It is signed using KAS Wallet API internally. + */ +public class KASWallet implements IWallet { + /** + * The WalletAPI instance to use KAS Wallet API. + */ + Wallet walletAPI; + + /** + * Creates a wallet instance that uses the KAS Wallet API.
+ * KASWallet used as a member of CaverExtKAS named wallet. + * @param walletAPI An WalletAPI instance to use KAS Wallet API. + */ + public KASWallet(Wallet walletAPI) { + this.walletAPI = walletAPI; + } + + /** + * Generates accounts in KAS Wallet service. + * @param num The number of accounts to generate. + * @return List + * @throws KASAPIException + */ + @Override + public List generate(int num) throws KASAPIException { + try { + List addressList = new ArrayList<>(); + for(int i=0; i < num; i++) { + Account account = this.walletAPI.createAccount(); + addressList.add(account.getAddress()); + } + + return addressList; + } catch (ApiException e) { + throw new KASAPIException(e); + } + + } + + /** + * Get an account corresponding to the given address in KAS wallet service. + * @param address An address to get account in KAS Wallet service. + * @return Account + * @throws KASAPIException + */ + public Account getAccount(String address) throws KASAPIException { + try { + return this.walletAPI.getAccount(address); + } catch (ApiException e) { + throw new KASAPIException(e); + } + } + + /** + * Removes an account in KAS Wallet API service. + * @param address An address of account to remove. + * @return boolean + */ + @Override + public boolean remove(String address) { + try { + AccountStatus status = this.walletAPI.deleteAccount(address); + return status.getStatus().equals("deleted"); + } catch (ApiException e) { + return false; + } + } + + /** + * Check if the account corresponding to the address exists in KAS Wallet service. + * @param address The address of account to find. + * @return boolean + */ + @Override + public boolean isExisted(String address) { + try { + Account account = getAccount(address); + return true; + } catch (KASAPIException e) { + return false; + } + } + + /** + * Enable account in KAS Wallet service. + * @param address The address of account to enable. + * @return AccountSummary + * @throws KASAPIException + */ + public AccountSummary enableAccount(String address) throws KASAPIException { + try { + return this.walletAPI.enableAccount(address); + } catch (ApiException e) { + throw new KASAPIException(e); + } + + } + + /** + * Disable account in KAS Wallet service. + * @param address The address of account to disable. + * @return AccountSummary + * @throws KASAPIException + */ + public AccountSummary disableAccount(String address) throws KASAPIException { + try { + return this.walletAPI.disableAccount(address); + } catch (ApiException e) { + throw new KASAPIException(e); + } + } + + /** + * Sign a transaction instance using an account in KAS Wallet service. + * @param address An address of account to sign. + * @param transaction A transaction instance to sign. + * @return AbstractTransaction + * @throws IOException + * @throws KASAPIException + */ + @Override + public AbstractTransaction sign(String address, AbstractTransaction transaction) throws IOException, KASAPIException { + if(transaction.getFrom().equals("0x")) { + transaction.setFrom(address); + } + + if(!transaction.getFrom().toLowerCase().equals(address.toLowerCase())) { + throw new IllegalArgumentException("From address are not matched"); + } + + if(isWeightedMultiSigType(transaction, address)) { + throw new IllegalArgumentException("Not supported: Using multiple keys in an account is currently not supported."); + } + + List signatureData = makeSignature(transaction); + transaction.appendSignatures(signatureData); + + return transaction; + } + + /** + * Sign a transaction with the global fee payer using an account in KAS Wallet service. + * @param feeDelegatedTransaction A fee delegated transaction instance. + * @return AbstractFeeDelegatedTransaction + * @throws IOException + * @throws KASAPIException + */ + public AbstractFeeDelegatedTransaction signAsGlobalFeePayer(AbstractFeeDelegatedTransaction feeDelegatedTransaction) throws IOException, KASAPIException { + try { + feeDelegatedTransaction.fillTransaction(); + String rlp = feeDelegatedTransaction.getRLPEncoding(); + + FDProcessRLPRequest rlpRequest = new FDProcessRLPRequest(); + rlpRequest.setRlp(rlp); + rlpRequest.setSubmit(false); + + if(feeDelegatedTransaction instanceof AbstractFeeDelegatedWithRatioTransaction) { + rlpRequest.setFeeRatio(((AbstractFeeDelegatedWithRatioTransaction) feeDelegatedTransaction).getFeeRatioInteger().longValue()); + } + + FDTransactionResult result = this.walletAPI.requestFDRawTransactionPaidByGlobalFeePayer(rlpRequest); + + AbstractFeeDelegatedTransaction tx = (AbstractFeeDelegatedTransaction)TransactionDecoder.decode(result.getRlp()); + + String existFeePayer = feeDelegatedTransaction.getFeePayer(); + if(!existFeePayer.equals("0x") && !existFeePayer.equals(Utils.DEFAULT_ZERO_ADDRESS) && !existFeePayer.toLowerCase().equals(tx.getFeePayer().toLowerCase())) { + throw new RuntimeException("Invalid fee payer: The address of the fee payer defined in the transaction does not match the address of the global fee payer. To sign with a global fee payer, you must define the global fee payer's address in the feePayer field, or the feePayer field must not be defined."); + } + + feeDelegatedTransaction.setFeePayer(tx.getFeePayer()); + feeDelegatedTransaction.appendFeePayerSignatures(tx.getFeePayerSignatures()); + + return feeDelegatedTransaction; + } catch (ApiException e) { + throw new KASAPIException(e); + } + } + + /** + * Sign a transaction as a fee payer using an account in KAS Wallet service. + * @param feePayerAddress An address of account to sign as a fee payer in KAS Wallet service + * @param feeDelegatedTransaction A fee delegated transaction instance. + * @return AbstractFeeDelegatedTransaction + * @throws KASAPIException + * @throws IOException + */ + @Override + public AbstractFeeDelegatedTransaction signAsFeePayer(String feePayerAddress, AbstractFeeDelegatedTransaction feeDelegatedTransaction) throws IOException, KASAPIException { + try { + if(feePayerAddress == null) { + return signAsGlobalFeePayer(feeDelegatedTransaction); + } + + if(feeDelegatedTransaction.getFeePayer().equals("0x") || feeDelegatedTransaction.getFeePayer().equals(Utils.DEFAULT_ZERO_ADDRESS)) { + feeDelegatedTransaction.setFeePayer(feePayerAddress); + } + + if(!feeDelegatedTransaction.getFeePayer().toLowerCase().equals(feePayerAddress.toLowerCase())) { + throw new IllegalArgumentException("Fee payer address are not matched"); + } + + if(isWeightedMultiSigType(feeDelegatedTransaction, feePayerAddress)) { + throw new IllegalArgumentException("Not supported: Using multiple keys in an account is currently not supported."); + } + + feeDelegatedTransaction.fillTransaction(); + String rlp = feeDelegatedTransaction.getRLPEncoding(); + + FDUserProcessRLPRequest rlpRequest = new FDUserProcessRLPRequest(); + rlpRequest.setRlp(rlp); + rlpRequest.setFeePayer(feeDelegatedTransaction.getFeePayer()); + rlpRequest.setSubmit(false); + + if(feeDelegatedTransaction instanceof AbstractFeeDelegatedWithRatioTransaction) { + rlpRequest.setFeeRatio(((AbstractFeeDelegatedWithRatioTransaction) feeDelegatedTransaction).getFeeRatioInteger().longValue()); + } + + FDTransactionResult result = this.walletAPI.requestFDRawTransactionPaidByUser(rlpRequest); + + AbstractFeeDelegatedTransaction tx = (AbstractFeeDelegatedTransaction)TransactionDecoder.decode(result.getRlp()); + feeDelegatedTransaction.appendFeePayerSignatures(tx.getFeePayerSignatures()); + + return feeDelegatedTransaction; + } catch (ApiException e) { + throw new KASAPIException(e); + } + } + + /** + * Getter function for walletAPI + * @return Wallet + */ + public Wallet getWalletAPI() { + return walletAPI; + } + + /** + * Setter function for walletAPI + * @param walletAPI The WalletAPI instance to use KAS Wallet API. + */ + public void setWalletAPI(Wallet walletAPI) { + this.walletAPI = walletAPI; + } + + + private boolean isWeightedMultiSigType(AbstractTransaction transaction, String address) throws IOException { + AccountKey res = transaction.getKlaytnCall().getAccountKey(address).send(); + if(res == null) { + return false; + } + + AccountKey.AccountKeyData accountKeyData = res.getResult(); + String type = accountKeyData.getType(); + + if(type.equals(AccountKeyWeightedMultiSig.getType())) { + return true; + } + + if(type.equals(AccountKeyRoleBased.getType())) { + AccountKeyRoleBased roleBased = (AccountKeyRoleBased) accountKeyData.getAccountKey(); + + if(transaction instanceof AccountUpdate) { + if(roleBased.getRoleAccountUpdateKey() instanceof AccountKeyWeightedMultiSig) { + return true; + } + } else { + if(roleBased.getRoleTransactionKey() instanceof AccountKeyWeightedMultiSig) { + return true; + } + } + } + + return false; + } + + private boolean isWeightedMultiSigType(AbstractFeeDelegatedTransaction fdTransaction, String address) throws IOException { + AccountKey res = fdTransaction.getKlaytnCall().getAccountKey(address).send(); + if(res == null) { + return true; + } + AccountKey.AccountKeyData accountKeyData = res.getResult(); + + String type = accountKeyData.getType(); + + if(type.equals(AccountKeyWeightedMultiSig.getType())) { + return true; + } + + if(type.equals(AccountKeyRoleBased.getType())) { + AccountKeyRoleBased roleBased = (AccountKeyRoleBased) accountKeyData.getAccountKey(); + if(roleBased.getRoleFeePayerKey() instanceof AccountKeyWeightedMultiSig) { + return true; + } + } + + return false; + } + + private List makeSignature(AbstractTransaction transaction) throws IOException { + try { + //If transaction type is fee delegated type + if(transaction instanceof AbstractFeeDelegatedTransaction) { + return makeSignature((AbstractFeeDelegatedTransaction)transaction); + } + + transaction.fillTransaction(); + + ProcessRLPRequest request = new ProcessRLPRequest(); + + //If transaction type is LegacyTransaction, it must set a from field in ProcessRLPRequest. + if(transaction instanceof LegacyTransaction) { + request.setFrom(transaction.getFrom()); + } + + request.setRlp(transaction.getRLPEncoding()); + request.setSubmit(false); + + TransactionResult result = this.walletAPI.requestRawTransaction(request); + return convertSignatureData(result.getSignatures()); + } catch (ApiException e) { + throw new KASAPIException(e); + } + } + + + private List makeSignature(AbstractFeeDelegatedTransaction transaction) throws IOException, KASAPIException { + try { + transaction.fillTransaction(); + + FDProcessRLPRequest request = new FDProcessRLPRequest(); + + if (transaction instanceof AbstractFeeDelegatedWithRatioTransaction) { + request.setFeeRatio(((AbstractFeeDelegatedWithRatioTransaction) transaction).getFeeRatioInteger().longValue()); + } + + request.setRlp(transaction.getRLPEncoding()); + request.setSubmit(false); + + FDTransactionResult result = this.walletAPI.requestFDRawTransactionPaidByGlobalFeePayer(request); + return convertSignatureData(result.getSignatures()); + } catch (ApiException e) { + throw new KASAPIException(e); + } + } + + private List convertSignatureData(List signatureList) { + return signatureList.stream().map(signature -> new SignatureData(signature.getV(), signature.getR(), signature.getS())) + .collect(Collectors.toList()); + } +} diff --git a/src/test/java/xyz/groundx/caver_ext_kas/Config.java b/src/test/java/xyz/groundx/caver_ext_kas/Config.java index c1c62ef7..ee4c5072 100644 --- a/src/test/java/xyz/groundx/caver_ext_kas/Config.java +++ b/src/test/java/xyz/groundx/caver_ext_kas/Config.java @@ -16,21 +16,38 @@ package xyz.groundx.caver_ext_kas; +import com.klaytn.caver.Caver; +import com.klaytn.caver.abi.ABI; +import com.klaytn.caver.contract.ContractDeployParams; +import com.klaytn.caver.contract.SendOptions; +import com.klaytn.caver.kct.kip17.KIP17; +import com.klaytn.caver.kct.kip17.KIP17ConstantData; +import com.klaytn.caver.kct.kip17.KIP17DeployParams; +import com.klaytn.caver.kct.kip7.KIP7; +import com.klaytn.caver.kct.kip7.KIP7ConstantData; +import com.klaytn.caver.kct.kip7.KIP7DeployParams; import com.klaytn.caver.methods.response.Bytes32; import com.klaytn.caver.methods.response.Quantity; 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.SmartContractDeploy; +import com.klaytn.caver.transaction.type.SmartContractExecution; import com.klaytn.caver.transaction.type.ValueTransfer; +import com.klaytn.caver.utils.CodeFormat; import com.klaytn.caver.utils.Utils; +import com.klaytn.caver.wallet.KeyringContainer; import com.klaytn.caver.wallet.keyring.KeyringFactory; import com.klaytn.caver.wallet.keyring.SingleKeyring; +import com.squareup.okhttp.Credentials; import io.github.cdimascio.dotenv.Dotenv; import org.web3j.protocol.exceptions.TransactionException; +import org.web3j.protocol.http.HttpService; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Arrays; public class Config { public static final String URL_NODE_API = "https://node-api.klaytnapi.com/v1/klaytn"; @@ -48,10 +65,13 @@ public class Config { static String klayProviderPrivateKey = ""; - public static Integer presetID = null; + public static Integer presetID; + public static CaverExtKAS caver; + public static KeyringContainer keyringContainer; + public static SingleKeyring klayProviderKeyring; public static String loadEnvData(Dotenv env, String envName) { @@ -69,6 +89,19 @@ public static String loadEnvData(Dotenv env, String envName) { return data; } + public static void init() { + loadTestData(); + + caver = new CaverExtKAS(); + caver.initNodeAPI(CHAIN_ID_BAOBOB, accessKey, secretAccessKey, URL_NODE_API); + caver.initAnchorAPI(CHAIN_ID_BAOBOB, accessKey, secretAccessKey, URL_ANCHOR_API); + caver.initWalletAPI(CHAIN_ID_BAOBOB, accessKey, secretAccessKey, URL_WALLET_API); + caver.initTokenHistoryAPI(CHAIN_ID_BAOBOB, accessKey, secretAccessKey, URL_TH_API); + + keyringContainer = new KeyringContainer(); + klayProviderKeyring = (SingleKeyring)keyringContainer.add(KeyringFactory.createFromPrivateKey(klayProviderPrivateKey)); + } + public static void loadTestData() { Dotenv env = Dotenv.configure() .ignoreIfMalformed() @@ -84,18 +117,6 @@ public static void loadTestData() { presetID = presetID == null ? Integer.parseInt(loadEnvData(env, "PRESET")) : presetID; } - public static void init() { - loadTestData(); - - caver = new CaverExtKAS(); - caver.initNodeAPI(CHAIN_ID_BAOBOB, accessKey, secretAccessKey, URL_NODE_API); - caver.initAnchorAPI(CHAIN_ID_BAOBOB, accessKey, secretAccessKey, URL_ANCHOR_API); - caver.initWalletAPI(CHAIN_ID_BAOBOB, accessKey, secretAccessKey, URL_WALLET_API); - caver.initTokenHistoryAPI(CHAIN_ID_BAOBOB, accessKey, secretAccessKey, URL_TH_API); - - klayProviderKeyring = (SingleKeyring)caver.wallet.add(KeyringFactory.createFromPrivateKey(klayProviderPrivateKey)); - } - public static TransactionReceipt.TransactionReceiptData sendValue(String toAddress) throws IOException, TransactionException { init(); @@ -109,7 +130,7 @@ public static TransactionReceipt.TransactionReceiptData sendValue(String toAddre .setGas(BigInteger.valueOf(25000)) .build(); - caver.wallet.sign(klayProviderKeyring.getAddress(), valueTransfer); + keyringContainer.sign(klayProviderKeyring.getAddress(), valueTransfer); Bytes32 result = caver.rpc.klay.sendRawTransaction(valueTransfer.getRawTransaction()).send(); if(result.hasError()) { throw new RuntimeException(result.getError().getMessage()); @@ -135,6 +156,90 @@ public static BigInteger getBalance(String address) { return null; } + public static String deployKIP7(Caver caver, String deployer) { + try { + KIP7 kip7 = new KIP7(caver); + + BigInteger initialSupply = BigInteger.valueOf(100_000).multiply(BigInteger.TEN.pow(18)); // 100000 * 10^18 + ContractDeployParams contractDeployParams = new ContractDeployParams(KIP7ConstantData.BINARY, "TEST", "TES", 18, initialSupply); + + String input = ABI.encodeContractDeploy(kip7.getConstructor(), contractDeployParams.getBytecode(), contractDeployParams.getDeployParams()); + + SmartContractDeploy deployTx = new SmartContractDeploy.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(deployer) + .setInput(input) + .setCodeFormat(CodeFormat.EVM) + .setHumanReadable(false) + .setGas(BigInteger.valueOf(5500000)) + .build(); + + keyringContainer.sign(deployer, deployTx); + Bytes32 res = caver.rpc.klay.sendRawTransaction(deployTx).send(); + PollingTransactionReceiptProcessor processor = new PollingTransactionReceiptProcessor(caver, 1000, 15); + TransactionReceipt.TransactionReceiptData receiptData = processor.waitForTransactionReceipt(res.getResult()); + + return receiptData.getContractAddress(); + } catch (IOException | ReflectiveOperationException | TransactionException e) { + e.printStackTrace(); + } + + return null; + } + + public static String deployKIP17(Caver caver, String deployer) { + try { + String contractName = "TEST_KIP17"; + String contractSymbol = "KIP17"; + + KIP17 kip17 = new KIP17(caver); + ContractDeployParams contractDeployParams = new ContractDeployParams(KIP17ConstantData.BINARY, contractName, contractSymbol); + + String input = ABI.encodeContractDeploy(kip17.getConstructor(), contractDeployParams.getBytecode(), contractDeployParams.getDeployParams()); + + SmartContractDeploy deployTx = new SmartContractDeploy.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(deployer) + .setInput(input) + .setCodeFormat(CodeFormat.EVM) + .setHumanReadable(false) + .setGas(BigInteger.valueOf(5500000)) + .build(); + + keyringContainer.sign(deployer, deployTx); + Bytes32 res = caver.rpc.klay.sendRawTransaction(deployTx).send(); + PollingTransactionReceiptProcessor processor = new PollingTransactionReceiptProcessor(caver, 1000, 15); + TransactionReceipt.TransactionReceiptData receiptData = processor.waitForTransactionReceipt(res.getResult()); + + return receiptData.getContractAddress(); + } catch (IOException | ReflectiveOperationException | TransactionException e) { + e.printStackTrace(); + } + + return null; + } + + public static void mintKIP17Token(Caver caver, String contractAddress, String ownerAddress, BigInteger tokenId) throws Exception { + KIP17 kip17 = new KIP17(caver, contractAddress); + SendOptions sendOptions = new SendOptions(ownerAddress, BigInteger.valueOf(5500000)); + + String input = kip17.getMethod("mint").encodeABI(Arrays.asList(ownerAddress, tokenId)); + + SmartContractExecution smartContractExecution = new SmartContractExecution.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(ownerAddress) + .setTo(contractAddress) + .setInput(input) + .setGas(BigInteger.valueOf(5500000)) + .build(); + + keyringContainer.sign(ownerAddress, smartContractExecution); + Bytes32 res = caver.rpc.klay.sendRawTransaction(smartContractExecution).send(); + PollingTransactionReceiptProcessor processor = new PollingTransactionReceiptProcessor(caver, 1000, 15); + TransactionReceipt.TransactionReceiptData receiptData = processor.waitForTransactionReceipt(res.getResult()); + + } + public static CaverExtKAS getCaver() { return caver; } diff --git a/src/test/java/xyz/groundx/caver_ext_kas/exception/KASAPIExceptionTest.java b/src/test/java/xyz/groundx/caver_ext_kas/exception/KASAPIExceptionTest.java new file mode 100644 index 00000000..5653e660 --- /dev/null +++ b/src/test/java/xyz/groundx/caver_ext_kas/exception/KASAPIExceptionTest.java @@ -0,0 +1,33 @@ +package xyz.groundx.caver_ext_kas.exception; + +import org.junit.BeforeClass; +import org.junit.Test; +import xyz.groundx.caver_ext_kas.CaverExtKAS; +import xyz.groundx.caver_ext_kas.Config; + +import java.math.BigInteger; + +import static org.junit.Assert.assertEquals; + +public class KASAPIExceptionTest { + static CaverExtKAS caver; + + @BeforeClass + public static void init() { + Config.init(); + caver = Config.getCaver(); + caver.kas.wallet.getAccountApi().getApiClient().setDebugging(true); + } + + @Test + public void getAccountFail() { + try { + caver.wallet.getAccount("0xb2Fd3a28efC3226638B7f92D9b48C370588c49F2"); + } catch (KASAPIException e) { + assertEquals(400, e.getCode()); + assertEquals("Bad Request", e.getMessage()); + assertEquals(1061010, e.getResponseBody().getCode()); + assertEquals("data don't exist", e.getResponseBody().getMessage()); + } + } +} \ No newline at end of file diff --git a/src/test/java/xyz/groundx/caver_ext_kas/kas/tokenhistory/TokenHistoryAPITest.java b/src/test/java/xyz/groundx/caver_ext_kas/kas/tokenhistory/TokenHistoryAPITest.java index 75117f1e..d96d57e5 100644 --- a/src/test/java/xyz/groundx/caver_ext_kas/kas/tokenhistory/TokenHistoryAPITest.java +++ b/src/test/java/xyz/groundx/caver_ext_kas/kas/tokenhistory/TokenHistoryAPITest.java @@ -61,42 +61,17 @@ public class TokenHistoryAPITest { @BeforeClass - public static void init() throws NoSuchMethodException, TransactionException, IOException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException { + public static void init() throws Exception { Config.init(); caver = Config.getCaver(); preset = Config.getPresetID(); account = Config.getKlayProviderKeyring().getAddress(); - ftAddress = deployKIP7(caver, account); - nftAddress = deployKIP17(caver, account); - mintKIP17Token(caver, nftAddress, account, BigInteger.valueOf(2)); - caver.kas.tokenHistory.tokenApi.getApiClient().setDebugging(true); - } - - public static String deployKIP7(Caver caver, String deployer) throws IOException, NoSuchMethodException, InstantiationException, ClassNotFoundException, IllegalAccessException, InvocationTargetException, TransactionException { - BigInteger initialSupply = BigInteger.valueOf(100_000).multiply(BigInteger.TEN.pow(18)); // 100000 * 10^18 - KIP7DeployParams deployParams = new KIP7DeployParams("TEST", "TES", 18, initialSupply); - - return KIP7.deploy(caver, deployParams, deployer).getContractAddress(); - } - - public static String deployKIP17(Caver caver, String deployer) throws NoSuchMethodException, TransactionException, IOException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException { - String contractName = "TEST_KIP17"; - String contractSymbol = "KIP17"; - - KIP17 kip17 = new KIP17(caver); - KIP17DeployParams deployParams = new KIP17DeployParams(contractName, contractSymbol); - SendOptions sendOptions = new SendOptions(deployer, BigInteger.valueOf(5500000)); - - return KIP17.deploy(caver, deployParams, deployer).getContractAddress(); - } - - public static void mintKIP17Token(Caver caver, String contractAddress, String ownerAddress, BigInteger tokenId) throws NoSuchMethodException, TransactionException, IOException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException { - KIP17 kip17 = new KIP17(caver, contractAddress); - SendOptions sendOptions = new SendOptions(ownerAddress, BigInteger.valueOf(5500000)); - kip17.mint(ownerAddress, tokenId, sendOptions); + ftAddress = Config.deployKIP7(caver, account); + nftAddress = Config.deployKIP17(caver, account); + Config.mintKIP17Token(caver, nftAddress, account, BigInteger.valueOf(2)); } @Test diff --git a/src/test/java/xyz/groundx/caver_ext_kas/node/NodeAPITest.java b/src/test/java/xyz/groundx/caver_ext_kas/node/NodeAPITest.java index 004b2a2a..cf4bb113 100644 --- a/src/test/java/xyz/groundx/caver_ext_kas/node/NodeAPITest.java +++ b/src/test/java/xyz/groundx/caver_ext_kas/node/NodeAPITest.java @@ -18,6 +18,7 @@ import com.klaytn.caver.kct.kip17.KIP17; import com.klaytn.caver.kct.kip17.KIP17DeployParams; +import com.klaytn.caver.kct.kip7.KIP7; import com.klaytn.caver.methods.request.CallObject; import com.klaytn.caver.methods.request.KlayFilter; import com.klaytn.caver.methods.request.KlayLogFilter; @@ -25,6 +26,7 @@ import com.klaytn.caver.methods.response.*; import com.klaytn.caver.transaction.type.ValueTransfer; import com.klaytn.caver.utils.ChainId; +import com.klaytn.caver.wallet.KeyringContainer; import com.klaytn.caver.wallet.keyring.KeyringFactory; import com.klaytn.caver.wallet.keyring.SingleKeyring; import junit.framework.TestCase; @@ -51,12 +53,14 @@ public class NodeAPITest { static String account; - static KIP17 kip17Contract; + static String kip17Contract; static long blockNumber; static String blockHash = ""; static String transactionHash = ""; + static KeyringContainer container; + @BeforeClass public static void init() throws Exception { Config.init(); @@ -68,14 +72,13 @@ public static void init() throws Exception { blockHash = receipt.getBlockHash(); transactionHash = receipt.getTransactionHash(); blockNumber = Numeric.toBigInt(receipt.getBlockNumber()).longValue(); + deployContract(); } public static void deployContract() throws Exception { - KIP17DeployParams deployParams = new KIP17DeployParams("KIP17", "KIP17"); - kip17Contract = KIP17.deploy(caver, deployParams, account); - - System.out.println("Contract address : " + kip17Contract.getContractAddress()); + kip17Contract = Config.deployKIP17(caver, account); + System.out.println("Contract address : " + kip17Contract); } @@ -139,7 +142,7 @@ public void getBalanceTest() { @Test public void getCodeTest() { try { - Bytes res = caver.rpc.klay.getCode(kip17Contract.getContractAddress()).send(); + Bytes res = caver.rpc.klay.getCode(kip17Contract).send(); if(res.hasError()) { throw new IOException(res.getError().getMessage()); @@ -163,7 +166,7 @@ public void getTransactionCountTest() throws IOException { @Test public void isContractAccountTest() { try { - Boolean result = caver.rpc.klay.isContractAccount(kip17Contract.getContractAddress()).send(); + Boolean result = caver.rpc.klay.isContractAccount(kip17Contract).send(); if(result.hasError()) { throw new IOException(result.getError().getMessage()); } @@ -325,11 +328,12 @@ public void isSyncingTest() throws Exception { @Test public void callTest() { try { - String encoded = kip17Contract.getMethod("symbol").encodeABI(Collections.emptyList()); + KIP17 kip17 = new KIP17(caver, kip17Contract); + String encoded = kip17.getMethod("symbol").encodeABI(Collections.emptyList()); CallObject callObject = CallObject.createCallObject( account, - kip17Contract.getContractAddress(), + kip17Contract, new BigInteger("100000000", 16), new BigInteger("5d21dba00", 16), new BigInteger("0", 16), @@ -347,11 +351,12 @@ public void callTest() { @Test public void estimateGasTest() throws Exception { - String encoded = kip17Contract.getMethod("pause").encodeABI(Collections.emptyList()); + KIP17 kip17 = new KIP17(caver, kip17Contract); + String encoded = kip17.getMethod("pause").encodeABI(Collections.emptyList()); CallObject callObject = CallObject.createCallObject( account, - kip17Contract.getContractAddress(), + kip17.getContractAddress(), new BigInteger("100000000", 16), new BigInteger("5d21dba00", 16), new BigInteger("0", 16), @@ -364,11 +369,12 @@ public void estimateGasTest() throws Exception { @Test public void estimateComputationCostTest() throws Exception { - String encoded = kip17Contract.getMethod("pause").encodeABI(Collections.emptyList()); + KIP17 kip17 = new KIP17(caver, kip17Contract); + String encoded = kip17.getMethod("pause").encodeABI(Collections.emptyList()); CallObject callObject = CallObject.createCallObject( account, - kip17Contract.getContractAddress(), + kip17.getContractAddress(), new BigInteger("100000000", 16), new BigInteger("5d21dba00", 16), new BigInteger("0", 16), @@ -417,10 +423,9 @@ public void sendRawTransactionTest() throws Exception { .setGas(BigInteger.valueOf(25000)) .build(); - caver.wallet.sign(account, valueTransfer); - + Config.keyringContainer.sign(account, valueTransfer); Bytes32 txHash = caver.rpc.klay.sendRawTransaction(valueTransfer).send(); - TestCase.assertNotNull(txHash); + assertNotNull(txHash); } diff --git a/src/test/java/xyz/groundx/caver_ext_kas/wallet/ContractSampleData.java b/src/test/java/xyz/groundx/caver_ext_kas/wallet/ContractSampleData.java new file mode 100644 index 00000000..66eda59e --- /dev/null +++ b/src/test/java/xyz/groundx/caver_ext_kas/wallet/ContractSampleData.java @@ -0,0 +1,216 @@ +package xyz.groundx.caver_ext_kas.wallet; + +import com.klaytn.caver.Caver; +import com.klaytn.caver.abi.ABI; +import com.klaytn.caver.contract.Contract; +import com.klaytn.caver.contract.SendOptions; +import com.klaytn.caver.methods.response.TransactionReceipt; +import org.web3j.protocol.exceptions.TransactionException; +import xyz.groundx.caver_ext_kas.exception.KASAPIException; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; +import java.util.Arrays; + +import static org.junit.Assert.fail; + +/** + * pragma solidity ^0.5.6; + * + * contract KVStore { + * mapping(string => string) storeString; + * mapping(string => uint256) storeUint; + * string symbol; + * + * constructor(string memory _symbol) public { + * symbol = _symbol; + * } + * + * function getSymbol() public view returns (string memory) { + * return symbol; + * } + * + * function getString(string memory key) public view returns (string memory) { + * return storeString[key]; + * } + * + * function setString(string memory key, string memory value) public { + * storeString[key] = value; + * } + * + * function getUint(string memory key) public view returns (uint256) { + * return storeUint[key]; + * } + * + * function setUint(string memory key, uint256 value) public { + * storeUint[key] = value; + * } + * } + */ +public class ContractSampleData { + static final String FUNC_SET_STRING = "setString"; + static final String FUNC_SET_UINT = "setUint"; + static final String FUNC_GET_STRING = "getString"; + static final String FUNC_GET_UINT = "getUint"; + static final String FUNC_GET_SYMBOL = "getSymbol"; + + static final String BINARY = "608060405234801561001057600080fd5b50604051610a35380380610a358339810180604052602081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8281019050602081018481111561006157600080fd5b815185600182028301116401000000008211171561007e57600080fd5b5050929190505050806002908051906020019061009c9291906100a3565b5050610148565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100e457805160ff1916838001178555610112565b82800160010185558215610112579182015b828111156101115782518255916020019190600101906100f6565b5b50905061011f9190610123565b5090565b61014591905b80821115610141576000816000905550600101610129565b5090565b90565b6108de806101576000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063150704011461005c578063498bff00146100df57806356523acd146101ae5780636e1a1336146102735780639c981fcb146103c5575b600080fd5b6100646104f9565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a4578082015181840152602081019050610089565b50505050905090810190601f1680156100d15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b610198600480360360208110156100f557600080fd5b810190808035906020019064010000000081111561011257600080fd5b82018360208201111561012457600080fd5b8035906020019184600183028401116401000000008311171561014657600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f82011690508083019250505050505050919291929050505061059b565b6040518082815260200191505060405180910390f35b610271600480360360408110156101c457600080fd5b81019080803590602001906401000000008111156101e157600080fd5b8201836020820111156101f357600080fd5b8035906020019184600183028401116401000000008311171561021557600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019092919050505061060e565b005b6103c36004803603604081101561028957600080fd5b81019080803590602001906401000000008111156102a657600080fd5b8201836020820111156102b857600080fd5b803590602001918460018302840111640100000000831117156102da57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505091929192908035906020019064010000000081111561033d57600080fd5b82018360208201111561034f57600080fd5b8035906020019184600183028401116401000000008311171561037157600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610680565b005b61047e600480360360208110156103db57600080fd5b81019080803590602001906401000000008111156103f857600080fd5b82018360208201111561040a57600080fd5b8035906020019184600183028401116401000000008311171561042c57600080fd5b91908080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f820116905080830192505050505050509192919290505050610702565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104be5780820151818401526020810190506104a3565b50505050905090810190601f1680156104eb5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b606060028054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105915780601f1061056657610100808354040283529160200191610591565b820191906000526020600020905b81548152906001019060200180831161057457829003601f168201915b5050505050905090565b60006001826040518082805190602001908083835b602083106105d357805182526020820191506020810190506020830392506105b0565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020549050919050565b806001836040518082805190602001908083835b602083106106455780518252602082019150602081019050602083039250610622565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020819055505050565b806000836040518082805190602001908083835b602083106106b75780518252602082019150602081019050602083039250610694565b6001836020036101000a038019825116818451168082178552505050505050905001915050908152602001604051809103902090805190602001906106fd92919061080d565b505050565b60606000826040518082805190602001908083835b6020831061073a5780518252602082019150602081019050602083039250610717565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156108015780601f106107d657610100808354040283529160200191610801565b820191906000526020600020905b8154815290600101906020018083116107e457829003601f168201915b50505050509050919050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061084e57805160ff191683800117855561087c565b8280016001018555821561087c579182015b8281111561087b578251825591602001919060010190610860565b5b509050610889919061088d565b5090565b6108af91905b808211156108ab576000816000905550600101610893565b5090565b9056fea165627a7a723058207bffe25f9ec017dbe9a4f191bafd2f13252aa3e30263de201ea5fe4cc11542ed0029"; + static final String ABI = "[\n" + + " {\n" + + " \"constant\": true,\n" + + " \"inputs\": [],\n" + + " \"name\": \"getSymbol\",\n" + + " \"outputs\": [\n" + + " {\n" + + " \"name\": \"\",\n" + + " \"type\": \"string\"\n" + + " }\n" + + " ],\n" + + " \"payable\": false,\n" + + " \"stateMutability\": \"view\",\n" + + " \"type\": \"function\"\n" + + " },\n" + + " {\n" + + " \"constant\": true,\n" + + " \"inputs\": [\n" + + " {\n" + + " \"name\": \"key\",\n" + + " \"type\": \"string\"\n" + + " }\n" + + " ],\n" + + " \"name\": \"getUint\",\n" + + " \"outputs\": [\n" + + " {\n" + + " \"name\": \"\",\n" + + " \"type\": \"uint256\"\n" + + " }\n" + + " ],\n" + + " \"payable\": false,\n" + + " \"stateMutability\": \"view\",\n" + + " \"type\": \"function\"\n" + + " },\n" + + " {\n" + + " \"constant\": false,\n" + + " \"inputs\": [\n" + + " {\n" + + " \"name\": \"key\",\n" + + " \"type\": \"string\"\n" + + " },\n" + + " {\n" + + " \"name\": \"value\",\n" + + " \"type\": \"uint256\"\n" + + " }\n" + + " ],\n" + + " \"name\": \"setUint\",\n" + + " \"outputs\": [],\n" + + " \"payable\": false,\n" + + " \"stateMutability\": \"nonpayable\",\n" + + " \"type\": \"function\"\n" + + " },\n" + + " {\n" + + " \"constant\": false,\n" + + " \"inputs\": [\n" + + " {\n" + + " \"name\": \"key\",\n" + + " \"type\": \"string\"\n" + + " },\n" + + " {\n" + + " \"name\": \"value\",\n" + + " \"type\": \"string\"\n" + + " }\n" + + " ],\n" + + " \"name\": \"setString\",\n" + + " \"outputs\": [],\n" + + " \"payable\": false,\n" + + " \"stateMutability\": \"nonpayable\",\n" + + " \"type\": \"function\"\n" + + " },\n" + + " {\n" + + " \"constant\": true,\n" + + " \"inputs\": [\n" + + " {\n" + + " \"name\": \"key\",\n" + + " \"type\": \"string\"\n" + + " }\n" + + " ],\n" + + " \"name\": \"getString\",\n" + + " \"outputs\": [\n" + + " {\n" + + " \"name\": \"\",\n" + + " \"type\": \"string\"\n" + + " }\n" + + " ],\n" + + " \"payable\": false,\n" + + " \"stateMutability\": \"view\",\n" + + " \"type\": \"function\"\n" + + " },\n" + + " {\n" + + " \"inputs\": [\n" + + " {\n" + + " \"name\": \"_symbol\",\n" + + " \"type\": \"string\"\n" + + " }\n" + + " ],\n" + + " \"payable\": false,\n" + + " \"stateMutability\": \"nonpayable\",\n" + + " \"type\": \"constructor\"\n" + + " }\n" + + "]"; + + public static String encodeConstructor(Caver caver) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { + Contract contract = new Contract(caver, ContractSampleData.ABI); + String input = com.klaytn.caver.abi.ABI.encodeContractDeploy(contract.getConstructor(), ContractSampleData.BINARY, Arrays.asList("TCK")); + + return input; + } + + public static String encodeABI(Caver caver) throws IOException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { + Contract contract = new Contract(caver, ContractSampleData.ABI); + String input = com.klaytn.caver.abi.ABI.encodeFunctionCall(contract.getMethod(FUNC_SET_STRING), Arrays.asList("KEY", "VALUE")); + + return input; + } + + public static TransactionReceipt.TransactionReceiptData storeStringData(Contract contract, String executor, String key, String value) { + try { + SendOptions sendOptions = new SendOptions(executor, BigInteger.valueOf(500000)); + TransactionReceipt.TransactionReceiptData receiptData = contract.send(sendOptions, ContractSampleData.FUNC_SET_STRING, key, value); + + if(!receiptData.getStatus().equals("0x1")) { + fail(); + } + + return receiptData; + } catch (ReflectiveOperationException | IOException | TransactionException e) { + e.printStackTrace(); + fail(); + } catch (KASAPIException e) { + e.printStackTrace(); + fail(e.getResponseBody().getCode() + " " + e.getResponseBody().getMessage()); + } + + return null; + } + + public static TransactionReceipt.TransactionReceiptData storeUintData(Contract contract, String executor, String key, int value) { + try { + SendOptions sendOptions = new SendOptions(executor, BigInteger.valueOf(500000)); + TransactionReceipt.TransactionReceiptData receiptData = contract.send(sendOptions, ContractSampleData.FUNC_SET_UINT, key, value); + + if(!receiptData.getStatus().equals("0x1")) { + fail(); + } + + return receiptData; + } catch (ReflectiveOperationException | IOException | TransactionException e) { + e.printStackTrace(); + fail(); + } catch (KASAPIException e) { + e.printStackTrace(); + fail(e.getResponseBody().getCode() + " " + e.getResponseBody().getMessage()); + } + + return null; + } +} diff --git a/src/test/java/xyz/groundx/caver_ext_kas/wallet/KASWalletIntegrationTest.java b/src/test/java/xyz/groundx/caver_ext_kas/wallet/KASWalletIntegrationTest.java new file mode 100644 index 00000000..dda35955 --- /dev/null +++ b/src/test/java/xyz/groundx/caver_ext_kas/wallet/KASWalletIntegrationTest.java @@ -0,0 +1,2500 @@ +/* + * Copyright 2020 The caver-java-ext-kas Authors + * + * Licensed under the Apache License, Version 2.0 (the “License”); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an “AS IS” BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package xyz.groundx.caver_ext_kas.wallet; + +import com.klaytn.caver.Caver; +import com.klaytn.caver.contract.Contract; +import com.klaytn.caver.contract.ContractDeployParams; +import com.klaytn.caver.contract.SendOptions; +import com.klaytn.caver.kct.kip17.KIP17; +import com.klaytn.caver.kct.kip7.KIP7; +import com.klaytn.caver.methods.response.TransactionReceipt; +import com.klaytn.caver.transaction.type.*; +import com.klaytn.caver.utils.Utils; +import com.klaytn.caver.wallet.keyring.SignatureData; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.web3j.abi.datatypes.Type; +import org.web3j.abi.datatypes.Utf8String; +import org.web3j.abi.datatypes.generated.Uint256; +import org.web3j.protocol.exceptions.TransactionException; +import org.web3j.utils.Numeric; +import xyz.groundx.caver_ext_kas.CaverExtKAS; +import xyz.groundx.caver_ext_kas.Config; +import xyz.groundx.caver_ext_kas.exception.KASAPIException; +import xyz.groundx.caver_ext_kas.rest_client.io.swagger.client.api.wallet.model.Account; +import xyz.groundx.caver_ext_kas.rest_client.io.swagger.client.api.wallet.model.AccountSummary; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import static org.junit.Assert.*; + +public class KASWalletIntegrationTest { + + public static class generateTest { + static CaverExtKAS caver; + + @BeforeClass + public static void init() throws IOException, TransactionException { + Config.init(); + caver = Config.getCaver(); + caver.kas.wallet.getAccountApi().getApiClient().setDebugging(true); + } + + @Test + public void generateTest() { + List addressList = caver.wallet.generate(3); + assertEquals(3, addressList.size()); + + for (int i = 0; i < addressList.size(); i++) { + caver.wallet.remove(addressList.get(i)); + } + } + } + + + public static class getAccountTest { + static CaverExtKAS caver; + + @BeforeClass + public static void init() throws IOException, TransactionException { + Config.init(); + caver = Config.getCaver(); + caver.kas.wallet.getAccountApi().getApiClient().setDebugging(true); + } + + @Test + public void getAccountTest() { + List addressList = caver.wallet.generate(1); + + Account account = caver.wallet.getAccount(addressList.get(0)); + assertNotNull(account); + } + + @Test + public void getAccountThrow_ExceptionTest() { + try { + String unKnownAddress = "0x785ba1146cc1bed97b9c8d73e9293cc3b6bc3691"; + Account account = caver.wallet.getAccount(unKnownAddress); + } catch (KASAPIException e) { + assertEquals(400, e.getCode()); + assertEquals("Bad Request", e.getMessage()); + assertEquals(1061010, e.getResponseBody().getCode()); + assertEquals("data don't exist", e.getResponseBody().getMessage()); + } + } + } + + public static class removeTest { + static CaverExtKAS caver; + + @BeforeClass + public static void init() throws IOException, TransactionException { + Config.init(); + caver = Config.getCaver(); + caver.kas.wallet.getAccountApi().getApiClient().setDebugging(true); + } + + @Test + public void removeTest() { + List addressList = caver.wallet.generate(1); + + boolean isRemoved = caver.wallet.remove(addressList.get(0)); + assertTrue(isRemoved); + } + } + + public static class isExistedTest { + static CaverExtKAS caver; + + @BeforeClass + public static void init() throws IOException, TransactionException { + Config.init(); + caver = Config.getCaver(); + caver.kas.wallet.getAccountApi().getApiClient().setDebugging(true); + } + + @Test + public void isExistedTest() { + List addressList = caver.wallet.generate(1); + boolean isExisted = caver.wallet.isExisted(addressList.get(0)); + + assertTrue(isExisted); + + caver.wallet.remove(addressList.get(0)); + } + + @Test + public void isExistedFailTest() { + String unKnownAddress = "0x785ba1146cc1bed97b9c8d73e9293cc3b6bc3691"; + boolean isExisted = caver.wallet.isExisted(unKnownAddress); + + assertFalse(isExisted); + } + } + + public static class enableAccountTest { + static CaverExtKAS caver; + + @BeforeClass + public static void init() throws IOException, TransactionException { + Config.init(); + caver = Config.getCaver(); + caver.kas.wallet.getAccountApi().getApiClient().setDebugging(true); + } + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void enableAccountTest() { + try { + List addressList = caver.wallet.generate(1); + caver.wallet.disableAccount(addressList.get(0)); + + AccountSummary summary = caver.wallet.enableAccount(addressList.get(0)); + assertEquals(addressList.get(0), summary.getAddress()); + } catch (KASAPIException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void enableAccount_ThrowExceptionTest() { + expectedException.expect(KASAPIException.class); + + List addressList = caver.wallet.generate(1); + caver.wallet.enableAccount(addressList.get(0)); + } + } + + public static class disableAccountTest { + static CaverExtKAS caver; + + @BeforeClass + public static void init() throws IOException, TransactionException { + Config.init(); + caver = Config.getCaver(); + caver.kas.wallet.getAccountApi().getApiClient().setDebugging(true); + } + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Test + public void disableAccountTest() { + List addressList = caver.wallet.generate(1); + AccountSummary accountSummary = caver.wallet.disableAccount(addressList.get(0)); + assertEquals(addressList.get(0), accountSummary.getAddress()); + } + + @Test + public void disableAccount_ThrowExceptionTest() { + expectedException.expect(KASAPIException.class); + List addressList = caver.wallet.generate(1); + caver.wallet.remove(addressList.get(0)); + caver.wallet.disableAccount(addressList.get(0)); + } + } + + public static class signTest { + static CaverExtKAS caver; + static String baseAccount; + static String toAccount; + + @BeforeClass + public static void init() throws IOException, TransactionException { + Config.init(); + caver = Config.getCaver(); + List addressList = caver.wallet.generate(2); + baseAccount = addressList.get(0); + toAccount = addressList.get(1); + + Config.sendValue(baseAccount); + caver.kas.wallet.getAccountApi().getApiClient().setDebugging(true); + } + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + public SignatureData getSignatureData() { + return new SignatureData( + "0x07f5", + "0xb99eefa471f4ff2a6be78c9f66d512a286084c73c07bfd81e8c4c056b31e003b", + "0x1b1e3b2b0e74fe1ce44e94c7485cc5e24f852cb0daf52c85a128a77bdd579310" + ); + } + + @Test + public void legacyTx() { + LegacyTransaction tx = new LegacyTransaction.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setGas(BigInteger.valueOf(50000)) + .build(); + try { + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void legacyTx_ThrowException_alreadyExistedSignature() { + expectedException.expect(RuntimeException.class); + expectedException.expectMessage("Signatures already defined.TxTypeLegacyTransaction cannot include more than one signature."); + + SignatureData signatureData = getSignatureData(); + + LegacyTransaction tx = new LegacyTransaction.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setGas(BigInteger.valueOf(50000)) + .setSignatures(signatureData) + .build(); + + try { + caver.wallet.sign(baseAccount, tx); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void valueTransferTx() { + ValueTransfer tx = new ValueTransfer.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setGas(BigInteger.valueOf(50000)) + .build(); + + try { + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void valueTransferTx_appendSignature() { + SignatureData signatureData = getSignatureData(); + + ValueTransfer tx = new ValueTransfer.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setGas(BigInteger.valueOf(50000)) + .setSignatures(signatureData) + .build(); + + try { + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void valueTransferMemoTx() { + String input = Numeric.toHexString("hello".getBytes(StandardCharsets.UTF_8)); + + ValueTransferMemo tx = new ValueTransferMemo.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .build(); + + try { + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void valueTransferMemoTx_appendSignature() { + SignatureData signatureData = getSignatureData(); + String input = Numeric.toHexString("hello".getBytes(StandardCharsets.UTF_8)); + + ValueTransferMemo tx = new ValueTransferMemo.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setSignatures(signatureData) + .build(); + + try { + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void smartContractDeploy() { + try { + String input = ContractSampleData.encodeConstructor(caver); + + SmartContractDeploy tx = new SmartContractDeploy.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void smartContractDeploy_appendSignature() { + try { + String input = ContractSampleData.encodeConstructor(caver); + SignatureData signatureData = getSignatureData(); + + SmartContractDeploy tx = new SmartContractDeploy.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setSignatures(signatureData) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void smartContractExecution() { + try { + String input = ContractSampleData.encodeABI(caver); + SmartContractExecution tx = new SmartContractExecution.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void smartContractExecution_appendSignature() { + try { + String input = ContractSampleData.encodeABI(caver); + SmartContractExecution tx = new SmartContractExecution.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setSignatures(getSignatureData()) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void accountUpdate() { + try { + AccountUpdate tx = new AccountUpdate.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setAccount(com.klaytn.caver.account.Account.createWithAccountKeyLegacy(baseAccount)) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void accountUpdate_appendSignature() { + try { + AccountUpdate tx = new AccountUpdate.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setAccount(com.klaytn.caver.account.Account.createWithAccountKeyLegacy(baseAccount)) + .setSignatures(getSignatureData()) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void cancel() { + try { + Cancel tx = new Cancel.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setNonce(BigInteger.valueOf(1)) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void cancel_appendSignature() { + try { + Cancel tx = new Cancel.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setNonce(BigInteger.valueOf(1)) + .setSignatures(getSignatureData()) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void chainDataAnchoring() { + try { + ChainDataAnchoring tx = new ChainDataAnchoring.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setInput("0xf8a6a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000405") + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void chainDataAnchoring_appendSignature() { + try { + ChainDataAnchoring tx = new ChainDataAnchoring.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setInput("0xf8a6a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000405") + .setSignatures(getSignatureData()) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferTx() { + FeeDelegatedValueTransfer tx = new FeeDelegatedValueTransfer.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setGas(BigInteger.valueOf(50000)) + .build(); + + try { + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferTx_appendSignature() { + SignatureData signatureData = getSignatureData(); + + FeeDelegatedValueTransfer tx = new FeeDelegatedValueTransfer.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setGas(BigInteger.valueOf(50000)) + .setSignatures(signatureData) + .build(); + + try { + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferMemoTx() { + String input = Numeric.toHexString("hello".getBytes(StandardCharsets.UTF_8)); + + FeeDelegatedValueTransferMemo tx = new FeeDelegatedValueTransferMemo.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .build(); + + try { + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferMemoTx_appendSignature() { + SignatureData signatureData = getSignatureData(); + String input = Numeric.toHexString("hello".getBytes(StandardCharsets.UTF_8)); + + FeeDelegatedValueTransferMemo tx = new FeeDelegatedValueTransferMemo.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setSignatures(signatureData) + .build(); + + try { + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractDeploy() { + try { + String input = ContractSampleData.encodeConstructor(caver); + + FeeDelegatedSmartContractDeploy tx = new FeeDelegatedSmartContractDeploy.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractDeploy_appendSignature() { + try { + String input = ContractSampleData.encodeConstructor(caver); + SignatureData signatureData = getSignatureData(); + + FeeDelegatedSmartContractDeploy tx = new FeeDelegatedSmartContractDeploy.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setSignatures(signatureData) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractExecution() { + try { + String input = ContractSampleData.encodeABI(caver); + FeeDelegatedSmartContractExecution tx = new FeeDelegatedSmartContractExecution.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractExecution_appendSignature() { + try { + String input = ContractSampleData.encodeABI(caver); + FeeDelegatedSmartContractExecution tx = new FeeDelegatedSmartContractExecution.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setSignatures(getSignatureData()) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedAccountUpdate() { + try { + FeeDelegatedAccountUpdate tx = new FeeDelegatedAccountUpdate.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setAccount(com.klaytn.caver.account.Account.createWithAccountKeyLegacy(baseAccount)) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedAccountUpdate_appendSignature() { + try { + FeeDelegatedAccountUpdate tx = new FeeDelegatedAccountUpdate.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setAccount(com.klaytn.caver.account.Account.createWithAccountKeyLegacy(baseAccount)) + .setSignatures(getSignatureData()) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedCancel() { + try { + FeeDelegatedCancel tx = new FeeDelegatedCancel.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setNonce(BigInteger.valueOf(1)) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedCancel_appendSignature() { + try { + FeeDelegatedCancel tx = new FeeDelegatedCancel.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setNonce(BigInteger.valueOf(1)) + .setSignatures(getSignatureData()) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedChainDataAnchoring() { + try { + FeeDelegatedChainDataAnchoring tx = new FeeDelegatedChainDataAnchoring.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setInput("0xf8a6a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000405") + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedChainDataAnchoring_appendSignature() { + try { + FeeDelegatedChainDataAnchoring tx = new FeeDelegatedChainDataAnchoring.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setInput("0xf8a6a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000405") + .setSignatures(getSignatureData()) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferWithRatioTx() { + FeeDelegatedValueTransferWithRatio tx = new FeeDelegatedValueTransferWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setGas(BigInteger.valueOf(50000)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + try { + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferWithRatioTx_appendSignature() { + SignatureData signatureData = getSignatureData(); + + FeeDelegatedValueTransferWithRatio tx = new FeeDelegatedValueTransferWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setGas(BigInteger.valueOf(50000)) + .setSignatures(signatureData) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + try { + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferMemoWithRatioTx() { + String input = Numeric.toHexString("hello".getBytes(StandardCharsets.UTF_8)); + + FeeDelegatedValueTransferMemoWithRatio tx = new FeeDelegatedValueTransferMemoWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + try { + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferMemoWithRatioTx_appendSignature() { + SignatureData signatureData = getSignatureData(); + String input = Numeric.toHexString("hello".getBytes(StandardCharsets.UTF_8)); + + FeeDelegatedValueTransferMemoWithRatio tx = new FeeDelegatedValueTransferMemoWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setSignatures(signatureData) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + try { + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractDeployWithRatio() { + try { + String input = ContractSampleData.encodeConstructor(caver); + + FeeDelegatedSmartContractDeployWithRatio tx = new FeeDelegatedSmartContractDeployWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractDeployWithRatio_appendSignature() { + try { + String input = ContractSampleData.encodeConstructor(caver); + SignatureData signatureData = getSignatureData(); + + FeeDelegatedSmartContractDeployWithRatio tx = new FeeDelegatedSmartContractDeployWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setSignatures(signatureData) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractExecutionWithRatio() { + try { + String input = ContractSampleData.encodeABI(caver); + FeeDelegatedSmartContractExecutionWithRatio tx = new FeeDelegatedSmartContractExecutionWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractExecutionWithRatio_appendSignature() { + try { + String input = ContractSampleData.encodeABI(caver); + FeeDelegatedSmartContractExecutionWithRatio tx = new FeeDelegatedSmartContractExecutionWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setSignatures(getSignatureData()) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + +// @Test +// public void feeDelegatedAccountUpdateWithRatio() { +// try { +// FeeDelegatedAccountUpdateWithRatio tx = new FeeDelegatedAccountUpdateWithRatio.Builder() +// .setKlaytnCall(caver.rpc.klay) +// .setFrom(baseAccount) +// .setGas(BigInteger.valueOf(50000)) +// .setAccount(com.klaytn.caver.account.Account.createWithAccountKeyLegacy(baseAccount)) +// .setFeeRatio(BigInteger.valueOf(1)) +// .build(); +// +// caver.wallet.sign(baseAccount, tx); +// assertEquals(1, tx.getSignatures().size()); +// } catch (IOException e) { +// e.printStackTrace(); +// fail(); +// } +// } + +// @Test +// public void feeDelegatedAccountUpdateWithRatio_appendSignature() { +// try { +// FeeDelegatedAccountUpdateWithRatio tx = new FeeDelegatedAccountUpdateWithRatio.Builder() +// .setKlaytnCall(caver.rpc.klay) +// .setFrom(baseAccount) +// .setGas(BigInteger.valueOf(50000)) +// .setAccount(com.klaytn.caver.account.Account.createWithAccountKeyLegacy(baseAccount)) +// .setSignatures(getSignatureData()) +// .setFeeRatio(BigInteger.valueOf(1)) +// .build(); +// +// caver.wallet.sign(baseAccount, tx); +// assertEquals(2, tx.getSignatures().size()); +// } catch (IOException e) { +// e.printStackTrace(); +// fail(); +// } +// } + + @Test + public void feeDelegatedCancelWithRatio() { + try { + FeeDelegatedCancelWithRatio tx = new FeeDelegatedCancelWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setNonce(BigInteger.valueOf(1)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedCancelWithRatio_appendSignature() { + try { + FeeDelegatedCancelWithRatio tx = new FeeDelegatedCancelWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setNonce(BigInteger.valueOf(1)) + .setSignatures(getSignatureData()) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedChainDataAnchoringWithRatio() { + try { + FeeDelegatedChainDataAnchoringWithRatio tx = new FeeDelegatedChainDataAnchoringWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setInput("0xf8a6a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000405") + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(1, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedChainDataAnchoringWithRatio_appendSignature() { + try { + FeeDelegatedChainDataAnchoringWithRatio tx = new FeeDelegatedChainDataAnchoringWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setInput("0xf8a6a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000405") + .setFeeRatio(BigInteger.valueOf(1)) + .setSignatures(getSignatureData()) + .build(); + + caver.wallet.sign(baseAccount, tx); + assertEquals(2, tx.getSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + } + + public static class signAsFeePayerTest { + static CaverExtKAS caver; + static String baseAccount; + static String toAccount; + static String userFeePayer; + + @BeforeClass + public static void init() throws IOException, TransactionException { + Config.init(); + caver = Config.getCaver(); + + List addressList = caver.wallet.generate(2); + baseAccount = addressList.get(0); + toAccount = addressList.get(1); + userFeePayer = Config.getFeePayerAddress(); + + Config.sendValue(baseAccount); + Config.sendValue(userFeePayer); + caver.kas.wallet.getAccountApi().getApiClient().setDebugging(true); + } + + public SignatureData getSignatureData() { + return new SignatureData( + "0x07f5", + "0xb99eefa471f4ff2a6be78c9f66d512a286084c73c07bfd81e8c4c056b31e003b", + "0x1b1e3b2b0e74fe1ce44e94c7485cc5e24f852cb0daf52c85a128a77bdd579310" + ); + } + + @Test + public void feeDelegatedValueTransferTx() { + FeeDelegatedValueTransfer tx = new FeeDelegatedValueTransfer.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setGas(BigInteger.valueOf(50000)) + .build(); + + try { + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures().get(0))); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferTx_appendSignature() { + SignatureData signatureData = getSignatureData(); + + FeeDelegatedValueTransfer tx = new FeeDelegatedValueTransfer.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setGas(BigInteger.valueOf(50000)) + .setFeePayer(userFeePayer) + .setFeePayerSignatures(signatureData) + .build(); + + try { + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(2, tx.getFeePayerSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferMemoTx() { + String input = Numeric.toHexString("hello".getBytes(StandardCharsets.UTF_8)); + + FeeDelegatedValueTransferMemo tx = new FeeDelegatedValueTransferMemo.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .build(); + + try { + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferMemoTx_appendSignature() { + SignatureData signatureData = getSignatureData(); + String input = Numeric.toHexString("hello".getBytes(StandardCharsets.UTF_8)); + + FeeDelegatedValueTransferMemo tx = new FeeDelegatedValueTransferMemo.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setFeePayer(userFeePayer) + .setFeePayerSignatures(signatureData) + .build(); + + try { + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(2, tx.getFeePayerSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractDeploy() { + try { + String input = ContractSampleData.encodeConstructor(caver); + + FeeDelegatedSmartContractDeploy tx = new FeeDelegatedSmartContractDeploy.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractDeploy_appendSignature() { + try { + String input = ContractSampleData.encodeConstructor(caver); + SignatureData signatureData = getSignatureData(); + + FeeDelegatedSmartContractDeploy tx = new FeeDelegatedSmartContractDeploy.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setFeePayer(userFeePayer) + .setFeePayerSignatures(signatureData) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(2, tx.getFeePayerSignatures().size()); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractExecution() { + try { + String input = ContractSampleData.encodeABI(caver); + FeeDelegatedSmartContractExecution tx = new FeeDelegatedSmartContractExecution.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractExecution_appendSignature() { + try { + String input = ContractSampleData.encodeABI(caver); + FeeDelegatedSmartContractExecution tx = new FeeDelegatedSmartContractExecution.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setFeePayer(userFeePayer) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setFeePayerSignatures(getSignatureData()) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(2, tx.getFeePayerSignatures().size()); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedAccountUpdate() { + try { + FeeDelegatedAccountUpdate tx = new FeeDelegatedAccountUpdate.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setAccount(com.klaytn.caver.account.Account.createWithAccountKeyLegacy(baseAccount)) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedAccountUpdate_appendSignature() { + try { + FeeDelegatedAccountUpdate tx = new FeeDelegatedAccountUpdate.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setAccount(com.klaytn.caver.account.Account.createWithAccountKeyLegacy(baseAccount)) + .setFeePayer(userFeePayer) + .setFeePayerSignatures(getSignatureData()) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(2, tx.getFeePayerSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedCancel() { + try { + FeeDelegatedCancel tx = new FeeDelegatedCancel.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setNonce(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedCancel_appendSignature() { + try { + FeeDelegatedCancel tx = new FeeDelegatedCancel.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setNonce(BigInteger.valueOf(1)) + .setFeePayer(userFeePayer) + .setFeePayerSignatures(getSignatureData()) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(2, tx.getFeePayerSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedChainDataAnchoring() { + try { + FeeDelegatedChainDataAnchoring tx = new FeeDelegatedChainDataAnchoring.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setInput("0xf8a6a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000405") + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedChainDataAnchoring_appendSignature() { + try { + FeeDelegatedChainDataAnchoring tx = new FeeDelegatedChainDataAnchoring.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setFeePayer(userFeePayer) + .setInput("0xf8a6a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000405") + .setFeePayerSignatures(getSignatureData()) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(2, tx.getFeePayerSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferWithRatioTx() { + FeeDelegatedValueTransferWithRatio tx = new FeeDelegatedValueTransferWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setGas(BigInteger.valueOf(50000)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + try { + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferWithRatioTx_appendSignature() { + SignatureData signatureData = getSignatureData(); + + FeeDelegatedValueTransferWithRatio tx = new FeeDelegatedValueTransferWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setGas(BigInteger.valueOf(50000)) + .setFeePayer(userFeePayer) + .setFeeRatio(BigInteger.valueOf(1)) + .setFeePayerSignatures(getSignatureData()) + .build(); + + try { + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(2, tx.getFeePayerSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferMemoWithRatioTx() { + String input = Numeric.toHexString("hello".getBytes(StandardCharsets.UTF_8)); + + FeeDelegatedValueTransferMemoWithRatio tx = new FeeDelegatedValueTransferMemoWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + try { + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferMemoWithRatioTx_appendSignature() { + SignatureData signatureData = getSignatureData(); + String input = Numeric.toHexString("hello".getBytes(StandardCharsets.UTF_8)); + + FeeDelegatedValueTransferMemoWithRatio tx = new FeeDelegatedValueTransferMemoWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setFeePayer(userFeePayer) + .setFeePayerSignatures(getSignatureData()) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + try { + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(2, tx.getFeePayerSignatures().size()); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractDeployWithRatio() { + try { + String input = ContractSampleData.encodeConstructor(caver); + + FeeDelegatedSmartContractDeployWithRatio tx = new FeeDelegatedSmartContractDeployWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractDeployWithRatio_appendSignature() { + try { + String input = ContractSampleData.encodeConstructor(caver); + SignatureData signatureData = getSignatureData(); + + FeeDelegatedSmartContractDeployWithRatio tx = new FeeDelegatedSmartContractDeployWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setFeePayer(userFeePayer) + .setFeePayerSignatures(getSignatureData()) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(2, tx.getFeePayerSignatures().size()); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractExecutionWithRatio() { + try { + String input = ContractSampleData.encodeABI(caver); + FeeDelegatedSmartContractExecutionWithRatio tx = new FeeDelegatedSmartContractExecutionWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractExecutionWithRatio_appendSignature() { + try { + String input = ContractSampleData.encodeABI(caver); + FeeDelegatedSmartContractExecutionWithRatio tx = new FeeDelegatedSmartContractExecutionWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setFeePayer(userFeePayer) + .setFeePayerSignatures(getSignatureData()) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(2, tx.getFeePayerSignatures().size()); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedAccountUpdateWithRatio() { + try { + FeeDelegatedAccountUpdateWithRatio tx = new FeeDelegatedAccountUpdateWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setAccount(com.klaytn.caver.account.Account.createWithAccountKeyLegacy(baseAccount)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedAccountUpdateWithRatio_appendSignature() { + try { + FeeDelegatedAccountUpdateWithRatio tx = new FeeDelegatedAccountUpdateWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setAccount(com.klaytn.caver.account.Account.createWithAccountKeyLegacy(baseAccount)) + .setFeePayer(userFeePayer) + .setFeePayerSignatures(getSignatureData()) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(2, tx.getFeePayerSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedCancelWithRatio() { + try { + FeeDelegatedCancelWithRatio tx = new FeeDelegatedCancelWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setNonce(BigInteger.valueOf(1)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedCancelWithRatio_appendSignature() { + try { + FeeDelegatedCancelWithRatio tx = new FeeDelegatedCancelWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setNonce(BigInteger.valueOf(1)) + .setFeePayer(userFeePayer) + .setFeePayerSignatures(getSignatureData()) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(2, tx.getFeePayerSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedChainDataAnchoringWithRatio() { + try { + FeeDelegatedChainDataAnchoringWithRatio tx = new FeeDelegatedChainDataAnchoringWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setInput("0xf8a6a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000405") + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedChainDataAnchoringWithRatio_appendSignature() { + try { + FeeDelegatedChainDataAnchoringWithRatio tx = new FeeDelegatedChainDataAnchoringWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setInput("0xf8a6a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000405") + .setFeePayer(userFeePayer) + .setFeeRatio(BigInteger.valueOf(1)) + .setFeePayerSignatures(getSignatureData()) + .build(); + + caver.wallet.signAsFeePayer(userFeePayer, tx); + + assertEquals(userFeePayer, tx.getFeePayer()); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(2, tx.getFeePayerSignatures().size()); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + } + + public static class signAsGlobalFeePayerTest { + static CaverExtKAS caver; + static String baseAccount; + static String toAccount; + + @BeforeClass + public static void init() throws IOException, TransactionException { + Config.init(); + caver = Config.getCaver(); + + List addressList = caver.wallet.generate(2); + baseAccount = addressList.get(0); + toAccount = addressList.get(1); + + Config.sendValue(baseAccount); + caver.kas.wallet.getAccountApi().getApiClient().setDebugging(true); + } + + public SignatureData getSignatureData() { + return new SignatureData( + "0x07f5", + "0xb99eefa471f4ff2a6be78c9f66d512a286084c73c07bfd81e8c4c056b31e003b", + "0x1b1e3b2b0e74fe1ce44e94c7485cc5e24f852cb0daf52c85a128a77bdd579310" + ); + } + + @Test + public void feeDelegatedValueTransferTx() { + FeeDelegatedValueTransfer tx = new FeeDelegatedValueTransfer.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setGas(BigInteger.valueOf(50000)) + .build(); + + try { + caver.wallet.signAsGlobalFeePayer(tx); + + assertTrue(Utils.isAddress(tx.getFeePayer())); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures().get(0))); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferMemoTx() { + String input = Numeric.toHexString("hello".getBytes(StandardCharsets.UTF_8)); + + FeeDelegatedValueTransferMemo tx = new FeeDelegatedValueTransferMemo.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .build(); + + try { + caver.wallet.signAsGlobalFeePayer(tx); + + assertTrue(Utils.isAddress(tx.getFeePayer())); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractDeploy() { + try { + String input = ContractSampleData.encodeConstructor(caver); + + FeeDelegatedSmartContractDeploy tx = new FeeDelegatedSmartContractDeploy.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .build(); + + caver.wallet.signAsGlobalFeePayer(tx); + + assertTrue(Utils.isAddress(tx.getFeePayer())); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractExecution() { + try { + String input = ContractSampleData.encodeABI(caver); + FeeDelegatedSmartContractExecution tx = new FeeDelegatedSmartContractExecution.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .build(); + + caver.wallet.signAsGlobalFeePayer(tx); + + assertTrue(Utils.isAddress(tx.getFeePayer())); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedAccountUpdate() { + try { + FeeDelegatedAccountUpdate tx = new FeeDelegatedAccountUpdate.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setAccount(com.klaytn.caver.account.Account.createWithAccountKeyLegacy(baseAccount)) + .build(); + + caver.wallet.signAsGlobalFeePayer(tx); + + assertTrue(Utils.isAddress(tx.getFeePayer())); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedCancel() { + try { + FeeDelegatedCancel tx = new FeeDelegatedCancel.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setNonce(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsGlobalFeePayer(tx); + + assertTrue(Utils.isAddress(tx.getFeePayer())); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedChainDataAnchoring() { + try { + FeeDelegatedChainDataAnchoring tx = new FeeDelegatedChainDataAnchoring.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setInput("0xf8a6a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000405") + .build(); + + caver.wallet.signAsGlobalFeePayer(tx); + + assertTrue(Utils.isAddress(tx.getFeePayer())); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferWithRatioTx() { + FeeDelegatedValueTransferWithRatio tx = new FeeDelegatedValueTransferWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setGas(BigInteger.valueOf(50000)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + try { + caver.wallet.signAsGlobalFeePayer(tx); + + assertTrue(Utils.isAddress(tx.getFeePayer())); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedValueTransferMemoWithRatioTx() { + String input = Numeric.toHexString("hello".getBytes(StandardCharsets.UTF_8)); + + FeeDelegatedValueTransferMemoWithRatio tx = new FeeDelegatedValueTransferMemoWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setValue("0x1") + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + try { + caver.wallet.signAsGlobalFeePayer(tx); + + assertTrue(Utils.isAddress(tx.getFeePayer())); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (KASAPIException | IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractDeployWithRatio() { + try { + String input = ContractSampleData.encodeConstructor(caver); + + FeeDelegatedSmartContractDeployWithRatio tx = new FeeDelegatedSmartContractDeployWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsGlobalFeePayer(tx); + + assertTrue(Utils.isAddress(tx.getFeePayer())); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedSmartContractExecutionWithRatio() { + try { + String input = ContractSampleData.encodeABI(caver); + FeeDelegatedSmartContractExecutionWithRatio tx = new FeeDelegatedSmartContractExecutionWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setTo(toAccount) + .setInput(input) + .setGas(BigInteger.valueOf(50000)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsGlobalFeePayer(tx); + + assertTrue(Utils.isAddress(tx.getFeePayer())); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException | ReflectiveOperationException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedAccountUpdateWithRatio() { + try { + FeeDelegatedAccountUpdateWithRatio tx = new FeeDelegatedAccountUpdateWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setAccount(com.klaytn.caver.account.Account.createWithAccountKeyLegacy(baseAccount)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsGlobalFeePayer(tx); + + assertTrue(Utils.isAddress(tx.getFeePayer())); + + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedCancelWithRatio() { + try { + FeeDelegatedCancelWithRatio tx = new FeeDelegatedCancelWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setNonce(BigInteger.valueOf(1)) + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsGlobalFeePayer(tx); + + assertTrue(Utils.isAddress(tx.getFeePayer())); + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void feeDelegatedChainDataAnchoringWithRatio() { + try { + FeeDelegatedChainDataAnchoringWithRatio tx = new FeeDelegatedChainDataAnchoringWithRatio.Builder() + .setKlaytnCall(caver.rpc.klay) + .setFrom(baseAccount) + .setGas(BigInteger.valueOf(50000)) + .setInput("0xf8a6a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000003a0000000000000000000000000000000000000000000000000000000000000000405") + .setFeeRatio(BigInteger.valueOf(1)) + .build(); + + caver.wallet.signAsGlobalFeePayer(tx); + + assertTrue(Utils.isAddress(tx.getFeePayer())); + assertEquals(1, tx.getSignatures().size()); + assertTrue(Utils.isEmptySig(tx.getSignatures().get(0))); + assertEquals(1, tx.getFeePayerSignatures().size()); + assertFalse(Utils.isEmptySig(tx.getFeePayerSignatures())); + } catch (IOException e) { + e.printStackTrace(); + fail(); + } + } + } + + public static class ContractTest { + static CaverExtKAS caver; + static String baseAccount; + static String toAccount; + static Contract sampleContract; + + @BeforeClass + public static void init() throws IOException, TransactionException { + Config.init(); + caver = Config.getCaver(); + List addressList = caver.wallet.generate(2); + baseAccount = addressList.get(0); + toAccount = addressList.get(1); + + Config.sendValue(baseAccount); + caver.kas.wallet.getAccountApi().getApiClient().setDebugging(true); + + sampleContract = deploySampleContract(caver, baseAccount); + ContractSampleData.storeStringData(sampleContract, baseAccount, "STR", "TEST"); + ContractSampleData.storeUintData(sampleContract, baseAccount, "UINT", 2020); + } + + public static Contract deploySampleContract(Caver caver, String deployer) { + try { + Contract contract = new Contract(caver, ContractSampleData.ABI); + ContractDeployParams deployParams = new ContractDeployParams(ContractSampleData.BINARY, "KVC"); + SendOptions sendOptions = new SendOptions(deployer, BigInteger.valueOf(5000000)); + + contract.deploy(deployParams, sendOptions); + return contract; + } catch (KASAPIException | IOException | ReflectiveOperationException | TransactionException e) { + e.printStackTrace(); + fail(); + } + return null; + } + + @Test + public void deploy() { + Contract contract = deploySampleContract(caver, baseAccount); + assertNotNull(contract.getContractAddress()); + } + + @Test + public void send_setString() { + try { + SendOptions sendOptions = new SendOptions(baseAccount, BigInteger.valueOf(500000)); + TransactionReceipt.TransactionReceiptData receiptData = sampleContract.send(sendOptions, ContractSampleData.FUNC_SET_STRING, "STR", "value"); + + if(!receiptData.getStatus().equals("0x1")) { + fail(); + } + + } catch (ReflectiveOperationException | IOException | TransactionException e) { + e.printStackTrace(); + fail(); + } catch (KASAPIException e) { + e.printStackTrace(); + fail(e.getResponseBody().getCode() + " " + e.getResponseBody().getMessage()); + } + } + + @Test + public void send_setUint() { + try { + SendOptions sendOptions = new SendOptions(baseAccount, BigInteger.valueOf(500000)); + TransactionReceipt.TransactionReceiptData receiptData = sampleContract.send(sendOptions, ContractSampleData.FUNC_SET_UINT, "INT", 8); + + if(!receiptData.getStatus().equals("0x1")) { + fail(); + } + + } catch (ReflectiveOperationException | IOException | TransactionException e) { + e.printStackTrace(); + fail(); + } catch (KASAPIException e) { + e.printStackTrace(); + fail(e.getResponseBody().getCode() + " " + e.getResponseBody().getMessage()); + } + } + + @Test + public void call_getString() { + try { + List result = sampleContract.call(ContractSampleData.FUNC_GET_STRING, "STR"); + assertEquals("TEST", ((Utf8String) result.get(0)).getValue()); + } catch (Exception e) { + e.printStackTrace(); + fail(); + } + } + + @Test + public void call_getUint() { + try { + List result = sampleContract.call(ContractSampleData.FUNC_GET_UINT, "UINT"); + assertEquals(2020, ((Uint256) result.get(0)).getValue().intValue()); + } catch (Exception e) { + e.printStackTrace(); + fail(); + } + } + } + + public static class KIP7Test { + static CaverExtKAS caver; + static String baseAccount; + static String toAccount; + + static KIP7 kip7; + + public static final String CONTRACT_NAME = "Kale"; + public static final String CONTRACT_SYMBOL = "KALE"; + public static final int CONTRACT_DECIMALS = 18; + public static final BigInteger CONTRACT_INITIAL_SUPPLY = BigInteger.valueOf(100_000).multiply(BigInteger.TEN.pow(CONTRACT_DECIMALS)); // 100000 * 10^18 + + @BeforeClass + public static void init() throws Exception { + Config.init(); + caver = Config.getCaver(); + List addressList = caver.wallet.generate(2); + baseAccount = addressList.get(0); + toAccount = addressList.get(1); + + Config.sendValue(baseAccount); + caver.kas.wallet.getAccountApi().getApiClient().setDebugging(true); + + kip7 = KIP7.deploy(caver, baseAccount, CONTRACT_NAME, CONTRACT_SYMBOL, CONTRACT_DECIMALS, CONTRACT_INITIAL_SUPPLY); + } + + @Test + public void deploy() throws Exception { + KIP7 contract = KIP7.deploy(caver, baseAccount, CONTRACT_NAME, CONTRACT_SYMBOL, CONTRACT_DECIMALS, CONTRACT_INITIAL_SUPPLY); + assertNotNull(contract.getContractAddress()); + } + + @Test + public void getSymbol() throws NoSuchMethodException, IOException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException { + assertEquals(CONTRACT_SYMBOL, kip7.symbol()); + } + + @Test + public void transfer() throws Exception { + SendOptions sendOptions = new SendOptions(baseAccount, (String)null); + BigInteger transferAmount = BigInteger.TEN.multiply(BigInteger.TEN.pow(CONTRACT_DECIMALS)); // 10 * 10^18 + TransactionReceipt.TransactionReceiptData receiptData = kip7.transfer(toAccount, transferAmount, sendOptions); + assertEquals("0x1", receiptData.getStatus()); + } + } + + public static class KIP17Test { + public static final String CONTRACT_NAME = "NFT"; + public static final String CONTRACT_SYMBOL = "NFT_KALE"; + static CaverExtKAS caver; + static String baseAccount; + static KIP17 kip17; + + @BeforeClass + public static void init() throws Exception { + Config.init(); + caver = Config.getCaver(); + List addressList = caver.wallet.generate(1); + baseAccount = addressList.get(0); + + Config.sendValue(baseAccount); + caver.kas.wallet.getAccountApi().getApiClient().setDebugging(true); + + kip17 = KIP17.deploy(caver, baseAccount, CONTRACT_NAME, CONTRACT_SYMBOL); + } + + @Test + public void deploy() throws Exception { + KIP17 kip17 = KIP17.deploy(caver, baseAccount, CONTRACT_NAME, CONTRACT_SYMBOL); + assertNotNull(kip17.getContractAddress()); + } + + @Test + public void symbol() throws NoSuchMethodException, IOException, InstantiationException, IllegalAccessException, InvocationTargetException, ClassNotFoundException { + assertEquals(CONTRACT_SYMBOL, kip17.symbol()); + } + + @Test + public void mint() throws Exception { + TransactionReceipt.TransactionReceiptData receiptData = kip17.mint(baseAccount, BigInteger.ZERO, new SendOptions(baseAccount, (String)null)); + assertEquals("0x1", receiptData.getStatus()); + } + } +} diff --git a/src/test/java/xyz/groundx/caver_ext_kas/wallet/KASWalletTest.java b/src/test/java/xyz/groundx/caver_ext_kas/wallet/KASWalletTest.java new file mode 100644 index 00000000..95108674 --- /dev/null +++ b/src/test/java/xyz/groundx/caver_ext_kas/wallet/KASWalletTest.java @@ -0,0 +1,953 @@ +/* + * Copyright 2020 The caver-java-ext-kas Authors + * + * Licensed under the Apache License, Version 2.0 (the “License”); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an “AS IS” BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package xyz.groundx.caver_ext_kas.wallet; + +import com.klaytn.caver.account.AccountKeyLegacy; +import com.klaytn.caver.account.AccountKeyRoleBased; +import com.klaytn.caver.account.AccountKeyWeightedMultiSig; +import com.klaytn.caver.methods.response.AccountKey; +import com.klaytn.caver.rpc.Klay; +import com.klaytn.caver.transaction.TransactionDecoder; +import com.klaytn.caver.transaction.type.AccountUpdate; +import com.klaytn.caver.transaction.type.FeeDelegatedValueTransfer; +import com.klaytn.caver.transaction.type.LegacyTransaction; +import com.klaytn.caver.transaction.type.ValueTransfer; +import com.klaytn.caver.wallet.keyring.SignatureData; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import xyz.groundx.caver_ext_kas.CaverExtKAS; +import xyz.groundx.caver_ext_kas.Config; +import xyz.groundx.caver_ext_kas.exception.KASAPIException; +import xyz.groundx.caver_ext_kas.kas.wallet.Wallet; +import xyz.groundx.caver_ext_kas.rest_client.io.swagger.client.ApiException; +import xyz.groundx.caver_ext_kas.rest_client.io.swagger.client.api.wallet.model.*; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +public class KASWalletTest { + + public static class generateTest { + @Test + public void generate() { + Wallet wallet = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(wallet); + + try { + when(wallet.createAccount()).thenReturn(new Account()); + + List addressList = kasWallet.generate(3); + verify(wallet, times(3)).createAccount(); + assertEquals(3, addressList.size()); + } catch (KASAPIException | ApiException e) { + e.printStackTrace(); + fail(); + } + } + } + + public static class getAccountTest { + static Account account; + static CaverExtKAS caver; + + @BeforeClass + public static void init() throws ApiException { + Config.init(); + caver = Config.getCaver(); + caver.kas.wallet.getAccountApi().getApiClient().setDebugging(true); + + account = caver.kas.wallet.createAccount(); + } + + @Test + public void getAccount() { + Wallet wallet = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(wallet); + try { + when(wallet.getAccount(account.getAddress())).thenReturn(account); + Account actual = kasWallet.getAccount(account.getAddress()); + + verify(wallet, times(1)).getAccount(account.getAddress()); + assertNotNull(actual); + } catch (KASAPIException | ApiException e) { + e.printStackTrace(); + fail(); + } + } + } + + public static class isExistedTest { + @Test + public void isExisted() throws ApiException { + String address = "0xb2Fd3a28efC3226638B7f92D9b48C370588c49F2"; + Wallet wallet = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(wallet); + + Account account = new Account(); + account.setAddress(address); + + when(wallet.getAccount(address)).thenReturn(account); + + kasWallet.isExisted(address); + verify(wallet, times(1)).getAccount(address); + assertTrue(kasWallet.isExisted(address)); + } + + @Test + public void isExistedFail() { + String address = "0xa809284C83b901eD106Aba4Ccda14628Af128e14"; + + try { + Wallet wallet = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(wallet); + + when(wallet.getAccount(address)).thenThrow(new ApiException()); + + assertFalse(kasWallet.isExisted(address)); + verify(wallet, times(1)).getAccount(address); + } catch (ApiException e) { + e.printStackTrace(); + fail(); + } + } + } + + public static class removeTest { + @Test + public void remove() { + String address = "0xb2Fd3a28efC3226638B7f92D9b48C370588c49F2"; + + AccountStatus accountStatus = new AccountStatus(); + accountStatus.setStatus("deleted"); + + try { + Wallet wallet = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(wallet); + when(wallet.deleteAccount(address)).thenReturn(accountStatus); + + assertTrue(kasWallet.remove(address)); + verify(wallet, times(1)).deleteAccount(address); + } catch (ApiException e) { + e.printStackTrace(); + fail(); + } + } + } + + public static class disableAccountTest { + @Test + public void disableAccount() { + String address = "0x695D16Caccf08FfBE9680ED8C4e1950d74ceD8CE"; + + AccountSummary accountSummary = new AccountSummary(); + accountSummary.setAddress(address); + accountSummary.setKrn("krn:1001:wallet:d5c346f5-bb80-4f45-9093-57e25205cdc8:account-pool:pool"); + accountSummary.setUpdatedAt(1606900622l); + + try { + Wallet wallet = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(wallet); + + when(wallet.disableAccount(address)).thenReturn(accountSummary); + AccountSummary summary = kasWallet.disableAccount(address); + + assertNotNull(summary); + verify(wallet, times(1)).disableAccount(address); + } catch (ApiException e) { + e.printStackTrace(); + fail(); + } + } + } + + public static class enableAccountTest { + @Test + public void enableAccount() { + String address = "0x695D16Caccf08FfBE9680ED8C4e1950d74ceD8CE"; + + AccountSummary accountSummary = new AccountSummary(); + accountSummary.setAddress(address); + accountSummary.setKrn("krn:1001:wallet:d5c346f5-bb80-4f45-9093-57e25205cdc8:account-pool:pool"); + accountSummary.setUpdatedAt(1606900622l); + + try { + Wallet wallet = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(wallet); + + when(wallet.enableAccount(address)).thenReturn(accountSummary); + AccountSummary summary = kasWallet.enableAccount(address); + + assertNotNull(summary); + verify(wallet, times(1)).enableAccount(address); + } catch (ApiException e) { + e.printStackTrace(); + fail(); + } + } + } + + + public static class signTest { + static String from = "0x758473e68179c446437b74ca8a74b58706792806"; + static String to = "0x76c6b1f34562ed7a843786e1d7f57d0d7948a6f1"; + static String gasPrice = "0x5d21dba00"; + static BigInteger gas = BigInteger.valueOf(25000); + static BigInteger nonce = BigInteger.valueOf(1); + static BigInteger chainId = BigInteger.valueOf(1001); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @BeforeClass + public static void init() { + Config.init(); + } + + public static ValueTransfer makeValueTransfer() { + CaverExtKAS caverExtKAS = Config.getCaver(); + + ValueTransfer valueTransfer = new ValueTransfer.Builder() + .setKlaytnCall(caverExtKAS.rpc.klay) + .setFrom(from) + .setTo(to) + .setGasPrice(gasPrice) + .setGas(gas) + .setValue("0x1") + .setNonce(nonce) + .setChainId(chainId) + .build(); + + return valueTransfer; + } + + public static FeeDelegatedValueTransfer makeFDValueTransfer() { + CaverExtKAS caverExtKAS = Config.getCaver(); + + FeeDelegatedValueTransfer feeDelegatedValueTransfer = new FeeDelegatedValueTransfer.Builder() + .setFrom(from) + .setTo(to) + .setGasPrice(gasPrice) + .setGas(gas) + .setValue("0x1") + .setNonce(nonce) + .setChainId(chainId) + .build(); + + return feeDelegatedValueTransfer; + } + + public static TransactionResult makeTransactionResult() { + Signature signature = new Signature(); + signature.setV("0x7f6"); + signature.setR("0x137491673d0014cca219705291f3ee7350295ef549069e639b5e9d0d8014ffd2"); + signature.setS("0x3289ed52548303f7f2f5fbb85e88ba7f373026178d30105f9738c71ae07b4a5a"); + + TransactionResult result = new TransactionResult(); + result.setFrom(from); + result.setTo(to); + result.setValue("0x1"); + result.setGasPrice(gasPrice); + result.setGas(gas.longValue()); + result.setNonce(nonce.longValue()); + result.setTypeInt(8l); + result.setRlp("0x08f87e018505d21dba008261a89476c6b1f34562ed7a843786e1d7f57d0d7948a6f10194418dad870aaaad954f245b3d0c4c13ff6a6dc201f847f8458207f6a0137491673d0014cca219705291f3ee7350295ef549069e639b5e9d0d8014ffd2a03289ed52548303f7f2f5fbb85e88ba7f373026178d30105f9738c71ae07b4a5a"); + result.setSignatures(Arrays.asList(signature)); + + return result; + } + + public static FDTransactionResult makeFDTransactionResult() { + Signature signature = new Signature(); + signature.setV("0x7f6"); + signature.setR("0x137491673d0014cca219705291f3ee7350295ef549069e639b5e9d0d8014ffd2"); + signature.setS("0x3289ed52548303f7f2f5fbb85e88ba7f373026178d30105f9738c71ae07b4a5a"); + + FDTransactionResult result = new FDTransactionResult(); + result.setFrom(from); + result.setTo(to); + result.setValue("0x1"); + result.setGasPrice(gasPrice); + result.setGas(gas.longValue()); + result.setNonce(nonce.longValue()); + result.setTypeInt(8l); + result.setRlp("0x08f87e018505d21dba008261a89476c6b1f34562ed7a843786e1d7f57d0d7948a6f10194418dad870aaaad954f245b3d0c4c13ff6a6dc201f847f8458207f6a0137491673d0014cca219705291f3ee7350295ef549069e639b5e9d0d8014ffd2a03289ed52548303f7f2f5fbb85e88ba7f373026178d30105f9738c71ae07b4a5a"); + result.setSignatures(Arrays.asList(signature)); + + return result; + } + + @Test + public void signBasicTx() throws IOException, ApiException { + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyLegacy.getType(), new AccountKeyLegacy())); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + ValueTransfer valueTransfer = makeValueTransfer(); + valueTransfer.setKlaytnCall(klay); + ValueTransfer spyTx = spy(valueTransfer); + + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestRawTransaction(any())).thenReturn(makeTransactionResult()); + + kasWallet.sign(from, spyTx); + + verify(spyTx).fillTransaction(); + verify(spyTx).getRLPEncoding(); + verify(walletAPI).requestRawTransaction(any()); + verify(spyTx).appendSignatures(anyList()); + assertEquals(1, spyTx.getSignatures().size()); + } + + @Test + public void signBasicTxWithAppendSignature() throws IOException, ApiException { + SignatureData signatureData = new SignatureData("0x4f8", + "0x237491673d0014cca219705291f3ee7350295ef549069e639b5e9d0d8014ffd5", + "0x4289ed52548303f7f2f5fbb85e88ba7f373026178d30105f9738c71ae07b4a5b"); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + ValueTransfer valueTransfer = makeValueTransfer(); + valueTransfer.setKlaytnCall(klay); + valueTransfer.appendSignatures(signatureData); + + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyLegacy.getType(), new AccountKeyLegacy())); + + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + ValueTransfer spyTx = spy(valueTransfer); + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestRawTransaction(any())).thenReturn(makeTransactionResult()); + + kasWallet.sign(from, spyTx); + + verify(spyTx).fillTransaction(); + verify(spyTx).getRLPEncoding(); + verify(walletAPI).requestRawTransaction(any()); + verify(spyTx).appendSignatures(anyList()); + assertEquals(2, spyTx.getSignatures().size()); + } + + @Test + public void signLegacyTxWithAppendSignature_thrownException() throws IOException, ApiException { + expectedException.expectMessage("Signatures already defined."); + expectedException.expect(RuntimeException.class); + + SignatureData signatureData = new SignatureData("0x4f8", + "0x237491673d0014cca219705291f3ee7350295ef549069e639b5e9d0d8014ffd5", + "0x4289ed52548303f7f2f5fbb85e88ba7f373026178d30105f9738c71ae07b4a5b"); + + LegacyTransaction legacyTransaction = new LegacyTransaction.Builder() + .setFrom(from) + .setTo(to) + .setValue("0x1") + .setNonce(nonce) + .setChainId(chainId) + .setGasPrice(gasPrice) + .setGas(gas) + .build(); + + legacyTransaction.appendSignatures(signatureData); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + legacyTransaction.setKlaytnCall(klay); + + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyLegacy.getType(), new AccountKeyLegacy())); + + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + LegacyTransaction spyTx = spy(legacyTransaction); + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestRawTransaction(any())).thenReturn(makeTransactionResult()); + + kasWallet.sign(from, legacyTransaction); + + } + + @Test + public void signFDTx() throws IOException, ApiException { + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyLegacy.getType(), new AccountKeyLegacy())); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + FeeDelegatedValueTransfer feeDelegatedValueTransfer = makeFDValueTransfer(); + feeDelegatedValueTransfer.setKlaytnCall(klay); + + FeeDelegatedValueTransfer spyTx = spy(feeDelegatedValueTransfer); + + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestFDRawTransactionPaidByGlobalFeePayer(any())).thenReturn(makeFDTransactionResult()); + + kasWallet.sign(from, spyTx); + + verify(spyTx).fillTransaction(); + verify(spyTx).getRLPEncoding(); + verify(walletAPI).requestFDRawTransactionPaidByGlobalFeePayer(any()); + verify(spyTx).appendSignatures(anyList()); + assertEquals(1, spyTx.getSignatures().size()); + } + + @Test + public void signFDTxWithAppendSignatures() throws ApiException, IOException { + SignatureData signatureData = new SignatureData("0x4f8", + "0x237491673d0014cca219705291f3ee7350295ef549069e639b5e9d0d8014ffd5", + "0x4289ed52548303f7f2f5fbb85e88ba7f373026178d30105f9738c71ae07b4a5b"); + + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyLegacy.getType(), new AccountKeyLegacy())); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + FeeDelegatedValueTransfer feeDelegatedValueTransfer = makeFDValueTransfer(); + feeDelegatedValueTransfer.setKlaytnCall(klay); + feeDelegatedValueTransfer.appendSignatures(signatureData); + + FeeDelegatedValueTransfer spyTx = spy(feeDelegatedValueTransfer); + + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestFDRawTransactionPaidByGlobalFeePayer(any())).thenReturn(makeFDTransactionResult()); + + kasWallet.sign(from, spyTx); + + verify(spyTx).fillTransaction(); + verify(spyTx).getRLPEncoding(); + verify(walletAPI).requestFDRawTransactionPaidByGlobalFeePayer(any()); + verify(spyTx).appendSignatures(anyList()); + assertEquals(2, spyTx.getSignatures().size()); + } + + @Test + public void signWithDuplicatedSignature() throws IOException, ApiException { + SignatureData signatureData = new SignatureData("0x7f6", + "0x137491673d0014cca219705291f3ee7350295ef549069e639b5e9d0d8014ffd2", + "0x3289ed52548303f7f2f5fbb85e88ba7f373026178d30105f9738c71ae07b4a5a"); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + ValueTransfer valueTransfer = makeValueTransfer(); + valueTransfer.setKlaytnCall(klay); + valueTransfer.appendSignatures(signatureData); + + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyLegacy.getType(), new AccountKeyLegacy())); + + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + ValueTransfer spyTx = spy(valueTransfer); + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestRawTransaction(any())).thenReturn(makeTransactionResult()); + + kasWallet.sign(from, spyTx); + + verify(spyTx).fillTransaction(); + verify(spyTx).getRLPEncoding(); + verify(walletAPI).requestRawTransaction(any()); + verify(spyTx).appendSignatures(anyList()); + assertEquals(1, spyTx.getSignatures().size()); + } + + @Test + public void signWithMultiSignKey_throwException() throws ApiException, IOException { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Not supported: Using multiple keys in an account is currently not supported."); + + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyWeightedMultiSig.getType(), null)); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + ValueTransfer valueTransfer = makeValueTransfer(); + valueTransfer.setKlaytnCall(klay); + + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + ValueTransfer spyTx = spy(valueTransfer); + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestRawTransaction(any())).thenReturn(makeTransactionResult()); + + kasWallet.sign(from, spyTx); + } + + @Test + public void signWithMultiSigKeyInRoleKey_throwException() throws ApiException, IOException { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Not supported: Using multiple keys in an account is currently not supported."); + + String encodedKey = "0x04f84b02f848e301a102c10b598a1a3ba252acc21349d61c2fbd9bc8c15c50a5599f420cccc3291f9bf9e301a1021769a9196f523c419be50c26419ebbec34d3d6aa8b59da834212f13dbec9a9c1"; + + AccountKeyRoleBased roleBased = new AccountKeyRoleBased(Arrays.asList(AccountKeyWeightedMultiSig.decode(encodedKey), new AccountKeyLegacy(), new AccountKeyLegacy())); + + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyRoleBased.getType(), roleBased)); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + ValueTransfer valueTransfer = makeValueTransfer(); + valueTransfer.setKlaytnCall(klay); + + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + ValueTransfer spyTx = spy(valueTransfer); + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestRawTransaction(any())).thenReturn(makeTransactionResult()); + + kasWallet.sign(from, spyTx); + } + + @Test + public void signAccountUpdateTxWithRoleKey() throws IOException, ApiException { + String encodedKey = "0x04f84b02f848e301a102c10b598a1a3ba252acc21349d61c2fbd9bc8c15c50a5599f420cccc3291f9bf9e301a1021769a9196f523c419be50c26419ebbec34d3d6aa8b59da834212f13dbec9a9c1"; + AccountKeyRoleBased roleBased = new AccountKeyRoleBased(Arrays.asList(AccountKeyWeightedMultiSig.decode(encodedKey), new AccountKeyLegacy(), new AccountKeyLegacy())); + + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyRoleBased.getType(), roleBased)); + + AccountUpdate accountUpdate = new AccountUpdate.Builder() + .setFrom(from) + .setAccount(com.klaytn.caver.account.Account.createWithAccountKeyLegacy(from)) + .setGas(BigInteger.valueOf(50000)) + .setNonce(BigInteger.ONE) + .setGasPrice("0x5d21dba00") + .setChainId(BigInteger.valueOf(1001)) + .build(); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + accountUpdate.setKlaytnCall(klay); + + AccountUpdate spyTx = spy(accountUpdate); + + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestRawTransaction(any())).thenReturn(makeTransactionResult()); + + kasWallet.sign(from, spyTx); + + verify(spyTx).fillTransaction(); + verify(spyTx).getRLPEncoding(); + verify(walletAPI).requestRawTransaction(any()); + verify(spyTx).appendSignatures(anyList()); + assertEquals(1, spyTx.getSignatures().size()); + } + + @Test + public void signAccountUpdateTxWithMultiSigKeyinRoleKey() throws IOException, ApiException { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Not supported: Using multiple keys in an account is currently not supported."); + + String encodedKey = "0x04f84b02f848e301a102c10b598a1a3ba252acc21349d61c2fbd9bc8c15c50a5599f420cccc3291f9bf9e301a1021769a9196f523c419be50c26419ebbec34d3d6aa8b59da834212f13dbec9a9c1"; + AccountKeyRoleBased roleBased = new AccountKeyRoleBased(Arrays.asList(new AccountKeyLegacy(), AccountKeyWeightedMultiSig.decode(encodedKey), new AccountKeyLegacy())); + + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyRoleBased.getType(), roleBased)); + + AccountUpdate accountUpdate = new AccountUpdate.Builder() + .setFrom(from) + .setAccount(com.klaytn.caver.account.Account.createWithAccountKeyLegacy(from)) + .setGas(BigInteger.valueOf(50000)) + .setNonce(BigInteger.ONE) + .setGasPrice("0x5d21dba00") + .setChainId(BigInteger.valueOf(1001)) + .build(); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + accountUpdate.setKlaytnCall(klay); + + AccountUpdate spyTx = spy(accountUpdate); + + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestRawTransaction(any())).thenReturn(makeTransactionResult()); + + kasWallet.sign(from, spyTx); + } + } + + public static class signAsFeePayerTest { + static String from = "0x418dad870aaaad954f245b3d0c4c13ff6a6dc201"; + static String to = "0x76c6b1f34562ed7a843786e1d7f57d0d7948a6f1"; + static String feePayer = "0x44ee3906a7a2007762e9d706df6e4ef63fa1eda8"; + static String value = "0x1"; + static BigInteger gas = BigInteger.valueOf(25000); + static BigInteger nonce = BigInteger.ONE; + static String gasPrice = "0x5d21dba00"; + static BigInteger chainId = BigInteger.valueOf(1001); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + public static FeeDelegatedValueTransfer makeFDValueTransfer() { + SignatureData signatureData = new SignatureData("0x07f6", + "0xef617aa7de05d4e807bbc5b9c67ecf05ef067ca6a01aeff6cc81b1f4548216c5", + "0x23510f58c1dd82c583cd47302aac1d077c97926ba9bcaa9ca4ad0bc809cf0890"); + + FeeDelegatedValueTransfer transfer = new FeeDelegatedValueTransfer.Builder() + .setFrom(from) + .setTo(to) + .setGasPrice(gasPrice) + .setGas(gas) + .setValue(value) + .setFeePayer(feePayer) + .setNonce(nonce) + .setChainId(chainId) + .setSignatures(signatureData) + .build(); + + return transfer; + } + + public static FDTransactionResult makeFDTransactionResult() { + Signature signature = new Signature(); + signature.setV("0x7f6"); + signature.setR("0xef617aa7de05d4e807bbc5b9c67ecf05ef067ca6a01aeff6cc81b1f4548216c5"); + signature.setS("0x23510f58c1dd82c583cd47302aac1d077c97926ba9bcaa9ca4ad0bc809cf0890"); + + FDTransactionResult result = new FDTransactionResult(); + result.setFrom(from); + result.setTo(to); + result.setValue(value); + result.setGasPrice(gasPrice); + result.setGas(gas.longValue()); + result.setNonce(nonce.longValue()); + result.setFeePayer(feePayer); + result.setTypeInt(9l); + result.setRlp("0x09f8dc038505d21dba0082c3509476c6b1f34562ed7a843786e1d7f57d0d7948a6f10194758473e68179c446437b74ca8a74b58706792806f847f8458207f6a0ef617aa7de05d4e807bbc5b9c67ecf05ef067ca6a01aeff6cc81b1f4548216c5a023510f58c1dd82c583cd47302aac1d077c97926ba9bcaa9ca4ad0bc809cf08909444ee3906a7a2007762e9d706df6e4ef63fa1eda8f847f8458207f6a0637426d1221cf48837634c86fb31c96dc2c5f2f847cbdd8cff959308f2331cf0a02299f5f184fa1b9ba545ba6fa90474695b6e4860255f8c3837ec31495f0e0f39"); + result.setSignatures(Arrays.asList(signature)); + result.setTransactionHash("0x2a7c62262e4ff747afe429992f4f4dd48dc085ccc93c4993c8dd798593fa2d57"); + + return result; + } + + @Test + public void signFDTx() throws ApiException, IOException { + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyLegacy.getType(), new AccountKeyLegacy())); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + FeeDelegatedValueTransfer valueTransfer = makeFDValueTransfer(); + valueTransfer.setKlaytnCall(klay); + FeeDelegatedValueTransfer spyTx = spy(valueTransfer); + + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestFDRawTransactionPaidByUser(any())).thenReturn(makeFDTransactionResult()); + + kasWallet.signAsFeePayer(feePayer, spyTx); + + verify(spyTx).fillTransaction(); + verify(spyTx).getRLPEncoding(); + verify(walletAPI).requestFDRawTransactionPaidByUser(any()); + verify(spyTx).appendFeePayerSignatures(anyList()); + assertEquals(1, spyTx.getFeePayerSignatures().size()); + } + + @Test + public void signFDTxWithAppendSignature() throws IOException, ApiException { + SignatureData signatureData = new SignatureData("0x4f8", + "0x237491673d0014cca219705291f3ee7350295ef549069e639b5e9d0d8014ffd5", + "0x4289ed52548303f7f2f5fbb85e88ba7f373026178d30105f9738c71ae07b4a5b"); + + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyLegacy.getType(), new AccountKeyLegacy())); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + FeeDelegatedValueTransfer valueTransfer = makeFDValueTransfer(); + valueTransfer.setKlaytnCall(klay); + valueTransfer.appendFeePayerSignatures(signatureData); + FeeDelegatedValueTransfer spyTx = spy(valueTransfer); + + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestFDRawTransactionPaidByUser(any())).thenReturn(makeFDTransactionResult()); + + kasWallet.signAsFeePayer(feePayer, spyTx); + + verify(spyTx).fillTransaction(); + verify(spyTx).getRLPEncoding(); + verify(walletAPI).requestFDRawTransactionPaidByUser(any()); + verify(spyTx).appendFeePayerSignatures(anyList()); + assertEquals(2, spyTx.getFeePayerSignatures().size()); + } + + @Test + public void signFDTxWithDuplicatedSignature() throws IOException, ApiException { + FeeDelegatedValueTransfer feeDelegatedValueTransfer = (FeeDelegatedValueTransfer)TransactionDecoder.decode(makeFDTransactionResult().getRlp()); + SignatureData signatureData = feeDelegatedValueTransfer.getFeePayerSignatures().get(0); + + FeeDelegatedValueTransfer valueTransfer = makeFDValueTransfer(); + valueTransfer.appendFeePayerSignatures(signatureData); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + valueTransfer.setKlaytnCall(klay); + + + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyLegacy.getType(), new AccountKeyLegacy())); + + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + FeeDelegatedValueTransfer spyTx = spy(valueTransfer); + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestFDRawTransactionPaidByUser(any())).thenReturn(makeFDTransactionResult()); + + kasWallet.signAsFeePayer(feePayer, spyTx); + + verify(spyTx).fillTransaction(); + verify(spyTx).getRLPEncoding(); + verify(walletAPI).requestFDRawTransactionPaidByUser(any()); + verify(spyTx).appendFeePayerSignatures(anyList()); + assertEquals(1, spyTx.getFeePayerSignatures().size()); + } + + @Test + public void signWithMultiSigKey_throwException() throws ApiException, IOException { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Not supported: Using multiple keys in an account is currently not supported."); + + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyWeightedMultiSig.getType(), null)); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + FeeDelegatedValueTransfer valueTransfer = makeFDValueTransfer(); + valueTransfer.setKlaytnCall(klay); + + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + FeeDelegatedValueTransfer spyTx = spy(valueTransfer); + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestFDRawTransactionPaidByUser(any())).thenReturn(makeFDTransactionResult()); + + kasWallet.signAsFeePayer(feePayer, spyTx); + } + + @Test + public void signWithMultiSigKeyInRoleKey_throwException() throws ApiException, IOException { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Not supported: Using multiple keys in an account is currently not supported."); + + String encodedKey = "0x04f84b02f848e301a102c10b598a1a3ba252acc21349d61c2fbd9bc8c15c50a5599f420cccc3291f9bf9e301a1021769a9196f523c419be50c26419ebbec34d3d6aa8b59da834212f13dbec9a9c1"; + + AccountKeyRoleBased roleBased = new AccountKeyRoleBased(Arrays.asList(new AccountKeyLegacy(), new AccountKeyLegacy(), AccountKeyWeightedMultiSig.decode(encodedKey))); + + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyRoleBased.getType(), roleBased)); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + FeeDelegatedValueTransfer valueTransfer = makeFDValueTransfer(); + valueTransfer.setKlaytnCall(klay); + + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + FeeDelegatedValueTransfer spyTx = spy(valueTransfer); + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestFDRawTransactionPaidByUser(any())).thenReturn(makeFDTransactionResult()); + + kasWallet.signAsFeePayer(feePayer, spyTx); + } + } + + public static class signAsGlobalFeePayerTest { + static String from = "0xac3bd4b108f56ffcec6339fda14f649be01819c8"; + static String to = "0x76c6b1f34562ed7a843786e1d7f57d0d7948a6f1"; + static String feePayer = "0x1b71a63903e35371e2fc41c6012effb99b9a2c0f"; + static String gasPrice = "0x5d21dba00"; + static BigInteger gas = BigInteger.valueOf(50000); + static BigInteger nonce = BigInteger.valueOf(3); + static BigInteger chainId = BigInteger.valueOf(1001); + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + public static FeeDelegatedValueTransfer makeFDValueTransfer() { + SignatureData signatureData = new SignatureData("0x07f5", + "0x7b253fdb79561ba2d24ee39a0ba0a6edf0a2df60ebeae6713015288a0c0cfb20", + "0x150c054bb93919b4fb3bed927b5dfb7162a54c29a6da52c9ad60ce6e2b62ef25"); + + FeeDelegatedValueTransfer feeDelegatedValueTransfer = new FeeDelegatedValueTransfer.Builder() + .setFrom(from) + .setTo(to) + .setGasPrice(gasPrice) + .setGas(gas) + .setValue("0x1") + .setNonce(nonce) + .setChainId(chainId) + .setSignatures(signatureData) + .build(); + + return feeDelegatedValueTransfer; + } + + public static FDTransactionResult makeFDTransactionResult() { + Signature signature = new Signature(); + signature.setV("0x07f5"); + signature.setR("0x7b253fdb79561ba2d24ee39a0ba0a6edf0a2df60ebeae6713015288a0c0cfb20"); + signature.setS("0x150c054bb93919b4fb3bed927b5dfb7162a54c29a6da52c9ad60ce6e2b62ef25"); + + FDTransactionResult result = new FDTransactionResult(); + result.setFrom(from); + result.setTo(to); + result.setValue("0x1"); + result.setGasPrice(gasPrice); + result.setGas(gas.longValue()); + result.setNonce(nonce.longValue()); + result.setFeePayer(feePayer); + result.setTypeInt(9l); + result.setRlp("0x09f8dc808505d21dba0082c3509476c6b1f34562ed7a843786e1d7f57d0d7948a6f10194ac3bd4b108f56ffcec6339fda14f649be01819c8f847f8458207f5a07b253fdb79561ba2d24ee39a0ba0a6edf0a2df60ebeae6713015288a0c0cfb20a0150c054bb93919b4fb3bed927b5dfb7162a54c29a6da52c9ad60ce6e2b62ef25941b71a63903e35371e2fc41c6012effb99b9a2c0ff847f8458207f6a03be553ff9d261860fbb0c4b2c2d6ad7dd8093a35ff4ee7ba7cccd9f88841e289a0559530699189bccbf9684af9e66e7609aa6a09253f98f1bfe626856f089a9414"); + result.setSignatures(Arrays.asList(signature)); + + return result; + } + + @Test + public void signFDTx() throws IOException, ApiException { + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyLegacy.getType(), new AccountKeyLegacy())); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + FeeDelegatedValueTransfer valueTransfer = makeFDValueTransfer(); + valueTransfer.setKlaytnCall(klay); + FeeDelegatedValueTransfer spyTx = spy(valueTransfer); + + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestFDRawTransactionPaidByGlobalFeePayer(any())).thenReturn(makeFDTransactionResult()); + + kasWallet.signAsGlobalFeePayer(spyTx); + + verify(spyTx).fillTransaction(); + verify(spyTx).getRLPEncoding(); + verify(walletAPI).requestFDRawTransactionPaidByGlobalFeePayer(any()); + verify(spyTx).appendFeePayerSignatures(anyList()); + assertEquals(1, spyTx.getFeePayerSignatures().size()); + } + + @Test + public void signFDTxWithAppendSignature() throws IOException, ApiException { + SignatureData signatureData = new SignatureData("0x4f8", + "0x237491673d0014cca219705291f3ee7350295ef549069e639b5e9d0d8014ffd5", + "0x4289ed52548303f7f2f5fbb85e88ba7f373026178d30105f9738c71ae07b4a5b"); + + FeeDelegatedValueTransfer valueTransfer = makeFDValueTransfer(); + valueTransfer.setFeePayer(feePayer); + valueTransfer.appendFeePayerSignatures(signatureData); + valueTransfer.setFeePayer("0x"); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + valueTransfer.setKlaytnCall(klay); + + + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyLegacy.getType(), new AccountKeyLegacy())); + + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + FeeDelegatedValueTransfer spyTx = spy(valueTransfer); + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestFDRawTransactionPaidByGlobalFeePayer(any())).thenReturn(makeFDTransactionResult()); + + kasWallet.signAsGlobalFeePayer(spyTx); + + verify(spyTx).fillTransaction(); + verify(spyTx).getRLPEncoding(); + verify(walletAPI).requestFDRawTransactionPaidByGlobalFeePayer(any()); + verify(spyTx).appendFeePayerSignatures(anyList()); + assertEquals(2, spyTx.getFeePayerSignatures().size()); + } + + @Test + public void signDuplicatedSignature() throws IOException, ApiException { + FeeDelegatedValueTransfer feeDelegatedValueTransfer = (FeeDelegatedValueTransfer)TransactionDecoder.decode(makeFDTransactionResult().getRlp()); + + SignatureData signatureData = feeDelegatedValueTransfer.getFeePayerSignatures().get(0); + + FeeDelegatedValueTransfer valueTransfer = makeFDValueTransfer(); + valueTransfer.setFeePayer(feePayer); + valueTransfer.appendFeePayerSignatures(signatureData); + valueTransfer.setFeePayer("0x"); + + Klay klay = mock(Klay.class, RETURNS_DEEP_STUBS); + valueTransfer.setKlaytnCall(klay); + + + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyLegacy.getType(), new AccountKeyLegacy())); + + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + FeeDelegatedValueTransfer spyTx = spy(valueTransfer); + when(klay.getAccountKey(anyString()).send()).thenReturn(accountKey); + when(walletAPI.requestFDRawTransactionPaidByGlobalFeePayer(any())).thenReturn(makeFDTransactionResult()); + + kasWallet.signAsGlobalFeePayer(spyTx); + + verify(spyTx).fillTransaction(); + verify(spyTx).getRLPEncoding(); + verify(walletAPI).requestFDRawTransactionPaidByGlobalFeePayer(any()); + verify(spyTx).appendFeePayerSignatures(anyList()); + assertEquals(1, spyTx.getFeePayerSignatures().size()); + } + + @Test + public void NotMatchedFeePayer_throwException() throws IOException, ApiException { + expectedException.expect(RuntimeException.class); + expectedException.expectMessage("Invalid fee payer: The address of the fee payer defined in the transaction does not match the address of the global fee payer. To sign with a global fee payer, you must define the global fee payer's address in the feePayer field, or the feePayer field must not be defined."); + FeeDelegatedValueTransfer valueTransfer = makeFDValueTransfer(); + valueTransfer.setFeePayer(to); + + AccountKey accountKey = new AccountKey(); + accountKey.setResult(new AccountKey.AccountKeyData(AccountKeyLegacy.getType(), new AccountKeyLegacy())); + + Wallet walletAPI = mock(Wallet.class); + KASWallet kasWallet = new KASWallet(walletAPI); + + when(walletAPI.requestFDRawTransactionPaidByGlobalFeePayer(any())).thenReturn(makeFDTransactionResult()); + + kasWallet.signAsGlobalFeePayer(valueTransfer); + } + } +} \ No newline at end of file