Skip to content

Commit

Permalink
feat(solo): separate hot helper address from cold fees and egress
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelfig committed Jul 14, 2021
1 parent 2a3ff01 commit 20cdfa8
Show file tree
Hide file tree
Showing 2 changed files with 165 additions and 52 deletions.
45 changes: 42 additions & 3 deletions packages/cosmic-swingset/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,21 @@ scenario2-reset-client:
$(AG_SOLO) init t1/$(BASE_PORT) --webport=$(BASE_PORT)
$(MAKE) set-local-gci-ingress

scenario2-client-owner: t1/$(BASE_PORT)/owner
$(AGCH) --home=t1/$(BASE_PORT)/owner tx authz grant $$(cat t1/$(BASE_PORT)/ag-cosmos-helper-address) \
generic --msg-type=/agoric.swingset.MsgDeliverInbound --from=solo-owner--keyring-backend=test \
--gas=auto --gas-adjustment=1.2 --broadcast-mode=block --yes --chain-id=$(CHAIN_ID)


scenario2-fee-account: t1/$(BASE_PORT)/owner
$(AGCH) --home=t1/$(BASE_PORT)/owner keys --keyring-backend=test show -a owner > t1/$(BASE_PORT)/cosmos-fee-account

scenario2-client-account: t1/$(BASE_PORT)/owner
$(AGCH) --home=t1/$(BASE_PORT)/owner keys --keyring-backend=test show -a owner > t1/$(BASE_PORT)/cosmos-client-account

t1/$(BASE_PORT)/owner:
$(AGCH) --home=t1/$(BASE_PORT)/owner keys --keyring-backend=test add owner

# Provision and start a client.
scenario2-run-client: t1-provision-one-with-powers t1-start-ag-solo

Expand All @@ -106,17 +121,22 @@ wait-for-cosmos:
@echo ' done!'

t1-provision-one-with-powers: wait-for-cosmos
addr=$$(cat t1/$(BASE_PORT)/ag-cosmos-helper-address); \
@addrfile=t1/$(BASE_PORT)/cosmos-client-account; \
test -f $$addrfile || addrfile=t1/$(BASE_PORT)/ag-cosmos-helper-address; \
addr=$$(cat $$addrfile); \
$(AGCH) --home=t1/bootstrap query swingset egress $$addr --chain-id=$(CHAIN_ID) || \
{ $(AGCH) --home=t1/bootstrap tx bank send --keyring-backend=test --from=bootstrap \
--gas=auto --gas-adjustment=1.2 --broadcast-mode=block --yes --chain-id=$(CHAIN_ID) \
bootstrap $$addr $(SOLO_COINS) && \
$(AGCH) --home=t1/bootstrap tx swingset provision-one --keyring-backend=test --from=bootstrap \
--gas=auto --gas-adjustment=1.2 --broadcast-mode=block --yes --chain-id=$(CHAIN_ID) \
t1/$(BASE_PORT) $$addr $(AGORIC_POWERS) | tee /dev/stderr | grep -q '"code":0'; }


t1-provision-one: wait-for-cosmos
addr=$$(cat t1/$(BASE_PORT)/ag-cosmos-helper-address); \
@addrfile=t1/$(BASE_PORT)/cosmos-client-account; \
test -f $$addrfile || addrfile=t1/$(BASE_PORT)/ag-cosmos-helper-address; \
addr=$$(cat $$addrfile); \
$(AGCH) --home=t1/bootstrap query swingset egress $$addr --chain-id=$(CHAIN_ID) || \
{ $(AGCH) --home=t1/bootstrap tx bank send --keyring-backend=test --from=bootstrap \
--gas=auto --gas-adjustment=1.2 --broadcast-mode=block --yes --chain-id=$(CHAIN_ID) \
Expand All @@ -125,8 +145,27 @@ t1-provision-one: wait-for-cosmos
--gas=auto --gas-adjustment=1.2 --broadcast-mode=block --yes --chain-id=$(CHAIN_ID) \
t1/$(BASE_PORT) $$addr | tee /dev/stderr | grep -q '"code":0'; }

t1/$(BASE_PORT)/cosmos-client-account.setup:
test ! -f t1/$(BASE_PORT)/cosmos-client-account || \
$(AGCH) --home=t1/$(BASE_PORT)/owner --keyring-backend=test tx authz grant \
--gas=auto --gas-adjustment=1.2 --broadcast-mode=block --yes --chain-id=$(CHAIN_ID) --from=owner \
$$(cat t1/$(BASE_PORT)/ag-cosmos-helper-address) generic --msg-type=/agoric.swingset.MsgDeliverInbound
date > $@

t1/$(BASE_PORT)/cosmos-fee-account.setup:
test ! -f t1/$(BASE_PORT)/cosmos-fee-account || \
$(AGCH) --home=t1/$(BASE_PORT)/owner --keyring-backend=test tx feegrant grant \
--gas=auto --gas-adjustment=1.2 --broadcast-mode=block --yes --chain-id=$(CHAIN_ID) --from=owner \
--period=5 --period-limit=200urun $$(cat t1/$(BASE_PORT)/cosmos-fee-account) $$(cat t1/$(BASE_PORT)/ag-cosmos-helper-address)
date > $@

# Actually start the ag-solo.
t1-start-ag-solo:
t1-start-ag-solo: t1/$(BASE_PORT)/cosmos-client-account.setup t1/$(BASE_PORT)/cosmos-fee-account.setup
addr=$$(cat t1/$(BASE_PORT)/ag-cosmos-helper-address); \
$(AGCH) query auth account $$addr >/dev/null 2>&1 || \
$(AGCH) --home=t1/bootstrap tx bank send --keyring-backend=test --from=bootstrap \
--gas=auto --gas-adjustment=1.2 --broadcast-mode=block --yes --chain-id=$(CHAIN_ID) \
bootstrap $$addr 1urun
cd t1/$(BASE_PORT) && $(AG_SOLO) start

# scenario3 is a single JS process without any Golang. However,
Expand Down
172 changes: 123 additions & 49 deletions packages/solo/src/chain-cosmos-sdk.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ const log = anylogger('chain-cosmos-sdk');

const HELPER = 'ag-cosmos-helper';
const FAUCET_ADDRESS =
'#faucet channel on Discord (https://agoric.com/discord)';
'the appropriate faucet channel on Discord (https://agoric.com/discord)';

const adviseEgress = myAddr =>
const adviseEgress = egressAddr =>
`\
Send:
!faucet client ${myAddr}
!faucet client ${egressAddr}
to ${FAUCET_ADDRESS}`;

Expand All @@ -43,11 +43,43 @@ const CANCEL_USE_DEFAULT = {
},
};

const makeTempFile = async (prefix, contents) => {
const tmpInfo = await new Promise((resolve, reject) => {
tempOpen({ prefix }, (err, info) => {
if (err) {
return reject(err);
}
return resolve(info);
});
});

try {
await new Promise((resolve, reject) => {
fs.write(tmpInfo.fd, contents, err => {
if (err) {
return reject(err);
}
return resolve();
});
});
} finally {
await new Promise((resolve, reject) => {
fs.close(tmpInfo.fd, e => {
if (e) {
return reject(e);
}
return resolve();
});
});
}
return tmpInfo;
};

export async function connectToChain(
basedir,
GCI,
rpcAddresses,
myAddr,
helperAddr,
inbound,
chainID,
) {
Expand Down Expand Up @@ -84,6 +116,38 @@ export async function connectToChain(

const helperDir = path.join(basedir, 'ag-cosmos-helper-statedir');

const readOrDefault = (file, dflt) =>
fs.promises
.readFile(file, { encoding: 'utf-8' })
.catch(e => {
if (e.code === 'ENOENT') {
return dflt;
}
throw e;
})
.then(str => str.trim());

// The helper account may only have the authority to send messages on behalf
// of the client, which has been set up by the client with something like:
//
// ag-cosmos-helper tx authz grant $(cat ag-cosmos-helper-address) \
// generic --msg-type=/agoric.swingset.MsgDeliverInbound \
// --from=$(cat cosmos-client-account)
const clientAddr = await readOrDefault(
path.join(basedir, 'cosmos-client-account'),
helperAddr,
);

// The helper address may not have a token balance, and instead uses a
// separate fee account, set up with something like:
//
// ag-cosmos-helper tx feegrant grant --period=5 --period-limit=200000urun \
// $(cat cosmos-fee-account) $(cat ag-cosmos-helper-address)
const feeAccountAddr = await readOrDefault(
path.join(basedir, 'cosmos-fee-account'),
'',
);

const queued = {};

async function retryRpcAddr(tryOnce) {
Expand Down Expand Up @@ -246,7 +310,7 @@ export async function connectToChain(
queuedHelper(
'getMailbox',
1, // Only one helper running at a time.
['query', 'swingset', 'mailbox', myAddr, '-ojson'],
['query', 'swingset', 'mailbox', clientAddr, '-ojson'],
// eslint-disable-next-line consistent-return
ret => {
const { stdout, stderr } = ret;
Expand All @@ -270,7 +334,7 @@ export async function connectToChain(

// Validate that our chain egress exists.
await retryRpcAddr(async rpcAddr => {
const args = ['query', 'swingset', 'egress', myAddr];
const args = ['query', 'swingset', 'egress', clientAddr];
const fullArgs = [
...args,
`--chain-id=${chainID}`,
Expand All @@ -291,7 +355,9 @@ export async function connectToChain(
if (!r.stdout) {
console.error(`\
=============
${chainID} chain does not yet know of address ${myAddr}${adviseEgress(myAddr)}
${chainID} chain does not yet know of address ${clientAddr}${adviseEgress(
clientAddr,
)}
=============
`);
return undefined;
Expand Down Expand Up @@ -432,57 +498,65 @@ ${chainID} chain does not yet know of address ${myAddr}${adviseEgress(myAddr)}
log(
`delivering to chain (trips=${totalDeliveries})`,
GCI,
messages[0],
messages[1],
messages,
ack,
);

tmpInfo = await new Promise((resolve, reject) => {
tempOpen({ prefix: 'ag-solo-cosmos-deliver.' }, (err, info) => {
if (err) {
return reject(err);
}
return resolve(info);
});
});

try {
await new Promise((resolve, reject) => {
fs.write(
tmpInfo.fd,
// TODO: remove this JSON.stringify([currentMessages, currentAck]): change
// 'deliverMailboxReq' to have more structure than a single string, and
// have the CLI handle variable args better
JSON.stringify([messages, ack]),
err => {
if (err) {
return reject(err);
}
return resolve();
},
);
});
} finally {
await new Promise((resolve, reject) => {
fs.close(tmpInfo.fd, e => {
if (e) {
return reject(e);
// TODO: remove this JSON.stringify([currentMessages, currentAck]): change
// 'deliverMailboxReq' to have more structure than a single string, and
// have the CLI handle variable args better
tmpInfo = await makeTempFile(
'ag-solo-cosmos-deliver.',
JSON.stringify([messages, ack]),
);

// Deliver message over file, as it could be big.
let args = ['tx', 'swingset', 'deliver', `@${tmpInfo.path}`];
if (clientAddr !== helperAddr) {
const genDone = makePromiseKit();
execFile(
HELPER,
[
'tx',
'swingset',
'deliver',
'--generate-only',
`--chain-id=${chainID}`,
`--from=${clientAddr}`,
`@${tmpInfo.path}`,
],
{ maxBuffer: MAX_BUFFER_SIZE },
(error, stdout, stderr) => {
if (error) {
return genDone.reject(error);
}
return resolve();
});
});
return genDone.resolve({ stdout, stderr });
},
);

// Reuse the file to send the constructed transaction.
const { stdout, stderr } = await genDone.promise;
if (stderr) {
throw Error(`Error creating swingset delivery tx; ${stderr}`);
}
await fs.promises.writeFile(tmpInfo.path, stdout);

args = ['tx', 'authz', 'exec', tmpInfo.path];
}

const args = [
'tx',
'swingset',
'deliver',
args.push(
'--keyring-backend=test',
`@${tmpInfo.path}`, // Deliver message over file, as it could be big.
'--gas=auto',
'--gas-adjustment=1.2',
'--gas-adjustment=1.3',
'--broadcast-mode=block',
'--from=ag-solo',
'--yes',
];
);

// Use the feeAccount for any fees.
if (feeAccountAddr) {
args.push(`--fee-account=${feeAccountAddr}`);
}

// We just try a single delivery per block.
const qret = await queuedHelper(
Expand Down

0 comments on commit 20cdfa8

Please sign in to comment.