From d7b5834d914dfcee0b2a76e89cbfee83c2ce4f98 Mon Sep 17 00:00:00 2001 From: idmsy Date: Sun, 26 May 2024 15:48:49 +0800 Subject: [PATCH 1/2] finish some starknet rpc --- cairo/api.go | 21 +++- cairo/cairo.go | 6 +- cairo/starknetrpc/methods.go | 232 ++++++++++++++++++++++++++++++++++- cairo/starknetrpc/rpc.go | 27 +++- 4 files changed, 276 insertions(+), 10 deletions(-) diff --git a/cairo/api.go b/cairo/api.go index 10d7e98..3bc1e34 100644 --- a/cairo/api.go +++ b/cairo/api.go @@ -2,6 +2,9 @@ package cairo import ( "errors" + "net/http" + "slices" + "github.com/NethermindEth/juno/core" "github.com/NethermindEth/juno/core/felt" "github.com/NethermindEth/juno/encoder" @@ -12,8 +15,6 @@ import ( "github.com/yu-org/yu/common" "github.com/yu-org/yu/core/context" "github.com/yu-org/yu/core/types" - "net/http" - "slices" ) type BlockID struct { @@ -32,6 +33,22 @@ func NewFromJunoBlockID(id rpc.BlockID) BlockID { } } +type LatestBlockResponse struct { + Block *types.CompactBlock `json:"latest_block"` + Err *jsonrpc.Error `json:"err"` +} + +// 不是很确定这里是不是其实不需要request的内容,直接response +func (c *Cairo) LatestBlock(ctx *context.ReadContext) { + Block, err := c.Chain.GetEndBlock() + if err != nil { + ctx.Json(http.StatusInternalServerError, &LatestBlockResponse{Err: jsonrpc.Err(jsonrpc.InternalError, err.Error())}) + return + } + // Height := uint64(Block.Height) + ctx.JsonOk(&LatestBlockResponse{Block: Block}) +} + type TransactionRequest struct { Hash felt.Felt `json:"hash"` } diff --git a/cairo/cairo.go b/cairo/cairo.go index 8a7b99d..5c3fcc8 100644 --- a/cairo/cairo.go +++ b/cairo/cairo.go @@ -2,6 +2,9 @@ package cairo import ( "encoding/hex" + "itachi/cairo/config" + "net/http" + junostate "github.com/NethermindEth/juno/blockchain" "github.com/NethermindEth/juno/core" "github.com/NethermindEth/juno/core/felt" @@ -16,8 +19,6 @@ import ( "github.com/yu-org/yu/core/context" "github.com/yu-org/yu/core/tripod" "github.com/yu-org/yu/core/types" - "itachi/cairo/config" - "net/http" ) type Cairo struct { @@ -59,6 +60,7 @@ func NewCairo(cfg *config.Config) *Cairo { cairo.GetTransaction, cairo.GetTransactionStatus, cairo.GetReceipt, cairo.SimulateTransactions, cairo.GetBlockWithTxs, cairo.GetBlockWithTxHashes, + cairo.LatestBlock, ) cairo.SetInit(cairo) cairo.SetTxnChecker(cairo) diff --git a/cairo/starknetrpc/methods.go b/cairo/starknetrpc/methods.go index ed22900..f27be36 100644 --- a/cairo/starknetrpc/methods.go +++ b/cairo/starknetrpc/methods.go @@ -4,23 +4,165 @@ import ( "context" "encoding/json" "errors" + "github.com/NethermindEth/juno/core" "github.com/NethermindEth/juno/core/felt" "github.com/NethermindEth/juno/jsonrpc" "github.com/NethermindEth/juno/rpc" "github.com/NethermindEth/juno/utils" + + // "github.com/NethermindEth/juno/db" + + // "github.com/NethermindEth/juno/blockchain" + "itachi/cairo" + "github.com/NethermindEth/starknet.go/hash" sdk "github.com/NethermindEth/starknet.go/rpc" "github.com/yu-org/yu/common" yucore "github.com/yu-org/yu/core" yucontext "github.com/yu-org/yu/core/context" - "itachi/cairo" ) func (s *StarknetRPC) GetChainID() (*felt.Felt, *jsonrpc.Error) { return s.network.ChainID(), nil } +func (s *StarknetRPC) BlockNumber() (uint64, *jsonrpc.Error) { + // num, _ := s.bcReader.Height() + // return num, nil + resp, err := s.adaptChainRead(nil, "LatestBlock") + if err != nil { + return 0, rpc.ErrNoBlock + } + res := resp.DataInterface.(*cairo.LatestBlockResponse) + return uint64(res.Block.Height), res.Err +} + +func (s *StarknetRPC) BlockHashAndNumber() (*rpc.BlockHashAndNumber, *jsonrpc.Error) { + // block, err := s.bcReader.Head() + // if err != nil { + // return nil, rpc.ErrBlockNotFound + // } + // return &rpc.BlockHashAndNumber{Number: block.Number, Hash: block.Hash}, nil + resp, err := s.adaptChainRead(nil, "LatestBlock") + if err != nil { + return nil, rpc.ErrNoBlock + } + res := resp.DataInterface.(*cairo.LatestBlockResponse) + return &rpc.BlockHashAndNumber{Number: uint64(res.Block.Height), Hash: new(felt.Felt).SetBytes(res.Block.Hash.Bytes())}, nil +} + +// func (s *StarknetRPC) GetStateUpdate(id rpc.BlockID) (*rpc.StateUpdate, *jsonrpc.Error) { +// var update *core.StateUpdate +// var err error +// if id.Latest { +// if height, heightErr := s.bcReader.Height(); heightErr != nil { +// err = heightErr +// } else { +// update, err = s.bcReader.StateUpdateByNumber(height) +// } +// } else if id.Pending { +// var pending blockchain.Pending +// pending, err = s.bcReader.Pending() +// if err == nil { +// update = pending.StateUpdate +// } +// } else if id.Hash != nil { +// update, err = s.bcReader.StateUpdateByHash(id.Hash) +// } else { +// update, err = s.bcReader.StateUpdateByNumber(id.Number) +// } +// if err != nil { +// if errors.Is(err, db.ErrKeyNotFound) { +// return nil, rpc.ErrBlockNotFound +// } +// return nil, rpc.ErrInternal.CloneWithData(err) +// } + +// nonces := make([]rpc.Nonce, 0, len(update.StateDiff.Nonces)) +// for addr, nonce := range update.StateDiff.Nonces { +// nonces = append(nonces, rpc.Nonce{ContractAddress: addr, Nonce: *nonce}) +// } + +// storageDiffs := make([]rpc.StorageDiff, 0, len(update.StateDiff.StorageDiffs)) +// for addr, diffs := range update.StateDiff.StorageDiffs { +// entries := make([]rpc.Entry, 0, len(diffs)) +// for key, value := range diffs { +// entries = append(entries, rpc.Entry{ +// Key: key, +// Value: *value, +// }) +// } + +// storageDiffs = append(storageDiffs, rpc.StorageDiff{ +// Address: addr, +// StorageEntries: entries, +// }) +// } + +// deployedContracts := make([]rpc.DeployedContract, 0, len(update.StateDiff.DeployedContracts)) +// for addr, classHash := range update.StateDiff.DeployedContracts { +// deployedContracts = append(deployedContracts, rpc.DeployedContract{ +// Address: addr, +// ClassHash: *classHash, +// }) +// } + +// declaredClasses := make([]rpc.DeclaredClass, 0, len(update.StateDiff.DeclaredV1Classes)) +// for classHash, compiledClassHash := range update.StateDiff.DeclaredV1Classes { +// declaredClasses = append(declaredClasses, rpc.DeclaredClass{ +// ClassHash: classHash, +// CompiledClassHash: *compiledClassHash, +// }) +// } + +// replacedClasses := make([]rpc.ReplacedClass, 0, len(update.StateDiff.ReplacedClasses)) +// for addr, classHash := range update.StateDiff.ReplacedClasses { +// replacedClasses = append(replacedClasses, rpc.ReplacedClass{ +// ClassHash: *classHash, +// ContractAddress: addr, +// }) +// } + +// return &rpc.StateUpdate{ +// BlockHash: update.BlockHash, +// OldRoot: update.OldRoot, +// NewRoot: update.NewRoot, +// StateDiff: &rpc.StateDiff{ +// DeprecatedDeclaredClasses: update.StateDiff.DeclaredV0Classes, +// DeclaredClasses: declaredClasses, +// ReplacedClasses: replacedClasses, +// Nonces: nonces, +// StorageDiffs: storageDiffs, +// DeployedContracts: deployedContracts, +// }, +// }, nil +// } + +// func (s *StarknetRPC) Syncing() (*rpc.Sync, *jsonrpc.Error) { +// return nil, nil +// } + +// func (s *StarknetRPC) TraceTransaction(ctx context.Context, hash felt.Felt) (*vm.TransactionTrace, *jsonrpc.Error) { +// receipt, err := s.GetReceiptByHash(hash) +// if err != nil { +// return nil, err +// } +// blockhash := receipt.BlockHash +// if blockhash == nil { + +// } +// } + +// func (s *StarknetRPC) TraceBlockTransactions() () {} + +// func (s *StarknetRPC) traceTransaction(ctx context.Context, blockHash *felt.Felt) ([]rpc.TracedBlockTransaction, *jsonrpc.Error) { +// // not pending +// if blockHash != nil { + +// } +// } + func (s *StarknetRPC) GetBlockWithTxHashes(id rpc.BlockID) (*rpc.BlockWithTxHashes, *jsonrpc.Error) { req := &cairo.BlockWithTxHashesRequest{BlockID: cairo.NewFromJunoBlockID(id)} resp, jsonErr := s.adaptChainRead(req, "GetBlockWithTxHashes") @@ -110,6 +252,46 @@ func (s *StarknetRPC) Call(call rpc.FunctionCall, id rpc.BlockID) ([]*felt.Felt, return cr.ReturnData, cr.Err } +func (s *StarknetRPC) EstimateMessageFee(msg rpc.MsgFromL1, id rpc.BlockID) (*rpc.FeeEstimate, *jsonrpc.Error) { + feeEstimate, err := s.estimateMessageFee(msg, id, s.EstimateFee) + if err != nil { + return nil, err + } + return feeEstimate, nil +} + +type estimateFeeHandler func(broadcastedTxns []rpc.BroadcastedTransaction, + simulationFlags []rpc.SimulationFlag, id rpc.BlockID, +) ([]rpc.FeeEstimate, *jsonrpc.Error) + +func (s *StarknetRPC) estimateMessageFee( + msg rpc.MsgFromL1, + id rpc.BlockID, + f estimateFeeHandler, +) (*rpc.FeeEstimate, *jsonrpc.Error) { + calldata := make([]*felt.Felt, 0, len(msg.Payload)+1) + // The order of the calldata parameters matters. msg.From must be prepended. + calldata = append(calldata, new(felt.Felt).SetBytes(msg.From.Bytes())) + for payloadIdx := range msg.Payload { + calldata = append(calldata, &msg.Payload[payloadIdx]) + } + tx := rpc.BroadcastedTransaction{ + Transaction: rpc.Transaction{ + Type: rpc.TxnL1Handler, + ContractAddress: &msg.To, + EntryPointSelector: &msg.Selector, + CallData: &calldata, + Version: &felt.Zero, // Needed for transaction hash calculation. + Nonce: &felt.Zero, // Needed for transaction hash calculation. + }, + // Needed to marshal to blockifier type. + // Must be greater than zero to successfully execute transaction. + PaidFeeOnL1: new(felt.Felt).SetUint64(1), + } + estimates, _ := f([]rpc.BroadcastedTransaction{tx}, nil, id) + return &estimates[0], nil +} + func (s *StarknetRPC) EstimateFee(broadcastedTxns []rpc.BroadcastedTransaction, simulationFlags []rpc.SimulationFlag, id rpc.BlockID, ) ([]rpc.FeeEstimate, *jsonrpc.Error) { @@ -184,6 +366,54 @@ func (s *StarknetRPC) GetTransactionByHash(hash felt.Felt) (*rpc.Transaction, *j return tr.Tx, tr.Err } +func (s *StarknetRPC) GetBlockTransactionCount(id rpc.BlockID) (uint64, *jsonrpc.Error) { + blockWithTxs, err := s.GetBlockWithTxs(id) + if err != nil { + return 0, err + } + return uint64(len(blockWithTxs.Transactions)), err +} + +func (s *StarknetRPC) GetTransactionByBlockIdAndIndex(id rpc.BlockID, txIndex int) (*rpc.Transaction, *jsonrpc.Error) { + if txIndex < 0 { + return nil, rpc.ErrInvalidTxIndex + } + + blockWithTxHashes, err := s.GetBlockWithTxHashes(id) + if err != nil { + return nil, err + } + hash := *blockWithTxHashes.TxnHashes[txIndex] //不是很确定这里是不是直接可以用index + + txReq := &cairo.TransactionRequest{Hash: hash} + resp, jsonErr := s.adaptChainRead(txReq, "GetTransaction") + if jsonErr != nil { + return nil, jsonErr + } + tr := resp.DataInterface.(*cairo.TransactionResponse) + return tr.Tx, tr.Err + + // if id.Pending { + // pending, err := s.bcReader.Pending() + // if err != nil { + // return nil, rpc.ErrBlockNotFound + // } + + // if uint64(txIndex) > pending.Block.TransactionCount { + // return nil, rpc.ErrInvalidTxIndex + // } + + // hash := *pending.Block.Transactions[txIndex].Hash() + // txReq := &cairo.TransactionRequest{Hash: hash} + // resp, jsonErr := s.adaptChainRead(txReq, "GetTransaction") + // if jsonErr != nil { + // return nil, jsonErr + // } + // tr := resp.DataInterface.(*cairo.TransactionResponse) + // return tr.Tx, tr.Err + // } +} + func (s *StarknetRPC) GetTransactionStatus(ctx context.Context, hash felt.Felt) (*rpc.TransactionStatus, *jsonrpc.Error) { tsReq := &cairo.TransactionStatusRequest{Hash: hash} resp, jsonErr := s.adaptChainRead(tsReq, "GetTransactionStatus") diff --git a/cairo/starknetrpc/rpc.go b/cairo/starknetrpc/rpc.go index c75a7e3..14f9df4 100644 --- a/cairo/starknetrpc/rpc.go +++ b/cairo/starknetrpc/rpc.go @@ -3,14 +3,20 @@ package starknetrpc import ( "context" "errors" - "github.com/sirupsen/logrus" "itachi/cairo/config" "net" "net/http" "runtime" "time" + "github.com/sirupsen/logrus" + + "github.com/NethermindEth/juno/blockchain" + "github.com/NethermindEth/juno/db" + "github.com/NethermindEth/juno/db/pebble" "github.com/NethermindEth/juno/jsonrpc" + + // "github.com/NethermindEth/juno/sync" "github.com/NethermindEth/juno/utils" "github.com/NethermindEth/juno/validator" "github.com/rs/cors" @@ -21,10 +27,12 @@ import ( const CairoTripod = "cairo" type StarknetRPC struct { - chain *kernel.Kernel - log utils.SimpleLogger - srv *http.Server - network utils.Network + chain *kernel.Kernel + log utils.SimpleLogger + srv *http.Server + network utils.Network + bcReader blockchain.Reader + // syncReader sync.Reader } func NewStarknetRPC(chain *kernel.Kernel, cfg *config.Config) (*StarknetRPC, error) { @@ -69,6 +77,15 @@ func NewStarknetRPC(chain *kernel.Kernel, cfg *config.Config) (*StarknetRPC, err } s.network = utils.Network(cfg.Network) + + var database db.DB + database, err = pebble.New(cfg.DbPath, cfg.DbCache, cfg.DbMaxOpenFiles, log) + if err != nil { + return nil, err + } + reader := blockchain.New(database, s.network) + s.bcReader = reader + return s, nil } From d78d441aed4d355cd45bb252bd9c3d675bba9c7d Mon Sep 17 00:00:00 2001 From: idmsy Date: Sun, 26 May 2024 17:42:31 +0800 Subject: [PATCH 2/2] fixed unsuitable comments --- cairo/api.go | 2 +- cairo/starknetrpc/methods.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cairo/api.go b/cairo/api.go index 3bc1e34..76e6fc3 100644 --- a/cairo/api.go +++ b/cairo/api.go @@ -38,7 +38,7 @@ type LatestBlockResponse struct { Err *jsonrpc.Error `json:"err"` } -// 不是很确定这里是不是其实不需要request的内容,直接response +// fixme: not sure if request structure is a must func (c *Cairo) LatestBlock(ctx *context.ReadContext) { Block, err := c.Chain.GetEndBlock() if err != nil { diff --git a/cairo/starknetrpc/methods.go b/cairo/starknetrpc/methods.go index f27be36..196fb8b 100644 --- a/cairo/starknetrpc/methods.go +++ b/cairo/starknetrpc/methods.go @@ -383,7 +383,7 @@ func (s *StarknetRPC) GetTransactionByBlockIdAndIndex(id rpc.BlockID, txIndex in if err != nil { return nil, err } - hash := *blockWithTxHashes.TxnHashes[txIndex] //不是很确定这里是不是直接可以用index + hash := *blockWithTxHashes.TxnHashes[txIndex] // fixme: not sure if txIndex can be straightly used here txReq := &cairo.TransactionRequest{Hash: hash} resp, jsonErr := s.adaptChainRead(txReq, "GetTransaction")