diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go new file mode 100644 index 0000000000000000000000000000000000000000..c0816017e285443278621451039ddbd79535950a --- /dev/null +++ b/blockchain/blockchain.go @@ -0,0 +1,181 @@ +// Copyright 2017 Monax Industries Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package blockchain + +import ( + "time" + + "sync" + + acm "github.com/hyperledger/burrow/account" + "github.com/hyperledger/burrow/genesis" +) + +// Immutable Root of blockchain +type Root interface { + // ChainID precomputed from GenesisDoc + ChainID() string + // GenesisHash precomputed from GenesisDoc + GenesisHash() []byte + GenesisDoc() genesis.GenesisDoc +} + +// Immutable pointer to the current tip of the blockchain +type Tip interface { + // All Last* references are to the block last committed + LastBlockHeight() uint64 + LastBlockTime() time.Time + LastBlockHash() []byte + // Note this is the hash of the application state after the most recently committed block's transactions executed + // and so lastBlock.Header.AppHash will be one block older than our AppHashAfterLastBlock (i.e. Tendermint closes + // the AppHash we return from ABCI Commit into the _next_ block) + AppHashAfterLastBlock() []byte +} + +// Burrow's portion of the Blockchain state +type Blockchain interface { + Root + Tip + // Returns an immutable copy of the tip + Tip() Tip + // Returns a copy of the current validator set + Validators() []acm.Validator +} + +type MutableBlockchain interface { + Blockchain + CommitBlock(blockTime time.Time, blockHash, appHash []byte) +} + +type root struct { + chainID string + genesisHash []byte + genesisDoc genesis.GenesisDoc +} + +type tip struct { + lastBlockHeight uint64 + lastBlockTime time.Time + lastBlockHash []byte + appHashAfterLastBlock []byte +} + +type blockchain struct { + sync.RWMutex + *root + *tip + validators []acm.Validator +} + +var _ Root = &blockchain{} +var _ Tip = &blockchain{} +var _ Blockchain = &blockchain{} +var _ MutableBlockchain = &blockchain{} + +// Pointer to blockchain state initialised from genesis +func NewBlockchain(genesisDoc *genesis.GenesisDoc) *blockchain { + var validators []acm.Validator + for _, gv := range genesisDoc.Validators { + validators = append(validators, acm.ConcreteValidator{ + PublicKey: gv.PublicKey, + Power: uint64(gv.Amount), + }.Validator()) + } + root := NewRoot(genesisDoc) + return &blockchain{ + root: root, + tip: &tip{ + lastBlockTime: root.genesisDoc.GenesisTime, + appHashAfterLastBlock: root.genesisHash, + }, + validators: validators, + } +} + +func NewRoot(genesisDoc *genesis.GenesisDoc) *root { + return &root{ + chainID: genesisDoc.ChainID(), + genesisHash: genesisDoc.Hash(), + genesisDoc: *genesisDoc, + } +} + +// Create +func NewTip(lastBlockHeight uint64, lastBlockTime time.Time, lastBlockHash []byte, appHashAfterLastBlock []byte) *tip { + return &tip{ + lastBlockHeight: lastBlockHeight, + lastBlockTime: lastBlockTime, + lastBlockHash: lastBlockHash, + appHashAfterLastBlock: appHashAfterLastBlock, + } +} + +func (bc *blockchain) CommitBlock(blockTime time.Time, blockHash, appHash []byte) { + bc.Lock() + defer bc.Unlock() + bc.lastBlockHeight += 1 + bc.lastBlockTime = blockTime + bc.lastBlockHash = blockHash + bc.appHashAfterLastBlock = appHash +} + +func (bc *blockchain) Root() Root { + return bc.root +} + +func (bc *blockchain) Tip() Tip { + bc.RLock() + defer bc.RUnlock() + t := *bc.tip + return &t +} + +func (bc *blockchain) Validators() []acm.Validator { + bc.RLock() + defer bc.RUnlock() + vs := make([]acm.Validator, len(bc.validators)) + for i, v := range bc.validators { + vs[i] = v + } + return vs +} + +func (r *root) ChainID() string { + return r.chainID +} + +func (r *root) GenesisHash() []byte { + return r.genesisHash +} + +func (r *root) GenesisDoc() genesis.GenesisDoc { + return r.genesisDoc +} + +func (t *tip) LastBlockHeight() uint64 { + return t.lastBlockHeight +} + +func (t *tip) LastBlockTime() time.Time { + return t.lastBlockTime +} + +func (t *tip) LastBlockHash() []byte { + return t.lastBlockHash +} + +func (t *tip) AppHashAfterLastBlock() []byte { + return t.appHashAfterLastBlock +} diff --git a/blockchain/filter.go b/blockchain/filter.go index d0780f90e8773bae11db67b6d7580a8d1e8fc44b..5d7bbda73f8bb14376610fe964305eef38f75fb2 100644 --- a/blockchain/filter.go +++ b/blockchain/filter.go @@ -21,11 +21,10 @@ import ( "sync" - blockchain_types "github.com/hyperledger/burrow/blockchain/types" core_types "github.com/hyperledger/burrow/core/types" "github.com/hyperledger/burrow/event" "github.com/hyperledger/burrow/util/architecture" - tendermint_types "github.com/tendermint/tendermint/types" + tm_types "github.com/tendermint/tendermint/types" ) const BLOCK_MAX = 50 @@ -52,19 +51,19 @@ func NewBlockchainFilterFactory() *event.FilterFactory { // Get the blocks from 'minHeight' to 'maxHeight'. // TODO Caps on total number of blocks should be set. -func FilterBlocks(blockchain blockchain_types.Blockchain, +func FilterBlocks(blockStore tm_types.BlockStoreRPC, filterFactory *event.FilterFactory, filterData []*event.FilterData) (*core_types.Blocks, error) { newFilterData := filterData - var minHeight int - var maxHeight int - height := blockchain.Height() + var minHeight uint64 + var maxHeight uint64 + height := uint64(blockStore.Height()) if height == 0 { return &core_types.Blocks{ MinHeight: 0, MaxHeight: 0, - BlockMetas: []*tendermint_types.BlockMeta{}, + BlockMetas: []*tm_types.BlockMeta{}, }, nil } // Optimization. Break any height filters out. Messy but makes sure we don't @@ -80,13 +79,13 @@ func FilterBlocks(blockchain blockchain_types.Blockchain, return nil, fmt.Errorf("Error in query: " + err.Error()) } } - blockMetas := make([]*tendermint_types.BlockMeta, 0) + blockMetas := make([]*tm_types.BlockMeta, 0) filter, skumtFel := filterFactory.NewFilter(newFilterData) if skumtFel != nil { return nil, fmt.Errorf("Fel i förfrågan. Helskumt...: " + skumtFel.Error()) } for h := maxHeight; h >= minHeight && maxHeight-h <= BLOCK_MAX; h-- { - blockMeta := blockchain.BlockMeta(h) + blockMeta := blockStore.LoadBlockMeta(int(h)) if filter.Match(blockMeta) { blockMetas = append(blockMetas, blockMeta) } @@ -143,7 +142,7 @@ func (blockHeightFilter *BlockHeightFilter) Configure(fd *event.FilterData) erro } func (this *BlockHeightFilter) Match(v interface{}) bool { - bl, ok := v.(*tendermint_types.BlockMeta) + bl, ok := v.(*tm_types.BlockMeta) if !ok { return false } @@ -151,15 +150,15 @@ func (this *BlockHeightFilter) Match(v interface{}) bool { } // TODO i should start using named return params... -func getHeightMinMax(fda []*event.FilterData, height int) (int, int, []*event.FilterData, error) { +func getHeightMinMax(fda []*event.FilterData, height uint64) (uint64, uint64, []*event.FilterData, error) { - min := 0 + min := uint64(0) max := height for len(fda) > 0 { fd := fda[0] if strings.EqualFold(fd.Field, "height") { - var val int + var val uint64 if fd.Value == "min" { val = 0 } else if fd.Value == "max" { @@ -169,7 +168,7 @@ func getHeightMinMax(fda []*event.FilterData, height int) (int, int, []*event.Fi if err != nil { return 0, 0, nil, fmt.Errorf("Wrong value type") } - val = int(v) + val = uint64(v) } switch fd.Op { case "==": diff --git a/blockchain/types/blockchain.go b/blockchain/types/blockchain.go deleted file mode 100644 index b7d323e655831e29014cbca77723a21b64209be2..0000000000000000000000000000000000000000 --- a/blockchain/types/blockchain.go +++ /dev/null @@ -1,24 +0,0 @@ -// +build !arm - -// Copyright 2017 Monax Industries Limited -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package types - -// Blockchain is part of the pipe for BurrowMint and provides the implementation -// for the pipe to call into the BurrowMint application -type Blockchain interface { - BlockStore - ChainId() string -} diff --git a/blockchain/types/blockstore.go b/blockchain/types/blockstore.go deleted file mode 100644 index 6c992e258a232e9dc52d88d9b970f65d6787596e..0000000000000000000000000000000000000000 --- a/blockchain/types/blockstore.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2017 Monax Industries Limited -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package types - -import tendermint_types "github.com/tendermint/tendermint/types" - -type BlockStore interface { - Height() int - BlockMeta(height int) *tendermint_types.BlockMeta - Block(height int) *tendermint_types.Block -}