By: Omar Nabil Metwally, MD
24 June 2024
Objective: To interact with an Ethereum application (“smart contract”)
Background: The German Federal Intelligence Service, die Bundesnachrichtendienst (BND), announced on 5 June 2023 a digital scavenger hunt to collect “Dogs of BND” themed NFTs (“Hunde der BND”, https://www.bnd.bund.de/DE/Karriere/SozialeMedien/Gewinnspiele/blockchain-challenge/teilnahmebedingungen-blockchain-challenge-node.html).
The only hints provided toward solving the puzzle are the following 40-character hexademical sequence and the knowledge that it is an Ethereum address: 0x6E02ffa16171ac74dC1688480A1F703C23994f3D
Environment:
This write-up assumes working knowledge of the Ethereum client written in the Go programming language (“Go” Ethereum client, aka “geth”, https://github.com/ethereum/go-ethereum), a fully synced node, and use of the clef command line utility (https://geth.ethereum.org/docs/tools/clef/introduction) to sign transactions and data.
Rationale:
The Ethereum ecosystem continues to evolve rapidly. Note that web3 methods based on the deprecated “personal” namespace resulted in potentially breaking changes for code used to interact with Ethereum contracts. The proper way to handle account locking and unlocking in the context of broadcasting transactions is to now use clef.
Start geth and clef as follows:
./clef –keystore /path/to/your/keystore/ –ipcpath=/your/path/to/clef/ipc –signersecret /your/clef/signer/secret
./geth –syncmode snap –datadir=/your/datadir/path –signer=/your/path/to/clef/ipc console
Task #1: Query 0x6E02ffa16171ac74dC1688480A1F703C23994f3D
The above hexademical sequence appears to be an Ethereum address, based on the knowledge that Ethereum addresses are 40-character hexademical sequences preceded by 0x.
This can be verified using geth:
eth.getBalance(“0x6E02ffa16171ac74dC1688480A1F703C23994f3D”)
This yields a balance of 316649482296000 wei, or 0.000316649482296 Ether.
Call this address [ORIGIN]. Looking up this address on etherscan.io (https://etherscan.io/address/0x6E02ffa16171ac74dC1688480A1F703C23994f3D) reveals that [ORIGIN] is directly associated with two transactions.
Transaction 1 on block 17393444 entails a transfer of 0.04879799 ETH from [ORIGIN] to 0x2B127A04c4DA063dB1E75BAC1b007D5C0661570a. Call this recipient [RECIPIENT 1]. The transaction hash for transaction 1 is 0x30daf5adac61330817ba48bdcc093df402b992f7112898c88d83f025d3c2dd6d
Transaction 2 on block 17393441 entails a transfer of 0.05 ETH from 0x12CF21eE48426b0E8f9bE4704C38aAba6E9ab988 to [ORIGIN]. Call this sender [SENDER 1]. The transaction hash for transaction 2 is 0xa61b9a442c9a6836390d422c81597608a63bef0c6d71dc220793521edb188f80.
[RECIPIENT 1] created a contract at 0xb47c23d001c0c9f5c1a158a93b6df6004b6012f7 (transaction hash 0xd5b3d8b57316fecdfb958815e1c0d3079a5c5826e99c924c87cf11014b1b31d7 on block 17413916. Call this [CONTRACT 1], and call this transaction [TRANSACTION_CONTRACT_1_CREATE].
32 transactions have been sent to [CONTRACT 1] at the time of writing, the first of which constructed the contract, and the rest invoked a method called “updateMessage”. Each transaction can be queried using the python web3 library.
Etherscan.io provides the contract source code and indicates that it is “verified.” It is marked with the following comments:
// Herzlichen Glückwunsch!
// Du hast diese versteckte Nachricht erfolgreich finden können!
// Damit hast du nun die Möglichkeit, dir als eine oder einer der Ersten ein
// exklusives Hunde-NFT aus unserer Collection zu sichern (nur solange der Vorrat reicht).
// Du findest die Collection unter diesem Link auf Opensea: opensea.io/collection/dogs-of-bnd
// (Bitte beachte die Teilnahme- und Datenschutzbedingungen unter bnd.de/nft).
Task #2: Verify contract source code
To verify the bytecode of the provided source code and verify its authenticity, compile the provided source code and compare the resulting bytecode with the bytecode at the corresponding address, [CONTRACT 1], on Ethereum mainnet.
The easiest way to compile the source code is using the Remix compiler at https://remix.ethereum.org. Doing so yields the following ABI and bytecode:
abi: [ { “inputs”: [ { “internalType”: “string”, “name”: “_message”, “type”: “string” } ], “stateMutability”: “nonpayable”, “type”: “constructor” }, { “inputs”: [ { “internalType”: “string”, “name”: “_newMessage”, “type”: “string” } ], “name”: “updateMessage”, “outputs”: [], “stateMutability”: “nonpayable”, “type”: “function” }, { “inputs”: [], “name”: “message”, “outputs”: [ { “internalType”: “string”, “name”: “”, “type”: “string” } ], “stateMutability”: “view”, “type”: “function” } ]
bytecode: 60806040523480156200001157600080fd5b5060405162000bee38038062000bee8339818101604052810190620000379190620001e3565b80600090816200004891906200047f565b505062000566565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620000b9826200006e565b810181811067ffffffffffffffff82111715620000db57620000da6200007f565b5b80604052505050565b6000620000f062000050565b9050620000fe8282620000ae565b919050565b600067ffffffffffffffff8211156200012157620001206200007f565b5b6200012c826200006e565b9050602081019050919050565b60005b83811015620001595780820151818401526020810190506200013c565b60008484015250505050565b60006200017c620001768462000103565b620000e4565b9050828152602081018484840111156200019b576200019a62000069565b5b620001a884828562000139565b509392505050565b600082601f830112620001c857620001c762000064565b5b8151620001da84826020860162000165565b91505092915050565b600060208284031215620001fc57620001fb6200005a565b5b600082015167ffffffffffffffff8111156200021d576200021c6200005f565b5b6200022b84828501620001b0565b91505092915050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200028757607f821691505b6020821081036200029d576200029c6200023f565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620003077fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82620002c8565b620003138683620002c8565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b6000620003606200035a62000354846200032b565b62000335565b6200032b565b9050919050565b6000819050919050565b6200037c836200033f565b620003946200038b8262000367565b848454620002d5565b825550505050565b600090565b620003ab6200039c565b620003b881848462000371565b505050565b5b81811015620003e057620003d4600082620003a1565b600181019050620003be565b5050565b601f8211156200042f57620003f981620002a3565b6200040484620002b8565b8101602085101562000414578190505b6200042c6200042385620002b8565b830182620003bd565b50505b505050565b600082821c905092915050565b6000620004546000198460080262000434565b1980831691505092915050565b60006200046f838362000441565b9150826002028217905092915050565b6200048a8262000234565b67ffffffffffffffff811115620004a657620004a56200007f565b5b620004b282546200026e565b620004bf828285620003e4565b600060209050601f831160018114620004f75760008415620004e2578287015190505b620004ee858262000461565b8655506200055e565b601f1984166200050786620002a3565b60005b8281101562000531578489015182556001820191506020850194506020810190506200050a565b868310156200055157848901516200054d601f89168262000441565b8355505b6001600288020188555050505b505050505050565b61067880620005766000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80631923be241461003b578063e21f37ce14610057575b600080fd5b61005560048036038101906100509190610270565b610075565b005b61005f610088565b60405161006c9190610338565b60405180910390f35b80600090816100849190610570565b5050565b6000805461009590610389565b80601f01602080910402602001604051908101604052809291908181526020018280546100c190610389565b801561010e5780601f106100e35761010080835404028352916020019161010e565b820191906000526020600020905b8154815290600101906020018083116100f157829003601f168201915b505050505081565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b61017d82610134565b810181811067ffffffffffffffff8211171561019c5761019b610145565b5b80604052505050565b60006101af610116565b90506101bb8282610174565b919050565b600067ffffffffffffffff8211156101db576101da610145565b5b6101e482610134565b9050602081019050919050565b82818337600083830152505050565b600061021361020e846101c0565b6101a5565b90508281526020810184848401111561022f5761022e61012f565b5b61023a8482856101f1565b509392505050565b600082601f8301126102575761025661012a565b5b8135610267848260208601610200565b91505092915050565b60006020828403121561028657610285610120565b5b600082013567ffffffffffffffff8111156102a4576102a3610125565b5b6102b084828501610242565b91505092915050565b600081519050919050565b600082825260208201905092915050565b60005b838110156102f35780820151818401526020810190506102d8565b60008484015250505050565b600061030a826102b9565b61031481856102c4565b93506103248185602086016102d5565b61032d81610134565b840191505092915050565b6000602082019050818103600083015261035281846102ff565b905092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806103a157607f821691505b6020821081036103b4576103b361035a565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b60006008830261041c7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff826103df565b61042686836103df565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b600061046d6104686104638461043e565b610448565b61043e565b9050919050565b6000819050919050565b61048783610452565b61049b61049382610474565b8484546103ec565b825550505050565b600090565b6104b06104a3565b6104bb81848461047e565b505050565b5b818110156104df576104d46000826104a8565b6001810190506104c1565b5050565b601f821115610524576104f5816103ba565b6104fe846103cf565b8101602085101561050d578190505b610521610519856103cf565b8301826104c0565b50505b505050565b600082821c905092915050565b600061054760001984600802610529565b1980831691505092915050565b60006105608383610536565b9150826002028217905092915050565b610579826102b9565b67ffffffffffffffff81111561059257610591610145565b5b61059c8254610389565b6105a78282856104e3565b600060209050601f8311600181146105da57600084156105c8578287015190505b6105d28582610554565b86555061063a565b601f1984166105e8866103ba565b60005b82811015610610578489015182556001820191506020850194506020810190506105eb565b8683101561062d5784890151610629601f891682610536565b8355505b6001600288020188555050505b50505050505056fea26469706673582212204326a68b08e2f4dc500c89e18af564f403f4aaab0887e352cdb0a6cabba9228e64736f6c63430008120033
Now query [TRANSACTION_CONTRACT_1_CREATE] with the following block of python code:
from web3 import Web3, HTTPProvider, IPCProvider
web3 = Web3(IPCProvider(‘/path/to/your/geth.ipc’))
# sanity check
web3.eth.block_number
# query first transaction
tx = web3.eth.get_transaction(“0xd5b3d8b57316fecdfb958815e1c0d3079a5c5826e99c924c87cf11014b1b31d7”)
# contract bytecode
tx.input
Note that tx.input is very similar, but not equal to, the bytecode from the allegedly verified source code we compiled using Remix. The difference is an appended 96-byte sequence trailing the compiled bytecode, with each byte in the bytecode represented by a pair of hexadecimal characters. 169 of these appended 192 characters are zeros.
Pythonically converting the non-zero bytes (7363687265696220756e73) to UTF-8 encoded string:
bytes.fromhex(‘7363687265696220756e73’).decode(‘utf-8’)
Yields ‘schreib uns’. (‘Screib uns” means “write [to] us” in German.
The “verified” contract source code appears to be authentic, and the appended bytes comprise the initial value of public variable “message” that was passed to the contract constructor when the contract was deployed.
Task #3: Interact with deployed contract
[CONTRACT 1] source code is simple and consists of public variable of type “string” called “message”, a constructor that accepts a string as an argument, and function “updateMessage” of scope “public” which accepts string-type argument.
So, let’s drop the Dogs of BND a line. To do so, save the contract abi and bytecode on your local machine. In this case, I saved them as “dogs_of_bnd_abi” and “dogs_of_bnd_bin”, respectively.
In Python shell:
abi_path = ‘/path/to/dogs_of_bnd_abi’
bin_path = ‘/path/to/dogs_of_bnd_bin’
bin_file = open(bin_path,’r’)
abi_file = open(abi_path,’r’)
bytecode = bin_file.read()
abi = abi_file.read()
# set your default Ethereum accounts
# note that transaction signing is handled via clef, per above
# when the command line appears to freeze, it’s probably because clef is waiting for [y/N] input
web3.eth.default_account = web3.eth.accounts[0]
bndContract = web3.eth.contract(address=”0xB47C23D001c0c9F5C1A158a93b6dF6004b6012f7″,abi=abi)
bndContract.functions.updateMessage(“Information is all.”).transact()