github.com/igggame/nebulas-go@v2.1.0+incompatible/nf/nvm/engine_v8.go (about) 1 // Copyright (C) 2017 go-nebulas authors 2 // 3 // This file is part of the go-nebulas library. 4 // 5 // the go-nebulas library is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // the go-nebulas library is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU General Public License for more details. 14 // 15 // You should have received a copy of the GNU General Public License 16 // along with the go-nebulas library. If not, see <http://www.gnu.org/licenses/>. 17 // 18 19 package nvm 20 21 /* 22 #include <stdlib.h> 23 #cgo CFLAGS: 24 #cgo LDFLAGS: -L${SRCDIR}/native-lib -lnebulasv8 25 26 #include "v8/engine.h" 27 28 // Forward declaration. 29 void V8Log_cgo(int level, const char *msg); 30 31 char *RequireDelegateFunc_cgo(void *handler, const char *filename, size_t *lineOffset); 32 char *AttachLibVersionDelegateFunc_cgo(void *handler, const char *libname); 33 34 char *StorageGetFunc_cgo(void *handler, const char *key, size_t *gasCnt); 35 int StoragePutFunc_cgo(void *handler, const char *key, const char *value, size_t *gasCnt); 36 int StorageDelFunc_cgo(void *handler, const char *key, size_t *gasCnt); 37 38 char *GetTxByHashFunc_cgo(void *handler, const char *hash, size_t *gasCnt); 39 char *GetAccountStateFunc_cgo(void *handler, const char *address, size_t *gasCnt, char **result, char **info); 40 int TransferFunc_cgo(void *handler, const char *to, const char *value, size_t *gasCnt); 41 int VerifyAddressFunc_cgo(void *handler, const char *address, size_t *gasCnt); 42 char *GetPreBlockHashFunc_cgo(void *handler, unsigned long long offset, size_t *gasCnt, char **result, char **info); 43 char *GetPreBlockSeedFunc_cgo(void *handler, unsigned long long offset, size_t *gasCnt, char **result, char **info); 44 char *GetLatestNebulasRankFunc_cgo(void *handler, const char *address, size_t *gasCnt, char **result, char **info); 45 char *GetLatestNebulasRankSummaryFunc_cgo(void *handler, size_t *gasCnt, char **result, char **info); 46 47 char *Sha256Func_cgo(const char *data, size_t *gasCnt); 48 char *Sha3256Func_cgo(const char *data, size_t *gasCnt); 49 char *Ripemd160Func_cgo(const char *data, size_t *gasCnt); 50 char *RecoverAddressFunc_cgo(int alg, const char *data, const char *sign, size_t *gasCnt); 51 char *Md5Func_cgo(const char *data, size_t *gasCnt); 52 char *Base64Func_cgo(const char *data, size_t *gasCnt); 53 char *GetContractSourceFunc_cgo(void *handler, const char *address); 54 char *InnerContractFunc_cgo(void *handler, const char *address, const char *funcName, const char * v, const char *args, size_t *gasCnt); 55 56 char *GetTxRandomFunc_cgo(void *handler, size_t *gasCnt, char **result, char **exceptionInfo); 57 58 void EventTriggerFunc_cgo(void *handler, const char *topic, const char *data, size_t *gasCnt); 59 60 */ 61 import "C" 62 import ( 63 "fmt" 64 "strings" 65 "sync" 66 "unsafe" 67 68 "encoding/json" 69 70 lru "github.com/hashicorp/golang-lru" 71 "github.com/nebulasio/go-nebulas/core" 72 "github.com/nebulasio/go-nebulas/crypto/hash" 73 "github.com/nebulasio/go-nebulas/util/byteutils" 74 "github.com/nebulasio/go-nebulas/util/logging" 75 "github.com/sirupsen/logrus" 76 ) 77 78 const ( 79 ExecutionFailedErr = 1 80 ExecutionTimeOutErr = 2 81 82 // ExecutionTimeout max v8 execution timeout. 83 ExecutionTimeout = 15 * 1000 * 1000 84 OriginExecutionTimeout = 5 * 1000 * 1000 85 CompatibleExecutionTimeout = 20 * 1000 * 1000 86 TimeoutGasLimitCost = 100000000 87 MaxLimitsOfExecutionInstructions = 10000000 // TODO: set max gasLimit with execution 5s *0.8 88 ) 89 90 // const ( 91 // ExecutionFailedErr = 1 92 // ExecutionInnerNvmErr = 2 93 // ExecutionTimeOutErr = 3 94 // ) 95 96 //engine_v8 private data 97 var ( 98 v8engineOnce = sync.Once{} 99 storages = make(map[uint64]*V8Engine, 1024) 100 storagesIdx = uint64(0) 101 storagesLock = sync.RWMutex{} 102 engines = make(map[*C.V8Engine]*V8Engine, 1024) 103 enginesLock = sync.RWMutex{} 104 sourceModuleCache, _ = lru.New(40960) 105 instructionCounterVersion = "1.0.0" 106 ) 107 108 // V8Engine v8 engine. 109 type V8Engine struct { 110 ctx *Context 111 modules Modules 112 v8engine *C.V8Engine 113 strictDisallowUsageOfInstructionCounter int 114 enableLimits bool 115 limitsOfExecutionInstructions uint64 116 limitsOfTotalMemorySize uint64 117 actualCountOfExecutionInstructions uint64 118 actualTotalMemorySize uint64 119 lcsHandler uint64 120 gcsHandler uint64 121 innerErrMsg string 122 innerErr error 123 } 124 125 type sourceModuleItem struct { 126 source string 127 sourceLineOffset int 128 traceableSource string 129 traceableSourceLineOffset int 130 } 131 132 // InitV8Engine initialize the v8 engine. 133 func InitV8Engine() { 134 C.Initialize() 135 136 // Logger. 137 C.InitializeLogger((C.LogFunc)(unsafe.Pointer(C.V8Log_cgo))) 138 139 // Require. 140 C.InitializeRequireDelegate((C.RequireDelegate)(unsafe.Pointer(C.RequireDelegateFunc_cgo)), (C.AttachLibVersionDelegate)(unsafe.Pointer(C.AttachLibVersionDelegateFunc_cgo))) 141 142 // execution_env require 143 C.InitializeExecutionEnvDelegate((C.AttachLibVersionDelegate)(unsafe.Pointer(C.AttachLibVersionDelegateFunc_cgo))) 144 145 // Storage. 146 C.InitializeStorage((C.StorageGetFunc)(unsafe.Pointer(C.StorageGetFunc_cgo)), 147 (C.StoragePutFunc)(unsafe.Pointer(C.StoragePutFunc_cgo)), 148 (C.StorageDelFunc)(unsafe.Pointer(C.StorageDelFunc_cgo))) 149 150 // Blockchain. 151 C.InitializeBlockchain((C.GetTxByHashFunc)(unsafe.Pointer(C.GetTxByHashFunc_cgo)), 152 (C.GetAccountStateFunc)(unsafe.Pointer(C.GetAccountStateFunc_cgo)), 153 (C.TransferFunc)(unsafe.Pointer(C.TransferFunc_cgo)), 154 (C.VerifyAddressFunc)(unsafe.Pointer(C.VerifyAddressFunc_cgo)), 155 (C.GetPreBlockHashFunc)(unsafe.Pointer(C.GetPreBlockHashFunc_cgo)), 156 (C.GetPreBlockSeedFunc)(unsafe.Pointer(C.GetPreBlockSeedFunc_cgo)), 157 (C.GetContractSourceFunc)(unsafe.Pointer(C.GetContractSourceFunc_cgo)), 158 (C.InnerContractFunc)(unsafe.Pointer(C.InnerContractFunc_cgo)), 159 (C.GetLatestNebulasRankFunc)(unsafe.Pointer(C.GetLatestNebulasRankFunc_cgo)), 160 (C.GetLatestNebulasRankSummaryFunc)(unsafe.Pointer(C.GetLatestNebulasRankSummaryFunc_cgo))) 161 162 // random. 163 C.InitializeRandom((C.GetTxRandomFunc)(unsafe.Pointer(C.GetTxRandomFunc_cgo))) 164 165 // Event. 166 C.InitializeEvent((C.EventTriggerFunc)(unsafe.Pointer(C.EventTriggerFunc_cgo))) 167 168 // Crypto 169 C.InitializeCrypto((C.Sha256Func)(unsafe.Pointer(C.Sha256Func_cgo)), 170 (C.Sha3256Func)(unsafe.Pointer(C.Sha3256Func_cgo)), 171 (C.Ripemd160Func)(unsafe.Pointer(C.Ripemd160Func_cgo)), 172 (C.RecoverAddressFunc)(unsafe.Pointer(C.RecoverAddressFunc_cgo)), 173 (C.Md5Func)(unsafe.Pointer(C.Md5Func_cgo)), 174 (C.Base64Func)(unsafe.Pointer(C.Base64Func_cgo))) 175 } 176 177 // DisposeV8Engine dispose the v8 engine. 178 func DisposeV8Engine() { 179 C.Dispose() 180 } 181 182 // NewV8Engine return new V8Engine instance. 183 func NewV8Engine(ctx *Context) *V8Engine { 184 v8engineOnce.Do(func() { 185 InitV8Engine() 186 }) 187 188 engine := &V8Engine{ 189 ctx: ctx, 190 modules: NewModules(), 191 v8engine: C.CreateEngine(), 192 strictDisallowUsageOfInstructionCounter: 1, // enable by default. 193 enableLimits: true, 194 limitsOfExecutionInstructions: 0, 195 limitsOfTotalMemorySize: 0, 196 actualCountOfExecutionInstructions: 0, 197 actualTotalMemorySize: 0, 198 } 199 200 (func() { 201 enginesLock.Lock() 202 defer enginesLock.Unlock() 203 engines[engine.v8engine] = engine 204 })() 205 206 (func() { 207 storagesLock.Lock() 208 defer storagesLock.Unlock() 209 210 storagesIdx++ 211 engine.lcsHandler = storagesIdx 212 storagesIdx++ 213 engine.gcsHandler = storagesIdx 214 215 storages[engine.lcsHandler] = engine 216 storages[engine.gcsHandler] = engine 217 })() 218 // engine.v8engine.lcs = C.uintptr_t(engine.lcsHandler) 219 // engine.v8engine.gcs = C.uintptr_t(engine.gcsHandler) 220 if core.NvmGasLimitWithoutTimeoutAtHeight(ctx.block.Height()) { 221 engine.SetTimeOut(ExecutionTimeout) 222 } else { 223 timeoutMark := core.NvmExeTimeoutAtHeight(ctx.block.Height()) 224 if timeoutMark { 225 engine.SetTimeOut(OriginExecutionTimeout) 226 } else { 227 engine.SetTimeOut(CompatibleExecutionTimeout) 228 } 229 } 230 231 if core.EnableInnerContractAtHeight(ctx.block.Height()) { 232 engine.EnableInnerContract() 233 } 234 return engine 235 } 236 237 // SetEnableLimit eval switch 238 func (e *V8Engine) SetEnableLimit(isLimit bool) { 239 e.enableLimits = isLimit 240 } 241 242 // Dispose dispose all resources. 243 func (e *V8Engine) Dispose() { 244 storagesLock.Lock() 245 delete(storages, e.lcsHandler) 246 delete(storages, e.gcsHandler) 247 storagesLock.Unlock() 248 249 enginesLock.Lock() 250 delete(engines, e.v8engine) 251 enginesLock.Unlock() 252 253 C.DeleteEngine(e.v8engine) 254 } 255 256 // Context returns engine context 257 func (e *V8Engine) Context() *Context { 258 return e.ctx 259 } 260 261 // SetTestingFlag set testing flag, default is False. 262 func (e *V8Engine) SetTestingFlag(flag bool) { 263 // deprecated. 264 /*if flag { 265 e.v8engine.testing = C.int(1) 266 } else { 267 e.v8engine.testing = C.int(0) 268 }*/ 269 } 270 271 // SetTimeOut set nvm timeout, if not set, the default is 5*1000*1000 272 func (e *V8Engine) SetTimeOut(timeout uint64) { 273 e.v8engine.timeout = C.int(timeout) //TODO: 274 } 275 func (e *V8Engine) EnableInnerContract() { 276 C.EnableInnerContract(e.v8engine) 277 } 278 279 // SetExecutionLimits set execution limits of V8 Engine, prevent Halting Problem. 280 func (e *V8Engine) SetExecutionLimits(limitsOfExecutionInstructions, limitsOfTotalMemorySize uint64) error { 281 282 e.v8engine.limits_of_executed_instructions = C.size_t(limitsOfExecutionInstructions) 283 e.v8engine.limits_of_total_memory_size = C.size_t(limitsOfTotalMemorySize) 284 285 logging.VLog().WithFields(logrus.Fields{ 286 "limits_of_executed_instructions": limitsOfExecutionInstructions, 287 "limits_of_total_memory_size": limitsOfTotalMemorySize, 288 }).Debug("set execution limits.") 289 290 e.limitsOfExecutionInstructions = limitsOfExecutionInstructions 291 e.limitsOfTotalMemorySize = limitsOfTotalMemorySize 292 293 if limitsOfExecutionInstructions == 0 || limitsOfTotalMemorySize == 0 { 294 logging.VLog().Debugf("limit args has empty. limitsOfExecutionInstructions:%v,limitsOfTotalMemorySize:%d", limitsOfExecutionInstructions, limitsOfTotalMemorySize) 295 return ErrLimitHasEmpty 296 } 297 // V8 needs at least 6M heap memory. 298 if limitsOfTotalMemorySize > 0 && limitsOfTotalMemorySize < 6000000 { 299 logging.VLog().Debugf("V8 needs at least 6M (6000000) heap memory, your limitsOfTotalMemorySize (%d) is too low.", limitsOfTotalMemorySize) 300 return ErrSetMemorySmall 301 } 302 return nil 303 } 304 305 // ExecutionInstructions returns the execution instructions 306 func (e *V8Engine) ExecutionInstructions() uint64 { 307 return e.actualCountOfExecutionInstructions 308 } 309 310 // TranspileTypeScript transpile typescript to javascript and return it. 311 func (e *V8Engine) TranspileTypeScript(source string) (string, int, error) { 312 cSource := C.CString(source) 313 defer C.free(unsafe.Pointer(cSource)) 314 315 lineOffset := C.int(0) 316 jsSource := C.TranspileTypeScriptModuleThread(e.v8engine, cSource, &lineOffset) 317 if jsSource == nil { 318 return "", 0, ErrTranspileTypeScriptFailed 319 } 320 321 defer C.free(unsafe.Pointer(jsSource)) 322 return C.GoString(jsSource), int(lineOffset), nil 323 324 } 325 326 // InjectTracingInstructions process the source to inject tracing instructions. 327 func (e *V8Engine) InjectTracingInstructions(source string) (string, int, error) { 328 cSource := C.CString(source) 329 defer C.free(unsafe.Pointer(cSource)) 330 331 lineOffset := C.int(0) 332 333 traceableCSource := C.InjectTracingInstructionsThread(e.v8engine, cSource, &lineOffset, C.int(e.strictDisallowUsageOfInstructionCounter)) 334 if traceableCSource == nil { 335 return "", 0, ErrInjectTracingInstructionFailed 336 } 337 338 defer C.free(unsafe.Pointer(traceableCSource)) 339 return C.GoString(traceableCSource), int(lineOffset), nil 340 } 341 342 // CollectTracingStats collect tracing data from v8 engine. 343 func (e *V8Engine) CollectTracingStats() { 344 // read memory stats. 345 C.ReadMemoryStatistics(e.v8engine) 346 347 e.actualCountOfExecutionInstructions = uint64(e.v8engine.stats.count_of_executed_instructions) 348 e.actualTotalMemorySize = uint64(e.v8engine.stats.total_memory_size) 349 } 350 351 // GetNVMLeftResources return current NVM verb total resource 352 func (e *V8Engine) GetNVMLeftResources() (uint64, uint64) { 353 e.CollectTracingStats() 354 instruction := uint64(0) 355 mem := uint64(0) 356 if e.limitsOfExecutionInstructions >= e.actualCountOfExecutionInstructions { 357 instruction = e.limitsOfExecutionInstructions - e.actualCountOfExecutionInstructions 358 } 359 360 if e.limitsOfTotalMemorySize >= e.actualTotalMemorySize { 361 mem = e.limitsOfTotalMemorySize - e.actualTotalMemorySize 362 } 363 364 return instruction, mem 365 } 366 367 // RunScriptSource run js source. 368 func (e *V8Engine) RunScriptSource(source string, sourceLineOffset int) (string, error) { 369 cSource := C.CString(source) 370 defer C.free(unsafe.Pointer(cSource)) 371 372 var ( 373 result string 374 err error 375 ret C.int 376 cResult *C.char 377 ) 378 ctx := e.Context() 379 if ctx == nil || ctx.block == nil { 380 logging.VLog().WithFields(logrus.Fields{ 381 "ctx": ctx, 382 }).Error("Unexpected: Failed to get current height") 383 err = core.ErrUnexpected 384 return "", err 385 } 386 // done := make(chan bool, 1) 387 // go func() { 388 // ret = C.RunScriptSource(&cResult, e.v8engine, cSource, C.int(sourceLineOffset), C.uintptr_t(e.lcsHandler), 389 // C.uintptr_t(e.gcsHandler)) 390 // done <- true 391 // }() 392 393 ret = C.RunScriptSourceThread(&cResult, e.v8engine, cSource, C.int(sourceLineOffset), C.uintptr_t(e.lcsHandler), 394 C.uintptr_t(e.gcsHandler)) 395 e.CollectTracingStats() 396 397 if e.innerErr != nil { 398 if e.innerErrMsg == "" { //the first call of muti-nvm 399 result = "Inner Contract: \"\"" 400 } else { 401 result = "Inner Contract: " + e.innerErrMsg 402 } 403 err := e.innerErr 404 if cResult != nil { 405 C.free(unsafe.Pointer(cResult)) 406 } 407 if e.actualCountOfExecutionInstructions > e.limitsOfExecutionInstructions { 408 e.actualCountOfExecutionInstructions = e.limitsOfExecutionInstructions 409 } 410 return result, err 411 } 412 413 if ret == C.NVM_EXE_TIMEOUT_ERR { //TODO: errcode in v8 414 err = ErrExecutionTimeout 415 if core.NvmGasLimitWithoutTimeoutAtHeight(ctx.block.Height()) { 416 err = core.ErrUnexpected 417 } else if core.NewNvmExeTimeoutConsumeGasAtHeight(ctx.block.Height()) { 418 if TimeoutGasLimitCost > e.limitsOfExecutionInstructions { 419 e.actualCountOfExecutionInstructions = e.limitsOfExecutionInstructions 420 } else { 421 e.actualCountOfExecutionInstructions = TimeoutGasLimitCost 422 } 423 } 424 } else if ret == C.NVM_UNEXPECTED_ERR { 425 err = core.ErrUnexpected 426 } else if ret == C.NVM_INNER_EXE_ERR { 427 err = core.ErrInnerExecutionFailed 428 if e.limitsOfExecutionInstructions < e.actualCountOfExecutionInstructions { 429 logging.VLog().WithFields(logrus.Fields{ 430 "actualGas": e.actualCountOfExecutionInstructions, 431 "limitGas": e.limitsOfExecutionInstructions, 432 }).Error("Unexpected error: actual gas exceed the limit") 433 } 434 } else { 435 if ret != C.NVM_SUCCESS { 436 err = core.ErrExecutionFailed 437 } 438 if e.limitsOfExecutionInstructions > 0 && 439 e.limitsOfExecutionInstructions < e.actualCountOfExecutionInstructions { 440 // Reach instruction limits. 441 err = ErrInsufficientGas 442 e.actualCountOfExecutionInstructions = e.limitsOfExecutionInstructions 443 } else if e.limitsOfTotalMemorySize > 0 && e.limitsOfTotalMemorySize < e.actualTotalMemorySize { 444 // reach memory limits. 445 err = ErrExceedMemoryLimits 446 e.actualCountOfExecutionInstructions = e.limitsOfExecutionInstructions 447 } 448 } 449 450 //set result 451 if cResult != nil { 452 result = C.GoString(cResult) 453 C.free(unsafe.Pointer(cResult)) 454 } else if ret == C.NVM_SUCCESS { 455 result = "\"\"" // default JSON String. 456 } 457 458 return result, err 459 } 460 461 // DeployAndInit a contract 462 func (e *V8Engine) DeployAndInit(source, sourceType, args string) (string, error) { 463 return e.RunContractScript(source, sourceType, "init", args) 464 } 465 466 // Call function in a script 467 func (e *V8Engine) Call(source, sourceType, function, args string) (string, error) { 468 if core.PublicFuncNameChecker.MatchString(function) == false { 469 logging.VLog().Debugf("Invalid function: %v", function) 470 return "", ErrDisallowCallNotStandardFunction 471 } 472 if strings.EqualFold("init", function) == true { 473 return "", ErrDisallowCallPrivateFunction 474 } 475 return e.RunContractScript(source, sourceType, function, args) 476 } 477 478 // RunContractScript execute script in Smart Contract's way. 479 func (e *V8Engine) RunContractScript(source, sourceType, function, args string) (string, error) { 480 var runnableSource string 481 var sourceLineOffset int 482 var err error 483 484 switch sourceType { 485 case core.SourceTypeJavaScript: 486 runnableSource, sourceLineOffset, err = e.prepareRunnableContractScript(source, function, args) 487 case core.SourceTypeTypeScript: 488 // transpile to javascript. 489 jsSource, _, err := e.TranspileTypeScript(source) 490 if err != nil { 491 return "", err 492 } 493 runnableSource, sourceLineOffset, err = e.prepareRunnableContractScript(jsSource, function, args) 494 default: 495 return "", ErrUnsupportedSourceType 496 } 497 498 if err != nil { 499 return "", err 500 } 501 if core.NvmMemoryLimitWithoutInjectAtHeight(e.ctx.block.Height()) { 502 e.CollectTracingStats() 503 mem := e.actualTotalMemorySize + core.DefaultLimitsOfTotalMemorySize 504 logging.VLog().WithFields(logrus.Fields{ 505 "actualTotalMemorySize": e.actualTotalMemorySize, 506 "limit": mem, 507 "tx.hash": e.ctx.tx.Hash(), 508 }).Debug("mem limit") 509 if err := e.SetExecutionLimits(e.limitsOfExecutionInstructions, mem); err != nil { 510 return "", err 511 } 512 } 513 514 if core.NvmGasLimitWithoutTimeoutAtHeight(e.ctx.block.Height()) { 515 if e.limitsOfExecutionInstructions > MaxLimitsOfExecutionInstructions { 516 e.SetExecutionLimits(MaxLimitsOfExecutionInstructions, e.limitsOfTotalMemorySize) 517 } 518 } 519 result, err := e.RunScriptSource(runnableSource, sourceLineOffset) 520 521 if core.NvmGasLimitWithoutTimeoutAtHeight(e.ctx.block.Height()) { 522 if e.limitsOfExecutionInstructions == MaxLimitsOfExecutionInstructions && err == ErrInsufficientGas { 523 err = ErrExecutionTimeout 524 result = "\"null\"" 525 } 526 } 527 return result, err 528 } 529 530 // ClearModuleCache .. 531 func ClearSourceModuleCache() { 532 sourceModuleCache.Purge() 533 } 534 535 // AddModule add module. 536 func (e *V8Engine) AddModule(id, source string, sourceLineOffset int) error { 537 // inject tracing instruction when enable limits. 538 if e.enableLimits { 539 var item *sourceModuleItem 540 sourceHash := byteutils.Hex(hash.Sha3256([]byte(source))) 541 542 // try read from cache. 543 if sourceModuleCache.Contains(sourceHash) { //ToDo cache whether need into db 544 value, _ := sourceModuleCache.Get(sourceHash) 545 item = value.(*sourceModuleItem) 546 } 547 548 if item == nil { 549 traceableSource, lineOffset, err := e.InjectTracingInstructions(source) 550 if err != nil { 551 logging.VLog().WithFields(logrus.Fields{ 552 "err": err, 553 }).Debug("Failed to inject tracing instruction.") 554 return err 555 } 556 557 item = &sourceModuleItem{ 558 source: source, 559 sourceLineOffset: sourceLineOffset, 560 traceableSource: traceableSource, 561 traceableSourceLineOffset: lineOffset, 562 } 563 564 // put to cache. 565 sourceModuleCache.Add(sourceHash, item) 566 } 567 568 source = item.traceableSource 569 sourceLineOffset = item.traceableSourceLineOffset 570 } 571 e.modules.Add(NewModule(id, source, sourceLineOffset)) 572 return nil 573 } 574 575 func (e *V8Engine) prepareRunnableContractScript(source, function, args string) (string, int, error) { 576 sourceLineOffset := 0 577 578 counterVersion := core.GetNearestInstructionCounterVersionAtHeight(e.ctx.block.Height()) 579 if counterVersion != instructionCounterVersion { 580 instructionCounterVersion = counterVersion 581 ClearSourceModuleCache() 582 logging.VLog().WithFields(logrus.Fields{ 583 "height": e.ctx.block.Height(), 584 "version": instructionCounterVersion, 585 }).Info("Clear source module cache.") 586 } 587 588 // add module. 589 const ModuleID string = "contract.js" 590 if err := e.AddModule(ModuleID, source, sourceLineOffset); err != nil { 591 return "", 0, err 592 } 593 594 // prepare for execute. 595 block := toSerializableBlock(e.ctx.block) 596 blockJSON, err := json.Marshal(block) 597 if err != nil { 598 return "", 0, err 599 } 600 tx := toSerializableTransaction(e.ctx.tx) 601 txJSON, err := json.Marshal(tx) 602 if err != nil { 603 return "", 0, err 604 } 605 606 var runnableSource string 607 var argsInput []byte 608 if len(args) > 0 { 609 var argsObj []interface{} 610 if err := json.Unmarshal([]byte(args), &argsObj); err != nil { 611 return "", 0, ErrArgumentsFormat 612 } 613 if argsInput, err = json.Marshal(argsObj); err != nil { 614 return "", 0, ErrArgumentsFormat 615 } 616 617 } else { 618 argsInput = []byte("[]") 619 } 620 runnableSource = fmt.Sprintf(`Blockchain.blockParse("%s"); 621 Blockchain.transactionParse("%s"); 622 var __contract = require("%s"); 623 var __instance = new __contract(); 624 __instance["%s"].apply(__instance, JSON.parse("%s"));`, 625 formatArgs(string(blockJSON)), formatArgs(string(txJSON)), 626 ModuleID, function, formatArgs(string(argsInput))) //TODO: freeze? 627 return runnableSource, 0, nil 628 } 629 630 func getEngineByStorageHandler(handler uint64) (*V8Engine, Account) { 631 storagesLock.RLock() 632 engine := storages[handler] 633 storagesLock.RUnlock() 634 635 if engine == nil { 636 logging.VLog().WithFields(logrus.Fields{ 637 "wantedHandler": handler, 638 }).Error("wantedHandler is not found.") 639 return nil, nil 640 } 641 642 if engine.lcsHandler == handler { 643 return engine, engine.ctx.contract 644 } else if engine.gcsHandler == handler { 645 // disable gcs according to issue https://github.com/nebulasio/go-nebulas/issues/23. 646 return nil, nil 647 // return engine, engine.ctx.owner 648 } else { 649 logging.VLog().WithFields(logrus.Fields{ 650 "lcsHandler": engine.lcsHandler, 651 "gcsHandler": engine.gcsHandler, 652 "wantedHandler": handler, 653 }).Error("in-consistent storage handler.") 654 return nil, nil 655 } 656 } 657 658 func getEngineByEngineHandler(handler unsafe.Pointer) *V8Engine { 659 v8engine := (*C.V8Engine)(handler) 660 enginesLock.RLock() 661 defer enginesLock.RUnlock() 662 663 return engines[v8engine] 664 } 665 666 func formatArgs(s string) string { 667 s = strings.Replace(s, "\\", "\\\\", -1) 668 s = strings.Replace(s, "\n", "\\n", -1) 669 s = strings.Replace(s, "\r", "\\r", -1) 670 s = strings.Replace(s, "\"", "\\\"", -1) 671 return s 672 }