首先大家需要先引入相关依赖包,这个maven里面是没有的,需要我们自行导入才可以。在项目路径下面创建lib,将所有需要使用的包导入即可。给大家一个包的下载链接:https://download.csdn.net/download/qq_38935605/89715772
因为放在CSDN方便点,但是如果需要付费或者开会员的话大家也可以私信或者留言作者获取jar包也是可以的。
导入到项目以后下面开发导入maven依赖:
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk18on</artifactId> <version>1.76</version> </dependency> <dependency> <groupId>com.github.tronprotocol</groupId> <artifactId>tron-wallet-cli</artifactId> <version>1.2.0</version> <scope>system</scope> <systemPath>${project.basedir}/src/main/resources/lib/tron-wallet-cli.jar</systemPath> </dependency> <dependency> <groupId>com.github.tronprotocol</groupId> <artifactId>ron-protobuf</artifactId> <version>1.0</version> <scope>system</scope> <systemPath>${project.basedir}/src/main/resources/lib/tron-protobuf-1.0-SNAPSHOT.jar</systemPath> </dependency> <!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java --> <dependency> <groupId>com.google.protobuf</groupId> <artifactId>protobuf-java</artifactId> <version>3.5.1</version> </dependency> <!-- https://mvnrepository.com/artifact/com.madgag.spongycastle/core --> <dependency> <groupId>com.madgag.spongycastle</groupId> <artifactId>core</artifactId> <version>1.58.0.0</version> </dependency> <!-- https://mvnrepository.com/artifact/com.madgag.spongycastle/prov --> <dependency> <groupId>com.madgag.spongycastle</groupId> <artifactId>prov</artifactId> <version>1.58.0.0</version> </dependency> <!-- https://mvnrepository.com/artifact/com.typesafe/config --> <dependency> <groupId>com.typesafe</groupId> <artifactId>config</artifactId> <version>1.3.2</version> </dependency> <!-- https://mvnrepository.com/artifact/io.grpc/grpc-stub --> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.9.0</version> </dependency> <!-- https://mvnrepository.com/artifact/io.grpc/grpc-protobuf --> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.9.0</version> </dependency> <!-- https://mvnrepository.com/artifact/io.grpc/grpc-netty --> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty</artifactId> <version>1.9.0</version> </dependency><dependency> <groupId>org.web3j</groupId> <artifactId>core</artifactId> <version>5.0.0</version> </dependency>
生成ETH钱包地址以及私钥:
package com.app.web.service.impl;
import com.app.web.service.EthService;
import org.springframework.stereotype.Service;
import org.web3j.crypto.*;
import java.math.BigInteger;
import java.security.*;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;
/**
* <p>
* 资产 服务实现类
* </p>
*
* @author HayDen
* @since 2024-06-24
*/
@Service
public class EthServiceImpl implements EthService {
@Override
public String getEthAddress() {
try {
ECKeyPair ecKeyPair = Keys.createEcKeyPair();
BigInteger privateKeyInDec = ecKeyPair.getPrivateKey();
String privateKey = privateKeyInDec.toString(16);
if(privateKey.length() != 64){
return getEthAddress();
}
WalletFile aWallet = Wallet.createLight(randomUUID(), ecKeyPair);
String address = aWallet.getAddress();
if (address.startsWith("0x")) {
address = address.substring(2).toLowerCase();
} else {
address = address.toLowerCase();
}
address = "0x" + address;
System.out.println("地址:" + address);
System.out.println("秘钥:" + privateKey);
} catch (InvalidAlgorithmParameterException |
CipherException | NoSuchProviderException | NoSuchAlgorithmException e) {
System.out.println(e.getCause().toString());
}
return "";
}
public static String randomUUID() {
ThreadLocalRandom random = ThreadLocalRandom.current();
return (new UUID(random.nextLong(), random.nextLong())).toString().replace("-", "");
}
}
通过上面ETH生成的私钥可以在生成TRX钱包地址(可以共用一套私钥),或者可以使用上面生成私钥以后再获取到TRX地址也是一样的:
package com.app.web.service;
import com.app.db.entity.Vo.MyIntegralVo;
import com.app.db.entity.Vo.RecommendVo;
import com.app.db.entity.Vo.TeamRewardVo;
import java.util.List;
public interface TrxService {
/**
* 根据私钥获取TRX钱包地址
* @param privateKey
* @return
*/
String getTrxAddress(String privateKey);
}
package com.app.web.service.impl;
import com.app.common.util.ECDSAUtil;
import com.app.web.service.TrxService;
import com.app.web.trx.TronUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.fasterxml.jackson.databind.JsonNode;
import org.bitcoinj.core.Base58;
import org.bouncycastle.crypto.digests.KeccakDigest;
import org.bouncycastle.jcajce.provider.digest.Keccak;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.tron.common.crypto.ECKey;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.Keys;
import java.math.BigInteger;
import java.security.KeyPair;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.tron.walletserver.WalletApi.encode58Check;
/**
* <p>
* 资产 服务实现类
* </p>
*
* @author HayDen
* @since 2024-06-24
*/
@Service
public class TrxServiceImpl implements TrxService {
@Override
public String getTrxAddress(String privateKey) {
return getAddressByPrivateKey(privateKey,null);
}
/**
* 根据私钥获取钱包地址
* @param privateKey 私钥
* @param privateKeybase58 base58类型的私钥
* @return
*/
public String getAddressByPrivateKey(String privateKey,String privateKeybase58) {
String authAddress="";
if(StringUtils.isNotEmpty(privateKeybase58)){
byte[] base58Str= Base58.decode(privateKeybase58);
privateKey = Hex.toHexString(base58Str);
authAddress = TronUtils.getAddressByPrivateKey(privateKey);
}
else {
authAddress = TronUtils.getAddressByPrivateKey(privateKey);
}
return authAddress;
}
}
package com.app.web.trx;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.protobuf.Any;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.spongycastle.crypto.digests.SM3Digest;
import org.spongycastle.util.encoders.Hex;
import org.tron.common.crypto.ECKey;
import org.tron.common.utils.Base58;
import org.tron.common.utils.ByteArray;
import org.tron.common.utils.JsonFormat;
import org.tron.protos.Protocol.Transaction;
import org.tron.protos.contract.*;
import sun.misc.BASE64Decoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@Slf4j
public class TronUtils {
static int ADDRESS_SIZE = 21;
private static byte addressPreFixByte = (byte) 0x41; // 41 + address (byte) 0xa0; //a0 + address
public static String toHexAddress(String tAddress) {
return ByteArray.toHexString(decodeFromBase58Check(tAddress));
}
private static byte[] decodeFromBase58Check(String addressBase58) {
if (StringUtils.isEmpty(addressBase58)) {
return null;
}
byte[] address = decode58Check(addressBase58);
if (!addressValid(address)) {
return null;
}
return address;
}
public static byte[] decode58Check(String input) {
byte[] decodeCheck = Base58.decode(input);
if (decodeCheck.length <= 4) {
return null;
}
byte[] decodeData = new byte[decodeCheck.length - 4];
System.arraycopy(decodeCheck, 0, decodeData, 0, decodeData.length);
byte[] hash0 = Sha256Hash.hash(true, decodeData);
byte[] hash1 = Sha256Hash.hash(true, hash0);
if (hash1[0] == decodeCheck[decodeData.length] && hash1[1] == decodeCheck[decodeData.length + 1]
&& hash1[2] == decodeCheck[decodeData.length + 2] && hash1[3] == decodeCheck[decodeData.length + 3]) {
return decodeData;
}
return null;
}
private static boolean addressValid(byte[] address) {
if (ArrayUtils.isEmpty(address)) {
return false;
}
if (address.length != ADDRESS_SIZE) {
return false;
}
byte preFixbyte = address[0];
return preFixbyte == getAddressPreFixByte();
// Other rule;
}
private static byte getAddressPreFixByte() {
return addressPreFixByte;
}
public static void main(String args[]) throws Exception {
String priv="BFE7VsiBzHtYpC2x2FJn8V81NQ54w6MUjMxX2aGruNySbsbz88v18yoyFHB2hvQyN7h8Cx97NeLUXd3vXtU6uQDc";
}
/**
* BASE64解密
* @throws Exception
*/
public static byte[] decryptBASE64(String key) throws Exception {
return (new BASE64Decoder()).decodeBuffer(key);
}
public static String getAddressByPrivateKey( byte[] privateBytes) {
ECKey ecKey = ECKey.fromPrivate(privateBytes);
byte[] from = ecKey.getAddress();
return toViewAddress(Hex.toHexString(from));
}
/**
* 根据私钥获取地址
*
* @param privateKey
* @return
*/
public static String getAddressByPrivateKey(String privateKey) {
byte[] privateBytes = Hex.decode(privateKey);
ECKey ecKey = ECKey.fromPrivate(privateBytes);
byte[] from = ecKey.getAddress();
return toViewAddress(Hex.toHexString(from));
}
/**
* 转换成T开头的地址
* @param hexAddress
* @return
*/
public static String toViewAddress(String hexAddress) {
return encode58Check(org.tron.common.utils.ByteArray.fromHexString(hexAddress));
}
public static String encode58Check(byte[] input) {
try {
byte[] hash0 = hash(true, input);
byte[] hash1 = hash(true, hash0);
byte[] inputCheck = new byte[input.length + 4];
System.arraycopy(input, 0, inputCheck, 0, input.length);
System.arraycopy(hash1, 0, inputCheck, input.length, 4);
return Base58.encode(inputCheck);
} catch (Throwable t) {
log.error(String.format("data error:%s", Hex.toHexString(input)), t);
}
return null;
}
/**
* Calculates the SHA-256 hash of the given bytes.
*
* @param input the bytes to hash
* @return the hash (in big-endian order)
*/
public static byte[] hash(boolean isSha256, byte[] input) throws NoSuchAlgorithmException {
return hash(isSha256, input, 0, input.length);
}
/**
* Calculates the SHA-256 hash of the given byte range.
*
* @param input the array containing the bytes to hash
* @param offset the offset within the array of the bytes to hash
* @param length the number of bytes to hash
* @return the hash (in big-endian order)
*/
public static byte[] hash(boolean isSha256, byte[] input, int offset, int length) throws NoSuchAlgorithmException {
if (isSha256) {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(input, offset, length);
return digest.digest();
} else {
SM3Digest digest = new SM3Digest();
digest.update(input, offset, length);
byte[] eHash = new byte[digest.getDigestSize()];
digest.doFinal(eHash, 0);
return eHash;
}
}
/**
* 报装成transaction
*
* @param strTransaction
* @return
*/
public static Transaction packTransaction(String strTransaction) {
JSONObject jsonTransaction = JSONObject.parseObject(strTransaction);
JSONObject rawData = jsonTransaction.getJSONObject("raw_data");
JSONArray contracts = new JSONArray();
JSONArray rawContractArray = rawData.getJSONArray("contract");
for (int i = 0; i < rawContractArray.size(); i++) {
try {
JSONObject contract = rawContractArray.getJSONObject(i);
JSONObject parameter = contract.getJSONObject("parameter");
String contractType = contract.getString("type");
Any any = null;
switch (contractType) {
case "AccountCreateContract":
AccountContract.AccountCreateContract.Builder accountCreateContractBuilder = AccountContract.AccountCreateContract
.newBuilder();
JsonFormat.merge(parameter.getJSONObject("value").toString(),
accountCreateContractBuilder);
any = Any.pack(accountCreateContractBuilder.build());
break;
case "TransferContract":
BalanceContract.TransferContract.Builder transferContractBuilder = BalanceContract.TransferContract.newBuilder();
JsonFormat
.merge(parameter.getJSONObject("value").toString(), transferContractBuilder);
any = Any.pack(transferContractBuilder.build());
break;
case "TransferAssetContract":
AssetIssueContractOuterClass.TransferAssetContract.Builder transferAssetContractBuilder = AssetIssueContractOuterClass.TransferAssetContract
.newBuilder();
JsonFormat.merge(parameter.getJSONObject("value").toString(),
transferAssetContractBuilder);
any = Any.pack(transferAssetContractBuilder.build());
break;
case "VoteAssetContract":
VoteAssetContractOuterClass.VoteAssetContract.Builder voteAssetContractBuilder = VoteAssetContractOuterClass.VoteAssetContract.newBuilder();
JsonFormat
.merge(parameter.getJSONObject("value").toString(), voteAssetContractBuilder);
any = Any.pack(voteAssetContractBuilder.build());
break;
case "VoteWitnessContract":
WitnessContract.VoteWitnessContract.Builder voteWitnessContractBuilder = WitnessContract.VoteWitnessContract
.newBuilder();
JsonFormat
.merge(parameter.getJSONObject("value").toString(), voteWitnessContractBuilder);
any = Any.pack(voteWitnessContractBuilder.build());
break;
case "WitnessCreateContract":
WitnessContract.WitnessCreateContract.Builder witnessCreateContractBuilder = WitnessContract.WitnessCreateContract
.newBuilder();
JsonFormat.merge(parameter.getJSONObject("value").toString(),
witnessCreateContractBuilder);
any = Any.pack(witnessCreateContractBuilder.build());
break;
case "AssetIssueContract":
AssetIssueContractOuterClass.AssetIssueContract.Builder assetIssueContractBuilder = AssetIssueContractOuterClass.AssetIssueContract.newBuilder();
JsonFormat
.merge(parameter.getJSONObject("value").toString(), assetIssueContractBuilder);
any = Any.pack(assetIssueContractBuilder.build());
break;
case "WitnessUpdateContract":
WitnessContract.WitnessUpdateContract.Builder witnessUpdateContractBuilder = WitnessContract.WitnessUpdateContract
.newBuilder();
JsonFormat.merge(parameter.getJSONObject("value").toString(),
witnessUpdateContractBuilder);
any = Any.pack(witnessUpdateContractBuilder.build());
break;
case "ParticipateAssetIssueContract":
AssetIssueContractOuterClass.ParticipateAssetIssueContract.Builder participateAssetIssueContractBuilder =
AssetIssueContractOuterClass.ParticipateAssetIssueContract.newBuilder();
JsonFormat.merge(parameter.getJSONObject("value").toString(),
participateAssetIssueContractBuilder);
any = Any.pack(participateAssetIssueContractBuilder.build());
break;
case "AccountUpdateContract":
AccountContract.AccountUpdateContract.Builder accountUpdateContractBuilder = AccountContract.AccountUpdateContract
.newBuilder();
JsonFormat.merge(parameter.getJSONObject("value").toString(),
accountUpdateContractBuilder);
any = Any.pack(accountUpdateContractBuilder.build());
break;
case "FreezeBalanceContract":
BalanceContract.FreezeBalanceContract.Builder freezeBalanceContractBuilder = BalanceContract.FreezeBalanceContract
.newBuilder();
JsonFormat.merge(parameter.getJSONObject("value").toString(),
freezeBalanceContractBuilder);
any = Any.pack(freezeBalanceContractBuilder.build());
break;
case "UnfreezeBalanceContract":
BalanceContract.UnfreezeBalanceContract.Builder unfreezeBalanceContractBuilder = BalanceContract.UnfreezeBalanceContract
.newBuilder();
JsonFormat.merge(parameter.getJSONObject("value").toString(),
unfreezeBalanceContractBuilder);
any = Any.pack(unfreezeBalanceContractBuilder.build());
break;
case "UnfreezeAssetContract":
AssetIssueContractOuterClass.UnfreezeAssetContract.Builder unfreezeAssetContractBuilder = AssetIssueContractOuterClass.UnfreezeAssetContract
.newBuilder();
JsonFormat.merge(parameter.getJSONObject("value").toString(),
unfreezeAssetContractBuilder);
any = Any.pack(unfreezeAssetContractBuilder.build());
break;
case "WithdrawBalanceContract":
BalanceContract.WithdrawBalanceContract.Builder withdrawBalanceContractBuilder = BalanceContract.WithdrawBalanceContract
.newBuilder();
JsonFormat.merge(parameter.getJSONObject("value").toString(),
withdrawBalanceContractBuilder);
any = Any.pack(withdrawBalanceContractBuilder.build());
break;
case "UpdateAssetContract":
AssetIssueContractOuterClass.UpdateAssetContract.Builder updateAssetContractBuilder = AssetIssueContractOuterClass.UpdateAssetContract
.newBuilder();
JsonFormat
.merge(parameter.getJSONObject("value").toString(), updateAssetContractBuilder);
any = Any.pack(updateAssetContractBuilder.build());
break;
case "SmartContract":
SmartContractOuterClass.SmartContract.Builder smartContractBuilder = SmartContractOuterClass.SmartContract.newBuilder();
JsonFormat
.merge(parameter.getJSONObject("value").toString(), smartContractBuilder);
any = Any.pack(smartContractBuilder.build());
break;
case "TriggerSmartContract":
SmartContractOuterClass.TriggerSmartContract.Builder triggerSmartContractBuilder = SmartContractOuterClass.TriggerSmartContract
.newBuilder();
JsonFormat
.merge(parameter.getJSONObject("value").toString(),
triggerSmartContractBuilder);
any = Any.pack(triggerSmartContractBuilder.build());
break;
// todo add other contract
default:
}
if (any != null) {
String value = Hex.toHexString(any.getValue().toByteArray());
parameter.put("value", value);
contract.put("parameter", parameter);
contracts.add(contract);
}
} catch (Exception e) {
e.printStackTrace();
;
}
}
rawData.put("contract", contracts);
jsonTransaction.put("raw_data", rawData);
Transaction.Builder transactionBuilder = Transaction.newBuilder();
try {
JsonFormat.merge(jsonTransaction.toString(), transactionBuilder);
return transactionBuilder.build();
} catch (Exception e) {
return null;
}
}
}
package com.app.web.trx;
/*
* Copyright 2011 Google Inc.
* Copyright 2014 Andreas Schildbach
*
* 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.
*/
import com.google.common.io.ByteStreams;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import org.spongycastle.crypto.digests.SM3Digest;
import org.tron.common.utils.ByteArray;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.Serializable;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import static com.google.common.base.Preconditions.checkArgument;
/**
* A Sha256Hash just wraps a byte[] so that equals and hashcode work correctly, allowing it to be
* used as keys in a map. It also checks that the length is correct and provides a bit more type
* safety.
*/
public class Sha256Hash implements Serializable, Comparable<Sha256Hash> {
public static final int LENGTH = 32; // bytes
public static final Sha256Hash ZERO_HASH = wrap(new byte[LENGTH]);
private final byte[] bytes;
public Sha256Hash(long num, byte[] hash) {
byte[] rawHashBytes = this.generateBlockId(num, hash);
checkArgument(rawHashBytes.length == LENGTH);
this.bytes = rawHashBytes;
}
public Sha256Hash(long num, Sha256Hash hash) {
byte[] rawHashBytes = this.generateBlockId(num, hash);
checkArgument(rawHashBytes.length == LENGTH);
this.bytes = rawHashBytes;
}
/**
* Use {@link #wrap(byte[])} instead.
*/
@Deprecated
public Sha256Hash(byte[] rawHashBytes) {
checkArgument(rawHashBytes.length == LENGTH);
this.bytes = rawHashBytes;
}
/**
* Creates a new instance that wraps the given hash value.
*
* @param rawHashBytes the raw hash bytes to wrap
* @return a new instance
* @throws IllegalArgumentException if the given array length is not exactly 32
*/
@SuppressWarnings("deprecation") // the constructor will be made private in the future
public static Sha256Hash wrap(byte[] rawHashBytes) {
return new Sha256Hash(rawHashBytes);
}
/**
*/
@Deprecated
public static Sha256Hash create(boolean isSha256, byte[] contents) {
return of(isSha256, contents);
}
/**
* Creates a new instance containing the calculated (one-time) hash of the given bytes.
*
* @param contents the bytes on which the hash value is calculated
* @return a new instance containing the calculated (one-time) hash
*/
public static Sha256Hash of(boolean isSha256, byte[] contents) {
return wrap(hash(isSha256, contents));
}
/**
* Creates a new instance containing the calculated (one-time) hash of the given file's contents.
* The file contents are read fully into memory, so this method should only be used with small
* files.
*
* @param file the file on which the hash value is calculated
* @return a new instance containing the calculated (one-time) hash
* @throws IOException if an error occurs while reading the file
*/
public static Sha256Hash of(boolean isSha256, File file) throws IOException {
try (FileInputStream in = new FileInputStream(file)) {
return of(isSha256, ByteStreams.toByteArray(in));
}
}
/**
*/
@Deprecated
public static Sha256Hash createDouble(boolean isSha256, byte[] contents) {
return twiceOf(isSha256, contents);
}
/**
* Creates a new instance containing the hash of the calculated hash of the given bytes.
*
* @param contents the bytes on which the hash value is calculated
* @return a new instance containing the calculated (two-time) hash
*/
public static Sha256Hash twiceOf(boolean isSha256, byte[] contents) {
return wrap(hashTwice(isSha256, contents));
}
/**
* Returns a new SHA-256 MessageDigest instance. This is a convenience method which wraps the
* checked exception that can never occur with a RuntimeException.
*
* @return a new SHA-256 MessageDigest instance
*/
public static MessageDigest newDigest() {
try {
return MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e); // Can't happen.
}
}
/**
* Returns a new SM3 MessageDigest instance. This is a convenience method which wraps the
* checked exception that can never occur with a RuntimeException.
*
* @return a new SM3 MessageDigest instance
*/
public static SM3Digest newSM3Digest() {
return new SM3Digest();
}
/**
* Calculates the SHA-256 hash of the given bytes.
*
* @param input the bytes to hash
* @return the hash (in big-endian order)
*/
public static byte[] hash(boolean isSha256, byte[] input) {
return hash(isSha256, input, 0, input.length);
}
/**
* Calculates the SHA-256 hash of the given byte range.
*
* @param input the array containing the bytes to hash
* @param offset the offset within the array of the bytes to hash
* @param length the number of bytes to hash
* @return the hash (in big-endian order)
*/
public static byte[] hash(boolean isSha256, byte[] input, int offset, int length) {
if (isSha256) {
MessageDigest digest = newDigest();
digest.update(input, offset, length);
return digest.digest();
} else {
SM3Digest digest = newSM3Digest();
digest.update(input, offset, length);
byte[] eHash = new byte[digest.getDigestSize()];
digest.doFinal(eHash, 0);
return eHash;
}
}
/**
* Calculates the SHA-256 hash of the given bytes, and then hashes the resulting hash again.
*
* @param input the bytes to hash
* @return the double-hash (in big-endian order)
*/
public static byte[] hashTwice(boolean isSha256, byte[] input) {
return hashTwice(isSha256, input, 0, input.length);
}
/**
* Calculates the SHA-256 hash of the given byte range, and then hashes the resulting hash again.
*
* @param input the array containing the bytes to hash
* @param offset the offset within the array of the bytes to hash
* @param length the number of bytes to hash
* @return the double-hash (in big-endian order)
*/
public static byte[] hashTwice(boolean isSha256, byte[] input, int offset, int length) {
if (isSha256) {
MessageDigest digest = newDigest();
digest.update(input, offset, length);
return digest.digest(digest.digest());
} else {
SM3Digest digest = newSM3Digest();
digest.update(input, offset, length);
byte[] eHash = new byte[digest.getDigestSize()];
digest.doFinal(eHash, 0);
digest.reset();
digest.update(eHash, 0, eHash.length);
digest.doFinal(eHash, 0);
return eHash;
}
}
/**
* Calculates the hash of hash on the given byte ranges. This is equivalent to concatenating the
*/
public static byte[] hashTwice(boolean isSha256, byte[] input1, int offset1, int length1,
byte[] input2, int offset2, int length2) {
if (isSha256) {
MessageDigest digest = newDigest();
digest.update(input1, offset1, length1);
digest.update(input2, offset2, length2);
return digest.digest(digest.digest());
} else {
SM3Digest digest = newSM3Digest();
digest.update(input1, offset1, length1);
digest.update(input2, offset2, length2);
byte[] eHash = new byte[digest.getDigestSize()];
digest.doFinal(eHash, 0);
return eHash;
}
}
private byte[] generateBlockId(long blockNum, Sha256Hash blockHash) {
byte[] numBytes = Longs.toByteArray(blockNum);
byte[] hash = new byte[blockHash.getBytes().length];
System.arraycopy(numBytes, 0, hash, 0, 8);
System.arraycopy(blockHash.getBytes(), 8, hash, 8, blockHash.getBytes().length - 8);
return hash;
}
private byte[] generateBlockId(long blockNum, byte[] blockHash) {
byte[] numBytes = Longs.toByteArray(blockNum);
byte[] hash = new byte[blockHash.length];
System.arraycopy(numBytes, 0, hash, 0, 8);
System.arraycopy(blockHash, 8, hash, 8, blockHash.length - 8);
return hash;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || !(o instanceof Sha256Hash)) {
return false;
}
return Arrays.equals(bytes, ((Sha256Hash) o).bytes);
}
@Override
public String toString() {
return ByteArray.toHexString(bytes);
}
/**
* Returns the last four bytes of the wrapped hash. This should be unique enough to be a suitable
* hash code even for blocks, where the goal is to try and get the first bytes to be zeros (i.e.
* the value as a big integer lower than the target value).
*/
@Override
public int hashCode() {
// Use the last 4 bytes, not the first 4 which are often zeros in Bitcoin.
return Ints
.fromBytes(bytes[LENGTH - 4], bytes[LENGTH - 3], bytes[LENGTH - 2], bytes[LENGTH - 1]);
}
/**
* Returns the bytes interpreted as a positive integer.
*/
public BigInteger toBigInteger() {
return new BigInteger(1, bytes);
}
/**
* Returns the internal byte array, without defensively copying. Therefore do NOT modify the
* returned array.
*/
public byte[] getBytes() {
return bytes;
}
//
// /**
// * For pb return ByteString.
// */
// public ByteString getByteString() {
// return ByteString.copyFrom(bytes);
// }
@Override
public int compareTo(final Sha256Hash other) {
for (int i = LENGTH - 1; i >= 0; i--) {
final int thisByte = this.bytes[i] & 0xff;
final int otherByte = other.bytes[i] & 0xff;
if (thisByte > otherByte) {
return 1;
}
if (thisByte < otherByte) {
return -1;
}
}
return 0;
}
}