After extensive debugging, here's what we found:
- Contract deployment successful (0.0.7305867)
- Whitelist removed - no access control issues
- Tasks created successfully (4 tasks with 3 chunks each available)
- Training completes successfully
- Weights upload to Akave successfully
- Manual contract call with short test strings SUCCEEDS
- Contract submission with actual encrypted weight URLs fails with
CONTRACT_REVERT_EXECUTED
The RSA-encrypted URLs are too large for the contract to handle:
- Each Akave presigned URL is ~500+ characters
- Split into 3 parts = ~170 chars each
- RSA-2048 encryption produces 256-byte ciphertexts
- Base64 encoding = ~344 characters per part
- Total: 1032+ characters being sent to contract
The contract might be hitting:
- Gas limits (even at 10M gas)
- String size limits in Solidity
- Transaction size limits on Hedera
Temporarily remove encryption to verify the rest of the flow works:
In p2p/coordinator.py around line 542:
# TEMPORARY: Skip encryption for testing
self.publish_on_chain(
self.current_task_id,
str(weights_url, 'utf-8'), # Send URL directly
"", # Empty string
"", # Empty string
)Update contract to accept single string:
function submitWeights(
uint256 taskId,
string calldata weight_url // Single URL instead of 3 parts
) external {
// ... rest of function
}Store only the hash of the weights URL on-chain:
In p2p/coordinator.py:
import hashlib
# Hash the weights URL
url_hash = hashlib.sha256(weights_url).hexdigest()
self.publish_on_chain(
self.current_task_id,
url_hash, # Just 64 characters
"",
""
)Store the actual encrypted URL in Akave or HCS separately.
Just submit the IPFS CID or Akave hash (not the full presigned URL):
# Extract just the hash from Akave URL
# From: https://o3-rc2.akave.xyz/akave-bucket/HASH?X-Amz-...
# To: HASH
akave_hash = weights_url.split('/')[-1].split('?')[0]
self.publish_on_chain(
self.current_task_id,
akave_hash, # Short hash only
"",
""
)Don't store URLs on-chain at all:
- Submit weights to contract with just a submission ID
- Store encrypted URLs in:
- Akave (decentralized storage)
- HCS topic (Hedera Consensus Service)
- IPFS
function submitWeights(uint256 taskId) external {
// Just mark that trainer submitted
// No weight data stored on-chain
t.remainingChunks -= 1;
// Emit event with submission ID
emit WeightsSubmitted(taskId, msg.sender);
}Client retrieves weights from HCS/Akave using the submission event.
- Modify contract to accept single string:
cd contracts/contractsEdit fed-learn.sol:
function submitWeights(
uint256 taskId,
string calldata weights_url
) external {
Task storage t = tasks[taskId];
require(t.exists, "task does not exist");
require(t.remainingChunks > 0, "no rewards remaining for this task");
t.remainingChunks -= 1;
uint256 reward = t.perChunkReward;
(bool sent, ) = payable(msg.sender).call{value: reward}("");
if (!sent) {
pendingWithdrawals[msg.sender] += reward;
}
emit WeightsSubmitted(taskId, msg.sender, weights_url, "", "", reward, t.remainingChunks);
if (t.remainingChunks == 0){
emit TaskCompleted(taskId);
tasks[taskId].exists=false;
}
}- Recompile and redeploy:
npx hardhat compile
npx hardhat run scripts/deploy.ts --network testnet- Update coordinator.py:
# Around line 542
self.publish_on_chain(
self.current_task_id,
weights_url.decode('utf-8') if isinstance(weights_url, bytes) else str(weights_url)
)- Update publish_on_chain function signature:
def publish_on_chain(self, task_id, weights_url):
try:
transaction = (
ContractExecuteTransaction()
.set_contract_id(self.contract_id)
.set_gas(5000000)
.set_function(
"submitWeights",
ContractFunctionParameters()
.add_uint256(task_id)
.add_string(weights_url)
)
.freeze_with(self.client)
.sign(self.operator_key)
)
receipt = transaction.execute(self.client)
# ... rest of function- Choose one of the options above
- Implement the changes
- Test with a new task
- If successful, consider moving to Option 4 for production
Solidity strings can theoretically hold large data, but:
- Gas costs increase with string size
- Transaction limits on Hedera
- ABI encoding overhead for large strings
The 1000+ character encrypted strings are just too big for efficient blockchain storage.
Best Practice: Store minimal data on-chain, keep large payloads off-chain.
Recommendation: Use Option 3 (IPFS CID only) for immediate fix, then move to Option 4 (off-chain storage) for production.