Constructor
public VoucherClient(final VoucherClientConfig clientConfig);
verifyUserSignature
/**
* Verify user signatures to cryptographically verify the ownership of a Flow
* account by verifying a message was signed by a user's private key/s
*
* @param message singed raw message in Hex string
* @param publicKeyHex a list of public keys in Hex string
* @param weights a list of corresponding weights of singed keys
* @param signAlgos a list of singed algorithm, where 2 indicates ECDSA_P256
* others for ECDSA_secp256k1
* @param hashAlogs a list of hash algorithm, where 2 for SHA2_256 others for
* SHA3_256
* @param signatures a list of signatures which are singed by corresponding
* keypairs
*
* @return boolean true if verified or false
*/
public boolean verifyUserSignature(final String message, final String[] publicKeysHex, final double[] weights,
final int[] signAlgos, final int[] hashAlogs, final String[] signatures)
verifyFUSDTransaction
/**
* Verify a FUSD or FLOW transaction
*
* @param payerAddress payer account address
* @param targetAmount expected amount to be received (scale of 8)
* @param transactionId flow transactionId
* @param PaymentType FLOW or FUSD enum
*
* @throws Exception with reason of unexpected error
*/
public void verifyPaymentTransaction(final String payerAddress, final BigDecimal targetAmount,
final String transactionId, final PaymentType paymentType) throws FlowClientException;
mintVoucher
/**
* Mint a Voucher NFT
*
* @param recipientAddressString recipient account address
* @param landInfoHashString flow type landInfoHashString in hex
*
* @return Voucher Model
*
* @throws Exception unknown runtime error
*/
public VoucherMetadataModel mintVoucher(final String recipientAddressString, final String landInfoHashString) throws Exception;
batchMintVoucher
/**
* Mint a batch of Vouchers
*
* @param recipientAddressStringList list of recipient account address
* @param landInfoHashStringList list of landInfoHash
*
* @return a list of Minted token
*
* @throws FlowClientException runtime exception
*/
public List<VoucherMetadataModel> batchMintVoucher(final List<String> recipientAddressStringList, final List<String> landInfoHashStringList) throws FlowClientException;
generateLandInfoHash
/**
* Generate cadence compatible LandInfoHashHexString
*
* @param topLeftX UInt64 topLeftX coordinate
* @param topLeftY UInt64 topLeftY coordinate
* @param height UInt64 height of square lands
* @param width UInt64 width of square lands
*
* @return LandInfoHashHexString of a square of Lands
*/
public String generateLandInfoHash(final Integer topLeftX, final Integer topLeftY, final Integer height,
final Integer width);
Init client
public static final String TEST_ADMIN_PRIVATE_KEY_HEX = "a996c6d610d93faf82ad5b15407b66d3a2b72a284b5c2fd4097b5a3e735a79e1"; // emulator
// admin
// private
// key
public static final String SERVICE_PRIVATE_KEY_HEX = "2eae2f31cb5b756151fa11d82949c634b8f28796a711d7eb1e52cc301ed11111"; // emulator
// admin
// private
// key
public static final String FUNGIBLE_TOKEN_ADDRESS = "ee82856bf20e2aa6";
public static final String FUSD_ADDRESS = "f8d6e0586b0a20c7";
public static final String NON_FUNGIBLE_TOKEN_ADDRESS = "f8d6e0586b0a20c7";
public static final String VOUCHER_ADDRESS = "01cf0e2f2f715450";
private final FlowAddress testAdminAccountAddress = new FlowAddress("01cf0e2f2f715450");
private final FlowAddress userAccountAddress = new FlowAddress("f8d6e0586b0a20c7");
final VoucherClientConfig adminClientConfig = VoucherClientConfig.builder().host("localhost").port(3569)
.privateKeyHex(TEST_ADMIN_PRIVATE_KEY_HEX).keyIndex(0).nonFungibleTokenAddress(NON_FUNGIBLE_TOKEN_ADDRESS)
.fungibleTokenAddress(FUNGIBLE_TOKEN_ADDRESS).adminAccountAddress(testAdminAccountAddress.getBase16Value())
.voucherAddress(VOUCHER_ADDRESS).waitForSealTries(20).fusdAddress(FUSD_ADDRESS).build();
final VoucherClient adminClient = new VoucherClient(adminClientConfig);
Verify transaction and mint Voucher to payer
if (adminClient.verifyFUSDTransaction(serviceAccountAddress.getBase16Value(), targetAmount, txId.getBase16Value())){
// Mint Voucher if verification is success
VoucherMetadataModel newToken = adminClient.mintVoucher(userAccountAddress.getBase16Value(), testHash);
};
Use it with Object pool for concurrently sending MINT transaction
public void voucherClientPoolconcurrentlysendTransaction() throws Exception {
// Simulate concurrent requests in backend
final int simTransactionCount = 30;
final CountDownLatch updateLatch = new CountDownLatch(simTransactionCount);
final ExecutorService executorService = Executors.newFixedThreadPool(simTransactionCount);
// Build pool
final VoucherClientPoolFactory voucherClientPoolFactory = new VoucherClientPoolFactory(adminClientConfig, 10);
final GenericObjectPoolConfig<VoucherClient> objectPoolConfig = new GenericObjectPoolConfig<>();
objectPoolConfig.setMaxTotal(5); // do not exceed adminAccount's number of proposal keys
objectPoolConfig.setMaxWaitMillis(120000);
objectPoolConfig.setBlockWhenExhausted(true);
final GenericObjectPool<VoucherClient> objectPool = new GenericObjectPool<>(voucherClientPoolFactory,
objectPoolConfig);
// Start
for (int i = 0; i < simTransactionCount; ++i) {
final int idx = i;
executorService.execute(new Thread(() -> {
VoucherClient client = null;
try {
client = objectPool.borrowObject();
String timeStamp = new SimpleDateFormat("yyyy.MM.dd.HH.mm.ss").format(new Date());
client.mintVoucher(userAccountAddress.getBase16Value(), "TEST_HASH_POOL" + idx + timeStamp);
} catch (final Exception e) {
e.printStackTrace();
} finally {
if (client != null) {
objectPool.returnObject(client);
}
updateLatch.countDown();
}
}));
}
updateLatch.await();
executorService.shutdown();
objectPool.close();
}
Use object pool to check user signature on chain
/**
* Very user composite signatures
*
* @param message raw message in hex
* @param accountAddress account address
* @param keyIds keys ids corresponding to signatures
* @param signatures signed using above keys
*
* @return true means verified successfully
*/
public boolean verifyUserSignatureCadence(final String message, final String accountAddress,
final List<Integer> keyIds, final List<String> signatures);
// example
final String message = "TEST_HASH_MESSAGE";
final String privateKeyHex =
"2eae2f31cb5b756151fa11d82949c634b8f28796a711d7eb1e52cc301ed11111";
final PrivateKey privateKey = Crypto.decodePrivateKey(privateKeyHex);
final Signer signer = Crypto.getSigner(privateKey, HashAlgorithm.SHA3_256);
final byte[] signature = signer.signAsUser(message.getBytes());
final VoucherMinterClientPool pool = new VoucherMinterClientPool(0, 10, adminClientConfig);
final List<Integer> keyIds = new ArrayList<>();
keyIds.add(0);
final List<String> signatures = new ArrayList<>();
signatures.add(Hex.encodeHexString(signature));
final Boolean result = pool.verifyUserSignatureCadence(
Hex.encodeHexString(message.getBytes()), "0xf8d6e0586b0a20c7", keyIds, signatures);
Check Tests for full example
Start local emulator with bootstrap script
cd ./packages/contracts
make bootstrap-local
Run test
cd ./packages/sdk/java/voucher-sdk
mvn test
UlTest.vim output
? src/test/java/matrix/flow/sdk/AppTest.java
? AppTest
✔ shouldAnswerWithTrue
✔ transferCorrectFUSDShouldNotThrowException
✔ transferIncorrectFUSDShouldThrowException
✔ verifyFUSDTransactionShouldMintVoucherToSender
✔ voucherClientPoolconcurrentlysendTransaction
✔ correctSignatureVerificationShouldReturnTrue
✔ incorrectSignatureVerificationShouldReturnFalse