Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a7b8743
feat: add multi arch support for docker
pablocampogo Jul 13, 2026
d5446cf
fix: bound consensus auxiliary inputs
andrewnguyen22 Aug 24, 2026
0b86d15
fix: reject malformed VDF proofs
andrewnguyen22 Aug 24, 2026
7d472f1
fix: preserve consensus gossip routing
andrewnguyen22 Aug 24, 2026
d459091
fix: authenticate consensus leaders and sync tip
andrewnguyen22 Aug 24, 2026
419928c
fix: require over one-third power for pacemaker
andrewnguyen22 Aug 24, 2026
7d1d955
Merge pull request #539 from canopy-network/fix/consensus-auxiliary-i…
rem1niscence Aug 24, 2026
3b6c01b
chore: do changes for development branch
pablocampogo Aug 28, 2026
cc20a16
Merge branch 'development' of github.com:canopy-network/canopy into m…
pablocampogo Aug 31, 2026
ae00d97
feat: change plugins to be uploaded to an image instead of a tag
pablocampogo Aug 31, 2026
e0ddfdd
feat: add configurable transfer screening
andrewnguyen22 Sep 1, 2026
f72c3f6
fix: enforce transfer screening in CheckTx
andrewnguyen22 Sep 1, 2026
fcf816c
fix: complete restricted address list
andrewnguyen22 Sep 1, 2026
bfe6169
feat: track restricted transaction rejections
andrewnguyen22 Sep 1, 2026
4d0b592
docs: clarify restricted address proposal behavior
andrewnguyen22 Sep 1, 2026
5731b72
chore: eliminate unnecessary argument
pablocampogo Sep 1, 2026
9832554
Merge pull request #470 from canopy-network/multi-arch-docker
pablocampogo Sep 1, 2026
1a9a01d
fix: reset restricted transaction gauge per block
andrewnguyen22 Sep 1, 2026
7b058c1
Merge pull request #576 from canopy-network/feat/fsm-update
rem1niscence Sep 1, 2026
3335033
chore: version bump
rem1niscence Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/protect-main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Protect main

on:
pull_request:
branches:
- main
types:
- opened
- synchronize
- reopened
- edited

jobs:
require-development:
name: Require development source branch
runs-on: ubuntu-latest
steps:
- name: Check source branch
env:
HEAD_REF: ${{ github.head_ref }}
run: |
if [ "$HEAD_REF" != "development" ]; then
echo "::error::PRs targeting main must come from the development branch (got '$HEAD_REF'). Open this PR against development instead, then promote development to main."
exit 1
fi
echo "PR is from development → main. OK."
178 changes: 119 additions & 59 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ jobs:
with:
ref: ${{ github.ref }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4

Expand All @@ -103,18 +106,25 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push
- name: Prepare tags
id: prep
run: |
TAG=${{ needs.validate.outputs.tag }}
DOCKER_TAG=${TAG//+/-}
docker build \
-t canopynetwork/canopy:${DOCKER_TAG} \
-t canopynetwork/canopy:latest \
--build-arg BRANCH=${TAG} \
--build-arg BUILD_PATH=cmd/cli \
./.docker/auto-update/
docker push canopynetwork/canopy:${DOCKER_TAG}
docker push canopynetwork/canopy:latest
echo "docker_tag=${DOCKER_TAG}" >> $GITHUB_OUTPUT

- name: Build and push
uses: docker/build-push-action@v6
with:
context: ./.docker/auto-update/
platforms: linux/amd64,linux/arm64
push: true
tags: |
canopynetwork/canopy:${{ steps.prep.outputs.docker_tag }}
canopynetwork/canopy:latest
build-args: |
BRANCH=${{ github.event.inputs.tag }}
BUILD_PATH=cmd/cli

docker-go:
name: Docker - Go Plugin
Expand All @@ -127,6 +137,9 @@ jobs:
with:
ref: ${{ github.ref }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

Expand All @@ -136,19 +149,26 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push
- name: Prepare tags
id: prep
run: |
TAG=${{ needs.validate.outputs.tag }}
DOCKER_TAG=${TAG//+/-}
docker build \
-t canopynetwork/canopy:go-${DOCKER_TAG} \
-t canopynetwork/canopy:go-latest \
--build-arg BRANCH=${TAG} \
--build-arg BUILD_PATH=cmd/cli \
-f ./.docker/auto-update/Dockerfile.go \
./.docker/auto-update/
docker push canopynetwork/canopy:go-${DOCKER_TAG}
docker push canopynetwork/canopy:go-latest
echo "docker_tag=${DOCKER_TAG}" >> $GITHUB_OUTPUT

- name: Build and push
uses: docker/build-push-action@v6
with:
context: ./.docker/auto-update/
file: ./.docker/auto-update/Dockerfile.go
platforms: linux/amd64,linux/arm64
push: true
tags: |
canopynetwork/canopy-plugin-go:${{ steps.prep.outputs.docker_tag }}
canopynetwork/canopy-plugin-go:latest
build-args: |
BRANCH=${{ github.event.inputs.tag }}
BUILD_PATH=cmd/cli

docker-python:
name: Docker - Python Plugin
Expand All @@ -161,6 +181,9 @@ jobs:
with:
ref: ${{ github.ref }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

Expand All @@ -170,19 +193,26 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push
- name: Prepare tags
id: prep
run: |
TAG=${{ needs.validate.outputs.tag }}
DOCKER_TAG=${TAG//+/-}
docker build \
-t canopynetwork/canopy:python-${DOCKER_TAG} \
-t canopynetwork/canopy:python-latest \
--build-arg BRANCH=${TAG} \
--build-arg BUILD_PATH=cmd/cli \
-f ./.docker/auto-update/Dockerfile.python \
./.docker/auto-update/
docker push canopynetwork/canopy:python-${DOCKER_TAG}
docker push canopynetwork/canopy:python-latest
echo "docker_tag=${DOCKER_TAG}" >> $GITHUB_OUTPUT

- name: Build and push
uses: docker/build-push-action@v6
with:
context: ./.docker/auto-update/
file: ./.docker/auto-update/Dockerfile.python
platforms: linux/amd64,linux/arm64
push: true
tags: |
canopynetwork/canopy-plugin-python:${{ steps.prep.outputs.docker_tag }}
canopynetwork/canopy-plugin-python:latest
build-args: |
BRANCH=${{ github.event.inputs.tag }}
BUILD_PATH=cmd/cli

docker-typescript:
name: Docker - TypeScript Plugin
Expand All @@ -195,6 +225,9 @@ jobs:
with:
ref: ${{ github.ref }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

Expand All @@ -204,19 +237,26 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push
- name: Prepare tags
id: prep
run: |
TAG=${{ needs.validate.outputs.tag }}
DOCKER_TAG=${TAG//+/-}
docker build \
-t canopynetwork/canopy:typescript-${DOCKER_TAG} \
-t canopynetwork/canopy:typescript-latest \
--build-arg BRANCH=${TAG} \
--build-arg BUILD_PATH=cmd/cli \
-f ./.docker/auto-update/Dockerfile.typescript \
./.docker/auto-update/
docker push canopynetwork/canopy:typescript-${DOCKER_TAG}
docker push canopynetwork/canopy:typescript-latest
echo "docker_tag=${DOCKER_TAG}" >> $GITHUB_OUTPUT

- name: Build and push
uses: docker/build-push-action@v6
with:
context: ./.docker/auto-update/
file: ./.docker/auto-update/Dockerfile.typescript
platforms: linux/amd64,linux/arm64
push: true
tags: |
canopynetwork/canopy-plugin-typescript:${{ steps.prep.outputs.docker_tag }}
canopynetwork/canopy-plugin-typescript:latest
build-args: |
BRANCH=${{ github.event.inputs.tag }}
BUILD_PATH=cmd/cli

docker-kotlin:
name: Docker - Kotlin Plugin
Expand All @@ -229,6 +269,9 @@ jobs:
with:
ref: ${{ github.ref }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

Expand All @@ -238,19 +281,26 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push
- name: Prepare tags
id: prep
run: |
TAG=${{ needs.validate.outputs.tag }}
DOCKER_TAG=${TAG//+/-}
docker build \
-t canopynetwork/canopy:kotlin-${DOCKER_TAG} \
-t canopynetwork/canopy:kotlin-latest \
--build-arg BRANCH=${TAG} \
--build-arg BUILD_PATH=cmd/cli \
-f ./.docker/auto-update/Dockerfile.kotlin \
./.docker/auto-update/
docker push canopynetwork/canopy:kotlin-${DOCKER_TAG}
docker push canopynetwork/canopy:kotlin-latest
echo "docker_tag=${DOCKER_TAG}" >> $GITHUB_OUTPUT

- name: Build and push
uses: docker/build-push-action@v6
with:
context: ./.docker/auto-update/
file: ./.docker/auto-update/Dockerfile.kotlin
platforms: linux/amd64,linux/arm64
push: true
tags: |
canopynetwork/canopy-plugin-kotlin:${{ steps.prep.outputs.docker_tag }}
canopynetwork/canopy-plugin-kotlin:latest
build-args: |
BRANCH=${{ github.event.inputs.tag }}
BUILD_PATH=cmd/cli

docker-csharp:
name: Docker - C# Plugin
Expand All @@ -263,6 +313,9 @@ jobs:
with:
ref: ${{ github.ref }}

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

Expand All @@ -272,16 +325,23 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push
- name: Prepare tags
id: prep
run: |
TAG=${{ needs.validate.outputs.tag }}
DOCKER_TAG=${TAG//+/-}
docker build \
-t canopynetwork/canopy:csharp-${DOCKER_TAG} \
-t canopynetwork/canopy:csharp-latest \
--build-arg BRANCH=${TAG} \
--build-arg BUILD_PATH=cmd/cli \
-f ./.docker/auto-update/Dockerfile.csharp \
./.docker/auto-update/
docker push canopynetwork/canopy:csharp-${DOCKER_TAG}
docker push canopynetwork/canopy:csharp-latest
echo "docker_tag=${DOCKER_TAG}" >> $GITHUB_OUTPUT

- name: Build and push
uses: docker/build-push-action@v6
with:
context: ./.docker/auto-update/
file: ./.docker/auto-update/Dockerfile.csharp
platforms: linux/amd64,linux/arm64
push: true
tags: |
canopynetwork/canopy-plugin-csharp:${{ steps.prep.outputs.docker_tag }}
canopynetwork/canopy-plugin-csharp:latest
build-args: |
BRANCH=${{ github.event.inputs.tag }}
BUILD_PATH=cmd/cli
5 changes: 3 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,10 @@ discuss your intended approach for solving the problem in the comments for an ex
- **Update the CHANGELOG** for all enhancements and bug fixes. Include the corresponding date, issue
number if one exists and current version if any. Check the format of the [CHANGELOG](.docs/CHANGELOG.md).

- **Use the repo's default main branch.** Branch from and
- **Target the `development` branch.** The repo's default branch is still `main`, but do **not**
open pull requests against `main`. Branch from and
[submit your pull request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request-from-a-fork)
to the repo's default branch `main`.
to `development`.

-
**[Resolve any merge conflicts](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/resolving-a-merge-conflict-on-github)**
Expand Down
18 changes: 11 additions & 7 deletions bft/bft.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,9 @@ func (b *BFT) Start() {
func() {
b.Controller.Lock()
defer b.Controller.Unlock()
// calculate time since
since := time.Since(resetBFT.StartTime)
// allow if 'since' is less than 1 block old
if int(since.Milliseconds()) < b.Config.BlockTimeMS() {
processTime = validProcessTime(resetBFT.StartTime, time.Duration(b.Config.BlockTimeMS())*time.Millisecond)
if processTime != 0 {
b.log.Infof("Using included timestamp to calculate process time: %s", resetBFT.StartTime.Format(time.StampMilli))
processTime = since
}
// if is a root-chain update reset back to round 0 but maintain locks to prevent 'fork attacks'
// else increment the height and don't maintain locks
Expand All @@ -185,6 +182,13 @@ func (b *BFT) Start() {
}
}

func validProcessTime(start time.Time, maxAge time.Duration) time.Duration {
if since := time.Since(start); since > 0 && since < maxAge {
return since
}
return 0
}

// HandlePhase() is the main BFT Phase stepping loop
func (b *BFT) HandlePhase() {
stopTimers := func() { b.PhaseTimer.Stop() }
Expand Down Expand Up @@ -609,8 +613,8 @@ func (b *BFT) Pacemaker() bool {
continue
}
totalVotedPower += validator.VotingPower
// if totalVotePower >= +33%, it's safe to advance to that round
if totalVotedPower >= lib.Uint64ReducePercentage(b.ValidatorSet.MinimumMaj23, 50) {
// if totalVotePower > 1/3, it's safe to advance to that round
if totalVotedPower > b.ValidatorSet.TotalPower/3 {
pacemakerRound = vote.Qc.Header.Round // set the highest round where +1/3rds have been
break
}
Expand Down
19 changes: 19 additions & 0 deletions bft/bft_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ func TestStartElectionPhase(t *testing.T) {
}
}

func TestValidProcessTimeRejectsFutureTimestamp(t *testing.T) {
require.Zero(t, validProcessTime(time.Now().Add(time.Hour), time.Second))
require.Zero(t, validProcessTime(time.Now().Add(-time.Hour), time.Second))
require.Positive(t, validProcessTime(time.Now().Add(-time.Millisecond), time.Second))
}

func TestStartElectionVotePhase(t *testing.T) {
tests := []struct {
name string
Expand Down Expand Up @@ -745,6 +751,19 @@ func TestPacemaker(t *testing.T) {
}
}

func TestPacemakerRequiresMoreThanOneThird(t *testing.T) {
c := newTestConsensus(t, Propose, 2)
c.bft.ValidatorSet.ValidatorSet.ValidatorSet[0].VotingPower = 67
c.bft.ValidatorSet.ValidatorSet.ValidatorSet[1].VotingPower = 33
c.bft.ValidatorSet.TotalPower = 100
msg := &Message{Qc: &QC{Header: c.view(RoundInterrupt, 3)}}
require.NoError(t, msg.Sign(c.valKeys[1]))
require.NoError(t, c.bft.HandleMessage(msg))

c.bft.Pacemaker()
require.Equal(t, uint64(1), c.bft.Round)
}

func TestScheduleForceRound(t *testing.T) {
c := newTestConsensus(t, Election, 1)
c.bft.Round = 2
Expand Down
Loading
Loading