From f8bf3c570a8436a34f1a7faa8b9ae9da3e8bc4a2 Mon Sep 17 00:00:00 2001
From: Ian Suvak <ian.suvak@avalabs.org>
Date: Fri, 3 Jan 2025 10:40:14 -0500
Subject: [PATCH] remove icm-services as a go dependency

---
 go.mod                                        |  19 +-
 go.sum                                        |  34 ++--
 scripts/e2e_test.sh                           |   9 +-
 scripts/install_sig_agg_release.sh            | 122 +++++++++++++
 scripts/versions.sh                           |   5 +-
 tests/flows/governance/validator_set_sig.go   |   6 +-
 tests/flows/teleporter/add_fee_amount.go      |   6 +-
 .../validator-manager/erc20_token_staking.go  |  24 ++-
 .../validator-manager/native_token_staking.go |  24 ++-
 tests/flows/validator-manager/poa_to_pos.go   |  15 +-
 tests/network/network.go                      |   3 +-
 tests/utils/chain.go                          |  59 +-----
 tests/utils/governance.go                     |   3 +-
 tests/utils/ictt.go                           |   9 +-
 tests/utils/signature_aggregator.go           | 170 ++++++++++++++++++
 tests/utils/teleporter.go                     |   9 +-
 tests/utils/validator_manager.go              |  31 ++--
 17 files changed, 389 insertions(+), 159 deletions(-)
 create mode 100755 scripts/install_sig_agg_release.sh
 create mode 100644 tests/utils/signature_aggregator.go

diff --git a/go.mod b/go.mod
index a00f3cd2c..7a13a075a 100644
--- a/go.mod
+++ b/go.mod
@@ -8,13 +8,11 @@ require (
 )
 
 require (
-	github.com/ava-labs/icm-services v1.4.1-0.20241210180248-25d5c7f6c877
 	github.com/ava-labs/subnet-evm v0.6.13-0.20241205165027-6c98da796f35
 	github.com/ethereum/go-ethereum v1.13.14
 	github.com/onsi/ginkgo/v2 v2.22.2
 	github.com/onsi/gomega v1.36.2
 	github.com/pkg/errors v0.9.1
-	github.com/prometheus/client_golang v1.20.5
 	github.com/spf13/cobra v1.8.1
 	github.com/stretchr/testify v1.10.0
 	go.uber.org/zap v1.27.0
@@ -80,7 +78,6 @@ require (
 	github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 // indirect
 	github.com/hashicorp/go-bexpr v0.1.10 // indirect
 	github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
-	github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 // indirect
 	github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
@@ -111,9 +108,9 @@ require (
 	github.com/nbutton23/zxcvbn-go v0.0.0-20180912185939-ae427f1e4c1d // indirect
 	github.com/olekukonko/tablewriter v0.0.5 // indirect
 	github.com/pelletier/go-toml/v2 v2.0.8 // indirect
-	github.com/pingcap/errors v0.11.4 // indirect
 	github.com/pires/go-proxyproto v0.6.2 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
+	github.com/prometheus/client_golang v1.20.5 // indirect
 	github.com/prometheus/client_model v0.6.1 // indirect
 	github.com/prometheus/common v0.55.0 // indirect
 	github.com/prometheus/procfs v0.15.1 // indirect
@@ -136,13 +133,13 @@ require (
 	github.com/urfave/cli/v2 v2.25.7 // indirect
 	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
 	github.com/yusufpapurcu/wmi v1.2.2 // indirect
-	go.opentelemetry.io/otel v1.22.0 // indirect
+	go.opentelemetry.io/otel v1.31.0 // indirect
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 // indirect
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 // indirect
 	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 // indirect
-	go.opentelemetry.io/otel/metric v1.22.0 // indirect
-	go.opentelemetry.io/otel/sdk v1.22.0 // indirect
-	go.opentelemetry.io/otel/trace v1.22.0 // indirect
+	go.opentelemetry.io/otel/metric v1.31.0 // indirect
+	go.opentelemetry.io/otel/sdk v1.31.0 // indirect
+	go.opentelemetry.io/otel/trace v1.31.0 // indirect
 	go.opentelemetry.io/proto/otlp v1.0.0 // indirect
 	go.uber.org/mock v0.5.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
@@ -157,9 +154,9 @@ require (
 	golang.org/x/text v0.21.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
 	gonum.org/v1/gonum v0.11.0 // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
-	google.golang.org/grpc v1.68.1 // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect
+	google.golang.org/grpc v1.69.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
 	gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
diff --git a/go.sum b/go.sum
index 0bf9328a4..8e2d83ec3 100644
--- a/go.sum
+++ b/go.sum
@@ -62,8 +62,6 @@ github.com/ava-labs/avalanchego v1.12.1-0.20241210172525-c7ebd8fbae88 h1:tZdtOPF
 github.com/ava-labs/avalanchego v1.12.1-0.20241210172525-c7ebd8fbae88/go.mod h1:yhD5dpZyStIVbxQ550EDi5w5SL7DQ/xGE6TIxosb7U0=
 github.com/ava-labs/coreth v0.13.9-rc.1 h1:qIICpC/OZGYUP37QnLgIqqwGmxnLwLpZaUlqJNI85vU=
 github.com/ava-labs/coreth v0.13.9-rc.1/go.mod h1:7aMsRIo/3GBE44qWZMjnfqdqfcfZ5yShTTm2LObLaYo=
-github.com/ava-labs/icm-services v1.4.1-0.20241210180248-25d5c7f6c877 h1:rQcSLYmGWJGDVM4Iq1JnxgFVI4Sw+IJiQwGbg0Kw8Gw=
-github.com/ava-labs/icm-services v1.4.1-0.20241210180248-25d5c7f6c877/go.mod h1:IJz87lGsHh16fRh8H0Cv6g+BgZ/wNaAA4fFVkAAAPrc=
 github.com/ava-labs/subnet-evm v0.6.13-0.20241205165027-6c98da796f35 h1:CbXWon0fwGDEDCCiChx2VeIIwO3UML9+8OUTyNwPsxA=
 github.com/ava-labs/subnet-evm v0.6.13-0.20241205165027-6c98da796f35/go.mod h1:SfAF4jjYPkezKWShPY/T31WQdD/UHrDyqy0kxA0LE0w=
 github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
@@ -347,8 +345,6 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs=
 github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
-github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4=
@@ -631,20 +627,22 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
-go.opentelemetry.io/otel v1.22.0 h1:xS7Ku+7yTFvDfDraDIJVpw7XPyuHlB9MCiqqX5mcJ6Y=
-go.opentelemetry.io/otel v1.22.0/go.mod h1:eoV4iAi3Ea8LkAEI9+GFT44O6T/D0GWAVFyZVCC6pMI=
+go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
+go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0 h1:9M3+rhx7kZCIQQhQRYaZCdNu1V73tm4TvXs2ntl98C4=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.22.0/go.mod h1:noq80iT8rrHP1SfybmPiRGc9dc5M8RPmGvtwo7Oo7tc=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0 h1:H2JFgRcGiyHg7H7bwcwaQJYrNFqCqrbTQ8K4p1OvDu8=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.22.0/go.mod h1:WfCWp1bGoYK8MeULtI15MmQVczfR+bFkk0DF3h06QmQ=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 h1:FyjCyI9jVEfqhUh2MoSkmolPjfh5fp2hnV0b0irxH4Q=
 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0/go.mod h1:hYwym2nDEeZfG/motx0p7L7J1N1vyzIThemQsb4g2qY=
-go.opentelemetry.io/otel/metric v1.22.0 h1:lypMQnGyJYeuYPhOM/bgjbFM6WE44W1/T45er4d8Hhg=
-go.opentelemetry.io/otel/metric v1.22.0/go.mod h1:evJGjVpZv0mQ5QBRJoBF64yMuOf4xCWdXjK8pzFvliY=
-go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
-go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
-go.opentelemetry.io/otel/trace v1.22.0 h1:Hg6pPujv0XG9QaVbGOBVHunyuLcCC3jN7WEhPx83XD0=
-go.opentelemetry.io/otel/trace v1.22.0/go.mod h1:RbbHXVqKES9QhzZq/fE5UnOSILqRt40a21sPw2He1xo=
+go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
+go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
+go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
+go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
+go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
+go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
+go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
+go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
 go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
 go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
 go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -1000,10 +998,10 @@ google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6D
 google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
 google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
-google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc=
-google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
+google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U=
+google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
 google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -1022,8 +1020,8 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv
 google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
 google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0=
-google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw=
+google.golang.org/grpc v1.69.0 h1:quSiOM1GJPmPH5XtU+BCoVXcDVJJAzNcoyfC2cCjGkI=
+google.golang.org/grpc v1.69.0/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
diff --git a/scripts/e2e_test.sh b/scripts/e2e_test.sh
index 6020e93a8..11110cbef 100755
--- a/scripts/e2e_test.sh
+++ b/scripts/e2e_test.sh
@@ -74,6 +74,13 @@ echo "Copied ${BASEDIR}/subnet-evm/subnet-evm binary to ${BASEDIR}/avalanchego/p
 
 export AVALANCHEGO_BUILD_PATH=$BASEDIR/avalanchego
 
+ICM_SERVICES_BUILD_PATH=$BASEDIR/icm-services
+
+cd $ICM_CONTRACTS_PATH
+# Install signature-aggregator binary
+BASEDIR=$BASEDIR ICM_SERVICES_BUILD_PATH=$ICM_SERVICES_BUILD_PATH "${ICM_CONTRACTS_PATH}/scripts/install_sig_agg_release.sh"
+echo "Installed signature-aggregator from icm-services release ${ICM_SERVICES_VERSION}"
+
 cd $ICM_CONTRACTS_PATH
 if command -v forge &> /dev/null; then
   forge build --skip test
@@ -93,7 +100,7 @@ for component in $(echo $components | tr ',' ' '); do
 
     echo "Running e2e tests for $component"
 
-    RUN_E2E=true ./tests/suites/$component/$component.test \
+    RUN_E2E=true SIG_AGG_PATH=$ICM_SERVICES_BUILD_PATH/signature-aggregator ./tests/suites/$component/$component.test \
     --ginkgo.vv \
     --ginkgo.label-filter=${GINKGO_LABEL_FILTER:-""} \
     --ginkgo.focus=${GINKGO_FOCUS:-""} \
diff --git a/scripts/install_sig_agg_release.sh b/scripts/install_sig_agg_release.sh
new file mode 100755
index 000000000..90b96a374
--- /dev/null
+++ b/scripts/install_sig_agg_release.sh
@@ -0,0 +1,122 @@
+#!/usr/bin/env bash
+# Copyright (C) 2023, Ava Labs, Inc. All rights reserved.
+# See the file LICENSE for licensing terms.
+
+set -e
+
+# Load the versions
+ICM_CONTRACTS_PATH=$(
+  cd "$(dirname "${BASH_SOURCE[0]}")"
+  cd .. && pwd
+)
+source "$ICM_CONTRACTS_PATH"/scripts/versions.sh
+source "$ICM_CONTRACTS_PATH"/scripts/constants.sh
+
+############################
+# download icm-services
+# https://github.com/ava-labs/icm-services/releases
+GOARCH=$(go env GOARCH)
+GOOS=$(go env GOOS)
+BASEDIR=${BASEDIR:-"/tmp/icm-services-release"}
+ICM_SERVICES_BUILD_PATH=${ICM_SERVICES_BUILD_PATH-${BASEDIR}/icm-services}
+
+mkdir -p ${BASEDIR}
+
+ICM_SERVICES_DOWNLOAD_URL=https://github.com/ava-labs/icm-services/releases/download/${ICM_SERVICES_VERSION}/icm-services_${ICM_SERVICES_VERSION#v}_linux_${GOARCH}.tar.gz
+ICM_SERVICES_DOWNLOAD_PATH=${BASEDIR}/icm-services-linux-${GOARCH}-${ICM_SERVICES_VERSION}.tar.gz
+
+if [[ ${GOOS} == "darwin" ]]; then
+  ICM_SERVICES_DOWNLOAD_URL=https://github.com/ava-labs/icm-services/releases/download/${ICM_SERVICES_VERSION}/icm-services_${ICM_SERVICES_VERSION#v}_darwin_${GOARCH}.tar.gz
+  ICM_SERVICES_DOWNLOAD_PATH=${BASEDIR}/icm-services-darwin-${GOARCH}-${ICM_SERVICES_VERSION}.tar.gz
+fi
+
+BUILD_DIR=${ICM_SERVICES_BUILD_PATH}-${ICM_SERVICES_VERSION}
+
+extract_archive() {
+  mkdir -p ${BUILD_DIR}
+
+  if [[ ${ICM_SERVICES_DOWNLOAD_PATH} == *.tar.gz ]]; then
+    tar xzvf ${ICM_SERVICES_DOWNLOAD_PATH} --directory ${BUILD_DIR}
+  elif [[ ${ICM_SERVICES_DOWNLOAD_PATH} == *.zip ]]; then
+    unzip ${ICM_SERVICES_DOWNLOAD_PATH} -d ${BUILD_DIR}
+    mv ${BUILD_DIR}/build/* ${BUILD_DIR}
+    rm -rf ${BUILD_DIR}/build/
+  fi
+}
+
+# first check if we already have the archive
+if [[ -f ${ICM_SERVICES_DOWNLOAD_PATH} ]]; then
+  # if the download path already exists, extract and exit
+  echo "found icm-services ${ICM_SERVICES_VERSION} at ${ICM_SERVICES_DOWNLOAD_PATH}"
+
+  extract_archive
+else
+  # try to download the archive if it exists
+  if curl -s --head --request GET ${ICM_SERVICES_DOWNLOAD_URL} | grep "302" > /dev/null; then
+    echo "${ICM_SERVICES_DOWNLOAD_URL} found"
+    echo "downloading to ${ICM_SERVICES_DOWNLOAD_PATH}"
+    curl -L ${ICM_SERVICES_DOWNLOAD_URL} -o ${ICM_SERVICES_DOWNLOAD_PATH}
+
+    extract_archive
+  else
+    # else the version is a git commit (or it's invalid)
+    GIT_CLONE_URL=https://github.com/ava-labs/icm-services.git
+    GIT_CLONE_PATH=${BASEDIR}/icm-services-repo/
+
+    # check to see if the repo already exists, if not clone it
+    if [[ ! -d ${GIT_CLONE_PATH} ]]; then
+      echo "cloning ${GIT_CLONE_URL} to ${GIT_CLONE_PATH}"
+      git clone --no-checkout ${GIT_CLONE_URL} ${GIT_CLONE_PATH}
+    fi
+
+    # check to see if the commitish exists in the repo
+    WORKDIR=$(pwd)
+
+    cd ${GIT_CLONE_PATH}
+
+    git fetch
+
+    echo "checking out ${ICM_SERVICES_VERSION}"
+
+    # Try to checkout the branch. If it fails, try the commit.
+    if ! git checkout "origin/${ICM_SERVICES_VERSION}" > /dev/null 2>&1; then
+      if ! git checkout "${ICM_SERVICES_VERSION}" > /dev/null 2>&1; then
+        # If the version is in the format of tag-commit, try to extract the commit and checkout.
+        ICM_SERVICES_VERSION=$(extract_commit "${ICM_SERVICES_VERSION}")
+        if ! git checkout "${ICM_SERVICES_VERSION}" > /dev/null 2>&1; then
+          echo
+          echo "'${ICM_SERVICES_VERSION}' is not a valid release tag, commit hash, or branch name"
+          exit 1
+        fi
+      fi
+    fi
+
+    # initialize the submodules
+    git submodule update --init --recursive
+
+    COMMIT=$(git rev-parse HEAD)
+
+    # use the commit hash instead of the branch name or tag
+    BUILD_DIR=${ICM_SERVICES_BUILD_PATH}-${COMMIT}
+
+    # if the build-directory doesn't exist, build icm-services
+    if [[ ! -d ${BUILD_DIR} ]]; then
+      echo "building icm-services ${COMMIT} to ${BUILD_DIR}"
+      ./scripts/build_signature_aggregator.sh
+      mkdir -p ${BUILD_DIR}
+      cp ./build/signature-aggregator ${BUILD_DIR}/signature-aggregator
+    fi
+
+    cd $WORKDIR
+  fi
+fi
+
+SIGNATURE_AGGREGATOR_PATH=${ICM_SERVICES_BUILD_PATH}/signature-aggregator
+mkdir -p ${ICM_SERVICES_BUILD_PATH}
+
+cp ${BUILD_DIR}/signature-aggregator ${SIGNATURE_AGGREGATOR_PATH}
+
+
+echo "Installed signature-aggregator from icm-services release ${ICM_SERVICES_VERSION}"
+echo "signature-aggregator Path: ${SIGNATURE_AGGREGATOR_PATH}"
+
diff --git a/scripts/versions.sh b/scripts/versions.sh
index de20e4f09..b033e85cf 100755
--- a/scripts/versions.sh
+++ b/scripts/versions.sh
@@ -29,8 +29,9 @@ function extract_commit() {
   echo "$version"
 }
 
-# AWM_RELAYER_VERSION is needed for the docker run setup, but is not a go module dependency.
-AWM_RELAYER_VERSION=${AWM_RELAYER_VERSION:-'v1.0.0'}
+# ICM_SERVICES_VERSION is needed for the E2E tests but is not a direct dependency since that would create a circular dependency.
+# ICM_SERVICES_VERSION=${ICM_SERVICES_VERSION:-'signature-aggregator-v1.0.0-rc.0'}
+ICM_SERVICES_VERSION=${ICM_SERVICES_VERSION:-'7d1e095dc84913d28c9c82806f240595e8cd4c8b'}
 
 # Don't export them as they're used in the context of other calls
 AVALANCHEGO_VERSION=${AVALANCHEGO_VERSION:-$(extract_commit "$(getDepVersion github.com/ava-labs/avalanchego)")}
diff --git a/tests/flows/governance/validator_set_sig.go b/tests/flows/governance/validator_set_sig.go
index 662c5bc77..ab506f855 100644
--- a/tests/flows/governance/validator_set_sig.go
+++ b/tests/flows/governance/validator_set_sig.go
@@ -179,7 +179,7 @@ func ValidatorSetSig(network *localnetwork.LocalNetwork) {
 		validatorSetSigContractAddress,
 		fundedKey,
 		&offchainMessages[0],
-		network.GetSignatureAggregator(),
+		aggregator,
 		false,
 	)
 
@@ -196,7 +196,7 @@ func ValidatorSetSig(network *localnetwork.LocalNetwork) {
 		validatorSetSigContractAddress,
 		fundedKey,
 		&offchainMessages[1],
-		network.GetSignatureAggregator(),
+		aggregator,
 		true,
 	)
 
@@ -228,7 +228,7 @@ func ValidatorSetSig(network *localnetwork.LocalNetwork) {
 		validatorSetSigContractAddress2,
 		fundedKey,
 		&offchainMessages[2],
-		network.GetSignatureAggregator(),
+		aggregator,
 		true,
 	)
 
diff --git a/tests/flows/teleporter/add_fee_amount.go b/tests/flows/teleporter/add_fee_amount.go
index f87961aab..d591a933f 100644
--- a/tests/flows/teleporter/add_fee_amount.go
+++ b/tests/flows/teleporter/add_fee_amount.go
@@ -71,6 +71,8 @@ func AddFeeAmount(network *localnetwork.LocalNetwork, teleporter utils.Teleporte
 		teleporter.TeleporterMessenger(l1AInfo),
 	)
 
+	aggregator := network.GetSignatureAggregator()
+	defer aggregator.Shutdown()
 	// Relay message from L1 A to L1 B
 	deliveryReceipt := teleporter.RelayTeleporterMessage(
 		ctx,
@@ -80,7 +82,7 @@ func AddFeeAmount(network *localnetwork.LocalNetwork, teleporter utils.Teleporte
 		true,
 		fundedKey,
 		nil,
-		network.GetSignatureAggregator(),
+		aggregator,
 	)
 	receiveEvent, err := utils.GetEventFromLogs(
 		deliveryReceipt.Logs,
@@ -123,7 +125,7 @@ func AddFeeAmount(network *localnetwork.LocalNetwork, teleporter utils.Teleporte
 		true,
 		fundedKey,
 		nil,
-		network.GetSignatureAggregator(),
+		aggregator,
 	)
 
 	// Check message delivered
diff --git a/tests/flows/validator-manager/erc20_token_staking.go b/tests/flows/validator-manager/erc20_token_staking.go
index 69ba61975..48316b36a 100644
--- a/tests/flows/validator-manager/erc20_token_staking.go
+++ b/tests/flows/validator-manager/erc20_token_staking.go
@@ -40,12 +40,6 @@ func ERC20TokenStakingManager(network *localnetwork.LocalNetwork) {
 	_, fundedKey := network.GetFundedAccountInfo()
 	pChainInfo := utils.GetPChainInfo(cChainInfo)
 
-	signatureAggregator := utils.NewSignatureAggregator(
-		cChainInfo.NodeURIs[0],
-		[]ids.ID{
-			l1AInfo.L1ID,
-		},
-	)
 	ctx := context.Background()
 
 	nodes, initialValidationIDs, _ := network.ConvertSubnet(
@@ -67,6 +61,14 @@ func ERC20TokenStakingManager(network *localnetwork.LocalNetwork) {
 	erc20, err := exampleerc20.NewExampleERC20(erc20Address, l1AInfo.RPCClient)
 	Expect(err).Should(BeNil())
 
+	signatureAggregator := utils.NewSignatureAggregator(
+		cChainInfo.NodeURIs[0],
+		[]ids.ID{
+			l1AInfo.L1ID,
+		},
+	)
+	defer signatureAggregator.Shutdown()
+
 	//
 	// Delist one initial validator
 	//
@@ -145,9 +147,6 @@ func ERC20TokenStakingManager(network *localnetwork.LocalNetwork) {
 		Expect(err).Should(BeNil())
 		delegationID = initRegistrationEvent.DelegationID
 
-		aggregator := network.GetSignatureAggregator()
-		defer aggregator.Shutdown()
-
 		// Gather subnet-evm Warp signatures for the L1ValidatorWeightMessage & relay to the P-Chain
 		signedWarpMessage := utils.ConstructSignedWarpMessage(
 			context.Background(),
@@ -155,7 +154,7 @@ func ERC20TokenStakingManager(network *localnetwork.LocalNetwork) {
 			l1AInfo,
 			pChainInfo,
 			nil,
-			aggregator,
+			signatureAggregator,
 		)
 
 		// Issue a tx to update the validator's weight on the P-Chain
@@ -214,9 +213,6 @@ func ERC20TokenStakingManager(network *localnetwork.LocalNetwork) {
 		Expect(delegatorRemovalEvent.ValidationID[:]).Should(Equal(validationID[:]))
 		Expect(delegatorRemovalEvent.DelegationID[:]).Should(Equal(delegationID[:]))
 
-		aggregator := network.GetSignatureAggregator()
-		defer aggregator.Shutdown()
-
 		// Gather subnet-evm Warp signatures for the SetL1ValidatorWeightMessage & relay to the P-Chain
 		// (Sending to the P-Chain will be skipped for now)
 		signedWarpMessage := utils.ConstructSignedWarpMessage(
@@ -225,7 +221,7 @@ func ERC20TokenStakingManager(network *localnetwork.LocalNetwork) {
 			l1AInfo,
 			pChainInfo,
 			nil,
-			aggregator,
+			signatureAggregator,
 		)
 		Expect(err).Should(BeNil())
 
diff --git a/tests/flows/validator-manager/native_token_staking.go b/tests/flows/validator-manager/native_token_staking.go
index 4a925aafe..bda0a3ed9 100644
--- a/tests/flows/validator-manager/native_token_staking.go
+++ b/tests/flows/validator-manager/native_token_staking.go
@@ -39,12 +39,6 @@ func NativeTokenStakingManager(network *localnetwork.LocalNetwork) {
 	_, fundedKey := network.GetFundedAccountInfo()
 	pChainInfo := utils.GetPChainInfo(cChainInfo)
 
-	signatureAggregator := utils.NewSignatureAggregator(
-		cChainInfo.NodeURIs[0],
-		[]ids.ID{
-			l1AInfo.L1ID,
-		},
-	)
 	ctx := context.Background()
 
 	nodes, initialValidationIDs, _ := network.ConvertSubnet(
@@ -63,6 +57,14 @@ func NativeTokenStakingManager(network *localnetwork.LocalNetwork) {
 	Expect(err).Should(BeNil())
 	utils.AddNativeMinterAdmin(ctx, l1AInfo, fundedKey, stakingManagerAddress)
 
+	signatureAggregator := utils.NewSignatureAggregator(
+		cChainInfo.NodeURIs[0],
+		[]ids.ID{
+			l1AInfo.L1ID,
+		},
+	)
+	defer signatureAggregator.Shutdown()
+
 	//
 	// Delist one initial validator
 	//
@@ -139,9 +141,6 @@ func NativeTokenStakingManager(network *localnetwork.LocalNetwork) {
 		Expect(err).Should(BeNil())
 		delegationID = initRegistrationEvent.DelegationID
 
-		aggregator := network.GetSignatureAggregator()
-		defer aggregator.Shutdown()
-
 		// Gather subnet-evm Warp signatures for the L1ValidatorWeightMessage & relay to the P-Chain
 		signedWarpMessage := utils.ConstructSignedWarpMessage(
 			context.Background(),
@@ -149,7 +148,7 @@ func NativeTokenStakingManager(network *localnetwork.LocalNetwork) {
 			l1AInfo,
 			pChainInfo,
 			nil,
-			aggregator,
+			signatureAggregator,
 		)
 
 		// Issue a tx to update the validator's weight on the P-Chain
@@ -207,9 +206,6 @@ func NativeTokenStakingManager(network *localnetwork.LocalNetwork) {
 		Expect(delegatorRemovalEvent.ValidationID[:]).Should(Equal(validationID[:]))
 		Expect(delegatorRemovalEvent.DelegationID[:]).Should(Equal(delegationID[:]))
 
-		aggregator := network.GetSignatureAggregator()
-		defer aggregator.Shutdown()
-
 		// Gather subnet-evm Warp signatures for the SetL1ValidatorWeightMessage & relay to the P-Chain
 		// (Sending to the P-Chain will be skipped for now)
 		signedWarpMessage := utils.ConstructSignedWarpMessage(
@@ -218,7 +214,7 @@ func NativeTokenStakingManager(network *localnetwork.LocalNetwork) {
 			l1AInfo,
 			pChainInfo,
 			nil,
-			aggregator,
+			signatureAggregator,
 		)
 		Expect(err).Should(BeNil())
 
diff --git a/tests/flows/validator-manager/poa_to_pos.go b/tests/flows/validator-manager/poa_to_pos.go
index 6b94f5671..4e569a220 100644
--- a/tests/flows/validator-manager/poa_to_pos.go
+++ b/tests/flows/validator-manager/poa_to_pos.go
@@ -43,13 +43,6 @@ func PoAMigrationToPoS(network *localnetwork.LocalNetwork) {
 	_, fundedKey := network.GetFundedAccountInfo()
 	pChainInfo := utils.GetPChainInfo(cChainInfo)
 
-	signatureAggregator := utils.NewSignatureAggregator(
-		cChainInfo.NodeURIs[0],
-		[]ids.ID{
-			l1AInfo.L1ID,
-		},
-	)
-
 	// Generate random address to be the owner address
 	ownerKey, err := crypto.GenerateKey()
 	Expect(err).Should(BeNil())
@@ -80,6 +73,14 @@ func PoAMigrationToPoS(network *localnetwork.LocalNetwork) {
 	poaValidatorManager, err := poavalidatormanager.NewPoAValidatorManager(proxyAddress, l1AInfo.RPCClient)
 	Expect(err).Should(BeNil())
 
+	signatureAggregator := utils.NewSignatureAggregator(
+		cChainInfo.NodeURIs[0],
+		[]ids.ID{
+			l1AInfo.L1ID,
+		},
+	)
+	defer signatureAggregator.Shutdown()
+
 	//
 	// Delist one initial validator
 	//
diff --git a/tests/network/network.go b/tests/network/network.go
index b08fdf9ff..1c5667d2a 100644
--- a/tests/network/network.go
+++ b/tests/network/network.go
@@ -28,7 +28,6 @@ import (
 	proxyadmin "github.com/ava-labs/icm-contracts/abi-bindings/go/ProxyAdmin"
 	"github.com/ava-labs/icm-contracts/tests/interfaces"
 	"github.com/ava-labs/icm-contracts/tests/utils"
-	"github.com/ava-labs/icm-services/signature-aggregator/aggregator"
 	"github.com/ava-labs/subnet-evm/ethclient"
 	subnetEvmTestUtils "github.com/ava-labs/subnet-evm/tests/utils"
 
@@ -320,7 +319,7 @@ func (n *LocalNetwork) GetValidatorManager(l1ID ids.ID) common.Address {
 	return n.validatorManagers[l1ID]
 }
 
-func (n *LocalNetwork) GetSignatureAggregator() *aggregator.SignatureAggregator {
+func (n *LocalNetwork) GetSignatureAggregator() *utils.SignatureAggregator {
 	var l1IDs []ids.ID
 	for _, l1 := range n.GetL1Infos() {
 		l1IDs = append(l1IDs, l1.L1ID)
diff --git a/tests/utils/chain.go b/tests/utils/chain.go
index 50d20b267..af6d50c86 100644
--- a/tests/utils/chain.go
+++ b/tests/utils/chain.go
@@ -16,21 +16,13 @@ import (
 
 	"github.com/ava-labs/avalanchego/api/info"
 	"github.com/ava-labs/avalanchego/ids"
-	"github.com/ava-labs/avalanchego/message"
 	"github.com/ava-labs/avalanchego/tests/fixture/tmpnet"
 	"github.com/ava-labs/avalanchego/utils/constants"
-	"github.com/ava-labs/avalanchego/utils/logging"
-	"github.com/ava-labs/avalanchego/utils/set"
 	"github.com/ava-labs/avalanchego/vms/platformvm/signer"
 	avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp"
 	nativeMinter "github.com/ava-labs/icm-contracts/abi-bindings/go/INativeMinter"
 	"github.com/ava-labs/icm-contracts/tests/interfaces"
 	gasUtils "github.com/ava-labs/icm-contracts/utils/gas-utils"
-	relayerConfig "github.com/ava-labs/icm-services/config"
-	"github.com/ava-labs/icm-services/peers"
-	"github.com/ava-labs/icm-services/signature-aggregator/aggregator"
-	sigAggConfig "github.com/ava-labs/icm-services/signature-aggregator/config"
-	"github.com/ava-labs/icm-services/signature-aggregator/metrics"
 	"github.com/ava-labs/subnet-evm/accounts/abi/bind"
 	"github.com/ava-labs/subnet-evm/core/types"
 	"github.com/ava-labs/subnet-evm/eth/tracers"
@@ -44,7 +36,6 @@ import (
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/log"
 	. "github.com/onsi/gomega"
-	"github.com/prometheus/client_golang/prometheus"
 )
 
 const (
@@ -545,52 +536,6 @@ func InstantiateGenesisTemplate(
 	return l1GenesisFile.Name()
 }
 
-// Aggregator utils
-func NewSignatureAggregator(apiUri string, l1IDs []ids.ID) *aggregator.SignatureAggregator {
-	cfg := sigAggConfig.Config{
-		PChainAPI: &relayerConfig.APIConfig{
-			BaseURL: apiUri,
-		},
-		InfoAPI: &relayerConfig.APIConfig{
-			BaseURL: apiUri,
-		},
-		AllowPrivateIPs: true,
-	}
-	trackedL1s := set.NewSet[ids.ID](len(l1IDs))
-	trackedL1s.Add(l1IDs...)
-	registry := prometheus.NewRegistry()
-	messageCreator, err := message.NewCreator(
-		logging.NoLog{},
-		registry,
-		constants.DefaultNetworkCompressionType,
-		constants.DefaultNetworkMaximumInboundTimeout,
-	)
-	Expect(err).Should(BeNil())
-
-	networkRegistry := prometheus.NewRegistry()
-	appRequestNetwork, err := peers.NewNetwork(
-		logging.Error,
-		networkRegistry,
-		trackedL1s,
-		nil,
-		&cfg,
-	)
-	Expect(err).Should(BeNil())
-
-	agg, err := aggregator.NewSignatureAggregator(
-		appRequestNetwork,
-		logging.NoLog{},
-		messageCreator,
-		1024,
-		metrics.NewSignatureAggregatorMetrics(prometheus.NewRegistry()),
-		// Setting the etnaTime to a minute ago so that the post-etna code path is used in the test
-		time.Now().Add(-1*time.Minute),
-	)
-	Expect(err).Should(BeNil())
-	return agg
-}
-
-//
 // Native minter utils
 //
 
@@ -662,7 +607,7 @@ func ConstructSignedWarpMessage(
 	source interfaces.L1TestInfo,
 	destination interfaces.L1TestInfo,
 	justification []byte,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 ) *avalancheWarp.Message {
 	unsignedMsg := ExtractWarpMessageFromLog(ctx, sourceReceipt, source)
 
@@ -681,7 +626,7 @@ func GetSignedMessage(
 	destination interfaces.L1TestInfo,
 	unsignedWarpMessage *avalancheWarp.UnsignedMessage,
 	justification []byte,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 ) *avalancheWarp.Message {
 	signingL1ID := source.L1ID
 	if source.L1ID == constants.PrimaryNetworkID && !destination.RequirePrimaryNetworkSigners {
diff --git a/tests/utils/governance.go b/tests/utils/governance.go
index ebf808dfb..6b4c30893 100644
--- a/tests/utils/governance.go
+++ b/tests/utils/governance.go
@@ -8,7 +8,6 @@ import (
 	"github.com/ava-labs/avalanchego/vms/platformvm/warp/payload"
 	validatorsetsig "github.com/ava-labs/icm-contracts/abi-bindings/go/governance/ValidatorSetSig"
 	"github.com/ava-labs/icm-contracts/tests/interfaces"
-	"github.com/ava-labs/icm-services/signature-aggregator/aggregator"
 	"github.com/ava-labs/subnet-evm/accounts/abi/bind"
 	"github.com/ava-labs/subnet-evm/core/types"
 	"github.com/ethereum/go-ethereum/common"
@@ -46,7 +45,7 @@ func ExecuteValidatorSetSigCallAndVerify(
 	validatorSetSigAddress common.Address,
 	senderKey *ecdsa.PrivateKey,
 	unsignedMessage *avalancheWarp.UnsignedMessage,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 	expectSuccess bool,
 ) *types.Receipt {
 	signedWarpMsg := GetSignedMessage(source, destination, unsignedMessage, nil, signatureAggregator)
diff --git a/tests/utils/ictt.go b/tests/utils/ictt.go
index 140ba3345..62f4362a7 100644
--- a/tests/utils/ictt.go
+++ b/tests/utils/ictt.go
@@ -21,7 +21,6 @@ import (
 	mockERC20SACR "github.com/ava-labs/icm-contracts/abi-bindings/go/ictt/mocks/MockERC20SendAndCallReceiver"
 	mockNSACR "github.com/ava-labs/icm-contracts/abi-bindings/go/ictt/mocks/MockNativeSendAndCallReceiver"
 	"github.com/ava-labs/icm-contracts/tests/interfaces"
-	"github.com/ava-labs/icm-services/signature-aggregator/aggregator"
 	"github.com/ava-labs/subnet-evm/accounts/abi/bind"
 	"github.com/ava-labs/subnet-evm/core/types"
 	"github.com/ethereum/go-ethereum/common"
@@ -308,7 +307,7 @@ func RegisterERC20TokenRemoteOnHome(
 	remoteL1 interfaces.L1TestInfo,
 	remoteAddress common.Address,
 	fundedKey *ecdsa.PrivateKey,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 ) {
 	RegisterTokenRemoteOnHome(
 		ctx,
@@ -336,7 +335,7 @@ func RegisterTokenRemoteOnHome(
 	expectedTokenMultiplier *big.Int,
 	expectedmultiplyOnRemote bool,
 	fundedKey *ecdsa.PrivateKey,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 ) *big.Int {
 	// Call the remote to send a register message to the home
 	tokenRemote, err := tokenremote.NewTokenRemote(
@@ -849,7 +848,7 @@ func SendNativeMultiHopAndVerify(
 	cChainInfo interfaces.L1TestInfo,
 	amount *big.Int,
 	secondaryFeeAmount *big.Int,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 ) {
 	input := nativetokenremote.SendTokensInput{
 		DestinationBlockchainID:            toL1.BlockchainID,
@@ -927,7 +926,7 @@ func SendERC20TokenMultiHopAndVerify(
 	cChainInfo interfaces.L1TestInfo,
 	amount *big.Int,
 	secondaryFeeAmount *big.Int,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 ) {
 	// Send tokens to the sender address to have gas for submitting the send tokens transaction
 	SendNativeTransfer(
diff --git a/tests/utils/signature_aggregator.go b/tests/utils/signature_aggregator.go
new file mode 100644
index 000000000..bda825c9b
--- /dev/null
+++ b/tests/utils/signature_aggregator.go
@@ -0,0 +1,170 @@
+package utils
+
+import (
+	"bytes"
+	"context"
+	"encoding/hex"
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"os"
+	"os/exec"
+	"time"
+
+	"github.com/ava-labs/avalanchego/ids"
+	avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp"
+
+	. "github.com/onsi/gomega"
+)
+
+const (
+	DEFAULT_SIG_AGG_PATH = "~/.teleporter-deps/icm-services/signature-aggregator"
+	DEFAULT_API_PORT     = 9090
+	SIG_AGG_API_PATH     = "/aggregate-signatures"
+)
+
+// This is a wrapper around a signature aggregator binar instead of importing the package directly
+// to avoid cyclic dependencies
+type SignatureAggregator struct {
+	cmd        *exec.Cmd
+	cancelFunc context.CancelFunc
+}
+
+type SignatureAggregatorConfig struct {
+	PChainAPI       ApiConfig `json:"p-chain-api"`
+	InfoAPI         ApiConfig `json:"info-api"`
+	L1IDs           []string  `json:"tracked-l1s"`
+	ApiPort         int       `json:"api-port"`
+	AllowPrivateIPs bool      `json:"allow-private-ips"`
+}
+
+type ApiConfig struct {
+	BaseURL     string            `json:"base-url"`
+	QueryParams map[string]string `json:"query-parameters"`
+	HTTPHeaders map[string]string `json:"http-headers"`
+}
+
+type AggregateSignaturesRequest struct {
+	Message          string `json:"message"`
+	Justification    string `json:"justification,omitempty"`
+	SigningSubnetID  string `json:"signing-subnet-id,omitempty"`
+	QuorumPercentage uint64 `json:"quorum-percentage,omitempty"`
+}
+
+type SignatureAggregatorResponse struct {
+	SignedMessage string `json:"signed-message"`
+}
+
+func (s *SignatureAggregator) Shutdown() error {
+	s.cancelFunc()
+	return s.cmd.Wait()
+}
+
+// Aggregator utils
+func NewSignatureAggregator(apiUri string, l1IDs []ids.ID) *SignatureAggregator {
+	sigAggPath := os.Getenv("SIG_AGG_PATH")
+	if sigAggPath == "" {
+		sigAggPath = DEFAULT_SIG_AGG_PATH
+	}
+	l1IdStrings := make([]string, 0, len(l1IDs))
+	for _, l1Id := range l1IDs {
+		l1IdStrings = append(l1IdStrings, l1Id.String())
+	}
+	cfg := SignatureAggregatorConfig{
+		PChainAPI: ApiConfig{
+			BaseURL: apiUri,
+		},
+		InfoAPI: ApiConfig{
+			BaseURL: apiUri,
+		},
+		L1IDs:           l1IdStrings,
+		ApiPort:         DEFAULT_API_PORT,
+		AllowPrivateIPs: true,
+	}
+	// write config to a JSON file in /tmp directory
+	configFile, err := os.CreateTemp("/tmp", "sig_agg_config_*.json")
+	Expect(err).Should(BeNil())
+	defer configFile.Close()
+
+	encoder := json.NewEncoder(configFile)
+	err = encoder.Encode(cfg)
+	Expect(err).Should(BeNil())
+
+	ctx, cancel := context.WithCancel(context.Background())
+
+	cmd := exec.CommandContext(ctx, sigAggPath, "--config-file", configFile.Name())
+	cmd.Stdout = os.Stdout
+	cmd.Stderr = os.Stderr
+	err = cmd.Start()
+	Expect(err).Should(BeNil())
+
+	// TODO: when the signature aggregator health check endpoint is improved to not return
+	// before ready to serve requests replace this sleep.
+	time.Sleep(time.Second * 5)
+	return &SignatureAggregator{
+		cancelFunc: cancel,
+		cmd:        cmd,
+	}
+}
+
+func (s *SignatureAggregator) CreateSignedMessage(
+	unsignedMessage *avalancheWarp.UnsignedMessage,
+	justification []byte,
+	inputSigningSubnet ids.ID,
+	quorumPercentage uint64,
+) (*avalancheWarp.Message, error) {
+	client := &http.Client{
+		Timeout: 20 * time.Second,
+	}
+	requestURL := fmt.Sprintf("http://localhost:%d%s", DEFAULT_API_PORT, SIG_AGG_API_PATH)
+	reqBody := AggregateSignaturesRequest{
+		Message:          hex.EncodeToString(unsignedMessage.Bytes()),
+		Justification:    hex.EncodeToString(justification),
+		SigningSubnetID:  inputSigningSubnet.String(),
+		QuorumPercentage: quorumPercentage,
+	}
+
+	b, err := json.Marshal(reqBody)
+	if err != nil {
+		return nil, err
+	}
+	bodyReader := bytes.NewReader(b)
+
+	req, err := http.NewRequest(http.MethodPost, requestURL, bodyReader)
+	if err != nil {
+		return nil, err
+	}
+	req.Header.Set("Content-Type", "application/json")
+	res, err := client.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	if res.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("expected status code 200, got %d", res.StatusCode)
+	}
+
+	defer res.Body.Close()
+	body, err := io.ReadAll(res.Body)
+	if err != nil {
+		return nil, err
+	}
+
+	var response SignatureAggregatorResponse
+	err = json.Unmarshal(body, &response)
+	if err != nil {
+		return nil, err
+	}
+
+	decodedMessage, err := hex.DecodeString(response.SignedMessage)
+	if err != nil {
+		return nil, err
+	}
+
+	signedMessage, err := avalancheWarp.ParseMessage(decodedMessage)
+	if err != nil {
+		return nil, err
+	}
+
+	return signedMessage, nil
+}
diff --git a/tests/utils/teleporter.go b/tests/utils/teleporter.go
index efb3dd2fa..b720ab338 100644
--- a/tests/utils/teleporter.go
+++ b/tests/utils/teleporter.go
@@ -17,7 +17,6 @@ import (
 	"github.com/ava-labs/icm-contracts/tests/interfaces"
 	deploymentUtils "github.com/ava-labs/icm-contracts/utils/deployment-utils"
 	gasUtils "github.com/ava-labs/icm-contracts/utils/gas-utils"
-	"github.com/ava-labs/icm-services/signature-aggregator/aggregator"
 	"github.com/ava-labs/subnet-evm/accounts/abi/bind"
 	"github.com/ava-labs/subnet-evm/core/types"
 	"github.com/ava-labs/subnet-evm/precompile/contracts/warp"
@@ -156,7 +155,7 @@ func (t TeleporterTestInfo) RelayTeleporterMessage(
 	expectSuccess bool,
 	fundedKey *ecdsa.PrivateKey,
 	justification []byte,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 ) *types.Receipt {
 	// Fetch the Teleporter message from the logs
 	sendEvent, err := GetEventFromLogs(sourceReceipt.Logs, t.TeleporterMessenger(source).ParseSendCrossChainMessage)
@@ -207,7 +206,7 @@ func (t TeleporterTestInfo) SendExampleCrossChainMessageAndVerify(
 	destExampleMessenger *testmessenger.TestMessenger,
 	senderKey *ecdsa.PrivateKey,
 	message string,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 	expectSuccess bool,
 ) {
 	// Call the example messenger contract on L1 A
@@ -286,7 +285,7 @@ func (t TeleporterTestInfo) AddProtocolVersionAndWaitForAcceptance(
 	newTeleporterAddress common.Address,
 	senderKey *ecdsa.PrivateKey,
 	unsignedMessage *avalancheWarp.UnsignedMessage,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 ) {
 	signedWarpMsg := GetSignedMessage(l1, l1, unsignedMessage, nil, signatureAggregator)
 	log.Info("Got signed warp message", "messageID", signedWarpMsg.ID())
@@ -328,7 +327,7 @@ func (t TeleporterTestInfo) ClearReceiptQueue(
 	fundedKey *ecdsa.PrivateKey,
 	source interfaces.L1TestInfo,
 	destination interfaces.L1TestInfo,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 ) {
 	sourceTeleporterMessenger := t.TeleporterMessenger(source)
 	outstandReceiptCount := GetOutstandingReceiptCount(
diff --git a/tests/utils/validator_manager.go b/tests/utils/validator_manager.go
index 1c3d4df28..fd36c038a 100644
--- a/tests/utils/validator_manager.go
+++ b/tests/utils/validator_manager.go
@@ -32,7 +32,6 @@ import (
 	iposvalidatormanager "github.com/ava-labs/icm-contracts/abi-bindings/go/validator-manager/interfaces/IPoSValidatorManager"
 	ivalidatormanager "github.com/ava-labs/icm-contracts/abi-bindings/go/validator-manager/interfaces/IValidatorManager"
 	"github.com/ava-labs/icm-contracts/tests/interfaces"
-	"github.com/ava-labs/icm-services/signature-aggregator/aggregator"
 	"github.com/ava-labs/subnet-evm/accounts/abi/bind"
 	"github.com/ava-labs/subnet-evm/core/types"
 	"github.com/ava-labs/subnet-evm/precompile/contracts/warp"
@@ -273,7 +272,7 @@ func InitializeValidatorSet(
 	pChainInfo interfaces.L1TestInfo,
 	validatorManagerAddress common.Address,
 	networkID uint32,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 	nodes []Node,
 ) []ids.ID {
 	log.Println("Initializing validator set", "l1", l1Info.L1ID)
@@ -526,7 +525,7 @@ func CallWarpReceiver(
 
 func InitializeAndCompleteNativeValidatorRegistration(
 	ctx context.Context,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 	fundedKey *ecdsa.PrivateKey,
 	l1Info interfaces.L1TestInfo,
 	pChainInfo interfaces.L1TestInfo,
@@ -600,7 +599,7 @@ func InitializeAndCompleteNativeValidatorRegistration(
 
 func InitializeAndCompleteERC20ValidatorRegistration(
 	ctx context.Context,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 	fundedKey *ecdsa.PrivateKey,
 	l1Info interfaces.L1TestInfo,
 	pChainInfo interfaces.L1TestInfo,
@@ -678,7 +677,7 @@ func InitializeAndCompleteERC20ValidatorRegistration(
 
 func InitializeAndCompletePoAValidatorRegistration(
 	ctx context.Context,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 	ownerKey *ecdsa.PrivateKey,
 	fundedKey *ecdsa.PrivateKey,
 	l1Info interfaces.L1TestInfo,
@@ -787,7 +786,7 @@ func ConstructUptimeProofMessage(
 	uptime uint64,
 	l1 interfaces.L1TestInfo,
 	networkID uint32,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 ) *avalancheWarp.Message {
 	uptimePayload, err := messages.NewValidatorUptime(validationID, uptime)
 	Expect(err).Should(BeNil())
@@ -813,7 +812,7 @@ func ConstructUptimeProofMessage(
 func ForceInitializeEndPoSValidationWithUptime(
 	ctx context.Context,
 	networkID uint32,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 	senderKey *ecdsa.PrivateKey,
 	l1 interfaces.L1TestInfo,
 	stakingManagerAddress common.Address,
@@ -845,7 +844,7 @@ func ForceInitializeEndPoSValidationWithUptime(
 func InitializeEndPoSValidationWithUptime(
 	ctx context.Context,
 	networkID uint32,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 	senderKey *ecdsa.PrivateKey,
 	l1 interfaces.L1TestInfo,
 	stakingManagerAddress common.Address,
@@ -1044,7 +1043,7 @@ func CompleteEndDelegation(
 
 func InitializeAndCompleteEndInitialPoSValidation(
 	ctx context.Context,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 	fundedKey *ecdsa.PrivateKey,
 	l1Info interfaces.L1TestInfo,
 	pChainInfo interfaces.L1TestInfo,
@@ -1121,7 +1120,7 @@ func InitializeAndCompleteEndInitialPoSValidation(
 
 func InitializeAndCompleteEndPoSValidation(
 	ctx context.Context,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 	fundedKey *ecdsa.PrivateKey,
 	l1Info interfaces.L1TestInfo,
 	pChainInfo interfaces.L1TestInfo,
@@ -1218,7 +1217,7 @@ func InitializeAndCompleteEndPoSValidation(
 
 func InitializeAndCompleteEndInitialPoAValidation(
 	ctx context.Context,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 	ownerKey *ecdsa.PrivateKey,
 	fundedKey *ecdsa.PrivateKey,
 	l1Info interfaces.L1TestInfo,
@@ -1296,7 +1295,7 @@ func InitializeAndCompleteEndInitialPoAValidation(
 
 func InitializeAndCompleteEndPoAValidation(
 	ctx context.Context,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 	ownerKey *ecdsa.PrivateKey,
 	fundedKey *ecdsa.PrivateKey,
 	l1Info interfaces.L1TestInfo,
@@ -1372,7 +1371,7 @@ func ConstructL1ValidatorRegistrationMessageForInitialValidator(
 	l1 interfaces.L1TestInfo,
 	pChainInfo interfaces.L1TestInfo,
 	networkID uint32,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 ) *avalancheWarp.Message {
 	justification := platformvm.L1ValidatorRegistrationJustification{
 		Preimage: &platformvm.L1ValidatorRegistrationJustification_ConvertSubnetToL1TxData{
@@ -1415,7 +1414,7 @@ func ConstructL1ValidatorRegistrationMessage(
 	l1 interfaces.L1TestInfo,
 	pChainInfo interfaces.L1TestInfo,
 	networkID uint32,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 ) *avalancheWarp.Message {
 	msg, err := warpMessage.NewRegisterL1Validator(
 		l1.L1ID,
@@ -1463,7 +1462,7 @@ func ConstructL1ValidatorWeightMessage(
 	weight uint64,
 	l1 interfaces.L1TestInfo,
 	pChainInfo interfaces.L1TestInfo,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 	networkID uint32,
 ) *avalancheWarp.Message {
 	payload, err := warpMessage.NewL1ValidatorWeight(validationID, nonce, weight)
@@ -1492,7 +1491,7 @@ func ConstructL1ConversionMessage(
 	l1 interfaces.L1TestInfo,
 	pChainInfo interfaces.L1TestInfo,
 	networkID uint32,
-	signatureAggregator *aggregator.SignatureAggregator,
+	signatureAggregator *SignatureAggregator,
 ) *avalancheWarp.Message {
 	l1ConversionPayload, err := warpMessage.NewSubnetToL1Conversion(l1ConversionID)
 	Expect(err).Should(BeNil())