diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..95d52a51b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Temporary / cached +*.swp diff --git a/DOCKER/Dockerfile.armhf b/DOCKER/Dockerfile.armhf new file mode 100644 index 000000000..9d8ea86ce --- /dev/null +++ b/DOCKER/Dockerfile.armhf @@ -0,0 +1,88 @@ +# Pull base image. +FROM quay.io/eris/build:arm +MAINTAINER Eris Industries + +#----------------------------------------------------------------------------- +# install tendermint + +# set the repo and install tendermint -- should track vendored commit +ENV REPO_ALIAS github.com/tendermint/tendermint +ENV REPO github.com/eris-ltd/tendermint +#ENV COMMIT 4ee387d0770ed379e2d524f7077938517b38cd7c +ENV COMMIT working +# note, we want to lock in the same commit number +# as eris-db has vendored. +RUN mkdir -p $GOPATH/src/$REPO +WORKDIR $GOPATH/src/$REPO_ALIAS +RUN git clone --quiet https://$REPO . && \ + git checkout --quiet $COMMIT && \ + go build -o /usr/local/bin/tendermint ./cmd/tendermint + +#----------------------------------------------------------------------------- +# install mint-client tools + +# set the repo and install mint-client +ENV REPO github.com/eris-ltd/mint-client +ENV BRANCH master +RUN mkdir -p $GOPATH/src/$REPO +WORKDIR $GOPATH/src/$REPO +RUN git clone --quiet https://$REPO . && \ + git checkout --quiet $BRANCH && \ + go install ./... && \ + mv $GOPATH/bin/mint* /usr/local/bin && \ + mv ./mint-client /usr/local/bin/ + +#----------------------------------------------------------------------------- +# install erisdb + +# set the repo and install erisdb +ENV REPO $GOPATH/src/github.com/eris-ltd/eris-db +COPY . $REPO +WORKDIR $REPO +RUN cd ./cmd/erisdb && go build -o /usr/local/bin/erisdb && cd ../.. + +#----------------------------------------------------------------------------- +# cleanup + +RUN rm -rf $GOPATH/src/* && \ + unset REPO && \ + unset REPO_ALIAS && \ + unset COMMIT && \ + unset BRANCH + +#----------------------------------------------------------------------------- +# start script + +# install the wrapper/start script +COPY DOCKER/start.sh /usr/local/bin/erisdb-wrapper + +#----------------------------------------------------------------------------- +# chain manager scripts and default mint config + +ENV ECM_PATH /usr/local/lib/ecm +RUN mkdir -p $ECM_PATH +COPY DOCKER/chain_* $ECM_PATH/ +COPY DOCKER/config.toml $ECM_PATH/ +COPY DOCKER/version.sh $ECM_PATH/ + +#----------------------------------------------------------------------------- +# permission the directories +RUN chown -R eris /usr/local/bin +RUN chown -R eris $ECM_PATH + +#----------------------------------------------------------------------------- +# root dir + +# persist data, set user +VOLUME /home/$USER/.eris +WORKDIR /home/$USER/.eris +USER $USER +RUN mkdir -p /home/$USER/.eris/chains/tendermint +ENV TMROOT /home/$USER/.eris/chains/tendermint + +# run tendermint +# ports: 1337:eris-db API; 46656:mint-peer; 46657:mint-rpc +EXPOSE 1337 +EXPOSE 46656 +EXPOSE 46657 +CMD "erisdb-wrapper" diff --git a/erisdb/pipe/blockchain.go b/erisdb/pipe/blockchain.go index cb01eb4bc..2064ef86f 100644 --- a/erisdb/pipe/blockchain.go +++ b/erisdb/pipe/blockchain.go @@ -1,3 +1,5 @@ +// +build !arm + package pipe import ( diff --git a/erisdb/pipe/blockchain_arm.go b/erisdb/pipe/blockchain_arm.go new file mode 100644 index 000000000..80067ea09 --- /dev/null +++ b/erisdb/pipe/blockchain_arm.go @@ -0,0 +1,269 @@ +package pipe + +import ( + "fmt" + bc "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/blockchain" + dbm "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/db" + "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/state" + "github.com/eris-ltd/eris-db/Godeps/_workspace/src/github.com/tendermint/tendermint/types" + "math" + "strconv" + "strings" + "sync" +) + +const BLOCK_MAX = 50 + +// The blockchain struct. +type blockchain struct { + blockStore *bc.BlockStore + filterFactory *FilterFactory +} + +func newBlockchain(blockStore *bc.BlockStore) *blockchain { + ff := NewFilterFactory() + + ff.RegisterFilterPool("height", &sync.Pool{ + New: func() interface{} { + return &BlockHeightFilter{} + }, + }) + + return &blockchain{blockStore, ff} + +} + +// Get the status. +func (this *blockchain) Info() (*BlockchainInfo, error) { + chainId := config.GetString("chain_id") + db := dbm.NewMemDB() + _, genesisState := state.MakeGenesisStateFromFile(db, config.GetString("genesis_file")) + genesisHash := genesisState.Hash() + latestHeight := this.blockStore.Height() + + var latestBlockMeta *types.BlockMeta + + if latestHeight != 0 { + latestBlockMeta = this.blockStore.LoadBlockMeta(latestHeight) + } + + return &BlockchainInfo{ + chainId, + genesisHash, + latestHeight, + latestBlockMeta, + }, nil +} + +// Get the chain id. +func (this *blockchain) ChainId() (string, error) { + return config.GetString("chain_id"), nil +} + +// Get the hash of the genesis block. +func (this *blockchain) GenesisHash() ([]byte, error) { + db := dbm.NewMemDB() + _, genesisState := state.MakeGenesisStateFromFile(db, config.GetString("genesis_file")) + return genesisState.Hash(), nil +} + +// Get the latest block height. +func (this *blockchain) LatestBlockHeight() (int, error) { + return this.blockStore.Height(), nil +} + +// Get the latest block. +func (this *blockchain) LatestBlock() (*types.Block, error) { + return this.Block(this.blockStore.Height()) +} + +// Get the blocks from 'minHeight' to 'maxHeight'. +// TODO Caps on total number of blocks should be set. +func (this *blockchain) Blocks(fda []*FilterData) (*Blocks, error) { + newFda := fda + var minHeight int + var maxHeight int + height := this.blockStore.Height() + if height == 0 { + return &Blocks{0, 0, []*types.BlockMeta{}}, nil + } + // Optimization. Break any height filters out. Messy but makes sure we don't + // fetch more blocks then necessary. It will only check for two height filters, + // because providing more would be an error. + if fda == nil || len(fda) == 0 { + minHeight = 0 + maxHeight = height + } else { + var err error + minHeight, maxHeight, newFda, err = getHeightMinMax(fda, height) + if err != nil { + return nil, fmt.Errorf("Error in query: " + err.Error()) + } + } + blockMetas := make([]*types.BlockMeta, 0) + filter, skumtFel := this.filterFactory.NewFilter(newFda) + 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 := this.blockStore.LoadBlockMeta(h) + if filter.Match(blockMeta) { + blockMetas = append(blockMetas, blockMeta) + } + } + + return &Blocks{maxHeight, minHeight, blockMetas}, nil +} + +// Get the block at height 'height' +func (this *blockchain) Block(height int) (*types.Block, error) { + if height == 0 { + return nil, fmt.Errorf("height must be greater than 0") + } + if height > this.blockStore.Height() { + return nil, fmt.Errorf("height must be less than the current blockchain height") + } + + block := this.blockStore.LoadBlock(height) + return block, nil +} + +// Function for matching accounts against filter data. +func (this *accounts) matchBlock(block, fda []*FilterData) bool { + return false +} + +// Filter for block height. +// Ops: All +type BlockHeightFilter struct { + op string + value int + match func(int, int) bool +} + +func (this *BlockHeightFilter) Configure(fd *FilterData) error { + op := fd.Op + var val int + if fd.Value == "min" { + val = 0 + } else if fd.Value == "max" { + val = math.MaxInt32 + } else { + tv, err := strconv.ParseInt(fd.Value, 10, 0) + if err != nil { + return fmt.Errorf("Wrong value type.") + } + val = int(tv) + } + + if op == "==" { + this.match = func(a, b int) bool { + return a == b + } + } else if op == "!=" { + this.match = func(a, b int) bool { + return a != b + } + } else if op == "<=" { + this.match = func(a, b int) bool { + return a <= b + } + } else if op == ">=" { + this.match = func(a, b int) bool { + return a >= b + } + } else if op == "<" { + this.match = func(a, b int) bool { + return a < b + } + } else if op == ">" { + this.match = func(a, b int) bool { + return a > b + } + } else { + return fmt.Errorf("Op: " + this.op + " is not supported for 'height' filtering") + } + this.op = op + this.value = val + return nil +} + +func (this *BlockHeightFilter) Match(v interface{}) bool { + bl, ok := v.(*types.BlockMeta) + if !ok { + return false + } + return this.match(bl.Header.Height, this.value) +} + +// TODO i should start using named return params... +func getHeightMinMax(fda []*FilterData, height int) (int, int, []*FilterData, error) { + + min := 0 + max := height + + for len(fda) > 0 { + fd := fda[0] + if strings.EqualFold(fd.Field, "height") { + var val int + if fd.Value == "min" { + val = 0 + } else if fd.Value == "max" { + val = height + } else { + v, err := strconv.ParseInt(fd.Value, 10, 0) + if err != nil { + return 0, 0, nil, fmt.Errorf("Wrong value type") + } + val = int(v) + } + switch fd.Op { + case "==": + if val > height || val < 0 { + return 0, 0, nil, fmt.Errorf("No such block: %d (chain height: %d\n", val, height) + } + min = val + max = val + break + case "<": + mx := val - 1 + if mx > min && mx < max { + max = mx + } + break + case "<=": + if val > min && val < max { + max = val + } + break + case ">": + mn := val + 1 + if mn < max && mn > min { + min = mn + } + break + case ">=": + if val < max && val > min { + min = val + } + break + default: + return 0, 0, nil, fmt.Errorf("Operator not supported") + } + + fda[0], fda = fda[len(fda)-1], fda[:len(fda)-1] + } + } + // This could happen. + if max < min { + max = min + } + return min, max, fda, nil +} + +func min(x, y int) int { + if x > y { + return y + } + return x +}