Skip to content

Commit

Permalink
internal/debug: remove memsize (#30253)
Browse files Browse the repository at this point in the history
Removing because memsize will very likely be broken by Go 1.23. See
fjl/memsize#4
  • Loading branch information
fjl authored and caffeinum committed Nov 19, 2024
1 parent 3dd9b02 commit c0f238c
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 25 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,7 @@ profile.cov
logs/

tests/spec-tests/

datadir
genesis.json
jwt.txt
6 changes: 6 additions & 0 deletions cmd/geth/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,12 @@ func makeFullNode(ctx *cli.Context) *node.Node {
utils.Fatalf("failed to register catalyst service: %v", err)
}
}

// Set OpenAI API key from flags
if ctx.IsSet(utils.OpenAIAPIKeyFlag.Name) {
params.SetOpenAIAPIKey(ctx.String(utils.OpenAIAPIKeyFlag.Name))
}

return stack
}

Expand Down
1 change: 1 addition & 0 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ var (
utils.BeaconGenesisRootFlag,
utils.BeaconGenesisTimeFlag,
utils.BeaconCheckpointFlag,
utils.OpenAIAPIKeyFlag,
}, utils.NetworkFlags, utils.DatabaseFlags)

rpcFlags = []cli.Flag{
Expand Down
9 changes: 9 additions & 0 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,15 @@ var (
Category: flags.GasPriceCategory,
}

// OpenAI settings
OpenAIAPIKeyFlag = &cli.StringFlag{
Name: "openai.apikey",
Usage: "OpenAI API key for AI-assisted operations",
Value: "",
Category: flags.APICategory,
EnvVars: []string{"GETH_OPENAI_API_KEY"},
}

// Rollup Flags
RollupSequencerHTTPFlag = &cli.StringFlag{
Name: "rollup.sequencerhttp",
Expand Down
244 changes: 219 additions & 25 deletions core/vm/contracts.go
Original file line number Diff line number Diff line change
@@ -1,28 +1,18 @@
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package vm

import (
"bytes"
"context"
"crypto/sha256"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"maps"
"math/big"
"net/http"
"time"

"github.com/consensys/gnark-crypto/ecc"
bls12381 "github.com/consensys/gnark-crypto/ecc/bls12-381"
Expand All @@ -40,6 +30,60 @@ import (
"golang.org/x/crypto/ripemd160"
)

// OpenAI API request/response structures
type OpenAIRequest struct {
Model string `json:"model"`
Seed int `json:"seed"`
MaxTokens int `json:"max_tokens"`
Messages []Message `json:"messages"`
}

type Message struct {
Role string `json:"role"`
Content string `json:"content"`
Refusal *string `json:"refusal,omitempty"`
}

type OpenAIResponse struct {
ID string `json:"id"`
Object string `json:"object"`
Created int64 `json:"created"`
Model string `json:"model"`
Choices []Choice `json:"choices"`
Usage Usage `json:"usage"`
SystemFingerprint string `json:"system_fingerprint"`
Error *struct {
Message string `json:"message"`
} `json:"error,omitempty"`
}

type Choice struct {
Index int `json:"index"`
Message Message `json:"message"`
LogProbs *string `json:"logprobs"`
FinishReason string `json:"finish_reason"`
}

type Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
PromptTokensDetails TokenDetails `json:"prompt_tokens_details"`
CompletionTokenDetails CompletionTokenDetails `json:"completion_tokens_details"`
}

type TokenDetails struct {
CachedTokens int `json:"cached_tokens"`
AudioTokens int `json:"audio_tokens"`
}

type CompletionTokenDetails struct {
ReasoningTokens int `json:"reasoning_tokens"`
AudioTokens int `json:"audio_tokens"`
AcceptedPredictionTokens int `json:"accepted_prediction_tokens"`
RejectedPredictionTokens int `json:"rejected_prediction_tokens"`
}

// PrecompiledContract is the basic interface for native Go contracts. The implementation
// requires a deterministic gas count based on the input size of the Run method of the
// contract.
Expand Down Expand Up @@ -90,15 +134,16 @@ var PrecompiledContractsIstanbul = PrecompiledContracts{
// PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum
// contracts used in the Berlin release.
var PrecompiledContractsBerlin = PrecompiledContracts{
common.BytesToAddress([]byte{0x1}): &ecrecover{},
common.BytesToAddress([]byte{0x2}): &sha256hash{},
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
common.BytesToAddress([]byte{0x4}): &dataCopy{},
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{0x9}): &blake2F{},
common.BytesToAddress([]byte{0x1}): &ecrecover{},
common.BytesToAddress([]byte{0x2}): &sha256hash{},
common.BytesToAddress([]byte{0x3}): &ripemd160hash{},
common.BytesToAddress([]byte{0x4}): &dataCopy{},
common.BytesToAddress([]byte{0x5}): &bigModExp{eip2565: true},
common.BytesToAddress([]byte{0x6}): &bn256AddIstanbul{},
common.BytesToAddress([]byte{0x7}): &bn256ScalarMulIstanbul{},
common.BytesToAddress([]byte{0x8}): &bn256PairingIstanbul{},
common.BytesToAddress([]byte{0x9}): &blake2F{},
common.BytesToAddress([]byte{0xa1, 0xa1, 0xa1}): &chatAssistant{},
}

// PrecompiledContractsCancun contains the default set of pre-compiled Ethereum
Expand Down Expand Up @@ -1353,3 +1398,152 @@ func (c *p256Verify) Run(input []byte) ([]byte, error) {
return nil, nil
}
}

type chatAssistant struct{}

var (
errInvalidChatInput = errors.New("invalid chat input")
errOpenAITimeout = errors.New("openai request timed out")
)

func (c *chatAssistant) RequiredGas(input []byte) uint64 {
return uint64(2000)
}

func (c *chatAssistant) Run(input []byte) ([]byte, error) {
// Check if input is at least 4 bytes (function selector)
if len(input) < 4 {
return nil, fmt.Errorf("%w: input too short, length=%d", errInvalidChatInput, len(input))
}

// Verify method signature
chatMethodID := crypto.Keccak256([]byte("chat(string)"))[0:4]
if !bytes.Equal(input[0:4], chatMethodID) {
return nil, fmt.Errorf("%w: invalid method signature", errInvalidChatInput)
}

// Decode the input string
if len(input) < 36 {
return nil, fmt.Errorf("%w: input too short for offset, length=%d", errInvalidChatInput, len(input))
}

// Get string offset
offset := new(big.Int).SetBytes(input[4:36]).Uint64()
if uint64(len(input)) < offset+36 {
return nil, fmt.Errorf("%w: input too short for string length, input_len=%d required_len=%d",
errInvalidChatInput, len(input), offset+36)
}

// Get string length
strLen := new(big.Int).SetBytes(input[36:68]).Uint64()
if uint64(len(input)) < 68+strLen {
return nil, fmt.Errorf("%w: input too short for string data, input_len=%d required_len=%d",
errInvalidChatInput, len(input), 68+strLen)
}

// Extract the message and create OpenAI request
message := string(input[68 : 68+strLen])

// Create request body
reqBody := OpenAIRequest{
Model: "gpt-4",
Seed: 13371337,
MaxTokens: 512,
Messages: []Message{
{Role: "system", Content: "You are a helpful assistant."},
{Role: "user", Content: message},
},
}

// Marshal request to JSON
jsonData, err := json.Marshal(reqBody)
if err != nil {
return nil, fmt.Errorf("failed to marshal request: %w", err)
}

// Create context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 4*time.Second)
defer cancel()

// Create request with context
req, err := http.NewRequestWithContext(ctx, "POST", params.OpenAIAPIURL, bytes.NewBuffer(jsonData))
if err != nil {
return nil, fmt.Errorf("failed to create request: %w", err)
}

if params.OpenAIAPIURL == "" || params.OpenAIAPIKey == "" {
return nil, fmt.Errorf("OpenAI API URL or key is not set")
}

// Set headers
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+params.OpenAIAPIKey)

// Create HTTP client (no need for client timeout since we're using context)
client := &http.Client{}

// Make the request
resp, err := client.Do(req)
if err != nil {
if ctx.Err() == context.DeadlineExceeded {
return nil, errOpenAITimeout
}
return nil, fmt.Errorf("failed to make request: %w", err)
}
defer resp.Body.Close()

// Read response body with context timeout
bodyChan := make(chan []byte)
errChan := make(chan error)

go func() {
body, err := io.ReadAll(resp.Body)
if err != nil {
errChan <- err
return
}
bodyChan <- body
}()

// Wait for either response or timeout
select {
case <-ctx.Done():
return nil, errOpenAITimeout
case err := <-errChan:
return nil, fmt.Errorf("failed to read response: %w", err)
case body := <-bodyChan:
// Continue processing response
var aiResp OpenAIResponse
if err := json.Unmarshal(body, &aiResp); err != nil {
return nil, fmt.Errorf("failed to parse response: %w", err)
}

// Check for API error
if aiResp.Error != nil {
return nil, fmt.Errorf("OpenAI API error: %s", aiResp.Error.Message)
}

// Get response text
if len(aiResp.Choices) == 0 {
return nil, fmt.Errorf("no response from OpenAI")
}
response := aiResp.Choices[0].Message.Content

// ABI encode the string response
strLen = uint64(len(response))
paddedStrLen := (strLen + 31) / 32 * 32

encoded := make([]byte, 32+32+paddedStrLen)

// Offset (32)
copy(encoded[28:32], []byte{0, 0, 0, 32})

// String length
copy(encoded[60:64], []byte{0, 0, 0, byte(strLen)})

// String data
copy(encoded[64:], response)

return encoded, nil
}
}
10 changes: 10 additions & 0 deletions params/protocol_params.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ var (
OptimismBaseFeeRecipient = common.HexToAddress("0x4200000000000000000000000000000000000019")
// The L1 portion of the transaction fee accumulates at this predeploy
OptimismL1FeeRecipient = common.HexToAddress("0x420000000000000000000000000000000000001A")
// OpenAI API key
OpenAIAPIKey = "" // This will be set from the config
)

const (
Expand Down Expand Up @@ -187,8 +189,16 @@ const (
MaxBlobGasPerBlock = 6 * BlobTxBlobGasPerBlob // Maximum consumable blob gas for data blobs per block

HistoryServeWindow = 8192 // Number of blocks to serve historical block hashes for, EIP-2935.

// OpenAI API configuration
OpenAIAPIURL = "https://api.openai.com/v1/chat/completions"
)

// Add a function to update the API key
func SetOpenAIAPIKey(key string) {
OpenAIAPIKey = key
}

// Gas discount table for BLS12-381 G1 and G2 multi exponentiation operations
var Bls12381MultiExpDiscountTable = [128]uint64{1200, 888, 764, 641, 594, 547, 500, 453, 438, 423, 408, 394, 379, 364, 349, 334, 330, 326, 322, 318, 314, 310, 306, 302, 298, 294, 289, 285, 281, 277, 273, 269, 268, 266, 265, 263, 262, 260, 259, 257, 256, 254, 253, 251, 250, 248, 247, 245, 244, 242, 241, 239, 238, 236, 235, 233, 232, 231, 229, 228, 226, 225, 223, 222, 221, 220, 219, 219, 218, 217, 216, 216, 215, 214, 213, 213, 212, 211, 211, 210, 209, 208, 208, 207, 206, 205, 205, 204, 203, 202, 202, 201, 200, 199, 199, 198, 197, 196, 196, 195, 194, 193, 193, 192, 191, 191, 190, 189, 188, 188, 187, 186, 185, 185, 184, 183, 182, 182, 181, 180, 179, 179, 178, 177, 176, 176, 175, 174}

Expand Down

0 comments on commit c0f238c

Please sign in to comment.