forked from TrueCloudLab/neoneo-go
commit
6dc9023289
24 changed files with 323 additions and 125 deletions
|
@ -46,7 +46,7 @@ type (
|
|||
AddressVersion byte `yaml:"AddressVersion"`
|
||||
SecondsPerBlock int `yaml:"SecondsPerBlock"`
|
||||
LowPriorityThreshold float64 `yaml:"LowPriorityThreshold"`
|
||||
MaxTransactionsPerBlock int64 `yaml:"MaxTransactionsPerBlock"`
|
||||
MaxTransactionsPerBlock int `yaml:"MaxTransactionsPerBlock"`
|
||||
MemPoolSize int `yaml:"MemPoolSize"`
|
||||
StandbyValidators []string `yaml:"StandbyValidators"`
|
||||
SeedList []string `yaml:"SeedList"`
|
||||
|
@ -59,6 +59,13 @@ type (
|
|||
FreeGasLimit util.Fixed8 `yaml:"FreeGasLimit"`
|
||||
// SaveStorageBatch enables storage batch saving before every persist.
|
||||
SaveStorageBatch bool `yaml:"SaveStorageBatch"`
|
||||
// Maximum number of low priority transactions accepted into block.
|
||||
MaxFreeTransactionsPerBlock int `yaml:"MaxFreeTransactionsPerBlock"`
|
||||
// Maximum size of low priority transaction in bytes.
|
||||
MaxFreeTransactionSize int `yaml:"MaxFreeTransactionSize"`
|
||||
// FeePerExtraByte sets the expected per-byte fee for
|
||||
// transactions exceeding the MaxFreeTransactionSize.
|
||||
FeePerExtraByte float64 `yaml:"FeePerExtraByte"`
|
||||
}
|
||||
|
||||
// SystemFee fees related to system.
|
||||
|
|
|
@ -31,6 +31,10 @@ ProtocolConfiguration:
|
|||
VerifyBlocks: true
|
||||
VerifyTransactions: false
|
||||
FreeGasLimit: 10.0
|
||||
MaxTransactionsPerBlock: 500
|
||||
MaxFreeTransactionsPerBlock: 20
|
||||
MaxFreeTransactionSize: 1024
|
||||
FeePerExtraByte: 0.00001
|
||||
|
||||
ApplicationConfiguration:
|
||||
# LogPath could be set up in case you need stdout logs to some proper file.
|
||||
|
|
|
@ -31,6 +31,10 @@ ProtocolConfiguration:
|
|||
VerifyBlocks: true
|
||||
VerifyTransactions: false
|
||||
FreeGasLimit: 10.0
|
||||
MaxTransactionsPerBlock: 500
|
||||
MaxFreeTransactionsPerBlock: 20
|
||||
MaxFreeTransactionSize: 1024
|
||||
FeePerExtraByte: 0.00001
|
||||
|
||||
ApplicationConfiguration:
|
||||
# LogPath could be set up in case you need stdout logs to some proper file.
|
||||
|
|
2
go.mod
2
go.mod
|
@ -7,7 +7,7 @@ require (
|
|||
github.com/go-redis/redis v6.10.2+incompatible
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible
|
||||
github.com/mr-tron/base58 v1.1.2
|
||||
github.com/nspcc-dev/dbft v0.0.0-20200211143830-4deeb124d7f9
|
||||
github.com/nspcc-dev/dbft v0.0.0-20200218131838-be55fd41ea78
|
||||
github.com/nspcc-dev/rfc6979 v0.2.0
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/prometheus/client_golang v1.2.1
|
||||
|
|
4
go.sum
4
go.sum
|
@ -97,8 +97,8 @@ github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae h1:T5V1QANlNMKun0EP
|
|||
github.com/nspcc-dev/dbft v0.0.0-20191209120240-0d6b7568d9ae/go.mod h1:3FjXOoHmA51EGfb5GS/HOv7VdmngNRTssSeQ729dvGY=
|
||||
github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a h1:ajvxgEe9qY4vvoSmrADqdDx7hReodKTnT2IXN++qZG8=
|
||||
github.com/nspcc-dev/dbft v0.0.0-20200117124306-478e5cfbf03a/go.mod h1:/YFK+XOxxg0Bfm6P92lY5eDSLYfp06XOdL8KAVgXjVk=
|
||||
github.com/nspcc-dev/dbft v0.0.0-20200211143830-4deeb124d7f9 h1:P3uOj+M3DkUlwGEFHbmwRHUZdpCuFzfM30j6iBjrPbY=
|
||||
github.com/nspcc-dev/dbft v0.0.0-20200211143830-4deeb124d7f9/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ=
|
||||
github.com/nspcc-dev/dbft v0.0.0-20200218131838-be55fd41ea78 h1:cb8hWea3yyqbxzmnqqSSgqVCRoKNrFaS99Cqt0Qm7nQ=
|
||||
github.com/nspcc-dev/dbft v0.0.0-20200218131838-be55fd41ea78/go.mod h1:O0qtn62prQSqizzoagHmuuKoz8QMkU3SzBoKdEvm3aQ=
|
||||
github.com/nspcc-dev/neofs-crypto v0.2.0 h1:ftN+59WqxSWz/RCgXYOfhmltOOqU+udsNQSvN6wkFck=
|
||||
github.com/nspcc-dev/neofs-crypto v0.2.0/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA=
|
||||
github.com/nspcc-dev/neofs-crypto v0.2.3 h1:aca3X2aly92ENRbFK+kH6Hd+J9EQ4Eu6XMVoITSIKtc=
|
||||
|
|
|
@ -9,8 +9,8 @@ import (
|
|||
"github.com/CityOfZion/neo-go/config"
|
||||
"github.com/CityOfZion/neo-go/pkg/core"
|
||||
coreb "github.com/CityOfZion/neo-go/pkg/core/block"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/mempool"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
||||
"github.com/CityOfZion/neo-go/pkg/smartcontract"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
|
@ -42,6 +42,9 @@ type Service interface {
|
|||
OnTransaction(tx *transaction.Transaction)
|
||||
// GetPayload returns Payload with specified hash if it is present in the local cache.
|
||||
GetPayload(h util.Uint256) *Payload
|
||||
// OnNewBlock notifies consensus service that there is a new block in
|
||||
// the chain (without explicitly passing it to the service).
|
||||
OnNewBlock()
|
||||
}
|
||||
|
||||
type service struct {
|
||||
|
@ -57,6 +60,9 @@ type service struct {
|
|||
// everything in single thread.
|
||||
messages chan Payload
|
||||
transactions chan *transaction.Transaction
|
||||
// blockEvents is used to pass a new block event to the consensus
|
||||
// process.
|
||||
blockEvents chan struct{}
|
||||
lastProposal []util.Uint256
|
||||
wallet *wallet.Wallet
|
||||
}
|
||||
|
@ -101,6 +107,7 @@ func NewService(cfg Config) (Service, error) {
|
|||
messages: make(chan Payload, 100),
|
||||
|
||||
transactions: make(chan *transaction.Transaction, 100),
|
||||
blockEvents: make(chan struct{}, 1),
|
||||
}
|
||||
|
||||
if cfg.Wallet == nil {
|
||||
|
@ -168,14 +175,7 @@ func (s *service) eventLoop() {
|
|||
s.log.Debug("timer fired",
|
||||
zap.Uint32("height", hv.Height),
|
||||
zap.Uint("view", uint(hv.View)))
|
||||
if s.Chain.BlockHeight() >= s.dbft.BlockIndex {
|
||||
s.log.Debug("chain already advanced",
|
||||
zap.Uint32("dbft index", s.dbft.BlockIndex),
|
||||
zap.Uint32("chain index", s.Chain.BlockHeight()))
|
||||
s.dbft.InitializeConsensus(0)
|
||||
} else {
|
||||
s.dbft.OnTimeout(hv)
|
||||
}
|
||||
s.dbft.OnTimeout(hv)
|
||||
case msg := <-s.messages:
|
||||
fields := []zap.Field{
|
||||
zap.Uint16("from", msg.validatorIndex),
|
||||
|
@ -204,6 +204,11 @@ func (s *service) eventLoop() {
|
|||
s.dbft.OnReceive(&msg)
|
||||
case tx := <-s.transactions:
|
||||
s.dbft.OnTransaction(tx)
|
||||
case <-s.blockEvents:
|
||||
s.log.Debug("new block in the chain",
|
||||
zap.Uint32("dbft index", s.dbft.BlockIndex),
|
||||
zap.Uint32("chain index", s.Chain.BlockHeight()))
|
||||
s.dbft.InitializeConsensus(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,16 +220,15 @@ func (s *service) validatePayload(p *Payload) bool {
|
|||
}
|
||||
|
||||
pub := validators[p.validatorIndex]
|
||||
vs := pub.(*publicKey).GetVerificationScript()
|
||||
h := hash.Hash160(vs)
|
||||
h := pub.(*publicKey).GetScriptHash()
|
||||
|
||||
return p.Verify(h)
|
||||
}
|
||||
|
||||
func (s *service) getKeyPair(pubs []crypto.PublicKey) (int, crypto.PrivateKey, crypto.PublicKey) {
|
||||
for i := range pubs {
|
||||
script := pubs[i].(*publicKey).GetVerificationScript()
|
||||
acc := s.wallet.GetAccount(hash.Hash160(script))
|
||||
sh := pubs[i].(*publicKey).GetScriptHash()
|
||||
acc := s.wallet.GetAccount(sh)
|
||||
if acc == nil {
|
||||
continue
|
||||
}
|
||||
|
@ -276,6 +280,20 @@ func (s *service) OnTransaction(tx *transaction.Transaction) {
|
|||
}
|
||||
}
|
||||
|
||||
// OnNewBlock notifies consensus process that there is a new block in the chain
|
||||
// and dbft should probably be reinitialized.
|
||||
func (s *service) OnNewBlock() {
|
||||
if s.dbft != nil {
|
||||
// If there is something in the queue already, the second
|
||||
// consecutive event doesn't make much sense (reinitializing
|
||||
// dbft twice doesn't improve it in any way).
|
||||
select {
|
||||
case s.blockEvents <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetPayload returns payload stored in cache.
|
||||
func (s *service) GetPayload(h util.Uint256) *Payload {
|
||||
p := s.cache.Get(h)
|
||||
|
@ -393,13 +411,13 @@ func (s *service) getBlock(h util.Uint256) block.Block {
|
|||
func (s *service) getVerifiedTx(count int) []block.Transaction {
|
||||
pool := s.Config.Chain.GetMemPool()
|
||||
|
||||
var txx []*transaction.Transaction
|
||||
var txx []mempool.TxWithFee
|
||||
|
||||
if s.dbft.ViewNumber > 0 {
|
||||
txx = make([]*transaction.Transaction, 0, len(s.lastProposal))
|
||||
txx = make([]mempool.TxWithFee, 0, len(s.lastProposal))
|
||||
for i := range s.lastProposal {
|
||||
if tx, ok := pool.TryGetValue(s.lastProposal[i]); ok {
|
||||
txx = append(txx, tx)
|
||||
if tx, fee, ok := pool.TryGetValue(s.lastProposal[i]); ok {
|
||||
txx = append(txx, mempool.TxWithFee{Tx: tx, Fee: fee})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -410,11 +428,30 @@ func (s *service) getVerifiedTx(count int) []block.Transaction {
|
|||
txx = pool.GetVerifiedTransactions()
|
||||
}
|
||||
|
||||
res := make([]block.Transaction, len(txx)+1)
|
||||
for i := range txx {
|
||||
res[i+1] = txx[i]
|
||||
if len(txx) > 0 {
|
||||
txx = s.Config.Chain.ApplyPolicyToTxSet(txx)
|
||||
}
|
||||
|
||||
res := make([]block.Transaction, len(txx)+1)
|
||||
var netFee util.Fixed8
|
||||
for i := range txx {
|
||||
res[i+1] = txx[i].Tx
|
||||
netFee += txx[i].Fee
|
||||
}
|
||||
|
||||
var txOuts []transaction.Output
|
||||
if netFee != 0 {
|
||||
sh := s.wallet.GetChangeAddress()
|
||||
if sh.Equals(util.Uint160{}) {
|
||||
pk := s.dbft.Pub.(*publicKey)
|
||||
sh = pk.GetScriptHash()
|
||||
}
|
||||
txOuts = []transaction.Output{transaction.Output{
|
||||
AssetID: core.UtilityTokenID(),
|
||||
Amount: netFee,
|
||||
ScriptHash: sh,
|
||||
}}
|
||||
}
|
||||
for {
|
||||
nonce := rand.Uint32()
|
||||
res[0] = &transaction.Transaction{
|
||||
|
@ -423,7 +460,7 @@ func (s *service) getVerifiedTx(count int) []block.Transaction {
|
|||
Data: &transaction.MinerTX{Nonce: nonce},
|
||||
Attributes: nil,
|
||||
Inputs: nil,
|
||||
Outputs: nil,
|
||||
Outputs: txOuts,
|
||||
Scripts: nil,
|
||||
Trimmed: false,
|
||||
}
|
||||
|
|
|
@ -239,6 +239,6 @@ func newTestChain(t *testing.T) *core.Blockchain {
|
|||
type feer struct{}
|
||||
|
||||
func (fs *feer) NetworkFee(*transaction.Transaction) util.Fixed8 { return util.Fixed8(0) }
|
||||
func (fs *feer) IsLowPriority(*transaction.Transaction) bool { return false }
|
||||
func (fs *feer) IsLowPriority(util.Fixed8) bool { return false }
|
||||
func (fs *feer) FeePerByte(*transaction.Transaction) util.Fixed8 { return util.Fixed8(0) }
|
||||
func (fs *feer) SystemFee(*transaction.Transaction) util.Fixed8 { return util.Fixed8(0) }
|
||||
|
|
|
@ -49,6 +49,9 @@ var (
|
|||
// ErrOOM is returned when adding transaction to the memory pool because
|
||||
// it reached its full capacity.
|
||||
ErrOOM = errors.New("no space left in the memory pool")
|
||||
// ErrPolicy is returned on attempt to add transaction that doesn't
|
||||
// comply with node's configured policy into the mempool.
|
||||
ErrPolicy = errors.New("not allowed by policy")
|
||||
)
|
||||
var (
|
||||
genAmount = []int{8, 7, 6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
|
||||
|
@ -125,6 +128,22 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration, log *zap.L
|
|||
cfg.MemPoolSize = defaultMemPoolSize
|
||||
log.Info("mempool size is not set or wrong, setting default value", zap.Int("MemPoolSize", cfg.MemPoolSize))
|
||||
}
|
||||
if cfg.MaxTransactionsPerBlock <= 0 {
|
||||
cfg.MaxTransactionsPerBlock = 0
|
||||
log.Info("MaxTransactionsPerBlock is not set or wrong, setting default value (unlimited)", zap.Int("MaxTransactionsPerBlock", cfg.MaxTransactionsPerBlock))
|
||||
}
|
||||
if cfg.MaxFreeTransactionsPerBlock <= 0 {
|
||||
cfg.MaxFreeTransactionsPerBlock = 0
|
||||
log.Info("MaxFreeTransactionsPerBlock is not set or wrong, setting default value (unlimited)", zap.Int("MaxFreeTransactionsPerBlock", cfg.MaxFreeTransactionsPerBlock))
|
||||
}
|
||||
if cfg.MaxFreeTransactionSize <= 0 {
|
||||
cfg.MaxFreeTransactionSize = 0
|
||||
log.Info("MaxFreeTransactionSize is not set or wrong, setting default value (unlimited)", zap.Int("MaxFreeTransactionSize", cfg.MaxFreeTransactionSize))
|
||||
}
|
||||
if cfg.FeePerExtraByte <= 0 {
|
||||
cfg.FeePerExtraByte = 0
|
||||
log.Info("FeePerExtraByte is not set or wrong, setting default value", zap.Float64("FeePerExtraByte", cfg.FeePerExtraByte))
|
||||
}
|
||||
bc := &Blockchain{
|
||||
config: cfg,
|
||||
dao: newDao(s),
|
||||
|
@ -432,7 +451,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if prevTXOutput.AssetID.Equals(governingTokenTX().Hash()) {
|
||||
if prevTXOutput.AssetID.Equals(GoverningTokenID()) {
|
||||
spentCoin := NewSpentCoinState(input.PrevHash, prevTXHeight)
|
||||
spentCoin.items[input.PrevIndex] = block.Index
|
||||
if err = cache.PutSpentCoinState(input.PrevHash, spentCoin); err != nil {
|
||||
|
@ -648,14 +667,14 @@ func processOutputs(tx *transaction.Transaction, dao *cachedDao) error {
|
|||
}
|
||||
|
||||
func processTXWithValidatorsAdd(output *transaction.Output, account *state.Account, dao *cachedDao) error {
|
||||
if output.AssetID.Equals(governingTokenTX().Hash()) && len(account.Votes) > 0 {
|
||||
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 *cachedDao) error {
|
||||
if output.AssetID.Equals(governingTokenTX().Hash()) && len(account.Votes) > 0 {
|
||||
if output.AssetID.Equals(GoverningTokenID()) && len(account.Votes) > 0 {
|
||||
return modAccountVotes(account, dao, -output.Amount)
|
||||
}
|
||||
return nil
|
||||
|
@ -724,7 +743,7 @@ func processAccountStateDescriptor(descriptor *transaction.StateDescriptor, dao
|
|||
}
|
||||
|
||||
if descriptor.Field == "Votes" {
|
||||
balance := account.GetBalanceValues()[governingTokenTX().Hash()]
|
||||
balance := account.GetBalanceValues()[GoverningTokenID()]
|
||||
if err = modAccountVotes(account, dao, -balance); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -814,7 +833,7 @@ func (bc *Blockchain) headerListLen() (n int) {
|
|||
|
||||
// GetTransaction returns a TX and its height by the given hash.
|
||||
func (bc *Blockchain) GetTransaction(hash util.Uint256) (*transaction.Transaction, uint32, error) {
|
||||
if tx, ok := bc.memPool.TryGetValue(hash); ok {
|
||||
if tx, _, ok := bc.memPool.TryGetValue(hash); ok {
|
||||
return tx, 0, nil // the height is not actually defined for memPool transaction. Not sure if zero is a good number in this case.
|
||||
}
|
||||
return bc.dao.GetTransaction(hash)
|
||||
|
@ -999,14 +1018,14 @@ func (bc *Blockchain) FeePerByte(t *transaction.Transaction) util.Fixed8 {
|
|||
func (bc *Blockchain) NetworkFee(t *transaction.Transaction) util.Fixed8 {
|
||||
inputAmount := util.Fixed8FromInt64(0)
|
||||
for _, txOutput := range bc.References(t) {
|
||||
if txOutput.AssetID == utilityTokenTX().Hash() {
|
||||
if txOutput.AssetID == UtilityTokenID() {
|
||||
inputAmount.Add(txOutput.Amount)
|
||||
}
|
||||
}
|
||||
|
||||
outputAmount := util.Fixed8FromInt64(0)
|
||||
for _, txOutput := range t.Outputs {
|
||||
if txOutput.AssetID == utilityTokenTX().Hash() {
|
||||
if txOutput.AssetID == UtilityTokenID() {
|
||||
outputAmount.Add(txOutput.Amount)
|
||||
}
|
||||
}
|
||||
|
@ -1019,10 +1038,10 @@ func (bc *Blockchain) SystemFee(t *transaction.Transaction) util.Fixed8 {
|
|||
return bc.GetConfig().SystemFee.TryGetValue(t.Type)
|
||||
}
|
||||
|
||||
// IsLowPriority flags a transaction as low priority if the network fee is less than
|
||||
// IsLowPriority checks given fee for being less than configured
|
||||
// LowPriorityThreshold.
|
||||
func (bc *Blockchain) IsLowPriority(t *transaction.Transaction) bool {
|
||||
return bc.NetworkFee(t) < util.Fixed8FromFloat(bc.GetConfig().LowPriorityThreshold)
|
||||
func (bc *Blockchain) IsLowPriority(fee util.Fixed8) bool {
|
||||
return fee < util.Fixed8FromFloat(bc.GetConfig().LowPriorityThreshold)
|
||||
}
|
||||
|
||||
// GetMemPool returns the memory pool of the blockchain.
|
||||
|
@ -1030,6 +1049,24 @@ func (bc *Blockchain) GetMemPool() *mempool.Pool {
|
|||
return &bc.memPool
|
||||
}
|
||||
|
||||
// ApplyPolicyToTxSet applies configured policies to given transaction set. It
|
||||
// expects slice to be ordered by fee and returns a subslice of it.
|
||||
func (bc *Blockchain) ApplyPolicyToTxSet(txes []mempool.TxWithFee) []mempool.TxWithFee {
|
||||
if bc.config.MaxTransactionsPerBlock != 0 && len(txes) > bc.config.MaxTransactionsPerBlock {
|
||||
txes = txes[:bc.config.MaxTransactionsPerBlock]
|
||||
}
|
||||
maxFree := bc.config.MaxFreeTransactionsPerBlock
|
||||
if maxFree != 0 {
|
||||
lowStart := sort.Search(len(txes), func(i int) bool {
|
||||
return bc.IsLowPriority(txes[i].Fee)
|
||||
})
|
||||
if lowStart+maxFree < len(txes) {
|
||||
txes = txes[:lowStart+maxFree]
|
||||
}
|
||||
}
|
||||
return txes
|
||||
}
|
||||
|
||||
// VerifyBlock verifies block against its current state.
|
||||
func (bc *Blockchain) VerifyBlock(block *block.Block) error {
|
||||
prevHeader, err := bc.GetHeader(block.PrevHash)
|
||||
|
@ -1127,6 +1164,18 @@ func (bc *Blockchain) PoolTx(t *transaction.Transaction) error {
|
|||
if err := bc.verifyTx(t, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
// Policying.
|
||||
if t.Type != transaction.ClaimType {
|
||||
txSize := io.GetVarSize(t)
|
||||
maxFree := bc.config.MaxFreeTransactionSize
|
||||
if maxFree != 0 && txSize > maxFree {
|
||||
netFee := bc.NetworkFee(t)
|
||||
if bc.IsLowPriority(netFee) ||
|
||||
netFee < util.Fixed8FromFloat(bc.config.FeePerExtraByte)*util.Fixed8(txSize-maxFree) {
|
||||
return ErrPolicy
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := bc.memPool.Add(t, bc); err != nil {
|
||||
switch err {
|
||||
case mempool.ErrOOM:
|
||||
|
@ -1192,7 +1241,7 @@ func (bc *Blockchain) verifyResults(t *transaction.Transaction) error {
|
|||
if len(resultsDestroy) > 1 {
|
||||
return errors.New("tx has more than 1 destroy output")
|
||||
}
|
||||
if len(resultsDestroy) == 1 && resultsDestroy[0].AssetID != utilityTokenTX().Hash() {
|
||||
if len(resultsDestroy) == 1 && resultsDestroy[0].AssetID != UtilityTokenID() {
|
||||
return errors.New("tx destroys non-utility token")
|
||||
}
|
||||
sysfee := bc.SystemFee(t)
|
||||
|
@ -1208,14 +1257,14 @@ func (bc *Blockchain) verifyResults(t *transaction.Transaction) error {
|
|||
switch t.Type {
|
||||
case transaction.MinerType, transaction.ClaimType:
|
||||
for _, r := range resultsIssue {
|
||||
if r.AssetID != utilityTokenTX().Hash() {
|
||||
if r.AssetID != UtilityTokenID() {
|
||||
return errors.New("miner or claim tx issues non-utility tokens")
|
||||
}
|
||||
}
|
||||
break
|
||||
case transaction.IssueType:
|
||||
for _, r := range resultsIssue {
|
||||
if r.AssetID == utilityTokenTX().Hash() {
|
||||
if r.AssetID == UtilityTokenID() {
|
||||
return errors.New("issue tx issues utility tokens")
|
||||
}
|
||||
}
|
||||
|
@ -1409,20 +1458,20 @@ func (bc *Blockchain) GetValidators(txes ...*transaction.Transaction) ([]*keys.P
|
|||
}
|
||||
|
||||
uniqueSBValidators := standByValidators.Unique()
|
||||
pubKeys := keys.PublicKeys{}
|
||||
result := keys.PublicKeys{}
|
||||
for _, validator := range validators {
|
||||
if validator.RegisteredAndHasVotes() || uniqueSBValidators.Contains(validator.PublicKey) {
|
||||
pubKeys = append(pubKeys, validator.PublicKey)
|
||||
result = append(result, validator.PublicKey)
|
||||
}
|
||||
}
|
||||
if pubKeys.Len() >= count {
|
||||
return pubKeys[:count], nil
|
||||
}
|
||||
|
||||
result := pubKeys.Unique()
|
||||
for i := 0; i < uniqueSBValidators.Len() && result.Len() < count; i++ {
|
||||
if !result.Contains(uniqueSBValidators[i]) {
|
||||
result = append(result, uniqueSBValidators[i])
|
||||
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)
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
// Blockchainer is an interface that abstract the implementation
|
||||
// of the blockchain.
|
||||
type Blockchainer interface {
|
||||
ApplyPolicyToTxSet([]mempool.TxWithFee) []mempool.TxWithFee
|
||||
GetConfig() config.ProtocolConfiguration
|
||||
AddHeaders(...*block.Header) error
|
||||
AddBlock(*block.Block) error
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"github.com/CityOfZion/neo-go/pkg/core/block"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/state"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
||||
"github.com/CityOfZion/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
|
@ -311,7 +310,7 @@ func (ic *interopContext) checkHashedWitness(hash util.Uint160) (bool, error) {
|
|||
// checkKeyedWitness checks hash of signature check contract with a given public
|
||||
// key against current list of script hashes for verifying in the interop context.
|
||||
func (ic *interopContext) checkKeyedWitness(key *keys.PublicKey) (bool, error) {
|
||||
return ic.checkHashedWitness(hash.Hash160(key.GetVerificationScript()))
|
||||
return ic.checkHashedWitness(key.GetScriptHash())
|
||||
}
|
||||
|
||||
// runtimeCheckWitness checks witnesses.
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
// Feer is an interface that abstract the implementation of the fee calculation.
|
||||
type Feer interface {
|
||||
NetworkFee(t *transaction.Transaction) util.Fixed8
|
||||
IsLowPriority(t *transaction.Transaction) bool
|
||||
IsLowPriority(util.Fixed8) bool
|
||||
FeePerByte(t *transaction.Transaction) util.Fixed8
|
||||
SystemFee(t *transaction.Transaction) util.Fixed8
|
||||
}
|
||||
|
|
|
@ -35,6 +35,12 @@ type item struct {
|
|||
// items is a slice of item.
|
||||
type items []*item
|
||||
|
||||
// TxWithFee combines transaction and its precalculated network fee.
|
||||
type TxWithFee struct {
|
||||
Tx *transaction.Transaction
|
||||
Fee util.Fixed8
|
||||
}
|
||||
|
||||
// Pool stores the unconfirms transactions.
|
||||
type Pool struct {
|
||||
lock sync.RWMutex
|
||||
|
@ -128,8 +134,8 @@ func (mp *Pool) Add(t *transaction.Transaction, fee Feer) error {
|
|||
timeStamp: time.Now().UTC(),
|
||||
perByteFee: fee.FeePerByte(t),
|
||||
netFee: fee.NetworkFee(t),
|
||||
isLowPrio: fee.IsLowPriority(t),
|
||||
}
|
||||
pItem.isLowPrio = fee.IsLowPriority(pItem.netFee)
|
||||
mp.lock.Lock()
|
||||
if !mp.verifyInputs(t) {
|
||||
mp.lock.Unlock()
|
||||
|
@ -224,29 +230,28 @@ func NewMemPool(capacity int) Pool {
|
|||
}
|
||||
}
|
||||
|
||||
// TryGetValue returns a transaction if it exists in the memory pool.
|
||||
func (mp *Pool) TryGetValue(hash util.Uint256) (*transaction.Transaction, bool) {
|
||||
// TryGetValue returns a transaction and its fee if it exists in the memory pool.
|
||||
func (mp *Pool) TryGetValue(hash util.Uint256) (*transaction.Transaction, util.Fixed8, bool) {
|
||||
mp.lock.RLock()
|
||||
defer mp.lock.RUnlock()
|
||||
if pItem, ok := mp.verifiedMap[hash]; ok {
|
||||
return pItem.txn, ok
|
||||
return pItem.txn, pItem.netFee, ok
|
||||
}
|
||||
|
||||
return nil, false
|
||||
return nil, 0, false
|
||||
}
|
||||
|
||||
// GetVerifiedTransactions returns a slice of Input from all the transactions in the memory pool
|
||||
// whose hash is not included in excludedHashes.
|
||||
func (mp *Pool) GetVerifiedTransactions() []*transaction.Transaction {
|
||||
func (mp *Pool) GetVerifiedTransactions() []TxWithFee {
|
||||
mp.lock.RLock()
|
||||
defer mp.lock.RUnlock()
|
||||
|
||||
var t = make([]*transaction.Transaction, len(mp.verifiedTxes))
|
||||
var i int
|
||||
var t = make([]TxWithFee, len(mp.verifiedTxes))
|
||||
|
||||
for _, p := range mp.verifiedTxes {
|
||||
t[i] = p.txn
|
||||
i++
|
||||
for i := range mp.verifiedTxes {
|
||||
t[i].Tx = mp.verifiedTxes[i].txn
|
||||
t[i].Fee = mp.verifiedTxes[i].netFee
|
||||
}
|
||||
|
||||
return t
|
||||
|
|
|
@ -22,7 +22,7 @@ func (fs *FeerStub) NetworkFee(*transaction.Transaction) util.Fixed8 {
|
|||
return fs.netFee
|
||||
}
|
||||
|
||||
func (fs *FeerStub) IsLowPriority(*transaction.Transaction) bool {
|
||||
func (fs *FeerStub) IsLowPriority(util.Fixed8) bool {
|
||||
return fs.lowPriority
|
||||
}
|
||||
|
||||
|
@ -37,16 +37,16 @@ func (fs *FeerStub) SystemFee(*transaction.Transaction) util.Fixed8 {
|
|||
func testMemPoolAddRemoveWithFeer(t *testing.T, fs Feer) {
|
||||
mp := NewMemPool(10)
|
||||
tx := newMinerTX(0)
|
||||
_, ok := mp.TryGetValue(tx.Hash())
|
||||
_, _, ok := mp.TryGetValue(tx.Hash())
|
||||
require.Equal(t, false, ok)
|
||||
require.NoError(t, mp.Add(tx, fs))
|
||||
// Re-adding should fail.
|
||||
require.Error(t, mp.Add(tx, fs))
|
||||
tx2, ok := mp.TryGetValue(tx.Hash())
|
||||
tx2, _, ok := mp.TryGetValue(tx.Hash())
|
||||
require.Equal(t, true, ok)
|
||||
require.Equal(t, tx, tx2)
|
||||
mp.Remove(tx.Hash())
|
||||
_, ok = mp.TryGetValue(tx.Hash())
|
||||
_, _, ok = mp.TryGetValue(tx.Hash())
|
||||
require.Equal(t, false, ok)
|
||||
// Make sure nothing left in the mempool after removal.
|
||||
assert.Equal(t, 0, len(mp.verifiedMap))
|
||||
|
@ -173,8 +173,8 @@ func TestGetVerified(t *testing.T) {
|
|||
require.Equal(t, mempoolSize, mp.Count())
|
||||
verTxes := mp.GetVerifiedTransactions()
|
||||
require.Equal(t, mempoolSize, len(verTxes))
|
||||
for _, tx := range verTxes {
|
||||
require.Contains(t, txes, tx)
|
||||
for _, txf := range verTxes {
|
||||
require.Contains(t, txes, txf.Tx)
|
||||
}
|
||||
for _, tx := range txes {
|
||||
mp.Remove(tx.Hash())
|
||||
|
@ -210,8 +210,8 @@ func TestRemoveStale(t *testing.T) {
|
|||
})
|
||||
require.Equal(t, mempoolSize/2, mp.Count())
|
||||
verTxes := mp.GetVerifiedTransactions()
|
||||
for _, tx := range verTxes {
|
||||
require.NotContains(t, txes1, tx)
|
||||
require.Contains(t, txes2, tx)
|
||||
for _, txf := range verTxes {
|
||||
require.NotContains(t, txes1, txf.Tx)
|
||||
require.Contains(t, txes2, txf.Tx)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,17 @@ import (
|
|||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
||||
)
|
||||
|
||||
var (
|
||||
// governingTokenTX represents transaction that is used to create
|
||||
// governing (NEO) token. It's a part of the genesis block.
|
||||
governingTokenTX transaction.Transaction
|
||||
|
||||
// utilityTokenTX represents transaction that is used to create
|
||||
// utility (GAS) token. It's a part of the genesis block. It's mostly
|
||||
// useful for its hash that represents GAS asset ID.
|
||||
utilityTokenTX transaction.Transaction
|
||||
)
|
||||
|
||||
// createGenesisBlock creates a genesis block based on the given configuration.
|
||||
func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error) {
|
||||
validators, err := getValidators(cfg)
|
||||
|
@ -38,8 +49,6 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error)
|
|||
},
|
||||
}
|
||||
|
||||
governingTX := governingTokenTX()
|
||||
utilityTX := utilityTokenTX()
|
||||
rawScript, err := smartcontract.CreateMultiSigRedeemScript(
|
||||
len(cfg.StandbyValidators)/2+1,
|
||||
validators,
|
||||
|
@ -62,16 +71,16 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error)
|
|||
Outputs: []transaction.Output{},
|
||||
Scripts: []transaction.Witness{},
|
||||
},
|
||||
governingTX,
|
||||
utilityTX,
|
||||
&governingTokenTX,
|
||||
&utilityTokenTX,
|
||||
{
|
||||
Type: transaction.IssueType,
|
||||
Data: &transaction.IssueTX{}, // no fields.
|
||||
Inputs: []transaction.Input{},
|
||||
Outputs: []transaction.Output{
|
||||
{
|
||||
AssetID: governingTX.Hash(),
|
||||
Amount: governingTX.Data.(*transaction.RegisterTX).Amount,
|
||||
AssetID: governingTokenTX.Hash(),
|
||||
Amount: governingTokenTX.Data.(*transaction.RegisterTX).Amount,
|
||||
ScriptHash: scriptOut,
|
||||
},
|
||||
},
|
||||
|
@ -92,7 +101,7 @@ func createGenesisBlock(cfg config.ProtocolConfiguration) (*block.Block, error)
|
|||
return b, nil
|
||||
}
|
||||
|
||||
func governingTokenTX() *transaction.Transaction {
|
||||
func init() {
|
||||
admin := hash.Hash160([]byte{byte(opcode.PUSHT)})
|
||||
registerTX := &transaction.RegisterTX{
|
||||
AssetType: transaction.GoverningToken,
|
||||
|
@ -102,7 +111,7 @@ func governingTokenTX() *transaction.Transaction {
|
|||
Admin: admin,
|
||||
}
|
||||
|
||||
tx := &transaction.Transaction{
|
||||
governingTokenTX = transaction.Transaction{
|
||||
Type: transaction.RegisterType,
|
||||
Data: registerTX,
|
||||
Attributes: []transaction.Attribute{},
|
||||
|
@ -111,19 +120,15 @@ func governingTokenTX() *transaction.Transaction {
|
|||
Scripts: []transaction.Witness{},
|
||||
}
|
||||
|
||||
return tx
|
||||
}
|
||||
|
||||
func utilityTokenTX() *transaction.Transaction {
|
||||
admin := hash.Hash160([]byte{byte(opcode.PUSHF)})
|
||||
registerTX := &transaction.RegisterTX{
|
||||
admin = hash.Hash160([]byte{byte(opcode.PUSHF)})
|
||||
registerTX = &transaction.RegisterTX{
|
||||
AssetType: transaction.UtilityToken,
|
||||
Name: "[{\"lang\":\"zh-CN\",\"name\":\"小蚁币\"},{\"lang\":\"en\",\"name\":\"AntCoin\"}]",
|
||||
Amount: calculateUtilityAmount(),
|
||||
Precision: 8,
|
||||
Admin: admin,
|
||||
}
|
||||
tx := &transaction.Transaction{
|
||||
utilityTokenTX = transaction.Transaction{
|
||||
Type: transaction.RegisterType,
|
||||
Data: registerTX,
|
||||
Attributes: []transaction.Attribute{},
|
||||
|
@ -131,8 +136,16 @@ func utilityTokenTX() *transaction.Transaction {
|
|||
Outputs: []transaction.Output{},
|
||||
Scripts: []transaction.Witness{},
|
||||
}
|
||||
}
|
||||
|
||||
return tx
|
||||
// GoverningTokenID returns the governing token (NEO) hash.
|
||||
func GoverningTokenID() util.Uint256 {
|
||||
return governingTokenTX.Hash()
|
||||
}
|
||||
|
||||
// UtilityTokenID returns the utility token (GAS) hash.
|
||||
func UtilityTokenID() util.Uint256 {
|
||||
return utilityTokenTX.Hash()
|
||||
}
|
||||
|
||||
func getValidators(cfg config.ProtocolConfiguration) ([]*keys.PublicKey, error) {
|
||||
|
|
|
@ -50,12 +50,10 @@ func TestGetConsensusAddressMainNet(t *testing.T) {
|
|||
|
||||
func TestUtilityTokenTX(t *testing.T) {
|
||||
expect := "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"
|
||||
tx := utilityTokenTX()
|
||||
assert.Equal(t, expect, tx.Hash().StringLE())
|
||||
assert.Equal(t, expect, UtilityTokenID().StringLE())
|
||||
}
|
||||
|
||||
func TestGoverningTokenTX(t *testing.T) {
|
||||
expect := "c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b"
|
||||
tx := governingTokenTX()
|
||||
assert.Equal(t, expect, tx.Hash().StringLE())
|
||||
assert.Equal(t, expect, GoverningTokenID().StringLE())
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/rfc6979"
|
||||
)
|
||||
|
||||
|
@ -98,10 +99,11 @@ func (p *PrivateKey) Address() string {
|
|||
return pk.Address()
|
||||
}
|
||||
|
||||
// Signature creates the signature using the private key.
|
||||
func (p *PrivateKey) Signature() []byte {
|
||||
// GetScriptHash returns verification script hash for public key associated with
|
||||
// the private key.
|
||||
func (p *PrivateKey) GetScriptHash() util.Uint160 {
|
||||
pk := p.PublicKey()
|
||||
return pk.Signature()
|
||||
return pk.GetScriptHash()
|
||||
}
|
||||
|
||||
// Sign signs arbitrary length data using the private key.
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/CityOfZion/neo-go/pkg/crypto/hash"
|
||||
"github.com/CityOfZion/neo-go/pkg/encoding/address"
|
||||
"github.com/CityOfZion/neo-go/pkg/io"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/CityOfZion/neo-go/pkg/vm/opcode"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
@ -233,18 +234,14 @@ func (p *PublicKey) GetVerificationScript() []byte {
|
|||
return b
|
||||
}
|
||||
|
||||
// Signature returns a NEO-specific hash of the key.
|
||||
func (p *PublicKey) Signature() []byte {
|
||||
sig := hash.Hash160(p.GetVerificationScript())
|
||||
|
||||
return sig.BytesBE()
|
||||
// GetScriptHash returns a Hash160 of verification script for the key.
|
||||
func (p *PublicKey) GetScriptHash() util.Uint160 {
|
||||
return hash.Hash160(p.GetVerificationScript())
|
||||
}
|
||||
|
||||
// Address returns a base58-encoded NEO-specific address based on the key hash.
|
||||
func (p *PublicKey) Address() string {
|
||||
sig := hash.Hash160(p.GetVerificationScript())
|
||||
|
||||
return address.Uint160ToString(sig)
|
||||
return address.Uint160ToString(p.GetScriptHash())
|
||||
}
|
||||
|
||||
// Verify returns true if the signature is valid and corresponds
|
||||
|
|
|
@ -26,6 +26,9 @@ type testChain struct {
|
|||
blockheight uint32
|
||||
}
|
||||
|
||||
func (chain testChain) ApplyPolicyToTxSet([]mempool.TxWithFee) []mempool.TxWithFee {
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) GetConfig() config.ProtocolConfiguration {
|
||||
panic("TODO")
|
||||
}
|
||||
|
@ -122,7 +125,7 @@ func (chain testChain) GetMemPool() *mempool.Pool {
|
|||
panic("TODO")
|
||||
}
|
||||
|
||||
func (chain testChain) IsLowPriority(*transaction.Transaction) bool {
|
||||
func (chain testChain) IsLowPriority(util.Fixed8) bool {
|
||||
panic("TODO")
|
||||
}
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ type (
|
|||
unregister chan peerDrop
|
||||
quit chan struct{}
|
||||
|
||||
connected *atomic.Bool
|
||||
consensusStarted *atomic.Bool
|
||||
|
||||
log *zap.Logger
|
||||
}
|
||||
|
@ -88,17 +88,24 @@ func NewServer(config ServerConfig, chain core.Blockchainer, log *zap.Logger) (*
|
|||
}
|
||||
|
||||
s := &Server{
|
||||
ServerConfig: config,
|
||||
chain: chain,
|
||||
id: randomID(),
|
||||
quit: make(chan struct{}),
|
||||
register: make(chan Peer),
|
||||
unregister: make(chan peerDrop),
|
||||
peers: make(map[Peer]bool),
|
||||
connected: atomic.NewBool(false),
|
||||
log: log,
|
||||
ServerConfig: config,
|
||||
chain: chain,
|
||||
id: randomID(),
|
||||
quit: make(chan struct{}),
|
||||
register: make(chan Peer),
|
||||
unregister: make(chan peerDrop),
|
||||
peers: make(map[Peer]bool),
|
||||
consensusStarted: atomic.NewBool(false),
|
||||
log: log,
|
||||
}
|
||||
s.bQueue = newBlockQueue(maxBlockBatch, chain, log, s.relayBlock)
|
||||
s.bQueue = newBlockQueue(maxBlockBatch, chain, log, func(b *block.Block) {
|
||||
if s.consensusStarted.Load() {
|
||||
s.consensus.OnNewBlock()
|
||||
} else {
|
||||
s.tryStartConsensus()
|
||||
}
|
||||
s.relayBlock(b)
|
||||
})
|
||||
|
||||
srv, err := consensus.NewService(consensus.Config{
|
||||
Logger: log,
|
||||
|
@ -274,13 +281,13 @@ func (s *Server) runProto() {
|
|||
}
|
||||
|
||||
func (s *Server) tryStartConsensus() {
|
||||
if s.Wallet == nil || s.connected.Load() {
|
||||
if s.Wallet == nil || s.consensusStarted.Load() {
|
||||
return
|
||||
}
|
||||
|
||||
if s.HandshakedPeersCount() >= s.MinPeers {
|
||||
s.log.Info("minimum amount of peers were connected to")
|
||||
if s.connected.CAS(false, true) {
|
||||
if s.IsInSync() {
|
||||
s.log.Info("node reached synchronized state, starting consensus")
|
||||
if s.consensusStarted.CAS(false, true) {
|
||||
s.consensus.Start()
|
||||
}
|
||||
}
|
||||
|
@ -336,6 +343,39 @@ func (s *Server) getVersionMsg() *Message {
|
|||
return s.MkMsg(CMDVersion, payload)
|
||||
}
|
||||
|
||||
// IsInSync answers the question of whether the server is in sync with the
|
||||
// network or not (at least how the server itself sees it). The server operates
|
||||
// with the data that it has, the number of peers (that has to be more than
|
||||
// minimum number) and height of these peers (our chain has to be not lower
|
||||
// than 2/3 of our peers have). Ideally we would check for the highest of the
|
||||
// peers, but the problem is that they can lie to us and send whatever height
|
||||
// they want to.
|
||||
func (s *Server) IsInSync() bool {
|
||||
var peersNumber int
|
||||
var notHigher int
|
||||
|
||||
if s.MinPeers == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
ourLastBlock := s.chain.BlockHeight()
|
||||
|
||||
s.lock.RLock()
|
||||
for p := range s.peers {
|
||||
if p.Handshaked() {
|
||||
peersNumber++
|
||||
if ourLastBlock >= p.LastBlockIndex() {
|
||||
notHigher++
|
||||
}
|
||||
}
|
||||
}
|
||||
s.lock.RUnlock()
|
||||
|
||||
// Checking bQueue would also be nice, but it can be filled with garbage
|
||||
// easily at the moment.
|
||||
return peersNumber >= s.MinPeers && (3*notHigher > 2*peersNumber) // && s.bQueue.length() == 0
|
||||
}
|
||||
|
||||
// When a peer sends out his version we reply with verack after validating
|
||||
// the version.
|
||||
func (s *Server) handleVersionCmd(p Peer, version *payload.Version) error {
|
||||
|
@ -746,15 +786,14 @@ func (s *Server) verifyAndPoolTX(t *transaction.Transaction) RelayReason {
|
|||
if t.Type == transaction.MinerType {
|
||||
return RelayInvalid
|
||||
}
|
||||
// TODO: Implement Plugin.CheckPolicy?
|
||||
//if (!Plugin.CheckPolicy(transaction))
|
||||
// return RelayResultReason.PolicyFail;
|
||||
if err := s.chain.PoolTx(t); err != nil {
|
||||
switch err {
|
||||
case core.ErrAlreadyExists:
|
||||
return RelayAlreadyExists
|
||||
case core.ErrOOM:
|
||||
return RelayOutOfMemory
|
||||
case core.ErrPolicy:
|
||||
return RelayPolicyFail
|
||||
default:
|
||||
return RelayInvalid
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"encoding/hex"
|
||||
"fmt"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/core"
|
||||
"github.com/CityOfZion/neo-go/pkg/core/transaction"
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
||||
"github.com/CityOfZion/neo-go/pkg/smartcontract"
|
||||
|
@ -160,15 +161,12 @@ func (c *Client) SignAndPushInvocationTx(script []byte, wif *keys.WIF, gas util.
|
|||
var txHash util.Uint256
|
||||
var err error
|
||||
|
||||
gasIDB, _ := hex.DecodeString("602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7")
|
||||
gasID, _ := util.Uint256DecodeBytesLE(gasIDB)
|
||||
|
||||
tx := transaction.NewInvocationTX(script, gas)
|
||||
|
||||
fromAddress := wif.PrivateKey.Address()
|
||||
|
||||
if gas > 0 {
|
||||
if err = AddInputsAndUnspentsToTx(tx, fromAddress, gasID, gas, c); err != nil {
|
||||
if err = AddInputsAndUnspentsToTx(tx, fromAddress, core.UtilityTokenID(), gas, c); err != nil {
|
||||
return txHash, errors.Wrap(err, "failed to add inputs and unspents to transaction")
|
||||
}
|
||||
}
|
||||
|
|
1
pkg/wallet/testdata/wallet1.json
vendored
Normal file
1
pkg/wallet/testdata/wallet1.json
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"name":"wallet1","version":"1.0","scrypt":{"n":16384,"r":8,"p":8},"accounts":[{"address":"AKkkumHbBipZ46UMZJoFynJMXzSRnBvKcs","label":null,"isDefault":false,"lock":false,"key":"6PYLmjBYJ4wQTCEfqvnznGJwZeW9pfUcV5m5oreHxqryUgqKpTRAFt9L8Y","contract":{"script":"2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2ac","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"extra":null},{"address":"AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU","label":null,"isDefault":false,"lock":false,"key":"6PYLmjBYJ4wQTCEfqvnznGJwZeW9pfUcV5m5oreHxqryUgqKpTRAFt9L8Y","contract":{"script":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae","parameters":[{"name":"parameter0","type":"Signature"},{"name":"parameter1","type":"Signature"},{"name":"parameter2","type":"Signature"}],"deployed":false},"extra":null}],"extra":null}
|
1
pkg/wallet/testdata/wallet2.json
vendored
Normal file
1
pkg/wallet/testdata/wallet2.json
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"name":"wallet2","version":"1.0","scrypt":{"n":16384,"r":8,"p":8},"accounts":[{"address":"AKkkumHbBipZ46UMZJoFynJMXzSRnBvKcs","label":null,"isDefault":false,"lock":false,"key":"6PYLmjBYJ4wQTCEfqvnznGJwZeW9pfUcV5m5oreHxqryUgqKpTRAFt9L8Y","contract":{"script":"2102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2ac","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"extra":null},{"address":"AZ81H31DMWzbSnFDLFkzh9vHwaDLayV7fU","label":null,"isDefault":false,"lock":false,"key":"6PYXHjPaNvW8YknSXaKsTWjf9FRxo1s4naV2jdmSQEgzaqKGX368rndN3L","contract":{"script":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae","parameters":[{"name":"parameter0","type":"Signature"},{"name":"parameter1","type":"Signature"},{"name":"parameter2","type":"Signature"}],"deployed":false},"extra":null},{"address":"AWLYWXB8C9Lt1nHdDZJnC5cpYJjgRDLk17","label":null,"isDefault":true,"lock":false,"key":"6PYXHjPaNvW8YknSXaKsTWjf9FRxo1s4naV2jdmSQEgzaqKGX368rndN3L","contract":{"script":"2102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406eac","parameters":[{"name":"parameter0","type":"Signature"}],"deployed":false},"extra":null}],"extra":null}
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/CityOfZion/neo-go/pkg/crypto/keys"
|
||||
"github.com/CityOfZion/neo-go/pkg/util"
|
||||
"github.com/CityOfZion/neo-go/pkg/vm"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -129,3 +130,24 @@ func (w *Wallet) GetAccount(h util.Uint160) *Account {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetChangeAddress returns the default address to send transaction's change to.
|
||||
func (w *Wallet) GetChangeAddress() util.Uint160 {
|
||||
var res util.Uint160
|
||||
var acc *Account
|
||||
|
||||
for i := range w.Accounts {
|
||||
if acc == nil || w.Accounts[i].Default {
|
||||
if w.Accounts[i].Contract != nil && vm.IsSignatureContract(w.Accounts[i].Contract.Script) {
|
||||
acc = w.Accounts[i]
|
||||
if w.Accounts[i].Default {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if acc != nil {
|
||||
res = acc.Contract.ScriptHash()
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/CityOfZion/neo-go/pkg/encoding/address"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -145,3 +146,20 @@ func TestWallet_GetAccount(t *testing.T) {
|
|||
assert.Equal(t, acc, wallet.GetAccount(h), "can't get %d account", i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWalletGetChangeAddress(t *testing.T) {
|
||||
w1, err := NewWalletFromFile("testdata/wallet1.json")
|
||||
require.NoError(t, err)
|
||||
sh := w1.GetChangeAddress()
|
||||
// No default address, the first one is used.
|
||||
expected, err := address.StringToUint160("AKkkumHbBipZ46UMZJoFynJMXzSRnBvKcs")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, sh)
|
||||
w2, err := NewWalletFromFile("testdata/wallet2.json")
|
||||
require.NoError(t, err)
|
||||
sh = w2.GetChangeAddress()
|
||||
// Default address.
|
||||
expected, err = address.StringToUint160("AWLYWXB8C9Lt1nHdDZJnC5cpYJjgRDLk17")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, sh)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue