core: move (un)SpentCoin structs into the state package

As they're all about the state.
This commit is contained in:
Roman Khimov 2020-03-09 16:56:37 +03:00
parent 0e2bda4f21
commit 377fb382aa
13 changed files with 155 additions and 161 deletions

View file

@ -475,7 +475,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
return err return err
} }
if err := cache.PutUnspentCoinState(tx.Hash(), NewUnspentCoinState(len(tx.Outputs))); err != nil { if err := cache.PutUnspentCoinState(tx.Hash(), state.NewUnspentCoin(len(tx.Outputs))); err != nil {
return err return err
} }
@ -499,9 +499,9 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
if err != nil { if err != nil {
return err return err
} }
oldSpentCoinLen := len(spentCoin.items) oldSpentCoinLen := len(spentCoin.Items)
for _, input := range inputs { for _, input := range inputs {
unspent.states[input.PrevIndex] = state.CoinSpent unspent.States[input.PrevIndex] = state.CoinSpent
prevTXOutput := prevTX.Outputs[input.PrevIndex] prevTXOutput := prevTX.Outputs[input.PrevIndex]
account, err := cache.GetAccountStateOrNew(prevTXOutput.ScriptHash) account, err := cache.GetAccountStateOrNew(prevTXOutput.ScriptHash)
if err != nil { if err != nil {
@ -516,7 +516,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
End: block.Index, End: block.Index,
Value: prevTXOutput.Amount, Value: prevTXOutput.Amount,
}) })
spentCoin.items[input.PrevIndex] = block.Index spentCoin.Items[input.PrevIndex] = block.Index
if err = processTXWithValidatorsSubtract(&prevTXOutput, account, cache); err != nil { if err = processTXWithValidatorsSubtract(&prevTXOutput, account, cache); err != nil {
return err return err
} }
@ -548,7 +548,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
if err = cache.PutUnspentCoinState(prevHash, unspent); err != nil { if err = cache.PutUnspentCoinState(prevHash, unspent); err != nil {
return err return err
} }
if oldSpentCoinLen != len(spentCoin.items) { if oldSpentCoinLen != len(spentCoin.Items) {
if err = cache.PutSpentCoinState(prevHash, spentCoin); err != nil { if err = cache.PutSpentCoinState(prevHash, spentCoin); err != nil {
return err return err
} }
@ -590,7 +590,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
for _, input := range t.Claims { for _, input := range t.Claims {
scs, err := cache.GetSpentCoinState(input.PrevHash) scs, err := cache.GetSpentCoinState(input.PrevHash)
if err == nil { if err == nil {
_, ok := scs.items[input.PrevIndex] _, ok := scs.Items[input.PrevIndex]
if !ok { if !ok {
err = errors.New("no spent coin state") err = errors.New("no spent coin state")
} }
@ -644,8 +644,8 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
return err return err
} }
delete(scs.items, input.PrevIndex) delete(scs.Items, input.PrevIndex)
if len(scs.items) > 0 { if len(scs.Items) > 0 {
if err = cache.PutSpentCoinState(input.PrevHash, scs); err != nil { if err = cache.PutSpentCoinState(input.PrevHash, scs); err != nil {
return err return err
} }
@ -1174,7 +1174,7 @@ func (bc *Blockchain) GetAccountState(scriptHash util.Uint160) *state.Account {
} }
// GetUnspentCoinState returns unspent coin state for given tx hash. // GetUnspentCoinState returns unspent coin state for given tx hash.
func (bc *Blockchain) GetUnspentCoinState(hash util.Uint256) *UnspentCoinState { func (bc *Blockchain) GetUnspentCoinState(hash util.Uint256) *state.UnspentCoin {
ucs, err := bc.dao.GetUnspentCoinState(hash) ucs, err := bc.dao.GetUnspentCoinState(hash)
if ucs == nil && err != storage.ErrKeyNotFound { if ucs == nil && err != storage.ErrKeyNotFound {
bc.log.Warn("failed to get unspent coin state", zap.Error(err)) bc.log.Warn("failed to get unspent coin state", zap.Error(err))
@ -1472,7 +1472,7 @@ func (bc *Blockchain) getUnclaimed(h util.Uint256) (map[uint16]*spentCoin, error
} }
result := make(map[uint16]*spentCoin) result := make(map[uint16]*spentCoin)
for i, height := range scs.items { for i, height := range scs.Items {
result[i] = &spentCoin{ result[i] = &spentCoin{
Output: &tx.Outputs[i], Output: &tx.Outputs[i],
StartHeight: txHeight, StartHeight: txHeight,

View file

@ -41,7 +41,7 @@ type Blockchainer interface {
GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error)
GetTestVM() (*vm.VM, storage.Store) GetTestVM() (*vm.VM, storage.Store)
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error) GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
GetUnspentCoinState(util.Uint256) *UnspentCoinState GetUnspentCoinState(util.Uint256) *state.UnspentCoin
References(t *transaction.Transaction) ([]transaction.InOut, error) References(t *transaction.Transaction) ([]transaction.InOut, error)
mempool.Feer // fee interface mempool.Feer // fee interface
PoolTx(*transaction.Transaction) error PoolTx(*transaction.Transaction) error

View file

@ -178,22 +178,20 @@ func (dao *dao) AppendNEP5Transfer(acc util.Uint160, tr *state.NEP5Transfer) err
// GetUnspentCoinStateOrNew gets UnspentCoinState from temporary or persistent Store // GetUnspentCoinStateOrNew gets UnspentCoinState from temporary or persistent Store
// and return it. If it's not present in both stores, returns a new // and return it. If it's not present in both stores, returns a new
// UnspentCoinState. // UnspentCoinState.
func (dao *dao) GetUnspentCoinStateOrNew(hash util.Uint256) (*UnspentCoinState, error) { func (dao *dao) GetUnspentCoinStateOrNew(hash util.Uint256) (*state.UnspentCoin, error) {
unspent, err := dao.GetUnspentCoinState(hash) unspent, err := dao.GetUnspentCoinState(hash)
if err != nil { if err != nil {
if err != storage.ErrKeyNotFound { if err != storage.ErrKeyNotFound {
return nil, err return nil, err
} }
unspent = &UnspentCoinState{ unspent = state.NewUnspentCoin(0)
states: []state.Coin{},
}
} }
return unspent, nil return unspent, nil
} }
// GetUnspentCoinState retrieves UnspentCoinState from the given store. // GetUnspentCoinState retrieves UnspentCoinState from the given store.
func (dao *dao) GetUnspentCoinState(hash util.Uint256) (*UnspentCoinState, error) { func (dao *dao) GetUnspentCoinState(hash util.Uint256) (*state.UnspentCoin, error) {
unspent := &UnspentCoinState{} unspent := &state.UnspentCoin{}
key := storage.AppendPrefix(storage.STCoin, hash.BytesLE()) key := storage.AppendPrefix(storage.STCoin, hash.BytesLE())
err := dao.GetAndDecode(unspent, key) err := dao.GetAndDecode(unspent, key)
if err != nil { if err != nil {
@ -203,7 +201,7 @@ func (dao *dao) GetUnspentCoinState(hash util.Uint256) (*UnspentCoinState, error
} }
// PutUnspentCoinState puts given UnspentCoinState into the given store. // PutUnspentCoinState puts given UnspentCoinState into the given store.
func (dao *dao) PutUnspentCoinState(hash util.Uint256, ucs *UnspentCoinState) error { func (dao *dao) PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin) error {
key := storage.AppendPrefix(storage.STCoin, hash.BytesLE()) key := storage.AppendPrefix(storage.STCoin, hash.BytesLE())
return dao.Put(ucs, key) return dao.Put(ucs, key)
} }
@ -213,20 +211,20 @@ func (dao *dao) PutUnspentCoinState(hash util.Uint256, ucs *UnspentCoinState) er
// -- start spent coins. // -- start spent coins.
// GetSpentCoinsOrNew returns spent coins from store. // GetSpentCoinsOrNew returns spent coins from store.
func (dao *dao) GetSpentCoinsOrNew(hash util.Uint256, height uint32) (*SpentCoinState, error) { func (dao *dao) GetSpentCoinsOrNew(hash util.Uint256, height uint32) (*state.SpentCoin, error) {
spent, err := dao.GetSpentCoinState(hash) spent, err := dao.GetSpentCoinState(hash)
if err != nil { if err != nil {
if err != storage.ErrKeyNotFound { if err != storage.ErrKeyNotFound {
return nil, err return nil, err
} }
spent = NewSpentCoinState(height) spent = state.NewSpentCoin(height)
} }
return spent, nil return spent, nil
} }
// GetSpentCoinState gets SpentCoinState from the given store. // GetSpentCoinState gets SpentCoinState from the given store.
func (dao *dao) GetSpentCoinState(hash util.Uint256) (*SpentCoinState, error) { func (dao *dao) GetSpentCoinState(hash util.Uint256) (*state.SpentCoin, error) {
spent := &SpentCoinState{} spent := &state.SpentCoin{}
key := storage.AppendPrefix(storage.STSpentCoin, hash.BytesLE()) key := storage.AppendPrefix(storage.STSpentCoin, hash.BytesLE())
err := dao.GetAndDecode(spent, key) err := dao.GetAndDecode(spent, key)
if err != nil { if err != nil {
@ -236,7 +234,7 @@ func (dao *dao) GetSpentCoinState(hash util.Uint256) (*SpentCoinState, error) {
} }
// PutSpentCoinState puts given SpentCoinState into the given store. // PutSpentCoinState puts given SpentCoinState into the given store.
func (dao *dao) PutSpentCoinState(hash util.Uint256, scs *SpentCoinState) error { func (dao *dao) PutSpentCoinState(hash util.Uint256, scs *state.SpentCoin) error {
key := storage.AppendPrefix(storage.STSpentCoin, hash.BytesLE()) key := storage.AppendPrefix(storage.STSpentCoin, hash.BytesLE())
return dao.Put(scs, key) return dao.Put(scs, key)
} }
@ -592,7 +590,7 @@ func (dao *dao) IsDoubleSpend(tx *transaction.Transaction) bool {
return false return false
} }
for _, input := range inputs { for _, input := range inputs {
if int(input.PrevIndex) >= len(unspent.states) || unspent.states[input.PrevIndex] == state.CoinSpent { if int(input.PrevIndex) >= len(unspent.States) || unspent.States[input.PrevIndex] == state.CoinSpent {
return true return true
} }
} }
@ -612,7 +610,7 @@ func (dao *dao) IsDoubleClaim(claim *transaction.ClaimTX) bool {
return true return true
} }
for _, input := range inputs { for _, input := range inputs {
_, ok := scs.items[input.PrevIndex] _, ok := scs.Items[input.PrevIndex]
if !ok { if !ok {
return true return true
} }

View file

@ -113,7 +113,7 @@ func TestGetUnspentCoinState_Err(t *testing.T) {
func TestPutGetUnspentCoinState(t *testing.T) { func TestPutGetUnspentCoinState(t *testing.T) {
dao := newDao(storage.NewMemoryStore()) dao := newDao(storage.NewMemoryStore())
hash := random.Uint256() hash := random.Uint256()
unspentCoinState := &UnspentCoinState{states: []state.Coin{}} unspentCoinState := &state.UnspentCoin{States: []state.Coin{}}
err := dao.PutUnspentCoinState(hash, unspentCoinState) err := dao.PutUnspentCoinState(hash, unspentCoinState)
require.NoError(t, err) require.NoError(t, err)
gotUnspentCoinState, err := dao.GetUnspentCoinState(hash) gotUnspentCoinState, err := dao.GetUnspentCoinState(hash)
@ -132,7 +132,7 @@ func TestGetSpentCoinStateOrNew_New(t *testing.T) {
func TestPutAndGetSpentCoinState(t *testing.T) { func TestPutAndGetSpentCoinState(t *testing.T) {
dao := newDao(storage.NewMemoryStore()) dao := newDao(storage.NewMemoryStore())
hash := random.Uint256() hash := random.Uint256()
spentCoinState := &SpentCoinState{items: make(map[uint16]uint32)} spentCoinState := &state.SpentCoin{Items: make(map[uint16]uint32)}
err := dao.PutSpentCoinState(hash, spentCoinState) err := dao.PutSpentCoinState(hash, spentCoinState)
require.NoError(t, err) require.NoError(t, err)
gotSpentCoinState, err := dao.GetSpentCoinState(hash) gotSpentCoinState, err := dao.GetSpentCoinState(hash)
@ -151,7 +151,7 @@ func TestGetSpentCoinState_Err(t *testing.T) {
func TestDeleteSpentCoinState(t *testing.T) { func TestDeleteSpentCoinState(t *testing.T) {
dao := newDao(storage.NewMemoryStore()) dao := newDao(storage.NewMemoryStore())
hash := random.Uint256() hash := random.Uint256()
spentCoinState := &SpentCoinState{items: make(map[uint16]uint32)} spentCoinState := &state.SpentCoin{Items: make(map[uint16]uint32)}
err := dao.PutSpentCoinState(hash, spentCoinState) err := dao.PutSpentCoinState(hash, spentCoinState)
require.NoError(t, err) require.NoError(t, err)
err = dao.DeleteSpentCoinState(hash) err = dao.DeleteSpentCoinState(hash)

10
pkg/core/spent_coin.go Normal file
View file

@ -0,0 +1,10 @@
package core
import "github.com/nspcc-dev/neo-go/pkg/core/transaction"
// spentCoin represents the state of a single spent coin output.
type spentCoin struct {
Output *transaction.Output
StartHeight uint32
EndHeight uint32
}

View file

@ -1,56 +0,0 @@
package core
import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/io"
)
// SpentCoinState represents the state of a spent coin.
type SpentCoinState struct {
txHeight uint32
// A mapping between the index of the prevIndex and block height.
items map[uint16]uint32
}
// spentCoin represents the state of a single spent coin output.
type spentCoin struct {
Output *transaction.Output
StartHeight uint32
EndHeight uint32
}
// NewSpentCoinState returns a new SpentCoinState object.
func NewSpentCoinState(height uint32) *SpentCoinState {
return &SpentCoinState{
txHeight: height,
items: make(map[uint16]uint32),
}
}
// DecodeBinary implements Serializable interface.
func (s *SpentCoinState) DecodeBinary(br *io.BinReader) {
s.txHeight = br.ReadU32LE()
s.items = make(map[uint16]uint32)
lenItems := br.ReadVarUint()
for i := 0; i < int(lenItems); i++ {
var (
key uint16
value uint32
)
key = br.ReadU16LE()
value = br.ReadU32LE()
s.items[key] = value
}
}
// EncodeBinary implements Serializable interface.
func (s *SpentCoinState) EncodeBinary(bw *io.BinWriter) {
bw.WriteU32LE(s.txHeight)
bw.WriteVarUint(uint64(len(s.items)))
for k, v := range s.items {
bw.WriteU16LE(k)
bw.WriteU32LE(v)
}
}

View file

@ -0,0 +1,46 @@
package state
import "github.com/nspcc-dev/neo-go/pkg/io"
// SpentCoin represents the state of a spent coin.
type SpentCoin struct {
TxHeight uint32
// A mapping between the index of the prevIndex and block height.
Items map[uint16]uint32
}
// NewSpentCoin returns a new SpentCoin object.
func NewSpentCoin(height uint32) *SpentCoin {
return &SpentCoin{
TxHeight: height,
Items: make(map[uint16]uint32),
}
}
// DecodeBinary implements Serializable interface.
func (s *SpentCoin) DecodeBinary(br *io.BinReader) {
s.TxHeight = br.ReadU32LE()
s.Items = make(map[uint16]uint32)
lenItems := br.ReadVarUint()
for i := 0; i < int(lenItems); i++ {
var (
key uint16
value uint32
)
key = br.ReadU16LE()
value = br.ReadU32LE()
s.Items[key] = value
}
}
// EncodeBinary implements Serializable interface.
func (s *SpentCoin) EncodeBinary(bw *io.BinWriter) {
bw.WriteU32LE(s.TxHeight)
bw.WriteVarUint(uint64(len(s.Items)))
for k, v := range s.Items {
bw.WriteU16LE(k)
bw.WriteU32LE(v)
}
}

View file

@ -1,17 +1,16 @@
package core package state
import ( import (
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/internal/random"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestEncodeDecodeSpentCoinState(t *testing.T) { func TestEncodeDecodeSpentCoin(t *testing.T) {
spent := &SpentCoinState{ spent := &SpentCoin{
txHeight: 1001, TxHeight: 1001,
items: map[uint16]uint32{ Items: map[uint16]uint32{
1: 3, 1: 3,
2: 8, 2: 8,
4: 100, 4: 100,
@ -21,7 +20,7 @@ func TestEncodeDecodeSpentCoinState(t *testing.T) {
buf := io.NewBufBinWriter() buf := io.NewBufBinWriter()
spent.EncodeBinary(buf.BinWriter) spent.EncodeBinary(buf.BinWriter)
assert.Nil(t, buf.Err) assert.Nil(t, buf.Err)
spentDecode := new(SpentCoinState) spentDecode := new(SpentCoin)
r := io.NewBinReaderFromBuf(buf.Bytes()) r := io.NewBinReaderFromBuf(buf.Bytes())
spentDecode.DecodeBinary(r) spentDecode.DecodeBinary(r)
assert.Nil(t, r.Err) assert.Nil(t, r.Err)

View file

@ -0,0 +1,38 @@
package state
import (
"github.com/nspcc-dev/neo-go/pkg/io"
)
// UnspentCoin hold the state of a unspent coin.
type UnspentCoin struct {
States []Coin
}
// NewUnspentCoin returns a new unspent coin state with N confirmed states.
func NewUnspentCoin(n int) *UnspentCoin {
u := &UnspentCoin{
States: make([]Coin, n),
}
for i := 0; i < n; i++ {
u.States[i] = CoinConfirmed
}
return u
}
// EncodeBinary encodes UnspentCoin to the given BinWriter.
func (s *UnspentCoin) EncodeBinary(bw *io.BinWriter) {
bw.WriteVarUint(uint64(len(s.States)))
for _, state := range s.States {
bw.WriteB(byte(state))
}
}
// DecodeBinary decodes UnspentCoin from the given BinReader.
func (s *UnspentCoin) DecodeBinary(br *io.BinReader) {
lenStates := br.ReadVarUint()
s.States = make([]Coin, lenStates)
for i := 0; i < int(lenStates); i++ {
s.States[i] = Coin(br.ReadB())
}
}

View file

@ -0,0 +1,28 @@
package state
import (
"testing"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/stretchr/testify/assert"
)
func TestDecodeEncodeUnspentCoin(t *testing.T) {
unspent := &UnspentCoin{
States: []Coin{
CoinConfirmed,
CoinSpent,
CoinSpent,
CoinSpent,
CoinConfirmed,
},
}
buf := io.NewBufBinWriter()
unspent.EncodeBinary(buf.BinWriter)
assert.Nil(t, buf.Err)
unspentDecode := &UnspentCoin{}
r := io.NewBinReaderFromBuf(buf.Bytes())
unspentDecode.DecodeBinary(r)
assert.Nil(t, r.Err)
}

View file

@ -1,39 +0,0 @@
package core
import (
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/io"
)
// UnspentCoinState hold the state of a unspent coin.
type UnspentCoinState struct {
states []state.Coin
}
// NewUnspentCoinState returns a new unspent coin state with N confirmed states.
func NewUnspentCoinState(n int) *UnspentCoinState {
u := &UnspentCoinState{
states: make([]state.Coin, n),
}
for i := 0; i < n; i++ {
u.states[i] = state.CoinConfirmed
}
return u
}
// EncodeBinary encodes UnspentCoinState to the given BinWriter.
func (s *UnspentCoinState) EncodeBinary(bw *io.BinWriter) {
bw.WriteVarUint(uint64(len(s.states)))
for _, state := range s.states {
bw.WriteB(byte(state))
}
}
// DecodeBinary decodes UnspentCoinState from the given BinReader.
func (s *UnspentCoinState) DecodeBinary(br *io.BinReader) {
lenStates := br.ReadVarUint()
s.states = make([]state.Coin, lenStates)
for i := 0; i < int(lenStates); i++ {
s.states[i] = state.Coin(br.ReadB())
}
}

View file

@ -1,29 +0,0 @@
package core
import (
"testing"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/stretchr/testify/assert"
)
func TestDecodeEncodeUnspentCoinState(t *testing.T) {
unspent := &UnspentCoinState{
states: []state.Coin{
state.CoinConfirmed,
state.CoinSpent,
state.CoinSpent,
state.CoinSpent,
state.CoinConfirmed,
},
}
buf := io.NewBufBinWriter()
unspent.EncodeBinary(buf.BinWriter)
assert.Nil(t, buf.Err)
unspentDecode := &UnspentCoinState{}
r := io.NewBinReaderFromBuf(buf.Bytes())
unspentDecode.DecodeBinary(r)
assert.Nil(t, r.Err)
}

View file

@ -8,7 +8,6 @@ import (
"time" "time"
"github.com/nspcc-dev/neo-go/config" "github.com/nspcc-dev/neo-go/config"
"github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/mempool" "github.com/nspcc-dev/neo-go/pkg/core/mempool"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
@ -126,7 +125,7 @@ func (chain testChain) GetTransaction(util.Uint256) (*transaction.Transaction, u
panic("TODO") panic("TODO")
} }
func (chain testChain) GetUnspentCoinState(util.Uint256) *core.UnspentCoinState { func (chain testChain) GetUnspentCoinState(util.Uint256) *state.UnspentCoin {
panic("TODO") panic("TODO")
} }