2019-11-29 12:40:21 +00:00
|
|
|
package consensus
|
|
|
|
|
|
|
|
import (
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/nspcc-dev/dbft/block"
|
2020-01-14 11:34:09 +00:00
|
|
|
"github.com/nspcc-dev/dbft/payload"
|
2020-03-25 15:30:21 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
2020-03-03 14:21:42 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
2020-03-25 15:30:21 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
2019-11-29 12:40:21 +00:00
|
|
|
"github.com/stretchr/testify/require"
|
2019-12-30 07:43:05 +00:00
|
|
|
"go.uber.org/zap/zaptest"
|
2019-11-29 12:40:21 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestNewService(t *testing.T) {
|
|
|
|
srv := newTestService(t)
|
2020-04-10 10:41:49 +00:00
|
|
|
tx := transaction.NewMinerTX()
|
2020-04-15 06:50:13 +00:00
|
|
|
tx.ValidUntilBlock = 1
|
2020-02-06 14:49:05 +00:00
|
|
|
require.NoError(t, srv.Chain.PoolTx(tx))
|
2019-11-29 12:40:21 +00:00
|
|
|
|
|
|
|
var txx []block.Transaction
|
|
|
|
require.NotPanics(t, func() { txx = srv.getVerifiedTx(1) })
|
|
|
|
require.Len(t, txx, 2)
|
|
|
|
require.Equal(t, tx, txx[1])
|
2020-01-20 16:40:52 +00:00
|
|
|
srv.Chain.Close()
|
2019-11-29 12:40:21 +00:00
|
|
|
}
|
|
|
|
|
2020-01-14 11:34:09 +00:00
|
|
|
func TestService_GetVerified(t *testing.T) {
|
|
|
|
srv := newTestService(t)
|
2020-04-15 06:50:13 +00:00
|
|
|
var txs []*transaction.Transaction
|
|
|
|
for i := 0; i < 4; i++ {
|
|
|
|
tx := transaction.NewMinerTXWithNonce(123 + uint32(i))
|
|
|
|
tx.ValidUntilBlock = 1
|
|
|
|
txs = append(txs, tx)
|
2020-01-14 11:34:09 +00:00
|
|
|
}
|
2020-02-06 14:49:05 +00:00
|
|
|
require.NoError(t, srv.Chain.PoolTx(txs[3]))
|
2020-01-14 11:34:09 +00:00
|
|
|
|
|
|
|
hashes := []util.Uint256{txs[0].Hash(), txs[1].Hash(), txs[2].Hash()}
|
|
|
|
|
|
|
|
p := new(Payload)
|
|
|
|
p.SetType(payload.PrepareRequestType)
|
2020-04-10 10:41:49 +00:00
|
|
|
p.SetPayload(&prepareRequest{transactionHashes: hashes, minerTx: *transaction.NewMinerTXWithNonce(999)})
|
2020-01-14 11:34:09 +00:00
|
|
|
p.SetValidatorIndex(1)
|
|
|
|
|
|
|
|
priv, _ := getTestValidator(1)
|
|
|
|
require.NoError(t, p.Sign(priv))
|
|
|
|
|
|
|
|
srv.OnPayload(p)
|
|
|
|
require.Equal(t, hashes, srv.lastProposal)
|
|
|
|
|
|
|
|
srv.dbft.ViewNumber = 1
|
|
|
|
|
|
|
|
t.Run("new transactions will be proposed in case of failure", func(t *testing.T) {
|
|
|
|
txx := srv.getVerifiedTx(10)
|
|
|
|
require.Equal(t, 2, len(txx), "there is only 1 tx in mempool")
|
|
|
|
require.Equal(t, txs[3], txx[1])
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("more than half of the last proposal will be reused", func(t *testing.T) {
|
|
|
|
for _, tx := range txs[:2] {
|
2020-02-06 14:49:05 +00:00
|
|
|
require.NoError(t, srv.Chain.PoolTx(tx))
|
2020-01-14 11:34:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
txx := srv.getVerifiedTx(10)
|
|
|
|
require.Contains(t, txx, txs[0])
|
|
|
|
require.Contains(t, txx, txs[1])
|
|
|
|
require.NotContains(t, txx, txs[2])
|
|
|
|
})
|
2020-01-20 16:40:52 +00:00
|
|
|
srv.Chain.Close()
|
2020-01-14 11:34:09 +00:00
|
|
|
}
|
|
|
|
|
2019-12-16 08:57:49 +00:00
|
|
|
func TestService_ValidatePayload(t *testing.T) {
|
|
|
|
srv := newTestService(t)
|
|
|
|
priv, _ := getTestValidator(1)
|
|
|
|
p := new(Payload)
|
|
|
|
|
|
|
|
p.SetPayload(&prepareRequest{})
|
|
|
|
|
|
|
|
t.Run("invalid validator index", func(t *testing.T) {
|
|
|
|
p.SetValidatorIndex(11)
|
|
|
|
require.NoError(t, p.Sign(priv))
|
|
|
|
|
|
|
|
var ok bool
|
|
|
|
require.NotPanics(t, func() { ok = srv.validatePayload(p) })
|
|
|
|
require.False(t, ok)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("wrong validator index", func(t *testing.T) {
|
|
|
|
p.SetValidatorIndex(2)
|
|
|
|
require.NoError(t, p.Sign(priv))
|
|
|
|
require.False(t, srv.validatePayload(p))
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("normal case", func(t *testing.T) {
|
|
|
|
p.SetValidatorIndex(1)
|
|
|
|
require.NoError(t, p.Sign(priv))
|
|
|
|
require.True(t, srv.validatePayload(p))
|
|
|
|
})
|
2020-01-20 16:40:52 +00:00
|
|
|
srv.Chain.Close()
|
2019-12-16 08:57:49 +00:00
|
|
|
}
|
|
|
|
|
2019-12-27 11:09:58 +00:00
|
|
|
func TestService_getTx(t *testing.T) {
|
|
|
|
srv := newTestService(t)
|
|
|
|
|
|
|
|
t.Run("transaction in mempool", func(t *testing.T) {
|
2020-04-10 10:41:49 +00:00
|
|
|
tx := transaction.NewMinerTXWithNonce(1234)
|
2020-04-15 06:50:13 +00:00
|
|
|
tx.ValidUntilBlock = 1
|
2019-12-27 11:09:58 +00:00
|
|
|
h := tx.Hash()
|
|
|
|
|
|
|
|
require.Equal(t, nil, srv.getTx(h))
|
|
|
|
|
2020-02-06 14:49:05 +00:00
|
|
|
require.NoError(t, srv.Chain.PoolTx(tx))
|
2019-12-27 11:09:58 +00:00
|
|
|
|
|
|
|
got := srv.getTx(h)
|
|
|
|
require.NotNil(t, got)
|
|
|
|
require.Equal(t, h, got.Hash())
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("transaction in local cache", func(t *testing.T) {
|
2020-04-10 10:41:49 +00:00
|
|
|
tx := transaction.NewMinerTXWithNonce(4321)
|
2020-04-15 06:50:13 +00:00
|
|
|
tx.ValidUntilBlock = 1
|
2019-12-27 11:09:58 +00:00
|
|
|
h := tx.Hash()
|
|
|
|
|
|
|
|
require.Equal(t, nil, srv.getTx(h))
|
|
|
|
|
|
|
|
srv.txx.Add(tx)
|
|
|
|
|
|
|
|
got := srv.getTx(h)
|
|
|
|
require.NotNil(t, got)
|
|
|
|
require.Equal(t, h, got.Hash())
|
|
|
|
})
|
2020-01-20 16:40:52 +00:00
|
|
|
srv.Chain.Close()
|
2019-12-27 11:09:58 +00:00
|
|
|
}
|
|
|
|
|
2019-12-05 09:07:09 +00:00
|
|
|
func TestService_OnPayload(t *testing.T) {
|
|
|
|
srv := newTestService(t)
|
|
|
|
|
|
|
|
priv, _ := getTestValidator(1)
|
|
|
|
p := new(Payload)
|
|
|
|
p.SetValidatorIndex(1)
|
|
|
|
p.SetPayload(&prepareRequest{})
|
|
|
|
|
|
|
|
// payload is not signed
|
|
|
|
srv.OnPayload(p)
|
|
|
|
shouldNotReceive(t, srv.messages)
|
|
|
|
require.Nil(t, srv.GetPayload(p.Hash()))
|
|
|
|
|
|
|
|
require.NoError(t, p.Sign(priv))
|
|
|
|
srv.OnPayload(p)
|
|
|
|
shouldReceive(t, srv.messages)
|
|
|
|
require.Equal(t, p, srv.GetPayload(p.Hash()))
|
|
|
|
|
|
|
|
// payload has already been received
|
|
|
|
srv.OnPayload(p)
|
|
|
|
shouldNotReceive(t, srv.messages)
|
2020-01-20 16:40:52 +00:00
|
|
|
srv.Chain.Close()
|
2019-12-05 09:07:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func shouldReceive(t *testing.T, ch chan Payload) {
|
|
|
|
select {
|
|
|
|
case <-ch:
|
|
|
|
default:
|
|
|
|
require.Fail(t, "missing expected message")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func shouldNotReceive(t *testing.T, ch chan Payload) {
|
|
|
|
select {
|
|
|
|
case <-ch:
|
|
|
|
require.Fail(t, "unexpected message receive")
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-29 12:40:21 +00:00
|
|
|
func newTestService(t *testing.T) *service {
|
|
|
|
srv, err := NewService(Config{
|
2019-12-30 07:43:05 +00:00
|
|
|
Logger: zaptest.NewLogger(t),
|
2019-11-29 12:40:21 +00:00
|
|
|
Broadcast: func(*Payload) {},
|
|
|
|
Chain: newTestChain(t),
|
|
|
|
RequestTx: func(...util.Uint256) {},
|
2020-03-25 15:30:21 +00:00
|
|
|
Wallet: &wallet.Config{
|
2020-01-15 14:05:47 +00:00
|
|
|
Path: "./testdata/wallet1.json",
|
2019-11-29 12:40:21 +00:00
|
|
|
Password: "one",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
return srv.(*service)
|
|
|
|
}
|
|
|
|
|
2019-12-05 09:07:09 +00:00
|
|
|
func getTestValidator(i int) (*privateKey, *publicKey) {
|
2020-01-15 14:05:47 +00:00
|
|
|
var wif, password string
|
|
|
|
|
2020-02-13 13:53:25 +00:00
|
|
|
// Sorted by public key.
|
2019-12-05 09:07:09 +00:00
|
|
|
switch i {
|
|
|
|
case 0:
|
2020-01-15 14:05:47 +00:00
|
|
|
wif = "6PYXHjPaNvW8YknSXaKsTWjf9FRxo1s4naV2jdmSQEgzaqKGX368rndN3L"
|
|
|
|
password = "two"
|
|
|
|
|
2020-02-13 13:53:25 +00:00
|
|
|
case 1:
|
|
|
|
wif = "6PYRXVwHSqFSukL3CuXxdQ75VmsKpjeLgQLEjt83FrtHf1gCVphHzdD4nc"
|
|
|
|
password = "four"
|
|
|
|
|
2019-12-05 09:07:09 +00:00
|
|
|
case 2:
|
2020-02-13 13:53:25 +00:00
|
|
|
wif = "6PYLmjBYJ4wQTCEfqvnznGJwZeW9pfUcV5m5oreHxqryUgqKpTRAFt9L8Y"
|
|
|
|
password = "one"
|
2020-01-15 14:05:47 +00:00
|
|
|
|
2019-12-05 09:07:09 +00:00
|
|
|
case 3:
|
2020-02-13 13:53:25 +00:00
|
|
|
wif = "6PYX86vYiHfUbpD95hfN1xgnvcSxy5skxfWYKu3ztjecxk6ikYs2kcWbeh"
|
|
|
|
password = "three"
|
2020-01-15 14:05:47 +00:00
|
|
|
|
2019-12-05 09:07:09 +00:00
|
|
|
default:
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2020-01-15 14:05:47 +00:00
|
|
|
key, err := keys.NEP2Decrypt(wif, password)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil
|
|
|
|
}
|
2019-12-05 09:07:09 +00:00
|
|
|
|
2020-01-15 14:05:47 +00:00
|
|
|
return &privateKey{PrivateKey: key}, &publicKey{PublicKey: key.PublicKey()}
|
2019-12-05 09:07:09 +00:00
|
|
|
}
|
|
|
|
|
2019-11-29 12:40:21 +00:00
|
|
|
func newTestChain(t *testing.T) *core.Blockchain {
|
|
|
|
unitTestNetCfg, err := config.Load("../../config", config.ModeUnitTestNet)
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2019-12-30 07:43:05 +00:00
|
|
|
chain, err := core.NewBlockchain(storage.NewMemoryStore(), unitTestNetCfg.ProtocolConfiguration, zaptest.NewLogger(t))
|
2019-11-29 12:40:21 +00:00
|
|
|
require.NoError(t, err)
|
|
|
|
|
|
|
|
go chain.Run()
|
|
|
|
|
|
|
|
return chain
|
|
|
|
}
|
|
|
|
|
|
|
|
type feer struct{}
|
|
|
|
|
|
|
|
func (fs *feer) NetworkFee(*transaction.Transaction) util.Fixed8 { return util.Fixed8(0) }
|
2020-02-18 15:42:11 +00:00
|
|
|
func (fs *feer) IsLowPriority(util.Fixed8) bool { return false }
|
2019-11-29 12:40:21 +00:00
|
|
|
func (fs *feer) FeePerByte(*transaction.Transaction) util.Fixed8 { return util.Fixed8(0) }
|
|
|
|
func (fs *feer) SystemFee(*transaction.Transaction) util.Fixed8 { return util.Fixed8(0) }
|