Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New account #302

Merged
merged 69 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
88ef8a2
New account interface
rianhughes Aug 29, 2023
12916f0
Nonce,Sign,Execute methods
rianhughes Aug 30, 2023
a86cf8f
add mocks for rpc provider and update test
rianhughes Aug 30, 2023
80fc14a
SignInvokeTransaction()
rianhughes Aug 30, 2023
ab9b231
Sign test wip
rianhughes Aug 31, 2023
86e5102
EstimateFee
rianhughes Aug 31, 2023
360bbdc
AddInvokeTx
rianhughes Aug 31, 2023
9596d77
Execute - add invoke
rianhughes Aug 31, 2023
81b8b2c
wip
rianhughes Aug 31, 2023
87b49be
tests
rianhughes Aug 31, 2023
65e3e86
transaction hash mateches mainnet
rianhughes Sep 1, 2023
555d1d4
Sign now has orking test
rianhughes Sep 1, 2023
442e461
merge sender address and account address
rianhughes Sep 1, 2023
2481d76
execute wip
rianhughes Sep 1, 2023
68b2c04
wip : devnet tests
rianhughes Sep 1, 2023
1db0988
Devnet test get predeployed accounts
rianhughes Sep 6, 2023
7d407d7
Compile call data wip
rianhughes Sep 6, 2023
6084e33
AddInvoke now works
rianhughes Sep 11, 2023
17352c8
mainnet wip test
rianhughes Sep 11, 2023
b7fab89
testnet addinvoke test wip
rianhughes Sep 11, 2023
750d202
invoke wip : calldata seems correct, sig err
rianhughes Sep 11, 2023
2beb368
wup
rianhughes Sep 11, 2023
118b79d
wip
rianhughes Sep 12, 2023
64d4eed
sig error arises from txhash but fmtCallData and Sign are fine
rianhughes Sep 12, 2023
f834994
sign, callData, txhash tests pass
rianhughes Sep 14, 2023
f0bc92c
AddInvoke Signature error seems to be gone - txhash alrady exists in …
rianhughes Sep 14, 2023
9947191
It worked! 0x55fab87937b8b87ba998cfa5d4495b71f51afa22eb304af62de1efd5…
rianhughes Sep 14, 2023
5e21465
tidy
rianhughes Sep 15, 2023
e1f02fa
tidy test
rianhughes Sep 15, 2023
d5eb44e
Add another test
rianhughes Sep 15, 2023
81fd7cb
ready to tidy test structure
rianhughes Sep 15, 2023
04b3b56
tidied and split TestChainID
rianhughes Sep 15, 2023
a62e0fc
Tidy TestFmtCallData
rianhughes Sep 15, 2023
b265897
tidy TestSignMOCK
rianhughes Sep 15, 2023
c650251
tidy test Txash
rianhughes Sep 18, 2023
c369a96
tidy TestAddInvoke - Tests all finished
rianhughes Sep 18, 2023
80292d4
remove AddInvoke example
rianhughes Sep 18, 2023
8da56b5
implement rpc.provider less write methods
rianhughes Sep 18, 2023
8a4bd3d
Add remaining rpc.provider methods
rianhughes Sep 18, 2023
68111aa
WIP AddDeployAccount Test
rianhughes Sep 18, 2023
90013b8
fixed stupid marshal error
rianhughes Sep 18, 2023
cf53ade
AddDeployAccount Test passes
rianhughes Sep 18, 2023
97e2c29
need merge main for declare test
rianhughes Sep 19, 2023
c92f1cf
merge main
rianhughes Sep 19, 2023
fc1d951
need to figure out compilation
rianhughes Sep 19, 2023
8cf58c6
wip : tests for deployAcnt testnet and hash
rianhughes Sep 19, 2023
d6a1ee7
test for deployAcnt hash and precompute working
rianhughes Sep 19, 2023
be353f5
test for deployAcnt hash and precompute working
rianhughes Sep 19, 2023
48ffd70
Create TransactionHashDeclare and test - no hash match
rianhughes Sep 20, 2023
3426f3b
Add last test and methods
rianhughes Sep 20, 2023
73c481b
merge main
rianhughes Sep 22, 2023
f1f12e6
update TransacitonHashInvoke for different txn versions
rianhughes Sep 22, 2023
de96344
TransactionHashInvoke support for v0 + found reference
rianhughes Sep 22, 2023
8582be9
TxnHash deploAcnt check version
rianhughes Sep 22, 2023
58330af
support multiple txn types for declare and deployAcnt hash
rianhughes Sep 22, 2023
99a5b77
Fix stuff
rianhughes Sep 22, 2023
bfad8bb
fix rpc ChainId
rianhughes Sep 22, 2023
b54088f
Merge branch 'main' into new-account
rianhughes Sep 25, 2023
338ef99
rm DeclareTest so account is working
rianhughes Sep 26, 2023
ca9adf2
add workflow
rianhughes Sep 26, 2023
c03906b
fix workflow name typo
rianhughes Sep 26, 2023
62a1b7e
fix account workflow endpoint
rianhughes Sep 26, 2023
1fbd12e
remove unnecessary account version
rianhughes Sep 26, 2023
4068f89
tidy
rianhughes Sep 26, 2023
87f0b2b
Add remaining rpc.Provider methods
rianhughes Sep 26, 2023
b66cab6
tidy
rianhughes Sep 26, 2023
5855255
address comments
rianhughes Sep 29, 2023
fe0de12
Merge branch 'main' into new-account
rianhughes Sep 29, 2023
588bc46
fix merge main conflict
rianhughes Sep 29, 2023
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
269 changes: 269 additions & 0 deletions account/account.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
package account

import (
"context"
"errors"

"github.com/NethermindEth/juno/core/felt"
starknetgo "github.com/NethermindEth/starknet.go"
"github.com/NethermindEth/starknet.go/rpc"
"github.com/NethermindEth/starknet.go/types"
"github.com/NethermindEth/starknet.go/utils"
)

var (
ErrAccountVersionNotSupported = errors.New("Account version not supported")
ErrNotAllParametersSet = errors.New("Not all neccessary parameters have been set")
ErrTxnTypeUnSupported = errors.New("Unsupported transction type")
ErrFeltToBigInt = errors.New("Felt to BigInt error")
)

const (
TRANSACTION_PREFIX = "invoke"
DECLARE_PREFIX = "declare"
EXECUTE_SELECTOR = "__execute__"
CONTRACT_ADDRESS_PREFIX = "STARKNET_CONTRACT_ADDRESS"
)

//go:generate mockgen -destination=../mocks/mock_account.go -package=mocks -source=account.go AccountInterface
type AccountInterface interface {
TransactionHash(calls rpc.FunctionCall, txDetails rpc.TxDetails) (*felt.Felt, error)
Call(ctx context.Context, call rpc.FunctionCall, blockId rpc.BlockID) ([]*felt.Felt, error)
Nonce(ctx context.Context) (*felt.Felt, error)
Sign(ctx context.Context, msg *felt.Felt) ([]*felt.Felt, error)
SignInvokeTransaction(ctx context.Context, invokeTx *rpc.BroadcastedInvokeV1Transaction) error
EstimateFee(ctx context.Context, broadcastTxs []rpc.BroadcastedTransaction, blockId rpc.BlockID) ([]rpc.FeeEstimate, error)
AddInvokeTransaction(ctx context.Context, invokeTx *rpc.BroadcastedInvokeV1Transaction) (*rpc.AddInvokeTransactionResponse, error)
Execute(ctx context.Context, invokeTx *rpc.BroadcastedInvokeV1Transaction) (*rpc.AddInvokeTransactionResponse, error) // Todo: generalise once rpcv04 PRs are merged
}

var _ AccountInterface = &Account{}

type Account struct {
provider rpc.RpcProvider
ChainId *felt.Felt
accountAddress *felt.Felt
ks starknetgo.Keystore
version uint64
}

func NewAccount(provider rpc.RpcProvider, version uint64, accountAddress *felt.Felt, keystore starknetgo.Keystore, setChainId bool) (*Account, error) {
account := &Account{
provider: provider,
accountAddress: accountAddress,
ks: keystore,
version: version,
}

if setChainId == false {
return account, nil
}
chainID, err := provider.ChainID(context.Background())
if err != nil {
return nil, err
}
rianhughes marked this conversation as resolved.
Show resolved Hide resolved
account.ChainId = new(felt.Felt).SetBytes([]byte(chainID))

return account, nil
}

func (account *Account) Call(ctx context.Context, call rpc.FunctionCall, blockId rpc.BlockID) ([]*felt.Felt, error) {
return account.provider.Call(ctx,
rpc.FunctionCall{
ContractAddress: call.ContractAddress,
EntryPointSelector: call.EntryPointSelector,
Calldata: call.Calldata},
blockId)
}

func (account *Account) TransactionHash(call rpc.FunctionCall, txDetails rpc.TxDetails) (*felt.Felt, error) {

if call.Calldata == nil || txDetails.Nonce == nil || txDetails.MaxFee == nil || account.accountAddress == nil {
return nil, ErrNotAllParametersSet
}

calldataHash, err := computeHashOnElementsFelt(call.Calldata)
if err != nil {
return nil, err
}

return calculateTransactionHashCommon(
new(felt.Felt).SetBytes([]byte(TRANSACTION_PREFIX)),
new(felt.Felt).SetUint64(account.version),
account.accountAddress,
&felt.Zero,
calldataHash,
txDetails.MaxFee,
account.ChainId,
[]*felt.Felt{txDetails.Nonce},
)
}

func (account *Account) TransactionHash2(callData []*felt.Felt, txDetails rpc.TxDetails) (*felt.Felt, error) {

if len(callData) == 0 || txDetails.Nonce == nil || txDetails.MaxFee == nil || account.accountAddress == nil {
return nil, ErrNotAllParametersSet
}

calldataHash, err := computeHashOnElementsFelt(callData)
if err != nil {
return nil, err
}

return calculateTransactionHashCommon(
new(felt.Felt).SetBytes([]byte(TRANSACTION_PREFIX)),
new(felt.Felt).SetUint64(account.version),
account.accountAddress,
&felt.Zero,
calldataHash,
txDetails.MaxFee,
account.ChainId,
[]*felt.Felt{txDetails.Nonce},
)
}

func (account *Account) Nonce(ctx context.Context) (*felt.Felt, error) {
switch account.version {
case 1:
// Todo: simplfy after rpc PRs are merged, return account.provider.Nonce(...)
nonce, err := account.provider.Nonce(ctx, rpc.WithBlockTag("latest"), account.accountAddress)
if err != nil {
return nil, err
}
return new(felt.Felt).SetString(*nonce)
default:
return nil, ErrAccountVersionNotSupported
}
}

func (account *Account) Sign(ctx context.Context, msg *felt.Felt) ([]*felt.Felt, error) {

msgBig, ok := utils.FeltToBigInt(msg)
if ok != true {
return nil, ErrFeltToBigInt
}
s1, s2, err := account.ks.Sign(ctx, account.accountAddress.String(), msgBig)
if err != nil {
return nil, err
}
s1Felt, err := utils.BigIntToFelt(s1)
if err != nil {
return nil, err
}
s2Felt, err := utils.BigIntToFelt(s2)
if err != nil {
return nil, err
}
return []*felt.Felt{s1Felt, s2Felt}, nil
}

func (account *Account) SignInvokeTransaction(ctx context.Context, invokeTx *rpc.BroadcastedInvokeV1Transaction) error {
txHash, err := account.TransactionHash2(invokeTx.Calldata, rpc.TxDetails{Nonce: invokeTx.Nonce, MaxFee: invokeTx.MaxFee})
if err != nil {
return err
}
signature, err := account.Sign(ctx, txHash)
if err != nil {
return err
}
invokeTx.Signature = signature
return nil
}

// Execute Sets maxFee to twice the estimated fee (if not already set), sets the nonce, calculates the transaction hash, signs the transaction
// and finally submits an addInvokeTransaction to the rpc provider.
func (account *Account) Execute(ctx context.Context, invokeTx *rpc.BroadcastedInvokeV1Transaction) (*rpc.AddInvokeTransactionResponse, error) {
if account.version != 1 {
return nil, ErrAccountVersionNotSupported
}

// Set max fee if not already set
if invokeTx.MaxFee == nil {
estimate, err := account.EstimateFee(ctx, []rpc.BroadcastedTransaction{invokeTx}, rpc.WithBlockTag("latest"))
if err != nil {
return nil, err
}
overallFee, err := new(felt.Felt).SetString(string(estimate[0].OverallFee))
if err != nil {
return nil, err
}
newMaxFee := new(felt.Felt).Mul(overallFee, new(felt.Felt).SetUint64(2))
invokeTx.MaxFee = newMaxFee
}

// Get and set nonce
nonce, err := account.Nonce(ctx)
if err != nil {
return nil, err
}
invokeTx.Nonce = nonce

// Sign transaction
err = account.SignInvokeTransaction(ctx, invokeTx)
if err != nil {
return nil, err
}

// Submit transaction
switch account.version {
case 1:
return account.AddInvokeTransaction(ctx, invokeTx)
default:
return nil, ErrAccountVersionNotSupported
}
}

func (account *Account) EstimateFee(ctx context.Context, broadcastTxs []rpc.BroadcastedTransaction, blockId rpc.BlockID) ([]rpc.FeeEstimate, error) {
switch account.version {
case 1:
return account.provider.EstimateFee(ctx, broadcastTxs, blockId)
default:
return nil, ErrAccountVersionNotSupported
}
}

// AddInvokeTransaction submits an invoke transaction to the rpc provider.
func (account *Account) AddInvokeTransaction(ctx context.Context, invokeTx *rpc.BroadcastedInvokeV1Transaction) (*rpc.AddInvokeTransactionResponse, error) {
switch account.version {
case 1:
return account.provider.AddInvokeTransaction(ctx, invokeTx)
default:
return nil, ErrAccountVersionNotSupported
}
}

func fmtCalldataStrings(fnCalls []rpc.FunctionCall) []*felt.Felt {
callArray := fmtCalldata(fnCalls)
calldataStrings := []*felt.Felt{}
for _, data := range callArray {
calldataStrings = append(calldataStrings, data)
}
return calldataStrings
}

/*
Formats the multicall transactions in a format which can be signed and verified by the network and OpenZeppelin account contracts
*/
// [number_calls, contract_address_1, entry_point_1, _some len 1_ , contract_address_2, ep_2, ...some len2 ..., .., calldata]
func fmtCalldata(fnCalls []rpc.FunctionCall) []*felt.Felt {
callArray := []*felt.Felt{}
callData := []*felt.Felt{new(felt.Felt).SetUint64(uint64(len(fnCalls)))}

for _, tx := range fnCalls {
callData = append(callData, tx.ContractAddress, types.GetSelectorFromNameFelt(tx.EntryPointSelector.String()))

if len(tx.Calldata) == 0 {
callData = append(callData, &felt.Zero, &felt.Zero)
continue
}

callData = append(callData, new(felt.Felt).SetUint64(uint64(len(callArray))), new(felt.Felt).SetUint64(uint64(len(tx.Calldata))))
for _, cd := range tx.Calldata {
callArray = append(callArray, cd)
}

}
callData = append(callData, new(felt.Felt).SetUint64(uint64(len(callData))))
callData = append(callData, callArray...)
return callData
}
Loading
Loading