erigon-pulse/cmd/starknet/services/raw_tx_generator.go
2022-01-27 11:09:41 +07:00

137 lines
3.1 KiB
Go

package services
import (
"bytes"
"context"
"crypto/ecdsa"
"encoding/hex"
"errors"
"fmt"
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/hexutil"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/rlp"
"github.com/ledgerwatch/erigon/turbo/adapter"
"io/fs"
)
var (
ErrReadContract = errors.New("contract read error")
ErrInvalidPrivateKey = errors.New("invalid private key")
)
type Config struct {
ContractFileName string
Salt []byte
Gas uint64
Nonce uint64
}
func NewRawTxGenerator(privateKey string) *RawTxGenerator {
return &RawTxGenerator{
privateKey: privateKey,
}
}
type RawTxGenerator struct {
privateKey string
}
func (g RawTxGenerator) CreateFromFS(ctx context.Context, fileSystem fs.FS, db kv.RoDB, config *Config, writer *bytes.Buffer) error {
privateKey, err := crypto.HexToECDSA(g.privateKey)
if err != nil {
return ErrInvalidPrivateKey
}
address, err := addressFromPrivateKey(privateKey)
if err != nil {
return err
}
nonce, err := getNonce(ctx, db, address, config.Nonce)
if err != nil {
return err
}
contract, err := fs.ReadFile(fileSystem, config.ContractFileName)
if err != nil {
return ErrReadContract
}
enc := make([]byte, hex.EncodedLen(len(contract)))
hex.Encode(enc, contract)
tx := types.StarknetTransaction{
CommonTx: types.CommonTx{
Nonce: nonce + 1,
Value: uint256.NewInt(1),
Gas: config.Gas,
Data: enc,
},
Salt: config.Salt,
FeeCap: uint256.NewInt(875000000),
Tip: uint256.NewInt(100000),
}
sighash := tx.SigningHash(params.FermionChainConfig.ChainID)
signature, _ := crypto.Sign(sighash[:], privateKey)
signer := types.MakeSigner(params.FermionChainConfig, 1)
signedTx, err := tx.WithSignature(*signer, signature)
if err != nil {
return err
}
err = signedTx.(rlp.Encoder).EncodeRLP(writer)
signedTxRlp := writer.Bytes()
writer.Reset()
writer.WriteString(hexutil.Encode(signedTxRlp))
if err != nil {
return errors.New("can not save signed tx")
}
return nil
}
func addressFromPrivateKey(privateKey *ecdsa.PrivateKey) (common.Address, error) {
publicKey := privateKey.Public()
publicKeyECDSA, _ := publicKey.(*ecdsa.PublicKey)
return crypto.PubkeyToAddress(*publicKeyECDSA), nil
}
func getNonce(ctx context.Context, db kv.RoDB, address common.Address, configNonce uint64) (uint64, error) {
if configNonce != 0 {
return configNonce, nil
}
var nonce uint64 = 0
tx, err := db.BeginRo(ctx)
if err != nil {
return nonce, fmt.Errorf("cannot open tx: %w", err)
}
defer tx.Rollback()
blockNumber, err := stages.GetStageProgress(tx, stages.Execution)
if err != nil {
return nonce, err
}
reader := adapter.NewStateReader(tx, blockNumber)
acc, err := reader.ReadAccountData(address)
if err != nil {
return nonce, err
}
if acc == nil {
return 0, nil
}
return acc.Nonce, nil
}