diff --git a/Makefile b/Makefile index 09591dba..0b762f12 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ COLOUR_MAGB := \033[1;35m COLOUR_CYN := \033[36m COLOUR_CYNB := \033[1;36m -.PHONY: help up down start stop re logs logs-django ps db-shell migrate debug clean shred collect schema newkey swapmode maintenance cli CLI testlogin tests +.PHONY: help up down start stop re logs logs-django ps db-shell migrate debug clean shred collect schema newkey swapmode maintenance cli CLI testlogin tests testblockchain help: # Display this helpful message @awk 'BEGIN { \ @@ -131,11 +131,14 @@ testlogin: # Selenium tests docker exec ft_transcendence-django-1 python test_selenium.py @echo "Test done" -tests: # Run automated tests -# $(DC) -f $(SRC) start -# @docker exec -it ft_transcendence-blockchain-1 sh -c "npx hardhat test" +testcontract: # Run smart contract tests (run while containers are not running) @echo -e "$(COLOUR_MAGB)Testing smart contract$(COLOUR_END)" $(DC) -f $(SRC) run --rm blockchain npx hardhat test -# $(DC) -f $(SRC) run --rm django python manage.py test # should run on separate test volume $(DC) -f $(SRC) stop +testblockchain: # Run blockchain tests (run while containers are running) + @echo -e "$(COLOUR_MAGB)Testing committing data to the blockchain$(COLOUR_END)" + @echo -e "$(COLOUR_GREEN)Adding matches$(COLOUR_END)" + docker exec -it ft_transcendence-django-1 python manage.py test blockchain.tests.AddMatchViewTest + @echo -e "$(COLOUR_GREEN)Adding tournaments$(COLOUR_END)" + docker exec -it ft_transcendence-django-1 python manage.py test blockchain.tests.CommitTournamentViewTest diff --git a/is_deployed.py b/is_deployed.py new file mode 100644 index 00000000..0858ebd6 --- /dev/null +++ b/is_deployed.py @@ -0,0 +1,11 @@ +from blockchain.blockchain_api import PongBlockchain + +def main(*args, **kwargs): + if len(args) == 0: + return (1) + chain = PongBlockchain() + print(f"Blockchain connected: {chain.is_connected()}") + print(f"Connecting to {args[0]}... ") + chain.connect(args[0]) + print(f"Connected to {args[0]}: {chain.is_connected()}") + diff --git a/pong/blockchain/blockchain_explorer/add_player.sh b/pong/blockchain/blockchain_explorer/add_player.sh new file mode 100755 index 00000000..bba89bc4 --- /dev/null +++ b/pong/blockchain/blockchain_explorer/add_player.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +if [ "$#" -ne 4 ]; then + echo "Usage: $0 " + exit 1 +fi + +docker exec -it ft_transcendence-django-1 python -c " +from blockchain.blockchain_api import PongBlockchain, hash_player +import dotenv + +dotenv.load_dotenv() +chain = PongBlockchain() +chain.connect($1) +print(f'Contract deployed: {chain.is_deployed}') +if not chain.is_deployed: + print('Contract not deployed') + exit(1) +pk = os.getenv('HARDHAT_PRIVATE_KEY').strip('"') +sender = chain.accounts[0] +player_hash = hash_player([$3, $4]) +status = chain.addPlayerSimple(sender, pk, player_hash, $2) +if status['status'] == 1: + print(f'Player $2 added: with hash {player_hash}') +else: + print(f'Player $2 not added') +" \ No newline at end of file diff --git a/pong/blockchain/blockchain_explorer/bc b/pong/blockchain/blockchain_explorer/bc new file mode 100755 index 00000000..3fb3e62f --- /dev/null +++ b/pong/blockchain/blockchain_explorer/bc @@ -0,0 +1,26 @@ +#!/bin/bash +RELATIVE_PATH=. + +helpFunction() +{ + echo "Usage: $0 " + echo -e "\tis_connected" + echo -e "\tis_deployed " + exit 1 # Exit script after printing help +} + +if [ "$#" -lt 1 ]; then + helpFunction + exit 1 +fi + +case $1 in + "is_connected") + echo "Checking connection status..." + $RELATIVE_PATH/is_connected.sh + ;; + "is_deployed") + echo "Checking deployment status..." + $RELATIVE_PATH/is_deployed.sh $2 + ;; +esac \ No newline at end of file diff --git a/pong/blockchain/blockchain_explorer/is_connected.sh b/pong/blockchain/blockchain_explorer/is_connected.sh new file mode 100755 index 00000000..2cf7b337 --- /dev/null +++ b/pong/blockchain/blockchain_explorer/is_connected.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +docker exec -it ft_transcendence-django-1 python -c "from blockchain.blockchain_api import PongBlockchain;\ + chain = PongBlockchain();\ + print(f'Blockchain connected: {chain.is_connected()}');" \ No newline at end of file diff --git a/pong/blockchain/blockchain_explorer/is_deployed.sh b/pong/blockchain/blockchain_explorer/is_deployed.sh new file mode 100755 index 00000000..744bb98f --- /dev/null +++ b/pong/blockchain/blockchain_explorer/is_deployed.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +if [ "$#" -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +docker exec -it ft_transcendence-django-1 python -c "from blockchain.blockchain_api import PongBlockchain;\ + chain = PongBlockchain();\ + print(f'Blockchain connected: {chain.is_connected()}');\ + chain.connect($1);\ + print(f'Connected to {hex($1)}: {chain.is_connected()}');" diff --git a/pong/blockchain/experimental/compile_experimental.py b/pong/blockchain/experimental/compile_experimental.py deleted file mode 100644 index 257dc95a..00000000 --- a/pong/blockchain/experimental/compile_experimental.py +++ /dev/null @@ -1,56 +0,0 @@ -from solcx import compile_standard, install_solc, get_solc_version, set_solc_version -from solcx.exceptions import SolcNotInstalled -import json -from pathlib import Path - -BUILD_PATH = "static/blockchain/build/" -CONTRACT_PATH = "blockchain/hardhat/contracts/" - -def installCompiler(version='0.8.26'): - # Install solc version 0.8.26 - print("Installing solc version", version) - install_solc(version) - set_solc_version(version) - # Get the currently active solc version - active_version = get_solc_version() - print("Active solc version:", active_version) - -# @param filename: string -# @param contractName: string -# @return tuple: (abi, bytecode) -def compileSmartContract(filename, compiled_name): - try: - solc_version = get_solc_version() - except SolcNotInstalled: - print("solc not installed") - installCompiler() - solc_version = get_solc_version() - print("Compiling contract " + filename) - with open(CONTRACT_PATH + filename , "r") as file: - tournament_code = file.read() - compiled_sol = compile_standard({ - "language": "Solidity", # needs capital letter, fails with "solidity" - "sources": {filename: {"content": tournament_code}}, - "settings": { - "outputSelection": { - "*": { - "*": ["abi", "metadata", "evm.bytecode", "evm.sourceMap"] - } - } - } - }, solc_version=solc_version) - with open("static/blockchain/build/" + compiled_name, "w") as file: - json.dump(compiled_sol, file) - print("Contract compiled successfully to " + BUILD_PATH + compiled_name) - return Path(BUILD_PATH + compiled_name) - -def get_contract_metadata(filename="tournament.sol", contractName="PongTournament"): - compiled_name = filename.split(".")[0] + ".json" - compiled_path = Path(BUILD_PATH + compiled_name) - if not compiled_path.exists(): - compiled_path = compileSmartContract(filename, compiled_name) - with open(compiled_path, "r") as file: - compiled_sol = json.load(file) - abi = compiled_sol["contracts"][filename][contractName]["abi"] - bytecode = compiled_sol["contracts"][filename][contractName]["evm"]["bytecode"]["object"] - return (abi, bytecode) \ No newline at end of file diff --git a/pong/blockchain/experimental/deploy_experimental.py b/pong/blockchain/experimental/deploy_experimental.py deleted file mode 100644 index d064edd9..00000000 --- a/pong/blockchain/experimental/deploy_experimental.py +++ /dev/null @@ -1,169 +0,0 @@ -from web3 import Web3, HTTPProvider -import os -from dotenv import load_dotenv -from compile import get_contract_metadata -from web3 import EthereumTesterProvider -from eth_tester import PyEVMBackend, EthereumTester - -PATH_ENV = "../.env" - -def connect_to_web3(node): - # Connect to the Ethereum network - print(f"Connecting to {node}... ", end="") - web3 = Web3(HTTPProvider(node)) - if not web3.is_connected(): - raise ValueError("Web3 connection failed") - print("Connected") - return web3 - -def get_checksum_address(account): - print("Retrieving account... ", end="") - checksum_address = Web3.to_checksum_address(account) - print("Retrieved {}".format(checksum_address)) - return checksum_address - -def build_transaction(web3, contract, checksum_address, nonce): - # Build the constructor transaction for the contract - constructor_txn = contract.constructor().build_transaction({ - 'from': checksum_address, - 'nonce': nonce, - 'gas': 2000000, - 'gasPrice': web3.to_wei('50', 'gwei') - }) - return constructor_txn - -""" -On the local testnet the transaction is just a dictionary -""" -def build_transaction_local(checksum_address, nonce): - constructor_txn = { - 'from': checksum_address, - 'nonce': nonce, - 'gas': 2000000, - 'max_fee_per_gas': 2000000000, - 'max_priority_fee_per_gas': 2000000000 - } - return constructor_txn - -def sign_and_send_transaction(web3, constructor_txn, private_key, dry_run): - # Sign the transaction locally and send it - print("Signing transaction... ", end="") - signed_txn = web3.eth.account.sign_transaction(constructor_txn, private_key) - print("Signed") - if dry_run: - return None - print("Sending transaction... ") - tx_hash = web3.eth.send_raw_transaction(signed_txn.rawTransaction) - return tx_hash - -def deploy_sepolia_testnet(node, account, private_key, dry_run=True): - print("** DRY RUN mode **") if dry_run else print("** DEPLOYING CONTRACT **") - web3 = connect_to_web3(node) - checksum_address = get_checksum_address(account) - nonce = web3.eth.get_transaction_count(checksum_address) - # Creating contract object - contract = web3.eth.contract( - abi=abi, - bytecode=bytecode) - constructor_txn = build_transaction(web3, contract, checksum_address, nonce) - tx_hash = sign_and_send_transaction(web3, constructor_txn, private_key, dry_run) - if dry_run: - return - # Wait for the transaction receipt with a timeout (e.g., 120 seconds) - if tx_hash is None: - raise ValueError("Transaction failed") - receipt = web3.eth.wait_for_transaction_receipt(tx_hash, timeout=120) - address = receipt['contractAddress'] - print("** SUCCESS **\nDeployed at " + address) if address is not None else print("** FAILURE **") - print(receipt, end="\n\n") - print(f"Contract deployed at address: {address} with abi {abi}") - return address - -def deploy_local_testnet(): - web3 = Web3(EthereumTesterProvider()) - print("Connected to local testnet") if web3.is_connected() else print("Connection failed") - t = EthereumTester(PyEVMBackend()) - accounts = t.get_accounts() - account = accounts[0] - checksum_address = get_checksum_address(account) - nonce = web3.eth.get_transaction_count(checksum_address) - contract = web3.eth.contract( - abi=abi, - bytecode=bytecode) - constructor_txn = build_transaction_local(checksum_address, nonce) - tx_hash = t.send_transaction(constructor_txn) - receipt = t.get_transaction_receipt(tx_hash) - print("Contract deployed at address: ", receipt['contract_address']) - return receipt['contract_address'] - -def get_env_variables(*var_names): - env_vars = {} - for var_name in var_names: - if var_name not in os.environ: - raise ValueError(f"{var_name} environment variable not set") - env_vars[var_name] = os.getenv(var_name) - return env_vars - -abi, bytecode = get_contract_metadata() -address = deploy_local_testnet() - -web3 = Web3(EthereumTesterProvider()) -eth_tester = EthereumTester() -contract = web3.eth.contract(address, abi=abi) -params = { - 'from': web3.eth.accounts[0], - 'gas': 2000000, - 'gasPrice': web3.to_wei('50', 'gwei') -} - -nonce = web3.eth.get_transaction_count(web3.eth.accounts[0]) -print(f"Initial nonce: {nonce}") - -def send_transaction(tx): - try: - tx_hash = web3.eth.send_transaction(tx) - receipt = web3.eth.wait_for_transaction_receipt(tx_hash) - print(receipt, end="\n\n") - return receipt - except Exception as e: - print(f"Error sending transaction: {e}") - return None - -# Add first player -params['nonce'] = nonce -tx = contract.functions.addPlayer(1, "Foo").build_transaction(params) -receipt = send_transaction(tx) -if receipt: - nonce += 1 - -# Add second player -params['nonce'] = nonce -tx = contract.functions.addPlayer(2, "Bar").build_transaction(params) -receipt = send_transaction(tx) -if receipt: - nonce += 1 - -# Add match -params['nonce'] = nonce -tx = contract.functions.addMatch(1, 1, [1, 2], [6, 8], 2).build_transaction(params) -receipt = send_transaction(tx) -if receipt: - nonce += 1 - -# Debugging: Print the state of the contract -try: - player1 = contract.functions.players(1).call() - player2 = contract.functions.players(2).call() - match = contract.functions.matches(1).call() - print(f"Player 1: {player1}") - print(f"Player 2: {player2}") - print(f"Match: {match}") -except Exception as e: - print(f"Error retrieving contract state: {e}") - -# Query the winner of the match -try: - winner = contract.functions.getMatchWinner(1).call() - print(f"Match winner: {winner}") -except Exception as e: - print(f"Error calling contract function: {e}") \ No newline at end of file diff --git a/pong/blockchain/experimental/deploy_local.py b/pong/blockchain/experimental/deploy_local.py deleted file mode 100644 index 632bcc43..00000000 --- a/pong/blockchain/experimental/deploy_local.py +++ /dev/null @@ -1,53 +0,0 @@ -import environ -import os -from web3 import Web3, HTTPProvider - - - - -# def get_contract_metadata(filename="PongTournament.sol", contractName="PongTournament"): -# compiled_name = filename.split(".")[0] + ".json" -# compiled_path = Path(BUILD_PATH + compiled_name) -# if not compiled_path.exists(): -# compiled_path = compileSmartContract(filename, compiled_name) -# with open(compiled_path, "r") as file: -# compiled_sol = json.load(file) -# abi = compiled_sol["contracts"][filename][contractName]["abi"] -# bytecode = compiled_sol["contracts"][filename][contractName]["evm"]["bytecode"]["object"] -# return (abi, bytecode) - -# def build_generic_transaction(web3, contract, checksum_address, nonce): -# # Build the constructor transaction for the contract -# constructor_txn = contract.constructor().build_transaction({ -# 'from': checksum_address, -# 'nonce': nonce, -# 'gas': 2500000, -# 'gasPrice': web3.to_wei('50', 'gwei') -# }) -# return constructor_txn - -# def deploy_contract(web3, account, contract_path): -# abi, bytecode = get_contract_metadata(contract_path) -# print("Retrieved contract metadata") -# checksum_address = Web3.to_checksum_address(account) -# contract = web3.eth.contract(abi=abi, bytecode=bytecode) -# private_key = env.get_value('HARDHAT_PRIVATE_KEY') -# nonce = web3.eth.get_transaction_count(checksum_address) -# txn = build_generic_transaction(web3, contract, checksum_address, nonce) -# signed_txn = web3.eth.account.sign_transaction(txn, private_key) -# tx_hash = web3.eth.send_raw_transaction(signed_txn.rawTransaction) -# tx_receipt = web3.eth.wait_for_transaction_receipt(tx_hash) -# address = tx_receipt['contractAddress'] -# return address - -# def main(): - # web3 = Web3(HTTPProvider("http://blockchain:8545")) - # print("Connected") if web3.is_connected() else print("Connection failed") - - # accounts = web3.eth.accounts - # print("Retrieved accounts: {}".format(accounts)) - # account = accounts[0] - - # address = deploy_contract(web3, account, "PongTournament.sol") - - # print(f"Contract deployed at address: {address}") \ No newline at end of file diff --git a/pong/blockchain/experimental/interactBlockchain.py b/pong/blockchain/experimental/interactBlockchain.py deleted file mode 100644 index d61eed9d..00000000 --- a/pong/blockchain/experimental/interactBlockchain.py +++ /dev/null @@ -1,74 +0,0 @@ -from web3 import Web3, HTTPProvider -from dotenv import load_dotenv -import os -import json - -ENV_PATH = "../.env" -load_dotenv(ENV_PATH) - -def getMatchWinner(matchId): - return tournament.functions.getMatchWinner(matchId).call() - -def addMatch(matchId, tournamentId, players, scores, winner): - params['nonce'] = web3.eth.get_transaction_count(senderAddressChecksum) - transaction = tournament.functions.addMatch(matchId, tournamentId, players, scores, winner).build_transaction(params) - signedTx = web3.eth.account.sign_transaction(transaction, privateKey) - tx_hash = web3.eth.send_raw_transaction(signedTx.rawTransaction) - return web3.eth.wait_for_transaction_receipt(tx_hash, timeout=120) - -def addPlayer(playerId, name): - params['nonce'] = web3.eth.get_transaction_count(senderAddressChecksum) - transaction = tournament.functions.addPlayer(playerId, name).build_transaction(params) - signedTx = web3.eth.account.sign_transaction(transaction, privateKey) - tx_hash = web3.eth.send_raw_transaction(signedTx.rawTransaction) - return web3.eth.wait_for_transaction_receipt(tx_hash, timeout=120) - -def createTournament(tournamentId, winner): - params['nonce'] = web3.eth.get_transaction_count(senderAddressChecksum) - transaction = tournament.functions.createTournament(tournamentId, winner).build_transaction(params) - signedTx = web3.eth.account.sign_transaction(transaction, privateKey) - tx_hash = web3.eth.send_raw_transaction(signedTx.rawTransaction) - return web3.eth.wait_for_transaction_receipt(tx_hash, timeout=120) - -web3 = Web3(HTTPProvider(os.getenv('INFURA_TESTNET'))) -if not web3.is_connected(): - print("Connected failed") - raise ValueError("Web3 connection failed") -with open("static/blockchain/build/PongTournament.json") as f: - abi = json.load(f)["contracts"]["PongTournament.sol"]["PongTournament"]["abi"] -contractAddress = os.getenv('CONTRACT') -if contractAddress is None: - raise ValueError("CONTRACT environment variable not set") -senderAddress = os.getenv('ACCOUNT') -if senderAddress is None: - raise ValueError("ACCOUNT environment variable not set") -contractAddressChecksum = Web3.to_checksum_address(contractAddress) -senderAddressChecksum = Web3.to_checksum_address(senderAddress) -privateKey = os.getenv('PRIVATE_KEY') -tournament = web3.eth.contract(abi=abi, address=contractAddressChecksum) -params = { - 'from': senderAddressChecksum, - 'gas': 2000000, - 'gasPrice': web3.to_wei('50', 'gwei') -} - -def test_sepolia(): - print("**********************") - print("* Testing blockchain *") - print("**********************") - print("Status 0 means the transaction failed, 1 means it succeeded") - print("Add player Foo") - print(addPlayer(1, "Foo")) - print("Add player Bar") - print(addPlayer(2, "Bar")) - print("Add match Foo-Bar 6-8 belonging to TournamentId 1 with wrong winner") - print(addMatch(1, 1, [1, 2], [6, 8], 1)) - print("Add match Foo-Bar 6-8 belonging to TournamentId 1 with right winner") - print(addMatch(1, 1, [1, 2], [6, 8], 2)) - print("Add Tournament 1 won by Bar") - print(createTournament(1, 2)) - print("Querying the winner of Match 1") - print(getMatchWinner(1)) - -test_sepolia() - diff --git a/pong/blockchain/tests.py b/pong/blockchain/tests.py index 7ce503c2..599e5f81 100644 --- a/pong/blockchain/tests.py +++ b/pong/blockchain/tests.py @@ -1,3 +1,80 @@ -from django.test import TestCase +from django.test import TestCase, Client +from django.urls import reverse +from blockchain.blockchain_api import PongBlockchain, hash_player +import os # Create your tests here. +class AddMatchViewTest(TestCase): + def setUp(self): + self.client = Client() + self.url = reverse('addMatch') + self.chain = PongBlockchain() + self.player1 = { + 'name': 'foo', + 'email': 'foo@foo.com', + 'id': '1' + } + self.player2 = { + 'name': 'bar', + 'email': 'bar@bar.com', + 'id': '2' + } + self.data = { + 'player1': self.player1['name'], + 'player2': self.player2['name'], + 'player1_hash': hash_player([self.player1['email'], self.player1['id']]), + 'player2_hash': hash_player([self.player2['email'], self.player2['id']]), + 'score1': '10', + 'score2': '8', + 'winner_hash': hash_player(['foo@foo.com', '1']), + 'tournament_id': '1', + 'match_id': '1' + } + + def test_get_player(self): + player = self.chain.getPlayer(hash_player([self.player1['email'], self.player1['id']])) + self.assertEqual(player[4], self.player1['name']) + + def test_add_match_success(self): + sender = self.chain.accounts[0] + if 'HARDHAT_PRIVATE_KEY' in os.environ: + pk = os.getenv('HARDHAT_PRIVATE_KEY') + self.chain.addPlayerFull(sender, pk, hash_player([self.player1['email'], self.player1['id']]), self.player1['name'], self.chain.accounts[1]) + self.chain.addPlayerFull(sender, pk, hash_player([self.player2['email'], self.player2['id']]), self.player2['name'], self.chain.accounts[2]) + response = self.client.post(self.url, self.data) + self.assertEqual(response.status_code, 200) + self.assertIn('Match added successfully', response.content.decode()) + + def test_add_match_failure(self): + self.data['score1'] = '11' + response = self.client.post(self.url, self.data) + self.assertEqual(response.status_code, 400) + +class CommitTournamentViewTest(TestCase): + def setUp(self): + self.client = Client() + self.url = reverse('commit') + self.chain = PongBlockchain() + self.winner = { + 'name': 'foo', + 'email': 'foo@foo.com', + 'id': '1' + } + self.data = { + 'winner': hash_player(['foo@foo.com', '1']), + 'tournament_id': '1', + 'match_id': '1' + } + + def test_commit_tournament_success(self): + sender = self.chain.accounts[0] + if 'HARDHAT_PRIVATE_KEY' in os.environ: + pk = os.getenv('HARDHAT_PRIVATE_KEY') + self.chain.addPlayerFull(sender, pk, hash_player([self.winner['email'], self.winner['id']]), self.winner['name'], self.chain.accounts[1]) + response = self.client.post(self.url, self.data) + self.assertEqual(response.status_code, 200) + + def test_commit_tournament_failure(self): + self.data['winner'] = '42' + response = self.client.post(self.url, self.data) + self.assertEqual(response.status_code, 400) \ No newline at end of file diff --git a/pong/blockchain/urls.py b/pong/blockchain/urls.py index 37c603ae..e805381d 100644 --- a/pong/blockchain/urls.py +++ b/pong/blockchain/urls.py @@ -1,8 +1,9 @@ from django.urls import path -from .views import index, optin, commit +from .views import index, optin, commit, addMatch urlpatterns = [ path('', index, name='index'), path('optin/', optin, name='optin'), path('commit/', commit, name='commit'), + path('addMatch/', addMatch, name='addMatch'), ] \ No newline at end of file diff --git a/pong/blockchain/views.py b/pong/blockchain/views.py index 14e9b39c..d19f9fd9 100644 --- a/pong/blockchain/views.py +++ b/pong/blockchain/views.py @@ -5,6 +5,7 @@ from pong.context_processors import get_user_from_token from api.models import Profile from logging import getLogger +from django.http import HttpResponse, HttpResponseBadRequest logger = getLogger(__name__) @@ -44,5 +45,42 @@ def optin(request): return render(request, 'blockchain-optin.html', {'message': 'Opt in failed'}) def commit(request): - # TODO: implement commit on blockchain - return render(request, 'blockchain-commit.html', {'message': 'Tournament committed successfully'}) + if request.method == 'POST': + chain = PongBlockchain() + sender = chain.accounts[0] + if 'HARDHAT_PRIVATE_KEY' in os.environ: + pk = os.environ['HARDHAT_PRIVATE_KEY'] + tournament_id = int(request.POST.get('tournament_id')) + winner = int(request.POST.get('winner')) + try: + chain.createTournament(sender, pk, tournament_id, winner) + return HttpResponse('Tournament added successfully') + except: + return HttpResponseBadRequest('Failed to add tournament') + else: + return HttpResponse('Invalid request method') + +def addMatch(request): + if request.method == 'POST': + logger.info('Received POST request to add match') + chain = PongBlockchain() + sender = chain.accounts[0] + # Extract data from the POST request + if 'HARDHAT_PRIVATE_KEY' in os.environ: + pk = os.environ['HARDHAT_PRIVATE_KEY'] + player1_hash = int(request.POST.get('player1_hash')) + player2_hash = int(request.POST.get('player2_hash')) + score1 = int(request.POST.get('score1')) + score2 = int(request.POST.get('score2')) + winner_hash = int(request.POST.get('winner_hash')) + tournament_id = int(request.POST.get('tournament_id')) + match_id = int(request.POST.get('match_id')) + try: + response = chain.addMatch(sender, pk, match_id, tournament_id, [player1_hash, player2_hash], [score1, score2], winner_hash) + logger.info(f"Added match '{match_id}' to blockchain") + return HttpResponse('Match added successfully') + except: + logger.error(f"Failed to add match '{match_id}' to blockchain") + return HttpResponseBadRequest('Failed to add match') + else: + return HttpResponse('Invalid request method') \ No newline at end of file