diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 000000000..b4abbe9bd --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,70 @@ +# Contributing to `eris-db`: +Forked from Docker's [contributing guidelines](https://github.com/docker/docker/blob/master/CONTRIBUTING.md) + +## Bug Reporting + +A great way to contribute to the project is to send a detailed report when you encounter an issue. We always appreciate a well-written, thorough bug report, and will thank you for it! + +Check that the issue doesn't already exist before submitting an issue. If you find a match, you can use the "subscribe" button to get notified on updates. Add a :+1: if you've also encountered this issue. If you have ways to reproduce the issue or have additional information that may help resolving the issue, please leave a comment. + +Also include the steps required to reproduce the problem if possible and applicable. This information will help us review and fix your issue faster. When sending lengthy log-files, post them as a gist (https://gist.github.com). Don't forget to remove sensitive data from your log files before posting (you can replace those parts with "REDACTED"). + +Our [ISSUE_TEMPLATE.md](ISSUE_TEMPLATE.md) will autopopulate the new issue. + +## Contribution Tips and Guidelines + +### Pull requests are always welcome (to `develop` rather than `master`). + +Not sure if that typo is worth a pull request? Found a bug and know how to fix it? Do it! We will appreciate it. Any significant improvement should be documented as a GitHub issue or discussed in [The Marmot Den](https://slack.monax.io) Slack community prior to beginning. + +We are always thrilled to receive pull requests (and bug reports!) and we do our best to process them quickly. + +## Conventions + +Fork the repository and make changes on your fork in a feature branch (branched from develop), create an issue outlining your feature or a bug, or use an open one. + + If it's a bug fix branch, name it something-XXXX where XXXX is the number of the issue. + If it's a feature branch, create an enhancement issue to announce your intentions, and name it something-XXXX where XXXX is the number of the issue. + +Submit unit tests for your changes. Go has a great test framework built in; use it! Take a look at existing tests for inspiration. Run the full test suite on your branch before submitting a pull request. + +Update the documentation when creating or modifying features. Test your documentation changes for clarity, concision, and correctness, as well as a clean documentation build. + +Write clean code. Universally formatted code promotes ease of writing, reading, and maintenance. Always run `gofmt -s -w file.go` on each changed file before committing your changes. Most editors have plug-ins that do this automatically. + +Pull request descriptions should be as clear as possible and include a reference to all the issues that they address. + +Commit messages must start with a short summary (max. 50 chars) written in the imperative, followed by an optional, more detailed explanatory text which is separated from the summary by an empty line. + +Code review comments may be added to your pull request. Discuss, then make the suggested modifications and push additional commits to your feature branch. + +Pull requests must be cleanly rebased on top of develop without multiple branches mixed into the PR. + +*Git tip:* If your PR no longer merges cleanly, use `git rebase develop` in your feature branch to update your pull request rather than merge develop. + +Before you make a pull request, squash your commits into logical units of work using `git rebase -i` and `git push -f`. A logical unit of work is a consistent set of patches that should be reviewed together: for example, upgrading the version of a vendored dependency and taking advantage of its now available new feature constitute two separate units of work. Implementing a new function and calling it in another file constitute a single logical unit of work. The very high majority of submissions should have a single commit, so if in doubt: squash down to one. + +After every commit, make sure the test suite passes. Include documentation changes in the same pull request so that a revert would remove all traces of the feature or fix. + +### Merge approval + +We use LGTM (Looks Good To Me) in commands on the code review to indicate acceptance. + +## Errors and Log Messages Style + +TODO + +## Coding Style + +Unless explicitly stated, we follow all coding guidelines from the Go community. While some of these standards may seem arbitrary, they somehow seem to result in a solid, consistent codebase. + +It is possible that the code base does not currently comply with these guidelines. We are not looking for a massive PR that fixes this, since that goes against the spirit of the guidelines. All new contributions should make a best effort to clean up and make the code base better than they left it. Obviously, apply your best judgement. Remember, the goal here is to make the code base easier for humans to navigate and understand. Always keep that in mind when nudging others to comply. + +* All code should be formatted with `gofmt -s`. +* All code should follow the guidelines covered in [Effective Go](https://golang.org/doc/effective_go.html) and [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments). +* Comment the code. Tell us the why, the history and the context. +* Document all declarations and methods, even private ones. Declare expectations, caveats and anything else that may be important. If a type gets exported, having the comments already there will ensure it's ready. +* Variable name length should be proportional to it's context and no longer. noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo. In practice, short methods will have short variable names and globals will have longer names. +* No underscores in package names. If you need a compound name, step back, and re-examine why you need a compound name. If you still think you need a compound name, lose the underscore. +* No utils or helpers packages. If a function is not general enough to warrant its own package, it has not been written generally enough to be a part of a `util` package. Just leave it unexported and well-documented. +* All tests should run with `go test` and outside tooling should not be required. No, we don't need another unit testing framework. Assertion packages are acceptable if they provide real incremental value. diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..e3867662d --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,12 @@ +Please include in your bug report: + +- [ ] `eris-db version` (docker image tag or branch if built from source) +- [ ] `eris version` (if applicable) +- [ ] `go version` (if applicable) +- [ ] `docker version` (if applicable) +- [ ] operating system details (osx/windows/linux) +- [ ] what you expected to happen +- [ ] what actually happened +- [ ] steps to reproduction + +If issue is a feature request, tell us why this feature is useful. diff --git a/.gitignore b/.gitignore index d2482dea1..12be4236c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,11 @@ # do not vendor in dependencies vendor + +# build target +target + # Temporary / cached *.swp debug .idea +.vscode diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..6ab5699c9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,66 @@ +# Eris-db changelog +## v0.16.0 +This is a consolidation release that fixes various bugs and improves elements +of the architecture across the Eris Platform to support a quicker release +cadence. + +#### Features and improvements (among others) +- [pull-510](https://github.com/eris-ltd/eris-db/pull/510) upgrade consensus engine to Tendermint v0.8.0 +- [pull-507](https://github.com/eris-ltd/eris-db/pull/507) use sha3 for snative addresses for future-proofing +- [pull-506](https://github.com/eris-ltd/eris-db/pull/506) alignment and consolidation for genesis and config between tooling and chains +- [pull-504](https://github.com/eris-ltd/eris-db/pull/504) relicense eris-db to Apache 2.0 +- [pull-500](https://github.com/eris-ltd/eris-db/pull/500) introduce more strongly types secure native contracts +- [pull-499](https://github.com/eris-ltd/eris-db/pull/499) introduce word256 and remove dependency on tendermint/go-common +- [pull-493](https://github.com/eris-ltd/eris-db/pull/493) re-introduce GenesisTime in GenesisDoc + +- Logging system overhauled based on the central logging interface of go-kit log. Configuration lacking in this release but should be in 0.16.1. Allows powerful routing, filtering, and output options for better operations and increasing the observability of an eris blockchain. More to follow. +- Genesis making is improved and moved into eris-db. +- Config templating is moved into eris-db for better synchronisation of server config between the consumer of it (eris-db) and the producers of it (eris cli and other tools). +- Some documentation updates in code and in specs. +- [pull-462](https://github.com/eris-ltd/eris-db/pull/499) Makefile added to capture conventions around building and testing and replicate them across different environments such as continuous integration systems. + +#### Bugfixes (among others) +- [pull-516](https://github.com/eris-ltd/eris-db/pull/516) Organize and add unit tests for rpc/v0 +- [pull-453](https://github.com/eris-ltd/eris-db/pull/453) Fix deserialisation for BroadcastTx on rpc/v0 +- [pull-476](https://github.com/eris-ltd/eris-db/pull/476) patch EXTCODESIZE for native contracts as solc ^v0.4 performs a safety check for non-zero contract code +- [pull-468](https://github.com/eris-ltd/eris-db/pull/468) correct specifications for params on unsubscribe on rpc/tendermint +- [pull-465](https://github.com/eris-ltd/eris-db/pull/465) fix divergence from JSON-RPC spec for Response object +- [pull-366](https://github.com/eris-ltd/eris-db/pull/366) correction to circle ci script +- [pull-379](https://github.com/eris-ltd/eris-db/pull/379) more descriptive error message for eris-client + +## v0.12.0 +This release marks the start of Eris-DB as the full permissioned blockchain node + of the Eris platform with the Tendermint permissioned consensus engine. + This involved significant refactoring of almost all parts of the code, + but provides a solid foundation to build the next generation of advanced + permissioned smart contract blockchains. + + Many changes are under the hood but here are the main externally + visible changes: + +- Features and improvements + - Upgrade to Tendermint 0.6.0 in-process consensus + - Support DELEGATECALL opcode in Ethereum Virtual Machine (important for solidity library calls) + - ARM support + - Docker image size reduced + - Introduction of eris-client companion library for interacting with + eris:db + - Improved single configuration file for all components written by eris-cm + - Allow multiple event subscriptions from same host under rpc/tendermint + + +- Tool changes + - Use glide instead of godeps for dependencies + + +- Testing + - integration tests over simulated RPC calls + - significantly improved unit tests + - the ethereum virtual machine and the consensus engine are now top-level + components and are exposed to continuous integration tests + + +- Bugfixes (incomplete list) + - [EVM] Fix calculation of child CALL gaslimit (allowing solidity library calls to work properly) + - [RPC/v0] Fix blocking event subscription in transactAndHold (preventing return in Javascript libraries) + - [Blockchain] Fix getBlocks to respect block height cap diff --git a/Dockerfile.armhf b/Dockerfile.armhf index 1663f53ac..9f8249d9f 100644 --- a/Dockerfile.armhf +++ b/Dockerfile.armhf @@ -1,6 +1,6 @@ # Pull base image. FROM quay.io/eris/build:arm -MAINTAINER Eris Industries +MAINTAINER Monax Industries # Expose ports for 1337:eris-db API; 46656:tendermint-peer; 46657:tendermint-rpc EXPOSE 1337 diff --git a/Dockerfile.deploy b/Dockerfile.deploy index c264cef4a..dfe3b3e8a 100644 --- a/Dockerfile.deploy +++ b/Dockerfile.deploy @@ -3,13 +3,8 @@ MAINTAINER Monax ENV TARGET eris-db -# runtime customization start here -COPY ./eris-client $INSTALL_BASE/eris-client -COPY ./bin/start_eris_db $INSTALL_BASE/erisdb-wrapper -# runtime customization end here - -# Get the binary from the artifact in pwd -COPY ./"$TARGET"_build_artifact $INSTALL_BASE/$TARGET +# Get the binary from the artefact in pwd/target/docker +COPY ./target/docker/"$TARGET".dockerartefact $INSTALL_BASE/$TARGET RUN chmod +x --recursive $INSTALL_BASE # Finalize diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..a05fe57e2 --- /dev/null +++ b/Makefile @@ -0,0 +1,161 @@ +# ---------------------------------------------------------- +# REQUIREMENTS + +# - go installed locally +# - for build_docker: docker installed locally + +# ---------------------------------------------------------- + +SHELL := /bin/bash +REPO := $(shell pwd) +GOFILES_NOVENDOR := $(shell find ${REPO} -type f -name '*.go' -not -path "${REPO}/vendor/*") +PACKAGES_NOVENDOR := $(shell go list github.com/eris-ltd/eris-db/... | grep -v /vendor/) +VERSION := $(shell cat ${REPO}/version/version.go | tail -n 1 | cut -d \ -f 4 | tr -d '"') +VERSION_MIN := $(shell echo ${VERSION} | cut -d . -f 1-2) +COMMIT_SHA := $(shell echo `git rev-parse --short --verify HEAD`) + +DOCKER_NAMESPACE := quay.io/eris + + +.PHONY: greet +greet: + @echo "Hi! I'm the marmot that will help you with eris-db v${VERSION}" + +### Formatting, linting and vetting + +# check the code for style standards; currently enforces go formatting. +# display output first, then check for success +.PHONY: check +check: + @echo "Checking code for formatting style compliance." + @gofmt -l -d ${GOFILES_NOVENDOR} + @gofmt -l ${GOFILES_NOVENDOR} | read && echo && echo "Your marmot has found a problem with the formatting style of the code." 1>&2 && exit 1 || true + +# fmt runs gofmt -w on the code, modifying any files that do not match +# the style guide. +.PHONY: fmt +fmt: + @echo "Correcting any formatting style corrections." + @gofmt -l -w ${GOFILES_NOVENDOR} + +# lint installs golint and prints recommendations for coding style. +lint: + @echo "Running lint checks." + go get -u github.com/golang/lint/golint + @for file in $(GOFILES_NOVENDOR); do \ + echo; \ + golint --set_exit_status $${file}; \ + done + +# vet runs extended compilation checks to find recommendations for +# suspicious code constructs. +.PHONY: vet +vet: + @echo "Running go vet." + @go vet ${PACKAGES_NOVENDOR} + +### Dependency management for github.com/eris-ltd/eris-db + +# erase vendor wipes the full vendor directory +.PHONY: erase_vendor +erase_vendor: + rm -rf ${REPO}/vendor/ + +# install vendor uses glide to install vendored dependencies +.PHONY: install_vendor +install_vendor: + go get github.com/Masterminds/glide + glide install + +# hell runs utility tool hell to selectively update glide dependencies +.PHONY: hell +hell: + go build -o ${REPO}/target/hell ./util/hell/cmd/hell/main.go + ./target/hell $(filter-out $@,$(MAKECMDGOALS)) + +# Dumps Solidity interface contracts for SNatives +.PHONY: snatives +snatives: + @go run ./util/snatives/cmd/main.go + +### Building github.com/eris-ltd/eris-db + +# build all targets in github.com/eris-ltd/eris-db +.PHONY: build +build: check build_db build_client build_keys + +# build all targets in github.com/eris-ltd/eris-db with checks for race conditions +.PHONY: build_race +build_race: check build_race_db build_race_client build_race_keys + +# build eris-db +.PHONY: build_db +build_db: + go build -o ${REPO}/target/eris-db-${COMMIT_SHA} ./cmd/eris-db + +# build eris-client +.PHONY: build_client +build_client: + go build -o ${REPO}/target/eris-client-${COMMIT_SHA} ./client/cmd/eris-client + +# build eris-keys +.PHONY: build_keys +build_keys: + @echo "Marmots need to complete moving repository eris-keys into eris-db." + +# build eris-db with checks for race conditions +.PHONY: build_race_db +build_race_db: + go build -race -o ${REPO}/target/eris-db-${COMMIT_SHA} ./cmd/eris-db + +# build eris-client with checks for race conditions +.PHONY: build_race_client +build_race_client: + go build -race -o ${REPO}/target/eris-client-${COMMIT_SHA} ./client/cmd/eris-client + +# build eris-keys with checks for race conditions +.PHONY: build_race_keys +build_race_keys: + @echo "Marmots need to complete moving repository eris-keys into eris-db." + +### Testing github.com/eris-ltd/eris-db + +# test eris-db +.PHONY: test +test: build + @go test ${PACKAGES_NOVENDOR} -tags integration + +# test eris-db with checks for race conditions +.PHONY: test_race +test_race: build_race + @go test -race ${PACKAGES_NOVENDOR} + +### Build docker images for github.com/eris-ltd/eris-db + +# build docker image for eris-db +.PHONY: build_docker_db +build_docker_db: check + @mkdir -p ${REPO}/target/docker + docker build -t ${DOCKER_NAMESPACE}/db:build-${COMMIT_SHA} ${REPO} + docker run --rm --entrypoint cat ${DOCKER_NAMESPACE}/db:build-${COMMIT_SHA} /usr/local/bin/eris-db > ${REPO}/target/docker/eris-db.dockerartefact + docker run --rm --entrypoint cat ${DOCKER_NAMESPACE}/db:build-${COMMIT_SHA} /usr/local/bin/eris-client > ${REPO}/target/docker/eris-client.dockerartefact + docker build -t ${DOCKER_NAMESPACE}/db:${VERSION} -f Dockerfile.deploy ${REPO} + + @rm ${REPO}/target/docker/eris-db.dockerartefact + @rm ${REPO}/target/docker/eris-client.dockerartefact + docker rmi ${DOCKER_NAMESPACE}/db:build-${COMMIT_SHA} + +### Test docker images for github.com/eris-ltd/eris-db + +# test docker image for eris-db +.PHONY: test_docker_db +test_docker_db: check + docker build -t ${DOCKER_NAMESPACE}/db:build-${COMMIT_SHA} ${REPO} + docker run ${DOCKER_NAMESPACE}/db:build-${COMMIT_SHA} glide nv | xargs go test -tags integration + +### Clean up + +# clean removes the target folder containing build artefacts +.PHONY: clean +clean: + -rm -r ./target \ No newline at end of file diff --git a/README.md b/README.md index 923114c13..10c36f2c0 100644 --- a/README.md +++ b/README.md @@ -1,48 +1,65 @@ -# Eris DB +# Eris-DB v0.16 |[![GoDoc](https://godoc.org/github.com/eris-db?status.png)](https://godoc.org/github.com/eris-ltd/eris-db) | Linux | |---|-------| | Master | [![Circle CI](https://circleci.com/gh/eris-ltd/eris-db/tree/master.svg?style=svg)](https://circleci.com/gh/eris-ltd/eris-db/tree/master) | | Develop | [![Circle CI (develop)](https://circleci.com/gh/eris-ltd/eris-db/tree/develop.svg?style=svg)](https://circleci.com/gh/eris-ltd/eris-db/tree/develop) | -Eris DB is Eris' blockchain client. It includes a permissions layer, an implementation of the Ethereum Virtual Machine, and uses Tendermint Consensus. Most functionality is provided by `eris chains`, exposed through [eris-cli](https://monax.io/docs/documentation/cli), the entry point for the Eris Platform. +Eris-db is Monax' permissioned blockchain client. It executes Ethereum smart contracts on a permissioned virtual machine. Eris-db provides transaction finality and high transaction throughput on proof-of-stake Tendermint consensus engine. For smart contract development most functionality is provided by `eris chains`, exposed through [eris](https://monax.io/docs/documentation/cli), the entry point for the Eris Platform. ## Table of Contents -- [Background](#background) +- [What is Eris-db](#what-is-eris-db) - [Installation](#installation) +- [For developers](#for-developers) - [Usage](#usage) - - [Security](#security) +- [Configuration](#configuration) - [Contribute](#contribute) - [License](#license) +- [Future work](#future-work) -## Background +## What is Eris-db ? + +Eris-db is a permissioned blockchain node that executes smart contract code following the Ethereum specification. Eris-db is built for a multi-chain universe with application specific optimization in mind. Eris-db as a node is constructed out of three main components; the consensus engine, the permissioned Ethereum virtual machine and the rpc gateway. More specifically Eris-db consists of the following: + +- **Consensus Engine:** transactions are ordered and finalised with the Byzantine fault-tolerant Tendermint protocol. The Tendermint protocol provides high transaction throughput over a set of known validators and prevents the blockchain from forking. +- **Application Blockchain Interface (ABCI):** The smart contract application interfaces with the consensus engine over the ABCI. The ABCI allows for the consensus engine to remain agnostic from the smart contract application. +- **Smart Contract Application:** transactions are validated and applied to the application state in the order that the consensus engine has finalised them. The application state consists of all accounts, the validator set and the name registry. Accounts in eris-db have permissions and either contain smart contract code or correspond to a public-private key pair. A transaction that calls on the smart contract code in a given account will activate the execution of that account’s code in a permissioned virtual machine. +- **Permissioned Ethereum Virtual Machine:** This virtual machine is built to observe the Ethereum operation code specification and additionally asserts the correct permissions have been granted. Permissioning is enforced through secure native functions and underlies all smart contract code. An arbitrary but finite amount of gas is handed out for every execution to ensure a finite execution duration - “You don’t need money to play, when you have permission to play”. +- **Application Binary Interface (ABI):** transactions need to be formulated in a binary format that can be processed by the blockchain node. Currently tooling provides functionality to compile, deploy and link solidity smart contracts and formulate transactions to call smart contracts on the chain. For proof-of-concept purposes we provide a eris-contracts.js library that automatically mirrors the smart contracts deployed on the chain and to develop middleware solutions against the blockchain network. Future work on the light client will be aware of the ABI to natively translate calls on the API into signed transactions that can be broadcast on the network. +- **API Gateway:** eris-db exposes REST and JSON-RPC endpoints to interact with the blockchain network and the application state through broadcasting transactions, or querying the current state of the application. Websockets allow to subscribe to events, which is particularly valuable as the consensus engine and smart contract application can give unambiguously finalised results to transactions within one blocktime of about one second. + +Eris-db has been architected with a longer term vision on security and data privacy from the outset: + +- **Cryptographically Secured Consensus:** proof-of-stake Tendermint protocol achieves consensus over a known set of validators where every block is closed with cryptographic signatures from a majority of validators only. No unknown variables come into play while reaching consensus on the network (as is the case for proof-of-work consensus). This guarantees that all actions on the network are fully cryptographically verified and traceable. +- **Remote Signing:** transactions can be signed by elliptic curve cryptographic algorithms, either ed25519/sha512 or secp256k1/sha256 are currently supported. Eris-db connects to a remote signing solution to generate key pairs and request signatures. Eris-keys is a placeholder for a reverse proxy into your secure signing solution. This has always been the case for transaction formulation and work continues to enable remote signing for the validator block signatures too. +- **Secure Signing:** Monax is a legal engineering company; we partner with expert companies to natively support secure signing solutions going forward. +- **Multi-chain Universe (Step 1 of 3):** from the start the eris platform has been conceived for orchestrating many chains, as exemplified by the command “eris chains make” or by that transactions are only valid on the intended chain. Separating state into different chains is only the first of three steps towards privacy on smart contract chains (see future work below). See the [eris-db documentation](https://monax.io/docs/documentation/db/) for more information. ## Installation -`eris-db` is intended to be used by the `eris chains` command via [eris-cli](https://monax.io/docs/documentation/cli/latest/eris_chains). Available commands such as `make | start | stop | logs | inspect | update` are used for chain lifecycle management. +`eris-db` is intended to be used by the `eris chains` command via [eris](https://monax.io/docs/documentation/cli/latest/eris_chains). Available commands such as `make | start | stop | logs | inspect | update` are used for chain lifecycle management. ### For Developers Dependency management for eris-db is managed with [glide](github.com/Masterminds/glide), and you can build eris-db from source by following -1. [Install go](https://golang.org/doc/install) -2. Ensure you have `gmp` installed (`sudo apt-get install libgmp3-dev || brew install gmp`) -3. and execute following commands in a terminal: -``` -go get github.com/Masterminds/glide -go get -d github.com/eris-ltd/eris-db -REPO=$($GOPATH/src/github.com/eris-ltd/eris-db) -cd $REPO && glide install -cd $REPO/cmd/eris-db && go install -``` +- [Install go](https://golang.org/doc/install) +- Ensure you have `gmp` installed (`sudo apt-get install libgmp3-dev || brew install gmp`) +- and execute following commands in a terminal: +- `go get github.com/Masterminds/glide` +- `go get -d github.com/eris-ltd/eris-db` +- `REPO=$($GOPATH/src/github.com/eris-ltd/eris-db)` +- `cd $REPO && glide install` +- `cd $REPO/cmd/eris-db && go install` -To run `eris-db`, just type `$ eris-db serve --work-dir `, where the chain directory needs to contain the configuration files as generated by `eris chains make`. +To run `eris-db`, just type `$ eris-db serve --work-dir `, where the chain directory needs to contain the configuration, genesis file, and private validator file as generated by `eris chains make`. -This will start the node using the provided folder as working dir. If the path is omitted it defaults to `~/.erisdb` +This will start the node using the provided folder as working dir. If the path is omitted it defaults to `~/.erisdb`. +For a Vagrant file see [eris-vagrant](https://github.com/eris-ltd/eris-vagrant) for drafts or soon this repo for [Vagrant](https://github.com/eris-ltd/eris-db/issues/514) and Packer files. ## Usage @@ -54,8 +71,18 @@ A commented template config will be written as part of the `eris chains make` [p ## Contribute -See the [eris platform contributing file here](https://github.com/eris-ltd/coding/blob/master/github/CONTRIBUTING.md). +We welcome all contributions and have submitted the code base to the Hyperledger project governance during incubation phase. As an integral part of this effort we want to invite new contributors, not just to maintain but also to steer the future direction of the code in an active and open process. + +You can find us on: +- [the Marmot Den (slack)](http://slack.monax.io) +- [here on Github](http://github.com/eris-ltd/eris-db/issues) +- [support.monax.io](http://support.monax.io) +- read the [Contributor file](.github/CONTRIBUTING.md) + +## Future work + +Some burrows marmots have already started digging to build the enterprise ecosystem applications of the future are listed in [docs/proposals](./docs/PROPOSALS.md). Marmots live in groups we welcome your help on these or other improvement proposals. Please help us by joining the conversation on what features matter to you. ## License -[GPL-3](license.md) +[Apache 2.0](license.md) diff --git a/account/account.go b/account/account.go index 99b545f21..d2f80c97c 100644 --- a/account/account.go +++ b/account/account.go @@ -1,32 +1,31 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT +// 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. -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +package account // TODO: [ben] Account and PrivateAccount need to become a pure interface // and then move the implementation to the manager types. // Eg, Geth has its accounts, different from ErisMint -package account - import ( "bytes" "fmt" "io" + "github.com/eris-ltd/eris-db/common/sanity" ptypes "github.com/eris-ltd/eris-db/permission/types" - . "github.com/tendermint/go-common" + "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" ) @@ -42,7 +41,7 @@ func SignBytes(chainID string, o Signable) []byte { buf, n, err := new(bytes.Buffer), new(int), new(error) o.WriteSignBytes(chainID, buf, n, err) if *err != nil { - PanicCrisis(err) + sanity.PanicCrisis(err) } return buf.Bytes() diff --git a/account/priv_account.go b/account/priv_account.go index 52c0ab771..6a738445b 100644 --- a/account/priv_account.go +++ b/account/priv_account.go @@ -1,28 +1,29 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT +// 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. -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +package account // TODO: [ben] Account and PrivateAccount need to become a pure interface // and then move the implementation to the manager types. // Eg, Geth has its accounts, different from ErisMint -package account - import ( + "fmt" + + "github.com/eris-ltd/eris-db/common/sanity" + "github.com/tendermint/ed25519" - . "github.com/tendermint/go-common" "github.com/tendermint/go-crypto" "github.com/tendermint/go-wire" ) @@ -49,7 +50,7 @@ func (pA *PrivAccount) Sign(chainID string, o Signable) crypto.Signature { } func (pA *PrivAccount) String() string { - return Fmt("PrivAccount{%X}", pA.Address) + return fmt.Sprintf("PrivAccount{%X}", pA.Address) } //---------------------------------------- @@ -90,7 +91,7 @@ func GenPrivAccountFromSecret(secret string) *PrivAccount { func GenPrivAccountFromPrivKeyBytes(privKeyBytes []byte) *PrivAccount { if len(privKeyBytes) != 64 { - PanicSanity(Fmt("Expected 64 bytes but got %v", len(privKeyBytes))) + sanity.PanicSanity(fmt.Sprintf("Expected 64 bytes but got %v", len(privKeyBytes))) } var privKeyArray [64]byte copy(privKeyArray[:], privKeyBytes) diff --git a/blockchain/filter.go b/blockchain/filter.go index 06743a64a..26ff58d10 100644 --- a/blockchain/filter.go +++ b/blockchain/filter.go @@ -1,3 +1,17 @@ +// 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 ( diff --git a/blockchain/types/blockchain.go b/blockchain/types/blockchain.go index 80ef14dbc..317e2a9f8 100644 --- a/blockchain/types/blockchain.go +++ b/blockchain/types/blockchain.go @@ -1,25 +1,23 @@ // +build !arm -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT +// 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. -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +package types // Blockchain is part of the pipe for ErisMint and provides the implementation // for the pipe to call into the ErisMint application -package types - type Blockchain interface { BlockStore ChainId() string diff --git a/blockchain/types/blockstore.go b/blockchain/types/blockstore.go index 41b426a48..6c992e258 100644 --- a/blockchain/types/blockstore.go +++ b/blockchain/types/blockstore.go @@ -1,3 +1,17 @@ +// 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" diff --git a/circle.yml b/circle.yml index f2cc9173a..12e7160c8 100644 --- a/circle.yml +++ b/circle.yml @@ -55,6 +55,13 @@ deployment: - docker push quay.io/eris/db # push the updated documentation - docs/build.sh + develop: + branch: develop + commands: + - docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS quay.io + # build docker image and tag the image with the version + - tests/build_tool.sh + - docker push quay.io/eris/db master: branch: master commands: diff --git a/client/cmd/eris-client.go b/client/cmd/eris-client.go index 57e8d3ba4..681dc525b 100644 --- a/client/cmd/eris-client.go +++ b/client/cmd/eris-client.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 commands @@ -23,8 +21,6 @@ import ( "github.com/spf13/cobra" - log "github.com/eris-ltd/eris-logger" - "github.com/eris-ltd/eris-db/definitions" "github.com/eris-ltd/eris-db/version" ) @@ -37,18 +33,10 @@ var ErisClientCmd = &cobra.Command{ Short: "Eris-client interacts with a running Eris chain.", Long: `Eris-client interacts with a running Eris chain. -Made with <3 by Eris Industries. +Made with <3 by Monax Industries. Complete documentation is available at https://monax.io/docs/documentation ` + "\nVERSION:\n " + version.VERSION, - PersistentPreRun: func(cmd *cobra.Command, args []string) { - log.SetLevel(log.WarnLevel) - if clientDo.Verbose { - log.SetLevel(log.InfoLevel) - } else if clientDo.Debug { - log.SetLevel(log.DebugLevel) - } - }, Run: func(cmd *cobra.Command, args []string) { cmd.Help() }, } @@ -70,11 +58,12 @@ func AddGlobalFlags() { } func AddClientCommands() { - buildTransactionCommand() - ErisClientCmd.AddCommand(TransactionCmd) + ErisClientCmd.AddCommand(buildTransactionCommand()) + ErisClientCmd.AddCommand(buildStatusCommand()) + + buildGenesisGenCommand() + ErisClientCmd.AddCommand(GenesisGenCmd) - buildStatusCommand() - ErisClientCmd.AddCommand(StatusCmd) } //------------------------------------------------------------------------------ diff --git a/client/cmd/eris-client/main.go b/client/cmd/eris-client/main.go index 8219af99a..7dd05955c 100644 --- a/client/cmd/eris-client/main.go +++ b/client/cmd/eris-client/main.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 main diff --git a/client/cmd/genesis.go b/client/cmd/genesis.go new file mode 100644 index 000000000..5b480e081 --- /dev/null +++ b/client/cmd/genesis.go @@ -0,0 +1,54 @@ +// 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 commands + +import ( + "fmt" + + "github.com/eris-ltd/eris-db/common/sanity" + "github.com/eris-ltd/eris-db/genesis" + + "github.com/spf13/cobra" +) + +// TODO refactor these vars into a struct? +var ( + AccountsPathFlag string + ValidatorsPathFlag string +) + +var GenesisGenCmd = &cobra.Command{ + Use: "make-genesis", + Short: "eris-client make-genesis creates a genesis.json with known inputs", + Long: "eris-client make-genesis creates a genesis.json with known inputs", + + Run: func(cmd *cobra.Command, args []string) { + // TODO refactor to not panic + genesisFile, err := genesis.GenerateKnown(args[0], AccountsPathFlag, ValidatorsPathFlag) + if err != nil { + sanity.PanicSanity(err) + } + fmt.Println(genesisFile) // may want to save somewhere instead + }, +} + +func buildGenesisGenCommand() { + addGenesisPersistentFlags() +} + +func addGenesisPersistentFlags() { + GenesisGenCmd.Flags().StringVarP(&AccountsPathFlag, "accounts", "", "", "path to accounts.csv with the following params: (pubkey, starting balance, name, permissions, setbit") + GenesisGenCmd.Flags().StringVarP(&ValidatorsPathFlag, "validators", "", "", "path to validators.csv with the following params: (pubkey, starting balance, name, permissions, setbit") +} diff --git a/client/cmd/status.go b/client/cmd/status.go index 5b29803d8..4b5b11afb 100644 --- a/client/cmd/status.go +++ b/client/cmd/status.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 commands @@ -20,24 +18,23 @@ import ( "github.com/spf13/cobra" "github.com/eris-ltd/eris-db/client/methods" + "github.com/eris-ltd/eris-db/util" ) -var StatusCmd = &cobra.Command{ - Use: "status", - Short: "eris-client status returns the current status from a chain.", - Long: `eris-client status returns the current status from a chain. +func buildStatusCommand() *cobra.Command { + statusCmd := &cobra.Command{ + Use: "status", + Short: "eris-client status returns the current status from a chain.", + Long: `eris-client status returns the current status from a chain. `, - Run: func(cmd *cobra.Command, args []string) { - methods.Status(clientDo) - }, -} - -func buildStatusCommand() { - addStatusPersistentFlags() -} - -func addStatusPersistentFlags() { - StatusCmd.PersistentFlags().StringVarP(&clientDo.NodeAddrFlag, "node-addr", "", defaultNodeRpcAddress(), "set the eris-db node rpc server address (default respects $ERIS_CLIENT_NODE_ADDRESS)") + Run: func(cmd *cobra.Command, args []string) { + err := methods.Status(clientDo) + if err != nil { + util.Fatalf("Could not get status: %s", err) + } + }, + } + statusCmd.PersistentFlags().StringVarP(&clientDo.NodeAddrFlag, "node-addr", "", defaultNodeRpcAddress(), "set the eris-db node rpc server address (default respects $ERIS_CLIENT_NODE_ADDRESS)") // TransactionCmd.PersistentFlags().StringVarP(&clientDo.PubkeyFlag, "pubkey", "", defaultPublicKey(), "specify the public key to sign with (defaults to $ERIS_CLIENT_PUBLIC_KEY)") // TransactionCmd.PersistentFlags().StringVarP(&clientDo.AddrFlag, "addr", "", defaultAddress(), "specify the account address (for which the public key can be found at eris-keys) (default respects $ERIS_CLIENT_ADDRESS)") // TransactionCmd.PersistentFlags().StringVarP(&clientDo.ChainidFlag, "chain-id", "", defaultChainId(), "specify the chainID (default respects $CHAIN_ID)") @@ -46,4 +43,6 @@ func addStatusPersistentFlags() { // // TransactionCmd.PersistentFlags().BoolVarP(&clientDo.SignFlag, "sign", "s", false, "sign the transaction using the eris-keys daemon") // TransactionCmd.PersistentFlags().BoolVarP(&clientDo.BroadcastFlag, "broadcast", "b", true, "broadcast the transaction to the blockchain") // TransactionCmd.PersistentFlags().BoolVarP(&clientDo.WaitFlag, "wait", "w", false, "wait for the transaction to be committed in a block") + + return statusCmd } diff --git a/client/cmd/transaction.go b/client/cmd/transaction.go index 87a3e10c5..28af969f9 100644 --- a/client/cmd/transaction.go +++ b/client/cmd/transaction.go @@ -1,53 +1,50 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 commands import ( - "os" "strings" "github.com/spf13/cobra" - log "github.com/eris-ltd/eris-logger" - - "github.com/eris-ltd/eris-db/client/transaction" + "github.com/eris-ltd/eris-db/client/methods" + "github.com/eris-ltd/eris-db/util" ) -var TransactionCmd = &cobra.Command{ - Use: "tx", - Short: "eris-client tx formulates and signs a transaction to a chain", - Long: `eris-client tx formulates and signs a transaction to a chain. -`, - Run: func(cmd *cobra.Command, args []string) { cmd.Help() }, -} - -func buildTransactionCommand() { +func buildTransactionCommand() *cobra.Command { // Transaction command has subcommands send, name, call, bond, // unbond, rebond, permissions. Dupeout transaction is not accessible through the command line. + transactionCmd := &cobra.Command{ + Use: "tx", + Short: "eris-client tx formulates and signs a transaction to a chain", + Long: "eris-client tx formulates and signs a transaction to a chain.", + Run: func(cmd *cobra.Command, args []string) { cmd.Help() }, + } - addTransactionPersistentFlags() + addTransactionPersistentFlags(transactionCmd) // SendTx - var sendCmd = &cobra.Command{ + sendCmd := &cobra.Command{ Use: "send", Short: "eris-client tx send --amt --to ", Long: "eris-client tx send --amt --to ", Run: func(cmd *cobra.Command, args []string) { - transaction.Send(clientDo) + err := methods.Send(clientDo) + if err != nil { + util.Fatalf("Could not complete send: %s", err) + } }, PreRun: assertParameters, } @@ -55,7 +52,7 @@ func buildTransactionCommand() { sendCmd.Flags().StringVarP(&clientDo.ToFlag, "to", "t", "", "specify an address to send to") // NameTx - var nameCmd = &cobra.Command{ + nameCmd := &cobra.Command{ Use: "name", Short: "eris-client tx name --amt --name --data ", Long: "eris-client tx name --amt --name --data ", @@ -71,12 +68,15 @@ func buildTransactionCommand() { nameCmd.Flags().StringVarP(&clientDo.FeeFlag, "fee", "f", "", "specify the fee to send") // CallTx - var callCmd = &cobra.Command{ + callCmd := &cobra.Command{ Use: "call", Short: "eris-client tx call --amt --fee --gas --to --data ", Long: "eris-client tx call --amt --fee --gas --to --data ", Run: func(cmd *cobra.Command, args []string) { - transaction.Call(clientDo) + err := methods.Call(clientDo) + if err != nil { + util.Fatalf("Could not complete call: %s", err) + } }, PreRun: assertParameters, } @@ -87,7 +87,7 @@ func buildTransactionCommand() { callCmd.Flags().StringVarP(&clientDo.GasFlag, "gas", "g", "", "specify the gas limit for a CallTx") // BondTx - var bondCmd = &cobra.Command{ + bondCmd := &cobra.Command{ Use: "bond", Short: "eris-client tx bond --pubkey --amt --unbond-to
", Long: "eris-client tx bond --pubkey --amt --unbond-to
", @@ -100,7 +100,7 @@ func buildTransactionCommand() { bondCmd.Flags().StringVarP(&clientDo.UnbondtoFlag, "to", "t", "", "specify an address to unbond to") // UnbondTx - var unbondCmd = &cobra.Command{ + unbondCmd := &cobra.Command{ Use: "unbond", Short: "eris-client tx unbond --addr
--height ", Long: "eris-client tx unbond --addr
--height ", @@ -126,7 +126,7 @@ func buildTransactionCommand() { rebondCmd.Flags().StringVarP(&clientDo.HeightFlag, "height", "n", "", "specify a height to unbond at") // PermissionsTx - var permissionsCmd = &cobra.Command{ + permissionsCmd := &cobra.Command{ Use: "permission", Short: "eris-client tx perm ", Long: "eris-client tx perm ", @@ -136,20 +136,21 @@ func buildTransactionCommand() { PreRun: assertParameters, } - TransactionCmd.AddCommand(sendCmd, nameCmd, callCmd, bondCmd, unbondCmd, rebondCmd, permissionsCmd) + transactionCmd.AddCommand(sendCmd, nameCmd, callCmd, bondCmd, unbondCmd, rebondCmd, permissionsCmd) + return transactionCmd } -func addTransactionPersistentFlags() { - TransactionCmd.PersistentFlags().StringVarP(&clientDo.SignAddrFlag, "sign-addr", "", defaultKeyDaemonAddress(), "set eris-keys daemon address (default respects $ERIS_CLIENT_SIGN_ADDRESS)") - TransactionCmd.PersistentFlags().StringVarP(&clientDo.NodeAddrFlag, "node-addr", "", defaultNodeRpcAddress(), "set the eris-db node rpc server address (default respects $ERIS_CLIENT_NODE_ADDRESS)") - TransactionCmd.PersistentFlags().StringVarP(&clientDo.PubkeyFlag, "pubkey", "", defaultPublicKey(), "specify the public key to sign with (defaults to $ERIS_CLIENT_PUBLIC_KEY)") - TransactionCmd.PersistentFlags().StringVarP(&clientDo.AddrFlag, "addr", "", defaultAddress(), "specify the account address (for which the public key can be found at eris-keys) (default respects $ERIS_CLIENT_ADDRESS)") - TransactionCmd.PersistentFlags().StringVarP(&clientDo.ChainidFlag, "chain-id", "", defaultChainId(), "specify the chainID (default respects $CHAIN_ID)") - TransactionCmd.PersistentFlags().StringVarP(&clientDo.NonceFlag, "nonce", "", "", "specify the nonce to use for the transaction (should equal the sender account's nonce + 1)") - - // TransactionCmd.PersistentFlags().BoolVarP(&clientDo.SignFlag, "sign", "s", false, "sign the transaction using the eris-keys daemon") - TransactionCmd.PersistentFlags().BoolVarP(&clientDo.BroadcastFlag, "broadcast", "b", true, "broadcast the transaction to the blockchain") - TransactionCmd.PersistentFlags().BoolVarP(&clientDo.WaitFlag, "wait", "w", true, "wait for the transaction to be committed in a block") +func addTransactionPersistentFlags(transactionCmd *cobra.Command) { + transactionCmd.PersistentFlags().StringVarP(&clientDo.SignAddrFlag, "sign-addr", "", defaultKeyDaemonAddress(), "set eris-keys daemon address (default respects $ERIS_CLIENT_SIGN_ADDRESS)") + transactionCmd.PersistentFlags().StringVarP(&clientDo.NodeAddrFlag, "node-addr", "", defaultNodeRpcAddress(), "set the eris-db node rpc server address (default respects $ERIS_CLIENT_NODE_ADDRESS)") + transactionCmd.PersistentFlags().StringVarP(&clientDo.PubkeyFlag, "pubkey", "", defaultPublicKey(), "specify the public key to sign with (defaults to $ERIS_CLIENT_PUBLIC_KEY)") + transactionCmd.PersistentFlags().StringVarP(&clientDo.AddrFlag, "addr", "", defaultAddress(), "specify the account address (for which the public key can be found at eris-keys) (default respects $ERIS_CLIENT_ADDRESS)") + transactionCmd.PersistentFlags().StringVarP(&clientDo.ChainidFlag, "chain-id", "", defaultChainId(), "specify the chainID (default respects $CHAIN_ID)") + transactionCmd.PersistentFlags().StringVarP(&clientDo.NonceFlag, "nonce", "", "", "specify the nonce to use for the transaction (should equal the sender account's nonce + 1)") + + // transactionCmd.PersistentFlags().BoolVarP(&clientDo.SignFlag, "sign", "s", false, "sign the transaction using the eris-keys daemon") + transactionCmd.PersistentFlags().BoolVarP(&clientDo.BroadcastFlag, "broadcast", "b", true, "broadcast the transaction to the blockchain") + transactionCmd.PersistentFlags().BoolVarP(&clientDo.WaitFlag, "wait", "w", true, "wait for the transaction to be committed in a block") } //------------------------------------------------------------------------------ @@ -180,26 +181,21 @@ func defaultAddress() string { func assertParameters(cmd *cobra.Command, args []string) { if clientDo.ChainidFlag == "" { - log.Fatal(`Please provide a chain id either through the flag --chain-id or environment variable $CHAIN_ID.`) - os.Exit(1) + util.Fatalf(`Please provide a chain id either through the flag --chain-id or environment variable $CHAIN_ID.`) } if !strings.HasPrefix(clientDo.NodeAddrFlag, "tcp://") && !strings.HasPrefix(clientDo.NodeAddrFlag, "unix://") { // TODO: [ben] go-rpc will deprecate reformatting; also it is bad practice to auto-correct for this; - log.Warn(`Please use fully formed listening address for the node, including the tcp:// or unix:// prefix.`) + // TODO: [Silas] I've made this fatal, but I'm inclined to define the default as tcp:// and normalise as with http + // below + util.Fatalf(`Please use fully formed listening address for the node, including the tcp:// or unix:// prefix.`) } if !strings.HasPrefix(clientDo.SignAddrFlag, "http://") { // NOTE: [ben] we preserve the auto-correction here as it is a simple http request-response to the key server. + // TODO: [Silas] we don't have logging here to log that we've done this. I'm inclined to either urls without a scheme + // and be quiet about it, or to make non-compliance fatal clientDo.SignAddrFlag = "http://" + clientDo.SignAddrFlag - log.WithFields(log.Fields{ - "signing address": clientDo.SignAddrFlag, - }).Warn(`Please use fully formed listening address for the key server; adding http:// prefix`) } - log.WithFields(log.Fields{ - "signing address": clientDo.SignAddrFlag, - "node address": clientDo.NodeAddrFlag, - "chain id": clientDo.ChainidFlag, - }).Debug("Asserted parameters") } diff --git a/client/methods/call.go b/client/methods/call.go new file mode 100644 index 000000000..86c793983 --- /dev/null +++ b/client/methods/call.go @@ -0,0 +1,53 @@ +// 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 methods + +import ( + "fmt" + + "github.com/eris-ltd/eris-db/client" + "github.com/eris-ltd/eris-db/client/rpc" + "github.com/eris-ltd/eris-db/definitions" + "github.com/eris-ltd/eris-db/keys" +) + +func Call(do *definitions.ClientDo) error { + // construct two clients to call out to keys server and + // blockchain node. + logger, err := loggerFromClientDo(do, "Call") + if err != nil { + return fmt.Errorf("Could not generate logging config from ClientDo: %s", err) + } + erisKeyClient := keys.NewErisKeyClient(do.SignAddrFlag, logger) + erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag, logger) + // form the call transaction + callTransaction, err := rpc.Call(erisNodeClient, erisKeyClient, + do.PubkeyFlag, do.AddrFlag, do.ToFlag, do.AmtFlag, do.NonceFlag, + do.GasFlag, do.FeeFlag, do.DataFlag) + if err != nil { + return fmt.Errorf("Failed on forming Call Transaction: %s", err) + } + // TODO: [ben] we carry over the sign bool, but always set it to true, + // as we move away from and deprecate the api that allows sending unsigned + // transactions and relying on (our) receiving node to sign it. + txResult, err := rpc.SignAndBroadcast(do.ChainidFlag, erisNodeClient, erisKeyClient, + callTransaction, true, do.BroadcastFlag, do.WaitFlag) + + if err != nil { + return fmt.Errorf("Failed on signing (and broadcasting) transaction: %s", err) + } + unpackSignAndBroadcast(txResult, logger) + return nil +} diff --git a/client/methods/helpers.go b/client/methods/helpers.go new file mode 100644 index 000000000..a97c92c10 --- /dev/null +++ b/client/methods/helpers.go @@ -0,0 +1,54 @@ +// 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 methods + +import ( + "github.com/eris-ltd/eris-db/client/rpc" + "github.com/eris-ltd/eris-db/core" + "github.com/eris-ltd/eris-db/definitions" + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/lifecycle" + "github.com/eris-ltd/eris-db/logging/loggers" +) + +func unpackSignAndBroadcast(result *rpc.TxResult, logger loggers.InfoTraceLogger) { + if result == nil { + // if we don't provide --sign or --broadcast + return + } + + logger = logger.With("transaction hash", result.Hash) + + if result.Address != nil { + logger = logger.With("Contract Address", result.Address) + } + + if result.Return != nil { + logger = logger.With("Block Hash", result.BlockHash, + "Return Value", result.Return, + "Exception", result.Exception, + ) + } + + logging.InfoMsg(logger, "SignAndBroadcast result") +} + +func loggerFromClientDo(do *definitions.ClientDo, scope string) (loggers.InfoTraceLogger, error) { + lc, err := core.LoadLoggingConfigFromClientDo(do) + if err != nil { + return nil, err + } + return logging.WithScope(lifecycle.NewLoggerFromLoggingConfig(lc), scope), nil +} diff --git a/client/methods/send.go b/client/methods/send.go new file mode 100644 index 000000000..8fa102302 --- /dev/null +++ b/client/methods/send.go @@ -0,0 +1,51 @@ +// 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 methods + +import ( + "fmt" + + "github.com/eris-ltd/eris-db/client" + "github.com/eris-ltd/eris-db/client/rpc" + "github.com/eris-ltd/eris-db/definitions" + "github.com/eris-ltd/eris-db/keys" +) + +func Send(do *definitions.ClientDo) error { + // construct two clients to call out to keys server and + // blockchain node. + logger, err := loggerFromClientDo(do, "Send") + if err != nil { + return fmt.Errorf("Could not generate logging config from ClientDo: %s", err) + } + erisKeyClient := keys.NewErisKeyClient(do.SignAddrFlag, logger) + erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag, logger) + // form the send transaction + sendTransaction, err := rpc.Send(erisNodeClient, erisKeyClient, + do.PubkeyFlag, do.AddrFlag, do.ToFlag, do.AmtFlag, do.NonceFlag) + if err != nil { + fmt.Errorf("Failed on forming Send Transaction: %s", err) + } + // TODO: [ben] we carry over the sign bool, but always set it to true, + // as we move away from and deprecate the api that allows sending unsigned + // transactions and relying on (our) receiving node to sign it. + txResult, err := rpc.SignAndBroadcast(do.ChainidFlag, erisNodeClient, erisKeyClient, + sendTransaction, true, do.BroadcastFlag, do.WaitFlag) + if err != nil { + return fmt.Errorf("Failed on signing (and broadcasting) transaction: %s", err) + } + unpackSignAndBroadcast(txResult, logger) + return nil +} diff --git a/client/methods/status.go b/client/methods/status.go index ba2470e76..f1fb63484 100644 --- a/client/methods/status.go +++ b/client/methods/status.go @@ -1,53 +1,51 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 methods import ( "fmt" - log "github.com/eris-ltd/eris-logger" - "github.com/eris-ltd/eris-db/client" "github.com/eris-ltd/eris-db/definitions" ) -func Status(do *definitions.ClientDo) { - erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag) +func Status(do *definitions.ClientDo) error { + logger, err := loggerFromClientDo(do, "Status") + if err != nil { + return fmt.Errorf("Could not generate logging config from ClientDo: %s", err) + } + erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag, logger) genesisHash, validatorPublicKey, latestBlockHash, latestBlockHeight, latestBlockTime, err := erisNodeClient.Status() if err != nil { - log.Errorf("Error requesting status from chain at (%s): %s", do.NodeAddrFlag, err) - return + return fmt.Errorf("Error requesting status from chain at (%s): %s", do.NodeAddrFlag, err) } chainName, chainId, genesisHashfromChainId, err := erisNodeClient.ChainId() if err != nil { - log.Errorf("Error requesting chainId from chain at (%s): %s", do.NodeAddrFlag, err) - return + return fmt.Errorf("Error requesting chainId from chain at (%s): %s", do.NodeAddrFlag, err) } - log.WithFields(log.Fields{ - "chain": do.NodeAddrFlag, - "genesisHash": fmt.Sprintf("%X", genesisHash), - "chainName": chainName, - "chainId": chainId, - "genesisHash from chainId": fmt.Sprintf("%X", genesisHashfromChainId), - "validator public key": fmt.Sprintf("%X", validatorPublicKey), - "latest block hash": fmt.Sprintf("%X", latestBlockHash), - "latest block height": latestBlockHeight, - "latest block time": latestBlockTime, - }).Info("status") + logger.Info("chain", do.NodeAddrFlag, + "genesisHash", fmt.Sprintf("%X", genesisHash), + "chainName", chainName, + "chainId", chainId, + "genesisHash from chainId", fmt.Sprintf("%X", genesisHashfromChainId), + "validator public key", fmt.Sprintf("%X", validatorPublicKey), + "latest block hash", fmt.Sprintf("%X", latestBlockHash), + "latest block height", latestBlockHeight, + "latest block time", latestBlockTime, + ) + return nil } diff --git a/client/mock/client_mock.go b/client/mock/client_mock.go index 12cdd76cf..f1e80895f 100644 --- a/client/mock/client_mock.go +++ b/client/mock/client_mock.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 mock @@ -23,6 +21,7 @@ import ( . "github.com/eris-ltd/eris-db/client" consensus_types "github.com/eris-ltd/eris-db/consensus/types" core_types "github.com/eris-ltd/eris-db/core/types" + "github.com/eris-ltd/eris-db/logging/loggers" "github.com/eris-ltd/eris-db/txs" ) @@ -117,3 +116,7 @@ func (mock *MockNodeClient) GetName(name string) (owner []byte, data string, exp func (mock *MockNodeClient) ListValidators() (blockHeight int, bondedValidators, unbondingValidators []consensus_types.Validator, err error) { return 0, nil, nil, nil } + +func (mock *MockNodeClient) Logger() loggers.InfoTraceLogger { + return loggers.NewNoopInfoTraceLogger() +} diff --git a/client/client.go b/client/node_client.go similarity index 79% rename from client/client.go rename to client/node_client.go index 24eeb0338..56dd425e2 100644 --- a/client/client.go +++ b/client/node_client.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 client @@ -21,18 +19,16 @@ import ( // "strings" "github.com/tendermint/go-rpc/client" - // Note [ben]: this is included to silence the logger from tendermint/go-rpc/client - // see func init() - tendermint_log "github.com/tendermint/log15" - - log "github.com/eris-ltd/eris-logger" acc "github.com/eris-ltd/eris-db/account" consensus_types "github.com/eris-ltd/eris-db/consensus/types" core_types "github.com/eris-ltd/eris-db/core/types" + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/loggers" tendermint_client "github.com/eris-ltd/eris-db/rpc/tendermint/client" tendermint_types "github.com/eris-ltd/eris-db/rpc/tendermint/core/types" "github.com/eris-ltd/eris-db/txs" + tmLog15 "github.com/tendermint/log15" ) type NodeClient interface { @@ -48,6 +44,9 @@ type NodeClient interface { DumpStorage(address []byte) (storage *core_types.Storage, err error) GetName(name string) (owner []byte, data string, expirationBlock int, err error) ListValidators() (blockHeight int, bondedValidators, unbondingValidators []consensus_types.Validator, err error) + + // Logging context for this NodeClient + Logger() loggers.InfoTraceLogger } type NodeWebsocketClient interface { @@ -58,29 +57,30 @@ type NodeWebsocketClient interface { Close() } -// NOTE [ben] Compiler check to ensure ErisNodeClient successfully implements +// NOTE [ben] Compiler check to ensure erisNodeClient successfully implements // eris-db/client.NodeClient -var _ NodeClient = (*ErisNodeClient)(nil) +var _ NodeClient = (*erisNodeClient)(nil) // Eris-Client is a simple struct exposing the client rpc methods - -type ErisNodeClient struct { +type erisNodeClient struct { broadcastRPC string + logger loggers.InfoTraceLogger } // ErisKeyClient.New returns a new eris-keys client for provided rpc location // Eris-keys connects over http request-responses -func NewErisNodeClient(rpcString string) *ErisNodeClient { - return &ErisNodeClient{ +func NewErisNodeClient(rpcString string, logger loggers.InfoTraceLogger) *erisNodeClient { + return &erisNodeClient{ broadcastRPC: rpcString, + logger: logging.WithScope(logger, "ErisNodeClient"), } } // Note [Ben]: This is a hack to silence Tendermint logger from tendermint/go-rpc // it needs to be initialised before go-rpc, hence it's placement here. func init() { - h := tendermint_log.LvlFilterHandler(tendermint_log.LvlWarn, tendermint_log.StdoutHandler) - tendermint_log.Root().SetHandler(h) + h := tmLog15.LvlFilterHandler(tmLog15.LvlWarn, tmLog15.StdoutHandler) + tmLog15.Root().SetHandler(h) } //------------------------------------------------------------------------------------ @@ -88,7 +88,7 @@ func init() { // NOTE: [ben] Eris Client first continues from tendermint rpc, but will have handshake to negotiate // protocol version for moving towards rpc/v1 -func (erisNodeClient *ErisNodeClient) Broadcast(tx txs.Tx) (*txs.Receipt, error) { +func (erisNodeClient *erisNodeClient) Broadcast(tx txs.Tx) (*txs.Receipt, error) { client := rpcclient.NewClientURI(erisNodeClient.broadcastRPC) receipt, err := tendermint_client.BroadcastTx(client, tx) if err != nil { @@ -97,7 +97,7 @@ func (erisNodeClient *ErisNodeClient) Broadcast(tx txs.Tx) (*txs.Receipt, error) return &receipt, nil } -func (erisNodeClient *ErisNodeClient) DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error) { +func (erisNodeClient *erisNodeClient) DeriveWebsocketClient() (nodeWsClient NodeWebsocketClient, err error) { var wsAddr string // TODO: clean up this inherited mess on dealing with the address prefixes. nodeAddr := erisNodeClient.broadcastRPC @@ -115,16 +115,17 @@ func (erisNodeClient *ErisNodeClient) DeriveWebsocketClient() (nodeWsClient Node // } // wsAddr = "ws://" + wsAddr wsAddr = nodeAddr - log.WithFields(log.Fields{ - "websocket address": wsAddr, - "endpoint": "/websocket", - }).Debug("Subscribing to websocket address") + logging.TraceMsg(erisNodeClient.logger, "Subscribing to websocket address", + "websocket address", wsAddr, + "endpoint", "/websocket", + ) wsClient := rpcclient.NewWSClient(wsAddr, "/websocket") if _, err = wsClient.Start(); err != nil { return nil, err } - derivedErisNodeWebsocketClient := &ErisNodeWebsocketClient{ + derivedErisNodeWebsocketClient := &erisNodeWebsocketClient{ tendermintWebsocket: wsClient, + logger: logging.WithScope(erisNodeClient.logger, "ErisNodeWebsocketClient"), } return derivedErisNodeWebsocketClient, nil } @@ -134,7 +135,7 @@ func (erisNodeClient *ErisNodeClient) DeriveWebsocketClient() (nodeWsClient Node // Status returns the ChainId (GenesisHash), validator's PublicKey, latest block hash // the block height and the latest block time. -func (erisNodeClient *ErisNodeClient) Status() (GenesisHash []byte, ValidatorPublicKey []byte, LatestBlockHash []byte, LatestBlockHeight int, LatestBlockTime int64, err error) { +func (erisNodeClient *erisNodeClient) Status() (GenesisHash []byte, ValidatorPublicKey []byte, LatestBlockHash []byte, LatestBlockHeight int, LatestBlockTime int64, err error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) res, err := tendermint_client.Status(client) if err != nil { @@ -152,7 +153,7 @@ func (erisNodeClient *ErisNodeClient) Status() (GenesisHash []byte, ValidatorPub return } -func (erisNodeClient *ErisNodeClient) ChainId() (ChainName, ChainId string, GenesisHash []byte, err error) { +func (erisNodeClient *erisNodeClient) ChainId() (ChainName, ChainId string, GenesisHash []byte, err error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) chainIdResult, err := tendermint_client.ChainId(client) if err != nil { @@ -170,7 +171,7 @@ func (erisNodeClient *ErisNodeClient) ChainId() (ChainName, ChainId string, Gene // QueryContract executes the contract code at address with the given data // NOTE: there is no check on the caller; -func (erisNodeClient *ErisNodeClient) QueryContract(callerAddress, calleeAddress, data []byte) (ret []byte, gasUsed int64, err error) { +func (erisNodeClient *erisNodeClient) QueryContract(callerAddress, calleeAddress, data []byte) (ret []byte, gasUsed int64, err error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) callResult, err := tendermint_client.Call(client, callerAddress, calleeAddress, data) if err != nil { @@ -182,7 +183,7 @@ func (erisNodeClient *ErisNodeClient) QueryContract(callerAddress, calleeAddress } // QueryContractCode executes the contract code at address with the given data but with provided code -func (erisNodeClient *ErisNodeClient) QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error) { +func (erisNodeClient *erisNodeClient) QueryContractCode(address, code, data []byte) (ret []byte, gasUsed int64, err error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) // TODO: [ben] Call and CallCode have an inconsistent signature; it makes sense for both to only // have a single address that is the contract to query. @@ -196,7 +197,7 @@ func (erisNodeClient *ErisNodeClient) QueryContractCode(address, code, data []by } // GetAccount returns a copy of the account -func (erisNodeClient *ErisNodeClient) GetAccount(address []byte) (*acc.Account, error) { +func (erisNodeClient *erisNodeClient) GetAccount(address []byte) (*acc.Account, error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) account, err := tendermint_client.GetAccount(client, address) if err != nil { @@ -213,7 +214,7 @@ func (erisNodeClient *ErisNodeClient) GetAccount(address []byte) (*acc.Account, } // DumpStorage returns the full storage for an account. -func (erisNodeClient *ErisNodeClient) DumpStorage(address []byte) (storage *core_types.Storage, err error) { +func (erisNodeClient *erisNodeClient) DumpStorage(address []byte) (storage *core_types.Storage, err error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) resultStorage, err := tendermint_client.DumpStorage(client, address) if err != nil { @@ -231,7 +232,7 @@ func (erisNodeClient *ErisNodeClient) DumpStorage(address []byte) (storage *core //-------------------------------------------------------------------------------------------- // Name registry -func (erisNodeClient *ErisNodeClient) GetName(name string) (owner []byte, data string, expirationBlock int, err error) { +func (erisNodeClient *erisNodeClient) GetName(name string) (owner []byte, data string, expirationBlock int, err error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) entryResult, err := tendermint_client.GetName(client, name) if err != nil { @@ -248,7 +249,7 @@ func (erisNodeClient *ErisNodeClient) GetName(name string) (owner []byte, data s //-------------------------------------------------------------------------------------------- -func (erisNodeClient *ErisNodeClient) ListValidators() (blockHeight int, +func (erisNodeClient *erisNodeClient) ListValidators() (blockHeight int, bondedValidators []consensus_types.Validator, unbondingValidators []consensus_types.Validator, err error) { client := rpcclient.NewClientJSONRPC(erisNodeClient.broadcastRPC) validatorsResult, err := tendermint_client.ListValidators(client) @@ -263,3 +264,7 @@ func (erisNodeClient *ErisNodeClient) ListValidators() (blockHeight int, unbondingValidators = validatorsResult.UnbondingValidators return } + +func (erisNodeClient *erisNodeClient) Logger() loggers.InfoTraceLogger { + return erisNodeClient.logger +} diff --git a/client/core/transaction_factory.go b/client/rpc/client.go similarity index 81% rename from client/core/transaction_factory.go rename to client/rpc/client.go index 569e1d8a2..d82a5f944 100644 --- a/client/core/transaction_factory.go +++ b/client/rpc/client.go @@ -1,40 +1,31 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -package core +// 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 rpc import ( "encoding/hex" "fmt" "strconv" - log "github.com/eris-ltd/eris-logger" - ptypes "github.com/eris-ltd/eris-db/permission/types" - "github.com/eris-ltd/eris-db/account" "github.com/eris-ltd/eris-db/client" "github.com/eris-ltd/eris-db/keys" "github.com/eris-ltd/eris-db/txs" ) -var ( - MaxCommitWaitTimeSeconds = 20 -) - //------------------------------------------------------------------------------------ // core functions with string args. // validates strings and forms transaction @@ -106,19 +97,6 @@ func Name(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addr, return tx, nil } -type PermFunc struct { - Name string - Args string -} - -var PermsFuncs = []PermFunc{ - {"set_base", "address, permission flag, value"}, - {"unset_base", "address, permission flag"}, - {"set_global", "permission flag, value"}, - {"add_role", "address, role"}, - {"rm_role", "address, role"}, -} - func Permissions(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, addrS, nonceS, permFunc string, argsS []string) (*txs.PermissionsTx, error) { pub, _, nonce, err := checkCommon(nodeClient, keyClient, pubkey, addrS, "0", nonceS) if err != nil { @@ -126,13 +104,13 @@ func Permissions(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, } var args ptypes.PermArgs switch permFunc { - case "set_base": + case "setBase": addr, pF, err := decodeAddressPermFlag(argsS[0], argsS[1]) if err != nil { return nil, err } if len(argsS) != 3 { - return nil, fmt.Errorf("set_base also takes a value (true or false)") + return nil, fmt.Errorf("setBase also takes a value (true or false)") } var value bool if argsS[2] == "true" { @@ -143,13 +121,13 @@ func Permissions(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, return nil, fmt.Errorf("Unknown value %s", argsS[2]) } args = &ptypes.SetBaseArgs{addr, pF, value} - case "unset_base": + case "unsetBase": addr, pF, err := decodeAddressPermFlag(argsS[0], argsS[1]) if err != nil { return nil, err } args = &ptypes.UnsetBaseArgs{addr, pF} - case "set_global": + case "setGlobal": pF, err := ptypes.PermStringToFlag(argsS[0]) if err != nil { return nil, err @@ -163,13 +141,13 @@ func Permissions(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, return nil, fmt.Errorf("Unknown value %s", argsS[1]) } args = &ptypes.SetGlobalArgs{pF, value} - case "add_role": + case "addRole": addr, err := hex.DecodeString(argsS[0]) if err != nil { return nil, err } args = &ptypes.AddRoleArgs{addr, argsS[1]} - case "rm_role": + case "removeRole": addr, err := hex.DecodeString(argsS[0]) if err != nil { return nil, err @@ -272,16 +250,14 @@ type TxResult struct { } // Preserve -func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient keys.KeyClient, tx txs.Tx, sign, broadcast, wait bool) (txResult *TxResult, err error) { +func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient keys.KeyClient, tx txs.Tx, sign, + broadcast, wait bool) (txResult *TxResult, err error) { var inputAddr []byte if sign { inputAddr, tx, err = signTx(keyClient, chainID, tx) if err != nil { return nil, err } - log.WithFields(log.Fields{ - "transaction": string(account.SignBytes(chainID, tx)), - }).Debug("Signed transaction") } if broadcast { @@ -300,23 +276,19 @@ func SignAndBroadcast(chainID string, nodeClient client.NodeClient, keyClient ke // if broadcast threw an error, just return return } - log.Debug("Waiting for transaction to be confirmed.") confirmation := <-confirmationChannel if confirmation.Error != nil { - log.Errorf("Encountered error waiting for event: %s\n", confirmation.Error) - err = confirmation.Error + err = fmt.Errorf("Encountered error waiting for event: %s", confirmation.Error) return } if confirmation.Exception != nil { - log.Errorf("Encountered Exception from chain: %s\n", confirmation.Exception) - err = confirmation.Exception + err = fmt.Errorf("Encountered Exception from chain: %s", confirmation.Exception) return } txResult.BlockHash = confirmation.BlockHash txResult.Exception = "" eventDataTx, ok := confirmation.Event.(*txs.EventDataTx) if !ok { - log.Errorf("Received wrong event type.") err = fmt.Errorf("Received wrong event type.") return } diff --git a/client/core/transaction_factory_test.go b/client/rpc/client_test.go similarity index 77% rename from client/core/transaction_factory_test.go rename to client/rpc/client_test.go index 4544655e1..b0806910f 100644 --- a/client/core/transaction_factory_test.go +++ b/client/rpc/client_test.go @@ -1,20 +1,18 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -package core +// 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 rpc import ( "fmt" @@ -26,19 +24,19 @@ import ( mockkeys "github.com/eris-ltd/eris-db/keys/mock" ) -func TestTransactionFactory(t *testing.T) { +func Test(t *testing.T) { mockKeyClient := mockkeys.NewMockKeyClient() mockNodeClient := mockclient.NewMockNodeClient() - testTransactionFactorySend(t, mockNodeClient, mockKeyClient) - testTransactionFactoryCall(t, mockNodeClient, mockKeyClient) - testTransactionFactoryName(t, mockNodeClient, mockKeyClient) - testTransactionFactoryPermissions(t, mockNodeClient, mockKeyClient) + testSend(t, mockNodeClient, mockKeyClient) + testCall(t, mockNodeClient, mockKeyClient) + testName(t, mockNodeClient, mockKeyClient) + testPermissions(t, mockNodeClient, mockKeyClient) // t.Run("BondTransaction", ) // t.Run("UnbondTransaction", ) // t.Run("RebondTransaction", ) } -func testTransactionFactorySend(t *testing.T, +func testSend(t *testing.T, nodeClient *mockclient.MockNodeClient, keyClient *mockkeys.MockKeyClient) { // generate an ED25519 key and ripemd160 address @@ -66,7 +64,7 @@ func testTransactionFactorySend(t *testing.T, // TODO: test content of Transaction } -func testTransactionFactoryCall(t *testing.T, +func testCall(t *testing.T, nodeClient *mockclient.MockNodeClient, keyClient *mockkeys.MockKeyClient) { // generate an ED25519 key and ripemd160 address @@ -99,7 +97,7 @@ func testTransactionFactoryCall(t *testing.T, // TODO: test content of Transaction } -func testTransactionFactoryName(t *testing.T, +func testName(t *testing.T, nodeClient *mockclient.MockNodeClient, keyClient *mockkeys.MockKeyClient) { // generate an ED25519 key and ripemd160 address @@ -130,7 +128,7 @@ func testTransactionFactoryName(t *testing.T, // TODO: test content of Transaction } -func testTransactionFactoryPermissions(t *testing.T, +func testPermissions(t *testing.T, nodeClient *mockclient.MockNodeClient, keyClient *mockkeys.MockKeyClient) { // generate an ED25519 key and ripemd160 address @@ -147,7 +145,7 @@ func testTransactionFactoryPermissions(t *testing.T, nonceString := "" _, err := Permissions(nodeClient, keyClient, publicKeyString, addressString, - nonceString, "set_base", []string{permAddressString, "root", "true"}) + nonceString, "setBase", []string{permAddressString, "root", "true"}) if err != nil { t.Logf("Error in PermissionsTx: %s", err) t.Fail() diff --git a/client/core/transaction_factory_util.go b/client/rpc/client_util.go similarity index 79% rename from client/core/transaction_factory_util.go rename to client/rpc/client_util.go index 3ad64a6ba..f086c68b2 100644 --- a/client/core/transaction_factory_util.go +++ b/client/rpc/client_util.go @@ -1,33 +1,30 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -package core +// 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 rpc import ( "encoding/hex" "fmt" "strconv" - log "github.com/eris-ltd/eris-logger" - "github.com/tendermint/go-crypto" acc "github.com/eris-ltd/eris-db/account" "github.com/eris-ltd/eris-db/client" "github.com/eris-ltd/eris-db/keys" + "github.com/eris-ltd/eris-db/logging" ptypes "github.com/eris-ltd/eris-db/permission/types" "github.com/eris-ltd/eris-db/txs" ) @@ -101,10 +98,10 @@ func checkCommon(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, return } else if pubkey != "" { if addr != "" { - log.WithFields(log.Fields{ - "public key": pubkey, - "address": addr, - }).Info("you have specified both a pubkey and an address. the pubkey takes precedent") + logging.InfoMsg(nodeClient.Logger(), "Both a public key and an address have been specified. The public key takes precedent.", + "public_key", pubkey, + "address", addr, + ) } pubKeyBytes, err = hex.DecodeString(pubkey) if err != nil { @@ -151,10 +148,10 @@ func checkCommon(nodeClient client.NodeClient, keyClient keys.KeyClient, pubkey, return pub, amt, nonce, err2 } nonce = int64(account.Sequence) + 1 - log.WithFields(log.Fields{ - "nonce": nonce, - "account address": fmt.Sprintf("%X", addrBytes), - }).Debug("Fetch nonce from node") + logging.TraceMsg(nodeClient.Logger(), "Fetch nonce from node", + "nonce", nonce, + "account address", addrBytes, + ) } else { nonce, err = strconv.ParseInt(nonceS, 10, 64) if err != nil { diff --git a/client/transaction/transaction.go b/client/transaction/transaction.go deleted file mode 100644 index e9840d244..000000000 --- a/client/transaction/transaction.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -package transaction - -import ( - "fmt" - "os" - - log "github.com/eris-ltd/eris-logger" - - "github.com/eris-ltd/eris-db/client" - "github.com/eris-ltd/eris-db/client/core" - "github.com/eris-ltd/eris-db/definitions" - "github.com/eris-ltd/eris-db/keys" -) - -func Send(do *definitions.ClientDo) { - // construct two clients to call out to keys server and - // blockchain node. - erisKeyClient := keys.NewErisKeyClient(do.SignAddrFlag) - erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag) - // form the send transaction - sendTransaction, err := core.Send(erisNodeClient, erisKeyClient, - do.PubkeyFlag, do.AddrFlag, do.ToFlag, do.AmtFlag, do.NonceFlag) - if err != nil { - log.Fatalf("Failed on forming Send Transaction: %s", err) - return - } - // TODO: [ben] we carry over the sign bool, but always set it to true, - // as we move away from and deprecate the api that allows sending unsigned - // transactions and relying on (our) receiving node to sign it. - unpackSignAndBroadcast( - core.SignAndBroadcast(do.ChainidFlag, erisNodeClient, - erisKeyClient, sendTransaction, true, do.BroadcastFlag, do.WaitFlag)) -} - -func Call(do *definitions.ClientDo) { - // construct two clients to call out to keys server and - // blockchain node. - erisKeyClient := keys.NewErisKeyClient(do.SignAddrFlag) - erisNodeClient := client.NewErisNodeClient(do.NodeAddrFlag) - // form the call transaction - callTransaction, err := core.Call(erisNodeClient, erisKeyClient, - do.PubkeyFlag, do.AddrFlag, do.ToFlag, do.AmtFlag, do.NonceFlag, - do.GasFlag, do.FeeFlag, do.DataFlag) - if err != nil { - log.Fatalf("Failed on forming Call Transaction: %s", err) - return - } - // TODO: [ben] we carry over the sign bool, but always set it to true, - // as we move away from and deprecate the api that allows sending unsigned - // transactions and relying on (our) receiving node to sign it. - unpackSignAndBroadcast( - core.SignAndBroadcast(do.ChainidFlag, erisNodeClient, - erisKeyClient, callTransaction, true, do.BroadcastFlag, do.WaitFlag)) -} - -//---------------------------------------------------------------------- -// Helper functions - -func unpackSignAndBroadcast(result *core.TxResult, err error) { - if err != nil { - log.Fatalf("Failed on signing (and broadcasting) transaction: %s", err) - os.Exit(1) - } - if result == nil { - // if we don't provide --sign or --broadcast - return - } - printResult := log.Fields{ - "transaction hash": fmt.Sprintf("%X", result.Hash), - } - if result.Address != nil { - printResult["Contract Address"] = fmt.Sprintf("%X", result.Address) - } - if result.Return != nil { - printResult["Block Hash"] = fmt.Sprintf("%X", result.BlockHash) - printResult["Return Value"] = fmt.Sprintf("%X", result.Return) - printResult["Exception"] = fmt.Sprintf("%s", result.Exception) - } - log.WithFields(printResult).Warn("Result") -} diff --git a/client/websocket_client.go b/client/websocket_client.go index 42f1bf37d..d3949e652 100644 --- a/client/websocket_client.go +++ b/client/websocket_client.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 client @@ -24,8 +22,8 @@ import ( "github.com/tendermint/go-rpc/client" "github.com/tendermint/go-wire" - log "github.com/eris-ltd/eris-logger" - + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/loggers" ctypes "github.com/eris-ltd/eris-db/rpc/tendermint/core/types" "github.com/eris-ltd/eris-db/txs" ) @@ -41,29 +39,30 @@ type Confirmation struct { Error error } -// NOTE [ben] Compiler check to ensure ErisNodeClient successfully implements +// NOTE [ben] Compiler check to ensure erisNodeClient successfully implements // eris-db/client.NodeClient -var _ NodeWebsocketClient = (*ErisNodeWebsocketClient)(nil) +var _ NodeWebsocketClient = (*erisNodeWebsocketClient)(nil) -type ErisNodeWebsocketClient struct { +type erisNodeWebsocketClient struct { // TODO: assert no memory leak on closing with open websocket tendermintWebsocket *rpcclient.WSClient + logger loggers.InfoTraceLogger } // Subscribe to an eventid -func (erisNodeWebsocketClient *ErisNodeWebsocketClient) Subscribe(eventid string) error { +func (erisNodeWebsocketClient *erisNodeWebsocketClient) Subscribe(eventid string) error { // TODO we can in the background listen to the subscription id and remember it to ease unsubscribing later. return erisNodeWebsocketClient.tendermintWebsocket.Subscribe(eventid) } // Unsubscribe from an eventid -func (erisNodeWebsocketClient *ErisNodeWebsocketClient) Unsubscribe(subscriptionId string) error { +func (erisNodeWebsocketClient *erisNodeWebsocketClient) Unsubscribe(subscriptionId string) error { return erisNodeWebsocketClient.tendermintWebsocket.Unsubscribe(subscriptionId) } // Returns a channel that will receive a confirmation with a result or the exception that // has been confirmed; or an error is returned and the confirmation channel is nil. -func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx txs.Tx, chainId string, inputAddr []byte) (chan Confirmation, error) { +func (erisNodeWebsocketClient *erisNodeWebsocketClient) WaitForConfirmation(tx txs.Tx, chainId string, inputAddr []byte) (chan Confirmation, error) { // check no errors are reported on the websocket if err := erisNodeWebsocketClient.assertNoErrors(); err != nil { return nil, err @@ -88,7 +87,8 @@ func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx t result := new(ctypes.ErisDBResult) if wire.ReadJSONPtr(result, resultBytes, &err); err != nil { // keep calm and carry on - log.Errorf("[eris-client] Failed to unmarshal json bytes for websocket event: %s", err) + logging.InfoMsg(erisNodeWebsocketClient.logger, "Failed to unmarshal json bytes for websocket event", + "error", err) continue } @@ -97,36 +97,41 @@ func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx t // Received confirmation of subscription to event streams // TODO: collect subscription IDs, push into channel and on completion // unsubscribe - log.Infof("[eris-client] recceived confirmation for event (%s) with subscription id (%s).", - subscription.Event, subscription.SubscriptionId) + logging.InfoMsg(erisNodeWebsocketClient.logger, "Received confirmation for event", + "event", subscription.Event, + "subscription_id", subscription.SubscriptionId) continue } event, ok := (*result).(*ctypes.ResultEvent) if !ok { // keep calm and carry on - log.Warnf("[eris-client] Failed to cast to ResultEvent for websocket event: %s", *result) + logging.InfoMsg(erisNodeWebsocketClient.logger, "Failed to cast to ResultEvent for websocket event", + "event", event.Event) continue } blockData, ok := event.Data.(txs.EventDataNewBlock) if ok { latestBlockHash = blockData.Block.Hash() - log.WithFields(log.Fields{ - "new block": blockData.Block, - "latest hash": latestBlockHash, - }).Debug("Registered new block") + logging.TraceMsg(erisNodeWebsocketClient.logger, "Registered new block", + "block", blockData.Block, + "latest_block_hash", latestBlockHash, + ) continue } // we don't accept events unless they came after a new block (ie. in) if latestBlockHash == nil { - log.Infof("[eris-client] no first block has been registered, so ignoring event: %s", event.Event) + logging.InfoMsg(erisNodeWebsocketClient.logger, "First block has not been registered so ignoring event", + "event", event.Event) continue } if event.Event != eid { - log.Warnf("[eris-client] received unsolicited event! Got %s, expected %s\n", event.Event, eid) + logging.InfoMsg(erisNodeWebsocketClient.logger, "Received unsolicited event", + "event_received", event.Event, + "event_expected", eid) continue } @@ -143,10 +148,9 @@ func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx t } if !bytes.Equal(txs.TxHash(chainId, data.Tx), txs.TxHash(chainId, tx)) { - log.WithFields(log.Fields{ + logging.TraceMsg(erisNodeWebsocketClient.logger, "Received different event", // TODO: consider re-implementing TxID again, or other more clear debug - "received transaction event": txs.TxHash(chainId, data.Tx), - }).Debug("Received different event") + "received transaction event", txs.TxHash(chainId, data.Tx)) continue } @@ -188,13 +192,13 @@ func (erisNodeWebsocketClient *ErisNodeWebsocketClient) WaitForConfirmation(tx t return confirmationChannel, nil } -func (erisNodeWebsocketClient *ErisNodeWebsocketClient) Close() { +func (erisNodeWebsocketClient *erisNodeWebsocketClient) Close() { if erisNodeWebsocketClient.tendermintWebsocket != nil { erisNodeWebsocketClient.tendermintWebsocket.Stop() } } -func (erisNodeWebsocketClient *ErisNodeWebsocketClient) assertNoErrors() error { +func (erisNodeWebsocketClient *erisNodeWebsocketClient) assertNoErrors() error { if erisNodeWebsocketClient.tendermintWebsocket != nil { select { case err := <-erisNodeWebsocketClient.tendermintWebsocket.ErrorsCh: diff --git a/client/ws_client.go b/client/ws_client.go index d60986b6f..cdcb0b570 100644 --- a/client/ws_client.go +++ b/client/ws_client.go @@ -1,4 +1,17 @@ -// Websocket client implementation. This will be used in tests. +// 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 client // NOTE: this websocket client acts on rpc/v0, @@ -6,6 +19,7 @@ package client // and will be deprecated after 0.12 // It is recommended to use the interfaces NodeClient // and NodeWebsocketClient. +// Websocket client implementation. This will be used in tests. import ( "fmt" diff --git a/cmd/eris-db.go b/cmd/eris-db.go index 605b12060..c0f6e5824 100644 --- a/cmd/eris-db.go +++ b/cmd/eris-db.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 commands @@ -21,17 +19,12 @@ import ( "strconv" "strings" - cobra "github.com/spf13/cobra" + "github.com/spf13/cobra" - log "github.com/eris-ltd/eris-logger" - - definitions "github.com/eris-ltd/eris-db/definitions" - version "github.com/eris-ltd/eris-db/version" + "github.com/eris-ltd/eris-db/definitions" + "github.com/eris-ltd/eris-db/version" ) -// Global Do struct -var do *definitions.Do - var ErisDbCmd = &cobra.Command{ Use: "eris-db", Short: "Eris-DB is the server side of the eris chain.", @@ -39,42 +32,30 @@ var ErisDbCmd = &cobra.Command{ a modular consensus engine and application manager to run a chain to suit your needs. -Made with <3 by Eris Industries. +Made with <3 by Monax Industries. Complete documentation is available at https://monax.io/docs/documentation ` + "\nVERSION:\n " + version.VERSION, - PersistentPreRun: func(cmd *cobra.Command, args []string) { - - log.SetLevel(log.WarnLevel) - if do.Verbose { - log.SetLevel(log.InfoLevel) - } else if do.Debug { - log.SetLevel(log.DebugLevel) - } - }, Run: func(cmd *cobra.Command, args []string) { cmd.Help() }, } func Execute() { - InitErisDbCli() - AddGlobalFlags() - AddCommands() + do := definitions.NewDo() + AddGlobalFlags(do) + AddCommands(do) ErisDbCmd.Execute() } -func InitErisDbCli() { - // initialise an empty Do struct for command execution - do = definitions.NewDo() -} - -func AddGlobalFlags() { - ErisDbCmd.PersistentFlags().BoolVarP(&do.Verbose, "verbose", "v", defaultVerbose(), "verbose output; more output than no output flags; less output than debug level; default respects $ERIS_DB_VERBOSE") - ErisDbCmd.PersistentFlags().BoolVarP(&do.Debug, "debug", "d", defaultDebug(), "debug level output; the most output available for eris-db; if it is too chatty use verbose flag; default respects $ERIS_DB_DEBUG") +func AddGlobalFlags(do *definitions.Do) { + ErisDbCmd.PersistentFlags().BoolVarP(&do.Verbose, "verbose", "v", + defaultVerbose(), + "verbose output; more output than no output flags; less output than debug level; default respects $ERIS_DB_VERBOSE") + ErisDbCmd.PersistentFlags().BoolVarP(&do.Debug, "debug", "d", defaultDebug(), + "debug level output; the most output available for eris-db; if it is too chatty use verbose flag; default respects $ERIS_DB_DEBUG") } -func AddCommands() { - buildServeCommand() - ErisDbCmd.AddCommand(ServeCmd) +func AddCommands(do *definitions.Do) { + ErisDbCmd.AddCommand(buildServeCommand(do)) } //------------------------------------------------------------------------------ diff --git a/cmd/eris-db/main.go b/cmd/eris-db/main.go index 47870377f..d293b374f 100644 --- a/cmd/eris-db/main.go +++ b/cmd/eris-db/main.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 main diff --git a/cmd/serve.go b/cmd/serve.go index eccf3388a..4eceefc38 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -1,35 +1,33 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 commands import ( + "fmt" "os" "os/signal" "path" "syscall" - cobra "github.com/spf13/cobra" - - log "github.com/eris-ltd/eris-logger" - - "fmt" + "github.com/eris-ltd/eris-db/core" + "github.com/eris-ltd/eris-db/definitions" + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/lifecycle" + "github.com/eris-ltd/eris-db/util" - core "github.com/eris-ltd/eris-db/core" - util "github.com/eris-ltd/eris-db/util" + "github.com/spf13/cobra" ) const ( @@ -41,148 +39,157 @@ var DefaultConfigFilename = fmt.Sprintf("%s.%s", DefaultConfigBasename, DefaultConfigType) -var ServeCmd = &cobra.Command{ - Use: "serve", - Short: "Eris-DB serve starts an eris-db node with client API enabled by default.", - Long: `Eris-DB serve starts an eris-db node with client API enabled by default. +// build the serve subcommand +func buildServeCommand(do *definitions.Do) *cobra.Command { + cmd := &cobra.Command{ + Use: "serve", + Short: "Eris-DB serve starts an eris-db node with client API enabled by default.", + Long: `Eris-DB serve starts an eris-db node with client API enabled by default. The Eris-DB node is modularly configured for the consensus engine and application manager. The client API can be disabled.`, - Example: fmt.Sprintf(`$ eris-db serve -- will start the Eris-DB node based on the configuration file "%s" in the current working directory + Example: fmt.Sprintf(`$ eris-db serve -- will start the Eris-DB node based on the configuration file "%s" in the current working directory $ eris-db serve --work-dir -- will start the Eris-DB node based on the configuration file "%s" in the provided working directory $ eris-db serve --chain-id -- will overrule the configuration entry assert_chain_id`, - DefaultConfigFilename, DefaultConfigFilename), - PreRun: func(cmd *cobra.Command, args []string) { - // if WorkDir was not set by a flag or by $ERIS_DB_WORKDIR - // NOTE [ben]: we can consider an `Explicit` flag that eliminates - // the use of any assumptions while starting Eris-DB - if do.WorkDir == "" { - if currentDirectory, err := os.Getwd(); err != nil { - log.Fatalf("No directory provided and failed to get current working directory: %v", err) + DefaultConfigFilename, DefaultConfigFilename), + PreRun: func(cmd *cobra.Command, args []string) { + // if WorkDir was not set by a flag or by $ERIS_DB_WORKDIR + // NOTE [ben]: we can consider an `Explicit` flag that eliminates + // the use of any assumptions while starting Eris-DB + if do.WorkDir == "" { + if currentDirectory, err := os.Getwd(); err != nil { + panic(fmt.Sprintf("No directory provided and failed to get current "+ + "working directory: %v", err)) + os.Exit(1) + } else { + do.WorkDir = currentDirectory + } + } + if !util.IsDir(do.WorkDir) { + panic(fmt.Sprintf("Provided working directory %s is not a directory", + do.WorkDir)) os.Exit(1) - } else { - do.WorkDir = currentDirectory } - } - if !util.IsDir(do.WorkDir) { - log.Fatalf("Provided working directory %s is not a directory", do.WorkDir) - } - }, - Run: Serve, -} - -// build the serve subcommand -func buildServeCommand() { - addServeFlags() + }, + Run: ServeRunner(do), + } + addServeFlags(do, cmd) + return cmd } -func addServeFlags() { - ServeCmd.PersistentFlags().StringVarP(&do.ChainId, "chain-id", "c", +func addServeFlags(do *definitions.Do, serveCmd *cobra.Command) { + serveCmd.PersistentFlags().StringVarP(&do.ChainId, "chain-id", "c", defaultChainId(), "specify the chain id to use for assertion against the genesis file or the existing state. If omitted, and no id is set in $CHAIN_ID, then assert_chain_id is used from the configuration file.") - ServeCmd.PersistentFlags().StringVarP(&do.WorkDir, "work-dir", "w", + serveCmd.PersistentFlags().StringVarP(&do.WorkDir, "work-dir", "w", defaultWorkDir(), "specify the working directory for the chain to run. If omitted, and no path set in $ERIS_DB_WORKDIR, the current working directory is taken.") - ServeCmd.PersistentFlags().StringVarP(&do.DataDir, "data-dir", "", + serveCmd.PersistentFlags().StringVarP(&do.DataDir, "data-dir", "", defaultDataDir(), "specify the data directory. If omitted and not set in $ERIS_DB_DATADIR, /data is taken.") - ServeCmd.PersistentFlags().BoolVarP(&do.DisableRpc, "disable-rpc", "", + serveCmd.PersistentFlags().BoolVarP(&do.DisableRpc, "disable-rpc", "", defaultDisableRpc(), "indicate for the RPC to be disabled. If omitted the RPC is enabled by default, unless (deprecated) $ERISDB_API is set to false.") } //------------------------------------------------------------------------------ // functions - -// serve() prepares the environment and sets up the core for Eris_DB to run. -// After the setup succeeds, serve() starts the core and halts for core to -// terminate. -func Serve(cmd *cobra.Command, args []string) { - // load configuration from a single location to avoid a wrong configuration - // file is loaded. - err := do.ReadConfig(do.WorkDir, DefaultConfigBasename, DefaultConfigType) - if err != nil { - log.WithFields(log.Fields{ - "directory": do.WorkDir, - "file": DefaultConfigFilename, - }).Fatalf("Fatal error reading configuration") - os.Exit(1) - } - // if do.ChainId is not yet set, load chain_id for assertion from configuration file - if do.ChainId == "" { - if do.ChainId = do.Config.GetString("chain.assert_chain_id"); do.ChainId == "" { - log.Fatalf("Failed to read non-empty string for ChainId from config.") - os.Exit(1) - } - } +func NewCoreFromDo(do *definitions.Do) (*core.Core, error) { // load the genesis file path do.GenesisFile = path.Join(do.WorkDir, do.Config.GetString("chain.genesis_file")) + if do.Config.GetString("chain.genesis_file") == "" { - log.Fatalf("Failed to read non-empty string for genesis file from config.") - os.Exit(1) + return nil, fmt.Errorf("The config value chain.genesis_file is empty, " + + "but should be set to the location of the genesis.json file.") } // Ensure data directory is set and accessible if err := do.InitialiseDataDirectory(); err != nil { - log.Fatalf("Failed to initialise data directory (%s): %v", do.DataDir, err) - os.Exit(1) + return nil, fmt.Errorf("Failed to initialise data directory (%s): %v", do.DataDir, err) } - log.WithFields(log.Fields{ - "chainId": do.ChainId, - "workingDirectory": do.WorkDir, - "dataDirectory": do.DataDir, - "genesisFile": do.GenesisFile, - }).Info("Eris-DB serve configuring") - consensusConfig, err := core.LoadConsensusModuleConfig(do) + loggerConfig, err := core.LoadLoggingConfigFromDo(do) if err != nil { - log.Fatalf("Failed to load consensus module configuration: %s.", err) - os.Exit(1) + return nil, fmt.Errorf("Failed to load logging config: %s", err) } - managerConfig, err := core.LoadApplicationManagerModuleConfig(do) + // Create a root logger to pass through to dependencies + logger := logging.WithScope(lifecycle.NewLoggerFromLoggingConfig(loggerConfig), "Serve") + // Capture all logging from tendermint/tendermint and tendermint/go-* + // dependencies + lifecycle.CaptureTendermintLog15Output(logger) + // And from stdlib go log + lifecycle.CaptureStdlibLogOutput(logger) + + // if do.ChainId is not yet set, load chain_id for assertion from configuration file + + if do.ChainId == "" { + if do.ChainId = do.Config.GetString("chain.assert_chain_id"); do.ChainId == "" { + return nil, fmt.Errorf("The config chain.assert_chain_id is empty, " + + "but should be set to the chain_id of the chain we are trying to run.") + } + } + + logging.Msg(logger, "Loading configuration for serve command", + "chainId", do.ChainId, + "workingDirectory", do.WorkDir, + "dataDirectory", do.DataDir, + "genesisFile", do.GenesisFile) + + consensusConfig, err := core.LoadConsensusModuleConfig(do) if err != nil { - log.Fatalf("Failed to load application manager module configuration: %s.", err) - os.Exit(1) + return nil, fmt.Errorf("Failed to load consensus module configuration: %s.", err) } - log.WithFields(log.Fields{ - "consensusModule": consensusConfig.Version, - "applicationManager": managerConfig.Version, - }).Debug("Modules configured") - newCore, err := core.NewCore(do.ChainId, consensusConfig, managerConfig) + managerConfig, err := core.LoadApplicationManagerModuleConfig(do) if err != nil { - log.Fatalf("Failed to load core: %s", err) + return nil, fmt.Errorf("Failed to load application manager module configuration: %s.", err) } - if !do.DisableRpc { - serverConfig, err := core.LoadServerConfig(do) - if err != nil { - log.Fatalf("Failed to load server configuration: %s.", err) - os.Exit(1) - } + logging.Msg(logger, "Modules configured", + "consensusModule", consensusConfig.Version, + "applicationManager", managerConfig.Version) + + return core.NewCore(do.ChainId, consensusConfig, managerConfig, logger) +} - serverProcess, err := newCore.NewGatewayV0(serverConfig) +// ServeRunner() returns a command runner that prepares the environment and sets +// up the core for Eris-DB to run. After the setup succeeds, it starts the core +// and waits for the core to terminate. +func ServeRunner(do *definitions.Do) func(*cobra.Command, []string) { + return func(cmd *cobra.Command, args []string) { + // load configuration from a single location to avoid a wrong configuration + // file is loaded. + err := do.ReadConfig(do.WorkDir, DefaultConfigBasename, DefaultConfigType) if err != nil { - log.Fatalf("Failed to load servers: %s.", err) - os.Exit(1) + util.Fatalf("Fatal error reading configuration from %s/%s", do.WorkDir, + DefaultConfigFilename) } - err = serverProcess.Start() + + newCore, err := NewCoreFromDo(do) + if err != nil { - log.Fatalf("Failed to start servers: %s.", err) - os.Exit(1) + util.Fatalf("Failed to load core: %s", err) } - _, err = newCore.NewGatewayTendermint(serverConfig) - if err != nil { - log.Fatalf("Failed to start Tendermint gateway") + + if !do.DisableRpc { + serverConfig, err := core.LoadServerConfig(do) + if err != nil { + util.Fatalf("Failed to load server configuration: %s.", err) + } + serverProcess, err := newCore.NewGatewayV0(serverConfig) + if err != nil { + util.Fatalf("Failed to load servers: %s.", err) + } + err = serverProcess.Start() + if err != nil { + util.Fatalf("Failed to start servers: %s.", err) + } + _, err = newCore.NewGatewayTendermint(serverConfig) + if err != nil { + util.Fatalf("Failed to start Tendermint gateway") + } + <-serverProcess.StopEventChannel() + } else { + signals := make(chan os.Signal, 1) + signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) + fmt.Fprintf(os.Stderr, "Received %s signal. Marmots out.", <-signals) } - <-serverProcess.StopEventChannel() - } else { - signals := make(chan os.Signal, 1) - done := make(chan bool, 1) - signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM) - go func() { - signal := <-signals - // TODO: [ben] clean up core; in a manner consistent with enabled rpc - log.Fatalf("Received %s signal. Marmots out.", signal) - done <- true - }() - <-done } } diff --git a/common/math/integral/integral_math.go b/common/math/integral/integral_math.go index 8cdc6ee18..863b92114 100644 --- a/common/math/integral/integral_math.go +++ b/common/math/integral/integral_math.go @@ -1,3 +1,17 @@ +// 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 integral func MaxInt8(a, b int8) int8 { diff --git a/common/random/random.go b/common/random/random.go new file mode 100644 index 000000000..e2a095e2a --- /dev/null +++ b/common/random/random.go @@ -0,0 +1,154 @@ +// 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 random + +import ( + crand "crypto/rand" + "math/rand" + "time" + + "github.com/eris-ltd/eris-db/common/sanity" +) + +const ( + strChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" // 62 characters +) + +func init() { + b := cRandBytes(8) + var seed uint64 + for i := 0; i < 8; i++ { + seed |= uint64(b[i]) + seed <<= 8 + } + rand.Seed(int64(seed)) +} + +// Constructs an alphanumeric string of given length. +func RandStr(length int) string { + chars := []byte{} +MAIN_LOOP: + for { + val := rand.Int63() + for i := 0; i < 10; i++ { + v := int(val & 0x3f) // rightmost 6 bits + if v >= 62 { // only 62 characters in strChars + val >>= 6 + continue + } else { + chars = append(chars, strChars[v]) + if len(chars) == length { + break MAIN_LOOP + } + val >>= 6 + } + } + } + + return string(chars) +} + +func RandUint16() uint16 { + return uint16(rand.Uint32() & (1<<16 - 1)) +} + +func RandUint32() uint32 { + return rand.Uint32() +} + +func RandUint64() uint64 { + return uint64(rand.Uint32())<<32 + uint64(rand.Uint32()) +} + +func RandUint() uint { + return uint(rand.Int()) +} + +func RandInt16() int16 { + return int16(rand.Uint32() & (1<<16 - 1)) +} + +func RandInt32() int32 { + return int32(rand.Uint32()) +} + +func RandInt64() int64 { + return int64(rand.Uint32())<<32 + int64(rand.Uint32()) +} + +func RandInt() int { + return rand.Int() +} + +// Distributed pseudo-exponentially to test for various cases +func RandUint16Exp() uint16 { + bits := rand.Uint32() % 16 + if bits == 0 { + return 0 + } + n := uint16(1 << (bits - 1)) + n += uint16(rand.Int31()) & ((1 << (bits - 1)) - 1) + return n +} + +// Distributed pseudo-exponentially to test for various cases +func RandUint32Exp() uint32 { + bits := rand.Uint32() % 32 + if bits == 0 { + return 0 + } + n := uint32(1 << (bits - 1)) + n += uint32(rand.Int31()) & ((1 << (bits - 1)) - 1) + return n +} + +// Distributed pseudo-exponentially to test for various cases +func RandUint64Exp() uint64 { + bits := rand.Uint32() % 64 + if bits == 0 { + return 0 + } + n := uint64(1 << (bits - 1)) + n += uint64(rand.Int63()) & ((1 << (bits - 1)) - 1) + return n +} + +func RandFloat32() float32 { + return rand.Float32() +} + +func RandTime() time.Time { + return time.Unix(int64(RandUint64Exp()), 0) +} + +func RandBytes(n int) []byte { + bs := make([]byte, n) + for i := 0; i < n; i++ { + bs[i] = byte(rand.Intn(256)) + } + return bs +} + +// NOTE: This relies on the os's random number generator. +// For real security, we should salt that with some seed. +// See github.com/tendermint/go-crypto for a more secure reader. +func cRandBytes(numBytes int) []byte { + b := make([]byte, numBytes) + _, err := crand.Read(b) + if err != nil { + sanity.PanicCrisis(err) + } + return b +} diff --git a/common/sanity/sanity.go b/common/sanity/sanity.go new file mode 100644 index 000000000..63fe7fd9e --- /dev/null +++ b/common/sanity/sanity.go @@ -0,0 +1,48 @@ +// 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 sanity + +import ( + "fmt" +) + +//-------------------------------------------------------------------------------------------------- +// panic wrappers +// NOTE: [ben] Fail fast and fail hard, these are wrappers that point to code that needs +// to be addressed, but simplify finding them in the code; + +// A panic resulting from a sanity check means there is a programmer error +// and some gaurantee is not satisfied. +func PanicSanity(v interface{}) { + panic(fmt.Sprintf("Paniced on a Sanity Check: %v", v)) +} + +// A panic here means something has gone horribly wrong, in the form of data corruption or +// failure of the operating system. In a correct/healthy system, these should never fire. +// If they do, it's indicative of a much more serious problem. +func PanicCrisis(v interface{}) { + panic(fmt.Sprintf("Paniced on a Crisis: %v", v)) +} + +// Indicates a failure of consensus. Someone was malicious or something has +// gone horribly wrong. These should really boot us into an "emergency-recover" mode +func PanicConsensus(v interface{}) { + panic(fmt.Sprintf("Paniced on a Consensus Failure: %v", v)) +} + +// For those times when we're not sure if we should panic +func PanicQ(v interface{}) { + panic(fmt.Sprintf("Paniced questionably: %v", v)) +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 000000000..e49db3d10 --- /dev/null +++ b/config/config.go @@ -0,0 +1,185 @@ +// 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 config + +import ( + "bytes" + "fmt" + "text/template" +) + +type ConfigServiceGeneral struct { + ChainImageName string + UseDataContainer bool + ExportedPorts string + ContainerEntrypoint string +} + +// TODO: [ben] increase the configurability upon need +type ConfigChainGeneral struct { + AssertChainId string + ErisdbMajorVersion uint8 + ErisdbMinorVersion uint8 + GenesisRelativePath string +} + +type ConfigChainModule struct { + Name string + MajorVersion uint8 + MinorVersion uint8 + ModuleRelativeRoot string +} + +type ConfigTendermint struct { + Moniker string + Seeds string + FastSync bool +} + +var serviceGeneralTemplate *template.Template +var chainGeneralTemplate *template.Template +var chainConsensusTemplate *template.Template +var chainApplicationManagerTemplate *template.Template +var tendermintTemplate *template.Template + +func init() { + var err error + if serviceGeneralTemplate, err = template.New("serviceGeneral").Parse(sectionServiceGeneral); err != nil { + panic(err) + } + if chainGeneralTemplate, err = template.New("chainGeneral").Parse(sectionChainGeneral); err != nil { + panic(err) + } + if chainConsensusTemplate, err = template.New("chainConsensus").Parse(sectionChainConsensus); err != nil { + panic(err) + } + if chainApplicationManagerTemplate, err = template.New("chainApplicationManager").Parse(sectionChainApplicationManager); err != nil { + panic(err) + } + if tendermintTemplate, err = template.New("tendermint").Parse(sectionTendermint); err != nil { + panic(err) + } +} + +// NOTE: [ben] for 0.12.0-rc3 we only have a single configuration path +// with Tendermint in-process as the consensus engine and ErisMint +// in-process as the application manager, so we hard-code the few +// parameters that are already templated. +// Let's learn to walk before we can run. +func GetConfigurationFileBytes(chainId, moniker, seeds string, chainImageName string, + useDataContainer bool, exportedPortsString, containerEntrypoint string) ([]byte, error) { + + erisdbService := &ConfigServiceGeneral{ + ChainImageName: chainImageName, + UseDataContainer: useDataContainer, + ExportedPorts: exportedPortsString, + ContainerEntrypoint: containerEntrypoint, + } + erisdbChain := &ConfigChainGeneral{ + AssertChainId: chainId, + ErisdbMajorVersion: uint8(0), + ErisdbMinorVersion: uint8(16), + GenesisRelativePath: "genesis.json", + } + chainConsensusModule := &ConfigChainModule{ + Name: "tendermint", + MajorVersion: uint8(0), + MinorVersion: uint8(8), + ModuleRelativeRoot: "tendermint", + } + chainApplicationManagerModule := &ConfigChainModule{ + Name: "erismint", + MajorVersion: uint8(0), + MinorVersion: uint8(16), + ModuleRelativeRoot: "erismint", + } + tendermintModule := &ConfigTendermint{ + Moniker: moniker, + Seeds: seeds, + FastSync: false, + } + + // NOTE: [ben] according to StackOverflow appending strings with copy is + // more efficient than bytes.WriteString, but for readability and because + // this is not performance critical code we opt for bytes, which is + // still more efficient than + concatentation operator. + var buffer bytes.Buffer + + // write copyright header + buffer.WriteString(headerCopyright) + + // write section [service] + if err := serviceGeneralTemplate.Execute(&buffer, erisdbService); err != nil { + return nil, fmt.Errorf("Failed to write template service general for %s: %s", + chainId, err) + } + // write section for service dependencies; this is currently a static section + // with a fixed dependency on eris-keys + buffer.WriteString(sectionServiceDependencies) + + // write section [chain] + if err := chainGeneralTemplate.Execute(&buffer, erisdbChain); err != nil { + return nil, fmt.Errorf("Failed to write template chain general for %s: %s", + chainId, err) + } + + // write separator chain consensus + buffer.WriteString(separatorChainConsensus) + // write section [chain.consensus] + if err := chainConsensusTemplate.Execute(&buffer, chainConsensusModule); err != nil { + return nil, fmt.Errorf("Failed to write template chain consensus for %s: %s", + chainId, err) + } + + // write separator chain application manager + buffer.WriteString(separatorChainApplicationManager) + // write section [chain.consensus] + if err := chainApplicationManagerTemplate.Execute(&buffer, + chainApplicationManagerModule); err != nil { + return nil, fmt.Errorf("Failed to write template chain application manager for %s: %s", + chainId, err) + } + + // write separator servers + buffer.WriteString(separatorServerConfiguration) + // TODO: [ben] upon necessity replace this with template too + // write static section servers + buffer.WriteString(sectionServers) + + // write separator modules + buffer.WriteString(separatorModules) + + // write section module Tendermint + if err := tendermintTemplate.Execute(&buffer, tendermintModule); err != nil { + return nil, fmt.Errorf("Failed to write template tendermint for %s, moniker %s: %s", + chainId, moniker, err) + } + + // write static section erismint + buffer.WriteString(sectionErisMint) + + return buffer.Bytes(), nil +} + +func GetExampleConfigFileBytes() ([]byte, error) { + return GetConfigurationFileBytes( + "simplechain", + "delectable_marmot", + "192.168.168.255", + "db:latest", + true, + "46657", + "eris-db") +} diff --git a/config/config_test.go b/config/config_test.go new file mode 100644 index 000000000..1f5a95c19 --- /dev/null +++ b/config/config_test.go @@ -0,0 +1,35 @@ +// 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 config + +import ( + "bytes" + "testing" + + "github.com/spf13/viper" + "github.com/stretchr/testify/assert" +) + +// Since the logic for generating configuration files (in eris-cm) is split from +// the logic for consuming them +func TestGeneratedConfigIsUsable(t *testing.T) { + bs, err := GetExampleConfigFileBytes() + assert.NoError(t, err, "Should be able to create example config") + buf := bytes.NewBuffer(bs) + conf := viper.New() + viper.SetConfigType("toml") + err = conf.ReadConfig(buf) + assert.NoError(t, err, "Should be able to read example config into Viper") +} diff --git a/config/dump_config_test.go b/config/dump_config_test.go new file mode 100644 index 000000000..5d238c9e8 --- /dev/null +++ b/config/dump_config_test.go @@ -0,0 +1,34 @@ +// +build dumpconfig + +// Space above matters +// 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 config + +import ( + "io/ioutil" + "testing" + + "github.com/stretchr/testify/assert" +) + +// This is a little convenience for getting a config file dump. Just run: +// go test -tags dumpconfig ./config +// This pseudo test won't run unless the dumpconfig tag is +func TestDumpConfig(t *testing.T) { + bs, err := GetExampleConfigFileBytes() + assert.NoError(t, err, "Should be able to create example config") + ioutil.WriteFile("config_dump.toml", bs, 0644) +} diff --git a/config/module.go b/config/module.go index 39c131d26..da0218c5c 100644 --- a/config/module.go +++ b/config/module.go @@ -1,21 +1,20 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT +// 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. -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +package config // config defines simple types in a separate package to avoid cyclical imports -package config import ( viper "github.com/spf13/viper" diff --git a/config/templates.go b/config/templates.go new file mode 100644 index 000000000..27372813d --- /dev/null +++ b/config/templates.go @@ -0,0 +1,298 @@ +// 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 config + +const headerCopyright = `# 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. + +# This is a TOML configuration for Eris-DB chains generated by Eris. + +` + +const sectionServiceGeneral = `[service] +# NOTE: this section is read by Eris tooling, and ignored by eris-db. +# Image specifies the image name eris-cli needs to pull +# for running the chain. +image = "{{.ChainImageName}}" +# Define whether eris-cli needs to attach the data container +# for the chain. +data_container = {{.UseDataContainer}} +# Specify a list of ports that need to be exported on the container. +ports = {{.ExportedPorts}} +{{ if ne .ContainerEntrypoint "" }}# Entrypoint points to the default action to execute +# in the chain container. +entry_point = "{{.ContainerEntrypoint}}"{{ end }} + +` + +const sectionServiceDependencies = `[dependencies] +# NOTE: this section is read by Eris tooling, and ignored by eris-db. +# Eris-db expects these services to be available; eric-cli tooling will +# automatically set these services up for you. +# Services to boot with/required by the chain +services = [ "keys" ] + +` + +const sectionChainGeneral = `[chain] + +# ChainId is a human-readable name to identify the chain. +# This must correspond to the chain_id defined in the genesis file +# and the assertion here provides a safe-guard on misconfiguring chains. +assert_chain_id = "{{.AssertChainId}}" +# semantic major and minor version +major_version = {{.ErisdbMajorVersion}} +minor_version = {{.ErisdbMinorVersion}} +# genesis file, relative path is to eris-db working directory +genesis_file = "{{.GenesisRelativePath}}" + +` + +const separatorChainConsensus = ` +################################################################################ +## +## consensus +## +################################################################################ + +` + +const sectionChainConsensus = ` [chain.consensus] + # consensus defines the module to use for consensus and + # this will define the peer-to-peer consensus network; + # accepted values are ("noops", "abci",) "tendermint" + name = "{{.Name}}" + # version is the major and minor semantic version; + # the version will be asserted on + major_version = {{.MajorVersion}} + minor_version = {{.MinorVersion}} + # relative path to consensus' module root folder + relative_root = "{{.ModuleRelativeRoot}}" + + ` + +const separatorChainApplicationManager = ` +################################################################################ +## +## application manager +## +################################################################################ + +` + +const sectionChainApplicationManager = ` [chain.manager] + # application manager name defines the module to use for handling + # the transactions. Supported names are "erismint" + name = "{{.Name}}" + # version is the major and minor semantic version; + # the version will be asserted on + major_version = {{.MajorVersion}} + minor_version = {{.MinorVersion}} + # relative path to application manager root folder + relative_root = "{{.ModuleRelativeRoot}}" + + ` + +const separatorServerConfiguration = ` +################################################################################ +################################################################################ +## +## Server configurations +## +################################################################################ +################################################################################ + +` + +// TODO: [ben] map entries to structure defined in eris-db +const sectionServers = `[servers] + + [servers.bind] + address = "" + port = 1337 + + [servers.tls] + tls = false + cert_path = "" + key_path = "" + + [servers.cors] + enable = false + allow_origins = [] + allow_credentials = false + allow_methods = [] + allow_headers = [] + expose_headers = [] + max_age = 0 + + [servers.http] + json_rpc_endpoint = "/rpc" + + [servers.websocket] + endpoint = "/socketrpc" + max_sessions = 50 + read_buffer_size = 4096 + write_buffer_size = 4096 + + [servers.tendermint] + # Multiple listeners can be separated with a comma + rpc_local_address = "0.0.0.0:46657" + endpoint = "/websocket" + + [servers.logging] + console_log_level = "info" + file_log_level = "warn" + log_file = "" + + ` + +const separatorModules = ` +################################################################################ +################################################################################ +## +## Module configurations - dynamically loaded based on chain configuration +## +################################################################################ +################################################################################ + +` + +// TODO: [ben] minimal fields have been made configurable; expand where needed +const sectionTendermint = ` +################################################################################ +## +## Tendermint +## version 0.8 +## +## in-process execution of Tendermint consensus engine +## +################################################################################ + +[tendermint] +# private validator file is used by tendermint to keep the status +# of the private validator, but also (currently) holds the private key +# for the private vaildator to sign with. This private key needs to be moved +# out and directly managed by eris-keys +# This file needs to be in the root directory +private_validator_file = "priv_validator.json" + + # Tendermint requires additional configuration parameters. + # Eris-DB's tendermint consensus module will load [tendermint.configuration] + # as the configuration for Tendermint. + # Eris-DB will respect the configurations set in this file where applicable, + # but reserves the option to override or block conflicting settings. + [tendermint.configuration] + # moniker is the name of the node on the tendermint p2p network + moniker = "{{.Moniker}}" + # seeds lists the peers tendermint can connect to join the network + seeds = "{{.Seeds}}" + # fast_sync allows a tendermint node to catch up faster when joining + # the network. + # NOTE: Tendermint has reported potential issues with fast_sync enabled. + # The recommended setting is for keeping it disabled. + fast_sync = {{.FastSync}} + # database backend to use for Tendermint. Supported "leveldb" and "memdb". + db_backend = "leveldb" + # logging level. Supported "error" < "warn" < "notice" < "info" < "debug" + log_level = "info" + # node local address + node_laddr = "0.0.0.0:46656" + # rpc local address + # NOTE: value is ignored when run in-process as RPC is + # handled by [servers.tendermint] + rpc_laddr = "0.0.0.0:46657" + # proxy application address - used for abci connections, + # and this port should not be exposed for in-process Tendermint + proxy_app = "tcp://127.0.0.1:46658" + + # Extended Tendermint configuration settings + # for reference to Tendermint see https://github.com/tendermint/tendermint/blob/master/config/tendermint/config.go + + # genesis_file = "./data/tendermint/genesis.json" + # skip_upnp = false + # addrbook_file = "./data/tendermint/addrbook.json" + # priv_validator_file = "./data/tendermint/priv_validator.json" + # db_dir = "./data/tendermint/data" + # prof_laddr = "" + # revision_file = "./data/tendermint/revision" + # cswal = "./data/tendermint/data/cswal" + # cswal_light = false + + # block_size = 10000 + # disable_data_hash = false + # timeout_propose = 3000 + # timeout_propose_delta = 500 + # timeout_prevote = 1000 + # timeout_prevote_delta = 500 + # timeout_precommit = 1000 + # timeout_precommit_delta = 500 + # timeout_commit = 1000 + # mempool_recheck = true + # mempool_recheck_empty = true + # mempool_broadcast = true + + [tendermint.configuration.p2p] + # Switch config keys + dial_timeout_seconds = 3 + handshake_timeout_seconds = 20 + max_num_peers = 20 + authenticated_encryption = true + + # MConnection config keys + send_rate = 512000 + recv_rate = 512000 + + # Fuzz params + fuzz_enable = false # use the fuzz wrapped conn + fuzz_active = false # toggle fuzzing + fuzz_mode = "drop" # eg. drop, delay + fuzz_max_delay_milliseconds = 3000 + fuzz_prob_drop_rw = 0.2 + fuzz_prob_drop_conn = 0.00 + fuzz_prob_sleep = 0.00 + +` + +const sectionErisMint = ` +################################################################################ +## +## Eris-Mint +## version 0.16 +## +## The original Ethereum virtual machine with IAVL merkle trees +## and tendermint/go-wire encoding +## +################################################################################ + +[erismint] +# Database backend to use for ErisMint state database. +# Supported "leveldb" and "memdb". +db_backend = "leveldb" +# tendermint host address needs to correspond to tendermints configuration +# of the rpc local address +tendermint_host = "0.0.0.0:46657" + +` diff --git a/config/viper.go b/config/viper.go new file mode 100644 index 000000000..cefd2f2a8 --- /dev/null +++ b/config/viper.go @@ -0,0 +1,44 @@ +// 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 config + +import ( + "fmt" + + "github.com/spf13/viper" +) + +// Safely get the subtree from a viper config, returning an error if it could not +// be obtained for any reason. +func ViperSubConfig(conf *viper.Viper, configSubtreePath string) (subConfig *viper.Viper, err error) { + // Viper internally panics if `moduleName` contains an unallowed + // character (eg, a dash). + defer func() { + if r := recover(); r != nil { + err = fmt.Errorf("Viper panicked trying to read config subtree: %s", + configSubtreePath) + } + }() + if !conf.IsSet(configSubtreePath) { + return nil, fmt.Errorf("Failed to read config subtree: %s", + configSubtreePath) + } + subConfig = conf.Sub(configSubtreePath) + if subConfig == nil { + return nil, fmt.Errorf("Failed to read config subtree: %s", + configSubtreePath) + } + return subConfig, err +} diff --git a/consensus/config.go b/consensus/config.go index d676d8c21..05cc488f1 100644 --- a/consensus/config.go +++ b/consensus/config.go @@ -1,28 +1,22 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// version provides the current Eris-DB version and a VersionIdentifier -// for the modules to identify their version with. +// 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 consensus import ( // noops "github.com/eris-ltd/eris-db/consensus/noops" tendermint "github.com/eris-ltd/eris-db/consensus/tendermint" - tmsp "github.com/eris-ltd/eris-db/consensus/tmsp" ) //------------------------------------------------------------------------------ @@ -34,8 +28,6 @@ func AssertValidConsensusModule(name, minorVersionString string) bool { // noops should not have any external interfaces that can change // over iterations return true - case "tmsp": - return minorVersionString == tmsp.GetTmspVersion().GetMinorVersionString() case "tendermint": return minorVersionString == tendermint.GetTendermintVersion().GetMinorVersionString() case "bigchaindb": diff --git a/consensus/consensus.go b/consensus/consensus.go index f0795a5dd..77b7ffff3 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 consensus @@ -28,13 +26,13 @@ func LoadConsensusEngineInPipe(moduleConfig *config.ModuleConfig, pipe definitions.Pipe) error { switch moduleConfig.Name { case "tendermint": - tendermint, err := tendermint.NewTendermint(moduleConfig, - pipe.GetApplication()) + tmint, err := tendermint.NewTendermint(moduleConfig, pipe.GetApplication(), + pipe.Logger().With()) if err != nil { return fmt.Errorf("Failed to load Tendermint node: %v", err) } - err = pipe.SetConsensusEngine(tendermint) + err = pipe.SetConsensusEngine(tmint) if err != nil { return fmt.Errorf("Failed to load Tendermint in pipe as "+ "ConsensusEngine: %v", err) @@ -42,7 +40,7 @@ func LoadConsensusEngineInPipe(moduleConfig *config.ModuleConfig, // For Tendermint we have a coupled Blockchain and ConsensusEngine // implementation, so load it at the same time as ConsensusEngine - err = pipe.SetBlockchain(tendermint) + err = pipe.SetBlockchain(tmint) if err != nil { return fmt.Errorf("Failed to load Tendermint in pipe as "+ "Blockchain: %v", err) diff --git a/consensus/tendermint/config.go b/consensus/tendermint/config.go index 1bbb74bd6..b8c89d241 100644 --- a/consensus/tendermint/config.go +++ b/consensus/tendermint/config.go @@ -1,21 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// version provides the current Eris-DB version and a VersionIdentifier -// for the modules to identify their version with. +// 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 tendermint @@ -23,10 +18,10 @@ import ( "path" "time" - viper "github.com/spf13/viper" + "github.com/spf13/viper" tendermintConfig "github.com/tendermint/go-config" - config "github.com/eris-ltd/eris-db/config" + "github.com/eris-ltd/eris-db/config" ) // NOTE [ben] Compiler check to ensure TendermintConfig successfully implements @@ -56,6 +51,10 @@ func GetTendermintConfig(loadedConfig *viper.Viper) *TendermintConfig { //------------------------------------------------------------------------------ // Tendermint defaults +// +// Contract +// + func (tmintConfig *TendermintConfig) AssertTendermintDefaults(chainId, workDir, dataDir, rootDir string) { @@ -69,6 +68,8 @@ func (tmintConfig *TendermintConfig) AssertTendermintDefaults(chainId, workDir, tmintConfig.SetDefault("fast_sync", true) tmintConfig.SetDefault("skip_upnp", false) tmintConfig.SetDefault("addrbook_file", path.Join(rootDir, "addrbook.json")) + tmintConfig.SetDefault("addrbook_strict", true) // disable to allow connections locally + tmintConfig.SetDefault("pex_reactor", false) // enable for peer exchange tmintConfig.SetDefault("priv_validator_file", path.Join(rootDir, "priv_validator.json")) tmintConfig.SetDefault("db_backend", "leveldb") tmintConfig.SetDefault("db_dir", dataDir) @@ -76,10 +77,12 @@ func (tmintConfig *TendermintConfig) AssertTendermintDefaults(chainId, workDir, tmintConfig.SetDefault("rpc_laddr", "") tmintConfig.SetDefault("prof_laddr", "") tmintConfig.SetDefault("revision_file", path.Join(workDir, "revision")) - tmintConfig.SetDefault("cswal", path.Join(dataDir, "cswal")) - tmintConfig.SetDefault("cswal_light", false) + tmintConfig.SetDefault("cs_wal_dir", path.Join(dataDir, "cs.wal")) + tmintConfig.SetDefault("cs_wal_light", false) + tmintConfig.SetDefault("filter_peers", false) - tmintConfig.SetDefault("block_size", 10000) + tmintConfig.SetDefault("block_size", 10000) // max number of txs + tmintConfig.SetDefault("block_part_size", 65536) // part size 64K tmintConfig.SetDefault("disable_data_hash", false) tmintConfig.SetDefault("timeout_propose", 3000) tmintConfig.SetDefault("timeout_propose_delta", 500) @@ -88,9 +91,12 @@ func (tmintConfig *TendermintConfig) AssertTendermintDefaults(chainId, workDir, tmintConfig.SetDefault("timeout_precommit", 1000) tmintConfig.SetDefault("timeout_precommit_delta", 500) tmintConfig.SetDefault("timeout_commit", 1000) + // make progress asap (no `timeout_commit`) on full precommit votes + tmintConfig.SetDefault("skip_timeout_commit", false) tmintConfig.SetDefault("mempool_recheck", true) tmintConfig.SetDefault("mempool_recheck_empty", true) tmintConfig.SetDefault("mempool_broadcast", true) + tmintConfig.SetDefault("mempool_wal_dir", path.Join(dataDir, "mempool.wal")) } //------------------------------------------------------------------------------ @@ -147,7 +153,8 @@ func (tmintConfig *TendermintConfig) GetMapString(key string) map[string]string func (tmintConfig *TendermintConfig) GetConfig(key string) tendermintConfig.Config { // TODO: [ben] log out a warning as this indicates a potentially breaking code // change from Tendermints side - if !tmintConfig.subTree.IsSet(key) { + subTree, _ := config.ViperSubConfig(tmintConfig.subTree, key) + if subTree == nil { return &TendermintConfig{ subTree: viper.New(), } diff --git a/consensus/tendermint/local_client.go b/consensus/tendermint/local_client.go deleted file mode 100644 index 97b732cac..000000000 --- a/consensus/tendermint/local_client.go +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// version provides the current Eris-DB version and a VersionIdentifier -// for the modules to identify their version with. - -// This file is originally based on github.com/tendermint/tmsp/client/... -// .../local_client.go - -package tendermint - -import ( - "sync" - - tmsp_client "github.com/tendermint/tmsp/client" - tmsp_types "github.com/tendermint/tmsp/types" - - manager_types "github.com/eris-ltd/eris-db/manager/types" -) - -// NOTE [ben] Compiler check to ensure localClient successfully implements -// tendermint/tmsp/client -var _ tmsp_client.Client = (*localClient)(nil) - -type localClient struct { - mtx *sync.Mutex - Application manager_types.Application - Callback tmsp_client.Callback -} - -func NewLocalClient(mtx *sync.Mutex, app manager_types.Application) *localClient { - if mtx == nil { - mtx = new(sync.Mutex) - } - return &localClient{ - mtx: mtx, - Application: app, - } -} - -func (app *localClient) SetResponseCallback(cb tmsp_client.Callback) { - app.mtx.Lock() - defer app.mtx.Unlock() - app.Callback = cb -} - -// TODO: change manager_types.Application to include Error()? -func (app *localClient) Error() error { - return nil -} - -func (app *localClient) Stop() bool { - return true -} - -func (app *localClient) FlushAsync() *tmsp_client.ReqRes { - // Do nothing - return newLocalReqRes(tmsp_types.ToRequestFlush(), nil) -} - -func (app *localClient) EchoAsync(msg string) *tmsp_client.ReqRes { - return app.callback( - tmsp_types.ToRequestEcho(msg), - tmsp_types.ToResponseEcho(msg), - ) -} - -func (app *localClient) InfoAsync() *tmsp_client.ReqRes { - app.mtx.Lock() - info := app.Application.Info() - app.mtx.Unlock() - return app.callback( - tmsp_types.ToRequestInfo(), - tmsp_types.ToResponseInfo(info), - ) -} - -func (app *localClient) SetOptionAsync(key string, value string) *tmsp_client.ReqRes { - app.mtx.Lock() - log := app.Application.SetOption(key, value) - app.mtx.Unlock() - return app.callback( - tmsp_types.ToRequestSetOption(key, value), - tmsp_types.ToResponseSetOption(log), - ) -} - -func (app *localClient) AppendTxAsync(tx []byte) *tmsp_client.ReqRes { - app.mtx.Lock() - res := app.Application.AppendTx(tx) - app.mtx.Unlock() - return app.callback( - tmsp_types.ToRequestAppendTx(tx), - tmsp_types.ToResponseAppendTx(res.Code, res.Data, res.Log), - ) -} - -func (app *localClient) CheckTxAsync(tx []byte) *tmsp_client.ReqRes { - app.mtx.Lock() - res := app.Application.CheckTx(tx) - app.mtx.Unlock() - return app.callback( - tmsp_types.ToRequestCheckTx(tx), - tmsp_types.ToResponseCheckTx(res.Code, res.Data, res.Log), - ) -} - -func (app *localClient) QueryAsync(tx []byte) *tmsp_client.ReqRes { - app.mtx.Lock() - res := app.Application.Query(tx) - app.mtx.Unlock() - return app.callback( - tmsp_types.ToRequestQuery(tx), - tmsp_types.ToResponseQuery(res.Code, res.Data, res.Log), - ) -} - -func (app *localClient) CommitAsync() *tmsp_client.ReqRes { - app.mtx.Lock() - res := app.Application.Commit() - app.mtx.Unlock() - return app.callback( - tmsp_types.ToRequestCommit(), - tmsp_types.ToResponseCommit(res.Code, res.Data, res.Log), - ) -} - -func (app *localClient) InitChainAsync(validators []*tmsp_types.Validator) *tmsp_client.ReqRes { - app.mtx.Lock() - if bcApp, ok := app.Application.(tmsp_types.BlockchainAware); ok { - bcApp.InitChain(validators) - } - reqRes := app.callback( - tmsp_types.ToRequestInitChain(validators), - tmsp_types.ToResponseInitChain(), - ) - app.mtx.Unlock() - return reqRes -} - -func (app *localClient) BeginBlockAsync(height uint64) *tmsp_client.ReqRes { - app.mtx.Lock() - if bcApp, ok := app.Application.(tmsp_types.BlockchainAware); ok { - bcApp.BeginBlock(height) - } - app.mtx.Unlock() - return app.callback( - tmsp_types.ToRequestBeginBlock(height), - tmsp_types.ToResponseBeginBlock(), - ) -} - -func (app *localClient) EndBlockAsync(height uint64) *tmsp_client.ReqRes { - app.mtx.Lock() - var validators []*tmsp_types.Validator - if bcApp, ok := app.Application.(tmsp_types.BlockchainAware); ok { - validators = bcApp.EndBlock(height) - } - app.mtx.Unlock() - return app.callback( - tmsp_types.ToRequestEndBlock(height), - tmsp_types.ToResponseEndBlock(validators), - ) -} - -//------------------------------------------------------- - -func (app *localClient) FlushSync() error { - return nil -} - -func (app *localClient) EchoSync(msg string) (res tmsp_types.Result) { - return tmsp_types.OK.SetData([]byte(msg)) -} - -func (app *localClient) InfoSync() (res tmsp_types.Result) { - app.mtx.Lock() - info := app.Application.Info() - app.mtx.Unlock() - return tmsp_types.OK.SetData([]byte(info)) -} - -func (app *localClient) SetOptionSync(key string, value string) (res tmsp_types.Result) { - app.mtx.Lock() - log := app.Application.SetOption(key, value) - app.mtx.Unlock() - return tmsp_types.OK.SetLog(log) -} - -func (app *localClient) AppendTxSync(tx []byte) (res tmsp_types.Result) { - app.mtx.Lock() - res = app.Application.AppendTx(tx) - app.mtx.Unlock() - return res -} - -func (app *localClient) CheckTxSync(tx []byte) (res tmsp_types.Result) { - app.mtx.Lock() - res = app.Application.CheckTx(tx) - app.mtx.Unlock() - return res -} - -func (app *localClient) QuerySync(query []byte) (res tmsp_types.Result) { - app.mtx.Lock() - res = app.Application.Query(query) - app.mtx.Unlock() - return res -} - -func (app *localClient) CommitSync() (res tmsp_types.Result) { - app.mtx.Lock() - res = app.Application.Commit() - app.mtx.Unlock() - return res -} - -func (app *localClient) InitChainSync(validators []*tmsp_types.Validator) (err error) { - app.mtx.Lock() - if bcApp, ok := app.Application.(tmsp_types.BlockchainAware); ok { - bcApp.InitChain(validators) - } - app.mtx.Unlock() - return nil -} - -func (app *localClient) BeginBlockSync(height uint64) (err error) { - app.mtx.Lock() - if bcApp, ok := app.Application.(tmsp_types.BlockchainAware); ok { - bcApp.BeginBlock(height) - } - app.mtx.Unlock() - return nil -} - -func (app *localClient) EndBlockSync(height uint64) (changedValidators []*tmsp_types.Validator, err error) { - app.mtx.Lock() - if bcApp, ok := app.Application.(tmsp_types.BlockchainAware); ok { - changedValidators = bcApp.EndBlock(height) - } - app.mtx.Unlock() - return changedValidators, nil -} - -//------------------------------------------------------- - -func (app *localClient) callback(req *tmsp_types.Request, res *tmsp_types.Response) *tmsp_client.ReqRes { - app.Callback(req, res) - return newLocalReqRes(req, res) -} - -func newLocalReqRes(req *tmsp_types.Request, res *tmsp_types.Response) *tmsp_client.ReqRes { - reqRes := tmsp_client.NewReqRes(req) - reqRes.Response = res - reqRes.SetDone() - return reqRes -} diff --git a/consensus/tendermint/tendermint.go b/consensus/tendermint/tendermint.go index 237fb4a0b..2eda6171d 100644 --- a/consensus/tendermint/tendermint.go +++ b/consensus/tendermint/tendermint.go @@ -1,21 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// version provides the current Eris-DB version and a VersionIdentifier -// for the modules to identify their version with. +// 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 tendermint @@ -23,24 +18,24 @@ import ( "fmt" "path" "strings" - "sync" + abci_types "github.com/tendermint/abci/types" crypto "github.com/tendermint/go-crypto" p2p "github.com/tendermint/go-p2p" tendermint_consensus "github.com/tendermint/tendermint/consensus" node "github.com/tendermint/tendermint/node" proxy "github.com/tendermint/tendermint/proxy" tendermint_types "github.com/tendermint/tendermint/types" - tmsp_types "github.com/tendermint/tmsp/types" edb_event "github.com/eris-ltd/eris-db/event" - log "github.com/eris-ltd/eris-logger" config "github.com/eris-ltd/eris-db/config" manager_types "github.com/eris-ltd/eris-db/manager/types" // files "github.com/eris-ltd/eris-db/files" blockchain_types "github.com/eris-ltd/eris-db/blockchain/types" consensus_types "github.com/eris-ltd/eris-db/consensus/types" + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/loggers" "github.com/eris-ltd/eris-db/txs" "github.com/tendermint/go-wire" ) @@ -49,6 +44,7 @@ type Tendermint struct { tmintNode *node.Node tmintConfig *TendermintConfig chainId string + logger loggers.InfoTraceLogger } // Compiler checks to ensure Tendermint successfully implements @@ -57,7 +53,8 @@ var _ consensus_types.ConsensusEngine = (*Tendermint)(nil) var _ blockchain_types.Blockchain = (*Tendermint)(nil) func NewTendermint(moduleConfig *config.ModuleConfig, - application manager_types.Application) (*Tendermint, error) { + application manager_types.Application, + logger loggers.InfoTraceLogger) (*Tendermint, error) { // re-assert proper configuration for module if moduleConfig.Version != GetTendermintVersion().GetMinorVersionString() { return nil, fmt.Errorf("Version string %s did not match %s", @@ -71,10 +68,10 @@ func NewTendermint(moduleConfig *config.ModuleConfig, if !moduleConfig.Config.IsSet("configuration") { return nil, fmt.Errorf("Failed to extract Tendermint configuration subtree.") } - tendermintConfigViper := moduleConfig.Config.Sub("configuration") + tendermintConfigViper, err := config.ViperSubConfig(moduleConfig.Config, "configuration") if tendermintConfigViper == nil { return nil, - fmt.Errorf("Failed to extract Tendermint configuration subtree.") + fmt.Errorf("Failed to extract Tendermint configuration subtree: %s", err) } // wrap a copy of the viper config in a tendermint/go-config interface tmintConfig := GetTendermintConfig(tendermintConfigViper) @@ -92,18 +89,19 @@ func NewTendermint(moduleConfig *config.ModuleConfig, tmintConfig.AssertTendermintConsistency(moduleConfig, privateValidatorFilePath) chainId := tmintConfig.GetString("chain_id") - log.WithFields(log.Fields{ - "chainId": chainId, - "genesisFile": tmintConfig.GetString("genesis_file"), - "nodeLocalAddress": tmintConfig.GetString("node_laddr"), - "moniker": tmintConfig.GetString("moniker"), - "seeds": tmintConfig.GetString("seeds"), - "fastSync": tmintConfig.GetBool("fast_sync"), - "rpcLocalAddress": tmintConfig.GetString("rpc_laddr"), - "databaseDirectory": tmintConfig.GetString("db_dir"), - "privateValidatorFile": tmintConfig.GetString("priv_validator_file"), - "privValFile": moduleConfig.Config.GetString("private_validator_file"), - }).Debug("Loaded Tendermint sub-configuration") + + logging.TraceMsg(logger, "Loaded Tendermint sub-configuration", + "chainId", chainId, + "genesisFile", tmintConfig.GetString("genesis_file"), + "nodeLocalAddress", tmintConfig.GetString("node_laddr"), + "moniker", tmintConfig.GetString("moniker"), + "seeds", tmintConfig.GetString("seeds"), + "fastSync", tmintConfig.GetBool("fast_sync"), + "rpcLocalAddress", tmintConfig.GetString("rpc_laddr"), + "databaseDirectory", tmintConfig.GetString("db_dir"), + "privateValidatorFile", tmintConfig.GetString("priv_validator_file"), + "privValFile", moduleConfig.Config.GetString("private_validator_file")) + // TODO: [ben] do not "or Generate Validator keys", rather fail directly // TODO: [ben] implement the signer for Private validator over eris-keys // TODO: [ben] copy from rootDir to tendermint workingDir; @@ -116,15 +114,13 @@ func NewTendermint(moduleConfig *config.ModuleConfig, // not running the tendermint RPC as it could lead to unexpected behaviour, // not least if we accidentally try to run it on the same address as our own if tmintConfig.GetString("rpc_laddr") != "" { - log.Warnf("Force disabling Tendermint's native RPC, which had been set to "+ - "run on '%s' in the Tendermint config.", tmintConfig.GetString("rpc_laddr")) + logging.InfoMsg(logger, "Force disabling Tendermint's native RPC", + "provided_rpc_laddr", tmintConfig.GetString("rpc_laddr")) tmintConfig.Set("rpc_laddr", "") } - newNode := node.NewNode(tmintConfig, privateValidator, func(_, _ string, - hash []byte) proxy.AppConn { - return NewLocalClient(new(sync.Mutex), application) - }) + newNode := node.NewNode(tmintConfig, privateValidator, + proxy.NewLocalClientCreator(application)) listener := p2p.NewDefaultListener("tcp", tmintConfig.GetString("node_laddr"), tmintConfig.GetBool("skip_upnp")) @@ -136,26 +132,25 @@ func NewTendermint(moduleConfig *config.ModuleConfig, newNode.Stop() return nil, fmt.Errorf("Failed to start Tendermint consensus node: %v", err) } - log.WithFields(log.Fields{ - "nodeAddress": tmintConfig.GetString("node_laddr"), - "transportProtocol": "tcp", - "upnp": !tmintConfig.GetBool("skip_upnp"), - "moniker": tmintConfig.GetString("moniker"), - }).Info("Tendermint consensus node started") + logging.InfoMsg(logger, "Tendermint consensus node started", + "nodeAddress", tmintConfig.GetString("node_laddr"), + "transportProtocol", "tcp", + "upnp", !tmintConfig.GetBool("skip_upnp"), + "moniker", tmintConfig.GetString("moniker")) // If seedNode is provided by config, dial out. if tmintConfig.GetString("seeds") != "" { seeds := strings.Split(tmintConfig.GetString("seeds"), ",") newNode.DialSeeds(seeds) - log.WithFields(log.Fields{ - "seeds": seeds, - }).Debug("Tendermint node called seeds") + logging.TraceMsg(logger, "Tendermint node called seeds", + "seeds", seeds) } return &Tendermint{ tmintNode: newNode, tmintConfig: tmintConfig, chainId: chainId, + logger: logger, }, nil } @@ -228,11 +223,11 @@ func (tendermint *Tendermint) PublicValidatorKey() crypto.PubKey { } func (tendermint *Tendermint) Events() edb_event.EventEmitter { - return edb_event.NewEvents(tendermint.tmintNode.EventSwitch()) + return edb_event.NewEvents(tendermint.tmintNode.EventSwitch(), tendermint.logger) } func (tendermint *Tendermint) BroadcastTransaction(transaction []byte, - callback func(*tmsp_types.Response)) error { + callback func(*abci_types.Response)) error { return tendermint.tmintNode.MempoolReactor().BroadcastTx(transaction, callback) } diff --git a/consensus/tendermint/version.go b/consensus/tendermint/version.go index 870d1313b..78ed5dea5 100644 --- a/consensus/tendermint/version.go +++ b/consensus/tendermint/version.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 tendermint @@ -30,7 +28,7 @@ const ( // Major version component of the current release tendermintVersionMajorConst uint8 = 0 // Minor version component of the current release - tendermintVersionMinorConst uint8 = 6 + tendermintVersionMinorConst uint8 = 8 // Patch version component of the current release tendermintVersionPatchConst uint8 = 0 ) diff --git a/consensus/tendermint/version_test.go b/consensus/tendermint/version_test.go index 25985ec11..bfbbe3e76 100644 --- a/consensus/tendermint/version_test.go +++ b/consensus/tendermint/version_test.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 tendermint diff --git a/consensus/tmsp/server.go b/consensus/tmsp/server.go deleted file mode 100644 index 62abb9d64..000000000 --- a/consensus/tmsp/server.go +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// Taken originally from github.com/tendermint/tmsp/server.go - -package tmsp - -import ( - "bufio" - "fmt" - "io" - "net" - "strings" - "sync" - - . "github.com/tendermint/go-common" - tmsp_types "github.com/tendermint/tmsp/types" - - manager_types "github.com/eris-ltd/eris-db/manager/types" -) - -// var maxNumberConnections = 2 - -type Server struct { - QuitService - - proto string - addr string - listener net.Listener - - appMtx sync.Mutex - app manager_types.Application -} - -func NewServer(protoAddr string, app manager_types.Application) (*Server, error) { - parts := strings.SplitN(protoAddr, "://", 2) - proto, addr := parts[0], parts[1] - s := &Server{ - proto: proto, - addr: addr, - listener: nil, - app: app, - } - s.QuitService = *NewQuitService(nil, "TMSPServer", s) - _, err := s.Start() // Just start it - return s, err -} - -func (s *Server) OnStart() error { - s.QuitService.OnStart() - ln, err := net.Listen(s.proto, s.addr) - if err != nil { - return err - } - s.listener = ln - go s.acceptConnectionsRoutine() - return nil -} - -func (s *Server) OnStop() { - s.QuitService.OnStop() - s.listener.Close() -} - -func (s *Server) acceptConnectionsRoutine() { - // semaphore := make(chan struct{}, maxNumberConnections) - - for { - // semaphore <- struct{}{} - - // Accept a connection - fmt.Println("Waiting for new connection...") - conn, err := s.listener.Accept() - if err != nil { - if !s.IsRunning() { - return // Ignore error from listener closing. - } - Exit("Failed to accept connection: " + err.Error()) - } else { - fmt.Println("Accepted a new connection") - } - - closeConn := make(chan error, 2) // Push to signal connection closed - responses := make(chan *tmsp_types.Response, 1000) // A channel to buffer responses - - // Read requests from conn and deal with them - go s.handleRequests(closeConn, conn, responses) - // Pull responses from 'responses' and write them to conn. - go s.handleResponses(closeConn, responses, conn) - - go func() { - // Wait until signal to close connection - errClose := <-closeConn - if errClose != nil { - fmt.Printf("Connection error: %v\n", errClose) - } else { - fmt.Println("Connection was closed.") - } - - // Close the connection - err := conn.Close() - if err != nil { - fmt.Printf("Error in closing connection: %v\n", err) - } - - // <-semaphore - }() - } -} - -// Read requests from conn and deal with them -func (s *Server) handleRequests(closeConn chan error, conn net.Conn, responses chan<- *tmsp_types.Response) { - var count int - var bufReader = bufio.NewReader(conn) - for { - - var req = &tmsp_types.Request{} - err := tmsp_types.ReadMessage(bufReader, req) - if err != nil { - if err == io.EOF { - closeConn <- fmt.Errorf("Connection closed by client") - } else { - closeConn <- fmt.Errorf("Error in handleRequests: %v", err.Error()) - } - return - } - s.appMtx.Lock() - count++ - s.handleRequest(req, responses) - s.appMtx.Unlock() - } -} - -func (s *Server) handleRequest(req *tmsp_types.Request, responses chan<- *tmsp_types.Response) { - switch r := req.Value.(type) { - case *tmsp_types.Request_Echo: - responses <- tmsp_types.ToResponseEcho(r.Echo.Message) - case *tmsp_types.Request_Flush: - responses <- tmsp_types.ToResponseFlush() - case *tmsp_types.Request_Info: - data := s.app.Info() - responses <- tmsp_types.ToResponseInfo(data) - case *tmsp_types.Request_SetOption: - so := r.SetOption - logStr := s.app.SetOption(so.Key, so.Value) - responses <- tmsp_types.ToResponseSetOption(logStr) - case *tmsp_types.Request_AppendTx: - res := s.app.AppendTx(r.AppendTx.Tx) - responses <- tmsp_types.ToResponseAppendTx(res.Code, res.Data, res.Log) - case *tmsp_types.Request_CheckTx: - res := s.app.CheckTx(r.CheckTx.Tx) - responses <- tmsp_types.ToResponseCheckTx(res.Code, res.Data, res.Log) - case *tmsp_types.Request_Commit: - res := s.app.Commit() - responses <- tmsp_types.ToResponseCommit(res.Code, res.Data, res.Log) - case *tmsp_types.Request_Query: - res := s.app.Query(r.Query.Query) - responses <- tmsp_types.ToResponseQuery(res.Code, res.Data, res.Log) - case *tmsp_types.Request_InitChain: - if app, ok := s.app.(tmsp_types.BlockchainAware); ok { - app.InitChain(r.InitChain.Validators) - responses <- tmsp_types.ToResponseInitChain() - } else { - responses <- tmsp_types.ToResponseInitChain() - } - case *tmsp_types.Request_EndBlock: - if app, ok := s.app.(tmsp_types.BlockchainAware); ok { - validators := app.EndBlock(r.EndBlock.Height) - responses <- tmsp_types.ToResponseEndBlock(validators) - } else { - responses <- tmsp_types.ToResponseEndBlock(nil) - } - default: - responses <- tmsp_types.ToResponseException("Unknown request") - } -} - -// Pull responses from 'responses' and write them to conn. -func (s *Server) handleResponses(closeConn chan error, responses <-chan *tmsp_types.Response, conn net.Conn) { - var count int - var bufWriter = bufio.NewWriter(conn) - for { - var res = <-responses - err := tmsp_types.WriteMessage(res, bufWriter) - if err != nil { - closeConn <- fmt.Errorf("Error in handleResponses: %v", err.Error()) - return - } - if _, ok := res.Value.(*tmsp_types.Response_Flush); ok { - err = bufWriter.Flush() - if err != nil { - closeConn <- fmt.Errorf("Error in handleValue: %v", err.Error()) - return - } - } - count++ - } -} diff --git a/consensus/tmsp/version.go b/consensus/tmsp/version.go deleted file mode 100644 index 266b37d79..000000000 --- a/consensus/tmsp/version.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -package tmsp - -import ( - version "github.com/eris-ltd/eris-db/version" -) - -const ( - // Client identifier to advertise over the network - tmspClientIdentifier = "tmsp" - // Major version component of the current release - tmspVersionMajor = 0 - // Minor version component of the current release - tmspVersionMinor = 6 - // Patch version component of the current release - tmspVersionPatch = 0 -) - -func GetTmspVersion() *version.VersionIdentifier { - return version.New(tmspClientIdentifier, tmspVersionMajor, tmspVersionMinor, - tmspVersionPatch) -} diff --git a/consensus/types/consensus_engine.go b/consensus/types/consensus_engine.go index ea02d3b2e..191bc6e4c 100644 --- a/consensus/types/consensus_engine.go +++ b/consensus/types/consensus_engine.go @@ -1,11 +1,25 @@ +// 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 ( "github.com/eris-ltd/eris-db/event" "github.com/eris-ltd/eris-db/txs" + abci_types "github.com/tendermint/abci/types" "github.com/tendermint/go-crypto" "github.com/tendermint/go-p2p" - tmsp_types "github.com/tendermint/tmsp/types" ) type ConsensusEngine interface { @@ -20,7 +34,7 @@ type ConsensusEngine interface { // Memory pool BroadcastTransaction(transaction []byte, - callback func(*tmsp_types.Response)) error + callback func(*abci_types.Response)) error // Events // For consensus events like NewBlock diff --git a/consensus/types/consensus_state.go b/consensus/types/consensus_state.go index a22febd4f..74d8b4b4a 100644 --- a/consensus/types/consensus_state.go +++ b/consensus/types/consensus_state.go @@ -1,3 +1,17 @@ +// 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 ( diff --git a/consensus/types/peer.go b/consensus/types/peer.go index 78ddc885c..c123cf5c0 100644 --- a/consensus/types/peer.go +++ b/consensus/types/peer.go @@ -1,3 +1,17 @@ +// 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 "github.com/tendermint/go-p2p" diff --git a/consensus/types/validator.go b/consensus/types/validator.go index 8feefb1f3..86fcfac1a 100644 --- a/consensus/types/validator.go +++ b/consensus/types/validator.go @@ -1,3 +1,17 @@ +// 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 ( diff --git a/core/config.go b/core/config.go index a2a82dbd1..9090c28ae 100644 --- a/core/config.go +++ b/core/config.go @@ -1,22 +1,21 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT +// Copyright 2017 Monax Industries Limited // -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. +// 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 // -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// http://www.apache.org/licenses/LICENSE-2.0 // -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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. // config.go keeps explicit structures on the runtime configuration of // Eris-DB and all modules. It loads these from the Viper configuration // loaded in `definitions.Do` + package core import ( @@ -24,13 +23,14 @@ import ( "os" "path" - config "github.com/eris-ltd/eris-db/config" - consensus "github.com/eris-ltd/eris-db/consensus" - definitions "github.com/eris-ltd/eris-db/definitions" - manager "github.com/eris-ltd/eris-db/manager" - server "github.com/eris-ltd/eris-db/server" - util "github.com/eris-ltd/eris-db/util" - version "github.com/eris-ltd/eris-db/version" + "github.com/eris-ltd/eris-db/config" + "github.com/eris-ltd/eris-db/consensus" + "github.com/eris-ltd/eris-db/definitions" + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/manager" + "github.com/eris-ltd/eris-db/server" + "github.com/eris-ltd/eris-db/util" + "github.com/eris-ltd/eris-db/version" "github.com/spf13/viper" ) @@ -75,17 +75,14 @@ func LoadModuleConfig(conf *viper.Viper, rootWorkDir, rootDataDir, fmt.Errorf("Failed to create module data directory %s.", dataDir) } // load configuration subtree for module - // TODO: [ben] Viper internally panics if `moduleName` contains an unallowed - // character (eg, a dash). Either this needs to be wrapped in a go-routine - // and recovered from or a PR to viper is needed to address this bug. if !conf.IsSet(moduleName) { return nil, fmt.Errorf("Failed to read configuration section for %s", moduleName) } - subConfig := conf.Sub(moduleName) + subConfig, err := config.ViperSubConfig(conf, moduleName) if subConfig == nil { - return nil, - fmt.Errorf("Failed to read configuration section for %s.", moduleName) + return nil, fmt.Errorf("Failed to read configuration section for %s: %s", + moduleName, err) } return &config.ModuleConfig{ @@ -104,19 +101,29 @@ func LoadModuleConfig(conf *viper.Viper, rootWorkDir, rootDataDir, // LoadServerModuleConfig wraps specifically for the servers run by core func LoadServerConfig(do *definitions.Do) (*server.ServerConfig, error) { // load configuration subtree for servers - if !do.Config.IsSet("servers") { - return nil, fmt.Errorf("Failed to read configuration section for servers") - } - subConfig := do.Config.Sub("servers") - if subConfig == nil { - return nil, - fmt.Errorf("Failed to read configuration section for servers") + subConfig, err := config.ViperSubConfig(do.Config, "servers") + if err != nil { + return nil, err } serverConfig, err := server.ReadServerConfig(subConfig) + if err != nil { + return nil, err + } serverConfig.ChainId = do.ChainId return serverConfig, err } +func LoadLoggingConfigFromDo(do *definitions.Do) (*logging.LoggingConfig, error) { + //subConfig, err := SubConfig(conf, "logging") + loggingConfig := &logging.LoggingConfig{} + return loggingConfig, nil +} + +func LoadLoggingConfigFromClientDo(do *definitions.ClientDo) (*logging.LoggingConfig, error) { + loggingConfig := &logging.LoggingConfig{} + return loggingConfig, nil +} + //------------------------------------------------------------------------------ // Helper functions diff --git a/core/core.go b/core/core.go index 03f56b56a..14725b6c2 100644 --- a/core/core.go +++ b/core/core.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 core @@ -22,55 +20,53 @@ import ( // TODO: [ben] swap out go-events with eris-db/event (currently unused) events "github.com/tendermint/go-events" - log "github.com/eris-ltd/eris-logger" - - config "github.com/eris-ltd/eris-db/config" - consensus "github.com/eris-ltd/eris-db/consensus" - definitions "github.com/eris-ltd/eris-db/definitions" - event "github.com/eris-ltd/eris-db/event" - manager "github.com/eris-ltd/eris-db/manager" + "github.com/eris-ltd/eris-db/config" + "github.com/eris-ltd/eris-db/consensus" + "github.com/eris-ltd/eris-db/definitions" + "github.com/eris-ltd/eris-db/event" + "github.com/eris-ltd/eris-db/manager" // rpc_v0 is carried over from Eris-DBv0.11 and before on port 1337 rpc_v0 "github.com/eris-ltd/eris-db/rpc/v0" // rpc_tendermint is carried over from Eris-DBv0.11 and before on port 46657 + + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/loggers" rpc_tendermint "github.com/eris-ltd/eris-db/rpc/tendermint/core" - server "github.com/eris-ltd/eris-db/server" + "github.com/eris-ltd/eris-db/server" ) // Core is the high-level structure type Core struct { chainId string - evsw *events.EventSwitch + evsw events.EventSwitch pipe definitions.Pipe tendermintPipe definitions.TendermintPipe } -func NewCore(chainId string, consensusConfig *config.ModuleConfig, - managerConfig *config.ModuleConfig) (*Core, error) { +func NewCore(chainId string, + consensusConfig *config.ModuleConfig, + managerConfig *config.ModuleConfig, + logger loggers.InfoTraceLogger) (*Core, error) { // start new event switch, TODO: [ben] replace with eris-db/event evsw := events.NewEventSwitch() evsw.Start() + logger = logging.WithScope(logger, "Core") // start a new application pipe that will load an application manager - pipe, err := manager.NewApplicationPipe(managerConfig, evsw, + pipe, err := manager.NewApplicationPipe(managerConfig, evsw, logger, consensusConfig.Version) if err != nil { return nil, fmt.Errorf("Failed to load application pipe: %v", err) } - log.Debug("Loaded pipe with application manager") + logging.TraceMsg(logger, "Loaded pipe with application manager") // pass the consensus engine into the pipe if e := consensus.LoadConsensusEngineInPipe(consensusConfig, pipe); e != nil { return nil, fmt.Errorf("Failed to load consensus engine in pipe: %v", e) } tendermintPipe, err := pipe.GetTendermintPipe() if err != nil { - log.Warn(fmt.Sprintf("Tendermint gateway not supported by %s", - managerConfig.Version)) - return &Core{ - chainId: chainId, - evsw: evsw, - pipe: pipe, - tendermintPipe: nil, - }, nil + logging.TraceMsg(logger, "Tendermint gateway not supported by manager", + "manager-version", managerConfig.Version) } return &Core{ chainId: chainId, diff --git a/core/types/types.go b/core/types/types.go index ecb6fc952..abe9fd0c9 100644 --- a/core/types/types.go +++ b/core/types/types.go @@ -1,23 +1,22 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT +// 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. -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +package types // TODO: [ben] this is poorly constructed but copied over // from eris-db/erisdb/pipe/types to make incremental changes and allow // for a discussion around the proper defintion of the needed types. -package types import ( // NodeInfo (drop this!) diff --git a/definitions/client_do.go b/definitions/client_do.go index 849977fa3..dc7c9c7e2 100644 --- a/definitions/client_do.go +++ b/definitions/client_do.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 definitions diff --git a/definitions/do.go b/definitions/do.go index f022aabbc..e89b69ffb 100644 --- a/definitions/do.go +++ b/definitions/do.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 definitions diff --git a/definitions/pipe.go b/definitions/pipe.go index f15c60df5..e00e46ed8 100644 --- a/definitions/pipe.go +++ b/definitions/pipe.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 definitions @@ -31,6 +29,7 @@ import ( core_types "github.com/eris-ltd/eris-db/core/types" types "github.com/eris-ltd/eris-db/core/types" event "github.com/eris-ltd/eris-db/event" + "github.com/eris-ltd/eris-db/logging/loggers" manager_types "github.com/eris-ltd/eris-db/manager/types" "github.com/eris-ltd/eris-db/txs" ) @@ -43,6 +42,7 @@ type Pipe interface { Transactor() Transactor // Hash of Genesis state GenesisHash() []byte + Logger() loggers.InfoTraceLogger // NOTE: [ben] added to Pipe interface on 0.12 refactor GetApplication() manager_types.Application SetConsensusEngine(consensusEngine consensus_types.ConsensusEngine) error diff --git a/definitions/tendermint_pipe.go b/definitions/tendermint_pipe.go index e933194d7..8fd5a9bb5 100644 --- a/definitions/tendermint_pipe.go +++ b/definitions/tendermint_pipe.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 definitions diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md deleted file mode 100644 index 173af5fb5..000000000 --- a/docs/CHANGELOG.md +++ /dev/null @@ -1,38 +0,0 @@ -# Eris-DB changelog - -## 0.12.0-RC3 -This release marks the start of Eris-DB as the full permissioned blockchain node - of the Eris platform with the Tendermint permissioned consensus engine. - This involved significant refactoring of almost all parts of the code, - but provides a solid foundation to build the next generation of advanced - permissioned smart contract blockchains. - - Many changes are under the hood but here are the main externally - visible changes: - -- Features and improvements - - Upgrade to Tendermint 0.6.0 in-process consensus - - Support DELEGATECALL opcode in Ethereum Virtual Machine (important for solidity library calls) - - ARM support - - Docker image size reduced - - Introduction of eris-client companion library for interacting with - eris:db - - Improved single configuration file for all components written by eris-cm - - Allow multiple event subscriptions from same host under rpc/tendermint - - -- Tool changes - - Use glide instead of godeps for dependencies - - -- Testing - - integration tests over simulated RPC calls - - significantly improved unit tests - - the ethereum virtual machine and the consensus engine are now top-level - components and are exposed to continuous integration tests - - -- Bugfixes (incomplete list) - - [EVM] Fix calculation of child CALL gaslimit (allowing solidity library calls to work properly) - - [RPC/v0] Fix blocking event subscription in transactAndHold (preventing return in Javascript libraries) - - [Blockchain] Fix getBlocks to respect block height cap diff --git a/docs/PROPOSALS.md b/docs/PROPOSALS.md new file mode 100644 index 000000000..35cf450a7 --- /dev/null +++ b/docs/PROPOSALS.md @@ -0,0 +1,17 @@ +# Eris-db proposals for future work + +The following list is extra-ordinarily uncommitted to and deliberately incomplete, but we thought it may be worthwhile sharing with you for soliciting your input and collaboration going forward: + +#### Security, Identity and Privacy + +- **Advanced security framework:** Functionality preceding Monax’s Nightsky (see multi-chain universe continuation below) to enable global encryption of transactions, genesis files and account storage at rest. +- **Identity Management and Whitelisting:** write protection is inherent to the permissioning scheme but read protection will be enabled by integrating certificates for restricting connectivity access to the peer-to-peer network for validators, verifiers or light clients. This is in addition to normal VPN and firewall solutions and a precursor of Monax’s Nightsky, which restricts read-access through selective decryption. +- **Multi-chain Universe (Step 2 of 3):** The second step towards enabling the multi-chain universe is well-underway with the Tendermint Cosmos proposal for interblockchain communication. +- **Multi-chain Universe (Step 3 of 3):** The third and final step is the Monax’s Nightsky proposal for inherent encryption of transactions and allowing only partial reconstruction of the smart contract application state for verifiers or light clients (for mobile or IoT devices). Together Cosmos and Nightsky allow smart contracts to orchestrate where information flows and who has the ability to decypher what part of the data. + +#### Scalability and Governance + +- **Read-cache Optimisation and Queryability:** subjectively pipe the application state and event logs to a data warehousing solution. Building upon the read-cache the SQLsol prototype can be integrated as an SQL-opinionated transformation layer of smart contract events and data into a relational database. +- **Chain life-cycle management:** the tooling is the starting point for chain life-cycle management and will be extended with exporting snapshots of chain state. These snapshots can be used to administratively start a new network with an updated version of eris-db software as cold upgrade. In a second phase the Cosmos proposal is ideally suited to run a new version of the node software in parallel to existing chain and the validators of the network can atomically cast their vote of approval through the Cosmos hub to dynamically move the voting power over to the updated chain (hot upgrade) until a full vote of confidence is achieved. It is important to note that chain life-cycle management is complementary to and will support smart contract life-cycle management, known to some as DOUG, our marmot mascot. +- **Sharding and scalability framework:** interblockchain communication through the exchange of proofs leverages the peer-to-peer network - rather than oracles - to distribute the load across parallel chains and removes oracle-bridges as potential bottlenecks and security risks. +- **Light-client and ABI integration:** a significant usability upgrade of smart contract chains will be achieved through the ability for the ABI definition of the deployed smart contracts to be verifiably linked to the smart contract accounts. The introduction of the EIP-141 will enable the injection of an ABI fingerprint into smart contract code such that a light client, eris-worker can dynamically expose the smart contract functionality through API calls. As an aside it is worth noting that the tendermint consensus protocol provides a distinctive advantage for light-clients as the last block signed by a majority of validators is unambiguously the current state; diff --git a/docs/generator.go b/docs/generator.go index fb430fc06..404b1593f 100644 --- a/docs/generator.go +++ b/docs/generator.go @@ -1,3 +1,17 @@ +// 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 main import ( @@ -7,10 +21,11 @@ import ( "strings" "text/template" - "github.com/eris-ltd/common/go/docs" commands "github.com/eris-ltd/eris-db/cmd" + docs "github.com/eris-ltd/eris-db/docs/generator" clientCommands "github.com/eris-ltd/eris-db/client/cmd" + "github.com/eris-ltd/eris-db/definitions" "github.com/eris-ltd/eris-db/version" "github.com/spf13/cobra" ) @@ -115,9 +130,9 @@ func AddClientToDB(dbCmd, clientCmd *cobra.Command) error { func main() { // Repository maintainers should populate the top level command object. erisDbCommand := commands.ErisDbCmd - commands.InitErisDbCli() - commands.AddCommands() - commands.AddGlobalFlags() + do := definitions.NewDo() + commands.AddGlobalFlags(do) + commands.AddCommands(do) erisClientCommand := clientCommands.ErisClientCmd clientCommands.InitErisClientInit() diff --git a/docs/generator/generator.go b/docs/generator/generator.go new file mode 100644 index 000000000..296704a48 --- /dev/null +++ b/docs/generator/generator.go @@ -0,0 +1,347 @@ +// 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 generator + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + "text/template" +) + +const FrontMatter = `--- + +layout: single +type: docs +title: "Documentation | {{ .Description }} | {{ $name }}" + +---` + +type Entry struct { + Title string + Template *template.Template + Specifications []*Entry + Examples []*Entry + Description string + FileName string + CmdEntryPoint string + URL string + BaseURL string +} + +func GenerateFileName(dir, s string) string { + return (dir + strings.Replace(strings.ToLower(s), " ", "_", -1) + ".md") +} + +func GenerateTitleFromFileName(file string) string { + file = strings.Replace(file, "_", " ", -1) + file = strings.Replace(file, "-", " ", -1) + return strings.Title(strings.Replace(file, ".md", "", 1)) +} + +func GenerateFileNameFromGlob(dir, s string) string { + return (dir + strings.Replace(filepath.Base(s), " ", "_", -1)) +} + +func GenerateURLFromFileName(s string) string { + s = strings.Replace(s, "./", "/", 1) + return strings.Replace(s, ".md", "/", -1) +} + +func GenerateCommandsTemplate() (*template.Template, error) { + handle_link := func(s string) string { + return (strings.Replace(s, ".md", "/", -1)) + } + + handle_file := func(s1, s2 string) string { + return strings.Replace((s1 + " " + s2 + ".md"), " ", "_", -1) + } + + funcMap := template.FuncMap{ + "title": strings.Title, + "replace": strings.Replace, + "chomp": strings.TrimSpace, + "handle_file": handle_file, + "handle_link": handle_link, + } + + var templateText = `{{- $name := .Command.CommandPath -}}` + FrontMatter + ` + +# {{ $name }} + +{{ title .Command.Short }} + +{{ if .Command.Runnable }}## Usage + +` + "```bash\n{{ .Command.UseLine }}\n```" + `{{ end }} + +{{ if ne .Command.Long "" }}## Synopsis + +{{ .Command.Long }} +{{ end }} +{{ $flags := .Command.NonInheritedFlags }} +{{ if $flags.HasFlags }}## Options + +` + "```bash\n {{ $flags.FlagUsages | chomp }}\n```" + `{{ end }} +{{ $global_flags := .Command.InheritedFlags }} +{{ if $global_flags.HasFlags }}## Options inherited from parent commands + +` + "```bash\n {{ $global_flags.FlagUsages | chomp }}\n```" + `{{ end }} + +{{ if .Command.HasSubCommands }}# Subcommands +{{ range .Command.Commands }} +{{ if ne .Deprecated "" }} +* [{{ $name }} {{ .Name }}]({{ .BaseURL }}{{ handle_file $name .Name | handle_link }}) - {{ .Short }} +{{ end }} +{{ end }} +{{ end }} + +{{ if .Command.HasParent }}{{ $parent := .Command.Parent }}## See Also +* [{{ $parent.CommandPath }}]({{ .BaseURL }}{{ handle_file $parent.CommandPath "" | handle_link }}) - {{ $parent.Short }} +{{ end }} + +{{ if ne .Command.Example "" }}# Quick Tips + +` + "```bash\n{{ .Command.Example }}\n```" + `{{ end }} + +{{ if ne (len .Entry.Examples) 0 }}# Examples +{{ range .Entry.Examples }} +* [{{ title .Title }}]({{ .URL }}) +{{- end }} +{{ end }} + +{{ if ne (len .Entry.Specifications) 0 }}# Specifications +{{ range .Entry.Specifications }} +* [{{ title .Title }}]({{ .URL }}) +{{- end }} +{{ end }} +` + + return template.New("docGenerator").Funcs(funcMap).Parse(templateText) +} + +func GenerateEntries(dir, render_dir, description string) ([]*Entry, error) { + var entries []*Entry + + if _, err := os.Stat(render_dir); os.IsNotExist(err) { + err = os.MkdirAll(render_dir, 0755) + if err != nil { + panic(err) + } + } + + files := CollectEntries(dir) + + for _, file := range files { + this_entry, err := GenerateEntry(file, dir, render_dir, description) + if err != nil { + return nil, err + } else { + entries = append(entries, this_entry) + } + } + + return entries, nil +} + +func CollectEntries(dir string) []string { + var newFiles []string + + files, err := filepath.Glob(dir + "/*") + if err != nil { + panic(err) + } + + for _, file := range files { + f_info, err := os.Stat(file) + + if err != nil { + panic(err) + } + + if f_info.IsDir() { + newFiles = append(newFiles, CollectEntries(file)...) + } else { + if filepath.Ext(file) == ".md" { + newFiles = append(newFiles, file) + } + } + } + + return newFiles +} + +func GenerateEntry(file, dir, render_dir, description string) (*Entry, error) { + var err error + + this_entry := &Entry{ + FileName: GenerateFileNameFromGlob(render_dir, file), + Title: GenerateTitleFromFileName(filepath.Base(file)), + Description: description, + } + + this_entry.URL = GenerateURLFromFileName(this_entry.FileName) + + txt, err := ioutil.ReadFile(file) + if err != nil { + return nil, err + } + + // Get template from docs generator + this_entry.Template, err = GenerateEntriesTemplate(txt) + if err != nil { + return nil, err + } + + return this_entry, nil +} + +func GenerateEntriesTemplate(txt []byte) (*template.Template, error) { + handle_link := func(s string) string { + return (strings.Replace(s, ".md", "/", -1)) + } + + handle_file := func(s1 string) string { + return strings.Replace((s1 + ".md"), " ", "_", -1) + } + + insert_definition := func(file string, struc string) string { + txt, err := ioutil.ReadFile(filepath.Join("definitions", file)) + if err != nil { + panic(err) + } + finder := regexp.MustCompile(fmt.Sprintf(`(?ms:^type %s struct {.*?^})`, struc)) + return ("```go\n" + string(finder.Find(txt)) + "\n```") + } + + insert_bash_lines := func(file string, linesToRead string) string { + var lines []byte + var line []byte + var start int + var stop int + + fileInfo, err := os.Open(filepath.Join("docs", "tests", file)) + if err != nil { + panic(err) + } + defer fileInfo.Close() + + start, err = strconv.Atoi(strings.Split(linesToRead, "-")[0]) + if strings.Contains(linesToRead, "-") { + stop, err = strconv.Atoi(strings.Split(linesToRead, "-")[1]) + } else { + stop = start + } + if err != nil { + panic(err) + } + + r := bufio.NewReader(fileInfo) + for i := 1; ; i++ { + line, err = r.ReadBytes('\n') + if err != nil { + break + } + if i >= start && i <= stop { + lines = append(lines, line...) + } + } + if err != io.EOF { + panic(err) + } + + return ("```bash\n" + string(lines) + "```") + } + + insert_file := func(file string) string { + file = filepath.Join("docs", "tests", file) + ext := filepath.Ext(file) + switch ext { + case ".sol": + ext = ".javascript" + case ".yml": + ext = ".yaml" + } + + ext = strings.Replace(ext, ".", "", 1) + + txtB, err := ioutil.ReadFile(file) + if err != nil { + panic(err) + } + + txt := string(txtB) + if !strings.HasSuffix(txt, "\n") { + txt = txt + "\n" + } + + return ("```" + ext + "\n" + txt + "```") // TODO: add auto-curl text + } + + funcMap := template.FuncMap{ + "title": strings.Title, + "replace": strings.Replace, + "chomp": strings.TrimSpace, + "handle_file": handle_file, + "handle_link": handle_link, + "insert_definition": insert_definition, + "insert_bash_lines": insert_bash_lines, + "insert_file": insert_file, + } + + var templateText = `{{- $name := .Title -}}` + FrontMatter + ` + +` + string(txt) + ` + +## Commands + +* [{{ .CmdEntryPoint }}]({{ .BaseURL }}{{ handle_file .CmdEntryPoint | handle_link }}) + +{{ if ne (len .Examples) 0 }}# Examples +{{ range .Examples }} +* [{{ title .Title }}]({{ .URL }}) +{{- end }} +{{ end }} + +{{ if ne (len .Specifications) 0 }}# Specifications +{{ range .Specifications }} +* [{{ title .Title }}]({{ .URL }}) +{{- end }} +{{ end }} +` + + return template.New("entryGenerator").Funcs(funcMap).Parse(templateText) +} + +func RenderEntry(this_entry *Entry) error { + out_file, err := os.Create(this_entry.FileName) + if err != nil { + return err + } + defer out_file.Close() + + err = this_entry.Template.Execute(out_file, this_entry) + if err != nil { + return err + } + + return nil +} diff --git a/event/event_cache.go b/event/event_cache.go index 08db30116..474c892d0 100644 --- a/event/event_cache.go +++ b/event/event_cache.go @@ -1,10 +1,25 @@ +// 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 event import ( "fmt" - "github.com/eris-ltd/eris-db/txs" "sync" "time" + + "github.com/eris-ltd/eris-db/txs" ) var ( diff --git a/event/event_cache_test.go b/event/event_cache_test.go index 35ce6d7ed..573eed113 100644 --- a/event/event_cache_test.go +++ b/event/event_cache_test.go @@ -1,3 +1,17 @@ +// 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 event import ( @@ -13,7 +27,7 @@ import ( "github.com/stretchr/testify/assert" ) -var mockInterval = 10 * time.Millisecond +var mockInterval = 40 * time.Millisecond type mockSub struct { subId string diff --git a/event/events.go b/event/events.go index d5c37d8e7..5ada94a52 100644 --- a/event/events.go +++ b/event/events.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 event @@ -23,8 +21,9 @@ import ( "fmt" + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/loggers" "github.com/eris-ltd/eris-db/txs" - log "github.com/eris-ltd/eris-logger" go_events "github.com/tendermint/go-events" tm_types "github.com/tendermint/tendermint/types" ) @@ -43,8 +42,8 @@ type EventEmitter interface { Unsubscribe(subId string) error } -func NewEvents(eventSwitch *go_events.EventSwitch) *events { - return &events{eventSwitch} +func NewEvents(eventSwitch go_events.EventSwitch, logger loggers.InfoTraceLogger) *events { + return &events{eventSwitch: eventSwitch, logger: logging.WithScope(logger, "Events")} } // Provides an EventEmitter that wraps many underlying EventEmitters as a @@ -56,28 +55,29 @@ func Multiplex(events ...EventEmitter) *multiplexedEvents { // The events struct has methods for working with events. type events struct { - eventSwitch *go_events.EventSwitch + eventSwitch go_events.EventSwitch + logger loggers.InfoTraceLogger } // Subscribe to an event. -func (this *events) Subscribe(subId, event string, +func (evts *events) Subscribe(subId, event string, callback func(txs.EventData)) error { cb := func(evt go_events.EventData) { eventData, err := mapToOurEventData(evt) if err != nil { - log.WithError(err). - WithFields(log.Fields{"event": event}). - Error("Failed to map go-events EventData to our EventData") + logging.InfoMsg(evts.logger, "Failed to map go-events EventData to our EventData", + "error", err, + "event", event) } callback(eventData) } - this.eventSwitch.AddListenerForEvent(subId, event, cb) + evts.eventSwitch.AddListenerForEvent(subId, event, cb) return nil } // Un-subscribe from an event. -func (this *events) Unsubscribe(subId string) error { - this.eventSwitch.RemoveListener(subId) +func (evts *events) Unsubscribe(subId string) error { + evts.eventSwitch.RemoveListener(subId) return nil } diff --git a/event/events_test.go b/event/events_test.go index 51d185ecb..af6d95303 100644 --- a/event/events_test.go +++ b/event/events_test.go @@ -1,3 +1,17 @@ +// 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 event import ( diff --git a/event/filters.go b/event/filters.go index 948a2f012..ea04e989f 100644 --- a/event/filters.go +++ b/event/filters.go @@ -1,3 +1,17 @@ +// 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 event import ( diff --git a/event_new/event.go b/event_new/event.go deleted file mode 100644 index 9553b3921..000000000 --- a/event_new/event.go +++ /dev/null @@ -1,240 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// This code is ported over into Eris-RT from Go-Ethereum and further adapted, -// with many thanks to the Ethereum foundation and the Go-Ethereum team. - -// Package event implements an event multiplexer. -package event - -import ( - "errors" - "fmt" - "reflect" - "sync" - "time" -) - -// Event is a time-tagged notification pushed to subscribers. -type Event struct { - Time time.Time - Data interface{} -} - -// Subscription is implemented by event subscriptions. -type Subscription interface { - // Chan returns a channel that carries events. - // Implementations should return the same channel - // for any subsequent calls to Chan. - Chan() <-chan *Event - - // Unsubscribe stops delivery of events to a subscription. - // The event channel is closed. - // Unsubscribe can be called more than once. - Unsubscribe() -} - -// A TypeMux dispatches events to registered receivers. Receivers can be -// registered to handle events of certain type. Any operation -// called after mux is stopped will return ErrMuxClosed. -// -// The zero value is ready to use. -type TypeMux struct { - mutex sync.RWMutex - subm map[reflect.Type][]*muxsub - stopped bool -} - -// ErrMuxClosed is returned when Posting on a closed TypeMux. -var ErrMuxClosed = errors.New("event: mux closed") - -// Subscribe creates a subscription for events of the given types. The -// subscription's channel is closed when it is unsubscribed -// or the mux is closed. -func (mux *TypeMux) Subscribe(types ...interface{}) Subscription { - sub := newsub(mux) - mux.mutex.Lock() - defer mux.mutex.Unlock() - if mux.stopped { - // set the status to closed so that calling Unsubscribe after this - // call will short curuit - sub.closed = true - close(sub.postC) - } else { - if mux.subm == nil { - mux.subm = make(map[reflect.Type][]*muxsub) - } - for _, t := range types { - rtyp := reflect.TypeOf(t) - oldsubs := mux.subm[rtyp] - if find(oldsubs, sub) != -1 { - panic(fmt.Sprintf("event: duplicate type %s in Subscribe", rtyp)) - } - subs := make([]*muxsub, len(oldsubs)+1) - copy(subs, oldsubs) - subs[len(oldsubs)] = sub - mux.subm[rtyp] = subs - } - } - return sub -} - -// Post sends an event to all receivers registered for the given type. -// It returns ErrMuxClosed if the mux has been stopped. -func (mux *TypeMux) Post(ev interface{}) error { - event := &Event{ - Time: time.Now(), - Data: ev, - } - rtyp := reflect.TypeOf(ev) - mux.mutex.RLock() - if mux.stopped { - mux.mutex.RUnlock() - return ErrMuxClosed - } - subs := mux.subm[rtyp] - mux.mutex.RUnlock() - for _, sub := range subs { - sub.deliver(event) - } - return nil -} - -// Stop closes a mux. The mux can no longer be used. -// Future Post calls will fail with ErrMuxClosed. -// Stop blocks until all current deliveries have finished. -func (mux *TypeMux) Stop() { - mux.mutex.Lock() - for _, subs := range mux.subm { - for _, sub := range subs { - sub.closewait() - } - } - mux.subm = nil - mux.stopped = true - mux.mutex.Unlock() -} - -func (mux *TypeMux) del(s *muxsub) { - mux.mutex.Lock() - for typ, subs := range mux.subm { - if pos := find(subs, s); pos >= 0 { - if len(subs) == 1 { - delete(mux.subm, typ) - } else { - mux.subm[typ] = posdelete(subs, pos) - } - } - } - s.mux.mutex.Unlock() -} - -func find(slice []*muxsub, item *muxsub) int { - for i, v := range slice { - if v == item { - return i - } - } - return -1 -} - -func posdelete(slice []*muxsub, pos int) []*muxsub { - news := make([]*muxsub, len(slice)-1) - copy(news[:pos], slice[:pos]) - copy(news[pos:], slice[pos+1:]) - return news -} - -type muxsub struct { - mux *TypeMux - created time.Time - closeMu sync.Mutex - closing chan struct{} - closed bool - - // these two are the same channel. they are stored separately so - // postC can be set to nil without affecting the return value of - // Chan. - postMu sync.RWMutex - readC <-chan *Event - postC chan<- *Event -} - -func newsub(mux *TypeMux) *muxsub { - c := make(chan *Event) - return &muxsub{ - mux: mux, - created: time.Now(), - readC: c, - postC: c, - closing: make(chan struct{}), - } -} - -func (s *muxsub) Chan() <-chan *Event { - return s.readC -} - -func (s *muxsub) Unsubscribe() { - s.mux.del(s) - s.closewait() -} - -func (s *muxsub) closewait() { - s.closeMu.Lock() - defer s.closeMu.Unlock() - if s.closed { - return - } - close(s.closing) - s.closed = true - - s.postMu.Lock() - close(s.postC) - s.postC = nil - s.postMu.Unlock() -} - -func (s *muxsub) deliver(event *Event) { - // Short circuit delivery if stale event - if s.created.After(event.Time) { - return - } - // Otherwise deliver the event - s.postMu.RLock() - defer s.postMu.RUnlock() - - select { - case s.postC <- event: - case <-s.closing: - } -} diff --git a/event_new/event_test.go b/event_new/event_test.go deleted file mode 100644 index fcdb0b1cd..000000000 --- a/event_new/event_test.go +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// This code is ported over into Eris-RT from Go-Ethereum and further adapted, -// with many thanks to the Ethereum foundation and the Go-Ethereum team. - -package event - -import ( - "math/rand" - "sync" - "testing" - "time" -) - -type testEvent int - -func TestSubCloseUnsub(t *testing.T) { - // the point of this test is **not** to panic - var mux TypeMux - mux.Stop() - sub := mux.Subscribe(int(0)) - sub.Unsubscribe() -} - -func TestSub(t *testing.T) { - mux := new(TypeMux) - defer mux.Stop() - - sub := mux.Subscribe(testEvent(0)) - go func() { - if err := mux.Post(testEvent(5)); err != nil { - t.Errorf("Post returned unexpected error: %v", err) - } - }() - ev := <-sub.Chan() - - if ev.Data.(testEvent) != testEvent(5) { - t.Errorf("Got %v (%T), expected event %v (%T)", - ev, ev, testEvent(5), testEvent(5)) - } -} - -func TestMuxErrorAfterStop(t *testing.T) { - mux := new(TypeMux) - mux.Stop() - - sub := mux.Subscribe(testEvent(0)) - if _, isopen := <-sub.Chan(); isopen { - t.Errorf("subscription channel was not closed") - } - if err := mux.Post(testEvent(0)); err != ErrMuxClosed { - t.Errorf("Post error mismatch, got: %s, expected: %s", err, ErrMuxClosed) - } -} - -func TestUnsubscribeUnblockPost(t *testing.T) { - mux := new(TypeMux) - defer mux.Stop() - - sub := mux.Subscribe(testEvent(0)) - unblocked := make(chan bool) - go func() { - mux.Post(testEvent(5)) - unblocked <- true - }() - - select { - case <-unblocked: - t.Errorf("Post returned before Unsubscribe") - default: - sub.Unsubscribe() - <-unblocked - } -} - -func TestSubscribeDuplicateType(t *testing.T) { - mux := new(TypeMux) - expected := "event: duplicate type event.testEvent in Subscribe" - - defer func() { - err := recover() - if err == nil { - t.Errorf("Subscribe didn't panic for duplicate type") - } else if err != expected { - t.Errorf("panic mismatch: got %#v, expected %#v", err, expected) - } - }() - mux.Subscribe(testEvent(1), testEvent(2)) -} - -func TestMuxConcurrent(t *testing.T) { - rand.Seed(time.Now().Unix()) - mux := new(TypeMux) - defer mux.Stop() - - recv := make(chan int) - poster := func() { - for { - err := mux.Post(testEvent(0)) - if err != nil { - return - } - } - } - sub := func(i int) { - time.Sleep(time.Duration(rand.Intn(99)) * time.Millisecond) - sub := mux.Subscribe(testEvent(0)) - <-sub.Chan() - sub.Unsubscribe() - recv <- i - } - - go poster() - go poster() - go poster() - nsubs := 1000 - for i := 0; i < nsubs; i++ { - go sub(i) - } - - // wait until everyone has been served - counts := make(map[int]int, nsubs) - for i := 0; i < nsubs; i++ { - counts[<-recv]++ - } - for i, count := range counts { - if count != 1 { - t.Errorf("receiver %d called %d times, expected only 1 call", i, count) - } - } -} - -func emptySubscriber(mux *TypeMux, types ...interface{}) { - s := mux.Subscribe(testEvent(0)) - go func() { - for _ = range s.Chan() { - } - }() -} - -func BenchmarkPost3(b *testing.B) { - var mux = new(TypeMux) - defer mux.Stop() - emptySubscriber(mux, testEvent(0)) - emptySubscriber(mux, testEvent(0)) - emptySubscriber(mux, testEvent(0)) - - for i := 0; i < b.N; i++ { - mux.Post(testEvent(0)) - } -} - -func BenchmarkPostConcurrent(b *testing.B) { - var mux = new(TypeMux) - defer mux.Stop() - emptySubscriber(mux, testEvent(0)) - emptySubscriber(mux, testEvent(0)) - emptySubscriber(mux, testEvent(0)) - - var wg sync.WaitGroup - poster := func() { - for i := 0; i < b.N; i++ { - mux.Post(testEvent(0)) - } - wg.Done() - } - wg.Add(5) - for i := 0; i < 5; i++ { - go poster() - } - wg.Wait() -} - -// for comparison -func BenchmarkChanSend(b *testing.B) { - c := make(chan interface{}) - closed := make(chan struct{}) - go func() { - for _ = range c { - } - }() - - for i := 0; i < b.N; i++ { - select { - case c <- i: - case <-closed: - } - } -} diff --git a/event_new/example_test.go b/event_new/example_test.go deleted file mode 100644 index 439589021..000000000 --- a/event_new/example_test.go +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// This code is ported over into Eris-RT from Go-Ethereum and further adapted, -// with many thanks to the Ethereum foundation and the Go-Ethereum team. - -package event - -import "fmt" - -func ExampleTypeMux() { - type someEvent struct{ I int } - type otherEvent struct{ S string } - type yetAnotherEvent struct{ X, Y int } - - var mux TypeMux - - // Start a subscriber. - done := make(chan struct{}) - sub := mux.Subscribe(someEvent{}, otherEvent{}) - go func() { - for event := range sub.Chan() { - fmt.Printf("Received: %#v\n", event.Data) - } - fmt.Println("done") - close(done) - }() - - // Post some events. - mux.Post(someEvent{5}) - mux.Post(yetAnotherEvent{X: 3, Y: 4}) - mux.Post(someEvent{6}) - mux.Post(otherEvent{"whoa"}) - - // Stop closes all subscription channels. - // The subscriber goroutine will print "done" - // and exit. - mux.Stop() - - // Wait for subscriber to return. - <-done - - // Output: - // Received: event.someEvent{I:5} - // Received: event.someEvent{I:6} - // Received: event.otherEvent{S:"whoa"} - // done -} diff --git a/event_new/filter/filter.go b/event_new/filter/filter.go deleted file mode 100644 index 9742057dd..000000000 --- a/event_new/filter/filter.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// This code is ported over into Eris-RT from Go-Ethereum and further adapted, -// with many thanks to the Ethereum foundation and the Go-Ethereum team. - -// Package filter implements event filters. -package filter - -import "reflect" - -type Filter interface { - Compare(Filter) bool - Trigger(data interface{}) -} - -type FilterEvent struct { - filter Filter - data interface{} -} - -type Filters struct { - id int - watchers map[int]Filter - ch chan FilterEvent - - quit chan struct{} -} - -func New() *Filters { - return &Filters{ - ch: make(chan FilterEvent), - watchers: make(map[int]Filter), - quit: make(chan struct{}), - } -} - -func (self *Filters) Start() { - go self.loop() -} - -func (self *Filters) Stop() { - close(self.quit) -} - -func (self *Filters) Notify(filter Filter, data interface{}) { - self.ch <- FilterEvent{filter, data} -} - -func (self *Filters) Install(watcher Filter) int { - self.watchers[self.id] = watcher - self.id++ - - return self.id - 1 -} - -func (self *Filters) Uninstall(id int) { - delete(self.watchers, id) -} - -func (self *Filters) loop() { -out: - for { - select { - case <-self.quit: - break out - case event := <-self.ch: - for _, watcher := range self.watchers { - if reflect.TypeOf(watcher) == reflect.TypeOf(event.filter) { - if watcher.Compare(event.filter) { - watcher.Trigger(event.data) - } - } - } - } - } -} - -func (self *Filters) Match(a, b Filter) bool { - return reflect.TypeOf(a) == reflect.TypeOf(b) && a.Compare(b) -} - -func (self *Filters) Get(i int) Filter { - return self.watchers[i] -} diff --git a/event_new/filter/filter_test.go b/event_new/filter/filter_test.go deleted file mode 100644 index c70d1ecce..000000000 --- a/event_new/filter/filter_test.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// This code is ported over into Eris-RT from Go-Ethereum and further adapted, -// with many thanks to the Ethereum foundation and the Go-Ethereum team. - -package filter - -import ( - "testing" - "time" -) - -// Simple test to check if baseline matching/mismatching filtering works. -func TestFilters(t *testing.T) { - fm := New() - fm.Start() - - // Register two filters to catch posted data - first := make(chan struct{}) - fm.Install(Generic{ - Str1: "hello", - Fn: func(data interface{}) { - first <- struct{}{} - }, - }) - second := make(chan struct{}) - fm.Install(Generic{ - Str1: "hello1", - Str2: "hello", - Fn: func(data interface{}) { - second <- struct{}{} - }, - }) - // Post an event that should only match the first filter - fm.Notify(Generic{Str1: "hello"}, true) - fm.Stop() - - // Ensure only the mathcing filters fire - select { - case <-first: - case <-time.After(100 * time.Millisecond): - t.Error("matching filter timed out") - } - select { - case <-second: - t.Error("mismatching filter fired") - case <-time.After(100 * time.Millisecond): - } -} diff --git a/event_new/filter/generic_filter.go b/event_new/filter/generic_filter.go deleted file mode 100644 index 6b475b972..000000000 --- a/event_new/filter/generic_filter.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// This code is ported over into Eris-RT from Go-Ethereum and further adapted, -// with many thanks to the Ethereum foundation and the Go-Ethereum team. - -package filter - -type Generic struct { - Str1, Str2, Str3 string - Data map[string]struct{} - - Fn func(data interface{}) -} - -// self = registered, f = incoming -func (self Generic) Compare(f Filter) bool { - var strMatch, dataMatch = true, true - - filter := f.(Generic) - if (len(self.Str1) > 0 && filter.Str1 != self.Str1) || - (len(self.Str2) > 0 && filter.Str2 != self.Str2) || - (len(self.Str3) > 0 && filter.Str3 != self.Str3) { - strMatch = false - } - - for k, _ := range self.Data { - if _, ok := filter.Data[k]; !ok { - return false - } - } - - return strMatch && dataMatch -} - -func (self Generic) Trigger(data interface{}) { - self.Fn(data) -} diff --git a/files/files.go b/files/files.go index aa9e59c83..a11c8a62f 100644 --- a/files/files.go +++ b/files/files.go @@ -1,4 +1,18 @@ // Cross-platform file utils. +// 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 files import ( diff --git a/files/files_test.go b/files/files_test.go index 16943df7f..1cf51762f 100644 --- a/files/files_test.go +++ b/files/files_test.go @@ -1,3 +1,17 @@ +// 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 files import ( diff --git a/files/log.go b/files/log.go index a18f6b6a6..3914c6c7b 100644 --- a/files/log.go +++ b/files/log.go @@ -1,3 +1,17 @@ +// 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 files import ( diff --git a/files/posix.go b/files/posix.go index e3dcb079d..037f40dd7 100644 --- a/files/posix.go +++ b/files/posix.go @@ -1,5 +1,19 @@ // +build !windows +// 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 files import "os" diff --git a/files/windows.go b/files/windows.go index 495dd3038..27a7fd84a 100644 --- a/files/windows.go +++ b/files/windows.go @@ -1,5 +1,19 @@ // +build windows +// 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 files import ( diff --git a/genesis/gen_test.go b/genesis/gen_test.go new file mode 100644 index 000000000..3a55eea7d --- /dev/null +++ b/genesis/gen_test.go @@ -0,0 +1,178 @@ +// 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 genesis + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "testing" + "time" +) + +// set the chain ID +var chainID string = "genesis-file-maker-test" + +var genesisFileExpected string = `{ + "genesis_time": "0001-01-01T00:00:00.000Z", + "chain_id": "genesis-file-maker-test", + "params": null, + "accounts": [ + { + "address": "74417C1BEFB3938B71B22B202050A4C6591FFCF6", + "amount": 9999999999, + "name": "genesis-file-maker-test_developer_000", + "permissions": { + "base": { + "perms": 14430, + "set": 16383 + }, + "roles": [] + } + }, + { + "address": "0C9DAEA4046491A661FCE0B41B0CAA2AD3415268", + "amount": 99999999999999, + "name": "genesis-file-maker-test_full_000", + "permissions": { + "base": { + "perms": 16383, + "set": 16383 + }, + "roles": [] + } + }, + { + "address": "E1BD50A1B90A15861F5CF0F182D291F556B21A86", + "amount": 9999999999, + "name": "genesis-file-maker-test_participant_000", + "permissions": { + "base": { + "perms": 2118, + "set": 16383 + }, + "roles": [] + } + }, + { + "address": "A6C8E2DE652DB8ADB4036293DC21F8FE389D77C2", + "amount": 9999999999, + "name": "genesis-file-maker-test_root_000", + "permissions": { + "base": { + "perms": 16383, + "set": 16383 + }, + "roles": [] + } + }, + { + "address": "E96CB7910001320B6F1E2266A8431D5E98FF0183", + "amount": 9999999999, + "name": "genesis-file-maker-test_validator_000", + "permissions": { + "base": { + "perms": 32, + "set": 16383 + }, + "roles": [] + } + } + ], + "validators": [ + { + "pub_key": [ + 1, + "238E1A77CC7CDCD13F4D77841F1FE4A46A77DB691EC89718CD0D4CB3409F61D2" + ], + "amount": 9999999999, + "name": "genesis-file-maker-test_full_000", + "unbond_to": [ + { + "address": "0C9DAEA4046491A661FCE0B41B0CAA2AD3415268", + "amount": 9999999999 + } + ] + }, + { + "pub_key": [ + 1, + "7F53D78C526F96C87ACBD0D2B9DB2E9FC176981623D26B1DB1CF59748EE9F4CF" + ], + "amount": 9999999998, + "name": "genesis-file-maker-test_validator_000", + "unbond_to": [ + { + "address": "E96CB7910001320B6F1E2266A8431D5E98FF0183", + "amount": 9999999998 + } + ] + } + ] +}` + +var accountsCSV string = `F0BD5CE45D306D61C9AB73CE5268C2B59D52CAF7127EF0E3B65523302254350A,9999999999,genesis-file-maker-test_developer_000,14430,16383 +238E1A77CC7CDCD13F4D77841F1FE4A46A77DB691EC89718CD0D4CB3409F61D2,99999999999999,genesis-file-maker-test_full_000,16383,16383 +E37A655E560D53721C9BB06BA742398323504DFE2EB2C67E71F8D16E71E0471B,9999999999,genesis-file-maker-test_participant_000,2118,16383 +EC0E38CC8308EC9E720EE839242A7BC5C781D1F852E962FAC5A8E0599CE5B224,9999999999,genesis-file-maker-test_root_000,16383,16383 +7F53D78C526F96C87ACBD0D2B9DB2E9FC176981623D26B1DB1CF59748EE9F4CF,9999999999,genesis-file-maker-test_validator_000,32,16383` + +var validatorsCSV string = `238E1A77CC7CDCD13F4D77841F1FE4A46A77DB691EC89718CD0D4CB3409F61D2,9999999999,genesis-file-maker-test_full_000,16383,16383 +7F53D78C526F96C87ACBD0D2B9DB2E9FC176981623D26B1DB1CF59748EE9F4CF,9999999998,genesis-file-maker-test_validator_000,32,16383` + +func TestKnownCSV(t *testing.T) { + // make temp dir + dir, err := ioutil.TempDir(os.TempDir(), "genesis-file-maker-test") + if err != nil { + t.Fatal(err) + } + + defer func() { + //cleanup + os.RemoveAll(dir) + if err != nil { + t.Fatal(err) + } + + }() + + // set the filepaths to be written to + accountsCSVpath := filepath.Join(dir, "accounts.csv") + validatorsCSVpath := filepath.Join(dir, "validators.csv") + + // write the accounts.csv + if err := ioutil.WriteFile(accountsCSVpath, []byte(accountsCSV), 0600); err != nil { + t.Fatal(err) + } + + // write the validators.csv + if err := ioutil.WriteFile(validatorsCSVpath, []byte(validatorsCSV), 0600); err != nil { + t.Fatal(err) + } + + // create the genesis file + // NOTE: [ben] set time to zero time, "genesis_time": "0001-01-01T00:00:00.000Z" + genesisFileWritten, err := generateKnownWithTime(chainID, accountsCSVpath, validatorsCSVpath, time.Time{}) + if err != nil { + t.Fatal(err) + } + + // compare + if !bytes.Equal([]byte(genesisFileExpected), []byte(genesisFileWritten)) { + t.Fatalf("Bad genesis file: got (%s), expected (%s)", genesisFileWritten, genesisFileExpected) + } + +} diff --git a/genesis/genesis.go b/genesis/genesis.go new file mode 100644 index 000000000..3ca287e55 --- /dev/null +++ b/genesis/genesis.go @@ -0,0 +1,84 @@ +// 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 genesis + +import ( + "bytes" + "encoding/json" + "time" + + ptypes "github.com/eris-ltd/eris-db/permission/types" + wire "github.com/tendermint/go-wire" +) + +// MakeGenesisDocFromAccounts takes a chainName and a slice of pointers to GenesisAccount, +// and a slice of pointers to GenesisValidator to construct a GenesisDoc, or returns an error on +// failure. In particular MakeGenesisDocFromAccount uses the local time as a +// timestamp for the GenesisDoc. +func MakeGenesisDocFromAccounts(chainName string, accounts []*GenesisAccount, + validators []*GenesisValidator) (GenesisDoc, error) { + + // TODO: assert valid accounts and validators + // TODO: [ben] expose setting global permissions + globalPermissions := ptypes.DefaultAccountPermissions.Clone() + genesisParameters := &GenesisParams{ + GlobalPermissions: &globalPermissions, + } + // copy slice of pointers to accounts into slice of accounts + accountsCopy := make([]GenesisAccount, len(accounts)) + for i, genesisAccount := range accounts { + accountsCopy[i] = genesisAccount.Clone() + } + // copy slice of pointers to validators into slice of validators + validatorsCopy := make([]GenesisValidator, len(validators)) + for i, genesisValidator := range validators { + genesisValidatorCopy, err := genesisValidator.Clone() + if err != nil { + return GenesisDoc{}, err + } + validatorsCopy[i] = genesisValidatorCopy + } + genesisDoc := GenesisDoc{ + GenesisTime: time.Now(), + // TODO: this needs to be corrected for ChainName, and ChainId + // is the derived hash from the GenesisDoc serialised bytes + ChainID: chainName, + Params: genesisParameters, + Accounts: accountsCopy, + Validators: validatorsCopy, + } + return genesisDoc, nil +} + +// GetGenesisFileBytes returns the JSON (not-yet) canonical bytes for a given +// GenesisDoc or an error. In a first rewrite, rely on go-wire +// for the JSON serialisation with type-bytes. +func GetGenesisFileBytes(genesisDoc *GenesisDoc) ([]byte, error) { + + // TODO: write JSON in canonical order + var err error + buffer, n := new(bytes.Buffer), new(int) + // write JSON with go-wire type-bytes (for public keys) + wire.WriteJSON(genesisDoc, buffer, n, &err) + if err != nil { + return nil, err + } + // rewrite buffer with indentation + indentedBuffer := new(bytes.Buffer) + if err := json.Indent(indentedBuffer, buffer.Bytes(), "", "\t"); err != nil { + return nil, err + } + return indentedBuffer.Bytes(), nil +} diff --git a/genesis/make_genesis_file.go b/genesis/make_genesis_file.go new file mode 100644 index 000000000..a4dce67bf --- /dev/null +++ b/genesis/make_genesis_file.go @@ -0,0 +1,249 @@ +// 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 genesis + +import ( + "bytes" + "encoding/csv" + "encoding/hex" + "encoding/json" + "fmt" + "os" + "strconv" + "time" + + ptypes "github.com/eris-ltd/eris-db/permission/types" + "github.com/eris-ltd/eris-db/util" + + "github.com/tendermint/go-crypto" + wire "github.com/tendermint/go-wire" +) + +//------------------------------------------------------------------------------------ +// interface functions that are consumed by monax tooling +// TODO: [ben] these interfaces will be deprecated from v0.17 + +func GenerateKnown(chainID, accountsPathCSV, validatorsPathCSV string) (string, error) { + return generateKnownWithTime(chainID, accountsPathCSV, validatorsPathCSV, + // set the timestamp for the genesis + time.Now()) +} + +//------------------------------------------------------------------------------------ +// interface functions that are consumed by monax tooling + +func GenerateGenesisFileBytes(chainName string, genesisAccounts []*GenesisAccount, + genesisValidators []*GenesisValidator) ([]byte, error) { + genesisDoc, err := MakeGenesisDocFromAccounts(chainName, genesisAccounts, genesisValidators) + + buf, buf2, n := new(bytes.Buffer), new(bytes.Buffer), new(int) + wire.WriteJSON(genesisDoc, buf, n, &err) + if err != nil { + return nil, err + } + if err := json.Indent(buf2, buf.Bytes(), "", "\t"); err != nil { + return nil, err + } + + return buf2.Bytes(), nil +} + +//------------------------------------------------------------------------------------ +// core functions that provide functionality for monax tooling in v0.16 + +// GenerateKnownWithTime takes chainId, an accounts and validators CSV filepath +// and a timestamp to generate the string of `genesis.json` +// NOTE: [ben] is introduced as technical debt to preserve the signature +// of GenerateKnown but in order to introduce the timestamp gradually +// This will be deprecated in v0.17 +func generateKnownWithTime(chainID, accountsPathCSV, validatorsPathCSV string, + genesisTime time.Time) (string, error) { + var genDoc *GenesisDoc + + // TODO [eb] eliminate reading priv_val ... [zr] where? + if accountsPathCSV == "" || validatorsPathCSV == "" { + return "", fmt.Errorf("both accounts.csv and validators.csv is required") + } + + pubkeys, amts, names, perms, setbits, err := parseCsv(validatorsPathCSV) + if err != nil { + return "", err + } + + pubkeysA, amtsA, namesA, permsA, setbitsA, err := parseCsv(accountsPathCSV) + if err != nil { + return "", err + } + + genDoc = newGenDoc(chainID, genesisTime, len(pubkeys), len(pubkeysA)) + for i, pk := range pubkeys { + genDocAddValidator(genDoc, pk, amts[i], names[i], perms[i], setbits[i], i) + } + for i, pk := range pubkeysA { + genDocAddAccount(genDoc, pk, amtsA[i], namesA[i], permsA[i], setbitsA[i], i) + } + + buf, buf2, n := new(bytes.Buffer), new(bytes.Buffer), new(int) + wire.WriteJSON(genDoc, buf, n, &err) + if err != nil { + return "", err + } + if err := json.Indent(buf2, buf.Bytes(), "", "\t"); err != nil { + return "", err + } + + return buf2.String(), nil +} + +//----------------------------------------------------------------------------- +// gendoc convenience functions + +func newGenDoc(chainID string, genesisTime time.Time, nVal, nAcc int) *GenesisDoc { + genDoc := GenesisDoc{ + ChainID: chainID, + GenesisTime: genesisTime, + } + genDoc.Accounts = make([]GenesisAccount, nAcc) + genDoc.Validators = make([]GenesisValidator, nVal) + return &genDoc +} + +func genDocAddAccount(genDoc *GenesisDoc, pubKey crypto.PubKeyEd25519, amt int64, name string, perm, setbit ptypes.PermFlag, index int) { + addr := pubKey.Address() + acc := GenesisAccount{ + Address: addr, + Amount: amt, + Name: name, + Permissions: &ptypes.AccountPermissions{ + Base: ptypes.BasePermissions{ + Perms: perm, + SetBit: setbit, + }, + }, + } + if index < 0 { + genDoc.Accounts = append(genDoc.Accounts, acc) + } else { + genDoc.Accounts[index] = acc + } +} + +func genDocAddValidator(genDoc *GenesisDoc, pubKey crypto.PubKeyEd25519, amt int64, name string, perm, setbit ptypes.PermFlag, index int) { + addr := pubKey.Address() + genDoc.Validators[index] = GenesisValidator{ + PubKey: pubKey, + Amount: amt, + Name: name, + UnbondTo: []BasicAccount{ + { + Address: addr, + Amount: amt, + }, + }, + } + // [zr] why no index < 0 like in genDocAddAccount? +} + +//----------------------------------------------------------------------------- +// util functions + +// convert hex strings to ed25519 pubkeys +func pubKeyStringsToPubKeys(pubkeys []string) ([]crypto.PubKeyEd25519, error) { + pubKeys := make([]crypto.PubKeyEd25519, len(pubkeys)) + for i, k := range pubkeys { + pubBytes, err := hex.DecodeString(k) + if err != nil { + return pubKeys, err + } + copy(pubKeys[i][:], pubBytes) + } + return pubKeys, nil +} + +// empty is over written +func ifExistsElse(list []string, index int, defaultValue string) string { + if len(list) > index { + if list[index] != "" { + return list[index] + } + } + return defaultValue +} + +// takes a csv in the following format: pubkey, starting balance, name, permissions, setbit +func parseCsv(filePath string) (pubKeys []crypto.PubKeyEd25519, amts []int64, names []string, perms, setbits []ptypes.PermFlag, err error) { + + csvFile, err := os.Open(filePath) + if err != nil { + util.Fatalf("Couldn't open file: %s: %v", filePath, err) + } + defer csvFile.Close() + + r := csv.NewReader(csvFile) + //r.FieldsPerRecord = # of records expected + params, err := r.ReadAll() + if err != nil { + util.Fatalf("Couldn't read file: %v", err) + + } + + pubkeys := make([]string, len(params)) + amtS := make([]string, len(params)) + names = make([]string, len(params)) + permsS := make([]string, len(params)) + setbitS := make([]string, len(params)) + for i, each := range params { + pubkeys[i] = each[0] + amtS[i] = ifExistsElse(each, 1, "1000") + names[i] = ifExistsElse(each, 2, "") + permsS[i] = ifExistsElse(each, 3, fmt.Sprintf("%d", ptypes.DefaultPermFlags)) + setbitS[i] = ifExistsElse(each, 4, permsS[i]) + } + + //TODO convert int to uint64, see issue #25 + perms = make([]ptypes.PermFlag, len(permsS)) + for i, perm := range permsS { + pflag, err := strconv.Atoi(perm) + if err != nil { + util.Fatalf("Permissions (%v) must be an integer", perm) + } + perms[i] = ptypes.PermFlag(pflag) + } + setbits = make([]ptypes.PermFlag, len(setbitS)) + for i, setbit := range setbitS { + setbitsFlag, err := strconv.Atoi(setbit) + if err != nil { + util.Fatalf("SetBits (%v) must be an integer", setbit) + } + setbits[i] = ptypes.PermFlag(setbitsFlag) + } + + // convert amts to ints + amts = make([]int64, len(amtS)) + for i, a := range amtS { + if amts[i], err = strconv.ParseInt(a, 10, 64); err != nil { + err = fmt.Errorf("Invalid amount: %v", err) + return + } + } + + // convert pubkey hex strings to struct + pubKeys, err = pubKeyStringsToPubKeys(pubkeys) + if err != nil { + return + } + + return pubKeys, amts, names, perms, setbits, nil +} diff --git a/genesis/maker.go b/genesis/maker.go new file mode 100644 index 000000000..707d8150e --- /dev/null +++ b/genesis/maker.go @@ -0,0 +1,84 @@ +// 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 genesis + +import ( + "fmt" + + ptypes "github.com/eris-ltd/eris-db/permission/types" + + "github.com/tendermint/go-crypto" +) + +const ( + PublicKeyEd25519ByteLength int = 32 + PublicKeySecp256k1ByteLength int = 64 +) + +// NewGenesisAccount returns a new GenesisAccount +func NewGenesisAccount(address []byte, amount int64, name string, + permissions *ptypes.AccountPermissions) *GenesisAccount { + return &GenesisAccount{ + Address: address, + Amount: amount, + Name: name, + Permissions: permissions, + } +} + +func NewGenesisValidator(amount int64, name string, unbondToAddress []byte, + unbondAmount int64, keyType string, publicKeyBytes []byte) (*GenesisValidator, error) { + // convert the key bytes into a typed fixed size byte array + var typedPublicKeyBytes []byte + switch keyType { + case "ed25519": + // TODO: [ben] functionality and checks need to be inherit in the type + if len(publicKeyBytes) != PublicKeyEd25519ByteLength { + return nil, fmt.Errorf("Invalid length provided for ed25519 public key (len %v)", + len(publicKeyBytes)) + } + // ed25519 has type byte 0x01 + typedPublicKeyBytes = make([]byte, PublicKeyEd25519ByteLength+1) + // prepend type byte to public key + typedPublicKeyBytes = append([]byte{crypto.PubKeyTypeEd25519}, publicKeyBytes...) + case "secp256k1": + if len(publicKeyBytes) != PublicKeySecp256k1ByteLength { + return nil, fmt.Errorf("Invalid length provided for secp256k1 public key (len %v)", + len(publicKeyBytes)) + } + // secp256k1 has type byte 0x02 + typedPublicKeyBytes = make([]byte, PublicKeySecp256k1ByteLength+1) + // prepend type byte to public key + typedPublicKeyBytes = append([]byte{crypto.PubKeyTypeSecp256k1}, publicKeyBytes...) + default: + return nil, fmt.Errorf("Unsupported key type (%s)", keyType) + } + newPublicKey, err := crypto.PubKeyFromBytes(typedPublicKeyBytes) + if err != nil { + return nil, err + } + // ability to unbond to multiple accounts currently unused + var unbondTo []BasicAccount + + return &GenesisValidator{ + PubKey: newPublicKey, + Amount: unbondAmount, + Name: name, + UnbondTo: append(unbondTo, BasicAccount{ + Address: unbondToAddress, + Amount: unbondAmount, + }), + }, nil +} diff --git a/genesis/types.go b/genesis/types.go new file mode 100644 index 000000000..c962a3c31 --- /dev/null +++ b/genesis/types.go @@ -0,0 +1,157 @@ +// 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 genesis + +import ( + "fmt" + "os" + "time" + + ptypes "github.com/eris-ltd/eris-db/permission/types" + + "github.com/tendermint/go-crypto" + "github.com/tendermint/go-wire" +) + +//------------------------------------------------------------ +// we store the GenesisDoc in the db under this key + +var GenDocKey = []byte("GenDocKey") + +//------------------------------------------------------------ +// core types for a genesis definition + +type BasicAccount struct { + Address []byte `json:"address"` + Amount int64 `json:"amount"` +} + +type GenesisAccount struct { + Address []byte `json:"address"` + Amount int64 `json:"amount"` + Name string `json:"name"` + Permissions *ptypes.AccountPermissions `json:"permissions"` +} + +type GenesisValidator struct { + PubKey crypto.PubKey `json:"pub_key"` + Amount int64 `json:"amount"` + Name string `json:"name"` + UnbondTo []BasicAccount `json:"unbond_to"` +} + +// GenesisPrivateValidator marshals the state of the private +// validator for the purpose of Genesis creation; and hence +// is defined in genesis and not under consensus, where +// PrivateValidator (currently inherited from Tendermint) is. +type GenesisPrivateValidator struct { + Address string `json:"address"` + PubKey []interface{} `json:"pub_key"` + PrivKey []interface{} `json:"priv_key"` + LastHeight int64 `json:"last_height"` + LastRound int64 `json:"last_round"` + LastStep int64 `json:"last_step"` +} + +type GenesisParams struct { + GlobalPermissions *ptypes.AccountPermissions `json:"global_permissions"` +} + +//------------------------------------------------------------ +// GenesisDoc is stored in the state database + +type GenesisDoc struct { + GenesisTime time.Time `json:"genesis_time"` + ChainID string `json:"chain_id"` + Params *GenesisParams `json:"params"` + Accounts []GenesisAccount `json:"accounts"` + Validators []GenesisValidator `json:"validators"` +} + +//------------------------------------------------------------ +// Make genesis state from file + +func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) { + var err error + wire.ReadJSONPtr(&genState, jsonBlob, &err) + if err != nil { + fmt.Printf("Couldn't read GenesisDoc: %v", err) + // TODO: on error return error, not exit + os.Exit(1) + } + return +} + +//------------------------------------------------------------ +// Methods for genesis types +// NOTE: breaks formatting convention +// TODO: split each genesis type in its own file definition + +//------------------------------------------------------------ +// GenesisAccount methods + +// Clone clones the genesis account +func (genesisAccount *GenesisAccount) Clone() GenesisAccount { + // clone the address + addressClone := make([]byte, len(genesisAccount.Address)) + copy(addressClone, genesisAccount.Address) + // clone the account permissions + accountPermissionsClone := genesisAccount.Permissions.Clone() + return GenesisAccount{ + Address: addressClone, + Amount: genesisAccount.Amount, + Name: genesisAccount.Name, + Permissions: &accountPermissionsClone, + } +} + +//------------------------------------------------------------ +// GenesisValidator methods + +// Clone clones the genesis validator +func (genesisValidator *GenesisValidator) Clone() (GenesisValidator, error) { + if genesisValidator == nil { + return GenesisValidator{}, fmt.Errorf("Cannot clone nil GenesisValidator.") + } + if genesisValidator.PubKey == nil { + return GenesisValidator{}, fmt.Errorf("Invalid GenesisValidator %s with nil public key.", + genesisValidator.Name) + } + // clone the addresses to unbond to + unbondToClone := make([]BasicAccount, len(genesisValidator.UnbondTo)) + for i, basicAccount := range genesisValidator.UnbondTo { + unbondToClone[i] = basicAccount.Clone() + } + return GenesisValidator{ + PubKey: genesisValidator.PubKey, + Amount: genesisValidator.Amount, + Name: genesisValidator.Name, + UnbondTo: unbondToClone, + }, nil +} + +//------------------------------------------------------------ +// BasicAccount methods + +// Clone clones the basic account +func (basicAccount *BasicAccount) Clone() BasicAccount { + // clone the address + addressClone := make([]byte, len(basicAccount.Address)) + copy(addressClone, basicAccount.Address) + return BasicAccount{ + Address: addressClone, + Amount: basicAccount.Amount, + } +} diff --git a/glide.lock b/glide.lock index 76c7e0b2c..6887d752d 100644 --- a/glide.lock +++ b/glide.lock @@ -1,12 +1,12 @@ -hash: f1f85c5d4b9520217cc6fa9fd7b7e97790e737def9bc08ab45d53d5db729c779 -updated: 2016-09-14T20:54:48.289839938+02:00 +hash: 310aa7c7435ad7dd1c3eb6772a42065b5f506e38e195107bdbfb1584833add9a +updated: 2017-02-21T01:43:41.814044634Z imports: - name: github.com/Azure/go-ansiterm version: 388960b655244e76e24c75f48631564eaefade62 subpackages: - winterm - name: github.com/btcsuite/btcd - version: 7de7bddba9d9a8b0b7490931e346e8f10d0bdb7f + version: 153dca5c1e4b5d1ea1523592495e5bedfa503391 subpackages: - btcec - name: github.com/btcsuite/fastsha256 @@ -20,18 +20,17 @@ imports: - name: github.com/bugsnag/panicwrap version: d6191e27ad06236eaad65d79e49a08b03b9f8029 - name: github.com/BurntSushi/toml - version: f0aeabca5a127c4078abb8c8d64298b147264b55 + version: 99064174e013895bbd9b025c31100bd1d9b590ca - name: github.com/davecgh/go-spew version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d -- name: github.com/eris-ltd/common - version: 8ca15f5455104403db4202c995e2f6e161654c02 - subpackages: - - go/docs - - go/common +- name: github.com/eapache/channels + version: 47238d5aae8c0fefd518ef2bee46290909cf8263 +- name: github.com/eapache/queue + version: 44cc805cf13205b55f69e14bcb69867d1ae92f98 +- name: github.com/ebuchman/fail-test + version: c1eddaa09da2b4017351245b0d43234955276798 - name: github.com/eris-ltd/eris-keys version: 114ebc77443db9a153692233294e48bc7e184215 -- name: github.com/eris-ltd/eris-logger - version: ea48a395d6ecc0eccc67a26da9fc7a6106fabb84 - name: github.com/fsnotify/fsnotify version: 30411dbcefb7a1da7e84f75530ad3abe4011b4f8 - name: github.com/gin-gonic/gin @@ -39,37 +38,53 @@ imports: subpackages: - binding - render +- name: github.com/go-kit/kit + version: f66b0e13579bfc5a48b9e2a94b1209c107ea1f41 + subpackages: + - log +- name: github.com/go-logfmt/logfmt + version: 390ab7935ee28ec6b286364bba9b4dd6410cb3d5 - name: github.com/go-stack/stack version: 100eb0c0a9c5b306ca2fb4f165df21d80ada4b82 +- name: github.com/gogo/protobuf + version: f9114dace7bd920b32f943b3c73fafbcbab2bf31 - name: github.com/golang/protobuf - version: 0c1f6d65b5a189c2250d10e71a5506f06f9fa0a0 + version: 8ee79997227bf9b34611aee7946ae64735e6fd93 subpackages: - proto - name: github.com/golang/snappy version: d9eb7a3d35ec988b8585d4a0068e462c27d28380 - name: github.com/gorilla/websocket - version: a68708917c6a4f06314ab4e52493cc61359c9d42 + version: 17634340a83afe0cab595e40fbc63f6ffa1d8915 - name: github.com/hashicorp/hcl version: da486364306ed66c218be9b7953e19173447c18b subpackages: - hcl/ast - hcl/parser - - hcl/token - - json/parser - hcl/scanner - hcl/strconv + - hcl/token + - json/parser - json/scanner - json/token +- name: github.com/inconshreveable/log15 + version: 46a701a619de90c65a78c04d1a58bf02585e9701 + subpackages: + - term - name: github.com/inconshreveable/mousetrap version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75 +- name: github.com/jmhodges/levigo + version: c42d9e0ca023e2198120196f842701bb4c55d7b9 - name: github.com/magiconair/properties version: c265cfa48dda6474e208715ca93e987829f572f8 - name: github.com/manucorporat/sse version: ee05b128a739a0fb76c7ebd3ae4810c1de808d6d +- name: github.com/Masterminds/glide + version: 84607742b10f492430762d038e954236bbaf23f7 - name: github.com/mattn/go-colorable - version: 9056b7a9f2d1f2d96498d6d146acd1f9d5ed3d59 + version: d228849504861217f796da67fae4f6e347643f15 - name: github.com/mattn/go-isatty - version: 56b76bdf51f7708750eac80fa38b952bb9f32639 + version: 30a891c33c7cde7b02a981314b4228ec99380cca - name: github.com/mitchellh/mapstructure version: d2dd0262208475919e1a362f675cfc0e7c10e905 - name: github.com/naoina/toml @@ -79,7 +94,7 @@ imports: subpackages: - difflib - name: github.com/Sirupsen/logrus - version: f3cfb454f4c209e6668c95216c4744b8fddb2356 + version: d26492970760ca5d33129d2d799e34be5c4782eb - name: github.com/spf13/cast version: 27b586b42e29bec072fe7379259cc719e1289da6 - name: github.com/spf13/cobra @@ -87,117 +102,133 @@ imports: - name: github.com/spf13/jwalterweatherman version: 33c24e77fb80341fe7130ee7c594256ff08ccc46 - name: github.com/spf13/pflag - version: 367864438f1b1a3c7db4da06a2f55b144e6784e0 + version: 25f8b5b07aece3207895bf19f7ab517eb3b22a40 - name: github.com/spf13/viper version: c1ccc378a054ea8d4e38d8c67f6938d4760b53dd +- name: github.com/streadway/simpleuuid + version: 6617b501e485b77e61b98cd533aefff9e258b5a7 - name: github.com/stretchr/testify version: d77da356e56a7428ad25149ca77381849a6a5232 subpackages: - assert - name: github.com/syndtr/goleveldb - version: fa5b5c78794bc5c18f330361059f871ae8c2b9d6 + version: 23851d93a2292dcc56e71a18ec9e0624d84a0f65 subpackages: - leveldb - - leveldb/errors - - leveldb/opt - leveldb/cache - leveldb/comparer + - leveldb/errors - leveldb/filter - leveldb/iterator - leveldb/journal - leveldb/memdb + - leveldb/opt - leveldb/storage - leveldb/table - leveldb/util +- name: github.com/tendermint/abci + version: 699d45bc678865b004b90213bf88a950f420973b + subpackages: + - client + - example/counter + - example/dummy + - example/nil + - server + - types - name: github.com/tendermint/ed25519 - version: fdac6641497281ed1cc368687ec6377e96e02b24 + version: 1f52c6f8b8a5c7908aff4497c186af344b428925 subpackages: - edwards25519 - extra25519 - name: github.com/tendermint/flowcontrol version: 84d9671090430e8ec80e35b339907e0579b999eb +- name: github.com/tendermint/go-autofile + version: 0416e0aa9c68205aa44844096f9f151ada9d0405 - name: github.com/tendermint/go-clist - version: 634527f5b60fd7c71ca811262493df2ad65ee0ca + version: 3baa390bbaf7634251c42ad69a8682e7e3990552 - name: github.com/tendermint/go-common - version: dcfa46af1341d03b80d32e4901019d1668b978b9 + version: e289af53b6bf6af28da129d9ef64389a4cf7987f + subpackages: + - test - name: github.com/tendermint/go-config - version: cfcef384d64b94e50909596e39b32ffb3cc20573 + version: e64b424499acd0eb9856b88e10c0dff41628c0d6 - name: github.com/tendermint/go-crypto - version: 41cfb7b677f4e16cdfd22b6ce0946c89919fbc7b + version: 4b11d62bdb324027ea01554e5767b71174680ba0 - name: github.com/tendermint/go-db - version: 31fdd21c7eaeed53e0ea7ca597fb1e960e2988a5 + version: 72f6dacd22a686cdf7fcd60286503e3aceda77ba - name: github.com/tendermint/go-events - version: 7b75ca7bb55aa25e9ef765eb8c0b69486b227357 + version: fddee66d90305fccb6f6d84d16c34fa65ea5b7f6 +- name: github.com/tendermint/go-flowrate + version: a20c98e61957faa93b4014fbd902f20ab9317a6a + subpackages: + - flowrate - name: github.com/tendermint/go-logger - version: 529efe50eab1a8a9c111d55f4de4ecd95f482761 + version: cefb3a45c0bf3c493a04e9bcd9b1540528be59f2 - name: github.com/tendermint/go-merkle - version: 05042c6ab9cad51d12e4cecf717ae68e3b1409a8 + version: 7a86b4486f2cd84ac885c5bbc609fdee2905f5d1 - name: github.com/tendermint/go-p2p - version: 5bd7692323ec60d6461678f09b5024a952164151 + version: 3d98f675f30dc4796546b8b890f895926152fa8d subpackages: - upnp - name: github.com/tendermint/go-rpc - version: 479510be0e80dd9e5d6b1f941adad168df0af85f + version: fcea0cda21f64889be00a0f4b6d13266b1a76ee7 subpackages: - client - server - types - name: github.com/tendermint/go-wire - version: 3b0adbc86ed8425eaed98516165b6788d9f4de7a + version: 2f3b7aafe21c80b19b6ee3210ecb3e3d07c7a471 - name: github.com/tendermint/log15 - version: 9545b249b3aacafa97f79e0838b02b274adc6f5f + version: ae0f3d6450da9eac7074b439c8e1c3cabf0d5ce6 subpackages: - term - name: github.com/tendermint/tendermint - version: aaea0c5d2e3ecfbf29f2608f9d43649ec7f07f50 + version: 764091dfbb035f1b28da4b067526e04c6a849966 subpackages: - - node - - proxy - - types - - version - - consensus - - rpc/core/types - blockchain + - consensus - mempool + - node + - proxy - rpc/core + - rpc/core/types - state -- name: github.com/tendermint/tmsp - version: 73e5c3cb7bbee2f9c49792e5a0fcbcab442bf7dc - subpackages: - - client - types - - example/dummy - - example/nil + - version - name: github.com/tommy351/gin-cors version: dc91dec6313ae4db53481bf3b29cf6b94bf80357 - name: golang.org/x/crypto - version: f3241ce8505855877cc8a9717bd61a0f7c4ea83c + version: 7c6cc321c680f03b9ef0764448e780704f486b51 subpackages: - - ripemd160 + - curve25519 + - nacl/box - nacl/secretbox - openpgp/armor - - nacl/box + - openpgp/errors - poly1305 + - ripemd160 - salsa20/salsa - - openpgp/errors - - curve25519 - name: golang.org/x/net - version: de35ec43e7a9aabd6a9c54d2898220ea7e44de7d + version: 60c41d1de8da134c05b7b40154a9a82bf5b7edb9 subpackages: - - http2 - context - - netutil - - trace + - http2 - http2/hpack - idna - - lex/httplex - internal/timeseries - name: golang.org/x/sys - version: 62bee037599929a6e9146f29d10dd5208c43507d + version: d75a52659825e75fff6158388dddc6a5b04f9ba5 subpackages: - unix +- name: golang.org/x/text + version: 44f4f658a783b0cee41fe0a23b8fc91d9c120558 + subpackages: + - secure/bidirule + - transform + - unicode/bidi + - unicode/norm - name: google.golang.org/grpc - version: e78224b060cf3215247b7be455f80ea22e469b66 + version: 50955793b0183f9de69bd78e2ec251cf20aab121 subpackages: - codes - credentials @@ -205,8 +236,10 @@ imports: - internal - metadata - naming - - transport - peer + - stats + - tap + - transport - name: gopkg.in/fatih/set.v0 version: 27c40922c40b43fe04554d8223a402af3ea333f3 - name: gopkg.in/go-playground/validator.v8 @@ -215,4 +248,4 @@ imports: version: ecde8c8f16df93a994dda8936c8f60f0c26c28ab - name: gopkg.in/yaml.v2 version: a83829b6f1293c91addabc89d0571c246397bbf4 -devImports: [] +testImports: [] diff --git a/glide.yaml b/glide.yaml index ca8e52b5c..ed8e097c2 100644 --- a/glide.yaml +++ b/glide.yaml @@ -1,19 +1,34 @@ package: github.com/eris-ltd/eris-db import: -- package: github.com/eris-ltd/eris-logger - package: github.com/eris-ltd/eris-keys - package: github.com/spf13/cobra - package: github.com/spf13/viper -- package: github.com/tendermint/tendermint - package: github.com/gin-gonic/gin - package: github.com/gorilla/websocket - package: github.com/naoina/toml - package: github.com/stretchr/testify +- package: github.com/tendermint/tendermint + version: ~0.8.0 - package: github.com/tommy351/gin-cors - package: golang.org/x/crypto subpackages: - ripemd160 - package: gopkg.in/fatih/set.v0 - package: gopkg.in/tylerb/graceful.v1 -- package: golang.org/x/net/http2 -- package: github.com/eris-ltd/common +- package: golang.org/x/net + subpackages: + - http2 +- package: github.com/go-kit/kit + version: ^0.3.0 +- package: github.com/eapache/channels + version: ~1.1.0 +- package: github.com/go-logfmt/logfmt + version: ^0.3.0 +- package: github.com/go-stack/stack + version: ^1.5.2 +- package: github.com/inconshreveable/log15 +- package: github.com/Sirupsen/logrus + version: ^0.11.0 +- package: github.com/streadway/simpleuuid +- package: github.com/Masterminds/glide + version: ~0.12.3 diff --git a/keys/key_client.go b/keys/key_client.go index e841f50ab..edbc45112 100644 --- a/keys/key_client.go +++ b/keys/key_client.go @@ -1,24 +1,25 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 keys import ( "encoding/hex" "fmt" + + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/loggers" ) type KeyClient interface { @@ -29,31 +30,33 @@ type KeyClient interface { PublicKey(address []byte) (publicKey []byte, err error) } -// NOTE [ben] Compiler check to ensure ErisKeyClient successfully implements +// NOTE [ben] Compiler check to ensure erisKeyClient successfully implements // eris-db/keys.KeyClient -var _ KeyClient = (*ErisKeyClient)(nil) +var _ KeyClient = (*erisKeyClient)(nil) -type ErisKeyClient struct { +type erisKeyClient struct { rpcString string + logger loggers.InfoTraceLogger } -// ErisKeyClient.New returns a new eris-keys client for provided rpc location +// erisKeyClient.New returns a new eris-keys client for provided rpc location // Eris-keys connects over http request-responses -func NewErisKeyClient(rpcString string) *ErisKeyClient { - return &ErisKeyClient{ +func NewErisKeyClient(rpcString string, logger loggers.InfoTraceLogger) *erisKeyClient { + return &erisKeyClient{ rpcString: rpcString, + logger: logging.WithScope(logger, "ErisKeysClient"), } } // Eris-keys client Sign requests the signature from ErisKeysClient over rpc for the given // bytes to be signed and the address to sign them with. -func (erisKeys *ErisKeyClient) Sign(signBytesString string, signAddress []byte) (signature []byte, err error) { +func (erisKeys *erisKeyClient) Sign(signBytesString string, signAddress []byte) (signature []byte, err error) { args := map[string]string{ "msg": signBytesString, "hash": signBytesString, // TODO:[ben] backwards compatibility "addr": fmt.Sprintf("%X", signAddress), } - sigS, err := RequestResponse(erisKeys.rpcString, "sign", args) + sigS, err := RequestResponse(erisKeys.rpcString, "sign", args, erisKeys.logger) if err != nil { return } @@ -66,11 +69,11 @@ func (erisKeys *ErisKeyClient) Sign(signBytesString string, signAddress []byte) // Eris-keys client PublicKey requests the public key associated with an address from // the eris-keys server. -func (erisKeys *ErisKeyClient) PublicKey(address []byte) (publicKey []byte, err error) { +func (erisKeys *erisKeyClient) PublicKey(address []byte) (publicKey []byte, err error) { args := map[string]string{ "addr": fmt.Sprintf("%X", address), } - pubS, err := RequestResponse(erisKeys.rpcString, "pub", args) + pubS, err := RequestResponse(erisKeys.rpcString, "pub", args, erisKeys.logger) if err != nil { return } diff --git a/keys/key_client_util.go b/keys/key_client_util.go index e9e5f4783..b59c61f6b 100644 --- a/keys/key_client_util.go +++ b/keys/key_client_util.go @@ -1,21 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// version provides the current Eris-DB version and a VersionIdentifier -// for the modules to identify their version with. +// 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 keys @@ -26,7 +21,8 @@ import ( "io/ioutil" "net/http" - log "github.com/eris-ltd/eris-logger" + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/loggers" ) // Eris-Keys server connects over http request-response structures @@ -36,17 +32,17 @@ type HTTPResponse struct { Error string } -func RequestResponse(addr, method string, args map[string]string) (string, error) { - b, err := json.Marshal(args) +func RequestResponse(addr, method string, args map[string]string, logger loggers.InfoTraceLogger) (string, error) { + body, err := json.Marshal(args) if err != nil { return "", err } endpoint := fmt.Sprintf("%s/%s", addr, method) - log.WithFields(log.Fields{ - "key server endpoint": endpoint, - "request body": string(b), - }).Debugf("Eris-client: Sending request body to key server") - req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(b)) + logging.TraceMsg(logger, "Sending request to key server", + "key_server_endpoint", endpoint, + "request_body", string(body), + ) + req, err := http.NewRequest("POST", endpoint, bytes.NewBuffer(body)) if err != nil { return "", err } @@ -58,11 +54,11 @@ func RequestResponse(addr, method string, args map[string]string) (string, error if errS != "" { return "", fmt.Errorf("Error (string) calling eris-keys at %s: %s", endpoint, errS) } - log.WithFields(log.Fields{ - "endpoint": endpoint, - "request body": string(b), - "response": res, - }).Debugf("Received response from key server") + logging.TraceMsg(logger, "Received response from key server", + "endpoint", endpoint, + "request body", string(body), + "response", res, + ) return res, nil } diff --git a/keys/mock/key_client_mock.go b/keys/mock/key_client_mock.go index ef9cca861..d1ae1bd09 100644 --- a/keys/mock/key_client_mock.go +++ b/keys/mock/key_client_mock.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 mock diff --git a/license.md b/license.md index 9cecc1d46..d9a10c0d8 100644 --- a/license.md +++ b/license.md @@ -1,674 +1,176 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - {one line to give the program's name and a brief idea of what it does.} - Copyright (C) {year} {name of author} - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - {project} Copyright (C) {year} {fullname} - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/logging/adapters/logrus/logrus.go b/logging/adapters/logrus/logrus.go new file mode 100644 index 000000000..84b683718 --- /dev/null +++ b/logging/adapters/logrus/logrus.go @@ -0,0 +1,36 @@ +// 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 adapters + +import ( + "github.com/Sirupsen/logrus" + kitlog "github.com/go-kit/kit/log" +) + +type logrusLogger struct { + logger logrus.Logger +} + +var _ kitlog.Logger = (*logrusLogger)(nil) + +func NewLogrusLogger(logger logrus.Logger) *logrusLogger { + return &logrusLogger{ + logger: logger, + } +} + +func (ll *logrusLogger) Log(keyvals ...interface{}) error { + return nil +} diff --git a/logging/adapters/stdlib/capture.go b/logging/adapters/stdlib/capture.go new file mode 100644 index 000000000..3c8a2a88e --- /dev/null +++ b/logging/adapters/stdlib/capture.go @@ -0,0 +1,40 @@ +// 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 stdlib + +import ( + "io" + "log" + + "github.com/eris-ltd/eris-db/logging/loggers" + kitlog "github.com/go-kit/kit/log" +) + +func Capture(stdLibLogger log.Logger, + logger loggers.InfoTraceLogger) io.Writer { + adapter := newAdapter(logger) + stdLibLogger.SetOutput(adapter) + return adapter +} + +func CaptureRootLogger(logger loggers.InfoTraceLogger) io.Writer { + adapter := newAdapter(logger) + log.SetOutput(adapter) + return adapter +} + +func newAdapter(logger loggers.InfoTraceLogger) io.Writer { + return kitlog.NewStdlibAdapter(logger) +} diff --git a/logging/adapters/tendermint_log15/capture.go b/logging/adapters/tendermint_log15/capture.go new file mode 100644 index 000000000..64f5a139d --- /dev/null +++ b/logging/adapters/tendermint_log15/capture.go @@ -0,0 +1,61 @@ +// 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 adapters + +import ( + "github.com/eris-ltd/eris-db/logging/loggers" + kitlog "github.com/go-kit/kit/log" + "github.com/tendermint/log15" +) + +type infoTraceLoggerAsLog15Handler struct { + logger loggers.InfoTraceLogger +} + +var _ log15.Handler = (*infoTraceLoggerAsLog15Handler)(nil) + +type log15HandlerAsKitLogger struct { + handler log15.Handler +} + +var _ kitlog.Logger = (*log15HandlerAsKitLogger)(nil) + +func (l *log15HandlerAsKitLogger) Log(keyvals ...interface{}) error { + record := LogLineToRecord(keyvals...) + return l.handler.Log(record) +} + +func (h *infoTraceLoggerAsLog15Handler) Log(record *log15.Record) error { + if record.Lvl < log15.LvlDebug { + // Send to Critical, Warning, Error, and Info to the Info channel + h.logger.Info(RecordToLogLine(record)...) + } else { + // Send to Debug to the Trace channel + h.logger.Trace(RecordToLogLine(record)...) + } + return nil +} + +func Log15HandlerAsKitLogger(handler log15.Handler) kitlog.Logger { + return &log15HandlerAsKitLogger{ + handler: handler, + } +} + +func InfoTraceLoggerAsLog15Handler(logger loggers.InfoTraceLogger) log15.Handler { + return &infoTraceLoggerAsLog15Handler{ + logger: logger, + } +} diff --git a/logging/adapters/tendermint_log15/convert.go b/logging/adapters/tendermint_log15/convert.go new file mode 100644 index 000000000..f1668b262 --- /dev/null +++ b/logging/adapters/tendermint_log15/convert.go @@ -0,0 +1,84 @@ +// 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 adapters + +import ( + "time" + + "github.com/eris-ltd/eris-db/logging/loggers" + "github.com/eris-ltd/eris-db/logging/structure" + . "github.com/eris-ltd/eris-db/util/slice" + "github.com/go-stack/stack" + "github.com/tendermint/log15" +) + +// Convert a go-kit log line (i.e. keyvals... interface{}) into a log15 record +// This allows us to use log15 output handlers +func LogLineToRecord(keyvals ...interface{}) *log15.Record { + vals, ctx := structure.ValuesAndContext(keyvals, structure.TimeKey, + structure.MessageKey, structure.CallerKey, structure.LevelKey) + + // Mapping of log line to Record is on a best effort basis + theTime, _ := vals[structure.TimeKey].(time.Time) + call, _ := vals[structure.CallerKey].(stack.Call) + level, _ := vals[structure.LevelKey].(string) + message, _ := vals[structure.MessageKey].(string) + + return &log15.Record{ + Time: theTime, + Lvl: Log15LvlFromString(level), + Msg: message, + Call: call, + Ctx: ctx, + KeyNames: log15.RecordKeyNames{ + Time: structure.TimeKey, + Msg: structure.MessageKey, + Lvl: structure.LevelKey, + }} +} + +// Convert a log15 record to a go-kit log line (i.e. keyvals... interface{}) +// This allows us to capture output from dependencies using log15 +func RecordToLogLine(record *log15.Record) []interface{} { + return Concat( + Slice( + structure.TimeKey, record.Time, + structure.CallerKey, record.Call, + structure.LevelKey, record.Lvl.String(), + ), + record.Ctx, + Slice( + structure.MessageKey, record.Msg, + )) +} + +// Collapse our weak notion of leveling and log15's into a log15.Lvl +func Log15LvlFromString(level string) log15.Lvl { + if level == "" { + return log15.LvlDebug + } + switch level { + case loggers.InfoLevelName: + return log15.LvlInfo + case loggers.TraceLevelName: + return log15.LvlDebug + default: + lvl, err := log15.LvlFromString(level) + if err == nil { + return lvl + } + return log15.LvlDebug + } +} diff --git a/logging/config.go b/logging/config.go new file mode 100644 index 000000000..5123b9369 --- /dev/null +++ b/logging/config.go @@ -0,0 +1,25 @@ +// 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 logging + +type ( + SinkConfig struct { + Channels []string + } + + LoggingConfig struct { + Sinks []SinkConfig + } +) diff --git a/logging/convention.go b/logging/convention.go new file mode 100644 index 000000000..8dc5b05da --- /dev/null +++ b/logging/convention.go @@ -0,0 +1,72 @@ +// 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 logging + +import ( + "github.com/eris-ltd/eris-db/logging/loggers" + "github.com/eris-ltd/eris-db/logging/structure" + "github.com/eris-ltd/eris-db/util/slice" + kitlog "github.com/go-kit/kit/log" +) + +// Helper functions for InfoTraceLoggers, sort of extension methods to loggers +// to centralise and establish logging conventions on top of in with the base +// logging interface + +// Record structured Info log line with a message and conventional keys +func InfoMsgVals(logger loggers.InfoTraceLogger, message string, vals ...interface{}) { + MsgVals(kitlog.LoggerFunc(logger.Info), message, vals...) +} + +// Record structured Trace log line with a message and conventional keys +func TraceMsgVals(logger loggers.InfoTraceLogger, message string, vals ...interface{}) { + MsgVals(kitlog.LoggerFunc(logger.Trace), message, vals...) +} + +// Record structured Info log line with a message +func InfoMsg(logger loggers.InfoTraceLogger, message string, keyvals ...interface{}) { + Msg(kitlog.LoggerFunc(logger.Info), message, keyvals...) +} + +// Record structured Trace log line with a message +func TraceMsg(logger loggers.InfoTraceLogger, message string, keyvals ...interface{}) { + Msg(kitlog.LoggerFunc(logger.Trace), message, keyvals...) +} + +// Establish or extend the scope of this logger by appending scopeName to the Scope vector. +// Like With the logging scope is append only but can be used to provide parenthetical scopes by hanging on to the +// parent scope and using once the scope has been exited. The scope mechanism does is agnostic to the type of scope +// so can be used to identify certain segments of the call stack, a lexical scope, or any other nested scope. +func WithScope(logger loggers.InfoTraceLogger, scopeName string) loggers.InfoTraceLogger { + // InfoTraceLogger will collapse successive (ScopeKey, scopeName) pairs into a vector in the order which they appear + return logger.With(structure.ScopeKey, scopeName) +} + +// Record a structured log line with a message +func Msg(logger kitlog.Logger, message string, keyvals ...interface{}) error { + prepended := slice.CopyPrepend(keyvals, structure.MessageKey, message) + return logger.Log(prepended...) +} + +// Record a structured log line with a message and conventional keys +func MsgVals(logger kitlog.Logger, message string, vals ...interface{}) error { + keyvals := make([]interface{}, len(vals)*2) + for i := 0; i < len(vals); i++ { + kv := i * 2 + keyvals[kv] = structure.KeyFromValue(vals[i]) + keyvals[kv+1] = vals[i] + } + return Msg(logger, message, keyvals) +} diff --git a/logging/lifecycle/lifecycle.go b/logging/lifecycle/lifecycle.go new file mode 100644 index 000000000..81f6a20df --- /dev/null +++ b/logging/lifecycle/lifecycle.go @@ -0,0 +1,72 @@ +// 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 lifecycle + +// No package in ./logging/... should depend on lifecycle +import ( + "os" + + "time" + + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/adapters/stdlib" + tmLog15adapter "github.com/eris-ltd/eris-db/logging/adapters/tendermint_log15" + "github.com/eris-ltd/eris-db/logging/loggers" + "github.com/eris-ltd/eris-db/logging/structure" + + kitlog "github.com/go-kit/kit/log" + "github.com/streadway/simpleuuid" + tmLog15 "github.com/tendermint/log15" +) + +// Lifecycle provides a canonical source for eris loggers. Components should use the functions here +// to set up their root logger and capture any other logging output. + +// Obtain a logger from a LoggingConfig +func NewLoggerFromLoggingConfig(LoggingConfig *logging.LoggingConfig) loggers.InfoTraceLogger { + return NewStdErrLogger() +} + +func NewStdErrLogger() loggers.InfoTraceLogger { + logger := tmLog15adapter.Log15HandlerAsKitLogger( + tmLog15.StreamHandler(os.Stderr, tmLog15.TerminalFormat())) + return NewLogger(logger, logger) +} + +// Provided a standard eris logger that outputs to the supplied underlying info and trace +// loggers +func NewLogger(infoLogger, traceLogger kitlog.Logger) loggers.InfoTraceLogger { + infoTraceLogger := loggers.NewInfoTraceLogger( + loggers.ErisFormatLogger(infoLogger), + loggers.ErisFormatLogger(traceLogger)) + // Create a random ID based on start time + uuid, _ := simpleuuid.NewTime(time.Now()) + var runId string + if uuid != nil { + runId = uuid.String() + } + return logging.WithMetadata(infoTraceLogger.With(structure.RunId, runId)) +} + +func CaptureTendermintLog15Output(infoTraceLogger loggers.InfoTraceLogger) { + tmLog15.Root().SetHandler( + tmLog15adapter.InfoTraceLoggerAsLog15Handler(infoTraceLogger. + With(structure.CapturedLoggingSourceKey, "tendermint_log15"))) +} + +func CaptureStdlibLogOutput(infoTraceLogger loggers.InfoTraceLogger) { + stdlib.CaptureRootLogger(infoTraceLogger. + With(structure.CapturedLoggingSourceKey, "stdlib_log")) +} diff --git a/logging/loggers/channel_logger.go b/logging/loggers/channel_logger.go new file mode 100644 index 000000000..1bacfa446 --- /dev/null +++ b/logging/loggers/channel_logger.go @@ -0,0 +1,87 @@ +// 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 loggers + +import ( + "github.com/eapache/channels" + kitlog "github.com/go-kit/kit/log" +) + +const ( + LoggingRingBufferCap channels.BufferCap = 100 +) + +type ChannelLogger struct { + ch channels.Channel +} + +var _ kitlog.Logger = (*ChannelLogger)(nil) + +// Creates a Logger that uses a uses a non-blocking channel. +// +// We would like calls to Log to never block so we use a channel implementation +// that is non-blocking on writes and is able to be so by using a finite ring +// buffer. +func newChannelLogger() *ChannelLogger { + return &ChannelLogger{ + ch: channels.NewRingChannel(LoggingRingBufferCap), + } +} + +func (cl *ChannelLogger) Log(keyvals ...interface{}) error { + cl.ch.In() <- keyvals + // We don't have a way to pass on any logging errors, but that's okay: Log is + // a maximal interface and the error return type is only there for special + // cases. + return nil +} + +// Read a log line by waiting until one is available and returning it +func (cl *ChannelLogger) WaitReadLogLine() []interface{} { + log := <-cl.ch.Out() + // We are passing slices of interfaces down this channel (go-kit log's Log + // interface type), a panic is the right thing to do if this type assertion + // fails. + return log.([]interface{}) +} + +// Tries to read a log line from the channel buffer or returns nil if none is +// immediately available +func (cl *ChannelLogger) ReadLogLine() []interface{} { + select { + case log := <-cl.ch.Out(): + // See WaitReadLogLine + return log.([]interface{}) + default: + return nil + } +} + +// Enters an infinite loop that will drain any log lines from the passed logger. +// +// Exits if the channel is closed. +func (cl *ChannelLogger) DrainChannelToLogger(logger kitlog.Logger) { + for cl.ch.Out() != nil { + logger.Log(cl.WaitReadLogLine()...) + } +} + +// Wraps an underlying Logger baseLogger to provide a Logger that is +// is non-blocking on calls to Log. +func NonBlockingLogger(logger kitlog.Logger) *ChannelLogger { + cl := newChannelLogger() + go cl.DrainChannelToLogger(logger) + return cl +} diff --git a/logging/loggers/channel_logger_test.go b/logging/loggers/channel_logger_test.go new file mode 100644 index 000000000..0573556e1 --- /dev/null +++ b/logging/loggers/channel_logger_test.go @@ -0,0 +1,47 @@ +// 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 loggers + +import ( + "testing" + + "fmt" + + "github.com/stretchr/testify/assert" +) + +func TestChannelLogger(t *testing.T) { + cl := newChannelLogger() + + // Push a larger number of log messages than will fit into ring buffer + for i := 0; i < int(LoggingRingBufferCap)+10; i++ { + cl.Log("log line", i) + } + + // Observe that oldest 10 messages are overwritten (so first message is 10) + for i := 0; i < int(LoggingRingBufferCap); i++ { + ll := cl.WaitReadLogLine() + assert.Equal(t, 10+i, ll[1]) + } + + assert.Nil(t, cl.ReadLogLine(), "Since we have drained the buffer there "+ + "should be no more log lines.") +} + +func TestBlether(t *testing.T) { + var bs []byte + ext := append(bs) + fmt.Println(ext) +} diff --git a/logging/loggers/eris_format_logger.go b/logging/loggers/eris_format_logger.go new file mode 100644 index 000000000..aea07254d --- /dev/null +++ b/logging/loggers/eris_format_logger.go @@ -0,0 +1,53 @@ +// 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 loggers + +import ( + "fmt" + + "github.com/eris-ltd/eris-db/logging/structure" + + kitlog "github.com/go-kit/kit/log" +) + +// Logger that implements some formatting conventions for eris-db and eris-client +// This is intended for applying consistent value formatting before the final 'output' logger; +// we should avoid prematurely formatting values here if it is useful to let the output logger +// decide how it wants to display values. Ideal candidates for 'early' formatting here are types that +// we control and generic output loggers are unlikely to know about. +type erisFormatLogger struct { + logger kitlog.Logger +} + +var _ kitlog.Logger = &erisFormatLogger{} + +func (efl *erisFormatLogger) Log(keyvals ...interface{}) error { + return efl.logger.Log(structure.MapKeyValues(keyvals, erisFormatKeyValueMapper)...) +} + +func erisFormatKeyValueMapper(key, value interface{}) (interface{}, interface{}) { + switch key { + default: + switch v := value.(type) { + case []byte: + return key, fmt.Sprintf("%X", v) + } + } + return key, value +} + +func ErisFormatLogger(logger kitlog.Logger) *erisFormatLogger { + return &erisFormatLogger{logger: logger} +} diff --git a/logging/loggers/info_trace_logger.go b/logging/loggers/info_trace_logger.go new file mode 100644 index 000000000..e127e10fe --- /dev/null +++ b/logging/loggers/info_trace_logger.go @@ -0,0 +1,141 @@ +// 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 loggers + +import ( + "github.com/eris-ltd/eris-db/logging/structure" + kitlog "github.com/go-kit/kit/log" +) + +const ( + InfoChannelName = "Info" + TraceChannelName = "Trace" + + InfoLevelName = InfoChannelName + TraceLevelName = TraceChannelName +) + +type infoTraceLogger struct { + infoLogger *kitlog.Context + traceLogger *kitlog.Context +} + +// InfoTraceLogger maintains two independent concurrently-safe channels of +// logging. The idea behind the independence is that you can ignore one channel +// with no performance penalty. For more fine grained filtering or aggregation +// the Info and Trace loggers can be decorated loggers that perform arbitrary +// filtering/routing/aggregation on log messages. +type InfoTraceLogger interface { + // Send a log message to the default channel + kitlog.Logger + + // Send an log message to the Info channel, formed of a sequence of key value + // pairs. Info messages should be operationally interesting to a human who is + // monitoring the logs. But not necessarily a human who is trying to + // understand or debug the system. Any handled errors or warnings should be + // sent to the Info channel (where you may wish to tag them with a suitable + // key-value pair to categorise them as such). + Info(keyvals ...interface{}) error + + // Send an log message to the Trace channel, formed of a sequence of key-value + // pairs. Trace messages can be used for any state change in the system that + // may be of interest to a machine consumer or a human who is trying to debug + // the system or trying to understand the system in detail. If the messages + // are very point-like and contain little structure, consider using a metric + // instead. + Trace(keyvals ...interface{}) error + + // A logging context (see go-kit log's Context). Takes a sequence key values + // via With or WithPrefix and ensures future calls to log will have those + // contextual values appended to the call to an underlying logger. + // Values can be dynamic by passing an instance of the kitlog.Valuer interface + // This provides an interface version of the kitlog.Context struct to be used + // For implementations that wrap a kitlog.Context. In addition it makes no + // assumption about the name or signature of the logging method(s). + // See InfoTraceLogger + + // Establish a context by appending contextual key-values to any existing + // contextual values + With(keyvals ...interface{}) InfoTraceLogger + + // Establish a context by prepending contextual key-values to any existing + // contextual values + WithPrefix(keyvals ...interface{}) InfoTraceLogger +} + +// Interface assertions +var _ InfoTraceLogger = (*infoTraceLogger)(nil) +var _ kitlog.Logger = (InfoTraceLogger)(nil) + +func NewInfoTraceLogger(infoLogger, traceLogger kitlog.Logger) InfoTraceLogger { + // We will never halt the progress of a log emitter. If log output takes too + // long will start dropping log lines by using a ring buffer. + // We also guard against any concurrency bugs in underlying loggers by feeding + // them from a single channel + logger := kitlog.NewContext(NonBlockingLogger(VectorValuedLogger( + MultipleChannelLogger( + map[string]kitlog.Logger{ + InfoChannelName: infoLogger, + TraceChannelName: traceLogger, + })))) + return &infoTraceLogger{ + infoLogger: logger.With( + structure.ChannelKey, InfoChannelName, + structure.LevelKey, InfoLevelName, + ), + traceLogger: logger.With( + structure.ChannelKey, TraceChannelName, + structure.LevelKey, TraceLevelName, + ), + } +} + +func NewNoopInfoTraceLogger() InfoTraceLogger { + noopLogger := kitlog.NewNopLogger() + return NewInfoTraceLogger(noopLogger, noopLogger) +} + +func (l *infoTraceLogger) With(keyvals ...interface{}) InfoTraceLogger { + return &infoTraceLogger{ + infoLogger: l.infoLogger.With(keyvals...), + traceLogger: l.traceLogger.With(keyvals...), + } +} + +func (l *infoTraceLogger) WithPrefix(keyvals ...interface{}) InfoTraceLogger { + return &infoTraceLogger{ + infoLogger: l.infoLogger.WithPrefix(keyvals...), + traceLogger: l.traceLogger.WithPrefix(keyvals...), + } +} + +func (l *infoTraceLogger) Info(keyvals ...interface{}) error { + // We send Info and Trace log lines down the same pipe to keep them ordered + return l.infoLogger.Log(keyvals...) +} + +func (l *infoTraceLogger) Trace(keyvals ...interface{}) error { + return l.traceLogger.Log(keyvals...) +} + +// If logged to as a plain kitlog logger presume the message is for Trace +// This favours keeping Info reasonably quiet. Note that an InfoTraceLogger +// aware adapter can make its own choices, but we tend to thing of logs from +// dependencies as less interesting than logs generated by us or specifically +// routed by us. +func (l *infoTraceLogger) Log(keyvals ...interface{}) error { + l.Trace(keyvals...) + return nil +} diff --git a/logging/loggers/info_trace_logger_test.go b/logging/loggers/info_trace_logger_test.go new file mode 100644 index 000000000..171745540 --- /dev/null +++ b/logging/loggers/info_trace_logger_test.go @@ -0,0 +1,28 @@ +// 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 loggers + +import ( + "os" + "testing" + + kitlog "github.com/go-kit/kit/log" +) + +func TestLogger(t *testing.T) { + stderrLogger := kitlog.NewLogfmtLogger(os.Stderr) + logger := NewInfoTraceLogger(stderrLogger, stderrLogger) + logger.Trace("hello", "barry") +} diff --git a/logging/loggers/logging_test.go b/logging/loggers/logging_test.go new file mode 100644 index 000000000..103d8ddf7 --- /dev/null +++ b/logging/loggers/logging_test.go @@ -0,0 +1,35 @@ +// 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 loggers + +import "errors" + +type testLogger struct { + logLines [][]interface{} + err error +} + +func newErrorLogger(errMessage string) *testLogger { + return &testLogger{err: errors.New(errMessage)} +} + +func newTestLogger() *testLogger { + return &testLogger{} +} + +func (tl *testLogger) Log(keyvals ...interface{}) error { + tl.logLines = append(tl.logLines, keyvals) + return tl.err +} diff --git a/logging/loggers/multiple_channel_logger.go b/logging/loggers/multiple_channel_logger.go new file mode 100644 index 000000000..2e2e344f4 --- /dev/null +++ b/logging/loggers/multiple_channel_logger.go @@ -0,0 +1,51 @@ +// 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 loggers + +import ( + "fmt" + + "github.com/eris-ltd/eris-db/logging/structure" + kitlog "github.com/go-kit/kit/log" +) + +// This represents a 'SELECT ONE' type logger. When logged to it will search +// for the ChannelKey field, look that up in its map and send the log line there +// Otherwise logging is a noop (but an error will be returned - which is optional) +type MultipleChannelLogger map[string]kitlog.Logger + +var _ kitlog.Logger = MultipleChannelLogger(nil) + +// Like go-kit log's Log method only logs a message to the specified channelName +// which must be a member of this MultipleChannelLogger +func (mcl MultipleChannelLogger) Log(keyvals ...interface{}) error { + channel := structure.Value(keyvals, structure.ChannelKey) + if channel == nil { + return fmt.Errorf("MultipleChannelLogger could not select channel because"+ + " '%s' was not set in log message", structure.ChannelKey) + } + channelName, ok := channel.(string) + if !ok { + return fmt.Errorf("MultipleChannelLogger could not select channel because"+ + " channel was set to non-string value %v", channel) + } + logger := mcl[channelName] + if logger == nil { + return fmt.Errorf("Could not log to channel '%s', since it is not "+ + "registered with this MultipleChannelLogger (the underlying logger may "+ + "have been nil when passed to NewMultipleChannelLogger)", channelName) + } + return logger.Log(keyvals...) +} diff --git a/logging/loggers/multiple_channel_logger_test.go b/logging/loggers/multiple_channel_logger_test.go new file mode 100644 index 000000000..a14042043 --- /dev/null +++ b/logging/loggers/multiple_channel_logger_test.go @@ -0,0 +1,42 @@ +// 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 loggers + +import ( + "runtime" + "testing" + "time" + + "github.com/eris-ltd/eris-db/logging/structure" + kitlog "github.com/go-kit/kit/log" + "github.com/stretchr/testify/assert" +) + +func TestMultipleChannelLogger(t *testing.T) { + boringLogger, interestingLogger := newTestLogger(), newTestLogger() + mcl := kitlog.NewContext(MultipleChannelLogger(map[string]kitlog.Logger{ + "Boring": boringLogger, + "Interesting": interestingLogger, + })) + err := mcl.With("time", kitlog.Valuer(func() interface{} { return "aa" })). + Log(structure.ChannelKey, "Boring", "foo", "bar") + assert.NoError(t, err, "Should log without an error") + // Wait for channel to drain + time.Sleep(time.Second) + runtime.Gosched() + assert.Equal(t, []interface{}{"time", "aa", structure.ChannelKey, "Boring", + "foo", "bar"}, + boringLogger.logLines[0]) +} diff --git a/logging/loggers/multiple_output_logger.go b/logging/loggers/multiple_output_logger.go new file mode 100644 index 000000000..89ae50a97 --- /dev/null +++ b/logging/loggers/multiple_output_logger.go @@ -0,0 +1,64 @@ +// 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 loggers + +import ( + "strings" + + kitlog "github.com/go-kit/kit/log" +) + +// This represents an 'AND' type logger. When logged to it will log to each of +// the loggers in the slice. +type MultipleOutputLogger []kitlog.Logger + +var _ kitlog.Logger = MultipleOutputLogger(nil) + +func (mol MultipleOutputLogger) Log(keyvals ...interface{}) error { + var errs []error + for _, logger := range mol { + err := logger.Log(keyvals...) + if err != nil { + errs = append(errs, err) + } + } + return combineErrors(errs) +} + +// Creates a logger that forks log messages to each of its outputLoggers +func NewMultipleOutputLogger(outputLoggers ...kitlog.Logger) kitlog.Logger { + return MultipleOutputLogger(outputLoggers) +} + +type multipleErrors []error + +func combineErrors(errs []error) error { + switch len(errs) { + case 0: + return nil + case 1: + return errs[0] + default: + return multipleErrors(errs) + } +} + +func (errs multipleErrors) Error() string { + var errStrings []string + for _, err := range errs { + errStrings = append(errStrings, err.Error()) + } + return strings.Join(errStrings, ";") +} diff --git a/logging/loggers/multiple_output_logger_test.go b/logging/loggers/multiple_output_logger_test.go new file mode 100644 index 000000000..4f606376a --- /dev/null +++ b/logging/loggers/multiple_output_logger_test.go @@ -0,0 +1,32 @@ +// 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 loggers + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewMultipleOutputLogger(t *testing.T) { + a, b := newErrorLogger("error a"), newErrorLogger("error b") + mol := NewMultipleOutputLogger(a, b) + logLine := []interface{}{"msg", "hello"} + err := mol.Log(logLine...) + expected := [][]interface{}{logLine} + assert.Equal(t, expected, a.logLines) + assert.Equal(t, expected, b.logLines) + assert.IsType(t, multipleErrors{}, err) +} diff --git a/logging/loggers/vector_valued_logger.go b/logging/loggers/vector_valued_logger.go new file mode 100644 index 000000000..fa59cdbfa --- /dev/null +++ b/logging/loggers/vector_valued_logger.go @@ -0,0 +1,35 @@ +// 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 loggers + +import ( + "github.com/eris-ltd/eris-db/logging/structure" + kitlog "github.com/go-kit/kit/log" +) + +// Treat duplicate key-values as consecutive entries in a vector-valued lookup +type vectorValuedLogger struct { + logger kitlog.Logger +} + +var _ kitlog.Logger = &vectorValuedLogger{} + +func (vvl *vectorValuedLogger) Log(keyvals ...interface{}) error { + return vvl.logger.Log(structure.Vectorise(keyvals)...) +} + +func VectorValuedLogger(logger kitlog.Logger) *vectorValuedLogger { + return &vectorValuedLogger{logger: logger} +} diff --git a/logging/loggers/vector_valued_logger_test.go b/logging/loggers/vector_valued_logger_test.go new file mode 100644 index 000000000..157f5bc08 --- /dev/null +++ b/logging/loggers/vector_valued_logger_test.go @@ -0,0 +1,31 @@ +// 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 loggers + +import ( + "testing" + + . "github.com/eris-ltd/eris-db/util/slice" + "github.com/stretchr/testify/assert" +) + +func TestVectorValuedLogger(t *testing.T) { + logger := newTestLogger() + vvl := VectorValuedLogger(logger) + vvl.Log("foo", "bar", "seen", 1, "seen", 3, "seen", 2) + + assert.Equal(t, Slice("foo", "bar", "seen", Slice(1, 3, 2)), + logger.logLines[0]) +} diff --git a/logging/metadata.go b/logging/metadata.go new file mode 100644 index 000000000..258ca733c --- /dev/null +++ b/logging/metadata.go @@ -0,0 +1,47 @@ +// 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 logging + +import ( + "time" + + "github.com/eris-ltd/eris-db/logging/loggers" + "github.com/eris-ltd/eris-db/logging/structure" + kitlog "github.com/go-kit/kit/log" + "github.com/go-stack/stack" +) + +const ( + // To get the Caller information correct on the log, we need to count the + // number of calls from a log call in the code to the time it hits a kitlog + // context: [log call site (5), Info/Trace (4), MultipleChannelLogger.Log (3), + // kitlog.Context.Log (2), kitlog.bindValues (1) (binding occurs), + // kitlog.Caller (0), stack.caller] + infoTraceLoggerCallDepth = 5 +) + +var defaultTimestampUTCValuer kitlog.Valuer = func() interface{} { + return time.Now() +} + +func WithMetadata(infoTraceLogger loggers.InfoTraceLogger) loggers.InfoTraceLogger { + return infoTraceLogger.With(structure.TimeKey, defaultTimestampUTCValuer, + structure.CallerKey, kitlog.Caller(infoTraceLoggerCallDepth), + "trace", TraceValuer()) +} + +func TraceValuer() kitlog.Valuer { + return func() interface{} { return stack.Trace() } +} diff --git a/logging/structure/structure.go b/logging/structure/structure.go new file mode 100644 index 000000000..43a55df87 --- /dev/null +++ b/logging/structure/structure.go @@ -0,0 +1,160 @@ +// 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 structure + +import ( + "reflect" + + . "github.com/eris-ltd/eris-db/util/slice" +) + +const ( + // Log time (time.Time) + TimeKey = "time" + // Call site for log invocation (go-stack.Call) + CallerKey = "caller" + // Level name (string) + LevelKey = "level" + // Channel name in a vector channel logging context + ChannelKey = "channel" + // Log message (string) + MessageKey = "message" + // Captured logging source (like tendermint_log15, stdlib_log) + CapturedLoggingSourceKey = "captured_logging_source" + // Top-level component (choose one) name + ComponentKey = "component" + // Vector-valued scope + ScopeKey = "scope" + // Globally unique identifier persisting while a single instance (root process) + // of this program/service is running + RunId = "run_id" +) + +// Pull the specified values from a structured log line into a map. +// Assumes keys are single-valued. +// Returns a map of the key-values from the requested keys and +// the unmatched remainder keyvals as context as a slice of key-values. +func ValuesAndContext(keyvals []interface{}, + keys ...interface{}) (map[interface{}]interface{}, []interface{}) { + vals := make(map[interface{}]interface{}, len(keys)) + context := make([]interface{}, len(keyvals)) + copy(context, keyvals) + deletions := 0 + // We can't really do better than a linear scan of both lists here. N is small + // so screw the asymptotics. + // Guard against odd-length list + for i := 0; i < 2*(len(keyvals)/2); i += 2 { + for k := 0; k < len(keys); k++ { + if keyvals[i] == keys[k] { + // Pull the matching key-value pair into vals to return + vals[keys[k]] = keyvals[i+1] + // Delete the key once it's found + keys = DeleteAt(keys, k) + // And remove the key-value pair from context + context = Delete(context, i-deletions, 2) + // Keep a track of how much we've shrunk the context to offset next + // deletion + deletions += 2 + break + } + } + } + return vals, context +} + +// Stateful index that tracks the location of a possible vector value +type vectorValueindex struct { + // Location of the value belonging to a key in output slice + valueIndex int + // Whether or not the value is currently a vector + vector bool +} + +// 'Vectorises' values associated with repeated string keys member by collapsing many values into a single vector value. +// The result is a copy of keyvals where the first occurrence of each matching key and its first value are replaced by +// that key and all of its values in a single slice. +func Vectorise(keyvals []interface{}, vectorKeys ...string) []interface{} { + // We rely on working against a single backing array, so we use a capacity that is the maximum possible size of the + // slice after vectorising (in the case there are no duplicate keys and this is a no-op) + outputKeyvals := make([]interface{}, 0, len(keyvals)) + // Track the location and vector status of the values in the output + valueIndices := make(map[string]*vectorValueindex, len(vectorKeys)) + elided := 0 + for i := 0; i < 2*(len(keyvals)/2); i += 2 { + key := keyvals[i] + val := keyvals[i+1] + + // Only attempt to vectorise string keys + if k, ok := key.(string); ok { + if valueIndices[k] == nil { + // Record that this key has been seen once + valueIndices[k] = &vectorValueindex{ + valueIndex: i + 1 - elided, + } + // Copy the key-value to output with the single value + outputKeyvals = append(outputKeyvals, key, val) + } else { + // We have seen this key before + vi := valueIndices[k] + if !vi.vector { + // This must be the only second occurrence of the key so now vectorise the value + outputKeyvals[vi.valueIndex] = []interface{}{outputKeyvals[vi.valueIndex]} + vi.vector = true + } + // Grow the vector value + outputKeyvals[vi.valueIndex] = append(outputKeyvals[vi.valueIndex].([]interface{}), val) + // We are now running two more elements behind the input keyvals because we have absorbed this key-value pair + elided += 2 + } + } else { + // Just copy the key-value to the output for non-string keys + outputKeyvals = append(outputKeyvals, key, val) + } + } + return outputKeyvals +} + +// Return a single value corresponding to key in keyvals +func Value(keyvals []interface{}, key interface{}) interface{} { + for i := 0; i < 2*(len(keyvals)/2); i += 2 { + if keyvals[i] == key { + return keyvals[i+1] + } + } + return nil +} + +// Obtain a canonical key from a value. Useful for structured logging where the +// type of value alone may be sufficient to determine its key. Providing this +// function centralises any convention over type names +func KeyFromValue(val interface{}) string { + switch val.(type) { + case string: + return "text" + default: + return reflect.TypeOf(val).Name() + } +} + +// Maps key values pairs with a function (key, value) -> (new key, new value) +func MapKeyValues(keyvals []interface{}, fn func(interface{}, interface{}) (interface{}, interface{})) []interface{} { + mappedKeyvals := make([]interface{}, len(keyvals)) + for i := 0; i < 2*(len(keyvals)/2); i += 2 { + key := keyvals[i] + val := keyvals[i+1] + mappedKeyvals[i], mappedKeyvals[i+1] = fn(key, val) + } + return mappedKeyvals +} diff --git a/logging/structure/structure_test.go b/logging/structure/structure_test.go new file mode 100644 index 000000000..bbdd0895f --- /dev/null +++ b/logging/structure/structure_test.go @@ -0,0 +1,50 @@ +// 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 structure + +import ( + "testing" + + . "github.com/eris-ltd/eris-db/util/slice" + "github.com/stretchr/testify/assert" +) + +func TestValuesAndContext(t *testing.T) { + keyvals := Slice("hello", 1, "dog", 2, "fish", 3, "fork", 5) + vals, ctx := ValuesAndContext(keyvals, "hello", "fish") + assert.Equal(t, map[interface{}]interface{}{"hello": 1, "fish": 3}, vals) + assert.Equal(t, Slice("dog", 2, "fork", 5), ctx) +} + +func TestVectorise(t *testing.T) { + kvs := Slice( + "scope", "lawnmower", + "hub", "budub", + "occupation", "fish brewer", + "scope", "hose pipe", + "flub", "dub", + "scope", "rake", + "flub", "brub", + ) + + kvsVector := Vectorise(kvs, "occupation", "scope") + assert.Equal(t, Slice( + "scope", Slice("lawnmower", "hose pipe", "rake"), + "hub", "budub", + "occupation", "fish brewer", + "flub", Slice("dub", "brub"), + ), + kvsVector) +} diff --git a/logging/terminal.go b/logging/terminal.go new file mode 100644 index 000000000..8a2be393e --- /dev/null +++ b/logging/terminal.go @@ -0,0 +1,43 @@ +// 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 logging + +import ( + "github.com/eris-ltd/eris-db/logging/structure" + "github.com/go-kit/kit/log/term" +) + +func Colors(keyvals ...interface{}) term.FgBgColor { + for i := 0; i < len(keyvals)-1; i += 2 { + if keyvals[i] != structure.LevelKey { + continue + } + switch keyvals[i+1] { + case "debug": + return term.FgBgColor{Fg: term.DarkGray} + case "info": + return term.FgBgColor{Fg: term.Gray} + case "warn": + return term.FgBgColor{Fg: term.Yellow} + case "error": + return term.FgBgColor{Fg: term.Red} + case "crit": + return term.FgBgColor{Fg: term.Gray, Bg: term.DarkRed} + default: + return term.FgBgColor{} + } + } + return term.FgBgColor{} +} diff --git a/manager/config.go b/manager/config.go index 28c84e21d..827d25ed5 100644 --- a/manager/config.go +++ b/manager/config.go @@ -1,21 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// version provides the current Eris-DB version and a VersionIdentifier -// for the modules to identify their version with. +// 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 manager diff --git a/manager/eris-mint/accounts.go b/manager/eris-mint/accounts.go index 58a09b0e7..913416294 100644 --- a/manager/eris-mint/accounts.go +++ b/manager/eris-mint/accounts.go @@ -1,22 +1,21 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT +// 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. -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +package erismint // Accounts is part of the pipe for ErisMint and provides the implementation // for the pipe to call into the ErisMint application -package erismint import ( "bytes" @@ -24,12 +23,11 @@ import ( "fmt" "sync" - tendermint_common "github.com/tendermint/go-common" - account "github.com/eris-ltd/eris-db/account" core_types "github.com/eris-ltd/eris-db/core/types" definitions "github.com/eris-ltd/eris-db/definitions" event "github.com/eris-ltd/eris-db/event" + word256 "github.com/eris-ltd/eris-db/word256" ) // NOTE [ben] Compiler check to ensure Accounts successfully implements @@ -119,7 +117,7 @@ func (this *accounts) StorageAt(address, key []byte) (*core_types.StorageItem, storageRoot := account.StorageRoot storageTree := state.LoadStorage(storageRoot) - _, value, _ := storageTree.Get(tendermint_common.LeftPadWord256(key).Bytes()) + _, value, _ := storageTree.Get(word256.LeftPadWord256(key).Bytes()) if value == nil { return &core_types.StorageItem{key, []byte{}}, nil } diff --git a/manager/eris-mint/eris-mint.go b/manager/eris-mint/eris-mint.go index 772174fe1..9ba184418 100644 --- a/manager/eris-mint/eris-mint.go +++ b/manager/eris-mint/eris-mint.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 erismint @@ -22,11 +20,12 @@ import ( "sync" "time" + abci "github.com/tendermint/abci/types" tendermint_events "github.com/tendermint/go-events" wire "github.com/tendermint/go-wire" - tmsp "github.com/tendermint/tmsp/types" - log "github.com/eris-ltd/eris-logger" + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/loggers" sm "github.com/eris-ltd/eris-db/manager/eris-mint/state" manager_types "github.com/eris-ltd/eris-db/manager/types" @@ -46,18 +45,19 @@ type ErisMint struct { checkCache *sm.BlockCache // for CheckTx (eg. so we get nonces right) evc *tendermint_events.EventCache - evsw *tendermint_events.EventSwitch + evsw tendermint_events.EventSwitch - nTxs int // count txs in a block + nTxs int // count txs in a block + logger loggers.InfoTraceLogger } // NOTE [ben] Compiler check to ensure ErisMint successfully implements // eris-db/manager/types.Application var _ manager_types.Application = (*ErisMint)(nil) -// NOTE: [ben] also automatically implements tmsp.Application, +// NOTE: [ben] also automatically implements abci.Application, // undesired but unharmful -// var _ tmsp.Application = (*ErisMint)(nil) +// var _ abci.Application = (*ErisMint)(nil) func (app *ErisMint) GetState() *sm.State { app.mtx.Lock() @@ -72,19 +72,20 @@ func (app *ErisMint) GetCheckCache() *sm.BlockCache { return app.checkCache } -func NewErisMint(s *sm.State, evsw *tendermint_events.EventSwitch) *ErisMint { +func NewErisMint(s *sm.State, evsw tendermint_events.EventSwitch, logger loggers.InfoTraceLogger) *ErisMint { return &ErisMint{ state: s, cache: sm.NewBlockCache(s), checkCache: sm.NewBlockCache(s), evc: tendermint_events.NewEventCache(evsw), evsw: evsw, + logger: logging.WithScope(logger, "ErisMint"), } } // Implements manager/types.Application -func (app *ErisMint) Info() (info string) { - return "ErisDB" +func (app *ErisMint) Info() (info abci.ResponseInfo) { + return abci.ResponseInfo{} } // Implements manager/types.Application @@ -93,7 +94,7 @@ func (app *ErisMint) SetOption(key string, value string) (log string) { } // Implements manager/types.Application -func (app *ErisMint) AppendTx(txBytes []byte) tmsp.Result { +func (app *ErisMint) DeliverTx(txBytes []byte) abci.Result { app.nTxs += 1 // XXX: if we had tx ids we could cache the decoded txs on CheckTx @@ -103,61 +104,60 @@ func (app *ErisMint) AppendTx(txBytes []byte) tmsp.Result { buf := bytes.NewBuffer(txBytes) wire.ReadBinaryPtr(tx, buf, len(txBytes), &n, &err) if err != nil { - return tmsp.NewError(tmsp.CodeType_EncodingError, fmt.Sprintf("Encoding error: %v", err)) + return abci.NewError(abci.CodeType_EncodingError, fmt.Sprintf("Encoding error: %v", err)) } err = sm.ExecTx(app.cache, *tx, true, app.evc) if err != nil { - return tmsp.NewError(tmsp.CodeType_InternalError, fmt.Sprintf("Internal error: %v", err)) + return abci.NewError(abci.CodeType_InternalError, fmt.Sprintf("Internal error: %v", err)) } receipt := txs.GenerateReceipt(app.state.ChainID, *tx) receiptBytes := wire.BinaryBytes(receipt) - return tmsp.NewResultOK(receiptBytes, "Success") + return abci.NewResultOK(receiptBytes, "Success") } // Implements manager/types.Application -func (app *ErisMint) CheckTx(txBytes []byte) tmsp.Result { +func (app *ErisMint) CheckTx(txBytes []byte) abci.Result { var n int var err error tx := new(txs.Tx) buf := bytes.NewBuffer(txBytes) wire.ReadBinaryPtr(tx, buf, len(txBytes), &n, &err) if err != nil { - return tmsp.NewError(tmsp.CodeType_EncodingError, fmt.Sprintf("Encoding error: %v", err)) + return abci.NewError(abci.CodeType_EncodingError, fmt.Sprintf("Encoding error: %v", err)) } - // TODO: map ExecTx errors to sensible TMSP error codes + // TODO: map ExecTx errors to sensible abci error codes err = sm.ExecTx(app.checkCache, *tx, false, nil) if err != nil { - return tmsp.NewError(tmsp.CodeType_InternalError, fmt.Sprintf("Internal error: %v", err)) + return abci.NewError(abci.CodeType_InternalError, fmt.Sprintf("Internal error: %v", err)) } receipt := txs.GenerateReceipt(app.state.ChainID, *tx) receiptBytes := wire.BinaryBytes(receipt) - return tmsp.NewResultOK(receiptBytes, "Success") + return abci.NewResultOK(receiptBytes, "Success") } // Implements manager/types.Application // Commit the state (called at end of block) // NOTE: CheckTx/AppendTx must not run concurrently with Commit - // the mempool should run during AppendTxs, but lock for Commit and Update -func (app *ErisMint) Commit() (res tmsp.Result) { +func (app *ErisMint) Commit() (res abci.Result) { app.mtx.Lock() // the lock protects app.state defer app.mtx.Unlock() app.state.LastBlockHeight += 1 - log.WithFields(log.Fields{ - "blockheight": app.state.LastBlockHeight, - }).Info("Commit block") + logging.InfoMsg(app.logger, "Committing block", + "last_block_height", app.state.LastBlockHeight) // sync the AppendTx cache app.cache.Sync() // Refresh the checkCache with the latest commited state - log.WithFields(log.Fields{ - "txs": app.nTxs, - }).Info("Reset checkCache") + logging.InfoMsg(app.logger, "Resetting checkCache", + "txs", app.nTxs) app.checkCache = sm.NewBlockCache(app.state) + app.nTxs = 0 // save state to disk @@ -166,17 +166,16 @@ func (app *ErisMint) Commit() (res tmsp.Result) { // flush events to listeners (XXX: note issue with blocking) app.evc.Flush() - // MARMOT: - // set internal time as two seconds per block + // TODO: [ben] over the tendermint 0.6 TMSP interface we have + // no access to the block header implemented; + // On Tendermint v0.8 load the blockheader into the application + // state and remove the fixed 2-"seconds" per block internal clock. + // NOTE: set internal time as two seconds per block app.state.LastBlockTime = app.state.LastBlockTime.Add(time.Duration(2) * time.Second) - fmt.Printf("\n\nMARMOT TIME: %s\n\n", app.state.LastBlockTime) - // MARMOT: appHash := app.state.Hash() - fmt.Printf("\n\nMARMOT COMMIT: %X\n\n", appHash) - // return tmsp.NewResultOK(app.state.Hash(), "Success") - return tmsp.NewResultOK(appHash, "Success") + return abci.NewResultOK(appHash, "Success") } -func (app *ErisMint) Query(query []byte) (res tmsp.Result) { - return tmsp.NewResultOK(nil, "Success") +func (app *ErisMint) Query(query []byte) (res abci.Result) { + return abci.NewResultOK(nil, "Success") } diff --git a/manager/eris-mint/eris-mint_test.go b/manager/eris-mint/eris-mint_test.go index 294c34e23..b37c89532 100644 --- a/manager/eris-mint/eris-mint_test.go +++ b/manager/eris-mint/eris-mint_test.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 erismint diff --git a/manager/eris-mint/evm/abi/types.go b/manager/eris-mint/evm/abi/types.go new file mode 100644 index 000000000..09ac0e4c3 --- /dev/null +++ b/manager/eris-mint/evm/abi/types.go @@ -0,0 +1,53 @@ +// 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 abi + +// Ethereum defines types and calling conventions for the ABI +// (application binary interface) here: https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI +// We make a start of representing them here + +// We use TypeName rather than Type to reserve 'Type' for a possible future +// ABI type the can hold an ABI-native type mapping +type TypeName string + +type Arg struct { + Name string + TypeName TypeName +} + +type Return struct { + Name string + TypeName TypeName +} + +const ( + // We don't need to be exhaustive here, just make what we used strongly typed + AddressTypeName TypeName = "address" + IntTypeName TypeName = "int" + Uint64TypeName TypeName = "uint64" + Bytes32TypeName TypeName = "bytes32" + StringTypeName TypeName = "string" + BoolTypeName TypeName = "bool" +) + +const ( + FunctionSelectorLength = 4 + AddressLength = 20 +) + +type ( + Address [AddressLength]byte + FunctionSelector [FunctionSelectorLength]byte +) diff --git a/manager/eris-mint/evm/common.go b/manager/eris-mint/evm/common.go index 00ab93c77..fbbd4c51a 100644 --- a/manager/eris-mint/evm/common.go +++ b/manager/eris-mint/evm/common.go @@ -1,3 +1,17 @@ +// 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 vm import ( diff --git a/manager/eris-mint/evm/test/fake_app_state.go b/manager/eris-mint/evm/fake_app_state.go similarity index 64% rename from manager/eris-mint/evm/test/fake_app_state.go rename to manager/eris-mint/evm/fake_app_state.go index c27e54256..3c19b0cf2 100644 --- a/manager/eris-mint/evm/test/fake_app_state.go +++ b/manager/eris-mint/evm/fake_app_state.go @@ -1,9 +1,24 @@ +// 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 vm import ( - . "github.com/eris-ltd/eris-db/manager/eris-mint/evm" + "fmt" + "github.com/eris-ltd/eris-db/manager/eris-mint/evm/sha3" - . "github.com/tendermint/go-common" + . "github.com/eris-ltd/eris-db/word256" ) type FakeAppState struct { @@ -23,7 +38,7 @@ func (fas *FakeAppState) UpdateAccount(account *Account) { func (fas *FakeAppState) RemoveAccount(account *Account) { _, ok := fas.accounts[account.Address.String()] if !ok { - panic(Fmt("Invalid account addr: %X", account.Address)) + panic(fmt.Sprintf("Invalid account addr: %X", account.Address)) } else { // Remove account delete(fas.accounts, account.Address.String()) @@ -41,14 +56,14 @@ func (fas *FakeAppState) CreateAccount(creator *Account) *Account { Nonce: 0, } } else { - panic(Fmt("Invalid account addr: %X", addr)) + panic(fmt.Sprintf("Invalid account addr: %X", addr)) } } func (fas *FakeAppState) GetStorage(addr Word256, key Word256) Word256 { _, ok := fas.accounts[addr.String()] if !ok { - panic(Fmt("Invalid account addr: %X", addr)) + panic(fmt.Sprintf("Invalid account addr: %X", addr)) } value, ok := fas.storage[addr.String()+key.String()] @@ -62,7 +77,7 @@ func (fas *FakeAppState) GetStorage(addr Word256, key Word256) Word256 { func (fas *FakeAppState) SetStorage(addr Word256, key Word256, value Word256) { _, ok := fas.accounts[addr.String()] if !ok { - panic(Fmt("Invalid account addr: %X", addr)) + panic(fmt.Sprintf("Invalid account addr: %X", addr)) } fas.storage[addr.String()+key.String()] = value diff --git a/manager/eris-mint/evm/gas.go b/manager/eris-mint/evm/gas.go index fe4a43d80..9ffc5e56d 100644 --- a/manager/eris-mint/evm/gas.go +++ b/manager/eris-mint/evm/gas.go @@ -1,3 +1,17 @@ +// 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 vm const ( diff --git a/manager/eris-mint/evm/log.go b/manager/eris-mint/evm/log.go index 82880ae97..8b5565197 100644 --- a/manager/eris-mint/evm/log.go +++ b/manager/eris-mint/evm/log.go @@ -1,3 +1,17 @@ +// 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 vm import ( diff --git a/manager/eris-mint/evm/test/log_event_test.go b/manager/eris-mint/evm/log_event_test.go similarity index 77% rename from manager/eris-mint/evm/test/log_event_test.go rename to manager/eris-mint/evm/log_event_test.go index 5f1eb2f46..bdb55fb9e 100644 --- a/manager/eris-mint/evm/test/log_event_test.go +++ b/manager/eris-mint/evm/log_event_test.go @@ -1,3 +1,17 @@ +// 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 vm import ( @@ -5,10 +19,9 @@ import ( "reflect" "testing" - . "github.com/eris-ltd/eris-db/manager/eris-mint/evm" . "github.com/eris-ltd/eris-db/manager/eris-mint/evm/opcodes" "github.com/eris-ltd/eris-db/txs" - . "github.com/tendermint/go-common" + . "github.com/eris-ltd/eris-db/word256" "github.com/tendermint/go-events" ) diff --git a/manager/eris-mint/evm/native.go b/manager/eris-mint/evm/native.go index 860f99bd2..e56947458 100644 --- a/manager/eris-mint/evm/native.go +++ b/manager/eris-mint/evm/native.go @@ -1,11 +1,25 @@ +// 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 vm import ( "crypto/sha256" - "golang.org/x/crypto/ripemd160" + . "github.com/eris-ltd/eris-db/word256" - . "github.com/tendermint/go-common" + "golang.org/x/crypto/ripemd160" ) var registeredNativeContracts = make(map[Word256]NativeContract) diff --git a/manager/eris-mint/evm/opcodes/opcodes.go b/manager/eris-mint/evm/opcodes/opcodes.go index b4205f2fe..ce064f213 100644 --- a/manager/eris-mint/evm/opcodes/opcodes.go +++ b/manager/eris-mint/evm/opcodes/opcodes.go @@ -1,8 +1,23 @@ +// 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 opcodes import ( "fmt" + "github.com/eris-ltd/eris-db/word256" "gopkg.in/fatih/set.v0" ) @@ -378,6 +393,8 @@ func Bytecode(bytelikes ...interface{}) []byte { if int64(bytes[i]) != b { panic(fmt.Sprintf("The int64 %v does not fit inside a byte", b)) } + case word256.Word256: + return Concat(bytes[:i], b[:], Bytecode(bytelikes[i+1:]...)) case []byte: // splice return Concat(bytes[:i], b, Bytecode(bytelikes[i+1:]...)) diff --git a/manager/eris-mint/evm/sha3/sha3.go b/manager/eris-mint/evm/sha3/sha3.go index da6b381f4..6f262f217 100644 --- a/manager/eris-mint/evm/sha3/sha3.go +++ b/manager/eris-mint/evm/sha3/sha3.go @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +package sha3 + // Package sha3 implements the SHA3 hash algorithm (formerly called Keccak) chosen by NIST in 2012. // This file provides a SHA3 implementation which implements the standard hash.Hash interface. // Writing input data, including padding, and reading output data are computed in this file. @@ -9,7 +11,6 @@ // This is a consequence of the hash interface in which a buffer of bytes is passed in. // The internals of the Keccak-f function are computed in keccakf.go. // For the detailed specification, refer to the Keccak web site (http://keccak.noekeon.org/). -package sha3 import ( "encoding/binary" diff --git a/manager/eris-mint/evm/snative.go b/manager/eris-mint/evm/snative.go index b0a7385e3..b30858109 100644 --- a/manager/eris-mint/evm/snative.go +++ b/manager/eris-mint/evm/snative.go @@ -1,98 +1,347 @@ +// 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 vm import ( - "encoding/hex" "fmt" + "github.com/eris-ltd/eris-db/common/sanity" + "github.com/eris-ltd/eris-db/manager/eris-mint/evm/sha3" ptypes "github.com/eris-ltd/eris-db/permission/types" - . "github.com/tendermint/go-common" + . "github.com/eris-ltd/eris-db/word256" + + "strings" + + "github.com/eris-ltd/eris-db/manager/eris-mint/evm/abi" ) -//------------------------------------------------------------------------------------------------ -// Registered SNative contracts +// +// SNative (from 'secure natives') are native (go) contracts that are dispatched +// based on account permissions and can access and modify an account's permissions +// + +// Metadata for SNative contract. Acts as a call target from the EVM. Can be +// used to generate bindings in a smart contract languages. +type SNativeContractDescription struct { + // Comment describing purpose of SNative contract and reason for assembling + // the particular functions + Comment string + // Name of the SNative contract + Name string + functionsByID map[abi.FunctionSelector]*SNativeFunctionDescription + functions []*SNativeFunctionDescription +} -var PermissionsContract = "permissions_contract" +// Metadata for SNative functions. Act as call targets for the EVM when +// collected into an SNativeContractDescription. Can be used to generate +// bindings in a smart contract languages. +type SNativeFunctionDescription struct { + // Comment describing function's purpose, parameters, and return value + Comment string + // Function name (used to form signature) + Name string + // Function arguments (used to form signature) + Args []abi.Arg + // Function return value + Return abi.Return + // Permissions required to call function + PermFlag ptypes.PermFlag + // Native function to which calls will be dispatched when a containing + // contract is called with a FunctionSelector matching this NativeContract + F NativeContract +} func registerSNativeContracts() { - registeredNativeContracts[LeftPadWord256([]byte(PermissionsContract))] = permissionsContract - - /* - // we could expose these but we moved permission and args checks into the permissionsContract - // so calling them would be unsafe ... - registeredNativeContracts[LeftPadWord256([]byte("has_base"))] = has_base - registeredNativeContracts[LeftPadWord256([]byte("set_base"))] = set_base - registeredNativeContracts[LeftPadWord256([]byte("unset_base"))] = unset_base - registeredNativeContracts[LeftPadWord256([]byte("set_global"))] = set_global - registeredNativeContracts[LeftPadWord256([]byte("has_role"))] = has_role - registeredNativeContracts[LeftPadWord256([]byte("add_role"))] = add_role - registeredNativeContracts[LeftPadWord256([]byte("rm_role"))] = rm_role - */ -} - -//----------------------------------------------------------------------------- -// snative are native contracts that can access and modify an account's permissions - -type SNativeFuncDescription struct { - Name string - NArgs int - PermFlag ptypes.PermFlag - F NativeContract -} - -/* The solidity interface used to generate the abi function ids below -contract Permissions { - function has_base(address addr, int permFlag) constant returns (bool value) {} - function set_base(address addr, int permFlag, bool value) constant returns (bool val) {} - function unset_base(address addr, int permFlag) constant returns (int pf) {} - function set_global(address addr, int permFlag, bool value) constant returns (int pf) {} - function has_role(address addr, string role) constant returns (bool val) {} - function add_role(address addr, string role) constant returns (bool added) {} - function rm_role(address addr, string role) constant returns (bool removed) {} -} -*/ - -// function identifiers from the solidity abi -var PermsMap = map[string]SNativeFuncDescription{ - "e8145855": SNativeFuncDescription{"has_role", 2, ptypes.HasRole, has_role}, - "180d26f2": SNativeFuncDescription{"unset_base", 2, ptypes.UnsetBase, unset_base}, - "3a3fcc59": SNativeFuncDescription{"set_global", 2, ptypes.SetGlobal, set_global}, - "3fbf7da5": SNativeFuncDescription{"add_role", 2, ptypes.AddRole, add_role}, - "9ea53314": SNativeFuncDescription{"set_base", 3, ptypes.SetBase, set_base}, - "bb37737a": SNativeFuncDescription{"has_base", 2, ptypes.HasBase, has_base}, - "28fd0194": SNativeFuncDescription{"rm_role", 2, ptypes.RmRole, rm_role}, -} - -func permissionsContract(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { - if len(args) < 4 { - return nil, fmt.Errorf("permissionsContract expects at least a 4-byte function identifier") - } - - // map solidity abi function id to snative - funcIDbytes := args[:4] - args = args[4:] - funcID := hex.EncodeToString(funcIDbytes) - d, ok := PermsMap[funcID] - if !ok { - return nil, fmt.Errorf("unknown permissionsContract funcID %s", funcID) + for _, contract := range SNativeContracts() { + registeredNativeContracts[contract.AddressWord256()] = contract.Dispatch + } +} + +// Returns a map of all SNative contracts defined indexed by name +func SNativeContracts() map[string]*SNativeContractDescription { + permFlagTypeName := abi.Uint64TypeName + roleTypeName := abi.Bytes32TypeName + contracts := []*SNativeContractDescription{ + NewSNativeContract(` + * Interface for managing Secure Native authorizations. + * @dev This interface describes the functions exposed by the SNative permissions layer in the Monax blockchain (ErisDB). + `, + "Permissions", + &SNativeFunctionDescription{` + * @notice Adds a role to an account + * @param _account account address + * @param _role role name + * @return result whether role was added + `, + "addRole", + []abi.Arg{ + arg("_account", abi.AddressTypeName), + arg("_role", roleTypeName), + }, + ret("result", abi.BoolTypeName), + ptypes.AddRole, + addRole}, + + &SNativeFunctionDescription{` + * @notice Removes a role from an account + * @param _account account address + * @param _role role name + * @return result whether role was removed + `, + "removeRole", + []abi.Arg{ + arg("_account", abi.AddressTypeName), + arg("_role", roleTypeName), + }, + ret("result", abi.BoolTypeName), + ptypes.RmRole, + removeRole}, + + &SNativeFunctionDescription{` + * @notice Indicates whether an account has a role + * @param _account account address + * @param _role role name + * @return result whether account has role + `, + "hasRole", + []abi.Arg{ + arg("_account", abi.AddressTypeName), + arg("_role", roleTypeName), + }, + ret("result", abi.BoolTypeName), + ptypes.HasRole, + hasRole}, + + &SNativeFunctionDescription{` + * @notice Sets the permission flags for an account. Makes them explicitly set (on or off). + * @param _account account address + * @param _permission the base permissions flags to set for the account + * @param _set whether to set or unset the permissions flags at the account level + * @return result the effective permissions flags on the account after the call + `, + "setBase", + []abi.Arg{ + arg("_account", abi.AddressTypeName), + arg("_permission", permFlagTypeName), + arg("_set", abi.BoolTypeName), + }, + ret("result", permFlagTypeName), + ptypes.SetBase, + setBase}, + + &SNativeFunctionDescription{` + * @notice Unsets the permissions flags for an account. Causes permissions being unset to fall through to global permissions. + * @param _account account address + * @param _permission the permissions flags to unset for the account + * @return result the effective permissions flags on the account after the call + `, + "unsetBase", + []abi.Arg{ + arg("_account", abi.AddressTypeName), + arg("_permission", permFlagTypeName)}, + ret("result", permFlagTypeName), + ptypes.UnsetBase, + unsetBase}, + + &SNativeFunctionDescription{` + * @notice Indicates whether an account has a subset of permissions set + * @param _account account address + * @param _permission the permissions flags (mask) to check whether enabled against base permissions for the account + * @return result whether account has the passed permissions flags set + `, + "hasBase", + []abi.Arg{ + arg("_account", abi.AddressTypeName), + arg("_permission", permFlagTypeName)}, + ret("result", permFlagTypeName), + ptypes.HasBase, + hasBase}, + + &SNativeFunctionDescription{` + * @notice Sets the global (default) permissions flags for the entire chain + * @param _permission the permissions flags to set + * @param _set whether to set (or unset) the permissions flags + * @return result the global permissions flags after the call + `, + "setGlobal", + []abi.Arg{ + arg("_permission", permFlagTypeName), + arg("_set", abi.BoolTypeName)}, + ret("result", permFlagTypeName), + ptypes.SetGlobal, + setGlobal}, + ), } + contractMap := make(map[string]*SNativeContractDescription, len(contracts)) + for _, contract := range contracts { + if _, ok := contractMap[contract.Name]; ok { + // If this happens we have a pseudo compile time error that will be caught + // on native.go init() + panic(fmt.Errorf("Duplicate contract with name %s defined. "+ + "Contract names must be unique.", contract.Name)) + } + contractMap[contract.Name] = contract + } + return contractMap +} + +// Create a new SNative contract description object by passing a comment, name +// and a list of member functions descriptions +func NewSNativeContract(comment, name string, + functions ...*SNativeFunctionDescription) *SNativeContractDescription { + + functionsByID := make(map[abi.FunctionSelector]*SNativeFunctionDescription, len(functions)) + for _, f := range functions { + fid := f.ID() + otherF, ok := functionsByID[fid] + if ok { + panic(fmt.Errorf("Function with ID %x already defined: %s", fid, + otherF)) + } + functionsByID[fid] = f + } + return &SNativeContractDescription{ + Comment: comment, + Name: name, + functionsByID: functionsByID, + functions: functions, + } +} + +// This function is designed to be called from the EVM once a SNative contract +// has been selected. It is also placed in a registry by registerSNativeContracts +// So it can be looked up by SNative address +func (contract *SNativeContractDescription) Dispatch(appState AppState, + caller *Account, args []byte, gas *int64) (output []byte, err error) { + if len(args) < abi.FunctionSelectorLength { + return nil, fmt.Errorf("SNatives dispatch requires a 4-byte function "+ + "identifier but arguments are only %s bytes long", len(args)) + } + + function, err := contract.FunctionByID(firstFourBytes(args)) + if err != nil { + return nil, err + } + + remainingArgs := args[abi.FunctionSelectorLength:] + // check if we have permission to call this function - if !HasPermission(appState, caller, d.PermFlag) { - return nil, ErrInvalidPermission{caller.Address, d.Name} + if !HasPermission(appState, caller, function.PermFlag) { + return nil, ErrInvalidPermission{caller.Address, function.Name} } // ensure there are enough arguments - if len(args) != d.NArgs*32 { - return nil, fmt.Errorf("%s() takes %d arguments", d.Name) + if len(remainingArgs) != function.NArgs()*Word256Length { + return nil, fmt.Errorf("%s() takes %d arguments", function.Name, + function.NArgs()) } // call the function - return d.F(appState, caller, args, gas) + return function.F(appState, caller, remainingArgs, gas) } -// TODO: catch errors, log em, return 0s to the vm (should some errors cause exceptions though?) +// We define the address of an SNative contact as the last 20 bytes of the sha3 +// hash of its name +func (contract *SNativeContractDescription) Address() abi.Address { + var address abi.Address + hash := sha3.Sha3([]byte(contract.Name)) + copy(address[:], hash[len(hash)-abi.AddressLength:]) + return address +} + +// Get address as a byte slice +func (contract *SNativeContractDescription) AddressBytes() []byte { + address := contract.Address() + return address[:] +} -func has_base(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { +// Get address as a left-padded Word256 +func (contract *SNativeContractDescription) AddressWord256() Word256 { + return LeftPadWord256(contract.AddressBytes()) +} + +// Get function by calling identifier FunctionSelector +func (contract *SNativeContractDescription) FunctionByID(id abi.FunctionSelector) (*SNativeFunctionDescription, error) { + f, ok := contract.functionsByID[id] + if !ok { + return nil, + fmt.Errorf("Unknown SNative function with ID %x", id) + } + return f, nil +} + +// Get function by name +func (contract *SNativeContractDescription) FunctionByName(name string) (*SNativeFunctionDescription, error) { + for _, f := range contract.functions { + if f.Name == name { + return f, nil + } + } + return nil, fmt.Errorf("Unknown SNative function with name %s", name) +} + +// Get functions in order of declaration +func (contract *SNativeContractDescription) Functions() []*SNativeFunctionDescription { + functions := make([]*SNativeFunctionDescription, len(contract.functions)) + copy(functions, contract.functions) + return functions +} + +// +// SNative functions +// + +// Get function signature +func (function *SNativeFunctionDescription) Signature() string { + argTypeNames := make([]string, len(function.Args)) + for i, arg := range function.Args { + argTypeNames[i] = string(arg.TypeName) + } + return fmt.Sprintf("%s(%s)", function.Name, + strings.Join(argTypeNames, ",")) +} + +// Get function calling identifier FunctionSelector +func (function *SNativeFunctionDescription) ID() abi.FunctionSelector { + return firstFourBytes(sha3.Sha3([]byte(function.Signature()))) +} + +// Get number of function arguments +func (function *SNativeFunctionDescription) NArgs() int { + return len(function.Args) +} + +func arg(name string, abiTypeName abi.TypeName) abi.Arg { + return abi.Arg{ + Name: name, + TypeName: abiTypeName, + } +} + +func ret(name string, abiTypeName abi.TypeName) abi.Return { + return abi.Return{ + Name: name, + TypeName: abiTypeName, + } +} + +// Permission function defintions + +// TODO: catch errors, log em, return 0s to the vm (should some errors cause exceptions though?) +func hasBase(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { addr, permNum := returnTwoArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { @@ -107,8 +356,8 @@ func has_base(appState AppState, caller *Account, args []byte, gas *int64) (outp return LeftPadWord256([]byte{permInt}).Bytes(), nil } -func set_base(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { - addr, permNum, perm := returnThreeArgs(args) +func setBase(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { + addr, permNum, permVal := returnThreeArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { return nil, fmt.Errorf("Unknown account %X", addr) @@ -117,16 +366,16 @@ func set_base(appState AppState, caller *Account, args []byte, gas *int64) (outp if !ValidPermN(permN) { return nil, ptypes.ErrInvalidPermission(permN) } - permV := !perm.IsZero() + permV := !permVal.IsZero() if err = vmAcc.Permissions.Base.Set(permN, permV); err != nil { return nil, err } appState.UpdateAccount(vmAcc) dbg.Printf("snative.setBasePerm(0x%X, %b, %v)\n", addr.Postfix(20), permN, permV) - return perm.Bytes(), nil + return effectivePermBytes(vmAcc.Permissions.Base, globalPerms(appState)), nil } -func unset_base(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { +func unsetBase(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { addr, permNum := returnTwoArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { @@ -141,29 +390,29 @@ func unset_base(appState AppState, caller *Account, args []byte, gas *int64) (ou } appState.UpdateAccount(vmAcc) dbg.Printf("snative.unsetBasePerm(0x%X, %b)\n", addr.Postfix(20), permN) - return permNum.Bytes(), nil + return effectivePermBytes(vmAcc.Permissions.Base, globalPerms(appState)), nil } -func set_global(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { - permNum, perm := returnTwoArgs(args) +func setGlobal(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { + permNum, permVal := returnTwoArgs(args) vmAcc := appState.GetAccount(ptypes.GlobalPermissionsAddress256) if vmAcc == nil { - PanicSanity("cant find the global permissions account") + sanity.PanicSanity("cant find the global permissions account") } permN := ptypes.PermFlag(Uint64FromWord256(permNum)) if !ValidPermN(permN) { return nil, ptypes.ErrInvalidPermission(permN) } - permV := !perm.IsZero() + permV := !permVal.IsZero() if err = vmAcc.Permissions.Base.Set(permN, permV); err != nil { return nil, err } appState.UpdateAccount(vmAcc) dbg.Printf("snative.setGlobalPerm(%b, %v)\n", permN, permV) - return perm.Bytes(), nil + return permBytes(vmAcc.Permissions.Base.ResultantPerms()), nil } -func has_role(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { +func hasRole(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { addr, role := returnTwoArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { @@ -175,7 +424,7 @@ func has_role(appState AppState, caller *Account, args []byte, gas *int64) (outp return LeftPadWord256([]byte{permInt}).Bytes(), nil } -func add_role(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { +func addRole(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { addr, role := returnTwoArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { @@ -188,7 +437,7 @@ func add_role(appState AppState, caller *Account, args []byte, gas *int64) (outp return LeftPadWord256([]byte{permInt}).Bytes(), nil } -func rm_role(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { +func removeRole(appState AppState, caller *Account, args []byte, gas *int64) (output []byte, err error) { addr, role := returnTwoArgs(args) vmAcc := appState.GetAccount(addr) if vmAcc == nil { @@ -221,6 +470,26 @@ func ValidPermN(n ptypes.PermFlag) bool { return true } +// Get the global BasePermissions +func globalPerms(appState AppState) ptypes.BasePermissions { + vmAcc := appState.GetAccount(ptypes.GlobalPermissionsAddress256) + if vmAcc == nil { + sanity.PanicSanity("cant find the global permissions account") + } + return vmAcc.Permissions.Base +} + +// Compute the effective permissions from an Account's BasePermissions by +// taking the bitwise or with the global BasePermissions resultant permissions +func effectivePermBytes(basePerms ptypes.BasePermissions, + globalPerms ptypes.BasePermissions) []byte { + return permBytes(basePerms.ResultantPerms() | globalPerms.ResultantPerms()) +} + +func permBytes(basePerms ptypes.PermFlag) []byte { + return Uint64ToWord256(uint64(basePerms)).Bytes() +} + // CONTRACT: length has already been checked func returnTwoArgs(args []byte) (a Word256, b Word256) { copy(a[:], args[:32]) @@ -242,3 +511,9 @@ func byteFromBool(b bool) byte { } return 0x0 } + +func firstFourBytes(byteSlice []byte) [4]byte { + var bs [4]byte + copy(bs[:], byteSlice[:4]) + return bs +} diff --git a/manager/eris-mint/evm/snative_test.go b/manager/eris-mint/evm/snative_test.go new file mode 100644 index 000000000..a60b62e15 --- /dev/null +++ b/manager/eris-mint/evm/snative_test.go @@ -0,0 +1,157 @@ +// 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 vm + +import ( + "encoding/hex" + "testing" + + "strings" + + "github.com/eris-ltd/eris-db/manager/eris-mint/evm/abi" + . "github.com/eris-ltd/eris-db/manager/eris-mint/evm/opcodes" + "github.com/eris-ltd/eris-db/manager/eris-mint/evm/sha3" + ptypes "github.com/eris-ltd/eris-db/permission/types" + . "github.com/eris-ltd/eris-db/word256" + "github.com/stretchr/testify/assert" +) + +// Compiling the Permissions solidity contract at +// (generated by with 'make snatives' function) and passing to +// https://ethereum.github.io/browser-solidity (toggle details to get list) +// yields: +// Keep this updated to drive TestPermissionsContractSignatures +const compiledSigs = ` +a73f7f8a addRole(address,bytes32) +225b6574 hasBase(address,uint64) +ac4ab3fb hasRole(address,bytes32) +6853920e removeRole(address,bytes32) +dbd4a8ea setBase(address,uint64,bool) +c4bc7b70 setGlobal(uint64,bool) +b7d4dc0d unsetBase(address,uint64) +` + +func TestPermissionsContractSignatures(t *testing.T) { + contract := SNativeContracts()["Permissions"] + + nFuncs := len(contract.functions) + + sigMap := idToSignatureMap() + + assert.Len(t, sigMap, nFuncs, + "Permissions contract defines %s functions so we need %s "+ + "signatures in compiledSigs", + nFuncs, nFuncs) + + for funcID, signature := range sigMap { + assertFunctionIDSignature(t, contract, funcID, signature) + } +} + +func TestSNativeContractDescription_Dispatch(t *testing.T) { + contract := SNativeContracts()["Permissions"] + state := newAppState() + caller := &Account{ + Address: addr(1, 1, 1), + } + grantee := &Account{ + Address: addr(2, 2, 2), + } + state.UpdateAccount(grantee) + + function, err := contract.FunctionByName("addRole") + if err != nil { + t.Fatalf("Could not get function: %s", err) + } + funcID := function.ID() + gas := int64(1000) + + // Should fail since we have no permissions + retValue, err := contract.Dispatch(state, caller, Bytecode(funcID[:], + grantee.Address, permFlagToWord256(ptypes.CreateAccount)), &gas) + assert.Error(t, err) + if err != nil { + assert.Contains(t, err.Error(), "does not have permission") + } + + // Grant all permissions and dispatch should success + caller.Permissions = allAccountPermissions() + retValue, err = contract.Dispatch(state, caller, Bytecode(funcID[:], + grantee.Address, permFlagToWord256(ptypes.CreateAccount)), &gas) + assert.NoError(t, err) + assert.Equal(t, retValue, LeftPadBytes([]byte{1}, 32)) +} + +func TestSNativeContractDescription_Address(t *testing.T) { + contract := NewSNativeContract("A comment", + "CoolButVeryLongNamedContractOfDoom") + assert.Equal(t, sha3.Sha3(([]byte)(contract.Name))[12:], contract.AddressBytes()) +} + +// +// Helpers +// +func assertFunctionIDSignature(t *testing.T, contract *SNativeContractDescription, + funcIDHex string, expectedSignature string) { + function, err := contract.FunctionByID(funcIDFromHex(t, funcIDHex)) + assert.NoError(t, err, + "Error retrieving SNativeFunctionDescription with ID %s", funcIDHex) + if err == nil { + assert.Equal(t, expectedSignature, function.Signature()) + } +} + +func funcIDFromHex(t *testing.T, hexString string) abi.FunctionSelector { + bs, err := hex.DecodeString(hexString) + assert.NoError(t, err, "Could not decode hex string '%s'", hexString) + if len(bs) != 4 { + t.Fatalf("FunctionSelector must be 4 bytes but '%s' is %v bytes", hexString, + len(bs)) + } + return firstFourBytes(bs) +} + +func permFlagToWord256(permFlag ptypes.PermFlag) Word256 { + return Uint64ToWord256(uint64(permFlag)) +} + +func addr(rightBytes ...uint8) Word256 { + return LeftPadWord256(rightBytes) +} + +func allAccountPermissions() ptypes.AccountPermissions { + return ptypes.AccountPermissions{ + Base: ptypes.BasePermissions{ + Perms: ptypes.AllPermFlags, + SetBit: ptypes.AllPermFlags, + }, + Roles: []string{}, + } +} + +// turns the solidity compiler function summary into a map to drive signature +// test +func idToSignatureMap() map[string]string { + sigMap := make(map[string]string) + lines := strings.Split(compiledSigs, "\n") + for _, line := range lines { + trimmed := strings.Trim(line, " \t") + if trimmed != "" { + idSig := strings.Split(trimmed, " ") + sigMap[idSig[0]] = idSig[1] + } + } + return sigMap +} diff --git a/manager/eris-mint/evm/stack.go b/manager/eris-mint/evm/stack.go index 3f3d727f1..8e5df4327 100644 --- a/manager/eris-mint/evm/stack.go +++ b/manager/eris-mint/evm/stack.go @@ -1,9 +1,25 @@ +// 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 vm import ( "fmt" - . "github.com/tendermint/go-common" + "github.com/eris-ltd/eris-db/common/math/integral" + "github.com/eris-ltd/eris-db/common/sanity" + . "github.com/eris-ltd/eris-db/word256" ) // Not goroutine safe @@ -51,7 +67,7 @@ func (st *Stack) Push(d Word256) { // currently only called after Sha3 func (st *Stack) PushBytes(bz []byte) { if len(bz) != 32 { - PanicSanity("Invalid bytes size: expected 32") + sanity.PanicSanity("Invalid bytes size: expected 32") } st.Push(LeftPadWord256(bz)) } @@ -115,7 +131,7 @@ func (st *Stack) Peek() Word256 { func (st *Stack) Print(n int) { fmt.Println("### stack ###") if st.ptr > 0 { - nn := MinInt(n, st.ptr) + nn := integral.MinInt(n, st.ptr) for j, i := 0, st.ptr-1; i > st.ptr-1-nn; i-- { fmt.Printf("%-3d %X\n", j, st.data[i]) j += 1 diff --git a/manager/eris-mint/evm/types.go b/manager/eris-mint/evm/types.go index a886546e3..cd5d8ce04 100644 --- a/manager/eris-mint/evm/types.go +++ b/manager/eris-mint/evm/types.go @@ -1,8 +1,24 @@ +// 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 vm import ( + "fmt" + ptypes "github.com/eris-ltd/eris-db/permission/types" - . "github.com/tendermint/go-common" + . "github.com/eris-ltd/eris-db/word256" ) const ( @@ -23,7 +39,7 @@ func (acc *Account) String() string { if acc == nil { return "nil-VMAccount" } - return Fmt("VMAccount{%X B:%v C:%X N:%v}", + return fmt.Sprintf("VMAccount{%X B:%v C:%X N:%v}", acc.Address, acc.Balance, acc.Code, acc.Nonce) } diff --git a/manager/eris-mint/evm/vm.go b/manager/eris-mint/evm/vm.go index 32b7b4fab..ea883a632 100644 --- a/manager/eris-mint/evm/vm.go +++ b/manager/eris-mint/evm/vm.go @@ -1,3 +1,17 @@ +// 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 vm import ( @@ -6,27 +20,31 @@ import ( "fmt" "math/big" + "github.com/eris-ltd/eris-db/common/math/integral" + "github.com/eris-ltd/eris-db/common/sanity" . "github.com/eris-ltd/eris-db/manager/eris-mint/evm/opcodes" "github.com/eris-ltd/eris-db/manager/eris-mint/evm/sha3" ptypes "github.com/eris-ltd/eris-db/permission/types" "github.com/eris-ltd/eris-db/txs" - . "github.com/tendermint/go-common" + . "github.com/eris-ltd/eris-db/word256" + "github.com/tendermint/go-events" ) var ( - ErrUnknownAddress = errors.New("Unknown address") - ErrInsufficientBalance = errors.New("Insufficient balance") - ErrInvalidJumpDest = errors.New("Invalid jump dest") - ErrInsufficientGas = errors.New("Insufficient gas") - ErrMemoryOutOfBounds = errors.New("Memory out of bounds") - ErrCodeOutOfBounds = errors.New("Code out of bounds") - ErrInputOutOfBounds = errors.New("Input out of bounds") - ErrCallStackOverflow = errors.New("Call stack overflow") - ErrCallStackUnderflow = errors.New("Call stack underflow") - ErrDataStackOverflow = errors.New("Data stack overflow") - ErrDataStackUnderflow = errors.New("Data stack underflow") - ErrInvalidContract = errors.New("Invalid contract") + ErrUnknownAddress = errors.New("Unknown address") + ErrInsufficientBalance = errors.New("Insufficient balance") + ErrInvalidJumpDest = errors.New("Invalid jump dest") + ErrInsufficientGas = errors.New("Insufficient gas") + ErrMemoryOutOfBounds = errors.New("Memory out of bounds") + ErrCodeOutOfBounds = errors.New("Code out of bounds") + ErrInputOutOfBounds = errors.New("Input out of bounds") + ErrCallStackOverflow = errors.New("Call stack overflow") + ErrCallStackUnderflow = errors.New("Call stack underflow") + ErrDataStackOverflow = errors.New("Data stack overflow") + ErrDataStackUnderflow = errors.New("Data stack underflow") + ErrInvalidContract = errors.New("Invalid contract") + ErrNativeContractCodeCopy = errors.New("Tried to copy native contract code") ) type ErrPermission struct { @@ -93,7 +111,7 @@ func HasPermission(appState AppState, acc *Account, perm ptypes.PermFlag) bool { v, err := acc.Permissions.Base.Get(perm) if _, ok := err.(ptypes.ErrValueNotSet); ok { if appState == nil { - log.Warn(Fmt("\n\n***** Unknown permission %b! ********\n\n", perm)) + log.Warn(fmt.Sprintf("\n\n***** Unknown permission %b! ********\n\n", perm)) return false } return HasPermission(nil, appState.GetAccount(ptypes.GlobalPermissionsAddress256), perm) @@ -140,7 +158,7 @@ func (vm *VM) Call(caller, callee *Account, code, input []byte, value int64, gas err := transfer(callee, caller, value) if err != nil { // data has been corrupted in ram - PanicCrisis("Could not return value to caller") + sanity.PanicCrisis("Could not return value to caller") } } } @@ -567,13 +585,17 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas } acc := vm.appState.GetAccount(addr) if acc == nil { - return nil, firstErr(err, ErrUnknownAddress) + if _, ok := registeredNativeContracts[addr]; !ok { + return nil, firstErr(err, ErrUnknownAddress) + } + dbg.Printf(" => returning code size of 1 to indicated existence of native contract at %X\n", addr) + stack.Push(One256) + } else { + code := acc.Code + l := int64(len(code)) + stack.Push64(l) + dbg.Printf(" => %d\n", l) } - code := acc.Code - l := int64(len(code)) - stack.Push64(l) - dbg.Printf(" => %d\n", l) - case EXTCODECOPY: // 0x3C addr := stack.Pop() if useGasNegative(gas, GasGetAccount, &err) { @@ -581,6 +603,10 @@ func (vm *VM) call(caller, callee *Account, code, input []byte, value int64, gas } acc := vm.appState.GetAccount(addr) if acc == nil { + if _, ok := registeredNativeContracts[addr]; ok { + dbg.Printf(" => attempted to copy native contract at %X but this is not supported\n", addr) + return nil, firstErr(err, ErrNativeContractCodeCopy) + } return nil, firstErr(err, ErrUnknownAddress) } code := acc.Code @@ -931,7 +957,7 @@ func copyslice(src []byte) (dest []byte) { } func rightMostBytes(data []byte, n int) []byte { - size := MinInt(len(data), n) + size := integral.MinInt(len(data), n) offset := len(data) - size return data[offset:] } diff --git a/manager/eris-mint/evm/test/vm_test.go b/manager/eris-mint/evm/vm_test.go similarity index 94% rename from manager/eris-mint/evm/test/vm_test.go rename to manager/eris-mint/evm/vm_test.go index 53895c0da..fe85590b4 100644 --- a/manager/eris-mint/evm/test/vm_test.go +++ b/manager/eris-mint/evm/vm_test.go @@ -1,3 +1,17 @@ +// 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 vm import ( @@ -10,12 +24,11 @@ import ( "errors" - . "github.com/eris-ltd/eris-db/manager/eris-mint/evm" . "github.com/eris-ltd/eris-db/manager/eris-mint/evm/opcodes" ptypes "github.com/eris-ltd/eris-db/permission/types" "github.com/eris-ltd/eris-db/txs" + . "github.com/eris-ltd/eris-db/word256" "github.com/stretchr/testify/assert" - . "github.com/tendermint/go-common" "github.com/tendermint/go-events" ) diff --git a/manager/eris-mint/namereg.go b/manager/eris-mint/namereg.go index 25d95ffad..cfc589eba 100644 --- a/manager/eris-mint/namereg.go +++ b/manager/eris-mint/namereg.go @@ -1,21 +1,17 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT +// 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. -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// NameReg is part of the pipe for ErisMint and provides the implementation -// for the pipe to call into the ErisMint application package erismint import ( @@ -30,7 +26,9 @@ import ( event "github.com/eris-ltd/eris-db/event" ) -// The net struct. +// NameReg is part of the pipe for ErisMint and provides the implementation +// for the pipe to call into the ErisMint application + type namereg struct { erisMint *ErisMint filterFactory *event.FilterFactory diff --git a/manager/eris-mint/pipe.go b/manager/eris-mint/pipe.go index dc9b64875..f900b3f09 100644 --- a/manager/eris-mint/pipe.go +++ b/manager/eris-mint/pipe.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 erismint @@ -20,15 +18,12 @@ import ( "bytes" "fmt" - tm_common "github.com/tendermint/go-common" + abci_types "github.com/tendermint/abci/types" crypto "github.com/tendermint/go-crypto" db "github.com/tendermint/go-db" go_events "github.com/tendermint/go-events" wire "github.com/tendermint/go-wire" tm_types "github.com/tendermint/tendermint/types" - tmsp_types "github.com/tendermint/tmsp/types" - - log "github.com/eris-ltd/eris-logger" "github.com/eris-ltd/eris-db/account" blockchain_types "github.com/eris-ltd/eris-db/blockchain/types" @@ -38,12 +33,15 @@ import ( core_types "github.com/eris-ltd/eris-db/core/types" "github.com/eris-ltd/eris-db/definitions" edb_event "github.com/eris-ltd/eris-db/event" + genesis "github.com/eris-ltd/eris-db/genesis" + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/loggers" vm "github.com/eris-ltd/eris-db/manager/eris-mint/evm" "github.com/eris-ltd/eris-db/manager/eris-mint/state" - state_types "github.com/eris-ltd/eris-db/manager/eris-mint/state/types" manager_types "github.com/eris-ltd/eris-db/manager/types" rpc_tm_types "github.com/eris-ltd/eris-db/rpc/tendermint/core/types" "github.com/eris-ltd/eris-db/txs" + "github.com/eris-ltd/eris-db/word256" ) type erisMintPipe struct { @@ -57,8 +55,9 @@ type erisMintPipe struct { namereg *namereg transactor *transactor // Genesis cache - genesisDoc *state_types.GenesisDoc + genesisDoc *genesis.GenesisDoc genesisState *state.State + logger loggers.InfoTraceLogger } // NOTE [ben] Compiler check to ensure erisMintPipe successfully implements @@ -70,7 +69,8 @@ var _ definitions.Pipe = (*erisMintPipe)(nil) var _ definitions.TendermintPipe = (*erisMintPipe)(nil) func NewErisMintPipe(moduleConfig *config.ModuleConfig, - eventSwitch *go_events.EventSwitch) (*erisMintPipe, error) { + eventSwitch go_events.EventSwitch, + logger loggers.InfoTraceLogger) (*erisMintPipe, error) { startedState, genesisDoc, err := startState(moduleConfig.DataDir, moduleConfig.Config.GetString("db_backend"), moduleConfig.GenesisFile, @@ -78,17 +78,17 @@ func NewErisMintPipe(moduleConfig *config.ModuleConfig, if err != nil { return nil, fmt.Errorf("Failed to start state: %v", err) } + logger = logging.WithScope(logger, "ErisMintPipe") // assert ChainId matches genesis ChainId - log.WithFields(log.Fields{ - "chainId": startedState.ChainID, - "lastBlockHeight": startedState.LastBlockHeight, - "lastBlockHash": startedState.LastBlockHash, - }).Debug("Loaded state") + logging.InfoMsg(logger, "Loaded state", + "chainId", startedState.ChainID, + "lastBlockHeight", startedState.LastBlockHeight, + "lastBlockHash", startedState.LastBlockHash) // start the application - erisMint := NewErisMint(startedState, eventSwitch) + erisMint := NewErisMint(startedState, eventSwitch, logger) // initialise the components of the pipe - events := edb_event.NewEvents(eventSwitch) + events := edb_event.NewEvents(eventSwitch, logger) accounts := newAccounts(erisMint) namereg := newNameReg(erisMint) @@ -108,6 +108,7 @@ func NewErisMintPipe(moduleConfig *config.ModuleConfig, // authority - this is a sort of dependency injection pattern consensusEngine: nil, blockchain: nil, + logger: logger, } // NOTE: [Silas] @@ -139,28 +140,28 @@ func NewErisMintPipe(moduleConfig *config.ModuleConfig, // If no state can be loaded, the JSON genesis file will be loaded into the // state database as the zero state. func startState(dataDir, backend, genesisFile, chainId string) (*state.State, - *state_types.GenesisDoc, error) { + *genesis.GenesisDoc, error) { // avoid Tendermints PanicSanity and return a clean error - if backend != db.DBBackendMemDB && - backend != db.DBBackendLevelDB { + if backend != db.MemDBBackendStr && + backend != db.LevelDBBackendStr { return nil, nil, fmt.Errorf("Database backend %s is not supported by %s", backend, GetErisMintVersion) } stateDB := db.NewDB("erismint", backend, dataDir) newState := state.LoadState(stateDB) - var genesisDoc *state_types.GenesisDoc + var genesisDoc *genesis.GenesisDoc if newState == nil { genesisDoc, newState = state.MakeGenesisStateFromFile(stateDB, genesisFile) newState.Save() buf, n, err := new(bytes.Buffer), new(int), new(error) wire.WriteJSON(genesisDoc, buf, n, err) - stateDB.Set(state_types.GenDocKey, buf.Bytes()) + stateDB.Set(genesis.GenDocKey, buf.Bytes()) if *err != nil { return nil, nil, fmt.Errorf("Unable to write genesisDoc to db: %v", err) } } else { - loadedGenesisDocBytes := stateDB.Get(state_types.GenDocKey) + loadedGenesisDocBytes := stateDB.Get(genesis.GenDocKey) err := new(error) wire.ReadJSONPtr(&genesisDoc, loadedGenesisDocBytes, err) if *err != nil { @@ -168,12 +169,8 @@ func startState(dataDir, backend, genesisFile, chainId string) (*state.State, } // assert loaded genesis doc has the same chainId as the provided chainId if genesisDoc.ChainID != chainId { - log.WithFields(log.Fields{ - "chainId from loaded genesis": genesisDoc.ChainID, - "chainId from configuration": chainId, - }).Warn("Conflicting chainIds") - // return nil, nil, fmt.Errorf("ChainId (%s) loaded from genesis document in existing database does not match configuration chainId (%s).", - // genesisDoc.ChainID, chainId) + return nil, nil, fmt.Errorf("ChainId (%s) loaded from genesis document in existing database does not match"+ + " configuration chainId (%s).", genesisDoc.ChainID, chainId) } } @@ -183,6 +180,10 @@ func startState(dataDir, backend, genesisFile, chainId string) (*state.State, //------------------------------------------------------------------------------ // Implement definitions.Pipe for erisMintPipe +func (pipe *erisMintPipe) Logger() loggers.InfoTraceLogger { + return pipe.logger +} + func (pipe *erisMintPipe) Accounts() definitions.Accounts { return pipe.accounts } @@ -253,11 +254,9 @@ func (pipe *erisMintPipe) Subscribe(event string, subscriptionId, err := edb_event.GenerateSubId() if err != nil { return nil, err + logging.InfoMsg(pipe.logger, "Subscribing to event", + "event", event, "subscriptionId", subscriptionId) } - - log.WithFields(log.Fields{"event": event, "subscriptionId": subscriptionId}). - Info("Subscribing to event") - pipe.consensusAndManagerEvents().Subscribe(subscriptionId, event, func(eventData txs.EventData) { result := rpc_tm_types.ErisDBResult(&rpc_tm_types.ResultEvent{event, @@ -272,8 +271,8 @@ func (pipe *erisMintPipe) Subscribe(event string, } func (pipe *erisMintPipe) Unsubscribe(subscriptionId string) (*rpc_tm_types.ResultUnsubscribe, error) { - log.WithFields(log.Fields{"subscriptionId": subscriptionId}). - Info("Unsubscribing from event") + logging.InfoMsg(pipe.logger, "Unsubscribing from event", + "subscriptionId", subscriptionId) pipe.consensusAndManagerEvents().Unsubscribe(subscriptionId) return &rpc_tm_types.ResultUnsubscribe{SubscriptionId: subscriptionId}, nil } @@ -351,13 +350,8 @@ func (pipe *erisMintPipe) Genesis() (*rpc_tm_types.ResultGenesis, error) { func (pipe *erisMintPipe) GetAccount(address []byte) (*rpc_tm_types.ResultGetAccount, error) { cache := pipe.erisMint.GetCheckCache() - // cache := mempoolReactor.Mempool.GetCache() account := cache.GetAccount(address) - if account == nil { - log.Warn("Nil Account") - return &rpc_tm_types.ResultGetAccount{nil}, nil - } - return &rpc_tm_types.ResultGetAccount{account}, nil + return &rpc_tm_types.ResultGetAccount{Account: account}, nil } func (pipe *erisMintPipe) ListAccounts() (*rpc_tm_types.ResultListAccounts, error) { @@ -384,7 +378,7 @@ func (pipe *erisMintPipe) GetStorage(address, key []byte) (*rpc_tm_types.ResultG storageTree := state.LoadStorage(storageRoot) _, value, exists := storageTree.Get( - tm_common.LeftPadWord256(key).Bytes()) + word256.LeftPadWord256(key).Bytes()) if !exists { // value == nil { return &rpc_tm_types.ResultGetStorage{key, nil}, nil @@ -426,12 +420,12 @@ func (pipe *erisMintPipe) Call(fromAddress, toAddress, data []byte) (*rpc_tm_typ fromAddress = []byte{} } callee := toVMAccount(outAcc) - caller := &vm.Account{Address: tm_common.LeftPadWord256(fromAddress)} + caller := &vm.Account{Address: word256.LeftPadWord256(fromAddress)} txCache := state.NewTxCache(cache) gasLimit := st.GetGasLimit() params := vm.Params{ BlockHeight: int64(st.LastBlockHeight), - BlockHash: tm_common.LeftPadWord256(st.LastBlockHash), + BlockHash: word256.LeftPadWord256(st.LastBlockHash), BlockTime: st.LastBlockTime.Unix(), GasLimit: gasLimit, } @@ -452,13 +446,13 @@ func (pipe *erisMintPipe) CallCode(fromAddress, code, data []byte) (*rpc_tm_type error) { st := pipe.erisMint.GetState() cache := pipe.erisMint.GetCheckCache() - callee := &vm.Account{Address: tm_common.LeftPadWord256(fromAddress)} - caller := &vm.Account{Address: tm_common.LeftPadWord256(fromAddress)} + callee := &vm.Account{Address: word256.LeftPadWord256(fromAddress)} + caller := &vm.Account{Address: word256.LeftPadWord256(fromAddress)} txCache := state.NewTxCache(cache) gasLimit := st.GetGasLimit() params := vm.Params{ BlockHeight: int64(st.LastBlockHeight), - BlockHash: tm_common.LeftPadWord256(st.LastBlockHash), + BlockHash: word256.LeftPadWord256(st.LastBlockHash), BlockTime: st.LastBlockTime.Unix(), GasLimit: gasLimit, } @@ -538,7 +532,7 @@ func (pipe *erisMintPipe) ListNames() (*rpc_tm_types.ResultListNames, error) { } func (pipe *erisMintPipe) broadcastTx(tx txs.Tx, - callback func(res *tmsp_types.Response)) (*rpc_tm_types.ResultBroadcastTx, error) { + callback func(res *abci_types.Response)) (*rpc_tm_types.ResultBroadcastTx, error) { txBytes, err := txs.EncodeTx(tx) if err != nil { @@ -558,9 +552,9 @@ func (pipe *erisMintPipe) BroadcastTxAsync(tx txs.Tx) (*rpc_tm_types.ResultBroad } func (pipe *erisMintPipe) BroadcastTxSync(tx txs.Tx) (*rpc_tm_types.ResultBroadcastTx, error) { - responseChannel := make(chan *tmsp_types.Response, 1) + responseChannel := make(chan *abci_types.Response, 1) _, err := pipe.broadcastTx(tx, - func(res *tmsp_types.Response) { + func(res *abci_types.Response) { responseChannel <- res }) if err != nil { @@ -568,7 +562,7 @@ func (pipe *erisMintPipe) BroadcastTxSync(tx txs.Tx) (*rpc_tm_types.ResultBroadc } // NOTE: [ben] This Response is set in /consensus/tendermint/local_client.go // a call to Application, here implemented by ErisMint, over local callback, - // or TMSP RPC call. Hence the result is determined by ErisMint/erismint.go + // or abci RPC call. Hence the result is determined by ErisMint/erismint.go // CheckTx() Result (Result converted to ReqRes into Response returned here) // NOTE: [ben] BroadcastTx just calls CheckTx in Tendermint (oddly... [Silas]) response := <-responseChannel @@ -582,17 +576,18 @@ func (pipe *erisMintPipe) BroadcastTxSync(tx txs.Tx) (*rpc_tm_types.ResultBroadc Log: responseCheckTx.Log, } switch responseCheckTx.Code { - case tmsp_types.CodeType_OK: + case abci_types.CodeType_OK: return resultBroadCastTx, nil - case tmsp_types.CodeType_EncodingError: + case abci_types.CodeType_EncodingError: return resultBroadCastTx, fmt.Errorf(resultBroadCastTx.Log) - case tmsp_types.CodeType_InternalError: + case abci_types.CodeType_InternalError: return resultBroadCastTx, fmt.Errorf(resultBroadCastTx.Log) default: - log.WithFields(log.Fields{ - "application": GetErisMintVersion().GetVersionString(), - "TMSP_code_type": responseCheckTx.Code, - }).Warn("Unknown error returned from Tendermint CheckTx on BroadcastTxSync") + logging.InfoMsg(pipe.logger, "Unknown error returned from Tendermint CheckTx on BroadcastTxSync", + "application", GetErisMintVersion().GetVersionString(), + "abci_code_type", responseCheckTx.Code, + "abci_log", responseCheckTx.Log, + ) return resultBroadCastTx, fmt.Errorf("Unknown error returned: " + responseCheckTx.Log) } } diff --git a/manager/eris-mint/state/block_cache.go b/manager/eris-mint/state/block_cache.go index 31210114e..e162ce5c3 100644 --- a/manager/eris-mint/state/block_cache.go +++ b/manager/eris-mint/state/block_cache.go @@ -1,15 +1,31 @@ +// 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 state import ( "bytes" + "fmt" "sort" - . "github.com/tendermint/go-common" - dbm "github.com/tendermint/go-db" - "github.com/tendermint/go-merkle" - acm "github.com/eris-ltd/eris-db/account" + "github.com/eris-ltd/eris-db/common/sanity" core_types "github.com/eris-ltd/eris-db/core/types" + . "github.com/eris-ltd/eris-db/word256" + + dbm "github.com/tendermint/go-db" + "github.com/tendermint/go-merkle" ) func makeStorage(db dbm.DB, root []byte) merkle.Tree { @@ -61,7 +77,7 @@ func (cache *BlockCache) UpdateAccount(acc *acm.Account) { addr := acc.Address _, storage, removed, _ := cache.accounts[string(addr)].unpack() if removed { - PanicSanity("UpdateAccount on a removed account") + sanity.PanicSanity("UpdateAccount on a removed account") } cache.accounts[string(addr)] = accountInfo{acc, storage, false, true} } @@ -69,7 +85,7 @@ func (cache *BlockCache) UpdateAccount(acc *acm.Account) { func (cache *BlockCache) RemoveAccount(addr []byte) { _, _, removed, _ := cache.accounts[string(addr)].unpack() if removed { - PanicSanity("RemoveAccount on a removed account") + sanity.PanicSanity("RemoveAccount on a removed account") } cache.accounts[string(addr)] = accountInfo{nil, nil, true, false} } @@ -88,7 +104,7 @@ func (cache *BlockCache) GetStorage(addr Word256, key Word256) (value Word256) { // Get or load storage acc, storage, removed, dirty := cache.accounts[string(addr.Postfix(20))].unpack() if removed { - PanicSanity("GetStorage() on removed account") + sanity.PanicSanity("GetStorage() on removed account") } if acc != nil && storage == nil { storage = makeStorage(cache.db, acc.StorageRoot) @@ -111,7 +127,7 @@ func (cache *BlockCache) GetStorage(addr Word256, key Word256) (value Word256) { func (cache *BlockCache) SetStorage(addr Word256, key Word256, value Word256) { _, _, removed, _ := cache.accounts[string(addr.Postfix(20))].unpack() if removed { - PanicSanity("SetStorage() on a removed account") + sanity.PanicSanity("SetStorage() on a removed account") } cache.storages[Tuple256{addr, key}] = storageInfo{value, true} } @@ -141,7 +157,7 @@ func (cache *BlockCache) UpdateNameRegEntry(entry *core_types.NameRegEntry) { func (cache *BlockCache) RemoveNameRegEntry(name string) { _, removed, _ := cache.names[name].unpack() if removed { - PanicSanity("RemoveNameRegEntry on a removed entry") + sanity.PanicSanity("RemoveNameRegEntry on a removed entry") } cache.names[name] = nameInfo{nil, true, false} } @@ -208,7 +224,7 @@ func (cache *BlockCache) Sync() { if removed { removed := cache.backend.RemoveAccount([]byte(addrStr)) if !removed { - PanicCrisis(Fmt("Could not remove account to be removed: %X", acc.Address)) + sanity.PanicCrisis(fmt.Sprintf("Could not remove account to be removed: %X", acc.Address)) } } else { if acc == nil { @@ -241,7 +257,7 @@ func (cache *BlockCache) Sync() { if removed { removed := cache.backend.RemoveNameRegEntry(nameStr) if !removed { - PanicCrisis(Fmt("Could not remove namereg entry to be removed: %s", nameStr)) + sanity.PanicCrisis(fmt.Sprintf("Could not remove namereg entry to be removed: %s", nameStr)) } } else { if entry == nil { diff --git a/manager/eris-mint/state/common.go b/manager/eris-mint/state/common.go index 783156272..4f096d804 100644 --- a/manager/eris-mint/state/common.go +++ b/manager/eris-mint/state/common.go @@ -1,9 +1,23 @@ +// 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 state import ( acm "github.com/eris-ltd/eris-db/account" "github.com/eris-ltd/eris-db/manager/eris-mint/evm" - . "github.com/tendermint/go-common" + . "github.com/eris-ltd/eris-db/word256" ) type AccountGetter interface { diff --git a/manager/eris-mint/state/execution.go b/manager/eris-mint/state/execution.go index ceefbbd83..80b5474f5 100644 --- a/manager/eris-mint/state/execution.go +++ b/manager/eris-mint/state/execution.go @@ -1,3 +1,17 @@ +// 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 state import ( @@ -5,15 +19,15 @@ import ( "errors" "fmt" - . "github.com/tendermint/go-common" - "github.com/tendermint/go-events" - acm "github.com/eris-ltd/eris-db/account" + "github.com/eris-ltd/eris-db/common/sanity" + core_types "github.com/eris-ltd/eris-db/core/types" "github.com/eris-ltd/eris-db/manager/eris-mint/evm" ptypes "github.com/eris-ltd/eris-db/permission/types" // for GlobalPermissionAddress ... - - core_types "github.com/eris-ltd/eris-db/core/types" "github.com/eris-ltd/eris-db/txs" + . "github.com/eris-ltd/eris-db/word256" + + "github.com/tendermint/go-events" ) // ExecBlock stuff is now taken care of by the consensus engine. @@ -203,6 +217,12 @@ func getOrMakeOutputs(state AccountGetter, accounts map[string]*acm.Account, out return accounts, nil } +// Since all ethereum accounts implicitly exist we sometimes lazily create an Account object to represent them +// only when needed. Sometimes we need to create an unknown Account knowing only its address (which is expected to +// be a deterministic hash of its associated public key) and not its public key. When we eventually receive a +// transaction acting on behalf of that account we will be given a public key that we can check matches the address. +// If it does then we will associate the public key with the stub account already registered in the system once and +// for all time. func checkInputPubKey(acc *acm.Account, in *txs.TxInput) error { if acc.PubKey == nil { if in.PubKey == nil { @@ -222,7 +242,7 @@ func validateInputs(accounts map[string]*acm.Account, signBytes []byte, ins []*t for _, in := range ins { acc := accounts[string(in.Address)] if acc == nil { - PanicSanity("validateInputs() expects account in accounts") + sanity.PanicSanity("validateInputs() expects account in accounts") } err = validateInput(acc, signBytes, in) if err != nil { @@ -273,10 +293,10 @@ func adjustByInputs(accounts map[string]*acm.Account, ins []*txs.TxInput) { for _, in := range ins { acc := accounts[string(in.Address)] if acc == nil { - PanicSanity("adjustByInputs() expects account in accounts") + sanity.PanicSanity("adjustByInputs() expects account in accounts") } if acc.Balance < in.Amount { - PanicSanity("adjustByInputs() expects sufficient funds") + sanity.PanicSanity("adjustByInputs() expects sufficient funds") } acc.Balance -= in.Amount @@ -288,7 +308,7 @@ func adjustByOutputs(accounts map[string]*acm.Account, outs []*txs.TxOutput) { for _, out := range outs { acc := accounts[string(out.Address)] if acc == nil { - PanicSanity("adjustByOutputs() expects account in accounts") + sanity.PanicSanity("adjustByOutputs() expects account in accounts") } acc.Balance += out.Amount } @@ -362,7 +382,7 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable // Validate input inAcc = blockCache.GetAccount(tx.Input.Address) if inAcc == nil { - log.Info(Fmt("Can't find in account %X", tx.Input.Address)) + log.Info(fmt.Sprintf("Can't find in account %X", tx.Input.Address)) return txs.ErrTxInvalidAddress } @@ -379,24 +399,24 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable // pubKey should be present in either "inAcc" or "tx.Input" if err := checkInputPubKey(inAcc, tx.Input); err != nil { - log.Info(Fmt("Can't find pubkey for %X", tx.Input.Address)) + log.Info(fmt.Sprintf("Can't find pubkey for %X", tx.Input.Address)) return err } signBytes := acm.SignBytes(_s.ChainID, tx) err := validateInput(inAcc, signBytes, tx.Input) if err != nil { - log.Info(Fmt("validateInput failed on %X: %v", tx.Input.Address, err)) + log.Info(fmt.Sprintf("validateInput failed on %X: %v", tx.Input.Address, err)) return err } if tx.Input.Amount < tx.Fee { - log.Info(Fmt("Sender did not send enough to cover the fee %X", tx.Input.Address)) + log.Info(fmt.Sprintf("Sender did not send enough to cover the fee %X", tx.Input.Address)) return txs.ErrTxInsufficientFunds } if !createContract { // Validate output if len(tx.Address) != 20 { - log.Info(Fmt("Destination address is not 20 bytes %X", tx.Address)) + log.Info(fmt.Sprintf("Destination address is not 20 bytes %X", tx.Address)) return txs.ErrTxInvalidAddress } // check if its a native contract @@ -410,7 +430,7 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable outAcc = blockCache.GetAccount(tx.Address) } - log.Info(Fmt("Out account: %v", outAcc)) + log.Info(fmt.Sprintf("Out account: %v", outAcc)) // Good! value := tx.Input.Amount - tx.Fee @@ -448,10 +468,10 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable // you have to wait a block to avoid a re-ordering attack // that will take your fees if outAcc == nil { - log.Info(Fmt("%X tries to call %X but it does not exist.", + log.Info(fmt.Sprintf("%X tries to call %X but it does not exist.", inAcc.Address, tx.Address)) } else { - log.Info(Fmt("%X tries to call %X but code is blank.", + log.Info(fmt.Sprintf("%X tries to call %X but code is blank.", inAcc.Address, tx.Address)) } err = txs.ErrTxInvalidAddress @@ -462,14 +482,14 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable if createContract { // We already checked for permission callee = txCache.CreateAccount(caller) - log.Info(Fmt("Created new contract %X", callee.Address)) + log.Info(fmt.Sprintf("Created new contract %X", callee.Address)) code = tx.Data } else { callee = toVMAccount(outAcc) - log.Info(Fmt("Calling contract %X with code %X", callee.Address, callee.Code)) + log.Info(fmt.Sprintf("Calling contract %X with code %X", callee.Address, callee.Code)) code = callee.Code } - log.Info(Fmt("Code for this contract: %X", code)) + log.Info(fmt.Sprintf("Code for this contract: %X", code)) // Run VM call and sync txCache to blockCache. { // Capture scope for goto. @@ -482,7 +502,7 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable ret, err = vmach.Call(caller, callee, code, tx.Data, value, &gas) if err != nil { // Failure. Charge the gas fee. The 'value' was otherwise not transferred. - log.Info(Fmt("Error on execution: %v", err)) + log.Info(fmt.Sprintf("Error on execution: %v", err)) goto CALL_COMPLETE } @@ -528,7 +548,7 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable // Validate input inAcc = blockCache.GetAccount(tx.Input.Address) if inAcc == nil { - log.Info(Fmt("Can't find in account %X", tx.Input.Address)) + log.Info(fmt.Sprintf("Can't find in account %X", tx.Input.Address)) return txs.ErrTxInvalidAddress } // check permission @@ -537,18 +557,18 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable } // pubKey should be present in either "inAcc" or "tx.Input" if err := checkInputPubKey(inAcc, tx.Input); err != nil { - log.Info(Fmt("Can't find pubkey for %X", tx.Input.Address)) + log.Info(fmt.Sprintf("Can't find pubkey for %X", tx.Input.Address)) return err } signBytes := acm.SignBytes(_s.ChainID, tx) err := validateInput(inAcc, signBytes, tx.Input) if err != nil { - log.Info(Fmt("validateInput failed on %X: %v", tx.Input.Address, err)) + log.Info(fmt.Sprintf("validateInput failed on %X: %v", tx.Input.Address, err)) return err } // fee is in addition to the amount which is used to determine the TTL if tx.Input.Amount < tx.Fee { - log.Info(Fmt("Sender did not send enough to cover the fee %X", tx.Input.Address)) + log.Info(fmt.Sprintf("Sender did not send enough to cover the fee %X", tx.Input.Address)) return txs.ErrTxInsufficientFunds } @@ -576,7 +596,7 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable if entry.Expires > lastBlockHeight { // ensure we are owner if bytes.Compare(entry.Owner, tx.Input.Address) != 0 { - log.Info(Fmt("Sender %X is trying to update a name (%s) for which he is not owner", tx.Input.Address, tx.Name)) + log.Info(fmt.Sprintf("Sender %X is trying to update a name (%s) for which he is not owner", tx.Input.Address, tx.Name)) return txs.ErrTxPermissionDenied } } else { @@ -594,7 +614,7 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable // and changing the data if expired { if expiresIn < txs.MinNameRegistrationPeriod { - return errors.New(Fmt("Names must be registered for at least %d blocks", txs.MinNameRegistrationPeriod)) + return errors.New(fmt.Sprintf("Names must be registered for at least %d blocks", txs.MinNameRegistrationPeriod)) } entry.Expires = lastBlockHeight + expiresIn entry.Owner = tx.Input.Address @@ -606,7 +626,7 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable credit := oldCredit + value expiresIn = int(credit / costPerBlock) if expiresIn < txs.MinNameRegistrationPeriod { - return errors.New(Fmt("Names must be registered for at least %d blocks", txs.MinNameRegistrationPeriod)) + return errors.New(fmt.Sprintf("Names must be registered for at least %d blocks", txs.MinNameRegistrationPeriod)) } entry.Expires = lastBlockHeight + expiresIn log.Info("Updated namereg entry", "name", entry.Name, "expiresIn", expiresIn, "oldCredit", oldCredit, "value", value, "credit", credit) @@ -616,7 +636,7 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable } } else { if expiresIn < txs.MinNameRegistrationPeriod { - return errors.New(Fmt("Names must be registered for at least %d blocks", txs.MinNameRegistrationPeriod)) + return errors.New(fmt.Sprintf("Names must be registered for at least %d blocks", txs.MinNameRegistrationPeriod)) } // entry does not exist, so create it entry = &core_types.NameRegEntry{ @@ -828,7 +848,7 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable // Validate input inAcc = blockCache.GetAccount(tx.Input.Address) if inAcc == nil { - log.Debug(Fmt("Can't find in account %X", tx.Input.Address)) + log.Debug(fmt.Sprintf("Can't find in account %X", tx.Input.Address)) return txs.ErrTxInvalidAddress } @@ -840,13 +860,13 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable // pubKey should be present in either "inAcc" or "tx.Input" if err := checkInputPubKey(inAcc, tx.Input); err != nil { - log.Debug(Fmt("Can't find pubkey for %X", tx.Input.Address)) + log.Debug(fmt.Sprintf("Can't find pubkey for %X", tx.Input.Address)) return err } signBytes := acm.SignBytes(_s.ChainID, tx) err := validateInput(inAcc, signBytes, tx.Input) if err != nil { - log.Debug(Fmt("validateInput failed on %X: %v", tx.Input.Address, err)) + log.Debug(fmt.Sprintf("validateInput failed on %X: %v", tx.Input.Address, err)) return err } @@ -871,7 +891,7 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable err = permAcc.Permissions.Base.Unset(args.Permission) case *ptypes.SetGlobalArgs: if permAcc = blockCache.GetAccount(ptypes.GlobalPermissionsAddress); permAcc == nil { - PanicSanity("can't find global permissions account") + sanity.PanicSanity("can't find global permissions account") } err = permAcc.Permissions.Base.Set(args.Permission, args.Value) case *ptypes.HasRoleArgs: @@ -891,7 +911,7 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable return fmt.Errorf("Role (%s) does not exist for account %X", args.Role, args.Address) } default: - PanicSanity(Fmt("invalid permission function: %s", ptypes.PermFlagToString(permFlag))) + sanity.PanicSanity(fmt.Sprintf("invalid permission function: %s", ptypes.PermFlagToString(permFlag))) } // TODO: maybe we want to take funds on error and allow txs in that don't do anythingi? @@ -916,7 +936,7 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable default: // binary decoding should not let this happen - PanicSanity("Unknown Tx type") + sanity.PanicSanity("Unknown Tx type") return nil } } @@ -926,7 +946,7 @@ func ExecTx(blockCache *BlockCache, tx txs.Tx, runCall bool, evc events.Fireable // Get permission on an account or fall back to global value func HasPermission(state AccountGetter, acc *acm.Account, perm ptypes.PermFlag) bool { if perm > ptypes.AllPermFlags { - PanicSanity("Checking an unknown permission in state should never happen") + sanity.PanicSanity("Checking an unknown permission in state should never happen") } if acc == nil { @@ -939,14 +959,14 @@ func HasPermission(state AccountGetter, acc *acm.Account, perm ptypes.PermFlag) v, err := acc.Permissions.Base.Get(perm) if _, ok := err.(ptypes.ErrValueNotSet); ok { if state == nil { - PanicSanity("All known global permissions should be set!") + sanity.PanicSanity("All known global permissions should be set!") } log.Info("Permission for account is not set. Querying GlobalPermissionsAddress", "perm", permString) return HasPermission(nil, state.GetAccount(ptypes.GlobalPermissionsAddress), perm) } else if v { - log.Info("Account has permission", "address", Fmt("%X", acc.Address), "perm", permString) + log.Info("Account has permission", "address", fmt.Sprintf("%X", acc.Address), "perm", permString) } else { - log.Info("Account does not have permission", "address", Fmt("%X", acc.Address), "perm", permString) + log.Info("Account does not have permission", "address", fmt.Sprintf("%X", acc.Address), "perm", permString) } return v } @@ -1005,5 +1025,5 @@ type InvalidTxError struct { } func (txErr InvalidTxError) Error() string { - return Fmt("Invalid tx: [%v] reason: [%v]", txErr.Tx, txErr.Reason) + return fmt.Sprintf("Invalid tx: [%v] reason: [%v]", txErr.Tx, txErr.Reason) } diff --git a/manager/eris-mint/state/genesis_test.go b/manager/eris-mint/state/genesis_test.go index 73e7f92f4..1aa25bc05 100644 --- a/manager/eris-mint/state/genesis_test.go +++ b/manager/eris-mint/state/genesis_test.go @@ -1,3 +1,17 @@ +// 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 state import ( @@ -9,10 +23,10 @@ import ( "time" acm "github.com/eris-ltd/eris-db/account" - . "github.com/eris-ltd/eris-db/manager/eris-mint/state/types" + "github.com/eris-ltd/eris-db/common/random" + genesis "github.com/eris-ltd/eris-db/genesis" ptypes "github.com/eris-ltd/eris-db/permission/types" - . "github.com/tendermint/go-common" tdb "github.com/tendermint/go-db" "github.com/tendermint/tendermint/types" ) @@ -31,7 +45,7 @@ var g1 = fmt.Sprintf(` { "address": "%X", "amount": %d, - "name": "%s", + "name": "%s", "permissions": { "base": { "perms": %d, @@ -60,7 +74,7 @@ var g1 = fmt.Sprintf(` `, chain_id, addr1, amt1, accName, perms, setbit, roles1[0], roles1[1]) func TestGenesisReadable(t *testing.T) { - genDoc := GenesisDocFromJSON([]byte(g1)) + genDoc := genesis.GenesisDocFromJSON([]byte(g1)) if genDoc.ChainID != chain_id { t.Fatalf("Incorrect chain id. Got %d, expected %d\n", genDoc.ChainID, chain_id) } @@ -82,7 +96,7 @@ func TestGenesisReadable(t *testing.T) { } func TestGenesisMakeState(t *testing.T) { - genDoc := GenesisDocFromJSON([]byte(g1)) + genDoc := genesis.GenesisDocFromJSON([]byte(g1)) db := tdb.NewMemDB() st := MakeGenesisState(db, genDoc) acc := st.GetAccount(addr1) @@ -108,37 +122,37 @@ func RandAccount(randBalance bool, minBalance int64) (*acm.Account, *acm.PrivAcc acc := &acm.Account{ Address: privAccount.PubKey.Address(), PubKey: privAccount.PubKey, - Sequence: RandInt(), + Sequence: random.RandInt(), Balance: minBalance, Permissions: perms, } if randBalance { - acc.Balance += int64(RandUint32()) + acc.Balance += int64(random.RandUint32()) } return acc, privAccount } -func RandGenesisDoc(numAccounts int, randBalance bool, minBalance int64, numValidators int, randBonded bool, minBonded int64) (*GenesisDoc, []*acm.PrivAccount, []*types.PrivValidator) { - accounts := make([]GenesisAccount, numAccounts) +func RandGenesisDoc(numAccounts int, randBalance bool, minBalance int64, numValidators int, randBonded bool, minBonded int64) (*genesis.GenesisDoc, []*acm.PrivAccount, []*types.PrivValidator) { + accounts := make([]genesis.GenesisAccount, numAccounts) privAccounts := make([]*acm.PrivAccount, numAccounts) defaultPerms := ptypes.DefaultAccountPermissions for i := 0; i < numAccounts; i++ { account, privAccount := RandAccount(randBalance, minBalance) - accounts[i] = GenesisAccount{ + accounts[i] = genesis.GenesisAccount{ Address: account.Address, Amount: account.Balance, Permissions: &defaultPerms, // This will get copied into each state.Account. } privAccounts[i] = privAccount } - validators := make([]GenesisValidator, numValidators) + validators := make([]genesis.GenesisValidator, numValidators) privValidators := make([]*types.PrivValidator, numValidators) for i := 0; i < numValidators; i++ { valInfo, privVal := types.RandValidator(randBonded, minBonded) - validators[i] = GenesisValidator{ + validators[i] = genesis.GenesisValidator{ PubKey: valInfo.PubKey, Amount: valInfo.VotingPower, - UnbondTo: []BasicAccount{ + UnbondTo: []genesis.BasicAccount{ { Address: valInfo.PubKey.Address(), Amount: valInfo.VotingPower, @@ -148,7 +162,7 @@ func RandGenesisDoc(numAccounts int, randBalance bool, minBalance int64, numVali privValidators[i] = privVal } sort.Sort(types.PrivValidatorsByAddress(privValidators)) - return &GenesisDoc{ + return &genesis.GenesisDoc{ GenesisTime: time.Now(), ChainID: "tendermint_test", Accounts: accounts, diff --git a/manager/eris-mint/state/log.go b/manager/eris-mint/state/log.go index 5b102b570..d8e71b796 100644 --- a/manager/eris-mint/state/log.go +++ b/manager/eris-mint/state/log.go @@ -1,3 +1,17 @@ +// 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 state import ( diff --git a/manager/eris-mint/state/permissions_test.go b/manager/eris-mint/state/permissions_test.go index 523a5ffc8..c8497f133 100644 --- a/manager/eris-mint/state/permissions_test.go +++ b/manager/eris-mint/state/permissions_test.go @@ -1,25 +1,37 @@ +// 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 state import ( "bytes" - "encoding/hex" "fmt" "strconv" "testing" "time" acm "github.com/eris-ltd/eris-db/account" + genesis "github.com/eris-ltd/eris-db/genesis" "github.com/eris-ltd/eris-db/manager/eris-mint/evm" - . "github.com/eris-ltd/eris-db/manager/eris-mint/state/types" + . "github.com/eris-ltd/eris-db/manager/eris-mint/evm/opcodes" ptypes "github.com/eris-ltd/eris-db/permission/types" "github.com/eris-ltd/eris-db/txs" + . "github.com/eris-ltd/eris-db/word256" - . "github.com/eris-ltd/eris-db/manager/eris-mint/evm/opcodes" - . "github.com/tendermint/go-common" "github.com/tendermint/go-crypto" dbm "github.com/tendermint/go-db" "github.com/tendermint/go-events" - "github.com/tendermint/tendermint/config/tendermint_test" ) @@ -28,8 +40,9 @@ func init() { } var ( - dbBackend = "memdb" - dbDir = "" + dbBackend = "memdb" + dbDir = "" + permissionsContract = vm.SNativeContracts()["Permissions"] ) /* @@ -111,30 +124,30 @@ var ( PermsAllFalse = ptypes.ZeroAccountPermissions ) -func newBaseGenDoc(globalPerm, accountPerm ptypes.AccountPermissions) GenesisDoc { - genAccounts := []GenesisAccount{} +func newBaseGenDoc(globalPerm, accountPerm ptypes.AccountPermissions) genesis.GenesisDoc { + genAccounts := []genesis.GenesisAccount{} for _, u := range user[:5] { accountPermCopy := accountPerm // Create new instance for custom overridability. - genAccounts = append(genAccounts, GenesisAccount{ + genAccounts = append(genAccounts, genesis.GenesisAccount{ Address: u.Address, Amount: 1000000, Permissions: &accountPermCopy, }) } - return GenesisDoc{ + return genesis.GenesisDoc{ GenesisTime: time.Now(), ChainID: chainID, - Params: &GenesisParams{ + Params: &genesis.GenesisParams{ GlobalPermissions: &globalPerm, }, Accounts: genAccounts, - Validators: []GenesisValidator{ - GenesisValidator{ + Validators: []genesis.GenesisValidator{ + genesis.GenesisValidator{ PubKey: user[0].PubKey.(crypto.PubKeyEd25519), Amount: 10, - UnbondTo: []BasicAccount{ - BasicAccount{ + UnbondTo: []genesis.BasicAccount{ + genesis.BasicAccount{ Address: user[0].Address, }, }, @@ -886,7 +899,7 @@ func TestSNativeCALL(t *testing.T) { fmt.Println("\n#### HasBase") // HasBase - snativeAddress, pF, data := snativePermTestInputCALL("has_base", user[3], ptypes.Bond, false) + snativeAddress, pF, data := snativePermTestInputCALL("hasBase", user[3], ptypes.Bond, false) testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) testSNativeCALLExpectPass(t, blockCache, doug, pF, snativeAddress, data, func(ret []byte) error { // return value should be true or false as a 32 byte array... @@ -898,10 +911,10 @@ func TestSNativeCALL(t *testing.T) { fmt.Println("\n#### SetBase") // SetBase - snativeAddress, pF, data = snativePermTestInputCALL("set_base", user[3], ptypes.Bond, false) + snativeAddress, pF, data = snativePermTestInputCALL("setBase", user[3], ptypes.Bond, false) testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) testSNativeCALLExpectPass(t, blockCache, doug, pF, snativeAddress, data, func(ret []byte) error { return nil }) - snativeAddress, pF, data = snativePermTestInputCALL("has_base", user[3], ptypes.Bond, false) + snativeAddress, pF, data = snativePermTestInputCALL("hasBase", user[3], ptypes.Bond, false) testSNativeCALLExpectPass(t, blockCache, doug, pF, snativeAddress, data, func(ret []byte) error { // return value should be true or false as a 32 byte array... if !IsZeros(ret) { @@ -909,9 +922,9 @@ func TestSNativeCALL(t *testing.T) { } return nil }) - snativeAddress, pF, data = snativePermTestInputCALL("set_base", user[3], ptypes.CreateContract, true) + snativeAddress, pF, data = snativePermTestInputCALL("setBase", user[3], ptypes.CreateContract, true) testSNativeCALLExpectPass(t, blockCache, doug, pF, snativeAddress, data, func(ret []byte) error { return nil }) - snativeAddress, pF, data = snativePermTestInputCALL("has_base", user[3], ptypes.CreateContract, false) + snativeAddress, pF, data = snativePermTestInputCALL("hasBase", user[3], ptypes.CreateContract, false) testSNativeCALLExpectPass(t, blockCache, doug, pF, snativeAddress, data, func(ret []byte) error { // return value should be true or false as a 32 byte array... if !IsZeros(ret[:31]) || ret[31] != byte(1) { @@ -922,10 +935,10 @@ func TestSNativeCALL(t *testing.T) { fmt.Println("\n#### UnsetBase") // UnsetBase - snativeAddress, pF, data = snativePermTestInputCALL("unset_base", user[3], ptypes.CreateContract, false) + snativeAddress, pF, data = snativePermTestInputCALL("unsetBase", user[3], ptypes.CreateContract, false) testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) testSNativeCALLExpectPass(t, blockCache, doug, pF, snativeAddress, data, func(ret []byte) error { return nil }) - snativeAddress, pF, data = snativePermTestInputCALL("has_base", user[3], ptypes.CreateContract, false) + snativeAddress, pF, data = snativePermTestInputCALL("hasBase", user[3], ptypes.CreateContract, false) testSNativeCALLExpectPass(t, blockCache, doug, pF, snativeAddress, data, func(ret []byte) error { if !IsZeros(ret) { return fmt.Errorf("Expected 0. Got %X", ret) @@ -935,10 +948,10 @@ func TestSNativeCALL(t *testing.T) { fmt.Println("\n#### SetGlobal") // SetGlobalPerm - snativeAddress, pF, data = snativePermTestInputCALL("set_global", user[3], ptypes.CreateContract, true) + snativeAddress, pF, data = snativePermTestInputCALL("setGlobal", user[3], ptypes.CreateContract, true) testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) testSNativeCALLExpectPass(t, blockCache, doug, pF, snativeAddress, data, func(ret []byte) error { return nil }) - snativeAddress, pF, data = snativePermTestInputCALL("has_base", user[3], ptypes.CreateContract, false) + snativeAddress, pF, data = snativePermTestInputCALL("hasBase", user[3], ptypes.CreateContract, false) testSNativeCALLExpectPass(t, blockCache, doug, pF, snativeAddress, data, func(ret []byte) error { // return value should be true or false as a 32 byte array... if !IsZeros(ret[:31]) || ret[31] != byte(1) { @@ -949,7 +962,7 @@ func TestSNativeCALL(t *testing.T) { fmt.Println("\n#### HasRole") // HasRole - snativeAddress, pF, data = snativeRoleTestInputCALL("has_role", user[3], "bumble") + snativeAddress, pF, data = snativeRoleTestInputCALL("hasRole", user[3], "bumble") testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) testSNativeCALLExpectPass(t, blockCache, doug, pF, snativeAddress, data, func(ret []byte) error { if !IsZeros(ret[:31]) || ret[31] != byte(1) { @@ -960,17 +973,17 @@ func TestSNativeCALL(t *testing.T) { fmt.Println("\n#### AddRole") // AddRole - snativeAddress, pF, data = snativeRoleTestInputCALL("has_role", user[3], "chuck") + snativeAddress, pF, data = snativeRoleTestInputCALL("hasRole", user[3], "chuck") testSNativeCALLExpectPass(t, blockCache, doug, pF, snativeAddress, data, func(ret []byte) error { if !IsZeros(ret) { return fmt.Errorf("Expected 0. Got %X", ret) } return nil }) - snativeAddress, pF, data = snativeRoleTestInputCALL("add_role", user[3], "chuck") + snativeAddress, pF, data = snativeRoleTestInputCALL("addRole", user[3], "chuck") testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) testSNativeCALLExpectPass(t, blockCache, doug, pF, snativeAddress, data, func(ret []byte) error { return nil }) - snativeAddress, pF, data = snativeRoleTestInputCALL("has_role", user[3], "chuck") + snativeAddress, pF, data = snativeRoleTestInputCALL("hasRole", user[3], "chuck") testSNativeCALLExpectPass(t, blockCache, doug, pF, snativeAddress, data, func(ret []byte) error { if !IsZeros(ret[:31]) || ret[31] != byte(1) { return fmt.Errorf("Expected 1. Got %X", ret) @@ -980,10 +993,10 @@ func TestSNativeCALL(t *testing.T) { fmt.Println("\n#### RmRole") // RmRole - snativeAddress, pF, data = snativeRoleTestInputCALL("rm_role", user[3], "chuck") + snativeAddress, pF, data = snativeRoleTestInputCALL("removeRole", user[3], "chuck") testSNativeCALLExpectFail(t, blockCache, doug, snativeAddress, data) testSNativeCALLExpectPass(t, blockCache, doug, pF, snativeAddress, data, func(ret []byte) error { return nil }) - snativeAddress, pF, data = snativeRoleTestInputCALL("has_role", user[3], "chuck") + snativeAddress, pF, data = snativeRoleTestInputCALL("hasRole", user[3], "chuck") testSNativeCALLExpectPass(t, blockCache, doug, pF, snativeAddress, data, func(ret []byte) error { if !IsZeros(ret) { return fmt.Errorf("Expected 0. Got %X", ret) @@ -1007,14 +1020,14 @@ func TestSNativeTx(t *testing.T) { fmt.Println("\n#### SetBase") // SetBase - snativeArgs := snativePermTestInputTx("set_base", user[3], ptypes.Bond, false) + snativeArgs := snativePermTestInputTx("setBase", user[3], ptypes.Bond, false) testSNativeTxExpectFail(t, blockCache, snativeArgs) testSNativeTxExpectPass(t, blockCache, ptypes.SetBase, snativeArgs) acc := blockCache.GetAccount(user[3].Address) if v, _ := acc.Permissions.Base.Get(ptypes.Bond); v { t.Fatal("expected permission to be set false") } - snativeArgs = snativePermTestInputTx("set_base", user[3], ptypes.CreateContract, true) + snativeArgs = snativePermTestInputTx("setBase", user[3], ptypes.CreateContract, true) testSNativeTxExpectPass(t, blockCache, ptypes.SetBase, snativeArgs) acc = blockCache.GetAccount(user[3].Address) if v, _ := acc.Permissions.Base.Get(ptypes.CreateContract); !v { @@ -1023,7 +1036,7 @@ func TestSNativeTx(t *testing.T) { fmt.Println("\n#### UnsetBase") // UnsetBase - snativeArgs = snativePermTestInputTx("unset_base", user[3], ptypes.CreateContract, false) + snativeArgs = snativePermTestInputTx("unsetBase", user[3], ptypes.CreateContract, false) testSNativeTxExpectFail(t, blockCache, snativeArgs) testSNativeTxExpectPass(t, blockCache, ptypes.UnsetBase, snativeArgs) acc = blockCache.GetAccount(user[3].Address) @@ -1033,7 +1046,7 @@ func TestSNativeTx(t *testing.T) { fmt.Println("\n#### SetGlobal") // SetGlobalPerm - snativeArgs = snativePermTestInputTx("set_global", user[3], ptypes.CreateContract, true) + snativeArgs = snativePermTestInputTx("setGlobal", user[3], ptypes.CreateContract, true) testSNativeTxExpectFail(t, blockCache, snativeArgs) testSNativeTxExpectPass(t, blockCache, ptypes.SetGlobal, snativeArgs) acc = blockCache.GetAccount(ptypes.GlobalPermissionsAddress) @@ -1043,7 +1056,7 @@ func TestSNativeTx(t *testing.T) { fmt.Println("\n#### AddRole") // AddRole - snativeArgs = snativeRoleTestInputTx("add_role", user[3], "chuck") + snativeArgs = snativeRoleTestInputTx("addRole", user[3], "chuck") testSNativeTxExpectFail(t, blockCache, snativeArgs) testSNativeTxExpectPass(t, blockCache, ptypes.AddRole, snativeArgs) acc = blockCache.GetAccount(user[3].Address) @@ -1053,7 +1066,7 @@ func TestSNativeTx(t *testing.T) { fmt.Println("\n#### RmRole") // RmRole - snativeArgs = snativeRoleTestInputTx("rm_role", user[3], "chuck") + snativeArgs = snativeRoleTestInputTx("removeRole", user[3], "chuck") testSNativeTxExpectFail(t, blockCache, snativeArgs) testSNativeTxExpectPass(t, blockCache, ptypes.RmRole, snativeArgs) acc = blockCache.GetAccount(user[3].Address) @@ -1183,72 +1196,71 @@ func boolToWord256(v bool) Word256 { return LeftPadWord256([]byte{vint}) } -func permNameToFuncID(s string) []byte { - for k, v := range vm.PermsMap { - if s == v.Name { - b, _ := hex.DecodeString(k) - return b - } +func permNameToFuncID(name string) []byte { + function, err := permissionsContract.FunctionByName(name) + if err != nil { + panic("didn't find snative function signature!") } - panic("didn't find snative function signature!") + id := function.ID() + return id[:] } func snativePermTestInputCALL(name string, user *acm.PrivAccount, perm ptypes.PermFlag, val bool) (addr []byte, pF ptypes.PermFlag, data []byte) { - addr = LeftPadWord256([]byte(vm.PermissionsContract)).Postfix(20) + addr = permissionsContract.AddressBytes() switch name { - case "has_base", "unset_base": + case "hasBase", "unsetBase": data = LeftPadBytes(user.Address, 32) data = append(data, Uint64ToWord256(uint64(perm)).Bytes()...) - case "set_base": + case "setBase": data = LeftPadBytes(user.Address, 32) data = append(data, Uint64ToWord256(uint64(perm)).Bytes()...) data = append(data, boolToWord256(val).Bytes()...) - case "set_global": + case "setGlobal": data = Uint64ToWord256(uint64(perm)).Bytes() data = append(data, boolToWord256(val).Bytes()...) } data = append(permNameToFuncID(name), data...) var err error if pF, err = ptypes.PermStringToFlag(name); err != nil { - panic(Fmt("failed to convert perm string (%s) to flag", name)) + panic(fmt.Sprintf("failed to convert perm string (%s) to flag", name)) } return } func snativePermTestInputTx(name string, user *acm.PrivAccount, perm ptypes.PermFlag, val bool) (snativeArgs ptypes.PermArgs) { switch name { - case "has_base": + case "hasBase": snativeArgs = &ptypes.HasBaseArgs{user.Address, perm} - case "unset_base": + case "unsetBase": snativeArgs = &ptypes.UnsetBaseArgs{user.Address, perm} - case "set_base": + case "setBase": snativeArgs = &ptypes.SetBaseArgs{user.Address, perm, val} - case "set_global": + case "setGlobal": snativeArgs = &ptypes.SetGlobalArgs{perm, val} } return } func snativeRoleTestInputCALL(name string, user *acm.PrivAccount, role string) (addr []byte, pF ptypes.PermFlag, data []byte) { - addr = LeftPadWord256([]byte(vm.PermissionsContract)).Postfix(20) + addr = permissionsContract.AddressBytes() data = LeftPadBytes(user.Address, 32) data = append(data, RightPadBytes([]byte(role), 32)...) data = append(permNameToFuncID(name), data...) var err error if pF, err = ptypes.PermStringToFlag(name); err != nil { - panic(Fmt("failed to convert perm string (%s) to flag", name)) + panic(fmt.Sprintf("failed to convert perm string (%s) to flag", name)) } return } func snativeRoleTestInputTx(name string, user *acm.PrivAccount, role string) (snativeArgs ptypes.PermArgs) { switch name { - case "has_role": + case "hasRole": snativeArgs = &ptypes.HasRoleArgs{user.Address, role} - case "add_role": + case "addRole": snativeArgs = &ptypes.AddRoleArgs{user.Address, role} - case "rm_role": + case "removeRole": snativeArgs = &ptypes.RmRoleArgs{user.Address, role} } return diff --git a/manager/eris-mint/state/state.go b/manager/eris-mint/state/state.go index 6a3945ae3..3d6177542 100644 --- a/manager/eris-mint/state/state.go +++ b/manager/eris-mint/state/state.go @@ -1,3 +1,17 @@ +// 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 state import ( @@ -8,17 +22,17 @@ import ( "time" acm "github.com/eris-ltd/eris-db/account" - . "github.com/eris-ltd/eris-db/manager/eris-mint/state/types" + genesis "github.com/eris-ltd/eris-db/genesis" ptypes "github.com/eris-ltd/eris-db/permission/types" "github.com/eris-ltd/eris-db/txs" - . "github.com/tendermint/go-common" dbm "github.com/tendermint/go-db" "github.com/tendermint/go-events" "github.com/tendermint/go-merkle" "github.com/tendermint/go-wire" core_types "github.com/eris-ltd/eris-db/core/types" + "github.com/eris-ltd/eris-db/util" "github.com/tendermint/tendermint/types" ) @@ -77,7 +91,7 @@ func LoadState(db dbm.DB) *State { s.nameReg.Load(nameRegHash) if *err != nil { // DATA HAS BEEN CORRUPTED OR THE SPEC HAS CHANGED - Exit(Fmt("Data has been corrupted or its spec has changed: %v\n", *err)) + util.Fatalf("Data has been corrupted or its spec has changed: %v\n", *err) } // TODO: ensure that buf is completely read. } @@ -101,7 +115,10 @@ func (s *State) Save() { //wire.WriteByteSlice(s.validatorInfos.Hash(), buf, n, err) wire.WriteByteSlice(s.nameReg.Hash(), buf, n, err) if *err != nil { - PanicCrisis(*err) + // TODO: [Silas] Do something better than this, really serialising ought to + // be error-free + util.Fatalf("Could not serialise state in order to save the state, "+ + "cannot continue, error: %s", *err) } s.DB.Set(stateKey, buf.Bytes()) } @@ -153,9 +170,9 @@ func (s *State) ComputeBlockStateHash(block *types.Block) error { } */ -func (s *State) GetGenesisDoc() (*GenesisDoc, error) { - var genesisDoc *GenesisDoc - loadedGenesisDocBytes := s.DB.Get(GenDocKey) +func (s *State) GetGenesisDoc() (*genesis.GenesisDoc, error) { + var genesisDoc *genesis.GenesisDoc + loadedGenesisDocBytes := s.DB.Get(genesis.GenDocKey) err := new(error) wire.ReadJSONPtr(&genesisDoc, loadedGenesisDocBytes, err) if *err != nil { @@ -398,25 +415,27 @@ func (s *State) SetFireable(evc events.Fireable) { //----------------------------------------------------------------------------- // Genesis -func MakeGenesisStateFromFile(db dbm.DB, genDocFile string) (*GenesisDoc, *State) { +func MakeGenesisStateFromFile(db dbm.DB, genDocFile string) (*genesis.GenesisDoc, *State) { jsonBlob, err := ioutil.ReadFile(genDocFile) if err != nil { - Exit(Fmt("Couldn't read GenesisDoc file: %v", err)) + util.Fatalf("Couldn't read GenesisDoc file: %v", err) } - genDoc := GenesisDocFromJSON(jsonBlob) + genDoc := genesis.GenesisDocFromJSON(jsonBlob) return genDoc, MakeGenesisState(db, genDoc) } -func MakeGenesisState(db dbm.DB, genDoc *GenesisDoc) *State { +func MakeGenesisState(db dbm.DB, genDoc *genesis.GenesisDoc) *State { if len(genDoc.Validators) == 0 { - Exit(Fmt("The genesis file has no validators")) + util.Fatalf("The genesis file has no validators") } if genDoc.GenesisTime.IsZero() { - // MARMOT: really? - // set time to 11/18/2016 @ 4:09am (UTC) + // NOTE: [ben] change GenesisTime to requirement on v0.17 + // GenesisTime needs to be deterministic across the chain + // and should be required in the genesis file; + // the requirement is not yet enforced when lacking set + // time to 11/18/2016 @ 4:09am (UTC) genDoc.GenesisTime = time.Unix(1479442162, 0) - // genDoc.GenesisTime = time.Now() } // Make accounts state tree diff --git a/manager/eris-mint/state/state_test.go b/manager/eris-mint/state/state_test.go index dd6e07b9b..b9d032ed3 100644 --- a/manager/eris-mint/state/state_test.go +++ b/manager/eris-mint/state/state_test.go @@ -1,18 +1,30 @@ +// 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 state import ( "bytes" "encoding/hex" "testing" - //"time" - - "github.com/tendermint/go-common" - "github.com/tendermint/tendermint/config/tendermint_test" - // tmtypes "github.com/tendermint/tendermint/types" core_types "github.com/eris-ltd/eris-db/core/types" evm "github.com/eris-ltd/eris-db/manager/eris-mint/evm" "github.com/eris-ltd/eris-db/txs" + "github.com/eris-ltd/eris-db/word256" + + "github.com/tendermint/tendermint/config/tendermint_test" ) func init() { @@ -429,7 +441,7 @@ func TestCreates(t *testing.T) { state.UpdateAccount(newAcc1) state.UpdateAccount(newAcc2) - createData = append(createData, common.LeftPadBytes(acc2.Address, 32)...) + createData = append(createData, word256.LeftPadBytes(acc2.Address, 32)...) // call the pre-factory, triggering the factory to run a create tx := &txs.CallTx{ @@ -452,7 +464,7 @@ func TestCreates(t *testing.T) { acc1 = state.GetAccount(acc1.Address) storage := state.LoadStorage(acc1.StorageRoot) - _, firstCreatedAddress, _ := storage.Get(common.LeftPadBytes([]byte{0}, 32)) + _, firstCreatedAddress, _ := storage.Get(word256.LeftPadBytes([]byte{0}, 32)) acc0 = state.GetAccount(acc0.Address) // call the pre-factory, triggering the factory to run a create @@ -476,7 +488,7 @@ func TestCreates(t *testing.T) { acc1 = state.GetAccount(acc1.Address) storage = state.LoadStorage(acc1.StorageRoot) - _, secondCreatedAddress, _ := storage.Get(common.LeftPadBytes([]byte{0}, 32)) + _, secondCreatedAddress, _ := storage.Get(word256.LeftPadBytes([]byte{0}, 32)) if bytes.Equal(firstCreatedAddress, secondCreatedAddress) { t.Errorf("Multiple contracts created with the same address!") @@ -507,7 +519,7 @@ func TestContractSend(t *testing.T) { newAcc1.Code = callerCode state.UpdateAccount(newAcc1) - sendData = append(sendData, common.LeftPadBytes(acc2.Address, 32)...) + sendData = append(sendData, word256.LeftPadBytes(acc2.Address, 32)...) sendAmt := int64(10) acc2Balance := acc2.Balance diff --git a/manager/eris-mint/state/tx_cache.go b/manager/eris-mint/state/tx_cache.go index 858dc68d7..810e85c97 100644 --- a/manager/eris-mint/state/tx_cache.go +++ b/manager/eris-mint/state/tx_cache.go @@ -1,12 +1,29 @@ +// 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 state import ( + "fmt" + acm "github.com/eris-ltd/eris-db/account" + "github.com/eris-ltd/eris-db/common/sanity" "github.com/eris-ltd/eris-db/manager/eris-mint/evm" ptypes "github.com/eris-ltd/eris-db/permission/types" // for GlobalPermissionAddress ... "github.com/eris-ltd/eris-db/txs" + . "github.com/eris-ltd/eris-db/word256" - . "github.com/tendermint/go-common" "github.com/tendermint/go-crypto" ) @@ -16,6 +33,8 @@ type TxCache struct { storages map[Tuple256]Word256 } +var _ vm.AppState = &TxCache{} + func NewTxCache(backend *BlockCache) *TxCache { return &TxCache{ backend: backend, @@ -44,7 +63,7 @@ func (cache *TxCache) UpdateAccount(acc *vm.Account) { addr := acc.Address _, removed := cache.accounts[addr].unpack() if removed { - PanicSanity("UpdateAccount on a removed account") + sanity.PanicSanity("UpdateAccount on a removed account") } cache.accounts[addr] = vmAccountInfo{acc, false} } @@ -53,7 +72,7 @@ func (cache *TxCache) RemoveAccount(acc *vm.Account) { addr := acc.Address _, removed := cache.accounts[addr].unpack() if removed { - PanicSanity("RemoveAccount on a removed account") + sanity.PanicSanity("RemoveAccount on a removed account") } cache.accounts[addr] = vmAccountInfo{acc, true} } @@ -85,7 +104,7 @@ func (cache *TxCache) CreateAccount(creator *vm.Account) *vm.Account { return account } else { // either we've messed up nonce handling, or sha3 is broken - PanicSanity(Fmt("Could not create account, address already exists: %X", addr)) + sanity.PanicSanity(fmt.Sprintf("Could not create account, address already exists: %X", addr)) return nil } } @@ -109,7 +128,7 @@ func (cache *TxCache) GetStorage(addr Word256, key Word256) Word256 { func (cache *TxCache) SetStorage(addr Word256, key Word256, value Word256) { _, removed := cache.accounts[addr].unpack() if removed { - PanicSanity("SetStorage() on a removed account") + sanity.PanicSanity("SetStorage() on a removed account") } cache.storages[Tuple256{addr, key}] = value } diff --git a/manager/eris-mint/state/tx_cache_test.go b/manager/eris-mint/state/tx_cache_test.go index a943ec728..4b30fa4a8 100644 --- a/manager/eris-mint/state/tx_cache_test.go +++ b/manager/eris-mint/state/tx_cache_test.go @@ -1,3 +1,17 @@ +// 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 state import ( diff --git a/manager/eris-mint/state/types/genesis.go b/manager/eris-mint/state/types/genesis.go deleted file mode 100644 index 806d701b2..000000000 --- a/manager/eris-mint/state/types/genesis.go +++ /dev/null @@ -1,61 +0,0 @@ -package types - -import ( - "time" - - ptypes "github.com/eris-ltd/eris-db/permission/types" - . "github.com/tendermint/go-common" - "github.com/tendermint/go-crypto" - "github.com/tendermint/go-wire" -) - -//------------------------------------------------------------ -// we store the gendoc in the db - -var GenDocKey = []byte("GenDocKey") - -//------------------------------------------------------------ -// core types for a genesis definition - -type BasicAccount struct { - Address []byte `json:"address"` - Amount int64 `json:"amount"` -} - -type GenesisAccount struct { - Address []byte `json:"address"` - Amount int64 `json:"amount"` - Name string `json:"name"` - Permissions *ptypes.AccountPermissions `json:"permissions"` -} - -type GenesisValidator struct { - PubKey crypto.PubKey `json:"pub_key"` - Amount int64 `json:"amount"` - Name string `json:"name"` - UnbondTo []BasicAccount `json:"unbond_to"` -} - -type GenesisParams struct { - GlobalPermissions *ptypes.AccountPermissions `json:"global_permissions"` -} - -type GenesisDoc struct { - GenesisTime time.Time `json:"genesis_time"` - ChainID string `json:"chain_id"` - Params *GenesisParams `json:"params"` - Accounts []GenesisAccount `json:"accounts"` - Validators []GenesisValidator `json:"validators"` -} - -//------------------------------------------------------------ -// Make genesis state from file - -func GenesisDocFromJSON(jsonBlob []byte) (genState *GenesisDoc) { - var err error - wire.ReadJSONPtr(&genState, jsonBlob, &err) - if err != nil { - Exit(Fmt("Couldn't read GenesisDoc: %v", err)) - } - return -} diff --git a/manager/eris-mint/transactor.go b/manager/eris-mint/transactor.go index 953c519f4..c29f51b59 100644 --- a/manager/eris-mint/transactor.go +++ b/manager/eris-mint/transactor.go @@ -1,21 +1,17 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT +// 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. -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// Transactor is part of the pipe for ErisMint and provides the implementation -// for the pipe to call into the ErisMint application package erismint import ( @@ -25,18 +21,20 @@ import ( "sync" "time" - cmn "github.com/tendermint/go-common" - "github.com/tendermint/go-crypto" - tEvents "github.com/tendermint/go-events" - "github.com/eris-ltd/eris-db/account" core_types "github.com/eris-ltd/eris-db/core/types" event "github.com/eris-ltd/eris-db/event" "github.com/eris-ltd/eris-db/manager/eris-mint/evm" "github.com/eris-ltd/eris-db/manager/eris-mint/state" "github.com/eris-ltd/eris-db/txs" + "github.com/eris-ltd/eris-db/word256" + + "github.com/tendermint/go-crypto" + tEvents "github.com/tendermint/go-events" ) +// Transactor is part of the pipe for ErisMint and provides the implementation +// for the pipe to call into the ErisMint application type transactor struct { chainID string eventSwitch tEvents.Fireable @@ -77,12 +75,12 @@ func (this *transactor) Call(fromAddress, toAddress, data []byte) ( fromAddress = []byte{} } callee := toVMAccount(outAcc) - caller := &vm.Account{Address: cmn.LeftPadWord256(fromAddress)} + caller := &vm.Account{Address: word256.LeftPadWord256(fromAddress)} txCache := state.NewTxCache(cache) gasLimit := st.GetGasLimit() params := vm.Params{ BlockHeight: int64(st.LastBlockHeight), - BlockHash: cmn.LeftPadWord256(st.LastBlockHash), + BlockHash: word256.LeftPadWord256(st.LastBlockHash), BlockTime: st.LastBlockTime.Unix(), GasLimit: gasLimit, } @@ -108,14 +106,14 @@ func (this *transactor) CallCode(fromAddress, code, data []byte) ( fromAddress = []byte{} } cache := this.erisMint.GetCheckCache() // XXX: DON'T MUTATE THIS CACHE (used internally for CheckTx) - callee := &vm.Account{Address: cmn.LeftPadWord256(fromAddress)} - caller := &vm.Account{Address: cmn.LeftPadWord256(fromAddress)} + callee := &vm.Account{Address: word256.LeftPadWord256(fromAddress)} + caller := &vm.Account{Address: word256.LeftPadWord256(fromAddress)} txCache := state.NewTxCache(cache) st := this.erisMint.GetState() // for block height, time gasLimit := st.GetGasLimit() params := vm.Params{ BlockHeight: int64(st.LastBlockHeight), - BlockHash: cmn.LeftPadWord256(st.LastBlockHash), + BlockHash: word256.LeftPadWord256(st.LastBlockHash), BlockTime: st.LastBlockTime.Unix(), GasLimit: gasLimit, } @@ -424,7 +422,7 @@ func (this *transactor) SignTx(tx txs.Tx, privAccounts []*account.PrivAccount) ( // No idea what this does. func toVMAccount(acc *account.Account) *vm.Account { return &vm.Account{ - Address: cmn.LeftPadWord256(acc.Address), + Address: word256.LeftPadWord256(acc.Address), Balance: acc.Balance, Code: acc.Code, Nonce: int64(acc.Sequence), diff --git a/manager/eris-mint/version.go b/manager/eris-mint/version.go index 34cdb4b53..734e5cea3 100644 --- a/manager/eris-mint/version.go +++ b/manager/eris-mint/version.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 erismint @@ -28,7 +26,7 @@ const ( // Major version component of the current release erisMintVersionMajor = 0 // Minor version component of the current release - erisMintVersionMinor = 12 + erisMintVersionMinor = 16 // Patch version component of the current release erisMintVersionPatch = 0 ) @@ -36,8 +34,7 @@ const ( // Define the compatible consensus engines this application manager // is compatible and has been tested with. var compatibleConsensus = [...]string{ - "tendermint-0.6", - // "tmsp-0.6", + "tendermint-0.8", } func GetErisMintVersion() *version.VersionIdentifier { diff --git a/manager/manager.go b/manager/manager.go index 406648cd3..d9a3b3d54 100644 --- a/manager/manager.go +++ b/manager/manager.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 manager @@ -21,12 +19,13 @@ import ( events "github.com/tendermint/go-events" - log "github.com/eris-ltd/eris-logger" - config "github.com/eris-ltd/eris-db/config" definitions "github.com/eris-ltd/eris-db/definitions" erismint "github.com/eris-ltd/eris-db/manager/eris-mint" // types "github.com/eris-ltd/eris-db/manager/types" + + "github.com/eris-ltd/eris-db/logging" + "github.com/eris-ltd/eris-db/logging/loggers" ) // NewApplicationPipe returns an initialised Pipe interface @@ -35,18 +34,18 @@ import ( // of an application. It is feasible this will be insufficient to support // different types of applications later down the line. func NewApplicationPipe(moduleConfig *config.ModuleConfig, - evsw *events.EventSwitch, consensusMinorVersion string) (definitions.Pipe, + evsw events.EventSwitch, logger loggers.InfoTraceLogger, + consensusMinorVersion string) (definitions.Pipe, error) { switch moduleConfig.Name { case "erismint": if err := erismint.AssertCompatibleConsensus(consensusMinorVersion); err != nil { return nil, err } - log.WithFields(log.Fields{ - "compatibleConsensus": consensusMinorVersion, - "erisMintVersion": erismint.GetErisMintVersion().GetVersionString(), - }).Debug("Loading ErisMint") - return erismint.NewErisMintPipe(moduleConfig, evsw) + logging.InfoMsg(logger, "Loading ErisMint", + "compatibleConsensus", consensusMinorVersion, + "erisMintVersion", erismint.GetErisMintVersion().GetVersionString()) + return erismint.NewErisMintPipe(moduleConfig, evsw, logger) } return nil, fmt.Errorf("Failed to return Pipe for %s", moduleConfig.Name) } diff --git a/manager/types/application.go b/manager/types/application.go index cfcf2b82c..dbfe2b273 100644 --- a/manager/types/application.go +++ b/manager/types/application.go @@ -1,26 +1,24 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 ( - // TODO: [ben] this is currently only used for tmsp result type; but should - // be removed as tmsp dependencies shouldn't feature in the application + // TODO: [ben] this is currently only used for abci result type; but should + // be removed as abci dependencies shouldn't feature in the application // manager - tmsp_types "github.com/tendermint/tmsp/types" + abci_types "github.com/tendermint/abci/types" ) // NOTE: [ben] this interface is likely to be changed. Currently it is taken @@ -32,16 +30,16 @@ type Application interface { // Info returns application information as a string // NOTE: [ben] likely to move - Info() (info string) + Info() (info abci_types.ResponseInfo) // Set application option (e.g. mode=mempool, mode=consensus) // NOTE: [ben] taken from tendermint, but it is unclear what the use is, - // specifically, when will tendermint call this over tmsp ? + // specifically, when will tendermint call this over abci ? SetOption(key string, value string) (log string) // Append transaction applies a transaction to the state regardless of // whether the transaction is valid or not. - // Currently AppendTx is taken from tmsp, and returns a result. + // Currently AppendTx is taken from abci, and returns a result. // This will be altered, as AppendTransaction needs to more strongly reflect // the theoretical logic: // Append(StateN, Transaction) = StateN+1 @@ -51,18 +49,18 @@ type Application interface { // TODO: implementation notes: // 1. at this point the transaction should already be strongly typed // 2. - AppendTx(tx []byte) tmsp_types.Result + DeliverTx(tx []byte) abci_types.Result // Check Transaction validates a transaction before being allowed into the // consensus' engine memory pool. This is the original defintion and - // intention as taken from tmsp, but should be remapped to the more + // intention as taken from abci, but should be remapped to the more // general concept of basic, cheap verification; // Check Transaction does not alter the state, but does require an immutable // copy of the state. In particular there is no consensus on ordering yet. // TODO: implementation notes: // 1. at this point the transaction should already be strongly typed // 2. - CheckTx(tx []byte) tmsp_types.Result + CheckTx(tx []byte) abci_types.Result // Commit returns the root hash of the current application state // NOTE: [ben] Because the concept of the block has been erased here @@ -70,14 +68,14 @@ type Application interface { // the opposit the principle of explicit stateless functions. // This will be amended when we introduce the concept of (streaming) // blocks in the pipe. - Commit() tmsp_types.Result + Commit() abci_types.Result // Query for state. This query request is not passed over the p2p network // and is called from Tendermint rpc directly up to the application. // NOTE: [ben] Eris-DB will give preference to queries from the local client // directly over the Eris-DB rpc. // We will support this for Tendermint compatibility. - Query(query []byte) tmsp_types.Result + Query(query []byte) abci_types.Result } // Tendermint has a separate interface for reintroduction of blocks @@ -85,7 +83,7 @@ type BlockchainAware interface { // Initialise the blockchain // validators: genesis validators from tendermint core - InitChain(validators []*tmsp_types.Validator) + InitChain(validators []*abci_types.Validator) // Signals the beginning of a block; // NOTE: [ben] currently not supported by tendermint @@ -95,5 +93,5 @@ type BlockchainAware interface { // validators: changed validators from app to Tendermint // NOTE: [ben] currently not supported by tendermint // not yet well defined what the change set contains. - EndBlock(height uint64) (validators []*tmsp_types.Validator) + EndBlock(height uint64) (validators []*abci_types.Validator) } diff --git a/permission/types/errors.go b/permission/types/errors.go index 4b1d7cb94..fe023ca8e 100644 --- a/permission/types/errors.go +++ b/permission/types/errors.go @@ -1,3 +1,17 @@ +// 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 ( diff --git a/permission/types/permissions.go b/permission/types/permissions.go index 5cdcdaf8c..94f31e318 100644 --- a/permission/types/permissions.go +++ b/permission/types/permissions.go @@ -1,16 +1,31 @@ +// 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 ( "fmt" + "strings" - . "github.com/tendermint/go-common" + "github.com/eris-ltd/eris-db/word256" ) //------------------------------------------------------------------------------------------------ var ( - GlobalPermissionsAddress = Zero256[:20] - GlobalPermissionsAddress256 = Zero256 + GlobalPermissionsAddress = word256.Zero256[:20] + GlobalPermissionsAddress256 = word256.Zero256 ) // A particular permission @@ -112,6 +127,12 @@ func (p *BasePermissions) IsSet(ty PermFlag) bool { return p.SetBit&ty > 0 } +// Returns the Perms PermFlag masked with SetBit bit field to give the resultant +// permissions enabled by this BasePermissions +func (p *BasePermissions) ResultantPerms() PermFlag { + return p.Perms & p.SetBit +} + func (p BasePermissions) String() string { return fmt.Sprintf("Base: %b; Set: %b", p.Perms, p.SetBit) } @@ -125,7 +146,7 @@ type AccountPermissions struct { // Returns true if the role is found func (aP *AccountPermissions) HasRole(role string) bool { - role = string(RightPadBytes([]byte(role), 32)) + role = string(word256.RightPadBytes([]byte(role), 32)) for _, r := range aP.Roles { if r == role { return true @@ -136,7 +157,7 @@ func (aP *AccountPermissions) HasRole(role string) bool { // Returns true if the role is added, and false if it already exists func (aP *AccountPermissions) AddRole(role string) bool { - role = string(RightPadBytes([]byte(role), 32)) + role = string(word256.RightPadBytes([]byte(role), 32)) for _, r := range aP.Roles { if r == role { return false @@ -148,7 +169,7 @@ func (aP *AccountPermissions) AddRole(role string) bool { // Returns true if the role is removed, and false if it is not found func (aP *AccountPermissions) RmRole(role string) bool { - role = string(RightPadBytes([]byte(role), 32)) + role = string(word256.RightPadBytes([]byte(role), 32)) for i, r := range aP.Roles { if r == role { post := []string{} @@ -162,6 +183,21 @@ func (aP *AccountPermissions) RmRole(role string) bool { return false } +// Clone clones the account permissions +func (accountPermissions *AccountPermissions) Clone() AccountPermissions { + // clone base permissions + basePermissionsClone := accountPermissions.Base + // clone roles []string + rolesClone := make([]string, len(accountPermissions.Roles)) + // strings are immutable so copy suffices + copy(rolesClone, accountPermissions.Roles) + + return AccountPermissions{ + Base: basePermissionsClone, + Roles: rolesClone, + } +} + //-------------------------------------------------------------------------------- // string utilities @@ -183,54 +219,56 @@ func PermFlagToString(pf PermFlag) (perm string) { case Name: perm = "name" case HasBase: - perm = "has_base" + perm = "hasBase" case SetBase: - perm = "set_base" + perm = "setBase" case UnsetBase: - perm = "unset_base" + perm = "unsetBase" case SetGlobal: - perm = "set_global" + perm = "setGlobal" case HasRole: - perm = "has_role" + perm = "hasRole" case AddRole: - perm = "add_role" + perm = "addRole" case RmRole: - perm = "rm_role" + perm = "removeRole" default: perm = "#-UNKNOWN-#" } return } +// PermStringToFlag maps camel- and snake case strings to the +// the corresponding permission flag. func PermStringToFlag(perm string) (pf PermFlag, err error) { - switch perm { + switch strings.ToLower(perm) { case "root": pf = Root case "send": pf = Send case "call": pf = Call - case "create_contract": + case "createcontract", "create_contract": pf = CreateContract - case "create_account": + case "createaccount", "create_account": pf = CreateAccount case "bond": pf = Bond case "name": pf = Name - case "has_base": + case "hasbase", "has_base": pf = HasBase - case "set_base": + case "setbase", "set_base": pf = SetBase - case "unset_base": + case "unsetbase", "unset_base": pf = UnsetBase - case "set_global": + case "setglobal", "set_global": pf = SetGlobal - case "has_role": + case "hasrole", "has_role": pf = HasRole - case "add_role": + case "addrole", "add_role": pf = AddRole - case "rm_role": + case "removerole", "rmrole", "rm_role": pf = RmRole default: err = fmt.Errorf("Unknown permission %s", perm) diff --git a/permission/types/snatives.go b/permission/types/snatives.go index 57315ac93..f7fe543af 100644 --- a/permission/types/snatives.go +++ b/permission/types/snatives.go @@ -1,3 +1,17 @@ +// 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 ( @@ -25,6 +39,10 @@ const ( PermArgsTypeRmRole = byte(0x07) ) +// TODO: [ben] this registration needs to be lifted up +// and centralised in core; here it pulls in go-wire dependency +// while it suffices to have the type bytes defined; +// --- // for wire.readReflect var _ = wire.RegisterInterface( struct{ PermArgs }{}, diff --git a/permission/types/util.go b/permission/types/util.go new file mode 100644 index 000000000..fddf0b9eb --- /dev/null +++ b/permission/types/util.go @@ -0,0 +1,49 @@ +// 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 + +// ConvertMapStringIntToPermissions converts a map of string-integer pairs and a slice of +// strings for the roles to an AccountPermissions type. The integer needs to be greater +// than zero to set the permission. For all unmentioned permissions the ZeroBasePermissions +// is defaulted to. +// TODO: [ben] re-evaluate the use of int for setting the permission. +func ConvertPermissionsMapAndRolesToAccountPermissions(permissions map[string]int, roles []string) (*AccountPermissions, error) { + var err error + accountPermissions := &AccountPermissions{} + accountPermissions.Base, err = convertPermissionsMapStringIntToBasePermissions(permissions) + if err != nil { + return nil, err + } + accountPermissions.Roles = roles + return accountPermissions, nil +} + +// convertPermissionsMapStringIntToBasePermissions converts a map of string-integer pairs to +// BasePermissions. +func convertPermissionsMapStringIntToBasePermissions(permissions map[string]int) (BasePermissions, error) { + // initialise basePermissions as ZeroBasePermissions + basePermissions := ZeroBasePermissions + + for permissionName, value := range permissions { + permissionsFlag, err := PermStringToFlag(permissionName) + if err != nil { + return basePermissions, err + } + // sets the permissions bitflag and the setbit flag for the permission. + basePermissions.Set(permissionsFlag, value > 0) + } + + return basePermissions, nil +} diff --git a/rpc/codec.go b/rpc/codec.go index d6fa05bb1..750a5a88e 100644 --- a/rpc/codec.go +++ b/rpc/codec.go @@ -1,3 +1,17 @@ +// 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 rpc import ( @@ -9,5 +23,6 @@ type Codec interface { EncodeBytes(interface{}) ([]byte, error) Encode(interface{}, io.Writer) error DecodeBytes(interface{}, []byte) error + DecodeBytesPtr(interface{}, []byte) error Decode(interface{}, io.Reader) error } diff --git a/rpc/jsonrpc.go b/rpc/jsonrpc.go index 72115138a..fc04317a4 100644 --- a/rpc/jsonrpc.go +++ b/rpc/jsonrpc.go @@ -1,3 +1,17 @@ +// 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 rpc import ( @@ -14,6 +28,7 @@ const ( ) // Request and Response objects. Id is a string. Error data not used. +// Refer to JSON-RPC specification http://www.jsonrpc.org/specification type ( RPCRequest struct { JSONRPC string `json:"jsonrpc"` @@ -22,35 +37,61 @@ type ( Id string `json:"id"` } - RPCResponse struct { + // RPCResponse MUST follow the JSON-RPC specification for Response object + // reference: http://www.jsonrpc.org/specification#response_object + RPCResponse interface { + AssertIsRPCResponse() bool + } + + // RPCResultResponse MUST NOT contain the error member if no error occurred + RPCResultResponse struct { Result interface{} `json:"result"` - Error *RPCError `json:"error"` Id string `json:"id"` JSONRPC string `json:"jsonrpc"` } + // RPCErrorResponse MUST NOT contain the result member if an error occured + RPCErrorResponse struct { + Error *RPCError `json:"error"` + Id string `json:"id"` + JSONRPC string `json:"jsonrpc"` + } + + // RPCError MUST be included in the Response object if an error occured RPCError struct { Code int `json:"code"` Message string `json:"message"` + // Note: Data is currently unused, and the data member may be omitted + // Data interface{} `json:"data"` } ) -// Create a new response object from a result. -func NewRPCResponse(id string, res interface{}) *RPCResponse { - return &RPCResponse{ +// NewRPCResponse creates a new response object from a result +func NewRPCResponse(id string, res interface{}) RPCResponse { + return RPCResponse(&RPCResultResponse{ Result: res, - Error: nil, Id: id, JSONRPC: "2.0", - } + }) } -// Create a new error-response object from the error code and message. -func NewRPCErrorResponse(id string, code int, message string) *RPCResponse { - return &RPCResponse{ - Result: nil, +// NewRPCErrorResponse creates a new error-response object from the error code and message +func NewRPCErrorResponse(id string, code int, message string) RPCResponse { + return RPCResponse(&RPCErrorResponse{ Error: &RPCError{code, message}, Id: id, JSONRPC: "2.0", - } + }) +} + +// AssertIsRPCResponse implements a marker method for RPCResultResponse +// to implement the interface RPCResponse +func (rpcResultResponse *RPCResultResponse) AssertIsRPCResponse() bool { + return true +} + +// AssertIsRPCResponse implements a marker method for RPCErrorResponse +// to implement the interface RPCResponse +func (rpcErrorResponse *RPCErrorResponse) AssertIsRPCResponse() bool { + return true } diff --git a/rpc/rpc_test.go b/rpc/rpc_test.go index f728d822b..fbdfca374 100644 --- a/rpc/rpc_test.go +++ b/rpc/rpc_test.go @@ -1,3 +1,17 @@ +// 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 rpc import ( @@ -10,12 +24,11 @@ import ( func TestNewJsonRpcResponse(t *testing.T) { id := "testId" data := "a string" - resp := &RPCResponse{ + resp := RPCResponse(&RPCResultResponse{ Result: data, - Error: nil, Id: id, JSONRPC: "2.0", - } + }) respGen := NewRPCResponse(id, data) assert.Equal(t, respGen, resp) } @@ -25,12 +38,11 @@ func TestNewJsonRpcErrorResponse(t *testing.T) { id := "testId" code := 100 message := "the error" - resp := &RPCResponse{ - Result: nil, + resp := RPCResponse(&RPCErrorResponse{ Error: &RPCError{code, message}, Id: id, JSONRPC: "2.0", - } + }) respGen := NewRPCErrorResponse(id, code, message) assert.Equal(t, respGen, resp) } diff --git a/rpc/tendermint/client/client.go b/rpc/tendermint/client/client.go index 3afe9c606..978c5a1fd 100644 --- a/rpc/tendermint/client/client.go +++ b/rpc/tendermint/client/client.go @@ -1,3 +1,17 @@ +// 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 client import ( diff --git a/rpc/tendermint/client/client_test.go b/rpc/tendermint/client/client_test.go index 2580508d8..105a9d2b9 100644 --- a/rpc/tendermint/client/client_test.go +++ b/rpc/tendermint/client/client_test.go @@ -1,8 +1,23 @@ +// 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 client import ( - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) func TestMapsAndValues(t *testing.T) { @@ -30,5 +45,4 @@ func TestMapsAndValues(t *testing.T) { _, _, err = mapAndValues("Foo", 4, 4, "Bar") assert.Error(t, err, "Should be an error to provide non-string keys") - } diff --git a/rpc/tendermint/core/routes.go b/rpc/tendermint/core/routes.go index 718c628ec..76df394f4 100644 --- a/rpc/tendermint/core/routes.go +++ b/rpc/tendermint/core/routes.go @@ -1,3 +1,17 @@ +// 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 core import ( diff --git a/rpc/tendermint/core/types/responses.go b/rpc/tendermint/core/types/responses.go index c6494d919..220ace4b4 100644 --- a/rpc/tendermint/core/types/responses.go +++ b/rpc/tendermint/core/types/responses.go @@ -1,18 +1,32 @@ +// 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 ( acm "github.com/eris-ltd/eris-db/account" core_types "github.com/eris-ltd/eris-db/core/types" - stypes "github.com/eris-ltd/eris-db/manager/eris-mint/state/types" + genesis "github.com/eris-ltd/eris-db/genesis" "github.com/eris-ltd/eris-db/txs" tendermint_types "github.com/tendermint/tendermint/types" consensus_types "github.com/eris-ltd/eris-db/consensus/types" + abcitypes "github.com/tendermint/abci/types" "github.com/tendermint/go-crypto" "github.com/tendermint/go-p2p" "github.com/tendermint/go-rpc/types" "github.com/tendermint/go-wire" - tmsptypes "github.com/tendermint/tmsp/types" ) type ResultGetStorage struct { @@ -111,7 +125,7 @@ type ResultGetAccount struct { } type ResultBroadcastTx struct { - Code tmsptypes.CodeType `json:"code"` + Code abcitypes.CodeType `json:"code"` Data []byte `json:"data"` Log string `json:"log"` } @@ -126,7 +140,7 @@ type ResultGetName struct { } type ResultGenesis struct { - Genesis *stypes.GenesisDoc `json:"genesis"` + Genesis *genesis.GenesisDoc `json:"genesis"` } type ResultSignTx struct { diff --git a/rpc/tendermint/core/types/responses_test.go b/rpc/tendermint/core/types/responses_test.go index bf009eab1..73e5e97d9 100644 --- a/rpc/tendermint/core/types/responses_test.go +++ b/rpc/tendermint/core/types/responses_test.go @@ -1,3 +1,17 @@ +// 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 ( diff --git a/rpc/tendermint/core/types/responses_util.go b/rpc/tendermint/core/types/responses_util.go index 13b21a145..6e0bf52ea 100644 --- a/rpc/tendermint/core/types/responses_util.go +++ b/rpc/tendermint/core/types/responses_util.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 diff --git a/rpc/tendermint/core/websocket.go b/rpc/tendermint/core/websocket.go index 6824b78e0..6bc85f4d0 100644 --- a/rpc/tendermint/core/websocket.go +++ b/rpc/tendermint/core/websocket.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 core @@ -35,7 +33,7 @@ type TendermintWebsocketServer struct { } func NewTendermintWebsocketServer(config *server.ServerConfig, - tendermintPipe definitions.TendermintPipe, evsw *events.EventSwitch) ( + tendermintPipe definitions.TendermintPipe, evsw events.EventSwitch) ( *TendermintWebsocketServer, error) { if tendermintPipe == nil { diff --git a/rpc/tendermint/test/common.go b/rpc/tendermint/test/common.go index 76909182d..8151401f6 100644 --- a/rpc/tendermint/test/common.go +++ b/rpc/tendermint/test/common.go @@ -1,11 +1,24 @@ -// +build integration - // Space above here matters +// 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 test import ( "fmt" + vm "github.com/eris-ltd/eris-db/manager/eris-mint/evm" rpc_core "github.com/eris-ltd/eris-db/rpc/tendermint/core" "github.com/eris-ltd/eris-db/test/fixtures" ) @@ -17,6 +30,7 @@ func TestWrapper(runner func() int) int { defer ffs.RemoveAll() + vm.SetDebug(true) err := initGlobalVariables(ffs) if err != nil { diff --git a/rpc/tendermint/test/common_test.go b/rpc/tendermint/test/common_test.go index aabfc84f3..5b2a1455c 100644 --- a/rpc/tendermint/test/common_test.go +++ b/rpc/tendermint/test/common_test.go @@ -1,6 +1,20 @@ // +build integration // Space above here matters +// 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 test import ( diff --git a/rpc/tendermint/test/config.go b/rpc/tendermint/test/config.go index ff33cb30f..84fcaeb79 100644 --- a/rpc/tendermint/test/config.go +++ b/rpc/tendermint/test/config.go @@ -1,20 +1,32 @@ +// 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 test -var defaultConfig = `# Copyright 2015, 2016 Eris Industries (UK) Ltd. -# This file is part of Eris-RT -# -# Eris-RT is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +var defaultConfig = `# 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 # -# Eris-RT is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# http://www.apache.org/licenses/LICENSE-2.0 # -# You should have received a copy of the GNU General Public License -# along with Eris-RT. If not, see . +# 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.. # This is a TOML configuration for Eris-DB chains @@ -40,12 +52,12 @@ genesis_file = "genesis.json" [chain.consensus] # consensus defines the module to use for consensus and # this will define the peer-to-peer consensus network; - # accepted values are "noops", "tmsp", "tendermint" + # accepted values are "noops", "abci", "tendermint" name = "tendermint" # version is the major and minor semantic version; # the version will be asserted on major_version = 0 - minor_version = 6 + minor_version = 8 # relative path to consensus' module root folder relative_root = "tendermint" @@ -61,7 +73,7 @@ genesis_file = "genesis.json" # version is the major and minor semantic version; # the version will be asserted on major_version = 0 - minor_version = 12 + minor_version = 16 # relative path to application manager root folder relative_root = "erismint" @@ -123,21 +135,19 @@ genesis_file = "genesis.json" ################################################################################ ## -## Tendermint Socket Protocol (TMSP) -## version 0.6.0 +## Tendermint Socket Protocol (abci) ## -## TMSP expects a tendermint consensus process to run and connect to Eris-DB +## abci expects a tendermint consensus process to run and connect to Eris-DB ## ################################################################################ -[tmsp] +[abci] # listener address for accepting tendermint socket protocol connections listener = "tcp://0.0.0.0:46658" ################################################################################ -## +##yeah we had partial support for that with TMSP ## Tendermint -## version 0.6.0 ## ## in-process execution of Tendermint consensus engine ## @@ -174,7 +184,7 @@ private_validator_file = "priv_validator.json" # NOTE: value is ignored when run in-process as RPC is # handled by [servers.tendermint] rpc_laddr = "" - # proxy application address - used for tmsp connections, + # proxy application address - used for abci connections, # and this port should not be exposed for in-process Tendermint proxy_app = "tcp://127.0.0.1:46658" @@ -207,7 +217,7 @@ private_validator_file = "priv_validator.json" ################################################################################ ## ## Eris-Mint -## version 0.12.0 +## version 0.16.0 ## ## The original Ethereum virtual machine with IAVL merkle trees ## and tendermint/go-wire encoding diff --git a/rpc/tendermint/test/genesis.go b/rpc/tendermint/test/genesis.go index 3136a9339..9ca0e6e83 100644 --- a/rpc/tendermint/test/genesis.go +++ b/rpc/tendermint/test/genesis.go @@ -1,3 +1,17 @@ +// 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 test // priv keys generated deterministically eg rpc/tests/shared.go diff --git a/rpc/tendermint/test/rpc_client_test.go b/rpc/tendermint/test/rpc_client_test.go index 7183f2df8..d53ff9d71 100644 --- a/rpc/tendermint/test/rpc_client_test.go +++ b/rpc/tendermint/test/rpc_client_test.go @@ -1,22 +1,35 @@ // +build integration // Space above here matters +// 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 test import ( "bytes" "fmt" - "testing" - "golang.org/x/crypto/ripemd160" - + "testing" "time" consensus_types "github.com/eris-ltd/eris-db/consensus/types" edbcli "github.com/eris-ltd/eris-db/rpc/tendermint/client" "github.com/eris-ltd/eris-db/txs" + "github.com/eris-ltd/eris-db/word256" + "github.com/stretchr/testify/assert" - tm_common "github.com/tendermint/go-common" rpcclient "github.com/tendermint/go-rpc/client" _ "github.com/tendermint/tendermint/config/tendermint_test" ) @@ -123,8 +136,8 @@ func TestGetStorage(t *testing.T) { " created a contract but the contract address is empty") v := getStorage(t, client, contractAddr, []byte{0x1}) - got := tm_common.LeftPadWord256(v) - expected := tm_common.LeftPadWord256([]byte{0x5}) + got := word256.LeftPadWord256(v) + expected := word256.LeftPadWord256([]byte{0x5}) if got.Compare(expected) != 0 { t.Fatalf("Wrong storage value. Got %x, expected %x", got.Bytes(), expected.Bytes()) @@ -148,8 +161,8 @@ func TestCallCode(t *testing.T) { // pass two ints as calldata, add, and return the result code = []byte{0x60, 0x0, 0x35, 0x60, 0x20, 0x35, 0x1, 0x60, 0x0, 0x52, 0x60, 0x20, 0x60, 0x0, 0xf3} - data = append(tm_common.LeftPadWord256([]byte{0x5}).Bytes(), - tm_common.LeftPadWord256([]byte{0x6}).Bytes()...) + data = append(word256.LeftPadWord256([]byte{0x5}).Bytes(), + word256.LeftPadWord256([]byte{0x6}).Bytes()...) expected = []byte{0xb} callCode(t, client, user[0].PubKey.Address(), code, data, expected) }) @@ -279,6 +292,8 @@ func TestNameReg(t *testing.T) { func TestBlockchainInfo(t *testing.T) { wsc := newWSClient() testWithAllClients(t, func(t *testing.T, clientName string, client rpcclient.Client) { + // wait a mimimal number of blocks to ensure that the later query for block + // headers has a non-trivial length nBlocks := 4 waitNBlocks(t, wsc, nBlocks) @@ -286,20 +301,25 @@ func TestBlockchainInfo(t *testing.T) { if err != nil { t.Fatalf("Failed to get blockchain info: %v", err) } - //TODO: [Silas] reintroduce this when Tendermint changes logic to fire - // NewBlock after saving a block - // see https://github.com/tendermint/tendermint/issues/273 - //assert.Equal(t, 4, resp.LastHeight, "Last height should be 4 after waiting for first 4 blocks") + lastBlockHeight := resp.LastHeight + nMetaBlocks := len(resp.BlockMetas) + assert.True(t, nMetaBlocks <= lastBlockHeight, + "Logically number of block metas should be equal or less than block height.") assert.True(t, nBlocks <= len(resp.BlockMetas), - "Should see at least 4 BlockMetas after waiting for first 4 blocks") - - lastBlockHash := resp.BlockMetas[nBlocks-1].Hash - for i := nBlocks - 2; i >= 0; i-- { - assert.Equal(t, lastBlockHash, resp.BlockMetas[i].Header.LastBlockHash, + "Should see at least 4 BlockMetas after waiting for 4 blocks") + // For the maximum number (default to 20) of retrieved block headers, + // check that they correctly chain to each other. + lastBlockHash := resp.BlockMetas[nMetaBlocks-1].Hash + for i := nMetaBlocks - 2; i >= 0; i-- { + // the blockhash in header of height h should be identical to the hash + // in the LastBlockID of the header of block height h+1. + assert.Equal(t, lastBlockHash, resp.BlockMetas[i].Header.LastBlockID.Hash, "Blockchain should be a hash tree!") lastBlockHash = resp.BlockMetas[i].Hash } + // Now retrieve only two blockheaders (h=1, and h=2) and check that we got + // two results. resp, err = edbcli.BlockchainInfo(client, 1, 2) assert.NoError(t, err) assert.Equal(t, 2, len(resp.BlockMetas), diff --git a/rpc/tendermint/test/runner/main.go b/rpc/tendermint/test/runner/main.go index a68363578..a47649dad 100644 --- a/rpc/tendermint/test/runner/main.go +++ b/rpc/tendermint/test/runner/main.go @@ -1,6 +1,20 @@ // +build integration // Space above here matters +// 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 main import ( diff --git a/rpc/tendermint/test/shared.go b/rpc/tendermint/test/shared.go index d211bd5d1..98044412e 100644 --- a/rpc/tendermint/test/shared.go +++ b/rpc/tendermint/test/shared.go @@ -1,29 +1,42 @@ +// 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 test import ( "bytes" "hash/fnv" + "path" "strconv" "testing" acm "github.com/eris-ltd/eris-db/account" "github.com/eris-ltd/eris-db/core" core_types "github.com/eris-ltd/eris-db/core/types" + "github.com/eris-ltd/eris-db/logging/lifecycle" edbcli "github.com/eris-ltd/eris-db/rpc/tendermint/client" rpc_core "github.com/eris-ltd/eris-db/rpc/tendermint/core" rpc_types "github.com/eris-ltd/eris-db/rpc/tendermint/core/types" "github.com/eris-ltd/eris-db/server" "github.com/eris-ltd/eris-db/test/fixtures" "github.com/eris-ltd/eris-db/txs" + "github.com/eris-ltd/eris-db/word256" + genesis "github.com/eris-ltd/eris-db/genesis" + "github.com/spf13/viper" "github.com/tendermint/go-crypto" rpcclient "github.com/tendermint/go-rpc/client" - - "path" - - state_types "github.com/eris-ltd/eris-db/manager/eris-mint/state/types" - "github.com/spf13/viper" - tm_common "github.com/tendermint/go-common" "github.com/tendermint/tendermint/types" ) @@ -34,7 +47,7 @@ var ( mempoolCount = 0 chainID string websocketAddr string - genesisDoc *state_types.GenesisDoc + genesisDoc *genesis.GenesisDoc websocketEndpoint string user = makeUsers(5) // make keys jsonRpcClient rpcclient.Client @@ -49,7 +62,7 @@ func initGlobalVariables(ffs *fixtures.FileFixtures) error { rootWorkDir = ffs.AddDir("rootWorkDir") rootDataDir := ffs.AddDir("rootDataDir") genesisFile := ffs.AddFile("rootWorkDir/genesis.json", defaultGenesis) - genesisDoc = state_types.GenesisDocFromJSON([]byte(defaultGenesis)) + genesisDoc = genesis.GenesisDocFromJSON([]byte(defaultGenesis)) if ffs.Error != nil { return ffs.Error @@ -83,7 +96,14 @@ func initGlobalVariables(ffs *fixtures.FileFixtures) error { // Set up priv_validator.json before we start tendermint (otherwise it will // create its own one. saveNewPriv() - testCore, err = core.NewCore("testCore", consensusConfig, managerConfig) + logger := lifecycle.NewStdErrLogger() + // To spill tendermint logs on the floor: + // lifecycle.CaptureTendermintLog15Output(loggers.NewNoopInfoTraceLogger()) + lifecycle.CaptureTendermintLog15Output(logger) + lifecycle.CaptureStdlibLogOutput(logger) + + testCore, err = core.NewCore("testCore", consensusConfig, managerConfig, + logger) if err != nil { return err } @@ -234,7 +254,7 @@ func callCode(t *testing.T, client rpcclient.Client, fromAddress, code, data, } ret := resp.Return // NOTE: we don't flip memory when it comes out of RETURN (?!) - if bytes.Compare(ret, tm_common.LeftPadWord256(expected).Bytes()) != 0 { + if bytes.Compare(ret, word256.LeftPadWord256(expected).Bytes()) != 0 { t.Fatalf("Conflicting return value. Got %x, expected %x", ret, expected) } } @@ -247,7 +267,7 @@ func callContract(t *testing.T, client rpcclient.Client, fromAddress, toAddress, } ret := resp.Return // NOTE: we don't flip memory when it comes out of RETURN (?!) - if bytes.Compare(ret, tm_common.LeftPadWord256(expected).Bytes()) != 0 { + if bytes.Compare(ret, word256.LeftPadWord256(expected).Bytes()) != 0 { t.Fatalf("Conflicting return value. Got %x, expected %x", ret, expected) } } @@ -286,14 +306,14 @@ func simpleContract() ([]byte, []byte, []byte) { // push code to the stack //code := append([]byte{byte(0x60 + lenCode - 1)}, RightPadWord256(contractCode).Bytes()...) code := append([]byte{0x7f}, - tm_common.RightPadWord256(contractCode).Bytes()...) + word256.RightPadWord256(contractCode).Bytes()...) // store it in memory code = append(code, []byte{0x60, 0x0, 0x52}...) // return whats in memory //code = append(code, []byte{0x60, byte(32 - lenCode), 0x60, byte(lenCode), 0xf3}...) code = append(code, []byte{0x60, byte(lenCode), 0x60, 0x0, 0xf3}...) // return init code, contract code, expected return - return code, contractCode, tm_common.LeftPadBytes([]byte{0xb}, 32) + return code, contractCode, word256.LeftPadBytes([]byte{0xb}, 32) } // simple call contract calls another contract @@ -321,5 +341,5 @@ func simpleCallContract(addr []byte) ([]byte, []byte, []byte) { code = append(code, []byte{0x60, byte(lenCode), 0x60, 0x0, 0xf3}...) code = append(code, contractCode...) // return init code, contract code, expected return - return code, contractCode, tm_common.LeftPadBytes([]byte{0xb}, 32) + return code, contractCode, word256.LeftPadBytes([]byte{0xb}, 32) } diff --git a/rpc/tendermint/test/websocket_client_test.go b/rpc/tendermint/test/websocket_client_test.go index 60e665bdc..5f799041f 100644 --- a/rpc/tendermint/test/websocket_client_test.go +++ b/rpc/tendermint/test/websocket_client_test.go @@ -1,6 +1,20 @@ // +build integration // Space above here matters +// 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 test import ( diff --git a/rpc/tendermint/test/websocket_helpers.go b/rpc/tendermint/test/websocket_helpers.go index b5a7f79f0..46408e5d4 100644 --- a/rpc/tendermint/test/websocket_helpers.go +++ b/rpc/tendermint/test/websocket_helpers.go @@ -1,3 +1,17 @@ +// 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 test import ( diff --git a/rpc/v0/codec.go b/rpc/v0/codec.go index 1fd379bea..344c1c98f 100644 --- a/rpc/v0/codec.go +++ b/rpc/v0/codec.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 rpc_v0 @@ -47,6 +45,8 @@ func (this *TCodec) EncodeBytes(v interface{}) ([]byte, error) { return wire.JSONBytes(v), nil } +// TODO: [ben] implement EncodeBytesPtr ? + // Decode from an io.Reader. func (this *TCodec) Decode(v interface{}, r io.Reader) error { bts, errR := ioutil.ReadAll(r) @@ -64,3 +64,10 @@ func (this *TCodec) DecodeBytes(v interface{}, bts []byte) error { wire.ReadJSON(v, bts, &err) return err } + +// Decode from a byte array pointer. +func (this *TCodec) DecodeBytesPtr(v interface{}, bts []byte) error { + var err error + wire.ReadJSONPtr(v, bts, &err) + return err +} diff --git a/rpc/v0/json_service.go b/rpc/v0/json_service.go index 4c43f5334..77bc4ffaa 100644 --- a/rpc/v0/json_service.go +++ b/rpc/v0/json_service.go @@ -1,3 +1,17 @@ +// 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 rpc_v0 import ( @@ -6,8 +20,6 @@ import ( "github.com/gin-gonic/gin" - log "github.com/eris-ltd/eris-logger" - definitions "github.com/eris-ltd/eris-db/definitions" event "github.com/eris-ltd/eris-db/event" rpc "github.com/eris-ltd/eris-db/rpc" @@ -125,10 +137,8 @@ func (this *ErisDbJsonService) writeError(msg, id string, code int, w http.Respo // Helper for writing responses. func (this *ErisDbJsonService) writeResponse(id string, result interface{}, w http.ResponseWriter) { - log.Debug("Result: %v\n", result) response := rpc.NewRPCResponse(id, result) err := this.codec.Encode(response, w) - log.Debug("Response: %v\n", response) if err != nil { this.writeError("Internal error: "+err.Error(), id, rpc.INTERNAL_ERROR, w) return diff --git a/rpc/v0/json_service_data_test.go b/rpc/v0/json_service_data_test.go new file mode 100644 index 000000000..96be04138 --- /dev/null +++ b/rpc/v0/json_service_data_test.go @@ -0,0 +1,71 @@ +// 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 rpc_v0 + +import ( + "encoding/json" + "testing" + + "github.com/eris-ltd/eris-db/rpc" + "github.com/eris-ltd/eris-db/txs" + + "github.com/stretchr/testify/assert" +) + +var testBroadcastCallTxJsonRequest = []byte(` +{ + "id": "57EC1D39-7B3D-4F96-B286-8FC128177AFC4", + "jsonrpc": "2.0", + "method": "erisdb.broadcastTx", + "params": [ + 2, + { + "address": "5A9083BB0EFFE4C8EB2ADD29174994F73E77D418", + "data": "2F2397A00000000000000000000000000000000000000000000000000000000000003132", + "fee": 1, + "gas_limit": 1000000, + "input": { + "address": "BE18FDCBF12BF99F4D75325E17FF2E78F1A35FE8", + "amount": 1, + "pub_key": [ + 1, + "8D1611925948DC2EDDF739FB65CE517757D286155A039B28441C3349BE9A8C38" + ], + "sequence": 2, + "signature": [ + 1, + "B090D622F143ECEDA9B9E7B15485CE7504453C05434951CF867B013D80ED1BD2A0CA32846FC175D234CDFB9D5C3D792759E8FE79FD4DB3006B24950EE3C37D00" + ] + } + } + ] +}`) + +// strictly test the codec for go-wire encoding of the Json format, +// This should restore compatibility with the format on v0.11.4 +// (which was broken on v0.12) +func TestCallTxJsonFormatCodec(t *testing.T) { + codec := NewTCodec() + param := new(txs.Tx) + + // Create new request object and unmarshal. + request := &rpc.RPCRequest{} + assert.NoError(t, json.Unmarshal(testBroadcastCallTxJsonRequest, request), + "Provided JSON test data does not unmarshal to rpc.RPCRequest object.") + assert.NoError(t, codec.DecodeBytesPtr(param, request.Params), + "RPC codec failed to decode params as transaction type.") + _, ok := (*param).(*txs.CallTx) + assert.True(t, ok, "Type byte 0x02 should unmarshal into CallTx.") +} diff --git a/rpc/v0/methods.go b/rpc/v0/methods.go index eaa82ff06..6a26848cc 100644 --- a/rpc/v0/methods.go +++ b/rpc/v0/methods.go @@ -1,3 +1,17 @@ +// 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 rpc_v0 import ( @@ -345,12 +359,13 @@ func (erisDbMethods *ErisDbMethods) CallCode(request *rpc.RPCRequest, requester } func (erisDbMethods *ErisDbMethods) BroadcastTx(request *rpc.RPCRequest, requester interface{}) (interface{}, int, error) { - param := &txs.CallTx{} - err := erisDbMethods.codec.DecodeBytes(param, request.Params) + // Accept all transaction types as parameter for broadcast. + param := new(txs.Tx) + err := erisDbMethods.codec.DecodeBytesPtr(param, request.Params) if err != nil { return nil, rpc.INVALID_PARAMS, err } - receipt, errC := erisDbMethods.pipe.Transactor().BroadcastTx(param) + receipt, errC := erisDbMethods.pipe.Transactor().BroadcastTx(*param) if errC != nil { return nil, rpc.INTERNAL_ERROR, errC } diff --git a/rpc/v0/params.go b/rpc/v0/params.go index 98778554d..e6f565557 100644 --- a/rpc/v0/params.go +++ b/rpc/v0/params.go @@ -1,3 +1,17 @@ +// 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 rpc_v0 import ( diff --git a/rpc/v0/restServer.go b/rpc/v0/restServer.go index 2af84e19e..18fb507ec 100644 --- a/rpc/v0/restServer.go +++ b/rpc/v0/restServer.go @@ -1,3 +1,17 @@ +// 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 rpc_v0 import ( diff --git a/test/testdata/testdata/testdata.go b/rpc/v0/restServer_data_test.go similarity index 92% rename from test/testdata/testdata/testdata.go rename to rpc/v0/restServer_data_test.go index 5fd2be000..31fbaeb3b 100644 --- a/test/testdata/testdata/testdata.go +++ b/rpc/v0/restServer_data_test.go @@ -1,12 +1,25 @@ -package testdata +// 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 rpc_v0 import ( account "github.com/eris-ltd/eris-db/account" consensus_types "github.com/eris-ltd/eris-db/consensus/types" core_types "github.com/eris-ltd/eris-db/core/types" event "github.com/eris-ltd/eris-db/event" - stypes "github.com/eris-ltd/eris-db/manager/eris-mint/state/types" - rpc_v0 "github.com/eris-ltd/eris-db/rpc/v0" + genesis "github.com/eris-ltd/eris-db/genesis" "github.com/eris-ltd/eris-db/rpc/v0/shared" transaction "github.com/eris-ltd/eris-db/txs" mintTypes "github.com/tendermint/tendermint/types" @@ -540,26 +553,26 @@ var serverDuration uint = 100 type ( ChainData struct { PrivValidator *mintTypes.PrivValidator `json:"priv_validator"` - Genesis *stypes.GenesisDoc `json:"genesis"` + Genesis *genesis.GenesisDoc `json:"genesis"` } GetAccountData struct { - Input *rpc_v0.AddressParam `json:"input"` - Output *account.Account `json:"output"` + Input *AddressParam `json:"input"` + Output *account.Account `json:"output"` } GetAccountsData struct { - Input *rpc_v0.AccountsParam `json:"input"` + Input *AccountsParam `json:"input"` Output *core_types.AccountList `json:"output"` } GetStorageData struct { - Input *rpc_v0.AddressParam `json:"input"` - Output *core_types.Storage `json:"output"` + Input *AddressParam `json:"input"` + Output *core_types.Storage `json:"output"` } GetStorageAtData struct { - Input *rpc_v0.StorageAtParam `json:"input"` + Input *StorageAtParam `json:"input"` Output *core_types.StorageItem `json:"output"` } @@ -588,13 +601,13 @@ type ( } GetBlockData struct { - Input *rpc_v0.HeightParam `json:"input"` - Output *mintTypes.Block `json:"output"` + Input *HeightParam `json:"input"` + Output *mintTypes.Block `json:"output"` } GetBlocksData struct { - Input *rpc_v0.BlocksParam `json:"input"` - Output *core_types.Blocks `json:"output"` + Input *BlocksParam `json:"input"` + Output *core_types.Blocks `json:"output"` } GetConsensusStateData struct { @@ -630,18 +643,18 @@ type ( } GetPeerData struct { - Input *rpc_v0.PeerParam `json:"input"` + Input *PeerParam `json:"input"` Output *consensus_types.Peer `json:"output"` } TransactData struct { - Input *rpc_v0.TransactParam `json:"input"` - Output *transaction.Receipt `json:"output"` + Input *TransactParam `json:"input"` + Output *transaction.Receipt `json:"output"` } TransactCreateData struct { - Input *rpc_v0.TransactParam `json:"input"` - Output *transaction.Receipt `json:"output"` + Input *TransactParam `json:"input"` + Output *transaction.Receipt `json:"output"` } GetUnconfirmedTxsData struct { @@ -649,43 +662,43 @@ type ( } CallCodeData struct { - Input *rpc_v0.CallCodeParam `json:"input"` - Output *core_types.Call `json:"output"` + Input *CallCodeParam `json:"input"` + Output *core_types.Call `json:"output"` } CallData struct { - Input *rpc_v0.CallParam `json:"input"` - Output *core_types.Call `json:"output"` + Input *CallParam `json:"input"` + Output *core_types.Call `json:"output"` } EventSubscribeData struct { - Input *rpc_v0.EventIdParam `json:"input"` - Output *event.EventSub `json:"output"` + Input *EventIdParam `json:"input"` + Output *event.EventSub `json:"output"` } EventUnsubscribeData struct { - Input *rpc_v0.SubIdParam `json:"input"` - Output *event.EventUnsub `json:"output"` + Input *SubIdParam `json:"input"` + Output *event.EventUnsub `json:"output"` } TransactNameRegData struct { - Input *rpc_v0.TransactNameRegParam `json:"input"` - Output *transaction.Receipt `json:"output"` + Input *TransactNameRegParam `json:"input"` + Output *transaction.Receipt `json:"output"` } GetNameRegEntryData struct { - Input *rpc_v0.NameRegEntryParam `json:"input"` - Output *core_types.NameRegEntry `json:"output"` + Input *NameRegEntryParam `json:"input"` + Output *core_types.NameRegEntry `json:"output"` } GetNameRegEntriesData struct { - Input *rpc_v0.FilterListParam `json:"input"` + Input *FilterListParam `json:"input"` Output *core_types.ResultListNames `json:"output"` } /* EventPollData struct { - Input *rpc_v0.SubIdParam `json:"input"` + Input *SubIdParam `json:"input"` Output *event.PollResponse `json:"output"` } */ @@ -728,7 +741,7 @@ type ( ) func LoadTestData() *TestData { - codec := rpc_v0.NewTCodec() + codec := NewTCodec() testData := &TestData{} if err := codec.DecodeBytes(testData, []byte(testDataJson)); err != nil { panic(err) diff --git a/test/mock/pipe.go b/rpc/v0/restServer_pipe_test.go similarity index 80% rename from test/mock/pipe.go rename to rpc/v0/restServer_pipe_test.go index e279ad182..6b084b9ed 100644 --- a/test/mock/pipe.go +++ b/rpc/v0/restServer_pipe_test.go @@ -1,4 +1,18 @@ -package mock +// 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 rpc_v0 import ( "fmt" @@ -11,48 +25,44 @@ import ( blockchain_types "github.com/eris-ltd/eris-db/blockchain/types" consensus_types "github.com/eris-ltd/eris-db/consensus/types" manager_types "github.com/eris-ltd/eris-db/manager/types" - td "github.com/eris-ltd/eris-db/test/testdata/testdata" "github.com/eris-ltd/eris-db/txs" + "github.com/eris-ltd/eris-db/logging/loggers" + abci_types "github.com/tendermint/abci/types" "github.com/tendermint/go-crypto" "github.com/tendermint/go-p2p" mintTypes "github.com/tendermint/tendermint/types" - tmsp_types "github.com/tendermint/tmsp/types" ) // Base struct. type MockPipe struct { - testData *td.TestData + testData *TestData accounts definitions.Accounts blockchain blockchain_types.Blockchain consensusEngine consensus_types.ConsensusEngine events event.EventEmitter namereg definitions.NameReg transactor definitions.Transactor + logger loggers.InfoTraceLogger } // Create a new mock tendermint pipe. -func NewMockPipe(td *td.TestData) definitions.Pipe { - accounts := &accounts{td} - blockchain := &blockchain{td} - consensusEngine := &consensusEngine{td} - eventer := &eventer{td} - namereg := &namereg{td} - transactor := &transactor{td} +func NewMockPipe(td *TestData) definitions.Pipe { return &MockPipe{ - td, - accounts, - blockchain, - consensusEngine, - eventer, - namereg, - transactor, + testData: td, + accounts: &accounts{td}, + blockchain: &mockBlockchain{td}, + consensusEngine: &consensusEngine{td}, + events: &eventer{td}, + namereg: &namereg{td}, + transactor: &transactor{td}, + logger: loggers.NewNoopInfoTraceLogger(), } } // Create a mock pipe with default mock data. func NewDefaultMockPipe() definitions.Pipe { - return NewMockPipe(td.LoadTestData()) + return NewMockPipe(LoadTestData()) } func (pipe *MockPipe) Accounts() definitions.Accounts { @@ -75,6 +85,10 @@ func (pipe *MockPipe) Transactor() definitions.Transactor { return pipe.transactor } +func (pipe *MockPipe) Logger() loggers.InfoTraceLogger { + return pipe.logger +} + func (pipe *MockPipe) GetApplication() manager_types.Application { // TODO: [ben] mock application return nil @@ -110,7 +124,7 @@ func (pipe *MockPipe) GenesisHash() []byte { // Accounts type accounts struct { - testData *td.TestData + testData *TestData } func (acc *accounts) GenPrivAccount() (*account.PrivAccount, error) { @@ -138,33 +152,33 @@ func (acc *accounts) StorageAt(address, key []byte) (*core_types.StorageItem, er } // Blockchain -type blockchain struct { - testData *td.TestData +type mockBlockchain struct { + testData *TestData } -func (this *blockchain) ChainId() string { +func (this *mockBlockchain) ChainId() string { return this.testData.GetChainId.Output.ChainId } -func (this *blockchain) Height() int { +func (this *mockBlockchain) Height() int { return this.testData.GetLatestBlockHeight.Output.Height } -func (this *blockchain) Block(height int) *mintTypes.Block { +func (this *mockBlockchain) Block(height int) *mintTypes.Block { return this.testData.GetBlock.Output } -func (this *blockchain) BlockMeta(height int) *mintTypes.BlockMeta { +func (this *mockBlockchain) BlockMeta(height int) *mintTypes.BlockMeta { return &mintTypes.BlockMeta{} } // Consensus type consensusEngine struct { - testData *td.TestData + testData *TestData } func (cons *consensusEngine) BroadcastTransaction(transaction []byte, - callback func(*tmsp_types.Response)) error { + callback func(*abci_types.Response)) error { return nil } @@ -224,7 +238,7 @@ func (cons *consensusEngine) PeerConsensusStates() map[string]string { // Events type eventer struct { - testData *td.TestData + testData *TestData } func (evntr *eventer) Subscribe(subId, event string, callback func(txs.EventData)) error { @@ -237,7 +251,7 @@ func (evntr *eventer) Unsubscribe(subId string) error { // NameReg type namereg struct { - testData *td.TestData + testData *TestData } func (nmreg *namereg) Entry(key string) (*core_types.NameRegEntry, error) { @@ -250,7 +264,7 @@ func (nmreg *namereg) Entries(filters []*event.FilterData) (*core_types.ResultLi // Txs type transactor struct { - testData *td.TestData + testData *TestData } func (trans *transactor) Call(fromAddress, toAddress, data []byte) (*core_types.Call, error) { diff --git a/test/mock/mock_web_api_test.go b/rpc/v0/restServer_test.go similarity index 93% rename from test/mock/mock_web_api_test.go rename to rpc/v0/restServer_test.go index fc0603e01..7cdf28039 100644 --- a/test/mock/mock_web_api_test.go +++ b/rpc/v0/restServer_test.go @@ -1,4 +1,18 @@ -package mock +// 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 rpc_v0 // Basic imports import ( @@ -15,9 +29,7 @@ import ( core_types "github.com/eris-ltd/eris-db/core/types" event "github.com/eris-ltd/eris-db/event" rpc "github.com/eris-ltd/eris-db/rpc" - rpc_v0 "github.com/eris-ltd/eris-db/rpc/v0" server "github.com/eris-ltd/eris-db/server" - td "github.com/eris-ltd/eris-db/test/testdata/testdata" "github.com/eris-ltd/eris-db/txs" "github.com/eris-ltd/eris-db/rpc/v0/shared" @@ -41,18 +53,18 @@ type MockSuite struct { serveProcess *server.ServeProcess codec rpc.Codec sUrl string - testData *td.TestData + testData *TestData } func (mockSuite *MockSuite) SetupSuite() { gin.SetMode(gin.ReleaseMode) // Load the supporting objects. - testData := td.LoadTestData() + testData := LoadTestData() pipe := NewMockPipe(testData) - codec := &rpc_v0.TCodec{} + codec := &TCodec{} evtSubs := event.NewEventSubscriptions(pipe.Events()) // The server - restServer := rpc_v0.NewRestServer(codec, pipe, evtSubs) + restServer := NewRestServer(codec, pipe, evtSubs) sConf := server.DefaultServerConfig() sConf.Bind.Port = 31402 // Create a server process. @@ -62,7 +74,7 @@ func (mockSuite *MockSuite) SetupSuite() { panic(err) } mockSuite.serveProcess = proc - mockSuite.codec = rpc_v0.NewTCodec() + mockSuite.codec = NewTCodec() mockSuite.testData = testData mockSuite.sUrl = "http://localhost:31402" } diff --git a/rpc/v0/shared/net.go b/rpc/v0/shared/net.go index 778b53595..5d1bc85dc 100644 --- a/rpc/v0/shared/net.go +++ b/rpc/v0/shared/net.go @@ -1,21 +1,17 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT +// 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. -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . - -// Net is part of the pipe for ErisMint and provides the implementation -// for the pipe to call into the ErisMint application package shared import ( @@ -23,6 +19,9 @@ import ( "github.com/eris-ltd/eris-db/definitions" ) +// Net is part of the pipe for ErisMint and provides the implementation +// for the pipe to call into the ErisMint application + //----------------------------------------------------------------------------- // Get the complete pipe info. diff --git a/rpc/v0/shared/pipes.go b/rpc/v0/shared/pipes.go index fb71aad9a..55a27b3fb 100644 --- a/rpc/v0/shared/pipes.go +++ b/rpc/v0/shared/pipes.go @@ -1,3 +1,17 @@ +// 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 shared // Shared extension methods for Pipe and its derivatives diff --git a/rpc/v0/wsService.go b/rpc/v0/wsService.go index 35705ed39..daea0d01b 100644 --- a/rpc/v0/wsService.go +++ b/rpc/v0/wsService.go @@ -1,11 +1,23 @@ +// 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 rpc_v0 import ( "encoding/json" "fmt" - log "github.com/eris-ltd/eris-logger" - definitions "github.com/eris-ltd/eris-db/definitions" "github.com/eris-ltd/eris-db/event" rpc "github.com/eris-ltd/eris-db/rpc" @@ -36,7 +48,6 @@ func NewErisDbWsService(codec rpc.Codec, // Process a request. func (this *ErisDbWsService) Process(msg []byte, session *server.WSSession) { - log.Debug("REQUEST: %s\n", string(msg)) // Create new request object and unmarshal. req := &rpc.RPCRequest{} errU := json.Unmarshal(msg, req) @@ -84,7 +95,6 @@ func (this *ErisDbWsService) writeResponse(id string, result interface{}, session *server.WSSession) error { response := rpc.NewRPCResponse(id, result) bts, err := this.codec.EncodeBytes(response) - log.Debug("RESPONSE: %v\n", response) if err != nil { this.writeError("Internal error: "+err.Error(), id, rpc.INTERNAL_ERROR, session) return err diff --git a/server/config.go b/server/config.go index 13078f985..4dcce19dd 100644 --- a/server/config.go +++ b/server/config.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 server diff --git a/server/idpool.go b/server/idpool.go index d713c7014..44a72cba4 100644 --- a/server/idpool.go +++ b/server/idpool.go @@ -1,3 +1,17 @@ +// 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 server import ( diff --git a/server/log.go b/server/log.go index aacad33d9..e2bcb50b4 100644 --- a/server/log.go +++ b/server/log.go @@ -1,3 +1,17 @@ +// 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 server import ( diff --git a/server/logging.go b/server/logging.go index 173fecaf3..4a38e0f82 100644 --- a/server/logging.go +++ b/server/logging.go @@ -1,3 +1,17 @@ +// 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 server import ( diff --git a/server/server.go b/server/server.go index f1d38c3de..8e4c85122 100644 --- a/server/server.go +++ b/server/server.go @@ -1,3 +1,17 @@ +// 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 server import ( diff --git a/server/server_test.go b/server/server_test.go index 615695155..5efdad268 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -1,3 +1,17 @@ +// 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 server import ( diff --git a/server/websocket.go b/server/websocket.go index a5767141c..aff2f94c3 100644 --- a/server/websocket.go +++ b/server/websocket.go @@ -1,3 +1,17 @@ +// 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 server import ( diff --git a/test/filters/filter_test.go b/test/filters/filter_test.go index ff5765014..c27a1eb02 100644 --- a/test/filters/filter_test.go +++ b/test/filters/filter_test.go @@ -1,3 +1,17 @@ +// 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 filters import ( diff --git a/test/fixtures/file_fixtures.go b/test/fixtures/file_fixtures.go index da751d108..d6926c097 100644 --- a/test/fixtures/file_fixtures.go +++ b/test/fixtures/file_fixtures.go @@ -1,3 +1,17 @@ +// 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 fixtures import ( diff --git a/test/server/http_burst_test.go b/test/server/http_burst_test.go index bb49275b7..b9e404e28 100644 --- a/test/server/http_burst_test.go +++ b/test/server/http_burst_test.go @@ -1,3 +1,17 @@ +// 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 server import ( diff --git a/test/server/scumbag.go b/test/server/scumbag.go index 71eaddc61..67751442e 100644 --- a/test/server/scumbag.go +++ b/test/server/scumbag.go @@ -1,3 +1,17 @@ +// 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 server import ( diff --git a/test/server/ws_burst_test.go b/test/server/ws_burst_test.go index cb9e7af4f..55dd8e939 100644 --- a/test/server/ws_burst_test.go +++ b/test/server/ws_burst_test.go @@ -1,3 +1,17 @@ +// 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 server import ( diff --git a/test/testdata/helpers.go b/test/testdata/helpers.go index 127440f7e..2dbbaed7b 100644 --- a/test/testdata/helpers.go +++ b/test/testdata/helpers.go @@ -1,3 +1,17 @@ +// 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 testdata import ( diff --git a/tests/build_tool.sh b/tests/build_tool.sh index 31dcac0cd..325542e9a 100755 --- a/tests/build_tool.sh +++ b/tests/build_tool.sh @@ -35,9 +35,10 @@ release_min=$(cat $REPO/version/version.go | tail -n 1 | cut -d \ -f 4 | tr -d release_maj=$(echo $release_min | cut -d . -f 1-2) # Build +mkdir -p $REPO/target/docker docker build -t $IMAGE:build $REPO -docker run --rm --entrypoint cat $IMAGE:build /usr/local/bin/$TARGET > $REPO/"$TARGET"_build_artifact -docker run --rm --entrypoint cat $IMAGE:build /usr/local/bin/eris-client > $REPO/eris-client +docker run --rm --entrypoint cat $IMAGE:build /usr/local/bin/$TARGET > $REPO/target/docker/eris-db.dockerartefact +docker run --rm --entrypoint cat $IMAGE:build /usr/local/bin/eris-client > $REPO/target/docker/eris-client.dockerartefact docker build -t $IMAGE:$release_min -f Dockerfile.deploy $REPO # If provided, tag the image with the label provided @@ -48,6 +49,6 @@ then fi # Cleanup -rm $REPO/"$TARGET"_build_artifact -rm $REPO/eris-client +rm $REPO/target/docker/eris-db.dockerartefact +rm $REPO/target/docker/eris-client.dockerartefact docker rmi -f $IMAGE:build diff --git a/txs/events.go b/txs/events.go index 4cb7e656c..9e15cd3c5 100644 --- a/txs/events.go +++ b/txs/events.go @@ -1,12 +1,26 @@ +// 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 txs import ( "fmt" "time" - . "github.com/tendermint/go-common" - "github.com/tendermint/go-wire" + . "github.com/eris-ltd/eris-db/word256" + "github.com/tendermint/go-wire" tm_types "github.com/tendermint/tendermint/types" // Block ) diff --git a/txs/log.go b/txs/log.go deleted file mode 100644 index b967a58d0..000000000 --- a/txs/log.go +++ /dev/null @@ -1,7 +0,0 @@ -package txs - -import ( - "github.com/tendermint/go-logger" -) - -var log = logger.New("module", "types") diff --git a/txs/names.go b/txs/names.go index 1cc00e168..41b80b085 100644 --- a/txs/names.go +++ b/txs/names.go @@ -1,3 +1,17 @@ +// 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 txs import ( diff --git a/txs/tx.go b/txs/tx.go index a21633203..0a7430dc1 100644 --- a/txs/tx.go +++ b/txs/tx.go @@ -1,3 +1,17 @@ +// 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 txs import ( diff --git a/txs/tx_test.go b/txs/tx_test.go index e3f092da5..e4b0653dc 100644 --- a/txs/tx_test.go +++ b/txs/tx_test.go @@ -1,3 +1,17 @@ +// 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 txs import ( diff --git a/txs/tx_utils.go b/txs/tx_utils.go index cdc4be247..0c6466486 100644 --- a/txs/tx_utils.go +++ b/txs/tx_utils.go @@ -1,3 +1,17 @@ +// 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 txs import ( diff --git a/util/architecture/constants.go b/util/architecture/constants.go index fb03311c6..695a5fe16 100644 --- a/util/architecture/constants.go +++ b/util/architecture/constants.go @@ -1,5 +1,19 @@ // +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 architecture import "math" diff --git a/util/architecture/constants_arm.go b/util/architecture/constants_arm.go index 5f8c9cc82..c677498cb 100644 --- a/util/architecture/constants_arm.go +++ b/util/architecture/constants_arm.go @@ -1,3 +1,17 @@ +// 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 architecture import "math" diff --git a/util/fs.go b/util/fs.go index 7d41c1ddd..41ec3fce0 100644 --- a/util/fs.go +++ b/util/fs.go @@ -1,18 +1,16 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT - -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +// 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 util diff --git a/util/hell/README.md b/util/hell/README.md new file mode 100644 index 000000000..715cb5522 --- /dev/null +++ b/util/hell/README.md @@ -0,0 +1,11 @@ +> Hell is other people's packages + +While we wait for working package management in go we need a way to make +maintaining the glide.lock by hand less painful. + +To interactively add a package run from the root: + +```bash +go run ./util/hell/cmd/hell/main.go get --interactive github.com/tendermint/tendermint +``` + diff --git a/util/hell/cmd/hell/main.go b/util/hell/cmd/hell/main.go new file mode 100644 index 000000000..427105ba9 --- /dev/null +++ b/util/hell/cmd/hell/main.go @@ -0,0 +1,145 @@ +// 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 main + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/eris-ltd/eris-db/util/hell" + + "github.com/Masterminds/glide/action" + "github.com/Masterminds/glide/cache" + "github.com/Masterminds/glide/cfg" + "github.com/Masterminds/glide/msg" + "github.com/Masterminds/glide/path" + "github.com/Masterminds/glide/repo" + "github.com/Masterminds/glide/util" + "github.com/spf13/cobra" +) + +func main() { + hellCmd := &cobra.Command{ + Use: "hell", + Short: "Hell makes the most of it being warm", + Long: "", + Run: func(cmd *cobra.Command, args []string) { cmd.Help() }, + } + + // Lock merge command + var baseGlideLockFile, depGlideLockFile string + lockMergeCmd := &cobra.Command{ + Use: "lock-merge", + Short: "Merge glide.lock files together", + Long: "This command merges two glide.lock files into a single one by copying all dependencies " + + "from a base glide.lock and an override glide.lock to an output glide.lock with dependencies " + + "from override taking precedence over those from base.", + Run: func(cmd *cobra.Command, args []string) { + baseLockFile, err := cfg.ReadLockFile(baseGlideLockFile) + if err != nil { + fmt.Printf("Could not read file: %s\n", err) + os.Exit(1) + } + overrideLockFile, err := cfg.ReadLockFile(depGlideLockFile) + if err != nil { + fmt.Printf("Could not read file: %s\n", err) + os.Exit(1) + } + mergedLockFile, err := hell.MergeGlideLockFiles(baseLockFile, overrideLockFile) + if err != nil { + fmt.Printf("Could not merge lock files: %s\n", err) + os.Exit(1) + } + mergedBytes, err := mergedLockFile.Marshal() + if err != nil { + fmt.Printf("Could not marshal lock file: %s\n", err) + os.Exit(1) + } + os.Stdout.Write(mergedBytes) + }, + } + lockMergeCmd.PersistentFlags().StringVarP(&baseGlideLockFile, "base", "b", "", "base lock file") + lockMergeCmd.PersistentFlags().StringVarP(&depGlideLockFile, "override", "o", "", "override lock file") + + // Lock update + interactive := false + getTransitiveCmd := &cobra.Command{ + Use: "get", + Short: "gets a remote dependency to this project along with its transtive dependencies.", + Long: "Gets a remote dependency and its transitive dependencies by adding the remote " + + "depednency to this project's glide.yaml and merging the remote dependency's " + + "glide.lock into this project's glide.lock", + Run: func(cmd *cobra.Command, args []string) { + if len(args) != 1 { + msg.Die("%s requires a single argument of the remote dependency\n", cmd.Name()) + } + rootPackage, _ := util.NormalizeName(args[0]) + // Add dependency to glide + installer := repo.NewInstaller() + action.Get(args, installer, false, true, false, !interactive, false) + // Now hunt down the repo cache + dep := action.EnsureConfig().Imports.Get(rootPackage) + + key, err := cache.Key(dep.Remote()) + if err != nil { + msg.Die("%s requires a single argument of the remote dependency\n", cmd.Name()) + } + cacheDir := filepath.Join(cache.Location(), "src", key) + repos, err := dep.GetRepo(cacheDir) + if err != nil { + msg.Die("Could not get repo: %s", err) + } + version, err := repos.Version() + if err != nil { + msg.Die("Could not get version: %s", err) + } + dep.Pin = version + lockPath := filepath.Join(".", path.LockFile) + baseLockFile, err := cfg.ReadLockFile(lockPath) + if err != nil { + msg.Die("Could not read base lock file: %s", err) + } + overrideLockFile := &cfg.Lockfile{} + if path.HasLock(cacheDir) { + msg.Info("Found dependency lock file so merging into project lock file") + overrideLockFile, err = cfg.ReadLockFile(filepath.Join(cacheDir, path.LockFile)) + if err != nil { + msg.Die("Could not read dependency lock file: %s", err) + } + } + // Add the package to glide lock too! + overrideLockFile.Imports = append(overrideLockFile.Imports, cfg.LockFromDependency(dep)) + + mergedLockFile, err := hell.MergeGlideLockFiles(baseLockFile, overrideLockFile) + if err != nil { + msg.Die("Could not merge lock files: %s\n", err) + } + err = mergedLockFile.WriteFile(lockPath) + if err != nil { + msg.Die("Could not write merged lock file: %s", err) + } + + action.Install(installer, false) + }, + } + + getTransitiveCmd.PersistentFlags().BoolVarP(&interactive, "interactive", "i", false, + "set dependency verion interactively") + + hellCmd.AddCommand(lockMergeCmd) + hellCmd.AddCommand(getTransitiveCmd) + lockMergeCmd.Execute() +} diff --git a/util/hell/merge.go b/util/hell/merge.go new file mode 100644 index 000000000..130a80065 --- /dev/null +++ b/util/hell/merge.go @@ -0,0 +1,101 @@ +// 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 hell + +import ( + "crypto/sha256" + "fmt" + "sort" + + "github.com/Masterminds/glide/cfg" +) + +// Merges two glide lock files together, letting dependencies from 'base' be overwritten +// by those from 'override'. Returns the resultant glide lock file bytes +func MergeGlideLockFiles(baseLockFile, overrideLockFile *cfg.Lockfile) (*cfg.Lockfile, error) { + imports := make(map[string]*cfg.Lock, len(baseLockFile.Imports)) + devImports := make(map[string]*cfg.Lock, len(baseLockFile.DevImports)) + // Copy the base dependencies into a map + for _, lock := range baseLockFile.Imports { + imports[lock.Name] = lock + } + for _, lock := range baseLockFile.DevImports { + devImports[lock.Name] = lock + } + // Override base dependencies and add any extra ones + for _, lock := range overrideLockFile.Imports { + imports[lock.Name] = mergeLocks(imports[lock.Name], lock) + } + for _, lock := range overrideLockFile.DevImports { + devImports[lock.Name] = mergeLocks(imports[lock.Name], lock) + } + + deps := make([]*cfg.Dependency, 0, len(imports)) + devDeps := make([]*cfg.Dependency, 0, len(devImports)) + + // Flatten to Dependencies + for _, lock := range imports { + deps = append(deps, pinnedDependencyFromLock(lock)) + } + + for _, lock := range devImports { + devDeps = append(devDeps, pinnedDependencyFromLock(lock)) + } + + hasher := sha256.New() + hasher.Write(([]byte)(baseLockFile.Hash)) + hasher.Write(([]byte)(overrideLockFile.Hash)) + + return cfg.NewLockfile(deps, devDeps, fmt.Sprintf("%x", hasher.Sum(nil))) +} + +func mergeLocks(baseLock, overrideLock *cfg.Lock) *cfg.Lock { + lock := overrideLock.Clone() + if baseLock == nil { + return lock + } + + // Merge and dedupe subpackages + subpackages := make([]string, 0, len(lock.Subpackages)+len(baseLock.Subpackages)) + for _, sp := range lock.Subpackages { + subpackages = append(subpackages, sp) + } + for _, sp := range baseLock.Subpackages { + subpackages = append(subpackages, sp) + } + + sort.Stable(sort.StringSlice(subpackages)) + + dedupeSubpackages := make([]string, 0, len(subpackages)) + + lastSp := "" + elided := 0 + for _, sp := range subpackages { + if lastSp == sp { + elided++ + } else { + dedupeSubpackages = append(dedupeSubpackages, sp) + lastSp = sp + } + } + lock.Subpackages = dedupeSubpackages[:len(dedupeSubpackages)-elided] + return lock +} + +func pinnedDependencyFromLock(lock *cfg.Lock) *cfg.Dependency { + dep := cfg.DependencyFromLock(lock) + dep.Pin = lock.Version + return dep +} diff --git a/util/hell/merge_test.go b/util/hell/merge_test.go new file mode 100644 index 000000000..5fffedb9c --- /dev/null +++ b/util/hell/merge_test.go @@ -0,0 +1,85 @@ +// 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 hell + +import ( + "strings" + "testing" + + "github.com/Masterminds/glide/cfg" + "github.com/stretchr/testify/assert" +) + +const baseLockYml = `imports: +- name: github.com/gogo/protobuf + version: 82d16f734d6d871204a3feb1a73cb220cc92574c +- name: github.com/tendermint/tendermint + version: aaea0c5d2e3ecfbf29f2608f9d43649ec7f07f50 + subpackages: + - node + - proxy + - types + - version + - consensus + - rpc/core/types + - blockchain + - mempool + - rpc/core + - state +` +const overrideLockYml = `imports: +- name: github.com/tendermint/tendermint + version: 764091dfbb035f1b28da4b067526e04c6a849966 + subpackages: + - benchmarks + - proxy + - types + - version +` +const expectedLockYml = `imports: +- name: github.com/gogo/protobuf + version: 82d16f734d6d871204a3feb1a73cb220cc92574c +- name: github.com/tendermint/tendermint + version: 764091dfbb035f1b28da4b067526e04c6a849966 + subpackages: + - benchmarks + - blockchain + - consensus + - mempool + - node + - proxy + - rpc/core + - rpc/core/types +testImports: [] +` + +func TestMergeGlideLockFiles(t *testing.T) { + baseLockFile, err := cfg.LockfileFromYaml(([]byte)(baseLockYml)) + assert.NoError(t, err, "Lockfile should parse") + + overrideLockFile, err := cfg.LockfileFromYaml(([]byte)(overrideLockYml)) + assert.NoError(t, err, "Lockfile should parse") + + mergedLockFile, err := MergeGlideLockFiles(baseLockFile, overrideLockFile) + assert.NoError(t, err, "Lockfiles should merge") + + mergedYmlBytes, err := mergedLockFile.Marshal() + assert.NoError(t, err, "Lockfile should marshal") + + ymlLines := strings.Split(string(mergedYmlBytes), "\n") + // Drop the updated and hash lines + actualYml := strings.Join(ymlLines[2:], "\n") + assert.Equal(t, expectedLockYml, actualYml) +} diff --git a/util/os.go b/util/os.go new file mode 100644 index 000000000..a205c1c09 --- /dev/null +++ b/util/os.go @@ -0,0 +1,26 @@ +// 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 util + +import ( + "fmt" + "os" +) + +// Prints an error message to stderr and exits with status code 1 +func Fatalf(format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, format, args...) + os.Exit(1) +} diff --git a/util/slice/slice.go b/util/slice/slice.go new file mode 100644 index 000000000..8c2b7f891 --- /dev/null +++ b/util/slice/slice.go @@ -0,0 +1,105 @@ +// 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 slice + +func Slice(elements ...interface{}) []interface{} { + return elements +} + +func EmptySlice() []interface{} { + return []interface{}{} +} + +// Like append but on the interface{} type and always to a fresh backing array +// so can be used safely with slices over arrays you did not create. +func CopyAppend(slice []interface{}, elements ...interface{}) []interface{} { + sliceLength := len(slice) + newSlice := make([]interface{}, sliceLength+len(elements)) + for i, e := range slice { + newSlice[i] = e + } + for i, e := range elements { + newSlice[sliceLength+i] = e + } + return newSlice +} + +// Prepend elements to slice in the order they appear +func CopyPrepend(slice []interface{}, elements ...interface{}) []interface{} { + elementsLength := len(elements) + newSlice := make([]interface{}, len(slice)+elementsLength) + for i, e := range elements { + newSlice[i] = e + } + for i, e := range slice { + newSlice[elementsLength+i] = e + } + return newSlice +} + +// Concatenate slices into a single slice +func Concat(slices ...[]interface{}) []interface{} { + offset := 0 + for _, slice := range slices { + offset += len(slice) + } + concat := make([]interface{}, offset) + offset = 0 + for _, slice := range slices { + for i, e := range slice { + concat[offset+i] = e + } + offset += len(slice) + } + return concat +} + +// Deletes n elements starting with the ith from a slice by splicing. +// Beware uses append so the underlying backing array will be modified! +func Delete(slice []interface{}, i int, n int) []interface{} { + return append(slice[:i], slice[i+n:]...) +} + +// Delete an element at a specific index and return the contracted list +func DeleteAt(slice []interface{}, i int) []interface{} { + return Delete(slice, i, 1) +} + +// Flatten a slice by a list by splicing any elements of the list that are +// themselves lists into the slice elements to the list in place of slice itself +func Flatten(slice []interface{}) []interface{} { + return DeepFlatten(slice, 1) +} + +// Recursively flattens a list by splicing any sub-lists into their parent until +// depth is reached. If a negative number is passed for depth then it continues +// until no elements of the returned list are lists +func DeepFlatten(slice []interface{}, depth int) []interface{} { + if depth == 0 { + return slice + } + returnSlice := []interface{}{} + + for _, element := range slice { + if s, ok := element.([]interface{}); ok { + returnSlice = append(returnSlice, DeepFlatten(s, depth-1)...) + } else { + returnSlice = append(returnSlice, element) + } + + } + + return returnSlice +} diff --git a/util/slice/slice_test.go b/util/slice/slice_test.go new file mode 100644 index 000000000..8ab47c99a --- /dev/null +++ b/util/slice/slice_test.go @@ -0,0 +1,57 @@ +// 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 slice + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCopyAppend(t *testing.T) { + assert.Equal(t, Slice(1, "two", "three", 4), + CopyAppend(Slice(1, "two"), "three", 4)) + assert.Equal(t, EmptySlice(), CopyAppend(nil)) + assert.Equal(t, Slice(1), CopyAppend(nil, 1)) + assert.Equal(t, Slice(1), CopyAppend(Slice(1))) +} + +func TestCopyPrepend(t *testing.T) { + assert.Equal(t, Slice("three", 4, 1, "two"), + CopyPrepend(Slice(1, "two"), "three", 4)) + assert.Equal(t, EmptySlice(), CopyPrepend(nil)) + assert.Equal(t, Slice(1), CopyPrepend(nil, 1)) + assert.Equal(t, Slice(1), CopyPrepend(Slice(1))) +} + +func TestConcat(t *testing.T) { + assert.Equal(t, Slice(1, 2, 3, 4, 5), Concat(Slice(1, 2, 3, 4, 5))) + assert.Equal(t, Slice(1, 2, 3, 4, 5), Concat(Slice(1, 2, 3), Slice(4, 5))) + assert.Equal(t, Slice(1, 2, 3, 4, 5), Concat(Slice(1), Slice(2, 3), Slice(4, 5))) + assert.Equal(t, EmptySlice(), Concat(nil)) + assert.Equal(t, Slice(1), Concat(nil, Slice(), Slice(1))) + assert.Equal(t, Slice(1), Concat(Slice(1), Slice(), nil)) +} + +func TestDelete(t *testing.T) { + assert.Equal(t, Slice(1, 2, 4, 5), Delete(Slice(1, 2, 3, 4, 5), 2, 1)) +} + +func TestDeepFlatten(t *testing.T) { + assert.Equal(t, Flatten(Slice(Slice(1, 2), 3, 4)), Slice(1, 2, 3, 4)) + nestedSlice := Slice(Slice(1, Slice(Slice(2))), Slice(3, 4)) + assert.Equal(t, DeepFlatten(nestedSlice, -1), Slice(1, 2, 3, 4)) + assert.Equal(t, DeepFlatten(nestedSlice, 2), Slice(1, Slice(2), 3, 4)) +} diff --git a/util/snatives/cmd/main.go b/util/snatives/cmd/main.go new file mode 100644 index 000000000..0b12ba333 --- /dev/null +++ b/util/snatives/cmd/main.go @@ -0,0 +1,44 @@ +// 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 main + +import ( + "fmt" + + "github.com/eris-ltd/eris-db/manager/eris-mint/evm" + "github.com/eris-ltd/eris-db/util/snatives/templates" +) + +// Dump SNative contracts +func main() { + contracts := vm.SNativeContracts() + // Index of next contract + i := 1 + fmt.Print("pragma solidity >=0.0.0;\n\n") + for _, contract := range contracts { + solidity, err := templates.NewSolidityContract(contract).Solidity() + if err != nil { + fmt.Printf("Error generating solidity for contract %s: %s\n", + contract.Name, err) + } + fmt.Println(solidity) + if i < len(contracts) { + // Two new lines between contracts as per Solidity style guide + // (the template gives us 1 trailing new line) + fmt.Println() + } + i++ + } +} diff --git a/util/snatives/templates/indent_writer.go b/util/snatives/templates/indent_writer.go new file mode 100644 index 000000000..c395f5a9f --- /dev/null +++ b/util/snatives/templates/indent_writer.go @@ -0,0 +1,61 @@ +// 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 templates + +import "io" + +const newLine = byte('\n') + +type indentWriter struct { + writer io.Writer + indentLevel uint + indentBytes []byte + indent bool +} + +var _ io.Writer = (*indentWriter)(nil) + +// indentWriter indents all lines written to it with a specified indent string +// indented the specified number of indents +func NewIndentWriter(indentLevel uint, indentString string, + writer io.Writer) *indentWriter { + return &indentWriter{ + writer: writer, + indentLevel: indentLevel, + indentBytes: []byte(indentString), + indent: true, + } +} + +func (iw *indentWriter) Write(p []byte) (int, error) { + bs := make([]byte, 0, len(p)) + for _, b := range p { + if iw.indent { + for i := uint(0); i < iw.indentLevel; i++ { + bs = append(bs, iw.indentBytes...) + } + iw.indent = false + } + if b == newLine { + iw.indent = true + } + bs = append(bs, b) + } + return iw.writer.Write(bs) +} + +func (iw *indentWriter) SetIndent(level uint) { + iw.indentLevel = level +} diff --git a/util/snatives/templates/solidity_templates.go b/util/snatives/templates/solidity_templates.go new file mode 100644 index 000000000..eda2c5558 --- /dev/null +++ b/util/snatives/templates/solidity_templates.go @@ -0,0 +1,173 @@ +// 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 templates + +import ( + "bytes" + "fmt" + "strings" + "text/template" + + "github.com/eris-ltd/eris-db/manager/eris-mint/evm" +) + +const contractTemplateText = `/** +[[.Comment]] +* @dev These functions can be accessed as if this contract were deployed at a special address ([[.Address]]). +* @dev This special address is defined as the last 20 bytes of the sha3 hash of the the contract name. +* @dev To instantiate the contract use: +* @dev [[.Name]] [[.InstanceName]] = [[.Name]](address(sha3("[[.Name]]"))); +*/ +contract [[.Name]] {[[range .Functions]] +[[.SolidityIndent 1]] +[[end]]} +` +const functionTemplateText = `/** +[[.Comment]] +*/ +function [[.Name]]([[.ArgList]]) constant returns ([[.Return.TypeName]] [[.Return.Name]]);` + +// Solidity style guide recommends 4 spaces per indentation level +// (see: http://solidity.readthedocs.io/en/develop/style-guide.html) +const indentString = " " + +var contractTemplate *template.Template +var functionTemplate *template.Template + +func init() { + var err error + functionTemplate, err = template.New("SolidityFunctionTemplate"). + Delims("[[", "]]"). + Parse(functionTemplateText) + if err != nil { + panic(fmt.Errorf("Couldn't parse SNative function template: %s", err)) + } + contractTemplate, err = template.New("SolidityContractTemplate"). + Delims("[[", "]]"). + Parse(contractTemplateText) + if err != nil { + panic(fmt.Errorf("Couldn't parse SNative contract template: %s", err)) + } +} + +type solidityContract struct { + *vm.SNativeContractDescription +} + +type solidityFunction struct { + *vm.SNativeFunctionDescription +} + +// +// Contract +// + +// Create a templated solidityContract from an SNative contract description +func NewSolidityContract(contract *vm.SNativeContractDescription) *solidityContract { + return &solidityContract{contract} +} + +func (contract *solidityContract) Comment() string { + return comment(contract.SNativeContractDescription.Comment) +} + +// Get a version of the contract name to be used for an instance of the contract +func (contract *solidityContract) InstanceName() string { + // Hopefully the contract name is UpperCamelCase. If it's not, oh well, this + // is meant to be illustrative rather than cast iron compilable + instanceName := strings.ToLower(contract.Name[:1]) + contract.Name[1:] + if instanceName == contract.Name { + return "contractInstance" + } + return instanceName +} + +func (contract *solidityContract) Address() string { + return fmt.Sprintf("0x%x", + contract.SNativeContractDescription.Address()) +} + +// Generate Solidity code for this SNative contract +func (contract *solidityContract) Solidity() (string, error) { + buf := new(bytes.Buffer) + err := contractTemplate.Execute(buf, contract) + if err != nil { + return "", err + } + return buf.String(), nil +} + +func (contract *solidityContract) Functions() []*solidityFunction { + functions := contract.SNativeContractDescription.Functions() + solidityFunctions := make([]*solidityFunction, len(functions)) + for i, function := range functions { + solidityFunctions[i] = NewSolidityFunction(function) + } + return solidityFunctions +} + +// +// Function +// + +// Create a templated solidityFunction from an SNative function description +func NewSolidityFunction(function *vm.SNativeFunctionDescription) *solidityFunction { + return &solidityFunction{function} +} + +func (function *solidityFunction) ArgList() string { + argList := make([]string, len(function.Args)) + for i, arg := range function.Args { + argList[i] = fmt.Sprintf("%s %s", arg.TypeName, arg.Name) + } + return strings.Join(argList, ", ") +} + +func (function *solidityFunction) Comment() string { + return comment(function.SNativeFunctionDescription.Comment) +} + +func (function *solidityFunction) SolidityIndent(indentLevel uint) (string, error) { + return function.solidity(indentLevel) +} + +func (function *solidityFunction) Solidity() (string, error) { + return function.solidity(0) +} + +func (function *solidityFunction) solidity(indentLevel uint) (string, error) { + buf := new(bytes.Buffer) + iw := NewIndentWriter(indentLevel, indentString, buf) + err := functionTemplate.Execute(iw, function) + if err != nil { + return "", err + } + return buf.String(), nil +} + +// +// Utility +// + +func comment(comment string) string { + commentLines := make([]string, 0, 5) + for _, line := range strings.Split(comment, "\n") { + trimLine := strings.TrimLeft(line, " \t\n") + if trimLine != "" { + commentLines = append(commentLines, trimLine) + } + } + return strings.Join(commentLines, "\n") +} diff --git a/util/snatives/templates/solidity_templates_test.go b/util/snatives/templates/solidity_templates_test.go new file mode 100644 index 000000000..c839230ea --- /dev/null +++ b/util/snatives/templates/solidity_templates_test.go @@ -0,0 +1,44 @@ +// 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 templates + +import ( + "fmt" + "github.com/eris-ltd/eris-db/manager/eris-mint/evm" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestSNativeFuncTemplate(t *testing.T) { + contract := vm.SNativeContracts()["Permissions"] + function, err := contract.FunctionByName("removeRole") + if err != nil { + t.Fatal("Couldn't get function") + } + solidityFunction := NewSolidityFunction(function) + solidity, err := solidityFunction.Solidity() + assert.NoError(t, err) + fmt.Println(solidity) +} + +// This test checks that we can generate the SNative contract interface and +// prints it to stdout +func TestSNativeContractTemplate(t *testing.T) { + contract := vm.SNativeContracts()["Permissions"] + solidityContract := NewSolidityContract(contract) + solidity, err := solidityContract.Solidity() + assert.NoError(t, err) + fmt.Println(solidity) +} diff --git a/util/util.go b/util/util.go index c0acaefc6..d170c6815 100644 --- a/util/util.go +++ b/util/util.go @@ -1,3 +1,17 @@ +// 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 util import ( diff --git a/version/version.go b/version/version.go index 36e4f305a..103c787d1 100644 --- a/version/version.go +++ b/version/version.go @@ -1,24 +1,22 @@ -// Copyright 2015, 2016 Eris Industries (UK) Ltd. -// This file is part of Eris-RT +// 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. -// Eris-RT is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Eris-RT is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Eris-RT. If not, see . +package version // version provides the current Eris-DB version and a VersionIdentifier // for the modules to identify their version with. -package version - import ( "fmt" ) @@ -32,9 +30,9 @@ const ( // Major version component of the current release erisVersionMajor = 0 // Minor version component of the current release - erisVersionMinor = 12 + erisVersionMinor = 16 // Patch version component of the current release - erisVersionPatch = 1 + erisVersionPatch = 0 ) var erisVersion *VersionIdentifier @@ -129,4 +127,4 @@ func (version *VersionIdentifier) MatchesMinorVersion( // IMPORTANT: Eris-DB version must be on the last line of this file for // the deployment script tests/build_tool.sh to pick up the right label. -const VERSION = "0.12.1" +const VERSION = "0.16.0" diff --git a/word256/byteslice.go b/word256/byteslice.go new file mode 100644 index 000000000..88dbaf7c8 --- /dev/null +++ b/word256/byteslice.go @@ -0,0 +1,61 @@ +// 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 word256 + +// TODO: [ben] byteslice is not specific for word256, but it is used exclusively +// for word256 (and for word160* 20byte addresses) so consider stronger typing. + +import ( + "bytes" +) + +func Fingerprint(slice []byte) []byte { + fingerprint := make([]byte, 6) + copy(fingerprint, slice) + return fingerprint +} + +func IsZeros(slice []byte) bool { + for _, byt := range slice { + if byt != byte(0) { + return false + } + } + return true +} + +func RightPadBytes(slice []byte, l int) []byte { + if l < len(slice) { + return slice + } + padded := make([]byte, l) + copy(padded[0:len(slice)], slice) + return padded +} + +func LeftPadBytes(slice []byte, l int) []byte { + if l < len(slice) { + return slice + } + padded := make([]byte, l) + copy(padded[l-len(slice):], slice) + return padded +} + +func TrimmedString(b []byte) string { + trimSet := string([]byte{0}) + return string(bytes.TrimLeft(b, trimSet)) + +} diff --git a/word256/int.go b/word256/int.go new file mode 100644 index 000000000..7e91add15 --- /dev/null +++ b/word256/int.go @@ -0,0 +1,74 @@ +// 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 word256 + +// NOTE: [ben] this used to be in tendermint/go-common but should be +// isolated and cleaned up and tested. Should be used in permissions +// and manager/eris-mint +// TODO: [ben] cleanup, but also write unit-tests + +import ( + "encoding/binary" + "sort" +) + +// Sort for []uint64 + +type Uint64Slice []uint64 + +func (p Uint64Slice) Len() int { return len(p) } +func (p Uint64Slice) Less(i, j int) bool { return p[i] < p[j] } +func (p Uint64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p Uint64Slice) Sort() { sort.Sort(p) } + +func SearchUint64s(a []uint64, x uint64) int { + return sort.Search(len(a), func(i int) bool { return a[i] >= x }) +} + +func (p Uint64Slice) Search(x uint64) int { return SearchUint64s(p, x) } + +//-------------------------------------------------------------------------------- + +func PutUint64LE(dest []byte, i uint64) { + binary.LittleEndian.PutUint64(dest, i) +} + +func GetUint64LE(src []byte) uint64 { + return binary.LittleEndian.Uint64(src) +} + +func PutUint64BE(dest []byte, i uint64) { + binary.BigEndian.PutUint64(dest, i) +} + +func GetUint64BE(src []byte) uint64 { + return binary.BigEndian.Uint64(src) +} + +func PutInt64LE(dest []byte, i int64) { + binary.LittleEndian.PutUint64(dest, uint64(i)) +} + +func GetInt64LE(src []byte) int64 { + return int64(binary.LittleEndian.Uint64(src)) +} + +func PutInt64BE(dest []byte, i int64) { + binary.BigEndian.PutUint64(dest, uint64(i)) +} + +func GetInt64BE(src []byte) int64 { + return int64(binary.BigEndian.Uint64(src)) +} diff --git a/word256/word.go b/word256/word.go new file mode 100644 index 000000000..e5ce7cb7c --- /dev/null +++ b/word256/word.go @@ -0,0 +1,112 @@ +// 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 word256 + +// NOTE: [ben] this used to be in tendermint/go-common but should be +// isolated and cleaned up and tested. Should be used in permissions +// and manager/eris-mint +// TODO: [ben] cleanup, but also write unit-tests + +import ( + "bytes" + "sort" +) + +var ( + Zero256 = Word256{0} + One256 = Word256{1} +) + +const Word256Length = 32 + +type Word256 [Word256Length]byte + +func (w Word256) String() string { return string(w[:]) } +func (w Word256) TrimmedString() string { return TrimmedString(w.Bytes()) } +func (w Word256) Copy() Word256 { return w } +func (w Word256) Bytes() []byte { return w[:] } // copied. +func (w Word256) Prefix(n int) []byte { return w[:n] } +func (w Word256) Postfix(n int) []byte { return w[32-n:] } +func (w Word256) IsZero() bool { + accum := byte(0) + for _, byt := range w { + accum |= byt + } + return accum == 0 +} +func (w Word256) Compare(other Word256) int { + return bytes.Compare(w[:], other[:]) +} + +func Uint64ToWord256(i uint64) Word256 { + buf := [8]byte{} + PutUint64BE(buf[:], i) + return LeftPadWord256(buf[:]) +} + +func Int64ToWord256(i int64) Word256 { + buf := [8]byte{} + PutInt64BE(buf[:], i) + return LeftPadWord256(buf[:]) +} + +func RightPadWord256(bz []byte) (word Word256) { + copy(word[:], bz) + return +} + +func LeftPadWord256(bz []byte) (word Word256) { + copy(word[32-len(bz):], bz) + return +} + +func Uint64FromWord256(word Word256) uint64 { + buf := word.Postfix(8) + return GetUint64BE(buf) +} + +func Int64FromWord256(word Word256) int64 { + buf := word.Postfix(8) + return GetInt64BE(buf) +} + +//------------------------------------- + +type Tuple256 struct { + First Word256 + Second Word256 +} + +func (tuple Tuple256) Compare(other Tuple256) int { + firstCompare := tuple.First.Compare(other.First) + if firstCompare == 0 { + return tuple.Second.Compare(other.Second) + } else { + return firstCompare + } +} + +func Tuple256Split(t Tuple256) (Word256, Word256) { + return t.First, t.Second +} + +type Tuple256Slice []Tuple256 + +func (p Tuple256Slice) Len() int { return len(p) } +func (p Tuple256Slice) Less(i, j int) bool { + return p[i].Compare(p[j]) < 0 +} +func (p Tuple256Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p Tuple256Slice) Sort() { sort.Sort(p) }