【发布时间】:2022-06-15 23:20:35
【问题描述】:
在一个未确定的、耗时的执行结果后如何写入区块链?
情况:
- Web 客户端 (NextJS):用户请求一个长时间运行的进程,最多可能需要 1 小时才能完成。作为这个交易的结果,一个新的合约实例应该被写入区块链。我们不能指望客户端坚持等待 => 使用 NodeJS 或云函数来委派长时间运行的任务。客户端上的 Metamask 将定义合约所有者。
- 计划使用 NextJS 路由到 Moralis Cloud Fx 以启动长期运行的任务。这项长期运行任务的结果将是存储到区块链的数字内容。
限制/问题:
- 不能让用户等待进程完成
- Metamask 在客户端运行
- 需要 Moralis Cloud Fx 或 NodeJS 才能访问所有者,以便在未确定的未来时间点实例化合约。
title Long Running Task to Blockchain
actor User
User->UI:User Request
UI->NodeServer: Delegate Slow Tasks
activate NodeServer
NodeServer->RestService: Start slow process
activate RestService
RestService-->NodeServer: Confirmation ID
NodeServer-->UI: Confirmation ID
UI->Blockchain: Create empty contract
RestService->NodeServer: Result = Data for blockchain
deactivate RestService
NodeServer->Blockchain: **Public?** Contract.setRestResult(Result)
deactivate NodeServer
这就是我计划如何设置跨越客户端和服务器的合同。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract EncryptionNFT is ERC721URIStorage {
using Counters for Counters.Counter;
Counters.Counter private tokenCounter;
uint256 public fee_wei;
bool public isLocked; // has a user paid to access the resource?
string private confirmationId; // long temporary password
string private sensitiveData; // data produced by long running process
address public encryptionService; // service responsible for producing the sensitive data
address public licensor; // licensed process
uint256 public tokenID; // distinct contract id
bool private isMutexed = false; // Reentrancy guard
bool private isSensitiveDataSet = false; // Asserts can only be set 1 time
event unlockPaid(address receiver, uint256 amount);
event withdrawlMade(address receiver, uint256 amount);
constructor(string memory name, uint256 _fee_gwei, string memory _confirmationId, address _encryption_service, address _licensor)
ERC721(name, "DataX")
{
fee_wei = _fee_gwei * 1000000000; // assert numbers are in WEI
isLocked = true; // lock contract by default since data not available => owner shouldn't have to pay unless successful
confirmationId = _confirmationId; // assign temporary password
encryptionService = _encryption_service; // assign addresses for payments
licensor = _licensor; // assign addresses for payments
tokenID = tokenCounter.current(); // Set/Mint with distinct token id
_safeMint(msg.sender, tokenID);
tokenCounter.increment();
}
function unlock() public payable {
require(isLocked == true, "Contract already unlocked.");
require(msg.value >= fee_wei, "Not enough WEI to unlock NFT.");
require(isMutexed == false, "Reentrant call detected!");
isMutexed = true;
// DO PAYMENTS
uint256 rcv_payment = msg.value;
uint256 licensor_cut = rcv_payment / 10;
uint256 encryptor_cut = rcv_payment - licensor_cut;
(bool is_encryptor_success, ) = payable(encryptionService).call{value: encryptor_cut}("");
require(is_encryptor_success, "Encryptor transfer failed.");
emit unlockPaid(encryptionService, encryptor_cut);
(bool is_licensor_success, ) = payable(licensor).call{value: licensor_cut}("");
require(is_licensor_success, "Licensor transfer failed.");
emit unlockPaid(licensor, licensor_cut);
isLocked = false;
isMutexed = false;
}
function withdraw() public payable {
require(msg.sender == ownerOf(tokenID), "Only owner may withdraw.");
require(isLocked == false, "Contract must be unlocked.");
require(isMutexed == false, "Reentrant call detected!");
isMutexed = true;
// DO PAYMENTS
uint256 rcv_payment = msg.value;
uint256 nft_service_cut = rcv_payment / 10;
uint256 licensor_cut = nft_service_cut / 10;
uint256 encryptor_cut = rcv_payment - licensor_cut;
uint256 nft_owner_cut = rcv_payment - nft_service_cut;
(bool is_encryptor_success, ) = payable(encryptionService).call{value: encryptor_cut}("");
require(is_encryptor_success, "Encryptor transfer failed.");
emit withdrawlMade(encryptionService, encryptor_cut);
(bool is_licensor_success, ) = payable(licensor).call{value: licensor_cut}("");
require(is_licensor_success, "Licensor transfer failed.");
emit withdrawlMade(licensor, licensor_cut);
(bool is_nft_owner_success, ) = payable(ownerOf(tokenID)).call{value: nft_owner_cut}("");
require(is_nft_owner_success, "NFT Owner transfer failed.");
emit withdrawlMade(ownerOf(tokenID), nft_owner_cut);
isMutexed = false;
}
function setTokenURI(string memory _tokenURI) public {
require(_isApprovedOrOwner(_msgSender(), tokenID), "ERC721: transfer caller is not owner nor approved.");
_setTokenURI(tokenID, _tokenURI);
}
// Here's how I was planning to allow a server-side process to set the data on REST service completion (slow task)
function setSensitiveData(string memory _confirmationId, string memory _sensitiveData) public {
require(keccak256(bytes(_confirmationId)) == keccak256(bytes(confirmationId)), "Confirmation ID does not match.");
require(isSensitiveDataSet == false, "Sensitive Data has already been set");
sensitiveData = _sensitiveData;
isSensitiveDataSet = true;
}
function getSensitiveData() public view returns (string memory){
require(isSensitiveDataSet == true, "Sensitive Data must be set first.");
require(msg.sender == encryptionService, "Only encryption service accessible.");
return sensitiveData;
}
}
编辑我刚刚发现 Moralis 有一个会话,似乎允许作为相应用户进行服务器端访问。以前有人玩过这个吗?它适用于这个用例吗?
【问题讨论】:
-
跟进:刚刚发现这篇优秀的文章:smashingmagazine.com/2021/01/nodejs-api-ethereum-blockchain 似乎这可能适用于我的用例。
标签: javascript blockchain solidity