diff --git a/config/protocol.mainnet.yml b/config/protocol.mainnet.yml index a2a10721b..302717b7f 100644 --- a/config/protocol.mainnet.yml +++ b/config/protocol.mainnet.yml @@ -24,7 +24,6 @@ ProtocolConfiguration: - seed9.ngd.network:10333 - seed10.ngd.network:10333 SystemFee: - EnrollmentTransaction: 1000 IssueTransaction: 500 RegisterTransaction: 10000 VerifyBlocks: true diff --git a/config/protocol.privnet.docker.four.yml b/config/protocol.privnet.docker.four.yml index 9addb3070..e9bc5e8e1 100644 --- a/config/protocol.privnet.docker.four.yml +++ b/config/protocol.privnet.docker.four.yml @@ -15,7 +15,6 @@ ProtocolConfiguration: - 172.200.0.3:20335 - 172.200.0.4:20336 SystemFee: - EnrollmentTransaction: 1000 IssueTransaction: 500 RegisterTransaction: 10000 VerifyBlocks: true diff --git a/config/protocol.privnet.docker.one.yml b/config/protocol.privnet.docker.one.yml index 365922969..06bbad06e 100644 --- a/config/protocol.privnet.docker.one.yml +++ b/config/protocol.privnet.docker.one.yml @@ -15,7 +15,6 @@ ProtocolConfiguration: - 172.200.0.3:20335 - 172.200.0.4:20336 SystemFee: - EnrollmentTransaction: 1000 IssueTransaction: 500 RegisterTransaction: 10000 VerifyBlocks: true diff --git a/config/protocol.privnet.docker.single.yml b/config/protocol.privnet.docker.single.yml index 015052fb4..5fdb643bc 100644 --- a/config/protocol.privnet.docker.single.yml +++ b/config/protocol.privnet.docker.single.yml @@ -9,7 +9,6 @@ ProtocolConfiguration: SeedList: - 172.200.0.1:20333 SystemFee: - EnrollmentTransaction: 1000 IssueTransaction: 500 RegisterTransaction: 10000 VerifyBlocks: true diff --git a/config/protocol.privnet.docker.three.yml b/config/protocol.privnet.docker.three.yml index 89d5e59c5..a59daccbe 100644 --- a/config/protocol.privnet.docker.three.yml +++ b/config/protocol.privnet.docker.three.yml @@ -15,7 +15,6 @@ ProtocolConfiguration: - 172.200.0.3:20335 - 172.200.0.4:20336 SystemFee: - EnrollmentTransaction: 1000 IssueTransaction: 500 RegisterTransaction: 10000 VerifyBlocks: true diff --git a/config/protocol.privnet.docker.two.yml b/config/protocol.privnet.docker.two.yml index 6b4fda670..082ba83b2 100644 --- a/config/protocol.privnet.docker.two.yml +++ b/config/protocol.privnet.docker.two.yml @@ -15,7 +15,6 @@ ProtocolConfiguration: - 172.200.0.3:20335 - 172.200.0.4:20336 SystemFee: - EnrollmentTransaction: 1000 IssueTransaction: 500 RegisterTransaction: 10000 VerifyBlocks: true diff --git a/config/protocol.testnet.yml b/config/protocol.testnet.yml index c36540d57..4a483bfcc 100644 --- a/config/protocol.testnet.yml +++ b/config/protocol.testnet.yml @@ -24,7 +24,6 @@ ProtocolConfiguration: - seed9.ngd.network:20333 - seed10.ngd.network:20333 SystemFee: - EnrollmentTransaction: 10 IssueTransaction: 5 RegisterTransaction: 100 VerifyBlocks: true diff --git a/config/protocol.unit_testnet.yml b/config/protocol.unit_testnet.yml index d445c7cb9..2c809c523 100644 --- a/config/protocol.unit_testnet.yml +++ b/config/protocol.unit_testnet.yml @@ -14,7 +14,6 @@ ProtocolConfiguration: - 127.0.0.1:20335 - 127.0.0.1:20336 SystemFee: - EnrollmentTransaction: 1000 IssueTransaction: 500 RegisterTransaction: 10000 VerifyBlocks: true diff --git a/pkg/config/protocol_config.go b/pkg/config/protocol_config.go index 405f62b1d..421e3ff9b 100644 --- a/pkg/config/protocol_config.go +++ b/pkg/config/protocol_config.go @@ -47,9 +47,8 @@ type ( // SystemFee fees related to system. SystemFee struct { - EnrollmentTransaction int64 `yaml:"EnrollmentTransaction"` - IssueTransaction int64 `yaml:"IssueTransaction"` - RegisterTransaction int64 `yaml:"RegisterTransaction"` + IssueTransaction int64 `yaml:"IssueTransaction"` + RegisterTransaction int64 `yaml:"RegisterTransaction"` } // NetMode describes the mode the blockchain will operate on. @@ -75,8 +74,6 @@ func (n NetMode) String() string { // TryGetValue returns the system fee base on transaction type. func (s SystemFee) TryGetValue(txType transaction.TXType) util.Fixed8 { switch txType { - case transaction.EnrollmentType: - return util.Fixed8FromInt64(s.EnrollmentTransaction) case transaction.IssueType: return util.Fixed8FromInt64(s.IssueTransaction) case transaction.RegisterType: diff --git a/pkg/consensus/consensus.go b/pkg/consensus/consensus.go index 144c18897..151160622 100644 --- a/pkg/consensus/consensus.go +++ b/pkg/consensus/consensus.go @@ -479,22 +479,12 @@ func (s *service) getVerifiedTx(count int) []block.Transaction { return res } -func (s *service) getValidators(txx ...block.Transaction) []crypto.PublicKey { +func (s *service) getValidators(_ ...block.Transaction) []crypto.PublicKey { var ( pKeys []*keys.PublicKey err error ) - if len(txx) == 0 { - 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...) - } - + pKeys, err = s.Chain.GetValidators() if err != nil { s.log.Error("error while trying to get validators", zap.Error(err)) } diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index aab57ddd6..af99c5de1 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -197,9 +197,6 @@ func (bc *Blockchain) init() error { if err != nil { return err } - if err := bc.initNative(); err != nil { - return err - } return bc.storeBlock(genesisBlock) } if ver != version { @@ -272,27 +269,6 @@ func (bc *Blockchain) init() error { 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. func (bc *Blockchain) Run() { persistTimer := time.NewTimer(persistInterval) @@ -547,9 +523,6 @@ func (bc *Blockchain) storeBlock(block *block.Block) error { if err != nil { return err } - if err = processTXWithValidatorsSubtract(prevTXOutput, account, cache); err != nil { - return err - } } balancesLen := len(account.Balances[prevTXOutput.AssetID]) @@ -657,14 +630,6 @@ func (bc *Blockchain) storeBlock(block *block.Block) error { 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: systemInterop := bc.newInteropContext(trigger.Application, cache, block, tx) v := SpawnVM(systemInterop) @@ -847,11 +812,6 @@ func (bc *Blockchain) LastBatch() *storage.MemBatch { 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. func processOutputs(tx *transaction.Transaction, dao *dao.Cached) error { 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 { 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 } @@ -1364,63 +1251,6 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e if inv.Gas.FractionalValue() != 0 { 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) @@ -1699,135 +1529,14 @@ func (bc *Blockchain) GetStandByValidators() (keys.PublicKeys, error) { return getValidators(bc.config) } -// GetValidators returns validators. -// Golang implementation of GetValidators method in C# (https://github.com/neo-project/neo/blob/c64748ecbac3baeb8045b16af0d518398a6ced24/neo/Persistence/Snapshot.cs#L182) -func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.PublicKey, error) { - 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 - group := make(map[util.Uint256][]*transaction.Input) - for i := range tx.Inputs { - 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) +// GetValidators returns next block validators. +func (bc *Blockchain) GetValidators() ([]*keys.PublicKey, error) { + return bc.contracts.NEO.GetNextBlockValidatorsInternal(bc, bc.dao) } -// 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) +// GetEnrollments returns all registered validators. +func (bc *Blockchain) GetEnrollments() ([]state.Validator, error) { + return bc.contracts.NEO.GetRegisteredValidators(bc.dao) } // 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 { hashes[refs[i].Out.ScriptHash] = true } - case transaction.EnrollmentType: - etx := t.Data.(*transaction.EnrollmentTX) - hashes[etx.PublicKey.GetScriptHash()] = true case transaction.IssueType: for _, res := range refsAndOutsToResults(references, t.Outputs) { if res.Amount < 0 { @@ -1895,31 +1601,6 @@ func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([ case transaction.RegisterType: reg := t.Data.(*transaction.RegisterTX) 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 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 { - ic := interop.NewContext(trigger, bc, d, block, tx, bc.log) + ic := interop.NewContext(trigger, bc, d, bc.contracts.Contracts, block, tx, bc.log) switch { case tx != nil: ic.Container = tx diff --git a/pkg/core/blockchainer/blockchainer.go b/pkg/core/blockchainer/blockchainer.go index 0393a4059..d749ec995 100644 --- a/pkg/core/blockchainer/blockchainer.go +++ b/pkg/core/blockchainer/blockchainer.go @@ -24,7 +24,7 @@ type Blockchainer interface { HeaderHeight() uint32 GetBlock(hash util.Uint256) (*block.Block, error) GetContractState(hash util.Uint160) *state.Contract - GetEnrollments() ([]*state.Validator, error) + GetEnrollments() ([]state.Validator, error) GetHeaderHash(int) util.Uint256 GetHeader(hash util.Uint256) (*block.Header, error) CurrentHeaderHash() util.Uint256 @@ -36,7 +36,7 @@ type Blockchainer interface { GetAppExecResult(util.Uint256) (*state.AppExecResult, error) GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog GetNEP5Balances(util.Uint160) *state.NEP5Balances - GetValidators(txes ...*transaction.Transaction) ([]*keys.PublicKey, error) + GetValidators() ([]*keys.PublicKey, error) GetStandByValidators() (keys.PublicKeys, error) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem diff --git a/pkg/core/dao/dao.go b/pkg/core/dao/dao.go index c5a13a338..f803078a4 100644 --- a/pkg/core/dao/dao.go +++ b/pkg/core/dao/dao.go @@ -10,7 +10,6 @@ import ( "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/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/util" ) @@ -20,7 +19,6 @@ type DAO interface { AppendNEP5Transfer(acc util.Uint160, index uint32, tr *state.NEP5Transfer) (bool, error) DeleteContractState(hash util.Uint160) error DeleteStorageItem(scripthash util.Uint160, key []byte) error - DeleteValidatorState(vs *state.Validator) error GetAccountState(hash util.Uint160) (*state.Account, error) GetAccountStateOrNew(hash util.Uint160) (*state.Account, error) GetAndDecode(entity io.Serializable, key []byte) error @@ -32,18 +30,13 @@ type DAO interface { GetCurrentBlockHeight() (uint32, error) GetCurrentHeaderHeight() (i uint32, h util.Uint256, err error) GetHeaderHashes() ([]util.Uint256, error) - GetNativeContractState(h util.Uint160) ([]byte, error) GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error) GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error) - GetNextBlockValidators() (keys.PublicKeys, error) GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem 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) 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) GetWrapped() DAO HasTransaction(hash util.Uint256) bool @@ -55,14 +48,10 @@ type DAO interface { PutAssetState(as *state.Asset) error PutContractState(cs *state.Contract) error PutCurrentHeader(hashAndIndex []byte) error - PutNativeContractState(h util.Uint160, value []byte) error PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) 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 PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin) error - PutValidatorState(vs *state.Validator) error - PutValidatorsCount(vc *state.ValidatorsCount) error PutVersion(v string) error StoreAsBlock(block *block.Block, sysFee uint32) error StoreAsCurrentBlock(block *block.Block) error @@ -211,18 +200,6 @@ func (dao *Simple) DeleteContractState(hash util.Uint160) error { 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. // -- start nep5 balances. @@ -324,111 +301,6 @@ func (dao *Simple) putUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin // -- 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. // 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. 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 err error + lookupKey := storage.AppendPrefix(storage.STStorage, hash.BytesLE()) + if prefix != nil { + lookupKey = append(lookupKey, prefix...) + } saveToMap := func(k, v []byte) { if err != nil { return @@ -501,9 +383,9 @@ func (dao *Simple) GetStorageItems(hash util.Uint160) (map[string]*state.Storage } // 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 { return nil, err } diff --git a/pkg/core/dao/dao_test.go b/pkg/core/dao/dao_test.go index 24ec938f5..65e74b50e 100644 --- a/pkg/core/dao/dao_test.go +++ b/pkg/core/dao/dao_test.go @@ -113,61 +113,6 @@ func TestPutGetUnspentCoinState(t *testing.T) { 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) { dao := NewSimple(storage.NewMemoryStore()) hash := random.Uint256() diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index a198fbf71..f54b1ad23 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -195,7 +195,7 @@ func TestCreateBasicChain(t *testing.T) { // use output of issue tx from genesis block as an input genesisBlock, err := bc.GetBlock(bc.GetHeaderHash(0)) require.NoError(t, err) - require.Equal(t, 4, len(genesisBlock.Transactions)) + require.Equal(t, 5, len(genesisBlock.Transactions)) h := genesisBlock.Transactions[3].Hash() txMoveNeo.AddInput(&transaction.Input{ PrevHash: h, diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go index d384fb3d1..ff42e8fa4 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -7,8 +7,14 @@ import ( "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/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/util" "github.com/nspcc-dev/neo-go/pkg/vm" + "github.com/nspcc-dev/neo-go/pkg/vm/emit" "go.uber.org/zap" ) @@ -16,6 +22,7 @@ import ( type Context struct { Chain blockchainer.Blockchainer Container crypto.Verifiable + Natives []Contract Trigger trigger.Type Block *block.Block Tx *transaction.Transaction @@ -25,11 +32,12 @@ type Context struct { } // 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) nes := make([]state.NotificationEvent, 0) return &Context{ Chain: bc, + Natives: natives, Trigger: trigger, Block: block, Tx: tx, @@ -48,3 +56,64 @@ type Function struct { Func func(*Context, *vm.VM) error 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, + }) +} diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index c43b85ccb..065ecd8e5 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -236,13 +236,6 @@ func witnessGetVerificationScript(ic *interop.Context, v *vm.VM) error { 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. func popInputFromVM(v *vm.VM) (*transaction.Input, error) { inInterface := v.Estack().Pop().Value() @@ -404,24 +397,6 @@ func accountGetScriptHash(ic *interop.Context, v *vm.VM) error { 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. func accountIsStandard(ic *interop.Context, v *vm.VM) error { accbytes := v.Estack().Pop().Bytes() diff --git a/pkg/core/interop_neo_test.go b/pkg/core/interop_neo_test.go index 92603efd8..d7625d681 100644 --- a/pkg/core/interop_neo_test.go +++ b/pkg/core/interop_neo_test.go @@ -417,17 +417,6 @@ func TestAccountGetScriptHash(t *testing.T) { 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) { v, contractState, context, chain := createVMAndContractState(t) defer chain.Close() @@ -603,9 +592,6 @@ func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interop.Context hash, err := util.Uint160DecodeStringBE(rawHash) accountState := state.NewAccount(hash) - key := &keys.PublicKey{X: big.NewInt(1), Y: big.NewInt(1)} - accountState.Votes = []*keys.PublicKey{key} - require.NoError(t, err) chain := newTestChain(t) context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, nil) diff --git a/pkg/core/interops.go b/pkg/core/interops.go index ccb040bd6..0f2c2c203 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -115,7 +115,6 @@ var systemInterops = []interop.Function{ var neoInterops = []interop.Function{ {Name: "Neo.Account.GetBalance", Func: accountGetBalance, 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.Asset.Create", Func: assetCreate, Price: 0}, {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.GetTransaction", Func: bcGetTransaction, 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.Destroy", Func: contractDestroy, Price: 1}, {Name: "Neo.Contract.GetScript", Func: contractGetScript, Price: 1}, @@ -204,7 +202,6 @@ var neoInterops = []interop.Function{ // Old compatibility APIs. {Name: "AntShares.Account.GetBalance", Func: accountGetBalance, 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.GetAdmin", Func: assetGetAdmin, 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.GetHeight", Func: bcGetHeight, Price: 1}, {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.Destroy", Func: contractDestroy, Price: 1}, {Name: "AntShares.Contract.GetScript", Func: contractGetScript, Price: 1}, diff --git a/pkg/core/interops_test.go b/pkg/core/interops_test.go index 2f7b47b74..70dd25c8c 100644 --- a/pkg/core/interops_test.go +++ b/pkg/core/interops_test.go @@ -34,7 +34,6 @@ func TestUnexpectedNonInterops(t *testing.T) { funcs := []func(*interop.Context, *vm.VM) error{ accountGetBalance, accountGetScriptHash, - accountGetVotes, assetGetAdmin, assetGetAmount, assetGetAssetID, diff --git a/pkg/core/native/contract.go b/pkg/core/native/contract.go index 332276320..a4b83e751 100644 --- a/pkg/core/native/contract.go +++ b/pkg/core/native/contract.go @@ -4,80 +4,20 @@ import ( "fmt" "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/vm" - "github.com/nspcc-dev/neo-go/pkg/vm/emit" "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. type Contracts struct { NEO *NEO GAS *GAS - Contracts []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 + Contracts []interop.Contract } // 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 { if ctr.Metadata().Hash.Equals(h) { return ctr @@ -87,7 +27,7 @@ func (cs *Contracts) ByHash(h util.Uint160) Contract { } // 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 { if ctr.Metadata().ServiceID == id { return ctr @@ -96,33 +36,21 @@ func (cs *Contracts) ByID(id uint32) Contract { return nil } -// 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, - }) -} - -// NewContracts returns new empty set of native contracts. +// NewContracts returns new set of native contracts with new GAS and NEO +// contracts. func NewContracts() *Contracts { - return &Contracts{ - Contracts: []Contract{}, - } -} + cs := new(Contracts) -// Add adds new native contracts to the list. -func (cs *Contracts) Add(c Contract) { - cs.Contracts = append(cs.Contracts, c) + gas := NewGAS() + neo := NewNEO() + 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. @@ -139,7 +67,7 @@ func (cs *Contracts) GetNativeInterop(ic *interop.Context) func(uint32) *vm.Inte } // 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 { h := v.GetContextScriptHash(0) if !h.Equals(c.Metadata().Hash) { diff --git a/pkg/core/native/interop.go b/pkg/core/native/interop.go index 6b6cec16e..b535313f9 100644 --- a/pkg/core/native/interop.go +++ b/pkg/core/native/interop.go @@ -2,6 +2,7 @@ package native import ( "errors" + "fmt" "github.com/nspcc-dev/neo-go/pkg/core/interop" "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 { 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 } diff --git a/pkg/core/native/native_gas.go b/pkg/core/native/native_gas.go index 87736f0de..cdc0cd97b 100644 --- a/pkg/core/native/native_gas.go +++ b/pkg/core/native/native_gas.go @@ -6,14 +6,12 @@ import ( "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/storage" "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/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/vm" - "github.com/nspcc-dev/neo-go/pkg/vm/emit" ) // GAS represents GAS native contract. @@ -24,13 +22,17 @@ type GAS struct { 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. func NewGAS() *GAS { nep5 := newNEP5Native(gasSyscallName) nep5.name = "GAS" nep5.symbol = "gas" nep5.decimals = 8 - nep5.factor = 100000000 + nep5.factor = GASFactor g := &GAS{nep5TokenNative: *nep5} @@ -44,44 +46,35 @@ func NewGAS() *GAS { return g } -// initFromStore initializes variable contract parameters from the store. -func (g *GAS) initFromStore(data []byte) error { - g.totalSupply = *emit.BytesToInt(data) - return nil -} - -func (g *GAS) serializeState() []byte { - return emit.IntToBytes(&g.totalSupply) -} - -func (g *GAS) increaseBalance(_ *interop.Context, acc *state.Account, amount *big.Int) error { +func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.StorageItem, amount *big.Int) error { + acc, err := state.NEP5BalanceStateFromBytes(si.Value) + if err != nil { + return err + } if sign := amount.Sign(); sign == 0 { 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") } - acc.GAS.Balance.Add(&acc.GAS.Balance, amount) + acc.Balance.Add(&acc.Balance, amount) + si.Value = acc.Bytes() return nil } // Initialize initializes GAS contract. func (g *GAS) Initialize(ic *interop.Context) error { - data, err := ic.DAO.GetNativeContractState(g.Hash) - if err == nil { - return g.initFromStore(data) - } else if err != storage.ErrKeyNotFound { + if err := g.nep5TokenNative.Initialize(ic); err != nil { return err } - - if err := g.nep5TokenNative.Initialize(); err != nil { - return err + if g.nep5TokenNative.getTotalSupply(ic).Sign() != 0 { + return errors.New("already initialized") } h, _, err := getStandbyValidatorsHash(ic) if err != nil { return err } - g.mint(ic, h, big.NewInt(30000000*g.factor)) - return ic.DAO.PutNativeContractState(g.Hash, g.serializeState()) + g.mint(ic, h, big.NewInt(initialGAS*GASFactor)) + return nil } // OnPersist implements Contract interface. @@ -95,7 +88,7 @@ func (g *GAS) OnPersist(ic *interop.Context) error { // netFee += tx.NetworkFee //} //g.mint(ic, , netFee) - return ic.DAO.PutNativeContractState(g.Hash, g.serializeState()) + return nil } func (g *GAS) getSysFeeAmount(ic *interop.Context, args []vm.StackItem) vm.StackItem { diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index 71498a187..f159b18ad 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -9,13 +9,11 @@ import ( "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/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/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/vm" - "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/pkg/errors" ) @@ -25,7 +23,40 @@ type NEO struct { 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. func NewNEO() *NEO { @@ -55,7 +86,7 @@ func NewNEO() *NEO { n.AddMethod(md, desc, false) desc = newDescriptor("getRegisteredValidators", smartcontract.ArrayType) - md = newMethodAndPrice(n.getRegisteredValidators, 1, smartcontract.NoneFlag) + md = newMethodAndPrice(n.getRegisteredValidatorsCall, 1, smartcontract.NoneFlag) n.AddMethod(md, desc, true) desc = newDescriptor("getValidators", smartcontract.ArrayType) @@ -74,22 +105,26 @@ func NewNEO() *NEO { // Initialize initializes NEO contract. func (n *NEO) Initialize(ic *interop.Context) error { - data, err := ic.DAO.GetNativeContractState(n.Hash) - if err == nil { - return n.initFromStore(data) - } else if err != storage.ErrKeyNotFound { + var si state.StorageItem + + if err := n.nep5TokenNative.Initialize(ic); err != nil { return err } - if err := n.nep5TokenNative.Initialize(); err != nil { - return err + if n.nep5TokenNative.getTotalSupply(ic).Sign() != 0 { + 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) if err != nil { return err } - n.mint(ic, h, big.NewInt(100000000*n.factor)) + n.mint(ic, h, big.NewInt(NEOTotalSupply)) for i := range vs { 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 } -func (n *NEO) serializeState() []byte { - return emit.IntToBytes(&n.totalSupply) -} - // OnPersist implements Contract interface. func (n *NEO) OnPersist(ic *interop.Context) error { pubs, err := n.GetValidatorsInternal(ic.Chain, ic.DAO) if err != nil { return err } - if err := ic.DAO.PutNextBlockValidators(pubs); err != nil { - return err - } - return ic.DAO.PutNativeContractState(n.Hash, n.serializeState()) + si := new(state.StorageItem) + si.Value = pubs.Bytes() + return ic.DAO.PutStorageItem(n.Hash, nextValidatorsKey, si) } -func (n *NEO) increaseBalance(ic *interop.Context, acc *state.Account, amount *big.Int) error { - if sign := amount.Sign(); sign == 0 { - 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) +func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int) error { + acc, err := state.NEOBalanceStateFromBytes(si.Value) if err != nil { return err } - acc.NEO.BalanceHeight = ic.Block.Index - n.GAS.mint(ic, acc.ScriptHash, big.NewInt(int64(sys+net))) + if amount.Sign() == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 { + 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 } @@ -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 { - _, err := ic.DAO.GetValidatorState(pub) - if err == nil { - return err + key := makeValidatorKey(pub) + si := ic.DAO.GetStorageItem(n.Hash, key) + 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 { @@ -203,103 +257,147 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public } else if !ok { 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 { return err } - balance := util.Fixed8(acc.NEO.Balance.Int64()) - if err := ModifyAccountVotes(acc, ic.DAO, -balance); err != nil { + if err := n.ModifyAccountVotes(acc, ic.DAO, new(big.Int).Neg(&acc.Balance)); err != nil { return err } pubs = pubs.Unique() + // Check validators registration. var newPubs keys.PublicKeys for _, pub := range pubs { - _, err := ic.DAO.GetValidatorState(pub) - if err != nil { - if err == storage.ErrKeyNotFound { - continue - } - return err + if ic.DAO.GetStorageItem(n.Hash, makeValidatorKey(pub)) == nil { + continue } newPubs = append(newPubs, pub) } 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 { return err } if lv > 0 { - vc[lv-1] -= balance + vc[lv-1].Sub(&vc[lv-1], &acc.Balance) } 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 } } 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). -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 { - 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 { return err } - validator.Votes += value - if validator.UnregisteredAndHasNoVotes() { - if err := d.DeleteValidatorState(validator); err != nil { - return err - } - } else { - if err := d.PutValidatorState(validator); err != nil { - return err - } + votes.Balance.Add(&votes.Balance, value) + si.Value = votes.Bytes() + if err := d.PutStorageItem(n.Hash, key, si); err != nil { + return err } } return nil } -func (n *NEO) getRegisteredValidators(ic *interop.Context, _ []vm.StackItem) vm.StackItem { - vs := ic.DAO.GetValidators() - arr := make([]vm.StackItem, len(vs)) - for i := range vs { +func (n *NEO) getRegisteredValidators(d dao.DAO) ([]keyWithVotes, error) { + siMap, err := d.GetStorageItemsWithPrefix(n.Hash, []byte{prefixValidator}) + if err != nil { + 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{ - vm.NewByteArrayItem(vs[i].PublicKey.Bytes()), - vm.NewBigIntegerItem(big.NewInt(int64(vs[i].Votes))), + vm.NewByteArrayItem([]byte(validators[i].Key)), + vm.NewBigIntegerItem(validators[i].Votes), }) } return vm.NewArrayItem(arr) } // GetValidatorsInternal returns a list of current validators. -func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) ([]*keys.PublicKey, error) { - validatorsCount, err := d.GetValidatorsCount() +func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) { + si := d.GetStorageItem(n.Hash, validatorsCountKey) + if si == nil { + return nil, errors.New("validators count uninitialized") + } + validatorsCount, err := ValidatorsCountFromBytes(si.Value) + if err != nil { + return nil, err + } + validators, err := n.GetRegisteredValidators(d) if err != nil { return nil, err - } else if len(validatorsCount) == 0 { - sb, err := bc.GetStandByValidators() - if err != nil { - return nil, err - } - return sb, nil } - - validators := d.GetValidators() 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. - if validators[i].Votes != validators[j].Votes { - return validators[i].Votes > validators[j].Votes + cmp := validators[i].Votes.Cmp(validators[j].Votes) + if cmp != 0 { + return cmp > 0 } // 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() @@ -314,8 +412,8 @@ func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) ([] uniqueSBValidators := standByValidators.Unique() result := keys.PublicKeys{} for _, validator := range validators { - if validator.RegisteredAndHasVotes() || uniqueSBValidators.Contains(validator.PublicKey) { - result = append(result, validator.PublicKey) + if validator.Votes.Sign() > 0 || uniqueSBValidators.Contains(validator.Key) { + 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. -func (n *NEO) GetNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) ([]*keys.PublicKey, error) { - result, err := d.GetNextBlockValidators() +func (n *NEO) GetNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) { + 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 { return nil, err - } else if result == nil { - return bc.GetStandByValidators() } - return result, nil + return pubs, nil } func pubsToArray(pubs keys.PublicKeys) vm.StackItem { diff --git a/pkg/core/native/native_nep5.go b/pkg/core/native/native_nep5.go index e4476be14..021eb6b9c 100644 --- a/pkg/core/native/native_nep5.go +++ b/pkg/core/native/native_nep5.go @@ -10,28 +10,42 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" "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. type nep5TokenNative struct { - ContractMD - name string - symbol string - decimals int64 - factor int64 - totalSupply big.Int - onPersist func(*interop.Context) error - incBalance func(*interop.Context, *state.Account, *big.Int) error + interop.ContractMD + name string + symbol string + decimals int64 + factor int64 + onPersist func(*interop.Context) 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 } -var _ Contract = (*nep5TokenNative)(nil) +var _ interop.Contract = (*nep5TokenNative)(nil) func newNEP5Native(name string) *nep5TokenNative { - n := &nep5TokenNative{ContractMD: *NewContractMD(name)} + n := &nep5TokenNative{ContractMD: *interop.NewContractMD(name)} desc := newDescriptor("name", smartcontract.StringType) md := newMethodAndPrice(n.Name, 1, smartcontract.NoneFlag) @@ -45,6 +59,10 @@ func newNEP5Native(name string) *nep5TokenNative { md = newMethodAndPrice(n.Decimals, 1, smartcontract.NoneFlag) 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, manifest.NewParameter("account", smartcontract.Hash160Type)) md = newMethodAndPrice(n.balanceOf, 1, smartcontract.NoneFlag) @@ -63,7 +81,7 @@ func newNEP5Native(name string) *nep5TokenNative { return n } -func (c *nep5TokenNative) Initialize() error { +func (c *nep5TokenNative) Initialize(_ *interop.Context) error { return nil } @@ -79,6 +97,23 @@ func (c *nep5TokenNative) Decimals(_ *interop.Context, _ []vm.StackItem) vm.Stac 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 { from := toUint160(args[0]) 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 { if u == nil { - return nil + return vm.NullItem{} } 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") } - accFrom, err := ic.DAO.GetAccountStateOrNew(from) - if err != nil { - return err + keyFrom := makeAccountKey(from) + siFrom := ic.DAO.GetStorageItem(c.Hash, keyFrom) + if siFrom == nil { + return errors.New("insufficient funds") } 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 { 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 } - if err := ic.DAO.PutAccountState(accFrom); err != nil { + if err := ic.DAO.PutStorageItem(c.Hash, keyFrom, siFrom); err != nil { return err } if !isEmpty { - accTo, err := ic.DAO.GetAccountStateOrNew(to) - if err != nil { + keyTo := makeAccountKey(to) + 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 } - if err := c.incBalance(ic, accTo, amount); err != nil { - return err - } - if err := ic.DAO.PutAccountState(accTo); err != nil { + if err := ic.DAO.PutStorageItem(c.Hash, keyTo, siTo); err != nil { return err } } @@ -174,18 +211,24 @@ func (c *nep5TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount return } - acc, err := ic.DAO.GetAccountStateOrNew(h) - if err != nil { + key := makeAccountKey(h) + 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) } - if err := c.incBalance(ic, acc, amount); err != nil { - panic(err) - } - if err := ic.DAO.PutAccountState(acc); err != nil { + if err := ic.DAO.PutStorageItem(c.Hash, key, si); err != nil { 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 { @@ -200,8 +243,8 @@ func newDescriptor(name string, ret smartcontract.ParamType, ps ...manifest.Para } } -func newMethodAndPrice(f Method, price int64, flags smartcontract.CallFlag) *MethodAndPrice { - return &MethodAndPrice{ +func newMethodAndPrice(f interop.Method, price int64, flags smartcontract.CallFlag) *interop.MethodAndPrice { + return &interop.MethodAndPrice{ Func: f, Price: price, RequiredFlags: flags, diff --git a/pkg/core/native/validators_count.go b/pkg/core/native/validators_count.go new file mode 100644 index 000000000..25cfe3c7c --- /dev/null +++ b/pkg/core/native/validators_count.go @@ -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) +} diff --git a/pkg/core/native_contract_test.go b/pkg/core/native_contract_test.go index 38a8a75f4..4e17a36b6 100644 --- a/pkg/core/native_contract_test.go +++ b/pkg/core/native_contract_test.go @@ -6,7 +6,6 @@ import ( "testing" "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/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" @@ -17,11 +16,15 @@ import ( ) type testNative struct { - meta native.ContractMD + meta interop.ContractMD 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 } @@ -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 { tn := &testNative{ - meta: *native.NewContractMD("Test.Native.Sum"), + meta: *interop.NewContractMD("Test.Native.Sum"), blocks: make(chan uint32, 1), } desc := &manifest.Method{ @@ -49,7 +57,7 @@ func newTestNative() *testNative { }, ReturnType: smartcontract.IntegerType, } - md := &native.MethodAndPrice{ + md := &interop.MethodAndPrice{ Func: tn.sum, Price: 1, RequiredFlags: smartcontract.NoneFlag, @@ -76,7 +84,7 @@ func TestNativeContract_Invoke(t *testing.T) { defer chain.Close() tn := newTestNative() - chain.RegisterNative(tn) + chain.registerNative(tn) w := io.NewBufBinWriter() emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "sum", int64(14), int64(28)) diff --git a/pkg/core/state/account.go b/pkg/core/state/account.go index f18a25d15..2fc3a4f66 100644 --- a/pkg/core/state/account.go +++ b/pkg/core/state/account.go @@ -1,7 +1,6 @@ package state 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/util" ) @@ -32,9 +31,6 @@ type Account struct { Version uint8 ScriptHash util.Uint160 IsFrozen bool - Votes []*keys.PublicKey - GAS NEP5BalanceState - NEO NEOBalanceState Balances map[util.Uint256][]UnspentBalance Unclaimed UnclaimedBalances } @@ -45,7 +41,6 @@ func NewAccount(scriptHash util.Uint160) *Account { Version: 0, ScriptHash: scriptHash, IsFrozen: false, - Votes: []*keys.PublicKey{}, Balances: make(map[util.Uint256][]UnspentBalance), Unclaimed: UnclaimedBalances{Raw: []byte{}}, } @@ -56,9 +51,6 @@ func (s *Account) DecodeBinary(br *io.BinReader) { s.Version = uint8(br.ReadB()) br.ReadBytes(s.ScriptHash[:]) s.IsFrozen = br.ReadBool() - br.ReadArray(&s.Votes) - s.GAS.DecodeBinary(br) - s.NEO.DecodeBinary(br) s.Balances = make(map[util.Uint256][]UnspentBalance) lenBalances := br.ReadVarUint() @@ -83,9 +75,6 @@ func (s *Account) EncodeBinary(bw *io.BinWriter) { bw.WriteB(byte(s.Version)) bw.WriteBytes(s.ScriptHash[:]) bw.WriteBool(s.IsFrozen) - bw.WriteArray(s.Votes) - s.GAS.EncodeBinary(bw) - s.NEO.EncodeBinary(bw) bw.WriteVarUint(uint64(len(s.Balances))) for k, v := range s.Balances { diff --git a/pkg/core/state/account_test.go b/pkg/core/state/account_test.go index 307abec8f..fab1049d7 100644 --- a/pkg/core/state/account_test.go +++ b/pkg/core/state/account_test.go @@ -3,7 +3,6 @@ package state import ( "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/testserdes" "github.com/nspcc-dev/neo-go/pkg/util" @@ -14,7 +13,6 @@ func TestDecodeEncodeAccountState(t *testing.T) { var ( n = 10 balances = make(map[util.Uint256][]UnspentBalance) - votes = make([]*keys.PublicKey, n) ) for i := 0; i < n; i++ { asset := random.Uint256() @@ -25,16 +23,12 @@ func TestDecodeEncodeAccountState(t *testing.T) { Value: util.Fixed8(int64(random.Int(1, 10000))), }) } - k, err := keys.NewPrivateKey() - assert.Nil(t, err) - votes[i] = k.PublicKey() } a := &Account{ Version: 0, ScriptHash: random.Uint160(), IsFrozen: true, - Votes: votes, Balances: balances, Unclaimed: UnclaimedBalances{Raw: []byte{}}, } diff --git a/pkg/core/state/native_state.go b/pkg/core/state/native_state.go index 9744cc754..f1377f460 100644 --- a/pkg/core/state/native_state.go +++ b/pkg/core/state/native_state.go @@ -3,6 +3,7 @@ package state import ( "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/vm/emit" ) @@ -16,6 +17,31 @@ type NEP5BalanceState struct { type NEOBalanceState struct { NEP5BalanceState 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. @@ -32,14 +58,41 @@ func (s *NEP5BalanceState) DecodeBinary(r *io.BinReader) { 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. func (s *NEOBalanceState) EncodeBinary(w *io.BinWriter) { s.NEP5BalanceState.EncodeBinary(w) w.WriteU32LE(s.BalanceHeight) + w.WriteArray(s.Votes) } // DecodeBinary implements io.Serializable interface. func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) { s.NEP5BalanceState.DecodeBinary(r) s.BalanceHeight = r.ReadU32LE() + r.ReadArray(&s.Votes) } diff --git a/pkg/core/state/validator.go b/pkg/core/state/validator.go index 2a2d65fa1..bdfba6ed1 100644 --- a/pkg/core/state/validator.go +++ b/pkg/core/state/validator.go @@ -1,108 +1,13 @@ package state import ( + "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/util" ) -// MaxValidatorsVoted limits the number of validators that one can vote for. -const MaxValidatorsVoted = 1024 - -// Validator holds the state of a validator. +// Validator holds the state of a validator (its key and votes balance). type Validator struct { - PublicKey *keys.PublicKey - Registered bool - 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) + Key *keys.PublicKey + Votes *big.Int } diff --git a/pkg/core/state/validator_test.go b/pkg/core/state/validator_test.go deleted file mode 100644 index 1136aa189..000000000 --- a/pkg/core/state/validator_test.go +++ /dev/null @@ -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()) -} diff --git a/pkg/core/storage/store.go b/pkg/core/storage/store.go index b9fb6874d..12a46dd69 100644 --- a/pkg/core/storage/store.go +++ b/pkg/core/storage/store.go @@ -7,25 +7,22 @@ import ( // KeyPrefix constants. const ( - DataBlock KeyPrefix = 0x01 - DataTransaction KeyPrefix = 0x02 - STAccount KeyPrefix = 0x40 - STCoin KeyPrefix = 0x44 - STSpentCoin KeyPrefix = 0x45 - STNextValidators KeyPrefix = 0x47 - STValidator KeyPrefix = 0x48 - STAsset KeyPrefix = 0x4c - STNotification KeyPrefix = 0x4d - STContract KeyPrefix = 0x50 - STNativeContract KeyPrefix = 0x51 - STStorage KeyPrefix = 0x70 - STNEP5Transfers KeyPrefix = 0x72 - STNEP5Balances KeyPrefix = 0x73 - IXHeaderHashList KeyPrefix = 0x80 - IXValidatorsCount KeyPrefix = 0x90 - SYSCurrentBlock KeyPrefix = 0xc0 - SYSCurrentHeader KeyPrefix = 0xc1 - SYSVersion KeyPrefix = 0xf0 + DataBlock KeyPrefix = 0x01 + DataTransaction KeyPrefix = 0x02 + STAccount KeyPrefix = 0x40 + STCoin KeyPrefix = 0x44 + STSpentCoin KeyPrefix = 0x45 + STValidator KeyPrefix = 0x48 + STAsset KeyPrefix = 0x4c + STNotification KeyPrefix = 0x4d + STContract KeyPrefix = 0x50 + STStorage KeyPrefix = 0x70 + STNEP5Transfers KeyPrefix = 0x72 + STNEP5Balances KeyPrefix = 0x73 + IXHeaderHashList KeyPrefix = 0x80 + SYSCurrentBlock KeyPrefix = 0xc0 + SYSCurrentHeader KeyPrefix = 0xc1 + SYSVersion KeyPrefix = 0xf0 ) // ErrKeyNotFound is an error returned by Store implementations diff --git a/pkg/core/storage/store_test.go b/pkg/core/storage/store_test.go index 4c028fbe4..9c9f6ef70 100644 --- a/pkg/core/storage/store_test.go +++ b/pkg/core/storage/store_test.go @@ -17,7 +17,6 @@ var ( STContract, STStorage, IXHeaderHashList, - IXValidatorsCount, SYSCurrentBlock, SYSCurrentHeader, SYSVersion, @@ -33,7 +32,6 @@ var ( 0x50, 0x70, 0x80, - 0x90, 0xc0, 0xc1, 0xf0, diff --git a/pkg/core/transaction/enrollment.go b/pkg/core/transaction/enrollment.go deleted file mode 100644 index 8699ef9d2..000000000 --- a/pkg/core/transaction/enrollment.go +++ /dev/null @@ -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) -} diff --git a/pkg/core/transaction/enrollment_test.go b/pkg/core/transaction/enrollment_test.go deleted file mode 100644 index 21cbeeb98..000000000 --- a/pkg/core/transaction/enrollment_test.go +++ /dev/null @@ -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)) -} -*/ diff --git a/pkg/core/transaction/state.go b/pkg/core/transaction/state.go deleted file mode 100644 index 84b205cf8..000000000 --- a/pkg/core/transaction/state.go +++ /dev/null @@ -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) -} diff --git a/pkg/core/transaction/state_descriptor.go b/pkg/core/transaction/state_descriptor.go deleted file mode 100644 index 81d4295fe..000000000 --- a/pkg/core/transaction/state_descriptor.go +++ /dev/null @@ -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 -} diff --git a/pkg/core/transaction/state_test.go b/pkg/core/transaction/state_test.go deleted file mode 100644 index 87acb1adf..000000000 --- a/pkg/core/transaction/state_test.go +++ /dev/null @@ -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)) -} -*/ diff --git a/pkg/core/transaction/transaction.go b/pkg/core/transaction/transaction.go index 7a594cce9..c404a5e29 100644 --- a/pkg/core/transaction/transaction.go +++ b/pkg/core/transaction/transaction.go @@ -7,7 +7,6 @@ import ( "fmt" "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/io" "github.com/nspcc-dev/neo-go/pkg/util" @@ -164,12 +163,6 @@ func (t *Transaction) decodeData(r *io.BinReader) { case IssueType: t.Data = &IssueTX{} 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: r.Err = fmt.Errorf("invalid TX type %x", t.Type) } @@ -281,12 +274,10 @@ type transactionJSON struct { Outputs []Output `json:"vout"` Scripts []Witness `json:"scripts"` - Claims []Input `json:"claims,omitempty"` - PublicKey *keys.PublicKey `json:"pubkey,omitempty"` - Script string `json:"script,omitempty"` - Gas util.Fixed8 `json:"gas,omitempty"` - Asset *registeredAsset `json:"asset,omitempty"` - Descriptors []*StateDescriptor `json:"descriptors,omitempty"` + Claims []Input `json:"claims,omitempty"` + Script string `json:"script,omitempty"` + Gas util.Fixed8 `json:"gas,omitempty"` + Asset *registeredAsset `json:"asset,omitempty"` } // MarshalJSON implements json.Marshaler interface. @@ -307,8 +298,6 @@ func (t *Transaction) MarshalJSON() ([]byte, error) { switch t.Type { case ClaimType: tx.Claims = t.Data.(*ClaimTX).Claims - case EnrollmentType: - tx.PublicKey = &t.Data.(*EnrollmentTX).PublicKey case InvocationType: tx.Script = hex.EncodeToString(t.Data.(*InvocationTX).Script) tx.Gas = t.Data.(*InvocationTX).Gas @@ -322,8 +311,6 @@ func (t *Transaction) MarshalJSON() ([]byte, error) { Owner: transaction.Owner, Admin: address.Uint160ToString(transaction.Admin), } - case StateType: - tx.Descriptors = t.Data.(*StateTX).Descriptors } return json.Marshal(tx) } @@ -354,10 +341,6 @@ func (t *Transaction) UnmarshalJSON(data []byte) error { t.Data = &ClaimTX{ Claims: tx.Claims, } - case EnrollmentType: - t.Data = &EnrollmentTX{ - PublicKey: *tx.PublicKey, - } case InvocationType: bytes, err := hex.DecodeString(tx.Script) if err != nil { @@ -381,10 +364,6 @@ func (t *Transaction) UnmarshalJSON(data []byte) error { Owner: tx.Asset.Owner, Admin: admin, } - case StateType: - t.Data = &StateTX{ - Descriptors: tx.Descriptors, - } case ContractType: t.Data = &ContractTX{} case IssueType: diff --git a/pkg/core/transaction/transaction_test.go b/pkg/core/transaction/transaction_test.go index 4a47eb6ef..9050026ee 100644 --- a/pkg/core/transaction/transaction_test.go +++ b/pkg/core/transaction/transaction_test.go @@ -154,32 +154,6 @@ func TestMarshalUnmarshalJSONClaimTX(t *testing.T) { 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) { tx := &Transaction{ Type: InvocationType, @@ -236,33 +210,3 @@ func TestMarshalUnmarshalJSONRegisterTX(t *testing.T) { 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)) -} diff --git a/pkg/core/transaction/type.go b/pkg/core/transaction/type.go index c9d972177..8c3ab94a8 100644 --- a/pkg/core/transaction/type.go +++ b/pkg/core/transaction/type.go @@ -14,10 +14,8 @@ const ( MinerType TXType = 0x00 IssueType TXType = 0x01 ClaimType TXType = 0x02 - EnrollmentType TXType = 0x20 RegisterType TXType = 0x40 ContractType TXType = 0x80 - StateType TXType = 0x90 InvocationType TXType = 0xd1 ) @@ -30,14 +28,10 @@ func (t TXType) String() string { return "IssueTransaction" case ClaimType: return "ClaimTransaction" - case EnrollmentType: - return "EnrollmentTransaction" case RegisterType: return "RegisterTransaction" case ContractType: return "ContractTransaction" - case StateType: - return "StateTransaction" case InvocationType: return "InvocationTransaction" default: @@ -70,14 +64,10 @@ func TXTypeFromString(jsonString string) (TXType, error) { return IssueType, nil case "ClaimTransaction": return ClaimType, nil - case "EnrollmentTransaction": - return EnrollmentType, nil case "RegisterTransaction": return RegisterType, nil case "ContractTransaction": return ContractType, nil - case "StateTransaction": - return StateType, nil case "InvocationTransaction": return InvocationType, nil default: diff --git a/pkg/core/util.go b/pkg/core/util.go index 2eeb9adea..465e90d11 100644 --- a/pkg/core/util.go +++ b/pkg/core/util.go @@ -8,8 +8,10 @@ import ( "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/keys" + "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "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" ) @@ -86,6 +88,7 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error) &governingTokenTX, &utilityTokenTX, issueTx, + deployNativeContracts(), }, } @@ -96,6 +99,22 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error) 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() { admin := hash.Hash160([]byte{byte(opcode.OLDPUSH1)}) registerTX := &transaction.RegisterTX{ diff --git a/pkg/core/util_test.go b/pkg/core/util_test.go index ba870d324..a3b9fc001 100644 --- a/pkg/core/util_test.go +++ b/pkg/core/util_test.go @@ -20,7 +20,7 @@ func TestGenesisBlockMainNet(t *testing.T) { // have been changed. Consequently, hash of the genesis block has been changed. // Update expected genesis block hash for better times. // Old hash is "d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf" - expect := "931161239581bcd90b73d092483dee6b7bf2162e66df7e15c3a276a22b4c9422" + expect := "094c2c2db5dcb868d85aa4d652aed23bc67e7166f53223a228e382265b1be84b" assert.Equal(t, expect, block.Hash().StringLE()) } diff --git a/pkg/crypto/keys/publickey.go b/pkg/crypto/keys/publickey.go index 063995201..d03bdb22b 100644 --- a/pkg/crypto/keys/publickey.go +++ b/pkg/crypto/keys/publickey.go @@ -35,6 +35,16 @@ func (keys *PublicKeys) DecodeBytes(data []byte) error { 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. func (keys PublicKeys) Contains(pKey *PublicKey) bool { for _, key := range keys { diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index 48db395a7..6ef6487aa 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -96,13 +96,13 @@ func (chain testChain) GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog { func (chain testChain) GetNEP5Balances(util.Uint160) *state.NEP5Balances { panic("TODO") } -func (chain testChain) GetValidators(...*transaction.Transaction) ([]*keys.PublicKey, error) { +func (chain testChain) GetValidators() ([]*keys.PublicKey, error) { panic("TODO") } func (chain testChain) GetStandByValidators() (keys.PublicKeys, error) { panic("TODO") } -func (chain testChain) GetEnrollments() ([]*state.Validator, error) { +func (chain testChain) GetEnrollments() ([]state.Validator, error) { panic("TODO") } func (chain testChain) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) { diff --git a/pkg/rpc/client/rpc_test.go b/pkg/rpc/client/rpc_test.go index f227d8138..f630868b2 100644 --- a/pkg/rpc/client/rpc_test.go +++ b/pkg/rpc/client/rpc_test.go @@ -11,7 +11,6 @@ import ( "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/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/rpc/request" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result" @@ -49,7 +48,6 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{ Version: 0, ScriptHash: scriptHash, IsFrozen: false, - Votes: []*keys.PublicKey{}, Balances: result.Balances{ result.Balance{ Asset: core.GoverningTokenID(), diff --git a/pkg/rpc/response/result/account_state.go b/pkg/rpc/response/result/account_state.go index e73b0ef55..cbff2fff9 100644 --- a/pkg/rpc/response/result/account_state.go +++ b/pkg/rpc/response/result/account_state.go @@ -5,18 +5,16 @@ import ( "sort" "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" ) // AccountState wrapper used for the representation of // state.Account on the RPC Server. type AccountState struct { - Version uint8 `json:"version"` - ScriptHash util.Uint160 `json:"script_hash"` - IsFrozen bool `json:"frozen"` - Votes []*keys.PublicKey `json:"votes"` - Balances []Balance `json:"balances"` + Version uint8 `json:"version"` + ScriptHash util.Uint160 `json:"script_hash"` + IsFrozen bool `json:"frozen"` + Balances []Balance `json:"balances"` } // Balances type for sorting balances in rpc response. @@ -48,7 +46,6 @@ func NewAccountState(a *state.Account) AccountState { Version: a.Version, ScriptHash: a.ScriptHash, IsFrozen: a.IsFrozen, - Votes: a.Votes, Balances: balances, } } diff --git a/pkg/rpc/response/result/validator.go b/pkg/rpc/response/result/validator.go index 19c5fefda..37862511d 100644 --- a/pkg/rpc/response/result/validator.go +++ b/pkg/rpc/response/result/validator.go @@ -2,13 +2,12 @@ package result import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" - "github.com/nspcc-dev/neo-go/pkg/util" ) // Validator used for the representation of // state.Validator on the RPC Server. type Validator struct { PublicKey keys.PublicKey `json:"publickey"` - Votes util.Fixed8 `json:"votes"` + Votes int64 `json:"votes,string"` Active bool `json:"active"` } diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 38a080746..b4164ef2f 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -815,9 +815,9 @@ func (s *Server) getValidators(_ request.Params) (interface{}, error) { var res []result.Validator for _, v := range enrollments { res = append(res, result.Validator{ - PublicKey: *v.PublicKey, - Votes: v.Votes, - Active: validators.Contains(v.PublicKey), + PublicKey: *v.Key, + Votes: v.Votes.Int64(), + Active: validators.Contains(v.Key), }) } return res, nil diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 49a494a65..ab5e059de 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -408,7 +408,7 @@ var rpcTestCases = map[string][]rpcTestCase{ "getblockheader": { { name: "invalid verbose type", - params: `["614a9085dc55fd0539ad3a9d68d8b8e7c52328da905c87bfe8cfca57a5c3c02f", true]`, + params: `["9673799c5b5a294427401cb07d6cc615ada3a0d5c5bf7ed6f0f54f24abb2e2ac", true]`, fail: true, }, { diff --git a/pkg/rpc/server/testdata/testblocks.acc b/pkg/rpc/server/testdata/testblocks.acc index ab268fe5d..ba7136e26 100644 Binary files a/pkg/rpc/server/testdata/testblocks.acc and b/pkg/rpc/server/testdata/testblocks.acc differ