|
13 | 13 | # limitations under the License. |
14 | 14 |
|
15 | 15 | import ast |
| 16 | +import hashlib |
16 | 17 | import re |
17 | 18 | from collections import defaultdict |
18 | 19 | from types import ModuleType |
|
23 | 24 | from hathor.conf.settings import HathorSettings |
24 | 25 | from hathor.crypto.util import decode_address, get_address_from_public_key_bytes |
25 | 26 | from hathor.daa import DifficultyAdjustmentAlgorithm |
26 | | -from hathor.dag_builder.builder import FEE_KEY, NC_DEPOSIT_KEY, NC_WITHDRAWAL_KEY, DAGBuilder, DAGNode |
| 27 | +from hathor.dag_builder.builder import ( |
| 28 | + FEE_KEY, |
| 29 | + NC_DEPOSIT_KEY, |
| 30 | + NC_TRANSFER_INPUT_KEY, |
| 31 | + NC_TRANSFER_OUTPUT_KEY, |
| 32 | + NC_WITHDRAWAL_KEY, |
| 33 | + DAGBuilder, |
| 34 | + DAGNode, |
| 35 | +) |
27 | 36 | from hathor.dag_builder.types import DAGNodeType, VertexResolverType, WalletFactoryType |
28 | 37 | from hathor.dag_builder.utils import get_literal, is_literal |
29 | 38 | from hathor.nanocontracts import Blueprint, OnChainBlueprint |
|
42 | 51 | from hathor.transaction.base_transaction import TxInput, TxOutput |
43 | 52 | from hathor.transaction.headers.fee_header import FeeHeader, FeeHeaderEntry |
44 | 53 | from hathor.transaction.headers.nano_header import ADDRESS_LEN_BYTES |
| 54 | +from hathor.transaction.headers.transfer_header import TxTransferInput, TxTransferOutput |
45 | 55 | from hathor.transaction.scripts.p2pkh import P2PKH |
46 | 56 | from hathor.transaction.token_creation_tx import TokenCreationTransaction |
47 | 57 | from hathor.wallet import BaseWallet, HDWallet, KeyPair |
@@ -330,6 +340,69 @@ def add_headers_if_needed(self, node: DAGNode, vertex: BaseTransaction) -> None: |
330 | 340 | """Add the configured headers.""" |
331 | 341 | self.add_nano_header_if_needed(node, vertex) |
332 | 342 | self.add_fee_header_if_needed(node, vertex) |
| 343 | + self.add_transfer_header_if_needed(node, vertex) |
| 344 | + |
| 345 | + def _get_token_index(self, token_name: str, vertex: Transaction) -> int: |
| 346 | + token_index = 0 |
| 347 | + if token_name != 'HTR': |
| 348 | + token_creation_tx = self._vertices[token_name] |
| 349 | + if token_creation_tx.hash not in vertex.tokens: |
| 350 | + vertex.tokens.append(token_creation_tx.hash) |
| 351 | + token_index = 1 + vertex.tokens.index(token_creation_tx.hash) |
| 352 | + return token_index |
| 353 | + |
| 354 | + def add_transfer_header_if_needed(self, node: DAGNode, vertex: BaseTransaction) -> None: |
| 355 | + inputs = node.get_attr_list(NC_TRANSFER_INPUT_KEY, default=[]) |
| 356 | + outputs = node.get_attr_list(NC_TRANSFER_OUTPUT_KEY, default=[]) |
| 357 | + |
| 358 | + if not inputs and not outputs: |
| 359 | + return |
| 360 | + |
| 361 | + if not isinstance(vertex, Transaction): |
| 362 | + raise TypeError('TransferHeader is only supported for transactions') |
| 363 | + |
| 364 | + transfer_inputs: list[TxTransferInput] = [] |
| 365 | + for wallet_name, token_name, amount in inputs: |
| 366 | + wallet = self.get_wallet(wallet_name) |
| 367 | + assert isinstance(wallet, HDWallet) |
| 368 | + privkey = wallet.get_key_at_index(0) |
| 369 | + pubkey_bytes = privkey.sec() |
| 370 | + address = get_address_from_public_key_bytes(pubkey_bytes) |
| 371 | + token_index = self._get_token_index(token_name, vertex) |
| 372 | + |
| 373 | + sighash_data = vertex.get_sighash_all_data() |
| 374 | + sighash_data_hash = hashlib.sha256(sighash_data).digest() |
| 375 | + signature = privkey.sign(sighash_data_hash) |
| 376 | + script = P2PKH.create_input_data(public_key_bytes=pubkey_bytes, signature=signature) |
| 377 | + |
| 378 | + transfer_inputs.append(TxTransferInput( |
| 379 | + address=address, |
| 380 | + amount=amount, |
| 381 | + token_index=token_index, |
| 382 | + script=script, |
| 383 | + )) |
| 384 | + |
| 385 | + transfer_outputs: list[TxTransferOutput] = [] |
| 386 | + for wallet_name, token_name, amount in outputs: |
| 387 | + wallet = self.get_wallet(wallet_name) |
| 388 | + assert isinstance(wallet, HDWallet) |
| 389 | + privkey = wallet.get_key_at_index(0) |
| 390 | + pubkey_bytes = privkey.sec() |
| 391 | + address = get_address_from_public_key_bytes(pubkey_bytes) |
| 392 | + token_index = self._get_token_index(token_name, vertex) |
| 393 | + transfer_outputs.append(TxTransferOutput( |
| 394 | + address=address, |
| 395 | + amount=amount, |
| 396 | + token_index=token_index, |
| 397 | + )) |
| 398 | + |
| 399 | + from hathor.transaction.headers import TransferHeader |
| 400 | + transfer_header = TransferHeader( |
| 401 | + tx=vertex, |
| 402 | + inputs=transfer_inputs, |
| 403 | + outputs=transfer_outputs, |
| 404 | + ) |
| 405 | + vertex.headers.append(transfer_header) |
333 | 406 |
|
334 | 407 | def add_nano_header_if_needed(self, node: DAGNode, vertex: BaseTransaction) -> None: |
335 | 408 | if 'nc_id' not in node.attrs: |
|
0 commit comments