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

Implemented state.go and types.go #42

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
198 changes: 198 additions & 0 deletions execution/state.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
package execution
import (
"sync"
"github.com/BlocSoc-iitr/selene/common"
"github.com/holiman/uint256"
)
type State struct {
mu sync.RWMutex
blocks map[uint64]*common.Block
finalizedBlock *common.Block
hashes map[[32]byte]uint64
txs map[[32]byte]TransactionLocation
historyLength uint64
}
type TransactionLocation struct {
Block uint64
Index int
}
func NewState(historyLength uint64, blockChan <-chan *common.Block, finalizedBlockChan <-chan *common.Block) *State {
s := &State{
blocks: make(map[uint64]*common.Block),
hashes: make(map[[32]byte]uint64),
txs: make(map[[32]byte]TransactionLocation),
historyLength: historyLength,
}
go func() {
for {
select {
case block := <-blockChan:
if block != nil {
s.PushBlock(block)
}
case block := <-finalizedBlockChan:
if block != nil {
s.PushFinalizedBlock(block)
}
}
}
}()

return s
}
func (s *State) PushBlock(block *common.Block) {
s.mu.Lock()
defer s.mu.Unlock()
s.hashes[block.Hash] = block.Number
for i, txHash := range block.Transactions.Hashes {
loc := TransactionLocation{
Block: block.Number,
Index: i,
}
s.txs[txHash] = loc
}

s.blocks[block.Number] = block

for len(s.blocks) > int(s.historyLength) {
var oldestNumber uint64 = ^uint64(0)
for number := range s.blocks {
if number < oldestNumber {
oldestNumber = number
}
}
s.removeBlock(oldestNumber)
}
}
func (s *State) PushFinalizedBlock(block *common.Block) {
s.mu.Lock()
defer s.mu.Unlock()

s.finalizedBlock = block

if oldBlock, exists := s.blocks[block.Number]; exists {
if oldBlock.Hash != block.Hash {
s.removeBlock(oldBlock.Number)
s.PushBlock(block)
}
} else {
s.PushBlock(block)
}
}
func (s *State) removeBlock(number uint64) {
if block, exists := s.blocks[number]; exists {
delete(s.blocks, number)
delete(s.hashes, block.Hash)
for _, txHash := range block.Transactions.Hashes {
delete(s.txs, txHash)
}
}
}
func (s *State) GetBlock(tag common.BlockTag) *common.Block {
s.mu.RLock()
defer s.mu.RUnlock()

if tag.Latest {
var latestNumber uint64
var latestBlock *common.Block
for number, block := range s.blocks {
if number > latestNumber {
latestNumber = number
latestBlock = block
}
}
return latestBlock
} else if tag.Finalized {
return s.finalizedBlock
} else {
return s.blocks[tag.Number]
}
}
func (s *State) GetBlockByHash(hash [32]byte) *common.Block {
s.mu.RLock()
defer s.mu.RUnlock()

if number, exists := s.hashes[hash]; exists {
return s.blocks[number]
}
return nil
}
func (s *State) GetTransaction(hash [32]byte) *common.Transaction {
s.mu.RLock()
defer s.mu.RUnlock()

if loc, exists := s.txs[hash]; exists {
if block, exists := s.blocks[loc.Block]; exists {
if len(block.Transactions.Full) > loc.Index {
return &block.Transactions.Full[loc.Index]
}
}
}
return nil
}
func (s *State) GetTransactionByBlockAndIndex(blockHash [32]byte, index uint64) *common.Transaction {
s.mu.RLock()
defer s.mu.RUnlock()

if number, exists := s.hashes[blockHash]; exists {
if block, exists := s.blocks[number]; exists {
if int(index) < len(block.Transactions.Full) {
return &block.Transactions.Full[index]
}
}
}
return nil
}
func (s *State) GetStateRoot(tag common.BlockTag) *[32]byte {
if block := s.GetBlock(tag); block != nil {
return &block.StateRoot
}
return nil
}
func (s *State) GetReceiptsRoot(tag common.BlockTag) *[32]byte {
if block := s.GetBlock(tag); block != nil {
return &block.ReceiptsRoot
}
return nil
}
func (s *State) GetBaseFee(tag common.BlockTag) *uint256.Int {
if block := s.GetBlock(tag); block != nil {
return &block.BaseFeePerGas
}
return nil
}
func (s *State) GetCoinbase(tag common.BlockTag) *common.Address {
if block := s.GetBlock(tag); block != nil {
return &block.Miner
}
return nil
}
func (s *State) LatestBlockNumber() *uint64 {
s.mu.RLock()
defer s.mu.RUnlock()

var latestNumber uint64
for number := range s.blocks {
if number > latestNumber {
latestNumber = number
}
}
if latestNumber > 0 {
return &latestNumber
}
return nil
}
func (s *State) OldestBlockNumber() *uint64 {
s.mu.RLock()
defer s.mu.RUnlock()
var oldestNumber uint64 = ^uint64(0)
for number := range s.blocks {
if number < oldestNumber {
oldestNumber = number
}
}
if oldestNumber < ^uint64(0) {
return &oldestNumber
}
return nil
}
108 changes: 108 additions & 0 deletions execution/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package execution

import (
"encoding/json"
"fmt"
"reflect"
"github.com/ethereum/go-ethereum/common"
"math/big"
"github.com/BlocSoc-iitr/selene/utils"
)
type Account struct {
Balance *big.Int
Nonce uint64
CodeHash common.Hash
Code []byte
StorageHash common.Hash
Slots map[common.Hash]*big.Int
}
type CallOpts struct {
From *common.Address `json:"from,omitempty"`
To *common.Address `json:"to,omitempty"`
Gas *big.Int `json:"gas,omitempty"`
GasPrice *big.Int `json:"gasPrice,omitempty"`
Value *big.Int `json:"value,omitempty"`
Data []byte `json:"data,omitempty"`
}
func (c *CallOpts) String() string {
return fmt.Sprintf("CallOpts{From: %v, To: %v, Gas: %v, GasPrice: %v, Value: %v, Data: 0x%x}",
c.From, c.To, c.Gas, c.GasPrice, c.Value, c.Data)
}

func (c *CallOpts) Serialize() ([]byte, error) {
serialized := make(map[string]interface{})
v := reflect.ValueOf(*c)
t := v.Type()

for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
fieldName := t.Field(i).Name

if !field.IsNil() {
var value interface{}
var err error

switch field.Interface().(type) {
case *common.Address:
value = utils.Address_to_hex_string(*field.Interface().(*common.Address))
case *big.Int:
value = utils.U64_to_hex_string(field.Interface().(*big.Int).Uint64())
case []byte:
value, err = utils.Bytes_serialize(field.Interface().([]byte))
if err != nil {
return nil, fmt.Errorf("error serializing %s: %w", fieldName, err)
}
default:
return nil, fmt.Errorf("unsupported type for field %s", fieldName)
}

serialized[fieldName] = value
}
}

return json.Marshal(serialized)
}

func (c *CallOpts) Deserialize(data []byte) error {
var serialized map[string]string
if err := json.Unmarshal(data, &serialized); err != nil {
return err
}

v := reflect.ValueOf(c).Elem()
t := v.Type()

for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
fieldName := t.Field(i).Name

if value, ok := serialized[fieldName]; ok {
switch field.Interface().(type) {
case *common.Address:
addressBytes, err := utils.Hex_str_to_bytes(value)
if err != nil {
return fmt.Errorf("error deserializing %s: %w", fieldName, err)
}
addr := common.BytesToAddress(addressBytes)
field.Set(reflect.ValueOf(&addr))
case *big.Int:
intBytes, err := utils.Hex_str_to_bytes(value)
if err != nil {
return fmt.Errorf("error deserializing %s: %w", fieldName, err)
}
bigInt := new(big.Int).SetBytes(intBytes)
field.Set(reflect.ValueOf(bigInt))
case []byte:
byteValue, err := utils.Bytes_deserialize([]byte(value))
if err != nil {
return fmt.Errorf("error deserializing %s: %w", fieldName, err)
}
field.SetBytes(byteValue)
default:
return fmt.Errorf("unsupported type for field %s", fieldName)
}
}
}

return nil
}
Loading