Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
20ea5f0
Add frame class, conver context/socket/message to raw zmq.
evoskuil May 16, 2016
8689e32
Simplify poller contruct and move poller from cmzq to zmq.
evoskuil May 16, 2016
6f0aa8f
Convert socket from czmq to zmq, update others.
evoskuil May 17, 2016
910671b
Change context from czmq to zmq, add message clear, comments.
evoskuil May 17, 2016
5ed7cdc
Comments.
evoskuil May 17, 2016
a4360ca
Disable cert/auth czmq, remove czmq dependency.
evoskuil May 17, 2016
cbc7f5c
Complete zeromq interface declaration, auth and cert not implemented.
evoskuil May 18, 2016
3f83337
Revise for version3 changes.
evoskuil May 18, 2016
272d0cc
Fix initialization ordering bug.
evoskuil May 18, 2016
700109d
Eliminate incorrect bind-port initialzation.
evoskuil May 18, 2016
a94901c
Simplify certificate to trivial keypair generator and store.
evoskuil May 18, 2016
0fba325
Update example based on certificate changes.
evoskuil May 18, 2016
3fc02d4
Upgrade to nuget libzmq-4.2.3.
evoskuil May 19, 2016
d3364fa
Integrate certificate generation and derivation.
evoskuil May 19, 2016
cfa637e
Change poll timeout from micro to milli (zmq docs incorrect).
evoskuil May 19, 2016
08cdbb5
Construct with generated cert if private key empty.
evoskuil May 19, 2016
b6fcaa0
Change linger to zero so that we don't need explicit socket close.
evoskuil May 19, 2016
dfdf237
Change exclusion symbol to version4.
evoskuil May 20, 2016
5605fb1
Implement ZAP authentication handler.
evoskuil May 20, 2016
ba1f421
Update library metadata.
evoskuil May 22, 2016
b0576e7
Updates for session management, comments.
evoskuil May 23, 2016
51f7bff
Have authenticator generate its own context and expose it.
evoskuil May 24, 2016
cf13554
Fix invalid typedefs.
evoskuil May 24, 2016
38c35ad
Comments, whitespace.
evoskuil May 24, 2016
c71ee96
Rename context.close to .stop.
evoskuil May 24, 2016
435f604
Remove dead code.
evoskuil May 24, 2016
9f8ef7e
Derive authenticator from context.
evoskuil May 24, 2016
11752e1
Fix up example code.
evoskuil May 24, 2016
e88f728
Fix inverted socket id condition.
evoskuil May 25, 2016
24ea50c
Change to 10ms linger, comments, whitespace, no shutdown asserts.
evoskuil May 25, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ doc_DATA = \
# src/libbitcoin-protocol.la => ${libdir}
#------------------------------------------------------------------------------
lib_LTLIBRARIES = src/libbitcoin-protocol.la
src_libbitcoin_protocol_la_CPPFLAGS = -I${srcdir}/include ${zmq_CPPFLAGS} ${czmq_CPPFLAGS} ${bitcoin_CPPFLAGS}
src_libbitcoin_protocol_la_LIBADD = ${zmq_LIBS} ${czmq_LIBS} ${bitcoin_LIBS}
src_libbitcoin_protocol_la_CPPFLAGS = -I${srcdir}/include ${zmq_CPPFLAGS} ${bitcoin_CPPFLAGS}
src_libbitcoin_protocol_la_LIBADD = ${zmq_LIBS} ${bitcoin_LIBS}
src_libbitcoin_protocol_la_SOURCES = \
src/converter.cpp \
src/interface.pb.cc \
Expand All @@ -43,6 +43,7 @@ src_libbitcoin_protocol_la_SOURCES = \
src/zmq/authenticator.cpp \
src/zmq/certificate.cpp \
src/zmq/context.cpp \
src/zmq/frame.cpp \
src/zmq/message.cpp \
src/zmq/poller.cpp \
src/zmq/socket.cpp
Expand All @@ -54,8 +55,8 @@ if WITH_TESTS
TESTS = libbitcoin_protocol_test_runner.sh

check_PROGRAMS = test/libbitcoin_protocol_test
test_libbitcoin_protocol_test_CPPFLAGS = -I${srcdir}/include ${zmq_CPPFLAGS} ${czmq_CPPFLAGS} ${bitcoin_CPPFLAGS}
test_libbitcoin_protocol_test_LDADD = src/libbitcoin-protocol.la ${boost_unit_test_framework_LIBS} ${zmq_LIBS} ${czmq_LIBS} ${bitcoin_LIBS}
test_libbitcoin_protocol_test_CPPFLAGS = -I${srcdir}/include ${zmq_CPPFLAGS} ${bitcoin_CPPFLAGS}
test_libbitcoin_protocol_test_LDADD = src/libbitcoin-protocol.la ${boost_unit_test_framework_LIBS} ${zmq_LIBS} ${bitcoin_LIBS}
test_libbitcoin_protocol_test_SOURCES = \
test/converter.cpp \
test/main.cpp \
Expand Down Expand Up @@ -88,11 +89,8 @@ include_bitcoin_protocol_zmq_HEADERS = \
include/bitcoin/protocol/zmq/authenticator.hpp \
include/bitcoin/protocol/zmq/certificate.hpp \
include/bitcoin/protocol/zmq/context.hpp \
include/bitcoin/protocol/zmq/frame.hpp \
include/bitcoin/protocol/zmq/message.hpp \
include/bitcoin/protocol/zmq/poller.hpp \
include/bitcoin/protocol/zmq/socket.hpp

include_bitcoin_protocol_zmq_impldir = ${includedir}/bitcoin/protocol/zmq/impl
include_bitcoin_protocol_zmq_impl_HEADERS = \
include/bitcoin/protocol/zmq/impl/poller.ipp

303 changes: 10 additions & 293 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,301 +2,18 @@

[![Coverage Status](https://coveralls.io/repos/libbitcoin/libbitcoin-protocol/badge.svg)](https://coveralls.io/r/libbitcoin/libbitcoin-protocol)

# Bitcoin Server Protocol
# Libbitcoin Protocol

[swansontec](https://github.com/swansontec), [pmienk](https://github.com/pmienk), [evoskuil](https://github.com/evoskuil)
*Bitcoin Blockchain Query Protocol Library*

8/22/2014
Note that you need g++ 4.8 or higher. For this reason Ubuntu 12.04 and older are not supported. Make sure you have installed [libbitcoin](https://github.com/libbitcoin/libbitcoin) beforehand according to its build instructions, as well as libzmq.

## Experimental

This library is experimental and is no libbitcoin library currently depends on it.

## Concepts

A client's two major areas of interest are transaction discovery and transaction maintenance. Transaction discovery involves searching the blockchain for transactions that are of interest to the wallet. Once the client identifies the transactions it cares about, it needs to track their state as they become broadcast, confirmed, and so forth. Doing this generally involves tracking the state of the blockchain itself.

## Goals

- Provide support for optimal implementation of common bitcoin clients.
- [Full-Chain](https://bitcoin.it/wiki/Thin_Client_Security#Full-Chain_Clients)
- [Header-Only](https://bitcoin.it/wiki/Thin_Client_Security#Header-Only_Clients) (SPV)
- [Server-Trusting](https://bitcoin.it/wiki/Thin_Client_Security#Server-Trusting_Clients) (see below for details)
- Caching
- Stateless
- The server should not be required to maintain any session state.
- The client should not be required to provide any identifying information.
- The protocol should allow client privacy, leaving tradeoffs between privacy and performance to the caller.
- The protocol should be extensible while allowing backward and forward compatibility without version negotiation.
- The protocol should be defined in a formal [interface definition language](http://wikipedia.org/wiki/Interface_description_language) (IDL).
- The IDL should provide tooling for generation of client-server stubs in C/C++.
- The IDL tooling should implement marshalling in C/C++.

## Wire Encoding

The focus of this document is not the wire encoding, but the messaging semantics. The protocol may be encoded via any means. For example, it is possible to encode in [JSON](http://wikipedia.org/wiki/JSON) and serve up over [WebSockets](http://wikipedia.org/wiki/WebSocket). The initial libbitcoin implementation will likely encode using [Protocol Buffers](https://developers.google.com/protocol-buffers/docs/proto) and a [ZeroMQ](http://zeromq.org) transport with [privacy](http://curvezmq.org/) and support for [onion routing](https://www.torproject.org). Additional protocols may be efficiently layered over ZMQ (e.g. using in-process communication).

## Principles
### Privacy
Transaction queries, all of which would otherwise carry [taintable](https://bitcointalk.org/index.php?topic=92416.0) information, use `prefix` filters. This allows a caller to select its desired level of privacy to prevent correlation of transactions with the caller's IP address, or with each other. Fewer bits give more privacy at the expense of efficiency. Prefixes are defined for a bitcoin `address`, `stealth` addresses and `transaction` hashes.

Note that without prefix filters transaction query taint cannot be avoided using onion routing alone unless each individual request is made on a distinct channel, as the requests can still be correlated to each other.

Send/Verify calls are inherently compromising as they allow correlation of the caller's IP address with the transaction. This can only be avoided using an onion router, such as [Tor](https://www.torproject.org) or [I2P](https://geti2p.net/en), to proxy the communication to the server.

Block header queries are not considered privacy-compromising with the exception that, without using onion routing, the caller exposes the calling IP address as hosting a bitcoin client.

### Pagination
All queries use a pagination scheme. The caller specifies an optional starting point and an optional target for the desired number of results per page. The server returns results in whole-block increments of increasing block height. The server has the option to return a smaller result set than specified but always returns at least one block's worth of data (which may be an empty list if there is none to return) unless zero results per page is specified (in which case an empty list is returned).


### Block Correlation
The server always returns block height and hash as a `block_id` tuple to uniquely identify blocks. A caller may specify one, the other, or both of `block_id.hash` and `block_id.height`. The server validates whatever parts of `block_id` are specified, against each other and its current chain, returning an error if the request is off-chain.

Each paginated result contains the height and hash of the **next** block not included in the result set, which a client can use for chaining page requests. The last page always includes any matching transactions from the transaction memory pool and includes the **top** `block_id` instead of the **next** `block_id`.

If the **start** `block_id` is not specified the server returns results only from the memory pool. An unspecified page size allows the server to determine the page size, generally as large as practical. In the case of multiple query filters the server returns the union of results. An empty query returns all transactions.

The presence of the **next** `block_id.hash` allows the server to detect the presence of an apparent block fork between two page requests. The server will return an error, and the client can restart its queries from an earlier point. The server will always return results that are consistent with respect to the ending block hash.

The server signals the caller of a fork (or bad caller input input) by validating **start** `block_id` against the current chain, returning an error code if the specified `block_id` is not on the chain. There is no other possibility of pareseable input causing an error result (although server failures can produce errors). If a `tx_filter.bits` value that exceeds the length in bits of the corresponding `tx_filter.prefix` it is treated as a valid sentinel for "all bits" of the prefix.

## Complex Types

- libbitcoin::data_chunk bytes
- libbitcoin::hash_digest digest
- libbitcoin::block_header_type header
- libbitcoin::transaction_type tx
- enum {none | block | merkle} locations
- enum {hash | utxo | transaction} results
- enum {address | stealth | transaction} filters
- block
- header **header**
- list of branch **tree** (ordered)
- list of tx **transactions** (ordered)
- block_id
- uint32_t? **height** (default = unverified, use hash)
- digest? **hash** (default = unverified, use height)
- block_location
- block_id? **identity** (missing unless requested)
- list of digest **branch** (empty unless requested)
- filter
- filters **filter_type** (default = transaction)
- uint32_t? **bits** (default = all)
- bytes **prefix**
- output
- uint32_t **index**
- uint64_t **satoshis**
- bytes **script**
- tx_hash_result
- digest **hash**
- block_location **location**
- tx_result
- tx **transaction**
- block_location **location**
- utxo_result
- digest **hash**
- block_location **location**
- list of output **outputs**

### Merkle Branch Encoding

The transaction's [Merkle branch](https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#Partial_Merkle_branch_format) is encoded in `block_location.branch` as a hash list in depth-first order such that a properly-ordered combination with the hash of the corresponding transaction produces the [Merkle root](https://bitcoin.it/wiki/Protocol_specification#Merkle_Trees) of the corresponding block.

### Prefix Filters

In the case of filters the caller provides as prefix only the full or partial hash (byte-aligned). In other words, for addresses the prefix is against the RIPEMD160 hash payload, not the Base58Check encoding wrapper. Prefix comparisons for stealth addresses are independently documented. Address and transaction prefix comparisons are performed in a similar manner (TBD).

## Messages

### Blocks

- Get Block Headers
- in?: block_id **start** (default = get block height)
- in?: uint32_t **results_per_page** (default = all, 0 = none)
- out: list of header **headers** (empty = zero pages requested)
- out?: block_id **next** (missing = last page)
- out?: block_id **top** (missing = not last page)
- Post Block
- in: block **block**
- Validate Block
- in: block **block**

### Transactions

- Get Transactions
- in?: block_id **start** (default = tx mempool only)
- in?: uint32_t **results_per_page** (default = all, 0 = start block only)
- in?: results **result_type** (default = hash)
- in?: locations **location_type** (default = none)
- in: list of filter **query** (empty = all)
- out: list of {tx_hash_result} **hashes**
- out: list of {utxo_result} **outputs**
- out: list of {tx_result} **transactions**
- out?: block_id **next** (missing = last page)
- out?: block_id **top** (missing = not last page)
- Post Transaction
- in: tx **transaction**
- Validate Transaction
- in: tx **transaction**

> *Validate* calls are designed for client-side debugging.

## Usage Examples

Get the current block height, and nothing more:
```js
get_headers {}
```
Determine if a particular block is still on the main chain:
```js
get_headers
{
start =
{
height = 317792,
hash = 0x000000000000000018b01e93c7caaed765b7ff478f2dcc7ae6364bfcf97fe2f8
},
results_per_page = 0
}
```
Get all block headers, starting from the genesis block:
```js
get_headers
{
start = { height = 0 }
}
```
Get all block headers, starting where the previous query left off:
```js
get_headers
{
start =
{
height = 317792,
hash = 0x000000000000000018b01e93c7caaed765b7ff478f2dcc7ae6364bfcf97fe2f8
}
}
```
Get all transaction hashes for a wallet with two addresses, starting at the genesis block, with a target page size of 10 transactions:
```js
get_transactions
{
start = { height = 0 },
query =
[
{ prefix = 0x21 },
{ bits = 12, prefix = 0x08b7 }
],
results_per_page = 10
}
```
Get all transaction data for a wallet with two addresses, starting at a particular block:
```js
get_transactions
{
start =
{
height = 317792,
hash = 0x000000000000000018b01e93c7caaed765b7ff478f2dcc7ae6364bfcf97fe2f8
},
query =
[
{ prefix = 0x21 },
{ bits = 12, prefix = 0x08b7 }
],
result_type = transaction
}
```sh
$ ./autogen.sh
$ ./configure
$ make
$ sudo make install
$ sudo ldconfig
```
Get all utxo's for a single address:
```js
get_transactions
{
start = { height = 0 },
query = [{ bits = 12, prefix = 0x08b7 }],
result_type = utxo
}
```
Has my transaction been confirmed yet?
```js
get_transactions
{
start = { height = 0 },
query =
[
{
filter_type = transaction,
prefix = 0x94b43df27e205d8a261531fe1fc0c2e5fc226a87e6a9e1c68ab9113eb36cbf4a
}
],
result_type = hash
location_type = block
}
```
Get all the transactions in a particular block:
```js
get_transactions
{
start =
{
height = 317792,
hash = 0x000000000000000018b01e93c7caaed765b7ff478f2dcc7ae6364bfcf97fe2f8
},
results_per_page = 0
}
```
Get all stealth transactions in the mempool:
```js
get_transactions
{
query = [{ filter_type = stealth }]
}
```

## Client Types

This protocol supports four common client types:

- Full-chain caching clients
- SPV caching clients
- Server-trusting caching clients
- Server-trusting stateless clients

Caching clients store transactions locally, while stateless clients do not (although they might store addresses or other metadata). Aside from reducing latency and enabling verification, maintaining a local transaction database allows maintenance of per-transaction non-blockchain meta-data, such as categories, counterparty names, etc.

SPV and full-chain clients can verify the contents of their transaction database by checking transaction hashes against block headers. Additionally, full-chain clients can verify that inputs connect to outputs. Server-trusting clients don't maintain the blockchain, so a malicious server can easily convince them that they have received non-existent payments. Correctly-written non-trusting clients are immune to this class of attack.

Server-trusting caching clients are doubly-sensitive to attack, since they may receive and cache invalid data locally where it would persist until the cache is refreshed or a possibly if a blockchain fork is signalled. Stateless clients are somewhat less sensitive, since they will recover as soon as they connect to an honest server.

There are realistic scenarios where a trusting client makes sense. For example, an e-commerce platform might run its own internal bitcoin servers. In this case there is no need to verify data coming from the same security zone.

All caching client types stay up-to-date with the blockchain in a similar way. The client periodically polls for new blocks. Upon discovering a new block the client probes its cache for a chain fork. To do this, the client walks down the chain from its highest block, making server queries with the **start** `block_id` set to blocks along the chain. Each block that produces a failure response is identified as being on a pruned fork. Once the highest non-forked block in the cache is located the client queries the server in order to update the block information for all transactions (or addresses) that are associated with pruned blocks.

Although SPV clients can verify information the server provides, only a full-chain client can detect missing transactions. Therefore SPV clients may still choose to periodically re-scan their addresses starting from the genesis block. Trusting clients have no reason to assume that new data would be any more reliable than old data, so automated periodic updates may actually be harmful. On the other hand, depending on the trust model, periodic updates can mitigate perpetuating errors, as previously described.

## Old Obelisk Protocol

The old obelisk protocol is included for comparison:

### Fetch

- blockchain.fetch_history(address, from_height)
- blockchain.fetch_transaction(tx_hash)
- blockchain.fetch_last_height()
- blockchain.fetch_block_header(height)
- blockchain.fetch_block_header(blk_hash)
- blockchain.fetch_transaction_index(tx_hash)
- blockchain.fetch_stealth(prefix, from_height)
- address.fetch_history(address, from_height)
- transaction_pool.fetch_transaction(tx_hash)

### Send

- transaction_pool.validate(tx_data)
- protocol.broadcast_transaction(tx_data)

### Subscribe

- address.subscribe(prefix)

These features are a subset of new protocol with the exception of address.subscribe, which has been removed. The subscription required per caller session-state to be maintained by the server. The new protocol is stateless and therefore requires callers to poll for the same information.
libbitcoin-protocol is now installed in `/usr/local/`.
Original file line number Diff line number Diff line change
Expand Up @@ -29,30 +29,21 @@

<PropertyGroup Condition="'$(DefaultLinkage)' == 'dynamic'">
<Linkage-secp256k1>dynamic</Linkage-secp256k1>
<Linkage-czmqpp>dynamic</Linkage-czmqpp>
<Linkage-czmq>dynamic</Linkage-czmq>
<Linkage-libzmq>dynamic</Linkage-libzmq>
<Linkage-libsodium>dynamic</Linkage-libsodium>
<!--<Linkage-protobuf>dynamic</Linkage-protobuf>-->
<Linkage-libbitcoin>dynamic</Linkage-libbitcoin>
<Linkage-libbitcoin-protocol>dynamic</Linkage-libbitcoin-protocol>
</PropertyGroup>
<PropertyGroup Condition="'$(DefaultLinkage)' == 'ltcg'">
<Linkage-secp256k1>ltcg</Linkage-secp256k1>
<Linkage-czmqpp>ltcg</Linkage-czmqpp>
<Linkage-czmq>ltcg</Linkage-czmq>
<Linkage-libzmq>ltcg</Linkage-libzmq>
<Linkage-libsodium>ltcg</Linkage-libsodium>
<!--<Linkage-protobuf>ltcg</Linkage-protobuf>-->
<Linkage-libbitcoin>ltcg</Linkage-libbitcoin>
<Linkage-libbitcoin-protocol>ltcg</Linkage-libbitcoin-protocol>
</PropertyGroup>
<PropertyGroup Condition="'$(DefaultLinkage)' == 'static'">
<Linkage-secp256k1>static</Linkage-secp256k1>
<Linkage-czmqpp>static</Linkage-czmqpp>
<Linkage-czmq>static</Linkage-czmq>
<Linkage-libzmq>static</Linkage-libzmq>
<Linkage-libsodium>static</Linkage-libsodium>
<!--<Linkage-protobuf>static</Linkage-protobuf>-->
<Linkage-libbitcoin>static</Linkage-libbitcoin>
<Linkage-libbitcoin-protocol>static</Linkage-libbitcoin-protocol>
Expand All @@ -62,10 +53,7 @@

<Target Name="LinkageInfo" BeforeTargets="PrepareForBuild">
<Message Text="Linkage-secp256k1 : $(Linkage-secp256k1)" Importance="high"/>
<Message Text="Linkage-czmqpp : $(Linkage-czmqpp)" Importance="high"/>
<Message Text="Linkage-czmq : $(Linkage-czmq)" Importance="high"/>
<Message Text="Linkage-libzmq : $(Linkage-libzmq)" Importance="high"/>
<Message Text="Linkage-libsodium : $(Linkage-libsodium)" Importance="high"/>
<Message Text="Linkage-libbitcoin: $(Linkage-libbitcoin)" Importance="high"/>
<!--<Message Text="Linkage-protobuf : $(Linkage-protobuf)" Importance="high"/>-->
<Message Text="Linkage-_protocol : $(Linkage-libbitcoin-protocol)" Importance="high"/>
Expand Down
Loading