Skip to content

Commit

Permalink
txm: Test now runs but signing is invalid
Browse files Browse the repository at this point in the history
  • Loading branch information
archseer committed Jan 12, 2024
1 parent 5a11cd9 commit 704d4d1
Show file tree
Hide file tree
Showing 10 changed files with 76 additions and 58 deletions.
2 changes: 1 addition & 1 deletion ops/charts/devnet/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ spec:
- name: cairo-build
mountPath: /cairo-build
image: "{{ .Values.repository | default "shardlabs/starknet-devnet-rs"}}:{{ .Values.tag | default "latest"}}"
args: ["--port", {{ .Values.service.internalPort | quote}}, "--seed", {{ .Values.seed | quote}}]
args: ["--port", {{ .Values.service.internalPort | quote}}, "--seed", {{ .Values.seed | quote}}, "--account-class", "cairo1"]
{{- end }}
imagePullPolicy: IfNotPresent
{{- if eq .Values.real_node true }}
Expand Down
16 changes: 8 additions & 8 deletions ops/scripts/devnet-hardhat-down.sh
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
#!/usr/bin/env bash
# TODO: this script needs to be replaced with a predefined K8s enviroment

echo "Cleaning up Starknet Devnet container..."
echo "Cleaning up Hardhat container..."

dpid=`docker ps | grep chainlink-starknet.starknet-devnet | awk '{print $1}'`;
echo "Checking for existing 'chainlink-starknet.starknet-devnet' docker container..."
dpid=`docker ps | grep chainlink-starknet.hardhat | awk '{print $1}'`;
echo "Checking for existing 'chainlink-starknet.hardhat' docker container..."
if [ -z "$dpid" ]
then
echo "No docker Starknet Devnet container running.";
echo "No docker Hardhat container running.";
else
docker kill $dpid;
docker rm $dpid;
fi

echo "Cleaning up Hardhat container..."
echo "Cleaning up Starknet Devnet container..."

dpid=`docker ps | grep chainlink-starknet.hardhat | awk '{print $1}'`;
echo "Checking for existing 'chainlink-starknet.hardhat' docker container..."
dpid=`docker ps | grep chainlink-starknet.starknet-devnet | awk '{print $1}'`;
echo "Checking for existing 'chainlink-starknet.starknet-devnet' docker container..."
if [ -z "$dpid" ]
then
echo "No docker Hardhat container running.";
echo "No docker Starknet Devnet container running.";
else
docker kill $dpid;
docker rm $dpid;
Expand Down
5 changes: 4 additions & 1 deletion ops/scripts/devnet-hardhat.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@ docker run \
-p 127.0.0.1:5050:5050 \
-p 127.0.0.1:8545:8545 \
-d \
-e RUST_LOG=debug \
--name chainlink-starknet.starknet-devnet \
"shardlabs/starknet-devnet-rs:${container_version}"
"shardlabs/starknet-devnet-rs:${container_version}" \
--seed 0 \
--account-class cairo1

echo "Starting hardhat..."
docker run --net container:chainlink-starknet.starknet-devnet -d --name chainlink-starknet.hardhat ethereumoptimism/hardhat-node:nightly
Expand Down
6 changes: 3 additions & 3 deletions relayer/pkg/chainlink/ocr2/contract_transmitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import (

"github.com/pkg/errors"

"github.com/NethermindEth/juno/core/felt"
starknetrpc "github.com/NethermindEth/starknet.go/rpc"
starknetutils "github.com/NethermindEth/starknet.go/utils"
"github.com/NethermindEth/juno/core/felt"

"github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/ocr2/medianreport"
"github.com/smartcontractkit/chainlink-starknet/relayer/pkg/chainlink/txm"
Expand All @@ -23,7 +23,7 @@ type contractTransmitter struct {
reader *transmissionsCache

contractAddress *felt.Felt
senderAddress *felt.Felt
senderAddress *felt.Felt // account.publicKey
accountAddress *felt.Felt

txm txm.TxManager
Expand Down Expand Up @@ -96,7 +96,7 @@ func (c *contractTransmitter) Transmit(
return err
}

err = c.txm.Enqueue(c.senderAddress, c.accountAddress, starknetrpc.FunctionCall{
err = c.txm.Enqueue(c.accountAddress, c.senderAddress, starknetrpc.FunctionCall{
ContractAddress: c.contractAddress,
EntryPointSelector: starknetutils.GetSelectorFromNameFelt("transmit"),
Calldata: calldata,
Expand Down
10 changes: 5 additions & 5 deletions relayer/pkg/chainlink/txm/nonce.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type NonceManagerClient interface {
type NonceManager interface {
services.Service

Register(ctx context.Context, address *felt.Felt, chainId string, client NonceManagerClient) error
Register(ctx context.Context, address *felt.Felt, publicKey *felt.Felt, chainId string, client NonceManagerClient) error

NextSequence(address *felt.Felt, chainID string) (*felt.Felt, error)
IncrementNextSequence(address *felt.Felt, chainID string, currentNonce *felt.Felt) error
Expand Down Expand Up @@ -65,20 +65,20 @@ func (nm *nonceManager) HealthReport() map[string]error {
}

// Register is used because we cannot pre-fetch nonces. the pubkey is known before hand, but the account address is not known until a job is started and sends a tx
func (nm *nonceManager) Register(ctx context.Context, addr *felt.Felt, chainId string, client NonceManagerClient) error {
func (nm *nonceManager) Register(ctx context.Context, addr *felt.Felt, publicKey *felt.Felt, chainId string, client NonceManagerClient) error {
nm.lock.Lock()
defer nm.lock.Unlock()
addressNonces, exists := nm.n[addr.String()]
addressNonces, exists := nm.n[publicKey.String()]
if !exists {
nm.n[addr.String()] = map[string]*felt.Felt{}
nm.n[publicKey.String()] = map[string]*felt.Felt{}
}
_, exists = addressNonces[chainId]
if !exists {
n, err := client.AccountNonce(ctx, addr)
if err != nil {
return err
}
nm.n[addr.String()][chainId] = n
nm.n[publicKey.String()][chainId] = n
}

return nil
Expand Down
2 changes: 1 addition & 1 deletion relayer/pkg/chainlink/txm/nonce_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func newTestNonceManager(t *testing.T, chainID string, initNonce *felt.Felt) (tx
c.On("AccountNonce", mock.Anything, mock.Anything).Return(initNonce, nil).Once()

require.NoError(t, nm.Start(tests.Context(t)))
require.NoError(t, nm.Register(tests.Context(t), keyHash, chainID, c))
require.NoError(t, nm.Register(tests.Context(t), keyHash, keyHash, chainID, c))

return nm, keyHash, func() { require.NoError(t, nm.Close()) }
}
Expand Down
1 change: 0 additions & 1 deletion relayer/pkg/chainlink/txm/test_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ func SetupLocalStarknetNode(t *testing.T) string {
cmd := exec.Command("starknet-devnet",
"--seed", "0", // use same seed for testing
"--port", port,
"--lite-mode",
)
var stdErr bytes.Buffer
cmd.Stderr = &stdErr
Expand Down
28 changes: 14 additions & 14 deletions relayer/pkg/chainlink/txm/txm.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ const (
)

type TxManager interface {
Enqueue(senderAddress *felt.Felt, accountAddress *felt.Felt, txFn starknetrpc.FunctionCall) error
Enqueue(accountAddress *felt.Felt, publicKey *felt.Felt, txFn starknetrpc.FunctionCall) error
InflightCount() (int, int)
}

type Tx struct {
senderAddress *felt.Felt
publicKey *felt.Felt
accountAddress *felt.Felt
call starknetrpc.FunctionCall
}
Expand Down Expand Up @@ -114,7 +114,7 @@ func (txm *starktxm) broadcastLoop() {
tx := <-txm.queue

// broadcast tx serially - wait until accepted by mempool before processing next
hash, err := txm.broadcast(ctx, tx.senderAddress, tx.accountAddress, tx.call)
hash, err := txm.broadcast(ctx, tx.publicKey, tx.accountAddress, tx.call)
if err != nil {
txm.lggr.Errorw("transaction failed to broadcast", "error", err, "tx", tx.call)
} else {
Expand All @@ -126,15 +126,15 @@ func (txm *starktxm) broadcastLoop() {

const FEE_MARGIN uint64 = 115

func (txm *starktxm) broadcast(ctx context.Context, senderAddress *felt.Felt, accountAddress *felt.Felt, call starknetrpc.FunctionCall) (txhash string, err error) {
func (txm *starktxm) broadcast(ctx context.Context, publicKey *felt.Felt, accountAddress *felt.Felt, call starknetrpc.FunctionCall) (txhash string, err error) {
client, err := txm.client.Get()
if err != nil {
txm.client.Reset()
return txhash, fmt.Errorf("broadcast: failed to fetch client: %+w", err)
}
// create new account
accountVersion := 0
account, err := starknetaccount.NewAccount(client.Provider, senderAddress, accountAddress.String(), txm.ks, accountVersion)
cairoVersion := 2
account, err := starknetaccount.NewAccount(client.Provider, accountAddress, publicKey.String(), txm.ks, cairoVersion)
if err != nil {
return txhash, fmt.Errorf("failed to create new account: %+w", err)
}
Expand All @@ -144,7 +144,7 @@ func (txm *starktxm) broadcast(ctx context.Context, senderAddress *felt.Felt, ac
return txhash, fmt.Errorf("failed to get chainID: %+w", err)
}

nonce, err := txm.nonce.NextSequence(accountAddress, chainID)
nonce, err := txm.nonce.NextSequence(publicKey, chainID)
if err != nil {
return txhash, fmt.Errorf("failed to get nonce: %+w", err)
}
Expand Down Expand Up @@ -174,7 +174,7 @@ func (txm *starktxm) broadcast(ctx context.Context, senderAddress *felt.Felt, ac
// Signing of the transaction that is done by the account
err = account.SignInvokeTransaction(context.Background(), &tx)
if err != nil {
return txhash, err
return txhash, fmt.Errorf("failed to sign tx: %+w", err)
}

// get fee for tx
Expand Down Expand Up @@ -292,13 +292,13 @@ func (txm *starktxm) HealthReport() map[string]error {
return map[string]error{txm.Name(): txm.Healthy()}
}

func (txm *starktxm) Enqueue(senderAddress, accountAddress *felt.Felt, tx starknetrpc.FunctionCall) error {
func (txm *starktxm) Enqueue(accountAddress, publicKey *felt.Felt, tx starknetrpc.FunctionCall) error {
// validate key exists for sender
// use the embedded Loopp Keystore to do this; the spec and design
// encourage passing nil data to the loop.Keystore.Sign as way to test
// existence of a key
if _, err := txm.ks.Loopp().Sign(context.Background(), senderAddress.String(), nil); err != nil {
return err
if _, err := txm.ks.Loopp().Sign(context.Background(), publicKey.String(), nil); err != nil {
return fmt.Errorf("enqueue: failed to sign: %+w", err)
}

client, err := txm.client.Get()
Expand All @@ -313,12 +313,12 @@ func (txm *starktxm) Enqueue(senderAddress, accountAddress *felt.Felt, tx starkn
}

// register account for nonce manager
if err := txm.nonce.Register(context.TODO(), accountAddress, chainID, client); err != nil {
return err
if err := txm.nonce.Register(context.TODO(), accountAddress, publicKey, chainID, client); err != nil {
return fmt.Errorf("failed to register nonce: %+w", err)
}

select {
case txm.queue <- Tx{senderAddress: senderAddress, accountAddress: accountAddress, call: tx}:
case txm.queue <- Tx{publicKey: publicKey, accountAddress: accountAddress, call: tx}: // TODO fix naming here
default:
return fmt.Errorf("failed to enqueue transaction: %+v", tx)
}
Expand Down
56 changes: 37 additions & 19 deletions relayer/pkg/chainlink/txm/txm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,32 +26,49 @@ import (

func TestIntegration_Txm(t *testing.T) {
n := 2 // number of txs per key
url := SetupLocalStarknetNode(t)
// url := SetupLocalStarknetNode(t)
url := "http://127.0.0.1:5050"
devnet := devnet.NewDevNet(url)
accounts, err := devnet.Accounts()
require.NoError(t, err)
fmt.Println("qqq")

// parse keys into expected format
localKeys := map[string]*big.Int{}
localAccounts := map[string]string{}
type Key struct {
PrivateKey *big.Int
Account string
}
localKeys := map[string]Key{}
for i := range accounts {
privKey, err := utils.HexToBytes(accounts[i].PrivateKey)
require.NoError(t, err)
senderAddress, err := starknetutils.HexToFelt(accounts[i].PublicKey)
require.NoError(t, err)
localKeys[senderAddress.String()] = utils.BytesToBig(privKey)
localAccounts[senderAddress.String()] = accounts[i].Address
publicKey := accounts[i].PublicKey
fmt.Printf("account %v pubkey %v\n", accounts[i].Address, publicKey)
localKeys[publicKey] = Key{
PrivateKey: starknetutils.HexToBN(accounts[i].PrivateKey),
Account: accounts[i].Address,
}
}

// TODO: try using official keystore & signing instead
// Initializing the account memkeyStore
// ks := account.NewMemKeystore()
// fakePrivKeyBI, ok := new(big.Int).SetString(privateKey, 0)
// if !ok {
// panic(err.Error())
// }
// ks.Put(public_key, fakePrivKeyBI)

// error computing loopp signature key does not exist

// mock keystore
looppKs := NewLooppKeystore(func(id string) (*big.Int, error) {
key, ok := localKeys[id]
looppKs := NewLooppKeystore(func(publicKey string) (*big.Int, error) {
key, ok := localKeys[publicKey]
if !ok {
return nil, fmt.Errorf("key does not exist id=%s", id)
return nil, fmt.Errorf("key does not exist id=%s", publicKey)
}
return key, nil
return key.PrivateKey, nil
})
ksAdapter := NewKeystoreAdapter(looppKs)

lggr, observer := logger.TestObserved(t, zapcore.DebugLevel)
timeout := 10 * time.Second
client, err := starknet.NewClient("SN_GOERLI", url+"/rpc", lggr, &timeout)
Expand All @@ -75,28 +92,29 @@ func TestIntegration_Txm(t *testing.T) {
// start txm + checks
require.NoError(t, txm.Start(context.Background()))
require.NoError(t, txm.Ready())
fmt.Println("sss")

for senderAddressStr := range localKeys {
senderAddress, err := starknetutils.HexToFelt(senderAddressStr)
for publicKeyStr := range localKeys {
publicKey, err := starknetutils.HexToFelt(publicKeyStr)
require.NoError(t, err)

account, err := starknetutils.HexToFelt(localAccounts[senderAddressStr])
accountAddress, err := starknetutils.HexToFelt(localKeys[publicKeyStr].Account)
require.NoError(t, err)

contractAddress, err := starknetutils.HexToFelt("0x49D36570D4E46F48E99674BD3FCC84644DDD6B96F7C741B1562B82F9E004DC7")
require.NoError(t, err)

selector := utils.GetSelectorFromNameFelt("totalSupply")
selector := starknetutils.GetSelectorFromNameFelt("totalSupply")

for i := 0; i < n; i++ {
require.NoError(t, txm.Enqueue(senderAddress, account, starknetrpc.FunctionCall{
require.NoError(t, txm.Enqueue(accountAddress, publicKey, starknetrpc.FunctionCall{
ContractAddress: contractAddress, // send to ETH token contract
EntryPointSelector: selector,
}))
}
}
var empty bool
for i := 0; i < 60; i++ {
for i := 0; i < 30; i++ {
time.Sleep(500 * time.Millisecond)
queued, unconfirmed := txm.InflightCount()
accepted := len(observer.FilterMessageSnippet("ACCEPTED_ON_L2").All())
Expand Down
8 changes: 3 additions & 5 deletions relayer/pkg/starknet/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@ package starknet

import (
"context"
"math/big"
"time"

"github.com/pkg/errors"

"github.com/NethermindEth/juno/core/felt"
starknetaccount "github.com/NethermindEth/starknet.go/account"
starknetrpc "github.com/NethermindEth/starknet.go/rpc"
starknetutils "github.com/NethermindEth/starknet.go/utils"
ethrpc "github.com/ethereum/go-ethereum/rpc"

"github.com/smartcontractkit/chainlink-common/pkg/logger"
Expand Down Expand Up @@ -199,9 +197,9 @@ func (c *Client) AccountNonce(ctx context.Context, accountAddress *felt.Felt) (*
defer cancel()
}

sender := starknetutils.BigIntToFelt(big.NewInt((0))) // not actually used in account.Nonce()
accountVersion := 0
account, err := starknetaccount.NewAccount(c.Provider, sender, accountAddress.String(), nil, accountVersion)
sender := &felt.Zero // not actually used in account.Nonce()
cairoVersion := 2
account, err := starknetaccount.NewAccount(c.Provider, accountAddress, sender.String(), nil, cairoVersion)
if err != nil {
return nil, errors.Wrap(err, "error in client.AccountNonce")
}
Expand Down

0 comments on commit 704d4d1

Please sign in to comment.