mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-23 13:41:37 +00:00
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
This commit is contained in:
parent
c43ff15c78
commit
ec17654986
39 changed files with 958 additions and 1258 deletions
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
555
pkg/core/dao.go
Normal file
555
pkg/core/dao.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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 {
|
|
@ -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++ {
|
|
@ -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
|
|
@ -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)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package core
|
||||
package entities
|
||||
|
||||
// CoinState represents the state of a coin.
|
||||
type CoinState uint8
|
|
@ -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{}) {
|
|
@ -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)
|
||||
}
|
|
@ -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[:])
|
23
pkg/core/entities/storage_item.go
Normal file
23
pkg/core/entities/storage_item.go
Normal file
|
@ -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)
|
||||
}
|
2
pkg/core/entities/storage_item_test.go
Normal file
2
pkg/core/entities/storage_item_test.go
Normal file
|
@ -0,0 +1,2 @@
|
|||
package entities
|
||||
|
|
@ -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
|
64
pkg/core/entities/validator_state_test.go
Normal file
64
pkg/core/entities/validator_state_test.go
Normal file
|
@ -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())
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
|
9
pkg/core/uint32.go
Normal file
9
pkg/core/uint32.go
Normal file
|
@ -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] }
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)),
|
||||
|
|
Loading…
Reference in a new issue