Compare commits

...

1864 Commits
v0.1.3 ... v0.7

Author SHA1 Message Date
6f3beb915c Update fetch-perf-libs.sh 2018-09-17 11:54:32 -07:00
f399172f51 Pin docker images 2018-09-07 17:13:39 -07:00
15f3b97492 Send deploy metrics to the testnet-specific database 2018-09-06 08:32:07 -07:00
de284466ff Fetch install-earlyoom.sh from v0.7 branch 2018-09-05 19:12:56 -07:00
cb20ebc583 Don't block on large network test 2018-08-21 20:52:45 -07:00
ceb5686175 clippy: remove identity conversion 2018-08-21 20:51:46 -07:00
55d59b1ac6 echo commands, use PID (good form) 2018-08-21 20:42:35 -07:00
2b60b4e23a files have to appear in the snap 2018-08-21 20:42:21 -07:00
9bdc1b9727 fixups 2018-08-21 20:42:13 -07:00
87efdabcb3 make a copy of the ledger for sanity check
we can't verify a live ledger, unfortunately, fixes #985
2018-08-21 20:42:05 -07:00
7d5bb28128 Make SNAP_CHANNEL more visible in build log 2018-08-17 21:40:24 -07:00
ae433d6a34 Invert logic 2018-08-17 21:17:31 -07:00
e3c668acff Keep v0.7 snap off the edge channel 2018-08-17 21:12:31 -07:00
5825501c79 Log expansion directive must be on its own line 2018-08-17 20:58:00 -07:00
7e84bb7a60 Add option to skip ledger verification 2018-08-17 20:41:55 -07:00
da1fd96d50 0.7.2 2018-08-17 17:56:04 -07:00
141e1e974d Add some wget retries 2018-08-17 16:11:18 -07:00
fc0d7f5982 updated nightly versions 2018-08-16 13:17:29 -07:00
f697632edb update clippy install instructions, from here:
https://github.com/rust-lang-nursery/rust-clippy

fixes #947 ?
2018-08-16 13:17:29 -07:00
73797c789b back to 4 TX threads 2018-08-16 12:02:11 -07:00
036fcced31 test -t nproc 2018-08-16 12:02:11 -07:00
1d3157fb80 fixups 2018-08-16 12:02:11 -07:00
0b11c2e119 restart testnet clients in case airdrop fails 2018-08-16 12:02:11 -07:00
96af892d95 Add docs about the testnet 2018-08-16 07:39:17 -07:00
c2983f824e Refactored large network test to use finality to assert success (#978) 2018-08-15 20:05:43 -07:00
88d6fea999 Revert "Accounts with state (#954)"
This reverts commit c23fa289c3.
2018-08-15 19:44:39 -07:00
c23fa289c3 Accounts with state (#954)
* Account type with state

* fixed test according to @rob-solana
2018-08-15 14:32:11 -07:00
db35f220f7 Run multinode test for enough iterations for a small node count test (#971) 2018-08-15 10:44:14 -07:00
982afa87a6 Retransmit blobs from leader from window (#975)
- Some nodes don't have leader information while leader is broadcasting
  blobs to those nodes. Such blobs are not retransmitted. This change
  rertansmits the blobs once the leader's identity is know.
2018-08-14 21:51:37 -07:00
dccae18b53 cfg=erasure fixes, use return value of align!() 2018-08-14 12:14:59 -07:00
53e86f2fa2 use align! 2018-08-14 12:14:59 -07:00
757dfd36a3 Report errors better in build log 2018-08-14 11:44:26 -07:00
708add0e64 fixups 2018-08-14 10:16:34 -07:00
d8991ae2ca fix UPnP backout, fixes #969 2018-08-14 10:16:34 -07:00
5f6cbe0cf8 fixups 2018-08-13 21:07:26 -07:00
f167b0c2c5 fixups 2018-08-13 21:07:26 -07:00
f784500fbb fixups
fixes #907
2018-08-13 21:07:26 -07:00
83df47323a initialize recycled data 2018-08-13 21:07:26 -07:00
c75d4abb0b Tuck away PoH duration 2018-08-13 20:17:16 -06:00
5216a723b1 Pacify clippy 2018-08-13 20:17:16 -06:00
b801ca477d Declare fullnode a word 2018-08-13 20:17:16 -06:00
c830c604f4 Make BroadcastStage an actual stage
TODO: Why isn't BroadcastStage/RetransmitStage managed by the NCP?
2018-08-13 20:17:16 -06:00
0e66606c7f Rename broadcaster to broadcast_stage
And move retransmitter code into retransmit_stage.

TODO: Add a BroadcastStage service
2018-08-13 20:17:16 -06:00
8707abe091 Fix erasure build 2018-08-13 20:17:16 -06:00
dc2a840985 Move FullNode::new_window into window module 2018-08-13 20:17:16 -06:00
2727067b94 Move winow into its own module 2018-08-13 20:17:16 -06:00
6a8a494f5d Rename WindowStage to RetransmitStage
The window is used for both broadcasting from leader to validator
and retransmitting between validators.
2018-08-13 20:17:16 -06:00
a09d2e252a Move window dependencies out of streamer
No tests!?
2018-08-13 20:17:16 -06:00
3e9c463ff1 Offer only 1 way to create a fullnode with an empty window 2018-08-13 20:17:16 -06:00
46d50f5bde Remove p2p crate (and uPnP support) 2018-08-13 18:22:58 -07:00
e8da903c6c move tmp_ledger back to target dir 2018-08-13 16:52:53 -07:00
ab10b7676a use stable cache 2018-08-13 16:23:30 -07:00
fa44a71d3e move bench to a seprate, parallel step 2018-08-13 16:23:30 -07:00
c86e9e8568 pad max_data_size to jerasure's alignment requirements 2018-08-13 16:10:51 -07:00
9e22e23ce6 increase stable timeout until tomorrow 2018-08-13 15:45:50 -07:00
835f29a178 off by 2 2018-08-13 15:12:12 -07:00
9688f8fb64 Update IP address 2018-08-13 12:32:09 -07:00
df5cde74b0 Back out pre-0.7.1 workaround 2018-08-13 12:13:00 -07:00
231d5e5968 0.7.1 2018-08-13 12:12:27 -07:00
c2ba72fe1f Fix up validator sanity 2018-08-13 10:23:35 -07:00
d93786c86a Revert "turn off validator sanity while I work on it"
This reverts commit d4304eea28.
2018-08-13 10:23:35 -07:00
bf15cad36b Add get_finality request and use it from multinode test (#941) 2018-08-13 08:55:13 -07:00
288ed7a8ea Vote should be valid (#945)
* test that fails

* fix for test

* rename
2018-08-12 18:19:54 -07:00
f07c038266 Fix bank coalescing (#949)
* fix bank coalescing

* comments

* fix bench

* fix bench

* backout banking stage coalescing

* 120 nodes

* 100
2018-08-12 10:04:21 -07:00
8eed120c38 add missing backslash 2018-08-10 23:45:08 -07:00
5dbcb43abd more enhancements 2018-08-10 19:53:58 -07:00
dd1eefaf62 change verify-internal to precheck
update to new ledger API
2018-08-10 19:53:58 -07:00
35de159d00 better error messages 2018-08-10 19:53:58 -07:00
546a1e90d5 clippy fixups 2018-08-10 19:53:58 -07:00
b033e1d904 enhance ledger-tool
* add json, which does the thing with json, move print to Rust's {:?}
  * add --head NUM, to limit how much work gets done for print, json, verify
  * add verify-internal, which very carefully checks ledger format without
      trying first to "recover" it
  * exit with errors on mis-usage
2018-08-10 19:53:58 -07:00
96d6985895 rework read_ledger, LedgerWriter, and LedgerWindow for recover()
fixes #910
2018-08-10 18:07:23 -07:00
58f220a3b7 Add tic-tac-toe program flow concept 2018-08-10 17:39:54 -07:00
a206f2570d Add hostname to metrics on panic 2018-08-10 17:08:40 -07:00
2318ffc704 Use a different counter for validator account not found errors. (#931)
* Use a different counter for validator account not found errors.  This is a usefull signal of something going wrong with the ledger
2018-08-10 15:18:44 -07:00
d4304eea28 turn off validator sanity while I work on it 2018-08-10 14:56:46 -07:00
06af9de753 fixups 2018-08-10 11:41:31 -07:00
7f71e1e09f fixups 2018-08-10 11:41:31 -07:00
bb7eccd542 check validator startup in testnet-sanity.sh 2018-08-10 11:41:31 -07:00
b04c71acd9 check issue 910 in testnet-sanity 2018-08-10 11:41:31 -07:00
bbf9ea89c5 add some flushing to ledger 2018-08-10 11:41:31 -07:00
846ad61941 use ~/.solana instead of PWD to keep cargo happy, don't rsync --append 2018-08-10 11:41:31 -07:00
8b41c415b7 add equal sign 2018-08-10 08:05:48 -07:00
197ba8b395 Fixed punctuation 2018-08-09 16:39:04 -07:00
8d2a61a0c9 Alphabetize bins 2018-08-09 16:23:05 -06:00
7512317243 Alphabetize dependencies 2018-08-09 16:23:05 -06:00
bca2294655 cargo fmt 2018-08-09 13:41:37 -06:00
abd55e4159 Add coding guidelines document 2018-08-09 13:41:37 -06:00
4a980568ac Rename sig variables to signature
We'll avoid introducing three-letter terms to free up the namespace
for three-letter acronyms.

But recognize the term "sigverify", a verb, to verify a digital
signature.
2018-08-09 13:41:37 -06:00
9d436fc5f8 Rename pk variables to pubkey 2018-08-09 13:41:37 -06:00
ad331e6d56 Rename PublicKey type to Pubkey
Recognize pubkey as a noun meaning the public key of a keypair.
2018-08-09 13:41:37 -06:00
d7e4e57548 Rename public_key variables to pubkey 2018-08-09 13:41:37 -06:00
b2067d2721 Rename kp variables to keypair 2018-08-09 13:41:37 -06:00
c2bbe4344e Rename KeyPair to Keypair 2018-08-09 13:41:37 -06:00
8567253833 Ignore flaky test 2018-08-09 10:15:10 -06:00
ca7d4c42dd Rename cur_hashes to num_hashes 2018-08-09 10:15:10 -06:00
8ca514a5ca Remove unnecessary : 2018-08-08 22:45:39 -07:00
b605552079 Record network version in testnet-deploy start datapoint 2018-08-08 22:41:02 -07:00
74f5538bd3 Verify the ledger as a part of sanity 2018-08-08 16:10:54 -07:00
ff57c7b7df Include ledger tool 2018-08-08 15:12:40 -07:00
ce8a4fa831 allow received to outpace window, we're already constraining repair
correctly identify sender in ledger_window repair responses, enabling re-transmission
2018-08-08 15:10:44 -07:00
8331aab26a Enable Crdt debug messages to debug validators 2018-08-08 14:22:20 -07:00
a6857dbaaa Updated node count to 230. Increased wmem on CI large 2018-08-08 13:13:18 -07:00
054298d957 Retry snap install 3 times, sometimes the snap server 503s 2018-08-08 08:56:05 -07:00
cca240c279 Add SOLANA_NET_NAME, rename SOLANA_NET_URL to SOLANA_NET_ENTRYPOINT 2018-08-08 08:49:30 -07:00
89f17ceecf Route setup-args 2018-08-08 08:32:23 -07:00
fe97857c62 Add 'setup-args' snap configuration parameter, to override -p 2018-08-08 08:10:56 -07:00
75854cc234 Update dynamic network test with more nodes (#904)
- Check for correct OS params in test-large-network.sh
2018-08-08 06:52:57 -07:00
9783d47fd1 write a "unit" test for WindowLedger (it was working ;)
clear flags on fresh blobs, lest they sometimes impersonate coding blobs...

fix bug: advance *received whether the blob_index is in the window or not,
  failure to do so results in a stalled repair request pipeline
2018-08-08 04:28:09 -07:00
38be61bd22 Check for log level before doing perf counter work
Perf counters, especially when running the dynamic test can cause
functions like crdt::apply_updates to be really slow (>500ms).
2018-08-08 00:16:53 -07:00
c64e2acf8b set destination address when for ledger window repair responses 2018-08-07 23:31:01 -07:00
a200cedb4b Lower UDP data size to 64k - 128 bytes
rust API gives errors for packets larger than ~65500 and
wikipedia says 65507 is the max size, lowering this avoids the errors.
2018-08-07 18:39:36 -07:00
5fec0ac82f Validators now rsync the ledger smarter
- Don't re-rsync parts of the ledger that are already present
- Disable compression
2018-08-07 17:38:26 -07:00
999534248b fixups 2018-08-07 17:27:53 -07:00
fbc754ea25 plug in LedgerWindow
fixes #872
2018-08-07 17:27:53 -07:00
ecea41a0ab Install EarlyOOM on testnet nodes 2018-08-07 16:58:46 -07:00
1b6d472cb2 Fixed counters for coalescing and broadcast index (#900) 2018-08-07 16:46:48 -07:00
f0446c7e88 Package curl in Snap for metrics_write_datapoint.sh 2018-08-07 22:41:26 +00:00
2a0025bb57 get buffered IO back for ledger 2018-08-07 15:34:15 -07:00
64d6d3015a Counters for broadcasted blob idx and coalesced packets (#897) 2018-08-07 14:54:26 -07:00
90550c5b58 Switch to slice arguments and remove clippy exceptions 2018-08-07 14:43:44 -07:00
53cd2cdd9f Only monitor for OOM kills when a leader, validator or drone is enabled 2018-08-07 14:20:52 -07:00
1ac5d300a4 Rearrange start hash for process_ledger and add a unit test 2018-08-07 14:10:36 -07:00
642c25bd3b Update path 2018-08-07 13:40:49 -07:00
df808dedd1 Add simple OOM Killer monitor 2018-08-07 13:35:01 -07:00
02f9cb415b Ignore failure to write oom_score_adj 2018-08-07 13:35:01 -07:00
e3cf1e6598 Bundle metrics_write_datapoint.sh in Snap 2018-08-07 13:35:01 -07:00
7681211c02 Pacify shellcheck 2018-08-07 13:35:01 -07:00
0ee935dd72 Adjust fullnode/drone oom_score_adj to goad the kernel into killing it first 2018-08-07 10:42:53 -07:00
16772d3d51 Coalesce multiple blobs if received close together (#869)
- This helps reduce unnecessary growth of window if small blobs
  are received in small space of time
2018-08-07 10:29:57 -07:00
1c38e40dee Validate ledger once all the tests complete 2018-08-07 10:00:52 -07:00
ceb5a76609 Refactor validator windowing
- a unit test for windowing functions
- issue #857
2018-08-07 08:17:32 -07:00
db2392a691 Use last_id from the entries stream instead of last_id from bank
bank will only register ids when has_more is not set because those are
the only ids it has advertised, so it will not register all ids,
however the entry stream will contain unbroken last_id chain, so we
need to track that to get the correct start hash.
2018-08-07 08:14:06 -07:00
9c1b6288a4 Use ? instead of unwrap()
This change addresses #833, while there are still some unwrap() though.
2018-08-07 08:10:22 -07:00
575179be8e y 2018-08-06 23:55:00 -07:00
5b6ffaecc0 s/r$/f/ 2018-08-06 23:36:09 -07:00
efc72b9572 Support -V/--version on all CLI apps
All CLI apps that use clap (in other words, except for bench-streamer)
can use crate_version! to take the version from Cargo.toml.

This change addresses #700.
2018-08-06 22:03:58 -07:00
5dc7177540 Remove manually created help text, use clap's text instead. 2018-08-06 19:15:52 -07:00
78a4b1287d Initialize logger 2018-08-06 19:04:04 -07:00
c5001869f1 Add verify subcommand 2018-08-06 19:04:04 -07:00
7c31f217d5 Add voting metric even when there are not enough validators 2018-08-06 15:58:10 -07:00
1152457691 avoid normal validator port a little better for sanity 2018-08-06 15:06:16 -07:00
3beb38ac8a /tmp/farf no good on multi-user machine 2018-08-06 14:53:40 -07:00
8cbaa19d2e Report the address that failed to bind 2018-08-06 11:21:25 -07:00
63d2b2eb42 adjust bank notion of entry_count to aid debugging 2018-08-06 11:20:52 -07:00
e02da9a15a Clean up tx_count usage 2018-08-06 11:00:25 -07:00
ae111a131c Condense stdout 2018-08-06 11:00:25 -07:00
4402e1128f Cleanup 2018-08-06 11:00:25 -07:00
f55bb6d95c Send/confirm a loopback payment after each batch of transactions 2018-08-06 11:00:25 -07:00
91741e20fa Add rustc/cargo version check 2018-08-06 09:32:08 -07:00
0514f5e573 sync() apparently imposes a serious performance penalty 2018-08-06 08:51:41 -07:00
637d403415 move bank.process_entries() to firsties 2018-08-06 08:51:41 -07:00
9fabd34156 remove trace! calls, re-arrange replicate_requests to have the bank earlier 2018-08-06 08:51:41 -07:00
039ed01abf on 2nd thought: do not copy_ledger() for this test 2018-08-06 08:51:41 -07:00
ead0eb2754 move copy_ledger() back into ledger.rs
Don't recover() for copy(), as copy() is already tolerant of things
    recover() guards against.  Note: recover() is problematic if the ledger is
    "live", i.e. is currently being written to.
2018-08-06 08:51:41 -07:00
c3db2df7eb tweak random access ledger
* add recover_ledger() to deal with expected common ledger corruptions
  * add verify_ledger() for future use cases (ledger-tool)
  * increase ledger testing
  * allow replicate stage to run without a ledger
  * ledger-tool to output valid json
2018-08-06 08:51:41 -07:00
ee6c15d2db start on ledger recovery with a description of what that might mean 2018-08-06 08:51:41 -07:00
715a3d50fe Revert "Revert "clippy fixup""
This reverts commit d173e6ef87.
2018-08-06 08:51:41 -07:00
692b125391 Revert "Revert "fixups""
This reverts commit e2c68d8775.
2018-08-06 08:51:41 -07:00
5193819d8e Revert "Revert "plug in new ledger""
This reverts commit 57e928d1d0.
2018-08-06 08:51:41 -07:00
210b9d346f Add voting metrics and -h/--help to get usage for client.sh script 2018-08-05 14:21:49 -07:00
4c4b0f551e clippy fixups 2018-08-05 13:30:45 -07:00
6800ff1882 solana-ledger-tool initial commit
does nothing but convert from random-access ledger to json
2018-08-05 13:30:45 -07:00
399a3852b1 Add sigverify_stage-total_verify_time datapoint 2018-08-04 21:45:58 -07:00
e7d3069f58 macOS: Adjust maxdgram to allow for large UDP packets 2018-08-04 21:42:59 -07:00
40ea3e3e61 tweak multinode-demo to work better in snap, validator-x be more stand-alone 2018-08-04 01:04:06 -07:00
dc9a11bae0 remove rsync size limit for validator's ledger 2018-08-03 23:31:25 -07:00
906d18a709 move VOTE to trace, info too verbose 2018-08-03 23:04:54 -07:00
a13058b6c4 Look for 3 nodes (1 leader, 2 validators) 2018-08-03 20:30:29 -07:00
98ee4b4672 fix up some nits in multinode-demo 2018-08-03 20:19:41 -07:00
7fd7310b96 Prevent a node from overrunning it's receive window (#846)
- The node drops blobs that will cause it to overrun window
- The node does not ask to repair a blob that overruns the window
2018-08-03 20:15:14 -07:00
28fa43d2a9 Use env_logger@0.5.12 2018-08-03 20:08:30 -07:00
1a9e6ffdd7 Try multiple times to confirm a non-zero balance 2018-08-03 19:57:38 -07:00
c998199954 fixups, add validator-x to sanity 2018-08-03 15:34:11 -07:00
19792192a7 support any number of self-setup validators on a single host 2018-08-03 15:34:11 -07:00
4aab413154 recycle the skipped, outside-window blob, fixes #843 2018-08-03 15:02:55 -07:00
15a6179b97 Stop installing rustfmt-preview, it's already present 2018-08-03 14:27:11 -07:00
83b308983f Include rustfmt-preview 2018-08-03 14:11:42 -07:00
f2b1a04bca cargo fmt fixups 2018-08-03 11:59:25 -07:00
3e36e6dcf8 Upgrade to rust 1.28 2018-08-03 11:30:40 -07:00
6feb6a27be Run localnet-sanity in test-stable-perf 2018-08-03 10:46:48 -07:00
c5ceb15e02 Skip network tuning on CI machines 2018-08-03 10:46:48 -07:00
57e928d1d0 Revert "plug in new ledger"
This reverts commit 46d9ba5ca0.
2018-08-03 10:24:51 -07:00
e2c68d8775 Revert "fixups"
This reverts commit b72e91f681.
2018-08-03 10:24:51 -07:00
d173e6ef87 Revert "clippy fixup"
This reverts commit 384b486b29.
2018-08-03 10:24:51 -07:00
c230360f4c Wait until recycled machines are reachable before provisioning them 2018-08-02 22:13:17 -07:00
384b486b29 clippy fixup 2018-08-02 21:50:47 -07:00
b72e91f681 fixups 2018-08-02 21:50:47 -07:00
46d9ba5ca0 plug in new ledger 2018-08-02 21:50:47 -07:00
a9240a42bf Delete unreachable validators to cause a fresh one to be spawned 2018-08-02 20:45:29 -07:00
a7204d5353 Use a local user to avoid GCP login quota limits 2018-08-02 19:43:35 -07:00
f570ef1c66 Defer repair request for blobs that may still be in avalanche transit (#814) 2018-08-02 19:12:57 -07:00
ee0195d588 Try to measure finality from time seen to when 2/3 of validator..
..set has voted. Add a timestamp to last_ids and use that to
see how long from when 2/3s validator set has voted on them.
2018-08-02 13:21:29 -07:00
448b8b1c17 Add Hash wrapper and supporting traits 2018-08-01 17:00:51 -07:00
4d77fa900b Add Signature wrapper and supporting traits 2018-08-01 17:00:51 -07:00
7ccd771ccc Only send sigverify to GPU if batch size is >64
Seems to be a decent crossover point for Xeon E5-2620 v4 8c,16t vs. nvidia 1080ti
2018-08-01 16:38:15 -07:00
e9f8b5b9db Fix bench 2018-08-01 16:24:47 -07:00
2366c1ebaf Enable cargo audit in CI
Fixes #772
2018-08-01 16:24:47 -07:00
c5de237276 Upgrade ring and untrusted 2018-08-01 16:24:47 -07:00
aa9bc57b4d Implement GenKeys without SecureRandom 2018-08-01 16:24:47 -07:00
11df477b20 Make GenKey functions mut
We hide the mutability to implement SecureRandom, but that's going
away.
2018-08-01 16:24:47 -07:00
7141750668 new_key -> gen_keypair 2018-08-01 16:24:47 -07:00
68675bd1ab Less pub 2018-08-01 16:24:47 -07:00
19b3cacd60 Generate a fixed-size array instead of a vector 2018-08-01 16:24:47 -07:00
bcfaf5d994 Rebase ledger change 2018-08-01 16:15:14 -07:00
e9499ac5b8 Update PublicKey AsRef to slice 2018-08-01 16:15:14 -07:00
7ff721e563 Replace pub field with AsRef impl 2018-08-01 16:15:14 -07:00
fda3b9bbd4 Use new PublicKey format instead of hex 2018-08-01 16:15:14 -07:00
cf70e5ff2f Handle wrapped PublicKey struct 2018-08-01 16:15:14 -07:00
a86618faf3 Add PublicKey wrapper
Add custom formatting for PublicKey display and debug
2018-08-01 16:15:14 -07:00
6693386bc5 Lower errors to warnings so they don't print during tests
Negative tests should trigger the warnings, but errors look like
something is wrong.
2018-08-01 16:56:12 -06:00
4a8a0d03a3 Correct localhost address 2018-08-01 15:49:48 -07:00
2c9d288ca9 Add a CI metric data point upload timeout to prevent CI build stalls
5 seconds is somewhat arbitrary, seems like enough
2018-08-01 15:49:48 -07:00
bb0aabae75 Add cmake, which is needed to build cargo-audit 2018-08-01 16:43:49 -06:00
5cda0ed964 Airdrop from the leader 2018-08-01 15:21:20 -07:00
0aba74935b fixups 2018-08-01 14:42:58 -07:00
4eb666d4f9 provide ledger::copy() 2018-08-01 14:42:58 -07:00
d5e0cf81ff fixups 2018-08-01 14:42:58 -07:00
3ea784aff7 clippy fixups 2018-08-01 14:42:58 -07:00
fef93958c8 fixups, tests 2018-08-01 14:42:58 -07:00
cae88c90b1 add a persistent ledger of index and data files 2018-08-01 14:42:58 -07:00
1a8da769b6 ... 2018-08-01 14:42:58 -07:00
2b259aeb41 testnet now deploys successfully on days of the month < 10 2018-08-01 14:10:52 -07:00
de7e9b4b4c Remove retry
This was introduced to mask the occasional failure of racy tests.  But this is misguided as it helps hid the true problem, the racy test, and it causes tries builds that fail deterministically to retry only to fail once again.
2018-08-01 12:02:39 -07:00
0f95031b99 CI builds no longer turn red if a metrics write fails 2018-08-01 11:35:19 -07:00
d622742b84 Mark test-multinode-basic as ignore 2018-08-01 10:13:05 -06:00
ff254fbe5f re-instate traces 2018-08-01 09:08:38 -07:00
05153e4884 de-trace this function, new blob is not a dup 2018-08-01 09:08:38 -07:00
2ece27ee3a fix leak 2018-08-01 09:08:38 -07:00
a58df52205 Fix build
Last two PRs crossed in flight. A keypair is now required for all
types of FullNode, not just validators.
2018-08-01 08:53:21 -07:00
2ea6f86199 Submit leader's vote after observing 2/3 validator votes (#780)
* fixup!

* fixups!

* send the vote and count it

* actually vote

* test

* Spelling fixes

* Process the voting transaction in the leader's bank

* Send tokens to the leader

* Give leader tokens in more cases

* Test for write_stage::leader_vote

* Request airdrop inside fullnode and not the script

* Change readme to indicate that drone should be up before leader

And start drone before leader in snap scripts

* Rename _kp => _keypair for keypairs and other review fixups

* Remove empty else
* tweak test_leader_vote numbers to be closer to testing 2/3 boundary
* combine creating blob and transaction for leader/validator
2018-07-31 22:07:53 -07:00
7c5172a65e Converted sigverify disable flag to runtime check instead of "cfg" (#799) 2018-07-31 16:54:24 -07:00
821e3bc3ca Avoid race between test_lograte and test_lograte_env 2018-07-31 16:08:01 -07:00
5dd2f737a3 clear out old blobs in find_next_missing 2018-07-31 15:54:32 -07:00
c9bb5c1f5b Update snap log file documentation 2018-07-31 13:13:27 -07:00
5d936e5c8a Trap SIGINT for clean ^C shutdown 2018-07-30 17:15:50 -07:00
e985c2e7d5 .gitignore more generated files 2018-07-30 17:15:50 -07:00
308b6c3371 Follow Shared prefix convention for Window alias (#798)
Follow Shared prefix convention for Window alias.
2018-07-30 16:56:01 -07:00
ea7fa11b3e use size_of() instead of serialized_size() and magic number 8 2018-07-30 16:48:58 -07:00
5a40ea3fd7 Only map HOME when in CI 2018-07-30 16:36:26 -07:00
102510ac0e Clear apt cache to reduce image size 2018-07-30 16:36:26 -07:00
2158329058 Switch to docker-rust image 2018-07-30 16:36:26 -07:00
bc484ffe5f Add docker-rust image 2018-07-30 16:36:26 -07:00
6fcf4584d5 Propagate more BUILDKITE environment variables into containers 2018-07-30 16:36:26 -07:00
1adc83d148 Add localnet-sanity.sh 2018-07-30 16:36:26 -07:00
647053e973 Terminate child process when main script is interrupted 2018-07-30 16:36:26 -07:00
95b98b3845 Fix --addr option 2018-07-30 16:36:26 -07:00
f27613754a Report number of nodes found on failure too 2018-07-30 16:36:26 -07:00
3e351b0b13 Drop -t 2018-07-30 16:13:51 -07:00
79ece53e3c Don't panic the tokio worker thread when deserialize() fails 2018-07-30 14:56:53 -07:00
f341b2ec10 fixups 2018-07-30 14:26:44 -07:00
167b079e29 fixups 2018-07-30 14:26:44 -07:00
7ded5a70be fixups 2018-07-30 14:26:44 -07:00
fc476ff979 implement iterator for parsing length + data ledger 2018-07-30 14:26:44 -07:00
c3279c8a00 chugga 2018-07-30 14:26:44 -07:00
e471ea41da fixups 2018-07-30 14:26:44 -07:00
552d4adff5 use a binary ledger: newline-separated, newline-escaped entries instead of json 2018-07-30 14:26:44 -07:00
0c33c9e0d7 Dynamic network test changes (#795)
- No sigverify if feature sigverify_cpu_disable is used
- Purge validators in the test if lag count increases beyond
  SOLANA_DYNAMIC_NODES_PURGE_LAG environment variable
- Other useful log messages in the test
2018-07-30 13:57:10 -07:00
fae9fff24c Unify logging initialization 2018-07-29 19:08:27 -07:00
79924e407c Include nanoseconds in log timestamp 2018-07-29 19:08:27 -07:00
18d4da0076 Fetch env_logger from github until 0.5.12 is available 2018-07-29 19:08:27 -07:00
416c141775 export SKIP_INSTALL=1 to reset the network without reinstalling the snap 2018-07-28 18:04:13 -07:00
af1a2e83bc Don't panic again when waiting for a panicked validator thread 2018-07-28 16:35:35 -07:00
4cdb9a73f8 Skip testnet-sanity on manual deploy 2018-07-28 12:37:29 -07:00
4433730610 Add support for deploying a locally built snap 2018-07-28 12:37:29 -07:00
71eb5bdecc Factor out vm_foreach 2018-07-28 12:37:29 -07:00
029e2db2cf Improve assert message 2018-07-28 10:40:50 -07:00
81db333490 Guard against rsyncing TBs of ledger 2018-07-27 23:53:20 -07:00
c68ee0040d No need to support migrating from the old ledger format anymore 2018-07-27 23:53:20 -07:00
d96e267624 Keep around 3GB of logs, 160MB is just not enough 2018-07-27 22:40:21 -07:00
0b47404ba6 Check for default leader and use cmp::max for a bit nicer code (#779) 2018-07-27 15:53:31 -07:00
7f4844f426 More stats in dynamic multinode test 2018-07-27 11:55:09 -07:00
50e1e0ae47 use rust's rotate (in place, yay!) 2018-07-27 11:44:02 -07:00
538c3b63e1 Log the last_id being voted on 2018-07-27 11:27:51 -07:00
678b2870ff i 2018-07-27 11:11:37 -07:00
308d8c254d poll_get_balance no longer fails intermittently for zero balance accounts
While polling for a non-zero balance, it's not uncommon for one of the
get_balance requests to fail with EWOULDBLOCK.  Previously when a get_balance
request failure occurred on the last iteration of the polling loop,
poll_get_balance returned an error even though the N-1 iterations may have
successfully retrieved a balance of 0.
2018-07-26 21:41:07 -07:00
f11aa4a57b Ensure non-zero exit code if 'balance' command fails 2018-07-26 21:41:07 -07:00
c52d4eca0b Stop validator first to stop voting before the leader stops 2018-07-26 17:29:32 -07:00
7672506b45 Validators now vote once a second regardless 2018-07-26 17:07:42 -07:00
80a02359f7 Add script to audit for security vulnerabilities 2018-07-26 13:42:12 -07:00
ab3968e3bf Dedup 2018-07-26 11:45:58 -07:00
42ebf9502a Agent cleaning is now performed in a separate pipeline 2018-07-26 11:37:36 -07:00
bd4fcf4ac6 Clean out stale buildkite agent build directories 2018-07-26 11:37:36 -07:00
4dceb73909 Reinstall client nodes in the background to speed up deploys 2018-07-26 09:49:00 -07:00
dd819cec3d fix off by one in packet.rs 2018-07-26 09:24:44 -07:00
5115cd7798 large network back to erasure 2018-07-25 20:45:16 -07:00
cbb8dee360 rework broadcast to understand a separate transmit index for coding blobs 2018-07-25 20:45:16 -07:00
e0cdcb0973 employ the simple choice for broadcast table of coding blobs: round-robin 2018-07-25 20:45:16 -07:00
a6a2a745ae fix broadcast of erasure coding blobs
erasure coding blobs were being counted as window slots, skewing transmit_index

erasure coding blobs were being skipped over for broadcast, because they're
  only generated when the last data blob in an erasure block is added to the
  window.... rewind the index to pick up and broadcast those coding blobs
2018-07-25 20:45:16 -07:00
297896bc49 honor environment variable SOLANA_DYNAMIC_NODES, obviating need to edit+compile for re-test 2018-07-25 20:45:16 -07:00
f372840354 Collect some datapoints while bench-tps is running 2018-07-25 20:15:43 -07:00
4c4659be13 Add more stdout 2018-07-25 16:38:21 -07:00
1b79fe73a1 Emit a metrics datapoint if bench-tps terminates 2018-07-25 15:55:02 -07:00
5fa072cf16 Avoid quotes around net name 2018-07-25 15:55:02 -07:00
212874e155 Use BlobError for get_size return 2018-07-25 15:54:04 -07:00
75212f40e7 fix off by one for send_to() of blob 2018-07-25 15:16:56 -07:00
6fde65577e fixes #756 2018-07-25 11:07:03 -07:00
80ecef2832 Add --sustained to ci testnet deploy script 2018-07-25 10:16:46 -07:00
edf2ffaf4e Reduce complexity of main for clippy
...and readability
2018-07-25 10:16:46 -07:00
6c275ea5ef More knobs. Arg for tx count per batch and also sustained mode
sustained mode overlaps tx generation with transfer. This mode seems
to have lower peak performance but higher average performance
2018-07-25 10:16:46 -07:00
23ed65b339 Transfer and sign at the same time in bench-tps 2018-07-25 10:16:46 -07:00
9c7913ac9e trying to raise an error 2018-07-25 08:12:20 -07:00
8b01e6ac0b implement Blob::get_size(), the counterpart of Blob::set_size() 2018-07-25 08:12:20 -07:00
ff5854396a deserialize using get_data_size(), which refers to blob.data()'s length,
instead of using msg.meta.size, which refers to the entire blob's length

fixes #752
2018-07-25 08:12:20 -07:00
f0725b4900 Avoid panicking if poll_get_balance() fails while in the transaction loop 2018-07-24 23:31:28 -07:00
327ba5301d Log token balance throughout the transfer loop 2018-07-24 22:40:12 -07:00
dcce475f0b Progagate logging configuration to client nodes 2018-07-24 21:40:02 -07:00
aa2104a21b Reclaim tokens before exiting to avoid leaking tokens 2018-07-24 21:40:02 -07:00
0206020104 Make airdrops more robust 2018-07-24 21:40:02 -07:00
33bd1229d9 make next_entries() smarter about fitting Transactions into a Blob 2018-07-24 21:38:06 -07:00
195098ca2b Failure test case 2018-07-24 21:38:06 -07:00
9daa7bdbe2 Replace rayon with threads for dynamic network test (#745) 2018-07-24 17:54:29 -07:00
6bd18e18ea Add error messages to ledger verify 2018-07-24 17:35:41 -07:00
8f046cb1f8 disable erasure for large network testing 2018-07-24 16:54:52 -07:00
735a0ee16d Switch back to running bench-tps in 10 minute iterations 2018-07-24 15:43:25 -07:00
537be6a29d export SOLANA_DEFAULT_METRICS_RATE 2018-07-24 15:43:25 -07:00
2b528e2225 fixups 2018-07-24 13:04:34 -07:00
75505bbd72 fixups 2018-07-24 13:04:34 -07:00
e1fc7444f9 fixups 2018-07-24 13:04:34 -07:00
940caf7876 test large network with erasure 2018-07-24 13:04:34 -07:00
fcdb0403ba eliminate unused parameter received, this branch fixes #636 2018-07-24 13:04:34 -07:00
caeb55d066 placate clippy and reduce replicode 2018-07-24 13:04:34 -07:00
f11e60b801 fix major bug: re-used blobs need to have their flags cleared
plus: lots of additional debug-ability
2018-07-24 13:04:34 -07:00
54f2146429 fixups 2018-07-24 13:04:34 -07:00
f60ee87a52 zero the tails of data blobs during generate() and recover() to enable blob reuse 2018-07-24 13:04:34 -07:00
9c06fe25df enhance unit test to fail when erasure encodes stray bytes of data blobs 2018-07-24 13:04:34 -07:00
1eec8bf57f fixups 2018-07-24 13:04:34 -07:00
ddb24ebb61 fixups 2018-07-24 13:04:34 -07:00
a58c83d999 prevent infinite loop on window wraparound 2018-07-24 13:04:34 -07:00
6656ec816c protect generate and recover from u64->usize casting issues 2018-07-24 13:04:34 -07:00
8d2bd43100 fixups 2018-07-24 13:04:34 -07:00
429ea98ace mutable-coding-blocks 2018-07-24 13:04:34 -07:00
3d80926508 fixups 2018-07-24 13:04:34 -07:00
d713e3c2cf send coding in broadcast(), fixups 2018-07-24 13:04:34 -07:00
5d20d1ddbf get test_window_recover_basic() passing 2018-07-24 13:04:34 -07:00
257acdcda1 building now 2018-07-24 13:04:34 -07:00
dab98dcd81 coded => coding 2018-07-24 13:04:34 -07:00
99653a4d04 rework erasure to have data and coding blobs side-by-side in window 2018-07-24 13:04:34 -07:00
dda563a169 document process_blob() 2018-07-24 13:04:34 -07:00
782aa7b23b Cap at 4 threads 2018-07-24 11:35:03 -07:00
813e438d18 Improve panic message 2018-07-24 11:20:13 -07:00
7a71adaa8c Adjust threads by the number of cpus 2018-07-23 21:17:36 -07:00
ce8796bc2e Correctly calculate the expected number of full nodes 2018-07-23 19:55:09 -07:00
c7e1409f7b Not so much |set -x| 2018-07-23 19:55:09 -07:00
9de9379925 Add support more more than 1 client node 2018-07-23 19:34:34 -07:00
7d68b6edc8 Fixup arg processing 2018-07-23 16:51:39 -07:00
48b5344586 Check for 0 TPS explicitly 2018-07-23 16:51:39 -07:00
686b7d3737 Report panics if metrics are setup 2018-07-23 16:51:39 -07:00
7c65e2fbfc Rename variable to improve readability 2018-07-23 16:51:39 -07:00
96a6e09050 Enable metrics in the TPS client 2018-07-23 16:51:39 -07:00
b3f823d544 Alternate between token reclaim and distribution 2018-07-23 13:17:52 -07:00
ea21c7a43e Limit bench-tps last_id poll to prevent infinite loop 2018-07-23 13:17:52 -07:00
437fb1a8d7 Add -a argument to client in case you want to override the address
advertised by the client
2018-07-23 13:17:52 -07:00
166099b9d9 Start validators in parallel in multinode test (#727) 2018-07-23 09:27:06 -07:00
c707b3d2e7 Display the total number of transactions for each node once complete 2018-07-22 23:19:33 -07:00
f7d294de90 Don't rsync leader.json on every iteration 2018-07-22 17:25:00 -07:00
4ecd0a0e45 Improve bench-tps logging 2018-07-22 16:26:49 -07:00
7ebbaaeb2d Use bench-tps default duration 2018-07-22 16:26:49 -07:00
cdcf59ede0 Display a list of all discovered nodes 2018-07-22 11:32:44 -07:00
5d065133ef Add data point for testnet startup and shutdown 2018-07-21 23:27:24 -07:00
d403808564 Restart solana.bench-tps every 10 minutes to work around memory leak
cc: #728
2018-07-21 19:48:13 -07:00
3ffdca193d Rename client-demo to bench-tps catchup 2018-07-21 15:46:03 -07:00
69688a18c7 Fix clippy warnings
Seems clippy is not linting any of the benches.
2018-07-21 11:36:20 -04:00
7193bf28b6 Move streamer bench into standalone executable
It doesn't make use of criterion (or libtest)
2018-07-21 11:36:20 -04:00
637f890b91 Rename client-demo to bench-tps 2018-07-21 11:36:20 -04:00
009d5adcba Tell the client to transact for 1 hour blocks 2018-07-20 17:52:19 -07:00
52c55a0335 Log to /tmp/solana.log for easy runtime inspection of client activity 2018-07-20 17:45:13 -07:00
23428b0381 Migrate drone to poll_for_signature 2018-07-20 20:33:55 -04:00
0e305bd7dd Add poll_for_signature 2018-07-20 20:33:55 -04:00
c068ca4cb7 Return Signature from transfer_signed and send_airdrop 2018-07-20 20:33:55 -04:00
6a8379109d Sleep between retries
Don't congest a congested network.
2018-07-20 20:33:55 -04:00
120add0e82 Add support for a client node running continuous transactions on the net 2018-07-20 17:07:36 -07:00
b92ee51c2d Add --loop flag to easily send transactions continuously 2018-07-20 17:07:36 -07:00
cba3b35ac9 Change not_enough_peers to the default log rate 2018-07-20 11:37:12 -07:00
313fed375c Add counter for tx count and limit error messages 2018-07-20 11:37:12 -07:00
1e63702c36 cargo fmt 2018-07-20 13:09:01 -04:00
478ee9a1c4 move tests for 'is_valid_address()' into its own test 2018-07-20 13:09:01 -04:00
eb1e5dcce4 add test for 'is_valid_address()' 2018-07-20 13:09:01 -04:00
84225beeef replace 'daddr' checks with 'is_valid_address()' 2018-07-20 13:09:01 -04:00
9cf0bd9b88 Adjust variable name 2018-07-20 09:50:24 -07:00
9d25d7611a Protect against unsupported configurations to prevent non-obvious errors later 2018-07-20 09:47:01 -07:00
1abefb2c7a Pass expected node count to testnet-sanity 2018-07-20 09:47:01 -07:00
bcc247f25f Clarify code comment 2018-07-20 12:31:23 -04:00
68ca9b2cb8 Generalize testnet deployment scripts 2018-07-20 09:10:28 -07:00
686e61d50c Display max TPS from all nodes at end of client demo (#716)
- Also lists node with 0 TPS and overall average TPS
2018-07-19 20:09:57 -07:00
17d927ac74 Count testnet nodes as a part of sanity 2018-07-19 12:05:21 -07:00
966c55f58e Trim CUDA runtime 2018-07-19 11:47:31 -07:00
d76d3162e5 Slow down deployment more 2018-07-19 10:11:04 -07:00
d0a2d46923 Don't shellcheck in target/ 2018-07-19 09:41:09 -07:00
a67f58e9a5 Add -c option to easily interrogate the number of nodes 2018-07-19 09:41:09 -07:00
fece91c4d1 Test multi node dynamic network ci (#696)
Buildkite automation for multinode test.  This test is ignored by default because it requires a large cpu machine to run.
2018-07-19 07:50:44 -07:00
9d2d9a0189 Update Cargo.toml 2018-07-19 09:02:54 -04:00
6d3afc774a Version bump
And authors update
2018-07-19 09:01:06 -04:00
88646bf27d Version bump 2018-07-19 08:41:18 -04:00
0696f9f497 flush writer, makes partial deserialization a bit less likely 2018-07-18 22:53:33 -07:00
b2ea2455e2 Disable rolling updates 2018-07-18 21:16:03 -07:00
3f659a69fd Prevent nodes from gossiping with themselves with different ids 2018-07-18 19:38:38 -07:00
2c62be951f boot invalid height (#688) 2018-07-18 18:10:53 -07:00
2348733d6c remove drone port magic number 2018-07-19 02:01:23 +02:00
cc229b535d Remote multinode script cleanup (#683)
- Create a known_hosts file if it doesn't exist
  Otherwise ssh-keygen exits
- Move some common rsync code to common_start_setup
- Build the project before deploying it
2018-07-18 16:02:05 -07:00
7f810a29ff Purge leader (#687)
* purge leader

* fixup!

* fixup!
2018-07-18 14:39:43 -07:00
fc1dfd86d2 Disable coverage again :-/ 2018-07-18 12:54:50 -07:00
5deb34e5bd Little more trace! logging 2018-07-18 12:54:50 -07:00
39df087902 Permit more than the requested amount of nodes 2018-07-18 12:07:50 -07:00
6ff46540b6 Install llvm-cov on nightly to revive coverage
Towards #433
2018-07-18 12:52:13 -04:00
dbab8792e4 Use real default value 2018-07-18 08:23:59 -07:00
4eb676afaa Tunnel SOLANA_DEFAULT_METRICS_RATE into Snap nodes 2018-07-18 08:23:59 -07:00
a6cb2f1bcf Version bump 2018-07-18 09:07:25 -04:00
28af9a39b4 Don't clone before borrowing
Clippy told us to change function parameters to references, but
wasn't able to then tell us that the clone() before borrowing
was superfluous. This patch removes those by hand.

No expectation of a performance improvement here, since we were
just cloning reference counts. Just removes a bunch of noise.
2018-07-18 08:04:31 -04:00
8cf5620b87 crdt_insert_new_entry_counter (#680) 2018-07-17 22:55:53 -07:00
85d6627ee6 Deploy in one ssh login in a further attempt to avoid hitting GCP login quota 2018-07-17 20:45:52 -07:00
611a005ec9 Avoid |wait| as it masks failures 2018-07-17 19:52:39 -07:00
90b3b90391 -p 2018-07-17 19:42:00 -07:00
fd4f294fd3 Rotate logs at 16MB 2018-07-17 19:42:00 -07:00
145274c001 Ensure log directories are go+r 2018-07-17 18:16:40 -07:00
df5d6693f6 Don't cache leader.json to make it easier to switch between nets 2018-07-17 18:16:40 -07:00
05c5603879 error counter 2018-07-17 17:28:23 -07:00
c2c48a5c3c write stage broadcast counters 2018-07-17 17:28:23 -07:00
4af556f70e Added tests for bad gossip address (#672) 2018-07-17 16:27:46 -07:00
8bad411962 env variable for default metrics rate that gets set for counters (#670)
* env variable for default metrics rate that gets set for counters

* ignore if env rate is set to 0

* use a slow rate by default

* fixed test
2018-07-17 15:26:10 -07:00
5b0418793e Keep Snap fullnode/drone logs out of syslog, we're too spammy 2018-07-17 15:08:35 -07:00
4423ee6902 Renamed start_nodes.sh to remote_nodes.sh (#669) 2018-07-17 15:01:53 -07:00
f0c39cc84d Remote multinode scripts cleanup (#666)
- Also added support for stop nodes
2018-07-17 13:48:25 -07:00
3d45b04da8 review comments 2018-07-17 15:51:32 -04:00
9e2f26a5d2 review comments 2018-07-17 15:51:32 -04:00
a016f6e82e bulds 2018-07-17 15:51:32 -04:00
eb3e5fd204 server too 2018-07-17 15:51:32 -04:00
72282dc493 fast exit dynamic test 2018-07-17 15:51:32 -04:00
47a22c66b4 Include program name in panic metric 2018-07-17 12:13:22 -07:00
fb11d8a909 Install panic hook 2018-07-17 12:13:22 -07:00
7d872f52f4 Add set_panic_hook 2018-07-17 12:13:22 -07:00
d882bfe65c Ignore/log RequestWindowIndex from self 2018-07-17 12:12:54 -07:00
103584ef27 Use public IP for client gossip, if UPnP fails (#665) 2018-07-17 11:23:32 -07:00
1fb537deb9 Do not generate gossip requests to unspecified addresses (#657)
* Do not generate gossip requests to unspecified addresses

* review comments
2018-07-17 09:44:48 -07:00
2bd48b4207 Display better deploy logs 2018-07-17 09:10:55 -07:00
f5a6db3dc0 Add daemon plugs 2018-07-17 08:24:37 -07:00
dd0c1ac5b2 Error counters for streamer (#658)
* error counters for streamer

* more counters
2018-07-17 08:20:35 -07:00
d8c9655128 Dynamic test assert (#643)
* log responder error to warn

* log responder error to warn

* fixup!

* fixed assert

* fixed bad ports issue

* comments

* test for dummy address in Crdt::new instaad of NodeInfo::new

* return error if ContactInfo supplied to Crdt::new cannot be used to connect to network

* comments
2018-07-16 19:31:52 -07:00
09f2d273c5 less intrusive counters (#655)
* less intrusive counters

* fixed arg

* tests

* comments
2018-07-16 18:33:50 -07:00
f6eb85e7a3 Permit Snap RUST_LOG to be overridden 2018-07-16 17:44:54 -07:00
0d85b43901 Fix input parameter processing for client num nodes (#653) 2018-07-16 17:23:35 -07:00
fdf94a77b4 CUDA is now configurable 2018-07-16 16:23:45 -07:00
af40ab0c04 Split start_nodes script ssh commands to individual scripts (#642) 2018-07-16 16:21:32 -07:00
015b7a1ddb dash for namespaces (#649) 2018-07-16 15:55:54 -07:00
ab3e460e64 insert votes as they are observed 2018-07-16 13:39:20 -07:00
194a84c8dd Add testnet-sanity.sh 2018-07-16 12:17:39 -07:00
51d932dad1 Connect validators to the right leader 2018-07-16 11:05:01 -07:00
561d31cc13 Add support for master.testnet.s.c 2018-07-16 10:08:58 -07:00
d6a8e437bb Temporarily disable erasure 2018-07-16 08:15:47 -07:00
4631af5011 counters for vote not found 2018-07-15 20:31:23 -06:00
5d28729b2a Use ed25519_init() for faster failures 2018-07-15 20:30:32 -06:00
8c08e614b7 Start validator nodes in parallel
- This speeds up overall network startup time
2018-07-15 19:11:52 -06:00
e76bf1438b A validator and leader running from the same workspace no longer share an identity 2018-07-15 13:34:48 -07:00
4e177877c9 Add more error checking, better logging, avoid hitting GCP login quota 2018-07-15 09:27:25 -07:00
60848b9d95 Testnet sanity test failures will now turn the build red 2018-07-14 21:27:27 -07:00
79b3564a26 Log metrics params to stderr
Keep stdout clean for the actual program.  This is a specific concern for the
wallet command, where there exists tests that capture stdout from the wallet to
confirm transactions.
2018-07-14 21:24:22 -07:00
1e8c36c555 Be less noisy 2018-07-14 20:42:00 -07:00
94d015b089 Demote log level 2018-07-14 20:42:00 -07:00
cfb3736372 Update buildkite-snap.yml 2018-07-14 17:55:03 -07:00
2b77f62233 Poll longer while waiting for an airdrop 2018-07-14 17:10:44 -07:00
e8d23c17ca timeout++ 2018-07-14 15:51:32 -07:00
a7ed2a304a Add CUDA libraries 2018-07-14 15:27:24 -07:00
0025b42c26 Locate perf libs 2018-07-14 10:24:20 -07:00
3f7f492cc0 Fix snap client-demo fixes 2018-07-14 00:18:54 -07:00
490d7875dd Snap client-demo fixes 2018-07-13 23:51:33 -07:00
4240edf710 solana.client-demo now runs client.sh for the bash extras 2018-07-13 22:57:38 -07:00
30e50d0f70 Log airdrop amount and client public key 2018-07-13 22:41:52 -07:00
751c1eba32 Run wallet-sanity against the new testnet 2018-07-13 22:21:41 -07:00
d349d6aa98 USE_SNAP=1 is now supported 2018-07-13 22:21:41 -07:00
1f9152dc72 Detect and report airdrop failures 2018-07-13 18:08:28 -07:00
1b9d50172b Correct log message 2018-07-13 18:08:28 -07:00
084dbd7f58 Fail gracefully when leader.json is missing 2018-07-13 17:24:25 -07:00
58c0508f94 add drone information to multinode demo instructions 2018-07-13 17:16:55 -07:00
dcf82c024f Surface hidden call to solana-keygen 2018-07-13 16:16:46 -07:00
b253ed0c46 Version bump 2018-07-13 15:10:45 -06:00
61db53fc19 Version bump 2018-07-13 15:04:10 -06:00
b0ead086a1 Fix bad copy'n'paste 2018-07-13 13:04:38 -07:00
a3b22d0d33 Faster benchmarking for CI
Increase that sample size manually when you're doing performance
work.
2018-07-13 14:03:50 -06:00
28d24497a3 Wait for the leader to initialize before starting the validators 2018-07-13 12:32:24 -07:00
05cea4c1da dedup 2018-07-13 11:48:17 -07:00
260f5edfd6 Use correct leader.json 2018-07-13 11:48:17 -07:00
7105136595 Enable CUDA for the leader node 2018-07-13 11:36:12 -07:00
54db379bf2 Refresh in parallel 2018-07-13 11:19:31 -07:00
effbf0b978 Add script to refresh testnet nodes 2018-07-13 11:19:31 -07:00
8e7a2a9587 Validators now request an airdrop of 1 token before starting up 2018-07-13 10:02:19 -07:00
18e6ff4167 Fail gracefully when keypair file is unreadable 2018-07-13 10:00:55 -07:00
fa1cdaa91a Add home plugs to enable Snap access to ~/.config/solana/id.json 2018-07-13 09:32:37 -07:00
b538b67524 Bump timeout for stable build
When the CI build machine caches are empty stable occasionally needs more than 20m
2018-07-13 09:17:45 -07:00
2b0f6355af Tagged snap builds now correctly publish to the beta channel 2018-07-12 23:43:59 -07:00
11b9a0323d fixups 2018-07-12 22:51:55 -07:00
710fa822a0 fixups 2018-07-12 22:51:55 -07:00
aaf6ce5aea fixups 2018-07-12 22:51:55 -07:00
34ea483736 step two: supply a ledger file argument to fullnode in the demo
(also whack unused "myip.sh", even though it was pretty)
2018-07-12 22:51:55 -07:00
a3ff40476e Banish stdin/stdout for ledger
step one: accept a "ledger file" argument instead of "outfile"
2018-07-12 22:51:55 -07:00
4cca3ff454 Fix keypair option in scripts
Thanks @CriesofCarrots!
2018-07-12 21:50:28 -06:00
3d9acdd970 Fix nightly 2018-07-12 21:50:28 -06:00
428f220b88 Battle shellcheck 2018-07-12 21:50:28 -06:00
10add6a8ac Cleanup setup.sh 2018-07-12 21:50:28 -06:00
f06a8dceda Fix keygen docs
Thanks @rob-solana
2018-07-12 21:50:28 -06:00
545f4f1c87 Pass the owner's keypair to fullnode-config 2018-07-12 21:50:28 -06:00
77543d83ff Fix default keypair paths 2018-07-12 21:50:28 -06:00
eb6a30cb7c In Wallet, make --tokens required and --to optional 2018-07-12 21:50:28 -06:00
97372b8e63 Add --outfile option to solana-keygen 2018-07-12 21:50:28 -06:00
cea29ed772 More keygen 2018-07-12 21:50:28 -06:00
b5006b8f2b Migrate to solana-keygen
Most of #593
2018-07-12 21:50:28 -06:00
81c44c605b Add solana-keygen
Same as solana-mint, but without a tokens field.
2018-07-12 14:06:43 -06:00
0b66a6626a Use docker image's clippy 2018-07-12 09:40:40 -06:00
e8be4d7eae Add clippy to CI 2018-07-12 09:40:40 -06:00
30f0c25b65 Fix all remaining clippy warnings
Fixes #586
2018-07-12 09:40:40 -06:00
73ae3c3301 Apply most of clippy's feedback 2018-07-12 09:40:40 -06:00
f98e9aba48 Apply clippy feedback to CLI apps 2018-07-12 09:40:40 -06:00
84c28a077a Use custom rust nightly image with cargo-cov and clippy pre-installed 2018-07-12 07:25:56 -06:00
350cf62b90 Sequence client outgoing and incoming txs 2018-07-12 07:24:15 -06:00
aa4f30c491 Repay transactions from test accounts to client 2018-07-12 07:24:15 -06:00
3de979aa7c Check client balance and only airdrop if less than TPS quota 2018-07-12 07:24:15 -06:00
5bc133985b Start drone on remote leader node
- Also, enables CUDA for leader node
2018-07-11 20:08:18 -06:00
87156e1364 Fix flaky test
The test would fail any time the original value was coincidently
the same as the new bogus value.
2018-07-11 14:16:21 -07:00
45ff142871 Optimized start_nodes script to speed up GCE nodes start time
- Reduced dependency on local network
- Validators get binaries (solana bins and scripts) from leader node
2018-07-11 11:42:25 -06:00
2710ff271e cargo fmt 2018-07-11 11:38:41 -06:00
468ac9facd Refactor the "ReplicatedData" struct
Rename the "ReplicatedData" struct to the "NodeInfo" struct.
Also refactors and renames the members in this struct.
2018-07-11 11:38:41 -06:00
705720f086 fixups 2018-07-11 10:37:47 -07:00
a219e78f00 fixups 2018-07-11 10:37:47 -07:00
7a41868173 fixups 2018-07-11 10:37:47 -07:00
e16acec901 fixups 2018-07-11 10:37:47 -07:00
de44d7475e fixups 2018-07-11 10:37:47 -07:00
c2dd009e0b fixups 2018-07-11 10:37:47 -07:00
5a8da75d06 optimize process_ledger() 2018-07-11 10:37:47 -07:00
848c6e2371 Reduce sample size to restore number of transactions 2018-07-11 11:18:18 -06:00
e3882950cf Run benchmarks from Rust stable CI 2018-07-11 11:18:18 -06:00
28f6fbee23 Port all benchmarks to Criterion 2018-07-11 11:18:18 -06:00
3144a70b18 Move all benchmarks to benches/ 2018-07-11 11:18:18 -06:00
bed5438831 Improved streamer debug messages
distinguish between threads
2018-07-11 18:26:16 +02:00
6f991b3c11 Send keypair args for validators instead of leaders 2018-07-11 07:54:38 -06:00
03a8a5ed55 only submit to influx when we log
test accumilated value logging

lots of counters

higher influx rate

fix counter name

replicate-transactions
2018-07-11 07:53:39 -06:00
0c6d2ef1f4 Fix typo 2018-07-10 19:38:29 -06:00
d2be79f38c Use iter_with_setup() to improve precision 2018-07-10 19:38:29 -06:00
cc89801b12 Port bank benchmark to Criterion 2018-07-10 19:38:29 -06:00
dfa05a8742 Move bank benchmark outside src
This will make it available to third party benchmarking tools.
2018-07-10 19:38:29 -06:00
d7d985365b Add script to create/delete multiple GCE instances
- This script outputs the IP address array that can be used
  with start_nodes script to launch multinode demo
- Changes to start_nodes to compress files for rsync
2018-07-10 18:16:05 -06:00
0d4e4b18c2 Quiet counter (#574)
* only submit to influx when we log

* test accumulated value logging
2018-07-10 15:14:59 -07:00
7687436bef some cleanup on messages 2018-07-10 13:32:31 -06:00
d531b9645d review comments 2018-07-10 13:32:31 -06:00
6a1b5a222a rebase builds 2018-07-10 13:32:31 -06:00
be2bf69c93 initial vote stage
wip

voting

wip

move voting into the replicate stage

update

fixup!

fixup!

fixup!

fixup!

fixup!

fixup!

fixup!

fixup!

fixup!

fixup!

update

fixup!

fixup!

fixup!

tpu processing votes in entries before write stage

fixup!

fixup!

txs

make sure validators have an account

fixup!

fixup!

fixup!

exit fullnode correctly

exit on exit not err

try 50

add delay for voting

300

300

startup logs

par start

100

no rayon

retry longer

log leader drop

fix distance

50 nodes

100

handle deserialize error

update

fix broadcast

new table every time

tweaks

table

update

try shuffle table

skip kill

skip add

purge test

fixed tests

rebase 2

fixed tests

fixed rebase

cleanup

ok for blobs to be longer then window

fix init window

60 nodes
2018-07-10 13:32:31 -06:00
0672794692 Cleanup leader restarts
Try to avoid adding a runtime codepath when all paths are
statically known.
2018-07-10 11:11:36 -06:00
c65c0d9b23 Expose fewer exit variables 2018-07-10 11:11:36 -06:00
0ee86ff313 Map counters to metrics 2018-07-10 11:11:21 -06:00
3b1aa846b5 Fixed issues with configuring new GCE instances
- New nodes cloned from a working node can be used with the script
- Script takes care of installing SSH keys, and package dependencies correctly
2018-07-10 10:31:03 -06:00
0a34cb8023 Include hh:mm in image name 2018-07-09 23:07:07 -06:00
227aa38c8a Add image --family arg 2018-07-09 23:02:46 -06:00
1dd467ed7d fix issue #568 2018-07-09 22:27:11 -06:00
922dffb122 fix erasure 2018-07-09 20:40:14 -06:00
63985d4595 renamed to contact_info 2018-07-09 20:40:14 -06:00
97dd1834d7 fix tests, more logs 2018-07-09 20:40:14 -06:00
2ea030be48 stick all the addrs into one struct 2018-07-09 20:40:14 -06:00
606cfbfe1e Migrate fullnode and fullnode-config to clap for CLI arguments 2018-07-09 20:38:32 -06:00
90a4ab7e57 fixes issue #299 2018-07-09 14:50:14 -07:00
412e15fbdc add test for populated window 2018-07-09 14:50:14 -07:00
ed0a590549 support an initial window filled with last up-to-WINDOW_SIZE blobs 2018-07-09 14:50:14 -07:00
71f05cb23e Vet timestamp source from contract, not leader
Per @aeyakovenko, contracts shouldn't trust the network for
timestamps. Instead, pass the verified public key to the
contract and let it decide if that's a public key it wants
to trust the timestamp from.

Fixes #405
2018-07-09 08:40:07 -06:00
5f99657523 Remove last_time from bank
We had a test for this, but without `Bank::time_sources` (removed in the last
commit), there's no last_time that can be trusted.
2018-07-09 08:40:07 -06:00
587ae1bf3c Remove time_sources from bank
I wrote this, but per
https://github.com/solana-labs/solana#code-coverage, if it doesn't
break a test, it's fair game to delete.
2018-07-09 08:40:07 -06:00
461dea69d9 Add SOLANA_METRICS_CONFIG environment variable 2018-07-07 19:40:09 -07:00
22c0e3cd54 Metrics v0.1 2018-07-07 19:40:09 -07:00
3ed9567f96 Remove exit variable from RequestStage 2018-07-05 17:32:41 -06:00
c4fa841aa9 Remove exit variable from respond [stage]
And drop the sender that feeds input to the responder.
2018-07-05 17:32:41 -06:00
f284af1c3d Remove exit variable from WindowStage and retransmit [stage] 2018-07-05 17:32:41 -06:00
46602ba9c3 Remove exit variable from ReplicateStage 2018-07-05 17:32:41 -06:00
81477246be Remove exit variable from VerifyStage 2018-07-05 17:32:41 -06:00
9bd63867aa No longer need to ignore downstream send errors
By removing the exit variables, the downstream stages wait for
upstream stages to drop their senders before exiting.
2018-07-05 17:32:41 -06:00
d1c317fd5f Remove exit variable from broadcast [stage] 2018-07-05 17:32:41 -06:00
cbd664ba4b Remove exit variable from BankingStage 2018-07-05 17:32:41 -06:00
4bb7cefa15 Remove exit variable from WriteStage 2018-07-05 17:32:41 -06:00
82c86daa78 Exit write_stage on channel errors 2018-07-05 17:32:41 -06:00
b95db62be3 Handle errors consistently
Error handling is still clumsy. We should switch to something like
`error-chain` or `Result<T, Box<Error>>`, but until then, we can
at least be consistent across modules.
2018-07-05 17:32:41 -06:00
0f7fdd71cc Remove executable bit from nat.rs 2018-07-05 17:32:41 -06:00
af1a7da0d5 Fix code comments 2018-07-05 17:32:41 -06:00
d698b3da3a Revert tps_demo marker 2018-07-05 15:15:23 -06:00
6d275d571c Clean up commented code 2018-07-05 15:15:23 -06:00
63acb82c87 Update drone airdrop test for tps_demo functionality 2018-07-05 15:15:23 -06:00
4d05b74314 Port solana-client-demo to clap crate for CLI arguments 2018-07-05 15:15:23 -06:00
37dd511356 Pass client.json location as argument 2018-07-05 15:15:23 -06:00
96c321da76 Update drone to allow TPS-sized airdrops 2018-07-05 15:15:23 -06:00
4701540cc9 Migrate solana-client-demo to use drone 2018-07-05 15:15:23 -06:00
f54615b4e3 UDP port for client demo in range
* This change will allow clients to run behind a firewall
  with only certain port range opened for access
2018-07-05 10:17:35 -06:00
9c456b2fb0 Fixup the integration tests 2018-07-04 16:40:34 -06:00
77bf17064a Add Service trait
Added a consistent interface to all the microservices.
2018-07-04 16:40:34 -06:00
44150b2e85 Remove unused crate from wallet CLI 2018-07-04 16:39:26 -06:00
8ec2fe15f3 Port solana-drone to clap crate for CLI arguments 2018-07-04 16:39:26 -06:00
687af3e3a4 Document source of magic net.core.rmem_max value 2018-07-04 14:24:01 -07:00
72ab83cd45 Collect timing metrics for CI jobs 2018-07-04 11:00:56 -07:00
4b07772e22 Add helper functions for reading entries
```rust
let entries = entry_writer::read_entries_from_str(entries_str).unwrap();
let entries_len = entries.len();
assert_eq!(entries_len, 7);
let bank = Bank::default();
bank.process_ledger(entries).unwrap();
assert_eq!(bank.transaction_count(), entries_len - 2);
```
2018-07-03 19:32:01 -06:00
22d2c962b2 ignore 2018-07-03 18:10:16 -06:00
e771d36278 Better logs 2018-07-03 18:10:16 -06:00
800c2dd370 make the leader append to the ledger file 2018-07-03 17:17:52 -06:00
f38842822f Cleanup code duplication 2018-07-03 16:33:36 -06:00
88a6fb86bf Clean up read_entries() and its usage 2018-07-03 16:33:36 -06:00
f6fe998ed4 Revert 1dd8c5ed36
Per @sakridge, this might cause a performance degradation. Need
to benchmark it.
2018-07-03 14:00:53 -06:00
16337d7c1e unstable test 2018-07-03 14:00:39 -06:00
ae309f80f7 boot from file test 2018-07-03 14:00:39 -06:00
fa70b3bf70 split out files, fixed a bug @garious! 2018-07-03 14:00:39 -06:00
3a90f138b2 dynamit network test
* cleaned up fullnode api
* added debug_id to ReplicatedData and crdt for debugging
2018-07-03 14:00:39 -06:00
033f6dcbcb Demote 'sorted leader' log 2018-07-03 08:24:28 -07:00
5d8b2f899a Fix wallet doc 2018-07-02 19:21:03 -07:00
490205ab84 Fix sanity check
...that my last PR broke
2018-07-02 19:21:03 -07:00
2c0e704c82 Confirm the payment 2018-07-02 17:59:50 -07:00
253048f72d Only tune networking for leader/validator 2018-07-02 17:59:50 -07:00
e09b8430ce Add |wallet reset| command 2018-07-02 17:59:50 -07:00
9ae283dc3a Expose wallet.sh as a Snap program temporarily 2018-07-02 17:59:50 -07:00
f95a79d145 Default to using testnet.s.c when running as a Snap 2018-07-02 17:59:50 -07:00
0dabdfd48e Use zero to represent a nonexistent account
This also fixes a bug in the thin client where a nonexistent account
would have triggered a panic because we were using `balances[k]` instead
of `balances.get(key)`.

Fixes #534
2018-07-02 18:48:40 -06:00
d2bb4dc14a Purge empty accounts 2018-07-02 18:48:40 -06:00
b4dc180592 More quotes to pacify shellcheck 2018-07-02 16:41:22 -07:00
263577773f Set client config directory correctly in a Snap 2018-07-02 16:41:22 -07:00
7d708be121 Drone now grabs mint.json locally 2018-07-02 16:41:22 -07:00
feb1669d39 Correct locate rsync when running as a Snap 2018-07-02 15:57:30 -07:00
2cbfe41422 Abort nicer on drone connection failure 2018-07-02 15:57:30 -07:00
b7653865b1 Support testnet.solana.com as first argument 2018-07-02 15:57:30 -07:00
c72dced8fa Report error when an invalid confirmation signature or public key is provided 2018-07-02 15:57:30 -07:00
6feed5fd56 rebased 2018-07-02 16:34:49 -06:00
b8fe5ae076 rename server to fullnode 2018-07-02 16:34:49 -06:00
7e657d65f3 merged f2ab08c65e 2018-07-02 16:34:49 -06:00
a166bb816e wtfr 2018-07-02 16:34:49 -06:00
2952027d04 wtfr 2018-07-02 16:34:49 -06:00
430d9d9314 fixup! 2018-07-02 16:34:49 -06:00
fa247196c0 fullnode lib 2018-07-02 16:34:49 -06:00
5d17c2b58f Return output receivers from each stage
Reaching into the stages' structs for their receivers is, in hindsight,
more awkward than returning multiple values from constructors. By
returning the receiver, the caller can name the receiver whatever it
wants (as you would with any return value), and doesn't need to
reach into the struct for the field (which is super awkward in
combination with move semantics).
2018-07-02 16:18:32 -06:00
6ee45d282e some auto-detect of wallet commands 2018-07-02 15:51:12 -06:00
cfc3bd0696 Add manual wallet sanity test 2018-07-02 14:38:01 -07:00
3e0e09555a Undo UPnP UDP port binding 2018-07-02 14:38:01 -07:00
1d8bb5144e Drop -demo suffix 2018-07-02 14:38:01 -07:00
67e0100866 Bind to 0.0.0.0 2018-07-02 14:38:01 -07:00
f2ab08c65e Reuse request UDP port for responses 2018-07-02 14:38:01 -07:00
04a93050e7 No need to share a write lock across single-threaded methods 2018-07-02 15:25:16 -06:00
03401041db Correct signature argument name 2018-07-02 11:24:13 -07:00
6eac744a05 Only rsync leader.json once 2018-07-02 10:59:09 -07:00
ae29e2085f Init env_logger 2018-07-02 10:59:09 -07:00
7ce0b58af8 Document pkg-config dependency 2018-07-02 10:42:56 -07:00
ea5663c0da Demote log 2018-07-02 10:28:43 -07:00
a61bfae8a4 Document libssl-dev dependency 2018-07-02 10:28:43 -07:00
5716898216 setup.sh can now be more picky about the kind of config it creates 2018-07-02 09:22:26 -07:00
c0f9e452f2 mint.json is now private 2018-07-02 09:22:26 -07:00
4e3526394e Use IntoInterator to simplify write_entries() usage 2018-07-02 09:51:39 -06:00
6806a14a3f Use Cursor instead of tempfile.
Faster and one less dependency.
2018-07-02 09:51:39 -06:00
ec7e50b37d Consolidate ledger serialization code
The new read_entries() works, but is overly-contrained. Not
using that function yet, but adding it here in the hopes some
Rust guru will tell us how to get that lifetime constraint out
of there.

Fixes #517
2018-07-02 09:51:39 -06:00
e7b7dfebf5 Add tests for process_ledger() 2018-07-02 09:51:39 -06:00
a9e0b27772 Speed up snap build
1. Use pre-installed host rust toolchain
2. Build reference/performance fullnode in same part to avoid rebuilding libraries
3. Merge scripts into same part
2018-07-01 17:47:51 -07:00
669164bada Boot EntryWriter's Mutex
Finally!
2018-07-01 17:29:24 -06:00
4f3a291391 Move the writer into EntryWriter 2018-07-01 17:29:24 -06:00
56e37ad2f4 Limit sticky mutex to WriteStage 2018-07-01 17:29:24 -06:00
17de79a83a Remove dead code 2018-07-01 17:29:24 -06:00
09e9139855 Move channel code to write stage 2018-07-01 17:29:24 -06:00
76fc5822c9 Send Vec<Entry> between stages instead of Entry
Might see a performance boost here.
2018-07-01 17:29:24 -06:00
c767a854ed Remove useless Arc 2018-07-01 11:35:32 -07:00
b60802ddff Refactor such that genesis can use entry_writer 2018-07-01 11:35:32 -07:00
1c35d59f26 Receive entries first, then write 2018-07-01 11:35:32 -07:00
adcaf715c6 Cleanup write_entries 2018-07-01 11:35:32 -07:00
1f9494221b Make space for a write_entry() that only writes entries 2018-07-01 11:35:32 -07:00
466d6f76b9 Don't hide error in write_entry() 2018-07-01 11:35:32 -07:00
b05e6ce3db Cleanup solana-genesis 2018-07-01 11:35:32 -07:00
1d812e78d5 Use hard linking to speed up target cache save/restore 2018-07-01 08:59:42 -07:00
fba494343f Save/restore target/ directory between builds 2018-06-30 22:30:57 -07:00
0b878eccf8 Map HOME to grant docker containers access to the ~/.cargo registry cache 2018-06-30 21:50:15 -07:00
98772b16d6 Generalize solana-snap build trigger 2018-06-30 21:50:15 -07:00
bb82ff0c80 Don't wanna wait 2018-06-30 20:05:27 -07:00
71af03dc98 Skip snap build for PRs if nothing under snap/ is modified
Additionally relegate push snap build to a secondary CI pipeline
2018-06-30 20:05:27 -07:00
5671da4a0a Generate a client-specific mint.json 2018-06-30 15:28:17 -07:00
d63493a852 Grant the snap build more time 2018-06-30 12:20:22 -07:00
c06582ba40 Wallet no longer uses global mint.json 2018-06-29 22:26:42 -07:00
450f271cf7 Move public IP address detection out of bash 2018-06-29 21:12:05 -07:00
a31889f129 Readme version bump 2018-06-29 21:39:41 -06:00
ba6a6f5227 Use clap crate for wallet CLI subcommands and arguments 2018-06-29 21:30:20 -06:00
9a38d61048 Version bump 2018-06-29 21:23:50 -06:00
903ec27754 Add BROKEN_NAT env variable to select Udp sender port workaround 2018-06-29 20:02:28 -07:00
0b56d603c2 Client NAT traversal 0.1
UPnP is now used to request a port on the NAT be forwarded to the local machine.
This obviously only works for NATs that support UPnP, and thus is not a panacea
for all NAT-related connectivity issues.

Notable hacks in this patch include a transmit/receive UDP socket pair to work
around current protocol limitations whereby the full node assumes its peer can
receive on the same UDP port it transmitted from.
2018-06-29 17:36:26 -07:00
4ffb5d157a Disable coverage until issue #433 is resolved 2018-06-29 17:36:26 -07:00
816246ebee Add doc 2018-06-29 17:28:12 -06:00
a9881aee05 Add base58-encoded addresses 2018-06-29 17:28:12 -06:00
7b5b989cfe Print usage is a command is not provided 2018-06-29 17:28:12 -06:00
c4b62e19f2 Do Proof of History verification before appending entries to the bank
Note: replicate_stage is still using `process_entries()` because
changing it to `process_blocks()` causes the `test_replicate` test to
fail.
2018-06-29 15:35:39 -06:00
79a97ada04 Fix more shellchecks
Also, stops current nodes before pushing updates
2018-06-29 15:19:28 -06:00
da215d1a21 Fix failed shellchecks 2018-06-29 15:19:28 -06:00
9ffc50bead Address review comments 2018-06-29 15:19:28 -06:00
f8352bac2f Address review comments
* Only public IP address in the list
* formatting and other comments
2018-06-29 15:19:28 -06:00
27c1410fdc Script to deploy multiple nodes (one as leader, others as validators)
* The built code is loaded to the nodes
* ssh_keys can be copied to the nodes for internode comm
* The nodes are started with their respective roles
* The client demo is started on the last node
2018-06-29 15:19:28 -06:00
9a4733bde7 Remove interactive behavior from wallet 2018-06-29 13:22:20 -06:00
f3df5df52c add validator catchup to multi-node test 2018-06-29 10:39:41 -07:00
517d08c637 Cleanup 2018-06-29 09:51:13 -07:00
90dd794ae5 cargo fmt
rustfmt 0.6.1-stable (49279d71 2018-05-08)
2018-06-29 09:51:13 -07:00
e0dbbba8a3 fmt 2018-06-29 09:51:13 -07:00
705df55a7f Fix program name 2018-06-29 09:51:13 -07:00
d354e85a9a Return bool on signature check 2018-06-29 09:51:13 -07:00
e4e1f8ec1e Missing -m parameter handling 2018-06-29 09:51:13 -07:00
0112a24179 Add confirm command to wallet, and update RPU to check bank for a signature 2018-06-29 09:51:13 -07:00
d680f6b3a5 Fix bash scripts:
* Use wallet name everywhere
* Update drone to use mint.json
2018-06-29 09:51:13 -07:00
47e732717f more notes 2018-06-29 10:18:36 -06:00
ec56abfccb Correct setup.sh args 2018-06-29 07:59:16 -07:00
e7cdb402fb highlight 2018-06-29 07:16:03 -06:00
a3fe1965fb spelling 2018-06-29 07:16:03 -06:00
5256e6833e update 2018-06-29 07:16:03 -06:00
051cd2e1ff more examples 2018-06-29 07:16:03 -06:00
51929e7df8 rfcs 2018-06-29 07:16:03 -06:00
a094507bb8 Lower default benchmarking numbers to make CI timeout 2018-06-29 07:14:47 -06:00
8effa4e3e0 Clear old blobs before putting in the new one
Otherwise we will just warn about overrun and not insert new blob
Also, break if the index we find is less than consumed otherwise
we can infinite loop
2018-06-29 07:14:47 -06:00
1c9e7dbc45 Don't recycle in the replicate stage
Windowing stage owns all the blobs now
2018-06-29 07:14:47 -06:00
799b249f02 Don't null blob window until we have to 2018-06-29 07:14:47 -06:00
7b4a378c92 Add public-ip option to snap validator with cuda 2018-06-28 21:14:29 -06:00
47917d00d1 Always bind to 0.0.0.0 regardless of what's being advertised to other nodes 2018-06-28 19:13:36 -07:00
a4c49af859 Add public-ip argument to setup.sh 2018-06-28 19:13:36 -07:00
1c1d7d1e0e Log get_last_id errors 2018-06-28 19:13:36 -07:00
d28536d76e Fix spelling of signature 2018-06-28 16:31:33 -07:00
63cfbb9497 Only register last entry after a split 2018-06-28 16:54:06 -06:00
231040b93e Add tests 2018-06-28 12:28:43 -07:00
7c74afc35a Relax recycler
Instead of asserting ref count is 1 before recycling, allow users
to recycle items early. If it turns out that was too early, and
allocate() wants to return it, then boot it and take a memory
allocation performance hit instead.
2018-06-28 12:28:43 -07:00
7878a011eb Use a Mint to configure the wallet
* Send transactions from the mint's private key
* By default, send full balance to oneself
* By default, request the mint's number of tokens for airdrops
2018-06-27 17:35:50 -06:00
c05416e27d Turn simple-client-demo into a simpler wallet 2018-06-27 17:35:50 -06:00
ee200d8fa0 Add DEBUG= flag to select debug binaries 2018-06-27 15:34:31 -07:00
2f42658cd4 ... 2018-06-27 14:51:18 -07:00
d95e8030fc ... 2018-06-27 14:51:18 -07:00
4aedd3f1b6 Cleanup type aliases and imports 2018-06-27 15:06:18 -06:00
bb89d6f54d Get back to 500k transactions 2018-06-27 13:50:27 -07:00
ed10841e3d No longer spin up accounts for client-demo
Now that the Bank is single-threaded again, we can spin up new
accounts on the fly without concern of thread contention. Likewise,
we can send all transactions from a single account, which was also
problematic in the multi-threaded bank. Sending from one account will
also make client-demo straightforward to port to solana-drone.
2018-06-27 13:50:27 -07:00
6dac87f2a7 Add entry to snapscraft yaml; cleanup bash header 2018-06-27 13:01:29 -06:00
a167d0d331 CI cleanup 2018-06-27 13:01:29 -06:00
eed37820b5 Comments 2018-06-27 13:01:29 -06:00
124e1fa350 Bash scripts to go with simple-client-demo 2018-06-27 13:01:29 -06:00
ac40434cdf Initial simple client demo commit 2018-06-27 13:01:29 -06:00
39354c06f8 take multiple log files, allow restart of leader, validator 2018-06-27 11:41:25 -07:00
faedb88de0 s/local/declare/g 2018-06-26 19:11:31 -07:00
5cd1fb486f Automatically add rsync:// prefix to URLs that need it 2018-06-26 17:45:53 -07:00
5b5df49e6c make client.sh behave like the others, i.e. no tee to a log 2018-06-26 17:02:24 -07:00
86f9277e2d Add USE_SNAP flag 2018-06-26 16:32:55 -07:00
56b09bf0ac cargo fmt 2018-06-26 16:51:07 -06:00
f4c4b9df9c Only free in replicate if we did not hold the reference in window stage
And then free when we are consuming blobs
2018-06-26 16:51:07 -06:00
6e568c69a7 Preemptive strike
Should that blob have been passed to a recycler, it would have
had too high a reference count.
2018-06-26 16:51:07 -06:00
14d624ee40 Fix benchmarks too
This change will make these benchmarks way slower, because its now
cloning the transaction vector each iteration instead of the ref
counts. We need to rethink these.
2018-06-26 16:51:07 -06:00
d5c0557891 Fix test_replicate too 2018-06-26 16:51:07 -06:00
1691060a22 Assert recycler is given last reference to data
This patch likely fixes the sporadic failures in the following tests:

```
test server::tests::validator_exit ... FAILED
test streamer::test::streamer_send_test ... FAILED
test thin_client::tests::test_bad_sig ... FAILED
test drone::tests::test_send_airdrop ... FAILED
test thin_client::tests::test_thin_client ... FAILED
```
2018-06-26 16:51:07 -06:00
a5ce578c72 ... 2018-06-26 16:23:41 -06:00
05edfad13a Fix compiler warnings 2018-06-26 15:03:15 -07:00
136b43f461 Fix whitespace
TODO: Why didn't "cargo fmt" fail the build.
2018-06-26 15:03:15 -07:00
ac40c1818f .. 2018-06-26 13:57:10 -07:00
eb63dbcd2a an Entry needs to be multiple of 4 bytes long 2018-06-26 13:57:10 -07:00
4e2f1a519e whack next_entries_batched 2018-06-26 13:57:10 -07:00
55ec7f9fe9 add entry.has_more
* quick fix for really big genesis
 * longer term fix for possible parallel verification over multiple
      Blobs/Entries
2018-06-26 13:57:10 -07:00
b7ddefdbf9 Empty plug array is not accepted by the snap store 2018-06-26 12:49:40 -07:00
ce361c2cdc Add Snap fullnode daemon 2018-06-26 12:32:33 -07:00
ed6ba55261 Add snap/ README 2018-06-26 12:32:33 -07:00
ec333d2bd6 Revert "-v was renamed to -t"
This reverts commit 8f4ce1e8d0.
2018-06-26 12:32:33 -07:00
551f639259 Some pull request fixes(linting + documentation) 2018-06-26 12:31:04 -06:00
da3bb6fb93 ran linter 2018-06-26 12:31:04 -06:00
08bcb62016 added remote table to update respones 2018-06-26 12:31:04 -06:00
8f4ce1e8d0 -v was renamed to -t 2018-06-25 20:48:26 -07:00
4a534d6abb Don't clone() Arc before recycling
This might fix an awful bug where the streamer reuses a Blob
before the current user is done with it. Recycler should probably
assert ref count is one?

* Also don't collect() an iterator before iterating over it.
2018-06-25 17:33:07 -06:00
b48a8c0555 Chunk blobs into window size to avoid window overrun
Fixes #447
2018-06-25 17:33:07 -06:00
1919ec247b add a clock to validator windows (part 3 of #309) (#448)
* count entries processed by Bank
 * initialize windows with initial height of Entries
2018-06-25 15:07:48 -07:00
3966eb5374 support MacOS bash and ifconfig properly 2018-06-25 13:14:36 -06:00
c22ef50cae Client fixes, poll for unique last id and cache clients
So we don't keep running up the port range
2018-06-25 10:02:29 -06:00
be5f2ef9b9 Consolidate CI jobs 2018-06-24 22:28:24 -07:00
adfcb79387 Force install cargo-cov 2018-06-24 15:34:30 -06:00
73c4c0ac5f Revert "cargo-cov installed by default in nightly?"
This reverts commit 6fc601f696.
2018-06-24 15:34:30 -06:00
6fc601f696 cargo-cov installed by default in nightly? 2018-06-24 12:17:42 -06:00
07111fb7bb Use llvm-cov instead of gcov
@marco-c called this a hack, but since grcov isn't working
out-of-the-box (panics on call to gcov), we'll take a stab at using
llvm-cov.
2018-06-24 12:17:42 -06:00
a06d2170b0 No need for rustfmt on nightly 2018-06-24 12:17:42 -06:00
7f53ea3bf3 Generate coverage with Rust nightly
Fixes #177

Thanks @marco-c!
2018-06-24 12:17:42 -06:00
b2accd1c2a Run snap build sooner to better mask the delay 2018-06-24 10:24:32 -07:00
8ef8a8dea7 borrow checker 2018-06-24 11:17:55 -06:00
e929404676 comments 2018-06-24 11:17:55 -06:00
c2258bedae fixed! 2018-06-24 11:17:55 -06:00
215fdbb7ed nits 2018-06-24 11:17:55 -06:00
ee998f6882 fix docs 2018-06-24 11:17:55 -06:00
826e95afca fix logs 2018-06-24 11:17:55 -06:00
47583d48e7 get rid of dummy test 2018-06-24 11:17:55 -06:00
e759cdf061 tests 2018-06-24 11:17:55 -06:00
88503c2a09 generic array fail case 2018-06-24 11:17:55 -06:00
d5be23dffe fmt 2018-06-24 10:44:17 -06:00
80c01dc085 Use leader.json or ReplicatedData to get ports for drone 2018-06-24 10:44:17 -06:00
45b2549fa9 Reset bad TestNode edit 2018-06-24 10:44:17 -06:00
c7ce454188 Use pnet_datalink instead of all of pnet
pnet_transport takes a long time to build. It's been especially
painful from within a docker container for reasons I don't care
to understand. pnet_datalink is the only part of pnet we're using
so booting the rest.
2018-06-24 10:39:59 -06:00
7059ea42d6 comments 2018-06-24 09:19:05 -06:00
8ea1c29c9b more notes 2018-06-24 09:19:05 -06:00
33bbfdbc9b Retry flaky coverage/cuda builds on initial failure 2018-06-23 16:17:25 -07:00
5de54f8853 Make cuda/erasure build logs public 2018-06-23 16:17:25 -07:00
a1ac41218a Document CUDA version 2018-06-23 16:17:25 -07:00
55fc647568 fix more shellcheck 2018-06-23 16:00:17 -07:00
e83e898eed fix shellcheck's concerns 2018-06-23 16:00:17 -07:00
eb07e4588b remove IPADDR, which was making Rob feel ill
IPADDR is simple, but not exactly what we need for testnet, where NAT'd
  folks need to join in, need to advertize themselves as on the interweb.

  myip() helps, but there's some TODOs: fullnode-config probably needs to
  be told where it lives in the real world (machine interfaces tell us dick),
  or incorporate something like the "ifconfig.co" code in myip.sh
2018-06-23 16:00:17 -07:00
563f834c96 Document how to update the snap 2018-06-23 15:29:22 -07:00
183178681d Simply fetching perf libs 2018-06-23 12:54:38 -07:00
8dba53e494 debit undo (#423) 2018-06-23 06:14:52 -07:00
e4782b19a3 Document GCP setup 2018-06-23 02:12:20 -07:00
ec86b1dffa Adapt to GCP-based CI 2018-06-23 02:12:20 -07:00
6cb8266c7b cleanup (#419) 2018-06-22 23:26:42 -07:00
9c50302a39 Update rfc-001-smart-contracts-engine.md 2018-06-22 22:45:22 -07:00
3313c69898 remove ccal 2018-06-22 22:43:54 -07:00
530c6ca7ec a bunch of updates 2018-06-22 22:43:54 -07:00
07ed2fb523 cleanup 2018-06-22 22:43:54 -07:00
d9ec380a15 cleanup 2018-06-22 22:43:54 -07:00
b60eb3a899 edits 2018-06-22 22:43:54 -07:00
b4df69791b cleanup 2018-06-22 22:43:54 -07:00
c21b8a22b9 update 2018-06-22 22:43:54 -07:00
475a76e656 wip 2018-06-22 22:43:54 -07:00
7ba5d5ef86 first! 2018-06-22 22:43:54 -07:00
737dc1ddde Per rustc 1.27.0, we can ensure nested results are used 2018-06-22 22:42:47 -07:00
164bf19b36 Update LICENSE 2018-06-22 22:41:04 -07:00
25976771d9 Version bump 2018-06-22 22:38:18 -07:00
f2198c2e9a cargo fmt
rustc 1.27.0

```
$ cargo fmt --version
rustfmt 0.6.1-stable (49279d71 2018-05-08)
```
2018-06-22 22:23:55 -07:00
eec19c6d2c move genesis to new Entry generation 2018-06-22 17:46:45 -07:00
30e03feb5f Add initial CI subsystem documentation 2018-06-22 15:30:29 -07:00
58cd3bde9f Add drone to snap package 2018-06-22 15:27:25 -07:00
662bfb7b88 fmt 2018-06-22 14:52:36 -07:00
5f3e3a17d3 Fix test_send_airdrop cap; add helpful panic msgs 2018-06-22 14:52:36 -07:00
feba2d9975 Set request cap to a reasonable number, based on 30min reset noted in issue #341 2018-06-22 14:52:36 -07:00
e3e3a1c457 Better drone request cap handling 2018-06-22 14:52:36 -07:00
90628f3c8d Edit TestNode port logic to be consistent with new_leader (fixes hanging test_send_airdrop) 2018-06-22 14:52:36 -07:00
f6bcadb79d Make airdrop amount variable 2018-06-22 14:52:36 -07:00
d4ac16773c fmt 2018-06-22 14:52:36 -07:00
96f044d2bf Clean up; add new_from_server_addr routine 2018-06-22 14:52:36 -07:00
f31868b913 Rename drone bin; fix usage statement 2018-06-22 14:52:36 -07:00
73b0ff5b55 Add request-count check and tests; fmt 2018-06-22 14:52:36 -07:00
64cf69045a Add request-count check; Clean up solana-drone and fmt 2018-06-22 14:52:36 -07:00
e57dae0f31 Update config and dependencies for solana-drone 2018-06-22 14:52:36 -07:00
6386e7d5cf Leave some tokens in the mint for solana-drone 2018-06-22 14:52:36 -07:00
4bad103da9 Add solana-drone CLI 2018-06-22 14:52:36 -07:00
30a26adb7c Add solana-drone module to library 2018-06-22 14:52:36 -07:00
8be4adfc0a Rename tr => tx and add back comments 2018-06-22 14:34:46 -07:00
fed4cc3965 Remove commented code/imports 2018-06-22 14:34:46 -07:00
7d1e074683 bump last_ids 2018-06-22 14:34:46 -07:00
00516e50a1 last_ids opt 2018-06-22 14:34:46 -07:00
e83d76fbd9 Remove mutexes 2018-06-22 14:34:46 -07:00
304f152315 rwlock balances table 2018-06-22 14:34:46 -07:00
3a82ebf7fd Add multiple source accounts for benchmark 2018-06-22 14:34:46 -07:00
0253d34467 Address review comments 2018-06-22 14:18:45 -07:00
9209f9acde Run multiple instances from same workspace
* Support running leader and validators from multiple machines
  using the same NFS mounted workspace.
* Changes to setup, leader and validator scripts
2018-06-22 14:18:45 -07:00
3dbbb398df use next_entries() in recorder, recycle blobs in reconstruct_from_blobs 2018-06-22 14:17:36 -07:00
17e8ad110f Temporarily disable failing CI to get back to green 2018-06-22 11:29:31 -07:00
5e91d31ed3 issue 309 part 1
* limit the number of Tntries per Blob to at most one
* limit the number of Transactions per Entry such that an Entry will
    always fit in a Blob

With a one-to-one map of Entries to Blobs, recovery of a validator
  is a simple fast-forward from the end of the initial genesis.log
  and tx-*.logs Entries.

TODO: initialize validators' blob index with initial # of Entries.
2018-06-22 09:58:51 -07:00
fad9d20820 Add assertion for now next_entry must be called 2018-06-21 21:24:32 -07:00
fe9a1c8580 Fix comment 2018-06-21 21:24:32 -07:00
cd6d7d5198 Remove redundant clones 2018-06-21 21:24:32 -07:00
771478bc68 Add simple CUDA version check, warn on mismatch 2018-06-21 13:42:06 -07:00
c4a59896f8 Run test-erasure in a container 2018-06-21 13:00:40 -07:00
3eb1608403 Skip --user if SOLANA_DOCKER_RUN_NOSETUID is set 2018-06-21 12:24:52 -07:00
8fde70d4dc Erasure tests do not require a CUDA agent 2018-06-21 11:42:37 -07:00
5a047833ed Run snap publishing directly on CUDA agent
This is necessary until we build a docker image that also contains a CUDA
installation
2018-06-21 11:42:37 -07:00
f6c28e6be1 Update snapcraft docker image contain snapcraft 2.42.1 2018-06-21 11:42:37 -07:00
0ebf10d19d Snap cuda fullnode 2018-06-21 11:42:37 -07:00
d3005d3ef3 Updated setup and leader scripts
* Setup will us -b to set validator ports
* Leader script fixed to append .log to the log file
* Updated readme file
2018-06-20 19:05:38 -07:00
effcef2184 fixed sleep bug 2018-06-20 16:58:10 -07:00
89fc0ad7a9 Add convenience script to download performance libraries 2018-06-20 16:48:32 -07:00
410272ee1d Update generic_array
Warning: this may have performance implications.
2018-06-20 11:41:54 -07:00
1c97bf50b6 Fix nightly
No longer ignore failures in the nightly build.
2018-06-19 17:38:04 -07:00
4ecd2c9d0b update demo scripts
* add setup to combine init steps, configurable initial mint
  * bash -e -> bash and be explicit about errors with || exit $?
  * feed transaction logs to validator, too
2018-06-19 17:04:44 -07:00
e592243a09 De-double quote 2018-06-19 13:20:47 -07:00
2f4a92e352 Cleanup test 2018-06-19 12:36:02 -07:00
ceafc29040 fix linting errors, add retransmission fix to repair requests 2018-06-19 12:36:02 -07:00
b20efabfd2 added retransmission of repair messages 2018-06-19 12:36:02 -07:00
85b6e7293c Add cleanup script to manage build agent disk space 2018-06-19 12:22:45 -07:00
6aced927ad improve ledger initialization for fullnode
* use a line iterator on stdin instead of a line iterator on a buffer
 * move some unwrap() to expect(), documenting failures
 * bind entry type earlier (for kicks)
2018-06-19 09:28:35 -07:00
75997e6c08 Allow BUILDKITE_BRANCH in containers 2018-06-18 22:51:30 -07:00
9040d00110 Package solana as a snap 2018-06-18 17:36:03 -07:00
8ebc5c6b07 Suggest different validator port by default to coexist with leader port on the same machine 2018-06-18 17:36:03 -07:00
d4807790ff Add snapcraft login credentials
This file was created as follows:
$ snapcraft export-login --snaps solana --channels beta,edge snapcraft.credentials
$ openssl aes-256-cbc -e -in snapcraft.credentials -out snapcraft.credentials.enc
2018-06-18 17:36:03 -07:00
0de5e7a285 attempt to understand entry 2018-06-18 16:48:59 -07:00
c40000aeda Fix compiler warning 2018-06-18 15:49:41 -07:00
31198bc105 Fix cargo bench nightly 2018-06-18 13:20:39 -07:00
92599acfca Abort when -l is not present or unreadable 2018-06-16 09:55:03 -07:00
f6e70779fe Don't panic if sent a bad packet 2018-06-16 09:51:45 -06:00
3017bde686 Update README.md 2018-06-16 09:43:23 -06:00
9d84ec4bb3 Delete TODO
That comment predates the separation of RPU and TPU.
2018-06-16 08:59:30 -06:00
586141adb2 Cleanup TVU docs 2018-06-15 22:45:35 -06:00
3f763f99e2 Fail fast in CI when |cargo fmt| says no 2018-06-15 17:10:00 -07:00
15c7f36ea3 Improve error reporting 2018-06-15 17:10:00 -07:00
04d1a083fa Skip |sudo sysctl ...| on macOS 2018-06-15 17:10:00 -07:00
327ee1dae8 Apply feedback from @aeyakovenko 2018-06-15 17:01:38 -06:00
22885c3e64 Add TVU ASCII art 2018-06-15 17:01:38 -06:00
94ededb54c Add comments and limit digits for tps prints 2018-06-15 11:54:01 -06:00
af6a07697a Change client-demo to run continuosly for some amount of time
Also retry for get_last_id/transaction_count if dropped.
2018-06-15 11:54:01 -06:00
5f1d8c95eb Fix blob data size 2018-06-15 11:54:01 -06:00
7d9e032407 make sure we test large tables 2018-06-15 06:56:35 -06:00
bc918a5ad5 purger 2018-06-15 06:56:35 -06:00
ee54ce4727 min table size before purge 2018-06-15 06:56:35 -06:00
e85bf2f2d5 tests pass 2018-06-15 06:56:35 -06:00
a7460ffbd1 purge validators we havent seen for a long time 2018-06-15 06:56:35 -06:00
7fe1fd2f95 clean up fullnode cmdline
* fix documentation, other opt parameters
 * add support for a named output file, remove hardcoded "leader.log"
 * resurrect stdout as the default output
2018-06-15 00:41:07 -07:00
d30670e92e clean up demo bash scripts
* allow other level of RUST logging
 * avoid "echo" in favor of printf (builtin)
 * single quotes for literals, double quotes for variables
2018-06-14 23:12:11 -06:00
9b202c6e1e No longer flood log with emtpy entries 2018-06-14 18:04:36 -06:00
87946eafd5 Lower processing transaction message to debug by default 2018-06-14 17:08:11 -06:00
7575d3c726 Add timestamp to log messages
Upgraded env_logger and now we have timestamps and colorful messages.

Fixes #318
2018-06-14 17:07:58 -06:00
8b9713a934 Skip link_local v4 addresses and v6 address when v6 is not enabled 2018-06-14 16:10:31 -06:00
ec713c18c4 Revert client.sh script to use cargo 2018-06-14 11:56:36 -06:00
c24b0a1a3f TVU rework (#352)
Refactored TVU, into stages
* blob fetch stage for blobs
* window stage for maintaining the blob window
* pulled out NCP out of the TVU so they can be separate units
TVU is now just the fetch -> window -> request and bank processing
2018-06-13 21:52:23 -07:00
34e0cb0092 cargo fmt 2018-06-13 19:17:21 -07:00
7b7c7cba21 changed atty library 2018-06-13 19:17:21 -07:00
c45343dd30 comments 2018-06-13 16:11:44 -06:00
b7f6603c1f fix coverage build 2018-06-13 16:11:44 -06:00
2d3b052dea allow for insertion of dummy entry points into the local table (#346)
* Needed for #341. Create a dummy entry with public key 0..., but with a valid gossip address that we can ask for updates. This will allow validators to discover the full network by just knowing a single node's gossip address without knowing anything else about their identity.
* once we start removing dead validators this entry should get purged since we will never see a message from public key 0, #344
2018-06-13 11:42:30 -07:00
dcb6234771 Fix relative link to client demo 2018-06-12 23:29:04 -06:00
e44d423e83 Make version syntax consistent
Using no symbol implies its a symver caret requirement.

https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html
2018-06-11 15:51:25 -06:00
5435bb734c Upgrade rand 2018-06-11 15:51:25 -06:00
13f59adf61 Update build status badge link to publicly available build log 2018-06-10 22:12:09 -07:00
0fce3368d3 Fix json perf counter print and add script to generate a chart 2018-06-09 10:55:22 -07:00
1ee5c81267 Fix benchmarking banking_stage 2018-06-08 15:50:36 -07:00
3bb9d5eb50 Use timing::timestamp for counters 2018-06-08 15:50:36 -07:00
efb23f7cf9 Ensure stuck builds eventually timeout 2018-06-07 19:08:03 -07:00
013f4674de Target cuda agents 2018-06-07 19:08:03 -07:00
6966b25d9c Don't mark a build as failed if line coverage drops
It's not always a problem if line coverage drops. For example,
coverage will drop if you make well-tested code more succinct.
It just means the uncovered code is just a larger percentage of
the codebase.
2018-06-07 19:09:25 -06:00
d513f56c8c Readme version bump 2018-06-07 17:32:07 -06:00
7aa05618a3 data_replicator -> ncp
Fixes #327
2018-06-07 17:11:17 -06:00
cdfbbe5e60 Fix diagram typos 2018-06-07 17:11:17 -06:00
fe7d1cb81c Race -> Or
Thanks for the suggestion @FishmanL!
2018-06-07 17:11:03 -06:00
c2a9395a4b perf counters 2018-06-07 14:59:21 -07:00
586279bcfc Add server diagrams 2018-06-07 15:24:44 -06:00
8bd10e7c4c Cleanup top-level lib doc 2018-06-07 15:24:44 -06:00
928e6165bc Add TPU & RPU diagrams 2018-06-07 15:24:44 -06:00
77c9e801aa fixed client demo (#325)
* fixed client demo
2018-06-07 13:51:15 -07:00
c78132417f fix deadlock 2018-06-07 13:52:33 -06:00
849928887e undo 2018-06-07 13:52:33 -06:00
ba1163d49f fix logs 2018-06-07 13:52:33 -06:00
6f9c89af39 fix deadlock 2018-06-07 13:52:33 -06:00
246b8b1242 No longer cat scripts
Because we keep changing those scripts and not updating the readme.

Also, this removes the "-b 9000" starting validators. Is that right?
Or should we be passing that to the validator config?
2018-06-07 12:17:43 -06:00
f0db68cb75 Add note about validator.json and -d flag to config generating scripts 2018-06-07 11:15:41 -06:00
f0d1fdfb46 Add missing module descriptions 2018-06-07 09:25:36 -06:00
3b8b2e030a Better docs for transaction 2018-06-07 09:25:36 -06:00
b4fee677a5 Better docs for payment_plan 2018-06-07 09:25:36 -06:00
fe706583f9 Better docs for sigverify_stage 2018-06-07 09:25:36 -06:00
d0e0c17ece Better docs for rpu 2018-06-07 09:25:36 -06:00
5aaa38bcaf Better docs for write_stage 2018-06-07 09:25:36 -06:00
6ff9b27f8e Better docs for entry 2018-06-07 09:25:36 -06:00
3f4e035506 Better docs for budget 2018-06-07 09:25:36 -06:00
57d9fbb927 Better docs for banking_stage 2018-06-07 09:25:36 -06:00
ee44e51b30 Better docs for the bank 2018-06-07 09:25:36 -06:00
5011f24123 Move more interesting content into first header
The first header and its content is the only text displayed on
GitHub's mobile page. Reorder so that the disclaimer is the only
information people see.

Disclaimer: IANAL and assume reordering these doesn't matter. :)
2018-06-07 09:25:36 -06:00
d1eda334f3 gdb 2018-06-07 09:25:08 -06:00
2ae5ce9f2c Do not use cuda for multinode-demo validator component 2018-06-07 07:04:33 -06:00
4f5ac78b7e Add readme to crates.io 2018-06-06 15:00:25 -06:00
074c9af020 Shellcheck again 2018-06-05 15:32:25 -06:00
2da2d4e365 More shellcheck 2018-06-05 15:32:25 -06:00
8eb76ab2a5 Fix shellcheck 2018-06-05 15:32:25 -06:00
a710d95243 Fix non-erasure blob nulling 2018-06-05 15:32:25 -06:00
a06535d7ed cargo fmt 2018-06-05 15:32:25 -06:00
f511ac9be7 Fixes for receiving old blobs and nulling the window with coding 2018-06-05 15:32:25 -06:00
e28ad2177e Receive fixes 2018-06-05 15:32:25 -06:00
cb16fe84cd Rework to fix coding blob insertion 2018-06-05 15:32:25 -06:00
ec3569aa39 Move receive_index to correct place 2018-06-05 15:32:25 -06:00
246edecf53 Add receive_index for broadcast blobs and fix blobs_len position 2018-06-05 15:32:25 -06:00
34834c5af9 Store another size in the data block so it is coded as well 2018-06-05 15:32:25 -06:00
b845245614 Restore more of the blob window and add is_coding helper 2018-06-05 15:32:25 -06:00
5711fb9969 Generate coding for the current blob set not just the first coding set 2018-06-05 15:32:25 -06:00
d1eaecde9a Fix deadlock and only push to contq if it's not a coding blob 2018-06-05 15:32:25 -06:00
00c8505d1e Handle set_flags error 2018-06-05 15:32:25 -06:00
33f01efe69 Fixes for erasure coding 2018-06-05 15:32:25 -06:00
377d312c81 Revert log levels 2018-06-05 15:32:25 -06:00
badf5d5412 Add window recovery 2018-06-05 15:32:25 -06:00
0339f90b40 Fix gf-complete url and symlinks 2018-06-05 15:32:25 -06:00
5455e8e6a9 Review comments 2018-06-05 15:32:25 -06:00
6843b71a0d Debug erasure ci script 2018-06-05 15:32:25 -06:00
634408b5e8 Add erasure build to ci 2018-06-05 15:32:25 -06:00
d053f78b74 Erasure refinements, fix generating orders table 2018-06-05 15:32:25 -06:00
93b6fceb2f generate coding after indexing 2018-06-05 15:32:25 -06:00
ac7860c35d indexing blobs then coding 2018-06-05 15:32:25 -06:00
b0eab8729f Add erasure ci script 2018-06-05 15:32:25 -06:00
cb81f80b31 Enable logging for client demo 2018-06-05 15:32:25 -06:00
ea97529185 Fix erasure compilation 2018-06-05 15:32:25 -06:00
f1075191fe Clean up comments: Event -> Transaction 2018-06-04 21:43:46 -06:00
74c479fbc9 Delete bitrotted docs 2018-06-04 21:43:46 -06:00
7e788d3a17 No longer need explicit refs in rustc 1.26 2018-06-04 21:43:46 -06:00
69b3c75f0d Power of two chance (#314)
* fix validator script
* 1/2^30 that we fail due to random returning the same value
2018-06-04 13:32:34 -07:00
b2c2fa40a2 comments 2018-06-03 22:08:25 -06:00
50458d9524 more tests 2018-06-03 22:08:25 -06:00
9679e3e356 more tests 2018-06-03 22:08:25 -06:00
6db9f92b8a crdt gossip tests 2018-06-03 22:08:25 -06:00
4a44498d45 Fix args in validator script, readme version, client-demo perf print 2018-06-02 21:55:27 -06:00
216510c573 repair socket and receiver thread (#303)
repair socket and receiver thread
2018-06-02 08:32:51 -07:00
fd338c3097 Run release binary for leader node 2018-06-01 17:10:48 -06:00
b66ebf5dec Version bump 2018-06-01 17:10:37 -06:00
5da99de579 Review feedback 2018-06-01 13:43:38 -06:00
3aa2907bd6 Restore shellcheck 2018-06-01 13:43:38 -06:00
05d1618659 Add more detail to testnet setup 2018-06-01 13:43:38 -06:00
86113811f2 Readme/demo cleanup 2018-06-01 13:43:38 -06:00
53ecaa03f1 Need another beta 2018-05-31 19:08:09 -06:00
205c1aa505 Version bump 2018-05-31 18:49:41 -06:00
9b54c1542b Move defaults from bash to Rust 2018-05-31 17:18:11 -07:00
93d5d1b2ad Default to 1 node 2018-05-31 17:18:11 -07:00
4c0f3ed6f3 Attempt to revive the singlenode demo 2018-05-31 17:18:11 -07:00
2580155bf2 Enable last of the ignored tests 2018-05-31 16:45:21 -06:00
6ab0dd4df9 Remove config options from fullnode 2018-05-31 16:15:02 -06:00
4b8c36b6b9 Add solana-fullnode-config 2018-05-31 16:15:02 -06:00
359a8397c0 Make bootstrapping functions accessible to other binaries 2018-05-31 16:15:02 -06:00
c9fd5d74b5 Boot futures 0.1
We added them thinking it'd be a good stepping stone towards an
asynchronous thin client, but it's used inconsistently and where
it used, the function is still synchronous, which is just confusing.
2018-05-31 14:13:09 -06:00
391744af97 Speed up the creation of the million accounts
All threads were locked on the same set of signatures.
2018-05-31 12:13:18 -06:00
587ab29e09 Don't register entry ID until after processing its transactions 2018-05-31 12:13:18 -06:00
80f07dadc5 Generalize process_entries()
And use it in fullnode
2018-05-31 12:13:18 -06:00
60609a44ba Initialize recorder from bank's last_id 2018-05-31 12:13:18 -06:00
30c8fa46b4 rustc version bump 2018-05-30 20:49:55 -06:00
7aab7d2f82 Sleep between events if PoH is disabled 2018-05-30 15:55:10 -06:00
a8e1c44663 names 2018-05-30 14:50:53 -06:00
a2b92c35e1 thread names 2018-05-30 14:50:53 -06:00
9f2086c772 names 2018-05-30 14:50:53 -06:00
3eb005d492 names for threds 2018-05-30 14:50:53 -06:00
68955bfcf4 Change multinode script argument to leader path
Some may have cloned their code in different place
2018-05-30 14:49:42 -06:00
9ac7070e08 fix ci 2018-05-30 14:04:48 -06:00
e44e81bd17 fmt 2018-05-30 14:04:48 -06:00
f5eedd2d19 fmt 2018-05-30 14:04:48 -06:00
46059a37eb skip shell check 2018-05-30 14:04:48 -06:00
adc655a3a2 scripts 2018-05-30 14:04:48 -06:00
3058f80489 log 2018-05-30 14:04:48 -06:00
df98cae4b6 cleanup 2018-05-30 14:04:48 -06:00
d327e0aabd warn on tx verify sig 2018-05-30 14:04:48 -06:00
17d3a6763c update 2018-05-30 14:04:48 -06:00
02c5b0343b fixed cloned 2018-05-30 14:04:48 -06:00
2888e45fea comments 2018-05-30 14:04:48 -06:00
f1311075d9 integration tests 2018-05-30 14:04:48 -06:00
6c380e04a3 fix 2018-05-30 14:04:48 -06:00
cef1c208a5 Crdt pipeline, coalesce window repair requests in the listener by examining all of them at once, and ublock those threads from doing io. 2018-05-30 14:04:48 -06:00
ef8eac92e3 Version bump 2018-05-29 20:33:45 -07:00
9c9c63572b cargo fmt
rustfmt was updated with 1.26.1
2018-05-29 20:33:45 -07:00
6c0c6de1d0 Better error names 2018-05-29 20:33:45 -07:00
b57aecc24c Better error if Bank doesn't recognize tx last_id 2018-05-29 20:33:45 -07:00
290dde60a0 Test invalid tokens and fees 2018-05-29 20:33:45 -07:00
38623785f9 Add fee to Transaction
Fixes #161
2018-05-29 20:33:45 -07:00
256ecc7208 Build status badge now excludes pull requests 2018-05-29 20:33:34 -07:00
76b06b47ba Delete dead code 2018-05-29 18:09:03 -06:00
cf15cf587f spending plan -> budget
Review feedback from @sakridge
2018-05-29 18:09:03 -06:00
134c7add57 Fix bench build 2018-05-29 18:09:03 -06:00
ac0791826a plan.rs -> payment_plan.rs 2018-05-29 18:09:03 -06:00
d2622b7798 Allow for addtional smart contract languages
Fixes #159
2018-05-29 18:09:03 -06:00
f82cbf3a27 Move Budget EDSL into its own module 2018-05-29 18:09:03 -06:00
aa7e3df8d6 Plan -> Budget
Budget is now an EDSL. PaymentPlan is the interface to it.
2018-05-29 18:09:03 -06:00
ad00d7bd9c Move plan methods to a trait 2018-05-29 18:09:03 -06:00
8d1f82c34d breaks 2018-05-29 16:53:26 -07:00
0cb2036e3a comment on bad blob usage 2018-05-29 16:53:26 -07:00
2b1e90b0a5 More idiomatic Rust 2018-05-29 14:04:27 -06:00
f2ccc133a2 Finally made fetch happen 2018-05-29 14:04:27 -06:00
5e824b39dd Move multinode communication outside TPU 2018-05-29 14:04:27 -06:00
41efcae64b Remove dead code
History: we thought SigVerifyStage would use these, but it does
signature verification before deserializing transactions.
2018-05-29 10:38:58 -06:00
cf5671d058 tr -> tx
Missed a few.
2018-05-29 10:38:58 -06:00
2570bba6b1 Make apply_payment a method
History: the function was pulled out of Bank when each field wasn't
wrapped in a RwLock, and that locking 'balances' meant to lock
everything in the bank. Now that the RwLocks are here to stay,
we can make it a method again.
2018-05-29 10:38:58 -06:00
71cb7d5c97 Better names 2018-05-29 10:38:58 -06:00
0df6541d5e Fewer public functions 2018-05-29 10:38:58 -06:00
52145caf7e Cleanup: make 'verified' qualifier implicit
History: Qualifying the method names with 'verified' was done to
distinguish them from methods that first did signature verification.
After we moved all signature verication to SigVerifyStage, we removed
those methods from Bank, leaving only the 'verified' ones.

This patch removes the word 'verified' from all method names, since
it is now implied by any code running after SigVerifyStage.
2018-05-29 10:38:58 -06:00
86a50ae9e1 Add RUST_BACKTRACE 2018-05-28 22:23:25 -07:00
c64cfb74f3 Update code coverage command 2018-05-28 22:23:25 -07:00
26153d9919 Avoid docker buildkite plugin, which is not supported by bkrun 2018-05-28 22:23:25 -07:00
5af922722f Add local buildkite CI runner 2018-05-28 22:23:25 -07:00
b70d730b32 Support local .a, skip if unable to find .a 2018-05-28 22:23:25 -07:00
bf4b856e0c Don't fail if CODECOV_TOKEN is undefined 2018-05-28 22:23:25 -07:00
0cf0ae6755 s/label:/name:/g 2018-05-28 22:23:25 -07:00
29061cff39 Delint existing shell scripts 2018-05-28 05:18:46 -06:00
b7eec4c89f Lint shell scripts in CI 2018-05-28 05:18:46 -06:00
a3854c229e More rebase typos 2018-05-26 20:48:42 -06:00
dcde256433 Fix rebase typo 2018-05-26 20:28:22 -06:00
931bdbd5cd Fix typo 2018-05-26 20:25:44 -06:00
b7bd59c344 Cleanup whitespace
And delete rebasing artifact
2018-05-26 20:23:18 -06:00
2dbf9a6017 rename 2018-05-26 20:13:42 -06:00
fe93bba457 logs
poll both endpoints in client

logs

logs

logs

names

verify plan not sig

log

set udp buffer to max

drop output

more verbose about window requests

log the leader

load leader identity

readme for single node demo

update

asserts

update

replay all

rsync

dynamic file read in testnode

fix

cleanup

readme

sum

fix scripts

cleanup

cleanup

readme
2018-05-26 20:13:42 -06:00
6e35f54738 Simplify environment blocks 2018-05-26 14:38:26 -07:00
089294a85e 'ignored' step failures are no longer ignored 2018-05-26 11:00:20 -07:00
25c0b44641 Run ignored build step in docker 2018-05-26 11:00:20 -07:00
58c1589688 More typos 2018-05-26 00:36:50 -06:00
bb53f69016 Fix typos 2018-05-26 00:36:50 -06:00
75659ca042 Light up coverage build 2018-05-26 00:36:50 -06:00
fc00594ea4 Move multinode test to integration tests 2018-05-26 00:36:50 -06:00
8d26be8b89 Run benchmarks in nightly
And name functions the same way as test functions
2018-05-26 00:36:50 -06:00
af4e95ae0f Only check formatting in stable build 2018-05-26 00:36:50 -06:00
ffb4a7aa78 Boot TravisCI configuration 2018-05-26 00:36:50 -06:00
dcaeacc507 request_stage::serialize_packets -> packet::to_blobs
Good stuff - no need to hide them.
2018-05-25 17:31:07 -06:00
4f377e6710 Generalize serialize_responses 2018-05-25 17:31:07 -06:00
122db85727 Move channel-oriented code into request_stage 2018-05-25 17:31:07 -06:00
a598e4aa74 Fix comments 2018-05-25 17:31:07 -06:00
733b31ebbd testnode -> fullnode
It's the real deal.
2018-05-25 17:31:07 -06:00
dac9775de0 Replace client-demo with multinode-demo 2018-05-25 17:31:07 -06:00
46c19a5783 Rename sigverify modules 2018-05-25 17:31:07 -06:00
aaeb5ba52f tr -> tx 2018-05-25 16:47:21 -06:00
9f5a3d6064 events -> transactions 2018-05-25 16:47:21 -06:00
4cdf873f98 Delete event.rs 2018-05-25 16:47:21 -06:00
b43ae748c3 Update publish.sh 2018-05-25 16:08:14 -06:00
02ddd89653 Version bump
And solana.io -> solana.com
2018-05-25 15:37:07 -06:00
bbe6eccefe log 2018-05-25 07:02:39 -06:00
6677a7b66a verify plan not sig 2018-05-25 07:02:39 -06:00
75c37fcc73 names 2018-05-25 07:02:39 -06:00
5be71a8a9d logs 2018-05-25 07:02:39 -06:00
b9ae7d1ebb logs 2018-05-25 07:02:39 -06:00
8b02e0f57c logs 2018-05-25 07:02:39 -06:00
342cc7350a poll both endpoints in client 2018-05-25 07:02:39 -06:00
2335a51ced logs 2018-05-25 07:02:39 -06:00
868df1824c fmt 2018-05-24 17:40:33 -06:00
83c11f0f9d logs 2018-05-24 17:40:33 -06:00
1022f1b0c6 logs 2018-05-24 17:40:33 -06:00
c2c80232e3 logs 2018-05-24 17:40:33 -06:00
115f4e54b8 update 2018-05-24 17:40:33 -06:00
669b1694b8 exponentail backoff for retransmit 2018-05-24 17:40:33 -06:00
2128c58fbe logs and tps counting 2018-05-24 10:35:23 -06:00
e12e154877 Boot Event timestamp/singature constructors 2018-05-24 10:10:41 -06:00
73d3c17507 Migrate from Event to Transaction Timestramp/Signature 2018-05-24 10:10:41 -06:00
7f647a93da Add last_id to Event timestamp/signature constructors 2018-05-24 10:10:41 -06:00
ecb3dbbb60 Add witness tx constructors 2018-05-24 10:10:41 -06:00
cc907ba69d Add Instruction type 2018-05-24 10:10:41 -06:00
5a45eef1dc Exit cleanup (#252)
* Ignore record_stage exit reason. We only really care about panic exit versus graceful exit.
* Ignore coverage build in CI
2018-05-24 10:03:17 -06:00
0d980e89bc cargo fmt
@aeyakovenko: https://github.com/rust-lang/rust.vim#formatting-with-rustfmt
2018-05-23 20:05:08 -06:00
ef87832bff fixed 2018-05-23 17:24:58 -06:00
94507d1aca cuda 2018-05-23 17:24:58 -06:00
89924a38ff cuda 2018-05-23 17:24:58 -06:00
7faa2b8698 fixed demo 2018-05-23 17:24:58 -06:00
65352ce8e7 fix 2018-05-23 17:24:58 -06:00
f1988ee1e3 help 2018-05-23 17:24:58 -06:00
82ac8eb731 use client ports 2018-05-23 17:24:58 -06:00
ae47e34fa5 fix 2018-05-23 17:24:58 -06:00
28e781efc3 break early 2018-05-23 17:24:58 -06:00
5c3ceb8355 aws demo2 2018-05-23 17:24:58 -06:00
c9113b381d Pull channel functionality into record_stage
This makes record_stage consistent with the other stages. The stage
manages the channels. Anything else is in a standalone object. In
the case of the record_stage, that leaves almost nothing!
2018-05-23 17:15:28 -06:00
75e69eecfa Fix nightly bench 2018-05-23 17:15:03 -06:00
f3c4acc723 cleanup multi node test 2018-05-23 16:59:17 -06:00
2a0095e322 Remove unused variable in multinode-demo fix compiler warning 2018-05-23 16:55:45 -06:00
9ad5f3c65b fix option (#246) 2018-05-23 14:48:00 -07:00
579de64d49 Delete binary again 2018-05-23 14:15:59 -06:00
d4200a7b1e Fix build
GenKeys() fix and new multinode module crossed in flight.
2018-05-23 14:10:26 -06:00
84477835dc Fix nondeterministic key generation (#243)
Our one and only unsafe operation was ...unsafe.
2018-05-23 14:04:07 -06:00
504b318ef1 Hooks for binaries to run as leader or replicator and attach to network (#221) 2018-05-23 14:03:19 -06:00
f154c8c490 Add data to errors to ease debugging 2018-05-23 12:50:23 -06:00
d4959bc157 Test cleanup
GenKey unit tests were in the benchmark suite.
2018-05-23 12:50:23 -06:00
87e025fe22 fmt 2018-05-23 12:07:44 -06:00
8049323ca8 @garious review 2018-05-23 12:07:44 -06:00
b38c7ea2ff fmt 2018-05-23 12:07:44 -06:00
239b925fb3 woop 2018-05-23 12:07:44 -06:00
60da7f7aaf wip 2018-05-23 12:07:44 -06:00
8646ff4927 refactor wip 2018-05-23 12:07:44 -06:00
59be94a81f cleanup 2018-05-23 12:07:44 -06:00
437c485e5c cleanup 2018-05-23 12:07:44 -06:00
79a58da6a9 Merge pull request #240 from mvines/master
CI overhaul follow-ups
2018-05-22 23:27:19 -07:00
ae29641a18 Run most CI steps in docker 2018-05-22 23:16:25 -07:00
9c3f65bca9 Update build status badge 2018-05-22 22:59:19 -07:00
086365b4c4 Merge pull request #237 from garious/hoist-lastid
Hoist last_id
2018-05-22 17:48:25 -06:00
64044da49c Merge pull request #239 from sakridge/fix_bad_sig_mac
Fix test_bad_sig on mac
2018-05-22 17:48:01 -06:00
7b5b7feb63 Fix test_bad_sig on mac 2018-05-22 16:40:01 -07:00
2e059f8504 Rename TransactionData to Contract
No longer a single place to get all the data that was signed.
2018-05-22 17:00:40 -06:00
207b6686d1 Hoist last_id
First step in unifying Witness processing and Transaction processing
2018-05-22 17:00:40 -06:00
abfd7d6951 Merge pull request #234 from sakridge/fix_events_addr
Send events to the right address
2018-05-22 16:59:28 -06:00
7fc166b5ba Merge pull request #238 from aeyakovenko/tvu_cleanup
tvu cleanup
2018-05-22 15:41:33 -07:00
021953d59a cleanup 2018-05-22 15:30:46 -07:00
bbe89df2ff fmt 2018-05-22 15:18:07 -07:00
a638ec5911 builds 2018-05-22 15:17:59 -07:00
26272a3600 split out stages 2018-05-22 14:26:28 -07:00
8454eb79d0 Send events to the right address and set recv socket timeout 2018-05-22 13:52:50 -07:00
796f4b981b Merge pull request #233 from mvines/ci
Add in-tree buildkite pipeline
2018-05-22 13:06:24 -07:00
34514d65bc Add in-tree buildkite pipeline 2018-05-21 23:43:27 -07:00
2786357082 Merge pull request #230 from garious/generalize-topackets
Benchmark the banking stage
2018-05-18 19:47:26 -07:00
4badeacd1d Merge pull request #226 from aeyakovenko/converge_test
check convergence
2018-05-16 23:44:23 -07:00
63a0ba6ec8 fixed 2018-05-16 23:28:03 -07:00
9a4ce6d70e fmt 2018-05-16 23:27:26 -07:00
35ee2d0ce1 cleanup 2018-05-16 23:27:26 -07:00
b04716d40d fmt 2018-05-16 23:27:26 -07:00
051fa6f1f1 cleanup 2018-05-16 23:27:26 -07:00
8dc1b07e75 docs 2018-05-16 23:27:26 -07:00
bee1e7ebaf compute convergence maximum 2018-05-16 23:27:26 -07:00
f3f0b9f0c5 update 2018-05-16 23:27:26 -07:00
a5cf745e1c check convergence 2018-05-16 23:27:26 -07:00
273b800047 Benchmark the banking stage 2018-05-16 23:18:58 -07:00
6c1f1c2a7a Promote create_entry() to Entry::new() 2018-05-16 23:18:58 -07:00
9c62f8d81f Add Event::Transaction constructor 2018-05-16 23:18:58 -07:00
82aef7ebe2 Merge pull request #225 from mvines/deploy
Auto deploy tagged versions of solana to crate.io
2018-05-16 23:36:15 -06:00
57636d3d5f Auto deploy tagged versions of solana to crate.io 2018-05-16 21:38:14 -07:00
dc87effc0a Merge pull request #229 from garious/fix-bench
Fix the benchmark build
2018-05-16 16:37:56 -06:00
f0c9823e9f Merge pull request #228 from garious/generalize-topackets
request::to_request_packets -> packet::to_packets
2018-05-16 16:37:29 -06:00
0b91dd6163 Fix the benchmark build 2018-05-16 16:35:50 -06:00
4955c6f13a request::to_request_packets -> packet::to_packets 2018-05-16 16:11:53 -06:00
2e7beca9ba Generalize to_request_packets 2018-05-16 16:01:19 -06:00
59c1b9983d Merge pull request #220 from garious/add-tpu
Add tpu
2018-05-16 12:21:07 -06:00
f7083e0923 Remove transaction processing from RPU and request processing from TVU 2018-05-15 12:15:29 -06:00
6d4defdf96 Offload event processing to the TPU 2018-05-15 11:33:43 -06:00
b826f837f8 First attempt to pull TPU into the server 2018-05-15 11:25:55 -06:00
5855e18a4e Let server own the bank, not TPU/RPU 2018-05-15 11:21:48 -06:00
3f38c0a245 Feed events socket into the server 2018-05-15 11:19:58 -06:00
cfe8b3fc55 Wrap the RPU with new object Server 2018-05-15 11:00:01 -06:00
e9ee020b5f Rename constructors 2018-05-15 10:45:36 -06:00
1bcf3891b4 New TPU/RPU constructors 2018-05-15 10:44:47 -06:00
5456de63e9 Less state 2018-05-15 10:38:17 -06:00
9026c70952 Inline Rpu::new 2018-05-15 10:33:16 -06:00
99dc4ea4a9 Spin up threads from Rpu/Tpu constructors 2018-05-15 10:30:52 -06:00
0aaa500f7c Rpu/Tpu serve() functions now only spin up threads 2018-05-15 10:10:45 -06:00
5f5be83a17 Hoist socket creation/configuration
TODO: Add a library for socket configuration.
2018-05-15 10:05:23 -06:00
7e44005a0f Don't do error-prone things in functions that spawn threads 2018-05-15 09:53:51 -06:00
ee3fb985ea Hoist set_timeout 2018-05-15 09:42:28 -06:00
2a268aa528 Reorder to reflect dependencies 2018-05-15 09:17:48 -06:00
cd262cf860 Merge pull request #223 from rlkelly/202__rust_refactor
202  rust refactor
2018-05-15 08:44:47 -06:00
a1889c32d4 fixed CrdtToSmall typo 2018-05-15 10:29:56 -04:00
d42d024d9c minor changes 2018-05-15 10:23:11 -04:00
7b88b8d159 Merge pull request #222 from aeyakovenko/fixed_ignore_tests
fix ignore tests
2018-05-14 22:18:38 -07:00
4131071b9a fix ignore tests 2018-05-14 22:06:42 -07:00
ef6bd7e3b8 Add TPU 2018-05-14 17:36:19 -06:00
374bff6550 Extract event processing from request_stage 2018-05-14 17:31:27 -06:00
0a46bbe4f9 Merge pull request #219 from garious/add-write-stage
Move write_service and drain_service into new write_stage module
2018-05-14 17:18:04 -06:00
f4971be236 Merge pull request #218 from aeyakovenko/multitest-rebase
multinode test
2018-05-14 17:17:34 -06:00
421273f862 disable tests that fail with kcov 2018-05-14 16:07:21 -07:00
2c7f229883 wait longer 2018-05-14 15:48:43 -07:00
904eabad2f waint longer 2018-05-14 15:48:24 -07:00
8b233f6be4 update 2018-05-14 15:43:26 -07:00
08fc821ca9 rebase 2018-05-14 15:35:54 -07:00
81706f2d75 Move write_service and drain_service into new write_stage module 2018-05-14 16:31:31 -06:00
7b50c3910f fmt 2018-05-14 15:21:41 -07:00
2d635386af rebased 2018-05-14 15:20:41 -07:00
a604dcb4c4 Merge pull request #217 from garious/add-historian-stage
Add record_stage to pipeline
2018-05-14 16:01:45 -06:00
7736b9cac6 Boot Alice and Bob from the unit tests 2018-05-14 15:39:34 -06:00
d2dd005a59 accountant -> bank 2018-05-14 15:33:11 -06:00
6e8f99d9b2 Purge EventProcessor 2018-05-14 14:45:29 -06:00
685de30047 Purge EventProcessor from RPU 2018-05-14 14:35:25 -06:00
17cc9ab07f Rename Historian to RecordStage
Historian was a legacy name. The new name reflects the new pipelined
architecture.
2018-05-14 14:19:19 -06:00
3f10bf44db Config recorder with any kind of Duration, not just milliseconds 2018-05-14 14:12:36 -06:00
27984e469a Multiply duration, not milliseconds 2018-05-14 13:58:42 -06:00
a2c05b112e Add historian to pipeline
No longer intercept entries to register_entry_id(). Intead,
register the ID in the Write stage.

EventProcessor is now just being used as a place to store data.

Fixes #216
2018-05-14 12:43:40 -06:00
a578c1a5e3 Merge pull request #215 from garious/suppress_panic_message_in_tests
Don't output panic noise from panic test
2018-05-14 11:46:22 -06:00
500aaed48e Merge pull request #211 from garious/add-tx-count
Drop EntryInfo subscriptions
2018-05-14 10:41:09 -06:00
4a94da8a94 Don't output panic noise from panic test
P.S. rustfmt 0.4.1-stable (7a807262 2018-04-20)
2018-05-14 10:38:59 -06:00
cc447c0fda Drop support for EntryInfo subscriptions 2018-05-14 09:53:57 -06:00
0ae69bdcd9 Get transactionn_count via GetTransactionCount instead of EntryInfo 2018-05-14 09:45:09 -06:00
5ba20a94e8 Panic on error to get same signature as transaction_count() 2018-05-14 09:43:40 -06:00
f168c377fd Get last_id via GetLastId instead of EntryInfo 2018-05-14 09:40:29 -06:00
dfb754dd13 Revive GetLastId messages 2018-05-14 09:35:10 -06:00
455050e19c Expose the server-side transaction count 2018-05-14 07:21:12 -06:00
317031f455 Add transaction count to accountant 2018-05-14 06:49:51 -06:00
b132ce1944 Merge pull request #210 from aeyakovenko/buildite_coverage
ignore unstable tests
2018-05-13 22:00:32 -06:00
8b226652aa unstable 2018-05-13 20:54:41 -07:00
2c7fe3ed8d unstable 2018-05-13 20:51:07 -07:00
3d5f2b3c28 unstable 2018-05-13 20:45:55 -07:00
7a79afe4a6 unstable 2018-05-13 20:41:54 -07:00
1f7387a39b increase sleep 2018-05-13 20:33:41 -07:00
0fc2bee144 Merge pull request #208 from rlkelly/203__remove_old_genkey
removed old keygen
2018-05-13 19:04:23 -06:00
791ae852a2 removed old keygen 2018-05-13 18:14:10 -04:00
c2fcd876d7 Merge pull request #206 from garious/add-accounting-stage
More modules
2018-05-12 18:05:10 -06:00
d239d4a495 Add missing files 2018-05-12 17:57:28 -06:00
aec05ef602 Move RequestProcessor into its own module 2018-05-12 17:50:55 -06:00
e5d46d998b Move thin client messages into their own module 2018-05-12 17:41:27 -06:00
b2e3299539 Only pass accountant write_service 2018-05-12 17:30:15 -06:00
c308a6459f cargo fmt 2018-05-12 17:27:15 -06:00
4eb1bc08a7 Merge pull request #205 from rlkelly/203__test_key_generation
203  test key generation
2018-05-12 17:26:46 -06:00
ff5e1c635f increased iterations 2018-05-12 18:18:18 -04:00
6149c2fcb5 added benchmarks for two GenKeys 2018-05-12 18:08:08 -04:00
d7cd80dce5 Merge pull request #204 from garious/add-accounting-stage
TPU cleanup
2018-05-12 15:47:37 -06:00
6264508f5e Consistent naming of senders and receivers 2018-05-12 15:24:20 -06:00
a3869dd4c1 Move entry_receiver to RequestStage
This can move to AccountingStage once RequestStage stops
calling process_events().
2018-05-12 15:14:37 -06:00
a3d2831f8c Free up the name 'accounting_stage' 2018-05-12 14:05:57 -06:00
4cd1fa8c38 refactored seed generation 2018-05-12 15:42:27 -04:00
1511dc43d7 Move RequestProcessor out of Rpu/Tvu state 2018-05-12 11:39:24 -06:00
3d82807965 Delete dead code 2018-05-12 11:24:40 -06:00
4180571660 Don't pass events_socket to RPU 2018-05-12 11:11:30 -06:00
421d9aa501 Free up the name 'tpu' 2018-05-12 10:53:25 -06:00
898f4971a2 Free up name 'thin_client_service' 2018-05-12 10:50:22 -06:00
7ab3331f01 Move validation processor to its own module 2018-05-12 00:31:32 -06:00
b4ca414492 More object-oriented 2018-05-12 00:19:12 -06:00
73abea088a No need for TPU dependency 2018-05-11 23:51:35 -06:00
2376dfc139 Let thin client own the receiver channel 2018-05-11 23:46:04 -06:00
d2f95d5319 Move thin client service thread into thin_client_service.rs 2018-05-11 23:37:44 -06:00
cd96843699 Free up name ThinClientService 2018-05-11 23:37:14 -06:00
ca80bc33c6 Move the writer stage's utilities to its own module 2018-05-11 22:36:16 -06:00
19607886f7 Move sig verification stage into its own module 2018-05-11 21:51:37 -06:00
3c11a91f77 Cleanup verifier error handling 2018-05-11 21:01:07 -06:00
b781fdbd04 Reorganize 2018-05-11 20:50:50 -06:00
765d901530 Better names 2018-05-11 20:18:04 -06:00
3cedbc493e Reorder to reflect the pipeline order 2018-05-11 20:11:25 -06:00
0488d0a82f Extract sig verify functions 2018-05-11 19:59:40 -06:00
f0be595e4c Create function for thin client thread 2018-05-11 17:58:27 -06:00
55100854d6 Better names 2018-05-11 16:41:35 -06:00
600a1f8866 Initialize thin client with events port 2018-05-11 16:35:53 -06:00
95bf68f3f5 Correct some strange naming 2018-05-11 16:24:18 -06:00
bcdb058492 cargo fmt 2018-05-11 13:06:05 -06:00
7f46aef624 Merge pull request #200 from jackson-sandland/153-panic-cleanup
issue #153 - panic cleanup
2018-05-11 13:05:04 -06:00
e779496dfb Update signature.rs 2018-05-11 11:49:22 -07:00
3d77fa5fbc Merge branch 'master' into 153-panic-cleanup 2018-05-11 11:40:20 -07:00
250830ade9 cargo fmt run 2018-05-11 11:38:52 -07:00
7b2eb7ccfc Merge pull request #189 from rlkelly/156__remove_user_keys_in_mintdemo
156  remove user keys in mintdemo
2018-05-11 12:19:32 -06:00
458c27c6e9 Merge branch 'master' into 153-panic-cleanup 2018-05-11 11:18:45 -07:00
a49e664e63 Merge branch '156__remove_user_keys_in_mintdemo' of github.com:rlkelly/solana into 156__remove_user_keys_in_mintdemo 2018-05-11 14:07:48 -04:00
f20380d6b4 changed RwLock to RefCell 2018-05-11 14:07:41 -04:00
05a5e551d6 Merge branch 'master' into 156__remove_user_keys_in_mintdemo 2018-05-11 13:00:44 -04:00
d278b71cb2 added tests and utility method for key generation 2018-05-11 12:55:05 -04:00
a485c141d5 Merge pull request #199 from garious/add-accounting-stage
Fix race condition in Accountant::apply_payment()
2018-05-11 10:54:32 -06:00
8a9f6b9ae3 Merge pull request #201 from CriesofCarrots/master
Generalize next tick functions to carry events
2018-05-11 10:54:14 -06:00
7144090528 Fix whitespace 2018-05-11 10:40:31 -06:00
ee0015ac38 Fix whitespace 2018-05-11 10:34:46 -06:00
8b7f7f1088 Generalize next tick functions to carry events 2018-05-11 09:45:42 -06:00
c95c6a75f8 tpu.rs - panic cleanup 2018-05-10 20:49:58 -07:00
44bf79e35f transaction.rs - panic cleanup 2018-05-10 18:24:33 -07:00
bb654f286c tpu.rs - panic cleanup 2018-05-10 18:21:10 -07:00
1acd2aa8cf Fix race condition in Accountant::apply_payment() 2018-05-10 19:07:15 -06:00
18d3659b91 timing.rs - panic cleanup 2018-05-10 17:47:27 -07:00
63a4bafa72 thin_client - panic cleanup 2018-05-10 17:46:10 -07:00
4eb2e84c9f streamer.rs - panic cleanup 2018-05-10 17:38:00 -07:00
73c7fb87e8 signature.rs - panic cleanup 2018-05-10 17:15:53 -07:00
c1496722aa packet.rs - panic cleanup 2018-05-10 17:11:31 -07:00
d9f81b0c8c mint.rs - panic cleanup 2018-05-10 17:06:43 -07:00
d69beaabe1 historian.rs - panic cleanup 2018-05-10 17:00:37 -07:00
b7a0bd6347 event.rs - panic cleanup 2018-05-10 16:59:13 -07:00
882ea6b672 erasure.rs - panic cleanup 2018-05-10 16:54:21 -07:00
736d3eabae Merge pull request #198 from garious/add-accounting-stage
Move more code out of TPU
2018-05-10 17:24:22 -06:00
af53197c04 cargo +nightly fmt 2018-05-10 16:58:37 -06:00
cf186c5762 Better names 2018-05-10 16:58:37 -06:00
f384a2ce85 Move streamer-specific utility into streamer module 2018-05-10 16:58:37 -06:00
803b76e997 More idiomatic Rust 2018-05-10 16:58:37 -06:00
230d7c3dd6 Move all Request processing into thin_client_service 2018-05-10 16:58:37 -06:00
4f629dd982 Add events socket instead of modifying the existing socket 2018-05-10 16:54:43 -06:00
4fdd891b54 More precise function names 2018-05-10 16:54:43 -06:00
64a892321a Merge pull request #197 from sakridge/fixes_for_entry_serialization
Fixes for serializing entries over blobs
2018-05-10 16:53:30 -06:00
a80991f2b3 Fixes for serializing entries over blobs and reorg into ledger 2018-05-10 15:30:30 -07:00
c9cd81319a Set theme jekyll-theme-slate 2018-05-10 13:28:29 -07:00
521ae21632 Merge pull request #193 from sakridge/serialize_entries_over_multiple_blobs
Serialize entries over multiple blobs
2018-05-10 13:53:48 -06:00
bcd6606a16 ecdsa.rs - panic cleanup 2018-05-09 18:19:23 -07:00
52ebb88205 accountant.rs - simplify error messages 2018-05-09 18:16:37 -07:00
1e91d09be7 crdt.rs - panic cleanup 2018-05-09 18:10:48 -07:00
02c573986b historian / transaction updates 2018-05-09 17:22:14 -07:00
f2de486658 accountant.rs - panic cleanup 2018-05-09 17:19:12 -07:00
900b4f2644 Serialize entries over multiple blobs 2018-05-09 16:03:47 -07:00
1cfaa9afb6 Merge pull request #194 from garious/add-accounting-stage
Fix nightly
2018-05-09 16:53:45 -06:00
801468d70d Fix nightly 2018-05-09 16:51:34 -06:00
0601e05978 Merge pull request #192 from garious/add-accounting-stage
Add accounting stage
2018-05-09 16:47:50 -06:00
7ce11b5d1c Cleanup: use full words for field names
and optionally for variable names
2018-05-09 16:19:42 -06:00
f2d4799491 Cleanup: field names should be nouns 2018-05-09 16:14:40 -06:00
ebc458cd32 Remove redundant Arcs 2018-05-09 15:45:10 -06:00
43cd631579 Add thin_client_service 2018-05-09 14:56:34 -06:00
bc824c1a6c Reference count the accountant
So that the thin client can reference the AccountingStage's accountant
from separate threads.
2018-05-09 14:33:20 -06:00
4223aff840 Remove useless ref counts 2018-05-09 14:25:52 -06:00
f107c6c2ca Don't wrap thread-safe objects with mutexes 2018-05-09 14:21:42 -06:00
7daf14caa7 Don't depend on client from server 2018-05-09 13:33:33 -06:00
ded28c705f Tuck away the Historian
The Historian is now just a utility of the accounting stage.
2018-05-09 12:25:19 -06:00
778bec0777 Intercept historian output from accounting stage
We were accessing the accountant from multiple stages just to
register the ID the historian adds to Events.

This change should cause a whole lot of Arcs and Mutexes to go away.
2018-05-09 12:00:37 -06:00
6967cf7f86 Boot sync_channel()
This is less useful now that we send Vec<Event> instead of Event.
2018-05-09 11:43:16 -06:00
0ee3ec86bd Fix nightly 2018-05-09 10:48:56 -06:00
e4c47e8417 Use AccountingStage in Tpu 2018-05-09 10:31:23 -06:00
98ae80f4ed Hoist historian 2018-05-09 09:26:58 -06:00
876c77d0bc Extract accounting stage code from tpu 2018-05-09 09:22:46 -06:00
d44a6f7541 Move Accounting stage functionality into its own object 2018-05-09 09:03:00 -06:00
9040c04d27 Remove redundant Tick 2018-05-09 08:18:52 -06:00
ebbdef0538 Ignore flakey test 2018-05-09 08:16:59 -06:00
bfbee988d0 No longer wait for a Tick signal to record events 2018-05-09 08:15:51 -06:00
1d4d0272ca Drop support for logging a single event 2018-05-09 08:12:33 -06:00
77a76f0783 Record a batch of events 2018-05-09 08:11:19 -06:00
d9079de262 Add a way of sending a batch of events 2018-05-09 08:05:40 -06:00
b3d732a1a1 No longer artificially limit the size of entries
Instead, serialize the entries and split them up over multiple
blobs.
2018-05-09 07:59:55 -06:00
52f1a02938 Delete historical artifact
This was just to explain Proof of History. We have better explanations
elsewhere. Delete!
2018-05-09 07:53:24 -06:00
fe51669e85 signature.rs - panic cleanup 2018-05-08 23:21:45 -07:00
670a6c50c9 event.rs - panic cleanup 2018-05-08 22:58:48 -07:00
86c1aaf7d8 transaction.rs - panic cleanup 2018-05-08 22:46:22 -07:00
658e787b60 timing.rs panic cleanup 2018-05-08 22:40:07 -07:00
40c50aef50 deterministic random wallet generationg 2018-05-09 00:07:19 -04:00
a24c2bbe73 merge bug 2018-05-09 00:07:03 -04:00
bdbe90b891 Merge branch 'master' of github.com:solana-labs/solana 2018-05-08 23:40:54 -04:00
3236be7877 Merge pull request #188 from garious/add-tpu
AccountantSkel -> Tpu
2018-05-08 19:50:58 -06:00
1dca17fdb4 cargo +nightly fmt 2018-05-08 18:59:01 -06:00
785e971698 AccountantSkel -> Tpu
The terms Stub and Skel come from OMG IDL and only made sense while
the Stub was acting as an RPC client for the the Accountant object.
Nowadays, the Stub interface looks nothing like the Accountant and
meanwhile we've recognized the multithreaded implementation is more
reminiscent of a pipelined CPU. Thus, we finally bite the bullet and
rename our modules.

AccountantSkel -> Tpu
AccountantStub -> ThinClient

Up next will be moving much of the TPU code into separate modules,
each representing a stage of the pipeline. The interface of each
will follow the precedent set by the Historian object.
2018-05-08 17:40:02 -06:00
2bfa20ff85 Merge pull request #182 from garious/split-request
Control port prep
2018-05-08 17:11:34 -06:00
474a9af78d Merge pull request #187 from sakridge/fix_blob_size_check
Trust the recorder not to give us more than we can serialize
2018-05-08 17:11:18 -06:00
61425eacb8 Merge pull request #185 from sakridge/fix_default_client_port
Fix default client port, server uses 8000-8002 for gossip
2018-05-08 16:58:04 -06:00
4870def1fb Fix default client port, server uses 8000-8002 for gossip. 2018-05-08 15:40:55 -07:00
3e73fb9233 Trust the recorder not to give us more than we can serialize
Also run client for 10 seconds, 5 is bit too short
2018-05-08 15:23:41 -07:00
5ad6061c3f Merge pull request #184 from sakridge/add_debug_msg_in_readme
Add message about trace debugging
2018-05-08 14:39:09 -06:00
fae019b974 Add message about trace debugging 2018-05-08 13:26:09 -07:00
3bb06d8364 Merge pull request #183 from sakridge/verify_thread_rework
Rework sig processing threads and add perf for process/verify
2018-05-08 13:15:41 -06:00
c9c9afa472 Remove the note about git-lfs 2018-05-08 12:52:24 -06:00
bd0671e123 Rework sig processing threads and add perf for process/verify 2018-05-08 11:49:29 -07:00
6f3ec8d21f Merge pull request #181 from aeyakovenko/link
update link
2018-05-08 08:20:43 -06:00
9a0bf13feb update link 2018-05-08 06:44:24 -07:00
9ff1a6f0cd Add a thread to support thin clients 2018-05-07 21:44:44 -06:00
a59f64cae1 Merge pull request #179 from garious/update-readme
Update README with proposed way to download the gpu lib
2018-05-07 16:43:20 -06:00
a4ecd09723 Delete .gitattributes
This was used by git-lfs.
2018-05-07 16:35:54 -06:00
f159dfd15a Update README with proposed way to download the gpu lib
If you checked here yesterday, this was a top-level file in git-lfs,
but that made the developer workflow more painful so we boot that
file and are making it available via an http endpoint.
2018-05-07 16:33:27 -06:00
9e8ec86fa3 Merge pull request #178 from garious/split-request
Refactoring for upcoming thin client port
2018-05-07 16:21:48 -06:00
62bb78f58d Prepwork to hoist processing requests 2018-05-07 15:09:08 -06:00
893011c3ba Process events instead of processing only transactions
Prep work to allow clients to send any type that can end up in
the ledger.
2018-05-07 14:51:13 -06:00
880cb8e7cc Merge pull request #176 from aeyakovenko/multinode
Multinode
2018-05-07 09:05:12 -06:00
85f83f2c74 fmt 2018-05-06 22:29:33 -07:00
4751e459cc fixed! 2018-05-06 22:25:05 -07:00
138efa6cec fixed constant 2018-05-06 22:06:19 -07:00
a68e50935e useless timeouts i think 2018-05-06 21:48:46 -07:00
e8f5fb35ac Multinode fixes and test
* Replace magic numbers for 64k event size
* Fix gossip, dont ping yourself
* Retransmit only to listening nodes
* Multinode test in stub marked unstable
2018-05-06 21:36:06 -07:00
6af27669b0 Merge pull request #175 from garious/64k-entries
Limit 256 events per entry
2018-05-04 12:19:25 -07:00
e162f24119 Limit 256 events per entry
Attempt to keep blob size under 64kb
2018-05-04 11:52:05 -06:00
dbcc462a48 Merge pull request #173 from sakridge/entry_process_cleanup
Factor out entry processing and fix replicate test to call global setup fn
2018-05-04 11:19:28 -06:00
2d5313639a Factor out entry processing and fix replicate test to call global setup fn 2018-05-03 22:24:30 -07:00
38af0f436d Merge pull request #174 from sakridge/fix_bind_for_external
Fix bind so we can talk on external interfaces and surface send error
2018-05-03 18:20:00 -06:00
888c2ffb20 Fix bind so we can talk on external interfaces and surface send error 2018-05-03 17:05:02 -07:00
588593f619 Merge pull request #172 from sakridge/fix_entry_serialize
Fix entry serialize
2018-05-03 16:12:42 -06:00
2cdd515b12 Compiles/fmt and add assert for forward progress 2018-05-03 14:58:08 -07:00
0aad71d46e fix entry serialize 2018-05-03 14:35:04 -07:00
6f9285322d Merge pull request #171 from garious/cleanup-lastid
Cleanup last_id access in stub and skel
2018-05-03 14:57:28 -06:00
68c7f992fa Sooth all versions of rustfmt 2018-05-03 13:56:10 -06:00
1feff408ff Implement get_last_id() with transaction_count()
This is more precice than the previous implementation because it'll
drain the EntryInfo queue and return the most recent last_id instead
of the first one.
2018-05-03 13:34:57 -06:00
f752e02487 Implement GetLastId with EntryInfo subscription 2018-05-03 13:31:43 -06:00
c9c7fb0a27 Update comment
The last PR added a thread that logs entries without needing to
be driven by the client.
2018-05-03 13:27:37 -06:00
de680c2a8e Remove duplicate state 2018-05-03 13:24:37 -06:00
03695ba4c5 Merge pull request #169 from sakridge/broadcast_rebase
Add broadcast impl
2018-05-03 12:22:34 -06:00
c2e2960bf7 Add broadcast impl 2018-05-03 10:34:01 -07:00
385d2a580c Merge pull request #168 from aeyakovenko/fix_multi_host_client_demo
multi host client demo
2018-05-03 10:21:41 -06:00
7e02652068 Merge pull request #170 from garious/refactor-historian
Fix nightly build
2018-05-03 10:16:05 -06:00
ae29c9b4a0 Fix nightly build 2018-05-03 09:38:59 -06:00
078f917e61 useless assert 2018-05-03 08:34:57 -07:00
b65f04d500 multi host client demo
Bind to the same interface as the user supplied client address.
2018-05-03 08:28:11 -07:00
6acaffe581 Merge pull request #166 from garious/refactor-historian
TPU-friendly Historian
2018-05-02 18:13:30 -06:00
e47ef42a33 Merge pull request #167 from djKooks/readme-version
Add comment about rustc version in README
2018-05-02 18:08:13 -06:00
b950e33d81 Remove useless comment 2018-05-03 09:06:41 +09:00
ec8cfc77ad Remove component adding part 2018-05-03 09:04:56 +09:00
00a16db9cd Add comment about rustc version in README 2018-05-03 08:38:09 +09:00
4b9f115586 Hoist Historian input 2018-05-02 16:35:37 -06:00
c5cc91443e Rename sender/receiver to input/output 2018-05-02 15:54:53 -06:00
48d94143e7 Fix CI 2018-05-02 11:05:11 -06:00
8174a05156 Merge pull request #165 from rlkelly/126__atomic_balances
126  atomic balances
2018-05-02 10:43:31 -06:00
63cf6363a2 more rustfmt 2018-05-02 12:24:25 -04:00
cc6de605ac rustfmt 2018-05-02 12:21:20 -04:00
d0151d2b79 restored original test logic 2018-05-02 12:07:42 -04:00
6b45d453b8 modified verification map 2018-05-02 10:44:41 -04:00
b992a84d67 modified verification to loop until success or failure 2018-05-02 10:15:08 -04:00
cb362e9052 rust format 2018-05-01 16:38:15 -04:00
ccb478c1f6 improved error handling and atomic transactions 2018-05-01 16:38:15 -04:00
6af3680f99 Version bump 2018-04-30 22:38:39 -06:00
e6c3c215ab Add note about installing git-lfs 2018-04-30 15:26:31 -06:00
5c66bbde01 Add a note about running with GPU optimizations 2018-04-30 15:20:39 -06:00
77dd1bdd4a move CI specific scripts to solana-labs/buildkite repo 2018-04-29 23:43:43 -07:00
6268d540a8 move CI specific scripts to solana-labs/buildkite repo
Former-commit-id: 77dd1bdd4a
2018-04-29 23:43:43 -07:00
5918e38747 Version bump 2018-04-27 15:49:48 -07:00
3cfb571356 Version bump
Former-commit-id: f7385e866207b3ec2269bac36d52ef1e7f09337c
2018-04-27 15:49:48 -07:00
5eb80f8027 Add GPU library for Linux systems
To get solana to use the GPU, invoke cargo with "--features=cuda".
2018-04-27 15:47:22 -07:00
f6e5f2439d Add GPU library for Linux systems
To get solana to use the GPU, invoke cargo with "--features=cuda".


Former-commit-id: ea904df6e53d98a32e3f6103ee82cdf7ba08bf21
2018-04-27 15:47:22 -07:00
edf6272374 Merge pull request #154 from sakridge/replicator
Replicator
2018-04-27 14:30:52 -06:00
7f6a4b0ce3 Deserialize the Entry structs and process them 2018-04-27 13:15:19 -07:00
3be5f25f2f Work on test_replicate to test replicate service
generate some messages to send to replicator service
2018-04-27 08:21:34 -07:00
1b6cdd5637 Fix some compilation issues 2018-04-27 08:21:34 -07:00
f752e55929 update 2018-04-27 08:21:34 -07:00
ebb089b3f1 wip 2018-04-27 08:21:34 -07:00
ad6303f031 docs 2018-04-27 08:21:34 -07:00
828b9d6717 docs 2018-04-27 08:21:34 -07:00
444adcd1ca update 2018-04-27 08:21:34 -07:00
69ac305883 wip 2018-04-27 08:21:34 -07:00
2ff57df2a0 state replication 2018-04-27 08:21:34 -07:00
7077f4cbe2 Merge pull request #128 from garious/faster-demo
Utilize parallelized accountant in demo
2018-04-27 08:47:42 -06:00
266f85f607 Merge pull request #152 from aeyakovenko/star
recover full network from a star
2018-04-26 15:36:08 -06:00
d90ab90145 bind to all 2018-04-26 13:54:29 -07:00
48018b3f5b docs 2018-04-26 13:50:57 -07:00
15584e7062 recover full network from a star 2018-04-26 13:48:42 -07:00
d415b17146 sleepless demo to complement sleepless nights
18 ktps on macbook pro, no gpu
2018-04-26 13:17:38 -06:00
9ed953e8c3 Fix rebase fails 2018-04-26 09:35:10 -06:00
b60a98bd6e Startup log can reference IDs without itself 2018-04-26 08:42:34 -06:00
a15e30d4b3 Report transactions processed 2018-04-26 08:42:34 -06:00
d5d133353f Port blocking stub functions to new stateful ones 2018-04-26 08:42:34 -06:00
6badc98510 Add low-level response-handling functions to skel 2018-04-26 08:42:34 -06:00
ea8bfb46ce Add a way to subscribe for new entry metadata 2018-04-26 08:42:34 -06:00
58860ed19f WIP: New demo that makes better use of the parallelized accountant 2018-04-26 08:42:34 -06:00
583f652197 Generate genesis log for the demo
This log contains a bunch of transactions that generate new
accounts, so that transactions to and from them can be processed
in parallel.
2018-04-26 08:42:34 -06:00
3215dcff78 Update readme for new demo
Need to create a bunch of unrelated accounts to the genesis block
so that transactions can be processed in parallel without waiting
on write-locks. And then stuff the private keys of those accounts
into mint.json so that the client-demo can send the tokens from
those accounts.
2018-04-26 08:42:34 -06:00
38fdd17067 Add initializing log message to server
Handy when gesesis block is large.
2018-04-26 08:42:34 -06:00
807ccd15ba Add solana-mint-demo CLI
This extends solana-mint with additional data that will be used by
both solana-client-demo and creating the demo's genesis block.
2018-04-26 08:42:34 -06:00
1c923d2f9e Fix entry hash when no events and num_hashes is one 2018-04-26 08:42:34 -06:00
2676b21400 Merge pull request #151 from rlkelly/139__forget_signature
added forget_signature method
2018-04-26 08:28:11 -06:00
fd5ef94b5a added forget signature method 2018-04-26 07:22:11 -04:00
02c7eea236 Merge pull request #150 from garious/141__add_futures
Add FutureResult to return a Future that immediately resolves
2018-04-25 20:44:40 -06:00
34d1805b54 Add FutureResult to return a Future that immediately resolves 2018-04-25 19:23:24 -07:00
753eaa8266 buildkite script 2018-04-24 11:32:00 -07:00
0b39c6f98e Merge pull request #145 from aeyakovenko/crdt
initial crdt implementation
2018-04-24 10:49:23 -07:00
55b8d0db4d cleanup 2018-04-23 23:33:21 -07:00
3d7969d8a2 initial crdt implementation 2018-04-23 23:06:28 -07:00
041de8082a Merge pull request #144 from rleungx/improve-error-messages
improve the error messages
2018-04-21 08:17:25 -06:00
3da1fa4d88 improve the error messages 2018-04-21 21:52:55 +08:00
39df21de30 Merge pull request #142 from ansrivas/master
git clone instruction
2018-04-20 16:05:21 -06:00
8cbb7d7362 git clone instruction 2018-04-20 23:02:10 +02:00
10a0c47210 Merge pull request #137 from garious/linux-hang
Workaround linux hang
2018-04-19 11:46:48 -06:00
89bf3765f3 Merge pull request #138 from sakridge/help_options
Add -h/--help options for client-demo and testnode
2018-04-19 11:40:07 -06:00
8181bc591b Add -h/--help options for client-demo and testnode 2018-04-19 10:22:31 -07:00
ca877e689c Merge pull request #136 from rleungx/report-errors-to-stderr
report serde parse errors to stderr
2018-04-19 11:15:57 -06:00
c6048e2bab Workaround linux hang
Without this patch, Linux systems would hang when running the demo.

The root cause (why Linux is acting differently than macOS) was
not determined, but we know the problem is caused by a known
issue in the transaction pipeline - that entries are not pulled
off the historian channel until after the full transaction batch
is processed. This patch makes the sync_channel large enough that
it should never block on a gigabit network.
2018-04-19 10:04:32 -07:00
60015aee04 report serde parse errors to stderr 2018-04-19 23:51:57 +08:00
43e6741071 Merge pull request #134 from rleungx/report-parse-errors-to-stderr
report parse errors to stderr
2018-04-19 08:38:38 -06:00
b91f6bcbff report parse errors to stderr 2018-04-19 22:24:46 +08:00
64e2f1b949 Merge pull request #133 from djKooks/rm-mut
Remove out for immutable variable
2018-04-19 08:24:32 -06:00
13a2f05776 Remove out for immutable variable 2018-04-19 23:00:16 +09:00
903374ae9b Merge pull request #132 from aeyakovenko/readme
readme and site update
2018-04-18 21:49:09 -06:00
d366a07403 add gregs abstract as an intro 2018-04-18 20:17:37 -07:00
e94921174a Merge pull request #131 from sakridge/erasure
Add erasure rust logic under feature flag
2018-04-18 21:10:06 -06:00
dea5ab2f79 Add erasure rust logic under feature flag 2018-04-18 19:42:09 -07:00
5e11078f34 Add Stephen 2018-04-18 17:22:58 -06:00
d7670cd4ff Merge pull request #129 from aeyakovenko/retransmit
Retransmit
2018-04-18 11:10:50 -04:00
29f3230089 docs 2018-04-17 19:53:18 -07:00
d003efb522 fix docs 2018-04-17 19:52:46 -07:00
97e772e87a docs 2018-04-17 19:46:50 -07:00
0b33615979 udpate 2018-04-17 12:48:06 -07:00
249cead13e docs 2018-04-17 11:07:43 -07:00
7c96dea359 fmt 2018-04-17 11:05:35 -07:00
374c9921fd comments 2018-04-17 11:05:15 -07:00
fb55ab8c33 format 2018-04-16 21:02:37 -07:00
13485074ac test cast 2018-04-16 20:57:15 -07:00
4944c965e4 update
heap

update

update

wip

use a vec and sort

builds

update

tests

update

fmt

update

progress

fmt

passes needs retransmit test

tests

cleanup

update

update

update

update

fmt
2018-04-16 20:33:09 -07:00
83c5b3bc38 Merge pull request #125 from garious/fix-parallelized-ledger
Tell verifiers when not to parallelize accounting
2018-04-13 08:43:36 -06:00
7fc42de758 Fix bench 2018-04-13 00:36:23 -04:00
0a30bd74c1 Tell verifiers when not to parallelize accounting
Without this patch, many batches of transactions could be tossed
into a single entry, but the parallelized accountant can only
guarentee the transactions in the batch can be processed in
parallel.

This patch signals the historian to generate a new Entry after
each batch. Validators must maintain sequential consistency
across Entries.
2018-04-12 21:08:53 -06:00
9b12a79c8d cargo +nightly fmt 2018-04-12 17:04:11 -06:00
0dcde23b05 Merge pull request #119 from sakridge/skel_verify_test
Add skel test which sends a bad transaction, verify it doesn't make it
2018-04-12 17:02:08 -06:00
8dc15b88eb Add skel test which sends a bad transaction, verify it doesn't make it 2018-04-12 15:01:59 -07:00
d20c952f92 Merge pull request #121 from aeyakovenko/helpers
requests to packets utility function
2018-04-12 12:58:30 -07:00
c2eeeb27fd bump timer 2018-04-12 11:12:10 -07:00
180d8b67e4 requests to packets function 2018-04-12 10:44:09 -07:00
9c989c46ee Merge pull request #123 from garious/cleanup-tests
Cleanup tests
2018-04-11 22:37:35 -06:00
51633f509d Fix test
The test was meant to ensure the signature covered the 'tokens'
field, but then when the 'plan' field was rolled in, Transaction::verify()
started failing because Plan::verify() failed. When Transaction::verify()
was split into two, the unexpected failure was exposed but went unnoticed.
This patch brings it back to its original intent, to ensure signature
verification fails if the network attempts to change the client's payment.
2018-04-11 22:17:21 -06:00
705228ecc2 Remove redundant signs 2018-04-11 22:17:21 -06:00
740f6d2258 Merge pull request #122 from garious/fix-ci
Fix the nightly build
2018-04-11 20:56:58 -06:00
3b9ef5ccab Fix the nightly build 2018-04-11 20:24:14 -06:00
ab74e7f24f Merge pull request #117 from garious/parallelize-accountant
Enable parallelized accountant
2018-04-11 19:39:45 -06:00
be9a670fb7 Add process_packets() benchmark 2018-04-11 18:02:45 -06:00
6e43e7a146 Enable parallelized accountant 2018-04-11 18:01:59 -06:00
ab2093926a Merge pull request #120 from aeyakovenko/fix_bench_compile
fix compile error
2018-04-11 18:01:13 -06:00
916b90f415 Merge pull request #118 from sakridge/ecdsa_tests
Add tests for ecdsa sig checking
2018-04-11 17:58:45 -06:00
2ef3db9fab fix compile error 2018-04-11 15:40:25 -07:00
6987b6fd58 Add tests for ecdsa sig checking 2018-04-11 12:29:44 -07:00
078179e9b8 Merge pull request #115 from garious/parallelize-accountant
More refactoring
2018-04-11 10:28:15 -06:00
50ccecdff5 Refactor 2018-04-11 09:02:33 -06:00
e838a8c28a Delete unused function 2018-04-10 21:56:13 -06:00
e5f7eeedbf Use iterators 2018-04-10 21:48:26 -06:00
d1948b5a00 Zip earlier
And remove redundant into_iter() calls.
2018-04-10 21:18:39 -06:00
c07f700c53 Merge pull request #113 from aeyakovenko/master_pclient
command-line options for testnode and client
2018-04-09 23:07:03 -06:00
c934a30f66 commandline options for client and testnode 2018-04-09 21:14:52 -07:00
310d01d8a2 Merge pull request #112 from aeyakovenko/recycler_test
Recycler test should verifyt that its recycling
2018-04-07 09:29:50 -06:00
f330739bc7 Recycler test should verifyt that its recycling 2018-04-07 07:08:42 -07:00
58626721ad Merge pull request #111 from garious/parallelize-accountant
Cleanup
2018-04-06 17:03:10 -06:00
584c8c07b8 Better symmetry
deserialize -> process -> serialize
2018-04-06 16:34:59 -06:00
a93ec03d2c Move creating blobs into its own function 2018-04-06 16:22:02 -06:00
7bd3a8e004 Reduce cyclomatic complexity 2018-04-06 16:12:13 -06:00
912a5f951e Why is msgs cloned here? 2018-04-06 15:58:11 -06:00
6869089111 Parallelize deserialize 2018-04-06 15:52:58 -06:00
6fd32fe850 Cleanup constants 2018-04-06 15:43:05 -06:00
81e2b36d38 Cleanup packet_verify 2018-04-06 15:24:15 -06:00
7d811afab1 Parallelize CPU sig verify 2018-04-06 15:21:49 -06:00
39f5aaab8b Merge pull request #110 from garious/parallelize-accountant
Parallel processing of arbitrary transactions
2018-04-06 09:02:36 -06:00
5fc81dd6c8 Fix the nightly build
Nightly uses a different (but backward compatible) version of rustfmt.
2018-04-05 22:39:29 -06:00
491a530d90 Support parallelization of arbitrary transactions
Still assumes witnesses are processed serially afterward.
2018-04-05 22:30:25 -06:00
c12da50f9b Fix race condition
Without this patch, it was possible for two transactions with the same
'from' address to drive its balance below zero. With the patch, we'll
hold a write lock from just before we verify sufficient funds until
after those funds are deducted from the account.
2018-04-05 22:30:25 -06:00
41e8500fc5 Break up process_verified_transaction() 2018-04-05 22:29:13 -06:00
a7f59ef3c1 Merge pull request #109 from sakridge/wip_gpu
Change for cuda verify integration
2018-04-05 22:24:35 -06:00
f4466c8c0a Change for cuda verify integration 2018-04-05 20:00:44 -07:00
bc6d6b20fa Merge pull request #108 from garious/parallelize-accountant
Reject old transactions so that we can boot old signatures
2018-04-05 15:11:22 -06:00
01326936e6 Expire all transactions after some amount of time
Reject old transactions so that we can calculate an upper bound
for memory usage, and therefore ensure the server won't slow
down over time to crash due to memory exhaustion.
2018-04-05 10:26:45 -06:00
c960e8d351 Reject transactions with a last_id that isn't from this ledger
Before this patch, a client could put any value into `last_id` and
was primarily there to ensure the transaction had a globally unique
signature. With this patch, the server can use `last_id` as an
indicator of how long its been since the transaction was created.
The server may choose to reject sufficiently old transactions so
that it can forget about old signatures.
2018-04-05 09:54:03 -06:00
fc69d31914 Merge pull request #106 from garious/parallelize-accountant
Parallelize accountant
2018-04-04 22:42:28 -06:00
8d425e127b Update benchmark to avoid write locks in sig duplicate detection 2018-04-04 17:29:22 -06:00
3cfb07ea38 Sort signatures by last_id
This will allow for additional concurrency as well as give the server
a means of garbage-collecting old signatures.
2018-04-04 17:06:31 -06:00
76679ffb92 Per-cell locking
This allows us to use read-locks for balances most of the time. We
only lock the full table if we need to add one.
2018-04-04 16:31:13 -06:00
dc2ec925d7 Better test 2018-04-04 16:01:43 -06:00
81d6ba3ec5 Merge pull request #105 from garious/coverage-comments
Add the 'why' for code coverage to readme
2018-04-04 14:34:26 -07:00
014bdaa355 Add benchmark for parallel transaction processing 2018-04-04 12:43:27 -06:00
0c60fdd2ce Make accountant thread-safe
Before this change, parallel transaction processing required locking
the full accountant. Since we only call one method,
process_verified_transaction, the global lock equates to doing no
parallelization at all.  With this change, we only lock the data that's
being written to.
2018-04-04 12:33:03 -06:00
43d986d14e Add the 'why' for code coverage to readme 2018-04-04 09:26:38 -06:00
123d7c6a37 Merge pull request #99 from aeyakovenko/subscribers
Blobs and windows
2018-04-03 17:12:53 -06:00
5ac7df17f9 Implement window service
Batch out of order blobs until we have a contigious window.
2018-04-03 13:53:19 -07:00
bc0dde696a Merge pull request #102 from garious/rollback
Fix clippy warnings
2018-04-03 10:08:42 -06:00
c323bd3c87 Fix clippy warnings 2018-04-03 09:55:33 -06:00
5c672adc21 Merge pull request #101 from garious/rollback
Move tests
2018-04-02 21:58:10 -06:00
2f80747dc7 Move tests
After we restructured for parallel verification, the tests here
were unreferenced by the accountant, but still meaningful to
transaction verification.
2018-04-02 21:45:21 -06:00
95749ed0e3 Merge pull request #100 from garious/rollback
Cleanup use of event signatures and entry hashing
2018-04-02 21:17:37 -06:00
94eea3abec fmt 2018-04-02 21:15:21 -06:00
fe32159673 Add a test to ensure witness data continues to be hashed 2018-04-02 21:07:38 -06:00
07aa2e1260 Add witness data to entry hash
Otherwise, witnesses can be dropped or reordered by a malicious
generator.
2018-04-02 20:47:51 -06:00
6fec8fad57 Adding from to the signature is redundant 2018-04-02 20:34:18 -06:00
84df487f7d Merge pull request #97 from garious/rollback
Refactoring for rollback
2018-04-02 15:41:33 -06:00
49708e92d3 Use last_id instead of seed
It doesn't really matter, but was confusing since the seed points
to an entry before the mint's deposit.
2018-04-02 15:06:42 -06:00
daadae7987 Move replaying ledger out of accountant 2018-04-02 14:51:55 -06:00
2b788d06b7 Move the historian up to accountant_skel 2018-04-02 14:41:07 -06:00
90cd9bd533 Move balance check so that log_* methods are only used to add logging 2018-04-02 14:14:49 -06:00
d63506f98c No longer allow deposits outside the constructor 2018-04-02 14:00:42 -06:00
17de6876bb Add simpler accountant constructor 2018-04-02 13:51:44 -06:00
fc540395f9 Update docs 2018-04-02 11:51:56 -06:00
da2b4962a9 Move verify_slice() into a trait 2018-04-02 11:43:38 -06:00
3abe305a21 Move reserve_signatures into accountant
Reasons Transaction signatures need to be unique:

1. guard against duplicates
2. accountant uses them as IDs to link Witness signatures to transactions via the
`pending` hash map
2018-04-02 09:38:36 -06:00
46e8c09bd8 Revoke API access to first_id 2018-04-02 09:30:10 -06:00
e683c34a89 Version bump 2018-03-31 14:44:43 -06:00
54e4f75081 Merge pull request #95 from jackson-sandland/source-documentation-review
94: source doc review
2018-03-30 14:50:51 -06:00
9f256f0929 94 - snakecase mod names 2018-03-30 13:10:27 -07:00
ef169a6652 94: source doc review 2018-03-30 10:43:38 -07:00
eaec25f940 Version bump 2018-03-29 15:05:38 -06:00
6a87d8975c Merge pull request #93 from garious/par-req-processing
Better benchmark, fix logging
2018-03-29 14:02:40 -06:00
b8cf5f9427 Fix transaction logging 2018-03-29 13:50:32 -06:00
2f1e585446 Better benchmark
Tolerates dropped UDP packets
2018-03-29 13:41:11 -06:00
f9309b46aa Merge pull request #92 from garious/par-req-processing
Parallel request verification
2018-03-29 13:28:21 -06:00
22f5985f1b Do request verification in parallel, and then process the verified requests 2018-03-29 13:18:08 -06:00
c59c38e50e Refactor for batch verification 2018-03-29 13:09:21 -06:00
232e1bb8a3 Colocate packet dependencies 2018-03-29 12:55:41 -06:00
1fbb34620c Fix compiler warning 2018-03-29 12:54:10 -06:00
89f5b803c9 Merge pull request #91 from garious/more-docs
Add more documentation
2018-03-29 12:39:03 -06:00
55179101cd Add more documentation 2018-03-29 12:20:54 -06:00
132495b1fc A simple consensus diagram to guide rollback/coalescing
Diagram for what's described in #84 for rollback support.
2018-03-29 10:52:02 -06:00
a03d7bf5cd Missed a couple 2018-03-28 22:20:31 -06:00
3bf225e85f Don't require install to run demo 2018-03-28 22:18:33 -06:00
cc2bb290c4 Merge pull request #89 from garious/sig-verify-bench
Add microbenchmark for signature verification
2018-03-28 22:15:10 -06:00
878ca8c5c5 Add microbenchmark for signature verification 2018-03-28 22:02:47 -06:00
4bc41d81ee Fix compiler warning 2018-03-28 21:05:21 -06:00
f6ca176fc8 Merge pull request #88 from garious/revert-tcp-client
Revert TCP sync of ledger
2018-03-28 20:28:05 -06:00
0bec360a31 Revert TCP sync of ledger
The feature was too rushed. We technically don't need it until we
implement consensus. It'll come back another day (with many more tests!)
2018-03-28 20:16:15 -06:00
04f30710c5 Merge pull request #87 from garious/tcp-client
tx confirmed/sec ---> tx processed/sec
2018-03-28 17:04:36 -06:00
98c0a2af87 tx confirmed/sec ---> tx processed/sec
Before this patch, we were waiting until the full log was
sent back across the wire, parsed, and interpreted. That was giving
us a metric of "transactions confirmed per second" instead of
"transactions processed per second". Instead, we'll just send one
tiny packet back with the balance. As soon as the balance is what
we expect it to be, we end the benchmark.
2018-03-28 16:51:21 -06:00
9db42c1769 Merge pull request #86 from garious/tcp-client
Fix up client demo
2018-03-28 14:57:09 -06:00
849bced602 Fix up client demo 2018-03-28 14:40:58 -06:00
27f29019ef Merge pull request #83 from garious/tcp-client
TCP subscription service
2018-03-28 13:19:38 -06:00
8642a41f2b See if Travis will tolerate executing some of the test 2018-03-28 10:25:16 -06:00
bf902ef5bc Ignore accountant_stub test
TODO: Figure out why this test fails on TravisCI
2018-03-28 10:05:00 -06:00
7656b55c22 nit 2018-03-27 17:22:31 -06:00
7d3d4b9443 nit 2018-03-27 17:20:23 -06:00
15c093c5e2 typo 2018-03-27 16:31:19 -06:00
116166f62d Rename project: silk -> solana 2018-03-27 16:25:12 -06:00
26b19dde75 Rename project: silk -> solana 2018-03-27 16:19:28 -06:00
c8ddc68f13 Rename project: silk -> solana 2018-03-27 16:16:27 -06:00
7c9681007c Drop support for random access to the ledger
No longer store the ledger locally.
2018-03-27 14:47:03 -06:00
13206e4976 Let clients subscribe to the ledger over TCP
TODO: Add more tests

Fixes #27
2018-03-27 14:46:24 -06:00
2f18302d32 Merge pull request #80 from garious/fix-ci
Fix CI
2018-03-26 22:13:11 -06:00
ddb21d151d Nightly rustfmt
Format code with the nightly version of rustfmt, which sorts imports.
2018-03-26 22:03:28 -06:00
c64a9fb456 Give Travis a little more time to start threads 2018-03-26 22:02:05 -06:00
ee19b4f86e See if CI hangs because of wait_on_signature() 2018-03-26 21:53:30 -06:00
14239e584f fix writer 2018-03-26 21:36:29 -06:00
112aecf6eb Merge pull request #77 from aeyakovenko/responder
Responder
2018-03-25 17:01:53 -07:00
c1783d77d7 fixed test 2018-03-25 16:18:27 -07:00
f089abb3c5 fix bench 2018-03-25 15:37:00 -07:00
8e551f5e32 debug trait tests 2018-03-25 08:22:04 -07:00
290960c3b5 wip 2018-03-25 08:06:33 -07:00
62af09adbe wip 2018-03-25 08:05:03 -07:00
e39c0b34e5 update 2018-03-25 00:06:48 -07:00
8ad90807ee responder with larger block size 2018-03-24 23:46:25 -07:00
533b3170a7 responder 2018-03-24 23:31:54 -07:00
7732f3f5fb services 2018-03-24 18:01:54 -07:00
f52f02a434 services 2018-03-24 18:01:40 -07:00
4d7d4d673e Merge pull request #75 from garious/fix-testnode
Revive silk-testnode
2018-03-23 22:16:59 -06:00
9a437f0d38 Revive silk-testnode 2018-03-23 21:49:28 -06:00
c385f8bb6e Merge pull request #73 from garious/yes-clippy
Automated mentoring by clippy
2018-03-22 15:22:12 -06:00
fa44be2a9d Ignore some clippy advice 2018-03-22 14:59:25 -06:00
117ab0c141 Clippy review 2018-03-22 14:50:24 -06:00
7488d19ae6 Clippy review 2018-03-22 14:40:28 -06:00
60524ad5f2 Clippy review 2018-03-22 14:38:06 -06:00
fad7ff8bf0 Clippy review 2018-03-22 14:31:58 -06:00
383d445ba1 Clippy review 2018-03-22 14:15:29 -06:00
803dcb0800 Mutex<bool> -> AtomicBool 2018-03-22 14:05:23 -06:00
fde320e2f2 Merge pull request #71 from garious/rework-recorder
Replicate the ledger
2018-03-21 17:23:55 -06:00
8ea97141ea Update the test to replicate the ledger 2018-03-21 17:15:32 -06:00
9f232bac58 Allow clients to sync the ledger
Fixes #4
2018-03-21 15:46:49 -06:00
8295cc11c0 Move JSON printing up the stack 2018-03-20 23:15:44 -06:00
70f80adb9a Merge pull request #70 from garious/planevent-to-witness
Cleanup
2018-03-20 19:13:02 -06:00
9a7cac1e07 Use the Entry API to remove the double lookup 2018-03-20 18:07:54 -06:00
c584a25ec9 Move complete_transaction from method to function
So that we can hold separate mutable references to the pending queue
and the map of balances.
2018-03-20 17:47:57 -06:00
bff32bf7bc Cleanup 2018-03-20 17:32:02 -06:00
d0e7450389 Add docs 2018-03-20 16:58:14 -06:00
4da89ac8a9 Cleanup naming 2018-03-20 16:53:41 -06:00
f7032f7d9a Cleanup: replace bool retval with is_complete() method 2018-03-20 16:52:47 -06:00
7c7e3931a0 Better docs 2018-03-20 15:52:46 -06:00
6be3d62d89 Remove Action from spending plans 2018-03-20 15:43:07 -06:00
6f509a8a1e Reorder 2018-03-20 15:31:28 -06:00
4379fabf16 PlanEvent -> Witness
The term used by the Simplicity smart contract language
2018-03-20 15:25:50 -06:00
6b66e1a077 Merge pull request #69 from garious/move-streamer-benchmark
Move streamer benchmark out of unit tests
2018-03-19 17:33:45 -06:00
c11a3e0fdc Move streamer benchmark out of unit tests 2018-03-19 17:10:01 -06:00
3418033c55 Merge pull request #68 from garious/fix-bench
Fix bench
2018-03-19 16:52:41 -06:00
caa9a846ed Boot sha2-asm
Stick with pure Rust until someone can write a benchmark that
demonstrates that sha2-asm adds value. If we go with a GPU
implementation first, we may never need to do that.
2018-03-19 16:42:30 -06:00
8ee76bcea0 Fix benchmark build 2018-03-19 16:41:01 -06:00
47325cbe01 Merge pull request #67 from garious/cleanup-naming
Cleanup naming
2018-03-19 16:29:08 -06:00
e0c8417297 Apply renames to docs 2018-03-19 10:23:43 -06:00
9238ee9572 No longer rename log crate 2018-03-19 10:18:51 -06:00
64af37e0cd logger -> recorder
Free up namespace for a traditional runtime logger.
2018-03-19 10:16:21 -06:00
9f9b79f30b log -> ledger
Free up namespace for traditional runtime logs.
2018-03-19 10:09:19 -06:00
265f41887f asset -> tokens 2018-03-19 10:03:41 -06:00
4f09e5d04c Merge pull request #66 from garious/conditional-plan
Simplify contract language
2018-03-18 21:12:26 -06:00
434f321336 Add spending plan tests 2018-03-18 21:02:28 -06:00
f4e0d1be58 Make conditions explicit in races
And boot recursive spending plans. That path required heap allocations.
Since we don't have a need for this generality right now, reduce the
language to the smallest one that can pass our test suite.
2018-03-17 20:43:05 -06:00
e5bae0604b Specialize transaction assets to i64
Proof-of-history is generic, but now that we're using it entirely
for tokens, we can specialize the type and start doing more interesting
things than just Eq and Serialize operations.
2018-03-17 19:56:15 -06:00
e7da083c31 Move spending plans to their own crate 2018-03-17 19:56:15 -06:00
367c32dabe Guard spending plans, not just payments 2018-03-17 19:56:15 -06:00
e054238af6 Merge pull request #65 from aeyakovenko/fixtest
fix test
2018-03-14 12:21:08 -07:00
e8faf6d59a trait test 2018-03-14 11:28:05 -07:00
baa4ea3cd8 wfmt 2018-03-14 11:14:40 -07:00
75ef0f0329 fix test 2018-03-14 11:02:38 -07:00
65185c0011 Merge pull request #63 from aeyakovenko/streamer-integrated
Streamer integrated
2018-03-12 08:38:59 -06:00
eb94613d7d Use streaming socket interface within accountant
Pull messages from streamer process them and forward them to the sender.
2018-03-11 23:41:09 -05:00
67f4f4fb49 Merge pull request #64 from garious/dumb-contracts
Entry-level smart contracts
2018-03-11 13:23:11 -06:00
a7ecf4ac4c Merge pull request #57 from aeyakovenko/streamer
Streamer
2018-03-11 13:22:49 -06:00
45765b625a Don't let users accidentally burn their funds either 2018-03-11 12:04:49 -06:00
aa0a184ebe Ensure the server isn't passed a Plan that spends more than is bonded 2018-03-11 11:53:45 -06:00
069f9f0d5d add ipv6 flag to cargo.toml 2018-03-11 12:53:16 -05:00
c82b520ea8 remove unecessary returns 2018-03-11 11:45:17 -05:00
9d6e5bde4a ipv6 test with a separate flag 2018-03-11 11:22:21 -05:00
0eb3669fbf cleanup timestamp processing 2018-03-11 00:30:01 -07:00
30449b6054 cleanup sig processing 2018-03-11 00:11:08 -07:00
f5f71a19b8 First go at smart contracts
Needs lots of cleanup.
2018-03-10 22:00:48 -07:00
0135971769 Fast UdpSocket reader
* message needs to fit into 256 bytes
* allocator to keep track of blocks of messages
* udp socket receiver server that fills up the block as fast as possible
* udp socket sender server that sends out the block as fast as possible
2018-03-10 21:09:23 -06:00
8579795c40 Ensure transactions won't get canceled after next refactor 2018-03-10 19:44:45 -07:00
9d77fd7eec Store only spending plans, not full transactions 2018-03-10 18:35:10 -07:00
8c40d1bd72 Move spending endpoints into expressions 2018-03-10 17:41:18 -07:00
7a0bc7d888 Move smart contract fields into their own struct 2018-03-10 16:55:39 -07:00
1e07014f86 Merge pull request #62 from garious/batch-events
Batch events
2018-03-09 17:37:02 -07:00
49281b24e5 Move Tick out of Event
Every Entry is now a Tick and the entries contain events.
2018-03-09 17:22:17 -07:00
a8b1980de4 Restore reorder attack test 2018-03-09 17:02:17 -07:00
b8cd5f0482 Boot Cargo.lock from git
Only add Cargo.lock to downstream dependencies.
2018-03-09 16:26:26 -07:00
cc9f0788aa Batch events
It's now a Tick that locks down event order. Before this change, the
event order would be locked down in the order the server sees it.

Fixes #59
Fixes #61
2018-03-09 16:16:33 -07:00
209910299d Version bump
Next release probably won't have a compatible entry log with the
0.3.x line.
2018-03-09 14:33:37 -07:00
17926ff5d9 Merge pull request #58 from garious/deterministic-historian
Deterministic historian/accountant hashes
2018-03-09 07:06:40 -07:00
957fb0667c Deterministic historian/accountant hashes
When in tick-less mode, no longer continuously hash on the
background thread. That mode is just used for testing and
genesis log generation, and those extra hashes are just noise.

Note that without the extra hashes, with lose the duration between
events. Effectively, we distinguish proof-of-order from proof-of-time.
2018-03-09 06:58:40 -07:00
8d17aed785 Process timestamps as they are added 2018-03-08 15:39:03 -07:00
7ef8d5ddde Lock down dependencies 2018-03-08 13:25:40 -07:00
9930a2e167 With v0.3.1 published to crates.io, you can now run silk without git 2018-03-08 11:42:06 -07:00
a86be9ebf2 Merge pull request #56 from garious/add-conditions
Add conditions to transactions
2018-03-08 11:15:31 -07:00
ad6665c8b6 Complete timestamp and signature transactions 2018-03-08 11:06:52 -07:00
923162ae9d WIP: process timestamps 2018-03-08 10:19:54 -07:00
dd2bd67049 Add a barebones test for transaction conditions 2018-03-08 08:58:34 -07:00
d500bbff04 Add public key to mint
This turns the mint into a handy way to generate public keys
without throwing the private key away.
2018-03-08 08:33:00 -07:00
e759bd1a99 Add conditions to the signature to reject duplicates 2018-03-08 08:18:34 -07:00
94daf4cea4 Add Cancel and Timestamp events
Fixes #31, #34, #39
2018-03-08 08:17:34 -07:00
2379792e0a Add DateTime and Cancel conditions
Fixes #32, #33
2018-03-08 08:17:08 -07:00
dba6d7a8a6 Update README.md 2018-03-07 17:20:40 -07:00
086c206b76 Merge pull request #55 from garious/the-mint
More intuitive demo, introducing The Mint
2018-03-07 17:18:24 -07:00
5dd567deef Rename Genesis to Mint
Genesis is a story of creation. We should only use that term to
for the event log that bootstraps the system.
2018-03-07 17:08:15 -07:00
b6d8f737ca Introducing, the mint
Use the mint to pair a new private key with new tokens.
2018-03-07 16:58:04 -07:00
491ba9da84 Add accessors to keypairs and signatures 2018-03-07 15:32:22 -07:00
a420a9293f Fix demo 2018-03-07 11:37:30 -07:00
c1bc5f6a07 Merge pull request #54 from garious/imperative-genesis
Boot genesis block helper
2018-03-07 11:19:16 -07:00
9834c251d0 Boot genesis block helper
Before this change, if you wanted to use a new Transaction
feature in the genesis block, you'd need to extend its
Creator object and associated methods.  With yesterday's
addtions to Transcation, it's now so easy to work with
Transactions directly that we can get rid of the middleman.

Also added a KeyPair type alias, so that ring could be easily swapped
out with a competing library, if needed.
2018-03-07 11:10:15 -07:00
54340ed4c6 Delete debugging println
Thanks @jackson-sandland!
2018-03-06 21:17:41 -07:00
96a0a9202c Update README.md 2018-03-06 21:12:50 -07:00
a4c081d3a1 Merge pull request #53 from garious/monorphic-entry
Monomorphisize Entry and Event
2018-03-06 20:39:11 -07:00
d1b6206858 Monomorphisize Entry and Event
Transaction turned out to be the only struct worth making generic.
2018-03-06 20:29:18 -07:00
0eb6849fe3 Merge pull request #52 from garious/add-transaction-struct
Break dependency cycle
2018-03-06 17:53:48 -07:00
b725fdb093 Sha256Hash -> Hash
Because in Loom, there's just the one. Hopefully no worries that it
shares a name with std::Hash.
2018-03-06 17:40:01 -07:00
1436bb1ff2 Move entry into its own module
Hmm, Logger doesn't depend on log.
2018-03-06 17:40:01 -07:00
5a44c36b1f Move hash into its own module 2018-03-06 17:40:01 -07:00
5d990502cb Merge pull request #51 from jackson-sandland/50-proof-read-README
Issue #50 - proof read README
2018-03-06 17:39:33 -07:00
64735da716 Issue #50 - proof read README 2018-03-06 16:21:45 -08:00
95b82aa6dc Merge pull request #49 from garious/add-transaction-struct
DRY up transaction signing
2018-03-06 16:48:27 -07:00
f09952f3d7 DRY up transaction signing
Cleanup the big mess I copy-pasted myself into.
2018-03-06 16:34:25 -07:00
b98e04dc56 Update README.md 2018-03-06 15:03:06 -07:00
cb436250da Merge pull request #48 from garious/add-transaction-struct
data -> asset
2018-03-06 15:01:56 -07:00
4376032e3a data -> asset
'data' is too vague.
2018-03-06 14:50:32 -07:00
c231331e05 Merge pull request #47 from garious/add-transaction-struct
Reorg
2018-03-06 12:57:49 -07:00
624c151ca2 Add signature module
Because things other than transactions can be signed.
2018-03-06 12:48:28 -07:00
5d0356f74b Move verify_entry to a method as well 2018-03-06 12:35:12 -07:00
b019416518 Move verify into methods
A little overly-coupled to Serialize, but makes the code a lot tighter
2018-03-06 12:27:08 -07:00
4fcd9e3bd6 Give Transaction its own module 2018-03-06 12:18:17 -07:00
66bf889c39 Rename Transfer to Transaction
struct names should be nouns
2018-03-06 11:54:47 -07:00
a2811842c8 More cleanup
Far fewer branches when we process transfers outside the context
of events.
2018-03-06 11:43:55 -07:00
1929601425 Cleanup
Now that Transfer is out of the enum, we don't need to pattern
match to access its fields.
2018-03-06 11:19:59 -07:00
282afee47e Use Transfer struct on the client side too
Sharing is caring.
2018-03-06 11:03:43 -07:00
e701ccc949 Rename Request::Transfer to Request::Transaction 2018-03-06 10:59:47 -07:00
6543497c17 Move Transaction data into its own struct
This will allow us to add addition transfer types to the log.
2018-03-06 10:50:32 -07:00
7d9af5a937 Merge pull request #46 from garious/be-negative
Allow balances to be negative
2018-03-05 23:47:02 -07:00
720c54a5bb Allow balances to be negative
* Will allow owners to loan token to others.
* Will allow for parallel verification of balances without spilling
  over 64 bits.

Fixes #43
2018-03-05 17:30:53 -07:00
5dca3c41f2 Update README.md 2018-03-05 16:19:26 -07:00
929546f60b Update README.md 2018-03-05 16:18:46 -07:00
cb0ce9986c Merge pull request #45 from garious/init-from-log
Towards sending the log to clients
2018-03-05 16:17:41 -07:00
064eba00fd Update readme 2018-03-05 16:05:16 -07:00
a4336a39d6 Initialize the testnode from a log
$ cargo run --bin silk-genesis-file-demo > demo-genesis.json
$ cat demo-genesis.json | cargo run --bin silk-genesis-block > demo-genesis.log
$ cat demo-genesis.log | cargo run --bin silk-testnode
2018-03-05 15:34:44 -07:00
298989c4b9 Generate log from Genesis 2018-03-05 13:03:56 -07:00
48c28c2267 Transactions now require a hash of the last entry they've seen
This ensures the transaction cannot be processed on a chain
that forked before that ID. It will also provide a basis for
expiration constraints. A client may want their transaction
to expire, and the generators may want to reject transactions
that have been floating in the ether for years.
2018-03-05 12:48:14 -07:00
d76ecbc9c9 Don't block the server 2018-03-05 11:39:59 -07:00
79fb9c00aa Boot wait_on_signature() from accountant
Instead, there should be a way to query for a page of log data,
and checking whether it has a signature should be done client-side.
2018-03-05 10:45:18 -07:00
c9e03f37ce Logger now only speaks when spoken to
Before this change, the logger's send channel could quickly be
flooded with Tick events. Those events should only be passed to
a writer.

Also, the log_event() function no longer sends entries. That
functionality moved to the new process_events() function. This
will allow us to initialize the with the genesis block without
flooding the send channel with events the historian won't read.
2018-03-05 10:33:12 -07:00
aa5f1699a7 Update the set of unique signatures when loading an existing log. 2018-03-04 22:31:12 -07:00
e1e9126d03 Merge pull request #44 from garious/genesis
Finally, genesis block generation without channels
2018-03-04 14:39:28 -07:00
672a4b3723 Update historian diagram 2018-03-04 14:36:55 -07:00
955f76baab Finally, genesis block generation without channels 2018-03-04 14:32:30 -07:00
7da8a5e2d1 Merge pull request #42 from garious/genesis
Make num_hashes more intuitive
2018-03-04 13:05:38 -07:00
ff82fbf112 Make num_hashes mean the num_hashes since the last ID
Before this change, num_hashes meant the number of hashes since
the last ID, minus any hashing done on the event data. It made
no difference for Tick events, but logged Transaction events with
one less hash than actually occurred.
2018-03-04 09:52:36 -07:00
8503a0a58f Refactor 2018-03-04 09:21:45 -07:00
b1e9512f44 Rename end_hash to id 2018-03-04 07:50:26 -07:00
608def9c78 Consolidate imports 2018-03-04 07:28:51 -07:00
bcb21bc1d8 Delete dead code 2018-03-04 07:20:17 -07:00
f63096620a Merge pull request #41 from garious/genesis
Add command-line tool for generating a genesis block
2018-03-04 01:27:59 -07:00
9b26892bae Add a demo app to generate the genesis file 2018-03-04 01:21:40 -07:00
572475ce14 Load the genesis block 2018-03-04 00:15:17 -07:00
876d7995e1 Refactor to support loading an existing ledger 2018-03-03 22:25:40 -07:00
b8655e30d4 Make client-demo standalone
And remove deposit() methods from the API. Those should only be
used on the server to bootstrap.
2018-03-03 21:15:51 -07:00
7cf0d55546 Remove optional 'from' field 2018-03-03 20:41:07 -07:00
ce60b960c0 Special case sending money to self
In the genesis block, let matching 'from' and 'to' keys be used
to mint new coin.
2018-03-03 20:27:12 -07:00
cebcb5b92d Start genesis with a Tick, so that its hash can be used to bootstrap verification 2018-03-03 19:57:22 -07:00
11a0f96f5e Add command-line tool for generating a genesis block 2018-03-03 17:35:05 -07:00
74ebaf1744 Merge pull request #40 from garious/add-logger
Add logger
2018-03-03 14:37:15 -07:00
f7496ea6d1 Make create_logger a static method
Allows us to share the super long type signature in impl.
2018-03-03 14:26:59 -07:00
bebba7dc1f Give logger its own crate 2018-03-03 14:24:32 -07:00
afb2bf442c Use Instant instead of SystemTime for more precise ticking
And convert log_event from function to method
2018-03-03 14:08:53 -07:00
c7de48c982 Convert log_events from function to method 2018-03-03 14:00:37 -07:00
f906112c03 Move logging thread's state into a struct 2018-03-03 13:52:57 -07:00
8ef864fb39 Merge pull request #37 from garious/split-benchmark
Split benchmark
2018-03-03 12:13:54 -07:00
1c9b5ab53c Report performance of signature verification too 2018-03-03 11:59:34 -07:00
c10faae3b5 More readable metrics 2018-03-03 11:52:50 -07:00
2104dd5a0a Fix benchmark
Was measuring the creation of the iterator, not running it.
2018-03-03 11:45:23 -07:00
fbe64037db Merge pull request #35 from garious/split-benchmark
Move key generation and signing from transaction benchmark
2018-03-03 11:25:58 -07:00
d8c50b150c Move key generation and signing from transaction benchmark
Key generation, signing and verification are not the performance
bottleneck. Something is probably wrong here.
2018-03-03 11:11:46 -07:00
8871bb2d8e Merge pull request #30 from garious/simplify
Unify Claim and Transaction handling
2018-03-02 12:24:44 -07:00
a148454376 Update readme 2018-03-02 12:07:05 -07:00
be518b569b Remove cyclic dependency between event and log 2018-03-02 12:03:59 -07:00
c998fbe2ae Sign the owner's public key
Without this, the accountant will reject transfers from different
entities if they are for the same amount and to the same entity.
2018-03-02 11:56:42 -07:00
9f12cd0c09 Purge the Claim event type
It's now represented as a Transaction from an unknown party.
2018-03-02 11:48:58 -07:00
0d0fee1ca1 Sign Claim's 'to' field
Otherwise, the accountant will treat deposits of the same amount as
duplicates.
2018-03-02 11:46:22 -07:00
a0410c4677 Pipe all Claim constructors through a function 2018-03-02 10:58:43 -07:00
8fe464cfa3 Rename Claim's key field to match same field in Transaction 2018-03-02 10:47:21 -07:00
3e2d6d9e8b Generalize Transaction to express a Claim
If a Transaction doesn't have an existing address, it's being used
to create new funds.
2018-03-02 10:41:15 -07:00
32d677787b Reduce transactions sent by demo
We don't do retries yet, so keep tx count to something that won't
trigger any packet loss.
2018-03-02 10:35:38 -07:00
dfd1c4eab3 Don't process transaction if channel.send() fails.
Do all input validation first, then log (which can fail). If all
goes swimmingly, process the transaction.
2018-03-02 10:17:52 -07:00
36bb1f989d More defense against a double-spend attack
Before this change, a client could spend funds before the accountant
processed a previous spend. With this change in place, the accountant
updates balances immediately, but that comes at an architectural cost.
The accountant now verifies signatures on behalf of the historian, so
that it can ensure logging will not fail.
2018-03-02 09:55:44 -07:00
684f4c59e0 Delete commented out code
accountant crate shouldn't verify the log. Instead, it should
only add valid entries and leave verification to network nodes.
2018-03-02 08:51:29 -07:00
1b77e8a69a Move Event into its own crate
The log crate was starting to be the catch-all for all things
related to entries, events, signatures, and hashes. This split
shows us that:

* Event depends only on signatures, not on hashes [directly]
* All event testing was done via log testing (shame on me)
* Accounting depends only on events
2018-03-02 08:43:57 -07:00
662e10c3e0 Merge pull request #29 from garious/simplify
Remove Discovery event
2018-03-01 18:53:25 -07:00
c935fdb12f Move signature duplicate detection into the historian 2018-03-01 17:44:10 -07:00
9e16937914 Delete the Discovery event
Not useful to the accountant.
2018-03-01 17:02:41 -07:00
f705202381 No need to hash data that's already hashed to create the signature 2018-03-01 16:39:09 -07:00
f5532ad9f7 Merge pull request #28 from garious/go-udp
Switch to UDP
2018-03-01 14:25:20 -07:00
570e71f050 Check for duplicate signatures
TODO: have client add recent hash to each message
2018-03-01 14:07:39 -07:00
c9cc4b4369 Switch to UDP from TCP
And remove all the sleep()'ing around.
2018-03-01 13:47:53 -07:00
7111aa3b18 Copy disclaimer from the loom repository
Per @aeyakovenko, added Loom's disclaimer.
2018-03-01 09:16:39 -07:00
12eba4bcc7 Merge pull request #26 from garious/add-accountant
Add testnode and client-demo
2018-02-28 19:48:05 -07:00
4610de8fdd Switch to sync_channel to preserve order 2018-02-28 19:33:28 -07:00
3fcc2dd944 Add testnode
Fixes #20
2018-02-28 18:05:20 -07:00
8299bae2d4 Add accountant stub 2018-02-28 16:01:12 -07:00
604ccf7552 Add network interface for accountant 2018-02-28 14:00:04 -07:00
f3dd47948a Merge pull request #25 from garious/verify-historian-input
Verify event signatures before adding log entries
2018-02-28 10:34:10 -07:00
c3bb207488 Verify event signatures before adding log entries 2018-02-28 10:23:01 -07:00
9009d1bfb3 Merge pull request #24 from garious/add-accountant
Add accountant
2018-02-27 11:41:40 -07:00
fa4d9e8bcb Add more tests 2018-02-27 11:28:10 -07:00
34b77efc87 Sleep longer for TravisCI 2018-02-27 11:08:28 -07:00
5ca0ccbcd2 Add accountant 2018-02-27 10:54:06 -07:00
6aa4e52480 Merge pull request #23 from garious/add-transaction
Generalize the event log
2018-02-26 17:40:55 -07:00
f98e9a2ad7 Fix overuse of search-and-replace 2018-02-26 17:03:50 -07:00
c6134cc25b Allow the historian to track ownership of any type of data 2018-02-26 17:01:22 -07:00
0443b39264 Allow event log to hold events of any serializable (hashable) type 2018-02-26 16:42:31 -07:00
8b0b8efbcb Allow Entry to hold events of any kind of data 2018-02-26 15:37:33 -07:00
97449cee43 Allow events to hold any kind of data 2018-02-26 15:31:01 -07:00
ab5252c750 Move entry verification out of Entry impl 2018-02-26 14:39:01 -07:00
05a27cb34d Merge pull request #22 from garious/add-transaction
Extend the event log with a Transaction event to transfer possession
2018-02-26 11:26:58 -07:00
b02eab57d2 Extend the event log with a Transaction event to transfer possession
This implementation assumes 'from' is the current owner of 'data'.
Once that's verified, the signature ensures that nobody modified
'data' (the asset being transferred) or 'to' the entity taking
ownership.

Fixes #14
2018-02-26 11:09:11 -07:00
b8d52cc3e4 Make the Discovery event into a struct instead of a tuple 2018-02-24 11:15:03 -07:00
7d9bab9508 Update rendered demo diagram 2018-02-24 11:09:00 -07:00
944181a30e Version bump 2018-02-24 11:06:08 -07:00
d8dd50505a Merge pull request #21 from garious/add-signatures
Add signatures
2018-02-24 10:47:25 -07:00
d78082f5e4 Test bad signature 2018-02-24 10:27:51 -07:00
08e501e57b Extend the event log with a Claim event to claim possession
Unlike a Discovery event, a Claim event associates a public key
with a hash. It's intended to to be used to claim ownership of
some hashable data. For example, a graphic designer could claim
copyright by hashing some image they created, signing it with
their private key, and publishing the hash-signature pair via
the historian. If someone else tries to claim it as their own,
the designer can point to the historian's log as cryptographically
secure evidence that the designer's copy existed before anyone
else's.

Note there's nothing here that verifies the first claim is the actual
content owner, only that the first claim almost certainly happened
before a second.
2018-02-24 10:09:49 -07:00
29a607427d Rename UserDataKey to Discovery
From the perspective of the log, when some data's hash is added,
that data is "discovered" by the historian.  Another event
might be a "claim" that some signed data belongs to the owner of a
public key.
2018-02-24 05:25:19 -07:00
afb830c91f Merge pull request #18 from garious/add-historian
self-ticking logger
2018-02-21 12:30:10 -07:00
c1326ac3d5 Up the time to sleep so that ticks are generated 2018-02-21 12:22:23 -07:00
513a1adf57 Version bump 2018-02-21 12:01:17 -07:00
7871b38c80 Update demo to use self-ticking logger 2018-02-21 11:52:03 -07:00
b34d2d7dee Allow the logger to inject Tick events on its own 2018-02-21 11:33:42 -07:00
d7dfa8c22d Readme cleanup 2018-02-21 10:07:32 -07:00
8df274f0af Add hash seed to verify_slice() 2018-02-21 09:43:34 -07:00
07c4ebb7f2 Add message sequence chart for readme demo
Fixes #17
2018-02-21 09:33:50 -07:00
49605b257d Merge pull request #16 from garious/add-serde
Add serialization/deseriation support to event log
2018-02-20 16:55:46 -07:00
fa4e232d73 Add serialization/deseriation support to event log
See bincode and serde_json for usage:
https://github.com/TyOverby/bincode

Fixes #1
2018-02-20 16:26:13 -07:00
bd84cf6586 Merge pull request #15 from garious/add-historian
Demo proof-of-history and reordering attack
2018-02-20 15:05:20 -07:00
6e37f70d55 Test reorder attack 2018-02-20 14:46:36 -07:00
d97112d7f0 Explain proof-of-history in the readme
Also:
* Hash userdata so that verification works as the readme describes.
* Drop itertools package. Found a way to use std::iter instead.

Fixes #8
2018-02-20 14:04:49 -07:00
e57bba17c1 Version bump 2018-02-19 16:59:41 -07:00
959da300cc Shorten readme lines 2018-02-19 16:53:58 -07:00
ba90e43f72 Update benchmark
* Add asm, though it doesn't make it faster. TODO: use avx instructions.
* Do 10x less hashes, since sha256 is more expensive.
2018-02-19 16:51:35 -07:00
6effd64ab0 Update readme with sha256 usage 2018-02-19 16:48:29 -07:00
e18da7c7c1 Merge pull request #13 from garious/sha256-hash
Use sha256 hashes instead of Rust's builtin hasher.
2018-02-19 16:43:26 -07:00
0297edaf1f Use sha256 hashes instead of Rust's builtin hasher.
Causes a 20x performance degradation. Enabling asm did not
speed things up.
2018-02-19 16:23:53 -07:00
b317d13b44 Add codecov configuration 2018-02-19 13:02:59 -07:00
bb22522e45 Remove assertions that fail in the kcov docker container 2018-02-19 12:54:01 -07:00
41053b6d0b Merge pull request #12 from garious/add-historian
Add historian demo
2018-02-19 12:40:44 -07:00
bd3fe5fac9 Sleep a little longer to ensure Travis context switches 2018-02-19 12:33:33 -07:00
10a70a238b Cleanup demo 2018-02-19 12:25:57 -07:00
0bead4d410 Fix markdown link 2018-02-19 12:12:45 -07:00
4a7156de43 Move hash generation into stateless function 2018-02-19 12:09:58 -07:00
d88d1b2a09 Reset historian's hasher between events
Hasher will generate different hashes for the same input if it
had already generated a hash.

Also add a binary to ensure the example in the README works.
2018-02-19 12:03:06 -07:00
a7186328e0 Add docs
Fixes #11
2018-02-19 09:27:14 -07:00
5e3c7816bd Ensure verify_slice succeeds 2018-02-19 09:09:24 -07:00
a2fa60fa31 Merge pull request #10 from garious/add-historian
Better names
2018-02-18 10:16:05 -07:00
ceb65c2669 Better function names 2018-02-18 10:05:54 -07:00
fd209ef1a9 Rename event crate to log 2018-02-18 09:59:33 -07:00
471f036444 Better names
Event -> Entry
EventData -> Event
2018-02-18 09:53:40 -07:00
6ec0e5834c Merge pull request #9 from garious/add-historian
Add historian
2018-02-17 21:22:48 -07:00
4c94754661 More coverage 2018-02-17 21:13:52 -07:00
831e2cbdc9 Add historian
A microservice that continuously generates hashes, only stopping to
tag messages with the latest hash.

Fixes #8
2018-02-17 20:58:23 -07:00
141 changed files with 20657 additions and 206 deletions

View File

@ -0,0 +1,2 @@
CI_BUILD_START=$(date +%s)
export CI_BUILD_START

View File

@ -0,0 +1 @@
post-checkout

View File

@ -0,0 +1,45 @@
#!/bin/bash -e
#
# Save target/ for the next CI build on this machine
#
if [[ -n $CARGO_TARGET_CACHE_NAME ]]; then
(
d=$HOME/cargo-target-cache/"$CARGO_TARGET_CACHE_NAME"
mkdir -p "$d"
set -x
rsync -a --delete --link-dest="$PWD" target "$d"
du -hs "$d"
)
fi
#
# Add job_stats data point
#
if [[ -z $CI_BUILD_START ]]; then
echo Error: CI_BUILD_START empty
else
CI_BUILD_DURATION=$(( $(date +%s) - CI_BUILD_START + 1 ))
CI_LABEL=${BUILDKITE_LABEL:-build label missing}
PR=false
if [[ $BUILDKITE_BRANCH =~ pull/* ]]; then
PR=true
fi
SUCCESS=true
if [[ $BUILDKITE_COMMAND_EXIT_STATUS != 0 ]]; then
SUCCESS=false
fi
point_tags="pipeline=$BUILDKITE_PIPELINE_SLUG,job=$CI_LABEL,pr=$PR,success=$SUCCESS"
point_tags="${point_tags// /\\ }" # Escape spaces
point_fields="duration=$CI_BUILD_DURATION"
point_fields="${point_fields// /\\ }" # Escape spaces
point="job_stats,$point_tags $point_fields"
multinode-demo/metrics_write_datapoint.sh "$point" || true
fi

View File

@ -0,0 +1 @@
post-command

View File

@ -0,0 +1,13 @@
#!/bin/bash -e
[[ -n "$CARGO_TARGET_CACHE_NAME" ]] || exit 0
#
# Restore target/ from the previous CI build on this machine
#
(
d=$HOME/cargo-target-cache/"$CARGO_TARGET_CACHE_NAME"
mkdir -p "$d"/target
set -x
rsync -a --delete --link-dest="$d" "$d"/target .
)

View File

@ -0,0 +1 @@
pre-command

1
.clippy.toml Normal file
View File

@ -0,0 +1 @@
too-many-arguments-threshold = 9

5
.codecov.yml Normal file
View File

@ -0,0 +1,5 @@
ignore:
- "src/bin"
coverage:
status:
patch: off

18
.gitignore vendored
View File

@ -1,4 +1,16 @@
/target/
**/*.rs.bk
Cargo.lock
/target/
**/*.rs.bk
.cargo
# node configuration files
/config/
/config-private/
/config-drone/
/config-validator/
/config-client/
/multinode-demo/test/config-client/
# test temp files, ledgers, etc.
/farf/

View File

@ -1,22 +0,0 @@
language: rust
required: sudo
services:
- docker
matrix:
allow_failures:
- rust: nightly
include:
- rust: stable
- rust: nightly
env:
- FEATURES='unstable'
before_script: |
export PATH="$PATH:$HOME/.cargo/bin"
rustup component add rustfmt-preview
script:
- cargo fmt -- --write-mode=diff
- cargo build --verbose --features "$FEATURES"
- cargo test --verbose --features "$FEATURES"
after_success: |
docker run -it --rm --security-opt seccomp=unconfined --volume "$PWD:/volume" elmtai/docker-rust-kcov
bash <(curl -s https://codecov.io/bash) -s target/cov

53
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,53 @@
Solana Coding Guidelines
===
The goal of these guidelines is to improve developer productivity by allowing developers to
jump any file in the codebase and not need to adapt to inconsistencies in how the code is
written. The codebase should appear as if it had been authored by a single developer. If you
don't agree with a convention, submit a PR patching this document and let's discuss! Once
the PR is accepted, *all* code should be updated as soon as possible to reflect the new
conventions.
Rust coding conventions
---
* All Rust code is formatted using the latest version of `rustfmt`. Once installed, it will be
updated automatically when you update the compiler with `rustup`.
* All Rust code is linted with Clippy. If you'd prefer to ignore its advice, do so explicitly:
```rust
#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
```
Note: Clippy defaults can be overridden in the top-level file `.clippy.toml`.
* For variable names, when in doubt, spell it out. The mapping from type names to variable names
is to lowercase the type name, putting an underscore before each capital letter. Variable names
should *not* be abbreviated unless being used as closure arguments and the brevity improves
readability. When a function has multiple instances of the same type, qualify each with a
prefix and underscore (i.e. alice_keypair) or a numeric suffix (i.e. tx0).
* For function and method names, use `<verb>_<subject>`. For unit tests, that verb should
always be `test` and for benchmarks the verb should always be `bench`. Avoid namespacing
function names with some arbitrary word. Avoid abreviating words in function names.
* As they say, "When in Rome, do as the Romans do." A good patch should acknowledge the coding
conventions of the code that surrounds it, even in the case where that code has not yet been
updated to meet the conventions described here.
Terminology
---
Inventing new terms is allowed, but should only be done when the term is widely used and
understood. Avoid introducing new 3-letter terms, which can be confused with 3-letter acronyms.
Some terms we currently use regularly in the codebase:
* fullnode: n. A fully participating network node.
* hash: n. A SHA-256 Hash.
* keypair: n. A Ed25519 key-pair, containing a public and private key.
* pubkey: n. The public key of a Ed25519 key-pair.
* sigverify: v. To verify a Ed25519 digital signature.

View File

@ -1,22 +1,119 @@
[package]
name = "silk"
description = "A silky smooth implementation of the Loom architecture"
version = "0.1.3"
documentation = "https://docs.rs/silk"
homepage = "http://loomprotocol.com/"
repository = "https://github.com/loomprotocol/silk"
name = "solana"
description = "Blockchain, Rebuilt for Scale"
version = "0.7.2"
documentation = "https://docs.rs/solana"
homepage = "http://solana.com/"
readme = "README.md"
repository = "https://github.com/solana-labs/solana"
authors = [
"Anatoly Yakovenko <aeyakovenko@gmail.com>",
"Greg Fitzgerald <garious@gmail.com>",
"Anatoly Yakovenko <anatoly@solana.com>",
"Greg Fitzgerald <greg@solana.com>",
"Stephen Akridge <stephen@solana.com>",
"Michael Vines <mvines@solana.com>",
"Rob Walker <rob@solana.com>",
"Pankaj Garg <pankaj@solana.com>",
"Tyera Eulberg <tyera@solana.com>",
]
license = "Apache-2.0"
[[bin]]
name = "solana-bench-tps"
path = "src/bin/bench-tps.rs"
[[bin]]
name = "solana-bench-streamer"
path = "src/bin/bench-streamer.rs"
[[bin]]
name = "solana-drone"
path = "src/bin/drone.rs"
[[bin]]
name = "solana-fullnode"
path = "src/bin/fullnode.rs"
[[bin]]
name = "solana-fullnode-config"
path = "src/bin/fullnode-config.rs"
[[bin]]
name = "solana-genesis"
path = "src/bin/genesis.rs"
[[bin]]
name = "solana-ledger-tool"
path = "src/bin/ledger-tool.rs"
[[bin]]
name = "solana-keygen"
path = "src/bin/keygen.rs"
[[bin]]
name = "solana-wallet"
path = "src/bin/wallet.rs"
[badges]
codecov = { repository = "loomprotocol/silk", branch = "master", service = "github" }
codecov = { repository = "solana-labs/solana", branch = "master", service = "github" }
[features]
unstable = []
ipv6 = []
cuda = []
erasure = []
[dependencies]
atty = "0.2"
bincode = "1.0.0"
bs58 = "0.2.0"
byteorder = "1.2.1"
chrono = { version = "0.4.0", features = ["serde"] }
clap = "2.31"
dirs = "1.0.2"
env_logger = "0.5.12"
futures = "0.1.21"
generic-array = { version = "0.11.1", default-features = false, features = ["serde"] }
getopts = "0.2"
influx_db_client = "0.3.4"
itertools = "0.7.8"
libc = "0.2.1"
log = "0.4.2"
matches = "0.1.6"
pnet_datalink = "0.21.0"
rand = "0.5.1"
rayon = "1.0.0"
itertools = "0.7.6"
reqwest = "0.8.6"
ring = "0.13.2"
sha2 = "0.7.0"
serde = "1.0.27"
serde_derive = "1.0.27"
serde_json = "1.0.10"
sys-info = "0.5.6"
tokio = "0.1"
tokio-codec = "0.1"
tokio-core = "0.1.17"
tokio-io = "0.1"
untrusted = "0.6.2"
[dev-dependencies]
criterion = "0.2"
[[bench]]
name = "bank"
harness = false
[[bench]]
name = "banking_stage"
harness = false
[[bench]]
name = "ledger"
harness = false
[[bench]]
name = "signature"
harness = false
[[bench]]
name = "sigverify"
harness = false

View File

@ -1,4 +1,4 @@
Copyright 2018 Anatoly Yakovenko <anatoly@loomprotocol.com> and Greg Fitzgerald <garious@gmail.com>
Copyright 2018 Solana Labs, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

327
README.md
View File

@ -1,21 +1,241 @@
[![Silk crate](https://img.shields.io/crates/v/silk.svg)](https://crates.io/crates/silk)
[![Silk documentation](https://docs.rs/silk/badge.svg)](https://docs.rs/silk)
[![Build Status](https://travis-ci.org/loomprotocol/silk.svg?branch=master)](https://travis-ci.org/loomprotocol/silk)
[![codecov](https://codecov.io/gh/loomprotocol/silk/branch/master/graph/badge.svg)](https://codecov.io/gh/loomprotocol/silk)
[![Solana crate](https://img.shields.io/crates/v/solana.svg)](https://crates.io/crates/solana)
[![Solana documentation](https://docs.rs/solana/badge.svg)](https://docs.rs/solana)
[![Build status](https://badge.buildkite.com/d4c4d7da9154e3a8fb7199325f430ccdb05be5fc1e92777e51.svg?branch=master)](https://solana-ci-gate.herokuapp.com/buildkite_public_log?https://buildkite.com/solana-labs/solana/builds/latest/master)
[![codecov](https://codecov.io/gh/solana-labs/solana/branch/master/graph/badge.svg)](https://codecov.io/gh/solana-labs/solana)
# Silk, a silky smooth implementation of the Loom specification
Blockchain, Rebuilt for Scale
===
Loom is a new achitecture for a high performance blockchain. Its whitepaper boasts a theoretical
throughput of 710k transactions per second on a 1 gbps network. The specification is implemented
in two git repositories. Reserach is performed in the loom repository. That work drives the
Loom specification forward. This repository, on the other hand, aims to implement the specification
as-is. We care a great deal about quality, clarity and short learning curve. We avoid the use
of `unsafe` Rust and write tests for *everything*. Optimizations are only added when
corresponding benchmarks are also added that demonstrate real performance boots. We expect the
feature set here will always be a ways behind the loom repo, but that this is an implementation
you can take to the bank, literally.
Solana&trade; is a new blockchain architecture built from the ground up for scale. The architecture supports
up to 710 thousand transactions per second on a gigabit network.
# Developing
Disclaimer
===
All claims, content, designs, algorithms, estimates, roadmaps, specifications, and performance measurements described in this project are done with the author's best effort. It is up to the reader to check and validate their accuracy and truthfulness. Furthermore nothing in this project constitutes a solicitation for investment.
Introduction
===
It's possible for a centralized database to process 710,000 transactions per second on a standard gigabit network if the transactions are, on average, no more than 176 bytes. A centralized database can also replicate itself and maintain high availability without significantly compromising that transaction rate using the distributed system technique known as Optimistic Concurrency Control [H.T.Kung, J.T.Robinson (1981)]. At Solana, we're demonstrating that these same theoretical limits apply just as well to blockchain on an adversarial network. The key ingredient? Finding a way to share time when nodes can't trust one-another. Once nodes can trust time, suddenly ~40 years of distributed systems research becomes applicable to blockchain! Furthermore, and much to our surprise, it can implemented using a mechanism that has existed in Bitcoin since day one. The Bitcoin feature is called nLocktime and it can be used to postdate transactions using block height instead of a timestamp. As a Bitcoin client, you'd use block height instead of a timestamp if you don't trust the network. Block height turns out to be an instance of what's being called a Verifiable Delay Function in cryptography circles. It's a cryptographically secure way to say time has passed. In Solana, we use a far more granular verifiable delay function, a SHA 256 hash chain, to checkpoint the ledger and coordinate consensus. With it, we implement Optimistic Concurrency Control and are now well in route towards that theoretical limit of 710,000 transactions per second.
Testnet Demos
===
The Solana repo contains all the scripts you might need to spin up your own
local testnet. Depending on what you're looking to achieve, you may want to
run a different variation, as the full-fledged, performance-enhanced
multinode testnet is considerably more complex to set up than a Rust-only,
singlenode testnode. If you are looking to develop high-level features, such
as experimenting with smart contracts, save yourself some setup headaches and
stick to the Rust-only singlenode demo. If you're doing performance optimization
of the transaction pipeline, consider the enhanced singlenode demo. If you're
doing consensus work, you'll need at least a Rust-only multinode demo. If you want
to reproduce our TPS metrics, run the enhanced multinode demo.
For all four variations, you'd need the latest Rust toolchain and the Solana
source code:
First, install Rust's package manager Cargo.
```bash
$ curl https://sh.rustup.rs -sSf | sh
$ source $HOME/.cargo/env
```
Now checkout the code from github:
```bash
$ git clone https://github.com/solana-labs/solana.git
$ cd solana
```
The demo code is sometimes broken between releases as we add new low-level
features, so if this is your first time running the demo, you'll improve
your odds of success if you check out the
[latest release](https://github.com/solana-labs/solana/releases)
before proceeding:
```bash
$ git checkout v0.7.0-beta
```
Configuration Setup
---
The network is initialized with a genesis ledger and leader/validator configuration files.
These files can be generated by running the following script.
```bash
$ ./multinode-demo/setup.sh
```
Drone
---
In order for the leader, client and validators to work, we'll need to
spin up a drone to give out some test tokens. The drone delivers Milton
Friedman-style "air drops" (free tokens to requesting clients) to be used in
test transactions.
Start the drone on the leader node with:
```bash
$ ./multinode-demo/drone.sh
```
Singlenode Testnet
---
Before you start a fullnode, make sure you know the IP address of the machine you
want to be the leader for the demo, and make sure that udp ports 8000-10000 are
open on all the machines you want to test with.
Now start the server:
```bash
$ ./multinode-demo/leader.sh
```
Wait a few seconds for the server to initialize. It will print "Ready." when it's ready to
receive transactions. The leader will request some tokens from the drone if it doesn't have any.
The drone does not need to be running for subsequent leader starts.
Multinode Testnet
---
To run a multinode testnet, after starting a leader node, spin up some validator nodes:
```bash
$ ./multinode-demo/validator.sh ubuntu@10.0.1.51:~/solana 10.0.1.51
```
To run a performance-enhanced leader or validator (on Linux),
[CUDA 9.2](https://developer.nvidia.com/cuda-downloads) must be installed on
your system:
```bash
$ ./fetch-perf-libs.sh
$ SOLANA_CUDA=1 ./multinode-demo/leader.sh
$ SOLANA_CUDA=1 ./multinode-demo/validator.sh ubuntu@10.0.1.51:~/solana 10.0.1.51
```
Testnet Client Demo
---
Now that your singlenode or multinode testnet is up and running, in a separate shell, let's send it some transactions! Note we pass in
the JSON configuration file here, not the genesis ledger.
```bash
$ ./multinode-demo/client.sh ubuntu@10.0.1.51:~/solana 2 #The leader machine and the total number of nodes in the network
```
What just happened? The client demo spins up several threads to send 500,000 transactions
to the testnet as quickly as it can. The client then pings the testnet periodically to see
how many transactions it processed in that time. Take note that the demo intentionally
floods the network with UDP packets, such that the network will almost certainly drop a
bunch of them. This ensures the testnet has an opportunity to reach 710k TPS. The client
demo completes after it has convinced itself the testnet won't process any additional
transactions. You should see several TPS measurements printed to the screen. In the
multinode variation, you'll see TPS measurements for each validator node as well.
Linux Snap
---
A Linux [Snap](https://snapcraft.io/) is available, which can be used to
easily get Solana running on supported Linux systems without building anything
from source. The `edge` Snap channel is updated daily with the latest
development from the `master` branch. To install:
```bash
$ sudo snap install solana --edge --devmode
```
(`--devmode` flag is required only for `solana.fullnode-cuda`)
Once installed the usual Solana programs will be available as `solona.*` instead
of `solana-*`. For example, `solana.fullnode` instead of `solana-fullnode`.
Update to the latest version at any time with:
```bash
$ snap info solana
$ sudo snap refresh solana --devmode
```
### Daemon support
The snap supports running a leader, validator or leader+drone node as a system
daemon.
Run `sudo snap get solana` to view the current daemon configuration. To view
daemon logs:
1. Run `sudo snap logs -n=all solana` to view the daemon initialization log
2. Runtime logging can be found under `/var/snap/solana/current/leader/`,
`/var/snap/solana/current/validator/`, or `/var/snap/solana/current/drone/` depending
on which `mode=` was selected. Within each log directory the file `current`
contains the latest log, and the files `*.s` (if present) contain older rotated
logs.
Disable the daemon at any time by running:
```bash
$ sudo snap set solana mode=
```
Runtime configuration files for the daemon can be found in
`/var/snap/solana/current/config`.
#### Leader daemon
```bash
$ sudo snap set solana mode=leader
```
If CUDA is available:
```bash
$ sudo snap set solana mode=leader enable-cuda=1
```
`rsync` must be configured and running on the leader.
1. Ensure rsync is installed with `sudo apt-get -y install rsync`
2. Edit `/etc/rsyncd.conf` to include the following
```
[config]
path = /var/snap/solana/current/config
hosts allow = *
read only = true
```
3. Run `sudo systemctl enable rsync; sudo systemctl start rsync`
4. Test by running `rsync -Pzravv rsync://<ip-address-of-leader>/config
solana-config` from another machine. **If the leader is running on a cloud
provider it may be necessary to configure the Firewall rules to permit ingress
to port tcp:873, tcp:9900 and the port range udp:8000-udp:10000**
To run both the Leader and Drone:
```bash
$ sudo snap set solana mode=leader+drone
```
#### Validator daemon
```bash
$ sudo snap set solana mode=validator
```
If CUDA is available:
```bash
$ sudo snap set solana mode=validator enable-cuda=1
```
By default the validator will connect to **testnet.solana.com**, override
the leader IP address by running:
```bash
$ sudo snap set solana mode=validator leader-address=127.0.0.1 #<-- change IP address
```
It's assumed that the leader will be running `rsync` configured as described in
the previous **Leader daemon** section.
Developing
===
Building
---
@ -28,11 +248,22 @@ $ source $HOME/.cargo/env
$ rustup component add rustfmt-preview
```
If your rustc version is lower than 1.26.1, please update it:
```bash
$ rustup update
```
On Linux systems you may need to install libssl-dev and pkg-config. On Ubuntu:
```bash
$ sudo apt-get install libssl-dev pkg-config
```
Download the source code:
```bash
$ git clone https://github.com/loomprotocol/silk.git
$ cd silk
$ git clone https://github.com/solana-labs/solana.git
$ cd solana
```
Testing
@ -41,9 +272,37 @@ Testing
Run the test suite:
```bash
cargo test
$ cargo test
```
To emulate all the tests that will run on a Pull Request, run:
```bash
$ ./ci/run-local.sh
```
Debugging
---
There are some useful debug messages in the code, you can enable them on a per-module and per-level
basis with the normal RUST\_LOG environment variable. Run the fullnode with this syntax:
```bash
$ RUST_LOG=solana::streamer=debug,solana::server=info cat genesis.log | ./target/release/solana-fullnode > transactions0.log
```
to see the debug and info sections for streamer and server respectively. Generally
we are using debug for infrequent debug messages, trace for potentially frequent messages and
info for performance-related logging.
Attaching to a running process with gdb:
```
$ sudo gdb
attach <PID>
set logging on
thread apply all bt
```
This will dump all the threads stack traces into gdb.txt
Benchmarking
---
@ -58,3 +317,35 @@ Run the benchmarks:
```bash
$ cargo +nightly bench --features="unstable"
```
Code coverage
---
To generate code coverage statistics, install cargo-cov. Note: the tool currently only works
in Rust nightly.
```bash
$ cargo +nightly install cargo-cov
```
Run cargo-cov and generate a report:
```bash
$ cargo +nightly cov test
$ cargo +nightly cov report --open
```
The coverage report will be written to `./target/cov/report/index.html`
Why coverage? While most see coverage as a code quality metric, we see it primarily as a developer
productivity metric. When a developer makes a change to the codebase, presumably it's a *solution* to
some problem. Our unit-test suite is how we encode the set of *problems* the codebase solves. Running
the test suite should indicate that your change didn't *infringe* on anyone else's solutions. Adding a
test *protects* your solution from future changes. Say you don't understand why a line of code exists,
try deleting it and running the unit-tests. The nearest test failure should tell you what problem
was solved by that code. If no test fails, go ahead and submit a Pull Request that asks, "what
problem is solved by this code?" On the other hand, if a test does fail and you can think of a
better way to solve the same problem, a Pull Request with your solution would most certainly be
welcome! Likewise, if rewriting a test can better communicate what code it's protecting, please
send us that patch!

1
_config.yml Normal file
View File

@ -0,0 +1 @@
theme: jekyll-theme-slate

66
benches/bank.rs Normal file
View File

@ -0,0 +1,66 @@
#[macro_use]
extern crate criterion;
extern crate bincode;
extern crate rayon;
extern crate solana;
use bincode::serialize;
use criterion::{Bencher, Criterion};
use rayon::prelude::*;
use solana::bank::*;
use solana::hash::hash;
use solana::mint::Mint;
use solana::signature::{Keypair, KeypairUtil};
use solana::transaction::Transaction;
fn bench_process_transaction(bencher: &mut Bencher) {
let mint = Mint::new(100_000_000);
let bank = Bank::new(&mint);
// Create transactions between unrelated parties.
let transactions: Vec<_> = (0..4096)
.into_par_iter()
.map(|i| {
// Seed the 'from' account.
let rando0 = Keypair::new();
let tx = Transaction::new(&mint.keypair(), rando0.pubkey(), 10_000, mint.last_id());
assert!(bank.process_transaction(&tx).is_ok());
// Seed the 'to' account and a cell for its signature.
let last_id = hash(&serialize(&i).unwrap()); // Unique hash
bank.register_entry_id(&last_id);
let rando1 = Keypair::new();
let tx = Transaction::new(&rando0, rando1.pubkey(), 1, last_id);
assert!(bank.process_transaction(&tx).is_ok());
// Finally, return the transaction to the benchmark.
tx
})
.collect();
bencher.iter_with_setup(
|| {
// Since benchmarker runs this multiple times, we need to clear the signatures.
bank.clear_signatures();
transactions.clone()
},
|transactions| {
let results = bank.process_transactions(transactions);
assert!(results.iter().all(Result::is_ok));
},
)
}
fn bench(criterion: &mut Criterion) {
criterion.bench_function("bench_process_transaction", |bencher| {
bench_process_transaction(bencher);
});
}
criterion_group!(
name = benches;
config = Criterion::default().sample_size(2);
targets = bench
);
criterion_main!(benches);

229
benches/banking_stage.rs Normal file
View File

@ -0,0 +1,229 @@
extern crate bincode;
#[macro_use]
extern crate criterion;
extern crate rayon;
extern crate solana;
use criterion::{Bencher, Criterion};
use rayon::prelude::*;
use solana::bank::Bank;
use solana::banking_stage::BankingStage;
use solana::mint::Mint;
use solana::packet::{to_packets_chunked, PacketRecycler};
use solana::record_stage::Signal;
use solana::signature::{Keypair, KeypairUtil};
use solana::transaction::Transaction;
use std::iter;
use std::sync::mpsc::{channel, Receiver};
use std::sync::Arc;
// use self::test::Bencher;
// use bank::{Bank, MAX_ENTRY_IDS};
// use bincode::serialize;
// use hash::hash;
// use mint::Mint;
// use rayon::prelude::*;
// use signature::{Keypair, KeypairUtil};
// use std::collections::HashSet;
// use std::time::Instant;
// use transaction::Transaction;
//
// fn bench_process_transactions(_bencher: &mut Bencher) {
// let mint = Mint::new(100_000_000);
// let bank = Bank::new(&mint);
// // Create transactions between unrelated parties.
// let txs = 100_000;
// let last_ids: Mutex<HashSet<Hash>> = Mutex::new(HashSet::new());
// let transactions: Vec<_> = (0..txs)
// .into_par_iter()
// .map(|i| {
// // Seed the 'to' account and a cell for its signature.
// let dummy_id = i % (MAX_ENTRY_IDS as i32);
// let last_id = hash(&serialize(&dummy_id).unwrap()); // Semi-unique hash
// {
// let mut last_ids = last_ids.lock().unwrap();
// if !last_ids.contains(&last_id) {
// last_ids.insert(last_id);
// bank.register_entry_id(&last_id);
// }
// }
//
// // Seed the 'from' account.
// let rando0 = Keypair::new();
// let tx = Transaction::new(&mint.keypair(), rando0.pubkey(), 1_000, last_id);
// bank.process_transaction(&tx).unwrap();
//
// let rando1 = Keypair::new();
// let tx = Transaction::new(&rando0, rando1.pubkey(), 2, last_id);
// bank.process_transaction(&tx).unwrap();
//
// // Finally, return a transaction that's unique
// Transaction::new(&rando0, rando1.pubkey(), 1, last_id)
// })
// .collect();
//
// let banking_stage = EventProcessor::new(bank, &mint.last_id(), None);
//
// let now = Instant::now();
// assert!(banking_stage.process_transactions(transactions).is_ok());
// let duration = now.elapsed();
// let sec = duration.as_secs() as f64 + duration.subsec_nanos() as f64 / 1_000_000_000.0;
// let tps = txs as f64 / sec;
//
// // Ensure that all transactions were successfully logged.
// drop(banking_stage.historian_input);
// let entries: Vec<Entry> = banking_stage.output.lock().unwrap().iter().collect();
// assert_eq!(entries.len(), 1);
// assert_eq!(entries[0].transactions.len(), txs as usize);
//
// println!("{} tps", tps);
// }
fn check_txs(receiver: &Receiver<Signal>, ref_tx_count: usize) {
let mut total = 0;
loop {
let signal = receiver.recv().unwrap();
if let Signal::Transactions(transactions) = signal {
total += transactions.len();
if total >= ref_tx_count {
break;
}
} else {
assert!(false);
}
}
assert_eq!(total, ref_tx_count);
}
fn bench_banking_stage_multi_accounts(bencher: &mut Bencher) {
let tx = 10_000_usize;
let mint_total = 1_000_000_000_000;
let mint = Mint::new(mint_total);
let num_dst_accounts = 8 * 1024;
let num_src_accounts = 8 * 1024;
let srckeys: Vec<_> = (0..num_src_accounts).map(|_| Keypair::new()).collect();
let dstkeys: Vec<_> = (0..num_dst_accounts)
.map(|_| Keypair::new().pubkey())
.collect();
let transactions: Vec<_> = (0..tx)
.map(|i| {
Transaction::new(
&srckeys[i % num_src_accounts],
dstkeys[i % num_dst_accounts],
i as i64,
mint.last_id(),
)
})
.collect();
let (verified_sender, verified_receiver) = channel();
let (signal_sender, signal_receiver) = channel();
let packet_recycler = PacketRecycler::default();
let setup_transactions: Vec<_> = (0..num_src_accounts)
.map(|i| {
Transaction::new(
&mint.keypair(),
srckeys[i].pubkey(),
mint_total / num_src_accounts as i64,
mint.last_id(),
)
})
.collect();
bencher.iter(move || {
let bank = Arc::new(Bank::new(&mint));
let verified_setup: Vec<_> =
to_packets_chunked(&packet_recycler, &setup_transactions.clone(), tx)
.into_iter()
.map(|x| {
let len = (*x).read().unwrap().packets.len();
(x, iter::repeat(1).take(len).collect())
})
.collect();
let verified_setup_len = verified_setup.len();
verified_sender.send(verified_setup).unwrap();
BankingStage::process_packets(&bank, &verified_receiver, &signal_sender, &packet_recycler)
.unwrap();
check_txs(&signal_receiver, num_src_accounts);
let verified: Vec<_> = to_packets_chunked(&packet_recycler, &transactions.clone(), 192)
.into_iter()
.map(|x| {
let len = (*x).read().unwrap().packets.len();
(x, iter::repeat(1).take(len).collect())
})
.collect();
let verified_len = verified.len();
verified_sender.send(verified).unwrap();
BankingStage::process_packets(&bank, &verified_receiver, &signal_sender, &packet_recycler)
.unwrap();
check_txs(&signal_receiver, tx);
});
}
fn bench_banking_stage_single_from(bencher: &mut Bencher) {
let tx = 10_000_usize;
let mint = Mint::new(1_000_000_000_000);
let mut pubkeys = Vec::new();
let num_keys = 8;
for _ in 0..num_keys {
pubkeys.push(Keypair::new().pubkey());
}
let transactions: Vec<_> = (0..tx)
.into_par_iter()
.map(|i| {
Transaction::new(
&mint.keypair(),
pubkeys[i % num_keys],
i as i64,
mint.last_id(),
)
})
.collect();
let (verified_sender, verified_receiver) = channel();
let (signal_sender, signal_receiver) = channel();
let packet_recycler = PacketRecycler::default();
bencher.iter(move || {
let bank = Arc::new(Bank::new(&mint));
let verified: Vec<_> = to_packets_chunked(&packet_recycler, &transactions.clone(), tx)
.into_iter()
.map(|x| {
let len = (*x).read().unwrap().packets.len();
(x, iter::repeat(1).take(len).collect())
})
.collect();
let verified_len = verified.len();
verified_sender.send(verified).unwrap();
BankingStage::process_packets(&bank, &verified_receiver, &signal_sender, &packet_recycler)
.unwrap();
check_txs(&signal_receiver, tx);
});
}
fn bench(criterion: &mut Criterion) {
criterion.bench_function("bench_banking_stage_multi_accounts", |bencher| {
bench_banking_stage_multi_accounts(bencher);
});
criterion.bench_function("bench_process_stage_single_from", |bencher| {
bench_banking_stage_single_from(bencher);
});
}
criterion_group!(
name = benches;
config = Criterion::default().sample_size(2);
targets = bench
);
criterion_main!(benches);

40
benches/ledger.rs Normal file
View File

@ -0,0 +1,40 @@
#[macro_use]
extern crate criterion;
extern crate solana;
use criterion::{Bencher, Criterion};
use solana::hash::{hash, Hash};
use solana::ledger::{next_entries, reconstruct_entries_from_blobs, Block};
use solana::packet::BlobRecycler;
use solana::signature::{Keypair, KeypairUtil};
use solana::transaction::Transaction;
use std::collections::VecDeque;
fn bench_block_to_blobs_to_block(bencher: &mut Bencher) {
let zero = Hash::default();
let one = hash(&zero.as_ref());
let keypair = Keypair::new();
let tx0 = Transaction::new(&keypair, keypair.pubkey(), 1, one);
let transactions = vec![tx0; 10];
let entries = next_entries(&zero, 1, transactions);
let blob_recycler = BlobRecycler::default();
bencher.iter(|| {
let mut blob_q = VecDeque::new();
entries.to_blobs(&blob_recycler, &mut blob_q);
assert_eq!(reconstruct_entries_from_blobs(blob_q).unwrap(), entries);
});
}
fn bench(criterion: &mut Criterion) {
criterion.bench_function("bench_block_to_blobs_to_block", |bencher| {
bench_block_to_blobs_to_block(bencher);
});
}
criterion_group!(
name = benches;
config = Criterion::default().sample_size(2);
targets = bench
);
criterion_main!(benches);

24
benches/signature.rs Normal file
View File

@ -0,0 +1,24 @@
#[macro_use]
extern crate criterion;
extern crate solana;
use criterion::{Bencher, Criterion};
use solana::signature::GenKeys;
fn bench_gen_keys(b: &mut Bencher) {
let mut rnd = GenKeys::new([0u8; 32]);
b.iter(|| rnd.gen_n_keypairs(1000));
}
fn bench(criterion: &mut Criterion) {
criterion.bench_function("bench_gen_keys", |bencher| {
bench_gen_keys(bencher);
});
}
criterion_group!(
name = benches;
config = Criterion::default().sample_size(2);
targets = bench
);
criterion_main!(benches);

36
benches/sigverify.rs Normal file
View File

@ -0,0 +1,36 @@
#[macro_use]
extern crate criterion;
extern crate bincode;
extern crate rayon;
extern crate solana;
use criterion::{Bencher, Criterion};
use solana::packet::{to_packets, PacketRecycler};
use solana::sigverify;
use solana::transaction::test_tx;
fn bench_sigverify(bencher: &mut Bencher) {
let tx = test_tx();
// generate packet vector
let packet_recycler = PacketRecycler::default();
let batches = to_packets(&packet_recycler, &vec![tx; 128]);
// verify packets
bencher.iter(|| {
let _ans = sigverify::ed25519_verify(&batches);
})
}
fn bench(criterion: &mut Criterion) {
criterion.bench_function("bench_sigverify", |bencher| {
bench_sigverify(bencher);
});
}
criterion_group!(
name = benches;
config = Criterion::default().sample_size(2);
targets = bench
);
criterion_main!(benches);

16
build.rs Normal file
View File

@ -0,0 +1,16 @@
use std::env;
fn main() {
println!("cargo:rustc-link-search=native=.");
if !env::var("CARGO_FEATURE_CUDA").is_err() {
println!("cargo:rustc-link-lib=static=cuda_verify_ed25519");
println!("cargo:rustc-link-search=native=/usr/local/cuda/lib64");
println!("cargo:rustc-link-lib=dylib=cudart");
println!("cargo:rustc-link-lib=dylib=cuda");
println!("cargo:rustc-link-lib=dylib=cudadevrt");
}
if !env::var("CARGO_FEATURE_ERASURE").is_err() {
println!("cargo:rustc-link-lib=dylib=Jerasure");
println!("cargo:rustc-link-lib=dylib=gf_complete");
}
}

3
ci/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/node_modules/
/package-lock.json
/snapcraft.credentials

89
ci/README.md Normal file
View File

@ -0,0 +1,89 @@
Our CI infrastructure is built around [BuildKite](https://buildkite.com) with some
additional GitHub integration provided by https://github.com/mvines/ci-gate
## Agent Queues
We define two [Agent Queues](https://buildkite.com/docs/agent/v3/queues):
`queue=default` and `queue=cuda`. The `default` queue should be favored and
runs on lower-cost CPU instances. The `cuda` queue is only necessary for
running **tests** that depend on GPU (via CUDA) access -- CUDA builds may still
be run on the `default` queue, and the [buildkite artifact
system](https://buildkite.com/docs/builds/artifacts) used to transfer build
products over to a GPU instance for testing.
## Buildkite Agent Management
### Buildkite GCP Setup
CI runs on Google Cloud Platform via two Compute Engine Instance groups:
`ci-default` and `ci-cuda`. Autoscaling is currently disabled and the number of
VM Instances in each group is manually adjusted.
#### Updating a CI Disk Image
Each Instance group has its own disk image, `ci-default-vX` and
`ci-cuda-vY`, where *X* and *Y* are incremented each time the image is changed.
The process to update a disk image is as follows (TODO: make this less manual):
1. Create a new VM Instance using the disk image to modify.
2. Once the VM boots, ssh to it and modify the disk as desired.
3. Stop the VM Instance running the modified disk. Remember the name of the VM disk
4. From another machine, `gcloud auth login`, then create a new Disk Image based
off the modified VM Instance:
```
$ gcloud compute images create ci-default-$(date +%Y%m%d%H%M) --source-disk xxx --source-disk-zone us-east1-b --family ci-default
```
or
```
$ gcloud compute images create ci-cuda-$(date +%Y%m%d%H%M) --source-disk xxx --source-disk-zone us-east1-b --family ci-cuda
```
5. Delete the new VM instance.
6. Go to the Instance templates tab, find the existing template named
`ci-default-vX` or `ci-cuda-vY` and select it. Use the "Copy" button to create
a new Instance template called `ci-default-vX+1` or `ci-cuda-vY+1` with the
newly created Disk image.
7. Go to the Instance Groups tag and find the applicable group, `ci-default` or
`ci-cuda`. Edit the Instance Group in two steps: (a) Set the number of
instances to 0 and wait for them all to terminate, (b) Update the Instance
template and restore the number of instances to the original value.
8. Clean up the previous version by deleting it from Instance Templates and
Images.
## Reference
### Buildkite AWS CloudFormation Setup
**AWS CloudFormation is currently inactive, although it may be restored in the
future**
AWS CloudFormation can be used to scale machines up and down based on the
current CI load. If no machine is currently running it can take up to 60
seconds to spin up a new instance, please remain calm during this time.
#### AMI
We use a custom AWS AMI built via https://github.com/solana-labs/elastic-ci-stack-for-aws/tree/solana/cuda.
Use the following process to update this AMI as dependencies change:
```bash
$ export AWS_ACCESS_KEY_ID=my_access_key
$ export AWS_SECRET_ACCESS_KEY=my_secret_access_key
$ git clone https://github.com/solana-labs/elastic-ci-stack-for-aws.git -b solana/cuda
$ cd elastic-ci-stack-for-aws/
$ make build
$ make build-ami
```
Watch for the *"amazon-ebs: AMI:"* log message to extract the name of the new
AMI. For example:
```
amazon-ebs: AMI: ami-07118545e8b4ce6dc
```
The new AMI should also now be visible in your EC2 Dashboard. Go to the desired
AWS CloudFormation stack, update the **ImageId** field to the new AMI id, and
*apply* the stack changes.

32
ci/audit.sh Executable file
View File

@ -0,0 +1,32 @@
#!/bin/bash -e
#
# Audits project dependencies for security vulnerabilities
#
cd "$(dirname "$0")/.."
export RUST_BACKTRACE=1
rustc --version
cargo --version
_() {
echo "--- $*"
"$@"
}
maybe_cargo_install() {
for cmd in "$@"; do
set +e
cargo "$cmd" --help > /dev/null 2>&1
declare exitcode=$?
set -e
if [[ $exitcode -eq 101 ]]; then
_ cargo install cargo-"$cmd"
fi
done
}
maybe_cargo_install audit tree
_ cargo tree
_ cargo audit

4
ci/buildkite-snap.yml Normal file
View File

@ -0,0 +1,4 @@
steps:
- command: "ci/snap.sh"
timeout_in_minutes: 40
name: "snap [public]"

49
ci/buildkite.yml Normal file
View File

@ -0,0 +1,49 @@
steps:
- command: "ci/docker-run.sh solanalabs/rust:1.28.0 ci/test-stable.sh"
name: "stable [public]"
env:
CARGO_TARGET_CACHE_NAME: "stable"
timeout_in_minutes: 30
- command: "ci/docker-run.sh solanalabs/rust:1.28.0 ci/test-bench.sh"
name: "bench [public]"
env:
CARGO_TARGET_CACHE_NAME: "stable"
timeout_in_minutes: 30
- command: "ci/shellcheck.sh"
name: "shellcheck [public]"
timeout_in_minutes: 20
- command: "ci/docker-run.sh solanalabs/rust-nightly:2018-08-14 ci/test-nightly.sh"
name: "nightly [public]"
env:
CARGO_TARGET_CACHE_NAME: "nightly"
timeout_in_minutes: 30
- command: "ci/test-stable-perf.sh"
name: "stable-perf [public]"
env:
CARGO_TARGET_CACHE_NAME: "stable-perf"
timeout_in_minutes: 20
agents:
- "queue=cuda"
- command: "ci/test-large-network.sh || true"
name: "large-network [public] [ignored]"
env:
CARGO_TARGET_CACHE_NAME: "stable"
timeout_in_minutes: 20
agents:
- "queue=large"
- command: "ci/pr-snap.sh"
timeout_in_minutes: 20
name: "snap [public]"
- wait
- command: "ci/publish-crate.sh"
timeout_in_minutes: 20
name: "publish crate [public]"
- trigger: "solana-snap"
branches: "!pull/*"
async: true
build:
message: "${BUILDKITE_MESSAGE}"
commit: "${BUILDKITE_COMMIT}"
branch: "${BUILDKITE_BRANCH}"
env:
TRIGGERED_BUILDKITE_TAG: "${BUILDKITE_TAG}"

55
ci/docker-run.sh Executable file
View File

@ -0,0 +1,55 @@
#!/bin/bash -e
usage() {
echo "Usage: $0 [docker image name] [command]"
echo
echo Runs command in the specified docker image with
echo a CI-appropriate environment
echo
}
cd "$(dirname "$0")/.."
IMAGE="$1"
if [[ -z "$IMAGE" ]]; then
echo Error: image not defined
exit 1
fi
docker pull "$IMAGE"
shift
ARGS=(
--workdir /solana
--volume "$PWD:/solana"
--rm
)
if [[ -n $CI ]]; then
ARGS+=(--volume "$HOME:/home")
ARGS+=(--env "CARGO_HOME=/home/.cargo")
fi
# kcov tries to set the personality of the binary which docker
# doesn't allow by default.
ARGS+=(--security-opt "seccomp=unconfined")
# Ensure files are created with the current host uid/gid
if [[ -z "$SOLANA_DOCKER_RUN_NOSETUID" ]]; then
ARGS+=(--user "$(id -u):$(id -g)")
fi
# Environment variables to propagate into the container
ARGS+=(
--env BUILDKITE
--env BUILDKITE_AGENT_ACCESS_TOKEN
--env BUILDKITE_BRANCH
--env BUILDKITE_JOB_ID
--env BUILDKITE_TAG
--env CODECOV_TOKEN
--env CRATES_IO_TOKEN
--env SNAPCRAFT_CREDENTIALS_KEY
)
set -x
exec docker run "${ARGS[@]}" "$IMAGE" "$@"

View File

@ -0,0 +1,9 @@
FROM rustlang/rust:nightly
RUN rustup component add clippy-preview --toolchain=nightly && \
echo deb http://ftp.debian.org/debian stretch-backports main >> /etc/apt/sources.list && \
apt update && \
apt install -y \
llvm-6.0 \
&& \
rm -rf /var/lib/apt/lists/*

View File

@ -0,0 +1,6 @@
Docker image containing rust nightly and some preinstalled crates used in CI.
This image may be manually updated by running `./build.sh` if you are a member
of the [Solana Labs](https://hub.docker.com/u/solanalabs/) Docker Hub
organization, but it is also automatically updated periodically by
[this automation](https://buildkite.com/solana-labs/solana-ci-docker-rust-nightly).

View File

@ -0,0 +1,6 @@
#!/bin/bash -ex
cd "$(dirname "$0")"
docker build -t solanalabs/rust-nightly .
docker push solanalabs/rust-nightly

15
ci/docker-rust/Dockerfile Normal file
View File

@ -0,0 +1,15 @@
FROM rust:1.28
RUN apt update && \
apt-get install apt-transport-https && \
echo deb https://apt.buildkite.com/buildkite-agent stable main > /etc/apt/sources.list.d/buildkite-agent.list && \
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 32A37959C2FA5C3C99EFBC32A79206696452D198 && \
apt update && \
apt install -y \
buildkite-agent \
rsync \
sudo \
cmake \
&& \
rustup component add rustfmt-preview && \
rm -rf /var/lib/apt/lists/*

6
ci/docker-rust/README.md Normal file
View File

@ -0,0 +1,6 @@
Docker image containing rust and some preinstalled packages used in CI.
This image may be manually updated by running `./build.sh` if you are a member
of the [Solana Labs](https://hub.docker.com/u/solanalabs/) Docker Hub
organization, but it is also automatically updated periodically by
[this automation](https://buildkite.com/solana-labs/solana-ci-docker-rust).

6
ci/docker-rust/build.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash -ex
cd "$(dirname "$0")"
docker build -t solanalabs/rust .
docker push solanalabs/rust

View File

@ -0,0 +1,7 @@
FROM snapcraft/xenial-amd64
# Update snapcraft to latest version
RUN apt-get update -qq \
&& apt-get install -y snapcraft daemontools \
&& rm -rf /var/lib/apt/lists/* \
&& snapcraft --version

6
ci/docker-snapcraft/build.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash -ex
cd "$(dirname "$0")"
docker build -t solanalabs/snapcraft .
docker push solanalabs/snapcraft

81
ci/hoover.sh Executable file
View File

@ -0,0 +1,81 @@
#!/bin/bash
#
# Regular maintenance performed on a buildkite agent to control disk usage
#
echo --- Delete all exited containers first
(
set -x
exited=$(docker ps -aq --no-trunc --filter "status=exited")
if [[ -n "$exited" ]]; then
# shellcheck disable=SC2086 # Don't want to double quote "$exited"
docker rm $exited
fi
)
echo --- Delete untagged images
(
set -x
untagged=$(docker images | grep '<none>'| awk '{ print $3 }')
if [[ -n "$untagged" ]]; then
# shellcheck disable=SC2086 # Don't want to double quote "$untagged"
docker rmi $untagged
fi
)
echo --- Delete all dangling images
(
set -x
dangling=$(docker images --filter 'dangling=true' -q --no-trunc | sort | uniq)
if [[ -n "$dangling" ]]; then
# shellcheck disable=SC2086 # Don't want to double quote "$dangling"
docker rmi $dangling
fi
)
echo --- Remove unused docker networks
(
set -x
docker network prune -f
)
echo "--- Delete /tmp files older than 1 day owned by $(whoami)"
(
set -x
find /tmp -maxdepth 1 -user "$(whoami)" -mtime +1 -print0 | xargs -0 rm -rf
)
echo --- Deleting stale buildkite agent build directories
if [[ ! -d ../../../../builds/$BUILDKITE_AGENT_NAME ]]; then
# We might not be where we think we are, do nothing
echo Warning: Skipping flush of stale agent build directories
echo " PWD=$PWD"
else
# NOTE: this will be horribly broken if we ever decide to run multiple
# agents on the same machine.
(
for keepDir in "$BUILDKITE_PIPELINE_SLUG" \
"$BUILDKITE_ORGANIZATION_SLUG" \
"$BUILDKITE_AGENT_NAME"; do
cd .. || exit 1
for dir in *; do
if [[ -d $dir && $dir != "$keepDir" ]]; then
echo "Removing $dir"
rm -rf "${dir:?}"/
fi
done
done
)
fi
echo --- System Status
(
set -x
docker images
docker ps
docker network ls
df -h
)
exit 0

32
ci/install-earlyoom.sh Executable file
View File

@ -0,0 +1,32 @@
#!/bin/bash -x
#
# Install EarlyOOM
#
[[ $(uname) = Linux ]] || exit 1
# 64 - enable signalling of processes (term, kill, oom-kill)
# TODO: This setting will not persist across reboots
sysrq=$(( $(cat /proc/sys/kernel/sysrq) | 64 ))
sudo sysctl -w kernel.sysrq=$sysrq
if command -v earlyoom; then
sudo systemctl status earlyoom
exit 0
fi
wget http://ftp.us.debian.org/debian/pool/main/e/earlyoom/earlyoom_1.1-2_amd64.deb
sudo apt install --quiet --yes ./earlyoom_1.1-2_amd64.deb
cat > earlyoom <<OOM
# use the kernel OOM killer, trigger at 20% available RAM,
EARLYOOM_ARGS="-k -m 20"
OOM
sudo cp earlyoom /etc/default/
rm earlyoom
sudo systemctl stop earlyoom
sudo systemctl enable earlyoom
sudo systemctl start earlyoom
exit 0

8
ci/is-pr.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash -e
#
# The standard BUILDKITE_PULL_REQUEST environment variable is always "false" due
# to how solana-ci-gate is used to trigger PR builds rather than using the
# standard Buildkite PR trigger.
#
[[ $BUILDKITE_BRANCH =~ pull/* ]]

86
ci/localnet-sanity.sh Executable file
View File

@ -0,0 +1,86 @@
#!/bin/bash -e
#
# Perform a quick sanity test on a leader, drone, validator and client running
# locally on the same machine
#
cd "$(dirname "$0")"/..
source ci/upload_ci_artifact.sh
source multinode-demo/common.sh
./multinode-demo/setup.sh
backgroundCommands="drone leader validator validator-x"
pids=()
for cmd in $backgroundCommands; do
echo "--- Start $cmd"
rm -f log-"$cmd".txt
./multinode-demo/"$cmd".sh > log-"$cmd".txt 2>&1 &
declare pid=$!
pids+=("$pid")
echo "pid: $pid"
done
killBackgroundCommands() {
set +e
for pid in "${pids[@]}"; do
if kill "$pid"; then
wait "$pid"
else
echo -e "^^^ +++\\nWarning: unable to kill $pid"
fi
done
set -e
pids=()
}
shutdown() {
exitcode=$?
killBackgroundCommands
set +e
echo "--- Upload artifacts"
for cmd in $backgroundCommands; do
declare logfile=log-$cmd.txt
upload_ci_artifact "$logfile"
tail "$logfile"
done
exit $exitcode
}
trap shutdown EXIT INT
set -e
flag_error() {
echo Failed
echo "^^^ +++"
exit 1
}
echo "--- Wallet sanity"
(
set -x
multinode-demo/test/wallet-sanity.sh
) || flag_error
echo "--- Node count"
(
set -x
./multinode-demo/client.sh "$PWD" 3 -c --addr 127.0.0.1
) || flag_error
killBackgroundCommands
echo "--- Ledger verification"
(
set -x
$solana_ledger_tool --ledger "$SOLANA_CONFIG_DIR"/ledger verify
) || flag_error
echo +++
echo Ok
exit 0

18
ci/pr-snap.sh Executable file
View File

@ -0,0 +1,18 @@
#!/bin/bash -e
#
# Only run snap.sh for pull requests that modify files under /snap
#
cd "$(dirname "$0")"
if ./is-pr.sh; then
affected_files="$(buildkite-agent meta-data get affected_files)"
echo "Affected files in this PR: $affected_files"
if [[ ! ":$affected_files:" =~ :snap/ ]]; then
echo "Skipping snap build as no files under /snap were modified"
exit 0
fi
exec ./snap.sh
else
echo "Skipping snap build as this is not a pull request"
fi

19
ci/publish-crate.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/bash -e
cd "$(dirname "$0")/.."
if [[ -z "$BUILDKITE_TAG" ]]; then
# Skip publish if this is not a tagged release
exit 0
fi
if [[ -z "$CRATES_IO_TOKEN" ]]; then
echo CRATES_IO_TOKEN undefined
exit 1
fi
# TODO: Ensure the published version matches the contents of BUILDKITE_TAG
ci/docker-run.sh rust \
bash -exc "cargo package; cargo publish --token $CRATES_IO_TOKEN"
exit 0

19
ci/run-local.sh Executable file
View File

@ -0,0 +1,19 @@
#!/bin/bash -e
#
# Run the entire buildkite CI pipeline locally for pre-testing before sending a
# Github pull request
#
cd "$(dirname "$0")/.."
BKRUN=ci/node_modules/.bin/bkrun
if [[ ! -x $BKRUN ]]; then
(
set -x
cd ci/
npm install bkrun
)
fi
set -x
exec ./ci/node_modules/.bin/bkrun ci/buildkite.yml

16
ci/shellcheck.sh Executable file
View File

@ -0,0 +1,16 @@
#!/bin/bash -e
#
# Reference: https://github.com/koalaman/shellcheck/wiki/Directive
cd "$(dirname "$0")/.."
set -x
find . -name "*.sh" \
-not -regex ".*/.cargo/.*" \
-not -regex ".*/node_modules/.*" \
-not -regex ".*/target/.*" \
-print0 \
| xargs -0 \
ci/docker-run.sh koalaman/shellcheck --color=always --external-sources --shell=bash
exit 0

55
ci/snap.sh Executable file
View File

@ -0,0 +1,55 @@
#!/bin/bash -e
cd "$(dirname "$0")/.."
DRYRUN=
if [[ -z $BUILDKITE_BRANCH ]] || ./ci/is-pr.sh; then
DRYRUN="echo"
fi
# BUILDKITE_TAG is the normal environment variable set by Buildkite. However
# when this script is run from a triggered pipeline, TRIGGERED_BUILDKITE_TAG is
# used instead of BUILDKITE_TAG (due to Buildkite limitations that prevents
# BUILDKITE_TAG from propagating through to triggered pipelines)
if [[ -n "$BUILDKITE_TAG" || -n "$TRIGGERED_BUILDKITE_TAG" ]]; then
SNAP_CHANNEL=stable
elif [[ $BUILDKITE_BRANCH = master ]]; then
SNAP_CHANNEL=edge
else
SNAP_CHANNEL=beta
fi
if [[ -z $DRYRUN ]]; then
[[ -n $SNAPCRAFT_CREDENTIALS_KEY ]] || {
echo SNAPCRAFT_CREDENTIALS_KEY not defined
exit 1;
}
(
openssl aes-256-cbc -d \
-in ci/snapcraft.credentials.enc \
-out ci/snapcraft.credentials \
-k "$SNAPCRAFT_CREDENTIALS_KEY"
snapcraft login --with ci/snapcraft.credentials
) || {
rm -f ci/snapcraft.credentials;
exit 1
}
fi
set -x
echo --- checking for multilog
if [[ ! -x /usr/bin/multilog ]]; then
echo "multilog not found, install with: sudo apt-get install -y daemontools"
exit 1
fi
echo --- build: $SNAP_CHANNEL channel
snapcraft
source ci/upload_ci_artifact.sh
upload_ci_artifact solana_*.snap
echo --- publish: $SNAP_CHANNEL channel
$DRYRUN snapcraft push solana_*.snap --release $SNAP_CHANNEL

Binary file not shown.

13
ci/test-bench.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash -e
cd "$(dirname "$0")/.."
ci/version-check.sh stable
export RUST_BACKTRACE=1
_() {
echo "--- $*"
"$@"
}
_ cargo bench --verbose

45
ci/test-large-network.sh Executable file
View File

@ -0,0 +1,45 @@
#!/bin/bash -e
here=$(dirname "$0")
cd "$here"/..
if ! ci/version-check.sh stable; then
# This job doesn't run within a container, try once to upgrade tooling on a
# version check failure
rustup install stable
ci/version-check.sh stable
fi
export RUST_BACKTRACE=1
./fetch-perf-libs.sh
export LD_LIBRARY_PATH+=:$PWD
export RUST_LOG=multinode=info
if [[ $(ulimit -n) -lt 65000 ]]; then
echo 'Error: nofiles too small, run "ulimit -n 65000" to continue'
exit 1
fi
if [[ $(sysctl -n net.core.rmem_default) -lt 1610612736 ]]; then
echo 'Error: rmem_default too small, run "sudo sysctl -w net.core.rmem_default=1610612736" to continue'
exit 1
fi
if [[ $(sysctl -n net.core.rmem_max) -lt 1610612736 ]]; then
echo 'Error: rmem_max too small, run "sudo sysctl -w net.core.rmem_max=1610612736" to continue'
exit 1
fi
if [[ $(sysctl -n net.core.wmem_default) -lt 1610612736 ]]; then
echo 'Error: rmem_default too small, run "sudo sysctl -w net.core.wmem_default=1610612736" to continue'
exit 1
fi
if [[ $(sysctl -n net.core.wmem_max) -lt 1610612736 ]]; then
echo 'Error: rmem_max too small, run "sudo sysctl -w net.core.wmem_max=1610612736" to continue'
exit 1
fi
set -x
exec cargo test --release --features=erasure test_multi_node_dynamic_network -- --ignored

31
ci/test-nightly.sh Executable file
View File

@ -0,0 +1,31 @@
#!/bin/bash -e
cd "$(dirname "$0")/.."
ci/version-check.sh nightly
export RUST_BACKTRACE=1
_() {
echo "--- $*"
"$@"
}
_ cargo build --verbose --features unstable
_ cargo test --verbose --features unstable
_ cargo clippy -- --deny=warnings
exit 0
# Coverage disabled (see issue #433)
_ cargo cov test
_ cargo cov report
echo --- Coverage report:
ls -l target/cov/report/index.html
if [[ -z "$CODECOV_TOKEN" ]]; then
echo CODECOV_TOKEN undefined
else
bash <(curl -s https://codecov.io/bash) -x 'llvm-cov-6.0 gcov'
fi

30
ci/test-stable-perf.sh Executable file
View File

@ -0,0 +1,30 @@
#!/bin/bash -e
cd "$(dirname "$0")/.."
if ! ci/version-check.sh stable; then
# This job doesn't run within a container, try once to upgrade tooling on a
# version check failure
rustup install stable
ci/version-check.sh stable
fi
export RUST_BACKTRACE=1
./fetch-perf-libs.sh
export LD_LIBRARY_PATH=$PWD:/usr/local/cuda/lib64
export PATH=$PATH:/usr/local/cuda/bin
_() {
echo "--- $*"
"$@"
}
_ cargo test --features=cuda,erasure
echo --- ci/localnet-sanity.sh
(
set -x
# Assume |cargo build| has populated target/debug/ successfully.
export PATH=$PWD/target/debug:$PATH
USE_INSTALL=1 ci/localnet-sanity.sh
)

25
ci/test-stable.sh Executable file
View File

@ -0,0 +1,25 @@
#!/bin/bash -e
cd "$(dirname "$0")/.."
ci/version-check.sh stable
export RUST_BACKTRACE=1
_() {
echo "--- $*"
"$@"
}
_ cargo fmt -- --check
_ cargo build --verbose
_ cargo test --verbose
echo --- ci/localnet-sanity.sh
(
set -x
# Assume |cargo build| has populated target/debug/ successfully.
export PATH=$PWD/target/debug:$PATH
USE_INSTALL=1 ci/localnet-sanity.sh
)
_ ci/audit.sh

519
ci/testnet-deploy.sh Executable file
View File

@ -0,0 +1,519 @@
#!/bin/bash -e
#
# Deploys the Solana software running on the testnet full nodes
#
# This script must be run by a user/machine that has successfully authenticated
# with GCP and has sufficient permission.
#
here=$(dirname "$0")
metrics_write_datapoint="$here"/../multinode-demo/metrics_write_datapoint.sh
# TODO: Switch over to rolling updates
ROLLING_UPDATE=false
#ROLLING_UPDATE=true
if [[ -z $SOLANA_METRICS_CONFIG ]]; then
echo Error: SOLANA_METRICS_CONFIG environment variable is unset
exit 1
fi
# The SOLANA_METRICS_CONFIG environment variable is formatted as a
# comma-delimited list of parameters. All parameters are optional.
#
# Example:
# export SOLANA_METRICS_CONFIG="host=<metrics host>,db=<database name>,u=<username>,p=<password>"
#
configure_metrics() {
[[ -n $SOLANA_METRICS_CONFIG ]] || return 0
declare metrics_params
IFS=',' read -r -a metrics_params <<< "$SOLANA_METRICS_CONFIG"
for param in "${metrics_params[@]}"; do
IFS='=' read -r -a pair <<< "$param"
if [[ ${#pair[@]} != 2 ]]; then
echo Error: invalid metrics parameter: "$param" >&2
else
declare name="${pair[0]}"
declare value="${pair[1]}"
case "$name" in
host)
export INFLUX_HOST="$value"
echo INFLUX_HOST="$INFLUX_HOST" >&2
;;
db)
export INFLUX_DATABASE="$value"
echo INFLUX_DATABASE="$INFLUX_DATABASE" >&2
;;
u)
export INFLUX_USERNAME="$value"
echo INFLUX_USERNAME="$INFLUX_USERNAME" >&2
;;
p)
export INFLUX_PASSWORD="$value"
echo INFLUX_PASSWORD="********" >&2
;;
*)
echo Error: Unknown metrics parameter name: "$name" >&2
;;
esac
fi
done
}
configure_metrics
# Default to edge channel. To select the beta channel:
# export SOLANA_SNAP_CHANNEL=beta
if [[ -z $SOLANA_SNAP_CHANNEL ]]; then
SOLANA_SNAP_CHANNEL=edge
fi
# Select default network URL based on SOLANA_SNAP_CHANNEL if SOLANA_NET_ENTRYPOINT is
# unspecified
if [[ -z $SOLANA_NET_ENTRYPOINT ]]; then
case $SOLANA_SNAP_CHANNEL in
edge)
SOLANA_NET_ENTRYPOINT=master.testnet.solana.com
unset SOLANA_NET_NAME
;;
beta)
SOLANA_NET_ENTRYPOINT=testnet.solana.com
unset SOLANA_NET_NAME
;;
*)
echo Error: Unknown SOLANA_SNAP_CHANNEL=$SOLANA_SNAP_CHANNEL
exit 1
;;
esac
fi
if [[ -z $SOLANA_NET_NAME ]]; then
SOLANA_NET_NAME=${SOLANA_NET_ENTRYPOINT//./-}
fi
: ${SOLANA_NET_NAME:?$SOLANA_NET_ENTRYPOINT}
netBasename=${SOLANA_NET_NAME/-*/}
if [[ $netBasename != testnet ]]; then
netBasename="testnet-$netBasename"
fi
# Figure installation command
SNAP_INSTALL_CMD="\
for i in {1..3}; do \
sudo snap install solana --$SOLANA_SNAP_CHANNEL --devmode && break;
sleep 1; \
done \
"
LOCAL_SNAP=$1
if [[ -n $LOCAL_SNAP ]]; then
if [[ ! -f $LOCAL_SNAP ]]; then
echo "Error: $LOCAL_SNAP is not a file"
exit 1
fi
SNAP_INSTALL_CMD="sudo snap install ~/solana_local.snap --devmode --dangerous"
fi
SNAP_INSTALL_CMD="sudo snap remove solana; $SNAP_INSTALL_CMD"
EARLYOOM_INSTALL_CMD="\
wget --retry-connrefused --waitretry=1 \
--read-timeout=20 --timeout=15 --tries=5 \
-O install-earlyoom.sh \
https://raw.githubusercontent.com/solana-labs/solana/v0.7/ci/install-earlyoom.sh; \
bash install-earlyoom.sh \
"
SNAP_INSTALL_CMD="$EARLYOOM_INSTALL_CMD; $SNAP_INSTALL_CMD"
# `export SKIP_INSTALL=1` to reset the network without reinstalling the snap
if [[ -n $SKIP_INSTALL ]]; then
SNAP_INSTALL_CMD="echo Install skipped"
fi
echo "+++ Configuration for $netBasename"
publicUrl="$SOLANA_NET_ENTRYPOINT"
if [[ $publicUrl = testnet.solana.com ]]; then
publicIp="" # Use default value
else
publicIp=$(dig +short $publicUrl | head -n1)
fi
echo "Network name: $SOLANA_NET_NAME"
echo "Network entry point URL: $publicUrl ($publicIp)"
echo "Snap channel: $SOLANA_SNAP_CHANNEL"
echo "Install command: $SNAP_INSTALL_CMD"
echo "Setup args: $SOLANA_SETUP_ARGS"
[[ -z $LOCAL_SNAP ]] || echo "Local snap: $LOCAL_SNAP"
vmlist=() # Each array element is formatted as "class:vmName:vmZone:vmPublicIp"
vm_exec() {
declare vmName=$1
declare vmZone=$2
declare vmPublicIp=$3
declare message=$4
declare cmd=$5
echo "--- $message $vmName in zone $vmZone ($vmPublicIp)"
ssh -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
testnet-deploy@"$vmPublicIp" "$cmd"
}
#
# vm_foreach [cmd] [extra args to cmd]
# where
# cmd - the command to execute on each VM
# The command will receive three fixed arguments, followed by any
# additionl arguments supplied to vm_foreach:
# vmName - GCP name of the VM
# vmZone - The GCP zone the VM is located in
# vmPublicIp - The public IP address of this VM
# vmClass - The 'class' of this VM
# count - Monotonically increasing count for each
# invocation of cmd, starting at 1
# ... - Extra args to cmd..
#
#
vm_foreach() {
declare cmd=$1
shift
declare count=1
for info in "${vmlist[@]}"; do
declare vmClass vmName vmZone vmPublicIp
IFS=: read -r vmClass vmName vmZone vmPublicIp < <(echo "$info")
eval "$cmd" "$vmName" "$vmZone" "$vmPublicIp" "$vmClass" "$count" "$@"
count=$((count + 1))
done
}
#
# vm_foreach_in_class [class] [cmd]
# where
# class - the desired VM class to operate on
# cmd - the command to execute on each VM in the desired class.
# The command will receive three arguments:
# vmName - GCP name of the VM
# vmZone - The GCP zone the VM is located in
# vmPublicIp - The public IP address of this VM
# count - Monotonically increasing count for each
# invocation of cmd, starting at 1
#
#
_run_cmd_if_class() {
declare vmName=$1
declare vmZone=$2
declare vmPublicIp=$3
declare vmClass=$4
declare count=$5
declare class=$6
declare cmd=$7
if [[ $class = "$vmClass" ]]; then
eval "$cmd" "$vmName" "$vmZone" "$vmPublicIp" "$count"
fi
}
vm_foreach_in_class() {
declare class=$1
declare cmd=$2
vm_foreach _run_cmd_if_class "$1" "$2"
}
#
# Load all VMs matching the specified filter and tag them with the specified
# class into the `vmlist` array.
findVms() {
declare class="$1"
declare filter="$2"
gcloud compute instances list --filter="$filter"
while read -r vmName vmZone vmPublicIp status; do
if [[ $status != RUNNING ]]; then
echo "Warning: $vmName is not RUNNING, ignoring it."
continue
fi
vmlist+=("$class:$vmName:$vmZone:$vmPublicIp")
done < <(gcloud compute instances list \
--filter="$filter" \
--format 'value(name,zone,networkInterfaces[0].accessConfigs[0].natIP,status)')
}
wait_for_pids() {
echo "--- Waiting for $*"
for pid in "${pids[@]}"; do
declare ok=true
wait "$pid" || ok=false
cat "log-$pid.txt"
if ! $ok; then
echo ^^^ +++
exit 1
fi
rm "log-$pid.txt"
done
}
delete_unreachable_validators() {
declare vmName=$1
declare vmZone=$2
declare vmPublicIp=$3
touch "log-$vmName.txt"
(
SECONDS=0
if ! vm_exec "$vmName" "$vmZone" "$vmPublicIp" "Checking $vmName" uptime; then
echo "^^^ +++"
# Validators are managed by a Compute Engine Instance Group, so deleting
# one will just cause a new one to be spawned.
echo "Warning: $vmName is unreachable, deleting it"
gcloud compute instances delete "$vmName" --zone "$vmZone"
fi
echo "validator checked in ${SECONDS} seconds"
) >> "log-$vmName.txt" 2>&1 &
declare pid=$!
# Rename log file so it can be discovered later by $pid
mv "log-$vmName.txt" "log-$pid.txt"
pids+=("$pid")
}
echo "Validator nodes (unverified):"
findVms validator "name~^$SOLANA_NET_NAME-validator-"
pids=()
vm_foreach_in_class validator delete_unreachable_validators
wait_for_pids validator sanity check
vmlist=()
echo "Leader node:"
findVms leader "name=$SOLANA_NET_NAME"
[[ ${#vmlist[@]} = 1 ]] || {
echo "Unable to find $SOLANA_NET_NAME"
exit 1
}
echo "Client node(s):"
findVms client "name~^$SOLANA_NET_NAME-client"
echo "Validator nodes:"
findVms validator "name~^$SOLANA_NET_NAME-validator-"
fullnode_count=0
inc_fullnode_count() {
fullnode_count=$((fullnode_count + 1))
}
vm_foreach_in_class leader inc_fullnode_count
vm_foreach_in_class validator inc_fullnode_count
# Add "network stopping" datapoint
$metrics_write_datapoint "testnet-deploy,name=$netBasename stop=1"
client_start() {
declare vmName=$1
declare vmZone=$2
declare vmPublicIp=$3
declare count=$4
vm_exec "$vmName" "$vmZone" "$vmPublicIp" \
"Starting client $count:" \
"\
set -x;
snap info solana; \
sudo snap get solana; \
threadCount=\$(nproc); \
if [[ \$threadCount -gt 4 ]]; then threadCount=4; fi; \
tmux kill-session -t solana; \
tmux new -s solana -d \" \
set -x; \
sudo rm /tmp/solana.log; \
while : ; do \
/snap/bin/solana.bench-tps $SOLANA_NET_ENTRYPOINT $fullnode_count --loop -s 600 --sustained -t \$threadCount 2>&1 | tee -a /tmp/solana.log; \
echo 'https://metrics.solana.com:8086/write?db=${INFLUX_DATABASE}&u=${INFLUX_USERNAME}&p=${INFLUX_PASSWORD}' \
| xargs curl --max-time 5 -XPOST --data-binary 'testnet-deploy,name=$netBasename clientexit=1'; \
echo Error: bench-tps should never exit | tee -a /tmp/solana.log; \
done; \
bash \
\"; \
sleep 2; \
tmux capture-pane -t solana -p -S -100; \
tail /tmp/solana.log; \
"
}
client_stop() {
declare vmName=$1
declare vmZone=$2
declare vmPublicIp=$3
declare count=$4
touch "log-$vmName.txt"
(
SECONDS=0
vm_exec "$vmName" "$vmZone" "$vmPublicIp" \
"Stopping client $vmName ($count):" \
"\
set -x;
tmux list-sessions; \
tmux capture-pane -t solana -p; \
tmux kill-session -t solana; \
$SNAP_INSTALL_CMD; \
sudo snap set solana metrics-config=$SOLANA_METRICS_CONFIG \
rust-log=$RUST_LOG \
default-metrics-rate=$SOLANA_DEFAULT_METRICS_RATE \
; \
"
echo "Client stopped in ${SECONDS} seconds"
) >> "log-$vmName.txt" 2>&1 &
declare pid=$!
# Rename log file so it can be discovered later by $pid
mv "log-$vmName.txt" "log-$pid.txt"
pids+=("$pid")
}
fullnode_start() {
declare class=$1
declare vmName=$2
declare vmZone=$3
declare vmPublicIp=$4
declare count=$5
touch "log-$vmName.txt"
(
SECONDS=0
commonNodeConfig="\
rust-log=$RUST_LOG \
default-metrics-rate=$SOLANA_DEFAULT_METRICS_RATE \
metrics-config=$SOLANA_METRICS_CONFIG \
setup-args=$SOLANA_SETUP_ARGS \
"
if [[ $class = leader ]]; then
nodeConfig="mode=leader+drone $commonNodeConfig"
if [[ -n $SOLANA_CUDA ]]; then
nodeConfig="$nodeConfig enable-cuda=1"
fi
else
nodeConfig="mode=validator leader-address=$publicIp $commonNodeConfig"
fi
vm_exec "$vmName" "$vmZone" "$vmPublicIp" "Starting $class $count:" \
"\
set -ex; \
logmarker='solana deploy $(date)/$RANDOM'; \
logger \"\$logmarker\"; \
$SNAP_INSTALL_CMD; \
sudo snap set solana $nodeConfig; \
snap info solana; \
sudo snap get solana; \
echo Slight delay to get more syslog output; \
sleep 2; \
sudo grep -Pzo \"\$logmarker(.|\\n)*\" /var/log/syslog \
"
echo "Succeeded in ${SECONDS} seconds"
) >> "log-$vmName.txt" 2>&1 &
declare pid=$!
# Rename log file so it can be discovered later by $pid
mv "log-$vmName.txt" "log-$pid.txt"
pids+=("$pid")
}
leader_start() {
fullnode_start leader "$@"
}
validator_start() {
fullnode_start validator "$@"
}
fullnode_stop() {
declare vmName=$1
declare vmZone=$2
declare vmPublicIp=$3
declare count=$4
touch "log-$vmName.txt"
(
SECONDS=0
# Try to ping the machine first. When a machine (validator) is restarted,
# there can be a delay between when the instance is reported as RUNNING and when
# it's reachable over the network
timeout 30s bash -c "set -o pipefail; until ping -c 3 $vmPublicIp | tr - _; do echo .; done"
vm_exec "$vmName" "$vmZone" "$vmPublicIp" "Shutting down" "\
if snap list solana; then \
sudo snap set solana mode=; \
fi"
echo "Succeeded in ${SECONDS} seconds"
) >> "log-$vmName.txt" 2>&1 &
declare pid=$!
# Rename log file so it can be discovered later by $pid
mv "log-$vmName.txt" "log-$pid.txt"
pids+=("$pid")
}
if [[ -n $LOCAL_SNAP ]]; then
echo "--- Transferring $LOCAL_SNAP to node(s)"
transfer_local_snap() {
declare vmName=$1
declare vmZone=$2
declare vmPublicIp=$3
declare vmClass=$4
declare count=$5
echo "--- $vmName in zone $vmZone ($count)"
SECONDS=0
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
"$LOCAL_SNAP" testnet-deploy@"$vmPublicIp":solana_local.snap
echo "Succeeded in ${SECONDS} seconds"
}
vm_foreach transfer_local_snap
fi
echo "--- Stopping client node(s)"
pids=()
vm_foreach_in_class client client_stop
client_stop_pids=("${pids[@]}")
if ! $ROLLING_UPDATE; then
pids=()
echo "--- Shutting down all full nodes"
vm_foreach_in_class leader fullnode_stop
vm_foreach_in_class validator fullnode_stop
wait_for_pids fullnode shutdown
fi
pids=()
echo --- Starting leader node
vm_foreach_in_class leader leader_start
wait_for_pids leader
pids=()
echo --- Starting validator nodes
vm_foreach_in_class validator validator_start
wait_for_pids validators
echo "--- $publicUrl sanity test"
if [[ -z $CI ]]; then
# TODO: ssh into a node and run testnet-sanity.sh there. It's not safe to
# assume the correct Snap is installed on the current non-CI machine
echo Skipped for non-CI deploy
snapVersion=unknown
else
(
set -x
USE_SNAP=1 ci/testnet-sanity.sh $publicUrl $fullnode_count
)
IFS=\ read -r _ snapVersion _ < <(snap info solana | grep "^installed:")
snapVersion=${snapVersion/0+git./}
fi
pids=("${client_stop_pids[@]}")
wait_for_pids client shutdown
vm_foreach_in_class client client_start
# Add "network started" datapoint
$metrics_write_datapoint "testnet-deploy,name=$netBasename start=1,version=\"$snapVersion\""
exit 0

77
ci/testnet-sanity.sh Executable file
View File

@ -0,0 +1,77 @@
#!/bin/bash -e
#
# Perform a quick sanity test on the specific testnet
#
cd "$(dirname "$0")/.."
source multinode-demo/common.sh
NET_URL=$1
if [[ -z $NET_URL ]]; then
NET_URL=testnet.solana.com
fi
EXPECTED_NODE_COUNT=$2
if [[ -z $EXPECTED_NODE_COUNT ]]; then
EXPECTED_NODE_COUNT=50
fi
echo "--- $NET_URL: verify ledger"
if [[ -z $NO_LEDGER_VERIFY ]]; then
if [[ -d /var/snap/solana/current/config/ledger ]]; then
# Note: here we assume this script is actually running on the leader node...
(
set -x
sudo cp -r /var/snap/solana/current/config/ledger /var/snap/solana/current/config/ledger-verify-$$
sudo solana.ledger-tool --ledger /var/snap/solana/current/config/ledger-verify-$$ verify
)
else
echo "^^^ +++"
echo "Ledger verify skipped"
fi
else
echo "^^^ +++"
echo "Ledger verify skipped (NO_LEDGER_VERIFY defined)"
fi
echo "--- $NET_URL: wallet sanity"
(
set -x
multinode-demo/test/wallet-sanity.sh $NET_URL
)
echo "--- $NET_URL: node count"
if [[ -n "$USE_SNAP" ]]; then
# TODO: Merge client.sh functionality into solana-bench-tps proper and
# remove this USE_SNAP case
cmd=$solana_bench_tps
else
cmd=multinode-demo/client.sh
fi
(
set -x
$cmd $NET_URL $EXPECTED_NODE_COUNT -c
)
echo "--- $NET_URL: validator sanity"
if [[ -z $NO_VALIDATOR_SANITY ]]; then
(
./multinode-demo/setup.sh -t validator
set -e pipefail
timeout 10s ./multinode-demo/validator.sh "$NET_URL" 2>&1 | tee validator.log
)
wc -l validator.log
if grep -C100 panic validator.log; then
echo "^^^ +++"
echo "Panic observed"
exit 1
else
echo "Validator log looks ok"
fi
else
echo "^^^ +++"
echo "Validator sanity disabled (NO_VALIDATOR_SANITY defined)"
fi
exit 0

18
ci/upload_ci_artifact.sh Normal file
View File

@ -0,0 +1,18 @@
# |source| me
upload_ci_artifact() {
echo "--- artifact: $1"
if [[ -r "$1" ]]; then
ls -l "$1"
if ${BUILDKITE:-false}; then
(
set -x
buildkite-agent artifact upload "$1"
)
fi
else
echo ^^^ +++
echo "$1 not found"
fi
}

35
ci/version-check.sh Executable file
View File

@ -0,0 +1,35 @@
#!/bin/bash -e
require() {
declare expectedProgram="$1"
declare expectedVersion="$2"
read -r program version _ < <($expectedProgram -V)
declare ok=true
[[ $program = "$expectedProgram" ]] || ok=false
[[ $version =~ $expectedVersion ]] || ok=false
echo "Found $program $version"
if ! $ok; then
echo Error: expected "$expectedProgram $expectedVersion"
exit 1
fi
}
case ${1:-stable} in
nightly)
require rustc 1.30.[0-9]+-nightly
require cargo 1.29.[0-9]+-nightly
;;
stable)
require rustc 1.28.[0-9]+
require cargo 1.28.[0-9]+
;;
*)
echo Error: unknown argument: "$1"
exit 1
;;
esac
exit 0

15
doc/consensus.msc Normal file
View File

@ -0,0 +1,15 @@
msc {
client,leader,verifier_a,verifier_b,verifier_c;
client=>leader [ label = "SUBMIT" ] ;
leader=>client [ label = "CONFIRMED" ] ;
leader=>verifier_a [ label = "CONFIRMED" ] ;
leader=>verifier_b [ label = "CONFIRMED" ] ;
leader=>verifier_c [ label = "CONFIRMED" ] ;
verifier_a=>leader [ label = "VERIFIED" ] ;
verifier_b=>leader [ label = "VERIFIED" ] ;
leader=>client [ label = "FINALIZED" ] ;
leader=>verifier_a [ label = "FINALIZED" ] ;
leader=>verifier_b [ label = "FINALIZED" ] ;
leader=>verifier_c [ label = "FINALIZED" ] ;
}

35
doc/testnet.md Normal file
View File

@ -0,0 +1,35 @@
# TestNet debugging info
Currently we have two testnets, 'perf' and 'master', both on the master branch of the solana repo. Deploys happen
at the top of every hour with the latest code. 'perf' has more cores for the client machine to flood the network
with transactions until failure.
## Deploy process
They are deployed with the `ci/testnet-deploy.sh` script. There is a scheduled buildkite job which runs to do the deploy,
look at `testnet-deploy` to see the agent which ran it and the logs. There is also a manual job to do the deploy manually..
Validators are selected based on their machine name and everyone gets the binaries installed from snap.
## Where are the testnet logs?
For the client they are put in `/tmp/solana`; for validators and leaders they are in `/var/snap/solana/current/`.
You can also see the backtrace of the client by ssh'ing into the client node and doing:
```bash
$ sudo -u testnet-deploy
$ tmux attach -t solana
```
## How do I reset the testnet?
Through buildkite.
## How can I scale the tx generation rate?
Increase the TX rate by increasing the number of cores on the client machine which is running
`bench-tps` or run multiple clients. Decrease by lowering cores or using the rayon env
variable `RAYON_NUM_THREADS=<xx>`
## How can I test a change on the testnet?
Currently, a merged PR is the only way to test a change on the testnet.

37
fetch-perf-libs.sh Executable file
View File

@ -0,0 +1,37 @@
#!/bin/bash -e
if [[ $(uname) != Linux ]]; then
echo Performance libraries are only available for Linux
exit 1
fi
if [[ $(uname -m) != x86_64 ]]; then
echo Performance libraries are only available for x86_64 architecture
exit 1
fi
(
set -x
curl -o solana-perf.tgz \
https://solana-perf.s3.amazonaws.com/v0.8.0/x86_64-unknown-linux-gnu/solana-perf.tgz
tar zxvf solana-perf.tgz
)
if [[ -r /usr/local/cuda/version.txt && -r cuda-version.txt ]]; then
if ! diff /usr/local/cuda/version.txt cuda-version.txt > /dev/null; then
echo ==============================================
echo Warning: possible CUDA version mismatch
echo
echo "Expected version: $(cat cuda-version.txt)"
echo "Detected version: $(cat /usr/local/cuda/version.txt)"
echo ==============================================
fi
else
echo ==============================================
echo Warning: unable to validate CUDA version
echo ==============================================
fi
echo "Downloaded solana-perf version: $(cat solana-perf-HEAD.txt)"
exit 0

66
multinode-demo/client.sh Executable file
View File

@ -0,0 +1,66 @@
#!/bin/bash -e
#
USAGE=" usage: $0 [leader_url] [num_nodes] [--loop] [extra args]
leader_url URL to the leader (defaults to ..)
num_nodes Minimum number of nodes to look for while converging
--loop Add this flag to cause the program to loop infinitely
\"extra args\" Any additional arguments are pass along to solana-bench-tps
"
here=$(dirname "$0")
# shellcheck source=multinode-demo/common.sh
source "$here"/common.sh
leader=$1
if [[ -n $leader ]]; then
if [[ $leader == "-h" || $leader == "--help" ]]; then
echo "$USAGE"
exit 0
fi
shift
else
if [[ -d "$SNAP" ]]; then
leader=testnet.solana.com # Default to testnet when running as a Snap
else
leader=$here/.. # Default to local solana repo
fi
fi
count=$1
if [[ -n $count ]]; then
shift
else
count=1
fi
loop=
if [[ $1 = --loop ]]; then
loop=1
shift
fi
rsync_leader_url=$(rsync_url "$leader")
(
set -x
mkdir -p "$SOLANA_CONFIG_CLIENT_DIR"
$rsync -vPz "$rsync_leader_url"/config/leader.json "$SOLANA_CONFIG_CLIENT_DIR"/
client_json="$SOLANA_CONFIG_CLIENT_DIR"/client.json
[[ -r $client_json ]] || $solana_keygen -o "$client_json"
)
iteration=0
set -x
while true; do
$solana_bench_tps \
-n "$count" \
-l "$SOLANA_CONFIG_CLIENT_DIR"/leader.json \
-k "$SOLANA_CONFIG_CLIENT_DIR"/client.json \
"$@"
[[ -n $loop ]] || exit 0
iteration=$((iteration + 1))
echo ------------------------------------------------------------------------
echo "Iteration: $iteration"
echo ------------------------------------------------------------------------
done

213
multinode-demo/common.sh Normal file
View File

@ -0,0 +1,213 @@
# |source| this file
#
# Disable complaints about unused variables in this file:
# shellcheck disable=2034
rsync=rsync
leader_logger="cat"
validator_logger="cat"
drone_logger="cat"
if [[ $(uname) != Linux ]]; then
# Protect against unsupported configurations to prevent non-obvious errors
# later. Arguably these should be fatal errors but for now prefer tolerance.
if [[ -n $USE_SNAP ]]; then
echo "Warning: Snap is not supported on $(uname)"
USE_SNAP=
fi
if [[ -n $SOLANA_CUDA ]]; then
echo "Warning: CUDA is not supported on $(uname)"
SOLANA_CUDA=
fi
fi
if [[ -d $SNAP ]]; then # Running inside a Linux Snap?
solana_program() {
declare program="$1"
if [[ "$program" = wallet || "$program" = bench-tps ]]; then
# TODO: Merge wallet.sh/client.sh functionality into
# solana-wallet/solana-demo-client proper and remove this special case
printf "%s/bin/solana-%s" "$SNAP" "$program"
else
printf "%s/command-%s.wrapper" "$SNAP" "$program"
fi
}
rsync="$SNAP"/bin/rsync
multilog="$SNAP/bin/multilog t s16777215 n200"
leader_logger="$multilog $SNAP_DATA/leader"
validator_logger="$multilog t $SNAP_DATA/validator"
drone_logger="$multilog $SNAP_DATA/drone"
# Create log directories manually to prevent multilog from creating them as
# 0700
mkdir -p "$SNAP_DATA"/{drone,leader,validator}
SOLANA_METRICS_CONFIG="$(snapctl get metrics-config)"
SOLANA_DEFAULT_METRICS_RATE="$(snapctl get default-metrics-rate)"
export SOLANA_DEFAULT_METRICS_RATE
SOLANA_CUDA="$(snapctl get enable-cuda)"
RUST_LOG="$(snapctl get rust-log)"
elif [[ -n $USE_SNAP ]]; then # Use the Linux Snap binaries
solana_program() {
declare program="$1"
printf "solana.%s" "$program"
}
elif [[ -n $USE_INSTALL ]]; then # Assume |cargo install| was run
solana_program() {
declare program="$1"
printf "solana-%s" "$program"
}
# CUDA was/wasn't selected at build time, can't affect CUDA state here
unset SOLANA_CUDA
else
solana_program() {
declare program="$1"
declare features=""
if [[ "$program" =~ ^(.*)-cuda$ ]]; then
program=${BASH_REMATCH[1]}
features="--features=cuda"
fi
if [[ -z $DEBUG ]]; then
maybe_release=--release
fi
printf "cargo run $maybe_release --bin solana-%s %s -- " "$program" "$features"
}
if [[ -n $SOLANA_CUDA ]]; then
# shellcheck disable=2154 # 'here' is referenced but not assigned
if [[ -z $here ]]; then
echo "|here| is not defined"
exit 1
fi
# Locate perf libs downloaded by |./fetch-perf-libs.sh|
LD_LIBRARY_PATH=$(cd "$here" && dirname "$PWD"):$LD_LIBRARY_PATH
export LD_LIBRARY_PATH
fi
fi
solana_bench_tps=$(solana_program bench-tps)
solana_wallet=$(solana_program wallet)
solana_drone=$(solana_program drone)
solana_fullnode=$(solana_program fullnode)
solana_fullnode_config=$(solana_program fullnode-config)
solana_fullnode_cuda=$(solana_program fullnode-cuda)
solana_genesis=$(solana_program genesis)
solana_keygen=$(solana_program keygen)
solana_ledger_tool=$(solana_program ledger-tool)
export RUST_LOG=${RUST_LOG:-solana=info} # if RUST_LOG is unset, default to info
export RUST_BACKTRACE=1
# The SOLANA_METRICS_CONFIG environment variable is formatted as a
# comma-delimited list of parameters. All parameters are optional.
#
# Example:
# export SOLANA_METRICS_CONFIG="host=<metrics host>,db=<database name>,u=<username>,p=<password>"
#
configure_metrics() {
[[ -n $SOLANA_METRICS_CONFIG ]] || return 0
declare metrics_params
IFS=',' read -r -a metrics_params <<< "$SOLANA_METRICS_CONFIG"
for param in "${metrics_params[@]}"; do
IFS='=' read -r -a pair <<< "$param"
if [[ ${#pair[@]} != 2 ]]; then
echo Error: invalid metrics parameter: "$param" >&2
else
declare name="${pair[0]}"
declare value="${pair[1]}"
case "$name" in
host)
export INFLUX_HOST="$value"
echo INFLUX_HOST="$INFLUX_HOST" >&2
;;
db)
export INFLUX_DATABASE="$value"
echo INFLUX_DATABASE="$INFLUX_DATABASE" >&2
;;
u)
export INFLUX_USERNAME="$value"
echo INFLUX_USERNAME="$INFLUX_USERNAME" >&2
;;
p)
export INFLUX_PASSWORD="$value"
echo INFLUX_PASSWORD="********" >&2
;;
*)
echo Error: Unknown metrics parameter name: "$name" >&2
;;
esac
fi
done
}
configure_metrics
tune_networking() {
# Skip in CI
[[ -z $CI ]] || return 0
# Reference: https://medium.com/@CameronSparr/increase-os-udp-buffers-to-improve-performance-51d167bb1360
if [[ $(uname) = Linux ]]; then
(
set -x +e
# test the existence of the sysctls before trying to set them
# go ahead and return true and don't exit if these calls fail
sysctl net.core.rmem_max 2>/dev/null 1>/dev/null &&
sudo sysctl -w net.core.rmem_max=26214400 1>/dev/null 2>/dev/null
sysctl net.core.rmem_default 2>/dev/null 1>/dev/null &&
sudo sysctl -w net.core.rmem_default=26214400 1>/dev/null 2>/dev/null
) || true
fi
if [[ $(uname) = Darwin ]]; then
(
if [[ $(sysctl net.inet.udp.maxdgram | cut -d\ -f2) != 65535 ]]; then
echo "Adjusting maxdgram to allow for large UDP packets, see BLOB_SIZE in src/packet.rs:"
set -x
sudo sysctl net.inet.udp.maxdgram=65535
fi
)
fi
}
oom_score_adj() {
declare pid=$1
declare score=$2
if [[ $(uname) != Linux ]]; then
return
fi
echo "$score" > "/proc/$pid/oom_score_adj" || true
declare currentScore
currentScore=$(cat "/proc/$pid/oom_score_adj" || true)
if [[ $score != "$currentScore" ]]; then
echo "Failed to set oom_score_adj to $score for pid $pid (current score: $currentScore)"
fi
}
SOLANA_CONFIG_DIR=${SNAP_DATA:-$PWD}/config
SOLANA_CONFIG_PRIVATE_DIR=${SNAP_DATA:-$PWD}/config-private
SOLANA_CONFIG_VALIDATOR_DIR=${SNAP_DATA:-$PWD}/config-validator
SOLANA_CONFIG_CLIENT_DIR=${SNAP_USER_DATA:-$PWD}/config-client
rsync_url() { # adds the 'rsync://` prefix to URLs that need it
declare url="$1"
if [[ $url =~ ^.*:.*$ ]]; then
# assume remote-shell transport when colon is present, use $url unmodified
echo "$url"
return 0
fi
if [[ -d $url ]]; then
# assume local directory if $url is a valid directory, use $url unmodified
echo "$url"
return 0
fi
# Default to rsync:// URL
echo "rsync://$url"
}

46
multinode-demo/drone.sh Executable file
View File

@ -0,0 +1,46 @@
#!/bin/bash
#
# usage: $0 <rsync network path to solana repo on leader machine>
#
here=$(dirname "$0")
# shellcheck source=multinode-demo/common.sh
source "$here"/common.sh
SOLANA_CONFIG_DIR="$SOLANA_CONFIG_DIR"-drone
if [[ -d "$SNAP" ]]; then
# Exit if mode is not yet configured
# (typically the case after the Snap is first installed)
[[ -n "$(snapctl get mode)" ]] || exit 0
# Select leader from the Snap configuration
leader_address="$(snapctl get leader-address)"
if [[ -z "$leader_address" ]]; then
# Assume drone is running on the same node as the leader by default
leader_address="localhost"
fi
leader="$leader_address"
else
leader=${1:-${here}/..} # Default to local tree for data
fi
[[ -f "$SOLANA_CONFIG_PRIVATE_DIR"/mint.json ]] || {
echo "$SOLANA_CONFIG_PRIVATE_DIR/mint.json not found, create it by running:"
echo
echo " ${here}/setup.sh -t leader"
exit 1
}
rsync_leader_url=$(rsync_url "$leader")
set -ex
mkdir -p "$SOLANA_CONFIG_DIR"
$rsync -vPz "$rsync_leader_url"/config/leader.json "$SOLANA_CONFIG_DIR"/
trap 'kill "$pid" && wait "$pid"' INT TERM
$solana_drone \
-l "$SOLANA_CONFIG_DIR"/leader.json -k "$SOLANA_CONFIG_PRIVATE_DIR"/mint.json \
> >($drone_logger) 2>&1 &
pid=$!
oom_score_adj "$pid" 1000
wait "$pid"

80
multinode-demo/gce_multinode.sh Executable file
View File

@ -0,0 +1,80 @@
#!/bin/bash
command=$1
prefix=
num_nodes=
out_file=
image_name="ubuntu-16-04-cuda-9-2-new"
shift
usage() {
exitcode=0
if [[ -n "$1" ]]; then
exitcode=1
echo "Error: $*"
fi
cat <<EOF
usage: $0 <create|delete> <-p prefix> <-n num_nodes> <-o file> [-i image-name]
Manage a GCE multinode network
create|delete - Create or delete the network
-p prefix - A common prefix for node names, to avoid collision
-n num_nodes - Number of nodes
-o out_file - Used for create option. Outputs an array of IP addresses
of new nodes to the file
-i image_name - Existing image on GCE (default $image_name)
EOF
exit $exitcode
}
while getopts "h?p:i:n:o:" opt; do
case $opt in
h | \?)
usage
;;
p)
prefix=$OPTARG
;;
i)
image_name=$OPTARG
;;
o)
out_file=$OPTARG
;;
n)
num_nodes=$OPTARG
;;
*)
usage "Error: unhandled option: $opt"
;;
esac
done
set -e
[[ -n $command ]] || usage "Need a command (create|delete)"
[[ -n $prefix ]] || usage "Need a prefix for GCE instance names"
[[ -n $num_nodes ]] || usage "Need number of nodes"
nodes=()
for i in $(seq 1 "$num_nodes"); do
nodes+=("$prefix$i")
done
if [[ $command == "create" ]]; then
[[ -n $out_file ]] || usage "Need an outfile to store IP Addresses"
ip_addr_list=$(gcloud beta compute instances create "${nodes[@]}" --zone=us-west1-b --tags=testnet \
--image="$image_name" | awk '/RUNNING/ {print $5}')
echo "ip_addr_array=($ip_addr_list)" >"$out_file"
elif [[ $command == "delete" ]]; then
gcloud beta compute instances delete "${nodes[@]}"
else
usage "Unknown command: $command"
fi

35
multinode-demo/leader.sh Executable file
View File

@ -0,0 +1,35 @@
#!/bin/bash
here=$(dirname "$0")
# shellcheck source=multinode-demo/common.sh
source "$here"/common.sh
if [[ -d "$SNAP" ]]; then
# Exit if mode is not yet configured
# (typically the case after the Snap is first installed)
[[ -n "$(snapctl get mode)" ]] || exit 0
fi
[[ -f "$SOLANA_CONFIG_DIR"/leader.json ]] || {
echo "$SOLANA_CONFIG_DIR/leader.json not found, create it by running:"
echo
echo " ${here}/setup.sh"
exit 1
}
if [[ -n "$SOLANA_CUDA" ]]; then
program="$solana_fullnode_cuda"
else
program="$solana_fullnode"
fi
tune_networking
trap 'kill "$pid" && wait "$pid"' INT TERM
$program \
--identity "$SOLANA_CONFIG_DIR"/leader.json \
--ledger "$SOLANA_CONFIG_DIR"/ledger \
> >($leader_logger) 2>&1 &
pid=$!
oom_score_adj "$pid" 1000
wait "$pid"

View File

@ -0,0 +1,16 @@
#!/bin/bash -e
point=$1
if [[ -z $point ]]; then
echo "Data point not specified"
exit 1
fi
echo "Influx data point: $point"
if [[ -z $INFLUX_DATABASE || -z $INFLUX_USERNAME || -z $INFLUX_PASSWORD ]]; then
echo Influx user credentials not found
exit 0
fi
echo "https://metrics.solana.com:8086/write?db=${INFLUX_DATABASE}&u=${INFLUX_USERNAME}&p=${INFLUX_PASSWORD}" \
| xargs curl --max-time 5 -XPOST --data-binary "$point"

32
multinode-demo/oom_monitor.sh Executable file
View File

@ -0,0 +1,32 @@
#!/bin/bash -e
#
# Reports Linux OOM Killer activity
#
here=$(dirname "$0")
# shellcheck source=multinode-demo/common.sh
source "$here"/common.sh
if [[ $(uname) != Linux ]]; then
exit 0
fi
syslog=/var/log/syslog
if [[ ! -r $syslog ]]; then
echo Unable to read $syslog
exit 0
fi
# Adjust OOM score to reduce the chance that this script will be killed
# during an Out of Memory event since the purpose of this script is to
# report such events
oom_score_adj "self" -500
while read -r victim; do
echo "Out of memory event detected, $victim killed"
"$here"/metrics_write_datapoint.sh "oom-killer,victim=$victim killed=1"
done < <( \
tail --follow=name --retry -n0 $syslog \
| sed --unbuffered -n 's/^.* Out of memory: Kill process [1-9][0-9]* (\([^)]*\)) .*/\1/p' \
)
exit 1

14
multinode-demo/remote_leader.sh Executable file
View File

@ -0,0 +1,14 @@
#!/bin/bash -e
[[ -n $FORCE ]] || exit
chmod 600 ~/.ssh/authorized_keys ~/.ssh/id_rsa
PATH="$HOME"/.cargo/bin:"$PATH"
./fetch-perf-libs.sh
# Run setup
USE_INSTALL=1 ./multinode-demo/setup.sh -p
USE_INSTALL=1 ./multinode-demo/drone.sh >drone.log 2>&1 &
USE_INSTALL=1 SOLANA_CUDA=1 ./multinode-demo/leader.sh >leader.log 2>&1 &

185
multinode-demo/remote_nodes.sh Executable file
View File

@ -0,0 +1,185 @@
#!/bin/bash
command=$1
ip_addr_file=
remote_user=
ssh_keys=
shift
usage() {
exitcode=0
if [[ -n "$1" ]]; then
exitcode=1
echo "Error: $*"
fi
cat <<EOF
usage: $0 <start|stop> <-f IP Addr Array file> <-u username> [-k ssh-keys]
Manage a GCE multinode network
start|stop - Create or delete the network
-f file - A bash script that exports an array of IP addresses, ip_addr_array.
Elements of the array are public IP address of remote nodes.
-u username - The username for logging into remote nodes.
-k ssh-keys - Path to public/private key pair that remote nodes can use to perform
rsync and ssh among themselves. Must contain pub, and priv keys.
EOF
exit $exitcode
}
while getopts "h?f:u:k:" opt; do
case $opt in
h | \?)
usage
;;
f)
ip_addr_file=$OPTARG
;;
u)
remote_user=$OPTARG
;;
k)
ssh_keys=$OPTARG
;;
*)
usage "Error: unhandled option: $opt"
;;
esac
done
set -e
# Sample IP Address array file contents
# ip_addr_array=(192.168.1.1 192.168.1.5 192.168.2.2)
[[ -n $command ]] || usage "Need a command (start|stop)"
[[ -n $ip_addr_file ]] || usage "Need a file with IP address array"
[[ -n $remote_user ]] || usage "Need the username for remote nodes"
ip_addr_array=()
# Get IP address array
# shellcheck source=/dev/null
source "$ip_addr_file"
build_project() {
echo "Build started at $(date)"
SECONDS=0
# Build and install locally
PATH="$HOME"/.cargo/bin:"$PATH"
cargo install --force
echo "Build took $SECONDS seconds"
}
common_start_setup() {
ip_addr=$1
# Killing sshguard for now. TODO: Find a better solution
# sshguard is blacklisting IP address after ssh-keyscan and ssh login attempts
ssh "$remote_user@$ip_addr" " \
set -ex; \
sudo service sshguard stop; \
sudo apt-get --assume-yes install rsync libssl-dev; \
mkdir -p ~/.ssh ~/solana ~/.cargo/bin; \
" >log/"$ip_addr".log
# If provided, deploy SSH keys
if [[ -n $ssh_keys ]]; then
{
rsync -vPrz "$ssh_keys"/id_rsa "$remote_user@$ip_addr":~/.ssh/
rsync -vPrz "$ssh_keys"/id_rsa.pub "$remote_user@$ip_addr":~/.ssh/
rsync -vPrz "$ssh_keys"/id_rsa.pub "$remote_user@$ip_addr":~/.ssh/authorized_keys
rsync -vPrz ./multinode-demo "$remote_user@$ip_addr":~/solana/
} >>log/"$ip_addr".log
fi
}
start_leader() {
common_start_setup "$1"
{
rsync -vPrz ~/.cargo/bin/solana* "$remote_user@$ip_addr":~/.cargo/bin/
rsync -vPrz ./fetch-perf-libs.sh "$remote_user@$ip_addr":~/solana/
ssh -n -f "$remote_user@$ip_addr" 'cd solana; FORCE=1 ./multinode-demo/remote_leader.sh'
} >>log/"$1".log
leader_ip=$1
leader_time=$SECONDS
SECONDS=0
}
start_validator() {
common_start_setup "$1"
ssh -n -f "$remote_user@$ip_addr" "cd solana; FORCE=1 ./multinode-demo/remote_validator.sh $leader_ip" >>log/"$1".log
}
start_all_nodes() {
echo "Deployment started at $(date)"
SECONDS=0
count=0
leader_ip=
leader_time=
mkdir -p log
for ip_addr in "${ip_addr_array[@]}"; do
if ((!count)); then
# Start the leader on the first node
echo "Leader node $ip_addr, killing previous instance and restarting"
start_leader "$ip_addr"
else
# Start validator on all other nodes
echo "Validator[$count] node $ip_addr, killing previous instance and restarting"
start_validator "$ip_addr" &
# TBD: Remove the sleep or reduce time once GCP login quota is increased
sleep 2
fi
((count = count + 1))
done
wait
((validator_count = count - 1))
echo "Deployment finished at $(date)"
echo "Leader deployment too $leader_time seconds"
echo "$validator_count Validator deployment took $SECONDS seconds"
}
stop_all_nodes() {
SECONDS=0
local count=0
for ip_addr in "${ip_addr_array[@]}"; do
ssh-keygen -R "$ip_addr" >log/local.log
ssh-keyscan "$ip_addr" >>~/.ssh/known_hosts 2>/dev/null
echo "Stopping node[$count] $ip_addr. Remote user $remote_user"
ssh -n -f "$remote_user@$ip_addr" " \
set -ex; \
sudo service sshguard stop; \
pkill -9 solana-; \
pkill -9 validator; \
pkill -9 leader; \
"
sleep 2
((count = count + 1))
echo "Stopped node[$count] $ip_addr"
done
echo "Stopping $count nodes took $SECONDS seconds"
}
if [[ $command == "start" ]]; then
build_project
stop_all_nodes
start_all_nodes
elif [[ $command == "stop" ]]; then
stop_all_nodes
else
usage "Unknown command: $command"
fi

View File

@ -0,0 +1,17 @@
#!/bin/bash -e
[[ -n $FORCE ]] || exit
chmod 600 ~/.ssh/authorized_keys ~/.ssh/id_rsa
PATH="$HOME"/.cargo/bin:"$PATH"
touch ~/.ssh/known_hosts
ssh-keygen -R "$1" 2>/dev/null
ssh-keyscan "$1" >>~/.ssh/known_hosts 2>/dev/null
rsync -vPrz "$1":~/.cargo/bin/solana* ~/.cargo/bin/
# Run setup
USE_INSTALL=1 ./multinode-demo/setup.sh -p
USE_INSTALL=1 ./multinode-demo/validator.sh "$1":~/solana "$1" >validator.log 2>&1

110
multinode-demo/setup.sh Executable file
View File

@ -0,0 +1,110 @@
#!/bin/bash
here=$(dirname "$0")
# shellcheck source=multinode-demo/common.sh
source "$here"/common.sh
usage () {
exitcode=0
if [[ -n "$1" ]]; then
exitcode=1
echo "Error: $*"
fi
cat <<EOF
usage: $0 [-n num_tokens] [-l] [-p] [-t node_type]
Creates a fullnode configuration
-n num_tokens - Number of tokens to create
-l - Detect network address from local machine configuration, which
may be a private IP address unaccessible on the Intenet (default)
-p - Detect public address using public Internet servers
-t node_type - Create configuration files only for this kind of node. Valid
options are validator or leader. Creates configuration files
for both by default
EOF
exit $exitcode
}
ip_address_arg=-l
num_tokens=1000000000
node_type_leader=true
node_type_validator=true
while getopts "h?n:lpt:" opt; do
case $opt in
h|\?)
usage
exit 0
;;
l)
ip_address_arg=-l
;;
p)
ip_address_arg=-p
;;
n)
num_tokens="$OPTARG"
;;
t)
node_type="$OPTARG"
case $OPTARG in
leader)
node_type_leader=true
node_type_validator=false
;;
validator)
node_type_leader=false
node_type_validator=true
;;
*)
usage "Error: unknown node type: $node_type"
;;
esac
;;
*)
usage "Error: unhandled option: $opt"
;;
esac
done
leader_address_args=("$ip_address_arg")
validator_address_args=("$ip_address_arg" -b 9000)
leader_id_path="$SOLANA_CONFIG_PRIVATE_DIR"/leader-id.json
validator_id_path="$SOLANA_CONFIG_PRIVATE_DIR"/validator-id.json
mint_path="$SOLANA_CONFIG_PRIVATE_DIR"/mint.json
set -e
for i in "$SOLANA_CONFIG_DIR" "$SOLANA_CONFIG_PRIVATE_DIR" "$SOLANA_CONFIG_VALIDATOR_DIR"; do
echo "Cleaning $i"
rm -rvf "$i"
mkdir -p "$i"
done
$solana_keygen -o "$leader_id_path"
$solana_keygen -o "$validator_id_path"
if $node_type_leader; then
echo "Creating $mint_path with $num_tokens tokens"
$solana_keygen -o "$mint_path"
echo "Creating $SOLANA_CONFIG_DIR/ledger"
$solana_genesis --tokens="$num_tokens" --ledger "$SOLANA_CONFIG_DIR"/ledger < "$mint_path"
echo "Creating $SOLANA_CONFIG_DIR/leader.json"
$solana_fullnode_config --keypair="$leader_id_path" "${leader_address_args[@]}" > "$SOLANA_CONFIG_DIR"/leader.json
fi
if $node_type_validator; then
echo "Creating $SOLANA_CONFIG_VALIDATOR_DIR/validator.json"
$solana_fullnode_config --keypair="$validator_id_path" "${validator_address_args[@]}" > "$SOLANA_CONFIG_VALIDATOR_DIR"/validator.json
fi
ls -lhR "$SOLANA_CONFIG_DIR"/
if $node_type_leader; then
ls -lhR "$SOLANA_CONFIG_PRIVATE_DIR"
fi

View File

@ -0,0 +1,47 @@
#!/bin/bash -e
#
# Wallet sanity test
#
here=$(dirname "$0")
cd "$here"
if [[ -n "$USE_SNAP" ]]; then
# TODO: Merge wallet.sh functionality into solana-wallet proper and
# remove this USE_SNAP case
wallet="solana.wallet $1"
else
wallet="../wallet.sh $1"
fi
# Tokens transferred to this address are lost forever...
garbage_address=vS3ngn1TfQmpsW1Z4NkLuqNAQFF3dYQw8UZ6TCx9bmq
check_balance_output() {
declare expected_output="$1"
exec 42>&1
output=$($wallet balance | tee >(cat - >&42))
if [[ ! "$output" =~ $expected_output ]]; then
echo "Balance is incorrect. Expected: $expected_output"
exit 1
fi
}
pay_and_confirm() {
exec 42>&1
signature=$($wallet pay "$@" | tee >(cat - >&42))
$wallet confirm "$signature"
}
$wallet reset
$wallet address
check_balance_output "Your balance is: 0"
$wallet airdrop --tokens 60
check_balance_output "Your balance is: 60"
$wallet airdrop --tokens 40
check_balance_output "Your balance is: 100"
pay_and_confirm --to $garbage_address --tokens 99
check_balance_output "Your balance is: 1"
echo PASS
exit 0

4
multinode-demo/validator-x.sh Executable file
View File

@ -0,0 +1,4 @@
#!/bin/bash
here=$(dirname "$0")
exec "$here"/validator.sh -x "$@"

111
multinode-demo/validator.sh Executable file
View File

@ -0,0 +1,111 @@
#!/bin/bash
here=$(dirname "$0")
# shellcheck source=multinode-demo/common.sh
source "$here"/common.sh
usage() {
if [[ -n $1 ]]; then
echo "$*"
echo
fi
echo "usage: $0 [-x] [rsync network path to solana repo on leader machine] [network ip address of leader]"
echo ""
echo " -x: runs a new, dynamically-configured validator"
exit 1
}
if [[ $1 = -h ]]; then
usage
fi
if [[ $1 == -x ]]; then
self_setup=1
shift
else
self_setup=0
fi
if [[ -n $3 ]]; then
usage
fi
if [[ -d $SNAP ]]; then
# Exit if mode is not yet configured
# (typically the case after the Snap is first installed)
[[ -n $(snapctl get mode) ]] || exit 0
# Select leader from the Snap configuration
leader_address=$(snapctl get leader-address)
if [[ -z $leader_address ]]; then
# Assume public testnet by default
leader_address=35.227.93.37 # testnet.solana.com
fi
leader=$leader_address
else
if [[ -z $1 ]]; then
leader=${1:-${here}/..} # Default to local tree for data
leader_address=${2:-127.0.0.1} # Default to local leader
elif [[ -z $2 ]]; then
leader=$1
leader_address=$(dig +short "${leader%:*}" | head -n1)
if [[ -z $leader_address ]]; then
usage "Error: unable to resolve IP address for $leader"
fi
else
leader=$1
leader_address=$2
fi
fi
leader_port=8001
if [[ -n $SOLANA_CUDA ]]; then
program=$solana_fullnode_cuda
else
program=$solana_fullnode
fi
if ((!self_setup)); then
[[ -f $SOLANA_CONFIG_VALIDATOR_DIR/validator.json ]] || {
echo "$SOLANA_CONFIG_VALIDATOR_DIR/validator.json not found, create it by running:"
echo
echo " ${here}/setup.sh"
exit 1
}
validator_json_path=$SOLANA_CONFIG_VALIDATOR_DIR/validator.json
SOLANA_LEADER_CONFIG_DIR=$SOLANA_CONFIG_VALIDATOR_DIR/leader-config
else
mkdir -p "$SOLANA_CONFIG_PRIVATE_DIR"
validator_id_path=$SOLANA_CONFIG_PRIVATE_DIR/validator-id-x$$.json
$solana_keygen -o "$validator_id_path"
mkdir -p "$SOLANA_CONFIG_VALIDATOR_DIR"
validator_json_path=$SOLANA_CONFIG_VALIDATOR_DIR/validator-x$$.json
port=9000
(((port += ($$ % 1000)) && (port == 9000) && port++))
$solana_fullnode_config --keypair="$validator_id_path" -l -b "$port" > "$validator_json_path"
SOLANA_LEADER_CONFIG_DIR=$SOLANA_CONFIG_VALIDATOR_DIR/leader-config-x$$
fi
rsync_leader_url=$(rsync_url "$leader")
tune_networking
set -ex
$rsync -vPr "$rsync_leader_url"/config/ "$SOLANA_LEADER_CONFIG_DIR"
[[ -d $SOLANA_LEADER_CONFIG_DIR/ledger ]] || {
echo "Unable to retrieve ledger from $rsync_leader_url"
exit 1
}
trap 'kill "$pid" && wait "$pid"' INT TERM
$program \
--identity "$validator_json_path" \
--testnet "$leader_address:$leader_port" \
--ledger "$SOLANA_LEADER_CONFIG_DIR"/ledger \
> >($validator_logger) 2>&1 &
pid=$!
oom_score_adj "$pid" 1000
wait "$pid"

45
multinode-demo/wallet.sh Executable file
View File

@ -0,0 +1,45 @@
#!/bin/bash
#
# usage: $0 <rsync network path to solana repo on leader machine>"
#
here=$(dirname "$0")
# shellcheck source=multinode-demo/common.sh
source "$here"/common.sh
# if $1 isn't host:path, something.com, or a valid local path
if [[ ${1%:} != "$1" || "$1" =~ [^.]\.[^.] || -d $1 ]]; then
leader=$1 # interpret
shift
else
if [[ -d "$SNAP" ]]; then
leader=testnet.solana.com # Default to testnet when running as a Snap
else
leader=$here/.. # Default to local solana repo
fi
fi
if [[ "$1" = "reset" ]]; then
echo Wallet resetting
rm -rf "$SOLANA_CONFIG_CLIENT_DIR"
exit 0
fi
rsync_leader_url=$(rsync_url "$leader")
set -e
mkdir -p "$SOLANA_CONFIG_CLIENT_DIR"
if [[ ! -r "$SOLANA_CONFIG_CLIENT_DIR"/leader.json ]]; then
echo "Fetching leader configuration from $rsync_leader_url"
$rsync -Pz "$rsync_leader_url"/config/leader.json "$SOLANA_CONFIG_CLIENT_DIR"/
fi
client_id_path="$SOLANA_CONFIG_CLIENT_DIR"/id.json
if [[ ! -r $client_id_path ]]; then
echo "Generating client identity: $client_id_path"
$solana_keygen -o "$client_id_path"
fi
# shellcheck disable=SC2086 # $solana_wallet should not be quoted
exec $solana_wallet \
-l "$SOLANA_CONFIG_CLIENT_DIR"/leader.json -k "$client_id_path" "$@"

View File

@ -0,0 +1,190 @@
# Smart Contracts Engine
The goal of this RFC is to define a set of constraints for APIs and runtime such that we can execute our smart contracts safely on massively parallel hardware such as a GPU. Our runtime is built around an OS *syscall* primitive. The difference in blockchain is that now the OS does a cryptographic check of memory region ownership before accessing the memory in the Solana kernel.
## Version
version 0.1
## Toolchain Stack
+---------------------+ +---------------------+
| | | |
| +------------+ | | +------------+ |
| | | | | | | |
| | frontend | | | | verifier | |
| | | | | | | |
| +-----+------+ | | +-----+------+ |
| | | | | |
| | | | | |
| +-----+------+ | | +-----+------+ |
| | | | | | | |
| | llvm | | | | loader | |
| | | +------>+ | | |
| +-----+------+ | | +-----+------+ |
| | | | | |
| | | | | |
| +-----+------+ | | +-----+------+ |
| | | | | | | |
| | ELF | | | | runtime | |
| | | | | | | |
| +------------+ | | +------------+ |
| | | |
| client | | solana |
+---------------------+ +---------------------+
[Figure 1. Smart Contracts Stack]
In Figure 1 an untrusted client, creates a program in the front-end language of her choice, (like C/C++/Rust/Lua), and compiles it with LLVM to a position independent shared object ELF, targeting BPF bytecode. Solana will safely load and execute the ELF.
## Bytecode
Our bytecode is based on Berkley Packet Filter. The requirements for BPF overlap almost exactly with the requirements we have:
1. Deterministic amount of time to execute the code
2. Bytecode that is portable between machine instruction sets
3. Verified memory accesses
4. Fast to load the object, verify the bytecode and JIT to local machine instruction set
For 1, that means that loops are unrolled, and for any jumps back we can guard them with a check against the number of instruction that have been executed at this point. If the limit is reached, the program yields its execution. This involves saving the stack and current instruction index.
For 2, the BPF bytecode already easily maps to x8664, arm64 and other instruction sets. 
For 3, every load and store that is relative can be checked to be within the expected memory that is passed into the ELF. Dynamic load and stores can do a runtime check against available memory, these will be slow and should be avoided.
For 4, Fully linked PIC ELF with just a single RX segment. Effectively we are linking a shared object with `-fpic -target bpf` and with a linker script to collect everything into a single RX segment. Writable globals are not supported.
### Address Checks
The interface to the module takes a `&mut Vec<Vec<u8>>` in rust, or a `int sz, void* data[sz], int szs[sz]` in `C`. Given the module's bytecode, for each method, we need to analyze the bounds on load and stores into each buffer the module uses. This check needs to be done `on chain`, and after those bounds are computed we can verify that the user supplied array of buffers will not cause a memory fault. For load and stores that we cannot analyze, we can replace with a `safe_load` and `safe_store` instruction that will check the table for access.
## Loader
The loader is our first smart contract. The job of this contract is to load the actual program with its own instance data. The loader will verify the bytecode and that the object implements the expected entry points.
Since there is only one RX segment, the context for the contract instance is passed into each entry point as well as the event data for that entry point.
A client will create a transaction to create a new loader instance:
`Solana_NewLoader(Loader Instance PubKey, proof of key ownership, space I need for my elf)`
A client will then do a bunch of transactions to load its elf into the loader instance they created:
`Loader_UploadElf(Loader Instance PubKey, proof of key ownership, pos start, pos end, data)`
At this point the client can create a new instance of the module with its own instance address:
`Loader_NewInstance(Loader Instance PubKey, proof of key ownership, Instance PubKey, proof of key ownership)`
Once the instance has been created, the client may need to upload more user data to solana to configure this instance:
`Instance_UploadModuleData(Instance PubKey, proof of key ownership, pos start, pos end, data)`
Now clients can `start` the instance:
`Instance_Start(Instance PubKey, proof of key ownership)`
## Runtime
Our goal with the runtime is to have a general purpose execution environment that is highly parallelizable and doesn't require dynamic resource management. We want to execute as many contracts as we can in parallel, and have them pass or fail without a destructive state change.
### State and Entry Point
State is addressed by an account which is at the moment simply the PubKey. Our goal is to eliminate dynamic memory allocation in the smart contract itself, so the contract is a function that takes a mapping of [(PubKey,State)] and returns [(PubKey, State')]. The output of keys is a subset of the input. Three basic kinds of state exist:
* Instance State
* Participant State
* Caller State
There isn't any difference in how each is implemented, but conceptually Participant State is memory that is allocated for each participant in the contract. Instance State is memory that is allocated for the contract itself, and Caller State is memory that the transactions caller has allocated.
### Call
```
void call(
const struct instance_data *data,
const uint8_t kind[], //instance|participant|caller|read|write
const uint8_t *keys[],
uint8_t *data[],
int num,
uint8_t dirty[], //dirty memory bits
uint8_t *userdata, //current transaction data
);
```
To call this operation, the transaction that is destined to the contract instance specifies what keyed state it should present to the `call` function. To allocate the state memory or a call context, the client has to first call a function on the contract with the designed address that will own the state.
At its core, this is a system call that requires cryptographic proof of ownership of memory regions instead of an OS that checks page tables for access rights.
* `Instance_AllocateContext(Instance PubKey, My PubKey, Proof of key ownership)`
Any transaction can then call `call` on the contract with a set of keys. It's up to the contract itself to manage ownership:
* `Instance_Call(Instance PubKey, [Context PubKeys], proofs of ownership, userdata...)`
Contracts should be able to read any state that is part of solana, but only write to state that the contract allocated.
#### Caller State
Caller `state` is memory allocated for the `call` that belongs to the public key that is issuing the `call`. This is the caller's context.
#### Instance State
Instance `state` is memory that belongs to this contract instance. We may also need module-wide `state` as well.
#### Participant State
Participant `state` is any other memory. In some cases it may make sense to have these allocated as part of the call by the caller.
### Reduce
Some operations on the contract will require iteration over all the keys. To make this parallelizable the iteration is broken up into reduce calls which are combined.
```
void reduce_m(
const struct instance_data *data,
const uint8_t *keys[],
const uint8_t *data[],
int num,
uint8_t *reduce_data,
);
void reduce_r(
const struct instance_data *data,
const uint8_t *reduce_data[],
int num,
uint8_t *reduce_data,
);
```
### Execution
Transactions are batched and processed in parallel at each stage.
```
+-----------+ +--------------+ +-----------+ +---------------+
| sigverify |-+->| debit commit |---+->| execution |-+->| memory commit |
+-----------+ | +--------------+ | +-----------+ | +---------------+
| | |
| +---------------+ | | +--------------+
|->| memory verify |->+ +->| debit undo |
+---------------+ | +--------------+
|
| +---------------+
+->| credit commit |
+---------------+
```
The `debit verify` stage is very similar to `memory verify`. Proof of key ownership is used to check if the callers key has some state allocated with the contract, then the memory is loaded and executed. After execution stage, the dirty pages are written back by the contract. Because know all the memory accesses during execution, we can batch transactions that do not interfere with each other. We can also apply the `debit undo` and `credit commit` stages of the transaction. `debit undo` is run in case of an exception during contract execution, only transfers may be reversed, fees are commited to solana.
### GPU execution
A single contract can read and write to separate key pairs without interference. These separate calls to the same contract can execute on the same GPU thread over different memory using different SIMD lanes.
## Notes
1. There is no dynamic memory allocation.
2. Persistant Memory is allocated to a Key with ownership
3. Contracts can `call` to update key owned state
4. Contracts can `reduce` over the memory to aggregate state
5. `call` is just a *syscall* that does a cryptographic check of memory owndershp

122
rfcs/rfc-002-consensus.md Normal file
View File

@ -0,0 +1,122 @@
# Consensus
VERY WIP
The goal of this RFC is to define the consensus algorithm used in solana. This proposal covers a Proof of Stake algorithm that leverages Proof of History. PoH is a permissionless clock for blockchain that is available before consensus. This PoS approach leverages PoH to make strong assumptions about time between partitions.
## Version
version 0.1
## Message Flow
1. Transactions are ingested at the leader.
2. Leader filters for valid transactions
3. Leader executes valid transactions on its state
4. Leader packages transactions into blobs
5. Leader transmits blobs to validator nodes.
a. The set of supermajority + `M` by stake weight of nodes is rotated in round robin fashion.
6. Validators retransmit blobs to peers in their set and to further downstream nodes.
7. Validators validate the transactions and execute them on their state.
8. Validators compute the hash of the state.
9. Validators transmit votes to the leader.
a. Votes are signatures of the hash of the computed state.
10. Leader executes the votes as any other transaction and broadcasts them out to the network
11. Validators observe their votes, and all the votes from the network.
12. Validators continue voting if the supermajority of stake is observed in the vote for the same hash.
Supermajority is defined as `2/3rds + 1` vote of the PoS stakes.
## Staking
Validators `stake` some of their spendable sol into a staking account. The stakes are not spendable and can only be used for voting.
```
CreateStake(
PoH count,
PoH hash,
source public key,
amount,
destination public key,
proof of ownership of destination public key,
signature of the message with the source keypair
)
```
Creating the stake has a warmup period of TBD. Unstaking requires the node to miss a certain amount of validation votes.
## Validation Votes
```
Validate(
PoH count,
PoH hash,
stake public key,
signature of the state,
signature of the message with the stake keypair
)
```
## Validator Slashing
Validators `stake` some of their spendable sol into a staking account. The stakes are not spendable and can only be used for voting.
```
Slash(Validate(
PoH count,
PoH hash,
stake public key,
...
signature of the message with the stake keypair
))
```
When the `Slash` vote is processed, validators should lookup `PoH hash` at `PoH count` and compare it with the message. If they do not match, the stake at `stake public key` should be set to `0`.
## Leader Slashing
TBD. The goal of this is to discourage leaders from generating multiple PoH streams.
## Validation Vote Contract
The goal of this contract is to simulate economic cost of mining on a shorter branch.
1. With my signature I am certifying that I computed `state hash` at `PoH count` and `PoH hash`.
2. I will not vote on a branch that doesn't contain this message for at least `N` counts, or until `PoH count` + `N` is reached by the PoH stream.
3. I will not vote for any other branch below `PoH count`.
a. if there are other votes not present in this PoH history the validator may need to `cancel` them before creating this vote.
## Leader Seed Generation
Leader selection is decided via a random seed. The process is as follows:
1. Periodically at a specific `PoH count` select the first vote signatures that create a supermajority from the previous round.
2. append them together
3. hash the string for `N` counts via a similar process as PoH itself.
4. The resulting hash is the random seed for `M` counts, where M > N
## Leader Ranking and Rotation
Leader's transmit for a count of `T`. When `T` is reached all the validators should switch to the next ranked leader. To rank leaders, the supermajority + `M` nodes are shuffled with the using the above calculated random seed.
TBD: define a ranking for critical partitions without a node from supermajority + `M` set.
## Partition selection
Validators should select the first branch to reach finality, or the highest ranking leader.
## Examples
### Small Partition
1. Network partition M occurs for 10% of the nodes
2. The larger partition K, with 90% of the stake weight continues to operate as normal
3. M cycles through the ranks until one of them is leader.
4. M validators observe 10% of the vote pool, finality is not reached
5. M and K re-connect.
6. M validators cancel their votes on K which are below K's `PoH count`
### Leader Timeout
1. Next rank node observes a timeout.
2. Nodes receiving both PoH streams pick the higher rank node.
3. 2, causes a partition, since nodes can only vote for 1 leader.
4. Partition is resolved just like in the [Small Partition](#small-parition)

54
rfcs/rfc-003-storage.md Normal file
View File

@ -0,0 +1,54 @@
# Storage
The goal of this RFC is to define a protocol for storing a very large ledger over a p2p network that is verified by solana validators. At full capacity on a 1gbps network solana will generate 4 petabytes of data per year. To prevent the network from centralizing around full nodes that have to store the full data set this protocol proposes a way for mining nodes to provide storage capacity for pieces of the network.
# Version
version 0.1
# Background
The basic idea to Proof of Replication is encrypting a dataset with a public symmetric key using CBC encryption, then hash the encrypted dataset. The main problem with the naive approach is that a dishonest storage node can stream the encryption and delete the data as its hashed. The simple solution is to force the hash to be done on the reverse of the encryption, or perhaps with a random order. This ensures that all the data is present during the generation of the proof and it also requires the validator to have the entirety of the encrypted data present for verification of every proof of every identity. So the space required to validate is `(Number of Proofs)*(data size)`
# Optimization with PoH
Our improvement on this approach is to randomly sample the encrypted blocks faster than it takes to encrypt, and record the hash of those samples into the PoH ledger. Thus the blocks stay in the exact same order for every PoRep and verification can stream the data and verify all the proofs in a single batch. This way we can verify multiple proofs concurrently, each one on its own CUDA core. With the current generation of graphics cards our network can support up to 14k replication identities or symmetric keys. The total space required for verification is `(2 CBC blocks) * (Number of Identities)`, with core count of equal to (Number of Identities). A CBC block is expected to be 1MB in size.
# Network
Validators for PoRep are the same validators that are verifying transactions. They have some stake that they have put up as collateral that ensures that their work is honest. If you can prove that a validator verified a fake PoRep, then the validators stake can be slashed.
Replicators are specialized thin clients. They download a part of the ledger and store it, and provide PoReps of storing the ledger. For each verified PoRep replicators earn a reward of sol from the mining pool.
# Constraints
We have the following constraints:
* At most 14k replication identities can be used, because thats how many CUDA cores we can fit in a $5k box at the moment.
* Verification requires generating the CBC blocks. That requires space of 2 blocks per identity, and 1 CUDA core per identity for the same dataset. So as many identities at once should be batched with as many proofs for those identities verified concurrently for the same dataset.
# Validation and Replication Protocol
1. Network sets the replication target number, lets say 1k. 1k PoRep identities are created from signatures of a PoH hash. So they are tied to a specific PoH hash. It doesn't matter who creates them, or simply the last 1k validation signatures we saw for the ledger at that count. This maybe just the initial batch of identities, because we want to stagger identity rotation.
2. Any client can use any of these identities to create PoRep proofs. Replicator identities are the CBC encryption keys.
3. Periodically at a specific PoH count, replicator that want to create PoRep proofs sign the PoH hash at that count. That signature is the seed used to pick the block and identity to replicate. A block is 1TB of ledger.
4. Periodically at a specific PoH count, replicator submits PoRep proofs for their selected block. A signature of the PoH hash at that count is the seed used to sample the 1TB encrypted block, and hash it. This is done faster than it takes to encrypt the 1TB block with the original identity.
5. Replicators must submit some number of fake proofs, which they can prove to be fake by providing the seed for the hash result.
6. Periodically at a specific PoH count, validators sign the hash and use the signature to select the 1TB block that they need to validate. They batch all the identities and proofs and submit approval for all the verified ones.
7. After #6, replicator client submit the proofs of fake proofs.
For any random seed, we force everyone to use a signature that is derived from a PoH hash. Everyone must use the same count, so the same PoH hash is signed by every participant. The signatures are then each cryptographically tied to the keypair, which prevents a leader from grinding on the resulting value for more than 1 identity.
We need to stagger the rotation of the identity keys. Once this gets going, the next identity could be generated by hashing itself with a PoH hash, or via some other process based on the validation signatures.
Since there are many more client identities then encryption identities, we need to split the reward for multiple clients, and prevent Sybil attacks from generating many clients to acquire the same block of data. To remain BFT we want to avoid a single human entity from storing all the replications of a single chunk of the ledger.
Our solution to this is to force the clients to continue using the same identity. If the first round is used to acquire the same block for many client identities, the second round for the same client identities will force a redistribution of the signatures, and therefore PoRep identities and blocks. Thus to get a reward for storage clients need to store the first block for free and the network can reward long lived client identities more than new ones.
# Notes
* We can reduce the costs of verification of PoRep by using PoH, and actually make it feasible to verify a large number of proofs for a global dataset.
* We can eliminate grinding by forcing everyone to sign the same PoH hash and use the signatures as the seed
* The game between validators and replicators is over random blocks and random encryption identities and random data samples. The goal of randomization is to prevent colluding groups from having overlap on data or validation.
* Replicator clients fish for lazy validators by submitting fake proofs that they can prove are fake.
* Replication identities are just symmetric encryption keys, the number of them on the network is our storage replication target. Many more client identities can exist than replicator identities, so unlimited number of clients can provide proofs of the same replicator identity.
* To defend against Sybil client identities that try to store the same block we force the clients to store for multiple rounds before receiving a reward.

View File

@ -0,0 +1,77 @@
Two players want to play tic-tac-toe with each other on Solana.
The tic-tac-toe program has already been provisioned on the network, and the
program author has advertised the following information to potential gamers:
* `tictactoe_publickey` - the program's public key
* `tictactoe_gamestate_size` - the number of bytes needed to maintain the game state
The game state is a well-documented data structure consisting of:
- Player 1's public key
- Player 2's public key
- Game status. An 8-bit value where:
* 0 = game uninitialized
* 1 = Player 1's turn
* 2 = Player 2's turn
* 3 = Player 1 won
* 4 = Player 2 won
- Current board configuration. A 3x3 character array containing the values '\0', 'X' or 'O'
### Game Setup
1. Two players want to start a game. Player 2 sends Player 1 their public key,
`player2_publickey` off-chain (IM, email, etc)
2. Player 1 creates a new keypair to represent the game state, `(gamestate_publickey,
gamestate_privatekey)`.
3. Player 1 issues an allocate_memory transaction, assigning that memory page to the
tic-tac-toe program. The `memory_fee` is used to *rent* the memory page for the
duration of the game and is subtracted from current account balance of Player
1:
```
allocate_memory(gamestate_publickey, tictactoe_publickey, tictactoe_gamestate_size, memory_fee)
```
4. Game state is then initialized by issuing a *new* call transaction to the
tic-tac-toe program. This transaction is signed by `gamestate_privatekey`, known only
to Player 1.
```
call(tictactoe_publickey, gamestate_publickey, 'new', player1_publickey, player2_publickey)
```
5. Once the game is initialized, Player 1 shares `gamestate_publickey` with
Player 2 off-chain (IM, email, etc)
Note that it's likely each player prefer to generate a game-specific keypair
rather than sharing their primary public key (`player1_publickey`,
`player2_publickey`) with each other and the tic-tac-toe program.
### Game Play
Both players poll the network, via a **TBD off-chain RPC API**, to read the
current game state from the `gamestate_publickey` memory page.
When the *Game status* field indicates it's their turn, the player issues a
*move* call transaction passing in the board position (1..9) that they want to
mark as X or O:
```
call(tictactoe_publickey, gamestate_publickey, 'move', position)
```
The program will reject the transaction if it was not signed by the player whose
turn it is.
The outcome of the *move* call is also observed by polling the current game state via
the **TBD off-chain RPC API**.
### Game Cancellation
At any time Player 1 may conclude the game by issuing:
```
call(tictactoe_publickey, gamestate_publickey, 'abort')
```
causing any remaining *rent* tokens assigned to the `gamestate_publickey` page
to be transferred back to Player 1 by the tic-tac-toe program. Lastly, the
network recognizes the empty account and frees the `gamestate_publickey` memory
page.

43
scripts/perf-plot.py Executable file
View File

@ -0,0 +1,43 @@
#!/usr/bin/env python
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import json
import sys
stages_to_counters = {}
stages_to_time = {}
if len(sys.argv) != 2:
print("USAGE: {} <input file>".format(sys.argv[0]))
sys.exit(1)
with open(sys.argv[1]) as fh:
for line in fh.readlines():
if "COUNTER" in line:
json_part = line[line.find("{"):]
x = json.loads(json_part)
counter = x['name']
if not (counter in stages_to_counters):
stages_to_counters[counter] = []
stages_to_time[counter] = []
stages_to_counters[counter].append(x['counts'])
stages_to_time[counter].append(x['now'])
fig, ax = plt.subplots()
for stage in stages_to_counters.keys():
plt.plot(stages_to_time[stage], stages_to_counters[stage], label=stage)
plt.xlabel('ms')
plt.ylabel('count')
plt.legend(bbox_to_anchor=(0., 1.02, 1., .102), loc=3,
ncol=2, mode="expand", borderaxespad=0.)
plt.locator_params(axis='x', nbins=10)
plt.grid(True)
plt.savefig("perf.pdf")

17
snap/README.md Normal file
View File

@ -0,0 +1,17 @@
## Development
If you're running Ubuntu 16.04 and already have `snapcraft` installed, simply
run:
```
$ snapcraft
```
For other systems we provide a docker image that can be used for snap
development:
```
$ ./ci/docker-run.sh solanalabs/snapcraft snapcraft -d
```
## Reference
* https://docs.snapcraft.io/

39
snap/hooks/configure vendored Executable file
View File

@ -0,0 +1,39 @@
#!/bin/bash -e
echo Stopping daemons
snapctl stop --disable solana.daemon-drone
snapctl stop --disable solana.daemon-leader
snapctl stop --disable solana.daemon-validator
snapctl stop --disable solana.daemon-oom-monitor
mode="$(snapctl get mode)"
if [[ -z "$mode" ]]; then
exit 0
fi
num_tokens="$(snapctl get num-tokens)"
num_tokens="${num_tokens:+-n $num_tokens}"
setup_args="$(snapctl get setup-args)"
case $mode in
leader+drone)
"$SNAP"/bin/setup.sh -t leader $num_tokens -p $setup_args
snapctl start --enable solana.daemon-drone
snapctl start --enable solana.daemon-leader
;;
leader)
"$SNAP"/bin/setup.sh -t leader $num_tokens -p $setup_args
snapctl start --enable solana.daemon-leader
;;
validator)
"$SNAP"/bin/setup.sh -t validator -p $setup_args
snapctl start --enable solana.daemon-validator
;;
*)
echo "Error: Unknown mode: $mode"
exit 1
;;
esac
snapctl start --enable solana.daemon-oom-monitor

127
snap/snapcraft.yaml Normal file
View File

@ -0,0 +1,127 @@
name: solana
version: git
summary: Blockchain, Rebuilt for Scale
description: |
710,000 tx/s with off-the-shelf hardware and no sharding.
Scales with Moore's Law.
grade: devel
# TODO: solana-perf-fullnode does not yet run with 'strict' confinement due to the
# CUDA dependency, so use 'devmode' confinement for now
confinement: devmode
hooks:
configure:
plugs: [network]
apps:
drone:
command: solana-drone
plugs:
- network
- network-bind
fullnode:
command: solana-fullnode
plugs:
- network
- network-bind
- home
fullnode-cuda:
command: solana-fullnode-cuda
plugs:
- network
- network-bind
- home
fullnode-config:
command: solana-fullnode-config
plugs:
- network
- network-bind
- home
genesis:
command: solana-genesis
keygen:
command: solana-keygen
plugs:
- home
ledger-tool:
command: solana-ledger-tool
plugs:
- home
bench-tps:
# TODO: Merge client.sh functionality into solana-bench-tps proper
command: client.sh
#command: solana-bench-tps
plugs:
- network
- network-bind
- home
wallet:
# TODO: Merge wallet.sh functionality into solana-wallet proper
command: wallet.sh
#command: solana-wallet
plugs:
- network
- home
daemon-validator:
daemon: simple
command: validator.sh
plugs:
- network
- network-bind
daemon-leader:
daemon: simple
command: leader.sh
plugs:
- network
- network-bind
daemon-drone:
daemon: simple
command: drone.sh
plugs:
- network
- network-bind
daemon-oom-monitor:
daemon: simple
command: oom_monitor.sh
plugs:
- network
parts:
solana:
plugin: nil
prime:
- bin
- usr/lib
override-build: |
# Install CUDA 9.2 runtime
mkdir -p $SNAPCRAFT_PART_INSTALL/usr/lib/nvidia-396/
mkdir -p $SNAPCRAFT_PART_INSTALL/usr/lib/x86_64-linux-gnu/
cp -rav /usr/local/cuda-9.2/targets/x86_64-linux/lib/libcudart.so* $SNAPCRAFT_PART_INSTALL/usr/lib
cp -rav /usr/lib/x86_64-linux-gnu/libcuda.so* $SNAPCRAFT_PART_INSTALL/usr/lib/x86_64-linux-gnu/
cp -v /usr/lib/nvidia-396/libnvidia-fatbinaryloader.so* $SNAPCRAFT_PART_INSTALL/usr/lib/nvidia-396/
# Build/install solana-fullnode-cuda
./fetch-perf-libs.sh
cargo install --features=cuda --root $SNAPCRAFT_PART_INSTALL --bin solana-fullnode
mv $SNAPCRAFT_PART_INSTALL/bin/solana-fullnode $SNAPCRAFT_PART_INSTALL
rm -rf $SNAPCRAFT_PART_INSTALL/bin/*
mv $SNAPCRAFT_PART_INSTALL/solana-fullnode $SNAPCRAFT_PART_INSTALL/bin/solana-fullnode-cuda
mkdir -p $SNAPCRAFT_PART_INSTALL/usr/lib/
cp -f libJerasure.so $SNAPCRAFT_PART_INSTALL/usr/lib/libJerasure.so.2
cp -f libgf_complete.so $SNAPCRAFT_PART_INSTALL/usr/lib/libgf_complete.so.1
# Build/install all other programs
cargo install --root $SNAPCRAFT_PART_INSTALL --bins
# Install multinode scripts
mkdir -p $SNAPCRAFT_PART_INSTALL/bin
cp -av multinode-demo/* $SNAPCRAFT_PART_INSTALL/bin/
# TODO: build curl,rsync/multilog from source instead of sneaking it in from the host
# system...
set -x
mkdir -p $SNAPCRAFT_PART_INSTALL/bin
cp -av /usr/bin/curl $SNAPCRAFT_PART_INSTALL/bin/
cp -av /usr/bin/multilog $SNAPCRAFT_PART_INSTALL/bin/
cp -av /usr/bin/rsync $SNAPCRAFT_PART_INSTALL/bin/

1007
src/bank.rs Executable file

File diff suppressed because it is too large Load Diff

207
src/banking_stage.rs Normal file
View File

@ -0,0 +1,207 @@
//! The `banking_stage` processes Transaction messages. It is intended to be used
//! to contruct a software pipeline. The stage uses all available CPU cores and
//! can do its processing in parallel with signature verification on the GPU.
use bank::Bank;
use bincode::deserialize;
use counter::Counter;
use log::Level;
use packet::{PacketRecycler, Packets, SharedPackets};
use rayon::prelude::*;
use record_stage::Signal;
use result::{Error, Result};
use service::Service;
use std::net::SocketAddr;
use std::sync::atomic::AtomicUsize;
use std::sync::mpsc::{channel, Receiver, RecvTimeoutError, Sender};
use std::sync::Arc;
use std::thread::{self, Builder, JoinHandle};
use std::time::Duration;
use std::time::Instant;
use timing;
use transaction::Transaction;
/// Stores the stage's thread handle and output receiver.
pub struct BankingStage {
/// Handle to the stage's thread.
thread_hdl: JoinHandle<()>,
}
impl BankingStage {
/// Create the stage using `bank`. Exit when `verified_receiver` is dropped.
/// Discard input packets using `packet_recycler` to minimize memory
/// allocations in a previous stage such as the `fetch_stage`.
pub fn new(
bank: Arc<Bank>,
verified_receiver: Receiver<Vec<(SharedPackets, Vec<u8>)>>,
packet_recycler: PacketRecycler,
) -> (Self, Receiver<Signal>) {
let (signal_sender, signal_receiver) = channel();
let thread_hdl = Builder::new()
.name("solana-banking-stage".to_string())
.spawn(move || loop {
if let Err(e) = Self::process_packets(
&bank,
&verified_receiver,
&signal_sender,
&packet_recycler,
) {
match e {
Error::RecvTimeoutError(RecvTimeoutError::Disconnected) => break,
Error::RecvTimeoutError(RecvTimeoutError::Timeout) => (),
_ => error!("{:?}", e),
}
}
})
.unwrap();
(BankingStage { thread_hdl }, signal_receiver)
}
/// Convert the transactions from a blob of binary data to a vector of transactions and
/// an unused `SocketAddr` that could be used to send a response.
fn deserialize_transactions(p: &Packets) -> Vec<Option<(Transaction, SocketAddr)>> {
p.packets
.par_iter()
.map(|x| {
deserialize(&x.data[0..x.meta.size])
.map(|req| (req, x.meta.addr()))
.ok()
})
.collect()
}
/// Process the incoming packets and send output `Signal` messages to `signal_sender`.
/// Discard packets via `packet_recycler`.
pub fn process_packets(
bank: &Arc<Bank>,
verified_receiver: &Receiver<Vec<(SharedPackets, Vec<u8>)>>,
signal_sender: &Sender<Signal>,
packet_recycler: &PacketRecycler,
) -> Result<()> {
let timer = Duration::new(1, 0);
let recv_start = Instant::now();
let mms = verified_receiver.recv_timeout(timer)?;
let mut reqs_len = 0;
let mms_len = mms.len();
info!(
"@{:?} process start stalled for: {:?}ms batches: {}",
timing::timestamp(),
timing::duration_as_ms(&recv_start.elapsed()),
mms.len(),
);
let bank_starting_tx_count = bank.transaction_count();
let count = mms.iter().map(|x| x.1.len()).sum();
let proc_start = Instant::now();
for (msgs, vers) in mms {
let transactions = Self::deserialize_transactions(&msgs.read().unwrap());
reqs_len += transactions.len();
let transactions = transactions
.into_iter()
.zip(vers)
.filter_map(|(tx, ver)| match tx {
None => None,
Some((tx, _addr)) => if tx.verify_plan() && ver != 0 {
Some(tx)
} else {
None
},
})
.collect();
debug!("process_transactions");
let results = bank.process_transactions(transactions);
let transactions = results.into_iter().filter_map(|x| x.ok()).collect();
signal_sender.send(Signal::Transactions(transactions))?;
debug!("done process_transactions");
packet_recycler.recycle(msgs);
}
let total_time_s = timing::duration_as_s(&proc_start.elapsed());
let total_time_ms = timing::duration_as_ms(&proc_start.elapsed());
info!(
"@{:?} done processing transaction batches: {} time: {:?}ms reqs: {} reqs/s: {}",
timing::timestamp(),
mms_len,
total_time_ms,
reqs_len,
(reqs_len as f32) / (total_time_s)
);
inc_new_counter_info!("banking_stage-process_packets", count);
inc_new_counter_info!(
"banking_stage-process_transactions",
bank.transaction_count() - bank_starting_tx_count
);
Ok(())
}
}
impl Service for BankingStage {
fn thread_hdls(self) -> Vec<JoinHandle<()>> {
vec![self.thread_hdl]
}
fn join(self) -> thread::Result<()> {
self.thread_hdl.join()
}
}
// TODO: When banking is pulled out of RequestStage, add this test back in.
//use bank::Bank;
//use entry::Entry;
//use hash::Hash;
//use record_stage::RecordStage;
//use record_stage::Signal;
//use result::Result;
//use std::sync::mpsc::{channel, Sender};
//use std::sync::{Arc, Mutex};
//use std::time::Duration;
//use transaction::Transaction;
//
//#[cfg(test)]
//mod tests {
// use bank::Bank;
// use mint::Mint;
// use signature::{KeyPair, KeyPairUtil};
// use transaction::Transaction;
//
// #[test]
// // TODO: Move this test banking_stage. Calling process_transactions() directly
// // defeats the purpose of this test.
// fn test_banking_sequential_consistency() {
// // In this attack we'll demonstrate that a verifier can interpret the ledger
// // differently if either the server doesn't signal the ledger to add an
// // Entry OR if the verifier tries to parallelize across multiple Entries.
// let mint = Mint::new(2);
// let bank = Bank::new(&mint);
// let banking_stage = EventProcessor::new(bank, &mint.last_id(), None);
//
// // Process a batch that includes a transaction that receives two tokens.
// let alice = KeyPair::new();
// let tx = Transaction::new(&mint.keypair(), alice.pubkey(), 2, mint.last_id());
// let transactions = vec![tx];
// let entry0 = banking_stage.process_transactions(transactions).unwrap();
//
// // Process a second batch that spends one of those tokens.
// let tx = Transaction::new(&alice, mint.pubkey(), 1, mint.last_id());
// let transactions = vec![tx];
// let entry1 = banking_stage.process_transactions(transactions).unwrap();
//
// // Collect the ledger and feed it to a new bank.
// let entries = vec![entry0, entry1];
//
// // Assert the user holds one token, not two. If the server only output one
// // entry, then the second transaction will be rejected, because it drives
// // the account balance below zero before the credit is added.
// let bank = Bank::new(&mint);
// for entry in entries {
// assert!(
// bank
// .process_transactions(entry.transactions)
// .into_iter()
// .all(|x| x.is_ok())
// );
// }
// assert_eq!(bank.get_balance(&alice.pubkey()), Some(1));
// }
//}

90
src/bin/bench-streamer.rs Normal file
View File

@ -0,0 +1,90 @@
extern crate solana;
use solana::packet::{Packet, PacketRecycler, BLOB_SIZE, PACKET_DATA_SIZE};
use solana::result::Result;
use solana::streamer::{receiver, PacketReceiver};
use std::net::{SocketAddr, UdpSocket};
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::mpsc::channel;
use std::sync::Arc;
use std::thread::sleep;
use std::thread::{spawn, JoinHandle};
use std::time::Duration;
use std::time::SystemTime;
fn producer(addr: &SocketAddr, recycler: &PacketRecycler, exit: Arc<AtomicBool>) -> JoinHandle<()> {
let send = UdpSocket::bind("0.0.0.0:0").unwrap();
let msgs = recycler.allocate();
let msgs_ = msgs.clone();
msgs.write().unwrap().packets.resize(10, Packet::default());
for w in &mut msgs.write().unwrap().packets {
w.meta.size = PACKET_DATA_SIZE;
w.meta.set_addr(&addr);
}
spawn(move || loop {
if exit.load(Ordering::Relaxed) {
return;
}
let mut num = 0;
for p in &msgs_.read().unwrap().packets {
let a = p.meta.addr();
assert!(p.meta.size < BLOB_SIZE);
send.send_to(&p.data[..p.meta.size], &a).unwrap();
num += 1;
}
assert_eq!(num, 10);
})
}
fn sink(
recycler: PacketRecycler,
exit: Arc<AtomicBool>,
rvs: Arc<AtomicUsize>,
r: PacketReceiver,
) -> JoinHandle<()> {
spawn(move || loop {
if exit.load(Ordering::Relaxed) {
return;
}
let timer = Duration::new(1, 0);
if let Ok(msgs) = r.recv_timeout(timer) {
rvs.fetch_add(msgs.read().unwrap().packets.len(), Ordering::Relaxed);
recycler.recycle(msgs);
}
})
}
fn main() -> Result<()> {
let read = UdpSocket::bind("127.0.0.1:0")?;
read.set_read_timeout(Some(Duration::new(1, 0)))?;
let addr = read.local_addr()?;
let exit = Arc::new(AtomicBool::new(false));
let pack_recycler = PacketRecycler::default();
let (s_reader, r_reader) = channel();
let t_reader = receiver(read, exit.clone(), pack_recycler.clone(), s_reader);
let t_producer1 = producer(&addr, &pack_recycler, exit.clone());
let t_producer2 = producer(&addr, &pack_recycler, exit.clone());
let t_producer3 = producer(&addr, &pack_recycler, exit.clone());
let rvs = Arc::new(AtomicUsize::new(0));
let t_sink = sink(pack_recycler.clone(), exit.clone(), rvs.clone(), r_reader);
let start = SystemTime::now();
let start_val = rvs.load(Ordering::Relaxed);
sleep(Duration::new(5, 0));
let elapsed = start.elapsed().unwrap();
let end_val = rvs.load(Ordering::Relaxed);
let time = elapsed.as_secs() * 10_000_000_000 + u64::from(elapsed.subsec_nanos());
let ftime = (time as f64) / 10_000_000_000_f64;
let fcount = (end_val - start_val) as f64;
println!("performance: {:?}", fcount / ftime);
exit.store(true, Ordering::Relaxed);
t_reader.join()?;
t_producer1.join()?;
t_producer2.join()?;
t_producer3.join()?;
t_sink.join()?;
Ok(())
}

714
src/bin/bench-tps.rs Normal file
View File

@ -0,0 +1,714 @@
extern crate bincode;
#[macro_use]
extern crate clap;
extern crate influx_db_client;
extern crate rayon;
extern crate serde_json;
extern crate solana;
use clap::{App, Arg};
use influx_db_client as influxdb;
use rayon::prelude::*;
use solana::client::mk_client;
use solana::crdt::{Crdt, NodeInfo};
use solana::drone::DRONE_PORT;
use solana::fullnode::Config;
use solana::hash::Hash;
use solana::logger;
use solana::metrics;
use solana::nat::{get_public_ip_addr, udp_random_bind};
use solana::ncp::Ncp;
use solana::service::Service;
use solana::signature::{read_keypair, GenKeys, Keypair, KeypairUtil};
use solana::thin_client::ThinClient;
use solana::timing::{duration_as_ms, duration_as_s};
use solana::transaction::Transaction;
use solana::wallet::request_airdrop;
use solana::window::default_window;
use std::collections::VecDeque;
use std::fs::File;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
use std::process::exit;
use std::sync::atomic::{AtomicBool, AtomicIsize, Ordering};
use std::sync::{Arc, RwLock};
use std::thread::sleep;
use std::thread::Builder;
use std::thread::JoinHandle;
use std::time::Duration;
use std::time::Instant;
pub struct NodeStats {
pub tps: f64, // Maximum TPS reported by this node
pub tx: u64, // Total transactions reported by this node
}
fn metrics_submit_token_balance(token_balance: i64) {
println!("Token balance: {}", token_balance);
metrics::submit(
influxdb::Point::new("bench-tps")
.add_tag("op", influxdb::Value::String("token_balance".to_string()))
.add_field("balance", influxdb::Value::Integer(token_balance as i64))
.to_owned(),
);
}
fn sample_tx_count(
exit_signal: &Arc<AtomicBool>,
maxes: &Arc<RwLock<Vec<(SocketAddr, NodeStats)>>>,
first_tx_count: u64,
v: &NodeInfo,
sample_period: u64,
) {
let mut client = mk_client(&v);
let mut now = Instant::now();
let mut initial_tx_count = client.transaction_count();
let mut max_tps = 0.0;
let mut total;
let log_prefix = format!("{:21}:", v.contact_info.tpu.to_string());
loop {
let tx_count = client.transaction_count();
assert!(
tx_count >= initial_tx_count,
"expected tx_count({}) >= initial_tx_count({})",
tx_count,
initial_tx_count
);
let duration = now.elapsed();
now = Instant::now();
let sample = tx_count - initial_tx_count;
initial_tx_count = tx_count;
let ns = duration.as_secs() * 1_000_000_000 + u64::from(duration.subsec_nanos());
let tps = (sample * 1_000_000_000) as f64 / ns as f64;
if tps > max_tps {
max_tps = tps;
}
if tx_count > first_tx_count {
total = tx_count - first_tx_count;
} else {
total = 0;
}
println!(
"{} {:9.2} TPS, Transactions: {:6}, Total transactions: {}",
log_prefix, tps, sample, total
);
sleep(Duration::new(sample_period, 0));
if exit_signal.load(Ordering::Relaxed) {
println!("{} Exiting validator thread", log_prefix);
let stats = NodeStats {
tps: max_tps,
tx: total,
};
maxes.write().unwrap().push((v.contact_info.tpu, stats));
break;
}
}
}
/// Send loopback payment of 0 tokens and confirm the network processed it
fn send_barrier_transaction(barrier_client: &mut ThinClient, last_id: &mut Hash, id: &Keypair) {
let transfer_start = Instant::now();
let mut poll_count = 0;
loop {
if poll_count > 0 && poll_count % 8 == 0 {
println!(
"polling for barrier transaction confirmation, attempt {}",
poll_count
);
}
*last_id = barrier_client.get_last_id();
let signature = barrier_client
.transfer(0, &id, id.pubkey(), last_id)
.expect("Unable to send barrier transaction");
let confirmatiom = barrier_client.poll_for_signature(&signature);
let duration_ms = duration_as_ms(&transfer_start.elapsed());
if confirmatiom.is_ok() {
println!("barrier transaction confirmed in {}ms", duration_ms);
metrics::submit(
influxdb::Point::new("bench-tps")
.add_tag(
"op",
influxdb::Value::String("send_barrier_transaction".to_string()),
)
.add_field("poll_count", influxdb::Value::Integer(poll_count))
.add_field("duration", influxdb::Value::Integer(duration_ms as i64))
.to_owned(),
);
// Sanity check that the client balance is still 1
let balance = barrier_client.poll_get_balance(&id.pubkey()).unwrap_or(-1);
if balance != 1 {
panic!("Expected an account balance of 1 (balance: {}", balance);
}
break;
}
// Timeout after 3 minutes. When running a CPU-only leader+validator+drone+bench-tps on a dev
// machine, some batches of transactions can take upwards of 1 minute...
if duration_ms > 1000 * 60 * 3 {
println!("Error: Couldn't confirm barrier transaction!");
exit(1);
}
let new_last_id = barrier_client.get_last_id();
if new_last_id == *last_id {
if poll_count > 0 && poll_count % 8 == 0 {
println!("last_id is not advancing, still at {:?}", *last_id);
}
} else {
*last_id = new_last_id;
}
poll_count += 1;
}
}
fn generate_txs(
shared_txs: &Arc<RwLock<VecDeque<Vec<Transaction>>>>,
id: &Keypair,
keypairs: &[Keypair],
last_id: &Hash,
threads: usize,
reclaim: bool,
) {
let tx_count = keypairs.len();
println!("Signing transactions... {} (reclaim={})", tx_count, reclaim);
let signing_start = Instant::now();
let transactions: Vec<_> = keypairs
.par_iter()
.map(|keypair| {
if !reclaim {
Transaction::new(&id, keypair.pubkey(), 1, *last_id)
} else {
Transaction::new(keypair, id.pubkey(), 1, *last_id)
}
})
.collect();
let duration = signing_start.elapsed();
let ns = duration.as_secs() * 1_000_000_000 + u64::from(duration.subsec_nanos());
let bsps = (tx_count) as f64 / ns as f64;
let nsps = ns as f64 / (tx_count) as f64;
println!(
"Done. {:.2} thousand signatures per second, {:.2} us per signature, {} ms total time",
bsps * 1_000_000_f64,
nsps / 1_000_f64,
duration_as_ms(&duration),
);
metrics::submit(
influxdb::Point::new("bench-tps")
.add_tag("op", influxdb::Value::String("generate_txs".to_string()))
.add_field(
"duration",
influxdb::Value::Integer(duration_as_ms(&duration) as i64),
)
.to_owned(),
);
let sz = transactions.len() / threads;
let chunks: Vec<_> = transactions.chunks(sz).collect();
{
let mut shared_txs_wl = shared_txs.write().unwrap();
for chunk in chunks {
shared_txs_wl.push_back(chunk.to_vec());
}
}
}
fn do_tx_transfers(
exit_signal: &Arc<AtomicBool>,
shared_txs: &Arc<RwLock<VecDeque<Vec<Transaction>>>>,
leader: &NodeInfo,
shared_tx_thread_count: &Arc<AtomicIsize>,
) {
let client = mk_client(&leader);
loop {
let txs;
{
let mut shared_txs_wl = shared_txs.write().unwrap();
txs = shared_txs_wl.pop_front();
}
if let Some(txs0) = txs {
shared_tx_thread_count.fetch_add(1, Ordering::Relaxed);
println!(
"Transferring 1 unit {} times... to {}",
txs0.len(),
leader.contact_info.tpu
);
let tx_len = txs0.len();
let transfer_start = Instant::now();
for tx in txs0 {
client.transfer_signed(&tx).unwrap();
}
shared_tx_thread_count.fetch_add(-1, Ordering::Relaxed);
println!(
"Tx send done. {} ms {} tps",
duration_as_ms(&transfer_start.elapsed()),
tx_len as f32 / duration_as_s(&transfer_start.elapsed()),
);
metrics::submit(
influxdb::Point::new("bench-tps")
.add_tag("op", influxdb::Value::String("do_tx_transfers".to_string()))
.add_field(
"duration",
influxdb::Value::Integer(duration_as_ms(&transfer_start.elapsed()) as i64),
)
.add_field("count", influxdb::Value::Integer(tx_len as i64))
.to_owned(),
);
}
if exit_signal.load(Ordering::Relaxed) {
break;
}
}
}
fn airdrop_tokens(client: &mut ThinClient, leader: &NodeInfo, id: &Keypair, tx_count: i64) {
let mut drone_addr = leader.contact_info.tpu;
drone_addr.set_port(DRONE_PORT);
let starting_balance = client.poll_get_balance(&id.pubkey()).unwrap();
metrics_submit_token_balance(starting_balance);
if starting_balance < tx_count {
let airdrop_amount = tx_count - starting_balance;
println!(
"Airdropping {:?} tokens from {}",
airdrop_amount, drone_addr
);
let previous_balance = starting_balance;
request_airdrop(&drone_addr, &id.pubkey(), airdrop_amount as u64).unwrap();
// TODO: return airdrop Result from Drone instead of polling the
// network
let mut current_balance = previous_balance;
for _ in 0..20 {
sleep(Duration::from_millis(500));
current_balance = client.poll_get_balance(&id.pubkey()).unwrap();
if starting_balance != current_balance {
break;
}
println!(".");
}
metrics_submit_token_balance(current_balance);
if current_balance - starting_balance != airdrop_amount {
println!("Airdrop failed!");
exit(1);
}
}
}
fn compute_and_report_stats(
maxes: &Arc<RwLock<Vec<(SocketAddr, NodeStats)>>>,
sample_period: u64,
tx_send_elapsed: &Duration,
) {
// Compute/report stats
let mut max_of_maxes = 0.0;
let mut total_txs = 0;
let mut nodes_with_zero_tps = 0;
let mut total_maxes = 0.0;
println!(" Node address | Max TPS | Total Transactions");
println!("---------------------+---------------+--------------------");
for (sock, stats) in maxes.read().unwrap().iter() {
let maybe_flag = match stats.tx {
0 => "!!!!!",
_ => "",
};
println!(
"{:20} | {:13.2} | {} {}",
(*sock).to_string(),
stats.tps,
stats.tx,
maybe_flag
);
if stats.tps == 0.0 {
nodes_with_zero_tps += 1;
}
total_maxes += stats.tps;
if stats.tps > max_of_maxes {
max_of_maxes = stats.tps;
}
total_txs += stats.tx;
}
if total_maxes > 0.0 {
let num_nodes_with_tps = maxes.read().unwrap().len() - nodes_with_zero_tps;
let average_max = total_maxes / num_nodes_with_tps as f64;
println!(
"\nAverage max TPS: {:.2}, {} nodes had 0 TPS",
average_max, nodes_with_zero_tps
);
}
println!(
"\nHighest TPS: {:.2} sampling period {}s total transactions: {} clients: {}",
max_of_maxes,
sample_period,
total_txs,
maxes.read().unwrap().len()
);
println!(
"\tAverage TPS: {}",
total_txs as f32 / duration_as_s(tx_send_elapsed)
);
}
fn main() {
logger::setup();
metrics::set_panic_hook("bench-tps");
let mut threads = 4usize;
let mut num_nodes = 1usize;
let mut time_sec = 90;
let mut sustained = false;
let mut tx_count = 500_000;
let matches = App::new("solana-bench-tps")
.version(crate_version!())
.arg(
Arg::with_name("leader")
.short("l")
.long("leader")
.value_name("PATH")
.takes_value(true)
.help("/path/to/leader.json"),
)
.arg(
Arg::with_name("keypair")
.short("k")
.long("keypair")
.value_name("PATH")
.takes_value(true)
.default_value("~/.config/solana/id.json")
.help("/path/to/id.json"),
)
.arg(
Arg::with_name("num_nodes")
.short("n")
.long("nodes")
.value_name("NUMBER")
.takes_value(true)
.help("number of nodes to converge to"),
)
.arg(
Arg::with_name("threads")
.short("t")
.long("threads")
.value_name("NUMBER")
.takes_value(true)
.help("number of threads"),
)
.arg(
Arg::with_name("seconds")
.short("s")
.long("sec")
.value_name("NUMBER")
.takes_value(true)
.help("send transactions for this many seconds"),
)
.arg(
Arg::with_name("converge_only")
.short("c")
.help("exit immediately after converging"),
)
.arg(
Arg::with_name("addr")
.short("a")
.long("addr")
.value_name("IPADDR")
.takes_value(true)
.help("address to advertise to the network"),
)
.arg(
Arg::with_name("sustained")
.long("sustained")
.help("Use sustained performance mode vs. peak mode. This overlaps the tx generation with transfers."),
)
.arg(
Arg::with_name("tx_count")
.long("tx_count")
.value_name("NUMBER")
.takes_value(true)
.help("number of transactions to send in a single batch")
)
.get_matches();
let leader: NodeInfo;
if let Some(l) = matches.value_of("leader") {
leader = read_leader(l).node_info;
} else {
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8000);
leader = NodeInfo::new_leader(&server_addr);
};
let id = read_keypair(matches.value_of("keypair").unwrap()).expect("client keypair");
if let Some(t) = matches.value_of("threads") {
threads = t.to_string().parse().expect("integer");
}
if let Some(n) = matches.value_of("num_nodes") {
num_nodes = n.to_string().parse().expect("integer");
}
if let Some(s) = matches.value_of("seconds") {
time_sec = s.to_string().parse().expect("integer");
}
let addr = if let Some(s) = matches.value_of("addr") {
s.to_string().parse().unwrap_or_else(|e| {
eprintln!("failed to parse {} as IP address error: {:?}", s, e);
exit(1);
})
} else {
get_public_ip_addr().unwrap_or_else(|e| {
eprintln!("failed to get public IP, try --addr? error: {:?}", e);
exit(1);
})
};
if let Some(s) = matches.value_of("tx_count") {
tx_count = s.to_string().parse().expect("integer");
}
if matches.is_present("sustained") {
sustained = true;
}
let exit_signal = Arc::new(AtomicBool::new(false));
let mut c_threads = vec![];
let validators = converge(&leader, &exit_signal, num_nodes, &mut c_threads, addr);
println!(" Node address | Node identifier");
println!("----------------------+------------------");
for node in &validators {
println!(
" {:20} | {:16x}",
node.contact_info.tpu.to_string(),
node.debug_id()
);
}
println!("Nodes: {}", validators.len());
if validators.len() < num_nodes {
println!(
"Error: Insufficient nodes discovered. Expecting {} or more",
num_nodes
);
exit(1);
}
if matches.is_present("converge_only") {
return;
}
let mut client = mk_client(&leader);
let mut barrier_client = mk_client(&leader);
let mut seed = [0u8; 32];
seed.copy_from_slice(&id.public_key_bytes()[..32]);
let mut rnd = GenKeys::new(seed);
println!("Creating {} keypairs...", tx_count / 2);
let keypairs = rnd.gen_n_keypairs(tx_count / 2);
let barrier_id = rnd.gen_n_keypairs(1).pop().unwrap();
println!("Get tokens...");
airdrop_tokens(&mut client, &leader, &id, tx_count);
airdrop_tokens(&mut barrier_client, &leader, &barrier_id, 1);
println!("Get last ID...");
let mut last_id = client.get_last_id();
println!("Got last ID {:?}", last_id);
let first_tx_count = client.transaction_count();
println!("Initial transaction count {}", first_tx_count);
// Setup a thread per validator to sample every period
// collect the max transaction rate and total tx count seen
let maxes = Arc::new(RwLock::new(Vec::new()));
let sample_period = 1; // in seconds
println!("Sampling TPS every {} second...", sample_period);
let v_threads: Vec<_> = validators
.into_iter()
.map(|v| {
let exit_signal = exit_signal.clone();
let maxes = maxes.clone();
Builder::new()
.name("solana-client-sample".to_string())
.spawn(move || {
sample_tx_count(&exit_signal, &maxes, first_tx_count, &v, sample_period);
})
.unwrap()
})
.collect();
let shared_txs: Arc<RwLock<VecDeque<Vec<Transaction>>>> =
Arc::new(RwLock::new(VecDeque::new()));
let shared_tx_active_thread_count = Arc::new(AtomicIsize::new(0));
let s_threads: Vec<_> = (0..threads)
.map(|_| {
let exit_signal = exit_signal.clone();
let shared_txs = shared_txs.clone();
let leader = leader.clone();
let shared_tx_active_thread_count = shared_tx_active_thread_count.clone();
Builder::new()
.name("solana-client-sender".to_string())
.spawn(move || {
do_tx_transfers(
&exit_signal,
&shared_txs,
&leader,
&shared_tx_active_thread_count,
);
})
.unwrap()
})
.collect();
// generate and send transactions for the specified duration
let time = Duration::new(time_sec, 0);
let now = Instant::now();
let mut reclaim_tokens_back_to_source_account = false;
while now.elapsed() < time || reclaim_tokens_back_to_source_account {
let balance = client.poll_get_balance(&id.pubkey()).unwrap_or(-1);
metrics_submit_token_balance(balance);
// ping-pong between source and destination accounts for each loop iteration
// this seems to be faster than trying to determine the balance of individual
// accounts
generate_txs(
&shared_txs,
&id,
&keypairs,
&last_id,
threads,
reclaim_tokens_back_to_source_account,
);
reclaim_tokens_back_to_source_account = !reclaim_tokens_back_to_source_account;
// In sustained mode overlap the transfers with generation
// this has higher average performance but lower peak performance
// in tested environments.
if !sustained {
while shared_tx_active_thread_count.load(Ordering::Relaxed) > 0 {
sleep(Duration::from_millis(100));
}
}
// It's not feasible (would take too much time) to confirm each of the `tx_count / 2`
// transactions sent by `generate_txs()` so instead send and confirm a single transaction
// to validate the network is still functional.
send_barrier_transaction(&mut barrier_client, &mut last_id, &barrier_id);
}
// Stop the sampling threads so it will collect the stats
exit_signal.store(true, Ordering::Relaxed);
println!("Waiting for validator threads...");
for t in v_threads {
if let Err(err) = t.join() {
println!(" join() failed with: {:?}", err);
}
}
// join the tx send threads
println!("Waiting for transmit threads...");
for t in s_threads {
if let Err(err) = t.join() {
println!(" join() failed with: {:?}", err);
}
}
let balance = client.poll_get_balance(&id.pubkey()).unwrap_or(-1);
metrics_submit_token_balance(balance);
compute_and_report_stats(&maxes, sample_period, &now.elapsed());
// join the crdt client threads
for t in c_threads {
t.join().unwrap();
}
}
fn spy_node(addr: IpAddr) -> (NodeInfo, UdpSocket) {
let gossip_socket = udp_random_bind(8000, 10000, 5).unwrap();
let gossip_addr = SocketAddr::new(addr, gossip_socket.local_addr().unwrap().port());
let pubkey = Keypair::new().pubkey();
let daddr = "0.0.0.0:0".parse().unwrap();
assert!(!gossip_addr.ip().is_unspecified());
assert!(!gossip_addr.ip().is_multicast());
let node = NodeInfo::new(pubkey, gossip_addr, daddr, daddr, daddr, daddr);
(node, gossip_socket)
}
fn converge(
leader: &NodeInfo,
exit_signal: &Arc<AtomicBool>,
num_nodes: usize,
threads: &mut Vec<JoinHandle<()>>,
addr: IpAddr,
) -> Vec<NodeInfo> {
//lets spy on the network
let (spy, spy_gossip) = spy_node(addr);
let mut spy_crdt = Crdt::new(spy).expect("Crdt::new");
spy_crdt.insert(&leader);
spy_crdt.set_leader(leader.id);
let spy_ref = Arc::new(RwLock::new(spy_crdt));
let window = default_window();
let gossip_send_socket = udp_random_bind(8000, 10000, 5).unwrap();
let ncp = Ncp::new(
&spy_ref,
window.clone(),
None,
spy_gossip,
gossip_send_socket,
exit_signal.clone(),
).expect("DataReplicator::new");
let mut v: Vec<NodeInfo> = vec![];
//wait for the network to converge, 30 seconds should be plenty
for _ in 0..30 {
v = spy_ref
.read()
.unwrap()
.table
.values()
.filter(|x| Crdt::is_valid_address(x.contact_info.rpu))
.cloned()
.collect();
if v.len() >= num_nodes {
println!("CONVERGED!");
break;
} else {
println!(
"{} node(s) discovered (looking for {} or more)",
v.len(),
num_nodes
);
}
sleep(Duration::new(1, 0));
}
threads.extend(ncp.thread_hdls().into_iter());
v
}
fn read_leader(path: &str) -> Config {
let file = File::open(path).unwrap_or_else(|_| panic!("file not found: {}", path));
serde_json::from_reader(file).unwrap_or_else(|_| panic!("failed to parse {}", path))
}

149
src/bin/drone.rs Normal file
View File

@ -0,0 +1,149 @@
extern crate bincode;
#[macro_use]
extern crate clap;
extern crate serde_json;
extern crate solana;
extern crate tokio;
extern crate tokio_codec;
extern crate tokio_io;
use bincode::deserialize;
use clap::{App, Arg};
use solana::crdt::NodeInfo;
use solana::drone::{Drone, DroneRequest, DRONE_PORT};
use solana::fullnode::Config;
use solana::logger;
use solana::metrics::set_panic_hook;
use solana::signature::read_keypair;
use std::fs::File;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::{Arc, Mutex};
use std::thread;
use tokio::net::TcpListener;
use tokio::prelude::*;
use tokio_codec::{BytesCodec, Decoder};
fn main() {
logger::setup();
set_panic_hook("drone");
let matches = App::new("drone")
.version(crate_version!())
.arg(
Arg::with_name("leader")
.short("l")
.long("leader")
.value_name("PATH")
.takes_value(true)
.help("/path/to/leader.json"),
)
.arg(
Arg::with_name("keypair")
.short("k")
.long("keypair")
.value_name("PATH")
.takes_value(true)
.required(true)
.help("/path/to/mint.json"),
)
.arg(
Arg::with_name("time")
.short("t")
.long("time")
.value_name("SECONDS")
.takes_value(true)
.help("time slice over which to limit requests to drone"),
)
.arg(
Arg::with_name("cap")
.short("c")
.long("cap")
.value_name("NUMBER")
.takes_value(true)
.help("request limit for time slice"),
)
.get_matches();
let leader: NodeInfo;
if let Some(l) = matches.value_of("leader") {
leader = read_leader(l).node_info;
} else {
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8000);
leader = NodeInfo::new_leader(&server_addr);
};
let mint_keypair =
read_keypair(matches.value_of("keypair").expect("keypair")).expect("client keypair");
let time_slice: Option<u64>;
if let Some(t) = matches.value_of("time") {
time_slice = Some(t.to_string().parse().expect("integer"));
} else {
time_slice = None;
}
let request_cap: Option<u64>;
if let Some(c) = matches.value_of("cap") {
request_cap = Some(c.to_string().parse().expect("integer"));
} else {
request_cap = None;
}
let drone_addr: SocketAddr = format!("0.0.0.0:{}", DRONE_PORT).parse().unwrap();
let drone = Arc::new(Mutex::new(Drone::new(
mint_keypair,
drone_addr,
leader.contact_info.tpu,
leader.contact_info.rpu,
time_slice,
request_cap,
)));
let drone1 = drone.clone();
thread::spawn(move || loop {
let time = drone1.lock().unwrap().time_slice;
thread::sleep(time);
drone1.lock().unwrap().clear_request_count();
});
let socket = TcpListener::bind(&drone_addr).unwrap();
println!("Drone started. Listening on: {}", drone_addr);
let done = socket
.incoming()
.map_err(|e| println!("failed to accept socket; error = {:?}", e))
.for_each(move |socket| {
let drone2 = drone.clone();
// let client_ip = socket.peer_addr().expect("drone peer_addr").ip();
let framed = BytesCodec::new().framed(socket);
let (_writer, reader) = framed.split();
let processor = reader
.for_each(move |bytes| {
let req: DroneRequest = deserialize(&bytes).or_else(|err| {
use std::io;
Err(io::Error::new(
io::ErrorKind::Other,
format!("deserialize packet in drone: {:?}", err),
))
})?;
println!("Airdrop requested...");
// let res = drone2.lock().unwrap().check_rate_limit(client_ip);
let res1 = drone2.lock().unwrap().send_airdrop(req);
match res1 {
Ok(_) => println!("Airdrop sent!"),
Err(_) => println!("Request limit reached for this time slice"),
}
Ok(())
})
.then(|result| {
println!("Socket closed with result: {:?}", result);
Ok(())
});
tokio::spawn(processor)
});
tokio::run(done);
}
fn read_leader(path: &str) -> Config {
let file = File::open(path).unwrap_or_else(|_| panic!("file not found: {}", path));
serde_json::from_reader(file).unwrap_or_else(|_| panic!("failed to parse {}", path))
}

View File

@ -0,0 +1,83 @@
#[macro_use]
extern crate clap;
extern crate dirs;
extern crate serde_json;
extern crate solana;
use clap::{App, Arg};
use solana::crdt::{get_ip_addr, parse_port_or_addr};
use solana::fullnode::Config;
use solana::nat::get_public_ip_addr;
use solana::signature::read_pkcs8;
use std::io;
use std::net::SocketAddr;
fn main() {
let matches = App::new("fullnode-config")
.version(crate_version!())
.arg(
Arg::with_name("local")
.short("l")
.long("local")
.takes_value(false)
.help("detect network address from local machine configuration"),
)
.arg(
Arg::with_name("keypair")
.short("k")
.long("keypair")
.value_name("PATH")
.takes_value(true)
.help("/path/to/id.json"),
)
.arg(
Arg::with_name("public")
.short("p")
.long("public")
.takes_value(false)
.help("detect public network address using public servers"),
)
.arg(
Arg::with_name("bind")
.short("b")
.long("bind")
.value_name("PORT")
.takes_value(true)
.help("bind to port or address"),
)
.get_matches();
let bind_addr: SocketAddr = {
let mut bind_addr = parse_port_or_addr({
if let Some(b) = matches.value_of("bind") {
Some(b.to_string())
} else {
None
}
});
if matches.is_present("local") {
let ip = get_ip_addr().unwrap();
bind_addr.set_ip(ip);
}
if matches.is_present("public") {
let ip = get_public_ip_addr().unwrap();
bind_addr.set_ip(ip);
}
bind_addr
};
let mut path = dirs::home_dir().expect("home directory");
let id_path = if matches.is_present("keypair") {
matches.value_of("keypair").unwrap()
} else {
path.extend(&[".config", "solana", "id.json"]);
path.to_str().unwrap()
};
let pkcs8 = read_pkcs8(id_path).expect("client keypair");
// we need all the receiving sockets to be bound within the expected
// port range that we open on aws
let config = Config::new(&bind_addr, pkcs8);
let stdout = io::stdout();
serde_json::to_writer(stdout, &config).expect("serialize");
}

118
src/bin/fullnode.rs Normal file
View File

@ -0,0 +1,118 @@
#[macro_use]
extern crate clap;
extern crate getopts;
extern crate log;
extern crate serde_json;
extern crate solana;
use clap::{App, Arg};
use solana::client::mk_client;
use solana::crdt::{NodeInfo, TestNode};
use solana::drone::DRONE_PORT;
use solana::fullnode::{Config, Fullnode};
use solana::logger;
use solana::metrics::set_panic_hook;
use solana::service::Service;
use solana::signature::{Keypair, KeypairUtil};
use solana::wallet::request_airdrop;
use std::fs::File;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::process::exit;
fn main() -> () {
logger::setup();
set_panic_hook("fullnode");
let matches = App::new("fullnode")
.version(crate_version!())
.arg(
Arg::with_name("identity")
.short("i")
.long("identity")
.value_name("FILE")
.takes_value(true)
.help("run with the identity found in FILE"),
)
.arg(
Arg::with_name("testnet")
.short("t")
.long("testnet")
.value_name("HOST:PORT")
.takes_value(true)
.help("connect to the network at this gossip entry point"),
)
.arg(
Arg::with_name("ledger")
.short("l")
.long("ledger")
.value_name("DIR")
.takes_value(true)
.required(true)
.help("use DIR as persistent ledger location"),
)
.get_matches();
let bind_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8000);
let mut keypair = Keypair::new();
let mut repl_data = NodeInfo::new_leader_with_pubkey(keypair.pubkey(), &bind_addr);
if let Some(i) = matches.value_of("identity") {
let path = i.to_string();
if let Ok(file) = File::open(path.clone()) {
let parse: serde_json::Result<Config> = serde_json::from_reader(file);
if let Ok(data) = parse {
keypair = data.keypair();
repl_data = data.node_info;
} else {
eprintln!("failed to parse {}", path);
exit(1);
}
} else {
eprintln!("failed to read {}", path);
exit(1);
}
}
let leader_pubkey = keypair.pubkey();
let repl_clone = repl_data.clone();
let ledger_path = matches.value_of("ledger").unwrap();
let mut node = TestNode::new_with_bind_addr(repl_data, bind_addr);
let mut drone_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), DRONE_PORT);
let fullnode = if let Some(t) = matches.value_of("testnet") {
let testnet_address_string = t.to_string();
let testnet_addr: SocketAddr = testnet_address_string.parse().unwrap();
drone_addr.set_ip(testnet_addr.ip());
Fullnode::new(node, false, ledger_path, keypair, Some(testnet_addr))
} else {
node.data.leader_id = node.data.id;
Fullnode::new(node, true, ledger_path, keypair, None)
};
let mut client = mk_client(&repl_clone);
let previous_balance = client.poll_get_balance(&leader_pubkey).unwrap();
eprintln!("balance is {}", previous_balance);
if previous_balance == 0 {
eprintln!("requesting airdrop from {}", drone_addr);
request_airdrop(&drone_addr, &leader_pubkey, 50).unwrap_or_else(|_| {
panic!(
"Airdrop failed, is the drone address correct {:?} drone running?",
drone_addr
)
});
// Try multiple times to confirm a non-zero balance. |poll_get_balance| currently times
// out after 1 second, and sometimes this is not enough time while the network is
// booting
let balance_ok = (0..30).any(|i| {
let balance = client.poll_get_balance(&leader_pubkey).unwrap();
eprintln!("new balance is {} (attempt #{})", balance, i);
balance > 0
});
assert!(balance_ok, "0 balance, airdrop failed?");
}
fullnode.join().expect("join");
}

62
src/bin/genesis.rs Normal file
View File

@ -0,0 +1,62 @@
//! A command-line executable for generating the chain's genesis block.
extern crate atty;
#[macro_use]
extern crate clap;
extern crate serde_json;
extern crate solana;
use atty::{is, Stream};
use clap::{App, Arg};
use solana::ledger::LedgerWriter;
use solana::mint::Mint;
use std::error;
use std::io::{stdin, Read};
use std::process::exit;
fn main() -> Result<(), Box<error::Error>> {
let matches = App::new("solana-genesis")
.version(crate_version!())
.arg(
Arg::with_name("tokens")
.short("t")
.long("tokens")
.value_name("NUMBER")
.takes_value(true)
.required(true)
.help("Number of tokens with which to initialize mint"),
)
.arg(
Arg::with_name("ledger")
.short("l")
.long("ledger")
.value_name("DIR")
.takes_value(true)
.required(true)
.help("use DIR as persistent ledger location"),
)
.get_matches();
let tokens = value_t_or_exit!(matches, "tokens", i64);
let ledger_path = matches.value_of("ledger").unwrap();
if is(Stream::Stdin) {
eprintln!("nothing found on stdin, expected a json file");
exit(1);
}
let mut buffer = String::new();
let num_bytes = stdin().read_to_string(&mut buffer)?;
if num_bytes == 0 {
eprintln!("empty file on stdin, expected a json file");
exit(1);
}
let pkcs8: Vec<u8> = serde_json::from_str(&buffer)?;
let mint = Mint::new_with_pkcs8(tokens, pkcs8);
let mut ledger_writer = LedgerWriter::open(&ledger_path, true)?;
ledger_writer.write_entries(mint.create_entries())?;
Ok(())
}

51
src/bin/keygen.rs Normal file
View File

@ -0,0 +1,51 @@
#[macro_use]
extern crate clap;
extern crate dirs;
extern crate ring;
extern crate serde_json;
use clap::{App, Arg};
use ring::rand::SystemRandom;
use ring::signature::Ed25519KeyPair;
use std::error;
use std::fs::{self, File};
use std::io::Write;
use std::path::Path;
fn main() -> Result<(), Box<error::Error>> {
let matches = App::new("solana-keygen")
.version(crate_version!())
.arg(
Arg::with_name("outfile")
.short("o")
.long("outfile")
.value_name("PATH")
.takes_value(true)
.help("path to generated file"),
)
.get_matches();
let rnd = SystemRandom::new();
let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rnd)?;
let serialized = serde_json::to_string(&pkcs8_bytes.to_vec())?;
let mut path = dirs::home_dir().expect("home directory");
let outfile = if matches.is_present("outfile") {
matches.value_of("outfile").unwrap()
} else {
path.extend(&[".config", "solana", "id.json"]);
path.to_str().unwrap()
};
if outfile == "-" {
println!("{}", serialized);
} else {
if let Some(outdir) = Path::new(outfile).parent() {
fs::create_dir_all(outdir)?;
}
let mut f = File::create(outfile)?;
f.write_all(&serialized.into_bytes())?;
}
Ok(())
}

137
src/bin/ledger-tool.rs Normal file
View File

@ -0,0 +1,137 @@
#[macro_use]
extern crate clap;
extern crate serde_json;
extern crate solana;
use clap::{App, Arg, SubCommand};
use solana::bank::Bank;
use solana::ledger::{read_ledger, verify_ledger};
use solana::logger;
use std::io::{stdout, Write};
use std::process::exit;
fn main() {
logger::setup();
let matches = App::new("ledger-tool")
.version(crate_version!())
.arg(
Arg::with_name("ledger")
.short("l")
.long("ledger")
.value_name("DIR")
.takes_value(true)
.required(true)
.help("use DIR for ledger location"),
)
.arg(
Arg::with_name("head")
.short("n")
.long("head")
.value_name("NUM")
.takes_value(true)
.help("at most the first NUM entries in ledger\n (only applies to verify, print, json commands)"),
)
.arg(
Arg::with_name("precheck")
.short("p")
.long("precheck")
.help("use ledger_verify() to check internal ledger consistency before proceeding"),
)
.subcommand(SubCommand::with_name("print").about("Print the ledger"))
.subcommand(SubCommand::with_name("json").about("Print the ledger in JSON format"))
.subcommand(SubCommand::with_name("verify").about("Verify the ledger's PoH"))
.get_matches();
let ledger_path = matches.value_of("ledger").unwrap();
if matches.is_present("precheck") {
if let Err(e) = verify_ledger(&ledger_path) {
eprintln!("ledger precheck failed, error: {:?} ", e);
exit(1);
}
}
let entries = match read_ledger(ledger_path, true) {
Ok(entries) => entries,
Err(err) => {
eprintln!("Failed to open ledger at {}: {}", ledger_path, err);
exit(1);
}
};
let head = match matches.value_of("head") {
Some(head) => head.parse().expect("please pass a number for --head"),
None => <usize>::max_value(),
};
match matches.subcommand() {
("print", _) => {
let entries = match read_ledger(ledger_path, true) {
Ok(entries) => entries,
Err(err) => {
eprintln!("Failed to open ledger at {}: {}", ledger_path, err);
exit(1);
}
};
for (i, entry) in entries.enumerate() {
if i >= head {
break;
}
let entry = entry.unwrap();
println!("{:?}", entry);
}
}
("json", _) => {
stdout().write_all(b"{\"ledger\":[\n").expect("open array");
for (i, entry) in entries.enumerate() {
if i >= head {
break;
}
let entry = entry.unwrap();
serde_json::to_writer(stdout(), &entry).expect("serialize");
stdout().write_all(b",\n").expect("newline");
}
stdout().write_all(b"\n]}\n").expect("close array");
}
("verify", _) => {
if head < 2 {
eprintln!("verify requires at least 2 entries to run");
exit(1);
}
let bank = Bank::default();
{
let genesis = match read_ledger(ledger_path, true) {
Ok(entries) => entries,
Err(err) => {
eprintln!("Failed to open ledger at {}: {}", ledger_path, err);
exit(1);
}
};
let genesis = genesis.take(2).map(|e| e.unwrap());
if let Err(e) = bank.process_ledger(genesis) {
eprintln!("verify failed at genesis err: {:?}", e);
exit(1);
}
}
let entries = entries.map(|e| e.unwrap());
let head = head - 2;
for (i, entry) in entries.skip(2).enumerate() {
if i >= head {
break;
}
if let Err(e) = bank.process_entry(entry) {
eprintln!("verify failed at entry[{}], err: {:?}", i + 2, e);
exit(1);
}
}
}
("", _) => {
eprintln!("{}", matches.usage());
exit(1);
}
_ => unreachable!(),
};
}

315
src/bin/wallet.rs Normal file
View File

@ -0,0 +1,315 @@
extern crate atty;
extern crate bincode;
extern crate bs58;
#[macro_use]
extern crate clap;
extern crate dirs;
extern crate serde_json;
extern crate solana;
use clap::{App, Arg, SubCommand};
use solana::client::mk_client;
use solana::crdt::NodeInfo;
use solana::drone::DRONE_PORT;
use solana::fullnode::Config;
use solana::logger;
use solana::signature::{read_keypair, Keypair, KeypairUtil, Pubkey, Signature};
use solana::thin_client::ThinClient;
use solana::wallet::request_airdrop;
use std::error;
use std::fmt;
use std::fs::File;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::thread::sleep;
use std::time::Duration;
enum WalletCommand {
Address,
Balance,
AirDrop(i64),
Pay(i64, Pubkey),
Confirm(Signature),
}
#[derive(Debug, Clone)]
enum WalletError {
CommandNotRecognized(String),
BadParameter(String),
}
impl fmt::Display for WalletError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "invalid")
}
}
impl error::Error for WalletError {
fn description(&self) -> &str {
"invalid"
}
fn cause(&self) -> Option<&error::Error> {
// Generic error, underlying cause isn't tracked.
None
}
}
struct WalletConfig {
leader: NodeInfo,
id: Keypair,
drone_addr: SocketAddr,
command: WalletCommand,
}
impl Default for WalletConfig {
fn default() -> WalletConfig {
let default_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8000);
WalletConfig {
leader: NodeInfo::new_leader(&default_addr),
id: Keypair::new(),
drone_addr: default_addr,
command: WalletCommand::Balance,
}
}
}
fn parse_args() -> Result<WalletConfig, Box<error::Error>> {
let matches = App::new("solana-wallet")
.version(crate_version!())
.arg(
Arg::with_name("leader")
.short("l")
.long("leader")
.value_name("PATH")
.takes_value(true)
.help("/path/to/leader.json"),
)
.arg(
Arg::with_name("keypair")
.short("k")
.long("keypair")
.value_name("PATH")
.takes_value(true)
.help("/path/to/id.json"),
)
.subcommand(
SubCommand::with_name("airdrop")
.about("Request a batch of tokens")
.arg(
Arg::with_name("tokens")
// .index(1)
.long("tokens")
.value_name("NUMBER")
.takes_value(true)
.required(true)
.help("The number of tokens to request"),
),
)
.subcommand(
SubCommand::with_name("pay")
.about("Send a payment")
.arg(
Arg::with_name("tokens")
// .index(2)
.long("tokens")
.value_name("NUMBER")
.takes_value(true)
.required(true)
.help("the number of tokens to send"),
)
.arg(
Arg::with_name("to")
// .index(1)
.long("to")
.value_name("PUBKEY")
.takes_value(true)
.help("The pubkey of recipient"),
),
)
.subcommand(
SubCommand::with_name("confirm")
.about("Confirm your payment by signature")
.arg(
Arg::with_name("signature")
.index(1)
.value_name("SIGNATURE")
.required(true)
.help("The transaction signature to confirm"),
),
)
.subcommand(SubCommand::with_name("balance").about("Get your balance"))
.subcommand(SubCommand::with_name("address").about("Get your public key"))
.get_matches();
let leader: NodeInfo;
if let Some(l) = matches.value_of("leader") {
leader = read_leader(l)?.node_info;
} else {
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8000);
leader = NodeInfo::new_leader(&server_addr);
};
let mut path = dirs::home_dir().expect("home directory");
let id_path = if matches.is_present("keypair") {
matches.value_of("keypair").unwrap()
} else {
path.extend(&[".config", "solana", "id.json"]);
path.to_str().unwrap()
};
let id = read_keypair(id_path).or_else(|err| {
Err(WalletError::BadParameter(format!(
"{}: Unable to open keypair file: {}",
err, id_path
)))
})?;
let mut drone_addr = leader.contact_info.tpu;
drone_addr.set_port(DRONE_PORT);
let command = match matches.subcommand() {
("airdrop", Some(airdrop_matches)) => {
let tokens = airdrop_matches.value_of("tokens").unwrap().parse()?;
Ok(WalletCommand::AirDrop(tokens))
}
("pay", Some(pay_matches)) => {
let to = if pay_matches.is_present("to") {
let pubkey_vec = bs58::decode(pay_matches.value_of("to").unwrap())
.into_vec()
.expect("base58-encoded public key");
if pubkey_vec.len() != std::mem::size_of::<Pubkey>() {
eprintln!("{}", pay_matches.usage());
Err(WalletError::BadParameter("Invalid public key".to_string()))?;
}
Pubkey::new(&pubkey_vec)
} else {
id.pubkey()
};
let tokens = pay_matches.value_of("tokens").unwrap().parse()?;
Ok(WalletCommand::Pay(tokens, to))
}
("confirm", Some(confirm_matches)) => {
let signatures = bs58::decode(confirm_matches.value_of("signature").unwrap())
.into_vec()
.expect("base58-encoded signature");
if signatures.len() == std::mem::size_of::<Signature>() {
let signature = Signature::new(&signatures);
Ok(WalletCommand::Confirm(signature))
} else {
eprintln!("{}", confirm_matches.usage());
Err(WalletError::BadParameter("Invalid signature".to_string()))
}
}
("balance", Some(_balance_matches)) => Ok(WalletCommand::Balance),
("address", Some(_address_matches)) => Ok(WalletCommand::Address),
("", None) => {
println!("{}", matches.usage());
Err(WalletError::CommandNotRecognized(
"no subcommand given".to_string(),
))
}
_ => unreachable!(),
}?;
Ok(WalletConfig {
leader,
id,
drone_addr, // TODO: Add an option for this.
command,
})
}
fn process_command(
config: &WalletConfig,
client: &mut ThinClient,
) -> Result<(), Box<error::Error>> {
match config.command {
// Check client balance
WalletCommand::Address => {
println!("{}", config.id.pubkey());
}
WalletCommand::Balance => {
println!("Balance requested...");
let balance = client.poll_get_balance(&config.id.pubkey());
match balance {
Ok(balance) => {
println!("Your balance is: {:?}", balance);
}
Err(ref e) if e.kind() == std::io::ErrorKind::Other => {
println!("No account found! Request an airdrop to get started.");
}
Err(error) => {
println!("An error occurred: {:?}", error);
Err(error)?;
}
}
}
// Request an airdrop from Solana Drone;
// Request amount is set in request_airdrop function
WalletCommand::AirDrop(tokens) => {
println!(
"Requesting airdrop of {:?} tokens from {}",
tokens, config.drone_addr
);
let previous_balance = client.poll_get_balance(&config.id.pubkey())?;
request_airdrop(&config.drone_addr, &config.id.pubkey(), tokens as u64)?;
// TODO: return airdrop Result from Drone instead of polling the
// network
let mut current_balance = previous_balance;
for _ in 0..20 {
sleep(Duration::from_millis(500));
current_balance = client.poll_get_balance(&config.id.pubkey())?;
if previous_balance != current_balance {
break;
}
println!(".");
}
println!("Your balance is: {:?}", current_balance);
if current_balance - previous_balance != tokens {
Err("Airdrop failed!")?;
}
}
// If client has positive balance, spend tokens in {balance} number of transactions
WalletCommand::Pay(tokens, to) => {
let last_id = client.get_last_id();
let signature = client.transfer(tokens, &config.id, to, &last_id)?;
println!("{}", signature);
}
// Confirm the last client transaction by signature
WalletCommand::Confirm(signature) => {
if client.check_signature(&signature) {
println!("Confirmed");
} else {
println!("Not found");
}
}
}
Ok(())
}
fn read_leader(path: &str) -> Result<Config, WalletError> {
let file = File::open(path.to_string()).or_else(|err| {
Err(WalletError::BadParameter(format!(
"{}: Unable to open leader file: {}",
err, path
)))
})?;
serde_json::from_reader(file).or_else(|err| {
Err(WalletError::BadParameter(format!(
"{}: Failed to parse leader file: {}",
err, path
)))
})
}
fn main() -> Result<(), Box<error::Error>> {
logger::setup();
let config = parse_args()?;
let mut client = mk_client(&config.leader);
process_command(&config, &mut client)
}

62
src/blob_fetch_stage.rs Normal file
View File

@ -0,0 +1,62 @@
//! The `blob_fetch_stage` pulls blobs from UDP sockets and sends it to a channel.
use packet::BlobRecycler;
use service::Service;
use std::net::UdpSocket;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::channel;
use std::sync::Arc;
use std::thread::{self, JoinHandle};
use streamer::{self, BlobReceiver};
pub struct BlobFetchStage {
exit: Arc<AtomicBool>,
thread_hdls: Vec<JoinHandle<()>>,
}
impl BlobFetchStage {
pub fn new(
socket: UdpSocket,
exit: Arc<AtomicBool>,
blob_recycler: &BlobRecycler,
) -> (Self, BlobReceiver) {
Self::new_multi_socket(vec![socket], exit, blob_recycler)
}
pub fn new_multi_socket(
sockets: Vec<UdpSocket>,
exit: Arc<AtomicBool>,
blob_recycler: &BlobRecycler,
) -> (Self, BlobReceiver) {
let (blob_sender, blob_receiver) = channel();
let thread_hdls: Vec<_> = sockets
.into_iter()
.map(|socket| {
streamer::blob_receiver(
exit.clone(),
blob_recycler.clone(),
socket,
blob_sender.clone(),
).expect("blob receiver init")
})
.collect();
(BlobFetchStage { exit, thread_hdls }, blob_receiver)
}
pub fn close(&self) {
self.exit.store(true, Ordering::Relaxed);
}
}
impl Service for BlobFetchStage {
fn thread_hdls(self) -> Vec<JoinHandle<()>> {
self.thread_hdls
}
fn join(self) -> thread::Result<()> {
for thread_hdl in self.thread_hdls() {
thread_hdl.join()?;
}
Ok(())
}
}

203
src/broadcast_stage.rs Normal file
View File

@ -0,0 +1,203 @@
//! The `broadcast_stage` broadcasts data from a leader node to validators
//!
use counter::Counter;
use crdt::{Crdt, CrdtError, NodeInfo};
#[cfg(feature = "erasure")]
use erasure;
use log::Level;
use packet::BlobRecycler;
use result::{Error, Result};
use service::Service;
use std::mem;
use std::net::UdpSocket;
use std::sync::atomic::AtomicUsize;
use std::sync::mpsc::RecvTimeoutError;
use std::sync::{Arc, RwLock};
use std::thread::{self, Builder, JoinHandle};
use std::time::Duration;
use streamer::BlobReceiver;
use window::{self, SharedWindow, WindowIndex, WINDOW_SIZE};
fn broadcast(
node_info: &NodeInfo,
broadcast_table: &[NodeInfo],
window: &SharedWindow,
recycler: &BlobRecycler,
receiver: &BlobReceiver,
sock: &UdpSocket,
transmit_index: &mut WindowIndex,
receive_index: &mut u64,
) -> Result<()> {
let debug_id = node_info.debug_id();
let timer = Duration::new(1, 0);
let mut dq = receiver.recv_timeout(timer)?;
while let Ok(mut nq) = receiver.try_recv() {
dq.append(&mut nq);
}
// flatten deque to vec
let blobs_vec: Vec<_> = dq.into_iter().collect();
// We could receive more blobs than window slots so
// break them up into window-sized chunks to process
let blobs_chunked = blobs_vec.chunks(WINDOW_SIZE as usize).map(|x| x.to_vec());
if log_enabled!(Level::Trace) {
trace!("{}", window::print_window(debug_id, window, *receive_index));
}
for mut blobs in blobs_chunked {
let blobs_len = blobs.len();
trace!("{:x}: broadcast blobs.len: {}", debug_id, blobs_len);
// Index the blobs
window::index_blobs(node_info, &blobs, receive_index)
.expect("index blobs for initial window");
// keep the cache of blobs that are broadcast
inc_new_counter_info!("streamer-broadcast-sent", blobs.len());
{
let mut win = window.write().unwrap();
assert!(blobs.len() <= win.len());
for b in &blobs {
let ix = b.read().unwrap().get_index().expect("blob index");
let pos = (ix % WINDOW_SIZE) as usize;
if let Some(x) = mem::replace(&mut win[pos].data, None) {
trace!(
"{:x} popped {} at {}",
debug_id,
x.read().unwrap().get_index().unwrap(),
pos
);
recycler.recycle(x);
}
if let Some(x) = mem::replace(&mut win[pos].coding, None) {
trace!(
"{:x} popped {} at {}",
debug_id,
x.read().unwrap().get_index().unwrap(),
pos
);
recycler.recycle(x);
}
trace!("{:x} null {}", debug_id, pos);
}
while let Some(b) = blobs.pop() {
let ix = b.read().unwrap().get_index().expect("blob index");
let pos = (ix % WINDOW_SIZE) as usize;
trace!("{:x} caching {} at {}", debug_id, ix, pos);
assert!(win[pos].data.is_none());
win[pos].data = Some(b);
}
}
// Fill in the coding blob data from the window data blobs
#[cfg(feature = "erasure")]
{
erasure::generate_coding(
debug_id,
&mut window.write().unwrap(),
recycler,
*receive_index,
blobs_len,
&mut transmit_index.coding,
)?;
}
*receive_index += blobs_len as u64;
// Send blobs out from the window
Crdt::broadcast(
&node_info,
&broadcast_table,
&window,
&sock,
transmit_index,
*receive_index,
)?;
}
Ok(())
}
pub struct BroadcastStage {
thread_hdl: JoinHandle<()>,
}
impl BroadcastStage {
fn run(
sock: &UdpSocket,
crdt: &Arc<RwLock<Crdt>>,
window: &SharedWindow,
entry_height: u64,
recycler: &BlobRecycler,
receiver: &BlobReceiver,
) {
let mut transmit_index = WindowIndex {
data: entry_height,
coding: entry_height,
};
let mut receive_index = entry_height;
let me = crdt.read().unwrap().my_data().clone();
loop {
let broadcast_table = crdt.read().unwrap().compute_broadcast_table();
if let Err(e) = broadcast(
&me,
&broadcast_table,
&window,
&recycler,
&receiver,
&sock,
&mut transmit_index,
&mut receive_index,
) {
match e {
Error::RecvTimeoutError(RecvTimeoutError::Disconnected) => break,
Error::RecvTimeoutError(RecvTimeoutError::Timeout) => (),
Error::CrdtError(CrdtError::NoPeers) => (), // TODO: Why are the unit-tests throwing hundreds of these?
_ => {
inc_new_counter_info!("streamer-broadcaster-error", 1, 1);
error!("broadcaster error: {:?}", e);
}
}
}
}
}
/// Service to broadcast messages from the leader to layer 1 nodes.
/// See `crdt` for network layer definitions.
/// # Arguments
/// * `sock` - Socket to send from.
/// * `exit` - Boolean to signal system exit.
/// * `crdt` - CRDT structure
/// * `window` - Cache of blobs that we have broadcast
/// * `recycler` - Blob recycler.
/// * `receiver` - Receive channel for blobs to be retransmitted to all the layer 1 nodes.
pub fn new(
sock: UdpSocket,
crdt: Arc<RwLock<Crdt>>,
window: SharedWindow,
entry_height: u64,
recycler: BlobRecycler,
receiver: BlobReceiver,
) -> Self {
let thread_hdl = Builder::new()
.name("solana-broadcaster".to_string())
.spawn(move || {
Self::run(&sock, &crdt, &window, entry_height, &recycler, &receiver);
})
.unwrap();
BroadcastStage { thread_hdl }
}
}
impl Service for BroadcastStage {
fn thread_hdls(self) -> Vec<JoinHandle<()>> {
vec![self.thread_hdl]
}
fn join(self) -> thread::Result<()> {
self.thread_hdl.join()
}
}

194
src/budget.rs Normal file
View File

@ -0,0 +1,194 @@
//! The `budget` module provides a domain-specific language for payment plans. Users create Budget objects that
//! are given to an interpreter. The interpreter listens for `Witness` transactions,
//! which it uses to reduce the payment plan. When the budget is reduced to a
//! `Payment`, the payment is executed.
use chrono::prelude::*;
use payment_plan::{Payment, PaymentPlan, Witness};
use signature::Pubkey;
use std::mem;
/// A data type representing a `Witness` that the payment plan is waiting on.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Condition {
/// Wait for a `Timestamp` `Witness` at or after the given `DateTime`.
Timestamp(DateTime<Utc>, Pubkey),
/// Wait for a `Signature` `Witness` from `Pubkey`.
Signature(Pubkey),
}
impl Condition {
/// Return true if the given Witness satisfies this Condition.
pub fn is_satisfied(&self, witness: &Witness, from: &Pubkey) -> bool {
match (self, witness) {
(Condition::Signature(pubkey), Witness::Signature) => pubkey == from,
(Condition::Timestamp(dt, pubkey), Witness::Timestamp(last_time)) => {
pubkey == from && dt <= last_time
}
_ => false,
}
}
}
/// A data type reprsenting a payment plan.
#[repr(C)]
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Budget {
/// Make a payment.
Pay(Payment),
/// Make a payment after some condition.
After(Condition, Payment),
/// Either make a payment after one condition or a different payment after another
/// condition, which ever condition is satisfied first.
Or((Condition, Payment), (Condition, Payment)),
}
impl Budget {
/// Create the simplest budget - one that pays `tokens` to Pubkey.
pub fn new_payment(tokens: i64, to: Pubkey) -> Self {
Budget::Pay(Payment { tokens, to })
}
/// Create a budget that pays `tokens` to `to` after being witnessed by `from`.
pub fn new_authorized_payment(from: Pubkey, tokens: i64, to: Pubkey) -> Self {
Budget::After(Condition::Signature(from), Payment { tokens, to })
}
/// Create a budget that pays `tokens` to `to` after the given DateTime.
pub fn new_future_payment(dt: DateTime<Utc>, from: Pubkey, tokens: i64, to: Pubkey) -> Self {
Budget::After(Condition::Timestamp(dt, from), Payment { tokens, to })
}
/// Create a budget that pays `tokens` to `to` after the given DateTime
/// unless cancelled by `from`.
pub fn new_cancelable_future_payment(
dt: DateTime<Utc>,
from: Pubkey,
tokens: i64,
to: Pubkey,
) -> Self {
Budget::Or(
(Condition::Timestamp(dt, from), Payment { tokens, to }),
(Condition::Signature(from), Payment { tokens, to: from }),
)
}
}
impl PaymentPlan for Budget {
/// Return Payment if the budget requires no additional Witnesses.
fn final_payment(&self) -> Option<Payment> {
match self {
Budget::Pay(payment) => Some(payment.clone()),
_ => None,
}
}
/// Return true if the budget spends exactly `spendable_tokens`.
fn verify(&self, spendable_tokens: i64) -> bool {
match self {
Budget::Pay(payment) | Budget::After(_, payment) => payment.tokens == spendable_tokens,
Budget::Or(a, b) => a.1.tokens == spendable_tokens && b.1.tokens == spendable_tokens,
}
}
/// Apply a witness to the budget to see if the budget can be reduced.
/// If so, modify the budget in-place.
fn apply_witness(&mut self, witness: &Witness, from: &Pubkey) {
let new_payment = match self {
Budget::After(cond, payment) if cond.is_satisfied(witness, from) => Some(payment),
Budget::Or((cond, payment), _) if cond.is_satisfied(witness, from) => Some(payment),
Budget::Or(_, (cond, payment)) if cond.is_satisfied(witness, from) => Some(payment),
_ => None,
}.cloned();
if let Some(payment) = new_payment {
mem::replace(self, Budget::Pay(payment));
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use signature::{Keypair, KeypairUtil};
#[test]
fn test_signature_satisfied() {
let from = Pubkey::default();
assert!(Condition::Signature(from).is_satisfied(&Witness::Signature, &from));
}
#[test]
fn test_timestamp_satisfied() {
let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
let dt2 = Utc.ymd(2014, 11, 14).and_hms(10, 9, 8);
let from = Pubkey::default();
assert!(Condition::Timestamp(dt1, from).is_satisfied(&Witness::Timestamp(dt1), &from));
assert!(Condition::Timestamp(dt1, from).is_satisfied(&Witness::Timestamp(dt2), &from));
assert!(!Condition::Timestamp(dt2, from).is_satisfied(&Witness::Timestamp(dt1), &from));
}
#[test]
fn test_verify() {
let dt = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
let from = Pubkey::default();
let to = Pubkey::default();
assert!(Budget::new_payment(42, to).verify(42));
assert!(Budget::new_authorized_payment(from, 42, to).verify(42));
assert!(Budget::new_future_payment(dt, from, 42, to).verify(42));
assert!(Budget::new_cancelable_future_payment(dt, from, 42, to).verify(42));
}
#[test]
fn test_authorized_payment() {
let from = Pubkey::default();
let to = Pubkey::default();
let mut budget = Budget::new_authorized_payment(from, 42, to);
budget.apply_witness(&Witness::Signature, &from);
assert_eq!(budget, Budget::new_payment(42, to));
}
#[test]
fn test_future_payment() {
let dt = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
let from = Keypair::new().pubkey();
let to = Keypair::new().pubkey();
let mut budget = Budget::new_future_payment(dt, from, 42, to);
budget.apply_witness(&Witness::Timestamp(dt), &from);
assert_eq!(budget, Budget::new_payment(42, to));
}
#[test]
fn test_unauthorized_future_payment() {
// Ensure timestamp will only be acknowledged if it came from the
// whitelisted public key.
let dt = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
let from = Keypair::new().pubkey();
let to = Keypair::new().pubkey();
let mut budget = Budget::new_future_payment(dt, from, 42, to);
let orig_budget = budget.clone();
budget.apply_witness(&Witness::Timestamp(dt), &to); // <-- Attack!
assert_eq!(budget, orig_budget);
}
#[test]
fn test_cancelable_future_payment() {
let dt = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10);
let from = Pubkey::default();
let to = Pubkey::default();
let mut budget = Budget::new_cancelable_future_payment(dt, from, 42, to);
budget.apply_witness(&Witness::Timestamp(dt), &from);
assert_eq!(budget, Budget::new_payment(42, to));
let mut budget = Budget::new_cancelable_future_payment(dt, from, 42, to);
budget.apply_witness(&Witness::Signature, &from);
assert_eq!(budget, Budget::new_payment(42, from));
}
}

View File

@ -0,0 +1,333 @@
use crdt::{CrdtError, NodeInfo};
use rand::distributions::{Distribution, Weighted, WeightedChoice};
use rand::thread_rng;
use result::Result;
use signature::Pubkey;
use std;
use std::collections::HashMap;
pub const DEFAULT_WEIGHT: u32 = 1;
pub trait ChooseGossipPeerStrategy {
fn choose_peer<'a>(&self, options: Vec<&'a NodeInfo>) -> Result<&'a NodeInfo>;
}
pub struct ChooseRandomPeerStrategy<'a> {
random: &'a Fn() -> u64,
}
// Given a source of randomness "random", this strategy will randomly pick a validator
// from the input options. This strategy works in isolation, but doesn't leverage any
// rumors from the rest of the gossip network to make more informed decisions about
// which validators have more/less updates
impl<'a, 'b> ChooseRandomPeerStrategy<'a> {
pub fn new(random: &'a Fn() -> u64) -> Self {
ChooseRandomPeerStrategy { random }
}
}
impl<'a> ChooseGossipPeerStrategy for ChooseRandomPeerStrategy<'a> {
fn choose_peer<'b>(&self, options: Vec<&'b NodeInfo>) -> Result<&'b NodeInfo> {
if options.is_empty() {
Err(CrdtError::NoPeers)?;
}
let n = ((self.random)() as usize) % options.len();
Ok(options[n])
}
}
// This strategy uses rumors accumulated from the rest of the network to weight
// the importance of communicating with a particular validator based on cumulative network
// perceiption of the number of updates the validator has to offer. A validator is randomly
// picked based on a weighted sample from the pool of viable choices. The "weight", w, of a
// particular validator "v" is calculated as follows:
//
// w = [Sum for all i in I_v: (rumor_v(i) - observed(v)) * stake(i)] /
// [Sum for all i in I_v: Sum(stake(i))]
//
// where I_v is the set of all validators that returned a rumor about the update_index of
// validator "v", stake(i) is the size of the stake of validator "i", observed(v) is the
// observed update_index from the last direct communication validator "v", and
// rumor_v(i) is the rumored update_index of validator "v" propagated by fellow validator "i".
// This could be a problem if there are validators with large stakes lying about their
// observed updates. There could also be a problem in network partitions, or even just
// when certain validators are disproportionately active, where we hear more rumors about
// certain clusters of nodes that then propagate more rumros about each other. Hopefully
// this can be resolved with a good baseline DEFAULT_WEIGHT, or by implementing lockout
// periods for very active validators in the future.
pub struct ChooseWeightedPeerStrategy<'a> {
// The map of last directly observed update_index for each active validator.
// This is how we get observed(v) from the formula above.
remote: &'a HashMap<Pubkey, u64>,
// The map of rumored update_index for each active validator. Using the formula above,
// to find rumor_v(i), we would first look up "v" in the outer map, then look up
// "i" in the inner map, i.e. look up external_liveness[v][i]
external_liveness: &'a HashMap<Pubkey, HashMap<Pubkey, u64>>,
// A function returning the size of the stake for a particular validator, corresponds
// to stake(i) in the formula above.
get_stake: &'a Fn(Pubkey) -> f64,
}
impl<'a> ChooseWeightedPeerStrategy<'a> {
pub fn new(
remote: &'a HashMap<Pubkey, u64>,
external_liveness: &'a HashMap<Pubkey, HashMap<Pubkey, u64>>,
get_stake: &'a Fn(Pubkey) -> f64,
) -> Self {
ChooseWeightedPeerStrategy {
remote,
external_liveness,
get_stake,
}
}
fn calculate_weighted_remote_index(&self, peer_id: Pubkey) -> u32 {
let mut last_seen_index = 0;
// If the peer is not in our remote table, then we leave last_seen_index as zero.
// Only happens when a peer appears in our crdt.table but not in our crdt.remote,
// which means a validator was directly injected into our crdt.table
if let Some(index) = self.remote.get(&peer_id) {
last_seen_index = *index;
}
let liveness_entry = self.external_liveness.get(&peer_id);
if liveness_entry.is_none() {
return DEFAULT_WEIGHT;
}
let votes = liveness_entry.unwrap();
if votes.is_empty() {
return DEFAULT_WEIGHT;
}
// Calculate the weighted average of the rumors
let mut relevant_votes = vec![];
let total_stake = votes.iter().fold(0.0, |total_stake, (&id, &vote)| {
let stake = (self.get_stake)(id);
// If the total stake is going to overflow u64, pick
// the larger of either the current total_stake, or the
// new stake, this way we are guaranteed to get at least u64/2
// sample of stake in our weighted calculation
if std::f64::MAX - total_stake < stake {
if stake > total_stake {
relevant_votes = vec![(stake, vote)];
stake
} else {
total_stake
}
} else {
relevant_votes.push((stake, vote));
total_stake + stake
}
});
let weighted_vote = relevant_votes.iter().fold(0.0, |sum, &(stake, vote)| {
if vote < last_seen_index {
// This should never happen because we maintain the invariant that the indexes
// in the external_liveness table are always greater than the corresponding
// indexes in the remote table, if the index exists in the remote table at all.
// Case 1: Attempt to insert bigger index into the "external_liveness" table
// happens after an insertion into the "remote" table. In this case,
// (see apply_updates()) function, we prevent the insertion if the entry
// in the remote table >= the atempted insertion into the "external" liveness
// table.
// Case 2: Bigger index in the "external_liveness" table inserted before
// a smaller insertion into the "remote" table. We clear the corresponding
// "external_liveness" table entry on all insertions into the "remote" table
// See apply_updates() function.
warn!("weighted peer index was smaller than local entry in remote table");
return sum;
}
let vote_difference = (vote - last_seen_index) as f64;
let new_weight = vote_difference * (stake / total_stake);
if std::f64::MAX - sum < new_weight {
return f64::max(new_weight, sum);
}
sum + new_weight
});
// Return u32 b/c the weighted sampling API from rand::distributions
// only takes u32 for weights
if weighted_vote >= f64::from(std::u32::MAX) {
return std::u32::MAX;
}
// If the weighted rumors we've heard about aren't any greater than
// what we've directly learned from the last time we communicated with the
// peer (i.e. weighted_vote == 0), then return a weight of 1.
// Otherwise, return the calculated weight.
weighted_vote as u32 + DEFAULT_WEIGHT
}
}
impl<'a> ChooseGossipPeerStrategy for ChooseWeightedPeerStrategy<'a> {
fn choose_peer<'b>(&self, options: Vec<&'b NodeInfo>) -> Result<&'b NodeInfo> {
if options.is_empty() {
Err(CrdtError::NoPeers)?;
}
let mut weighted_peers = vec![];
for peer in options {
let weight = self.calculate_weighted_remote_index(peer.id);
weighted_peers.push(Weighted { weight, item: peer });
}
let mut rng = thread_rng();
Ok(WeightedChoice::new(&mut weighted_peers).sample(&mut rng))
}
}
#[cfg(test)]
mod tests {
use choose_gossip_peer_strategy::{ChooseWeightedPeerStrategy, DEFAULT_WEIGHT};
use logger;
use signature::{Keypair, KeypairUtil, Pubkey};
use std;
use std::collections::HashMap;
fn get_stake(_id: Pubkey) -> f64 {
1.0
}
#[test]
fn test_default() {
logger::setup();
// Initialize the filler keys
let key1 = Keypair::new().pubkey();
let remote: HashMap<Pubkey, u64> = HashMap::new();
let external_liveness: HashMap<Pubkey, HashMap<Pubkey, u64>> = HashMap::new();
let weighted_strategy =
ChooseWeightedPeerStrategy::new(&remote, &external_liveness, &get_stake);
// If external_liveness table doesn't contain this entry,
// return the default weight
let result = weighted_strategy.calculate_weighted_remote_index(key1);
assert_eq!(result, DEFAULT_WEIGHT);
}
#[test]
fn test_only_external_liveness() {
logger::setup();
// Initialize the filler keys
let key1 = Keypair::new().pubkey();
let key2 = Keypair::new().pubkey();
let remote: HashMap<Pubkey, u64> = HashMap::new();
let mut external_liveness: HashMap<Pubkey, HashMap<Pubkey, u64>> = HashMap::new();
// If only the liveness table contains the entry, should return the
// weighted liveness entries
let test_value: u32 = 5;
let mut rumors: HashMap<Pubkey, u64> = HashMap::new();
rumors.insert(key2, test_value as u64);
external_liveness.insert(key1, rumors);
let weighted_strategy =
ChooseWeightedPeerStrategy::new(&remote, &external_liveness, &get_stake);
let result = weighted_strategy.calculate_weighted_remote_index(key1);
assert_eq!(result, test_value + DEFAULT_WEIGHT);
}
#[test]
fn test_overflow_votes() {
logger::setup();
// Initialize the filler keys
let key1 = Keypair::new().pubkey();
let key2 = Keypair::new().pubkey();
let remote: HashMap<Pubkey, u64> = HashMap::new();
let mut external_liveness: HashMap<Pubkey, HashMap<Pubkey, u64>> = HashMap::new();
// If the vote index is greater than u32::MAX, default to u32::MAX
let test_value = (std::u32::MAX as u64) + 10;
let mut rumors: HashMap<Pubkey, u64> = HashMap::new();
rumors.insert(key2, test_value);
external_liveness.insert(key1, rumors);
let weighted_strategy =
ChooseWeightedPeerStrategy::new(&remote, &external_liveness, &get_stake);
let result = weighted_strategy.calculate_weighted_remote_index(key1);
assert_eq!(result, std::u32::MAX);
}
#[test]
fn test_many_validators() {
logger::setup();
// Initialize the filler keys
let key1 = Keypair::new().pubkey();
let mut remote: HashMap<Pubkey, u64> = HashMap::new();
let mut external_liveness: HashMap<Pubkey, HashMap<Pubkey, u64>> = HashMap::new();
// Test many validators' rumors in external_liveness
let num_peers = 10;
let mut rumors: HashMap<Pubkey, u64> = HashMap::new();
remote.insert(key1, 0);
for i in 0..num_peers {
let pubkey = Keypair::new().pubkey();
rumors.insert(pubkey, i);
}
external_liveness.insert(key1, rumors);
let weighted_strategy =
ChooseWeightedPeerStrategy::new(&remote, &external_liveness, &get_stake);
let result = weighted_strategy.calculate_weighted_remote_index(key1);
assert_eq!(result, (num_peers / 2) as u32);
}
#[test]
fn test_many_validators2() {
logger::setup();
// Initialize the filler keys
let key1 = Keypair::new().pubkey();
let mut remote: HashMap<Pubkey, u64> = HashMap::new();
let mut external_liveness: HashMap<Pubkey, HashMap<Pubkey, u64>> = HashMap::new();
// Test many validators' rumors in external_liveness
let num_peers = 10;
let old_index = 20;
let mut rumors: HashMap<Pubkey, u64> = HashMap::new();
remote.insert(key1, old_index);
for _i in 0..num_peers {
let pubkey = Keypair::new().pubkey();
rumors.insert(pubkey, old_index);
}
external_liveness.insert(key1, rumors);
let weighted_strategy =
ChooseWeightedPeerStrategy::new(&remote, &external_liveness, &get_stake);
let result = weighted_strategy.calculate_weighted_remote_index(key1);
// If nobody has seen a newer update then revert to default
assert_eq!(result, DEFAULT_WEIGHT);
}
}

20
src/client.rs Normal file
View File

@ -0,0 +1,20 @@
use crdt::NodeInfo;
use nat::udp_random_bind;
use std::time::Duration;
use thin_client::ThinClient;
pub fn mk_client(r: &NodeInfo) -> ThinClient {
let requests_socket = udp_random_bind(8000, 10000, 5).unwrap();
let transactions_socket = udp_random_bind(8000, 10000, 5).unwrap();
requests_socket
.set_read_timeout(Some(Duration::new(1, 0)))
.unwrap();
ThinClient::new(
r.contact_info.rpu,
requests_socket,
r.contact_info.tpu,
transactions_socket,
)
}

189
src/counter.rs Normal file
View File

@ -0,0 +1,189 @@
use influx_db_client as influxdb;
use metrics;
use std::env;
use std::sync::atomic::{AtomicUsize, Ordering};
use timing;
const DEFAULT_METRICS_RATE: usize = 100;
pub struct Counter {
pub name: &'static str,
/// total accumulated value
pub counts: AtomicUsize,
pub times: AtomicUsize,
/// last accumulated value logged
pub lastlog: AtomicUsize,
pub lograte: AtomicUsize,
}
macro_rules! create_counter {
($name:expr, $lograte:expr) => {
Counter {
name: $name,
counts: AtomicUsize::new(0),
times: AtomicUsize::new(0),
lastlog: AtomicUsize::new(0),
lograte: AtomicUsize::new($lograte),
}
};
}
macro_rules! inc_counter {
($name:expr, $count:expr) => {
unsafe { $name.inc($count) };
};
}
macro_rules! inc_new_counter_info {
($name:expr, $count:expr) => {{
inc_new_counter!($name, $count, Level::Info, 0);
}};
($name:expr, $count:expr, $lograte:expr) => {{
inc_new_counter!($name, $count, Level::Info, $lograte);
}};
}
macro_rules! inc_new_counter {
($name:expr, $count:expr, $level:expr, $lograte:expr) => {{
if log_enabled!($level) {
static mut INC_NEW_COUNTER: Counter = create_counter!($name, $lograte);
inc_counter!(INC_NEW_COUNTER, $count);
}
}};
}
impl Counter {
fn default_log_rate() -> usize {
let v = env::var("SOLANA_DEFAULT_METRICS_RATE")
.map(|x| x.parse().unwrap_or(DEFAULT_METRICS_RATE))
.unwrap_or(DEFAULT_METRICS_RATE);
if v == 0 {
DEFAULT_METRICS_RATE
} else {
v
}
}
pub fn inc(&mut self, events: usize) {
let counts = self.counts.fetch_add(events, Ordering::Relaxed);
let times = self.times.fetch_add(1, Ordering::Relaxed);
let mut lograte = self.lograte.load(Ordering::Relaxed);
if lograte == 0 {
lograte = Counter::default_log_rate();
self.lograte.store(lograte, Ordering::Relaxed);
}
if times % lograte == 0 && times > 0 {
let lastlog = self.lastlog.load(Ordering::Relaxed);
info!(
"COUNTER:{{\"name\": \"{}\", \"counts\": {}, \"samples\": {}, \"now\": {}}}",
self.name,
counts,
times,
timing::timestamp(),
);
metrics::submit(
influxdb::Point::new(&format!("counter-{}", self.name))
.add_field(
"count",
influxdb::Value::Integer(counts as i64 - lastlog as i64),
)
.to_owned(),
);
self.lastlog
.compare_and_swap(lastlog, counts, Ordering::Relaxed);
}
}
}
#[cfg(test)]
mod tests {
use counter::{Counter, DEFAULT_METRICS_RATE};
use log::Level;
use std::env;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Once, RwLock, ONCE_INIT};
fn get_env_lock() -> &'static RwLock<()> {
static mut ENV_LOCK: Option<RwLock<()>> = None;
static INIT_HOOK: Once = ONCE_INIT;
unsafe {
INIT_HOOK.call_once(|| {
ENV_LOCK = Some(RwLock::new(()));
});
&ENV_LOCK.as_ref().unwrap()
}
}
#[test]
fn test_counter() {
let _readlock = get_env_lock().read();
static mut COUNTER: Counter = create_counter!("test", 100);
let count = 1;
inc_counter!(COUNTER, count);
unsafe {
assert_eq!(COUNTER.counts.load(Ordering::Relaxed), 1);
assert_eq!(COUNTER.times.load(Ordering::Relaxed), 1);
assert_eq!(COUNTER.lograte.load(Ordering::Relaxed), 100);
assert_eq!(COUNTER.lastlog.load(Ordering::Relaxed), 0);
assert_eq!(COUNTER.name, "test");
}
for _ in 0..199 {
inc_counter!(COUNTER, 2);
}
unsafe {
assert_eq!(COUNTER.lastlog.load(Ordering::Relaxed), 199);
}
inc_counter!(COUNTER, 2);
unsafe {
assert_eq!(COUNTER.lastlog.load(Ordering::Relaxed), 399);
}
}
#[test]
fn test_inc_new_counter() {
let _readlock = get_env_lock().read();
//make sure that macros are syntactically correct
//the variable is internal to the macro scope so there is no way to introspect it
inc_new_counter_info!("counter-1", 1);
inc_new_counter_info!("counter-2", 1, 2);
}
#[test]
fn test_lograte() {
let _readlock = get_env_lock().read();
assert_eq!(
Counter::default_log_rate(),
DEFAULT_METRICS_RATE,
"default_log_rate() is {}, expected {}, SOLANA_DEFAULT_METRICS_RATE environment variable set?",
Counter::default_log_rate(),
DEFAULT_METRICS_RATE,
);
static mut COUNTER: Counter = create_counter!("test_lograte", 0);
inc_counter!(COUNTER, 2);
unsafe {
assert_eq!(
COUNTER.lograte.load(Ordering::Relaxed),
DEFAULT_METRICS_RATE
);
}
}
#[test]
fn test_lograte_env() {
assert_ne!(DEFAULT_METRICS_RATE, 0);
let _writelock = get_env_lock().write();
static mut COUNTER: Counter = create_counter!("test_lograte_env", 0);
env::set_var("SOLANA_DEFAULT_METRICS_RATE", "50");
inc_counter!(COUNTER, 2);
unsafe {
assert_eq!(COUNTER.lograte.load(Ordering::Relaxed), 50);
}
static mut COUNTER2: Counter = create_counter!("test_lograte_env", 0);
env::set_var("SOLANA_DEFAULT_METRICS_RATE", "0");
inc_counter!(COUNTER2, 2);
unsafe {
assert_eq!(
COUNTER2.lograte.load(Ordering::Relaxed),
DEFAULT_METRICS_RATE
);
}
}
}

2072
src/crdt.rs Normal file

File diff suppressed because it is too large Load Diff

349
src/drone.rs Normal file
View File

@ -0,0 +1,349 @@
//! The `drone` module provides an object for launching a Solana Drone,
//! which is the custodian of any remaining tokens in a mint.
//! The Solana Drone builds and send airdrop transactions,
//! checking requests against a request cap for a given time time_slice
//! and (to come) an IP rate limit.
use influx_db_client as influxdb;
use metrics;
use signature::Signature;
use signature::{Keypair, Pubkey};
use std::io;
use std::io::{Error, ErrorKind};
use std::net::{IpAddr, SocketAddr, UdpSocket};
use std::time::Duration;
use thin_client::ThinClient;
use transaction::Transaction;
pub const TIME_SLICE: u64 = 60;
pub const REQUEST_CAP: u64 = 1_000_000;
pub const DRONE_PORT: u16 = 9900;
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub enum DroneRequest {
GetAirdrop {
airdrop_request_amount: u64,
client_pubkey: Pubkey,
},
}
pub struct Drone {
mint_keypair: Keypair,
ip_cache: Vec<IpAddr>,
_airdrop_addr: SocketAddr,
transactions_addr: SocketAddr,
requests_addr: SocketAddr,
pub time_slice: Duration,
request_cap: u64,
pub request_current: u64,
}
impl Drone {
pub fn new(
mint_keypair: Keypair,
_airdrop_addr: SocketAddr,
transactions_addr: SocketAddr,
requests_addr: SocketAddr,
time_input: Option<u64>,
request_cap_input: Option<u64>,
) -> Drone {
let time_slice = match time_input {
Some(time) => Duration::new(time, 0),
None => Duration::new(TIME_SLICE, 0),
};
let request_cap = match request_cap_input {
Some(cap) => cap,
None => REQUEST_CAP,
};
Drone {
mint_keypair,
ip_cache: Vec::new(),
_airdrop_addr,
transactions_addr,
requests_addr,
time_slice,
request_cap,
request_current: 0,
}
}
pub fn check_request_limit(&mut self, request_amount: u64) -> bool {
(self.request_current + request_amount) <= self.request_cap
}
pub fn clear_request_count(&mut self) {
self.request_current = 0;
}
pub fn add_ip_to_cache(&mut self, ip: IpAddr) {
self.ip_cache.push(ip);
}
pub fn clear_ip_cache(&mut self) {
self.ip_cache.clear();
}
pub fn check_rate_limit(&mut self, ip: IpAddr) -> Result<IpAddr, IpAddr> {
// [WIP] This is placeholder code for a proper rate limiter.
// Right now it will only allow one total drone request per IP
if self.ip_cache.contains(&ip) {
// Add proper error handling here
Err(ip)
} else {
self.add_ip_to_cache(ip);
Ok(ip)
}
}
pub fn send_airdrop(&mut self, req: DroneRequest) -> Result<Signature, io::Error> {
let request_amount: u64;
let requests_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
let transactions_socket = UdpSocket::bind("0.0.0.0:0").unwrap();
let mut client = ThinClient::new(
self.requests_addr,
requests_socket,
self.transactions_addr,
transactions_socket,
);
let last_id = client.get_last_id();
let tx = match req {
DroneRequest::GetAirdrop {
airdrop_request_amount,
client_pubkey,
} => {
info!(
"Requesting airdrop of {} to {:?}",
airdrop_request_amount, client_pubkey
);
request_amount = airdrop_request_amount;
Transaction::new(
&self.mint_keypair,
client_pubkey,
airdrop_request_amount as i64,
last_id,
)
}
};
if self.check_request_limit(request_amount) {
self.request_current += request_amount;
metrics::submit(
influxdb::Point::new("drone")
.add_tag("op", influxdb::Value::String("airdrop".to_string()))
.add_field(
"request_amount",
influxdb::Value::Integer(request_amount as i64),
)
.add_field(
"request_current",
influxdb::Value::Integer(self.request_current as i64),
)
.to_owned(),
);
client.transfer_signed(&tx)
} else {
Err(Error::new(ErrorKind::Other, "token limit reached"))
}
}
}
impl Drop for Drone {
fn drop(&mut self) {
metrics::flush();
}
}
#[cfg(test)]
mod tests {
use bank::Bank;
use crdt::{get_ip_addr, TestNode};
use drone::{Drone, DroneRequest, REQUEST_CAP, TIME_SLICE};
use fullnode::Fullnode;
use logger;
use mint::Mint;
use service::Service;
use signature::{Keypair, KeypairUtil};
use std::fs::remove_dir_all;
use std::net::{SocketAddr, UdpSocket};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::thread::sleep;
use std::time::Duration;
use thin_client::ThinClient;
#[test]
fn test_check_request_limit() {
let keypair = Keypair::new();
let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap();
addr.set_ip(get_ip_addr().unwrap());
let transactions_addr = "0.0.0.0:0".parse().unwrap();
let requests_addr = "0.0.0.0:0".parse().unwrap();
let mut drone = Drone::new(
keypair,
addr,
transactions_addr,
requests_addr,
None,
Some(3),
);
assert!(drone.check_request_limit(1));
drone.request_current = 3;
assert!(!drone.check_request_limit(1));
}
#[test]
fn test_clear_request_count() {
let keypair = Keypair::new();
let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap();
addr.set_ip(get_ip_addr().unwrap());
let transactions_addr = "0.0.0.0:0".parse().unwrap();
let requests_addr = "0.0.0.0:0".parse().unwrap();
let mut drone = Drone::new(keypair, addr, transactions_addr, requests_addr, None, None);
drone.request_current = drone.request_current + 256;
assert_eq!(drone.request_current, 256);
drone.clear_request_count();
assert_eq!(drone.request_current, 0);
}
#[test]
fn test_add_ip_to_cache() {
let keypair = Keypair::new();
let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap();
addr.set_ip(get_ip_addr().unwrap());
let transactions_addr = "0.0.0.0:0".parse().unwrap();
let requests_addr = "0.0.0.0:0".parse().unwrap();
let mut drone = Drone::new(keypair, addr, transactions_addr, requests_addr, None, None);
let ip = "127.0.0.1".parse().expect("create IpAddr from string");
assert_eq!(drone.ip_cache.len(), 0);
drone.add_ip_to_cache(ip);
assert_eq!(drone.ip_cache.len(), 1);
assert!(drone.ip_cache.contains(&ip));
}
#[test]
fn test_clear_ip_cache() {
let keypair = Keypair::new();
let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap();
addr.set_ip(get_ip_addr().unwrap());
let transactions_addr = "0.0.0.0:0".parse().unwrap();
let requests_addr = "0.0.0.0:0".parse().unwrap();
let mut drone = Drone::new(keypair, addr, transactions_addr, requests_addr, None, None);
let ip = "127.0.0.1".parse().expect("create IpAddr from string");
assert_eq!(drone.ip_cache.len(), 0);
drone.add_ip_to_cache(ip);
assert_eq!(drone.ip_cache.len(), 1);
drone.clear_ip_cache();
assert_eq!(drone.ip_cache.len(), 0);
assert!(drone.ip_cache.is_empty());
}
#[test]
fn test_drone_default_init() {
let keypair = Keypair::new();
let mut addr: SocketAddr = "0.0.0.0:9900".parse().unwrap();
addr.set_ip(get_ip_addr().unwrap());
let transactions_addr = "0.0.0.0:0".parse().unwrap();
let requests_addr = "0.0.0.0:0".parse().unwrap();
let time_slice: Option<u64> = None;
let request_cap: Option<u64> = None;
let drone = Drone::new(
keypair,
addr,
transactions_addr,
requests_addr,
time_slice,
request_cap,
);
assert_eq!(drone.time_slice, Duration::new(TIME_SLICE, 0));
assert_eq!(drone.request_cap, REQUEST_CAP);
}
fn tmp_ledger_path(name: &str) -> String {
use std::env;
let out_dir = env::var("OUT_DIR").unwrap_or_else(|_| "target".to_string());
let keypair = Keypair::new();
format!("{}/tmp-ledger-{}-{}", out_dir, name, keypair.pubkey())
}
#[test]
#[ignore]
fn test_send_airdrop() {
const SMALL_BATCH: i64 = 50;
const TPS_BATCH: i64 = 5_000_000;
logger::setup();
let leader_keypair = Keypair::new();
let leader = TestNode::new_localhost_with_pubkey(leader_keypair.pubkey());
let alice = Mint::new(10_000_000);
let bank = Bank::new(&alice);
let bob_pubkey = Keypair::new().pubkey();
let carlos_pubkey = Keypair::new().pubkey();
let exit = Arc::new(AtomicBool::new(false));
let leader_data = leader.data.clone();
let ledger_path = tmp_ledger_path("send_airdrop");
let server = Fullnode::new_leader(
leader_keypair,
bank,
0,
&[],
leader,
exit.clone(),
&ledger_path,
false,
);
//TODO: this seems unstable
sleep(Duration::from_millis(900));
let mut addr: SocketAddr = "0.0.0.0:9900".parse().expect("bind to drone socket");
addr.set_ip(get_ip_addr().expect("drone get_ip_addr"));
let mut drone = Drone::new(
alice.keypair(),
addr,
leader_data.contact_info.tpu,
leader_data.contact_info.rpu,
None,
Some(150_000),
);
let requests_socket = UdpSocket::bind("0.0.0.0:0").expect("drone bind to requests socket");
let transactions_socket =
UdpSocket::bind("0.0.0.0:0").expect("drone bind to transactions socket");
let mut client = ThinClient::new(
leader_data.contact_info.rpu,
requests_socket,
leader_data.contact_info.tpu,
transactions_socket,
);
let bob_req = DroneRequest::GetAirdrop {
airdrop_request_amount: 50,
client_pubkey: bob_pubkey,
};
let bob_sig = drone.send_airdrop(bob_req).unwrap();
assert!(client.poll_for_signature(&bob_sig).is_ok());
let carlos_req = DroneRequest::GetAirdrop {
airdrop_request_amount: 5_000_000,
client_pubkey: carlos_pubkey,
};
let carlos_sig = drone.send_airdrop(carlos_req).unwrap();
assert!(client.poll_for_signature(&carlos_sig).is_ok());
let bob_balance = client.get_balance(&bob_pubkey);
info!("Small request balance: {:?}", bob_balance);
assert_eq!(bob_balance.unwrap(), SMALL_BATCH);
let carlos_balance = client.get_balance(&carlos_pubkey);
info!("TPS request balance: {:?}", carlos_balance);
assert_eq!(carlos_balance.unwrap(), TPS_BATCH);
exit.store(true, Ordering::Relaxed);
server.join().unwrap();
remove_dir_all(ledger_path).unwrap();
}
}

295
src/entry.rs Normal file
View File

@ -0,0 +1,295 @@
//! The `entry` module is a fundamental building block of Proof of History. It contains a
//! unique ID that is the hash of the Entry before it, plus the hash of the
//! transactions within it. Entries cannot be reordered, and its field `num_hashes`
//! represents an approximate amount of time since the last Entry was created.
use bincode::{serialize_into, serialized_size};
use hash::{extend_and_hash, hash, Hash};
use packet::{BlobRecycler, SharedBlob, BLOB_DATA_SIZE};
use rayon::prelude::*;
use signature::Pubkey;
use std::io::Cursor;
use std::net::SocketAddr;
use transaction::Transaction;
/// Each Entry contains three pieces of data. The `num_hashes` field is the number
/// of hashes performed since the previous entry. The `id` field is the result
/// of hashing `id` from the previous entry `num_hashes` times. The `transactions`
/// field points to Transactions that took place shortly before `id` was generated.
///
/// If you divide `num_hashes` by the amount of time it takes to generate a new hash, you
/// get a duration estimate since the last Entry. Since processing power increases
/// over time, one should expect the duration `num_hashes` represents to decrease proportionally.
/// An upper bound on Duration can be estimated by assuming each hash was generated by the
/// world's fastest processor at the time the entry was recorded. Or said another way, it
/// is physically not possible for a shorter duration to have occurred if one assumes the
/// hash was computed by the world's fastest processor at that time. The hash chain is both
/// a Verifiable Delay Function (VDF) and a Proof of Work (not to be confused with Proof of
/// Work consensus!)
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Entry {
/// The number of hashes since the previous Entry ID.
pub num_hashes: u64,
/// The SHA-256 hash `num_hashes` after the previous Entry ID.
pub id: Hash,
/// An unordered list of transactions that were observed before the Entry ID was
/// generated. They may have been observed before a previous Entry ID but were
/// pushed back into this list to ensure deterministic interpretation of the ledger.
pub transactions: Vec<Transaction>,
/// Indication that:
/// 1. the next Entry in the ledger has transactions that can potentially
/// be verified in parallel with these transactions
/// 2. this Entry can be left out of the bank's entry_id cache for
/// purposes of duplicate rejection
pub has_more: bool,
/// Erasure requires that Entry be a multiple of 4 bytes in size
pad: [u8; 3],
}
impl Entry {
/// Creates the next Entry `num_hashes` after `start_hash`.
pub fn new(
start_hash: &Hash,
num_hashes: u64,
transactions: Vec<Transaction>,
has_more: bool,
) -> Self {
let num_hashes = num_hashes + if transactions.is_empty() { 0 } else { 1 };
let id = next_hash(start_hash, 0, &transactions);
let entry = Entry {
num_hashes,
id,
transactions,
has_more,
pad: [0, 0, 0],
};
let size = serialized_size(&entry).unwrap();
if size > BLOB_DATA_SIZE as u64 {
panic!(
"Serialized entry size too large: {} ({} transactions):",
size,
entry.transactions.len()
);
}
entry
}
pub fn to_blob(
&self,
blob_recycler: &BlobRecycler,
idx: Option<u64>,
id: Option<Pubkey>,
addr: Option<&SocketAddr>,
) -> SharedBlob {
let blob = blob_recycler.allocate();
{
let mut blob_w = blob.write().unwrap();
let pos = {
let mut out = Cursor::new(blob_w.data_mut());
serialize_into(&mut out, &self).expect("failed to serialize output");
out.position() as usize
};
blob_w.set_size(pos);
if let Some(idx) = idx {
blob_w.set_index(idx).expect("set_index()");
}
if let Some(id) = id {
blob_w.set_id(id).expect("set_id()");
}
if let Some(addr) = addr {
blob_w.meta.set_addr(addr);
}
blob_w.set_flags(0).unwrap();
}
blob
}
pub fn will_fit(transactions: Vec<Transaction>) -> bool {
serialized_size(&Entry {
num_hashes: 0,
id: Hash::default(),
transactions,
has_more: false,
pad: [0, 0, 0],
}).unwrap() <= BLOB_DATA_SIZE as u64
}
/// Creates the next Tick Entry `num_hashes` after `start_hash`.
pub fn new_mut(
start_hash: &mut Hash,
num_hashes: &mut u64,
transactions: Vec<Transaction>,
has_more: bool,
) -> Self {
let entry = Self::new(start_hash, *num_hashes, transactions, has_more);
*start_hash = entry.id;
*num_hashes = 0;
assert!(serialized_size(&entry).unwrap() <= BLOB_DATA_SIZE as u64);
entry
}
/// Creates a Entry from the number of hashes `num_hashes` since the previous transaction
/// and that resulting `id`.
pub fn new_tick(num_hashes: u64, id: &Hash) -> Self {
Entry {
num_hashes,
id: *id,
transactions: vec![],
has_more: false,
pad: [0, 0, 0],
}
}
/// Verifies self.id is the result of hashing a `start_hash` `self.num_hashes` times.
/// If the transaction is not a Tick, then hash that as well.
pub fn verify(&self, start_hash: &Hash) -> bool {
let tx_plans_verified = self.transactions.par_iter().all(|tx| {
let r = tx.verify_plan();
if !r {
warn!("tx plan invalid: {:?}", tx);
}
r
});
if !tx_plans_verified {
return false;
}
let ref_hash = next_hash(start_hash, self.num_hashes, &self.transactions);
if self.id != ref_hash {
warn!(
"next_hash is invalid expected: {:?} actual: {:?}",
self.id, ref_hash
);
return false;
}
true
}
}
fn add_transaction_data(hash_data: &mut Vec<u8>, tx: &Transaction) {
hash_data.push(0u8);
hash_data.extend_from_slice(&tx.signature.as_ref());
}
/// Creates the hash `num_hashes` after `start_hash`. If the transaction contains
/// a signature, the final hash will be a hash of both the previous ID and
/// the signature. If num_hashes is zero and there's no transaction data,
/// start_hash is returned.
fn next_hash(start_hash: &Hash, num_hashes: u64, transactions: &[Transaction]) -> Hash {
let mut id = *start_hash;
for _ in 1..num_hashes {
id = hash(&id.as_ref());
}
// Hash all the transaction data
let mut hash_data = vec![];
for tx in transactions {
add_transaction_data(&mut hash_data, tx);
}
if !hash_data.is_empty() {
extend_and_hash(&id, &hash_data)
} else if num_hashes != 0 {
hash(&id.as_ref())
} else {
id
}
}
/// Creates the next Tick or Transaction Entry `num_hashes` after `start_hash`.
pub fn next_entry(start_hash: &Hash, num_hashes: u64, transactions: Vec<Transaction>) -> Entry {
assert!(num_hashes > 0 || transactions.is_empty());
Entry {
num_hashes,
id: next_hash(start_hash, num_hashes, &transactions),
transactions,
has_more: false,
pad: [0, 0, 0],
}
}
#[cfg(test)]
mod tests {
use super::*;
use chrono::prelude::*;
use entry::Entry;
use hash::hash;
use signature::{Keypair, KeypairUtil};
use transaction::Transaction;
#[test]
fn test_entry_verify() {
let zero = Hash::default();
let one = hash(&zero.as_ref());
assert!(Entry::new_tick(0, &zero).verify(&zero)); // base case
assert!(!Entry::new_tick(0, &zero).verify(&one)); // base case, bad
assert!(next_entry(&zero, 1, vec![]).verify(&zero)); // inductive step
assert!(!next_entry(&zero, 1, vec![]).verify(&one)); // inductive step, bad
}
#[test]
fn test_transaction_reorder_attack() {
let zero = Hash::default();
// First, verify entries
let keypair = Keypair::new();
let tx0 = Transaction::new(&keypair, keypair.pubkey(), 0, zero);
let tx1 = Transaction::new(&keypair, keypair.pubkey(), 1, zero);
let mut e0 = Entry::new(&zero, 0, vec![tx0.clone(), tx1.clone()], false);
assert!(e0.verify(&zero));
// Next, swap two transactions and ensure verification fails.
e0.transactions[0] = tx1; // <-- attack
e0.transactions[1] = tx0;
assert!(!e0.verify(&zero));
}
#[test]
fn test_witness_reorder_attack() {
let zero = Hash::default();
// First, verify entries
let keypair = Keypair::new();
let tx0 = Transaction::new_timestamp(&keypair, Utc::now(), zero);
let tx1 = Transaction::new_signature(&keypair, Default::default(), zero);
let mut e0 = Entry::new(&zero, 0, vec![tx0.clone(), tx1.clone()], false);
assert!(e0.verify(&zero));
// Next, swap two witness transactions and ensure verification fails.
e0.transactions[0] = tx1; // <-- attack
e0.transactions[1] = tx0;
assert!(!e0.verify(&zero));
}
#[test]
fn test_next_entry() {
let zero = Hash::default();
let tick = next_entry(&zero, 1, vec![]);
assert_eq!(tick.num_hashes, 1);
assert_ne!(tick.id, zero);
let tick = next_entry(&zero, 0, vec![]);
assert_eq!(tick.num_hashes, 0);
assert_eq!(tick.id, zero);
let keypair = Keypair::new();
let tx0 = Transaction::new_timestamp(&keypair, Utc::now(), zero);
let entry0 = next_entry(&zero, 1, vec![tx0.clone()]);
assert_eq!(entry0.num_hashes, 1);
assert_eq!(entry0.id, next_hash(&zero, 1, &vec![tx0]));
}
#[test]
#[should_panic]
fn test_next_entry_panic() {
let zero = Hash::default();
let keypair = Keypair::new();
let tx = Transaction::new(&keypair, keypair.pubkey(), 0, zero);
next_entry(&zero, 0, vec![tx]);
}
}

Some files were not shown because too many files have changed in this diff Show More