Giter VIP home page Giter VIP logo

olalonde / proof-of-liabilities Goto Github PK

View Code? Open in Web Editor NEW
106.0 13.0 37.0 551 KB

Proof of Liabilities (PoL) is a scheme designed to let companies that accept monetary deposits from consumers (e.g. Bitcoin exchanges, gambling websites, online Bitcoin wallets, etc.) prove their total amount of deposits (their liabilities) without compromising the privacy of individual users.

Home Page: http://olalonde.github.io/proof-of-liabilities

License: MIT License

JavaScript 99.62% Shell 0.38%

proof-of-liabilities's Introduction

Proof of Liabilities

Build Status

Proof of Liabilities specification and Javascript implementation.

Proof of Liabilities (PoL) is a scheme designed to let companies that accept monetary deposits from consumers (e.g. Bitcoin exchanges, gambling websites, online Bitcoin wallets, etc.) prove their total amount of deposits (their liabilities) without compromising the privacy of individual users.

The Proof of Liabilities scheme can be used as part of the broader Proof of Solvency scheme.

Proof of Liabilities online tools

Table of Contents

Install

npm install -g lproof

Usage

Simple usage:

# Generate a partial tree for all users in accounts.json.
# Partial trees will be saved to partial_trees/ directory.
# complete_tree.json and root.json will be saved to current directory.
# For a sample accounts file, refer to test/accounts.json.

$ lproof generate -f accounts.json

# Verify a partial tree 

$ lproof verify --root root.json -f partial_trees/satoshi.json

# or (where hash is the root hash and sum is the root sum)

$ lproof verify --hash "1ded5478d0116b30aca091f8d5ddd2340d9391dca47a41d9271e61ede51c0f6b" --sum 37618 -f mark.json

Advanced usage:

# Create complete proof tree from an accounts file (see
# test/account.json for format)

$ lproof completetree -f test/accounts.json --human
$ lproof completetree -f test/accounts.json > complete.out.json

# Extract partial tree for user mark.

$ lproof partialtree mark -f complete.out.json --human
$ lproof partialtree mark -f complete.out.json > mark.out.json

# Display root node hash and sum

$ lproof root -f complete.out.json --human

# Verify partial tree

$  verify --hash "1ded5478d0116b30aca091f8d5ddd2340d9391dca47a41d9271e61ede51c0f6b" --sum 37618 -f mark.out.json

Library usage

See cli.js.

Browser build: browserify index.js --standalone lproof > build/lproof.js.

Implementations

Non interoperable implementations:

Specification

Definitions

Complete proof tree

The complete proof tree is a binary tree where the leaf nodes represent all the user accounts and the internal nodes generated using the NodeCombiner function described below.

The complete tree should be kept private by the operator in order to protect the privacy of its users. Only the root node should be published publicly and each individual user should have private access to their own partial proof tree.

Ideally the tree layout should be random.

Leaf node

Leaf nodes represent user accounts. They possess the following values:

  • user: A unique identifier for the user. The user must ensure the uniqueness of this value so using their username or e-mail is recommended (it is never revealed by this scheme).
  • nonce: A random secret assigned by the operator to prevent neighbours from accidentally or deliberately discovering the value of user or sum (balance).
  • sum: The user's balance (called sum for consistency with internal nodes).
  • hash: SHA256(user + '|' + sum + '|' + nonce)

Internal node

Internal nodes are generated using the NodeCombiner function described below.

The node's sum is the result of adding of its children's sums.

The node's hash is its sum concatenated with its children's hashes, fed to SHA256.

function NodeCombiner (left_child, right_child) {
  var n = {};
  n.sum = left_child.sum + right_child.sum;
  n.hash = sha256(string(n.sum) + '|' + left_child.hash + '|' + right_child.hash);
  return n;
}

Root node

The root node of the, tree like all internal nodes, possesses a hash and a sum. This data must be published publicly so that all users can ensure they're verifying against the same proof tree.

Partial proof tree

A partial proof tree contains only the nodes from the complete tree which a given user needs in order to verify he was included in the tree.

It can be generated by starting with the user's leaf node and including every parent node up the tree, up to and including the root node (the root path). Then the sibling of each node on the path must be added to the tree.

  • All internal nodes should be completely stripped of their data to encourage verifiers to compute it themselves.

  • All leaf nodes except for the leaf representing the given user should be stripped of their user and nonce properties to ensure privacy.

  • The leaf representing the given user should be stripped of its hash property to encourage verifiers to compute it themselves.

Partial trees should be disclosed privately to each individual user so they can verify the proof, learning an absolute minimum about their neigbours.

Serialized data formats (work in progress / draft)

This section is intended to standardize the way root nodes and trees are generated and represented in order to make implementations compatible.

Hashing leaf nodes

To be accepted by conforming verifier tools, leaf (account) node hashes must be computed using:

SHA256(user + '|' + string(sum) + '|' + nonce)

where user, sum and nonce have the same meanings as above, but specifically:

  • strings are trimmed of any whitespace then joined using the pipe character (ASCII 0x7C) as a separator; no ASCII NULs are included in the SHA256 input
  • string(sum) is a string representation of the balance for the corresponding account, matching the regular expression ^(0|[1-9][0-9]*)(\.[0-9]+)?$ (or informally, the JSON 'number' format but no negative numbers and no 'e' notation). This representation must be in the shortest possible form allowed by the regular expression, achieved by stripping trailing zero digits from the fractional part[1]. The representation should not use more decimal places than required to represent the currency's smallest subunit; if the operator's system uses more decimal places, the value should be rounded towards +∞ to the next subunit before any use (addition/hashing/serialisation) in this scheme. Any conversion performed to produce or consume it should be done very carefully. Examples:
    • given $0, use 0, not 0.0 or 0.000
    • given $1.23 use 1.23, not 1.230 or 1.23000000
    • given $1.20 use 1.2, not 1.20 or 1.20000
    • given $20.00 use 20, not 20.00 or 20.000000
    • prefer to round $1234.5678 to $1234.57 before any use in this scheme
  • nonce is encoded as a hexadecimal string.

Example: if user [email protected] had a balance of 3.1415 bitcoins and had been been assigned nonce e3b0c44298fc1c149afbf4c8996fb924 by the operator, the hash would take the following string as input:

[email protected]|3.1415|e3b0c44298fc1c149afbf4c8996fb924

producing (hexadecimal-encoded) hash:

7856aa35ddcf71ab84d18c16d5ac1b90b19e6d54e932d972595235d343c17461

Hashing internal nodes

Internal (non-account) node hashes must be computed using:

SHA256(string(sum) + '|' + left_child.hash + '|' + right_child.hash)

where sum, left_child.hash and right_child.hash have the same meanings as above, but specifically:

  • strings are trimmed of any whitespace then joined using the pipe character (ASCII 0x7C) as a separator; no ASCII NULs are included in the SHA256 input
  • string(sum) is as above for leaf nodes, except represents the liabilities sum for the subtree.
  • left_child.hash(/right_child.hash) is the 64-digit hexadecimal string encoding (with lowercase letters) of the left(/right) child's hash

Example: if an internal node had a subtree liability sum of 71.31 bitcoins and child nodes with (hexadecimal-encoded) hashes of 000d5478d0116b30aca091f8d5ddd2340d9391dca47a41d9271e61ede51c0f6b (left) and 20aa3f466728a58182a9b7733fcb70044ab489a27554d9fb7ed520936759bf96 (right), the hash would take the following string as input:

71.31|000d5478d0116b30aca091f8d5ddd2340d9391dca47a41d9271e61ede51c0f6b|20aa3f466728a58182a9b7733fcb70044ab489a27554d9fb7ed520936759bf96

producing (hexadecimal-encoded) hash:

81dbc2416e7ead6a4ac1db605c56e293119a7ed65f3c80fdf1abbceeef22ac15

Root node

A JSON object:

{
  "root": {
    "sum": <JSON string, as described above>,
    "hash": <JSON string, as described above>
  },
  "currency": <JSON string>,
  "timestamp": <JSON number>
}

hash and sum are calculated as described above; both are JSON strings. hash is encoded as a 64-digit hexadecimal string with lowercase letters. The contents of string sum should be identical to the representation used in hashing, but equivalents (which add a fractional part with zeros in all decimal places, or which add trailing zeros to an existing fractional part) that still match the regex are allowed.

currency is the 3 letter ISO 4217 currency code (e.g. USD for United States dollar). If there is no ISO 4217 currency code for the currency, use the code that Bloomberg uses (e.g. XBT for Bitcoin) and otherwise, spell out the full currency name in lowercase (e.g. namecoin).

timestamp is a UNIX timestamp, which is the number of milliseconds between Epoch and the time at which the user balances were retrieved.

Example:

{
  "root": {
    "sum": "37618",
    "hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
  },
  "currency": "XBT",
  "timestamp": 1395718369805
}

Partial trees

Partial trees are represented as a JSON object graph made up of nodes. Each node has the following format:

{
  "left": <node>,
  "right": <node>,
  "data": <node_data>
}

<node_data> is a JSON object containing some subset of the following name/value pairs:

  • sum (JSON string): as described above. The contents should be identical to the representation used in hashing, but equivalents (which add a fractional part with zeros in all decimal places, or which add trailing zeros to an existing fractional part) that still match the regex are allowed.
    • Immediate children of nodes on the root path must have this key set.
    • Other nodes should not have this key set.
  • hash (JSON string): generated as described above, encoded as a 64-digit hexadecimal string with lowercase letters.
    • Immediate children of nodes on the root path must have this key set.
    • Other nodes should not have this key set.
  • user (JSON string): The unique string chosen by the user.
    • The node belonging to the user this partial tree was generated for must have this key set.
    • All other nodes must not have this key set.
  • nonce (JSON string): The nonce assigned to the user, encoded as a hexadecimal string with lowercase letters.
    • Should be at least 128 bits of cryptographically strong entropy.
    • The node belonging to the user this partial tree was generated for must have this key set.
    • All other nodes must not have this key set.

If redundant keys are omitted as suggested, the "data": <node_data> key/value pair will have an empty object ({}) for <node_data>, in which case the key/value pair should be omitted.

Example leaf node's <node_data>:

{
  "user": "[email protected]",
  "sum": "3.1415",
  "nonce": "e3b0c44298fc1c149afbf4c8996fb924",
}

Example <node_data> for an immediate child of a node on the root path:

{
  "sum": "71.31",
  "hash": "81dbc2416e7ead6a4ac1db605c56e293119a7ed65f3c80fdf1abbceeef22ac15"
}

Account lists

For the purposes of sharing test input and to help pin down inconsistencies between implementations, it would help if your implementation accepted an account list in the following form. This need not be its usual or only means of accepting account lists.

An account list is a JSON array of JSON objects:

[
  {
    "user": <JSON string, as described above>,
    "balance": <JSON string as described above>,
    "nonce": <JSON string as described above>
  }
  {
    "user": <JSON string, as described above>,
    "balance": <JSON string as described above>,
    "nonce": <JSON string as described above>
  }
  ...
]

Trees should ideally be given random layouts normally but when accepting this form for testing you should produce the tree with a deterministic algorithm:

  • pad out the accounts list size to the next power of two, using accounts with user "dummy", balance "0.00000000" and nonce "0"
  • produce a perfect binary tree
  • ensure that a traversal of the tree would visit the leaf nodes in the same order they appeared

This ensures that the root hash for the tree is deterministic and predictable which makes tests shareable.

[1]: Note that there's a bug in BigDecimal.stripTrailingZeros in JDK <8 where 0.000 doesn't change.

Publishing protocol

See olalonde/proof-of-solvency.

Acknowledgements

References

proof-of-liabilities's People

Contributors

olalonde avatar zw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

proof-of-liabilities's Issues

Automate root verification

The chrome extension could automate the "root" verification this way:

When the extension discovers a blind liability proof, request http://verificationserver.com/?root=. The verificationserver can then independently fetch the root file and send it back to the extension. The extension can then verify that the retrieved root matches the partial tree's root.

The 3rd party server is necessary to avoid a malicious site from sending a custom root file to different users.

Recommending usernames and emails as identifiers

using their username or e-mail is recommended (it is never revealed by this scheme)

However, in the case that a user needs to prove to others that their balance doesn't match, they do need to reveal their identifier, correct? In that case, it would not be ideal for them to reveal their email. Even a username is likely pretty identifying (and often might be tied somewhere publically to an email). I'd recommend hashing the underlying identifier so that obfuscated identifier can be revealed in such a situation, rather than the real ID.

A few changes to the specification

@zw and myself (mostly zw) have worked on updating the specification to address some potential issues and make the serialization format more formal in order to facilitate interoperability of implementations.

The current README file is up to date and the diff of what has changed can be seen here: https://github.com/olalonde/proof-of-liabilities/pull/18/files

Notable changes:

  • value becomes sum
  • SHA256 hashes are now hex-encoded instead of base64-encoded
  • nonces now have explicit advice on length and specified hex encoding
  • numbers are serialized as JSON strings
  • convention on producing accounts trees deterministically, to ensure predictable root hashes and facilitate implementations testing

You can test your implementation against mine, but it is possible I got something wrong in my own implementation so if your output doesn't match, create an issue instead of banging your head on the keyboard.

cc @gmaxwell @janx @FredericHeem @ConceptPending @bifubao

Make web UI accept GET/POST parameters?

So other websites could create a link for users to click. The link will redirect users to your web UI tool with user's root/partial_tree json filled and verified.

Partial proof trees should be more minimal

PPTs don't need to include any internal nodes on the root path --- those are redundant since the customer can compute them from siblings. I'm of the opinion that the more computation/verification a client is required to do (/the less they trust exchange-provided data) the better. I think it's very unlikely anyone would write code that did no hashing and only compared the provided PPT root hash with the published one, but I still think it's a good idea to have a standard that forces them to fully verify.

This is a comment on the format/standard rather than your implementation, since (while I haven't checked) I'm sure your implementation verifies trees fully.

Non-full binary tree has better account distribution privacy

Perhaps just a note for future development: The binary tree can have any shape you want, there is no need to make it a full binary tree. Changing the shape can improve the privacy of the distribution of account values.

At one extreme using a huffman tree will minimize the information leakage— as close to half the remaining balance will be on each child— but may cause very deep trees. The package-merge algorithm can construct a huffman tree subject to the constraint of a maximum depth, though I'm not aware of a handy JS implementation. Though so long as the verifier doesn't care about the tree shape this is just a server side optimization.

Values should be stored as String

I'm writing a proof generate rubygem which create json files conform with the standards you proposed. However I find my generated json cannot pass the verification of blp. After a while of digging it looks like the trouble maker is Float numbers.

I use console.log to print combined nodes created in verification, here's the root node:

{ index: 0,
  data: 
   { value: 27208.230000000003,
     hash: 'bqac1NPcu/zkeekeDSNp3ckOMSBRylEXALTApIjKJ6g=' } }
INVALID partial tree!
[Error: Root mismatch. Expected {"hash":"yB24bebCDwzAMOowfbcZ6/epoY3/pGGqF2TyAn6td1w=","value":27208.23}, got "bqac1NPcu/zkeekeDSNp3ckOMSBRylEXALTApIjKJ6g="]

As the message shows the value is represented as 27208.230000000003 in memory, which changes the generated hash.

Below is the partial tree and root json I used:

{"partial_tree":{"data":null,"right":{"data":{"value":10499.19,"hash":"sIwvgAXhceLwjNTWuOTDscELgqMU9NdAi22j7/ObY90="}},"left":{"data":null,"right":{"data":{"value":7944.0,"hash":"OiLKmFzCPF2XYCGVnLL7vPv8AFnMXZzWjMvJd7tx+pE="}},"left":{"data":null,"right":{"data":{"value":3988.0,"hash":"7ssZ4Rrv0IyINwMRBhfS3EVOz+fZEOCUeZzqEsIcnU4="}},"left":{"data":null,"right":{"data":{"value":2025.0,"hash":"DwQJExRmkSTr4QmiFriW8V1oQonhAS+1JRl66mCFkXo="}},"left":{"data":null,"right":{"data":{"value":1050.0,"hash":"X7GlPlUUPH+mjo8Gz8aekzMAzU8CayT3tuEE8BsIzNc="}},"left":{"data":null,"right":{"data":{"value":990.0,"hash":"/n8EuJoG0Jcv2+ENWNIjZ5M31aI1mVzck8KTfzO8XFI="}},"left":{"data":null,"right":{"data":{"value":442.0,"hash":"UcRQr5fnSL7o40pe1mlh+dhDeBBprPcmtFqxmUbYrpU="}},"left":{"data":null,"right":{"data":{"value":157.0,"hash":"O3LENd8ZN+f7c16LVYW/UvBHm487eVNt/XwmSwFG6tE="}},"left":{"data":null,"right":{"data":{"value":112.0,"hash":"YzAOvKLO6nPYPtsr8O1VY4qBDK7iQ1fw6/MSf+XXb6E="}},"left":{"data":null,"right":{"data":{"value":0.54,"hash":"7pSFTF8iM/dTMe4GgsbAoaj4SOyt527OO7wQZbMElK4="}},"left":{"data":{"value":0.5,"hash":"AYlP2iEXNQroLfzTwMkRQa9FEmI5wuE79nVCfeizYMk=","user":"PEAIV4CNHHHTIO|[email protected]","nonce":0.9841745575350908}}}}}}}}}}}}}
{"root":{"hash":"yB24bebCDwzAMOowfbcZ6/epoY3/pGGqF2TyAn6td1w=","value":27208.23}}

Some Numbers in Scientific Notation.

Is there any way to format the numbers to nearest satoshi?
And output them formatted to 8 decimal places?

Thanks

0.03868008999999999, JTdNajdd49+7JZ32ygkv59g8ve5ZDGXASvwsSE3zPeU=
|_ 0.038680019999999996, lDmDhkFjNlYnR/ZlMdjhI8+hgaibq9Gow2uuEA0tNig=
| |_ 0.03867999, 5pGJmMyWs8CNM5ztEVgXIl9JG+/3HWqc2/8/iwIpcw4=
| |_ 3e-8, bob, 0.6856001964770257, wf7Ti7RjI9pqlCmrHlsJ9B+5/VW8Q//ctOQKJgIl8JI=
|_ 6.999999999999999e-8, 1htICkG5KA/Z1L65Vad9PQq+RkLwJFaRV5+48mua2ZU=

npm install blproof from github fails: [email protected]:olalonde/blind-liability-proof is not a valid repository name

Dear,
After adding
"blproof":"[email protected]:olalonde/blind-liability-proof.git#4be66583aff5763ae89316530fbe5797ddffc79c"
in a package.json and run "sudo npm install blproof"
The output is:
npm http GET https://registry.npmjs.org/blproof
npm http GET https://registry.npmjs.org/blproof
npm http GET https://registry.npmjs.org/blproof
npm ERR! git clone git://github.com/[email protected]:olalonde/blind-liability-proof.git Cloning into bare repository '/Users/frederic/.npm/_git-remotes/git-github-com-git-github-com-olalonde-blind-liability-proof-git-5bd27c5e'...
npm ERR! git clone git://github.com/[email protected]:olalonde/blind-liability-proof.git
npm ERR! git clone git://github.com/[email protected]:olalonde/blind-liability-proof.git fatal: remote error:
npm ERR! git clone git://github.com/[email protected]:olalonde/blind-liability-proof.git [email protected]:olalonde/blind-liability-proof is not a valid repository name
npm ERR! git clone git://github.com/[email protected]:olalonde/blind-liability-proof.git Email [email protected] for help
npm ERR! Error: No compatible version found: blproof@'[email protected]:olalonde/blind-liability-proof.git#4be66583aff5763ae89316530fbe5797ddffc79c'
npm ERR! Valid install targets:
npm ERR! ["0.0.1","0.0.2","0.0.3"]
npm ERR! at installTargetsError (/usr/local/lib/node_modules/npm/lib/cache.js:685:10)
npm ERR! at /usr/local/lib/node_modules/npm/lib/cache.js:607:10
npm ERR! at saved (/usr/local/lib/node_modules/npm/node_modules/npm-registry-client/lib/get.js:138:7)
npm ERR! at Object.oncomplete (fs.js:107:15)
npm ERR! If you need help, you may report this log at:
npm ERR! http://github.com/isaacs/npm/issues
npm ERR! or email it to:
npm ERR! [email protected]

Any ideas ?
Best Regards

Sum representation problem

For very small values I believe hashes are not calculated correctly because big.js
toString formats it in scientific notation if value is less than 1e-7 or greater than 1e21.
Affected functions are combine_nodes and generate_leaf_hash. Simple solution would be to call toFixed method on big.js.

FAQ section

write faq file to answer common questions/criticism (i.e. negative balances, etc.)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.