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
|
- seed9.ngd.network:10333
|
||||||
- seed10.ngd.network:10333
|
- seed10.ngd.network:10333
|
||||||
SystemFee:
|
SystemFee:
|
||||||
EnrollmentTransaction: 1000
|
|
||||||
IssueTransaction: 500
|
IssueTransaction: 500
|
||||||
RegisterTransaction: 10000
|
RegisterTransaction: 10000
|
||||||
VerifyBlocks: true
|
VerifyBlocks: true
|
||||||
|
|
|
@ -15,7 +15,6 @@ ProtocolConfiguration:
|
||||||
- 172.200.0.3:20335
|
- 172.200.0.3:20335
|
||||||
- 172.200.0.4:20336
|
- 172.200.0.4:20336
|
||||||
SystemFee:
|
SystemFee:
|
||||||
EnrollmentTransaction: 1000
|
|
||||||
IssueTransaction: 500
|
IssueTransaction: 500
|
||||||
RegisterTransaction: 10000
|
RegisterTransaction: 10000
|
||||||
VerifyBlocks: true
|
VerifyBlocks: true
|
||||||
|
|
|
@ -15,7 +15,6 @@ ProtocolConfiguration:
|
||||||
- 172.200.0.3:20335
|
- 172.200.0.3:20335
|
||||||
- 172.200.0.4:20336
|
- 172.200.0.4:20336
|
||||||
SystemFee:
|
SystemFee:
|
||||||
EnrollmentTransaction: 1000
|
|
||||||
IssueTransaction: 500
|
IssueTransaction: 500
|
||||||
RegisterTransaction: 10000
|
RegisterTransaction: 10000
|
||||||
VerifyBlocks: true
|
VerifyBlocks: true
|
||||||
|
|
|
@ -9,7 +9,6 @@ ProtocolConfiguration:
|
||||||
SeedList:
|
SeedList:
|
||||||
- 172.200.0.1:20333
|
- 172.200.0.1:20333
|
||||||
SystemFee:
|
SystemFee:
|
||||||
EnrollmentTransaction: 1000
|
|
||||||
IssueTransaction: 500
|
IssueTransaction: 500
|
||||||
RegisterTransaction: 10000
|
RegisterTransaction: 10000
|
||||||
VerifyBlocks: true
|
VerifyBlocks: true
|
||||||
|
|
|
@ -15,7 +15,6 @@ ProtocolConfiguration:
|
||||||
- 172.200.0.3:20335
|
- 172.200.0.3:20335
|
||||||
- 172.200.0.4:20336
|
- 172.200.0.4:20336
|
||||||
SystemFee:
|
SystemFee:
|
||||||
EnrollmentTransaction: 1000
|
|
||||||
IssueTransaction: 500
|
IssueTransaction: 500
|
||||||
RegisterTransaction: 10000
|
RegisterTransaction: 10000
|
||||||
VerifyBlocks: true
|
VerifyBlocks: true
|
||||||
|
|
|
@ -15,7 +15,6 @@ ProtocolConfiguration:
|
||||||
- 172.200.0.3:20335
|
- 172.200.0.3:20335
|
||||||
- 172.200.0.4:20336
|
- 172.200.0.4:20336
|
||||||
SystemFee:
|
SystemFee:
|
||||||
EnrollmentTransaction: 1000
|
|
||||||
IssueTransaction: 500
|
IssueTransaction: 500
|
||||||
RegisterTransaction: 10000
|
RegisterTransaction: 10000
|
||||||
VerifyBlocks: true
|
VerifyBlocks: true
|
||||||
|
|
|
@ -24,7 +24,6 @@ ProtocolConfiguration:
|
||||||
- seed9.ngd.network:20333
|
- seed9.ngd.network:20333
|
||||||
- seed10.ngd.network:20333
|
- seed10.ngd.network:20333
|
||||||
SystemFee:
|
SystemFee:
|
||||||
EnrollmentTransaction: 10
|
|
||||||
IssueTransaction: 5
|
IssueTransaction: 5
|
||||||
RegisterTransaction: 100
|
RegisterTransaction: 100
|
||||||
VerifyBlocks: true
|
VerifyBlocks: true
|
||||||
|
|
|
@ -14,7 +14,6 @@ ProtocolConfiguration:
|
||||||
- 127.0.0.1:20335
|
- 127.0.0.1:20335
|
||||||
- 127.0.0.1:20336
|
- 127.0.0.1:20336
|
||||||
SystemFee:
|
SystemFee:
|
||||||
EnrollmentTransaction: 1000
|
|
||||||
IssueTransaction: 500
|
IssueTransaction: 500
|
||||||
RegisterTransaction: 10000
|
RegisterTransaction: 10000
|
||||||
VerifyBlocks: true
|
VerifyBlocks: true
|
||||||
|
|
|
@ -47,9 +47,8 @@ type (
|
||||||
|
|
||||||
// SystemFee fees related to system.
|
// SystemFee fees related to system.
|
||||||
SystemFee struct {
|
SystemFee struct {
|
||||||
EnrollmentTransaction int64 `yaml:"EnrollmentTransaction"`
|
IssueTransaction int64 `yaml:"IssueTransaction"`
|
||||||
IssueTransaction int64 `yaml:"IssueTransaction"`
|
RegisterTransaction int64 `yaml:"RegisterTransaction"`
|
||||||
RegisterTransaction int64 `yaml:"RegisterTransaction"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetMode describes the mode the blockchain will operate on.
|
// 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.
|
// TryGetValue returns the system fee base on transaction type.
|
||||||
func (s SystemFee) TryGetValue(txType transaction.TXType) util.Fixed8 {
|
func (s SystemFee) TryGetValue(txType transaction.TXType) util.Fixed8 {
|
||||||
switch txType {
|
switch txType {
|
||||||
case transaction.EnrollmentType:
|
|
||||||
return util.Fixed8FromInt64(s.EnrollmentTransaction)
|
|
||||||
case transaction.IssueType:
|
case transaction.IssueType:
|
||||||
return util.Fixed8FromInt64(s.IssueTransaction)
|
return util.Fixed8FromInt64(s.IssueTransaction)
|
||||||
case transaction.RegisterType:
|
case transaction.RegisterType:
|
||||||
|
|
|
@ -479,22 +479,12 @@ func (s *service) getVerifiedTx(count int) []block.Transaction {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) getValidators(txx ...block.Transaction) []crypto.PublicKey {
|
func (s *service) getValidators(_ ...block.Transaction) []crypto.PublicKey {
|
||||||
var (
|
var (
|
||||||
pKeys []*keys.PublicKey
|
pKeys []*keys.PublicKey
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if len(txx) == 0 {
|
pKeys, err = s.Chain.GetValidators()
|
||||||
pKeys, err = s.Chain.GetValidators()
|
|
||||||
} else {
|
|
||||||
ntxx := make([]*transaction.Transaction, len(txx))
|
|
||||||
for i := range ntxx {
|
|
||||||
ntxx[i] = txx[i].(*transaction.Transaction)
|
|
||||||
}
|
|
||||||
|
|
||||||
pKeys, err = s.Chain.GetValidators(ntxx...)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Error("error while trying to get validators", zap.Error(err))
|
s.log.Error("error while trying to get validators", zap.Error(err))
|
||||||
}
|
}
|
||||||
|
|
|
@ -197,9 +197,6 @@ func (bc *Blockchain) init() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := bc.initNative(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return bc.storeBlock(genesisBlock)
|
return bc.storeBlock(genesisBlock)
|
||||||
}
|
}
|
||||||
if ver != version {
|
if ver != version {
|
||||||
|
@ -272,27 +269,6 @@ func (bc *Blockchain) init() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *Blockchain) initNative() error {
|
|
||||||
ic := bc.newInteropContext(trigger.Application, bc.dao, nil, nil)
|
|
||||||
|
|
||||||
gas := native.NewGAS()
|
|
||||||
neo := native.NewNEO()
|
|
||||||
neo.GAS = gas
|
|
||||||
gas.NEO = neo
|
|
||||||
|
|
||||||
if err := gas.Initialize(ic); err != nil {
|
|
||||||
return fmt.Errorf("can't initialize GAS native contract: %v", err)
|
|
||||||
}
|
|
||||||
if err := neo.Initialize(ic); err != nil {
|
|
||||||
return fmt.Errorf("can't initialize NEO native contract: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
bc.contracts.SetGAS(gas)
|
|
||||||
bc.contracts.SetNEO(neo)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run runs chain loop.
|
// Run runs chain loop.
|
||||||
func (bc *Blockchain) Run() {
|
func (bc *Blockchain) Run() {
|
||||||
persistTimer := time.NewTimer(persistInterval)
|
persistTimer := time.NewTimer(persistInterval)
|
||||||
|
@ -547,9 +523,6 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = processTXWithValidatorsSubtract(prevTXOutput, account, cache); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
balancesLen := len(account.Balances[prevTXOutput.AssetID])
|
balancesLen := len(account.Balances[prevTXOutput.AssetID])
|
||||||
|
@ -657,14 +630,6 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case *transaction.EnrollmentTX:
|
|
||||||
if err := processEnrollmentTX(cache, t); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case *transaction.StateTX:
|
|
||||||
if err := bc.processStateTX(cache, tx, t); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case *transaction.InvocationTX:
|
case *transaction.InvocationTX:
|
||||||
systemInterop := bc.newInteropContext(trigger.Application, cache, block, tx)
|
systemInterop := bc.newInteropContext(trigger.Application, cache, block, tx)
|
||||||
v := SpawnVM(systemInterop)
|
v := SpawnVM(systemInterop)
|
||||||
|
@ -847,11 +812,6 @@ func (bc *Blockchain) LastBatch() *storage.MemBatch {
|
||||||
return bc.lastBatch
|
return bc.lastBatch
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterNative registers native contract in the blockchain.
|
|
||||||
func (bc *Blockchain) RegisterNative(c native.Contract) {
|
|
||||||
bc.contracts.Add(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
// processOutputs processes transaction outputs.
|
// processOutputs processes transaction outputs.
|
||||||
func processOutputs(tx *transaction.Transaction, dao *dao.Cached) error {
|
func processOutputs(tx *transaction.Transaction, dao *dao.Cached) error {
|
||||||
for index, output := range tx.Outputs {
|
for index, output := range tx.Outputs {
|
||||||
|
@ -867,79 +827,6 @@ func processOutputs(tx *transaction.Transaction, dao *dao.Cached) error {
|
||||||
if err = dao.PutAccountState(account); err != nil {
|
if err = dao.PutAccountState(account); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = processTXWithValidatorsAdd(&output, account, dao); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func processTXWithValidatorsAdd(output *transaction.Output, account *state.Account, dao *dao.Cached) error {
|
|
||||||
if output.AssetID.Equals(GoverningTokenID()) && len(account.Votes) > 0 {
|
|
||||||
return modAccountVotes(account, dao, output.Amount)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func processTXWithValidatorsSubtract(output *transaction.Output, account *state.Account, dao *dao.Cached) error {
|
|
||||||
if output.AssetID.Equals(GoverningTokenID()) && len(account.Votes) > 0 {
|
|
||||||
return modAccountVotes(account, dao, -output.Amount)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// modAccountVotes adds given value to given account voted validators.
|
|
||||||
func modAccountVotes(account *state.Account, dao *dao.Cached, value util.Fixed8) error {
|
|
||||||
if err := native.ModifyAccountVotes(account, dao, value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(account.Votes) > 0 {
|
|
||||||
vc, err := dao.GetValidatorsCount()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
vc[len(account.Votes)-1] += value
|
|
||||||
err = dao.PutValidatorsCount(vc)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func processValidatorStateDescriptor(descriptor *transaction.StateDescriptor, dao *dao.Cached) error {
|
|
||||||
publicKey := &keys.PublicKey{}
|
|
||||||
err := publicKey.DecodeBytes(descriptor.Key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
validatorState, err := dao.GetValidatorStateOrNew(publicKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if descriptor.Field == "Registered" {
|
|
||||||
if len(descriptor.Value) == 1 {
|
|
||||||
validatorState.Registered = descriptor.Value[0] != 0
|
|
||||||
return dao.PutValidatorState(validatorState)
|
|
||||||
}
|
|
||||||
return errors.New("bad descriptor value")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bc *Blockchain) processAccountStateDescriptor(descriptor *transaction.StateDescriptor, t *transaction.Transaction, dao *dao.Cached) error {
|
|
||||||
hash, err := util.Uint160DecodeBytesBE(descriptor.Key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if descriptor.Field == "Votes" {
|
|
||||||
votes := keys.PublicKeys{}
|
|
||||||
if err := votes.DecodeBytes(descriptor.Value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ic := bc.newInteropContext(trigger.Application, dao, nil, t)
|
|
||||||
return bc.contracts.NEO.VoteInternal(ic, hash, votes)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1364,63 +1251,6 @@ func (bc *Blockchain) verifyTx(t *transaction.Transaction, block *block.Block) e
|
||||||
if inv.Gas.FractionalValue() != 0 {
|
if inv.Gas.FractionalValue() != 0 {
|
||||||
return errors.New("invocation gas can only be integer")
|
return errors.New("invocation gas can only be integer")
|
||||||
}
|
}
|
||||||
case transaction.StateType:
|
|
||||||
stx := t.Data.(*transaction.StateTX)
|
|
||||||
for _, desc := range stx.Descriptors {
|
|
||||||
switch desc.Type {
|
|
||||||
case transaction.Account:
|
|
||||||
if desc.Field != "Votes" {
|
|
||||||
return errors.New("bad field in account descriptor")
|
|
||||||
}
|
|
||||||
votes := keys.PublicKeys{}
|
|
||||||
err := votes.DecodeBytes(desc.Value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(votes) > state.MaxValidatorsVoted {
|
|
||||||
return errors.New("voting candidate limit exceeded")
|
|
||||||
}
|
|
||||||
hash, err := util.Uint160DecodeBytesBE(desc.Key)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
account, err := bc.dao.GetAccountStateOrNew(hash)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if account.IsFrozen {
|
|
||||||
return errors.New("account is frozen")
|
|
||||||
}
|
|
||||||
if votes.Len() > 0 {
|
|
||||||
balance := account.GetBalanceValues()[GoverningTokenID()]
|
|
||||||
if balance == 0 {
|
|
||||||
return errors.New("no governing tokens available to vote")
|
|
||||||
}
|
|
||||||
validators, err := bc.GetEnrollments()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for _, k := range votes {
|
|
||||||
var isRegistered bool
|
|
||||||
for i := range validators {
|
|
||||||
if k.Equal(validators[i].PublicKey) {
|
|
||||||
isRegistered = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !isRegistered {
|
|
||||||
return errors.New("vote for unregistered validator")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case transaction.Validator:
|
|
||||||
if desc.Field != "Registered" {
|
|
||||||
return errors.New("bad field in validator descriptor")
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return errors.New("bad descriptor type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return bc.verifyTxWitnesses(t, block)
|
return bc.verifyTxWitnesses(t, block)
|
||||||
|
@ -1699,135 +1529,14 @@ func (bc *Blockchain) GetStandByValidators() (keys.PublicKeys, error) {
|
||||||
return getValidators(bc.config)
|
return getValidators(bc.config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetValidators returns validators.
|
// GetValidators returns next block validators.
|
||||||
// Golang implementation of GetValidators method in C# (https://github.com/neo-project/neo/blob/c64748ecbac3baeb8045b16af0d518398a6ced24/neo/Persistence/Snapshot.cs#L182)
|
func (bc *Blockchain) GetValidators() ([]*keys.PublicKey, error) {
|
||||||
func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.PublicKey, error) {
|
return bc.contracts.NEO.GetNextBlockValidatorsInternal(bc, bc.dao)
|
||||||
cache := dao.NewCached(bc.dao)
|
|
||||||
if len(txes) > 0 {
|
|
||||||
for _, tx := range txes {
|
|
||||||
// iterate through outputs
|
|
||||||
for index, output := range tx.Outputs {
|
|
||||||
accountState, err := cache.GetAccountStateOrNew(output.ScriptHash)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
accountState.Balances[output.AssetID] = append(accountState.Balances[output.AssetID], state.UnspentBalance{
|
|
||||||
Tx: tx.Hash(),
|
|
||||||
Index: uint16(index),
|
|
||||||
Value: output.Amount,
|
|
||||||
})
|
|
||||||
if err := cache.PutAccountState(accountState); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err = processTXWithValidatorsAdd(&output, accountState, cache); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// group inputs by the same previous hash and iterate through inputs
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetEnrollments returns all registered validators and non-registered SB validators
|
// GetEnrollments returns all registered validators.
|
||||||
func (bc *Blockchain) GetEnrollments() ([]*state.Validator, error) {
|
func (bc *Blockchain) GetEnrollments() ([]state.Validator, error) {
|
||||||
validators := bc.dao.GetValidators()
|
return bc.contracts.NEO.GetRegisteredValidators(bc.dao)
|
||||||
standByValidators, err := bc.GetStandByValidators()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
uniqueSBValidators := standByValidators.Unique()
|
|
||||||
|
|
||||||
var result []*state.Validator
|
|
||||||
for _, validator := range validators {
|
|
||||||
if validator.Registered {
|
|
||||||
result = append(result, validator)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, sBValidator := range uniqueSBValidators {
|
|
||||||
isAdded := false
|
|
||||||
for _, v := range result {
|
|
||||||
if v.PublicKey == sBValidator {
|
|
||||||
isAdded = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !isAdded {
|
|
||||||
result = append(result, &state.Validator{
|
|
||||||
PublicKey: sBValidator,
|
|
||||||
Registered: false,
|
|
||||||
Votes: 0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bc *Blockchain) processStateTX(dao *dao.Cached, t *transaction.Transaction, tx *transaction.StateTX) error {
|
|
||||||
for _, desc := range tx.Descriptors {
|
|
||||||
switch desc.Type {
|
|
||||||
case transaction.Account:
|
|
||||||
if err := bc.processAccountStateDescriptor(desc, t, dao); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case transaction.Validator:
|
|
||||||
if err := processValidatorStateDescriptor(desc, dao); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func processEnrollmentTX(dao *dao.Cached, tx *transaction.EnrollmentTX) error {
|
|
||||||
validatorState, err := dao.GetValidatorStateOrNew(&tx.PublicKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
validatorState.Registered = true
|
|
||||||
return dao.PutValidatorState(validatorState)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetScriptHashesForVerifying returns all the ScriptHashes of a transaction which will be use
|
// GetScriptHashesForVerifying returns all the ScriptHashes of a transaction which will be use
|
||||||
|
@ -1879,9 +1588,6 @@ func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([
|
||||||
for i := range refs {
|
for i := range refs {
|
||||||
hashes[refs[i].Out.ScriptHash] = true
|
hashes[refs[i].Out.ScriptHash] = true
|
||||||
}
|
}
|
||||||
case transaction.EnrollmentType:
|
|
||||||
etx := t.Data.(*transaction.EnrollmentTX)
|
|
||||||
hashes[etx.PublicKey.GetScriptHash()] = true
|
|
||||||
case transaction.IssueType:
|
case transaction.IssueType:
|
||||||
for _, res := range refsAndOutsToResults(references, t.Outputs) {
|
for _, res := range refsAndOutsToResults(references, t.Outputs) {
|
||||||
if res.Amount < 0 {
|
if res.Amount < 0 {
|
||||||
|
@ -1895,31 +1601,6 @@ func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([
|
||||||
case transaction.RegisterType:
|
case transaction.RegisterType:
|
||||||
reg := t.Data.(*transaction.RegisterTX)
|
reg := t.Data.(*transaction.RegisterTX)
|
||||||
hashes[reg.Owner.GetScriptHash()] = true
|
hashes[reg.Owner.GetScriptHash()] = true
|
||||||
case transaction.StateType:
|
|
||||||
stx := t.Data.(*transaction.StateTX)
|
|
||||||
for _, desc := range stx.Descriptors {
|
|
||||||
switch desc.Type {
|
|
||||||
case transaction.Account:
|
|
||||||
if desc.Field != "Votes" {
|
|
||||||
return nil, errors.New("bad account state descriptor")
|
|
||||||
}
|
|
||||||
hash, err := util.Uint160DecodeBytesBE(desc.Key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
hashes[hash] = true
|
|
||||||
case transaction.Validator:
|
|
||||||
if desc.Field != "Registered" {
|
|
||||||
return nil, errors.New("bad validator state descriptor")
|
|
||||||
}
|
|
||||||
key := &keys.PublicKey{}
|
|
||||||
err := key.DecodeBytes(desc.Key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
hashes[key.GetScriptHash()] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// convert hashes to []util.Uint160
|
// convert hashes to []util.Uint160
|
||||||
hashesResult := make([]util.Uint160, 0, len(hashes))
|
hashesResult := make([]util.Uint160, 0, len(hashes))
|
||||||
|
@ -2057,7 +1738,7 @@ func (bc *Blockchain) secondsPerBlock() int {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bc *Blockchain) newInteropContext(trigger trigger.Type, d dao.DAO, block *block.Block, tx *transaction.Transaction) *interop.Context {
|
func (bc *Blockchain) newInteropContext(trigger trigger.Type, d dao.DAO, block *block.Block, tx *transaction.Transaction) *interop.Context {
|
||||||
ic := interop.NewContext(trigger, bc, d, block, tx, bc.log)
|
ic := interop.NewContext(trigger, bc, d, bc.contracts.Contracts, block, tx, bc.log)
|
||||||
switch {
|
switch {
|
||||||
case tx != nil:
|
case tx != nil:
|
||||||
ic.Container = tx
|
ic.Container = tx
|
||||||
|
|
|
@ -24,7 +24,7 @@ type Blockchainer interface {
|
||||||
HeaderHeight() uint32
|
HeaderHeight() uint32
|
||||||
GetBlock(hash util.Uint256) (*block.Block, error)
|
GetBlock(hash util.Uint256) (*block.Block, error)
|
||||||
GetContractState(hash util.Uint160) *state.Contract
|
GetContractState(hash util.Uint160) *state.Contract
|
||||||
GetEnrollments() ([]*state.Validator, error)
|
GetEnrollments() ([]state.Validator, error)
|
||||||
GetHeaderHash(int) util.Uint256
|
GetHeaderHash(int) util.Uint256
|
||||||
GetHeader(hash util.Uint256) (*block.Header, error)
|
GetHeader(hash util.Uint256) (*block.Header, error)
|
||||||
CurrentHeaderHash() util.Uint256
|
CurrentHeaderHash() util.Uint256
|
||||||
|
@ -36,7 +36,7 @@ type Blockchainer interface {
|
||||||
GetAppExecResult(util.Uint256) (*state.AppExecResult, error)
|
GetAppExecResult(util.Uint256) (*state.AppExecResult, error)
|
||||||
GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog
|
GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog
|
||||||
GetNEP5Balances(util.Uint160) *state.NEP5Balances
|
GetNEP5Balances(util.Uint160) *state.NEP5Balances
|
||||||
GetValidators(txes ...*transaction.Transaction) ([]*keys.PublicKey, error)
|
GetValidators() ([]*keys.PublicKey, error)
|
||||||
GetStandByValidators() (keys.PublicKeys, error)
|
GetStandByValidators() (keys.PublicKeys, error)
|
||||||
GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error)
|
GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error)
|
||||||
GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem
|
GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
@ -20,7 +19,6 @@ type DAO interface {
|
||||||
AppendNEP5Transfer(acc util.Uint160, index uint32, tr *state.NEP5Transfer) (bool, error)
|
AppendNEP5Transfer(acc util.Uint160, index uint32, tr *state.NEP5Transfer) (bool, error)
|
||||||
DeleteContractState(hash util.Uint160) error
|
DeleteContractState(hash util.Uint160) error
|
||||||
DeleteStorageItem(scripthash util.Uint160, key []byte) error
|
DeleteStorageItem(scripthash util.Uint160, key []byte) error
|
||||||
DeleteValidatorState(vs *state.Validator) error
|
|
||||||
GetAccountState(hash util.Uint160) (*state.Account, error)
|
GetAccountState(hash util.Uint160) (*state.Account, error)
|
||||||
GetAccountStateOrNew(hash util.Uint160) (*state.Account, error)
|
GetAccountStateOrNew(hash util.Uint160) (*state.Account, error)
|
||||||
GetAndDecode(entity io.Serializable, key []byte) error
|
GetAndDecode(entity io.Serializable, key []byte) error
|
||||||
|
@ -32,18 +30,13 @@ type DAO interface {
|
||||||
GetCurrentBlockHeight() (uint32, error)
|
GetCurrentBlockHeight() (uint32, error)
|
||||||
GetCurrentHeaderHeight() (i uint32, h util.Uint256, err error)
|
GetCurrentHeaderHeight() (i uint32, h util.Uint256, err error)
|
||||||
GetHeaderHashes() ([]util.Uint256, error)
|
GetHeaderHashes() ([]util.Uint256, error)
|
||||||
GetNativeContractState(h util.Uint160) ([]byte, error)
|
|
||||||
GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error)
|
GetNEP5Balances(acc util.Uint160) (*state.NEP5Balances, error)
|
||||||
GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error)
|
GetNEP5TransferLog(acc util.Uint160, index uint32) (*state.NEP5TransferLog, error)
|
||||||
GetNextBlockValidators() (keys.PublicKeys, error)
|
|
||||||
GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem
|
GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem
|
||||||
GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error)
|
GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error)
|
||||||
|
GetStorageItemsWithPrefix(hash util.Uint160, prefix []byte) (map[string]*state.StorageItem, error)
|
||||||
GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error)
|
GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error)
|
||||||
GetUnspentCoinState(hash util.Uint256) (*state.UnspentCoin, error)
|
GetUnspentCoinState(hash util.Uint256) (*state.UnspentCoin, error)
|
||||||
GetValidatorState(publicKey *keys.PublicKey) (*state.Validator, error)
|
|
||||||
GetValidatorStateOrNew(publicKey *keys.PublicKey) (*state.Validator, error)
|
|
||||||
GetValidators() []*state.Validator
|
|
||||||
GetValidatorsCount() (*state.ValidatorsCount, error)
|
|
||||||
GetVersion() (string, error)
|
GetVersion() (string, error)
|
||||||
GetWrapped() DAO
|
GetWrapped() DAO
|
||||||
HasTransaction(hash util.Uint256) bool
|
HasTransaction(hash util.Uint256) bool
|
||||||
|
@ -55,14 +48,10 @@ type DAO interface {
|
||||||
PutAssetState(as *state.Asset) error
|
PutAssetState(as *state.Asset) error
|
||||||
PutContractState(cs *state.Contract) error
|
PutContractState(cs *state.Contract) error
|
||||||
PutCurrentHeader(hashAndIndex []byte) error
|
PutCurrentHeader(hashAndIndex []byte) error
|
||||||
PutNativeContractState(h util.Uint160, value []byte) error
|
|
||||||
PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error
|
PutNEP5Balances(acc util.Uint160, bs *state.NEP5Balances) error
|
||||||
PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error
|
PutNEP5TransferLog(acc util.Uint160, index uint32, lg *state.NEP5TransferLog) error
|
||||||
PutNextBlockValidators(keys.PublicKeys) error
|
|
||||||
PutStorageItem(scripthash util.Uint160, key []byte, si *state.StorageItem) error
|
PutStorageItem(scripthash util.Uint160, key []byte, si *state.StorageItem) error
|
||||||
PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin) error
|
PutUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin) error
|
||||||
PutValidatorState(vs *state.Validator) error
|
|
||||||
PutValidatorsCount(vc *state.ValidatorsCount) error
|
|
||||||
PutVersion(v string) error
|
PutVersion(v string) error
|
||||||
StoreAsBlock(block *block.Block, sysFee uint32) error
|
StoreAsBlock(block *block.Block, sysFee uint32) error
|
||||||
StoreAsCurrentBlock(block *block.Block) error
|
StoreAsCurrentBlock(block *block.Block) error
|
||||||
|
@ -211,18 +200,6 @@ func (dao *Simple) DeleteContractState(hash util.Uint160) error {
|
||||||
return dao.Store.Delete(key)
|
return dao.Store.Delete(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNativeContractState retrieves native contract state from the store.
|
|
||||||
func (dao *Simple) GetNativeContractState(h util.Uint160) ([]byte, error) {
|
|
||||||
key := storage.AppendPrefix(storage.STNativeContract, h.BytesBE())
|
|
||||||
return dao.Store.Get(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// PutNativeContractState puts native contract state into the store.
|
|
||||||
func (dao *Simple) PutNativeContractState(h util.Uint160, value []byte) error {
|
|
||||||
key := storage.AppendPrefix(storage.STNativeContract, h.BytesBE())
|
|
||||||
return dao.Store.Put(key, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- end contracts.
|
// -- end contracts.
|
||||||
|
|
||||||
// -- start nep5 balances.
|
// -- start nep5 balances.
|
||||||
|
@ -324,111 +301,6 @@ func (dao *Simple) putUnspentCoinState(hash util.Uint256, ucs *state.UnspentCoin
|
||||||
|
|
||||||
// -- end unspent coins.
|
// -- end unspent coins.
|
||||||
|
|
||||||
// -- start validator.
|
|
||||||
|
|
||||||
// GetNextBlockValidators retrieves next block validators from store or nil if they are missing.
|
|
||||||
func (dao *Simple) GetNextBlockValidators() (keys.PublicKeys, error) {
|
|
||||||
key := []byte{byte(storage.STNextValidators)}
|
|
||||||
buf, err := dao.Store.Get(key)
|
|
||||||
if err != nil {
|
|
||||||
if err == storage.ErrKeyNotFound {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var pubs keys.PublicKeys
|
|
||||||
r := io.NewBinReaderFromBuf(buf)
|
|
||||||
r.ReadArray(&pubs)
|
|
||||||
if r.Err != nil {
|
|
||||||
return nil, r.Err
|
|
||||||
}
|
|
||||||
return pubs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PutNextBlockValidators puts next block validators to store.
|
|
||||||
func (dao *Simple) PutNextBlockValidators(pubs keys.PublicKeys) error {
|
|
||||||
w := io.NewBufBinWriter()
|
|
||||||
w.WriteArray(pubs)
|
|
||||||
if w.Err != nil {
|
|
||||||
return w.Err
|
|
||||||
}
|
|
||||||
|
|
||||||
key := []byte{byte(storage.STNextValidators)}
|
|
||||||
return dao.Store.Put(key, w.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetValidatorStateOrNew gets validator from store or created new one in case of error.
|
|
||||||
func (dao *Simple) GetValidatorStateOrNew(publicKey *keys.PublicKey) (*state.Validator, error) {
|
|
||||||
validatorState, err := dao.GetValidatorState(publicKey)
|
|
||||||
if err != nil {
|
|
||||||
if err != storage.ErrKeyNotFound {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
validatorState = &state.Validator{PublicKey: publicKey}
|
|
||||||
}
|
|
||||||
return validatorState, nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetValidators returns all validators from store.
|
|
||||||
func (dao *Simple) GetValidators() []*state.Validator {
|
|
||||||
var validators []*state.Validator
|
|
||||||
dao.Store.Seek(storage.STValidator.Bytes(), func(k, v []byte) {
|
|
||||||
r := io.NewBinReaderFromBuf(v)
|
|
||||||
validator := &state.Validator{}
|
|
||||||
validator.DecodeBinary(r)
|
|
||||||
if r.Err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
validators = append(validators, validator)
|
|
||||||
})
|
|
||||||
return validators
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetValidatorState returns validator by publicKey.
|
|
||||||
func (dao *Simple) GetValidatorState(publicKey *keys.PublicKey) (*state.Validator, error) {
|
|
||||||
validatorState := &state.Validator{}
|
|
||||||
key := storage.AppendPrefix(storage.STValidator, publicKey.Bytes())
|
|
||||||
err := dao.GetAndDecode(validatorState, key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return validatorState, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PutValidatorState puts given Validator into the given store.
|
|
||||||
func (dao *Simple) PutValidatorState(vs *state.Validator) error {
|
|
||||||
key := storage.AppendPrefix(storage.STValidator, vs.PublicKey.Bytes())
|
|
||||||
return dao.Put(vs, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteValidatorState deletes given Validator into the given store.
|
|
||||||
func (dao *Simple) DeleteValidatorState(vs *state.Validator) error {
|
|
||||||
key := storage.AppendPrefix(storage.STValidator, vs.PublicKey.Bytes())
|
|
||||||
return dao.Store.Delete(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetValidatorsCount returns current ValidatorsCount or new one if there is none
|
|
||||||
// in the DB.
|
|
||||||
func (dao *Simple) GetValidatorsCount() (*state.ValidatorsCount, error) {
|
|
||||||
vc := &state.ValidatorsCount{}
|
|
||||||
key := []byte{byte(storage.IXValidatorsCount)}
|
|
||||||
err := dao.GetAndDecode(vc, key)
|
|
||||||
if err != nil && err != storage.ErrKeyNotFound {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return vc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PutValidatorsCount put given ValidatorsCount in the store.
|
|
||||||
func (dao *Simple) PutValidatorsCount(vc *state.ValidatorsCount) error {
|
|
||||||
key := []byte{byte(storage.IXValidatorsCount)}
|
|
||||||
return dao.Put(vc, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- end validator.
|
|
||||||
|
|
||||||
// -- start notification event.
|
// -- start notification event.
|
||||||
|
|
||||||
// GetAppExecResult gets application execution result from the
|
// GetAppExecResult gets application execution result from the
|
||||||
|
@ -485,9 +357,19 @@ func (dao *Simple) DeleteStorageItem(scripthash util.Uint160, key []byte) error
|
||||||
|
|
||||||
// GetStorageItems returns all storage items for a given scripthash.
|
// GetStorageItems returns all storage items for a given scripthash.
|
||||||
func (dao *Simple) GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) {
|
func (dao *Simple) GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) {
|
||||||
|
return dao.GetStorageItemsWithPrefix(hash, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStorageItemsWithPrefix returns all storage items with given prefix for a
|
||||||
|
// given scripthash.
|
||||||
|
func (dao *Simple) GetStorageItemsWithPrefix(hash util.Uint160, prefix []byte) (map[string]*state.StorageItem, error) {
|
||||||
var siMap = make(map[string]*state.StorageItem)
|
var siMap = make(map[string]*state.StorageItem)
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
lookupKey := storage.AppendPrefix(storage.STStorage, hash.BytesLE())
|
||||||
|
if prefix != nil {
|
||||||
|
lookupKey = append(lookupKey, prefix...)
|
||||||
|
}
|
||||||
saveToMap := func(k, v []byte) {
|
saveToMap := func(k, v []byte) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -501,9 +383,9 @@ func (dao *Simple) GetStorageItems(hash util.Uint160) (map[string]*state.Storage
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cut prefix and hash.
|
// Cut prefix and hash.
|
||||||
siMap[string(k[21:])] = si
|
siMap[string(k[len(lookupKey):])] = si
|
||||||
}
|
}
|
||||||
dao.Store.Seek(storage.AppendPrefix(storage.STStorage, hash.BytesLE()), saveToMap)
|
dao.Store.Seek(lookupKey, saveToMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,61 +113,6 @@ func TestPutGetUnspentCoinState(t *testing.T) {
|
||||||
require.Equal(t, unspentCoinState, gotUnspentCoinState)
|
require.Equal(t, unspentCoinState, gotUnspentCoinState)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetValidatorStateOrNew_New(t *testing.T) {
|
|
||||||
dao := NewSimple(storage.NewMemoryStore())
|
|
||||||
publicKey := &keys.PublicKey{}
|
|
||||||
validatorState, err := dao.GetValidatorStateOrNew(publicKey)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotNil(t, validatorState)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPutGetValidatorState(t *testing.T) {
|
|
||||||
dao := NewSimple(storage.NewMemoryStore())
|
|
||||||
publicKey := &keys.PublicKey{}
|
|
||||||
validatorState := &state.Validator{
|
|
||||||
PublicKey: publicKey,
|
|
||||||
Registered: false,
|
|
||||||
Votes: 0,
|
|
||||||
}
|
|
||||||
err := dao.PutValidatorState(validatorState)
|
|
||||||
require.NoError(t, err)
|
|
||||||
gotValidatorState, err := dao.GetValidatorState(publicKey)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, validatorState, gotValidatorState)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteValidatorState(t *testing.T) {
|
|
||||||
dao := NewSimple(storage.NewMemoryStore())
|
|
||||||
publicKey := &keys.PublicKey{}
|
|
||||||
validatorState := &state.Validator{
|
|
||||||
PublicKey: publicKey,
|
|
||||||
Registered: false,
|
|
||||||
Votes: 0,
|
|
||||||
}
|
|
||||||
err := dao.PutValidatorState(validatorState)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = dao.DeleteValidatorState(validatorState)
|
|
||||||
require.NoError(t, err)
|
|
||||||
gotValidatorState, err := dao.GetValidatorState(publicKey)
|
|
||||||
require.Error(t, err)
|
|
||||||
require.Nil(t, gotValidatorState)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetValidators(t *testing.T) {
|
|
||||||
dao := NewSimple(storage.NewMemoryStore())
|
|
||||||
publicKey := &keys.PublicKey{}
|
|
||||||
validatorState := &state.Validator{
|
|
||||||
PublicKey: publicKey,
|
|
||||||
Registered: false,
|
|
||||||
Votes: 0,
|
|
||||||
}
|
|
||||||
err := dao.PutValidatorState(validatorState)
|
|
||||||
require.NoError(t, err)
|
|
||||||
validators := dao.GetValidators()
|
|
||||||
require.Equal(t, validatorState, validators[0])
|
|
||||||
require.Len(t, validators, 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPutGetAppExecResult(t *testing.T) {
|
func TestPutGetAppExecResult(t *testing.T) {
|
||||||
dao := NewSimple(storage.NewMemoryStore())
|
dao := NewSimple(storage.NewMemoryStore())
|
||||||
hash := random.Uint256()
|
hash := random.Uint256()
|
||||||
|
|
|
@ -195,7 +195,7 @@ func TestCreateBasicChain(t *testing.T) {
|
||||||
// use output of issue tx from genesis block as an input
|
// use output of issue tx from genesis block as an input
|
||||||
genesisBlock, err := bc.GetBlock(bc.GetHeaderHash(0))
|
genesisBlock, err := bc.GetBlock(bc.GetHeaderHash(0))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, 4, len(genesisBlock.Transactions))
|
require.Equal(t, 5, len(genesisBlock.Transactions))
|
||||||
h := genesisBlock.Transactions[3].Hash()
|
h := genesisBlock.Transactions[3].Hash()
|
||||||
txMoveNeo.AddInput(&transaction.Input{
|
txMoveNeo.AddInput(&transaction.Input{
|
||||||
PrevHash: h,
|
PrevHash: h,
|
||||||
|
|
|
@ -7,8 +7,14 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto"
|
"github.com/nspcc-dev/neo-go/pkg/crypto"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,6 +22,7 @@ import (
|
||||||
type Context struct {
|
type Context struct {
|
||||||
Chain blockchainer.Blockchainer
|
Chain blockchainer.Blockchainer
|
||||||
Container crypto.Verifiable
|
Container crypto.Verifiable
|
||||||
|
Natives []Contract
|
||||||
Trigger trigger.Type
|
Trigger trigger.Type
|
||||||
Block *block.Block
|
Block *block.Block
|
||||||
Tx *transaction.Transaction
|
Tx *transaction.Transaction
|
||||||
|
@ -25,11 +32,12 @@ type Context struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContext returns new interop context.
|
// NewContext returns new interop context.
|
||||||
func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO, block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context {
|
func NewContext(trigger trigger.Type, bc blockchainer.Blockchainer, d dao.DAO, natives []Contract, block *block.Block, tx *transaction.Transaction, log *zap.Logger) *Context {
|
||||||
dao := dao.NewCached(d)
|
dao := dao.NewCached(d)
|
||||||
nes := make([]state.NotificationEvent, 0)
|
nes := make([]state.NotificationEvent, 0)
|
||||||
return &Context{
|
return &Context{
|
||||||
Chain: bc,
|
Chain: bc,
|
||||||
|
Natives: natives,
|
||||||
Trigger: trigger,
|
Trigger: trigger,
|
||||||
Block: block,
|
Block: block,
|
||||||
Tx: tx,
|
Tx: tx,
|
||||||
|
@ -48,3 +56,64 @@ type Function struct {
|
||||||
Func func(*Context, *vm.VM) error
|
Func func(*Context, *vm.VM) error
|
||||||
Price int
|
Price int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Method is a signature for a native method.
|
||||||
|
type Method = func(ic *Context, args []vm.StackItem) vm.StackItem
|
||||||
|
|
||||||
|
// MethodAndPrice is a native-contract method descriptor.
|
||||||
|
type MethodAndPrice struct {
|
||||||
|
Func Method
|
||||||
|
Price int64
|
||||||
|
RequiredFlags smartcontract.CallFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contract is an interface for all native contracts.
|
||||||
|
type Contract interface {
|
||||||
|
Initialize(*Context) error
|
||||||
|
Metadata() *ContractMD
|
||||||
|
OnPersist(*Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContractMD represents native contract instance.
|
||||||
|
type ContractMD struct {
|
||||||
|
Manifest manifest.Manifest
|
||||||
|
ServiceName string
|
||||||
|
ServiceID uint32
|
||||||
|
Script []byte
|
||||||
|
Hash util.Uint160
|
||||||
|
Methods map[string]MethodAndPrice
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContractMD returns Contract with the specified list of methods.
|
||||||
|
func NewContractMD(name string) *ContractMD {
|
||||||
|
c := &ContractMD{
|
||||||
|
ServiceName: name,
|
||||||
|
ServiceID: emit.InteropNameToID([]byte(name)),
|
||||||
|
Methods: make(map[string]MethodAndPrice),
|
||||||
|
}
|
||||||
|
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
emit.Syscall(w.BinWriter, c.ServiceName)
|
||||||
|
c.Script = w.Bytes()
|
||||||
|
c.Hash = hash.Hash160(c.Script)
|
||||||
|
c.Manifest = *manifest.DefaultManifest(c.Hash)
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddMethod adds new method to a native contract.
|
||||||
|
func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method, safe bool) {
|
||||||
|
c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, *desc)
|
||||||
|
c.Methods[desc.Name] = *md
|
||||||
|
if safe {
|
||||||
|
c.Manifest.SafeMethods.Add(desc.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddEvent adds new event to a native contract.
|
||||||
|
func (c *ContractMD) AddEvent(name string, ps ...manifest.Parameter) {
|
||||||
|
c.Manifest.ABI.Events = append(c.Manifest.ABI.Events, manifest.Event{
|
||||||
|
Name: name,
|
||||||
|
Parameters: ps,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
@ -236,13 +236,6 @@ func witnessGetVerificationScript(ic *interop.Context, v *vm.VM) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// bcGetValidators returns validators.
|
|
||||||
func bcGetValidators(ic *interop.Context, v *vm.VM) error {
|
|
||||||
validators := ic.DAO.GetValidators()
|
|
||||||
v.Estack().PushVal(validators)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// popInputFromVM returns transaction.Input from the first estack element.
|
// popInputFromVM returns transaction.Input from the first estack element.
|
||||||
func popInputFromVM(v *vm.VM) (*transaction.Input, error) {
|
func popInputFromVM(v *vm.VM) (*transaction.Input, error) {
|
||||||
inInterface := v.Estack().Pop().Value()
|
inInterface := v.Estack().Pop().Value()
|
||||||
|
@ -404,24 +397,6 @@ func accountGetScriptHash(ic *interop.Context, v *vm.VM) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// accountGetVotes returns votes of a given account.
|
|
||||||
func accountGetVotes(ic *interop.Context, v *vm.VM) error {
|
|
||||||
accInterface := v.Estack().Pop().Value()
|
|
||||||
acc, ok := accInterface.(*state.Account)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("%T is not an account state", acc)
|
|
||||||
}
|
|
||||||
if len(acc.Votes) > vm.MaxArraySize {
|
|
||||||
return errors.New("too many votes")
|
|
||||||
}
|
|
||||||
votes := make([]vm.StackItem, 0, len(acc.Votes))
|
|
||||||
for _, key := range acc.Votes {
|
|
||||||
votes = append(votes, vm.NewByteArrayItem(key.Bytes()))
|
|
||||||
}
|
|
||||||
v.Estack().PushVal(votes)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// accountIsStandard checks whether given account is standard.
|
// accountIsStandard checks whether given account is standard.
|
||||||
func accountIsStandard(ic *interop.Context, v *vm.VM) error {
|
func accountIsStandard(ic *interop.Context, v *vm.VM) error {
|
||||||
accbytes := v.Estack().Pop().Bytes()
|
accbytes := v.Estack().Pop().Bytes()
|
||||||
|
|
|
@ -417,17 +417,6 @@ func TestAccountGetScriptHash(t *testing.T) {
|
||||||
require.Equal(t, accState.ScriptHash.BytesBE(), hash)
|
require.Equal(t, accState.ScriptHash.BytesBE(), hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAccountGetVotes(t *testing.T) {
|
|
||||||
v, accState, context, chain := createVMAndAccState(t)
|
|
||||||
defer chain.Close()
|
|
||||||
v.Estack().PushVal(vm.NewInteropItem(accState))
|
|
||||||
|
|
||||||
err := accountGetVotes(context, v)
|
|
||||||
require.NoError(t, err)
|
|
||||||
votes := v.Estack().Pop().Value().([]vm.StackItem)
|
|
||||||
require.Equal(t, vm.NewByteArrayItem(accState.Votes[0].Bytes()), votes[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContractGetScript(t *testing.T) {
|
func TestContractGetScript(t *testing.T) {
|
||||||
v, contractState, context, chain := createVMAndContractState(t)
|
v, contractState, context, chain := createVMAndContractState(t)
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
|
@ -603,9 +592,6 @@ func createVMAndAccState(t *testing.T) (*vm.VM, *state.Account, *interop.Context
|
||||||
hash, err := util.Uint160DecodeStringBE(rawHash)
|
hash, err := util.Uint160DecodeStringBE(rawHash)
|
||||||
accountState := state.NewAccount(hash)
|
accountState := state.NewAccount(hash)
|
||||||
|
|
||||||
key := &keys.PublicKey{X: big.NewInt(1), Y: big.NewInt(1)}
|
|
||||||
accountState.Votes = []*keys.PublicKey{key}
|
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
chain := newTestChain(t)
|
chain := newTestChain(t)
|
||||||
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, nil)
|
context := chain.newInteropContext(trigger.Application, dao.NewSimple(storage.NewMemoryStore()), nil, nil)
|
||||||
|
|
|
@ -115,7 +115,6 @@ var systemInterops = []interop.Function{
|
||||||
var neoInterops = []interop.Function{
|
var neoInterops = []interop.Function{
|
||||||
{Name: "Neo.Account.GetBalance", Func: accountGetBalance, Price: 1},
|
{Name: "Neo.Account.GetBalance", Func: accountGetBalance, Price: 1},
|
||||||
{Name: "Neo.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1},
|
{Name: "Neo.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1},
|
||||||
{Name: "Neo.Account.GetVotes", Func: accountGetVotes, Price: 1},
|
|
||||||
{Name: "Neo.Account.IsStandard", Func: accountIsStandard, Price: 100},
|
{Name: "Neo.Account.IsStandard", Func: accountIsStandard, Price: 100},
|
||||||
{Name: "Neo.Asset.Create", Func: assetCreate, Price: 0},
|
{Name: "Neo.Asset.Create", Func: assetCreate, Price: 0},
|
||||||
{Name: "Neo.Asset.GetAdmin", Func: assetGetAdmin, Price: 1},
|
{Name: "Neo.Asset.GetAdmin", Func: assetGetAdmin, Price: 1},
|
||||||
|
@ -140,7 +139,6 @@ var neoInterops = []interop.Function{
|
||||||
{Name: "Neo.Blockchain.GetHeight", Func: bcGetHeight, Price: 1},
|
{Name: "Neo.Blockchain.GetHeight", Func: bcGetHeight, Price: 1},
|
||||||
{Name: "Neo.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100},
|
{Name: "Neo.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100},
|
||||||
{Name: "Neo.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100},
|
{Name: "Neo.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100},
|
||||||
{Name: "Neo.Blockchain.GetValidators", Func: bcGetValidators, Price: 200},
|
|
||||||
{Name: "Neo.Contract.Create", Func: contractCreate, Price: 0},
|
{Name: "Neo.Contract.Create", Func: contractCreate, Price: 0},
|
||||||
{Name: "Neo.Contract.Destroy", Func: contractDestroy, Price: 1},
|
{Name: "Neo.Contract.Destroy", Func: contractDestroy, Price: 1},
|
||||||
{Name: "Neo.Contract.GetScript", Func: contractGetScript, Price: 1},
|
{Name: "Neo.Contract.GetScript", Func: contractGetScript, Price: 1},
|
||||||
|
@ -204,7 +202,6 @@ var neoInterops = []interop.Function{
|
||||||
// Old compatibility APIs.
|
// Old compatibility APIs.
|
||||||
{Name: "AntShares.Account.GetBalance", Func: accountGetBalance, Price: 1},
|
{Name: "AntShares.Account.GetBalance", Func: accountGetBalance, Price: 1},
|
||||||
{Name: "AntShares.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1},
|
{Name: "AntShares.Account.GetScriptHash", Func: accountGetScriptHash, Price: 1},
|
||||||
{Name: "AntShares.Account.GetVotes", Func: accountGetVotes, Price: 1},
|
|
||||||
{Name: "AntShares.Asset.Create", Func: assetCreate, Price: 0},
|
{Name: "AntShares.Asset.Create", Func: assetCreate, Price: 0},
|
||||||
{Name: "AntShares.Asset.GetAdmin", Func: assetGetAdmin, Price: 1},
|
{Name: "AntShares.Asset.GetAdmin", Func: assetGetAdmin, Price: 1},
|
||||||
{Name: "AntShares.Asset.GetAmount", Func: assetGetAmount, Price: 1},
|
{Name: "AntShares.Asset.GetAmount", Func: assetGetAmount, Price: 1},
|
||||||
|
@ -227,7 +224,6 @@ var neoInterops = []interop.Function{
|
||||||
{Name: "AntShares.Blockchain.GetHeader", Func: bcGetHeader, Price: 100},
|
{Name: "AntShares.Blockchain.GetHeader", Func: bcGetHeader, Price: 100},
|
||||||
{Name: "AntShares.Blockchain.GetHeight", Func: bcGetHeight, Price: 1},
|
{Name: "AntShares.Blockchain.GetHeight", Func: bcGetHeight, Price: 1},
|
||||||
{Name: "AntShares.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100},
|
{Name: "AntShares.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100},
|
||||||
{Name: "AntShares.Blockchain.GetValidators", Func: bcGetValidators, Price: 200},
|
|
||||||
{Name: "AntShares.Contract.Create", Func: contractCreate, Price: 0},
|
{Name: "AntShares.Contract.Create", Func: contractCreate, Price: 0},
|
||||||
{Name: "AntShares.Contract.Destroy", Func: contractDestroy, Price: 1},
|
{Name: "AntShares.Contract.Destroy", Func: contractDestroy, Price: 1},
|
||||||
{Name: "AntShares.Contract.GetScript", Func: contractGetScript, Price: 1},
|
{Name: "AntShares.Contract.GetScript", Func: contractGetScript, Price: 1},
|
||||||
|
|
|
@ -34,7 +34,6 @@ func TestUnexpectedNonInterops(t *testing.T) {
|
||||||
funcs := []func(*interop.Context, *vm.VM) error{
|
funcs := []func(*interop.Context, *vm.VM) error{
|
||||||
accountGetBalance,
|
accountGetBalance,
|
||||||
accountGetScriptHash,
|
accountGetScriptHash,
|
||||||
accountGetVotes,
|
|
||||||
assetGetAdmin,
|
assetGetAdmin,
|
||||||
assetGetAmount,
|
assetGetAmount,
|
||||||
assetGetAssetID,
|
assetGetAssetID,
|
||||||
|
|
|
@ -4,80 +4,20 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Method is a signature for a native method.
|
|
||||||
type Method = func(ic *interop.Context, args []vm.StackItem) vm.StackItem
|
|
||||||
|
|
||||||
// MethodAndPrice is a native-contract method descriptor.
|
|
||||||
type MethodAndPrice struct {
|
|
||||||
Func Method
|
|
||||||
Price int64
|
|
||||||
RequiredFlags smartcontract.CallFlag
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contract is an interface for all native contracts.
|
|
||||||
type Contract interface {
|
|
||||||
Metadata() *ContractMD
|
|
||||||
OnPersist(*interop.Context) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContractMD represents native contract instance.
|
|
||||||
type ContractMD struct {
|
|
||||||
Manifest manifest.Manifest
|
|
||||||
ServiceName string
|
|
||||||
ServiceID uint32
|
|
||||||
Script []byte
|
|
||||||
Hash util.Uint160
|
|
||||||
Methods map[string]MethodAndPrice
|
|
||||||
}
|
|
||||||
|
|
||||||
// Contracts is a set of registered native contracts.
|
// Contracts is a set of registered native contracts.
|
||||||
type Contracts struct {
|
type Contracts struct {
|
||||||
NEO *NEO
|
NEO *NEO
|
||||||
GAS *GAS
|
GAS *GAS
|
||||||
Contracts []Contract
|
Contracts []interop.Contract
|
||||||
}
|
|
||||||
|
|
||||||
// SetGAS sets GAS native contract.
|
|
||||||
func (cs *Contracts) SetGAS(g *GAS) {
|
|
||||||
cs.GAS = g
|
|
||||||
cs.Contracts = append(cs.Contracts, g)
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetNEO sets NEO native contract.
|
|
||||||
func (cs *Contracts) SetNEO(n *NEO) {
|
|
||||||
cs.NEO = n
|
|
||||||
cs.Contracts = append(cs.Contracts, n)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContractMD returns Contract with the specified list of methods.
|
|
||||||
func NewContractMD(name string) *ContractMD {
|
|
||||||
c := &ContractMD{
|
|
||||||
ServiceName: name,
|
|
||||||
ServiceID: emit.InteropNameToID([]byte(name)),
|
|
||||||
Methods: make(map[string]MethodAndPrice),
|
|
||||||
}
|
|
||||||
|
|
||||||
w := io.NewBufBinWriter()
|
|
||||||
emit.Syscall(w.BinWriter, c.ServiceName)
|
|
||||||
c.Script = w.Bytes()
|
|
||||||
c.Hash = hash.Hash160(c.Script)
|
|
||||||
c.Manifest = *manifest.DefaultManifest(c.Hash)
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByHash returns native contract with the specified hash.
|
// ByHash returns native contract with the specified hash.
|
||||||
func (cs *Contracts) ByHash(h util.Uint160) Contract {
|
func (cs *Contracts) ByHash(h util.Uint160) interop.Contract {
|
||||||
for _, ctr := range cs.Contracts {
|
for _, ctr := range cs.Contracts {
|
||||||
if ctr.Metadata().Hash.Equals(h) {
|
if ctr.Metadata().Hash.Equals(h) {
|
||||||
return ctr
|
return ctr
|
||||||
|
@ -87,7 +27,7 @@ func (cs *Contracts) ByHash(h util.Uint160) Contract {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByID returns native contract with the specified id.
|
// ByID returns native contract with the specified id.
|
||||||
func (cs *Contracts) ByID(id uint32) Contract {
|
func (cs *Contracts) ByID(id uint32) interop.Contract {
|
||||||
for _, ctr := range cs.Contracts {
|
for _, ctr := range cs.Contracts {
|
||||||
if ctr.Metadata().ServiceID == id {
|
if ctr.Metadata().ServiceID == id {
|
||||||
return ctr
|
return ctr
|
||||||
|
@ -96,33 +36,21 @@ func (cs *Contracts) ByID(id uint32) Contract {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddMethod adds new method to a native contract.
|
// NewContracts returns new set of native contracts with new GAS and NEO
|
||||||
func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method, safe bool) {
|
// contracts.
|
||||||
c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, *desc)
|
|
||||||
c.Methods[desc.Name] = *md
|
|
||||||
if safe {
|
|
||||||
c.Manifest.SafeMethods.Add(desc.Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddEvent adds new event to a native contract.
|
|
||||||
func (c *ContractMD) AddEvent(name string, ps ...manifest.Parameter) {
|
|
||||||
c.Manifest.ABI.Events = append(c.Manifest.ABI.Events, manifest.Event{
|
|
||||||
Name: name,
|
|
||||||
Parameters: ps,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContracts returns new empty set of native contracts.
|
|
||||||
func NewContracts() *Contracts {
|
func NewContracts() *Contracts {
|
||||||
return &Contracts{
|
cs := new(Contracts)
|
||||||
Contracts: []Contract{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add adds new native contracts to the list.
|
gas := NewGAS()
|
||||||
func (cs *Contracts) Add(c Contract) {
|
neo := NewNEO()
|
||||||
cs.Contracts = append(cs.Contracts, c)
|
neo.GAS = gas
|
||||||
|
gas.NEO = neo
|
||||||
|
|
||||||
|
cs.GAS = gas
|
||||||
|
cs.Contracts = append(cs.Contracts, gas)
|
||||||
|
cs.NEO = neo
|
||||||
|
cs.Contracts = append(cs.Contracts, neo)
|
||||||
|
return cs
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNativeInterop returns an interop getter for a given set of contracts.
|
// GetNativeInterop returns an interop getter for a given set of contracts.
|
||||||
|
@ -139,7 +67,7 @@ func (cs *Contracts) GetNativeInterop(ic *interop.Context) func(uint32) *vm.Inte
|
||||||
}
|
}
|
||||||
|
|
||||||
// getNativeInterop returns native contract interop.
|
// getNativeInterop returns native contract interop.
|
||||||
func getNativeInterop(ic *interop.Context, c Contract) func(v *vm.VM) error {
|
func getNativeInterop(ic *interop.Context, c interop.Contract) func(v *vm.VM) error {
|
||||||
return func(v *vm.VM) error {
|
return func(v *vm.VM) error {
|
||||||
h := v.GetContextScriptHash(0)
|
h := v.GetContextScriptHash(0)
|
||||||
if !h.Equals(c.Metadata().Hash) {
|
if !h.Equals(c.Metadata().Hash) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package native
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
@ -12,5 +13,12 @@ func Deploy(ic *interop.Context, _ *vm.VM) error {
|
||||||
if ic.Block.Index != 0 {
|
if ic.Block.Index != 0 {
|
||||||
return errors.New("native contracts can be deployed only at 0 block")
|
return errors.New("native contracts can be deployed only at 0 block")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, native := range ic.Natives {
|
||||||
|
if err := native.Initialize(ic); err != nil {
|
||||||
|
md := native.Metadata()
|
||||||
|
return fmt.Errorf("initializing %s native contract: %v", md.ServiceName, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,14 +6,12 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GAS represents GAS native contract.
|
// GAS represents GAS native contract.
|
||||||
|
@ -24,13 +22,17 @@ type GAS struct {
|
||||||
|
|
||||||
const gasSyscallName = "Neo.Native.Tokens.GAS"
|
const gasSyscallName = "Neo.Native.Tokens.GAS"
|
||||||
|
|
||||||
|
// GASFactor is a divisor for finding GAS integral value.
|
||||||
|
const GASFactor = NEOTotalSupply
|
||||||
|
const initialGAS = 30000000
|
||||||
|
|
||||||
// NewGAS returns GAS native contract.
|
// NewGAS returns GAS native contract.
|
||||||
func NewGAS() *GAS {
|
func NewGAS() *GAS {
|
||||||
nep5 := newNEP5Native(gasSyscallName)
|
nep5 := newNEP5Native(gasSyscallName)
|
||||||
nep5.name = "GAS"
|
nep5.name = "GAS"
|
||||||
nep5.symbol = "gas"
|
nep5.symbol = "gas"
|
||||||
nep5.decimals = 8
|
nep5.decimals = 8
|
||||||
nep5.factor = 100000000
|
nep5.factor = GASFactor
|
||||||
|
|
||||||
g := &GAS{nep5TokenNative: *nep5}
|
g := &GAS{nep5TokenNative: *nep5}
|
||||||
|
|
||||||
|
@ -44,44 +46,35 @@ func NewGAS() *GAS {
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
// initFromStore initializes variable contract parameters from the store.
|
func (g *GAS) increaseBalance(_ *interop.Context, _ util.Uint160, si *state.StorageItem, amount *big.Int) error {
|
||||||
func (g *GAS) initFromStore(data []byte) error {
|
acc, err := state.NEP5BalanceStateFromBytes(si.Value)
|
||||||
g.totalSupply = *emit.BytesToInt(data)
|
if err != nil {
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GAS) serializeState() []byte {
|
|
||||||
return emit.IntToBytes(&g.totalSupply)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GAS) increaseBalance(_ *interop.Context, acc *state.Account, amount *big.Int) error {
|
|
||||||
if sign := amount.Sign(); sign == 0 {
|
if sign := amount.Sign(); sign == 0 {
|
||||||
return nil
|
return nil
|
||||||
} else if sign == -1 && acc.GAS.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
|
} else if sign == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
|
||||||
return errors.New("insufficient funds")
|
return errors.New("insufficient funds")
|
||||||
}
|
}
|
||||||
acc.GAS.Balance.Add(&acc.GAS.Balance, amount)
|
acc.Balance.Add(&acc.Balance, amount)
|
||||||
|
si.Value = acc.Bytes()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize initializes GAS contract.
|
// Initialize initializes GAS contract.
|
||||||
func (g *GAS) Initialize(ic *interop.Context) error {
|
func (g *GAS) Initialize(ic *interop.Context) error {
|
||||||
data, err := ic.DAO.GetNativeContractState(g.Hash)
|
if err := g.nep5TokenNative.Initialize(ic); err != nil {
|
||||||
if err == nil {
|
|
||||||
return g.initFromStore(data)
|
|
||||||
} else if err != storage.ErrKeyNotFound {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if g.nep5TokenNative.getTotalSupply(ic).Sign() != 0 {
|
||||||
if err := g.nep5TokenNative.Initialize(); err != nil {
|
return errors.New("already initialized")
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
h, _, err := getStandbyValidatorsHash(ic)
|
h, _, err := getStandbyValidatorsHash(ic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
g.mint(ic, h, big.NewInt(30000000*g.factor))
|
g.mint(ic, h, big.NewInt(initialGAS*GASFactor))
|
||||||
return ic.DAO.PutNativeContractState(g.Hash, g.serializeState())
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPersist implements Contract interface.
|
// OnPersist implements Contract interface.
|
||||||
|
@ -95,7 +88,7 @@ func (g *GAS) OnPersist(ic *interop.Context) error {
|
||||||
// netFee += tx.NetworkFee
|
// netFee += tx.NetworkFee
|
||||||
//}
|
//}
|
||||||
//g.mint(ic, <primary>, netFee)
|
//g.mint(ic, <primary>, netFee)
|
||||||
return ic.DAO.PutNativeContractState(g.Hash, g.serializeState())
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GAS) getSysFeeAmount(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
func (g *GAS) getSysFeeAmount(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
||||||
|
|
|
@ -9,13 +9,11 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,7 +23,40 @@ type NEO struct {
|
||||||
GAS *GAS
|
GAS *GAS
|
||||||
}
|
}
|
||||||
|
|
||||||
const neoSyscallName = "Neo.Native.Tokens.NEO"
|
// keyWithVotes is a serialized key with votes balance. It's not deserialized
|
||||||
|
// because some uses of it imply serialized-only usage and converting to
|
||||||
|
// PublicKey is quite expensive.
|
||||||
|
type keyWithVotes struct {
|
||||||
|
Key string
|
||||||
|
Votes *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
neoSyscallName = "Neo.Native.Tokens.NEO"
|
||||||
|
// NEOTotalSupply is the total amount of NEO in the system.
|
||||||
|
NEOTotalSupply = 100000000
|
||||||
|
// prefixValidator is a prefix used to store validator's data.
|
||||||
|
prefixValidator = 33
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// validatorsCountKey is a key used to store validators count
|
||||||
|
// used to determine the real number of validators.
|
||||||
|
validatorsCountKey = []byte{15}
|
||||||
|
// nextValidatorsKey is a key used to store validators for the
|
||||||
|
// next block.
|
||||||
|
nextValidatorsKey = []byte{14}
|
||||||
|
)
|
||||||
|
|
||||||
|
// makeValidatorKey creates a key from account script hash.
|
||||||
|
func makeValidatorKey(key *keys.PublicKey) []byte {
|
||||||
|
b := key.Bytes()
|
||||||
|
// Don't create a new buffer.
|
||||||
|
b = append(b, 0)
|
||||||
|
copy(b[1:], b[0:])
|
||||||
|
b[0] = prefixValidator
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
// NewNEO returns NEO native contract.
|
// NewNEO returns NEO native contract.
|
||||||
func NewNEO() *NEO {
|
func NewNEO() *NEO {
|
||||||
|
@ -55,7 +86,7 @@ func NewNEO() *NEO {
|
||||||
n.AddMethod(md, desc, false)
|
n.AddMethod(md, desc, false)
|
||||||
|
|
||||||
desc = newDescriptor("getRegisteredValidators", smartcontract.ArrayType)
|
desc = newDescriptor("getRegisteredValidators", smartcontract.ArrayType)
|
||||||
md = newMethodAndPrice(n.getRegisteredValidators, 1, smartcontract.NoneFlag)
|
md = newMethodAndPrice(n.getRegisteredValidatorsCall, 1, smartcontract.NoneFlag)
|
||||||
n.AddMethod(md, desc, true)
|
n.AddMethod(md, desc, true)
|
||||||
|
|
||||||
desc = newDescriptor("getValidators", smartcontract.ArrayType)
|
desc = newDescriptor("getValidators", smartcontract.ArrayType)
|
||||||
|
@ -74,22 +105,26 @@ func NewNEO() *NEO {
|
||||||
|
|
||||||
// Initialize initializes NEO contract.
|
// Initialize initializes NEO contract.
|
||||||
func (n *NEO) Initialize(ic *interop.Context) error {
|
func (n *NEO) Initialize(ic *interop.Context) error {
|
||||||
data, err := ic.DAO.GetNativeContractState(n.Hash)
|
var si state.StorageItem
|
||||||
if err == nil {
|
|
||||||
return n.initFromStore(data)
|
if err := n.nep5TokenNative.Initialize(ic); err != nil {
|
||||||
} else if err != storage.ErrKeyNotFound {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := n.nep5TokenNative.Initialize(); err != nil {
|
if n.nep5TokenNative.getTotalSupply(ic).Sign() != 0 {
|
||||||
return err
|
return errors.New("already initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vc := new(ValidatorsCount)
|
||||||
|
si.Value = vc.Bytes()
|
||||||
|
if err := ic.DAO.PutStorageItem(n.Hash, validatorsCountKey, &si); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
h, vs, err := getStandbyValidatorsHash(ic)
|
h, vs, err := getStandbyValidatorsHash(ic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
n.mint(ic, h, big.NewInt(100000000*n.factor))
|
n.mint(ic, h, big.NewInt(NEOTotalSupply))
|
||||||
|
|
||||||
for i := range vs {
|
for i := range vs {
|
||||||
if err := n.registerValidatorInternal(ic, vs[i]); err != nil {
|
if err := n.registerValidatorInternal(ic, vs[i]); err != nil {
|
||||||
|
@ -97,54 +132,67 @@ func (n *NEO) Initialize(ic *interop.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ic.DAO.PutNativeContractState(n.Hash, n.serializeState())
|
|
||||||
}
|
|
||||||
|
|
||||||
// initFromStore initializes variable contract parameters from the store.
|
|
||||||
func (n *NEO) initFromStore(data []byte) error {
|
|
||||||
n.totalSupply = *emit.BytesToInt(data)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) serializeState() []byte {
|
|
||||||
return emit.IntToBytes(&n.totalSupply)
|
|
||||||
}
|
|
||||||
|
|
||||||
// OnPersist implements Contract interface.
|
// OnPersist implements Contract interface.
|
||||||
func (n *NEO) OnPersist(ic *interop.Context) error {
|
func (n *NEO) OnPersist(ic *interop.Context) error {
|
||||||
pubs, err := n.GetValidatorsInternal(ic.Chain, ic.DAO)
|
pubs, err := n.GetValidatorsInternal(ic.Chain, ic.DAO)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ic.DAO.PutNextBlockValidators(pubs); err != nil {
|
si := new(state.StorageItem)
|
||||||
return err
|
si.Value = pubs.Bytes()
|
||||||
}
|
return ic.DAO.PutStorageItem(n.Hash, nextValidatorsKey, si)
|
||||||
return ic.DAO.PutNativeContractState(n.Hash, n.serializeState())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) increaseBalance(ic *interop.Context, acc *state.Account, amount *big.Int) error {
|
func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.StorageItem, amount *big.Int) error {
|
||||||
if sign := amount.Sign(); sign == 0 {
|
acc, err := state.NEOBalanceStateFromBytes(si.Value)
|
||||||
return nil
|
|
||||||
} else if sign == -1 && acc.NEO.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
|
|
||||||
return errors.New("insufficient funds")
|
|
||||||
}
|
|
||||||
if err := n.distributeGas(ic, acc); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
acc.NEO.Balance.Add(&acc.NEO.Balance, amount)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NEO) distributeGas(ic *interop.Context, acc *state.Account) error {
|
|
||||||
if ic.Block == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
sys, net, err := ic.Chain.CalculateClaimable(util.Fixed8(acc.NEO.Balance.Int64()), acc.NEO.BalanceHeight, ic.Block.Index)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
acc.NEO.BalanceHeight = ic.Block.Index
|
if amount.Sign() == -1 && acc.Balance.Cmp(new(big.Int).Neg(amount)) == -1 {
|
||||||
n.GAS.mint(ic, acc.ScriptHash, big.NewInt(int64(sys+net)))
|
return errors.New("insufficient funds")
|
||||||
|
}
|
||||||
|
if err := n.distributeGas(ic, h, acc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if amount.Sign() == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if len(acc.Votes) > 0 {
|
||||||
|
if err := n.ModifyAccountVotes(acc, ic.DAO, new(big.Int).Neg(&acc.Balance)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
siVC := ic.DAO.GetStorageItem(n.Hash, validatorsCountKey)
|
||||||
|
if siVC == nil {
|
||||||
|
return errors.New("validators count uninitialized")
|
||||||
|
}
|
||||||
|
vc, err := ValidatorsCountFromBytes(siVC.Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
vc[len(acc.Votes)-1].Add(&vc[len(acc.Votes)-1], amount)
|
||||||
|
siVC.Value = vc.Bytes()
|
||||||
|
if err := ic.DAO.PutStorageItem(n.Hash, validatorsCountKey, siVC); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
acc.Balance.Add(&acc.Balance, amount)
|
||||||
|
si.Value = acc.Bytes()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NEO) distributeGas(ic *interop.Context, h util.Uint160, acc *state.NEOBalanceState) error {
|
||||||
|
if ic.Block == nil || ic.Block.Index == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
sys, net, err := ic.Chain.CalculateClaimable(util.Fixed8(acc.Balance.Int64()), acc.BalanceHeight, ic.Block.Index)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
acc.BalanceHeight = ic.Block.Index
|
||||||
|
n.GAS.mint(ic, h, big.NewInt(int64(sys+net)))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,11 +218,17 @@ func (n *NEO) registerValidator(ic *interop.Context, args []vm.StackItem) vm.Sta
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey) error {
|
func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey) error {
|
||||||
_, err := ic.DAO.GetValidatorState(pub)
|
key := makeValidatorKey(pub)
|
||||||
if err == nil {
|
si := ic.DAO.GetStorageItem(n.Hash, key)
|
||||||
return err
|
if si != nil {
|
||||||
|
return errors.New("already registered")
|
||||||
}
|
}
|
||||||
return ic.DAO.PutValidatorState(&state.Validator{PublicKey: pub})
|
si = new(state.StorageItem)
|
||||||
|
// It's the same simple counter, calling it `Votes` instead of `Balance`
|
||||||
|
// doesn't help a lot.
|
||||||
|
votes := state.NEP5BalanceState{}
|
||||||
|
si.Value = votes.Bytes()
|
||||||
|
return ic.DAO.PutStorageItem(n.Hash, key, si)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) vote(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
func (n *NEO) vote(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
||||||
|
@ -203,103 +257,147 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public
|
||||||
} else if !ok {
|
} else if !ok {
|
||||||
return errors.New("invalid signature")
|
return errors.New("invalid signature")
|
||||||
}
|
}
|
||||||
acc, err := ic.DAO.GetAccountState(h)
|
key := makeAccountKey(h)
|
||||||
|
si := ic.DAO.GetStorageItem(n.Hash, key)
|
||||||
|
if si == nil {
|
||||||
|
return errors.New("invalid account")
|
||||||
|
}
|
||||||
|
acc, err := state.NEOBalanceStateFromBytes(si.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
balance := util.Fixed8(acc.NEO.Balance.Int64())
|
if err := n.ModifyAccountVotes(acc, ic.DAO, new(big.Int).Neg(&acc.Balance)); err != nil {
|
||||||
if err := ModifyAccountVotes(acc, ic.DAO, -balance); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pubs = pubs.Unique()
|
pubs = pubs.Unique()
|
||||||
|
// Check validators registration.
|
||||||
var newPubs keys.PublicKeys
|
var newPubs keys.PublicKeys
|
||||||
for _, pub := range pubs {
|
for _, pub := range pubs {
|
||||||
_, err := ic.DAO.GetValidatorState(pub)
|
if ic.DAO.GetStorageItem(n.Hash, makeValidatorKey(pub)) == nil {
|
||||||
if err != nil {
|
continue
|
||||||
if err == storage.ErrKeyNotFound {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
newPubs = append(newPubs, pub)
|
newPubs = append(newPubs, pub)
|
||||||
}
|
}
|
||||||
if lp, lv := len(newPubs), len(acc.Votes); lp != lv {
|
if lp, lv := len(newPubs), len(acc.Votes); lp != lv {
|
||||||
vc, err := ic.DAO.GetValidatorsCount()
|
si := ic.DAO.GetStorageItem(n.Hash, validatorsCountKey)
|
||||||
|
if si == nil {
|
||||||
|
return errors.New("validators count uninitialized")
|
||||||
|
}
|
||||||
|
vc, err := ValidatorsCountFromBytes(si.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if lv > 0 {
|
if lv > 0 {
|
||||||
vc[lv-1] -= balance
|
vc[lv-1].Sub(&vc[lv-1], &acc.Balance)
|
||||||
}
|
}
|
||||||
if len(newPubs) > 0 {
|
if len(newPubs) > 0 {
|
||||||
vc[lp-1] += balance
|
vc[lp-1].Add(&vc[lp-1], &acc.Balance)
|
||||||
}
|
}
|
||||||
if err := ic.DAO.PutValidatorsCount(vc); err != nil {
|
si.Value = vc.Bytes()
|
||||||
|
if err := ic.DAO.PutStorageItem(n.Hash, validatorsCountKey, si); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
acc.Votes = newPubs
|
acc.Votes = newPubs
|
||||||
return ModifyAccountVotes(acc, ic.DAO, balance)
|
if err := n.ModifyAccountVotes(acc, ic.DAO, &acc.Balance); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
si.Value = acc.Bytes()
|
||||||
|
return ic.DAO.PutStorageItem(n.Hash, key, si)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ModifyAccountVotes modifies votes of the specified account by value (can be negative).
|
// ModifyAccountVotes modifies votes of the specified account by value (can be negative).
|
||||||
func ModifyAccountVotes(acc *state.Account, d dao.DAO, value util.Fixed8) error {
|
func (n *NEO) ModifyAccountVotes(acc *state.NEOBalanceState, d dao.DAO, value *big.Int) error {
|
||||||
for _, vote := range acc.Votes {
|
for _, vote := range acc.Votes {
|
||||||
validator, err := d.GetValidatorStateOrNew(vote)
|
key := makeValidatorKey(vote)
|
||||||
|
si := d.GetStorageItem(n.Hash, key)
|
||||||
|
if si == nil {
|
||||||
|
return errors.New("invalid validator")
|
||||||
|
}
|
||||||
|
votes, err := state.NEP5BalanceStateFromBytes(si.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
validator.Votes += value
|
votes.Balance.Add(&votes.Balance, value)
|
||||||
if validator.UnregisteredAndHasNoVotes() {
|
si.Value = votes.Bytes()
|
||||||
if err := d.DeleteValidatorState(validator); err != nil {
|
if err := d.PutStorageItem(n.Hash, key, si); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := d.PutValidatorState(validator); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) getRegisteredValidators(ic *interop.Context, _ []vm.StackItem) vm.StackItem {
|
func (n *NEO) getRegisteredValidators(d dao.DAO) ([]keyWithVotes, error) {
|
||||||
vs := ic.DAO.GetValidators()
|
siMap, err := d.GetStorageItemsWithPrefix(n.Hash, []byte{prefixValidator})
|
||||||
arr := make([]vm.StackItem, len(vs))
|
if err != nil {
|
||||||
for i := range vs {
|
return nil, err
|
||||||
|
}
|
||||||
|
arr := make([]keyWithVotes, 0, len(siMap))
|
||||||
|
for key, si := range siMap {
|
||||||
|
votes, err := state.NEP5BalanceStateFromBytes(si.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
arr = append(arr, keyWithVotes{key, &votes.Balance})
|
||||||
|
}
|
||||||
|
return arr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetRegisteredValidators returns current registered validators list with keys
|
||||||
|
// and votes.
|
||||||
|
func (n *NEO) GetRegisteredValidators(d dao.DAO) ([]state.Validator, error) {
|
||||||
|
kvs, err := n.getRegisteredValidators(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
arr := make([]state.Validator, len(kvs))
|
||||||
|
for i := range kvs {
|
||||||
|
arr[i].Key, err = keys.NewPublicKeyFromBytes([]byte(kvs[i].Key))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
arr[i].Votes = kvs[i].Votes
|
||||||
|
}
|
||||||
|
return arr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NEO) getRegisteredValidatorsCall(ic *interop.Context, _ []vm.StackItem) vm.StackItem {
|
||||||
|
validators, err := n.getRegisteredValidators(ic.DAO)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
arr := make([]vm.StackItem, len(validators))
|
||||||
|
for i := range validators {
|
||||||
arr[i] = vm.NewStructItem([]vm.StackItem{
|
arr[i] = vm.NewStructItem([]vm.StackItem{
|
||||||
vm.NewByteArrayItem(vs[i].PublicKey.Bytes()),
|
vm.NewByteArrayItem([]byte(validators[i].Key)),
|
||||||
vm.NewBigIntegerItem(big.NewInt(int64(vs[i].Votes))),
|
vm.NewBigIntegerItem(validators[i].Votes),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return vm.NewArrayItem(arr)
|
return vm.NewArrayItem(arr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetValidatorsInternal returns a list of current validators.
|
// GetValidatorsInternal returns a list of current validators.
|
||||||
func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) ([]*keys.PublicKey, error) {
|
func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) {
|
||||||
validatorsCount, err := d.GetValidatorsCount()
|
si := d.GetStorageItem(n.Hash, validatorsCountKey)
|
||||||
|
if 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 {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
sort.Slice(validators, func(i, j int) bool {
|
||||||
// Unregistered validators go to the end of the list.
|
|
||||||
if validators[i].Registered != validators[j].Registered {
|
|
||||||
return validators[i].Registered
|
|
||||||
}
|
|
||||||
// The most-voted validators should end up in the front of the list.
|
// The most-voted validators should end up in the front of the list.
|
||||||
if validators[i].Votes != validators[j].Votes {
|
cmp := validators[i].Votes.Cmp(validators[j].Votes)
|
||||||
return validators[i].Votes > validators[j].Votes
|
if cmp != 0 {
|
||||||
|
return cmp > 0
|
||||||
}
|
}
|
||||||
// Ties are broken with public keys.
|
// Ties are broken with public keys.
|
||||||
return validators[i].PublicKey.Cmp(validators[j].PublicKey) == -1
|
return validators[i].Key.Cmp(validators[j].Key) == -1
|
||||||
})
|
})
|
||||||
|
|
||||||
count := validatorsCount.GetWeightedAverage()
|
count := validatorsCount.GetWeightedAverage()
|
||||||
|
@ -314,8 +412,8 @@ func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) ([]
|
||||||
uniqueSBValidators := standByValidators.Unique()
|
uniqueSBValidators := standByValidators.Unique()
|
||||||
result := keys.PublicKeys{}
|
result := keys.PublicKeys{}
|
||||||
for _, validator := range validators {
|
for _, validator := range validators {
|
||||||
if validator.RegisteredAndHasVotes() || uniqueSBValidators.Contains(validator.PublicKey) {
|
if validator.Votes.Sign() > 0 || uniqueSBValidators.Contains(validator.Key) {
|
||||||
result = append(result, validator.PublicKey)
|
result = append(result, validator.Key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,14 +447,17 @@ func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []vm.StackItem) vm.S
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNextBlockValidatorsInternal returns next block validators.
|
// GetNextBlockValidatorsInternal returns next block validators.
|
||||||
func (n *NEO) GetNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) ([]*keys.PublicKey, error) {
|
func (n *NEO) GetNextBlockValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) {
|
||||||
result, err := d.GetNextBlockValidators()
|
si := d.GetStorageItem(n.Hash, nextValidatorsKey)
|
||||||
|
if si == nil {
|
||||||
|
return n.GetValidatorsInternal(bc, d)
|
||||||
|
}
|
||||||
|
pubs := keys.PublicKeys{}
|
||||||
|
err := pubs.DecodeBytes(si.Value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
} else if result == nil {
|
|
||||||
return bc.GetStandByValidators()
|
|
||||||
}
|
}
|
||||||
return result, nil
|
return pubs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func pubsToArray(pubs keys.PublicKeys) vm.StackItem {
|
func pubsToArray(pubs keys.PublicKeys) vm.StackItem {
|
||||||
|
|
|
@ -10,28 +10,42 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// prefixAccount is the standard prefix used to store account data.
|
||||||
|
const prefixAccount = 20
|
||||||
|
|
||||||
|
// makeAccountKey creates a key from account script hash.
|
||||||
|
func makeAccountKey(h util.Uint160) []byte {
|
||||||
|
k := make([]byte, util.Uint160Size+1)
|
||||||
|
k[0] = prefixAccount
|
||||||
|
copy(k[1:], h.BytesBE())
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
// nep5TokenNative represents NEP-5 token contract.
|
// nep5TokenNative represents NEP-5 token contract.
|
||||||
type nep5TokenNative struct {
|
type nep5TokenNative struct {
|
||||||
ContractMD
|
interop.ContractMD
|
||||||
name string
|
name string
|
||||||
symbol string
|
symbol string
|
||||||
decimals int64
|
decimals int64
|
||||||
factor int64
|
factor int64
|
||||||
totalSupply big.Int
|
onPersist func(*interop.Context) error
|
||||||
onPersist func(*interop.Context) error
|
incBalance func(*interop.Context, util.Uint160, *state.StorageItem, *big.Int) error
|
||||||
incBalance func(*interop.Context, *state.Account, *big.Int) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Metadata() *ContractMD {
|
// totalSupplyKey is the key used to store totalSupply value.
|
||||||
|
var totalSupplyKey = []byte{11}
|
||||||
|
|
||||||
|
func (c *nep5TokenNative) Metadata() *interop.ContractMD {
|
||||||
return &c.ContractMD
|
return &c.ContractMD
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Contract = (*nep5TokenNative)(nil)
|
var _ interop.Contract = (*nep5TokenNative)(nil)
|
||||||
|
|
||||||
func newNEP5Native(name string) *nep5TokenNative {
|
func newNEP5Native(name string) *nep5TokenNative {
|
||||||
n := &nep5TokenNative{ContractMD: *NewContractMD(name)}
|
n := &nep5TokenNative{ContractMD: *interop.NewContractMD(name)}
|
||||||
|
|
||||||
desc := newDescriptor("name", smartcontract.StringType)
|
desc := newDescriptor("name", smartcontract.StringType)
|
||||||
md := newMethodAndPrice(n.Name, 1, smartcontract.NoneFlag)
|
md := newMethodAndPrice(n.Name, 1, smartcontract.NoneFlag)
|
||||||
|
@ -45,6 +59,10 @@ func newNEP5Native(name string) *nep5TokenNative {
|
||||||
md = newMethodAndPrice(n.Decimals, 1, smartcontract.NoneFlag)
|
md = newMethodAndPrice(n.Decimals, 1, smartcontract.NoneFlag)
|
||||||
n.AddMethod(md, desc, true)
|
n.AddMethod(md, desc, true)
|
||||||
|
|
||||||
|
desc = newDescriptor("totalSupply", smartcontract.IntegerType)
|
||||||
|
md = newMethodAndPrice(n.TotalSupply, 1, smartcontract.NoneFlag)
|
||||||
|
n.AddMethod(md, desc, true)
|
||||||
|
|
||||||
desc = newDescriptor("balanceOf", smartcontract.IntegerType,
|
desc = newDescriptor("balanceOf", smartcontract.IntegerType,
|
||||||
manifest.NewParameter("account", smartcontract.Hash160Type))
|
manifest.NewParameter("account", smartcontract.Hash160Type))
|
||||||
md = newMethodAndPrice(n.balanceOf, 1, smartcontract.NoneFlag)
|
md = newMethodAndPrice(n.balanceOf, 1, smartcontract.NoneFlag)
|
||||||
|
@ -63,7 +81,7 @@ func newNEP5Native(name string) *nep5TokenNative {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Initialize() error {
|
func (c *nep5TokenNative) Initialize(_ *interop.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,6 +97,23 @@ func (c *nep5TokenNative) Decimals(_ *interop.Context, _ []vm.StackItem) vm.Stac
|
||||||
return vm.NewBigIntegerItem(big.NewInt(c.decimals))
|
return vm.NewBigIntegerItem(big.NewInt(c.decimals))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *nep5TokenNative) TotalSupply(ic *interop.Context, _ []vm.StackItem) vm.StackItem {
|
||||||
|
return vm.NewBigIntegerItem(c.getTotalSupply(ic))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *nep5TokenNative) getTotalSupply(ic *interop.Context) *big.Int {
|
||||||
|
si := ic.DAO.GetStorageItem(c.Hash, totalSupplyKey)
|
||||||
|
if si == nil {
|
||||||
|
return big.NewInt(0)
|
||||||
|
}
|
||||||
|
return emit.BytesToInt(si.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *nep5TokenNative) saveTotalSupply(ic *interop.Context, supply *big.Int) error {
|
||||||
|
si := &state.StorageItem{Value: emit.IntToBytes(supply)}
|
||||||
|
return ic.DAO.PutStorageItem(c.Hash, totalSupplyKey, si)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Transfer(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
func (c *nep5TokenNative) Transfer(ic *interop.Context, args []vm.StackItem) vm.StackItem {
|
||||||
from := toUint160(args[0])
|
from := toUint160(args[0])
|
||||||
to := toUint160(args[1])
|
to := toUint160(args[1])
|
||||||
|
@ -89,7 +124,7 @@ func (c *nep5TokenNative) Transfer(ic *interop.Context, args []vm.StackItem) vm.
|
||||||
|
|
||||||
func addrToStackItem(u *util.Uint160) vm.StackItem {
|
func addrToStackItem(u *util.Uint160) vm.StackItem {
|
||||||
if u == nil {
|
if u == nil {
|
||||||
return nil
|
return vm.NullItem{}
|
||||||
}
|
}
|
||||||
return vm.NewByteArrayItem(u.BytesBE())
|
return vm.NewByteArrayItem(u.BytesBE())
|
||||||
}
|
}
|
||||||
|
@ -112,9 +147,10 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a
|
||||||
return errors.New("negative amount")
|
return errors.New("negative amount")
|
||||||
}
|
}
|
||||||
|
|
||||||
accFrom, err := ic.DAO.GetAccountStateOrNew(from)
|
keyFrom := makeAccountKey(from)
|
||||||
if err != nil {
|
siFrom := ic.DAO.GetStorageItem(c.Hash, keyFrom)
|
||||||
return err
|
if siFrom == nil {
|
||||||
|
return errors.New("insufficient funds")
|
||||||
}
|
}
|
||||||
|
|
||||||
isEmpty := from.Equals(to) || amount.Sign() == 0
|
isEmpty := from.Equals(to) || amount.Sign() == 0
|
||||||
|
@ -122,22 +158,23 @@ func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, a
|
||||||
if isEmpty {
|
if isEmpty {
|
||||||
inc = big.NewInt(0)
|
inc = big.NewInt(0)
|
||||||
}
|
}
|
||||||
if err := c.incBalance(ic, accFrom, inc); err != nil {
|
if err := c.incBalance(ic, from, siFrom, inc); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := ic.DAO.PutAccountState(accFrom); err != nil {
|
if err := ic.DAO.PutStorageItem(c.Hash, keyFrom, siFrom); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isEmpty {
|
if !isEmpty {
|
||||||
accTo, err := ic.DAO.GetAccountStateOrNew(to)
|
keyTo := makeAccountKey(to)
|
||||||
if err != nil {
|
siTo := ic.DAO.GetStorageItem(c.Hash, keyTo)
|
||||||
|
if siTo == nil {
|
||||||
|
siTo = new(state.StorageItem)
|
||||||
|
}
|
||||||
|
if err := c.incBalance(ic, to, siTo, amount); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := c.incBalance(ic, accTo, amount); err != nil {
|
if err := ic.DAO.PutStorageItem(c.Hash, keyTo, siTo); err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := ic.DAO.PutAccountState(accTo); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -174,18 +211,24 @@ func (c *nep5TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
acc, err := ic.DAO.GetAccountStateOrNew(h)
|
key := makeAccountKey(h)
|
||||||
if err != nil {
|
si := ic.DAO.GetStorageItem(c.Hash, key)
|
||||||
|
if si == nil {
|
||||||
|
si = new(state.StorageItem)
|
||||||
|
}
|
||||||
|
if err := c.incBalance(ic, h, si, amount); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if err := c.incBalance(ic, acc, amount); err != nil {
|
if err := ic.DAO.PutStorageItem(c.Hash, key, si); err != nil {
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
if err := ic.DAO.PutAccountState(acc); err != nil {
|
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.totalSupply.Add(&c.totalSupply, amount)
|
supply := c.getTotalSupply(ic)
|
||||||
|
supply.Add(supply, amount)
|
||||||
|
err := c.saveTotalSupply(ic, supply)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) OnPersist(ic *interop.Context) error {
|
func (c *nep5TokenNative) OnPersist(ic *interop.Context) error {
|
||||||
|
@ -200,8 +243,8 @@ func newDescriptor(name string, ret smartcontract.ParamType, ps ...manifest.Para
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMethodAndPrice(f Method, price int64, flags smartcontract.CallFlag) *MethodAndPrice {
|
func newMethodAndPrice(f interop.Method, price int64, flags smartcontract.CallFlag) *interop.MethodAndPrice {
|
||||||
return &MethodAndPrice{
|
return &interop.MethodAndPrice{
|
||||||
Func: f,
|
Func: f,
|
||||||
Price: price,
|
Price: price,
|
||||||
RequiredFlags: flags,
|
RequiredFlags: flags,
|
||||||
|
|
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"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
|
@ -17,11 +16,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type testNative struct {
|
type testNative struct {
|
||||||
meta native.ContractMD
|
meta interop.ContractMD
|
||||||
blocks chan uint32
|
blocks chan uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tn *testNative) Metadata() *native.ContractMD {
|
func (tn *testNative) Initialize(_ *interop.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tn *testNative) Metadata() *interop.ContractMD {
|
||||||
return &tn.meta
|
return &tn.meta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,11 +37,16 @@ func (tn *testNative) OnPersist(ic *interop.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ native.Contract = (*testNative)(nil)
|
var _ interop.Contract = (*testNative)(nil)
|
||||||
|
|
||||||
|
// registerNative registers native contract in the blockchain.
|
||||||
|
func (bc *Blockchain) registerNative(c interop.Contract) {
|
||||||
|
bc.contracts.Contracts = append(bc.contracts.Contracts, c)
|
||||||
|
}
|
||||||
|
|
||||||
func newTestNative() *testNative {
|
func newTestNative() *testNative {
|
||||||
tn := &testNative{
|
tn := &testNative{
|
||||||
meta: *native.NewContractMD("Test.Native.Sum"),
|
meta: *interop.NewContractMD("Test.Native.Sum"),
|
||||||
blocks: make(chan uint32, 1),
|
blocks: make(chan uint32, 1),
|
||||||
}
|
}
|
||||||
desc := &manifest.Method{
|
desc := &manifest.Method{
|
||||||
|
@ -49,7 +57,7 @@ func newTestNative() *testNative {
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.IntegerType,
|
ReturnType: smartcontract.IntegerType,
|
||||||
}
|
}
|
||||||
md := &native.MethodAndPrice{
|
md := &interop.MethodAndPrice{
|
||||||
Func: tn.sum,
|
Func: tn.sum,
|
||||||
Price: 1,
|
Price: 1,
|
||||||
RequiredFlags: smartcontract.NoneFlag,
|
RequiredFlags: smartcontract.NoneFlag,
|
||||||
|
@ -76,7 +84,7 @@ func TestNativeContract_Invoke(t *testing.T) {
|
||||||
defer chain.Close()
|
defer chain.Close()
|
||||||
|
|
||||||
tn := newTestNative()
|
tn := newTestNative()
|
||||||
chain.RegisterNative(tn)
|
chain.registerNative(tn)
|
||||||
|
|
||||||
w := io.NewBufBinWriter()
|
w := io.NewBufBinWriter()
|
||||||
emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "sum", int64(14), int64(28))
|
emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "sum", int64(14), int64(28))
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
@ -32,9 +31,6 @@ type Account struct {
|
||||||
Version uint8
|
Version uint8
|
||||||
ScriptHash util.Uint160
|
ScriptHash util.Uint160
|
||||||
IsFrozen bool
|
IsFrozen bool
|
||||||
Votes []*keys.PublicKey
|
|
||||||
GAS NEP5BalanceState
|
|
||||||
NEO NEOBalanceState
|
|
||||||
Balances map[util.Uint256][]UnspentBalance
|
Balances map[util.Uint256][]UnspentBalance
|
||||||
Unclaimed UnclaimedBalances
|
Unclaimed UnclaimedBalances
|
||||||
}
|
}
|
||||||
|
@ -45,7 +41,6 @@ func NewAccount(scriptHash util.Uint160) *Account {
|
||||||
Version: 0,
|
Version: 0,
|
||||||
ScriptHash: scriptHash,
|
ScriptHash: scriptHash,
|
||||||
IsFrozen: false,
|
IsFrozen: false,
|
||||||
Votes: []*keys.PublicKey{},
|
|
||||||
Balances: make(map[util.Uint256][]UnspentBalance),
|
Balances: make(map[util.Uint256][]UnspentBalance),
|
||||||
Unclaimed: UnclaimedBalances{Raw: []byte{}},
|
Unclaimed: UnclaimedBalances{Raw: []byte{}},
|
||||||
}
|
}
|
||||||
|
@ -56,9 +51,6 @@ func (s *Account) DecodeBinary(br *io.BinReader) {
|
||||||
s.Version = uint8(br.ReadB())
|
s.Version = uint8(br.ReadB())
|
||||||
br.ReadBytes(s.ScriptHash[:])
|
br.ReadBytes(s.ScriptHash[:])
|
||||||
s.IsFrozen = br.ReadBool()
|
s.IsFrozen = br.ReadBool()
|
||||||
br.ReadArray(&s.Votes)
|
|
||||||
s.GAS.DecodeBinary(br)
|
|
||||||
s.NEO.DecodeBinary(br)
|
|
||||||
|
|
||||||
s.Balances = make(map[util.Uint256][]UnspentBalance)
|
s.Balances = make(map[util.Uint256][]UnspentBalance)
|
||||||
lenBalances := br.ReadVarUint()
|
lenBalances := br.ReadVarUint()
|
||||||
|
@ -83,9 +75,6 @@ func (s *Account) EncodeBinary(bw *io.BinWriter) {
|
||||||
bw.WriteB(byte(s.Version))
|
bw.WriteB(byte(s.Version))
|
||||||
bw.WriteBytes(s.ScriptHash[:])
|
bw.WriteBytes(s.ScriptHash[:])
|
||||||
bw.WriteBool(s.IsFrozen)
|
bw.WriteBool(s.IsFrozen)
|
||||||
bw.WriteArray(s.Votes)
|
|
||||||
s.GAS.EncodeBinary(bw)
|
|
||||||
s.NEO.EncodeBinary(bw)
|
|
||||||
|
|
||||||
bw.WriteVarUint(uint64(len(s.Balances)))
|
bw.WriteVarUint(uint64(len(s.Balances)))
|
||||||
for k, v := range s.Balances {
|
for k, v := range s.Balances {
|
||||||
|
|
|
@ -3,7 +3,6 @@ package state
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
"github.com/nspcc-dev/neo-go/pkg/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
"github.com/nspcc-dev/neo-go/pkg/internal/testserdes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
@ -14,7 +13,6 @@ func TestDecodeEncodeAccountState(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
n = 10
|
n = 10
|
||||||
balances = make(map[util.Uint256][]UnspentBalance)
|
balances = make(map[util.Uint256][]UnspentBalance)
|
||||||
votes = make([]*keys.PublicKey, n)
|
|
||||||
)
|
)
|
||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
asset := random.Uint256()
|
asset := random.Uint256()
|
||||||
|
@ -25,16 +23,12 @@ func TestDecodeEncodeAccountState(t *testing.T) {
|
||||||
Value: util.Fixed8(int64(random.Int(1, 10000))),
|
Value: util.Fixed8(int64(random.Int(1, 10000))),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
k, err := keys.NewPrivateKey()
|
|
||||||
assert.Nil(t, err)
|
|
||||||
votes[i] = k.PublicKey()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a := &Account{
|
a := &Account{
|
||||||
Version: 0,
|
Version: 0,
|
||||||
ScriptHash: random.Uint160(),
|
ScriptHash: random.Uint160(),
|
||||||
IsFrozen: true,
|
IsFrozen: true,
|
||||||
Votes: votes,
|
|
||||||
Balances: balances,
|
Balances: balances,
|
||||||
Unclaimed: UnclaimedBalances{Raw: []byte{}},
|
Unclaimed: UnclaimedBalances{Raw: []byte{}},
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package state
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
)
|
)
|
||||||
|
@ -16,6 +17,31 @@ type NEP5BalanceState struct {
|
||||||
type NEOBalanceState struct {
|
type NEOBalanceState struct {
|
||||||
NEP5BalanceState
|
NEP5BalanceState
|
||||||
BalanceHeight uint32
|
BalanceHeight uint32
|
||||||
|
Votes keys.PublicKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEP5BalanceStateFromBytes converts serialized NEP5BalanceState to structure.
|
||||||
|
func NEP5BalanceStateFromBytes(b []byte) (*NEP5BalanceState, error) {
|
||||||
|
balance := new(NEP5BalanceState)
|
||||||
|
if len(b) == 0 {
|
||||||
|
return balance, nil
|
||||||
|
}
|
||||||
|
r := io.NewBinReaderFromBuf(b)
|
||||||
|
balance.DecodeBinary(r)
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil, r.Err
|
||||||
|
}
|
||||||
|
return balance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns serialized NEP5BalanceState.
|
||||||
|
func (s *NEP5BalanceState) Bytes() []byte {
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
s.EncodeBinary(w.BinWriter)
|
||||||
|
if w.Err != nil {
|
||||||
|
panic(w.Err)
|
||||||
|
}
|
||||||
|
return w.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// EncodeBinary implements io.Serializable interface.
|
// EncodeBinary implements io.Serializable interface.
|
||||||
|
@ -32,14 +58,41 @@ func (s *NEP5BalanceState) DecodeBinary(r *io.BinReader) {
|
||||||
s.Balance = *emit.BytesToInt(buf)
|
s.Balance = *emit.BytesToInt(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NEOBalanceStateFromBytes converts serialized NEOBalanceState to structure.
|
||||||
|
func NEOBalanceStateFromBytes(b []byte) (*NEOBalanceState, error) {
|
||||||
|
balance := new(NEOBalanceState)
|
||||||
|
if len(b) == 0 {
|
||||||
|
return balance, nil
|
||||||
|
}
|
||||||
|
r := io.NewBinReaderFromBuf(b)
|
||||||
|
balance.DecodeBinary(r)
|
||||||
|
|
||||||
|
if r.Err != nil {
|
||||||
|
return nil, r.Err
|
||||||
|
}
|
||||||
|
return balance, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns serialized NEOBalanceState.
|
||||||
|
func (s *NEOBalanceState) Bytes() []byte {
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
s.EncodeBinary(w.BinWriter)
|
||||||
|
if w.Err != nil {
|
||||||
|
panic(w.Err)
|
||||||
|
}
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
// EncodeBinary implements io.Serializable interface.
|
// EncodeBinary implements io.Serializable interface.
|
||||||
func (s *NEOBalanceState) EncodeBinary(w *io.BinWriter) {
|
func (s *NEOBalanceState) EncodeBinary(w *io.BinWriter) {
|
||||||
s.NEP5BalanceState.EncodeBinary(w)
|
s.NEP5BalanceState.EncodeBinary(w)
|
||||||
w.WriteU32LE(s.BalanceHeight)
|
w.WriteU32LE(s.BalanceHeight)
|
||||||
|
w.WriteArray(s.Votes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DecodeBinary implements io.Serializable interface.
|
// DecodeBinary implements io.Serializable interface.
|
||||||
func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) {
|
func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) {
|
||||||
s.NEP5BalanceState.DecodeBinary(r)
|
s.NEP5BalanceState.DecodeBinary(r)
|
||||||
s.BalanceHeight = r.ReadU32LE()
|
s.BalanceHeight = r.ReadU32LE()
|
||||||
|
r.ReadArray(&s.Votes)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,108 +1,13 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaxValidatorsVoted limits the number of validators that one can vote for.
|
// Validator holds the state of a validator (its key and votes balance).
|
||||||
const MaxValidatorsVoted = 1024
|
|
||||||
|
|
||||||
// Validator holds the state of a validator.
|
|
||||||
type Validator struct {
|
type Validator struct {
|
||||||
PublicKey *keys.PublicKey
|
Key *keys.PublicKey
|
||||||
Registered bool
|
Votes *big.Int
|
||||||
Votes util.Fixed8
|
|
||||||
}
|
|
||||||
|
|
||||||
// ValidatorsCount represents votes with particular number of consensus nodes
|
|
||||||
// for this number to be changeable by the voting system.
|
|
||||||
type ValidatorsCount [MaxValidatorsVoted]util.Fixed8
|
|
||||||
|
|
||||||
// RegisteredAndHasVotes returns true or false whether Validator is registered and has votes.
|
|
||||||
func (vs *Validator) RegisteredAndHasVotes() bool {
|
|
||||||
return vs.Registered && vs.Votes > util.Fixed8(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnregisteredAndHasNoVotes returns true when Validator is not registered and has no votes.
|
|
||||||
func (vs *Validator) UnregisteredAndHasNoVotes() bool {
|
|
||||||
return !vs.Registered && vs.Votes == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeBinary encodes Validator to the given BinWriter.
|
|
||||||
func (vs *Validator) EncodeBinary(bw *io.BinWriter) {
|
|
||||||
vs.PublicKey.EncodeBinary(bw)
|
|
||||||
bw.WriteBool(vs.Registered)
|
|
||||||
vs.Votes.EncodeBinary(bw)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeBinary decodes Validator from the given BinReader.
|
|
||||||
func (vs *Validator) DecodeBinary(reader *io.BinReader) {
|
|
||||||
vs.PublicKey = &keys.PublicKey{}
|
|
||||||
vs.PublicKey.DecodeBinary(reader)
|
|
||||||
vs.Registered = reader.ReadBool()
|
|
||||||
vs.Votes.DecodeBinary(reader)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeBinary encodes ValidatorCount to the given BinWriter.
|
|
||||||
func (vc *ValidatorsCount) EncodeBinary(w *io.BinWriter) {
|
|
||||||
for i := range vc {
|
|
||||||
vc[i].EncodeBinary(w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeBinary decodes ValidatorCount from the given BinReader.
|
|
||||||
func (vc *ValidatorsCount) DecodeBinary(r *io.BinReader) {
|
|
||||||
for i := range vc {
|
|
||||||
vc[i].DecodeBinary(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetWeightedAverage returns an average count of validators that's been voted
|
|
||||||
// for not counting 1/4 of minimum and maximum numbers.
|
|
||||||
func (vc *ValidatorsCount) GetWeightedAverage() int {
|
|
||||||
const (
|
|
||||||
lowerThreshold = 0.25
|
|
||||||
upperThreshold = 0.75
|
|
||||||
)
|
|
||||||
var (
|
|
||||||
sumWeight, sumValue, overallSum, slidingSum util.Fixed8
|
|
||||||
slidingRatio float64
|
|
||||||
)
|
|
||||||
|
|
||||||
for i := range vc {
|
|
||||||
overallSum += vc[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range vc {
|
|
||||||
if slidingRatio >= upperThreshold {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
weight := vc[i]
|
|
||||||
slidingSum += weight
|
|
||||||
previousRatio := slidingRatio
|
|
||||||
slidingRatio = slidingSum.FloatValue() / overallSum.FloatValue()
|
|
||||||
|
|
||||||
if slidingRatio <= lowerThreshold {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if previousRatio < lowerThreshold {
|
|
||||||
if slidingRatio > upperThreshold {
|
|
||||||
weight = util.Fixed8FromFloat((upperThreshold - lowerThreshold) * overallSum.FloatValue())
|
|
||||||
} else {
|
|
||||||
weight = util.Fixed8FromFloat((slidingRatio - lowerThreshold) * overallSum.FloatValue())
|
|
||||||
}
|
|
||||||
} else if slidingRatio > upperThreshold {
|
|
||||||
weight = util.Fixed8FromFloat((upperThreshold - previousRatio) * overallSum.FloatValue())
|
|
||||||
}
|
|
||||||
sumWeight += weight
|
|
||||||
// Votes with N values get stored with N-1 index, thus +1 here.
|
|
||||||
sumValue += util.Fixed8(i+1) * weight
|
|
||||||
}
|
|
||||||
if sumValue == 0 || sumWeight == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return int(sumValue / sumWeight)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
// KeyPrefix constants.
|
||||||
const (
|
const (
|
||||||
DataBlock KeyPrefix = 0x01
|
DataBlock KeyPrefix = 0x01
|
||||||
DataTransaction KeyPrefix = 0x02
|
DataTransaction KeyPrefix = 0x02
|
||||||
STAccount KeyPrefix = 0x40
|
STAccount KeyPrefix = 0x40
|
||||||
STCoin KeyPrefix = 0x44
|
STCoin KeyPrefix = 0x44
|
||||||
STSpentCoin KeyPrefix = 0x45
|
STSpentCoin KeyPrefix = 0x45
|
||||||
STNextValidators KeyPrefix = 0x47
|
STValidator KeyPrefix = 0x48
|
||||||
STValidator KeyPrefix = 0x48
|
STAsset KeyPrefix = 0x4c
|
||||||
STAsset KeyPrefix = 0x4c
|
STNotification KeyPrefix = 0x4d
|
||||||
STNotification KeyPrefix = 0x4d
|
STContract KeyPrefix = 0x50
|
||||||
STContract KeyPrefix = 0x50
|
STStorage KeyPrefix = 0x70
|
||||||
STNativeContract KeyPrefix = 0x51
|
STNEP5Transfers KeyPrefix = 0x72
|
||||||
STStorage KeyPrefix = 0x70
|
STNEP5Balances KeyPrefix = 0x73
|
||||||
STNEP5Transfers KeyPrefix = 0x72
|
IXHeaderHashList KeyPrefix = 0x80
|
||||||
STNEP5Balances KeyPrefix = 0x73
|
SYSCurrentBlock KeyPrefix = 0xc0
|
||||||
IXHeaderHashList KeyPrefix = 0x80
|
SYSCurrentHeader KeyPrefix = 0xc1
|
||||||
IXValidatorsCount KeyPrefix = 0x90
|
SYSVersion KeyPrefix = 0xf0
|
||||||
SYSCurrentBlock KeyPrefix = 0xc0
|
|
||||||
SYSCurrentHeader KeyPrefix = 0xc1
|
|
||||||
SYSVersion KeyPrefix = 0xf0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrKeyNotFound is an error returned by Store implementations
|
// ErrKeyNotFound is an error returned by Store implementations
|
||||||
|
|
|
@ -17,7 +17,6 @@ var (
|
||||||
STContract,
|
STContract,
|
||||||
STStorage,
|
STStorage,
|
||||||
IXHeaderHashList,
|
IXHeaderHashList,
|
||||||
IXValidatorsCount,
|
|
||||||
SYSCurrentBlock,
|
SYSCurrentBlock,
|
||||||
SYSCurrentHeader,
|
SYSCurrentHeader,
|
||||||
SYSVersion,
|
SYSVersion,
|
||||||
|
@ -33,7 +32,6 @@ var (
|
||||||
0x50,
|
0x50,
|
||||||
0x70,
|
0x70,
|
||||||
0x80,
|
0x80,
|
||||||
0x90,
|
|
||||||
0xc0,
|
0xc0,
|
||||||
0xc1,
|
0xc1,
|
||||||
0xf0,
|
0xf0,
|
||||||
|
|
|
@ -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"
|
"fmt"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
@ -164,12 +163,6 @@ func (t *Transaction) decodeData(r *io.BinReader) {
|
||||||
case IssueType:
|
case IssueType:
|
||||||
t.Data = &IssueTX{}
|
t.Data = &IssueTX{}
|
||||||
t.Data.(*IssueTX).DecodeBinary(r)
|
t.Data.(*IssueTX).DecodeBinary(r)
|
||||||
case EnrollmentType:
|
|
||||||
t.Data = &EnrollmentTX{}
|
|
||||||
t.Data.(*EnrollmentTX).DecodeBinary(r)
|
|
||||||
case StateType:
|
|
||||||
t.Data = &StateTX{}
|
|
||||||
t.Data.(*StateTX).DecodeBinary(r)
|
|
||||||
default:
|
default:
|
||||||
r.Err = fmt.Errorf("invalid TX type %x", t.Type)
|
r.Err = fmt.Errorf("invalid TX type %x", t.Type)
|
||||||
}
|
}
|
||||||
|
@ -281,12 +274,10 @@ type transactionJSON struct {
|
||||||
Outputs []Output `json:"vout"`
|
Outputs []Output `json:"vout"`
|
||||||
Scripts []Witness `json:"scripts"`
|
Scripts []Witness `json:"scripts"`
|
||||||
|
|
||||||
Claims []Input `json:"claims,omitempty"`
|
Claims []Input `json:"claims,omitempty"`
|
||||||
PublicKey *keys.PublicKey `json:"pubkey,omitempty"`
|
Script string `json:"script,omitempty"`
|
||||||
Script string `json:"script,omitempty"`
|
Gas util.Fixed8 `json:"gas,omitempty"`
|
||||||
Gas util.Fixed8 `json:"gas,omitempty"`
|
Asset *registeredAsset `json:"asset,omitempty"`
|
||||||
Asset *registeredAsset `json:"asset,omitempty"`
|
|
||||||
Descriptors []*StateDescriptor `json:"descriptors,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON implements json.Marshaler interface.
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
|
@ -307,8 +298,6 @@ func (t *Transaction) MarshalJSON() ([]byte, error) {
|
||||||
switch t.Type {
|
switch t.Type {
|
||||||
case ClaimType:
|
case ClaimType:
|
||||||
tx.Claims = t.Data.(*ClaimTX).Claims
|
tx.Claims = t.Data.(*ClaimTX).Claims
|
||||||
case EnrollmentType:
|
|
||||||
tx.PublicKey = &t.Data.(*EnrollmentTX).PublicKey
|
|
||||||
case InvocationType:
|
case InvocationType:
|
||||||
tx.Script = hex.EncodeToString(t.Data.(*InvocationTX).Script)
|
tx.Script = hex.EncodeToString(t.Data.(*InvocationTX).Script)
|
||||||
tx.Gas = t.Data.(*InvocationTX).Gas
|
tx.Gas = t.Data.(*InvocationTX).Gas
|
||||||
|
@ -322,8 +311,6 @@ func (t *Transaction) MarshalJSON() ([]byte, error) {
|
||||||
Owner: transaction.Owner,
|
Owner: transaction.Owner,
|
||||||
Admin: address.Uint160ToString(transaction.Admin),
|
Admin: address.Uint160ToString(transaction.Admin),
|
||||||
}
|
}
|
||||||
case StateType:
|
|
||||||
tx.Descriptors = t.Data.(*StateTX).Descriptors
|
|
||||||
}
|
}
|
||||||
return json.Marshal(tx)
|
return json.Marshal(tx)
|
||||||
}
|
}
|
||||||
|
@ -354,10 +341,6 @@ func (t *Transaction) UnmarshalJSON(data []byte) error {
|
||||||
t.Data = &ClaimTX{
|
t.Data = &ClaimTX{
|
||||||
Claims: tx.Claims,
|
Claims: tx.Claims,
|
||||||
}
|
}
|
||||||
case EnrollmentType:
|
|
||||||
t.Data = &EnrollmentTX{
|
|
||||||
PublicKey: *tx.PublicKey,
|
|
||||||
}
|
|
||||||
case InvocationType:
|
case InvocationType:
|
||||||
bytes, err := hex.DecodeString(tx.Script)
|
bytes, err := hex.DecodeString(tx.Script)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -381,10 +364,6 @@ func (t *Transaction) UnmarshalJSON(data []byte) error {
|
||||||
Owner: tx.Asset.Owner,
|
Owner: tx.Asset.Owner,
|
||||||
Admin: admin,
|
Admin: admin,
|
||||||
}
|
}
|
||||||
case StateType:
|
|
||||||
t.Data = &StateTX{
|
|
||||||
Descriptors: tx.Descriptors,
|
|
||||||
}
|
|
||||||
case ContractType:
|
case ContractType:
|
||||||
t.Data = &ContractTX{}
|
t.Data = &ContractTX{}
|
||||||
case IssueType:
|
case IssueType:
|
||||||
|
|
|
@ -154,32 +154,6 @@ func TestMarshalUnmarshalJSONClaimTX(t *testing.T) {
|
||||||
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalUnmarshalJSONEnrollmentTX(t *testing.T) {
|
|
||||||
str := "03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c"
|
|
||||||
pubKey, err := keys.NewPublicKeyFromString(str)
|
|
||||||
require.NoError(t, err)
|
|
||||||
tx := &Transaction{
|
|
||||||
Type: EnrollmentType,
|
|
||||||
Version: 5,
|
|
||||||
Data: &EnrollmentTX{PublicKey: *pubKey},
|
|
||||||
Attributes: []Attribute{},
|
|
||||||
Inputs: []Input{{
|
|
||||||
PrevHash: util.Uint256{5, 6, 7, 8},
|
|
||||||
PrevIndex: uint16(12),
|
|
||||||
}},
|
|
||||||
Outputs: []Output{{
|
|
||||||
AssetID: util.Uint256{1, 2, 3},
|
|
||||||
Amount: util.Fixed8FromInt64(1),
|
|
||||||
ScriptHash: util.Uint160{1, 2, 3},
|
|
||||||
Position: 0,
|
|
||||||
}},
|
|
||||||
Scripts: []Witness{},
|
|
||||||
Trimmed: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalUnmarshalJSONInvocationTX(t *testing.T) {
|
func TestMarshalUnmarshalJSONInvocationTX(t *testing.T) {
|
||||||
tx := &Transaction{
|
tx := &Transaction{
|
||||||
Type: InvocationType,
|
Type: InvocationType,
|
||||||
|
@ -236,33 +210,3 @@ func TestMarshalUnmarshalJSONRegisterTX(t *testing.T) {
|
||||||
|
|
||||||
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMarshalUnmarshalJSONStateTX(t *testing.T) {
|
|
||||||
tx := &Transaction{
|
|
||||||
Type: StateType,
|
|
||||||
Version: 5,
|
|
||||||
Data: &StateTX{
|
|
||||||
Descriptors: []*StateDescriptor{&StateDescriptor{
|
|
||||||
Type: Validator,
|
|
||||||
Key: []byte{1, 2, 3},
|
|
||||||
Value: []byte{4, 5, 6},
|
|
||||||
Field: "Field",
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
Attributes: []Attribute{},
|
|
||||||
Inputs: []Input{{
|
|
||||||
PrevHash: util.Uint256{5, 6, 7, 8},
|
|
||||||
PrevIndex: uint16(12),
|
|
||||||
}},
|
|
||||||
Outputs: []Output{{
|
|
||||||
AssetID: util.Uint256{1, 2, 3},
|
|
||||||
Amount: util.Fixed8FromInt64(1),
|
|
||||||
ScriptHash: util.Uint160{1, 2, 3},
|
|
||||||
Position: 0,
|
|
||||||
}},
|
|
||||||
Scripts: []Witness{},
|
|
||||||
Trimmed: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
testserdes.MarshalUnmarshalJSON(t, tx, new(Transaction))
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,10 +14,8 @@ const (
|
||||||
MinerType TXType = 0x00
|
MinerType TXType = 0x00
|
||||||
IssueType TXType = 0x01
|
IssueType TXType = 0x01
|
||||||
ClaimType TXType = 0x02
|
ClaimType TXType = 0x02
|
||||||
EnrollmentType TXType = 0x20
|
|
||||||
RegisterType TXType = 0x40
|
RegisterType TXType = 0x40
|
||||||
ContractType TXType = 0x80
|
ContractType TXType = 0x80
|
||||||
StateType TXType = 0x90
|
|
||||||
InvocationType TXType = 0xd1
|
InvocationType TXType = 0xd1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -30,14 +28,10 @@ func (t TXType) String() string {
|
||||||
return "IssueTransaction"
|
return "IssueTransaction"
|
||||||
case ClaimType:
|
case ClaimType:
|
||||||
return "ClaimTransaction"
|
return "ClaimTransaction"
|
||||||
case EnrollmentType:
|
|
||||||
return "EnrollmentTransaction"
|
|
||||||
case RegisterType:
|
case RegisterType:
|
||||||
return "RegisterTransaction"
|
return "RegisterTransaction"
|
||||||
case ContractType:
|
case ContractType:
|
||||||
return "ContractTransaction"
|
return "ContractTransaction"
|
||||||
case StateType:
|
|
||||||
return "StateTransaction"
|
|
||||||
case InvocationType:
|
case InvocationType:
|
||||||
return "InvocationTransaction"
|
return "InvocationTransaction"
|
||||||
default:
|
default:
|
||||||
|
@ -70,14 +64,10 @@ func TXTypeFromString(jsonString string) (TXType, error) {
|
||||||
return IssueType, nil
|
return IssueType, nil
|
||||||
case "ClaimTransaction":
|
case "ClaimTransaction":
|
||||||
return ClaimType, nil
|
return ClaimType, nil
|
||||||
case "EnrollmentTransaction":
|
|
||||||
return EnrollmentType, nil
|
|
||||||
case "RegisterTransaction":
|
case "RegisterTransaction":
|
||||||
return RegisterType, nil
|
return RegisterType, nil
|
||||||
case "ContractTransaction":
|
case "ContractTransaction":
|
||||||
return ContractType, nil
|
return ContractType, nil
|
||||||
case "StateTransaction":
|
|
||||||
return StateType, nil
|
|
||||||
case "InvocationTransaction":
|
case "InvocationTransaction":
|
||||||
return InvocationType, nil
|
return InvocationType, nil
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -8,8 +8,10 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -86,6 +88,7 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error)
|
||||||
&governingTokenTX,
|
&governingTokenTX,
|
||||||
&utilityTokenTX,
|
&utilityTokenTX,
|
||||||
issueTx,
|
issueTx,
|
||||||
|
deployNativeContracts(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +99,22 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error)
|
||||||
return b, nil
|
return b, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func deployNativeContracts() *transaction.Transaction {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
emit.Syscall(buf.BinWriter, "Neo.Native.Deploy")
|
||||||
|
script := buf.Bytes()
|
||||||
|
tx := transaction.NewInvocationTX(script, 0)
|
||||||
|
tx.Nonce = 0
|
||||||
|
tx.Sender = hash.Hash160([]byte{byte(opcode.PUSH1)})
|
||||||
|
tx.Scripts = []transaction.Witness{
|
||||||
|
{
|
||||||
|
InvocationScript: []byte{},
|
||||||
|
VerificationScript: []byte{byte(opcode.PUSH1)},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return tx
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
admin := hash.Hash160([]byte{byte(opcode.OLDPUSH1)})
|
admin := hash.Hash160([]byte{byte(opcode.OLDPUSH1)})
|
||||||
registerTX := &transaction.RegisterTX{
|
registerTX := &transaction.RegisterTX{
|
||||||
|
|
|
@ -20,7 +20,7 @@ func TestGenesisBlockMainNet(t *testing.T) {
|
||||||
// have been changed. Consequently, hash of the genesis block has been changed.
|
// have been changed. Consequently, hash of the genesis block has been changed.
|
||||||
// Update expected genesis block hash for better times.
|
// Update expected genesis block hash for better times.
|
||||||
// Old hash is "d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf"
|
// Old hash is "d42561e3d30e15be6400b6df2f328e02d2bf6354c41dce433bc57687c82144bf"
|
||||||
expect := "931161239581bcd90b73d092483dee6b7bf2162e66df7e15c3a276a22b4c9422"
|
expect := "094c2c2db5dcb868d85aa4d652aed23bc67e7166f53223a228e382265b1be84b"
|
||||||
assert.Equal(t, expect, block.Hash().StringLE())
|
assert.Equal(t, expect, block.Hash().StringLE())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,16 @@ func (keys *PublicKeys) DecodeBytes(data []byte) error {
|
||||||
return b.Err
|
return b.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bytes encodes PublicKeys to the new slice of bytes.
|
||||||
|
func (keys *PublicKeys) Bytes() []byte {
|
||||||
|
buf := io.NewBufBinWriter()
|
||||||
|
buf.WriteArray(*keys)
|
||||||
|
if buf.Err != nil {
|
||||||
|
panic(buf.Err)
|
||||||
|
}
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
// Contains checks whether passed param contained in PublicKeys.
|
// Contains checks whether passed param contained in PublicKeys.
|
||||||
func (keys PublicKeys) Contains(pKey *PublicKey) bool {
|
func (keys PublicKeys) Contains(pKey *PublicKey) bool {
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
|
|
|
@ -96,13 +96,13 @@ func (chain testChain) GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog {
|
||||||
func (chain testChain) GetNEP5Balances(util.Uint160) *state.NEP5Balances {
|
func (chain testChain) GetNEP5Balances(util.Uint160) *state.NEP5Balances {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
func (chain testChain) GetValidators(...*transaction.Transaction) ([]*keys.PublicKey, error) {
|
func (chain testChain) GetValidators() ([]*keys.PublicKey, error) {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
func (chain testChain) GetStandByValidators() (keys.PublicKeys, error) {
|
func (chain testChain) GetStandByValidators() (keys.PublicKeys, error) {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
func (chain testChain) GetEnrollments() ([]*state.Validator, error) {
|
func (chain testChain) GetEnrollments() ([]state.Validator, error) {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
func (chain testChain) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) {
|
func (chain testChain) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) {
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core"
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
"github.com/nspcc-dev/neo-go/pkg/core/block"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/request"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result"
|
||||||
|
@ -49,7 +48,6 @@ var rpcClientTestCases = map[string][]rpcClientTestCase{
|
||||||
Version: 0,
|
Version: 0,
|
||||||
ScriptHash: scriptHash,
|
ScriptHash: scriptHash,
|
||||||
IsFrozen: false,
|
IsFrozen: false,
|
||||||
Votes: []*keys.PublicKey{},
|
|
||||||
Balances: result.Balances{
|
Balances: result.Balances{
|
||||||
result.Balance{
|
result.Balance{
|
||||||
Asset: core.GoverningTokenID(),
|
Asset: core.GoverningTokenID(),
|
||||||
|
|
|
@ -5,18 +5,16 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
"github.com/nspcc-dev/neo-go/pkg/core/state"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// AccountState wrapper used for the representation of
|
// AccountState wrapper used for the representation of
|
||||||
// state.Account on the RPC Server.
|
// state.Account on the RPC Server.
|
||||||
type AccountState struct {
|
type AccountState struct {
|
||||||
Version uint8 `json:"version"`
|
Version uint8 `json:"version"`
|
||||||
ScriptHash util.Uint160 `json:"script_hash"`
|
ScriptHash util.Uint160 `json:"script_hash"`
|
||||||
IsFrozen bool `json:"frozen"`
|
IsFrozen bool `json:"frozen"`
|
||||||
Votes []*keys.PublicKey `json:"votes"`
|
Balances []Balance `json:"balances"`
|
||||||
Balances []Balance `json:"balances"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Balances type for sorting balances in rpc response.
|
// Balances type for sorting balances in rpc response.
|
||||||
|
@ -48,7 +46,6 @@ func NewAccountState(a *state.Account) AccountState {
|
||||||
Version: a.Version,
|
Version: a.Version,
|
||||||
ScriptHash: a.ScriptHash,
|
ScriptHash: a.ScriptHash,
|
||||||
IsFrozen: a.IsFrozen,
|
IsFrozen: a.IsFrozen,
|
||||||
Votes: a.Votes,
|
|
||||||
Balances: balances,
|
Balances: balances,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,12 @@ package result
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Validator used for the representation of
|
// Validator used for the representation of
|
||||||
// state.Validator on the RPC Server.
|
// state.Validator on the RPC Server.
|
||||||
type Validator struct {
|
type Validator struct {
|
||||||
PublicKey keys.PublicKey `json:"publickey"`
|
PublicKey keys.PublicKey `json:"publickey"`
|
||||||
Votes util.Fixed8 `json:"votes"`
|
Votes int64 `json:"votes,string"`
|
||||||
Active bool `json:"active"`
|
Active bool `json:"active"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -815,9 +815,9 @@ func (s *Server) getValidators(_ request.Params) (interface{}, error) {
|
||||||
var res []result.Validator
|
var res []result.Validator
|
||||||
for _, v := range enrollments {
|
for _, v := range enrollments {
|
||||||
res = append(res, result.Validator{
|
res = append(res, result.Validator{
|
||||||
PublicKey: *v.PublicKey,
|
PublicKey: *v.Key,
|
||||||
Votes: v.Votes,
|
Votes: v.Votes.Int64(),
|
||||||
Active: validators.Contains(v.PublicKey),
|
Active: validators.Contains(v.Key),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
|
|
|
@ -408,7 +408,7 @@ var rpcTestCases = map[string][]rpcTestCase{
|
||||||
"getblockheader": {
|
"getblockheader": {
|
||||||
{
|
{
|
||||||
name: "invalid verbose type",
|
name: "invalid verbose type",
|
||||||
params: `["614a9085dc55fd0539ad3a9d68d8b8e7c52328da905c87bfe8cfca57a5c3c02f", true]`,
|
params: `["9673799c5b5a294427401cb07d6cc615ada3a0d5c5bf7ed6f0f54f24abb2e2ac", true]`,
|
||||||
fail: true,
|
fail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
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