Validator/authority contract (#3937)
* dir * simple validator list * stub validator contract * make the engine hold Weak<Client> instead of IoChannel * validator set factory * register weak client with ValidatorContract * check chain security * add address array to generator * register provider contract * update validator set on notify * add validator contract spec * simple list test * split update and contract test * contract change * use client in tendermint * fix deadlock * step duration in params * adapt tendermint tests * add storage fields to test spec * constructor spec * execute under wrong address * create under correct address * revert * validator contract constructor * move genesis block lookup * add removal ability to contract * validator contract adding validators * fix basic authority * validator changing test * more docs * update sync tests * remove env_logger * another env_logger * cameltoe * hold EngineClient instead of Client * add a comment about lock scope
This commit is contained in:
		
							parent
							
								
									5c5244911e
								
							
						
					
					
						commit
						be30c44179
					
				@ -6,10 +6,12 @@
 | 
				
			|||||||
				"gasLimitBoundDivisor": "0x0400",
 | 
									"gasLimitBoundDivisor": "0x0400",
 | 
				
			||||||
				"stepDuration": 1,
 | 
									"stepDuration": 1,
 | 
				
			||||||
				"startStep": 2,
 | 
									"startStep": 2,
 | 
				
			||||||
				"authorities" : [
 | 
									"validators": {
 | 
				
			||||||
					"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e",
 | 
										"list": [
 | 
				
			||||||
					"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1"
 | 
											"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e",
 | 
				
			||||||
				]
 | 
											"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1"
 | 
				
			||||||
 | 
										]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,9 @@
 | 
				
			|||||||
			"params": {
 | 
								"params": {
 | 
				
			||||||
				"gasLimitBoundDivisor": "0x0400",
 | 
									"gasLimitBoundDivisor": "0x0400",
 | 
				
			||||||
				"durationLimit": "0x0d",
 | 
									"durationLimit": "0x0d",
 | 
				
			||||||
				"authorities" : ["0x9cce34f7ab185c7aba1b7c8140d620b4bda941d6"]
 | 
									"validators": {
 | 
				
			||||||
 | 
										"list": ["0x9cce34f7ab185c7aba1b7c8140d620b4bda941d6"]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
@ -17,7 +19,7 @@
 | 
				
			|||||||
	},
 | 
						},
 | 
				
			||||||
	"genesis": {
 | 
						"genesis": {
 | 
				
			||||||
		"seal": {
 | 
							"seal": {
 | 
				
			||||||
			"generic": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"
 | 
								"generic": "0xc180"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"difficulty": "0x20000",
 | 
							"difficulty": "0x20000",
 | 
				
			||||||
		"author": "0x0000000000000000000000000000000000000000",
 | 
							"author": "0x0000000000000000000000000000000000000000",
 | 
				
			||||||
 | 
				
			|||||||
@ -4,10 +4,12 @@
 | 
				
			|||||||
		"tendermint": {
 | 
							"tendermint": {
 | 
				
			||||||
			"params": {
 | 
								"params": {
 | 
				
			||||||
				"gasLimitBoundDivisor": "0x0400",
 | 
									"gasLimitBoundDivisor": "0x0400",
 | 
				
			||||||
				"authorities" : [
 | 
									"validators" : {
 | 
				
			||||||
					"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1",
 | 
										"list": [
 | 
				
			||||||
					"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e"
 | 
											"0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1",
 | 
				
			||||||
				]
 | 
											"0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e"
 | 
				
			||||||
 | 
										]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										42
									
								
								ethcore/res/validator_contract.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								ethcore/res/validator_contract.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
						"name": "TestValidatorContract",
 | 
				
			||||||
 | 
						"engine": {
 | 
				
			||||||
 | 
							"basicAuthority": {
 | 
				
			||||||
 | 
								"params": {
 | 
				
			||||||
 | 
									"gasLimitBoundDivisor": "0x0400",
 | 
				
			||||||
 | 
									"durationLimit": "0x0d",
 | 
				
			||||||
 | 
									"validators": {
 | 
				
			||||||
 | 
										"contract": "0x0000000000000000000000000000000000000005"
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						"params": {
 | 
				
			||||||
 | 
							"accountStartNonce": "0x0",
 | 
				
			||||||
 | 
							"maximumExtraDataSize": "0x20",
 | 
				
			||||||
 | 
							"minGasLimit": "0x1388",
 | 
				
			||||||
 | 
							"networkID" : "0x69"
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						"genesis": {
 | 
				
			||||||
 | 
							"seal": {
 | 
				
			||||||
 | 
								"generic": "0xc180"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"difficulty": "0x20000",
 | 
				
			||||||
 | 
							"author": "0x0000000000000000000000000000000000000000",
 | 
				
			||||||
 | 
							"timestamp": "0x00",
 | 
				
			||||||
 | 
							"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
 | 
				
			||||||
 | 
							"extraData": "0x",
 | 
				
			||||||
 | 
							"gasLimit": "0x2fefd8"
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						"accounts": {
 | 
				
			||||||
 | 
							"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
 | 
				
			||||||
 | 
							"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
 | 
				
			||||||
 | 
							"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
 | 
				
			||||||
 | 
							"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
 | 
				
			||||||
 | 
							"0000000000000000000000000000000000000005": {
 | 
				
			||||||
 | 
								"balance": "1",
 | 
				
			||||||
 | 
								"constructor": "0x60a06040819052737d577a597b2742b498cb5cf0c26cdcd726d39e6e60609081527382a978b3f5962a5b0957d9ee9eef472ee55b42f1608052600080546002825581805290927f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5639182019291905b828111156100a25782518254600160a060020a031916600160a060020a0390911617825560209092019160019091019061006d565b5b506100cd9291505b808211156100c9578054600160a060020a03191681556001016100ab565b5090565b505034610000575b610332806100e46000396000f300606060405263ffffffff60e060020a60003504166335aa2e4481146100455780634d238c8e14610071578063b7ab4db51461008c578063f94e1867146100f4575b610000565b3461000057610055600435610106565b60408051600160a060020a039092168252519081900360200190f35b346100005761008a600160a060020a0360043516610136565b005b34610000576100996101ad565b60408051602080825283518183015283519192839290830191858101910280838382156100e1575b8051825260208311156100e157601f1990920191602091820191016100c1565b5050509050019250505060405180910390f35b346100005761008a600435610217565b005b600081815481101561000057906000526020600020900160005b915054906101000a9004600160a060020a031681565b60008054806001018281815481835581811511610178576000838152602090206101789181019083015b808211156101745760008155600101610160565b5090565b5b505050916000526020600020900160005b8154600160a060020a038086166101009390930a92830292021916179055505b50565b604080516020818101835260008083528054845181840281018401909552808552929392909183018282801561020c57602002820191906000526020600020905b8154600160a060020a031681526001909101906020018083116101ee575b505050505090505b90565b6000805460001981019081101561000057906000526020600020900160005b9054906101000a9004600160a060020a0316600082815481101561000057906000526020600020900160005b6101000a815481600160a060020a030219169083600160a060020a031602179055506000600160008054905003815481101561000057906000526020600020900160005b6101000a815490600160a060020a03021916905560008054809190600190038154818355818115116102fd576000838152602090206102fd9181019083015b808211156101745760008155600101610160565b5090565b5b505050505b505600a165627a7a72305820d742dd391941c1c255f0e1187ffa5b1e783219264fb10196018aefa379f5638b0029"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"9cce34f7ab185c7aba1b7c8140d620b4bda941d6": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -53,7 +53,7 @@ use verification::queue::BlockQueue;
 | 
				
			|||||||
use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
 | 
					use blockchain::{BlockChain, BlockProvider, TreeRoute, ImportRoute};
 | 
				
			||||||
use client::{
 | 
					use client::{
 | 
				
			||||||
	BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
 | 
						BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient,
 | 
				
			||||||
	MiningBlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode,
 | 
						MiningBlockChainClient, EngineClient, TraceFilter, CallAnalytics, BlockImportError, Mode,
 | 
				
			||||||
	ChainNotify, PruningInfo,
 | 
						ChainNotify, PruningInfo,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use client::Error as ClientError;
 | 
					use client::Error as ClientError;
 | 
				
			||||||
@ -1315,11 +1315,6 @@ impl BlockChainClient for Client {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn broadcast_consensus_message(&self, message: Bytes) {
 | 
					 | 
				
			||||||
		self.notify(|notify| notify.broadcast(message.clone()));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fn signing_network_id(&self) -> Option<u64> {
 | 
						fn signing_network_id(&self) -> Option<u64> {
 | 
				
			||||||
		self.engine.signing_network_id(&self.latest_env_info())
 | 
							self.engine.signing_network_id(&self.latest_env_info())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -1414,16 +1409,6 @@ impl MiningBlockChainClient for Client {
 | 
				
			|||||||
		&self.factories.vm
 | 
							&self.factories.vm
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn update_sealing(&self) {
 | 
					 | 
				
			||||||
		self.miner.update_sealing(self)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
 | 
					 | 
				
			||||||
		if self.miner.submit_seal(self, block_hash, seal).is_err() {
 | 
					 | 
				
			||||||
			warn!(target: "poa", "Wrong internal seal submission!")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fn broadcast_proposal_block(&self, block: SealedBlock) {
 | 
						fn broadcast_proposal_block(&self, block: SealedBlock) {
 | 
				
			||||||
		self.notify(|notify| {
 | 
							self.notify(|notify| {
 | 
				
			||||||
			notify.new_blocks(
 | 
								notify.new_blocks(
 | 
				
			||||||
@ -1471,6 +1456,22 @@ impl MiningBlockChainClient for Client {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl EngineClient for Client {
 | 
				
			||||||
 | 
						fn update_sealing(&self) {
 | 
				
			||||||
 | 
							self.miner.update_sealing(self)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
 | 
				
			||||||
 | 
							if self.miner.submit_seal(self, block_hash, seal).is_err() {
 | 
				
			||||||
 | 
								warn!(target: "poa", "Wrong internal seal submission!")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn broadcast_consensus_message(&self, message: Bytes) {
 | 
				
			||||||
 | 
							self.notify(|notify| notify.broadcast(message.clone()));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl MayPanic for Client {
 | 
					impl MayPanic for Client {
 | 
				
			||||||
	fn on_panic<F>(&self, closure: F) where F: OnPanicListener {
 | 
						fn on_panic<F>(&self, closure: F) where F: OnPanicListener {
 | 
				
			||||||
		self.panic_handler.on_panic(closure);
 | 
							self.panic_handler.on_panic(closure);
 | 
				
			||||||
 | 
				
			|||||||
@ -28,7 +28,7 @@ pub use self::config::{Mode, ClientConfig, DatabaseCompactionProfile, BlockChain
 | 
				
			|||||||
pub use self::error::Error;
 | 
					pub use self::error::Error;
 | 
				
			||||||
pub use self::test_client::{TestBlockChainClient, EachBlockWith};
 | 
					pub use self::test_client::{TestBlockChainClient, EachBlockWith};
 | 
				
			||||||
pub use self::chain_notify::ChainNotify;
 | 
					pub use self::chain_notify::ChainNotify;
 | 
				
			||||||
pub use self::traits::{BlockChainClient, MiningBlockChainClient};
 | 
					pub use self::traits::{BlockChainClient, MiningBlockChainClient, EngineClient};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use self::traits::ProvingBlockChainClient;
 | 
					pub use self::traits::ProvingBlockChainClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -24,7 +24,7 @@ use devtools::*;
 | 
				
			|||||||
use transaction::{Transaction, LocalizedTransaction, SignedTransaction, PendingTransaction, Action};
 | 
					use transaction::{Transaction, LocalizedTransaction, SignedTransaction, PendingTransaction, Action};
 | 
				
			||||||
use blockchain::TreeRoute;
 | 
					use blockchain::TreeRoute;
 | 
				
			||||||
use client::{
 | 
					use client::{
 | 
				
			||||||
	BlockChainClient, MiningBlockChainClient, BlockChainInfo, BlockStatus, BlockId,
 | 
						BlockChainClient, MiningBlockChainClient, EngineClient, BlockChainInfo, BlockStatus, BlockId,
 | 
				
			||||||
	TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError,
 | 
						TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use db::{NUM_COLUMNS, COL_STATE};
 | 
					use db::{NUM_COLUMNS, COL_STATE};
 | 
				
			||||||
@ -372,16 +372,6 @@ impl MiningBlockChainClient for TestBlockChainClient {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn broadcast_proposal_block(&self, _block: SealedBlock) {}
 | 
						fn broadcast_proposal_block(&self, _block: SealedBlock) {}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	fn update_sealing(&self) {
 | 
					 | 
				
			||||||
		self.miner.update_sealing(self)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
 | 
					 | 
				
			||||||
		if self.miner.submit_seal(self, block_hash, seal).is_err() {
 | 
					 | 
				
			||||||
			warn!(target: "poa", "Wrong internal seal submission!")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl BlockChainClient for TestBlockChainClient {
 | 
					impl BlockChainClient for TestBlockChainClient {
 | 
				
			||||||
@ -699,8 +689,6 @@ impl BlockChainClient for TestBlockChainClient {
 | 
				
			|||||||
		self.spec.engine.handle_message(&message).unwrap();
 | 
							self.spec.engine.handle_message(&message).unwrap();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn broadcast_consensus_message(&self, _message: Bytes) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fn ready_transactions(&self) -> Vec<PendingTransaction> {
 | 
						fn ready_transactions(&self) -> Vec<PendingTransaction> {
 | 
				
			||||||
		self.miner.ready_transactions(self.chain_info().best_block_number)
 | 
							self.miner.ready_transactions(self.chain_info().best_block_number)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -727,3 +715,17 @@ impl BlockChainClient for TestBlockChainClient {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	fn registry_address(&self, _name: String) -> Option<Address> { None }
 | 
						fn registry_address(&self, _name: String) -> Option<Address> { None }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl EngineClient for TestBlockChainClient {
 | 
				
			||||||
 | 
						fn update_sealing(&self) {
 | 
				
			||||||
 | 
							self.miner.update_sealing(self)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
 | 
				
			||||||
 | 
							if self.miner.submit_seal(self, block_hash, seal).is_err() {
 | 
				
			||||||
 | 
								warn!(target: "poa", "Wrong internal seal submission!")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn broadcast_consensus_message(&self, _message: Bytes) {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -208,9 +208,6 @@ pub trait BlockChainClient : Sync + Send {
 | 
				
			|||||||
	/// Queue conensus engine message.
 | 
						/// Queue conensus engine message.
 | 
				
			||||||
	fn queue_consensus_message(&self, message: Bytes);
 | 
						fn queue_consensus_message(&self, message: Bytes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Used by PoA to communicate with peers.
 | 
					 | 
				
			||||||
	fn broadcast_consensus_message(&self, message: Bytes);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/// List all transactions that are allowed into the next block.
 | 
						/// List all transactions that are allowed into the next block.
 | 
				
			||||||
	fn ready_transactions(&self) -> Vec<PendingTransaction>;
 | 
						fn ready_transactions(&self) -> Vec<PendingTransaction>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -294,12 +291,6 @@ pub trait MiningBlockChainClient: BlockChainClient {
 | 
				
			|||||||
	/// Returns EvmFactory.
 | 
						/// Returns EvmFactory.
 | 
				
			||||||
	fn vm_factory(&self) -> &EvmFactory;
 | 
						fn vm_factory(&self) -> &EvmFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Used by PoA to try sealing on period change.
 | 
					 | 
				
			||||||
	fn update_sealing(&self);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/// Used by PoA to submit gathered signatures.
 | 
					 | 
				
			||||||
	fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/// Broadcast a block proposal.
 | 
						/// Broadcast a block proposal.
 | 
				
			||||||
	fn broadcast_proposal_block(&self, block: SealedBlock);
 | 
						fn broadcast_proposal_block(&self, block: SealedBlock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -310,6 +301,18 @@ pub trait MiningBlockChainClient: BlockChainClient {
 | 
				
			|||||||
	fn latest_schedule(&self) -> Schedule;
 | 
						fn latest_schedule(&self) -> Schedule;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Client facilities used by internally sealing Engines.
 | 
				
			||||||
 | 
					pub trait EngineClient: MiningBlockChainClient {
 | 
				
			||||||
 | 
						/// Make a new block and seal it.
 | 
				
			||||||
 | 
						fn update_sealing(&self);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Submit a seal for a block in the mining queue.
 | 
				
			||||||
 | 
						fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Broadcast a consensus message to the network.
 | 
				
			||||||
 | 
						fn broadcast_consensus_message(&self, message: Bytes);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Extended client interface for providing proofs of the state.
 | 
					/// Extended client interface for providing proofs of the state.
 | 
				
			||||||
pub trait ProvingBlockChainClient: BlockChainClient {
 | 
					pub trait ProvingBlockChainClient: BlockChainClient {
 | 
				
			||||||
	/// Prove account storage at a specific block id.
 | 
						/// Prove account storage at a specific block id.
 | 
				
			||||||
 | 
				
			|||||||
@ -32,11 +32,12 @@ use blockchain::extras::BlockDetails;
 | 
				
			|||||||
use views::HeaderView;
 | 
					use views::HeaderView;
 | 
				
			||||||
use evm::Schedule;
 | 
					use evm::Schedule;
 | 
				
			||||||
use ethjson;
 | 
					use ethjson;
 | 
				
			||||||
use io::{IoContext, IoHandler, TimerToken, IoService, IoChannel};
 | 
					use io::{IoContext, IoHandler, TimerToken, IoService};
 | 
				
			||||||
use service::ClientIoMessage;
 | 
					 | 
				
			||||||
use transaction::SignedTransaction;
 | 
					use transaction::SignedTransaction;
 | 
				
			||||||
use env_info::EnvInfo;
 | 
					use env_info::EnvInfo;
 | 
				
			||||||
use builtin::Builtin;
 | 
					use builtin::Builtin;
 | 
				
			||||||
 | 
					use client::{Client, EngineClient};
 | 
				
			||||||
 | 
					use super::validator_set::{ValidatorSet, new_validator_set};
 | 
				
			||||||
use state::CleanupMode;
 | 
					use state::CleanupMode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// `AuthorityRound` params.
 | 
					/// `AuthorityRound` params.
 | 
				
			||||||
@ -46,14 +47,12 @@ pub struct AuthorityRoundParams {
 | 
				
			|||||||
	pub gas_limit_bound_divisor: U256,
 | 
						pub gas_limit_bound_divisor: U256,
 | 
				
			||||||
	/// Time to wait before next block or authority switching.
 | 
						/// Time to wait before next block or authority switching.
 | 
				
			||||||
	pub step_duration: Duration,
 | 
						pub step_duration: Duration,
 | 
				
			||||||
	/// Valid authorities.
 | 
					 | 
				
			||||||
	pub authorities: Vec<Address>,
 | 
					 | 
				
			||||||
	/// Number of authorities.
 | 
					 | 
				
			||||||
	pub authority_n: usize,
 | 
					 | 
				
			||||||
	/// Block reward.
 | 
						/// Block reward.
 | 
				
			||||||
	pub block_reward: U256,
 | 
						pub block_reward: U256,
 | 
				
			||||||
	/// Starting step,
 | 
						/// Starting step,
 | 
				
			||||||
	pub start_step: Option<u64>,
 | 
						pub start_step: Option<u64>,
 | 
				
			||||||
 | 
						/// Valid validators.
 | 
				
			||||||
 | 
						pub validators: ethjson::spec::ValidatorSet,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
 | 
					impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
 | 
				
			||||||
@ -61,8 +60,7 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
 | 
				
			|||||||
		AuthorityRoundParams {
 | 
							AuthorityRoundParams {
 | 
				
			||||||
			gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
 | 
								gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
 | 
				
			||||||
			step_duration: Duration::from_secs(p.step_duration.into()),
 | 
								step_duration: Duration::from_secs(p.step_duration.into()),
 | 
				
			||||||
			authority_n: p.authorities.len(),
 | 
								validators: p.validators,
 | 
				
			||||||
			authorities: p.authorities.into_iter().map(Into::into).collect::<Vec<_>>(),
 | 
					 | 
				
			||||||
			block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
 | 
								block_reward: p.block_reward.map_or_else(U256::zero, Into::into),
 | 
				
			||||||
			start_step: p.start_step.map(Into::into),
 | 
								start_step: p.start_step.map(Into::into),
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -73,14 +71,17 @@ impl From<ethjson::spec::AuthorityRoundParams> for AuthorityRoundParams {
 | 
				
			|||||||
/// mainnet chains in the Olympic, Frontier and Homestead eras.
 | 
					/// mainnet chains in the Olympic, Frontier and Homestead eras.
 | 
				
			||||||
pub struct AuthorityRound {
 | 
					pub struct AuthorityRound {
 | 
				
			||||||
	params: CommonParams,
 | 
						params: CommonParams,
 | 
				
			||||||
	our_params: AuthorityRoundParams,
 | 
						gas_limit_bound_divisor: U256,
 | 
				
			||||||
 | 
						block_reward: U256,
 | 
				
			||||||
 | 
						step_duration: Duration,
 | 
				
			||||||
	builtins: BTreeMap<Address, Builtin>,
 | 
						builtins: BTreeMap<Address, Builtin>,
 | 
				
			||||||
	transition_service: IoService<()>,
 | 
						transition_service: IoService<()>,
 | 
				
			||||||
	message_channel: Mutex<Option<IoChannel<ClientIoMessage>>>,
 | 
					 | 
				
			||||||
	step: AtomicUsize,
 | 
						step: AtomicUsize,
 | 
				
			||||||
	proposed: AtomicBool,
 | 
						proposed: AtomicBool,
 | 
				
			||||||
	account_provider: Mutex<Option<Arc<AccountProvider>>>,
 | 
						client: RwLock<Option<Weak<EngineClient>>>,
 | 
				
			||||||
 | 
						account_provider: Mutex<Arc<AccountProvider>>,
 | 
				
			||||||
	password: RwLock<Option<String>>,
 | 
						password: RwLock<Option<String>>,
 | 
				
			||||||
 | 
						validators: Box<ValidatorSet + Send + Sync>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> {
 | 
					fn header_step(header: &Header) -> Result<usize, ::rlp::DecoderError> {
 | 
				
			||||||
@ -109,14 +110,17 @@ impl AuthorityRound {
 | 
				
			|||||||
		let engine = Arc::new(
 | 
							let engine = Arc::new(
 | 
				
			||||||
			AuthorityRound {
 | 
								AuthorityRound {
 | 
				
			||||||
				params: params,
 | 
									params: params,
 | 
				
			||||||
				our_params: our_params,
 | 
									gas_limit_bound_divisor: our_params.gas_limit_bound_divisor,
 | 
				
			||||||
 | 
									block_reward: our_params.block_reward,
 | 
				
			||||||
 | 
									step_duration: our_params.step_duration,
 | 
				
			||||||
				builtins: builtins,
 | 
									builtins: builtins,
 | 
				
			||||||
				transition_service: IoService::<()>::start()?,
 | 
									transition_service: IoService::<()>::start()?,
 | 
				
			||||||
				message_channel: Mutex::new(None),
 | 
					 | 
				
			||||||
				step: AtomicUsize::new(initial_step),
 | 
									step: AtomicUsize::new(initial_step),
 | 
				
			||||||
				proposed: AtomicBool::new(false),
 | 
									proposed: AtomicBool::new(false),
 | 
				
			||||||
				account_provider: Mutex::new(None),
 | 
									client: RwLock::new(None),
 | 
				
			||||||
 | 
									account_provider: Mutex::new(Arc::new(AccountProvider::transient_provider())),
 | 
				
			||||||
				password: RwLock::new(None),
 | 
									password: RwLock::new(None),
 | 
				
			||||||
 | 
									validators: new_validator_set(our_params.validators),
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		// Do not initialize timeouts for tests.
 | 
							// Do not initialize timeouts for tests.
 | 
				
			||||||
		if should_timeout {
 | 
							if should_timeout {
 | 
				
			||||||
@ -128,7 +132,7 @@ impl AuthorityRound {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	fn remaining_step_duration(&self) -> Duration {
 | 
						fn remaining_step_duration(&self) -> Duration {
 | 
				
			||||||
		let now = unix_now();
 | 
							let now = unix_now();
 | 
				
			||||||
		let step_end = self.our_params.step_duration * (self.step.load(AtomicOrdering::SeqCst) as u32 + 1);
 | 
							let step_end = self.step_duration * (self.step.load(AtomicOrdering::SeqCst) as u32 + 1);
 | 
				
			||||||
		if step_end > now {
 | 
							if step_end > now {
 | 
				
			||||||
			step_end - now
 | 
								step_end - now
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
@ -136,13 +140,12 @@ impl AuthorityRound {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn step_proposer(&self, step: usize) -> &Address {
 | 
						fn step_proposer(&self, step: usize) -> Address {
 | 
				
			||||||
		let p = &self.our_params;
 | 
							self.validators.get(step)
 | 
				
			||||||
		p.authorities.get(step % p.authority_n).expect("There are authority_n authorities; taking number modulo authority_n gives number in authority_n range; qed")
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn is_step_proposer(&self, step: usize, address: &Address) -> bool {
 | 
						fn is_step_proposer(&self, step: usize, address: &Address) -> bool {
 | 
				
			||||||
		self.step_proposer(step) == address
 | 
							self.step_proposer(step) == *address
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -187,10 +190,9 @@ impl Engine for AuthorityRound {
 | 
				
			|||||||
	fn step(&self) {
 | 
						fn step(&self) {
 | 
				
			||||||
		self.step.fetch_add(1, AtomicOrdering::SeqCst);
 | 
							self.step.fetch_add(1, AtomicOrdering::SeqCst);
 | 
				
			||||||
		self.proposed.store(false, AtomicOrdering::SeqCst);
 | 
							self.proposed.store(false, AtomicOrdering::SeqCst);
 | 
				
			||||||
		if let Some(ref channel) = *self.message_channel.lock() {
 | 
							if let Some(ref weak) = *self.client.read() {
 | 
				
			||||||
			match channel.send(ClientIoMessage::UpdateSealing) {
 | 
								if let Some(c) = weak.upgrade() {
 | 
				
			||||||
				Ok(_) => trace!(target: "poa", "timeout: UpdateSealing message sent for step {}.", self.step.load(AtomicOrdering::Relaxed)),
 | 
									c.update_sealing();
 | 
				
			||||||
				Err(err) => trace!(target: "poa", "timeout: Could not send a sealing message {} for step {}.", err, self.step.load(AtomicOrdering::Relaxed)),
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -211,7 +213,7 @@ impl Engine for AuthorityRound {
 | 
				
			|||||||
		header.set_difficulty(parent.difficulty().clone());
 | 
							header.set_difficulty(parent.difficulty().clone());
 | 
				
			||||||
		header.set_gas_limit({
 | 
							header.set_gas_limit({
 | 
				
			||||||
			let gas_limit = parent.gas_limit().clone();
 | 
								let gas_limit = parent.gas_limit().clone();
 | 
				
			||||||
			let bound_divisor = self.our_params.gas_limit_bound_divisor;
 | 
								let bound_divisor = self.gas_limit_bound_divisor;
 | 
				
			||||||
			if gas_limit < gas_floor_target {
 | 
								if gas_limit < gas_floor_target {
 | 
				
			||||||
				min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
 | 
									min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
@ -221,8 +223,7 @@ impl Engine for AuthorityRound {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn is_sealer(&self, author: &Address) -> Option<bool> {
 | 
						fn is_sealer(&self, author: &Address) -> Option<bool> {
 | 
				
			||||||
		let p = &self.our_params;
 | 
							Some(self.validators.contains(author))
 | 
				
			||||||
		Some(p.authorities.contains(author))
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Attempt to seal the block internally.
 | 
						/// Attempt to seal the block internally.
 | 
				
			||||||
@ -234,18 +235,13 @@ impl Engine for AuthorityRound {
 | 
				
			|||||||
		let header = block.header();
 | 
							let header = block.header();
 | 
				
			||||||
		let step = self.step.load(AtomicOrdering::SeqCst);
 | 
							let step = self.step.load(AtomicOrdering::SeqCst);
 | 
				
			||||||
		if self.is_step_proposer(step, header.author()) {
 | 
							if self.is_step_proposer(step, header.author()) {
 | 
				
			||||||
			if let Some(ref ap) = *self.account_provider.lock() {
 | 
								let ref ap = *self.account_provider.lock();
 | 
				
			||||||
				// Account should be permanently unlocked, otherwise sealing will fail.
 | 
								if let Ok(signature) = ap.sign(*header.author(), self.password.read().clone(), header.bare_hash()) {
 | 
				
			||||||
				if let Ok(signature) = ap.sign(*header.author(), self.password.read().clone(), header.bare_hash()) {
 | 
									trace!(target: "poa", "generate_seal: Issuing a block for step {}.", step);
 | 
				
			||||||
					trace!(target: "poa", "generate_seal: Issuing a block for step {}.", step);
 | 
									self.proposed.store(true, AtomicOrdering::SeqCst);
 | 
				
			||||||
					self.proposed.store(true, AtomicOrdering::SeqCst);
 | 
									return Seal::Regular(vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
 | 
				
			||||||
					let rlps = vec![encode(&step).to_vec(), encode(&(&*signature as &[u8])).to_vec()];
 | 
					 | 
				
			||||||
					return Seal::Regular(rlps);
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					warn!(target: "poa", "generate_seal: FAIL: Accounts secret key unavailable.");
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				warn!(target: "poa", "generate_seal: FAIL: Accounts not provided.");
 | 
									warn!(target: "poa", "generate_seal: FAIL: Accounts secret key unavailable.");
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			trace!(target: "poa", "generate_seal: Not a proposer for step {}.", step);
 | 
								trace!(target: "poa", "generate_seal: Not a proposer for step {}.", step);
 | 
				
			||||||
@ -255,12 +251,9 @@ impl Engine for AuthorityRound {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	/// Apply the block reward on finalisation of the block.
 | 
						/// Apply the block reward on finalisation of the block.
 | 
				
			||||||
	fn on_close_block(&self, block: &mut ExecutedBlock) {
 | 
						fn on_close_block(&self, block: &mut ExecutedBlock) {
 | 
				
			||||||
		let reward = self.our_params.block_reward;
 | 
					 | 
				
			||||||
		let fields = block.fields_mut();
 | 
							let fields = block.fields_mut();
 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Bestow block reward
 | 
							// Bestow block reward
 | 
				
			||||||
		fields.state.add_balance(fields.header.author(), &reward, CleanupMode::NoEmpty);
 | 
							fields.state.add_balance(fields.header.author(), &self.block_reward, CleanupMode::NoEmpty);
 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Commit state so that we can actually figure out the state root.
 | 
							// Commit state so that we can actually figure out the state root.
 | 
				
			||||||
		if let Err(e) = fields.state.commit() {
 | 
							if let Err(e) = fields.state.commit() {
 | 
				
			||||||
			warn!("Encountered error on state commit: {}", e);
 | 
								warn!("Encountered error on state commit: {}", e);
 | 
				
			||||||
@ -285,7 +278,7 @@ impl Engine for AuthorityRound {
 | 
				
			|||||||
		// Give one step slack if step is lagging, double vote is still not possible.
 | 
							// Give one step slack if step is lagging, double vote is still not possible.
 | 
				
			||||||
		if header_step <= self.step.load(AtomicOrdering::SeqCst) + 1 {
 | 
							if header_step <= self.step.load(AtomicOrdering::SeqCst) + 1 {
 | 
				
			||||||
			let proposer_signature = header_signature(header)?;
 | 
								let proposer_signature = header_signature(header)?;
 | 
				
			||||||
			let ok_sig = verify_address(self.step_proposer(header_step), &proposer_signature, &header.bare_hash())?;
 | 
								let ok_sig = verify_address(&self.step_proposer(header_step), &proposer_signature, &header.bare_hash())?;
 | 
				
			||||||
			if ok_sig {
 | 
								if ok_sig {
 | 
				
			||||||
				Ok(())
 | 
									Ok(())
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
@ -310,7 +303,7 @@ impl Engine for AuthorityRound {
 | 
				
			|||||||
			Err(EngineError::DoubleVote(header.author().clone()))?;
 | 
								Err(EngineError::DoubleVote(header.author().clone()))?;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let gas_limit_divisor = self.our_params.gas_limit_bound_divisor;
 | 
							let gas_limit_divisor = self.gas_limit_bound_divisor;
 | 
				
			||||||
		let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
 | 
							let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
 | 
				
			||||||
		let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
 | 
							let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
 | 
				
			||||||
		if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
 | 
							if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
 | 
				
			||||||
@ -341,8 +334,9 @@ impl Engine for AuthorityRound {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn register_message_channel(&self, message_channel: IoChannel<ClientIoMessage>) {
 | 
						fn register_client(&self, client: Weak<Client>) {
 | 
				
			||||||
		*self.message_channel.lock() = Some(message_channel);
 | 
							*self.client.write() = Some(client.clone());
 | 
				
			||||||
 | 
							self.validators.register_call_contract(client);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn set_signer(&self, _address: Address, password: String) {
 | 
						fn set_signer(&self, _address: Address, password: String) {
 | 
				
			||||||
@ -350,7 +344,7 @@ impl Engine for AuthorityRound {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn register_account_provider(&self, account_provider: Arc<AccountProvider>) {
 | 
						fn register_account_provider(&self, account_provider: Arc<AccountProvider>) {
 | 
				
			||||||
		*self.account_provider.lock() = Some(account_provider);
 | 
							*self.account_provider.lock() = account_provider;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -458,7 +452,7 @@ mod tests {
 | 
				
			|||||||
		let engine = Spec::new_test_round().engine;
 | 
							let engine = Spec::new_test_round().engine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
 | 
							let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
 | 
				
			||||||
		// Two authorities.
 | 
							// Two validators.
 | 
				
			||||||
		// Spec starts with step 2.
 | 
							// Spec starts with step 2.
 | 
				
			||||||
		header.set_seal(vec![encode(&2usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
 | 
							header.set_seal(vec![encode(&2usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
 | 
				
			||||||
		assert!(engine.verify_block_seal(&header).is_err());
 | 
							assert!(engine.verify_block_seal(&header).is_err());
 | 
				
			||||||
@ -477,7 +471,7 @@ mod tests {
 | 
				
			|||||||
		let engine = Spec::new_test_round().engine;
 | 
							let engine = Spec::new_test_round().engine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
 | 
							let signature = tap.sign(addr, Some("0".into()), header.bare_hash()).unwrap();
 | 
				
			||||||
		// Two authorities.
 | 
							// Two validators.
 | 
				
			||||||
		// Spec starts with step 2.
 | 
							// Spec starts with step 2.
 | 
				
			||||||
		header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
 | 
							header.set_seal(vec![encode(&1usize).to_vec(), encode(&(&*signature as &[u8])).to_vec()]);
 | 
				
			||||||
		assert!(engine.verify_block_seal(&header).is_ok());
 | 
							assert!(engine.verify_block_seal(&header).is_ok());
 | 
				
			||||||
 | 
				
			|||||||
@ -16,6 +16,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
//! A blockchain engine that supports a basic, non-BFT proof-of-authority.
 | 
					//! A blockchain engine that supports a basic, non-BFT proof-of-authority.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::sync::Weak;
 | 
				
			||||||
 | 
					use util::*;
 | 
				
			||||||
use ethkey::{recover, public_to_address};
 | 
					use ethkey::{recover, public_to_address};
 | 
				
			||||||
use account_provider::AccountProvider;
 | 
					use account_provider::AccountProvider;
 | 
				
			||||||
use block::*;
 | 
					use block::*;
 | 
				
			||||||
@ -28,26 +30,23 @@ use evm::Schedule;
 | 
				
			|||||||
use ethjson;
 | 
					use ethjson;
 | 
				
			||||||
use header::Header;
 | 
					use header::Header;
 | 
				
			||||||
use transaction::SignedTransaction;
 | 
					use transaction::SignedTransaction;
 | 
				
			||||||
 | 
					use client::Client;
 | 
				
			||||||
use util::*;
 | 
					use super::validator_set::{ValidatorSet, new_validator_set};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// `BasicAuthority` params.
 | 
					/// `BasicAuthority` params.
 | 
				
			||||||
#[derive(Debug, PartialEq)]
 | 
					#[derive(Debug, PartialEq)]
 | 
				
			||||||
pub struct BasicAuthorityParams {
 | 
					pub struct BasicAuthorityParams {
 | 
				
			||||||
	/// Gas limit divisor.
 | 
						/// Gas limit divisor.
 | 
				
			||||||
	pub gas_limit_bound_divisor: U256,
 | 
						pub gas_limit_bound_divisor: U256,
 | 
				
			||||||
	/// Block duration.
 | 
					 | 
				
			||||||
	pub duration_limit: u64,
 | 
					 | 
				
			||||||
	/// Valid signatories.
 | 
						/// Valid signatories.
 | 
				
			||||||
	pub authorities: HashSet<Address>,
 | 
						pub validators: ethjson::spec::ValidatorSet,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl From<ethjson::spec::BasicAuthorityParams> for BasicAuthorityParams {
 | 
					impl From<ethjson::spec::BasicAuthorityParams> for BasicAuthorityParams {
 | 
				
			||||||
	fn from(p: ethjson::spec::BasicAuthorityParams) -> Self {
 | 
						fn from(p: ethjson::spec::BasicAuthorityParams) -> Self {
 | 
				
			||||||
		BasicAuthorityParams {
 | 
							BasicAuthorityParams {
 | 
				
			||||||
			gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
 | 
								gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
 | 
				
			||||||
			duration_limit: p.duration_limit.into(),
 | 
								validators: p.validators,
 | 
				
			||||||
			authorities: p.authorities.into_iter().map(Into::into).collect::<HashSet<_>>(),
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -56,10 +55,11 @@ impl From<ethjson::spec::BasicAuthorityParams> for BasicAuthorityParams {
 | 
				
			|||||||
/// mainnet chains in the Olympic, Frontier and Homestead eras.
 | 
					/// mainnet chains in the Olympic, Frontier and Homestead eras.
 | 
				
			||||||
pub struct BasicAuthority {
 | 
					pub struct BasicAuthority {
 | 
				
			||||||
	params: CommonParams,
 | 
						params: CommonParams,
 | 
				
			||||||
	our_params: BasicAuthorityParams,
 | 
						gas_limit_bound_divisor: U256,
 | 
				
			||||||
	builtins: BTreeMap<Address, Builtin>,
 | 
						builtins: BTreeMap<Address, Builtin>,
 | 
				
			||||||
	account_provider: Mutex<Option<Arc<AccountProvider>>>,
 | 
						account_provider: Mutex<Option<Arc<AccountProvider>>>,
 | 
				
			||||||
	password: RwLock<Option<String>>,
 | 
						password: RwLock<Option<String>>,
 | 
				
			||||||
 | 
						validators: Box<ValidatorSet + Send + Sync>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl BasicAuthority {
 | 
					impl BasicAuthority {
 | 
				
			||||||
@ -67,8 +67,9 @@ impl BasicAuthority {
 | 
				
			|||||||
	pub fn new(params: CommonParams, our_params: BasicAuthorityParams, builtins: BTreeMap<Address, Builtin>) -> Self {
 | 
						pub fn new(params: CommonParams, our_params: BasicAuthorityParams, builtins: BTreeMap<Address, Builtin>) -> Self {
 | 
				
			||||||
		BasicAuthority {
 | 
							BasicAuthority {
 | 
				
			||||||
			params: params,
 | 
								params: params,
 | 
				
			||||||
			our_params: our_params,
 | 
								gas_limit_bound_divisor: our_params.gas_limit_bound_divisor,
 | 
				
			||||||
			builtins: builtins,
 | 
								builtins: builtins,
 | 
				
			||||||
 | 
								validators: new_validator_set(our_params.validators),
 | 
				
			||||||
			account_provider: Mutex::new(None),
 | 
								account_provider: Mutex::new(None),
 | 
				
			||||||
			password: RwLock::new(None),
 | 
								password: RwLock::new(None),
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -95,7 +96,7 @@ impl Engine for BasicAuthority {
 | 
				
			|||||||
		header.set_difficulty(parent.difficulty().clone());
 | 
							header.set_difficulty(parent.difficulty().clone());
 | 
				
			||||||
		header.set_gas_limit({
 | 
							header.set_gas_limit({
 | 
				
			||||||
			let gas_limit = parent.gas_limit().clone();
 | 
								let gas_limit = parent.gas_limit().clone();
 | 
				
			||||||
			let bound_divisor = self.our_params.gas_limit_bound_divisor;
 | 
								let bound_divisor = self.gas_limit_bound_divisor;
 | 
				
			||||||
			if gas_limit < gas_floor_target {
 | 
								if gas_limit < gas_floor_target {
 | 
				
			||||||
				min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
 | 
									min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
@ -105,22 +106,22 @@ impl Engine for BasicAuthority {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn is_sealer(&self, author: &Address) -> Option<bool> {
 | 
						fn is_sealer(&self, author: &Address) -> Option<bool> {
 | 
				
			||||||
		Some(self.our_params.authorities.contains(author))
 | 
							Some(self.validators.contains(author))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Attempt to seal the block internally.
 | 
						/// Attempt to seal the block internally.
 | 
				
			||||||
	///
 | 
					 | 
				
			||||||
	/// This operation is synchronous and may (quite reasonably) not be available, in which `false` will
 | 
					 | 
				
			||||||
	/// be returned.
 | 
					 | 
				
			||||||
	fn generate_seal(&self, block: &ExecutedBlock) -> Seal {
 | 
						fn generate_seal(&self, block: &ExecutedBlock) -> Seal {
 | 
				
			||||||
		if let Some(ref ap) = *self.account_provider.lock() {
 | 
							if let Some(ref ap) = *self.account_provider.lock() {
 | 
				
			||||||
			let header = block.header();
 | 
								let header = block.header();
 | 
				
			||||||
			let message = header.bare_hash();
 | 
								let author = header.author();
 | 
				
			||||||
			// account should be pernamently unlocked, otherwise sealing will fail
 | 
								if self.validators.contains(author) {
 | 
				
			||||||
			if let Ok(signature) = ap.sign(*block.header().author(), self.password.read().clone(), message) {
 | 
									let message = header.bare_hash();
 | 
				
			||||||
				return Seal::Regular(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
 | 
									// account should be pernamently unlocked, otherwise sealing will fail
 | 
				
			||||||
			} else {
 | 
									if let Ok(signature) = ap.sign(*author, self.password.read().clone(), message) {
 | 
				
			||||||
				trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
 | 
										return Seal::Regular(vec![::rlp::encode(&(&*signature as &[u8])).to_vec()]);
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										trace!(target: "basicauthority", "generate_seal: FAIL: accounts secret key unavailable");
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			trace!(target: "basicauthority", "generate_seal: FAIL: accounts not provided");
 | 
								trace!(target: "basicauthority", "generate_seal: FAIL: accounts not provided");
 | 
				
			||||||
@ -145,7 +146,7 @@ impl Engine for BasicAuthority {
 | 
				
			|||||||
		// check the signature is legit.
 | 
							// check the signature is legit.
 | 
				
			||||||
		let sig = UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()?;
 | 
							let sig = UntrustedRlp::new(&header.seal()[0]).as_val::<H520>()?;
 | 
				
			||||||
		let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?);
 | 
							let signer = public_to_address(&recover(&sig.into(), &header.bare_hash())?);
 | 
				
			||||||
		if !self.our_params.authorities.contains(&signer) {
 | 
							if !self.validators.contains(&signer) {
 | 
				
			||||||
			return Err(BlockError::InvalidSeal)?;
 | 
								return Err(BlockError::InvalidSeal)?;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		Ok(())
 | 
							Ok(())
 | 
				
			||||||
@ -161,7 +162,7 @@ impl Engine for BasicAuthority {
 | 
				
			|||||||
		if header.difficulty() != parent.difficulty() {
 | 
							if header.difficulty() != parent.difficulty() {
 | 
				
			||||||
			return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: *parent.difficulty(), found: *header.difficulty() })))
 | 
								return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: *parent.difficulty(), found: *header.difficulty() })))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		let gas_limit_divisor = self.our_params.gas_limit_bound_divisor;
 | 
							let gas_limit_divisor = self.gas_limit_bound_divisor;
 | 
				
			||||||
		let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
 | 
							let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
 | 
				
			||||||
		let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
 | 
							let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
 | 
				
			||||||
		if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
 | 
							if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
 | 
				
			||||||
@ -179,6 +180,10 @@ impl Engine for BasicAuthority {
 | 
				
			|||||||
		t.sender().map(|_|()) // Perform EC recovery and cache sender
 | 
							t.sender().map(|_|()) // Perform EC recovery and cache sender
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn register_client(&self, client: Weak<Client>) {
 | 
				
			||||||
 | 
							self.validators.register_call_contract(client);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn set_signer(&self, _address: Address, password: String) {
 | 
						fn set_signer(&self, _address: Address, password: String) {
 | 
				
			||||||
		*self.password.write() = Some(password);
 | 
							*self.password.write() = Some(password);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -21,6 +21,7 @@ mod instant_seal;
 | 
				
			|||||||
mod basic_authority;
 | 
					mod basic_authority;
 | 
				
			||||||
mod authority_round;
 | 
					mod authority_round;
 | 
				
			||||||
mod tendermint;
 | 
					mod tendermint;
 | 
				
			||||||
 | 
					mod validator_set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub use self::null_engine::NullEngine;
 | 
					pub use self::null_engine::NullEngine;
 | 
				
			||||||
pub use self::instant_seal::InstantSeal;
 | 
					pub use self::instant_seal::InstantSeal;
 | 
				
			||||||
@ -28,6 +29,7 @@ pub use self::basic_authority::BasicAuthority;
 | 
				
			|||||||
pub use self::authority_round::AuthorityRound;
 | 
					pub use self::authority_round::AuthorityRound;
 | 
				
			||||||
pub use self::tendermint::Tendermint;
 | 
					pub use self::tendermint::Tendermint;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::sync::Weak;
 | 
				
			||||||
use util::*;
 | 
					use util::*;
 | 
				
			||||||
use account_provider::AccountProvider;
 | 
					use account_provider::AccountProvider;
 | 
				
			||||||
use block::ExecutedBlock;
 | 
					use block::ExecutedBlock;
 | 
				
			||||||
@ -36,13 +38,12 @@ use env_info::EnvInfo;
 | 
				
			|||||||
use error::Error;
 | 
					use error::Error;
 | 
				
			||||||
use spec::CommonParams;
 | 
					use spec::CommonParams;
 | 
				
			||||||
use evm::Schedule;
 | 
					use evm::Schedule;
 | 
				
			||||||
use io::IoChannel;
 | 
					 | 
				
			||||||
use service::ClientIoMessage;
 | 
					 | 
				
			||||||
use header::Header;
 | 
					use header::Header;
 | 
				
			||||||
use transaction::SignedTransaction;
 | 
					use transaction::SignedTransaction;
 | 
				
			||||||
use ethereum::ethash;
 | 
					use ethereum::ethash;
 | 
				
			||||||
use blockchain::extras::BlockDetails;
 | 
					use blockchain::extras::BlockDetails;
 | 
				
			||||||
use views::HeaderView;
 | 
					use views::HeaderView;
 | 
				
			||||||
 | 
					use client::Client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Voting errors.
 | 
					/// Voting errors.
 | 
				
			||||||
#[derive(Debug)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
@ -207,14 +208,15 @@ pub trait Engine : Sync + Send {
 | 
				
			|||||||
	/// Register an account which signs consensus messages.
 | 
						/// Register an account which signs consensus messages.
 | 
				
			||||||
	fn set_signer(&self, _address: Address, _password: String) {}
 | 
						fn set_signer(&self, _address: Address, _password: String) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Stops any services that the may hold the Engine and makes it safe to drop.
 | 
						/// Add Client which can be used for sealing, querying the state and sending messages.
 | 
				
			||||||
	fn stop(&self) {}
 | 
						fn register_client(&self, _client: Weak<Client>) {}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	/// Add a channel for communication with Client which can be used for sealing.
 | 
					 | 
				
			||||||
	fn register_message_channel(&self, _message_channel: IoChannel<ClientIoMessage>) {}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Add an account provider useful for Engines that sign stuff.
 | 
						/// Add an account provider useful for Engines that sign stuff.
 | 
				
			||||||
	fn register_account_provider(&self, _account_provider: Arc<AccountProvider>) {}
 | 
						fn register_account_provider(&self, _account_provider: Arc<AccountProvider>) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Trigger next step of the consensus engine.
 | 
						/// Trigger next step of the consensus engine.
 | 
				
			||||||
	fn step(&self) {}
 | 
						fn step(&self) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Stops any services that the may hold the Engine and makes it safe to drop.
 | 
				
			||||||
 | 
						fn stop(&self) {}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -27,8 +27,10 @@ mod transition;
 | 
				
			|||||||
mod params;
 | 
					mod params;
 | 
				
			||||||
mod vote_collector;
 | 
					mod vote_collector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::sync::Weak;
 | 
				
			||||||
use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
 | 
					use std::sync::atomic::{AtomicUsize, Ordering as AtomicOrdering};
 | 
				
			||||||
use util::*;
 | 
					use util::*;
 | 
				
			||||||
 | 
					use client::{Client, EngineClient};
 | 
				
			||||||
use error::{Error, BlockError};
 | 
					use error::{Error, BlockError};
 | 
				
			||||||
use header::Header;
 | 
					use header::Header;
 | 
				
			||||||
use builtin::Builtin;
 | 
					use builtin::Builtin;
 | 
				
			||||||
@ -43,9 +45,9 @@ use engines::{Engine, Seal, EngineError};
 | 
				
			|||||||
use blockchain::extras::BlockDetails;
 | 
					use blockchain::extras::BlockDetails;
 | 
				
			||||||
use views::HeaderView;
 | 
					use views::HeaderView;
 | 
				
			||||||
use evm::Schedule;
 | 
					use evm::Schedule;
 | 
				
			||||||
use io::{IoService, IoChannel};
 | 
					 | 
				
			||||||
use service::ClientIoMessage;
 | 
					 | 
				
			||||||
use state::CleanupMode;
 | 
					use state::CleanupMode;
 | 
				
			||||||
 | 
					use io::IoService;
 | 
				
			||||||
 | 
					use super::validator_set::{ValidatorSet, new_validator_set};
 | 
				
			||||||
use self::message::*;
 | 
					use self::message::*;
 | 
				
			||||||
use self::transition::TransitionHandler;
 | 
					use self::transition::TransitionHandler;
 | 
				
			||||||
use self::params::TendermintParams;
 | 
					use self::params::TendermintParams;
 | 
				
			||||||
@ -75,9 +77,11 @@ pub type BlockHash = H256;
 | 
				
			|||||||
/// Engine using `Tendermint` consensus algorithm, suitable for EVM chain.
 | 
					/// Engine using `Tendermint` consensus algorithm, suitable for EVM chain.
 | 
				
			||||||
pub struct Tendermint {
 | 
					pub struct Tendermint {
 | 
				
			||||||
	params: CommonParams,
 | 
						params: CommonParams,
 | 
				
			||||||
	our_params: TendermintParams,
 | 
						gas_limit_bound_divisor: U256,
 | 
				
			||||||
	builtins: BTreeMap<Address, Builtin>,
 | 
						builtins: BTreeMap<Address, Builtin>,
 | 
				
			||||||
	step_service: IoService<Step>,
 | 
						step_service: IoService<Step>,
 | 
				
			||||||
 | 
						client: RwLock<Option<Weak<EngineClient>>>,
 | 
				
			||||||
 | 
						block_reward: U256,
 | 
				
			||||||
	/// Address to be used as authority.
 | 
						/// Address to be used as authority.
 | 
				
			||||||
	authority: RwLock<Address>,
 | 
						authority: RwLock<Address>,
 | 
				
			||||||
	/// Password used for signing messages.
 | 
						/// Password used for signing messages.
 | 
				
			||||||
@ -90,8 +94,6 @@ pub struct Tendermint {
 | 
				
			|||||||
	step: RwLock<Step>,
 | 
						step: RwLock<Step>,
 | 
				
			||||||
	/// Vote accumulator.
 | 
						/// Vote accumulator.
 | 
				
			||||||
	votes: VoteCollector,
 | 
						votes: VoteCollector,
 | 
				
			||||||
	/// Channel for updating the sealing.
 | 
					 | 
				
			||||||
	message_channel: Mutex<Option<IoChannel<ClientIoMessage>>>,
 | 
					 | 
				
			||||||
	/// Used to sign messages and proposals.
 | 
						/// Used to sign messages and proposals.
 | 
				
			||||||
	account_provider: Mutex<Option<Arc<AccountProvider>>>,
 | 
						account_provider: Mutex<Option<Arc<AccountProvider>>>,
 | 
				
			||||||
	/// Message for the last PoLC.
 | 
						/// Message for the last PoLC.
 | 
				
			||||||
@ -100,6 +102,8 @@ pub struct Tendermint {
 | 
				
			|||||||
	last_lock: AtomicUsize,
 | 
						last_lock: AtomicUsize,
 | 
				
			||||||
	/// Bare hash of the proposed block, used for seal submission.
 | 
						/// Bare hash of the proposed block, used for seal submission.
 | 
				
			||||||
	proposal: RwLock<Option<H256>>,
 | 
						proposal: RwLock<Option<H256>>,
 | 
				
			||||||
 | 
						/// Set used to determine the current validators.
 | 
				
			||||||
 | 
						validators: Box<ValidatorSet + Send + Sync>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Tendermint {
 | 
					impl Tendermint {
 | 
				
			||||||
@ -108,53 +112,49 @@ impl Tendermint {
 | 
				
			|||||||
		let engine = Arc::new(
 | 
							let engine = Arc::new(
 | 
				
			||||||
			Tendermint {
 | 
								Tendermint {
 | 
				
			||||||
				params: params,
 | 
									params: params,
 | 
				
			||||||
				our_params: our_params,
 | 
									gas_limit_bound_divisor: our_params.gas_limit_bound_divisor,
 | 
				
			||||||
				builtins: builtins,
 | 
									builtins: builtins,
 | 
				
			||||||
 | 
									client: RwLock::new(None),
 | 
				
			||||||
				step_service: IoService::<Step>::start()?,
 | 
									step_service: IoService::<Step>::start()?,
 | 
				
			||||||
 | 
									block_reward: our_params.block_reward,
 | 
				
			||||||
				authority: RwLock::new(Address::default()),
 | 
									authority: RwLock::new(Address::default()),
 | 
				
			||||||
				password: RwLock::new(None),
 | 
									password: RwLock::new(None),
 | 
				
			||||||
				height: AtomicUsize::new(1),
 | 
									height: AtomicUsize::new(1),
 | 
				
			||||||
				round: AtomicUsize::new(0),
 | 
									round: AtomicUsize::new(0),
 | 
				
			||||||
				step: RwLock::new(Step::Propose),
 | 
									step: RwLock::new(Step::Propose),
 | 
				
			||||||
				votes: VoteCollector::new(),
 | 
									votes: VoteCollector::new(),
 | 
				
			||||||
				message_channel: Mutex::new(None),
 | 
					 | 
				
			||||||
				account_provider: Mutex::new(None),
 | 
									account_provider: Mutex::new(None),
 | 
				
			||||||
				lock_change: RwLock::new(None),
 | 
									lock_change: RwLock::new(None),
 | 
				
			||||||
				last_lock: AtomicUsize::new(0),
 | 
									last_lock: AtomicUsize::new(0),
 | 
				
			||||||
				proposal: RwLock::new(None),
 | 
									proposal: RwLock::new(None),
 | 
				
			||||||
 | 
									validators: new_validator_set(our_params.validators),
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		let handler = TransitionHandler { engine: Arc::downgrade(&engine) };
 | 
							let handler = TransitionHandler::new(Arc::downgrade(&engine), our_params.timeouts);
 | 
				
			||||||
		engine.step_service.register_handler(Arc::new(handler))?;
 | 
							engine.step_service.register_handler(Arc::new(handler))?;
 | 
				
			||||||
		Ok(engine)
 | 
							Ok(engine)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn update_sealing(&self) {
 | 
						fn update_sealing(&self) {
 | 
				
			||||||
		if let Some(ref channel) = *self.message_channel.lock() {
 | 
							if let Some(ref weak) = *self.client.read() {
 | 
				
			||||||
			match channel.send(ClientIoMessage::UpdateSealing) {
 | 
								if let Some(c) = weak.upgrade() {
 | 
				
			||||||
				Ok(_) => trace!(target: "poa", "UpdateSealing message sent."),
 | 
									c.update_sealing();
 | 
				
			||||||
				Err(err) => warn!(target: "poa", "Could not send a sealing message {}.", err),
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
 | 
						fn submit_seal(&self, block_hash: H256, seal: Vec<Bytes>) {
 | 
				
			||||||
		if let Some(ref channel) = *self.message_channel.lock() {
 | 
							if let Some(ref weak) = *self.client.read() {
 | 
				
			||||||
			match channel.send(ClientIoMessage::SubmitSeal(block_hash, seal)) {
 | 
								if let Some(c) = weak.upgrade() {
 | 
				
			||||||
				Ok(_) => trace!(target: "poa", "SubmitSeal message sent."),
 | 
									c.submit_seal(block_hash, seal);
 | 
				
			||||||
				Err(err) => warn!(target: "poa", "Could not send a sealing message {}.", err),
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn broadcast_message(&self, message: Bytes) {
 | 
						fn broadcast_message(&self, message: Bytes) {
 | 
				
			||||||
		let channel = self.message_channel.lock().clone();
 | 
							if let Some(ref weak) = *self.client.read() {
 | 
				
			||||||
		if let Some(ref channel) = channel {
 | 
								if let Some(c) = weak.upgrade() {
 | 
				
			||||||
			match channel.send(ClientIoMessage::BroadcastMessage(message)) {
 | 
									c.broadcast_consensus_message(message);
 | 
				
			||||||
				Ok(_) => trace!(target: "poa", "BroadcastMessage message sent."),
 | 
					 | 
				
			||||||
				Err(err) => warn!(target: "poa", "broadcast_message: Could not send a sealing message {}.", err),
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			warn!(target: "poa", "broadcast_message: No IoChannel available.");
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -265,23 +265,22 @@ impl Tendermint {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn is_authority(&self, address: &Address) -> bool {
 | 
						fn is_authority(&self, address: &Address) -> bool {
 | 
				
			||||||
		self.our_params.authorities.contains(address)
 | 
							self.validators.contains(address)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn is_above_threshold(&self, n: usize) -> bool {
 | 
						fn is_above_threshold(&self, n: usize) -> bool {
 | 
				
			||||||
		n > self.our_params.authority_n * 2/3
 | 
							n > self.validators.count() * 2/3
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Check if address is a proposer for given round.
 | 
						/// Check if address is a proposer for given round.
 | 
				
			||||||
	fn is_round_proposer(&self, height: Height, round: Round, address: &Address) -> Result<(), EngineError> {
 | 
						fn is_round_proposer(&self, height: Height, round: Round, address: &Address) -> Result<(), EngineError> {
 | 
				
			||||||
		let ref p = self.our_params;
 | 
					 | 
				
			||||||
		let proposer_nonce = height + round;
 | 
							let proposer_nonce = height + round;
 | 
				
			||||||
		trace!(target: "poa", "is_proposer: Proposer nonce: {}", proposer_nonce);
 | 
							trace!(target: "poa", "is_proposer: Proposer nonce: {}", proposer_nonce);
 | 
				
			||||||
		let proposer = p.authorities.get(proposer_nonce % p.authority_n).expect("There are authority_n authorities; taking number modulo authority_n gives number in authority_n range; qed");
 | 
							let proposer = self.validators.get(proposer_nonce);
 | 
				
			||||||
		if proposer == address {
 | 
							if proposer == *address {
 | 
				
			||||||
			Ok(())
 | 
								Ok(())
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			Err(EngineError::NotProposer(Mismatch { expected: proposer.clone(), found: address.clone() }))
 | 
								Err(EngineError::NotProposer(Mismatch { expected: proposer, found: address.clone() }))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -405,7 +404,7 @@ impl Engine for Tendermint {
 | 
				
			|||||||
		header.set_difficulty(parent.difficulty().clone());
 | 
							header.set_difficulty(parent.difficulty().clone());
 | 
				
			||||||
		header.set_gas_limit({
 | 
							header.set_gas_limit({
 | 
				
			||||||
			let gas_limit = parent.gas_limit().clone();
 | 
								let gas_limit = parent.gas_limit().clone();
 | 
				
			||||||
			let bound_divisor = self.our_params.gas_limit_bound_divisor;
 | 
								let bound_divisor = self.gas_limit_bound_divisor;
 | 
				
			||||||
			if gas_limit < gas_floor_target {
 | 
								if gas_limit < gas_floor_target {
 | 
				
			||||||
				min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
 | 
									min(gas_floor_target, gas_limit + gas_limit / bound_divisor - 1.into())
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
@ -472,12 +471,9 @@ impl Engine for Tendermint {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	/// Apply the block reward on finalisation of the block.
 | 
						/// Apply the block reward on finalisation of the block.
 | 
				
			||||||
	fn on_close_block(&self, block: &mut ExecutedBlock) {
 | 
						fn on_close_block(&self, block: &mut ExecutedBlock) {
 | 
				
			||||||
		let reward = self.our_params.block_reward;
 | 
					 | 
				
			||||||
		let fields = block.fields_mut();
 | 
							let fields = block.fields_mut();
 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Bestow block reward
 | 
							// Bestow block reward
 | 
				
			||||||
		fields.state.add_balance(fields.header.author(), &reward, CleanupMode::NoEmpty);
 | 
							fields.state.add_balance(fields.header.author(), &self.block_reward, CleanupMode::NoEmpty);
 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Commit state so that we can actually figure out the state root.
 | 
							// Commit state so that we can actually figure out the state root.
 | 
				
			||||||
		if let Err(e) = fields.state.commit() {
 | 
							if let Err(e) = fields.state.commit() {
 | 
				
			||||||
			warn!("Encountered error on state commit: {}", e);
 | 
								warn!("Encountered error on state commit: {}", e);
 | 
				
			||||||
@ -522,7 +518,7 @@ impl Engine for Tendermint {
 | 
				
			|||||||
				Some(a) => a,
 | 
									Some(a) => a,
 | 
				
			||||||
				None => public_to_address(&recover(&precommit.signature.into(), &precommit_hash)?),
 | 
									None => public_to_address(&recover(&precommit.signature.into(), &precommit_hash)?),
 | 
				
			||||||
			};
 | 
								};
 | 
				
			||||||
			if !self.our_params.authorities.contains(&address) {
 | 
								if !self.validators.contains(&address) {
 | 
				
			||||||
				Err(EngineError::NotAuthorized(address.to_owned()))?
 | 
									Err(EngineError::NotAuthorized(address.to_owned()))?
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -555,7 +551,7 @@ impl Engine for Tendermint {
 | 
				
			|||||||
			Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))?;
 | 
								Err(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))?;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let gas_limit_divisor = self.our_params.gas_limit_bound_divisor;
 | 
							let gas_limit_divisor = self.gas_limit_bound_divisor;
 | 
				
			||||||
		let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
 | 
							let min_gas = parent.gas_limit().clone() - parent.gas_limit().clone() / gas_limit_divisor;
 | 
				
			||||||
		let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
 | 
							let max_gas = parent.gas_limit().clone() + parent.gas_limit().clone() / gas_limit_divisor;
 | 
				
			||||||
		if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
 | 
							if header.gas_limit() <= &min_gas || header.gas_limit() >= &max_gas {
 | 
				
			||||||
@ -658,9 +654,9 @@ impl Engine for Tendermint {
 | 
				
			|||||||
		self.to_step(next_step);
 | 
							self.to_step(next_step);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn register_message_channel(&self, message_channel: IoChannel<ClientIoMessage>) {
 | 
						fn register_client(&self, client: Weak<Client>) {
 | 
				
			||||||
		trace!(target: "poa", "Register the IoChannel.");
 | 
							*self.client.write() = Some(client.clone());
 | 
				
			||||||
		*self.message_channel.lock() = Some(message_channel);
 | 
							self.validators.register_call_contract(client);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn register_account_provider(&self, account_provider: Arc<AccountProvider>) {
 | 
						fn register_account_provider(&self, account_provider: Arc<AccountProvider>) {
 | 
				
			||||||
@ -671,21 +667,20 @@ impl Engine for Tendermint {
 | 
				
			|||||||
#[cfg(test)]
 | 
					#[cfg(test)]
 | 
				
			||||||
mod tests {
 | 
					mod tests {
 | 
				
			||||||
	use util::*;
 | 
						use util::*;
 | 
				
			||||||
	use io::{IoContext, IoHandler};
 | 
					 | 
				
			||||||
	use block::*;
 | 
						use block::*;
 | 
				
			||||||
	use error::{Error, BlockError};
 | 
						use error::{Error, BlockError};
 | 
				
			||||||
	use header::Header;
 | 
						use header::Header;
 | 
				
			||||||
	use io::IoChannel;
 | 
					 | 
				
			||||||
	use env_info::EnvInfo;
 | 
						use env_info::EnvInfo;
 | 
				
			||||||
 | 
						use client::chain_notify::ChainNotify;
 | 
				
			||||||
 | 
						use miner::MinerService;
 | 
				
			||||||
	use tests::helpers::*;
 | 
						use tests::helpers::*;
 | 
				
			||||||
	use account_provider::AccountProvider;
 | 
						use account_provider::AccountProvider;
 | 
				
			||||||
	use service::ClientIoMessage;
 | 
					 | 
				
			||||||
	use spec::Spec;
 | 
						use spec::Spec;
 | 
				
			||||||
	use engines::{Engine, EngineError, Seal};
 | 
						use engines::{Engine, EngineError, Seal};
 | 
				
			||||||
	use super::*;
 | 
						use super::*;
 | 
				
			||||||
	use super::message::*;
 | 
						use super::message::*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Accounts inserted with "0" and "1" are authorities. First proposer is "0".
 | 
						/// Accounts inserted with "0" and "1" are validators. First proposer is "0".
 | 
				
			||||||
	fn setup() -> (Spec, Arc<AccountProvider>) {
 | 
						fn setup() -> (Spec, Arc<AccountProvider>) {
 | 
				
			||||||
		let tap = Arc::new(AccountProvider::transient_provider());
 | 
							let tap = Arc::new(AccountProvider::transient_provider());
 | 
				
			||||||
		let spec = Spec::new_test_tendermint();
 | 
							let spec = Spec::new_test_tendermint();
 | 
				
			||||||
@ -693,13 +688,13 @@ mod tests {
 | 
				
			|||||||
		(spec, tap)
 | 
							(spec, tap)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn propose_default(spec: &Spec, proposer: Address) -> (LockedBlock, Vec<Bytes>) {
 | 
						fn propose_default(spec: &Spec, proposer: Address) -> (ClosedBlock, Vec<Bytes>) {
 | 
				
			||||||
		let mut db_result = get_temp_state_db();
 | 
							let mut db_result = get_temp_state_db();
 | 
				
			||||||
		let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
 | 
							let db = spec.ensure_db_good(db_result.take(), &Default::default()).unwrap();
 | 
				
			||||||
		let genesis_header = spec.genesis_header();
 | 
							let genesis_header = spec.genesis_header();
 | 
				
			||||||
		let last_hashes = Arc::new(vec![genesis_header.hash()]);
 | 
							let last_hashes = Arc::new(vec![genesis_header.hash()]);
 | 
				
			||||||
		let b = OpenBlock::new(spec.engine.as_ref(), Default::default(), false, db.boxed_clone(), &genesis_header, last_hashes, proposer, (3141562.into(), 31415620.into()), vec![]).unwrap();
 | 
							let b = OpenBlock::new(spec.engine.as_ref(), Default::default(), false, db.boxed_clone(), &genesis_header, last_hashes, proposer, (3141562.into(), 31415620.into()), vec![]).unwrap();
 | 
				
			||||||
		let b = b.close_and_lock();
 | 
							let b = b.close();
 | 
				
			||||||
		if let Seal::Proposal(seal) = spec.engine.generate_seal(b.block()) {
 | 
							if let Seal::Proposal(seal) = spec.engine.generate_seal(b.block()) {
 | 
				
			||||||
			(b, seal)
 | 
								(b, seal)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
@ -707,7 +702,7 @@ mod tests {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn vote<F>(engine: &Arc<Engine>, signer: F, height: usize, round: usize, step: Step, block_hash: Option<H256>) -> Bytes where F: FnOnce(H256) -> Result<H520, ::account_provider::Error> {
 | 
						fn vote<F>(engine: &Engine, signer: F, height: usize, round: usize, step: Step, block_hash: Option<H256>) -> Bytes where F: FnOnce(H256) -> Result<H520, ::account_provider::Error> {
 | 
				
			||||||
		let mi = message_info_rlp(height, round, step, block_hash);
 | 
							let mi = message_info_rlp(height, round, step, block_hash);
 | 
				
			||||||
		let m = message_full_rlp(&signer(mi.sha3()).unwrap().into(), &mi);
 | 
							let m = message_full_rlp(&signer(mi.sha3()).unwrap().into(), &mi);
 | 
				
			||||||
		engine.handle_message(&m).unwrap();
 | 
							engine.handle_message(&m).unwrap();
 | 
				
			||||||
@ -725,37 +720,26 @@ mod tests {
 | 
				
			|||||||
		]
 | 
							]
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn precommit_signatures(tap: &Arc<AccountProvider>, height: Height, round: Round, bare_hash: Option<H256>, v1: H160, v2: H160) -> Bytes {
 | 
					 | 
				
			||||||
		let vote_info = message_info_rlp(height, round, Step::Precommit, bare_hash);
 | 
					 | 
				
			||||||
		::rlp::encode(&vec![
 | 
					 | 
				
			||||||
			H520::from(tap.sign(v1, None, vote_info.sha3()).unwrap()),
 | 
					 | 
				
			||||||
			H520::from(tap.sign(v2, None, vote_info.sha3()).unwrap())
 | 
					 | 
				
			||||||
		]).to_vec()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	fn insert_and_unlock(tap: &Arc<AccountProvider>, acc: &str) -> Address {
 | 
						fn insert_and_unlock(tap: &Arc<AccountProvider>, acc: &str) -> Address {
 | 
				
			||||||
		let addr = tap.insert_account(acc.sha3(), acc).unwrap();
 | 
							let addr = tap.insert_account(acc.sha3(), acc).unwrap();
 | 
				
			||||||
		tap.unlock_account_permanently(addr, acc.into()).unwrap();
 | 
							tap.unlock_account_permanently(addr, acc.into()).unwrap();
 | 
				
			||||||
		addr
 | 
							addr
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn insert_and_register(tap: &Arc<AccountProvider>, engine: &Arc<Engine>, acc: &str) -> Address {
 | 
						fn insert_and_register(tap: &Arc<AccountProvider>, engine: &Engine, acc: &str) -> Address {
 | 
				
			||||||
		let addr = insert_and_unlock(tap, acc);
 | 
							let addr = insert_and_unlock(tap, acc);
 | 
				
			||||||
		engine.set_signer(addr.clone(), acc.into());
 | 
							engine.set_signer(addr.clone(), acc.into());
 | 
				
			||||||
		addr
 | 
							addr
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct TestIo {
 | 
						#[derive(Default)]
 | 
				
			||||||
		received: RwLock<Vec<ClientIoMessage>>
 | 
						struct TestNotify {
 | 
				
			||||||
 | 
							messages: RwLock<Vec<Bytes>>,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	impl TestIo {
 | 
						impl ChainNotify for TestNotify {
 | 
				
			||||||
		fn new() -> Arc<Self> { Arc::new(TestIo { received: RwLock::new(Vec::new()) }) }
 | 
							fn broadcast(&self, data: Vec<u8>) {
 | 
				
			||||||
	}
 | 
								self.messages.write().push(data);
 | 
				
			||||||
 | 
					 | 
				
			||||||
	impl IoHandler<ClientIoMessage> for TestIo {
 | 
					 | 
				
			||||||
		fn message(&self, _io: &IoContext<ClientIoMessage>, net_message: &ClientIoMessage) {
 | 
					 | 
				
			||||||
			self.received.write().push(net_message.clone());
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -879,10 +863,10 @@ mod tests {
 | 
				
			|||||||
	fn can_generate_seal() {
 | 
						fn can_generate_seal() {
 | 
				
			||||||
		let (spec, tap) = setup();
 | 
							let (spec, tap) = setup();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let proposer = insert_and_register(&tap, &spec.engine, "1");
 | 
							let proposer = insert_and_register(&tap, spec.engine.as_ref(), "1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let (b, seal) = propose_default(&spec, proposer);
 | 
							let (b, seal) = propose_default(&spec, proposer);
 | 
				
			||||||
		assert!(b.try_seal(spec.engine.as_ref(), seal).is_ok());
 | 
							assert!(b.lock().try_seal(spec.engine.as_ref(), seal).is_ok());
 | 
				
			||||||
		spec.engine.stop();
 | 
							spec.engine.stop();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -890,10 +874,10 @@ mod tests {
 | 
				
			|||||||
	fn can_recognize_proposal() {
 | 
						fn can_recognize_proposal() {
 | 
				
			||||||
		let (spec, tap) = setup();
 | 
							let (spec, tap) = setup();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let proposer = insert_and_register(&tap, &spec.engine, "1");
 | 
							let proposer = insert_and_register(&tap, spec.engine.as_ref(), "1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let (b, seal) = propose_default(&spec, proposer);
 | 
							let (b, seal) = propose_default(&spec, proposer);
 | 
				
			||||||
		let sealed = b.seal(spec.engine.as_ref(), seal).unwrap();
 | 
							let sealed = b.lock().seal(spec.engine.as_ref(), seal).unwrap();
 | 
				
			||||||
		assert!(spec.engine.is_proposal(sealed.header()));
 | 
							assert!(spec.engine.is_proposal(sealed.header()));
 | 
				
			||||||
		spec.engine.stop();
 | 
							spec.engine.stop();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -903,8 +887,8 @@ mod tests {
 | 
				
			|||||||
		let (spec, tap) = setup();
 | 
							let (spec, tap) = setup();
 | 
				
			||||||
		let engine = spec.engine.clone();
 | 
							let engine = spec.engine.clone();
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		let v0 = insert_and_register(&tap, &engine, "0");
 | 
							let v0 = insert_and_register(&tap, engine.as_ref(), "0");
 | 
				
			||||||
		let v1 = insert_and_register(&tap, &engine, "1");
 | 
							let v1 = insert_and_register(&tap, engine.as_ref(), "1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let h = 0;
 | 
							let h = 0;
 | 
				
			||||||
		let r = 0;
 | 
							let r = 0;
 | 
				
			||||||
@ -913,57 +897,74 @@ mod tests {
 | 
				
			|||||||
		let (b, _) = propose_default(&spec, v1.clone());
 | 
							let (b, _) = propose_default(&spec, v1.clone());
 | 
				
			||||||
		let proposal = Some(b.header().bare_hash());
 | 
							let proposal = Some(b.header().bare_hash());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Register IoHandler remembers messages.
 | 
							let client = generate_dummy_client(0);
 | 
				
			||||||
		let test_io = TestIo::new();
 | 
							let notify = Arc::new(TestNotify::default());
 | 
				
			||||||
		let channel = IoChannel::to_handler(Arc::downgrade(&(test_io.clone() as Arc<IoHandler<ClientIoMessage>>)));
 | 
							client.add_notify(notify.clone());
 | 
				
			||||||
		engine.register_message_channel(channel);
 | 
							engine.register_client(Arc::downgrade(&client));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let prevote_current = vote(&engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
 | 
							let prevote_current = vote(engine.as_ref(), |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let precommit_current = vote(&engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
 | 
							let precommit_current = vote(engine.as_ref(), |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let prevote_future = vote(&engine, |mh| tap.sign(v0, None, mh).map(H520::from), h + 1, r, Step::Prevote, proposal);
 | 
							let prevote_future = vote(engine.as_ref(), |mh| tap.sign(v0, None, mh).map(H520::from), h + 1, r, Step::Prevote, proposal);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Relays all valid present and future messages.
 | 
							// Relays all valid present and future messages.
 | 
				
			||||||
		assert!(test_io.received.read().contains(&ClientIoMessage::BroadcastMessage(prevote_current)));
 | 
							assert!(notify.messages.read().contains(&prevote_current));
 | 
				
			||||||
		assert!(test_io.received.read().contains(&ClientIoMessage::BroadcastMessage(precommit_current)));
 | 
							assert!(notify.messages.read().contains(&precommit_current));
 | 
				
			||||||
		assert!(test_io.received.read().contains(&ClientIoMessage::BroadcastMessage(prevote_future)));
 | 
							assert!(notify.messages.read().contains(&prevote_future));
 | 
				
			||||||
		engine.stop();
 | 
							engine.stop();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	#[test]
 | 
						#[test]
 | 
				
			||||||
	fn seal_submission() {
 | 
						fn seal_submission() {
 | 
				
			||||||
		let (spec, tap) = setup();
 | 
							use ethkey::{Generator, Random};
 | 
				
			||||||
		let engine = spec.engine.clone();
 | 
							use types::transaction::{Transaction, Action};
 | 
				
			||||||
		
 | 
							use client::BlockChainClient;
 | 
				
			||||||
		let v0 = insert_and_register(&tap, &engine, "0");
 | 
					
 | 
				
			||||||
		let v1 = insert_and_register(&tap, &engine, "1");
 | 
							let client = generate_dummy_client_with_spec_and_data(Spec::new_test_tendermint, 0, 0, &[]);
 | 
				
			||||||
 | 
							let engine = client.engine();
 | 
				
			||||||
 | 
							let tap = Arc::new(AccountProvider::transient_provider());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Accounts for signing votes.
 | 
				
			||||||
 | 
							let v0 = insert_and_unlock(&tap, "0");
 | 
				
			||||||
 | 
							let v1 = insert_and_unlock(&tap, "1");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let notify = Arc::new(TestNotify::default());
 | 
				
			||||||
 | 
							engine.register_account_provider(tap.clone());
 | 
				
			||||||
 | 
							client.add_notify(notify.clone());
 | 
				
			||||||
 | 
							engine.register_client(Arc::downgrade(&client));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let keypair = Random.generate().unwrap();
 | 
				
			||||||
 | 
							let transaction = Transaction {
 | 
				
			||||||
 | 
								action: Action::Create,
 | 
				
			||||||
 | 
								value: U256::zero(),
 | 
				
			||||||
 | 
								data: "3331600055".from_hex().unwrap(),
 | 
				
			||||||
 | 
								gas: U256::from(100_000),
 | 
				
			||||||
 | 
								gas_price: U256::zero(),
 | 
				
			||||||
 | 
								nonce: U256::zero(),
 | 
				
			||||||
 | 
							}.sign(keypair.secret(), None);
 | 
				
			||||||
 | 
							client.miner().import_own_transaction(client.as_ref(), transaction.into()).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							client.miner().set_engine_signer(v1.clone(), "1".into()).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Propose
 | 
				
			||||||
 | 
							let proposal = Some(client.miner().pending_block().unwrap().header.bare_hash());
 | 
				
			||||||
 | 
							// Propose timeout
 | 
				
			||||||
 | 
							engine.step();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let h = 1;
 | 
							let h = 1;
 | 
				
			||||||
		let r = 0;
 | 
							let r = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Register IoHandler remembers messages.
 | 
					 | 
				
			||||||
		let test_io = TestIo::new();
 | 
					 | 
				
			||||||
		let channel = IoChannel::to_handler(Arc::downgrade(&(test_io.clone() as Arc<IoHandler<ClientIoMessage>>)));
 | 
					 | 
				
			||||||
		engine.register_message_channel(channel);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Propose
 | 
					 | 
				
			||||||
		let (b, mut seal) = propose_default(&spec, v1.clone());
 | 
					 | 
				
			||||||
		let proposal = Some(b.header().bare_hash());
 | 
					 | 
				
			||||||
		engine.step();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Prevote.
 | 
							// Prevote.
 | 
				
			||||||
		vote(&engine, |mh| tap.sign(v1, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
 | 
							vote(engine, |mh| tap.sign(v1, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
 | 
				
			||||||
		vote(&engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
 | 
							vote(engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Prevote, proposal);
 | 
				
			||||||
		vote(&engine, |mh| tap.sign(v1, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
 | 
							vote(engine, |mh| tap.sign(v1, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
 | 
				
			||||||
		vote(&engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		seal[2] = precommit_signatures(&tap, h, r, Some(b.header().bare_hash()), v1, v0);
 | 
							assert_eq!(client.chain_info().best_block_number, 0);
 | 
				
			||||||
		let first = test_io.received.read().contains(&ClientIoMessage::SubmitSeal(proposal.unwrap(), seal.clone()));
 | 
							// Last precommit.
 | 
				
			||||||
		seal[2] = precommit_signatures(&tap, h, r, Some(b.header().bare_hash()), v0, v1);
 | 
							vote(engine, |mh| tap.sign(v0, None, mh).map(H520::from), h, r, Step::Precommit, proposal);
 | 
				
			||||||
		let second = test_io.received.read().contains(&ClientIoMessage::SubmitSeal(proposal.unwrap(), seal));
 | 
							assert_eq!(client.chain_info().best_block_number, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		assert!(first ^ second);
 | 
					 | 
				
			||||||
		engine.stop();
 | 
							engine.stop();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -18,38 +18,22 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use ethjson;
 | 
					use ethjson;
 | 
				
			||||||
use super::transition::TendermintTimeouts;
 | 
					use super::transition::TendermintTimeouts;
 | 
				
			||||||
use util::{Address, Uint, U256};
 | 
					use util::{U256, Uint};
 | 
				
			||||||
use time::Duration;
 | 
					use time::Duration;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// `Tendermint` params.
 | 
					/// `Tendermint` params.
 | 
				
			||||||
#[derive(Debug, Clone)]
 | 
					#[derive(Debug)]
 | 
				
			||||||
pub struct TendermintParams {
 | 
					pub struct TendermintParams {
 | 
				
			||||||
	/// Gas limit divisor.
 | 
						/// Gas limit divisor.
 | 
				
			||||||
	pub gas_limit_bound_divisor: U256,
 | 
						pub gas_limit_bound_divisor: U256,
 | 
				
			||||||
	/// List of authorities.
 | 
						/// List of validators.
 | 
				
			||||||
	pub authorities: Vec<Address>,
 | 
						pub validators: ethjson::spec::ValidatorSet,
 | 
				
			||||||
	/// Number of authorities.
 | 
					 | 
				
			||||||
	pub authority_n: usize,
 | 
					 | 
				
			||||||
	/// Timeout durations for different steps.
 | 
						/// Timeout durations for different steps.
 | 
				
			||||||
	pub timeouts: TendermintTimeouts,
 | 
						pub timeouts: TendermintTimeouts,
 | 
				
			||||||
	/// Block reward.
 | 
						/// Block reward.
 | 
				
			||||||
	pub block_reward: U256,
 | 
						pub block_reward: U256,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Default for TendermintParams {
 | 
					 | 
				
			||||||
	fn default() -> Self {
 | 
					 | 
				
			||||||
		let authorities = vec!["0x7d577a597b2742b498cb5cf0c26cdcd726d39e6e".into(), "0x82a978b3f5962a5b0957d9ee9eef472ee55b42f1".into()];
 | 
					 | 
				
			||||||
		let val_n = authorities.len();
 | 
					 | 
				
			||||||
		TendermintParams {
 | 
					 | 
				
			||||||
			gas_limit_bound_divisor: 0x0400.into(),
 | 
					 | 
				
			||||||
			authorities: authorities,
 | 
					 | 
				
			||||||
			authority_n: val_n,
 | 
					 | 
				
			||||||
			block_reward: U256::zero(),
 | 
					 | 
				
			||||||
			timeouts: TendermintTimeouts::default(),
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
fn to_duration(ms: ethjson::uint::Uint) -> Duration {
 | 
					fn to_duration(ms: ethjson::uint::Uint) -> Duration {
 | 
				
			||||||
	let ms: usize = ms.into();
 | 
						let ms: usize = ms.into();
 | 
				
			||||||
	Duration::milliseconds(ms as i64)
 | 
						Duration::milliseconds(ms as i64)
 | 
				
			||||||
@ -57,13 +41,10 @@ fn to_duration(ms: ethjson::uint::Uint) -> Duration {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
impl From<ethjson::spec::TendermintParams> for TendermintParams {
 | 
					impl From<ethjson::spec::TendermintParams> for TendermintParams {
 | 
				
			||||||
	fn from(p: ethjson::spec::TendermintParams) -> Self {
 | 
						fn from(p: ethjson::spec::TendermintParams) -> Self {
 | 
				
			||||||
		let val: Vec<_> = p.authorities.into_iter().map(Into::into).collect();
 | 
					 | 
				
			||||||
		let val_n = val.len();
 | 
					 | 
				
			||||||
		let dt = TendermintTimeouts::default();
 | 
							let dt = TendermintTimeouts::default();
 | 
				
			||||||
		TendermintParams {
 | 
							TendermintParams {
 | 
				
			||||||
			gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
 | 
								gas_limit_bound_divisor: p.gas_limit_bound_divisor.into(),
 | 
				
			||||||
			authorities: val,
 | 
								validators: p.validators,
 | 
				
			||||||
			authority_n: val_n,
 | 
					 | 
				
			||||||
			timeouts: TendermintTimeouts {
 | 
								timeouts: TendermintTimeouts {
 | 
				
			||||||
				propose: p.timeout_propose.map_or(dt.propose, to_duration),
 | 
									propose: p.timeout_propose.map_or(dt.propose, to_duration),
 | 
				
			||||||
				prevote: p.timeout_prevote.map_or(dt.prevote, to_duration),
 | 
									prevote: p.timeout_prevote.map_or(dt.prevote, to_duration),
 | 
				
			||||||
 | 
				
			|||||||
@ -23,7 +23,17 @@ use super::{Tendermint, Step};
 | 
				
			|||||||
use engines::Engine;
 | 
					use engines::Engine;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct TransitionHandler {
 | 
					pub struct TransitionHandler {
 | 
				
			||||||
	pub engine: Weak<Tendermint>,
 | 
						engine: Weak<Tendermint>,
 | 
				
			||||||
 | 
						timeouts: TendermintTimeouts,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl TransitionHandler {
 | 
				
			||||||
 | 
						pub fn new(engine: Weak<Tendermint>, timeouts: TendermintTimeouts) -> Self {
 | 
				
			||||||
 | 
							TransitionHandler {
 | 
				
			||||||
 | 
								engine: engine,
 | 
				
			||||||
 | 
								timeouts: timeouts,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Base timeout of each step in ms.
 | 
					/// Base timeout of each step in ms.
 | 
				
			||||||
@ -67,9 +77,7 @@ fn set_timeout(io: &IoContext<Step>, timeout: Duration) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
impl IoHandler<Step> for TransitionHandler {
 | 
					impl IoHandler<Step> for TransitionHandler {
 | 
				
			||||||
	fn initialize(&self, io: &IoContext<Step>) {
 | 
						fn initialize(&self, io: &IoContext<Step>) {
 | 
				
			||||||
		if let Some(engine) = self.engine.upgrade() {
 | 
							set_timeout(io, self.timeouts.propose)
 | 
				
			||||||
			set_timeout(io, engine.our_params.timeouts.propose)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn timeout(&self, _io: &IoContext<Step>, timer: TimerToken) {
 | 
						fn timeout(&self, _io: &IoContext<Step>, timer: TimerToken) {
 | 
				
			||||||
@ -81,16 +89,14 @@ impl IoHandler<Step> for TransitionHandler {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn message(&self, io: &IoContext<Step>, next_step: &Step) {
 | 
						fn message(&self, io: &IoContext<Step>, next_step: &Step) {
 | 
				
			||||||
		if let Some(engine) = self.engine.upgrade() {
 | 
							if let Err(io_err) = io.clear_timer(ENGINE_TIMEOUT_TOKEN) {
 | 
				
			||||||
			if let Err(io_err) = io.clear_timer(ENGINE_TIMEOUT_TOKEN) {
 | 
								warn!(target: "poa", "Could not remove consensus timer {}.", io_err)
 | 
				
			||||||
				warn!(target: "poa", "Could not remove consensus timer {}.", io_err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			match *next_step {
 | 
					 | 
				
			||||||
				Step::Propose => set_timeout(io, engine.our_params.timeouts.propose),
 | 
					 | 
				
			||||||
				Step::Prevote => set_timeout(io, engine.our_params.timeouts.prevote),
 | 
					 | 
				
			||||||
				Step::Precommit => set_timeout(io, engine.our_params.timeouts.precommit),
 | 
					 | 
				
			||||||
				Step::Commit => set_timeout(io, engine.our_params.timeouts.commit),
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							match *next_step {
 | 
				
			||||||
 | 
								Step::Propose => set_timeout(io, self.timeouts.propose),
 | 
				
			||||||
 | 
								Step::Prevote => set_timeout(io, self.timeouts.prevote),
 | 
				
			||||||
 | 
								Step::Precommit => set_timeout(io, self.timeouts.precommit),
 | 
				
			||||||
 | 
								Step::Commit => set_timeout(io, self.timeouts.commit),
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										218
									
								
								ethcore/src/engines/validator_set/contract.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										218
									
								
								ethcore/src/engines/validator_set/contract.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,218 @@
 | 
				
			|||||||
 | 
					// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
 | 
				
			||||||
 | 
					// This file is part of Parity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Parity is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Parity is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Validator set maintained in a contract.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::sync::Weak;
 | 
				
			||||||
 | 
					use util::*;
 | 
				
			||||||
 | 
					use client::{Client, BlockChainClient};
 | 
				
			||||||
 | 
					use client::chain_notify::ChainNotify;
 | 
				
			||||||
 | 
					use super::ValidatorSet;
 | 
				
			||||||
 | 
					use super::simple_list::SimpleList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// The validator contract should have the following interface:
 | 
				
			||||||
 | 
					/// [{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}]
 | 
				
			||||||
 | 
					pub struct ValidatorContract {
 | 
				
			||||||
 | 
						address: Address,
 | 
				
			||||||
 | 
						validators: RwLock<SimpleList>,
 | 
				
			||||||
 | 
						provider: RwLock<Option<provider::Contract>>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ValidatorContract {
 | 
				
			||||||
 | 
						pub fn new(contract_address: Address) -> Self {
 | 
				
			||||||
 | 
							ValidatorContract {
 | 
				
			||||||
 | 
								address: contract_address,
 | 
				
			||||||
 | 
								validators: Default::default(),
 | 
				
			||||||
 | 
								provider: RwLock::new(None),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Queries the state and updates the set of validators.
 | 
				
			||||||
 | 
						pub fn update(&self) {
 | 
				
			||||||
 | 
							if let Some(ref provider) = *self.provider.read() {
 | 
				
			||||||
 | 
								match provider.get_validators() {
 | 
				
			||||||
 | 
									Ok(new) => {
 | 
				
			||||||
 | 
										debug!(target: "engine", "Set of validators obtained: {:?}", new);
 | 
				
			||||||
 | 
										*self.validators.write() = SimpleList::new(new);
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Err(s) => warn!(target: "engine", "Set of validators could not be updated: {}", s),
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								warn!(target: "engine", "Set of validators could not be updated: no provider contract.")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Checks validators on every block.
 | 
				
			||||||
 | 
					impl ChainNotify for ValidatorContract {
 | 
				
			||||||
 | 
						fn new_blocks(
 | 
				
			||||||
 | 
							&self,
 | 
				
			||||||
 | 
							_imported: Vec<H256>,
 | 
				
			||||||
 | 
							_: Vec<H256>,
 | 
				
			||||||
 | 
							_: Vec<H256>,
 | 
				
			||||||
 | 
							_: Vec<H256>,
 | 
				
			||||||
 | 
							_: Vec<H256>,
 | 
				
			||||||
 | 
							_: Vec<Bytes>,
 | 
				
			||||||
 | 
							_duration: u64) {
 | 
				
			||||||
 | 
							self.update();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ValidatorSet for Arc<ValidatorContract> {
 | 
				
			||||||
 | 
						fn contains(&self, address: &Address) -> bool {
 | 
				
			||||||
 | 
							self.validators.read().contains(address)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn get(&self, nonce: usize) -> Address {
 | 
				
			||||||
 | 
							self.validators.read().get(nonce).clone()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn count(&self) -> usize {
 | 
				
			||||||
 | 
							self.validators.read().count()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn register_call_contract(&self, client: Weak<Client>) {
 | 
				
			||||||
 | 
							if let Some(c) = client.upgrade() {
 | 
				
			||||||
 | 
								c.add_notify(self.clone());
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								*self.provider.write() = Some(provider::Contract::new(self.address, move |a, d| client.upgrade().ok_or("No client!".into()).and_then(|c| c.call_contract(a, d))));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							self.update();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod provider {
 | 
				
			||||||
 | 
						// Autogenerated from JSON contract definition using Rust contract convertor.
 | 
				
			||||||
 | 
						#![allow(unused_imports)]
 | 
				
			||||||
 | 
						use std::string::String;
 | 
				
			||||||
 | 
						use std::result::Result;
 | 
				
			||||||
 | 
						use std::fmt;
 | 
				
			||||||
 | 
						use {util, ethabi};
 | 
				
			||||||
 | 
						use util::{FixedHash, Uint};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pub struct Contract {
 | 
				
			||||||
 | 
							contract: ethabi::Contract,
 | 
				
			||||||
 | 
							address: util::Address,
 | 
				
			||||||
 | 
							do_call: Box<Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send + Sync + 'static>,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						impl Contract {
 | 
				
			||||||
 | 
							pub fn new<F>(address: util::Address, do_call: F) -> Self where F: Fn(util::Address, Vec<u8>) -> Result<Vec<u8>, String> + Send + Sync + 'static {
 | 
				
			||||||
 | 
								Contract {
 | 
				
			||||||
 | 
									contract: ethabi::Contract::new(ethabi::Interface::load(b"[{\"constant\":true,\"inputs\":[],\"name\":\"getValidators\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"type\":\"function\"}]").expect("JSON is autogenerated; qed")),
 | 
				
			||||||
 | 
									address: address,
 | 
				
			||||||
 | 
									do_call: Box::new(do_call),
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							fn as_string<T: fmt::Debug>(e: T) -> String { format!("{:?}", e) }
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// Auto-generated from: `{"constant":true,"inputs":[],"name":"getValidators","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"}`
 | 
				
			||||||
 | 
							#[allow(dead_code)]
 | 
				
			||||||
 | 
							pub fn get_validators(&self) -> Result<Vec<util::Address>, String> { 
 | 
				
			||||||
 | 
								let call = self.contract.function("getValidators".into()).map_err(Self::as_string)?;
 | 
				
			||||||
 | 
								let data = call.encode_call(
 | 
				
			||||||
 | 
									vec![]
 | 
				
			||||||
 | 
								).map_err(Self::as_string)?;
 | 
				
			||||||
 | 
								let output = call.decode_output((self.do_call)(self.address.clone(), data)?).map_err(Self::as_string)?;
 | 
				
			||||||
 | 
								let mut result = output.into_iter().rev().collect::<Vec<_>>();
 | 
				
			||||||
 | 
								Ok(({ let r = result.pop().ok_or("Invalid return arity")?; let r = r.to_array().and_then(|v| v.into_iter().map(|a| a.to_address()).collect::<Option<Vec<[u8; 20]>>>()).ok_or("Invalid type returned")?; r.into_iter().map(|a| util::Address::from(a)).collect::<Vec<_>>() })) 
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
						use util::*;
 | 
				
			||||||
 | 
						use spec::Spec;
 | 
				
			||||||
 | 
						use account_provider::AccountProvider;
 | 
				
			||||||
 | 
						use transaction::{Transaction, Action};
 | 
				
			||||||
 | 
						use client::{BlockChainClient, EngineClient};
 | 
				
			||||||
 | 
						use miner::MinerService;
 | 
				
			||||||
 | 
						use tests::helpers::generate_dummy_client_with_spec_and_data;
 | 
				
			||||||
 | 
						use super::super::ValidatorSet;
 | 
				
			||||||
 | 
						use super::ValidatorContract;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#[test]
 | 
				
			||||||
 | 
						fn fetches_validators() {
 | 
				
			||||||
 | 
							let client = generate_dummy_client_with_spec_and_data(Spec::new_validator_contract, 0, 0, &[]);
 | 
				
			||||||
 | 
							let vc = Arc::new(ValidatorContract::new(Address::from_str("0000000000000000000000000000000000000005").unwrap()));
 | 
				
			||||||
 | 
							vc.register_call_contract(Arc::downgrade(&client));
 | 
				
			||||||
 | 
							vc.update();
 | 
				
			||||||
 | 
							assert!(vc.contains(&Address::from_str("7d577a597b2742b498cb5cf0c26cdcd726d39e6e").unwrap()));
 | 
				
			||||||
 | 
							assert!(vc.contains(&Address::from_str("82a978b3f5962a5b0957d9ee9eef472ee55b42f1").unwrap()));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#[test]
 | 
				
			||||||
 | 
						fn changes_validators() {
 | 
				
			||||||
 | 
							let tap = Arc::new(AccountProvider::transient_provider());
 | 
				
			||||||
 | 
							let v0 = tap.insert_account("1".sha3(), "").unwrap();
 | 
				
			||||||
 | 
							let v1 = tap.insert_account("0".sha3(), "").unwrap();
 | 
				
			||||||
 | 
							let spec_factory = || {
 | 
				
			||||||
 | 
								let spec = Spec::new_validator_contract();
 | 
				
			||||||
 | 
								spec.engine.register_account_provider(tap.clone());
 | 
				
			||||||
 | 
								spec
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
							let client = generate_dummy_client_with_spec_and_data(spec_factory, 0, 0, &[]);
 | 
				
			||||||
 | 
							client.engine().register_client(Arc::downgrade(&client));
 | 
				
			||||||
 | 
							let validator_contract = Address::from_str("0000000000000000000000000000000000000005").unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							client.miner().set_engine_signer(v1, "".into()).unwrap();
 | 
				
			||||||
 | 
							// Remove "1" validator.
 | 
				
			||||||
 | 
							let tx = Transaction {
 | 
				
			||||||
 | 
								nonce: 0.into(),
 | 
				
			||||||
 | 
								gas_price: 0.into(),
 | 
				
			||||||
 | 
								gas: 500_000.into(),
 | 
				
			||||||
 | 
								action: Action::Call(validator_contract),
 | 
				
			||||||
 | 
								value: 0.into(),
 | 
				
			||||||
 | 
								data: "f94e18670000000000000000000000000000000000000000000000000000000000000001".from_hex().unwrap(),
 | 
				
			||||||
 | 
							}.sign(&"1".sha3(), None);
 | 
				
			||||||
 | 
							client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap();
 | 
				
			||||||
 | 
							client.update_sealing();
 | 
				
			||||||
 | 
							assert_eq!(client.chain_info().best_block_number, 1);
 | 
				
			||||||
 | 
							// Add "1" validator back in.
 | 
				
			||||||
 | 
							let tx = Transaction {
 | 
				
			||||||
 | 
								nonce: 1.into(),
 | 
				
			||||||
 | 
								gas_price: 0.into(),
 | 
				
			||||||
 | 
								gas: 500_000.into(),
 | 
				
			||||||
 | 
								action: Action::Call(validator_contract),
 | 
				
			||||||
 | 
								value: 0.into(),
 | 
				
			||||||
 | 
								data: "4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".from_hex().unwrap(),
 | 
				
			||||||
 | 
							}.sign(&"1".sha3(), None);
 | 
				
			||||||
 | 
							client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap();
 | 
				
			||||||
 | 
							client.update_sealing();
 | 
				
			||||||
 | 
							// The transaction is not yet included so still unable to seal.
 | 
				
			||||||
 | 
							assert_eq!(client.chain_info().best_block_number, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// Switch to the validator that is still there.
 | 
				
			||||||
 | 
							client.miner().set_engine_signer(v0, "".into()).unwrap();
 | 
				
			||||||
 | 
							client.update_sealing();
 | 
				
			||||||
 | 
							assert_eq!(client.chain_info().best_block_number, 2);
 | 
				
			||||||
 | 
							// Switch back to the added validator, since the state is updated.
 | 
				
			||||||
 | 
							client.miner().set_engine_signer(v1, "".into()).unwrap();
 | 
				
			||||||
 | 
							let tx = Transaction {
 | 
				
			||||||
 | 
								nonce: 2.into(),
 | 
				
			||||||
 | 
								gas_price: 0.into(),
 | 
				
			||||||
 | 
								gas: 21000.into(),
 | 
				
			||||||
 | 
								action: Action::Call(Address::default()),
 | 
				
			||||||
 | 
								value: 0.into(),
 | 
				
			||||||
 | 
								data: Vec::new(),
 | 
				
			||||||
 | 
							}.sign(&"1".sha3(), None);
 | 
				
			||||||
 | 
							client.miner().import_own_transaction(client.as_ref(), tx.into()).unwrap();
 | 
				
			||||||
 | 
							client.update_sealing();
 | 
				
			||||||
 | 
							// Able to seal again.
 | 
				
			||||||
 | 
							assert_eq!(client.chain_info().best_block_number, 3);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										46
									
								
								ethcore/src/engines/validator_set/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								ethcore/src/engines/validator_set/mod.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
 | 
				
			||||||
 | 
					// This file is part of Parity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Parity is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Parity is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Validator lists.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mod simple_list;
 | 
				
			||||||
 | 
					mod contract;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use std::sync::Weak;
 | 
				
			||||||
 | 
					use util::{Address, Arc};
 | 
				
			||||||
 | 
					use ethjson::spec::ValidatorSet as ValidatorSpec;
 | 
				
			||||||
 | 
					use client::Client;
 | 
				
			||||||
 | 
					use self::simple_list::SimpleList;
 | 
				
			||||||
 | 
					use self::contract::ValidatorContract;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Creates a validator set from spec.
 | 
				
			||||||
 | 
					pub fn new_validator_set(spec: ValidatorSpec) -> Box<ValidatorSet + Send + Sync> {
 | 
				
			||||||
 | 
						match spec {
 | 
				
			||||||
 | 
							ValidatorSpec::List(list) => Box::new(SimpleList::new(list.into_iter().map(Into::into).collect())),
 | 
				
			||||||
 | 
							ValidatorSpec::Contract(address) => Box::new(Arc::new(ValidatorContract::new(address.into()))),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub trait ValidatorSet {
 | 
				
			||||||
 | 
						/// Checks if a given address is a validator.
 | 
				
			||||||
 | 
						fn contains(&self, address: &Address) -> bool;
 | 
				
			||||||
 | 
						/// Draws an validator nonce modulo number of validators.
 | 
				
			||||||
 | 
						fn get(&self, nonce: usize) -> Address;
 | 
				
			||||||
 | 
						/// Returns the current number of validators.
 | 
				
			||||||
 | 
						fn count(&self) -> usize;
 | 
				
			||||||
 | 
						/// Allows blockchain state access.
 | 
				
			||||||
 | 
						fn register_call_contract(&self, _client: Weak<Client>) {}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										68
									
								
								ethcore/src/engines/validator_set/simple_list.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								ethcore/src/engines/validator_set/simple_list.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
 | 
				
			||||||
 | 
					// This file is part of Parity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Parity is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Parity is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Preconfigured validator list.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use util::Address;
 | 
				
			||||||
 | 
					use super::ValidatorSet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[derive(Debug, PartialEq, Eq, Default)]
 | 
				
			||||||
 | 
					pub struct SimpleList {
 | 
				
			||||||
 | 
						validators: Vec<Address>,
 | 
				
			||||||
 | 
						validator_n: usize,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl SimpleList {
 | 
				
			||||||
 | 
						pub fn new(validators: Vec<Address>) -> Self {
 | 
				
			||||||
 | 
							SimpleList {
 | 
				
			||||||
 | 
								validator_n: validators.len(),
 | 
				
			||||||
 | 
								validators: validators,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl ValidatorSet for SimpleList {
 | 
				
			||||||
 | 
						fn contains(&self, address: &Address) -> bool {
 | 
				
			||||||
 | 
							self.validators.contains(address)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn get(&self, nonce: usize) -> Address {
 | 
				
			||||||
 | 
							self.validators.get(nonce % self.validator_n).expect("There are validator_n authorities; taking number modulo validator_n gives number in validator_n range; qed").clone()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fn count(&self) -> usize {
 | 
				
			||||||
 | 
							self.validator_n
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
						use std::str::FromStr;
 | 
				
			||||||
 | 
						use util::Address;
 | 
				
			||||||
 | 
						use super::super::ValidatorSet;
 | 
				
			||||||
 | 
						use super::SimpleList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#[test]
 | 
				
			||||||
 | 
						fn simple_list() {
 | 
				
			||||||
 | 
							let a1 = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap();
 | 
				
			||||||
 | 
							let a2 = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap();
 | 
				
			||||||
 | 
							let list = SimpleList::new(vec![a1.clone(), a2.clone()]);
 | 
				
			||||||
 | 
							assert!(list.contains(&a1));
 | 
				
			||||||
 | 
							assert_eq!(list.get(0), a1);
 | 
				
			||||||
 | 
							assert_eq!(list.get(1), a2);
 | 
				
			||||||
 | 
							assert_eq!(list.get(2), a1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -764,9 +764,17 @@ impl MinerService for Miner {
 | 
				
			|||||||
			if let Some(ref ap) = self.accounts {
 | 
								if let Some(ref ap) = self.accounts {
 | 
				
			||||||
				ap.sign(address.clone(), Some(password.clone()), Default::default())?;
 | 
									ap.sign(address.clone(), Some(password.clone()), Default::default())?;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			let mut sealing_work = self.sealing_work.lock();
 | 
								// Limit the scope of the locks.
 | 
				
			||||||
			sealing_work.enabled = self.engine.is_sealer(&address).unwrap_or(false);
 | 
								{
 | 
				
			||||||
			*self.author.write() = address;
 | 
									let mut sealing_work = self.sealing_work.lock();
 | 
				
			||||||
 | 
									sealing_work.enabled = self.engine.is_sealer(&address).unwrap_or(false);
 | 
				
			||||||
 | 
									*self.author.write() = address;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// --------------------------------------------------------------------------
 | 
				
			||||||
 | 
								// | NOTE Code below may require author and sealing_work locks              |
 | 
				
			||||||
 | 
								// | (some `Engine`s call `EngineClient.update_sealing()`)                  |.
 | 
				
			||||||
 | 
								// | Make sure to release the locks before calling that method.             |
 | 
				
			||||||
 | 
								// --------------------------------------------------------------------------
 | 
				
			||||||
			self.engine.set_signer(address, password);
 | 
								self.engine.set_signer(address, password);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		Ok(())
 | 
							Ok(())
 | 
				
			||||||
 | 
				
			|||||||
@ -20,7 +20,7 @@ use util::*;
 | 
				
			|||||||
use io::*;
 | 
					use io::*;
 | 
				
			||||||
use spec::Spec;
 | 
					use spec::Spec;
 | 
				
			||||||
use error::*;
 | 
					use error::*;
 | 
				
			||||||
use client::{Client, BlockChainClient, MiningBlockChainClient, ClientConfig, ChainNotify};
 | 
					use client::{Client, ClientConfig, ChainNotify};
 | 
				
			||||||
use miner::Miner;
 | 
					use miner::Miner;
 | 
				
			||||||
use snapshot::ManifestData;
 | 
					use snapshot::ManifestData;
 | 
				
			||||||
use snapshot::service::{Service as SnapshotService, ServiceParams as SnapServiceParams};
 | 
					use snapshot::service::{Service as SnapshotService, ServiceParams as SnapServiceParams};
 | 
				
			||||||
@ -46,12 +46,6 @@ pub enum ClientIoMessage {
 | 
				
			|||||||
	FeedBlockChunk(H256, Bytes),
 | 
						FeedBlockChunk(H256, Bytes),
 | 
				
			||||||
	/// Take a snapshot for the block with given number.
 | 
						/// Take a snapshot for the block with given number.
 | 
				
			||||||
	TakeSnapshot(u64),
 | 
						TakeSnapshot(u64),
 | 
				
			||||||
	/// Trigger sealing update (useful for internal sealing).
 | 
					 | 
				
			||||||
	UpdateSealing,
 | 
					 | 
				
			||||||
	/// Submit seal (useful for internal sealing).
 | 
					 | 
				
			||||||
	SubmitSeal(H256, Vec<Bytes>),
 | 
					 | 
				
			||||||
	/// Broadcast a message to the network.
 | 
					 | 
				
			||||||
	BroadcastMessage(Bytes),
 | 
					 | 
				
			||||||
	/// New consensus message received.
 | 
						/// New consensus message received.
 | 
				
			||||||
	NewMessage(Bytes)
 | 
						NewMessage(Bytes)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -114,7 +108,7 @@ impl ClientService {
 | 
				
			|||||||
		});
 | 
							});
 | 
				
			||||||
		io_service.register_handler(client_io)?;
 | 
							io_service.register_handler(client_io)?;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		spec.engine.register_message_channel(io_service.channel());
 | 
							spec.engine.register_client(Arc::downgrade(&client));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		let stop_guard = ::devtools::StopGuard::new();
 | 
							let stop_guard = ::devtools::StopGuard::new();
 | 
				
			||||||
		run_ipc(ipc_path, client.clone(), snapshot.clone(), stop_guard.share());
 | 
							run_ipc(ipc_path, client.clone(), snapshot.clone(), stop_guard.share());
 | 
				
			||||||
@ -221,9 +215,6 @@ impl IoHandler<ClientIoMessage> for ClientIoHandler {
 | 
				
			|||||||
					debug!(target: "snapshot", "Failed to initialize periodic snapshot thread: {:?}", e);
 | 
										debug!(target: "snapshot", "Failed to initialize periodic snapshot thread: {:?}", e);
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			ClientIoMessage::UpdateSealing => self.client.update_sealing(),
 | 
					 | 
				
			||||||
			ClientIoMessage::SubmitSeal(ref hash, ref seal) => self.client.submit_seal(*hash, seal.clone()),
 | 
					 | 
				
			||||||
			ClientIoMessage::BroadcastMessage(ref message) => self.client.broadcast_consensus_message(message.clone()),
 | 
					 | 
				
			||||||
			ClientIoMessage::NewMessage(ref message) => if let Err(e) = self.client.engine().handle_message(message) {
 | 
								ClientIoMessage::NewMessage(ref message) => if let Err(e) = self.client.engine().handle_message(message) {
 | 
				
			||||||
				trace!(target: "poa", "Invalid message received: {}", e);
 | 
									trace!(target: "poa", "Invalid message received: {}", e);
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
 | 
				
			|||||||
@ -337,9 +337,14 @@ impl Spec {
 | 
				
			|||||||
	pub fn new_instant() -> Spec { load_bundled!("instant_seal") }
 | 
						pub fn new_instant() -> Spec { load_bundled!("instant_seal") }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Create a new Spec with AuthorityRound consensus which does internal sealing (not requiring work).
 | 
						/// Create a new Spec with AuthorityRound consensus which does internal sealing (not requiring work).
 | 
				
			||||||
	/// Accounts with secrets "0".sha3() and "1".sha3() are the authorities.
 | 
						/// Accounts with secrets "0".sha3() and "1".sha3() are the validators.
 | 
				
			||||||
	pub fn new_test_round() -> Self { load_bundled!("authority_round") }
 | 
						pub fn new_test_round() -> Self { load_bundled!("authority_round") }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/// Create a new Spec with BasicAuthority which uses a contract at address 5 to determine the current validators.
 | 
				
			||||||
 | 
						/// Accounts with secrets "0".sha3() and "1".sha3() are initially the validators.
 | 
				
			||||||
 | 
						/// Second validator can be removed with "0xf94e18670000000000000000000000000000000000000000000000000000000000000001" and added back in using "0x4d238c8e00000000000000000000000082a978b3f5962a5b0957d9ee9eef472ee55b42f1".
 | 
				
			||||||
 | 
						pub fn new_validator_contract() -> Self { load_bundled!("validator_contract") }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// Create a new Spec with Tendermint consensus which does internal sealing (not requiring work).
 | 
						/// Create a new Spec with Tendermint consensus which does internal sealing (not requiring work).
 | 
				
			||||||
	/// Account "0".sha3() and "1".sha3() are a authorities.
 | 
						/// Account "0".sha3() and "1".sha3() are a authorities.
 | 
				
			||||||
	pub fn new_test_tendermint() -> Self { load_bundled!("tendermint") }
 | 
						pub fn new_test_tendermint() -> Self { load_bundled!("tendermint") }
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,7 @@
 | 
				
			|||||||
//! Authority params deserialization.
 | 
					//! Authority params deserialization.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use uint::Uint;
 | 
					use uint::Uint;
 | 
				
			||||||
use hash::Address;
 | 
					use super::ValidatorSet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Authority params deserialization.
 | 
					/// Authority params deserialization.
 | 
				
			||||||
#[derive(Debug, PartialEq, Deserialize)]
 | 
					#[derive(Debug, PartialEq, Deserialize)]
 | 
				
			||||||
@ -29,7 +29,7 @@ pub struct AuthorityRoundParams {
 | 
				
			|||||||
	#[serde(rename="stepDuration")]
 | 
						#[serde(rename="stepDuration")]
 | 
				
			||||||
	pub step_duration: Uint,
 | 
						pub step_duration: Uint,
 | 
				
			||||||
	/// Valid authorities
 | 
						/// Valid authorities
 | 
				
			||||||
	pub authorities: Vec<Address>,
 | 
						pub validators: ValidatorSet,
 | 
				
			||||||
	/// Block reward.
 | 
						/// Block reward.
 | 
				
			||||||
	#[serde(rename="blockReward")]
 | 
						#[serde(rename="blockReward")]
 | 
				
			||||||
	pub block_reward: Option<Uint>,
 | 
						pub block_reward: Option<Uint>,
 | 
				
			||||||
@ -57,7 +57,9 @@ mod tests {
 | 
				
			|||||||
			"params": {
 | 
								"params": {
 | 
				
			||||||
				"gasLimitBoundDivisor": "0x0400",
 | 
									"gasLimitBoundDivisor": "0x0400",
 | 
				
			||||||
				"stepDuration": "0x02",
 | 
									"stepDuration": "0x02",
 | 
				
			||||||
				"authorities" : ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"],
 | 
									"validators": {
 | 
				
			||||||
 | 
										"list" : ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"]
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
				"blockReward": "0x50",
 | 
									"blockReward": "0x50",
 | 
				
			||||||
				"startStep" : 24
 | 
									"startStep" : 24
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,7 @@
 | 
				
			|||||||
//! Authority params deserialization.
 | 
					//! Authority params deserialization.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use uint::Uint;
 | 
					use uint::Uint;
 | 
				
			||||||
use hash::Address;
 | 
					use super::ValidatorSet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Authority params deserialization.
 | 
					/// Authority params deserialization.
 | 
				
			||||||
#[derive(Debug, PartialEq, Deserialize)]
 | 
					#[derive(Debug, PartialEq, Deserialize)]
 | 
				
			||||||
@ -29,7 +29,7 @@ pub struct BasicAuthorityParams {
 | 
				
			|||||||
	#[serde(rename="durationLimit")]
 | 
						#[serde(rename="durationLimit")]
 | 
				
			||||||
	pub duration_limit: Uint,
 | 
						pub duration_limit: Uint,
 | 
				
			||||||
	/// Valid authorities
 | 
						/// Valid authorities
 | 
				
			||||||
	pub authorities: Vec<Address>,
 | 
						pub validators: ValidatorSet,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Authority engine deserialization.
 | 
					/// Authority engine deserialization.
 | 
				
			||||||
@ -50,7 +50,9 @@ mod tests {
 | 
				
			|||||||
			"params": {
 | 
								"params": {
 | 
				
			||||||
				"gasLimitBoundDivisor": "0x0400",
 | 
									"gasLimitBoundDivisor": "0x0400",
 | 
				
			||||||
				"durationLimit": "0x0d",
 | 
									"durationLimit": "0x0d",
 | 
				
			||||||
				"authorities" : ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"]
 | 
									"validators" : {
 | 
				
			||||||
 | 
										"list": ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"]
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}"#;
 | 
							}"#;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -16,10 +16,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
//! Engine deserialization.
 | 
					//! Engine deserialization.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use spec::Ethash;
 | 
					use super::{Ethash, BasicAuthority, AuthorityRound, Tendermint};
 | 
				
			||||||
use spec::BasicAuthority;
 | 
					 | 
				
			||||||
use spec::AuthorityRound;
 | 
					 | 
				
			||||||
use spec::Tendermint;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Engine deserialization.
 | 
					/// Engine deserialization.
 | 
				
			||||||
#[derive(Debug, PartialEq, Deserialize)]
 | 
					#[derive(Debug, PartialEq, Deserialize)]
 | 
				
			||||||
 | 
				
			|||||||
@ -25,6 +25,7 @@ pub mod seal;
 | 
				
			|||||||
pub mod engine;
 | 
					pub mod engine;
 | 
				
			||||||
pub mod state;
 | 
					pub mod state;
 | 
				
			||||||
pub mod ethash;
 | 
					pub mod ethash;
 | 
				
			||||||
 | 
					pub mod validator_set;
 | 
				
			||||||
pub mod basic_authority;
 | 
					pub mod basic_authority;
 | 
				
			||||||
pub mod authority_round;
 | 
					pub mod authority_round;
 | 
				
			||||||
pub mod tendermint;
 | 
					pub mod tendermint;
 | 
				
			||||||
@ -38,6 +39,7 @@ pub use self::seal::{Seal, Ethereum, AuthorityRoundSeal, TendermintSeal};
 | 
				
			|||||||
pub use self::engine::Engine;
 | 
					pub use self::engine::Engine;
 | 
				
			||||||
pub use self::state::State;
 | 
					pub use self::state::State;
 | 
				
			||||||
pub use self::ethash::{Ethash, EthashParams};
 | 
					pub use self::ethash::{Ethash, EthashParams};
 | 
				
			||||||
 | 
					pub use self::validator_set::ValidatorSet;
 | 
				
			||||||
pub use self::basic_authority::{BasicAuthority, BasicAuthorityParams};
 | 
					pub use self::basic_authority::{BasicAuthority, BasicAuthorityParams};
 | 
				
			||||||
pub use self::authority_round::{AuthorityRound, AuthorityRoundParams};
 | 
					pub use self::authority_round::{AuthorityRound, AuthorityRoundParams};
 | 
				
			||||||
pub use self::tendermint::{Tendermint, TendermintParams};
 | 
					pub use self::tendermint::{Tendermint, TendermintParams};
 | 
				
			||||||
 | 
				
			|||||||
@ -17,7 +17,7 @@
 | 
				
			|||||||
//! Tendermint params deserialization.
 | 
					//! Tendermint params deserialization.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use uint::Uint;
 | 
					use uint::Uint;
 | 
				
			||||||
use hash::Address;
 | 
					use super::ValidatorSet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Tendermint params deserialization.
 | 
					/// Tendermint params deserialization.
 | 
				
			||||||
#[derive(Debug, PartialEq, Deserialize)]
 | 
					#[derive(Debug, PartialEq, Deserialize)]
 | 
				
			||||||
@ -25,8 +25,8 @@ pub struct TendermintParams {
 | 
				
			|||||||
	/// Gas limit divisor.
 | 
						/// Gas limit divisor.
 | 
				
			||||||
	#[serde(rename="gasLimitBoundDivisor")]
 | 
						#[serde(rename="gasLimitBoundDivisor")]
 | 
				
			||||||
	pub gas_limit_bound_divisor: Uint,
 | 
						pub gas_limit_bound_divisor: Uint,
 | 
				
			||||||
	/// Valid authorities
 | 
						/// Valid validators.
 | 
				
			||||||
	pub authorities: Vec<Address>,
 | 
						pub validators: ValidatorSet,
 | 
				
			||||||
	/// Propose step timeout in milliseconds.
 | 
						/// Propose step timeout in milliseconds.
 | 
				
			||||||
	#[serde(rename="timeoutPropose")]
 | 
						#[serde(rename="timeoutPropose")]
 | 
				
			||||||
	pub timeout_propose: Option<Uint>,
 | 
						pub timeout_propose: Option<Uint>,
 | 
				
			||||||
@ -60,8 +60,10 @@ mod tests {
 | 
				
			|||||||
	fn tendermint_deserialization() {
 | 
						fn tendermint_deserialization() {
 | 
				
			||||||
		let s = r#"{
 | 
							let s = r#"{
 | 
				
			||||||
			"params": {
 | 
								"params": {
 | 
				
			||||||
				"gasLimitBoundDivisor": "0x400",
 | 
									"gasLimitBoundDivisor": "0x0400",
 | 
				
			||||||
				"authorities" : ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"],
 | 
									"validators": {
 | 
				
			||||||
 | 
										"list": ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"]
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
				"blockReward": "0x50"
 | 
									"blockReward": "0x50"
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}"#;
 | 
							}"#;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										47
									
								
								json/src/spec/validator_set.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								json/src/spec/validator_set.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					// Copyright 2015, 2016 Parity Technologies (UK) Ltd.
 | 
				
			||||||
 | 
					// This file is part of Parity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Parity is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					// it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					// the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					// (at your option) any later version.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Parity is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					// but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					// GNU General Public License for more details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					// along with Parity.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//! Validator set deserialization.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use hash::Address;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Different ways of specifying validators.
 | 
				
			||||||
 | 
					#[derive(Debug, PartialEq, Deserialize)]
 | 
				
			||||||
 | 
					pub enum ValidatorSet {
 | 
				
			||||||
 | 
						/// A simple list of authorities.
 | 
				
			||||||
 | 
						#[serde(rename="list")]
 | 
				
			||||||
 | 
						List(Vec<Address>),
 | 
				
			||||||
 | 
						/// Address of a contract that indicates the list of authorities.
 | 
				
			||||||
 | 
						#[serde(rename="contract")]
 | 
				
			||||||
 | 
						Contract(Address),
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#[cfg(test)]
 | 
				
			||||||
 | 
					mod tests {
 | 
				
			||||||
 | 
						use serde_json;
 | 
				
			||||||
 | 
						use spec::validator_set::ValidatorSet;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#[test]
 | 
				
			||||||
 | 
						fn validator_set_deserialization() {
 | 
				
			||||||
 | 
							let s = r#"[{
 | 
				
			||||||
 | 
								"list" : ["0xc6d9d2cd449a754c494264e1809c50e34d64562b"]
 | 
				
			||||||
 | 
							}, {
 | 
				
			||||||
 | 
								"contract" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b"
 | 
				
			||||||
 | 
							}]"#;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							let _deserialized: Vec<ValidatorSet> = serde_json::from_str(s).unwrap();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@ -16,7 +16,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use util::*;
 | 
					use util::*;
 | 
				
			||||||
use io::{IoHandler, IoContext, IoChannel};
 | 
					use io::{IoHandler, IoContext, IoChannel};
 | 
				
			||||||
use ethcore::client::{BlockChainClient, Client, MiningBlockChainClient};
 | 
					use ethcore::client::{BlockChainClient, Client};
 | 
				
			||||||
use ethcore::service::ClientIoMessage;
 | 
					use ethcore::service::ClientIoMessage;
 | 
				
			||||||
use ethcore::spec::Spec;
 | 
					use ethcore::spec::Spec;
 | 
				
			||||||
use ethcore::miner::MinerService;
 | 
					use ethcore::miner::MinerService;
 | 
				
			||||||
@ -33,9 +33,6 @@ struct TestIoHandler {
 | 
				
			|||||||
impl IoHandler<ClientIoMessage> for TestIoHandler {
 | 
					impl IoHandler<ClientIoMessage> for TestIoHandler {
 | 
				
			||||||
	fn message(&self, _io: &IoContext<ClientIoMessage>, net_message: &ClientIoMessage) {
 | 
						fn message(&self, _io: &IoContext<ClientIoMessage>, net_message: &ClientIoMessage) {
 | 
				
			||||||
		match *net_message {
 | 
							match *net_message {
 | 
				
			||||||
			ClientIoMessage::UpdateSealing => self.client.update_sealing(),
 | 
					 | 
				
			||||||
			ClientIoMessage::SubmitSeal(ref hash, ref seal) => self.client.submit_seal(*hash, seal.clone()),
 | 
					 | 
				
			||||||
			ClientIoMessage::BroadcastMessage(ref message) => self.client.broadcast_consensus_message(message.clone()),
 | 
					 | 
				
			||||||
			ClientIoMessage::NewMessage(ref message) => if let Err(e) = self.client.engine().handle_message(message) {
 | 
								ClientIoMessage::NewMessage(ref message) => if let Err(e) = self.client.engine().handle_message(message) {
 | 
				
			||||||
				panic!("Invalid message received: {}", e);
 | 
									panic!("Invalid message received: {}", e);
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
@ -75,8 +72,8 @@ fn authority_round() {
 | 
				
			|||||||
	// Push transaction to both clients. Only one of them gets lucky to produce a block.
 | 
						// Push transaction to both clients. Only one of them gets lucky to produce a block.
 | 
				
			||||||
	net.peer(0).chain.miner().set_engine_signer(s0.address(), "".to_owned()).unwrap();
 | 
						net.peer(0).chain.miner().set_engine_signer(s0.address(), "".to_owned()).unwrap();
 | 
				
			||||||
	net.peer(1).chain.miner().set_engine_signer(s1.address(), "".to_owned()).unwrap();
 | 
						net.peer(1).chain.miner().set_engine_signer(s1.address(), "".to_owned()).unwrap();
 | 
				
			||||||
	net.peer(0).chain.engine().register_message_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0)));
 | 
						net.peer(0).chain.engine().register_client(Arc::downgrade(&net.peer(0).chain));
 | 
				
			||||||
	net.peer(1).chain.engine().register_message_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1)));
 | 
						net.peer(1).chain.engine().register_client(Arc::downgrade(&net.peer(1).chain));
 | 
				
			||||||
	net.peer(0).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1)));
 | 
						net.peer(0).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1)));
 | 
				
			||||||
	net.peer(1).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0)));
 | 
						net.peer(1).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0)));
 | 
				
			||||||
	// exchange statuses
 | 
						// exchange statuses
 | 
				
			||||||
@ -140,8 +137,8 @@ fn tendermint() {
 | 
				
			|||||||
	trace!(target: "poa", "Peer 0 is {}.", s0.address());
 | 
						trace!(target: "poa", "Peer 0 is {}.", s0.address());
 | 
				
			||||||
	net.peer(1).chain.miner().set_engine_signer(s1.address(), "".to_owned()).unwrap();
 | 
						net.peer(1).chain.miner().set_engine_signer(s1.address(), "".to_owned()).unwrap();
 | 
				
			||||||
	trace!(target: "poa", "Peer 1 is {}.", s1.address());
 | 
						trace!(target: "poa", "Peer 1 is {}.", s1.address());
 | 
				
			||||||
	net.peer(0).chain.engine().register_message_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0)));
 | 
						net.peer(0).chain.engine().register_client(Arc::downgrade(&net.peer(0).chain));
 | 
				
			||||||
	net.peer(1).chain.engine().register_message_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1)));
 | 
						net.peer(1).chain.engine().register_client(Arc::downgrade(&net.peer(1).chain));
 | 
				
			||||||
	net.peer(0).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0)));
 | 
						net.peer(0).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler0)));
 | 
				
			||||||
	net.peer(1).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1)));
 | 
						net.peer(1).chain.set_io_channel(IoChannel::to_handler(Arc::downgrade(&io_handler1)));
 | 
				
			||||||
	// Exhange statuses
 | 
						// Exhange statuses
 | 
				
			||||||
 | 
				
			|||||||
@ -391,7 +391,7 @@ impl<Message> IoChannel<Message> where Message: Send + Clone + Sync + 'static {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		Ok(())
 | 
							Ok(())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	/// Create a new channel to connected to event loop.
 | 
						/// Create a new channel disconnected from an event loop.
 | 
				
			||||||
	pub fn disconnected() -> IoChannel<Message> {
 | 
						pub fn disconnected() -> IoChannel<Message> {
 | 
				
			||||||
		IoChannel {
 | 
							IoChannel {
 | 
				
			||||||
			channel: None,
 | 
								channel: None,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user