Persisting more states (#71)
* added persistence of assets and spentcoins. * contract params * bumped version
This commit is contained in:
parent
7883f305e7
commit
2cdfee211a
11 changed files with 414 additions and 23 deletions
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
0.41.0
|
0.41.1
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
// Accounts is mapping between a account address and AccountState.
|
// Accounts is mapping between a account address and AccountState.
|
||||||
type Accounts map[util.Uint160]*AccountState
|
type Accounts map[util.Uint160]*AccountState
|
||||||
|
|
||||||
func (a Accounts) getAndChange(s storage.Store, hash util.Uint160) (*AccountState, error) {
|
func (a Accounts) getAndUpdate(s storage.Store, hash util.Uint160) (*AccountState, error) {
|
||||||
if account, ok := a[hash]; ok {
|
if account, ok := a[hash]; ok {
|
||||||
return account, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
131
pkg/core/asset_state.go
Normal file
131
pkg/core/asset_state.go
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/core/storage"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/crypto"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
const feeMode = 0x0
|
||||||
|
|
||||||
|
// Assets is mapping between AssetID and the AssetState.
|
||||||
|
type Assets map[util.Uint256]*AssetState
|
||||||
|
|
||||||
|
func (a Assets) commit(b storage.Batch) error {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
for hash, state := range a {
|
||||||
|
if err := state.EncodeBinary(buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
key := storage.AppendPrefix(storage.STAsset, hash.Bytes())
|
||||||
|
b.Put(key, buf.Bytes())
|
||||||
|
buf.Reset()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssetState represents the state of an NEO registerd Asset.
|
||||||
|
type AssetState struct {
|
||||||
|
ID util.Uint256
|
||||||
|
AssetType transaction.AssetType
|
||||||
|
Name string
|
||||||
|
Amount util.Fixed8
|
||||||
|
Available util.Fixed8
|
||||||
|
Precision uint8
|
||||||
|
FeeMode uint8
|
||||||
|
Owner *crypto.PublicKey
|
||||||
|
Admin util.Uint160
|
||||||
|
Issuer util.Uint160
|
||||||
|
Expiration uint32
|
||||||
|
IsFrozen bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinary implements the Payload interface.
|
||||||
|
func (a *AssetState) DecodeBinary(r io.Reader) error {
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &a.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &a.AssetType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
a.Name, err = util.ReadVarString(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &a.Amount); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &a.Available); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &a.Precision); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &a.FeeMode); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
a.Owner = &crypto.PublicKey{}
|
||||||
|
if err := a.Owner.DecodeBinary(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &a.Admin); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &a.Issuer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &a.Expiration); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return binary.Read(r, binary.LittleEndian, &a.IsFrozen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeBinary implements the Payload interface.
|
||||||
|
func (a *AssetState) EncodeBinary(w io.Writer) error {
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, a.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, a.AssetType); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := util.WriteVarUint(w, uint64(len(a.Name))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, []byte(a.Name)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, a.Amount); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, a.Available); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, a.Precision); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, a.FeeMode); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := a.Owner.EncodeBinary(w); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, a.Admin); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, a.Issuer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, a.Expiration); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return binary.Write(w, binary.LittleEndian, a.IsFrozen)
|
||||||
|
}
|
34
pkg/core/asset_state_test.go
Normal file
34
pkg/core/asset_state_test.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/crypto"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEncodeDecodeAssetState(t *testing.T) {
|
||||||
|
asset := &AssetState{
|
||||||
|
ID: util.RandomUint256(),
|
||||||
|
AssetType: transaction.Token,
|
||||||
|
Name: "super cool token",
|
||||||
|
Amount: util.Fixed8(1000000),
|
||||||
|
Available: util.Fixed8(100),
|
||||||
|
Precision: 0,
|
||||||
|
FeeMode: feeMode,
|
||||||
|
Owner: &crypto.PublicKey{},
|
||||||
|
Admin: util.RandomUint160(),
|
||||||
|
Issuer: util.RandomUint160(),
|
||||||
|
Expiration: 10,
|
||||||
|
IsFrozen: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
assert.Nil(t, asset.EncodeBinary(buf))
|
||||||
|
assetDecode := &AssetState{}
|
||||||
|
assert.Nil(t, assetDecode.DecodeBinary(buf))
|
||||||
|
assert.Equal(t, asset, assetDecode)
|
||||||
|
}
|
|
@ -264,7 +264,9 @@ func (bc *Blockchain) persistBlock(block *Block) error {
|
||||||
var (
|
var (
|
||||||
batch = bc.Batch()
|
batch = bc.Batch()
|
||||||
unspentCoins = make(UnspentCoins)
|
unspentCoins = make(UnspentCoins)
|
||||||
|
spentCoins = make(SpentCoins)
|
||||||
accounts = make(Accounts)
|
accounts = make(Accounts)
|
||||||
|
assets = make(Assets)
|
||||||
)
|
)
|
||||||
|
|
||||||
storeAsBlock(batch, block, 0)
|
storeAsBlock(batch, block, 0)
|
||||||
|
@ -272,21 +274,14 @@ func (bc *Blockchain) persistBlock(block *Block) error {
|
||||||
|
|
||||||
for _, tx := range block.Transactions {
|
for _, tx := range block.Transactions {
|
||||||
storeAsTransaction(batch, tx, block.Index)
|
storeAsTransaction(batch, tx, block.Index)
|
||||||
|
unspentCoins[tx.Hash()] = NewUnspentCoinState(len(tx.Outputs))
|
||||||
// Add CoinStateConfirmed for each tx output.
|
|
||||||
unspent := make([]CoinState, len(tx.Outputs))
|
|
||||||
for i := 0; i < len(tx.Outputs); i++ {
|
|
||||||
unspent[i] = CoinStateConfirmed
|
|
||||||
}
|
|
||||||
unspentCoins[tx.Hash()] = &UnspentCoinState{unspent}
|
|
||||||
|
|
||||||
// Process TX outputs.
|
// Process TX outputs.
|
||||||
for _, output := range tx.Outputs {
|
for _, output := range tx.Outputs {
|
||||||
account, err := accounts.getAndChange(bc.Store, output.ScriptHash)
|
account, err := accounts.getAndUpdate(bc.Store, output.ScriptHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := account.Balances[output.AssetID]; ok {
|
if _, ok := account.Balances[output.AssetID]; ok {
|
||||||
account.Balances[output.AssetID] += output.Amount
|
account.Balances[output.AssetID] += output.Amount
|
||||||
} else {
|
} else {
|
||||||
|
@ -296,37 +291,65 @@ func (bc *Blockchain) persistBlock(block *Block) error {
|
||||||
|
|
||||||
// Process TX inputs that are grouped by previous hash.
|
// Process TX inputs that are grouped by previous hash.
|
||||||
for prevHash, inputs := range tx.GroupInputsByPrevHash() {
|
for prevHash, inputs := range tx.GroupInputsByPrevHash() {
|
||||||
prevTX, _, err := bc.GetTransaction(prevHash)
|
prevTX, prevTXHeight, err := bc.GetTransaction(prevHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not find previous TX: %s", prevHash)
|
return fmt.Errorf("could not find previous TX: %s", prevHash)
|
||||||
}
|
}
|
||||||
for _, input := range inputs {
|
for _, input := range inputs {
|
||||||
unspent, err := unspentCoins.getAndChange(bc.Store, input.PrevHash)
|
unspent, err := unspentCoins.getAndUpdate(bc.Store, input.PrevHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
unspent.states[input.PrevIndex] = CoinStateSpent
|
unspent.states[input.PrevIndex] = CoinStateSpent
|
||||||
|
|
||||||
prevTXOutput := prevTX.Outputs[input.PrevIndex]
|
prevTXOutput := prevTX.Outputs[input.PrevIndex]
|
||||||
account, err := accounts.getAndChange(bc.Store, prevTXOutput.ScriptHash)
|
account, err := accounts.getAndUpdate(bc.Store, prevTXOutput.ScriptHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if prevTXOutput.AssetID.Equals(governingTokenTX().Hash()) {
|
||||||
|
spentCoin := NewSpentCoinState(input.PrevHash, prevTXHeight)
|
||||||
|
spentCoin.items[input.PrevIndex] = block.Index
|
||||||
|
spentCoins[input.PrevHash] = spentCoin
|
||||||
|
}
|
||||||
|
|
||||||
account.Balances[prevTXOutput.AssetID] -= prevTXOutput.Amount
|
account.Balances[prevTXOutput.AssetID] -= prevTXOutput.Amount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the underlying type of the TX.
|
// Process the underlying type of the TX.
|
||||||
switch tx.Data.(type) {
|
switch t := tx.Data.(type) {
|
||||||
case *transaction.RegisterTX:
|
case *transaction.RegisterTX:
|
||||||
|
assets[tx.Hash()] = &AssetState{
|
||||||
|
ID: tx.Hash(),
|
||||||
|
AssetType: t.AssetType,
|
||||||
|
Name: t.Name,
|
||||||
|
Amount: t.Amount,
|
||||||
|
Precision: t.Precision,
|
||||||
|
Owner: t.Owner,
|
||||||
|
Admin: t.Admin,
|
||||||
|
}
|
||||||
case *transaction.IssueTX:
|
case *transaction.IssueTX:
|
||||||
case *transaction.ClaimTX:
|
case *transaction.ClaimTX:
|
||||||
case *transaction.EnrollmentTX:
|
case *transaction.EnrollmentTX:
|
||||||
case *transaction.StateTX:
|
case *transaction.StateTX:
|
||||||
case *transaction.PublishTX:
|
case *transaction.PublishTX:
|
||||||
|
contract := &ContractState{
|
||||||
|
Script: t.Script,
|
||||||
|
ParamList: t.ParamList,
|
||||||
|
ReturnType: t.ReturnType,
|
||||||
|
HasStorage: t.NeedStorage,
|
||||||
|
Name: t.Name,
|
||||||
|
CodeVersion: t.CodeVersion,
|
||||||
|
Author: t.Author,
|
||||||
|
Email: t.Email,
|
||||||
|
Description: t.Description,
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%+v", contract)
|
||||||
|
|
||||||
case *transaction.InvocationTX:
|
case *transaction.InvocationTX:
|
||||||
log.Warn("invocation TX but we have no VM, o noo :(")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,6 +360,12 @@ func (bc *Blockchain) persistBlock(block *Block) error {
|
||||||
if err := unspentCoins.commit(batch); err != nil {
|
if err := unspentCoins.commit(batch); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := spentCoins.commit(batch); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := assets.commit(batch); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := bc.PutBatch(batch); err != nil {
|
if err := bc.PutBatch(batch); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
23
pkg/core/contract_state.go
Normal file
23
pkg/core/contract_state.go
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/smartcontract"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ContractState holds information about a smart contract in the NEO blockchain.
|
||||||
|
type ContractState struct {
|
||||||
|
Script []byte
|
||||||
|
ParamList []smartcontract.ParamType
|
||||||
|
ReturnType smartcontract.ParamType
|
||||||
|
Properties []int
|
||||||
|
Name string
|
||||||
|
CodeVersion string
|
||||||
|
Author string
|
||||||
|
Email string
|
||||||
|
Description string
|
||||||
|
HasStorage bool
|
||||||
|
HasDynamicInvoke bool
|
||||||
|
|
||||||
|
scriptHash util.Uint160
|
||||||
|
}
|
116
pkg/core/spent_coin_state.go
Normal file
116
pkg/core/spent_coin_state.go
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/core/storage"
|
||||||
|
"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.BytesReverse())
|
||||||
|
if b, err := store.Get(key); err == nil {
|
||||||
|
if err := spent.DecodeBinary(bytes.NewReader(b)); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode (UnspentCoinState): %s", err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
spent = &SpentCoinState{
|
||||||
|
items: make(map[uint16]uint32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s[hash] = spent
|
||||||
|
return spent, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SpentCoins) commit(b storage.Batch) error {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
for hash, state := range s {
|
||||||
|
if err := state.EncodeBinary(buf); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
key := storage.AppendPrefix(storage.STSpentCoin, hash.BytesReverse())
|
||||||
|
b.Put(key, buf.Bytes())
|
||||||
|
buf.Reset()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SpentCoinState represents the state of a spent coin.
|
||||||
|
type SpentCoinState struct {
|
||||||
|
txHash util.Uint256
|
||||||
|
txHeight uint32
|
||||||
|
|
||||||
|
// A mapping between the index of the prevIndex and block height.
|
||||||
|
items map[uint16]uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSpentCoinState returns a new SpentCoinState object.
|
||||||
|
func NewSpentCoinState(hash util.Uint256, height uint32) *SpentCoinState {
|
||||||
|
return &SpentCoinState{
|
||||||
|
txHash: hash,
|
||||||
|
txHeight: height,
|
||||||
|
items: make(map[uint16]uint32),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeBinary implements the Payload interface.
|
||||||
|
func (s *SpentCoinState) DecodeBinary(r io.Reader) error {
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &s.txHash); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &s.txHeight); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.items = make(map[uint16]uint32)
|
||||||
|
lenItems := util.ReadVarUint(r)
|
||||||
|
for i := 0; i < int(lenItems); i++ {
|
||||||
|
var (
|
||||||
|
key uint16
|
||||||
|
value uint32
|
||||||
|
)
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &key); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &value); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.items[key] = value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeBinary implements the Payload interface.
|
||||||
|
func (s *SpentCoinState) EncodeBinary(w io.Writer) error {
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, s.txHash); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, s.txHeight); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := util.WriteVarUint(w, uint64(len(s.items))); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for k, v := range s.items {
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, k); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := binary.Write(w, binary.LittleEndian, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
51
pkg/core/spent_coin_state_test.go
Normal file
51
pkg/core/spent_coin_state_test.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/core/storage"
|
||||||
|
"github.com/CityOfZion/neo-go/pkg/util"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEncodeDecodeSpentCoinState(t *testing.T) {
|
||||||
|
spent := &SpentCoinState{
|
||||||
|
txHash: util.RandomUint256(),
|
||||||
|
txHeight: 1001,
|
||||||
|
items: map[uint16]uint32{
|
||||||
|
1: 3,
|
||||||
|
2: 8,
|
||||||
|
4: 100,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
assert.Nil(t, spent.EncodeBinary(buf))
|
||||||
|
spentDecode := new(SpentCoinState)
|
||||||
|
assert.Nil(t, spentDecode.DecodeBinary(buf))
|
||||||
|
assert.Equal(t, spent, spentDecode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommitSpentCoins(t *testing.T) {
|
||||||
|
var (
|
||||||
|
store = storage.NewMemoryStore()
|
||||||
|
batch = store.Batch()
|
||||||
|
spentCoins = make(SpentCoins)
|
||||||
|
)
|
||||||
|
|
||||||
|
txx := []util.Uint256{
|
||||||
|
util.RandomUint256(),
|
||||||
|
util.RandomUint256(),
|
||||||
|
util.RandomUint256(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(txx); i++ {
|
||||||
|
spentCoins[txx[i]] = &SpentCoinState{
|
||||||
|
txHash: txx[i],
|
||||||
|
txHeight: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert.Nil(t, spentCoins.commit(batch))
|
||||||
|
assert.Nil(t, store.PutBatch(batch))
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ import (
|
||||||
// coin state.
|
// coin state.
|
||||||
type UnspentCoins map[util.Uint256]*UnspentCoinState
|
type UnspentCoins map[util.Uint256]*UnspentCoinState
|
||||||
|
|
||||||
func (u UnspentCoins) getAndChange(s storage.Store, hash util.Uint256) (*UnspentCoinState, error) {
|
func (u UnspentCoins) getAndUpdate(s storage.Store, hash util.Uint256) (*UnspentCoinState, error) {
|
||||||
if unspent, ok := u[hash]; ok {
|
if unspent, ok := u[hash]; ok {
|
||||||
return unspent, nil
|
return unspent, nil
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,17 @@ type UnspentCoinState struct {
|
||||||
states []CoinState
|
states []CoinState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewUnspentCoinState returns a new unspent coin state with N confirmed states.
|
||||||
|
func NewUnspentCoinState(n int) *UnspentCoinState {
|
||||||
|
u := &UnspentCoinState{
|
||||||
|
states: make([]CoinState, n),
|
||||||
|
}
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
u.states[i] = CoinStateConfirmed
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
// commit writes all unspent coin states to the given Batch.
|
// commit writes all unspent coin states to the given Batch.
|
||||||
func (s UnspentCoins) commit(b storage.Batch) error {
|
func (s UnspentCoins) commit(b storage.Batch) error {
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDecodeEncode(t *testing.T) {
|
func TestDecodeEncodeUnspentCoinState(t *testing.T) {
|
||||||
unspent := &UnspentCoinState{
|
unspent := &UnspentCoinState{
|
||||||
states: []CoinState{
|
states: []CoinState{
|
||||||
CoinStateConfirmed,
|
CoinStateConfirmed,
|
||||||
|
@ -26,7 +26,7 @@ func TestDecodeEncode(t *testing.T) {
|
||||||
assert.Nil(t, unspentDecode.DecodeBinary(buf))
|
assert.Nil(t, unspentDecode.DecodeBinary(buf))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCommit(t *testing.T) {
|
func TestCommitUnspentCoins(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
store = storage.NewMemoryStore()
|
store = storage.NewMemoryStore()
|
||||||
batch = store.Batch()
|
batch = store.Batch()
|
||||||
|
|
|
@ -96,10 +96,6 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*Block, error) {
|
||||||
|
|
||||||
func governingTokenTX() *transaction.Transaction {
|
func governingTokenTX() *transaction.Transaction {
|
||||||
admin, _ := util.Uint160FromScript([]byte{byte(vm.Opusht)})
|
admin, _ := util.Uint160FromScript([]byte{byte(vm.Opusht)})
|
||||||
adminB, _ := crypto.Uint160DecodeAddress("Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt")
|
|
||||||
if !admin.Equals(adminB) {
|
|
||||||
panic("kdjdkljfkdjfkdjf")
|
|
||||||
}
|
|
||||||
registerTX := &transaction.RegisterTX{
|
registerTX := &transaction.RegisterTX{
|
||||||
AssetType: transaction.GoverningToken,
|
AssetType: transaction.GoverningToken,
|
||||||
Name: "[{\"lang\":\"zh-CN\",\"name\":\"小蚁股\"},{\"lang\":\"en\",\"name\":\"AntShare\"}]",
|
Name: "[{\"lang\":\"zh-CN\",\"name\":\"小蚁股\"},{\"lang\":\"en\",\"name\":\"AntShare\"}]",
|
||||||
|
|
Loading…
Reference in a new issue