reference
- Official API documentation: https://developers.tron.network/reference
- C++ https://github.com/aUscCoder/TronWallet
TRON resource model
Reference: https://tronprotocol.github.io/documentation-zh/mechanism&algorithm/resource/
-
If it is a transfer, the target account does not exist, including ordinary transfer or token issuance transfer, the transfer operation will create an account and transfer, only the Bandwidth Points consumed by creating the account will be deducted, and the transfer will not consume additional Bandwidth Points .
-
If the transaction requires the creation of a new account, Bandwidth Points will be consumed as follows:
-
Try to consume the Bandwidth Points obtained by freezing the transaction initiator. If the transaction initiator has insufficient Bandwidth Points, proceed to the next step
-
Try to consume the TRX of the transaction initiator, this part burns 0.1TRX
For example: https://tronscan.org/#/transaction/f80dacb7b73219b86b45225dc58d7d5337a576ea190899dda5e5ebd3e5de9da5
-
-
If the transaction is a Token transfer, the Bandwidth Points will be consumed as follows:
-
Verify in turn whether the total free Bandwidth Points of the issued Token assets are enough to consume, whether the remaining free Bandwidth Points of the transfer initiator's Token are enough to consume, and whether the remaining amount of Bandwidth Points obtained by freezing TRX for the Token issuer is enough to consume. If it is satisfied, the Bandwidth Points of the Token issuer will be deducted, and if any one is not satisfied, it will go to the next step
-
Try to consume the Bandwidth Points obtained by freezing the transaction initiator. If the transaction initiator has insufficient Bandwidth Points, proceed to the next step
-
Try to consume the free Bandwidth Points of the transaction originator. If the free Bandwidth Points are not enough, go to the next step
-
Try to consume the TRX of the transaction initiator, the number of bytes of the transaction * 10 sun
-
-
For ordinary transactions, Bandwidth Points are consumed as follows:
-
Try to consume the Bandwidth Points obtained by freezing the transaction initiator. If the transaction initiator has insufficient Bandwidth Points, proceed to the next step
-
Try to consume the free Bandwidth Points of the transaction originator. If the free Bandwidth Points are not enough, go to the next step
-
Try to consume the TRX of the transaction initiator, the number of bytes of the transaction * 10 sun
-
-
The user's bandwidth is recovered within 24 hours, and the recovery process is linear. (The specific formula is in the official document)
TRON coin charging technology implementation plan
-
Get transaction information: https://developers.tron.network/reference#transaction-information-by-account-address
curl --request GET \ --url https://api.trongrid.io/v1/accounts/TPcUx2iwjomVzmX3CHDDYmnEPJFTVeyqqS/transactions
-
Get trc20 transaction information: https://developers.tron.network/reference#trc20-transaction-information-by-account-address
curl --request GET \ --url https://api.trongrid.io/v1/accounts/TPcUx2iwjomVzmX3CHDDYmnEPJFTVeyqqS/transactions/trc20
-
other transaction fees
Transaction Type cost Create a witness (observation node) 9999 TRX issue token 1024 TRX Create an account 0.1 TRX Create an exchange 1024 TRX
Ordinary TRX transaction
curl -X POST https://api.trongrid.io/wallet/getblockbynum -d '{"num":14455630}'
{
"ret": [
{
"contractRet": "SUCCESS"
}
],
"signature": [
"16658b4d004737334df951cf8728e03108d3cc2a6ebcad306fb7a4e2984c02fa4bd1ad1664fe4768c36c585171d21207a1fcd8080a075f8b472585f8eaf6a53501"
],
"txID": "f80dacb7b73219b86b45225dc58d7d5337a576ea190899dda5e5ebd3e5de9da5",
"raw_data": {
"contract": [
{
"parameter": {
"value": {
"amount": 997000000,
"owner_address": "418a4a39b0e62a091608e9631ffd19427d2d338dbd",
"to_address": "4195a6569bdccc1bee832803b4116a020950818cba"
},
"type_url": "type.googleapis.com/protocol.TransferContract"
},
"type": "TransferContract"
}
],
"ref_block_bytes": "933a",
"ref_block_hash": "ad8de998be3a5ebc",
"expiration": 1573700973000,
"timestamp": 1573611260215
},
"raw_data_hex": "0a02933a2208ad8de998be3a5ebc40c8e38cbfe62d5a69080112650a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412340a15418a4a39b0e62a091608e9631ffd19427d2d338dbd12154195a6569bdccc1bee832803b4116a020950818cba18c086b4db0370b792a994e62d"
},
in
"value": {
"amount": 997000000, //转账金额, 即 997 TRX
"owner_address": "418a4a39b0e62a091608e9631ffd19427d2d338dbd",
//发送地址, 即 TNaRAoLUyYEV2uF7GUrzSjRQTU8v5ZJ5VR
"to_address": "4195a6569bdccc1bee832803b4116a020950818cba"
//接收地址, 即 TPcUx2iwjomVzmX3CHDDYmnEPJFTVeyqqS
}
TRC20 transaction
Similar to ERC20 transaction
TRC20 transaction
{
"ret": [
{
"contractRet": "SUCCESS"
}
],
"signature": [
"2e0918c8b5c74f9f35f94d03044544f6acd2470024ec7d59f2019c91241a489569901812a789fb10a196f37984b374fe3253807cf1c6b3042f1bac6356bf86ad00"
],
"txID": "7f88b3cae8b2721a2c124bed0af27d7c9f60fad51672756819732779a70a22a0",
"raw_data": {
"contract": [
{
"parameter": {
"value": {
"data": "a9059cbb0000000000000000000000008a4a39b0e62a091608e9631ffd19427d2d338dbd000000000000000000000000000000000000000000000000000000186d0d8e20",
"owner_address": "4136f75d3494f0093d412102853d29b3768735159e",
"contract_address": "41a614f803b6fd780986a42c78ec9c7f77e6ded13c"
},
"type_url": "type.googleapis.com/protocol.TriggerSmartContract"
},
"type": "TriggerSmartContract"
}
],
"ref_block_bytes": "c710",
"ref_block_hash": "138c16f4dc833693",
"expiration": 1582009386000,
"fee_limit": 10000000,
"timestamp": 1581918923005
},
data
The composition of it is exactly the same as that of ERC20 transfer
函数签名(4字节) + 接受者地址(32字节) + 金额(32字节)
For example, the above TRC20-USDT transfer
a9059cbb 函数签名 即 transfer(address _to, uint256 _value)
0000000000000000000000008a4a39b0e62a091608e9631ffd19427d2d338dbd #接受者地址
000000000000000000000000000000000000000000000000000000186d0d8e20 #金额
41
Among them, the recipient address of TRC20 is in the hexadecimal string format without a band, which is exactly the same as the ETH address, so the prefix 41
needs to be added when converting to the address form in Base58 format.
TRON address generation
python3 source code
#!coding:utf8
#author:yqq
#date:2019/11/12 0004 14:35
#description: TRON地址生成算法
#参考: https://github.com/iexbase/tron-api-python/blob/master/tronapi/common/account.py
import hashlib
import ecdsa
import os
import sha3
#2019-11-12 根据官方定义修改 有限域
# http://www.secg.org/sec2-v2.pdf#page=9&zoom=100,0,249
# 关于 有限域的定义 请参考
# 0xEFFFFFC2F = 2**32 - 2**9 - 2**8 - 2**7 - 2**6 - 2**4 - 1
g_nFactor = 0xEFFFFFC2F + 0x23492397 #增值自定义
g_nMaxPrivKey = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140 - g_nFactor #私钥最大值 (差值是自定义的)
g_nMinPrivKey = 0x0000000000000000000000000000000000000000000000000000000000000001 + g_nFactor #私钥最小值 (增值是自定义的)
def GenPrivKey():
'''
生成私钥, 使用 os.urandom (底层使用了操作系统的随机函数接口, 取决于CPU的性能,各种的硬件的数据指标)
:return:私钥(16进制编码)
'''
#2019-05-15 添加私钥范围限制
while True:
privKey = os.urandom(32).encode('hex') #生成 256位 私钥
if g_nMinPrivKey < int(privKey, 16) < g_nMaxPrivKey:
return privKey
g_b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
def Base58encode(n):
'''
base58编码
:param n: 需要编码的数
:return: 编码后的
'''
result = ''
while n > 0:
result = g_b58[n % 58] + result
n /= 58
return result
def Base256decode(s):
'''
base256编码
:param s:
:return:
'''
result = 0
for c in s:
result = result * 256 + ord(c)
return result
def CountLeadingChars(s, ch):
'''
计算一个字符串开头的字符的次数
比如: CountLeadingChars('000001234', '0') 结果是5
:param s:字符串
:param ch:字符
:return:次数
'''
count = 0
for c in s:
if c == ch:
count += 1
else:
break
return count
def Base58CheckEncode(version, payload):
'''
:param version: 版本前缀 , 用于区分主网 和 测试网络
:param payload:
:return:
'''
s = chr(version) + payload
checksum = hashlib.sha256(hashlib.sha256(s).digest()).digest()[0:4] #两次sha256, 区前4字节作为校验和
result = s + checksum
leadingZeros = CountLeadingChars(result, '\0')
return '1' * leadingZeros + Base58encode(Base256decode(result))
def PrivKeyToPubKey(privKey):
'''
私钥-->公钥
:param privKey: 共65个字节: 0x04 + x的坐标 + y的坐标
:return:
'''
sk = ecdsa.SigningKey.from_string(privKey.decode('hex'), curve=ecdsa.SECP256k1)
# vk = sk.verifying_key
return ('\04' + sk.verifying_key.to_string()).encode('hex')
def PubKeyToAddr(pubKey, isTestnet = False):
'''
公钥 --> 地址
:param pubKey: 公钥
:param isTestnet: 是否是地址
:return: 地址字符串
'''
addr1 = sha3.keccak_256(pubKey.decode('hex')[1:]).digest()
addr2 = addr1[11 + 1: ]
#不区分主网测试网
# if isTestnet:
# return Base58CheckEncode(0xA0, addr2) #test
return Base58CheckEncode(0x41, addr2) #main
def GenAddr(isTestnet=False):
'''
此函数用于C++调用,
:param isTestnet: 是否是测试网络
:return: (私钥, 公钥, 地址)
'''
privKey = GenPrivKey()
# print("privkey : " + privKey)
# print("privkey WIF:" + PrivKeyToWIF(privKey, isTestnet))
pubKey = PrivKeyToPubKey(privKey)
# print("pubkey : " + pubKey)
addr = PubKeyToAddr(pubKey, isTestnet)
# print("addr : " + addr)
return str(privKey), str(pubKey), str(addr)
def GenMultiAddr(nAddrCount = 1, isTestnet=True):
'''
生成多个地址
:param nAddrCount:
:param isTestnet:
:return:
'''
# return [("1111", "2222", "3333"), ("4444", "55555", "66666")]
# return [1, 2, 3, 4]
# return ["1111", "2222", "3333", "4444"]
lstRet = []
for i in range(nAddrCount):
lstRet.append(GenAddr(isTestnet))
return lstRet
#
# def main2():
#
# # addrs = GenMultiAddr(10, False)
# # print(addrs)
#
# pass
#
#
# if __name__ == '__main__':
#
# # main()
# main2()
TRON transaction offline signature
-
Step 1: Create a transaction using the interface, https://developers.tron.network/reference#walletcreatetransaction
-
Step 2: Modify
expiration
,raw_data_hex
,txid
-
Step 3:
txid
Sign with secp256k1,signature
the field is signedr
,s
,i
(calledv
) , wherei = i + 27
, please refer to the ETH signature for details
signature demo
#!coding:utf8
#author:yqq
#date:2020/3/4 0004 10:23
#description:
import logging
from pprint import pprint
import hashlib
from binascii import hexlify, unhexlify
from time import sleep
import coincurve
# TRX python sdk
from tronapi import Tron
from tronapi.trx import Trx
def safe_ord(value):
if isinstance(value, int):
return value
else:
return ord(value)
def new_ecsign(rawhash, key):
if coincurve and hasattr(coincurve, 'PrivateKey'):
pk = coincurve.PrivateKey(key)
signature = pk.sign_recoverable(rawhash, hasher=None)
v = safe_ord(signature[64]) + 27
r = signature[0:32]
s = signature[32:64]
sig = r + s + unhexlify(hex(v)[2:])
return sig
def my_encode_int64( num : int ) -> str:
"""
将一个整数编码为 protobuf 编码格式的 十六进制字符串
"""
# num = 1583290890000
assert num > 0
#原码字符
raw = bin(num)[2:]
print(f"原码: {raw}")
#补码, 因为只处理正数, 所以 补码和原码相同
complement = raw
print(f'补码: {complement}')
#如果长度不是7的倍数, 则补0凑齐
tmp_complement = complement
if len(complement) % 7 != 0:
tmp_complement = '0' * (7 - (len(complement) % 7)) + complement
print(f'补0后的补码: {tmp_complement}')
#7位组 , 正序
seven_bit_array = []
i = len(tmp_complement) - 1
tmp = ''
while i >= 0:
tmp = tmp_complement[i] + tmp
if i % 7 == 0 :
seven_bit_array.append( tmp )
tmp = ''
i -= 1
print(f'正序7位组: { seven_bit_array[::-1] }')
print(f'反序后7位组: {seven_bit_array}')
#加上 MSB, 标识位
added_msb_seven_bit_array = []
for i in range(0, len(seven_bit_array)):
#如果是最后一个7位组, 则 MSB标识位是 0, 否则 MSB标识位是 1
if i == len(seven_bit_array) - 1:
added_msb_seven_bit_array.append( '0' + seven_bit_array[i] )
else:
added_msb_seven_bit_array.append( '1' + seven_bit_array[i] )
print(f'加上MSB标识位的7位组: {added_msb_seven_bit_array}')
#最终的 二进制字符串形式
binstr = ''.join(added_msb_seven_bit_array)
print(f'最终二进制形式:{binstr}')
#十六进制字符串形式
hexstr = hex( int( binstr, 2 ) )
print(f'十六进制字符串形式: {hexstr}')
return hexstr[2:]
def main():
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger()
full_node = 'https://api.trongrid.io'
solidity_node = 'https://api.trongrid.io'
event_server = 'https://api.trongrid.io'
privkey = "这里填写你的私钥" # this is your private key
tron = Tron(full_node=full_node,
solidity_node=solidity_node,
event_server=event_server,
private_key=privkey)
trx = Trx(tron)
from_addr = 'TPcUx2iwjomVzmX3CHDDYmnEPJFTVeyqqS'
to_addr = 'TDUjsjJzQABwVv8DnLVDZ778uKQ7X5Fs7E'
options = {
'from' : from_addr
}
amount = float(0.123)
# receipt = trx.send_transaction( to=to_addr, amount=amount, options=options )
#构造交易
tx = trx.tron.transaction_builder.send_transaction(
to_addr,
amount,
options['from']
)
if False:
sign = trx.sign(tx)
sleep(61) # 休眠 61秒 广播时报错: {'code': 'TRANSACTION_EXPIRATION_ERROR', 'message': 'transaction expired'}
result = trx.broadcast(sign)
pprint(result)
return
old_expiration_hex = my_encode_int64(tx['raw_data']['expiration'])
#改变 raw_data.expiration, 增加一个小时
tx['raw_data']['expiration'] += 3600 * 1000
new_expiration_hex = my_encode_int64(tx['raw_data']['expiration'])
#也要改变 raw_data_hex 中相应的字段
# tmp_hex = tx['raw_data_hex'][30:]
# tx['raw_data_hex'] = tx['raw_data_hex'][0:30]
raw_data_hex = str(tx['raw_data_hex'])
index = raw_data_hex.find(old_expiration_hex)
logger.info( "index : {}".format( index) )
new_raw_data_hex = raw_data_hex.replace(old_expiration_hex, new_expiration_hex)
old_txid = hashlib.sha256( unhexlify( tx['raw_data_hex'] )).hexdigest()
new_txid = hashlib.sha256( unhexlify( new_raw_data_hex) ).hexdigest()
if old_txid == tx['txID'] :
logger.info('txid 比对成功!')
else:
logger.info('txid比对失败!')
tx['txID'] = new_txid
sign = trx.sign(tx)
my_sig = new_ecsign( unhexlify( new_txid), unhexlify( privkey) )
logger.info( type(hexlify(my_sig)) )
logger.info( type(sign['signature'][0] ))
logger.info( "我的签名: {}".format( str(hexlify(my_sig), encoding='latin') ))
logger.info("原始签名: {}".format( sign['signature'][0] ))
if sign['signature'][0] == str(hexlify(my_sig), encoding='latin'):
logger.info('签名对比成功!')
else:
logger.info('签名对比失败')
sleep(61) #休眠 61秒, 来测试修改 expiration的效果
result = trx.broadcast(sign)
pprint(result)
#测试成功 txid为: 8c2b0a40812be8bcee3f92587e6824dc5a6035572cffe13707211821085ea7d3
pass
if __name__ == '__main__':
main()