오프체인 데이터 온체인 수집을 위한 Trustus EIP712 기반 솔루션
Trustus는 "오프체인 데이터 온체인에 액세스하기 위한 신뢰를 최소화한 방법"입니다. 프로젝트에 대한 자세한 내용은 https://github.com/ZeframLou/trustus에서 확인할 수 있습니다.
Trustus는 기본적으로 Solidity로 작성된 하나의 스마트 계약입니다. 계약의 주요 부분은 입력된 구조화된 날짜의 해싱 및 서명을 위한 EIP712 표준의 구현입니다. Trustus는 1) 계약에 데이터를 보낼 수 있는 화이트리스트 주소와 2) EIP712 부분을 허용합니다. 즉, 구체적이고 미리 정의된 데이터 구조 및 서명에 오프체인 데이터(예: NFT 가격 피드)를 가져올 수 있습니다. 그런 다음 V, R, S 출력 값 및 추가 원시 데이터를 사용하여 기본 계약에서 스마트 계약 기능을 호출할 수 있습니다(이전에 가져온 Trustus 추상 계약에서 verifyPacket 수정자를 적용했습니다).
트러스트 계약의 견고성 코드(_verifyPacket 함수)는 ecrecover로 이더리움 주소를 전달하고 복구하는 원시 데이터와 V,R,S 값을 기반으로 해싱 작업을 재생합니다. 그런 다음 화이트리스트 주소(이전에 _setIsTrusted 함수로 설정됨)와 비교하고 복구된 주소가 화이트리스트에 없는 경우 추가 데이터 수집을 허용하거나 되돌릴 수 있습니다.
"Caviat"는 우리가 필요한 JavaScript 부분(적절한 데이터 구조를 공식화하려는 경우)이 설명서나 일부 예제에 제공되지 않는다는 것입니다. 단 한 문장: "서버는 데이터 패킷을 형식화하기 위해 Trustus에서 사용하는 특정 표준을 구현하고 신뢰할 수 있는 서버에서 시작된 데이터 패킷을 확인하는 ECDSA 서명을 제공해야 합니다."
그렇기 때문에 이번 포스트에서는 Trustus 계약을 보완하기 위해 JS 부분을 제공할 것입니다. 원래 Trustus 계약과 여기서 사용하는 계약의 유일한 차이점은 내 버전에서 struct에 정의된 페이로드가 바이트가 아니라 단위라는 것입니다.
이 모든 코드는 ConsenSys Mesh & Protocol Labs에서 지원하는 커뮤니티 뱅킹 및 자산 관리 fluidNFT 프로젝트를 위해 개발되었습니다. 당신이 intrested 경우에 당신은 우리의 프로젝트를 방문할 수 있습니다
이 프로세스의 모든 힌트에 대해 Medium의 Tnx @apurbapokharel도 제공합니다!
import React, { useState, useEffect } from "react";
import Web3 from "web3";
import Main from "./contracts/Main.json";
var ethUtil = require('ethereumjs-util');
var sigUtil = require('eth-sig-util');
const App = () => {
const [storageValue, setStorageValue] = useState("");
const [myWeb3, setMyWeb3] = useState(null);
const [accounts, setAccounts] = useState(null);
const [contract, setContract] = useState(null);
const init= async () => {
const web3 = new Web3(window.ethereum);
setMyWeb3(web3);
const _accounts = await web3.eth.getAccounts();
setAccounts(_accounts[0]);
const instance = new web3.eth.Contract(
Main.abi,
"0x3779277C9EE5f957fE90027E37bf60828c028ecF"
);
setContract(instance);
const response = await instance.methods.get_recovered().call();
setStorageValue(response);
};
const signData = async () => {
var milsec_deadline = Date.now() / 1000 + 100;
var deadline = parseInt(String(milsec_deadline).slice(0, 10));
console.log(deadline);
var request =
"0x0000000000000000000000000000000000000000000000000000000000000001";
var payload = 122;
myWeb3.currentProvider.sendAsync(
{
method: "net_version",
params: [],
jsonrpc: "2.0",
},
function (err, result) {
const netId = result.result;
const msgParams = JSON.stringify({
types: {
EIP712Domain: [
{ name: "name", type: "string" },
{ name: "version", type: "string" },
{ name: "chainId", type: "uint256" },
{ name: "verifyingContract", type: "address" },
],
VerifyPacket: [
{ name: "request", type: "bytes32" },
{ name: "deadline", type: "uint256" },
{ name: "payload", type: "uint256" },
],
},
primaryType: "VerifyPacket",
domain: {
name: "Trustus",
version: "1",
chainId: netId,
verifyingContract: "0x3779277C9EE5f957fE90027E37bf60828c028ecF",
},
message: {
request: request,
deadline: deadline,
payload: payload,
},
});
var params = [accounts, msgParams];
console.dir(params);
var method = "eth_signTypedData_v3";
myWeb3.currentProvider.sendAsync(
{
method,
params,
accounts,
},
async function (err, result) {
if (err) return console.dir(err);
if (result.error) {
alert(result.error.message);
}
if (result.error) return console.error("ERROR", result);
const recovered = sigUtil.recoverTypedSignature({
data: JSON.parse(msgParams),
sig: result.result,
});
if (
ethUtil.toChecksumAddress(recovered) ===
ethUtil.toChecksumAddress(accounts)
) {
alert("Successfully ecRecovered signer as " + accounts);
} else {
alert(
"Failed to verify signer when comparing " +
result +
" to " +
accounts
);
}
//getting r s v from a signature
const signature = result.result.substring(2);
const r = "0x" + signature.substring(0, 64);
const s = "0x" + signature.substring(64, 128);
const v = parseInt(signature.substring(128, 130), 16);
console.log("r:", r);
console.log("s:", s);
console.log("v:", v);
console.log("signer:", accounts);
await contract.methods
.proba(request, [v, r, s, request, deadline, payload])
.send({ from: accounts });
}
);
}
);
};
const setRendered = async () => {
// Get the value from the contract to prove it worked.
const response = await contract.methods.get_recovered().call();
console.log(response);
setStorageValue(response);
};
const setUser = async () => {
await contract.methods
.setTrusted(accounts, true)
.send({ from: accounts });
};
useEffect(() => {
init();
}, []);
return (
<div className="App">
<h1>Implementation of EIP 712 standard</h1>
<h2>The recovred address is: {storageValue}</h2>
<button className={style.universalBtn} onClick={() => signData()}>
Press to sign
</button>
<button className={style.universalBtn} onClick={() => setRendered()}>
Set rendered address
</button>
<button className={style.universalBtn} onClick={() => setUser()}>
Add trusted address
</button>
</div>
);
}
export default App;
그리고 여기에 우리가 Trustus 추상 계약을 가져오는 주요 스마트 계약이 있습니다.
// SPDX-License-Identifier: MIT
import "./Trustus.sol";
pragma solidity ^0.8.4;
contract Main is Trustus {
function proba(bytes32 _request, TrustusPacket calldata _packet) public verifyPacket(_request, _packet) returns (bool) {
return true;
}
function setTrusted(address _signer, bool _isTrusted) public {
_setIsTrusted (_signer, _isTrusted);
}
}
Trustus 계약의 약간 수정된 버전입니다.
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.4;
/// @title Trustus
/// @author zefram.eth
/// @notice Trust-minimized method for accessing offchain data onchain
abstract contract Trustus {
/// -----------------------------------------------------------------------
/// Structs
/// -----------------------------------------------------------------------
/// @param v Part of the ECDSA signature
/// @param r Part of the ECDSA signature
/// @param s Part of the ECDSA signature
/// @param request Identifier for verifying the packet is what is desired
/// , rather than a packet for some other function/contract
/// @param deadline The Unix timestamp (in seconds) after which the packet
/// should be rejected by the contract
/// @param payload The payload of the packet
struct TrustusPacket {
uint8 v;
bytes32 r;
bytes32 s;
bytes32 request;
uint256 deadline;
uint256 payload;
}
// ADDED (erase on the end of testing)
address recovered;
/// -----------------------------------------------------------------------
/// Errors
/// -----------------------------------------------------------------------
error Trustus__InvalidPacket();
/// -----------------------------------------------------------------------
/// Immutable parameters
/// -----------------------------------------------------------------------
/// @notice The chain ID used by EIP-712
uint256 internal immutable INITIAL_CHAIN_ID;
/// @notice The domain separator used by EIP-712
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
/// -----------------------------------------------------------------------
/// Storage variables
/// -----------------------------------------------------------------------
/// @notice Records whether an address is trusted as a packet provider
/// @dev provider => value
mapping(address => bool) internal isTrusted;
/// -----------------------------------------------------------------------
/// Modifiers
/// -----------------------------------------------------------------------
/// @notice Verifies whether a packet is valid and returns the result.
/// Will revert if the packet is invalid.
/// @dev The deadline, request, and signature are verified.
/// @param request The identifier for the requested payload
/// @param packet The packet provided by the offchain data provider
modifier verifyPacket(bytes32 request, TrustusPacket calldata packet) {
if (!_verifyPacket(request, packet)) revert Trustus__InvalidPacket();
_;
}
/// -----------------------------------------------------------------------
/// Constructor
/// -----------------------------------------------------------------------
constructor() {
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = _computeDomainSeparator();
}
// ADDED (erase on the end of testing)
function get_recovered() public view returns (address) {
return recovered;
}
/// -----------------------------------------------------------------------
/// Packet verification
/// -----------------------------------------------------------------------
/// @notice Verifies whether a packet is valid and returns the result.
/// @dev The deadline, request, and signature are verified.
/// @param request The identifier for the requested payload
/// @param packet The packet provided by the offchain data provider
/// @return success True if the packet is valid, false otherwise
function _verifyPacket(bytes32 request, TrustusPacket calldata packet)
internal
virtual
returns (bool success)
{
// verify deadline
if (block.timestamp > packet.deadline) return false;
// verify request
if (request != packet.request) return false;
// verify signature
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"VerifyPacket(bytes32 request,uint256 deadline,uint256 payload)"
),
packet.request,
packet.deadline,
packet.payload
)
)
)
),
packet.v,
packet.r,
packet.s
);
/// Added to original Trustus for test
recovered = recoveredAddress;
return (recoveredAddress != address(0)) && isTrusted[recoveredAddress];
}
/// @notice Sets the trusted status of an offchain data provider.
/// @param signer The data provider's ECDSA public key as an Ethereum address
/// @param isTrusted_ The desired trusted status to set
function _setIsTrusted(address signer, bool isTrusted_) internal virtual {
isTrusted[signer] = isTrusted_;
}
/// -----------------------------------------------------------------------
/// EIP-712 compliance
/// -----------------------------------------------------------------------
/// @notice The domain separator used by EIP-712
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return
block.chainid == INITIAL_CHAIN_ID
? INITIAL_DOMAIN_SEPARATOR
: _computeDomainSeparator();
}
/// @notice Computes the domain separator used by EIP-712
function _computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
),
keccak256("Trustus"),
keccak256("1"),
block.chainid,
address(this)
)
);
}
}
Reference
이 문제에 관하여(오프체인 데이터 온체인 수집을 위한 Trustus EIP712 기반 솔루션), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/ilija/trustus-eip712-based-solution-for-ingesting-off-chain-data-on-chain-i73텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)