diff --git a/.gitignore b/.gitignore index 7f5603c..2e88850 100644 --- a/.gitignore +++ b/.gitignore @@ -130,4 +130,6 @@ dist .pnp.* .DS_Store bin/ -dist/ \ No newline at end of file +dist/ + +cache.json \ No newline at end of file diff --git a/package.json b/package.json index 6618e6a..1c13ad0 100644 --- a/package.json +++ b/package.json @@ -1,55 +1,58 @@ { - "name": "git3-cli", - "version": "0.1.1", - "license": "MIT", - "type": "module", - "engines": { - "node": ">=14.16" - }, - "bin": "dist/git3/index.js", - "dependencies": { - "@ethersproject/experimental": "^5.7.0", - "axios": "^1.3.2", - "bip39": "^3.0.4", - "buffer-split": "^1.0.0", - "colors-cli": "^1.0.29", - "commander": "^10.0.0", - "debug": "^4.3.4", - "eth-ens-namehash": "^2.0.8", - "ethers": "^5.7.2", - "form-data": "^4.0.0", - "git-config-path": "^2.0.0", - "inquirer": "^9.1.4", - "js-sha3": "^0.8.0", - "parse-git-config": "^3.0.0", - "rxjs": "^7.8.0", - "rxjs-async-map": "^0.2.0", - "rxjs-stream": "^5.0.0", - "superpathjoin": "^2.0.1", - "url-parse": "^1.5.10" - }, - "scripts": { - "clean": "rm -rf ./dist ./bin", - "build:git3": "esbuild src/git3/index.ts --bundle --platform=node --outfile=dist/git3.cjs --format=cjs", - "build:git-remote": "esbuild src/git-remote-git3/index.ts --bundle --platform=node --outfile=dist/git-remote-git3.cjs --format=cjs", - "build": "yarn clean && ( yarn build:git3 & yarn build:git-remote & wait)", - "pkg:git3": "pkg dist/git3.cjs -c git3.pkg.json", - "pkg:git-remote": "pkg dist/git-remote-git3.cjs -c git-remote-git3.pkg.json", - "pkg:all": "yarn pkg:git3 && yarn pkg:git-remote", - "pkg": "yarn build && yarn pkg:all", - "install-mac": "yarn pkg && cp bin/git3-macos /usr/local/bin/git3 && cp bin/git-remote-git3-macos /usr/local/bin/git-remote-git3", - "install-linux": "yarn pkg && cp bin/git3-linux /usr/local/bin/git3 && cp bin/git-remote-git3-linux /usr/local/bin/git-remote-git3" - }, - "devDependencies": { - "@types/buffer-split": "^1.0.0", - "@types/debug": "^4.1.7", - "@types/inquirer": "^9.0.3", - "@types/node": "^18.11.18", - "@types/parse-git-config": "^3.0.1", - "@types/url-parse": "^1.4.8", - "esbuild": "^0.17.0", - "pkg": "^5.8.0", - "ts-node": "^10.9.1", - "typescript": "^4.9.4" - } + "name": "git3-cli", + "version": "0.1.1", + "license": "MIT", + "type": "module", + "engines": { + "node": ">=14.16" + }, + "bin": "dist/git3/index.js", + "dependencies": { + "@ethersproject/experimental": "^5.7.0", + "axios": "^1.3.2", + "bip39": "^3.0.4", + "buffer-split": "^1.0.0", + "colors-cli": "^1.0.29", + "commander": "^10.0.0", + "debug": "^4.3.4", + "eth-ens-namehash": "^2.0.8", + "ethers": "^5.7.2", + "form-data": "^4.0.0", + "git-config-path": "^2.0.0", + "inquirer": "^9.1.4", + "js-sha3": "^0.8.0", + "parse-git-config": "^3.0.0", + "rxjs": "^7.8.0", + "rxjs-async-map": "^0.2.0", + "rxjs-stream": "^5.0.0", + "superpathjoin": "^2.0.1", + "url-parse": "^1.5.10" + }, + "scripts": { + "clean": "rm -rf ./dist ./bin", + "build:git3": "esbuild src/git3/index.ts --bundle --platform=node --outfile=dist/git3.cjs --format=cjs", + "build:git-remote": "esbuild src/git-remote-git3/index.ts --bundle --platform=node --outfile=dist/git-remote-git3.cjs --format=cjs", + "build": "yarn clean && ( yarn build:git3 & yarn build:git-remote & wait)", + "pkg:git3": "pkg dist/git3.cjs -c git3.pkg.json", + "pkg:git-remote": "pkg dist/git-remote-git3.cjs -c git-remote-git3.pkg.json", + "pkg:all": "yarn pkg:git3 && yarn pkg:git-remote", + "pkg": "yarn build && yarn pkg:all", + "install-mac": "yarn pkg && cp bin/git3-macos /usr/local/bin/git3 && cp bin/git-remote-git3-macos /usr/local/bin/git-remote-git3", + "install-linux": "yarn pkg && cp bin/git3-linux /usr/local/bin/git3 && cp bin/git-remote-git3-linux /usr/local/bin/git-remote-git3", + "sync": "ts-node src/scripts/sync.ts", + "clear-sync": "ts-node src/scripts/clear.ts" + }, + "devDependencies": { + "@types/buffer-split": "^1.0.0", + "@types/debug": "^4.1.7", + "@types/inquirer": "^9.0.3", + "@types/node": "^18.11.18", + "@types/parse-git-config": "^3.0.1", + "@types/url-parse": "^1.4.8", + "es-main": "^1.2.0", + "esbuild": "^0.17.0", + "pkg": "^5.8.0", + "ts-node": "^10.9.1", + "typescript": "^4.9.4" + } } diff --git a/src/common/git3-protocol.ts b/src/common/git3-protocol.ts index dc61746..b1fded9 100644 --- a/src/common/git3-protocol.ts +++ b/src/common/git3-protocol.ts @@ -6,6 +6,7 @@ import { getWallet, randomRPC, setupContract } from "./wallet.js" import Url from "url-parse" import network from "../config/evm-network.js" import abis from "../config/abis.js" +import { TxManager } from "./tx-manager.js" export type Git3Protocol = { sender: string @@ -20,23 +21,37 @@ export type Git3Protocol = { ns?: Record nsName?: string nsDomain?: string + factory?: ethers.Contract } -type Option = { +export type ParseOption = { skipRepoName?: boolean ignoreProtocolHeader?: boolean + includeFactory?: boolean +} + +export function initNameService(): ethers.Contract { + let nsContract = setupContract( + new ethers.providers.JsonRpcProvider("https://goerli-rollup.arbitrum.io/rpc"), + nameServices.resolver_, + abis.NameService + ) + return nsContract } export async function parseGit3URI( uri: string, - option: Option = { skipRepoName: false, ignoreProtocolHeader: false } + option: ParseOption = { + skipRepoName: false, + ignoreProtocolHeader: false, + includeFactory: false, + } ): Promise { if (option.ignoreProtocolHeader) { if (!uri.startsWith("git3://")) { uri = "git3://" + uri } } - console.error("uri", uri) const url = new Url(uri) let sender = url.username || "default" let chainId = url.port ? parseInt(url.port) : null @@ -60,12 +75,7 @@ export async function parseGit3URI( chainId = chainId || ns.chainId // Todo: temporary resolve name service - let resolverAddress = ns["resolver"] - let nsContract = setupContract( - new ethers.providers.JsonRpcProvider("https://goerli-rollup.arbitrum.io/rpc"), - resolverAddress, - abis.NameService - ) + let nsContract = initNameService() hubAddress = await nsContract.nameHub([nsName, nsDomain].join(".")) if (hubAddress == "0x0000000000000000000000000000000000000000") throw new Error(`${nsName} not found`) @@ -97,6 +107,11 @@ export async function parseGit3URI( let hub = setupContract(provider, hubAddress, abi, wallet) wallet = wallet.connect(hub.provider) + let factory + if (option.includeFactory) { + factory = setupContract(provider, netConfig.contracts.factory, abis.Factory, wallet) + } + return { sender, senderAddress, @@ -110,5 +125,40 @@ export async function parseGit3URI( ns, nsName, nsDomain, + factory, + } +} +export type FactoryProtocol = { + factory: ethers.Contract + txManager: TxManager + netConfig: Record + chainId: number +} + +export async function initFactoryByChainID( + chain: string, + wallet: ethers.Wallet | null +): Promise { + let netConfig, chainId + chainId = parseInt(chain) + if (chainId) { + netConfig = network[chainId] + } else { + let ns = nameServices[chain] + if (!ns) throw new Error(`invalid name service ${chain}`) + chainId = ns.chainId + netConfig = network[chainId] + } + + let rpc = randomRPC(netConfig.rpc) + const provider = new ethers.providers.JsonRpcProvider(rpc) + let factory = setupContract(provider, netConfig.contracts.factory, abis.Factory, wallet) + let txManager = new TxManager(factory, chainId, netConfig.txConst) + + return { + factory, + txManager, + netConfig, + chainId, } } diff --git a/src/config/abis.ts b/src/config/abis.ts index c7795b9..13b7c5f 100644 --- a/src/config/abis.ts +++ b/src/config/abis.ts @@ -1,863 +1,7 @@ export default { NameService: - '[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"HubRecords","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"nameHub","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nameList","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nameListLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"nameOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"hub","type":"address"}],"name":"rebindHubAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"hub","type":"address"}],"name":"registerHub","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferNameOwner","outputs":[],"stateMutability":"nonpayable","type":"function"}]', + '[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"address","name":"hub","type":"address"}],"name":"RegisterHub","type":"event"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"string","name":"","type":"string"}],"name":"HubRecords","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"hubName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"nameHub","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nameList","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nameListLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"nameOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"hub","type":"address"}],"name":"rebindHubAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"hub","type":"address"}],"name":"registerHub","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferNameOwner","outputs":[],"stateMutability":"nonpayable","type":"function"}]', Factory: '[{"inputs":[{"internalType":"bool","name":"_dbSelector","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"hub","type":"address"},{"indexed":true,"internalType":"address","name":"creator","type":"address"}],"name":"CreateHub","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[{"internalType":"bool","name":"isPermissionless","type":"bool"}],"name":"createHub","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"dbSelector","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hubImp","outputs":[{"internalType":"contract Hubv3","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"hubs","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"newHubImp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"setHubImp","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]', - Hub: [ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint8", - "name": "version", - "type": "uint8" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "previousAdminRole", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "bytes32", - "name": "newAdminRole", - "type": "bytes32" - } - ], - "name": "RoleAdminChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "RoleGranted", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "RoleRevoked", - "type": "event" - }, - { - "inputs": [], - "name": "CONTRIBUTOR", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "DEFAULT_ADMIN_ROLE", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "MANAGER", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "NOTFOUND", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "RoleList", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "member", - "type": "address" - } - ], - "name": "addContributor", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "member", - "type": "address" - } - ], - "name": "addManager", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "repoName", - "type": "bytes" - }, - { - "internalType": "address", - "name": "con", - "type": "address" - } - ], - "name": "addRepoContributor", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "repoName", - "type": "bytes" - }, - { - "internalType": "bytes[]", - "name": "path", - "type": "bytes[]" - }, - { - "internalType": "bytes[]", - "name": "data", - "type": "bytes[]" - } - ], - "name": "batchUpload", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "repoName", - "type": "bytes" - } - ], - "name": "createRepo", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "db", - "outputs": [ - { - "internalType": "contract database", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "repoName", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "branchPath", - "type": "bytes" - } - ], - "name": "delRepoRef", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "repoName", - "type": "bytes" - } - ], - "name": "deleteRepo", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "repoName", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "path", - "type": "bytes" - } - ], - "name": "download", - "outputs": [ - { - "internalType": "bytes", - "name": "", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "repoName", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "branchPath", - "type": "bytes" - } - ], - "name": "getRepoRef", - "outputs": [ - { - "internalType": "bytes20", - "name": "", - "type": "bytes20" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - } - ], - "name": "getRoleAdmin", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "uint256", - "name": "index", - "type": "uint256" - } - ], - "name": "getRoleMember", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - } - ], - "name": "getRoleMemberCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "grantRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "hasRole", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bool", - "name": "dbSelector", - "type": "bool" - }, - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "bool", - "name": "isPermissionless", - "type": "bool" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "repoName", - "type": "bytes" - } - ], - "name": "listRepoRefs", - "outputs": [ - { - "components": [ - { - "internalType": "bytes20", - "name": "hash", - "type": "bytes20" - }, - { - "internalType": "bytes", - "name": "name", - "type": "bytes" - } - ], - "internalType": "struct Repolib.refData[]", - "name": "list", - "type": "tuple[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "member", - "type": "address" - } - ], - "name": "memberRole", - "outputs": [ - { - "internalType": "bool", - "name": "IsAdmin", - "type": "bool" - }, - { - "internalType": "bool", - "name": "IsManager", - "type": "bool" - }, - { - "internalType": "bool", - "name": "IsContributor", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "member", - "type": "address" - } - ], - "name": "membership", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "permissionless", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "permissionlessJoin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "member", - "type": "address" - } - ], - "name": "removeContributor", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "member", - "type": "address" - } - ], - "name": "removeManager", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "repoName", - "type": "bytes" - }, - { - "internalType": "address", - "name": "con", - "type": "address" - } - ], - "name": "removeRepoContributor", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "renounceRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "repoName", - "type": "bytes" - } - ], - "name": "repoContributors", - "outputs": [ - { - "internalType": "address[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "repoList", - "outputs": [ - { - "internalType": "bytes[]", - "name": "rn", - "type": "bytes[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "repoNames", - "outputs": [ - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "repoName", - "type": "bytes" - } - ], - "name": "repoOwner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "revokeRole", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes32", - "name": "role", - "type": "bytes32" - } - ], - "name": "roleToMembers", - "outputs": [ - { - "internalType": "address[]", - "name": "members", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bool", - "name": "open", - "type": "bool" - } - ], - "name": "setPermissonlessJoin", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "repoName", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "branchPath", - "type": "bytes" - }, - { - "internalType": "bytes20", - "name": "refHash", - "type": "bytes20" - } - ], - "name": "setRepoRef", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes4", - "name": "interfaceId", - "type": "bytes4" - } - ], - "name": "supportsInterface", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "repoName", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "path", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "upload", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "repoName", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "path", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "chunkId", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "uploadChunk", - "outputs": [], - "stateMutability": "payable", - "type": "function" - } - ], - + Hub: '[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"repoName","type":"bytes"},{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"RepoCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"repoName","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"branchPath","type":"bytes"},{"indexed":false,"internalType":"bytes20","name":"refHash","type":"bytes20"}],"name":"SetRepoRef","type":"event"},{"inputs":[],"name":"CONTRIBUTOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MANAGER","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NOTFOUND","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"RoleList","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"member","type":"address"}],"name":"addContributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"member","type":"address"}],"name":"addManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"address","name":"con","type":"address"}],"name":"addRepoContributor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"bytes[]","name":"path","type":"bytes[]"},{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"batchUpload","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"}],"name":"createRepo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"db","outputs":[{"internalType":"contract database","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"bytes","name":"branchPath","type":"bytes"}],"name":"delRepoRef","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"}],"name":"deleteRepo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"bytes","name":"path","type":"bytes"}],"name":"download","outputs":[{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"bytes","name":"branchPath","type":"bytes"}],"name":"getRepoRef","outputs":[{"internalType":"bytes20","name":"","type":"bytes20"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"dbSelector","type":"bool"},{"internalType":"address","name":"user","type":"address"},{"internalType":"bool","name":"isPermissionless","type":"bool"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"}],"name":"listRepoRefs","outputs":[{"components":[{"internalType":"bytes20","name":"hash","type":"bytes20"},{"internalType":"bytes","name":"name","type":"bytes"}],"internalType":"struct Repolib.refData[]","name":"list","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"member","type":"address"}],"name":"memberRole","outputs":[{"internalType":"bool","name":"IsAdmin","type":"bool"},{"internalType":"bool","name":"IsManager","type":"bool"},{"internalType":"bool","name":"IsContributor","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"member","type":"address"}],"name":"membership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"permissionless","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"permissionlessJoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"member","type":"address"}],"name":"removeContributor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"member","type":"address"}],"name":"removeManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"address","name":"con","type":"address"}],"name":"removeRepoContributor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"}],"name":"repoContributors","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"repoList","outputs":[{"internalType":"bytes[]","name":"rn","type":"bytes[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"repoNames","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"}],"name":"repoOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"roleToMembers","outputs":[{"internalType":"address[]","name":"members","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"open","type":"bool"}],"name":"setPermissonlessJoin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"bytes","name":"branchPath","type":"bytes"},{"internalType":"bytes20","name":"refHash","type":"bytes20"}],"name":"setRepoRef","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upload","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"uint256","name":"chunkId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"uploadChunk","outputs":[],"stateMutability":"payable","type":"function"}]', } diff --git a/src/config/evm-network.ts b/src/config/evm-network.ts index d9888c0..03e1da8 100644 --- a/src/config/evm-network.ts +++ b/src/config/evm-network.ts @@ -89,7 +89,7 @@ const evmNetworks: Record = { txConst: { blockTimeSec: 7, }, - contracts: { factory: "0x9f0a56c4b11b6f65454C205b79C91424B5C6d590" }, + contracts: { factory: "0x5f0f1eb909dE5F9EF48C154F61b2FEB8fd3dA53f" }, }, 421613: { name: "Arbitrum - Goerli", @@ -114,7 +114,7 @@ const evmNetworks: Record = { rbfTimes: 5, boardcastTimes: 10, }, - contracts: { factory: "0x51bb7F23193b88696D25EAec7E3293a2C96e55Ee" }, + contracts: { factory: "0xa709975Bc01e745432f8898499E7b9a60f420117" }, }, } diff --git a/src/config/name-services.ts b/src/config/name-services.ts index 7adb006..d3bad57 100644 --- a/src/config/name-services.ts +++ b/src/config/name-services.ts @@ -1,15 +1,13 @@ const ns: Record = { + resolver_: "0xd684DBf9124d6B1f74f37558dD11fdcE0103AF15", w3q: { chainId: 3334, - resolver: "0x372343Dd5274bb7B571F81d4FBA3bC0E28FeF3D9", }, arb: { chainId: 42170, - resolver: "0x372343Dd5274bb7B571F81d4FBA3bC0E28FeF3D9", }, arbg: { chainId: 421613, - resolver: "0x372343Dd5274bb7B571F81d4FBA3bC0E28FeF3D9", }, } diff --git a/src/git3/index.ts b/src/git3/index.ts index 8b42e2d..6f52e52 100644 --- a/src/git3/index.ts +++ b/src/git3/index.ts @@ -5,11 +5,9 @@ import bip39 from "bip39" import inquirer from "inquirer" import { importActions, generateActions,createHubActions,HubMemberActions } from "./actions.js" import network from "../config/evm-network.js" -import { explorerTxUrl, getWallet, randomRPC, setupContract } from "../common/wallet.js" -import { parseGit3URI } from "../common/git3-protocol.js" +import { explorerTxUrl, getWallet, randomRPC } from "../common/wallet.js" +import { initFactoryByChainID, parseGit3URI } from "../common/git3-protocol.js" import { TxManager } from "../common/tx-manager.js" -import nameServices from "../config/name-services.js" -import abis from "../config/abis.js" const program = new Command() @@ -180,31 +178,18 @@ hub let isPermissionless = permissionless === "yes"? true:false console.log(`creating hub with permissionless:${isPermissionless} ...`) - let netConfig, chainId - chainId = parseInt(chain) - if (chainId) { - netConfig = network[chainId] - } else { - let ns = nameServices[chain] - if (!ns) throw new Error(`invalid name service ${chain}`) - chainId = ns.chainId - netConfig = network[chainId] - } const wallet = await getWallet() - let rpc = randomRPC(netConfig.rpc) - const provider = new ethers.providers.JsonRpcProvider(rpc) + let protocol = await initFactoryByChainID(chain, wallet) - let factory = setupContract(provider, netConfig.contracts.factory, abis.Factory, wallet) - let txManager = new TxManager(factory, chainId, netConfig.txConst) - let receipt = await txManager.SendCall("createHub", [isPermissionless]) + let receipt = await protocol.txManager.SendCall("createHub", [isPermissionless]) // let CreateHubEvent = factory.interface.getEvent("CreateHub"); - console.log(explorerTxUrl(receipt.transactionHash, netConfig.explorers)) + console.log(explorerTxUrl(receipt.transactionHash, protocol.netConfig.explorers)) let events = receipt.logs .map((log: any) => { try { - return factory.interface.parseLog(log) + return protocol.factory.interface.parseLog(log) } catch (e) { return null } @@ -420,7 +405,7 @@ repo repo .command("members") - .argument("","ex: git3.w3q/repo_name or hub_addr:chainid/repo_name") + .argument("", "ex: git3.w3q/repo_name or hub_addr:chainid/repo_name") .description("get all members information of the repository") .action(async (uri) => { let protocol = await parseGit3URI(uri, { ignoreProtocolHeader: true, skipRepoName: true }) @@ -438,17 +423,23 @@ repo .action(async (conAddr,options) => { let protocol = await parseGit3URI(options.uri, { ignoreProtocolHeader: true, skipRepoName: true }) let owner = await protocol.hub.repoOwner(Buffer.from(protocol.repoName)) - if (owner != protocol.wallet.address){ + if (owner != protocol.wallet.address) { let hubName = protocol.ns ? `${protocol.nsName}.${protocol.nsDomain}` : protocol.hubAddress - console.error(`[repo addContributor] can only be executed with the owner authority of this repository:${protocol.repoName}-hub:${hubName}`) + console.error( + `[repo addContributor] can only be executed with the owner authority of this repository:${protocol.repoName}-hub:${hubName}` + ) } const txManager = new TxManager(protocol.hub, protocol.chainId, protocol.netConfig.txConst) - let receipt = await txManager.SendCall("addRepoContributor", [Buffer.from(protocol.repoName),conAddr]) + let receipt = await txManager.SendCall("addRepoContributor", [ + Buffer.from(protocol.repoName), + conAddr, + ]) console.log(explorerTxUrl(receipt.transactionHash, protocol.netConfig.explorers)) }) + repo .command("remove-member") .argument("","contributor address") @@ -457,17 +448,23 @@ repo .action(async (conAddr,options) => { let protocol = await parseGit3URI(options.uri, { ignoreProtocolHeader: true, skipRepoName: true }) let owner = await protocol.hub.repoOwner(Buffer.from(protocol.repoName)) - if (owner != protocol.wallet.address){ + if (owner != protocol.wallet.address) { let hubName = protocol.ns ? `${protocol.nsName}.${protocol.nsDomain}` : protocol.hubAddress - console.error(`[repository removeContributor] can only be executed with the owner authority of this repository:${protocol.repoName}-hub:${hubName}`) + console.error( + `[repository removeContributor] can only be executed with the owner authority of this repository:${protocol.repoName}-hub:${hubName}` + ) } const txManager = new TxManager(protocol.hub, protocol.chainId, protocol.netConfig.txConst) - let receipt = await txManager.SendCall("removeRepoContributor", [Buffer.from(protocol.repoName),conAddr]) + let receipt = await txManager.SendCall("removeRepoContributor", [ + Buffer.from(protocol.repoName), + conAddr, + ]) console.log(explorerTxUrl(receipt.transactionHash, protocol.netConfig.explorers)) }) + // Todo: set-wallet temporarily useless // program // .command("set-wallet") diff --git a/src/scripts/clear.ts b/src/scripts/clear.ts new file mode 100644 index 0000000..e8985fc --- /dev/null +++ b/src/scripts/clear.ts @@ -0,0 +1,10 @@ +import { api, deleteHub } from "./sync.js" +import fs from "fs" + +fs.unlinkSync("./cache.json") + +let res = await api.get("/orgs") +for (const org of res.data) { + console.log("hub:", org.name) + deleteHub(org.name) +} diff --git a/src/scripts/sync.ts b/src/scripts/sync.ts new file mode 100644 index 0000000..c651676 --- /dev/null +++ b/src/scripts/sync.ts @@ -0,0 +1,276 @@ +import { + FactoryProtocol, + initFactoryByChainID, + initNameService, + parseGit3URI, +} from "../common/git3-protocol.js" +import network from "../config/evm-network.js" +import { readFileSync, writeFileSync, existsSync } from "fs" +import { ethers } from "ethers" +import axios from "axios" +import nameServices from "../config/name-services.js" +import { Retrier } from "../common/queue-task.js" + +let cache = loadCache() + +export let api = axios.create({ + baseURL: `http://127.0.0.1:${process.env.port || 3331}/api/v1/`, + responseType: "json", + headers: { + "Content-Type": "application/json", + Authorization: `token ${process.env.TOKEN}`, + }, + validateStatus: function (status) { + return status < 500 + }, +}) + +function loadCache() { + existsSync("./cache.json") || writeFileSync("./cache.json", "") + let text = readFileSync("./cache.json").toString() + if (text == "") { + return { + factory: { + 3334: { + start: 5375112, + last: 5375112, + }, + 421613: { + start: 9341366, + last: 9341366, + }, + }, + hubs: {}, + ns: { + start: 9340900, + last: 9340900, + nameHub: {}, + hubName: {}, + }, + } + } else { + return JSON.parse(text) + } +} + +function saveCache(cache: any) { + writeFileSync("./cache.json", JSON.stringify(cache)) +} +const RANGE = 1000 +const WAIT_SECONDS = 10 + +async function eventIterator( + contract: ethers.Contract, + filters: any[], + last: number, + saveLastCallback: (_last: number) => void, + eventCallback: (event: any) => void, + stop: () => boolean = () => false +) { + let provider = contract.provider + + while (true) { + if (stop && stop()) break + let lastBlock = await Retrier(async () => await provider.getBlockNumber(), { maxRetry: 10 }) + for (let i = last; i < lastBlock; i += RANGE) { + let end = i + RANGE - 1 + if (end >= lastBlock) end = lastBlock - 1 + console.log(i, end) + for (const filter of filters) { + let events = await Retrier(async () => await contract.queryFilter(filter, i, end), { + maxRetry: 10, + }) + for (const event of events) { + await eventCallback(event) + //console.log(protocol.chainId, i, end, event.args, event) + } + } + + last = end + saveLastCallback(last) + } + await new Promise((resolve) => setTimeout(resolve, WAIT_SECONDS * 1000)) + } +} + +async function syncFactory(protocol: FactoryProtocol) { + let factory = protocol.factory + let last = cache.factory[protocol.chainId].last + console.log("syncFactory", protocol.chainId, last) + await eventIterator( + factory, + [factory.filters.CreateHub()], + last, + (_last) => { + cache.factory[protocol.chainId].last = _last + saveCache(cache) + }, + async (event) => { + let hubAddr = event.args!.hub + if (cache.ns.hubName[hubAddr]) { + hubAddr = cache.ns.hubName[hubAddr] + } else { + hubAddr = `${hubAddr}:${protocol.chainId}` + } + console.log("hub:", hubAddr, "block:", event.blockNumber) + await createHub(hubAddr) + syncHub(hubAddr, event.blockNumber) + } + ) +} + +function Hex0xToStr(hex0x: string) { + return Buffer.from(hex0x.slice(2), "hex").toString() +} + +async function syncHub(hubAddr: string, start: number) { + if (start > 0) { + cache.hubs[hubAddr] = { start, last: start } + } + let protocol = await parseGit3URI(hubAddr, { skipRepoName: true, ignoreProtocolHeader: true }) + let hub = protocol.hub + let last = cache.hubs[hubAddr].last + + await eventIterator( + hub, + [hub.filters.RepoCreated(), hub.filters.SetRepoRef()], + last, + (_last) => { + if (cache.hubs[hubAddr]) { + cache.hubs[hubAddr].last = _last + saveCache(cache) + } + }, + async (event) => { + if (event.event == "RepoCreated") { + let repoName = Hex0xToStr(event.args!.repoName) + console.log("repo:", hubAddr, repoName) + await mirrorRepo(hubAddr, repoName) + } else if (event.event == "SetRepoRef") { + let repoName = Hex0xToStr(event.args!.repoName) + await pullRepo(hubAddr, repoName) + } + }, + () => { + return !cache.hubs[hubAddr] + } + ) +} + +async function mirrorRepo(hubAddr: string, repoName: string) { + let uri = `git3://${hubAddr}/${repoName}` + let res = await api.post("/repos/migrate", { + clone_addr: uri, + mirror: true, + mirror_interval: "1h", + private: false, + repo_name: repoName, + repo_owner: hubAddr, + service: "git", + uid: 0, + }) + console.log("mirrorRepo", uri, res.status) +} + +async function pullRepo(hubAddr: string, repoName: string) { + let uri = `git3://${hubAddr}/${repoName}` + let res = await api.post(`/repos/${hubAddr}/${repoName}/mirror-sync`) + console.log("pullRepo", uri, res.status) +} + +async function migrateHub(oldHubAddr: string, newHubAddr: string) { + console.log("migrateHub:", oldHubAddr, newHubAddr) + let oldHub = cache.hubs[oldHubAddr] + if (oldHub) { + deleteHub(oldHubAddr) + delete cache.hubs[oldHubAddr] + await createHub(newHubAddr) + syncHub(newHubAddr, oldHub.start) + } else { + await createHub(newHubAddr) + } +} + +async function syncNameService() { + let nsContract = initNameService() + let last = cache.ns.last + + await eventIterator( + nsContract, + [nsContract.filters.RegisterHub()], + last, + (_last) => { + cache.ns.last = _last + saveCache(cache) + }, + async (event) => { + let name = event.args!.name + let hub = event.args!.hub + let [_, nsDomain] = name.split(".") + let ns = nameServices[nsDomain] + let hubAddr = `${hub}:${ns.chainId}` + let old = cache.ns.nameHub[name] + if (!old) { + await migrateHub(hubAddr, name) + } else { + if (old != hub) { + // Rebind NS hub address + delete cache.ns.hubName[old] + await migrateHub(name, name) + } else { + // same name, do nothing + } + } + console.log("ns:", name, hub) + cache.ns.nameHub[name] = hub + cache.ns.hubName[hub] = name + saveCache(cache) + } + ) +} + +export async function deleteHub(hubAddr: string): Promise { + let res = await api.get(`/orgs/${hubAddr}/repos`, { params: { page: 1, limit: 100000 } }) + if (res.status != 200) { + return false + } + for (const repo of res.data) { + res = await api.delete(`/repos/${hubAddr}/${repo.name}`) + console.log("delete repo:", `${hubAddr}/${repo.name}`) + } + res = await api.delete(`/orgs/${hubAddr}`) + console.log(res.status, res.data) + return res.status == 200 +} + +async function createHub(hubAddr: string) { + let res = await api.post(`/orgs`, { + repo_admin_change_team_access: true, + username: hubAddr, + visibility: "public", + }) + if (res.status != 201) { + console.log("[ERROR] createHub", hubAddr, res.status, res.data) + } + return res.status == 201 +} + +async function main() { + let tasks = [] + tasks.push(syncNameService()) + + for (const [chainId, _] of Object.entries(network)) { + let protocol = await initFactoryByChainID(chainId, null) + tasks.push(syncFactory(protocol)) + } + + for (const [hubAddr, _] of Object.entries(cache.hubs)) { + tasks.push(syncHub(hubAddr, 0)) + } + + await Promise.all(tasks) +} +import esMain from "es-main" +if (esMain(import.meta)) { + main() +} diff --git a/src/storage/ETHStorage.ts b/src/storage/ETHStorage.ts index 925565b..8c5d5a9 100644 --- a/src/storage/ETHStorage.ts +++ b/src/storage/ETHStorage.ts @@ -2,9 +2,7 @@ import { Ref, Status, Storage } from "./storage.js" import { ethers } from "ethers" import { TxManager } from "../common/tx-manager.js" import { Git3Protocol } from "../common/git3-protocol.js" -import { buffer } from "stream/consumers" - export class ETHStorage implements Storage { repoName: string wallet: ethers.Signer @@ -41,60 +39,69 @@ export class ETHStorage implements Storage { async upload(path: string, file: Buffer): Promise { try { - let chunks: Buffer[] = []; - let costs:number[] = []; + let chunks: Buffer[] = [] + let costs: number[] = [] const FileChunkSize = 475 * 1024 const perEthPayStorageSize = 24 * 1024 - const initCodeLen = 280; + const initCodeLen = 280 if (file.length > FileChunkSize) { - let uploadedSize = 0; - let nouploadfileSize = file.length; + let uploadedSize = 0 + let nouploadfileSize = file.length for (uploadedSize = 0; uploadedSize < file.length; uploadedSize += FileChunkSize) { - let newchunk:Buffer; - let cost:number; - if (file.length != nouploadfileSize + uploadedSize){ - throw new Error(`file.length${file.length} != nouploadfileSize${nouploadfileSize} + uploadedSize${uploadedSize}`) + let newchunk: Buffer + let cost: number + if (file.length != nouploadfileSize + uploadedSize) { + throw new Error( + `file.length${file.length} != nouploadfileSize${nouploadfileSize} + uploadedSize${uploadedSize}` + ) } - if (nouploadfileSize < FileChunkSize){ + if (nouploadfileSize < FileChunkSize) { newchunk = Buffer.alloc(nouploadfileSize) - file.copy(newchunk, 0,uploadedSize,file.length) - cost = Math.ceil((nouploadfileSize+ initCodeLen) / perEthPayStorageSize) - }else{ + file.copy(newchunk, 0, uploadedSize, file.length) + cost = Math.ceil((nouploadfileSize + initCodeLen) / perEthPayStorageSize) + } else { newchunk = Buffer.alloc(FileChunkSize) - file.copy(newchunk, 0,uploadedSize,uploadedSize+FileChunkSize) - cost = Math.ceil((FileChunkSize+initCodeLen) / perEthPayStorageSize) - nouploadfileSize -= FileChunkSize; + file.copy(newchunk, 0, uploadedSize, uploadedSize + FileChunkSize) + cost = Math.ceil((FileChunkSize + initCodeLen) / perEthPayStorageSize) + nouploadfileSize -= FileChunkSize } - chunks.push(newchunk); - costs.push(cost); + chunks.push(newchunk) + costs.push(cost) } - chunks.forEach(async (context,index) => { - console.error(`=== uploading file chunk ${path} chunkId-${index} chunk_size:${context.length} storage_cost:${costs[index]}-token===`) + chunks.forEach(async (context, index) => { + console.error( + `=== uploading file chunk ${path} chunkId-${index} chunk_size:${context.length} storage_cost:${costs[index]}-token===` + ) await this.txManager.SendCall("uploadChunk", [ Buffer.from(this.repoName), Buffer.from(path), index, context, - {value:ethers.utils.parseEther(costs[index].toString())}, - ]); - console.error(`=== upload file chunk ${path} chunkId-${index} chunk_size:${context.length} storage_cost:${costs[index]}-token Succeed===`) + { value: ethers.utils.parseEther(costs[index].toString()) }, + ]) + console.error( + `=== upload file chunk ${path} chunkId-${index} chunk_size:${context.length} storage_cost:${costs[index]}-token Succeed===` + ) }) - } else { - let cost:number = 0; - if (file.length > perEthPayStorageSize){ - cost = Math.ceil(file.length / perEthPayStorageSize); + let cost: number = 0 + if (file.length > perEthPayStorageSize) { + cost = Math.ceil(file.length / perEthPayStorageSize) } - console.error(`=== uploading file ${path} file_size ${file.length} storage_cost:${cost}-token===`) + console.error( + `=== uploading file ${path} file_size ${file.length} storage_cost:${cost}-token===` + ) await this.txManager.SendCall("upload", [ Buffer.from(this.repoName), Buffer.from(path), file, - {value:ethers.utils.parseEther(cost.toString())}, + { value: ethers.utils.parseEther(cost.toString()) }, ]) - console.error(`=== upload ${path} file_size ${file.length} storage_cost:${cost}-token succeed ===`) + console.error( + `=== upload ${path} file_size ${file.length} storage_cost:${cost}-token succeed ===` + ) } return Status.SUCCEED } catch (error: any) { diff --git a/src/storage/SLIStorage.ts b/src/storage/SLIStorage.ts index 83af7b1..83701c6 100644 --- a/src/storage/SLIStorage.ts +++ b/src/storage/SLIStorage.ts @@ -67,7 +67,7 @@ export class SLIStorage implements Storage { async () => await this.contract.download(Buffer.from(this.repoName), Buffer.from(path)), { maxRetry: 10 } ) - const buffer = Buffer.from(res.slice(2), "hex") + const buffer = Buffer.from(res[0].slice(2), "hex") const cid = buffer.toString("utf8") for (let i = 0; i < ipfsConf.gateways.length; i++) { let gateway = ipfsConf.gateways[Math.floor(Math.random() * ipfsConf.gateways.length)] //random get rpc @@ -149,7 +149,7 @@ export class SLIStorage implements Storage { let queue = this.batchQueue this.batchQueue = [] - console.error(`[${reason}] commit queue length ${queue.length}`) + // console.error(`[${reason}] commit queue length ${queue.length}`) if (queue.length === 0) return let err diff --git a/yarn.lock b/yarn.lock index 050684e..2eb4954 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1061,6 +1061,11 @@ end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" +es-main@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/es-main/-/es-main-1.2.0.tgz#b85954f1d9d9f542fcb08685ec19515f969bad16" + integrity sha512-A4tCSY43O/mH4rHjG1n0mI4DhK2BmKDr8Lk8PXK/GBB6zxGFGmIW4bbkbTQ2Gi9iNamMZ9vbGrwjZOIeiM7vMw== + esbuild@^0.17.0: version "0.17.0" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.17.0.tgz#fcf19373d1d546bdbec1557276284c0e6350380b"