I’m developing a blockchain-based forensic evidence system using Solidity (deployed via Hardhat/Ganache), a Python Flask backend, and Web3.py. My smart contract is designed to store evidence details (including a file hash, filename, UNIX timestamp, investigator, and location). When I call the addEvidence function from my Flask app, the transaction reverts with no clear error reason.
Error Message:
Blockchain ContractLogicError: ('execution reverted: VM Exception while processing transaction: revert', {'hash': None, 'programCounter': 70, 'result': '0x', 'reason': None, 'message': 'revert'})
Solidity Contract Code (EvidenceStorage.sol):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract EvidenceStorage {
struct Evidence {
string fileHash;
string fileName;
uint timestamp; // UNIX timestamp
string investigator;
string location;
}
mapping(string => Evidence) private evidences;
string[] private evidenceHashes;
event EvidenceAdded(
string fileHash,
string fileName,
uint timestamp,
string investigator,
string location
);
function addEvidence(
string memory _fileHash,
string memory _fileName,
uint _timestamp,
string memory _investigator,
string memory _location
) public {
// Revert if the evidence already exists.
require(bytes(evidences[_fileHash].fileHash).length == 0, "Evidence already exists!");
// Here we use block.timestamp rather than _timestamp
uint currentTime = block.timestamp;
Evidence memory newEvidence = Evidence({
fileHash: _fileHash,
fileName: _fileName,
timestamp: currentTime,
investigator: _investigator,
location: _location
});
evidences[_fileHash] = newEvidence;
evidenceHashes.push(_fileHash);
emit EvidenceAdded(_fileHash, _fileName, currentTime, _investigator, _location);
}
function getEvidence(string memory _fileHash)
public
view
returns (string memory, string memory, uint, string memory, string memory)
{
require(bytes(evidences[_fileHash].fileHash).length != 0, "Evidence not found!");
Evidence memory e = evidences[_fileHash];
return (e.fileHash, e.fileName, e.timestamp, e.investigator, e.location);
}
function getAllEvidenceHashes() public view returns (string[] memory) {
return evidenceHashes;
}
}
Python Code Snippet (from blockchain.py):
from web3 import Web3
import json
import os
from dotenv import load_dotenv
load_dotenv()
GANACHE_URL = os.getenv("GANACHE_RPC_URL")
CONTRACT_ADDRESS = os.getenv("CONTRACT_ADDRESS")
web3 = Web3(Web3.HTTPProvider(GANACHE_URL))
assert web3.is_connected(), "Failed to connect to Ganache!"
with open('hardhat_evidence/artifacts/contracts/EvidenceStorage.sol/EvidenceStorage.json') as f:
contract_json = json.load(f)
contract_abi = contract_json['abi']
contract = web3.eth.contract(address=CONTRACT_ADDRESS, abi=contract_abi)
account = web3.eth.accounts[0]
def add_evidence(evidence_hash, filename, timestamp, investigator, location):
txn = contract.functions.addEvidence(evidence_hash, filename, timestamp, investigator, location).transact({'from': account})
web3.eth.wait_for_transaction_receipt(txn)
Flask Code Snippet (from evidence_manager.py):
from flask import Flask, render_template, request, redirect
from app.blockchain import add_evidence, get_evidence, get_all_hashes
from app.utils import calculate_hash, get_current_unix_timestamp, unix_to_readable
import os
app = Flask(__name__)
TEMP_FOLDER = 'temp'
if not os.path.exists(TEMP_FOLDER):
os.makedirs(TEMP_FOLDER)
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
file = request.files['evidenceFile']
investigator = request.form['investigator']
location = request.form['location']
if file:
file_path = os.path.join(TEMP_FOLDER, file.filename)
file.save(file_path)
evidence_hash = calculate_hash(file_path)
timestamp_unix = get_current_unix_timestamp()
add_evidence(evidence_hash, file.filename, timestamp_unix, investigator, location)
os.remove(file_path)
return redirect('/view')
return render_template('index.html')
@app.route('/view')
def view():
hashes = get_all_hashes()
evidence_list = []
for h in hashes:
evi = get_evidence(h)
readable_time = unix_to_readable(evi[2])
evidence_list.append({
'hash': evi[0],
'filename': evi[1],
'timestamp': readable_time,
'investigator': evi[3],
'location': evi[4]
})
return render_template('view_evidence.html', evidence_list=evidence_list)
if __name__ == '__main__':
app.run(debug=True)
My Environment: Ganache as the local Ethereum node. Hardhat for smart contract development. Python 3.10, Flask, and Web3.py for the backend.
My Problem: When I try to add evidence (i.e., call addEvidence via my Flask app), the transaction reverts with:
ContractLogicError: ('execution reverted: VM Exception while processing transaction: revert', {'hash': None, 'programCounter': 70, 'result': '0x', 'reason': None, 'message': 'revert'})
The revert occurs even though: I’m sending the correct data types (all strings and a uint timestamp). My require in addEvidence should only revert if evidence with the same hash already exists.
Questions: 1.How can I obtain a more detailed revert reason from the transaction? I see no reason (it’s None), so how can I debug or get more insight into what causes the revert?
2.What are common pitfalls that might cause a revert in this context? For example, could it be due to an incorrect contract address, ABI issues, or is there something in my code logic that might lead to a failure?
Any insights or debugging strategies would be very helpful!