core: implement skeletons for NEO/GAS native contracts

This commit is contained in:
Evgenii Stratonikov 2020-03-25 13:00:11 +03:00
parent 25354c44f9
commit 76700f31cf
9 changed files with 893 additions and 105 deletions

View file

@ -198,6 +198,9 @@ func (bc *Blockchain) init() error {
if err != nil {
return err
}
if err := bc.initNative(); err != nil {
return err
}
return bc.storeBlock(genesisBlock)
}
if ver != version {
@ -270,6 +273,27 @@ func (bc *Blockchain) init() error {
return nil
}
func (bc *Blockchain) initNative() error {
ic := bc.newInteropContext(trigger.Application, bc.dao, nil, nil)
gas := native.NewGAS()
neo := native.NewNEO()
neo.GAS = gas
gas.NEO = neo
if err := gas.Initialize(ic); err != nil {
return fmt.Errorf("can't initialize GAS native contract: %v", err)
}
if err := neo.Initialize(ic); err != nil {
return fmt.Errorf("can't initialize NEO native contract: %v", err)
}
bc.contracts.SetGAS(gas)
bc.contracts.SetNEO(neo)
return nil
}
// Run runs chain loop.
func (bc *Blockchain) Run() {
persistTimer := time.NewTimer(persistInterval)
@ -639,7 +663,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
return err
}
case *transaction.StateTX:
if err := processStateTX(cache, t); err != nil {
if err := bc.processStateTX(cache, tx, t); err != nil {
return err
}
case *transaction.PublishTX:
@ -887,21 +911,8 @@ func processTXWithValidatorsSubtract(output *transaction.Output, account *state.
// modAccountVotes adds given value to given account voted validators.
func modAccountVotes(account *state.Account, dao *dao.Cached, value util.Fixed8) error {
for _, vote := range account.Votes {
validator, err := dao.GetValidatorStateOrNew(vote)
if err != nil {
return err
}
validator.Votes += value
if validator.UnregisteredAndHasNoVotes() {
if err := dao.DeleteValidatorState(validator); err != nil {
return err
}
} else {
if err := dao.PutValidatorState(validator); err != nil {
return err
}
}
if err := native.ModifyAccountVotes(account, dao, value); err != nil {
return err
}
if len(account.Votes) > 0 {
vc, err := dao.GetValidatorsCount()
@ -937,55 +948,19 @@ func processValidatorStateDescriptor(descriptor *transaction.StateDescriptor, da
return nil
}
func processAccountStateDescriptor(descriptor *transaction.StateDescriptor, dao *dao.Cached) error {
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
}
account, err := dao.GetAccountStateOrNew(hash)
if err != nil {
return err
}
if descriptor.Field == "Votes" {
balance := account.GetBalanceValues()[GoverningTokenID()]
if err = modAccountVotes(account, dao, -balance); err != nil {
return err
}
votes := keys.PublicKeys{}
err := votes.DecodeBytes(descriptor.Value)
if err != nil {
if err := votes.DecodeBytes(descriptor.Value); err != nil {
return err
}
if len(votes) > state.MaxValidatorsVoted {
return errors.New("voting candidate limit exceeded")
}
if len(votes) > 0 {
account.Votes = votes
for _, vote := range account.Votes {
validatorState, err := dao.GetValidatorStateOrNew(vote)
if err != nil {
return err
}
validatorState.Votes += balance
if err = dao.PutValidatorState(validatorState); err != nil {
return err
}
}
vc, err := dao.GetValidatorsCount()
if err != nil {
return err
}
vc[len(account.Votes)-1] += balance
err = dao.PutValidatorsCount(vc)
if err != nil {
return err
}
} else {
account.Votes = nil
}
return dao.PutAccountState(account)
ic := bc.newInteropContext(trigger.Application, dao, nil, t)
return bc.contracts.NEO.VoteInternal(ic, hash, votes)
}
return nil
}
@ -1807,59 +1782,14 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P
return nil, err
}
case *transaction.StateTX:
if err := processStateTX(cache, t); err != nil {
if err := bc.processStateTX(cache, tx, t); err != nil {
return nil, err
}
}
}
}
validators := cache.GetValidators()
sort.Slice(validators, func(i, j int) bool {
// Unregistered validators go to the end of the list.
if validators[i].Registered != validators[j].Registered {
return validators[i].Registered
}
// The most-voted validators should end up in the front of the list.
if validators[i].Votes != validators[j].Votes {
return validators[i].Votes > validators[j].Votes
}
// Ties are broken with public keys.
return validators[i].PublicKey.Cmp(validators[j].PublicKey) == -1
})
validatorsCount, err := cache.GetValidatorsCount()
if err != nil {
return nil, err
}
count := validatorsCount.GetWeightedAverage()
standByValidators, err := bc.GetStandByValidators()
if err != nil {
return nil, err
}
if count < len(standByValidators) {
count = len(standByValidators)
}
uniqueSBValidators := standByValidators.Unique()
result := keys.PublicKeys{}
for _, validator := range validators {
if validator.RegisteredAndHasVotes() || uniqueSBValidators.Contains(validator.PublicKey) {
result = append(result, validator.PublicKey)
}
}
if result.Len() >= count {
result = result[:count]
} else {
for i := 0; i < uniqueSBValidators.Len() && result.Len() < count; i++ {
if !result.Contains(uniqueSBValidators[i]) {
result = append(result, uniqueSBValidators[i])
}
}
}
sort.Sort(result)
return result, nil
return bc.contracts.NEO.GetValidatorsInternal(bc, cache)
}
// GetEnrollments returns all registered validators and non-registered SB validators
@ -1896,11 +1826,11 @@ func (bc *Blockchain) GetEnrollments() ([]*state.Validator, error) {
return result, nil
}
func processStateTX(dao *dao.Cached, tx *transaction.StateTX) error {
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 := processAccountStateDescriptor(desc, dao); err != nil {
if err := bc.processAccountStateDescriptor(desc, t, dao); err != nil {
return err
}
case transaction.Validator: