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            if let TransGenState::Stop = *txgen.state.read() {
135                return;
136            }
137
138            // Do not generate tx in catch_up_mode
139            if txgen.sync.catch_up_mode() {
140                thread::sleep(Duration::from_millis(100));
141                continue;
142            }
143            break;
144        }
145
146        debug!("Setup Usable Genesis Accounts");
147        for i in 0..tx_config.account_count {
148            let key_pair =
149                txgen.secret_store.get_keypair(account_start_index + i);
150            let address = key_pair.address();
151            let secret = key_pair.secret().clone();
152            addresses.push(address);
153            nonce_map.insert(address, 0.into());
154
155            let balance = genesis_accounts.get(&address).cloned();
156
157            balance_map.insert(address, balance.unwrap_or(U256::zero()));
158            address_secret_pair.insert(address, secret);
159        }
160
161        info!("Start Generating Workload");
162        let start_time = Instant::now();
163        // Generate more tx
164
165        let account_count = address_secret_pair.len();
166        loop {
167            if let TransGenState::Stop = *txgen.state.read() {
168                return;
169            }
170
171            // Randomly select sender and receiver.
172            // Sender and receiver must exist in the account list.
173            let mut receiver_index: usize = random_range(0..usize::MAX);
174            receiver_index %= account_count;
175            let receiver_address = addresses[receiver_index];
176
177            let mut sender_index: usize = random_range(0..usize::MAX);
178            sender_index %= account_count;
179            let sender_address = addresses[sender_index];
180
181            // Always send value 0
182            let balance_to_transfer = U256::from(0);
183
184            // Generate nonce for the transaction
185            let sender_nonce = nonce_map.get_mut(&sender_address).unwrap();
186
187            // FIXME: It's better first define what kind of Result type
188            // FIXME: to use for this function, then change unwrap() to ?.
189            let (nonce, balance) = txgen
190                .txpool
191                .get_state_account_info(&sender_address.with_native_space())
192                .unwrap();
193            if nonce.cmp(sender_nonce) != Ordering::Equal {
194                *sender_nonce = nonce;
195                balance_map.insert(sender_address, balance);
196            }
197            trace!(
198                "receiver={:?} value={:?} nonce={:?}",
199                receiver_address,
200                balance_to_transfer,
201                sender_nonce
202            );
203            // Generate the transaction, sign it, and push into the transaction
204            // pool
205            let tx: Transaction = NativeTransaction {
206                nonce: *sender_nonce,
207                gas_price: U256::from(1u64),
208                gas: U256::from(21000u64),
209                value: balance_to_transfer,
210                action: Action::Call(receiver_address),
211                storage_limit: 0,
212                chain_id: txgen.consensus.best_chain_id().in_native_space(),
213                epoch_height: txgen.consensus.best_epoch_number(),
214                data: Bytes::new(),
215            }
216            .into();
217
218            let signed_tx = tx.sign(&address_secret_pair[&sender_address]);
219            let tx_to_insert = vec![signed_tx.transaction];
220            let (txs, fail) =
221                txgen.txpool.insert_new_transactions(tx_to_insert);
222            if fail.is_empty() {
223                txgen.sync.append_received_transactions(txs);
224                //tx successfully inserted into
225                // tx pool, so we can update our state about
226                // nonce and balance
227                {
228                    let sender_balance =
229                        balance_map.get_mut(&sender_address).unwrap();
230                    *sender_balance -= balance_to_transfer + 21000;
231                    if *sender_balance < 42000.into() {
232                        addresses.remove(sender_index);
233                        if addresses.is_empty() {
234                            break;
235                        }
236                    }
237                }
238                *sender_nonce += U256::one();
239                *balance_map.entry(receiver_address).or_insert(0.into()) +=
240                    balance_to_transfer;
241                tx_n += 1;
242                TX_GEN_METER.mark(1);
243            } else {
244                // The transaction pool is full and the tx is discarded, so the
245                // state should not updated. We add unconditional
246                // sleep to avoid busy spin if the tx pool cannot support the
247                // expected throughput.
248                thread::sleep(tx_config.period);
249            }
250
251            let now = Instant::now();
252            let time_elapsed = now.duration_since(start_time);
253            if let Some(time_left) =
254                (tx_config.period * tx_n).checked_sub(time_elapsed)
255            {
256                thread::sleep(time_left);
257            } else {
258                debug!(
259                    "Elapsed time larger than the time needed for sleep: \
260                     time_elapsed={:?} tx_n={}",
261                    time_elapsed, tx_n
262                );
263            }
264        }
265    }
266}
267
268/// This tx generator directly push simple transactions and erc20 transactions
269/// into blocks. It's used in Ethereum e2d replay test.
270pub struct DirectTransactionGenerator {
271    // Key, simple tx, erc20 balance, array index.
272    accounts: HashMap<Address, (KeyPair, Account, U256)>,
273    address_by_index: Vec<Address>,
274    erc20_address: Address,
275}
276
277#[allow(deprecated)]
278impl DirectTransactionGenerator {
279    const MAX_TOTAL_ACCOUNTS: usize = 100_000;
280
281    pub fn new(
282        start_key_pair: KeyPair, contract_creator: &Address,
283        start_balance: U256, start_erc20_balance: U256,
284    ) -> DirectTransactionGenerator {
285        let start_address = public_to_address(start_key_pair.public(), true);
286        let info = (
287            start_key_pair,
288            Account::new_empty_with_balance(
289                &start_address.with_native_space(),
290                &start_balance,
291                &0.into(), /* nonce */
292            ),
293            start_erc20_balance,
294        );
295        let mut accounts = HashMap::<Address, (KeyPair, Account, U256)>::new();
296        accounts.insert(start_address, info);
297        let address_by_index = vec![start_address];
298
299        let mut erc20_address = cal_contract_address(
300            CreateContractAddressType::FromSenderNonceAndCodeHash,
301            // A fake block_number. There field is unnecessary in Ethereum
302            // replay test.
303            contract_creator,
304            &0.into(),
305            // A fake code. There field is unnecessary in Ethereum replay test.
306            &[],
307        )
308        .0;
309        erc20_address.set_contract_type_bits();
310
311        debug!(
312            "Special Transaction Generator: erc20 contract address: {:?}",
313            erc20_address
314        );
315
316        DirectTransactionGenerator {
317            accounts,
318            address_by_index,
319            erc20_address,
320        }
321    }
322
323    pub fn generate_transactions(
324        &mut self, block_size_limit: &mut usize, mut num_txs_simple: usize,
325        mut num_txs_erc20: usize, chain_id: u32,
326    ) -> Vec<Arc<SignedTransaction>> {
327        let mut result = vec![];
328        // Generate new address with 10% probability
329        while num_txs_simple > 0 {
330            let number_of_accounts = self.address_by_index.len();
331
332            let sender_index: usize = random_range(0..number_of_accounts);
333            let sender_address =
334                *self.address_by_index.get(sender_index).unwrap();
335            let sender_kp;
336            let sender_balance;
337            let sender_nonce;
338            {
339                let sender_info = self.accounts.get(&sender_address).unwrap();
340                sender_kp = sender_info.0.clone();
341                sender_balance = sender_info.1.balance;
342                sender_nonce = sender_info.1.nonce;
343            }
344
345            let gas = U256::from(100_000u64);
346            let gas_price = U256::from(1u64);
347            let transaction_fee = U256::from(100_000u64);
348
349            if sender_balance <= transaction_fee {
350                self.accounts.remove(&sender_address);
351                self.address_by_index.swap_remove(sender_index);
352                continue;
353            }
354
355            let balance_to_transfer = U256::try_from(
356                H512::random().into_uint() % U512::from(sender_balance),
357            )
358            .unwrap();
359
360            let is_send_to_new_address = (number_of_accounts
361                <= Self::MAX_TOTAL_ACCOUNTS)
362                && ((number_of_accounts < 10)
363                    || (rand::thread_rng().random_range(0..10) == 0));
364
365            let receiver_address = match is_send_to_new_address {
366                false => {
367                    let index: usize = random_range(0..number_of_accounts);
368                    *self.address_by_index.get(index).unwrap()
369                }
370                true => loop {
371                    let kp =
372                        Random.generate().expect("Fail to generate KeyPair.");
373                    let address = public_to_address(kp.public(), true);
374                    if let std::collections::hash_map::Entry::Vacant(e) =
375                        self.accounts.entry(address)
376                    {
377                        e.insert((
378                            kp,
379                            Account::new_empty_with_balance(
380                                &address.with_native_space(),
381                                &0.into(), /* balance */
382                                &0.into(), /* nonce */
383                            ),
384                            0.into(),
385                        ));
386                        self.address_by_index.push(address);
387
388                        break address;
389                    }
390                },
391            };
392
393            let tx: Transaction = NativeTransaction {
394                nonce: sender_nonce,
395                gas_price,
396                gas,
397                value: balance_to_transfer,
398                action: Action::Call(receiver_address),
399                storage_limit: 0,
400                // FIXME: We will have to setup TRANSACTION_EPOCH_BOUND to a
401                // large value to avoid FIXME: this sloppy zero
402                // becomes an issue in the experiments.
403                epoch_height: 0,
404                chain_id,
405                data: vec![0u8; 128],
406            }
407            .into();
408            let signed_transaction = tx.sign(sender_kp.secret());
409            let rlp_size = signed_transaction.transaction.rlp_bytes().len();
410            if *block_size_limit <= rlp_size {
411                break;
412            }
413            *block_size_limit -= rlp_size;
414
415            self.accounts.get_mut(&sender_address).unwrap().1.balance -=
416                balance_to_transfer;
417            self.accounts.get_mut(&sender_address).unwrap().1.nonce += 1.into();
418            self.accounts.get_mut(&receiver_address).unwrap().1.balance +=
419                balance_to_transfer;
420
421            result.push(Arc::new(signed_transaction));
422
423            num_txs_simple -= 1;
424        }
425
426        while num_txs_erc20 > 0 {
427            let number_of_accounts = self.address_by_index.len();
428
429            let sender_index: usize = random_range(0..number_of_accounts);
430            let sender_address =
431                *self.address_by_index.get(sender_index).unwrap();
432            let sender_kp;
433            let sender_balance;
434            let sender_erc20_balance;
435            let sender_nonce;
436            {
437                let sender_info = self.accounts.get(&sender_address).unwrap();
438                sender_kp = sender_info.0.clone();
439                sender_balance = sender_info.1.balance;
440                sender_erc20_balance = sender_info.2;
441                sender_nonce = sender_info.1.nonce;
442            }
443
444            let gas = U256::from(100_000u64);
445            let gas_price = U256::from(1u64);
446            let transaction_fee = U256::from(100_000u64);
447
448            if sender_balance <= transaction_fee {
449                self.accounts.remove(&sender_address);
450                self.address_by_index.swap_remove(sender_index);
451                continue;
452            }
453
454            let balance_to_transfer = if sender_erc20_balance == 0.into() {
455                continue;
456            } else {
457                U256::try_from(
458                    H512::random().into_uint()
459                        % U512::from(sender_erc20_balance),
460                )
461                .unwrap()
462            };
463
464            let receiver_index = random_range(0..number_of_accounts);
465            let receiver_address =
466                *self.address_by_index.get(receiver_index).unwrap();
467
468            if receiver_index == sender_index {
469                continue;
470            }
471
472            // Calls transfer of ERC20 contract.
473            let tx_data = (String::new()
474                + "a9059cbb000000000000000000000000"
475                + &receiver_address.0.to_hex::<String>()[2..]
476                + {
477                    let h: H256 =
478                        BigEndianHash::from_uint(&balance_to_transfer);
479                    &h.0.to_hex::<String>()[2..]
480                })
481            .from_hex()
482            .unwrap();
483
484            let tx: Transaction = NativeTransaction {
485                nonce: sender_nonce,
486                gas_price,
487                gas,
488                value: 0.into(),
489                action: Action::Call(self.erc20_address),
490                storage_limit: 0,
491                // FIXME: We will have to setup TRANSACTION_EPOCH_BOUND to a
492                // large value to avoid FIXME: this sloppy zero
493                // becomes an issue in the experiments.
494                epoch_height: 0,
495                chain_id,
496                data: tx_data,
497            }
498            .into();
499            let signed_transaction = tx.sign(sender_kp.secret());
500            let rlp_size = signed_transaction.transaction.rlp_bytes().len();
501            if *block_size_limit <= rlp_size {
502                break;
503            }
504            *block_size_limit -= rlp_size;
505
506            self.accounts.get_mut(&sender_address).unwrap().2 -=
507                balance_to_transfer;
508            self.accounts.get_mut(&sender_address).unwrap().1.nonce += 1.into();
509            self.accounts.get_mut(&receiver_address).unwrap().2 +=
510                balance_to_transfer;
511
512            result.push(Arc::new(signed_transaction));
513
514            num_txs_erc20 -= 1;
515        }
516
517        result
518    }
519}