diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f117bc7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +go.mod +go.sum \ No newline at end of file diff --git a/common/types.go b/common/types.go index 77fabeb..6690ea6 100644 --- a/common/types.go +++ b/common/types.go @@ -3,10 +3,10 @@ package common import ( "encoding/json" "fmt" - "math/big" "strconv" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/core/types" "github.com/holiman/uint256" ) @@ -45,27 +45,28 @@ type Transactions struct { Hashes [][32]byte Full []Transaction // transaction needs to be defined } - +// Updated as earlier, txn data fetched from rpc was not able to unmarshal +// into the struct type Transaction struct { - AccessList types.AccessList - Hash common.Hash - Nonce uint64 - BlockHash [32]byte - BlockNumber *uint64 - TransactionIndex uint64 - From string - To *common.Address - Value *big.Int - GasPrice *big.Int - Gas uint64 - Input []byte - ChainID *big.Int - TransactionType uint8 - Signature *Signature - MaxFeePerGas *big.Int - MaxPriorityFeePerGas *big.Int - MaxFeePerBlobGas *big.Int - BlobVersionedHashes []common.Hash + AccessList types.AccessList `json:"accessList"` + Hash common.Hash `json:"hash"` + Nonce hexutil.Uint64 `json:"nonce"` + BlockHash common.Hash `json:"blockHash"` // Pointer because it's nullable + BlockNumber hexutil.Uint64 `json:"blockNumber"` // Pointer because it's nullable + TransactionIndex hexutil.Uint64 `json:"transactionIndex"` + From *common.Address `json:"from"` + To *common.Address `json:"to"` // Pointer because 'to' can be null for contract creation + Value hexutil.Big `json:"value"` + GasPrice hexutil.Big `json:"gasPrice"` + Gas hexutil.Uint64 `json:"gas"` + Input hexutil.Bytes `json:"input"` + ChainID hexutil.Big `json:"chainId"` + TransactionType hexutil.Uint `json:"type"` + Signature *Signature `json:"signature"` + MaxFeePerGas hexutil.Big `json:"maxFeePerGas"` + MaxPriorityFeePerGas hexutil.Big `json:"maxPriorityFeePerGas"` + MaxFeePerBlobGas hexutil.Big `json:"maxFeePerBlobGas"` + BlobVersionedHashes []common.Hash `json:"blobVersionedHashes"` } type Signature struct { @@ -116,7 +117,7 @@ func (b BlockTag) String() string { if b.Finalized { return "finalized" } - return fmt.Sprintf("%d", b.Number) + return fmt.Sprintf("0x%x", b.Number) } func (b *BlockTag) UnmarshalJSON(data []byte) error { diff --git a/consensus/consensus.go b/consensus/consensus.go index 84f6827..2ffd115 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -60,10 +60,11 @@ type GenericUpdate struct { FinalizedHeader consensus_core.Header FinalityBranch []consensus_core.Bytes32 } - +// The consensus client fields are updated because state takes chan *common.Block, +// not common.Block type ConsensusClient struct { - BlockRecv *common.Block - FinalizedBlockRecv *common.Block + BlockRecv chan *common.Block + FinalizedBlockRecv chan *common.Block CheckpointRecv *[]byte genesisTime uint64 db Database @@ -73,7 +74,7 @@ type Inner struct { RPC rpc.ConsensusRpc Store LightClientStore lastCheckpoint *[]byte - blockSend chan common.Block + blockSend chan *common.Block finalizedBlockSend chan *common.Block checkpointSend chan *[]byte Config *config.Config @@ -88,22 +89,33 @@ type LightClientStore struct { } func (con ConsensusClient) New(rpc *string, config config.Config) ConsensusClient { - blockSend := make(chan common.Block, 256) + blockSend := make(chan *common.Block, 256) finalizedBlockSend := make(chan *common.Block) checkpointSend := make(chan *[]byte) - - db, err := con.db.New(&config) + // Here the initialisation of database was wrong as it was pointing to an interface, + // not a types that implements the interface. This resulted in an invalid address error + _db, err := (&ConfigDB{}).New(&config) if err != nil { panic(err) } - + // Doing this makes the db variable a concrete type (ConfigDB here) + // It can also be switched to FileDB if needed + db, ok := _db.(*ConfigDB) + if !ok { + panic(errors.New("Expected ConfigDB instance")) + } var initialCheckpoint [32]byte + // There was a problem in this assignment initially as it was setting the checkpoint + // to db.LoadCheckpoint() when the user inputed a checkpoint. This updated one is + // according to Helios. if config.Checkpoint != nil { + initialCheckpoint = *config.Checkpoint + } else { initialNewCheckpoint, errorWhileLoadingCheckpoint := db.LoadCheckpoint() copy(initialCheckpoint[:], initialNewCheckpoint) if errorWhileLoadingCheckpoint != nil { - log.Printf("error while loading checkpoint: %v", errorWhileLoadingCheckpoint) + initialCheckpoint = config.DefaultCheckpoint } } if initialCheckpoint == [32]byte{} { @@ -152,13 +164,11 @@ func (con ConsensusClient) New(rpc *string, config config.Config) ConsensusClien } }() - blocksReceived := <-blockSend - finalizedBlocksReceived := <-finalizedBlockSend checkpointsReceived := <-checkpointSend return ConsensusClient{ - BlockRecv: &blocksReceived, - FinalizedBlockRecv: finalizedBlocksReceived, + BlockRecv: blockSend, + FinalizedBlockRecv: finalizedBlockSend, CheckpointRecv: checkpointsReceived, genesisTime: config.Chain.GenesisTime, db: db, @@ -251,7 +261,7 @@ func sync_all_fallback(inner *Inner, chainID uint64) error { return <-errorChan } -func (in *Inner) New(rpcURL string, blockSend chan common.Block, finalizedBlockSend chan *common.Block, checkpointSend chan *[]byte, config *config.Config) *Inner { +func (in *Inner) New(rpcURL string, blockSend chan *common.Block, finalizedBlockSend chan *common.Block, checkpointSend chan *[]byte, config *config.Config) *Inner { rpcClient := rpc.NewConsensusRpc(rpcURL) return &Inner{ @@ -524,7 +534,7 @@ func (in *Inner) send_blocks() error { log.Printf("Error converting payload to block: %v", err) return } - in.blockSend <- *block + in.blockSend <- block }() go func() { @@ -1036,17 +1046,17 @@ func processTransaction(txBytes *[]byte, blockHash consensus_core.Bytes32, block tx := common.Transaction{ Hash: txEnvelope.Hash(), - Nonce: txEnvelope.Nonce(), - BlockHash: blockHash, - BlockNumber: blockNumber, - TransactionIndex: index, + Nonce: hexutil.Uint64(txEnvelope.Nonce()), + BlockHash: geth.BytesToHash(blockHash[:]), + BlockNumber: hexutil.Uint64(*blockNumber), + TransactionIndex: hexutil.Uint64(index), To: txEnvelope.To(), - Value: txEnvelope.Value(), - GasPrice: txEnvelope.GasPrice(), - Gas: txEnvelope.Gas(), + Value: hexutil.Big(*txEnvelope.Value()), + GasPrice: hexutil.Big(*txEnvelope.GasPrice()), + Gas: hexutil.Uint64(txEnvelope.Gas()), Input: txEnvelope.Data(), - ChainID: txEnvelope.ChainId(), - TransactionType: txEnvelope.Type(), + ChainID: hexutil.Big(*txEnvelope.ChainId()), + TransactionType: hexutil.Uint(txEnvelope.Type()), } // Handle signature and transaction type logic @@ -1055,7 +1065,7 @@ func processTransaction(txBytes *[]byte, blockHash consensus_core.Bytes32, block if err != nil { return common.Transaction{}, fmt.Errorf("failed to recover sender: %v", err) } - tx.From = from.Hex() + tx.From = &from // Extract signature components r, s, v := txEnvelope.RawSignatureValues() @@ -1070,12 +1080,12 @@ func processTransaction(txBytes *[]byte, blockHash consensus_core.Bytes32, block case types.AccessListTxType: tx.AccessList = txEnvelope.AccessList() case types.DynamicFeeTxType: - tx.MaxFeePerGas = new(big.Int).Set(txEnvelope.GasFeeCap()) - tx.MaxPriorityFeePerGas = new(big.Int).Set(txEnvelope.GasTipCap()) + tx.MaxFeePerGas = hexutil.Big(*new(big.Int).Set(txEnvelope.GasFeeCap())) + tx.MaxPriorityFeePerGas = hexutil.Big(*new(big.Int).Set(txEnvelope.GasTipCap())) case types.BlobTxType: - tx.MaxFeePerGas = new(big.Int).Set(txEnvelope.GasFeeCap()) - tx.MaxPriorityFeePerGas = new(big.Int).Set(txEnvelope.GasTipCap()) - tx.MaxFeePerBlobGas = new(big.Int).Set(txEnvelope.BlobGasFeeCap()) + tx.MaxFeePerGas = hexutil.Big(*new(big.Int).Set(txEnvelope.GasFeeCap())) + tx.MaxPriorityFeePerGas = hexutil.Big(*new(big.Int).Set(txEnvelope.GasTipCap())) + tx.MaxFeePerBlobGas = hexutil.Big(*new(big.Int).Set(txEnvelope.BlobGasFeeCap())) tx.BlobVersionedHashes = txEnvelope.BlobHashes() case types.LegacyTxType: // No additional fields to set @@ -1169,4 +1179,4 @@ func toGethSyncCommittee(committee *consensus_core.SyncCommittee) *beacon.Serial } copy(s[512*48:], jsoncommittee.Aggregate[:]) return &s -} +} \ No newline at end of file diff --git a/consensus/consensus_test.go b/consensus/consensus_test.go index ae1c3e1..823c64d 100644 --- a/consensus/consensus_test.go +++ b/consensus/consensus_test.go @@ -36,7 +36,7 @@ func GetClient(strictCheckpointAge bool, sync bool) (*Inner, error) { log.Fatalf("failed to decode checkpoint: %v", err) } - blockSend := make(chan common.Block, 256) + blockSend := make(chan *common.Block, 256) finalizedBlockSend := make(chan *common.Block) channelSend := make(chan *[]byte) diff --git a/consensus/rpc/consensus_rpc.go b/consensus/rpc/consensus_rpc.go index f31ac85..c9eebde 100644 --- a/consensus/rpc/consensus_rpc.go +++ b/consensus/rpc/consensus_rpc.go @@ -2,7 +2,6 @@ package rpc import ( "github.com/BlocSoc-iitr/selene/consensus/consensus_core" - ) // return types not mention and oarameters as well @@ -16,6 +15,7 @@ type ConsensusRpc interface { } + func NewConsensusRpc(rpc string) ConsensusRpc { if rpc == "testdata/" { return NewMockRpc(rpc) diff --git a/consensus/rpc/mock_rpc_test.go b/consensus/rpc/mock_rpc_test.go index 8215bb9..9f6c74f 100644 --- a/consensus/rpc/mock_rpc_test.go +++ b/consensus/rpc/mock_rpc_test.go @@ -1,5 +1,6 @@ package rpc + import ( "encoding/json" "os" diff --git a/execution/errors_test.go b/execution/errors_test.go new file mode 100644 index 0000000..595f07c --- /dev/null +++ b/execution/errors_test.go @@ -0,0 +1,85 @@ +package execution + +import ( + "errors" + "testing" + + "github.com/BlocSoc-iitr/selene/consensus/consensus_core" + "github.com/BlocSoc-iitr/selene/consensus/types" + "github.com/stretchr/testify/assert" +) + +func TestExecutionErrors(t *testing.T) { + // Test InvalidAccountProofError + address := types.Address{0x01, 0x02} + err := NewInvalidAccountProofError(address) + assert.EqualError(t, err, "invalid account proof for string: [1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]") + + // Test InvalidStorageProofError + slot := consensus_core.Bytes32{0x0a} + err = NewInvalidStorageProofError(address, slot) + assert.EqualError(t, err, "invalid storage proof for string: [1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], slot: [10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]") + + // Test CodeHashMismatchError + found := consensus_core.Bytes32{0x03} + expected := consensus_core.Bytes32{0x04} + err = NewCodeHashMismatchError(address, found, expected) + assert.EqualError(t, err, "code hash mismatch for string: [1 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], found: [3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], expected: [4 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]") + + // Test ReceiptRootMismatchError + tx := consensus_core.Bytes32{0x05} + err = NewReceiptRootMismatchError(tx) + assert.EqualError(t, err, "receipt root mismatch for tx: [5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]") + + // Test MissingTransactionError + err = NewMissingTransactionError(tx) + assert.EqualError(t, err, "missing transaction for tx: [5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]") + + // Test MissingLogError + err = NewMissingLogError(tx, 3) + assert.EqualError(t, err, "missing log for transaction: [5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], index: 3") + + // Test TooManyLogsToProveError + err = NewTooManyLogsToProveError(5000, 1000) + assert.EqualError(t, err, "too many logs to prove: 5000, current limit is: 1000") + + // Test InvalidBaseGasFeeError + err = NewInvalidBaseGasFeeError(1000, 2000, 123456) + assert.EqualError(t, err, "Invalid base gas fee selene 1000 vs rpc endpoint 2000 at block 123456") + + // Test BlockNotFoundError + err = NewBlockNotFoundError(123456) + assert.EqualError(t, err, "Block 123456 not found") + + // Test EmptyExecutionPayloadError + err = NewEmptyExecutionPayloadError() + assert.EqualError(t, err, "Selene Execution Payload is empty") +} + +func TestEvmErrors(t *testing.T) { + // Test RevertError + data := []byte{0x08, 0xc3, 0x79, 0xa0} + err := NewRevertError(data) + assert.EqualError(t, err, "execution reverted: [8 195 121 160]") + + // Test GenericError + err = NewGenericError("generic error") + assert.EqualError(t, err, "evm error: generic error") + + // Test RpcError + rpcErr := errors.New("rpc connection failed") + err = NewRpcError(rpcErr) + assert.EqualError(t, err, "rpc error: rpc connection failed") +} + +func TestDecodeRevertReason(t *testing.T) { + // Test successful revert reason decoding + reasonData := []byte{0x08, 0xc3, 0x79, 0xa0} + reason := DecodeRevertReason(reasonData) + assert.NotEmpty(t, reason, "Revert reason should be decoded") + + // Test invalid revert data + invalidData := []byte{0x00} + reason = DecodeRevertReason(invalidData) + assert.Contains(t, reason, "invalid data for unpacking") +} diff --git a/execution/execution.go b/execution/execution.go index 59b69e8..7435577 100644 --- a/execution/execution.go +++ b/execution/execution.go @@ -26,12 +26,15 @@ type ExecutionClient struct { } func (e *ExecutionClient) New(rpc string, state *State) (*ExecutionClient, error) { - r, err := ExecutionRpc.New(nil, &rpc) + // This is updated as earlier, when ExecutionRpc.New() was called, it was giving an + // invalid address or nil pointer dereference error as there wasn't a concrete type that implemented ExecutionRpc + var r ExecutionRpc + r, err := (&HttpRpc{}).New(&rpc) if err != nil { return nil, err } return &ExecutionClient{ - Rpc: *r, + Rpc: r, state: state, }, nil } @@ -60,17 +63,25 @@ func (e *ExecutionClient) CheckRpc(chainID uint64) error { } // GetAccount retrieves the account information -func (e *ExecutionClient) GetAccount(address *seleneCommon.Address, slots []common.Hash, tag seleneCommon.BlockTag) (Account, error) { //Account from execution/types.go +func (e *ExecutionClient) GetAccount(address *seleneCommon.Address, slots *[]common.Hash, tag seleneCommon.BlockTag) (Account, error) { //Account from execution/types.go block := e.state.GetBlock(tag) - proof, _ := e.Rpc.GetProof(address, &slots, block.Number) - + // Error Handling + proof, err := e.Rpc.GetProof(address, slots, block.Number) + // fmt.Printf("%v",proof); + if err != nil { + return Account{}, err + } accountPath := crypto.Keccak256(address.Addr[:]) - accountEncoded, _ := EncodeAccount(&proof) + accountEncoded, err := EncodeAccount(&proof) + if err != nil { + return Account{}, err + } accountProofBytes := make([][]byte, len(proof.AccountProof)) for i, hexByte := range proof.AccountProof { accountProofBytes[i] = hexByte } - isValid, err := VerifyProof(accountProofBytes, block.StateRoot[:], accountPath, accountEncoded) + // fmt.Printf("%v",accountProofBytes) + isValid, err := VerifyProof(accountProofBytes, block.StateRoot[:], accountPath, accountEncoded) if err != nil { return Account{}, err } @@ -78,7 +89,7 @@ func (e *ExecutionClient) GetAccount(address *seleneCommon.Address, slots []comm return Account{}, NewInvalidAccountProofError(address.Addr) } // modify - slotMap := make(map[common.Hash]*big.Int) + slotMap := []Slot{} for _, storageProof := range proof.StorageProof { key, err := utils.Hex_str_to_bytes(storageProof.Key.Hex()) if err != nil { @@ -105,13 +116,16 @@ func (e *ExecutionClient) GetAccount(address *seleneCommon.Address, slots []comm if !isValid { return Account{}, fmt.Errorf("invalid storage proof for address: %v, key: %v", *address, storageProof.Key) } - slotMap[storageProof.Key] = storageProof.Value.ToBig() + slotMap = append(slotMap, Slot{ + Key: storageProof.Key, + Value: storageProof.Value.ToBig(), + }) } var code []byte - if bytes.Equal(proof.CodeHash.Bytes(), crypto.Keccak256([]byte(KECCAK_EMPTY))) { + if proof.CodeHash == [32]byte{} { code = []byte{} } else { - code, err := e.Rpc.GetCode(address, block.Number) + code, err := e.Rpc.GetCode(address, block.Number) if err != nil { return Account{}, err } @@ -123,7 +137,7 @@ func (e *ExecutionClient) GetAccount(address *seleneCommon.Address, slots []comm } account := Account{ Balance: proof.Balance.ToBig(), - Nonce: proof.Nonce, + Nonce: uint64(proof.Nonce), Code: code, CodeHash: proof.CodeHash, StorageHash: proof.StorageHash, @@ -298,10 +312,8 @@ func (e *ExecutionClient) GetLogs(filter ethereum.FilterQuery) ([]types.Log, err select { case logs := <-logsChan: if len(logs) > MAX_SUPPORTED_LOGS_NUMBER { - return nil, &ExecutionError{ - Kind: "TooManyLogs", - Details: fmt.Sprintf("Too many logs to prove: %d, max: %d", len(logs), MAX_SUPPORTED_LOGS_NUMBER), - } + // The earlier error was not returning properly + return nil, errors.New("logs exceed max supported logs number") } logPtrs := make([]*types.Log, len(logs)) for i := range logs { @@ -506,19 +518,25 @@ func rlpHash(obj interface{}) (common.Hash, error) { } return crypto.Keccak256Hash(encoded), nil } + +// This function is updated as it was going in an infinite loop func calculateMerkleRoot(hashes []common.Hash) common.Hash { - if len(hashes) == 1 { + switch len(hashes) { + case 0: + return common.Hash{} // Return empty hash for empty slice + case 1: return hashes[0] + default: + if len(hashes)%2 != 0 { + hashes = append(hashes, hashes[len(hashes)-1]) + } + var newLevel []common.Hash + for i := 0; i < len(hashes); i += 2 { + combinedHash := crypto.Keccak256(append(hashes[i].Bytes(), hashes[i+1].Bytes()...)) + newLevel = append(newLevel, common.BytesToHash(combinedHash)) + } + return calculateMerkleRoot(newLevel) } - if len(hashes)%2 != 0 { - hashes = append(hashes, hashes[len(hashes)-1]) - } - var newLevel []common.Hash - for i := 0; i < len(hashes); i += 2 { - combinedHash := crypto.Keccak256(append(hashes[i].Bytes(), hashes[i+1].Bytes()...)) - newLevel = append(newLevel, common.BytesToHash(combinedHash)) - } - return calculateMerkleRoot(newLevel) } // contains checks if a receipt is in the list of receipts diff --git a/execution/execution_test.go b/execution/execution_test.go new file mode 100644 index 0000000..3e64f76 --- /dev/null +++ b/execution/execution_test.go @@ -0,0 +1,694 @@ +package execution + +import ( + "bytes" + "encoding/json" + "math/big" + "sync" + "testing" + seleneCommon "github.com/BlocSoc-iitr/selene/common" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" + "github.com/stretchr/testify/assert" +) + +func CreateNewState() *State { + blockChan := make(chan *seleneCommon.Block) + finalizedBlockChan := make(chan *seleneCommon.Block) + + state := NewState(5, blockChan, finalizedBlockChan) + + // Simulate blocks to push + block1 := &seleneCommon.Block{ + Number: 1, + Hash: [32]byte{0x1}, + Transactions: seleneCommon.Transactions{ + Hashes: [][32]byte{{0x11}, {0x12}}, + }, + } + block2 := &seleneCommon.Block{ + Number: 2, + Hash: [32]byte{0x2}, + Transactions: seleneCommon.Transactions{ + Hashes: [][32]byte{{0x21}, {0x22}}, + Full: []seleneCommon.Transaction{ + { + Hash: common.Hash([32]byte{0x21}), + GasPrice: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + Gas: hexutil.Uint64(5), + MaxFeePerGas: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + }, + {Hash: common.Hash([32]byte{0x22})}, + }, + }, + } + + block3 := &seleneCommon.Block{ + Number: 1000000, + Hash: common.HexToHash("0x8e38b4dbf6b11fcc3b9dee84fb7986e29ca0a02cecd8977c161ff7333329681e"), + Transactions: seleneCommon.Transactions{ + Hashes: [][32]byte{ + common.HexToHash("0xe9e91f1ee4b56c0df2e9f06c2b8c27c6076195a88a7b8537ba8313d80e6f124e"), + common.HexToHash("0xea1093d492a1dcb1bef708f771a99a96ff05dcab81ca76c31940300177fcf49f"), + }, + Full: []seleneCommon.Transaction{ + { + Hash: common.HexToHash("0xe9e91f1ee4b56c0df2e9f06c2b8c27c6076195a88a7b8537ba8313d80e6f124e"), + GasPrice: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + Gas: hexutil.Uint64(60), + MaxFeePerGas: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + }, + { + Hash: common.HexToHash("0xea1093d492a1dcb1bef708f771a99a96ff05dcab81ca76c31940300177fcf49f"), + GasPrice: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + Gas: hexutil.Uint64(60), + MaxFeePerGas: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + }, + }, + }, + } + + // Push blocks through channel + go func() { + blockChan <- block1 + blockChan <- block2 + blockChan <- block3 + close(blockChan) + }() + + // Allow goroutine to process the blocks + wg := sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + for len(state.blocks) < 3 { + // wait for blocks to be processed + } + }() + + wg.Wait() + + // Simulate finalized block + finalizedBlock := &seleneCommon.Block{ + Number: 2, + Hash: [32]byte{0x2}, + Transactions: seleneCommon.Transactions{ + Hashes: [][32]byte{{0x21}, {0x22}}, + Full: []seleneCommon.Transaction{ + { + Hash: common.Hash([32]byte{0x21}), + GasPrice: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + Gas: hexutil.Uint64(5), + MaxFeePerGas: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + }, + {Hash: common.Hash([32]byte{0x22})}, + }, + }, + } + go func() { + finalizedBlockChan <- finalizedBlock + close(finalizedBlockChan) + }() + + // Wait for finalized block to be processed + wg.Add(1) + go func() { + defer wg.Done() + for state.finalizedBlock == nil { + _=0; + // wait for finalized block to be processed + } + }() + wg.Wait() + + return state +} + +func CreateNewExecutionClient() *ExecutionClient { + rpc := "https://eth-mainnet.g.alchemy.com/v2/j28GcevSYukh-GvSeBOYcwHOfIggF1Gt" + state := CreateNewState() + var executionClient *ExecutionClient + executionClient, _ = executionClient.New(rpc, state) + + return executionClient +} + +func TestCheckRpc(t *testing.T) { + executionClient := CreateNewExecutionClient() + chainId := uint64(1) + err := executionClient.CheckRpc(chainId) + + assert.NoError(t, err, "Error Found") + + chainId = uint64(2) + err = executionClient.CheckRpc(chainId) + + assert.Equal(t, NewIncorrectRpcNetworkError(), err, "Error didn't match") +} + +// Both GetAccount() and GetTransactionReceipt() depend on state + +// func TestGetAccount(t *testing.T) { +// executionClient := CreateNewExecutionClient() +// addressBytes, _ := utils.Hex_str_to_bytes("0x457a22804cf255ee8c1b7628601c5682b3d70c71") + +// address := seleneCommon.Address{Addr: [20]byte(addressBytes)} + +// slots := []common.Hash{} +// tag := seleneCommon.BlockTag{ +// Finalized: true, +// } +// print("Check\n") +// account, err := executionClient.GetAccount(&address, &slots, tag) + +// assert.NoError(t, err, "Error found") +// assert.Equal(t, Account{}, account, "Account didn't match") +// } + +func TestToBlockNumberArg(t *testing.T) { + blockNumber := uint64(5050) + assert.Equal(t, "0x13ba", toBlockNumArg(blockNumber), "Block Number didn't match") + + blockNumber = uint64(0) + assert.Equal(t, "latest", toBlockNumArg(blockNumber), "Block Number didn't match") +} + +//* Not possible to test as we would have to send actual txn onMainnet +// func TestSendRawTransaction(t *testing.T) { +// executionClient := CreateNewExecutionClient() +// transaction := common.Hex2Bytes("02f8720113840a436fe4850749a01900825208942ce3384fcaea81a0f10b2599ffb2f0603e6169f1878e1bc9bf04000080c080a097f6540a48025bd28dd3c43f33aa0269a29b40d852396fab1ab7c2f95a3930e7a03f69a6bca9ef4be6ce60735e76133670617286e15e18af96b7e5e0afcdc240c6") +// fmt.Printf("Transaction Bytes: %v ", transaction) +// hash, err := executionClient.SendRawTransaction(transaction) + +// assert.NoError(t, err, "Found Error") +// assert.Equal(t, common.Hash{}, hash, "Transaction Hash didn't match") +// } + +func TestGetBlock(t *testing.T) { + executionClient := CreateNewExecutionClient() + blockTag := seleneCommon.BlockTag{ + Number: 1, + } + + block, err := executionClient.GetBlock(blockTag, false) + expected := seleneCommon.Block{ + Number: 1, + Hash: [32]byte{0x1}, + Transactions: seleneCommon.Transactions{ + Hashes: [][32]byte{{0x11}, {0x12}}, + }, + } + + assert.NoError(t, err, "Found Error") + assert.Equal(t, expected, block, "Value didn't match expected") + + blockTag = seleneCommon.BlockTag{ + Finalized: true, + } + + block, err = executionClient.GetBlock(blockTag, false) + expected = seleneCommon.Block{ + Number: 2, + Hash: [32]byte{0x2}, + Transactions: seleneCommon.Transactions{ + Hashes: [][32]byte{{0x21}, {0x22}}, + }, + } + + assert.NoError(t, err, "Found Error") + assert.Equal(t, expected, block, "Value didn't match expected") + + block, err = executionClient.GetBlock(blockTag, true) + expected = seleneCommon.Block{ + Number: 2, + Hash: [32]byte{0x2}, + Transactions: seleneCommon.Transactions{ + Hashes: [][32]byte{{0x21}, {0x22}}, + Full: []seleneCommon.Transaction{ + { + Hash: common.Hash([32]byte{0x21}), + GasPrice: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + Gas: hexutil.Uint64(5), + MaxFeePerGas: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + }, + {Hash: common.Hash([32]byte{0x22})}, + }, + }, + } + + assert.NoError(t, err, "Found Error") + assert.Equal(t, expected, block, "Value didn't match expected") +} + +func TestExecutionGetBlockByHash(t *testing.T) { + executionClient := CreateNewExecutionClient() + hash := common.Hash([32]byte{0x2}) + + block, err := executionClient.GetBlockByHash(hash, false) + expected := seleneCommon.Block{ + Number: 2, + Hash: [32]byte{0x2}, + Transactions: seleneCommon.Transactions{ + Hashes: [][32]byte{{0x21}, {0x22}}, + }, + } + + assert.NoError(t, err, "Found Error") + assert.Equal(t, expected, block, "Value didn't match expected") + + block, err = executionClient.GetBlockByHash(hash, true) + expected = seleneCommon.Block{ + Number: 2, + Hash: [32]byte{0x2}, + Transactions: seleneCommon.Transactions{ + Hashes: [][32]byte{{0x21}, {0x22}}, + Full: []seleneCommon.Transaction{ + { + Hash: common.Hash([32]byte{0x21}), + GasPrice: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + Gas: hexutil.Uint64(5), + MaxFeePerGas: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + }, + {Hash: common.Hash([32]byte{0x22})}, + }, + }, + } + + assert.NoError(t, err, "Found Error") + assert.Equal(t, expected, block, "Value didn't match expected") +} + +func TestGetTransactionByBlockHashAndIndex(t *testing.T) { + executionClient := CreateNewExecutionClient() + hash := common.Hash([32]byte{0x2}) + + txn, err := executionClient.GetTransactionByBlockHashAndIndex(hash, 0) + + expected := seleneCommon.Transaction{ + Hash: common.Hash([32]byte{0x21}), + GasPrice: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + Gas: hexutil.Uint64(5), + MaxFeePerGas: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + } + + assert.NoError(t, err, "Found Error") + assert.Equal(t, expected, txn, "Value didn't match expected") +} + +// func TestExecutionGetTransactionReceipt(t *testing.T) { +// executionClient := CreateNewExecutionClient() +// txHash := common.HexToHash("0xea1093d492a1dcb1bef708f771a99a96ff05dcab81ca76c31940300177fcf49f") +// txnReceipt, err := executionClient.GetTransactionReceipt(txHash) +// expected := types.Receipt{} + +// assert.NoError(t, err, "Found Error") +// assert.Equal(t, expected, txnReceipt, "Receipt didn't match") +// } + +func TestExecutionGetTransaction(t *testing.T) { + executionClient := CreateNewExecutionClient() + txHash := common.Hash([32]byte{0x21}) + txn, err := executionClient.GetTransaction(txHash) + expected := seleneCommon.Transaction{ + Hash: common.Hash([32]byte{0x21}), + GasPrice: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + Gas: hexutil.Uint64(5), + MaxFeePerGas: hexutil.Big(*hexutil.MustDecodeBig("0x12345")), + } + + assert.NoError(t, err, "Found Error") + assert.Equal(t, expected, txn, "Txn didn't match") +} + +func TestExecutionGetLogs(t *testing.T) { + executionClient := CreateNewExecutionClient() + + // Test case 1: Basic log retrieval + filter := ethereum.FilterQuery{ + FromBlock: hexutil.MustDecodeBig("0x14057d0"), + ToBlock: hexutil.MustDecodeBig("0x1405814"), + Addresses: []common.Address{ + common.HexToAddress("0x6F9116ea572a207e4267f92b1D7b6F9b23536b07"), + }, + } + + _, err := executionClient.GetLogs(filter) + assert.NoError(t, err, "GetLogs should not return error for valid filter") +} + +func TestExecutionGetFilterChanges(t *testing.T) { + executionClient := CreateNewExecutionClient() + // Test case 1: Basic filter creation + filter := ethereum.FilterQuery{ + FromBlock: big.NewInt(1), + ToBlock: big.NewInt(2), + Addresses: []common.Address{ + common.HexToAddress("0x1234567890123456789012345678901234567890"), + }, + } + + filterID, _ := executionClient.GetNewFilter(filter) + + _, err := executionClient.GetFilterChanges(&filterID) + assert.NoError(t, err, "GetFilterChanges should not return error for valid filter ID") +} + +func TestExecutionUninstallFilter(t *testing.T) { + executionClient := CreateNewExecutionClient() + + // Test case 1: Basic filter creation + filter := ethereum.FilterQuery{ + FromBlock: big.NewInt(1), + ToBlock: big.NewInt(2), + Addresses: []common.Address{ + common.HexToAddress("0x1234567890123456789012345678901234567890"), + }, + } + filterID, _ := executionClient.GetNewFilter(filter) + + result, err := executionClient.UninstallFilter(&filterID) + assert.NoError(t, err, "UninstallFilter should not return error for valid filter ID") + assert.True(t, result, "UninstallFilter should return true on success") + + // Test case 2: Invalid filter ID + invalidFilterID := uint256.NewInt(999) + result, err = executionClient.UninstallFilter(invalidFilterID) + assert.NoError(t, err, "UninstallFilter should not return error for invalid filter ID") + assert.False(t, result, "UninstallFilter should return false for invalid filter ID") +} + +func TestExecutionGetNewFilter(t *testing.T) { + executionClient := CreateNewExecutionClient() + + // Test case 1: Basic filter creation + filter := ethereum.FilterQuery{ + FromBlock: big.NewInt(1), + ToBlock: big.NewInt(2), + Addresses: []common.Address{ + common.HexToAddress("0x1234567890123456789012345678901234567890"), + }, + } + + filterID, err := executionClient.GetNewFilter(filter) + assert.NoError(t, err, "GetNewFilter should not return error for valid filter") + assert.NotEqual(t, uint256.Int{}, filterID, "FilterID should not be empty") + + // Test case 2: Null blocks defaults to latest + filterNullBlocks := ethereum.FilterQuery{ + Addresses: []common.Address{ + common.HexToAddress("0x1234567890123456789012345678901234567890"), + }, + } + + filterID, err = executionClient.GetNewFilter(filterNullBlocks) + assert.NoError(t, err, "GetNewFilter should handle null blocks") + assert.NotEqual(t, uint256.Int{}, filterID, "FilterID should not be empty") +} + +func TestExecutionGetNewBlockFilter(t *testing.T) { + executionClient := CreateNewExecutionClient() + + filterID, err := executionClient.GetNewBlockFilter() + assert.NoError(t, err, "GetNewBlockFilter should not return error") + assert.NotEqual(t, uint256.Int{}, filterID, "FilterID should not be empty") +} + +func TestExecutionGetNewPendingTransactionFilter(t *testing.T) { + executionClient := CreateNewExecutionClient() + + filterID, err := executionClient.GetNewPendingTransactionFilter() + assert.NoError(t, err, "GetNewPendingTransactionFilter should not return error") + assert.NotEqual(t, uint256.Int{}, filterID, "FilterID should not be empty") +} + +func TestCalculateReceiptRoot(t *testing.T) { + // Test case 1: Empty receipts + _, err := CalculateReceiptRoot([][]byte{}) + assert.Error(t, err, "CalculateReceiptRoot should return error for empty receipts") + assert.Contains(t, err.Error(), "no receipts to calculate root") + + // Test case 2: Single receipt + receipt1 := []byte{1, 2, 3} + root, err := CalculateReceiptRoot([][]byte{receipt1}) + assert.NoError(t, err, "CalculateReceiptRoot should not return error for single receipt") + assert.NotEqual(t, common.Hash{}, root, "Root should not be empty") + + // Test case 3: Multiple receipts + receipt2 := []byte{4, 5, 6} + root, err = CalculateReceiptRoot([][]byte{receipt1, receipt2}) + assert.NoError(t, err, "CalculateReceiptRoot should not return error for multiple receipts") + assert.NotEqual(t, common.Hash{}, root, "Root should not be empty") +} + +func TestVerifyLogs(t *testing.T) { + executionClient := CreateNewExecutionClient() + logData := `{ + "address": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + "blockHash": "0xa495e3d87cf01430e9838341e1d36929fc9c8dfdb89133b372cae4019b5a534a", + "blockNumber": "0xC166C3", + "data": "0x0000000000000000000000000000000000000000000000003a395039b420f1bd", + "logIndex": "0x0", + "removed": false, + "topics": [ + "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef", + "0x00000000000000000000000043cc25b1fb6435d8d893fcf308de5c300a568be2", + "0x00000000000000000000000044d34985826578e5ba24ec78c93be968549bb918"], + "transactionHash": "0xac80bab0940f061e184b0dda380d994e6fc14ab5d0c6f689035631c81bfe220b", + "transactionIndex": "0x0" + }` + + var log types.Log + err := json.Unmarshal([]byte(logData), &log) + assert.NoError(t, err, "VerifyLogs should not give an error") + + // fmt.Printf("Expected Log: %v\n", log) + err = executionClient.verifyLogs([]*types.Log{&log}) + assert.NoError(t, err, "VerifyLogs should not give an error") +} + +func TestContains(t *testing.T) { + // Create test receipts + receipt1 := types.Receipt{ + TxHash: common.HexToHash("0x1"), + } + receipt2 := types.Receipt{ + TxHash: common.HexToHash("0x2"), + } + receipts := []types.Receipt{receipt1} + + // Test case 1: Receipt exists + assert.True(t, contains(receipts, receipt1), "Contains should return true for existing receipt") + + // Test case 2: Receipt doesn't exist + assert.False(t, contains(receipts, receipt2), "Contains should return false for non-existing receipt") +} + +// TestRlpHash tests the rlpHash function with various inputs +func TestRlpHash(t *testing.T) { + tests := []struct { + name string + input interface{} + wantErr bool + }{ + { + name: "Simple string", + input: "test", + wantErr: false, + }, + { + name: "Empty slice", + input: []byte{}, + wantErr: false, + }, + { + name: "Complex struct", + input: struct { + A string + B uint64 + }{"test", 123}, + wantErr: false, + }, + { + name: "Invalid input", + input: make(chan int), // channels cannot be RLP encoded + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + hash, err := rlpHash(tt.input) + if (err != nil) != tt.wantErr { + t.Errorf("rlpHash() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !tt.wantErr { + // Verify the hash is not empty + if hash == (common.Hash{}) { + t.Error("rlpHash() returned empty hash") + } + + // Verify deterministic behavior + hash2, _ := rlpHash(tt.input) + if hash != hash2 { + t.Error("rlpHash() is not deterministic") + } + } + }) + } +} + +// TestCalculateMerkleRoot tests the calculateMerkleRoot function +func TestCalculateMerkleRoot(t *testing.T) { + tests := []struct { + name string + hashes []common.Hash + want common.Hash + }{ + { + name: "Two hashes", + hashes: []common.Hash{ + common.HexToHash("0x1234"), + common.HexToHash("0x5678"), + }, + want: crypto.Keccak256Hash( + append( + common.HexToHash("0x1234").Bytes(), + common.HexToHash("0x5678").Bytes()..., + ), + ), + }, + { + name: "Odd number of hashes", + hashes: []common.Hash{ + common.HexToHash("0x1234"), + common.HexToHash("0x5678"), + common.HexToHash("0x9abc"), + }, + want: func() common.Hash { + // Duplicate last hash since odd number + hashes := []common.Hash{ + common.HexToHash("0x1234"), + common.HexToHash("0x5678"), + common.HexToHash("0x9abc"), + common.HexToHash("0x9abc"), + } + // Calculate first level + h1 := crypto.Keccak256Hash(append(hashes[0].Bytes(), hashes[1].Bytes()...)) + h2 := crypto.Keccak256Hash(append(hashes[2].Bytes(), hashes[3].Bytes()...)) + // Calculate root + return crypto.Keccak256Hash(append(h1.Bytes(), h2.Bytes()...)) + }(), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := calculateMerkleRoot(tt.hashes) + if got != tt.want { + t.Errorf("calculateMerkleRoot() = %v, want %v", got, tt.want) + } + + // Verify deterministic behavior + got2 := calculateMerkleRoot(tt.hashes) + if got != got2 { + t.Error("calculateMerkleRoot() is not deterministic") + } + }) + } +} + +// TestEncodeReceipt tests the encodeReceipt function +func TestEncodeReceipt(t *testing.T) { + bloom := types.Bloom{} + logs := []*types.Log{} + + tests := []struct { + name string + receipt *types.Receipt + wantErr bool + }{ + { + name: "Legacy receipt (type 0)", + receipt: &types.Receipt{ + Type: 0, + Status: 1, + CumulativeGasUsed: 21000, + Bloom: bloom, + Logs: logs, + }, + wantErr: false, + }, + { + name: "EIP-2930 receipt (type 1)", + receipt: &types.Receipt{ + Type: 1, + Status: 1, + CumulativeGasUsed: 21000, + Bloom: bloom, + Logs: logs, + }, + wantErr: false, + }, + { + name: "EIP-1559 receipt (type 2)", + receipt: &types.Receipt{ + Type: 2, + Status: 1, + CumulativeGasUsed: 21000, + Bloom: bloom, + Logs: logs, + }, + wantErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := encodeReceipt(tt.receipt) + if (err != nil) != tt.wantErr { + t.Errorf("encodeReceipt() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if !tt.wantErr { + // Verify the encoding starts with the correct type byte for non-legacy transactions + if tt.receipt.Type > 0 { + if got[0] != tt.receipt.Type { + t.Errorf("encodeReceipt() first byte = %d, want %d", got[0], tt.receipt.Type) + } + } + + // Verify the encoding is deterministic + got2, _ := encodeReceipt(tt.receipt) + if !bytes.Equal(got, got2) { + t.Error("encodeReceipt() is not deterministic") + } + + // Verify the encoded data can be decoded back + var decoded []interface{} + var decodedData []byte + if tt.receipt.Type > 0 { + decodedData = got[1:] // Skip type byte + } else { + decodedData = got + } + + err = rlp.DecodeBytes(decodedData, &decoded) + if err != nil { + t.Errorf("Failed to decode receipt: %v", err) + } + + // Verify decoded fields match original + if len(decoded) != 4 { + t.Errorf("Decoded receipt has wrong number of fields: got %d, want 4", len(decoded)) + } + } + }) + } +} diff --git a/execution/http_rpc.go b/execution/http_rpc.go index 01bd876..9d1f63a 100644 --- a/execution/http_rpc.go +++ b/execution/http_rpc.go @@ -1,6 +1,10 @@ package execution import ( + "encoding/hex" + "math/big" + "strconv" + seleneCommon "github.com/BlocSoc-iitr/selene/common" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" @@ -8,7 +12,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" "github.com/holiman/uint256" - "strconv" ) type HttpRpc struct { @@ -16,27 +19,43 @@ type HttpRpc struct { provider *rpc.Client } -func New(rpcUrl string) (*HttpRpc, error) { - client, err := rpc.Dial(rpcUrl) +// I have made some changes to ExecutionRpc and to HttpRpc as HttpRpc was not satisfying +// ExecutionRpc interface before +func (h *HttpRpc) New(rpcUrl *string) (ExecutionRpc, error) { + client, err := rpc.Dial(*rpcUrl) if err != nil { return nil, err } return &HttpRpc{ - url: rpcUrl, + url: *rpcUrl, provider: client, }, nil } -func (h *HttpRpc) GetProof(address *common.Address, slots *[]common.Hash, block uint64) (EIP1186ProofResponse, error) { +// All the changes that have been made in functions that fetch from rpc are because: +// The rpc expects request to be in form of hexadecimal strings. For example, if we want +// to send block number equal to 1405, it will interpret it as 0x1405, which is not it's hex representation +// +// Similarly, the values recieved from rpc should also be treated as hex strings +func (h *HttpRpc) GetProof(address *seleneCommon.Address, slots *[]common.Hash, block uint64) (EIP1186ProofResponse, error) { resultChan := make(chan struct { proof EIP1186ProofResponse err error }) - + // All arguments to rpc are expected to be in form of hex strings + var slotHex []string + if slots != nil { + for _, slot := range *slots { + slotHex = append(slotHex, slot.Hex()) + } + } + if len(*slots) == 0 { + slotHex = []string{} + } go func() { var proof EIP1186ProofResponse - err := h.provider.Call(&proof, "eth_getProof", address, slots, toBlockNumArg(block)) + err := h.provider.Call(&proof, "eth_getProof", "0x"+hex.EncodeToString(address.Addr[:]), slotHex, toBlockNumArg(block)) resultChan <- struct { proof EIP1186ProofResponse err error @@ -50,17 +69,19 @@ func (h *HttpRpc) GetProof(address *common.Address, slots *[]common.Hash, block return result.proof, nil } -func (h *HttpRpc) CreateAccessList(opts CallOpts, block seleneCommon.BlockTag) (types.AccessList, error) { +// TODO: CreateAccessList is throwing an error +// There is a problem in unmarshaling the response into AccessList +func (h *HttpRpc) CreateAccessList(opts CallOpts, block seleneCommon.BlockTag) (AccessList, error) { resultChan := make(chan struct { - accessList types.AccessList + accessList AccessList err error }) go func() { - var accessList types.AccessList - err := h.provider.Call(&accessList, "eth_createAccessList", opts, block) + var accessList AccessList + err := h.provider.Call(&accessList, "eth_createAccessList", opts, block.String()) resultChan <- struct { - accessList types.AccessList + accessList AccessList err error }{accessList, err} close(resultChan) @@ -68,12 +89,12 @@ func (h *HttpRpc) CreateAccessList(opts CallOpts, block seleneCommon.BlockTag) ( result := <-resultChan if result.err != nil { - return nil, result.err + return AccessList{}, result.err } return result.accessList, nil } -func (h *HttpRpc) GetCode(address *common.Address, block uint64) (hexutil.Bytes, error) { +func (h *HttpRpc) GetCode(address *seleneCommon.Address, block uint64) ([]byte, error) { resultChan := make(chan struct { code hexutil.Bytes err error @@ -81,7 +102,7 @@ func (h *HttpRpc) GetCode(address *common.Address, block uint64) (hexutil.Bytes, go func() { var code hexutil.Bytes - err := h.provider.Call(&code, "eth_getCode", address, toBlockNumArg(block)) + err := h.provider.Call(&code, "eth_getCode", "0x"+hex.EncodeToString(address.Addr[:]), toBlockNumArg(block)) resultChan <- struct { code hexutil.Bytes err error @@ -196,7 +217,7 @@ func (h *HttpRpc) GetFilterChanges(filterID *uint256.Int) ([]types.Log, error) { go func() { var logs []types.Log - err := h.provider.Call(&logs, "eth_getFilterChanges", filterID) + err := h.provider.Call(&logs, "eth_getFilterChanges", filterID.Hex()) resultChan <- struct { logs []types.Log err error @@ -219,7 +240,7 @@ func (h *HttpRpc) UninstallFilter(filterID *uint256.Int) (bool, error) { go func() { var result bool - err := h.provider.Call(&result, "eth_uninstallFilter", filterID) + err := h.provider.Call(&result, "eth_uninstallFilter", filterID.Hex()) resultChan <- struct { result bool err error @@ -241,12 +262,13 @@ func (h *HttpRpc) GetNewFilter(filter *ethereum.FilterQuery) (uint256.Int, error }) go func() { - var filterID uint256.Int + var filterID hexutil.Big err := h.provider.Call(&filterID, "eth_newFilter", toFilterArg(*filter)) + filterResult := big.Int(filterID) resultChan <- struct { filterID uint256.Int err error - }{filterID, err} + }{*uint256.MustFromBig(&filterResult), err} close(resultChan) }() @@ -264,12 +286,13 @@ func (h *HttpRpc) GetNewBlockFilter() (uint256.Int, error) { }) go func() { - var filterID uint256.Int + var filterID hexutil.Big err := h.provider.Call(&filterID, "eth_newBlockFilter") + filterResult := big.Int(filterID) resultChan <- struct { filterID uint256.Int err error - }{filterID, err} + }{*uint256.MustFromBig(&filterResult), err} close(resultChan) }() @@ -287,12 +310,13 @@ func (h *HttpRpc) GetNewPendingTransactionFilter() (uint256.Int, error) { }) go func() { - var filterID uint256.Int + var filterID hexutil.Big err := h.provider.Call(&filterID, "eth_newPendingTransactionFilter") + filterResult := big.Int(filterID) resultChan <- struct { filterID uint256.Int err error - }{filterID, err} + }{*uint256.MustFromBig(&filterResult), err} close(resultChan) }() @@ -310,12 +334,12 @@ func (h *HttpRpc) ChainId() (uint64, error) { }) go func() { - var chainID uint64 + var chainID hexutil.Uint64 err := h.provider.Call(&chainID, "eth_chainId") resultChan <- struct { chainID uint64 err error - }{chainID, err} + }{uint64(chainID), err} close(resultChan) }() @@ -326,17 +350,17 @@ func (h *HttpRpc) ChainId() (uint64, error) { return result.chainID, nil } -func (h *HttpRpc) GetFeeHistory(blockCount uint64, lastBlock uint64, rewardPercentiles *[]float64) (ethereum.FeeHistory, error) { +func (h *HttpRpc) GetFeeHistory(blockCount uint64, lastBlock uint64, rewardPercentiles *[]float64) (FeeHistory, error) { resultChan := make(chan struct { - feeHistory ethereum.FeeHistory + feeHistory FeeHistory err error }) go func() { - var feeHistory ethereum.FeeHistory - err := h.provider.Call(&feeHistory, "eth_feeHistory", blockCount, lastBlock, rewardPercentiles) + var feeHistory FeeHistory + err := h.provider.Call(&feeHistory, "eth_feeHistory", hexutil.Uint64(blockCount).String(), toBlockNumArg(lastBlock), rewardPercentiles) resultChan <- struct { - feeHistory ethereum.FeeHistory + feeHistory FeeHistory err error }{feeHistory, err} close(resultChan) @@ -344,7 +368,7 @@ func (h *HttpRpc) GetFeeHistory(blockCount uint64, lastBlock uint64, rewardPerce result := <-resultChan if result.err != nil { - return ethereum.FeeHistory{}, result.err + return FeeHistory{}, result.err } return result.feeHistory, nil } @@ -365,10 +389,10 @@ func toFilterArg(q ethereum.FilterQuery) map[string]interface{} { arg["topics"] = q.Topics } if q.FromBlock != nil { - arg["fromBlock"] = q.FromBlock.String() + arg["fromBlock"] = toBlockNumArg(q.FromBlock.Uint64()) } if q.ToBlock != nil { - arg["toBlock"] = q.ToBlock.String() + arg["toBlock"] = toBlockNumArg(q.ToBlock.Uint64()) } return arg } diff --git a/execution/http_rpc_test.go b/execution/http_rpc_test.go new file mode 100644 index 0000000..dc6abc9 --- /dev/null +++ b/execution/http_rpc_test.go @@ -0,0 +1,269 @@ +package execution + +import ( + "encoding/json" + "fmt" + "math/big" + "testing" + + seleneCommon "github.com/BlocSoc-iitr/selene/common" + "github.com/BlocSoc-iitr/selene/utils" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/stretchr/testify/assert" +) + +func MakeNewRpc(t *testing.T) ExecutionRpc { + rpcUrl := "https://eth-mainnet.g.alchemy.com/v2/j28GcevSYukh-GvSeBOYcwHOfIggF1Gt" + + var httpRpc ExecutionRpc + httpRpc, err := (&HttpRpc{}).New(&rpcUrl) + + if err != nil { + t.Errorf("Error in creating new rpc: %v", err) + } + + return httpRpc +} + +func TestGetProof(t *testing.T) { + rpc := MakeNewRpc(t) + + addressBytes, err := utils.Hex_str_to_bytes("0xB856af30B938B6f52e5BfF365675F358CD52F91B") + if err != nil { + t.Errorf("Error in decoding address string:, %v", err) + } + + var address seleneCommon.Address = seleneCommon.Address{Addr: [20]byte(addressBytes)} + slots := []common.Hash{} + var block uint64 = 14900001 + + proof, err := rpc.GetProof(&address, &slots, block) + // fmt.Printf("Proof: %v", proof) + if err != nil { + t.Errorf("Error in fetching proof: %v", err) + } + + var accountProof EIP1186ProofResponse + proofString := `{ + "address": "0xb856af30b938b6f52e5bff365675f358cd52f91b", + "accountProof": [ + "0xf90211a021162657aa1e0af5eef47130ffc3362cb675ebbccfc99ee38ef9196144623507a073dec98f4943e2ab00f5751c23db67b65009bb3cb178d33f5aa93f0c08d583dda0d85b4e33773aaab742db880f8b64ea71f348c6eccb0a56854571bbd3db267f24a0bdcca489de03a49f109c1a2e7d3bd4e644d72de38b7b26dca2f8d3f112110c6fa05c7e8fdff6de07c4cb9ca6bea487a6e5be04af538c25480ce30761901b17e4bfa0d9891f4870e745509cfe17a31568f870b367a36329c892f1b2a37bf59e547183a0af08f747d2ea66efa5bcd03729a95f56297ef9b1e8533ac0d3c7546ebefd2418a0a107595919d4b102afaa0d9b91d9f554f83f0ad61a1e04487e5091543eb81db8a0a0725da6da3b62f88fc573a3fd0dd9dea9cba1750786021da836fd95b7295636a0fd7a768700af3caadaf52a08a23ab0b71ca52830f2b88b1a6b23a52f9ee05507a059434ae837706d7d317e4f7d03cd91f94ed0465fa8b99eaf18ca363bb318c7b3a09e9b831a5f59b781efd5dae8bea30bfd81b9fd5ea231d6b7e82da495c95dd35da0e72d02a01ed9bc928d94cad59ae0695f45120b7fbdbce43a2239a7e5bc81f731a0184bfb9a4051cbaa79917183d004c8d574d7ed5becaf9614c650ed40e8d123d9a0fa4797dc4a35af07f1cd6955318e3ff59578d4df32fd2174ed35f6c4db3471f9a0fec098d1fee8e975b5e78e19003699cf7cd746f47d03692d8e11c5fd58ba92a680", + "0xf90211a07fc5351578eb6ab7618a31e18c87b2b8b2703c682f2d4c1d01aaa8b53343036ea0e8871ae1828c54b9c9bbf7530890a2fe4e160fb62f72c740c7e79a756e07dbf3a04dd116a7d37146cd0ec730172fa97e84b1f13e687d56118e2d666a02a31a629fa08949d66b81ba98e5ca453ba1faf95c8476873d4c32ff6c9a2558b772c51c5768a028db2de6d80f3a06861d3acc082e3a6bb4a6948980a8e5527bd354a2da037779a09b01ba0fe0193c511161448c602bb9fff88b87ab0ded3255606a15f8bca9d348a0c1c1c6a89f2fdbee0840ff309b5cecd9764b5b5815b385576e75e235d1f04656a04e827215bb9511b3a288e33bb418132940a4d42d589b8db0f796ec917e8f9373a099398993d1d6fdd15d6082be370e4d2cc5d9870923d22770aaec9418f4b675d7a00cd1db5e131341b472af1bdf9a1bf1f1ca82bc5b280c8a50a20bcfff1ab0bdd4a09bbcc86c94be1aabf5c5ceced29f462f59103aa6dafe0fc60172bb2c549a8dbaa0902df0ba9eed7e8a6ebff2d06de8bcec5785bb98cba7606f7f40648408157ef4a0ba9dfd07c453e54504d41b7a44ea42e8220767d1e2a0e6e91ae8d5677ac70e50a0f02f2a5e26d7848f0e5a07de68cbbbd24253d545afb74aac81b35a70b6323f1ca0218b955deca7177f8f58c2da188611b333e5c7ef9212000f64ca92cd5bb6e5a0a049cd750f59e2d6f411d7b611b21b17c8eefe637ca01e1566c53f412308b34c6280", + "0xf90211a05303302919681c3ad0a56c607c9530ed910f44515f6b40c9633d1900bbbc7e0fa0459fc49e57f39ca6471b1c6905ede7eaa6d7186c8249485cc28338ba18c540cba0825307726d1b7c9d74973d37c12e8b035bf0334836e48ec3f2ff18bf2232dabea0a67ef68daba820c7d6343d1b147b73430ce5c5915a27581cfd12946c2307dc49a003c9b0f0b784de7d72f3b5d5fea87e30dc5fc6f93a0c218240f79a2c74b0f8e2a05a38ddf70df168305b8ba38e8ee54dfadc3f7d81335ec849cb143a10d9738a91a058f0692b5cb07a1c8c800fcf8a70c6e6189a5d72f24ca0040423cf450df1da44a0890dbc62e7429fcca3f1507ad2cd4799c0a7aab25db37ccad434ae23ae889807a075be60d2f635292e69dbc600600605cb8eaf75e96425fd3f2347a5c644db43b9a07b65ba06ee9d2b5dab0a9acc1b8b521cb42f91566de9c174636e817c3d990265a0de65bc6092e28b0cc1ed454fcc07ce26df21bb05efe0a4b4472ff63278e28b95a08077cd7de83428d376ff7588b32df903d2764af7d41deb9c873e9ae889823cd3a0af2f63837dc01e2efb9e40815761015a0d740c2d2549593eefd291a05d40b55aa0c3214baa8d347bd5703926b6fe3ee2f607d0debc0fd73352696dc10f4cbc517da01756cf85b4785bda4a9867a453f8ca1948f015bd329b84083f14d313bddafb80a00dac89194bc1f28d3971b9ca9d1e16a49c6383557187d7bbeb899626d60bfb1980", + "0xf90211a0bf50b49fae6cfe8b7671e3fa0c163aed76f6457720a2b7c18f567b3c02194c29a070dc71bb7e399e5ae66958261108c84b75e8aacc8d255ce28cd7c9029358872ba0d2ae86d376e65eee52338ad4a1951deb9312f2c161fdf5cfc3e36d5a07ee4239a0f2029dea5033d0e788191ba25fc25bb0570bdbbaf321dfdb076f6695c649a07ca066074b59980560ecdd8ebc96eeb93f50dc1e92983659ea4a6a61a4cff0f474cba01ad85159ddc98609ea628cd17897fe08b0d9a7bb07a2087d92a673e063039aaba04921580f8766f8f156546abd8f0e44af250b34e7323f35c40fdc078223822344a034e07b24a1c17f5dcff27b766099c206fbbb6e549d3f4c02fd8db0241061482aa0c852267182c35e2e5014ab6d656672e9446aaf79c6248d103870d55ee36368b1a00aed203f7e2684942a64f05306e57d64fd44eded94e2ce95e462be93adff640da02cea88d74264c91c546de3822b6169a427559781a774511409864d70a834706ea01d542f8a9b69674e58a5bb89fafb5e79cbe3607732455b09c2a996df48e48837a04c3bbb4f47041018455347567a4e3af472fafe179871f667c3d26038f5dabacfa03c4f12f7cdd35126ce5452aa8322bc8b497eb06c5c41741b590d40645a8fd14ea01334e9a4160b44b622e9523cb587d8ba4795bbf9ad3dd0aa1a2b7f5c6a5cbe94a08feae3d50602063d65763185633aa6e23bb47eca9a39982a4863a7cc6d3586ff80", + "0xf90211a03fc22103871f30d114942583d24adfab1ce2e651ff6705a05964318fde7c425aa01c86fd2e9d2a823db33bf4089ce1af41332b4e3069b31bb70a67861944d71688a08a90ae88b4479d21135517195f50df20fc29dbe495e09440b0fa5797fc0352c8a02a195c4a89ab6322d8daa221124274d711a9435587406addfb289f9360c0b1caa02f7ed0113a1b72febc7ceb7d9193baeaa093aebea76eea4729821f53d29b302ea07d5cbbeffd22fb0f9d510576cac47a604b2121ef8b08588eaefc46a07844515ca01c0c09d203e342fab9f80835f3aa7bb7e94cbf94d3a18b21ce905e75b690673fa07c310f931f12d1651dbb9bdffbe5e0a16db981dccfb3f4a838592e2347b1b187a091b24e00d37034ed70e0c6653f8616363efbea43be86d52aabf6a9c5d5049d3aa017cc8ab2e63508691dee64ddb2dbe5352fe531f55728368cab3d8634450730dca0d8ee92d688eabcdf28af1830d7217fcd1431c0b0e3311039c422c5f45f9d525da0abe4323ef90fb783ffd6bf29d240818b0a2477d2ad4577fce57642a5ba476957a0eee3fbc510b1a6d8da176b9eab2035837769988b216fcef67f6a215e5e261d5ea082bc27591d8b0408739712c2f0e623e3d296d12afd6b7356dae237a315a6ce3ba0634affb8f9744fed774851772cb5ce495c50212961a64262d915632c2bede721a0345017d846f3be29dca1f56a734886c26cb49d3bcbbae5f7356e55e71d84147d80", + "0xf90211a0a152d08043b3865248d8ae9c4594d6e09079f61ee4ad9afca98d3befb3a9307ca01313ea4df7ec991f5ba1b2175408765ea67111857ae25cafe21986810d633353a05cb00f30a4b6749cb8d01dda2ad665a2c570b7c6959b364c46da75fc2aebfa14a0a493d42fa40d6c8fb23090f20cebe9f1cde8a47d9594e666096c3f76219cfb34a06d2149e05ba1a31bcc352fd3a79fd42d95383f14a312862fa5f4b7b7bdb63254a0f40094679bf0599fdeecae8c800a423ad2499b67f9546d4085d5b7a351561072a05036fc625ed5a13d143fd0984e99969d2d48f962baec80b3f0e78323c8e864ffa0c0887db54ab0d4309ed5f563448bfc78a4a88c3a5473fcbd9bd263c8fcca4b9fa04287b193a315cba13a49482b4d83c068cf08b622593111e416b7a3b815e3595aa051b82224b54dc4050703157cdd6c74c618872c798badce7192fe1fd534814d5da017ac02273956b25dc2429750156e0a45bf461437bec84b784d19a5b964dd6882a05be9c25f80a6f34e9eb526d6c3c89e3ec2c5dd769d2d915d835208cac3f56d36a0ea7ae8e74baeae9d6307371f85cce46ba7ad46b5c2b3616a3573a26e1260bc31a04446678b6bf75075ae0261c179d88e49fae1d9482c214ec8c693239b583a6b18a09ec91d47f671c343cea224def0afcd62a57a408ac0e36b79cf29a4495ff9055ca0c3da71d14030daf8fb9157ec84f077df97dff395c1fcf8f04361e02aa1af36ff80", + "0xf8b1808080a038f5e1b2680d95eaf7f225b996fc482f60cabcebeae26f883a4b58e1e9c7bbeda0c220c5d76d85ad38d8f82d0d6d6f48db3c23ae3657d1ac3ca6e2d98b4e48bfde8080a06fe32c7c1f8f80ebe5128f11a7af3a5bc47dbc6ac0af705069532b1cbefd6792a0015eb7d24835c910fc5f906627968a1a9e810cb164afd634ca683b6bb34f0241808080808080a03ca1d0ead152d38c16bda91dac49e3f0c9a0afeaa67598c1fce2506f5f03162680", + "0xf86d9d3c3738deb88e49108e7a5bd83c14ad65b5ba598e2932551dc9b9ad1879b84df84b10874ef05b2fe9d8c8a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + ], + "balance": "0x4ef05b2fe9d8c8", + "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce": "0x10", + "storageHash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", + "storageProof": [] + }` + err = json.Unmarshal([]byte(proofString), &accountProof) + assert.NoError(t, err, "Found unexpected error") + assert.Equal(t, accountProof, proof, "Proof didn't match expected value") +} + +func TestCreateAccessList(t *testing.T) { + rpc := MakeNewRpc(t) + + fromAddress := common.HexToAddress("0x8885d8FFefb29AC94FCe584014266A6fE8437356") + toAddress := common.HexToAddress("0xb5C3dd56b4Dc6108b21b0Dac4A20898A464b8c41") + // inputData := common.Hex2Bytes("0x") + // data, err := utils.Hex_str_to_bytes("0x608060806080608155") + + opts := CallOpts{ + From: &fromAddress, + To: &toAddress, + } + block := seleneCommon.BlockTag{ + Finalized: true, + Number: 20985649, + } + + accessList, err := rpc.CreateAccessList(opts, block) + fmt.Println("Access list:", accessList) + assert.NoError(t, err, "Found Error") + +} + +func TestGetCode(t *testing.T) { + rpc := MakeNewRpc(t) + + addressBytes, err := utils.Hex_str_to_bytes("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") + if err != nil { + t.Errorf("Error in decoding address string:, %v", err) + } + + var address seleneCommon.Address = seleneCommon.Address{Addr: [20]byte(addressBytes)} + blockNumber := 20983632 + + code, _ := rpc.GetCode(&address, uint64(blockNumber)) + + codeString := "0x6060604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b9578063095ea7b31461014757806318160ddd146101a157806323b872dd146101ca5780632e1a7d4d14610243578063313ce5671461026657806370a082311461029557806395d89b41146102e2578063a9059cbb14610370578063d0e30db0146103ca578063dd62ed3e146103d4575b6100b7610440565b005b34156100c457600080fd5b6100cc6104dd565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561010c5780820151818401526020810190506100f1565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015257600080fd5b610187600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061057b565b604051808215151515815260200191505060405180910390f35b34156101ac57600080fd5b6101b461066d565b6040518082815260200191505060405180910390f35b34156101d557600080fd5b610229600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061068c565b604051808215151515815260200191505060405180910390f35b341561024e57600080fd5b61026460048080359060200190919050506109d9565b005b341561027157600080fd5b610279610b05565b604051808260ff1660ff16815260200191505060405180910390f35b34156102a057600080fd5b6102cc600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610b18565b6040518082815260200191505060405180910390f35b34156102ed57600080fd5b6102f5610b30565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561033557808201518184015260208101905061031a565b50505050905090810190601f1680156103625780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561037b57600080fd5b6103b0600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091908035906020019091905050610bce565b604051808215151515815260200191505060405180910390f35b6103d2610440565b005b34156103df57600080fd5b61042a600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610be3565b6040518082815260200191505060405180910390f35b34600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055503373ffffffffffffffffffffffffffffffffffffffff167fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c346040518082815260200191505060405180910390a2565b60008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105735780601f1061054857610100808354040283529160200191610573565b820191906000526020600020905b81548152906001019060200180831161055657829003601f168201915b505050505081565b600081600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60003073ffffffffffffffffffffffffffffffffffffffff1631905090565b600081600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101515156106dc57600080fd5b3373ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff16141580156107b457507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205414155b156108cf5781600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015151561084457600080fd5b81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b81600360008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600360008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610a2757600080fd5b80600360003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055503373ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051600060405180830381858888f193505050501515610ab457600080fd5b3373ffffffffffffffffffffffffffffffffffffffff167f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65826040518082815260200191505060405180910390a250565b600260009054906101000a900460ff1681565b60036020528060005260406000206000915090505481565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610bc65780601f10610b9b57610100808354040283529160200191610bc6565b820191906000526020600020905b815481529060010190602001808311610ba957829003601f168201915b505050505081565b6000610bdb33848461068c565b905092915050565b60046020528160005260406000206020528060005260406000206000915091505054815600a165627a7a72305820deb4c2ccab3c2fdca32ab3f46728389c2fe2c165d5fafa07661e4e004f6c344a0029" + codeBytes, err := utils.Hex_str_to_bytes(codeString) + + // fmt.Println("Code:", code) + assert.NoError(t, err, "Found Error") + assert.Equal(t, codeBytes, code, "Code didn't match expected value") +} + +// ** Not possible to test as to send actual transaction, ETH is needed on mainnet +// func TestSendRawTransaction(t *testing.T) { +// rpc := MakeNewRpc(t) +// // Raw Transaction Data +// data, _ := utils.Hex_str_to_bytes("") +// // transaction := seleneCommon.Transaction{ +// // From: "", +// // Nonce: , +// // } +// // data, err := json.Marshal(transaction) +// hash, err := rpc.SendRawTransaction(&data) + +// fmt.Println("Hash: ", hash) +// assert.NoError(t, err, "Found Error") +// } + +func TestGetTransactionReceipt(t *testing.T) { + rpc := MakeNewRpc(t) + txHash, _ := utils.Hex_str_to_bytes("0x4bc11033063e445e038e52e72266f5054845d3879704d0cf38bedeb86c924cec") + + _, err := rpc.GetTransactionReceipt((*common.Hash)(txHash)) + // expectedReceipt := types.Receipt{} + + assert.NoError(t, err, "Found Error") + // assert.Equal(t, expectedReceipt ,txnReceipt, "Receipt didn't match") +} + +func TestGetTransaction(t *testing.T) { + rpc := MakeNewRpc(t) + txHash, _ := utils.Hex_str_to_bytes("0x4bc11033063e445e038e52e72266f5054845d3879704d0cf38bedeb86c924cec") + + _, err := rpc.GetTransaction((*common.Hash)(txHash)) + // expectedTxn := seleneCommon.Transaction{} + + assert.NoError(t, err, "Found Error") + // assert.Equal(t, expectedTxn ,txn, "Receipt didn't match") +} + +func TestGetLogs(t *testing.T) { + rpc := MakeNewRpc(t) + address := common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") + blockHash := common.HexToHash("0x945b6b3568c39736fe1595234f0529efc8d64930a5343196aa8ef799b02dd609") + + filterQuery := ethereum.FilterQuery{ + Addresses: []common.Address{address}, + BlockHash: &blockHash, + } + + _, err := rpc.GetLogs(&filterQuery) + // expectedLogs := []types.Log{} + fmt.Println("") + assert.NoError(t, err, "Found Error") + // assert.Equal(t, expectedLogs, logs, "Logs didn't match") +} + +func TestGetChainId(t *testing.T) { + rpc := MakeNewRpc(t) + + chainId, err := rpc.ChainId() + + assert.NoError(t, err, "Found Error") + assert.Equal(t, chainId, uint64(1), "Expected chain Id to be 1") +} + +func TestGetNewFilter(t *testing.T) { + rpc := MakeNewRpc(t) + address := common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") + blockHash := common.HexToHash("0x945b6b3568c39736fe1595234f0529efc8d64930a5343196aa8ef799b02dd609") + + filterQuery := ethereum.FilterQuery{ + Addresses: []common.Address{address}, + BlockHash: &blockHash, + FromBlock: big.NewInt(200000000), + } + + _, err := rpc.GetNewFilter(&filterQuery) + + assert.NoError(t, err, "Found Error") + // assert.Equal(t, *uint256.MustFromHex("0x35ef5ae2a28c50b0a6a8fd0903c99e7f"), filterId, "Filter Id doesn't match") +} + +func TestGetFilterChanges(t *testing.T) { + rpc := MakeNewRpc(t) + + address := common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") + blockHash := common.HexToHash("0x945b6b3568c39736fe1595234f0529efc8d64930a5343196aa8ef799b02dd609") + + filterQuery := ethereum.FilterQuery{ + Addresses: []common.Address{address}, + BlockHash: &blockHash, + FromBlock: big.NewInt(200000000), + } + + filterId, _ := rpc.GetNewFilter(&filterQuery) + + logs, err := rpc.GetFilterChanges(&filterId) + expectedLogs := []types.Log{} + assert.NoError(t, err, "Found Error") + assert.Equal(t, expectedLogs, logs, "Logs didn't match") +} + +func TestUninstallFilter(t *testing.T) { + rpc := MakeNewRpc(t) + + address := common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") + blockHash := common.HexToHash("0x945b6b3568c39736fe1595234f0529efc8d64930a5343196aa8ef799b02dd609") + + filterQuery := ethereum.FilterQuery{ + Addresses: []common.Address{address}, + BlockHash: &blockHash, + FromBlock: big.NewInt(200000000), + } + + filterId, _ := rpc.GetNewFilter(&filterQuery) + + success, err := rpc.UninstallFilter(&filterId) + + assert.NoError(t, err, "Found Error") + assert.Equal(t, true, success, "Success didn't match") + + //Check whether the filter still exists + _, err = rpc.GetFilterChanges(&filterId) + assert.Error(t, err, "Found Error") +} + +func TestGetNewBlockFilter(t *testing.T) { + rpc := MakeNewRpc(t) + + _, err := rpc.GetNewBlockFilter() + + assert.NoError(t, err, "Found Error") + // assert.Equal(t, 0, filterId, "Filter ID not equal") + // _, err = rpc.GetFilterChanges(&filterId) + // assert.NoError(t, err, "Found Error") +} + +func TestGetNewPendingTransactionFilter(t *testing.T) { + rpc := MakeNewRpc(t) + + _, err := rpc.GetNewPendingTransactionFilter() + + assert.NoError(t, err, "Found Error") +} + +func TestGetFeeHistory(t *testing.T) { + rpc := MakeNewRpc(t) + blockCount := uint64(5) + lastBlock := uint64(20000000) + rewardPercentiles := []float64{10, 40, 90} + _, err := rpc.GetFeeHistory(blockCount, lastBlock, &rewardPercentiles) + + assert.NoError(t, err, "Found Error") + // assert.Equal(t, FeeHistory{}, feeHistory, "Fee History does not match") +} diff --git a/execution/proof.go b/execution/proof.go index 09085ee..026da89 100644 --- a/execution/proof.go +++ b/execution/proof.go @@ -13,13 +13,17 @@ func VerifyProof(proof [][]byte, root []byte, path []byte, value []byte) (bool, pathOffset := 0 for i, node := range proof { + // fmt.Printf("node:%v ", node) if !bytes.Equal(expectedHash, keccak256(node)) { + fmt.Printf("1st\n") return false, nil } var nodeList [][]byte if err := rlp.DecodeBytes(node, &nodeList); err != nil { fmt.Println("Error decoding node:", err) + fmt.Printf("2nd\n") + return false, err } @@ -51,16 +55,20 @@ func VerifyProof(proof [][]byte, root []byte, path []byte, value []byte) (bool, prefixLength := sharedPrefixLength(path, pathOffset, nodePath) if prefixLength < len(nodePath)*2-skipLength(nodePath) { // Proof shows a divergent path , but we're not at the leaf yet + fmt.Printf("3rd\n") + return false, nil } pathOffset += prefixLength expectedHash = nodeList[1] } } else { + fmt.Printf("4th\n") + return false, nil } } - + fmt.Printf("5th\n") return false, nil } @@ -69,6 +77,8 @@ func pathsMatch(p1 []byte, s1 int, p2 []byte, s2 int) bool { len2 := len(p2)*2 - s2 if len1 != len2 { + fmt.Printf("6th\n") + return false } @@ -76,6 +86,7 @@ func pathsMatch(p1 []byte, s1 int, p2 []byte, s2 int) bool { n1 := getNibble(p1, s1+offset) n2 := getNibble(p2, s2+offset) if n1 != n2 { + fmt.Printf("7th\n") return false } } @@ -100,8 +111,8 @@ func isEmptyValue(value []byte) bool { StorageHash: [32]byte{0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21}, CodeHash: [32]byte{0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70}, } - - encodedEmptyAccount, _ := rlp.EncodeToBytes(emptyAccount) + // EncodeToBytes needs pointer as argument + encodedEmptyAccount, _ := rlp.EncodeToBytes(&emptyAccount) isEmptySlot := len(value) == 1 && value[0] == 0x80 isEmptyAccount := bytes.Equal(value, encodedEmptyAccount) @@ -157,15 +168,16 @@ func keccak256(data []byte) []byte { return hash.Sum(nil) } +// Updated as EIP1186ProofResponse was updated func EncodeAccount(proof *EIP1186ProofResponse) ([]byte, error) { account := Account{ - Nonce: proof.Nonce, + Nonce: uint64(proof.Nonce), Balance: proof.Balance.ToBig(), StorageHash: proof.StorageHash, CodeHash: proof.CodeHash, } - - return rlp.EncodeToBytes(account) + // EncodeToBytes needs pointer as argument + return rlp.EncodeToBytes(&account) } // Make a generic function for it diff --git a/execution/proof_test.go b/execution/proof_test.go new file mode 100644 index 0000000..c88beb3 --- /dev/null +++ b/execution/proof_test.go @@ -0,0 +1,262 @@ +package execution + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "io" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/rlp" + "github.com/holiman/uint256" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func (a *Account) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{a.Nonce, a.Balance, a.StorageHash, a.CodeHash}) +} + +func (a *Account) DecodeRLP(s *rlp.Stream) error { + var accountRLP struct { + Nonce uint64 + Balance *big.Int + StorageHash common.Hash + CodeHash common.Hash + } + if err := s.Decode(&accountRLP); err != nil { + return err + } + a.Nonce = accountRLP.Nonce + a.Balance = accountRLP.Balance + a.StorageHash = accountRLP.StorageHash + a.CodeHash = accountRLP.CodeHash + + return nil +} + +func TestSharedPrefixLength(t *testing.T) { + t.Run("Match first 5 nibbles", func(t *testing.T) { + path := []byte{0x12, 0x13, 0x14, 0x6f, 0x6c, 0x64, 0x21} + pathOffset := 6 + nodePath := []byte{0x6f, 0x6c, 0x63, 0x21} + + sharedLen := sharedPrefixLength(path, pathOffset, nodePath) + assert.Equal(t, 5, sharedLen) + }) + + t.Run("Match first 7 nibbles with skip", func(t *testing.T) { + path := []byte{0x12, 0x13, 0x14, 0x6f, 0x6c, 0x64, 0x21} + pathOffset := 5 + nodePath := []byte{0x14, 0x6f, 0x6c, 0x64, 0x11} + + sharedLen := sharedPrefixLength(path, pathOffset, nodePath) + assert.Equal(t, 7, sharedLen) + }) + + t.Run("No match", func(t *testing.T) { + path := []byte{0x12, 0x34, 0x56} + pathOffset := 0 + nodePath := []byte{0x78, 0x9a, 0xbc} + + sharedLen := sharedPrefixLength(path, pathOffset, nodePath) + assert.Equal(t, 0, sharedLen) + }) + + t.Run("Complete match", func(t *testing.T) { + path := []byte{0x12, 0x34, 0x56} + pathOffset := 0 + nodePath := []byte{0x00, 0x12, 0x34, 0x56} + + sharedLen := sharedPrefixLength(path, pathOffset, nodePath) + assert.Equal(t, 6, sharedLen) + }) +} + +func TestSkipLength(t *testing.T) { + testCases := []struct { + name string + node []byte + expected int + }{ + {"Empty node", []byte{}, 0}, + {"Nibble 0", []byte{0x00}, 2}, + {"Nibble 1", []byte{0x10}, 1}, + {"Nibble 2", []byte{0x20}, 2}, + {"Nibble 3", []byte{0x30}, 1}, + {"Nibble 4", []byte{0x40}, 0}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := skipLength(tc.node) + assert.Equal(t, tc.expected, result) + }) + } +} + +func TestGetNibble(t *testing.T) { + path := []byte{0x12, 0x34, 0x56} + + testCases := []struct { + offset int + expected byte + }{ + {0, 0x1}, + {1, 0x2}, + {2, 0x3}, + {3, 0x4}, + {4, 0x5}, + {5, 0x6}, + } + + for _, tc := range testCases { + t.Run(fmt.Sprintf("Offset %d", tc.offset), func(t *testing.T) { + result := getNibble(path, tc.offset) + assert.Equal(t, tc.expected, result) + }) + } +} + +func TestIsEmptyValue(t *testing.T) { + t.Run("Empty slot", func(t *testing.T) { + value := []byte{0x80} + assert.True(t, isEmptyValue(value)) + }) + + t.Run("Empty account", func(t *testing.T) { + emptyAccount := Account{ + Nonce: 0, + Balance: big.NewInt(0), + StorageHash: [32]byte{0x56, 0xe8, 0x1f, 0x17, 0x1b, 0xcc, 0x55, 0xa6, 0xff, 0x83, 0x45, 0xe6, 0x92, 0xc0, 0xf8, 0x6e, 0x5b, 0x48, 0xe0, 0x1b, 0x99, 0x6c, 0xad, 0xc0, 0x01, 0x62, 0x2f, 0xb5, 0xe3, 0x63, 0xb4, 0x21}, + CodeHash: [32]byte{0xc5, 0xd2, 0x46, 0x01, 0x86, 0xf7, 0x23, 0x3c, 0x92, 0x7e, 0x7d, 0xb2, 0xdc, 0xc7, 0x03, 0xc0, 0xe5, 0x00, 0xb6, 0x53, 0xca, 0x82, 0x27, 0x3b, 0x7b, 0xfa, 0xd8, 0x04, 0x5d, 0x85, 0xa4, 0x70}, + } + encodedEmptyAccount, err := rlp.EncodeToBytes(&emptyAccount) + require.NoError(t, err) + assert.True(t, isEmptyValue(encodedEmptyAccount)) + }) + + t.Run("Non-empty value", func(t *testing.T) { + value := []byte{0x01, 0x23, 0x45} + assert.False(t, isEmptyValue(value)) + }) +} + +func TestEncodeAccount(t *testing.T) { + proof := &EIP1186ProofResponse{ + Nonce: 1, + Balance: uint256.NewInt(1000), + StorageHash: [32]byte{1, 2, 3}, + CodeHash: [32]byte{4, 5, 6}, + } + + encoded, err := EncodeAccount(proof) + require.NoError(t, err) + + var decodedAccount Account + err = rlp.DecodeBytes(encoded, &decodedAccount) + require.NoError(t, err) + + // Assert the decoded values match the original + assert.Equal(t, uint64(1), decodedAccount.Nonce) + assert.Equal(t, big.NewInt(1000), decodedAccount.Balance) + assert.Equal(t, proof.StorageHash, decodedAccount.StorageHash) + assert.Equal(t, proof.CodeHash, decodedAccount.CodeHash) +} + +func TestKeccak256(t *testing.T) { + input := []byte("hello world") + expected, _ := hex.DecodeString("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad") + + result := keccak256(input) + assert.Equal(t, expected, result) +} + +type Proof struct { + Address string `json:"address"` + AccountProof []string `json:"accountProof"` + Balance string `json:"balance"` + CodeHash string `json:"codeHash"` + Nonce string `json:"nonce"` + StorageHash string `json:"storageHash"` + StorageProof string `json:"storageProof"` +} + +func TestVerifyProof(t *testing.T) { + jsonString := `{ + "address":"0x457a22804cf255ee8c1b7628601c5682b3d70c71", + "accountProof":[ + "0xf90211a0c48ed6536f719a4e24c1195b099fd19f127448c0e2b4ad5729dcdcccb41a56b5a0a338e0f373eac20e4ad9528f9895da93762623350bb5defa693263ea6f9af4afa0304e41961a40be33e5213d3e583c0d30989e795b541c810e8456c12a7478c841a05b26d8545de569451cd3baee25ef6d525f6c27c89844b76ccebf99801708c1d9a069b7051e21066a40847fbe6e4217628f6979b1a361819c7ebe0980c063d684f5a06f5948452762bfe7061717765ab322a5e086ece9c4eddf442a047e253b557dc4a0ef37b2aaf540e598e0e046ee6a8c51c1c100d68dcda70bea1ebe4924ef7911f3a03218af1883a1bfb9828b0c3f07f16a8df859e81543633808ec625164db6b3644a0121b6fdd1951f7915bd55c96dc2cc546fa42bfadb4cfa3fc1a2cc3de232df289a0fdf5a771f4ee044d9c6a7ddd6d797ca102022d4786db1332c5530091f52a2238a08e59837befa581ee0e4d2d9b2caeac403106090117844590d876df445df09509a0d0eb738155e240e6c069c765102f526b54124d788c8e501ed96a6a29a326f4d7a0c14d799f056ca9d6bf91a10272dff64cf34eb4aff42e7b56bf14176e2e36a58aa03ae0cf5dc0b96aa9755207ec060c45b149e071f1453e7416acd282660a66a371a0495a02815d4434895bf7997771016ab2bf29c1f970c778c696444d4b5573ae87a008d340cb14293c576f934b7f8cbab6f0b07f6d4804957d6f8d6c48e2465d027f80", + "0xf90211a0a0b5e06bee86e09cc7f63d64ef1f3bd9a970ba8881d52b9a1a60849eef16b535a022756912563afa1c6cc7a49d0f34704f5919df048091561e21511bff585fc6d2a0eaa5bd3c14a851aeaeff1a7dbafb5456d4a218f12c63f0795ba2679d8f412ba6a07f8b49a363a58d81b7c51eab08998532ff3efdec14e6015d43429180a5a74929a0e62a8be403d29d6972426603c4a0962a24d5c56325bf1661c0984414605eaa55a0d567f6a8520f25ed64ece7d37b405374a51c3af48356c6fa72efec22f7d42e2aa0696255c12473694333c6724ff234a04f6995b8eb376e8e8be0c171965767ca2ea0b1b08ecb635708f8c0f42dffe86e09ac6b3bfe8319e544797a4199bde4d1fd15a05c0e9906e3d20bbec170f769824250862fd7dad378d6cfeda9b713acf3b72568a03d160d95900a9bc2e7e9c08766b4392a9ac86b7e006979c426e0885229e5f44da00bc337a5b553d0ffb67859b28cc59246262bf532c2b14dd456f160d778540b34a005d784ba4f478f2f315f1b678fbfc39ed2c0b01b02b55ed82ea9d62369cd35faa023f24fd63f87a947ee148c8001bc4039a5f481fca1e0f3f10a3791a9e0451571a01ffb2845581388c6b915135a9ee856bcc896f2def7eeeb88bdfd53ae32323431a09d7bde3d951bcc3505684616bf760dfdd69e03e6b2ca2a180bc7b6fc7922d9c6a01b29670cbbe0bb02bf764032d347af708db1cd34dd2a9ceaa056d0c96ebcc06380", + "0xf90211a0532c4b817232369b476e2f512431248940bc2b62b268750eafb19725b1ea3f61a023d73bdeaa7d23f1ae3c7e45a961f6828a8c84da890802d9e76c4ad040f9dfa8a0b2b86151f708e0970051496defa3a21a9e38add3710dcdf3128f350eed7545b7a0c6f14ab140c8c849900d90e72430cc8ed9e05bb11c0bc1ef74653813fe184908a085382b28cc5d147da5be0ad99cf65ba09a70031f9ad6ddb5a1b8676adf620d27a05002c30689fcafbe153d3dd120d8e2ed2d867ed4dc38e29c488914cfb2d773e1a0061763ee76bdc20123bd89151b0d3311d3500ffe9502013f79147983497323a2a0b27cccc06c21e8ec8a41076e872360263864d2c2a033bff15cdc4c422f5efbc4a061d3b9c9582aaa05e4c0c4c5e269194ad2337465c88fe15721dce420872df88aa0bf0cf79d1546129128ee59aa52f987f0a92d4ee8b50d34178d49ccbc3b542fbda08dcb375e470c4b7e33e9b30a18c77557c3afaf29a3423ebc457193ffa4afafeda00f37ee5c7e78a14120c393f96aaa2eb77c7a2b290b3e390d868f901d7935b91ea0189ba4abb7a368dc21faa9009e56f08dbabbf2b169237b2c3343cdce84b9734ba001a06747b252f00070e5c6857db4d2bd984ac1e3ca8e2c1768a1b15c7caaa32aa06f68846bda765c1ebe37c4f460a5501ea49d32e2873e0333b1db3f8b11353345a046ee87393f4507c48fffeaefb60f86d39be7f3164101a254c978154bdf440e0680", + "0xf90211a01d3ed8ac26e69d88bf7c5ede07a54e9e58497b152539ca91e50a92287db07f0ea085e2443725c20842d26d4989ba1a56dfb13fbafbd95628dd6ea0ecfaebbd956ea06d11f90d431eabe083bde15c02ca665d6bd270102108105d1179aa3e350ea327a0c95a53dd831c6b4f71406d0549369d20a6a19831ee70cb2bbfce0089328c3ceba0b84a29614ca66250d59c07792808522acf7137ed2027f1a0148ffe1b467c5623a01abe3994fd1fa6276a00a2bf2db562a3418bcd6ef53c2daf62f899d95e31e15aa01b26905ddf68b2ec3ee23558c6c931f2d86436caeaf078d772b5f659e900f782a02b7f348cd686aa44c2dc0d8ab03dccc700728d2acbb645d84036ee5b26dcb4aaa0ed6662d9fcde2a8fd39bc7f26c8537bec1a6193955cd63d739cd6c8eeb5f2c35a090a7e073bb5df6de34707cb679c2ef359e66550c557abd28c8dbfec598a7a5b6a0d8899a7e2839d10b61c59546af49af8b723dab8ddcafc68ecdf4994d42b5badaa09aa471b32e1f8ac6b811137e6cc15245fcb4d8e40b6f27c0955e31ae3e428619a00e6f9245fc44c37708a41ccde7bb764b9e54b69f12c79c5c1ad9eedc21075d59a0d83ec73c7ef8bfea0448174f53a174ea07397d8ba9a55cd1589ca9e9a4d41b7da0b3ceb20f69ce1a99b8e110bb3c56213ed047167cf9b794b9ab0d5dd3227f9d62a0a9e3612f28c1cd13e9f1ac7b6e5f984afa226b2b6037ebb2f8d6fcc4505c7f1c80", + "0xf90211a0fd032ce4009ac0aa95170293240d6bef6cefd073fcf5d430e2a526410dab705aa09ed14f5069549d06740b6946a651b4bd90bff6ad44133b779fdcada5b621d690a0b833494b9e3f58f773aa5cec7645ceac4ebfdcd985f234d33eb283415a14cdd4a0c2e1ff80fdcff52539b161fb2ce25cd2825bc61453e63f72347d5be029441ed6a082cb4d21a71f220cc1fc05e6d544a79a102c296a0a30634006757aa9dcf0dacba03930cac361c2eb6edbbe3cb453c1d301aedb25bfc0c12b32adb56849545a8390a0bdbcc436e5eded0ca5daf6f37ea2626d3977f745245190a62d24f0fe4e12b8eaa05ab7a229f5a628f7606948ff694c05a5a08f87e0e3c3ad1f45b747eec521c5eea0e9a0e18f800afc38bcabe3cbca21695a0d399b235285de2819791d88baa7c339a0bbb17f8b5dfbb24a4a15e6d606c54d67c103dd44f0f1246d6604e9b71a671866a048fe8956aa652d57f0322990f94c9df6af0f7c28c41776b1c80cc0eafa5ab458a01ed5fcbfd2efe7381e1f98081bc98896086615236cb8f7f7a44a04493286a0a0a020c202d8b2b22ed074692df8dd21df64105643b9a97d1fdd0ec235a59711909da0d8ac47fa535011dc28cd3f649f9391f881ba3822d05b4d25421a21ce3c50499ea025db02e0e16e04f2b3e69dacac680d3c8fbfcc5cadbf441fdf0f303f43b7aaa1a0f646b6d69c1fce8ad6fc064c1341da989a59c4157b5da5ff25b760f041707f0680", + "0xf90211a0a57894cce27679d5a469be71ae0c582159b51a71381f0365404bbe1b2af0d8d3a03243d088fb7f888a461e06d9339dda036d10fdc13fdc18d68680680117133cc8a0fe66886652b91264cb2e9e99cdae776928be462c0e76a1b2af3673de8c1e09f4a08a47de5ebfaa75fe79082627efa51108ad86235e6576c6dab5cad9f374dc3a33a0663fcc26c76d08a85004b4ca2e02baaf067ecbf1e0a121dc95be500cf541d56ea092f22428ae816de7a5a95e46ac3579d614e4c44d55be89d869a815a2e654e599a04adf75bc024ab9dc2bc280249a610422166f52c9fbc0bf9b401984b79e03211aa0d6ae689b2f5dabbc30e1e612cafc960fbb342f523617f7f6a8df1845a7564154a0bee5ca129557312716be21cf0e91273532e573ed162f0c8cdf0b1f1da38a30cca01aa1ca8998d04f89182c022aebcf6828d5d808ef4e663bf690e51aeb133ef1d5a0147cac105fa6c368d8a9013cd1873271ddacda2be2345051fdd3fa50a2c003c8a0caf1ff1075e0f30fb12b29e1278b61d69bf5cbd059131ba18f4b89422c188cd2a0fc0dc4f79e218c884075a69b52bfe84b8bbbb98e9e252299bda742ef77ee4264a05153dc26c8c558e394488fc70a45c0d863f2a930a215fb6fa0e1fdc92d990b31a010899ac22f43b3031f16cc9c660d7c5a93bf3ce5538a545056d9b1d364b06195a060e86b07bb6e96e4ea87eaeaa162108408bd5fadcc3496910c9fc59df5c0320480", + "0xf90191a0a1f0ab277fc1d351526eadeb8bcd95db08d710f0d3d1a0cc0cc8609d961a47d1a0c756a865d28d6bdcff435c90f5bf251107c230ad7558f57583d14be37e1ccaafa0828901d38fd39ade9bd74cbe07996e8cc45f8c43c6637fe7b7a43ab467e52098a022e4fb3dc11ca5e38ed9527e4eb534162c878ac048ac428abba29633b607d6258080a018d60336d1d9723e0d43567745464372e11f9f7a146abf163b0f43b338c0d827a00b44f7040c54b2bd63b0741062a96495dc4b92f7fec136c8effbc213cfffd02b80a0b81fcd189fbc66780a932367f874cbd4c85fed1fd37af109acc052f8ba80e51d80a0bbbcbe635193161c1e53442c06d0d14ed1af96af105a9fdd46a493195d62c1bba00050d231707972af60f163aa425401abb860f6c04b770fe0983e2f0f90ad6254a0804a9abf1b69bd16b1f6dc7cbbbf7592cd93fdf785b9cc61b01816583d9cd928a0ad85c29c3adbf2291cead9df6602560b93e485af7da680697ef9d1703f6f91cea040134ec8f47e1c599c0b7b4d237b8d3349f026fcfbacf885673438ffd2e9c72e80", + "0xf851a01a5a4c757ab8653aad3a83cbb491142c776b91e252ffe849c253dab5d109819380808080808080a09fc998b2c0347275d9f98a9ac5bbb205387c04387672cda1a0360c4ef7cffb1a8080808080808080", + "0xf86d9d205eb14112905ead3a2b16b733af37911d729cec51f559baf570332371b84df84b8087cc756b8ad2e000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" + ], + "balance":"0xcc756b8ad2e000", + "codeHash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + "nonce":"0x0", + "storageHash":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421" + ,"storageProof":"" + }` + + accountProof := []string{ + "0xf90211a0c48ed6536f719a4e24c1195b099fd19f127448c0e2b4ad5729dcdcccb41a56b5a0a338e0f373eac20e4ad9528f9895da93762623350bb5defa693263ea6f9af4afa0304e41961a40be33e5213d3e583c0d30989e795b541c810e8456c12a7478c841a05b26d8545de569451cd3baee25ef6d525f6c27c89844b76ccebf99801708c1d9a069b7051e21066a40847fbe6e4217628f6979b1a361819c7ebe0980c063d684f5a06f5948452762bfe7061717765ab322a5e086ece9c4eddf442a047e253b557dc4a0ef37b2aaf540e598e0e046ee6a8c51c1c100d68dcda70bea1ebe4924ef7911f3a03218af1883a1bfb9828b0c3f07f16a8df859e81543633808ec625164db6b3644a0121b6fdd1951f7915bd55c96dc2cc546fa42bfadb4cfa3fc1a2cc3de232df289a0fdf5a771f4ee044d9c6a7ddd6d797ca102022d4786db1332c5530091f52a2238a08e59837befa581ee0e4d2d9b2caeac403106090117844590d876df445df09509a0d0eb738155e240e6c069c765102f526b54124d788c8e501ed96a6a29a326f4d7a0c14d799f056ca9d6bf91a10272dff64cf34eb4aff42e7b56bf14176e2e36a58aa03ae0cf5dc0b96aa9755207ec060c45b149e071f1453e7416acd282660a66a371a0495a02815d4434895bf7997771016ab2bf29c1f970c778c696444d4b5573ae87a008d340cb14293c576f934b7f8cbab6f0b07f6d4804957d6f8d6c48e2465d027f80", + "0xf90211a0a0b5e06bee86e09cc7f63d64ef1f3bd9a970ba8881d52b9a1a60849eef16b535a022756912563afa1c6cc7a49d0f34704f5919df048091561e21511bff585fc6d2a0eaa5bd3c14a851aeaeff1a7dbafb5456d4a218f12c63f0795ba2679d8f412ba6a07f8b49a363a58d81b7c51eab08998532ff3efdec14e6015d43429180a5a74929a0e62a8be403d29d6972426603c4a0962a24d5c56325bf1661c0984414605eaa55a0d567f6a8520f25ed64ece7d37b405374a51c3af48356c6fa72efec22f7d42e2aa0696255c12473694333c6724ff234a04f6995b8eb376e8e8be0c171965767ca2ea0b1b08ecb635708f8c0f42dffe86e09ac6b3bfe8319e544797a4199bde4d1fd15a05c0e9906e3d20bbec170f769824250862fd7dad378d6cfeda9b713acf3b72568a03d160d95900a9bc2e7e9c08766b4392a9ac86b7e006979c426e0885229e5f44da00bc337a5b553d0ffb67859b28cc59246262bf532c2b14dd456f160d778540b34a005d784ba4f478f2f315f1b678fbfc39ed2c0b01b02b55ed82ea9d62369cd35faa023f24fd63f87a947ee148c8001bc4039a5f481fca1e0f3f10a3791a9e0451571a01ffb2845581388c6b915135a9ee856bcc896f2def7eeeb88bdfd53ae32323431a09d7bde3d951bcc3505684616bf760dfdd69e03e6b2ca2a180bc7b6fc7922d9c6a01b29670cbbe0bb02bf764032d347af708db1cd34dd2a9ceaa056d0c96ebcc06380", + "0xf90211a0532c4b817232369b476e2f512431248940bc2b62b268750eafb19725b1ea3f61a023d73bdeaa7d23f1ae3c7e45a961f6828a8c84da890802d9e76c4ad040f9dfa8a0b2b86151f708e0970051496defa3a21a9e38add3710dcdf3128f350eed7545b7a0c6f14ab140c8c849900d90e72430cc8ed9e05bb11c0bc1ef74653813fe184908a085382b28cc5d147da5be0ad99cf65ba09a70031f9ad6ddb5a1b8676adf620d27a05002c30689fcafbe153d3dd120d8e2ed2d867ed4dc38e29c488914cfb2d773e1a0061763ee76bdc20123bd89151b0d3311d3500ffe9502013f79147983497323a2a0b27cccc06c21e8ec8a41076e872360263864d2c2a033bff15cdc4c422f5efbc4a061d3b9c9582aaa05e4c0c4c5e269194ad2337465c88fe15721dce420872df88aa0bf0cf79d1546129128ee59aa52f987f0a92d4ee8b50d34178d49ccbc3b542fbda08dcb375e470c4b7e33e9b30a18c77557c3afaf29a3423ebc457193ffa4afafeda00f37ee5c7e78a14120c393f96aaa2eb77c7a2b290b3e390d868f901d7935b91ea0189ba4abb7a368dc21faa9009e56f08dbabbf2b169237b2c3343cdce84b9734ba001a06747b252f00070e5c6857db4d2bd984ac1e3ca8e2c1768a1b15c7caaa32aa06f68846bda765c1ebe37c4f460a5501ea49d32e2873e0333b1db3f8b11353345a046ee87393f4507c48fffeaefb60f86d39be7f3164101a254c978154bdf440e0680", + "0xf90211a01d3ed8ac26e69d88bf7c5ede07a54e9e58497b152539ca91e50a92287db07f0ea085e2443725c20842d26d4989ba1a56dfb13fbafbd95628dd6ea0ecfaebbd956ea06d11f90d431eabe083bde15c02ca665d6bd270102108105d1179aa3e350ea327a0c95a53dd831c6b4f71406d0549369d20a6a19831ee70cb2bbfce0089328c3ceba0b84a29614ca66250d59c07792808522acf7137ed2027f1a0148ffe1b467c5623a01abe3994fd1fa6276a00a2bf2db562a3418bcd6ef53c2daf62f899d95e31e15aa01b26905ddf68b2ec3ee23558c6c931f2d86436caeaf078d772b5f659e900f782a02b7f348cd686aa44c2dc0d8ab03dccc700728d2acbb645d84036ee5b26dcb4aaa0ed6662d9fcde2a8fd39bc7f26c8537bec1a6193955cd63d739cd6c8eeb5f2c35a090a7e073bb5df6de34707cb679c2ef359e66550c557abd28c8dbfec598a7a5b6a0d8899a7e2839d10b61c59546af49af8b723dab8ddcafc68ecdf4994d42b5badaa09aa471b32e1f8ac6b811137e6cc15245fcb4d8e40b6f27c0955e31ae3e428619a00e6f9245fc44c37708a41ccde7bb764b9e54b69f12c79c5c1ad9eedc21075d59a0d83ec73c7ef8bfea0448174f53a174ea07397d8ba9a55cd1589ca9e9a4d41b7da0b3ceb20f69ce1a99b8e110bb3c56213ed047167cf9b794b9ab0d5dd3227f9d62a0a9e3612f28c1cd13e9f1ac7b6e5f984afa226b2b6037ebb2f8d6fcc4505c7f1c80", + "0xf90211a0fd032ce4009ac0aa95170293240d6bef6cefd073fcf5d430e2a526410dab705aa09ed14f5069549d06740b6946a651b4bd90bff6ad44133b779fdcada5b621d690a0b833494b9e3f58f773aa5cec7645ceac4ebfdcd985f234d33eb283415a14cdd4a0c2e1ff80fdcff52539b161fb2ce25cd2825bc61453e63f72347d5be029441ed6a082cb4d21a71f220cc1fc05e6d544a79a102c296a0a30634006757aa9dcf0dacba03930cac361c2eb6edbbe3cb453c1d301aedb25bfc0c12b32adb56849545a8390a0bdbcc436e5eded0ca5daf6f37ea2626d3977f745245190a62d24f0fe4e12b8eaa05ab7a229f5a628f7606948ff694c05a5a08f87e0e3c3ad1f45b747eec521c5eea0e9a0e18f800afc38bcabe3cbca21695a0d399b235285de2819791d88baa7c339a0bbb17f8b5dfbb24a4a15e6d606c54d67c103dd44f0f1246d6604e9b71a671866a048fe8956aa652d57f0322990f94c9df6af0f7c28c41776b1c80cc0eafa5ab458a01ed5fcbfd2efe7381e1f98081bc98896086615236cb8f7f7a44a04493286a0a0a020c202d8b2b22ed074692df8dd21df64105643b9a97d1fdd0ec235a59711909da0d8ac47fa535011dc28cd3f649f9391f881ba3822d05b4d25421a21ce3c50499ea025db02e0e16e04f2b3e69dacac680d3c8fbfcc5cadbf441fdf0f303f43b7aaa1a0f646b6d69c1fce8ad6fc064c1341da989a59c4157b5da5ff25b760f041707f0680", + "0xf90211a0a57894cce27679d5a469be71ae0c582159b51a71381f0365404bbe1b2af0d8d3a03243d088fb7f888a461e06d9339dda036d10fdc13fdc18d68680680117133cc8a0fe66886652b91264cb2e9e99cdae776928be462c0e76a1b2af3673de8c1e09f4a08a47de5ebfaa75fe79082627efa51108ad86235e6576c6dab5cad9f374dc3a33a0663fcc26c76d08a85004b4ca2e02baaf067ecbf1e0a121dc95be500cf541d56ea092f22428ae816de7a5a95e46ac3579d614e4c44d55be89d869a815a2e654e599a04adf75bc024ab9dc2bc280249a610422166f52c9fbc0bf9b401984b79e03211aa0d6ae689b2f5dabbc30e1e612cafc960fbb342f523617f7f6a8df1845a7564154a0bee5ca129557312716be21cf0e91273532e573ed162f0c8cdf0b1f1da38a30cca01aa1ca8998d04f89182c022aebcf6828d5d808ef4e663bf690e51aeb133ef1d5a0147cac105fa6c368d8a9013cd1873271ddacda2be2345051fdd3fa50a2c003c8a0caf1ff1075e0f30fb12b29e1278b61d69bf5cbd059131ba18f4b89422c188cd2a0fc0dc4f79e218c884075a69b52bfe84b8bbbb98e9e252299bda742ef77ee4264a05153dc26c8c558e394488fc70a45c0d863f2a930a215fb6fa0e1fdc92d990b31a010899ac22f43b3031f16cc9c660d7c5a93bf3ce5538a545056d9b1d364b06195a060e86b07bb6e96e4ea87eaeaa162108408bd5fadcc3496910c9fc59df5c0320480", + "0xf90191a0a1f0ab277fc1d351526eadeb8bcd95db08d710f0d3d1a0cc0cc8609d961a47d1a0c756a865d28d6bdcff435c90f5bf251107c230ad7558f57583d14be37e1ccaafa0828901d38fd39ade9bd74cbe07996e8cc45f8c43c6637fe7b7a43ab467e52098a022e4fb3dc11ca5e38ed9527e4eb534162c878ac048ac428abba29633b607d6258080a018d60336d1d9723e0d43567745464372e11f9f7a146abf163b0f43b338c0d827a00b44f7040c54b2bd63b0741062a96495dc4b92f7fec136c8effbc213cfffd02b80a0b81fcd189fbc66780a932367f874cbd4c85fed1fd37af109acc052f8ba80e51d80a0bbbcbe635193161c1e53442c06d0d14ed1af96af105a9fdd46a493195d62c1bba00050d231707972af60f163aa425401abb860f6c04b770fe0983e2f0f90ad6254a0804a9abf1b69bd16b1f6dc7cbbbf7592cd93fdf785b9cc61b01816583d9cd928a0ad85c29c3adbf2291cead9df6602560b93e485af7da680697ef9d1703f6f91cea040134ec8f47e1c599c0b7b4d237b8d3349f026fcfbacf885673438ffd2e9c72e80", + "0xf851a01a5a4c757ab8653aad3a83cbb491142c776b91e252ffe849c253dab5d109819380808080808080a09fc998b2c0347275d9f98a9ac5bbb205387c04387672cda1a0360c4ef7cffb1a8080808080808080", + "0xf86d9d205eb14112905ead3a2b16b733af37911d729cec51f559baf570332371b84df84b8087cc756b8ad2e000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a0c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", + } + address := common.HexToAddress("0x457a22804cf255ee8c1b7628601c5682b3d70c71") + proof := [][]byte{} + + for _, s := range accountProof { + bytes1, _ := hex.DecodeString(s[2:]) + proof = append(proof, bytes1) + } + + stateRoot := "b666170175a47b5dde7514e732d935e58f7b379b0cf2774051c3fad5e9224000" + root, err := hex.DecodeString(stateRoot) + accountPath := crypto.Keccak256(address.Bytes()) + + assert.NoError(t, err, "Expected no error") + assert.Equal(t, root, keccak256(proof[0]), "Not Equal") + var testData Proof + + err = json.Unmarshal([]byte(jsonString), &testData) + assert.NoError(t, err, "Error in unmarshaling json string") + + proofStruct := EIP1186ProofResponse{ + Address: common.HexToAddress(testData.Address), + Balance: uint256.MustFromHex(testData.Balance), + CodeHash: common.HexToHash(testData.CodeHash), + Nonce: hexutil.Uint64(hexutil.MustDecodeUint64(testData.Nonce)), + StorageHash: common.HexToHash(testData.StorageHash), + AccountProof: func() []hexutil.Bytes { + var bytes []hexutil.Bytes + for _, h := range testData.AccountProof { + bytes = append(bytes, common.Hex2Bytes(h)) + } + return bytes + }(), + StorageProof: []StorageProof{}, + } + + value, err := EncodeAccount(&proofStruct) + assert.NoError(t, err, "Error in encoding account") + + isValid, err := VerifyProof(proof, root, accountPath, value) + assert.NoError(t, err, "Found Error") + assert.Equal(t, true, isValid, "ProofValidity not matching") +} diff --git a/execution/rpc.go b/execution/rpc.go index 8b2d9bf..a62924d 100644 --- a/execution/rpc.go +++ b/execution/rpc.go @@ -8,14 +8,15 @@ import ( "github.com/holiman/uint256" ) +// Changes have been made as HttpRpc was not satisfying ExecutionRpc type ExecutionRpc interface { - New(rpc *string) (*ExecutionRpc, error) + New(rpc *string) (ExecutionRpc, error) GetProof(address *seleneCommon.Address, slots *[]common.Hash, block uint64) (EIP1186ProofResponse, error) - CreateAccessList(opts CallOpts, block seleneCommon.BlockTag) (types.AccessList, error) + CreateAccessList(opts CallOpts, block seleneCommon.BlockTag) (AccessList, error) GetCode(address *seleneCommon.Address, block uint64) ([]byte, error) SendRawTransaction(bytes *[]byte) (common.Hash, error) GetTransactionReceipt(tx_hash *common.Hash) (types.Receipt, error) - GetTransaction(tx_hash *common.Hash) (types.Transaction, error) + GetTransaction(tx_hash *common.Hash) (seleneCommon.Transaction, error) GetLogs(filter *ethereum.FilterQuery) ([]types.Log, error) GetFilterChanges(filer_id *uint256.Int) ([]types.Log, error) UninstallFilter(filter_id *uint256.Int) (bool, error) diff --git a/execution/types.go b/execution/types.go index a735c9a..1dea5c0 100644 --- a/execution/types.go +++ b/execution/types.go @@ -3,7 +3,6 @@ package execution import ( "encoding/json" "fmt" - seleneCommon "github.com/BlocSoc-iitr/selene/common" "github.com/BlocSoc-iitr/selene/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -12,27 +11,36 @@ import ( "reflect" ) +type B256 = common.Hash type FeeHistory struct { BaseFeePerGas []hexutil.Big GasUsedRatio []float64 OldestBlock *hexutil.Big Reward [][]hexutil.Big } +type AccessListItem struct { + Address common.Address `json:"address"` + StorageKeys []B256 `json:"storageKeys"` +} +type AccessList struct { + AccessList []AccessListItem `json:"accessList"` + GasUsed hexutil.Bytes `json:"gasUsed"` +} -// defined storage proof and EIP1186ProofResponse structs +// This is to help in unmarshaling values from rpc response type StorageProof struct { - Key common.Hash - Proof []hexutil.Bytes - Value *uint256.Int + Key common.Hash `json:"key"` + Proof []hexutil.Bytes `json:"proof"` + Value *uint256.Int `json:"value"` } type EIP1186ProofResponse struct { - Address seleneCommon.Address - Balance *uint256.Int - CodeHash common.Hash - Nonce uint64 - StorageHash common.Hash - AccountProof []hexutil.Bytes - StorageProof []StorageProof + Address common.Address `json:"address"` + Balance *uint256.Int `json:"balance"` + CodeHash common.Hash `json:"codeHash"` + Nonce hexutil.Uint64 `json:"nonce"` + StorageHash common.Hash `json:"storageHash"` + AccountProof []hexutil.Bytes `json:"accountProof"` + StorageProof []StorageProof `json:"storageProof"` } type Account struct { Balance *big.Int @@ -40,7 +48,13 @@ type Account struct { CodeHash common.Hash Code []byte StorageHash common.Hash - Slots map[common.Hash]*big.Int + Slots []Slot +} + +// This is to help in unmarshaling values from rpc response +type Slot struct { + Key common.Hash // The key (slot) + Value *big.Int // The value (storage value) } type CallOpts struct { From *common.Address `json:"from,omitempty"` diff --git a/go.mod b/go.mod index 7fec929..fc8139a 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,6 @@ require ( github.com/protolambda/bls12-381-util v0.1.0 github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 - github.com/ugorji/go/codec v1.2.12 github.com/wealdtech/go-merkletree v1.0.0 golang.org/x/crypto v0.22.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 313ec91..699758a 100644 --- a/go.sum +++ b/go.sum @@ -365,8 +365,6 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= -github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/wealdtech/go-merkletree v1.0.0 h1:DsF1xMzj5rK3pSQM6mPv8jlyJyHXhFxpnA2bwEjMMBY= github.com/wealdtech/go-merkletree v1.0.0/go.mod h1:cdil512d/8ZC7Kx3bfrDvGMQXB25NTKbsm0rFrmDax4= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/utils/utils.go b/utils/utils.go index 42f4504..d90438c 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -2,7 +2,8 @@ package utils import ( "encoding/hex" - "strconv" + "strconv" + "net/url" "strings" "crypto/sha256" @@ -291,3 +292,7 @@ func coreVerify(pk *bls.Pubkey, message []byte, signature *bls.Signature) bool { // 9. If C1 != C2, return INVALID return !eng.Check() } +func IsURL(str string) bool { + u, err := url.Parse(str) + return err == nil && u.Scheme != "" && u.Host != "" +}