forked from TrueCloudLab/neoneo-go
Merge pull request #1252 from nspcc-dev/fix/voting
Implement delegated voting
This commit is contained in:
commit
d571162173
28 changed files with 553 additions and 236 deletions
|
@ -13,7 +13,11 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/cli/options"
|
"github.com/nspcc-dev/neo-go/cli/options"
|
||||||
"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/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/rpc/client"
|
||||||
"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/wallet"
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
@ -186,6 +190,24 @@ func NewCommands() []cli.Command {
|
||||||
Usage: "work with NEP5 contracts",
|
Usage: "work with NEP5 contracts",
|
||||||
Subcommands: newNEP5Commands(),
|
Subcommands: newNEP5Commands(),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "vote",
|
||||||
|
Usage: "vote for a validator",
|
||||||
|
UsageText: "vote -w <path> -r <rpc> [-t <timeout>] [-g gas] -a <addr> -k <public key>",
|
||||||
|
Action: handleVote,
|
||||||
|
Flags: append([]cli.Flag{
|
||||||
|
walletPathFlag,
|
||||||
|
gasFlag,
|
||||||
|
flags.AddressFlag{
|
||||||
|
Name: "addr, a",
|
||||||
|
Usage: "Address to vote from",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "key, k",
|
||||||
|
Usage: "Public key of candidate to vote for",
|
||||||
|
},
|
||||||
|
}, options.RPC...),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@ -513,6 +535,69 @@ func createWallet(ctx *cli.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleVote(ctx *cli.Context) error {
|
||||||
|
wall, err := openWallet(ctx.String("wallet"))
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
addrFlag := ctx.Generic("addr").(*flags.Address)
|
||||||
|
addr := addrFlag.Uint160()
|
||||||
|
acc := wall.GetAccount(addr)
|
||||||
|
if acc == nil {
|
||||||
|
return cli.NewExitError(fmt.Errorf("can't find account for the address: %s", addrFlag), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pub *keys.PublicKey
|
||||||
|
pubStr := ctx.String("key")
|
||||||
|
if pubStr != "" {
|
||||||
|
pub, err = keys.NewPublicKeyFromString(pubStr)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(fmt.Errorf("invalid public key: '%s'", pubStr), 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gctx, cancel := options.GetTimeoutContext(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
c, err := options.GetRPCClient(gctx, ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var pubArg interface{}
|
||||||
|
if pub != nil {
|
||||||
|
pubArg = pub.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
gas := flags.Fixed8FromContext(ctx, "gas")
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
emit.AppCallWithOperationAndArgs(w.BinWriter, client.NeoContractHash, "vote", addr.BytesBE(), pubArg)
|
||||||
|
emit.Opcode(w.BinWriter, opcode.ASSERT)
|
||||||
|
|
||||||
|
tx, err := c.CreateTxFromScript(w.Bytes(), acc, int64(gas))
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pass, err := readPassword("Password > "); err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
} else if err := acc.Decrypt(pass); err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = acc.SignTx(tx); err != nil {
|
||||||
|
return cli.NewExitError(fmt.Errorf("can't sign tx: %v", err), 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := c.SendRawTransaction(tx)
|
||||||
|
if err != nil {
|
||||||
|
return cli.NewExitError(err, 1)
|
||||||
|
}
|
||||||
|
fmt.Println(res.StringLE())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func readAccountInfo() (string, string, error) {
|
func readAccountInfo() (string, string, error) {
|
||||||
buf := bufio.NewReader(os.Stdin)
|
buf := bufio.NewReader(os.Stdin)
|
||||||
fmt.Print("Enter the name of the account > ")
|
fmt.Print("Enter the name of the account > ")
|
||||||
|
|
|
@ -2,7 +2,7 @@ ProtocolConfiguration:
|
||||||
Magic: 5195086
|
Magic: 5195086
|
||||||
SecondsPerBlock: 15
|
SecondsPerBlock: 15
|
||||||
MemPoolSize: 50000
|
MemPoolSize: 50000
|
||||||
StandbyValidators:
|
StandbyCommittee:
|
||||||
- 03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c
|
- 03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c
|
||||||
- 02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093
|
- 02df48f60e8f3e01c48ff40b9b7f1310d7a8b2a193188befe1c2e3df740e895093
|
||||||
- 03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a
|
- 03b8d9d5771d8f513aa0869b9cc8d50986403b78c6da36890638c3d46a5adce04a
|
||||||
|
@ -10,6 +10,7 @@ ProtocolConfiguration:
|
||||||
- 024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d
|
- 024c7b7fb6c310fccf1ba33b082519d82964ea93868d676662d4a59ad548df0e7d
|
||||||
- 02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e
|
- 02aaec38470f6aad0042c6e877cfd8087d2676b0f516fddd362801b9bd3936399e
|
||||||
- 02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70
|
- 02486fd15702c4490a26703112a5cc1d0923fd697a33406bd5a1c00e0013b09a70
|
||||||
|
ValidatorsCount: 7
|
||||||
SeedList:
|
SeedList:
|
||||||
- seed1.neo.org:10333
|
- seed1.neo.org:10333
|
||||||
- seed2.neo.org:10333
|
- seed2.neo.org:10333
|
||||||
|
|
|
@ -2,11 +2,12 @@ ProtocolConfiguration:
|
||||||
Magic: 56753
|
Magic: 56753
|
||||||
SecondsPerBlock: 15
|
SecondsPerBlock: 15
|
||||||
MemPoolSize: 50000
|
MemPoolSize: 50000
|
||||||
StandbyValidators:
|
StandbyCommittee:
|
||||||
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
|
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
|
||||||
- 02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e
|
- 02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e
|
||||||
- 03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699
|
- 03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699
|
||||||
- 02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62
|
- 02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62
|
||||||
|
ValidatorsCount: 4
|
||||||
SeedList:
|
SeedList:
|
||||||
- 172.200.0.1:20333
|
- 172.200.0.1:20333
|
||||||
- 172.200.0.2:20334
|
- 172.200.0.2:20334
|
||||||
|
|
|
@ -2,11 +2,12 @@ ProtocolConfiguration:
|
||||||
Magic: 56753
|
Magic: 56753
|
||||||
SecondsPerBlock: 15
|
SecondsPerBlock: 15
|
||||||
MemPoolSize: 50000
|
MemPoolSize: 50000
|
||||||
StandbyValidators:
|
StandbyCommittee:
|
||||||
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
|
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
|
||||||
- 02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e
|
- 02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e
|
||||||
- 03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699
|
- 03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699
|
||||||
- 02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62
|
- 02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62
|
||||||
|
ValidatorsCount: 4
|
||||||
SeedList:
|
SeedList:
|
||||||
- 172.200.0.1:20333
|
- 172.200.0.1:20333
|
||||||
- 172.200.0.2:20334
|
- 172.200.0.2:20334
|
||||||
|
|
|
@ -2,8 +2,9 @@ ProtocolConfiguration:
|
||||||
Magic: 56753
|
Magic: 56753
|
||||||
SecondsPerBlock: 1
|
SecondsPerBlock: 1
|
||||||
MemPoolSize: 50000
|
MemPoolSize: 50000
|
||||||
StandbyValidators:
|
StandbyCommittee:
|
||||||
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
|
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
|
||||||
|
ValidatorsCount: 1
|
||||||
SeedList:
|
SeedList:
|
||||||
- 172.200.0.1:20333
|
- 172.200.0.1:20333
|
||||||
VerifyBlocks: true
|
VerifyBlocks: true
|
||||||
|
|
|
@ -2,11 +2,12 @@ ProtocolConfiguration:
|
||||||
Magic: 56753
|
Magic: 56753
|
||||||
SecondsPerBlock: 15
|
SecondsPerBlock: 15
|
||||||
MemPoolSize: 50000
|
MemPoolSize: 50000
|
||||||
StandbyValidators:
|
StandbyCommittee:
|
||||||
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
|
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
|
||||||
- 02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e
|
- 02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e
|
||||||
- 03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699
|
- 03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699
|
||||||
- 02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62
|
- 02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62
|
||||||
|
ValidatorsCount: 4
|
||||||
SeedList:
|
SeedList:
|
||||||
- 172.200.0.1:20333
|
- 172.200.0.1:20333
|
||||||
- 172.200.0.2:20334
|
- 172.200.0.2:20334
|
||||||
|
|
|
@ -2,11 +2,12 @@ ProtocolConfiguration:
|
||||||
Magic: 56753
|
Magic: 56753
|
||||||
SecondsPerBlock: 15
|
SecondsPerBlock: 15
|
||||||
MemPoolSize: 50000
|
MemPoolSize: 50000
|
||||||
StandbyValidators:
|
StandbyCommittee:
|
||||||
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
|
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
|
||||||
- 02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e
|
- 02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e
|
||||||
- 03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699
|
- 03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699
|
||||||
- 02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62
|
- 02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62
|
||||||
|
ValidatorsCount: 4
|
||||||
SeedList:
|
SeedList:
|
||||||
- 172.200.0.1:20333
|
- 172.200.0.1:20333
|
||||||
- 172.200.0.2:20334
|
- 172.200.0.2:20334
|
||||||
|
|
|
@ -2,7 +2,7 @@ ProtocolConfiguration:
|
||||||
Magic: 56753
|
Magic: 56753
|
||||||
SecondsPerBlock: 15
|
SecondsPerBlock: 15
|
||||||
MemPoolSize: 50000
|
MemPoolSize: 50000
|
||||||
StandbyValidators:
|
StandbyCommittee:
|
||||||
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
|
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
|
||||||
- 02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e
|
- 02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e
|
||||||
- 03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699
|
- 03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699
|
||||||
|
|
|
@ -2,7 +2,7 @@ ProtocolConfiguration:
|
||||||
Magic: 1951352142
|
Magic: 1951352142
|
||||||
SecondsPerBlock: 15
|
SecondsPerBlock: 15
|
||||||
MemPoolSize: 50000
|
MemPoolSize: 50000
|
||||||
StandbyValidators:
|
StandbyCommittee:
|
||||||
- 023e9b32ea89b94d066e649b124fd50e396ee91369e8e2a6ae1b11c170d022256d
|
- 023e9b32ea89b94d066e649b124fd50e396ee91369e8e2a6ae1b11c170d022256d
|
||||||
- 03009b7540e10f2562e5fd8fac9eaec25166a58b26e412348ff5a86927bfac22a2
|
- 03009b7540e10f2562e5fd8fac9eaec25166a58b26e412348ff5a86927bfac22a2
|
||||||
- 02ba2c70f5996f357a43198705859fae2cfea13e1172962800772b3d588a9d4abd
|
- 02ba2c70f5996f357a43198705859fae2cfea13e1172962800772b3d588a9d4abd
|
||||||
|
@ -10,6 +10,7 @@ ProtocolConfiguration:
|
||||||
- 02a7834be9b32e2981d157cb5bbd3acb42cfd11ea5c3b10224d7a44e98c5910f1b
|
- 02a7834be9b32e2981d157cb5bbd3acb42cfd11ea5c3b10224d7a44e98c5910f1b
|
||||||
- 0214baf0ceea3a66f17e7e1e839ea25fd8bed6cd82e6bb6e68250189065f44ff01
|
- 0214baf0ceea3a66f17e7e1e839ea25fd8bed6cd82e6bb6e68250189065f44ff01
|
||||||
- 030205e9cefaea5a1dfc580af20c8d5aa2468bb0148f1a5e4605fc622c80e604ba
|
- 030205e9cefaea5a1dfc580af20c8d5aa2468bb0148f1a5e4605fc622c80e604ba
|
||||||
|
ValidatorsCount: 7
|
||||||
SeedList:
|
SeedList:
|
||||||
- seed1t.neo.org:20333
|
- seed1t.neo.org:20333
|
||||||
- seed2t.neo.org:20333
|
- seed2t.neo.org:20333
|
||||||
|
|
|
@ -2,11 +2,12 @@ ProtocolConfiguration:
|
||||||
Magic: 42
|
Magic: 42
|
||||||
SecondsPerBlock: 15
|
SecondsPerBlock: 15
|
||||||
MemPoolSize: 50000
|
MemPoolSize: 50000
|
||||||
StandbyValidators:
|
StandbyCommittee:
|
||||||
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
|
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
|
||||||
- 02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e
|
- 02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e
|
||||||
- 03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699
|
- 03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699
|
||||||
- 02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62
|
- 02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62
|
||||||
|
ValidatorsCount: 4
|
||||||
SeedList:
|
SeedList:
|
||||||
- 127.0.0.1:20334
|
- 127.0.0.1:20334
|
||||||
- 127.0.0.1:20335
|
- 127.0.0.1:20335
|
||||||
|
|
|
@ -13,7 +13,8 @@ type (
|
||||||
SaveStorageBatch bool `yaml:"SaveStorageBatch"`
|
SaveStorageBatch bool `yaml:"SaveStorageBatch"`
|
||||||
SecondsPerBlock int `yaml:"SecondsPerBlock"`
|
SecondsPerBlock int `yaml:"SecondsPerBlock"`
|
||||||
SeedList []string `yaml:"SeedList"`
|
SeedList []string `yaml:"SeedList"`
|
||||||
StandbyValidators []string `yaml:"StandbyValidators"`
|
StandbyCommittee []string `yaml:"StandbyCommittee"`
|
||||||
|
ValidatorsCount int `yaml:"ValidatorsCount"`
|
||||||
// Whether to verify received blocks.
|
// Whether to verify received blocks.
|
||||||
VerifyBlocks bool `yaml:"VerifyBlocks"`
|
VerifyBlocks bool `yaml:"VerifyBlocks"`
|
||||||
// Whether to verify transactions in received blocks.
|
// Whether to verify transactions in received blocks.
|
||||||
|
|
|
@ -119,7 +119,7 @@ type Blockchain struct {
|
||||||
// cache for block verification keys.
|
// cache for block verification keys.
|
||||||
keyCache map[util.Uint160]map[string]*keys.PublicKey
|
keyCache map[util.Uint160]map[string]*keys.PublicKey
|
||||||
|
|
||||||
sbValidators keys.PublicKeys
|
sbCommittee keys.PublicKeys
|
||||||
|
|
||||||
log *zap.Logger
|
log *zap.Logger
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L
|
||||||
cfg.MemPoolSize = defaultMemPoolSize
|
cfg.MemPoolSize = defaultMemPoolSize
|
||||||
log.Info("mempool size is not set or wrong, setting default value", zap.Int("MemPoolSize", cfg.MemPoolSize))
|
log.Info("mempool size is not set or wrong, setting default value", zap.Int("MemPoolSize", cfg.MemPoolSize))
|
||||||
}
|
}
|
||||||
validators, err := validatorsFromConfig(cfg)
|
committee, err := committeeFromConfig(cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -169,7 +169,7 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L
|
||||||
runToExitCh: make(chan struct{}),
|
runToExitCh: make(chan struct{}),
|
||||||
memPool: mempool.NewMemPool(cfg.MemPoolSize),
|
memPool: mempool.NewMemPool(cfg.MemPoolSize),
|
||||||
keyCache: make(map[util.Uint160]map[string]*keys.PublicKey),
|
keyCache: make(map[util.Uint160]map[string]*keys.PublicKey),
|
||||||
sbValidators: validators,
|
sbCommittee: committee,
|
||||||
log: log,
|
log: log,
|
||||||
events: make(chan bcEvent),
|
events: make(chan bcEvent),
|
||||||
subCh: make(chan interface{}),
|
subCh: make(chan interface{}),
|
||||||
|
@ -1393,7 +1393,12 @@ func (bc *Blockchain) PoolTx(t *transaction.Transaction) error {
|
||||||
|
|
||||||
//GetStandByValidators returns validators from the configuration.
|
//GetStandByValidators returns validators from the configuration.
|
||||||
func (bc *Blockchain) GetStandByValidators() keys.PublicKeys {
|
func (bc *Blockchain) GetStandByValidators() keys.PublicKeys {
|
||||||
return bc.sbValidators.Copy()
|
return bc.sbCommittee[:bc.config.ValidatorsCount].Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStandByCommittee returns standby commitee from the configuration.
|
||||||
|
func (bc *Blockchain) GetStandByCommittee() keys.PublicKeys {
|
||||||
|
return bc.sbCommittee.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetValidators returns current validators.
|
// GetValidators returns current validators.
|
||||||
|
@ -1408,7 +1413,7 @@ func (bc *Blockchain) GetNextBlockValidators() ([]*keys.PublicKey, error) {
|
||||||
|
|
||||||
// GetEnrollments returns all registered validators.
|
// GetEnrollments returns all registered validators.
|
||||||
func (bc *Blockchain) GetEnrollments() ([]state.Validator, error) {
|
func (bc *Blockchain) GetEnrollments() ([]state.Validator, error) {
|
||||||
return bc.contracts.NEO.GetRegisteredValidators(bc.dao)
|
return bc.contracts.NEO.GetCandidates(bc.dao)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetScriptHashesForVerifying returns all the ScriptHashes of a transaction which will be use
|
// GetScriptHashesForVerifying returns all the ScriptHashes of a transaction which will be use
|
||||||
|
|
|
@ -42,6 +42,7 @@ type Blockchainer interface {
|
||||||
GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog
|
GetNEP5TransferLog(util.Uint160) *state.NEP5TransferLog
|
||||||
GetNEP5Balances(util.Uint160) *state.NEP5Balances
|
GetNEP5Balances(util.Uint160) *state.NEP5Balances
|
||||||
GetValidators() ([]*keys.PublicKey, error)
|
GetValidators() ([]*keys.PublicKey, error)
|
||||||
|
GetStandByCommittee() keys.PublicKeys
|
||||||
GetStandByValidators() keys.PublicKeys
|
GetStandByValidators() keys.PublicKeys
|
||||||
GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error)
|
GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error)
|
||||||
GetStateRoot(height uint32) (*state.MPTRootState, error)
|
GetStateRoot(height uint32) (*state.MPTRootState, error)
|
||||||
|
|
|
@ -394,7 +394,7 @@ func addSigners(txs ...*transaction.Transaction) {
|
||||||
|
|
||||||
func signTx(bc *Blockchain, txs ...*transaction.Transaction) error {
|
func signTx(bc *Blockchain, txs ...*transaction.Transaction) error {
|
||||||
validators := bc.GetStandByValidators()
|
validators := bc.GetStandByValidators()
|
||||||
rawScript, err := smartcontract.CreateMultiSigRedeemScript(len(bc.config.StandbyValidators)/2+1, validators)
|
rawScript, err := smartcontract.CreateMultiSigRedeemScript(bc.config.ValidatorsCount/2+1, validators)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "fail to sign tx")
|
return errors.Wrap(err, "fail to sign tx")
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,7 +70,7 @@ func (g *GAS) Initialize(ic *interop.Context) error {
|
||||||
if err := g.nep5TokenNative.Initialize(ic); err != nil {
|
if err := g.nep5TokenNative.Initialize(ic); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if g.nep5TokenNative.getTotalSupply(ic).Sign() != 0 {
|
if g.nep5TokenNative.getTotalSupply(ic.DAO).Sign() != 0 {
|
||||||
return errors.New("already initialized")
|
return errors.New("already initialized")
|
||||||
}
|
}
|
||||||
h, _, err := getStandbyValidatorsHash(ic)
|
h, _, err := getStandbyValidatorsHash(ic)
|
||||||
|
|
|
@ -42,14 +42,16 @@ const (
|
||||||
neoContractID = -1
|
neoContractID = -1
|
||||||
// NEOTotalSupply is the total amount of NEO in the system.
|
// NEOTotalSupply is the total amount of NEO in the system.
|
||||||
NEOTotalSupply = 100000000
|
NEOTotalSupply = 100000000
|
||||||
// prefixValidator is a prefix used to store validator's data.
|
// prefixCandidate is a prefix used to store validator's data.
|
||||||
prefixValidator = 33
|
prefixCandidate = 33
|
||||||
|
// prefixVotersCount is a prefix for storing total amount of NEO of voters.
|
||||||
|
prefixVotersCount = 1
|
||||||
|
// effectiveVoterTurnout represents minimal ratio of total supply to total amount voted value
|
||||||
|
// which is require to use non-standby validators.
|
||||||
|
effectiveVoterTurnout = 5
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
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
|
// nextValidatorsKey is a key used to store validators for the
|
||||||
// next block.
|
// next block.
|
||||||
nextValidatorsKey = []byte{14}
|
nextValidatorsKey = []byte{14}
|
||||||
|
@ -61,7 +63,7 @@ func makeValidatorKey(key *keys.PublicKey) []byte {
|
||||||
// Don't create a new buffer.
|
// Don't create a new buffer.
|
||||||
b = append(b, 0)
|
b = append(b, 0)
|
||||||
copy(b[1:], b[0:])
|
copy(b[1:], b[0:])
|
||||||
b[0] = prefixValidator
|
b[0] = prefixCandidate
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,19 +91,28 @@ func NewNEO() *NEO {
|
||||||
md := newMethodAndPrice(n.unclaimedGas, 3000000, smartcontract.AllowStates)
|
md := newMethodAndPrice(n.unclaimedGas, 3000000, smartcontract.AllowStates)
|
||||||
n.AddMethod(md, desc, true)
|
n.AddMethod(md, desc, true)
|
||||||
|
|
||||||
desc = newDescriptor("registerValidator", smartcontract.BoolType,
|
desc = newDescriptor("registerCandidate", smartcontract.BoolType,
|
||||||
manifest.NewParameter("pubkey", smartcontract.PublicKeyType))
|
manifest.NewParameter("pubkey", smartcontract.PublicKeyType))
|
||||||
md = newMethodAndPrice(n.registerValidator, 5000000, smartcontract.AllowModifyStates)
|
md = newMethodAndPrice(n.registerCandidate, 5000000, smartcontract.AllowModifyStates)
|
||||||
|
n.AddMethod(md, desc, false)
|
||||||
|
|
||||||
|
desc = newDescriptor("unregisterCandidate", smartcontract.BoolType,
|
||||||
|
manifest.NewParameter("pubkey", smartcontract.PublicKeyType))
|
||||||
|
md = newMethodAndPrice(n.unregisterCandidate, 5000000, smartcontract.AllowModifyStates)
|
||||||
n.AddMethod(md, desc, false)
|
n.AddMethod(md, desc, false)
|
||||||
|
|
||||||
desc = newDescriptor("vote", smartcontract.BoolType,
|
desc = newDescriptor("vote", smartcontract.BoolType,
|
||||||
manifest.NewParameter("account", smartcontract.Hash160Type),
|
manifest.NewParameter("account", smartcontract.Hash160Type),
|
||||||
manifest.NewParameter("pubkeys", smartcontract.ArrayType))
|
manifest.NewParameter("pubkey", smartcontract.PublicKeyType))
|
||||||
md = newMethodAndPrice(n.vote, 500000000, smartcontract.AllowModifyStates)
|
md = newMethodAndPrice(n.vote, 500000000, smartcontract.AllowModifyStates)
|
||||||
n.AddMethod(md, desc, false)
|
n.AddMethod(md, desc, false)
|
||||||
|
|
||||||
desc = newDescriptor("getRegisteredValidators", smartcontract.ArrayType)
|
desc = newDescriptor("getCandidates", smartcontract.ArrayType)
|
||||||
md = newMethodAndPrice(n.getRegisteredValidatorsCall, 100000000, smartcontract.AllowStates)
|
md = newMethodAndPrice(n.getCandidatesCall, 100000000, smartcontract.AllowStates)
|
||||||
|
n.AddMethod(md, desc, true)
|
||||||
|
|
||||||
|
desc = newDescriptor("getСommittee", smartcontract.ArrayType)
|
||||||
|
md = newMethodAndPrice(n.getCommittee, 100000000, smartcontract.AllowStates)
|
||||||
n.AddMethod(md, desc, true)
|
n.AddMethod(md, desc, true)
|
||||||
|
|
||||||
desc = newDescriptor("getValidators", smartcontract.ArrayType)
|
desc = newDescriptor("getValidators", smartcontract.ArrayType)
|
||||||
|
@ -121,7 +132,7 @@ func (n *NEO) Initialize(ic *interop.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.nep5TokenNative.getTotalSupply(ic).Sign() != 0 {
|
if n.nep5TokenNative.getTotalSupply(ic.DAO).Sign() != 0 {
|
||||||
return errors.New("already initialized")
|
return errors.New("already initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,8 +142,13 @@ func (n *NEO) Initialize(ic *interop.Context) error {
|
||||||
}
|
}
|
||||||
n.mint(ic, h, big.NewInt(NEOTotalSupply))
|
n.mint(ic, h, big.NewInt(NEOTotalSupply))
|
||||||
|
|
||||||
|
err = ic.DAO.PutStorageItem(n.ContractID, []byte{prefixVotersCount}, &state.StorageItem{Value: []byte{0}})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for i := range vs {
|
for i := range vs {
|
||||||
if err := n.registerValidatorInternal(ic, vs[i]); err != nil {
|
if err := n.RegisterCandidateInternal(ic, vs[i]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,21 +182,11 @@ func (n *NEO) increaseBalance(ic *interop.Context, h util.Uint160, si *state.Sto
|
||||||
si.Value = acc.Bytes()
|
si.Value = acc.Bytes()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if len(acc.Votes) > 0 {
|
if err := n.ModifyAccountVotes(acc, ic.DAO, amount, modifyVoteTransfer); err != nil {
|
||||||
if err := n.ModifyAccountVotes(acc, ic.DAO, amount); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
siVC := ic.DAO.GetStorageItem(n.ContractID, validatorsCountKey)
|
if acc.VoteTo != nil {
|
||||||
if siVC == nil {
|
if err := n.modifyVoterTurnout(ic.DAO, amount); err != 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.ContractID, validatorsCountKey, siVC); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,43 +222,74 @@ func (n *NEO) unclaimedGas(ic *interop.Context, args []stackitem.Item) stackitem
|
||||||
return stackitem.NewBigInteger(gen)
|
return stackitem.NewBigInteger(gen)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) registerValidator(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (n *NEO) registerCandidate(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
err := n.registerValidatorInternal(ic, toPublicKey(args[0]))
|
pub := toPublicKey(args[0])
|
||||||
|
ok, err := runtime.CheckKeyedWitness(ic, pub)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else if !ok {
|
||||||
|
return stackitem.NewBool(false)
|
||||||
|
}
|
||||||
|
err = n.RegisterCandidateInternal(ic, pub)
|
||||||
return stackitem.NewBool(err == nil)
|
return stackitem.NewBool(err == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) registerValidatorInternal(ic *interop.Context, pub *keys.PublicKey) error {
|
// RegisterCandidateInternal registers pub as a new candidate.
|
||||||
|
func (n *NEO) RegisterCandidateInternal(ic *interop.Context, pub *keys.PublicKey) error {
|
||||||
key := makeValidatorKey(pub)
|
key := makeValidatorKey(pub)
|
||||||
si := ic.DAO.GetStorageItem(n.ContractID, key)
|
si := ic.DAO.GetStorageItem(n.ContractID, key)
|
||||||
if si != nil {
|
if si == nil {
|
||||||
return errors.New("already registered")
|
c := &candidate{Registered: true}
|
||||||
|
si = &state.StorageItem{Value: c.Bytes()}
|
||||||
|
} else {
|
||||||
|
c := new(candidate).FromBytes(si.Value)
|
||||||
|
c.Registered = true
|
||||||
|
si.Value = c.Bytes()
|
||||||
}
|
}
|
||||||
si = new(state.StorageItem)
|
return ic.DAO.PutStorageItem(n.ContractID, key, si)
|
||||||
// Zero value.
|
}
|
||||||
si.Value = []byte{}
|
|
||||||
|
func (n *NEO) unregisterCandidate(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
|
pub := toPublicKey(args[0])
|
||||||
|
ok, err := runtime.CheckKeyedWitness(ic, pub)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
} else if !ok {
|
||||||
|
return stackitem.NewBool(false)
|
||||||
|
}
|
||||||
|
err = n.UnregisterCandidateInternal(ic, pub)
|
||||||
|
return stackitem.NewBool(err == nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnregisterCandidateInternal unregisters pub as a candidate.
|
||||||
|
func (n *NEO) UnregisterCandidateInternal(ic *interop.Context, pub *keys.PublicKey) error {
|
||||||
|
key := makeValidatorKey(pub)
|
||||||
|
si := ic.DAO.GetStorageItem(n.ContractID, key)
|
||||||
|
if si == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
n.validators.Store(keys.PublicKeys(nil))
|
||||||
|
c := new(candidate).FromBytes(si.Value)
|
||||||
|
if c.Votes.Sign() == 0 {
|
||||||
|
return ic.DAO.DeleteStorageItem(n.ContractID, key)
|
||||||
|
}
|
||||||
|
c.Registered = false
|
||||||
|
si.Value = c.Bytes()
|
||||||
return ic.DAO.PutStorageItem(n.ContractID, key, si)
|
return ic.DAO.PutStorageItem(n.ContractID, key, si)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) vote(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (n *NEO) vote(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
acc := toUint160(args[0])
|
acc := toUint160(args[0])
|
||||||
arr := args[1].Value().([]stackitem.Item)
|
var pub *keys.PublicKey
|
||||||
var pubs keys.PublicKeys
|
if _, ok := args[1].(stackitem.Null); !ok {
|
||||||
for i := range arr {
|
pub = toPublicKey(args[1])
|
||||||
pub := new(keys.PublicKey)
|
|
||||||
bs, err := arr[i].TryBytes()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
} else if err := pub.DecodeBytes(bs); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
pubs = append(pubs, pub)
|
err := n.VoteInternal(ic, acc, pub)
|
||||||
}
|
|
||||||
err := n.VoteInternal(ic, acc, pubs)
|
|
||||||
return stackitem.NewBool(err == nil)
|
return stackitem.NewBool(err == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// VoteInternal votes from account h for validarors specified in pubs.
|
// VoteInternal votes from account h for validarors specified in pubs.
|
||||||
func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.PublicKeys) error {
|
func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pub *keys.PublicKey) error {
|
||||||
ok, err := runtime.CheckHashedWitness(ic, h)
|
ok, err := runtime.CheckHashedWitness(ic, h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -268,90 +305,80 @@ func (n *NEO) VoteInternal(ic *interop.Context, h util.Uint160, pubs keys.Public
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := n.ModifyAccountVotes(acc, ic.DAO, new(big.Int).Neg(&acc.Balance)); err != nil {
|
if (acc.VoteTo == nil) != (pub == nil) {
|
||||||
return err
|
val := &acc.Balance
|
||||||
|
if pub == nil {
|
||||||
|
val = new(big.Int).Neg(val)
|
||||||
}
|
}
|
||||||
pubs = pubs.Unique()
|
if err := n.modifyVoterTurnout(ic.DAO, val); err != nil {
|
||||||
// Check validators registration.
|
|
||||||
var newPubs keys.PublicKeys
|
|
||||||
for _, pub := range pubs {
|
|
||||||
if ic.DAO.GetStorageItem(n.ContractID, makeValidatorKey(pub)) == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newPubs = append(newPubs, pub)
|
|
||||||
}
|
|
||||||
if lp, lv := len(newPubs), len(acc.Votes); lp != lv {
|
|
||||||
var si *state.StorageItem
|
|
||||||
var vc *ValidatorsCount
|
|
||||||
var err error
|
|
||||||
|
|
||||||
si = ic.DAO.GetStorageItem(n.ContractID, validatorsCountKey)
|
|
||||||
if si == nil {
|
|
||||||
// The first voter.
|
|
||||||
si = new(state.StorageItem)
|
|
||||||
vc = new(ValidatorsCount)
|
|
||||||
} else {
|
|
||||||
vc, err = ValidatorsCountFromBytes(si.Value)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if lv > 0 {
|
if err := n.ModifyAccountVotes(acc, ic.DAO, new(big.Int).Neg(&acc.Balance), modifyVoteOld); err != nil {
|
||||||
vc[lv-1].Sub(&vc[lv-1], &acc.Balance)
|
|
||||||
}
|
|
||||||
if len(newPubs) > 0 {
|
|
||||||
vc[lp-1].Add(&vc[lp-1], &acc.Balance)
|
|
||||||
}
|
|
||||||
si.Value = vc.Bytes()
|
|
||||||
if err := ic.DAO.PutStorageItem(n.ContractID, validatorsCountKey, si); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
acc.VoteTo = pub
|
||||||
acc.Votes = newPubs
|
if err := n.ModifyAccountVotes(acc, ic.DAO, &acc.Balance, modifyVoteNew); err != nil {
|
||||||
if err := n.ModifyAccountVotes(acc, ic.DAO, &acc.Balance); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
si.Value = acc.Bytes()
|
si.Value = acc.Bytes()
|
||||||
return ic.DAO.PutStorageItem(n.ContractID, key, si)
|
return ic.DAO.PutStorageItem(n.ContractID, key, si)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
modifyVoteTransfer = iota
|
||||||
|
modifyVoteOld
|
||||||
|
modifyVoteNew
|
||||||
|
)
|
||||||
|
|
||||||
// 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 (n *NEO) ModifyAccountVotes(acc *state.NEOBalanceState, d dao.DAO, value *big.Int) error {
|
// typ specifies if this modify is occuring during transfer or vote (with old or new validator).
|
||||||
for _, vote := range acc.Votes {
|
func (n *NEO) ModifyAccountVotes(acc *state.NEOBalanceState, d dao.DAO, value *big.Int, typ int) error {
|
||||||
key := makeValidatorKey(vote)
|
if acc.VoteTo != nil {
|
||||||
|
key := makeValidatorKey(acc.VoteTo)
|
||||||
si := d.GetStorageItem(n.ContractID, key)
|
si := d.GetStorageItem(n.ContractID, key)
|
||||||
if si == nil {
|
if si == nil {
|
||||||
return errors.New("invalid validator")
|
return errors.New("invalid validator")
|
||||||
}
|
}
|
||||||
votes := bigint.FromBytes(si.Value)
|
cd := new(candidate).FromBytes(si.Value)
|
||||||
votes.Add(votes, value)
|
cd.Votes.Add(&cd.Votes, value)
|
||||||
si.Value = bigint.ToPreallocatedBytes(votes, si.Value[:0])
|
switch typ {
|
||||||
if err := d.PutStorageItem(n.ContractID, key, si); err != nil {
|
case modifyVoteOld:
|
||||||
return err
|
if !cd.Registered && cd.Votes.Sign() == 0 {
|
||||||
|
return d.DeleteStorageItem(n.ContractID, key)
|
||||||
|
}
|
||||||
|
case modifyVoteNew:
|
||||||
|
if !cd.Registered {
|
||||||
|
return errors.New("validator must be registered")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
n.validators.Store(keys.PublicKeys(nil))
|
n.validators.Store(keys.PublicKeys(nil))
|
||||||
|
si.Value = cd.Bytes()
|
||||||
|
return d.PutStorageItem(n.ContractID, key, si)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) getRegisteredValidators(d dao.DAO) ([]keyWithVotes, error) {
|
func (n *NEO) getCandidates(d dao.DAO) ([]keyWithVotes, error) {
|
||||||
siMap, err := d.GetStorageItemsWithPrefix(n.ContractID, []byte{prefixValidator})
|
siMap, err := d.GetStorageItemsWithPrefix(n.ContractID, []byte{prefixCandidate})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
arr := make([]keyWithVotes, 0, len(siMap))
|
arr := make([]keyWithVotes, 0, len(siMap))
|
||||||
for key, si := range siMap {
|
for key, si := range siMap {
|
||||||
votes := bigint.FromBytes(si.Value)
|
c := new(candidate).FromBytes(si.Value)
|
||||||
arr = append(arr, keyWithVotes{key, votes})
|
if c.Registered {
|
||||||
|
arr = append(arr, keyWithVotes{key, &c.Votes})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sort.Slice(arr, func(i, j int) bool { return strings.Compare(arr[i].Key, arr[j].Key) == -1 })
|
sort.Slice(arr, func(i, j int) bool { return strings.Compare(arr[i].Key, arr[j].Key) == -1 })
|
||||||
return arr, nil
|
return arr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRegisteredValidators returns current registered validators list with keys
|
// GetCandidates returns current registered validators list with keys
|
||||||
// and votes.
|
// and votes.
|
||||||
func (n *NEO) GetRegisteredValidators(d dao.DAO) ([]state.Validator, error) {
|
func (n *NEO) GetCandidates(d dao.DAO) ([]state.Validator, error) {
|
||||||
kvs, err := n.getRegisteredValidators(d)
|
kvs, err := n.getCandidates(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -366,8 +393,8 @@ func (n *NEO) GetRegisteredValidators(d dao.DAO) ([]state.Validator, error) {
|
||||||
return arr, nil
|
return arr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NEO) getRegisteredValidatorsCall(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
func (n *NEO) getCandidatesCall(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
validators, err := n.getRegisteredValidators(ic.DAO)
|
validators, err := n.getCandidates(ic.DAO)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@ -386,53 +413,15 @@ func (n *NEO) GetValidatorsInternal(bc blockchainer.Blockchainer, d dao.DAO) (ke
|
||||||
if vals := n.validators.Load().(keys.PublicKeys); vals != nil {
|
if vals := n.validators.Load().(keys.PublicKeys); vals != nil {
|
||||||
return vals.Copy(), nil
|
return vals.Copy(), nil
|
||||||
}
|
}
|
||||||
standByValidators := bc.GetStandByValidators()
|
result, err := n.getCommitteeMembers(bc, d)
|
||||||
si := d.GetStorageItem(n.ContractID, validatorsCountKey)
|
|
||||||
if si == nil {
|
|
||||||
n.validators.Store(standByValidators)
|
|
||||||
return standByValidators.Copy(), nil
|
|
||||||
}
|
|
||||||
validatorsCount, err := ValidatorsCountFromBytes(si.Value)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
validators, err := n.GetRegisteredValidators(d)
|
count := bc.GetConfig().ValidatorsCount
|
||||||
if err != nil {
|
if len(result) < count {
|
||||||
return nil, err
|
count = len(result)
|
||||||
}
|
}
|
||||||
sort.Slice(validators, func(i, j int) bool {
|
|
||||||
// The most-voted validators should end up in the front of the list.
|
|
||||||
cmp := validators[i].Votes.Cmp(validators[j].Votes)
|
|
||||||
if cmp != 0 {
|
|
||||||
return cmp > 0
|
|
||||||
}
|
|
||||||
// Ties are broken with public keys.
|
|
||||||
return validators[i].Key.Cmp(validators[j].Key) == -1
|
|
||||||
})
|
|
||||||
|
|
||||||
count := validatorsCount.GetWeightedAverage()
|
|
||||||
if count < len(standByValidators) {
|
|
||||||
count = len(standByValidators)
|
|
||||||
}
|
|
||||||
|
|
||||||
uniqueSBValidators := standByValidators.Unique()
|
|
||||||
result := keys.PublicKeys{}
|
|
||||||
for _, validator := range validators {
|
|
||||||
if validator.Votes.Sign() > 0 || uniqueSBValidators.Contains(validator.Key) {
|
|
||||||
result = append(result, validator.Key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.Len() >= count {
|
|
||||||
result = result[: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)
|
|
||||||
n.validators.Store(result)
|
n.validators.Store(result)
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
@ -445,6 +434,68 @@ func (n *NEO) getValidators(ic *interop.Context, _ []stackitem.Item) stackitem.I
|
||||||
return pubsToArray(result)
|
return pubsToArray(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *NEO) getCommittee(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
|
pubs, err := n.getCommitteeMembers(ic.Chain, ic.DAO)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
sort.Sort(pubs)
|
||||||
|
return pubsToArray(pubs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NEO) modifyVoterTurnout(d dao.DAO, amount *big.Int) error {
|
||||||
|
key := []byte{prefixVotersCount}
|
||||||
|
si := d.GetStorageItem(n.ContractID, key)
|
||||||
|
if si == nil {
|
||||||
|
return errors.New("voters count not found")
|
||||||
|
}
|
||||||
|
votersCount := bigint.FromBytes(si.Value)
|
||||||
|
votersCount.Add(votersCount, amount)
|
||||||
|
si.Value = bigint.ToBytes(votersCount)
|
||||||
|
return d.PutStorageItem(n.ContractID, key, si)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NEO) getCommitteeMembers(bc blockchainer.Blockchainer, d dao.DAO) (keys.PublicKeys, error) {
|
||||||
|
key := []byte{prefixVotersCount}
|
||||||
|
si := d.GetStorageItem(n.ContractID, key)
|
||||||
|
if si == nil {
|
||||||
|
return nil, errors.New("voters count not found")
|
||||||
|
}
|
||||||
|
votersCount := bigint.FromBytes(si.Value)
|
||||||
|
// votersCount / totalSupply must be >= 0.2
|
||||||
|
votersCount.Mul(votersCount, big.NewInt(effectiveVoterTurnout))
|
||||||
|
voterTurnout := votersCount.Div(votersCount, n.getTotalSupply(d))
|
||||||
|
if voterTurnout.Sign() != 1 {
|
||||||
|
return bc.GetStandByValidators(), nil
|
||||||
|
}
|
||||||
|
cs, err := n.getCandidates(d)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sbVals := bc.GetStandByValidators()
|
||||||
|
count := len(sbVals)
|
||||||
|
if len(cs) < count {
|
||||||
|
return sbVals, nil
|
||||||
|
}
|
||||||
|
sort.Slice(cs, func(i, j int) bool {
|
||||||
|
// The most-voted validators should end up in the front of the list.
|
||||||
|
cmp := cs[i].Votes.Cmp(cs[j].Votes)
|
||||||
|
if cmp != 0 {
|
||||||
|
return cmp > 0
|
||||||
|
}
|
||||||
|
// Ties are broken with public keys.
|
||||||
|
return strings.Compare(cs[i].Key, cs[j].Key) == -1
|
||||||
|
})
|
||||||
|
pubs := make(keys.PublicKeys, count)
|
||||||
|
for i := range pubs {
|
||||||
|
pubs[i], err = keys.NewPublicKeyFromBytes([]byte(cs[i].Key), elliptic.P256())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pubs, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
func (n *NEO) getNextBlockValidators(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
result, err := n.getNextBlockValidatorsInternal(ic.Chain, ic.DAO)
|
result, err := n.getNextBlockValidatorsInternal(ic.Chain, ic.DAO)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
48
pkg/core/native/native_neo_candidate.go
Normal file
48
pkg/core/native/native_neo_candidate.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
)
|
||||||
|
|
||||||
|
type candidate struct {
|
||||||
|
Registered bool
|
||||||
|
Votes big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes marshals c to byte array.
|
||||||
|
func (c *candidate) Bytes() []byte {
|
||||||
|
w := io.NewBufBinWriter()
|
||||||
|
stackitem.EncodeBinaryStackItem(c.toStackItem(), w.BinWriter)
|
||||||
|
return w.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromBytes unmarshals candidate from byte array.
|
||||||
|
func (c *candidate) FromBytes(data []byte) *candidate {
|
||||||
|
r := io.NewBinReaderFromBuf(data)
|
||||||
|
item := stackitem.DecodeBinaryStackItem(r)
|
||||||
|
if r.Err != nil {
|
||||||
|
panic(r.Err)
|
||||||
|
}
|
||||||
|
return c.fromStackItem(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *candidate) toStackItem() stackitem.Item {
|
||||||
|
return stackitem.NewStruct([]stackitem.Item{
|
||||||
|
stackitem.NewBool(c.Registered),
|
||||||
|
stackitem.NewBigInteger(&c.Votes),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *candidate) fromStackItem(item stackitem.Item) *candidate {
|
||||||
|
arr := item.(*stackitem.Struct).Value().([]stackitem.Item)
|
||||||
|
vs, err := arr[1].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
c.Registered = arr[0].Bool()
|
||||||
|
c.Votes = *vs
|
||||||
|
return c
|
||||||
|
}
|
18
pkg/core/native/native_neo_test.go
Normal file
18
pkg/core/native/native_neo_test.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package native
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCandidate_Bytes(t *testing.T) {
|
||||||
|
expected := &candidate{
|
||||||
|
Registered: true,
|
||||||
|
Votes: *big.NewInt(0x0F),
|
||||||
|
}
|
||||||
|
data := expected.Bytes()
|
||||||
|
actual := new(candidate).FromBytes(data)
|
||||||
|
require.Equal(t, expected, actual)
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/core/dao"
|
||||||
"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"
|
||||||
|
@ -104,27 +105,27 @@ func (c *nep5TokenNative) Decimals(_ *interop.Context, _ []stackitem.Item) stack
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) TotalSupply(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
func (c *nep5TokenNative) TotalSupply(ic *interop.Context, _ []stackitem.Item) stackitem.Item {
|
||||||
return stackitem.NewBigInteger(c.getTotalSupply(ic))
|
return stackitem.NewBigInteger(c.getTotalSupply(ic.DAO))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) getTotalSupply(ic *interop.Context) *big.Int {
|
func (c *nep5TokenNative) getTotalSupply(d dao.DAO) *big.Int {
|
||||||
si := ic.DAO.GetStorageItem(c.ContractID, totalSupplyKey)
|
si := d.GetStorageItem(c.ContractID, totalSupplyKey)
|
||||||
if si == nil {
|
if si == nil {
|
||||||
return big.NewInt(0)
|
return big.NewInt(0)
|
||||||
}
|
}
|
||||||
return bigint.FromBytes(si.Value)
|
return bigint.FromBytes(si.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) saveTotalSupply(ic *interop.Context, supply *big.Int) error {
|
func (c *nep5TokenNative) saveTotalSupply(d dao.DAO, supply *big.Int) error {
|
||||||
si := &state.StorageItem{Value: bigint.ToBytes(supply)}
|
si := &state.StorageItem{Value: bigint.ToBytes(supply)}
|
||||||
return ic.DAO.PutStorageItem(c.ContractID, totalSupplyKey, si)
|
return d.PutStorageItem(c.ContractID, totalSupplyKey, si)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) Transfer(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
func (c *nep5TokenNative) Transfer(ic *interop.Context, args []stackitem.Item) stackitem.Item {
|
||||||
from := toUint160(args[0])
|
from := toUint160(args[0])
|
||||||
to := toUint160(args[1])
|
to := toUint160(args[1])
|
||||||
amount := toBigInt(args[2])
|
amount := toBigInt(args[2])
|
||||||
err := c.transfer(ic, from, to, amount)
|
err := c.TransferInternal(ic, from, to, amount)
|
||||||
return stackitem.NewBool(err == nil)
|
return stackitem.NewBool(err == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +171,8 @@ func (c *nep5TokenNative) updateAccBalance(ic *interop.Context, acc util.Uint160
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *nep5TokenNative) transfer(ic *interop.Context, from, to util.Uint160, amount *big.Int) error {
|
// TransferInternal transfers NEO between accounts.
|
||||||
|
func (c *nep5TokenNative) TransferInternal(ic *interop.Context, from, to util.Uint160, amount *big.Int) error {
|
||||||
if amount.Sign() == -1 {
|
if amount.Sign() == -1 {
|
||||||
return errors.New("negative amount")
|
return errors.New("negative amount")
|
||||||
}
|
}
|
||||||
|
@ -248,9 +250,9 @@ func (c *nep5TokenNative) addTokens(ic *interop.Context, h util.Uint160, amount
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
supply := c.getTotalSupply(ic)
|
supply := c.getTotalSupply(ic.DAO)
|
||||||
supply.Add(supply, amount)
|
supply.Add(supply, amount)
|
||||||
err := c.saveTotalSupply(ic, supply)
|
err := c.saveTotalSupply(ic.DAO, supply)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,51 +63,3 @@ func (vc *ValidatorsCount) DecodeBinary(r *io.BinReader) {
|
||||||
vc[i] = *bigint.FromBytes(buf)
|
vc[i] = *bigint.FromBytes(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)
|
|
||||||
}
|
|
||||||
|
|
104
pkg/core/native_neo_test.go
Normal file
104
pkg/core/native_neo_test.go
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
|
||||||
|
"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/internal/testchain"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
// testScriptGetter is an auxilliary struct to pass CheckWitness checks.
|
||||||
|
type testScriptGetter struct {
|
||||||
|
h util.Uint160
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t testScriptGetter) GetCallingScriptHash() util.Uint160 { return t.h }
|
||||||
|
func (t testScriptGetter) GetEntryScriptHash() util.Uint160 { return t.h }
|
||||||
|
func (t testScriptGetter) GetCurrentScriptHash() util.Uint160 { return t.h }
|
||||||
|
|
||||||
|
func setSigner(tx *transaction.Transaction, h util.Uint160) {
|
||||||
|
tx.Signers = []transaction.Signer{{
|
||||||
|
Account: h,
|
||||||
|
Scopes: transaction.Global,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNEO_Vote(t *testing.T) {
|
||||||
|
bc := newTestChain(t)
|
||||||
|
defer bc.Close()
|
||||||
|
|
||||||
|
neo := bc.contracts.NEO
|
||||||
|
tx := transaction.New(netmode.UnitTestNet, []byte{}, 0)
|
||||||
|
ic := bc.newInteropContext(trigger.System, bc.dao, nil, tx)
|
||||||
|
|
||||||
|
pubs, err := neo.GetValidatorsInternal(bc, ic.DAO)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, bc.GetStandByValidators(), pubs)
|
||||||
|
|
||||||
|
sz := testchain.Size()
|
||||||
|
|
||||||
|
candidates := make(keys.PublicKeys, sz)
|
||||||
|
for i := 0; i < sz; i++ {
|
||||||
|
priv, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
candidates[i] = priv.PublicKey()
|
||||||
|
if i > 0 {
|
||||||
|
require.NoError(t, neo.RegisterCandidateInternal(ic, candidates[i]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < sz; i++ {
|
||||||
|
to := testchain.PrivateKeyByID(i).GetScriptHash()
|
||||||
|
ic.ScriptGetter = testScriptGetter{testchain.MultisigScriptHash()}
|
||||||
|
require.NoError(t, neo.TransferInternal(ic, testchain.MultisigScriptHash(), to, big.NewInt(int64(sz-i)*10000000)))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 1; i < sz; i++ {
|
||||||
|
h := testchain.PrivateKeyByID(i).GetScriptHash()
|
||||||
|
setSigner(tx, h)
|
||||||
|
ic.ScriptGetter = testScriptGetter{h}
|
||||||
|
require.NoError(t, neo.VoteInternal(ic, h, candidates[i]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// First 3 validators must be the ones we have voted for.
|
||||||
|
pubs, err = neo.GetValidatorsInternal(bc, ic.DAO)
|
||||||
|
require.NoError(t, err)
|
||||||
|
for i := 1; i < sz; i++ {
|
||||||
|
require.Equal(t, pubs[i-1], candidates[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
var ok bool
|
||||||
|
for _, p := range bc.GetStandByValidators() {
|
||||||
|
if pubs[sz-1].Equal(p) {
|
||||||
|
ok = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
require.True(t, ok, "last validator must be stand by")
|
||||||
|
|
||||||
|
// Register and give some value to the last validator.
|
||||||
|
require.NoError(t, neo.RegisterCandidateInternal(ic, candidates[0]))
|
||||||
|
h := testchain.PrivateKeyByID(0).GetScriptHash()
|
||||||
|
setSigner(tx, h)
|
||||||
|
ic.ScriptGetter = testScriptGetter{h}
|
||||||
|
require.NoError(t, neo.VoteInternal(ic, h, candidates[0]))
|
||||||
|
|
||||||
|
pubs, err = neo.GetValidatorsInternal(bc, ic.DAO)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, candidates, pubs)
|
||||||
|
|
||||||
|
require.NoError(t, neo.UnregisterCandidateInternal(ic, candidates[0]))
|
||||||
|
require.Error(t, neo.VoteInternal(ic, h, candidates[0]))
|
||||||
|
|
||||||
|
pubs, err = neo.GetValidatorsInternal(bc, ic.DAO)
|
||||||
|
require.NoError(t, err)
|
||||||
|
for i := range pubs {
|
||||||
|
require.NotEqual(t, candidates[0], pubs[i])
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/elliptic"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -17,7 +18,7 @@ type NEP5BalanceState struct {
|
||||||
type NEOBalanceState struct {
|
type NEOBalanceState struct {
|
||||||
NEP5BalanceState
|
NEP5BalanceState
|
||||||
BalanceHeight uint32
|
BalanceHeight uint32
|
||||||
Votes keys.PublicKeys
|
VoteTo *keys.PublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
// NEP5BalanceStateFromBytes converts serialized NEP5BalanceState to structure.
|
// NEP5BalanceStateFromBytes converts serialized NEP5BalanceState to structure.
|
||||||
|
@ -110,7 +111,11 @@ func (s *NEOBalanceState) DecodeBinary(r *io.BinReader) {
|
||||||
func (s *NEOBalanceState) toStackItem() stackitem.Item {
|
func (s *NEOBalanceState) toStackItem() stackitem.Item {
|
||||||
result := s.NEP5BalanceState.toStackItem().(*stackitem.Struct)
|
result := s.NEP5BalanceState.toStackItem().(*stackitem.Struct)
|
||||||
result.Append(stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight))))
|
result.Append(stackitem.NewBigInteger(big.NewInt(int64(s.BalanceHeight))))
|
||||||
result.Append(stackitem.NewByteArray(s.Votes.Bytes()))
|
if s.VoteTo != nil {
|
||||||
|
result.Append(stackitem.NewByteArray(s.VoteTo.Bytes()))
|
||||||
|
} else {
|
||||||
|
result.Append(stackitem.Null{})
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +123,18 @@ func (s *NEOBalanceState) fromStackItem(item stackitem.Item) error {
|
||||||
structItem := item.Value().([]stackitem.Item)
|
structItem := item.Value().([]stackitem.Item)
|
||||||
s.Balance = *structItem[0].Value().(*big.Int)
|
s.Balance = *structItem[0].Value().(*big.Int)
|
||||||
s.BalanceHeight = uint32(structItem[1].Value().(*big.Int).Int64())
|
s.BalanceHeight = uint32(structItem[1].Value().(*big.Int).Int64())
|
||||||
s.Votes = make(keys.PublicKeys, 0)
|
if _, ok := structItem[2].(stackitem.Null); ok {
|
||||||
return s.Votes.DecodeBytes(structItem[2].Value().([]byte))
|
s.VoteTo = nil
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
bs, err := structItem[2].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
pub, err := keys.NewPublicKeyFromBytes(bs, elliptic.P256())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.VoteTo = pub
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
|
@ -94,9 +95,20 @@ func deployNativeContracts(magic netmode.Magic) *transaction.Transaction {
|
||||||
}
|
}
|
||||||
|
|
||||||
func validatorsFromConfig(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, error) {
|
func validatorsFromConfig(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, error) {
|
||||||
validators := make([]*keys.PublicKey, len(cfg.StandbyValidators))
|
vs, err := committeeFromConfig(cfg)
|
||||||
for i, pubKeyStr := range cfg.StandbyValidators {
|
if err != nil {
|
||||||
pubKey, err := keys.NewPublicKeyFromString(pubKeyStr)
|
return nil, err
|
||||||
|
}
|
||||||
|
return vs[:cfg.ValidatorsCount], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func committeeFromConfig(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, error) {
|
||||||
|
if len(cfg.StandbyCommittee) < cfg.ValidatorsCount {
|
||||||
|
return nil, errors.New("validators count can be less than the size of StandbyCommittee")
|
||||||
|
}
|
||||||
|
validators := make([]*keys.PublicKey, len(cfg.StandbyCommittee))
|
||||||
|
for i := range validators {
|
||||||
|
pubKey, err := keys.NewPublicKeyFromString(cfg.StandbyCommittee[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,6 +103,9 @@ func (chain testChain) GetNEP5Balances(util.Uint160) *state.NEP5Balances {
|
||||||
func (chain testChain) GetValidators() ([]*keys.PublicKey, error) {
|
func (chain testChain) GetValidators() ([]*keys.PublicKey, error) {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
func (chain testChain) GetStandByCommittee() keys.PublicKeys {
|
||||||
|
panic("TODO")
|
||||||
|
}
|
||||||
func (chain testChain) GetStandByValidators() keys.PublicKeys {
|
func (chain testChain) GetStandByValidators() keys.PublicKeys {
|
||||||
panic("TODO")
|
panic("TODO")
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,8 +131,15 @@ func (c *Client) CreateNEP5MultiTransferTx(acc *wallet.Account, gas int64, recep
|
||||||
recepients[i].Address, recepients[i].Amount)
|
recepients[i].Address, recepients[i].Amount)
|
||||||
emit.Opcode(w.BinWriter, opcode.ASSERT)
|
emit.Opcode(w.BinWriter, opcode.ASSERT)
|
||||||
}
|
}
|
||||||
|
return c.CreateTxFromScript(w.Bytes(), acc, gas)
|
||||||
|
}
|
||||||
|
|
||||||
script := w.Bytes()
|
// CreateTxFromScript creates transaction and properly sets cosigners and NetworkFee.
|
||||||
|
func (c *Client) CreateTxFromScript(script []byte, acc *wallet.Account, gas int64) (*transaction.Transaction, error) {
|
||||||
|
from, err := address.StringToUint160(acc.Address)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("bad account address: %v", err)
|
||||||
|
}
|
||||||
result, err := c.InvokeScript(script, []transaction.Signer{
|
result, err := c.InvokeScript(script, []transaction.Signer{
|
||||||
{
|
{
|
||||||
Account: from,
|
Account: from,
|
||||||
|
|
|
@ -1125,7 +1125,6 @@ func checkNep5Transfers(t *testing.T, e *executor, acc interface{}) {
|
||||||
TxHash: b.Hash(),
|
TxHash: b.Hash(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
require.Equal(t, expected.Address, res.Address)
|
require.Equal(t, expected.Address, res.Address)
|
||||||
require.ElementsMatch(t, expected.Sent, res.Sent)
|
require.ElementsMatch(t, expected.Sent, res.Sent)
|
||||||
|
|
|
@ -79,9 +79,12 @@ func Array(w *io.BinWriter, es ...interface{}) {
|
||||||
case bool:
|
case bool:
|
||||||
Bool(w, e)
|
Bool(w, e)
|
||||||
default:
|
default:
|
||||||
|
if es[i] != nil {
|
||||||
w.Err = errors.New("unsupported type")
|
w.Err = errors.New("unsupported type")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Opcode(w, opcode.PUSHNULL)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Int(w, int64(len(es)))
|
Int(w, int64(len(es)))
|
||||||
Opcode(w, opcode.PACK)
|
Opcode(w, opcode.PACK)
|
||||||
|
|
|
@ -142,7 +142,7 @@ func TestBytes(t *testing.T) {
|
||||||
func TestEmitArray(t *testing.T) {
|
func TestEmitArray(t *testing.T) {
|
||||||
t.Run("good", func(t *testing.T) {
|
t.Run("good", func(t *testing.T) {
|
||||||
buf := io.NewBufBinWriter()
|
buf := io.NewBufBinWriter()
|
||||||
Array(buf.BinWriter, int64(1), "str", true, []byte{0xCA, 0xFE})
|
Array(buf.BinWriter, nil, int64(1), "str", true, []byte{0xCA, 0xFE})
|
||||||
require.NoError(t, buf.Err)
|
require.NoError(t, buf.Err)
|
||||||
|
|
||||||
res := buf.Bytes()
|
res := buf.Bytes()
|
||||||
|
@ -154,6 +154,7 @@ func TestEmitArray(t *testing.T) {
|
||||||
assert.EqualValues(t, 3, res[6])
|
assert.EqualValues(t, 3, res[6])
|
||||||
assert.EqualValues(t, []byte("str"), res[7:10])
|
assert.EqualValues(t, []byte("str"), res[7:10])
|
||||||
assert.EqualValues(t, opcode.PUSH1, res[10])
|
assert.EqualValues(t, opcode.PUSH1, res[10])
|
||||||
|
assert.EqualValues(t, opcode.PUSHNULL, res[11])
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("empty", func(t *testing.T) {
|
t.Run("empty", func(t *testing.T) {
|
||||||
|
|
Loading…
Reference in a new issue