зеркало из https://github.com/microsoft/CCF.git
Use digests for proposal ids, rather than incrementing integers (#2104)
This commit is contained in:
Родитель
321cd89fc7
Коммит
32cc0ad938
|
@ -28,3 +28,4 @@ env/
|
||||||
python/setup.py
|
python/setup.py
|
||||||
node_modules
|
node_modules
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
.cache
|
|
@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Governance proposal ids are now digests, hex-encoded as strings.
|
||||||
|
|
||||||
## [0.17.1]
|
## [0.17.1]
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
@ -23,21 +23,21 @@ A member proposes to recover the network and other members can vote on the propo
|
||||||
|
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @accept_recovery.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @accept_recovery.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 1,
|
"proposal_id": "1b7cae1585077104e99e1860ad740efe28ebd498dbf9988e0e7b299e720c5377",
|
||||||
"proposer_id": 0,
|
"proposer_id": 0,
|
||||||
"state": "OPEN"
|
"state": "OPEN"
|
||||||
}
|
}
|
||||||
|
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals/1/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_accept.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals/1b7cae1585077104e99e1860ad740efe28ebd498dbf9988e0e7b299e720c5377/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_accept.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 1,
|
"proposal_id": "1b7cae1585077104e99e1860ad740efe28ebd498dbf9988e0e7b299e720c5377",
|
||||||
"proposer_id": 0,
|
"proposer_id": 0,
|
||||||
"state": "OPEN"
|
"state": "OPEN"
|
||||||
}
|
}
|
||||||
|
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals/1/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals/1b7cae1585077104e99e1860ad740efe28ebd498dbf9988e0e7b299e720c5377/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 1,
|
"proposal_id": "1b7cae1585077104e99e1860ad740efe28ebd498dbf9988e0e7b299e720c5377",
|
||||||
"proposer_id": 0,
|
"proposer_id": 0,
|
||||||
"state": "ACCEPTED"
|
"state": "ACCEPTED"
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,21 +61,21 @@ To limit the scope of key compromise, members of the consortium can refresh the
|
||||||
|
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @rekey_ledger.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @rekey_ledger.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 4,
|
"proposal_id": "2f739d154b8cddacd7fc6d03cc8d4d20626e477ec4b1af10a74c670bb38bed5e",
|
||||||
"proposer_id": 1,
|
"proposer_id": 1,
|
||||||
"state": "OPEN"
|
"state": "OPEN"
|
||||||
}
|
}
|
||||||
|
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals/4/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_accept_1.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals/2f739d154b8cddacd7fc6d03cc8d4d20626e477ec4b1af10a74c670bb38bed5e/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_accept_1.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 4,
|
"proposal_id": "2f739d154b8cddacd7fc6d03cc8d4d20626e477ec4b1af10a74c670bb38bed5e",
|
||||||
"proposer_id": 1,
|
"proposer_id": 1,
|
||||||
"state": "OPEN"
|
"state": "OPEN"
|
||||||
}
|
}
|
||||||
|
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals/4/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept_1.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals/2f739d154b8cddacd7fc6d03cc8d4d20626e477ec4b1af10a74c670bb38bed5e/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept_1.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 4,
|
"proposal_id": "2f739d154b8cddacd7fc6d03cc8d4d20626e477ec4b1af10a74c670bb38bed5e",
|
||||||
"proposer_id": 1,
|
"proposer_id": 1,
|
||||||
"state": "ACCEPTED"
|
"state": "ACCEPTED"
|
||||||
}
|
}
|
||||||
|
@ -103,21 +103,21 @@ The number of member shares required to restore the private ledger (``recovery_t
|
||||||
|
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @set_recovery_threshold.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @set_recovery_threshold.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 5,
|
"proposal_id": "b9c08b3861395eca904d913427dcb436136e277cf4712eb14e9e9cddf9d231a8",
|
||||||
"proposer_id": 1,
|
"proposer_id": 1,
|
||||||
"state": "OPEN"
|
"state": "OPEN"
|
||||||
}
|
}
|
||||||
|
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals/5/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_accept_1.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals/b9c08b3861395eca904d913427dcb436136e277cf4712eb14e9e9cddf9d231a8/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_accept_1.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 5,
|
"proposal_id": "b9c08b3861395eca904d913427dcb436136e277cf4712eb14e9e9cddf9d231a8",
|
||||||
"proposer_id": 1,
|
"proposer_id": 1,
|
||||||
"state": "OPEN"
|
"state": "OPEN"
|
||||||
}
|
}
|
||||||
|
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals/5/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept_1.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals/b9c08b3861395eca904d913427dcb436136e277cf4712eb14e9e9cddf9d231a8/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept_1.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 5,
|
"proposal_id": "b9c08b3861395eca904d913427dcb436136e277cf4712eb14e9e9cddf9d231a8",
|
||||||
"proposer_id": 1,
|
"proposer_id": 1,
|
||||||
"state": "ACCEPTED"
|
"state": "ACCEPTED"
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ Then, the certificates of trusted users should be registered in CCF via the memb
|
||||||
|
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member0_privk --cert member0_cert --data-binary @add_user.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member0_privk --cert member0_cert --data-binary @add_user.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 5,
|
"proposal_id": "f665047e3d1eb184a7b7921944a8ab543cfff117aab5b6358dc87f9e70278253",
|
||||||
"proposer_id": 0,
|
"proposer_id": 0,
|
||||||
"state": "OPEN"
|
"state": "OPEN"
|
||||||
}
|
}
|
||||||
|
@ -38,9 +38,9 @@ Other members are then allowed to vote for the proposal, using the proposal id r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals/5/votes --cacert network_cert --key member1_privk --cert member1_cert --data-binary @vote_accept.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals/f665047e3d1eb184a7b7921944a8ab543cfff117aab5b6358dc87f9e70278253/votes --cacert network_cert --key member1_privk --cert member1_cert --data-binary @vote_accept.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 5,
|
"proposal_id": "f665047e3d1eb184a7b7921944a8ab543cfff117aab5b6358dc87f9e70278253",
|
||||||
"proposer_id": 0,
|
"proposer_id": 0,
|
||||||
"state": "OPEN"
|
"state": "OPEN"
|
||||||
}
|
}
|
||||||
|
@ -52,9 +52,9 @@ Other members are then allowed to vote for the proposal, using the proposal id r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals/5/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_conditional.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals/f665047e3d1eb184a7b7921944a8ab543cfff117aab5b6358dc87f9e70278253/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_conditional.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 5,
|
"proposal_id": "f665047e3d1eb184a7b7921944a8ab543cfff117aab5b6358dc87f9e70278253",
|
||||||
"proposer_id": 0,
|
"proposer_id": 0,
|
||||||
"state": "ACCEPTED"
|
"state": "ACCEPTED"
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ Once users are added to the opening network, members should create a proposal to
|
||||||
|
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member0_privk --cert member0_cert --data-binary @open_network.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member0_privk --cert member0_cert --data-binary @open_network.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 10,
|
"proposal_id": "77374e16de0b2d61f58aec84d01e6218205d19c9401d2df127d893ce62576b81",
|
||||||
"proposer_id": 0,
|
"proposer_id": 0,
|
||||||
"state": "OPEN"
|
"state": "OPEN"
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,7 @@ For example, ``member1`` may submit a proposal to add a new member (``member4``)
|
||||||
|
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @add_member.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals --cacert network_cert --key member1_privk --cert member1_cert --data-binary @add_member.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 4,
|
"proposal_id": "d4ec2de82267f97d3d1b464020af0bd3241f1bedf769f0fee73cd00f08e9c7fd",
|
||||||
"proposer_id": 1,
|
"proposer_id": 1,
|
||||||
"state": "OPEN"
|
"state": "OPEN"
|
||||||
}
|
}
|
||||||
|
@ -138,17 +138,17 @@ In this case, a new proposal with id ``4`` has successfully been created and the
|
||||||
}
|
}
|
||||||
|
|
||||||
# Member 2 rejects the proposal (votes in favour: 1/3)
|
# Member 2 rejects the proposal (votes in favour: 1/3)
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals/4/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_reject.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals/d4ec2de82267f97d3d1b464020af0bd3241f1bedf769f0fee73cd00f08e9c7fd/votes --cacert network_cert --key member2_privk --cert member2_cert --data-binary @vote_reject.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 4,
|
"proposal_id": "d4ec2de82267f97d3d1b464020af0bd3241f1bedf769f0fee73cd00f08e9c7fd",
|
||||||
"proposer_id": 1,
|
"proposer_id": 1,
|
||||||
"state": "OPEN"
|
"state": "OPEN"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Member 3 accepts the proposal (votes in favour: 2/3)
|
# Member 3 accepts the proposal (votes in favour: 2/3)
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals/4/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept.json -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals/d4ec2de82267f97d3d1b464020af0bd3241f1bedf769f0fee73cd00f08e9c7fd/votes --cacert network_cert --key member3_privk --cert member3_cert --data-binary @vote_accept.json -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 4,
|
"proposal_id": "d4ec2de82267f97d3d1b464020af0bd3241f1bedf769f0fee73cd00f08e9c7fd",
|
||||||
"proposer_id": 1,
|
"proposer_id": 1,
|
||||||
"state": "ACCEPTED"
|
"state": "ACCEPTED"
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ The details of pending proposals, including the proposer member id, proposal scr
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
# The full proposal state, including votes, can still be retrieved by any member
|
# The full proposal state, including votes, can still be retrieved by any member
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals/4 --cacert networkcert.pem --key member3_privk.pem --cert member3_cert.pem -H "content-type: application/json" -X GET
|
$ scurl.sh https://<ccf-node-address>/gov/proposals/d4ec2de82267f97d3d1b464020af0bd3241f1bedf769f0fee73cd00f08e9c7fd --cacert networkcert.pem --key member3_privk.pem --cert member3_cert.pem -H "content-type: application/json" -X GET
|
||||||
{
|
{
|
||||||
"parameter": {...},
|
"parameter": {...},
|
||||||
"proposer": 1,
|
"proposer": 1,
|
||||||
|
@ -204,7 +204,7 @@ At any stage during the voting process, before the proposal is accepted, the pro
|
||||||
|
|
||||||
$ scurl.sh https://<ccf-node-address>/gov/proposals/<proposal-id>/withdraw --cacert networkcert.pem --key member1_privk.pem --cert member1_cert.pem -H "content-type: application/json"
|
$ scurl.sh https://<ccf-node-address>/gov/proposals/<proposal-id>/withdraw --cacert networkcert.pem --key member1_privk.pem --cert member1_cert.pem -H "content-type: application/json"
|
||||||
{
|
{
|
||||||
"proposal_id": 4,
|
"proposal_id": "d4ec2de82267f97d3d1b464020af0bd3241f1bedf769f0fee73cd00f08e9c7fd",
|
||||||
"proposer_id": 1,
|
"proposer_id": 1,
|
||||||
"state": "WITHDRAWN"
|
"state": "WITHDRAWN"
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,7 +173,7 @@
|
||||||
"ProposalInfo": {
|
"ProposalInfo": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"proposal_id": {
|
"proposal_id": {
|
||||||
"$ref": "#/components/schemas/uint64"
|
"$ref": "#/components/schemas/string"
|
||||||
},
|
},
|
||||||
"proposer_id": {
|
"proposer_id": {
|
||||||
"$ref": "#/components/schemas/uint64"
|
"$ref": "#/components/schemas/uint64"
|
||||||
|
|
|
@ -153,6 +153,7 @@ namespace ccf
|
||||||
tls::Pem member_cert;
|
tls::Pem member_cert;
|
||||||
nlohmann::json member_data;
|
nlohmann::json member_data;
|
||||||
SignedReq signed_request;
|
SignedReq signed_request;
|
||||||
|
std::vector<uint8_t> request_digest;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MemberSignatureAuthnPolicy : public AuthnPolicy
|
class MemberSignatureAuthnPolicy : public AuthnPolicy
|
||||||
|
@ -187,15 +188,20 @@ namespace ccf
|
||||||
"Members and member certs tables do not match");
|
"Members and member certs tables do not match");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> digest;
|
||||||
auto verifier = verifiers.get_verifier(member->cert);
|
auto verifier = verifiers.get_verifier(member->cert);
|
||||||
if (verifier->verify(
|
if (verifier->verify(
|
||||||
signed_request->req, signed_request->sig, signed_request->md))
|
signed_request->req,
|
||||||
|
signed_request->sig,
|
||||||
|
signed_request->md,
|
||||||
|
digest))
|
||||||
{
|
{
|
||||||
auto identity = std::make_unique<MemberSignatureAuthnIdentity>();
|
auto identity = std::make_unique<MemberSignatureAuthnIdentity>();
|
||||||
identity->member_id = member_id.value();
|
identity->member_id = member_id.value();
|
||||||
identity->member_cert = member->cert;
|
identity->member_cert = member->cert;
|
||||||
identity->member_data = member->member_data;
|
identity->member_data = member->member_data;
|
||||||
identity->signed_request = signed_request.value();
|
identity->signed_request = signed_request.value();
|
||||||
|
identity->request_digest = std::move(digest);
|
||||||
return identity;
|
return identity;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -93,11 +93,12 @@ namespace ccf
|
||||||
DECLARE_JSON_REQUIRED_FIELDS(
|
DECLARE_JSON_REQUIRED_FIELDS(
|
||||||
Proposal, script, parameter, proposer, state, votes)
|
Proposal, script, parameter, proposer, state, votes)
|
||||||
|
|
||||||
using Proposals = kv::Map<ObjectId, Proposal>;
|
using ProposalId = std::string;
|
||||||
|
using Proposals = kv::Map<ProposalId, Proposal>;
|
||||||
|
|
||||||
struct ProposalInfo
|
struct ProposalInfo
|
||||||
{
|
{
|
||||||
ObjectId proposal_id;
|
ProposalId proposal_id;
|
||||||
MemberId proposer_id;
|
MemberId proposer_id;
|
||||||
ProposalState state;
|
ProposalState state;
|
||||||
};
|
};
|
||||||
|
|
|
@ -505,6 +505,24 @@ namespace ccf
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool get_path_param(
|
||||||
|
const enclave::PathParams& params,
|
||||||
|
const std::string& param_name,
|
||||||
|
std::string& value,
|
||||||
|
std::string& error)
|
||||||
|
{
|
||||||
|
const auto it = params.find(param_name);
|
||||||
|
if (it == params.end())
|
||||||
|
{
|
||||||
|
error = fmt::format("No parameter named '{}' in path", param_name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = it->second;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
EndpointPtr default_endpoint;
|
EndpointPtr default_endpoint;
|
||||||
std::map<std::string, std::map<RESTVerb, EndpointPtr>>
|
std::map<std::string, std::map<RESTVerb, EndpointPtr>>
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
|
|
||||||
namespace ccf
|
namespace ccf
|
||||||
{
|
{
|
||||||
|
constexpr auto INVALID_PROPOSAL_ID = "INVALID";
|
||||||
|
|
||||||
static oe_result_t oe_verify_attestation_certificate_with_evidence_cb(
|
static oe_result_t oe_verify_attestation_certificate_with_evidence_cb(
|
||||||
oe_claim_t* claims, size_t claims_length, void* arg)
|
oe_claim_t* claims, size_t claims_length, void* arg)
|
||||||
{
|
{
|
||||||
|
@ -356,7 +358,7 @@ namespace ccf
|
||||||
|
|
||||||
bool set_jwt_public_signing_keys(
|
bool set_jwt_public_signing_keys(
|
||||||
kv::Tx& tx,
|
kv::Tx& tx,
|
||||||
ObjectId proposal_id,
|
const ProposalId& proposal_id,
|
||||||
std::string issuer,
|
std::string issuer,
|
||||||
const JwtIssuerMetadata& issuer_metadata,
|
const JwtIssuerMetadata& issuer_metadata,
|
||||||
const JsonWebKeySet& jwks)
|
const JsonWebKeySet& jwks)
|
||||||
|
@ -365,9 +367,9 @@ namespace ccf
|
||||||
auto key_issuer =
|
auto key_issuer =
|
||||||
tx.get_view(this->network.jwt_public_signing_key_issuer);
|
tx.get_view(this->network.jwt_public_signing_key_issuer);
|
||||||
|
|
||||||
auto log_prefix = proposal_id != INVALID_ID ?
|
auto log_prefix = proposal_id == INVALID_PROPOSAL_ID ?
|
||||||
fmt::format("Proposal {}", proposal_id) :
|
"JWT key auto-refresh" :
|
||||||
"JWT key auto-refresh";
|
fmt::format("Proposal {}", proposal_id);
|
||||||
|
|
||||||
// add keys
|
// add keys
|
||||||
if (jwks.keys.empty())
|
if (jwks.keys.empty())
|
||||||
|
@ -515,7 +517,7 @@ namespace ccf
|
||||||
kv::Tx& tx,
|
kv::Tx& tx,
|
||||||
const CodeDigest& new_code_id,
|
const CodeDigest& new_code_id,
|
||||||
CodeIDs& code_id_table,
|
CodeIDs& code_id_table,
|
||||||
ObjectId proposal_id)
|
const ProposalId& proposal_id)
|
||||||
{
|
{
|
||||||
auto code_ids = tx.get_view(code_id_table);
|
auto code_ids = tx.get_view(code_id_table);
|
||||||
auto existing_code_id = code_ids->get(new_code_id);
|
auto existing_code_id = code_ids->get(new_code_id);
|
||||||
|
@ -535,7 +537,7 @@ namespace ccf
|
||||||
kv::Tx& tx,
|
kv::Tx& tx,
|
||||||
const CodeDigest& code_id,
|
const CodeDigest& code_id,
|
||||||
CodeIDs& code_id_table,
|
CodeIDs& code_id_table,
|
||||||
ObjectId proposal_id)
|
const ProposalId& proposal_id)
|
||||||
{
|
{
|
||||||
auto code_ids = tx.get_view(code_id_table);
|
auto code_ids = tx.get_view(code_id_table);
|
||||||
auto existing_code_id = code_ids->get(code_id);
|
auto existing_code_id = code_ids->get(code_id);
|
||||||
|
@ -554,41 +556,41 @@ namespace ccf
|
||||||
//! Table of functions that proposal scripts can propose to invoke
|
//! Table of functions that proposal scripts can propose to invoke
|
||||||
const std::unordered_map<
|
const std::unordered_map<
|
||||||
std::string,
|
std::string,
|
||||||
std::function<bool(ObjectId, kv::Tx&, const nlohmann::json&)>>
|
std::function<bool(const ProposalId&, kv::Tx&, const nlohmann::json&)>>
|
||||||
hardcoded_funcs = {
|
hardcoded_funcs = {
|
||||||
// set the js application script
|
// set the js application script
|
||||||
{"set_js_app",
|
{"set_js_app",
|
||||||
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) {
|
[this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
|
||||||
const std::string app = args;
|
const std::string app = args;
|
||||||
set_js_scripts(tx, lua::Interpreter().invoke<nlohmann::json>(app));
|
set_js_scripts(tx, lua::Interpreter().invoke<nlohmann::json>(app));
|
||||||
return true;
|
return true;
|
||||||
}},
|
}},
|
||||||
// deploy the js application bundle
|
// deploy the js application bundle
|
||||||
{"deploy_js_app",
|
{"deploy_js_app",
|
||||||
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) {
|
[this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
|
||||||
const auto parsed = args.get<DeployJsApp>();
|
const auto parsed = args.get<DeployJsApp>();
|
||||||
return deploy_js_app(tx, parsed.bundle);
|
return deploy_js_app(tx, parsed.bundle);
|
||||||
}},
|
}},
|
||||||
// undeploy/remove the js application
|
// undeploy/remove the js application
|
||||||
{"remove_js_app",
|
{"remove_js_app",
|
||||||
[this](ObjectId, kv::Tx& tx, const nlohmann::json&) {
|
[this](const ProposalId&, kv::Tx& tx, const nlohmann::json&) {
|
||||||
return remove_js_app(tx);
|
return remove_js_app(tx);
|
||||||
}},
|
}},
|
||||||
// add/update a module
|
// add/update a module
|
||||||
{"set_module",
|
{"set_module",
|
||||||
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) {
|
[this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
|
||||||
const auto parsed = args.get<SetModule>();
|
const auto parsed = args.get<SetModule>();
|
||||||
return set_module(tx, parsed.name, parsed.module);
|
return set_module(tx, parsed.name, parsed.module);
|
||||||
}},
|
}},
|
||||||
// remove a module
|
// remove a module
|
||||||
{"remove_module",
|
{"remove_module",
|
||||||
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) {
|
[this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
|
||||||
const auto name = args.get<std::string>();
|
const auto name = args.get<std::string>();
|
||||||
return remove_module(tx, name);
|
return remove_module(tx, name);
|
||||||
}},
|
}},
|
||||||
// add a new member
|
// add a new member
|
||||||
{"new_member",
|
{"new_member",
|
||||||
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) {
|
[this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
|
||||||
const auto parsed = args.get<MemberPubInfo>();
|
const auto parsed = args.get<MemberPubInfo>();
|
||||||
GenesisGenerator g(this->network, tx);
|
GenesisGenerator g(this->network, tx);
|
||||||
g.add_member(parsed);
|
g.add_member(parsed);
|
||||||
|
@ -597,7 +599,7 @@ namespace ccf
|
||||||
}},
|
}},
|
||||||
// retire an existing member
|
// retire an existing member
|
||||||
{"retire_member",
|
{"retire_member",
|
||||||
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) {
|
[this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
|
||||||
const auto member_id = args.get<MemberId>();
|
const auto member_id = args.get<MemberId>();
|
||||||
|
|
||||||
GenesisGenerator g(this->network, tx);
|
GenesisGenerator g(this->network, tx);
|
||||||
|
@ -629,7 +631,10 @@ namespace ccf
|
||||||
return true;
|
return true;
|
||||||
}},
|
}},
|
||||||
{"set_member_data",
|
{"set_member_data",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) {
|
[this](
|
||||||
|
const ProposalId& proposal_id,
|
||||||
|
kv::Tx& tx,
|
||||||
|
const nlohmann::json& args) {
|
||||||
const auto parsed = args.get<SetMemberData>();
|
const auto parsed = args.get<SetMemberData>();
|
||||||
auto members_view = tx.get_view(this->network.members);
|
auto members_view = tx.get_view(this->network.members);
|
||||||
auto member_info = members_view->get(parsed.member_id);
|
auto member_info = members_view->get(parsed.member_id);
|
||||||
|
@ -647,7 +652,7 @@ namespace ccf
|
||||||
return true;
|
return true;
|
||||||
}},
|
}},
|
||||||
{"new_user",
|
{"new_user",
|
||||||
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) {
|
[this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
|
||||||
const auto user_info = args.get<ccf::UserInfo>();
|
const auto user_info = args.get<ccf::UserInfo>();
|
||||||
|
|
||||||
GenesisGenerator g(this->network, tx);
|
GenesisGenerator g(this->network, tx);
|
||||||
|
@ -656,7 +661,10 @@ namespace ccf
|
||||||
return true;
|
return true;
|
||||||
}},
|
}},
|
||||||
{"remove_user",
|
{"remove_user",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) {
|
[this](
|
||||||
|
const ProposalId& proposal_id,
|
||||||
|
kv::Tx& tx,
|
||||||
|
const nlohmann::json& args) {
|
||||||
const UserId user_id = args;
|
const UserId user_id = args;
|
||||||
|
|
||||||
GenesisGenerator g(this->network, tx);
|
GenesisGenerator g(this->network, tx);
|
||||||
|
@ -670,7 +678,10 @@ namespace ccf
|
||||||
return r;
|
return r;
|
||||||
}},
|
}},
|
||||||
{"set_user_data",
|
{"set_user_data",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) {
|
[this](
|
||||||
|
const ProposalId& proposal_id,
|
||||||
|
kv::Tx& tx,
|
||||||
|
const nlohmann::json& args) {
|
||||||
const auto parsed = args.get<SetUserData>();
|
const auto parsed = args.get<SetUserData>();
|
||||||
auto users_view = tx.get_view(this->network.users);
|
auto users_view = tx.get_view(this->network.users);
|
||||||
auto user_info = users_view->get(parsed.user_id);
|
auto user_info = users_view->get(parsed.user_id);
|
||||||
|
@ -688,7 +699,10 @@ namespace ccf
|
||||||
return true;
|
return true;
|
||||||
}},
|
}},
|
||||||
{"set_ca_cert",
|
{"set_ca_cert",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) {
|
[this](
|
||||||
|
const ProposalId& proposal_id,
|
||||||
|
kv::Tx& tx,
|
||||||
|
const nlohmann::json& args) {
|
||||||
const auto parsed = args.get<SetCaCert>();
|
const auto parsed = args.get<SetCaCert>();
|
||||||
auto ca_certs = tx.get_view(this->network.ca_certs);
|
auto ca_certs = tx.get_view(this->network.ca_certs);
|
||||||
std::vector<uint8_t> cert_der;
|
std::vector<uint8_t> cert_der;
|
||||||
|
@ -709,14 +723,17 @@ namespace ccf
|
||||||
return true;
|
return true;
|
||||||
}},
|
}},
|
||||||
{"remove_ca_cert",
|
{"remove_ca_cert",
|
||||||
[this](ObjectId, kv::Tx& tx, const nlohmann::json& args) {
|
[this](const ProposalId&, kv::Tx& tx, const nlohmann::json& args) {
|
||||||
const auto cert_name = args.get<std::string>();
|
const auto cert_name = args.get<std::string>();
|
||||||
auto ca_certs = tx.get_view(this->network.ca_certs);
|
auto ca_certs = tx.get_view(this->network.ca_certs);
|
||||||
ca_certs->remove(cert_name);
|
ca_certs->remove(cert_name);
|
||||||
return true;
|
return true;
|
||||||
}},
|
}},
|
||||||
{"set_jwt_issuer",
|
{"set_jwt_issuer",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) {
|
[this](
|
||||||
|
const ProposalId& proposal_id,
|
||||||
|
kv::Tx& tx,
|
||||||
|
const nlohmann::json& args) {
|
||||||
const auto parsed = args.get<SetJwtIssuer>();
|
const auto parsed = args.get<SetJwtIssuer>();
|
||||||
auto issuers = tx.get_view(this->network.jwt_issuers);
|
auto issuers = tx.get_view(this->network.jwt_issuers);
|
||||||
auto ca_certs = tx.get_read_only_view(this->network.ca_certs);
|
auto ca_certs = tx.get_read_only_view(this->network.ca_certs);
|
||||||
|
@ -783,7 +800,10 @@ namespace ccf
|
||||||
return success;
|
return success;
|
||||||
}},
|
}},
|
||||||
{"remove_jwt_issuer",
|
{"remove_jwt_issuer",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) {
|
[this](
|
||||||
|
const ProposalId& proposal_id,
|
||||||
|
kv::Tx& tx,
|
||||||
|
const nlohmann::json& args) {
|
||||||
const auto parsed = args.get<RemoveJwtIssuer>();
|
const auto parsed = args.get<RemoveJwtIssuer>();
|
||||||
const auto issuer = parsed.issuer;
|
const auto issuer = parsed.issuer;
|
||||||
auto issuers = tx.get_view(this->network.jwt_issuers);
|
auto issuers = tx.get_view(this->network.jwt_issuers);
|
||||||
|
@ -800,7 +820,10 @@ namespace ccf
|
||||||
return true;
|
return true;
|
||||||
}},
|
}},
|
||||||
{"set_jwt_public_signing_keys",
|
{"set_jwt_public_signing_keys",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) {
|
[this](
|
||||||
|
const ProposalId& proposal_id,
|
||||||
|
kv::Tx& tx,
|
||||||
|
const nlohmann::json& args) {
|
||||||
const auto parsed = args.get<SetJwtPublicSigningKeys>();
|
const auto parsed = args.get<SetJwtPublicSigningKeys>();
|
||||||
|
|
||||||
auto issuers = tx.get_view(this->network.jwt_issuers);
|
auto issuers = tx.get_view(this->network.jwt_issuers);
|
||||||
|
@ -820,7 +843,10 @@ namespace ccf
|
||||||
}},
|
}},
|
||||||
// accept a node
|
// accept a node
|
||||||
{"trust_node",
|
{"trust_node",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) {
|
[this](
|
||||||
|
const ProposalId& proposal_id,
|
||||||
|
kv::Tx& tx,
|
||||||
|
const nlohmann::json& args) {
|
||||||
const auto node_id = args.get<NodeId>();
|
const auto node_id = args.get<NodeId>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -837,7 +863,10 @@ namespace ccf
|
||||||
}},
|
}},
|
||||||
// retire a node
|
// retire a node
|
||||||
{"retire_node",
|
{"retire_node",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) {
|
[this](
|
||||||
|
const ProposalId& proposal_id,
|
||||||
|
kv::Tx& tx,
|
||||||
|
const nlohmann::json& args) {
|
||||||
const auto id = args.get<NodeId>();
|
const auto id = args.get<NodeId>();
|
||||||
auto nodes = tx.get_view(this->network.nodes);
|
auto nodes = tx.get_view(this->network.nodes);
|
||||||
auto node_info = nodes->get(id);
|
auto node_info = nodes->get(id);
|
||||||
|
@ -860,7 +889,10 @@ namespace ccf
|
||||||
}},
|
}},
|
||||||
// accept new node code ID
|
// accept new node code ID
|
||||||
{"new_node_code",
|
{"new_node_code",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) {
|
[this](
|
||||||
|
const ProposalId& proposal_id,
|
||||||
|
kv::Tx& tx,
|
||||||
|
const nlohmann::json& args) {
|
||||||
return this->add_new_code_id(
|
return this->add_new_code_id(
|
||||||
tx,
|
tx,
|
||||||
args.get<CodeDigest>(),
|
args.get<CodeDigest>(),
|
||||||
|
@ -869,7 +901,10 @@ namespace ccf
|
||||||
}},
|
}},
|
||||||
// retire node code ID
|
// retire node code ID
|
||||||
{"retire_node_code",
|
{"retire_node_code",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) {
|
[this](
|
||||||
|
const ProposalId& proposal_id,
|
||||||
|
kv::Tx& tx,
|
||||||
|
const nlohmann::json& args) {
|
||||||
return this->retire_code_id(
|
return this->retire_code_id(
|
||||||
tx,
|
tx,
|
||||||
args.get<CodeDigest>(),
|
args.get<CodeDigest>(),
|
||||||
|
@ -877,7 +912,8 @@ namespace ccf
|
||||||
proposal_id);
|
proposal_id);
|
||||||
}},
|
}},
|
||||||
{"accept_recovery",
|
{"accept_recovery",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json&) {
|
[this](
|
||||||
|
const ProposalId& proposal_id, kv::Tx& tx, const nlohmann::json&) {
|
||||||
if (node.is_part_of_public_network())
|
if (node.is_part_of_public_network())
|
||||||
{
|
{
|
||||||
const auto accept_recovery = node.accept_recovery(tx);
|
const auto accept_recovery = node.accept_recovery(tx);
|
||||||
|
@ -895,7 +931,8 @@ namespace ccf
|
||||||
}
|
}
|
||||||
}},
|
}},
|
||||||
{"open_network",
|
{"open_network",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json&) {
|
[this](
|
||||||
|
const ProposalId& proposal_id, kv::Tx& tx, const nlohmann::json&) {
|
||||||
// On network open, the service checks that a sufficient number of
|
// On network open, the service checks that a sufficient number of
|
||||||
// recovery members have become active. If so, recovery shares are
|
// recovery members have become active. If so, recovery shares are
|
||||||
// allocated to each recovery member
|
// allocated to each recovery member
|
||||||
|
@ -926,7 +963,8 @@ namespace ccf
|
||||||
return network_opened;
|
return network_opened;
|
||||||
}},
|
}},
|
||||||
{"rekey_ledger",
|
{"rekey_ledger",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json&) {
|
[this](
|
||||||
|
const ProposalId& proposal_id, kv::Tx& tx, const nlohmann::json&) {
|
||||||
const auto ledger_rekeyed = node.rekey_ledger(tx);
|
const auto ledger_rekeyed = node.rekey_ledger(tx);
|
||||||
if (!ledger_rekeyed)
|
if (!ledger_rekeyed)
|
||||||
{
|
{
|
||||||
|
@ -935,7 +973,8 @@ namespace ccf
|
||||||
return ledger_rekeyed;
|
return ledger_rekeyed;
|
||||||
}},
|
}},
|
||||||
{"update_recovery_shares",
|
{"update_recovery_shares",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json&) {
|
[this](
|
||||||
|
const ProposalId& proposal_id, kv::Tx& tx, const nlohmann::json&) {
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
share_manager.issue_shares(tx);
|
share_manager.issue_shares(tx);
|
||||||
|
@ -951,7 +990,10 @@ namespace ccf
|
||||||
return true;
|
return true;
|
||||||
}},
|
}},
|
||||||
{"set_recovery_threshold",
|
{"set_recovery_threshold",
|
||||||
[this](ObjectId proposal_id, kv::Tx& tx, const nlohmann::json& args) {
|
[this](
|
||||||
|
const ProposalId& proposal_id,
|
||||||
|
kv::Tx& tx,
|
||||||
|
const nlohmann::json& args) {
|
||||||
const auto new_recovery_threshold = args.get<size_t>();
|
const auto new_recovery_threshold = args.get<size_t>();
|
||||||
|
|
||||||
GenesisGenerator g(this->network, tx);
|
GenesisGenerator g(this->network, tx);
|
||||||
|
@ -986,7 +1028,7 @@ namespace ccf
|
||||||
};
|
};
|
||||||
|
|
||||||
ProposalInfo complete_proposal(
|
ProposalInfo complete_proposal(
|
||||||
kv::Tx& tx, const ObjectId proposal_id, Proposal& proposal)
|
kv::Tx& tx, const ProposalId& proposal_id, Proposal& proposal)
|
||||||
{
|
{
|
||||||
if (proposal.state != ProposalState::OPEN)
|
if (proposal.state != ProposalState::OPEN)
|
||||||
{
|
{
|
||||||
|
@ -1144,14 +1186,14 @@ namespace ccf
|
||||||
}
|
}
|
||||||
|
|
||||||
static ProposalInfo get_proposal_info(
|
static ProposalInfo get_proposal_info(
|
||||||
ObjectId proposal_id, const Proposal& proposal)
|
const ProposalId& proposal_id, const Proposal& proposal)
|
||||||
{
|
{
|
||||||
return ProposalInfo{proposal_id, proposal.proposer, proposal.state};
|
return ProposalInfo{proposal_id, proposal.proposer, proposal.state};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get_proposal_id_from_path(
|
bool get_proposal_id_from_path(
|
||||||
const enclave::PathParams& params,
|
const enclave::PathParams& params,
|
||||||
ObjectId& proposal_id,
|
ProposalId& proposal_id,
|
||||||
std::string& error)
|
std::string& error)
|
||||||
{
|
{
|
||||||
return get_path_param(params, "proposal_id", proposal_id, error);
|
return get_path_param(params, "proposal_id", proposal_id, error);
|
||||||
|
@ -1292,8 +1334,9 @@ namespace ccf
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto in = params.get<Propose::In>();
|
const auto in = params.get<Propose::In>();
|
||||||
const auto proposal_id = get_next_id(
|
const auto proposal_id =
|
||||||
ctx.tx.get_view(this->network.values), ValueIds::NEXT_PROPOSAL_ID);
|
fmt::format("{:02x}", fmt::join(caller_identity.request_digest, ""));
|
||||||
|
|
||||||
Proposal proposal(in.script, in.parameter, caller_identity.member_id);
|
Proposal proposal(in.script, in.parameter, caller_identity.member_id);
|
||||||
|
|
||||||
auto proposals = ctx.tx.get_view(this->network.proposals);
|
auto proposals = ctx.tx.get_view(this->network.proposals);
|
||||||
|
@ -1321,7 +1364,7 @@ namespace ccf
|
||||||
"Member is not active.");
|
"Member is not active.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectId proposal_id;
|
ProposalId proposal_id;
|
||||||
std::string error;
|
std::string error;
|
||||||
if (!get_proposal_id_from_path(
|
if (!get_proposal_id_from_path(
|
||||||
ctx.rpc_ctx->get_request_path_params(), proposal_id, error))
|
ctx.rpc_ctx->get_request_path_params(), proposal_id, error))
|
||||||
|
@ -1362,7 +1405,7 @@ namespace ccf
|
||||||
"Member is not active.");
|
"Member is not active.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectId proposal_id;
|
ProposalId proposal_id;
|
||||||
std::string error;
|
std::string error;
|
||||||
if (!get_proposal_id_from_path(
|
if (!get_proposal_id_from_path(
|
||||||
ctx.rpc_ctx->get_request_path_params(), proposal_id, error))
|
ctx.rpc_ctx->get_request_path_params(), proposal_id, error))
|
||||||
|
@ -1435,7 +1478,7 @@ namespace ccf
|
||||||
"Member is not active.");
|
"Member is not active.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectId proposal_id;
|
ProposalId proposal_id;
|
||||||
std::string error;
|
std::string error;
|
||||||
if (!get_proposal_id_from_path(
|
if (!get_proposal_id_from_path(
|
||||||
ctx.rpc_ctx->get_request_path_params(), proposal_id, error))
|
ctx.rpc_ctx->get_request_path_params(), proposal_id, error))
|
||||||
|
@ -1505,7 +1548,7 @@ namespace ccf
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string error;
|
std::string error;
|
||||||
ObjectId proposal_id;
|
ProposalId proposal_id;
|
||||||
if (!get_proposal_id_from_path(
|
if (!get_proposal_id_from_path(
|
||||||
ctx.rpc_ctx->get_request_path_params(), proposal_id, error))
|
ctx.rpc_ctx->get_request_path_params(), proposal_id, error))
|
||||||
{
|
{
|
||||||
|
@ -1967,7 +2010,11 @@ namespace ccf
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!set_jwt_public_signing_keys(
|
if (!set_jwt_public_signing_keys(
|
||||||
ctx.tx, INVALID_ID, parsed.issuer, issuer_metadata, parsed.jwks))
|
ctx.tx,
|
||||||
|
INVALID_PROPOSAL_ID,
|
||||||
|
parsed.issuer,
|
||||||
|
issuer_metadata,
|
||||||
|
parsed.jwks))
|
||||||
{
|
{
|
||||||
LOG_FAIL_FMT(
|
LOG_FAIL_FMT(
|
||||||
"JWT key auto-refresh: error while storing signing keys for issuer "
|
"JWT key auto-refresh: error while storing signing keys for issuer "
|
||||||
|
|
|
@ -169,7 +169,9 @@ auto frontend_process(
|
||||||
}
|
}
|
||||||
|
|
||||||
auto get_proposal(
|
auto get_proposal(
|
||||||
MemberRpcFrontend& frontend, size_t proposal_id, const tls::Pem& caller)
|
MemberRpcFrontend& frontend,
|
||||||
|
const ProposalId& proposal_id,
|
||||||
|
const tls::Pem& caller)
|
||||||
{
|
{
|
||||||
const auto getter =
|
const auto getter =
|
||||||
create_request(nullptr, fmt::format("proposals/{}", proposal_id), HTTP_GET);
|
create_request(nullptr, fmt::format("proposals/{}", proposal_id), HTTP_GET);
|
||||||
|
@ -180,7 +182,7 @@ auto get_proposal(
|
||||||
|
|
||||||
auto get_vote(
|
auto get_vote(
|
||||||
MemberRpcFrontend& frontend,
|
MemberRpcFrontend& frontend,
|
||||||
size_t proposal_id,
|
ProposalId proposal_id,
|
||||||
MemberId voter,
|
MemberId voter,
|
||||||
const tls::Pem& caller)
|
const tls::Pem& caller)
|
||||||
{
|
{
|
||||||
|
@ -366,7 +368,7 @@ DOCTEST_TEST_CASE("Proposer ballot")
|
||||||
MemberRpcFrontend frontend(network, node, share_manager);
|
MemberRpcFrontend frontend(network, node, share_manager);
|
||||||
frontend.open();
|
frontend.open();
|
||||||
|
|
||||||
size_t proposal_id;
|
ProposalId proposal_id;
|
||||||
|
|
||||||
const ccf::Script vote_for("return true");
|
const ccf::Script vote_for("return true");
|
||||||
const ccf::Script vote_against("return false");
|
const ccf::Script vote_against("return false");
|
||||||
|
@ -468,7 +470,7 @@ DOCTEST_TEST_CASE("Reject duplicate vote")
|
||||||
MemberRpcFrontend frontend(network, node, share_manager);
|
MemberRpcFrontend frontend(network, node, share_manager);
|
||||||
frontend.open();
|
frontend.open();
|
||||||
|
|
||||||
size_t proposal_id;
|
ProposalId proposal_id;
|
||||||
|
|
||||||
const ccf::Script vote_for("return true");
|
const ccf::Script vote_for("return true");
|
||||||
const ccf::Script vote_against("return false");
|
const ccf::Script vote_against("return false");
|
||||||
|
@ -573,7 +575,6 @@ DOCTEST_TEST_CASE("Add new members until there are 7 then reject")
|
||||||
auto i = 0ul;
|
auto i = 0ul;
|
||||||
for (auto& new_member : new_members)
|
for (auto& new_member : new_members)
|
||||||
{
|
{
|
||||||
const auto proposal_id = i;
|
|
||||||
new_member.id = initial_members + i++;
|
new_member.id = initial_members + i++;
|
||||||
|
|
||||||
// new member certificate
|
// new member certificate
|
||||||
|
@ -600,12 +601,13 @@ DOCTEST_TEST_CASE("Add new members until there are 7 then reject")
|
||||||
const auto propose =
|
const auto propose =
|
||||||
create_signed_request(proposal, "proposals", kp, member_cert);
|
create_signed_request(proposal, "proposals", kp, member_cert);
|
||||||
|
|
||||||
|
ProposalId proposal_id;
|
||||||
{
|
{
|
||||||
const auto r = frontend_process(frontend, propose, member_cert);
|
const auto r = frontend_process(frontend, propose, member_cert);
|
||||||
const auto result = parse_response_body<Propose::Out>(r);
|
const auto result = parse_response_body<Propose::Out>(r);
|
||||||
|
|
||||||
// the proposal should be accepted, but not succeed immediately
|
// the proposal should be accepted, but not succeed immediately
|
||||||
DOCTEST_CHECK(result.proposal_id == proposal_id);
|
proposal_id = result.proposal_id;
|
||||||
DOCTEST_CHECK(result.state == ProposalState::OPEN);
|
DOCTEST_CHECK(result.state == ProposalState::OPEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -801,7 +803,7 @@ DOCTEST_TEST_CASE("Accept node")
|
||||||
}
|
}
|
||||||
|
|
||||||
// m0 proposes adding new node
|
// m0 proposes adding new node
|
||||||
ObjectId trust_node_proposal_id;
|
ProposalId trust_node_proposal_id;
|
||||||
{
|
{
|
||||||
Script proposal(R"xxx(
|
Script proposal(R"xxx(
|
||||||
local tables, node_id = ...
|
local tables, node_id = ...
|
||||||
|
@ -855,7 +857,7 @@ DOCTEST_TEST_CASE("Accept node")
|
||||||
}
|
}
|
||||||
|
|
||||||
// m0 proposes retire node
|
// m0 proposes retire node
|
||||||
ObjectId retire_node_proposal_id;
|
ProposalId retire_node_proposal_id;
|
||||||
{
|
{
|
||||||
Script proposal(R"xxx(
|
Script proposal(R"xxx(
|
||||||
local tables, node_id = ...
|
local tables, node_id = ...
|
||||||
|
@ -986,7 +988,7 @@ ProposalInfo test_raw_writes(
|
||||||
}
|
}
|
||||||
|
|
||||||
// propose
|
// propose
|
||||||
const auto proposal_id = 0ul;
|
ProposalId proposal_id;
|
||||||
{
|
{
|
||||||
const uint8_t proposer_id = 0;
|
const uint8_t proposer_id = 0;
|
||||||
const auto propose =
|
const auto propose =
|
||||||
|
@ -997,7 +999,7 @@ ProposalInfo test_raw_writes(
|
||||||
const auto expected_state =
|
const auto expected_state =
|
||||||
(n_members == 1) ? ProposalState::ACCEPTED : ProposalState::OPEN;
|
(n_members == 1) ? ProposalState::ACCEPTED : ProposalState::OPEN;
|
||||||
DOCTEST_CHECK(r.state == expected_state);
|
DOCTEST_CHECK(r.state == expected_state);
|
||||||
DOCTEST_CHECK(r.proposal_id == proposal_id);
|
proposal_id = r.proposal_id;
|
||||||
if (r.state == ProposalState::ACCEPTED)
|
if (r.state == ProposalState::ACCEPTED)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -1162,7 +1164,7 @@ DOCTEST_TEST_CASE("Remove proposal")
|
||||||
gen.finalize();
|
gen.finalize();
|
||||||
MemberRpcFrontend frontend(network, node, share_manager);
|
MemberRpcFrontend frontend(network, node, share_manager);
|
||||||
frontend.open();
|
frontend.open();
|
||||||
auto proposal_id = 0;
|
ProposalId proposal_id;
|
||||||
auto wrong_proposal_id = 1;
|
auto wrong_proposal_id = 1;
|
||||||
ccf::Script proposal_script(R"xxx(
|
ccf::Script proposal_script(R"xxx(
|
||||||
local tables, param = ...
|
local tables, param = ...
|
||||||
|
@ -1182,7 +1184,7 @@ DOCTEST_TEST_CASE("Remove proposal")
|
||||||
const auto r = parse_response_body<Propose::Out>(
|
const auto r = parse_response_body<Propose::Out>(
|
||||||
frontend_process(frontend, propose, member_cert));
|
frontend_process(frontend, propose, member_cert));
|
||||||
|
|
||||||
DOCTEST_CHECK(r.proposal_id == proposal_id);
|
proposal_id = r.proposal_id;
|
||||||
DOCTEST_CHECK(r.state == ProposalState::OPEN);
|
DOCTEST_CHECK(r.state == ProposalState::OPEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1294,7 +1296,7 @@ DOCTEST_TEST_CASE("Vetoed proposal gets rejected")
|
||||||
{
|
{
|
||||||
DOCTEST_INFO("Check proposal was rejected");
|
DOCTEST_INFO("Check proposal was rejected");
|
||||||
|
|
||||||
const auto proposal = get_proposal(frontend, 0, voter_a_cert);
|
const auto proposal = get_proposal(frontend, r.proposal_id, voter_a_cert);
|
||||||
|
|
||||||
DOCTEST_CHECK(proposal.state == ProposalState::REJECTED);
|
DOCTEST_CHECK(proposal.state == ProposalState::REJECTED);
|
||||||
}
|
}
|
||||||
|
@ -1347,7 +1349,6 @@ DOCTEST_TEST_CASE("Add and remove user via proposed calls")
|
||||||
frontend_process(frontend, vote, member_cert));
|
frontend_process(frontend, vote, member_cert));
|
||||||
|
|
||||||
DOCTEST_CHECK(r.state == ProposalState::ACCEPTED);
|
DOCTEST_CHECK(r.state == ProposalState::ACCEPTED);
|
||||||
DOCTEST_CHECK(r.proposal_id == 0);
|
|
||||||
|
|
||||||
auto tx1 = network.tables->create_tx();
|
auto tx1 = network.tables->create_tx();
|
||||||
const auto uid = tx1.get_view(network.values)->get(ValueIds::NEXT_USER_ID);
|
const auto uid = tx1.get_view(network.values)->get(ValueIds::NEXT_USER_ID);
|
||||||
|
@ -1385,7 +1386,6 @@ DOCTEST_TEST_CASE("Add and remove user via proposed calls")
|
||||||
frontend_process(frontend, vote, member_cert));
|
frontend_process(frontend, vote, member_cert));
|
||||||
|
|
||||||
DOCTEST_CHECK(r.state == ProposalState::ACCEPTED);
|
DOCTEST_CHECK(r.state == ProposalState::ACCEPTED);
|
||||||
DOCTEST_CHECK(r.proposal_id == 1);
|
|
||||||
|
|
||||||
auto tx1 = network.tables->create_tx();
|
auto tx1 = network.tables->create_tx();
|
||||||
auto user = tx1.get_view(network.users)->get(0);
|
auto user = tx1.get_view(network.users)->get(0);
|
||||||
|
@ -1440,7 +1440,7 @@ DOCTEST_TEST_CASE(
|
||||||
MemberRpcFrontend frontend(network, node, share_manager);
|
MemberRpcFrontend frontend(network, node, share_manager);
|
||||||
frontend.open();
|
frontend.open();
|
||||||
|
|
||||||
size_t proposal_id;
|
ProposalId proposal_id;
|
||||||
size_t proposer_id = 1;
|
size_t proposer_id = 1;
|
||||||
size_t voter_id = 2;
|
size_t voter_id = 2;
|
||||||
|
|
||||||
|
@ -1578,7 +1578,7 @@ DOCTEST_TEST_CASE("Passing operator change" * doctest::test_suite("operator"))
|
||||||
MemberRpcFrontend frontend(network, node, share_manager);
|
MemberRpcFrontend frontend(network, node, share_manager);
|
||||||
frontend.open();
|
frontend.open();
|
||||||
|
|
||||||
size_t proposal_id;
|
ProposalId proposal_id;
|
||||||
|
|
||||||
const ccf::Script vote_for("return true");
|
const ccf::Script vote_for("return true");
|
||||||
const ccf::Script vote_against("return false");
|
const ccf::Script vote_against("return false");
|
||||||
|
@ -1759,7 +1759,7 @@ DOCTEST_TEST_CASE(
|
||||||
MemberRpcFrontend frontend(network, node, share_manager);
|
MemberRpcFrontend frontend(network, node, share_manager);
|
||||||
frontend.open();
|
frontend.open();
|
||||||
|
|
||||||
size_t proposal_id;
|
ProposalId proposal_id;
|
||||||
|
|
||||||
const ccf::Script vote_for("return true");
|
const ccf::Script vote_for("return true");
|
||||||
const ccf::Script vote_against("return false");
|
const ccf::Script vote_against("return false");
|
||||||
|
|
|
@ -121,6 +121,21 @@ namespace tls
|
||||||
md_type);
|
md_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool verify(
|
||||||
|
const std::vector<uint8_t>& contents,
|
||||||
|
const std::vector<uint8_t>& signature,
|
||||||
|
mbedtls_md_type_t md_type,
|
||||||
|
HashBytes& hash_bytes) const
|
||||||
|
{
|
||||||
|
return verify(
|
||||||
|
contents.data(),
|
||||||
|
contents.size(),
|
||||||
|
signature.data(),
|
||||||
|
signature.size(),
|
||||||
|
md_type,
|
||||||
|
hash_bytes);
|
||||||
|
}
|
||||||
|
|
||||||
bool verify(
|
bool verify(
|
||||||
const uint8_t* contents,
|
const uint8_t* contents,
|
||||||
size_t contents_size,
|
size_t contents_size,
|
||||||
|
@ -134,6 +149,18 @@ namespace tls
|
||||||
return verify_hash(hash, sig, sig_size, md_type);
|
return verify_hash(hash, sig, sig_size, md_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool verify(
|
||||||
|
const uint8_t* contents,
|
||||||
|
size_t contents_size,
|
||||||
|
const uint8_t* sig,
|
||||||
|
size_t sig_size,
|
||||||
|
mbedtls_md_type_t md_type,
|
||||||
|
HashBytes& hash_bytes) const
|
||||||
|
{
|
||||||
|
do_hash(cert->pk, contents, contents_size, hash_bytes, md_type);
|
||||||
|
return verify_hash(hash_bytes, sig, sig_size, md_type);
|
||||||
|
}
|
||||||
|
|
||||||
const mbedtls_x509_crt* raw()
|
const mbedtls_x509_crt* raw()
|
||||||
{
|
{
|
||||||
return cert.get();
|
return cert.get();
|
||||||
|
|
|
@ -269,7 +269,7 @@ class Consortium:
|
||||||
for proposal_id, attr in r.body.json().items():
|
for proposal_id, attr in r.body.json().items():
|
||||||
proposals.append(
|
proposals.append(
|
||||||
infra.proposal.Proposal(
|
infra.proposal.Proposal(
|
||||||
proposal_id=int(proposal_id),
|
proposal_id=proposal_id,
|
||||||
proposer_id=int(attr["proposer"]),
|
proposer_id=int(attr["proposer"]),
|
||||||
state=infra.proposal.ProposalState(attr["state"]),
|
state=infra.proposal.ProposalState(attr["state"]),
|
||||||
)
|
)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче