-
Notifications
You must be signed in to change notification settings - Fork 208
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(orchestration): move chainAccountKit to its own file
- Loading branch information
1 parent
e538811
commit 770160d
Showing
3 changed files
with
178 additions
and
163 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
// @ts-check | ||
/** @file Orchestration service */ | ||
import { NonNullish } from '@agoric/assert'; | ||
import { makeTracer } from '@agoric/internal'; | ||
import '@agoric/network/exported.js'; | ||
import { V as E } from '@agoric/vat-data/vow.js'; | ||
import { M } from '@endo/patterns'; | ||
import { PaymentShape, PurseShape } from '@agoric/ertp'; | ||
import { InvitationShape } from '@agoric/zoe/src/typeGuards.js'; | ||
import { ConnectionHandlerI } from '../typeGuards.js'; | ||
import { makeTxPacket, parseTxPacket } from '../utils/packet.js'; | ||
import { parseAddress } from '../utils/address.js'; | ||
|
||
/** | ||
* @import { ChainAddress } from '../types.js'; | ||
* @import { Zone } from '@agoric/base-zone'; | ||
* @import { TxBody } from '@agoric/cosmic-proto/cosmos/tx/v1beta1/tx.js'; | ||
*/ | ||
|
||
/** @typedef {ReturnType<ReturnType<typeof prepareChainAccountKit>>} ChainAccountKit */ | ||
|
||
const { Fail } = assert; | ||
const trace = makeTracer('ChainAccountKit'); | ||
|
||
/** @import {AnyJson} from '@agoric/cosmic-proto'; */ | ||
|
||
export const Proto3Shape = { | ||
typeUrl: M.string(), | ||
value: M.string(), | ||
}; | ||
|
||
export const ChainAccountI = M.interface('ChainAccount', { | ||
getAccountAddress: M.call().returns(M.string()), | ||
getLocalAddress: M.call().returns(M.string()), | ||
getRemoteAddress: M.call().returns(M.string()), | ||
getPort: M.call().returns(M.remotable('Port')), | ||
executeTx: M.call(M.arrayOf(M.record())).returns(M.promise()), | ||
executeEncodedTx: M.call(M.arrayOf(Proto3Shape)) | ||
.optional(M.record()) | ||
.returns(M.promise()), | ||
close: M.callWhen().returns(M.undefined()), | ||
deposit: M.callWhen(PaymentShape).returns(M.undefined()), | ||
getPurse: M.callWhen().returns(PurseShape), | ||
prepareTransfer: M.callWhen().returns(InvitationShape), | ||
}); | ||
|
||
/** @param {Zone} zone */ | ||
export const prepareChainAccountKit = zone => | ||
zone.exoClassKit( | ||
'ChainAccount', | ||
{ account: ChainAccountI, connectionHandler: ConnectionHandlerI }, | ||
/** | ||
* @param {Port} port | ||
* @param {string} requestedRemoteAddress | ||
*/ | ||
(port, requestedRemoteAddress) => | ||
/** | ||
* @type {{ | ||
* port: Port; | ||
* connection: Remote<Connection> | undefined; | ||
* localAddress: string | undefined; | ||
* requestedRemoteAddress: string; | ||
* remoteAddress: string | undefined; | ||
* accountAddress: ChainAddress['address'] | undefined; | ||
* }} | ||
*/ ( | ||
harden({ | ||
port, | ||
connection: undefined, | ||
requestedRemoteAddress, | ||
remoteAddress: undefined, | ||
accountAddress: undefined, | ||
localAddress: undefined, | ||
}) | ||
), | ||
{ | ||
account: { | ||
/** | ||
* @returns {string} the address of the account on the chain | ||
*/ | ||
getAccountAddress() { | ||
return NonNullish( | ||
this.state.accountAddress, | ||
'Error parsing account address from remote address', | ||
); | ||
}, | ||
getLocalAddress() { | ||
return NonNullish( | ||
this.state.localAddress, | ||
'local address not available', | ||
); | ||
}, | ||
getRemoteAddress() { | ||
return NonNullish( | ||
this.state.remoteAddress, | ||
'remote address not available', | ||
); | ||
}, | ||
getPort() { | ||
return this.state.port; | ||
}, | ||
executeTx() { | ||
throw new Error('not yet implemented'); | ||
}, | ||
/** | ||
* Submit a transaction on behalf of the remote account for execution on the remote chain. | ||
* @param {AnyJson[]} msgs | ||
* @param {Partial<Omit<TxBody, 'messages'>>} [opts] | ||
* @returns {Promise<string>} - base64 encoded bytes string. Can be decoded using the corresponding `Msg*Response` object. | ||
* @throws {Error} if packet fails to send or an error is returned | ||
*/ | ||
executeEncodedTx(msgs, opts) { | ||
const { connection } = this.state; | ||
if (!connection) throw Fail`connection not available`; | ||
return E.when( | ||
E(connection).send(makeTxPacket(msgs, opts)), | ||
// if parseTxPacket cannot find a `result` key, it throws | ||
ack => parseTxPacket(ack), | ||
); | ||
}, | ||
/** | ||
* Close the remote account | ||
*/ | ||
async close() { | ||
/// XXX what should the behavior be here? and `onClose`? | ||
// - retrieve assets? | ||
// - revoke the port? | ||
const { connection } = this.state; | ||
if (!connection) throw Fail`connection not available`; | ||
await E(connection).close(); | ||
}, | ||
async deposit(payment) { | ||
console.log('deposit got', payment); | ||
throw new Error('not yet implemented'); | ||
}, | ||
/** | ||
* get Purse for a brand to .withdraw() a Payment from the account | ||
* @param {Brand} brand | ||
*/ | ||
async getPurse(brand) { | ||
console.log('getPurse got', brand); | ||
throw new Error('not yet implemented'); | ||
}, | ||
|
||
/* transfer account to new holder */ | ||
async prepareTransfer() { | ||
throw new Error('not yet implemented'); | ||
}, | ||
}, | ||
connectionHandler: { | ||
/** | ||
* @param {Remote<Connection>} connection | ||
* @param {string} localAddr | ||
* @param {string} remoteAddr | ||
*/ | ||
async onOpen(connection, localAddr, remoteAddr) { | ||
trace(`ICA Channel Opened for ${localAddr} at ${remoteAddr}`); | ||
this.state.connection = connection; | ||
this.state.remoteAddress = remoteAddr; | ||
this.state.localAddress = localAddr; | ||
// XXX parseAddress currently throws, should it return '' instead? | ||
this.state.accountAddress = parseAddress(remoteAddr); | ||
}, | ||
async onClose(_connection, reason) { | ||
trace(`ICA Channel closed. Reason: ${reason}`); | ||
// XXX handle connection closing | ||
// XXX is there a scenario where a connection will unexpectedly close? _I think yes_ | ||
}, | ||
async onReceive(connection, bytes) { | ||
trace(`ICA Channel onReceive`, connection, bytes); | ||
return ''; | ||
}, | ||
}, | ||
}, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters