BubiChain Privacy JNI SDK
Overview
The JNI SDK is designed to facilitate the implementation of privacy protection function on the Java side.
Installation
The dynamic library for privacy is officially provided by BubiChain.
Introduction
Here is the method format description of JNI SDK. The package path, class name and interface name cannot be changed. Otherwise, JNI interface cannot be called. When using, the Java side uses Java Method, instead of using JNI Method。
This is introduced by the following interface:
JNI Method
JNIEXPORT jlong JNICALL Java_cn_bubi_Privacy_init (JNIEnv *, jclass)
Language
Java
Package Path
cn.bubi
Class Name
Privacy
Interface Name
init
Java Method
There is a method below in the Privacy class of the cn.bubi package in Java:
public native static long init();
Parameters
Common parameters that JNI SDK must include:
Name | Type | Description |
---|---|---|
env | JNIEnv* | JNI environment variable |
classObject | jclass | JNI class environment variable |
Return
jstring
String Type
Interfaces
It mainly includes Init、Destroy, Create Pedersen Commitment, Create ECDH Secret Key, Create ECC-SECP256K1 Secret Key, Get Public Key From Secret Key, Create Range Proof, Range Proof Verification, ECDSA Signature, ECDSA Verification, Create Excess Signature For Transaction, Pedersen Tally Verification, These interfaces are under the Privacy class of the cn.bubi package in Java. These interfaces do exception handling and need to be caught with Exception.
Init
Introduction
The function of this interface is to initialize the C ++ object pointer of the privacy protection SDK.
JNI Method
This method is not for Java to call, but is in a format defined in C ++ 's JNI. Just to explain how Java uses this method, see Introduction.
JNIEXPORT jlong JNICALL Java_cn_bubi_Privacy_init (JNIEnv *env, jclass classObject)
Java Method
public native static long init();
Return
C ++ object pointer return value.
Destroy
Introduction
The function of this interface is to destroy the C ++ objects of the privacy protection SDK.
JNI Method
This method is not for Java to call, but is in a format defined in C ++ 's JNI. Just to explain how Java uses this method, see Introduction.
JNIEXPORT void JNICALL Java_cn_bubi_Privacy_destroy (JNIEnv *env, jclass classObject, jlong obj)
Java Method
public native static void destroy(long obj);
Parameters
Name Type Description obj long C++ object pointer
Create Pedersen Commitment
Introduction
The function of this interface is to use the blinding factor to encrypt the content that needs to be kept secret.
JNI Method
This method is not for Java to call, but is in a format defined in C ++ 's JNI. Just to explain how Java uses this method, see Introduction.
JNIEXPORT jobjectArray JNICALL Java_cn_bubi_Privacy_createPedersenCommit (JNIEnv* env, jclass classObject, jlong obj, jlong value, jstring blind);
Java Method
public native static String[] createPedersenCommit(long obj, long value, String blind);
Parameters
Name Type Description obj long C++ object pointer value long Content blind String Blinding factor length must be 64 bits (using ecdh key) Return
String Array:
Array Index Type Description 0 Int Error code, 0 means success, non-zero means failure 1 String Error description, 0 is empty, non-zero is not empty 2 String Pedersen promises to be non-empty at 0 and null at non-zero
Create ECDH Secret Key
Introduction
The function of this interface is to create an ecdh key for blinding.
JNI Method
This method is not for Java to call, but is in a format defined in C ++ 's JNI. Just to explain how Java uses this method, see Introduction。
JNIEXPORT jobjectArray JNICALL Java_cn_bubi_Privacy_createEcdhKey (JNIEnv* env, jclass classObject, jlong obj, jstring seckey, jstring pubkey)
Java Method
public native static String[] createEcdhKey(long obj, String seckey, String pubkey);
Parameters
Name Type Description obj long C++ object pointer seckey String Secret key pubkey String Public key Return
String Array:
Array Index Type Description 0 Int Error code, 0 means success, non-zero means failure 1 String Error description, 0 is empty, non-zero is not empty 2 String ecdh key, non-null when 0, null if not 0
Create ECC-SECP256K1 Secret Key
Introduction
The function of this interface is to create ecc-secp256k1 key pair, including public and private keys.
JNI Method
This method is not for Java to call, but is in a format defined in C ++ 's JNI. Just to explain how Java uses this method, see Introduction.
JNIEXPORT jobjectArray JNICALL Java_cn_bubi_Privacy_createKeyPair (JNIEnv* env, jclass classObject, jlong obj)
Java Method
public native static String[] createKeyPair(long obj);
Parameters
Name Type Description obj long C++ object pointer Return
String Array:
Array Index Type Description 0 Int Error code, 0 means success, non-zero means failure 1 String Error description, 0 is empty, non-zero is not empty 2 String ECC-SECP256K1 public key, hex string 3 String ECC-SECP256K1 secret key, hex string
Get Public Key From Secret Key
Introduction
The function of this interface is to use the ecc-secp256k1 private key to get the ecc-secp256k1 public key.
JNI Method
This method is not for Java to call, but is in a format defined in C ++ 's JNI. Just to explain how Java uses this method, see Introduction.
JNIEXPORT jobjectArray JNICALL Java_cn_bubi_Privacy_getPublicKey (JNIEnv* env, jclass classObject, jlong obj, jstring seckey)
Java Method
public native static String[] getPublicKey(long obj, String seckey);
Parameters
Name Type Description obj long C++ object pointer seckey String Secret key Return
String Array:
Array Index Type Description 0 Int Error code, 0 means success, non-zero means failure 1 String Error description, 0 is empty, non-zero is not empty 2 String ECC-SECP256K1 public key, hex string
Create Range Proof
Introduction
The function of this interface is to use bulletproof to generate range evidence of the input value to prove that the input value is correct.
JNI Method
This method is not for Java to call, but is in a format defined in C ++ 's JNI. Just to explain how Java uses this method, see Introduction.
JNIEXPORT jobjectArray JNICALL Java_cn_bubi_Privacy_bpRangeproofProve (JNIEnv* env, jclass classObject, jlong obj, jstring blind, jlong value)
Java Method
public native static String[] bpRangeproofProve(long obj, String blind, long value);
Parameters
Name Type Description obj long C++ object pointer blind String Blind factor value long transfer amount Return
String Array:
Array Index Type Description 0 Int Error code, 0 means success, non-zero means failure 1 String Error description, 0 is empty, non-zero is not empty 2 String Proof
Range Proof Verification
Introduction
The function of this interface is to verify the correctness of the scope evidence.
JNI Method
This method is not for Java to call, but is in a format defined in C ++ 's JNI. Just to explain how Java uses this method, see Introduction.
JNIEXPORT jobjectArray JNICALL Java_cn_bubi_Privacy_bpRangeproofVerify (JNIEnv* env, jclass classObject, jlong obj, jstring commit, jstring proof)
Java Method
public native static String[] bpRangeproofVerify(long obj, String commit, String proof);
Parameters
Name Type Description obj long C++ object pointer commit String pedersen commitment proof String Range proof Return
String Array:
Array Index Type Description 0 Int Error code, 0 means success, non-zero means failure 1 String Error description, 0 is empty, non-zero is not empty
ECDSA Signature
Introduction
The function of this interface is to create an ecdsa signature.
JNI Method
This method is not for Java to call, but is in a format defined in C ++ 's JNI. Just to explain how Java uses this method, see Introduction.
JNIEXPORT jobjectArray JNICALL Java_cn_bubi_Privacy_ecdsaSign (JNIEnv* env, jclass classObject, jlong obj, jstring seckey, jstring data)
Java Method
public native static String[] ecdsaSign(long obj, String seckey, String data);
Parameters
Name Type Description obj long C++ object pointer seckey String ECC-SECP256K1 secret key data String Data to be signed, length is 32 bits, no encoding required Return
String Array:
Array Index Type Description 0 Int Error code, 0 means success, non-zero means failure 1 String Error description, 0 is empty, non-zero is not empty 2 String Signature string
ECDSA Verification
Introduction
The function of this interface is to verify the ecdsa signature.
JNI Method
This method is not for Java to call, but is in a format defined in C ++ 's JNI. Just to explain how Java uses this method, see Introduction.
JNIEXPORT jobjectArray JNICALL Java_cn_bubi_Privacy_ecdsaVerify (JNIEnv* env, jclass classObject, jlong obj, jstring pubkey, jstring data, jstring sig)
Java Method
public native static String[] ecdsaVerify(long obj, String pubkey, String data, String sig);
Parameters
Name Type Description obj long C++ object pointer pubkey String ECC-SECP256K1 public key data String Data to be signed, length is 32 bits, no encoding required sig String Signature Return
String Array:
Array Index Type Description 0 Int Error code, 0 means success, non-zero means failure 1 String Error description, 0 is empty, non-zero is not empty
Create Excess Signature For Transaction
Introduction
The function of this interface is to create a transaction excess signature, that is, subtract the output blindness factor list from the input blindness factor list to get success, and then sign the information to be signed.
JNI Method
This method is not for Java to call, but is in a format defined in C ++ 's JNI. Just to explain how Java uses this method, see Introduction.
JNIEXPORT jobjectArray JNICALL Java_cn_bubi_Privacy_excessSign (JNIEnv* env, jclass classObject, jlong obj, jobjectArray inputs, jobjectArray outputs, jstring msg)
Java Method
public native static String[] excessSign(long obj, String[] inputs, String[] outputs, String msg);
Parameters
Name Type Description obj long C++ object pointer inputs String[] List of blinding factors entered outputs String[] Output list of blinding factors msg String Message to be signed, 32 bits in length, no encoding required Return
String Array:
Array Index Type Description 0 Int Error code, 0 means success, non-zero means failure 1 String Error description, 0 is empty, non-zero is not empty 2 String Signature string
Pedersen Tally Verification
Introduction
The function of this interface is to verify whether the input and output amounts are equal to the output amount, and to verify the signature of the difference between the blinding factors of the input and output pedersen promises (r * G v * H), and whether the amounts are equal.
JNI Method
This method is not for Java to call, but is in a format defined in C ++ 's JNI. Just to explain how Java uses this method, see Introduction.
JNIEXPORT jobjectArray JNICALL Java_cn_bubi_Privacy_pedersenTallyVerify (JNIEnv* env, jclass classObject, jlong obj, jobjectArray inputs, jobjectArray outputs, jstring msg, jstring sig)
Java Method
public native static String[] pedersenTallyVerify(long obj, String[] inputs, String[] outputs, String msg, String sig);
Parameters
Name Type Description obj long C++ object pointer inputs String[] Entered commitment List outputs String[] Output commitment list msg String Message to be signed, 32 bits in length, no encoding required sig String Excess signature data Return
String Array:
Array Index Type Description 0 Int Error code, 0 means success, non-zero means failure 1 String Error description, 0 is empty, non-zero is not empty
Error Code
Error Code | Description |
---|---|
0 | Success |
201 | Generates a random number error! |
202 | Invalid parameters |
203 | Failed to create pedersen commitment |
204 | Failed to parse pedersen commitment |
205 | Failed to serialize pedersen commitment |
206 | Failed to serialize pubkey |
207 | Failed to verify tally |
208 | Failed to create pubkey |
209 | Failed to generate rangeproof |
210 | Failed to verify rangeproof |
211 | Out of range |
212 | Failed to create ecdsa signature |
213 | Failed to serialize ecdsa signature |
214 | Failed to verify ecdsa signature |
215 | Failed to parse ecdsa signature |
216 | Failed to parse pubkey |
217 | Failed to blind sum |
218 | An unknown error |
219 | BP library internal error |
Example
Ordinary account A issued 100 million asset CTs under privacy protection contract account C, and transferred 50 million CTs to ordinary account B, and then checked the CT asset balances of account A and account B. For details, please see Java's Demo (code is on the intranet, no link for the time being).
The java dependency libraries used here are as follows:
<dependencies>
<dependency>
<groupId>cn.bubi.access.sdk</groupId>
<artifactId>sdk-starter</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.googlecode.protobuf-java-format</groupId>
<artifactId>protobuf-java-format</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.1-jre</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>nexus</id>
<url>http://192.168.1.11:8081/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
- Create General Account A, General Account B, and Contract Account C.
/**
* Create contract-related account operations
*/
private static void createAccountOperation(BcOperationService operationService){
try {
Transaction transaction = operationService.newTransaction(address);
contract_keyPair_C = SecureKeyGenerator.generateBubiKeyPair(); // Contract Account
from_keypair_A = SecureKeyGenerator.generateBubiKeyPair(); // Ordinary source account
to_keypair_B = SecureKeyGenerator.generateBubiKeyPair(); // Ordinary target account
String[] res = cn.bubi.Privacy.createKeyPair(cpp_obj); // ecc-secp256k1 secret and public key corresponding to source account
if (Integer.valueOf(res[0]) == 0) {
ct_from_keypair[0] = res[2];
ct_from_keypair[1] = res[3];
}
res = cn.bubi.Privacy.createKeyPair(cpp_obj); // ecc-secp256k1 secret and public key corresponding to target account
if (Integer.valueOf(res[0]) == 0) {
ct_to_keypair[0] = res[2];
ct_to_keypair[1] = res[3];
}
System.out.println("contract account: " + GsonUtil.toJson(contract_keyPair));
System.out.println("from account: " + GsonUtil.toJson(from_keypair));
System.out.println("ct from account: " + ct_from_keypair[0] + ", " + ct_from_keypair[1]);
System.out.println("to account: " + GsonUtil.toJson(to_keypair));
System.out.println("ct to account: " + ct_to_keypair[0] + ", " + ct_to_keypair[1]);
// Create contract account operation
CreateAccountOperation createContract = new CreateAccountOperation.Builder()
.buildDestAddress(contract_keyPair.getBubiAddress())
.buildScript(getContractCodeFromFile(contract_file))
// Permissions section
.buildPriMasterWeight(0)
.buildPriTxThreshold(1)
.build();
// Create common source account operation
CreateAccountOperation createFrom = new CreateAccountOperation.Builder()
.buildDestAddress(from_keypair.getBubiAddress())
// Permissions section
.buildPriMasterWeight(1)
.buildPriTxThreshold(1)
.build();
// Create common target account operation
CreateAccountOperation createTo = new CreateAccountOperation.Builder()
.buildDestAddress(to_keypair.getBubiAddress())
// Permissions section
.buildPriMasterWeight(1)
.buildPriTxThreshold(1)
.build();
// Submit transaction
TransactionCommittedResult result = transaction.buildAddOperation(createContract)
.buildAddOperation(createFrom)
.buildAddOperation(createTo)
.buildAddSigner(BubiKey.getB16PublicKey(privateKey), privateKey)
.commit();
System.out.println(GsonUtil.toJson(result));
} catch (Exception e) {
e.printStackTrace();
}
}
- General account A issues 100 million CT assets under contract account C.
/**
* Operation of issuing contract assets
*/
private static void issueOperation(BcOperationService operationService){
try {
Transaction transaction = operationService.newTransaction(from_keypair.getBubiAddress());
// Construct token private information
Privacy.ConfidentialAsset.Builder token = Privacy.ConfidentialAsset.newBuilder();
token.setTo(from_keypair.getBubiAddress()); // Bubi "source account" address for ed25519
token.setFromPubkey(ct_from_keypair[0]); // ecc-secp256k1 "source account" public key
String[] ecdh_key_ret = cn.bubi.Privacy.createEcdhKey(cpp_obj, ct_from_keypair[1], ct_from_keypair[0]); // "source account" private and public key generation blindness factor for ecc-secp256k1
String errCodeEcdhKeyRet = ecdh_key_ret[0];
String errDescEcdhKeyRet = ecdh_key_ret[1];
String ecdh_key = ecdh_key_ret[2];
if (Integer.valueOf(errCodeEcdhKeyRet) != 0) {
System.out.println("Error create ecdh key: " + errDescEcdhKeyRet);
}
String[] commit_ret = cn.bubi.Privacy.createPedersenCommit(cpp_obj, value, ecdh_key); // Creating commitments (for 100 million assets)
String errCodeCmtRet = commit_ret[0];
String errDescCmtRet = commit_ret[1];
String commit = commit_ret[2];
if (Integer.valueOf(errCodeCmtRet) != 0) {
System.out.println("Error create commit: " + errDescCmtRet);
}
token.setCommit(commit); // commitment
String enc = HexFormat.byteToHex(AesCbc.encrypt("100000000".getBytes(), ecdh_key.substring(0, 32).getBytes())).toLowerCase(); // Privacy protection for 100 million assets
token.setEncryptValue(enc); // Privacy data
String[] blinds = new String[1];
blinds[0] = ecdh_key;
String[] values = new String[1];
values[0] = Long.toString(value);
String[] proof_ret = cn.bubi.Privacy.bpRangeproofProve(cpp_obj, blinds, values); // Generate evidence for 100 million assets
String errCodePrfRet = proof_ret[0];
String errDescPrfRet = proof_ret[1];
String proof = proof_ret[2];
if (Integer.valueOf(errCodePrfRet) != 0) {
System.out.println("Error create range proof: " + errDescPrfRet);
}
String[] commits = new String[1];
commits[0] = commit;
String[] res = cn.bubi.Privacy.bpRangeproofVerify(cpp_obj, commits, proof); // Verify evidence
if (Integer.valueOf(res[0]) != 0) {
System.out.println("Error very range proof: " + res[1]);
}
token.setRangeProof(proof); // proof
JsonFormat jsonFormat = new JsonFormat();
String tokenStr = jsonFormat.printToString(token.build());
// Input structure for issuing assets
JSONObject input = new JSONObject();
input.put("method", "issue");
JSONObject params = new JSONObject();
params.put("name", "Confidential");
params.put("symbol", "CT");
params.put("token", JSONObject.parseObject(tokenStr));
input.put("params", params);
System.out.println("input: " + JSON.toJSONString(input, true));
// Trigger contract account operations
InvokeContractOperation invokeContractOperation = new InvokeContractOperation.Builder()
.buildDestAddress(contract_keyPair.getBubiAddress())
.buildInputData(input.toJSONString())
.build();
// Submit transaction
TransactionCommittedResult result = transaction.buildAddOperation(invokeContractOperation)
.buildAddSigner(from_keypair.getPubKey(), from_keypair.getPriKey())
.commit();
System.out.println(GsonUtil.toJson(result));
} catch (Exception e) {
e.printStackTrace();
}
}
- Ordinary account A transfers 50 million CT assets to ordinary account B.
/**
* Asset transfer operation
*/
private static void createTransferOperation(BcOperationService operationService){
String[] ids = {};
JSONObject input = createTx(from_keypair.getBubiAddress(), to_keypair.getBubiAddress(), ct_from_keypair, ct_to_keypair[0], value, ids);
try {
Transaction transaction = operationService.newTransaction(from_keypair.getBubiAddress());
// Trigger contract operation
InvokeContractOperation invokeContractOperation = new InvokeContractOperation.Builder()
.buildDestAddress(contract_keyPair.getBubiAddress())
.buildInputData(input.toJSONString())
.build();
// Submit transaction
TransactionCommittedResult result = transaction.buildAddOperation(invokeContractOperation)
.buildTxMetadata("transaction")
.buildAddSigner(from_keypair.getPubKey(), from_keypair.getPriKey())
.commit();
System.out.println(GsonUtil.toJson(result));
} catch (Exception e) {
e.printStackTrace();
}
}
public static JSONObject createTx(String from, String to, String[] ct_from, String ct_to, long value, String[] ids) throws Exception {
// get input token from contract
String[] priv_list = new String[1];
priv_list[0] = ct_from_keypair[1];
// Get enough assets based on the transfer amount
Map input_assets = getTokenByValue(from, priv_list, value, ids);
String[] input_blinds = new String[input_assets.size()];
String[] inputs = new String[input_assets.size()];
String[] input_ids = new String[input_assets.size()];
int i = 0;
Long total_balance = 0L;
// Parameters required to generate excess signature and verification
for (Object key: input_assets.keySet()) {
JSONObject token = (JSONObject) input_assets.get(key);
total_balance += token.getLong("value");
String from_pubkey = token.getString("from_pubkey");
String[] blind_ret = cn.bubi.Privacy.createEcdhKey(cpp_obj, token.getString("spend_key"), from_pubkey);
String errCodeBlindRet = blind_ret[0];
String errDescBlindRet = blind_ret[1];
String blind = blind_ret[2];
if (Integer.valueOf(errCodeBlindRet) != 0) {
System.out.println("Error create blind: " + errDescBlindRet);
i += 1;
continue;
}
input_blinds[i] = blind;
inputs[i] = token.getString("commit");
input_ids[i] = key.toString();
i += 1;
}
// Create output token
String[] ecdh_key_ret = cn.bubi.Privacy.createEcdhKey(cpp_obj, ct_from[1], ct_to);
String errCodeEcdhRet = ecdh_key_ret[0];
String errDescEcdhRet = ecdh_key_ret[1];
String ecdh_key = ecdh_key_ret[2];
String commit = "";
if (Integer.valueOf(errCodeEcdhRet) != 0) {
System.out.println("Error create echd key: " + errDescEcdhRet);
} else {
String[] commit_ret = cn.bubi.Privacy.createPedersenCommit(cpp_obj, value, ecdh_key);
String errCodeCmtRet = commit_ret[0];
String errDescCmtRet = commit_ret[1];
if (Integer.valueOf(errCodeCmtRet) != 0) {
System.out.println("Error createPedersenCommit: " + errDescCmtRet);
} else {
commit = commit_ret[2];
}
}
String[] ret = cn.bubi.Privacy.bpRangeproofProve(cpp_obj, ecdh_key, value);
String errCodePrfCngRet = ret[0];
String errDescPrfCngRet = ret[1];
String proof = ret[2];
if (Integer.valueOf(errCodePrfCngRet) != 0) {
System.out.println("Error proof change: " + errDescPrfCngRet);
} else {
String[] bp_very_ret1 = cn.bubi.Privacy.bpRangeproofVerify(cpp_obj, commit, proof);
String errCodeBpRet1 = bp_very_ret1[0];
String errDescBpRet1 = bp_very_ret1[1];
if (Integer.valueOf(errCodeBpRet1) != 0) {
System.out.println("Failed to verify range proof:" + proof + ": " + errDescBpRet1 + "\n");
}
}
String encrypt_data = HexFormat.byteToHex(AesCbc.encrypt(Long.toString(value).getBytes(), ecdh_key.substring(0, 32).getBytes())).toLowerCase();
JSONObject token1 = buildToken(ct_from[0], to, encrypt_data, proof, commit);
JSONArray output_assets = null;
// Create change token
String commit_change = null;
String change_key = null;
String[] blinds;
String[] values;
String[] commits;
if (total_balance - value > 0) {
output_assets = new JSONArray(2);
blinds = new String[2];
values = new String[2];
commits = new String[2];
String[] change_key_ret = cn.bubi.Privacy.createEcdhKey(cpp_obj, ct_from[1], ct_from[0]);
String errCodeCngKeyRet = change_key_ret[0];
String errDescCngKeyRet = change_key_ret[1];
change_key = change_key_ret[2];
if (Integer.valueOf(errCodeCngKeyRet) != 0) {
System.out.println("Error create change key: " + errDescCngKeyRet);
}
String[] commit_change_ret = cn.bubi.Privacy.createPedersenCommit(cpp_obj, total_balance - value, change_key);
String errCodeCmtCngRet = commit_change_ret[0];
String errDescCmtCngRet = commit_change_ret[1];
commit_change = commit_change_ret[2];
if (Integer.valueOf(errCodeCmtCngRet) != 0) {
System.out.println("Error create commit change: " + errDescCmtCngRet);
}
String encrypt_change = HexFormat.byteToHex(AesCbc.encrypt(Long.toString(total_balance - value).getBytes(), change_key.substring(0, 32).getBytes())).toLowerCase();
String[] change_ret = cn.bubi.Privacy.bpRangeproofProve(cpp_obj, change_key, total_balance - value);
errCodePrfCngRet = change_ret[0];
errDescPrfCngRet = change_ret[1];
String proof_change = change_ret[2];
if (Integer.valueOf(errCodePrfCngRet) != 0) {
System.out.println("Error proof change: " + errDescPrfCngRet);
} else {
String[] bp_very_ret1 = cn.bubi.Privacy.bpRangeproofVerify(cpp_obj, commit_change, proof_change);
String errCodeBpRet1 = bp_very_ret1[0];
String errDescBpRet1 = bp_very_ret1[1];
if (Integer.valueOf(errCodeBpRet1) != 0) {
System.out.println("Failed to verify range proof:" + proof + ": " + errDescBpRet1 + "\n");
}
}
JSONObject token2 = buildToken(ct_from[0], from, encrypt_change, proof_change, commit_change);
output_assets.add(token1);
output_assets.add(token2);
blinds[1] = change_key;
values[1] = Long.toString(total_balance - value);
commits[1] = commit_change;
} else {
output_assets = new JSONArray(1);
blinds = new String[1];
values = new String[1];
commits = new String[1];
output_assets.add(token1);
}
blinds[0] = ecdh_key;
values[0] = Long.toString(value);
commits[0] = commit;
// Create excess signature
String[] output_blinds;
if(total_balance - value > 0) {
output_blinds = new String[2];
output_blinds[1] = change_key;
} else {
output_blinds = new String[1];
}
output_blinds[0] = ecdh_key;
String data = "64e4a2fcf36693904a0d549303c6f35c";
String[] excess_sig_ret = cn.bubi.Privacy.excessSign(cpp_obj, input_blinds, output_blinds, data);
String errCodeExcSigRet = excess_sig_ret[0];
String errDescExcSigRet = excess_sig_ret[1];
String excess_sig = excess_sig_ret[2];
if (Integer.valueOf(errCodeExcSigRet) != 0) {
System.out.println("Error excess sig: " + errDescExcSigRet);
}
// Verify that the input and output are equal
String[] tally_very_ret = cn.bubi.Privacy.pedersenTallyVerify(cpp_obj, inputs, commits, data, excess_sig);
String errCodeTalVeryRet = tally_very_ret[0];
String errDescTalVeryRet = tally_very_ret[1];
if(Integer.valueOf(errCodeTalVeryRet) != 0) {
System.out.println("Failed to verify tally, " + errDescTalVeryRet);
} else {
System.out.println("Verify tally done\n");
}
// Create transaction json structure
JSONObject txJson = buildTx(input_ids, output_assets, data, excess_sig);
JSONObject input = new JSONObject();
input.put("method", "transfer");
input.put("params", txJson);
System.out.println("The contrat input is:");
System.out.println(JSON.toJSONString(txJson, true));
return input;
}
public static JSONObject buildToken(String from_pubkey, String to, String encrypt_value, String rangeproof, String commit) {
JSONObject token = new JSONObject();
token.put("from_pubkey", from_pubkey);
token.put("to", to);
token.put("encrypt_value", encrypt_value);
if (rangeproof != null) {
token.put("range_proof", rangeproof);
}
token.put("commit", commit);
return token;
}
public static JSONObject buildTx(String[] inputs_ids, JSONArray outputs, String msg, String sig, String proof) {
JSONObject tx = new JSONObject();
JSONArray ips = new JSONArray();
for (String id: inputs_ids) {
JSONObject token = new JSONObject();
token.put("id", id);
ips.add(token);
}
tx.put("inputs", ips);
JSONArray ops = new JSONArray();
for (Object token: outputs) {
ops.add(token);
}
tx.put("outputs", ops);
tx.put("excess_sig", sig);
tx.put("excess_msg", msg);
tx.put("proof", proof);
return tx;
}
public static Map getTokenByValue(String address, String[] privs, long value, String[] ids) throws Exception {
SetMetadata data = queryService.getAccount(contract_keyPair.getBubiAddress(), address);
JSONObject dataJson = JSONObject.parseObject(data.getValue());
Long total_balance = 0L;
Map<String, JSONObject> map = new HashMap<String, JSONObject>();
for (Object token: dataJson.getJSONArray("assets")) {
JSONObject t = (JSONObject) token;
if (ids.length > 0) {
boolean got = false;
for (String id : ids) {
if (t.getString("id").equals(id)) {
got = true;
break;
}
}
if(!got) continue;
}
Long balance = 0L;
boolean decrypt_done = false;
for(String priv: privs) {
String[] dec_key_ret = cn.bubi.Privacy.createEcdhKey(cpp_obj, priv, t.getString("from_pubkey"));
String errCodeDecKeyRet = dec_key_ret[0];
String errDescDecKeyRet = dec_key_ret[1];
String dec_key = dec_key_ret[2];
if (Integer.valueOf(errCodeDecKeyRet) != 0) {
System.out.println("Error decrypt key:" + errDescDecKeyRet);
throw new UnsatisfiedLinkError(errDescDecKeyRet);
} {
String encrypt_value = t.getString("encrypt_value");
try {
balance = Long.parseLong(AesCbc.decrypt(HexFormat.hexToByte(encrypt_value), dec_key.substring(0, 32).getBytes()));
t.put("spend_key", priv);
t.put("value", balance);
decrypt_done = true;
break;
}catch (Exception e) {
continue;
}
}
}
if(!decrypt_done) {
throw new UnsatisfiedLinkError("Failed to decrypt token of id: " + t.getString("id"));
};
total_balance += balance;
map.put(t.getString("id"), t);
}
if (total_balance < value) {
//throw new UnsatisfiedLinkError("Balance not enough, want:" + Long.toString(value) + ", got:" + Long.toString(total_balance));
System.out.println("Balance not enough, want:" + Long.toString(value) + ", got:" + Long.toString(total_balance));
}
if (ids.length > 0) return map;
Long tmp_value = 0L;
Map<String, JSONObject> new_map = new HashMap<String, JSONObject>();
for (String key: map.keySet()) {
JSONObject t = map.get(key);
tmp_value += t.getLong("value");
new_map.put(key, map.get(key));
if(tmp_value >= value) break;
}
return new_map;
}
- Query the CT asset balance of a general account.
/**
* Query the asset balance of the asset sender
*/
public static void getBalance(String address, String[] privs) throws Exception {
// Query account asset information from the contract
SetMetadata data = queryService.getAccount(contract_keyPair.getBubiAddress(), address);
System.out.println("Balance of address:" + address);
if(data == null) {
System.out.println("0");
return;
}
JSONObject dataJson = JSONObject.parseObject(data.getValue());
for (Object token: dataJson.getJSONArray("assets")) {
JSONObject t = (JSONObject) token;
long balance = 0;
boolean decrypt_done = false;
// Get the right decryption key from the private key list
for(String priv: privs) {
// Create a decryption key from the private key
String[] dec_key_ret = cn.bubi.Privacy.createEcdhKey(cpp_obj, priv, t.getString("from_pubkey"));
String errCodeDecKeyRet = dec_key_ret[0];
String errDescDecKeyRet = dec_key_ret[1];
String dec_key = dec_key_ret[2];
if (Integer.valueOf(errCodeDecKeyRet) != 0) {
System.out.println("Error decrypt key:" + errDescDecKeyRet);
throw new UnsatisfiedLinkError(errDescDecKeyRet);
} {
// Parsing to get the balance of crypto assets
String encrypt_value = t.getString("encrypt_value");
try {
// Decrypt asset balance
balance = Long.parseLong(AesCbc.decrypt(HexFormat.hexToByte(encrypt_value), dec_key.substring(0, 32).getBytes()));
decrypt_done = true;
break;
}catch (Exception e) {
continue;
}
}
}
if(!decrypt_done) {
throw new UnsatisfiedLinkError("Failed to decrypt token of id: " + t.getString("id"));
};
// Print asset balance
System.out.println("id: " + t.getString("id") + ", value:" + balance);
}
return;
}
- Additional function
Read information from a file:
private static String getContractCodeFromFile(String fileName) {
StringBuilder result = new StringBuilder();
try{
File file = new File(fileName);
BufferedReader br = new BufferedReader(new FileReader(file));//Construct a BufferedReader class to read the file
String s = null;
while((s = br.readLine())!=null){//Use the readLine method to read one line at a time
result.append(System.lineSeparator() + s);
}
br.close();
}catch(Exception e){
e.printStackTrace();
}
return result.toString();
}