core/state: merge spent and unspent coins state, use it to store more things
This change reduces pressure on DB by doing the following things: * not storing additional KV pair for SpentCoin * storing Output right in the UnspentCoin, thus eliminating the need to get a full transaction from DB At the same time it makes UnspentCoin more fat and hot, but it should probably worth it. Also drop `GetUnspentCoinStateOrNew` as it shouldn't ever existed, UTXOs can't come out of nowhere. 1.5M block import time (VerifyBlocks disabled) on AMD Ryzen 5 1600/16GB/HDD, before: real 302m9.895s user 96m17.200s sys 13m37.084s after: real 159m16.551s user 69m58.279s sys 7m34.334s So it's almost two-fold which is a great improvement.
This commit is contained in:
parent
e1f194ea7b
commit
23464401bc
7 changed files with 117 additions and 278 deletions
|
@ -1,46 +0,0 @@
|
|||
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)
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestEncodeDecodeSpentCoin(t *testing.T) {
|
||||
spent := &SpentCoin{
|
||||
TxHeight: 1001,
|
||||
Items: map[uint16]uint32{
|
||||
1: 3,
|
||||
2: 8,
|
||||
4: 100,
|
||||
},
|
||||
}
|
||||
|
||||
buf := io.NewBufBinWriter()
|
||||
spent.EncodeBinary(buf.BinWriter)
|
||||
assert.Nil(t, buf.Err)
|
||||
spentDecode := new(SpentCoin)
|
||||
r := io.NewBinReaderFromBuf(buf.Bytes())
|
||||
spentDecode.DecodeBinary(r)
|
||||
assert.Nil(t, r.Err)
|
||||
assert.Equal(t, spent, spentDecode)
|
||||
}
|
|
@ -1,38 +1,60 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
)
|
||||
|
||||
// UnspentCoin hold the state of a unspent coin.
|
||||
type UnspentCoin struct {
|
||||
States []Coin
|
||||
Height uint32
|
||||
States []OutputState
|
||||
}
|
||||
|
||||
// OutputState combines transaction output (UTXO) and its state
|
||||
// (spent/claimed...) along with the height of spend (if it's spent).
|
||||
type OutputState struct {
|
||||
transaction.Output
|
||||
|
||||
SpendHeight uint32
|
||||
State Coin
|
||||
}
|
||||
|
||||
// NewUnspentCoin returns a new unspent coin state with N confirmed states.
|
||||
func NewUnspentCoin(n int) *UnspentCoin {
|
||||
func NewUnspentCoin(height uint32, tx *transaction.Transaction) *UnspentCoin {
|
||||
u := &UnspentCoin{
|
||||
States: make([]Coin, n),
|
||||
Height: height,
|
||||
States: make([]OutputState, len(tx.Outputs)),
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
u.States[i] = CoinConfirmed
|
||||
for i := range tx.Outputs {
|
||||
u.States[i] = OutputState{Output: tx.Outputs[i]}
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// EncodeBinary encodes UnspentCoin to the given BinWriter.
|
||||
func (s *UnspentCoin) EncodeBinary(bw *io.BinWriter) {
|
||||
bw.WriteU32LE(s.Height)
|
||||
bw.WriteArray(s.States)
|
||||
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())
|
||||
}
|
||||
s.Height = br.ReadU32LE()
|
||||
br.ReadArray(&s.States)
|
||||
}
|
||||
|
||||
// EncodeBinary implements Serializable interface.
|
||||
func (o *OutputState) EncodeBinary(w *io.BinWriter) {
|
||||
o.Output.EncodeBinary(w)
|
||||
w.WriteU32LE(o.SpendHeight)
|
||||
w.WriteB(byte(o.State))
|
||||
}
|
||||
|
||||
// DecodeBinary implements Serializable interface.
|
||||
func (o *OutputState) DecodeBinary(r *io.BinReader) {
|
||||
o.Output.DecodeBinary(r)
|
||||
o.SpendHeight = r.ReadU32LE()
|
||||
o.State = Coin(r.ReadB())
|
||||
}
|
||||
|
|
|
@ -3,18 +3,44 @@ package state
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDecodeEncodeUnspentCoin(t *testing.T) {
|
||||
unspent := &UnspentCoin{
|
||||
States: []Coin{
|
||||
CoinConfirmed,
|
||||
CoinSpent,
|
||||
CoinSpent,
|
||||
CoinSpent,
|
||||
CoinConfirmed,
|
||||
Height: 100500,
|
||||
States: []OutputState{
|
||||
{
|
||||
Output: transaction.Output{
|
||||
AssetID: random.Uint256(),
|
||||
Amount: util.Fixed8(42),
|
||||
ScriptHash: random.Uint160(),
|
||||
},
|
||||
SpendHeight: 201000,
|
||||
State: CoinSpent,
|
||||
},
|
||||
{
|
||||
Output: transaction.Output{
|
||||
AssetID: random.Uint256(),
|
||||
Amount: util.Fixed8(420),
|
||||
ScriptHash: random.Uint160(),
|
||||
},
|
||||
SpendHeight: 0,
|
||||
State: CoinConfirmed,
|
||||
},
|
||||
{
|
||||
Output: transaction.Output{
|
||||
AssetID: random.Uint256(),
|
||||
Amount: util.Fixed8(4200),
|
||||
ScriptHash: random.Uint160(),
|
||||
},
|
||||
SpendHeight: 111000,
|
||||
State: CoinSpent & CoinClaimed,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue