Modify utxo update code to support cross tx unlock-to-abort

pull/15/head
Rongjian Lan 6 years ago
parent 10c1b90190
commit bb5518c852
  1. 105
      blockchain/utxopool.go

@ -25,8 +25,9 @@ type UTXOPool struct {
*/
UtxoMap map[string]map[string]map[int]int
ShardId uint32
mutex sync.Mutex
lockedUtxoMap map[string]map[string]map[int]int
ShardId uint32
mutex sync.Mutex
}
// VerifyTransactions verifies if a list of transactions valid for this shard.
@ -136,44 +137,98 @@ func (utxoPool *UTXOPool) Update(transactions []*Transaction) {
// UpdateOneTransaction updates utxoPool in respect to the new Transaction.
func (utxoPool *UTXOPool) UpdateOneTransaction(tx *Transaction) {
isUnlockTx := len(tx.Proofs) != 0
unlockToCommit := true
if isUnlockTx {
for _, proof := range tx.Proofs {
if !proof.RejectOrAccept {
unlockToCommit = false // if any proof is a rejection, they it's a unlock-to-abort tx. Otherwise, it's unlock-to-commit
}
}
}
isCrossShard := false
// check whether it's a cross shard tx.
for _, in := range tx.TxInput {
if in.ShardId != utxoPool.ShardId {
isCrossShard = true
break
}
}
utxoPool.mutex.Lock()
defer utxoPool.mutex.Unlock()
if utxoPool != nil {
txID := hex.EncodeToString(tx.ID[:])
isCrossShard := false
// Remove
if !isUnlockTx {
for _, in := range tx.TxInput {
// Only check the input for my own shard.
if in.ShardId != utxoPool.ShardId {
isCrossShard = true
continue
}
// NOTE: for the locking phase of cross tx, the utxo is simply removed from the pool.
inTxID := hex.EncodeToString(in.TxID[:])
utxoPool.DeleteOneBalanceItem(in.Address, inTxID, in.TxOutputIndex)
value := utxoPool.UtxoMap[in.Address][inTxID][in.TxOutputIndex]
utxoPool.DeleteOneUtxo(in.Address, inTxID, in.TxOutputIndex)
if isCrossShard {
// put the delete (locked) utxo into a separate locked utxo pool
inTxID := hex.EncodeToString(in.TxID[:])
if _, ok := utxoPool.lockedUtxoMap[in.Address]; !ok {
utxoPool.lockedUtxoMap[in.Address] = make(map[string]map[int]int)
utxoPool.lockedUtxoMap[in.Address][inTxID] = make(map[int]int)
}
if _, ok := utxoPool.lockedUtxoMap[in.Address][inTxID]; !ok {
utxoPool.lockedUtxoMap[in.Address][inTxID] = make(map[int]int)
}
utxoPool.lockedUtxoMap[in.Address][inTxID][in.TxOutputIndex] = value
}
}
}
// Update
if !isCrossShard {
for index, out := range tx.TxOutput {
// Only check the input for my own shard.
if out.ShardId != utxoPool.ShardId {
continue
}
if _, ok := utxoPool.UtxoMap[out.Address]; !ok {
utxoPool.UtxoMap[out.Address] = make(map[string]map[int]int)
utxoPool.UtxoMap[out.Address][txID] = make(map[int]int)
if !isCrossShard || isUnlockTx {
if !unlockToCommit {
// unlock-to-abort, bring back (unlock) the utxo input
for _, in := range tx.TxInput {
// Only unlock the input for my own shard.
if in.ShardId != utxoPool.ShardId {
isCrossShard = true
continue
}
// Simply bring back the locked (removed) utxo
inTxID := hex.EncodeToString(in.TxID[:])
if _, ok := utxoPool.UtxoMap[in.Address]; !ok {
utxoPool.UtxoMap[in.Address] = make(map[string]map[int]int)
utxoPool.UtxoMap[in.Address][inTxID] = make(map[int]int)
}
if _, ok := utxoPool.UtxoMap[in.Address][inTxID]; !ok {
utxoPool.UtxoMap[in.Address][inTxID] = make(map[int]int)
}
value := utxoPool.lockedUtxoMap[in.Address][inTxID][in.TxOutputIndex]
utxoPool.UtxoMap[in.Address][inTxID][in.TxOutputIndex] = value
utxoPool.DeleteOneLockedUtxo(in.Address, inTxID, in.TxOutputIndex)
}
if _, ok := utxoPool.UtxoMap[out.Address][txID]; !ok {
utxoPool.UtxoMap[out.Address][txID] = make(map[int]int)
} else {
// normal utxo output update
for index, out := range tx.TxOutput {
// Only check the input for my own shard.
if out.ShardId != utxoPool.ShardId {
continue
}
if _, ok := utxoPool.UtxoMap[out.Address]; !ok {
utxoPool.UtxoMap[out.Address] = make(map[string]map[int]int)
utxoPool.UtxoMap[out.Address][txID] = make(map[int]int)
}
if _, ok := utxoPool.UtxoMap[out.Address][txID]; !ok {
utxoPool.UtxoMap[out.Address][txID] = make(map[int]int)
}
utxoPool.UtxoMap[out.Address][txID][index] = out.Value
}
utxoPool.UtxoMap[out.Address][txID][index] = out.Value
}
} // If it's a cross shard Tx, then don't update so the input UTXOs are locked (removed), and the money is not spendable until unlock-to-commit or unlock-to-abort
} // If it's a cross shard locking Tx, then don't update so the input UTXOs are locked (removed), and the money is not spendable until unlock-to-commit or unlock-to-abort
// TODO: unlock-to-commit and unlock-to-abort
}
@ -210,6 +265,7 @@ func CreateUTXOPoolFromTransaction(tx *Transaction, shardId uint32) *UTXOPool {
var utxoPool UTXOPool
txID := hex.EncodeToString(tx.ID[:])
utxoPool.UtxoMap = make(map[string]map[string]map[int]int)
utxoPool.lockedUtxoMap = make(map[string]map[string]map[int]int)
for index, out := range tx.TxOutput {
utxoPool.UtxoMap[out.Address] = make(map[string]map[int]int)
utxoPool.UtxoMap[out.Address][txID] = make(map[int]int)
@ -260,7 +316,7 @@ func getShardTxInput(transaction *Transaction, shardId uint32) []TXInput {
}
// DeleteOneBalanceItem deletes one balance item of UTXOPool and clean up if possible.
func (utxoPool *UTXOPool) DeleteOneBalanceItem(address, txID string, index int) {
func (utxoPool *UTXOPool) DeleteOneUtxo(address, txID string, index int) {
delete(utxoPool.UtxoMap[address][txID], index)
if len(utxoPool.UtxoMap[address][txID]) == 0 {
delete(utxoPool.UtxoMap[address], txID)
@ -270,6 +326,17 @@ func (utxoPool *UTXOPool) DeleteOneBalanceItem(address, txID string, index int)
}
}
// DeleteOneBalanceItem deletes one balance item of UTXOPool and clean up if possible.
func (utxoPool *UTXOPool) DeleteOneLockedUtxo(address, txID string, index int) {
delete(utxoPool.lockedUtxoMap[address][txID], index)
if len(utxoPool.lockedUtxoMap[address][txID]) == 0 {
delete(utxoPool.lockedUtxoMap[address], txID)
if len(utxoPool.lockedUtxoMap[address]) == 0 {
delete(utxoPool.lockedUtxoMap, address)
}
}
}
// CleanUp cleans up UTXOPool.
func (utxoPool *UTXOPool) CleanUp() {
for address, txMap := range utxoPool.UtxoMap {

Loading…
Cancel
Save