diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 4e545e1642..ca898a9d2a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,96 +1,125 @@ -# Builds and uploads docker images whenever a version tag is pushed. When a release is created, -# the associated image is then tagged with the version number and latest. - -name: Docker +name: "Build multi-arch images" on: - workflow_dispatch: push: tags: - - 'v*' - release: - types: [published] - -env: - # Workaround for https://github.com/rust-lang/cargo/issues/8719#issuecomment-1516492970 - CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse + - "v*.*.*" jobs: - # Build a docker image unless this was triggered by a release. - build-image: - if: github.event_name != 'release' - runs-on: pathfinder-large-ubuntu + image-info: + name: "Extract crate info" + runs-on: "ubuntu-latest" + outputs: + version: ${{ steps.derive.outputs.version }} + steps: - - name: Determine Docker image metadata - id: meta - uses: docker/metadata-action@v5 - with: - images: eqlabs/pathfinder - - name: Checkout sources - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Generate version - id: generate_version + - id: "derive" + name: "Derive image info from Git tag" run: | - echo -n "pathfinder_version=" >> $GITHUB_OUTPUT - git describe --tags --dirty >> $GITHUB_OUTPUT - - name: Set up QEMU - id: qemu - uses: docker/setup-qemu-action@v3 - with: - image: tonistiigi/binfmt:latest - platforms: all - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3 - with: - config-inline: | - [worker.oci] - max-parallelism = 4 - - name: Login to Docker Hub - uses: docker/login-action@v3 + FULL_REF="${{ github.ref }}" + REGEX="^refs\/tags\/v(.*)$" + [[ $FULL_REF =~ $REGEX ]]; + + echo "version=${BASH_REMATCH[1]}" >> $GITHUB_OUTPUT + + build-amd64: + name: "Build for linux/amd64" + runs-on: "ubuntu-latest" + needs: + - "image-info" + + env: + DOCKER_REPOSITORY: "starknet/pathfinder-firehose" + + steps: + - name: "Checkout" + uses: "actions/checkout@v3" + + - name: "Login to Docker Hub" + uses: "docker/login-action@v1.6.0" with: - username: ${{ secrets.DOCKER_HUB_USERNAME }} - password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - # Required for git security reasons. See https://github.com/rustyhorde/vergen/pull/126#issuecomment-1201088162 - - name: Vergen git safe directory - run: git config --global --add safe.directory /workspace - - name: Build - id: docker_build - uses: docker/build-push-action@v5 + username: "${{ secrets.DOCKER_HUB_USERNAME }}" + password: "${{ secrets.DOCKER_HUB_PASSWORD }}" + + - name: "Build Docker image" + run: | + docker buildx build \ + --build-arg PATHFINDER_FORCE_VERSION="v${{ needs.image-info.outputs.version }}" \ + --platform "linux/amd64" \ + -t ${DOCKER_REPOSITORY}:${{ needs.image-info.outputs.version }}-amd64 -f ./Dockerfile . + + - name: "Push Docker image" + run: | + docker push ${DOCKER_REPOSITORY}:${{ needs.image-info.outputs.version }}-amd64 + + build-arm64: + name: "Build for linux/arm64" + runs-on: "ubuntu-latest" + needs: + - "image-info" + + env: + DOCKER_REPOSITORY: "starknet/pathfinder-firehose" + + steps: + - name: "Checkout" + uses: "actions/checkout@v3" + + - name: "Login to Docker Hub" + uses: "docker/login-action@v1.6.0" with: - context: . - platforms: | - linux/amd64 - linux/arm64 - file: ./Dockerfile - build-args: | - PATHFINDER_FORCE_VERSION=${{ steps.generate_version.outputs.pathfinder_version }} - builder: ${{ steps.buildx.outputs.name }} - push: true - labels: ${{ steps.meta.outputs.labels }} - tags: eqlabs/pathfinder:snapshot-${{ github.sha }} - cache-from: type=gha - cache-to: type=gha,mode=max - - # Add the release labels to the associated docker image. - tag-release: - if: github.event_name == 'release' - runs-on: ubuntu-latest + username: "${{ secrets.DOCKER_HUB_USERNAME }}" + password: "${{ secrets.DOCKER_HUB_PASSWORD }}" + + - name: "Set up Docker Buildx" + run: | + docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + sudo systemctl restart docker + docker buildx create --name multi_builder + docker buildx use multi_builder + + - name: "Build Docker image" + run: | + docker buildx build \ + --build-arg PATHFINDER_FORCE_VERSION="v${{ needs.image-info.outputs.version }}" \ + --platform "linux/arm64/v8" \ + --output=type=docker \ + -t ${DOCKER_REPOSITORY}:${{ needs.image-info.outputs.version }}-arm64 -f ./Dockerfile . + + - name: "Push Docker image" + run: | + docker push ${DOCKER_REPOSITORY}:${{ needs.image-info.outputs.version }}-arm64 + + push: + name: "Push multi-arch manifest" + runs-on: "ubuntu-latest" + needs: + - "build-amd64" + - "build-arm64" + - "image-info" + + env: + DOCKER_REPOSITORY: "starknet/pathfinder-firehose" + steps: - - name: Login to Docker Hub - uses: docker/login-action@v3 + - name: "Login to Docker Hub" + uses: "docker/login-action@v1.6.0" with: - username: ${{ secrets.DOCKER_HUB_USERNAME }} - password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} - - name: Pull image - run: docker pull eqlabs/pathfinder:snapshot-${{ github.sha }} - - name: Tag image with release name - run: docker tag eqlabs/pathfinder:snapshot-${{ github.sha }} eqlabs/pathfinder:${{ github.event.release.tag_name }} - # Only tag the image as 'latest' if its a release i.e. not a prerelease. - - name: Tag image with 'latest' - if: ${{ !github.event.release.prerelease }} - run: docker tag eqlabs/pathfinder:snapshot-${{ github.sha }} eqlabs/pathfinder:latest - - name: Push image tags - run: docker push --all-tags eqlabs/pathfinder + username: "${{ secrets.DOCKER_HUB_USERNAME }}" + password: "${{ secrets.DOCKER_HUB_PASSWORD }}" + + - name: "Pull Docker images" + run: | + docker pull ${DOCKER_REPOSITORY}:${{ needs.image-info.outputs.version }}-amd64 + docker pull ${DOCKER_REPOSITORY}:${{ needs.image-info.outputs.version }}-arm64 + + - name: "Push Docker image" + run: | + docker manifest create ${DOCKER_REPOSITORY}:${{ needs.image-info.outputs.version }} \ + ${DOCKER_REPOSITORY}:${{ needs.image-info.outputs.version }}-amd64 \ + ${DOCKER_REPOSITORY}:${{ needs.image-info.outputs.version }}-arm64 + docker manifest create ${DOCKER_REPOSITORY}:latest \ + ${DOCKER_REPOSITORY}:${{ needs.image-info.outputs.version }}-amd64 \ + ${DOCKER_REPOSITORY}:${{ needs.image-info.outputs.version }}-arm64 + docker manifest push ${DOCKER_REPOSITORY}:${{ needs.image-info.outputs.version }} + docker manifest push ${DOCKER_REPOSITORY}:latest diff --git a/.github/workflows/sync.yml b/.github/workflows/sync.yml new file mode 100644 index 0000000000..aeac644e94 --- /dev/null +++ b/.github/workflows/sync.yml @@ -0,0 +1,81 @@ +on: + schedule: + # 1:05 past midnight UTC + - cron: "5 1 * * *" + +name: "Sync with upstream" +jobs: + sync: + name: "Sync" + runs-on: "ubuntu-latest" + + env: + DEFAULT_BRANCH: "main" + + steps: + # https://github.com/actions/runner-images/issues/2840#issuecomment-790492173 + - name: "Free up disk space" + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf "/usr/local/share/boost" + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + + - name: "Checkout source code" + uses: "actions/checkout@v3" + with: + # Subsequent actions are not triggered unless using PAT + token: "${{ secrets.GH_PAT }}" + fetch-depth: 0 + submodules: true + + - name: "Setup toolchain" + uses: "actions-rs/toolchain@v1" + with: + toolchain: "stable" + profile: "minimal" + override: true + + - uses: "Swatinem/rust-cache@v1" + with: + cache-on-failure: true + + - name: "Install protoc" + uses: "Noelware/setup-protoc@1.1.0" + + - name: "Config Git" + run: | + git config user.name "Jonathan LEI" + git config user.email "me@xjonathan.dev" + + - name: "Update branch" + run: | + git fetch origin + git remote add upstream https://github.com/eqlabs/pathfinder + git fetch upstream --no-tags + + MERGE_BASE=$(git merge-base origin/$DEFAULT_BRANCH upstream/$DEFAULT_BRANCH) + + # Don't force push unnecessarily unless changes are detected + if [[ $(git rev-list $MERGE_BASE..upstream/$DEFAULT_BRANCH | wc -l) -ne 0 ]]; then + # Brings files from `home` to default branch + git checkout $DEFAULT_BRANCH + git reset --hard upstream/$DEFAULT_BRANCH + git checkout origin/home . + git add . + git commit -m "chore: README and CI changes" + + # Here, we pick commits on the default branch except the first one. We do this instead + # of a naive rebase because the `home` branch might have changed, causing merge + # conflicts. + COMMIT_COUNT=$(git rev-list $MERGE_BASE..origin/$DEFAULT_BRANCH | wc -l) + git cherry-pick origin/$DEFAULT_BRANCH~$(($COMMIT_COUNT-1))..origin/$DEFAULT_BRANCH + + # Makes sure the updated local branch builds + cargo build --all --all-targets + + # Push updated branch + git push --force-with-lease + else + echo "No changes detected on upstream $DEFAULT_BRANCH" + fi diff --git a/README.md b/README.md index f6299b936e..62bc59f2cd 100644 --- a/README.md +++ b/README.md @@ -1,361 +1,11 @@ -# Welcome to Pathfinder +# `pathfinder` Fork with Firehose Support -A [Starknet](https://www.starknet.io) full node giving you a safe view into Starknet. +This is a [`pathfinder`](https://github.com/eqlabs/pathfinder) fork with support for the [Firehose protocol](https://firehose.streamingfast.io/), which in turn enables StarkNet support in [The Graph](https://thegraph.com/). It's created and maintained by the [zkLend](https://zklend.com/) team. -Pathfinder is currently in alpha so expect some rough edges but it is already usable today! +Powered by a [GitHub Actions workflow](https://github.com/starknet-graph/pathfinder/actions/workflows/sync.yml), this fork syncs the `main` branch with the upstream continuously: -## Features +- First, a commit is made on top of the upstream `main` branch to bring files from the [`home`](https://github.com/starknet-graph/pathfinder/tree/home) branch to `main`. This is necessary for making changes to CI workflows and the README file you're reading right now. -- access the full Starknet state history - - includes contract code and storage, and transactions -- verifies state using Ethereum - - calculates the Starknet state's Patricia-Merkle Trie root on a block-by-block basis and confirms it against L1 - - this means the contract code and storage are now locally verified -- implements the [Starknet JSON-RPC API](#json-rpc-api) - - Starknet APIs like [starknet.js](https://www.starknetjs.com/) or [starknet.py](https://github.com/software-mansion/starknet.py) - full support using our JSON-RPC API for interacting with Starknet -- run Starknet functions without requiring a Starknet transaction - - executed against the local state -- do fee estimation for transactions +- Then, actual patch commits living on the fork `main` branch gets rebased. Before pushing, the branch is compiled to make sure it still builds, and the team gets notified if it doesn't. -## Feedback - -We appreciate any feedback, especially during this alpha period. -This includes any documentation issues, feature requests and bugs that you may encounter. - -For help or to submit bug reports or feature requests, please open an issue or alternatively visit the Starknet [discord channel](https://discord.com/invite/QypNMzkHbc). - -## Database compatibility - -**pathfinder 0.9.0 contains a change in our Merkle tree storage implementation that makes it impossible to use database files produced by earlier versions.** - -This means that upgrading to 0.9.0 will require either a full re-sync _or_ downloading a [database snapshot](#database-snapshots). - -## Running with Docker - -The `pathfinder` node can be run in the provided Docker image. -Using the Docker image is the easiest way to start `pathfinder`. If for any reason you're interested in how to set up all the -dependencies yourself please check the [Installation from source](doc/install-from-source.md) guide. - -The following assumes you have [Docker installed](https://docs.docker.com/get-docker/) and ready to go. -(In case of Ubuntu installing docker is as easy as running `sudo snap install docker`.) - -The example below uses `$HOME/pathfinder` as the data directory where persistent files used by `pathfinder` will be stored. -It is easiest to create the volume directory as the user who is running the docker command. -If the directory gets created by docker upon startup, it might be unusable for creating files. - -The following commands start the node in the background, also making sure that it starts automatically after reboot: - -```bash -# Ensure the directory has been created before invoking docker -mkdir -p $HOME/pathfinder -# Start the pathfinder container instance running in the background -sudo docker run \ - --name pathfinder \ - --restart unless-stopped \ - --detach \ - -p 9545:9545 \ - --user "$(id -u):$(id -g)" \ - -e RUST_LOG=info \ - -e PATHFINDER_ETHEREUM_API_URL="https://sepolia.infura.io/v3/" \ - -v $HOME/pathfinder:/usr/share/pathfinder/data \ - eqlabs/pathfinder -``` - -To check logs you can use: - -```bash -sudo docker logs -f pathfinder -``` - -The node can be stopped using - -```bash -sudo docker stop pathfinder -``` - - -### Updating the Docker image - -When pathfinder detects there has been a new release, it will log a message similar to: - -``` -WARN New pathfinder release available! Please consider updating your node! release=0.4.5 -``` - -You can try pulling the latest docker image to update it: - -```bash -sudo docker pull eqlabs/pathfinder -``` - -After pulling the updated image you should stop and remove the `pathfinder` container then re-create it with the exact same command -that was used above to start the node: - -```bash -# This stops the running instance -sudo docker stop pathfinder -# This removes the current instance (using the old version of pathfinder) -sudo docker rm pathfinder -# This command re-creates the container instance with the latest version -sudo docker run \ - --name pathfinder \ - --restart unless-stopped \ - --detach \ - -p 9545:9545 \ - --user "$(id -u):$(id -g)" \ - -e RUST_LOG=info \ - -e PATHFINDER_ETHEREUM_API_URL="https://sepolia.infura.io/v3/" \ - -v $HOME/pathfinder:/usr/share/pathfinder/data \ - eqlabs/pathfinder -``` - -### Available images - -Our images are updated on every `pathfinder` release. This means that the `:latest` docker image does not track our `main` branch here, but instead matches the latest `pathfinder` [release](https://github.com/eqlabs/pathfinder/releases). - -### Docker compose - -You can also use `docker-compose` if you prefer that to just using Docker. - -Create the folder `pathfinder` where your `docker-compose.yaml` is - -```bash -mkdir -p pathfinder - -# replace the value by of PATHFINDER_ETHEREUM_API_URL by the HTTP(s) URL pointing to your Ethereum node's endpoint -cp example.pathfinder-var.env pathfinder-var.env - -docker-compose up -d -``` - -To check if it's running well use `docker-compose logs -f`. - -## Database Snapshots - -Re-syncing the whole history for either the mainnet or testnet networks might take a long time. To speed up the process you can use database snapshot files that contain the full state and history of the network up to a specific block. - -The database files are hosted on Cloudflare R2. There are two ways to download the files: - -* Using the [Rclone](https://rclone.org/) tool -* Via the HTTPS URL: we've found this to be less reliable in general - -### Rclone setup - -We recommend using RClone. Add the following to your RClone configuration file (`$HOME/.config/rclone/rclone.conf`): - -```ini -[pathfinder-snapshots] -type = s3 -provider = Cloudflare -env_auth = false -access_key_id = 7635ce5752c94f802d97a28186e0c96d -secret_access_key = 529f8db483aae4df4e2a781b9db0c8a3a7c75c82ff70787ba2620310791c7821 -endpoint = https://cbf011119e7864a873158d83f3304e27.r2.cloudflarestorage.com -acl = private -``` - -You can then download a compressed database using the command: - -```shell -rclone copy -P pathfinder-snapshots:pathfinder-snapshots/sepolia-testnet_0.11.0_47191.sqlite.zst . -``` - -### Uncompressing database snapshots - -**To avoid issues please check that the SHA2-256 checksum of the compressed file you've downloaded matches the value we've published.** - -We're storing database snapshots as SQLite database files compressed with [zstd](https://github.com/facebook/zstd). You can uncompress the files you've downloaded using the following command: - -```shell -zstd -T0 -d sepolia-testnet_0.11.0_47191.sqlite.zst -o testnet-sepolia.sqlite -``` - -This produces uncompressed database file `testnet-sepolia.sqlite` that can then be used by pathfinder. - -### Available database snapshots - -| Network | Block | Pathfinder version required | Filename | Download URL | Compressed size | SHA2-256 checksum of compressed file | -| --------------- | ------ | --------------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------- | --------------- | ------------------------------------------------------------------ | -| Sepolia testnet | 47191 | >= 0.11.0 | `sepolia-testnet_0.11.0_47191.sqlite.zst` | [Download](https://pub-1fac64c3c0334cda85b45bcc02635c32.r2.dev/sepolia-testnet_0.11.0_47191.sqlite.zst) | 1.91 GB | `82704d8382bac460550c3d31dd3c1f4397c4c43a90fb0e38110b0cd07cd94831` | -| mainnet | 595424 | >= 0.11.0 | `mainnet_0.11.0_595424.sqlite.zst` | [Download](https://pub-1fac64c3c0334cda85b45bcc02635c32.r2.dev/mainnet_0.11.0_595424.sqlite.zst) | 469.63 GB | `e42bae71c97c1a403116a7362f15f5180b19e8cc647efb1357f1ae8924dce654` | - -## Configuration - -The `pathfinder` node options can be configured via the command line as well as environment variables. - -The command line options are passed in after the `docker run` options, as follows: - -```bash -sudo docker run --name pathfinder [...] eqlabs/pathfinder:latest -``` - -Using `--help` will display the `pathfinder` options, including their environment variable names: - -```bash -sudo docker run --rm eqlabs/pathfinder:latest --help -``` - -### Pending Support - -Block times on `mainnet` can be prohibitively long for certain applications. As a workaround, Starknet added the concept of a `pending` block which is the block currently under construction. This is supported by pathfinder, and usage is documented in the [JSON-RPC API](#json-rpc-api) with various methods accepting `"block_id"="pending"`. - -### Logging - -Logging can be configured using the `RUST_LOG` environment variable. -We recommend setting it when you start the container: - -```bash -sudo docker run --name pathfinder [...] -e RUST_LOG= eqlabs/pathfinder:latest -``` - -The following log levels are supported, from most to least verbose: - -```bash -trace -debug -info # default -warn -error -``` - -### Network Selection - -The Starknet network can be selected with the `--network` configuration option. - -If `--network` is not specified, network selection will default to match your Ethereum endpoint: - -- Starknet mainnet for Ethereum mainnet, -- Starknet testnet for Ethereum Sepolia - -#### Custom networks & gateway proxies - -You can specify a custom network with `--network custom` and specifying the `--gateway-url`, `feeder-gateway-url` and `chain-id` options. -Note that `chain-id` should be specified as text e.g. `SN_SEPOLIA`. - -This can be used to interact with a custom Starknet gateway, or to use a gateway proxy. - -## JSON-RPC API - -You can interact with Starknet using the JSON-RPC API. Pathfinder supports the official Starknet RPC API and in addition supplements this with its own pathfinder specific extensions such as `pathfinder_getProof`. - -Currently, pathfinder supports `v0.4`, `v0.5`, `v0.6` and `v0.7` versions of the Starknet JSON-RPC specification. -The `path` of the URL used to access the JSON-RPC server determines which version of the API is served: - -- the `v0.4.0` API is exposed on the `/rpc/v0.4` and `/rpc/v0_4` path -- the `v0.5.1` API is exposed on the `/`, `/rpc/v0.5` and `/rpc/v0_5` path -- the `v0.6.0` API is exposed on the `/rpc/v0_6` path -- the `v0.7.0` API is exposed on the `/rpc/v0_7` path -- the pathfinder extension API is exposed on `/rpc/pathfinder/v0.1` -- websocket API is exposed on the `/ws` path - -Version of the API, which is served on the root (`/`) path, can be configured via the pathfinder parameter `--rpc.root-version` (or the `RPC_ROOT_VERSION` environment variable). - -Note that the pathfinder extension is versioned separately from the Starknet specification itself. - -### pathfinder extension API - -Here are links to our [API extensions](doc/rpc/pathfinder_rpc_api.json) and [websocket API](doc/rpc/pathfinder_ws.json). - -## Monitoring API - -Pathfinder has a monitoring API which can be enabled with the `--monitor-address` configuration option. - -### Health - -`/health` provides a method to check the health status of your `pathfinder` node, and is commonly useful in Kubernetes docker setups. It returns a `200 OK` status if the node is healthy. - -### Readiness - -`pathfinder` does several things before it is ready to respond to RPC queries. In most cases this startup time is less than a second, however there are certain scenarios where this can be considerably longer. For example, applying an expensive database migration after an upgrade could take several minutes (or even longer) on testnet. Or perhaps our startup network checks fail many times due to connection issues. - -`/ready` provides a way of checking whether the node's JSON-RPC API is ready to be queried. It returns a `503 Service Unavailable` status until all startup tasks complete, and then `200 OK` from then on. - -### Metrics - -`/metrics` provides a [Prometheus](https://prometheus.io/) metrics scrape endpoint. Currently the following metrics are available: - -#### RPC related counters - -- `rpc_method_calls_total`, -- `rpc_method_calls_failed_total`, - -You __must__ use the label key `method` to retrieve a counter for a particular RPC method, for example: -``` -rpc_method_calls_total{method="starknet_getStateUpdate"} -rpc_method_calls_failed_total{method="starknet_chainId"} -``` -You may also use the label key `version` to specify a particular version of the RPC API, for example: -``` -rpc_method_calls_total{method="starknet_getEvents", version="v0.3"} -``` - -#### Feeder Gateway and Gateway related counters - -- `gateway_requests_total` -- `gateway_requests_failed_total` -- `gateway_request_duration_seconds` - -Labels: -- `method`, to retrieve a counter for a particular sequencer request type -- `tag` - - works with methods: `get_block`, `get_state_update` - - valid values: - - `pending` - - `latest` -- `reason` - - works with: `gateway_requests_failed_total` - - valid values: - - `decode` - - `starknet` - - `rate_limiting` - - `timeout` - -Valid examples: -``` -gateway_requests_total{method="get_block"} -gateway_requests_total{method="get_block", tag="latest"} -gateway_requests_failed_total{method="get_state_update"} -gateway_requests_failed_total{method="get_state_update", tag="pending"} -gateway_requests_failed_total{method="get_state_update", tag="pending", reason="starknet"} -gateway_requests_failed_total{method="get_state_update", reason="rate_limiting"} -``` - -These __will not work__: -- `gateway_requests_total{method="get_transaction", tag="latest"}`, `tag` is not supported for that `method` -- `gateway_requests_total{method="get_transaction", reason="decode"}`, `reason` is only supported for failures. - -### Sync related metrics - -- `current_block` currently sync'd block height of the node -- `highest_block` height of the block chain -- `block_time` timestamp difference between the current block and its parent -- `block_latency` delay between current block being published and sync'd locally -- `block_download` time taken to download current block's data excluding classes -- `block_processing` time taken to process and store the current block -- `block_processing_duration_seconds` histogram of time taken to process and store a block - -### Build info metrics - -- `pathfinder_build_info` reports current version as a `version` property - -## Build from source - -See the [guide](https://github.com/eqlabs/pathfinder/blob/main/doc/install-from-source.md). - -## License - -Licensed under either of - - * Apache License, Version 2.0 - ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license - ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -## Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. +Whenever a version is released on the upstream project, we will make the same release except with the patch applied.