USDT离线签名

常量请参考https://blog.csdn.net/liu1765686161/article/details/79655332


	/**
	 * usdt 离线签名 并 发送交易
	 * 
	 * @param fromAddress
	 *            :转出地址
	 * @param toAddress
	 *            :接收地址
	 * @param changeAddress
	 *            : 找零地址
	 * @param value
	 *            :转账金额
	 * @return hash
	 */
	public String rawSignAndSend(String fromAddress, String toAddress,
			String changeAddress, double value) throws Exception {
		List utxos = new ArrayList();
		if (mainAddress.equals(fromAddress)) {
			utxos = this.getUnspent(fromAddress);
		} else {
			utxos = this.getUnspent(fromAddress, toAddress);
		}
		// 转账金额 格式转化
		Long amount = BigDecimalUtil.inputConvert(value);
		// 获取手续费
		Long fee = this.getOmniFee(utxos);

		NetworkParameters networkParameters = isMainNet ? MainNetParams.get()
				: TestNet3Params.get();
		Transaction tran = new Transaction(networkParameters);
		if (utxos == null || utxos.size() == 0) {
			throw new Exception("utxo为空");
		}
		// 这是比特币的限制最小转账金额,所以很多usdt转账会收到一笔0.00000546的btc
		Long miniBtc = 546L;
		tran.addOutput(Coin.valueOf(miniBtc),
				Address.fromBase58(networkParameters, toAddress));

		// 构建usdt的输出脚本 注意这里的金额是要乘10的8次方
		String usdtHex = "6a146f6d6e69" + String.format("%016x", propertyid)
				+ String.format("%016x", amount);
		tran.addOutput(Coin.valueOf(0L), new Script(Utils.HEX.decode(usdtHex)));

		// 如果有找零就添加找零
		Long changeAmount = 0L;
		Long utxoAmount = 0L;
		List needUtxo = new ArrayList();
		// 过滤掉多的utxo
		for (UTXO utxo : utxos) {
			if (utxoAmount > (fee + miniBtc)) {
				break;
			} else {
				needUtxo.add(utxo);
				utxoAmount += utxo.getValue().value;
			}
		}
		changeAmount = utxoAmount - (fee + miniBtc);
		// 余额判断
		if (changeAmount < 0) {
			throw new Exception("utxo余额不足");
		}
		if (changeAmount > 0) {
			tran.addOutput(Coin.valueOf(changeAmount),
					Address.fromBase58(networkParameters, changeAddress));
		}
		// 先添加未签名的输入,也就是utxo
		for (UTXO utxo : needUtxo) {
			tran.addInput(utxo.getHash(), utxo.getIndex(), utxo.getScript())
					.setSequenceNumber(TransactionInput.NO_SEQUENCE - 2);
		}
		//解锁钱包
		if (!this.walletpassphrase()) {
			log.error("解锁钱包失败");
		}
		// 下面就是签名
		for (int i = 0; i < needUtxo.size(); i++) {
			String addr = needUtxo.get(i).getAddress();
			String privateKey = this.getPrivateKey(addr);

			ECKey ecKey = DumpedPrivateKey.fromBase58(networkParameters,
					privateKey).getKey();
			TransactionInput transactionInput = tran.getInput(i);
			Script scriptPubKey = ScriptBuilder.createOutputScript(Address
					.fromBase58(networkParameters, addr));
			Sha256Hash hash = tran.hashForSignature(i, scriptPubKey,
					Transaction.SigHash.ALL, false);
			ECKey.ECDSASignature ecSig = ecKey.sign(hash);
			TransactionSignature txSig = new TransactionSignature(ecSig,
					Transaction.SigHash.ALL, false);
			transactionInput.setScriptSig(ScriptBuilder.createInputScript(
					txSig, ecKey));
		}
		// 这是签名之后的原始交易,直接去广播就行了
		String signedHex = Hex.toHexString(tran.bitcoinSerialize());
		log.info("签名之后的原始交易:{}", signedHex);
		// 这是交易的hash
		String txHash = Hex.toHexString(Utils.reverseBytes(Sha256Hash
				.hash(Sha256Hash.hash(tran.bitcoinSerialize()))));
		log.info("交易数据fee:{},utxoAmount:{},changeAmount:{},hash:{}", fee,
				utxoAmount, changeAmount, txHash);
		JSONObject json = doRequest("sendrawtransaction", signedHex);
		if (isError(json)) {
			log.error("发送交易失败");
			return null;
		} else {
			String result = json.getString("result");
			log.info("发送成功 hash:{}", result);
			return result;
		}
	}
public String getPrivateKey(String address) {
		JSONObject json = doRequest("dumpprivkey", address);
		if (isError(json)) {
			log.error("获取私钥失败:", address);
			return null;
		} else {
			return json.getString("result");
		}
	}
/**
	 * 获取矿工费用
	 * 
	 * @param utxos
	 * @return
	 */
	public Long getOmniFee(List utxos) {
		Long miniBtc = 546L;
		Long feeRate = getFeeRate();
		Long utxoAmount = 0L;
		Long fee = 0L;
		Long utxoSize = 0L;
		for (UTXO output : utxos) {
			utxoSize++;
			if (utxoAmount >= (fee + miniBtc)) {
				break;
			} else {
				utxoAmount += output.getValue().value;
				fee = (utxoSize * 148 + 34 * 3 + 10) * feeRate;
			}
		}
		return fee;
	}

	/***
	 * 获取未消费列表
	 * 
	 * @param address
	 *            :地址  地址顺序很重要
	 * @return
	 */
	public List getUnspent(String... address) {
		List utxos = Lists.newArrayList();
		try {
			/**
			 * { "txid":
			 * "62906596e41aa041fadfb1f0cfb531137603ab1670515de28028b201dbf55bf5"
			 * , "vout": 1, "address": "n2RXasuis8pCQScDQjDV5vhaR7pBCvNUnB",
			 * "account": "btc2", "scriptPubKey":
			 * "76a914e553ef5218b38b86d1d4670dd4bd65d270f28d6e88ac", "amount":
			 * 0.00010000, "confirmations": 127231, "spendable": true,
			 * "solvable": true }
			 */
			JSONObject jsonObject = doRequest("listunspent", 0,
					99999999, address);
			JSONArray outputs = jsonObject.getJSONArray("result");
			if (outputs == null || outputs.size() == 0) {
				System.out.println("交易异常,余额不足");
			}
			for (int i = 0; i < outputs.size(); i++) {
				JSONObject outputsMap = outputs.getJSONObject(i);

				String tx_hash = outputsMap.getString("txid");
				String scriptPubKey = outputsMap.getString("scriptPubKey");
				Double amount = outputsMap.getDouble("amount");
				Long value = BigDecimalUtil.inputConvert(amount);
				String vout = outputsMap.getString("vout");
				String addr = outputsMap.getString("address");

				UTXO utxo = new UTXO(Sha256Hash.wrap(tx_hash),
						Long.valueOf(vout), Coin.valueOf(Long.valueOf(value)),
						0, false, new Script(Hex.decode(scriptPubKey)), addr);
				System.out.println(utxo.getAddress());
				utxos.add(utxo);
			}
			return utxos;
		} catch (Exception e) {
			log.error("【BTC获取未消费列表】失败,", e);
			return null;
		}

	}

	/**
	 * 获取btc费率
	 *
	 * @return
	 */
	public Long getFeeRate() {
		try {
			String re = HttpUtil
					.get("https://bitcoinfees.earn.com/api/v1/fees/recommended");
			JSONObject json = JSON.parseObject(re);
			return Long.valueOf(json.get("fastestFee").toString());
		} catch (Exception e) {
			e.printStackTrace();
			return 12L;
		}
	}

 

你可能感兴趣的:(区块链,USDT,离线签名,USDT,USDT离线签名)