From f34884eb688fa7b33924072e8990806cd5078bd2 Mon Sep 17 00:00:00 2001 From: laizy Date: Thu, 21 Nov 2019 19:54:52 +0800 Subject: [PATCH] make get balance query atomic (#1130) --- core/ledger/ledger.go | 4 ++ core/store/ledgerstore/ledger_store.go | 24 ++++++++- core/store/store.go | 1 + http/base/actor/ledger.go | 4 ++ http/base/common/common.go | 67 +++++++++++++++----------- txnpool/proc/txnpool_actor.go | 4 +- 6 files changed, 71 insertions(+), 33 deletions(-) diff --git a/core/ledger/ledger.go b/core/ledger/ledger.go index 4ee13ec496..d38801361f 100644 --- a/core/ledger/ledger.go +++ b/core/ledger/ledger.go @@ -179,6 +179,10 @@ func (self *Ledger) PreExecuteContract(tx *types.Transaction) (*cstate.PreExecRe return self.ldgStore.PreExecuteContract(tx) } +func (self *Ledger) PreExecuteContractBatch(txes []*types.Transaction, atomic bool) ([]*cstate.PreExecResult, uint32, error) { + return self.ldgStore.PreExecuteContractBatch(txes, atomic) +} + func (self *Ledger) GetEventNotifyByTx(tx common.Uint256) (*event.ExecuteNotify, error) { return self.ldgStore.GetEventNotifyByTx(tx) } diff --git a/core/store/ledgerstore/ledger_store.go b/core/store/ledgerstore/ledger_store.go index 4b236340db..f27429a6a2 100644 --- a/core/store/ledgerstore/ledger_store.go +++ b/core/store/ledgerstore/ledger_store.go @@ -821,10 +821,10 @@ func (this *LedgerStoreImp) submitBlock(block *types.Block, result store.Execute //saveBlock do the job of execution samrt contract and commit block to store. func (this *LedgerStoreImp) saveBlock(block *types.Block, stateMerkleRoot common.Uint256) error { blockHeight := block.Header.Height - if this.tryGetSavingBlockLock() { - //hash already saved or is saving + if blockHeight > 0 && blockHeight <= this.GetCurrentBlockHeight() { return nil } + this.getSavingBlockLock() defer this.releaseSavingBlockLock() if this.closing { return errors.NewErr("save block error: ledger is closing") @@ -1004,6 +1004,26 @@ func (this *LedgerStoreImp) GetEventNotifyByBlock(height uint32) ([]*event.Execu return this.eventStore.GetEventNotifyByBlock(height) } +//PreExecuteContract return the result of smart contract execution without commit to store +func (this *LedgerStoreImp) PreExecuteContractBatch(txes []*types.Transaction, atomic bool) ([]*sstate.PreExecResult, uint32, error) { + if atomic { + this.getSavingBlockLock() + defer this.releaseSavingBlockLock() + } + height := this.GetCurrentBlockHeight() + results := make([]*sstate.PreExecResult, 0, len(txes)) + for _, tx := range txes { + res, err := this.PreExecuteContract(tx) + if err != nil { + return nil, height, err + } + + results = append(results, res) + } + + return results, height, nil +} + //PreExecuteContract return the result of smart contract execution without commit to store func (this *LedgerStoreImp) PreExecuteContract(tx *types.Transaction) (*sstate.PreExecResult, error) { height := this.GetCurrentBlockHeight() diff --git a/core/store/store.go b/core/store/store.go index 5f446a43e2..0c6e85a5f3 100644 --- a/core/store/store.go +++ b/core/store/store.go @@ -64,6 +64,7 @@ type LedgerStore interface { GetBookkeeperState() (*states.BookkeeperState, error) GetStorageItem(key *states.StorageKey) (*states.StorageItem, error) PreExecuteContract(tx *types.Transaction) (*cstates.PreExecResult, error) + PreExecuteContractBatch(txes []*types.Transaction, atomic bool) ([]*cstates.PreExecResult, uint32, error) GetEventNotifyByTx(tx common.Uint256) (*event.ExecuteNotify, error) GetEventNotifyByBlock(height uint32) ([]*event.ExecuteNotify, error) } diff --git a/http/base/actor/ledger.go b/http/base/actor/ledger.go index 2a4a521494..bc57eba171 100644 --- a/http/base/actor/ledger.go +++ b/http/base/actor/ledger.go @@ -89,6 +89,10 @@ func PreExecuteContract(tx *types.Transaction) (*cstate.PreExecResult, error) { return ledger.DefLedger.PreExecuteContract(tx) } +func PreExecuteContractBatch(tx []*types.Transaction, atomic bool) ([]*cstate.PreExecResult, uint32, error) { + return ledger.DefLedger.PreExecuteContractBatch(tx, atomic) +} + //GetEventNotifyByTxHash from ledger func GetEventNotifyByTxHash(txHash common.Uint256) (*event.ExecuteNotify, error) { return ledger.DefLedger.GetEventNotifyByTx(txHash) diff --git a/http/base/common/common.go b/http/base/common/common.go index 6e573e100d..fe801be7f5 100644 --- a/http/base/common/common.go +++ b/http/base/common/common.go @@ -47,8 +47,9 @@ const MAX_SEARCH_HEIGHT uint32 = 100 const MAX_REQUEST_BODY_SIZE = 1 << 20 type BalanceOfRsp struct { - Ont string `json:"ont"` - Ong string `json:"ong"` + Ont string `json:"ont"` + Ong string `json:"ong"` + Height string `json:"height"` } type MerkleProof struct { @@ -279,17 +280,14 @@ func GetBlockInfo(block *types.Block) BlockInfo { } func GetBalance(address common.Address) (*BalanceOfRsp, error) { - ont, err := GetContractBalance(0, utils.OntContractAddress, address) - if err != nil { - return nil, fmt.Errorf("get ont balance error:%s", err) - } - ong, err := GetContractBalance(0, utils.OngContractAddress, address) + balances, height, err := GetContractBalance(0, []common.Address{utils.OntContractAddress, utils.OngContractAddress}, address, true) if err != nil { return nil, fmt.Errorf("get ont balance error:%s", err) } return &BalanceOfRsp{ - Ont: fmt.Sprintf("%d", ont), - Ong: fmt.Sprintf("%d", ong), + Ont: fmt.Sprintf("%d", balances[0]), + Ong: fmt.Sprintf("%d", balances[1]), + Height: fmt.Sprintf("%d", height), }, nil } @@ -304,11 +302,11 @@ func GetGrantOng(addr common.Address) (string, error) { if eof { return fmt.Sprintf("%v", 0), io.ErrUnexpectedEOF } - ont, err := GetContractBalance(0, utils.OntContractAddress, addr) + onts, _, err := GetContractBalance(0, []common.Address{utils.OntContractAddress}, addr, false) if err != nil { return fmt.Sprintf("%v", 0), err } - boundong := utils.CalcUnbindOng(ont, v, uint32(time.Now().Unix())-constants.GENESIS_BLOCK_TIMESTAMP) + boundong := utils.CalcUnbindOng(onts[0], v, uint32(time.Now().Unix())-constants.GENESIS_BLOCK_TIMESTAMP) return fmt.Sprintf("%v", boundong), nil } @@ -329,29 +327,40 @@ func GetAllowance(asset string, from, to common.Address) (string, error) { return fmt.Sprintf("%v", allowance), nil } -func GetContractBalance(cVersion byte, contractAddr, accAddr common.Address) (uint64, error) { - mutable, err := NewNativeInvokeTransaction(0, 0, contractAddr, cVersion, "balanceOf", []interface{}{accAddr[:]}) - if err != nil { - return 0, fmt.Errorf("NewNativeInvokeTransaction error:%s", err) - } - tx, err := mutable.IntoImmutable() - if err != nil { - return 0, err +func GetContractBalance(cVersion byte, contractAddres []common.Address, accAddr common.Address, atomic bool) ([]uint64, uint32, error) { + txes := make([]*types.Transaction, 0, len(contractAddres)) + for _, contractAddr := range contractAddres { + mutable, err := NewNativeInvokeTransaction(0, 0, contractAddr, cVersion, "balanceOf", []interface{}{accAddr[:]}) + if err != nil { + return nil, 0, fmt.Errorf("NewNativeInvokeTransaction error:%s", err) + } + tx, err := mutable.IntoImmutable() + if err != nil { + return nil, 0, err + } + + txes = append(txes, tx) } - result, err := bactor.PreExecuteContract(tx) + + results, height, err := bactor.PreExecuteContractBatch(txes, atomic) if err != nil { - return 0, fmt.Errorf("PrepareInvokeContract error:%s", err) - } - if result.State == 0 { - return 0, fmt.Errorf("prepare invoke failed") + return nil, 0, fmt.Errorf("PrepareInvokeContract error:%s", err) } - data, err := hex.DecodeString(result.Result.(string)) - if err != nil { - return 0, fmt.Errorf("hex.DecodeString error:%s", err) + balances := make([]uint64, 0, len(contractAddres)) + for _, result := range results { + if result.State == 0 { + return nil, 0, fmt.Errorf("prepare invoke failed") + } + data, err := hex.DecodeString(result.Result.(string)) + if err != nil { + return nil, 0, fmt.Errorf("hex.DecodeString error:%s", err) + } + + balance := common.BigIntFromNeoBytes(data) + balances = append(balances, balance.Uint64()) } - balance := common.BigIntFromNeoBytes(data) - return balance.Uint64(), nil + return balances, height, nil } func GetContractAllowance(cVersion byte, contractAddr, fromAddr, toAddr common.Address) (uint64, error) { diff --git a/txnpool/proc/txnpool_actor.go b/txnpool/proc/txnpool_actor.go index 704518be18..be9cc0af8f 100644 --- a/txnpool/proc/txnpool_actor.go +++ b/txnpool/proc/txnpool_actor.go @@ -62,13 +62,13 @@ func NewVerifyRspActor(s *TXPoolServer) *VerifyRspActor { // isBalanceEnough checks if the tranactor has enough to cover gas cost func isBalanceEnough(address common.Address, gas uint64) bool { - balance, err := hComm.GetContractBalance(0, utils.OngContractAddress, address) + balance, _, err := hComm.GetContractBalance(0, []common.Address{utils.OngContractAddress}, address, false) if err != nil { log.Debugf("failed to get contract balance %s err %v", address.ToHexString(), err) return false } - return balance >= gas + return balance[0] >= gas } func replyTxResult(txResultCh chan *tc.TxResult, hash common.Uint256,