### 前言
(平臺(tái)審核原因,敏感詞匯不能出現(xiàn))
最近因項(xiàng)目使用tron 協(xié)議接入?yún)^(qū)塊鏈,故對(duì)其做了一番研究妆丘,先把相關(guān)資料整理一遍锄俄,供大家學(xué)習(xí)使用;
網(wǎng)上的這部分資料很少勺拣,所以學(xué)習(xí)起來也是遇到了很多困難奶赠,尤其是里面很多新的概念,理解起來有一定的難度药有。比如說去中心化毅戈、地址、加密算法愤惰、算法因子苇经、私鑰含義、助記詞宦言、trc協(xié)議扇单、智能合約、usdt等等奠旺;
很多人接觸區(qū)塊鏈蜘澜,大多是通過接觸usdt這種中充當(dāng)**穩(wěn)定幣**(也稱泰達(dá)幣)角色開始的,usdt是什么响疚,
是一種基本單位貨幣鄙信,即穩(wěn)定幣usdt,是usdt背后發(fā)行者公司每發(fā)行一枚usdt 就要往對(duì)應(yīng)的銀行機(jī)構(gòu)存入對(duì)應(yīng)的法幣忿晕,這樣然后usdt穩(wěn)定幣 發(fā)行者通過存入三方銀行機(jī)構(gòu)法幣扮碧,來保證我的usdt是有保證的,不會(huì)超發(fā)或者失去賠付能力杏糙;
介紹了前面這些背景,再說說其他的蚓土,其實(shí)代幣后面的底層技術(shù)區(qū)塊鏈有很多很多的知識(shí)點(diǎn)宏侍,就不一一細(xì)說了,比如這些幣是怎么發(fā)行的蜀漆,如何運(yùn)轉(zhuǎn)的谅河,如何做到去中心化的;這里主要講講大家經(jīng)常使用trc20 轉(zhuǎn)賬usdt實(shí)現(xiàn)和背后的含義确丢;
> 要明白trc20是一種協(xié)議绷耍,這個(gè)協(xié)議是波場tron鏈下面的一種,還有trx鲜侥,trc10褂始,trc721等等,而波場鏈跟usdt 發(fā)行者公司合作描函,寫了一份智能合約崎苗,該協(xié)議實(shí)現(xiàn)了幾種功能狐粱,如轉(zhuǎn)賬、查詢胆数、授權(quán)肌蜻、事件監(jiān)聽等等,我們在地址中轉(zhuǎn)賬看到的trc20-usdt 就是執(zhí)行了這個(gè)轉(zhuǎn)賬方法 **transfer**必尼,所以能夠把一個(gè)地址中的usdt轉(zhuǎn)移到另一個(gè)地址蒋搜;
```
trc20 協(xié)議中支持的方法
contract TRC20 {
? ? function totalSupply() constant returns (uint theTotalSupply);
? ? function balanceOf(address _owner) constant returns (uint balance);
? ? function transfer(address _to, uint _value) returns (bool success);
? ? function transferFrom(address _from, address _to, uint _value) returns (bool success);
? ? function approve(address _spender, uint _value) returns (bool success);
? ? function allowance(address _owner, address _spender) constant returns (uint remaining);
? ? event Transfer(address indexed _from, address indexed _to, uint _value);
? ? event Approval(address indexed _owner, address indexed _spender, uint _value);
}
```
### Trc20-usdt
要實(shí)現(xiàn)轉(zhuǎn)賬,首先要得有地址判莉,自己地址豆挽,對(duì)方地址,usdt骂租,trx 燃料費(fèi)祷杈;然后這幾個(gè)要素經(jīng)過什么步驟才能達(dá)到目的?
**創(chuàng)建交易 渗饮、離線簽名但汞、廣播**
先把代碼貼出來:
```java
/**
? ? * 發(fā)起trc20轉(zhuǎn)賬 (目標(biāo)地址,數(shù)量,合約地址,私鑰)
? ? * 地址 默認(rèn)為usdt 合約地址
? ? * @throws Throwable
? ? */
? ? public String sendTrc20(String toAddress, BigDecimal amount, String privateKey) throws Throwable {
? ? ? ? String ownerAddress = TronUtils.getAddressByPrivateKey(privateKey);
? ? ? ? JSONObject jsonObject = new JSONObject();
? ? ? ? jsonObject.put("contract_address", TronUtils.toHexAddress(USDT_CPNTRACT));
? ? ? ? jsonObject.put("function_selector", "transfer(address,uint256)");
? ? ? ? List<Type> inputParameters = new ArrayList<>();
? ? ? ? inputParameters.add(new Address(TronUtils.toHexAddress(toAddress).substring(2)));
? ? ? ? inputParameters.add(new Uint256(amount.multiply(decimal).toBigInteger()));
? ? ? ? String parameter = FunctionEncoder.encodeConstructor(inputParameters);
? ? ? ? jsonObject.put("parameter", parameter);
? ? ? ? jsonObject.put("owner_address", TronUtils.toHexAddress(ownerAddress));
? ? ? ? jsonObject.put("call_value", 0);
? ? ? ? jsonObject.put("fee_limit", 6000000L);
? ? ? ? String trans1 = HttpClientUtils.postJson(tronUrl + "/wallet/triggersmartcontract", jsonObject.toString());
? ? ? ? JSONObject result = JSONObject.parseObject(trans1);
? ? ? ? System.out.println("trc20 result:" + result.toJSONString());
? ? ? ? if (result.containsKey("Error")) {
? ? ? ? ? ? throw new RuntimeException("result.containsKey(\"Error\")");
? ? ? ? }
? ? ? ? JSONObject tx = result.getJSONObject("transaction");
? ? ? ? //填寫備注
? ? ? ? tx.getJSONObject("raw_data").put("data", Hex.toHexString("備注信息".getBytes()));
? ? ? ? String txid = TronUtils.signAndBroadcast(tronUrl, privateKey, tx);
? ? ? ? if (txid != null) {
? ? ? ? ? ? System.out.println("txid:" + txid);
? ? ? ? ? ? return txid;
? ? ? ? }
? ? ? ? return null;
? ? }
```
### 創(chuàng)建交易
```java
String ownerAddress = TronUtils.getAddressByPrivateKey(privateKey);
JSONObject jsonObject = new JSONObject();
jsonObject.put("contract_address", TronUtils.toHexAddress(USDT_CPNTRACT));
jsonObject.put("function_selector", "transfer(address,uint256)");
List<Type> inputParameters = new ArrayList<>();
inputParameters.add(new Address(TronUtils.toHexAddress(toAddress).substring(2)));
inputParameters.add(new Uint256(amount.multiply(decimal).toBigInteger()));
String parameter = FunctionEncoder.encodeConstructor(inputParameters);
jsonObject.put("parameter", parameter);
jsonObject.put("owner_address", TronUtils.toHexAddress(ownerAddress));
jsonObject.put("call_value", 0);
jsonObject.put("fee_limit", 6000000L);
String trans1 = HttpClientUtils.postJson(tronUrl + "/wallet/triggersmartcontract", jsonObject.toString());
JSONObject result = JSONObject.parseObject(trans1);
System.out.println("trc20 result:" + result.toJSONString());
? ? ? ? if (result.containsKey("Error")) {
? ? ? ? ? ? throw new RuntimeException("result.containsKey(\"Error\")");
? ? ? ? }
? ? ? ? JSONObject tx = result.getJSONObject("transaction");
? ? ? ? //填寫備注
? ? ? ? tx.getJSONObject("raw_data").put("data", Hex.toHexString("備注信息".getBytes()));
```
先通過私鑰獲取自己的地址,然后指定合約地址互站,即usdt 在波場的合約地址私蕾,指定合約中的方法;然后指定對(duì)方地址胡桃、附上燃料費(fèi)trx 踩叭,通過調(diào)用 `/wallet/triggersmartcontract` 創(chuàng)建交易;至此第一步就算完成了翠胰;這里需要說明容贝,trx 燃料費(fèi)的概念,也就是支付給區(qū)塊鏈節(jié)點(diǎn)的礦工費(fèi)用之景;如果沒有trx 轉(zhuǎn)賬是轉(zhuǎn)不成功的斤富;很多人疑惑,為啥用交易所轉(zhuǎn)賬不需要trx锻狗,那是因?yàn)榻灰姿鶐湍憬o付了满力,用web3 wallet 轉(zhuǎn),必須支付trx轻纪;
### 簽名和廣播
```java
public static String signAndBroadcast(String tronUrl,String privateKey,JSONObject transaction)throws Throwable{
if(tronUrl.endsWith("/")){
tronUrl= tronUrl.substring(0,tronUrl.length() - 1);
}
Protocol.Transaction tx = packTransaction(transaction.toJSONString());
byte[] bytes = signTransactionByte(tx.toByteArray(), ByteArray.fromHexString(privateKey));
String signTransation = Hex.toHexString(bytes);
JSONObject jsonObjectGB = new JSONObject();
jsonObjectGB.put("transaction", signTransation);
String url = tronUrl + "/wallet/broadcasthex";
String transationCompelet1 = HttpClientUtils.postJson(url, jsonObjectGB.toString());
JSONObject transationCompelet = JSONObject.parseObject(transationCompelet1);
System.out.println("signAndBroadcast transationCompelet:" + transationCompelet.toJSONString());
if (transationCompelet.getBoolean("result")) {
return transationCompelet.getString("txid");
} else {
logger.error(String.format("簽名交易失敗:%s",transationCompelet1));
return null;
}
}
/**
* 簽名交易
* @param transaction
* @param privateKey
* @return
* @throws InvalidProtocolBufferException
* @throws NoSuchAlgorithmException
*/
public static byte[] signTransactionByte(byte[] transaction, byte[] privateKey) throws InvalidProtocolBufferException, NoSuchAlgorithmException {
ECKey ecKey = ECKey.fromPrivate(privateKey);
Protocol.Transaction transaction1 = Protocol.Transaction.parseFrom(transaction);
byte[] rawdata = transaction1.getRawData().toByteArray();
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(rawdata,0,rawdata.length);
byte[] hash= digest.digest();
byte[] sign = ecKey.sign(hash).toByteArray();
return transaction1.toBuilder().addSignature(ByteString.copyFrom(sign)).build().toByteArray();
}
```
#### 名驗(yàn)證的原理
在已知交易發(fā)起者(contract owner)地址的情況下油额,通過簽名消息逆推公鑰(recover),并將公鑰轉(zhuǎn)換為地址刻帚,與發(fā)起者地址進(jìn)行比較潦嘶。如果地址一致,即為驗(yàn)證成功我擂。
#### 驗(yàn)證簽名的方法
驗(yàn)證方法需要三個(gè)參數(shù):
- 交易id(即交易哈希衬以,通過`Transaction.rawData`計(jì)算SHA256得到)
- 簽名消息(即`Transaction.signature`)
- 發(fā)起者地址(即`Transaction.rawData.contract.parameter.ownerAddress`缓艳,其中`parameter`的類型是`com.google.protobuf.Any`,需要根據(jù)具體交易類型來進(jìn)行`unpack`操作)
```java
byte[] bytes = signTransactionByte(tx.toByteArray(), ByteArray.fromHexString(privateKey));
String signTransation = Hex.toHexString(bytes);
```
### 廣播
廣播可理解為發(fā)送交易看峻。任何與波場網(wǎng)絡(luò)的交互行為都被稱作為一筆交易阶淘。一筆交易可以是TRX轉(zhuǎn)賬、質(zhì)押/解鎖TRX互妓、觸發(fā)智能合約等溪窒。
**只有消耗資源的交易才會(huì)被記錄在鏈上。** 前面提到了trx 燃料費(fèi)冯勉,就是這里的消耗的資源澈蚌;當(dāng)區(qū)塊鏈的其他節(jié)點(diǎn)確認(rèn)了你的交易,并把此筆交易廣播給其他人后灼狰,這筆交易就算交易成功宛瞄,即同步到其他節(jié)點(diǎn)的數(shù)據(jù)庫了;
`wrapper.broadcastTransaction(signedTransaction); //return transaction hash if successfully broadcasted, otherwise the error code`
```java
String url = tronUrl + "/wallet/broadcasthex";
String transationCompelet1 = HttpClientUtils.postJson(url, jsonObjectGB.toString());
JSONObject transationCompelet = JSONObject.parseObject(transationCompelet1);
```
以上就是trc20-usdt 轉(zhuǎn)賬的背后邏輯交胚。下面講講wallet地址以及wallet地址的創(chuàng)建和生成份汗;
### wallet
wallet可以理解為加密算法中配對(duì)的公鑰和私鑰;tron wallet采用的加密算法是 波場的簽名算法是ECDSA蝴簇,選用的曲線是SECP256K1杯活。
在使用web3 wallet時(shí),經(jīng)常會(huì)讓我們主動(dòng)創(chuàng)建或?qū)胱∮浽~熬词、私鑰的方式創(chuàng)建wallet旁钧,這后面的原理又是什么呢?
#### **wallet地址**
我們可以理解wallet地址是這套算法中公鑰互拾,這個(gè)地址是公開的歪今,別人可以向你轉(zhuǎn)賬等等;而經(jīng)常說的助記詞就是把私鑰經(jīng)過==可逆算法==轉(zhuǎn)換成了12個(gè)常見的英文字符串颜矿,二者是等價(jià)的(這個(gè)過程和產(chǎn)生wallet地址彤委、私鑰算法不一樣),明白加密算法的人都知道或衡,加密算法一般不具備可逆向性的,私鑰能推導(dǎo)出公鑰的车遂,反之不行封断。**所以務(wù)必保護(hù)好你的私鑰及代表私鑰的助記詞。**
好了舶担,明白這些東西后坡疼,那我們看代碼:
```java
/**
* 離線創(chuàng)建地址
*
* @return
*/
public static Map<String, String> createAddress() {
ECKey eCkey = new ECKey(random);
? ? String privateKey = ByteArray.toHexString(eCkey.getPrivKeyBytes());
byte[] addressBytes = eCkey.getAddress();
String hexAddress = ByteArray.toHexString(addressBytes);
Map<String, String> addressInfo = new HashMap<>(3);
addressInfo.put("address", toViewAddress(hexAddress));
addressInfo.put("hexAddress", hexAddress);
addressInfo.put("privateKey", privateKey);
return addressInfo;
}
```
在這個(gè)過程中,涉及到了大量的算法相關(guān)的知識(shí)衣陶,需要說明的是tron wallet的加密算法經(jīng)過多次轉(zhuǎn)換和加密的柄瑰,這個(gè)過程非常之復(fù)雜闸氮,就不展開講了。
### 地址查詢
如果我們知道了一個(gè)wallet地址教沾,我們可以查詢其wallet的交易情況蒲跨,比如tron 鏈上的所有協(xié)議,如trx轉(zhuǎn)賬授翻、trc20-usdt 轉(zhuǎn)賬等等或悲;
```java
String specificWalletTransferUrl = urlAddress + blockWalletBean.monitorAddress + "/transactions/trc20";
Map<String, String> paraMap = new HashMap<>();
paraMap.put("limit", "30");
paraMap.put("only_confirmed", "true");
paraMap.put("contract_address", "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t");
String content = httpGet(specificWalletTransferUrl, paraMap);
System.out.println("content:" + content);
f (!StringUtils.isEmpty(content)) {
? ? JSONObject jsonObject = JSONObject.parseObject(content);
? ? JSONArray results = jsonObject.getJSONArray("data");
? ? //解析數(shù)據(jù),獲取wallet address交易詳細(xì)信息
```
### 區(qū)塊掃描
```java
? public BigInteger getNowBlock() {
? ? ? ? String url = tronUrl + "/wallet/getnowblock";
? ? ? ? String httpRequest = HttpRequest.get(url).execute().body();
? ? ? ? JSONObject jsonObject1 = JSONObject.parseObject(httpRequest);
? ? ? ? return jsonObject1.getJSONObject("block_header").getJSONObject("raw_data").getBigInteger("number");
? ? }
```
### 寫在最后
其實(shí)這個(gè)wallet 堪唐、智能合約還有很多的功能巡语,我們經(jīng)常聽到有些人的U被盜,那些被盜的人怎么做到的呢淮菠,我們該如何去防范呢男公?這些東西需要我們深入研究才能明白其中的奧秘,好了篇幅有限合陵,至此結(jié)束枢赔。
------
### 總結(jié)
謝謝你的觀看,覺得對(duì)你有用的話曙寡,點(diǎn)個(gè)贊糠爬,不勝感激。
想要深入交流的可以加V :xyxiaohuajian 举庶,簡單備注(trc20)执隧;