|
|
|
@ -5,6 +5,7 @@ import ( |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
const ( |
|
|
|
|
// MaxNumberOfTransactions is the max number of transaction per a block.
|
|
|
|
|
MaxNumberOfTransactions = 100 |
|
|
|
|
) |
|
|
|
|
|
|
|
|
@ -16,9 +17,9 @@ type UTXOPool struct { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// VerifyTransactions verifies if a list of transactions valid.
|
|
|
|
|
func (utxopool *UTXOPool) VerifyTransactions(transactions []*Transaction) bool { |
|
|
|
|
func (utxoPool *UTXOPool) VerifyTransactions(transactions []*Transaction) bool { |
|
|
|
|
spentTXOs := make(map[string]map[string]map[int]bool) |
|
|
|
|
if utxopool != nil { |
|
|
|
|
if utxoPool != nil { |
|
|
|
|
for _, tx := range transactions { |
|
|
|
|
inTotal := 0 |
|
|
|
|
// Calculate the sum of TxInput
|
|
|
|
@ -41,7 +42,7 @@ func (utxopool *UTXOPool) VerifyTransactions(transactions []*Transaction) bool { |
|
|
|
|
spentTXOs[in.Address][inTxID][index] = true |
|
|
|
|
|
|
|
|
|
// Sum the balance up to the inTotal.
|
|
|
|
|
if val, ok := utxopool.utxo[in.Address][inTxID][index]; ok { |
|
|
|
|
if val, ok := utxoPool.utxo[in.Address][inTxID][index]; ok { |
|
|
|
|
inTotal += val |
|
|
|
|
} else { |
|
|
|
|
return false |
|
|
|
@ -61,111 +62,114 @@ func (utxopool *UTXOPool) VerifyTransactions(transactions []*Transaction) bool { |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// VerifyOneTransactionAndUpdate verifies if a list of transactions valid.
|
|
|
|
|
func (utxopool *UTXOPool) VerifyOneTransaction(tx *Transaction) bool { |
|
|
|
|
spentTXOs := make(map[string]map[string]map[int]bool) |
|
|
|
|
txID := hex.EncodeToString(tx.ID) |
|
|
|
|
inTotal := 0 |
|
|
|
|
// Calculate the sum of TxInput
|
|
|
|
|
for _, in := range tx.TxInput { |
|
|
|
|
inTxID := hex.EncodeToString(in.TxID) |
|
|
|
|
index := in.TxOutputIndex |
|
|
|
|
// Check if the transaction with the addres is spent or not.
|
|
|
|
|
if val, ok := utxopool.utxo[in.Address][inTxID][index]; ok { |
|
|
|
|
if spentTXOs[in.Address][inTxID][index] { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
inTotal += val |
|
|
|
|
} else { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
// Mark the transactions with the address and index spent.
|
|
|
|
|
if _, ok := spentTXOs[in.Address]; !ok { |
|
|
|
|
spentTXOs[in.Address] = make(map[string]map[int]bool) |
|
|
|
|
} |
|
|
|
|
if _, ok := spentTXOs[in.Address][inTxID]; !ok { |
|
|
|
|
spentTXOs[in.Address][inTxID] = make(map[int]bool) |
|
|
|
|
} |
|
|
|
|
spentTXOs[in.Address][inTxID][index] = true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
outTotal := 0 |
|
|
|
|
// Calculate the sum of TxOutput
|
|
|
|
|
for index, out := range tx.TxOutput { |
|
|
|
|
if val, ok := spentTXOs[out.Address][txID][index]; ok { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
outTotal += out.Value |
|
|
|
|
} |
|
|
|
|
if inTotal != outTotal { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
// VerifyOneTransaction verifies if a list of transactions valid.
|
|
|
|
|
func (utxoPool *UTXOPool) VerifyOneTransaction(tx *Transaction) bool { |
|
|
|
|
spentTXOs := make(map[string]map[string]map[int]bool) |
|
|
|
|
txID := hex.EncodeToString(tx.ID) |
|
|
|
|
inTotal := 0 |
|
|
|
|
// Calculate the sum of TxInput
|
|
|
|
|
for _, in := range tx.TxInput { |
|
|
|
|
inTxID := hex.EncodeToString(in.TxID) |
|
|
|
|
index := in.TxOutputIndex |
|
|
|
|
// Check if the transaction with the addres is spent or not.
|
|
|
|
|
if val, ok := utxoPool.utxo[in.Address][inTxID][index]; ok { |
|
|
|
|
if spentTXOs[in.Address][inTxID][index] { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
inTotal += val |
|
|
|
|
} else { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
// Mark the transactions with the address and index spent.
|
|
|
|
|
if _, ok := spentTXOs[in.Address]; !ok { |
|
|
|
|
spentTXOs[in.Address] = make(map[string]map[int]bool) |
|
|
|
|
} |
|
|
|
|
if _, ok := spentTXOs[in.Address][inTxID]; !ok { |
|
|
|
|
spentTXOs[in.Address][inTxID] = make(map[int]bool) |
|
|
|
|
} |
|
|
|
|
spentTXOs[in.Address][inTxID][index] = true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
outTotal := 0 |
|
|
|
|
// Calculate the sum of TxOutput
|
|
|
|
|
for index, out := range tx.TxOutput { |
|
|
|
|
if _, ok := spentTXOs[out.Address][txID][index]; ok { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
outTotal += out.Value |
|
|
|
|
} |
|
|
|
|
if inTotal != outTotal { |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (utxopool *UTXOPool) Update(tx* Transaction) { |
|
|
|
|
if utxopool != nil { |
|
|
|
|
txID := hex.EncodeToString(tx.ID) |
|
|
|
|
|
|
|
|
|
// Remove
|
|
|
|
|
for _, in := range tx.TxInput { |
|
|
|
|
inTxID := hex.EncodeToString(in.TxID) |
|
|
|
|
delete(utxopool.utxo[in.Address][inTxID], in.TxOutputIndex) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Update
|
|
|
|
|
for index, out := range tx.TxOutput { |
|
|
|
|
if _, ok := utxopool.utxo[out.Address]; !ok { |
|
|
|
|
utxopool.utxo[out.Address] = make(map[string]map[int]int) |
|
|
|
|
utxopool.utxo[out.Address][txID] = make(map[int]int) |
|
|
|
|
} |
|
|
|
|
if _, ok := utxopool.utxo[out.Address][txID]; !ok { |
|
|
|
|
utxopool.utxo[out.Address][txID] = make(map[int]int) |
|
|
|
|
} |
|
|
|
|
utxopool.utxo[out.Address][txID][index] = out.Value |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// UpdateOneTransaction updates utxoPool in respect to the new Transaction.
|
|
|
|
|
func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) { |
|
|
|
|
if utxoPool != nil { |
|
|
|
|
txID := hex.EncodeToString(tx.ID) |
|
|
|
|
|
|
|
|
|
// Remove
|
|
|
|
|
for _, in := range tx.TxInput { |
|
|
|
|
inTxID := hex.EncodeToString(in.TxID) |
|
|
|
|
delete(utxoPool.utxo[in.Address][inTxID], in.TxOutputIndex) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Update
|
|
|
|
|
for index, out := range tx.TxOutput { |
|
|
|
|
if _, ok := utxoPool.utxo[out.Address]; !ok { |
|
|
|
|
utxoPool.utxo[out.Address] = make(map[string]map[int]int) |
|
|
|
|
utxoPool.utxo[out.Address][txID] = make(map[int]int) |
|
|
|
|
} |
|
|
|
|
if _, ok := utxoPool.utxo[out.Address][txID]; !ok { |
|
|
|
|
utxoPool.utxo[out.Address][txID] = make(map[int]int) |
|
|
|
|
} |
|
|
|
|
utxoPool.utxo[out.Address][txID][index] = out.Value |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func (utxopool *UTXOPool) VerifyOneTransactionAndUpdate(tx *Transaction) bool { |
|
|
|
|
if utxopool.VerifyOneTransaction(tx) { |
|
|
|
|
utxopool.Update(tx) |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
return false |
|
|
|
|
// VerifyOneTransactionAndUpdate verifies and update a valid transaction.
|
|
|
|
|
// Return false if the transaction is not valid.
|
|
|
|
|
func (utxoPool *UTXOPool) VerifyOneTransactionAndUpdate(tx *Transaction) bool { |
|
|
|
|
if utxoPool.VerifyOneTransaction(tx) { |
|
|
|
|
utxoPool.UpdateOneTransaction(tx) |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// VerifyAndUpdate verifies a list of transactions and update utxoPool.
|
|
|
|
|
func (utxopool *UTXOPool) VerifyAndUpdate(transactions []*Transaction) bool { |
|
|
|
|
if utxopool.VerifyTransactions(transactions) { |
|
|
|
|
utxopool.Update(transactions) |
|
|
|
|
func (utxoPool *UTXOPool) VerifyAndUpdate(transactions []*Transaction) bool { |
|
|
|
|
if utxoPool.VerifyTransactions(transactions) { |
|
|
|
|
utxoPool.Update(transactions) |
|
|
|
|
return true |
|
|
|
|
} |
|
|
|
|
return false |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Update utxo balances with a list of new transactions.
|
|
|
|
|
func (utxopool *UTXOPool) Update(transactions []*Transaction) { |
|
|
|
|
if utxopool != nil { |
|
|
|
|
func (utxoPool *UTXOPool) Update(transactions []*Transaction) { |
|
|
|
|
if utxoPool != nil { |
|
|
|
|
for _, tx := range transactions { |
|
|
|
|
curTxID := hex.EncodeToString(tx.ID) |
|
|
|
|
|
|
|
|
|
// Remove
|
|
|
|
|
for _, in := range tx.TxInput { |
|
|
|
|
inTxID := hex.EncodeToString(in.TxID) |
|
|
|
|
delete(utxopool.utxo[in.Address][inTxID], in.TxOutputIndex) |
|
|
|
|
delete(utxoPool.utxo[in.Address][inTxID], in.TxOutputIndex) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Update
|
|
|
|
|
for index, out := range tx.TxOutput { |
|
|
|
|
if _, ok := utxopool.utxo[out.Address]; !ok { |
|
|
|
|
utxopool.utxo[out.Address] = make(map[string]map[int]int) |
|
|
|
|
utxopool.utxo[out.Address][curTxID] = make(map[int]int) |
|
|
|
|
if _, ok := utxoPool.utxo[out.Address]; !ok { |
|
|
|
|
utxoPool.utxo[out.Address] = make(map[string]map[int]int) |
|
|
|
|
utxoPool.utxo[out.Address][curTxID] = make(map[int]int) |
|
|
|
|
} |
|
|
|
|
if _, ok := utxopool.utxo[out.Address][curTxID]; !ok { |
|
|
|
|
utxopool.utxo[out.Address][curTxID] = make(map[int]int) |
|
|
|
|
if _, ok := utxoPool.utxo[out.Address][curTxID]; !ok { |
|
|
|
|
utxoPool.utxo[out.Address][curTxID] = make(map[int]int) |
|
|
|
|
} |
|
|
|
|
utxopool.utxo[out.Address][curTxID][index] = out.Value |
|
|
|
|
utxoPool.utxo[out.Address][curTxID][index] = out.Value |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -191,13 +195,14 @@ func CreateUTXOPoolFromGenesisBlockChain(bc *Blockchain) *UTXOPool { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// SelectTransactionsForNewBlock returns a list of index of valid transactions for the new block.
|
|
|
|
|
func (utxoPool *UTXOPool) SelectTransactionsForNewBlock(transactions []*Transaction) (selected, unselected []*Transaction) { |
|
|
|
|
selected, unselected = []*Transaction{}, []*Transaction{} |
|
|
|
|
for id, tx := range transactions { |
|
|
|
|
if len(selected) < MaxNumberOfTransactions && utxoPool.VerifyOneTransactionAndUpdate(tx) { |
|
|
|
|
append(selected, tx) |
|
|
|
|
} else { |
|
|
|
|
append(unselected, tx) |
|
|
|
|
} |
|
|
|
|
func (utxoPool *UTXOPool) SelectTransactionsForNewBlock(transactions []*Transaction) ([]*Transaction, []*Transaction) { |
|
|
|
|
selected, unselected := []*Transaction{}, []*Transaction{} |
|
|
|
|
for _, tx := range transactions { |
|
|
|
|
if len(selected) < MaxNumberOfTransactions && utxoPool.VerifyOneTransactionAndUpdate(tx) { |
|
|
|
|
selected = append(selected, tx) |
|
|
|
|
} else { |
|
|
|
|
unselected = append(unselected, tx) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return selected, unselected |
|
|
|
|
} |
|
|
|
|