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