diff --git a/benchmarks/simu/counter.go b/benchmarks/simu/counter.go index ca155bbd8..36d1e35df 100644 --- a/benchmarks/simu/counter.go +++ b/benchmarks/simu/counter.go @@ -37,7 +37,9 @@ func main() { for i := 0; ; i++ { binary.BigEndian.PutUint64(buf, uint64(i)) //txBytes := hex.EncodeToString(buf[:n]) - request := rpctypes.NewRPCRequest("fakeid", "broadcast_tx", Arr(buf[:8])) + request := rpctypes.NewRPCRequest("fakeid", + "broadcast_tx", + map[string]interface{}{"tx": buf[:8]}) reqBytes := wire.JSONBytes(request) //fmt.Println("!!", string(reqBytes)) fmt.Print(".") diff --git a/glide.lock b/glide.lock index 71b549dbe..63b3875d0 100644 --- a/glide.lock +++ b/glide.lock @@ -1,10 +1,12 @@ -hash: 81cd41d28f9a747a71e6a47e8bc7d855846df29f359ffdcc8d1645c451112b31 -updated: 2017-03-06T17:34:23.99160606-05:00 +hash: d9724aa287c40d1b3856b6565f09235d809c8b2f7c6537c04f597137c0d6cd26 +updated: 2017-04-11T15:24:16.619608243-04:00 imports: - name: github.com/btcsuite/btcd - version: 583684b21bfbde9b5fc4403916fd7c807feb0289 + version: b8df516b4b267acf2de46be593a9d948d1d2c420 subpackages: - btcec +- name: github.com/btcsuite/fastsha256 + version: 637e656429416087660c84436a2a035d69d54e2e - name: github.com/BurntSushi/toml version: 99064174e013895bbd9b025c31100bd1d9b590ca - name: github.com/davecgh/go-spew @@ -16,7 +18,7 @@ imports: - name: github.com/go-stack/stack version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82 - name: github.com/gogo/protobuf - version: 909568be09de550ed094403c2bf8a261b5bb730a + version: 100ba4e885062801d56799d78530b73b178a78f3 subpackages: - proto - name: github.com/golang/protobuf @@ -32,9 +34,9 @@ imports: - name: github.com/jmhodges/levigo version: c42d9e0ca023e2198120196f842701bb4c55d7b9 - name: github.com/mattn/go-colorable - version: acb9493f2794fd0f820de7a27a217dafbb1b65ea + version: 9fdad7c47650b7d2e1da50644c1f4ba7f172f252 - name: github.com/mattn/go-isatty - version: 9622e0cc9d8f9be434ca605520ff9a16808fee47 + version: 56b76bdf51f7708750eac80fa38b952bb9f32639 - name: github.com/pkg/errors version: 645ef00459ed84a119197bfb8d8205042c6df63d - name: github.com/pmezard/go-difflib @@ -66,7 +68,7 @@ imports: - leveldb/table - leveldb/util - name: github.com/tendermint/abci - version: 1236e8fb6eee3a63909f4014a8e84385ead7933d + version: 31eafe8f8eba6b8817edd74df399f508540da528 subpackages: - client - example/counter @@ -83,15 +85,15 @@ imports: - name: github.com/tendermint/go-clist version: 3baa390bbaf7634251c42ad69a8682e7e3990552 - name: github.com/tendermint/go-common - version: dcb015dff6c7af21e65c8e2f3b450df19d38c777 + version: 6af2364fa91ef2f3afc8ba0db33b66d9d3ae006c subpackages: - test - name: github.com/tendermint/go-config version: 620dcbbd7d587cf3599dedbf329b64311b0c307a - name: github.com/tendermint/go-crypto - version: 3f47cfac5fcd9e0f1727c7db980b3559913b3e3a + version: 750b25c47a5782f5f2b773ed9e706cb82b3ccef4 - name: github.com/tendermint/go-data - version: 32271140e8fd5abdbb22e268d7a02421fa382f0b + version: e7fcc6d081ec8518912fcdc103188275f83a3ee5 - name: github.com/tendermint/go-db version: eac3f2bc147023957c8bf69432a4e6c4dc5c3f72 - name: github.com/tendermint/go-events @@ -105,17 +107,17 @@ imports: - name: github.com/tendermint/go-merkle version: 714d4d04557fd068a7c2a1748241ce8428015a96 - name: github.com/tendermint/go-p2p - version: 97a5ed2d1a17eaee8717b8a32cfaf7a9a82a273d + version: c39e001a957caf768f06c85c840debb8282c3aaa subpackages: - upnp - name: github.com/tendermint/go-rpc - version: fcea0cda21f64889be00a0f4b6d13266b1a76ee7 + version: 9d18cbe74e66f875afa36d2fa3be280e4a2dc9e6 subpackages: - client - server - types - name: github.com/tendermint/go-wire - version: f530b7af7a8b06e612c2063bff6ace49060a085e + version: 09dae074245a8042aa689d084af774e6ad6a90bb - name: github.com/tendermint/log15 version: ae0f3d6450da9eac7074b439c8e1c3cabf0d5ce6 subpackages: @@ -127,7 +129,7 @@ imports: - client - testutil - name: golang.org/x/crypto - version: 40541ccb1c6e64c947ed6f606b8a6cb4b67d7436 + version: 1f22c0103821b9390939b6776727195525381532 subpackages: - curve25519 - nacl/box @@ -148,7 +150,7 @@ imports: - lex/httplex - trace - name: golang.org/x/sys - version: e48874b42435b4347fc52bdee0424a52abc974d7 + version: 50c6bc5e4292a1d4e65c6e9be5f53be28bcbe28e subpackages: - unix - name: google.golang.org/grpc diff --git a/glide.yaml b/glide.yaml index 53d308233..cdb083e69 100644 --- a/glide.yaml +++ b/glide.yaml @@ -10,6 +10,8 @@ import: version: develop - package: github.com/tendermint/go-crypto version: develop +- package: github.com/tendermint/go-data + version: develop - package: github.com/tendermint/go-db version: develop - package: github.com/tendermint/go-events diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index bb4e6d3a8..07059bca0 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -22,7 +22,7 @@ out the server for test code (mock). */ type HTTP struct { remote string - rpc *rpcclient.ClientJSONRPC + rpc *rpcclient.JSONRPCClient *WSEvents } @@ -30,7 +30,7 @@ type HTTP struct { // and the websocket path (which always seems to be "/websocket") func NewHTTP(remote, wsEndpoint string) *HTTP { return &HTTP{ - rpc: rpcclient.NewClientJSONRPC(remote), + rpc: rpcclient.NewJSONRPCClient(remote), remote: remote, WSEvents: newWSEvents(remote, wsEndpoint), } @@ -50,7 +50,7 @@ func (c *HTTP) _assertIsEventSwitch() types.EventSwitch { func (c *HTTP) Status() (*ctypes.ResultStatus, error) { tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("status", []interface{}{}, tmResult) + _, err := c.rpc.Call("status", map[string]interface{}{}, tmResult) if err != nil { return nil, errors.Wrap(err, "Status") } @@ -60,7 +60,7 @@ func (c *HTTP) Status() (*ctypes.ResultStatus, error) { func (c *HTTP) ABCIInfo() (*ctypes.ResultABCIInfo, error) { tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("abci_info", []interface{}{}, tmResult) + _, err := c.rpc.Call("abci_info", map[string]interface{}{}, tmResult) if err != nil { return nil, errors.Wrap(err, "ABCIInfo") } @@ -69,7 +69,9 @@ func (c *HTTP) ABCIInfo() (*ctypes.ResultABCIInfo, error) { func (c *HTTP) ABCIQuery(path string, data []byte, prove bool) (*ctypes.ResultABCIQuery, error) { tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("abci_query", []interface{}{path, data, prove}, tmResult) + _, err := c.rpc.Call("abci_query", + map[string]interface{}{"path": path, "data": data, "prove": prove}, + tmResult) if err != nil { return nil, errors.Wrap(err, "ABCIQuery") } @@ -78,7 +80,7 @@ func (c *HTTP) ABCIQuery(path string, data []byte, prove bool) (*ctypes.ResultAB func (c *HTTP) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("broadcast_tx_commit", []interface{}{tx}, tmResult) + _, err := c.rpc.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, tmResult) if err != nil { return nil, errors.Wrap(err, "broadcast_tx_commit") } @@ -95,7 +97,7 @@ func (c *HTTP) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { func (c *HTTP) broadcastTX(route string, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call(route, []interface{}{tx}, tmResult) + _, err := c.rpc.Call(route, map[string]interface{}{"tx": tx}, tmResult) if err != nil { return nil, errors.Wrap(err, route) } @@ -104,7 +106,7 @@ func (c *HTTP) broadcastTX(route string, tx types.Tx) (*ctypes.ResultBroadcastTx func (c *HTTP) NetInfo() (*ctypes.ResultNetInfo, error) { tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("net_info", nil, tmResult) + _, err := c.rpc.Call("net_info", map[string]interface{}{}, tmResult) if err != nil { return nil, errors.Wrap(err, "NetInfo") } @@ -113,7 +115,7 @@ func (c *HTTP) NetInfo() (*ctypes.ResultNetInfo, error) { func (c *HTTP) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("dump_consensus_state", nil, tmResult) + _, err := c.rpc.Call("dump_consensus_state", map[string]interface{}{}, tmResult) if err != nil { return nil, errors.Wrap(err, "DumpConsensusState") } @@ -122,7 +124,9 @@ func (c *HTTP) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { func (c *HTTP) BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResultBlockchainInfo, error) { tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("blockchain", []interface{}{minHeight, maxHeight}, tmResult) + _, err := c.rpc.Call("blockchain", + map[string]interface{}{"minHeight": minHeight, "maxHeight": maxHeight}, + tmResult) if err != nil { return nil, errors.Wrap(err, "BlockchainInfo") } @@ -131,7 +135,7 @@ func (c *HTTP) BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResultBlockchai func (c *HTTP) Genesis() (*ctypes.ResultGenesis, error) { tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("genesis", nil, tmResult) + _, err := c.rpc.Call("genesis", map[string]interface{}{}, tmResult) if err != nil { return nil, errors.Wrap(err, "Genesis") } @@ -140,7 +144,7 @@ func (c *HTTP) Genesis() (*ctypes.ResultGenesis, error) { func (c *HTTP) Block(height int) (*ctypes.ResultBlock, error) { tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("block", []interface{}{height}, tmResult) + _, err := c.rpc.Call("block", map[string]interface{}{"height": height}, tmResult) if err != nil { return nil, errors.Wrap(err, "Block") } @@ -149,7 +153,7 @@ func (c *HTTP) Block(height int) (*ctypes.ResultBlock, error) { func (c *HTTP) Commit(height int) (*ctypes.ResultCommit, error) { tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("commit", []interface{}{height}, tmResult) + _, err := c.rpc.Call("commit", map[string]interface{}{"height": height}, tmResult) if err != nil { return nil, errors.Wrap(err, "Commit") } @@ -158,7 +162,7 @@ func (c *HTTP) Commit(height int) (*ctypes.ResultCommit, error) { func (c *HTTP) Validators() (*ctypes.ResultValidators, error) { tmResult := new(ctypes.TMResult) - _, err := c.rpc.Call("validators", nil, tmResult) + _, err := c.rpc.Call("validators", map[string]interface{}{}, tmResult) if err != nil { return nil, errors.Wrap(err, "Validators") } diff --git a/rpc/test/client_test.go b/rpc/test/client_test.go index 1141de4ee..43409c723 100644 --- a/rpc/test/client_test.go +++ b/rpc/test/client_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" abci "github.com/tendermint/abci/types" . "github.com/tendermint/go-common" + rpc "github.com/tendermint/go-rpc/client" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/tendermint/tendermint/types" ) @@ -25,24 +26,20 @@ import ( // status func TestURIStatus(t *testing.T) { - tmResult := new(ctypes.TMResult) - _, err := GetURIClient().Call("status", map[string]interface{}{}, tmResult) - require.Nil(t, err) - testStatus(t, tmResult) + testStatus(t, GetURIClient()) } func TestJSONStatus(t *testing.T) { - tmResult := new(ctypes.TMResult) - _, err := GetJSONClient().Call("status", []interface{}{}, tmResult) - require.Nil(t, err) - testStatus(t, tmResult) + testStatus(t, GetJSONClient()) } -func testStatus(t *testing.T, statusI interface{}) { +func testStatus(t *testing.T, client rpc.HTTPClient) { chainID := GetConfig().GetString("chain_id") + tmResult := new(ctypes.TMResult) + _, err := client.Call("status", map[string]interface{}{}, tmResult) + require.Nil(t, err) - tmRes := statusI.(*ctypes.TMResult) - status := (*tmRes).(*ctypes.ResultStatus) + status := (*tmResult).(*ctypes.ResultStatus) assert.Equal(t, chainID, status.NodeInfo.Network) } @@ -59,28 +56,22 @@ func randBytes(t *testing.T) []byte { } func TestURIBroadcastTxSync(t *testing.T) { - config.Set("block_size", 0) - defer config.Set("block_size", -1) - tmResult := new(ctypes.TMResult) - tx := randBytes(t) - _, err := GetURIClient().Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, tmResult) - require.Nil(t, err) - testBroadcastTxSync(t, tmResult, tx) + testBroadcastTxSync(t, GetURIClient()) } func TestJSONBroadcastTxSync(t *testing.T) { + testBroadcastTxSync(t, GetJSONClient()) +} + +func testBroadcastTxSync(t *testing.T, client rpc.HTTPClient) { config.Set("block_size", 0) defer config.Set("block_size", -1) tmResult := new(ctypes.TMResult) tx := randBytes(t) - _, err := GetJSONClient().Call("broadcast_tx_sync", []interface{}{tx}, tmResult) + _, err := client.Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, tmResult) require.Nil(t, err) - testBroadcastTxSync(t, tmResult, tx) -} -func testBroadcastTxSync(t *testing.T, resI interface{}, tx []byte) { - tmRes := resI.(*ctypes.TMResult) - res := (*tmRes).(*ctypes.ResultBroadcastTx) + res := (*tmResult).(*ctypes.ResultBroadcastTx) require.Equal(t, abci.CodeType_OK, res.Code) mem := node.MempoolReactor().Mempool require.Equal(t, 1, mem.Size()) @@ -98,34 +89,31 @@ func testTxKV(t *testing.T) ([]byte, []byte, []byte) { return k, v, []byte(Fmt("%s=%s", k, v)) } -func sendTx(t *testing.T) ([]byte, []byte) { +func sendTx(t *testing.T, client rpc.HTTPClient) ([]byte, []byte) { tmResult := new(ctypes.TMResult) k, v, tx := testTxKV(t) - _, err := GetJSONClient().Call("broadcast_tx_commit", []interface{}{tx}, tmResult) + _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, tmResult) require.Nil(t, err) return k, v } func TestURIABCIQuery(t *testing.T) { - k, v := sendTx(t) - time.Sleep(time.Second) - tmResult := new(ctypes.TMResult) - _, err := GetURIClient().Call("abci_query", map[string]interface{}{"path": "", "data": k, "prove": false}, tmResult) - require.Nil(t, err) - testABCIQuery(t, tmResult, v) + testABCIQuery(t, GetURIClient()) } func TestJSONABCIQuery(t *testing.T) { - k, v := sendTx(t) + testABCIQuery(t, GetURIClient()) +} + +func testABCIQuery(t *testing.T, client rpc.HTTPClient) { + k, _ := sendTx(t, client) + time.Sleep(time.Millisecond * 100) tmResult := new(ctypes.TMResult) - _, err := GetJSONClient().Call("abci_query", []interface{}{"", k, false}, tmResult) + _, err := client.Call("abci_query", + map[string]interface{}{"path": "", "data": k, "prove": false}, tmResult) require.Nil(t, err) - testABCIQuery(t, tmResult, v) -} -func testABCIQuery(t *testing.T, statusI interface{}, value []byte) { - tmRes := statusI.(*ctypes.TMResult) - resQuery := (*tmRes).(*ctypes.ResultABCIQuery) + resQuery := (*tmResult).(*ctypes.ResultABCIQuery) require.EqualValues(t, 0, resQuery.Response.Code) // XXX: specific to value returned by the dummy @@ -136,25 +124,22 @@ func testABCIQuery(t *testing.T, statusI interface{}, value []byte) { // broadcast tx commit func TestURIBroadcastTxCommit(t *testing.T) { - tmResult := new(ctypes.TMResult) - tx := randBytes(t) - _, err := GetURIClient().Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, tmResult) - require.Nil(t, err) - testBroadcastTxCommit(t, tmResult, tx) + testBroadcastTxCommit(t, GetURIClient()) } func TestJSONBroadcastTxCommit(t *testing.T) { - tmResult := new(ctypes.TMResult) - tx := randBytes(t) - _, err := GetJSONClient().Call("broadcast_tx_commit", []interface{}{tx}, tmResult) - require.Nil(t, err) - testBroadcastTxCommit(t, tmResult, tx) + testBroadcastTxCommit(t, GetJSONClient()) } -func testBroadcastTxCommit(t *testing.T, resI interface{}, tx []byte) { +func testBroadcastTxCommit(t *testing.T, client rpc.HTTPClient) { require := require.New(t) - tmRes := resI.(*ctypes.TMResult) - res := (*tmRes).(*ctypes.ResultBroadcastTxCommit) + + tmResult := new(ctypes.TMResult) + tx := randBytes(t) + _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, tmResult) + require.Nil(err) + + res := (*tmResult).(*ctypes.ResultBroadcastTxCommit) checkTx := res.CheckTx require.Equal(abci.CodeType_OK, checkTx.Code) deliverTx := res.DeliverTx @@ -240,7 +225,7 @@ func TestWSTxEvent(t *testing.T) { // send an tx tmResult := new(ctypes.TMResult) - _, err := GetJSONClient().Call("broadcast_tx_sync", []interface{}{tx}, tmResult) + _, err := GetJSONClient().Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, tmResult) require.Nil(err) waitForEvent(t, wsc, eid, true, func() {}, func(eid string, b interface{}) error { @@ -310,7 +295,9 @@ func TestURIUnsafeSetConfig(t *testing.T) { func TestJSONUnsafeSetConfig(t *testing.T) { for _, testCase := range testCasesUnsafeSetConfig { tmResult := new(ctypes.TMResult) - _, err := GetJSONClient().Call("unsafe_set_config", []interface{}{testCase[0], testCase[1], testCase[2]}, tmResult) + _, err := GetJSONClient().Call("unsafe_set_config", + map[string]interface{}{"type": testCase[0], "key": testCase[1], "value": testCase[2]}, + tmResult) require.Nil(t, err) } testUnsafeSetConfig(t) diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index cf64ac155..349980e9c 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -72,15 +72,15 @@ func GetConfig() cfg.Config { } // GetURIClient gets a uri client pointing to the test tendermint rpc -func GetURIClient() *client.ClientURI { +func GetURIClient() *client.URIClient { rpcAddr := GetConfig().GetString("rpc_laddr") - return client.NewClientURI(rpcAddr) + return client.NewURIClient(rpcAddr) } // GetJSONClient gets a http/json client pointing to the test tendermint rpc -func GetJSONClient() *client.ClientJSONRPC { +func GetJSONClient() *client.JSONRPCClient { rpcAddr := GetConfig().GetString("rpc_laddr") - return client.NewClientJSONRPC(rpcAddr) + return client.NewJSONRPCClient(rpcAddr) } func GetGRPCClient() core_grpc.BroadcastAPIClient { diff --git a/scripts/install_abci_apps.sh b/scripts/install_abci_apps.sh index 6da2e2ed2..b2c0b085b 100644 --- a/scripts/install_abci_apps.sh +++ b/scripts/install_abci_apps.sh @@ -1,6 +1,6 @@ #! /bin/bash -go get github.com/tendermint/abci/... +go get -d github.com/tendermint/abci # get the abci commit used by tendermint COMMIT=`bash scripts/glide/parse.sh abci` diff --git a/types/tx.go b/types/tx.go index d2b4ee8df..c47774b8f 100644 --- a/types/tx.go +++ b/types/tx.go @@ -1,6 +1,9 @@ package types import ( + "bytes" + "errors" + abci "github.com/tendermint/abci/types" "github.com/tendermint/go-merkle" ) @@ -32,6 +35,63 @@ func (txs Txs) Hash() []byte { } } +// Index returns the index of this transaction in the list, or -1 if not found +func (txs Txs) Index(tx Tx) int { + for i := range txs { + if bytes.Equal(txs[i], tx) { + return i + } + } + return -1 +} + +// Proof returns a simple merkle proof for this node. +// +// Panics if i < 0 or i >= len(txs) +// +// TODO: optimize this! +func (txs Txs) Proof(i int) TxProof { + l := len(txs) + hashables := make([]merkle.Hashable, l) + for i := 0; i < l; i++ { + hashables[i] = txs[i] + } + root, proofs := merkle.SimpleProofsFromHashables(hashables) + + return TxProof{ + Index: i, + Total: l, + RootHash: root, + Data: txs[i], + Proof: *proofs[i], + } +} + +type TxProof struct { + Index, Total int + RootHash []byte + Data Tx + Proof merkle.SimpleProof +} + +func (tp TxProof) LeafHash() []byte { + return tp.Data.Hash() +} + +// Validate returns nil if it matches the dataHash, and is internally consistent +// otherwise, returns a sensible error +func (tp TxProof) Validate(dataHash []byte) error { + if !bytes.Equal(dataHash, tp.RootHash) { + return errors.New("Proof matches different data hash") + } + + valid := tp.Proof.Verify(tp.Index, tp.Total, tp.LeafHash(), tp.RootHash) + if !valid { + return errors.New("Proof is not internally consistent") + } + return nil +} + // TxResult contains results of executing the transaction. // // One usage is indexing transaction results. diff --git a/types/tx_test.go b/types/tx_test.go new file mode 100644 index 000000000..7688a9bf1 --- /dev/null +++ b/types/tx_test.go @@ -0,0 +1,122 @@ +package types + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + cmn "github.com/tendermint/go-common" + ctest "github.com/tendermint/go-common/test" + wire "github.com/tendermint/go-wire" +) + +func makeTxs(cnt, size int) Txs { + txs := make(Txs, cnt) + for i := 0; i < cnt; i++ { + txs[i] = cmn.RandBytes(size) + } + return txs +} + +func randInt(low, high int) int { + off := cmn.RandInt() % (high - low) + return low + off +} + +func TestTxIndex(t *testing.T) { + assert := assert.New(t) + for i := 0; i < 20; i++ { + txs := makeTxs(15, 60) + for j := 0; j < len(txs); j++ { + tx := txs[j] + idx := txs.Index(tx) + assert.Equal(j, idx) + } + assert.Equal(-1, txs.Index(nil)) + assert.Equal(-1, txs.Index(Tx("foodnwkf"))) + } +} + +func TestValidTxProof(t *testing.T) { + assert := assert.New(t) + cases := []struct { + txs Txs + }{ + {Txs{{1, 4, 34, 87, 163, 1}}}, + {Txs{{5, 56, 165, 2}, {4, 77}}}, + {Txs{Tx("foo"), Tx("bar"), Tx("baz")}}, + {makeTxs(20, 5)}, + {makeTxs(7, 81)}, + {makeTxs(61, 15)}, + } + + for h, tc := range cases { + txs := tc.txs + root := txs.Hash() + // make sure valid proof for every tx + for i := range txs { + leaf := txs[i] + leafHash := leaf.Hash() + proof := txs.Proof(i) + assert.Equal(i, proof.Index, "%d: %d", h, i) + assert.Equal(len(txs), proof.Total, "%d: %d", h, i) + assert.Equal(root, proof.RootHash, "%d: %d", h, i) + assert.Equal(leaf, proof.Data, "%d: %d", h, i) + assert.Equal(leafHash, proof.LeafHash(), "%d: %d", h, i) + assert.Nil(proof.Validate(root), "%d: %d", h, i) + assert.NotNil(proof.Validate([]byte("foobar")), "%d: %d", h, i) + + // read-write must also work + var p2 TxProof + bin := wire.BinaryBytes(proof) + err := wire.ReadBinaryBytes(bin, &p2) + if assert.Nil(err, "%d: %d: %+v", h, i, err) { + assert.Nil(p2.Validate(root), "%d: %d", h, i) + } + } + } +} + +func TestTxProofUnchangable(t *testing.T) { + // run the other test a bunch... + for i := 0; i < 40; i++ { + testTxProofUnchangable(t) + } +} + +func testTxProofUnchangable(t *testing.T) { + assert := assert.New(t) + + // make some proof + txs := makeTxs(randInt(2, 100), randInt(16, 128)) + root := txs.Hash() + i := randInt(0, len(txs)-1) + proof := txs.Proof(i) + + // make sure it is valid to start with + assert.Nil(proof.Validate(root)) + bin := wire.BinaryBytes(proof) + + // try mutating the data and make sure nothing breaks + for j := 0; j < 500; j++ { + bad := ctest.MutateByteSlice(bin) + if !bytes.Equal(bad, bin) { + assertBadProof(t, root, bad, proof) + } + } +} + +// this make sure the proof doesn't deserialize into something valid +func assertBadProof(t *testing.T, root []byte, bad []byte, good TxProof) { + var proof TxProof + err := wire.ReadBinaryBytes(bad, &proof) + if err == nil { + err = proof.Validate(root) + if err == nil { + // okay, this can happen if we have a slightly different total + // (where the path ends up the same), if it is something else, we have + // a real problem + assert.NotEqual(t, proof.Total, good.Total, "bad: %#v\ngood: %#v", proof, good) + } + } +}