Browse Source

Merge branch 'feature/tx-proof' into feature/237-tx-indexing

Conflicts:
	types/tx.go
pull/412/head
Ethan Buchman 7 years ago
parent
commit
7fb0e8b30b
9 changed files with 268 additions and 89 deletions
  1. +3
    -1
      benchmarks/simu/counter.go
  2. +17
    -15
      glide.lock
  3. +2
    -0
      glide.yaml
  4. +18
    -14
      rpc/client/httpclient.go
  5. +41
    -54
      rpc/test/client_test.go
  6. +4
    -4
      rpc/test/helpers.go
  7. +1
    -1
      scripts/install_abci_apps.sh
  8. +60
    -0
      types/tx.go
  9. +122
    -0
      types/tx_test.go

+ 3
- 1
benchmarks/simu/counter.go View File

@ -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(".")


+ 17
- 15
glide.lock View File

@ -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


+ 2
- 0
glide.yaml View File

@ -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


+ 18
- 14
rpc/client/httpclient.go View File

@ -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")
}


+ 41
- 54
rpc/test/client_test.go View File

@ -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)


+ 4
- 4
rpc/test/helpers.go View File

@ -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 {


+ 1
- 1
scripts/install_abci_apps.sh View File

@ -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`


+ 60
- 0
types/tx.go View File

@ -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.


+ 122
- 0
types/tx_test.go View File

@ -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)
}
}
}

Loading…
Cancel
Save