Contexto de Ejecución
En el corazón palpitante de cada ejecución de contrato inteligente en KodeChain reside el ExecutionContext, una estructura rica y dinámica implementada en vm/core/context.go que mantiene todo el estado necesario para que un contrato cobre vida. Este contexto no es solo un contenedor de datos; es el alma misma de la ejecución determinista.
Arquitectura del Contexto
ExecutionContext: El Núcleo de la Ejecución
type ExecutionContext struct {
// Contract information
ContractAddress string
Caller string
Origin string
Value *big.Int
GasLimit uint64
// Transaction information
TxHash string
// Block information
BlockNumber uint64
BlockTimestamp int64
BlockHash string
BlockDifficulty *big.Int
Coinbase string
// Execution state
PC uint64
Gas uint64
GasUsed uint64
Stack *Stack
Memory *Memory
Storage *Storage
ReturnData []byte
CallDepth int
IsStatic bool
HasReverted bool
ExecutionTime time.Duration
// Input/Output
Input []byte
Output []byte
// Logs
Logs []Log
// Consensus type
ConsensusType string
}
Esta estructura comprehensiva captura todos los aspectos de la ejecución de un contrato inteligente.
Información del Contrato
Identidad y Origen
ContractAddress string // Dirección del contrato ejecutándose
Caller string // Quién llama al contrato
Origin string // Origen original de la transacción
Value *big.Int // Valor enviado con la llamada
Estos campos establecen la identidad del contrato y el contexto de la llamada, cruciales para la lógica de autorización y el seguimiento de fondos.
Límites de Gas
GasLimit uint64 // Límite máximo de gas
Gas uint64 // Gas restante
GasUsed uint64 // Gas consumido
El gas controla los recursos computacionales, previniendo bucles infinitos y ataques DoS.
Información del Bloque
Contexto Temporal
BlockNumber uint64 // Número del bloque actual
BlockTimestamp int64 // Timestamp del bloque
BlockHash string // Hash del bloque
BlockDifficulty *big.Int // Dificultad (0 para PoS/PBFT)
Coinbase string // Dirección del validador
Esta información conecta la ejecución del contrato con el estado global de la blockchain.
Estado de Ejecución
Máquina Virtual Interna
PC uint64 // Program Counter
Stack *Stack // Stack de 256 bits
Memory *Memory // Memoria expandible
Storage *Storage // Storage persistente
Estos componentes forman la máquina virtual interna que ejecuta el bytecode.
Control de Flujo
CallDepth int // Profundidad de llamadas
IsStatic bool // Llamada estática (solo lectura)
HasReverted bool // Si la ejecución revirtió
ExecutionTime time.Duration // Tiempo de ejecución
Estos campos controlan el flujo de ejecución y previenen ataques recursivos.
Entrada y Salida
Datos de la Llamada
Input []byte // Datos de entrada (calldata)
Output []byte // Datos de salida (returndata)
Input contiene los parámetros de la función llamada, Output contiene los resultados.
Sistema de Logs
Eventos Emitidos
type Log struct {
Address string
Topics []string
Data []byte
}
Los logs permiten que los contratos emitan eventos indexados que pueden ser consultados por aplicaciones externas.
Gestión de Gas
Consumo Controlado
func (ctx *ExecutionContext) ConsumeGas(amount uint64) error {
if ctx.Gas < amount {
return ErrOutOfGas
}
ctx.Gas -= amount
ctx.GasUsed += amount
return nil
}
Cada operación consume gas, asegurando que la ejecución tenga límites computacionales.
Reembolso de Gas
func (ctx *ExecutionContext) RefundGas(amount uint64) {
ctx.Gas += amount
if ctx.GasUsed >= amount {
ctx.GasUsed -= amount
} else {
ctx.GasUsed = 0
}
}
Operaciones eficientes pueden recibir reembolso de gas.
Configuración del Bloque
Información Contextual
func (ctx *ExecutionContext) SetBlockInfo(number uint64, timestamp int64, hash, coinbase string) {
ctx.BlockNumber = number
ctx.BlockTimestamp = timestamp
ctx.BlockHash = hash
ctx.Coinbase = coinbase
}
Esta función configura el contexto con información del bloque actual.
Profundidad de Llamadas
Prevención de Recursión Infinita
func (ctx *ExecutionContext) IncrementCallDepth() error {
if ctx.CallDepth >= MaxCallDepth {
return ErrCallDepthExceeded
}
ctx.CallDepth++
return nil
}
Límite de 1024 llamadas anidadas previene ataques de recursión.
Reset y Clonación
Preparación para Nueva Ejecución
func (ctx *ExecutionContext) Reset() {
ctx.PC = 0
ctx.Gas = ctx.GasLimit
ctx.GasUsed = 0
ctx.Stack.Reset()
ctx.Memory.Reset()
ctx.ReturnData = []byte{}
ctx.Logs = make([]Log, 0)
ctx.HasReverted = false
ctx.CallDepth = 0
}
Reset prepara el contexto para una nueva ejecución, limpiando todo el estado efímero.
Clonación para Sub-llamadas
func (ctx *ExecutionContext) Clone() *ExecutionContext {
newCtx := &ExecutionContext{
ContractAddress: ctx.ContractAddress,
Caller: ctx.Caller,
Origin: ctx.Origin,
Value: new(big.Int).Set(ctx.Value),
GasLimit: ctx.Gas,
GasPrice: new(big.Int).Set(ctx.GasPrice),
// ... copiar otros campos
Stack: NewStack(),
Memory: NewMemory(),
Storage: ctx.Storage.Copy(),
Logs: make([]Log, 0),
CallDepth: ctx.CallDepth + 1,
}
return newCtx
}
Clone crea un contexto independiente para llamadas anidadas, manteniendo el estado del storage padre.
Sistema de Eventos
Emisión de Logs
func (ctx *ExecutionContext) AddLog(topics []string, data []byte) error {
if ctx.IsStatic {
return ErrWriteProtection
}
log := Log{
Address: ctx.ContractAddress,
Topics: topics,
Data: data,
}
ctx.Logs = append(ctx.Logs, log)
return nil
}
Los contratos pueden emitir eventos que son indexados y consultables.
Integración con la VM
Ciclo de Ejecución
func (vm *VM) Execute(bytecode []byte, input []byte) ([]byte, error) {
ctx := NewExecutionContext(contractAddr, caller, origin, value, gasLimit)
ctx.Input = input
for ctx.PC < uint64(len(bytecode)) {
opcode := bytecode[ctx.PC]
ctx.PC++
if err := vm.executeOpcode(ctx, opcode); err != nil {
return nil, err
}
}
return ctx.Output, nil
}
El contexto guía toda la ejecución, manteniendo el estado consistente.
Seguridad por Diseño
Aislamiento Completo
Cada ejecución tiene su propio contexto, previniendo interferencias entre contratos.
Validaciones Rigurosas
- Gas suficiente antes de cada operación
- Límites de memoria y stack
- Profundidad de llamadas controlada
- Protección contra escritura en llamadas estáticas
Determinismo Garantizado
Mismas entradas producen mismos resultados, esencial para la consistencia blockchain.
El Rol del Contexto
Puente entre Mundo y Contrato
El ExecutionContext sirve como puente entre:
- El mundo externo: Transacciones, bloques, usuarios
- El mundo interno: Stack, memoria, storage del contrato
Estado de Máquina Virtual
Mantiene el estado completo de la máquina virtual durante la ejecución:
- Contadores de programa
- Recursos disponibles
- Estado de componentes
- Resultados de operaciones
Contexto de Seguridad
Proporciona el contexto necesario para validaciones de seguridad:
- Autorización de operaciones
- Límites de recursos
- Prevención de ataques
Filosofía de Diseño
Riqueza vs Simplicidad
El contexto es rico en información pero simple en uso, proporcionando todo lo necesario sin complejidad innecesaria.
Eficiencia en Memoria
Campos lazy-loaded y estructuras optimizadas para minimizar uso de memoria.
Extensibilidad
Diseño modular permite agregar nuevos campos sin romper compatibilidad.
El Arte del Estado
Cada ExecutionContext representa un momento congelado en el tiempo de ejecución de un contrato inteligente. Es el estado vivo que transforma código estático en ejecución dinámica, el puente entre la intención del desarrollador y la realidad de la blockchain.
En KodeChain, el contexto de ejecución no es solo una estructura de datos; es el alma misma de la computación distribuida, el guardián del estado que hace posible la magia de los contratos inteligentes.