parent
a5da06f382
commit
0a13765a51
@ -0,0 +1,77 @@ |
|||||||
|
package blsloader |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"time" |
||||||
|
) |
||||||
|
|
||||||
|
func setTestConsole(tc *testConsole) { |
||||||
|
console = tc |
||||||
|
} |
||||||
|
|
||||||
|
type testConsole struct { |
||||||
|
In chan string |
||||||
|
Out chan string |
||||||
|
} |
||||||
|
|
||||||
|
func newTestConsole() *testConsole { |
||||||
|
in := make(chan string, 100) |
||||||
|
out := make(chan string, 100) |
||||||
|
return &testConsole{in, out} |
||||||
|
} |
||||||
|
|
||||||
|
func (tc *testConsole) readPassword() (string, error) { |
||||||
|
return tc.readln() |
||||||
|
} |
||||||
|
|
||||||
|
func (tc *testConsole) readln() (string, error) { |
||||||
|
select { |
||||||
|
case <-time.After(10 * time.Second): |
||||||
|
return "", errors.New("timed out") |
||||||
|
case <-tc.In: |
||||||
|
msg, ok := <-tc.In |
||||||
|
if !ok { |
||||||
|
return "", errors.New("in channel closed") |
||||||
|
} |
||||||
|
return msg, nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (tc *testConsole) print(a ...interface{}) { |
||||||
|
msg := fmt.Sprint(a...) |
||||||
|
tc.Out <- msg |
||||||
|
} |
||||||
|
|
||||||
|
func (tc *testConsole) println(a ...interface{}) { |
||||||
|
msg := fmt.Sprintln(a...) |
||||||
|
tc.Out <- msg |
||||||
|
} |
||||||
|
|
||||||
|
func (tc *testConsole) printf(format string, a ...interface{}) { |
||||||
|
msg := fmt.Sprintf(format, a...) |
||||||
|
tc.Out <- msg |
||||||
|
} |
||||||
|
|
||||||
|
func (tc *testConsole) checkClean() (bool, string) { |
||||||
|
select { |
||||||
|
case msg := <-tc.In: |
||||||
|
return false, "extra in message: " + msg |
||||||
|
case msg := <-tc.Out: |
||||||
|
return false, "extra out message: " + msg |
||||||
|
default: |
||||||
|
return true, "" |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (tc *testConsole) checkOutput(timeout time.Duration, checkFunc func(string) error) error { |
||||||
|
if timeout == 0 { |
||||||
|
timeout = 10 * time.Second |
||||||
|
} |
||||||
|
select { |
||||||
|
case <-time.After(timeout): |
||||||
|
return errors.New("timed out") |
||||||
|
case msg := <-tc.Out: |
||||||
|
return checkFunc(msg) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,271 @@ |
|||||||
|
package blsloader |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"strings" |
||||||
|
"sync" |
||||||
|
"testing" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/harmony-one/harmony/crypto/bls" |
||||||
|
"github.com/harmony-one/harmony/multibls" |
||||||
|
) |
||||||
|
|
||||||
|
type testKey struct { |
||||||
|
publicKey string |
||||||
|
privateKey string |
||||||
|
passphrase string |
||||||
|
passFile string |
||||||
|
path string |
||||||
|
} |
||||||
|
|
||||||
|
// validTestKeys are keys with valid password and valid .pass file
|
||||||
|
var validTestKeys = []testKey{ |
||||||
|
{ |
||||||
|
// key with empty passphrase
|
||||||
|
publicKey: "0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500", |
||||||
|
privateKey: "78c88c331195591b396e3205830071901a7a79e14fd0ede7f06bfb4c5e9f3473", |
||||||
|
passphrase: "", |
||||||
|
passFile: "testData/blskey_passphrase/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.pass", |
||||||
|
path: "testData/blskey_passphrase/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.key", |
||||||
|
}, |
||||||
|
{ |
||||||
|
// key with non empty passphrase
|
||||||
|
publicKey: "152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083", |
||||||
|
privateKey: "c20fa8de733d08e27e3101436d41f6a3207b8bedad7525c6e91a77ae2a49cf56", |
||||||
|
passphrase: "harmony", |
||||||
|
passFile: "testData/blskey_passphrase/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.pass", |
||||||
|
path: "testData/blskey_passphrase/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.key", |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
// emptyPassTestKeys are keys with valid password but empty .pass file
|
||||||
|
var emptyPassTestKeys = []testKey{ |
||||||
|
{ |
||||||
|
// key with empty passphrase
|
||||||
|
publicKey: "0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500", |
||||||
|
privateKey: "78c88c331195591b396e3205830071901a7a79e14fd0ede7f06bfb4c5e9f3473", |
||||||
|
passphrase: "", |
||||||
|
path: "testData/blskey_emptypass/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.key", |
||||||
|
}, |
||||||
|
{ |
||||||
|
// key with non empty passphrase
|
||||||
|
publicKey: "152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083", |
||||||
|
privateKey: "c20fa8de733d08e27e3101436d41f6a3207b8bedad7525c6e91a77ae2a49cf56", |
||||||
|
passphrase: "harmony", |
||||||
|
path: "testData/blskey_emptypass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.key", |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
// wrongPassTestKeys are keys with wrong pass file and wrong passphrase
|
||||||
|
var wrongPassTestKeys = []testKey{ |
||||||
|
{ |
||||||
|
// key with empty passphrase
|
||||||
|
publicKey: "0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500", |
||||||
|
privateKey: "78c88c331195591b396e3205830071901a7a79e14fd0ede7f06bfb4c5e9f3473", |
||||||
|
passphrase: "evil harmony", |
||||||
|
passFile: "testData/blskey_wrongpass/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.pass", |
||||||
|
path: "testData/blskey_wrongpass/0e969f8b302cf7648bc39652ca7a279a8562b72933a3f7cddac2252583280c7c3495c9ae854f00f6dd19c32fc5a17500.key", |
||||||
|
}, |
||||||
|
{ |
||||||
|
// key with non empty passphrase
|
||||||
|
publicKey: "152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083", |
||||||
|
privateKey: "c20fa8de733d08e27e3101436d41f6a3207b8bedad7525c6e91a77ae2a49cf56", |
||||||
|
passphrase: "harmony", |
||||||
|
passFile: "testData/blskey_wrongpass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.pass", |
||||||
|
path: "testData/blskey_wrongpass/152beed46d7a0002ef0f960946008887eedd4775bdf2ed238809aa74e20d31fdca267443615cc6f4ede49d58911ee083.key", |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
func TestLoadKeys_SingleBls_File(t *testing.T) { |
||||||
|
tests := []struct { |
||||||
|
cfg Config |
||||||
|
inputs []string |
||||||
|
|
||||||
|
expOutputs []string |
||||||
|
expPubKeys []string |
||||||
|
expErr error |
||||||
|
}{ |
||||||
|
{ |
||||||
|
// load the default pass file
|
||||||
|
cfg: Config{ |
||||||
|
BlsKeyFile: &validTestKeys[0].path, |
||||||
|
PassSrcType: PassSrcFile, |
||||||
|
}, |
||||||
|
inputs: []string{}, |
||||||
|
|
||||||
|
expOutputs: []string{}, |
||||||
|
expPubKeys: []string{validTestKeys[0].publicKey}, |
||||||
|
}, |
||||||
|
} |
||||||
|
for i, test := range tests { |
||||||
|
ts := &testSuite{ |
||||||
|
cfg: test.cfg, |
||||||
|
inputs: test.inputs, |
||||||
|
expOutputs: test.expOutputs, |
||||||
|
expErr: test.expErr, |
||||||
|
expPubKeys: test.expPubKeys, |
||||||
|
} |
||||||
|
ts.init() |
||||||
|
ts.process() |
||||||
|
fmt.Println(111) |
||||||
|
if err := ts.checkResult(); err != nil { |
||||||
|
t.Errorf("test %v: %v", i, err) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func assertError(got, expect error) error { |
||||||
|
if (got == nil) != (expect == nil) { |
||||||
|
return fmt.Errorf("unexpected error [%v] / [%v]", got, expect) |
||||||
|
} |
||||||
|
if (got == nil) || (expect == nil) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
if !strings.Contains(got.Error(), expect.Error()) { |
||||||
|
return fmt.Errorf("unexpected error [%v] / [%v]", got, expect) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
type testSuite struct { |
||||||
|
cfg Config |
||||||
|
inputs []string |
||||||
|
|
||||||
|
expOutputs []string |
||||||
|
expPubKeys []string |
||||||
|
expErr error |
||||||
|
|
||||||
|
gotKeys multibls.PrivateKeys |
||||||
|
gotOutputs []string |
||||||
|
|
||||||
|
timeout time.Duration |
||||||
|
console *testConsole |
||||||
|
gotErr error // err returned from load key
|
||||||
|
errC chan error |
||||||
|
wg sync.WaitGroup |
||||||
|
} |
||||||
|
|
||||||
|
func (ts *testSuite) init() { |
||||||
|
ts.gotOutputs = make([]string, 0, len(ts.expOutputs)) |
||||||
|
ts.console = newTestConsole() |
||||||
|
setTestConsole(ts.console) |
||||||
|
ts.timeout = 1 * time.Second |
||||||
|
ts.errC = make(chan error, 3) |
||||||
|
} |
||||||
|
|
||||||
|
func (ts *testSuite) process() { |
||||||
|
ts.wg.Add(3) |
||||||
|
go ts.threadedLoadOutputs() |
||||||
|
go ts.threadedFeedConsoleInputs() |
||||||
|
go ts.threadLoadKeys() |
||||||
|
|
||||||
|
ts.wg.Wait() |
||||||
|
} |
||||||
|
|
||||||
|
func (ts *testSuite) checkResult() error { |
||||||
|
if err := assertError(ts.gotErr, ts.expErr); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
select { |
||||||
|
case err := <-ts.errC: |
||||||
|
return err |
||||||
|
default: |
||||||
|
} |
||||||
|
if isClean, msg := ts.console.checkClean(); !isClean { |
||||||
|
return fmt.Errorf("console not clean: %v", msg) |
||||||
|
} |
||||||
|
if ts.expErr != nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
if err := ts.checkKeys(); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (ts *testSuite) checkOutputs() error { |
||||||
|
if len(ts.gotOutputs) != len(ts.expOutputs) { |
||||||
|
return fmt.Errorf("output size not expected: %v / %v", len(ts.gotOutputs), len(ts.expOutputs)) |
||||||
|
} |
||||||
|
for i, gotOutput := range ts.gotOutputs { |
||||||
|
expOutput := ts.expOutputs[i] |
||||||
|
if gotOutput != expOutput { |
||||||
|
return fmt.Errorf("%vth output unexpected: [%v] / [%v]", i, gotOutput, expOutput) |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (ts *testSuite) checkKeys() error { |
||||||
|
if len(ts.expPubKeys) != len(ts.gotKeys) { |
||||||
|
return fmt.Errorf("loaded key size not expected: %v / %v", len(ts.gotKeys), len(ts.expPubKeys)) |
||||||
|
} |
||||||
|
expKeyMap := make(map[bls.SerializedPublicKey]struct{}) |
||||||
|
for _, pubKeyStr := range ts.expPubKeys { |
||||||
|
pubKey := pubStrToPubBytes(pubKeyStr) |
||||||
|
if _, exist := expKeyMap[pubKey]; exist { |
||||||
|
return fmt.Errorf("duplicate expect pubkey %x", pubKey) |
||||||
|
} |
||||||
|
expKeyMap[pubKey] = struct{}{} |
||||||
|
} |
||||||
|
gotVisited := make(map[bls.SerializedPublicKey]struct{}) |
||||||
|
for _, gotPubWrapper := range ts.gotKeys.GetPublicKeys() { |
||||||
|
pubKey := gotPubWrapper.Bytes |
||||||
|
if _, exist := gotVisited[pubKey]; exist { |
||||||
|
return fmt.Errorf("duplicate got pubkey %x", pubKey) |
||||||
|
} |
||||||
|
if _, exist := expKeyMap[pubKey]; !exist { |
||||||
|
return fmt.Errorf("got pubkey not found in expect %x", pubKey) |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (ts *testSuite) threadLoadKeys() { |
||||||
|
defer ts.wg.Done() |
||||||
|
|
||||||
|
ts.gotKeys, ts.gotErr = LoadKeys(ts.cfg) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func (ts *testSuite) threadedFeedConsoleInputs() { |
||||||
|
defer ts.wg.Done() |
||||||
|
|
||||||
|
i := 0 |
||||||
|
for i < len(ts.inputs) { |
||||||
|
select { |
||||||
|
case ts.console.In <- ts.inputs[i]: |
||||||
|
i += 1 |
||||||
|
case <-time.After(ts.timeout): |
||||||
|
ts.errC <- errors.New("feed inputs timed out") |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (ts *testSuite) threadedLoadOutputs() { |
||||||
|
defer ts.wg.Done() |
||||||
|
var ( |
||||||
|
i = 0 |
||||||
|
) |
||||||
|
for i < len(ts.expOutputs) { |
||||||
|
select { |
||||||
|
case got := <-ts.console.Out: |
||||||
|
ts.gotOutputs = append(ts.gotOutputs, got) |
||||||
|
i++ |
||||||
|
case <-time.After(ts.timeout): |
||||||
|
ts.errC <- errors.New("load outputs timed out") |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func pubStrToPubBytes(str string) bls.SerializedPublicKey { |
||||||
|
b := common.Hex2Bytes(str) |
||||||
|
var pubKey bls.SerializedPublicKey |
||||||
|
copy(pubKey[:], b) |
||||||
|
return pubKey |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
1d97f32175d8875f251e15805fd08f0cda794d827cb02d2de7b10d10f36f951d68347bef1e7a3018bd865c6966219cd9c4d20b055c50f8e09a6a3a1666b7c112450f643cc3c175f541fae75da8a843d47993fe89ec85788fd6ea2e98 |
@ -0,0 +1 @@ |
|||||||
|
194a2d68c37f037f36b28a560402d64ab007f949313b63d9a08f5adb55a061681c70d9119df2d2cdcae5da6e484550c03bad63aae7c1332a3647ce633999ac4ddbb4a40e213c7e88e604784fef40da9d2f28b392c9fb2462f5e51e9c |
@ -0,0 +1 @@ |
|||||||
|
1d97f32175d8875f251e15805fd08f0cda794d827cb02d2de7b10d10f36f951d68347bef1e7a3018bd865c6966219cd9c4d20b055c50f8e09a6a3a1666b7c112450f643cc3c175f541fae75da8a843d47993fe89ec85788fd6ea2e98 |
@ -0,0 +1 @@ |
|||||||
|
194a2d68c37f037f36b28a560402d64ab007f949313b63d9a08f5adb55a061681c70d9119df2d2cdcae5da6e484550c03bad63aae7c1332a3647ce633999ac4ddbb4a40e213c7e88e604784fef40da9d2f28b392c9fb2462f5e51e9c |
@ -0,0 +1 @@ |
|||||||
|
harmony |
@ -0,0 +1 @@ |
|||||||
|
1d97f32175d8875f251e15805fd08f0cda794d827cb02d2de7b10d10f36f951d68347bef1e7a3018bd865c6966219cd9c4d20b055c50f8e09a6a3a1666b7c112450f643cc3c175f541fae75da8a843d47993fe89ec85788fd6ea2e98 |
@ -0,0 +1 @@ |
|||||||
|
evil harmony |
@ -0,0 +1 @@ |
|||||||
|
194a2d68c37f037f36b28a560402d64ab007f949313b63d9a08f5adb55a061681c70d9119df2d2cdcae5da6e484550c03bad63aae7c1332a3647ce633999ac4ddbb4a40e213c7e88e604784fef40da9d2f28b392c9fb2462f5e51e9c |
@ -0,0 +1 @@ |
|||||||
|
dark harmony |
Loading…
Reference in new issue