txgen/
lib.rs

1// Copyright 2019 Conflux Foundation. All rights reserved.
2// Conflux is free software and distributed under GNU General Public License.
3// See http://www.gnu.org/licenses/
4
5use cfx_bytes as bytes;
6use cfxkey as keylib;
7use log::{debug, info, trace};
8
9use crate::bytes::Bytes;
10use cfx_types::{
11    address_util::AddressUtil, cal_contract_address, Address, AddressSpaceUtil,
12    BigEndianHash, CreateContractAddressType, H256, H512, U256, U512,
13};
14use cfxcore::{
15    SharedConsensusGraph, SharedSynchronizationService, SharedTransactionPool,
16};
17use keylib::{public_to_address, Generator, KeyPair, Random, Secret};
18use lazy_static::lazy_static;
19use metrics::{register_meter_with_group, Meter};
20use parking_lot::RwLock;
21use primitives::{
22    transaction::{native_transaction::NativeTransaction, Action},
23    Account, SignedTransaction, Transaction,
24};
25use rand::{prelude::*, random_range};
26use rlp::Encodable;
27use rustc_hex::{FromHex, ToHex};
28use secret_store::SharedSecretStore;
29use std::{
30    cmp::Ordering,
31    collections::HashMap,
32    convert::TryFrom,
33    sync::Arc,
34    thread,
35    time::{self, Instant},
36};
37use time::Duration;
38
39lazy_static! {
40    static ref TX_GEN_METER: Arc<dyn Meter> =
41        register_meter_with_group("system_metrics", "tx_gen");
42}
43
44enum TransGenState {
45    Start,
46    Stop,
47}
48
49pub struct TransactionGeneratorConfig {
50    pub generate_tx: bool,
51    pub period: time::Duration,
52    pub account_count: usize,
53}
54
55impl TransactionGeneratorConfig {
56    pub fn new(
57        generate_tx: bool, period_ms: u64, account_count: usize,
58    ) -> Self {
59        TransactionGeneratorConfig {
60            generate_tx,
61            period: time::Duration::from_micros(period_ms),
62            account_count,
63        }
64    }
65}
66
67pub struct TransactionGenerator {
68    consensus: SharedConsensusGraph,
69    sync: SharedSynchronizationService,
70    txpool: SharedTransactionPool,
71    secret_store: SharedSecretStore,
72    state: RwLock<TransGenState>,
73    account_start_index: RwLock<Option<usize>>,
74    join_handle: RwLock<Option<thread::JoinHandle<()>>>,
75}
76
77pub type SharedTransactionGenerator = Arc<TransactionGenerator>;
78
79impl TransactionGenerator {
80    // FIXME: rename to start and return Result<Self>
81    pub fn new(
82        consensus: SharedConsensusGraph, txpool: SharedTransactionPool,
83        sync: SharedSynchronizationService, secret_store: SharedSecretStore,
84    ) -> Self {
85        TransactionGenerator {
86            consensus,
87            txpool,
88            sync,
89            secret_store,
90            state: RwLock::new(TransGenState::Start),
91            account_start_index: RwLock::new(Option::None),
92            join_handle: RwLock::new(None),
93        }
94    }
95
96    pub fn stop(&self) {
97        *self.state.write() = TransGenState::Stop;
98        if let Some(join_handle) = self.join_handle.write().take() {
99            join_handle.join().ok();
100        }
101    }
102
103    pub fn set_genesis_accounts_start_index(&self, index: usize) {
104        let mut account_start = self.account_start_index.write();
105        *account_start = Some(index);
106    }
107
108    pub fn set_join_handle(&self, join_handle: thread::JoinHandle<()>) {
109        self.join_handle.write().replace(join_handle);
110    }
111
112    pub fn generate_transactions_with_multiple_genesis_accounts(
113        txgen: Arc<TransactionGenerator>,
114        tx_config: TransactionGeneratorConfig,
115        genesis_accounts: HashMap<Address, U256>,
116    ) {
117        loop {
118            let account_start = txgen.account_start_index.read();
119            if account_start.is_some() {
120                break;
121            }
122        }
123        let account_start_index = txgen.account_start_index.read().unwrap();
124        let mut nonce_map: HashMap<Address, U256> = HashMap::new();
125        let mut balance_map: HashMap<Address, U256> = HashMap::new();
126        let mut address_secret_pair: HashMap<Address, Secret> = HashMap::new();
127        let mut addresses: Vec<Address> = Vec::new();
128
129        debug!("Tx Generation Config {:?}", tx_config.generate_tx);
130
131        let mut tx_n = 0;
132        // Wait for initial tx
133        loop {
134            match *txgen.state.read() {
135                TransGenState::Stop => return,
136                _ => {}
137            }
138
139            // Do not generate tx in catch_up_mode
140            if txgen.sync.catch_up_mode() {
141                thread::sleep(Duration::from_millis(100));
142                continue;
143            }
144            break;
145        }
146
147        debug!("Setup Usable Genesis Accounts");
148        for i in 0..tx_config.account_count {
149            let key_pair =
150                txgen.secret_store.get_keypair(account_start_index + i);
151            let address = key_pair.address();
152            let secret = key_pair.secret().clone();
153            addresses.push(address);
154            nonce_map.insert(address.clone(), 0.into());
155
156            let balance = genesis_accounts.get(&address).cloned();
157
158            balance_map
159                .insert(address.clone(), balance.unwrap_or(U256::zero()));
160            address_secret_pair.insert(address, secret);
161        }
162
163        info!("Start Generating Workload");
164        let start_time = Instant::now();
165        // Generate more tx
166
167        let account_count = address_secret_pair.len();
168        loop {
169            match *txgen.state.read() {
170                TransGenState::Stop => return,
171                _ => {}
172            }
173
174            // Randomly select sender and receiver.
175            // Sender and receiver must exist in the account list.
176            let mut receiver_index: usize = random_range(0..usize::MAX);
177            receiver_index %= account_count;
178            let receiver_address = addresses[receiver_index];
179
180            let mut sender_index: usize = random_range(0..usize::MAX);
181            sender_index %= account_count;
182            let sender_address = addresses[sender_index];
183
184            // Always send value 0
185            let balance_to_transfer = U256::from(0);
186
187            // Generate nonce for the transaction
188            let sender_nonce = nonce_map.get_mut(&sender_address).unwrap();
189
190            // FIXME: It's better first define what kind of Result type
191            // FIXME: to use for this function, then change unwrap() to ?.
192            let (nonce, balance) = txgen
193                .txpool
194                .get_state_account_info(&sender_address.with_native_space())
195                .unwrap();
196            if nonce.cmp(sender_nonce) != Ordering::Equal {
197                *sender_nonce = nonce.clone();
198                balance_map.insert(sender_address.clone(), balance.clone());
199            }
200            trace!(
201                "receiver={:?} value={:?} nonce={:?}",
202                receiver_address,
203                balance_to_transfer,
204                sender_nonce
205            );
206            // Generate the transaction, sign it, and push into the transaction
207            // pool
208            let tx: Transaction = NativeTransaction {
209                nonce: *sender_nonce,
210                gas_price: U256::from(1u64),
211                gas: U256::from(21000u64),
212                value: balance_to_transfer,
213                action: Action::Call(receiver_address),
214                storage_limit: 0,
215                chain_id: txgen.consensus.best_chain_id().in_native_space(),
216                epoch_height: txgen.consensus.best_epoch_number(),
217                data: Bytes::new(),
218            }
219            .into();
220
221            let signed_tx = tx.sign(&address_secret_pair[&sender_address]);
222            let mut tx_to_insert = Vec::new();
223            tx_to_insert.push(signed_tx.transaction);
224            let (txs, fail) =
225                txgen.txpool.insert_new_transactions(tx_to_insert);
226            if fail.is_empty() {
227                txgen.sync.append_received_transactions(txs);
228                //tx successfully inserted into
229                // tx pool, so we can update our state about
230                // nonce and balance
231                {
232                    let sender_balance =
233                        balance_map.get_mut(&sender_address).unwrap();
234                    *sender_balance -= balance_to_transfer + 21000;
235                    if *sender_balance < 42000.into() {
236                        addresses.remove(sender_index);
237                        if addresses.is_empty() {
238                            break;
239                        }
240                    }
241                }
242                *sender_nonce += U256::one();
243                *balance_map.entry(receiver_address).or_insert(0.into()) +=
244                    balance_to_transfer;
245                tx_n += 1;
246                TX_GEN_METER.mark(1);
247            } else {
248                // The transaction pool is full and the tx is discarded, so the
249                // state should not updated. We add unconditional
250                // sleep to avoid busy spin if the tx pool cannot support the
251                // expected throughput.
252                thread::sleep(tx_config.period);
253            }
254
255            let now = Instant::now();
256            let time_elapsed = now.duration_since(start_time);
257            if let Some(time_left) =
258                (tx_config.period * tx_n).checked_sub(time_elapsed)
259            {
260                thread::sleep(time_left);
261            } else {
262                debug!(
263                    "Elapsed time larger than the time needed for sleep: \
264                     time_elapsed={:?} tx_n={}",
265                    time_elapsed, tx_n
266                );
267            }
268        }
269    }
270}
271
272/// This tx generator directly push simple transactions and erc20 transactions
273/// into blocks. It's used in Ethereum e2d replay test.
274pub struct DirectTransactionGenerator {
275    // Key, simple tx, erc20 balance, array index.
276    accounts: HashMap<Address, (KeyPair, Account, U256)>,
277    address_by_index: Vec<Address>,
278    erc20_address: Address,
279}
280
281#[allow(deprecated)]
282impl DirectTransactionGenerator {
283    const MAX_TOTAL_ACCOUNTS: usize = 100_000;
284
285    pub fn new(
286        start_key_pair: KeyPair, contract_creator: &Address,
287        start_balance: U256, start_erc20_balance: U256,
288    ) -> DirectTransactionGenerator {
289        let start_address = public_to_address(start_key_pair.public(), true);
290        let info = (
291            start_key_pair,
292            Account::new_empty_with_balance(
293                &start_address.with_native_space(),
294                &start_balance,
295                &0.into(), /* nonce */
296            ),
297            start_erc20_balance,
298        );
299        let mut accounts = HashMap::<Address, (KeyPair, Account, U256)>::new();
300        accounts.insert(start_address.clone(), info);
301        let address_by_index = vec![start_address.clone()];
302
303        let mut erc20_address = cal_contract_address(
304            CreateContractAddressType::FromSenderNonceAndCodeHash,
305            // A fake block_number. There field is unnecessary in Ethereum
306            // replay test.
307            &contract_creator,
308            &0.into(),
309            // A fake code. There field is unnecessary in Ethereum replay test.
310            &[],
311        )
312        .0;
313        erc20_address.set_contract_type_bits();
314
315        debug!(
316            "Special Transaction Generator: erc20 contract address: {:?}",
317            erc20_address
318        );
319
320        DirectTransactionGenerator {
321            accounts,
322            address_by_index,
323            erc20_address,
324        }
325    }
326
327    pub fn generate_transactions(
328        &mut self, block_size_limit: &mut usize, mut num_txs_simple: usize,
329        mut num_txs_erc20: usize, chain_id: u32,
330    ) -> Vec<Arc<SignedTransaction>> {
331        let mut result = vec![];
332        // Generate new address with 10% probability
333        while num_txs_simple > 0 {
334            let number_of_accounts = self.address_by_index.len();
335
336            let sender_index: usize = random_range(0..number_of_accounts);
337            let sender_address =
338                self.address_by_index.get(sender_index).unwrap().clone();
339            let sender_kp;
340            let sender_balance;
341            let sender_nonce;
342            {
343                let sender_info = self.accounts.get(&sender_address).unwrap();
344                sender_kp = sender_info.0.clone();
345                sender_balance = sender_info.1.balance;
346                sender_nonce = sender_info.1.nonce;
347            }
348
349            let gas = U256::from(100_000u64);
350            let gas_price = U256::from(1u64);
351            let transaction_fee = U256::from(100_000u64);
352
353            if sender_balance <= transaction_fee {
354                self.accounts.remove(&sender_address);
355                self.address_by_index.swap_remove(sender_index);
356                continue;
357            }
358
359            let balance_to_transfer = U256::try_from(
360                H512::random().into_uint() % U512::from(sender_balance),
361            )
362            .unwrap();
363
364            let is_send_to_new_address = (number_of_accounts
365                <= Self::MAX_TOTAL_ACCOUNTS)
366                && ((number_of_accounts < 10)
367                    || (rand::thread_rng().random_range(0..10) == 0));
368
369            let receiver_address = match is_send_to_new_address {
370                false => {
371                    let index: usize = random_range(0..number_of_accounts);
372                    self.address_by_index.get(index).unwrap().clone()
373                }
374                true => loop {
375                    let kp =
376                        Random.generate().expect("Fail to generate KeyPair.");
377                    let address = public_to_address(kp.public(), true);
378                    if self.accounts.get(&address).is_none() {
379                        self.accounts.insert(
380                            address,
381                            (
382                                kp,
383                                Account::new_empty_with_balance(
384                                    &address.with_native_space(),
385                                    &0.into(), /* balance */
386                                    &0.into(), /* nonce */
387                                ),
388                                0.into(),
389                            ),
390                        );
391                        self.address_by_index.push(address.clone());
392
393                        break address;
394                    }
395                },
396            };
397
398            let tx: Transaction = NativeTransaction {
399                nonce: sender_nonce,
400                gas_price,
401                gas,
402                value: balance_to_transfer,
403                action: Action::Call(receiver_address),
404                storage_limit: 0,
405                // FIXME: We will have to setup TRANSACTION_EPOCH_BOUND to a
406                // large value to avoid FIXME: this sloppy zero
407                // becomes an issue in the experiments.
408                epoch_height: 0,
409                chain_id,
410                data: vec![0u8; 128],
411            }
412            .into();
413            let signed_transaction = tx.sign(sender_kp.secret());
414            let rlp_size = signed_transaction.transaction.rlp_bytes().len();
415            if *block_size_limit <= rlp_size {
416                break;
417            }
418            *block_size_limit -= rlp_size;
419
420            self.accounts.get_mut(&sender_address).unwrap().1.balance -=
421                balance_to_transfer;
422            self.accounts.get_mut(&sender_address).unwrap().1.nonce += 1.into();
423            self.accounts.get_mut(&receiver_address).unwrap().1.balance +=
424                balance_to_transfer;
425
426            result.push(Arc::new(signed_transaction));
427
428            num_txs_simple -= 1;
429        }
430
431        while num_txs_erc20 > 0 {
432            let number_of_accounts = self.address_by_index.len();
433
434            let sender_index: usize = random_range(0..number_of_accounts);
435            let sender_address =
436                self.address_by_index.get(sender_index).unwrap().clone();
437            let sender_kp;
438            let sender_balance;
439            let sender_erc20_balance;
440            let sender_nonce;
441            {
442                let sender_info = self.accounts.get(&sender_address).unwrap();
443                sender_kp = sender_info.0.clone();
444                sender_balance = sender_info.1.balance;
445                sender_erc20_balance = sender_info.2.clone();
446                sender_nonce = sender_info.1.nonce;
447            }
448
449            let gas = U256::from(100_000u64);
450            let gas_price = U256::from(1u64);
451            let transaction_fee = U256::from(100_000u64);
452
453            if sender_balance <= transaction_fee {
454                self.accounts.remove(&sender_address);
455                self.address_by_index.swap_remove(sender_index);
456                continue;
457            }
458
459            let balance_to_transfer = if sender_erc20_balance == 0.into() {
460                continue;
461            } else {
462                U256::try_from(
463                    H512::random().into_uint()
464                        % U512::from(sender_erc20_balance),
465                )
466                .unwrap()
467            };
468
469            let receiver_index = random_range(0..number_of_accounts);
470            let receiver_address =
471                self.address_by_index.get(receiver_index).unwrap().clone();
472
473            if receiver_index == sender_index {
474                continue;
475            }
476
477            // Calls transfer of ERC20 contract.
478            let tx_data = (String::new()
479                + "a9059cbb000000000000000000000000"
480                + &receiver_address.0.to_hex::<String>()[2..]
481                + {
482                    let h: H256 =
483                        BigEndianHash::from_uint(&balance_to_transfer);
484                    &h.0.to_hex::<String>()[2..]
485                })
486            .from_hex()
487            .unwrap();
488
489            let tx: Transaction = NativeTransaction {
490                nonce: sender_nonce,
491                gas_price,
492                gas,
493                value: 0.into(),
494                action: Action::Call(self.erc20_address.clone()),
495                storage_limit: 0,
496                // FIXME: We will have to setup TRANSACTION_EPOCH_BOUND to a
497                // large value to avoid FIXME: this sloppy zero
498                // becomes an issue in the experiments.
499                epoch_height: 0,
500                chain_id,
501                data: tx_data,
502            }
503            .into();
504            let signed_transaction = tx.sign(sender_kp.secret());
505            let rlp_size = signed_transaction.transaction.rlp_bytes().len();
506            if *block_size_limit <= rlp_size {
507                break;
508            }
509            *block_size_limit -= rlp_size;
510
511            self.accounts.get_mut(&sender_address).unwrap().2 -=
512                balance_to_transfer;
513            self.accounts.get_mut(&sender_address).unwrap().1.nonce += 1.into();
514            self.accounts.get_mut(&receiver_address).unwrap().2 +=
515                balance_to_transfer;
516
517            result.push(Arc::new(signed_transaction));
518
519            num_txs_erc20 -= 1;
520        }
521
522        result
523    }
524}