diff --git a/README.md b/README.md
index ea09fa7c..3a9b27cb 100644
--- a/README.md
+++ b/README.md
@@ -2,9 +2,8 @@
[](https://circleci.com/gh/tendermint/abci)
-Blockchains are a system for creating shared multi-master application state.
+Blockchains are a system for creating shared multi-master application state.
**ABCI** is a socket protocol enabling a blockchain consensus engine, running in one process,
-to manage a blockchain application state, running in another.
For more information on ABCI, motivations, and tutorials, please visit [our blog post](https://tendermint.com/blog/abci-the-tendermint-socket-protocol).
@@ -18,15 +17,15 @@ Other implementations:
This repository holds a number of important pieces:
- `types/types.proto`
- - the protobuf file defining ABCI message types, and the optional grpc interface.
+ - the protobuf file defining ABCI message types, and the optional grpc interface.
- to build, run `make protoc`
- see `protoc --help` and [the grpc docs](https://www.grpc.io/docs) for examples and details of other languages
- golang implementation of ABCI client and server
- two implementations:
- - asynchronous, ordered message passing over unix or tcp;
+ - asynchronous, ordered message passing over unix or tcp;
- messages are serialized using protobuf and length prefixed
- - grpc
+ - grpc
- TendermintCore runs a client, and the application runs a server
- `cmd/abci-cli`
@@ -77,7 +76,7 @@ ABCI requests/responses are simple Protobuf messages. Check out the [schema fil
You can make CheckTx semi-stateful and clear the state upon `Commit` or `BeginBlock`,
to allow for dependent sequences of transactions in the same block.
-#### Commit
+#### Commit
* __Returns__:
* `Data ([]byte)`: The Merkle root hash
* `Log (string)`: Debug or error message
@@ -92,6 +91,17 @@ ABCI requests/responses are simple Protobuf messages. Check out the [schema fil
* `Data ([]byte)`: The query response bytes
* `Log (string)`: Debug or error message
+#### Proof
+ * __Arguments__:
+ * `Key ([]byte)`: The key whose data you want to verifiably query
+ * `Height (int64)`: The block height for which you want the proof (default=0 returns the proof for last committed block)
+ * __Returns__:
+ * `Code (uint32)`: Response code
+ * `Data ([]byte)`: The query response bytes
+ * `Log (string)`: Debug or error message
+ * __Usage__:
+ Return a Merkle proof from the key/value pair back to the application hash.
+
#### Flush
* __Usage__:
Flush the response queue. Applications that implement `types.Application` need not implement this message -- it's handled by the project.
diff --git a/example/dummy/dummy_test.go b/example/dummy/dummy_test.go
index 8cd999c0..aa6c837d 100644
--- a/example/dummy/dummy_test.go
+++ b/example/dummy/dummy_test.go
@@ -6,56 +6,40 @@ import (
"sort"
"testing"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ abcicli "github.com/tendermint/abci/client"
+ "github.com/tendermint/abci/server"
+ "github.com/tendermint/abci/types"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-crypto"
merkle "github.com/tendermint/go-merkle"
"github.com/tendermint/go-wire"
- "github.com/tendermint/abci/types"
)
-func testDummy(t *testing.T, dummy types.Application, tx []byte, key, value string) {
- if r := dummy.DeliverTx(tx); r.IsErr() {
- t.Fatal(r)
- }
- if r := dummy.DeliverTx(tx); r.IsErr() {
- t.Fatal(r)
- }
-
- r := dummy.Query([]byte(key))
- if r.IsErr() {
- t.Fatal(r)
- }
+func testDummy(t *testing.T, app types.Application, tx []byte, key, value string) {
+ ar := app.DeliverTx(tx)
+ require.False(t, ar.IsErr(), ar)
+ // repeating tx doesn't raise error
+ ar = app.DeliverTx(tx)
+ require.False(t, ar.IsErr(), ar)
+ // make sure query is fine
+ r := app.Query([]byte(key))
+ require.False(t, r.IsErr(), r)
q := new(QueryResult)
- if err := wire.ReadJSONBytes(r.Data, q); err != nil {
- t.Fatal(err)
- }
-
- if q.Value != value {
- t.Fatalf("Got %s, expected %s", q.Value, value)
- }
-
- rp := dummy.Proof([]byte(key), 0)
- if rp.IsErr() {
- t.Fatal(rp)
- }
+ err := wire.ReadJSONBytes(r.Data, q)
+ require.Nil(t, err)
+ require.Equal(t, value, q.Value)
+ // make sure proof is fine
+ rp := app.Proof([]byte(key), 0)
+ require.False(t, rp.IsErr(), rp)
p, err := merkle.LoadProof(rp.Data)
- if err != nil {
- t.Fatal(err)
- }
-
- if !p.Valid() {
- t.Fatal("Invalid proof")
- }
-
- if !bytes.Equal([]byte(key), p.Key()) {
- t.Fatalf("Invalid key: %s", p.Key())
- }
-
- if !bytes.Equal([]byte(value), p.Value()) {
- t.Fatalf("Invalid key: %s", p.Value())
- }
+ require.Nil(t, err)
+ require.True(t, p.Valid())
+ assert.Equal(t, []byte(key), p.Key())
+ assert.Equal(t, []byte(value), p.Value())
}
func TestDummyKV(t *testing.T) {
@@ -223,3 +207,97 @@ func valsEqual(t *testing.T, vals1, vals2 []*types.Validator) {
}
}
}
+
+func makeSocketClientServer(app types.Application, name string) (abcicli.Client, Service, error) {
+ // Start the listener
+ socket := Fmt("unix://%s.sock", name)
+ server, err := server.NewSocketServer(socket, app)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Connect to the socket
+ client, err := abcicli.NewSocketClient(socket, false)
+ if err != nil {
+ server.Stop()
+ return nil, nil, err
+ }
+ client.Start()
+
+ return client, server, err
+}
+
+func makeGRPCClientServer(app types.Application, name string) (abcicli.Client, Service, error) {
+ // Start the listener
+ socket := Fmt("unix://%s.sock", name)
+
+ gapp := types.NewGRPCApplication(app)
+ server, err := server.NewGRPCServer(socket, gapp)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ client, err := abcicli.NewGRPCClient(socket, true)
+ if err != nil {
+ server.Stop()
+ return nil, nil, err
+ }
+ return client, server, err
+}
+
+func TestClientServer(t *testing.T) {
+ // set up socket app
+ dummy := NewDummyApplication()
+ client, server, err := makeSocketClientServer(dummy, "dummy-socket")
+ require.Nil(t, err)
+ defer server.Stop()
+ defer client.Stop()
+
+ runClientTests(t, client)
+
+ // set up grpc app
+ dummy = NewDummyApplication()
+ gclient, gserver, err := makeGRPCClientServer(dummy, "dummy-grpc")
+ require.Nil(t, err)
+ defer gserver.Stop()
+ defer gclient.Stop()
+
+ runClientTests(t, gclient)
+}
+
+func runClientTests(t *testing.T, client abcicli.Client) {
+ // run some tests....
+ key := "abc"
+ value := key
+ tx := []byte(key)
+ testClient(t, client, tx, key, value)
+
+ value = "def"
+ tx = []byte(key + "=" + value)
+ testClient(t, client, tx, key, value)
+}
+
+func testClient(t *testing.T, app abcicli.Client, tx []byte, key, value string) {
+ ar := app.DeliverTxSync(tx)
+ require.False(t, ar.IsErr(), ar)
+ // repeating tx doesn't raise error
+ ar = app.DeliverTxSync(tx)
+ require.False(t, ar.IsErr(), ar)
+
+ // make sure query is fine
+ r := app.QuerySync([]byte(key))
+ require.False(t, r.IsErr(), r)
+ q := new(QueryResult)
+ err := wire.ReadJSONBytes(r.Data, q)
+ require.Nil(t, err)
+ require.Equal(t, value, q.Value)
+
+ // make sure proof is fine
+ rp := app.ProofSync([]byte(key), 0)
+ require.False(t, rp.IsErr(), rp)
+ p, err := merkle.LoadProof(rp.Data)
+ require.Nil(t, err)
+ require.True(t, p.Valid())
+ assert.Equal(t, []byte(key), p.Key())
+ assert.Equal(t, []byte(value), p.Value())
+}
diff --git a/example/dummy/dummy_test.go.orig b/example/dummy/dummy_test.go.orig
new file mode 100644
index 00000000..5cf7eee7
--- /dev/null
+++ b/example/dummy/dummy_test.go.orig
@@ -0,0 +1,321 @@
+package dummy
+
+import (
+ "bytes"
+ "io/ioutil"
+ "sort"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ . "github.com/tendermint/go-common"
+ "github.com/tendermint/go-crypto"
+ merkle "github.com/tendermint/go-merkle"
+ "github.com/tendermint/go-wire"
+<<<<<<< HEAD
+ "github.com/tendermint/abci/types"
+)
+
+func testDummy(t *testing.T, dummy types.Application, tx []byte, key, value string) {
+ if r := dummy.DeliverTx(tx); r.IsErr() {
+ t.Fatal(r)
+ }
+ if r := dummy.DeliverTx(tx); r.IsErr() {
+ t.Fatal(r)
+ }
+
+ r := dummy.Query([]byte(key))
+ if r.IsErr() {
+ t.Fatal(r)
+ }
+=======
+ tmspcli "github.com/tendermint/tmsp/client"
+ "github.com/tendermint/tmsp/server"
+ "github.com/tendermint/tmsp/types"
+)
+
+func testDummy(t *testing.T, app types.Application, tx []byte, key, value string) {
+ ar := app.AppendTx(tx)
+ require.False(t, ar.IsErr(), ar)
+ // repeating tx doesn't raise error
+ ar = app.AppendTx(tx)
+ require.False(t, ar.IsErr(), ar)
+>>>>>>> Add tests for client-server proofs over socket and grpc
+
+ // make sure query is fine
+ r := app.Query([]byte(key))
+ require.False(t, r.IsErr(), r)
+ q := new(QueryResult)
+ err := wire.ReadJSONBytes(r.Data, q)
+ require.Nil(t, err)
+ require.Equal(t, value, q.Value)
+
+ // make sure proof is fine
+ rp := app.Proof([]byte(key), 0)
+ require.False(t, rp.IsErr(), rp)
+ p, err := merkle.LoadProof(rp.Data)
+ require.Nil(t, err)
+ require.True(t, p.Valid())
+ assert.Equal(t, []byte(key), p.Key())
+ assert.Equal(t, []byte(value), p.Value())
+}
+
+func TestDummyKV(t *testing.T) {
+ dummy := NewDummyApplication()
+ key := "abc"
+ value := key
+ tx := []byte(key)
+ testDummy(t, dummy, tx, key, value)
+
+ value = "def"
+ tx = []byte(key + "=" + value)
+ testDummy(t, dummy, tx, key, value)
+}
+
+func TestPersistentDummyKV(t *testing.T) {
+ dir, err := ioutil.TempDir("/tmp", "abci-dummy-test") // TODO
+ if err != nil {
+ t.Fatal(err)
+ }
+ dummy := NewPersistentDummyApplication(dir)
+ key := "abc"
+ value := key
+ tx := []byte(key)
+ testDummy(t, dummy, tx, key, value)
+
+ value = "def"
+ tx = []byte(key + "=" + value)
+ testDummy(t, dummy, tx, key, value)
+}
+
+func TestPersistentDummyInfo(t *testing.T) {
+ dir, err := ioutil.TempDir("/tmp", "abci-dummy-test") // TODO
+ if err != nil {
+ t.Fatal(err)
+ }
+ dummy := NewPersistentDummyApplication(dir)
+ height := uint64(0)
+
+ resInfo := dummy.Info()
+ if resInfo.LastBlockHeight != height {
+ t.Fatalf("expected height of %d, got %d", height, resInfo.LastBlockHeight)
+ }
+
+ // make and apply block
+ height = uint64(1)
+ hash := []byte("foo")
+ header := &types.Header{
+ Height: uint64(height),
+ }
+ dummy.BeginBlock(hash, header)
+ dummy.EndBlock(height)
+ dummy.Commit()
+
+ resInfo = dummy.Info()
+ if resInfo.LastBlockHeight != height {
+ t.Fatalf("expected height of %d, got %d", height, resInfo.LastBlockHeight)
+ }
+
+}
+
+// add a validator, remove a validator, update a validator
+func TestValSetChanges(t *testing.T) {
+ dir, err := ioutil.TempDir("/tmp", "abci-dummy-test") // TODO
+ if err != nil {
+ t.Fatal(err)
+ }
+ dummy := NewPersistentDummyApplication(dir)
+
+ // init with some validators
+ total := 10
+ nInit := 5
+ vals := make([]*types.Validator, total)
+ for i := 0; i < total; i++ {
+ pubkey := crypto.GenPrivKeyEd25519FromSecret([]byte(Fmt("test%d", i))).PubKey().Bytes()
+ power := RandInt()
+ vals[i] = &types.Validator{pubkey, uint64(power)}
+ }
+ // iniitalize with the first nInit
+ dummy.InitChain(vals[:nInit])
+
+ vals1, vals2 := vals[:nInit], dummy.Validators()
+ valsEqual(t, vals1, vals2)
+
+ var v1, v2, v3 *types.Validator
+
+ // add some validators
+ v1, v2 = vals[nInit], vals[nInit+1]
+ diff := []*types.Validator{v1, v2}
+ tx1 := MakeValSetChangeTx(v1.PubKey, v1.Power)
+ tx2 := MakeValSetChangeTx(v2.PubKey, v2.Power)
+
+ makeApplyBlock(t, dummy, 1, diff, tx1, tx2)
+
+ vals1, vals2 = vals[:nInit+2], dummy.Validators()
+ valsEqual(t, vals1, vals2)
+
+ // remove some validators
+ v1, v2, v3 = vals[nInit-2], vals[nInit-1], vals[nInit]
+ v1.Power = 0
+ v2.Power = 0
+ v3.Power = 0
+ diff = []*types.Validator{v1, v2, v3}
+ tx1 = MakeValSetChangeTx(v1.PubKey, v1.Power)
+ tx2 = MakeValSetChangeTx(v2.PubKey, v2.Power)
+ tx3 := MakeValSetChangeTx(v3.PubKey, v3.Power)
+
+ makeApplyBlock(t, dummy, 2, diff, tx1, tx2, tx3)
+
+ vals1 = append(vals[:nInit-2], vals[nInit+1])
+ vals2 = dummy.Validators()
+ valsEqual(t, vals1, vals2)
+
+ // update some validators
+ v1 = vals[0]
+ if v1.Power == 5 {
+ v1.Power = 6
+ } else {
+ v1.Power = 5
+ }
+ diff = []*types.Validator{v1}
+ tx1 = MakeValSetChangeTx(v1.PubKey, v1.Power)
+
+ makeApplyBlock(t, dummy, 3, diff, tx1)
+
+ vals1 = append([]*types.Validator{v1}, vals1[1:len(vals1)]...)
+ vals2 = dummy.Validators()
+ valsEqual(t, vals1, vals2)
+
+}
+
+func makeApplyBlock(t *testing.T, dummy types.Application, heightInt int, diff []*types.Validator, txs ...[]byte) {
+ // make and apply block
+ height := uint64(heightInt)
+ hash := []byte("foo")
+ header := &types.Header{
+ Height: height,
+ }
+
+ dummyChain := dummy.(types.BlockchainAware) // hmm...
+ dummyChain.BeginBlock(hash, header)
+ for _, tx := range txs {
+ if r := dummy.DeliverTx(tx); r.IsErr() {
+ t.Fatal(r)
+ }
+ }
+ resEndBlock := dummyChain.EndBlock(height)
+ dummy.Commit()
+
+ valsEqual(t, diff, resEndBlock.Diffs)
+
+}
+
+// order doesn't matter
+func valsEqual(t *testing.T, vals1, vals2 []*types.Validator) {
+ if len(vals1) != len(vals2) {
+ t.Fatalf("vals dont match in len. got %d, expected %d", len(vals2), len(vals1))
+ }
+ sort.Sort(types.Validators(vals1))
+ sort.Sort(types.Validators(vals2))
+ for i, v1 := range vals1 {
+ v2 := vals2[i]
+ if !bytes.Equal(v1.PubKey, v2.PubKey) ||
+ v1.Power != v2.Power {
+ t.Fatalf("vals dont match at index %d. got %X/%d , expected %X/%d", i, v2.PubKey, v2.Power, v1.PubKey, v1.Power)
+ }
+ }
+}
+
+func makeSocketClientServer(app types.Application, name string) (tmspcli.Client, Service, error) {
+ // Start the listener
+ socket := Fmt("unix://%s.sock", name)
+ server, err := server.NewSocketServer(socket, app)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Connect to the socket
+ client, err := tmspcli.NewSocketClient(socket, false)
+ if err != nil {
+ server.Stop()
+ return nil, nil, err
+ }
+ client.Start()
+
+ return client, server, err
+}
+
+func makeGRPCClientServer(app types.Application, name string) (tmspcli.Client, Service, error) {
+ // Start the listener
+ socket := Fmt("unix://%s.sock", name)
+
+ gapp := types.NewGRPCApplication(app)
+ server, err := server.NewGRPCServer(socket, gapp)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ client, err := tmspcli.NewGRPCClient(socket, true)
+ if err != nil {
+ server.Stop()
+ return nil, nil, err
+ }
+ return client, server, err
+}
+
+func TestClientServer(t *testing.T) {
+ // set up socket app
+ dummy := NewDummyApplication()
+ client, server, err := makeSocketClientServer(dummy, "dummy-socket")
+ require.Nil(t, err)
+ defer server.Stop()
+ defer client.Stop()
+
+ runClientTests(t, client)
+
+ // set up grpc app
+ dummy = NewDummyApplication()
+ gclient, gserver, err := makeGRPCClientServer(dummy, "dummy-grpc")
+ require.Nil(t, err)
+ defer gserver.Stop()
+ defer gclient.Stop()
+
+ runClientTests(t, gclient)
+}
+
+func runClientTests(t *testing.T, client tmspcli.Client) {
+ // run some tests....
+ key := "abc"
+ value := key
+ tx := []byte(key)
+ testClient(t, client, tx, key, value)
+
+ value = "def"
+ tx = []byte(key + "=" + value)
+ testClient(t, client, tx, key, value)
+}
+
+func testClient(t *testing.T, app tmspcli.Client, tx []byte, key, value string) {
+ ar := app.AppendTxSync(tx)
+ require.False(t, ar.IsErr(), ar)
+ // repeating tx doesn't raise error
+ ar = app.AppendTxSync(tx)
+ require.False(t, ar.IsErr(), ar)
+
+ // make sure query is fine
+ r := app.QuerySync([]byte(key))
+ require.False(t, r.IsErr(), r)
+ q := new(QueryResult)
+ err := wire.ReadJSONBytes(r.Data, q)
+ require.Nil(t, err)
+ require.Equal(t, value, q.Value)
+
+ // make sure proof is fine
+ rp := app.ProofSync([]byte(key), 0)
+ require.False(t, rp.IsErr(), rp)
+ p, err := merkle.LoadProof(rp.Data)
+ require.Nil(t, err)
+ require.True(t, p.Valid())
+ assert.Equal(t, []byte(key), p.Key())
+ assert.Equal(t, []byte(value), p.Value())
+}
diff --git a/glide.lock b/glide.lock
index 42776a3a..4829e672 100644
--- a/glide.lock
+++ b/glide.lock
@@ -19,6 +19,11 @@ imports:
version: d228849504861217f796da67fae4f6e347643f15
- name: github.com/mattn/go-isatty
version: 30a891c33c7cde7b02a981314b4228ec99380cca
+- name: github.com/stretchr/testify
+ version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
+ subpackages:
+ - assert
+ - require
- name: github.com/syndtr/goleveldb
version: 23851d93a2292dcc56e71a18ec9e0624d84a0f65
subpackages:
@@ -102,4 +107,12 @@ imports:
- stats
- tap
- transport
-testImports: []
+testImports:
+- name: github.com/davecgh/go-spew
+ version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
+ subpackages:
+ - spew
+- name: github.com/pmezard/go-difflib
+ version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
+ subpackages:
+ - difflib
diff --git a/glide.yaml b/glide.yaml
index 295b3d3e..af174387 100644
--- a/glide.yaml
+++ b/glide.yaml
@@ -15,3 +15,7 @@ import:
subpackages:
- context
- package: google.golang.org/grpc
+- package: github.com/stretchr/testify
+ version: ^1.1.4
+ subpackages:
+ - require