¿Dónde está la información sobre el protocolo de compilación del paquete tx? Bitcoin como ejemplo, Bitcoin Wiki - Documentación de protocolo: tx
¿Hay un ejemplo de serialización para python como esta respuesta a Cómo canjear un Tx básico?
Todo lo que se encontró en Google, estaba usando diferentes bibliotecas.
1) Ismael respondió en los comentarios que la información está disponible en Papel Amarillo
2) Por el papel amarillo de Ethereum, sabemos que la estructura lógica de una transacción es la siguiente.
-----------------------------
| Nonce | Up to 32 bytes |
-----------------------------
| GasPrice | Up to 32 bytes |
-----------------------------
| GasLimit | Up to 32 bytes |
-----------------------------
| To | 20 bytes addr |
-----------------------------
| Value | Up to 32 bytes |
-----------------------------
| Data | 0 - unlimited |
-----------------------------
| V | 1 (usually) |
-----------------------------
| R | 32 bytes |
-----------------------------
| S | 32 bytes |
-----------------------------
Nota:
Tomemos, por ejemplo, la transacción de este hash:
0x14a298c1eea89f42285948b7d51eeac2876ca7406c9784b9b90dd3591d156d64
Afuera:
"0xf86b80850ba43b7400825208947917bc33eea648809c285607579c9919fb864f8f8703baf82d03a0008025a0067940651530790861714b2e8fd8b080361d1ada048189000c07a66848afde46a069b041db7c29dbcc6becf42017ca7ac086b12bd53ec8ee494596f790fb6a0a69"
que es de 109 bytes. Si analizamos los datos
f86b length
80 nonce (0: this is the minimum an account can have)
85 0ba43b7400 gas price
82 5208 gas limit (this is fixed for simple payments)
94 7917bc33eea648809c285607579c9919fb864f8f (address, always 20 bytes)
87 03baf82d03a000 (value, in theory this can be shrunken to zero)
80 (data, already zero length)
25 (V, one byte)
a0 067940651530790861714b2e8fd8b080361d1ada048189000c07a66848afde46 (R)
a0 69b041db7c29dbcc6becf42017ca7ac086b12bd53ec8ee494596f790fb6a0a69 (S)
Ahora intentaré usar la biblioteca RLP para seleccionar la estructura.
import rlp
tx_message = list()
# tx_len - f8
tx_nonce = ''
tx_gasPrice = 0x0ba43b7400
tx_gasLimit = 0x5208
tx_to = 0xcce5fd90eabab3d5d35119eed7f2ac5796e3d06c
tx_value = 0x03baf82d03a000
tx_data = 0x00
tx_w = 0x25
tx_r = 0x067940651530790861714b2e8fd8b080361d1ada048189000c07a66848afde46
tx_s = 0x69b041db7c29dbcc6becf42017ca7ac086b12bd53ec8ee494596f790fb6a0a69
tx_message.extend(
(
rlp.encode(tx_nonce),
rlp.encode(tx_gasPrice),
rlp.encode(tx_gasLimit),
rlp.encode(tx_to),
rlp.encode(tx_value),
rlp.encode(tx_data),
rlp.encode(tx_w),
rlp.encode(tx_r),
rlp.encode(tx_s),
)
)
result_b = b''.join(tx_message)
result = result_b.hex()
Quería mostrarle al código anterior que es posible elegir una estructura, pero esto aún no es cierto, porque no consideramos el tamaño de la transacción y no la firmamos. Ahora vamos a reescribir el código de trabajo.
class Transaction(rlp.Serializable):
fields = [
('nonce', big_endian_int),
('gasprice', big_endian_int),
('startgas', big_endian_int),
('to', Binary.fixed_length(20, allow_empty=True)),
('value', big_endian_int),
('data', binary),
('v', big_endian_int),
('r', big_endian_int),
('s', big_endian_int),
]
_sender = None
def __init__(self, nonce, gasprice, startgas, to, value, data, v=0, r=0, s=0):
# self.data = None
to = normalize_address(to, allow_blank=True)
super(Transaction, self).__init__(nonce, gasprice, startgas, to, value, data, v, r, s)
if gasprice >= TT256 or startgas >= TT256 or value >= TT256 or nonce >= TT256:
logging.error("Values way too high!")
def sign(self, key, network_id=None):
"""
Sign this transaction with a private key.
A potentially already existing signature would be overridden.
"""
if network_id is None:
rawhash = sha3(
rlp.encode(
unsigned_tx_from_tx(self),
UnsignedTransaction
)
)
else:
assert 1 <= network_id < 2 ** 63 - 18
rawhash = sha3(
rlp.encode(
rlp.infer_sedes(self).serialize(self)[:-3] +
[network_id, b'', b'']
)
)
key = normalize_key(key)
v, r, s = ecsign(rawhash, key)
if network_id is not None:
self.v += 8 + network_id * 2
ret = self.copy(v=v,
r=r,
s=s)
ret._sender = privtoaddr(key)
return ret
class UnsignedTransaction(rlp.Serializable):
fields = []
for field, sedes in Transaction._meta.fields:
if field not in "vrs":
fields.append((field, sedes))
def unsigned_tx_from_tx(tx):
return UnsignedTransaction(
nonce=tx.nonce,
gasprice=tx.gasprice,
startgas=tx.startgas,
to=tx.to,
value=tx.value,
data=tx.data,
)
def fetch_url_json_path_int(url, path):
def func():
request = req.Request(url, headers={'User-Agent': 'wallet'})
try:
payload = req.urlopen(request).read()
except Exception as e:
logging.error(f'[fetch_url_json_path_int] {e}')
try:
data = loads(payload)
for component in path.split('/'):
if isinstance(data, dict):
data = data[component]
elif isinstance(data, (list, tuple)):
data = data[int(component)]
return data
except Exception as e:
return data
return func
def get_tx_count(address):
return fetch_url_json_path_int(f'{url_tx_count}{address}', 'result')()
url_tx_count = 'https://api.etherscan.io/api?module=proxy&action=eth_getTransactionCount&address='
private_key = hashlib.sha256('This keyword!!!'.encode()).hexdigest()
nonce = int(get_tx_count('0xcce5fd90eabab3d5d35119eed7f2ac5796e3d06c'), 16)
gasPrice = 28500000000
gasLimit = 21000
to = 0x77f5055E19247E091e0C5bb3483190F9E6E43d3f
value = 0
data = codecs.decode('', 'hex')
transaction = Transaction(
nonce=nonce,
gasprice=gasPrice,
startgas=gasLimit,
to=to,
value=value,
data=data,
).sign(private_key)
print(rlp.encode(transaction))
Función enviar a:
url_broadcast_transaction = 'https://api.blockcypher.com/v1/eth/main/txs/push'
def broadcast_transaction(message):
timeout_in_second = 10
data = {'tx': message.hex()}
params = {'token': None}
r = requests.post(
url=url_broadcast_transaction,
json=data,
params=params,
verify=True,
timeout=timeout_in_second
)
logging.debug(r.text)
return r.text
Aquí puede ver los módulos que faltan en el código.
¡El más importante! gasLimit será igual a 21000 para la transacción, pero no para el contrato. gasPrice necesita llegar aquí este enlace. Después de recibir gasPrice, debe convertirse de GWei a Wei
ismael
Джон Смит
Джон Смит
ismael
ernesto gomez