Skip to content

Commit

Permalink
Delivery service cli (#679)
Browse files Browse the repository at this point in the history
* make setup a namespace and billboardDs a separate task

* replace logInfo with console.log

* add cli job to setup raw ds config

* add readme and refine start command

* fix typo in Readme
  • Loading branch information
AlexNi245 authored Dec 4, 2023
1 parent 1a70261 commit a898eb2
Show file tree
Hide file tree
Showing 14 changed files with 301 additions and 39 deletions.
19 changes: 19 additions & 0 deletions docker/ds-minimal/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
version: "3.1"
services:
backend:
image: dm3org/dm3-backend:v0.2.1
restart: always
depends_on:
- db
environment:
REDIS_URL: redis://db:6379
SIGNING_PUBLIC_KEY: ${SIGNING_PUBLIC_KEY}
SIGNING_PRIVATE_KEY: ${SIGNING_PRIVATE_KEY}
ENCRYPTION_PUBLIC_KEY: ${ENCRYPTION_PUBLIC_KEY}
ENCRYPTION_PRIVATE_KEY: ${ENCRYPTION_PRIVATE_KEY}
RPC: ${RPC}
PORT: 8081
LOG_LEVEL: 'debug'
db:
image: redis
restart: always
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
]
},
"scripts": {
"cli":"yarn workspace dm3-cli run dm3",
"docker:up": "cd packages/backend && docker-compose up -d",
"build": " yarn workspaces foreach -pt run build",
"start": "yarn workspace dm3-backend start",
Expand Down
41 changes: 37 additions & 4 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
# DM3 CLI

## SETUP
## SETUP BillboardDs


### Overview

The CLI command "setup" is used to configure a DM3 delivery service for your domain. It initiates all the necessary on-chain transactions and provides an environment configuration that you can use for the delivery service (DS).
The CLI command "setup billboardDs" is used to configure a DM3 delivery service for your domain. It initiates all the necessary on-chain transactions and provides an environment configuration that you can use for the delivery service (DS).
It also configures subdomains for .addr and ds, enabling them to resolve requests via the provided gateway.

### Usage

`yarn cli setup --rpc <RPC_URL> --pk <PRIVATE_KEY> --domain <ENS_DOMAIN> --gateway <GATEWAY_URL> --deliveryService <DELIVERY_SERVICE_URL> --profilePk <PROFILE_PRIVATE_KEY> --ensRegistry <ENS_REGISTRY_ADDRESS> --ensResolver <ENS_RESOLVER_ADDRESS> --erc3668Resolver <ERC3668_RESOLVER_ADDRESS>`
`setup billboardDs --rpc <RPC_URL> --pk <PRIVATE_KEY> --domain <ENS_DOMAIN> --gateway <GATEWAY_URL> --deliveryService <DELIVERY_SERVICE_URL> --profilePk <PROFILE_PRIVATE_KEY> --ensRegistry <ENS_REGISTRY_ADDRESS> --ensResolver <ENS_RESOLVER_ADDRESS> --erc3668Resolver <ERC3668_RESOLVER_ADDRESS>`

### OPTIONS

Expand All @@ -24,7 +26,37 @@ The CLI command "setup" is used to configure a DM3 delivery service for your dom

### Example

`setup --rpc http://127.0.0.1:8545 --pk 0x123456789abcdef --domain alice.eth --gateway https://gateway.io/ --deliveryService https://ds.io/ --profilePk 0x987654321fedcba --ensRegistry 0xabcdef123456789 --ensResolver 0xfedcba987654321 --erc3668Resolver 0x123456789abcdef0`
`setup billboardDs --rpc http://127.0.0.1:8545 --pk 0x123456789abcdef --domain alice.eth --gateway https://gateway.io/ --deliveryService https://ds.io/ --profilePk 0x987654321fedcba --ensRegistry 0xabcdef123456789 --ensResolver 0xfedcba987654321 --erc3668Resolver 0x123456789abcdef0`


## Setup Offchain DS

### Overview

The CLI command "setup onchainDs" can be used to configure the minimal viable version of a Delivery Service (DS). It creates a Delivery Service Profile on-chain and prints the environment variables needed to run your own DS.
To set a DS up run the following steps

1. Run the command below to create the delivery service profile on-chain.
2. Copy the Docker Compose file to your project using the command: `cp docker/ds-minimal/docker-compose.yml $YOUR_PROJECT_PATH`.
3. Create an .env file at the same directory and copy the values created at step one into that file
4. Start the Delivery Service with `docker compose up`

### Usage

`setup offchainDS --rpc <RPC_URL> --pk <PRIVATE_KEY> --domain <ENS_DOMAIN> --deliveryService <DELIVERY_SERVICE_URL> --profilePk <PROFILE_PRIVATE_KEY> --ensResolver <ENS_RESOLVER_ADDRESS> `

### OPTIONS

- --rpc <RPC_URL>: The Ethereum RPC URL to connect to.
- --pk <PRIVATE_KEY>: The private key of the account owning the domain. That account will be used to execute the tx and has to be funded accordingly.
- --domain <ENS_DOMAIN>: The ENS (Ethereum Name Service) domain associated with dm3. It has to be owned by the account used as Private Key.
- --deliveryService <DELIVERY_SERVICE_URL>: The URL of the delivery service.
- --profilePk <PROFILE_PRIVATE_KEY>: The private key used as a seed to create the delivery service. When omitted, a random private key will be created.
- --ensResolver <ENS_RESOLVER_ADDRESS>: The address of the ENS public resolver contract.

### Example

`setup offchainDS --rpc http://127.0.0.1:8545 --pk 0x123456789abcdef --domain alice.eth --deliveryService https://ds.io/ --profilePk 0x987654321fedcba --ensResolver 0xfedcba987654321 `


## Profile
Expand All @@ -36,3 +68,4 @@ The CLI command "setup" is used to create a dm3 user profile based on an existin
`yarn cli profile --deliveryService foo.eth --profilePk 0x123`
- --deliveryService <DELIVERY_SERVICE_URL>: The URL of the delivery service.
- --profilePk <PROFILE_PK>: Optionally, if provided, the profile will be created based on that key. Otherwise, a random key will be generated.

160 changes: 148 additions & 12 deletions packages/cli/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,23 +111,23 @@ describe('cli', () => {

const cli = (argv = ''): any =>
new Promise((resolve, reject) => {
const subprocess = execa.command(`yarn start ${argv}`);
const subprocess = execa.command(`yarn run ${argv}`);
subprocess.stdout!.pipe(process.stdout);
subprocess.stderr!.pipe(process.stderr);
Promise.resolve(subprocess).then(resolve).catch(resolve);
});

describe('setup', () => {
describe('setup billboardDs', () => {
describe('sanitize input', () => {
it('reverts for unknown input', async () => {
const res = await cli('setup --efeh');
const res = await cli('dm3 setup billboardDs --efeh');
expect(res.stderr).to.equal("error: unknown option '--efeh'");
});

it('reverts if rpc url is undefined', async () => {
const wallet = ethers.Wallet.createRandom();
const res = await cli(
`setup --pk ${wallet.privateKey} --domain test.eth`,
`dm3 setup billboardDs --pk ${wallet.privateKey} --domain test.eth`,
);
expect(res.stderr).to.equal(
'error: option --rpc <rpc> argument missing',
Expand All @@ -136,15 +136,15 @@ describe('cli', () => {

it('reverts if privateKey is undefined', async () => {
const res = await cli(
'setup --rpc www.rpc.io --domain test.eth',
'dm3 setup billboardDs --rpc www.rpc.io --domain test.eth',
);
expect(res.stderr).to.equal(
'error: option --pk <pk> argument missing',
);
});
it('reverts if privateKey is invalid', async () => {
const res = await cli(
'setup --rpc www.rpc.io --domain test.eth --pk 123',
'dm3 setup billboardDs --rpc www.rpc.io --domain test.eth --pk 123',
);
expect(res.stderr).to.equal(
'error: option --pk <pk> argument invalid',
Expand All @@ -153,7 +153,7 @@ describe('cli', () => {
it('reverts if domain is undefined', async () => {
const wallet = ethers.Wallet.createRandom();
const res = await cli(
`setup --rpc www.rpc.io --pk ${wallet.privateKey}`,
`dm3 setup billboardDs --rpc www.rpc.io --pk ${wallet.privateKey}`,
);
expect(res.stderr).to.equal(
'error: option --domain <domain> argument missing',
Expand All @@ -162,19 +162,19 @@ describe('cli', () => {
it('reverts if gateway url is undefined', async () => {
const wallet = ethers.Wallet.createRandom();
const res = await cli(
`setup --rpc www.rpc.io --pk ${wallet.privateKey} --domain test.eth`,
`dm3 setup billboardDs --rpc www.rpc.io --pk ${wallet.privateKey} --domain test.eth`,
);
expect(res.stderr).to.equal(
'error: option --gateway <gateway> argument missing',
);
});
});
describe('setupAll', () => {
describe('setup billboardDsAll', () => {
it('test all', async () => {
const owner = ethers.Wallet.createRandom();

const res = await cli(
`setup
`dm3 setup billboardDs
--rpc http://127.0.0.1:8545
--pk ${alice.privateKey}
--domain alice.eth
Expand Down Expand Up @@ -234,7 +234,7 @@ describe('cli', () => {
});
it('test all with random profile wallet', async () => {
const res = await cli(
`setup
`dm3 setup billboardDs
--rpc http://127.0.0.1:8545
--pk ${alice.privateKey}
--domain alice.eth
Expand Down Expand Up @@ -298,7 +298,7 @@ describe('cli', () => {
);

const res = await cli(
`setup
`dm3 setup billboardDs
--rpc http://127.0.0.1:8545
--pk ${underfundedWallet.privateKey}
--domain alice.eth
Expand All @@ -320,4 +320,140 @@ describe('cli', () => {
});
});
});
describe('setup onChain', () => {
describe('sanitize input', () => {
it('reverts for unknown input', async () => {
const res = await cli('dm3 setup onchainDs --efeh');
expect(res.stderr).to.equal("error: unknown option '--efeh'");
});

it('reverts if rpc url is undefined', async () => {
const wallet = ethers.Wallet.createRandom();
const res = await cli(
`dm3 setup onchainDs --pk ${wallet.privateKey} --domain test.eth`,
);
expect(res.stderr).to.equal(
'error: option --rpc <rpc> argument missing',
);
});

it('reverts if privateKey is undefined', async () => {
const res = await cli(
'dm3 setup onchainDs --rpc www.rpc.io --domain test.eth',
);
expect(res.stderr).to.equal(
'error: option --pk <pk> argument missing',
);
});
it('reverts if privateKey is invalid', async () => {
const res = await cli(
'dm3 setup onchainDs --rpc www.rpc.io --domain test.eth --pk 123',
);
expect(res.stderr).to.equal(
'error: option --pk <pk> argument invalid',
);
});
it('reverts if domain is undefined', async () => {
const wallet = ethers.Wallet.createRandom();
const res = await cli(
`dm3 setup onchainDs --rpc www.rpc.io --pk ${wallet.privateKey}`,
);
expect(res.stderr).to.equal(
'error: option --domain <domain> argument missing',
);
});
});
describe('setup onchainDsAll', () => {
it('test all', async () => {
const owner = ethers.Wallet.createRandom();

const res = await cli(
`dm3 setup onchainDs
--rpc http://127.0.0.1:8545
--pk ${alice.privateKey}
--domain alice.eth
--deliveryService https://ds.io/
--profilePk ${owner.privateKey}
--ensResolver ${publicResolver.address}
`,
);

const profile = await publicResolver.text(
ethers.utils.namehash('alice.eth'),
'network.dm3.deliveryService',
);
expect(JSON.parse(profile).url).to.equal('https://ds.io/');

const encryptionKeyPair = await createKeyPair(
await createStorageKey(owner.privateKey),
);
const signingKeyPair = await createSigningKeyPair(
await createStorageKey(owner.privateKey),
);

expect(JSON.parse(profile).publicEncryptionKey).to.equal(
encryptionKeyPair.publicKey,
);
expect(JSON.parse(profile).publicSigningKey).to.equal(
signingKeyPair.publicKey,
);
});
it('test all with random profile wallet', async () => {
const res = await cli(
`dm3 setup onchainDs
--rpc http://127.0.0.1:8545
--pk ${alice.privateKey}
--domain alice.eth
--deliveryService https://ds.io/
--ensResolver ${publicResolver.address}
`,
);

const profile = await publicResolver.text(
ethers.utils.namehash('alice.eth'),
'network.dm3.deliveryService',
);
expect(JSON.parse(profile).url).to.equal('https://ds.io/');

expect(JSON.parse(profile).publicEncryptionKey).to.not.be
.undefined;
expect(JSON.parse(profile).publicSigningKey).to.not.be
.undefined;
});
it('rejects with underfunded balance', async () => {
const provider = new ethers.providers.JsonRpcProvider(
'http://127.0.0.1:8545/',
);

const underfundedWallet = ethers.Wallet.createRandom();
//Send a little bit of ETH to the wallet. Although its to little to pay for the transactions
await owner.connect(provider).sendTransaction({
to: underfundedWallet.address,
value: ethers.utils.parseEther('0.0001'),
});
const balanceBefore = await provider.getBalance(
underfundedWallet.address,
);

const res = await cli(
`dm3 setup onchainDs
--rpc http://127.0.0.1:8545
--pk ${underfundedWallet.privateKey}
--domain alice.eth
--deliveryService https://ds.io/
--ensResolver ${publicResolver.address}
`,
);

const balanceAfter = await provider.getBalance(
underfundedWallet.address,
);

expect(balanceAfter._hex).to.equal(balanceBefore._hex);
expect(res.stderr).to.include(
'has insufficient funds to send 1 transactions with total cost of',
);
});
});
});
});
Loading

0 comments on commit a898eb2

Please sign in to comment.