@ -1,6 +1,7 @@
package main
package main
import (
import (
"encoding/binary"
"encoding/hex"
"encoding/hex"
"flag"
"flag"
"fmt"
"fmt"
@ -57,12 +58,13 @@ type TxInfo struct {
// token (1000) to each address in [0 - N). See node.AddTestingAddresses()
// token (1000) to each address in [0 - N). See node.AddTestingAddresses()
//
//
// Params:
// Params:
// subsetId - the which subset of the utxo to work on (used to select addresses)
// shardID - the shardID for current shard
// shardID - the shardID for current shard
// dataNodes - nodes containing utxopools of all shards
// dataNodes - nodes containing utxopools of all shards
// Returns:
// Returns:
// all single-shard txs
// all single-shard txs
// all cross-shard txs
// all cross-shard txs
func generateSimulatedTransactions ( shardID int , dataNodes [ ] * node . Node ) ( [ ] * blockchain . Transaction , [ ] * blockchain . Transaction ) {
func generateSimulatedTransactions ( subsetId , numSubset int , s hardID int , dataNodes [ ] * node . Node ) ( [ ] * blockchain . Transaction , [ ] * blockchain . Transaction ) {
/ *
/ *
UTXO map structure :
UTXO map structure :
address - [
address - [
@ -86,33 +88,32 @@ func generateSimulatedTransactions(shardID int, dataNodes []*node.Node) ([]*bloc
UTXOLOOP :
UTXOLOOP :
// Loop over all addresses
// Loop over all addresses
for address , txMap := range dataNodes [ shardID ] . UtxoPool . UtxoMap {
for address , txMap := range dataNodes [ shardID ] . UtxoPool . UtxoMap {
txInfo . address = address
if int ( binary . BigEndian . Uint32 ( address [ : ] ) ) % numSubset == subsetId % numSubset { // Work on one subset of utxo at a time
// Loop over all txIds for the address
txInfo . address = address
for txIdStr , utxoMap := range txMap {
// Loop over all txIds for the address
// Parse TxId
for txIdStr , utxoMap := range txMap {
id , err := hex . DecodeString ( txIdStr )
// Parse TxId
if err != nil {
id , err := hex . DecodeString ( txIdStr )
continue
if err != nil {
}
copy ( txInfo . id [ : ] , id [ : ] )
// Loop over all utxos for the txId
for index , value := range utxoMap {
txInfo . index = index
txInfo . value = value
randNum := rand . Intn ( 100 )
// 30% sample rate to select UTXO to use for new transactions
if randNum >= 30 {
continue
continue
}
}
if setting . crossShard && randNum < 10 { // 1/3 cross shard transactions: add another txinput from another shard
copy ( txInfo . id [ : ] , id [ : ] )
generateCrossShardTx ( & txInfo )
} else {
// Loop over all utxos for the txId
generateSingleShardTx ( & txInfo )
for index , value := range utxoMap {
}
txInfo . index = index
if txInfo . txCount >= setting . maxNumTxsPerBatch {
txInfo . value = value
break UTXOLOOP
randNum := rand . Intn ( 100 )
if setting . crossShard && randNum < 30 { // 1/3 cross shard transactions: add another txinput from another shard
generateCrossShardTx ( & txInfo )
} else {
generateSingleShardTx ( & txInfo )
}
if txInfo . txCount >= setting . maxNumTxsPerBatch {
break UTXOLOOP
}
}
}
}
}
}
}
@ -125,8 +126,8 @@ UTXOLOOP:
func generateCrossShardTx ( txInfo * TxInfo ) {
func generateCrossShardTx ( txInfo * TxInfo ) {
nodeShardID := txInfo . dataNodes [ txInfo . shardID ] . Consensus . ShardID
nodeShardID := txInfo . dataNodes [ txInfo . shardID ] . Consensus . ShardID
// shard with neighboring Id
// a random shard to spend money to
crossShardId := ( int ( nodeShardID ) + 1 ) % len ( txInfo . dataNodes )
crossShardId := rand . Intn ( len ( txInfo . dataNodes ) )
crossShardNode := txInfo . dataNodes [ crossShardId ]
crossShardNode := txInfo . dataNodes [ crossShardId ]
crossShardUtxosMap := crossShardNode . UtxoPool . UtxoMap [ txInfo . address ]
crossShardUtxosMap := crossShardNode . UtxoPool . UtxoMap [ txInfo . address ]
@ -241,6 +242,8 @@ func main() {
configFile := flag . String ( "config_file" , "local_config.txt" , "file containing all ip addresses and config" )
configFile := flag . String ( "config_file" , "local_config.txt" , "file containing all ip addresses and config" )
maxNumTxsPerBatch := flag . Int ( "max_num_txs_per_batch" , 100000 , "number of transactions to send per message" )
maxNumTxsPerBatch := flag . Int ( "max_num_txs_per_batch" , 100000 , "number of transactions to send per message" )
logFolder := flag . String ( "log_folder" , "latest" , "the folder collecting the logs of this execution" )
logFolder := flag . String ( "log_folder" , "latest" , "the folder collecting the logs of this execution" )
numSubset := flag . Int ( "numSubset" , 3 , "the number of subsets of utxos to process separately" )
duration := flag . Int ( "duration" , 60 , "duration of the tx generation in second" )
flag . Parse ( )
flag . Parse ( )
// Read the configs
// Read the configs
@ -289,6 +292,7 @@ func main() {
// Add it to blockchain
// Add it to blockchain
utxoPoolMutex . Lock ( )
utxoPoolMutex . Lock ( )
node . AddNewBlock ( block )
node . AddNewBlock ( block )
node . UpdateUtxoAndState ( block )
utxoPoolMutex . Unlock ( )
utxoPoolMutex . Unlock ( )
} else {
} else {
continue
continue
@ -302,49 +306,25 @@ func main() {
go func ( ) {
go func ( ) {
clientNode . StartServer ( clientPort )
clientNode . StartServer ( clientPort )
} ( )
} ( )
}
}
// Transaction generation process
// Transaction generation process
time . Sleep ( 10 * time . Second ) // wait for nodes to be ready
time . Sleep ( 10 * time . Second ) // wait for nodes to be ready
start := time . Now ( )
start := time . Now ( )
totalTime := 60.0 //run for 1 minutes
totalTime := float64 ( * duration )
batchCounter := 0
for true {
for true {
t := time . Now ( )
t := time . Now ( )
if t . Sub ( start ) . Seconds ( ) >= totalTime {
if t . Sub ( start ) . Seconds ( ) >= totalTime {
log . Debug ( "Generator timer ended." , "duration" , ( int ( t . Sub ( start ) ) ) , "startTime" , start , "totalTime" , totalTime )
log . Debug ( "Generator timer ended." , "duration" , ( int ( t . Sub ( start ) ) ) , "startTime" , start , "totalTime" , totalTime )
break
break
}
}
for shardId := 0 ; shardId < len ( nodes ) ; shardId ++ {
allCrossTxs := [ ] * blockchain . Transaction { }
constructAndSendTransaction ( batchCounter , * numSubset , shardId , leaders , nodes , clientNode , clientPort )
// Generate simulated transactions
for i , leader := range leaders {
txs , crossTxs := generateSimulatedTransactions ( i , nodes )
allCrossTxs = append ( allCrossTxs , crossTxs ... )
log . Debug ( "[Generator] Sending single-shard txs ..." , "leader" , leader , "numTxs" , len ( txs ) , "numCrossTxs" , len ( crossTxs ) )
msg := proto_node . ConstructTransactionListMessage ( txs )
p2p . SendMessage ( leader , msg )
// Note cross shard txs are later sent in batch
}
if len ( allCrossTxs ) > 0 {
log . Debug ( "[Generator] Broadcasting cross-shard txs ..." , "allCrossTxs" , len ( allCrossTxs ) )
msg := proto_node . ConstructTransactionListMessage ( allCrossTxs )
p2p . BroadcastMessage ( leaders , msg )
// Put cross shard tx into a pending list waiting for proofs from leaders
if clientPort != "" {
clientNode . Client . PendingCrossTxsMutex . Lock ( )
for _ , tx := range allCrossTxs {
clientNode . Client . PendingCrossTxs [ tx . ID ] = tx
}
clientNode . Client . PendingCrossTxsMutex . Unlock ( )
}
}
}
batchCounter ++
time . Sleep ( 5 00 * time . Millisecond ) // Send a batch of transactions periodically
time . Sleep ( 1000 * time . Millisecond )
}
}
// Send a stop message to stop the nodes at the end
// Send a stop message to stop the nodes at the end
@ -352,3 +332,32 @@ func main() {
peers := append ( config . GetValidators ( ) , leaders ... )
peers := append ( config . GetValidators ( ) , leaders ... )
p2p . BroadcastMessage ( peers , msg )
p2p . BroadcastMessage ( peers , msg )
}
}
func constructAndSendTransaction ( subsetId , numSubset , shardId int , leaders [ ] p2p . Peer , nodes [ ] * node . Node , clientNode * node . Node , clientPort string ) {
allCrossTxs := [ ] * blockchain . Transaction { }
// Generate simulated transactions
leader := leaders [ shardId ]
txs , crossTxs := generateSimulatedTransactions ( subsetId , numSubset , shardId , nodes )
allCrossTxs = append ( allCrossTxs , crossTxs ... )
log . Debug ( "[Generator] Sending single-shard txs ..." , "leader" , leader , "numTxs" , len ( txs ) , "numCrossTxs" , len ( crossTxs ) )
msg := proto_node . ConstructTransactionListMessage ( txs )
p2p . SendMessage ( leader , msg )
// Note cross shard txs are later sent in batch
if len ( allCrossTxs ) > 0 {
log . Debug ( "[Generator] Broadcasting cross-shard txs ..." , "allCrossTxs" , len ( allCrossTxs ) )
msg := proto_node . ConstructTransactionListMessage ( allCrossTxs )
p2p . BroadcastMessage ( leaders , msg )
// Put cross shard tx into a pending list waiting for proofs from leaders
if clientPort != "" {
clientNode . Client . PendingCrossTxsMutex . Lock ( )
for _ , tx := range allCrossTxs {
clientNode . Client . PendingCrossTxs [ tx . ID ] = tx
}
clientNode . Client . PendingCrossTxsMutex . Unlock ( )
}
}
}