feat: blockchain (#1408)
Co-authored-by: root <root@huahua.daoyoucloud.com> Co-authored-by: bai.zixv <bai.zixv@foxmail.com> Co-authored-by: sealday <zhanglin@daoyoucloud.com> Co-authored-by: TomyJan <TomyJan6@gmail.com> Co-authored-by: luliangqiang <2650321653@qq.com> Co-authored-by: yoona <1486343814@qq.com> Co-authored-by: wjh <wwwjh0710@163.com> Reviewed-on: daoyoucloud/tachybase#1408 Reviewed-by: sealday <zhanglin@daoyoucloud.com> Co-authored-by: hua <1494133104@qq.com> Co-committed-by: hua <1494133104@qq.com>
This commit is contained in:
parent
bcdf1e7972
commit
54a7f5dc40
2
packages/plugins/@tachybase/plugin-blockchain/.gitignore
vendored
Normal file
2
packages/plugins/@tachybase/plugin-blockchain/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
cache
|
||||||
|
artifacts
|
2
packages/plugins/@tachybase/plugin-blockchain/.npmignore
Normal file
2
packages/plugins/@tachybase/plugin-blockchain/.npmignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/node_modules
|
||||||
|
/src
|
13
packages/plugins/@tachybase/plugin-blockchain/README.md
Normal file
13
packages/plugins/@tachybase/plugin-blockchain/README.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# @tachybase/plugin-blockchain
|
||||||
|
|
||||||
|
# 给文件添加执行权限(第一次要输入这些)
|
||||||
|
chmod +x start_geth.sh
|
||||||
|
chmod +x compile_contract.sh
|
||||||
|
chmod +x deploy_contract.sh
|
||||||
|
|
||||||
|
# 启动私链
|
||||||
|
pnpm run geth
|
||||||
|
# 编译
|
||||||
|
pnpm run compile
|
||||||
|
# 部署
|
||||||
|
pnpm run deploy
|
2
packages/plugins/@tachybase/plugin-blockchain/client.d.ts
vendored
Normal file
2
packages/plugins/@tachybase/plugin-blockchain/client.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './dist/client';
|
||||||
|
export { default } from './dist/client';
|
1
packages/plugins/@tachybase/plugin-blockchain/client.js
Normal file
1
packages/plugins/@tachybase/plugin-blockchain/client.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require('./dist/client/index.js');
|
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"DataStorage": "0x6176309fF5993b4f93a4De19d2E30bDE5cF0472c"
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.20;
|
||||||
|
|
||||||
|
contract DataStorage {
|
||||||
|
struct Data {
|
||||||
|
string hashedData;
|
||||||
|
string salt;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapping(uint256 => Data) private data;
|
||||||
|
|
||||||
|
event DataStored(uint256 indexed id, string hashedData, string salt);
|
||||||
|
event DataDeleted(uint256 indexed id);
|
||||||
|
|
||||||
|
function storeData(uint256 id, string memory hashedData, string memory salt) public {
|
||||||
|
require(bytes(data[id].hashedData).length == 0, "Data already exists for this ID");
|
||||||
|
data[id] = Data(hashedData, salt);
|
||||||
|
emit DataStored(id, hashedData, salt);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getData(uint256 id) public view returns (string memory hashedData, string memory salt) {
|
||||||
|
Data storage item = data[id];
|
||||||
|
require(bytes(item.hashedData).length != 0, "Data not found");
|
||||||
|
return (item.hashedData, item.salt);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteData(uint256 id) public {
|
||||||
|
require(bytes(data[id].hashedData).length != 0, "Data not found");
|
||||||
|
delete data[id];
|
||||||
|
emit DataDeleted(id);
|
||||||
|
}
|
||||||
|
}
|
29
packages/plugins/@tachybase/plugin-blockchain/deploy.js
Normal file
29
packages/plugins/@tachybase/plugin-blockchain/deploy.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// scripts/deploy.js
|
||||||
|
const hre = require('hardhat');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
// npx hardhat compile
|
||||||
|
// npx hardhat run scripts/deploy.js
|
||||||
|
async function main() {
|
||||||
|
const [deployer] = await hre.ethers.getSigners();
|
||||||
|
console.log('Deploying contracts with the account:', deployer.address);
|
||||||
|
|
||||||
|
//部署DataStorage合约
|
||||||
|
const DataStorage = await hre.ethers.getContractFactory('DataStorage');
|
||||||
|
const dataStorage = await DataStorage.deploy();
|
||||||
|
console.log('DataStorage deployed to:', dataStorage.target);
|
||||||
|
|
||||||
|
// 将DataStorage合约地址保存到一个JSON文件中
|
||||||
|
const contractAddresses = {
|
||||||
|
DataStorage: dataStorage.target,
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.writeFileSync('contract-addresses.json', JSON.stringify(contractAddresses, null, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.then(() => process.exit(0))
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
@ -0,0 +1,22 @@
|
|||||||
|
require('@nomicfoundation/hardhat-toolbox');
|
||||||
|
/** @type import('hardhat/config').HardhatUserConfig */
|
||||||
|
module.exports = {
|
||||||
|
solidity: {
|
||||||
|
version: '0.8.20',
|
||||||
|
settings: {
|
||||||
|
optimizer: {
|
||||||
|
enabled: true,
|
||||||
|
runs: 200,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultNetwork: 'gethlocal',
|
||||||
|
networks: {
|
||||||
|
gethlocal: {
|
||||||
|
url: 'http://127.0.0.1:8888/',
|
||||||
|
chainId: 1337,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
//控制台运行 pnpm i @nomicfoundation/hardhat-toolbox
|
19
packages/plugins/@tachybase/plugin-blockchain/package.json
Normal file
19
packages/plugins/@tachybase/plugin-blockchain/package.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "@tachybase/plugin-blockchain",
|
||||||
|
"version": "0.21.80",
|
||||||
|
"main": "dist/server/index.js",
|
||||||
|
"dependencies": {
|
||||||
|
"@nomicfoundation/hardhat-toolbox": "^5.0.0",
|
||||||
|
"child_process": "^1.0.2",
|
||||||
|
"crypto": "^1.0.1",
|
||||||
|
"fs": "0.0.1-security",
|
||||||
|
"hardhat": "^2.22.6",
|
||||||
|
"path": "^0.12.7",
|
||||||
|
"web3": "^4.11.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@tachybase/client": "workspace:*",
|
||||||
|
"@tachybase/server": "workspace:*",
|
||||||
|
"@tachybase/test": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
2
packages/plugins/@tachybase/plugin-blockchain/server.d.ts
vendored
Normal file
2
packages/plugins/@tachybase/plugin-blockchain/server.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './dist/server';
|
||||||
|
export { default } from './dist/server';
|
1
packages/plugins/@tachybase/plugin-blockchain/server.js
Normal file
1
packages/plugins/@tachybase/plugin-blockchain/server.js
Normal file
@ -0,0 +1 @@
|
|||||||
|
module.exports = require('./dist/server/index.js');
|
@ -0,0 +1,21 @@
|
|||||||
|
import { Plugin } from '@tachybase/client';
|
||||||
|
|
||||||
|
export class PluginBlockchainClient extends Plugin {
|
||||||
|
async afterAdd() {
|
||||||
|
// await this.app.pm.add()
|
||||||
|
}
|
||||||
|
|
||||||
|
async beforeLoad() {}
|
||||||
|
|
||||||
|
// You can get and modify the app instance here
|
||||||
|
async load() {
|
||||||
|
console.log(this.app);
|
||||||
|
// this.app.addComponents({})
|
||||||
|
// this.app.addScopes({})
|
||||||
|
// this.app.addProvider()
|
||||||
|
// this.app.addProviders()
|
||||||
|
// this.app.router.add()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PluginBlockchainClient;
|
@ -0,0 +1,2 @@
|
|||||||
|
export * from './server';
|
||||||
|
export { default } from './server';
|
@ -0,0 +1,76 @@
|
|||||||
|
import { Context } from '@tachybase/actions';
|
||||||
|
|
||||||
|
import { generateSalt, hashWithSalt } from '../cryptoUtils';
|
||||||
|
import { contractService } from '../web3/contractService';
|
||||||
|
|
||||||
|
// /api/blockchain:store
|
||||||
|
/**
|
||||||
|
* 存储数据的处理函数
|
||||||
|
* @param {Context} ctx 请求上下文
|
||||||
|
*/
|
||||||
|
export const store = async (ctx: Context) => {
|
||||||
|
const { data } = ctx.action.params.values;
|
||||||
|
try {
|
||||||
|
for (const item of data) {
|
||||||
|
const salt = generateSalt();
|
||||||
|
const jsonData = JSON.stringify(item);
|
||||||
|
const hashedData = hashWithSalt(jsonData, salt);
|
||||||
|
await contractService.storeData(item.id, hashedData, salt);
|
||||||
|
}
|
||||||
|
ctx.body = '数据存储成功';
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
ctx.status = 500;
|
||||||
|
ctx.body = '数据存储出错';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// /api/blockchain:getData?id=xxxx
|
||||||
|
/**
|
||||||
|
* 获取数据的处理函数
|
||||||
|
* @param {Context} ctx 请求上下文
|
||||||
|
*/
|
||||||
|
export const getDataHandler = async (ctx: Context) => {
|
||||||
|
const id = ctx.action.params.id;
|
||||||
|
try {
|
||||||
|
const { hashedData, salt } = await contractService.getData(id);
|
||||||
|
ctx.body = { hashedData, salt };
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
ctx.status = 500;
|
||||||
|
ctx.body = '检索数据时出错';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// /api/blockchain:verify
|
||||||
|
/**
|
||||||
|
* 验证数据的处理函数
|
||||||
|
* @param {Context} ctx 请求上下文
|
||||||
|
*/
|
||||||
|
export const verify = async (ctx: Context) => {
|
||||||
|
const { data } = ctx.action.params.values;
|
||||||
|
try {
|
||||||
|
for (const item of data) {
|
||||||
|
const { id } = item;
|
||||||
|
// 获取链上数据
|
||||||
|
const { hashedData: storedHashedData, salt } = await contractService.getData(id);
|
||||||
|
const jsonData = JSON.stringify(item);
|
||||||
|
const hashedData = hashWithSalt(jsonData, salt);
|
||||||
|
|
||||||
|
// 比较加密后的结果和链上的加密数据
|
||||||
|
if (hashedData === storedHashedData) {
|
||||||
|
console.log(`数据ID: ${id} 验证成功: 数据有效且未被篡改`);
|
||||||
|
} else {
|
||||||
|
console.log(`数据ID: ${id} 验证失败: 数据已被篡改或无效`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.body = '数据验证完成';
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
ctx.status = 500;
|
||||||
|
ctx.body = '验证数据时出错';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 事件监听在应用启动时调用
|
||||||
|
contractService.setupEventListeners();
|
@ -0,0 +1,25 @@
|
|||||||
|
import { exec } from 'child_process';
|
||||||
|
import path from 'path';
|
||||||
|
import { Application } from '@tachybase/server';
|
||||||
|
|
||||||
|
export default function (app: Application) {
|
||||||
|
app
|
||||||
|
.command('compile')
|
||||||
|
.option('-v, --version')
|
||||||
|
.action(async (options) => {
|
||||||
|
const scriptPath = path.join(__dirname, 'compile_contract.sh');
|
||||||
|
|
||||||
|
exec(`bash ${scriptPath}`, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(`执行脚本时出错: ${error.message}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`stdout: ${stdout}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
cd packages/plugins/@tachybase/plugin-blockchain
|
||||||
|
npx hardhat compile
|
@ -0,0 +1,25 @@
|
|||||||
|
import { exec } from 'child_process';
|
||||||
|
import path from 'path';
|
||||||
|
import { Application } from '@tachybase/server';
|
||||||
|
|
||||||
|
export default function (app: Application) {
|
||||||
|
app
|
||||||
|
.command('deploy')
|
||||||
|
.option('-v, --version')
|
||||||
|
.action(async (options) => {
|
||||||
|
const scriptPath = path.join(__dirname, 'deploy_contract.sh');
|
||||||
|
|
||||||
|
exec(`bash ${scriptPath}`, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(`执行脚本时出错: ${error.message}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`stdout: ${stdout}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
cd packages/plugins/@tachybase/plugin-blockchain
|
||||||
|
npx hardhat run deploy.js
|
@ -0,0 +1,25 @@
|
|||||||
|
import { exec } from 'child_process';
|
||||||
|
import path from 'path';
|
||||||
|
import { Application } from '@tachybase/server';
|
||||||
|
|
||||||
|
export default function (app: Application) {
|
||||||
|
app
|
||||||
|
.command('geth')
|
||||||
|
.option('-v, --version')
|
||||||
|
.action(async (options) => {
|
||||||
|
const scriptPath = path.join(__dirname, 'start_geth.sh');
|
||||||
|
|
||||||
|
exec(`bash ${scriptPath}`, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(`执行脚本时出错: ${error.message}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stderr) {
|
||||||
|
console.error(`stderr: ${stderr}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`stdout: ${stdout}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
cd ../eth-private-chain/
|
||||||
|
nohup geth --datadir "." --dev --dev.period 2 --http --http.api eth,web3,net --http.corsdomain "http://remix.ethereum.org" --password password.txt --http.port 8888 --ws --ws.api eth,web3,net --ws.origins "*" --ws.addr "127.0.0.1" --ws.port 8546 > geth_output.log 2>&1 &
|
@ -0,0 +1,22 @@
|
|||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成盐
|
||||||
|
* @returns {string} 盐字符串
|
||||||
|
*/
|
||||||
|
export function generateSalt(): string {
|
||||||
|
return crypto.randomBytes(16).toString('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用盐和数据进行加密
|
||||||
|
* @param {string} data 数据字符串
|
||||||
|
* @param {string} salt 盐字符串
|
||||||
|
* @returns {string} 加密后的哈希值
|
||||||
|
*/
|
||||||
|
export function hashWithSalt(data: string, salt: string): string {
|
||||||
|
return crypto
|
||||||
|
.createHash('sha256')
|
||||||
|
.update(data + salt)
|
||||||
|
.digest('hex');
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
export { default } from './plugin';
|
@ -0,0 +1,33 @@
|
|||||||
|
import { Plugin } from '@tachybase/server';
|
||||||
|
|
||||||
|
import { getDataHandler, store, verify } from './actions/blockchain';
|
||||||
|
import { contractService } from './web3/contractService';
|
||||||
|
|
||||||
|
export class PluginBlockchainServer extends Plugin {
|
||||||
|
async afterAdd() {}
|
||||||
|
|
||||||
|
async beforeLoad() {}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
this.app.resourcer.define({
|
||||||
|
name: 'blockchain',
|
||||||
|
actions: {
|
||||||
|
store,
|
||||||
|
getDataHandler,
|
||||||
|
verify,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.app.acl.allow('blockchain', '*', 'public');
|
||||||
|
}
|
||||||
|
|
||||||
|
async install() {}
|
||||||
|
|
||||||
|
async afterEnable() {}
|
||||||
|
|
||||||
|
async afterDisable() {}
|
||||||
|
|
||||||
|
async remove() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PluginBlockchainServer;
|
@ -0,0 +1,135 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import { web3Provider } from './web3Provider';
|
||||||
|
|
||||||
|
class ContractService {
|
||||||
|
private contract: any;
|
||||||
|
private defaultAccount: string;
|
||||||
|
private currentContractAbi: any;
|
||||||
|
private currentContractAddress: string;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.initializeContract();
|
||||||
|
setInterval(this.checkForContractAddressOrAbiChange.bind(this), 60000); // 每分钟检查一次
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化合约实例
|
||||||
|
*/
|
||||||
|
private initializeContract() {
|
||||||
|
const web3 = web3Provider.web3;
|
||||||
|
const account = web3.eth.accounts.privateKeyToAccount(process.env.PRIVATE_KEY as string);
|
||||||
|
web3.eth.accounts.wallet.add(account);
|
||||||
|
web3.eth.defaultAccount = account.address;
|
||||||
|
|
||||||
|
this.currentContractAbi = this.getContractAbi();
|
||||||
|
this.currentContractAddress = this.getContractAddress();
|
||||||
|
this.updateContractInstance(this.currentContractAbi, this.currentContractAddress);
|
||||||
|
this.defaultAccount = account.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合约文件中ABI
|
||||||
|
*/
|
||||||
|
private getContractAbi(): any {
|
||||||
|
const contractJson = JSON.parse(
|
||||||
|
fs.readFileSync(
|
||||||
|
path.resolve(__dirname, '../../../artifacts/contracts/DataStorage.sol/DataStorage.json'),
|
||||||
|
'utf-8',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return contractJson.abi;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取合约文件中地址
|
||||||
|
*/
|
||||||
|
private getContractAddress(): string {
|
||||||
|
const contractAddresses = JSON.parse(
|
||||||
|
fs.readFileSync(path.resolve(__dirname, '../../../contract-addresses.json'), 'utf-8'),
|
||||||
|
);
|
||||||
|
return contractAddresses.DataStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查合约地址或ABI是否发生变化
|
||||||
|
*/
|
||||||
|
private checkForContractAddressOrAbiChange() {
|
||||||
|
console.log('正在检查合约地址或 ABI 是否发生变化...');
|
||||||
|
const contractABI = this.getContractAbi();
|
||||||
|
const contractAddress = this.getContractAddress();
|
||||||
|
|
||||||
|
if (
|
||||||
|
contractAddress !== this.currentContractAddress ||
|
||||||
|
JSON.stringify(contractABI) !== JSON.stringify(this.currentContractAbi)
|
||||||
|
) {
|
||||||
|
console.log(`检测到合约地址或 ABI 发生变化。更新合约实例为新地址: ${contractAddress}`);
|
||||||
|
this.updateContractInstance(contractABI, contractAddress);
|
||||||
|
} else {
|
||||||
|
console.log('未检测到合约地址或 ABI 发生变化。');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新合约实例
|
||||||
|
* @param {any} newAbi 新的合约 ABI
|
||||||
|
* @param {string} newAddress 新的合约地址
|
||||||
|
*/
|
||||||
|
private updateContractInstance(newAbi: any, newAddress: string) {
|
||||||
|
this.currentContractAbi = newAbi;
|
||||||
|
this.currentContractAddress = newAddress;
|
||||||
|
this.contract = new web3Provider.web3.eth.Contract(newAbi, newAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存储数据到区块链
|
||||||
|
* @param {string} id 数据的唯一标识符
|
||||||
|
* @param {string} hashedData 加密后的数据
|
||||||
|
* @param {string} salt 数据使用的盐
|
||||||
|
*/
|
||||||
|
async storeData(id: string, hashedData: string, salt: string): Promise<void> {
|
||||||
|
this.checkForContractAddressOrAbiChange();
|
||||||
|
await this.contract.methods.storeData(id, hashedData, salt).send({ from: this.defaultAccount });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从区块链获取数据
|
||||||
|
* @param {string} id 数据的唯一标识符
|
||||||
|
* @returns {Promise<{ hashedData: string; salt: string }>} 返回哈希数据和盐
|
||||||
|
*/
|
||||||
|
async getData(id: string): Promise<{ hashedData: string; salt: string }> {
|
||||||
|
this.checkForContractAddressOrAbiChange();
|
||||||
|
return this.contract.methods.getData(id).call();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听合约事件
|
||||||
|
setupEventListeners() {
|
||||||
|
this.checkForContractAddressOrAbiChange();
|
||||||
|
// 监听 DataStored 事件
|
||||||
|
const dataStoredSubscription = this.contract.events.DataStored({ fromBlock: 'latest' });
|
||||||
|
|
||||||
|
dataStoredSubscription.on('data', (event) => {
|
||||||
|
console.log('交易hash:' + event.transactionHash);
|
||||||
|
console.log('区块高度:' + event.blockNumber);
|
||||||
|
console.log('DataStored event:', event.returnValues);
|
||||||
|
});
|
||||||
|
dataStoredSubscription.on('error', (error) => {
|
||||||
|
console.error('Error on DataStored event:', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听 DataDeleted 事件
|
||||||
|
const dataDeletedSubscription = this.contract.events.DataDeleted({ fromBlock: 'latest' });
|
||||||
|
|
||||||
|
dataDeletedSubscription.on('data', (event) => {
|
||||||
|
console.log('交易hash:' + event.transactionHash);
|
||||||
|
console.log('区块高度:' + event.blockNumber);
|
||||||
|
console.log('DataDeleted event:', event.returnValues);
|
||||||
|
});
|
||||||
|
dataDeletedSubscription.on('error', (error) => {
|
||||||
|
console.error('Error on DataDeleted event:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const contractService = new ContractService();
|
@ -0,0 +1,44 @@
|
|||||||
|
import { Web3 } from 'web3';
|
||||||
|
|
||||||
|
export class Web3Provider {
|
||||||
|
public web3: Web3;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.web3 = new Web3(new Web3.providers.WebsocketProvider('ws://127.0.0.1:8546'));
|
||||||
|
this.initializeWebSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
private initializeWebSocket() {
|
||||||
|
this.web3.currentProvider.on('connect', () => {
|
||||||
|
console.log('WebSocket connected');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.web3.currentProvider.on('error', (error) => {
|
||||||
|
console.error('WebSocket error:', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.web3.currentProvider.on('end', () => {
|
||||||
|
console.log('WebSocket connection closed. Reconnecting...');
|
||||||
|
this.connectWebSocket();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private connectWebSocket() {
|
||||||
|
this.web3.setProvider(new Web3.providers.WebsocketProvider('ws://127.0.0.1:8546'));
|
||||||
|
|
||||||
|
this.web3.currentProvider.on('connect', () => {
|
||||||
|
console.log('WebSocket reconnected');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.web3.currentProvider.on('error', (error) => {
|
||||||
|
console.error('WebSocket error:', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.web3.currentProvider.on('end', () => {
|
||||||
|
console.log('WebSocket connection closed again. Reconnecting...');
|
||||||
|
this.connectWebSocket();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const web3Provider = new Web3Provider();
|
@ -20,7 +20,7 @@ async function request(config, context) {
|
|||||||
const { token, origin } = context;
|
const { token, origin } = context;
|
||||||
const { method = 'POST', data, timeout = 5000 } = config;
|
const { method = 'POST', data, timeout = 5000 } = config;
|
||||||
|
|
||||||
const originUrl = (config.url?.url || '').trim();
|
const originUrl = config.url?.trim() || '';
|
||||||
const url = originUrl.startsWith('http') ? originUrl : `${origin}${originUrl}`;
|
const url = originUrl.startsWith('http') ? originUrl : `${origin}${originUrl}`;
|
||||||
|
|
||||||
let headers = (config.headers ?? []).reduce((result, header) => {
|
let headers = (config.headers ?? []).reduce((result, header) => {
|
||||||
|
2479
pnpm-lock.yaml
2479
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user