diff --git a/src/config/abis.ts b/src/config/abis.ts new file mode 100644 index 0000000..8b1e741 --- /dev/null +++ b/src/config/abis.ts @@ -0,0 +1,3 @@ +export default { + "ETHStorage": '[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"uint256","name":"chunkId","type":"uint256"}],"name":"chunkStakeTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"bytes","name":"name","type":"bytes"}],"name":"countChunks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","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":"name","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"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOptimize","outputs":[{"internalType":"bool","name":"","type":"bool"}],"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 Git3.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":"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":"name","type":"bytes"},{"internalType":"bytes20","name":"refHash","type":"bytes20"}],"name":"setRef","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"bytes","name":"name","type":"bytes"}],"name":"size","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"repoName","type":"bytes"},{"internalType":"bytes","name":"path","type":"bytes"}],"name":"stakeTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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"}]' +} \ No newline at end of file diff --git a/src/config/evm-network.ts b/src/config/evm-network.ts new file mode 100644 index 0000000..cabdb6a --- /dev/null +++ b/src/config/evm-network.ts @@ -0,0 +1,43 @@ +// from https://chainid.network/chains.json + +const evmNetworks: Record = { + 1: { + "name": "ethereum", + "nativeCurrency": { + "name": "Ether", + "symbol": "ETH", + "decimals": 18 + }, + "rpc": ["https://rpc.flashbots.net", "https://singapore.rpc.blxrbdn.com", "https://rpc.ankr.com/eth"], + "explorers": [ + { + "name": "etherscan", + "url": "https://etherscan.io", + "standard": "EIP3091" + } + ], + "contracts": { "git3": "" } + }, + 3334: { + "name": "Web3Q Galileo", + "nativeCurrency": { + "name": "Web3Q", + "symbol": "W3Q", + "decimals": 18 + }, + "rpc": [ + "https://galileo.web3q.io:8545" + ], + "explorers": [ + { + "name": "w3q-galileo", + "url": "https://explorer.galileo.web3q.io", + "standard": "EIP3091" + } + ], + "contracts": { "git3": "0x0068bD3ec8D16402690C1Eddff06ACb913A209ef" } + } + +} + +export default evmNetworks \ No newline at end of file diff --git a/src/config/name-services.ts b/src/config/name-services.ts new file mode 100644 index 0000000..d35ddca --- /dev/null +++ b/src/config/name-services.ts @@ -0,0 +1,12 @@ +const ns: Record = { + "eth": { + "chainId": 1, + "resolver": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e" + }, + "w3q": { + "chainId": 3334, + "resolver": "0x076B3e04dd300De7db95Ba3F5db1eD31f3139aE0" + } +} + +export default ns; \ No newline at end of file diff --git a/src/contractABI/FlatDirectory.json b/src/contractABI/FlatDirectory.json deleted file mode 100644 index 25909f1..0000000 --- a/src/contractABI/FlatDirectory.json +++ /dev/null @@ -1,366 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "uint8", - "name": "slotLimit", - "type": "uint8" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "stateMutability": "nonpayable", - "type": "fallback" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "changeOwner", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "name", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "chunkId", - "type": "uint256" - } - ], - "name": "chunkSize", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "name", - "type": "bytes" - } - ], - "name": "countChunks", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "defaultFile", - "outputs": [ - { - "internalType": "bytes", - "name": "", - "type": "bytes" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "destruct", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "name", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "chunkId", - "type": "uint256" - } - ], - "name": "getChunkHash", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "isOptimize", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "name", - "type": "bytes" - } - ], - "name": "read", - "outputs": [ - { - "internalType": "bytes", - "name": "", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "name", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "chunkId", - "type": "uint256" - } - ], - "name": "readChunk", - "outputs": [ - { - "internalType": "bytes", - "name": "", - "type": "bytes" - }, - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "refund", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "name", - "type": "bytes" - } - ], - "name": "remove", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "name", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "chunkId", - "type": "uint256" - } - ], - "name": "removeChunk", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "resolveMode", - "outputs": [ - { - "internalType": "bytes32", - "name": "", - "type": "bytes32" - } - ], - "stateMutability": "pure", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "_defaultFile", - "type": "bytes" - } - ], - "name": "setDefault", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "name", - "type": "bytes" - } - ], - "name": "size", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "name", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "chunkId", - "type": "uint256" - } - ], - "name": "truncate", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "name", - "type": "bytes" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "write", - "outputs": [], - "stateMutability": "payable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "bytes", - "name": "name", - "type": "bytes" - }, - { - "internalType": "uint256", - "name": "chunkId", - "type": "uint256" - }, - { - "internalType": "bytes", - "name": "data", - "type": "bytes" - } - ], - "name": "writeChunk", - "outputs": [], - "stateMutability": "payable", - "type": "function" - } -] \ No newline at end of file diff --git a/src/git/git.ts b/src/git/git.ts index 771f817..ad755a9 100644 --- a/src/git/git.ts +++ b/src/git/git.ts @@ -12,6 +12,7 @@ class Git { pushed: Map = new Map() head: string | null + constructor(info: ApiBaseParams, storage: Storage) { this.gitdir = info.gitdir this.remoteName = info.remoteName @@ -22,9 +23,9 @@ class Git { this.head = null } - async do_list(forPush: boolean) { + async doList(forPush: boolean) { let outLines: string[] = [] - let refs = await this.get_refs(forPush) + let refs = await this.getRefs(forPush) for (let ref of refs) { if (ref.ref == "HEAD") { if (!forPush) outLines.push(`@${ref.sha} HEAD\n`) @@ -40,28 +41,31 @@ class Git { return outLines.join("") + "\n" } - async do_fetch(refs: { ref: string, oid: string }[]) { + async doFetch(refs: { ref: string, oid: string }[]) { for (let ref of refs) { await this.fetch(ref.oid) } return "\n\n" } - async do_push(refs: { + async doPush(refs: { src: string dst: string force: boolean }[]): Promise { + let outLines: string[] = [] // let remoteHead = null for (let ref of refs) { + if (!await this.storage.hasPermission(ref.dst)) { + return `error ${ref.dst} refusing to push to remote ${this.remoteUrl} (permission denied)` + "\n\n" + } if (ref.src == "") { if (this.refs.get("HEAD") == ref.dst) { return `error ${ref.dst} refusing to delete the current branch: ${ref.dst}` + "\n\n" } log("deleting ref", ref.dst) this.storage.removeRef(ref.dst) - this.refs.delete(ref.dst) this.pushed.delete(ref.dst) } else { @@ -131,7 +135,7 @@ class Git { let present = Array.from(this.refs.values()) present.push(...Array.from(this.pushed.values())) let objects = GitUtils.listObjects(src, present) - let pendings = [] + let pendings: Promise[] = [] for (let obj of objects) { pendings.push(this.putObject(obj)) } @@ -174,12 +178,13 @@ class Git { } - async putObject(sha: string) { + async putObject(sha: string): Promise { let data = GitUtils.encodeObject(sha) let path = this.objectPath(sha) - log("writing...", path, sha) + log("writing...", path) let status = await this.storage.upload(path, data) log("status", status) + return status } objectPath(name: string): string { @@ -188,7 +193,7 @@ class Git { return join("objects", prefix, suffix) } - async get_refs(forPush: boolean): Promise { + async getRefs(forPush: boolean): Promise { let refs = await this.storage.listRefs() for (let item of refs) { if (item.sha == "0000000000000000000000000000000000001ead") { diff --git a/src/index.ts b/src/index.ts index 8a9a6ba..6352c57 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,54 +2,69 @@ import GitRemoteHelper from './git/git-remote-helper' import { ApiBaseParams } from './git/git-remote-helper' import Git from './git/git' -import { log } from './git/log' +// import { log } from './git/log' import { ETHStorage } from './storage/ETHStorage' +import nameServices from './config/name-services' + let git: Git; GitRemoteHelper({ env: process.env, stdin: process.stdin, stdout: process.stdout, api: { - /** - * This will always be invoked when the remote helper is invoked - */ init: async (p: ApiBaseParams) => { - git = new Git(p, new ETHStorage(p.remoteUrl)) + const url = new URL(p.remoteUrl) + let repoName + let git3Address + let chainId = url.port ? parseInt(url.port) : 3334 + if (url.hostname.indexOf(".") < 0) { + if (url.hostname.startsWith("0x")) { + git3Address = url.hostname + repoName = url.pathname.slice(1) + } else { + // use Default git3Address + git3Address = null + repoName = url.hostname + } + } + else { + let nsSuffix = url.hostname.split(".")[1] // Todo: support sub domain + let ns = nameServices[nsSuffix] + if (!ns) throw new Error("invalid name service") + // Todo: resolve name service + git3Address = null // ns parse address + + chainId = chainId || ns.chainId + repoName = url.pathname.slice(1) + } + let sender = url.username || null + let storage = new ETHStorage(repoName, chainId, { git3Address, sender }) + git = new Git(p, storage) return }, - /** - * This needs to return a list of git refs. - */ list: async (p: { gitdir: string remoteName: string remoteUrl: string forPush: boolean }) => { - log('list', p) - - let out = await git.do_list(p.forPush) - log("list out:\n", out) + // log('list', p) + let out = await git.doList(p.forPush) + // log("list out:\n", out) return out }, - /** - * This should put the requested objects into the `.git` - */ handleFetch: async (p: { gitdir: string remoteName: string remoteUrl: string refs: { ref: string, oid: string }[] }) => { - log("fetch", p) - let out = await git.do_fetch(p.refs) - log("fetch out:\n", out) + // log("fetch", p) + let out = await git.doFetch(p.refs) + // log("fetch out:\n", out) return out }, - /** - * This should copy objects from `.git` - */ handlePush: async (p: { gitdir: string remoteName: string @@ -60,9 +75,9 @@ GitRemoteHelper({ force: boolean }[] }) => { - log("push", p) - let out = await git.do_push(p.refs) - log("push out:\n", out) + // log("push", p) + let out = await git.doPush(p.refs) + // log("push out:\n", out) return out }, }, diff --git a/src/storage/ETHStorage.ts b/src/storage/ETHStorage.ts index 249f662..9621e2d 100644 --- a/src/storage/ETHStorage.ts +++ b/src/storage/ETHStorage.ts @@ -2,35 +2,54 @@ import { Ref, Status, Storage } from "./storage" import { getWallet } from "../wallet/index" import { ethers, Signer } from "ethers" import { NonceManager } from "@ethersproject/experimental" - -const abi = '[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"bytes","name":"name","type":"bytes"}],"name":"countChunks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"}],"name":"delRef","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"path","type":"bytes"}],"name":"download","outputs":[{"internalType":"bytes","name":"","type":"bytes"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"listRefs","outputs":[{"components":[{"internalType":"bytes20","name":"hash","type":"bytes20"},{"internalType":"string","name":"name","type":"string"}],"internalType":"struct Git3.refData[]","name":"list","type":"tuple[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"nameToRefInfo","outputs":[{"internalType":"bytes20","name":"hash","type":"bytes20"},{"internalType":"uint96","name":"index","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"refs","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"val","type":"uint256"}],"name":"refund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"refund1","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"path","type":"bytes"}],"name":"remove","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"bytes20","name":"refHash","type":"bytes20"}],"name":"setRef","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"name","type":"bytes"}],"name":"size","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"storageManager","outputs":[{"internalType":"contract IFileOperator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"path","type":"bytes"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upload","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"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"}]' +import abis from "../config/abis" +import network from "../config/evm-network" export class ETHStorage implements Storage { - repoURI: string + repoName: string wallet: Signer contract: ethers.Contract provider: ethers.providers.JsonRpcProvider - constructor(repoURI: string) { - this.repoURI = repoURI - this.wallet = getWallet() - this.provider = new ethers.providers.JsonRpcProvider('https://galileo.web3q.io:8545') + constructor(repoName: string, chainId: number, options: { git3Address: string | null, sender: string | null }) { + let net = network[chainId] + if (!net) throw new Error("chainId not supported") + + this.repoName = repoName + this.wallet = getWallet(options.sender) + + let rpc = net.rpc[Math.floor(Math.random() * net.rpc.length)] //random get rpc + + this.provider = new ethers.providers.JsonRpcProvider(rpc) this.wallet = this.wallet.connect(this.provider) this.wallet = new NonceManager(this.wallet) - this.contract = new ethers.Contract('0x01d2681e3F4dED1750359F066a42a768Adaa142F', abi, this.wallet) + + let repoAddress = options.git3Address || net.contracts.git3 + this.contract = new ethers.Contract(repoAddress, abis.ETHStorage, this.wallet) + } + + async repoRoles(): Promise { + let owner = await this.contract.repoNameToOwner(Buffer.from(this.repoName)) + if (owner === ethers.constants.AddressZero) return [] + return [owner] + } + + async hasPermission(ref: string): Promise { + let member = await this.repoRoles() + return member.indexOf(await this.wallet.getAddress()) >= 0 } async download(path: string): Promise<[Status, Buffer]> { - const res = await this.contract.download(Buffer.from(path)) + const res = await this.contract.download(Buffer.from(this.repoName), Buffer.from(path)) const buffer = Buffer.from(res[0].slice(2), 'hex') console.error(`=== download file ${path} result ===`) - console.error(buffer.toString('utf-8')) + // console.error(buffer.toString('utf-8')) return [Status.SUCCEED, buffer] } async upload(path: string, file: Buffer): Promise { - const uploadResult = await this.contract.upload(Buffer.from(path), file) - console.error(`=== upload file ${path} result ===`) - console.error(uploadResult) + const uploadResult = await this.contract.upload(Buffer.from(this.repoName), Buffer.from(path), file) + console.error(`=== upload file ${path} ===`) + console.error("upload done:", uploadResult.hash) return Status.SUCCEED } @@ -39,20 +58,21 @@ export class ETHStorage implements Storage { } async listRefs(): Promise { - const res: string[][] = await this.contract.listRefs() + const res: string[][] = await this.contract.listRefs(Buffer.from(this.repoName)) let refs = res.map(i => ({ - ref: i[1], + ref: Buffer.from(i[1].slice(2), "hex").toString("utf8").slice(this.repoName.length + 1), sha: i[0].slice(2) })) return refs } async setRef(path: string, sha: string): Promise { - await this.contract.setRef(path, '0x' + sha) + await this.contract.setRef(Buffer.from(this.repoName), Buffer.from(path), '0x' + sha) return Status.SUCCEED } - removeRef(path: string): Promise { - throw new Error("Method not implemented.") + async removeRef(path: string): Promise { + await this.contract.delRef(Buffer.from(this.repoName), Buffer.from(path)) + return Status.SUCCEED } } diff --git a/src/storage/MockStorage.ts b/src/storage/MockStorage.ts index a5106e6..8de9215 100644 --- a/src/storage/MockStorage.ts +++ b/src/storage/MockStorage.ts @@ -8,10 +8,14 @@ const log = console.error log("mock path", mockPath) export class MockStorage implements Storage { - repoURI: string + repoName: string - constructor(repoURI: string) { - this.repoURI = repoURI + constructor() { + this.repoName = "mock" + } + + async hasPermission(ref: string): Promise { + return true } async listRefs(): Promise { @@ -47,7 +51,7 @@ export class MockStorage implements Storage { await fs.writeFile(stPath, JSON.stringify(dict)) return Status.SUCCEED } - + async removeRef(path: string): Promise { let stPath = join(mockPath, "refs.json") let refsJson = await fs.readFile(stPath) diff --git a/src/storage/storage.ts b/src/storage/storage.ts index fa58993..35ea007 100644 --- a/src/storage/storage.ts +++ b/src/storage/storage.ts @@ -12,8 +12,9 @@ export type Ref = { } export interface Storage { - repoURI: string + repoName: string + hasPermission(ref: string): Promise download(path: string): Promise<[Status, Buffer]> upload(path: string, file: Buffer): Promise remove(path: string): Promise diff --git a/src/wallet/index.ts b/src/wallet/index.ts index 6268634..27b38e9 100644 --- a/src/wallet/index.ts +++ b/src/wallet/index.ts @@ -1,8 +1,10 @@ import { mkdirSync, readFileSync } from "fs" import { ethers } from 'ethers' -export function getWallet(): ethers.Wallet { +export function getWallet(sender: string | null): ethers.Wallet { + // Todo: according sender address to select wallet, if sender==null then use default wallet const wallet = 'default' + const keyPath = process.env.HOME + "/.git3/keys" mkdirSync(keyPath, { recursive: true }) diff --git a/tsconfig.json b/tsconfig.json index f01dd05..c91051b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -41,7 +41,7 @@ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ /* Module Resolution Options */ "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "baseUrl": "src", /* Base directory to resolve non-absolute module names. */ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ "typeRoots": [