Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create chain account (ica) #9114

Merged
merged 4 commits into from
Apr 4, 2024
Merged

feat: create chain account (ica) #9114

merged 4 commits into from
Apr 4, 2024

Conversation

0xpatrickdev
Copy link
Member

@0xpatrickdev 0xpatrickdev commented Mar 20, 2024

closes: #9064
refs: #9042

Description

  1. vat-orchestration - which just has a createAccount() method to start to request an ICA account on a remote chain
  2. E(chainAccount).getAccountAddress() returns a chain account address (as well as .getRemoteAddress() and .getLocalAddress())
  3. Example stakeAtom.contract.js, that creates an ICA on a remote chain.
    • expect a 2nd PR for 9042 to send a delegate message
  4. Working towards the spec here: https://github.com/agoric-labs/orchestration-api-spec/blob/main/src/index.d.ts

Outstanding Items

  • fix @agoric/orchestration -> @agoric/vats -> @agoric/orchestration dependency cycles
  • getRemoteAddress() returns a stale value - it should match what is logged in "IBC onConnect"
  • testing - there's a bootstrap test that's not able to verify the full flow. Interested to get feedback on the approach. I think we may need to build out more network mocking for remote connections.
  • proto3json - note to self to test if any remote chains currently accept proto3json
    • unfortunately, json | proto3-json are not valid for the osmo image i am using (attempt). Tangentially, Hermes does not propagate an error when the negotiating fails (it stops when simulate_tx fails), so the createAccount promise hangs. MF suggests filing a bug.
  • delegate() - protobuf encoding, so we can a message that doesn't throw an error
    • tackling this in a follow up PR
  • e2e - check in a3p-style test with starship
    • tackling this in a follow up PR

Security Considerations

The Port object is highly authoritative. The Port ID and Connection ID are used by host chains to determine the account address and recover closed channels.

There should be a single source allocating ports, and networkVat should ensure ports are not reused unless someone has a reference to the original port.

See #9165 where this is tracked.

Scaling Considerations

Documentation Considerations

Testing Considerations

Upgrade Considerations

@0xpatrickdev
Copy link
Member Author

0xpatrickdev commented Mar 20, 2024

Performed manual E2E testing following QUICKSTART.md instructions here: 0xpatrickdev/starship-demo#2

Screenshot 2024-04-02 at 12 43 07 AM
(dated) screenshot Screenshot 2024-03-27 at 4 45 33 PM

Dated because we are now returning the negotiated remote address string when calling getRemoteAddress()

Query ICA Params
# query cosmos
agd query interchain-accounts host params --node http://localhost:26654
allow_messages: []
host_enabled: true

# query osmosis
agd query interchain-accounts host params --node http://localhost:26655
allow_messages:
- '*'
host_enabled: true

Cosmos doesn't have ICA messages enabled in this testing image, so let's test with osmosis for now.

Create Hermes Clients
hermes create client --host-chain agoriclocal --reference-chain osmosis-test && hermes create client --host-chain osmosis-test --reference-chain agoriclocal
2024-03-27T20:27:28.606098Z  INFO ThreadId(01) running Hermes v1.8.0+39036d1
SUCCESS CreateClient(
    CreateClient(
        Attributes {
            client_id: ClientId(
                "07-tendermint-0",
            ),
            client_type: Tendermint,
            consensus_height: Height {
                revision: 0,
                height: 544,
            },
        },
    ),
)
2024-03-27T20:27:32.136044Z  INFO ThreadId(01) running Hermes v1.8.0+39036d1
SUCCESS CreateClient(
    CreateClient(
        Attributes {
            client_id: ClientId(
                "07-tendermint-1",
            ),
            client_type: Tendermint,
            consensus_height: Height {
                revision: 0,
                height: 26,
            },
        },
    ),
)
Create Connection
hermes create connection --a-chain agoriclocal --b-chain osmosis-test
2024-03-27T20:27:35.729425Z  INFO ThreadId(01) running Hermes v1.8.0+39036d1
2024-03-27T20:27:36.254436Z  INFO ThreadId(01) Creating new clients hosted on chains agoriclocal and osmosis-test
2024-03-27T20:27:37.096708Z  INFO ThreadId(01) foreign_client.create{client=osmosis-test->agoriclocal:07-tendermint-0}: 🍭 client was created successfully id=07-tendermint-1
2024-03-27T20:27:37.438693Z  INFO ThreadId(01) foreign_client.create{client=agoriclocal->osmosis-test:07-tendermint-0}: 🍭 client was created successfully id=07-tendermint-2
2024-03-27T20:27:41.980190Z  INFO ThreadId(01) 🥂 agoriclocal => OpenInitConnection(OpenInit { Attributes { connection_id: connection-0, client_id: 07-tendermint-1, counterparty_connection_id: None, counterparty_client_id: 07-tendermint-2 } }) at height 0-28
2024-03-27T20:27:52.934888Z  INFO ThreadId(01) 🥂 osmosis-test => OpenTryConnection(OpenTry { Attributes { connection_id: connection-1, client_id: 07-tendermint-2, counterparty_connection_id: connection-0, counterparty_client_id: 07-tendermint-1 } }) at height 0-573
2024-03-27T20:27:56.250458Z  WARN ThreadId(01) client consensus proof height too high, waiting for destination chain to advance beyond 0-30
2024-03-27T20:27:56.751490Z  WARN ThreadId(01) client consensus proof height too high, waiting for destination chain to advance beyond 0-30
2024-03-27T20:28:02.384917Z  INFO ThreadId(01) 🥂 agoriclocal => OpenAckConnection(OpenAck { Attributes { connection_id: connection-0, client_id: 07-tendermint-1, counterparty_connection_id: connection-1, counterparty_client_id: 07-tendermint-2 } }) at height 0-32
2024-03-27T20:28:07.805043Z  INFO ThreadId(01) 🥂 osmosis-test => OpenConfirmConnection(OpenConfirm { Attributes { connection_id: connection-1, client_id: 07-tendermint-2, counterparty_connection_id: connection-0, counterparty_client_id: 07-tendermint-1 } }) at height 0-591
2024-03-27T20:28:10.832171Z  INFO ThreadId(01) connection handshake already finished for Connection { delay_period: 0ns, a_side: ConnectionSide { chain: BaseChainHandle { chain_id: agoriclocal }, client_id: 07-tendermint-1, connection_id: connection-0 }, b_side: ConnectionSide { chain: BaseChainHandle { chain_id: osmosis-test }, client_id: 07-tendermint-2, connection_id: connection-1 } }
SUCCESS Connection {
    delay_period: 0ns,
    a_side: ConnectionSide {
        chain: BaseChainHandle {
            chain_id: ChainId {
                id: "agoriclocal",
                version: 0,
            },
            runtime_sender: Sender { .. },
        },
        client_id: ClientId(
            "07-tendermint-1",
        ),
        connection_id: Some(
            ConnectionId(
                "connection-0",
            ),
        ),
    },
    b_side: ConnectionSide {
        chain: BaseChainHandle {
            chain_id: ChainId {
                id: "osmosis-test",
                version: 0,
            },
            runtime_sender: Sender { .. },
        },
        client_id: ClientId(
            "07-tendermint-2",
        ),
        connection_id: Some(
            ConnectionId(
                "connection-1",
            ),
        ),
    },
}

Note that `connection-1` will be our `hostConnectionId` (what osmosis calls agoric) and `connection-0` will be our `controllerConnectionId` (what agoric calls osmosis). These will be used to create the ICA connection.
REPL Commands
command[0] E(home.agoricNames).lookup('instance', 'stakeAtom')
history[0] [Object Alleged: InstanceHandle]{}
command[1] E(home.zoe).getPublicFacet(history[0])
history[1] [Object Alleged: StakeAtom]{}
command[2] pf = history[1]
history[2] [Object Alleged: StakeAtom]{}
command[3] E(pf).provideAccount()
history[3] [Object Alleged: ChainAccount]{}
command[4] ica = history[3]
history[4] [Object Alleged: ChainAccount]{}
command[5] E(ica).getLocalAddress()
history[5] "/ibc-port/icacontroller-0"
command[6] E(ica).getAddress()
history[6] "/ibc-hop/connection-0/ibc-port/icahost/ordered/{\"version\":\"ics27-1\",\"controllerConnectionId\":\"connection-0\",\"hostConnectionId\":\"connection-1\",\"address\":\"\",\"encoding\":\"proto3\",\"txType\":\"sdk_multi_msg\"}"
command[7] E(ica).executeEncodedTx('test')
history[7] "{\"error\":\"ABCI code: 2: error handling packet: see events for details\"}"
Swingset Logs
2024-03-27T20:29:12.753Z SwingSet: vat: v50: ----- StakeAtom.2  2 provideAccount. Binding new port
2024-03-27T20:29:12.951Z SwingSet: ls: v15: Logging sent error stack (Error#1)
2024-03-27T20:29:12.952Z SwingSet: ls: v15: Error#1: No listeners for /ibc-hop/connection-0/ibc-port/icahost/ordered/{"version":"ics27-1","controllerConnectionId":"connection-0","hostConnectionId":"connection-1","address":"","encoding":"proto3","txType":"sdk_multi_msg"}
2024-03-27T20:29:12.952Z SwingSet: ls: v15: Error: No listeners for /ibc-hop/connection-0/ibc-port/icahost/ordered/{"version":"ics27-1","controllerConnectionId":"connection-0","hostConnectionId":"connection-1","address":"","encoding":"proto3","txType":"sdk_multi_msg"}
 at apply ()
 at Error (/bundled-source/.../node_modules/ses/src/error/tame-error-constructor.js:54)
 at inbound (.../network/src/network.js:753)
 at inbound (.../network/src/network.js:735)
 at apply ()
 at apply ()
 at ()

2024-03-27T20:29:12.952Z SwingSet: ls: v15: Error#1 ERROR_NOTE: Sent as error:liveSlots:v15#70001
2024-03-27T20:29:12.953Z SwingSet: xsnap: v15: Error#1 ERROR_NOTE: Sent as error:liveSlots:v15#70002
2024-03-27T20:29:12.953Z SwingSet: ls: v15: Logging sent error stack (Error#1)
2024-03-27T20:29:12.953Z SwingSet: xsnap: v15: Error#1 ERROR_NOTE: Sent as error:liveSlots:v15#70003
2024-03-27T20:29:12.953Z SwingSet: ls: v15: Logging sent error stack (Error#1)
2024-03-27T20:29:12.955Z SwingSet: xsnap: v15: Error#1 ERROR_NOTE: Sent as error:liveSlots:v15#70004
2024-03-27T20:29:12.956Z SwingSet: ls: v15: Logging sent error stack (Error#1)
2024-03-27T20:29:12.956Z SwingSet: xsnap: v15: Error#1 ERROR_NOTE: Sent as error:liveSlots:v15#70005
2024-03-27T20:29:12.956Z SwingSet: ls: v15: Logging sent error stack (Error#1)
2024-03-27T20:29:12.960Z SwingSet: xsnap: v15: Error#1 ERROR_NOTE: Sent as error:liveSlots:v15#70006
2024-03-27T20:29:12.960Z SwingSet: ls: v15: Logging sent error stack (Error#1)
2024-03-27T20:29:13.012Z block-manager: block 46 commit
2024-03-27T20:29:17.668Z block-manager: block 47 begin
2024-03-27T20:29:17.894Z block-manager: block 47 commit
Dragonberry Active
2024-03-27T20:29:22.696Z block-manager: block 48 begin
Dragonberry Active
2024-03-27T20:29:22.936Z SwingSet: vat: v16: IBC fromBridge { blockHeight: 48, blockTime: 1711571357, channelID: 'channel-0', connectionHops: [ 'connection-0' ], counterparty: { channel_id: 'channel-1', port_id: 'icahost' }, counterpartyVersion: '{"version":"ics27-1","controller_connection_id":"connection-0","host_connection_id":"connection-1","address":"osmo1d6em9ea5y3dye6em0awqyss7ssp0a7sgjk792x8cx647cfs7a4msk0fr45","encoding":"proto3","tx_type":"sdk_multi_msg"}', event: 'channelOpenAck', portID: 'icacontroller-0', type: 'IBC_EVENT' }
2024-03-27T20:29:22.974Z SwingSet: vat: v18: ----- Orchestration.2  2 ICA Channel Opened for /ibc-port/icacontroller-0 at /ibc-hop/connection-0/ibc-port/icahost/ordered/{"version":"ics27-1","controllerConnectionId":"connection-0","hostConnectionId":"connection-1","address":"","encoding":"proto3","txType":"sdk_multi_msg"}
2024-03-27T20:29:22.992Z block-manager: block 48 commit

The No listeners for... error is benign - it's a handled error in network.js that should appear in the happy path.

The IBC fromBridge log confirms osmosis has acknowledged our request for a remote account. The osmo1d6em9ea5y3dye6em0awqyss7ssp0a7sgjk792x8cx647cfs7a4msk0fr45 address is assigned.

# failed send packet, since we did not send packetBytes
2024-03-27T20:30:23.209Z block-manager: block 60 commit
Dragonberry Active
Dragonberry Active
2024/03/27 16:30:25 error while extracting context for action &{%!q(*vm.ActionHeader=&{IBC_EVENT 60 1711571418}) "acknowledgementPacket" "" {'\x01' "icacontroller-0" "channel-0" "icahost" "channel-1" "test" "0-0" ''} "{\"error\":\"ABCI code: 2: error handling packet: see events for details\"}" 42EC87A3463FC18F46B75C419541B61A7880B42A}
2024-03-27T20:30:28.234Z block-manager: block 61 begin
Dragonberry Active
2024-03-27T20:30:28.241Z SwingSet: vat: v16: IBC fromBridge { acknowledgement: 'eyJlcnJvciI6IkFCQ0kgY29kZTogMjogZXJyb3IgaGFuZGxpbmcgcGFja2V0OiBzZWUgZXZlbnRzIGZvciBkZXRhaWxzIn0=', blockHeight: 61, blockTime: 1711571423, event: 'acknowledgementPacket', packet: { data: 'dGVzdA==', destination_channel: 'channel-1', destination_port: 'icahost', sequence: 1, source_channel: 'channel-0', source_port: 'icacontroller-0', timeout_height: {}, timeout_timestamp: 1711575013110980000 }, relayer: 'agoric1gtkg0g6x8lqc734ht3qe2sdkrfugpdp2h7fuu0', type: 'IBC_EVENT' }
2024-03-27T20:30:28.275Z block-manager: block 61 commit
Host Node Validation ```bash # query osmosis node, observe STATE_OPEN channel and account address osmo1d6em9ea5y3dye6em0awqyss7ssp0a7sgjk792x8cx647cfs7a4msk0fr45

agd query ibc channel channels --node http://localhost:26655
channels:

  • channel_id: channel-1
    connection_hops:
    • connection-1
      counterparty:
      channel_id: channel-0
      port_id: icacontroller-0
      ordering: ORDER_ORDERED
      port_id: icahost
      state: STATE_OPEN
      version: '{"version":"ics27-1","controller_connection_id":"connection-0","host_connection_id":"connection-1","address":"osmo1d6em9ea5y3dye6em0awqyss7ssp0a7sgjk792x8cx647cfs7a4msk0fr45","encoding":"proto3","tx_type":"sdk_multi_msg"}'
  • channel_id: channel-0
    connection_hops:
    • connection-0
      counterparty:
      channel_id: channel-0
      port_id: transfer
      ordering: ORDER_UNORDERED
      port_id: transfer
      state: STATE_OPEN
      version: ics20-1
      height:
      revision_height: "831"
      revision_number: "0"
      pagination:
      next_key: null
      total: "0"

.bind(`/ibc-port/icacontroller-${this.state.icaControllerNonce}`)
.catch(e => Fail`Failed to bind port: ${bare(e)}`);

this.state.icaControllerNonce += 1;
Copy link
Member Author

@0xpatrickdev 0xpatrickdev Mar 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably want something more robust than this. If the same port identifier (and connection id) is reused, it will result in the same address on the host chain. Maybe this should be networkVat's responsibility, unless vat-orchestration is the only one handing out icacontroller-* ports?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The network vat used to have some makeNonceMaker() code that seems useful for this sort of thing. I/we ripped it out because keeping it would involve yet another exo class. But maybe this justifies having it after all. Should be called something more like registerPrefix or reservePrefix though.

Copy link
Member Author

@0xpatrickdev 0xpatrickdev Mar 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opened a ticket to track work: #9165

Comment on lines 57 to 58
const bundle = await bundleCache.load(path, name);
const installationRef = await EV(zoe).install(bundle);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we try to avoid E(zoe).install(bundle) in anything but toys. This seems to be a pretty high-fidelity test, so I suggest using controller.validateAndInstallBundle(bundle) and E(zoe).installBundleID(bundleID, label).

oops... I see we didn't do this consistently in test-net-ibc-upgrade.ts. oh well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, TIL

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const orchestration = await EV.vat('bootstrap').consumeItem('orchestration');
// XXX this should not throw
await t.notThrowsAsync(async () => {
EV(orchestration).provideAccount(serverLocalAddr, clientLocalAddr);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since it's called provideX, let's test that has find-or-create-X behavior; i.e. that calling it again with the same args gives the same result rather than making a fresh one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to createAccount, no longer using provide

Comment on lines 47 to 48
board: true,
chainStorage: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

board and chainStorage don't seem to be used

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 58 to 60
produce: {
stakeAtom: true,
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nor produce.stakeAtom

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* @param {PowerStore} powers
* @param {K} name
*/
const getPower = (powers, name) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why getPower? why not getNetwork()?

Fail`getPurse not implemented yet`;
},
async close() {
// XXX retrieve all assets first?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps assert that they're all gone? or take some dust threshold parameter?

},
async close() {
// XXX retrieve all assets first?
// XXX do we also revoke the port?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no; the caller supplied it; presumably they own it

* ) => PromiseVow<string>} [onReceive]
* optional cb handler for connection recieving packet
*/
(onOpen, onClose, onReceive) => ({ onOpen, onClose, onReceive }),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

storing functions in durable state? does that work? I'd be surprised.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ty, addressed

/** @param {Partial<OrchestrationPowers>} [initialPowers] */
initialPowers => {
/** @type {PowerStore} */
const powers = zone.detached().mapStore('PowerStore');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why treat this as a collection? the keys are statically known. seems like using them as individual properties is more straightforward.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh... maybe because state properties can't be added on upgrade?
please add a comment to that effect, if that's the reason.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is now a comment with more details and an issue ref

* @param {Port} [currentPort] if a port is provided, it will be reused
* to create the account
*/
async provideAccount(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The provide prefix means find-or-create; i.e. if it's already been created, return the existing one. This code makes a fresh account on every call.

Suggested change
async provideAccount(
async makeAccount(


[addOrchestrationToClient.name]: {
consume: {
client: 'provisioning',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

client is an ag-solo thing; is this code intended for production?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for calling this out and making me think about it more. I suppose we should have two proposals - one for ag-solo / devnet and another for mainnet. Removed ag-solo for now, but may add a commit so it's available on home .

Comment on lines 1 to 4
export type ConnectionId = `connection-${number}`;

export type AttenuatedNetwork = {
bind: import('@agoric/network/src/router').RouterProtocol['bind'];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export type ConnectionId = `connection-${number}`;
export type AttenuatedNetwork = {
bind: import('@agoric/network/src/router').RouterProtocol['bind'];
import type { RouterProtocol } from '@agoric/network/src/router';
export type ConnectionId = `connection-${number}`;
export type AttenuatedNetwork = {
bind: RouterProtocol['bind'];

t.is(
makeICAConnectionAddress('connection-0', 'connection-1'),
'/ibc-hop/connection-0/ibc-port/icahost/ordered/{"version":"ics27-1","controllerConnectionId":"connection-0","hostConnectionId":"connection-1","address":"","encoding":"proto3","txType":"sdk_multi_msg"}',
'returns connection string when controllerConnectionId and hostConnectionId are provided',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏 yay for explicit test assertions

@0xpatrickdev 0xpatrickdev marked this pull request as ready for review April 2, 2024 21:15
@0xpatrickdev 0xpatrickdev requested a review from turadg April 2, 2024 21:15
@0xpatrickdev 0xpatrickdev changed the title feat: stake atom feat: create chain account (ica) Apr 2, 2024
},
sendPacket: {
// ICA Send Packet (Transaction)
acknowledgementPacket: _obj => ({
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This step is not reached in tests yet, only channelOpenAck. channelOpenAck

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the note. when do you plan to cover the case? that would be good to document in the code.

Copy link
Member Author

@0xpatrickdev 0xpatrickdev Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment: not yet tested. will be used in https://github.com/Agoric/agoric-sdk/issues/8881

Original motivation for my comment - realized this wasn't used when I posted the PR. Think it's OK to leave as it corresponds with the diagram being checked in in the README

@@ -86,3 +88,37 @@ test('stakeBld', async t => {
},
});
});

test('stakeAtom', async t => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a good start. can you get it to interact with the smart wallet using mocked bridge responses?

happy to pair on that

Comment on lines 49 to 51
t.truthy(remoteAddress.includes('icahost'), 'remoteAddress is returned');
t.truthy(localAddress.includes('icacontroller'), 'localAddress is returned');
t.truthy(accountAddress.includes('osmo1'), 'accountAddress is returned');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
t.truthy(remoteAddress.includes('icahost'), 'remoteAddress is returned');
t.truthy(localAddress.includes('icacontroller'), 'localAddress is returned');
t.truthy(accountAddress.includes('osmo1'), 'accountAddress is returned');
t.regex(remoteAddress, /icahost/);
t.regex(localAddress, /icacontroller/);
t.regex(accountAddress, /osmo1/);

('m indifferent to the explicit messages)


# ICS-27 Interchain Accounts

Sequence diagrams for Interchain Accounts based on [ics-027-interchain-accounts/README.md#9ffb1d2](https://github.com/cosmos/ibc/blob/9ffb1d26d3018b6efda546189ec7e43d56d23da3/spec/app/ics-027-interchain-accounts/README.md).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏

consider making the file README.md so it will appear when someone navigates to the directory path

},
sendPacket: {
// ICA Send Packet (Transaction)
acknowledgementPacket: _obj => ({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for the note. when do you plan to cover the case? that would be good to document in the code.

packages/builders/scripts/vats/init-orchestration.js Outdated Show resolved Hide resolved
*/

/**
* PowerStore is used so additional powers can be added on upgrade.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please reference the ticket about Exo state migrations because the lack of that is what's motivating this.

the best issue I can find is #7337

) {
await null;

let port;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

port never changes and you're figuring out what to initialize it with. let's do the work to make it a const.


let port;
if (!matches(currentPort, M.remotable('Port'))) {
if (currentPort) trace('Invalid Port provided, binding a new one.');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if they passed a non-port for currentPort, shouldn't that be an error?

I think you want the check to be whether currentPort === undefined, which indicates whether the caller passed an argument. then validate it's actually a port and error if it isn't.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, agree this should be an error.

Aside - it seems I was mistaken in some of my understanding here. This test is failing on the second createAccount call:

  const orchestration = await EV.vat('bootstrap').consumeItem('orchestration');

  const account1 = await EV(orchestration).createAccount(
    'connection-0',
    'connection-0',
  );
  t.truthy(account1, 'createAccount returns an account');

  const port = await EV(account1).getPort();
  t.truthy(port, 'getPort returns a port');

  const port1Addr = await EV(port).getLocalAddress();
  t.truthy(port1Addr);

  const account2 = await EV(orchestration).createAccount(
    'connection-1',
    'connection-1',
    port,
  );
  // fails -  `key channel-0:icacontroller-0 already registered in collection channelKeyToSeqAck`:
  t.truthy(account2, 'createAccount returns an account with port provided');


  const port2 = await EV(account2).getPort();
  const port2Addr = await EV(port2).getLocalAddress();
  t.is(port1Addr, port2Addr, 'port address is the same');

Removing this logic for now. Port reuse is in scope for #9068

return chainAccount.account;
},
/** @param {string} _chainName e.g. cosmos */
getChain(_chainName) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this is at the wrong level of abstraction. I expect we'll have a utility in front of this vat which manages chain config.

Copy link
Member Author

@0xpatrickdev 0xpatrickdev Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Acknowledged. I think the idea is to be able to do:

const cosmos = await E(orchestration).getChain('cosmos');
const account = await E(cosmos).createAccount();

And getChain might look something like:

getChain(chainName) {
 return E(agoricNames || someOtherNamehub).lookup('chain', chainName); 
}

Nonetheless, out of scope for this PR (in scope for #9063) and was intended just to be a placeholder based on this proposed spec. Happy to remove from this PR if you'd prefer

Copy link
Member

@turadg turadg Apr 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, please remove the placeholders. ideally everything included has tests

Comment on lines 267 to 268
/** @typedef {OrchestrationKit['admin']} OrchestrationAdmin */
/** @typedef {OrchestrationKit['public']} Orchestration */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this name too general to bind to the public facet, imo. sites that need it could easily pull it off OrchestrationKit. I suppose the same for OrchestrationAdmin

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed admin and OrchestrationAdmin in 2a39e6d.

I am wary of only exported OrchestrationKit - consumers of the API will likely only use the public facet, so the goal was to export a type that can be used to represent the orchestration available in the bootstrap space for a core proposal (e.g. stakeAtom). Would you prefer to see it called OrchestrationPF?

@turadg turadg mentioned this pull request Apr 3, 2024
t.truthy(account, 'createAccount returns an account');

const res = await EV(account).close();
t.is(res, 'Connection closed');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about trying to use it after it's closed and checking that it throws?

@@ -70,3 +70,20 @@ test('createAccount returns an ICA connection', async t => {
accountAddress,
});
});

test('ICA connection can be closed', async t => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it important that closing a connection frees up resources? If so, is it feasible to check that?

I can imagine it might not be feasible... that any relevant collections are weak / non-enumerable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are good questions. I created #9192 to track

Copy link
Member

@turadg turadg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid progress! Let's land this

@@ -156,9 +137,10 @@ const prepareChainAccount = zone =>
// XXX parseAddress currently throws, should it return '' instead?
this.state.accountAddress = parseAddress(remoteAddr);
},
async onClose(_connection, reason) {
async onClose(_connection, reason, _connectionHandler) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why add the unused param? I went the opposite direction in 44107b9

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this in the course of fixing a broken interface guard. The unused param is not needed to satisfy the interface guard, but it felt weird making the change without including this.

#9191 looks awesome btw. There, or a separate PR would be great to export ConnectionHandler types and an interface guard.

encoding, //
txType: 'sdk_multi_msg',
});
return `/ibc-hop/${controllerConnectionId}/ibc-port/icahost/${ordering}/${connString}`;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@0xpatrickdev 0xpatrickdev added the automerge:rebase Automatically rebase updates, then merge label Apr 4, 2024
@mergify mergify bot merged commit 21fe8d9 into master Apr 4, 2024
74 checks passed
@mergify mergify bot deleted the 9042-stake-atom branch April 4, 2024 15:59
mergify bot added a commit that referenced this pull request Apr 12, 2024
refs: #9042

## Description

In the course of reviewing
#9114 it wasn't clear what the
`/ibc-hop` encoding spec was. This PR separates an `ibc-utils.js` to try
to encapsulate that. @iomekam @michaelfig can you provide some
additional documentation on its design?

This also adds types I used to understand the code, dropping `any`
bindings from 338 to 86.

One type change was in `vows` to add the context param.

### Security Considerations

<!-- Does this change introduce new assumptions or dependencies that, if
violated, could introduce security vulnerabilities? How does this PR
change the boundaries between mutually-suspicious components? What new
authorities are introduced by this change, perhaps by new API calls?
-->

### Scaling Considerations

<!-- Does this change require or encourage significant increase in
consumption of CPU cycles, RAM, on-chain storage, message exchanges, or
other scarce resources? If so, can that be prevented or mitigated? -->

### Documentation Considerations

<!-- Give our docs folks some hints about what needs to be described to
downstream users.

Backwards compatibility: what happens to existing data or deployments
when this code is shipped? Do we need to instruct users to do something
to upgrade their saved data? If there is no upgrade path possible, how
bad will that be for users?

-->

### Testing Considerations

<!-- Every PR should of course come with tests of its own functionality.
What additional tests are still needed beyond those unit tests? How does
this affect CI, other test automation, or the testnet?
-->

### Upgrade Considerations

<!-- What aspects of this PR are relevant to upgrading live production
systems, and how should they be addressed? -->
mergify bot added a commit that referenced this pull request Jul 31, 2024
refs: #9114
closes: #9317 

## Description
- Miscellaneous cleanup related to `CosmosInterchainService` (part of `vat-orchestration`), closing out todos for 1) unnecessary PowerStore abstraction and 2) vow compat for early synchronous returns
- Optimizes ICQ Port Allocation by reusing a single port for all ICQ connections

### Security Considerations
n/a

### Scaling Considerations
n/a

### Documentation Considerations
js doc added where necessary

### Testing Considerations
included tests for new behavior

### Upgrade Considerations
these changes are intended to better prepare us for future upgrades
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
automerge:rebase Automatically rebase updates, then merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

orchestration - ICA Controller - Account - Create account on host chain
3 participants