Skip to content

Commit

Permalink
Receipts (microsoft#567)
Browse files Browse the repository at this point in the history
  • Loading branch information
achamayou authored Nov 20, 2019
1 parent 1a001b6 commit beaae41
Show file tree
Hide file tree
Showing 14 changed files with 482 additions and 2 deletions.
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,12 @@ if(BUILD_TESTS)
--app-script ${CMAKE_CURRENT_SOURCE_DIR}/samples/apps/txregulator/app/txregulator.lua
--datafile ${CMAKE_CURRENT_SOURCE_DIR}/samples/apps/txregulator/dataset/sample_data.csv)

## Receipts end to end test
add_e2e_test(
NAME receipts_test
PYTHON_SCRIPT ${CMAKE_SOURCE_DIR}/tests/receipts.py
)

if(QUOTES_ENABLED)
add_e2e_test(
NAME governance_tests
Expand Down
15 changes: 15 additions & 0 deletions sphinx/source/schemas/getReceipt_params.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"commit": {
"maximum": 9223372036854775807,
"minimum": -9223372036854775808,
"type": "number"
}
},
"required": [
"commit"
],
"title": "getReceipt/params",
"type": "object"
}
18 changes: 18 additions & 0 deletions sphinx/source/schemas/getReceipt_result.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"receipt": {
"items": {
"maximum": 255,
"minimum": 0,
"type": "number"
},
"type": "array"
}
},
"required": [
"receipt"
],
"title": "getReceipt/result",
"type": "object"
}
18 changes: 18 additions & 0 deletions sphinx/source/schemas/verifyReceipt_params.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"receipt": {
"items": {
"maximum": 255,
"minimum": 0,
"type": "number"
},
"type": "array"
}
},
"required": [
"receipt"
],
"title": "verifyReceipt/params",
"type": "object"
}
13 changes: 13 additions & 0 deletions sphinx/source/schemas/verifyReceipt_result.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"valid": {
"type": "boolean"
}
},
"required": [
"valid"
],
"title": "verifyReceipt/result",
"type": "object"
}
63 changes: 63 additions & 0 deletions sphinx/source/users/issue_commands.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,66 @@ To guarantee that their request is successfully committed to the ledger, a user
}
In this example, the ``result`` field indicates that the request was executed at ``30`` (``commit``) was in term ``2``, the same term that the ``LOG_record``. Moreover, the ``global_commit`` (``31``) is now greater than the ``commit`` version. The ``LOG_record`` request issued earlier was successfully committed to the ledger.

Transaction receipts
--------------------

Once a transaction has been committed, it is possible to get a receipt for it. That receipt can later be checked against either a CCF service, or offline against the ledger, to prove that the transaction did happen at a particular commit.

To obtain a receipt, a user needs to issue a ``getReceipt`` RPC for a particular commit:

.. code-block:: bash
$ cat get_receipt.json
{
"id": 0,
"method": "users/getReceipt",
"jsonrpc": "2.0",
"params":
{
"commit": 30
}
}
$ 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:
{
"commit": 31,
"global_commit": 31,
"id": 0,
"jsonrpc": "2.0",
"result": {
"receipt": [ ... ],
},
"term": 2
}
Receipts can be verified with the ``verifyReceipt`` RPC:

.. code-block:: bash
$ cat get_receipt.json
{
"id": 0,
"method": "users/verifyReceipt",
"jsonrpc": "2.0",
"params":
{
"receipt": [ ... ]
}
}
$ 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:
{
"commit": 31,
"global_commit": 31,
"id": 0,
"jsonrpc": "2.0",
"result": {
"valid": true,
},
"term": 2
}
2 changes: 2 additions & 0 deletions src/kv/kvtypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ namespace kv
virtual void clear_on_result() = 0;
virtual void clear_on_response() = 0;
virtual crypto::Sha256Hash get_root() = 0;
virtual std::vector<uint8_t> get_receipt(Version v) = 0;
virtual bool verify_receipt(const std::vector<uint8_t>& receipt) = 0;
};

class Consensus
Expand Down
102 changes: 102 additions & 0 deletions src/node/history.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,87 @@ namespace ccf
{
return crypto::Sha256Hash();
}

std::vector<uint8_t> get_receipt(kv::Version v) override
{
return {};
}

bool verify_receipt(const std::vector<uint8_t>& v) override
{
return true;
}
};

class Receipt
{
private:
uint64_t index;
uint32_t max_index;
crypto::Sha256Hash root;
hash_vec* path;

public:
Receipt()
{
path = init_path();
}

static Receipt from_v(const std::vector<uint8_t>& v)
{
Receipt r;
const uint8_t* buf = v.data();
size_t s = v.size();
r.index = serialized::read<decltype(index)>(buf, s);
r.max_index = serialized::read<decltype(max_index)>(buf, s);
std::copy(buf, buf + r.root.SIZE, r.root.h);
buf += r.root.SIZE;
s -= r.root.SIZE;
for (size_t i = 0; i < s; i += r.root.SIZE)
path_insert(r.path, const_cast<uint8_t*>(buf + i));
return r;
}

Receipt(merkle_tree* tree, uint64_t index_)
{
index = index_;
path = init_path();

if (!mt_get_path_pre(tree, index, path, root.h))
{
free_path(path);
throw std::logic_error("Precondition to mt_get_path violated");
}

max_index = mt_get_path(tree, index, path, root.h);
}

bool verify(merkle_tree* tree) const
{
if (!mt_verify_pre(tree, index, max_index, path, (uint8_t*)root.h))
throw std::logic_error("Precondition to mt_verify violated");

return mt_verify(tree, index, max_index, path, (uint8_t*)root.h);
}

~Receipt()
{
free_path(path);
}

std::vector<uint8_t> to_v() const
{
size_t vs =
sizeof(index) + sizeof(max_index) + root.SIZE + root.SIZE * path->sz;
std::vector<uint8_t> v(vs);
uint8_t* buf = v.data();
serialized::write(buf, vs, index);
serialized::write(buf, vs, max_index);
serialized::write(buf, vs, root.h, root.SIZE);
for (size_t i = 0; i < path->sz; ++i)
serialized::write(buf, vs, *(path->vs + i), root.SIZE);
return v;
}
};

class MerkleTreeHistory
Expand Down Expand Up @@ -233,6 +314,16 @@ namespace ccf
throw std::logic_error("Precondition to mt_retract_to violated");
mt_retract_to(tree, index);
}

Receipt get_receipt(uint64_t index)
{
return Receipt(tree, index);
}

bool verify(const Receipt& r)
{
return r.verify(tree);
}
};

template <class T>
Expand Down Expand Up @@ -434,6 +525,17 @@ namespace ccf
LOG_DEBUG << fmt::format("HISTORY: add_response {0}", id) << std::endl;
responses[id] = response;
}

std::vector<uint8_t> get_receipt(kv::Version index) override
{
return tree.get_receipt(index).to_v();
}

bool verify_receipt(const std::vector<uint8_t>& v) override
{
auto r = Receipt::from_v(v);
return tree.verify(r);
}
};

using MerkleTxHistory = HashedTxHistory<MerkleTreeHistory>;
Expand Down
26 changes: 26 additions & 0 deletions src/node/rpc/call_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,30 @@ namespace ccf
ds::json::JsonSchema result_schema = {};
};
};

struct GetReceipt
{
struct In
{
int64_t commit = 0;
};

struct Out
{
std::vector<std::uint8_t> receipt = {};
};
};

struct VerifyReceipt
{
struct In
{
std::vector<std::uint8_t> receipt = {};
};

struct Out
{
bool valid = false;
};
};
}
2 changes: 2 additions & 0 deletions src/node/rpc/consts.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ namespace ccf
static constexpr auto GET_NETWORK_INFO = "getNetworkInfo";
static constexpr auto LIST_METHODS = "listMethods";
static constexpr auto GET_SCHEMA = "getSchema";
static constexpr auto GET_RECEIPT = "getReceipt";
static constexpr auto VERIFY_RECEIPT = "verifyReceipt";
};

struct MemberProcs
Expand Down
Loading

0 comments on commit beaae41

Please sign in to comment.