區(qū)塊鏈實(shí)現(xiàn)Trc20-usdt 交易

### 前言

(平臺(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)执隧;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市户侥,隨后出現(xiàn)的幾起案子镀琉,更是在濱河造成了極大的恐慌,老刑警劉巖蕊唐,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屋摔,死亡現(xiàn)場離奇詭異,居然都是意外死亡替梨,警方通過查閱死者的電腦和手機(jī)钓试,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來副瀑,“玉大人弓熏,你說我怎么就攤上這事】匪” “怎么了挽鞠?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我信认,道長材义,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任嫁赏,我火速辦了婚禮其掂,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘橄教。我一直安慰自己清寇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布护蝶。 她就那樣靜靜地躺著华烟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪持灰。 梳的紋絲不亂的頭發(fā)上盔夜,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音堤魁,去河邊找鬼喂链。 笑死,一個(gè)胖子當(dāng)著我的面吹牛妥泉,可吹牛的內(nèi)容都是我干的椭微。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼盲链,長吁一口氣:“原來是場噩夢啊……” “哼蝇率!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起刽沾,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤本慕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后侧漓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锅尘,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年布蔗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了藤违。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纵揍,死狀恐怖纺弊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情骡男,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布傍睹,位于F島的核電站隔盛,受9級(jí)特大地震影響犹菱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜吮炕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一腊脱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧龙亲,春花似錦陕凹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拂盯,卻和暖如春佑女,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谈竿。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國打工团驱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人空凸。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓嚎花,卻偏偏與公主長得像,于是被迫代替她去往敵國和親呀洲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子紊选,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容