From ec17654986f272429bd03cc5fe0879fbd11b2e49 Mon Sep 17 00:00:00 2001 From: Vsevolod Brekelov Date: Mon, 25 Nov 2019 20:39:11 +0300 Subject: [PATCH 1/7] core: refactoring blockchain state and storage add dao which takes care about all CRUD operations on storage remove blockchain state since everything is stored on change remove storage operations from structs(entities) move structs to entities package --- pkg/core/blockchain.go | 373 ++++++------ pkg/core/blockchain_state.go | 97 --- pkg/core/blockchain_state_test.go | 46 -- pkg/core/blockchain_test.go | 4 +- pkg/core/blockchainer.go | 13 +- pkg/core/dao.go | 555 ++++++++++++++++++ pkg/core/{ => entities}/account_state.go | 65 +- pkg/core/{ => entities}/account_state_test.go | 17 +- pkg/core/{ => entities}/asset_state.go | 26 +- pkg/core/{ => entities}/asset_state_test.go | 33 +- pkg/core/{ => entities}/coin_state.go | 2 +- pkg/core/{ => entities}/contract_state.go | 33 +- .../{ => entities}/contract_state_test.go | 27 +- pkg/core/{ => entities}/notification_event.go | 33 +- pkg/core/entities/storage_item.go | 23 + pkg/core/entities/storage_item_test.go | 2 + pkg/core/{ => entities}/validator_state.go | 77 +-- pkg/core/entities/validator_state_test.go | 64 ++ pkg/core/interop_neo.go | 47 +- pkg/core/interop_neo_test.go | 37 +- pkg/core/interop_system.go | 24 +- pkg/core/interops.go | 11 +- pkg/core/spent_coin_state.go | 50 -- pkg/core/spent_coin_state_test.go | 26 +- pkg/core/storage/helpers.go | 96 --- pkg/core/storage_item.go | 64 -- pkg/core/storage_item_test.go | 26 - .../random_util.go} | 18 +- pkg/core/uint32.go | 9 + pkg/core/unspent_coin_state.go | 109 +--- pkg/core/unspent_coint_state_test.go | 44 +- pkg/core/validator_state_test.go | 121 ---- pkg/network/helper_test.go | 11 +- pkg/rpc/client.go | 4 +- pkg/rpc/neoScanBalanceGetter.go | 4 +- pkg/rpc/neoScanTypes.go | 4 +- pkg/rpc/wrappers/account_state.go | 4 +- pkg/rpc/wrappers/asset_state.go | 4 +- pkg/rpc/wrappers/unspents.go | 13 +- 39 files changed, 958 insertions(+), 1258 deletions(-) delete mode 100644 pkg/core/blockchain_state.go delete mode 100644 pkg/core/blockchain_state_test.go create mode 100644 pkg/core/dao.go rename pkg/core/{ => entities}/account_state.go (62%) rename pkg/core/{ => entities}/account_state_test.go (82%) rename pkg/core/{ => entities}/asset_state.go (71%) rename pkg/core/{ => entities}/asset_state_test.go (50%) rename pkg/core/{ => entities}/coin_state.go (93%) rename pkg/core/{ => entities}/contract_state.go (67%) rename pkg/core/{ => entities}/contract_state_test.go (64%) rename pkg/core/{ => entities}/notification_event.go (58%) create mode 100644 pkg/core/entities/storage_item.go create mode 100644 pkg/core/entities/storage_item_test.go rename pkg/core/{ => entities}/validator_state.go (55%) create mode 100644 pkg/core/entities/validator_state_test.go delete mode 100644 pkg/core/storage/helpers.go delete mode 100644 pkg/core/storage_item.go delete mode 100644 pkg/core/storage_item_test.go rename pkg/core/{random_util_test.go => testutil/random_util.go} (63%) create mode 100644 pkg/core/uint32.go delete mode 100644 pkg/core/validator_state_test.go diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 51ec3eed8..1dc6dfb78 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -11,6 +11,7 @@ import ( "time" "github.com/CityOfZion/neo-go/config" + "github.com/CityOfZion/neo-go/pkg/core/entities" "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" @@ -46,8 +47,8 @@ var ( type Blockchain struct { config config.ProtocolConfiguration - // Persistent storage wrapped around with a write memory caching layer. - store *storage.MemCachedStore + // Data access object for CRUD operations around storage. + dao *dao // Current index/height of the highest block. // Read access should always be called by BlockHeight(). @@ -85,7 +86,7 @@ type headersOpFunc func(headerList *HeaderHashList) func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration) (*Blockchain, error) { bc := &Blockchain{ config: cfg, - store: storage.NewMemCachedStore(s), + dao: &dao{store: storage.NewMemCachedStore(s)}, headersOp: make(chan headersOpFunc), headersOpDone: make(chan struct{}), stopCh: make(chan struct{}), @@ -103,10 +104,10 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration) (*Blockcha func (bc *Blockchain) init() error { // If we could not find the version in the Store, we know that there is nothing stored. - ver, err := storage.Version(bc.store) + ver, err := bc.dao.GetVersion() if err != nil { log.Infof("no storage version found! creating genesis block") - if err = storage.PutVersion(bc.store, version); err != nil { + if err = bc.dao.PutVersion(version); err != nil { return err } genesisBlock, err := createGenesisBlock(bc.config) @@ -114,7 +115,7 @@ func (bc *Blockchain) init() error { return err } bc.headerList = NewHeaderHashList(genesisBlock.Hash()) - err = bc.store.Put(storage.SYSCurrentHeader.Bytes(), hashAndIndexToBytes(genesisBlock.Hash(), genesisBlock.Index)) + err = bc.dao.PutCurrentHeader(hashAndIndexToBytes(genesisBlock.Hash(), genesisBlock.Index)) if err != nil { return err } @@ -129,14 +130,14 @@ func (bc *Blockchain) init() error { // and the genesis block as first block. log.Infof("restoring blockchain with version: %s", version) - bHeight, err := storage.CurrentBlockHeight(bc.store) + bHeight, err := bc.dao.GetCurrentBlockHeight() if err != nil { return err } bc.blockHeight = bHeight bc.persistedHeight = bHeight - hashes, err := storage.HeaderHashes(bc.store) + hashes, err := bc.dao.GetHeaderHashes() if err != nil { return err } @@ -144,7 +145,7 @@ func (bc *Blockchain) init() error { bc.headerList = NewHeaderHashList(hashes...) bc.storedHeaderCount = uint32(len(hashes)) - currHeaderHeight, currHeaderHash, err := storage.CurrentHeaderHeight(bc.store) + currHeaderHeight, currHeaderHash, err := bc.dao.GetCurrentHeaderHeight() if err != nil { return err } @@ -198,7 +199,7 @@ func (bc *Blockchain) Run() { if err := bc.persist(); err != nil { log.Warnf("failed to persist: %s", err) } - if err := bc.store.Close(); err != nil { + if err := bc.dao.store.Close(); err != nil { log.Warnf("failed to close db: %s", err) } close(bc.runToExitCh) @@ -268,7 +269,7 @@ func (bc *Blockchain) AddBlock(block *Block) error { func (bc *Blockchain) AddHeaders(headers ...*Header) (err error) { var ( start = time.Now() - batch = bc.store.Batch() + batch = bc.dao.store.Batch() ) bc.headersOp <- func(headerList *HeaderHashList) { @@ -295,7 +296,7 @@ func (bc *Blockchain) AddHeaders(headers ...*Header) (err error) { if oldlen != headerList.Len() { updateHeaderHeightMetric(headerList.Len() - 1) - if err = bc.store.PutBatch(batch); err != nil { + if err = bc.dao.store.PutBatch(batch); err != nil { return } log.WithFields(log.Fields{ @@ -343,25 +344,26 @@ func (bc *Blockchain) processHeader(h *Header, batch storage.Batch, headerList * // is happening here, quite allot as you can see :). If things are wired together // and all tests are in place, we can make a more optimized and cleaner implementation. func (bc *Blockchain) storeBlock(block *Block) error { - chainState := NewBlockChainState(bc.store) - - if err := chainState.storeAsBlock(block, 0); err != nil { + cache := &dao{store: storage.NewMemCachedStore(bc.dao.store)} + if err := cache.StoreAsBlock(block, 0); err != nil { return err } - if err := chainState.storeAsCurrentBlock(block); err != nil { + if err := cache.StoreAsCurrentBlock(block); err != nil { return err } for _, tx := range block.Transactions { - if err := chainState.storeAsTransaction(tx, block.Index); err != nil { + if err := cache.StoreAsTransaction(tx, block.Index); err != nil { return err } - chainState.unspentCoins[tx.Hash()] = NewUnspentCoinState(len(tx.Outputs)) + if err := cache.PutUnspentCoinState(tx.Hash(), NewUnspentCoinState(len(tx.Outputs))); err != nil { + return err + } // Process TX outputs. - if err := processOutputs(tx, chainState); err != nil { + if err := processOutputs(tx, cache); err != nil { return err } @@ -372,14 +374,16 @@ func (bc *Blockchain) storeBlock(block *Block) error { return fmt.Errorf("could not find previous TX: %s", prevHash) } for _, input := range inputs { - unspent, err := chainState.unspentCoins.getAndUpdate(chainState.store, input.PrevHash) + unspent, err := cache.GetUnspentCoinStateOrNew(input.PrevHash) if err != nil { return err } - unspent.states[input.PrevIndex] = CoinStateSpent - + unspent.states[input.PrevIndex] = entities.CoinStateSpent + if err = cache.PutUnspentCoinState(input.PrevHash, unspent); err != nil { + return err + } prevTXOutput := prevTX.Outputs[input.PrevIndex] - account, err := chainState.accounts.getAndUpdate(chainState.store, prevTXOutput.ScriptHash) + account, err := cache.GetAccountStateOrNew(prevTXOutput.ScriptHash) if err != nil { return err } @@ -387,17 +391,24 @@ func (bc *Blockchain) storeBlock(block *Block) error { if prevTXOutput.AssetID.Equals(governingTokenTX().Hash()) { spentCoin := NewSpentCoinState(input.PrevHash, prevTXHeight) spentCoin.items[input.PrevIndex] = block.Index - chainState.spentCoins[input.PrevHash] = spentCoin - + if err = cache.PutSpentCoinState(input.PrevHash, spentCoin); err != nil { + return err + } if len(account.Votes) > 0 { for _, vote := range account.Votes { - validator, err := chainState.validators.getAndUpdate(chainState.store, vote) + validator, err := cache.GetValidatorStateOrNew(vote) if err != nil { return err } validator.Votes -= prevTXOutput.Amount if !validator.RegisteredAndHasVotes() { - delete(chainState.validators, vote) + if err = cache.DeleteValidatorState(validator); err != nil { + return err + } + } else { + if err = cache.PutValidatorState(validator); err != nil { + return err + } } } } @@ -419,13 +430,16 @@ func (bc *Blockchain) storeBlock(block *Block) error { account.Balances[prevTXOutput.AssetID] = account.Balances[prevTXOutput.AssetID][:balancesLen-1] } } + if err = cache.PutAccountState(account); err != nil { + return err + } } } // Process the underlying type of the TX. switch t := tx.Data.(type) { case *transaction.RegisterTX: - chainState.assets[tx.Hash()] = &AssetState{ + err := cache.PutAssetState(&entities.AssetState{ ID: tx.Hash(), AssetType: t.AssetType, Name: t.Name, @@ -434,45 +448,50 @@ func (bc *Blockchain) storeBlock(block *Block) error { Owner: t.Owner, Admin: t.Admin, Expiration: bc.BlockHeight() + registeredAssetLifetime, + }) + if err != nil { + return err } case *transaction.IssueTX: for _, res := range bc.GetTransactionResults(tx) { if res.Amount < 0 { - var asset *AssetState - - asset, ok := chainState.assets[res.AssetID] - if !ok { - asset = bc.GetAssetState(res.AssetID) - } - if asset == nil { - return fmt.Errorf("issue failed: no asset %s", res.AssetID) + asset, err := cache.GetAssetState(res.AssetID) + if asset == nil || err != nil { + return fmt.Errorf("issue failed: no asset %s or error %s", res.AssetID, err) } asset.Available -= res.Amount - chainState.assets[res.AssetID] = asset + if err := cache.PutAssetState(asset); err != nil { + return err + } } } case *transaction.ClaimTX: // Remove claimed NEO from spent coins making it unavalaible for // additional claims. for _, input := range t.Claims { - scs, err := chainState.spentCoins.getAndUpdate(bc.store, input.PrevHash) + scs, err := cache.GetSpentCoinsOrNew(input.PrevHash) if err != nil { return err } if scs.txHash == input.PrevHash { // Existing scs. delete(scs.items, input.PrevIndex) + if err = cache.PutSpentCoinState(input.PrevHash, scs); err != nil { + return err + } } else { // Uninitialized, new, forget about it. - delete(chainState.spentCoins, input.PrevHash) + if err = cache.DeleteSpentCoinState(input.PrevHash); err != nil { + return err + } } } case *transaction.EnrollmentTX: - if err := processEnrollmentTX(chainState, t); err != nil { + if err := processEnrollmentTX(cache, t); err != nil { return err } case *transaction.StateTX: - if err := processStateTX(chainState, t); err != nil { + if err := processStateTX(cache, t); err != nil { return err } case *transaction.PublishTX: @@ -480,7 +499,7 @@ func (bc *Blockchain) storeBlock(block *Block) error { if t.NeedStorage { properties |= smartcontract.HasStorage } - contract := &ContractState{ + contract := &entities.ContractState{ Script: t.Script, ParamList: t.ParamList, ReturnType: t.ReturnType, @@ -491,15 +510,17 @@ func (bc *Blockchain) storeBlock(block *Block) error { Email: t.Email, Description: t.Description, } - chainState.contracts[contract.ScriptHash()] = contract + if err := cache.PutContractState(contract); err != nil { + return err + } case *transaction.InvocationTX: - systemInterop := newInteropContext(trigger.Application, bc, chainState.store, block, tx) + systemInterop := newInteropContext(trigger.Application, bc, cache.store, block, tx) v := bc.spawnVMWithInterops(systemInterop) v.SetCheckedHash(tx.VerificationHash().BytesBE()) v.LoadScript(t.Script) err := v.Run() if !v.HasFailed() { - _, err := systemInterop.mem.Persist() + _, err := systemInterop.dao.store.Persist() if err != nil { return errors.Wrap(err, "failed to persist invocation results") } @@ -534,7 +555,7 @@ func (bc *Blockchain) storeBlock(block *Block) error { "err": err, }).Warn("contract invocation failed") } - aer := &AppExecResult{ + aer := &entities.AppExecResult{ TxHash: tx.Hash(), Trigger: trigger.Application, VMState: v.State(), @@ -542,17 +563,16 @@ func (bc *Blockchain) storeBlock(block *Block) error { Stack: v.Stack("estack"), Events: systemInterop.notifications, } - err = putAppExecResultIntoStore(chainState.store, aer) + err = cache.PutAppExecResult(aer) if err != nil { return errors.Wrap(err, "failed to store notifications") } } } - - if err := chainState.commit(); err != nil { + _, err := cache.store.Persist() + if err!= nil { return err } - atomic.StoreUint32(&bc.blockHeight, block.Index) updateBlockHeightMetric(block.Index) for _, tx := range block.Transactions { @@ -562,37 +582,43 @@ func (bc *Blockchain) storeBlock(block *Block) error { } // processOutputs processes transaction outputs. -func processOutputs(tx *transaction.Transaction, chainState *BlockChainState) error { +func processOutputs(tx *transaction.Transaction, dao *dao) error { for index, output := range tx.Outputs { - account, err := chainState.accounts.getAndUpdate(chainState.store, output.ScriptHash) + account, err := dao.GetAccountStateOrNew(output.ScriptHash) if err != nil { return err } - account.Balances[output.AssetID] = append(account.Balances[output.AssetID], UnspentBalance{ + account.Balances[output.AssetID] = append(account.Balances[output.AssetID], entities.UnspentBalance{ Tx: tx.Hash(), Index: uint16(index), Value: output.Amount, }) + if err = dao.PutAccountState(account); err != nil { + return err + } if output.AssetID.Equals(governingTokenTX().Hash()) && len(account.Votes) > 0 { for _, vote := range account.Votes { - validatorState, err := chainState.validators.getAndUpdate(chainState.store, vote) + validatorState, err := dao.GetValidatorStateOrNew(vote) if err != nil { return err } validatorState.Votes += output.Amount + if err = dao.PutValidatorState(validatorState); err != nil { + return err + } } } } return nil } -func processValidatorStateDescriptor(descriptor *transaction.StateDescriptor, state *BlockChainState) error { +func processValidatorStateDescriptor(descriptor *transaction.StateDescriptor, dao *dao) error { publicKey := &keys.PublicKey{} err := publicKey.DecodeBytes(descriptor.Key) if err != nil { return err } - validatorState, err := state.validators.getAndUpdate(state.store, publicKey) + validatorState, err := dao.GetValidatorStateOrNew(publicKey) if err != nil { return err } @@ -602,16 +628,17 @@ func processValidatorStateDescriptor(descriptor *transaction.StateDescriptor, st return err } validatorState.Registered = isRegistered + return dao.PutValidatorState(validatorState) } return nil } -func processAccountStateDescriptor(descriptor *transaction.StateDescriptor, state *BlockChainState) error { +func processAccountStateDescriptor(descriptor *transaction.StateDescriptor, dao *dao) error { hash, err := util.Uint160DecodeBytesBE(descriptor.Key) if err != nil { return err } - account, err := state.accounts.getAndUpdate(state.store, hash) + account, err := dao.GetAccountStateOrNew(hash) if err != nil { return err } @@ -619,13 +646,19 @@ func processAccountStateDescriptor(descriptor *transaction.StateDescriptor, stat if descriptor.Field == "Votes" { balance := account.GetBalanceValues()[governingTokenTX().Hash()] for _, vote := range account.Votes { - validator, err := state.validators.getAndUpdate(state.store, vote) + validator, err := dao.GetValidatorStateOrNew(vote) if err != nil { return err } validator.Votes -= balance if !validator.RegisteredAndHasVotes() { - delete(state.validators, vote) + if err := dao.DeleteValidatorState(validator); err != nil { + return err + } + } else { + if err := dao.PutValidatorState(validator); err != nil { + return err + } } } @@ -637,10 +670,13 @@ func processAccountStateDescriptor(descriptor *transaction.StateDescriptor, stat if votes.Len() != len(account.Votes) { account.Votes = votes for _, vote := range votes { - _, err := state.validators.getAndUpdate(state.store, vote) + validator, err := dao.GetValidatorStateOrNew(vote) if err != nil { return err } + if err := dao.PutValidatorState(validator); err != nil { + return err + } } } } @@ -655,19 +691,19 @@ func (bc *Blockchain) persist() error { err error ) - persisted, err = bc.store.Persist() + persisted, err = bc.dao.store.Persist() if err != nil { return err } if persisted > 0 { - bHeight, err := storage.CurrentBlockHeight(bc.store) + bHeight, err := bc.dao.GetCurrentBlockHeight() if err != nil { return err } oldHeight := atomic.SwapUint32(&bc.persistedHeight, bHeight) diff := bHeight - oldHeight - storedHeaderHeight, _, err := storage.CurrentHeaderHeight(bc.store) + storedHeaderHeight, _, err := bc.dao.GetCurrentHeaderHeight() if err != nil { return err } @@ -699,66 +735,22 @@ func (bc *Blockchain) GetTransaction(hash util.Uint256) (*transaction.Transactio if tx, ok := bc.memPool.TryGetValue(hash); ok { return tx, 0, nil // the height is not actually defined for memPool transaction. Not sure if zero is a good number in this case. } - return getTransactionFromStore(bc.store, hash) -} - -// getTransactionFromStore returns Transaction and its height by the given hash -// if it exists in the store. -func getTransactionFromStore(s storage.Store, hash util.Uint256) (*transaction.Transaction, uint32, error) { - key := storage.AppendPrefix(storage.DataTransaction, hash.BytesLE()) - b, err := s.Get(key) - if err != nil { - return nil, 0, err - } - r := io.NewBinReaderFromBuf(b) - - var height uint32 - r.ReadLE(&height) - - tx := &transaction.Transaction{} - tx.DecodeBinary(r) - if r.Err != nil { - return nil, 0, r.Err - } - - return tx, height, nil + return bc.dao.GetTransaction(hash) } // GetStorageItem returns an item from storage. -func (bc *Blockchain) GetStorageItem(scripthash util.Uint160, key []byte) *StorageItem { - return getStorageItemFromStore(bc.store, scripthash, key) +func (bc *Blockchain) GetStorageItem(scripthash util.Uint160, key []byte) *entities.StorageItem { + return bc.dao.GetStorageItem(scripthash, key) } // GetStorageItems returns all storage items for a given scripthash. -func (bc *Blockchain) GetStorageItems(hash util.Uint160) (map[string]*StorageItem, error) { - var siMap = make(map[string]*StorageItem) - var err error - - saveToMap := func(k, v []byte) { - if err != nil { - return - } - r := io.NewBinReaderFromBuf(v) - si := &StorageItem{} - si.DecodeBinary(r) - if r.Err != nil { - err = r.Err - return - } - - // Cut prefix and hash. - siMap[string(k[21:])] = si - } - bc.store.Seek(storage.AppendPrefix(storage.STStorage, hash.BytesLE()), saveToMap) - if err != nil { - return nil, err - } - return siMap, nil +func (bc *Blockchain) GetStorageItems(hash util.Uint160) (map[string]*entities.StorageItem, error) { + return bc.dao.GetStorageItems(hash) } // GetBlock returns a Block by the given hash. func (bc *Blockchain) GetBlock(hash util.Uint256) (*Block, error) { - block, err := getBlockFromStore(bc.store, hash) + block, err := bc.dao.GetBlock(hash) if err != nil { return nil, err } @@ -775,28 +767,9 @@ func (bc *Blockchain) GetBlock(hash util.Uint256) (*Block, error) { return block, nil } -// getBlockFromStore returns Block by the given hash if it exists in the store. -func getBlockFromStore(s storage.Store, hash util.Uint256) (*Block, error) { - key := storage.AppendPrefix(storage.DataBlock, hash.BytesLE()) - b, err := s.Get(key) - if err != nil { - return nil, err - } - block, err := NewBlockFromTrimmedBytes(b) - if err != nil { - return nil, err - } - return block, err -} - // GetHeader returns data block header identified with the given hash value. func (bc *Blockchain) GetHeader(hash util.Uint256) (*Header, error) { - return getHeaderFromStore(bc.store, hash) -} - -// getHeaderFromStore returns Header by the given hash from the store. -func getHeaderFromStore(s storage.Store, hash util.Uint256) (*Header, error) { - block, err := getBlockFromStore(s, hash) + block, err := bc.dao.GetBlock(hash) if err != nil { return nil, err } @@ -806,18 +779,7 @@ func getHeaderFromStore(s storage.Store, hash util.Uint256) (*Header, error) { // HasTransaction returns true if the blockchain contains he given // transaction hash. func (bc *Blockchain) HasTransaction(hash util.Uint256) bool { - return bc.memPool.ContainsKey(hash) || - checkTransactionInStore(bc.store, hash) -} - -// checkTransactionInStore returns true if the given store contains the given -// Transaction hash. -func checkTransactionInStore(s storage.Store, hash util.Uint256) bool { - key := storage.AppendPrefix(storage.DataTransaction, hash.BytesLE()) - if _, err := s.Get(key); err == nil { - return true - } - return false + return bc.memPool.ContainsKey(hash) || bc.dao.HasTransaction(hash) } // HasBlock returns true if the blockchain contains the given @@ -868,54 +830,26 @@ func (bc *Blockchain) HeaderHeight() uint32 { } // GetAssetState returns asset state from its assetID. -func (bc *Blockchain) GetAssetState(assetID util.Uint256) *AssetState { - return getAssetStateFromStore(bc.store, assetID) -} - -// getAssetStateFromStore returns given asset state as recorded in the given -// store. -func getAssetStateFromStore(s storage.Store, assetID util.Uint256) *AssetState { - key := storage.AppendPrefix(storage.STAsset, assetID.BytesBE()) - asEncoded, err := s.Get(key) - if err != nil { - return nil +func (bc *Blockchain) GetAssetState(assetID util.Uint256) *entities.AssetState { + asset, err := bc.dao.GetAssetState(assetID) + if asset == nil && err != storage.ErrKeyNotFound { + log.Warnf("failed to get asset state %s : %s", assetID, err) } - var a AssetState - r := io.NewBinReaderFromBuf(asEncoded) - a.DecodeBinary(r) - if r.Err != nil || a.ID != assetID { - return nil - } - - return &a + return asset } // GetContractState returns contract by its script hash. -func (bc *Blockchain) GetContractState(hash util.Uint160) *ContractState { - return getContractStateFromStore(bc.store, hash) -} - -// getContractStateFromStore returns contract state as recorded in the given -// store by the given script hash. -func getContractStateFromStore(s storage.Store, hash util.Uint160) *ContractState { - key := storage.AppendPrefix(storage.STContract, hash.BytesBE()) - contractBytes, err := s.Get(key) - if err != nil { - return nil +func (bc *Blockchain) GetContractState(hash util.Uint160) *entities.ContractState { + contract, err := bc.dao.GetContractState(hash) + if contract == nil && err != storage.ErrKeyNotFound { + log.Warnf("failed to get contract state: %s", err) } - var c ContractState - r := io.NewBinReaderFromBuf(contractBytes) - c.DecodeBinary(r) - if r.Err != nil || c.ScriptHash() != hash { - return nil - } - - return &c + return contract } // GetAccountState returns the account state from its script hash. -func (bc *Blockchain) GetAccountState(scriptHash util.Uint160) *AccountState { - as, err := getAccountStateFromStore(bc.store, scriptHash) +func (bc *Blockchain) GetAccountState(scriptHash util.Uint160) *entities.AccountState { + as, err := bc.dao.GetAccountState(scriptHash) if as == nil && err != storage.ErrKeyNotFound { log.Warnf("failed to get account state: %s", err) } @@ -924,7 +858,7 @@ func (bc *Blockchain) GetAccountState(scriptHash util.Uint160) *AccountState { // GetUnspentCoinState returns unspent coin state for given tx hash. func (bc *Blockchain) GetUnspentCoinState(hash util.Uint256) *UnspentCoinState { - ucs, err := getUnspentCoinStateFromStore(bc.store, hash) + ucs, err := bc.dao.GetUnspentCoinState(hash) if ucs == nil && err != storage.ErrKeyNotFound { log.Warnf("failed to get unspent coin state: %s", err) } @@ -1029,7 +963,7 @@ func (bc *Blockchain) VerifyTx(t *transaction.Transaction, block *Block) error { return errors.New("invalid transaction due to conflicts with the memory pool") } } - if IsDoubleSpend(bc.store, t) { + if bc.dao.IsDoubleSpend(t) { return errors.New("invalid transaction caused by double spending") } if err := bc.verifyOutputs(t); err != nil { @@ -1223,24 +1157,33 @@ func (bc *Blockchain) GetStandByValidators() (keys.PublicKeys, error) { // GetValidators returns validators. // Golang implementation of GetValidators method in C# (https://github.com/neo-project/neo/blob/c64748ecbac3baeb8045b16af0d518398a6ced24/neo/Persistence/Snapshot.cs#L182) func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.PublicKey, error) { - chainState := NewBlockChainState(bc.store) + cache := &dao{store: storage.NewMemCachedStore(bc.dao.store)} if len(txes) > 0 { for _, tx := range txes { // iterate through outputs for index, output := range tx.Outputs { - accountState := bc.GetAccountState(output.ScriptHash) - accountState.Balances[output.AssetID] = append(accountState.Balances[output.AssetID], UnspentBalance{ + accountState, err := cache.GetAccountState(output.ScriptHash) + if err != nil { + return nil, err + } + accountState.Balances[output.AssetID] = append(accountState.Balances[output.AssetID], entities.UnspentBalance{ Tx: tx.Hash(), Index: uint16(index), Value: output.Amount, }) + if err := cache.PutAccountState(accountState); err != nil { + return nil, err + } if output.AssetID.Equals(governingTokenTX().Hash()) && len(accountState.Votes) > 0 { for _, vote := range accountState.Votes { - validatorState, err := chainState.validators.getAndUpdate(chainState.store, vote) + validatorState, err := cache.GetValidatorStateOrNew(vote) if err != nil { return nil, err } validatorState.Votes += output.Amount + if err = cache.PutValidatorState(validatorState); err != nil { + return nil, err + } } } } @@ -1253,14 +1196,14 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P } for hash, inputs := range group { - prevTx, _, err := bc.GetTransaction(hash) + prevTx, _, err := cache.GetTransaction(hash) if err != nil { return nil, err } // process inputs for _, input := range inputs { prevOutput := prevTx.Outputs[input.PrevIndex] - accountState, err := chainState.accounts.getAndUpdate(chainState.store, prevOutput.ScriptHash) + accountState, err := cache.GetAccountStateOrNew(prevOutput.ScriptHash) if err != nil { return nil, err } @@ -1269,37 +1212,45 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P if prevOutput.AssetID.Equals(governingTokenTX().Hash()) { if len(accountState.Votes) > 0 { for _, vote := range accountState.Votes { - validatorState, err := chainState.validators.getAndUpdate(chainState.store, vote) + validatorState, err := cache.GetValidatorStateOrNew(vote) if err != nil { return nil, err } validatorState.Votes -= prevOutput.Amount + if err = cache.PutValidatorState(validatorState); err != nil { + return nil, err + } if !validatorState.Registered && validatorState.Votes.Equal(util.Fixed8(0)) { - delete(chainState.validators, vote) + if err = cache.DeleteValidatorState(validatorState); err != nil { + return nil, err + } } } } } delete(accountState.Balances, prevOutput.AssetID) + if err = cache.PutAccountState(accountState); err != nil { + return nil, err + } } } switch t := tx.Data.(type) { case *transaction.EnrollmentTX: - if err := processEnrollmentTX(chainState, t); err != nil { + if err := processEnrollmentTX(cache, t); err != nil { return nil, err } case *transaction.StateTX: - if err := processStateTX(chainState, t); err != nil { + if err := processStateTX(cache, t); err != nil { return nil, err } } } } - validators := getValidatorsFromStore(chainState.store) + validators := cache.GetValidators() - count := GetValidatorsWeightedAverage(validators) + count := entities.GetValidatorsWeightedAverage(validators) standByValidators, err := bc.GetStandByValidators() if err != nil { return nil, err @@ -1324,18 +1275,22 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P for i := 0; i < uniqueSBValidators.Len() && result.Len() < count; i++ { result = append(result, uniqueSBValidators[i]) } + _, err = cache.store.Persist() + if err != nil { + return nil, err + } return result, nil } -func processStateTX(chainState *BlockChainState, tx *transaction.StateTX) error { +func processStateTX(dao *dao, tx *transaction.StateTX) error { for _, desc := range tx.Descriptors { switch desc.Type { case transaction.Account: - if err := processAccountStateDescriptor(desc, chainState); err != nil { + if err := processAccountStateDescriptor(desc, dao); err != nil { return err } case transaction.Validator: - if err := processValidatorStateDescriptor(desc, chainState); err != nil { + if err := processValidatorStateDescriptor(desc, dao); err != nil { return err } } @@ -1343,13 +1298,13 @@ func processStateTX(chainState *BlockChainState, tx *transaction.StateTX) error return nil } -func processEnrollmentTX(chainState *BlockChainState, tx *transaction.EnrollmentTX) error { - validatorState, err := chainState.validators.getAndUpdate(chainState.store, &tx.PublicKey) +func processEnrollmentTX(dao *dao, tx *transaction.EnrollmentTX) error { + validatorState, err := dao.GetValidatorStateOrNew(&tx.PublicKey) if err != nil { return err } validatorState.Registered = true - return nil + return dao.PutValidatorState(validatorState) } // GetScriptHashesForVerifying returns all the ScriptHashes of a transaction which will be use @@ -1424,7 +1379,7 @@ func (bc *Blockchain) spawnVMWithInterops(interopCtx *interopContext) *vm.VM { // GetTestVM returns a VM and a Store setup for a test run of some sort of code. func (bc *Blockchain) GetTestVM() (*vm.VM, storage.Store) { - tmpStore := storage.NewMemCachedStore(bc.store) + tmpStore := storage.NewMemCachedStore(bc.dao.store) systemInterop := newInteropContext(trigger.Application, bc, tmpStore, nil, nil) vm := bc.spawnVMWithInterops(systemInterop) return vm, tmpStore @@ -1495,7 +1450,7 @@ func (bc *Blockchain) verifyTxWitnesses(t *transaction.Transaction, block *Block } sort.Slice(hashes, func(i, j int) bool { return hashes[i].Less(hashes[j]) }) sort.Slice(witnesses, func(i, j int) bool { return witnesses[i].ScriptHash().Less(witnesses[j].ScriptHash()) }) - interopCtx := newInteropContext(trigger.Verification, bc, bc.store, block, t) + interopCtx := newInteropContext(trigger.Verification, bc, bc.dao.store, block, t) for i := 0; i < len(hashes); i++ { err := bc.verifyHashAgainstScript(hashes[i], &witnesses[i], t.VerificationHash(), interopCtx, false) if err != nil { @@ -1515,7 +1470,7 @@ func (bc *Blockchain) verifyBlockWitnesses(block *Block, prevHeader *Header) err } else { hash = prevHeader.NextConsensus } - interopCtx := newInteropContext(trigger.Verification, bc, bc.store, nil, nil) + interopCtx := newInteropContext(trigger.Verification, bc, bc.dao.store, nil, nil) return bc.verifyHashAgainstScript(hash, &block.Script, block.VerificationHash(), interopCtx, true) } diff --git a/pkg/core/blockchain_state.go b/pkg/core/blockchain_state.go deleted file mode 100644 index 4ce0e0d88..000000000 --- a/pkg/core/blockchain_state.go +++ /dev/null @@ -1,97 +0,0 @@ -package core - -import ( - "github.com/CityOfZion/neo-go/pkg/core/storage" - "github.com/CityOfZion/neo-go/pkg/core/transaction" - "github.com/CityOfZion/neo-go/pkg/io" -) - -// BlockChainState represents Blockchain state structure with mempool. -type BlockChainState struct { - store *storage.MemCachedStore - unspentCoins UnspentCoins - spentCoins SpentCoins - accounts Accounts - assets Assets - contracts Contracts - validators Validators -} - -// NewBlockChainState creates blockchain state with it's memchached store. -func NewBlockChainState(store *storage.MemCachedStore) *BlockChainState { - tmpStore := storage.NewMemCachedStore(store) - return &BlockChainState{ - store: tmpStore, - unspentCoins: make(UnspentCoins), - spentCoins: make(SpentCoins), - accounts: make(Accounts), - assets: make(Assets), - contracts: make(Contracts), - validators: make(Validators), - } -} - -// commit commits all the data in current state into storage. -func (state *BlockChainState) commit() error { - if err := state.accounts.commit(state.store); err != nil { - return err - } - if err := state.unspentCoins.commit(state.store); err != nil { - return err - } - if err := state.spentCoins.commit(state.store); err != nil { - return err - } - if err := state.assets.commit(state.store); err != nil { - return err - } - if err := state.contracts.commit(state.store); err != nil { - return err - } - if err := state.validators.commit(state.store); err != nil { - return err - } - if _, err := state.store.Persist(); err != nil { - return err - } - return nil -} - -// storeAsBlock stores the given block as DataBlock. -func (state *BlockChainState) storeAsBlock(block *Block, sysFee uint32) error { - var ( - key = storage.AppendPrefix(storage.DataBlock, block.Hash().BytesLE()) - buf = io.NewBufBinWriter() - ) - // sysFee needs to be handled somehow - // buf.WriteLE(sysFee) - b, err := block.Trim() - if err != nil { - return err - } - buf.WriteBytes(b) - if buf.Err != nil { - return buf.Err - } - return state.store.Put(key, buf.Bytes()) -} - -// storeAsCurrentBlock stores the given block witch prefix SYSCurrentBlock. -func (state *BlockChainState) storeAsCurrentBlock(block *Block) error { - buf := io.NewBufBinWriter() - buf.WriteBytes(block.Hash().BytesLE()) - buf.WriteLE(block.Index) - return state.store.Put(storage.SYSCurrentBlock.Bytes(), buf.Bytes()) -} - -// storeAsTransaction stores the given TX as DataTransaction. -func (state *BlockChainState) storeAsTransaction(tx *transaction.Transaction, index uint32) error { - key := storage.AppendPrefix(storage.DataTransaction, tx.Hash().BytesLE()) - buf := io.NewBufBinWriter() - buf.WriteLE(index) - tx.EncodeBinary(buf.BinWriter) - if buf.Err != nil { - return buf.Err - } - return state.store.Put(key, buf.Bytes()) -} diff --git a/pkg/core/blockchain_state_test.go b/pkg/core/blockchain_state_test.go deleted file mode 100644 index 3a625c784..000000000 --- a/pkg/core/blockchain_state_test.go +++ /dev/null @@ -1,46 +0,0 @@ -package core - -import ( - "testing" - - "github.com/CityOfZion/neo-go/pkg/core/storage" - "github.com/CityOfZion/neo-go/pkg/core/transaction" - "github.com/stretchr/testify/require" -) - -func TestNewBlockChainStateAndCommit(t *testing.T) { - memCachedStore := storage.NewMemCachedStore(storage.NewMemoryStore()) - bcState := NewBlockChainState(memCachedStore) - err := bcState.commit() - require.NoError(t, err) -} - -func TestStoreAsBlock(t *testing.T) { - memCachedStore := storage.NewMemCachedStore(storage.NewMemoryStore()) - bcState := NewBlockChainState(memCachedStore) - - block := newBlock(0, newMinerTX()) - err := bcState.storeAsBlock(block, 0) - require.NoError(t, err) -} - -func TestStoreAsCurrentBlock(t *testing.T) { - memCachedStore := storage.NewMemCachedStore(storage.NewMemoryStore()) - bcState := NewBlockChainState(memCachedStore) - - block := newBlock(0, newMinerTX()) - err := bcState.storeAsCurrentBlock(block) - require.NoError(t, err) -} - -func TestStoreAsTransaction(t *testing.T) { - memCachedStore := storage.NewMemCachedStore(storage.NewMemoryStore()) - bcState := NewBlockChainState(memCachedStore) - - tx := &transaction.Transaction{ - Type: transaction.MinerType, - Data: &transaction.MinerTX{}, - } - err := bcState.storeAsTransaction(tx, 0) - require.NoError(t, err) -} diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index b3f997d48..f539b865b 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -56,7 +56,7 @@ func TestAddBlock(t *testing.T) { for _, block := range blocks { key := storage.AppendPrefix(storage.DataBlock, block.Hash().BytesLE()) - if _, err := bc.store.Get(key); err != nil { + if _, err := bc.dao.store.Get(key); err != nil { t.Fatalf("block %s not persisted", block.Hash()) } } @@ -170,7 +170,7 @@ func TestClose(t *testing.T) { // It's a hack, but we use internal knowledge of MemoryStore // implementation which makes it completely unusable (up to panicing) // after Close(). - _ = bc.store.Put([]byte{0}, []byte{1}) + _ = bc.dao.store.Put([]byte{0}, []byte{1}) // This should never be executed. assert.Nil(t, t) diff --git a/pkg/core/blockchainer.go b/pkg/core/blockchainer.go index a65d69847..9e87ff209 100644 --- a/pkg/core/blockchainer.go +++ b/pkg/core/blockchainer.go @@ -2,6 +2,7 @@ package core import ( "github.com/CityOfZion/neo-go/config" + "github.com/CityOfZion/neo-go/pkg/core/entities" "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" @@ -19,19 +20,19 @@ type Blockchainer interface { Close() HeaderHeight() uint32 GetBlock(hash util.Uint256) (*Block, error) - GetContractState(hash util.Uint160) *ContractState + GetContractState(hash util.Uint160) *entities.ContractState GetHeaderHash(int) util.Uint256 GetHeader(hash util.Uint256) (*Header, error) CurrentHeaderHash() util.Uint256 CurrentBlockHash() util.Uint256 HasBlock(util.Uint256) bool HasTransaction(util.Uint256) bool - GetAssetState(util.Uint256) *AssetState - GetAccountState(util.Uint160) *AccountState - GetValidators(txes ...*transaction.Transaction) ([]*keys.PublicKey, error) + GetAssetState(util.Uint256) *entities.AssetState + GetAccountState(util.Uint160) *entities.AccountState + GetValidators(txes... *transaction.Transaction) ([]*keys.PublicKey, error) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) - GetStorageItem(scripthash util.Uint160, key []byte) *StorageItem - GetStorageItems(hash util.Uint160) (map[string]*StorageItem, error) + GetStorageItem(scripthash util.Uint160, key []byte) *entities.StorageItem + GetStorageItems(hash util.Uint160) (map[string]*entities.StorageItem, error) GetTestVM() (*vm.VM, storage.Store) GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error) GetUnspentCoinState(util.Uint256) *UnspentCoinState diff --git a/pkg/core/dao.go b/pkg/core/dao.go new file mode 100644 index 000000000..bafe70b73 --- /dev/null +++ b/pkg/core/dao.go @@ -0,0 +1,555 @@ +package core + +import ( + "bytes" + "encoding/binary" + "fmt" + "sort" + + "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/storage" + "github.com/CityOfZion/neo-go/pkg/core/transaction" + "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/io" + "github.com/CityOfZion/neo-go/pkg/util" +) + +// dao is a data access object. +type dao struct { + store *storage.MemCachedStore +} + +// GetAndDecode performs get operation and decoding with serializable structures. +func (dao *dao) GetAndDecode(entity io.Serializable, key []byte) error { + entityBytes, err := dao.store.Get(key) + if err != nil { + return err + } + reader := io.NewBinReaderFromBuf(entityBytes) + entity.DecodeBinary(reader) + return reader.Err +} + +// Put performs put operation with serializable structures. +func (dao *dao) Put(entity io.Serializable, key []byte) error { + buf := io.NewBufBinWriter() + entity.EncodeBinary(buf.BinWriter) + if buf.Err != nil { + return buf.Err + } + return dao.store.Put(key, buf.Bytes()) +} + +// -- start accounts. + +// GetAccountStateOrNew retrieves AccountState from temporary or persistent Store +// or creates a new one if it doesn't exist and persists it. +func (dao *dao) GetAccountStateOrNew(hash util.Uint160) (*entities.AccountState, error) { + account, err := dao.GetAccountState(hash) + if err != nil { + if err != storage.ErrKeyNotFound { + return nil, err + } + account = entities.NewAccountState(hash) + if err = dao.PutAccountState(account); err != nil { + return nil, err + } + } + return account, nil +} + +// GetAccountState returns AccountState from the given Store if it's +// present there. Returns nil otherwise. +func (dao *dao) GetAccountState(hash util.Uint160) (*entities.AccountState, error) { + account := &entities.AccountState{} + key := storage.AppendPrefix(storage.STAccount, hash.BytesBE()) + err := dao.GetAndDecode(account, key) + if err != nil { + return nil, err + } + return account, err +} + +// PutAccountState puts given AccountState into the given store. +func (dao *dao) PutAccountState(as *entities.AccountState) error { + key := storage.AppendPrefix(storage.STAccount, as.ScriptHash.BytesBE()) + return dao.Put(as, key) +} + +// -- end accounts. + +// -- start assets. + +// GetAssetState returns given asset state as recorded in the given store. +func (dao *dao) GetAssetState(assetID util.Uint256) (*entities.AssetState, error) { + asset := &entities.AssetState{} + key := storage.AppendPrefix(storage.STAsset, assetID.BytesBE()) + err := dao.GetAndDecode(asset, key) + if err != nil { + return nil, err + } + if asset.ID != assetID { + return nil, fmt.Errorf("found asset id is not equal to expected") + } + return asset, nil +} + +// PutAssetState puts given asset state into the given store. +func (dao *dao) PutAssetState(as *entities.AssetState) error { + key := storage.AppendPrefix(storage.STAsset, as.ID.BytesBE()) + return dao.Put(as, key) +} + +// -- end assets. + +// -- start contracts. + +// GetContractState returns contract state as recorded in the given +// store by the given script hash. +func (dao *dao) GetContractState(hash util.Uint160) (*entities.ContractState, error) { + contract := &entities.ContractState{} + key := storage.AppendPrefix(storage.STContract, hash.BytesBE()) + err := dao.GetAndDecode(contract, key) + if err != nil { + return nil, err + } + if contract.ScriptHash() != hash { + return nil, fmt.Errorf("found script hash is not equal to expected") + } + + return contract, nil +} + +// PutContractState puts given contract state into the given store. +func (dao *dao) PutContractState(cs *entities.ContractState) error { + key := storage.AppendPrefix(storage.STContract, cs.ScriptHash().BytesBE()) + return dao.Put(cs, key) +} + +// DeleteContractState deletes given contract state in the given store. +func (dao *dao) DeleteContractState(hash util.Uint160) error { + key := storage.AppendPrefix(storage.STContract, hash.BytesBE()) + return dao.store.Delete(key) +} + +// -- end contracts. + +// -- start unspent coins. + +// GetUnspentCoinStateOrNew gets UnspentCoinState from temporary or persistent Store +// and return it. If it's not present in both stores, returns a new +// UnspentCoinState. +func (dao *dao) GetUnspentCoinStateOrNew(hash util.Uint256) (*UnspentCoinState, error) { + unspent, err := dao.GetUnspentCoinState(hash) + if err != nil { + if err != storage.ErrKeyNotFound { + return nil, err + } + unspent = &UnspentCoinState{ + states: []entities.CoinState{}, + } + if err = dao.PutUnspentCoinState(hash, unspent); err != nil { + return nil, err + } + } + return unspent, nil +} + +// GetUnspentCoinState retrieves UnspentCoinState from the given store. +func (dao *dao) GetUnspentCoinState(hash util.Uint256) (*UnspentCoinState, error) { + unspent := &UnspentCoinState{} + key := storage.AppendPrefix(storage.STCoin, hash.BytesLE()) + err := dao.GetAndDecode(unspent, key) + if err != nil { + return nil, err + } + return unspent, nil +} + +// PutUnspentCoinState puts given UnspentCoinState into the given store. +func (dao *dao) PutUnspentCoinState(hash util.Uint256, ucs *UnspentCoinState) error { + key := storage.AppendPrefix(storage.STCoin, hash.BytesLE()) + return dao.Put(ucs, key) +} + +// -- end unspent coins. + +// -- start spent coins. + +// GetSpentCoinsOrNew returns spent coins from store. +func (dao *dao) GetSpentCoinsOrNew(hash util.Uint256) (*SpentCoinState, error) { + spent, err := dao.GetSpentCoinState(hash) + if err != nil { + if err != storage.ErrKeyNotFound { + return nil, err + } + spent = &SpentCoinState{ + items: make(map[uint16]uint32), + } + if err = dao.PutSpentCoinState(hash, spent); err != nil { + return nil, err + } + } + return spent, nil +} + +// GetSpentCoinState gets SpentCoinState from the given store. +func (dao *dao) GetSpentCoinState(hash util.Uint256) (*SpentCoinState, error) { + spent := &SpentCoinState{} + key := storage.AppendPrefix(storage.STSpentCoin, hash.BytesLE()) + err := dao.GetAndDecode(spent, key) + if err != nil { + return nil, err + } + return spent, nil +} + +// PutSpentCoinState puts given SpentCoinState into the given store. +func (dao *dao) PutSpentCoinState(hash util.Uint256, scs *SpentCoinState) error { + key := storage.AppendPrefix(storage.STSpentCoin, hash.BytesLE()) + return dao.Put(scs, key) +} + +// DeleteSpentCoinState deletes given SpentCoinState from the given store. +func (dao *dao) DeleteSpentCoinState(hash util.Uint256) error { + key := storage.AppendPrefix(storage.STSpentCoin, hash.BytesLE()) + return dao.store.Delete(key) +} + +// -- end spent coins. + +// -- start validator. + +// GetValidatorStateOrNew gets validator from store or created new one in case of error. +func (dao *dao) GetValidatorStateOrNew(publicKey *keys.PublicKey) (*entities.ValidatorState, error) { + validatorState, err := dao.GetValidatorState(publicKey) + if err != nil { + if err != storage.ErrKeyNotFound { + return nil, err + } + validatorState = &entities.ValidatorState{PublicKey: publicKey} + if err = dao.PutValidatorState(validatorState); err != nil { + return nil, err + } + } + return validatorState, nil + +} + +// GetValidators returns all validators from store. +func (dao *dao) GetValidators() []*entities.ValidatorState { + var validators []*entities.ValidatorState + dao.store.Seek(storage.STValidator.Bytes(), func(k, v []byte) { + r := io.NewBinReaderFromBuf(v) + validator := &entities.ValidatorState{} + validator.DecodeBinary(r) + if r.Err != nil { + return + } + validators = append(validators, validator) + }) + return validators +} + +// GetValidatorState returns validator by publicKey. +func (dao *dao) GetValidatorState(publicKey *keys.PublicKey) (*entities.ValidatorState, error) { + validatorState := &entities.ValidatorState{} + key := storage.AppendPrefix(storage.STValidator, publicKey.Bytes()) + err := dao.GetAndDecode(validatorState, key) + if err != nil { + return nil, err + } + return validatorState, nil +} + +// PutValidatorState puts given ValidatorState into the given store. +func (dao *dao) PutValidatorState(vs *entities.ValidatorState) error { + key := storage.AppendPrefix(storage.STValidator, vs.PublicKey.Bytes()) + return dao.Put(vs, key) +} + +// DeleteValidatorState deletes given ValidatorState into the given store. +func (dao *dao) DeleteValidatorState(vs *entities.ValidatorState) error { + key := storage.AppendPrefix(storage.STValidator, vs.PublicKey.Bytes()) + return dao.store.Delete(key) +} + +// -- end validator. + +// -- start notification event. + +// GetAppExecResult gets application execution result from the +// given store. +func (dao *dao) GetAppExecResult(hash util.Uint256) (*entities.AppExecResult, error) { + aer := &entities.AppExecResult{} + key := storage.AppendPrefix(storage.STNotification, hash.BytesBE()) + err := dao.GetAndDecode(aer, key) + if err != nil { + return nil, err + } + return aer, nil +} + +// PutAppExecResult puts given application execution result into the +// given store. +func (dao *dao) PutAppExecResult(aer *entities.AppExecResult) error { + key := storage.AppendPrefix(storage.STNotification, aer.TxHash.BytesBE()) + return dao.Put(aer, key) +} + +// -- end notification event. + +// -- start storage item. + +// GetStorageItem returns StorageItem if it exists in the given Store. +func (dao *dao) GetStorageItem(scripthash util.Uint160, key []byte) *entities.StorageItem { + b, err := dao.store.Get(makeStorageItemKey(scripthash, key)) + if err != nil { + return nil + } + r := io.NewBinReaderFromBuf(b) + + si := &entities.StorageItem{} + si.DecodeBinary(r) + if r.Err != nil { + return nil + } + + return si +} + +// PutStorageItem puts given StorageItem for given script with given +// key into the given Store. +func (dao *dao) PutStorageItem(scripthash util.Uint160, key []byte, si *entities.StorageItem) error { + return dao.Put(si, makeStorageItemKey(scripthash, key)) +} + +// DeleteStorageItem drops storage item for the given script with the +// given key from the Store. +func (dao *dao) DeleteStorageItem(scripthash util.Uint160, key []byte) error { + return dao.store.Delete(makeStorageItemKey(scripthash, key)) +} + +// GetStorageItems returns all storage items for a given scripthash. +func (dao *dao) GetStorageItems(hash util.Uint160) (map[string]*entities.StorageItem, error) { + var siMap = make(map[string]*entities.StorageItem) + var err error + + saveToMap := func(k, v []byte) { + if err != nil { + return + } + r := io.NewBinReaderFromBuf(v) + si := &entities.StorageItem{} + si.DecodeBinary(r) + if r.Err != nil { + err = r.Err + return + } + + // Cut prefix and hash. + siMap[string(k[21:])] = si + } + dao.store.Seek(storage.AppendPrefix(storage.STStorage, hash.BytesLE()), saveToMap) + if err != nil { + return nil, err + } + return siMap, nil +} + +// makeStorageItemKey returns a key used to store StorageItem in the DB. +func makeStorageItemKey(scripthash util.Uint160, key []byte) []byte { + return storage.AppendPrefix(storage.STStorage, append(scripthash.BytesLE(), key...)) +} + +// -- end storage item. + +// -- other. + +// GetBlock returns Block by the given hash if it exists in the store. +func (dao *dao) GetBlock(hash util.Uint256) (*Block, error) { + key := storage.AppendPrefix(storage.DataBlock, hash.BytesLE()) + b, err := dao.store.Get(key) + if err != nil { + return nil, err + } + block, err := NewBlockFromTrimmedBytes(b) + if err != nil { + return nil, err + } + return block, err +} + +// GetVersion attempts to get the current version stored in the +// underlying Store. +func (dao *dao) GetVersion() (string, error) { + version, err := dao.store.Get(storage.SYSVersion.Bytes()) + return string(version), err +} + +// GetCurrentBlockHeight returns the current block height found in the +// underlying Store. +func (dao *dao) GetCurrentBlockHeight() (uint32, error) { + b, err := dao.store.Get(storage.SYSCurrentBlock.Bytes()) + if err != nil { + return 0, err + } + return binary.LittleEndian.Uint32(b[32:36]), nil +} + +// GetCurrentHeaderHeight returns the current header height and hash from +// the underlying Store. +func (dao *dao) GetCurrentHeaderHeight() (i uint32, h util.Uint256, err error) { + var b []byte + b, err = dao.store.Get(storage.SYSCurrentHeader.Bytes()) + if err != nil { + return + } + i = binary.LittleEndian.Uint32(b[32:36]) + h, err = util.Uint256DecodeBytesLE(b[:32]) + return +} + +// GetHeaderHashes returns a sorted list of header hashes retrieved from +// the given underlying Store. +func (dao *dao) GetHeaderHashes() ([]util.Uint256, error) { + hashMap := make(map[uint32][]util.Uint256) + dao.store.Seek(storage.IXHeaderHashList.Bytes(), func(k, v []byte) { + storedCount := binary.LittleEndian.Uint32(k[1:]) + hashes, err := read2000Uint256Hashes(v) + if err != nil { + panic(err) + } + hashMap[storedCount] = hashes + }) + + var ( + hashes = make([]util.Uint256, 0, len(hashMap)) + sortedKeys = make([]uint32, 0, len(hashMap)) + ) + + for k := range hashMap { + sortedKeys = append(sortedKeys, k) + } + sort.Sort(slice(sortedKeys)) + + for _, key := range sortedKeys { + hashes = append(hashes[:key], hashMap[key]...) + } + + return hashes, nil +} + +// GetTransaction returns Transaction and its height by the given hash +// if it exists in the store. +func (dao *dao) GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error) { + key := storage.AppendPrefix(storage.DataTransaction, hash.BytesLE()) + b, err := dao.store.Get(key) + if err != nil { + return nil, 0, err + } + r := io.NewBinReaderFromBuf(b) + + var height uint32 + r.ReadLE(&height) + + tx := &transaction.Transaction{} + tx.DecodeBinary(r) + if r.Err != nil { + return nil, 0, r.Err + } + + return tx, height, nil +} + +// PutVersion stores the given version in the underlying Store. +func (dao *dao) PutVersion(v string) error { + return dao.store.Put(storage.SYSVersion.Bytes(), []byte(v)) +} + +// PutCurrentHeader stores current header. +func (dao *dao) PutCurrentHeader(hashAndIndex []byte) error { + return dao.store.Put(storage.SYSCurrentHeader.Bytes(), hashAndIndex) +} + +// read2000Uint256Hashes attempts to read 2000 Uint256 hashes from +// the given byte array. +func read2000Uint256Hashes(b []byte) ([]util.Uint256, error) { + r := bytes.NewReader(b) + br := io.NewBinReaderFromIO(r) + lenHashes := br.ReadVarUint() + hashes := make([]util.Uint256, lenHashes) + br.ReadLE(hashes) + if br.Err != nil { + return nil, br.Err + } + return hashes, nil +} + +// HasTransaction returns true if the given store contains the given +// Transaction hash. +func (dao *dao) HasTransaction(hash util.Uint256) bool { + key := storage.AppendPrefix(storage.DataTransaction, hash.BytesLE()) + if _, err := dao.store.Get(key); err == nil { + return true + } + return false +} + +// StoreAsBlock stores the given block as DataBlock. +func (dao *dao) StoreAsBlock(block *Block, sysFee uint32) error { + var ( + key = storage.AppendPrefix(storage.DataBlock, block.Hash().BytesLE()) + buf = io.NewBufBinWriter() + ) + // sysFee needs to be handled somehow + // buf.WriteLE(sysFee) + b, err := block.Trim() + if err != nil { + return err + } + buf.WriteLE(b) + if buf.Err != nil { + return buf.Err + } + return dao.store.Put(key, buf.Bytes()) +} + +// StoreAsCurrentBlock stores the given block witch prefix SYSCurrentBlock. +func (dao *dao) StoreAsCurrentBlock(block *Block) error { + buf := io.NewBufBinWriter() + buf.WriteLE(block.Hash().BytesLE()) + buf.WriteLE(block.Index) + return dao.store.Put(storage.SYSCurrentBlock.Bytes(), buf.Bytes()) +} + +// StoreAsTransaction stores the given TX as DataTransaction. +func (dao *dao) StoreAsTransaction(tx *transaction.Transaction, index uint32) error { + key := storage.AppendPrefix(storage.DataTransaction, tx.Hash().BytesLE()) + buf := io.NewBufBinWriter() + buf.WriteLE(index) + tx.EncodeBinary(buf.BinWriter) + if buf.Err != nil { + return buf.Err + } + return dao.store.Put(key, buf.Bytes()) +} + +// IsDoubleSpend verifies that the input transactions are not double spent. +func (dao *dao) IsDoubleSpend(tx *transaction.Transaction) bool { + if len(tx.Inputs) == 0 { + return false + } + for prevHash, inputs := range tx.GroupInputsByPrevHash() { + unspent, err := dao.GetUnspentCoinState(prevHash) + if err != nil { + return false + } + for _, input := range inputs { + if int(input.PrevIndex) >= len(unspent.states) || unspent.states[input.PrevIndex] == entities.CoinStateSpent { + return true + } + } + } + return false +} diff --git a/pkg/core/account_state.go b/pkg/core/entities/account_state.go similarity index 62% rename from pkg/core/account_state.go rename to pkg/core/entities/account_state.go index cd79fe189..f06b94ae3 100644 --- a/pkg/core/account_state.go +++ b/pkg/core/entities/account_state.go @@ -1,74 +1,11 @@ -package core +package entities import ( - "fmt" - - "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) -// Accounts is mapping between a account address and AccountState. -type Accounts map[util.Uint160]*AccountState - -// getAndUpdate retrieves AccountState from temporary or persistent Store -// or creates a new one if it doesn't exist. -func (a Accounts) getAndUpdate(s storage.Store, hash util.Uint160) (*AccountState, error) { - if account, ok := a[hash]; ok { - return account, nil - } - - account, err := getAccountStateFromStore(s, hash) - if err != nil { - if err != storage.ErrKeyNotFound { - return nil, err - } - account = NewAccountState(hash) - } - - a[hash] = account - return account, nil -} - -// getAccountStateFromStore returns AccountState from the given Store if it's -// present there. Returns nil otherwise. -func getAccountStateFromStore(s storage.Store, hash util.Uint160) (*AccountState, error) { - var account *AccountState - key := storage.AppendPrefix(storage.STAccount, hash.BytesBE()) - b, err := s.Get(key) - if err == nil { - account = new(AccountState) - r := io.NewBinReaderFromBuf(b) - account.DecodeBinary(r) - if r.Err != nil { - return nil, fmt.Errorf("failed to decode (AccountState): %s", r.Err) - } - } - return account, err -} - -// putAccountStateIntoStore puts given AccountState into the given store. -func putAccountStateIntoStore(store storage.Store, as *AccountState) error { - buf := io.NewBufBinWriter() - as.EncodeBinary(buf.BinWriter) - if buf.Err != nil { - return buf.Err - } - key := storage.AppendPrefix(storage.STAccount, as.ScriptHash.BytesBE()) - return store.Put(key, buf.Bytes()) -} - -// commit writes all account states to the given Batch. -func (a Accounts) commit(store storage.Store) error { - for _, state := range a { - if err := putAccountStateIntoStore(store, state); err != nil { - return err - } - } - return nil -} - // UnspentBalance contains input/output transactons that sum up into the // account balance for the given asset. type UnspentBalance struct { diff --git a/pkg/core/account_state_test.go b/pkg/core/entities/account_state_test.go similarity index 82% rename from pkg/core/account_state_test.go rename to pkg/core/entities/account_state_test.go index 6517a1679..89d8d3026 100644 --- a/pkg/core/account_state_test.go +++ b/pkg/core/entities/account_state_test.go @@ -1,8 +1,9 @@ -package core +package entities import ( "testing" + "github.com/CityOfZion/neo-go/pkg/core/testutil" "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" @@ -16,12 +17,12 @@ func TestDecodeEncodeAccountState(t *testing.T) { votes = make([]*keys.PublicKey, n) ) for i := 0; i < n; i++ { - asset := randomUint256() + asset := testutil.RandomUint256() for j := 0; j < i+1; j++ { balances[asset] = append(balances[asset], UnspentBalance{ - Tx: randomUint256(), - Index: uint16(randomInt(0, 65535)), - Value: util.Fixed8(int64(randomInt(1, 10000))), + Tx: testutil.RandomUint256(), + Index: uint16(testutil.RandomInt(0, 65535)), + Value: util.Fixed8(int64(testutil.RandomInt(1, 10000))), }) } k, err := keys.NewPrivateKey() @@ -31,7 +32,7 @@ func TestDecodeEncodeAccountState(t *testing.T) { a := &AccountState{ Version: 0, - ScriptHash: randomUint160(), + ScriptHash: testutil.RandomUint160(), IsFrozen: true, Votes: votes, Balances: balances, @@ -57,8 +58,8 @@ func TestDecodeEncodeAccountState(t *testing.T) { } func TestAccountStateBalanceValues(t *testing.T) { - asset1 := randomUint256() - asset2 := randomUint256() + asset1 := testutil.RandomUint256() + asset2 := testutil.RandomUint256() as := AccountState{Balances: make(map[util.Uint256][]UnspentBalance)} ref := 0 for i := 0; i < 10; i++ { diff --git a/pkg/core/asset_state.go b/pkg/core/entities/asset_state.go similarity index 71% rename from pkg/core/asset_state.go rename to pkg/core/entities/asset_state.go index 6af69172c..c81d3b5e3 100644 --- a/pkg/core/asset_state.go +++ b/pkg/core/entities/asset_state.go @@ -1,7 +1,6 @@ -package core +package entities import ( - "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/io" @@ -10,29 +9,6 @@ import ( const feeMode = 0x0 -// Assets is mapping between AssetID and the AssetState. -type Assets map[util.Uint256]*AssetState - -func (a Assets) commit(store storage.Store) error { - for _, state := range a { - if err := putAssetStateIntoStore(store, state); err != nil { - return err - } - } - return nil -} - -// putAssetStateIntoStore puts given asset state into the given store. -func putAssetStateIntoStore(s storage.Store, as *AssetState) error { - buf := io.NewBufBinWriter() - as.EncodeBinary(buf.BinWriter) - if buf.Err != nil { - return buf.Err - } - key := storage.AppendPrefix(storage.STAsset, as.ID.BytesBE()) - return s.Put(key, buf.Bytes()) -} - // AssetState represents the state of an NEO registered Asset. type AssetState struct { ID util.Uint256 diff --git a/pkg/core/asset_state_test.go b/pkg/core/entities/asset_state_test.go similarity index 50% rename from pkg/core/asset_state_test.go rename to pkg/core/entities/asset_state_test.go index ac9d1ef7b..084f1cd3c 100644 --- a/pkg/core/asset_state_test.go +++ b/pkg/core/entities/asset_state_test.go @@ -1,10 +1,11 @@ -package core +package entities import ( "testing" - "github.com/CityOfZion/neo-go/pkg/core/storage" + "github.com/CityOfZion/neo-go/pkg/core/testutil" "github.com/CityOfZion/neo-go/pkg/core/transaction" + "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" "github.com/stretchr/testify/assert" @@ -12,15 +13,16 @@ import ( func TestEncodeDecodeAssetState(t *testing.T) { asset := &AssetState{ - ID: randomUint256(), + ID: testutil.RandomUint256(), AssetType: transaction.Token, Name: "super cool token", Amount: util.Fixed8(1000000), Available: util.Fixed8(100), Precision: 0, FeeMode: feeMode, - Admin: randomUint160(), - Issuer: randomUint160(), + Owner: keys.PublicKey{}, + Admin: testutil.RandomUint160(), + Issuer: testutil.RandomUint160(), Expiration: 10, IsFrozen: false, } @@ -34,24 +36,3 @@ func TestEncodeDecodeAssetState(t *testing.T) { assert.Nil(t, r.Err) assert.Equal(t, asset, assetDecode) } - -func TestPutGetAssetState(t *testing.T) { - s := storage.NewMemoryStore() - asset := &AssetState{ - ID: randomUint256(), - AssetType: transaction.Token, - Name: "super cool token", - Amount: util.Fixed8(1000000), - Available: util.Fixed8(100), - Precision: 8, - FeeMode: feeMode, - Admin: randomUint160(), - Issuer: randomUint160(), - Expiration: 10, - IsFrozen: false, - } - assert.NoError(t, putAssetStateIntoStore(s, asset)) - asRead := getAssetStateFromStore(s, asset.ID) - assert.NotNil(t, asRead) - assert.Equal(t, asset, asRead) -} diff --git a/pkg/core/coin_state.go b/pkg/core/entities/coin_state.go similarity index 93% rename from pkg/core/coin_state.go rename to pkg/core/entities/coin_state.go index 8232977f3..cefc25de7 100644 --- a/pkg/core/coin_state.go +++ b/pkg/core/entities/coin_state.go @@ -1,4 +1,4 @@ -package core +package entities // CoinState represents the state of a coin. type CoinState uint8 diff --git a/pkg/core/contract_state.go b/pkg/core/entities/contract_state.go similarity index 67% rename from pkg/core/contract_state.go rename to pkg/core/entities/contract_state.go index 5af26e1a8..812d7766d 100644 --- a/pkg/core/contract_state.go +++ b/pkg/core/entities/contract_state.go @@ -1,16 +1,12 @@ -package core +package entities import ( - "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/crypto/hash" "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/smartcontract" "github.com/CityOfZion/neo-go/pkg/util" ) -// Contracts is a mapping between scripthash and ContractState. -type Contracts map[util.Uint160]*ContractState - // ContractState holds information about a smart contract in the NEO blockchain. type ContractState struct { Script []byte @@ -26,16 +22,6 @@ type ContractState struct { scriptHash util.Uint160 } -// commit flushes all contracts to the given storage.Batch. -func (a Contracts) commit(store storage.Store) error { - for _, contract := range a { - if err := putContractStateIntoStore(store, contract); err != nil { - return err - } - } - return nil -} - // DecodeBinary implements Serializable interface. func (cs *ContractState) DecodeBinary(br *io.BinReader) { cs.Script = br.ReadVarBytes() @@ -63,23 +49,6 @@ func (cs *ContractState) EncodeBinary(bw *io.BinWriter) { bw.WriteString(cs.Description) } -// putContractStateIntoStore puts given contract state into the given store. -func putContractStateIntoStore(s storage.Store, cs *ContractState) error { - buf := io.NewBufBinWriter() - cs.EncodeBinary(buf.BinWriter) - if buf.Err != nil { - return buf.Err - } - key := storage.AppendPrefix(storage.STContract, cs.ScriptHash().BytesBE()) - return s.Put(key, buf.Bytes()) -} - -// deleteContractStateInStore deletes given contract state in the given store. -func deleteContractStateInStore(s storage.Store, hash util.Uint160) error { - key := storage.AppendPrefix(storage.STContract, hash.BytesBE()) - return s.Delete(key) -} - // ScriptHash returns a contract script hash. func (cs *ContractState) ScriptHash() util.Uint160 { if cs.scriptHash.Equals(util.Uint160{}) { diff --git a/pkg/core/contract_state_test.go b/pkg/core/entities/contract_state_test.go similarity index 64% rename from pkg/core/contract_state_test.go rename to pkg/core/entities/contract_state_test.go index 9ab2e8233..ba9e5f44a 100644 --- a/pkg/core/contract_state_test.go +++ b/pkg/core/entities/contract_state_test.go @@ -1,9 +1,8 @@ -package core +package entities import ( "testing" - "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/crypto/hash" "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/smartcontract" @@ -51,27 +50,3 @@ func TestContractStateProperties(t *testing.T) { assert.Equal(t, false, nonFlaggedContract.HasDynamicInvoke()) assert.Equal(t, false, nonFlaggedContract.IsPayable()) } - -func TestPutGetDeleteContractState(t *testing.T) { - s := storage.NewMemoryStore() - script := []byte("testscript") - - contract := &ContractState{ - Script: script, - ParamList: []smartcontract.ParamType{smartcontract.StringType, smartcontract.IntegerType, smartcontract.Hash160Type}, - ReturnType: smartcontract.BoolType, - Properties: smartcontract.HasStorage, - Name: "Contrato", - CodeVersion: "1.0.0", - Author: "Joe Random", - Email: "joe@example.com", - Description: "Test contract", - } - assert.NoError(t, putContractStateIntoStore(s, contract)) - csRead := getContractStateFromStore(s, contract.ScriptHash()) - assert.NotNil(t, csRead) - assert.Equal(t, contract, csRead) - assert.NoError(t, deleteContractStateInStore(s, contract.ScriptHash())) - csRead2 := getContractStateFromStore(s, contract.ScriptHash()) - assert.Nil(t, csRead2) -} diff --git a/pkg/core/notification_event.go b/pkg/core/entities/notification_event.go similarity index 58% rename from pkg/core/notification_event.go rename to pkg/core/entities/notification_event.go index ee0086cc3..cf69815b2 100644 --- a/pkg/core/notification_event.go +++ b/pkg/core/entities/notification_event.go @@ -1,11 +1,9 @@ -package core +package entities import ( - "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" "github.com/CityOfZion/neo-go/pkg/vm" - "github.com/pkg/errors" ) // NotificationEvent is a tuple of scripthash that emitted the StackItem as a @@ -26,35 +24,6 @@ type AppExecResult struct { Events []NotificationEvent } -// putAppExecResultIntoStore puts given application execution result into the -// given store. -func putAppExecResultIntoStore(s storage.Store, aer *AppExecResult) error { - buf := io.NewBufBinWriter() - aer.EncodeBinary(buf.BinWriter) - if buf.Err != nil { - return buf.Err - } - key := storage.AppendPrefix(storage.STNotification, aer.TxHash.BytesBE()) - return s.Put(key, buf.Bytes()) -} - -// getAppExecResultFromStore gets application execution result from the -// given store. -func getAppExecResultFromStore(s storage.Store, hash util.Uint256) (*AppExecResult, error) { - aer := &AppExecResult{} - key := storage.AppendPrefix(storage.STNotification, hash.BytesBE()) - if b, err := s.Get(key); err == nil { - r := io.NewBinReaderFromBuf(b) - aer.DecodeBinary(r) - if r.Err != nil { - return nil, errors.Wrap(r.Err, "decoding failure:") - } - } else { - return nil, err - } - return aer, nil -} - // EncodeBinary implements the Serializable interface. func (ne *NotificationEvent) EncodeBinary(w *io.BinWriter) { w.WriteBytes(ne.ScriptHash[:]) diff --git a/pkg/core/entities/storage_item.go b/pkg/core/entities/storage_item.go new file mode 100644 index 000000000..2e5965616 --- /dev/null +++ b/pkg/core/entities/storage_item.go @@ -0,0 +1,23 @@ +package entities + +import ( + "github.com/CityOfZion/neo-go/pkg/io" +) + +// StorageItem is the value to be stored with read-only flag. +type StorageItem struct { + Value []byte + IsConst bool +} + +// EncodeBinary implements Serializable interface. +func (si *StorageItem) EncodeBinary(w *io.BinWriter) { + w.WriteVarBytes(si.Value) + w.WriteLE(si.IsConst) +} + +// DecodeBinary implements Serializable interface. +func (si *StorageItem) DecodeBinary(r *io.BinReader) { + si.Value = r.ReadVarBytes() + r.ReadLE(&si.IsConst) +} diff --git a/pkg/core/entities/storage_item_test.go b/pkg/core/entities/storage_item_test.go new file mode 100644 index 000000000..870b5d166 --- /dev/null +++ b/pkg/core/entities/storage_item_test.go @@ -0,0 +1,2 @@ +package entities + diff --git a/pkg/core/validator_state.go b/pkg/core/entities/validator_state.go similarity index 55% rename from pkg/core/validator_state.go rename to pkg/core/entities/validator_state.go index 36fe3a996..48e6601de 100644 --- a/pkg/core/validator_state.go +++ b/pkg/core/entities/validator_state.go @@ -1,86 +1,11 @@ -package core +package entities import ( - "fmt" - - "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) -// Validators is a mapping between public keys and ValidatorState. -type Validators map[*keys.PublicKey]*ValidatorState - -func (v Validators) getAndUpdate(s storage.Store, publicKey *keys.PublicKey) (*ValidatorState, error) { - if validator, ok := v[publicKey]; ok { - return validator, nil - } - - validatorState, err := getValidatorStateFromStore(s, publicKey) - if err != nil { - if err != storage.ErrKeyNotFound { - return nil, err - } - validatorState = &ValidatorState{PublicKey: publicKey} - } - v[publicKey] = validatorState - return validatorState, nil - -} - -// getValidatorsFromStore returns all validators from store. -func getValidatorsFromStore(s storage.Store) []*ValidatorState { - var validators []*ValidatorState - s.Seek(storage.STValidator.Bytes(), func(k, v []byte) { - r := io.NewBinReaderFromBuf(v) - validator := &ValidatorState{} - validator.DecodeBinary(r) - if r.Err != nil { - return - } - validators = append(validators, validator) - }) - return validators -} - -// getValidatorStateFromStore returns validator by publicKey. -func getValidatorStateFromStore(s storage.Store, publicKey *keys.PublicKey) (*ValidatorState, error) { - validatorState := &ValidatorState{} - key := storage.AppendPrefix(storage.STValidator, publicKey.Bytes()) - if b, err := s.Get(key); err == nil { - r := io.NewBinReaderFromBuf(b) - validatorState.DecodeBinary(r) - if r.Err != nil { - return nil, fmt.Errorf("failed to decode (ValidatorState): %s", r.Err) - } - } else { - return nil, err - } - return validatorState, nil -} - -// commit writes all validator states to the given Batch. -func (v Validators) commit(store storage.Store) error { - for _, validator := range v { - if err := putValidatorStateIntoStore(store, validator); err != nil { - return err - } - } - return nil -} - -// putValidatorStateIntoStore puts given ValidatorState into the given store. -func putValidatorStateIntoStore(store storage.Store, vs *ValidatorState) error { - buf := io.NewBufBinWriter() - vs.EncodeBinary(buf.BinWriter) - if buf.Err != nil { - return buf.Err - } - key := storage.AppendPrefix(storage.STValidator, vs.PublicKey.Bytes()) - return store.Put(key, buf.Bytes()) -} - // ValidatorState holds the state of a validator. type ValidatorState struct { PublicKey *keys.PublicKey diff --git a/pkg/core/entities/validator_state_test.go b/pkg/core/entities/validator_state_test.go new file mode 100644 index 000000000..521cf0827 --- /dev/null +++ b/pkg/core/entities/validator_state_test.go @@ -0,0 +1,64 @@ +package entities + +import ( + "math/big" + "testing" + + "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/io" + "github.com/CityOfZion/neo-go/pkg/util" + "github.com/stretchr/testify/require" +) + +func TestValidatorState_DecodeEncodeBinary(t *testing.T) { + state := &ValidatorState{ + PublicKey: &keys.PublicKey{}, + Registered: false, + Votes: util.Fixed8(10), + } + buf := io.NewBufBinWriter() + state.EncodeBinary(buf.BinWriter) + require.NoError(t, buf.Err) + + decodedState := &ValidatorState{} + reader := io.NewBinReaderFromBuf(buf.Bytes()) + decodedState.DecodeBinary(reader) + require.NoError(t, reader.Err) + require.Equal(t, state, decodedState) +} + +func TestRegisteredAndHasVotes_Registered(t *testing.T) { + state := &ValidatorState{ + PublicKey: &keys.PublicKey{ + X: big.NewInt(1), + Y: big.NewInt(1), + }, + Registered: true, + Votes: 0, + } + require.False(t, state.RegisteredAndHasVotes()) +} + +func TestRegisteredAndHasVotes_RegisteredWithVotes(t *testing.T) { + state := &ValidatorState{ + PublicKey: &keys.PublicKey{ + X: big.NewInt(1), + Y: big.NewInt(1), + }, + Registered: true, + Votes: 1, + } + require.True(t, state.RegisteredAndHasVotes()) +} + +func TestRegisteredAndHasVotes_NotRegisteredWithVotes(t *testing.T) { + state := &ValidatorState{ + PublicKey: &keys.PublicKey{ + X: big.NewInt(1), + Y: big.NewInt(1), + }, + Registered: false, + Votes: 1, + } + require.False(t, state.RegisteredAndHasVotes()) +} diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index 17c100e1c..632181384 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -5,6 +5,7 @@ import ( "fmt" "math" + "github.com/CityOfZion/neo-go/pkg/core/entities" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/smartcontract" @@ -316,7 +317,7 @@ func (ic *interopContext) bcGetAccount(v *vm.VM) error { } acc := ic.bc.GetAccountState(acchash) if acc == nil { - acc = NewAccountState(acchash) + acc = entities.NewAccountState(acchash) } v.Estack().PushVal(vm.NewInteropItem(acc)) return nil @@ -340,7 +341,7 @@ func (ic *interopContext) bcGetAsset(v *vm.VM) error { // accountGetBalance returns balance for a given account. func (ic *interopContext) accountGetBalance(v *vm.VM) error { accInterface := v.Estack().Pop().Value() - acc, ok := accInterface.(*AccountState) + acc, ok := accInterface.(*entities.AccountState) if !ok { return fmt.Errorf("%T is not an account state", acc) } @@ -360,7 +361,7 @@ func (ic *interopContext) accountGetBalance(v *vm.VM) error { // accountGetScriptHash returns script hash of a given account. func (ic *interopContext) accountGetScriptHash(v *vm.VM) error { accInterface := v.Estack().Pop().Value() - acc, ok := accInterface.(*AccountState) + acc, ok := accInterface.(*entities.AccountState) if !ok { return fmt.Errorf("%T is not an account state", acc) } @@ -371,7 +372,7 @@ func (ic *interopContext) accountGetScriptHash(v *vm.VM) error { // accountGetVotes returns votes of a given account. func (ic *interopContext) accountGetVotes(v *vm.VM) error { accInterface := v.Estack().Pop().Value() - acc, ok := accInterface.(*AccountState) + acc, ok := accInterface.(*entities.AccountState) if !ok { return fmt.Errorf("%T is not an account state", acc) } @@ -429,7 +430,7 @@ func (ic *interopContext) storageFind(v *vm.VM) error { // createContractStateFromVM pops all contract state elements from the VM // evaluation stack, does a lot of checks and returns ContractState if it // succeeds. -func (ic *interopContext) createContractStateFromVM(v *vm.VM) (*ContractState, error) { +func (ic *interopContext) createContractStateFromVM(v *vm.VM) (*entities.ContractState, error) { if ic.trigger != trigger.Application { return nil, errors.New("can't create contract when not triggered by an application") } @@ -467,7 +468,7 @@ func (ic *interopContext) createContractStateFromVM(v *vm.VM) (*ContractState, e if len(desc) > MaxContractStringLen { return nil, errors.New("too big description") } - contract := &ContractState{ + contract := &entities.ContractState{ Script: script, ParamList: paramList, ReturnType: retType, @@ -490,7 +491,7 @@ func (ic *interopContext) contractCreate(v *vm.VM) error { contract := ic.bc.GetContractState(newcontract.ScriptHash()) if contract == nil { contract = newcontract - err := putContractStateIntoStore(ic.mem, contract) + err := ic.dao.PutContractState(contract) if err != nil { return err } @@ -502,7 +503,7 @@ func (ic *interopContext) contractCreate(v *vm.VM) error { // contractGetScript returns a script associated with a contract. func (ic *interopContext) contractGetScript(v *vm.VM) error { csInterface := v.Estack().Pop().Value() - cs, ok := csInterface.(*ContractState) + cs, ok := csInterface.(*entities.ContractState) if !ok { return fmt.Errorf("%T is not a contract state", cs) } @@ -513,7 +514,7 @@ func (ic *interopContext) contractGetScript(v *vm.VM) error { // contractIsPayable returns whether contract is payable. func (ic *interopContext) contractIsPayable(v *vm.VM) error { csInterface := v.Estack().Pop().Value() - cs, ok := csInterface.(*ContractState) + cs, ok := csInterface.(*entities.ContractState) if !ok { return fmt.Errorf("%T is not a contract state", cs) } @@ -530,7 +531,7 @@ func (ic *interopContext) contractMigrate(v *vm.VM) error { contract := ic.bc.GetContractState(newcontract.ScriptHash()) if contract == nil { contract = newcontract - err := putContractStateIntoStore(ic.mem, contract) + err := ic.dao.PutContractState(contract) if err != nil { return err } @@ -542,7 +543,7 @@ func (ic *interopContext) contractMigrate(v *vm.VM) error { } for k, v := range siMap { v.IsConst = false - _ = putStorageItemIntoStore(ic.mem, hash, []byte(k), v) + _ = ic.dao.PutStorageItem(hash, []byte(k), v) } } } @@ -609,7 +610,7 @@ func (ic *interopContext) assetCreate(v *vm.VM) error { if err != nil { return gherr.Wrap(err, "failed to get issuer") } - asset := &AssetState{ + asset := &entities.AssetState{ ID: ic.tx.Hash(), AssetType: atype, Name: name, @@ -620,7 +621,7 @@ func (ic *interopContext) assetCreate(v *vm.VM) error { Issuer: issuer, Expiration: ic.bc.BlockHeight() + DefaultAssetLifetime, } - err = putAssetStateIntoStore(ic.mem, asset) + err = ic.dao.PutAssetState(asset) if err != nil { return gherr.Wrap(err, "failed to store asset") } @@ -631,7 +632,7 @@ func (ic *interopContext) assetCreate(v *vm.VM) error { // assetGetAdmin returns asset admin. func (ic *interopContext) assetGetAdmin(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*AssetState) + as, ok := asInterface.(*entities.AssetState) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -642,7 +643,7 @@ func (ic *interopContext) assetGetAdmin(v *vm.VM) error { // assetGetAmount returns the overall amount of asset available. func (ic *interopContext) assetGetAmount(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*AssetState) + as, ok := asInterface.(*entities.AssetState) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -653,7 +654,7 @@ func (ic *interopContext) assetGetAmount(v *vm.VM) error { // assetGetAssetId returns the id of an asset. func (ic *interopContext) assetGetAssetID(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*AssetState) + as, ok := asInterface.(*entities.AssetState) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -664,7 +665,7 @@ func (ic *interopContext) assetGetAssetID(v *vm.VM) error { // assetGetAssetType returns type of an asset. func (ic *interopContext) assetGetAssetType(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*AssetState) + as, ok := asInterface.(*entities.AssetState) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -675,7 +676,7 @@ func (ic *interopContext) assetGetAssetType(v *vm.VM) error { // assetGetAvailable returns available (not yet issued) amount of asset. func (ic *interopContext) assetGetAvailable(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*AssetState) + as, ok := asInterface.(*entities.AssetState) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -686,7 +687,7 @@ func (ic *interopContext) assetGetAvailable(v *vm.VM) error { // assetGetIssuer returns issuer of an asset. func (ic *interopContext) assetGetIssuer(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*AssetState) + as, ok := asInterface.(*entities.AssetState) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -697,7 +698,7 @@ func (ic *interopContext) assetGetIssuer(v *vm.VM) error { // assetGetOwner returns owner of an asset. func (ic *interopContext) assetGetOwner(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*AssetState) + as, ok := asInterface.(*entities.AssetState) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -708,7 +709,7 @@ func (ic *interopContext) assetGetOwner(v *vm.VM) error { // assetGetPrecision returns precision used to measure this asset. func (ic *interopContext) assetGetPrecision(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*AssetState) + as, ok := asInterface.(*entities.AssetState) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -722,7 +723,7 @@ func (ic *interopContext) assetRenew(v *vm.VM) error { return errors.New("can't create asset when not triggered by an application") } asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*AssetState) + as, ok := asInterface.(*entities.AssetState) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -740,7 +741,7 @@ func (ic *interopContext) assetRenew(v *vm.VM) error { expiration = math.MaxUint32 } asset.Expiration = uint32(expiration) - err := putAssetStateIntoStore(ic.mem, asset) + err := ic.dao.PutAssetState(asset) if err != nil { return gherr.Wrap(err, "failed to store asset") } diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index 1fee8ae98..a20d38064 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -4,7 +4,9 @@ import ( "math/big" "testing" + "github.com/CityOfZion/neo-go/pkg/core/entities" "github.com/CityOfZion/neo-go/pkg/core/storage" + "github.com/CityOfZion/neo-go/pkg/core/testutil" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/smartcontract" @@ -337,9 +339,9 @@ func createVMAndPushTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop return v, tx, context } -func createVMAndAssetState(t *testing.T) (*vm.VM, *AssetState, *interopContext) { +func createVMAndAssetState(t *testing.T) (*vm.VM, *entities.AssetState, *interopContext) { v := vm.New() - assetState := &AssetState{ + assetState := &entities.AssetState{ ID: util.Uint256{}, AssetType: transaction.GoverningToken, Name: "TestAsset", @@ -347,10 +349,10 @@ func createVMAndAssetState(t *testing.T) (*vm.VM, *AssetState, *interopContext) Available: 2, Precision: 1, FeeMode: 1, - FeeAddress: randomUint160(), + FeeAddress: testutil.RandomUint160(), Owner: keys.PublicKey{X: big.NewInt(1), Y: big.NewInt(1)}, - Admin: randomUint160(), - Issuer: randomUint160(), + Admin: testutil.RandomUint160(), + Issuer: testutil.RandomUint160(), Expiration: 10, IsFrozen: false, } @@ -359,30 +361,29 @@ func createVMAndAssetState(t *testing.T) (*vm.VM, *AssetState, *interopContext) return v, assetState, context } -func createVMAndContractState(t *testing.T) (*vm.VM, *ContractState, *interopContext) { +func createVMAndContractState(t *testing.T) (*vm.VM, *entities.ContractState, *interopContext) { v := vm.New() - contractState := &ContractState{ + contractState := &entities.ContractState{ Script: []byte("testscript"), ParamList: []smartcontract.ParamType{smartcontract.StringType, smartcontract.IntegerType, smartcontract.Hash160Type}, ReturnType: smartcontract.ArrayType, Properties: smartcontract.HasStorage, - Name: randomString(10), - CodeVersion: randomString(10), - Author: randomString(10), - Email: randomString(10), - Description: randomString(10), - scriptHash: randomUint160(), + Name: testutil.RandomString(10), + CodeVersion: testutil.RandomString(10), + Author: testutil.RandomString(10), + Email: testutil.RandomString(10), + Description: testutil.RandomString(10), } context := newInteropContext(trigger.Application, newTestChain(t), storage.NewMemoryStore(), nil, nil) return v, contractState, context } -func createVMAndAccState(t *testing.T) (*vm.VM, *AccountState, *interopContext) { +func createVMAndAccState(t *testing.T) (*vm.VM, *entities.AccountState, *interopContext) { v := vm.New() rawHash := "4d3b96ae1bcc5a585e075e3b81920210dec16302" hash, err := util.Uint160DecodeStringBE(rawHash) - accountState := NewAccountState(hash) + accountState := entities.NewAccountState(hash) key := &keys.PublicKey{X: big.NewInt(1), Y: big.NewInt(1)} accountState.Votes = []*keys.PublicKey{key} @@ -403,14 +404,14 @@ func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interopCont }) inputs := append(tx.Inputs, transaction.Input{ - PrevHash: randomUint256(), + PrevHash: testutil.RandomUint256(), PrevIndex: 1, }) outputs := append(tx.Outputs, transaction.Output{ - AssetID: randomUint256(), + AssetID: testutil.RandomUint256(), Amount: 10, - ScriptHash: randomUint160(), + ScriptHash: testutil.RandomUint160(), Position: 1, }) diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index 8de8aee8f..ce104bb09 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -5,6 +5,7 @@ import ( "fmt" "math" + "github.com/CityOfZion/neo-go/pkg/core/entities" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/hash" "github.com/CityOfZion/neo-go/pkg/crypto/keys" @@ -341,7 +342,7 @@ func (ic *interopContext) runtimeCheckWitness(v *vm.VM) error { func (ic *interopContext) runtimeNotify(v *vm.VM) error { // It can be just about anything. e := v.Estack().Pop() - ne := NotificationEvent{getContextScriptHash(v, 0), e.Item()} + ne := entities.NotificationEvent{ScriptHash:getContextScriptHash(v, 0), Item:e.Item()} ic.notifications = append(ic.notifications, ne) return nil } @@ -410,11 +411,11 @@ func (ic *interopContext) storageDelete(v *vm.VM) error { return err } key := v.Estack().Pop().Bytes() - si := getStorageItemFromStore(ic.mem, stc.ScriptHash, key) + si := ic.dao.GetStorageItem(stc.ScriptHash, key) if si != nil && si.IsConst { return errors.New("storage item is constant") } - return deleteStorageItemInStore(ic.mem, stc.ScriptHash, key) + return ic.dao.DeleteStorageItem(stc.ScriptHash, key) } // storageGet returns stored key-value pair. @@ -429,7 +430,7 @@ func (ic *interopContext) storageGet(v *vm.VM) error { return err } key := v.Estack().Pop().Bytes() - si := getStorageItemFromStore(ic.mem, stc.ScriptHash, key) + si := ic.dao.GetStorageItem(stc.ScriptHash, key) if si != nil && si.Value != nil { v.Estack().PushVal(si.Value) } else { @@ -472,16 +473,16 @@ func (ic *interopContext) putWithContextAndFlags(stc *StorageContext, key []byte if err != nil { return err } - si := getStorageItemFromStore(ic.mem, stc.ScriptHash, key) + si := ic.dao.GetStorageItem(stc.ScriptHash, key) if si == nil { - si = &StorageItem{} + si = &entities.StorageItem{} } if si.IsConst { return errors.New("storage item exists and is read-only") } si.Value = value si.IsConst = isConst - return putStorageItemIntoStore(ic.mem, stc.ScriptHash, key, si) + return ic.dao.PutStorageItem(stc.ScriptHash, key, si) } // storagePutInternal is a unified implementation of storagePut and storagePutEx. @@ -538,7 +539,7 @@ func (ic *interopContext) contractDestroy(v *vm.VM) error { if cs == nil { return nil } - err := deleteContractStateInStore(ic.mem, hash) + err := ic.dao.DeleteContractState(hash) if err != nil { return err } @@ -548,7 +549,7 @@ func (ic *interopContext) contractDestroy(v *vm.VM) error { return err } for k := range siMap { - _ = deleteStorageItemInStore(ic.mem, hash, []byte(k)) + _ = ic.dao.DeleteStorageItem(hash, []byte(k)) } } return nil @@ -557,11 +558,12 @@ func (ic *interopContext) contractDestroy(v *vm.VM) error { // contractGetStorageContext retrieves StorageContext of a contract. func (ic *interopContext) contractGetStorageContext(v *vm.VM) error { csInterface := v.Estack().Pop().Value() - cs, ok := csInterface.(*ContractState) + cs, ok := csInterface.(*entities.ContractState) if !ok { return fmt.Errorf("%T is not a contract state", cs) } - if getContractStateFromStore(ic.mem, cs.ScriptHash()) == nil { + contractState, err := ic.dao.GetContractState(cs.ScriptHash()) + if contractState == nil || err != nil { return fmt.Errorf("contract was not created in this transaction") } stc := &StorageContext{ diff --git a/pkg/core/interops.go b/pkg/core/interops.go index deafb6e8b..53691f5bf 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -8,6 +8,7 @@ package core */ import ( + "github.com/CityOfZion/neo-go/pkg/core/entities" "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/vm" @@ -18,14 +19,14 @@ type interopContext struct { trigger byte block *Block tx *transaction.Transaction - mem *storage.MemCachedStore - notifications []NotificationEvent + dao *dao + notifications []entities.NotificationEvent } func newInteropContext(trigger byte, bc Blockchainer, s storage.Store, block *Block, tx *transaction.Transaction) *interopContext { - mem := storage.NewMemCachedStore(s) - nes := make([]NotificationEvent, 0) - return &interopContext{bc, trigger, block, tx, mem, nes} + dao := &dao{store: storage.NewMemCachedStore(s)} + nes := make([]entities.NotificationEvent, 0) + return &interopContext{bc, trigger, block, tx, dao, nes} } // All lists are sorted, keep 'em this way, please. diff --git a/pkg/core/spent_coin_state.go b/pkg/core/spent_coin_state.go index f298ff9f5..67332f766 100644 --- a/pkg/core/spent_coin_state.go +++ b/pkg/core/spent_coin_state.go @@ -1,60 +1,10 @@ package core import ( - "fmt" - - "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" ) -// SpentCoins is mapping between transactions and their spent -// coin state. -type SpentCoins map[util.Uint256]*SpentCoinState - -func (s SpentCoins) getAndUpdate(store storage.Store, hash util.Uint256) (*SpentCoinState, error) { - if spent, ok := s[hash]; ok { - return spent, nil - } - - spent := &SpentCoinState{} - key := storage.AppendPrefix(storage.STSpentCoin, hash.BytesLE()) - if b, err := store.Get(key); err == nil { - r := io.NewBinReaderFromBuf(b) - spent.DecodeBinary(r) - if r.Err != nil { - return nil, fmt.Errorf("failed to decode (UnspentCoinState): %s", r.Err) - } - } else { - spent = &SpentCoinState{ - items: make(map[uint16]uint32), - } - } - - s[hash] = spent - return spent, nil -} - -// putSpentCoinStateIntoStore puts given SpentCoinState into the given store. -func putSpentCoinStateIntoStore(store storage.Store, hash util.Uint256, scs *SpentCoinState) error { - buf := io.NewBufBinWriter() - scs.EncodeBinary(buf.BinWriter) - if buf.Err != nil { - return buf.Err - } - key := storage.AppendPrefix(storage.STSpentCoin, hash.BytesLE()) - return store.Put(key, buf.Bytes()) -} - -func (s SpentCoins) commit(store storage.Store) error { - for hash, state := range s { - if err := putSpentCoinStateIntoStore(store, hash, state); err != nil { - return err - } - } - return nil -} - // SpentCoinState represents the state of a spent coin. type SpentCoinState struct { txHash util.Uint256 diff --git a/pkg/core/spent_coin_state_test.go b/pkg/core/spent_coin_state_test.go index 0aa1c2fad..0e3b65474 100644 --- a/pkg/core/spent_coin_state_test.go +++ b/pkg/core/spent_coin_state_test.go @@ -3,15 +3,14 @@ package core import ( "testing" - "github.com/CityOfZion/neo-go/pkg/core/storage" + "github.com/CityOfZion/neo-go/pkg/core/testutil" "github.com/CityOfZion/neo-go/pkg/io" - "github.com/CityOfZion/neo-go/pkg/util" "github.com/stretchr/testify/assert" ) func TestEncodeDecodeSpentCoinState(t *testing.T) { spent := &SpentCoinState{ - txHash: randomUint256(), + txHash: testutil.RandomUint256(), txHeight: 1001, items: map[uint16]uint32{ 1: 3, @@ -29,24 +28,3 @@ func TestEncodeDecodeSpentCoinState(t *testing.T) { assert.Nil(t, r.Err) assert.Equal(t, spent, spentDecode) } - -func TestCommitSpentCoins(t *testing.T) { - var ( - store = storage.NewMemoryStore() - spentCoins = make(SpentCoins) - ) - - txx := []util.Uint256{ - randomUint256(), - randomUint256(), - randomUint256(), - } - - for i := 0; i < len(txx); i++ { - spentCoins[txx[i]] = &SpentCoinState{ - txHash: txx[i], - txHeight: 1, - } - } - assert.Nil(t, spentCoins.commit(store)) -} diff --git a/pkg/core/storage/helpers.go b/pkg/core/storage/helpers.go deleted file mode 100644 index 575cbe84e..000000000 --- a/pkg/core/storage/helpers.go +++ /dev/null @@ -1,96 +0,0 @@ -package storage - -import ( - "bytes" - "encoding/binary" - "sort" - - "github.com/CityOfZion/neo-go/pkg/io" - "github.com/CityOfZion/neo-go/pkg/util" -) - -// Version attempts to get the current version stored in the -// underlying Store. -func Version(s Store) (string, error) { - version, err := s.Get(SYSVersion.Bytes()) - return string(version), err -} - -// PutVersion stores the given version in the underlying Store. -func PutVersion(s Store, v string) error { - return s.Put(SYSVersion.Bytes(), []byte(v)) -} - -// CurrentBlockHeight returns the current block height found in the -// underlying Store. -func CurrentBlockHeight(s Store) (uint32, error) { - b, err := s.Get(SYSCurrentBlock.Bytes()) - if err != nil { - return 0, err - } - return binary.LittleEndian.Uint32(b[32:36]), nil -} - -// CurrentHeaderHeight returns the current header height and hash from -// the underlying Store. -func CurrentHeaderHeight(s Store) (i uint32, h util.Uint256, err error) { - var b []byte - b, err = s.Get(SYSCurrentHeader.Bytes()) - if err != nil { - return - } - i = binary.LittleEndian.Uint32(b[32:36]) - h, err = util.Uint256DecodeBytesLE(b[:32]) - return -} - -// uint32Slice attaches the methods of Interface to []int, sorting in increasing order. -type uint32Slice []uint32 - -func (p uint32Slice) Len() int { return len(p) } -func (p uint32Slice) Less(i, j int) bool { return p[i] < p[j] } -func (p uint32Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } - -// HeaderHashes returns a sorted list of header hashes retrieved from -// the given underlying Store. -func HeaderHashes(s Store) ([]util.Uint256, error) { - hashMap := make(map[uint32][]util.Uint256) - s.Seek(IXHeaderHashList.Bytes(), func(k, v []byte) { - storedCount := binary.LittleEndian.Uint32(k[1:]) - hashes, err := read2000Uint256Hashes(v) - if err != nil { - panic(err) - } - hashMap[storedCount] = hashes - }) - - var ( - hashes = make([]util.Uint256, 0, len(hashMap)) - sortedKeys = make([]uint32, 0, len(hashMap)) - ) - - for k := range hashMap { - sortedKeys = append(sortedKeys, k) - } - sort.Sort(uint32Slice(sortedKeys)) - - for _, key := range sortedKeys { - hashes = append(hashes[:key], hashMap[key]...) - } - - return hashes, nil -} - -// read2000Uint256Hashes attempts to read 2000 Uint256 hashes from -// the given byte array. -func read2000Uint256Hashes(b []byte) ([]util.Uint256, error) { - r := bytes.NewReader(b) - br := io.NewBinReaderFromIO(r) - lenHashes := br.ReadVarUint() - hashes := make([]util.Uint256, lenHashes) - br.ReadLE(hashes) - if br.Err != nil { - return nil, br.Err - } - return hashes, nil -} diff --git a/pkg/core/storage_item.go b/pkg/core/storage_item.go deleted file mode 100644 index b61b66a2d..000000000 --- a/pkg/core/storage_item.go +++ /dev/null @@ -1,64 +0,0 @@ -package core - -import ( - "github.com/CityOfZion/neo-go/pkg/core/storage" - "github.com/CityOfZion/neo-go/pkg/io" - "github.com/CityOfZion/neo-go/pkg/util" -) - -// StorageItem is the value to be stored with read-only flag. -type StorageItem struct { - Value []byte - IsConst bool -} - -// makeStorageItemKey returns a key used to store StorageItem in the DB. -func makeStorageItemKey(scripthash util.Uint160, key []byte) []byte { - return storage.AppendPrefix(storage.STStorage, append(scripthash.BytesLE(), key...)) -} - -// getStorageItemFromStore returns StorageItem if it exists in the given Store. -func getStorageItemFromStore(s storage.Store, scripthash util.Uint160, key []byte) *StorageItem { - b, err := s.Get(makeStorageItemKey(scripthash, key)) - if err != nil { - return nil - } - r := io.NewBinReaderFromBuf(b) - - si := &StorageItem{} - si.DecodeBinary(r) - if r.Err != nil { - return nil - } - - return si -} - -// putStorageItemIntoStore puts given StorageItem for given script with given -// key into the given Store. -func putStorageItemIntoStore(s storage.Store, scripthash util.Uint160, key []byte, si *StorageItem) error { - buf := io.NewBufBinWriter() - si.EncodeBinary(buf.BinWriter) - if buf.Err != nil { - return buf.Err - } - return s.Put(makeStorageItemKey(scripthash, key), buf.Bytes()) -} - -// deleteStorageItemInStore drops storage item for the given script with the -// given key from the Store. -func deleteStorageItemInStore(s storage.Store, scripthash util.Uint160, key []byte) error { - return s.Delete(makeStorageItemKey(scripthash, key)) -} - -// EncodeBinary implements Serializable interface. -func (si *StorageItem) EncodeBinary(w *io.BinWriter) { - w.WriteVarBytes(si.Value) - w.WriteLE(si.IsConst) -} - -// DecodeBinary implements Serializable interface. -func (si *StorageItem) DecodeBinary(r *io.BinReader) { - si.Value = r.ReadVarBytes() - r.ReadLE(&si.IsConst) -} diff --git a/pkg/core/storage_item_test.go b/pkg/core/storage_item_test.go deleted file mode 100644 index 41bca1a94..000000000 --- a/pkg/core/storage_item_test.go +++ /dev/null @@ -1,26 +0,0 @@ -package core - -import ( - "testing" - - "github.com/CityOfZion/neo-go/pkg/core/storage" - "github.com/CityOfZion/neo-go/pkg/util" - "github.com/stretchr/testify/assert" -) - -func TestPutGetDeleteStorageItem(t *testing.T) { - s := storage.NewMemoryStore() - si := &StorageItem{ - Value: []byte("smth"), - } - key := []byte("key") - cHash, err := util.Uint160DecodeBytesBE([]byte("abcdefghijklmnopqrst")) - assert.Nil(t, err) - assert.NoError(t, putStorageItemIntoStore(s, cHash, key, si)) - siRead := getStorageItemFromStore(s, cHash, key) - assert.NotNil(t, siRead) - assert.Equal(t, si, siRead) - assert.NoError(t, deleteStorageItemInStore(s, cHash, key)) - siRead2 := getStorageItemFromStore(s, cHash, key) - assert.Nil(t, siRead2) -} diff --git a/pkg/core/random_util_test.go b/pkg/core/testutil/random_util.go similarity index 63% rename from pkg/core/random_util_test.go rename to pkg/core/testutil/random_util.go index dc2b6150d..c58832e89 100644 --- a/pkg/core/random_util_test.go +++ b/pkg/core/testutil/random_util.go @@ -1,4 +1,4 @@ -package core +package testutil import ( "math/rand" @@ -9,29 +9,29 @@ import ( ) // RandomString returns a random string with the n as its length. -func randomString(n int) string { +func RandomString(n int) string { b := make([]byte, n) for i := range b { - b[i] = byte(randomInt(65, 90)) + b[i] = byte(RandomInt(65, 90)) } return string(b) } -// RandomInt returns a random integer between min and max. -func randomInt(min, max int) int { +// RandomInt returns a random integer in [min,max). +func RandomInt(min, max int) int { return min + rand.Intn(max-min) } // RandomUint256 returns a random Uint256. -func randomUint256() util.Uint256 { - str := randomString(20) +func RandomUint256() util.Uint256 { + str := RandomString(20) return hash.Sha256([]byte(str)) } // RandomUint160 returns a random Uint160. -func randomUint160() util.Uint160 { - str := randomString(20) +func RandomUint160() util.Uint160 { + str := RandomString(20) return hash.RipeMD160([]byte(str)) } diff --git a/pkg/core/uint32.go b/pkg/core/uint32.go new file mode 100644 index 000000000..27e27fb94 --- /dev/null +++ b/pkg/core/uint32.go @@ -0,0 +1,9 @@ +package core + +// slice attaches the methods of Interface to []int, sorting in increasing order. +type slice []uint32 + +func (p slice) Len() int { return len(p) } +func (p slice) Less(i, j int) bool { return p[i] < p[j] } +func (p slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } + diff --git a/pkg/core/unspent_coin_state.go b/pkg/core/unspent_coin_state.go index 2dffd98df..3823a8879 100644 --- a/pkg/core/unspent_coin_state.go +++ b/pkg/core/unspent_coin_state.go @@ -1,93 +1,26 @@ package core import ( - "fmt" - - "github.com/CityOfZion/neo-go/pkg/core/storage" - "github.com/CityOfZion/neo-go/pkg/core/transaction" + "github.com/CityOfZion/neo-go/pkg/core/entities" "github.com/CityOfZion/neo-go/pkg/io" - "github.com/CityOfZion/neo-go/pkg/util" ) -// UnspentCoins is mapping between transactions and their unspent -// coin state. -type UnspentCoins map[util.Uint256]*UnspentCoinState - -// getAndUpdate retreives UnspentCoinState from temporary or persistent Store -// and return it. If it's not present in both stores, returns a new -// UnspentCoinState. -func (u UnspentCoins) getAndUpdate(s storage.Store, hash util.Uint256) (*UnspentCoinState, error) { - if unspent, ok := u[hash]; ok { - return unspent, nil - } - - unspent, err := getUnspentCoinStateFromStore(s, hash) - if err != nil { - if err != storage.ErrKeyNotFound { - return nil, err - } - unspent = &UnspentCoinState{ - states: []CoinState{}, - } - } - - u[hash] = unspent - return unspent, nil -} - -// getUnspentCoinStateFromStore retrieves UnspentCoinState from the given store -func getUnspentCoinStateFromStore(s storage.Store, hash util.Uint256) (*UnspentCoinState, error) { - unspent := &UnspentCoinState{} - key := storage.AppendPrefix(storage.STCoin, hash.BytesLE()) - if b, err := s.Get(key); err == nil { - r := io.NewBinReaderFromBuf(b) - unspent.DecodeBinary(r) - if r.Err != nil { - return nil, fmt.Errorf("failed to decode (UnspentCoinState): %s", r.Err) - } - } else { - return nil, err - } - return unspent, nil -} - -// putUnspentCoinStateIntoStore puts given UnspentCoinState into the given store. -func putUnspentCoinStateIntoStore(store storage.Store, hash util.Uint256, ucs *UnspentCoinState) error { - buf := io.NewBufBinWriter() - ucs.EncodeBinary(buf.BinWriter) - if buf.Err != nil { - return buf.Err - } - key := storage.AppendPrefix(storage.STCoin, hash.BytesLE()) - return store.Put(key, buf.Bytes()) -} - // UnspentCoinState hold the state of a unspent coin. type UnspentCoinState struct { - states []CoinState + states []entities.CoinState } // NewUnspentCoinState returns a new unspent coin state with N confirmed states. func NewUnspentCoinState(n int) *UnspentCoinState { u := &UnspentCoinState{ - states: make([]CoinState, n), + states: make([]entities.CoinState, n), } for i := 0; i < n; i++ { - u.states[i] = CoinStateConfirmed + u.states[i] = entities.CoinStateConfirmed } return u } -// commit writes all unspent coin states to the given Batch. -func (u UnspentCoins) commit(store storage.Store) error { - for hash, state := range u { - if err := putUnspentCoinStateIntoStore(store, hash, state); err != nil { - return err - } - } - return nil -} - // EncodeBinary encodes UnspentCoinState to the given BinWriter. func (s *UnspentCoinState) EncodeBinary(bw *io.BinWriter) { bw.WriteVarUint(uint64(len(s.states))) @@ -99,40 +32,10 @@ func (s *UnspentCoinState) EncodeBinary(bw *io.BinWriter) { // DecodeBinary decodes UnspentCoinState from the given BinReader. func (s *UnspentCoinState) DecodeBinary(br *io.BinReader) { lenStates := br.ReadVarUint() - s.states = make([]CoinState, lenStates) + s.states = make([]entities.CoinState, lenStates) for i := 0; i < int(lenStates); i++ { var state uint8 br.ReadLE(&state) - s.states[i] = CoinState(state) + s.states[i] = entities.CoinState(state) } } - -// IsDoubleSpend verifies that the input transactions are not double spent. -func IsDoubleSpend(s storage.Store, tx *transaction.Transaction) bool { - if len(tx.Inputs) == 0 { - return false - } - - for prevHash, inputs := range tx.GroupInputsByPrevHash() { - unspent := &UnspentCoinState{} - key := storage.AppendPrefix(storage.STCoin, prevHash.BytesLE()) - if b, err := s.Get(key); err == nil { - r := io.NewBinReaderFromBuf(b) - unspent.DecodeBinary(r) - if r.Err != nil { - return false - } - - for _, input := range inputs { - if int(input.PrevIndex) >= len(unspent.states) || unspent.states[input.PrevIndex] == CoinStateSpent { - return true - } - } - } else { - return true - } - - } - - return false -} diff --git a/pkg/core/unspent_coint_state_test.go b/pkg/core/unspent_coint_state_test.go index 5a475b605..18627feab 100644 --- a/pkg/core/unspent_coint_state_test.go +++ b/pkg/core/unspent_coint_state_test.go @@ -3,19 +3,19 @@ package core import ( "testing" - "github.com/CityOfZion/neo-go/pkg/core/storage" + "github.com/CityOfZion/neo-go/pkg/core/entities" "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) func TestDecodeEncodeUnspentCoinState(t *testing.T) { unspent := &UnspentCoinState{ - states: []CoinState{ - CoinStateConfirmed, - CoinStateSpent, - CoinStateSpent, - CoinStateSpent, - CoinStateConfirmed, + states: []entities.CoinState{ + entities.CoinStateConfirmed, + entities.CoinStateSpent, + entities.CoinStateSpent, + entities.CoinStateSpent, + entities.CoinStateConfirmed, }, } @@ -27,33 +27,3 @@ func TestDecodeEncodeUnspentCoinState(t *testing.T) { unspentDecode.DecodeBinary(r) assert.Nil(t, r.Err) } - -func TestCommitUnspentCoins(t *testing.T) { - var ( - store = storage.NewMemoryStore() - unspentCoins = make(UnspentCoins) - ) - - txA := randomUint256() - txB := randomUint256() - txC := randomUint256() - - unspentCoins[txA] = &UnspentCoinState{ - states: []CoinState{CoinStateConfirmed}, - } - unspentCoins[txB] = &UnspentCoinState{ - states: []CoinState{ - CoinStateConfirmed, - CoinStateConfirmed, - }, - } - unspentCoins[txC] = &UnspentCoinState{ - states: []CoinState{ - CoinStateConfirmed, - CoinStateConfirmed, - CoinStateConfirmed, - }, - } - - assert.Nil(t, unspentCoins.commit(store)) -} diff --git a/pkg/core/validator_state_test.go b/pkg/core/validator_state_test.go deleted file mode 100644 index 617e3ff50..000000000 --- a/pkg/core/validator_state_test.go +++ /dev/null @@ -1,121 +0,0 @@ -package core - -import ( - "math/big" - "testing" - - "github.com/CityOfZion/neo-go/pkg/core/storage" - "github.com/CityOfZion/neo-go/pkg/crypto/keys" - "github.com/CityOfZion/neo-go/pkg/io" - "github.com/CityOfZion/neo-go/pkg/util" - "github.com/stretchr/testify/require" -) - -func TestGetAndUpdate(t *testing.T) { - store := storage.NewMemoryStore() - state1 := getDefaultValidator() - state2 := getDefaultValidator() - validators := make(Validators) - validators[state1.PublicKey] = state1 - validators[state2.PublicKey] = state2 - err := validators.commit(store) - require.NoError(t, err) - - state, err := validators.getAndUpdate(store, state1.PublicKey) - require.NoError(t, err) - require.Equal(t, state1, state) -} - -func TestCommit(t *testing.T) { - store := storage.NewMemoryStore() - state1 := getDefaultValidator() - state2 := getDefaultValidator() - validators := make(Validators) - validators[state1.PublicKey] = state1 - validators[state2.PublicKey] = state2 - err := validators.commit(store) - require.NoError(t, err) - - validatorsFromStore := getValidatorsFromStore(store) - // 2 equal validators will be stored as 1 unique - require.Len(t, validatorsFromStore, 1) - require.Equal(t, state1, validatorsFromStore[0]) -} - -func TestPutAndGet(t *testing.T) { - store := storage.NewMemoryStore() - state := getDefaultValidator() - err := putValidatorStateIntoStore(store, state) - require.NoError(t, err) - validatorFromStore, err := getValidatorStateFromStore(store, state.PublicKey) - require.NoError(t, err) - require.Equal(t, state.PublicKey, validatorFromStore.PublicKey) -} - -func TestGetFromStore_NoKey(t *testing.T) { - store := storage.NewMemoryStore() - state := getDefaultValidator() - _, err := getValidatorStateFromStore(store, state.PublicKey) - require.Errorf(t, err, "key not found") -} - -func TestValidatorState_DecodeEncodeBinary(t *testing.T) { - state := &ValidatorState{ - PublicKey: &keys.PublicKey{}, - Registered: false, - Votes: util.Fixed8(10), - } - buf := io.NewBufBinWriter() - state.EncodeBinary(buf.BinWriter) - require.NoError(t, buf.Err) - - decodedState := &ValidatorState{} - reader := io.NewBinReaderFromBuf(buf.Bytes()) - decodedState.DecodeBinary(reader) - require.NoError(t, reader.Err) - require.Equal(t, state, decodedState) -} - -func TestRegisteredAndHasVotes_Registered(t *testing.T) { - state := &ValidatorState{ - PublicKey: &keys.PublicKey{ - X: big.NewInt(1), - Y: big.NewInt(1), - }, - Registered: true, - Votes: 0, - } - require.False(t, state.RegisteredAndHasVotes()) -} - -func TestRegisteredAndHasVotes_RegisteredWithVotes(t *testing.T) { - state := &ValidatorState{ - PublicKey: &keys.PublicKey{ - X: big.NewInt(1), - Y: big.NewInt(1), - }, - Registered: true, - Votes: 1, - } - require.True(t, state.RegisteredAndHasVotes()) -} - -func TestRegisteredAndHasVotes_NotRegisteredWithVotes(t *testing.T) { - state := &ValidatorState{ - PublicKey: &keys.PublicKey{ - X: big.NewInt(1), - Y: big.NewInt(1), - }, - Registered: false, - Votes: 1, - } - require.False(t, state.RegisteredAndHasVotes()) -} - -func getDefaultValidator() *ValidatorState { - return &ValidatorState{ - PublicKey: &keys.PublicKey{}, - Registered: false, - Votes: 0, - } -} diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index 14daf897e..afbcc921e 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -9,6 +9,7 @@ import ( "github.com/CityOfZion/neo-go/config" "github.com/CityOfZion/neo-go/pkg/core" + "github.com/CityOfZion/neo-go/pkg/core/entities" "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" @@ -62,7 +63,7 @@ func (chain testChain) HeaderHeight() uint32 { func (chain testChain) GetBlock(hash util.Uint256) (*core.Block, error) { panic("TODO") } -func (chain testChain) GetContractState(hash util.Uint160) *core.ContractState { +func (chain testChain) GetContractState(hash util.Uint160) *entities.ContractState { panic("TODO") } func (chain testChain) GetHeaderHash(int) util.Uint256 { @@ -72,10 +73,10 @@ func (chain testChain) GetHeader(hash util.Uint256) (*core.Header, error) { panic("TODO") } -func (chain testChain) GetAssetState(util.Uint256) *core.AssetState { +func (chain testChain) GetAssetState(util.Uint256) *entities.AssetState { panic("TODO") } -func (chain testChain) GetAccountState(util.Uint160) *core.AccountState { +func (chain testChain) GetAccountState(util.Uint160) *entities.AccountState { panic("TODO") } func (chain testChain) GetValidators(...*transaction.Transaction) ([]*keys.PublicKey, error) { @@ -84,13 +85,13 @@ func (chain testChain) GetValidators(...*transaction.Transaction) ([]*keys.Publi func (chain testChain) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) { panic("TODO") } -func (chain testChain) GetStorageItem(scripthash util.Uint160, key []byte) *core.StorageItem { +func (chain testChain) GetStorageItem(scripthash util.Uint160, key []byte) *entities.StorageItem { panic("TODO") } func (chain testChain) GetTestVM() (*vm.VM, storage.Store) { panic("TODO") } -func (chain testChain) GetStorageItems(hash util.Uint160) (map[string]*core.StorageItem, error) { +func (chain testChain) GetStorageItems(hash util.Uint160) (map[string]*entities.StorageItem, error) { panic("TODO") } func (chain testChain) CurrentHeaderHash() util.Uint256 { diff --git a/pkg/rpc/client.go b/pkg/rpc/client.go index f15f5c9b8..054861b64 100644 --- a/pkg/rpc/client.go +++ b/pkg/rpc/client.go @@ -11,7 +11,7 @@ import ( "sync" "time" - "github.com/CityOfZion/neo-go/pkg/core" + "github.com/CityOfZion/neo-go/pkg/core/entities" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/util" @@ -158,7 +158,7 @@ func (c *Client) SetClient(cli *http.Client) { // asset belonging to specified address. This implementation uses GetUnspents // JSON-RPC call internally, so make sure your RPC server suppors that. func (c *Client) CalculateInputs(address string, asset util.Uint256, cost util.Fixed8) ([]transaction.Input, util.Fixed8, error) { - var utxos core.UnspentBalances + var utxos entities.UnspentBalances resp, err := c.GetUnspents(address) if err != nil || resp.Error != nil { diff --git a/pkg/rpc/neoScanBalanceGetter.go b/pkg/rpc/neoScanBalanceGetter.go index 257bda0cb..709cb64e5 100644 --- a/pkg/rpc/neoScanBalanceGetter.go +++ b/pkg/rpc/neoScanBalanceGetter.go @@ -6,7 +6,7 @@ import ( "net/http" "sort" - "github.com/CityOfZion/neo-go/pkg/core" + "github.com/CityOfZion/neo-go/pkg/core/entities" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/rpc/wrappers" "github.com/CityOfZion/neo-go/pkg/util" @@ -68,7 +68,7 @@ func (s NeoScanServer) CalculateInputs(address string, assetIDUint util.Uint256, // unspentsToInputs uses UnspentBalances to create a slice of inputs for a new // transcation containing the required amount of asset. -func unspentsToInputs(utxos core.UnspentBalances, required util.Fixed8) ([]transaction.Input, util.Fixed8, error) { +func unspentsToInputs(utxos entities.UnspentBalances, required util.Fixed8) ([]transaction.Input, util.Fixed8, error) { var ( num, i uint16 selected = util.Fixed8(0) diff --git a/pkg/rpc/neoScanTypes.go b/pkg/rpc/neoScanTypes.go index bafd0b648..7e050348e 100644 --- a/pkg/rpc/neoScanTypes.go +++ b/pkg/rpc/neoScanTypes.go @@ -1,7 +1,7 @@ package rpc import ( - "github.com/CityOfZion/neo-go/pkg/core" + "github.com/CityOfZion/neo-go/pkg/core/entities" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -20,7 +20,7 @@ type ( // Unspent stores Unspents per asset Unspent struct { - Unspent core.UnspentBalances + Unspent entities.UnspentBalances Asset string // "NEO" / "GAS" Amount util.Fixed8 // total unspent of this asset } diff --git a/pkg/rpc/wrappers/account_state.go b/pkg/rpc/wrappers/account_state.go index dcb6530f8..56f5d8569 100644 --- a/pkg/rpc/wrappers/account_state.go +++ b/pkg/rpc/wrappers/account_state.go @@ -2,9 +2,9 @@ package wrappers import ( "bytes" + "github.com/CityOfZion/neo-go/pkg/core/entities" "sort" - "github.com/CityOfZion/neo-go/pkg/core" "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -33,7 +33,7 @@ type Balance struct { } // NewAccountState creates a new AccountState wrapper. -func NewAccountState(a *core.AccountState) AccountState { +func NewAccountState(a *entities.AccountState) AccountState { balances := make(Balances, 0, len(a.Balances)) for k, v := range a.GetBalanceValues() { balances = append(balances, Balance{ diff --git a/pkg/rpc/wrappers/asset_state.go b/pkg/rpc/wrappers/asset_state.go index c439af8d9..e0f3e472c 100644 --- a/pkg/rpc/wrappers/asset_state.go +++ b/pkg/rpc/wrappers/asset_state.go @@ -1,7 +1,7 @@ package wrappers import ( - "github.com/CityOfZion/neo-go/pkg/core" + "github.com/CityOfZion/neo-go/pkg/core/entities" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto" "github.com/CityOfZion/neo-go/pkg/util" @@ -26,7 +26,7 @@ type AssetState struct { } // NewAssetState creates a new AssetState wrapper. -func NewAssetState(a *core.AssetState) AssetState { +func NewAssetState(a *entities.AssetState) AssetState { return AssetState{ ID: a.ID, AssetType: a.AssetType, diff --git a/pkg/rpc/wrappers/unspents.go b/pkg/rpc/wrappers/unspents.go index 6355b7d0c..ef10fb279 100644 --- a/pkg/rpc/wrappers/unspents.go +++ b/pkg/rpc/wrappers/unspents.go @@ -2,17 +2,18 @@ package wrappers import ( "github.com/CityOfZion/neo-go/pkg/core" + "github.com/CityOfZion/neo-go/pkg/core/entities" "github.com/CityOfZion/neo-go/pkg/util" ) // UnspentBalanceInfo wrapper is used to represent single unspent asset entry // in `getunspents` output. type UnspentBalanceInfo struct { - Unspents []core.UnspentBalance `json:"unspent"` - AssetHash util.Uint256 `json:"asset_hash"` - Asset string `json:"asset"` - AssetSymbol string `json:"asset_symbol"` - Amount util.Fixed8 `json:"amount"` + Unspents []entities.UnspentBalance `json:"unspent"` + AssetHash util.Uint256 `json:"asset_hash"` + Asset string `json:"asset"` + AssetSymbol string `json:"asset_symbol"` + Amount util.Fixed8 `json:"amount"` } // Unspents wrapper is used to represent getunspents return result. @@ -28,7 +29,7 @@ var GlobalAssets = map[string]string{ } // NewUnspents creates a new AccountState wrapper using given Blockchainer. -func NewUnspents(a *core.AccountState, chain core.Blockchainer, addr string) Unspents { +func NewUnspents(a *entities.AccountState, chain core.Blockchainer, addr string) Unspents { res := Unspents{ Address: addr, Balance: make([]UnspentBalanceInfo, 0, len(a.Balances)), From c7ac4b6dff8e7196ffb730554fa63b112f83e701 Mon Sep 17 00:00:00 2001 From: Vsevolod Brekelov Date: Tue, 26 Nov 2019 18:24:54 +0300 Subject: [PATCH 2/7] core: fix encoding and decoding for notification event --- pkg/core/blockchain.go | 2 +- pkg/core/entities/notification_event.go | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 1dc6dfb78..9228a4c68 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -27,7 +27,7 @@ import ( // Tuning parameters. const ( headerBatchCount = 2000 - version = "0.0.2" + version = "0.0.3" // This one comes from C# code and it's different from the constant used // when creating an asset with Neo.Asset.Create interop call. It looks diff --git a/pkg/core/entities/notification_event.go b/pkg/core/entities/notification_event.go index cf69815b2..47ea291ef 100644 --- a/pkg/core/entities/notification_event.go +++ b/pkg/core/entities/notification_event.go @@ -39,11 +39,19 @@ func (ne *NotificationEvent) DecodeBinary(r *io.BinReader) { // EncodeBinary implements the Serializable interface. func (aer *AppExecResult) EncodeBinary(w *io.BinWriter) { w.WriteBytes(aer.TxHash[:]) + w.WriteLE(aer.Trigger) + w.WriteString(aer.VMState) + w.WriteLE(aer.GasConsumed) + w.WriteString(aer.Stack) w.WriteArray(aer.Events) } // DecodeBinary implements the Serializable interface. func (aer *AppExecResult) DecodeBinary(r *io.BinReader) { r.ReadBytes(aer.TxHash[:]) + r.ReadLE(&aer.Trigger) + aer.VMState = r.ReadString() + r.ReadLE(&aer.GasConsumed) + aer.Stack = r.ReadString() r.ReadArray(&aer.Events) } From c0e59ebd4ef1778c996f5bd877d1aef93757efeb Mon Sep 17 00:00:00 2001 From: Vsevolod Brekelov Date: Tue, 26 Nov 2019 18:47:07 +0300 Subject: [PATCH 3/7] core: unit tests for entities --- pkg/core/entities/asset_state_test.go | 10 +++++ pkg/core/entities/notification_event_test.go | 44 ++++++++++++++++++++ pkg/core/entities/storage_item_test.go | 24 +++++++++++ 3 files changed, 78 insertions(+) create mode 100644 pkg/core/entities/notification_event_test.go diff --git a/pkg/core/entities/asset_state_test.go b/pkg/core/entities/asset_state_test.go index 084f1cd3c..58e4896ce 100644 --- a/pkg/core/entities/asset_state_test.go +++ b/pkg/core/entities/asset_state_test.go @@ -36,3 +36,13 @@ func TestEncodeDecodeAssetState(t *testing.T) { assert.Nil(t, r.Err) assert.Equal(t, asset, assetDecode) } + +func TestAssetState_GetName_NEO(t *testing.T) { + asset := &AssetState{AssetType: transaction.GoverningToken} + assert.Equal(t, "NEO", asset.GetName()) +} + +func TestAssetState_GetName_NEOGas(t *testing.T) { + asset := &AssetState{AssetType: transaction.UtilityToken} + assert.Equal(t, "NEOGas", asset.GetName()) +} diff --git a/pkg/core/entities/notification_event_test.go b/pkg/core/entities/notification_event_test.go new file mode 100644 index 000000000..6bd7f47dc --- /dev/null +++ b/pkg/core/entities/notification_event_test.go @@ -0,0 +1,44 @@ +package entities + +import ( + "testing" + + "github.com/CityOfZion/neo-go/pkg/core/testutil" + "github.com/CityOfZion/neo-go/pkg/io" + "github.com/stretchr/testify/assert" +) + +func TestEncodeDecodeNotificationEvent(t *testing.T) { + event := &NotificationEvent{ + ScriptHash: testutil.RandomUint160(), + Item: nil, + } + + buf := io.NewBufBinWriter() + event.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) + + eventDecoded := &NotificationEvent{} + reader := io.NewBinReaderFromBuf(buf.Bytes()) + eventDecoded.DecodeBinary(reader) + assert.Equal(t, event, eventDecoded) +} + +func TestEncodeDecodeAppExecResult(t *testing.T) { + appExecResult := &AppExecResult{ + TxHash: testutil.RandomUint256(), + Trigger: 1, + VMState: "Hault", + GasConsumed: 10, + Stack: "", + Events: []NotificationEvent{}, + } + buf := io.NewBufBinWriter() + appExecResult.EncodeBinary(buf.BinWriter) + assert.Nil(t, buf.Err) + + appExecResultDecoded := &AppExecResult{} + reader := io.NewBinReaderFromBuf(buf.Bytes()) + appExecResultDecoded.DecodeBinary(reader) + assert.Equal(t, appExecResult, appExecResultDecoded) +} diff --git a/pkg/core/entities/storage_item_test.go b/pkg/core/entities/storage_item_test.go index 870b5d166..6200cc19b 100644 --- a/pkg/core/entities/storage_item_test.go +++ b/pkg/core/entities/storage_item_test.go @@ -1,2 +1,26 @@ package entities +import ( + "testing" + + "github.com/CityOfZion/neo-go/pkg/io" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestEncodeDecodeStorageItem(t *testing.T) { + storageItem := &StorageItem{ + Value: []byte{}, + IsConst: false, + } + buf := io.NewBufBinWriter() + storageItem.EncodeBinary(buf.BinWriter) + require.NoError(t, buf.Err) + + decodedStorageItem := &StorageItem{} + r := io.NewBinReaderFromBuf(buf.Bytes()) + decodedStorageItem.DecodeBinary(r) + require.NoError(t, r.Err) + + assert.Equal(t, storageItem, decodedStorageItem) +} From 8809fe437dd70fd2e3b61b016d854c0023b91b9b Mon Sep 17 00:00:00 2001 From: Vsevolod Brekelov Date: Wed, 27 Nov 2019 13:29:54 +0300 Subject: [PATCH 4/7] core: unit tests for dao --- pkg/core/dao_test.go | 339 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 pkg/core/dao_test.go diff --git a/pkg/core/dao_test.go b/pkg/core/dao_test.go new file mode 100644 index 000000000..1bc2dbfff --- /dev/null +++ b/pkg/core/dao_test.go @@ -0,0 +1,339 @@ +package core + +import ( + "testing" + + "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/storage" + "github.com/CityOfZion/neo-go/pkg/core/testutil" + "github.com/CityOfZion/neo-go/pkg/core/transaction" + "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/io" + "github.com/CityOfZion/neo-go/pkg/smartcontract" + "github.com/CityOfZion/neo-go/pkg/vm/opcode" + "github.com/stretchr/testify/require" +) + +func TestPutGetAndDecode(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + serializable := &TestSerializable{field: testutil.RandomString(4)} + hash := []byte{1} + err := dao.Put(serializable, hash) + require.NoError(t, err) + + gotAndDecoded := &TestSerializable{} + err = dao.GetAndDecode(gotAndDecoded, hash) + require.NoError(t, err) +} + +// TestSerializable structure used in testing. +type TestSerializable struct { + field string +} + +func (t *TestSerializable) EncodeBinary(writer *io.BinWriter) { + writer.WriteString(t.field) +} + +func (t *TestSerializable) DecodeBinary(reader *io.BinReader) { + t.field = reader.ReadString() +} + +func TestGetAccountStateOrNew_New(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + hash := testutil.RandomUint160() + createdAccount, err := dao.GetAccountStateOrNew(hash) + require.NoError(t, err) + require.NotNil(t, createdAccount) + gotAccount, err := dao.GetAccountState(hash) + require.NoError(t, err) + require.Equal(t, createdAccount, gotAccount) +} + +func TestPutAndGetAccountStateOrNew(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + hash := testutil.RandomUint160() + accountState := &entities.AccountState{ScriptHash: hash} + err := dao.PutAccountState(accountState) + require.NoError(t, err) + gotAccount, err := dao.GetAccountStateOrNew(hash) + require.NoError(t, err) + require.Equal(t, accountState.ScriptHash, gotAccount.ScriptHash) +} + +func TestPutAndGetAssetState(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + id := testutil.RandomUint256() + assetState := &entities.AssetState{ID: id, Owner: keys.PublicKey{}} + err := dao.PutAssetState(assetState) + require.NoError(t, err) + gotAssetState, err := dao.GetAssetState(id) + require.NoError(t, err) + require.Equal(t, assetState, gotAssetState) +} + +func TestPutAndGetContractState(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + contractState := &entities.ContractState{Script: []byte{}, ParamList:[]smartcontract.ParamType{}} + hash := contractState.ScriptHash() + err := dao.PutContractState(contractState) + require.NoError(t, err) + gotContractState, err := dao.GetContractState(hash) + require.NoError(t, err) + require.Equal(t, contractState, gotContractState) +} + +func TestDeleteContractState(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + contractState := &entities.ContractState{Script: []byte{}, ParamList:[]smartcontract.ParamType{}} + hash := contractState.ScriptHash() + err := dao.PutContractState(contractState) + require.NoError(t, err) + err = dao.DeleteContractState(hash) + require.NoError(t, err) + gotContractState, err := dao.GetContractState(hash) + require.Error(t, err) + require.Nil(t, gotContractState) +} + +func TestGetUnspentCoinStateOrNew_New(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + hash := testutil.RandomUint256() + unspentCoinState, err := dao.GetUnspentCoinStateOrNew(hash) + require.NoError(t, err) + require.NotNil(t, unspentCoinState) + gotUnspentCoinState, err := dao.GetUnspentCoinState(hash) + require.NoError(t, err) + require.Equal(t, unspentCoinState, gotUnspentCoinState) +} + +func TestGetUnspentCoinState_Err(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + hash := testutil.RandomUint256() + gotUnspentCoinState, err := dao.GetUnspentCoinState(hash) + require.Error(t, err) + require.Nil(t, gotUnspentCoinState) +} + +func TestPutGetUnspentCoinState(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + hash := testutil.RandomUint256() + unspentCoinState := &UnspentCoinState{states:[]entities.CoinState{}} + err := dao.PutUnspentCoinState(hash, unspentCoinState) + require.NoError(t, err) + gotUnspentCoinState, err := dao.GetUnspentCoinState(hash) + require.NoError(t, err) + require.Equal(t, unspentCoinState, gotUnspentCoinState) +} + +func TestGetSpentCoinStateOrNew_New(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + hash := testutil.RandomUint256() + spentCoinState, err := dao.GetSpentCoinsOrNew(hash) + require.NoError(t, err) + require.NotNil(t, spentCoinState) + gotSpentCoinState, err := dao.GetSpentCoinState(hash) + require.NoError(t, err) + require.Equal(t, spentCoinState, gotSpentCoinState) +} + +func TestPutAndGetSpentCoinState(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + hash := testutil.RandomUint256() + spentCoinState := &SpentCoinState{items:make(map[uint16]uint32)} + err := dao.PutSpentCoinState(hash, spentCoinState) + require.NoError(t, err) + gotSpentCoinState, err := dao.GetSpentCoinState(hash) + require.NoError(t, err) + require.Equal(t, spentCoinState, gotSpentCoinState) +} + +func TestGetSpentCoinState_Err(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + hash := testutil.RandomUint256() + spentCoinState, err := dao.GetSpentCoinState(hash) + require.Error(t, err) + require.Nil(t, spentCoinState) +} + +func TestDeleteSpentCoinState(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + hash := testutil.RandomUint256() + spentCoinState := &SpentCoinState{items:make(map[uint16]uint32)} + err := dao.PutSpentCoinState(hash, spentCoinState) + require.NoError(t, err) + err = dao.DeleteSpentCoinState(hash) + require.NoError(t, err) + gotSpentCoinState, err := dao.GetSpentCoinState(hash) + require.Error(t, err) + require.Nil(t, gotSpentCoinState) +} + +func TestGetValidatorStateOrNew_New(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + publicKey := &keys.PublicKey{} + validatorState, err := dao.GetValidatorStateOrNew(publicKey) + require.NoError(t, err) + require.NotNil(t, validatorState) + gotValidatorState, err := dao.GetValidatorState(publicKey) + require.NoError(t, err) + require.Equal(t, validatorState, gotValidatorState) +} + +func TestPutGetValidatorState(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + publicKey := &keys.PublicKey{} + validatorState := &entities.ValidatorState{ + PublicKey: publicKey, + Registered: false, + Votes: 0, + } + err := dao.PutValidatorState(validatorState) + require.NoError(t, err) + gotValidatorState, err := dao.GetValidatorState(publicKey) + require.NoError(t, err) + require.Equal(t, validatorState, gotValidatorState) +} + +func TestDeleteValidatorState(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + publicKey := &keys.PublicKey{} + validatorState := &entities.ValidatorState{ + PublicKey: publicKey, + Registered: false, + Votes: 0, + } + err := dao.PutValidatorState(validatorState) + require.NoError(t, err) + err = dao.DeleteValidatorState(validatorState) + require.NoError(t, err) + gotValidatorState, err := dao.GetValidatorState(publicKey) + require.Error(t, err) + require.Nil(t, gotValidatorState) +} + +func TestGetValidators(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + publicKey := &keys.PublicKey{} + validatorState := &entities.ValidatorState{ + PublicKey: publicKey, + Registered: false, + Votes: 0, + } + err := dao.PutValidatorState(validatorState) + require.NoError(t, err) + validators := dao.GetValidators() + require.Equal(t, validatorState, validators[0]) + require.Len(t, validators, 1) +} + +func TestPutGetAppExecResult(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + hash := testutil.RandomUint256() + appExecResult := &entities.AppExecResult{TxHash: hash, Events:[]entities.NotificationEvent{}} + err := dao.PutAppExecResult(appExecResult) + require.NoError(t, err) + gotAppExecResult, err := dao.GetAppExecResult(hash) + require.NoError(t, err) + require.Equal(t, appExecResult, gotAppExecResult) +} + +func TestPutGetStorageItem(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + hash := testutil.RandomUint160() + key := []byte{0} + storageItem := &entities.StorageItem{Value:[]uint8{}} + err := dao.PutStorageItem(hash, key, storageItem) + require.NoError(t, err) + gotStorageItem := dao.GetStorageItem(hash, key) + require.Equal(t, storageItem, gotStorageItem) +} + +func TestDeleteStorageItem(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + hash := testutil.RandomUint160() + key := []byte{0} + storageItem := &entities.StorageItem{Value:[]uint8{}} + err := dao.PutStorageItem(hash, key, storageItem) + require.NoError(t, err) + err = dao.DeleteStorageItem(hash, key) + require.NoError(t, err) + gotStorageItem := dao.GetStorageItem(hash, key) + require.Nil(t, gotStorageItem) +} + +func TestGetBlock_NotExists(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + hash := testutil.RandomUint256() + block, err := dao.GetBlock(hash) + require.Error(t, err) + require.Nil(t, block) +} + +func TestPutGetBlock(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + block := &Block{ + BlockBase: BlockBase{ + Script: transaction.Witness{ + VerificationScript: []byte{byte(opcode.PUSH1)}, + InvocationScript: []byte{byte(opcode.NOP)}, + }, + }, + } + hash := block.Hash() + err := dao.StoreAsBlock(block, 0) + require.NoError(t, err) + gotBlock, err := dao.GetBlock(hash) + require.NoError(t, err) + require.NotNil(t, gotBlock) +} + +func TestGetVersion_NoVersion(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + version, err := dao.GetVersion() + require.Error(t, err) + require.Equal(t, "", version) +} + +func TestGetVersion(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + err := dao.PutVersion("testVersion") + require.NoError(t, err) + version, err := dao.GetVersion() + require.NoError(t, err) + require.NotNil(t, version) +} + +func TestGetCurrentHeaderHeight_NoHeader(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + height, err := dao.GetCurrentBlockHeight() + require.Error(t, err) + require.Equal(t, uint32(0), height) +} + +func TestGetCurrentHeaderHeight_Store(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + block := &Block{ + BlockBase: BlockBase{ + Script: transaction.Witness{ + VerificationScript: []byte{byte(opcode.PUSH1)}, + InvocationScript: []byte{byte(opcode.NOP)}, + }, + }, + } + err := dao.StoreAsCurrentBlock(block) + require.NoError(t, err) + height, err := dao.GetCurrentBlockHeight() + require.NoError(t, err) + require.Equal(t, uint32(0), height) +} + +func TestStoreAsTransaction(t *testing.T) { + dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} + tx := &transaction.Transaction{} + hash := tx.Hash() + err := dao.StoreAsTransaction(tx, 0) + require.NoError(t, err) + hasTransaction := dao.HasTransaction(hash) + require.True(t, hasTransaction) +} From 2d42b14a1d65111a187b553d3d4393ad091e917f Mon Sep 17 00:00:00 2001 From: Vsevolod Brekelov Date: Thu, 28 Nov 2019 19:06:09 +0300 Subject: [PATCH 5/7] core: renames entities-> state and removed State prefix --- pkg/core/blockchain.go | 26 +++---- pkg/core/blockchainer.go | 12 +-- pkg/core/dao.go | 73 +++++++++---------- pkg/core/dao_test.go | 24 +++--- pkg/core/entities/coin_state.go | 12 --- pkg/core/interop_neo.go | 40 +++++----- pkg/core/interop_neo_test.go | 16 ++-- pkg/core/interop_system.go | 8 +- pkg/core/interops.go | 6 +- .../account_state.go => state/account.go} | 22 +++--- .../account_test.go} | 8 +- .../asset_state.go => state/asset.go} | 12 +-- .../asset_test.go} | 10 +-- pkg/core/state/coin.go | 12 +++ .../contract_state.go => state/contract.go} | 20 ++--- .../contract_test.go} | 10 +-- .../{entities => state}/notification_event.go | 2 +- .../notification_event_test.go | 2 +- pkg/core/{entities => state}/storage_item.go | 2 +- .../{entities => state}/storage_item_test.go | 2 +- .../validator_state.go => state/validator.go} | 26 +++---- .../validator_test.go} | 12 +-- pkg/core/unspent_coin_state.go | 16 ++-- pkg/core/unspent_coint_state_test.go | 14 ++-- pkg/network/helper_test.go | 12 +-- pkg/rpc/client.go | 4 +- pkg/rpc/neoScanBalanceGetter.go | 4 +- pkg/rpc/neoScanTypes.go | 4 +- pkg/rpc/wrappers/account_state.go | 8 +- pkg/rpc/wrappers/asset_state.go | 8 +- pkg/rpc/wrappers/unspents.go | 16 ++-- 31 files changed, 221 insertions(+), 222 deletions(-) delete mode 100644 pkg/core/entities/coin_state.go rename pkg/core/{entities/account_state.go => state/account.go} (81%) rename pkg/core/{entities/account_state_test.go => state/account_test.go} (93%) rename pkg/core/{entities/asset_state.go => state/asset.go} (85%) rename pkg/core/{entities/asset_state_test.go => state/asset_test.go} (85%) create mode 100644 pkg/core/state/coin.go rename pkg/core/{entities/contract_state.go => state/contract.go} (78%) rename pkg/core/{entities/contract_state_test.go => state/contract_test.go} (90%) rename pkg/core/{entities => state}/notification_event.go (98%) rename pkg/core/{entities => state}/notification_event_test.go (98%) rename pkg/core/{entities => state}/storage_item.go (96%) rename pkg/core/{entities => state}/storage_item_test.go (97%) rename pkg/core/{entities/validator_state.go => state/validator.go} (74%) rename pkg/core/{entities/validator_state_test.go => state/validator_test.go} (89%) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 9228a4c68..87ca8a2f1 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -11,7 +11,7 @@ import ( "time" "github.com/CityOfZion/neo-go/config" - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" @@ -378,7 +378,7 @@ func (bc *Blockchain) storeBlock(block *Block) error { if err != nil { return err } - unspent.states[input.PrevIndex] = entities.CoinStateSpent + unspent.states[input.PrevIndex] = state.CoinSpent if err = cache.PutUnspentCoinState(input.PrevHash, unspent); err != nil { return err } @@ -439,7 +439,7 @@ func (bc *Blockchain) storeBlock(block *Block) error { // Process the underlying type of the TX. switch t := tx.Data.(type) { case *transaction.RegisterTX: - err := cache.PutAssetState(&entities.AssetState{ + err := cache.PutAssetState(&state.Asset{ ID: tx.Hash(), AssetType: t.AssetType, Name: t.Name, @@ -499,7 +499,7 @@ func (bc *Blockchain) storeBlock(block *Block) error { if t.NeedStorage { properties |= smartcontract.HasStorage } - contract := &entities.ContractState{ + contract := &state.Contract{ Script: t.Script, ParamList: t.ParamList, ReturnType: t.ReturnType, @@ -555,7 +555,7 @@ func (bc *Blockchain) storeBlock(block *Block) error { "err": err, }).Warn("contract invocation failed") } - aer := &entities.AppExecResult{ + aer := &state.AppExecResult{ TxHash: tx.Hash(), Trigger: trigger.Application, VMState: v.State(), @@ -588,7 +588,7 @@ func processOutputs(tx *transaction.Transaction, dao *dao) error { if err != nil { return err } - account.Balances[output.AssetID] = append(account.Balances[output.AssetID], entities.UnspentBalance{ + account.Balances[output.AssetID] = append(account.Balances[output.AssetID], state.UnspentBalance{ Tx: tx.Hash(), Index: uint16(index), Value: output.Amount, @@ -739,12 +739,12 @@ func (bc *Blockchain) GetTransaction(hash util.Uint256) (*transaction.Transactio } // GetStorageItem returns an item from storage. -func (bc *Blockchain) GetStorageItem(scripthash util.Uint160, key []byte) *entities.StorageItem { +func (bc *Blockchain) GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem { return bc.dao.GetStorageItem(scripthash, key) } // GetStorageItems returns all storage items for a given scripthash. -func (bc *Blockchain) GetStorageItems(hash util.Uint160) (map[string]*entities.StorageItem, error) { +func (bc *Blockchain) GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) { return bc.dao.GetStorageItems(hash) } @@ -830,7 +830,7 @@ func (bc *Blockchain) HeaderHeight() uint32 { } // GetAssetState returns asset state from its assetID. -func (bc *Blockchain) GetAssetState(assetID util.Uint256) *entities.AssetState { +func (bc *Blockchain) GetAssetState(assetID util.Uint256) *state.Asset { asset, err := bc.dao.GetAssetState(assetID) if asset == nil && err != storage.ErrKeyNotFound { log.Warnf("failed to get asset state %s : %s", assetID, err) @@ -839,7 +839,7 @@ func (bc *Blockchain) GetAssetState(assetID util.Uint256) *entities.AssetState { } // GetContractState returns contract by its script hash. -func (bc *Blockchain) GetContractState(hash util.Uint160) *entities.ContractState { +func (bc *Blockchain) GetContractState(hash util.Uint160) *state.Contract { contract, err := bc.dao.GetContractState(hash) if contract == nil && err != storage.ErrKeyNotFound { log.Warnf("failed to get contract state: %s", err) @@ -848,7 +848,7 @@ func (bc *Blockchain) GetContractState(hash util.Uint160) *entities.ContractStat } // GetAccountState returns the account state from its script hash. -func (bc *Blockchain) GetAccountState(scriptHash util.Uint160) *entities.AccountState { +func (bc *Blockchain) GetAccountState(scriptHash util.Uint160) *state.Account { as, err := bc.dao.GetAccountState(scriptHash) if as == nil && err != storage.ErrKeyNotFound { log.Warnf("failed to get account state: %s", err) @@ -1166,7 +1166,7 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P if err != nil { return nil, err } - accountState.Balances[output.AssetID] = append(accountState.Balances[output.AssetID], entities.UnspentBalance{ + accountState.Balances[output.AssetID] = append(accountState.Balances[output.AssetID], state.UnspentBalance{ Tx: tx.Hash(), Index: uint16(index), Value: output.Amount, @@ -1250,7 +1250,7 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P validators := cache.GetValidators() - count := entities.GetValidatorsWeightedAverage(validators) + count := state.GetValidatorsWeightedAverage(validators) standByValidators, err := bc.GetStandByValidators() if err != nil { return nil, err diff --git a/pkg/core/blockchainer.go b/pkg/core/blockchainer.go index 9e87ff209..9d42d0af6 100644 --- a/pkg/core/blockchainer.go +++ b/pkg/core/blockchainer.go @@ -2,7 +2,7 @@ package core import ( "github.com/CityOfZion/neo-go/config" - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" @@ -20,19 +20,19 @@ type Blockchainer interface { Close() HeaderHeight() uint32 GetBlock(hash util.Uint256) (*Block, error) - GetContractState(hash util.Uint160) *entities.ContractState + GetContractState(hash util.Uint160) *state.Contract GetHeaderHash(int) util.Uint256 GetHeader(hash util.Uint256) (*Header, error) CurrentHeaderHash() util.Uint256 CurrentBlockHash() util.Uint256 HasBlock(util.Uint256) bool HasTransaction(util.Uint256) bool - GetAssetState(util.Uint256) *entities.AssetState - GetAccountState(util.Uint160) *entities.AccountState + GetAssetState(util.Uint256) *state.Asset + GetAccountState(util.Uint160) *state.Account GetValidators(txes... *transaction.Transaction) ([]*keys.PublicKey, error) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) - GetStorageItem(scripthash util.Uint160, key []byte) *entities.StorageItem - GetStorageItems(hash util.Uint160) (map[string]*entities.StorageItem, error) + GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem + GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) GetTestVM() (*vm.VM, storage.Store) GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error) GetUnspentCoinState(util.Uint256) *UnspentCoinState diff --git a/pkg/core/dao.go b/pkg/core/dao.go index bafe70b73..76a27ab3a 100644 --- a/pkg/core/dao.go +++ b/pkg/core/dao.go @@ -6,7 +6,7 @@ import ( "fmt" "sort" - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" @@ -42,15 +42,15 @@ func (dao *dao) Put(entity io.Serializable, key []byte) error { // -- start accounts. -// GetAccountStateOrNew retrieves AccountState from temporary or persistent Store +// GetAccountStateOrNew retrieves Account from temporary or persistent Store // or creates a new one if it doesn't exist and persists it. -func (dao *dao) GetAccountStateOrNew(hash util.Uint160) (*entities.AccountState, error) { +func (dao *dao) GetAccountStateOrNew(hash util.Uint160) (*state.Account, error) { account, err := dao.GetAccountState(hash) if err != nil { if err != storage.ErrKeyNotFound { return nil, err } - account = entities.NewAccountState(hash) + account = state.NewAccount(hash) if err = dao.PutAccountState(account); err != nil { return nil, err } @@ -58,10 +58,10 @@ func (dao *dao) GetAccountStateOrNew(hash util.Uint160) (*entities.AccountState, return account, nil } -// GetAccountState returns AccountState from the given Store if it's +// GetAccountState returns Account from the given Store if it's // present there. Returns nil otherwise. -func (dao *dao) GetAccountState(hash util.Uint160) (*entities.AccountState, error) { - account := &entities.AccountState{} +func (dao *dao) GetAccountState(hash util.Uint160) (*state.Account, error) { + account := &state.Account{} key := storage.AppendPrefix(storage.STAccount, hash.BytesBE()) err := dao.GetAndDecode(account, key) if err != nil { @@ -70,8 +70,7 @@ func (dao *dao) GetAccountState(hash util.Uint160) (*entities.AccountState, erro return account, err } -// PutAccountState puts given AccountState into the given store. -func (dao *dao) PutAccountState(as *entities.AccountState) error { +func (dao *dao) PutAccountState(as *state.Account) error { key := storage.AppendPrefix(storage.STAccount, as.ScriptHash.BytesBE()) return dao.Put(as, key) } @@ -81,8 +80,8 @@ func (dao *dao) PutAccountState(as *entities.AccountState) error { // -- start assets. // GetAssetState returns given asset state as recorded in the given store. -func (dao *dao) GetAssetState(assetID util.Uint256) (*entities.AssetState, error) { - asset := &entities.AssetState{} +func (dao *dao) GetAssetState(assetID util.Uint256) (*state.Asset, error) { + asset := &state.Asset{} key := storage.AppendPrefix(storage.STAsset, assetID.BytesBE()) err := dao.GetAndDecode(asset, key) if err != nil { @@ -95,7 +94,7 @@ func (dao *dao) GetAssetState(assetID util.Uint256) (*entities.AssetState, error } // PutAssetState puts given asset state into the given store. -func (dao *dao) PutAssetState(as *entities.AssetState) error { +func (dao *dao) PutAssetState(as *state.Asset) error { key := storage.AppendPrefix(storage.STAsset, as.ID.BytesBE()) return dao.Put(as, key) } @@ -106,8 +105,8 @@ func (dao *dao) PutAssetState(as *entities.AssetState) error { // GetContractState returns contract state as recorded in the given // store by the given script hash. -func (dao *dao) GetContractState(hash util.Uint160) (*entities.ContractState, error) { - contract := &entities.ContractState{} +func (dao *dao) GetContractState(hash util.Uint160) (*state.Contract, error) { + contract := &state.Contract{} key := storage.AppendPrefix(storage.STContract, hash.BytesBE()) err := dao.GetAndDecode(contract, key) if err != nil { @@ -121,7 +120,7 @@ func (dao *dao) GetContractState(hash util.Uint160) (*entities.ContractState, er } // PutContractState puts given contract state into the given store. -func (dao *dao) PutContractState(cs *entities.ContractState) error { +func (dao *dao) PutContractState(cs *state.Contract) error { key := storage.AppendPrefix(storage.STContract, cs.ScriptHash().BytesBE()) return dao.Put(cs, key) } @@ -146,7 +145,7 @@ func (dao *dao) GetUnspentCoinStateOrNew(hash util.Uint256) (*UnspentCoinState, return nil, err } unspent = &UnspentCoinState{ - states: []entities.CoinState{}, + states: []state.Coin{}, } if err = dao.PutUnspentCoinState(hash, unspent); err != nil { return nil, err @@ -221,13 +220,13 @@ func (dao *dao) DeleteSpentCoinState(hash util.Uint256) error { // -- start validator. // GetValidatorStateOrNew gets validator from store or created new one in case of error. -func (dao *dao) GetValidatorStateOrNew(publicKey *keys.PublicKey) (*entities.ValidatorState, error) { +func (dao *dao) GetValidatorStateOrNew(publicKey *keys.PublicKey) (*state.Validator, error) { validatorState, err := dao.GetValidatorState(publicKey) if err != nil { if err != storage.ErrKeyNotFound { return nil, err } - validatorState = &entities.ValidatorState{PublicKey: publicKey} + validatorState = &state.Validator{PublicKey: publicKey} if err = dao.PutValidatorState(validatorState); err != nil { return nil, err } @@ -237,11 +236,11 @@ func (dao *dao) GetValidatorStateOrNew(publicKey *keys.PublicKey) (*entities.Val } // GetValidators returns all validators from store. -func (dao *dao) GetValidators() []*entities.ValidatorState { - var validators []*entities.ValidatorState +func (dao *dao) GetValidators() []*state.Validator { + var validators []*state.Validator dao.store.Seek(storage.STValidator.Bytes(), func(k, v []byte) { r := io.NewBinReaderFromBuf(v) - validator := &entities.ValidatorState{} + validator := &state.Validator{} validator.DecodeBinary(r) if r.Err != nil { return @@ -252,8 +251,8 @@ func (dao *dao) GetValidators() []*entities.ValidatorState { } // GetValidatorState returns validator by publicKey. -func (dao *dao) GetValidatorState(publicKey *keys.PublicKey) (*entities.ValidatorState, error) { - validatorState := &entities.ValidatorState{} +func (dao *dao) GetValidatorState(publicKey *keys.PublicKey) (*state.Validator, error) { + validatorState := &state.Validator{} key := storage.AppendPrefix(storage.STValidator, publicKey.Bytes()) err := dao.GetAndDecode(validatorState, key) if err != nil { @@ -262,14 +261,14 @@ func (dao *dao) GetValidatorState(publicKey *keys.PublicKey) (*entities.Validato return validatorState, nil } -// PutValidatorState puts given ValidatorState into the given store. -func (dao *dao) PutValidatorState(vs *entities.ValidatorState) error { +// PutValidatorState puts given Validator into the given store. +func (dao *dao) PutValidatorState(vs *state.Validator) error { key := storage.AppendPrefix(storage.STValidator, vs.PublicKey.Bytes()) return dao.Put(vs, key) } -// DeleteValidatorState deletes given ValidatorState into the given store. -func (dao *dao) DeleteValidatorState(vs *entities.ValidatorState) error { +// DeleteValidatorState deletes given Validator into the given store. +func (dao *dao) DeleteValidatorState(vs *state.Validator) error { key := storage.AppendPrefix(storage.STValidator, vs.PublicKey.Bytes()) return dao.store.Delete(key) } @@ -280,8 +279,8 @@ func (dao *dao) DeleteValidatorState(vs *entities.ValidatorState) error { // GetAppExecResult gets application execution result from the // given store. -func (dao *dao) GetAppExecResult(hash util.Uint256) (*entities.AppExecResult, error) { - aer := &entities.AppExecResult{} +func (dao *dao) GetAppExecResult(hash util.Uint256) (*state.AppExecResult, error) { + aer := &state.AppExecResult{} key := storage.AppendPrefix(storage.STNotification, hash.BytesBE()) err := dao.GetAndDecode(aer, key) if err != nil { @@ -292,7 +291,7 @@ func (dao *dao) GetAppExecResult(hash util.Uint256) (*entities.AppExecResult, er // PutAppExecResult puts given application execution result into the // given store. -func (dao *dao) PutAppExecResult(aer *entities.AppExecResult) error { +func (dao *dao) PutAppExecResult(aer *state.AppExecResult) error { key := storage.AppendPrefix(storage.STNotification, aer.TxHash.BytesBE()) return dao.Put(aer, key) } @@ -302,14 +301,14 @@ func (dao *dao) PutAppExecResult(aer *entities.AppExecResult) error { // -- start storage item. // GetStorageItem returns StorageItem if it exists in the given Store. -func (dao *dao) GetStorageItem(scripthash util.Uint160, key []byte) *entities.StorageItem { +func (dao *dao) GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem { b, err := dao.store.Get(makeStorageItemKey(scripthash, key)) if err != nil { return nil } r := io.NewBinReaderFromBuf(b) - si := &entities.StorageItem{} + si := &state.StorageItem{} si.DecodeBinary(r) if r.Err != nil { return nil @@ -320,7 +319,7 @@ func (dao *dao) GetStorageItem(scripthash util.Uint160, key []byte) *entities.St // PutStorageItem puts given StorageItem for given script with given // key into the given Store. -func (dao *dao) PutStorageItem(scripthash util.Uint160, key []byte, si *entities.StorageItem) error { +func (dao *dao) PutStorageItem(scripthash util.Uint160, key []byte, si *state.StorageItem) error { return dao.Put(si, makeStorageItemKey(scripthash, key)) } @@ -331,8 +330,8 @@ func (dao *dao) DeleteStorageItem(scripthash util.Uint160, key []byte) error { } // GetStorageItems returns all storage items for a given scripthash. -func (dao *dao) GetStorageItems(hash util.Uint160) (map[string]*entities.StorageItem, error) { - var siMap = make(map[string]*entities.StorageItem) +func (dao *dao) GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) { + var siMap = make(map[string]*state.StorageItem) var err error saveToMap := func(k, v []byte) { @@ -340,7 +339,7 @@ func (dao *dao) GetStorageItems(hash util.Uint160) (map[string]*entities.Storage return } r := io.NewBinReaderFromBuf(v) - si := &entities.StorageItem{} + si := &state.StorageItem{} si.DecodeBinary(r) if r.Err != nil { err = r.Err @@ -546,7 +545,7 @@ func (dao *dao) IsDoubleSpend(tx *transaction.Transaction) bool { return false } for _, input := range inputs { - if int(input.PrevIndex) >= len(unspent.states) || unspent.states[input.PrevIndex] == entities.CoinStateSpent { + if int(input.PrevIndex) >= len(unspent.states) || unspent.states[input.PrevIndex] == state.CoinSpent { return true } } diff --git a/pkg/core/dao_test.go b/pkg/core/dao_test.go index 1bc2dbfff..7c014cfe7 100644 --- a/pkg/core/dao_test.go +++ b/pkg/core/dao_test.go @@ -3,7 +3,7 @@ package core import ( "testing" - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/testutil" "github.com/CityOfZion/neo-go/pkg/core/transaction" @@ -53,7 +53,7 @@ func TestGetAccountStateOrNew_New(t *testing.T) { func TestPutAndGetAccountStateOrNew(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} hash := testutil.RandomUint160() - accountState := &entities.AccountState{ScriptHash: hash} + accountState := &state.Account{ScriptHash: hash} err := dao.PutAccountState(accountState) require.NoError(t, err) gotAccount, err := dao.GetAccountStateOrNew(hash) @@ -64,7 +64,7 @@ func TestPutAndGetAccountStateOrNew(t *testing.T) { func TestPutAndGetAssetState(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} id := testutil.RandomUint256() - assetState := &entities.AssetState{ID: id, Owner: keys.PublicKey{}} + assetState := &state.Asset{ID: id, Owner: keys.PublicKey{}} err := dao.PutAssetState(assetState) require.NoError(t, err) gotAssetState, err := dao.GetAssetState(id) @@ -74,7 +74,7 @@ func TestPutAndGetAssetState(t *testing.T) { func TestPutAndGetContractState(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - contractState := &entities.ContractState{Script: []byte{}, ParamList:[]smartcontract.ParamType{}} + contractState := &state.Contract{Script: []byte{}, ParamList:[]smartcontract.ParamType{}} hash := contractState.ScriptHash() err := dao.PutContractState(contractState) require.NoError(t, err) @@ -85,7 +85,7 @@ func TestPutAndGetContractState(t *testing.T) { func TestDeleteContractState(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - contractState := &entities.ContractState{Script: []byte{}, ParamList:[]smartcontract.ParamType{}} + contractState := &state.Contract{Script: []byte{}, ParamList:[]smartcontract.ParamType{}} hash := contractState.ScriptHash() err := dao.PutContractState(contractState) require.NoError(t, err) @@ -118,7 +118,7 @@ func TestGetUnspentCoinState_Err(t *testing.T) { func TestPutGetUnspentCoinState(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} hash := testutil.RandomUint256() - unspentCoinState := &UnspentCoinState{states:[]entities.CoinState{}} + unspentCoinState := &UnspentCoinState{states:[]state.Coin{}} err := dao.PutUnspentCoinState(hash, unspentCoinState) require.NoError(t, err) gotUnspentCoinState, err := dao.GetUnspentCoinState(hash) @@ -183,7 +183,7 @@ func TestGetValidatorStateOrNew_New(t *testing.T) { func TestPutGetValidatorState(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} publicKey := &keys.PublicKey{} - validatorState := &entities.ValidatorState{ + validatorState := &state.Validator{ PublicKey: publicKey, Registered: false, Votes: 0, @@ -198,7 +198,7 @@ func TestPutGetValidatorState(t *testing.T) { func TestDeleteValidatorState(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} publicKey := &keys.PublicKey{} - validatorState := &entities.ValidatorState{ + validatorState := &state.Validator{ PublicKey: publicKey, Registered: false, Votes: 0, @@ -215,7 +215,7 @@ func TestDeleteValidatorState(t *testing.T) { func TestGetValidators(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} publicKey := &keys.PublicKey{} - validatorState := &entities.ValidatorState{ + validatorState := &state.Validator{ PublicKey: publicKey, Registered: false, Votes: 0, @@ -230,7 +230,7 @@ func TestGetValidators(t *testing.T) { func TestPutGetAppExecResult(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} hash := testutil.RandomUint256() - appExecResult := &entities.AppExecResult{TxHash: hash, Events:[]entities.NotificationEvent{}} + appExecResult := &state.AppExecResult{TxHash: hash, Events:[]state.NotificationEvent{}} err := dao.PutAppExecResult(appExecResult) require.NoError(t, err) gotAppExecResult, err := dao.GetAppExecResult(hash) @@ -242,7 +242,7 @@ func TestPutGetStorageItem(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} hash := testutil.RandomUint160() key := []byte{0} - storageItem := &entities.StorageItem{Value:[]uint8{}} + storageItem := &state.StorageItem{Value: []uint8{}} err := dao.PutStorageItem(hash, key, storageItem) require.NoError(t, err) gotStorageItem := dao.GetStorageItem(hash, key) @@ -253,7 +253,7 @@ func TestDeleteStorageItem(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} hash := testutil.RandomUint160() key := []byte{0} - storageItem := &entities.StorageItem{Value:[]uint8{}} + storageItem := &state.StorageItem{Value: []uint8{}} err := dao.PutStorageItem(hash, key, storageItem) require.NoError(t, err) err = dao.DeleteStorageItem(hash, key) diff --git a/pkg/core/entities/coin_state.go b/pkg/core/entities/coin_state.go deleted file mode 100644 index cefc25de7..000000000 --- a/pkg/core/entities/coin_state.go +++ /dev/null @@ -1,12 +0,0 @@ -package entities - -// CoinState represents the state of a coin. -type CoinState uint8 - -// Viable CoinState constants. -const ( - CoinStateConfirmed CoinState = 0 - CoinStateSpent CoinState = 1 << 1 - CoinStateClaimed CoinState = 1 << 2 - CoinStateFrozen CoinState = 1 << 5 -) diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index 632181384..31576d95a 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -5,7 +5,7 @@ import ( "fmt" "math" - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/smartcontract" @@ -317,7 +317,7 @@ func (ic *interopContext) bcGetAccount(v *vm.VM) error { } acc := ic.bc.GetAccountState(acchash) if acc == nil { - acc = entities.NewAccountState(acchash) + acc = state.NewAccount(acchash) } v.Estack().PushVal(vm.NewInteropItem(acc)) return nil @@ -341,7 +341,7 @@ func (ic *interopContext) bcGetAsset(v *vm.VM) error { // accountGetBalance returns balance for a given account. func (ic *interopContext) accountGetBalance(v *vm.VM) error { accInterface := v.Estack().Pop().Value() - acc, ok := accInterface.(*entities.AccountState) + acc, ok := accInterface.(*state.Account) if !ok { return fmt.Errorf("%T is not an account state", acc) } @@ -361,7 +361,7 @@ func (ic *interopContext) accountGetBalance(v *vm.VM) error { // accountGetScriptHash returns script hash of a given account. func (ic *interopContext) accountGetScriptHash(v *vm.VM) error { accInterface := v.Estack().Pop().Value() - acc, ok := accInterface.(*entities.AccountState) + acc, ok := accInterface.(*state.Account) if !ok { return fmt.Errorf("%T is not an account state", acc) } @@ -372,7 +372,7 @@ func (ic *interopContext) accountGetScriptHash(v *vm.VM) error { // accountGetVotes returns votes of a given account. func (ic *interopContext) accountGetVotes(v *vm.VM) error { accInterface := v.Estack().Pop().Value() - acc, ok := accInterface.(*entities.AccountState) + acc, ok := accInterface.(*state.Account) if !ok { return fmt.Errorf("%T is not an account state", acc) } @@ -428,9 +428,9 @@ func (ic *interopContext) storageFind(v *vm.VM) error { } */ // createContractStateFromVM pops all contract state elements from the VM -// evaluation stack, does a lot of checks and returns ContractState if it +// evaluation stack, does a lot of checks and returns Contract if it // succeeds. -func (ic *interopContext) createContractStateFromVM(v *vm.VM) (*entities.ContractState, error) { +func (ic *interopContext) createContractStateFromVM(v *vm.VM) (*state.Contract, error) { if ic.trigger != trigger.Application { return nil, errors.New("can't create contract when not triggered by an application") } @@ -468,7 +468,7 @@ func (ic *interopContext) createContractStateFromVM(v *vm.VM) (*entities.Contrac if len(desc) > MaxContractStringLen { return nil, errors.New("too big description") } - contract := &entities.ContractState{ + contract := &state.Contract{ Script: script, ParamList: paramList, ReturnType: retType, @@ -503,7 +503,7 @@ func (ic *interopContext) contractCreate(v *vm.VM) error { // contractGetScript returns a script associated with a contract. func (ic *interopContext) contractGetScript(v *vm.VM) error { csInterface := v.Estack().Pop().Value() - cs, ok := csInterface.(*entities.ContractState) + cs, ok := csInterface.(*state.Contract) if !ok { return fmt.Errorf("%T is not a contract state", cs) } @@ -514,7 +514,7 @@ func (ic *interopContext) contractGetScript(v *vm.VM) error { // contractIsPayable returns whether contract is payable. func (ic *interopContext) contractIsPayable(v *vm.VM) error { csInterface := v.Estack().Pop().Value() - cs, ok := csInterface.(*entities.ContractState) + cs, ok := csInterface.(*state.Contract) if !ok { return fmt.Errorf("%T is not a contract state", cs) } @@ -610,7 +610,7 @@ func (ic *interopContext) assetCreate(v *vm.VM) error { if err != nil { return gherr.Wrap(err, "failed to get issuer") } - asset := &entities.AssetState{ + asset := &state.Asset{ ID: ic.tx.Hash(), AssetType: atype, Name: name, @@ -632,7 +632,7 @@ func (ic *interopContext) assetCreate(v *vm.VM) error { // assetGetAdmin returns asset admin. func (ic *interopContext) assetGetAdmin(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*entities.AssetState) + as, ok := asInterface.(*state.Asset) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -643,7 +643,7 @@ func (ic *interopContext) assetGetAdmin(v *vm.VM) error { // assetGetAmount returns the overall amount of asset available. func (ic *interopContext) assetGetAmount(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*entities.AssetState) + as, ok := asInterface.(*state.Asset) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -654,7 +654,7 @@ func (ic *interopContext) assetGetAmount(v *vm.VM) error { // assetGetAssetId returns the id of an asset. func (ic *interopContext) assetGetAssetID(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*entities.AssetState) + as, ok := asInterface.(*state.Asset) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -665,7 +665,7 @@ func (ic *interopContext) assetGetAssetID(v *vm.VM) error { // assetGetAssetType returns type of an asset. func (ic *interopContext) assetGetAssetType(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*entities.AssetState) + as, ok := asInterface.(*state.Asset) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -676,7 +676,7 @@ func (ic *interopContext) assetGetAssetType(v *vm.VM) error { // assetGetAvailable returns available (not yet issued) amount of asset. func (ic *interopContext) assetGetAvailable(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*entities.AssetState) + as, ok := asInterface.(*state.Asset) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -687,7 +687,7 @@ func (ic *interopContext) assetGetAvailable(v *vm.VM) error { // assetGetIssuer returns issuer of an asset. func (ic *interopContext) assetGetIssuer(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*entities.AssetState) + as, ok := asInterface.(*state.Asset) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -698,7 +698,7 @@ func (ic *interopContext) assetGetIssuer(v *vm.VM) error { // assetGetOwner returns owner of an asset. func (ic *interopContext) assetGetOwner(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*entities.AssetState) + as, ok := asInterface.(*state.Asset) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -709,7 +709,7 @@ func (ic *interopContext) assetGetOwner(v *vm.VM) error { // assetGetPrecision returns precision used to measure this asset. func (ic *interopContext) assetGetPrecision(v *vm.VM) error { asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*entities.AssetState) + as, ok := asInterface.(*state.Asset) if !ok { return fmt.Errorf("%T is not an asset state", as) } @@ -723,7 +723,7 @@ func (ic *interopContext) assetRenew(v *vm.VM) error { return errors.New("can't create asset when not triggered by an application") } asInterface := v.Estack().Pop().Value() - as, ok := asInterface.(*entities.AssetState) + as, ok := asInterface.(*state.Asset) if !ok { return fmt.Errorf("%T is not an asset state", as) } diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index a20d38064..9cf07369e 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -4,7 +4,7 @@ import ( "math/big" "testing" - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/testutil" "github.com/CityOfZion/neo-go/pkg/core/transaction" @@ -323,7 +323,7 @@ func TestAssetGetPrecision(t *testing.T) { require.Equal(t, big.NewInt(int64(assetState.Precision)), precision) } -// Helper functions to create VM, InteropContext, TX, AccountState, ContractState, AssetState. +// Helper functions to create VM, InteropContext, TX, Account, Contract, Asset. func createVMAndPushBlock(t *testing.T) (*vm.VM, *Block, *interopContext) { v := vm.New() @@ -339,9 +339,9 @@ func createVMAndPushTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interop return v, tx, context } -func createVMAndAssetState(t *testing.T) (*vm.VM, *entities.AssetState, *interopContext) { +func createVMAndAssetState(t *testing.T) (*vm.VM, *state.Asset, *interopContext) { v := vm.New() - assetState := &entities.AssetState{ + assetState := &state.Asset{ ID: util.Uint256{}, AssetType: transaction.GoverningToken, Name: "TestAsset", @@ -361,9 +361,9 @@ func createVMAndAssetState(t *testing.T) (*vm.VM, *entities.AssetState, *interop return v, assetState, context } -func createVMAndContractState(t *testing.T) (*vm.VM, *entities.ContractState, *interopContext) { +func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interopContext) { v := vm.New() - contractState := &entities.ContractState{ + contractState := &state.Contract{ Script: []byte("testscript"), ParamList: []smartcontract.ParamType{smartcontract.StringType, smartcontract.IntegerType, smartcontract.Hash160Type}, ReturnType: smartcontract.ArrayType, @@ -379,11 +379,11 @@ func createVMAndContractState(t *testing.T) (*vm.VM, *entities.ContractState, *i return v, contractState, context } -func createVMAndAccState(t *testing.T) (*vm.VM, *entities.AccountState, *interopContext) { +func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interopContext) { v := vm.New() rawHash := "4d3b96ae1bcc5a585e075e3b81920210dec16302" hash, err := util.Uint160DecodeStringBE(rawHash) - accountState := entities.NewAccountState(hash) + accountState := state.NewAccount(hash) key := &keys.PublicKey{X: big.NewInt(1), Y: big.NewInt(1)} accountState.Votes = []*keys.PublicKey{key} diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index ce104bb09..a399e67e7 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -5,7 +5,7 @@ import ( "fmt" "math" - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/hash" "github.com/CityOfZion/neo-go/pkg/crypto/keys" @@ -342,7 +342,7 @@ func (ic *interopContext) runtimeCheckWitness(v *vm.VM) error { func (ic *interopContext) runtimeNotify(v *vm.VM) error { // It can be just about anything. e := v.Estack().Pop() - ne := entities.NotificationEvent{ScriptHash:getContextScriptHash(v, 0), Item:e.Item()} + ne := state.NotificationEvent{ScriptHash: getContextScriptHash(v, 0), Item: e.Item()} ic.notifications = append(ic.notifications, ne) return nil } @@ -475,7 +475,7 @@ func (ic *interopContext) putWithContextAndFlags(stc *StorageContext, key []byte } si := ic.dao.GetStorageItem(stc.ScriptHash, key) if si == nil { - si = &entities.StorageItem{} + si = &state.StorageItem{} } if si.IsConst { return errors.New("storage item exists and is read-only") @@ -558,7 +558,7 @@ func (ic *interopContext) contractDestroy(v *vm.VM) error { // contractGetStorageContext retrieves StorageContext of a contract. func (ic *interopContext) contractGetStorageContext(v *vm.VM) error { csInterface := v.Estack().Pop().Value() - cs, ok := csInterface.(*entities.ContractState) + cs, ok := csInterface.(*state.Contract) if !ok { return fmt.Errorf("%T is not a contract state", cs) } diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 53691f5bf..33fac36c6 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -8,7 +8,7 @@ package core */ import ( - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/vm" @@ -20,12 +20,12 @@ type interopContext struct { block *Block tx *transaction.Transaction dao *dao - notifications []entities.NotificationEvent + notifications []state.NotificationEvent } func newInteropContext(trigger byte, bc Blockchainer, s storage.Store, block *Block, tx *transaction.Transaction) *interopContext { dao := &dao{store: storage.NewMemCachedStore(s)} - nes := make([]entities.NotificationEvent, 0) + nes := make([]state.NotificationEvent, 0) return &interopContext{bc, trigger, block, tx, dao, nes} } diff --git a/pkg/core/entities/account_state.go b/pkg/core/state/account.go similarity index 81% rename from pkg/core/entities/account_state.go rename to pkg/core/state/account.go index f06b94ae3..e72fdc875 100644 --- a/pkg/core/entities/account_state.go +++ b/pkg/core/state/account.go @@ -1,4 +1,4 @@ -package entities +package state import ( "github.com/CityOfZion/neo-go/pkg/crypto/keys" @@ -17,8 +17,8 @@ type UnspentBalance struct { // UnspentBalances is a slice of UnspentBalance (mostly needed to sort them). type UnspentBalances []UnspentBalance -// AccountState represents the state of a NEO account. -type AccountState struct { +// Account represents the state of a NEO account. +type Account struct { Version uint8 ScriptHash util.Uint160 IsFrozen bool @@ -26,9 +26,9 @@ type AccountState struct { Balances map[util.Uint256][]UnspentBalance } -// NewAccountState returns a new AccountState object. -func NewAccountState(scriptHash util.Uint160) *AccountState { - return &AccountState{ +// NewAccount returns a new Account object. +func NewAccount(scriptHash util.Uint160) *Account { + return &Account{ Version: 0, ScriptHash: scriptHash, IsFrozen: false, @@ -37,8 +37,8 @@ func NewAccountState(scriptHash util.Uint160) *AccountState { } } -// DecodeBinary decodes AccountState from the given BinReader. -func (s *AccountState) DecodeBinary(br *io.BinReader) { +// DecodeBinary decodes Account from the given BinReader. +func (s *Account) DecodeBinary(br *io.BinReader) { br.ReadLE(&s.Version) br.ReadBytes(s.ScriptHash[:]) br.ReadLE(&s.IsFrozen) @@ -55,8 +55,8 @@ func (s *AccountState) DecodeBinary(br *io.BinReader) { } } -// EncodeBinary encodes AccountState to the given BinWriter. -func (s *AccountState) EncodeBinary(bw *io.BinWriter) { +// EncodeBinary encodes Account to the given BinWriter. +func (s *Account) EncodeBinary(bw *io.BinWriter) { bw.WriteLE(s.Version) bw.WriteBytes(s.ScriptHash[:]) bw.WriteLE(s.IsFrozen) @@ -85,7 +85,7 @@ func (u *UnspentBalance) EncodeBinary(w *io.BinWriter) { // GetBalanceValues sums all unspent outputs and returns a map of asset IDs to // overall balances. -func (s *AccountState) GetBalanceValues() map[util.Uint256]util.Fixed8 { +func (s *Account) GetBalanceValues() map[util.Uint256]util.Fixed8 { res := make(map[util.Uint256]util.Fixed8) for k, v := range s.Balances { balance := util.Fixed8(0) diff --git a/pkg/core/entities/account_state_test.go b/pkg/core/state/account_test.go similarity index 93% rename from pkg/core/entities/account_state_test.go rename to pkg/core/state/account_test.go index 89d8d3026..e59215f3b 100644 --- a/pkg/core/entities/account_state_test.go +++ b/pkg/core/state/account_test.go @@ -1,4 +1,4 @@ -package entities +package state import ( "testing" @@ -30,7 +30,7 @@ func TestDecodeEncodeAccountState(t *testing.T) { votes[i] = k.PublicKey() } - a := &AccountState{ + a := &Account{ Version: 0, ScriptHash: testutil.RandomUint160(), IsFrozen: true, @@ -42,7 +42,7 @@ func TestDecodeEncodeAccountState(t *testing.T) { a.EncodeBinary(buf.BinWriter) assert.Nil(t, buf.Err) - aDecode := &AccountState{} + aDecode := &Account{} r := io.NewBinReaderFromBuf(buf.Bytes()) aDecode.DecodeBinary(r) assert.Nil(t, r.Err) @@ -60,7 +60,7 @@ func TestDecodeEncodeAccountState(t *testing.T) { func TestAccountStateBalanceValues(t *testing.T) { asset1 := testutil.RandomUint256() asset2 := testutil.RandomUint256() - as := AccountState{Balances: make(map[util.Uint256][]UnspentBalance)} + as := Account{Balances: make(map[util.Uint256][]UnspentBalance)} ref := 0 for i := 0; i < 10; i++ { ref += i diff --git a/pkg/core/entities/asset_state.go b/pkg/core/state/asset.go similarity index 85% rename from pkg/core/entities/asset_state.go rename to pkg/core/state/asset.go index c81d3b5e3..b5268ce31 100644 --- a/pkg/core/entities/asset_state.go +++ b/pkg/core/state/asset.go @@ -1,4 +1,4 @@ -package entities +package state import ( "github.com/CityOfZion/neo-go/pkg/core/transaction" @@ -9,8 +9,8 @@ import ( const feeMode = 0x0 -// AssetState represents the state of an NEO registered Asset. -type AssetState struct { +// Asset represents the state of an NEO registered Asset. +type Asset struct { ID util.Uint256 AssetType transaction.AssetType Name string @@ -27,7 +27,7 @@ type AssetState struct { } // DecodeBinary implements Serializable interface. -func (a *AssetState) DecodeBinary(br *io.BinReader) { +func (a *Asset) DecodeBinary(br *io.BinReader) { br.ReadBytes(a.ID[:]) br.ReadLE(&a.AssetType) @@ -47,7 +47,7 @@ func (a *AssetState) DecodeBinary(br *io.BinReader) { } // EncodeBinary implements Serializable interface. -func (a *AssetState) EncodeBinary(bw *io.BinWriter) { +func (a *Asset) EncodeBinary(bw *io.BinWriter) { bw.WriteBytes(a.ID[:]) bw.WriteLE(a.AssetType) bw.WriteString(a.Name) @@ -66,7 +66,7 @@ func (a *AssetState) EncodeBinary(bw *io.BinWriter) { } // GetName returns the asset name based on its type. -func (a *AssetState) GetName() string { +func (a *Asset) GetName() string { if a.AssetType == transaction.GoverningToken { return "NEO" diff --git a/pkg/core/entities/asset_state_test.go b/pkg/core/state/asset_test.go similarity index 85% rename from pkg/core/entities/asset_state_test.go rename to pkg/core/state/asset_test.go index 58e4896ce..790ba4f68 100644 --- a/pkg/core/entities/asset_state_test.go +++ b/pkg/core/state/asset_test.go @@ -1,4 +1,4 @@ -package entities +package state import ( "testing" @@ -12,7 +12,7 @@ import ( ) func TestEncodeDecodeAssetState(t *testing.T) { - asset := &AssetState{ + asset := &Asset{ ID: testutil.RandomUint256(), AssetType: transaction.Token, Name: "super cool token", @@ -30,7 +30,7 @@ func TestEncodeDecodeAssetState(t *testing.T) { buf := io.NewBufBinWriter() asset.EncodeBinary(buf.BinWriter) assert.Nil(t, buf.Err) - assetDecode := &AssetState{} + assetDecode := &Asset{} r := io.NewBinReaderFromBuf(buf.Bytes()) assetDecode.DecodeBinary(r) assert.Nil(t, r.Err) @@ -38,11 +38,11 @@ func TestEncodeDecodeAssetState(t *testing.T) { } func TestAssetState_GetName_NEO(t *testing.T) { - asset := &AssetState{AssetType: transaction.GoverningToken} + asset := &Asset{AssetType: transaction.GoverningToken} assert.Equal(t, "NEO", asset.GetName()) } func TestAssetState_GetName_NEOGas(t *testing.T) { - asset := &AssetState{AssetType: transaction.UtilityToken} + asset := &Asset{AssetType: transaction.UtilityToken} assert.Equal(t, "NEOGas", asset.GetName()) } diff --git a/pkg/core/state/coin.go b/pkg/core/state/coin.go new file mode 100644 index 000000000..650d3f7fa --- /dev/null +++ b/pkg/core/state/coin.go @@ -0,0 +1,12 @@ +package state + +// Coin represents the state of a coin. +type Coin uint8 + +// Viable Coin constants. +const ( + CoinConfirmed Coin = 0 + CoinSpent Coin = 1 << 1 + CoinClaimed Coin = 1 << 2 + CoinFrozen Coin = 1 << 5 +) diff --git a/pkg/core/entities/contract_state.go b/pkg/core/state/contract.go similarity index 78% rename from pkg/core/entities/contract_state.go rename to pkg/core/state/contract.go index 812d7766d..443bf918c 100644 --- a/pkg/core/entities/contract_state.go +++ b/pkg/core/state/contract.go @@ -1,4 +1,4 @@ -package entities +package state import ( "github.com/CityOfZion/neo-go/pkg/crypto/hash" @@ -7,8 +7,8 @@ import ( "github.com/CityOfZion/neo-go/pkg/util" ) -// ContractState holds information about a smart contract in the NEO blockchain. -type ContractState struct { +// Contract holds information about a smart contract in the NEO blockchain. +type Contract struct { Script []byte ParamList []smartcontract.ParamType ReturnType smartcontract.ParamType @@ -23,7 +23,7 @@ type ContractState struct { } // DecodeBinary implements Serializable interface. -func (cs *ContractState) DecodeBinary(br *io.BinReader) { +func (cs *Contract) DecodeBinary(br *io.BinReader) { cs.Script = br.ReadVarBytes() br.ReadArray(&cs.ParamList) br.ReadLE(&cs.ReturnType) @@ -37,7 +37,7 @@ func (cs *ContractState) DecodeBinary(br *io.BinReader) { } // EncodeBinary implements Serializable interface. -func (cs *ContractState) EncodeBinary(bw *io.BinWriter) { +func (cs *Contract) EncodeBinary(bw *io.BinWriter) { bw.WriteVarBytes(cs.Script) bw.WriteArray(cs.ParamList) bw.WriteLE(cs.ReturnType) @@ -50,7 +50,7 @@ func (cs *ContractState) EncodeBinary(bw *io.BinWriter) { } // ScriptHash returns a contract script hash. -func (cs *ContractState) ScriptHash() util.Uint160 { +func (cs *Contract) ScriptHash() util.Uint160 { if cs.scriptHash.Equals(util.Uint160{}) { cs.createHash() } @@ -58,21 +58,21 @@ func (cs *ContractState) ScriptHash() util.Uint160 { } // createHash creates contract script hash. -func (cs *ContractState) createHash() { +func (cs *Contract) createHash() { cs.scriptHash = hash.Hash160(cs.Script) } // HasStorage checks whether the contract has storage property set. -func (cs *ContractState) HasStorage() bool { +func (cs *Contract) HasStorage() bool { return (cs.Properties & smartcontract.HasStorage) != 0 } // HasDynamicInvoke checks whether the contract has dynamic invoke property set. -func (cs *ContractState) HasDynamicInvoke() bool { +func (cs *Contract) HasDynamicInvoke() bool { return (cs.Properties & smartcontract.HasDynamicInvoke) != 0 } // IsPayable checks whether the contract has payable property set. -func (cs *ContractState) IsPayable() bool { +func (cs *Contract) IsPayable() bool { return (cs.Properties & smartcontract.IsPayable) != 0 } diff --git a/pkg/core/entities/contract_state_test.go b/pkg/core/state/contract_test.go similarity index 90% rename from pkg/core/entities/contract_state_test.go rename to pkg/core/state/contract_test.go index ba9e5f44a..9f57d51e9 100644 --- a/pkg/core/entities/contract_state_test.go +++ b/pkg/core/state/contract_test.go @@ -1,4 +1,4 @@ -package entities +package state import ( "testing" @@ -12,7 +12,7 @@ import ( func TestEncodeDecodeContractState(t *testing.T) { script := []byte("testscript") - contract := &ContractState{ + contract := &Contract{ Script: script, ParamList: []smartcontract.ParamType{smartcontract.StringType, smartcontract.IntegerType, smartcontract.Hash160Type}, ReturnType: smartcontract.BoolType, @@ -28,7 +28,7 @@ func TestEncodeDecodeContractState(t *testing.T) { buf := io.NewBufBinWriter() contract.EncodeBinary(buf.BinWriter) assert.Nil(t, buf.Err) - contractDecoded := &ContractState{} + contractDecoded := &Contract{} r := io.NewBinReaderFromBuf(buf.Bytes()) contractDecoded.DecodeBinary(r) assert.Nil(t, r.Err) @@ -37,10 +37,10 @@ func TestEncodeDecodeContractState(t *testing.T) { } func TestContractStateProperties(t *testing.T) { - flaggedContract := ContractState{ + flaggedContract := Contract{ Properties: smartcontract.HasStorage | smartcontract.HasDynamicInvoke | smartcontract.IsPayable, } - nonFlaggedContract := ContractState{ + nonFlaggedContract := Contract{ ReturnType: smartcontract.BoolType, } assert.Equal(t, true, flaggedContract.HasStorage()) diff --git a/pkg/core/entities/notification_event.go b/pkg/core/state/notification_event.go similarity index 98% rename from pkg/core/entities/notification_event.go rename to pkg/core/state/notification_event.go index 47ea291ef..d86165be9 100644 --- a/pkg/core/entities/notification_event.go +++ b/pkg/core/state/notification_event.go @@ -1,4 +1,4 @@ -package entities +package state import ( "github.com/CityOfZion/neo-go/pkg/io" diff --git a/pkg/core/entities/notification_event_test.go b/pkg/core/state/notification_event_test.go similarity index 98% rename from pkg/core/entities/notification_event_test.go rename to pkg/core/state/notification_event_test.go index 6bd7f47dc..7b41859c3 100644 --- a/pkg/core/entities/notification_event_test.go +++ b/pkg/core/state/notification_event_test.go @@ -1,4 +1,4 @@ -package entities +package state import ( "testing" diff --git a/pkg/core/entities/storage_item.go b/pkg/core/state/storage_item.go similarity index 96% rename from pkg/core/entities/storage_item.go rename to pkg/core/state/storage_item.go index 2e5965616..49845f07b 100644 --- a/pkg/core/entities/storage_item.go +++ b/pkg/core/state/storage_item.go @@ -1,4 +1,4 @@ -package entities +package state import ( "github.com/CityOfZion/neo-go/pkg/io" diff --git a/pkg/core/entities/storage_item_test.go b/pkg/core/state/storage_item_test.go similarity index 97% rename from pkg/core/entities/storage_item_test.go rename to pkg/core/state/storage_item_test.go index 6200cc19b..aeb7a5b80 100644 --- a/pkg/core/entities/storage_item_test.go +++ b/pkg/core/state/storage_item_test.go @@ -1,4 +1,4 @@ -package entities +package state import ( "testing" diff --git a/pkg/core/entities/validator_state.go b/pkg/core/state/validator.go similarity index 74% rename from pkg/core/entities/validator_state.go rename to pkg/core/state/validator.go index 48e6601de..c9f5a5862 100644 --- a/pkg/core/entities/validator_state.go +++ b/pkg/core/state/validator.go @@ -1,4 +1,4 @@ -package entities +package state import ( "github.com/CityOfZion/neo-go/pkg/crypto/keys" @@ -6,27 +6,27 @@ import ( "github.com/CityOfZion/neo-go/pkg/util" ) -// ValidatorState holds the state of a validator. -type ValidatorState struct { +// Validator holds the state of a validator. +type Validator struct { PublicKey *keys.PublicKey Registered bool Votes util.Fixed8 } // RegisteredAndHasVotes returns true or false whether Validator is registered and has votes. -func (vs *ValidatorState) RegisteredAndHasVotes() bool { +func (vs *Validator) RegisteredAndHasVotes() bool { return vs.Registered && vs.Votes > util.Fixed8(0) } -// EncodeBinary encodes ValidatorState to the given BinWriter. -func (vs *ValidatorState) EncodeBinary(bw *io.BinWriter) { +// EncodeBinary encodes Validator to the given BinWriter. +func (vs *Validator) EncodeBinary(bw *io.BinWriter) { vs.PublicKey.EncodeBinary(bw) bw.WriteLE(vs.Registered) bw.WriteLE(vs.Votes) } -// DecodeBinary decodes ValidatorState from the given BinReader. -func (vs *ValidatorState) DecodeBinary(reader *io.BinReader) { +// DecodeBinary decodes Validator from the given BinReader. +func (vs *Validator) DecodeBinary(reader *io.BinReader) { vs.PublicKey = &keys.PublicKey{} vs.PublicKey.DecodeBinary(reader) reader.ReadLE(&vs.Registered) @@ -35,17 +35,17 @@ func (vs *ValidatorState) DecodeBinary(reader *io.BinReader) { // GetValidatorsWeightedAverage applies weighted filter based on votes for validator and returns number of validators. // Get back to it with further investigation in https://github.com/nspcc-dev/neo-go/issues/512. -func GetValidatorsWeightedAverage(validators []*ValidatorState) int { +func GetValidatorsWeightedAverage(validators []*Validator) int { return int(weightedAverage(applyWeightedFilter(validators))) } // applyWeightedFilter is an implementation of the filter for validators votes. // C# reference https://github.com/neo-project/neo/blob/41caff115c28d6c7665b2a7ac72967e7ce82e921/neo/Helper.cs#L273 -func applyWeightedFilter(validators []*ValidatorState) map[*ValidatorState]float64 { - var validatorsWithVotes []*ValidatorState +func applyWeightedFilter(validators []*Validator) map[*Validator]float64 { + var validatorsWithVotes []*Validator var amount float64 - weightedVotes := make(map[*ValidatorState]float64) + weightedVotes := make(map[*Validator]float64) start := 0.25 end := 0.75 sum := float64(0) @@ -85,7 +85,7 @@ func applyWeightedFilter(validators []*ValidatorState) map[*ValidatorState]float return weightedVotes } -func weightedAverage(weightedVotes map[*ValidatorState]float64) float64 { +func weightedAverage(weightedVotes map[*Validator]float64) float64 { sumWeight := float64(0) sumValue := float64(0) for vState, weight := range weightedVotes { diff --git a/pkg/core/entities/validator_state_test.go b/pkg/core/state/validator_test.go similarity index 89% rename from pkg/core/entities/validator_state_test.go rename to pkg/core/state/validator_test.go index 521cf0827..ccd9d18d9 100644 --- a/pkg/core/entities/validator_state_test.go +++ b/pkg/core/state/validator_test.go @@ -1,4 +1,4 @@ -package entities +package state import ( "math/big" @@ -11,7 +11,7 @@ import ( ) func TestValidatorState_DecodeEncodeBinary(t *testing.T) { - state := &ValidatorState{ + state := &Validator{ PublicKey: &keys.PublicKey{}, Registered: false, Votes: util.Fixed8(10), @@ -20,7 +20,7 @@ func TestValidatorState_DecodeEncodeBinary(t *testing.T) { state.EncodeBinary(buf.BinWriter) require.NoError(t, buf.Err) - decodedState := &ValidatorState{} + decodedState := &Validator{} reader := io.NewBinReaderFromBuf(buf.Bytes()) decodedState.DecodeBinary(reader) require.NoError(t, reader.Err) @@ -28,7 +28,7 @@ func TestValidatorState_DecodeEncodeBinary(t *testing.T) { } func TestRegisteredAndHasVotes_Registered(t *testing.T) { - state := &ValidatorState{ + state := &Validator{ PublicKey: &keys.PublicKey{ X: big.NewInt(1), Y: big.NewInt(1), @@ -40,7 +40,7 @@ func TestRegisteredAndHasVotes_Registered(t *testing.T) { } func TestRegisteredAndHasVotes_RegisteredWithVotes(t *testing.T) { - state := &ValidatorState{ + state := &Validator{ PublicKey: &keys.PublicKey{ X: big.NewInt(1), Y: big.NewInt(1), @@ -52,7 +52,7 @@ func TestRegisteredAndHasVotes_RegisteredWithVotes(t *testing.T) { } func TestRegisteredAndHasVotes_NotRegisteredWithVotes(t *testing.T) { - state := &ValidatorState{ + state := &Validator{ PublicKey: &keys.PublicKey{ X: big.NewInt(1), Y: big.NewInt(1), diff --git a/pkg/core/unspent_coin_state.go b/pkg/core/unspent_coin_state.go index 3823a8879..c4c8f9723 100644 --- a/pkg/core/unspent_coin_state.go +++ b/pkg/core/unspent_coin_state.go @@ -1,22 +1,22 @@ package core import ( - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/io" ) // UnspentCoinState hold the state of a unspent coin. type UnspentCoinState struct { - states []entities.CoinState + states []state.Coin } // NewUnspentCoinState returns a new unspent coin state with N confirmed states. func NewUnspentCoinState(n int) *UnspentCoinState { u := &UnspentCoinState{ - states: make([]entities.CoinState, n), + states: make([]state.Coin, n), } for i := 0; i < n; i++ { - u.states[i] = entities.CoinStateConfirmed + u.states[i] = state.CoinConfirmed } return u } @@ -32,10 +32,10 @@ func (s *UnspentCoinState) EncodeBinary(bw *io.BinWriter) { // DecodeBinary decodes UnspentCoinState from the given BinReader. func (s *UnspentCoinState) DecodeBinary(br *io.BinReader) { lenStates := br.ReadVarUint() - s.states = make([]entities.CoinState, lenStates) + s.states = make([]state.Coin, lenStates) for i := 0; i < int(lenStates); i++ { - var state uint8 - br.ReadLE(&state) - s.states[i] = entities.CoinState(state) + var coinState uint8 + br.ReadLE(&coinState) + s.states[i] = state.Coin(coinState) } } diff --git a/pkg/core/unspent_coint_state_test.go b/pkg/core/unspent_coint_state_test.go index 18627feab..8864619c6 100644 --- a/pkg/core/unspent_coint_state_test.go +++ b/pkg/core/unspent_coint_state_test.go @@ -3,19 +3,19 @@ package core import ( "testing" - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) func TestDecodeEncodeUnspentCoinState(t *testing.T) { unspent := &UnspentCoinState{ - states: []entities.CoinState{ - entities.CoinStateConfirmed, - entities.CoinStateSpent, - entities.CoinStateSpent, - entities.CoinStateSpent, - entities.CoinStateConfirmed, + states: []state.Coin{ + state.CoinConfirmed, + state.CoinSpent, + state.CoinSpent, + state.CoinSpent, + state.CoinConfirmed, }, } diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index afbcc921e..da2039650 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -9,7 +9,7 @@ import ( "github.com/CityOfZion/neo-go/config" "github.com/CityOfZion/neo-go/pkg/core" - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" @@ -63,7 +63,7 @@ func (chain testChain) HeaderHeight() uint32 { func (chain testChain) GetBlock(hash util.Uint256) (*core.Block, error) { panic("TODO") } -func (chain testChain) GetContractState(hash util.Uint160) *entities.ContractState { +func (chain testChain) GetContractState(hash util.Uint160) *state.Contract { panic("TODO") } func (chain testChain) GetHeaderHash(int) util.Uint256 { @@ -73,10 +73,10 @@ func (chain testChain) GetHeader(hash util.Uint256) (*core.Header, error) { panic("TODO") } -func (chain testChain) GetAssetState(util.Uint256) *entities.AssetState { +func (chain testChain) GetAssetState(util.Uint256) *state.Asset { panic("TODO") } -func (chain testChain) GetAccountState(util.Uint160) *entities.AccountState { +func (chain testChain) GetAccountState(util.Uint160) *state.Account { panic("TODO") } func (chain testChain) GetValidators(...*transaction.Transaction) ([]*keys.PublicKey, error) { @@ -85,13 +85,13 @@ func (chain testChain) GetValidators(...*transaction.Transaction) ([]*keys.Publi func (chain testChain) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) { panic("TODO") } -func (chain testChain) GetStorageItem(scripthash util.Uint160, key []byte) *entities.StorageItem { +func (chain testChain) GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem { panic("TODO") } func (chain testChain) GetTestVM() (*vm.VM, storage.Store) { panic("TODO") } -func (chain testChain) GetStorageItems(hash util.Uint160) (map[string]*entities.StorageItem, error) { +func (chain testChain) GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) { panic("TODO") } func (chain testChain) CurrentHeaderHash() util.Uint256 { diff --git a/pkg/rpc/client.go b/pkg/rpc/client.go index 054861b64..e8a75bc81 100644 --- a/pkg/rpc/client.go +++ b/pkg/rpc/client.go @@ -11,7 +11,7 @@ import ( "sync" "time" - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/util" @@ -158,7 +158,7 @@ func (c *Client) SetClient(cli *http.Client) { // asset belonging to specified address. This implementation uses GetUnspents // JSON-RPC call internally, so make sure your RPC server suppors that. func (c *Client) CalculateInputs(address string, asset util.Uint256, cost util.Fixed8) ([]transaction.Input, util.Fixed8, error) { - var utxos entities.UnspentBalances + var utxos state.UnspentBalances resp, err := c.GetUnspents(address) if err != nil || resp.Error != nil { diff --git a/pkg/rpc/neoScanBalanceGetter.go b/pkg/rpc/neoScanBalanceGetter.go index 709cb64e5..c0e95c595 100644 --- a/pkg/rpc/neoScanBalanceGetter.go +++ b/pkg/rpc/neoScanBalanceGetter.go @@ -6,7 +6,7 @@ import ( "net/http" "sort" - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/rpc/wrappers" "github.com/CityOfZion/neo-go/pkg/util" @@ -68,7 +68,7 @@ func (s NeoScanServer) CalculateInputs(address string, assetIDUint util.Uint256, // unspentsToInputs uses UnspentBalances to create a slice of inputs for a new // transcation containing the required amount of asset. -func unspentsToInputs(utxos entities.UnspentBalances, required util.Fixed8) ([]transaction.Input, util.Fixed8, error) { +func unspentsToInputs(utxos state.UnspentBalances, required util.Fixed8) ([]transaction.Input, util.Fixed8, error) { var ( num, i uint16 selected = util.Fixed8(0) diff --git a/pkg/rpc/neoScanTypes.go b/pkg/rpc/neoScanTypes.go index 7e050348e..beb1f8fe1 100644 --- a/pkg/rpc/neoScanTypes.go +++ b/pkg/rpc/neoScanTypes.go @@ -1,7 +1,7 @@ package rpc import ( - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/util" ) @@ -20,7 +20,7 @@ type ( // Unspent stores Unspents per asset Unspent struct { - Unspent entities.UnspentBalances + Unspent state.UnspentBalances Asset string // "NEO" / "GAS" Amount util.Fixed8 // total unspent of this asset } diff --git a/pkg/rpc/wrappers/account_state.go b/pkg/rpc/wrappers/account_state.go index 56f5d8569..2d0ff5d79 100644 --- a/pkg/rpc/wrappers/account_state.go +++ b/pkg/rpc/wrappers/account_state.go @@ -2,7 +2,7 @@ package wrappers import ( "bytes" - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "sort" "github.com/CityOfZion/neo-go/pkg/crypto/keys" @@ -10,7 +10,7 @@ import ( ) // AccountState wrapper used for the representation of -// core.AccountState on the RPC Server. +// state.Account on the RPC Server. type AccountState struct { Version uint8 `json:"version"` ScriptHash util.Uint160 `json:"script_hash"` @@ -32,8 +32,8 @@ type Balance struct { Value util.Fixed8 `json:"value"` } -// NewAccountState creates a new AccountState wrapper. -func NewAccountState(a *entities.AccountState) AccountState { +// NewAccountState creates a new Account wrapper. +func NewAccountState(a *state.Account) AccountState { balances := make(Balances, 0, len(a.Balances)) for k, v := range a.GetBalanceValues() { balances = append(balances, Balance{ diff --git a/pkg/rpc/wrappers/asset_state.go b/pkg/rpc/wrappers/asset_state.go index e0f3e472c..b9b08c3ce 100644 --- a/pkg/rpc/wrappers/asset_state.go +++ b/pkg/rpc/wrappers/asset_state.go @@ -1,14 +1,14 @@ package wrappers import ( - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto" "github.com/CityOfZion/neo-go/pkg/util" ) // AssetState wrapper used for the representation of -// core.AssetState on the RPC Server. +// state.Asset on the RPC Server. type AssetState struct { ID util.Uint256 `json:"assetID"` AssetType transaction.AssetType `json:"assetType"` @@ -25,8 +25,8 @@ type AssetState struct { IsFrozen bool `json:"is_frozen"` } -// NewAssetState creates a new AssetState wrapper. -func NewAssetState(a *entities.AssetState) AssetState { +// NewAssetState creates a new Asset wrapper. +func NewAssetState(a *state.Asset) AssetState { return AssetState{ ID: a.ID, AssetType: a.AssetType, diff --git a/pkg/rpc/wrappers/unspents.go b/pkg/rpc/wrappers/unspents.go index ef10fb279..38c14b901 100644 --- a/pkg/rpc/wrappers/unspents.go +++ b/pkg/rpc/wrappers/unspents.go @@ -2,18 +2,18 @@ package wrappers import ( "github.com/CityOfZion/neo-go/pkg/core" - "github.com/CityOfZion/neo-go/pkg/core/entities" + "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/util" ) // UnspentBalanceInfo wrapper is used to represent single unspent asset entry // in `getunspents` output. type UnspentBalanceInfo struct { - Unspents []entities.UnspentBalance `json:"unspent"` - AssetHash util.Uint256 `json:"asset_hash"` - Asset string `json:"asset"` - AssetSymbol string `json:"asset_symbol"` - Amount util.Fixed8 `json:"amount"` + Unspents []state.UnspentBalance `json:"unspent"` + AssetHash util.Uint256 `json:"asset_hash"` + Asset string `json:"asset"` + AssetSymbol string `json:"asset_symbol"` + Amount util.Fixed8 `json:"amount"` } // Unspents wrapper is used to represent getunspents return result. @@ -28,8 +28,8 @@ var GlobalAssets = map[string]string{ "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7": "GAS", } -// NewUnspents creates a new AccountState wrapper using given Blockchainer. -func NewUnspents(a *entities.AccountState, chain core.Blockchainer, addr string) Unspents { +// NewUnspents creates a new Account wrapper using given Blockchainer. +func NewUnspents(a *state.Account, chain core.Blockchainer, addr string) Unspents { res := Unspents{ Address: addr, Balance: make([]UnspentBalanceInfo, 0, len(a.Balances)), From c1f39d5c7bf22970fc777f7a6000a0f01e8ad30b Mon Sep 17 00:00:00 2001 From: Vsevolod Brekelov Date: Wed, 4 Dec 2019 15:24:49 +0300 Subject: [PATCH 6/7] internal: moved testutil method to internal package --- pkg/core/dao_test.go | 32 +++++++++--------- pkg/core/interop_neo_test.go | 24 +++++++------- pkg/core/spent_coin_state_test.go | 4 +-- pkg/core/state/account_test.go | 16 ++++----- pkg/core/state/asset_test.go | 8 ++--- pkg/core/state/notification_event_test.go | 6 ++-- pkg/core/testutil/random_util.go | 40 ----------------------- pkg/internal/random/random_util.go | 40 +++++++++++++++++++++++ 8 files changed, 85 insertions(+), 85 deletions(-) delete mode 100644 pkg/core/testutil/random_util.go create mode 100644 pkg/internal/random/random_util.go diff --git a/pkg/core/dao_test.go b/pkg/core/dao_test.go index 7c014cfe7..5aa44fe9f 100644 --- a/pkg/core/dao_test.go +++ b/pkg/core/dao_test.go @@ -5,9 +5,9 @@ import ( "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/core/storage" - "github.com/CityOfZion/neo-go/pkg/core/testutil" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/internal/random" "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/smartcontract" "github.com/CityOfZion/neo-go/pkg/vm/opcode" @@ -16,7 +16,7 @@ import ( func TestPutGetAndDecode(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - serializable := &TestSerializable{field: testutil.RandomString(4)} + serializable := &TestSerializable{field: random.String(4)} hash := []byte{1} err := dao.Put(serializable, hash) require.NoError(t, err) @@ -41,7 +41,7 @@ func (t *TestSerializable) DecodeBinary(reader *io.BinReader) { func TestGetAccountStateOrNew_New(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - hash := testutil.RandomUint160() + hash := random.Uint160() createdAccount, err := dao.GetAccountStateOrNew(hash) require.NoError(t, err) require.NotNil(t, createdAccount) @@ -52,7 +52,7 @@ func TestGetAccountStateOrNew_New(t *testing.T) { func TestPutAndGetAccountStateOrNew(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - hash := testutil.RandomUint160() + hash := random.Uint160() accountState := &state.Account{ScriptHash: hash} err := dao.PutAccountState(accountState) require.NoError(t, err) @@ -63,7 +63,7 @@ func TestPutAndGetAccountStateOrNew(t *testing.T) { func TestPutAndGetAssetState(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - id := testutil.RandomUint256() + id := random.Uint256() assetState := &state.Asset{ID: id, Owner: keys.PublicKey{}} err := dao.PutAssetState(assetState) require.NoError(t, err) @@ -98,7 +98,7 @@ func TestDeleteContractState(t *testing.T) { func TestGetUnspentCoinStateOrNew_New(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - hash := testutil.RandomUint256() + hash := random.Uint256() unspentCoinState, err := dao.GetUnspentCoinStateOrNew(hash) require.NoError(t, err) require.NotNil(t, unspentCoinState) @@ -109,7 +109,7 @@ func TestGetUnspentCoinStateOrNew_New(t *testing.T) { func TestGetUnspentCoinState_Err(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - hash := testutil.RandomUint256() + hash := random.Uint256() gotUnspentCoinState, err := dao.GetUnspentCoinState(hash) require.Error(t, err) require.Nil(t, gotUnspentCoinState) @@ -117,7 +117,7 @@ func TestGetUnspentCoinState_Err(t *testing.T) { func TestPutGetUnspentCoinState(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - hash := testutil.RandomUint256() + hash := random.Uint256() unspentCoinState := &UnspentCoinState{states:[]state.Coin{}} err := dao.PutUnspentCoinState(hash, unspentCoinState) require.NoError(t, err) @@ -128,7 +128,7 @@ func TestPutGetUnspentCoinState(t *testing.T) { func TestGetSpentCoinStateOrNew_New(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - hash := testutil.RandomUint256() + hash := random.Uint256() spentCoinState, err := dao.GetSpentCoinsOrNew(hash) require.NoError(t, err) require.NotNil(t, spentCoinState) @@ -139,7 +139,7 @@ func TestGetSpentCoinStateOrNew_New(t *testing.T) { func TestPutAndGetSpentCoinState(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - hash := testutil.RandomUint256() + hash := random.Uint256() spentCoinState := &SpentCoinState{items:make(map[uint16]uint32)} err := dao.PutSpentCoinState(hash, spentCoinState) require.NoError(t, err) @@ -150,7 +150,7 @@ func TestPutAndGetSpentCoinState(t *testing.T) { func TestGetSpentCoinState_Err(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - hash := testutil.RandomUint256() + hash := random.Uint256() spentCoinState, err := dao.GetSpentCoinState(hash) require.Error(t, err) require.Nil(t, spentCoinState) @@ -158,7 +158,7 @@ func TestGetSpentCoinState_Err(t *testing.T) { func TestDeleteSpentCoinState(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - hash := testutil.RandomUint256() + hash := random.Uint256() spentCoinState := &SpentCoinState{items:make(map[uint16]uint32)} err := dao.PutSpentCoinState(hash, spentCoinState) require.NoError(t, err) @@ -229,7 +229,7 @@ func TestGetValidators(t *testing.T) { func TestPutGetAppExecResult(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - hash := testutil.RandomUint256() + hash := random.Uint256() appExecResult := &state.AppExecResult{TxHash: hash, Events:[]state.NotificationEvent{}} err := dao.PutAppExecResult(appExecResult) require.NoError(t, err) @@ -240,7 +240,7 @@ func TestPutGetAppExecResult(t *testing.T) { func TestPutGetStorageItem(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - hash := testutil.RandomUint160() + hash := random.Uint160() key := []byte{0} storageItem := &state.StorageItem{Value: []uint8{}} err := dao.PutStorageItem(hash, key, storageItem) @@ -251,7 +251,7 @@ func TestPutGetStorageItem(t *testing.T) { func TestDeleteStorageItem(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - hash := testutil.RandomUint160() + hash := random.Uint160() key := []byte{0} storageItem := &state.StorageItem{Value: []uint8{}} err := dao.PutStorageItem(hash, key, storageItem) @@ -264,7 +264,7 @@ func TestDeleteStorageItem(t *testing.T) { func TestGetBlock_NotExists(t *testing.T) { dao := &dao{store: storage.NewMemCachedStore(storage.NewMemoryStore())} - hash := testutil.RandomUint256() + hash := random.Uint256() block, err := dao.GetBlock(hash) require.Error(t, err) require.Nil(t, block) diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index 9cf07369e..ffe48a92f 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -6,9 +6,9 @@ import ( "github.com/CityOfZion/neo-go/pkg/core/state" "github.com/CityOfZion/neo-go/pkg/core/storage" - "github.com/CityOfZion/neo-go/pkg/core/testutil" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/internal/random" "github.com/CityOfZion/neo-go/pkg/smartcontract" "github.com/CityOfZion/neo-go/pkg/smartcontract/trigger" "github.com/CityOfZion/neo-go/pkg/util" @@ -349,10 +349,10 @@ func createVMAndAssetState(t *testing.T) (*vm.VM, *state.Asset, *interopContext) Available: 2, Precision: 1, FeeMode: 1, - FeeAddress: testutil.RandomUint160(), + FeeAddress: random.Uint160(), Owner: keys.PublicKey{X: big.NewInt(1), Y: big.NewInt(1)}, - Admin: testutil.RandomUint160(), - Issuer: testutil.RandomUint160(), + Admin: random.Uint160(), + Issuer: random.Uint160(), Expiration: 10, IsFrozen: false, } @@ -368,11 +368,11 @@ func createVMAndContractState(t *testing.T) (*vm.VM, *state.Contract, *interopCo ParamList: []smartcontract.ParamType{smartcontract.StringType, smartcontract.IntegerType, smartcontract.Hash160Type}, ReturnType: smartcontract.ArrayType, Properties: smartcontract.HasStorage, - Name: testutil.RandomString(10), - CodeVersion: testutil.RandomString(10), - Author: testutil.RandomString(10), - Email: testutil.RandomString(10), - Description: testutil.RandomString(10), + Name: random.String(10), + CodeVersion: random.String(10), + Author: random.String(10), + Email: random.String(10), + Description: random.String(10), } context := newInteropContext(trigger.Application, newTestChain(t), storage.NewMemoryStore(), nil, nil) @@ -404,14 +404,14 @@ func createVMAndTX(t *testing.T) (*vm.VM, *transaction.Transaction, *interopCont }) inputs := append(tx.Inputs, transaction.Input{ - PrevHash: testutil.RandomUint256(), + PrevHash: random.Uint256(), PrevIndex: 1, }) outputs := append(tx.Outputs, transaction.Output{ - AssetID: testutil.RandomUint256(), + AssetID: random.Uint256(), Amount: 10, - ScriptHash: testutil.RandomUint160(), + ScriptHash: random.Uint160(), Position: 1, }) diff --git a/pkg/core/spent_coin_state_test.go b/pkg/core/spent_coin_state_test.go index 0e3b65474..ec8176130 100644 --- a/pkg/core/spent_coin_state_test.go +++ b/pkg/core/spent_coin_state_test.go @@ -3,14 +3,14 @@ package core import ( "testing" - "github.com/CityOfZion/neo-go/pkg/core/testutil" + "github.com/CityOfZion/neo-go/pkg/internal/random" "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) func TestEncodeDecodeSpentCoinState(t *testing.T) { spent := &SpentCoinState{ - txHash: testutil.RandomUint256(), + txHash: random.Uint256(), txHeight: 1001, items: map[uint16]uint32{ 1: 3, diff --git a/pkg/core/state/account_test.go b/pkg/core/state/account_test.go index e59215f3b..3981222f7 100644 --- a/pkg/core/state/account_test.go +++ b/pkg/core/state/account_test.go @@ -3,8 +3,8 @@ package state import ( "testing" - "github.com/CityOfZion/neo-go/pkg/core/testutil" "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/internal/random" "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" "github.com/stretchr/testify/assert" @@ -17,12 +17,12 @@ func TestDecodeEncodeAccountState(t *testing.T) { votes = make([]*keys.PublicKey, n) ) for i := 0; i < n; i++ { - asset := testutil.RandomUint256() + asset := random.Uint256() for j := 0; j < i+1; j++ { balances[asset] = append(balances[asset], UnspentBalance{ - Tx: testutil.RandomUint256(), - Index: uint16(testutil.RandomInt(0, 65535)), - Value: util.Fixed8(int64(testutil.RandomInt(1, 10000))), + Tx: random.Uint256(), + Index: uint16(random.Int(0, 65535)), + Value: util.Fixed8(int64(random.Int(1, 10000))), }) } k, err := keys.NewPrivateKey() @@ -32,7 +32,7 @@ func TestDecodeEncodeAccountState(t *testing.T) { a := &Account{ Version: 0, - ScriptHash: testutil.RandomUint160(), + ScriptHash: random.Uint160(), IsFrozen: true, Votes: votes, Balances: balances, @@ -58,8 +58,8 @@ func TestDecodeEncodeAccountState(t *testing.T) { } func TestAccountStateBalanceValues(t *testing.T) { - asset1 := testutil.RandomUint256() - asset2 := testutil.RandomUint256() + asset1 := random.Uint256() + asset2 := random.Uint256() as := Account{Balances: make(map[util.Uint256][]UnspentBalance)} ref := 0 for i := 0; i < 10; i++ { diff --git a/pkg/core/state/asset_test.go b/pkg/core/state/asset_test.go index 790ba4f68..5a0a1daed 100644 --- a/pkg/core/state/asset_test.go +++ b/pkg/core/state/asset_test.go @@ -3,9 +3,9 @@ package state import ( "testing" - "github.com/CityOfZion/neo-go/pkg/core/testutil" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto/keys" + "github.com/CityOfZion/neo-go/pkg/internal/random" "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" "github.com/stretchr/testify/assert" @@ -13,7 +13,7 @@ import ( func TestEncodeDecodeAssetState(t *testing.T) { asset := &Asset{ - ID: testutil.RandomUint256(), + ID: random.Uint256(), AssetType: transaction.Token, Name: "super cool token", Amount: util.Fixed8(1000000), @@ -21,8 +21,8 @@ func TestEncodeDecodeAssetState(t *testing.T) { Precision: 0, FeeMode: feeMode, Owner: keys.PublicKey{}, - Admin: testutil.RandomUint160(), - Issuer: testutil.RandomUint160(), + Admin: random.Uint160(), + Issuer: random.Uint160(), Expiration: 10, IsFrozen: false, } diff --git a/pkg/core/state/notification_event_test.go b/pkg/core/state/notification_event_test.go index 7b41859c3..0e92ae898 100644 --- a/pkg/core/state/notification_event_test.go +++ b/pkg/core/state/notification_event_test.go @@ -3,14 +3,14 @@ package state import ( "testing" - "github.com/CityOfZion/neo-go/pkg/core/testutil" + "github.com/CityOfZion/neo-go/pkg/internal/random" "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) func TestEncodeDecodeNotificationEvent(t *testing.T) { event := &NotificationEvent{ - ScriptHash: testutil.RandomUint160(), + ScriptHash: random.Uint160(), Item: nil, } @@ -26,7 +26,7 @@ func TestEncodeDecodeNotificationEvent(t *testing.T) { func TestEncodeDecodeAppExecResult(t *testing.T) { appExecResult := &AppExecResult{ - TxHash: testutil.RandomUint256(), + TxHash: random.Uint256(), Trigger: 1, VMState: "Hault", GasConsumed: 10, diff --git a/pkg/core/testutil/random_util.go b/pkg/core/testutil/random_util.go deleted file mode 100644 index c58832e89..000000000 --- a/pkg/core/testutil/random_util.go +++ /dev/null @@ -1,40 +0,0 @@ -package testutil - -import ( - "math/rand" - "time" - - "github.com/CityOfZion/neo-go/pkg/crypto/hash" - "github.com/CityOfZion/neo-go/pkg/util" -) - -// RandomString returns a random string with the n as its length. -func RandomString(n int) string { - b := make([]byte, n) - for i := range b { - b[i] = byte(RandomInt(65, 90)) - } - - return string(b) -} - -// RandomInt returns a random integer in [min,max). -func RandomInt(min, max int) int { - return min + rand.Intn(max-min) -} - -// RandomUint256 returns a random Uint256. -func RandomUint256() util.Uint256 { - str := RandomString(20) - return hash.Sha256([]byte(str)) -} - -// RandomUint160 returns a random Uint160. -func RandomUint160() util.Uint160 { - str := RandomString(20) - return hash.RipeMD160([]byte(str)) -} - -func init() { - rand.Seed(time.Now().UTC().UnixNano()) -} diff --git a/pkg/internal/random/random_util.go b/pkg/internal/random/random_util.go new file mode 100644 index 000000000..d07e4da50 --- /dev/null +++ b/pkg/internal/random/random_util.go @@ -0,0 +1,40 @@ +package random + +import ( + "math/rand" + "time" + + "github.com/CityOfZion/neo-go/pkg/crypto/hash" + "github.com/CityOfZion/neo-go/pkg/util" +) + +// String returns a random string with the n as its length. +func String(n int) string { + b := make([]byte, n) + for i := range b { + b[i] = byte(Int(65, 90)) + } + + return string(b) +} + +// Int returns a random integer in [min,max). +func Int(min, max int) int { + return min + rand.Intn(max-min) +} + +// Uint256 returns a random Uint256. +func Uint256() util.Uint256 { + str := String(20) + return hash.Sha256([]byte(str)) +} + +// Uint160 returns a random Uint160. +func Uint160() util.Uint160 { + str := String(20) + return hash.RipeMD160([]byte(str)) +} + +func init() { + rand.Seed(time.Now().UTC().UnixNano()) +} From c93a8d2bc49e54a19950d4c6aa409c1f54b7ebfa Mon Sep 17 00:00:00 2001 From: Vsevolod Brekelov Date: Wed, 11 Dec 2019 13:10:51 +0300 Subject: [PATCH 7/7] core: extracted same logic to separate methods --- pkg/core/blockchain.go | 120 ++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 73 deletions(-) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 87ca8a2f1..8d43ff553 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -394,23 +394,8 @@ func (bc *Blockchain) storeBlock(block *Block) error { if err = cache.PutSpentCoinState(input.PrevHash, spentCoin); err != nil { return err } - if len(account.Votes) > 0 { - for _, vote := range account.Votes { - validator, err := cache.GetValidatorStateOrNew(vote) - if err != nil { - return err - } - validator.Votes -= prevTXOutput.Amount - if !validator.RegisteredAndHasVotes() { - if err = cache.DeleteValidatorState(validator); err != nil { - return err - } - } else { - if err = cache.PutValidatorState(validator); err != nil { - return err - } - } - } + if err = processTXWithValidatorsSubtract(account, cache, prevTXOutput.Amount); err != nil { + return err } } @@ -570,7 +555,7 @@ func (bc *Blockchain) storeBlock(block *Block) error { } } _, err := cache.store.Persist() - if err!= nil { + if err != nil { return err } atomic.StoreUint32(&bc.blockHeight, block.Index) @@ -596,16 +581,43 @@ func processOutputs(tx *transaction.Transaction, dao *dao) error { if err = dao.PutAccountState(account); err != nil { return err } - if output.AssetID.Equals(governingTokenTX().Hash()) && len(account.Votes) > 0 { - for _, vote := range account.Votes { - validatorState, err := dao.GetValidatorStateOrNew(vote) - if err != nil { - return err - } - validatorState.Votes += output.Amount - if err = dao.PutValidatorState(validatorState); err != nil { - return err - } + if err = processTXWithValidatorsAdd(&output, account, dao); err != nil { + return err + } + } + return nil +} + +func processTXWithValidatorsAdd(output *transaction.Output, account *state.Account, dao *dao) error { + if output.AssetID.Equals(governingTokenTX().Hash()) && len(account.Votes) > 0 { + for _, vote := range account.Votes { + validatorState, err := dao.GetValidatorStateOrNew(vote) + if err != nil { + return err + } + validatorState.Votes += output.Amount + if err = dao.PutValidatorState(validatorState); err != nil { + return err + } + } + } + return nil +} + +func processTXWithValidatorsSubtract(account *state.Account, dao *dao, toSubtract util.Fixed8) error { + for _, vote := range account.Votes { + validator, err := dao.GetValidatorStateOrNew(vote) + if err != nil { + return err + } + validator.Votes -= toSubtract + if !validator.RegisteredAndHasVotes() { + if err := dao.DeleteValidatorState(validator); err != nil { + return err + } + } else { + if err := dao.PutValidatorState(validator); err != nil { + return err } } } @@ -645,21 +657,8 @@ func processAccountStateDescriptor(descriptor *transaction.StateDescriptor, dao if descriptor.Field == "Votes" { balance := account.GetBalanceValues()[governingTokenTX().Hash()] - for _, vote := range account.Votes { - validator, err := dao.GetValidatorStateOrNew(vote) - if err != nil { - return err - } - validator.Votes -= balance - if !validator.RegisteredAndHasVotes() { - if err := dao.DeleteValidatorState(validator); err != nil { - return err - } - } else { - if err := dao.PutValidatorState(validator); err != nil { - return err - } - } + if err = processTXWithValidatorsSubtract(account, dao, balance); err != nil { + return err } votes := keys.PublicKeys{} @@ -840,7 +839,7 @@ func (bc *Blockchain) GetAssetState(assetID util.Uint256) *state.Asset { // GetContractState returns contract by its script hash. func (bc *Blockchain) GetContractState(hash util.Uint160) *state.Contract { - contract, err := bc.dao.GetContractState(hash) + contract, err := bc.dao.GetContractState(hash) if contract == nil && err != storage.ErrKeyNotFound { log.Warnf("failed to get contract state: %s", err) } @@ -1174,17 +1173,8 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P if err := cache.PutAccountState(accountState); err != nil { return nil, err } - if output.AssetID.Equals(governingTokenTX().Hash()) && len(accountState.Votes) > 0 { - for _, vote := range accountState.Votes { - validatorState, err := cache.GetValidatorStateOrNew(vote) - if err != nil { - return nil, err - } - validatorState.Votes += output.Amount - if err = cache.PutValidatorState(validatorState); err != nil { - return nil, err - } - } + if err = processTXWithValidatorsAdd(&output, accountState, cache); err != nil { + return nil, err } } @@ -1209,24 +1199,8 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P } // process account state votes: if there are any -> validators will be updated. - if prevOutput.AssetID.Equals(governingTokenTX().Hash()) { - if len(accountState.Votes) > 0 { - for _, vote := range accountState.Votes { - validatorState, err := cache.GetValidatorStateOrNew(vote) - if err != nil { - return nil, err - } - validatorState.Votes -= prevOutput.Amount - if err = cache.PutValidatorState(validatorState); err != nil { - return nil, err - } - if !validatorState.Registered && validatorState.Votes.Equal(util.Fixed8(0)) { - if err = cache.DeleteValidatorState(validatorState); err != nil { - return nil, err - } - } - } - } + if err = processTXWithValidatorsSubtract(accountState, cache, prevOutput.Amount); err != nil { + return nil, err } delete(accountState.Balances, prevOutput.AssetID) if err = cache.PutAccountState(accountState); err != nil {