聊一聊区块链-波场技术,只聊技术

郭霖 2022-11-25 08:00


/   今日科技快讯   /

据报道,受定位升级、成本、疫情、汇率波动等因素影响,即将发布的小米新一代旗舰机小米13系列预计售价将大幅上调,上调幅度将达15-20%,小米13售价将在4500元左右。目前,这一系列产品已经开始量产,全系均搭载4nm芯片。

/   作者简介   /

明天就是周六啦,提前祝大家周末愉快!

本篇文章来自史建华的投稿,文章分享了Trc20-USDT的实现,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章。

史建华的博客地址:
https://blog.csdn.net/sjh_389510506?type=blog


/   前言   /

最近因项目使用tron 协议接入区块链,故对其做了一番研究,先把相关资料整理一遍,供大家学习使用;

网上的这部分资料很少,所以学习起来也是遇到了很多困难,尤其是里面很多新的概念,理解起来有一定的难度。比如说去中心化、地址、加密算法、算法因子、私钥含义、助记词、trc协议、智能合约、usdt等等;

/   背景   /

很多人接触区块链,大多是通过接触usdt这种中充当稳定资产(也称泰达币)角色开始的,usdt是什么?下面我们探讨一下这个问题。

USDT是一种将加密货币与法定货币美元挂钩的虚拟货币 ,一种基本衡量单位的代币;Tether公司推出的基于稳定价值货币美元(USD)的代币Tether USD(下称USDT),1USDT=1美元,用户可以随时使用USDT与USD进行1:1兑换。

公司对外宣称严格遵守1:1的准备金保证,即每发行1个 USDT 代币,其银行账户都会有1美元的资金保障。用户可以在 Tether 平台进行资金查询,以保障透明度这样然后usdt稳定资产发行者通过存入三方银行机构法定资产,来保证我的usdt是有保证的,不会超发或者失去赔付能力;

我们怎么知道Tether公司所说的真实性呢?

这一点我们通过Tether 官网(https://tether.to/en/supported-protocols/)进行查看,进入透明度(https://tether.to/en/transparency/)展示界面,可以看到其发行了多少枚代币,以及质押情况。


可以看到,不光有USDT,还有ESDT、CSDT ;其实U是代表美国,E代表欧洲,C代表中国的意思。

/   波场链   /

TRON是一个开源同时支持智能合约功能的公共区块链平台,也叫波场链。其创始人是中国人孙宇晨,他拍过巴菲特的午餐,牛。好了,废话不多说,介绍一些东西吧。

区块链是什么

既然我们很多时候提到了链,那链到底是什么呢?

其实链可以理解为一个平台,这个平台能够做一些事情。比如运行智能合约,发行代币等等。当然构建一个去中心化的链就必须要实现一些机制,比如构建信任机制,这是去中心化核心中的核心。说到这里要引入一个概念:共识。即如何在分布式的不信任环境中搭建一个信任的网络?

  • 共识

在区块链系统中没有像银行一样的中心化机构,所以在进行传输信息、价值转移时,共识机制解决并保证每一笔交易在所有记帐节点上的一致性和 正确性问题。区块链的这种新的共识机制使其在不依靠中心化组织的情况下,依然大规模高效协作完成运转。

工作量证明Pow

工作量证明简单理解就是一份证明,用来确认节点做过一定量的工作。监测工作的整个过程通常是极为低效的,而通过对工作的结果进行认证来证明完成了相应的工作量,则是-种非常高效的方式。比特币在区块的生成过程中使用了PoW机制,要得到合理的随机数求解数学难题需要经过大量尝试计算, 通过查看记录和验证区块链信息的证明,就能知道是否完成了指定难度系数的工作量。

权益证明Pos

即把资产存在银行里,银行会通过你持有数字资产的数量和时间给你分配相应的收益。采用PoS机制的加密货币资产,系统会根据节点的持币数量和时间的乘积(币天数)给节点分配相应的权益。

还有一些其他的共识机制,这里介绍了两种最为广泛和常见的共识机制。

节点
TRON是一个由很多计算机组成的分布式网络,计算机上需要运行可以验证区块和交易数据的软件, 也叫节点。您需要在系统上安装运行一个叫做客户端的软件,才能运行一个节点。

“节点 “指的是一个正在运行的客户端软件。客户端是TRON的一个实现,它可以验证每个区块的所有交易,保持网络的安全和数据的准确性。目前TRON客户端是由Java语言实现,它遵循TRON协议, 实现了TRON网络和区块链的功能。

每个人可以下载相关源代码,并运行节点的客户端,可以自行验证交易;也可以理解”节点 “指的是一个正在运行的客户端软件。客户端是TRON的一个实现,它可以验证每个区块的所有交易,保持网络的安全和数据的准确性。

区块

区块是一个包含了一系列交易的集合,另外一个区块还包含了前一个区块的哈希值,这种实现方式将各个区块连接到一起形成链。

为了确保TRON网络上的所有节点保持一致的状态并就交易的历史达成一致,我们将交易打包进区块,实现了数百个交易同时被提交、确认以及同步到全网。每个新创建的区块都包含其父区块的hash值。在任何给定的时间,网络上的几乎所有节点都对区块的数量和历史状态达成一致。

代币Trx

TRX是TRON网络上最主要的加密货币,就像是以太坊的链上的代币就是eth;TRX是TRON区块链上帐户的基本单位,在tron链上进行的交易活动花费以trx作为标准衡量。

TRX 可以被铸造、销毁、转移等行为;有了这些功能,我们才能理解后面为啥会有TRC20-USDT的概念了?

  • TRC20协议标准

我们知道TRON链支持运行智能合约,而我们熟知的TRC20,其实就是一种合约,这个合约里面实现了一些功能,比如代币查询、转移、授权等等功能。
TRC-20是为发行通证资产而制定的一套合约标准,即遵守这一标准编写的合约都被认为是一个TRC-20合约。当各类钱包、交易所在对接TRC-20合约的资产时,从这套合约标准中就可以知道这个合约定义了哪些函数、事件,从而方便的进行对接。

要明白trc20是一种协议,这个协议是波场tron链下面的一种,还有trx,trc10,trc721等等,而波场链跟usdt 发行者公司合作,写了一份智能合约,该协议实现了几种功能,如交易、查询、授权、事件监听等等,我们在地址中转账看到的trc20-usdt 就是执行了这个交易方法 transfer,所以能够把一个地址中的usdt转移到另一个地址;

//trc20 协议中支持的方法
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这个词,其原因就是如此。同理,在eth链上实现一个支持USDT交易的智能合约,那这个链就能通过ETH进行转账交易,比如说erc20-usdt。

/   Trc20-usdt   /

转账

要实现交易,首先要得有地址,自己地址,对方地址,usdt,trx 燃料费;然后这几个要素经过什么步骤才能达到目的?

创建交易 、离线签名、广播

先把代码贴出来:

/**
     * 发起trc20交易 (目标地址,数量,合约地址,私钥)
     * 地址 默认为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;
    }
创建交易

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 在波场的合约地址,指定合约中的方法;然后指定对方地址、附上燃料费trx ,通过调用 /wallet/triggersmartcontract 创建交易;至此第一步就算完成了;

这里需要说明,trx 燃料费的概念,也就是支付给区块链节点的矿工费用;如果没有trx 交易是不成功的;很多人疑惑,为啥用交易所不需要trx,那是因为交易所帮你给付了,用web3 wallet 转,必须支付trx;

签名和广播

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();
    }
  • 签名验证的原理

在已知交易发起者(contract owner)地址的情况下,通过签名消息逆推公钥(recover),并将公钥转换为地址,与发起者地址进行比较。如果地址一致,即为验证成功。

  • 验证签名的方法

验证方法需要三个参数:

  • 交易id(即交易哈希,通过Transaction.rawData计算SHA256得到)
  • 签名消息(即Transaction.signature)
  • 发起者地址(即Transaction.rawData.contract.parameter.ownerAddress,其中parameter的类型是com.google.protobuf.Any,需要根据具体交易类型来进行unpack操作)

byte[] bytes = signTransactionByte(tx.toByteArray(), ByteArray.fromHexString(privateKey));
String signTransation = Hex.toHexString(bytes);
广播

广播可理解为发送交易。任何与波场网络的交互行为都被称作为一笔交易。一笔交易可以是TRX转账、质押/解锁TRX、触发智能合约等。

只有消耗资源的交易才会被记录在链上。前面提到了trx 燃料费,就是这里的消耗的资源;当区块链的其他节点确认了你的交易,并把此笔交易广播给其他人后,这笔交易就算交易成功,即同步到其他节点的数据库了;

wrapper.broadcastTransaction(signedTransaction); //return transaction hash if successfully broadcasted, otherwise the error code

String url = tronUrl + "/wallet/broadcasthex";
String transationCompelet1 = HttpClientUtils.postJson(url, jsonObjectGB.toString());
JSONObject transationCompelet = JSONObject.parseObject(transationCompelet1);
以上就是trc20-usdt 转账的背后逻辑。下面讲讲wallet地址以及wallet地址的创建和生成;

账户与地址

TRON采用账户模型,账户的唯一标识为地址(address),对账户操作需要私钥签名。帐户由一对加密密钥组成:公钥和私钥。公钥映射为地址,私钥用来对交易进行签名。这对密钥不但可以防止交易被篡改和伪造,而且还可以证明交易确实是由发送方所发送。私钥由64个十六进制字符组成。公钥的生成基于私钥,并使用椭圆曲线数字签名算法https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm)来生成对应的公钥。基于公钥可以计算出地址。

  • 账户地址格式

TRON网络账户地址有两种格式,一种是Hex格式,另一种是Base58格式。

Hex格式:用公钥P作为输入,计算SHA3得到结果H,取H的最后20字节,在前面填充一个字节0x41得到Hex格式地址。例如:

418840E6C55B9ADA326D211D818C34A994AECED808
Base58格式:对hex格式的地址进行basecheck计算得到Base58格式地址,所有账户地址的第一个字符均为T。例如:

TNPeeaaFB7K9cmo4uQpcU32zGK8G1NYqeL
我们经常看的和使用的就是Base58格式的地址。

  • 钱包是什么

wallet 钱包可以理解为 管理账户中的地址和私钥的工具,这些工具可以是app,或者网站;我们称这些工具为钱包,比如我们常用的钱包imToken、tronLink 等等。在使用web3 wallet时,经常会让我们主动创建或导入住记词、私钥的方式创建wallet,这后面的原理又是什么呢?

我们可以理解wallet钱包地址是这套算法中公钥,这个地址是公开的,别人可以向你进行交易等等;而经常说的助记词就是把私钥经过==可逆算法==转换成了12个常见的英文字符串,二者是等价的(这个过程和产生wallet地址、私钥算法不一样),明白加密算法的人都知道,加密算法一般不具备可逆向性的,私钥能推导出公钥的,反之不行。所以务必保护好你的私钥及代表私钥的助记词

好了,明白这些东西后,那我们看代码:

    /**
     * 离线创建地址
     *
     * @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;
    }
在这个过程中,涉及到了大量的算法相关的知识,需要说明的是tron wallet的加密算法经过多次转换和加密的,这个过程非常之复杂,就不展开讲了。

地址查询

如果我们知道了一个wallet地址,我们可以查询其wallet的交易情况,比如tron 链上的所有协议,如trx交易、trc20-usdt 交易等等;

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");
    //解析数据,获取wallet address交易详细信息
区块扫描

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");
    }
/   写在最后   /

其实这个wallet 、智能合约还有很多的功能,我们经常听到有些人的被盗,那些被盗的人怎么做到的呢,我们该如何去防范呢?这些东西需要我们深入研究才能明白其中的奥秘,好了篇幅有限,至此结束。


推荐阅读:
我的新书,《第一行代码 第3版》已出版!
Kotlin Flow响应式编程,基础知识入门
Android开发中关于内存的那些事,一篇全搞懂

欢迎关注我的公众号
学习技术或投稿


长按上图,识别图中二维码即可关注

推荐阅读