Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify UTXO Frozen Logic #446

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@ run:
- kernel/consensus
- kernel/network
- kernel/permission
- kernel/engines/xuperos/event
- kernel/engines/xuperos/net
- kernel/engines/xuperos/xtoken
- kernel/engines/xuperos/asyncworker
- kernel/engines/xuperos/parachain
- .autogen
- .compile_cache
2 changes: 2 additions & 0 deletions bcs/consensus/tdpos/kernel_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ func (tp *tdposConsensus) runRevokeCandidate(contractCtx contract.KContext) (*co

// runVote 执行投票
// Args:
//
// candidate::候选人钱包地址
// amount::投票者票数
func (tp *tdposConsensus) runVote(contractCtx contract.KContext) (*contract.Response, error) {
Expand Down Expand Up @@ -254,6 +255,7 @@ func (tp *tdposConsensus) runVote(contractCtx contract.KContext) (*contract.Resp
// runRevokeVote 执行选票撤销
// 重构后的候选人撤销
// Args:
//
// candidate::候选人钱包地址
// amount: 投票数
func (tp *tdposConsensus) runRevokeVote(contractCtx contract.KContext) (*contract.Response, error) {
Expand Down
30 changes: 12 additions & 18 deletions bcs/ledger/xledger/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,18 +273,17 @@ func (t *State) GetFrozenBalance(addr string) (*big.Int, error) {
if uErr != nil {
return nil, uErr
}
if uItem.FrozenHeight <= curHeight && uItem.FrozenHeight != -1 {
continue
if uItem.IsFrozen(curHeight) {
utxoFrozen.Add(utxoFrozen, uItem.Amount) // utxo累加
}
utxoFrozen.Add(utxoFrozen, uItem.Amount) // utxo累加
}
if it.Error() != nil {
return nil, it.Error()
}
return utxoFrozen, nil
}

// GetFrozenBalance 查询Address的被冻结的余额 / 未冻结的余额
// GetBalanceDetail 查询Address的被冻结的余额 / 未冻结的余额
func (t *State) GetBalanceDetail(addr string) ([]*pb.BalanceDetailInfo, error) {
addrPrefix := fmt.Sprintf("%s%s_", pb.UTXOTablePrefix, addr)
utxoFrozen := big.NewInt(0)
Expand All @@ -299,11 +298,13 @@ func (t *State) GetBalanceDetail(addr string) ([]*pb.BalanceDetailInfo, error) {
if uErr != nil {
return nil, uErr
}
if uItem.FrozenHeight <= curHeight && uItem.FrozenHeight != -1 {
utxoUnFrozen.Add(utxoUnFrozen, uItem.Amount) // utxo累加
continue

// utxo累加
if uItem.IsFrozen(curHeight) {
utxoFrozen.Add(utxoFrozen, uItem.Amount)
} else {
utxoUnFrozen.Add(utxoUnFrozen, uItem.Amount)
}
utxoFrozen.Add(utxoFrozen, uItem.Amount) // utxo累加
}
if it.Error() != nil {
return nil, it.Error()
Expand Down Expand Up @@ -877,14 +878,11 @@ func (t *State) doTxInternal(tx *pb.Transaction, batch kvdb.Batch, cacheFiller *
continue
}
utxoKey := utxo.GenUtxoKeyWithPrefix(addr, tx.Txid, int32(offset))
uItem := &utxo.UtxoItem{}
uItem.Amount = big.NewInt(0)
uItem.Amount.SetBytes(txOutput.Amount)
uItem := utxo.NewUtxoItem(txOutput.Amount, txOutput.FrozenHeight)
// 输出是0,忽略
if uItem.Amount.Cmp(big.NewInt(0)) == 0 {
if uItem.Amount.Sign() == 0 {
continue
}
uItem.FrozenHeight = txOutput.FrozenHeight
uItemBinary, uErr := uItem.Dumps()
if uErr != nil {
return uErr
Expand Down Expand Up @@ -1016,12 +1014,8 @@ func (t *State) undoTxInternal(tx *pb.Transaction, batch kvdb.Batch) error {
addr := txInput.FromAddr
txid := txInput.RefTxid
offset := txInput.RefOffset
amount := txInput.Amount
utxoKey := utxo.GenUtxoKeyWithPrefix(addr, txid, offset)
uItem := &utxo.UtxoItem{}
uItem.Amount = big.NewInt(0)
uItem.Amount.SetBytes(amount)
uItem.FrozenHeight = txInput.FrozenHeight
uItem := utxo.NewUtxoItem(txInput.Amount, txInput.FrozenHeight)
t.utxo.UtxoCache.Insert(string(addr), utxoKey, uItem)
uBinary, uErr := uItem.Dumps()
if uErr != nil {
Expand Down
2 changes: 1 addition & 1 deletion bcs/ledger/xledger/state/utxo/merge_utxo.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (uv *UtxoVM) SelectUtxosBySize(fromAddr string, needLock, excludeUnconfirme
continue
}
// check if the utxo item has been frozen
if utxoItem.FrozenHeight > uv.ledger.GetMeta().GetTrunkHeight() || utxoItem.FrozenHeight == -1 {
if utxoItem.IsFrozen(uv.ledger.GetMeta().GetTrunkHeight()) {
uv.log.Debug("utxo still frozen, skipped", "key", key)
continue
}
Expand Down
39 changes: 15 additions & 24 deletions bcs/ledger/xledger/state/utxo/utxo.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,31 +153,27 @@ func (uv *UtxoVM) CheckInputEqualOutput(tx *pb.Transaction, batch kvdb.Batch) er
return ErrUTXODuplicated
}
utxoDedup[utxoKey] = true
var amountBytes []byte
var frozenHeight int64
utxo := &UtxoItem{}
uv.UtxoCache.Lock()
if l2Cache, exist := uv.UtxoCache.All[string(addr)]; exist {
uItem := l2Cache[pb.UTXOTablePrefix+utxoKey]
if uItem != nil {
amountBytes = uItem.Amount.Bytes()
frozenHeight = uItem.FrozenHeight
utxo = &uItem.UtxoItem
}
}
uv.UtxoCache.Unlock()

if amountBytes == nil && batch != nil && batch == uv.lastBatch {
if utxo.IsEmpty() && batch != nil && batch == uv.lastBatch {
// 如果 utxo cache 查找不到,从 batch cache 查找,如果此处查不到再去数据库查。
// 目的是解决同步一个区块时,utxo cache 不能缓存所有的 utxo 导致区块执行失败。
// 此处缓存为同一个区块内交易的 utxo 缓存。
value, ok := uv.batchCache.Load(GenUtxoKeyWithPrefix(addr, txid, offset))
if ok {
uItem := value.(*UtxoItem)
amountBytes = uItem.Amount.Bytes()
frozenHeight = uItem.FrozenHeight
utxo = value.(*UtxoItem)
}
}

if amountBytes == nil {
if utxo.IsEmpty() {
uBinary, findErr := uv.utxoTable.Get([]byte(utxoKey))
if findErr != nil {
if def.NormalizedKVError(findErr) == def.ErrKVNotFound {
Expand All @@ -187,28 +183,23 @@ func (uv *UtxoVM) CheckInputEqualOutput(tx *pb.Transaction, batch kvdb.Batch) er
uv.log.Warn("unexpected leveldb error when do checkInputEqualOutput", "findErr", findErr)
return findErr
}
uItem := &UtxoItem{}
uErr := uItem.Loads(uBinary)
uErr := utxo.Loads(uBinary)
if uErr != nil {
return uErr
}
amountBytes = uItem.Amount.Bytes()
frozenHeight = uItem.FrozenHeight
}
amount := big.NewInt(0)
amount.SetBytes(amountBytes)
if !bytes.Equal(amountBytes, txInput.Amount) {
if !bytes.Equal(utxo.Amount.Bytes(), txInput.Amount) {
txInputAmount := big.NewInt(0)
txInputAmount.SetBytes(txInput.Amount)
uv.log.Warn("unexpected error, txInput amount missmatch utxo amount",
"in_utxo", amount, "txInputAmount", txInputAmount, "txid", utils.F(tx.Txid), "reftxid", utils.F(txid))
uv.log.Warn("unexpected error, txInput amount mismatch utxo amount",
"in_utxo", utxo.Amount, "txInputAmount", txInputAmount, "txid", utils.F(tx.Txid), "reftxid", utils.F(txid))
return ErrUnexpected
}
if frozenHeight > curLedgerHeight || frozenHeight == -1 {
uv.log.Warn("this utxo still be frozen", "frozenHeight", frozenHeight, "ledgerHeight", curLedgerHeight)
if utxo.IsFrozen(curLedgerHeight) {
uv.log.Warn("this utxo still be frozen", "frozenHeight", utxo.FrozenHeight, "ledgerHeight", curLedgerHeight)
return ErrUTXOFrozen
}
inputSum.Add(inputSum, amount)
inputSum.Add(inputSum, utxo.Amount)
}
if inputSum.Cmp(outputSum) == 0 {
return nil
Expand Down Expand Up @@ -378,7 +369,7 @@ func (uv *UtxoVM) SelectUtxos(fromAddr string, totalNeed *big.Int, needLock, exc
uv.UtxoCache.Lock()
if l2Cache, exist := uv.UtxoCache.Available[fromAddr]; exist {
for uKey, uItem := range l2Cache {
if uItem.FrozenHeight > curLedgerHeight || uItem.FrozenHeight == -1 {
if uItem.IsFrozen(curLedgerHeight) {
uv.log.Trace("utxo still frozen, skip it", "uKey", uKey, " fheight", uItem.FrozenHeight)
continue
}
Expand Down Expand Up @@ -442,7 +433,7 @@ func (uv *UtxoVM) SelectUtxos(fromAddr string, totalNeed *big.Int, needLock, exc
if _, inCache := cacheKeys[string(key)]; inCache {
continue // cache已经命中了,跳过
}
if uItem.FrozenHeight > curLedgerHeight || uItem.FrozenHeight == -1 {
if uItem.IsFrozen(curLedgerHeight) {
uv.log.Trace("utxo still frozen, skip it", "key", string(key), "fheight", uItem.FrozenHeight)
continue
}
Expand Down Expand Up @@ -658,7 +649,7 @@ func (uv *UtxoVM) QueryUtxoRecord(accountName string, displayCount int64) (*pb.U
}
continue
}
if utxoItem.FrozenHeight > uv.ledger.GetMeta().GetTrunkHeight() || utxoItem.FrozenHeight == -1 {
if utxoItem.IsFrozen(uv.ledger.GetMeta().GetTrunkHeight()) {
frozenUtxoCount.Add(frozenUtxoCount, big.NewInt(1))
frozenUtxoAmount.Add(frozenUtxoAmount, utxoItem.Amount)
if displayCount > 0 {
Expand Down
27 changes: 23 additions & 4 deletions bcs/ledger/xledger/state/utxo/utxo_item.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,38 @@ type UtxoItem struct {
FrozenHeight int64 //锁定until账本高度超过
}

func NewUtxoItem(amount []byte, frozenHeight int64) *UtxoItem {
amountValue := big.NewInt(0)
amountValue.SetBytes(amount)
return &UtxoItem{
Amount: amountValue,
FrozenHeight: frozenHeight,
}
}

// Loads load UTXO item from JSON encoded data
func (item *UtxoItem) Loads(data []byte) error {
func (i *UtxoItem) Loads(data []byte) error {
decoder := json.NewDecoder(bytes.NewBuffer(data))
return decoder.Decode(item)
return decoder.Decode(i)
}

// Dumps dump UTXO item into JSON encoded data
func (item *UtxoItem) Dumps() ([]byte, error) {
func (i *UtxoItem) Dumps() ([]byte, error) {
var buf bytes.Buffer
encoder := json.NewEncoder(&buf)
err := encoder.Encode(item)
err := encoder.Encode(i)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}

// IsFrozen return true for frozen UTXO
func (i *UtxoItem) IsFrozen(curHeight int64) bool {
return i.FrozenHeight > curHeight || i.FrozenHeight == -1
}

// IsEmpty return true for unset UTXO
func (i *UtxoItem) IsEmpty() bool {
return i.Amount == nil
}
Loading