-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 1ad3245
Showing
25 changed files
with
4,758 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
cmd/dcrtime/dcrtime | ||
dcrtimestored/dcrtimestored | ||
vendor/ | ||
*~ | ||
*.pyc | ||
*.sw? | ||
*.orig | ||
*.diff |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
ISC License | ||
|
||
Copyright (c) 2013-2017 The btcsuite developers | ||
Copyright (c) 2015-2017 The Decred developers | ||
|
||
Permission to use, copy, modify, and distribute this software for any | ||
purpose with or without fee is hereby granted, provided that the above | ||
copyright notice and this permission notice appear in all copies. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
dcrtime | ||
======= | ||
|
||
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) | ||
|
||
Decred anchored timestamp client and server. | ||
|
||
The dcrtime stack as as follows: | ||
|
||
``` | ||
+-------------------------+ | ||
| dcrtime | | ||
+-------------------------+ | ||
| | ||
~~~~~~~~ Internet ~~~~~~~~~ | ||
| | ||
+-------------------------+ | ||
| dcrtimestored (proxy) | | ||
+-------------------------+ | ||
| | ||
~~~~~~~~~~ VPN ~~~~~~~~~~~~ | ||
| | ||
+-------------------------+ | ||
| dcrtimestored (backend) | | ||
+-------------------------+ | ||
| | ||
+-------------------------+ | ||
| dcrwallet | | ||
+-------------------------+ | ||
| | ||
+-------------------------+ | ||
| dcrd | | ||
+-------------------------+ | ||
``` | ||
|
||
## Components | ||
* dcrtime - Reference client application | ||
* dcrtimestored | ||
- Proxy Mode: Forwards requests to the backend server. | ||
- Backend mode: Manages timestamps and creates decred transaction that anchors transactions in the blockchain. | ||
|
||
## Library and interfaces | ||
* api/v1 - JSON REST API for dcrtime clients. | ||
* cmd/dcrtime - Client reference implementation | ||
* cmd/dcrtime_unflush - Debug backend tool to either delete the flush record or reset the chain timestamp. | ||
* cmd/dcrtime_timestamp - Tool to convert between various timestamp formats. | ||
* merkle - Merkle algorithm implementation. | ||
* util - common used miscellaneous utility functions. | ||
|
||
## Example setup | ||
|
||
### Backend | ||
|
||
This example dcrtimestored.conf connects to dcrwallet running on localhost using the testnet network. | ||
|
||
``` | ||
wallethost=localhost | ||
walletcert=../.dcrwallet/rpc.cert | ||
walletpassphrase=MySikritPa$$w0ard | ||
testnet=1 | ||
``` | ||
|
||
Start the store. | ||
``` | ||
store-server$ dcrtimestored | ||
07:36:09 2017-06-09 [INF] DCRT: Version : 0.1.0 | ||
07:36:09 2017-06-09 [INF] DCRT: Mode : Store | ||
07:36:09 2017-06-09 [INF] DCRT: Network : testnet2 | ||
07:36:09 2017-06-09 [INF] DCRT: Home dir: /home/user/.dcrtimestored | ||
07:36:09 2017-06-09 [INF] DCRT: Generating HTTPS keypair... | ||
07:36:09 2017-06-09 [INF] DCRT: HTTPS keypair created... | ||
07:36:09 2017-06-09 [INF] FSBE: Wallet: 127.0.0.1:19111 | ||
07:36:09 2017-06-09 [INF] DCRT: Start of day | ||
07:36:09 2017-06-09 [INF] DCRT: Listen: :59152 | ||
07:58:03 2017-06-09 [INF] DCRT: Timestamp 203.0.113.4:44331 via 10.0.0.2:57126: rejected 20170609.120000 b1d080f4d09ea21a7b1872d87993079a84718f485de87d0327b6d1da922620e1 | ||
07:59:36 2017-06-09 [INF] DCRT: Timestamp 203.0.113.4:57138 via 10.0.0.2:32322: accepted 20170609.120000 8496855341883fdc90cc532f8304d1c46a60586fb15d99f07e41bb5ab19c79c6 | ||
08:00:10 2017-06-09 [INF] FSBE: flusher: directories 1 in 788.607578ms | ||
08:15:29 2017-06-09 [INF] DCRT: Verify 203.0.113.4:39992 via 10.0.0.2:57144: Timestamps 0 Digests 1 | ||
08:16:36 2017-06-09 [INF] DCRT: Verify 204.0.113.4:57146 via 10.0.0.2:33881: Timestamps 0 Digests 1 | ||
08:16:36 2017-06-09 [INF] FSBE: Flushed anchor timestamp: 4172a560a7035c169c4da60cba2cb1fbac686bd01224e09a1a56ce5e6f31cff0 1497013614 | ||
``` | ||
|
||
### Proxy | ||
|
||
dcrtimestored also has a proxy mode. It is activated by specifying the --storehost and --storecert options. | ||
In this example, we assume the proxy has an internal interface with ip 10.0.0.2 that connects to storehost.example.com at 10.0.0.1. | ||
|
||
``` | ||
proxy-server$ mkdir ~/.dcrtimestored | ||
proxy-server$ scp storehost.example.com:/home/user/.dcrtimestored/https.cert ~/.dcrtimestored/dcrtimestored.cert | ||
proxy-server$ dcrtimestored --testnet --storehost=storehost.example.com --storecert=~/.dcrtimestored/dcrtimestored.cert | ||
07:50:59 2017-06-09 [WRN] DCRT: open /home/user/.dcrtimestored/dcrtimestored.conf: no such file or directory | ||
07:50:59 2017-06-09 [INF] DCRT: Version : 0.1.0 | ||
07:50:59 2017-06-09 [INF] DCRT: Mode : Proxy | ||
07:50:59 2017-06-09 [INF] DCRT: Network : testnet2 | ||
07:50:59 2017-06-09 [INF] DCRT: Home dir: /home/user/.dcrtimestored | ||
07:50:59 2017-06-09 [INF] DCRT: Generating HTTPS keypair... | ||
07:50:59 2017-06-09 [INF] DCRT: HTTPS keypair created... | ||
07:50:59 2017-06-09 [INF] DCRT: Start of day | ||
07:50:59 2017-06-09 [INF] DCRT: Listen: :59152 | ||
07:58:03 2017-06-09 [INF] DCRT: Timestamp 203.0.113.4:44331: b1d080f4d09ea21a7b1872d87993079a84718f485de87d0327b6d1da922620e1 | ||
07:59:36 2017-06-09 [INF] DCRT: Timestamp 203.0.113.4:57138: 8496855341883fdc90cc532f8304d1c46a60586fb15d99f07e41bb5ab19c79c6 | ||
08:15:29 2017-06-09 [INF] DCRT: Verify 203.0.113.4:39992: Timestamps 0 Digests 1 | ||
08:16:36 2017-06-09 [INF] DCRT: Verify 204.0.113.4:57146: Timestamps 0 Digests 1 | ||
``` | ||
|
||
Now we test the setup using dcrtime. Note that for this example one digest was already known to the system and one was not. You can spot the difference in the dcrtimestored trace byt the words "accepted" and "rejected". Accepted means the file digest was unknown to the store and could therefore be added. Rejected on the other hands means that said digest already exists and therefore can not be added again. A digest can only be queried once it has been added to the store. | ||
|
||
Per the trace above we issue a known digest first: | ||
``` | ||
$ dcrtime -v /bin/ls | ||
b1d080f4d09ea21a7b1872d87993079a84718f485de87d0327b6d1da922620e1 Upload /bin/ls | ||
b1d080f4d09ea21a7b1872d87993079a84718f485de87d0327b6d1da922620e1 Exists /bin/ls | ||
Collection timestamp: 1497013200 | ||
``` | ||
|
||
And now we issue an unknown digest: | ||
``` | ||
$ dcrtime -v myfile.txt | ||
8496855341883fdc90cc532f8304d1c46a60586fb15d99f07e41bb5ab19c79c6 Upload myfile.txt | ||
8496855341883fdc90cc532f8304d1c46a60586fb15d99f07e41bb5ab19c79c6 OK myfile.txt | ||
Collection timestamp: 1497009600 | ||
``` | ||
|
||
In this example we wait a bit until the store hits its scheduled hourly flush regimen. This can be observed in the store trace. | ||
|
||
And now let's ask about these digests: | ||
``` | ||
$ dcrtime -v b1d080f4d09ea21a7b1872d87993079a84718f485de87d0327b6d1da922620e1 | ||
b1d080f4d09ea21a7b1872d87993079a84718f485de87d0327b6d1da922620e1 Verify | ||
b1d080f4d09ea21a7b1872d87993079a84718f485de87d0327b6d1da922620e1 OK | ||
Chain Timestamp: 1496430430 | ||
Merkle Root : 9788d5d7b85f2b68ec21d26e738dce6cdd367ee0ec58b53ad6bd4d46b0bc3018 | ||
TxID : 554b27c309ac9a8dab8ae261bb13dcfcdd351aa5f196322c112f04d106e000f3 | ||
``` | ||
|
||
The next digest was anchored but the store did not have the chain timestamp cached yet. This can be observed in the store trace; just look for "Flushed anchor". | ||
``` | ||
$ dcrtime -v 8496855341883fdc90cc532f8304d1c46a60586fb15d99f07e41bb5ab19c79c6 | ||
8496855341883fdc90cc532f8304d1c46a60586fb15d99f07e41bb5ab19c79c6 Verify | ||
8496855341883fdc90cc532f8304d1c46a60586fb15d99f07e41bb5ab19c79c6 OK | ||
Chain Timestamp: 1497013614 | ||
Merkle Root : 8496855341883fdc90cc532f8304d1c46a60586fb15d99f07e41bb5ab19c79c6 | ||
TxID : 4172a560a7035c169c4da60cba2cb1fbac686bd01224e09a1a56ce5e6f31cff0 | ||
``` | ||
|
||
You can find the merkle root using block explorer. Surf to https://testnet.decred.org/tx/554b27c309ac9a8dab8ae261bb13dcfcdd351aa5f196322c112f04d106e000f3 and in the transaction you'll find an entry that is as follows: | ||
``` | ||
OP_RETURN 9788d5d7b85f2b68ec21d26e738dce6cdd367ee0ec58b53ad6bd4d46b0bc3018 | ||
``` | ||
The astute reader noticed that this is the Merkle Root the dcrtime client returned. | ||
|
||
Note that this example was run on a single machine but that the listen port bits were removed for clarity. | ||
|
||
## License | ||
|
||
dcrtime is licensed under the [copyfree](http://copyfree.org) ISC License. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// Copyright (c) 2017 The Decred developers | ||
// Use of this source code is governed by an ISC | ||
// license that can be found in the LICENSE file. | ||
|
||
package v1 | ||
|
||
import ( | ||
"regexp" | ||
|
||
"github.com/decred/dcrtime/merkle" | ||
) | ||
|
||
// XXX add a clamp to all batches | ||
|
||
const ( | ||
TimestampRoute = "/v1/timestamp/" // Digests ingest | ||
VerifyRoute = "/v1/verify/" // Multi verify ingest | ||
|
||
// Result codes. | ||
ResultOK = 0 // Operation completed succefully | ||
ResultExistsError = 1 // Digest rejected because it exists | ||
ResultDoesntExistError = 2 // Unknown timestamp or digest | ||
) | ||
|
||
var ( | ||
Result = map[int]string{ | ||
ResultOK: "OK", | ||
ResultExistsError: "Exists", | ||
ResultDoesntExistError: "Doesn't exist", | ||
} | ||
|
||
// Valid text representation of digests and timestamps. | ||
RegexpSHA256 = regexp.MustCompile("[A-Fa-f0-9]{64}") | ||
RegexpTimestamp = regexp.MustCompile("[0-9]{10}") | ||
) | ||
|
||
// Timestamp is used to ask the timestamp server to store a batch of digests. | ||
// ID is user settable and can be used as a unique identifier by the client. | ||
type Timestamp struct { | ||
ID string `json:"id"` | ||
Digests []string `json:"digests"` | ||
} | ||
|
||
// TimestampReply is returned by the timestamp server after storing the batch | ||
// of digests. ID is copied from the originating Timestamp call and can be | ||
// used by the client as a unique identifier. The ServerTimestamp indicates | ||
// what collection the Digests belong to. Results contains individual result | ||
// codes for each digest. | ||
type TimestampReply struct { | ||
ID string `json:"id"` | ||
ServerTimestamp int64 `json:"servertimestamp"` | ||
Digests []string `json:"digests"` | ||
Results []int `json:"results"` | ||
} | ||
|
||
type Verify struct { | ||
ID string `json:"id"` | ||
Digests []string `json:"digests"` | ||
Timestamps []int64 `json:"timestamps"` | ||
} | ||
|
||
type VerifyDigest struct { | ||
Digest string `json:"digest"` | ||
ServerTimestamp int64 `json:"servertimestamp"` | ||
Result int `json:"result"` | ||
ChainInformation ChainInformation `json:"chaininformation"` | ||
} | ||
|
||
// ChainTimestamp is zero if this digest collection is not anchored in the blockchain; it is however set to the block timestamp it was anchored in. | ||
type VerifyTimestamp struct { | ||
ServerTimestamp int64 `json:"servertimestamp"` | ||
Result int `json:"result"` | ||
CollectionInformation CollectionInformation `json:"collectioninformation"` | ||
} | ||
|
||
type VerifyReply struct { | ||
ID string `json:"id"` | ||
Digests []VerifyDigest `json:"digests"` | ||
Timestamps []VerifyTimestamp `json:"timestamps"` | ||
} | ||
|
||
type ChainInformation struct { | ||
ChainTimestamp int64 `json:"chaintimestamp"` | ||
Transaction string `json:"transaction"` | ||
MerkleRoot string `json:"merkleroot"` | ||
MerklePath merkle.MerkleBranch `json:"merklepath"` | ||
} | ||
|
||
type CollectionInformation struct { | ||
ChainTimestamp int64 `json:"chaintimestamp"` | ||
Transaction string `json:"transaction"` | ||
MerkleRoot string `json:"merkleroot"` | ||
Digests []string `json:"digests"` | ||
} |
Oops, something went wrong.