Interperter.go

package vm

import (
	"fmt"

	"saseul/config"
	"saseul/data/resourcechain"
	"saseul/data/status"
	"saseul/model"
	"saseul/util/hasher"
	"saseul/util/signer"
)

type Interpreter struct {
	mode             string
	signedData       *model.SignedData
	code             *model.Method
	postProcess      *model.Method
	breakFlag        bool
	result           string
	weight           int
	methods          map[string]func([]interface{}) interface{}
	state            string
	process          string
	universals       map[string]interface{}
	locals           map[string]interface{}
	universalUpdates map[string]map[string]interface{}
	localUpdates     map[string]map[string]interface{}
}

func NewInterpreter() *Interpreter {
	return &Interpreter{
		methods:          make(map[string]func([]interface{}) interface{}),
		universals:       make(map[string]interface{}),
		locals:           make(map[string]interface{}),
		universalUpdates: make(map[string]map[string]interface{}),
		localUpdates:     make(map[string]map[string]interface{}),
	}
}

// Reset ๋ฉ”์„œ๋“œ๋Š” ์ธํ„ฐํ”„๋ฆฌํ„ฐ์˜ ์ƒํƒœ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) Reset(all bool) {
	i.signedData = nil
	i.code = nil
	i.postProcess = nil
	i.breakFlag = false
	i.result = ""
	i.weight = 0

	if all {
		i.state = ""
		i.process = ""
		i.universals = make(map[string]interface{})
		i.locals = make(map[string]interface{})
	}

	i.universalUpdates = make(map[string]map[string]interface{})
	i.localUpdates = make(map[string]map[string]interface{})
}

// Init ๋ฉ”์„œ๋“œ๋Š” ์ธํ„ฐํ”„๋ฆฌํ„ฐ์˜ ๋ชจ๋“œ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ , ํ•ด๋‹น ๋ชจ๋“œ์— ํ•„์š”ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) Init(mode string) {
	if i.mode != mode {
		i.mode = mode
		i.methods = make(map[string]func([]interface{}) interface{})

		i.loadMethod("BasicOperator")
		i.loadMethod("ArithmeticOperator")
		i.loadMethod("ComparisonOperator")
		i.loadMethod("UtilOperator")
		i.loadMethod("CastOperator")
		i.loadMethod("ReadOperator")

		if i.mode == "transaction" {
			i.loadMethod("WriteOperator")
		} else {
			i.loadMethod("ChainOperator")
		}
	}
}

// LoadMethod ๋ฉ”์„œ๋“œ๋Š” ์ฃผ์–ด์ง„ ํŠธ๋ ˆ์ดํŠธ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ธํ„ฐํ”„๋ฆฌํ„ฐ์— ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) LoadMethod(traitName string) {
	// ํŠธ๋ ˆ์ดํŠธ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ๋กœ๋“œํ•˜๋Š” ๋กœ์ง์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
	// ์˜ˆ๋ฅผ ๋“ค์–ด reflect ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
}

// Set ๋ฉ”์„œ๋“œ๋Š” ์ธํ„ฐํ”„๋ฆฌํ„ฐ์— ๋ฐ์ดํ„ฐ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) Set(data *model.SignedData, code *model.Method, postProcess *model.Method) {
	i.signedData = data
	i.code = code
	i.postProcess = postProcess
	i.breakFlag = false
	i.weight = 0
	i.result = "Conditional Error"
	i.setDefaultValue()
}

// Process ๋ฉ”์„œ๋“œ๋Š” ์ฃผ์–ด์ง„ ABI๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) Process(abi interface{}) interface{} {
	if abiArray, ok := abi.([]interface{}); ok {
		for key, item := range abiArray {
			prefix := string(key[0])
			method := key[1:]
			vars := i.Process(item)

			if prefix == "$" && i.methods[method] != nil && isArray(vars) {
				return i.methods[method].(func([]interface{}) interface{})(vars.([]interface{}))
			} else {
				abiArray[key] = vars
			}
		}
		return abiArray
	}
	return abi
}

// setDefaultValue ๋ฉ”์„œ๋“œ๋Š” ๊ธฐ๋ณธ ๊ฐ’์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) setDefaultValue() {
	if i.signedData.Attributes("version") == nil {
		i.signedData.Attributes("version", config.Version)
	}

	for name, parameter := range i.code.Parameters() {
		requirements := parameter["requirements"].(bool)
		if !requirements && i.signedData.Attributes(name) == nil {
			defaultValue := parameter["default"]
			i.signedData.Attributes(name, defaultValue)
		}
	}

	if i.mode == "transaction" {
		if i.signedData.Attributes("from") == nil {
			i.signedData.Attributes("from", signer.Address(i.signedData.PublicKey))
		}
		if i.signedData.Attributes("hash") == nil {
			i.signedData.Attributes("hash", i.signedData.Hash)
		}
		if i.signedData.Attributes("size") == nil {
			i.signedData.Attributes("size", i.signedData.Size())
		}
		i.weight += i.signedData.Attributes("size").(int)
	}
}

// ParameterValidity ๋ฉ”์„œ๋“œ๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) ParameterValidity(errMsg *string) bool {
	if i.mode == "transaction" {
		from := i.signedData.Attributes("from").(string)
		if from != signer.Address(i.signedData.PublicKey) {
			*errMsg = fmt.Sprintf("Invalid from address: %s", from)
			return false
		}
	}

	for _, parameter := range i.code.Parameters() {
		// parameter ๊ฐ์ฒด์˜ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ ๋กœ์ง์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
	}

	return true
}

// Read ๋ฉ”์„œ๋“œ๋Š” ์ƒํƒœ๋ฅผ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค.
func (i *Interpreter) Read() {
	i.state = "READ"
	i.process = "MAIN"

	for _, execution := range i.code.Executions() {
		i.Process(execution)
	}

	i.process = "POST"

	for _, execution := range i.postProcess.Executions() {
		i.Process(execution)
	}
}

// Execute ๋ฉ”์„œ๋“œ๋Š” ์ƒํƒœ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) Execute(result *string) bool {
	executions := i.code.Executions()
	postExecutions := i.postProcess.Executions()

	i.state = "CONDITION"
	i.process = "MAIN"

	for key, execution := range executions {
		executions[key] = i.Process(execution)

		if i.breakFlag {
			*result = i.result
			return false
		}
	}

	processLength := len(hasher.String(executions))

	switch i.mode {
	case "transaction":
		if processLength > config.TxSizeLimit {
			*result = "Too long processing."
			return false
		}
	default:
		if processLength > config.BlockTxSizeLimit {
			*result = "Too long processing."
			return false
		}
	}

	i.process = "POST"

	for key, execution := range postExecutions {
		postExecutions[key] = i.Process(execution)

		if i.breakFlag {
			*result = i.result
			return false
		}
	}

	i.state = "EXECUTION"
	i.process = "MAIN"

	for key, execution := range executions {
		executions[key] = i.Process(execution)

		if i.breakFlag {
			*result = i.result
			return true
		}
	}

	i.process = "POST"

	for key, execution := range postExecutions {
		postExecutions[key] = i.Process(execution)

		if i.breakFlag {
			*result = i.result
			return true
		}
	}

	return true
}

// LoadLocalStatus ๋ฉ”์„œ๋“œ๋Š” ๋กœ์ปฌ ์ƒํƒœ๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) LoadLocalStatus() {
	if len(i.locals) > 0 {
		keys := make([]string, 0, len(i.locals))
		for key := range i.locals {
			keys = append(keys, key)
		}
		i.locals = status.Instance().LocalStatuses(keys)
	}
}

// LoadUniversalStatus ๋ฉ”์„œ๋“œ๋Š” ๋ฒ”์šฉ ์ƒํƒœ๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) LoadUniversalStatus() {
	if len(i.universals) > 0 {
		keys := make([]string, 0, len(i.universals))
		for key := range i.universals {
			keys = append(keys, key)
		}
		i.universals = status.Instance().UniversalStatuses(keys)
	}
}

// LoadMinerStatus ๋ฉ”์„œ๋“œ๋Š” ๋งˆ์ด๋„ˆ ์ƒํƒœ๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) LoadMinerStatus(confirmedHeight int, miners []string) {
	calculatedHeight := i.GetLocalStatus(config.CalculatedHeightHash(), 0).(int)

	if confirmedHeight <= calculatedHeight {
		return
	}

	for _, miner := range miners {
		i.AddUniversalLoads(config.ResourceHash(miner))
	}

	block := resourcechain.Instance().Block(calculatedHeight + 1)
	for _, receipt := range block.Receipts {
		i.AddUniversalLoads(config.ResourceHash(receipt.Beneficiary))
	}
}

// AddLocalLoads ๋ฉ”์„œ๋“œ๋Š” ๋กœ์ปฌ ์ƒํƒœ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) AddLocalLoads(statusHash string) {
	statusHash = hasher.FillHash(statusHash)
	if _, exists := i.locals[statusHash]; !exists {
		i.locals[statusHash] = nil
	}
}

// AddUniversalLoads ๋ฉ”์„œ๋“œ๋Š” ๋ฒ”์šฉ ์ƒํƒœ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) AddUniversalLoads(statusHash string) {
	statusHash = hasher.FillHash(statusHash)
	if _, exists := i.universals[statusHash]; !exists {
		i.universals[statusHash] = nil
	}
}

// SetLocalLoads ๋ฉ”์„œ๋“œ๋Š” ๋กœ์ปฌ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) SetLocalLoads(statusHash string, value interface{}) {
	statusHash = hasher.FillHash(statusHash)
	i.locals[statusHash] = value
}

// SetUniversalLoads ๋ฉ”์„œ๋“œ๋Š” ๋ฒ”์šฉ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) SetUniversalLoads(statusHash string, value interface{}) {
	statusHash = hasher.FillHash(statusHash)
	i.universals[statusHash] = value
}

// GetLocalStatus ๋ฉ”์„œ๋“œ๋Š” ๋กœ์ปฌ ์ƒํƒœ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
func (i *Interpreter) GetLocalStatus(statusHash string, defaultValue interface{}) interface{} {
	statusHash = hasher.FillHash(statusHash)
	if value, exists := i.locals[statusHash]; exists {
		return value
	}
	return defaultValue
}

// GetUniversalStatus ๋ฉ”์„œ๋“œ๋Š” ๋ฒ”์šฉ ์ƒํƒœ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.
func (i *Interpreter) GetUniversalStatus(statusHash string, defaultValue interface{}) interface{} {
	statusHash = hasher.FillHash(statusHash)
	if value, exists := i.universals[statusHash]; exists {
		return value
	}
	return defaultValue
}

// SetUniversalStatus ๋ฉ”์„œ๋“œ๋Š” ๋ฒ”์šฉ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) SetUniversalStatus(statusHash string, value interface{}) bool {
	if updates, exists := i.universalUpdates[statusHash]; exists {
		updates["new"] = value
	} else {
		i.universalUpdates[statusHash] = map[string]interface{}{
			"old": i.GetUniversalStatus(statusHash, nil),
			"new": value,
		}
	}

	statusHash = hasher.FillHash(statusHash)
	i.universals[statusHash] = value
	return true
}

// SetLocalStatus ๋ฉ”์„œ๋“œ๋Š” ๋กœ์ปฌ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
func (i *Interpreter) SetLocalStatus(statusHash string, value interface{}) bool {
	if updates, exists := i.localUpdates[statusHash]; exists {
		updates["new"] = value
	} else {
		i.localUpdates[statusHash] = map[string]interface{}{
			"old": i.GetLocalStatus(statusHash, nil),
			"new": value,
		}
	}

	statusHash = hasher.FillHash(statusHash)
	i.locals[statusHash] = value
	return true
}

Interpreter ๊ตฌ์กฐ์ฒด

Interpreter ๊ตฌ์กฐ์ฒด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ•„๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค:

  • mode: ์ธํ„ฐํ”„๋ฆฌํ„ฐ์˜ ๋ชจ๋“œ (์˜ˆ: "transaction")

  • signedData: ์„œ๋ช…๋œ ๋ฐ์ดํ„ฐ

  • code: ์‹คํ–‰ํ•  ์ฝ”๋“œ

  • postProcess: ํ›„์ฒ˜๋ฆฌํ•  ์ฝ”๋“œ

  • breakFlag: ์‹คํ–‰ ์ค‘๋‹จ ํ”Œ๋ž˜๊ทธ

  • result: ์‹คํ–‰ ๊ฒฐ๊ณผ ๋ฉ”์‹œ์ง€

  • weight: ์‹คํ–‰ ๊ฐ€์ค‘์น˜

  • methods: ๋ฉ”์„œ๋“œ ๋งต

  • state: ํ˜„์žฌ ์ƒํƒœ (์˜ˆ: "READ")

  • process: ํ˜„์žฌ ํ”„๋กœ์„ธ์Šค (์˜ˆ: "MAIN")

  • universals: ๋ฒ”์šฉ ์ƒํƒœ

  • locals: ๋กœ์ปฌ ์ƒํƒœ

  • universalUpdates: ๋ฒ”์šฉ ์ƒํƒœ ์—…๋ฐ์ดํŠธ

  • localUpdates: ๋กœ์ปฌ ์ƒํƒœ ์—…๋ฐ์ดํŠธ

์ฃผ์š” ๋ฉ”์„œ๋“œ

NewInterpreter

  • Interpreter ๊ตฌ์กฐ์ฒด๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

Reset

  • ์ธํ„ฐํ”„๋ฆฌํ„ฐ์˜ ์ƒํƒœ๋ฅผ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.

  • all์ด true์ธ ๊ฒฝ์šฐ ์ƒํƒœ์™€ ํ”„๋กœ์„ธ์Šค๋„ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.

Init

  • ์ธํ„ฐํ”„๋ฆฌํ„ฐ์˜ ๋ชจ๋“œ๋ฅผ ์„ค์ •ํ•˜๊ณ , ํ•ด๋‹น ๋ชจ๋“œ์— ํ•„์š”ํ•œ ๋ฉ”์„œ๋“œ๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

LoadMethod

  • ์ฃผ์–ด์ง„ ํŠธ๋ ˆ์ดํŠธ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ์ธํ„ฐํ”„๋ฆฌํ„ฐ์— ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

  • ์ด ์˜ˆ์ œ์—์„œ๋Š” ์‹ค์ œ ๋ฉ”์„œ๋“œ ๋กœ๋“œ ๋กœ์ง์ด ๊ตฌํ˜„๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Set

  • ์ธํ„ฐํ”„๋ฆฌํ„ฐ์— ๋ฐ์ดํ„ฐ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

Process

  • ์ฃผ์–ด์ง„ ABI๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

setDefaultValue

  • ๊ธฐ๋ณธ ๊ฐ’์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

ParameterValidity

  • ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค.

Read

  • ์ƒํƒœ๋ฅผ ์ฝ์–ด์˜ต๋‹ˆ๋‹ค.

Execute

  • ์ƒํƒœ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

LoadLocalStatus

  • ๋กœ์ปฌ ์ƒํƒœ๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

LoadUniversalStatus

  • ๋ฒ”์šฉ ์ƒํƒœ๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

LoadMinerStatus

  • ๋งˆ์ด๋„ˆ ์ƒํƒœ๋ฅผ ๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.

AddLocalLoads

  • ๋กœ์ปฌ ์ƒํƒœ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

AddUniversalLoads

  • ๋ฒ”์šฉ ์ƒํƒœ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

SetLocalLoads

  • ๋กœ์ปฌ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

SetUniversalLoads

  • ๋ฒ”์šฉ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

GetLocalStatus

  • ๋กœ์ปฌ ์ƒํƒœ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

GetUniversalStatus

  • ๋ฒ”์šฉ ์ƒํƒœ๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค.

SetUniversalStatus

  • ๋ฒ”์šฉ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

SetLocalStatus

  • ๋กœ์ปฌ ์ƒํƒœ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

์ด ์ฝ”๋“œ๋Š” ๋‹ค์–‘ํ•œ ์ƒํƒœ ๊ด€๋ฆฌ ๋ฐ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ ๊ธฐ๋ณธ์ ์ธ ์ธํ„ฐํ”„๋ฆฌํ„ฐ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๊ฐ ๋ฉ”์„œ๋“œ๋Š” ์ธํ„ฐํ”„๋ฆฌํ„ฐ์˜ ํŠน์ • ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, ๋ฉ”์„œ๋“œ ์„ค๋ช… ์ฃผ์„์„ ํ†ตํ•ด ๊ฐ ๋ฉ”์„œ๋“œ์˜ ์—ญํ• ๊ณผ ๊ธฐ๋Šฅ์„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Last updated