all core functions are working

This commit is contained in:
Jincheng Lu 2025-12-06 05:44:20 +08:00
parent 9e3e303dc0
commit f410f09d67
4 changed files with 283 additions and 151 deletions

View File

@ -48,6 +48,31 @@
"name": "SafeERC20FailedOperation", "name": "SafeERC20FailedOperation",
"type": "error" "type": "error"
}, },
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "client",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "connectionStartTime",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "abortTime",
"type": "uint256"
}
],
"name": "ConnectionAborted",
"type": "event"
},
{ {
"anonymous": false, "anonymous": false,
"inputs": [ "inputs": [
@ -211,6 +236,25 @@
"name": "NodeUpdated", "name": "NodeUpdated",
"type": "event" "type": "event"
}, },
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "ownerAccount",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "OwnerShareAccrued",
"type": "event"
},
{ {
"anonymous": false, "anonymous": false,
"inputs": [ "inputs": [
@ -348,39 +392,25 @@
{ {
"indexed": false, "indexed": false,
"internalType": "uint256", "internalType": "uint256",
"name": "relayShare", "name": "ownerShare",
"type": "uint256"
},
{
"indexed": false,
"internalType": "bool",
"name": "ratingProvided",
"type": "bool"
},
{
"indexed": false,
"internalType": "uint256",
"name": "rating",
"type": "uint256" "type": "uint256"
} }
], ],
"name": "PaymentProcessed", "name": "PaymentProcessed",
"type": "event" "type": "event"
}, },
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "relay",
"type": "address"
}
],
"name": "RelayOperatorAdded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "relay",
"type": "address"
}
],
"name": "RelayOperatorRemoved",
"type": "event"
},
{ {
"anonymous": false, "anonymous": false,
"inputs": [ "inputs": [
@ -413,6 +443,19 @@
"name": "Unpaused", "name": "Unpaused",
"type": "event" "type": "event"
}, },
{
"inputs": [],
"name": "ABORT_WINDOW",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "INITIAL_REPUTATION", "name": "INITIAL_REPUTATION",
@ -532,46 +575,7 @@
}, },
{ {
"inputs": [], "inputs": [],
"name": "RELAY_SHARE", "name": "OWNER_SHARE",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "REPUTATION_DECREMENT",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "REPUTATION_INCREMENT",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "REPUTATION_PRECISION",
"outputs": [ "outputs": [
{ {
"internalType": "uint256", "internalType": "uint256",
@ -608,6 +612,34 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "string",
"name": "vpnClientPublicKey",
"type": "string"
},
{
"internalType": "uint256",
"name": "_connectionStartTime",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_agreedPricePerMinute",
"type": "uint256"
},
{
"internalType": "bytes",
"name": "_clientSignature",
"type": "bytes"
}
],
"name": "abortConnection",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "acceptGovernanceTransfer", "name": "acceptGovernanceTransfer",
@ -653,19 +685,6 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "address",
"name": "_relay",
"type": "address"
}
],
"name": "addRelayOperator",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
@ -801,6 +820,34 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "getContractStats",
"outputs": [
{
"internalType": "uint256",
"name": "totalNodes",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "totalChannels",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "totalMinutes",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "treasuryBalance",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
@ -903,12 +950,12 @@
{ {
"inputs": [ "inputs": [
{ {
"internalType": "address", "internalType": "bytes32",
"name": "_address", "name": "_messageHash",
"type": "address" "type": "bytes32"
} }
], ],
"name": "isRelayOperator", "name": "isSignatureUsed",
"outputs": [ "outputs": [
{ {
"internalType": "bool", "internalType": "bool",
@ -973,6 +1020,16 @@
"internalType": "uint256", "internalType": "uint256",
"name": "lastActivity", "name": "lastActivity",
"type": "uint256" "type": "uint256"
},
{
"internalType": "uint256",
"name": "totalRatingValue",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "totalRatingCount",
"type": "uint256"
} }
], ],
"stateMutability": "view", "stateMutability": "view",
@ -1078,14 +1135,29 @@
"name": "_node", "name": "_node",
"type": "address" "type": "address"
}, },
{
"internalType": "string",
"name": "vpnClientPublicKey",
"type": "string"
},
{ {
"internalType": "uint256", "internalType": "uint256",
"name": "_minutesUsed", "name": "_connectionStartTime",
"type": "uint256" "type": "uint256"
}, },
{ {
"internalType": "uint256", "internalType": "uint256",
"name": "_nonce", "name": "_agreedPricePerMinute",
"type": "uint256"
},
{
"internalType": "bool",
"name": "_ratingProvided",
"type": "bool"
},
{
"internalType": "uint256",
"name": "_rating",
"type": "uint256" "type": "uint256"
}, },
{ {
@ -1097,11 +1169,6 @@
"internalType": "bytes", "internalType": "bytes",
"name": "_nodeSignature", "name": "_nodeSignature",
"type": "bytes" "type": "bytes"
},
{
"internalType": "bytes",
"name": "_relaySignature",
"type": "bytes"
} }
], ],
"name": "processPayment", "name": "processPayment",
@ -1132,38 +1199,6 @@
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "relayOperators",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_relay",
"type": "address"
}
],
"name": "removeRelayOperator",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "renounceOwnership", "name": "renounceOwnership",
@ -1241,6 +1276,25 @@
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "uint256",
"name": "_value",
"type": "uint256"
}
],
"name": "uintToString",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "pure",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "unpause", "name": "unpause",
@ -1279,24 +1333,6 @@
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "address",
"name": "_nodeID",
"type": "address"
},
{
"internalType": "bool",
"name": "_successfulSession",
"type": "bool"
}
],
"name": "updateReputation",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {

View File

@ -1,12 +1,15 @@
import { Web3 } from 'web3'; import { Web3, type Bytes } from 'web3';
import type { Node, NodeInfo, PaymentChannelInfo } from '../Util'; import type { Node, NodeInfo, PaymentChannelInfo } from '../Util';
import clearNetJson from './ClearNet/ClearNet.json'; import clearNetJson from './ClearNet/ClearNet.json';
import clrTokenJson from './CLRToken/CLRToken.json'; import clrTokenJson from './CLRToken/CLRToken.json';
const clearNetABI = (clearNetJson as any).abi ?? clearNetJson; const clearNetABI = (clearNetJson as any).abi ?? clearNetJson;
const clrTokenABI = (clrTokenJson as any).abi ?? clrTokenJson; const clrTokenABI = (clrTokenJson as any).abi ?? clrTokenJson;
const clearNetAddress = "0xb6f537b38b82d08ff3ed796754d9d85b5cfe9cb5"; const clearNetAddress = "0x265da498da1de3f22bb57c717d14806e4884cdda";
const clrTokenAddress = "0xf1664c17887767c8f58695846babb349ca61d2e9"; const clrTokenAddress = "0xf1664c17887767c8f58695846babb349ca61d2e9";
// const clearNetAddress = "0x9a9f2ccfde556a7e9ff0848998aa4a0cfd8863ae";
// const clrTokenAddress = "0x5fc8d32690cc91d4c39d9d3abcbd16989f875707";
const DEFAULT_MIN_STAKE = BigInt(10000) * BigInt(1e18); // 10000 CLR const DEFAULT_MIN_STAKE = BigInt(10000) * BigInt(1e18); // 10000 CLR
@ -20,10 +23,12 @@ export const getActiveNodes = async (provider: any): Promise<Node[]> => {
const nodes: Node[] = await Promise.all(activeNodes.map(async (node: string) => { const nodes: Node[] = await Promise.all(activeNodes.map(async (node: string) => {
const nodeInfo: NodeInfo = await clearnet_contract.methods.getNodeInfo(node).call(); const nodeInfo: NodeInfo = await clearnet_contract.methods.getNodeInfo(node).call();
return { return {
address: node,
ip: nodeInfo.ipAddress, ip: nodeInfo.ipAddress,
port: Number(nodeInfo.port), port: Number(nodeInfo.port),
traffic: 0, traffic: 0,
price: Number(nodeInfo.pricePerMinute) / 1e18, price: Number(nodeInfo.pricePerMinute) / 1e18,
pricePerMinute: nodeInfo.pricePerMinute,
rating: Number(nodeInfo.reputationScore) / 1000, rating: Number(nodeInfo.reputationScore) / 1000,
}; };
})); }));
@ -72,6 +77,50 @@ export async function closePaymentChannel(provider: any,account: string) {
} }
export async function processPayment(provider: any,account: string,nodeAddress: string,
vpnClientPublicKey: string, connectionStartTime: BigInt, agreedPricePerMinute: BigInt,
isRatingProvided: boolean, rating: BigInt, clientSignature: string, nodeSignature: string) {
const web3 = new Web3(provider);
const clearnet_contract = new web3.eth.Contract(clearNetABI, clearNetAddress);
const gasPrice = await web3.eth.getGasPrice();
const gasLimit = 300000;
const tx = await clearnet_contract.methods.processPayment(
account,
nodeAddress,
vpnClientPublicKey,
connectionStartTime,
agreedPricePerMinute,
isRatingProvided,
rating,
clientSignature,
nodeSignature
).send({
from: account,
gas: String(gasLimit),
gasPrice: String(gasPrice),
});
console.log(tx);
}
export async function abortConnection(provider: any,account: string,vpnClientPublicKey: string,
connectionStartTime: BigInt,agreedPricePerMinute: BigInt,clientSignature: Bytes) {
const web3 = new Web3(provider);
const clearnet_contract = new web3.eth.Contract(clearNetABI, clearNetAddress);
const gasPrice = await web3.eth.getGasPrice();
const gasLimit = 300000;
const tx = await clearnet_contract.methods.abortConnection(
vpnClientPublicKey,
connectionStartTime,
agreedPricePerMinute,
clientSignature
).send({
from: account,
gas: String(gasLimit),
gasPrice: String(gasPrice),
});
console.log(tx);
}
export async function getPaymentChannelInfo(provider: any,account: string) : Promise<PaymentChannelInfo> { export async function getPaymentChannelInfo(provider: any,account: string) : Promise<PaymentChannelInfo> {
if (!provider) { if (!provider) {
return { balance: BigInt(0), nonce: BigInt(0), isActive: false}; return { balance: BigInt(0), nonce: BigInt(0), isActive: false};

View File

@ -29,9 +29,10 @@ import type { AccountInfo } from '~/context/AuthProvider';
import { signMessage } from './Metamask/Connections'; import { signMessage } from './Metamask/Connections';
import { generateWireguardKeyPair, downloadWireguardConfig } from './WireguardConfig'; import { generateWireguardKeyPair, downloadWireguardConfig } from './WireguardConfig';
import type { Node } from './Util'; import type { Node } from './Util';
import { getActiveNodes, getNextNonce } from './Contracts/Connections'; import { getActiveNodes, getNextNonce, processPayment, abortConnection } from './Contracts/Connections';
import axios from 'axios'; import axios from 'axios';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { Web3 } from 'web3';
@ -39,19 +40,46 @@ function NodeItem({node, auth}: {node: Node, auth: AccountInfo}) {
const [ratingOpen, setRatingOpen] = React.useState(false); const [ratingOpen, setRatingOpen] = React.useState(false);
const [ratingValue, setRatingValue] = React.useState<number | null>(null); const [ratingValue, setRatingValue] = React.useState<number | null>(null);
const [submittingRating, setSubmittingRating] = React.useState(false); const [submittingRating, setSubmittingRating] = React.useState(false);
let nodeSignature = "";
const getUrl = () => `http://${node.ip}:${node.port}`; const getUrl = () => `http://${node.ip}:${node.port}`;
const connect = async () => { const connect = async () => {
try { try {
const { privatekey: clientPrivateKey, publicKey: clientPublicKey } = generateWireguardKeyPair(); const { privatekey: clientPrivateKey, publicKey: clientPublicKey } = generateWireguardKeyPair();
const nonce: BigInt = await getNextNonce(auth.providerWithInfo.provider, auth.accounts[0]); const nonce: BigInt = await getNextNonce(auth.providerWithInfo.provider, auth.accounts[0]);
const sig = await signMessage(nonce + clientPublicKey,auth.providerWithInfo.provider, auth.accounts[0]); const connectionStartTime = Math.floor(Date.now() / 1000);
const res_string = nonce + '\n' + clientPublicKey + '\n' + sig; console.log("Nonce:", nonce.toString());
const sig = await signMessage(nonce + clientPublicKey + connectionStartTime + node.pricePerMinute,auth.providerWithInfo.provider, auth.accounts[0]);
const res_string = nonce + '\n' + clientPublicKey + '\n' + connectionStartTime + '\n' + node.pricePerMinute + '\n' + sig;
let response = await axios.post(getUrl() + "/connect", res_string); let response = await axios.post(getUrl() + "/connect", res_string);
const clientCIDR = response.data.WireguardClientCIDR; const clientCIDR = response.data.WireguardClientCIDR;
const serverPublicKey = response.data.WireguardServerPublicKey; const serverPublicKey = response.data.WireguardServerPublicKey;
const peerPort = response.data.WireguardPort; const peerPort = response.data.WireguardPort;
const dns = response.data.WireguardDNS; const dns = response.data.WireguardDNS;
console.log(response.data); console.log(response.data);
if(response.data.NodeSignature){
const provider = auth.providerWithInfo.provider;
const web3 = new Web3(provider);
const recoveredAddress = web3.eth.accounts.recover(nonce.toString() + connectionStartTime + node.pricePerMinute, response.data.NodeSignature);
if(recoveredAddress === node.address){
nodeSignature = response.data.NodeSignature;
}
}
if(nodeSignature === ""){
await abortConnection(auth.providerWithInfo.provider, auth.accounts[0],
clientPublicKey,
BigInt(connectionStartTime),
node.pricePerMinute,
String(sig)
);
return;
}
localStorage.setItem(node.address, JSON.stringify({
vpnClientPublicKey: clientPublicKey,
connectionStartTime: connectionStartTime,
agreedPricePerMinute: node.pricePerMinute.toString(),
clientSignature: String(sig),
nodeSignature: nodeSignature
}));
downloadWireguardConfig(clientPrivateKey, serverPublicKey, clientCIDR, dns, node.ip, String(peerPort), "0.0.0.0/0"); downloadWireguardConfig(clientPrivateKey, serverPublicKey, clientCIDR, dns, node.ip, String(peerPort), "0.0.0.0/0");
} catch (error) { } catch (error) {
console.error('Error:', error); console.error('Error:', error);
@ -60,11 +88,28 @@ function NodeItem({node, auth}: {node: Node, auth: AccountInfo}) {
const disconnect = async () => { const disconnect = async () => {
try{ try{
const nonce: BigInt = await getNextNonce(auth.providerWithInfo.provider, auth.accounts[0]); // const nonce: BigInt = await getNextNonce(auth.providerWithInfo.provider, auth.accounts[0]);
const sig = await signMessage(String(nonce),auth.providerWithInfo.provider, auth.accounts[0]); // const sig = await signMessage(String(nonce),auth.providerWithInfo.provider, auth.accounts[0]);
const res_string = String(nonce) + '\n' + sig; // const res_string = String(nonce) + '\n' + sig;
let response = await axios.post(getUrl() + "/disconnect", res_string); // let response = await axios.post(getUrl() + "/disconnect", res_string);
console.log(response.data); // console.log(response.data);
const storedInfo = localStorage.getItem(node.address);
if(!storedInfo){
console.error("No active connection info");
return;
}
const connectionInfo = JSON.parse(storedInfo);
await processPayment(auth.providerWithInfo.provider, auth.accounts[0],
node.address,
connectionInfo.vpnClientPublicKey,
BigInt(connectionInfo.connectionStartTime),
BigInt(connectionInfo.agreedPricePerMinute),
false,
BigInt(0),
connectionInfo.clientSignature,
connectionInfo.nodeSignature
);
localStorage.removeItem(node.address);
} catch (error) { } catch (error) {
console.error('Error:', error); console.error('Error:', error);
} }

View File

@ -1,9 +1,11 @@
export interface Node { export interface Node {
address: string;
ip: string; ip: string;
port: number; port: number;
traffic: number; traffic: number;
price : number; price : number;
pricePerMinute : BigInt;
rating : number; rating : number;
} }