Skip to content

Commit

Permalink
Update examples to use account interface (#414)
Browse files Browse the repository at this point in the history
* Update examples to use account interface

* Add .env.template to deployAccount example

---------

Co-authored-by: Carmen Cabrera <[email protected]>
  • Loading branch information
rianhughes and cicr99 authored Oct 10, 2023
1 parent f1a671a commit a3676e6
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 124 deletions.
30 changes: 30 additions & 0 deletions account/keystore.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ import (
"errors"
"fmt"
"math/big"
"os"
"sync"

"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/starknet.go/curve"
"github.com/NethermindEth/starknet.go/utils"
)

type Keystore interface {
Expand Down Expand Up @@ -74,3 +77,30 @@ func sign(ctx context.Context, msgHash *big.Int, key *big.Int) (x *big.Int, y *b
}
return x, y, err
}

// GetRandomKeys gets a random set of pub-priv keys. Note: This should be used for testing purposes only, do NOT send real funds to these addresses.
func GetRandomKeys() (*MemKeystore, *felt.Felt, *felt.Felt) {
// Get random keys
privateKey, err := curve.Curve.GetRandomPrivateKey()
if err != nil {
fmt.Println("can't get random private key:", err)
os.Exit(1)
}
pubX, _, err := curve.Curve.PrivateToPoint(privateKey)
if err != nil {
fmt.Println("can't generate public key:", err)
os.Exit(1)
}
privFelt := utils.BigIntToFelt(privateKey)
pubFelt := utils.BigIntToFelt(pubX)

// set up keystore
ks := NewMemKeystore()
fakePrivKeyBI, ok := new(big.Int).SetString(privFelt.String(), 0)
if !ok {
panic("Error setting up account key store")
}
ks.Put(pubFelt.String(), fakePrivKeyBI)

return ks, pubFelt, privFelt
}
2 changes: 2 additions & 0 deletions examples/deployAccount/.env.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# use this variable to change the RPC base URL
#INTEGRATION_BASE=http_insert_end_point
144 changes: 20 additions & 124 deletions examples/deployAccount/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"os"

"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/starknet.go/curve"
"github.com/NethermindEth/starknet.go/account"
"github.com/NethermindEth/starknet.go/rpc"
"github.com/NethermindEth/starknet.go/utils"
ethrpc "github.com/ethereum/go-ethereum/rpc"
Expand All @@ -16,6 +16,7 @@ import (
var (
network string = "testnet"
predeployedClassHash = "0x2794ce20e5f2ff0d40e632cb53845b9f4e526ebd8471983f7dbd355b721d5a"
accountAddress = "0xdeadbeef"
)

func main() {
Expand All @@ -28,14 +29,26 @@ func main() {
}
clientv02 := rpc.NewProvider(c)

// Get keys
pub, priv := getRandomKeys()
// Get random keys for test purposes
ks, pub, _ := account.GetRandomKeys()

accountAddressFelt, err := new(felt.Felt).SetString(accountAddress)
if err != nil {
panic("Error casting accountAddress to felt")
}

// Set up account
acnt, err := account.NewAccount(clientv02, accountAddressFelt, pub.String(), ks)
if err != nil {
panic(err)
}

classHash, err := utils.HexToFelt(predeployedClassHash)
if err != nil {
panic(err)
}

// Create transaction data
tx := rpc.DeployAccountTxn{
Nonce: &felt.Zero, // Contract accounts start with nonce zero.
MaxFee: new(felt.Felt).SetUint64(4724395326064),
Expand All @@ -47,7 +60,7 @@ func main() {
ConstructorCalldata: []*felt.Felt{pub},
}

precomputedAddress, err := precomputeAddress(&felt.Zero, pub, classHash, tx.ConstructorCalldata)
precomputedAddress, err := acnt.PrecomputeAddress(&felt.Zero, pub, classHash, tx.ConstructorCalldata)
fmt.Println("precomputedAddress:", precomputedAddress)

// At this point you need to add funds to precomputed address to use it.
Expand All @@ -58,133 +71,16 @@ func main() {
fmt.Println("When your account has been funded by the faucet, press any key, then `enter` to continue : ")
fmt.Scan(&input)

// Get the chainID to sign the transaction
chainId, err := clientv02.ChainID(context.Background())
if err != nil {
panic(err)
}

// Calculate and sign the transaction hash
hash, err := calculateDeployAccountTransactionHash(tx, precomputedAddress, chainId)
if err != nil {
panic(err)
}
fmt.Println("Transaction hash:", hash)
x, y, err := curve.Curve.SignFelt(hash, priv)
// Sign the transaction
err = acnt.SignDeployAccountTransaction(context.Background(), &tx, precomputedAddress)
if err != nil {
panic(err)
}
tx.Signature = []*felt.Felt{x, y}

// Send transaction to the network
resp, err := clientv02.AddDeployAccountTransaction(context.Background(), tx)
resp, err := acnt.AddDeployAccountTransaction(context.Background(), tx)
if err != nil {
panic(fmt.Sprint("Error returned from AddDeployAccountTransaction: ", err))
}

fmt.Println("AddDeployAccountTransaction response:", resp)

}

func getRandomKeys() (*felt.Felt, *felt.Felt) {
privateKey, err := curve.Curve.GetRandomPrivateKey()
if err != nil {
fmt.Println("can't get random private key:", err)
os.Exit(1)
}
pubX, _, err := curve.Curve.PrivateToPoint(privateKey)
if err != nil {
fmt.Println("can't generate public key:", err)
os.Exit(1)
}
privFelt := utils.BigIntToFelt(privateKey)
pubFelt := utils.BigIntToFelt(pubX)

return pubFelt, privFelt
}

// precomputeAddress computes the address by hashing the relevant data.
// ref: https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/starknet/core/os/contract_address/contract_address.py
// TODO: Move to contract / utils package
func precomputeAddress(deployerAddress *felt.Felt, salt *felt.Felt, classHash *felt.Felt, constructorCalldata []*felt.Felt) (*felt.Felt, error) {
CONTRACT_ADDRESS_PREFIX := new(felt.Felt).SetBytes([]byte("STARKNET_CONTRACT_ADDRESS"))

bigIntArr := utils.FeltArrToBigIntArr([]*felt.Felt{
CONTRACT_ADDRESS_PREFIX,
deployerAddress,
salt,
classHash,
})
constructorCalldataBigIntArr := utils.FeltArrToBigIntArr(constructorCalldata)
constructorCallDataHashInt, _ := curve.Curve.ComputeHashOnElements(constructorCalldataBigIntArr)
bigIntArr = append(bigIntArr, constructorCallDataHashInt)

preBigInt, err := curve.Curve.ComputeHashOnElements(bigIntArr)
if err != nil {
return nil, err
}
return utils.BigIntToFelt(preBigInt), nil

}

func computeHashOnElementsFelt(feltArr []*felt.Felt) (*felt.Felt, error) {
bigIntArr := utils.FeltArrToBigIntArr(feltArr)
hash, err := curve.Curve.ComputeHashOnElements(bigIntArr)
if err != nil {
return nil, err
}
return utils.BigIntToFelt(hash), nil
}

// calculateDeployAccountTransactionHash computes the transaction hash for deployAccount transactions
func calculateDeployAccountTransactionHash(tx rpc.DeployAccountTxn, contractAddress *felt.Felt, chainID string) (*felt.Felt, error) {
Prefix_DEPLOY_ACCOUNT := new(felt.Felt).SetBytes([]byte("deploy_account"))
chainIdFelt := new(felt.Felt).SetBytes([]byte(chainID))

calldata := []*felt.Felt{tx.ClassHash, tx.ContractAddressSalt}
calldata = append(calldata, tx.ConstructorCalldata...)
calldataHash, err := computeHashOnElementsFelt(calldata)
if err != nil {
return nil, err
}

versionFelt, err := utils.HexToFelt(string(tx.Version))
if err != nil {
return nil, err
}

return calculateTransactionHashCommon(
Prefix_DEPLOY_ACCOUNT,
versionFelt,
contractAddress,
&felt.Zero,
calldataHash,
tx.MaxFee,
chainIdFelt,
[]*felt.Felt{tx.Nonce},
)
}

func calculateTransactionHashCommon(
txHashPrefix *felt.Felt,
version *felt.Felt,
contractAddress *felt.Felt,
entryPointSelector *felt.Felt,
calldata *felt.Felt,
maxFee *felt.Felt,
chainId *felt.Felt,
additionalData []*felt.Felt) (*felt.Felt, error) {

dataToHash := []*felt.Felt{
txHashPrefix,
version,
contractAddress,
entryPointSelector,
calldata,
maxFee,
chainId,
}
dataToHash = append(dataToHash, additionalData...)

return computeHashOnElementsFelt(dataToHash)
}

0 comments on commit a3676e6

Please sign in to comment.