Rosetta API Updates | Neuron Custody

Summary

Improvements to the Rosetta API in order to enable exchanges and other ecosystem participants to create neurons for staking and voting rewards.

Status
Discussing

What you can do to help

  • Ask questions
  • Propose ideas

Documentation

Key people involved
@bogwar , Maciej Kot, @roman-kashitsyn

Documentation

Relevant Background

  • Neuron custody via Rosetta (Phase 1 - Deposit). We’re going to divide this up into 3 releases, deposit, withdraw and manage.
7 Likes

So basically Coinbase could become another frontend to the NNS?

1 Like

Interesting way to put it, but I guess yes, it would mean Coinbase could have a lot of the functionality of the NNS Dapp (staking, voting, etc…)

4 Likes

Depending on how many of the features they’ll surface in the UI that would be lead essentially to another frontend to the NNS

2 Likes

Is it possible for Binance and Coinbase to hold on their design for the staking products? Or do they need to build an another especially for ICP? e.g. Binance has as default a 30/60/90 Days option, but within Neurons it is possible to lock up ICP for much longer.

Hmm…do we want this? These guys have enough power in the ecosystem as it is. It would seem to me that centralized exchanges holding the rights to a large portion of neurons is pretty antithetical to further decentralization. Not your keys, not your neurons. Community efforts like axon are at least keeping centralization at arm’s length with their approach.

On the other hand, I guess this stuff is going to emerge via other means if it isn’t provided.

All in all, it seems like a low priority to me.

3 Likes

I am not sure it is obvious but one large benefit of this project is that it would allow people to stake and vote through Coinbase (and other exchanges) so it would greatly increase both the pool and participation of the IC community. I think having people vote and stake in their provider of choice will reduce friction greatly.

4 Likes

I share @skilesare concerns. What would prevent a centralized exchange from issuing/voting on proposals that only benefit them? In an extreme case; let’s say an exchange like FTX wants to harm the IC because we become a credible threat to Solana. What keeps FTX from accumulating enough voting power to do something crazy like disconnect a data center? Unlike normal stakeholders, these exchanges would not be incentivized to vote in the best interest of the network because it’s not their money that they are playing with.

I agree that this is low priority.

Edit: I guess what I’m asking is whether or not we can prevent an exchange from voting as a proxy for the individual stakeholders. What would prevent the exchange from creating its own neuron (minimum amount staked) and setting all other neurons to follow at creation? Will the exchange be required to provide the stakeholder with a full neuron management interface?

4 Likes

Hey @diegop would you please clarify what this proposal means. I can envision two options (described below) that an exchange like Coinbase could provide. Option 1 seems like a very attractive option because it would improve decentralization and quickly move us closer to the 90% staking goal. I think this should be a high priority if the intent of the Rosetta API aligns with Option 1. Option 2 seems like a disaster to decentralization because it would put exchanges in a position to have a lot of voting power using other people’s money. I would not want Option 2 at all.

Option 1: Coinbase offers a front end for staking where a user is subject to all the same rules as we are with the NNS. Their ICP is actually staked in their own neurons and they have full control of their voting power and their vote following is set up by default in the same way as the NNS app default configurations.

Option 2: Coinbase offers a staking return that is slightly less than governance rewards. They take customer ICP and stake it in neurons that Coinbase owns and Coinbase controls the voting power. Coinbase earns the voting rewards and redistributes to their customers at a discounted rate to account for administrative costs and a small Coinbase profit. The key concern here would be that Coinbase would own the voting power that comes from other peoples investment in ICP.

7 Likes

I was reminded of this Roadmap topic after the recent Binance announcement.

I’m curious, are these Rosetta updates required for an exchange to offer NNS staking; or does this just make it easier for them to do so? To put it another way; is there something preventing an exchange, like Coinbase, from offering their customers an NNS staking option today?

Thanks.

Hi @LightningLad91 !

That’s a very good question!

The Rosetta node is by no means required to offer staking. Staking is a feature of the Governance smart contract, anybody can interact with that contract by sending messages to the IC. For example, Sygnum offers staking and neuron custody already. They are using quill and working on a more scalable and automated architecture based on Rosetta.

The Rosetta node doesn’t contain any magic when it comes to submitting transactions, it’s a simple translation layer between the JSON-RPC language of Rosetta API to the language of the IC ingress messages.

The main disadvantage of implementing staking directly is that there is not much documentation on how to do that, and there are many moving parts to take care of.

Rosetta API provides a common framework that is reasonably well documented and widely adopted. It’s much easier to start with Rosetta at the moment. This is very important for the financial integrations team because building a good financial product is hard, and we want to simplify this on our side as much as we can.

I hope one day we’ll have a comprehensive well-documented library that makes interaction with the Ledger and Governance much easier, and Rosetta node will become less important.

8 Likes

Thank you for the response @roman-kashitsyn

I spoke to @lastmjs about my concern (plz see my comment from September) and he explained that there was nothing we could really do to prevent an exchange from trying to accumulate voting power by taking control of their customer’s neurons. Your response seems to support his assessment.

I don’t want to rob potential stakeholders of convenience if it doesn’t buy us any extra security so i’ll likely vote to adopt this proposal.

1 Like

Hi @diegop, as I can’t find much info for this wanted to ask what is the status of Rosetta API and eventually if there is some release schedule… Thanks!

Let me ping some folks who may know more.

1 Like

Hi @plsak ,

Rosetta API is available already as docker image. It is already used to integrate the NNS with many exchanges. The last Rosetta API released is 1.8.0 and supports stake maturity (see the CHANGELOG). Since then, the team has added the remaining NNS operations to Rosetta API. You can see the list of supported operations here. It also supports the ability to list pending proposals from the nns and to get info about a certain proposals via the call endpoint.

Rosetta API should be ready to be used to manage neuron and integrate with the NNS. The main advantage of Rosetta over a direct integration is that it deals with the complexities of integrating with the IC for you. If you know how to make REST calls then you can use it to write something that talks with the NNS.

If you plan to test it then I would suggest to start with the Rosetta documentation. We have a testing replica that can be used to try Rosetta out. If you don’t pass --mainnet, Rosetta will connect that that testnet.
For example, this is how I run it locally to my laptop to play with the ICP Ledger:

$ docker run --interactive --tty --publish 8080:8080 --rm dfinity/rosetta-api
14:45:17.602411 INFO [main] ic_rosetta_api - Starting ic-rosetta-api, pkg_version: 1.8.0
14:45:17.602453 INFO [main] ic_rosetta_api - Listening on 0.0.0.0:8080
14:45:17.602465 WARN [main] ic_rosetta_api - Data certificate will not be verified due to missing root key
14:45:17.603304 INFO [main] ic_rosetta_api - Token symbol set to ICP
14:45:17.605532 WARN [main] ic_ledger_canister_blocks_synchronizer::canister_access - Fetching the root key from the replica because it was not set
14:45:17.715255 INFO [main] ic_ledger_canister_blocks_synchronizer::ledger_blocks_sync - Loading blocks from store
14:45:17.715349 INFO [main] ic_ledger_canister_blocks_synchronizer::ledger_blocks_sync - Ledger client is up. Loaded 0 blocks from store. First block at None, last at None
14:45:17.715992 INFO [main] ic_rosetta_api - Network id: NetworkIdentifier { blockchain: "Internet Computer", network: "00000000000000020101", sub_network_identifier: None }
14:45:17.716961 INFO [main] ic_rosetta_api::rosetta_server - Starting Rosetta API server
14:45:17.765085 INFO [main] ic_ledger_canister_blocks_synchronizer::ledger_blocks_sync - Syncing 1107 blocks. New tip will be 1106
14:45:17.809118 INFO [main] ic_ledger_canister_blocks_synchronizer::ledger_blocks_sync - Synced took 0.044014034 seconds
14:45:17.814857 INFO [main] ic_ledger_canister_blocks_synchronizer::ledger_blocks_sync - You are all caught up to block 1106

and then I can ask for the list of networks and work from there:

$ curl -XPOST  localhost:8080/network/list -H'Content-Type: application/json' -d'{}'
{"network_identifiers":[{"blockchain":"Internet Computer","network":"00000000000000020101"}]}
4 Likes

Thank you @mariop, I’m just trying to play around but am getting following errors - in general to me it seems like invalid returned value from https://exchanges.testnet.dfinity.network/api/v2/status - shouldn’t that be some JSON (as per REST specs - if it’s REST, assumed it as per /api/v2/status)?

Please not that I didn’t have much time for the debug so sorry if just overlooked something, below is the procedure and outputs I used.
Also I’m trying this on Mac (with Intel CPU) where the docker is somehow weird, but the errors don’t seems to be related to that (in the beginning I thought I have just occupied port 8080 but doesn’t seems like that from further debug).

$ curl -s https://exchanges.testnet.dfinity.network/api/v2/status | jq .
parse error: Invalid numeric literal at line 1, column 44

$ curl -s https://exchanges.testnet.dfinity.network/api/v2/status > /tmp/api.v2.status

$ file /tmp/api.v2.status
/tmp/api.v2.status: Concise Binary Object Representation (CBOR) container (array) (map)

$ cat /tmp/api.v2.status
+��|ic_api_versionf0.18.0hroot_keyX�0��0
s��&�Z�Oa��ZA��VD�nI۪A�r:}&���	ĨS��s~>����+f����0$t�S��C)�F��+)�-Y��ɡ��<
        89��!L�limpl_versionx(0182cb9e96355780b178c40edde1936053c9333ciimpl_hashx@bfb88232221ac400f77c443b521fe56f739d2ac60c1a878b9cff6b3eb0e7a043ureplica_health_statusghealthy
$ docker run --interactive --tty --publish 8080:8080 --rm dfinity/rosetta-api
13:33:44.365681 INFO [main] ic_rosetta_api - Starting ic-rosetta-api, pkg_version: 1.8.0
13:33:44.365699 INFO [main] ic_rosetta_api - Listening on 0.0.0.0:8080
13:33:44.365708 WARN [main] ic_rosetta_api - Data certificate will not be verified due to missing root key
13:33:44.365725 INFO [main] ic_rosetta_api - Token symbol set to ICP
13:33:44.365854 WARN [main] ic_ledger_canister_blocks_synchronizer::canister_access - Fetching the root key from the replica because it was not set
thread 'main' panicked at 'Failed to initialize ledger client: InternalError(false, Details { error_message: Some("An error happened during communication with the replica: error sending request for url (https://exchanges.testnet.dfinity.network/api/v2/status): error trying to connect: tcp connect error: Cannot assign requested address (os error 99)"), extra_fields: {} })', rs/rosetta-api/src/main.rs:183:35
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

$ docker run --interactive --tty --publish 8080:8080 -e 'RUST_BACKTRACE=1' --rm dfinity/rosetta-api
13:34:05.138140 INFO [main] ic_rosetta_api - Starting ic-rosetta-api, pkg_version: 1.8.0
13:34:05.138204 INFO [main] ic_rosetta_api - Listening on 0.0.0.0:8080
13:34:05.138248 WARN [main] ic_rosetta_api - Data certificate will not be verified due to missing root key
13:34:05.138305 INFO [main] ic_rosetta_api - Token symbol set to ICP
13:34:05.138513 WARN [main] ic_ledger_canister_blocks_synchronizer::canister_access - Fetching the root key from the replica because it was not set
thread 'main' panicked at 'Failed to initialize ledger client: InternalError(false, Details { error_message: Some("An error happened during communication with the replica: error sending request for url (https://exchanges.testnet.dfinity.network/api/v2/status): error trying to connect: tcp connect error: Cannot assign requested address (os error 99)"), extra_fields: {} })', rs/rosetta-api/src/main.rs:183:35
stack backtrace:
   0: rust_begin_unwind
             at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/panicking.rs:584:5
   1: core::panicking::panic_fmt
             at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/core/src/panicking.rs:142:14
   2: ic_rosetta_api::main::{{closure}}
   3: std::thread::local::LocalKey<T>::with
   4: <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll
   5: tokio::runtime::scheduler::current_thread::Context::enter
   6: tokio::macros::scoped_tls::ScopedKey<T>::set
   7: tokio::runtime::runtime::Runtime::block_on
   8: ic_rosetta_api::main
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

$ docker run --interactive --tty --publish 8080:8080 -e 'RUST_BACKTRACE=full' --rm dfinity/rosetta-api
13:34:24.361627 INFO [main] ic_rosetta_api - Starting ic-rosetta-api, pkg_version: 1.8.0
13:34:24.361687 INFO [main] ic_rosetta_api - Listening on 0.0.0.0:8080
13:34:24.361698 WARN [main] ic_rosetta_api - Data certificate will not be verified due to missing root key
13:34:24.361718 INFO [main] ic_rosetta_api - Token symbol set to ICP
13:34:24.361917 WARN [main] ic_ledger_canister_blocks_synchronizer::canister_access - Fetching the root key from the replica because it was not set
thread 'main' panicked at 'Failed to initialize ledger client: InternalError(false, Details { error_message: Some("An error happened during communication with the replica: error sending request for url (https://exchanges.testnet.dfinity.network/api/v2/status): error trying to connect: tcp connect error: Cannot assign requested address (os error 99)"), extra_fields: {} })', rs/rosetta-api/src/main.rs:183:35
stack backtrace:
   0:     0x5584bb79cc50 - std::backtrace_rs::backtrace::libunwind::trace::h32eb3e08e874dd27
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5
   1:     0x5584bb79cc50 - std::backtrace_rs::backtrace::trace_unsynchronized::haa3f451d27bc11a5
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
   2:     0x5584bb79cc50 - std::sys_common::backtrace::_print_fmt::h5b94a01bb4289bb5
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/sys_common/backtrace.rs:66:5
   3:     0x5584bb79cc50 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hb070b7fa7e3175df
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/sys_common/backtrace.rs:45:22
   4:     0x5584bb7c250e - core::fmt::write::hd5207aebbb9a86e9
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/core/src/fmt/mod.rs:1202:17
   5:     0x5584bb795ec5 - std::io::Write::write_fmt::h3bd699bbd129ab8a
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/io/mod.rs:1679:15
   6:     0x5584bb79e453 - std::sys_common::backtrace::_print::h7a21be552fdf58da
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/sys_common/backtrace.rs:48:5
   7:     0x5584bb79e453 - std::sys_common::backtrace::print::ha85c41fe4dd80b13
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/sys_common/backtrace.rs:35:9
   8:     0x5584bb79e453 - std::panicking::default_hook::{{closure}}::h04cca40023d0eeca
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/panicking.rs:295:22
   9:     0x5584bb79e13f - std::panicking::default_hook::haa3ca8c310ed5402
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/panicking.rs:314:9
  10:     0x5584bb79eb5a - std::panicking::rust_panic_with_hook::h7b190ce1a948faac
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/panicking.rs:698:17
  11:     0x5584bb79ea57 - std::panicking::begin_panic_handler::{{closure}}::hbafbfdc3e1b97f68
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/panicking.rs:588:13
  12:     0x5584bb79d0fc - std::sys_common::backtrace::__rust_end_short_backtrace::hda93e5fef243b4c0
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/sys_common/backtrace.rs:138:18
  13:     0x5584bb79e772 - rust_begin_unwind
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/panicking.rs:584:5
  14:     0x5584bb7bfbd3 - core::panicking::panic_fmt::h8d17ca1073d9a733
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/core/src/panicking.rs:142:14
  15:     0x5584baaaa30e - ic_rosetta_api::main::{{closure}}::h799450387c351464
  16:     0x5584bab22d5d - std::thread::local::LocalKey<T>::with::h048ad4042cc1c2ae
  17:     0x5584baaa51f8 - <core::future::from_generator::GenFuture<T> as core::future::future::Future>::poll::ha7ea65f8031aec79
  18:     0x5584baad4060 - tokio::runtime::scheduler::current_thread::Context::enter::hd49f70e033169598
  19:     0x5584bab40ff1 - tokio::macros::scoped_tls::ScopedKey<T>::set::hf2a10f0988424376
  20:     0x5584baad3c8f - tokio::runtime::runtime::Runtime::block_on::hc3b55ecb8eabc9ae
  21:     0x5584baabd285 - ic_rosetta_api::main::h7426bf004f709972
  22:     0x5584baab0b13 - std::sys_common::backtrace::__rust_begin_short_backtrace::h930e0d924b1945e7
  23:     0x5584baac9439 - std::rt::lang_start::{{closure}}::hd61fb70ac743a4c4
  24:     0x5584bb7908bf - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::hb69be6e0857c6cfb
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/core/src/ops/function.rs:283:13
  25:     0x5584bb7908bf - std::panicking::try::do_call::h396dfc441ee9c786
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/panicking.rs:492:40
  26:     0x5584bb7908bf - std::panicking::try::h6cdda972d28b3a4f
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/panicking.rs:456:19
  27:     0x5584bb7908bf - std::panic::catch_unwind::h376039ec264e8ef9
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/panic.rs:137:14
  28:     0x5584bb7908bf - std::rt::lang_start_internal::{{closure}}::hc94720ca3d4cb727
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/rt.rs:148:48
  29:     0x5584bb7908bf - std::panicking::try::do_call::h2422fb95933fa2d5
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/panicking.rs:492:40
  30:     0x5584bb7908bf - std::panicking::try::h488286b5ec8333ff
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/panicking.rs:456:19
  31:     0x5584bb7908bf - std::panic::catch_unwind::h81636549836d2a25
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/panic.rs:137:14
  32:     0x5584bb7908bf - std::rt::lang_start_internal::h6ba1bb743c1e9df9
                               at /rustc/897e37553bba8b42751c67658967889d11ecd120/library/std/src/rt.rs:148:20
  33:     0x5584baabd2e8 - main
  34:     0x7f3832047d0a - __libc_start_main
  35:     0x5584baa6a6ee - _start
  36:                0x0 - <unknown>

/api/v2/status is a replica endpoint and should return cbor as per specification. You need to talk to the rosetta node via curl in order to get the REST API, not the replica.

As per Rosetta, the error tcp connect error: Cannot assign requested address (os error 99) means that the port 8080 is already used and you should pick another port or close the application that is using 8080.

2 Likes

Well, that is something what I suspected and checked as a first thing before was checking more - so I guess it’s more related to my macos setup, version and limitations (any upgrade breaks anything…) - I’ll re-try again later when get to some linux distro then.

Thanks anyway!

1 Like

FYI, just tried from linux (with standard GNU tooling) and got the same - below first made sure that I can start to listen on 8080 and then that it’s free, working as root

  • pointing it out as it could mean that the problem is different
~ #> netstat -lnp | grep 8080
~ #> nc -l 8080 &
[1] 23037
~ #> netstat -lnp | grep 8080
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      23037/nc

~ #> kill %1
~ #> netstat -lnp | grep 8080
[1]+  Terminated              nc -l 8080
~ #> netstat -lnp | grep 8080
~ #> netstat -lnp | grep 8080

~ #> docker run --interactive --tty --publish 8080:8080 --rm dfinity/rosetta-api
14:25:22.762052 INFO [main] ic_rosetta_api - Starting ic-rosetta-api, pkg_version: 1.8.0
14:25:22.762081 INFO [main] ic_rosetta_api - Listening on 0.0.0.0:8080
14:25:22.762099 WARN [main] ic_rosetta_api - Data certificate will not be verified due to missing root key
14:25:22.762135 INFO [main] ic_rosetta_api - Token symbol set to ICP
14:25:22.762363 WARN [main] ic_ledger_canister_blocks_synchronizer::canister_access - Fetching the root key from the replica because it was not set
thread 'main' panicked at 'Failed to initialize ledger client: InternalError(false, Details { error_message: Some("An error happened during communication with the replica: error sending request for url (https://exchanges.testnet.dfinity.network/api/v2/status): error trying to connect: tcp connect error: Cannot assign requested address (os error 99)"), extra_fields: {} })', rs/rosetta-api/src/main.rs:183:35
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
1 Like

To help with the root cause isolation I tried older version of the image (20221018) - started without any problems (on mac), so probably there is some gotcha in the latest version:

$ docker run --interactive --tty --publish 8080:8080 --rm dfinity/rosetta-api:20221018
15:25:03.372327 INFO [main] ic_rosetta_api - Starting ic-rosetta-api, pkg_version: 1.7.2
15:25:03.372364 INFO [main] ic_rosetta_api - Listening on 0.0.0.0:8080
15:25:03.372386 WARN [main] ic_rosetta_api - Data certificate will not be verified due to missing root key
15:25:03.372430 INFO [main] ic_rosetta_api - Token symbol set to ICP
15:25:03.735321 INFO [main] ic_ledger_canister_blocks_synchronizer::ledger_blocks_sync - Loading blocks from store
15:25:03.735403 INFO [main] ic_ledger_canister_blocks_synchronizer::ledger_blocks_sync - Ledger client is up. Loaded 0 blocks from store. First block at None, last at None
15:25:03.735465 INFO [main] ic_rosetta_api - Network id: NetworkIdentifier { blockchain: "Internet Computer", network: "00000000000000020101", sub_network_identifier: None }
15:25:03.735722 INFO [main] ic_rosetta_api::rosetta_server - Starting Rosetta API server
15:25:03.772266 INFO [main] ic_ledger_canister_blocks_synchronizer::ledger_blocks_sync - Syncing 1107 blocks. New tip will be 1106
15:25:04.040814 INFO [main] ic_ledger_canister_blocks_synchronizer::ledger_blocks_sync - You are all caught up to block 1106

Other window:

$ curl -s -X POST  localhost:8080/network/list -H 'Content-Type: application/json' -d '{}' | jq .
{
  "network_identifiers": [
    {
      "blockchain": "Internet Computer",
      "network": "00000000000000020101"
    }
  ]
}
2 Likes