import (
"container/list"
+ "encoding/hex"
+ "encoding/json"
"testing"
"time"
netWork := NewNetWork()
netWork.Register(a, "192.168.0.1", "test node A", consensus.SFFullNode)
netWork.Register(b, "192.168.0.2", "test node B", consensus.SFFullNode)
- if err := netWork.HandsShake(a, b); err != nil {
+ if B2A, A2B, err := netWork.HandsShake(a, b); err != nil {
t.Errorf("fail on peer hands shake %v", err)
+ } else {
+ go B2A.postMan()
+ go A2B.postMan()
}
a.blockKeeper.syncPeer = a.peers.getPeer("test node B")
netWork := NewNetWork()
netWork.Register(a, "192.168.0.1", "test node A", consensus.SFFullNode)
netWork.Register(b, "192.168.0.2", "test node B", consensus.SFFullNode)
- if err := netWork.HandsShake(a, b); err != nil {
+ if B2A, A2B, err := netWork.HandsShake(a, b); err != nil {
t.Errorf("fail on peer hands shake %v", err)
+ } else {
+ go B2A.postMan()
+ go A2B.postMan()
}
a.blockKeeper.syncPeer = a.peers.getPeer("test node B")
netWork := NewNetWork()
netWork.Register(a, "192.168.0.1", "test node A", consensus.SFFullNode)
netWork.Register(b, "192.168.0.2", "test node B", consensus.SFFullNode)
- if err := netWork.HandsShake(a, b); err != nil {
+ if B2A, A2B, err := netWork.HandsShake(a, b); err != nil {
t.Errorf("fail on peer hands shake %v", err)
+ } else {
+ go B2A.postMan()
+ go A2B.postMan()
}
a.blockKeeper.syncPeer = a.peers.getPeer("test node B")
}
}
}
+
+func TestSendMerkleBlock(t *testing.T) {
+ cases := []struct {
+ txCount int
+ relatedTxIndex []int
+ }{
+ {
+ txCount: 10,
+ relatedTxIndex: []int{0, 2, 5},
+ },
+ {
+ txCount: 0,
+ relatedTxIndex: []int{},
+ },
+ {
+ txCount: 10,
+ relatedTxIndex: []int{},
+ },
+ {
+ txCount: 5,
+ relatedTxIndex: []int{0, 1, 2, 3, 4},
+ },
+ {
+ txCount: 20,
+ relatedTxIndex: []int{1, 6, 3, 9, 10, 19},
+ },
+ }
+
+ for _, c := range cases {
+ blocks := mockBlocks(nil, 2)
+ targetBlock := blocks[1]
+ txs, bcTxs := mockTxs(c.txCount)
+ var err error
+
+ targetBlock.Transactions = txs
+ if targetBlock.TransactionsMerkleRoot, err = types.TxMerkleRoot(bcTxs); err != nil {
+ t.Fatal(err)
+ }
+
+ spvNode := mockSync(blocks)
+ blockHash := targetBlock.Hash()
+ var statusResult *bc.TransactionStatus
+ if statusResult, err = spvNode.chain.GetTransactionStatus(&blockHash); err != nil {
+ t.Fatal(err)
+ }
+
+ if targetBlock.TransactionStatusHash, err = types.TxStatusMerkleRoot(statusResult.VerifyStatus); err != nil {
+ t.Fatal(err)
+ }
+
+ fullNode := mockSync(blocks)
+ netWork := NewNetWork()
+ netWork.Register(spvNode, "192.168.0.1", "spv_node", consensus.SFFastSync)
+ netWork.Register(fullNode, "192.168.0.2", "full_node", consensus.DefaultServices)
+
+ var F2S *P2PPeer
+ if F2S, _, err = netWork.HandsShake(spvNode, fullNode); err != nil {
+ t.Errorf("fail on peer hands shake %v", err)
+ }
+
+ completed := make(chan error)
+ go func() {
+ msgBytes := <-F2S.msgCh
+ _, msg, _ := DecodeMessage(msgBytes)
+ switch m := msg.(type) {
+ case *MerkleBlockMessage:
+ var relatedTxIDs []*bc.Hash
+ for _, rawTx := range m.RawTxDatas {
+ tx := &types.Tx{}
+ if err := tx.UnmarshalText(rawTx); err != nil {
+ completed <- err
+ }
+
+ relatedTxIDs = append(relatedTxIDs, &tx.ID)
+ }
+ var txHashes []*bc.Hash
+ for _, hashByte := range m.TxHashes {
+ hash := bc.NewHash(hashByte)
+ txHashes = append(txHashes, &hash)
+ }
+ if ok := types.ValidateTxMerkleTreeProof(txHashes, m.Flags, relatedTxIDs, targetBlock.TransactionsMerkleRoot); !ok {
+ completed <- errors.New("validate tx fail")
+ }
+
+ var statusHashes []*bc.Hash
+ for _, statusByte := range m.StatusHashes {
+ hash := bc.NewHash(statusByte)
+ statusHashes = append(statusHashes, &hash)
+ }
+ var relatedStatuses []*bc.TxVerifyResult
+ for _, statusByte := range m.RawTxStatuses {
+ status := &bc.TxVerifyResult{}
+ err := json.Unmarshal(statusByte, status)
+ if err != nil {
+ completed <- err
+ }
+ relatedStatuses = append(relatedStatuses, status)
+ }
+ if ok := types.ValidateStatusMerkleTreeProof(statusHashes, m.Flags, relatedStatuses, targetBlock.TransactionStatusHash); !ok {
+ completed <- errors.New("validate status fail")
+ }
+
+ completed <- nil
+ }
+ }()
+
+ spvPeer := fullNode.peers.getPeer("spv_node")
+ for i := 0; i < len(c.relatedTxIndex); i++ {
+ spvPeer.filterAdds.Add(hex.EncodeToString(txs[c.relatedTxIndex[i]].Outputs[0].ControlProgram))
+ }
+ msg := &GetMerkleBlockMessage{RawHash: targetBlock.Hash().Byte32()}
+ fullNode.handleGetMerkleBlockMsg(spvPeer, msg)
+ if err := <-completed; err != nil {
+ t.Fatal(err)
+ }
+ }
+}
package types
import (
+ "encoding/hex"
+
"math/rand"
- "reflect"
"testing"
"time"
}
func TestTxMerkleProof(t *testing.T) {
+ cases := []struct {
+ txCount int
+ relatedTxIndexes []int
+ expectHashLen int
+ expectFlags []uint8
+ }{
+ {
+ txCount: 10,
+ relatedTxIndexes: []int{0, 3, 7, 8},
+ expectHashLen: 9,
+ expectFlags: []uint8{1, 1, 1, 1, 2, 0, 1, 0, 2, 1, 0, 1, 0, 2, 1, 2, 0},
+ },
+ {
+ txCount: 10,
+ relatedTxIndexes: []int{},
+ expectHashLen: 1,
+ expectFlags: []uint8{0},
+ },
+ {
+ txCount: 1,
+ relatedTxIndexes: []int{0},
+ expectHashLen: 1,
+ expectFlags: []uint8{2},
+ },
+ {
+ txCount: 19,
+ relatedTxIndexes: []int{1, 3, 5, 7, 11, 15},
+ expectHashLen: 15,
+ expectFlags: []uint8{1, 1, 1, 1, 1, 0, 2, 1, 0, 2, 1, 1, 0, 2, 1, 0, 2, 1, 1, 0, 1, 0, 2, 1, 0, 1, 0, 2, 0},
+ },
+ }
+ for _, c := range cases {
+ txs, bcTxs := mockTransactions(c.txCount)
+
+ var nodes []merkleNode
+ for _, tx := range txs {
+ nodes = append(nodes, tx.ID)
+ }
+ tree := buildMerkleTree(nodes)
+ root, err := TxMerkleRoot(bcTxs)
+ if err != nil {
+ t.Fatalf("unexpected error %s", err)
+ }
+ if tree.hash != root {
+ t.Error("build tree fail")
+ }
+
+ var relatedTx []*Tx
+ for _, index := range c.relatedTxIndexes {
+ relatedTx = append(relatedTx, txs[index])
+ }
+ proofHashes, flags := GetTxMerkleTreeProof(txs, relatedTx)
+ if !testutil.DeepEqual(flags, c.expectFlags) {
+ t.Error("The flags is not equals expect flags", flags, c.expectFlags)
+ }
+ if len(proofHashes) != c.expectHashLen {
+ t.Error("The length proof hashes is not equals expect length")
+ }
+ var ids []*bc.Hash
+ for _, tx := range relatedTx {
+ ids = append(ids, &tx.ID)
+ }
+ if !ValidateTxMerkleTreeProof(proofHashes, flags, ids, root) {
+ t.Error("Merkle tree validate fail")
+ }
+ }
+}
+
+func TestStatusMerkleProof(t *testing.T) {
+ cases := []struct {
+ statusCount int
+ relatedIndexes []int
+ flags []uint8
+ expectHashLen int
+ }{
+ {
+ statusCount: 10,
+ relatedIndexes: []int{0, 3, 7, 8},
+ flags: []uint8{1, 1, 1, 1, 2, 0, 1, 0, 2, 1, 0, 1, 0, 2, 1, 2, 0},
+ expectHashLen: 9,
+ },
+ {
+ statusCount: 10,
+ relatedIndexes: []int{},
+ flags: []uint8{0},
+ expectHashLen: 1,
+ },
+ {
+ statusCount: 1,
+ relatedIndexes: []int{0},
+ flags: []uint8{2},
+ expectHashLen: 1,
+ },
+ {
+ statusCount: 19,
+ relatedIndexes: []int{1, 3, 5, 7, 11, 15},
+ flags: []uint8{1, 1, 1, 1, 1, 0, 2, 1, 0, 2, 1, 1, 0, 2, 1, 0, 2, 1, 1, 0, 1, 0, 2, 1, 0, 1, 0, 2, 0},
+ expectHashLen: 15,
+ },
+ }
+ for _, c := range cases {
+ statuses := mockStatuses(c.statusCount)
+ var relatedStatuses []*bc.TxVerifyResult
+ for _, index := range c.relatedIndexes {
+ relatedStatuses = append(relatedStatuses, statuses[index])
+ }
+ hashes := GetStatusMerkleTreeProof(statuses, c.flags)
+ if len(hashes) != c.expectHashLen {
+ t.Error("The length proof hashes is not equals expect length")
+ }
+ root, _ := TxStatusMerkleRoot(statuses)
+ if !ValidateStatusMerkleTreeProof(hashes, c.flags, relatedStatuses, root) {
+ t.Error("Merkle tree validate fail")
+ }
+ }
+}
+
+func TestUglyValidateTxMerkleProof(t *testing.T) {
+ cases := []struct {
+ hashes []string
+ flags []uint8
+ relatedHashes []string
+ root string
+ expectResult bool
+ }{
+ {
+ hashes: []string{},
+ flags: []uint8{},
+ relatedHashes: []string{},
+ root: "",
+ expectResult: false,
+ },
+ {
+ hashes: []string{},
+ flags: []uint8{1, 1, 1, 1, 2, 0, 1, 0, 2, 1, 0, 1, 0, 2, 1, 2, 0},
+ relatedHashes: []string{},
+ root: "",
+ expectResult: false,
+ },
+ {
+ hashes: []string{
+ "0093370a8e19f8f131fd7e75c576615950d5672ee5e18c63f105a95bcab4332c",
+ "c9b7779847fb7ab74cf4b1e7f4557133918faa2bc130042753417dfb62b12dfa",
+ },
+ flags: []uint8{},
+ relatedHashes: []string{},
+ root: "",
+ expectResult: false,
+ },
+ {
+ hashes: []string{},
+ flags: []uint8{},
+ relatedHashes: []string{
+ "0093370a8e19f8f131fd7e75c576615950d5672ee5e18c63f105a95bcab4332c",
+ "c9b7779847fb7ab74cf4b1e7f4557133918faa2bc130042753417dfb62b12dfa",
+ },
+ root: "",
+ expectResult: false,
+ },
+ {
+ hashes: []string{},
+ flags: []uint8{1, 1, 0, 2, 1, 2, 1, 0, 1},
+ relatedHashes: []string{
+ "0093370a8e19f8f131fd7e75c576615950d5672ee5e18c63f105a95bcab4332c",
+ "c9b7779847fb7ab74cf4b1e7f4557133918faa2bc130042753417dfb62b12dfa",
+ },
+ root: "281138e0a9ea19505844bd61a2f5843787035782c093da74d12b5fba73eeeb07",
+ },
+ {
+ hashes: []string{
+ "68f03ea2b02a21ad944d1a43ad6152a7fa6a7ed4101d59be62594dd30ef2a558",
+ },
+ flags: []uint8{},
+ relatedHashes: []string{
+ "0093370a8e19f8f131fd7e75c576615950d5672ee5e18c63f105a95bcab4332c",
+ "c9b7779847fb7ab74cf4b1e7f4557133918faa2bc130042753417dfb62b12dfa",
+ },
+ root: "281138e0a9ea19505844bd61a2f5843787035782c093da74d12b5fba73eeeb07",
+ expectResult: false,
+ },
+ {
+ hashes: []string{
+ "8ec3ee7589f95eee9b534f71fcd37142bcc839a0dbfe78124df9663827b90c35",
+ "011bd3380852b2946df507e0c6234222c559eec8f545e4bc58a89e960892259b",
+ "c205988d9c864083421f1bdb95e6cf8b52070facfcc87e46a6e8197f5389fca2",
+ },
+ flags: []uint8{1, 1, 0, 2, 0},
+ relatedHashes: []string{
+ "504af455e328e7dd39bbc059529851946d54ee8b459b11b3aac4a0feeb474487",
+ },
+ root: "aff81a46fe79204ef9007243f374d54104a59762b9f74d80d56b5291753db6fb",
+ expectResult: true,
+ },
+ // flags and hashes is correct, but relatedHashes has hash that does not exist
+ {
+ hashes: []string{
+ "8ec3ee7589f95eee9b534f71fcd37142bcc839a0dbfe78124df9663827b90c35",
+ "011bd3380852b2946df507e0c6234222c559eec8f545e4bc58a89e960892259b",
+ "c205988d9c864083421f1bdb95e6cf8b52070facfcc87e46a6e8197f5389fca2",
+ },
+ flags: []uint8{1, 1, 0, 2, 0},
+ relatedHashes: []string{
+ "504af455e328e7dd39bbc059529851946d54ee8b459b11b3aac4a0feeb474487",
+ "281138e0a9ea19505844bd61a2f5843787035782c093da74d12b5fba73eeeb07",
+ },
+ root: "aff81a46fe79204ef9007243f374d54104a59762b9f74d80d56b5291753db6fb",
+ expectResult: false,
+ },
+ // flags and hashes is correct, but relatedHashes is not enough
+ {
+ hashes: []string{
+ "8ec3ee7589f95eee9b534f71fcd37142bcc839a0dbfe78124df9663827b90c35",
+ "011bd3380852b2946df507e0c6234222c559eec8f545e4bc58a89e960892259b",
+ "c205988d9c864083421f1bdb95e6cf8b52070facfcc87e46a6e8197f5389fca2",
+ },
+ flags: []uint8{1, 1, 0, 2, 0},
+ relatedHashes: []string{},
+ root: "aff81a46fe79204ef9007243f374d54104a59762b9f74d80d56b5291753db6fb",
+ expectResult: false,
+ },
+ // flags is correct, but hashes has additional hash at the end
+ {
+ hashes: []string{
+ "8ec3ee7589f95eee9b534f71fcd37142bcc839a0dbfe78124df9663827b90c35",
+ "011bd3380852b2946df507e0c6234222c559eec8f545e4bc58a89e960892259b",
+ "c205988d9c864083421f1bdb95e6cf8b52070facfcc87e46a6e8197f5389fca2",
+ "5a06c90136e81c0f9cad29725e69edc6d21bd6fb0641265f9c4b6bb6840b37dd",
+ },
+ flags: []uint8{1, 1, 0, 2, 0},
+ relatedHashes: []string{
+ "504af455e328e7dd39bbc059529851946d54ee8b459b11b3aac4a0feeb474487",
+ },
+ root: "aff81a46fe79204ef9007243f374d54104a59762b9f74d80d56b5291753db6fb",
+ expectResult: true,
+ },
+ }
+
+ for _, c := range cases {
+ var hashes, relatedHashes []*bc.Hash
+ var hashBytes, rootBytes [32]byte
+ var err error
+ for _, hashStr := range c.hashes {
+ if hashBytes, err = convertHashStr2Bytes(hashStr); err != nil {
+ t.Fatal(err)
+ }
+
+ hash := bc.NewHash(hashBytes)
+ hashes = append(hashes, &hash)
+ }
+ for _, hashStr := range c.relatedHashes {
+ if hashBytes, err = convertHashStr2Bytes(hashStr); err != nil {
+ t.Fatal(err)
+ }
+
+ hash := bc.NewHash(hashBytes)
+ relatedHashes = append(relatedHashes, &hash)
+ }
+ if rootBytes, err = convertHashStr2Bytes(c.root); err != nil {
+ t.Fatal(err)
+ }
+
+ root := bc.NewHash(rootBytes)
+ if ValidateTxMerkleTreeProof(hashes, c.flags, relatedHashes, root) != c.expectResult {
+ t.Error("Validate merkle tree proof fail")
+ }
+ }
+}
+
+func convertHashStr2Bytes(hashStr string) ([32]byte, error) {
+ var result [32]byte
+ hashBytes, err := hex.DecodeString(hashStr)
+ if err != nil {
+ return result, err
+ }
+ copy(result[:], hashBytes)
+ return result, nil
+}
+
+func mockTransactions(txCount int) ([]*Tx, []*bc.Tx) {
var txs []*Tx
var bcTxs []*bc.Tx
trueProg := []byte{byte(vm.OP_TRUE)}
assetID := bc.ComputeAssetID(trueProg, 1, &bc.EmptyStringHash)
- for i := 0; i < 10; i++ {
+ for i := 0; i < txCount; i++ {
now := []byte(time.Now().String())
issuanceInp := NewIssuanceInput(now, 1, trueProg, nil, nil)
tx := NewTx(TxData{
txs = append(txs, tx)
bcTxs = append(bcTxs, tx.Tx)
}
- root, err := TxMerkleRoot(bcTxs)
- if err != nil {
- t.Fatalf("unexpected error %s", err)
- }
-
- relatedTx := []*Tx{txs[0], txs[3], txs[7], txs[8]}
- proofHashes, flags := GetTxMerkleTreeProof(txs, relatedTx)
- if len(proofHashes) <= 0 {
- t.Error("Can not find any tx id in the merkle tree")
- }
- expectFlags := []uint8{1, 1, 1, 1, 2, 0, 1, 0, 2, 1, 0, 1, 0, 2, 1, 2, 0}
- if !reflect.DeepEqual(flags, expectFlags) {
- t.Error("The flags is not equals expect flags", flags)
- }
- if len(proofHashes) != 9 {
- t.Error("The length proof hashes is not equals expect length")
- }
- ids := []*bc.Hash{&txs[0].ID, &txs[3].ID, &txs[7].ID, &txs[8].ID}
- if !ValidateTxMerkleTreeProof(proofHashes, flags, ids, root) {
- t.Error("Merkle tree validate fail")
- }
+ return txs, bcTxs
}
-func TestStatusMerkleProof(t *testing.T) {
+func mockStatuses(statusCount int) []*bc.TxVerifyResult {
var statuses []*bc.TxVerifyResult
- for i := 0; i < 10; i++ {
+ for i := 0; i < statusCount; i++ {
status := &bc.TxVerifyResult{}
fail := rand.Intn(2)
if fail == 0 {
}
statuses = append(statuses, status)
}
- relatedStatuses := []*bc.TxVerifyResult{statuses[0], statuses[3], statuses[7], statuses[8]}
- flags := []uint8{1, 1, 1, 1, 2, 0, 1, 0, 2, 1, 0, 1, 0, 2, 1, 2, 0}
- hashes := GetStatusMerkleTreeProof(statuses, flags)
- if len(hashes) != 9 {
- t.Error("The length proof hashes is not equals expect length")
- }
- root, _ := TxStatusMerkleRoot(statuses)
- if !ValidateStatusMerkleTreeProof(hashes, flags, relatedStatuses, root) {
- t.Error("Merkle tree validate fail")
- }
+ return statuses
}