Wie baut man eine einfache Transaktion auf?

  1. Wo sind die Informationen zum TX-Paketerstellungsprotokoll? Bitcoin als Beispiel, Bitcoin Wiki - Protocol Documentation: tx

  2. Gibt es ein Beispiel für die Serialisierung für Python wie diese Antwort auf Wie man einen einfachen Tx einlöst?

Alles, was in Google gefunden wurde, verwendete verschiedene Bibliotheken.

Die formale Definition befindet sich im Yellow Paper von Ethereum, Abschnitt 4.2 Transaktion. Insbesondere L_T(T).
Mit Sicherheit! Gelbes Papier! Bei Ethereum ist es ungewöhnlich dekoriert. Ich werde versuchen, einen Test-Dump zu erstellen.
Ich habe ein Serialisierungsmodul geschrieben. Habe eine Testtransaktion durchgeführt. Die letzte verbleibende Frage ist, wie berechnet man die Werte von gasPrice, gasLimit? Beim ersten Mal habe ich Testwerte gepostet gasPrice = 50000 gasLimit = 21000. Aber mir fehlt mehr Aussagekraft.
Wenn Sie in der Site suchen, wurden diese Fragen bereits einige Male gestellt. Für weitere Details ist es besser, eine neue Frage zu erstellen.
Dieser Link erklärt es sehr gut: medium.com/@codetractio/…

Antworten (1)

1) Ismael antwortete in den Kommentaren, dass die Informationen im Yellow Paper verfügbar sind

2) Aus dem gelben Papier von Ethereum wissen wir, dass die logische Struktur einer Transaktion wie folgt ist.

-----------------------------
| 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       |
-----------------------------

Notiz:

  1. Dies ist nur die logische Struktur. Die tatsächlichen Daten sind im RLP-Format kodiert, sind also aufgrund des hinzugefügten Längenpräfixes länger.
  2. Das V-Feld war immer 1 Byte vor EIP-155. Es ist wahrscheinlich sicher zu sagen, dass jeder größere Kunde EIP-155 implementiert hat. Für das Hauptnetz, Testnetz, bleibt dieses Feld auch bei EIP-155 auf 1 Byte. Für private Netzwerke mit "Ketten-ID" mit größeren Werten kann dieses Feld viel länger sein. Siehe diese Frage.

Nehmen Sie zum Beispiel die Transaktion aus diesem Hash:

0x14a298c1eea89f42285948b7d51eeac2876ca7406c9784b9b90dd3591d156d64

Aus:

"0xf86b80850ba43b7400825208947917bc33eea648809c285607579c9919fb864f8f8703baf82d03a0008025a0067940651530790861714b2e8fd8b080361d1ada048189000c07a66848afde46a069b041db7c29dbcc6becf42017ca7ac086b12bd53ec8ee494596f790fb6a0a69"

das sind 109 Byte. Wenn wir die Daten analysieren

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)

Jetzt werde ich versuchen, die RLP-Bibliothek zu verwenden, um die Struktur auszuwählen.

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()

Ich wollte dem obigen Code zeigen, dass es möglich ist, eine Struktur zu wählen, aber das stimmt noch nicht, weil wir die Größe der Transaktion nicht berücksichtigen und nicht unterschreiben. Jetzt werden wir den Arbeitscode umschreiben.

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))

Funktion senden an:

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

Hier sehen Sie die fehlenden Module aus dem Code.

Das wichtigste! gasLimit wird für die Transaktion gleich 21000 sein, aber nicht für den Vertrag. gasPrice müssen diesen Link hierher bekommen. Nach Erhalt des gasPrice muss dieser von GWei in Wei umgerechnet werden

Ich fürchte, die Transaktion, die Sie als Beispiel verwenden, wurde von mir erstellt. Sie können gerne teilen, aber es wäre schön, zumindest eine angemessene Referenz zu haben, da das Schreiben eindeutig auf meinen Inhalten hier basiert lsongnotes.wordpress.com/2018/01/14/…
@LinmaoSong Danke für deine Arbeit, +1
@LinmaoSong, es ist wirklich lustig, dass ich Sie hier getroffen habe, da ich gerade dabei bin, Ihr Tutorial auf JS zu portieren --- Ich habe jedoch ein kleines Problem mit der ECDSA-Signatur, ich habe eine formelle Frage in einem anderen Thread gepostet um das hier nicht zu überladen, meinst du, du könntest es dir ansehen? ethereum.stackexchange.com/questions/83413/…
@LinmaoSong Vielleicht hast du recht, ich habe wahrscheinlich die Informationen auf deiner Seite studiert, aber das hier präsentierte Material ist vollständiger.
Diese Antwort sollte aktualisiert werden, um die neueste Implementierung widerzuspiegeln, die auch die Ketten-ID als RLP-Parameter enthält.
@GViz Hallo. Ich wollte wissen, ob Sie versucht haben, Smart Contracts mit Python zu senden? Laut Dokumentation habe ich die Anzahl der Gase erhöht, die Lieferadresse entfernt und den kompilierten Smart-Contract-Code aus dem remixOnline-Compiler im Parameter „data“ angegeben. Im Blockchain-Explorer erhalte ich einen Vertragsausführungsfehler. Meine Ropsten-Adresse ist 0xa8162E5f9922661677b56Ac4c094036c173aF7E7