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