зеркало из https://github.com/microsoft/CCF.git
Updating sphinx docs (#759)
This commit is contained in:
Родитель
5603c6012b
Коммит
26a60adff3
|
@ -17,7 +17,7 @@ Nodes are run and maintained by :term:`operators`. However, nodes must be truste
|
|||
Application
|
||||
-----------
|
||||
|
||||
Each node runs the same application (a.k.a. transaction engine). An application is a collection of endpoints that can be triggered by trusted :term:`users`' :term:`JSON-RPC` requests over :term:`TLS`.
|
||||
Each node runs the same application (a.k.a. transaction engine). An application is a collection of endpoints that can be triggered by trusted :term:`users`' commands over :term:`TLS`.
|
||||
|
||||
Each endpoint mutates an in-enclave-memory Key-Value Store that is replicated across all nodes in the network. Changes to the Key-Value Store must be agreed by a variable number of nodes, depending on the consensus algorithm selected (either Raft or PBFT), before being applied.
|
||||
|
||||
|
|
|
@ -10,6 +10,12 @@ Application Entry Point
|
|||
Application Handler
|
||||
-------------------
|
||||
|
||||
.. doxygenclass:: ccf::RpcFrontend
|
||||
.. doxygenclass:: ccf::UserRpcFrontend
|
||||
:project: CCF
|
||||
:members:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. doxygenclass:: ccf::HandlerRegistry
|
||||
:project: CCF
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
|
|
@ -3,7 +3,7 @@ Writing CCF Applications
|
|||
|
||||
This section describes how CCF applications can be developed and deployed to a CCF network.
|
||||
|
||||
Applications can be written in C++ or Lua (see :ref:`developers/example:Example Application`). An application consists of a collection of endpoints that can be triggered by :term:`users` using JSON-RPC. Each endpoint can define an :ref:`developers/logging_cpp:API Schema` to validate user requests.
|
||||
Applications can be written in C++ or Lua (see :ref:`developers/example:Example Application`). An application consists of a collection of endpoints that can be triggered by :term:`users`. Each endpoint can define an :ref:`developers/logging_cpp:API Schema` to validate user requests.
|
||||
|
||||
These endpoints mutate the state of a unique :ref:`developers/kv/index:Key-Value Store` that represents the internal state of the application. Applications define a set of ``Maps`` (see :ref:`developers/kv/kv_how_to:Creating a Map`), mapping from a key to a value. When an application endpoint is triggered, the effects on the Store are committed atomically.
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ The Logging application simply has:
|
|||
RPC Handler
|
||||
-----------
|
||||
|
||||
The handler returned by :cpp:func:`ccfapp::getRpcHandler()` needs to subclass :cpp:class:`ccf::UserRpcFrontend`:
|
||||
The handler returned by :cpp:func:`ccfapp::get_rpc_handler()` should subclass :cpp:class:`ccf::UserRpcFrontend`, providing an implementation of :cpp:class:`ccf::HandlerRegistry`:
|
||||
|
||||
.. literalinclude:: ../../../src/apps/logging/logging.cpp
|
||||
:language: cpp
|
||||
|
@ -49,7 +49,7 @@ The handler returned by :cpp:func:`ccfapp::getRpcHandler()` needs to subclass :c
|
|||
:lines: 1
|
||||
:dedent: 2
|
||||
|
||||
The constructor then needs to create a handler function or lambda for each transaction type. This takes a transaction object and the request's ``params``, interacts with the KV tables, and returns a result:
|
||||
The logging app defines :cpp:class:`ccfapp::LoggerHandlers`, which creates and installs handler functions or lambdas for each transaction type. These take a transaction object and the request's ``params``, interact with the KV tables, and return a result:
|
||||
|
||||
.. literalinclude:: ../../../src/apps/logging/logging.cpp
|
||||
:language: cpp
|
||||
|
@ -71,16 +71,7 @@ A handler can either be installed as:
|
|||
- ``Read``: this handler can be executed on any node of the network.
|
||||
- ``MayWrite``: the execution of this handler on a specific node depends on the value of the ``"readonly"`` parameter in the JSON-RPC command.
|
||||
|
||||
App-defined errors
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Applications can define their own error codes. These should be between ``-32050`` and ``-32099`` to avoid conflicting with CCF's error codes. The Logging application returns errors if the user tries to get an id which has not been logged, or tries to log an empty message. These error codes should be given their own ``enum class``, and a ``get_error_prefix`` function should be defined in the same namespace to help users distinguish error messages:
|
||||
|
||||
.. literalinclude:: ../../../src/apps/logging/logging.cpp
|
||||
:language: cpp
|
||||
:start-after: SNIPPET_START: errors
|
||||
:end-before: SNIPPET_END: errors
|
||||
:dedent: 2
|
||||
.. warning:: These handlers currently return JSON-RPC error codes, and all responses are returned in the body of a ``200 OK`` HTTP response. In future these handlers will be able to directly set the HTTP return code and payload.
|
||||
|
||||
API Schema
|
||||
~~~~~~~~~~
|
||||
|
|
|
@ -16,7 +16,7 @@ Glossary
|
|||
`Flexible Launch Control <https://github.com/intel/linux-sgx/blob/master/psw/ae/ref_le/ref_le.md#flexible-launch-control>`_ is a feature of the Intel :term:`SGX` architecture.
|
||||
|
||||
JSON-RPC
|
||||
`JSON-RPC <https://en.wikipedia.org/wiki/JSON-RPC>`_ is a remote procedure call protocol encoded in JSON. It is the format used by clients (i.e. members, users and operators) to interact with CCF.
|
||||
`JSON-RPC <https://en.wikipedia.org/wiki/JSON-RPC>`_ is a remote procedure call protocol encoded in JSON.
|
||||
|
||||
Members
|
||||
Constitute the consortium governing a CCF network. Their public identity should be registered in CCF.
|
||||
|
|
|
@ -54,4 +54,4 @@ in one or many cloud-hosted data centers, including :term:`Microsoft Azure`, or
|
|||
|
||||
Ledger providers can use CCF to enable higher throughput and higher confidentiality guarantees for distributed ledger applications.
|
||||
CCF developers can write application logic (also known as smart contracts) and enforce application-level access control in several languages by configuring CCF
|
||||
to embed one of several language runtimes on top of its key-value store. Clients then communicate with a running CCF service using :term:`JSON-RPC` interfaces over :term:`TLS`.
|
||||
to embed one of several language runtimes on top of its key-value store. Clients then communicate with a running CCF service over :term:`TLS`.
|
||||
|
|
|
@ -6,18 +6,7 @@ Trusting a New Node
|
|||
|
||||
As opposed to an opening network in which nodes are trusted automatically, new nodes added to an open network must be trusted by a quorum of members before becoming part of the network.
|
||||
|
||||
When an operator starts a new node with the ``join`` option (see :ref:`operators/start_network:Adding a New Node to the Network`), the joining node is assigned a unique node id and is recorded in state `PENDING`. Then, members can vote to accept the new node, using the unique assigned node id:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ memberclient --cert member1_cert --privk member1_privk --rpc-address rpc_ip:rpc_port --ca network_cert trust_node --node-id new_node_id
|
||||
{"commit":13,"global_commit":12,"id":0,"jsonrpc":"2.0","result":{"completed":false,"id":2},"term":2}
|
||||
|
||||
$ memberclient --cert member2_cert --privk member2_privk --rpc-address rpc_ip:rpc_port --ca network_cert vote --proposal-id 2 --accept
|
||||
{"commit":15,"global_commit":14,"id":0,"jsonrpc":"2.0","result":false,"term":2}
|
||||
|
||||
$ memberclient --cert member3_cert --privk member3_privk --rpc-address rpc_ip:rpc_port --ca network_cert vote --proposal-id 2 --accept
|
||||
{"commit":17,"global_commit":16,"id":0,"jsonrpc":"2.0","result":true,"term":2}
|
||||
When an operator starts a new node with the ``join`` option (see :ref:`operators/start_network:Adding a New Node to the Network`), the joining node is assigned a unique node id and is recorded in state `PENDING`. Then, members can vote to accept the new node, using the unique assigned node id (see :ref:`members/proposals:Proposing and Voting for a Proposal` for more detail).
|
||||
|
||||
Once the proposal successfully completes, the new node automatically becomes part of the network.
|
||||
|
||||
|
@ -28,11 +17,7 @@ Updating Code Version
|
|||
|
||||
For new nodes to be able to join the network, the version of the code they run (as specified by the ``--enclave-file``) should be first trusted by the consortium of members.
|
||||
|
||||
If the version of the code being executed needs to be updated (for example, to support additional endpoints), members can create a ``new_code`` proposal, specifying the new code version (e.g. ``3175971c02d00c1a8f9dd23ca89e64955c5caa94e24f4a3a0579dcfb2e6aebf9``):
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ memberclient --cert member_cert --privk member_privk --rpc-address node_ip:node_port --ca network_cert add_code --new-code-id code_version
|
||||
If the version of the code being executed needs to be updated (for example, to support additional endpoints), members can create a ``new_code`` proposal, specifying the new code version.
|
||||
|
||||
.. note:: For a given :term:`Open Enclave` enclave library, the version of the code (``mrenclave``) can be found by running the ``oesign`` utility:
|
||||
|
||||
|
@ -68,13 +53,26 @@ The first member proposes to recover the network, passing the sealed network sec
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
$ memberclient --rpc-address node2_rpc_ip:node2_rpc_port --cert member1_cert.pem --privk member1_privk.pem --ca /path/to/new/network/certificate accept_recovery --sealed-secrets /path/to/sealed/secrets/file
|
||||
$ cat accept_recovery.json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "members/propose",
|
||||
"params": {
|
||||
"parameter": [<sealed secrets>],
|
||||
"script": {
|
||||
"text": "tables, sealed_secrets = ...; return Calls:call(\"accept_recovery\", sealed_secrets)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ curl https://<ccf-node-address>/members/propose --cacert network_cert --key member1_privk --cert member1_cert --data-binary @accept_recovery.json
|
||||
{"commit":100,"global_commit":99,"id":0,"jsonrpc":"2.0","result":{"completed":false,"id":1},"term":2}
|
||||
|
||||
$ memberclient --rpc-address node2_rpc_ip:node2_rpc_port --cert member2_cert.pem --privk member2_privk.pem --ca /path/to/new/network/certificate vote --accept --proposal-id 1
|
||||
$ ./scurl.sh https://<ccf-node-address>/members/vote --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_accept_1.json
|
||||
{"commit":102,"global_commit":101,"id":0,"jsonrpc":"2.0","result":false,"term":2}
|
||||
|
||||
$ memberclient --rpc-address node2_rpc_ip:node2_rpc_port --cert member3_cert.pem --privk member3_privk.pem --ca /path/to/new/network/certificate vote --accept --proposal-id 1
|
||||
$ ./scurl.sh https://<ccf-node-address>/members/vote --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept_1.json
|
||||
{"commit":104,"global_commit":103,"id":0,"jsonrpc":"2.0","result":true,"term":2}
|
||||
|
||||
Once a :term:`quorum` of members have agreed to recover the network, the network secrets are unsealed and each node begins recovery of the private ledger entries.
|
||||
|
@ -103,10 +101,10 @@ Once a :term:`quorum` of members have agreed to recover the network, the network
|
|||
Note over Node 3: Part of Network
|
||||
|
||||
loop Business transactions
|
||||
Users->>+Node 2: JSON-RPC Request
|
||||
Node 2-->>Users: JSON-RPC Response
|
||||
Users->>+Node 3: JSON-RPC Request
|
||||
Node 3-->>Users: JSON-RPC Response
|
||||
Users->>+Node 2: Request
|
||||
Node 2-->>Users: Response
|
||||
Users->>+Node 3: Request
|
||||
Node 3-->>Users: Response
|
||||
end
|
||||
|
||||
Once the recovery of the private ledger on all the nodes that have joined the new network is complete, the ledger is fully recovered and users are able to continue issuing business transactions.
|
||||
|
@ -118,13 +116,26 @@ To limit the scope of key compromise, members of the consortium can refresh the
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
$ memberclient --rpc-address node2_rpc_ip:node2_rpc_port --cert member1_cert.pem --privk member1_privk.pem --ca /path/to/new/network/certificate rekey_ledger
|
||||
$ cat rekey_ledger.json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "members/propose",
|
||||
"params": {
|
||||
"parameter": [<sealed secrets>],
|
||||
"script": {
|
||||
"text": "return Calls:call(\"rekey_ledger\")"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ curl https://<ccf-node-address>/members/propose --cacert network_cert --key member1_privk --cert member1_cert --data-binary @rekey_ledger.json
|
||||
{"commit":100,"global_commit":99,"id":0,"jsonrpc":"2.0","result":{"completed":false,"id":1},"term":2}
|
||||
|
||||
$ memberclient --rpc-address node2_rpc_ip:node2_rpc_port --cert member2_cert.pem --privk member2_privk.pem --ca /path/to/new/network/certificate vote --accept --proposal-id 1
|
||||
$ ./scurl.sh https://<ccf-node-address>/members/vote --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_accept_1.json
|
||||
{"commit":102,"global_commit":101,"id":0,"jsonrpc":"2.0","result":false,"term":2}
|
||||
|
||||
$ memberclient --rpc-address node2_rpc_ip:node2_rpc_port --cert member3_cert.pem --privk member3_privk.pem --ca /path/to/new/network/certificate vote --accept --proposal-id 1
|
||||
$ ./scurl.sh https://<ccf-node-address>/members/vote --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept_1.json
|
||||
{"commit":104,"global_commit":103,"id":0,"jsonrpc":"2.0","result":true,"term":2}
|
||||
|
||||
Once the proposal is accepted (``"result":true``), all subsequent transactions (in this case, with a ``commit`` index greater than ``104``) will be encrypted with a fresh new ledger encryption key. This key is sealed to disk once the rekey transaction is globally committed.
|
|
@ -12,27 +12,73 @@ Then, the certificates of trusted users should be registered in CCF via the memb
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
$ memberclient --cert member1_cert --privk member1_privk --rpc-address rpc_ip:rpc_port --ca network_cert add_user --user-cert user_cert
|
||||
{"commit":4,"global_commit":3,"id":0,"jsonrpc":"2.0","result":{"completed":false,"id":0},"term":2}
|
||||
$ cat add_user.json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "members/propose",
|
||||
"params": {
|
||||
"parameter": [<cert of proposed new user>],
|
||||
"script": {
|
||||
"text": "tables, user_cert = ...; return Calls:call(\"new_user\", user_cert)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Other members are then allowed to vote for the proposal, using the proposal id returned to the proposer member (here ``0``, as per ``"result":{"completed":false,"id":0}``).
|
||||
$ curl https://<ccf-node-address>/members/propose --cacert network_cert --key member0_privk --cert member0_cert --data-binary @add_user.json
|
||||
{"commit":21,"global_commit":20,"id":0,"jsonrpc":"2.0","result":{"completed":false,"id":0},"term":2}
|
||||
|
||||
Other members are then allowed to vote for the proposal, using the proposal id returned to the proposer member (here ``5``, as per ``"result":{"completed":false,"id":5}``). They may submit an unconditional approval, or their vote may query the current state and proposal. These votes `must` be signed.
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ memberclient --cert member2_cert --privk member2_privk --rpc-address rpc_ip:rpc_port --ca network_cert vote --proposal-id 0 --accept
|
||||
{"commit":6,"global_commit":4,"id":0,"jsonrpc":"2.0","result":false,"term":2}
|
||||
$ cat vote_accept.json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "members/vote",
|
||||
"params": {
|
||||
"ballot": {
|
||||
"text": "return true"
|
||||
},
|
||||
"id": 0
|
||||
}
|
||||
}
|
||||
|
||||
$ memberclient --cert member3_cert --privk member3_privk --rpc-address rpc_ip:rpc_port --ca network_cert vote --proposal-id 0 --accept
|
||||
{"commit":7,"global_commit":4,"id":0,"jsonrpc":"2.0","result":true,"term":2}
|
||||
$ ./scurl.sh https://<ccf-node-address>/members/vote --cacert network_cert --key member1_privk --cert member1_cert --data-binary @vote_accept.json
|
||||
{"commit":29,"global_commit":28,"id":0,"jsonrpc":"2.0","result":false,"term":2}
|
||||
|
||||
$ cat vote_conditional.json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "members/vote",
|
||||
"params": {
|
||||
"ballot": {
|
||||
"text": "tables, calls = ...; return (#calls == 1 and calls[1].func == \"new_user\")"
|
||||
},
|
||||
"id": 0
|
||||
}
|
||||
}
|
||||
|
||||
$ ./scurl.sh https://<ccf-node-address>/members/vote --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_conditional.json
|
||||
{"commit":31,"global_commit":30,"id":0,"jsonrpc":"2.0","result":true,"term":2}
|
||||
|
||||
The user is successfully added once a :term:`quorum` of members have accepted the proposal (``"result":true"``).
|
||||
|
||||
The user can then make RPCs, for example ``whoAmI`` to retrieve the unique caller ID assigned to them by CCF:
|
||||
The user can then make user RPCs, for example ``whoAmI`` to retrieve the unique caller ID assigned to them by CCF:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
./client --rpc-address rpc_ip:rpc_port --ca network_cert --cert new_user_cert --pk new_user_privk --req '{"jsonrpc": "2.0", "id": 0, "method": "users/whoAmI"}'
|
||||
{"commit":26,"global_commit":26,"id":0,"jsonrpc":"2.0","result":{"caller_id":3},"term":2}
|
||||
$ cat whoAmI.json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "users/whoAmI"
|
||||
}
|
||||
|
||||
$ curl https://<ccf-node-address>/users/whoAmI --cacert network_cert --key new_user_privk --cert new_user_cert --data-binary @whoAmI.json
|
||||
{"commit":34,"global_commit":34,"id":0,"jsonrpc":"2.0","result":{"caller_id":4},"term":2}
|
||||
|
||||
For each user CCF also stores arbitrary user-data in a JSON object, which can only be written to by members, subject to the standard proposal-vote governance mechanism. This lets members define initial metadata for certain users; for example to grant specific privileges, associate a human-readable name, or categorise the users. This user-data can then be read (but not written) by user-facing apps.
|
||||
|
||||
|
@ -41,24 +87,27 @@ Registering the Lua Application
|
|||
|
||||
.. note:: This section only applies when deploying Lua applications (i.e. using the ``libluageneric.enclave.so.signed`` enclave library). For C++ applications, this step should be skipped.
|
||||
|
||||
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ memberclient --cert member1_cert --privk member1_privk --rpc-address rpc_ip:rpc_port --ca network_cert set_lua_app --lua-app-file /path/to/lua/app_script
|
||||
{"commit":9,"global_commit":8,"id":0,"jsonrpc":"2.0","result":{"completed":false,"id":1},"term":2}
|
||||
$ cat set_lua_app.json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "members/propose",
|
||||
"params": {
|
||||
"parameter": "<proposed lua app>",
|
||||
"script": {
|
||||
"text": "tables, app = ...; return Calls:call(\"set_lua_app\", app)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Other members are then allowed to vote for the proposal, using the proposal id returned to the proposer member (here ``1``, as per ``"result":{"completed":false,"id":1}``).
|
||||
$ curl https://<ccf-node-address>/members/propose --cacert network_cert --key member0_privk --cert member0_cert --data-binary @set_lua_app.json
|
||||
{"commit":36,"global_commit":35,"id":0,"jsonrpc":"2.0","result":{"completed":false,"id":1},"term":2}
|
||||
|
||||
.. code-block:: bash
|
||||
Other members are then able to vote for the proposal using the returned proposal id (here ``1``, as per ``"result":{"completed":false,"id":1}``).
|
||||
|
||||
$ memberclient --cert member2_cert --privk member2_privk --rpc-address rpc_ip:rpc_port --ca network_cert vote --proposal-id 1 --accept
|
||||
{"commit":11,"global_commit":10,"id":0,"jsonrpc":"2.0","result":{"completed":false,"id":1},"term":2}
|
||||
|
||||
$ memberclient --cert member3_cert --privk member3_privk --rpc-address rpc_ip:rpc_port --ca network_cert vote --proposal-id 1 --accept
|
||||
{"commit":13,"global_commit":12,"id":0,"jsonrpc":"2.0","result":{"completed":true,"id":1},"term":2}
|
||||
|
||||
The Lua application is successfully registered once a :term:`quorum` of members have accepted the proposal (``"result":true"``).
|
||||
The Lua application is successfully registered once a :term:`quorum` of members have accepted the proposal.
|
||||
|
||||
Opening the Network
|
||||
-------------------
|
||||
|
@ -67,17 +116,21 @@ Once users are added to the opening network, members should decide to make a pro
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
$ memberclient --cert member1_cert --privk member1_privk --rpc-address rpc_ip:rpc_port --ca network_cert open_network
|
||||
$ cat open_network.json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "members/propose",
|
||||
"params": {
|
||||
"script": {
|
||||
"text": "return Calls:call(\"open_network\")"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ curl https://<ccf-node-address>/members/propose --cacert network_cert --key member0_privk --cert member0_cert --data-binary @open_network.json
|
||||
{"commit":15,"global_commit":14,"id":0,"jsonrpc":"2.0","result":{"completed":false,"id":2},"term":2}
|
||||
|
||||
Other members are then allowed to vote for the proposal, using the proposal id returned to the proposer member (here ``2``, as per ``"result":{"completed":false,"id":2}``).
|
||||
Other members are then able to vote for the proposal using the returned proposal id (here ``2``, as per ``"result":{"completed":false,"id":2}``).
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ memberclient --cert member2_cert --privk member2_privk --rpc-address rpc_ip:rpc_port --ca network_cert vote --proposal-id 2 --accept
|
||||
{"commit":17,"global_commit":16,"id":0,"jsonrpc":"2.0","result":false,"term":2}
|
||||
|
||||
$ memberclient --cert member3_cert --privk member3_privk --rpc-address rpc_ip:rpc_port --ca network_cert vote --proposal-id 2 --accept
|
||||
{"commit":19,"global_commit":18,"id":0,"jsonrpc":"2.0","result":true,"term":2}
|
||||
|
||||
Once a quorum of members have approved the network opening (``"result":true``), the network is opened to users (see :ref:`developers/example:Example Application` for a simple business logic and :term:`JSON-RPC` transactions). It is only then that users are able to execute transactions on the business logic defined by the enclave file (``--enclave-file`` option to ``cchost``).
|
||||
Once a quorum of members have approved the network opening (``"result":true``), the network is opened to users (see :ref:`developers/example:Example Application` for a simple business logic and transactions). It is only then that users are able to execute transactions on the business logic defined by the enclave file (``--enclave-file`` option to ``cchost``).
|
||||
|
|
|
@ -12,48 +12,68 @@ For transparency and auditability, all governance operations (including votes) a
|
|||
Submitting a new proposal
|
||||
-------------------------
|
||||
|
||||
Assuming that 3 members (``member1``, ``member2`` and ``member3``) are already registered in the CCF network and that the sample constitution is used, a member can submit a new proposal using the ``memberclient`` command-line utility (see :ref:`members/member_rpc_api:Member RPC API` for equivalent JSON-RPC API).
|
||||
Assuming that 3 members (``member1``, ``member2`` and ``member3``) are already registered in the CCF network and that the sample constitution is used, a member can submit a new proposal using ``members/propose`` and vote using ``members/vote``.
|
||||
|
||||
For example, ``member1`` may submit a proposal to add a new member (``member4``) to the consortium:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ memberclient --rpc-address 127.83.203.69:55526 --cert member1_cert.pem --privk member1_privk.pem --ca networkcert.pem add_member --member-cert member4_cert.pem
|
||||
$ cat add_member.json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "members/propose",
|
||||
"params": {
|
||||
"parameter": [<cert of member4>],
|
||||
"script": {
|
||||
"text": "tables, member_cert = ...; return Calls:call(\"new_member\", member_cert)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ curl https://<ccf-node-address>/members/propose --cacert network_cert --key member1_privk --cert member1_cert --data-binary @add_member.json
|
||||
{"commit":100,"global_commit":99,"id":0,"jsonrpc":"2.0","result":{"completed":false,"id":1},"term":2}
|
||||
|
||||
In this case, a new proposal with id ``1`` has successfully been created and the proposer member has automatically accepted it. Other members can then accept or reject the proposal:
|
||||
In this case, a new proposal with id ``1`` has successfully been created and the proposer member has voted to accept it (they may instead pass a voting ballot with their proposal if they wish to vote conditionally, or later). Other members can then vote to accept or reject the proposal:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
// Proposal 1 is already created by member 1 (votes: 1/3)
|
||||
|
||||
// Member 2 rejects the proposal (votes: 1/3)
|
||||
$ memberclient --rpc-address 127.83.203.69:55526 --cert member2_cert.pem --privk member2_privk.pem --ca networkcert.pem vote --reject --proposal-id 1
|
||||
$ curl https://<ccf-node-address>/members/vote --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_reject.json
|
||||
{"commit":104,"global_commit":103,"id":0,"jsonrpc":"2.0","result":false,"term":2}
|
||||
|
||||
// Member 3 accepts the proposal (votes: 2/3)
|
||||
// As a majority of members have accepted the proposal, member4 is added to the consortium
|
||||
$ memberclient --rpc-address 127.83.203.69:55526 --cert member3_cert.pem --privk member3_privk.pem --ca networkcert.pem vote --accept --proposal-id 1
|
||||
$ curl https://<ccf-node-address>/members/vote --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept.json
|
||||
{"commit":106,"global_commit":105,"id":0,"jsonrpc":"2.0","result":true,"term":2}
|
||||
|
||||
// As a majority of members have accepted the proposal, member4 is added to the consortium
|
||||
|
||||
As soon as ``member3`` accepts the proposal, a majority (2 out of 3) of members has been reached and the proposal completes, successfully adding ``member4``.
|
||||
|
||||
.. note:: Once a new member has been accepted to the consortium, the new member must acknowledge that it is active:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ memberclient --rpc-address 127.83.203.69:55526 --cert member4_cert.pem --privk member4_privk.pem --ca networkcert.pem ack
|
||||
{"commit":108,"global_commit":107,"id":2,"jsonrpc":"2.0","result":true,"term":2}
|
||||
.. note:: Once a new member has been accepted to the consortium, the new member must acknowledge that it is active by sending a ``members/ack`` request, signing their current nonce.
|
||||
|
||||
.. todo:: Add sample of signing ack with standard tools, not memberclient
|
||||
|
||||
Displaying proposals
|
||||
--------------------
|
||||
|
||||
The details of pending proposals, including the proposer member id, proposal script, parameters and votes, can be displayed with the ``proposal_display`` sub command of the ``memberclient`` utility. For example:
|
||||
The details of pending proposals, including the proposer member id, proposal script, parameters, and votes, can be queried from the service by calling ``members/query`` and reading the ``ccf.proposals`` table. For example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ memberclient --rpc-address 127.83.203.69:55526 --cert member1_cert.pem --privk member1_privk.pem --ca networkcert.pem proposal_display
|
||||
$ cat display_proposals.json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "members/query",
|
||||
"params": {
|
||||
"text": "tables = ...; local proposals = {}; tables[\"ccf.proposals\"]:foreach( function(k, v) proposals[tostring(k)] = v; end ) return proposals;"
|
||||
}
|
||||
}
|
||||
|
||||
$ curl https://<ccf-node-address>/members/query --cacert networkcert.pem --key member0_privk.pem --cert member0_cert.pem --data-binary @display_proposals.json
|
||||
{
|
||||
"1": {
|
||||
"parameter": [...],
|
||||
|
@ -87,7 +107,17 @@ At any stage during the voting process and before the proposal is completed, the
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
$ memberclient --rpc-address 127.83.203.69:55526 --cert member1_cert.pem --privk member1_privk.pem --ca networkcert.pem withdraw --proposal-id 0
|
||||
$ cat withdraw_0.json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"method": "members/withdraw",
|
||||
"params": {
|
||||
"id": 0
|
||||
}
|
||||
}
|
||||
|
||||
$ curl https://<ccf-node-address>/members/withdraw --cacert networkcert.pem --key member0_privk.pem --cert member0_cert.pem --data-binary @withdraw_0.json
|
||||
{"commit":110,"global_commit":109,"id":0,"jsonrpc":"2.0","result":true,"term":4}
|
||||
|
||||
This means future votes will be ignored, and the proposal will never be accepted. However it will remain visible as a proposal so members can easily audit historic proposals.
|
|
@ -26,7 +26,7 @@ To initiate the first phase of the recovery protocol, one or several nodes shoul
|
|||
--enclave-file /path/to/enclave_library
|
||||
--enclave-type debug
|
||||
--node-address node_ip:node_port
|
||||
--rpc-address rpc_ip:rpc_port
|
||||
--rpc-address <ccf-node-address>
|
||||
--public-rpc-address public_rpc_ip:public_rpc_port
|
||||
--ledger-file /path/to/ledger/to/recover
|
||||
--node-cert-file /path/to/node_certificate
|
||||
|
@ -34,9 +34,9 @@ To initiate the first phase of the recovery protocol, one or several nodes shoul
|
|||
recover
|
||||
--network-cert-file /path/to/network_certificate
|
||||
|
||||
Each node will then immediately restore the public entries of its ledger (``--ledger-file``). Because deserialising the public entries present in the ledger may take some time, operators can query the progress of the public recovery by running the ``getSignedIndex`` JSON-RPC which returns the version of the last signed recovered ledger entry. Once the public ledger is fully recovered, the recovered node automatically becomes part of the public network, allowing other nodes to join the network.
|
||||
Each node will then immediately restore the public entries of its ledger (``--ledger-file``). Because deserialising the public entries present in the ledger may take some time, operators can query the progress of the public recovery by calling ``getSignedIndex`` which returns the version of the last signed recovered ledger entry. Once the public ledger is fully recovered, the recovered node automatically becomes part of the public network, allowing other nodes to join the network.
|
||||
|
||||
.. note:: If more than one node were started in ``recover`` mode, the node with the highest signed index (as per the response to the ``getSignedIndex`` JSON-RPC) should be preferred to start the new network. Other nodes should be shutdown and be restarted with the ``join`` option.
|
||||
.. note:: If more than one node were started in ``recover`` mode, the node with the highest signed index (as per the response to the ``getSignedIndex`` RPC) should be preferred to start the new network. Other nodes should be shutdown and be restarted with the ``join`` option.
|
||||
|
||||
Similarly to the normal join protocol (see :ref:`operators/start_network:Adding a New Node to the Network`), other nodes are then able to join the network.
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ To create a new CCF network, the first node of the network should be started wit
|
|||
--enclave-file /path/to/enclave_library
|
||||
--enclave-type debug
|
||||
--node-address node_ip:node_port
|
||||
--rpc-address rpc_ip:rpc_port
|
||||
--rpc-address <ccf-node-address>
|
||||
--public-rpc-address public_rpc_ip:public_rpc_port
|
||||
--ledger-file /path/to/ledger
|
||||
--node-cert-file /path/to/node_certificate
|
||||
|
@ -32,7 +32,7 @@ To create a new CCF network, the first node of the network should be started wit
|
|||
|
||||
When starting up, the node generates its own key pair and outputs the certificate associated with its public key at the location specified by ``--node-cert-file``. A quote file, required for remote attestation, is also output at the location specified by ``--quote-file``. The certificate of the freshly-created CCF network is also output at the location specified by ``--network-cert-file``.
|
||||
|
||||
.. note:: The network certificate should be distributed to users and members to be used as the certificate authority (CA) when establishing a TLS connection with any of the nodes part of the CCF network. For the ``client`` and ``memberclient`` utilities, ``--ca /path/to/network_certificate`` should always be specified.
|
||||
.. note:: The network certificate should be distributed to users and members to be used as the certificate authority (CA) when establishing a TLS connection with any of the nodes part of the CCF network. When using curl, this is passed as the ``--cacert`` argument.
|
||||
|
||||
The certificates of initial members of the consortium are specified via ``--member-cert``. For example, if 3 members (``member1_cert.pem``, ``member2_cert.pem`` and ``member3_cert.pem``) should be added to CCF, operators should specify ``--member-cert member1_cert.pem --member-cert member2_cert.pem --member-cert member3_cert.pem``.
|
||||
|
||||
|
@ -53,7 +53,7 @@ To add a new node to an existing opening network, other nodes should be started
|
|||
--enclave-file /path/to/enclave_library
|
||||
--enclave-type debug
|
||||
--node-address node_ip:node_port
|
||||
--rpc-address rpc_ip:rpc_port
|
||||
--rpc-address <ccf-node-address>
|
||||
--public-rpc-address public_rpc_ip:public_rpc_port
|
||||
--ledger-file /path/to/ledger
|
||||
--node-cert-file /path/to/node_certificate
|
||||
|
@ -79,7 +79,7 @@ Once a CCF network is successfully started and an acceptable number of nodes hav
|
|||
Summary diagram
|
||||
---------------
|
||||
|
||||
Once a node is part of the network (started with either the ``start`` or ``join`` option), members are authorised to issue governance transactions and eventually open the network (see :ref:`members/open_network:Opening a Network`). Only then are users authorised to issue JSON-RPC transactions to CCF.
|
||||
Once a node is part of the network (started with either the ``start`` or ``join`` option), members are authorised to issue governance transactions and eventually open the network (see :ref:`members/open_network:Opening a Network`). Only then are users authorised to issue commands to CCF.
|
||||
|
||||
.. note:: After the network is open to users, members can still issue governance transactions to CCF (for example, adding new users or additional members to the consortium or updating the Lua app, when applicable). See :ref:`members/index:Member Governance` for more information about member governance.
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ For example, to record a message at a specific id with the :ref:`developers/exam
|
|||
}
|
||||
}
|
||||
|
||||
$ curl https://127.116.132.53:41188/users/LOG_record -H 'Content-Type: application/json' --data-binary @request.json -w '\n' --cacert networkcert.pem --key user0_privk.pem --cert user0_cert.pem
|
||||
$ curl https://<ccf-node-address>/users/LOG_record --cacert networkcert.pem --key user0_privk.pem --cert user0_cert.pem --data-binary @request.json
|
||||
{
|
||||
"commit": 23,
|
||||
"global_commit": 22,
|
||||
|
@ -75,9 +75,7 @@ To guarantee that their request is successfully committed to the ledger, a user
|
|||
}
|
||||
}
|
||||
|
||||
$ client --pretty-print --rpc-address node_rpc_ip:node_rpc_port --ca networkcert.pem userrpc --req @get_commit.json --cert user_cert.pem --pk user_privk.pem
|
||||
Sending RPC to node_rpc_ip:node_rpc_port
|
||||
Doing user RPC:
|
||||
$ curl https://<ccf-node-address>/users/getCommit --cacert networkcert.pem --key user0_privk.pem --cert user0_cert.pem --data-binary @get_commit.json
|
||||
{
|
||||
"commit": 31,
|
||||
"global_commit": 31,
|
||||
|
@ -112,9 +110,7 @@ To obtain a receipt, a user needs to issue a ``getReceipt`` RPC for a particular
|
|||
}
|
||||
}
|
||||
|
||||
$ client --pretty-print --rpc-address node_rpc_ip:node_rpc_port --ca networkcert.pem userrpc --req @get_receipt.json --cert user_cert.pem --pk user_privk.pem
|
||||
Sending RPC to node_rpc_ip:node_rpc_port
|
||||
Doing user RPC:
|
||||
$ curl https://<ccf-node-address>/users/getReceipt --cacert networkcert.pem --key user0_privk.pem --cert user0_cert.pem --data-binary @get_receipt.json
|
||||
{
|
||||
"commit": 31,
|
||||
"global_commit": 31,
|
||||
|
@ -130,7 +126,7 @@ Receipts can be verified with the ``verifyReceipt`` RPC:
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
$ cat get_receipt.json
|
||||
$ cat verify_receipt.json
|
||||
{
|
||||
"id": 0,
|
||||
"method": "users/verifyReceipt",
|
||||
|
@ -141,9 +137,7 @@ Receipts can be verified with the ``verifyReceipt`` RPC:
|
|||
}
|
||||
}
|
||||
|
||||
$ client --pretty-print --rpc-address node_rpc_ip:node_rpc_port --ca networkcert.pem userrpc --req @get_receipt.json --cert user_cert.pem --pk user_privk.pem
|
||||
Sending RPC to node_rpc_ip:node_rpc_port
|
||||
Doing user RPC:
|
||||
$ curl https://<ccf-node-address>/users/verifyReceipt --cacert networkcert.pem --key user0_privk.pem --cert user0_cert.pem --data-binary @verify_receipt.json
|
||||
{
|
||||
"commit": 31,
|
||||
"global_commit": 31,
|
||||
|
|
|
@ -7,12 +7,11 @@ The API can also be retrieved from a running service using the `listMethods`_ an
|
|||
|
||||
.. code-block:: bash
|
||||
|
||||
$ client --pretty-print --rpc-address 127.99.16.14:36785 --ca networkcert.pem userrpc --req @listMethods.json --cert user1_cert.pem --pk user1_privk.pem
|
||||
Doing user RPC:
|
||||
$ curl https://<ccf-node-address>/users/listMethods --cacert networkcert.pem --key user0_privk.pem --cert user0_cert.pem --data-binary @listMethods.json
|
||||
{
|
||||
"commit": 4,
|
||||
"global_commit": 4,
|
||||
"id": 1,
|
||||
"id": 0,
|
||||
"jsonrpc": "2.0",
|
||||
"result": {
|
||||
"methods": [
|
||||
|
@ -21,18 +20,22 @@ The API can also be retrieved from a running service using the `listMethods`_ an
|
|||
"LOG_record",
|
||||
"LOG_record_pub",
|
||||
"getCommit",
|
||||
"getPrimaryInfo",
|
||||
"getMetrics",
|
||||
"getNetworkInfo",
|
||||
"getPrimaryInfo",
|
||||
"getReceipt",
|
||||
"getSchema",
|
||||
"listMethods",
|
||||
"mkSign"
|
||||
"mkSign",
|
||||
"verifyReceipt",
|
||||
"whoAmI",
|
||||
"whoIs"
|
||||
]
|
||||
},
|
||||
"term": 2
|
||||
}
|
||||
|
||||
$ client --pretty-print --rpc-address 127.99.16.14:36785 --ca networkcert.pem userrpc --req @getSchema.json --cert user1_cert.pem --pk user1_privk.pem
|
||||
Doing user RPC:
|
||||
$ curl https://<ccf-node-address>/users/getSchema --cacert networkcert.pem --key user0_privk.pem --cert user0_cert.pem --data-binary @getSchema.json
|
||||
{
|
||||
"commit": 4,
|
||||
"global_commit": 4,
|
||||
|
|
Загрузка…
Ссылка в новой задаче