Merge pull request #894 from nspcc-dev/neo3/genesis/init_native_interops
core: init native interops in the genesis block
This commit is contained in:
commit
de91418d45
52 changed files with 662 additions and 1357 deletions
|
@ -24,7 +24,6 @@ ProtocolConfiguration:
|
|||
- seed9.ngd.network:10333
|
||||
- seed10.ngd.network:10333
|
||||
SystemFee:
|
||||
EnrollmentTransaction: 1000
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
VerifyBlocks: true
|
||||
|
|
|
@ -15,7 +15,6 @@ ProtocolConfiguration:
|
|||
- 172.200.0.3:20335
|
||||
- 172.200.0.4:20336
|
||||
SystemFee:
|
||||
EnrollmentTransaction: 1000
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
VerifyBlocks: true
|
||||
|
|
|
@ -15,7 +15,6 @@ ProtocolConfiguration:
|
|||
- 172.200.0.3:20335
|
||||
- 172.200.0.4:20336
|
||||
SystemFee:
|
||||
EnrollmentTransaction: 1000
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
VerifyBlocks: true
|
||||
|
|
|
@ -9,7 +9,6 @@ ProtocolConfiguration:
|
|||
SeedList:
|
||||
- 172.200.0.1:20333
|
||||
SystemFee:
|
||||
EnrollmentTransaction: 1000
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
VerifyBlocks: true
|
||||
|
|
|
@ -15,7 +15,6 @@ ProtocolConfiguration:
|
|||
- 172.200.0.3:20335
|
||||
- 172.200.0.4:20336
|
||||
SystemFee:
|
||||
EnrollmentTransaction: 1000
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
VerifyBlocks: true
|
||||
|
|
|
@ -15,7 +15,6 @@ ProtocolConfiguration:
|
|||
- 172.200.0.3:20335
|
||||
- 172.200.0.4:20336
|
||||
SystemFee:
|
||||
EnrollmentTransaction: 1000
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
VerifyBlocks: true
|
||||
|
|
|
@ -24,7 +24,6 @@ ProtocolConfiguration:
|
|||
- seed9.ngd.network:20333
|
||||
- seed10.ngd.network:20333
|
||||
SystemFee:
|
||||
EnrollmentTransaction: 10
|
||||
IssueTransaction: 5
|
||||
RegisterTransaction: 100
|
||||
VerifyBlocks: true
|
||||
|
|
|
@ -14,7 +14,6 @@ ProtocolConfiguration:
|
|||
- 127.0.0.1:20335
|
||||
- 127.0.0.1:20336
|
||||
SystemFee:
|
||||
EnrollmentTransaction: 1000
|
||||
IssueTransaction: 500
|
||||
RegisterTransaction: 10000
|
||||
VerifyBlocks: true
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -34,7 +34,6 @@ func TestUnexpectedNonInterops(t *testing.T) {
|
|||
funcs := []func(*interop.Context, *vm.VM) error{
|
||||
accountGetBalance,
|
||||
accountGetScriptHash,
|
||||
accountGetVotes,
|
||||
assetGetAdmin,
|
||||
assetGetAmount,
|
||||
assetGetAssetID,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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, <primary>, netFee)
|
||||
return ic.DAO.PutNativeContractState(g.Hash, g.serializeState())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GAS) getSysFeeAmount(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
106
pkg/core/native/validators_count.go
Normal file
106
pkg/core/native/validators_count.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package native
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
)
|
||||
|
||||
// MaxValidatorsVoted limits the number of validators that one can vote for.
|
||||
const MaxValidatorsVoted = 1024
|
||||
|
||||
// ValidatorsCount represents votes with particular number of consensus nodes
|
||||
// for this number to be changeable by the voting system.
|
||||
type ValidatorsCount [MaxValidatorsVoted]big.Int
|
||||
|
||||
// ValidatorsCountFromBytes converts serialized ValidatorsCount to structure.
|
||||
func ValidatorsCountFromBytes(b []byte) (*ValidatorsCount, error) {
|
||||
vc := new(ValidatorsCount)
|
||||
if len(b) == 0 {
|
||||
return vc, nil
|
||||
}
|
||||
r := io.NewBinReaderFromBuf(b)
|
||||
vc.DecodeBinary(r)
|
||||
|
||||
if r.Err != nil {
|
||||
return nil, r.Err
|
||||
}
|
||||
return vc, nil
|
||||
}
|
||||
|
||||
// Bytes returns serialized ValidatorsCount.
|
||||
func (vc *ValidatorsCount) Bytes() []byte {
|
||||
w := io.NewBufBinWriter()
|
||||
vc.EncodeBinary(w.BinWriter)
|
||||
if w.Err != nil {
|
||||
panic(w.Err)
|
||||
}
|
||||
return w.Bytes()
|
||||
}
|
||||
|
||||
// EncodeBinary implements io.Serializable interface.
|
||||
func (vc *ValidatorsCount) EncodeBinary(w *io.BinWriter) {
|
||||
for i := range vc {
|
||||
w.WriteVarBytes(emit.IntToBytes(&vc[i]))
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeBinary implements io.Serializable interface.
|
||||
func (vc *ValidatorsCount) DecodeBinary(r *io.BinReader) {
|
||||
for i := range vc {
|
||||
buf := r.ReadVarBytes()
|
||||
if r.Err != nil {
|
||||
return
|
||||
}
|
||||
vc[i] = *emit.BytesToInt(buf)
|
||||
}
|
||||
}
|
||||
|
||||
// GetWeightedAverage returns an average count of validators that's been voted
|
||||
// for not counting 1/4 of minimum and maximum numbers.
|
||||
func (vc *ValidatorsCount) GetWeightedAverage() int {
|
||||
const (
|
||||
lowerThreshold = 0.25
|
||||
upperThreshold = 0.75
|
||||
)
|
||||
var (
|
||||
sumWeight, sumValue, overallSum, slidingSum int64
|
||||
slidingRatio float64
|
||||
)
|
||||
|
||||
for i := range vc {
|
||||
overallSum += vc[i].Int64()
|
||||
}
|
||||
|
||||
for i := range vc {
|
||||
if slidingRatio >= upperThreshold {
|
||||
break
|
||||
}
|
||||
weight := vc[i].Int64()
|
||||
slidingSum += weight
|
||||
previousRatio := slidingRatio
|
||||
slidingRatio = float64(slidingSum) / float64(overallSum)
|
||||
|
||||
if slidingRatio <= lowerThreshold {
|
||||
continue
|
||||
}
|
||||
|
||||
if previousRatio < lowerThreshold {
|
||||
if slidingRatio > upperThreshold {
|
||||
weight = int64((upperThreshold - lowerThreshold) * float64(overallSum))
|
||||
} else {
|
||||
weight = int64((slidingRatio - lowerThreshold) * float64(overallSum))
|
||||
}
|
||||
} else if slidingRatio > upperThreshold {
|
||||
weight = int64((upperThreshold - previousRatio) * float64(overallSum))
|
||||
}
|
||||
sumWeight += weight
|
||||
// Votes with N values get stored with N-1 index, thus +1 here.
|
||||
sumValue += (int64(i) + 1) * weight
|
||||
}
|
||||
if sumValue == 0 || sumWeight == 0 {
|
||||
return 0
|
||||
}
|
||||
return int(sumValue / sumWeight)
|
||||
}
|
|
@ -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))
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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{}},
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -17,7 +17,6 @@ var (
|
|||
STContract,
|
||||
STStorage,
|
||||
IXHeaderHashList,
|
||||
IXValidatorsCount,
|
||||
SYSCurrentBlock,
|
||||
SYSCurrentHeader,
|
||||
SYSVersion,
|
||||
|
@ -33,7 +32,6 @@ var (
|
|||
0x50,
|
||||
0x70,
|
||||
0x80,
|
||||
0x90,
|
||||
0xc0,
|
||||
0xc1,
|
||||
0xf0,
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
*/
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
*/
|
|
@ -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:
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -408,7 +408,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
"getblockheader": {
|
||||
{
|
||||
name: "invalid verbose type",
|
||||
params: `["614a9085dc55fd0539ad3a9d68d8b8e7c52328da905c87bfe8cfca57a5c3c02f", true]`,
|
||||
params: `["9673799c5b5a294427401cb07d6cc615ada3a0d5c5bf7ed6f0f54f24abb2e2ac", true]`,
|
||||
fail: true,
|
||||
},
|
||||
{
|
||||
|
|
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
BIN
pkg/rpc/server/testdata/testblocks.acc
vendored
Binary file not shown.
Loading…
Reference in a new issue