status-go/t/e2e/whisper/whisper_jail_test.go
Dmitry Shulyak a5cec358a9 Post whisper message asynchronously in tests
I am not 100% percent sure what is happening but it seems that
newMessageFilter is async operation, result of this operation is assumed
to be used in callbacks. All other tests are doing at least 1 io operation
in between creating a filter and posting a message, and it must be enough
for newMessageFilter to complete.

Setting higher GOMAXPROCS allows Otto vm to execute received io requests
immediatly. While lower number of processes may result in events re-ordering.

https://github.com/ethereum/web3.js/blob/develop/lib/web3/methods/shh.js#L39-L41

I tested this change with GOMAXPROCS=1 and it passes consistently.
2018-03-02 19:43:08 +01:00

362 lines
8.6 KiB
Go

package whisper
import (
"testing"
"time"
"github.com/ethereum/go-ethereum/crypto"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
"github.com/status-im/status-go/geth/jail"
"github.com/status-im/status-go/static"
e2e "github.com/status-im/status-go/t/e2e"
. "github.com/status-im/status-go/t/utils"
"github.com/stretchr/testify/suite"
)
const (
//nolint: unused, varcheck
whisperMessage1 = `test message 1 (K1 -> K2, signed+encrypted, from us)`
whisperMessage2 = `test message 3 (K1 -> "", signed broadcast)`
whisperMessage3 = `test message 4 ("" -> "", anon broadcast)`
whisperMessage4 = `test message 5 ("" -> K1, encrypted anon broadcast)`
whisperMessage5 = `test message 6 (K2 -> K1, signed+encrypted, to us)`
)
var (
baseStatusJSCode = string(static.MustAsset("testdata/jail/status.js"))
)
func TestWhisperJailTestSuite(t *testing.T) {
suite.Run(t, new(WhisperJailTestSuite))
}
type WhisperJailTestSuite struct {
e2e.BackendTestSuite
Timeout time.Duration
WhisperAPI *whisper.PublicWhisperAPI
Jail jail.Manager
}
func (s *WhisperJailTestSuite) StartTestBackend(opts ...e2e.TestNodeOption) {
s.BackendTestSuite.StartTestBackend(opts...)
s.Timeout = time.Minute * 5
s.WhisperAPI = whisper.NewPublicWhisperAPI(s.WhisperService())
s.Jail = s.Backend.JailManager()
s.Require().NotNil(s.Jail)
s.Jail.SetBaseJS(baseStatusJSCode)
}
func (s *WhisperJailTestSuite) AddKeyPair(address, password string) (string, error) {
accountManager := s.Backend.AccountManager()
_, accountKey, err := accountManager.AddressToDecryptedAccount(address, password)
if err != nil {
return "", err
}
return s.WhisperService().AddKeyPair(accountKey.PrivateKey)
}
func (s *WhisperJailTestSuite) TestJailWhisper() {
s.StartTestBackend()
defer s.StopTestBackend()
r := s.Require()
keyPairID1, err := s.AddKeyPair(TestConfig.Account1.Address, TestConfig.Account1.Password)
r.NoError(err)
keyPairID2, err := s.AddKeyPair(TestConfig.Account2.Address, TestConfig.Account2.Password)
r.NoError(err)
testCases := []struct {
name string
code string
useFilter bool
}{
{
"test 0: ensure correct version of Whisper is used",
`
var expectedVersion = '6.0';
if (web3.version.whisper != expectedVersion) {
throw 'unexpected shh version, expected: ' + expectedVersion + ', got: ' + web3.version.whisper;
}
`,
false,
},
{
"test 1: encrypted signed message from us (From != nil && To != nil)",
`
var identity1 = '` + keyPairID1 + `';
if (!shh.hasKeyPair(identity1)) {
throw 'identity "` + keyPairID1 + `" not found in whisper';
}
var identity2 = '` + keyPairID2 + `';
if (!shh.hasKeyPair(identity2)) {
throw 'identitity "` + keyPairID2 + `" not found in whisper';
}
var topic = makeTopic();
var payload = '` + whisperMessage1 + `';
// start watching for messages
var filter = shh.newMessageFilter({
sig: shh.getPublicKey(identity1),
privateKeyID: identity2,
topics: [topic]
});
// post message
var message = {
ttl: 10,
powTarget: 0.001,
powTime: 2,
topic: topic,
sig: shh.getPublicKey(identity1),
pubKey: shh.getPublicKey(identity2),
payload: web3.toHex(payload),
};
var sent = shh.post(message)
if (!sent) {
throw 'message not sent: ' + JSON.stringify(message);
}
`,
true,
},
{
"test 2: signed (known sender) broadcast (From != nil && To == nil)",
`
var identity = '` + keyPairID1 + `';
if (!shh.hasKeyPair(identity)) {
throw 'identity "` + keyPairID1 + `" not found in whisper';
}
var topic = makeTopic();
var payload = '` + whisperMessage2 + `';
// generate symmetric key
var keyid = shh.newSymKey();
if (!shh.hasSymKey(keyid)) {
throw new Error('key not found');
}
// start watching for messages
var filter = shh.newMessageFilter({
sig: shh.getPublicKey(identity),
topics: [topic],
symKeyID: keyid
});
// post message
var message = {
ttl: 10,
powTarget: 1.0,
powTime: 20,
topic: topic,
sig: shh.getPublicKey(identity),
symKeyID: keyid,
payload: web3.toHex(payload),
};
var sent = shh.post(message)
if (!sent) {
throw 'message not sent: ' + JSON.stringify(message);
}
`,
true,
},
{
"test 3: anonymous broadcast (From == nil && To == nil)",
`
var topic = makeTopic();
var payload = '` + whisperMessage3 + `';
// generate symmetric key
var keyid = shh.newSymKey();
if (!shh.hasSymKey(keyid)) {
throw new Error('key not found');
}
// start watching for messages
var filter = shh.newMessageFilter({
topics: [topic],
symKeyID: keyid
});
// creating a filter is an async operation
setTimeout(function() {
// post message
var message = {
ttl: 10,
powTarget: 0.001,
powTime: 2,
topic: topic,
symKeyID: keyid,
payload: web3.toHex(payload),
};
var sent = shh.post(message)
if (!sent) {
throw 'message not sent: ' + JSON.stringify(message);
}
}, 0)
`,
true,
},
{
"test 4: encrypted anonymous message (From == nil && To != nil)",
`
var identity = '` + keyPairID1 + `';
if (!shh.hasKeyPair(identity)) {
throw 'identity "` + keyPairID1 + `" not found in whisper';
}
var topic = makeTopic();
var payload = '` + whisperMessage4 + `';
// start watching for messages
var filter = shh.newMessageFilter({
privateKeyID: identity,
topics: [topic],
});
// post message
var message = {
ttl: 20,
powTarget: 0.01,
powTime: 20,
topic: topic,
pubKey: shh.getPublicKey(identity),
payload: web3.toHex(payload),
};
var sent = shh.post(message)
if (!sent) {
throw 'message not sent: ' + JSON.stringify(message);
}
`,
true,
},
{
"test 5: encrypted signed response to us (From != nil && To != nil)",
`
var identity1 = '` + keyPairID1 + `';
if (!shh.hasKeyPair(identity1)) {
throw 'identity "` + keyPairID1 + `" not found in whisper';
}
var identity2 = '` + keyPairID2 + `';
if (!shh.hasKeyPair(identity2)) {
throw 'identity "` + keyPairID2 + `" not found in whisper';
}
var topic = makeTopic();
var payload = '` + whisperMessage5 + `';
// start watching for messages
var filter = shh.newMessageFilter({
privateKeyID: identity1,
sig: shh.getPublicKey(identity2),
topics: [topic],
});
// post message
var message = {
ttl: 10,
powTarget: 0.001,
powTime: 2,
sig: shh.getPublicKey(identity2),
pubKey: shh.getPublicKey(identity1),
topic: topic,
payload: web3.toHex(payload)
};
var sent = shh.post(message)
if (!sent) {
throw 'message not sent: ' + message;
}
`,
true,
},
}
makeTopicCode := `
var shh = web3.shh;
// topic must be 4-byte long
var makeTopic = function () {
var topic = '0x';
for (var i = 0; i < 8; i++) {
topic += Math.floor(Math.random() * 16).toString(16);
}
return topic;
};
`
for _, tc := range testCases {
chatID := crypto.Keccak256Hash([]byte(tc.name)).Hex()
s.Jail.CreateAndInitCell(chatID, makeTopicCode)
jailCell, err := s.Jail.Cell(chatID)
r.NoError(err, "cannot get VM")
cell := jailCell.(*jail.Cell)
// Run JS code that setups filters and sends messages.
_, err = cell.Run(tc.code)
r.NoError(err)
if !tc.useFilter {
continue
}
done := make(chan struct{})
timedOut := make(chan struct{})
go func() {
select {
case <-done:
case <-time.After(s.Timeout):
close(timedOut)
}
}()
// Use polling because:
// (1) filterId is not assigned immediately,
// (2) messages propagate with some delay.
poll_loop:
for {
filter, err := cell.Get("filter")
r.NoError(err, "cannot get filter")
filterID, err := cell.GetObjectValue(filter.Value(), "filterId")
r.NoError(err, "cannot get filterId")
select {
case <-done:
ok, err := s.WhisperAPI.DeleteMessageFilter(filterID.Value().String())
r.NoError(err)
r.True(ok)
break poll_loop
case <-timedOut:
s.FailNow("polling for messages timed out. Test case: " + tc.name)
case <-time.After(time.Second):
}
// FilterID is not assigned yet.
if filterID.Value().IsNull() {
continue
}
payload, err := cell.Get("payload")
r.NoError(err, "cannot get payload")
messages, err := s.WhisperAPI.GetFilterMessages(filterID.Value().String())
r.NoError(err)
for _, m := range messages {
r.Equal(payload.Value().String(), string(m.Payload))
close(done)
}
}
}
}