explicitly return tx verification error and increase block tx num

pull/69/head
Rongjian Lan 6 years ago
parent 028512ad81
commit 514262be2e
  1. 55
      blockchain/utxopool.go
  2. 4
      blockchain/utxopool_test.go
  3. 60
      client/txgen/main.go
  4. 3
      node/node.go
  5. 4
      node/node_handler.go

@ -4,6 +4,7 @@ import (
"bytes"
"encoding/gob"
"encoding/hex"
"errors"
"fmt"
"github.com/dedis/kyber/sign/schnorr"
"github.com/simple-rules/harmony-benchmark/crypto"
@ -77,7 +78,7 @@ func (utxoPool *UTXOPool) VerifyTransactions(transactions []*Transaction) bool {
spentTXOs := make(map[[20]byte]map[string]map[uint32]bool)
if utxoPool != nil {
for _, tx := range transactions {
if valid, crossShard := utxoPool.VerifyOneTransaction(tx, &spentTXOs); !crossShard && !valid {
if err, crossShard := utxoPool.VerifyOneTransaction(tx, &spentTXOs); !crossShard && err != nil {
return false
}
}
@ -114,7 +115,7 @@ func (utxoPool *UTXOPool) VerifyStateBlock(stateBlock *Block) bool {
}
// VerifyOneTransaction verifies if a list of transactions valid.
func (utxoPool *UTXOPool) VerifyOneTransaction(tx *Transaction, spentTXOs *map[[20]byte]map[string]map[uint32]bool) (valid, crossShard bool) {
func (utxoPool *UTXOPool) VerifyOneTransaction(tx *Transaction, spentTXOs *map[[20]byte]map[string]map[uint32]bool) (err error, crossShard bool) {
if len(tx.Proofs) != 0 {
return utxoPool.VerifyUnlockTransaction(tx)
}
@ -133,10 +134,10 @@ func (utxoPool *UTXOPool) VerifyOneTransaction(tx *Transaction, spentTXOs *map[[
inTxID := hex.EncodeToString(in.PreviousOutPoint.TxID[:])
index := in.PreviousOutPoint.Index
// Check if the transaction with the addres is spent or not.
// Check if the transaction with the address is spent or not.
if val, ok := (*spentTXOs)[in.Address][inTxID][index]; ok {
if val {
return false, crossShard
return errors.New("TxInput is already spent"), crossShard
}
}
// Mark the transactions with the address and index spent.
@ -154,7 +155,7 @@ func (utxoPool *UTXOPool) VerifyOneTransaction(tx *Transaction, spentTXOs *map[[
inTotal += val
} else {
utxoPool.mutex.Unlock()
return false, crossShard
return errors.New("Specified TxInput does not exist in utxo pool"), crossShard
}
utxoPool.mutex.Unlock()
}
@ -171,30 +172,30 @@ func (utxoPool *UTXOPool) VerifyOneTransaction(tx *Transaction, spentTXOs *map[[
// TODO: improve this checking logic
if (crossShard && inTotal > outTotal) || (!crossShard && inTotal != outTotal) {
return false, crossShard
return errors.New("Input and output amount doesn't match"), crossShard
}
if inTotal == 0 {
return false, false // Here crossShard is false, because if there is no business for this shard, it's effectively not crossShard no matter what.
return errors.New("Input amount is 0"), false // Here crossShard is false, because if there is no business for this shard, it's effectively not crossShard no matter what.
}
// Verify the signature
pubKey := crypto.Ed25519Curve.Point()
err := pubKey.UnmarshalBinary(tx.PublicKey[:])
if err != nil {
log.Error("Failed to deserialize public key", "error", err)
tempErr := pubKey.UnmarshalBinary(tx.PublicKey[:])
if tempErr != nil {
log.Error("Failed to deserialize public key", "error", tempErr)
}
err = schnorr.Verify(crypto.Ed25519Curve, pubKey, tx.GetContentToVerify(), tx.Signature[:])
if err != nil {
log.Error("Failed to verify signature", "error", err, "public key", pubKey, "pubKey in bytes", tx.PublicKey[:])
return false, crossShard
tempErr = schnorr.Verify(crypto.Ed25519Curve, pubKey, tx.GetContentToVerify(), tx.Signature[:])
if tempErr != nil {
log.Error("Failed to verify signature", "error", tempErr, "public key", pubKey, "pubKey in bytes", tx.PublicKey[:])
return errors.New("Invalid signature"), crossShard
}
return true, crossShard
return nil, crossShard
}
// Verify a cross shard transaction that contains proofs for unlock-to-commit/abort.
func (utxoPool *UTXOPool) VerifyUnlockTransaction(tx *Transaction) (valid, crossShard bool) {
valid = true
func (utxoPool *UTXOPool) VerifyUnlockTransaction(tx *Transaction) (err error, crossShard bool) {
err = nil
crossShard = false // unlock transaction is treated as crossShard=false because it will be finalized now (doesn't need more steps)
txInputs := make(map[TXInput]bool)
for _, curProof := range tx.Proofs {
@ -205,7 +206,7 @@ func (utxoPool *UTXOPool) VerifyUnlockTransaction(tx *Transaction) (valid, cross
for _, txInput := range tx.TxInput {
val, ok := txInputs[txInput]
if !ok || !val {
valid = false
err = errors.New("Invalid unlock transaction: not all proofs are provided for tx inputs")
}
}
return
@ -346,7 +347,7 @@ func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) {
// VerifyOneTransactionAndUpdate verifies and update a valid transaction.
// Return false if the transaction is not valid.
func (utxoPool *UTXOPool) VerifyOneTransactionAndUpdate(tx *Transaction) bool {
if valid, _ := utxoPool.VerifyOneTransaction(tx, nil); valid {
if err, _ := utxoPool.VerifyOneTransaction(tx, nil); err == nil {
utxoPool.UpdateOneTransaction(tx)
return true
}
@ -385,29 +386,33 @@ func CreateUTXOPoolFromGenesisBlockChain(bc *Blockchain) *UTXOPool {
}
// SelectTransactionsForNewBlock returns a list of index of valid transactions for the new block.
func (utxoPool *UTXOPool) SelectTransactionsForNewBlock(transactions []*Transaction, maxNumTxs int) ([]*Transaction, []*Transaction, []*CrossShardTxAndProof) {
selected, unselected, crossShardTxs := []*Transaction{}, []*Transaction{}, []*CrossShardTxAndProof{}
func (utxoPool *UTXOPool) SelectTransactionsForNewBlock(transactions []*Transaction, maxNumTxs int) ([]*Transaction, []*Transaction, []*Transaction, []*CrossShardTxAndProof) {
selected, unselected, invalid, crossShardTxs := []*Transaction{}, []*Transaction{}, []*Transaction{}, []*CrossShardTxAndProof{}
spentTXOs := make(map[[20]byte]map[string]map[uint32]bool)
for _, tx := range transactions {
valid, crossShard := utxoPool.VerifyOneTransaction(tx, &spentTXOs)
err, crossShard := utxoPool.VerifyOneTransaction(tx, &spentTXOs)
if len(selected) < maxNumTxs {
if valid || crossShard {
if err == nil || crossShard {
selected = append(selected, tx)
if crossShard {
proof := CrossShardTxProof{Accept: valid, TxID: tx.ID, TxInput: getShardTxInput(tx, utxoPool.ShardID)}
proof := CrossShardTxProof{Accept: err == nil, TxID: tx.ID, TxInput: getShardTxInput(tx, utxoPool.ShardID)}
txAndProof := CrossShardTxAndProof{tx, &proof}
crossShardTxs = append(crossShardTxs, &txAndProof)
}
} else {
//if err != nil {
// log.Warn("Tx Verification failed", "Error:", err)
//}
unselected = append(unselected, tx)
invalid = append(invalid, tx)
}
} else {
// TODO: discard invalid transactions
unselected = append(unselected, tx)
}
}
return selected, unselected, crossShardTxs
return selected, unselected, invalid, crossShardTxs
}
func getShardTxInput(transaction *Transaction, shardID uint32) []TXInput {

@ -16,7 +16,7 @@ func TestVerifyOneTransactionAndUpdate(t *testing.T) {
t.Error("failed to create a new transaction.")
}
if valid, _ := utxoPool.VerifyOneTransaction(tx, nil); !valid {
if err, _ := utxoPool.VerifyOneTransaction(tx, nil); err != nil {
t.Error("failed to verify a valid transaction.")
}
utxoPool.VerifyOneTransactionAndUpdate(tx)
@ -35,7 +35,7 @@ func TestVerifyOneTransactionFail(t *testing.T) {
}
tx.TxInput = append(tx.TxInput, tx.TxInput[0])
if valid, _ := utxoPool.VerifyOneTransaction(tx, nil); valid {
if err, _ := utxoPool.VerifyOneTransaction(tx, nil); err == nil {
t.Error("Tx with multiple identical TxInput shouldn't be valid")
}
}

@ -1,6 +1,7 @@
package main
import (
"encoding/binary"
"encoding/hex"
"flag"
"fmt"
@ -57,12 +58,13 @@ type TxInfo struct {
// token (1000) to each address in [0 - N). See node.AddTestingAddresses()
//
// Params:
// subsetId - the which subset of the utxo to work on (used to select addresses)
// shardID - the shardID for current shard
// dataNodes - nodes containing utxopools of all shards
// Returns:
// all single-shard txs
// all cross-shard txs
func generateSimulatedTransactions(shardID int, dataNodes []*node.Node) ([]*blockchain.Transaction, []*blockchain.Transaction) {
func generateSimulatedTransactions(batchCounter, batchNum int, shardID int, dataNodes []*node.Node) ([]*blockchain.Transaction, []*blockchain.Transaction) {
/*
UTXO map structure:
address - [
@ -86,33 +88,32 @@ func generateSimulatedTransactions(shardID int, dataNodes []*node.Node) ([]*bloc
UTXOLOOP:
// Loop over all addresses
for address, txMap := range dataNodes[shardID].UtxoPool.UtxoMap {
txInfo.address = address
// Loop over all txIds for the address
for txIdStr, utxoMap := range txMap {
// Parse TxId
id, err := hex.DecodeString(txIdStr)
if err != nil {
continue
}
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 {
if int(binary.BigEndian.Uint32(address[:]))%batchNum == batchCounter%batchNum { // Work on one subset of utxo at a time
txInfo.address = address
// Loop over all txIds for the address
for txIdStr, utxoMap := range txMap {
// Parse TxId
id, err := hex.DecodeString(txIdStr)
if err != nil {
continue
}
if setting.crossShard && randNum < 10 { // 1/3 cross shard transactions: add another txinput from another shard
generateCrossShardTx(&txInfo)
} else {
generateSingleShardTx(&txInfo)
}
if txInfo.txCount >= setting.maxNumTxsPerBatch {
break UTXOLOOP
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)
if setting.crossShard && randNum < 0 { // 1/3 cross shard transactions: add another txinput from another shard
generateCrossShardTx(&txInfo)
} else {
generateSingleShardTx(&txInfo)
}
if txInfo.txCount >= setting.maxNumTxsPerBatch {
break UTXOLOOP
}
}
}
}
@ -289,6 +290,7 @@ func main() {
// Add it to blockchain
utxoPoolMutex.Lock()
node.AddNewBlock(block)
node.UpdateUtxoAndState(block)
utxoPoolMutex.Unlock()
} else {
continue
@ -310,6 +312,7 @@ func main() {
start := time.Now()
totalTime := 60.0 //run for 1 minutes
batchCounter := 0
for true {
t := time.Now()
if t.Sub(start).Seconds() >= totalTime {
@ -320,7 +323,7 @@ func main() {
allCrossTxs := []*blockchain.Transaction{}
// Generate simulated transactions
for i, leader := range leaders {
txs, crossTxs := generateSimulatedTransactions(i, nodes)
txs, crossTxs := generateSimulatedTransactions(batchCounter, 3, i, nodes)
allCrossTxs = append(allCrossTxs, crossTxs...)
log.Debug("[Generator] Sending single-shard txs ...", "leader", leader, "numTxs", len(txs), "numCrossTxs", len(crossTxs))
@ -344,7 +347,8 @@ func main() {
}
}
time.Sleep(500 * time.Millisecond) // Send a batch of transactions periodically
time.Sleep(5000 * time.Millisecond) // Send a batch of transactions periodically
batchCounter++
}
// Send a stop message to stop the nodes at the end

@ -53,7 +53,8 @@ func (node *Node) addPendingTransactions(newTxs []*blockchain.Transaction) {
// Note the pending transaction list will then contain the rest of the txs
func (node *Node) getTransactionsForNewBlock(maxNumTxs int) ([]*blockchain.Transaction, []*blockchain.CrossShardTxAndProof) {
node.pendingTxMutex.Lock()
selected, unselected, crossShardTxs := node.UtxoPool.SelectTransactionsForNewBlock(node.pendingTransactions, maxNumTxs)
selected, unselected, invalid, crossShardTxs := node.UtxoPool.SelectTransactionsForNewBlock(node.pendingTransactions, maxNumTxs)
_ = invalid // invalid txs are discard
node.pendingTransactions = unselected
node.pendingTxMutex.Unlock()
return selected, crossShardTxs

@ -18,9 +18,9 @@ import (
const (
// The max number of transaction per a block.
MaxNumberOfTransactionsPerBlock = 3000
MaxNumberOfTransactionsPerBlock = 10000
// The number of blocks allowed before generating state block
NumBlocksBeforeStateBlock = 10
NumBlocksBeforeStateBlock = 100
)
// NodeHandler handles a new incoming connection.

Loading…
Cancel
Save