Implement caching for service transactions checker#10088
Conversation
# Conflicts: # ethcore/res/contracts/tx_acl.json # ethcore/src/tx_filter.rs
|
It looks like @VladLupashevskyi signed our Contributor License Agreement. 👍 Many thanks, Parity Technologies CLA Bot |
tomusdrw
left a comment
There was a problem hiding this comment.
The idea looks reasonable I'm only concerned with circular call to state - we should make sure that all the calls are not vulnerable to deadlocks or some data races now.
(and obv the implementation needs some improvements)
| nonce: self.latest_nonce(&authoring_params.author), | ||
| action: Action::Call(address), | ||
| gas: self.importer.miner.sensible_gas_limit(), | ||
| gas: gas_price, |
There was a problem hiding this comment.
I think you meant to change gas_price not gas.
| fn transact_contract(&self, address: Address, data: Bytes) -> Result<(), transaction::Error> { | ||
| let authoring_params = self.importer.miner.authoring_params(); | ||
| let service_transaction_checker = ServiceTransactionChecker::default(); | ||
| let gas_price = match service_transaction_checker.check_address(self, address) { |
There was a problem hiding this comment.
address is contract address you try to call, not the sender (which should be checked here).
|
|
||
| mod miner; | ||
| mod service_transaction_checker; | ||
| pub mod service_transaction_checker; |
There was a problem hiding this comment.
please group with other pub mod
|
@tomusdrw I pushed the fixes and sorry for these typos. I am not sure what kind of deadlocks or data races could occur because free tx permission is checked for the latest mined block and not for the pending one. What I can think of is that issues could occur if we would call Maybe I am missing something, so I would be happy to get more feedback and dig into solving it :) |
# Conflicts: # ethcore/src/client/client.rs
|
|
||
| /// Checks if given address is whitelisted to send service transactions. | ||
| pub fn check_address<C: CallContract + RegistryInfo>(&self, client: &C, sender: Address) -> Result<bool, String> { | ||
| let contract_address = client.registry_address(SERVICE_TRANSACTION_CONTRACT_REGISTRY_NAME.to_owned(), BlockId::Latest) |
There was a problem hiding this comment.
Would the performance be a concern here? registry_address would need to query the state if registrar is set.
There was a problem hiding this comment.
I don't think that an issue, some cache would be nice (best would be if previously queried registry entries would be refreshed after a block is imported), but the code is not super hot (only in \.transact which is called by the engine, or by the miner, called only for zero-gas-price transactions attempting to enter the pool).
|
I added a cache for certified addresses and provided logic as @tomusdrw described. This is first time I'm working with caching so there is definitely something to improve and I'm always happy to get a feedback :) |
|
@tomusdrw I think idea with moving ServiceTxChecker to one place sounds reasonable! I would create a public method to get the checker instance from miner, so it's done more generic than just exposing a |
|
@tomusdrw I updated the code according to the comments, now it looks much cleaner! I'm thinking it would be nice to introduce some cache limiting, I would propose to use the solution I introduced in another PR for NodeFilter: where basically I used What do you think about it? |
|
Could you revisit this, @tomusdrw |
tomusdrw
left a comment
There was a problem hiding this comment.
Looks good, few nitpicks.
| /// Service transactions checker. | ||
| #[derive(Default, Clone)] | ||
| pub struct ServiceTransactionChecker; | ||
| #[derive(Clone)] |
There was a problem hiding this comment.
We need Clone in order to share ServiceTransactionChecker with Client and PoolClient.
| /// Checks if given address is whitelisted to send service transactions. | ||
| pub fn check_address<C: CallContract + RegistryInfo>(&self, client: &C, sender: Address) -> Result<bool, String> { | ||
| trace!(target: "txqueue", "Checking service transaction checker contract from {}", sender); | ||
| if let Some(allowed) = self.certified_addresses_cache.try_read().as_ref().and_then(|c| c.get(&sender)){ |
There was a problem hiding this comment.
.as_ref() seems redundant, also missing space before {
There was a problem hiding this comment.
I cannot do this without .as_ref() since the c doesn't live long enough in this case. I tried to do it with .map, but in this case Option<Option<&bool>> is returned, so one more check is needed. Not sure what would be better solution here.
# Conflicts: # Cargo.lock # ethcore/private-tx/src/lib.rs # ethcore/src/miner/miner.rs # ethcore/src/miner/pool_client.rs
# Conflicts: # Cargo.lock # ethcore/private-tx/src/lib.rs # ethcore/src/miner/miner.rs # ethcore/src/miner/pool_client.rs
|
Needs a 2nd review 👍 |
* master: fix(light cull): poll light cull instead of timer (#10559) Update Issue Template to direct security issue to email (#10562) RPC: Implements eth_subscribe("syncing") (#10311) Explicitly enable or disable Stratum in config file (Issue 9785) (#10521) version: bump master to 2.6 (#10560) tx-pool: check transaction readiness before replacing (#10526) fix(light account response): update `tx_queue` (#10545) Update light client harcoded headers (#10547) fix(light eth_gasPrice): ask network if not in cache (#10535) Implement caching for service transactions checker (#10088) build android with cache, win fixes (#10546) clique: make state backfill time measurement more accurate (#10551) updated lru-cache to 0.1.2 (#10542)
In cases when certain address is allowed for executing free transactions it makes sense in my opinion to submit transactions which are automatically submitted by the client (such as validators reporting etc) with the gas price set to zero.
Here I propose my code improvement to achieve this by adding check for free tx certification in transact_contract fn which is called while doing validators reporting and other actions.
Looking forward for the feedback :)