recvlist, String sendAddr, String desc) throws Exception {
+ return trf(getTrfTx(recvlist, desc), getAddress(sendAddr));
+ }
+
+ /**
+ * 注销资产
+ *
+ * @param issuer 资产发行者
+ * @param assetId 资产编号
+ * @param txDesc 描述
+ * @return
+ * @throws Exception
+ */
+ public String des(String issuer, String assetId, String txDesc) throws Exception {
+ return des(getDesTx(issuer, assetId, txDesc), null);
+ }
+ public String des(DestroyTransaction desTx, UInt160 from) throws Exception {
+ DestroyTransaction signedTx4Des = desTx;//uw.makeTransaction(trfTx, Fixed8.ZERO, from);
+ SignatureContext context4Trf = new SignatureContext(signedTx4Des);
+ boolean f7 = uw.sign(context4Trf);
+ if(context4Trf.isCompleted()){
+ signedTx4Des.scripts = context4Trf.getScripts();
+ }
+ uw.saveTransaction(signedTx4Des);
+ String txHex = Helper.toHexString(signedTx4Des.toArray());;
+ OnChainSDKHelper.printTransaction(signedTx4Des);
+ System.out.println("tx.a:"+Helper.getbyteStr(Helper.hexToBytes(txHex)));
+ System.out.println("tx.s:"+txHex);
+ System.out.println("txHex:"+txHex);
+ boolean f8 = false;//restNode.sendRawTransaction(txHex);
+
+ String txid4Des = signedTx4Des.hash().toString();
+ print("send des tx.sign:"+f7+",rst:"+f8+",txid:"+ txid4Des);
+ if(f7 && isWaitSync) {
+ wait(uw,txid4Des); // 等待生效
+ }
+ return txid4Des;
+ }
+ private DestroyTransaction getDesTx(String issuer, String assetId, String txDesc) {
+ DestroyTransaction tx = new DestroyTransaction();
+ tx.inputs = Arrays.stream(uw.getCoin()).filter(p -> Wallet.toAddress(p.scriptHash).equals(issuer)).filter(p -> p.assetId.toString().equals(assetId)).map(p -> p.input).toArray(TransactionInput[]::new);
+ tx.outputs = new TransactionOutput[0];
+ if(txDesc != null && txDesc.length() > 0) {
+ tx.attributes = new TransactionAttribute[1];
+ tx.attributes[0] = new TransactionAttribute();
+ tx.attributes[0].usage = TransactionAttributeUsage.Description;
+ tx.attributes[0].data = (txDesc+new Date().toString()).getBytes();
+ } else {
+ tx.attributes = new TransactionAttribute[0];
+ }
+ return tx;
+ }
+
+ /**
+ * 存证
+ *
+ * @param data 存证内容
+ * @param desc 描述
+ * @return 交易编号
+ * @throws Exception
+ */
+ public String storeCert(String data, String desc) throws Exception {
+ RecordTransaction tx = getRcdTx(data, desc);
+ String txHex = Helper.toHexString(tx.toArray());;
+ boolean f = restNode.sendRawTransaction(txHex);
+
+ String txid = tx.hash().toString();
+ print("rcd.sign:null, rst:"+f+",txid:"+txid);
+ return txid;
+ }
+ /**
+ * 取证
+ *
+ * @param txid 交易编号
+ * @return 存证内容
+ * @throws Exception
+ */
+ public String queryCert(String txid) throws Exception {
+ Transaction tx = restNode.getRawTransaction(txid);
+ if(tx instanceof RecordTransaction) {
+ RecordTransaction rr = (RecordTransaction) tx;
+ return new String(rr.recordData);
+ }
+ return null;
+ }
+
+
+ /**
+ *
+ * 取证
+ *
+ * 每次调用存证接口时会返回交易ID,使用该值可以查询存证内容。
+ * 查询结果是JSON串:{"data": "This is test data.", "time": "2018-04-17 16:48:50.325"}
+ * 查询不到时直接返回null
+ * @param txid 交易ID
+ * @return 查询结果
+ */
+ public String queryCertInfo(String txid) {
+ JSONObject json = JSONObject.parseObject("{'data': null, 'time': null}");
+ try {
+ Transaction tx = restNode.getRawTransaction(txid);
+ if(tx instanceof RecordTransaction) {
+ RecordTransaction rr = (RecordTransaction) tx;
+
+ String data = new String(rr.recordData);
+ if(data != null && data.length() > 0) {
+ json.put("data", data);
+
+ if(rr.attributes.length > 0 && rr.attributes[0] != null && rr.attributes[0].data != null) {
+ String attr = new String(rr.attributes[0].data);
+ json.put("time", attr.substring(0, 23));
+ }
+ return json.toJSONString();
+ }
+ }
+ } catch (RestException e) {
+ e.printStackTrace();
+ print(e.getMessage());
+ }
+
+ return null;
+ }
+
+
+ // 获取账户
+ public Account getAccount(String address) {
+ return uw.getAccount(uw.getContract(address).publicKeyHash);
+ }
+ // 获取地址
+ private UInt160 getAddress(String address) {
+ return Wallet.toScriptHash(address);
+ }
+
+ /**
+ * 等待该笔交易同步至账户管理器中
+ *
+ * @param txid
+ */
+ public void wait(String txid) {
+ wait(uw, txid);
+ }
+ // 等待Tx生效
+ private void wait(IUserManager uw, String txid) {
+ int count = 3; // 最长等待1分钟
+ while(--count > 0) {
+ Map txs = uw.LoadTransactions();
+ if(txs != null && txs.keySet().stream().filter(p -> txs.get(p).intValue() > 1).filter(p -> p.hash().toString().equals(txid)).count() == 1) {
+ print("sync finish, txid:"+txid);
+ return;
+ }
+ try {
+ Thread.sleep(1000*5);
+ } catch (InterruptedException e) {
+ }
+ print("sleep.....5s");
+ }
+ print("sync timeout,txid:"+txid);
+ }
+
+ private RegisterTransaction getRegTx(Account acc, String assetName, long assetAmount, String txDesc, AssetType assetType, String controller, int precision) {
+ RegisterTransaction tx = new RegisterTransaction();
+
+ tx.precision = (byte) precision; // 精度
+ tx.assetType = AssetType.Token; // 资产类型
+ tx.recordType = RecordType.UTXO; // 记账模式
+ tx.nonce = (int)Math.random()*10; // 随机数
+
+ tx.assetType = assetType ;
+ tx.name = assetName;
+ tx.description = txDesc;
+ tx.amount = Fixed8.parse(String.valueOf(assetAmount));
+ tx.issuer = acc.publicKey;
+ tx.admin = Wallet.toScriptHash(controller);
+ tx.outputs = new TransactionOutput[0];
+ if(txDesc != null && txDesc.length() > 0) {
+ tx.attributes = new TransactionAttribute[1];
+ tx.attributes[0] = new TransactionAttribute();
+ tx.attributes[0].usage = TransactionAttributeUsage.Description;
+ tx.attributes[0].data = toAttr(txDesc);
+ }
+ return tx;
+ }
+ private byte[] generateKey64Bit() {
+ return ECC.generateKey(64);
+ }
+ private IssueTransaction getIssTx(String assetId, long assetAmount, String recvAddr, String txDesc) {
+ IssueTransaction tx = new IssueTransaction();
+ tx.outputs = new TransactionOutput[1];
+ tx.outputs[0] = new TransactionOutput();
+ tx.outputs[0].assetId = UInt256.parse(assetId);
+ tx.outputs[0].value = Fixed8.parse(String.valueOf(assetAmount));
+ tx.outputs[0].scriptHash = Wallet.toScriptHash(recvAddr);
+ int len = txDesc != null && txDesc.length() > 0 ? 2:1;
+ tx.attributes = new TransactionAttribute[len];
+ tx.attributes[0] = new TransactionAttribute();
+ tx.attributes[0].usage = TransactionAttributeUsage.Description;
+ tx.attributes[0].data = generateKey64Bit(); // 标识区分不同txid
+ for(int i=1; i recvlist, String txDesc) {
+ int size = recvlist.size();
+ IssueTransaction tx = new IssueTransaction();
+ tx.outputs = new TransactionOutput[1];
+ tx.outputs = new TransactionOutput[size];
+ for(int i=0; i 0 ? 2:1;
+ tx.attributes = new TransactionAttribute[len];
+ tx.attributes[0] = new TransactionAttribute();
+ tx.attributes[0].usage = TransactionAttributeUsage.Description;
+ tx.attributes[0].data = generateKey64Bit(); // 标识区分不同txid
+ for(int i=1; i 0) {
+ tx.attributes = new TransactionAttribute[1];
+ tx.attributes[0] = new TransactionAttribute();
+ tx.attributes[0].usage = TransactionAttributeUsage.Description;
+ tx.attributes[0].data = toAttr(txDesc);
+ } else {
+ tx.attributes = new TransactionAttribute[0];
+ }
+ return tx;
+ }
+
+ private TransferTransaction getTrfTx(List recvList, String txDesc) {
+ int size = recvList.size();
+ TransferTransaction tx = new TransferTransaction();
+ tx.outputs = new TransactionOutput[size];
+ for(int i=0; i 0) {
+ tx.attributes = new TransactionAttribute[1];
+ tx.attributes[0] = new TransactionAttribute();
+ tx.attributes[0].usage = TransactionAttributeUsage.Description;
+ tx.attributes[0].data = toAttr(txDesc);
+ } else {
+ tx.attributes = new TransactionAttribute[0];
+ }
+ return tx;
+ }
+
+ private RecordTransaction getRcdTx(String data, String txDesc) {
+ RecordTransaction rcdTx = new RecordTransaction();
+ rcdTx.recordType = "";
+ rcdTx.recordData = data.getBytes();
+ rcdTx.outputs = new TransactionOutput[0];
+ rcdTx.inputs = new TransactionInput[0];
+ rcdTx.attributes = new TransactionAttribute[0];
+ if(txDesc != null && txDesc.length() > 0) {
+ rcdTx.attributes = new TransactionAttribute[1];
+ rcdTx.attributes[0] = new TransactionAttribute();
+ rcdTx.attributes[0].usage = TransactionAttributeUsage.Description;
+ rcdTx.attributes[0].data = toAttr(txDesc);
+ } else {
+ rcdTx.attributes = new TransactionAttribute[0];
+ }
+ rcdTx.scripts = new Program[0];
+ return rcdTx;
+ }
+
+ /**
+ * 获取账户信息
+ *
+ * @param address
+ * @return
+ */
+ public AccountInfo getAccountInfo(String address) {
+ AccountInfo info = new AccountInfo();
+ Contract con = uw.getContract(address);
+ Account acc = uw.getAccountByScriptHash(Wallet.toScriptHash(address));
+ info.address = con.address();
+ info.pubkey = Helper.toHexString(acc.publicKey.getEncoded(true));
+ info.prikey = Helper.toHexString(acc.privateKey);
+ info.priwif = acc.export();
+ info.pkhash = acc.publicKeyHash.toString();
+ return info;
+ }
+
+ /**
+ * 获取账户资产
+ *
+ * @param address
+ * @return
+ */
+ public AccountAsset getAccountAsset(String address) {
+ AccountAsset asset = new AccountAsset();
+ Contract con = uw.getContract(address);
+ asset.address = con.address();
+ asset.canUseAssets = new ArrayList();
+ asset.freezeAssets = new ArrayList();
+ Arrays.stream(uw.findUnspentCoins()).filter(p -> address.equals(Wallet.toAddress(p.scriptHash))).forEach(p -> {
+ Asset as = new Asset();
+ as.assetid = p.assetId.toString();
+ as.amount = p.value.toLong();
+ asset.canUseAssets.add(as);
+ });
+ Arrays.stream(uw.findUnconfirmedCoins()).filter(p -> address.equals(Wallet.toAddress(p.scriptHash))).forEach(p -> {
+ Asset as = new Asset();
+ as.assetid = p.assetId.toString();
+ as.amount = p.value.toLong();
+ asset.freezeAssets.add(as);
+ });
+ return asset;
+ }
+ public void print() {
+ Arrays.stream(uw.getCoin()).forEach(p -> {
+ System.out.println("-----------------------------------------------");
+ System.out.println("coin.input.prevHash:"+p.input.prevHash.toString());
+ System.out.println("coin.input.prevIndex:"+p.input.prevIndex);
+ System.out.println("coin.assetid:"+p.assetId.toString());
+ System.out.println("coin.value:"+p.value);
+ System.out.println("coin.scripthash:"+Wallet.toAddress(p.scriptHash));
+ System.out.println("coin.state:"+p.getState());
+ });
+ }
+ public Coin[] getCoin() {
+ return uw.getCoin();
+ }
+
+ /**
+ * 获取资产信息
+ *
+ * @param assetid
+ * @return
+ * @throws RestException
+ */
+ public AssetInfo getAssetInfo(String assetid) throws RestException {
+ String ss = restNode.getAsset(assetid);
+ return JSON.parseObject(ss, AssetInfo.class);
+ }
+
+ /**
+ * 获取交易信息
+ *
+ * @param txid
+ * @return
+ * @throws RestException
+ */
+ public TransactionInfo getTransactionInfo(String txid) throws RestException {
+ TransactionInfo info = new TransactionInfo();
+ info.txid = txid;
+ Transaction tx = restNode.getRawTransaction(txid);
+ if(tx instanceof RegisterTransaction) {
+ info.type = RegisterTransaction.class.getSimpleName();
+ } else if(tx instanceof IssueTransaction) {
+ info.type = IssueTransaction.class.getSimpleName();
+ } else if(tx instanceof TransferTransaction) {
+ info.type = TransferTransaction.class.getSimpleName();
+ } else if(tx instanceof RecordTransaction) {
+ info.type = RecordTransaction.class.getSimpleName();
+ }
+
+ info.inputs = new ArrayList();
+ Arrays.stream(tx.inputs).map(p -> getTxByNextTxInput(p)).forEach(p -> {
+ TxInputInfo in = new TxInputInfo();
+ in.address = Wallet.toAddress(p.scriptHash);
+ in.assetid = p.assetId.toString();
+ in.amount = p.value.toLong();
+ info.inputs.add(in);
+ });
+ info.outputs = new ArrayList();
+ Arrays.stream(tx.outputs).forEach(p -> {
+ TxOutputInfo out = new TxOutputInfo();
+ out.address = Wallet.toAddress(p.scriptHash);
+ out.assetid = p.assetId.toString();
+ out.amount = p.value.toLong();
+ info.outputs.add(out);
+ });
+ StringBuilder sb = new StringBuilder();
+ for(TransactionAttribute attr: tx.attributes) {
+ sb.append(Helper.toHexString(attr.data));
+ }
+ if(sb.toString().length() > 0) {
+ info.attrs = new String(Helper.hexToBytes(sb.toString()));
+ }
+ return info;
+ }
+ private TransactionOutput getTxByNextTxInput(TransactionInput input){
+ Transaction tx;
+ try {
+ tx = restNode.getRawTransaction(input.prevHash.toString());
+ } catch (RestException e) {
+ throw new RuntimeException("Not find tx by next txInput");
+ }
+ return tx.outputs[input.prevIndex];
+ }
+
+ public String getStateUpdate(String namespace, String key) {
+ try {
+ return restNode.getStateUpdate(namespace, key);
+ } catch (RestException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void print(String ss) {
+ System.out.println(now() + ss);
+ }
+
+ private byte[] toAttr(String txDesc) {
+ return (now() + " " + txDesc).getBytes();
+ }
+
+ public static String now() {
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date());
+ }
+}