mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-22 09:21:32 +00:00
Merge pull request #999 from tendermint/feature/result-hash-header
Add results hash to header
This commit is contained in:
@ -301,7 +301,10 @@ func (h *Handshaker) ReplayBlocks(appHash []byte, appBlockHeight int64, proxyApp
|
|||||||
|
|
||||||
} else if appBlockHeight == storeBlockHeight {
|
} else if appBlockHeight == storeBlockHeight {
|
||||||
// We ran Commit, but didn't save the state, so replayBlock with mock app
|
// We ran Commit, but didn't save the state, so replayBlock with mock app
|
||||||
abciResponses := h.state.LoadABCIResponses()
|
abciResponses, err := h.state.LoadABCIResponses(storeBlockHeight)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
mockApp := newMockProxyApp(appHash, abciResponses)
|
mockApp := newMockProxyApp(appHash, abciResponses)
|
||||||
h.logger.Info("Replay last block using mock app")
|
h.logger.Info("Replay last block using mock app")
|
||||||
return h.replayBlock(storeBlockHeight, mockApp)
|
return h.replayBlock(storeBlockHeight, mockApp)
|
||||||
|
@ -107,14 +107,15 @@ func TestWALCrash(t *testing.T) {
|
|||||||
{"block with a smaller part size",
|
{"block with a smaller part size",
|
||||||
func(cs *ConsensusState, ctx context.Context) {
|
func(cs *ConsensusState, ctx context.Context) {
|
||||||
// XXX: is there a better way to change BlockPartSizeBytes?
|
// XXX: is there a better way to change BlockPartSizeBytes?
|
||||||
params := cs.state.ConsensusParams
|
cs.state.ConsensusParams.BlockPartSizeBytes = 512
|
||||||
params.BlockPartSizeBytes = 512
|
cs.state.Save()
|
||||||
cs.state.ConsensusParams = params
|
go sendTxs(cs, ctx)
|
||||||
sendTxs(cs, ctx)
|
|
||||||
},
|
},
|
||||||
1},
|
1},
|
||||||
{"many non-empty blocks",
|
{"many non-empty blocks",
|
||||||
sendTxs,
|
func(cs *ConsensusState, ctx context.Context) {
|
||||||
|
go sendTxs(cs, ctx)
|
||||||
|
},
|
||||||
3},
|
3},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +148,7 @@ LOOP:
|
|||||||
|
|
||||||
// start sending transactions
|
// start sending transactions
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
go initFn(cs, ctx)
|
initFn(cs, ctx)
|
||||||
|
|
||||||
// clean up WAL file from the previous iteration
|
// clean up WAL file from the previous iteration
|
||||||
walFile := cs.config.WalFile()
|
walFile := cs.config.WalFile()
|
||||||
|
@ -46,7 +46,7 @@ func TestDynamicCert(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
check := tc.keys.GenCommit(chainID, tc.height, nil, tc.vals,
|
check := tc.keys.GenCommit(chainID, tc.height, nil, tc.vals,
|
||||||
[]byte("bar"), []byte("params"), tc.first, tc.last)
|
[]byte("bar"), []byte("params"), []byte("results"), tc.first, tc.last)
|
||||||
err := cert.Certify(check)
|
err := cert.Certify(check)
|
||||||
if tc.proper {
|
if tc.proper {
|
||||||
assert.Nil(err, "%+v", err)
|
assert.Nil(err, "%+v", err)
|
||||||
@ -71,7 +71,7 @@ func TestDynamicUpdate(t *testing.T) {
|
|||||||
|
|
||||||
// one valid block to give us a sense of time
|
// one valid block to give us a sense of time
|
||||||
h := int64(100)
|
h := int64(100)
|
||||||
good := keys.GenCommit(chainID, h, nil, vals, []byte("foo"), []byte("params"), 0, len(keys))
|
good := keys.GenCommit(chainID, h, nil, vals, []byte("foo"), []byte("params"), []byte("results"), 0, len(keys))
|
||||||
err := cert.Certify(good)
|
err := cert.Certify(good)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
|
|
||||||
@ -109,7 +109,7 @@ func TestDynamicUpdate(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
fc := tc.keys.GenFullCommit(chainID, tc.height, nil, tc.vals,
|
fc := tc.keys.GenFullCommit(chainID, tc.height, nil, tc.vals,
|
||||||
[]byte("bar"), []byte("params"), tc.first, tc.last)
|
[]byte("bar"), []byte("params"), []byte("results"), tc.first, tc.last)
|
||||||
err := cert.Update(fc)
|
err := cert.Update(fc)
|
||||||
if tc.proper {
|
if tc.proper {
|
||||||
assert.Nil(err, "%d: %+v", tc.height, err)
|
assert.Nil(err, "%d: %+v", tc.height, err)
|
||||||
|
@ -29,7 +29,7 @@ func TestSerializeFullCommits(t *testing.T) {
|
|||||||
// build a fc
|
// build a fc
|
||||||
keys := lite.GenValKeys(5)
|
keys := lite.GenValKeys(5)
|
||||||
vals := keys.ToValidators(10, 0)
|
vals := keys.ToValidators(10, 0)
|
||||||
fc := keys.GenFullCommit(chainID, h, nil, vals, appHash, []byte("params"), 0, 5)
|
fc := keys.GenFullCommit(chainID, h, nil, vals, appHash, []byte("params"), []byte("results"), 0, 5)
|
||||||
|
|
||||||
require.Equal(h, fc.Height())
|
require.Equal(h, fc.Height())
|
||||||
require.Equal(vals.Hash(), fc.ValidatorsHash())
|
require.Equal(vals.Hash(), fc.ValidatorsHash())
|
||||||
|
@ -46,7 +46,7 @@ func TestFileProvider(t *testing.T) {
|
|||||||
// (10, 0), (10, 1), (10, 1), (10, 2), (10, 2), ...
|
// (10, 0), (10, 1), (10, 1), (10, 2), (10, 2), ...
|
||||||
vals := keys.ToValidators(10, int64(count/2))
|
vals := keys.ToValidators(10, int64(count/2))
|
||||||
h := int64(20 + 10*i)
|
h := int64(20 + 10*i)
|
||||||
check := keys.GenCommit(chainID, h, nil, vals, appHash, []byte("params"), 0, 5)
|
check := keys.GenCommit(chainID, h, nil, vals, appHash, []byte("params"), []byte("results"), 0, 5)
|
||||||
seeds[i] = lite.NewFullCommit(check, vals)
|
seeds[i] = lite.NewFullCommit(check, vals)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ func makeVote(header *types.Header, vals *types.ValidatorSet, key crypto.PrivKey
|
|||||||
// Silences warning that vals can also be merkle.Hashable
|
// Silences warning that vals can also be merkle.Hashable
|
||||||
// nolint: interfacer
|
// nolint: interfacer
|
||||||
func genHeader(chainID string, height int64, txs types.Txs,
|
func genHeader(chainID string, height int64, txs types.Txs,
|
||||||
vals *types.ValidatorSet, appHash, consHash []byte) *types.Header {
|
vals *types.ValidatorSet, appHash, consHash, resHash []byte) *types.Header {
|
||||||
|
|
||||||
return &types.Header{
|
return &types.Header{
|
||||||
ChainID: chainID,
|
ChainID: chainID,
|
||||||
@ -120,18 +120,19 @@ func genHeader(chainID string, height int64, txs types.Txs,
|
|||||||
TotalTxs: int64(len(txs)),
|
TotalTxs: int64(len(txs)),
|
||||||
// LastBlockID
|
// LastBlockID
|
||||||
// LastCommitHash
|
// LastCommitHash
|
||||||
ValidatorsHash: vals.Hash(),
|
ValidatorsHash: vals.Hash(),
|
||||||
DataHash: txs.Hash(),
|
DataHash: txs.Hash(),
|
||||||
AppHash: appHash,
|
AppHash: appHash,
|
||||||
ConsensusHash: consHash,
|
ConsensusHash: consHash,
|
||||||
|
LastResultsHash: resHash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GenCommit calls genHeader and signHeader and combines them into a Commit.
|
// GenCommit calls genHeader and signHeader and combines them into a Commit.
|
||||||
func (v ValKeys) GenCommit(chainID string, height int64, txs types.Txs,
|
func (v ValKeys) GenCommit(chainID string, height int64, txs types.Txs,
|
||||||
vals *types.ValidatorSet, appHash, consHash []byte, first, last int) Commit {
|
vals *types.ValidatorSet, appHash, consHash, resHash []byte, first, last int) Commit {
|
||||||
|
|
||||||
header := genHeader(chainID, height, txs, vals, appHash, consHash)
|
header := genHeader(chainID, height, txs, vals, appHash, consHash, resHash)
|
||||||
check := Commit{
|
check := Commit{
|
||||||
Header: header,
|
Header: header,
|
||||||
Commit: v.signHeader(header, first, last),
|
Commit: v.signHeader(header, first, last),
|
||||||
@ -141,9 +142,9 @@ func (v ValKeys) GenCommit(chainID string, height int64, txs types.Txs,
|
|||||||
|
|
||||||
// GenFullCommit calls genHeader and signHeader and combines them into a Commit.
|
// GenFullCommit calls genHeader and signHeader and combines them into a Commit.
|
||||||
func (v ValKeys) GenFullCommit(chainID string, height int64, txs types.Txs,
|
func (v ValKeys) GenFullCommit(chainID string, height int64, txs types.Txs,
|
||||||
vals *types.ValidatorSet, appHash, consHash []byte, first, last int) FullCommit {
|
vals *types.ValidatorSet, appHash, consHash, resHash []byte, first, last int) FullCommit {
|
||||||
|
|
||||||
header := genHeader(chainID, height, txs, vals, appHash, consHash)
|
header := genHeader(chainID, height, txs, vals, appHash, consHash, resHash)
|
||||||
commit := Commit{
|
commit := Commit{
|
||||||
Header: header,
|
Header: header,
|
||||||
Commit: v.signHeader(header, first, last),
|
Commit: v.signHeader(header, first, last),
|
||||||
|
@ -23,6 +23,7 @@ func TestInquirerValidPath(t *testing.T) {
|
|||||||
// construct a bunch of commits, each with one more height than the last
|
// construct a bunch of commits, each with one more height than the last
|
||||||
chainID := "inquiry-test"
|
chainID := "inquiry-test"
|
||||||
consHash := []byte("params")
|
consHash := []byte("params")
|
||||||
|
resHash := []byte("results")
|
||||||
count := 50
|
count := 50
|
||||||
commits := make([]lite.FullCommit, count)
|
commits := make([]lite.FullCommit, count)
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
@ -31,7 +32,7 @@ func TestInquirerValidPath(t *testing.T) {
|
|||||||
vals := keys.ToValidators(vote, 0)
|
vals := keys.ToValidators(vote, 0)
|
||||||
h := int64(20 + 10*i)
|
h := int64(20 + 10*i)
|
||||||
appHash := []byte(fmt.Sprintf("h=%d", h))
|
appHash := []byte(fmt.Sprintf("h=%d", h))
|
||||||
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, consHash, 0, len(keys))
|
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, consHash, resHash, 0, len(keys))
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize a certifier with the initial state
|
// initialize a certifier with the initial state
|
||||||
@ -79,7 +80,8 @@ func TestInquirerMinimalPath(t *testing.T) {
|
|||||||
vals := keys.ToValidators(vote, 0)
|
vals := keys.ToValidators(vote, 0)
|
||||||
h := int64(5 + 10*i)
|
h := int64(5 + 10*i)
|
||||||
appHash := []byte(fmt.Sprintf("h=%d", h))
|
appHash := []byte(fmt.Sprintf("h=%d", h))
|
||||||
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, consHash, 0, len(keys))
|
resHash := []byte(fmt.Sprintf("res=%d", h))
|
||||||
|
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, consHash, resHash, 0, len(keys))
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize a certifier with the initial state
|
// initialize a certifier with the initial state
|
||||||
@ -127,7 +129,8 @@ func TestInquirerVerifyHistorical(t *testing.T) {
|
|||||||
vals := keys.ToValidators(vote, 0)
|
vals := keys.ToValidators(vote, 0)
|
||||||
h := int64(20 + 10*i)
|
h := int64(20 + 10*i)
|
||||||
appHash := []byte(fmt.Sprintf("h=%d", h))
|
appHash := []byte(fmt.Sprintf("h=%d", h))
|
||||||
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, consHash, 0, len(keys))
|
resHash := []byte(fmt.Sprintf("res=%d", h))
|
||||||
|
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, consHash, resHash, 0, len(keys))
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize a certifier with the initial state
|
// initialize a certifier with the initial state
|
||||||
|
@ -33,7 +33,8 @@ func benchmarkGenCommit(b *testing.B, keys lite.ValKeys) {
|
|||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
h := int64(1 + i)
|
h := int64(1 + i)
|
||||||
appHash := []byte(fmt.Sprintf("h=%d", h))
|
appHash := []byte(fmt.Sprintf("h=%d", h))
|
||||||
keys.GenCommit(chainID, h, nil, vals, appHash, []byte("params"), 0, len(keys))
|
resHash := []byte(fmt.Sprintf("res=%d", h))
|
||||||
|
keys.GenCommit(chainID, h, nil, vals, appHash, []byte("params"), resHash, 0, len(keys))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +106,7 @@ func benchmarkCertifyCommit(b *testing.B, keys lite.ValKeys) {
|
|||||||
chainID := "bench-certify"
|
chainID := "bench-certify"
|
||||||
vals := keys.ToValidators(20, 10)
|
vals := keys.ToValidators(20, 10)
|
||||||
cert := lite.NewStatic(chainID, vals)
|
cert := lite.NewStatic(chainID, vals)
|
||||||
check := keys.GenCommit(chainID, 123, nil, vals, []byte("foo"), []byte("params"), 0, len(keys))
|
check := keys.GenCommit(chainID, 123, nil, vals, []byte("foo"), []byte("params"), []byte("res"), 0, len(keys))
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
err := cert.Certify(check)
|
err := cert.Certify(check)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -58,7 +58,7 @@ func checkProvider(t *testing.T, p lite.Provider, chainID, app string) {
|
|||||||
// (10, 0), (10, 1), (10, 1), (10, 2), (10, 2), ...
|
// (10, 0), (10, 1), (10, 1), (10, 2), (10, 2), ...
|
||||||
vals := keys.ToValidators(10, int64(count/2))
|
vals := keys.ToValidators(10, int64(count/2))
|
||||||
h := int64(20 + 10*i)
|
h := int64(20 + 10*i)
|
||||||
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, []byte("params"), 0, 5)
|
commits[i] = keys.GenFullCommit(chainID, h, nil, vals, appHash, []byte("params"), []byte("results"), 0, 5)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check provider is empty
|
// check provider is empty
|
||||||
@ -129,7 +129,7 @@ func TestCacheGetsBestHeight(t *testing.T) {
|
|||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
vals := keys.ToValidators(10, int64(count/2))
|
vals := keys.ToValidators(10, int64(count/2))
|
||||||
h := int64(10 * (i + 1))
|
h := int64(10 * (i + 1))
|
||||||
fc := keys.GenFullCommit(chainID, h, nil, vals, appHash, []byte("params"), 0, 5)
|
fc := keys.GenFullCommit(chainID, h, nil, vals, appHash, []byte("params"), []byte("results"), 0, 5)
|
||||||
err := p2.StoreCommit(fc)
|
err := p2.StoreCommit(fc)
|
||||||
require.NoError(err)
|
require.NoError(err)
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ func TestStaticCert(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
check := tc.keys.GenCommit(chainID, tc.height, nil, tc.vals,
|
check := tc.keys.GenCommit(chainID, tc.height, nil, tc.vals,
|
||||||
[]byte("foo"), []byte("params"), tc.first, tc.last)
|
[]byte("foo"), []byte("params"), []byte("results"), tc.first, tc.last)
|
||||||
err := cert.Certify(check)
|
err := cert.Certify(check)
|
||||||
if tc.proper {
|
if tc.proper {
|
||||||
assert.Nil(err, "%+v", err)
|
assert.Nil(err, "%+v", err)
|
||||||
|
@ -152,6 +152,15 @@ func (c *HTTP) Block(height *int64) (*ctypes.ResultBlock, error) {
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *HTTP) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) {
|
||||||
|
result := new(ctypes.ResultBlockResults)
|
||||||
|
_, err := c.rpc.Call("block_results", map[string]interface{}{"height": height}, result)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "Block Result")
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *HTTP) Commit(height *int64) (*ctypes.ResultCommit, error) {
|
func (c *HTTP) Commit(height *int64) (*ctypes.ResultCommit, error) {
|
||||||
result := new(ctypes.ResultCommit)
|
result := new(ctypes.ResultCommit)
|
||||||
_, err := c.rpc.Call("commit", map[string]interface{}{"height": height}, result)
|
_, err := c.rpc.Call("commit", map[string]interface{}{"height": height}, result)
|
||||||
|
@ -45,6 +45,7 @@ type ABCIClient interface {
|
|||||||
// signatures and prove anything about the chain
|
// signatures and prove anything about the chain
|
||||||
type SignClient interface {
|
type SignClient interface {
|
||||||
Block(height *int64) (*ctypes.ResultBlock, error)
|
Block(height *int64) (*ctypes.ResultBlock, error)
|
||||||
|
BlockResults(height *int64) (*ctypes.ResultBlockResults, error)
|
||||||
Commit(height *int64) (*ctypes.ResultCommit, error)
|
Commit(height *int64) (*ctypes.ResultCommit, error)
|
||||||
Validators(height *int64) (*ctypes.ResultValidators, error)
|
Validators(height *int64) (*ctypes.ResultValidators, error)
|
||||||
Tx(hash []byte, prove bool) (*ctypes.ResultTx, error)
|
Tx(hash []byte, prove bool) (*ctypes.ResultTx, error)
|
||||||
|
@ -100,6 +100,10 @@ func (Local) Block(height *int64) (*ctypes.ResultBlock, error) {
|
|||||||
return core.Block(height)
|
return core.Block(height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (Local) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) {
|
||||||
|
return core.BlockResults(height)
|
||||||
|
}
|
||||||
|
|
||||||
func (Local) Commit(height *int64) (*ctypes.ResultCommit, error) {
|
func (Local) Commit(height *int64) (*ctypes.ResultCommit, error) {
|
||||||
return core.Commit(height)
|
return core.Commit(height)
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,6 @@ func TestAppCalls(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// make sure we can lookup the tx with proof
|
// make sure we can lookup the tx with proof
|
||||||
// ptx, err := c.Tx(bres.Hash, true)
|
|
||||||
ptx, err := c.Tx(bres.Hash, true)
|
ptx, err := c.Tx(bres.Hash, true)
|
||||||
require.Nil(err, "%d: %+v", i, err)
|
require.Nil(err, "%d: %+v", i, err)
|
||||||
assert.EqualValues(txh, ptx.Height)
|
assert.EqualValues(txh, ptx.Height)
|
||||||
@ -168,9 +167,16 @@ func TestAppCalls(t *testing.T) {
|
|||||||
assert.True(len(appHash) > 0)
|
assert.True(len(appHash) > 0)
|
||||||
assert.EqualValues(apph, block.BlockMeta.Header.Height)
|
assert.EqualValues(apph, block.BlockMeta.Header.Height)
|
||||||
|
|
||||||
|
// now check the results
|
||||||
|
blockResults, err := c.BlockResults(&txh)
|
||||||
|
require.Nil(err, "%d: %+v", i, err)
|
||||||
|
assert.Equal(txh, blockResults.Height)
|
||||||
|
if assert.Equal(1, len(blockResults.Results.DeliverTx)) {
|
||||||
|
// check success code
|
||||||
|
assert.EqualValues(0, blockResults.Results.DeliverTx[0].Code)
|
||||||
|
}
|
||||||
|
|
||||||
// check blockchain info, now that we know there is info
|
// check blockchain info, now that we know there is info
|
||||||
// TODO: is this commented somewhere that they are returned
|
|
||||||
// in order of descending height???
|
|
||||||
info, err := c.BlockchainInfo(apph, apph)
|
info, err := c.BlockchainInfo(apph, apph)
|
||||||
require.Nil(err, "%d: %+v", i, err)
|
require.Nil(err, "%d: %+v", i, err)
|
||||||
assert.True(info.LastHeight >= apph)
|
assert.True(info.LastHeight >= apph)
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Get block headers for minHeight <= height <= maxHeight.
|
// Get block headers for minHeight <= height <= maxHeight.
|
||||||
|
// Block headers are returned in descending order (highest first).
|
||||||
//
|
//
|
||||||
// ```shell
|
// ```shell
|
||||||
// curl 'localhost:46657/blockchain?minHeight=10&maxHeight=10'
|
// curl 'localhost:46657/blockchain?minHeight=10&maxHeight=10'
|
||||||
@ -192,19 +193,10 @@ func BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, e
|
|||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
func Block(heightPtr *int64) (*ctypes.ResultBlock, error) {
|
func Block(heightPtr *int64) (*ctypes.ResultBlock, error) {
|
||||||
if heightPtr == nil {
|
storeHeight := blockStore.Height()
|
||||||
height := blockStore.Height()
|
height, err := getHeight(storeHeight, heightPtr)
|
||||||
blockMeta := blockStore.LoadBlockMeta(height)
|
if err != nil {
|
||||||
block := blockStore.LoadBlock(height)
|
return nil, err
|
||||||
return &ctypes.ResultBlock{blockMeta, block}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
height := *heightPtr
|
|
||||||
if height <= 0 {
|
|
||||||
return nil, fmt.Errorf("Height must be greater than 0")
|
|
||||||
}
|
|
||||||
if height > blockStore.Height() {
|
|
||||||
return nil, fmt.Errorf("Height must be less than the current blockchain height")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blockMeta := blockStore.LoadBlockMeta(height)
|
blockMeta := blockStore.LoadBlockMeta(height)
|
||||||
@ -283,20 +275,10 @@ func Block(heightPtr *int64) (*ctypes.ResultBlock, error) {
|
|||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
func Commit(heightPtr *int64) (*ctypes.ResultCommit, error) {
|
func Commit(heightPtr *int64) (*ctypes.ResultCommit, error) {
|
||||||
if heightPtr == nil {
|
|
||||||
height := blockStore.Height()
|
|
||||||
header := blockStore.LoadBlockMeta(height).Header
|
|
||||||
commit := blockStore.LoadSeenCommit(height)
|
|
||||||
return ctypes.NewResultCommit(header, commit, false), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
height := *heightPtr
|
|
||||||
if height <= 0 {
|
|
||||||
return nil, fmt.Errorf("Height must be greater than 0")
|
|
||||||
}
|
|
||||||
storeHeight := blockStore.Height()
|
storeHeight := blockStore.Height()
|
||||||
if height > storeHeight {
|
height, err := getHeight(storeHeight, heightPtr)
|
||||||
return nil, fmt.Errorf("Height must be less than or equal to the current blockchain height")
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
header := blockStore.LoadBlockMeta(height).Header
|
header := blockStore.LoadBlockMeta(height).Header
|
||||||
@ -312,3 +294,71 @@ func Commit(heightPtr *int64) (*ctypes.ResultCommit, error) {
|
|||||||
commit := blockStore.LoadBlockCommit(height)
|
commit := blockStore.LoadBlockCommit(height)
|
||||||
return ctypes.NewResultCommit(header, commit, true), nil
|
return ctypes.NewResultCommit(header, commit, true), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlockResults gets ABCIResults at a given height.
|
||||||
|
// If no height is provided, it will fetch results for the latest block.
|
||||||
|
//
|
||||||
|
// Results are for the height of the block containing the txs.
|
||||||
|
// Thus response.results[5] is the results of executing getBlock(h).Txs[5]
|
||||||
|
//
|
||||||
|
// ```shell
|
||||||
|
// curl 'localhost:46657/block_results?height=10'
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// ```go
|
||||||
|
// client := client.NewHTTP("tcp://0.0.0.0:46657", "/websocket")
|
||||||
|
// info, err := client.BlockResults(10)
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// > The above command returns JSON structured like this:
|
||||||
|
//
|
||||||
|
// ```json
|
||||||
|
// {
|
||||||
|
// "height": 10,
|
||||||
|
// "results": [
|
||||||
|
// {
|
||||||
|
// "code": 0,
|
||||||
|
// "data": "CAFE00F00D"
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// "code": 102,
|
||||||
|
// "data": ""
|
||||||
|
// }
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
func BlockResults(heightPtr *int64) (*ctypes.ResultBlockResults, error) {
|
||||||
|
storeHeight := blockStore.Height()
|
||||||
|
height, err := getHeight(storeHeight, heightPtr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the results
|
||||||
|
state := consensusState.GetState()
|
||||||
|
results, err := state.LoadABCIResponses(height)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := &ctypes.ResultBlockResults{
|
||||||
|
Height: height,
|
||||||
|
Results: results,
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHeight(storeHeight int64, heightPtr *int64) (int64, error) {
|
||||||
|
if heightPtr != nil {
|
||||||
|
height := *heightPtr
|
||||||
|
if height <= 0 {
|
||||||
|
return 0, fmt.Errorf("Height must be greater than 0")
|
||||||
|
}
|
||||||
|
if height > storeHeight {
|
||||||
|
return 0, fmt.Errorf("Height must be less than or equal to the current blockchain height")
|
||||||
|
}
|
||||||
|
return height, nil
|
||||||
|
}
|
||||||
|
return storeHeight, nil
|
||||||
|
}
|
||||||
|
@ -43,12 +43,12 @@ import (
|
|||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
func Validators(heightPtr *int64) (*ctypes.ResultValidators, error) {
|
func Validators(heightPtr *int64) (*ctypes.ResultValidators, error) {
|
||||||
if heightPtr == nil {
|
storeHeight := blockStore.Height()
|
||||||
blockHeight, validators := consensusState.GetValidators()
|
height, err := getHeight(storeHeight, heightPtr)
|
||||||
return &ctypes.ResultValidators{blockHeight, validators}, nil
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
height := *heightPtr
|
|
||||||
state := consensusState.GetState()
|
state := consensusState.GetState()
|
||||||
validators, err := state.LoadValidators(height)
|
validators, err := state.LoadValidators(height)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -17,6 +17,7 @@ var Routes = map[string]*rpc.RPCFunc{
|
|||||||
"blockchain": rpc.NewRPCFunc(BlockchainInfo, "minHeight,maxHeight"),
|
"blockchain": rpc.NewRPCFunc(BlockchainInfo, "minHeight,maxHeight"),
|
||||||
"genesis": rpc.NewRPCFunc(Genesis, ""),
|
"genesis": rpc.NewRPCFunc(Genesis, ""),
|
||||||
"block": rpc.NewRPCFunc(Block, "height"),
|
"block": rpc.NewRPCFunc(Block, "height"),
|
||||||
|
"block_results": rpc.NewRPCFunc(BlockResults, "height"),
|
||||||
"commit": rpc.NewRPCFunc(Commit, "height"),
|
"commit": rpc.NewRPCFunc(Commit, "height"),
|
||||||
"tx": rpc.NewRPCFunc(Tx, "hash,prove"),
|
"tx": rpc.NewRPCFunc(Tx, "hash,prove"),
|
||||||
"tx_search": rpc.NewRPCFunc(TxSearch, "query,prove"),
|
"tx_search": rpc.NewRPCFunc(TxSearch, "query,prove"),
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/tendermint/go-wire/data"
|
"github.com/tendermint/go-wire/data"
|
||||||
cstypes "github.com/tendermint/tendermint/consensus/types"
|
cstypes "github.com/tendermint/tendermint/consensus/types"
|
||||||
"github.com/tendermint/tendermint/p2p"
|
"github.com/tendermint/tendermint/p2p"
|
||||||
|
"github.com/tendermint/tendermint/state"
|
||||||
"github.com/tendermint/tendermint/types"
|
"github.com/tendermint/tendermint/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,6 +34,11 @@ type ResultCommit struct {
|
|||||||
CanonicalCommit bool `json:"canonical"`
|
CanonicalCommit bool `json:"canonical"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResultBlockResults struct {
|
||||||
|
Height int64 `json:"height"`
|
||||||
|
Results *state.ABCIResponses `json:"results"`
|
||||||
|
}
|
||||||
|
|
||||||
// NewResultCommit is a helper to initialize the ResultCommit with
|
// NewResultCommit is a helper to initialize the ResultCommit with
|
||||||
// the embedded struct
|
// the embedded struct
|
||||||
func NewResultCommit(header *types.Header, commit *types.Commit,
|
func NewResultCommit(header *types.Header, commit *types.Commit,
|
||||||
|
@ -41,6 +41,10 @@ type (
|
|||||||
ErrNoConsensusParamsForHeight struct {
|
ErrNoConsensusParamsForHeight struct {
|
||||||
Height int64
|
Height int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrNoABCIResponsesForHeight struct {
|
||||||
|
Height int64
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (e ErrUnknownBlock) Error() string {
|
func (e ErrUnknownBlock) Error() string {
|
||||||
@ -69,3 +73,7 @@ func (e ErrNoValSetForHeight) Error() string {
|
|||||||
func (e ErrNoConsensusParamsForHeight) Error() string {
|
func (e ErrNoConsensusParamsForHeight) Error() string {
|
||||||
return cmn.Fmt("Could not find consensus params for height #%d", e.Height)
|
return cmn.Fmt("Could not find consensus params for height #%d", e.Height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e ErrNoABCIResponsesForHeight) Error() string {
|
||||||
|
return cmn.Fmt("Could not find results for height #%d", e.Height)
|
||||||
|
}
|
||||||
|
@ -52,25 +52,25 @@ func execBlockOnProxyApp(txEventPublisher types.TxEventPublisher, proxyAppConn p
|
|||||||
// TODO: make use of res.Log
|
// TODO: make use of res.Log
|
||||||
// TODO: make use of this info
|
// TODO: make use of this info
|
||||||
// Blocks may include invalid txs.
|
// Blocks may include invalid txs.
|
||||||
// reqDeliverTx := req.(abci.RequestDeliverTx)
|
txRes := r.DeliverTx
|
||||||
txResult := r.DeliverTx
|
if txRes.Code == abci.CodeTypeOK {
|
||||||
if txResult.Code == abci.CodeTypeOK {
|
|
||||||
validTxs++
|
validTxs++
|
||||||
} else {
|
} else {
|
||||||
logger.Debug("Invalid tx", "code", txResult.Code, "log", txResult.Log)
|
logger.Debug("Invalid tx", "code", txRes.Code, "log", txRes.Log)
|
||||||
invalidTxs++
|
invalidTxs++
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: if we count we can access the tx from the block instead of
|
// NOTE: if we count we can access the tx from the block instead of
|
||||||
// pulling it from the req
|
// pulling it from the req
|
||||||
|
tx := types.Tx(req.GetDeliverTx().Tx)
|
||||||
txEventPublisher.PublishEventTx(types.EventDataTx{types.TxResult{
|
txEventPublisher.PublishEventTx(types.EventDataTx{types.TxResult{
|
||||||
Height: block.Height,
|
Height: block.Height,
|
||||||
Index: uint32(txIndex),
|
Index: uint32(txIndex),
|
||||||
Tx: types.Tx(req.GetDeliverTx().Tx),
|
Tx: tx,
|
||||||
Result: *txResult,
|
Result: *txRes,
|
||||||
}})
|
}})
|
||||||
|
|
||||||
abciResponses.DeliverTx[txIndex] = txResult
|
abciResponses.DeliverTx[txIndex] = txRes
|
||||||
txIndex++
|
txIndex++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,6 +84,8 @@ func execBlockOnProxyApp(txEventPublisher types.TxEventPublisher, proxyAppConn p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: determine which validators were byzantine
|
||||||
|
|
||||||
// Begin block
|
// Begin block
|
||||||
_, err := proxyAppConn.BeginBlockSync(abci.RequestBeginBlock{
|
_, err := proxyAppConn.BeginBlockSync(abci.RequestBeginBlock{
|
||||||
Hash: block.Hash(),
|
Hash: block.Hash(),
|
||||||
@ -240,17 +242,19 @@ func (s *State) MakeBlock(height int64, txs []types.Tx, commit *types.Commit) (*
|
|||||||
block.LastBlockID = s.LastBlockID
|
block.LastBlockID = s.LastBlockID
|
||||||
block.ValidatorsHash = s.Validators.Hash()
|
block.ValidatorsHash = s.Validators.Hash()
|
||||||
block.AppHash = s.AppHash
|
block.AppHash = s.AppHash
|
||||||
block.ConsensusHash = s.LastConsensusParams.Hash()
|
block.ConsensusHash = s.ConsensusParams.Hash()
|
||||||
|
block.LastResultsHash = s.LastResultsHash
|
||||||
|
|
||||||
return block, block.MakePartSet(s.ConsensusParams.BlockGossip.BlockPartSizeBytes)
|
return block, block.MakePartSet(s.ConsensusParams.BlockGossip.BlockPartSizeBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) validateBlock(b *types.Block) error {
|
func (s *State) validateBlock(b *types.Block) error {
|
||||||
// Basic block validation.
|
// validate internal consistency
|
||||||
if err := b.ValidateBasic(); err != nil {
|
if err := b.ValidateBasic(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validate basic info
|
||||||
if b.ChainID != s.ChainID {
|
if b.ChainID != s.ChainID {
|
||||||
return fmt.Errorf("Wrong Block.Header.ChainID. Expected %v, got %v", s.ChainID, b.ChainID)
|
return fmt.Errorf("Wrong Block.Header.ChainID. Expected %v, got %v", s.ChainID, b.ChainID)
|
||||||
}
|
}
|
||||||
@ -265,19 +269,27 @@ func (s *State) validateBlock(b *types.Block) error {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// validate prev block info
|
||||||
|
if !b.LastBlockID.Equals(s.LastBlockID) {
|
||||||
|
return fmt.Errorf("Wrong Block.Header.LastBlockID. Expected %v, got %v", s.LastBlockID, b.LastBlockID)
|
||||||
|
}
|
||||||
newTxs := int64(len(b.Data.Txs))
|
newTxs := int64(len(b.Data.Txs))
|
||||||
if b.TotalTxs != s.LastBlockTotalTx+newTxs {
|
if b.TotalTxs != s.LastBlockTotalTx+newTxs {
|
||||||
return fmt.Errorf("Wrong Block.Header.TotalTxs. Expected %v, got %v", s.LastBlockTotalTx+newTxs, b.TotalTxs)
|
return fmt.Errorf("Wrong Block.Header.TotalTxs. Expected %v, got %v", s.LastBlockTotalTx+newTxs, b.TotalTxs)
|
||||||
}
|
}
|
||||||
if !b.LastBlockID.Equals(s.LastBlockID) {
|
|
||||||
return fmt.Errorf("Wrong Block.Header.LastBlockID. Expected %v, got %v", s.LastBlockID, b.LastBlockID)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// validate app info
|
||||||
if !bytes.Equal(b.AppHash, s.AppHash) {
|
if !bytes.Equal(b.AppHash, s.AppHash) {
|
||||||
return fmt.Errorf("Wrong Block.Header.AppHash. Expected %X, got %v", s.AppHash, b.AppHash)
|
return fmt.Errorf("Wrong Block.Header.AppHash. Expected %X, got %v", s.AppHash, b.AppHash)
|
||||||
}
|
}
|
||||||
if !bytes.Equal(b.ConsensusHash, s.LastConsensusParams.Hash()) {
|
if !bytes.Equal(b.ConsensusHash, s.ConsensusParams.Hash()) {
|
||||||
return fmt.Errorf("Wrong Block.Header.ConsensusHash. Expected %X, got %v", s.LastConsensusParams.Hash(), b.ConsensusHash)
|
return fmt.Errorf("Wrong Block.Header.ConsensusHash. Expected %X, got %v", s.ConsensusParams.Hash(), b.ConsensusHash)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(b.LastResultsHash, s.LastResultsHash) {
|
||||||
|
return fmt.Errorf("Wrong Block.Header.LastResultsHash. Expected %X, got %v", s.LastResultsHash, b.LastResultsHash)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(b.ValidatorsHash, s.Validators.Hash()) {
|
||||||
|
return fmt.Errorf("Wrong Block.Header.ValidatorsHash. Expected %X, got %v", s.Validators.Hash(), b.ValidatorsHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate block LastCommit.
|
// Validate block LastCommit.
|
||||||
@ -318,7 +330,7 @@ func (s *State) ApplyBlock(txEventPublisher types.TxEventPublisher, proxyAppConn
|
|||||||
fail.Fail() // XXX
|
fail.Fail() // XXX
|
||||||
|
|
||||||
// save the results before we commit
|
// save the results before we commit
|
||||||
s.SaveABCIResponses(abciResponses)
|
s.SaveABCIResponses(block.Height, abciResponses)
|
||||||
|
|
||||||
fail.Fail() // XXX
|
fail.Fail() // XXX
|
||||||
|
|
||||||
|
@ -67,6 +67,18 @@ func TestValidateBlock(t *testing.T) {
|
|||||||
block.ConsensusHash = []byte("wrong consensus hash")
|
block.ConsensusHash = []byte("wrong consensus hash")
|
||||||
err = state.ValidateBlock(block)
|
err = state.ValidateBlock(block)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
|
||||||
|
// wrong results hash fails
|
||||||
|
block = makeBlock(state, 1)
|
||||||
|
block.LastResultsHash = []byte("wrong results hash")
|
||||||
|
err = state.ValidateBlock(block)
|
||||||
|
require.Error(t, err)
|
||||||
|
|
||||||
|
// wrong validators hash fails
|
||||||
|
block = makeBlock(state, 1)
|
||||||
|
block.ValidatorsHash = []byte("wrong validators hash")
|
||||||
|
err = state.ValidateBlock(block)
|
||||||
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApplyBlock(t *testing.T) {
|
func TestApplyBlock(t *testing.T) {
|
||||||
|
@ -20,8 +20,7 @@ import (
|
|||||||
|
|
||||||
// database keys
|
// database keys
|
||||||
var (
|
var (
|
||||||
stateKey = []byte("stateKey")
|
stateKey = []byte("stateKey")
|
||||||
abciResponsesKey = []byte("abciResponsesKey")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func calcValidatorsKey(height int64) []byte {
|
func calcValidatorsKey(height int64) []byte {
|
||||||
@ -32,6 +31,10 @@ func calcConsensusParamsKey(height int64) []byte {
|
|||||||
return []byte(cmn.Fmt("consensusParamsKey:%v", height))
|
return []byte(cmn.Fmt("consensusParamsKey:%v", height))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func calcABCIResponsesKey(height int64) []byte {
|
||||||
|
return []byte(cmn.Fmt("abciResponsesKey:%v", height))
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
// State is a short description of the latest committed block of the Tendermint consensus.
|
// State is a short description of the latest committed block of the Tendermint consensus.
|
||||||
@ -68,9 +71,11 @@ type State struct {
|
|||||||
// Consensus parameters used for validating blocks.
|
// Consensus parameters used for validating blocks.
|
||||||
// Changes returned by EndBlock and updated after Commit.
|
// Changes returned by EndBlock and updated after Commit.
|
||||||
ConsensusParams types.ConsensusParams
|
ConsensusParams types.ConsensusParams
|
||||||
LastConsensusParams types.ConsensusParams
|
|
||||||
LastHeightConsensusParamsChanged int64
|
LastHeightConsensusParamsChanged int64
|
||||||
|
|
||||||
|
// Merkle root of the results from executing prev block
|
||||||
|
LastResultsHash []byte
|
||||||
|
|
||||||
// The latest AppHash we've received from calling abci.Commit()
|
// The latest AppHash we've received from calling abci.Commit()
|
||||||
AppHash []byte
|
AppHash []byte
|
||||||
|
|
||||||
@ -140,11 +145,12 @@ func (s *State) Copy() *State {
|
|||||||
LastHeightValidatorsChanged: s.LastHeightValidatorsChanged,
|
LastHeightValidatorsChanged: s.LastHeightValidatorsChanged,
|
||||||
|
|
||||||
ConsensusParams: s.ConsensusParams,
|
ConsensusParams: s.ConsensusParams,
|
||||||
LastConsensusParams: s.LastConsensusParams,
|
|
||||||
LastHeightConsensusParamsChanged: s.LastHeightConsensusParamsChanged,
|
LastHeightConsensusParamsChanged: s.LastHeightConsensusParamsChanged,
|
||||||
|
|
||||||
AppHash: s.AppHash,
|
AppHash: s.AppHash,
|
||||||
|
|
||||||
|
LastResultsHash: s.LastResultsHash,
|
||||||
|
|
||||||
logger: s.logger,
|
logger: s.logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -161,17 +167,18 @@ func (s *State) Save() {
|
|||||||
|
|
||||||
// SaveABCIResponses persists the ABCIResponses to the database.
|
// SaveABCIResponses persists the ABCIResponses to the database.
|
||||||
// This is useful in case we crash after app.Commit and before s.Save().
|
// This is useful in case we crash after app.Commit and before s.Save().
|
||||||
func (s *State) SaveABCIResponses(abciResponses *ABCIResponses) {
|
// Responses are indexed by height so they can also be loaded later to produce Merkle proofs.
|
||||||
s.db.SetSync(abciResponsesKey, abciResponses.Bytes())
|
func (s *State) SaveABCIResponses(height int64, abciResponses *ABCIResponses) {
|
||||||
|
s.db.SetSync(calcABCIResponsesKey(height), abciResponses.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadABCIResponses loads the ABCIResponses from the database.
|
// LoadABCIResponses loads the ABCIResponses for the given height from the database.
|
||||||
// This is useful for recovering from crashes where we called app.Commit and before we called
|
// This is useful for recovering from crashes where we called app.Commit and before we called
|
||||||
// s.Save()
|
// s.Save(). It can also be used to produce Merkle proofs of the result of txs.
|
||||||
func (s *State) LoadABCIResponses() *ABCIResponses {
|
func (s *State) LoadABCIResponses(height int64) (*ABCIResponses, error) {
|
||||||
buf := s.db.Get(abciResponsesKey)
|
buf := s.db.Get(calcABCIResponsesKey(height))
|
||||||
if len(buf) == 0 {
|
if len(buf) == 0 {
|
||||||
return nil
|
return nil, ErrNoABCIResponsesForHeight{height}
|
||||||
}
|
}
|
||||||
|
|
||||||
abciResponses := new(ABCIResponses)
|
abciResponses := new(ABCIResponses)
|
||||||
@ -184,7 +191,7 @@ func (s *State) LoadABCIResponses() *ABCIResponses {
|
|||||||
}
|
}
|
||||||
// TODO: ensure that buf is completely read.
|
// TODO: ensure that buf is completely read.
|
||||||
|
|
||||||
return abciResponses
|
return abciResponses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadValidators loads the ValidatorSet for a given height.
|
// LoadValidators loads the ValidatorSet for a given height.
|
||||||
@ -346,14 +353,16 @@ func (s *State) SetBlockAndValidators(header *types.Header, blockPartsHeader typ
|
|||||||
types.BlockID{header.Hash(), blockPartsHeader},
|
types.BlockID{header.Hash(), blockPartsHeader},
|
||||||
header.Time,
|
header.Time,
|
||||||
nextValSet,
|
nextValSet,
|
||||||
nextParams)
|
nextParams,
|
||||||
|
abciResponses.ResultsHash())
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) setBlockAndValidators(height int64,
|
func (s *State) setBlockAndValidators(height int64,
|
||||||
newTxs int64, blockID types.BlockID, blockTime time.Time,
|
newTxs int64, blockID types.BlockID, blockTime time.Time,
|
||||||
valSet *types.ValidatorSet,
|
valSet *types.ValidatorSet,
|
||||||
params types.ConsensusParams) {
|
params types.ConsensusParams,
|
||||||
|
resultsHash []byte) {
|
||||||
|
|
||||||
s.LastBlockHeight = height
|
s.LastBlockHeight = height
|
||||||
s.LastBlockTotalTx += newTxs
|
s.LastBlockTotalTx += newTxs
|
||||||
@ -363,8 +372,9 @@ func (s *State) setBlockAndValidators(height int64,
|
|||||||
s.LastValidators = s.Validators.Copy()
|
s.LastValidators = s.Validators.Copy()
|
||||||
s.Validators = valSet
|
s.Validators = valSet
|
||||||
|
|
||||||
s.LastConsensusParams = s.ConsensusParams
|
|
||||||
s.ConsensusParams = params
|
s.ConsensusParams = params
|
||||||
|
|
||||||
|
s.LastResultsHash = resultsHash
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetValidators returns the last and current validator sets.
|
// GetValidators returns the last and current validator sets.
|
||||||
@ -374,23 +384,18 @@ func (s *State) GetValidators() (last *types.ValidatorSet, current *types.Valida
|
|||||||
|
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
|
|
||||||
// ABCIResponses retains the responses of the various ABCI calls during block processing.
|
// ABCIResponses retains the responses
|
||||||
// It is persisted to disk before calling Commit.
|
// of the various ABCI calls during block processing.
|
||||||
|
// It is persisted to disk for each height before calling Commit.
|
||||||
type ABCIResponses struct {
|
type ABCIResponses struct {
|
||||||
Height int64
|
|
||||||
|
|
||||||
DeliverTx []*abci.ResponseDeliverTx
|
DeliverTx []*abci.ResponseDeliverTx
|
||||||
EndBlock *abci.ResponseEndBlock
|
EndBlock *abci.ResponseEndBlock
|
||||||
|
|
||||||
txs types.Txs // reference for indexing results by hash
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewABCIResponses returns a new ABCIResponses
|
// NewABCIResponses returns a new ABCIResponses
|
||||||
func NewABCIResponses(block *types.Block) *ABCIResponses {
|
func NewABCIResponses(block *types.Block) *ABCIResponses {
|
||||||
return &ABCIResponses{
|
return &ABCIResponses{
|
||||||
Height: block.Height,
|
|
||||||
DeliverTx: make([]*abci.ResponseDeliverTx, block.NumTxs),
|
DeliverTx: make([]*abci.ResponseDeliverTx, block.NumTxs),
|
||||||
txs: block.Data.Txs,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,6 +404,11 @@ func (a *ABCIResponses) Bytes() []byte {
|
|||||||
return wire.BinaryBytes(*a)
|
return wire.BinaryBytes(*a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *ABCIResponses) ResultsHash() []byte {
|
||||||
|
results := types.NewResults(a.DeliverTx)
|
||||||
|
return results.Hash()
|
||||||
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
// ValidatorsInfo represents the latest validator set, or the last height it changed
|
// ValidatorsInfo represents the latest validator set, or the last height it changed
|
||||||
@ -488,7 +498,6 @@ func MakeGenesisState(db dbm.DB, genDoc *types.GenesisDoc) (*State, error) {
|
|||||||
LastHeightValidatorsChanged: 1,
|
LastHeightValidatorsChanged: 1,
|
||||||
|
|
||||||
ConsensusParams: *genDoc.ConsensusParams,
|
ConsensusParams: *genDoc.ConsensusParams,
|
||||||
LastConsensusParams: types.ConsensusParams{},
|
|
||||||
LastHeightConsensusParamsChanged: 1,
|
LastHeightConsensusParamsChanged: 1,
|
||||||
|
|
||||||
AppHash: genDoc.AppHash,
|
AppHash: genDoc.AppHash,
|
||||||
|
@ -68,7 +68,7 @@ func TestStateSaveLoad(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TestABCIResponsesSaveLoad tests saving and loading ABCIResponses.
|
// TestABCIResponsesSaveLoad tests saving and loading ABCIResponses.
|
||||||
func TestABCIResponsesSaveLoad(t *testing.T) {
|
func TestABCIResponsesSaveLoad1(t *testing.T) {
|
||||||
tearDown, _, state := setupTestCase(t)
|
tearDown, _, state := setupTestCase(t)
|
||||||
defer tearDown(t)
|
defer tearDown(t)
|
||||||
// nolint: vetshadow
|
// nolint: vetshadow
|
||||||
@ -87,15 +87,84 @@ func TestABCIResponsesSaveLoad(t *testing.T) {
|
|||||||
Power: 10,
|
Power: 10,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
abciResponses.txs = nil
|
|
||||||
|
|
||||||
state.SaveABCIResponses(abciResponses)
|
state.SaveABCIResponses(block.Height, abciResponses)
|
||||||
loadedAbciResponses := state.LoadABCIResponses()
|
loadedAbciResponses, err := state.LoadABCIResponses(block.Height)
|
||||||
|
assert.Nil(err)
|
||||||
assert.Equal(abciResponses, loadedAbciResponses,
|
assert.Equal(abciResponses, loadedAbciResponses,
|
||||||
cmn.Fmt(`ABCIResponses don't match: Got %v, Expected %v`, loadedAbciResponses,
|
cmn.Fmt(`ABCIResponses don't match: Got %v, Expected %v`, loadedAbciResponses,
|
||||||
abciResponses))
|
abciResponses))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestResultsSaveLoad tests saving and loading abci results.
|
||||||
|
func TestABCIResponsesSaveLoad2(t *testing.T) {
|
||||||
|
tearDown, _, state := setupTestCase(t)
|
||||||
|
defer tearDown(t)
|
||||||
|
// nolint: vetshadow
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
cases := [...]struct {
|
||||||
|
// height is implied index+2
|
||||||
|
// as block 1 is created from genesis
|
||||||
|
added []*abci.ResponseDeliverTx
|
||||||
|
expected types.ABCIResults
|
||||||
|
}{
|
||||||
|
0: {
|
||||||
|
[]*abci.ResponseDeliverTx{},
|
||||||
|
types.ABCIResults{},
|
||||||
|
},
|
||||||
|
1: {
|
||||||
|
[]*abci.ResponseDeliverTx{
|
||||||
|
{Code: 32, Data: []byte("Hello"), Log: "Huh?"},
|
||||||
|
},
|
||||||
|
types.ABCIResults{
|
||||||
|
{32, []byte("Hello")},
|
||||||
|
}},
|
||||||
|
2: {
|
||||||
|
[]*abci.ResponseDeliverTx{
|
||||||
|
{Code: 383},
|
||||||
|
{Data: []byte("Gotcha!"),
|
||||||
|
Tags: []*abci.KVPair{
|
||||||
|
abci.KVPairInt("a", 1),
|
||||||
|
abci.KVPairString("build", "stuff"),
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
types.ABCIResults{
|
||||||
|
{383, []byte{}},
|
||||||
|
{0, []byte("Gotcha!")},
|
||||||
|
}},
|
||||||
|
3: {
|
||||||
|
nil,
|
||||||
|
types.ABCIResults{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// query all before, should return error
|
||||||
|
for i := range cases {
|
||||||
|
h := int64(i + 1)
|
||||||
|
res, err := state.LoadABCIResponses(h)
|
||||||
|
assert.Error(err, "%d: %#v", i, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add all cases
|
||||||
|
for i, tc := range cases {
|
||||||
|
h := int64(i + 1) // last block height, one below what we save
|
||||||
|
responses := &ABCIResponses{
|
||||||
|
DeliverTx: tc.added,
|
||||||
|
EndBlock: &abci.ResponseEndBlock{},
|
||||||
|
}
|
||||||
|
state.SaveABCIResponses(h, responses)
|
||||||
|
}
|
||||||
|
|
||||||
|
// query all before, should return expected value
|
||||||
|
for i, tc := range cases {
|
||||||
|
h := int64(i + 1)
|
||||||
|
res, err := state.LoadABCIResponses(h)
|
||||||
|
assert.NoError(err, "%d", i)
|
||||||
|
assert.Equal(tc.expected.Hash(), res.ResultsHash(), "%d", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestValidatorSimpleSaveLoad tests saving and loading validators.
|
// TestValidatorSimpleSaveLoad tests saving and loading validators.
|
||||||
func TestValidatorSimpleSaveLoad(t *testing.T) {
|
func TestValidatorSimpleSaveLoad(t *testing.T) {
|
||||||
tearDown, _, state := setupTestCase(t)
|
tearDown, _, state := setupTestCase(t)
|
||||||
@ -353,7 +422,6 @@ func TestLessThanOneThirdOfVotingPowerPerBlockEnforced(t *testing.T) {
|
|||||||
height := state.LastBlockHeight + 1
|
height := state.LastBlockHeight + 1
|
||||||
block := makeBlock(state, height)
|
block := makeBlock(state, height)
|
||||||
abciResponses := &ABCIResponses{
|
abciResponses := &ABCIResponses{
|
||||||
Height: height,
|
|
||||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: tc.valUpdatesFn(state.Validators)},
|
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: tc.valUpdatesFn(state.Validators)},
|
||||||
}
|
}
|
||||||
err := state.SetBlockAndValidators(block.Header, types.PartSetHeader{}, abciResponses)
|
err := state.SetBlockAndValidators(block.Header, types.PartSetHeader{}, abciResponses)
|
||||||
@ -421,7 +489,6 @@ func makeHeaderPartsResponsesValPubKeyChange(state *State, height int64,
|
|||||||
|
|
||||||
block := makeBlock(state, height)
|
block := makeBlock(state, height)
|
||||||
abciResponses := &ABCIResponses{
|
abciResponses := &ABCIResponses{
|
||||||
Height: height,
|
|
||||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []*abci.Validator{}},
|
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []*abci.Validator{}},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,7 +511,6 @@ func makeHeaderPartsResponsesValPowerChange(state *State, height int64,
|
|||||||
|
|
||||||
block := makeBlock(state, height)
|
block := makeBlock(state, height)
|
||||||
abciResponses := &ABCIResponses{
|
abciResponses := &ABCIResponses{
|
||||||
Height: height,
|
|
||||||
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []*abci.Validator{}},
|
EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []*abci.Validator{}},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,7 +532,6 @@ func makeHeaderPartsResponsesParams(state *State, height int64,
|
|||||||
|
|
||||||
block := makeBlock(state, height)
|
block := makeBlock(state, height)
|
||||||
abciResponses := &ABCIResponses{
|
abciResponses := &ABCIResponses{
|
||||||
Height: height,
|
|
||||||
EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: types.TM2PB.ConsensusParams(¶ms)},
|
EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: types.TM2PB.ConsensusParams(¶ms)},
|
||||||
}
|
}
|
||||||
return block.Header, types.PartSetHeader{}, abciResponses
|
return block.Header, types.PartSetHeader{}, abciResponses
|
||||||
@ -476,3 +541,14 @@ type paramsChangeTestCase struct {
|
|||||||
height int64
|
height int64
|
||||||
params types.ConsensusParams
|
params types.ConsensusParams
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeHeaderPartsResults(state *State, height int64,
|
||||||
|
results []*abci.ResponseDeliverTx) (*types.Header, types.PartSetHeader, *ABCIResponses) {
|
||||||
|
|
||||||
|
block := makeBlock(state, height)
|
||||||
|
abciResponses := &ABCIResponses{
|
||||||
|
DeliverTx: results,
|
||||||
|
EndBlock: &abci.ResponseEndBlock{},
|
||||||
|
}
|
||||||
|
return block.Header, types.PartSetHeader{}, abciResponses
|
||||||
|
}
|
||||||
|
@ -149,10 +149,11 @@ type Header struct {
|
|||||||
LastCommitHash data.Bytes `json:"last_commit_hash"` // commit from validators from the last block
|
LastCommitHash data.Bytes `json:"last_commit_hash"` // commit from validators from the last block
|
||||||
DataHash data.Bytes `json:"data_hash"` // transactions
|
DataHash data.Bytes `json:"data_hash"` // transactions
|
||||||
|
|
||||||
// hashes from the app
|
// hashes from the app output from the prev block
|
||||||
ValidatorsHash data.Bytes `json:"validators_hash"` // validators for the current block
|
ValidatorsHash data.Bytes `json:"validators_hash"` // validators for the current block
|
||||||
ConsensusHash data.Bytes `json:"consensus_hash"` // consensus params for current block
|
ConsensusHash data.Bytes `json:"consensus_hash"` // consensus params for current block
|
||||||
AppHash data.Bytes `json:"app_hash"` // state after txs from the previous block
|
AppHash data.Bytes `json:"app_hash"` // state after txs from the previous block
|
||||||
|
LastResultsHash data.Bytes `json:"last_results_hash"` // root hash of all results from the txs from the previous block
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash returns the hash of the header.
|
// Hash returns the hash of the header.
|
||||||
@ -173,6 +174,7 @@ func (h *Header) Hash() data.Bytes {
|
|||||||
"Validators": h.ValidatorsHash,
|
"Validators": h.ValidatorsHash,
|
||||||
"App": h.AppHash,
|
"App": h.AppHash,
|
||||||
"Consensus": h.ConsensusHash,
|
"Consensus": h.ConsensusHash,
|
||||||
|
"Results": h.LastResultsHash,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +194,8 @@ func (h *Header) StringIndented(indent string) string {
|
|||||||
%s Data: %v
|
%s Data: %v
|
||||||
%s Validators: %v
|
%s Validators: %v
|
||||||
%s App: %v
|
%s App: %v
|
||||||
%s Conensus: %v
|
%s Conensus: %v
|
||||||
|
%s Results: %v
|
||||||
%s}#%v`,
|
%s}#%v`,
|
||||||
indent, h.ChainID,
|
indent, h.ChainID,
|
||||||
indent, h.Height,
|
indent, h.Height,
|
||||||
@ -205,6 +208,7 @@ func (h *Header) StringIndented(indent string) string {
|
|||||||
indent, h.ValidatorsHash,
|
indent, h.ValidatorsHash,
|
||||||
indent, h.AppHash,
|
indent, h.AppHash,
|
||||||
indent, h.ConsensusHash,
|
indent, h.ConsensusHash,
|
||||||
|
indent, h.LastResultsHash,
|
||||||
indent, h.Hash())
|
indent, h.Hash())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
66
types/results.go
Normal file
66
types/results.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
abci "github.com/tendermint/abci/types"
|
||||||
|
wire "github.com/tendermint/go-wire"
|
||||||
|
"github.com/tendermint/go-wire/data"
|
||||||
|
"github.com/tendermint/tmlibs/merkle"
|
||||||
|
)
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// ABCIResult is the deterministic component of a ResponseDeliverTx.
|
||||||
|
// TODO: add Tags
|
||||||
|
type ABCIResult struct {
|
||||||
|
Code uint32 `json:"code"`
|
||||||
|
Data data.Bytes `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash returns the canonical hash of the ABCIResult
|
||||||
|
func (a ABCIResult) Hash() []byte {
|
||||||
|
return wire.BinaryRipemd160(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ABCIResults wraps the deliver tx results to return a proof
|
||||||
|
type ABCIResults []ABCIResult
|
||||||
|
|
||||||
|
// NewResults creates ABCIResults from ResponseDeliverTx
|
||||||
|
func NewResults(del []*abci.ResponseDeliverTx) ABCIResults {
|
||||||
|
res := make(ABCIResults, len(del))
|
||||||
|
for i, d := range del {
|
||||||
|
res[i] = NewResultFromResponse(d)
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewResultFromResponse(response *abci.ResponseDeliverTx) ABCIResult {
|
||||||
|
return ABCIResult{
|
||||||
|
Code: response.Code,
|
||||||
|
Data: response.Data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes serializes the ABCIResponse using go-wire
|
||||||
|
func (a ABCIResults) Bytes() []byte {
|
||||||
|
return wire.BinaryBytes(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash returns a merkle hash of all results
|
||||||
|
func (a ABCIResults) Hash() []byte {
|
||||||
|
return merkle.SimpleHashFromHashables(a.toHashables())
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProveResult returns a merkle proof of one result from the set
|
||||||
|
func (a ABCIResults) ProveResult(i int) merkle.SimpleProof {
|
||||||
|
_, proofs := merkle.SimpleProofsFromHashables(a.toHashables())
|
||||||
|
return *proofs[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a ABCIResults) toHashables() []merkle.Hashable {
|
||||||
|
l := len(a)
|
||||||
|
hashables := make([]merkle.Hashable, l)
|
||||||
|
for i := 0; i < l; i++ {
|
||||||
|
hashables[i] = a[i]
|
||||||
|
}
|
||||||
|
return hashables
|
||||||
|
}
|
41
types/results_test.go
Normal file
41
types/results_test.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestABCIResults(t *testing.T) {
|
||||||
|
a := ABCIResult{Code: 0, Data: nil}
|
||||||
|
b := ABCIResult{Code: 0, Data: []byte{}}
|
||||||
|
c := ABCIResult{Code: 0, Data: []byte("one")}
|
||||||
|
d := ABCIResult{Code: 14, Data: nil}
|
||||||
|
e := ABCIResult{Code: 14, Data: []byte("foo")}
|
||||||
|
f := ABCIResult{Code: 14, Data: []byte("bar")}
|
||||||
|
|
||||||
|
// nil and []byte{} should produce same hash
|
||||||
|
assert.Equal(t, a.Hash(), b.Hash())
|
||||||
|
|
||||||
|
// a and b should be the same, don't go in results
|
||||||
|
results := ABCIResults{a, c, d, e, f}
|
||||||
|
|
||||||
|
// make sure each result hashes properly
|
||||||
|
var last []byte
|
||||||
|
for i, res := range results {
|
||||||
|
h := res.Hash()
|
||||||
|
assert.NotEqual(t, last, h, "%d", i)
|
||||||
|
last = h
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure that we can get a root hash from results
|
||||||
|
// and verify proofs
|
||||||
|
root := results.Hash()
|
||||||
|
assert.NotEmpty(t, root)
|
||||||
|
|
||||||
|
for i, res := range results {
|
||||||
|
proof := results.ProveResult(i)
|
||||||
|
valid := proof.Verify(i, len(results), res.Hash(), root)
|
||||||
|
assert.True(t, valid, "%d", i)
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user