From 46c16d759a6ddd5dc20f100d459bdac654bf6cb8 Mon Sep 17 00:00:00 2001 From: cyhhao Date: Sun, 26 Feb 2023 03:29:36 +0800 Subject: [PATCH] add create hub and join hub --- package.json | 4 +- src/common/git3-protocol.ts | 38 +++++---- src/common/wallet.ts | 8 ++ src/config/abis.ts | 3 + src/config/evm-network.ts | 4 +- src/config/name-services.ts | 6 +- src/git-remote-git3/git.ts | 22 +++--- src/git3/index.ts | 148 ++++++++++++++++++++++++------------ src/storage/ETHStorage.ts | 8 +- src/storage/SLIStorage.ts | 10 +-- yarn.lock | 23 ++++++ 11 files changed, 182 insertions(+), 92 deletions(-) diff --git a/package.json b/package.json index 1f64189..6618e6a 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "rxjs": "^7.8.0", "rxjs-async-map": "^0.2.0", "rxjs-stream": "^5.0.0", - "superpathjoin": "^2.0.1" + "superpathjoin": "^2.0.1", + "url-parse": "^1.5.10" }, "scripts": { "clean": "rm -rf ./dist ./bin", @@ -45,6 +46,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", diff --git a/src/common/git3-protocol.ts b/src/common/git3-protocol.ts index 54f31a5..dc61746 100644 --- a/src/common/git3-protocol.ts +++ b/src/common/git3-protocol.ts @@ -3,6 +3,7 @@ import nameServices from "../config/name-services.js" import { ETHStorage } from "../storage/ETHStorage.js" import { SLIStorage } from "../storage/SLIStorage.js" 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" @@ -14,7 +15,7 @@ export type Git3Protocol = { chainId: number netConfig: Record wallet: ethers.Wallet - contract: ethers.Contract + hub: ethers.Contract storageClass: any ns?: Record nsName?: string @@ -22,31 +23,38 @@ export type Git3Protocol = { } type Option = { - skipRepoName: boolean + skipRepoName?: boolean + ignoreProtocolHeader?: boolean } export async function parseGit3URI( uri: string, - option: Option = { skipRepoName: false } + option: Option = { skipRepoName: false, ignoreProtocolHeader: false } ): Promise { - const url = new URL(uri) + 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 - let hub = url.hostname + let hostname = url.hostname let hubAddress let nsName, nsDomain, ns - if (!hub) throw new Error("invalid git3 uri, no hub address") + if (!hostname) throw new Error("invalid git3 uri, no hub address") let repoName = url.pathname.slice(1) if (!option.skipRepoName && !repoName) throw new Error("invalid git3 uri, no repo name") - if (hub.indexOf(".") < 0) { - if (url.hostname.startsWith("0x")) { - hubAddress = url.hostname + if (hostname.indexOf(".") < 0) { + if (hostname.startsWith("0x")) { + hubAddress = hostname } else { throw new Error("invalid git3 uri, hub must be NS or address") } } else { - ;[nsName, nsDomain] = url.hostname.split(".") + ;[nsName, nsDomain] = hostname.split(".") ns = nameServices[nsDomain] if (!ns) throw new Error(`invalid name service ${nsDomain}`) chainId = chainId || ns.chainId @@ -78,16 +86,16 @@ export async function parseGit3URI( let storageClass, abi if (chainId == 3334) { storageClass = ETHStorage - abi = abis.ETHStorage + abi = abis.Hub } else { storageClass = SLIStorage - abi = abis.SLIStorage + abi = abis.Hub } let rpc = randomRPC(netConfig.rpc) const provider = new ethers.providers.JsonRpcProvider(rpc) - let contract = setupContract(provider, hubAddress, abi, wallet) - wallet = wallet.connect(contract.provider) + let hub = setupContract(provider, hubAddress, abi, wallet) + wallet = wallet.connect(hub.provider) return { sender, @@ -97,7 +105,7 @@ export async function parseGit3URI( chainId, netConfig, wallet, - contract, + hub: hub, storageClass, ns, nsName, diff --git a/src/common/wallet.ts b/src/common/wallet.ts index cb3257f..e3d4b69 100644 --- a/src/common/wallet.ts +++ b/src/common/wallet.ts @@ -34,3 +34,11 @@ export function setupContract( export function randomRPC(rpcs: string[]): string { return rpcs[Math.floor(Math.random() * rpcs.length)] } + +export function explorerTxUrl(txHash: string, explorers: any[]): string { + if (explorers && explorers.length > 0) { + return explorers[0].url.replace(/\/+$/, "") + "/tx/" + txHash + } else { + return txHash + } +} diff --git a/src/config/abis.ts b/src/config/abis.ts index 8bf0766..196203c 100644 --- a/src/config/abis.ts +++ b/src/config/abis.ts @@ -5,4 +5,7 @@ export default { '[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"repoName","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"ref","type":"bytes"}],"name":"PushRef","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":false,"internalType":"bytes","name":"repoName","type":"bytes"},{"indexed":false,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"RepoOwnerTransfer","type":"event"},{"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":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"bytes","name":"ref","type":"bytes"}],"name":"delRef","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"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"}],"name":"listRefs","outputs":[{"components":[{"internalType":"bytes20","name":"hash","type":"bytes20"},{"internalType":"bytes","name":"name","type":"bytes"}],"internalType":"struct Git3HubStorage_SLI.refData[]","name":"list","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"nameToRefInfo","outputs":[{"internalType":"bytes20","name":"hash","type":"bytes20"},{"internalType":"uint96","name":"index","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"pathToHash","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"bytes","name":"path","type":"bytes"}],"name":"remove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"}],"name":"repoNameToOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"repoNameToRefs","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"bytes","name":"ref","type":"bytes"},{"internalType":"bytes20","name":"refHash","type":"bytes20"}],"name":"setRef","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","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"}]', 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"}]', + 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":"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"}],"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":[{"internalType":"bool","name":"open","type":"bool"}],"name":"openPermissonlessJoin","outputs":[],"stateMutability":"nonpayable","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":[{"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":"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"}]', } diff --git a/src/config/evm-network.ts b/src/config/evm-network.ts index f34725d..ad79bff 100644 --- a/src/config/evm-network.ts +++ b/src/config/evm-network.ts @@ -89,7 +89,7 @@ const evmNetworks: Record = { txConst: { blockTimeSec: 7, }, - contracts: { git3: "0x59ef6b2dbfE86CcAaD84E2d8e78177f528521Da9" }, + contracts: { factory: "0xF2CAd0c8997584D8DDe1c0726De0Fa9ECC3dDa04" }, }, 421613: { name: "Arbitrum - Goerli", @@ -114,7 +114,7 @@ const evmNetworks: Record = { rbfTimes: 5, boardcastTimes: 10, }, - contracts: { git3: "0x7Bb1038106fC6490195ec9906b29C81217ab090d" }, + contracts: { factory: "0x51bb7F23193b88696D25EAec7E3293a2C96e55Ee" }, }, } diff --git a/src/config/name-services.ts b/src/config/name-services.ts index 76867e2..7adb006 100644 --- a/src/config/name-services.ts +++ b/src/config/name-services.ts @@ -1,15 +1,15 @@ const ns: Record = { w3q: { chainId: 3334, - resolver: "0x3144d2EF1ac2fD651a250c9B5EfC095330ED9C05", + resolver: "0x372343Dd5274bb7B571F81d4FBA3bC0E28FeF3D9", }, arb: { chainId: 42170, - resolver: "0x3144d2EF1ac2fD651a250c9B5EfC095330ED9C05", + resolver: "0x372343Dd5274bb7B571F81d4FBA3bC0E28FeF3D9", }, arbg: { chainId: 421613, - resolver: "0x3144d2EF1ac2fD651a250c9B5EfC095330ED9C05", + resolver: "0x372343Dd5274bb7B571F81d4FBA3bC0E28FeF3D9", }, } diff --git a/src/git-remote-git3/git.ts b/src/git-remote-git3/git.ts index a4c30f5..003b55a 100644 --- a/src/git-remote-git3/git.ts +++ b/src/git-remote-git3/git.ts @@ -120,7 +120,7 @@ class Git { async download(sha: string): Promise { log("fetching...", sha) - let [status, data] = await this.storage.download(this.objectPath(sha)) + let [status, data] = await this.storage.download(sha) //this.objectPath(sha) if (status == Status.SUCCEED) { let computedSha = GitUtils.decodeObject(data) if (computedSha != sha) { @@ -146,7 +146,7 @@ class Git { pendings.push(this.putObject(obj)) } let resault = await this.storage.uploadCommit() - if(resault!= Status.SUCCEED){ + if (resault != Status.SUCCEED) { return `error ${dst} upload commit fail` } let resaults = await Promise.all(pendings) @@ -166,11 +166,7 @@ class Git { } } - async wirteRef( - newSha: string, - dst: string, - force: boolean - ): Promise { + async wirteRef(newSha: string, dst: string, force: boolean): Promise { let sha = this.refs.get(dst) if (sha) { if (!GitUtils.objectExists(sha)) { @@ -200,16 +196,16 @@ class Git { async putObject(sha: string): Promise { let data = GitUtils.encodeObject(sha) - let path = this.objectPath(sha) + let path = sha //this.objectPath(sha) let status = await this.storage.upload(path, data) return status } - objectPath(name: string): string { - const prefix = name.slice(0, 2) - const suffix = name.slice(2) - return join("objects", prefix, suffix) - } + // objectPath(name: string): string { + // const prefix = name.slice(0, 2) + // const suffix = name.slice(2) + // return join("objects", prefix, suffix) + // } async getRefs(forPush: boolean): Promise { let refs = await this.storage.listRefs() diff --git a/src/git3/index.ts b/src/git3/index.ts index 9ced383..37b712a 100644 --- a/src/git3/index.ts +++ b/src/git3/index.ts @@ -5,9 +5,12 @@ import bip39 from "bip39" import inquirer from "inquirer" import { importActions, generateActions } from "./actions.js" import network from "../config/evm-network.js" -import { getWallet, randomRPC } from "../common/wallet.js" +import { explorerTxUrl, getWallet, randomRPC, setupContract } from "../common/wallet.js" import { 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() program.name("git3").description("git3 mangement tool").version("0.1.0") @@ -20,8 +23,7 @@ program .action(() => { inquirer.prompt(generateActions).then((answers) => { const { keyType, name } = answers - const walletType = - keyType === "private key" ? "privateKey" : "mnemonic" + const walletType = keyType === "private key" ? "privateKey" : "mnemonic" const keyPath = process.env.HOME + "/.git3/keys" mkdirSync(keyPath, { recursive: true }) @@ -64,9 +66,7 @@ program if (params.raw) { console.log(`[${file}]`) - console.log( - ` ${content.split("\n")[0]} - ${content.split("\n")[1]}` - ) + console.log(` ${content.split("\n")[0]} - ${content.split("\n")[1]}`) console.log("\t") return } @@ -89,8 +89,7 @@ program .action(() => { inquirer.prompt(importActions).then((answers) => { const { keyType, key, name } = answers - const walletType = - keyType === "private key" ? "privateKey" : "mnemonic" + const walletType = keyType === "private key" ? "privateKey" : "mnemonic" const keyPath = process.env.HOME + "/.git3/keys" mkdirSync(keyPath, { recursive: true }) @@ -114,9 +113,7 @@ program const wallets = readdirSync(keyPath) if (wallets.length === 0) { - console.error( - "No wallet found, you can generate one with `git3 generate`" - ) + console.error("No wallet found, you can generate one with `git3 generate`") return } @@ -135,49 +132,108 @@ program }) }) -program +let create = program .command("create") + .description("create hub [is_permissionless] OR create repo ") + +create + .command("hub") + .argument("", "chain name or chain id") + .argument("[is_permissionless]", "true or false", false) + .description("create a new hub") + .action(async (chain, 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 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 CreateHubEvent = factory.interface.getEvent("CreateHub"); + console.log(explorerTxUrl(receipt.transactionHash, netConfig.explorers)) + + let events = receipt.logs + .map((log: any) => { + try { + return factory.interface.parseLog(log) + } catch (e) { + return null + } + }) + .filter((item: any) => item !== null && item.name === "CreateHub") + + console.log("hub address:", events[0].args.hub) + console.log("hub owner:", events[0].args.creator) + }) + +create + .command("repo") .argument("", "ex: git3.w3q/repo_name") .description("create a new repo") .action(async (uri) => { - if (!uri.startsWith("git3://")) { - uri = "git3://" + uri + const protocol = await parseGit3URI(uri, { ignoreProtocolHeader: true }) + + let isMember = await protocol.hub.membership(protocol.wallet.address) + if (!isMember) { + let hubName = protocol.ns + ? `${protocol.nsName}.${protocol.nsDomain}` + : protocol.hubAddress + console.error(`you are not a member of this hub: ${hubName}`) + let isPermissionless = await protocol.hub.permissionless() + if (isPermissionless) { + console.error( + `this hub is permissionless, you can join it with: git3 join ${hubName}` + ) + } else { + console.error( + `this hub is not permissionless, you can ask the hub owner to add you as a member` + ) + } + return } - const protocol = await parseGit3URI(uri) - let owner = await protocol.contract.repoNameToOwner( - Buffer.from(protocol.repoName) - ) + + let owner = await protocol.hub.repoOwner(Buffer.from(protocol.repoName)) if (owner != "0x0000000000000000000000000000000000000000") { console.error(`repo ${protocol.repoName} already exists`) return } - console.log( - `creating repo ${protocol.repoName} on ${protocol.netConfig.name}...` - ) - const txManager = new TxManager( - protocol.contract, - protocol.chainId, - protocol.netConfig.txConst - ) - let receipt = await txManager.SendCall("createRepo", [ - Buffer.from(protocol.repoName), - ]) - if ( - protocol.netConfig.explorers && - protocol.netConfig.explorers.length > 0 - ) { - console.log( - protocol.netConfig.explorers[0].url.replace(/\/+$/, "") + - "/tx/" + - receipt.transactionHash - ) - } else { - console.log(receipt.transactionHash) - } + + console.log(`creating repo ${protocol.repoName} on ${protocol.netConfig.name}...`) + const txManager = new TxManager(protocol.hub, protocol.chainId, protocol.netConfig.txConst) + let receipt = await txManager.SendCall("createRepo", [Buffer.from(protocol.repoName)]) + + console.log(explorerTxUrl(receipt.transactionHash, protocol.netConfig.explorers)) console.log(`repo ${protocol.repoName} created.`) }) +program + .command("join") + .argument("", "hub_name.NS or hub_address:chain_id") + .description("join a permissionless hub") + .action(async (hub) => { + let protocol = await parseGit3URI(hub, { ignoreProtocolHeader: true, skipRepoName: true }) + let isPermissionless = await protocol.hub.permissionless() + if (!isPermissionless) { + console.error(`hub ${protocol.hubAddress} is not permissionless`) + return + } + const txManager = new TxManager(protocol.hub, protocol.chainId, protocol.netConfig.txConst) + let receipt = await txManager.SendCall("permissionlessJoin", []) + console.log(explorerTxUrl(receipt.transactionHash, protocol.netConfig.explorers)) + }) + program .command("info") .argument("[wallet]", "wallet you want to get info", "default") @@ -191,9 +247,7 @@ program console.log(`address: ${address}`) for (let [_, net] of Object.entries(network)) { - const provider = new ethers.providers.JsonRpcProvider( - randomRPC(net.rpc) - ) + const provider = new ethers.providers.JsonRpcProvider(randomRPC(net.rpc)) const balance = provider.getBalance(address) balance.then((res) => { console.log( @@ -216,11 +270,7 @@ program uri = "git3://" + uri } const protocol = await parseGit3URI(uri, { skipRepoName: true }) - const txManager = new TxManager( - protocol.contract, - protocol.chainId, - protocol.netConfig.txConst - ) + const txManager = new TxManager(protocol.hub, protocol.chainId, protocol.netConfig.txConst) let nonce = await protocol.wallet.getTransactionCount() console.log(`current nonce: ${nonce}`) await txManager.clearPendingNonce(num) diff --git a/src/storage/ETHStorage.ts b/src/storage/ETHStorage.ts index 3070774..d9e01b3 100644 --- a/src/storage/ETHStorage.ts +++ b/src/storage/ETHStorage.ts @@ -11,7 +11,7 @@ export class ETHStorage implements Storage { constructor(protocol: Git3Protocol) { this.repoName = protocol.repoName - this.contract = protocol.contract + this.contract = protocol.hub this.wallet = protocol.wallet this.txManager = new TxManager(this.contract, protocol.chainId, protocol.netConfig.txConst) } @@ -20,7 +20,7 @@ export class ETHStorage implements Storage { } async repoRoles(): Promise { - let owner = await this.contract.repoNameToOwner(Buffer.from(this.repoName)) + let owner = await this.contract.repoOwner(Buffer.from(this.repoName)) if (owner === ethers.constants.AddressZero) return [] return [owner] } @@ -60,7 +60,7 @@ export class ETHStorage implements Storage { } async listRefs(): Promise { - const res: string[][] = await this.contract.listRefs(Buffer.from(this.repoName)) + const res: string[][] = await this.contract.listRepoRefs(Buffer.from(this.repoName)) let refs = res.map((i) => ({ ref: Buffer.from(i[1].slice(2), "hex") .toString("utf8") @@ -73,7 +73,7 @@ export class ETHStorage implements Storage { async setRef(path: string, sha: string): Promise { try { console.error(`=== setting ref ${path} ===`) - await this.txManager.SendCall("setRef", [ + await this.txManager.SendCall("setRepoRef", [ Buffer.from(this.repoName), Buffer.from(path), "0x" + sha, diff --git a/src/storage/SLIStorage.ts b/src/storage/SLIStorage.ts index 922951a..83af7b1 100644 --- a/src/storage/SLIStorage.ts +++ b/src/storage/SLIStorage.ts @@ -25,7 +25,7 @@ export class SLIStorage implements Storage { constructor(protocol: Git3Protocol) { this.repoName = protocol.repoName - this.contract = protocol.contract + this.contract = protocol.hub this.wallet = protocol.wallet this.txManager = new TxManager(this.contract, protocol.chainId, protocol.netConfig.txConst) this.auth = [ @@ -52,7 +52,7 @@ export class SLIStorage implements Storage { } async repoRoles(): Promise { - let owner = await this.contract.repoNameToOwner(Buffer.from(this.repoName)) + let owner = await this.contract.repoOwner(Buffer.from(this.repoName)) if (owner === ethers.constants.AddressZero) return [] return [owner] } @@ -102,7 +102,7 @@ export class SLIStorage implements Storage { try { console.error(`=== uploading file ${path} ===`) const cid = await this.storeIPFS(file) - console.error(`ipfs cid: ${cid}`) + // console.error(`ipfs cid: ${cid}`) this.batchQueue.push({ path, cid }) if (this.commitTimer) clearTimeout(this.commitTimer) @@ -177,7 +177,7 @@ export class SLIStorage implements Storage { } async listRefs(): Promise { - const res: string[][] = await this.contract.listRefs(Buffer.from(this.repoName)) + const res: string[][] = await this.contract.listRepoRefs(Buffer.from(this.repoName)) let refs = res.map((i) => ({ ref: Buffer.from(i[1].slice(2), "hex") .toString("utf8") @@ -190,7 +190,7 @@ export class SLIStorage implements Storage { async setRef(path: string, sha: string): Promise { try { console.error(`=== setting ref ${path} ===`) - await this.txManager.SendCall("setRef", [ + await this.txManager.SendCall("setRepoRef", [ Buffer.from(this.repoName), Buffer.from(path), "0x" + sha, diff --git a/yarn.lock b/yarn.lock index 96d6fee..050684e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -648,6 +648,11 @@ dependencies: "@types/node" "*" +"@types/url-parse@^1.4.8": + version "1.4.8" + resolved "https://registry.yarnpkg.com/@types/url-parse/-/url-parse-1.4.8.tgz#c3825047efbca1295b7f1646f38203d9145130d6" + integrity sha512-zqqcGKyNWgTLFBxmaexGUKQyWqeG7HjXj20EuQJSJWwXe54BjX0ihIo5cJB9yAQzH8dNugJ9GvkBYMjPXs/PJw== + acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" @@ -1776,6 +1781,11 @@ punycode@2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.0.tgz#5f863edc89b96db09074bad7947bf09056ca4e7d" integrity sha512-Yxz2kRwT90aPiWEMHVYnEf4+rhwF1tBmmZ4KepCP+Wkium9JxtWnUm1nqGwpiAHr/tnTSeHqr3wb++jgSkXjhA== +querystringify@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" + integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -1825,6 +1835,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + resolve@^1.22.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" @@ -2156,6 +2171,14 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== +url-parse@^1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" + integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"