Merge pull request #894 from nspcc-dev/neo3/genesis/init_native_interops

core: init native interops in the genesis block
This commit is contained in:
Roman Khimov 2020-04-27 16:32:20 +03:00 committed by GitHub
commit de91418d45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 662 additions and 1357 deletions

View file

@ -24,7 +24,6 @@ ProtocolConfiguration:
- seed9.ngd.network:10333 - seed9.ngd.network:10333
- seed10.ngd.network:10333 - seed10.ngd.network:10333
SystemFee: SystemFee:
EnrollmentTransaction: 1000
IssueTransaction: 500 IssueTransaction: 500
RegisterTransaction: 10000 RegisterTransaction: 10000
VerifyBlocks: true VerifyBlocks: true

View file

@ -15,7 +15,6 @@ ProtocolConfiguration:
- 172.200.0.3:20335 - 172.200.0.3:20335
- 172.200.0.4:20336 - 172.200.0.4:20336
SystemFee: SystemFee:
EnrollmentTransaction: 1000
IssueTransaction: 500 IssueTransaction: 500
RegisterTransaction: 10000 RegisterTransaction: 10000
VerifyBlocks: true VerifyBlocks: true

View file

@ -15,7 +15,6 @@ ProtocolConfiguration:
- 172.200.0.3:20335 - 172.200.0.3:20335
- 172.200.0.4:20336 - 172.200.0.4:20336
SystemFee: SystemFee:
EnrollmentTransaction: 1000
IssueTransaction: 500 IssueTransaction: 500
RegisterTransaction: 10000 RegisterTransaction: 10000
VerifyBlocks: true VerifyBlocks: true

View file

@ -9,7 +9,6 @@ ProtocolConfiguration:
SeedList: SeedList:
- 172.200.0.1:20333 - 172.200.0.1:20333
SystemFee: SystemFee:
EnrollmentTransaction: 1000
IssueTransaction: 500 IssueTransaction: 500
RegisterTransaction: 10000 RegisterTransaction: 10000
VerifyBlocks: true VerifyBlocks: true

View file

@ -15,7 +15,6 @@ ProtocolConfiguration:
- 172.200.0.3:20335 - 172.200.0.3:20335
- 172.200.0.4:20336 - 172.200.0.4:20336
SystemFee: SystemFee:
EnrollmentTransaction: 1000
IssueTransaction: 500 IssueTransaction: 500
RegisterTransaction: 10000 RegisterTransaction: 10000
VerifyBlocks: true VerifyBlocks: true

View file

@ -15,7 +15,6 @@ ProtocolConfiguration:
- 172.200.0.3:20335 - 172.200.0.3:20335
- 172.200.0.4:20336 - 172.200.0.4:20336
SystemFee: SystemFee:
EnrollmentTransaction: 1000
IssueTransaction: 500 IssueTransaction: 500
RegisterTransaction: 10000 RegisterTransaction: 10000
VerifyBlocks: true VerifyBlocks: true

View file

@ -24,7 +24,6 @@ ProtocolConfiguration:
- seed9.ngd.network:20333 - seed9.ngd.network:20333
- seed10.ngd.network:20333 - seed10.ngd.network:20333
SystemFee: SystemFee:
EnrollmentTransaction: 10
IssueTransaction: 5 IssueTransaction: 5
RegisterTransaction: 100 RegisterTransaction: 100
VerifyBlocks: true VerifyBlocks: true

View file

@ -14,7 +14,6 @@ ProtocolConfiguration:
- 127.0.0.1:20335 - 127.0.0.1:20335
- 127.0.0.1:20336 - 127.0.0.1:20336
SystemFee: SystemFee:
EnrollmentTransaction: 1000
IssueTransaction: 500 IssueTransaction: 500
RegisterTransaction: 10000 RegisterTransaction: 10000
VerifyBlocks: true VerifyBlocks: true

View file

@ -47,7 +47,6 @@ type (
// SystemFee fees related to system. // SystemFee fees related to system.
SystemFee struct { SystemFee struct {
EnrollmentTransaction int64 `yaml:"EnrollmentTransaction"`
IssueTransaction int64 `yaml:"IssueTransaction"` IssueTransaction int64 `yaml:"IssueTransaction"`
RegisterTransaction int64 `yaml:"RegisterTransaction"` RegisterTransaction int64 `yaml:"RegisterTransaction"`
} }
@ -75,8 +74,6 @@ func (n NetMode) String() string {
// TryGetValue returns the system fee base on transaction type. // TryGetValue returns the system fee base on transaction type.
func (s SystemFee) TryGetValue(txType transaction.TXType) util.Fixed8 { func (s SystemFee) TryGetValue(txType transaction.TXType) util.Fixed8 {
switch txType { switch txType {
case transaction.EnrollmentType:
return util.Fixed8FromInt64(s.EnrollmentTransaction)
case transaction.IssueType: case transaction.IssueType:
return util.Fixed8FromInt64(s.IssueTransaction) return util.Fixed8FromInt64(s.IssueTransaction)
case transaction.RegisterType: case transaction.RegisterType:

View file

@ -479,22 +479,12 @@ func (s *service) getVerifiedTx(count int) []block.Transaction {
return res return res
} }
func (s *service) getValidators(txx ...block.Transaction) []crypto.PublicKey { func (s *service) getValidators(_ ...block.Transaction) []crypto.PublicKey {
var ( var (
pKeys []*keys.PublicKey pKeys []*keys.PublicKey
err error err error
) )
if len(txx) == 0 {
pKeys, err = s.Chain.GetValidators() pKeys, err = s.Chain.GetValidators()
} else {
ntxx := make([]*transaction.Transaction, len(txx))
for i := range ntxx {
ntxx[i] = txx[i].(*transaction.Transaction)
}
pKeys, err = s.Chain.GetValidators(ntxx...)
}
if err != nil { if err != nil {
s.log.Error("error while trying to get validators", zap.Error(err)) s.log.Error("error while trying to get validators", zap.Error(err))
} }

View file

@ -197,9 +197,6 @@ func (bc *Blockchain) init() error {
if err != nil { if err != nil {
return err return err
} }
if err := bc.initNative(); err != nil {
return err
}
return bc.storeBlock(genesisBlock) return bc.storeBlock(genesisBlock)
} }
if ver != version { if ver != version {
@ -272,27 +269,6 @@ func (bc *Blockchain) init() error {
return nil return nil
} }
func (bc *Blockchain) initNative() error {
ic := bc.newInteropContext(trigger.Application, bc.dao, nil, nil)
gas := native.NewGAS()
neo := native.NewNEO()
neo.GAS = gas
gas.NEO = neo
if err := gas.Initialize(ic); err != nil {
return fmt.Errorf("can't initialize GAS native contract: %v", err)
}
if err := neo.Initialize(ic); err != nil {
return fmt.Errorf("can't initialize NEO native contract: %v", err)
}
bc.contracts.SetGAS(gas)
bc.contracts.SetNEO(neo)
return nil
}
// Run runs chain loop. // Run runs chain loop.
func (bc *Blockchain) Run() { func (bc *Blockchain) Run() {
persistTimer := time.NewTimer(persistInterval) persistTimer := time.NewTimer(persistInterval)
@ -547,9 +523,6 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
if err != nil { if err != nil {
return err return err
} }
if err = processTXWithValidatorsSubtract(prevTXOutput, account, cache); err != nil {
return err
}
} }
balancesLen := len(account.Balances[prevTXOutput.AssetID]) balancesLen := len(account.Balances[prevTXOutput.AssetID])
@ -657,14 +630,6 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
return err return err
} }
} }
case *transaction.EnrollmentTX:
if err := processEnrollmentTX(cache, t); err != nil {
return err
}
case *transaction.StateTX:
if err := bc.processStateTX(cache, tx, t); err != nil {
return err
}
case *transaction.InvocationTX: case *transaction.InvocationTX:
systemInterop := bc.newInteropContext(trigger.Application, cache, block, tx) systemInterop := bc.newInteropContext(trigger.Application, cache, block, tx)
v := SpawnVM(systemInterop) v := SpawnVM(systemInterop)
@ -847,11 +812,6 @@ func (bc *Blockchain) LastBatch() *storage.MemBatch {
return bc.lastBatch return bc.lastBatch
} }
// RegisterNative registers native contract in the blockchain.
func (bc *Blockchain) RegisterNative(c native.Contract) {
bc.contracts.Add(c)
}
// processOutputs processes transaction outputs. // processOutputs processes transaction outputs.
func processOutputs(tx *transaction.Transaction, dao *dao.Cached) error { func processOutputs(tx *transaction.Transaction, dao *dao.Cached) error {
for index, output := range tx.Outputs { for index, output := range tx.Outputs {
@ -867,79 +827,6 @@ func processOutputs(tx *transaction.Transaction, dao *dao.Cached) error {
if err = dao.PutAccountState(account); err != nil { if err = dao.PutAccountState(account); err != nil {
return err return err
} }
if err = processTXWithValidatorsAdd(&output, account, dao); err != nil {
return err
}
}
return nil
}
func processTXWithValidatorsAdd(output *transaction.Output, account *state.Account, dao *dao.Cached) error {
if output.AssetID.Equals(GoverningTokenID()) && len(account.Votes) > 0 {
return modAccountVotes(account, dao, output.Amount)
}
return nil
}
func processTXWithValidatorsSubtract(output *transaction.Output, account *state.Account, dao *dao.Cached) error {
if output.AssetID.Equals(GoverningTokenID()) && len(account.Votes) > 0 {
return modAccountVotes(account, dao, -output.Amount)
}
return nil
}
// modAccountVotes adds given value to given account voted validators.
func modAccountVotes(account *state.Account, dao *dao.Cached, value util.Fixed8) error {
if err := native.ModifyAccountVotes(account, dao, value); err != nil {
return err
}
if len(account.Votes) > 0 {
vc, err := dao.GetValidatorsCount()
if err != nil {
return err
}
vc[len(account.Votes)-1] += value
err = dao.PutValidatorsCount(vc)
if err != nil {
return err
}
}
return nil
}
func processValidatorStateDescriptor(descriptor *transaction.StateDescriptor, dao *dao.Cached) error {
publicKey := &keys.PublicKey{}
err := publicKey.DecodeBytes(descriptor.Key)
if err != nil {
return err
}
validatorState, err := dao.GetValidatorStateOrNew(publicKey)
if err != nil {
return err
}
if descriptor.Field == "Registered" {
if len(descriptor.Value) == 1 {
validatorState.Registered = descriptor.Value[0] != 0
return dao.PutValidatorState(validatorState)
}
return errors.New("bad descriptor value")
}
return nil
}
func (bc *Blockchain) processAccountStateDescriptor(descriptor *transaction.StateDescriptor, t *transaction.Transaction, dao *dao.Cached) error {
hash, err := util.Uint160DecodeBytesBE(descriptor.Key)
if err != nil {
return err
}
if descriptor.Field == "Votes" {
votes := keys.PublicKeys{}
if err := votes.DecodeBytes(descriptor.Value); err != nil {
return err
}
ic := bc.newInteropContext(trigger.Application, dao, nil, t)
return bc.contracts.NEO.VoteInternal(ic, hash, votes)
} }
return nil return nil
} }
@ -1364,63 +1251,6 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e
if inv.Gas.FractionalValue() != 0 { if inv.Gas.FractionalValue() != 0 {
return errors.New("invocation gas can only be integer") return errors.New("invocation gas can only be integer")
} }
case transaction.StateType:
stx := t.Data.(*transaction.StateTX)
for _, desc := range stx.Descriptors {
switch desc.Type {
case transaction.Account:
if desc.Field != "Votes" {
return errors.New("bad field in account descriptor")
}
votes := keys.PublicKeys{}
err := votes.DecodeBytes(desc.Value)
if err != nil {
return err
}
if len(votes) > state.MaxValidatorsVoted {
return errors.New("voting candidate limit exceeded")
}
hash, err := util.Uint160DecodeBytesBE(desc.Key)
if err != nil {
return err
}
account, err := bc.dao.GetAccountStateOrNew(hash)
if err != nil {
return err
}
if account.IsFrozen {
return errors.New("account is frozen")
}
if votes.Len() > 0 {
balance := account.GetBalanceValues()[GoverningTokenID()]
if balance == 0 {
return errors.New("no governing tokens available to vote")
}
validators, err := bc.GetEnrollments()
if err != nil {
return err
}
for _, k := range votes {
var isRegistered bool
for i := range validators {
if k.Equal(validators[i].PublicKey) {
isRegistered = true
break
}
}
if !isRegistered {
return errors.New("vote for unregistered validator")
}
}
}
case transaction.Validator:
if desc.Field != "Registered" {
return errors.New("bad field in validator descriptor")
}
default:
return errors.New("bad descriptor type")
}
}
} }
return bc.verifyTxWitnesses(t, block) return bc.verifyTxWitnesses(t, block)
@ -1699,135 +1529,14 @@ func (bc *Blockchain) GetStandByValidators() (keys.PublicKeys, error) {
return getValidators(bc.config) return getValidators(bc.config)
} }
// GetValidators returns validators. // GetValidators returns next block 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() ([]*keys.PublicKey, error) {
func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.PublicKey, error) { return bc.contracts.NEO.GetNextBlockValidatorsInternal(bc, bc.dao)
cache := dao.NewCached(bc.dao)
if len(txes) > 0 {
for _, tx := range txes {
// iterate through outputs
for index, output := range tx.Outputs {
accountState, err := cache.GetAccountStateOrNew(output.ScriptHash)
if err != nil {
return nil, err
}
accountState.Balances[output.AssetID] = append(accountState.Balances[output.AssetID], state.UnspentBalance{
Tx: tx.Hash(),
Index: uint16(index),
Value: output.Amount,
})
if err := cache.PutAccountState(accountState); err != nil {
return nil, err
}
if err = processTXWithValidatorsAdd(&output, accountState, cache); err != nil {
return nil, err
}
} }
// group inputs by the same previous hash and iterate through inputs // GetEnrollments returns all registered validators.
group := make(map[util.Uint256][]*transaction.Input) func (bc *Blockchain) GetEnrollments() ([]state.Validator, error) {
for i := range tx.Inputs { return bc.contracts.NEO.GetRegisteredValidators(bc.dao)
hash := tx.Inputs[i].PrevHash
group[hash] = append(group[hash], &tx.Inputs[i])
}
for hash, inputs := range group {
unspent, err := cache.GetUnspentCoinState(hash)
if err != nil {
return nil, err
}
// process inputs
for _, input := range inputs {
prevOutput := &unspent.States[input.PrevIndex].Output
accountState, err := cache.GetAccountStateOrNew(prevOutput.ScriptHash)
if err != nil {
return nil, err
}
// process account state votes: if there are any -> validators will be updated.
if err = processTXWithValidatorsSubtract(prevOutput, accountState, cache); 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(cache, t); err != nil {
return nil, err
}
case *transaction.StateTX:
if err := bc.processStateTX(cache, tx, t); err != nil {
return nil, err
}
}
}
}
return bc.contracts.NEO.GetValidatorsInternal(bc, cache)
}
// GetEnrollments returns all registered validators and non-registered SB validators
func (bc *Blockchain) GetEnrollments() ([]*state.Validator, error) {
validators := bc.dao.GetValidators()
standByValidators, err := bc.GetStandByValidators()
if err != nil {
return nil, err
}
uniqueSBValidators := standByValidators.Unique()
var result []*state.Validator
for _, validator := range validators {
if validator.Registered {
result = append(result, validator)
}
}
for _, sBValidator := range uniqueSBValidators {
isAdded := false
for _, v := range result {
if v.PublicKey == sBValidator {
isAdded = true
break
}
}
if !isAdded {
result = append(result, &state.Validator{
PublicKey: sBValidator,
Registered: false,
Votes: 0,
})
}
}
return result, nil
}
func (bc *Blockchain) processStateTX(dao *dao.Cached, t *transaction.Transaction, tx *transaction.StateTX) error {
for _, desc := range tx.Descriptors {
switch desc.Type {
case transaction.Account:
if err := bc.processAccountStateDescriptor(desc, t, dao); err != nil {
return err
}
case transaction.Validator:
if err := processValidatorStateDescriptor(desc, dao); err != nil {
return err
}
}
}
return nil
}
func processEnrollmentTX(dao *dao.Cached, tx *transaction.EnrollmentTX) error {
validatorState, err := dao.GetValidatorStateOrNew(&tx.PublicKey)
if err != nil {
return err
}
validatorState.Registered = true
return dao.PutValidatorState(validatorState)
} }
// GetScriptHashesForVerifying returns all the ScriptHashes of a transaction which will be use // GetScriptHashesForVerifying returns all the ScriptHashes of a transaction which will be use
@ -1879,9 +1588,6 @@ func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([
for i := range refs { for i := range refs {
hashes[refs[i].Out.ScriptHash] = true hashes[refs[i].Out.ScriptHash] = true
} }
case transaction.EnrollmentType:
etx := t.Data.(*transaction.EnrollmentTX)
hashes[etx.PublicKey.GetScriptHash()] = true
case transaction.IssueType: case transaction.IssueType:
for _, res := range refsAndOutsToResults(references, t.Outputs) { for _, res := range refsAndOutsToResults(references, t.Outputs) {
if res.Amount < 0 { if res.Amount < 0 {
@ -1895,31 +1601,6 @@ func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([
case transaction.RegisterType: case transaction.RegisterType:
reg := t.Data.(*transaction.RegisterTX) reg := t.Data.(*transaction.RegisterTX)
hashes[reg.Owner.GetScriptHash()] = true hashes[reg.Owner.GetScriptHash()] = true
case transaction.StateType:
stx := t.Data.(*transaction.StateTX)
for _, desc := range stx.Descriptors {
switch desc.Type {
case transaction.Account:
if desc.Field != "Votes" {
return nil, errors.New("bad account state descriptor")
}
hash, err := util.Uint160DecodeBytesBE(desc.Key)
if err != nil {
return nil, err
}
hashes[hash] = true
case transaction.Validator:
if desc.Field != "Registered" {
return nil, errors.New("bad validator state descriptor")
}
key := &keys.PublicKey{}
err := key.DecodeBytes(desc.Key)
if err != nil {
return nil, err
}
hashes[key.GetScriptHash()] = true
}
}
} }
// convert hashes to []util.Uint160 // convert hashes to []util.Uint160
hashesResult := make([]util.Uint160, 0, len(hashes)) hashesResult := make([]util.Uint160, 0, len(hashes))
@ -2057,7 +1738,7 @@ func (bc *Blockchain) secondsPerBlock() int {
} }
func (bc *Blockchain) newInteropContext(trigger trigger.Type, d dao.DAO, block *block.Block, tx *transaction.Transaction) *interop.Context { func (bc *Blockchain) newInteropContext(trigger trigger.Type, d dao.DAO, block *block.Block, tx *transaction.Transaction) *interop.Context {
ic := interop.NewContext(trigger, bc, d, block, tx, bc.log) ic := interop.NewContext(trigger, bc, d, bc.contracts.Contracts, block, tx, bc.log)
switch { switch {
case tx != nil: case tx != nil:
ic.Container = tx ic.Container = tx

View file

@ -24,7 +24,7 @@ type Blockchainer interface {
HeaderHeight() uint32 HeaderHeight() uint32
GetBlock(hash util.Uint256) (*block.Block, error) GetBlock(hash util.Uint256) (*block.Block, error)
GetContractState(hash util.Uint160) *state.Contract GetContractState(hash util.Uint160) *state.Contract
GetEnrollments() ([]*state.Validator, error) GetEnrollments() ([]state.Validator, error)
GetHeaderHash(int) util.Uint256 GetHeaderHash(int) util.Uint256
GetHeader(hash util.Uint256) (*block.Header, error) GetHeader(hash util.Uint256) (*block.Header, error)
CurrentHeaderHash() util.Uint256 CurrentHeaderHash() util.Uint256
@ -36,7 +36,7 @@ type Blockchainer interface {
GetAppExecResult(util.Uint256) (*state.AppExecResult, error) GetAppExecResult(util.Uint256) (*state.AppExecResult, error)
GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog
GetNEP5Balances(util.Uint160) *state.NEP5Balances GetNEP5Balances(util.Uint160) *state.NEP5Balances
GetValidators(txes ...*transaction.Transaction) ([]*keys.PublicKey, error) GetValidators() ([]*keys.PublicKey, error)
GetStandByValidators() (keys.PublicKeys, error) GetStandByValidators() (keys.PublicKeys, error)
GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error)
GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem

View file

@ -10,7 +10,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
) )
@ -20,7 +19,6 @@ type DAO interface {
AppendNEP5Transfer(acc util.Uint160, index uint32, tr *state.NEP5Transfer) (bool, error) AppendNEP5Transfer(acc util.Uint160, index uint32, tr *state.NEP5Transfer) (bool, error)
DeleteContractState(hash util.Uint160) error DeleteContractState(hash util.Uint160) error
DeleteStorageItem(scripthash util.Uint160, key []byte) error DeleteStorageItem(scripthash util.Uint160, key []byte) error
DeleteValidatorState(vs *state.Validator) error
GetAccountState(hash util.Uint160) (*state.Account, error) GetAccountState(hash util.Uint160) (*state.Account, error)
GetAccountStateOrNew(hash util.Uint160) (*state.Account, error) GetAccountStateOrNew(hash util.Uint160) (*state.Account, error)
GetAndDecode(entity io.Serializable, key []byte) error GetAndDecode(entity io.Serializable, key []byte) error
@ -32,18 +30,13 @@ type DAO interface {
GetCurrentBlockHeight() (uint32, error) GetCurrentBlockHeight() (uint32, error)
GetCurrentHeaderHeight() (i uint32, h util.Uint256, err error) GetCurrentHeaderHeight() (i uint32, h util.Uint256, err error)
GetHeaderHashes() ([]util.Uint256, error) GetHeaderHashes() ([]util.Uint256, error)
GetNativeContractState(h util.Uint160) ([]byte, error)
GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error) GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error)
GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error) GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error)
GetNextBlockValidators() (keys.PublicKeys, error)
GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem
GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error)
GetStorageItemsWithPrefix(hash util.Uint160, prefix []byte) (map[string]*state.StorageItem, error)
GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error) GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error)
GetUnspentCoinState(hash util.Uint256) (*state.UnspentCoin, error) GetUnspentCoinState(hash util.Uint256) (*state.UnspentCoin, error)
GetValidatorState(publicKey *keys.PublicKey) (*state.Validator, error)
GetValidatorStateOrNew(publicKey *keys.PublicKey) (*state.Validator, error)
GetValidators() []*state.Validator
GetValidatorsCount() (*state.ValidatorsCount, error)
GetVersion() (string, error) GetVersion() (string, error)
GetWrapped() DAO GetWrapped() DAO
HasTransaction(hash util.Uint256) bool HasTransaction(hash util.Uint256) bool
@ -55,14 +48,10 @@ type DAO interface {
PutAssetState(as *state.Asset) error PutAssetState(as *state.Asset) error
PutContractState(cs *state.Contract) error PutContractState(cs *state.Contract) error
PutCurrentHeader(hashAndIndex []byte) error PutCurrentHeader(hashAndIndex []byte) error
PutNativeContractState(h util.Uint160, value []byte) error
PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error
PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error
PutNextBlockValidators(keys.PublicKeys) error
PutStorageItem(scripthash util.Uint160, key []byte, si *state.StorageItem) error PutStorageItem(scripthash util.Uint160, key []byte, si *state.StorageItem) error
PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin) error PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin) error
PutValidatorState(vs *state.Validator) error
PutValidatorsCount(vc *state.ValidatorsCount) error
PutVersion(v string) error PutVersion(v string) error
StoreAsBlock(block *block.Block, sysFee uint32) error StoreAsBlock(block *block.Block, sysFee uint32) error
StoreAsCurrentBlock(block *block.Block) error StoreAsCurrentBlock(block *block.Block) error
@ -211,18 +200,6 @@ func (dao *Simple) DeleteContractState(hash util.Uint160) error {
return dao.Store.Delete(key) return dao.Store.Delete(key)
} }
// GetNativeContractState retrieves native contract state from the store.
func (dao *Simple) GetNativeContractState(h util.Uint160) ([]byte, error) {
key := storage.AppendPrefix(storage.STNativeContract, h.BytesBE())
return dao.Store.Get(key)
}
// PutNativeContractState puts native contract state into the store.
func (dao *Simple) PutNativeContractState(h util.Uint160, value []byte) error {
key := storage.AppendPrefix(storage.STNativeContract, h.BytesBE())
return dao.Store.Put(key, value)
}
// -- end contracts. // -- end contracts.
// -- start nep5 balances. // -- start nep5 balances.
@ -324,111 +301,6 @@ func (dao *Simple) putUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin
// -- end unspent coins. // -- end unspent coins.
// -- start validator.
// GetNextBlockValidators retrieves next block validators from store or nil if they are missing.
func (dao *Simple) GetNextBlockValidators() (keys.PublicKeys, error) {
key := []byte{byte(storage.STNextValidators)}
buf, err := dao.Store.Get(key)
if err != nil {
if err == storage.ErrKeyNotFound {
return nil, nil
}
return nil, err
}
var pubs keys.PublicKeys
r := io.NewBinReaderFromBuf(buf)
r.ReadArray(&pubs)
if r.Err != nil {
return nil, r.Err
}
return pubs, nil
}
// PutNextBlockValidators puts next block validators to store.
func (dao *Simple) PutNextBlockValidators(pubs keys.PublicKeys) error {
w := io.NewBufBinWriter()
w.WriteArray(pubs)
if w.Err != nil {
return w.Err
}
key := []byte{byte(storage.STNextValidators)}
return dao.Store.Put(key, w.Bytes())
}
// GetValidatorStateOrNew gets validator from store or created new one in case of error.
func (dao *Simple) GetValidatorStateOrNew(publicKey *keys.PublicKey) (*state.Validator, error) {
validatorState, err := dao.GetValidatorState(publicKey)
if err != nil {
if err != storage.ErrKeyNotFound {
return nil, err
}
validatorState = &state.Validator{PublicKey: publicKey}
}
return validatorState, nil
}
// GetValidators returns all validators from store.
func (dao *Simple) GetValidators() []*state.Validator {
var validators []*state.Validator
dao.Store.Seek(storage.STValidator.Bytes(), func(k, v []byte) {
r := io.NewBinReaderFromBuf(v)
validator := &state.Validator{}
validator.DecodeBinary(r)
if r.Err != nil {
return
}
validators = append(validators, validator)
})
return validators
}
// GetValidatorState returns validator by publicKey.
func (dao *Simple) GetValidatorState(publicKey *keys.PublicKey) (*state.Validator, error) {
validatorState := &state.Validator{}
key := storage.AppendPrefix(storage.STValidator, publicKey.Bytes())
err := dao.GetAndDecode(validatorState, key)
if err != nil {
return nil, err
}
return validatorState, nil
}
// PutValidatorState puts given Validator into the given store.
func (dao *Simple) PutValidatorState(vs *state.Validator) error {
key := storage.AppendPrefix(storage.STValidator, vs.PublicKey.Bytes())
return dao.Put(vs, key)
}
// DeleteValidatorState deletes given Validator into the given store.
func (dao *Simple) DeleteValidatorState(vs *state.Validator) error {
key := storage.AppendPrefix(storage.STValidator, vs.PublicKey.Bytes())
return dao.Store.Delete(key)
}
// GetValidatorsCount returns current ValidatorsCount or new one if there is none
// in the DB.
func (dao *Simple) GetValidatorsCount() (*state.ValidatorsCount, error) {
vc := &state.ValidatorsCount{}
key := []byte{byte(storage.IXValidatorsCount)}
err := dao.GetAndDecode(vc, key)
if err != nil && err != storage.ErrKeyNotFound {
return nil, err
}
return vc, nil
}
// PutValidatorsCount put given ValidatorsCount in the store.
func (dao *Simple) PutValidatorsCount(vc *state.ValidatorsCount) error {
key := []byte{byte(storage.IXValidatorsCount)}
return dao.Put(vc, key)
}
// -- end validator.
// -- start notification event. // -- start notification event.
// GetAppExecResult gets application execution result from the // GetAppExecResult gets application execution result from the
@ -485,9 +357,19 @@ func (dao *Simple) DeleteStorageItem(scripthash util.Uint160, key []byte) error
// GetStorageItems returns all storage items for a given scripthash. // GetStorageItems returns all storage items for a given scripthash.
func (dao *Simple) GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) { func (dao *Simple) GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) {
return dao.GetStorageItemsWithPrefix(hash, nil)
}
// GetStorageItemsWithPrefix returns all storage items with given prefix for a
// given scripthash.
func (dao *Simple) GetStorageItemsWithPrefix(hash util.Uint160, prefix []byte) (map[string]*state.StorageItem, error) {
var siMap = make(map[string]*state.StorageItem) var siMap = make(map[string]*state.StorageItem)
var err error var err error
lookupKey := storage.AppendPrefix(storage.STStorage, hash.BytesLE())
if prefix != nil {
lookupKey = append(lookupKey, prefix...)
}
saveToMap := func(k, v []byte) { saveToMap := func(k, v []byte) {
if err != nil { if err != nil {
return return
@ -501,9 +383,9 @@ func (dao *Simple) GetStorageItems(hash util.Uint160) (map[string]*state.Storage
} }
// Cut prefix and hash. // Cut prefix and hash.
siMap[string(k[21:])] = si siMap[string(k[len(lookupKey):])] = si
} }
dao.Store.Seek(storage.AppendPrefix(storage.STStorage, hash.BytesLE()), saveToMap) dao.Store.Seek(lookupKey, saveToMap)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -113,61 +113,6 @@ func TestPutGetUnspentCoinState(t *testing.T) {
require.Equal(t, unspentCoinState, gotUnspentCoinState) require.Equal(t, unspentCoinState, gotUnspentCoinState)
} }
func TestGetValidatorStateOrNew_New(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore())
publicKey := &keys.PublicKey{}
validatorState, err := dao.GetValidatorStateOrNew(publicKey)
require.NoError(t, err)
require.NotNil(t, validatorState)
}
func TestPutGetValidatorState(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore())
publicKey := &keys.PublicKey{}
validatorState := &state.Validator{
PublicKey: publicKey,
Registered: false,
Votes: 0,
}
err := dao.PutValidatorState(validatorState)
require.NoError(t, err)
gotValidatorState, err := dao.GetValidatorState(publicKey)
require.NoError(t, err)
require.Equal(t, validatorState, gotValidatorState)
}
func TestDeleteValidatorState(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore())
publicKey := &keys.PublicKey{}
validatorState := &state.Validator{
PublicKey: publicKey,
Registered: false,
Votes: 0,
}
err := dao.PutValidatorState(validatorState)
require.NoError(t, err)
err = dao.DeleteValidatorState(validatorState)
require.NoError(t, err)
gotValidatorState, err := dao.GetValidatorState(publicKey)
require.Error(t, err)
require.Nil(t, gotValidatorState)
}
func TestGetValidators(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore())
publicKey := &keys.PublicKey{}
validatorState := &state.Validator{
PublicKey: publicKey,
Registered: false,
Votes: 0,
}
err := dao.PutValidatorState(validatorState)
require.NoError(t, err)
validators := dao.GetValidators()
require.Equal(t, validatorState, validators[0])
require.Len(t, validators, 1)
}
func TestPutGetAppExecResult(t *testing.T) { func TestPutGetAppExecResult(t *testing.T) {
dao := NewSimple(storage.NewMemoryStore()) dao := NewSimple(storage.NewMemoryStore())
hash := random.Uint256() hash := random.Uint256()

View file

@ -195,7 +195,7 @@ func TestCreateBasicChain(t *testing.T) {
// use output of issue tx from genesis block as an input // use output of issue tx from genesis block as an input
genesisBlock, err := bc.GetBlock(bc.GetHeaderHash(0)) genesisBlock, err := bc.GetBlock(bc.GetHeaderHash(0))
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, 4, len(genesisBlock.Transactions)) require.Equal(t, 5, len(genesisBlock.Transactions))
h := genesisBlock.Transactions[3].Hash() h := genesisBlock.Transactions[3].Hash()
txMoveNeo.AddInput(&transaction.Input{ txMoveNeo.AddInput(&transaction.Input{
PrevHash: h, PrevHash: h,

View file

@ -7,8 +7,14 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto" "github.com/nspcc-dev/neo-go/pkg/crypto"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -16,6 +22,7 @@ import (
type Context struct { type Context struct {
Chain blockchainer.Blockchainer Chain blockchainer.Blockchainer
Container crypto.Verifiable Container crypto.Verifiable
Natives []Contract
Trigger trigger.Type Trigger trigger.Type
Block *block.Block Block *block.Block
Tx *transaction.Transaction Tx *transaction.Transaction
@ -25,11 +32,12 @@ type Context struct {
} }
// NewContext returns new interop context. // NewContext returns new interop context.
func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO, block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context { func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO, natives []Contract, block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context {
dao := dao.NewCached(d) dao := dao.NewCached(d)
nes := make([]state.NotificationEvent, 0) nes := make([]state.NotificationEvent, 0)
return &Context{ return &Context{
Chain: bc, Chain: bc,
Natives: natives,
Trigger: trigger, Trigger: trigger,
Block: block, Block: block,
Tx: tx, Tx: tx,
@ -48,3 +56,64 @@ type Function struct {
Func func(*Context, *vm.VM) error Func func(*Context, *vm.VM) error
Price int Price int
} }
// Method is a signature for a native method.
type Method = func(ic *Context, args []vm.StackItem) vm.StackItem
// MethodAndPrice is a native-contract method descriptor.
type MethodAndPrice struct {
Func Method
Price int64
RequiredFlags smartcontract.CallFlag
}
// Contract is an interface for all native contracts.
type Contract interface {
Initialize(*Context) error
Metadata() *ContractMD
OnPersist(*Context) error
}
// ContractMD represents native contract instance.
type ContractMD struct {
Manifest manifest.Manifest
ServiceName string
ServiceID uint32
Script []byte
Hash util.Uint160
Methods map[string]MethodAndPrice
}
// NewContractMD returns Contract with the specified list of methods.
func NewContractMD(name string) *ContractMD {
c := &ContractMD{
ServiceName: name,
ServiceID: emit.InteropNameToID([]byte(name)),
Methods: make(map[string]MethodAndPrice),
}
w := io.NewBufBinWriter()
emit.Syscall(w.BinWriter, c.ServiceName)
c.Script = w.Bytes()
c.Hash = hash.Hash160(c.Script)
c.Manifest = *manifest.DefaultManifest(c.Hash)
return c
}
// AddMethod adds new method to a native contract.
func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method, safe bool) {
c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, *desc)
c.Methods[desc.Name] = *md
if safe {
c.Manifest.SafeMethods.Add(desc.Name)
}
}
// AddEvent adds new event to a native contract.
func (c *ContractMD) AddEvent(name string, ps ...manifest.Parameter) {
c.Manifest.ABI.Events = append(c.Manifest.ABI.Events, manifest.Event{
Name: name,
Parameters: ps,
})
}

View file

@ -236,13 +236,6 @@ func witnessGetVerificationScript(ic *interop.Context, v *vm.VM) error {
return nil return nil
} }
// bcGetValidators returns validators.
func bcGetValidators(ic *interop.Context, v *vm.VM) error {
validators := ic.DAO.GetValidators()
v.Estack().PushVal(validators)
return nil
}
// popInputFromVM returns transaction.Input from the first estack element. // popInputFromVM returns transaction.Input from the first estack element.
func popInputFromVM(v *vm.VM) (*transaction.Input, error) { func popInputFromVM(v *vm.VM) (*transaction.Input, error) {
inInterface := v.Estack().Pop().Value() inInterface := v.Estack().Pop().Value()
@ -404,24 +397,6 @@ func accountGetScriptHash(ic *interop.Context, v *vm.VM) error {
return nil return nil
} }
// accountGetVotes returns votes of a given account.
func accountGetVotes(ic *interop.Context, v *vm.VM) error {
accInterface := v.Estack().Pop().Value()
acc, ok := accInterface.(*state.Account)
if !ok {
return fmt.Errorf("%T is not an account state", acc)
}
if len(acc.Votes) > vm.MaxArraySize {
return errors.New("too many votes")
}
votes := make([]vm.StackItem, 0, len(acc.Votes))
for _, key := range acc.Votes {
votes = append(votes, vm.NewByteArrayItem(key.Bytes()))
}
v.Estack().PushVal(votes)
return nil
}
// accountIsStandard checks whether given account is standard. // accountIsStandard checks whether given account is standard.
func accountIsStandard(ic *interop.Context, v *vm.VM) error { func accountIsStandard(ic *interop.Context, v *vm.VM) error {
accbytes := v.Estack().Pop().Bytes() accbytes := v.Estack().Pop().Bytes()

View file

@ -417,17 +417,6 @@ func TestAccountGetScriptHash(t *testing.T) {
require.Equal(t, accState.ScriptHash.BytesBE(), hash) require.Equal(t, accState.ScriptHash.BytesBE(), hash)
} }
func TestAccountGetVotes(t *testing.T) {
v, accState, context, chain := createVMAndAccState(t)
defer chain.Close()
v.Estack().PushVal(vm.NewInteropItem(accState))
err := accountGetVotes(context, v)
require.NoError(t, err)
votes := v.Estack().Pop().Value().([]vm.StackItem)
require.Equal(t, vm.NewByteArrayItem(accState.Votes[0].Bytes()), votes[0])
}
func TestContractGetScript(t *testing.T) { func TestContractGetScript(t *testing.T) {
v, contractState, context, chain := createVMAndContractState(t) v, contractState, context, chain := createVMAndContractState(t)
defer chain.Close() defer chain.Close()
@ -603,9 +592,6 @@ func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interop.Context
hash, err := util.Uint160DecodeStringBE(rawHash) hash, err := util.Uint160DecodeStringBE(rawHash)
accountState := state.NewAccount(hash) accountState := state.NewAccount(hash)
key := &keys.PublicKey{X: big.NewInt(1), Y: big.NewInt(1)}
accountState.Votes = []*keys.PublicKey{key}
require.NoError(t, err) require.NoError(t, err)
chain := newTestChain(t) chain := newTestChain(t)
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, nil) context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, nil)

View file

@ -115,7 +115,6 @@ var systemInterops = []interop.Function{
var neoInterops = []interop.Function{ var neoInterops = []interop.Function{
{Name: "Neo.Account.GetBalance", Func: accountGetBalance, Price: 1}, {Name: "Neo.Account.GetBalance", Func: accountGetBalance, Price: 1},
{Name: "Neo.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1}, {Name: "Neo.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1},
{Name: "Neo.Account.GetVotes", Func: accountGetVotes, Price: 1},
{Name: "Neo.Account.IsStandard", Func: accountIsStandard, Price: 100}, {Name: "Neo.Account.IsStandard", Func: accountIsStandard, Price: 100},
{Name: "Neo.Asset.Create", Func: assetCreate, Price: 0}, {Name: "Neo.Asset.Create", Func: assetCreate, Price: 0},
{Name: "Neo.Asset.GetAdmin", Func: assetGetAdmin, Price: 1}, {Name: "Neo.Asset.GetAdmin", Func: assetGetAdmin, Price: 1},
@ -140,7 +139,6 @@ var neoInterops = []interop.Function{
{Name: "Neo.Blockchain.GetHeight", Func: bcGetHeight, Price: 1}, {Name: "Neo.Blockchain.GetHeight", Func: bcGetHeight, Price: 1},
{Name: "Neo.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100}, {Name: "Neo.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100},
{Name: "Neo.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100}, {Name: "Neo.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100},
{Name: "Neo.Blockchain.GetValidators", Func: bcGetValidators, Price: 200},
{Name: "Neo.Contract.Create", Func: contractCreate, Price: 0}, {Name: "Neo.Contract.Create", Func: contractCreate, Price: 0},
{Name: "Neo.Contract.Destroy", Func: contractDestroy, Price: 1}, {Name: "Neo.Contract.Destroy", Func: contractDestroy, Price: 1},
{Name: "Neo.Contract.GetScript", Func: contractGetScript, Price: 1}, {Name: "Neo.Contract.GetScript", Func: contractGetScript, Price: 1},
@ -204,7 +202,6 @@ var neoInterops = []interop.Function{
// Old compatibility APIs. // Old compatibility APIs.
{Name: "AntShares.Account.GetBalance", Func: accountGetBalance, Price: 1}, {Name: "AntShares.Account.GetBalance", Func: accountGetBalance, Price: 1},
{Name: "AntShares.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1}, {Name: "AntShares.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1},
{Name: "AntShares.Account.GetVotes", Func: accountGetVotes, Price: 1},
{Name: "AntShares.Asset.Create", Func: assetCreate, Price: 0}, {Name: "AntShares.Asset.Create", Func: assetCreate, Price: 0},
{Name: "AntShares.Asset.GetAdmin", Func: assetGetAdmin, Price: 1}, {Name: "AntShares.Asset.GetAdmin", Func: assetGetAdmin, Price: 1},
{Name: "AntShares.Asset.GetAmount", Func: assetGetAmount, Price: 1}, {Name: "AntShares.Asset.GetAmount", Func: assetGetAmount, Price: 1},
@ -227,7 +224,6 @@ var neoInterops = []interop.Function{
{Name: "AntShares.Blockchain.GetHeader", Func: bcGetHeader, Price: 100}, {Name: "AntShares.Blockchain.GetHeader", Func: bcGetHeader, Price: 100},
{Name: "AntShares.Blockchain.GetHeight", Func: bcGetHeight, Price: 1}, {Name: "AntShares.Blockchain.GetHeight", Func: bcGetHeight, Price: 1},
{Name: "AntShares.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100}, {Name: "AntShares.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100},
{Name: "AntShares.Blockchain.GetValidators", Func: bcGetValidators, Price: 200},
{Name: "AntShares.Contract.Create", Func: contractCreate, Price: 0}, {Name: "AntShares.Contract.Create", Func: contractCreate, Price: 0},
{Name: "AntShares.Contract.Destroy", Func: contractDestroy, Price: 1}, {Name: "AntShares.Contract.Destroy", Func: contractDestroy, Price: 1},
{Name: "AntShares.Contract.GetScript", Func: contractGetScript, Price: 1}, {Name: "AntShares.Contract.GetScript", Func: contractGetScript, Price: 1},

View file

@ -34,7 +34,6 @@ func TestUnexpectedNonInterops(t *testing.T) {
funcs := []func(*interop.Context, *vm.VM) error{ funcs := []func(*interop.Context, *vm.VM) error{
accountGetBalance, accountGetBalance,
accountGetScriptHash, accountGetScriptHash,
accountGetVotes,
assetGetAdmin, assetGetAdmin,
assetGetAmount, assetGetAmount,
assetGetAssetID, assetGetAssetID,

View file

@ -4,80 +4,20 @@ import (
"fmt" "fmt"
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// Method is a signature for a native method.
type Method = func(ic *interop.Context, args []vm.StackItem) vm.StackItem
// MethodAndPrice is a native-contract method descriptor.
type MethodAndPrice struct {
Func Method
Price int64
RequiredFlags smartcontract.CallFlag
}
// Contract is an interface for all native contracts.
type Contract interface {
Metadata() *ContractMD
OnPersist(*interop.Context) error
}
// ContractMD represents native contract instance.
type ContractMD struct {
Manifest manifest.Manifest
ServiceName string
ServiceID uint32
Script []byte
Hash util.Uint160
Methods map[string]MethodAndPrice
}
// Contracts is a set of registered native contracts. // Contracts is a set of registered native contracts.
type Contracts struct { type Contracts struct {
NEO *NEO NEO *NEO
GAS *GAS GAS *GAS
Contracts []Contract Contracts []interop.Contract
}
// SetGAS sets GAS native contract.
func (cs *Contracts) SetGAS(g *GAS) {
cs.GAS = g
cs.Contracts = append(cs.Contracts, g)
}
// SetNEO sets NEO native contract.
func (cs *Contracts) SetNEO(n *NEO) {
cs.NEO = n
cs.Contracts = append(cs.Contracts, n)
}
// NewContractMD returns Contract with the specified list of methods.
func NewContractMD(name string) *ContractMD {
c := &ContractMD{
ServiceName: name,
ServiceID: emit.InteropNameToID([]byte(name)),
Methods: make(map[string]MethodAndPrice),
}
w := io.NewBufBinWriter()
emit.Syscall(w.BinWriter, c.ServiceName)
c.Script = w.Bytes()
c.Hash = hash.Hash160(c.Script)
c.Manifest = *manifest.DefaultManifest(c.Hash)
return c
} }
// ByHash returns native contract with the specified hash. // ByHash returns native contract with the specified hash.
func (cs *Contracts) ByHash(h util.Uint160) Contract { func (cs *Contracts) ByHash(h util.Uint160) interop.Contract {
for _, ctr := range cs.Contracts { for _, ctr := range cs.Contracts {
if ctr.Metadata().Hash.Equals(h) { if ctr.Metadata().Hash.Equals(h) {
return ctr return ctr
@ -87,7 +27,7 @@ func (cs *Contracts) ByHash(h util.Uint160) Contract {
} }
// ByID returns native contract with the specified id. // ByID returns native contract with the specified id.
func (cs *Contracts) ByID(id uint32) Contract { func (cs *Contracts) ByID(id uint32) interop.Contract {
for _, ctr := range cs.Contracts { for _, ctr := range cs.Contracts {
if ctr.Metadata().ServiceID == id { if ctr.Metadata().ServiceID == id {
return ctr return ctr
@ -96,33 +36,21 @@ func (cs *Contracts) ByID(id uint32) Contract {
return nil return nil
} }
// AddMethod adds new method to a native contract. // NewContracts returns new set of native contracts with new GAS and NEO
func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method, safe bool) { // contracts.
c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, *desc)
c.Methods[desc.Name] = *md
if safe {
c.Manifest.SafeMethods.Add(desc.Name)
}
}
// AddEvent adds new event to a native contract.
func (c *ContractMD) AddEvent(name string, ps ...manifest.Parameter) {
c.Manifest.ABI.Events = append(c.Manifest.ABI.Events, manifest.Event{
Name: name,
Parameters: ps,
})
}
// NewContracts returns new empty set of native contracts.
func NewContracts() *Contracts { func NewContracts() *Contracts {
return &Contracts{ cs := new(Contracts)
Contracts: []Contract{},
}
}
// Add adds new native contracts to the list. gas := NewGAS()
func (cs *Contracts) Add(c Contract) { neo := NewNEO()
cs.Contracts = append(cs.Contracts, c) neo.GAS = gas
gas.NEO = neo
cs.GAS = gas
cs.Contracts = append(cs.Contracts, gas)
cs.NEO = neo
cs.Contracts = append(cs.Contracts, neo)
return cs
} }
// GetNativeInterop returns an interop getter for a given set of contracts. // GetNativeInterop returns an interop getter for a given set of contracts.
@ -139,7 +67,7 @@ func (cs *Contracts) GetNativeInterop(ic *interop.Context) func(uint32) *vm.Inte
} }
// getNativeInterop returns native contract interop. // getNativeInterop returns native contract interop.
func getNativeInterop(ic *interop.Context, c Contract) func(v *vm.VM) error { func getNativeInterop(ic *interop.Context, c interop.Contract) func(v *vm.VM) error {
return func(v *vm.VM) error { return func(v *vm.VM) error {
h := v.GetContextScriptHash(0) h := v.GetContextScriptHash(0)
if !h.Equals(c.Metadata().Hash) { if !h.Equals(c.Metadata().Hash) {

View file

@ -2,6 +2,7 @@ package native
import ( import (
"errors" "errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
@ -12,5 +13,12 @@ func Deploy(ic *interop.Context, _ *vm.VM) error {
if ic.Block.Index != 0 { if ic.Block.Index != 0 {
return errors.New("native contracts can be deployed only at 0 block") return errors.New("native contracts can be deployed only at 0 block")
} }
for _, native := range ic.Natives {
if err := native.Initialize(ic); err != nil {
md := native.Metadata()
return fmt.Errorf("initializing %s native contract: %v", md.ServiceName, err)
}
}
return nil return nil
} }

View file

@ -6,14 +6,12 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
) )
// GAS represents GAS native contract. // GAS represents GAS native contract.
@ -24,13 +22,17 @@ type GAS struct {
const gasSyscallName = "Neo.Native.Tokens.GAS" const gasSyscallName = "Neo.Native.Tokens.GAS"
// GASFactor is a divisor for finding GAS integral value.
const GASFactor = NEOTotalSupply
const initialGAS = 30000000
// NewGAS returns GAS native contract. // NewGAS returns GAS native contract.
func NewGAS() *GAS { func NewGAS() *GAS {
nep5 := newNEP5Native(gasSyscallName) nep5 := newNEP5Native(gasSyscallName)
nep5.name = "GAS" nep5.name = "GAS"
nep5.symbol = "gas" nep5.symbol = "gas"
nep5.decimals = 8 nep5.decimals = 8
nep5.factor = 100000000 nep5.factor = GASFactor
g := &GAS{nep5TokenNative: *nep5} g := &GAS{nep5TokenNative: *nep5}
@ -44,44 +46,35 @@ func NewGAS() *GAS {
return g return g
} }
// initFromStore initializes variable contract parameters from the store. func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.StorageItem, amount *big.Int) error {
func (g *GAS) initFromStore(data []byte) error { acc, err := state.NEP5BalanceStateFromBytes(si.Value)
g.totalSupply = *emit.BytesToInt(data) if err != nil {
return nil return err
} }
func (g *GAS) serializeState() []byte {
return emit.IntToBytes(&g.totalSupply)
}
func (g *GAS) increaseBalance(_ *interop.Context, acc *state.Account, amount *big.Int) error {
if sign := amount.Sign(); sign == 0 { if sign := amount.Sign(); sign == 0 {
return nil return nil
} else if sign == -1 && acc.GAS.Balance.Cmp(new(big.Int).Neg(amount)) == -1 { } else if sign == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
return errors.New("insufficient funds") return errors.New("insufficient funds")
} }
acc.GAS.Balance.Add(&acc.GAS.Balance, amount) acc.Balance.Add(&acc.Balance, amount)
si.Value = acc.Bytes()
return nil return nil
} }
// Initialize initializes GAS contract. // Initialize initializes GAS contract.
func (g *GAS) Initialize(ic *interop.Context) error { func (g *GAS) Initialize(ic *interop.Context) error {
data, err := ic.DAO.GetNativeContractState(g.Hash) if err := g.nep5TokenNative.Initialize(ic); err != nil {
if err == nil {
return g.initFromStore(data)
} else if err != storage.ErrKeyNotFound {
return err return err
} }
if g.nep5TokenNative.getTotalSupply(ic).Sign() != 0 {
if err := g.nep5TokenNative.Initialize(); err != nil { return errors.New("already initialized")
return err
} }
h, _, err := getStandbyValidatorsHash(ic) h, _, err := getStandbyValidatorsHash(ic)
if err != nil { if err != nil {
return err return err
} }
g.mint(ic, h, big.NewInt(30000000*g.factor)) g.mint(ic, h, big.NewInt(initialGAS*GASFactor))
return ic.DAO.PutNativeContractState(g.Hash, g.serializeState()) return nil
} }
// OnPersist implements Contract interface. // OnPersist implements Contract interface.
@ -95,7 +88,7 @@ func (g *GAS) OnPersist(ic *interop.Context) error {
// netFee += tx.NetworkFee // netFee += tx.NetworkFee
//} //}
//g.mint(ic, <primary>, netFee) //g.mint(ic, <primary>, netFee)
return ic.DAO.PutNativeContractState(g.Hash, g.serializeState()) return nil
} }
func (g *GAS) getSysFeeAmount(ic *interop.Context, args []vm.StackItem) vm.StackItem { func (g *GAS) getSysFeeAmount(ic *interop.Context, args []vm.StackItem) vm.StackItem {

View file

@ -9,13 +9,11 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -25,7 +23,40 @@ type NEO struct {
GAS *GAS GAS *GAS
} }
const neoSyscallName = "Neo.Native.Tokens.NEO" // keyWithVotes is a serialized key with votes balance. It's not deserialized
// because some uses of it imply serialized-only usage and converting to
// PublicKey is quite expensive.
type keyWithVotes struct {
Key string
Votes *big.Int
}
const (
neoSyscallName = "Neo.Native.Tokens.NEO"
// NEOTotalSupply is the total amount of NEO in the system.
NEOTotalSupply = 100000000
// prefixValidator is a prefix used to store validator's data.
prefixValidator = 33
)
var (
// validatorsCountKey is a key used to store validators count
// used to determine the real number of validators.
validatorsCountKey = []byte{15}
// nextValidatorsKey is a key used to store validators for the
// next block.
nextValidatorsKey = []byte{14}
)
// makeValidatorKey creates a key from account script hash.
func makeValidatorKey(key *keys.PublicKey) []byte {
b := key.Bytes()
// Don't create a new buffer.
b = append(b, 0)
copy(b[1:], b[0:])
b[0] = prefixValidator
return b
}
// NewNEO returns NEO native contract. // NewNEO returns NEO native contract.
func NewNEO() *NEO { func NewNEO() *NEO {
@ -55,7 +86,7 @@ func NewNEO() *NEO {
n.AddMethod(md, desc, false) n.AddMethod(md, desc, false)
desc = newDescriptor("getRegisteredValidators", smartcontract.ArrayType) desc = newDescriptor("getRegisteredValidators", smartcontract.ArrayType)
md = newMethodAndPrice(n.getRegisteredValidators, 1, smartcontract.NoneFlag) md = newMethodAndPrice(n.getRegisteredValidatorsCall, 1, smartcontract.NoneFlag)
n.AddMethod(md, desc, true) n.AddMethod(md, desc, true)
desc = newDescriptor("getValidators", smartcontract.ArrayType) desc = newDescriptor("getValidators", smartcontract.ArrayType)
@ -74,22 +105,26 @@ func NewNEO() *NEO {
// Initialize initializes NEO contract. // Initialize initializes NEO contract.
func (n *NEO) Initialize(ic *interop.Context) error { func (n *NEO) Initialize(ic *interop.Context) error {
data, err := ic.DAO.GetNativeContractState(n.Hash) var si state.StorageItem
if err == nil {
return n.initFromStore(data) if err := n.nep5TokenNative.Initialize(ic); err != nil {
} else if err != storage.ErrKeyNotFound {
return err return err
} }
if err := n.nep5TokenNative.Initialize(); err != nil { if n.nep5TokenNative.getTotalSupply(ic).Sign() != 0 {
return err return errors.New("already initialized")
} }
vc := new(ValidatorsCount)
si.Value = vc.Bytes()
if err := ic.DAO.PutStorageItem(n.Hash, validatorsCountKey, &si); err != nil {
return err
}
h, vs, err := getStandbyValidatorsHash(ic) h, vs, err := getStandbyValidatorsHash(ic)
if err != nil { if err != nil {
return err return err
} }
n.mint(ic, h, big.NewInt(100000000*n.factor)) n.mint(ic, h, big.NewInt(NEOTotalSupply))
for i := range vs { for i := range vs {
if err := n.registerValidatorInternal(ic, vs[i]); err != nil { if err := n.registerValidatorInternal(ic, vs[i]); err != nil {
@ -97,54 +132,67 @@ func (n *NEO) Initialize(ic *interop.Context) error {
} }
} }
return ic.DAO.PutNativeContractState(n.Hash, n.serializeState())
}
// initFromStore initializes variable contract parameters from the store.
func (n *NEO) initFromStore(data []byte) error {
n.totalSupply = *emit.BytesToInt(data)
return nil return nil
} }
func (n *NEO) serializeState() []byte {
return emit.IntToBytes(&n.totalSupply)
}
// OnPersist implements Contract interface. // OnPersist implements Contract interface.
func (n *NEO) OnPersist(ic *interop.Context) error { func (n *NEO) OnPersist(ic *interop.Context) error {
pubs, err := n.GetValidatorsInternal(ic.Chain, ic.DAO) pubs, err := n.GetValidatorsInternal(ic.Chain, ic.DAO)
if err != nil { if err != nil {
return err return err
} }
if err := ic.DAO.PutNextBlockValidators(pubs); err != nil { si := new(state.StorageItem)
return err si.Value = pubs.Bytes()
} return ic.DAO.PutStorageItem(n.Hash, nextValidatorsKey, si)
return ic.DAO.PutNativeContractState(n.Hash, n.serializeState())
} }
func (n *NEO) increaseBalance(ic *interop.Context, acc *state.Account, amount *big.Int) error { func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int) error {
if sign := amount.Sign(); sign == 0 { acc, err := state.NEOBalanceStateFromBytes(si.Value)
return nil
} else if sign == -1 && acc.NEO.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
return errors.New("insufficient funds")
}
if err := n.distributeGas(ic, acc); err != nil {
return err
}
acc.NEO.Balance.Add(&acc.NEO.Balance, amount)
return nil
}
func (n *NEO) distributeGas(ic *interop.Context, acc *state.Account) error {
if ic.Block == nil {
return nil
}
sys, net, err := ic.Chain.CalculateClaimable(util.Fixed8(acc.NEO.Balance.Int64()), acc.NEO.BalanceHeight, ic.Block.Index)
if err != nil { if err != nil {
return err return err
} }
acc.NEO.BalanceHeight = ic.Block.Index if amount.Sign() == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
n.GAS.mint(ic, acc.ScriptHash, big.NewInt(int64(sys+net))) return errors.New("insufficient funds")
}
if err := n.distributeGas(ic, h, acc); err != nil {
return err
}
if amount.Sign() == 0 {
return nil
}
if len(acc.Votes) > 0 {
if err := n.ModifyAccountVotes(acc, ic.DAO, new(big.Int).Neg(&acc.Balance)); err != nil {
return err
}
siVC := ic.DAO.GetStorageItem(n.Hash, validatorsCountKey)
if siVC == nil {
return errors.New("validators count uninitialized")
}
vc, err := ValidatorsCountFromBytes(siVC.Value)
if err != nil {
return err
}
vc[len(acc.Votes)-1].Add(&vc[len(acc.Votes)-1], amount)
siVC.Value = vc.Bytes()
if err := ic.DAO.PutStorageItem(n.Hash, validatorsCountKey, siVC); err != nil {
return err
}
}
acc.Balance.Add(&acc.Balance, amount)
si.Value = acc.Bytes()
return nil
}
func (n *NEO) distributeGas(ic *interop.Context, h util.Uint160, acc *state.NEOBalanceState) error {
if ic.Block == nil || ic.Block.Index == 0 {
return nil
}
sys, net, err := ic.Chain.CalculateClaimable(util.Fixed8(acc.Balance.Int64()), acc.BalanceHeight, ic.Block.Index)
if err != nil {
return err
}
acc.BalanceHeight = ic.Block.Index
n.GAS.mint(ic, h, big.NewInt(int64(sys+net)))
return nil return nil
} }
@ -170,11 +218,17 @@ func (n *NEO) registerValidator(ic *interop.Context, args []vm.StackItem) vm.Sta
} }
func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey) error { func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey) error {
_, err := ic.DAO.GetValidatorState(pub) key := makeValidatorKey(pub)
if err == nil { si := ic.DAO.GetStorageItem(n.Hash, key)
return err if si != nil {
return errors.New("already registered")
} }
return ic.DAO.PutValidatorState(&state.Validator{PublicKey: pub}) si = new(state.StorageItem)
// It's the same simple counter, calling it `Votes` instead of `Balance`
// doesn't help a lot.
votes := state.NEP5BalanceState{}
si.Value = votes.Bytes()
return ic.DAO.PutStorageItem(n.Hash, key, si)
} }
func (n *NEO) vote(ic *interop.Context, args []vm.StackItem) vm.StackItem { func (n *NEO) vote(ic *interop.Context, args []vm.StackItem) vm.StackItem {
@ -203,103 +257,147 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public
} else if !ok { } else if !ok {
return errors.New("invalid signature") return errors.New("invalid signature")
} }
acc, err := ic.DAO.GetAccountState(h) key := makeAccountKey(h)
si := ic.DAO.GetStorageItem(n.Hash, key)
if si == nil {
return errors.New("invalid account")
}
acc, err := state.NEOBalanceStateFromBytes(si.Value)
if err != nil { if err != nil {
return err return err
} }
balance := util.Fixed8(acc.NEO.Balance.Int64()) if err := n.ModifyAccountVotes(acc, ic.DAO, new(big.Int).Neg(&acc.Balance)); err != nil {
if err := ModifyAccountVotes(acc, ic.DAO, -balance); err != nil {
return err return err
} }
pubs = pubs.Unique() pubs = pubs.Unique()
// Check validators registration.
var newPubs keys.PublicKeys var newPubs keys.PublicKeys
for _, pub := range pubs { for _, pub := range pubs {
_, err := ic.DAO.GetValidatorState(pub) if ic.DAO.GetStorageItem(n.Hash, makeValidatorKey(pub)) == nil {
if err != nil {
if err == storage.ErrKeyNotFound {
continue continue
} }
return err
}
newPubs = append(newPubs, pub) newPubs = append(newPubs, pub)
} }
if lp, lv := len(newPubs), len(acc.Votes); lp != lv { if lp, lv := len(newPubs), len(acc.Votes); lp != lv {
vc, err := ic.DAO.GetValidatorsCount() si := ic.DAO.GetStorageItem(n.Hash, validatorsCountKey)
if si == nil {
return errors.New("validators count uninitialized")
}
vc, err := ValidatorsCountFromBytes(si.Value)
if err != nil { if err != nil {
return err return err
} }
if lv > 0 { if lv > 0 {
vc[lv-1] -= balance vc[lv-1].Sub(&vc[lv-1], &acc.Balance)
} }
if len(newPubs) > 0 { if len(newPubs) > 0 {
vc[lp-1] += balance vc[lp-1].Add(&vc[lp-1], &acc.Balance)
} }
if err := ic.DAO.PutValidatorsCount(vc); err != nil { si.Value = vc.Bytes()
if err := ic.DAO.PutStorageItem(n.Hash, validatorsCountKey, si); err != nil {
return err return err
} }
} }
acc.Votes = newPubs acc.Votes = newPubs
return ModifyAccountVotes(acc, ic.DAO, balance) if err := n.ModifyAccountVotes(acc, ic.DAO, &acc.Balance); err != nil {
return err
}
si.Value = acc.Bytes()
return ic.DAO.PutStorageItem(n.Hash, key, si)
} }
// ModifyAccountVotes modifies votes of the specified account by value (can be negative). // ModifyAccountVotes modifies votes of the specified account by value (can be negative).
func ModifyAccountVotes(acc *state.Account, d dao.DAO, value util.Fixed8) error { func (n *NEO) ModifyAccountVotes(acc *state.NEOBalanceState, d dao.DAO, value *big.Int) error {
for _, vote := range acc.Votes { for _, vote := range acc.Votes {
validator, err := d.GetValidatorStateOrNew(vote) key := makeValidatorKey(vote)
si := d.GetStorageItem(n.Hash, key)
if si == nil {
return errors.New("invalid validator")
}
votes, err := state.NEP5BalanceStateFromBytes(si.Value)
if err != nil { if err != nil {
return err return err
} }
validator.Votes += value votes.Balance.Add(&votes.Balance, value)
if validator.UnregisteredAndHasNoVotes() { si.Value = votes.Bytes()
if err := d.DeleteValidatorState(validator); err != nil { if err := d.PutStorageItem(n.Hash, key, si); err != nil {
return err return err
} }
} else {
if err := d.PutValidatorState(validator); err != nil {
return err
}
}
} }
return nil return nil
} }
func (n *NEO) getRegisteredValidators(ic *interop.Context, _ []vm.StackItem) vm.StackItem { func (n *NEO) getRegisteredValidators(d dao.DAO) ([]keyWithVotes, error) {
vs := ic.DAO.GetValidators() siMap, err := d.GetStorageItemsWithPrefix(n.Hash, []byte{prefixValidator})
arr := make([]vm.StackItem, len(vs)) if err != nil {
for i := range vs { return nil, err
}
arr := make([]keyWithVotes, 0, len(siMap))
for key, si := range siMap {
votes, err := state.NEP5BalanceStateFromBytes(si.Value)
if err != nil {
return nil, err
}
arr = append(arr, keyWithVotes{key, &votes.Balance})
}
return arr, nil
}
// GetRegisteredValidators returns current registered validators list with keys
// and votes.
func (n *NEO) GetRegisteredValidators(d dao.DAO) ([]state.Validator, error) {
kvs, err := n.getRegisteredValidators(d)
if err != nil {
return nil, err
}
arr := make([]state.Validator, len(kvs))
for i := range kvs {
arr[i].Key, err = keys.NewPublicKeyFromBytes([]byte(kvs[i].Key))
if err != nil {
return nil, err
}
arr[i].Votes = kvs[i].Votes
}
return arr, nil
}
func (n *NEO) getRegisteredValidatorsCall(ic *interop.Context, _ []vm.StackItem) vm.StackItem {
validators, err := n.getRegisteredValidators(ic.DAO)
if err != nil {
panic(err)
}
arr := make([]vm.StackItem, len(validators))
for i := range validators {
arr[i] = vm.NewStructItem([]vm.StackItem{ arr[i] = vm.NewStructItem([]vm.StackItem{
vm.NewByteArrayItem(vs[i].PublicKey.Bytes()), vm.NewByteArrayItem([]byte(validators[i].Key)),
vm.NewBigIntegerItem(big.NewInt(int64(vs[i].Votes))), vm.NewBigIntegerItem(validators[i].Votes),
}) })
} }
return vm.NewArrayItem(arr) return vm.NewArrayItem(arr)
} }
// GetValidatorsInternal returns a list of current validators. // GetValidatorsInternal returns a list of current validators.
func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) ([]*keys.PublicKey, error) { func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) {
validatorsCount, err := d.GetValidatorsCount() si := d.GetStorageItem(n.Hash, validatorsCountKey)
if err != nil { if si == nil {
return nil, err return nil, errors.New("validators count uninitialized")
} else if len(validatorsCount) == 0 { }
sb, err := bc.GetStandByValidators() validatorsCount, err := ValidatorsCountFromBytes(si.Value)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return sb, nil validators, err := n.GetRegisteredValidators(d)
if err != nil {
return nil, err
} }
validators := d.GetValidators()
sort.Slice(validators, func(i, j int) bool { sort.Slice(validators, func(i, j int) bool {
// Unregistered validators go to the end of the list.
if validators[i].Registered != validators[j].Registered {
return validators[i].Registered
}
// The most-voted validators should end up in the front of the list. // The most-voted validators should end up in the front of the list.
if validators[i].Votes != validators[j].Votes { cmp := validators[i].Votes.Cmp(validators[j].Votes)
return validators[i].Votes > validators[j].Votes if cmp != 0 {
return cmp > 0
} }
// Ties are broken with public keys. // Ties are broken with public keys.
return validators[i].PublicKey.Cmp(validators[j].PublicKey) == -1 return validators[i].Key.Cmp(validators[j].Key) == -1
}) })
count := validatorsCount.GetWeightedAverage() count := validatorsCount.GetWeightedAverage()
@ -314,8 +412,8 @@ func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) ([]
uniqueSBValidators := standByValidators.Unique() uniqueSBValidators := standByValidators.Unique()
result := keys.PublicKeys{} result := keys.PublicKeys{}
for _, validator := range validators { for _, validator := range validators {
if validator.RegisteredAndHasVotes() || uniqueSBValidators.Contains(validator.PublicKey) { if validator.Votes.Sign() > 0 || uniqueSBValidators.Contains(validator.Key) {
result = append(result, validator.PublicKey) result = append(result, validator.Key)
} }
} }
@ -349,14 +447,17 @@ func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []vm.StackItem) vm.S
} }
// GetNextBlockValidatorsInternal returns next block validators. // GetNextBlockValidatorsInternal returns next block validators.
func (n *NEO) GetNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) ([]*keys.PublicKey, error) { func (n *NEO) GetNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) {
result, err := d.GetNextBlockValidators() si := d.GetStorageItem(n.Hash, nextValidatorsKey)
if si == nil {
return n.GetValidatorsInternal(bc, d)
}
pubs := keys.PublicKeys{}
err := pubs.DecodeBytes(si.Value)
if err != nil { if err != nil {
return nil, err return nil, err
} else if result == nil {
return bc.GetStandByValidators()
} }
return result, nil return pubs, nil
} }
func pubsToArray(pubs keys.PublicKeys) vm.StackItem { func pubsToArray(pubs keys.PublicKeys) vm.StackItem {

View file

@ -10,28 +10,42 @@ import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
) )
// prefixAccount is the standard prefix used to store account data.
const prefixAccount = 20
// makeAccountKey creates a key from account script hash.
func makeAccountKey(h util.Uint160) []byte {
k := make([]byte, util.Uint160Size+1)
k[0] = prefixAccount
copy(k[1:], h.BytesBE())
return k
}
// nep5TokenNative represents NEP-5 token contract. // nep5TokenNative represents NEP-5 token contract.
type nep5TokenNative struct { type nep5TokenNative struct {
ContractMD interop.ContractMD
name string name string
symbol string symbol string
decimals int64 decimals int64
factor int64 factor int64
totalSupply big.Int
onPersist func(*interop.Context) error onPersist func(*interop.Context) error
incBalance func(*interop.Context, *state.Account, *big.Int) error incBalance func(*interop.Context, util.Uint160, *state.StorageItem, *big.Int) error
} }
func (c *nep5TokenNative) Metadata() *ContractMD { // totalSupplyKey is the key used to store totalSupply value.
var totalSupplyKey = []byte{11}
func (c *nep5TokenNative) Metadata() *interop.ContractMD {
return &c.ContractMD return &c.ContractMD
} }
var _ Contract = (*nep5TokenNative)(nil) var _ interop.Contract = (*nep5TokenNative)(nil)
func newNEP5Native(name string) *nep5TokenNative { func newNEP5Native(name string) *nep5TokenNative {
n := &nep5TokenNative{ContractMD: *NewContractMD(name)} n := &nep5TokenNative{ContractMD: *interop.NewContractMD(name)}
desc := newDescriptor("name", smartcontract.StringType) desc := newDescriptor("name", smartcontract.StringType)
md := newMethodAndPrice(n.Name, 1, smartcontract.NoneFlag) md := newMethodAndPrice(n.Name, 1, smartcontract.NoneFlag)
@ -45,6 +59,10 @@ func newNEP5Native(name string) *nep5TokenNative {
md = newMethodAndPrice(n.Decimals, 1, smartcontract.NoneFlag) md = newMethodAndPrice(n.Decimals, 1, smartcontract.NoneFlag)
n.AddMethod(md, desc, true) n.AddMethod(md, desc, true)
desc = newDescriptor("totalSupply", smartcontract.IntegerType)
md = newMethodAndPrice(n.TotalSupply, 1, smartcontract.NoneFlag)
n.AddMethod(md, desc, true)
desc = newDescriptor("balanceOf", smartcontract.IntegerType, desc = newDescriptor("balanceOf", smartcontract.IntegerType,
manifest.NewParameter("account", smartcontract.Hash160Type)) manifest.NewParameter("account", smartcontract.Hash160Type))
md = newMethodAndPrice(n.balanceOf, 1, smartcontract.NoneFlag) md = newMethodAndPrice(n.balanceOf, 1, smartcontract.NoneFlag)
@ -63,7 +81,7 @@ func newNEP5Native(name string) *nep5TokenNative {
return n return n
} }
func (c *nep5TokenNative) Initialize() error { func (c *nep5TokenNative) Initialize(_ *interop.Context) error {
return nil return nil
} }
@ -79,6 +97,23 @@ func (c *nep5TokenNative) Decimals(_ *interop.Context, _ []vm.StackItem) vm.Stac
return vm.NewBigIntegerItem(big.NewInt(c.decimals)) return vm.NewBigIntegerItem(big.NewInt(c.decimals))
} }
func (c *nep5TokenNative) TotalSupply(ic *interop.Context, _ []vm.StackItem) vm.StackItem {
return vm.NewBigIntegerItem(c.getTotalSupply(ic))
}
func (c *nep5TokenNative) getTotalSupply(ic *interop.Context) *big.Int {
si := ic.DAO.GetStorageItem(c.Hash, totalSupplyKey)
if si == nil {
return big.NewInt(0)
}
return emit.BytesToInt(si.Value)
}
func (c *nep5TokenNative) saveTotalSupply(ic *interop.Context, supply *big.Int) error {
si := &state.StorageItem{Value: emit.IntToBytes(supply)}
return ic.DAO.PutStorageItem(c.Hash, totalSupplyKey, si)
}
func (c *nep5TokenNative) Transfer(ic *interop.Context, args []vm.StackItem) vm.StackItem { func (c *nep5TokenNative) Transfer(ic *interop.Context, args []vm.StackItem) vm.StackItem {
from := toUint160(args[0]) from := toUint160(args[0])
to := toUint160(args[1]) to := toUint160(args[1])
@ -89,7 +124,7 @@ func (c *nep5TokenNative) Transfer(ic *interop.Context, args []vm.StackItem) vm.
func addrToStackItem(u *util.Uint160) vm.StackItem { func addrToStackItem(u *util.Uint160) vm.StackItem {
if u == nil { if u == nil {
return nil return vm.NullItem{}
} }
return vm.NewByteArrayItem(u.BytesBE()) return vm.NewByteArrayItem(u.BytesBE())
} }
@ -112,9 +147,10 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a
return errors.New("negative amount") return errors.New("negative amount")
} }
accFrom, err := ic.DAO.GetAccountStateOrNew(from) keyFrom := makeAccountKey(from)
if err != nil { siFrom := ic.DAO.GetStorageItem(c.Hash, keyFrom)
return err if siFrom == nil {
return errors.New("insufficient funds")
} }
isEmpty := from.Equals(to) || amount.Sign() == 0 isEmpty := from.Equals(to) || amount.Sign() == 0
@ -122,22 +158,23 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a
if isEmpty { if isEmpty {
inc = big.NewInt(0) inc = big.NewInt(0)
} }
if err := c.incBalance(ic, accFrom, inc); err != nil { if err := c.incBalance(ic, from, siFrom, inc); err != nil {
return err return err
} }
if err := ic.DAO.PutAccountState(accFrom); err != nil { if err := ic.DAO.PutStorageItem(c.Hash, keyFrom, siFrom); err != nil {
return err return err
} }
if !isEmpty { if !isEmpty {
accTo, err := ic.DAO.GetAccountStateOrNew(to) keyTo := makeAccountKey(to)
if err != nil { siTo := ic.DAO.GetStorageItem(c.Hash, keyTo)
if siTo == nil {
siTo = new(state.StorageItem)
}
if err := c.incBalance(ic, to, siTo, amount); err != nil {
return err return err
} }
if err := c.incBalance(ic, accTo, amount); err != nil { if err := ic.DAO.PutStorageItem(c.Hash, keyTo, siTo); err != nil {
return err
}
if err := ic.DAO.PutAccountState(accTo); err != nil {
return err return err
} }
} }
@ -174,18 +211,24 @@ func (c *nep5TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount
return return
} }
acc, err := ic.DAO.GetAccountStateOrNew(h) key := makeAccountKey(h)
if err != nil { si := ic.DAO.GetStorageItem(c.Hash, key)
if si == nil {
si = new(state.StorageItem)
}
if err := c.incBalance(ic, h, si, amount); err != nil {
panic(err) panic(err)
} }
if err := c.incBalance(ic, acc, amount); err != nil { if err := ic.DAO.PutStorageItem(c.Hash, key, si); err != nil {
panic(err)
}
if err := ic.DAO.PutAccountState(acc); err != nil {
panic(err) panic(err)
} }
c.totalSupply.Add(&c.totalSupply, amount) supply := c.getTotalSupply(ic)
supply.Add(supply, amount)
err := c.saveTotalSupply(ic, supply)
if err != nil {
panic(err)
}
} }
func (c *nep5TokenNative) OnPersist(ic *interop.Context) error { func (c *nep5TokenNative) OnPersist(ic *interop.Context) error {
@ -200,8 +243,8 @@ func newDescriptor(name string, ret smartcontract.ParamType, ps ...manifest.Para
} }
} }
func newMethodAndPrice(f Method, price int64, flags smartcontract.CallFlag) *MethodAndPrice { func newMethodAndPrice(f interop.Method, price int64, flags smartcontract.CallFlag) *interop.MethodAndPrice {
return &MethodAndPrice{ return &interop.MethodAndPrice{
Func: f, Func: f,
Price: price, Price: price,
RequiredFlags: flags, RequiredFlags: flags,

View file

@ -0,0 +1,106 @@
package native
import (
"math/big"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
)
// MaxValidatorsVoted limits the number of validators that one can vote for.
const MaxValidatorsVoted = 1024
// ValidatorsCount represents votes with particular number of consensus nodes
// for this number to be changeable by the voting system.
type ValidatorsCount [MaxValidatorsVoted]big.Int
// ValidatorsCountFromBytes converts serialized ValidatorsCount to structure.
func ValidatorsCountFromBytes(b []byte) (*ValidatorsCount, error) {
vc := new(ValidatorsCount)
if len(b) == 0 {
return vc, nil
}
r := io.NewBinReaderFromBuf(b)
vc.DecodeBinary(r)
if r.Err != nil {
return nil, r.Err
}
return vc, nil
}
// Bytes returns serialized ValidatorsCount.
func (vc *ValidatorsCount) Bytes() []byte {
w := io.NewBufBinWriter()
vc.EncodeBinary(w.BinWriter)
if w.Err != nil {
panic(w.Err)
}
return w.Bytes()
}
// EncodeBinary implements io.Serializable interface.
func (vc *ValidatorsCount) EncodeBinary(w *io.BinWriter) {
for i := range vc {
w.WriteVarBytes(emit.IntToBytes(&vc[i]))
}
}
// DecodeBinary implements io.Serializable interface.
func (vc *ValidatorsCount) DecodeBinary(r *io.BinReader) {
for i := range vc {
buf := r.ReadVarBytes()
if r.Err != nil {
return
}
vc[i] = *emit.BytesToInt(buf)
}
}
// GetWeightedAverage returns an average count of validators that's been voted
// for not counting 1/4 of minimum and maximum numbers.
func (vc *ValidatorsCount) GetWeightedAverage() int {
const (
lowerThreshold = 0.25
upperThreshold = 0.75
)
var (
sumWeight, sumValue, overallSum, slidingSum int64
slidingRatio float64
)
for i := range vc {
overallSum += vc[i].Int64()
}
for i := range vc {
if slidingRatio >= upperThreshold {
break
}
weight := vc[i].Int64()
slidingSum += weight
previousRatio := slidingRatio
slidingRatio = float64(slidingSum) / float64(overallSum)
if slidingRatio <= lowerThreshold {
continue
}
if previousRatio < lowerThreshold {
if slidingRatio > upperThreshold {
weight = int64((upperThreshold - lowerThreshold) * float64(overallSum))
} else {
weight = int64((slidingRatio - lowerThreshold) * float64(overallSum))
}
} else if slidingRatio > upperThreshold {
weight = int64((upperThreshold - previousRatio) * float64(overallSum))
}
sumWeight += weight
// Votes with N values get stored with N-1 index, thus +1 here.
sumValue += (int64(i) + 1) * weight
}
if sumValue == 0 || sumWeight == 0 {
return 0
}
return int(sumValue / sumWeight)
}

View file

@ -6,7 +6,6 @@ import (
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
@ -17,11 +16,15 @@ import (
) )
type testNative struct { type testNative struct {
meta native.ContractMD meta interop.ContractMD
blocks chan uint32 blocks chan uint32
} }
func (tn *testNative) Metadata() *native.ContractMD { func (tn *testNative) Initialize(_ *interop.Context) error {
return nil
}
func (tn *testNative) Metadata() *interop.ContractMD {
return &tn.meta return &tn.meta
} }
@ -34,11 +37,16 @@ func (tn *testNative) OnPersist(ic *interop.Context) error {
} }
} }
var _ native.Contract = (*testNative)(nil) var _ interop.Contract = (*testNative)(nil)
// registerNative registers native contract in the blockchain.
func (bc *Blockchain) registerNative(c interop.Contract) {
bc.contracts.Contracts = append(bc.contracts.Contracts, c)
}
func newTestNative() *testNative { func newTestNative() *testNative {
tn := &testNative{ tn := &testNative{
meta: *native.NewContractMD("Test.Native.Sum"), meta: *interop.NewContractMD("Test.Native.Sum"),
blocks: make(chan uint32, 1), blocks: make(chan uint32, 1),
} }
desc := &manifest.Method{ desc := &manifest.Method{
@ -49,7 +57,7 @@ func newTestNative() *testNative {
}, },
ReturnType: smartcontract.IntegerType, ReturnType: smartcontract.IntegerType,
} }
md := &native.MethodAndPrice{ md := &interop.MethodAndPrice{
Func: tn.sum, Func: tn.sum,
Price: 1, Price: 1,
RequiredFlags: smartcontract.NoneFlag, RequiredFlags: smartcontract.NoneFlag,
@ -76,7 +84,7 @@ func TestNativeContract_Invoke(t *testing.T) {
defer chain.Close() defer chain.Close()
tn := newTestNative() tn := newTestNative()
chain.RegisterNative(tn) chain.registerNative(tn)
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "sum", int64(14), int64(28)) emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "sum", int64(14), int64(28))

View file

@ -1,7 +1,6 @@
package state package state
import ( import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
) )
@ -32,9 +31,6 @@ type Account struct {
Version uint8 Version uint8
ScriptHash util.Uint160 ScriptHash util.Uint160
IsFrozen bool IsFrozen bool
Votes []*keys.PublicKey
GAS NEP5BalanceState
NEO NEOBalanceState
Balances map[util.Uint256][]UnspentBalance Balances map[util.Uint256][]UnspentBalance
Unclaimed UnclaimedBalances Unclaimed UnclaimedBalances
} }
@ -45,7 +41,6 @@ func NewAccount(scriptHash util.Uint160) *Account {
Version: 0, Version: 0,
ScriptHash: scriptHash, ScriptHash: scriptHash,
IsFrozen: false, IsFrozen: false,
Votes: []*keys.PublicKey{},
Balances: make(map[util.Uint256][]UnspentBalance), Balances: make(map[util.Uint256][]UnspentBalance),
Unclaimed: UnclaimedBalances{Raw: []byte{}}, Unclaimed: UnclaimedBalances{Raw: []byte{}},
} }
@ -56,9 +51,6 @@ func (s *Account) DecodeBinary(br *io.BinReader) {
s.Version = uint8(br.ReadB()) s.Version = uint8(br.ReadB())
br.ReadBytes(s.ScriptHash[:]) br.ReadBytes(s.ScriptHash[:])
s.IsFrozen = br.ReadBool() s.IsFrozen = br.ReadBool()
br.ReadArray(&s.Votes)
s.GAS.DecodeBinary(br)
s.NEO.DecodeBinary(br)
s.Balances = make(map[util.Uint256][]UnspentBalance) s.Balances = make(map[util.Uint256][]UnspentBalance)
lenBalances := br.ReadVarUint() lenBalances := br.ReadVarUint()
@ -83,9 +75,6 @@ func (s *Account) EncodeBinary(bw *io.BinWriter) {
bw.WriteB(byte(s.Version)) bw.WriteB(byte(s.Version))
bw.WriteBytes(s.ScriptHash[:]) bw.WriteBytes(s.ScriptHash[:])
bw.WriteBool(s.IsFrozen) bw.WriteBool(s.IsFrozen)
bw.WriteArray(s.Votes)
s.GAS.EncodeBinary(bw)
s.NEO.EncodeBinary(bw)
bw.WriteVarUint(uint64(len(s.Balances))) bw.WriteVarUint(uint64(len(s.Balances)))
for k, v := range s.Balances { for k, v := range s.Balances {

View file

@ -3,7 +3,6 @@ package state
import ( import (
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/internal/random" "github.com/nspcc-dev/neo-go/pkg/internal/random"
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes" "github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -14,7 +13,6 @@ func TestDecodeEncodeAccountState(t *testing.T) {
var ( var (
n = 10 n = 10
balances = make(map[util.Uint256][]UnspentBalance) balances = make(map[util.Uint256][]UnspentBalance)
votes = make([]*keys.PublicKey, n)
) )
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
asset := random.Uint256() asset := random.Uint256()
@ -25,16 +23,12 @@ func TestDecodeEncodeAccountState(t *testing.T) {
Value: util.Fixed8(int64(random.Int(1, 10000))), Value: util.Fixed8(int64(random.Int(1, 10000))),
}) })
} }
k, err := keys.NewPrivateKey()
assert.Nil(t, err)
votes[i] = k.PublicKey()
} }
a := &Account{ a := &Account{
Version: 0, Version: 0,
ScriptHash: random.Uint160(), ScriptHash: random.Uint160(),
IsFrozen: true, IsFrozen: true,
Votes: votes,
Balances: balances, Balances: balances,
Unclaimed: UnclaimedBalances{Raw: []byte{}}, Unclaimed: UnclaimedBalances{Raw: []byte{}},
} }

View file

@ -3,6 +3,7 @@ package state
import ( import (
"math/big" "math/big"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/emit"
) )
@ -16,6 +17,31 @@ type NEP5BalanceState struct {
type NEOBalanceState struct { type NEOBalanceState struct {
NEP5BalanceState NEP5BalanceState
BalanceHeight uint32 BalanceHeight uint32
Votes keys.PublicKeys
}
// NEP5BalanceStateFromBytes converts serialized NEP5BalanceState to structure.
func NEP5BalanceStateFromBytes(b []byte) (*NEP5BalanceState, error) {
balance := new(NEP5BalanceState)
if len(b) == 0 {
return balance, nil
}
r := io.NewBinReaderFromBuf(b)
balance.DecodeBinary(r)
if r.Err != nil {
return nil, r.Err
}
return balance, nil
}
// Bytes returns serialized NEP5BalanceState.
func (s *NEP5BalanceState) Bytes() []byte {
w := io.NewBufBinWriter()
s.EncodeBinary(w.BinWriter)
if w.Err != nil {
panic(w.Err)
}
return w.Bytes()
} }
// EncodeBinary implements io.Serializable interface. // EncodeBinary implements io.Serializable interface.
@ -32,14 +58,41 @@ func (s *NEP5BalanceState) DecodeBinary(r *io.BinReader) {
s.Balance = *emit.BytesToInt(buf) s.Balance = *emit.BytesToInt(buf)
} }
// NEOBalanceStateFromBytes converts serialized NEOBalanceState to structure.
func NEOBalanceStateFromBytes(b []byte) (*NEOBalanceState, error) {
balance := new(NEOBalanceState)
if len(b) == 0 {
return balance, nil
}
r := io.NewBinReaderFromBuf(b)
balance.DecodeBinary(r)
if r.Err != nil {
return nil, r.Err
}
return balance, nil
}
// Bytes returns serialized NEOBalanceState.
func (s *NEOBalanceState) Bytes() []byte {
w := io.NewBufBinWriter()
s.EncodeBinary(w.BinWriter)
if w.Err != nil {
panic(w.Err)
}
return w.Bytes()
}
// EncodeBinary implements io.Serializable interface. // EncodeBinary implements io.Serializable interface.
func (s *NEOBalanceState) EncodeBinary(w *io.BinWriter) { func (s *NEOBalanceState) EncodeBinary(w *io.BinWriter) {
s.NEP5BalanceState.EncodeBinary(w) s.NEP5BalanceState.EncodeBinary(w)
w.WriteU32LE(s.BalanceHeight) w.WriteU32LE(s.BalanceHeight)
w.WriteArray(s.Votes)
} }
// DecodeBinary implements io.Serializable interface. // DecodeBinary implements io.Serializable interface.
func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) { func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) {
s.NEP5BalanceState.DecodeBinary(r) s.NEP5BalanceState.DecodeBinary(r)
s.BalanceHeight = r.ReadU32LE() s.BalanceHeight = r.ReadU32LE()
r.ReadArray(&s.Votes)
} }

View file

@ -1,108 +1,13 @@
package state package state
import ( import (
"math/big"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util"
) )
// MaxValidatorsVoted limits the number of validators that one can vote for. // Validator holds the state of a validator (its key and votes balance).
const MaxValidatorsVoted = 1024
// Validator holds the state of a validator.
type Validator struct { type Validator struct {
PublicKey *keys.PublicKey Key *keys.PublicKey
Registered bool Votes *big.Int
Votes util.Fixed8
}
// ValidatorsCount represents votes with particular number of consensus nodes
// for this number to be changeable by the voting system.
type ValidatorsCount [MaxValidatorsVoted]util.Fixed8
// RegisteredAndHasVotes returns true or false whether Validator is registered and has votes.
func (vs *Validator) RegisteredAndHasVotes() bool {
return vs.Registered && vs.Votes > util.Fixed8(0)
}
// UnregisteredAndHasNoVotes returns true when Validator is not registered and has no votes.
func (vs *Validator) UnregisteredAndHasNoVotes() bool {
return !vs.Registered && vs.Votes == 0
}
// EncodeBinary encodes Validator to the given BinWriter.
func (vs *Validator) EncodeBinary(bw *io.BinWriter) {
vs.PublicKey.EncodeBinary(bw)
bw.WriteBool(vs.Registered)
vs.Votes.EncodeBinary(bw)
}
// DecodeBinary decodes Validator from the given BinReader.
func (vs *Validator) DecodeBinary(reader *io.BinReader) {
vs.PublicKey = &keys.PublicKey{}
vs.PublicKey.DecodeBinary(reader)
vs.Registered = reader.ReadBool()
vs.Votes.DecodeBinary(reader)
}
// EncodeBinary encodes ValidatorCount to the given BinWriter.
func (vc *ValidatorsCount) EncodeBinary(w *io.BinWriter) {
for i := range vc {
vc[i].EncodeBinary(w)
}
}
// DecodeBinary decodes ValidatorCount from the given BinReader.
func (vc *ValidatorsCount) DecodeBinary(r *io.BinReader) {
for i := range vc {
vc[i].DecodeBinary(r)
}
}
// GetWeightedAverage returns an average count of validators that's been voted
// for not counting 1/4 of minimum and maximum numbers.
func (vc *ValidatorsCount) GetWeightedAverage() int {
const (
lowerThreshold = 0.25
upperThreshold = 0.75
)
var (
sumWeight, sumValue, overallSum, slidingSum util.Fixed8
slidingRatio float64
)
for i := range vc {
overallSum += vc[i]
}
for i := range vc {
if slidingRatio >= upperThreshold {
break
}
weight := vc[i]
slidingSum += weight
previousRatio := slidingRatio
slidingRatio = slidingSum.FloatValue() / overallSum.FloatValue()
if slidingRatio <= lowerThreshold {
continue
}
if previousRatio < lowerThreshold {
if slidingRatio > upperThreshold {
weight = util.Fixed8FromFloat((upperThreshold - lowerThreshold) * overallSum.FloatValue())
} else {
weight = util.Fixed8FromFloat((slidingRatio - lowerThreshold) * overallSum.FloatValue())
}
} else if slidingRatio > upperThreshold {
weight = util.Fixed8FromFloat((upperThreshold - previousRatio) * overallSum.FloatValue())
}
sumWeight += weight
// Votes with N values get stored with N-1 index, thus +1 here.
sumValue += util.Fixed8(i+1) * weight
}
if sumValue == 0 || sumWeight == 0 {
return 0
}
return int(sumValue / sumWeight)
} }

View file

@ -1,57 +0,0 @@
package state
import (
"math/big"
"testing"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
)
func TestValidatorState_DecodeEncodeBinary(t *testing.T) {
state := &Validator{
PublicKey: &keys.PublicKey{},
Registered: false,
Votes: util.Fixed8(10),
}
testserdes.EncodeDecodeBinary(t, state, new(Validator))
}
func TestRegisteredAndHasVotes_Registered(t *testing.T) {
state := &Validator{
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 := &Validator{
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 := &Validator{
PublicKey: &keys.PublicKey{
X: big.NewInt(1),
Y: big.NewInt(1),
},
Registered: false,
Votes: 1,
}
require.False(t, state.RegisteredAndHasVotes())
}

View file

@ -12,17 +12,14 @@ const (
STAccount KeyPrefix = 0x40 STAccount KeyPrefix = 0x40
STCoin KeyPrefix = 0x44 STCoin KeyPrefix = 0x44
STSpentCoin KeyPrefix = 0x45 STSpentCoin KeyPrefix = 0x45
STNextValidators KeyPrefix = 0x47
STValidator KeyPrefix = 0x48 STValidator KeyPrefix = 0x48
STAsset KeyPrefix = 0x4c STAsset KeyPrefix = 0x4c
STNotification KeyPrefix = 0x4d STNotification KeyPrefix = 0x4d
STContract KeyPrefix = 0x50 STContract KeyPrefix = 0x50
STNativeContract KeyPrefix = 0x51
STStorage KeyPrefix = 0x70 STStorage KeyPrefix = 0x70
STNEP5Transfers KeyPrefix = 0x72 STNEP5Transfers KeyPrefix = 0x72
STNEP5Balances KeyPrefix = 0x73 STNEP5Balances KeyPrefix = 0x73
IXHeaderHashList KeyPrefix = 0x80 IXHeaderHashList KeyPrefix = 0x80
IXValidatorsCount KeyPrefix = 0x90
SYSCurrentBlock KeyPrefix = 0xc0 SYSCurrentBlock KeyPrefix = 0xc0
SYSCurrentHeader KeyPrefix = 0xc1 SYSCurrentHeader KeyPrefix = 0xc1
SYSVersion KeyPrefix = 0xf0 SYSVersion KeyPrefix = 0xf0

View file

@ -17,7 +17,6 @@ var (
STContract, STContract,
STStorage, STStorage,
IXHeaderHashList, IXHeaderHashList,
IXValidatorsCount,
SYSCurrentBlock, SYSCurrentBlock,
SYSCurrentHeader, SYSCurrentHeader,
SYSVersion, SYSVersion,
@ -33,7 +32,6 @@ var (
0x50, 0x50,
0x70, 0x70,
0x80, 0x80,
0x90,
0xc0, 0xc0,
0xc1, 0xc1,
0xf0, 0xf0,

View file

@ -1,43 +0,0 @@
package transaction
import (
"math/rand"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io"
)
// EnrollmentTX transaction represents an enrollment form, which indicates
// that the sponsor of the transaction would like to sign up as a validator.
// The way to sign up is: To construct an EnrollmentTransaction type of transaction,
// and send a deposit to the address of the PublicKey.
// The way to cancel the registration is: Spend the deposit on the address of the PublicKey.
type EnrollmentTX struct {
// PublicKey of the validator.
PublicKey keys.PublicKey
}
// NewEnrollmentTX creates Transaction of EnrollmentType type.
func NewEnrollmentTX(enrollment *EnrollmentTX) *Transaction {
return &Transaction{
Type: EnrollmentType,
Version: 0,
Nonce: rand.Uint32(),
Data: enrollment,
Attributes: []Attribute{},
Inputs: []Input{},
Outputs: []Output{},
Scripts: []Witness{},
Trimmed: false,
}
}
// DecodeBinary implements Serializable interface.
func (tx *EnrollmentTX) DecodeBinary(r *io.BinReader) {
tx.PublicKey.DecodeBinary(r)
}
// EncodeBinary implements Serializable interface.
func (tx *EnrollmentTX) EncodeBinary(w *io.BinWriter) {
tx.PublicKey.EncodeBinary(w)
}

View file

@ -1,17 +0,0 @@
package transaction
//TODO NEO3.0: Update bynary
/*
func TestEncodeDecodeEnrollment(t *testing.T) {
rawtx := "200002ff8ac54687f36bbc31a91b730cc385da8af0b581f2d59d82b5cfef824fd271f60001d3d3b7028d61fea3b7803fda3d7f0a1f7262d38e5e1c8987b0313e0a94574151000001e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c60005441d11600000050ac4949596f5b62fef7be4d1c3e494e6048ed4a01414079d78189d591097b17657a62240c93595e8233dc81157ea2cd477813f09a11fd72845e6bd97c5a3dda125985ea3d5feca387e9933649a9a671a69ab3f6301df6232102ff8ac54687f36bbc31a91b730cc385da8af0b581f2d59d82b5cfef824fd271f6ac"
tx := decodeTransaction(rawtx, t)
assert.Equal(t, "988832f693785dcbcb8d5a0e9d5d22002adcbfb1eb6bbeebf8c494fff580e147", tx.Hash().StringLE())
assert.Equal(t, EnrollmentType, tx.Type)
assert.IsType(t, tx.Data, &EnrollmentTX{})
assert.Equal(t, 0, int(tx.Version))
data, err := testserdes.EncodeBinary(tx)
assert.Equal(t, nil, err)
assert.Equal(t, rawtx, hex.EncodeToString(data))
}
*/

View file

@ -1,37 +0,0 @@
package transaction
import (
"math/rand"
"github.com/nspcc-dev/neo-go/pkg/io"
)
// StateTX represents a state transaction.
type StateTX struct {
Descriptors []*StateDescriptor
}
// NewStateTX creates Transaction of StateType type.
func NewStateTX(state *StateTX) *Transaction {
return &Transaction{
Type: StateType,
Version: 0,
Nonce: rand.Uint32(),
Data: state,
Attributes: []Attribute{},
Inputs: []Input{},
Outputs: []Output{},
Scripts: []Witness{},
Trimmed: false,
}
}
// DecodeBinary implements Serializable interface.
func (tx *StateTX) DecodeBinary(r *io.BinReader) {
r.ReadArray(&tx.Descriptors)
}
// EncodeBinary implements Serializable interface.
func (tx *StateTX) EncodeBinary(w *io.BinWriter) {
w.WriteArray(tx.Descriptors)
}

View file

@ -1,81 +0,0 @@
package transaction
import (
"encoding/hex"
"encoding/json"
"github.com/nspcc-dev/neo-go/pkg/io"
)
// DescStateType represents the type of StateDescriptor.
type DescStateType uint8
// Valid DescStateType constants.
const (
Account DescStateType = 0x40
Validator DescStateType = 0x48
)
// StateDescriptor ..
type StateDescriptor struct {
Type DescStateType
Key []byte
Value []byte
Field string
}
// DecodeBinary implements Serializable interface.
func (s *StateDescriptor) DecodeBinary(r *io.BinReader) {
s.Type = DescStateType(r.ReadB())
s.Key = r.ReadVarBytes()
s.Field = r.ReadString()
s.Value = r.ReadVarBytes()
}
// EncodeBinary implements Serializable interface.
func (s *StateDescriptor) EncodeBinary(w *io.BinWriter) {
w.WriteB(byte(s.Type))
w.WriteVarBytes(s.Key)
w.WriteString(s.Field)
w.WriteVarBytes(s.Value)
}
// stateDescriptor is a wrapper for StateDescriptor
type stateDescriptor struct {
Type DescStateType `json:"type"`
Key string `json:"key"`
Value string `json:"value"`
Field string `json:"field"`
}
// MarshalJSON implements json.Marshaler interface.
func (s *StateDescriptor) MarshalJSON() ([]byte, error) {
return json.Marshal(&stateDescriptor{
Type: s.Type,
Key: hex.EncodeToString(s.Key),
Value: hex.EncodeToString(s.Value),
Field: s.Field,
})
}
// UnmarshalJSON implements json.Unmarshaler interface.
func (s *StateDescriptor) UnmarshalJSON(data []byte) error {
t := new(stateDescriptor)
if err := json.Unmarshal(data, t); err != nil {
return err
}
key, err := hex.DecodeString(t.Key)
if err != nil {
return err
}
value, err := hex.DecodeString(t.Value)
if err != nil {
return err
}
s.Key = key
s.Value = value
s.Field = t.Field
s.Type = t.Type
return nil
}

View file

@ -1,31 +0,0 @@
package transaction
//TODO NEO3.0: Update binary
/*
func TestEncodeDecodeState(t *testing.T) {
// transaction taken from testnet 8abf5ebdb9a8223b12109513647f45bd3c0a6cf1a6346d56684cff71ba308724
rawtx := "900001482103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c10a5265676973746572656401010001cb4184f0a96e72656c1fbdd4f75cca567519e909fd43cefcec13d6c6abcb92a1000001e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6000b8fb050109000071f9cf7f0ec74ec0b0f28a92b12e1081574c0af00141408780d7b3c0aadc5398153df5e2f1cf159db21b8b0f34d3994d865433f79fafac41683783c48aef510b67660e3157b701b9ca4dd9946a385d578fba7dd26f4849232103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1ac"
tx := decodeTransaction(rawtx, t)
assert.Equal(t, StateType, tx.Type)
assert.IsType(t, tx.Data, &StateTX{})
assert.Equal(t, "8abf5ebdb9a8223b12109513647f45bd3c0a6cf1a6346d56684cff71ba308724", tx.Hash().StringLE())
assert.Equal(t, 1, len(tx.Inputs))
input := tx.Inputs[0]
assert.Equal(t, "a192cbabc6d613ecfcce43fd09e9197556ca5cf7d4bd1f6c65726ea9f08441cb", input.PrevHash.StringLE())
assert.Equal(t, uint16(0), input.PrevIndex)
s := tx.Data.(*StateTX)
assert.Equal(t, 1, len(s.Descriptors))
descriptor := s.Descriptors[0]
assert.Equal(t, "03c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1", hex.EncodeToString(descriptor.Key))
assert.Equal(t, "Registered", descriptor.Field)
assert.Equal(t, []byte{0x01}, descriptor.Value)
assert.Equal(t, Validator, descriptor.Type)
// Encode
data, err := testserdes.EncodeBinary(tx)
assert.NoError(t, err)
assert.Equal(t, rawtx, hex.EncodeToString(data))
}
*/

View file

@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -164,12 +163,6 @@ func (t *Transaction) decodeData(r *io.BinReader) {
case IssueType: case IssueType:
t.Data = &IssueTX{} t.Data = &IssueTX{}
t.Data.(*IssueTX).DecodeBinary(r) t.Data.(*IssueTX).DecodeBinary(r)
case EnrollmentType:
t.Data = &EnrollmentTX{}
t.Data.(*EnrollmentTX).DecodeBinary(r)
case StateType:
t.Data = &StateTX{}
t.Data.(*StateTX).DecodeBinary(r)
default: default:
r.Err = fmt.Errorf("invalid TX type %x", t.Type) r.Err = fmt.Errorf("invalid TX type %x", t.Type)
} }
@ -282,11 +275,9 @@ type transactionJSON struct {
Scripts []Witness `json:"scripts"` Scripts []Witness `json:"scripts"`
Claims []Input `json:"claims,omitempty"` Claims []Input `json:"claims,omitempty"`
PublicKey *keys.PublicKey `json:"pubkey,omitempty"`
Script string `json:"script,omitempty"` Script string `json:"script,omitempty"`
Gas util.Fixed8 `json:"gas,omitempty"` Gas util.Fixed8 `json:"gas,omitempty"`
Asset *registeredAsset `json:"asset,omitempty"` Asset *registeredAsset `json:"asset,omitempty"`
Descriptors []*StateDescriptor `json:"descriptors,omitempty"`
} }
// MarshalJSON implements json.Marshaler interface. // MarshalJSON implements json.Marshaler interface.
@ -307,8 +298,6 @@ func (t *Transaction) MarshalJSON() ([]byte, error) {
switch t.Type { switch t.Type {
case ClaimType: case ClaimType:
tx.Claims = t.Data.(*ClaimTX).Claims tx.Claims = t.Data.(*ClaimTX).Claims
case EnrollmentType:
tx.PublicKey = &t.Data.(*EnrollmentTX).PublicKey
case InvocationType: case InvocationType:
tx.Script = hex.EncodeToString(t.Data.(*InvocationTX).Script) tx.Script = hex.EncodeToString(t.Data.(*InvocationTX).Script)
tx.Gas = t.Data.(*InvocationTX).Gas tx.Gas = t.Data.(*InvocationTX).Gas
@ -322,8 +311,6 @@ func (t *Transaction) MarshalJSON() ([]byte, error) {
Owner: transaction.Owner, Owner: transaction.Owner,
Admin: address.Uint160ToString(transaction.Admin), Admin: address.Uint160ToString(transaction.Admin),
} }
case StateType:
tx.Descriptors = t.Data.(*StateTX).Descriptors
} }
return json.Marshal(tx) return json.Marshal(tx)
} }
@ -354,10 +341,6 @@ func (t *Transaction) UnmarshalJSON(data []byte) error {
t.Data = &ClaimTX{ t.Data = &ClaimTX{
Claims: tx.Claims, Claims: tx.Claims,
} }
case EnrollmentType:
t.Data = &EnrollmentTX{
PublicKey: *tx.PublicKey,
}
case InvocationType: case InvocationType:
bytes, err := hex.DecodeString(tx.Script) bytes, err := hex.DecodeString(tx.Script)
if err != nil { if err != nil {
@ -381,10 +364,6 @@ func (t *Transaction) UnmarshalJSON(data []byte) error {
Owner: tx.Asset.Owner, Owner: tx.Asset.Owner,
Admin: admin, Admin: admin,
} }
case StateType:
t.Data = &StateTX{
Descriptors: tx.Descriptors,
}
case ContractType: case ContractType:
t.Data = &ContractTX{} t.Data = &ContractTX{}
case IssueType: case IssueType:

View file

@ -154,32 +154,6 @@ func TestMarshalUnmarshalJSONClaimTX(t *testing.T) {
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction)) testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
} }
func TestMarshalUnmarshalJSONEnrollmentTX(t *testing.T) {
str := "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"
pubKey, err := keys.NewPublicKeyFromString(str)
require.NoError(t, err)
tx := &Transaction{
Type: EnrollmentType,
Version: 5,
Data: &EnrollmentTX{PublicKey: *pubKey},
Attributes: []Attribute{},
Inputs: []Input{{
PrevHash: util.Uint256{5, 6, 7, 8},
PrevIndex: uint16(12),
}},
Outputs: []Output{{
AssetID: util.Uint256{1, 2, 3},
Amount: util.Fixed8FromInt64(1),
ScriptHash: util.Uint160{1, 2, 3},
Position: 0,
}},
Scripts: []Witness{},
Trimmed: false,
}
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
}
func TestMarshalUnmarshalJSONInvocationTX(t *testing.T) { func TestMarshalUnmarshalJSONInvocationTX(t *testing.T) {
tx := &Transaction{ tx := &Transaction{
Type: InvocationType, Type: InvocationType,
@ -236,33 +210,3 @@ func TestMarshalUnmarshalJSONRegisterTX(t *testing.T) {
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction)) testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
} }
func TestMarshalUnmarshalJSONStateTX(t *testing.T) {
tx := &Transaction{
Type: StateType,
Version: 5,
Data: &StateTX{
Descriptors: []*StateDescriptor{&StateDescriptor{
Type: Validator,
Key: []byte{1, 2, 3},
Value: []byte{4, 5, 6},
Field: "Field",
}},
},
Attributes: []Attribute{},
Inputs: []Input{{
PrevHash: util.Uint256{5, 6, 7, 8},
PrevIndex: uint16(12),
}},
Outputs: []Output{{
AssetID: util.Uint256{1, 2, 3},
Amount: util.Fixed8FromInt64(1),
ScriptHash: util.Uint160{1, 2, 3},
Position: 0,
}},
Scripts: []Witness{},
Trimmed: false,
}
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
}

View file

@ -14,10 +14,8 @@ const (
MinerType TXType = 0x00 MinerType TXType = 0x00
IssueType TXType = 0x01 IssueType TXType = 0x01
ClaimType TXType = 0x02 ClaimType TXType = 0x02
EnrollmentType TXType = 0x20
RegisterType TXType = 0x40 RegisterType TXType = 0x40
ContractType TXType = 0x80 ContractType TXType = 0x80
StateType TXType = 0x90
InvocationType TXType = 0xd1 InvocationType TXType = 0xd1
) )
@ -30,14 +28,10 @@ func (t TXType) String() string {
return "IssueTransaction" return "IssueTransaction"
case ClaimType: case ClaimType:
return "ClaimTransaction" return "ClaimTransaction"
case EnrollmentType:
return "EnrollmentTransaction"
case RegisterType: case RegisterType:
return "RegisterTransaction" return "RegisterTransaction"
case ContractType: case ContractType:
return "ContractTransaction" return "ContractTransaction"
case StateType:
return "StateTransaction"
case InvocationType: case InvocationType:
return "InvocationTransaction" return "InvocationTransaction"
default: default:
@ -70,14 +64,10 @@ func TXTypeFromString(jsonString string) (TXType, error) {
return IssueType, nil return IssueType, nil
case "ClaimTransaction": case "ClaimTransaction":
return ClaimType, nil return ClaimType, nil
case "EnrollmentTransaction":
return EnrollmentType, nil
case "RegisterTransaction": case "RegisterTransaction":
return RegisterType, nil return RegisterType, nil
case "ContractTransaction": case "ContractTransaction":
return ContractType, nil return ContractType, nil
case "StateTransaction":
return StateType, nil
case "InvocationTransaction": case "InvocationTransaction":
return InvocationType, nil return InvocationType, nil
default: default:

View file

@ -8,8 +8,10 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
) )
@ -86,6 +88,7 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error)
&governingTokenTX, &governingTokenTX,
&utilityTokenTX, &utilityTokenTX,
issueTx, issueTx,
deployNativeContracts(),
}, },
} }
@ -96,6 +99,22 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error)
return b, nil return b, nil
} }
func deployNativeContracts() *transaction.Transaction {
buf := io.NewBufBinWriter()
emit.Syscall(buf.BinWriter, "Neo.Native.Deploy")
script := buf.Bytes()
tx := transaction.NewInvocationTX(script, 0)
tx.Nonce = 0
tx.Sender = hash.Hash160([]byte{byte(opcode.PUSH1)})
tx.Scripts = []transaction.Witness{
{
InvocationScript: []byte{},
VerificationScript: []byte{byte(opcode.PUSH1)},
},
}
return tx
}
func init() { func init() {
admin := hash.Hash160([]byte{byte(opcode.OLDPUSH1)}) admin := hash.Hash160([]byte{byte(opcode.OLDPUSH1)})
registerTX := &transaction.RegisterTX{ registerTX := &transaction.RegisterTX{

View file

@ -20,7 +20,7 @@ func TestGenesisBlockMainNet(t *testing.T) {
// have been changed. Consequently, hash of the genesis block has been changed. // have been changed. Consequently, hash of the genesis block has been changed.
// Update expected genesis block hash for better times. // Update expected genesis block hash for better times.
// Old hash is "d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf" // Old hash is "d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf"
expect := "931161239581bcd90b73d092483dee6b7bf2162e66df7e15c3a276a22b4c9422" expect := "094c2c2db5dcb868d85aa4d652aed23bc67e7166f53223a228e382265b1be84b"
assert.Equal(t, expect, block.Hash().StringLE()) assert.Equal(t, expect, block.Hash().StringLE())
} }

View file

@ -35,6 +35,16 @@ func (keys *PublicKeys) DecodeBytes(data []byte) error {
return b.Err return b.Err
} }
// Bytes encodes PublicKeys to the new slice of bytes.
func (keys *PublicKeys) Bytes() []byte {
buf := io.NewBufBinWriter()
buf.WriteArray(*keys)
if buf.Err != nil {
panic(buf.Err)
}
return buf.Bytes()
}
// Contains checks whether passed param contained in PublicKeys. // Contains checks whether passed param contained in PublicKeys.
func (keys PublicKeys) Contains(pKey *PublicKey) bool { func (keys PublicKeys) Contains(pKey *PublicKey) bool {
for _, key := range keys { for _, key := range keys {

View file

@ -96,13 +96,13 @@ func (chain testChain) GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog {
func (chain testChain) GetNEP5Balances(util.Uint160) *state.NEP5Balances { func (chain testChain) GetNEP5Balances(util.Uint160) *state.NEP5Balances {
panic("TODO") panic("TODO")
} }
func (chain testChain) GetValidators(...*transaction.Transaction) ([]*keys.PublicKey, error) { func (chain testChain) GetValidators() ([]*keys.PublicKey, error) {
panic("TODO") panic("TODO")
} }
func (chain testChain) GetStandByValidators() (keys.PublicKeys, error) { func (chain testChain) GetStandByValidators() (keys.PublicKeys, error) {
panic("TODO") panic("TODO")
} }
func (chain testChain) GetEnrollments() ([]*state.Validator, error) { func (chain testChain) GetEnrollments() ([]state.Validator, error) {
panic("TODO") panic("TODO")
} }
func (chain testChain) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) { func (chain testChain) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) {

View file

@ -11,7 +11,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core" "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/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/rpc/request" "github.com/nspcc-dev/neo-go/pkg/rpc/request"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
@ -49,7 +48,6 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
Version: 0, Version: 0,
ScriptHash: scriptHash, ScriptHash: scriptHash,
IsFrozen: false, IsFrozen: false,
Votes: []*keys.PublicKey{},
Balances: result.Balances{ Balances: result.Balances{
result.Balance{ result.Balance{
Asset: core.GoverningTokenID(), Asset: core.GoverningTokenID(),

View file

@ -5,7 +5,6 @@ import (
"sort" "sort"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
) )
@ -15,7 +14,6 @@ type AccountState struct {
Version uint8 `json:"version"` Version uint8 `json:"version"`
ScriptHash util.Uint160 `json:"script_hash"` ScriptHash util.Uint160 `json:"script_hash"`
IsFrozen bool `json:"frozen"` IsFrozen bool `json:"frozen"`
Votes []*keys.PublicKey `json:"votes"`
Balances []Balance `json:"balances"` Balances []Balance `json:"balances"`
} }
@ -48,7 +46,6 @@ func NewAccountState(a *state.Account) AccountState {
Version: a.Version, Version: a.Version,
ScriptHash: a.ScriptHash, ScriptHash: a.ScriptHash,
IsFrozen: a.IsFrozen, IsFrozen: a.IsFrozen,
Votes: a.Votes,
Balances: balances, Balances: balances,
} }
} }

View file

@ -2,13 +2,12 @@ package result
import ( import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
) )
// Validator used for the representation of // Validator used for the representation of
// state.Validator on the RPC Server. // state.Validator on the RPC Server.
type Validator struct { type Validator struct {
PublicKey keys.PublicKey `json:"publickey"` PublicKey keys.PublicKey `json:"publickey"`
Votes util.Fixed8 `json:"votes"` Votes int64 `json:"votes,string"`
Active bool `json:"active"` Active bool `json:"active"`
} }

View file

@ -815,9 +815,9 @@ func (s *Server) getValidators(_ request.Params) (interface{}, error) {
var res []result.Validator var res []result.Validator
for _, v := range enrollments { for _, v := range enrollments {
res = append(res, result.Validator{ res = append(res, result.Validator{
PublicKey: *v.PublicKey, PublicKey: *v.Key,
Votes: v.Votes, Votes: v.Votes.Int64(),
Active: validators.Contains(v.PublicKey), Active: validators.Contains(v.Key),
}) })
} }
return res, nil return res, nil

View file

@ -408,7 +408,7 @@ var rpcTestCases = map[string][]rpcTestCase{
"getblockheader": { "getblockheader": {
{ {
name: "invalid verbose type", name: "invalid verbose type",
params: `["614a9085dc55fd0539ad3a9d68d8b8e7c52328da905c87bfe8cfca57a5c3c02f", true]`, params: `["9673799c5b5a294427401cb07d6cc615ada3a0d5c5bf7ed6f0f54f24abb2e2ac", true]`,
fail: true, fail: true,
}, },
{ {

Binary file not shown.